/** * Shared Chrome DevTools Protocol primitives for the browser plugin. * * The brand's VNC Chromium is launched by platform/scripts/vnc.sh with * `--remote-debugging-port=${CDP_PORT}`, so CDP listens on 127.0.0.1:. * Everything here attaches to that running browser — it never launches its own. * * Two consumers share these primitives: * - cdp-render.ts → stateless one-shot render (create target, read DOM, close). * - cdp-actions.ts → interactive automation driven through a persistent * BrowserSession that keeps one target + websocket alive * across tool calls. * * `fetch` and `WebSocket` are injected (CdpDeps) so unit tests drive every * branch without a real browser; production passes the Node 22 globals. */ /** Minimal structural shape of the browser-style WebSocket this module uses. * Both the Node 22 global and the test fake satisfy it. */ export interface MinimalWebSocket { send(data: string): void; close(): void; addEventListener(type: "open", listener: () => void): void; addEventListener(type: "message", listener: (ev: { data: unknown; }) => void): void; addEventListener(type: "error", listener: (ev: unknown) => void): void; addEventListener(type: "close", listener: () => void): void; } export type WebSocketCtor = new (url: string) => MinimalWebSocket; export interface CdpDeps { fetchImpl: typeof fetch; /** The Node 22 global `WebSocket`, or undefined on a runtime that lacks it. */ WebSocketImpl: WebSocketCtor | undefined; cdpHost: string; cdpPort: number; log: (line: string) => void; } export declare const DEFAULT_LOAD_TIMEOUT_MS = 30000; export declare const DEFAULT_COMMAND_TIMEOUT_MS = 15000; /** Transport-level outcomes shared by every CDP operation. The action layer * adds its own operation-specific outcomes (selector-not-found, etc.). */ export type CdpOutcome = "ok" | "cdp-unreachable" | "websocket-unavailable" | "navigate-failed" | "load-timeout" | "evaluate-failed" | "command-timeout" | "session-lost" | "error"; export interface CdpTarget { id: string; webSocketDebuggerUrl: string; } /** Create a blank target via the CDP HTTP surface. Maps every transport * failure to a structured outcome rather than throwing. */ export declare function createTarget(deps: CdpDeps, timeoutMs: number): Promise<{ target: CdpTarget; } | { outcome: CdpOutcome; detail: string; }>; /** Best-effort target close. Failure is logged, never surfaced as an outcome. */ export declare function closeTarget(deps: CdpDeps, targetId: string): Promise; /** List the open page targets via the CDP HTTP surface. */ export declare function listTargets(deps: CdpDeps, timeoutMs: number): Promise<{ targets: Array; } | { outcome: CdpOutcome; detail: string; }>; /** * Drives one CDP WebSocket session: correlates command ids to results, resolves * one-shot events (waitForEvent), and fans persistent events out to subscribers * (subscribe) — the latter is what console buffering and dialog handling need. * * ws message ──▶ has id? ──yes──▶ resolve/reject pending command * │no * ▼ * has method? ──▶ one-shot waiter (consume) + every subscriber */ export declare class CdpSession { private readonly ws; private nextId; private readonly pending; private readonly eventWaiters; private readonly subscribers; private closed; constructor(ws: MinimalWebSocket); isClosed(): boolean; private onMessage; command(method: string, params: Record, timeoutMs: number): Promise; waitForEvent(method: string, timeoutMs: number): Promise; /** Persistent subscription to a CDP event. Returns an unsubscribe function. */ subscribe(method: string, handler: (params: unknown) => void): () => void; } export declare function openSocket(WebSocketImpl: WebSocketCtor, url: string, timeoutMs: number): Promise<{ ws: MinimalWebSocket; } | { error: string; }>; /** What the dialog listener should do with the next javascript dialog. */ export interface DialogPolicy { accept: boolean; promptText?: string; } /** * A single live page driven across many tool calls. * * Lifecycle: * attach() → create target, open ws, Page.enable + Runtime.enable, wire the * console ring buffer and the dialog auto-responder. * ensure() → return the live session, or null if the page died (ws closed: * operator closed the tab, page crashed, browser restarted). * close() → close the target and drop all state. * * The action layer treats a null ensure() as "session-lost" and tells the agent * to navigate again — only navigate() re-attaches, so a dead page is never * silently replaced by a blank one mid-action. */ export declare class BrowserSession { private readonly deps; private target; private ws; private session; private readonly consoleRing; private dialogPolicy; private lastDialogUnhandled; constructor(deps: CdpDeps); /** The live session if the page is still alive, else null. */ ensure(): CdpSession | null; /** Drop our handle on the current page (close the websocket) without closing * the target itself — used when switching the active page to another tab so * the previous tab stays open. */ private detachWs; currentTargetId(): string | null; setDialogPolicy(policy: DialogPolicy | null): void; /** Drain the console ring buffer (returns and clears). */ drainConsole(): string[]; /** True (and resets) if a dialog fired with no policy armed. */ takeUnhandledDialogFlag(): boolean; /** * Attach a fresh target and wire console + dialog handling. * Pass an existing target (from the tabs tool) to adopt it instead of creating one. */ attach(commandTimeoutMs: number, existing?: CdpTarget): Promise<{ session: CdpSession; } | { outcome: CdpOutcome; detail: string; }>; close(commandTimeoutMs: number): Promise; private recordConsole; private recordException; private push; } //# sourceMappingURL=cdp-session.d.ts.map