Documentation
¶
Index ¶
- Constants
- Variables
- type BridgeClient
- type ExecInitializeParams
- type ExecInitializeResult
- type FsCopyParams
- type FsReadFileParams
- type FsReadFileResult
- type FsRemoveParams
- type FsWriteFileParams
- type JSONRPCError
- type JSONRPCMessage
- type Pool
- type ProcessOutputChunk
- type ProcessReadParams
- type ProcessReadResult
- type ProcessStartParams
- type ProcessStartResult
- type ProcessTerminateParams
- type ProcessWriteParams
- type RelayClient
- type RelayTicket
Constants ¶
const ( ExecMethodInitialize = "initialize" ExecMethodInitialized = "initialized" // notification ExecMethodProcessStart = "process/start" ExecMethodProcessRead = "process/read" ExecMethodProcessWrite = "process/write" ExecMethodProcessTerminate = "process/terminate" ExecMethodProcessExited = "process/exited" // notification (informational; we poll instead) ExecMethodProcessClosed = "process/closed" // notification (informational) ExecMethodFsReadFile = "fs/readFile" ExecMethodFsWriteFile = "fs/writeFile" ExecMethodFsRemove = "fs/remove" ExecMethodFsCopy = "fs/copy" )
Method names — must match codex-rs/exec-server/src/protocol.rs.
Variables ¶
var ErrRelayDisabled = fmt.Errorf("relay: HTTP relay path disabled (no exec-gateway-internal-url or secret)")
ErrRelayDisabled is returned by CreateRelay when the gateway URL or internal secret is empty (config-disabled).
Functions ¶
This section is empty.
Types ¶
type BridgeClient ¶
type BridgeClient struct {
// contains filtered or unexported fields
}
BridgeClient wraps one WebSocket connection to /bridge/{exe_id} on codex-exec-gateway and exposes a JSON-RPC client interface that env-mcp uses to talk codex's exec-server protocol.
Concurrency model: a single background goroutine reads frames and dispatches them to a per-id reply channel; Call() blocks on its channel until the goroutine delivers, the context is cancelled, or the connection closes.
func DialBridge ¶
func DialBridge(ctx context.Context, wsURL, authToken string, logger *slog.Logger) (*BridgeClient, error)
DialBridge dials wsURL and, when authToken is non-empty, sets `Authorization: Bearer <authToken>` on the upgrade request. Returns once the WebSocket handshake completes; subsequent reads are pumped by a background goroutine.
nhooyr.io/websocket does NOT request `permessage-deflate` by default — we rely on that, because codex's exec-server closes connections that do (see spec § PoC #2 gotchas).
func (*BridgeClient) Call ¶
func (bc *BridgeClient) Call(ctx context.Context, method string, params json.RawMessage) (json.RawMessage, error)
Call sends a JSON-RPC request and blocks until the response arrives, the context is cancelled, or the connection closes.
func (*BridgeClient) Close ¶
func (bc *BridgeClient) Close()
Close shuts the connection. Safe to call repeatedly; first call wins.
func (*BridgeClient) Notify ¶
func (bc *BridgeClient) Notify(ctx context.Context, method string, params json.RawMessage) error
Notify sends a JSON-RPC notification (no id, no reply expected).
type ExecInitializeParams ¶
type ExecInitializeParams struct {
ClientName string `json:"clientName"`
ResumeSessionID *string `json:"resumeSessionId,omitempty"`
}
ExecInitializeParams matches codex-rs's InitializeParams (camelCase).
type ExecInitializeResult ¶
type ExecInitializeResult struct {
SessionID string `json:"sessionId"`
}
type FsCopyParams ¶
type FsCopyParams struct {
SourcePath string `json:"sourcePath"`
DestinationPath string `json:"destinationPath"`
Recursive bool `json:"recursive,omitempty"`
}
FsCopyParams is the request body for fs/copy.
type FsReadFileParams ¶
type FsReadFileParams struct {
Path string `json:"path"`
}
FsReadFileParams is the request body for fs/readFile.
type FsReadFileResult ¶
type FsReadFileResult struct {
DataBase64 string `json:"dataBase64"`
}
FsReadFileResult: dataBase64 is the file's full content (codex returns the entire file; we expose offset/limit slicing in the MCP tool wrapper).
type FsRemoveParams ¶
type FsRemoveParams struct {
Path string `json:"path"`
Recursive bool `json:"recursive,omitempty"`
}
FsRemoveParams is the request body for fs/remove.
type FsWriteFileParams ¶
type FsWriteFileParams struct {
Path string `json:"path"`
DataBase64 string `json:"dataBase64"`
// CreateMissing controls whether intermediate directories are
// created. Codex's default is true.
CreateMissing bool `json:"createMissing,omitempty"`
}
FsWriteFileParams is the request body for fs/writeFile.
type JSONRPCError ¶
type JSONRPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
}
type JSONRPCMessage ¶
type JSONRPCMessage struct {
JSONRPC string `json:"jsonrpc"`
ID *int64 `json:"id,omitempty"`
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
Error *JSONRPCError `json:"error,omitempty"`
}
JSONRPCMessage is the JSON-RPC 2.0 envelope shared by both MCP (over stdio) and exec-server (over ws). The ID field is a pointer so notifications (which have no ID) marshal cleanly without the field.
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool maintains one BridgeClient per exe_id, dialed lazily on first Get and reused across calls. Closed connections (detected via the BridgeClient's `closed` channel) are dropped and redialed transparently. Used by env-mcp tools to multiplex multiple executor targets behind one stdio MCP server.
func NewPool ¶
NewPool returns a pool. gatewayBaseURL should be the prefix to which `/<exe_id>` is appended (i.e. include `/bridge` but no trailing slash); token is the workspace-scoped capability token issued for this turn.
func (*Pool) Get ¶
Get returns a live BridgeClient for exeID. Dials on first use (with codex exec-server `initialize` handshake performed in-band so the caller can start issuing process/* and fs/* calls immediately). On subsequent calls, returns the cached client if it's still open.
Race-safety: dial happens outside the pool lock so a slow dial for exe_a doesn't block Get(exe_b). If two goroutines race to dial the same exe_id, both will dial but only one connection ends up in the map; the loser closes its connection and returns the winner's.
type ProcessOutputChunk ¶
type ProcessOutputChunk struct {
Seq uint64 `json:"seq"`
Stream string `json:"stream"` // "stdout" | "stderr"
Chunk string `json:"chunk"`
}
ProcessOutputChunk: chunk is base64-encoded raw bytes (per codex's ByteChunk wrapper that uses serde_with for base64 encoding).
type ProcessReadParams ¶
type ProcessReadResult ¶
type ProcessStartParams ¶
type ProcessStartParams struct {
ProcessID string `json:"processId"`
Argv []string `json:"argv"`
// Cwd: when empty, omit from the wire so exec-server inherits its
// own working directory. Sending "" was treated by Windows
// exec-server as a literal (invalid) path → os error 267
// "目录名称无效".
Cwd string `json:"cwd,omitempty"`
// Env is REQUIRED on the wire (exec-server's ExecParams.env is
// HashMap<String,String>, not Option<...>). Nil/omitted → serde
// "missing field `env`". MarshalJSON below normalizes nil → {}.
Env map[string]string `json:"env"`
TTY bool `json:"tty"`
PipeStdin bool `json:"pipeStdin"`
Arg0 *string `json:"arg0,omitempty"`
}
func (ProcessStartParams) MarshalJSON ¶ added in v0.61.11
func (p ProcessStartParams) MarshalJSON() ([]byte, error)
MarshalJSON ensures Env is always emitted as an object, even when nil. A nil map serializes to `null` by default, which exec-server's serde rejects with "missing field `env`".
type ProcessStartResult ¶
type ProcessStartResult struct {
ProcessID string `json:"processId"`
}
type ProcessTerminateParams ¶
type ProcessTerminateParams struct {
ProcessID string `json:"processId"`
}
ProcessTerminateParams is the request body for process/terminate.
type ProcessWriteParams ¶
type ProcessWriteParams struct {
ProcessID string `json:"processId"`
Chunk string `json:"chunk"` // base64 raw bytes
}
ProcessWriteParams is the request body for process/write. The `chunk` field name matches upstream codex's WriteParams (see codex-rs/exec-server/src/protocol.rs); writing with `data` here would 400 with "missing field `chunk`".
type RelayClient ¶
type RelayClient struct {
// contains filtered or unexported fields
}
RelayClient mints HTTPS relay tickets on the codex-exec-gateway's /api/exec-gateway/relay/create endpoint. Used by CopyPathTool to route file bytes around the bridge ws path.
nil-safe: when ExecGatewayInternalURL or InternalSecret is empty, CreateRelay returns a sentinel error and the caller falls back to the ws cat-pump path.
func NewRelayClient ¶
func NewRelayClient(baseURL, secret, workspaceID string, logger *slog.Logger) *RelayClient
func (*RelayClient) CreateRelay ¶
func (c *RelayClient) CreateRelay(ctx context.Context, srcExeID, dstExeID string, ttl time.Duration, maxBytes int64) (*RelayTicket, error)
CreateRelay mints a ticket good for one PUT + one GET on the gateway's /relay/<ticket> endpoint.
func (*RelayClient) Enabled ¶
func (c *RelayClient) Enabled() bool
Enabled reports whether the relay path can be used (both URL + secret present).