session: remove Interrupt, Queue, Detach, and all remaining agent pass-throughs
Session now has only genuinely session-level methods: - Agent(), Bus() — access points - Close(), SetEnv() — lifecycle - CWD(), SetCWD(), SetSessionID() — filesystem identity - WaitChange() — field watching for 9P - SaveSession(), saveSession(), flushSave() — persistence Everything else (Interrupt, Queue, Detach, Reactions, etc.) lives on Agent. Callers go through session.Agent() to reach agent operations.
This commit is contained in:
parent
24b9a0bdd3
commit
ec62eb9189
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/simonfxr/pubsub"
|
||||
|
|
@ -509,3 +510,82 @@ func (ag *Agent) WaitForChange(ctx context.Context) {
|
|||
}
|
||||
ag.changeMu.Unlock()
|
||||
}
|
||||
|
||||
// Detach detaches the current running process to background.
|
||||
func (ag *Agent) Detach() bool {
|
||||
if srv := ag.ExecServer(); srv != nil {
|
||||
if d, ok := srv.(interface{ Detach() bool }); ok {
|
||||
return d.Detach()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DetachedInfo describes a detached process.
|
||||
type DetachedInfo struct {
|
||||
PID int
|
||||
Command string
|
||||
Started int64
|
||||
Exited bool
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
// ListDetached returns info about all detached processes.
|
||||
func (ag *Agent) ListDetached() []DetachedInfo {
|
||||
srv := ag.ExecServer()
|
||||
if srv == nil {
|
||||
return nil
|
||||
}
|
||||
type listDetacher interface{ ListDetachedRaw() []any }
|
||||
ld, ok := srv.(listDetacher)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
raw := ld.ListDetachedRaw()
|
||||
out := make([]DetachedInfo, 0, len(raw))
|
||||
for _, r := range raw {
|
||||
if m, ok := r.(map[string]any); ok {
|
||||
di := DetachedInfo{}
|
||||
if v, ok := m["pid"].(int); ok { di.PID = v }
|
||||
if v, ok := m["command"].(string); ok { di.Command = v }
|
||||
if v, ok := m["started"].(int64); ok { di.Started = v }
|
||||
if v, ok := m["exited"].(bool); ok { di.Exited = v }
|
||||
if v, ok := m["exit_code"].(int); ok { di.ExitCode = v }
|
||||
out = append(out, di)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SignalDetached sends a signal to a detached process.
|
||||
func (ag *Agent) SignalDetached(pid int, signal int) error {
|
||||
if srv := ag.ExecServer(); srv != nil {
|
||||
type signaler interface{ SignalDetached(int, syscall.Signal) error }
|
||||
if sg, ok := srv.(signaler); ok {
|
||||
return sg.SignalDetached(pid, syscall.Signal(signal))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no execute server available")
|
||||
}
|
||||
|
||||
// GetDetachedOutput reads output from a detached process.
|
||||
func (ag *Agent) GetDetachedOutput(pid int) (string, error) {
|
||||
if srv := ag.ExecServer(); srv != nil {
|
||||
type outputGetter interface{ GetDetachedOutput(int) (string, error) }
|
||||
if og, ok := srv.(outputGetter); ok {
|
||||
return og.GetDetachedOutput(pid)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no execute server available")
|
||||
}
|
||||
|
||||
// DismissDetached removes a finished detached process.
|
||||
func (ag *Agent) DismissDetached(pid int) bool {
|
||||
if srv := ag.ExecServer(); srv != nil {
|
||||
type dismisser interface{ DismissDetached(int) bool }
|
||||
if d, ok := srv.(dismisser); ok {
|
||||
return d.DismissDetached(pid)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/simonfxr/pubsub"
|
||||
|
|
@ -188,19 +187,6 @@ func (a *Session) SetEnv(key, value string) {
|
|||
func (a *Session) Agent() *agent.Agent { return a.r }
|
||||
func (a *Session) Bus() *pubsub.Bus { return a.bus }
|
||||
|
||||
func (a *Session) Interrupt(cause error) bool {
|
||||
return a.r.Interrupt(cause)
|
||||
}
|
||||
|
||||
func (a *Session) Queue(prompt string) {
|
||||
a.r.Queue(prompt)
|
||||
a.bus.Publish("queued", prompt)
|
||||
}
|
||||
|
||||
func (a *Session) InjectSystemEvent(content string) {
|
||||
a.Queue("<detached-process-result>\n" + content + "\n</detached-process-result>")
|
||||
}
|
||||
|
||||
// CWD returns the current working directory for tool execution.
|
||||
func (a *Session) CWD() string {
|
||||
if c := a.r.Cwd(); c != "" {
|
||||
|
|
@ -279,10 +265,6 @@ func (a *Session) WaitChange(ctx context.Context, field, current string) (string
|
|||
return "", false
|
||||
}
|
||||
|
||||
func (a *Session) emit(ev agent.Event) {
|
||||
a.bus.Publish("event", ev)
|
||||
}
|
||||
|
||||
func (a *Session) activeSessionPath(id, suffix string) string {
|
||||
return filepath.Join(a.sessionsDir, "active", id+suffix)
|
||||
}
|
||||
|
|
@ -322,78 +304,3 @@ func (a *Session) flushSave() {
|
|||
func (a *Session) SaveSession(path string) error {
|
||||
return a.r.SaveFull(path, a.id, a.CWD(), a.remote)
|
||||
}
|
||||
|
||||
// Detach operations delegate to the agent's execute server.
|
||||
func (a *Session) Detach() bool {
|
||||
if srv := a.r.ExecServer(); srv != nil {
|
||||
if d, ok := srv.(interface{ Detach() bool }); ok {
|
||||
return d.Detach()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DetachedInfo describes a detached process.
|
||||
type DetachedInfo struct {
|
||||
PID int
|
||||
Command string
|
||||
Started int64
|
||||
Exited bool
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
func (a *Session) ListDetached() []DetachedInfo {
|
||||
srv := a.r.ExecServer()
|
||||
if srv == nil {
|
||||
return nil
|
||||
}
|
||||
type listDetacher interface{ ListDetachedRaw() []any }
|
||||
ld, ok := srv.(listDetacher)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
raw := ld.ListDetachedRaw()
|
||||
out := make([]DetachedInfo, 0, len(raw))
|
||||
for _, r := range raw {
|
||||
if m, ok := r.(map[string]any); ok {
|
||||
di := DetachedInfo{}
|
||||
if v, ok := m["pid"].(int); ok { di.PID = v }
|
||||
if v, ok := m["command"].(string); ok { di.Command = v }
|
||||
if v, ok := m["started"].(int64); ok { di.Started = v }
|
||||
if v, ok := m["exited"].(bool); ok { di.Exited = v }
|
||||
if v, ok := m["exit_code"].(int); ok { di.ExitCode = v }
|
||||
out = append(out, di)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (a *Session) SignalDetached(pid, signal int) error {
|
||||
if srv := a.r.ExecServer(); srv != nil {
|
||||
type signaler interface{ SignalDetached(int, syscall.Signal) error }
|
||||
if sg, ok := srv.(signaler); ok {
|
||||
return sg.SignalDetached(pid, syscall.Signal(signal))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no execute server available")
|
||||
}
|
||||
|
||||
func (a *Session) GetDetachedOutput(pid int) (string, error) {
|
||||
if srv := a.r.ExecServer(); srv != nil {
|
||||
type outputGetter interface{ GetDetachedOutput(int) (string, error) }
|
||||
if og, ok := srv.(outputGetter); ok {
|
||||
return og.GetDetachedOutput(pid)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no execute server available")
|
||||
}
|
||||
|
||||
func (a *Session) DismissDetached(pid int) bool {
|
||||
if srv := a.r.ExecServer(); srv != nil {
|
||||
type dismisser interface{ DismissDetached(int) bool }
|
||||
if d, ok := srv.(dismisser); ok {
|
||||
return d.DismissDetached(pid)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue