/** * routes/browser.ts, the daemon actually serving `browser.*`. * * The engine became platform capability when it was hoisted into the SDK, and * the daemon could link it from that moment. It still could not USE it: no * `browser.*` verb existed in the operator contract and no `/api/browser` path * was routed anywhere, so a caller with no surface process attached, a * schedule, a trigger, an inbound channel message, had nothing to invoke. A * capability the daemon can link but cannot call is a capability the operator * has to open a surface for. * * This module is the thin part, exactly as routes/calendar.ts and routes/email.ts * are: it maps the descriptors' declared input and output shapes onto a narrow * service slice and nothing else. It performs no I/O, opens no browser, and * imports nothing from `platform/browser`, the engine arrives through * `BrowserGatewayService`, so a test serves every verb from a fake with no * driver, no display and no process, and the daemon composition * (routes/browser-composition.ts) is the only place that knows the engine * exists. * * Four properties of the engine are preserved here by NOT re-deciding them: * * - **A browser the daemon did not start is never closed.** Ownership is * recorded in the session registry at connect time and `closeSession` * refuses an attached session there. This module does not second-guess it, * and `browser.sessions.release` is offered as the honest alternative. * - **`timeoutMs` bounds one call, never the browser.** Every timeout in the * schema is forwarded to the engine as a per-operation deadline. Nothing * here installs a request-scoped timer that closes a session, which would * be the "unrelated timeout killed the browser" defect rebuilt at the HTTP * layer. * - **`extract` cannot express code.** The caller names fields from a fixed * set; unknown names are dropped rather than interpreted, and the function * that runs in the page ships in the engine. * - **Untrusted page content cannot cause an outward effect.** The engine * refuses form submission once the turn has read page content, and the * refusal reaches the caller as a 403 carrying its own fix rather than * being flattened into a 500. */ import type { GatewayMethodCatalog } from '../method-catalog.js'; import type { GatewayMethodHandler } from '../method-catalog-shared.js'; /** Which session and page a page-scoped verb acts on. Both default to the open one. */ export interface BrowserGatewayTarget { readonly sessionId?: string | undefined; readonly pageId?: string | undefined; } /** Launch arguments carried on an ordinary call, so an implicit open matches the ask. */ export interface BrowserGatewayLaunchArgs { readonly profileName?: string | undefined; readonly headless?: boolean | undefined; } /** Every verb answers with an open record; the descriptors declare the shape. */ export type BrowserGatewayResult = Record; /** The extraction contract's field set. Nothing here can invoke anything. */ export type BrowserGatewayExtractField = 'text' | 'html' | 'value' | 'attributes'; /** * What a browser backend must be able to do to serve these verbs. * * Deliberately the whole engine surface rather than a convenient subset: a * daemon that can navigate but cannot select an option, press a key, or move * back is a daemon an operator still has to open a surface for. */ export interface BrowserGatewayService { status(): Promise; provision(options: { readonly repair?: boolean | undefined; readonly allowDownload?: boolean | undefined; }): Promise; listSessions(): Promise; launch(options: BrowserGatewayLaunchArgs): Promise; attach(options: { readonly cdpEndpoint: string; }): Promise; release(sessionId: string): Promise; close(sessionId: string): Promise; navigate(target: BrowserGatewayTarget, args: { readonly url: string; readonly waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | undefined; readonly timeoutMs?: number | undefined; readonly launch?: BrowserGatewayLaunchArgs | undefined; }): Promise; snapshot(target: BrowserGatewayTarget, args: { readonly limit?: number | undefined; }): Promise; click(target: BrowserGatewayTarget, args: { readonly ref: string; readonly button?: 'left' | 'right' | 'middle' | undefined; readonly clickCount?: number | undefined; readonly timeoutMs?: number | undefined; }): Promise; type(target: BrowserGatewayTarget, args: { readonly ref: string; readonly text: string; readonly submit?: boolean | undefined; readonly replace?: boolean | undefined; readonly timeoutMs?: number | undefined; }): Promise; select(target: BrowserGatewayTarget, args: { readonly ref: string; readonly values: readonly string[]; readonly timeoutMs?: number | undefined; }): Promise; press(target: BrowserGatewayTarget, args: { readonly ref: string; readonly key: string; readonly timeoutMs?: number | undefined; }): Promise; scroll(target: BrowserGatewayTarget, args: { readonly ref?: string | undefined; readonly direction?: 'up' | 'down' | undefined; readonly amount?: number | undefined; }): Promise; waitFor(target: BrowserGatewayTarget, args: { readonly text?: string | undefined; readonly url?: string | undefined; readonly timeoutMs?: number | undefined; }): Promise; readText(target: BrowserGatewayTarget, args: { readonly maxChars?: number | undefined; }): Promise; extract(target: BrowserGatewayTarget, args: { readonly ref?: string | undefined; readonly selector?: string | undefined; readonly fields?: readonly BrowserGatewayExtractField[] | undefined; readonly all?: boolean | undefined; readonly limit?: number | undefined; }): Promise; screenshot(target: BrowserGatewayTarget, args: { readonly fullPage?: boolean | undefined; readonly path?: string | undefined; }): Promise; tabs(target: BrowserGatewayTarget): Promise; newTab(target: BrowserGatewayTarget, args: { readonly url?: string | undefined; readonly launch?: BrowserGatewayLaunchArgs | undefined; }): Promise; switchTab(target: BrowserGatewayTarget, args: { readonly pageId: string; }): Promise; closeTab(target: BrowserGatewayTarget, args: { readonly pageId: string; }): Promise; goBack(target: BrowserGatewayTarget): Promise; goForward(target: BrowserGatewayTarget): Promise; } export declare function createBrowserGatewayHandlers(service: BrowserGatewayService): ReadonlyMap; /** Attach the browser handlers to their registered descriptors (missing = no-op). */ export declare function registerBrowserGatewayMethods(catalog: GatewayMethodCatalog, service: BrowserGatewayService): void; //# sourceMappingURL=browser.d.ts.map