/** * Shared Chrome DevTools Protocol (CDP) primitives. * * Extracted from commands/browse.ts so both the CLI browse command and the * new BrowserSession (MCP/orchestrator-driven) reuse the same low-level * CDP code. No business logic here — pure WebSocket CDP helpers. */ import WebSocket from 'ws'; export interface CdpPage { ws: WebSocket; targetId: string; title: string; url: string; msgId: number; pending: Map void; reject: (e: Error) => void; }>; } export interface ElementInfo { i: number; tag: string; type: string; text: string; questionText: string; checked: boolean; x: number; y: number; group: number; } export interface EditorInfo { type: 'monaco' | 'cm5' | 'cm6' | 'ace' | 'quill' | 'prosemirror' | 'draft' | 'ck5' | 'generic'; selector: string; } export interface PageInfo { title: string; url: string; readyState: string; } /** One attachable page target as returned by /json/list. */ export interface AttachableTarget { id: string; title: string; url: string; webSocketDebuggerUrl: string; } /** All workable page targets (excludes chrome://, devtools://, extensions). */ export declare function listPageTargets(port: number): Promise; /** Visibility + focus of a single tab, read via a short-lived CDP session. */ export interface TabActivity { id: string; title: string; url: string; /** document.visibilityState === 'visible' (the active tab of its window). */ visible: boolean; /** document.hasFocus() (only the focused window's active tab). */ focused: boolean; } /** * Ask one tab whether it is visible/focused. Opens a throwaway WebSocket to the * page target, evaluates visibilityState/hasFocus(), closes. Never calls * Page.bringToFront - we observe, we don't steal the user's focus. */ export declare function probeTabActivity(target: AttachableTarget, timeoutMs?: number): Promise; /** * Choose the tab the human is actually looking at. We FOLLOW the active tab and * never activate one ourselves: the priority is * focused+visible > the single visible tab > the newest visible tab > * `prevTargetId` (still alive) > first page. * Returns null when the browser has no workable tab (caller can open one). * * A tab the site just opened and foregrounded is `visible`, so it is followed * without any `Page.bringToFront`. A tab that opened in the background stays * `hidden` and is ignored until the human (or the site) makes it active. */ export declare function pickActiveTab(port: number, prevTargetId?: string): Promise; /** * Resolve the page to bind to for an observation loop: the tab that is active * now (see `pickActiveTab`), falling back to the general attachable-page picker * only when no page can be probed (and that picker may open a blank tab). */ export declare function resolveActivePage(port: number, preferTargetId?: string): Promise; /** * Pick a page target worth attaching to. * * Chrome launched with no startup URL opens `chrome://newtab/`, which the old * `!url.startsWith('chrome://')` filter excluded - so a freshly launched debug * browser could never be attached to ("No browser tabs found"). Prefer a real * page, then accept a blank/new-tab, and finally open a blank tab ourselves so * a browser the user started on the NTP is still attachable. * * `preferTargetId` keeps the current tab when it is still open (re-attach after * a transient disconnect should not jump to a different tab). */ export declare function resolveAttachablePage(port: number, preferTargetId?: string): Promise; /** * Connect to the first attachable page on the given CDP port. * Returns a CdpPage handle with an open WebSocket. */ export declare function connectToPage(port: number, preferTargetId?: string): Promise; /** * Connect to a specific target by its WebSocket debugger URL. */ export declare function connectToWs(wsUrl: string, targetId: string, title: string, url: string): Promise; /** * List all open page targets on the given CDP port. */ export declare function listTargets(port: number): Promise>; /** * Send a CDP command and wait for the response. */ export declare function cdpSend(page: CdpPage, method: string, params: any): Promise; /** * Capture a screenshot of the current page. * Returns base64-encoded image data. */ export declare function captureScreenshot(page: CdpPage, format?: 'jpeg' | 'png', quality?: number): Promise; /** * Navigate the page to a URL. */ export declare function navigateTo(page: CdpPage, url: string): Promise; /** * Get the current page info (title, URL, readyState). */ export declare function getPageInfo(page: CdpPage): Promise; export interface PageText { title: string; url: string; text: string; truncated: boolean; } export interface PageLink { href: string; text: string; } /** * Extract the readable text of the current page. Prefers a semantic content * container (article/main/#content) and falls back to . Returns * whitespace-normalized text capped at maxChars. Text-only, so it is as cheap * token-wise as web_fetch while working on JS-heavy/blocked pages. */ export declare function getPageText(page: CdpPage, maxChars?: number): Promise; /** * Extract links (href + anchor text) from the page. Used to locate document * URLs (pdf/docx/xlsx/csv) so the research agent can download them. */ export declare function getPageLinks(page: CdpPage): Promise; /** * Extract all interactive elements from the DOM with their exact text, * bounding boxes, state, and question group assignments. */ export declare function extractElements(page: CdpPage): Promise; /** * Click at (x, y) coordinates. */ export declare function clickAt(page: CdpPage, x: number, y: number): Promise; /** * Type text using Input.insertText (handles multiline, unicode). */ export declare function typeText(page: CdpPage, text: string): Promise; /** * Press a keyboard key. */ export declare function pressKey(page: CdpPage, key: string): Promise; /** * Scroll the page by a delta (positive = down, negative = up). */ export declare function scrollPage(page: CdpPage, deltaY: number): Promise; /** * Evaluate a JavaScript expression in the page context. */ export declare function evaluateJs(page: CdpPage, expression: string): Promise; /** * Detect which code/rich-text editor is active on the page. */ export declare function detectEditor(page: CdpPage): Promise; /** * Focus an element by CSS selector. */ export declare function focusSelector(page: CdpPage, selector: string): Promise; export declare function sleep(ms: number): Promise; /** * Check if Chrome is reachable on the given port. */ export declare function isChromeReachable(port: number): Promise;