UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

114 lines 5.13 kB
/** * The agent's browser, streamed to a human (#802, part of #609). * * The browser hand-off gate (#796) parks an agent and asks someone to deal with a login wall or a * captcha. The browser it is parked on is headless and owned by the agent (#793), so there is * nothing for that person to click. This serves it: the latest screencast frame as MJPEG, and * clicks/keys back in over POST. * * Why the agent hosts this rather than the dashboard driving Chrome directly: Chrome refuses * DevTools socket connections carrying an `Origin` header unless launched with * `--remote-allow-origins`, and opening that up would let any page the user happens to visit * drive the agent's browser. The debug port stays unreachable from the web; this bridge is the * only way in. * * Why MJPEG rather than a WebSocket: an `<img>` renders `multipart/x-mixed-replace` natively * and input is a plain POST, so the dashboard needs no client library and the framework needs * no new dependency — Node's global WebSocket is enough to talk to Chrome. */ export interface BrowserStream { /** Where the dashboard points an `<img>` (`/stream`) and posts input (`/input`). */ url: string; /** The loopback port {@link url} is on, published on the agent's log so the daemon can proxy it (#813). */ port: number; /** Stop streaming and close the server. Safe to call twice. */ close(): Promise<void>; } /** One page Chrome is showing, from `/json/list`. */ export interface CdpPageTarget { id: string; type: string; url: string; webSocketDebuggerUrl?: string; } /** * The page a human should be looking at: the agent's current one. * * Chrome lists targets most-recently-used first, so the first `page` is the one the agent is * working in. Picking by position is what keeps the pane from going blind when the agent opens * a tab — the failure the spike hit. Ignores targets with no socket (a crashed or detached * tab) rather than returning something unusable. */ export declare function pickActivePage(targets: readonly CdpPageTarget[]): CdpPageTarget | undefined; /** The input a human can send back through the pane. Coordinates are in page pixels. */ export type BrowserInput = { type: 'click'; x: number; y: number; } | { type: 'key'; text: string; } | { type: 'scroll'; x: number; y: number; deltaY: number; } | { type: 'navigate'; url: string; }; /** A CDP call the bridge makes on the human's behalf. */ export interface CdpCall { method: string; params: Record<string, unknown>; } /** * The CDP calls one input maps to, or `[]` for anything unrecognized — a malformed POST must * never reach Chrome. A click is press + release (Chrome ignores a lone `mousePressed`), and * text goes through `insertText` so it types the character rather than a key code, which is * what makes non-ASCII and password managers behave. */ export declare function inputToCdp(input: BrowserInput): CdpCall[]; /** The MJPEG part header for one frame. */ export declare function framePart(boundary: string, jpeg: Buffer): Buffer; /** What the bridge needs from a CDP connection, so a test can stand in for Chrome. */ export interface CdpSession { send(method: string, params?: Record<string, unknown>): Promise<unknown>; on(event: 'Page.screencastFrame', handler: (params: { data: string; sessionId: number; }) => void): void; close(): void; } /** How the bridge reaches a page. Injectable: the real one speaks WebSocket to Chrome. */ export type CdpConnect = (webSocketDebuggerUrl: string) => Promise<CdpSession>; /** * Start the bridge. Returns undefined when Chrome has no page to stream — the caller carries * on without a pane rather than failing the agent. * * The stream is bound to loopback explicitly: the frames can contain whatever the human is * typing, including a password, so this must not be reachable from the network. For the same * reason no frame is ever written to disk or into the agent's event log. */ export declare function startBrowserStream(opts: { browserUrl: string; connect: CdpConnect; listTargets?: (browserUrl: string) => Promise<CdpPageTarget[]>; /** How often to check whether the agent moved to another tab. 0 disables following. */ followIntervalMs?: number; /** How often to re-send the newest frame so a still page still paints (#818). */ repeatIntervalMs?: number; /** * The page the human would be looking at changed (#1455 item 6b): fired for the first real * (http/https) page and on every change after — a navigation in place or a followed tab * switch. Never fired for about:blank or chrome:// (the browser idling is not the agent * showing something), and never twice for the same URL in a row. */ onPage?: (url: string) => void; }): Promise<BrowserStream | undefined>; /** * Talk CDP to Chrome over its debugger socket. Node's global WebSocket is enough, which is * what keeps this dependency-free. */ export declare const connectCdp: CdpConnect; //# sourceMappingURL=browser-stream.d.ts.map