Documentation
¶
Overview ¶
Package worker — daemon-side client for the sandbox worker (ADR-029 phase 1).
The daemon dials the worker once at first tool call and re-uses the connection for the lifetime of the dispatch. Phase 1 keeps a single connection per Client; multiple concurrent tool calls serialise through it. Phase 2 will pool connections.
Package worker — process-wide singleton client used by tool handlers (Bash / Read / Edit / Write) to route through the sandbox worker when configured.
The lifecycle: server.go's buildMCPServer reads cfg.SandboxWorker at boot, calls SetGlobal once if Mode != "off", and tool handlers consult Global() per call. nil global = host fallback (legacy behaviour preserved).
Package worker — sandbox-worker protocol shapes (ADR-029).
The worker is the second leg of clawtool's orchestrator+worker pair. The daemon dials the worker over a single bearer-auth'd WebSocket; tool calls (Bash / Read / Edit / Write / Glob / Grep) route through Request frames. Wire format: JSON-line over WS, one request → one response, no streaming primitive in Phase 1 (large outputs cap at 4 MiB and truncate; matches BIAM runner's existing readCapped policy).
Two design choices worth reading the ADR for:
- Daemon dials worker, NOT the reverse. claude.ai's mimic uses the same asymmetry — the orchestrator owns the connection lifetime. The worker is a passive listener that accepts a single trusted dial.
- Same binary serves both roles. `clawtool serve` is the daemon; `clawtool sandbox-worker` is the worker. Shared codebase = shared semantics for tool calls.
Package worker — sandbox-worker server (ADR-029 phase 1).
Listens on a single TCP port, accepts one bearer-authenticated WebSocket dial from the daemon, dispatches Request frames to per-kind handlers, writes Response frames back. Closes the listener after the first client (single-tenant by design; future phase will pool workers per-conversation).
Index ¶
- Constants
- Variables
- func DefaultTokenPath() string
- func EncodeRequest(r *Request) ([]byte, error)
- func LoadToken(path string) (string, error)
- func MarshalBody(v any) (json.RawMessage, error)
- func Run(ctx context.Context, opts ServerOptions) error
- func SetGlobal(c *Client)
- func UnmarshalBody(raw json.RawMessage, v any) error
- type Client
- func (c *Client) Close()
- func (c *Client) Exec(ctx context.Context, req ExecRequest) (*ExecResponse, error)
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) Read(ctx context.Context, req ReadRequest) (*ReadResponse, error)
- func (c *Client) Write(ctx context.Context, req WriteRequest) (*WriteResponse, error)
- type ExecRequest
- type ExecResponse
- type GlobRequest
- type GlobResponse
- type GrepHit
- type GrepRequest
- type GrepResponse
- type Kind
- type ReadRequest
- type ReadResponse
- type Request
- type Response
- type ServerOptions
- type StatRequest
- type StatResponse
- type WriteRequest
- type WriteResponse
Constants ¶
const ProtocolVersion = "1"
ProtocolVersion bumps when wire format breaks. Phase 1 = "1".
Variables ¶
var ErrUnconfigured = errors.New("worker: not configured (sandbox.worker.mode=off)")
ErrUnconfigured signals the daemon's tool path that no worker is wired (mode=off). Caller falls back to host execution.
Functions ¶
func DefaultTokenPath ¶
func DefaultTokenPath() string
DefaultTokenPath honours XDG conventions for the worker token file. Mirrors internal/cli/sandbox_worker.go's helper but duplicated here so daemon-side code doesn't import internal/cli (would create a cycle).
func EncodeRequest ¶
EncodeRequest marshals one request to a single JSON line.
func LoadToken ¶
LoadToken reads the bearer token from path with the same trimming rules the worker server uses on its end. Empty file or missing file returns ("", error).
func MarshalBody ¶
func MarshalBody(v any) (json.RawMessage, error)
MarshalBody is sugar for typed-payload → RawMessage.
func Run ¶
func Run(ctx context.Context, opts ServerOptions) error
Run is the worker's main entrypoint. Blocks until ctx is cancelled or the listener errors out fatally.
func SetGlobal ¶
func SetGlobal(c *Client)
SetGlobal registers the daemon-wide worker client. Pass nil to disable. Idempotent.
func UnmarshalBody ¶
func UnmarshalBody(raw json.RawMessage, v any) error
UnmarshalBody is the inverse — Request.Body → typed payload.
Types ¶
type Client ¶
type Client struct {
URL string // ws://host:port/ws
Token string
// contains filtered or unexported fields
}
Client is the daemon's handle on a sandbox worker. Goroutine- safe: Send serialises through a mutex.
func Global ¶
func Global() *Client
Global returns the registered client, or nil when worker mode is off / unconfigured. Tool handlers MUST handle nil by falling back to host execution — this is the contract that keeps `mode=off` backward-compatible.
func (*Client) Close ¶
func (c *Client) Close()
Close drops the underlying WebSocket. Safe to call repeatedly.
func (*Client) Exec ¶
func (c *Client) Exec(ctx context.Context, req ExecRequest) (*ExecResponse, error)
Exec routes a Bash tool call to the worker. Mirrors the host path's semantics so the daemon can route transparently.
func (*Client) Ping ¶
Ping verifies the worker is reachable + auth is correct. Returns nil on success.
func (*Client) Read ¶
func (c *Client) Read(ctx context.Context, req ReadRequest) (*ReadResponse, error)
Read routes a Read tool call.
func (*Client) Write ¶
func (c *Client) Write(ctx context.Context, req WriteRequest) (*WriteResponse, error)
Write routes a Write tool call.
type ExecRequest ¶
type ExecRequest struct {
Command string `json:"command"`
Cwd string `json:"cwd,omitempty"`
Env map[string]string `json:"env,omitempty"`
TimeoutMs int `json:"timeout_ms,omitempty"` // hard wall-clock cap
}
ExecRequest mirrors mcp__clawtool__Bash's input shape so the daemon can transparently route Bash tool calls here.
type ExecResponse ¶
type ExecResponse struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
ExitCode int `json:"exit_code"`
DurationMs int64 `json:"duration_ms"`
TimedOut bool `json:"timed_out"`
Cwd string `json:"cwd"`
}
ExecResponse mirrors clawtool's structured Bash output shape.
type GlobRequest ¶
type GlobResponse ¶
type GrepRequest ¶
type GrepResponse ¶
type Kind ¶
type Kind string
Kind enumerates the request types the worker handles. Adding new kinds is a wire-format break — bump the protocol version.
type ReadRequest ¶
type ReadResponse ¶
type Request ¶
type Request struct {
V string `json:"v"` // protocol version
ID string `json:"id"` // caller-assigned request id (uuid recommended)
Kind Kind `json:"kind"` // operation
Body json.RawMessage `json:"body,omitempty"` // per-kind payload
}
Request is the inbound shape on the worker WebSocket. ID is caller-assigned; responses echo it back so a client can pipeline multiple requests onto one connection (Phase 2).
func DecodeRequest ¶
DecodeRequest parses one JSON line. Caller must have already authenticated the WebSocket frame.
type Response ¶
type Response struct {
V string `json:"v"`
ID string `json:"id"`
Status int `json:"status"`
Body json.RawMessage `json:"body,omitempty"`
Error string `json:"error,omitempty"`
}
Response is the outbound shape. Either Body OR Error is populated, never both. Status mirrors HTTP-ish conventions: 0 = ok, 1 = caller error, 2 = worker internal error.
type ServerOptions ¶
type ServerOptions struct {
Listen string // ":2024" or "127.0.0.1:0" (port 0 = pick a free port)
Token string // bearer token; clients must present `Authorization: Bearer <token>`
Workdir string // root the worker resolves relative paths against; default cwd
MaxBytes int // per-response cap (default 4 MiB)
}
ServerOptions configures the worker's listener.
type StatRequest ¶
type StatRequest struct {
Path string `json:"path"`
}