/** * CDP Relay — bridges between Playwright and the Chrome extension. * * Based on Playwriter's proven relay pattern (github.com/remorses/playwriter). * Supports multiple concurrent Playwright/CDP clients, each with session-scoped * event routing. * * Passive/on-demand debugging: * Extension detects dev pages and announces them as "available" without attaching * the debugger. The relay requests debugging only when an agent needs CDP access * (via ensureDebugging()). After a period of inactivity (idle TTL), the relay * sends 'releaseDebug' to detach the debugger and stop the browser banner. * * WebSocket endpoints: * /__cdp-extension — extension connects here * /devtools/browser/* — Playwright clients connect here via connectOverCDP * * HTTP endpoints (Playwright discovery): * /json/version — browser info with rewritten webSocketDebuggerUrl * /json/list — list of attached targets * * Protocol (extension ↔ relay): * EXT→RELAY availability: { method: 'tabAvailable', params: { tabId, url, serverId, projectId } } * EXT→RELAY availability: { method: 'tabUnavailable', params: { tabId } } * RELAY→EXT control: { method: 'requestDebug', params: { tabId? } } * RELAY→EXT control: { method: 'releaseDebug', params: { tabId? } } * EXT→RELAY events: { method: 'forwardCDPEvent', params: { sessionId, method, params } } * EXT→RELAY responses: { id, result } or { id, error } * RELAY→EXT commands: { id, method: 'forwardCDPCommand', params: { sessionId, method, params } } * * CDP commands handled locally (not forwarded): * Browser.getVersion, Browser.setDownloadBehavior, Target.setDiscoverTargets, * Target.attachToTarget, Target.getTargetInfo, Target.getTargets * * CDP commands with special handling: * Target.setAutoAttach — forward, then synthesize Target.attachedToTarget for known targets * Runtime.enable — forward, then wait for executionContextCreated before responding */ import type { IncomingMessage } from 'node:http'; import type { Duplex } from 'node:stream'; import type { Page } from '@xmorse/playwright-core'; export interface CDPRelayOptions { gatewayPort: number; /** Idle timeout in ms before auto-releasing debugging. Default: 5 minutes. 0 = no auto-release. */ idleTimeoutMs?: number; } export declare class CDPRelay { private extensionWs; private extensionWss; private playwrightWss; private clients; private targets; private availableTabs; private pendingCommands; private nextCommandId; private gatewayPort; private events; private pwBrowser; private pwConnecting; private debuggingActive; private debuggingRequested; private idleTimeoutMs; private idleTimer; /** Cached refs from last a11y_snapshot call — maps ref string to element info */ refCache: Map | null; constructor(options: CDPRelayOptions); /** Debugging is active and has targets */ get isAvailable(): boolean; /** Extension is connected and has available tabs — debugging can be activated */ get canActivate(): boolean; /** * Ensure debugging is active. If not, request it from the extension and wait * for targets to appear. Returns true if debugging is active after the call. * Resets the idle timer on every call. */ ensureDebugging(): Promise; /** * Request the extension to start debugging all available tabs. * Waits up to 5s for at least one target to appear. */ requestDebugging(): Promise; /** * Release debugging — detach debugger from all tabs. * The extension will send Target.detachedFromTarget events. */ releaseDebugging(): void; private resetIdleTimer; private clearIdleTimer; /** Get all Playwright pages via the persistent CDP connection */ getPages(): Promise; /** Get a Playwright page, optionally matched by dev server port */ getPage(serverPort?: number): Promise; /** Get a CDP session for a page (using getExistingCDPSession, not newCDPSession) */ getCDPSession(page: Page): Promise; private ensurePlaywrightConnection; private disconnectPlaywright; handleUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): boolean; /** Handle CDP control actions. Returns response object or null if not matched. */ handleAction(url: string): object | null; handleHttp(url: string): object | null; private onExtensionConnect; private onExtensionMessage; private onPlaywrightConnect; private onPlaywrightMessage; private routeCommand; private sendToClient; private sendToExtension; close(): void; } //# sourceMappingURL=cdp-relay.d.ts.map