agent: expose detach methods on Core interface
Adds Detach, ListDetached, SignalDetached, GetDetachedOutput, DismissDetached to the Core interface. Implemented on agent struct via interface type assertions to the execute server. Also adds ListDetachedRaw() to execute.Server for cross-package consumption without import cycles.
This commit is contained in:
parent
16d057741c
commit
5df603a9e3
|
|
@ -15,6 +15,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/simonfxr/pubsub"
|
||||
|
|
@ -460,6 +461,85 @@ func (s *agent) Close() {
|
|||
}
|
||||
}
|
||||
|
||||
// execServer returns the execute server if available, or nil.
|
||||
func (s *agent) execServer() interface{} {
|
||||
if s.runtime == nil || s.runtime.Dispatcher == nil {
|
||||
return nil
|
||||
}
|
||||
srv, _ := s.runtime.Dispatcher.GetServer("execute")
|
||||
return srv
|
||||
}
|
||||
|
||||
func (s *agent) Detach() bool {
|
||||
if srv := s.execServer(); srv != nil {
|
||||
if d, ok := srv.(interface{ Detach() bool }); ok {
|
||||
return d.Detach()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *agent) ListDetached() []DetachedInfo {
|
||||
if srv := s.execServer(); srv != nil {
|
||||
type listDetacher interface {
|
||||
ListDetachedRaw() []any
|
||||
}
|
||||
if ld, ok := srv.(listDetacher); ok {
|
||||
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
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *agent) SignalDetached(pid, signal int) error {
|
||||
if srv := s.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 (s *agent) GetDetachedOutput(pid int) (string, error) {
|
||||
if srv := s.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 (s *agent) DismissDetached(pid int) bool {
|
||||
if srv := s.execServer(); srv != nil {
|
||||
type dismisser interface {
|
||||
DismissDetached(int) bool
|
||||
}
|
||||
if d, ok := srv.(dismisser); ok {
|
||||
return d.DismissDetached(pid)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *agent) AgentName() string {
|
||||
v := s.agentName
|
||||
s.log.Debug("AgentName() = %q", v)
|
||||
|
|
|
|||
|
|
@ -146,6 +146,31 @@ type Core interface {
|
|||
// Close releases resources associated with the session, including its
|
||||
// temporary directory under /tmp/ollie/.
|
||||
Close()
|
||||
|
||||
// Detach detaches the currently running process from the agent.
|
||||
// The process continues running; returns false if nothing is executing.
|
||||
Detach() bool
|
||||
|
||||
// ListDetached returns info about all detached processes.
|
||||
ListDetached() []DetachedInfo
|
||||
|
||||
// SignalDetached sends a signal to a detached process by PID.
|
||||
SignalDetached(pid, signal int) error
|
||||
|
||||
// GetDetachedOutput returns the ring buffer output for a detached process.
|
||||
GetDetachedOutput(pid int) (string, error)
|
||||
|
||||
// DismissDetached removes an exited process from the list.
|
||||
DismissDetached(pid int) bool
|
||||
}
|
||||
|
||||
// DetachedInfo describes a detached process for external consumers.
|
||||
type DetachedInfo struct {
|
||||
PID int
|
||||
Command string
|
||||
Started int64 // unix timestamp
|
||||
Exited bool
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
// PromptFIFO is a simple thread-safe FIFO for queued prompts.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,27 @@ type DetachedProcess struct {
|
|||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Info returns a plain-data snapshot of this process for external consumers.
|
||||
type DetachedInfoData struct {
|
||||
PID int
|
||||
Command string
|
||||
Started int64 // unix timestamp
|
||||
Exited bool
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
func (p *DetachedProcess) Info() DetachedInfoData {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return DetachedInfoData{
|
||||
PID: p.PID,
|
||||
Command: p.Command,
|
||||
Started: p.Started.Unix(),
|
||||
Exited: p.Exited,
|
||||
ExitCode: p.ExitCode,
|
||||
}
|
||||
}
|
||||
|
||||
// Output returns the current contents of the ring buffer.
|
||||
func (p *DetachedProcess) Output() string {
|
||||
p.mu.Lock()
|
||||
|
|
|
|||
|
|
@ -786,6 +786,36 @@ func (e *Server) ListDetached() []*DetachedProcess {
|
|||
return out
|
||||
}
|
||||
|
||||
// ListDetachedInfo returns plain-data snapshots of all detached processes.
|
||||
func (e *Server) ListDetachedInfo() []DetachedInfoData {
|
||||
e.detachMu.Lock()
|
||||
defer e.detachMu.Unlock()
|
||||
out := make([]DetachedInfoData, len(e.detached))
|
||||
for i, p := range e.detached {
|
||||
out[i] = p.Info()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ListDetachedRaw returns detached process info as []any (each element is map[string]any)
|
||||
// for consumption by packages that can't import this package directly.
|
||||
func (e *Server) ListDetachedRaw() []any {
|
||||
e.detachMu.Lock()
|
||||
defer e.detachMu.Unlock()
|
||||
out := make([]any, len(e.detached))
|
||||
for i, p := range e.detached {
|
||||
info := p.Info()
|
||||
out[i] = map[string]any{
|
||||
"pid": info.PID,
|
||||
"command": info.Command,
|
||||
"started": info.Started,
|
||||
"exited": info.Exited,
|
||||
"exit_code": info.ExitCode,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SignalDetached sends a signal to a detached process by PID.
|
||||
func (e *Server) SignalDetached(pid int, sig syscall.Signal) error {
|
||||
e.detachMu.Lock()
|
||||
|
|
|
|||
Reference in New Issue