UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

151 lines 6.92 kB
/** * WebSocket PTY Bridge * * Bridges browser WebSocket connections to PTY sessions. Three modes: * * Phase 1 (local exec): spawns commands using node-pty. * Phase 2a (sandbox WS): bridges to agentic-sandbox management WebSocket. * Connects to the sandbox's management WS server, subscribes to an agent, * starts an interactive shell, and multicasts PTY output to all browser clients. * Leverages the sandbox's tokio broadcast channel for zero-copy multicast. * Phase 2b (sandbox REST, deprecated): polls /api/v1/tasks log endpoint. * * Browser ↔ serve protocol: * Client → Server: { type: 'data', payload: string } — stdin to PTY * Client → Server: { type: 'resize', cols: number, rows: number } * Client → Server: { type: 'close' } — request graceful shutdown * Server → Client: { type: 'data', payload: string } — stdout/stderr from PTY * Server → Client: { type: 'exit', code: number } — PTY process exited * Server → Client: { type: 'error', message: string } — error notification * * Sandbox management WS protocol (agentic-sandbox): * → { type: 'subscribe', agent_id } * → { type: 'start_shell', agent_id, cols, rows } * → { type: 'list_sessions', agent_id } — resolves session_name (#901) * → { type: 'send_input', agent_id, command_id, data } * → { type: 'pty_resize', agent_id, command_id, cols, rows } * → { type: 'kill_session', agent_id, session_name } — must use session_name, not command_id * ← { type: 'shell_started', agent_id, command_id } — idempotent: same cmd_id on reconnect (#903) * ← { type: 'session_list', agent_id, sessions[] } — provides session_name for kill (#901) * ← { type: 'output', agent_id, command_id, stream, data, ts, seq? } * ← { type: 'session_killed' | 'session_detached', agent_id, exit_code? } * * Replay-on-attach (#1144). After `shell_started`, AIWG sends: * → { type: 'join_session', agent_id, command_id, replay_from } * and the sandbox emits buffered output frames carrying `seq`. AIWG prepends * `\x1bc` (full terminal reset) ahead of the first replay frame so xterm.js * agrees with tmux on the starting cursor/screen state, eliminating the * first-frame alignment glitch. On unexpected disconnect, the bridge resumes * from the last observed seq instead of replaying from zero. * * Capability negotiation (operator review on #1144, 2026-05-07): the original * implementation gated this strictly on the sandbox advertising `join_session` * + `replay_buffer` in a server-hello banner (agentic-sandbox#190). That * banner doesn't ship today, so the gate kept replay disabled even on * sandboxes that fully support it. The bridge now uses an opportunistic * probe: if the banner explicitly signals support, replay is enabled; if the * banner is absent, the bridge sends `join_session` anyway and treats an * `error` response with an "unknown" / "not supported" / "session not found" * message as the negative signal. Either path is logged so an operator can * tell from `aiwg serve` output which mode the bridge is running in. * * @issue #712 * @issue #1144 — replay session history when AIWG attaches to running session * @see #657 — agentic-sandbox PTY transport * @see #711 — HTTP server scaffold */ export interface WsMessage { type: 'data' | 'resize' | 'close' | 'exit' | 'error'; payload?: string; message?: string; cols?: number; rows?: number; code?: number; } export interface PtySession { id: string; /** Connected WebSocket clients (wsId → ws) */ clients: Map<string, WebSocketLike>; /** Recent output buffer for reconnect replay (max OUTPUT_BUFFER_MAX chars) */ outputBuffer: string; /** Underlying IPty instance (node-pty) */ pty: PtyLike | null; /** Timestamp of last client disconnect (for cleanup) */ lastDisconnect: number; /** Whether the PTY process has exited */ exited: boolean; } /** Minimal WebSocket interface for dependency injection / testing */ export interface WebSocketLike { send(data: string): void; close(code?: number, reason?: string): void; readonly readyState: number; } /** Minimal IPty interface (subset of node-pty IPty) */ export interface PtyLike { write(data: string): void; resize(cols: number, rows: number): void; kill(signal?: string): void; onData(callback: (data: string) => void): void; onExit(callback: (event: { exitCode: number; }) => void): void; } export declare class PtySessionRegistry { private sessions; private cleanupTimer; constructor(); get(id: string): PtySession | undefined; create(id: string): PtySession; delete(id: string): void; addClient(sessionId: string, clientId: string, ws: WebSocketLike): void; removeClient(sessionId: string, clientId: string): void; appendOutput(sessionId: string, data: string): void; broadcast(sessionId: string, msg: WsMessage): void; private evictExpired; shutdown(): void; } export declare const registry: PtySessionRegistry; /** * Spawn a PTY process for the given session. * * Priority order: * 1. Explicit wsEndpoint opt → sandbox management WebSocket bridge * 2. Auto-detect: first connected sandbox in registry → sandbox WS bridge * 3. AIWG_SANDBOX_ENDPOINT env var → legacy REST polling (deprecated) * 4. Fallback → local node-pty * * @issue #657 — agentic-sandbox PTY transport */ export declare function spawnPty(session: PtySession, command: string, args: string[], opts?: { cols?: number; rows?: number; cwd?: string; sandboxEndpoint?: string; agentId?: string; /** agentic-sandbox management WebSocket URL (e.g. ws://localhost:8121) */ wsEndpoint?: string; }): Promise<void>; /** * Handle a new WebSocket connection for a PTY session. * * @param sessionId - PTY session ID from the URL path * @param ws - WebSocket-like interface * @param command - Command to spawn (default: 'aiwg') * @param args - Command arguments (default: ['mc', 'watch']) * @param cwd - Working directory * @param wsEndpoint - Optional: sandbox management WS URL for explicit agent targeting * @param agentId - Optional: sandbox agent ID to target (requires wsEndpoint) */ export declare function handlePtyConnection(sessionId: string, ws: WebSocketLike, command?: string, cmdArgs?: string[], cwd?: string, wsEndpoint?: string, agentId?: string): Promise<void>; /** * Create a Hono-compatible WebSocket event object for the PTY route. * * Used with `upgradeWebSocket` from `@hono/node-server/ws`: * * ```ts * app.get('/ws/pty/:sessionId', upgradeWebSocket((c) => createPtyWsHandler(c))); * ``` */ export declare function createPtyWsHandler(c: any): any; //# sourceMappingURL=pty-bridge.d.ts.map