Documentation
¶
Overview ¶
This file implements the fs_read/fs_write/fs_list/fs_delete/fs_stat executors. See docs/specs/backend.md Section 3.3.
This file implements the input_key, input_mouse_click, input_mouse_move, and input_type executors via xdotool -- the pragmatic X11 implementation noted in docs/specs/backend.md Section 19 ("via xdotool or the XTest extension"). This is the `input` capability: a higher-risk area, independently toggle-able from shell/screenshot/filesystem/process/ sysinfo, requiring mandatory elicitation on every single action at the server layer (internal/mcp/tools/input.go) -- this file only performs the already-confirmed action.
This file implements the process_list/process_info/process_signal executors by reading /proc directly (Linux). See docs/specs/backend.md Section 3.4.
This file implements the screenshot_capture and screenshot_watch executors. See docs/specs/backend.md Section 3.2.
This file implements the Wayland screenshot capture backend for CaptureScreenshot (screenshot.go), via the grim CLI tool -- the pragmatic alternative to a full pipewire/xdg-desktop-portal D-Bus integration noted in docs/specs/backend.md Section 19. grim itself talks to the compositor's wlr-screencopy (or equivalent) protocol; on compositors/portals that require an interactive permission grant per capture (e.g. GNOME on Wayland without a wlroots-style protocol), grim may fail or hang waiting on a portal dialog the agent has no way to answer -- see ErrWaylandCaptureUnavailable.
Package executor implements the desktop agent's tool executors: the code that actually spawns commands, allocates PTYs, reads the filesystem, etc. This file implements shell_exec. See docs/specs/backend.md Section 3.1.1 and Section 2.2.
This file implements the PTY-backed interactive shell session executor (shell_session_start/write/close). See docs/specs/backend.md Section 3.1.2-3.1.4.
This file implements the sysinfo_get executor. See docs/specs/backend.md Section 3.5.1. Every gather* function degrades gracefully -- an unreadable subsystem yields a nil/zero-value result rather than an error, so one broken section never fails the whole call.
Index ¶
- Constants
- Variables
- func CaptureScreenshot(display string, maxWidth int, quality int) (pngBytes []byte, width, height int, err error)
- func FSWrite(path string, content []byte, mode string, fileMode os.FileMode, ...) (bytesWritten int, absPath string, err error)
- func InputKey(key string) error
- func InputMouseClick(x, y int, button string) error
- func InputMouseMove(x, y int) error
- func InputType(text string) error
- func SendProcessSignal(pid int, signalName string) (resolvedSignal string, err error)
- type DisplayServer
- type FSDeleteResult
- type FSListResult
- type FSReadResult
- type FSStatResult
- type ProcessInfo
- type ShellExecResult
- type ShellSession
- type ShellSessionManager
- func (m *ShellSessionManager) Close(id string, signal string, grace time.Duration) (exitCode int, finalOutput string, err error)
- func (m *ShellSessionManager) CloseAll()
- func (m *ShellSessionManager) Count() int
- func (m *ShellSessionManager) Get(id string) (*ShellSession, error)
- func (m *ShellSessionManager) Start(clientID string, input types.ShellSessionStartInput) (*ShellSession, error)
- type StreamFunc
- type SysinfoCPU
- type SysinfoDisk
- type SysinfoMemory
- type SysinfoNetworkIface
- type SysinfoOS
- type SysinfoResult
- type SysinfoUptime
- type WatchFrame
- type WatchOptions
- type WatchResult
Constants ¶
const ( // DefaultFSReadLimit is used when FSReadInput.Limit is unset (Section // 3.3.1: "default: 1048576 = 1MB"). DefaultFSReadLimit = 1 << 20 // FSChunkThreshold: file content at or above this size is streamed as // FrameFileContent binary chunks rather than inlined in the JSON // result (Section 3.3.1: "for large files the agent sends content as // binary file content frames"). FSChunkThreshold = 256 * 1024 // FSStreamChunkSize is the size of each streamed FrameFileContent chunk. FSStreamChunkSize = 64 * 1024 // DefaultFSListMaxDepth/DefaultFSListLimit are fs_list's defaults // (Section 3.3.3). DefaultFSListMaxDepth = 3 DefaultFSListLimit = 1000 // DefaultFSFileMode is used when FSWriteInput.FileMode is unset // (Section 3.3.2: "default: 0644"). DefaultFSFileMode = 0o644 )
const ( DefaultScreenshotDisplay = ":0" DefaultScreenshotQuality = 6 DefaultWatchIntervalMs = 2000 MinWatchIntervalMs = 500 DefaultWatchMaxFrames = 30 MaxWatchMaxFrames = 120 DefaultWatchDurationSecs = 60 MaxWatchDurationSecs = 300 )
const ( // DefaultShellTimeout is used when ShellExecInput.Timeout is nil. DefaultShellTimeout = 30 * time.Second // MaxShellTimeout bounds ShellExecInput.Timeout regardless of what the // client requests. MaxShellTimeout = 300 * time.Second )
const ( // DefaultShellSessionShell is used when neither ShellSessionStartInput.Shell // nor $SHELL is set. DefaultShellSessionShell = "/bin/bash" // DefaultShellSessionIdleTimeout is how long shell_session_write waits // for the PTY to go quiet before returning its accumulated output. DefaultShellSessionIdleTimeout = 2 * time.Second // DefaultShellSessionReadTimeout bounds shell_session_write overall, // even if the PTY never goes idle. DefaultShellSessionReadTimeout = 30 * time.Second // DefaultShellSessionCloseGrace is how long shell_session_close waits // after SIGTERM before escalating to SIGKILL. DefaultShellSessionCloseGrace = 5 * time.Second // DefaultMaxShellSessions is the default RC_MAX_SHELL_SESSIONS cap, // enforced by callers (typically per MCP session) via // ShellSessionManager.Count. DefaultMaxShellSessions = 5 )
Variables ¶
ErrInputUnavailable is returned when xdotool is not installed or the injected action itself fails (e.g. no X11 display reachable).
var ErrNoDisplay = errors.New("executor: no display available")
ErrNoDisplay is returned when no X11 display is configured/reachable.
var ErrPathNotAllowed = errors.New("executor: path outside allowed roots")
ErrPathNotAllowed is returned when AGENT_FS_ALLOWED_ROOTS is configured and the requested path falls outside every configured root.
var ErrProcessNotFound = errors.New("executor: process not found")
ErrProcessNotFound is returned when the given PID has no /proc entry (or exited between being listed and being read).
var ErrSelfSignalRejected = errors.New("executor: refusing to signal the agent's own process")
ErrSelfSignalRejected is returned by SendProcessSignal when asked to signal the agent's own PID -- a hard reject per Section 3.4.3, even if the server forwarded it.
var ErrShellSessionNotFound = errors.New("executor: shell session not found")
ErrShellSessionNotFound is returned by ShellSessionManager methods when the given shellSessionId is unknown (never existed, or already closed and reaped).
ErrWaylandCaptureUnavailable is returned when Wayland screenshot capture cannot proceed: grim is not installed, or the capture command itself failed (including a portal permission denial). Callers get this as a specific, immediate error rather than a hang -- grimCaptureTimeout bounds how long the external command may run.
Functions ¶
func CaptureScreenshot ¶
func CaptureScreenshot(display string, maxWidth int, quality int) (pngBytes []byte, width, height int, err error)
CaptureScreenshot captures the current display, encodes it as PNG, and downscales to maxWidth (preserving aspect ratio) if set and exceeded. quality maps to PNG compression level 0-9 the same way screenshot_ capture's input schema documents it (0 = no compression/fastest, 9 = best compression); values outside 0-9 fall back to DefaultScreenshotQuality. The tool surface (screenshot_capture/watch) is identical on X11 and Wayland -- CaptureScreenshot picks the backend at call time via DetectDisplayServer (Section 19). display is only meaningful for X11 (an explicit :N override); it is ignored under Wayland, which has no equivalent per-call display selector.
func FSWrite ¶
func FSWrite(path string, content []byte, mode string, fileMode os.FileMode, createDirs bool) (bytesWritten int, absPath string, err error)
FSWrite writes content to path per mode ("overwrite" default, or "append") and fileMode, creating parent directories first iff createDirs.
func InputMouseClick ¶
InputMouseClick moves to (x, y) and clicks button ("left"/"middle"/ "right", default "left") via `xdotool mousemove ... click`.
func InputMouseMove ¶
InputMouseMove moves the mouse cursor to absolute coordinates (x, y) via `xdotool mousemove`.
Types ¶
type DisplayServer ¶
type DisplayServer int
DisplayServer identifies which windowing system is active on the agent's desktop session (Section 19: "detect the active display server at runtime").
const ( // DisplayServerNone means neither a Wayland nor an X11 session was // detected (e.g. a headless machine with no desktop session at all). DisplayServerNone DisplayServer = iota DisplayServerX11 DisplayServerWayland )
func DetectDisplayServer ¶
func DetectDisplayServer() DisplayServer
DetectDisplayServer inspects the standard environment variables a desktop session sets to determine which display server is active. WAYLAND_DISPLAY takes precedence when both are set (common under Xwayland compatibility, where DISPLAY is also present) -- the native Wayland capture path is preferred whenever the session actually is Wayland.
type FSDeleteResult ¶
type FSDeleteResult struct {
ItemsRemoved int
}
FSDeleteResult is the outcome of FSDelete.
type FSListResult ¶
FSListResult is the outcome of FSList.
func FSList ¶
func FSList(path string, recursive bool, maxDepth int, showHidden bool, limit int) (FSListResult, error)
FSList lists path's contents, optionally recursively up to maxDepth, including dotfiles iff showHidden, capped at limit entries returned (though TotalCount reflects how many were actually found).
type FSReadResult ¶
type FSReadResult struct {
Content []byte
Encoding string // "utf8" | "base64"
Size int64
Truncated bool
}
FSReadResult is the outcome of FSRead.
type FSStatResult ¶
type FSStatResult struct {
Name string
Path string
Type string
Size int64
Mode string
ModTime string
Owner string
Group string
LinkTarget string
}
FSStatResult is the outcome of FSStat.
type ProcessInfo ¶
type ProcessInfo struct {
PID int
PPID int
Name string
Cmdline string
Exe string
Cwd string
User string
State string
Threads int
CPUPct float64
MemPct float64
MemRSSKB int64
MemVMSKB int64
StartTime time.Time
FDs int
Environ map[string]string
}
ProcessInfo is the full set of fields FSStat-style callers may want; process_list only surfaces a subset (see ProcessListEntry).
func GetProcessInfo ¶
func GetProcessInfo(pid int) (ProcessInfo, error)
GetProcessInfo returns full details for pid, or ErrProcessNotFound.
func ListProcesses ¶
func ListProcesses(filter, userFilter, sortBy string, limit int) (procs []ProcessInfo, totalCount int, err error)
ListProcesses returns processes matching filter (substring of Name) and userFilter (exact match), sorted by sortBy ("pid" default | "cpu" | "memory" | "name"), capped at limit (default 100). totalCount is the number of processes that matched the filters, before the limit is applied.
type ShellExecResult ¶
type ShellExecResult struct {
Stdout string
Stderr string
ExitCode int
Killed bool
DurationMs int64
}
ShellExecResult is the outcome of one Exec call.
func Exec ¶
func Exec(ctx context.Context, input types.ShellExecInput, onChunk StreamFunc) (ShellExecResult, error)
Exec spawns "/bin/sh -c <command>" per input, streaming combined stdout/stderr chunks to onChunk (if non-nil) as they arrive, and returns once the command exits, the timeout elapses (SIGKILL, Killed=true), or ctx is cancelled (also SIGKILL, Killed=true).
A command that fails to be found by /bin/sh itself (e.g. "nosuchcmd") is not a Go-level start error: /bin/sh reports it on stderr and exits 127, which Exec surfaces as ExitCode 127, not an error.
type ShellSession ¶
type ShellSession struct {
ID string
PID int
Shell string
ClientID string
// contains filtered or unexported fields
}
ShellSession is one PTY-backed interactive shell running on the agent.
func (*ShellSession) StreamUntilIdleOrTimeout ¶
func (s *ShellSession) StreamUntilIdleOrTimeout(ctx context.Context, onChunk StreamFunc, idleTimeout, readTimeout time.Duration) (output string, exited bool, exitCode int)
StreamUntilIdleOrTimeout streams PTY output via onChunk (per the 200ms/4KB cadence) until the PTY has been quiet for idleTimeout, readTimeout elapses, or the shell process exits -- whichever comes first. It returns the output accumulated since the previous call (or session start), whether the process has exited, and its exit code if so.
type ShellSessionManager ¶
type ShellSessionManager struct {
// contains filtered or unexported fields
}
ShellSessionManager tracks all PTY sessions currently open on this agent, keyed by shellSessionId.
func NewShellSessionManager ¶
func NewShellSessionManager() *ShellSessionManager
NewShellSessionManager constructs an empty ShellSessionManager.
func (*ShellSessionManager) Close ¶
func (m *ShellSessionManager) Close(id string, signal string, grace time.Duration) (exitCode int, finalOutput string, err error)
Close terminates sess: sends signal (default SIGTERM), waits up to grace for the process to exit, then escalates to SIGKILL. It kills the whole process group (see the process-tree kill note on Start) so children the shell forked don't outlive it and keep the pty open.
func (*ShellSessionManager) CloseAll ¶
func (m *ShellSessionManager) CloseAll()
CloseAll terminates every open session (SIGTERM with a short grace, then SIGKILL via Close's escalation) and releases their PTYs. Used when the agent's reconnect grace period lapses (Section 2.1).
func (*ShellSessionManager) Count ¶
func (m *ShellSessionManager) Count() int
Count returns the number of currently open (not-yet-closed) sessions.
func (*ShellSessionManager) Get ¶
func (m *ShellSessionManager) Get(id string) (*ShellSession, error)
Get returns the session for id, or ErrShellSessionNotFound.
func (*ShellSessionManager) Start ¶
func (m *ShellSessionManager) Start(clientID string, input types.ShellSessionStartInput) (*ShellSession, error)
Start allocates a PTY, spawns input.Shell (or $SHELL / DefaultShellSessionShell), and registers the resulting session under a freshly minted shellSessionId.
type StreamFunc ¶
type StreamFunc func(chunk []byte)
StreamFunc is called with each combined stdout/stderr chunk as the command runs, per the streaming cadence above. It must not block for long, since it is called synchronously from the pipe-reading goroutines.
type SysinfoCPU ¶
type SysinfoDisk ¶
type SysinfoMemory ¶
type SysinfoNetworkIface ¶
type SysinfoNetworkIface struct {
Name, IPv4, IPv6, MAC, State string
}
type SysinfoResult ¶
type SysinfoResult struct {
Hostname string
OS *SysinfoOS
Uptime *SysinfoUptime
CPU *SysinfoCPU
Memory *SysinfoMemory
Disk []SysinfoDisk
Network []SysinfoNetworkIface
}
SysinfoResult mirrors types.SysinfoGetOutput without importing the mcp types package's JSON tags, so this file stays agent-only.
func GatherSysinfo ¶
func GatherSysinfo(sections []string) SysinfoResult
GatherSysinfo collects the requested sections (case-insensitive; "all" or an empty slice means every section).
type SysinfoUptime ¶
type WatchFrame ¶
WatchFrame is delivered to onFrame for each successfully captured frame.
type WatchOptions ¶
type WatchOptions struct {
Display string
MaxWidth int
Quality int
IntervalMs int
MaxFrames int
DurationSecs int
}
WatchOptions configures a WatchScreenshots run.
type WatchResult ¶
type WatchResult struct {
FramesCaptured int
DurationMs int64
StoppedReason string // "maxFrames" | "duration" | "cancelled" | "error"
}
WatchResult is the terminal outcome of a WatchScreenshots run.
func WatchScreenshots ¶
func WatchScreenshots(ctx context.Context, opts WatchOptions, onFrame func(WatchFrame)) (WatchResult, error)
WatchScreenshots captures screenshots at opts.IntervalMs, invoking onFrame for each one, until opts.MaxFrames frames have been captured, opts.DurationSecs elapses, or ctx is cancelled -- whichever comes first. A capture error stops the watch early (StoppedReason "error") rather than looping forever against a broken display.