browsirai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts","../src/upgrade.ts","../src/server.ts","../src/tools/index.ts","../src/cdp/connection.ts","../src/chrome-launcher.ts","../src/tools/browser-navigate.ts","../src/tools/browser-screenshot.ts","../src/tools/browser-tabs.ts","../src/tools/browser-eval.ts","../src/tools/browser-snapshot.ts","../src/tools/browser-click.ts","../src/tools/browser-scroll.ts","../src/tools/browser-html.ts","../src/tools/browser-navigate-back.ts","../src/tools/browser-press-key.ts","../src/tools/browser-hover.ts","../src/tools/browser-resize.ts","../src/tools/browser-close.ts","../src/tools/browser-fill-form.ts","../src/tools/browser-type.ts","../src/tools/browser-select-option.ts","../src/tools/browser-wait-for.ts","../src/tools/browser-drag.ts","../src/tools/browser-handle-dialog.ts","../src/tools/browser-file-upload.ts","../src/event-buffer.ts","../src/redactor.ts","../src/tools/browser-network-requests.ts","../src/tools/browser-console-messages.ts","../src/tools/browser-annotated-screenshot.ts","../src/tools/browser-inspect-source.ts","../src/tools/browser-intercept.ts","../src/tools/browser-find.ts","../src/tools/browser-diff.ts","../src/tools/browser-session-state.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\nexport const VERSION: string = pkg.version;\n","/**\n * Auto-upgrade system for browsirai.\n *\n * On MCP server start, checks npm registry for a newer version.\n * If found, performs a background upgrade (npm cache clean for npx,\n * npm install -g for global). Changes apply on next server restart.\n *\n * State is persisted to ~/.browsirai/upgrade.json with a 1-hour rate limit.\n */\n\nimport { spawn, execSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { VERSION } from \"./version.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UpgradeStatus {\n current: string;\n latest: string;\n checkedAt: string;\n installMethod: InstallMethod;\n}\n\nexport type InstallMethod = \"npx\" | \"global\" | \"dev\";\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nconst BROWSIR_DIR = join(homedir(), \".browsirai\");\nconst UPGRADE_FILE = join(BROWSIR_DIR, \"upgrade.json\");\nconst CHECK_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\n\n// ---------------------------------------------------------------------------\n// Install method detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect how browsirai was installed by inspecting the running script path.\n */\nexport function getInstallMethod(): InstallMethod {\n try {\n const scriptPath = fileURLToPath(import.meta.url);\n\n // Local dev: has .git sibling or is inside a src/ directory with tsconfig\n const parentDir = dirname(dirname(scriptPath));\n if (\n existsSync(join(parentDir, \".git\")) ||\n existsSync(join(parentDir, \"tsconfig.json\"))\n ) {\n return \"dev\";\n }\n\n // npx: path contains _npx or .npm/_cacache\n if (scriptPath.includes(\"_npx\") || scriptPath.includes(\".npm\")) {\n return \"npx\";\n }\n\n // Global: check if script is under npm global prefix\n try {\n const globalPrefix = execSync(\"npm prefix -g\", { stdio: \"pipe\" })\n .toString()\n .trim();\n if (scriptPath.startsWith(globalPrefix)) {\n return \"global\";\n }\n } catch {\n // Can't determine global prefix\n }\n\n // Default to npx (most common for MCP servers)\n return \"npx\";\n } catch {\n return \"npx\";\n }\n}\n\n/**\n * Get the resolved install path of browsirai.\n */\nexport function getInstallPath(): string {\n try {\n return dirname(dirname(fileURLToPath(import.meta.url)));\n } catch {\n return \"unknown\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Version comparison\n// ---------------------------------------------------------------------------\n\n/** Returns true if `latest` is newer than `current` (semver without deps). */\nfunction isNewer(current: string, latest: string): boolean {\n const a = current.split(\".\").map(Number);\n const b = latest.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) {\n if ((b[i] ?? 0) > (a[i] ?? 0)) return true;\n if ((b[i] ?? 0) < (a[i] ?? 0)) return false;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// State persistence\n// ---------------------------------------------------------------------------\n\n/** Read cached upgrade status (sync, for use in tool handlers). */\nexport function getUpgradeStatus(): UpgradeStatus | null {\n try {\n if (!existsSync(UPGRADE_FILE)) return null;\n return JSON.parse(readFileSync(UPGRADE_FILE, \"utf-8\")) as UpgradeStatus;\n } catch {\n return null;\n }\n}\n\nfunction writeUpgradeStatus(status: UpgradeStatus): void {\n try {\n mkdirSync(BROWSIR_DIR, { recursive: true });\n writeFileSync(UPGRADE_FILE, JSON.stringify(status, null, 2));\n } catch {\n // Non-critical — status just won't be cached\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core: check + upgrade\n// ---------------------------------------------------------------------------\n\n/**\n * Check npm registry for a newer version and perform background upgrade.\n * Non-blocking, all errors silently caught. Safe to fire-and-forget.\n */\nexport async function checkForUpgrade(): Promise<UpgradeStatus | null> {\n try {\n const method = getInstallMethod();\n\n // Skip in dev mode\n if (method === \"dev\") return null;\n\n // Rate limit: skip if checked within the last hour\n const cached = getUpgradeStatus();\n if (cached) {\n const elapsed = Date.now() - new Date(cached.checkedAt).getTime();\n if (elapsed < CHECK_INTERVAL_MS) return cached;\n }\n\n // Fetch latest version from npm registry (5s timeout)\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n\n const res = await fetch(\"https://registry.npmjs.org/browsirai/latest\", {\n signal: controller.signal,\n headers: { Accept: \"application/json\" },\n });\n clearTimeout(timeout);\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { version?: string };\n const latest = data.version;\n if (!latest) return null;\n\n const status: UpgradeStatus = {\n current: VERSION,\n latest,\n checkedAt: new Date().toISOString(),\n installMethod: method,\n };\n\n writeUpgradeStatus(status);\n\n // Perform background upgrade if newer version available\n if (isNewer(VERSION, latest)) {\n process.stderr.write(\n `browsirai: v${latest} available (current: v${VERSION}). Upgrading in background...\\n`,\n );\n\n if (method === \"npx\") {\n // Clear npx cache so next invocation fetches latest\n spawn(\"npm\", [\"cache\", \"clean\", \"--force\"], {\n stdio: \"ignore\",\n detached: true,\n }).unref();\n } else if (method === \"global\") {\n spawn(\"npm\", [\"install\", \"-g\", `browsirai@${latest}`], {\n stdio: \"ignore\",\n detached: true,\n }).unref();\n }\n }\n\n return status;\n } catch {\n // Never crash the server for an upgrade check\n return null;\n }\n}\n","/**\n * MCP Server setup for browsirai.\n *\n * Creates and configures an McpServer with all browser tools registered,\n * connected to the user's Chrome via CDP.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { VERSION } from \"./version.js\";\nimport { registerTools } from \"./tools/index.js\";\n\n/**\n * Create and return a fully configured MCP server instance with all\n * browser tools registered. The caller is responsible for connecting\n * a transport (e.g. StdioServerTransport).\n */\nexport async function createServer(): Promise<McpServer> {\n const server = new McpServer({\n name: \"browsirai\",\n version: VERSION,\n });\n\n registerTools(server);\n\n return server;\n}\n","/**\n * Tool registry: Zod schemas and MCP tool registration for all 29 browser tools.\n *\n * Wires real tool implementations to the MCP server with lazy CDP connection.\n * On first tool call, connects to Chrome via DevToolsActivePort or port 9222.\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { CDPConnection } from \"../cdp/connection.js\";\nimport { connectChrome, readDevToolsActivePort, isPortReachable, launchHeadlessChrome, needsCookieResync } from \"../chrome-launcher.js\";\n\n// Tool implementations\nimport { browserNavigate } from \"./browser-navigate.js\";\nimport { browserScreenshot } from \"./browser-screenshot.js\";\nimport { browserTabs } from \"./browser-tabs.js\";\nimport { browserEval } from \"./browser-eval.js\";\nimport { browserSnapshot } from \"./browser-snapshot.js\";\nimport { browserClick } from \"./browser-click.js\";\nimport { browserScroll } from \"./browser-scroll.js\";\nimport { browserHtml } from \"./browser-html.js\";\nimport { browserNavigateBack } from \"./browser-navigate-back.js\";\nimport { browserPressKey } from \"./browser-press-key.js\";\nimport { browserHover } from \"./browser-hover.js\";\nimport { browserResize } from \"./browser-resize.js\";\nimport { browserClose } from \"./browser-close.js\";\nimport { browserFillForm } from \"./browser-fill-form.js\";\nimport { browserType } from \"./browser-type.js\";\nimport { browserSelectOption } from \"./browser-select-option.js\";\nimport { browserWaitFor } from \"./browser-wait-for.js\";\nimport { browserDrag } from \"./browser-drag.js\";\nimport { browserHandleDialog } from \"./browser-handle-dialog.js\";\nimport { browserFileUpload } from \"./browser-file-upload.js\";\nimport { browserNetworkRequests, setupNetworkCapture, resetNetworkBuffer } from \"./browser-network-requests.js\";\nimport { browserConsoleMessages, setupConsoleCapture, resetConsoleBuffer } from \"./browser-console-messages.js\";\nimport { browserAnnotatedScreenshot } from \"./browser-annotated-screenshot.js\";\nimport { browserInspectSource } from \"./browser-inspect-source.js\";\nimport { browserRoute, browserAbort, browserUnroute } from \"./browser-intercept.js\";\nimport { browserFind } from \"./browser-find.js\";\nimport { browserDiff } from \"./browser-diff.js\";\nimport { browserSaveState, browserLoadState } from \"./browser-session-state.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy CDP connection — browser-level with session routing\n// ---------------------------------------------------------------------------\n\n/** Browser-level CDP connection */\nlet browserCdp: CDPConnection | null = null;\n/** Current page session ID for target-scoped commands */\nlet activeSessionId: string | null = null;\n\n/**\n * Session-aware CDP proxy that wraps the browser connection\n * and automatically adds sessionId to all commands.\n */\nclass SessionCDP {\n constructor(\n private conn: CDPConnection,\n private sessionId: string,\n ) {}\n\n get isConnected() { return this.conn.isConnected; }\n\n send(method: string, params?: Record<string, unknown>, options?: { timeout?: number; sessionId?: string }) {\n return this.conn.send(method, params, {\n ...options,\n sessionId: options?.sessionId ?? this.sessionId,\n });\n }\n\n on(event: string, handler: (...args: unknown[]) => void) {\n this.conn.on(event, handler);\n }\n\n off(event: string, handler: (...args: unknown[]) => void) {\n this.conn.off(event, handler);\n }\n\n close() { /* don't close browser connection */ }\n}\n\n/**\n * Get or create a session-scoped CDP connection.\n *\n * 1. Connect to browser WebSocket via DevToolsActivePort\n * 2. List targets → find first page\n * 3. Attach to page → get sessionId\n * 4. Return session-scoped proxy\n */\n/** Whether to launch Chrome headless (set via browser_connect { headless: true }) */\nlet headlessMode = process.env.BROWSIR_HEADLESS === \"1\" || process.env.BROWSIR_HEADLESS === \"true\";\n\n/** Attach crash/disconnect listeners to reset state when Chrome dies. */\nfunction attachLifecycleListeners(conn: CDPConnection): void {\n const resetState = () => {\n browserCdp = null;\n activeSessionId = null;\n resetConsoleBuffer();\n resetNetworkBuffer();\n };\n conn.on(\"reconnectionFailed\", resetState);\n conn.on(\"browserCrashed\", resetState);\n}\n\nasync function getCDP(): Promise<CDPConnection> {\n // Reuse existing session if still connected\n if (browserCdp?.isConnected && activeSessionId) {\n return new SessionCDP(browserCdp, activeSessionId) as unknown as CDPConnection;\n }\n\n // 1. Connect to Chrome\n // Headless mode: launch separate Chrome on port 9333 (doesn't touch user's Chrome)\n // Normal mode: connect to user's Chrome (auto-launch if needed)\n if (headlessMode) {\n const launch = await launchHeadlessChrome();\n if (!launch.success) {\n throw new Error(launch.error ?? \"Failed to launch headless Chrome.\");\n }\n const wsUrl = launch.wsEndpoint ?? `ws://127.0.0.1:${launch.port}/devtools/browser`;\n\n browserCdp = new CDPConnection(wsUrl);\n await browserCdp.connect();\n attachLifecycleListeners(browserCdp);\n\n const targets = await browserCdp.send(\"Target.getTargets\") as {\n targetInfos: Array<{ targetId: string; type: string; url: string; title: string }>;\n };\n\n let page = targets.targetInfos.find(t => t.type === \"page\");\n if (!page) {\n // Create a new tab in headless\n const created = await browserCdp.send(\"Target.createTarget\", { url: \"about:blank\" }) as { targetId: string };\n page = { targetId: created.targetId, type: \"page\", url: \"about:blank\", title: \"\" };\n }\n\n const attached = await browserCdp.send(\"Target.attachToTarget\", {\n targetId: page.targetId, flatten: true,\n }) as { sessionId: string };\n\n activeSessionId = attached.sessionId;\n const session = new SessionCDP(browserCdp, activeSessionId);\n await Promise.all([\n session.send(\"Network.enable\"),\n session.send(\"Runtime.enable\"),\n session.send(\"Page.enable\"),\n ]).catch(() => {});\n\n // Wire CDP event capture for console + network\n setupConsoleCapture(session as unknown as CDPConnection);\n setupNetworkCapture(session as unknown as CDPConnection);\n\n return session as unknown as CDPConnection;\n }\n\n const connection = await connectChrome({ autoLaunch: true });\n\n if (!connection.success) {\n throw new Error(\n connection.error ?? \"Cannot connect to Chrome. Run `browsirai doctor` to set up.\"\n );\n }\n\n const wsUrl = connection.wsEndpoint ?? `ws://127.0.0.1:${connection.port}/devtools/browser`;\n\n // 2. Connect to browser WebSocket\n browserCdp = new CDPConnection(wsUrl);\n await browserCdp.connect();\n attachLifecycleListeners(browserCdp);\n\n // 3. Find a page target\n const targets = await browserCdp.send(\"Target.getTargets\") as {\n targetInfos: Array<{ targetId: string; type: string; url: string; title: string }>;\n };\n\n let page = targets.targetInfos.find(\n (t) => t.type === \"page\" && !t.url.startsWith(\"chrome://\")\n ) ?? targets.targetInfos.find(\n (t) => t.type === \"page\"\n );\n\n if (!page) {\n // No tabs open — create one (same as headless mode)\n const created = await browserCdp.send(\"Target.createTarget\", { url: \"about:blank\" }) as { targetId: string };\n page = { targetId: created.targetId, type: \"page\", url: \"about:blank\", title: \"\" };\n }\n\n // 4. Attach to page target\n const attached = await browserCdp.send(\"Target.attachToTarget\", {\n targetId: page.targetId,\n flatten: true,\n }) as { sessionId: string };\n\n activeSessionId = attached.sessionId;\n\n // 5. Enable CDP domains for network/console capture\n const session = new SessionCDP(browserCdp, activeSessionId);\n await Promise.all([\n session.send(\"Network.enable\"),\n session.send(\"Runtime.enable\"),\n session.send(\"Page.enable\"),\n ]).catch(() => {/* non-fatal if already enabled */});\n\n // Wire CDP event capture for console + network\n setupConsoleCapture(session as unknown as CDPConnection);\n setupNetworkCapture(session as unknown as CDPConnection);\n\n return session as unknown as CDPConnection;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: format tool response\n// ---------------------------------------------------------------------------\n\nfunction textResult(text: string) {\n return { content: [{ type: \"text\" as const, text }] };\n}\n\nfunction imageResult(base64: string, mimeType: string = \"image/png\") {\n return {\n content: [{\n type: \"image\" as const,\n data: base64,\n mimeType,\n }],\n };\n}\n\nfunction errorResult(msg: string) {\n return { content: [{ type: \"text\" as const, text: `Error: ${msg}` }], isError: true };\n}\n\n// ---------------------------------------------------------------------------\n// SKILL injection (on connect) & per-tool hints\n// ---------------------------------------------------------------------------\n\nconst SKILL_SUMMARY = `Connected to Chrome via CDP.\n\n## browsirai — Quick Reference\n\n**Cost hierarchy (cheapest first):**\n1. \\`browser_evaluate\\` — JS expression for single values (~10 tokens). Use when you need one data point (count, text, attribute).\n2. \\`browser_snapshot\\` — accessibility tree with @eN refs (~500 tokens). Use for page understanding and interaction.\n3. \\`browser_screenshot { visual: true }\\` — full image (~10K tokens). ONLY for layout/colors/visual bugs.\n\n**Core workflow: snapshot → ref → interact → snapshot**\n\n1. \\`browser_snapshot\\` — get accessibility tree with @eN refs\n2. Use @eN refs with: \\`browser_click\\`, \\`browser_fill_form\\`, \\`browser_hover\\`, \\`browser_type\\`, \\`browser_select_option\\`, \\`browser_drag\\`, \\`browser_inspect_source\\`\n3. \\`browser_snapshot\\` — verify result (ALWAYS use this)\n\n**Cost optimization (enforced server-side):**\n- \\`browser_screenshot\\` without \\`visual: true\\` auto-returns snapshot text\n- For single data extraction: \\`browser_evaluate\\` > \\`browser_snapshot\\` (50x cheaper)\n- Reserve \\`browser_screenshot { visual: true }\\` for CSS/layout debugging only\n\n**Identity resolution (cookie-based, always-first):**\n- NEVER guess usernames. The browser has an active session — use it.\n- To find the logged-in user: \\`browser_evaluate\\` on the site (e.g. GitHub avatar menu, X profile link).\n- When asked \"go to my profile/repo/account\" → navigate to the site root first, extract identity from session, then proceed.\n- Cookie sync means the browser IS the user. Trust the session, not assumptions.\n\n**Key patterns:**\n- \\`browser_fill_form\\` clears existing value. Use \\`browser_type\\` to append.\n- \\`browser_type\\` with \\`submit: true\\` presses Enter after typing.\n- \\`browser_wait_for\\` — wait for text, selector, URL, or JS condition before proceeding.\n- \\`browser_inspect_source\\` — find source file, line, component name (React/Vue/Svelte, dev mode only).\n- \\`browser_resize\\` with \\`preset: \"reset\"\\` — restore native viewport.\n- Refs become stale after navigation or major DOM changes — take a new snapshot.\n- \\`browser_evaluate\\` cannot access cross-origin iframes — use \\`browser_type\\` instead.\n- \\`browser_console_messages\\` / \\`browser_network_requests\\` — check for errors after interactions.`;\n\nconst toolHints: Record<string, string> = {\n browser_navigate: \"\\n\\n→ Next: browser_evaluate for quick data extraction (~10 tokens), or browser_snapshot for page structure and @eN refs (~500 tokens).\",\n browser_navigate_back: \"\\n\\n→ Next: browser_snapshot to see updated page.\",\n browser_snapshot: \"\\n\\n→ Next: Use @eN refs with browser_click, browser_fill_form, browser_hover, browser_type, browser_select_option, browser_drag, or browser_inspect_source.\",\n browser_screenshot: \"\\n\\n→ Cost: This call was auto-optimized to snapshot text. For full image, pass { visual: true }. Prefer browser_snapshot (~500 tokens) over browser_screenshot (~10K tokens).\",\n browser_annotated_screenshot: \"\\n\\n→ Cost: ~12K tokens. Consider browser_snapshot { interactive: true } (~500 tokens) for same info as text.\",\n browser_click: \"\\n\\n→ Next: browser_snapshot to verify result. Refs may be stale after DOM changes.\",\n browser_fill_form: \"\\n\\n→ Next: browser_snapshot to verify. Note: fill_form clears existing value first.\",\n browser_type: \"\\n\\n→ Tip: Use submit: true to press Enter. Does NOT clear existing value (unlike fill_form).\",\n browser_press_key: \"\\n\\n→ Next: browser_snapshot to verify effect.\",\n browser_hover: \"\\n\\n→ Next: browser_snapshot to verify hover state. Use browser_screenshot { visual: true } only for visual hover effects.\",\n browser_drag: \"\\n\\n→ Next: browser_snapshot to verify drag result.\",\n browser_scroll: \"\\n\\n→ Next: browser_snapshot to see new viewport content.\",\n browser_select_option: \"\\n\\n→ Next: browser_snapshot to verify selection.\",\n browser_handle_dialog: \"\\n\\n→ Next: browser_snapshot to see page state after dialog.\",\n browser_file_upload: \"\\n\\n→ Next: browser_snapshot to verify upload.\",\n browser_wait_for: \"\\n\\n→ Next: browser_snapshot to see current page state.\",\n browser_resize: \"\\n\\n→ Next: browser_snapshot to verify viewport. Use browser_screenshot { visual: true } only to check visual layout. Use preset: \\\"reset\\\" to restore.\",\n browser_close: \"\\n\\n→ Next: browser_tabs to see remaining tabs.\",\n browser_evaluate: \"\\n\\n→ Tip: Cheapest tool (~10 tokens). Use for single data extraction (counts, text, attributes). Prefer browser_snapshot + @eN refs for DOM interaction. Cannot access cross-origin iframes.\",\n browser_html: \"\\n\\n→ Tip: Prefer browser_snapshot for structured page understanding with @eN refs.\",\n browser_tabs: \"\\n\\n→ Tip: Use browser_navigate to open a URL in current tab.\",\n browser_console_messages: \"\\n\\n→ Tip: Use level: \\\"error\\\" to filter for errors only.\",\n browser_network_requests: \"\\n\\n→ Tip: Use filter: \\\"*api*\\\" and includeStatic: false for API calls only.\",\n browser_inspect_source: \"\\n\\n→ Tip: Dev mode only. Use browser_snapshot first to get @eN refs for specific elements.\",\n browser_route: \"\\n\\n→ Tip: Use browser_network_requests to verify intercepted responses. Use browser_unroute to remove.\",\n browser_abort: \"\\n\\n→ Tip: Use browser_network_requests to verify blocked requests. Use browser_unroute to remove.\",\n browser_unroute: \"\\n\\n→ Tip: Use {all: true} to clear all intercepts at once.\",\n browser_find: \"\\n\\n→ Next: Use the @eN ref with browser_click, browser_fill_form, browser_hover, or browser_inspect_source.\",\n browser_diff: \"\\n\\n→ Tip: Use 'current' as before value to capture the page now. Make changes, then call again with the first result as before.\",\n browser_save_state: \"\\n\\n→ Tip: State saved to ~/.browsirai/states/. Use browser_load_state to restore later.\",\n browser_load_state: \"\\n\\n→ Next: browser_snapshot to see the restored page state.\",\n browser_connect: \"\", // SKILL_SUMMARY is returned directly\n browser_list: \"\\n\\n→ Tip: Use browser_connect to connect to a specific instance.\",\n};\n\nfunction appendHint(result: ToolResult, toolName: string): ToolResult {\n const hint = toolHints[toolName];\n if (!hint) return result;\n\n const lastTextIdx = result.content.findLastIndex((c) => c.type === \"text\");\n if (lastTextIdx >= 0) {\n result.content[lastTextIdx] = {\n ...result.content[lastTextIdx],\n text: (result.content[lastTextIdx].text ?? \"\") + hint,\n };\n } else {\n result.content.push({ type: \"text\" as const, text: hint.trimStart() });\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Schema definitions\n// ---------------------------------------------------------------------------\n\n// No coercion in schemas — keep strict for validation tests.\n// MCP string coercion happens in coerceArgs() at the handler level.\n\n/**\n * Coerce string values from MCP clients to proper JS types.\n * MCP transports may send all values as strings.\n */\nfunction coerceArgs(args: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(args)) {\n if (typeof v === \"string\") {\n if (v === \"true\") { out[k] = true; continue; }\n if (v === \"false\") { out[k] = false; continue; }\n const n = Number(v);\n if (v !== \"\" && !isNaN(n)) { out[k] = n; continue; }\n }\n out[k] = v;\n }\n return out;\n}\n\nconst browser_connect = z.object({\n port: z.number().optional(),\n host: z.string().optional(),\n headless: z.boolean().optional(),\n});\n\nconst browser_tabs = z.object({\n filter: z.string().optional(),\n});\n\nconst browser_snapshot = z.object({\n selector: z.string().optional(),\n compact: z.boolean().optional(),\n interactive: z.boolean().optional(),\n cursor: z.boolean().optional(),\n depth: z.number().optional(),\n});\n\nconst browser_screenshot = z.object({\n selector: z.string().optional(),\n fullPage: z.boolean().optional(),\n format: z.enum([\"png\", \"jpeg\"]).optional(),\n quality: z.number().optional(),\n annotate: z.boolean().optional(),\n visual: z.boolean().optional(),\n});\n\nconst browser_click = z\n .object({\n selector: z.string().optional(),\n ref: z.string().optional(),\n x: z.number().optional(),\n y: z.number().optional(),\n newTab: z.boolean().optional(),\n })\n .refine(\n (data) => {\n if (data.selector) return true;\n if (data.ref) return true;\n if (data.x !== undefined && data.y !== undefined) return true;\n return false;\n },\n { message: \"Must provide selector, ref, or both x and y coordinates\" },\n );\n\nconst browser_fill_form = z\n .object({\n ref: z.string().optional(),\n selector: z.string().optional(),\n value: z.string(),\n })\n .refine(\n (data) => data.ref !== undefined || data.selector !== undefined,\n { message: \"Must provide ref or selector\" },\n );\n\nconst browser_type = z.object({\n text: z.string(),\n ref: z.string().optional(),\n slowly: z.boolean().optional(),\n submit: z.boolean().optional(),\n});\n\nconst browser_press_key = z.object({\n key: z.string(),\n});\n\nconst browser_navigate = z.object({\n url: z.string(),\n waitUntil: z.enum([\"load\", \"domcontentloaded\", \"networkidle\"]).optional(),\n});\n\nconst browser_navigate_back = z.object({\n direction: z.enum([\"back\", \"forward\"]).optional(),\n});\n\nconst browser_evaluate = z.object({\n expression: z.string(),\n frameId: z.string().optional(),\n});\n\nconst browser_scroll = z.object({\n direction: z.enum([\"up\", \"down\", \"left\", \"right\"]).optional(),\n pixels: z.number().optional(),\n selector: z.string().optional(),\n});\n\nconst browser_network_requests = z.object({\n filter: z.string().optional(),\n limit: z.number().optional(),\n includeHeaders: z.boolean().optional(),\n includeStatic: z.boolean().optional(),\n});\n\nconst browser_console_messages = z.object({\n limit: z.number().optional(),\n level: z.enum([\"log\", \"warn\", \"error\", \"info\"]).optional(),\n});\n\nconst browser_html = z.object({\n selector: z.string().optional(),\n});\n\nconst browser_close = z.object({\n targetId: z.string().optional(),\n force: z.boolean().optional(),\n closeAll: z.boolean().optional(),\n});\n\nconst browser_wait_for = z.object({\n text: z.string().optional(),\n textGone: z.string().optional(),\n time: z.number().optional(),\n timeout: z.number().optional(),\n url: z.string().optional(),\n fn: z.string().optional(),\n selector: z.string().optional(),\n state: z.string().optional(),\n loadState: z.string().optional(),\n});\n\nconst browser_hover = z.object({\n ref: z.string(),\n});\n\nconst browser_drag = z.object({\n startRef: z.string(),\n endRef: z.string(),\n});\n\nconst browser_select_option = z.object({\n ref: z.string(),\n values: z.array(z.string()),\n});\n\nconst browser_handle_dialog = z.object({\n accept: z.boolean(),\n promptText: z.string().optional(),\n});\n\nconst browser_file_upload = z.object({\n ref: z.string(),\n paths: z.array(z.string()),\n});\n\nconst browser_resize = z\n .object({\n width: z.number().optional(),\n height: z.number().optional(),\n deviceScaleFactor: z.number().optional(),\n preset: z.string().optional(),\n })\n .refine(\n (data) => {\n if (data.preset) return true;\n if (data.width !== undefined && data.height !== undefined) return true;\n return false;\n },\n { message: \"Must provide both width and height together, or a preset\" },\n );\n\nconst browser_annotated_screenshot = z.object({\n selector: z.string().optional(),\n});\n\nconst browser_inspect_source = z\n .object({\n ref: z.string().optional(),\n selector: z.string().optional(),\n })\n .refine(\n (data) => data.ref !== undefined || data.selector !== undefined,\n { message: \"Must provide ref or selector\" },\n );\n\nconst browser_route = z.object({\n url: z.string(),\n body: z.string(),\n status: z.number().optional(),\n headers: z.record(z.string()).optional(),\n});\n\nconst browser_abort = z.object({\n url: z.string(),\n});\n\nconst browser_unroute = z\n .object({\n url: z.string().optional(),\n all: z.boolean().optional(),\n })\n .refine(\n (data) => data.url || data.all,\n { message: \"Must provide url or all\" },\n );\n\nconst browser_find = z\n .object({\n role: z.string().optional(),\n name: z.string().optional(),\n text: z.string().optional(),\n nth: z.number().optional(),\n })\n .refine(\n (data) => data.role !== undefined || data.name !== undefined || data.text !== undefined,\n { message: \"Must provide at least one of role, name, or text\" },\n );\n\nconst browser_diff = z.object({\n before: z.string(),\n after: z.string().optional(),\n selector: z.string().optional(),\n threshold: z.number().optional(),\n});\n\nconst browser_save_state = z.object({\n name: z.string(),\n});\n\nconst browser_load_state = z.object({\n name: z.string(),\n url: z.string().optional(),\n});\n\nconst browser_list = z\n .object({})\n .passthrough()\n .optional()\n .transform((val) => val ?? {});\n\n// ---------------------------------------------------------------------------\n// Exported schemas record\n// ---------------------------------------------------------------------------\n\nexport const schemas: Record<string, z.ZodType> = {\n browser_connect,\n browser_tabs,\n browser_snapshot,\n browser_screenshot,\n browser_click,\n browser_fill_form,\n browser_type,\n browser_press_key,\n browser_navigate,\n browser_navigate_back,\n browser_evaluate,\n browser_scroll,\n browser_network_requests,\n browser_console_messages,\n browser_html,\n browser_close,\n browser_wait_for,\n browser_hover,\n browser_drag,\n browser_select_option,\n browser_handle_dialog,\n browser_file_upload,\n browser_resize,\n browser_annotated_screenshot,\n browser_inspect_source,\n browser_route,\n browser_abort,\n browser_unroute,\n browser_find,\n browser_diff,\n browser_save_state,\n browser_load_state,\n browser_list,\n};\n\n// ---------------------------------------------------------------------------\n// Tool descriptions\n// ---------------------------------------------------------------------------\n\nconst descriptions: Record<string, string> = {\n browser_connect: \"Connect to a running browser instance via Chrome DevTools Protocol. Use headless: true to run in background without visible window. Use resync: true to force cookie re-sync (e.g. after switching Chrome profile).\",\n browser_tabs: \"List open browser tabs, optionally filtered by title or URL\",\n browser_snapshot: \"Capture an accessibility snapshot of the current page or a specific element. [~500 tokens, PREFERRED for page understanding]\",\n browser_screenshot: \"Take a screenshot. Auto-returns snapshot text unless visual: true, fullPage: true, or selector is specified. [~10K tokens when image returned]\",\n browser_click: \"Click an element identified by selector, ref, or coordinates\",\n browser_fill_form: \"Fill a form field identified by ref or selector with a value\",\n browser_type: \"Type text into the focused element or a specific ref\",\n browser_press_key: \"Press a keyboard key or key combination\",\n browser_navigate: \"Navigate to a URL\",\n browser_navigate_back: \"Navigate back or forward in browser history\",\n browser_evaluate: \"Evaluate a JavaScript expression in the page context\",\n browser_scroll: \"Scroll the page or a specific element in a given direction\",\n browser_network_requests: \"List captured network requests, optionally filtered\",\n browser_console_messages: \"Retrieve console messages from the page, optionally filtered by level\",\n browser_html: \"Get the HTML content of the page or a specific element\",\n browser_close: \"Close a browser tab or the entire browser\",\n browser_wait_for: \"Wait for a condition: text appearance/disappearance, time, URL, selector, or JS function\",\n browser_hover: \"Hover over an element identified by ref\",\n browser_drag: \"Drag from one element to another by ref\",\n browser_select_option: \"Select option(s) in a select element identified by ref\",\n browser_handle_dialog: \"Accept or dismiss a browser dialog (alert, confirm, prompt)\",\n browser_file_upload: \"Upload file(s) to a file input element identified by ref\",\n browser_resize: \"Resize the browser viewport to specific dimensions or a preset\",\n browser_annotated_screenshot: \"Take a screenshot with element annotations overlaid. [~12K tokens, prefer browser_snapshot { interactive: true } for text alternative]\",\n browser_inspect_source: \"Inspect a DOM element and return its source code location (file, line, component name). Works with React, Vue, Svelte, Solid.\",\n browser_route: \"Intercept requests matching a URL pattern and respond with a custom body, status, and headers\",\n browser_abort: \"Block requests matching a URL pattern\",\n browser_unroute: \"Remove request intercept rules — a specific pattern or all at once\",\n browser_find: \"Find elements by ARIA role, accessible name, or text content. Returns @eN ref for use with other tools.\",\n browser_diff: \"Compare two screenshots pixel-by-pixel. Returns diff percentage and visual diff image highlighting changes. [~11K tokens]\",\n browser_save_state: \"Save browser state (cookies, localStorage, sessionStorage) to a named file for later restoration\",\n browser_load_state: \"Load a previously saved browser state (cookies, storage) and optionally navigate to a URL\",\n browser_list: \"List available browser instances\",\n};\n\n// ---------------------------------------------------------------------------\n// Shape definitions for McpServer.tool() registration\n// ---------------------------------------------------------------------------\n// MCP clients (Claude Code, Cursor, etc.) send ALL values as strings.\n// z.coerce.number() handles this correctly: Number(\"500\") → 500\n// z.coerce.boolean() does NOT: Boolean(\"false\") → true (JS quirk)\n// So we use a custom cBool preprocess for booleans.\n\nconst cNum = z.coerce.number();\nconst cBool = z.preprocess(\n (v) => (v === \"true\" ? true : v === \"false\" ? false : v),\n z.boolean(),\n);\n\nconst toolShapes: Record<string, Record<string, z.ZodType>> = {\n browser_connect: { port: cNum.optional(), host: z.string().optional(), headless: cBool.optional(), resync: cBool.optional() },\n browser_tabs: { filter: z.string().optional() },\n browser_snapshot: {\n selector: z.string().optional(),\n compact: cBool.optional(),\n interactive: cBool.optional(),\n cursor: cBool.optional(),\n depth: cNum.optional(),\n },\n browser_screenshot: {\n selector: z.string().optional(),\n fullPage: cBool.optional(),\n format: z.enum([\"png\", \"jpeg\"]).optional(),\n quality: cNum.optional(),\n annotate: cBool.optional(),\n visual: cBool.optional(),\n },\n browser_click: {\n selector: z.string().optional(),\n ref: z.string().optional(),\n x: cNum.optional(),\n y: cNum.optional(),\n newTab: cBool.optional(),\n },\n browser_fill_form: { ref: z.string().optional(), selector: z.string().optional(), value: z.string() },\n browser_type: { text: z.string(), ref: z.string().optional(), slowly: cBool.optional(), submit: cBool.optional() },\n browser_press_key: { key: z.string() },\n browser_navigate: { url: z.string(), waitUntil: z.enum([\"load\", \"domcontentloaded\", \"networkidle\"]).optional() },\n browser_navigate_back: { direction: z.enum([\"back\", \"forward\"]).optional() },\n browser_evaluate: { expression: z.string(), frameId: z.string().optional() },\n browser_scroll: { direction: z.enum([\"up\", \"down\", \"left\", \"right\"]).optional(), pixels: cNum.optional(), selector: z.string().optional() },\n browser_network_requests: { filter: z.string().optional(), limit: cNum.optional(), includeHeaders: cBool.optional(), includeStatic: cBool.optional() },\n browser_console_messages: { limit: cNum.optional(), level: z.enum([\"log\", \"warn\", \"error\", \"info\"]).optional() },\n browser_html: { selector: z.string().optional() },\n browser_close: { targetId: z.string().optional(), force: cBool.optional(), closeAll: cBool.optional() },\n browser_wait_for: { text: z.string().optional(), textGone: z.string().optional(), time: cNum.optional(), timeout: cNum.optional(), url: z.string().optional(), fn: z.string().optional(), selector: z.string().optional(), state: z.string().optional(), loadState: z.string().optional() },\n browser_hover: { ref: z.string() },\n browser_drag: { startRef: z.string(), endRef: z.string() },\n browser_select_option: { ref: z.string(), values: z.array(z.string()) },\n browser_handle_dialog: { accept: cBool, promptText: z.string().optional() },\n browser_file_upload: { ref: z.string(), paths: z.array(z.string()) },\n browser_resize: { width: cNum.optional(), height: cNum.optional(), deviceScaleFactor: cNum.optional(), preset: z.string().optional() },\n browser_annotated_screenshot: { selector: z.string().optional() },\n browser_inspect_source: { ref: z.string().optional(), selector: z.string().optional() },\n browser_route: { url: z.string(), body: z.string(), status: cNum.optional(), headers: z.record(z.string()).optional() },\n browser_abort: { url: z.string() },\n browser_unroute: { url: z.string().optional(), all: cBool.optional() },\n browser_find: { role: z.string().optional(), name: z.string().optional(), text: z.string().optional(), nth: cNum.optional() },\n browser_diff: { before: z.string(), after: z.string().optional(), selector: z.string().optional(), threshold: cNum.optional() },\n browser_save_state: { name: z.string() },\n browser_load_state: { name: z.string(), url: z.string().optional() },\n browser_list: {},\n};\n\n// ---------------------------------------------------------------------------\n// Tool handlers — wired to real implementations\n// ---------------------------------------------------------------------------\n\ntype ToolResult = { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean };\ntype ToolHandler = (args: Record<string, unknown>) => Promise<ToolResult>;\n\nfunction createHandlers(): Record<string, ToolHandler> {\n return {\n // --- Core tools with real implementations ---\n\n browser_navigate: async (args) => {\n try {\n // Navigate-hook: resync cookies if user's Chrome profile or cookies changed\n if (needsCookieResync() && browserCdp?.isConnected) {\n browserCdp.close();\n browserCdp = undefined as any;\n activeSessionId = undefined as any;\n }\n const conn = await getCDP();\n const result = await browserNavigate(conn, args as any);\n return textResult(`Navigated to ${result.url}\\nTitle: ${result.title}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_screenshot: async (args) => {\n try {\n const conn = await getCDP();\n // Cost optimization: auto-downgrade to snapshot when no visual need\n const needsImage = args.visual === true || args.fullPage === true\n || args.selector || args.annotate === true;\n if (!needsImage) {\n const snap = await browserSnapshot(conn, {});\n const text = typeof snap === \"string\" ? snap : (snap as any).snapshot ?? JSON.stringify(snap, null, 2);\n return textResult(`[auto-optimized: snapshot returned — pass visual: true for image]\\n\\n${text}`);\n }\n const result = await browserScreenshot(conn, args as any);\n const mimeType = (args.format === \"jpeg\") ? \"image/jpeg\" : \"image/png\";\n const content: any[] = [{\n type: \"image\",\n data: result.base64,\n mimeType,\n }];\n if (result.annotations?.length) {\n content.push({\n type: \"text\",\n text: result.annotations.map(a => `${a.label} ${a.role}: ${a.name} (${a.ref})`).join(\"\\n\"),\n });\n }\n return { content };\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_tabs: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserTabs(conn, args as any);\n const lines = result.tabs.map(t => `[${t.id}] ${t.title}\\n ${t.url}`);\n return textResult(lines.length ? lines.join(\"\\n\\n\") : \"No tabs found\");\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_evaluate: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserEval(conn, args as any);\n if (result.error) return errorResult(result.error);\n return textResult(JSON.stringify(result.result, null, 2));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_snapshot: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserSnapshot(conn, args as any);\n return textResult(typeof result === \"string\" ? result : JSON.stringify(result, null, 2));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_click: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserClick(conn, args as any);\n return textResult(typeof result === \"string\" ? result : JSON.stringify(result));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_scroll: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserScroll(conn, args as any);\n return textResult(typeof result === \"string\" ? result : JSON.stringify(result));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_html: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserHtml(conn, args as any);\n return textResult(typeof result === \"string\" ? result : JSON.stringify(result));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_connect: async (args) => {\n try {\n const typed = args as { headless?: boolean; resync?: boolean };\n const wasHeadless = headlessMode;\n headlessMode = typed.headless === true;\n // Reset connection when switching modes or resync requested (e.g. user switched Chrome profile)\n if ((wasHeadless !== headlessMode || typed.resync === true) && browserCdp?.isConnected) {\n browserCdp.close();\n browserCdp = undefined as any;\n activeSessionId = undefined as any;\n }\n const conn = await getCDP();\n const mode = headlessMode ? \" (headless)\" : \"\";\n let summary = SKILL_SUMMARY.replace(\"Connected to Chrome via CDP.\", `Connected to Chrome via CDP${mode}.`);\n\n // Append upgrade notice if a newer version is available\n try {\n const { getUpgradeStatus } = await import(\"../upgrade.js\");\n const status = getUpgradeStatus();\n if (status && status.latest !== status.current) {\n summary += `\\n\\n⚠️ browsirai v${status.latest} available (current: v${status.current}). Restart to apply.`;\n }\n } catch { /* ignore */ }\n\n return textResult(summary);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_list: async () => {\n try {\n if (!browserCdp?.isConnected) {\n await getCDP();\n }\n const targets = await browserCdp!.send(\"Target.getTargets\") as {\n targetInfos: Array<{ targetId: string; type: string; title: string; url: string }>;\n };\n const pages = targets.targetInfos.filter(t => t.type === \"page\");\n const lines = pages.map(t => `[${t.targetId}] ${t.title}\\n ${t.url}`);\n return textResult(lines.length ? lines.join(\"\\n\\n\") : \"No pages found\");\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n // --- Newly wired tools ---\n\n browser_navigate_back: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserNavigateBack(conn, args as any);\n return textResult(result.url ? `Navigated ${args.direction || \"back\"} to ${result.url}` : `Navigated ${args.direction || \"back\"}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_press_key: async (args) => {\n try {\n const conn = await getCDP();\n await browserPressKey(conn, args as any);\n return textResult(`Pressed key: ${args.key}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_type: async (args) => {\n try {\n const conn = await getCDP();\n await browserType(conn, args as any);\n return textResult(`Typed ${(args.text as string).length} characters`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_fill_form: async (args) => {\n try {\n const conn = await getCDP();\n // MCP schema sends flat {ref?, selector?, value} — wrap into fields array\n const fields = [{\n name: (args.selector as string) ?? (args.ref as string) ?? \"field\",\n type: \"textbox\",\n ref: args.ref as string | undefined,\n selector: args.selector as string | undefined,\n value: args.value as string,\n }];\n await browserFillForm(conn, { fields } as any);\n return textResult(`Filled form field with value`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_hover: async (args) => {\n try {\n const conn = await getCDP();\n await browserHover(conn, args as any);\n return textResult(`Hovered over element`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_drag: async (args) => {\n try {\n const conn = await getCDP();\n await browserDrag(conn, args as any);\n return textResult(`Dragged from ${args.startRef} to ${args.endRef}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_select_option: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserSelectOption(conn, args as any);\n return textResult(`Selected: ${result.selected.join(\", \")}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_handle_dialog: async (args) => {\n try {\n const conn = await getCDP();\n await browserHandleDialog(conn, args as any);\n return textResult(`Dialog ${args.accept ? \"accepted\" : \"dismissed\"}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_file_upload: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserFileUpload(conn, args as any);\n return textResult(`Uploaded ${result.filesCount} file(s)`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_wait_for: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserWaitFor(conn, args as any);\n return textResult(`Wait completed in ${result.elapsed}ms`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_resize: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserResize(conn, args as any);\n return textResult(`Resized to ${result.width}x${result.height}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_close: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserClose(conn, args as any);\n return textResult(`Closed ${result.closedTargets} tab(s)`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_network_requests: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserNetworkRequests(conn, args as any);\n const lines = result.requests.map(r => `${r.method} ${r.status ?? \"?\"} ${r.url}`);\n return textResult(lines.length ? lines.join(\"\\n\") : \"No network requests captured\");\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_console_messages: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserConsoleMessages(conn, args as any);\n const lines = result.messages.map(m => `[${m.level}] ${m.text}`);\n return textResult(lines.length ? lines.join(\"\\n\") : \"No console messages\");\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_inspect_source: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserInspectSource(conn, args as any);\n const lines: string[] = [];\n lines.push(`Element: <${result.tagName}>`);\n if (result.componentName) {\n lines.push(`Component: ${result.componentName}`);\n }\n if (result.source) {\n lines.push(`Source: ${result.source.filePath}:${result.source.lineNumber ?? \"?\"}:${result.source.columnNumber ?? \"?\"}`);\n } else {\n lines.push(\"Source: not found (no framework metadata detected)\");\n }\n if (result.stack.length > 0) {\n lines.push(\"\\nComponent Stack:\");\n for (const frame of result.stack) {\n const loc = `${frame.filePath}:${frame.lineNumber ?? \"?\"}`;\n lines.push(` ${frame.componentName ?? \"anonymous\"} (${loc})`);\n }\n }\n return textResult(lines.join(\"\\n\"));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_route: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserRoute(conn, args as any);\n return textResult(`Route registered: ${result.url} → ${result.status}\\nActive routes: ${result.activeRoutes}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_abort: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserAbort(conn, args as any);\n return textResult(`Abort registered: ${result.url}\\nActive aborts: ${result.activeAborts}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_unroute: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserUnroute(conn, args as any);\n return textResult(`Removed ${result.removed} rule(s)\\nRemaining: ${result.remaining}`);\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_annotated_screenshot: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserAnnotatedScreenshot(conn, args as any);\n const content: any[] = [{\n type: \"image\",\n data: result.base64,\n mimeType: \"image/png\",\n }];\n if (result.annotations?.length) {\n content.push({\n type: \"text\",\n text: result.annotations.map(a => `${a.label} ${a.role}: ${a.name} (${a.ref})`).join(\"\\n\"),\n });\n }\n return { content };\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_diff: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserDiff(conn, args as any);\n const content: any[] = [{\n type: \"image\",\n data: result.diffImage,\n mimeType: \"image/png\",\n }, {\n type: \"text\",\n text: [\n `Diff: ${result.diffPercentage}% changed`,\n `Pixels: ${result.diffPixels} / ${result.totalPixels} differ`,\n `Dimensions: ${result.width}x${result.height}`,\n `Identical: ${result.identical}`,\n ].join(\"\\n\"),\n }];\n return { content };\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_find: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserFind(conn, args as any);\n if (!result.found) {\n return textResult(`No match found (${result.count} candidates)`);\n }\n const lines: string[] = [];\n lines.push(`Found: ${result.ref ?? \"no ref\"}`);\n lines.push(`Role: ${result.role ?? \"unknown\"}`);\n lines.push(`Name: ${result.name ?? \"\"}`);\n lines.push(`Matches: ${result.count}`);\n return textResult(lines.join(\"\\n\"));\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_save_state: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserSaveState(conn, args as any);\n return textResult(\n `State \"${result.name}\" saved to ${result.path}\\n` +\n `Cookies: ${result.cookies}, localStorage: ${result.localStorage}, sessionStorage: ${result.sessionStorage}`\n );\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n\n browser_load_state: async (args) => {\n try {\n const conn = await getCDP();\n const result = await browserLoadState(conn, args as any);\n return textResult(\n `State \"${result.name}\" restored\\n` +\n `Cookies: ${result.cookies}, localStorage: ${result.localStorage}, sessionStorage: ${result.sessionStorage}`\n );\n } catch (e: any) {\n return errorResult(e.message);\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tool registration\n// ---------------------------------------------------------------------------\n\n/**\n * Register all browser tools on the given McpServer instance.\n * Core tools (navigate, screenshot, tabs, evaluate, snapshot, click, scroll, html)\n * are wired to real CDP implementations. Others return stubs.\n */\nexport function registerTools(\n server: McpServer,\n _manager?: unknown,\n): void {\n const handlers = createHandlers();\n const toolNames = Object.keys(schemas);\n\n for (const name of toolNames) {\n const description = descriptions[name] ?? name;\n const shape = toolShapes[name] ?? {};\n\n const rawHandler = handlers[name] ?? (async () =>\n textResult(`${name}: not yet implemented`)\n );\n\n const handler: ToolHandler = async (args) => {\n const coerced = coerceArgs(args as Record<string, unknown>);\n const result = await rawHandler(coerced);\n return appendHint(result, name);\n };\n\n (server as any).tool(name, description, shape, handler);\n }\n}\n","/**\n * CDP Connection — WebSocket-based Chrome DevTools Protocol client.\n *\n * Handles JSON-RPC command/response correlation, CDP event dispatch,\n * timeouts, reconnection on crash, and clean shutdown.\n *\n * @module\n */\n\nexport { waitForDocumentReady } from \"./wait-ready.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default command timeout in milliseconds. */\nexport const TIMEOUT = 15_000;\n\n/** Navigation timeout in milliseconds. */\nexport const NAVIGATION_TIMEOUT = 30_000;\n\n/** Idle timeout in milliseconds (20 minutes). */\nexport const IDLE_TIMEOUT = 1_200_000;\n\n/** Maximum reconnection retries when connecting via daemon. */\nexport const DAEMON_CONNECT_RETRIES = 20;\n\n/** Delay between daemon connection retries in milliseconds. */\nexport const DAEMON_CONNECT_DELAY = 300;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Options for sending a CDP command. */\nexport interface CDPCommandOptions {\n /** Timeout in ms for this specific command. */\n timeout?: number;\n /** Session ID for target-scoped commands. */\n sessionId?: string;\n}\n\n/** Internal pending command tracker. */\ninterface PendingCommand {\n resolve: (result: unknown) => void;\n reject: (error: Error) => void;\n method: string;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/** Shape of a parsed incoming CDP message. */\ninterface CDPMessage {\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: unknown;\n error?: { code: number; message: string };\n}\n\n// ---------------------------------------------------------------------------\n// Minimal WebSocket interface (Node.js 22 built-in + browser compatible)\n// ---------------------------------------------------------------------------\n\ninterface MinimalWebSocket {\n readyState: number;\n send(data: string): void;\n close(): void;\n addEventListener(event: string, handler: (...args: unknown[]) => void): void;\n removeEventListener(event: string, handler: (...args: unknown[]) => void): void;\n}\n\n// ---------------------------------------------------------------------------\n// CDPConnection\n// ---------------------------------------------------------------------------\n\ntype EventHandler = (...args: unknown[]) => void;\n\n/**\n * WebSocket-based CDP client.\n *\n * ```ts\n * const conn = new CDPConnection(\"ws://127.0.0.1:9222/devtools/browser/abc\");\n * await conn.connect();\n * const result = await conn.send(\"Target.getTargets\");\n * conn.close();\n * ```\n */\nexport class CDPConnection {\n private readonly wsUrl: string;\n private ws: MinimalWebSocket | null = null;\n private nextId = 1;\n private readonly pending = new Map<number, PendingCommand>();\n private readonly eventHandlers = new Map<string, Set<EventHandler>>();\n private closed = false;\n private reconnecting = false;\n private _connected = false;\n\n // Bound listener references for cleanup\n private boundOnMessage: ((...args: unknown[]) => void) | null = null;\n private boundOnClose: ((...args: unknown[]) => void) | null = null;\n private boundOnError: ((...args: unknown[]) => void) | null = null;\n\n constructor(wsUrl: string) {\n this.wsUrl = wsUrl;\n }\n\n // -----------------------------------------------------------------------\n // Public API\n // -----------------------------------------------------------------------\n\n /** Whether the underlying WebSocket is currently open. */\n get isConnected(): boolean {\n return this._connected;\n }\n\n /**\n * Open the WebSocket connection.\n * Resolves on the `open` event; rejects on `error`.\n */\n async connect(): Promise<void> {\n // Use native WebSocket (Node 22+) or fall back to `ws` package\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let WS = (globalThis as any).WebSocket;\n if (!WS) {\n try {\n // Dynamic import so it's not required when native WebSocket exists\n const wsModule = await import(\"ws\");\n WS = wsModule.default ?? wsModule.WebSocket ?? wsModule;\n } catch {\n throw new Error(\n \"No WebSocket implementation found. Install the `ws` package or use Node 22+.\"\n );\n }\n }\n const ws: MinimalWebSocket = new WS(this.wsUrl);\n this.ws = ws;\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n ws.removeEventListener(\"open\", onOpen);\n ws.removeEventListener(\"error\", onError);\n resolve();\n };\n\n const onError = (ev: unknown) => {\n ws.removeEventListener(\"open\", onOpen);\n ws.removeEventListener(\"error\", onError);\n const msg =\n (ev as { message?: string })?.message ?? \"WebSocket error\";\n reject(new Error(msg));\n };\n\n ws.addEventListener(\"open\", onOpen);\n ws.addEventListener(\"error\", onError);\n });\n\n this._connected = true;\n this.attachListeners(ws);\n }\n\n /**\n * Send a CDP command and await its result.\n *\n * @param method CDP method (e.g. `\"Runtime.evaluate\"`)\n * @param params Optional method parameters\n * @param options Optional timeout / sessionId overrides\n */\n send(\n method: string,\n params?: Record<string, unknown>,\n options?: CDPCommandOptions,\n ): Promise<unknown> {\n if (this.closed || !this._connected || !this.ws) {\n return Promise.reject(\n new Error(`Connection closed — cannot send ${method}`),\n );\n }\n\n const id = this.nextId++;\n const timeout = options?.timeout ?? TIMEOUT;\n\n // Build the JSON-RPC message\n const message: Record<string, unknown> = { id, method };\n if (params !== undefined) {\n message.params = params;\n }\n if (options?.sessionId !== undefined) {\n message.sessionId = options.sessionId;\n }\n\n this.ws.send(JSON.stringify(message));\n\n return new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`CDP command timeout: ${method} (${timeout}ms)`));\n }, timeout);\n\n this.pending.set(id, { resolve, reject, method, timer });\n });\n }\n\n /**\n * Register an event handler for a CDP event or lifecycle event.\n *\n * CDP events are dispatched as `handler(params, { method })`.\n * Lifecycle events: `disconnected`, `browserCrashed`, `reconnected`,\n * `reconnectionFailed`.\n */\n on(event: string, handler: EventHandler): void {\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.eventHandlers.set(event, handlers);\n }\n handlers.add(handler);\n }\n\n /** Remove a previously registered event handler. */\n off(event: string, handler: EventHandler): void {\n this.eventHandlers.get(event)?.delete(handler);\n }\n\n /**\n * Close the connection. Suppresses reconnection.\n * Safe to call multiple times.\n */\n close(): void {\n this.closed = true;\n this._connected = false;\n\n this.rejectAllPending(new Error(\"Connection closed\"));\n\n if (this.ws) {\n this.detachListeners(this.ws);\n try {\n this.ws.close();\n } catch {\n // Already closed or errored — ignore.\n }\n this.ws = null;\n }\n }\n\n // -----------------------------------------------------------------------\n // Private\n // -----------------------------------------------------------------------\n\n /** Attach message / close / error listeners to the WebSocket. */\n private attachListeners(ws: MinimalWebSocket): void {\n this.boundOnMessage = (event: unknown) => {\n const data = (event as { data?: string })?.data;\n if (typeof data !== \"string\") return;\n\n let msg: CDPMessage;\n try {\n msg = JSON.parse(data) as CDPMessage;\n } catch {\n return;\n }\n\n // --- Command response (has `id`) ---\n if (msg.id !== undefined) {\n const entry = this.pending.get(msg.id);\n if (entry) {\n this.pending.delete(msg.id);\n clearTimeout(entry.timer);\n if (msg.error) {\n entry.reject(new Error(msg.error.message));\n } else {\n entry.resolve(msg.result);\n }\n }\n return;\n }\n\n // --- CDP event (has `method`, no `id`) ---\n if (msg.method) {\n this.emit(msg.method, msg.params ?? {}, { method: msg.method });\n }\n };\n\n this.boundOnClose = (event: unknown) => {\n const code = (event as { code?: number })?.code ?? 1006;\n this._connected = false;\n\n // Reject all in-flight commands\n this.rejectAllPending(new Error(\"WebSocket disconnected — connection closed\"));\n\n // Abnormal close → browser crash\n if (code !== 1000) {\n this.emit(\"browserCrashed\");\n }\n\n // Always emit disconnected\n this.emit(\"disconnected\");\n\n // Reconnect on abnormal close unless user called close()\n if (!this.closed && code !== 1000) {\n this.attemptReconnection().catch(() => {\n // Swallow — reconnectionFailed event already emitted\n });\n }\n };\n\n this.boundOnError = () => {\n // Errors during an established connection surface via the close event.\n };\n\n ws.addEventListener(\"message\", this.boundOnMessage);\n ws.addEventListener(\"close\", this.boundOnClose);\n ws.addEventListener(\"error\", this.boundOnError);\n }\n\n /** Detach WebSocket listeners. */\n private detachListeners(ws: MinimalWebSocket): void {\n if (this.boundOnMessage) ws.removeEventListener(\"message\", this.boundOnMessage);\n if (this.boundOnClose) ws.removeEventListener(\"close\", this.boundOnClose);\n if (this.boundOnError) ws.removeEventListener(\"error\", this.boundOnError);\n }\n\n /** Reject every pending command. */\n private rejectAllPending(error: Error): void {\n for (const [id, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(error);\n this.pending.delete(id);\n }\n }\n\n /** Dispatch an event to all registered handlers. */\n private emit(event: string, ...args: unknown[]): void {\n const handlers = this.eventHandlers.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch {\n // Swallow handler errors.\n }\n }\n }\n\n /** Attempt reconnection with retries after abnormal close. */\n private async attemptReconnection(): Promise<void> {\n if (this.reconnecting || this.closed) return;\n this.reconnecting = true;\n\n for (let attempt = 0; attempt < DAEMON_CONNECT_RETRIES; attempt++) {\n if (this.closed) {\n this.reconnecting = false;\n return;\n }\n\n await this.delay(DAEMON_CONNECT_DELAY);\n\n if (this.closed) {\n this.reconnecting = false;\n return;\n }\n\n try {\n await this.connect();\n this.reconnecting = false;\n this.emit(\"reconnected\");\n return;\n } catch {\n // Will retry on next iteration.\n }\n }\n\n this.reconnecting = false;\n this.emit(\"reconnectionFailed\");\n }\n\n /** Promise-based delay. */\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * Chrome connection — connects to Chrome via CDP.\n *\n * Strategy (ordered by preference):\n * 1. If Chrome is already running with --remote-debugging-port → connect via DevToolsActivePort\n * 2. If Chrome is running without debugging → quit & relaunch with --remote-debugging-port\n * 3. If Chrome is not running → launch with --remote-debugging-port\n *\n * Using --remote-debugging-port avoids the Chrome M144 \"Allow remote debugging?\" modal\n * entirely. The default user data directory is preserved, so cookies, logins, tabs,\n * and extensions remain intact.\n */\n\nimport { execSync, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from \"node:fs\";\nimport http from \"node:http\";\nimport { join } from \"node:path\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { createConnection } from \"node:net\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ConnectOptions {\n /** CDP port override (normally read from DevToolsActivePort) */\n port?: number;\n /** If true, auto-launch Chrome with --remote-debugging-port when not connected */\n autoLaunch?: boolean;\n /** If true, launch Chrome in headless mode (no visible window) */\n headless?: boolean;\n}\n\nexport interface ConnectResult {\n /** Whether connection to Chrome succeeded */\n success: boolean;\n /** Port Chrome is listening on */\n port: number;\n /** Full WebSocket endpoint URL */\n wsEndpoint?: string;\n /** Whether DevToolsActivePort file was found */\n activePortFound: boolean;\n /** Error message if connection failed */\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Well-known Chrome paths per platform\n// ---------------------------------------------------------------------------\n\nconst CHROME_PATHS: Record<string, string[]> = {\n darwin: [\n \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\",\n \"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary\",\n \"/Applications/Chromium.app/Contents/MacOS/Chromium\",\n \"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\",\n \"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser\",\n ],\n linux: [\n \"google-chrome\",\n \"google-chrome-stable\",\n \"chromium\",\n \"chromium-browser\",\n \"microsoft-edge\",\n \"brave-browser\",\n ],\n win32: [\n \"C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe\",\n \"C:\\\\Program Files\\\\BraveSoftware\\\\Brave-Browser\\\\Application\\\\brave.exe\",\n ],\n};\n\n// ---------------------------------------------------------------------------\n// Default Chrome data directory per platform\n// ---------------------------------------------------------------------------\n\nexport function getDefaultChromeDataDir(): string {\n const home = homedir();\n switch (process.platform) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Google\", \"Chrome\");\n case \"win32\":\n return join(home, \"AppData\", \"Local\", \"Google\", \"Chrome\", \"User Data\");\n default: // linux\n return join(home, \".config\", \"google-chrome\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Find Chrome\n// ---------------------------------------------------------------------------\n\nexport function findChrome(): string | null {\n const platform = process.platform;\n const candidates = CHROME_PATHS[platform] ?? [];\n\n for (const candidate of candidates) {\n if (platform === \"darwin\" || platform === \"win32\") {\n if (existsSync(candidate)) return candidate;\n } else {\n try {\n const result = execSync(`which ${candidate}`, { stdio: \"pipe\" });\n const path = result.toString().trim();\n if (path) return path;\n } catch {\n // try next\n }\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Port check\n// ---------------------------------------------------------------------------\n\nexport function isPortReachable(port: number, host = \"127.0.0.1\"): Promise<boolean> {\n return new Promise((resolve) => {\n const socket = createConnection({ port, host });\n socket.setTimeout(2000);\n socket.on(\"connect\", () => { socket.end(); resolve(true); });\n socket.on(\"error\", () => { socket.destroy(); resolve(false); });\n socket.on(\"timeout\", () => { socket.destroy(); resolve(false); });\n });\n}\n\n/**\n * Verifies CDP is truly usable by hitting /json/version.\n * Chrome's M144 approach (chrome://inspect) opens the port but returns 404 on /json/version\n * and 403 on WebSocket connections. Only --remote-debugging-port gives real CDP access.\n */\nexport function isCDPHealthy(port: number, host = \"127.0.0.1\"): Promise<boolean> {\n return new Promise((resolve) => {\n const req = http.get(`http://${host}:${port}/json/version`, (res) => {\n resolve(res.statusCode === 200);\n res.resume();\n });\n req.setTimeout(3000, () => { req.destroy(); resolve(false); });\n req.on(\"error\", () => resolve(false));\n });\n}\n\n// ---------------------------------------------------------------------------\n// DevToolsActivePort reader\n// ---------------------------------------------------------------------------\n\nexport interface ActivePortInfo {\n /** The debug port Chrome is listening on */\n port: number;\n /** The WebSocket path (e.g. /devtools/browser/...) */\n wsPath: string;\n /** Full WebSocket endpoint: ws://127.0.0.1:{port}{wsPath} */\n wsEndpoint: string;\n}\n\n/**\n * Reads the DevToolsActivePort file from Chrome's data directory.\n *\n * Chrome writes this file when remote debugging is enabled via\n * chrome://inspect/#remote-debugging (Chrome M144+).\n *\n * File format:\n * Line 1: port number (e.g. \"9222\")\n * Line 2: WebSocket path (e.g. \"/devtools/browser/abc-123\")\n *\n * @param chromeDataDir - Override Chrome data dir (for testing)\n */\nexport function readDevToolsActivePort(chromeDataDir?: string): ActivePortInfo | null {\n const dataDir = chromeDataDir ?? getDefaultChromeDataDir();\n const portFile = join(dataDir, \"DevToolsActivePort\");\n\n if (!existsSync(portFile)) {\n return null;\n }\n\n try {\n const content = readFileSync(portFile, \"utf-8\");\n const lines = content.split(\"\\n\").map(l => l.trim()).filter(l => l.length > 0);\n\n if (lines.length < 2) return null;\n\n const port = parseInt(lines[0]!, 10);\n const wsPath = lines[1]!;\n\n if (isNaN(port) || !wsPath.startsWith(\"/\")) return null;\n\n return {\n port,\n wsPath,\n wsEndpoint: `ws://127.0.0.1:${port}${wsPath}`,\n };\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Open chrome://inspect to guide user\n// ---------------------------------------------------------------------------\n\n/**\n * Opens chrome://inspect/#remote-debugging in the user's Chrome.\n * On macOS uses `open`, on Linux uses `xdg-open`, on Windows uses `start`.\n */\nexport function openChromeInspect(): boolean {\n const url = \"chrome://inspect/#remote-debugging\";\n try {\n if (process.platform === \"darwin\") {\n execSync(`open -a \"Google Chrome\" \"${url}\"`, { stdio: \"pipe\", timeout: 5000 });\n } else if (process.platform === \"win32\") {\n execSync(`start chrome \"${url}\"`, { stdio: \"pipe\", timeout: 5000 });\n } else {\n execSync(`google-chrome \"${url}\" || chromium \"${url}\"`, { stdio: \"pipe\", timeout: 5000 });\n }\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Launch Chrome with remote debugging (zero modals)\n// ---------------------------------------------------------------------------\n\nexport interface LaunchResult {\n success: boolean;\n port: number;\n wsEndpoint?: string;\n error?: string;\n}\n\n/**\n * Checks if Chrome is currently running.\n */\nexport function isChromeRunning(): boolean {\n try {\n if (process.platform === \"win32\") {\n const r = execSync('tasklist /FI \"IMAGENAME eq chrome.exe\" /NH', { stdio: \"pipe\" }).toString();\n return r.includes(\"chrome.exe\");\n }\n const r = execSync(\"pgrep -x 'Google Chrome' || pgrep -x chrome || pgrep -x chromium\", { stdio: \"pipe\" }).toString().trim();\n return r.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Quits Chrome gracefully, waits for it to fully exit.\n * Falls back to force kill if graceful quit doesn't work (e.g. macOS session restore).\n */\nexport async function quitChrome(): Promise<void> {\n try {\n if (process.platform === \"darwin\") {\n execSync('osascript -e \\'tell application \"Google Chrome\" to quit\\'', { stdio: \"pipe\", timeout: 5000 });\n } else if (process.platform === \"win32\") {\n execSync(\"taskkill /IM chrome.exe\", { stdio: \"pipe\", timeout: 5000 });\n } else {\n execSync(\"pkill -TERM chrome || pkill -TERM chromium\", { stdio: \"pipe\", timeout: 5000 });\n }\n } catch {\n // May not be running\n }\n\n // Wait for Chrome to fully exit (up to 3 seconds)\n for (let i = 0; i < 15; i++) {\n if (!isChromeRunning()) break;\n await new Promise(r => setTimeout(r, 200));\n }\n\n // Force kill if still running (macOS session restore can relaunch Chrome)\n if (isChromeRunning()) {\n try {\n if (process.platform === \"win32\") {\n execSync(\"taskkill /F /IM chrome.exe\", { stdio: \"pipe\", timeout: 5000 });\n } else {\n execSync(\"pkill -9 'Google Chrome' || pkill -9 chrome || pkill -9 chromium\", { stdio: \"pipe\", timeout: 5000 });\n }\n } catch {\n // best effort\n }\n\n // Wait for force kill to take effect\n for (let i = 0; i < 15; i++) {\n if (!isChromeRunning()) break;\n await new Promise(r => setTimeout(r, 200));\n }\n }\n\n // Small delay for profile lock release\n await new Promise(r => setTimeout(r, 500));\n}\n\nconst SEPARATE_PORT = 9444;\n\n/**\n * Launches Chrome with --remote-debugging-port to avoid the M144 \"Allow?\" modal.\n *\n * NEVER quits the user's running Chrome. If Chrome is already running without\n * CDP, a separate instance is launched with a temp profile + cookie sync\n * (same strategy as headless, but with a visible window).\n *\n * @param port - CDP port (default 9222)\n * @returns LaunchResult with wsEndpoint if successful\n */\nexport async function launchChromeWithDebugging(port = 9222, headless = false): Promise<LaunchResult> {\n // Already healthy (launched with --remote-debugging-port)? Don't touch Chrome.\n const healthy = await isCDPHealthy(port);\n if (healthy) {\n const ws = await getWsEndpoint(port);\n return { success: true, port, wsEndpoint: ws };\n }\n\n // Check if a separate browsirai instance is already running\n const sepHealthy = await isCDPHealthy(SEPARATE_PORT);\n if (sepHealthy) {\n const ws = await getWsEndpoint(SEPARATE_PORT);\n return { success: true, port: SEPARATE_PORT, wsEndpoint: ws };\n }\n\n const chromePath = findChrome();\n if (!chromePath) {\n return { success: false, port, error: \"Chrome not found. Install Chrome and try again.\" };\n }\n\n // If Chrome is running without CDP, launch a SEPARATE instance.\n // NEVER quit the user's Chrome — their tabs, work, and session are sacred.\n const usesSeparateInstance = isChromeRunning();\n const targetPort = usesSeparateInstance ? SEPARATE_PORT : port;\n\n const dataDir = usesSeparateInstance\n ? join(tmpdir(), \"browsirai-normal\")\n : undefined; // use default Chrome profile when no Chrome is running\n\n if (dataDir) {\n mkdirSync(dataDir, { recursive: true });\n syncCookiesToHeadless(dataDir); // reuse cookie sync for the separate instance\n }\n\n const args = [\n `--remote-debugging-port=${targetPort}`,\n \"--remote-allow-origins=*\",\n ];\n\n if (dataDir) {\n args.push(`--user-data-dir=${dataDir}`, \"--no-first-run\", \"--no-default-browser-check\", \"--disable-extensions\");\n }\n\n if (headless) {\n args.push(\"--headless=new\");\n }\n\n const child = spawn(chromePath, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n\n // Wait for CDP to become healthy (up to 15 seconds)\n for (let i = 0; i < 75; i++) {\n await new Promise(r => setTimeout(r, 200));\n const ok = await isCDPHealthy(targetPort);\n if (ok) {\n const ws = await getWsEndpoint(targetPort);\n return { success: true, port: targetPort, wsEndpoint: ws };\n }\n }\n\n return {\n success: false,\n port: targetPort,\n error: \"Chrome launched but CDP port not reachable after 15s. Check if another Chrome instance is blocking the profile.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Headless Chrome — separate instance, doesn't touch user's Chrome\n// ---------------------------------------------------------------------------\n\nconst HEADLESS_PORT = 9333;\n\n/**\n * Fetches the webSocketDebuggerUrl from /json/version.\n */\nasync function getWsEndpoint(port: number): Promise<string | undefined> {\n return new Promise((resolve) => {\n const req = http.get(`http://127.0.0.1:${port}/json/version`, (res) => {\n let body = \"\";\n res.on(\"data\", (c: Buffer) => { body += c.toString(); });\n res.on(\"end\", () => {\n try {\n const data = JSON.parse(body) as { webSocketDebuggerUrl?: string };\n resolve(data.webSocketDebuggerUrl);\n } catch { resolve(undefined); }\n });\n });\n req.setTimeout(3000, () => { req.destroy(); resolve(undefined); });\n req.on(\"error\", () => resolve(undefined));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Cookie sync state — tracks last sync for navigate-hook resync detection\n// ---------------------------------------------------------------------------\n\ninterface CookieSyncState {\n profileName: string;\n cookieMtime: number;\n}\n\nlet cookieSyncState: CookieSyncState | null = null;\n\n/**\n * Returns the current cookie sync state (profile name + cookie file mtime).\n * Returns null if no sync has been performed yet.\n */\nexport function getCookieSyncState(): CookieSyncState | null {\n return cookieSyncState;\n}\n\n/**\n * Checks if cookies need re-syncing by comparing current cookie file mtime\n * and active profile against the last sync state.\n * Returns true if: cookie file modified, profile switched, or no prior sync.\n * Returns false if: nothing changed or Chrome data dir doesn't exist.\n */\nexport function needsCookieResync(chromeDataDir?: string): boolean {\n if (!cookieSyncState) return false; // no prior sync → nothing to compare\n\n const dataDir = chromeDataDir ?? getDefaultChromeDataDir();\n const localStatePath = join(dataDir, \"Local State\");\n if (!existsSync(localStatePath)) return false;\n\n try {\n const localState = JSON.parse(readFileSync(localStatePath, \"utf-8\")) as {\n profile?: { last_used?: string };\n };\n const profileName = localState.profile?.last_used ?? \"Default\";\n\n // Profile changed?\n if (profileName !== cookieSyncState.profileName) return true;\n\n // Cookie file mtime changed?\n const cookiePath = join(dataDir, profileName, \"Cookies\");\n if (!existsSync(cookiePath)) return false;\n const mtime = statSync(cookiePath).mtimeMs;\n return mtime !== cookieSyncState.cookieMtime;\n } catch {\n return false;\n }\n}\n\n/**\n * Detects the user's active Chrome profile, copies cookies to the dest profile,\n * and tracks sync state (mtime + profile name) for later resync detection.\n */\nexport function syncCookiesAndTrack(destDataDir: string, chromeDataDir?: string): void {\n const dataDir = chromeDataDir ?? getDefaultChromeDataDir();\n try {\n const localStatePath = join(dataDir, \"Local State\");\n if (!existsSync(localStatePath)) return;\n\n const localState = JSON.parse(readFileSync(localStatePath, \"utf-8\")) as {\n profile?: { last_used?: string };\n };\n const profileName = localState.profile?.last_used ?? \"Default\";\n const srcProfileDir = join(dataDir, profileName);\n\n if (!existsSync(join(srcProfileDir, \"Cookies\"))) return;\n\n // Ensure Default profile dir exists in dest data dir\n const destProfileDir = join(destDataDir, \"Default\");\n mkdirSync(destProfileDir, { recursive: true });\n\n // Copy all Cookies-related files\n const files = readdirSync(srcProfileDir).filter(f => f.startsWith(\"Cookies\"));\n for (const file of files) {\n copyFileSync(join(srcProfileDir, file), join(destProfileDir, file));\n }\n\n // Track sync state\n const mtime = statSync(join(srcProfileDir, \"Cookies\")).mtimeMs;\n cookieSyncState = { profileName, cookieMtime: mtime };\n } catch {\n // Best-effort — don't fail launch\n }\n}\n\n/** @deprecated Use syncCookiesAndTrack instead. Kept for internal compatibility. */\nfunction syncCookiesToHeadless(headlessDataDir: string): void {\n syncCookiesAndTrack(headlessDataDir);\n}\n\n/**\n * Launches a separate headless Chrome on port 9333 with a temp profile.\n * Does NOT quit or affect the user's running Chrome.\n */\nexport async function launchHeadlessChrome(): Promise<LaunchResult> {\n // Already running?\n const healthy = await isCDPHealthy(HEADLESS_PORT);\n if (healthy) {\n const ws = await getWsEndpoint(HEADLESS_PORT);\n return { success: true, port: HEADLESS_PORT, wsEndpoint: ws };\n }\n\n const chromePath = findChrome();\n if (!chromePath) {\n return { success: false, port: HEADLESS_PORT, error: \"Chrome not found.\" };\n }\n\n const dataDir = join(tmpdir(), \"browsirai-headless\");\n mkdirSync(dataDir, { recursive: true });\n\n // Copy user's cookies to headless profile before launch\n syncCookiesToHeadless(dataDir);\n\n const child = spawn(chromePath, [\n \"--headless=new\",\n `--remote-debugging-port=${HEADLESS_PORT}`,\n \"--remote-allow-origins=*\",\n `--user-data-dir=${dataDir}`,\n \"--no-first-run\",\n \"--no-default-browser-check\",\n \"--disable-extensions\",\n \"--disable-gpu\",\n ], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n\n // Wait for CDP to become healthy\n for (let i = 0; i < 75; i++) {\n await new Promise(r => setTimeout(r, 200));\n if (await isCDPHealthy(HEADLESS_PORT)) {\n const ws = await getWsEndpoint(HEADLESS_PORT);\n return { success: true, port: HEADLESS_PORT, wsEndpoint: ws };\n }\n }\n\n return { success: false, port: HEADLESS_PORT, error: \"Headless Chrome did not start in 15s.\" };\n}\n\n// ---------------------------------------------------------------------------\n// Connect to Chrome\n// ---------------------------------------------------------------------------\n\n/**\n * Connects to Chrome via CDP.\n *\n * Strategy:\n * 1. Try DevToolsActivePort (Chrome already has debugging enabled)\n * 2. Try manual port override or default port 9222\n * 3. If autoLaunch is true, quit Chrome and relaunch with --remote-debugging-port\n *\n * @returns ConnectResult with wsEndpoint if successful\n */\nexport async function connectChrome(options: ConnectOptions = {}): Promise<ConnectResult> {\n const targetPort = options.port ?? 9222;\n\n // 1. Try DevToolsActivePort first (must be CDP-healthy, not just TCP-reachable)\n const activePort = readDevToolsActivePort();\n\n if (activePort) {\n const healthy = await isCDPHealthy(activePort.port);\n if (healthy) {\n return {\n success: true,\n port: activePort.port,\n wsEndpoint: activePort.wsEndpoint,\n activePortFound: true,\n };\n }\n }\n\n // 2. Try port directly (must be CDP-healthy to avoid M144 modal)\n const healthy = await isCDPHealthy(targetPort);\n if (healthy) {\n return {\n success: true,\n port: targetPort,\n activePortFound: false,\n };\n }\n\n // 3. Auto-launch if enabled\n if (options.autoLaunch) {\n const launch = await launchChromeWithDebugging(targetPort, options.headless);\n if (launch.success) {\n return {\n success: true,\n port: launch.port,\n wsEndpoint: launch.wsEndpoint,\n activePortFound: false,\n };\n }\n return {\n success: false,\n port: targetPort,\n activePortFound: false,\n error: launch.error,\n };\n }\n\n // 4. Not connected\n return {\n success: false,\n port: targetPort,\n activePortFound: activePort !== null,\n error: \"Chrome remote debugging is not enabled. Enable it at chrome://inspect/#remote-debugging\",\n };\n}\n","/**\n * browser_navigate tool — navigates to a URL via CDP Page.navigate.\n *\n * Handles:\n * - Cross-document navigation (loaderId present) → waits for load completion\n * by racing Page.loadEventFired against document.readyState polling\n * - Same-document navigation (no loaderId, e.g. hash change) → resolves immediately\n * - Error responses (errorText from CDP)\n * - Configurable waitUntil strategy\n * - Navigation timeout (default 30 s)\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\ninterface NavigateParams {\n url: string;\n waitUntil?: \"load\" | \"domcontentloaded\" | \"networkidle\";\n timeout?: number;\n}\n\ninterface NavigateResult {\n url: string;\n title: string;\n}\n\nconst POLL_INTERVAL_MS = 100;\n\nexport async function browserNavigate(\n cdp: CDPConnection,\n params: NavigateParams,\n): Promise<NavigateResult> {\n const { url, timeout = 8 } = params;\n const timeoutMs = timeout * 1000;\n\n // Enable Page domain events before navigating\n await cdp.send(\"Page.enable\");\n\n // Race navigation against a timeout\n const result = await Promise.race([\n performNavigation(cdp, url, params.waitUntil),\n createTimeout(timeoutMs),\n ]);\n\n return result;\n}\n\nasync function performNavigation(\n cdp: CDPConnection,\n url: string,\n waitUntil?: string,\n): Promise<NavigateResult> {\n const navResponse = (await cdp.send(\"Page.navigate\", { url })) as {\n frameId?: string;\n loaderId?: string;\n errorText?: string;\n };\n\n // Check for navigation errors\n if (navResponse.errorText) {\n throw new Error(`Navigation failed: ${navResponse.errorText}`);\n }\n\n const hasCrossDocNavigation = Boolean(navResponse.loaderId);\n\n if (hasCrossDocNavigation) {\n // Cross-document navigation: race event listener against readyState polling.\n // This ensures tests that emit Page.loadEventFired work, AND tests that\n // only mock Runtime.evaluate to return readyState=complete also work.\n await waitForLoadCompletion(cdp, waitUntil);\n }\n // Same-document navigation (hash change / pushState): no load event needed\n\n return getPageInfo(cdp);\n}\n\n/**\n * Waits for page load completion by racing two strategies:\n * 1. Listening for the Page.loadEventFired (or domContentEventFired) CDP event\n * 2. Polling document.readyState via Runtime.evaluate\n *\n * Whichever resolves first wins, and the other is cleaned up.\n */\nfunction waitForLoadCompletion(\n cdp: CDPConnection,\n waitUntil?: string,\n): Promise<void> {\n const eventName =\n waitUntil === \"domcontentloaded\"\n ? \"Page.domContentEventFired\"\n : \"Page.loadEventFired\";\n\n return new Promise<void>((resolve) => {\n let settled = false;\n\n // Strategy 1: CDP event listener\n const handler = () => {\n if (settled) return;\n settled = true;\n cdp.off(eventName, handler as (params: unknown) => void);\n resolve();\n };\n cdp.on(eventName, handler as (params: unknown) => void);\n\n // Strategy 2: poll readyState\n const poll = async () => {\n while (!settled) {\n try {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"document.readyState\",\n returnByValue: true,\n })) as { result: { type?: string; value?: string } };\n\n const readyState = response.result.value;\n\n // Ready if:\n // 1. readyState is explicitly \"complete\"\n // 2. The response is not a recognized loading state (meaning the\n // execution context is alive and document is accessible)\n const isLoadingState =\n readyState === \"loading\" || readyState === \"interactive\";\n if (readyState === \"complete\" || !isLoadingState) {\n if (!settled) {\n settled = true;\n cdp.off(eventName, handler as (params: unknown) => void);\n resolve();\n }\n return;\n }\n } catch {\n // Runtime.evaluate can fail transiently during navigation — retry\n }\n\n if (!settled) {\n await delay(POLL_INTERVAL_MS);\n }\n }\n };\n\n poll();\n });\n}\n\nasync function getPageInfo(cdp: CDPConnection): Promise<NavigateResult> {\n const [titleResponse, urlResponse] = await Promise.all([\n cdp.send(\"Runtime.evaluate\", {\n expression: \"document.title\",\n }) as Promise<{ result: { value?: string } }>,\n cdp.send(\"Runtime.evaluate\", {\n expression: \"location.href\",\n }) as Promise<{ result: { value?: string } }>,\n ]);\n\n return {\n title: titleResponse.result.value ?? \"\",\n url: urlResponse.result.value ?? \"\",\n };\n}\n\nfunction createTimeout(ms: number): Promise<never> {\n return new Promise((_resolve, reject) => {\n setTimeout(() => {\n reject(new Error(`Navigation timeout after ${ms}ms`));\n }, ms);\n });\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * browser_screenshot tool — captures viewport or full-page screenshots via CDP.\n *\n * Supports:\n * - Viewport / full-page capture (Page.captureScreenshot)\n * - DPR detection cascade (3-level fallback)\n * - Element screenshot by CSS selector or @eN ref\n * - Annotated screenshots with ref labels\n * - Custom format (png/jpeg) and quality\n *\n * @module browser-screenshot\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ScreenshotParams {\n /** Capture the full scrollable page, not just the viewport. */\n fullPage?: boolean;\n /** Screenshot format. Default: \"png\". */\n format?: \"png\" | \"jpeg\";\n /** JPEG quality (0-100). Only applies when format is \"jpeg\". */\n quality?: number;\n /** CSS selector to screenshot a specific element. */\n selector?: string;\n /** @eN ref to screenshot a specific element. */\n ref?: string;\n /** Annotate interactive elements with numbered labels. */\n annotate?: boolean;\n}\n\ninterface Annotation {\n /** The @eN ref string, e.g. \"@e1\" */\n ref: string;\n /** The numbered label on the screenshot, e.g. \"[1]\" */\n label: string;\n /** Accessibility role */\n role?: string;\n /** Accessible name */\n name?: string;\n}\n\ninterface ScreenshotResult {\n /** Base64-encoded screenshot data. */\n base64: string;\n /** Annotation labels when `annotate: true`. */\n annotations?: Annotation[];\n}\n\n// ---------------------------------------------------------------------------\n// DPR Detection Cascade\n// ---------------------------------------------------------------------------\n\n/**\n * Detect device pixel ratio using a 3-level cascade:\n * Level 1: Page.getLayoutMetrics (visualViewport vs cssVisualViewport)\n * Level 2: Emulation.getDeviceMetricsOverride (deviceScaleFactor)\n * Level 3: Runtime.evaluate(\"window.devicePixelRatio\")\n * Default: 1\n */\nasync function detectDPR(cdp: CDPConnection): Promise<number> {\n // Level 1: Page.getLayoutMetrics\n try {\n const metrics = (await cdp.send(\"Page.getLayoutMetrics\", {})) as {\n visualViewport?: { clientWidth: number };\n cssVisualViewport?: { clientWidth: number };\n layoutViewport?: { clientWidth: number };\n contentSize?: { width: number };\n };\n\n if (metrics.visualViewport && metrics.cssVisualViewport) {\n const physicalWidth = metrics.visualViewport.clientWidth;\n const cssWidth = metrics.cssVisualViewport.clientWidth;\n if (cssWidth > 0 && physicalWidth > 0) {\n const dpr = physicalWidth / cssWidth;\n if (dpr >= 1) {\n return dpr;\n }\n }\n }\n } catch {\n // Level 1 failed, try Level 2\n }\n\n // Level 2: Runtime.evaluate\n try {\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"window.devicePixelRatio\",\n returnByValue: true,\n })) as {\n result: { type: string; value: unknown };\n };\n\n if (evalResult.result.type === \"number\" && typeof evalResult.result.value === \"number\") {\n return evalResult.result.value;\n }\n } catch {\n // Level 3 failed, use default\n }\n\n // Default: DPR = 1\n return 1;\n}\n\n// ---------------------------------------------------------------------------\n// Element bounding box\n// ---------------------------------------------------------------------------\n\ninterface BoundingBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Get the bounding box of an element by CSS selector.\n */\nasync function getElementBoxBySelector(\n cdp: CDPConnection,\n selector: string,\n): Promise<BoundingBox> {\n const doc = (await cdp.send(\"DOM.getDocument\", {})) as {\n root: { nodeId: number };\n };\n\n const queryResult = (await cdp.send(\"DOM.querySelector\", {\n nodeId: doc.root.nodeId,\n selector,\n })) as { nodeId: number };\n\n if (!queryResult.nodeId) {\n throw new Error(`Element not found: ${selector}`);\n }\n\n const boxModel = (await cdp.send(\"DOM.getBoxModel\", {\n nodeId: queryResult.nodeId,\n })) as {\n model: { content: number[]; width: number; height: number };\n };\n\n const content = boxModel.model.content;\n // content is [x1,y1, x2,y2, x3,y3, x4,y4] (quad)\n const x = content[0];\n const y = content[1];\n const width = content[2] - content[0];\n const height = content[5] - content[1];\n\n return { x, y, width, height };\n}\n\n/**\n * Get the bounding box of an element by @eN ref (backendNodeId).\n */\nasync function getElementBoxByRef(\n cdp: CDPConnection,\n ref: string,\n): Promise<BoundingBox> {\n // Parse @eN → extract numeric ref index\n const match = /^@e(\\d+)$/.exec(ref);\n if (!match) {\n throw new Error(`Invalid ref format: ${ref}`);\n }\n\n const backendNodeId = parseInt(match[1], 10);\n\n // Resolve the backendNodeId to a remote object\n const resolved = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n })) as { object: { objectId: string } };\n\n // Get the box model for the node\n const boxModel = (await cdp.send(\"DOM.getBoxModel\", {\n backendNodeId,\n })) as {\n model: { content: number[]; width: number; height: number };\n };\n\n const content = boxModel.model.content;\n const x = content[0];\n const y = content[1];\n const width = content[2] - content[0];\n const height = content[5] - content[1];\n\n return { x, y, width, height };\n}\n\n// ---------------------------------------------------------------------------\n// Annotated screenshot\n// ---------------------------------------------------------------------------\n\n/**\n * Build annotations from the accessibility tree for interactive elements.\n */\nasync function buildAnnotations(cdp: CDPConnection): Promise<Annotation[]> {\n const axTree = (await cdp.send(\"Accessibility.getFullAXTree\", {}, { timeout: 10000 })) as {\n nodes: Array<{\n nodeId: string;\n backendDOMNodeId?: number;\n role?: { type: string; value: string };\n name?: { type: string; value: string };\n }>;\n };\n\n const annotations: Annotation[] = [];\n let counter = 0;\n\n for (const node of axTree.nodes) {\n const role = node.role?.value;\n\n // Skip the root WebArea node\n if (role === \"WebArea\") {\n continue;\n }\n\n counter++;\n // Use backendDOMNodeId as ref so tools can resolve it directly\n const ref = node.backendDOMNodeId ? `@e${node.backendDOMNodeId}` : `@e${counter}`;\n const label = `[${counter}]`;\n\n annotations.push({\n ref,\n label,\n role: role ?? \"unknown\",\n name: node.name?.value ?? \"\",\n });\n }\n\n return annotations;\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Capture a screenshot of the browser page or a specific element.\n *\n * @param cdp - CDP connection.\n * @param params - Screenshot parameters.\n * @returns Base64-encoded screenshot data and optional annotations.\n */\nexport async function browserScreenshot(\n cdp: CDPConnection,\n params: ScreenshotParams,\n): Promise<ScreenshotResult> {\n const format = params.format ?? \"png\";\n\n // Build the captureScreenshot parameters\n const captureParams: Record<string, unknown> = {\n format,\n };\n\n // Quality only applies to jpeg\n if (format === \"jpeg\" && params.quality !== undefined) {\n captureParams.quality = params.quality;\n }\n\n // Detect DPR (needed for various calculations)\n const _dpr = await detectDPR(cdp);\n\n // Element screenshot by selector\n if (params.selector) {\n const box = await getElementBoxBySelector(cdp, params.selector);\n captureParams.clip = {\n x: box.x,\n y: box.y,\n width: box.width,\n height: box.height,\n scale: 1,\n };\n }\n\n // Element screenshot by @eN ref\n if (params.ref) {\n const box = await getElementBoxByRef(cdp, params.ref);\n captureParams.clip = {\n x: box.x,\n y: box.y,\n width: box.width,\n height: box.height,\n scale: 1,\n };\n }\n\n // Full page screenshot\n if (params.fullPage) {\n const metrics = (await cdp.send(\"Page.getLayoutMetrics\", {})) as {\n contentSize: { width: number; height: number };\n cssContentSize?: { width: number; height: number };\n layoutViewport: { clientWidth: number; clientHeight: number };\n };\n\n const contentWidth = metrics.cssContentSize?.width ?? metrics.contentSize.width;\n const contentHeight = metrics.cssContentSize?.height ?? metrics.contentSize.height;\n\n captureParams.clip = {\n x: 0,\n y: 0,\n width: contentWidth,\n height: contentHeight,\n scale: 1,\n };\n }\n\n // Capture the screenshot\n const screenshot = (await cdp.send(\"Page.captureScreenshot\", captureParams)) as {\n data: string;\n };\n\n const result: ScreenshotResult = {\n base64: screenshot.data,\n };\n\n // Annotated screenshot\n if (params.annotate) {\n result.annotations = await buildAnnotations(cdp);\n }\n\n return result;\n}\n","/**\n * browser_tabs tool — Lists open browser tabs via CDP Target.getTargets.\n *\n * Filters to page-type targets only, with optional URL pattern matching.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\nexport interface TabInfo {\n id: string;\n title: string;\n url: string;\n}\n\nexport interface BrowserTabsParams {\n /** Glob-style URL filter pattern (e.g. \"*github.com*\") */\n filter?: string;\n}\n\nexport interface BrowserTabsResult {\n tabs: TabInfo[];\n}\n\n/**\n * Converts a simple glob pattern (with `*` wildcards) to a RegExp.\n */\nfunction globToRegExp(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regexStr = escaped.replace(/\\*/g, \".*\");\n return new RegExp(`^${regexStr}$`, \"i\");\n}\n\n/**\n * Lists all open browser tabs (page-type targets).\n *\n * @param cdp - CDP connection\n * @param params - Optional filter parameters\n * @returns List of tabs with id, title, and url\n */\nexport async function browserTabs(\n cdp: CDPConnection,\n params: BrowserTabsParams = {},\n): Promise<BrowserTabsResult> {\n const response = (await cdp.send(\"Target.getTargets\")) as {\n targetInfos: Array<{\n targetId: string;\n type: string;\n title: string;\n url: string;\n attached: boolean;\n }>;\n };\n\n // Filter to page-type targets only (exclude service workers, extensions, etc.)\n let tabs = response.targetInfos.filter(\n (target) => target.type === \"page\",\n );\n\n // Apply URL pattern filter if specified\n if (params.filter) {\n const regex = globToRegExp(params.filter);\n tabs = tabs.filter((target) => regex.test(target.url));\n }\n\n return {\n tabs: tabs.map((target) => ({\n id: target.targetId,\n title: target.title,\n url: target.url,\n })),\n };\n}\n","/**\n * browser_eval tool — evaluates JavaScript expressions in the browser via CDP.\n *\n * Supports:\n * - Simple expression evaluation (Runtime.evaluate)\n * - Element-scoped evaluation via @eN refs (Runtime.callFunctionOn)\n * - Async expressions with awaitPromise\n * - Error handling (ReferenceError, TypeError, etc.)\n * - DOM node serialization to string description\n * - Primitive type handling (null, undefined, boolean)\n * - Multi-line expressions (stdin mode)\n * - Base64-encoded expressions\n *\n * @module browser-eval\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface EvalParams {\n /** JavaScript expression to evaluate. */\n expression: string;\n /** @eN ref for element-scoped evaluation via callFunctionOn. */\n ref?: string;\n /** Whether the expression is multi-line (stdin mode). */\n stdin?: boolean;\n /** Whether the expression is base64-encoded. */\n base64?: boolean;\n}\n\ninterface EvalResult {\n /** The evaluation result value. */\n result?: unknown;\n /** Error message if evaluation failed. */\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// CDP result parsing\n// ---------------------------------------------------------------------------\n\ninterface CDPRemoteObject {\n type: string;\n subtype?: string;\n className?: string;\n description?: string;\n value?: unknown;\n objectId?: string;\n}\n\ninterface CDPExceptionDetails {\n exceptionId: number;\n text: string;\n lineNumber: number;\n columnNumber: number;\n exception?: {\n type: string;\n subtype?: string;\n className?: string;\n description?: string;\n };\n}\n\n/**\n * Parse a CDP RemoteObject into a JavaScript value.\n *\n * Handles:\n * - Primitives (string, number, boolean)\n * - null (subtype \"null\")\n * - undefined (type \"undefined\")\n * - DOM nodes (subtype \"node\") → serialized to string description\n * - Objects → return the value directly\n */\nfunction parseRemoteObject(obj: CDPRemoteObject): unknown {\n // undefined\n if (obj.type === \"undefined\") {\n return undefined;\n }\n\n // null\n if (obj.type === \"object\" && obj.subtype === \"null\") {\n return null;\n }\n\n // DOM node\n if (obj.type === \"object\" && obj.subtype === \"node\") {\n return obj.description ?? `[${obj.className ?? \"Node\"}]`;\n }\n\n // Primitives and objects with value\n if (obj.value !== undefined) {\n return obj.value;\n }\n\n // Object without value (shouldn't happen with returnByValue: true)\n if (obj.description) {\n return obj.description;\n }\n\n return undefined;\n}\n\n/**\n * Extract error message from CDP exception details.\n */\nfunction formatException(details: CDPExceptionDetails): string {\n if (details.exception?.description) {\n return details.exception.description;\n }\n\n if (details.exception?.className) {\n return `${details.exception.className}: ${details.text}`;\n }\n\n return details.text;\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a JavaScript expression in the browser context.\n *\n * Without a ref, uses `Runtime.evaluate` for global scope evaluation.\n * With a ref (@eN), uses `DOM.resolveNode` + `Runtime.callFunctionOn`\n * for element-scoped evaluation.\n *\n * @param cdp - CDP connection.\n * @param params - Expression and optional ref/flags.\n * @returns Evaluation result or error.\n */\nexport async function browserEval(\n cdp: CDPConnection,\n params: EvalParams,\n): Promise<EvalResult> {\n let expression = params.expression;\n\n // Decode base64-encoded expression\n if (params.base64) {\n expression = atob(expression);\n }\n\n // Element-scoped evaluation via @eN ref\n if (params.ref) {\n return evalWithRef(cdp, expression, params.ref);\n }\n\n // Global scope evaluation\n return evalGlobal(cdp, expression);\n}\n\n/**\n * Evaluate an expression in the global scope via Runtime.evaluate.\n */\nasync function evalGlobal(\n cdp: CDPConnection,\n expression: string,\n): Promise<EvalResult> {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression,\n returnByValue: true,\n awaitPromise: true,\n })) as {\n result: CDPRemoteObject;\n exceptionDetails?: CDPExceptionDetails;\n };\n\n // Check for exceptions\n if (response.exceptionDetails) {\n return {\n error: formatException(response.exceptionDetails),\n };\n }\n\n const value = parseRemoteObject(response.result);\n\n return { result: value };\n}\n\n/**\n * Evaluate a function on a specific element via Runtime.callFunctionOn.\n *\n * Resolves the @eN ref to a backendNodeId, then resolves to a remote object,\n * and calls the function on it.\n */\nasync function evalWithRef(\n cdp: CDPConnection,\n functionDeclaration: string,\n ref: string,\n): Promise<EvalResult> {\n // Parse the @eN ref\n const match = /^@e(\\d+)$/.exec(ref);\n if (!match) {\n return { error: `Invalid ref format: ${ref}` };\n }\n\n const backendNodeId = parseInt(match[1], 10);\n\n // Resolve backendNodeId to a remote object\n const resolved = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n })) as { object: { objectId: string } };\n\n const objectId = resolved.object.objectId;\n\n // Call the function on the element\n const response = (await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration,\n returnByValue: true,\n awaitPromise: true,\n })) as {\n result: CDPRemoteObject;\n exceptionDetails?: CDPExceptionDetails;\n };\n\n // Check for exceptions\n if (response.exceptionDetails) {\n return {\n error: formatException(response.exceptionDetails),\n };\n }\n\n const value = parseRemoteObject(response.result);\n\n return { result: value };\n}\n","/**\n * browser_snapshot tool — captures the accessibility tree and returns a\n * formatted text representation with @eN refs for each element.\n *\n * Exports:\n * - browserSnapshot(cdp, params?) — main entry point\n * - shouldShowAxNode(node, options?) — filtering predicate\n * - processAccessibilityTree(nodes, options?) — tree formatter\n *\n * @module browser-snapshot\n */\n\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Shape of an accessibility tree node from CDP. */\ninterface AXNode {\n nodeId: string;\n backendDOMNodeId?: number;\n role?: { type: string; value: string };\n name?: { type: string; value: string };\n description?: { type: string; value: string };\n value?: { type: string; value: string } | null;\n properties?: Array<{\n name: string;\n value: { type: string; value: unknown };\n }>;\n parentId?: string;\n children?: Array<{ nodeId: string; backendDOMNodeId?: number }>;\n childIds?: string[];\n}\n\n/** Options controlling which nodes appear in the snapshot. */\nexport interface SnapshotOptions {\n /** Only show interactive elements (button, link, textbox, etc.). */\n interactive?: boolean;\n /** Include elements with cursor:pointer. */\n cursor?: boolean;\n /** Compact mode — hides InlineTextBox and empty structural wrappers. */\n compact?: boolean;\n /** Maximum tree depth to include. */\n depth?: number;\n /** CSS selector to scope the snapshot. */\n selector?: string;\n}\n\n/** Options for processAccessibilityTree. */\nexport interface ProcessTreeOptions {\n maxDepth?: number;\n}\n\n/** Result returned by browserSnapshot. */\nexport interface SnapshotResult {\n snapshot: string;\n truncated?: boolean;\n totalElements?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Interactive role set\n// ---------------------------------------------------------------------------\n\nconst INTERACTIVE_ROLES = new Set([\n \"button\",\n \"link\",\n \"textbox\",\n \"checkbox\",\n \"radio\",\n \"combobox\",\n \"listbox\",\n \"menuitem\",\n \"menuitemcheckbox\",\n \"menuitemradio\",\n \"option\",\n \"searchbox\",\n \"slider\",\n \"spinbutton\",\n \"switch\",\n \"tab\",\n \"treeitem\",\n]);\n\n// ---------------------------------------------------------------------------\n// shouldShowAxNode\n// ---------------------------------------------------------------------------\n\n/**\n * Determines whether an AX node should be included in the snapshot output.\n *\n * Filtering rules (from tests):\n * 1. role='none' → false\n * 2. role='generic' with empty name → false\n * 3. role='InlineTextBox' in compact mode → false\n * 4. Empty name AND empty/null value → false\n * 5. Otherwise → true\n */\nexport function shouldShowAxNode(\n node: AXNode,\n options?: { compact?: boolean },\n): boolean {\n const role = node.role?.value ?? \"\";\n\n // Rule 1: role='none'\n if (role === \"none\") {\n return false;\n }\n\n // Rule 2: role='generic' with empty name\n if (role === \"generic\") {\n const name = node.name?.value ?? \"\";\n if (!name) {\n return false;\n }\n }\n\n // Rule 3: InlineTextBox in compact mode\n if (options?.compact && role === \"InlineTextBox\") {\n return false;\n }\n\n // Rule 4: Empty name AND empty/null value\n const name = node.name?.value ?? \"\";\n const value = node.value?.value ?? null;\n if (!name && (value === null || value === undefined || value === \"\")) {\n return false;\n }\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// processAccessibilityTree\n// ---------------------------------------------------------------------------\n\n/**\n * Processes a flat array of AX nodes into a formatted, indented text tree.\n *\n * - Uses 2-space indentation per depth level.\n * - Caps traversal at maxDepth (default 10).\n * - Prevents cycles via a visited set.\n * - Orders children via childIds[] when present, falling back to children[].\n */\nexport function processAccessibilityTree(\n nodes: AXNode[],\n options: ProcessTreeOptions,\n): string {\n const maxDepth = options.maxDepth ?? 10;\n\n // Build lookup map: nodeId -> AXNode\n const nodeMap = new Map<string, AXNode>();\n for (const node of nodes) {\n nodeMap.set(node.nodeId, node);\n }\n\n // Find root node (first node without parentId, or first node)\n let root: AXNode | undefined;\n for (const node of nodes) {\n if (!node.parentId) {\n root = node;\n break;\n }\n }\n\n if (!root) {\n return \"\";\n }\n\n const lines: string[] = [];\n const visited = new Set<string>();\n\n function traverse(node: AXNode, depth: number): void {\n // Cycle prevention\n if (visited.has(node.nodeId)) {\n return;\n }\n visited.add(node.nodeId);\n\n // Depth cap\n if (depth > maxDepth) {\n return;\n }\n\n // Format node line\n const indent = \" \".repeat(depth);\n const role = node.role?.value ?? \"unknown\";\n const name = node.name?.value ?? \"\";\n const attrs = formatNodeAttributes(node);\n\n let line = `${indent}${role}`;\n if (name) {\n line += ` \"${name}\"`;\n }\n if (attrs) {\n line += ` ${attrs}`;\n }\n lines.push(line);\n\n // Resolve children: prefer childIds, fall back to children\n const childNodeIds = getChildNodeIds(node);\n\n for (const childId of childNodeIds) {\n const childNode = nodeMap.get(childId);\n if (childNode) {\n traverse(childNode, depth + 1);\n }\n }\n }\n\n traverse(root, 0);\n\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Helper: get child node IDs\n// ---------------------------------------------------------------------------\n\nfunction getChildNodeIds(node: AXNode): string[] {\n if (node.childIds && node.childIds.length > 0) {\n return node.childIds;\n }\n if (node.children && node.children.length > 0) {\n return node.children.map((c) => c.nodeId);\n }\n return [];\n}\n\n// ---------------------------------------------------------------------------\n// Helper: format node attributes\n// ---------------------------------------------------------------------------\n\nfunction formatNodeAttributes(node: AXNode): string {\n const parts: string[] = [];\n\n // Value\n if (node.value?.value !== undefined && node.value.value !== \"\") {\n parts.push(`value=\"${node.value.value}\"`);\n }\n\n // Description\n if (node.description?.value) {\n parts.push(`description=\"${node.description.value}\"`);\n }\n\n // Properties\n if (node.properties) {\n for (const prop of node.properties) {\n switch (prop.name) {\n case \"level\":\n parts.push(`level=${prop.value.value}`);\n break;\n case \"checked\": {\n const val = prop.value.value;\n if (val === true || val === \"true\" || val === \"mixed\") {\n parts.push(\"checked\");\n }\n break;\n }\n case \"selected\":\n if (prop.value.value === true) {\n parts.push(\"selected\");\n }\n break;\n case \"expanded\":\n if (prop.value.value === true || prop.value.value === \"true\") {\n parts.push(\"expanded\");\n } else {\n parts.push(\"collapsed\");\n }\n break;\n default:\n // Skip other properties\n break;\n }\n }\n }\n\n return parts.join(\" \");\n}\n\n// ---------------------------------------------------------------------------\n// browserSnapshot\n// ---------------------------------------------------------------------------\n\n/**\n * Takes an accessibility tree snapshot from the page, assigns @eN refs,\n * and returns formatted text output.\n *\n * @param cdp - CDP connection\n * @param params - Snapshot options\n * @returns SnapshotResult with snapshot text\n */\nexport async function browserSnapshot(\n cdp: CDPConnection,\n params?: Record<string, unknown>,\n): Promise<SnapshotResult> {\n const options: SnapshotOptions = {\n interactive: params?.interactive as boolean | undefined,\n cursor: params?.cursor as boolean | undefined,\n compact: params?.compact as boolean | undefined,\n depth: params?.depth as number | undefined,\n selector: params?.selector as string | undefined,\n };\n\n // Enable accessibility\n await cdp.send(\"Accessibility.enable\");\n\n // Get the accessibility tree\n let axNodes: AXNode[];\n\n if (options.selector) {\n // Scoped snapshot via CSS selector\n const docResult = (await cdp.send(\"DOM.getDocument\")) as {\n root: { nodeId: number };\n };\n const queryResult = (await cdp.send(\"DOM.querySelector\", {\n nodeId: docResult.root.nodeId,\n selector: options.selector,\n })) as { nodeId: number };\n\n if (queryResult.nodeId === 0) {\n return { snapshot: `No element found for selector: ${options.selector}` };\n }\n\n const partialResult = (await cdp.send(\"Accessibility.getPartialAXTree\", {\n nodeId: queryResult.nodeId,\n fetchRelatives: true,\n })) as { nodes: AXNode[] };\n axNodes = partialResult.nodes;\n } else {\n // Full page snapshot\n const fullResult = (await cdp.send(\"Accessibility.getFullAXTree\")) as {\n nodes: AXNode[];\n };\n axNodes = fullResult.nodes;\n }\n\n // Count non-root elements (exclude WebArea root)\n const totalElements = axNodes.filter(\n (n) => n.role?.value !== \"WebArea\",\n ).length;\n\n // Build a node map for tree traversal\n const nodeMap = new Map<string, AXNode>();\n for (const node of axNodes) {\n nodeMap.set(node.nodeId, node);\n }\n\n // Find root node\n let root: AXNode | undefined;\n for (const node of axNodes) {\n if (!node.parentId) {\n root = node;\n break;\n }\n }\n\n if (!root) {\n // If no root found, use first node\n root = axNodes[0];\n }\n\n if (!root) {\n return { snapshot: \"\" };\n }\n\n // Traverse the tree, filter, assign refs, and format\n const lines: string[] = [];\n let refCounter = 0;\n const visited = new Set<string>();\n const maxDepth = options.depth ?? 100;\n\n function traverse(node: AXNode, depth: number): void {\n // Cycle prevention\n if (visited.has(node.nodeId)) {\n return;\n }\n visited.add(node.nodeId);\n\n // Depth limit\n if (depth > maxDepth) {\n return;\n }\n\n const role = node.role?.value ?? \"\";\n const isRoot = role === \"WebArea\";\n\n // Apply filters\n const showNode = isRoot || shouldShow(node, options);\n\n if (showNode && !isRoot) {\n refCounter++;\n // Use backendDOMNodeId as ref so tools can resolve it directly\n const ref = node.backendDOMNodeId ? `@e${node.backendDOMNodeId}` : `@e${refCounter}`;\n const indent = \" \".repeat(depth);\n const name = node.name?.value ?? \"\";\n const attrs = formatNodeAttributes(node);\n\n let line = `${indent}${ref} ${role}`;\n if (name) {\n line += ` \"${name}\"`;\n }\n if (attrs) {\n line += ` ${attrs}`;\n }\n lines.push(line);\n }\n\n // Process children\n const childNodeIds = getChildNodeIds(node);\n const nextDepth = isRoot ? depth : depth + 1;\n\n for (const childId of childNodeIds) {\n const childNode = nodeMap.get(childId);\n if (childNode) {\n traverse(childNode, nextDepth);\n }\n }\n }\n\n traverse(root, 0);\n\n const snapshot = lines.join(\"\\n\");\n const result: SnapshotResult = { snapshot };\n\n // Add truncation info for large pages\n if (totalElements > 1000) {\n result.truncated = true;\n result.totalElements = totalElements;\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal show filter (combines shouldShowAxNode with interactive/cursor)\n// ---------------------------------------------------------------------------\n\nfunction shouldShow(node: AXNode, options: SnapshotOptions): boolean {\n // Base filtering via shouldShowAxNode\n if (!shouldShowAxNode(node, { compact: options.compact })) {\n // In compact mode, skip filtered nodes entirely.\n // In normal mode, also skip.\n return false;\n }\n\n const role = node.role?.value ?? \"\";\n\n // Interactive filter: only show interactive roles\n if (options.interactive) {\n if (INTERACTIVE_ROLES.has(role)) {\n return true;\n }\n // If cursor mode is also on, check for cursor:pointer property\n if (options.cursor) {\n return hasCursorPointer(node);\n }\n return false;\n }\n\n // Cursor filter: include elements with cursor:pointer even if role is generic\n if (options.cursor) {\n if (hasCursorPointer(node)) {\n return true;\n }\n }\n\n return true;\n}\n\nfunction hasCursorPointer(node: AXNode): boolean {\n if (!node.properties) return false;\n return node.properties.some(\n (p) => p.name === \"cursor\" && p.value.value === \"pointer\",\n );\n}\n","/**\n * browser_click tool — clicks an element by ref, CSS selector, or coordinates.\n *\n * Element resolution:\n * 1. If `ref` provided (@eN) -> extract backendNodeId -> DOM.resolveNode\n * 2. If `selector` provided -> DOM.getDocument -> DOM.querySelector -> nodeId\n * 3. DOM.scrollIntoViewIfNeeded -> DOM.getBoxModel -> center of content quad\n * 4. If `x`, `y` provided -> use directly\n *\n * Click sequence (CDP Input.dispatchMouseEvent):\n * 1. mouseMoved (x, y)\n * 2. mousePressed (x, y, button, clickCount)\n * 3. 50ms delay\n * 4. mouseReleased (x, y, button, clickCount)\n *\n * Supports: left/right/middle button, double-click, modifier keys, new-tab click.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Modifier bitfield values (CDP Input.dispatchMouseEvent modifiers)\n// ---------------------------------------------------------------------------\nconst MODIFIER_BITS: Record<string, number> = {\n Alt: 1,\n Control: 2,\n Meta: 4,\n Shift: 8,\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ClickParams {\n /** @eN ref from accessibility snapshot */\n ref?: string;\n /** CSS selector */\n selector?: string;\n /** Human-readable element description */\n element?: string;\n /** Direct x coordinate */\n x?: number;\n /** Direct y coordinate */\n y?: number;\n /** Mouse button: \"left\" | \"right\" | \"middle\" */\n button?: \"left\" | \"right\" | \"middle\";\n /** Whether to double-click */\n doubleClick?: boolean;\n /** Modifier keys to hold during click */\n modifiers?: string[];\n /** Whether to open in new tab (Meta on macOS, Ctrl otherwise) */\n newTab?: boolean;\n}\n\nexport interface ClickResult {\n success: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Ref pattern\n// ---------------------------------------------------------------------------\nconst REF_PATTERN = /^@e(\\d+)$/;\n\n// ---------------------------------------------------------------------------\n// Element resolution helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Calculates the center point of a content quad from DOM.getBoxModel.\n * Content quad is 8 numbers: [x1,y1, x2,y2, x3,y3, x4,y4].\n */\nfunction calculateCenter(content: number[]): { x: number; y: number } {\n const x = (content[0] + content[2] + content[4] + content[6]) / 4;\n const y = (content[1] + content[3] + content[5] + content[7]) / 4;\n return { x: Math.round(x), y: Math.round(y) };\n}\n\n/**\n * Resolves an element to coordinates by ref or selector.\n * Returns the center (x, y) of the element's content box.\n */\nasync function resolveElementCoordinates(\n cdp: CDPConnection,\n params: ClickParams,\n): Promise<{ x: number; y: number }> {\n let backendNodeId: number | undefined;\n let nodeId: number | undefined;\n\n if (params.ref) {\n // Parse @eN ref to extract backendNodeId\n const match = REF_PATTERN.exec(params.ref);\n if (!match) {\n throw new Error(`Invalid ref format: ${params.ref}`);\n }\n backendNodeId = parseInt(match[1], 10);\n\n // Resolve to get objectId (validates the node exists)\n await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n } as unknown as Record<string, unknown>);\n } else if (params.selector) {\n // Resolve via DOM.querySelector\n const docResponse = (await cdp.send(\"DOM.getDocument\")) as {\n root: { nodeId: number };\n };\n\n const queryResponse = (await cdp.send(\"DOM.querySelector\", {\n nodeId: docResponse.root.nodeId,\n selector: params.selector,\n } as unknown as Record<string, unknown>)) as { nodeId: number };\n\n if (!queryResponse.nodeId || queryResponse.nodeId === 0) {\n throw new Error(\n `Element not found: no element matches selector \"${params.selector}\"`,\n );\n }\n\n nodeId = queryResponse.nodeId;\n } else {\n throw new Error(\"Either ref, selector, or coordinates (x, y) must be provided\");\n }\n\n // Scroll element into view\n const scrollParams: Record<string, unknown> = {};\n if (backendNodeId !== undefined) {\n scrollParams.backendNodeId = backendNodeId;\n } else if (nodeId !== undefined) {\n scrollParams.nodeId = nodeId;\n }\n await cdp.send(\n \"DOM.scrollIntoViewIfNeeded\",\n scrollParams,\n );\n\n // Get box model for center coordinates\n const boxParams: Record<string, unknown> = {};\n if (backendNodeId !== undefined) {\n boxParams.backendNodeId = backendNodeId;\n } else if (nodeId !== undefined) {\n boxParams.nodeId = nodeId;\n }\n\n const boxResponse = (await cdp.send(\"DOM.getBoxModel\", boxParams)) as {\n model: {\n content: number[];\n width: number;\n height: number;\n };\n };\n\n const { content, width, height } = boxResponse.model;\n\n // Validate element is visible (non-zero size)\n if (width === 0 && height === 0) {\n throw new Error(\n \"Element is not visible: zero-size box model. The element may be hidden or not rendered.\",\n );\n }\n\n return calculateCenter(content);\n}\n\n/**\n * Calculates the combined modifier bitfield from modifier key names.\n */\nfunction computeModifiers(modifiers?: string[]): number {\n if (!modifiers || modifiers.length === 0) return 0;\n\n let bits = 0;\n for (const mod of modifiers) {\n const bit = MODIFIER_BITS[mod];\n if (bit !== undefined) {\n bits |= bit;\n }\n }\n return bits;\n}\n\n// ---------------------------------------------------------------------------\n// Delay helper\n// ---------------------------------------------------------------------------\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ---------------------------------------------------------------------------\n// Main click implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Clicks an element identified by ref, CSS selector, or coordinates.\n *\n * @param cdp - CDP connection with send/on/off methods.\n * @param params - Click parameters.\n * @returns Result with success status.\n */\nexport async function browserClick(\n cdp: CDPConnection,\n params: ClickParams,\n): Promise<ClickResult> {\n // Resolve target coordinates\n let x: number;\n let y: number;\n\n if (params.x !== undefined && params.y !== undefined) {\n // Direct coordinates\n x = params.x;\n y = params.y;\n } else {\n // Resolve from ref or selector\n const coords = await resolveElementCoordinates(cdp, params);\n x = coords.x;\n y = coords.y;\n }\n\n const button = params.button ?? \"left\";\n\n // When newTab is requested, add Meta (macOS) or Ctrl (other platforms) modifier\n let effectiveModifiers = params.modifiers ? [...params.modifiers] : [];\n if (params.newTab) {\n const isMac = typeof process !== \"undefined\" && process.platform === \"darwin\";\n const newTabModifier = isMac ? \"Meta\" : \"Control\";\n if (!effectiveModifiers.includes(newTabModifier)) {\n effectiveModifiers.push(newTabModifier);\n }\n }\n const modifiers = computeModifiers(effectiveModifiers);\n\n // Perform click sequence\n if (params.doubleClick) {\n await performDoubleClick(cdp, x, y, button, modifiers);\n } else {\n await performClick(cdp, x, y, button, modifiers);\n }\n\n return { success: true };\n}\n\n/**\n * Dispatches a single click: mouseMoved -> mousePressed -> delay -> mouseReleased.\n */\nasync function performClick(\n cdp: CDPConnection,\n x: number,\n y: number,\n button: string,\n modifiers: number,\n clickCount: number = 1,\n): Promise<void> {\n // 1. mouseMoved\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseMoved\",\n x,\n y,\n modifiers,\n } as unknown as Record<string, unknown>);\n\n // 2. mousePressed\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mousePressed\",\n x,\n y,\n button,\n clickCount,\n modifiers,\n } as unknown as Record<string, unknown>);\n\n // 3. 50ms delay\n await delay(50);\n\n // 4. mouseReleased\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseReleased\",\n x,\n y,\n button,\n clickCount,\n modifiers,\n } as unknown as Record<string, unknown>);\n}\n\n/**\n * Dispatches a double click: single click (clickCount 1) + second click (clickCount 2).\n */\nasync function performDoubleClick(\n cdp: CDPConnection,\n x: number,\n y: number,\n button: string,\n modifiers: number,\n): Promise<void> {\n // First click\n await performClick(cdp, x, y, button, modifiers, 1);\n\n // Second click (double-click)\n await performClick(cdp, x, y, button, modifiers, 2);\n}\n","/**\n * browser_scroll tool — Scrolls the page or a specific element.\n *\n * Supports directional scrolling (up/down/left/right) by pixel amount,\n * scrolling to an element via selector, and scrolling within a\n * scrollable container.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Parameter types\n// ---------------------------------------------------------------------------\n\ninterface ScrollParams {\n /** Direction to scroll: \"up\" | \"down\" | \"left\" | \"right\" */\n direction?: \"up\" | \"down\" | \"left\" | \"right\";\n /** Number of pixels to scroll. Defaults to 300. */\n amount?: number;\n /** CSS selector of an element to scroll into view, or a scrollable container. */\n selector?: string;\n}\n\ninterface ScrollResult {\n success: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SCROLL_AMOUNT = 300;\n\n/**\n * Resolves a CSS selector to a Runtime objectId.\n *\n * Uses Runtime.evaluate with document.querySelector. If the evaluate call\n * returns a node reference (objectId), uses that directly. Otherwise, falls\n * back to DOM.resolveNode with the evaluate result.\n *\n * @throws If the element cannot be found.\n */\nasync function resolveSelector(\n cdp: CDPConnection,\n selector: string,\n): Promise<{ objectId: string }> {\n // Use Runtime.evaluate to find the element\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector(${JSON.stringify(selector)})`,\n returnByValue: false,\n })) as {\n result: {\n type: string;\n subtype?: string;\n objectId?: string;\n value?: unknown;\n };\n };\n\n // If we got a proper node reference with objectId, use it\n if (evalResult.result.objectId && evalResult.result.subtype !== \"null\") {\n return { objectId: evalResult.result.objectId };\n }\n\n // If we got an explicit null, the element doesn't exist\n if (\n evalResult.result.subtype === \"null\" ||\n evalResult.result.value === null\n ) {\n throw new Error(`Element not found: ${selector}`);\n }\n\n // For non-null results without objectId (e.g., in mocked environments),\n // try DOM.resolveNode as a fallback resolution strategy.\n try {\n const resolveResponse = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId: undefined,\n })) as { object?: { objectId: string } };\n\n if (resolveResponse.object?.objectId) {\n return { objectId: resolveResponse.object.objectId };\n }\n } catch {\n // Fall through\n }\n\n throw new Error(`Could not find element: ${selector}`);\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Scrolls the page or an element.\n *\n * When `selector` is provided without a direction, scrolls the element into view.\n * When `selector` is provided with a direction, scrolls within that container.\n * When only direction is provided, scrolls the page viewport.\n */\nexport async function browserScroll(\n cdp: CDPConnection,\n params: ScrollParams,\n): Promise<ScrollResult> {\n const { direction, selector } = params;\n const amount = params.amount ?? DEFAULT_SCROLL_AMOUNT;\n\n // Case 1: Scroll element into view (selector without direction)\n if (selector && !direction) {\n const { objectId } = await resolveSelector(cdp, selector);\n\n // Use Runtime.callFunctionOn with scrollIntoView\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() { this.scrollIntoView({ behavior: \"smooth\", block: \"center\" }); }`,\n returnByValue: true,\n });\n\n return { success: true };\n }\n\n // Case 2: Scroll within a specific container\n if (selector && direction) {\n const { objectId } = await resolveSelector(cdp, selector);\n\n const scrollX = direction === \"left\" ? -amount : direction === \"right\" ? amount : 0;\n const scrollY = direction === \"up\" ? -amount : direction === \"down\" ? amount : 0;\n\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() { this.scrollBy(${scrollX}, ${scrollY}); }`,\n returnByValue: true,\n });\n\n return { success: true };\n }\n\n // Case 3: Scroll the page viewport\n const scrollX = direction === \"left\" ? -amount : direction === \"right\" ? amount : 0;\n const scrollY = direction === \"up\" ? -amount : direction === \"down\" ? amount : 0;\n\n await cdp.send(\"Runtime.evaluate\", {\n expression: `window.scrollBy(${scrollX}, ${scrollY})`,\n returnByValue: true,\n });\n\n return { success: true };\n}\n","/**\n * browser_html tool — retrieves page or element HTML via CDP.\n *\n * Supports:\n * - Full page HTML (document.documentElement.outerHTML)\n * - Element HTML by CSS selector (DOM.querySelector + DOM.getOuterHTML)\n * - Graceful handling of missing selectors\n *\n * @module browser-html\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HtmlParams {\n /** CSS selector to retrieve outerHTML for a specific element. */\n selector?: string;\n}\n\ninterface HtmlResult {\n /** The retrieved HTML string. */\n html: string;\n /** Error message when element is not found. */\n error?: string;\n}\n\ninterface MarkdownParams {\n /** CSS selector to scope markdown extraction. */\n selector?: string;\n}\n\ninterface MarkdownResult {\n /** Extracted markdown content. */\n markdown: string;\n}\n\n// ---------------------------------------------------------------------------\n// HTML retrieval\n// ---------------------------------------------------------------------------\n\n/**\n * Retrieve the outer HTML of the page or a specific element.\n *\n * Without a selector, returns document.documentElement.outerHTML via\n * Runtime.evaluate.\n *\n * With a selector, uses DOM.getDocument → DOM.querySelector → DOM.getOuterHTML.\n *\n * @param cdp - CDP connection.\n * @param params - Optional selector parameter.\n * @returns The HTML string, or an error if the element is not found.\n */\nexport async function browserHtml(\n cdp: CDPConnection,\n params: HtmlParams,\n): Promise<HtmlResult> {\n // No selector: return full page HTML\n if (!params.selector) {\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"document.documentElement.outerHTML\",\n returnByValue: true,\n })) as {\n result: { type: string; value: string };\n };\n\n return { html: evalResult.result.value };\n }\n\n // With selector: use DOM methods\n const doc = (await cdp.send(\"DOM.getDocument\", {})) as {\n root: { nodeId: number };\n };\n\n const queryResult = (await cdp.send(\"DOM.querySelector\", {\n nodeId: doc.root.nodeId,\n selector: params.selector,\n })) as { nodeId: number };\n\n // nodeId 0 means element not found\n if (!queryResult.nodeId) {\n return {\n html: \"\",\n error: `Element not found: ${params.selector}`,\n };\n }\n\n const outerResult = (await cdp.send(\"DOM.getOuterHTML\", {\n nodeId: queryResult.nodeId,\n })) as { outerHTML: string };\n\n return { html: outerResult.outerHTML };\n}\n\n// ---------------------------------------------------------------------------\n// HTML → Markdown conversion\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal HTML-to-Markdown converter.\n *\n * Handles:\n * - Headings (h1-h6) → # syntax\n * - Code blocks (<pre><code>) → fenced ``` blocks\n * - Tables → markdown table syntax with separator row\n * - Navigation/sidebar exclusion (<nav>, <aside>)\n * - Paragraphs → plain text with newlines\n */\nfunction htmlToMarkdown(html: string): string {\n let content = html;\n\n // Strip <nav> and <aside> elements and their contents\n content = content.replace(/<nav\\b[^>]*>[\\s\\S]*?<\\/nav>/gi, \"\");\n content = content.replace(/<aside\\b[^>]*>[\\s\\S]*?<\\/aside>/gi, \"\");\n\n const lines: string[] = [];\n\n // Process code blocks first (before stripping tags)\n content = content.replace(\n /<pre[^>]*>\\s*<code(?:\\s+class=\"language-(\\w+)\")?[^>]*>([\\s\\S]*?)<\\/code>\\s*<\\/pre>/gi,\n (_match, lang, code) => {\n const language = lang ?? \"\";\n const decoded = decodeHtmlEntities(code.trim());\n return `\\n\\`\\`\\`${language}\\n${decoded}\\n\\`\\`\\`\\n`;\n },\n );\n\n // Process tables\n content = content.replace(\n /<table[^>]*>([\\s\\S]*?)<\\/table>/gi,\n (_match, tableContent: string) => {\n const rows: string[][] = [];\n\n // Extract thead rows\n const theadMatch = tableContent.match(/<thead[^>]*>([\\s\\S]*?)<\\/thead>/i);\n if (theadMatch) {\n const headerRows = extractTableRows(theadMatch[1]);\n rows.push(...headerRows);\n }\n\n // Extract tbody rows\n const tbodyMatch = tableContent.match(/<tbody[^>]*>([\\s\\S]*?)<\\/tbody>/i);\n if (tbodyMatch) {\n const bodyRows = extractTableRows(tbodyMatch[1]);\n // Insert separator after header\n if (rows.length > 0 && bodyRows.length > 0) {\n const colCount = rows[0].length;\n const separator = Array(colCount).fill(\"---\");\n rows.push(separator);\n }\n rows.push(...bodyRows);\n }\n\n // If no thead/tbody, extract all rows directly\n if (!theadMatch && !tbodyMatch) {\n const allRows = extractTableRows(tableContent);\n if (allRows.length > 1) {\n const colCount = allRows[0].length;\n const separator = Array(colCount).fill(\"---\");\n const result = [allRows[0], separator, ...allRows.slice(1)];\n return \"\\n\" + result.map((r) => \"| \" + r.join(\" | \") + \" |\").join(\"\\n\") + \"\\n\";\n }\n return \"\\n\" + allRows.map((r) => \"| \" + r.join(\" | \") + \" |\").join(\"\\n\") + \"\\n\";\n }\n\n return \"\\n\" + rows.map((r) => \"| \" + r.join(\" | \") + \" |\").join(\"\\n\") + \"\\n\";\n },\n );\n\n // Process headings\n content = content.replace(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/gi, (_m, text) => `\\n# ${stripTags(text).trim()}\\n`);\n content = content.replace(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi, (_m, text) => `\\n## ${stripTags(text).trim()}\\n`);\n content = content.replace(/<h3[^>]*>([\\s\\S]*?)<\\/h3>/gi, (_m, text) => `\\n### ${stripTags(text).trim()}\\n`);\n content = content.replace(/<h4[^>]*>([\\s\\S]*?)<\\/h4>/gi, (_m, text) => `\\n#### ${stripTags(text).trim()}\\n`);\n content = content.replace(/<h5[^>]*>([\\s\\S]*?)<\\/h5>/gi, (_m, text) => `\\n##### ${stripTags(text).trim()}\\n`);\n content = content.replace(/<h6[^>]*>([\\s\\S]*?)<\\/h6>/gi, (_m, text) => `\\n###### ${stripTags(text).trim()}\\n`);\n\n // Process paragraphs\n content = content.replace(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi, (_m, text) => `\\n${stripTags(text).trim()}\\n`);\n\n // Strip remaining HTML tags\n content = stripTags(content);\n\n // Decode HTML entities\n content = decodeHtmlEntities(content);\n\n // Normalize whitespace: collapse multiple blank lines\n content = content.replace(/\\n{3,}/g, \"\\n\\n\");\n\n return content.trim();\n}\n\n/**\n * Extract rows from an HTML table section.\n */\nfunction extractTableRows(html: string): string[][] {\n const rows: string[][] = [];\n const rowRegex = /<tr[^>]*>([\\s\\S]*?)<\\/tr>/gi;\n let rowMatch: RegExpExecArray | null;\n\n while ((rowMatch = rowRegex.exec(html)) !== null) {\n const cells: string[] = [];\n const cellRegex = /<(?:td|th)[^>]*>([\\s\\S]*?)<\\/(?:td|th)>/gi;\n let cellMatch: RegExpExecArray | null;\n\n while ((cellMatch = cellRegex.exec(rowMatch[1])) !== null) {\n cells.push(stripTags(cellMatch[1]).trim());\n }\n\n if (cells.length > 0) {\n rows.push(cells);\n }\n }\n\n return rows;\n}\n\n/**\n * Strip all HTML tags from a string.\n */\nfunction stripTags(html: string): string {\n return html.replace(/<[^>]+>/g, \"\");\n}\n\n/**\n * Decode common HTML entities.\n */\nfunction decodeHtmlEntities(text: string): string {\n return text\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&nbsp;/g, \" \");\n}\n\n// ---------------------------------------------------------------------------\n// extractContentAsMarkdown\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the page content as Markdown.\n *\n * Fetches the full page HTML via Runtime.evaluate, then converts to markdown\n * using a lightweight HTML-to-Markdown converter that:\n * - Converts headings to # syntax\n * - Wraps code blocks in fenced markdown\n * - Converts tables to markdown tables\n * - Excludes nav/aside elements\n *\n * @param cdp - CDP connection.\n * @param params - Optional selector to scope extraction.\n * @returns Markdown string.\n */\nexport async function extractContentAsMarkdown(\n cdp: CDPConnection,\n params: MarkdownParams,\n): Promise<MarkdownResult> {\n // Get the HTML content\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"document.documentElement.outerHTML\",\n returnByValue: true,\n })) as {\n result: { type: string; value: string };\n };\n\n const html = evalResult.result.value;\n const markdown = htmlToMarkdown(html);\n\n return { markdown };\n}\n","/**\n * browser_navigate_back — navigates back or forward in browser history.\n * Uses Page.getNavigationHistory + Page.navigateToHistoryEntry (session-compatible).\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\nexport interface NavigateBackParams {\n direction?: \"back\" | \"forward\";\n}\n\nexport interface NavigateBackResult {\n success: boolean;\n url?: string;\n}\n\nexport async function browserNavigateBack(\n cdp: CDPConnection,\n params: NavigateBackParams,\n): Promise<NavigateBackResult> {\n const direction = params.direction ?? \"back\";\n\n const history = (await cdp.send(\"Page.getNavigationHistory\")) as {\n currentIndex: number;\n entries: Array<{ id: number; url: string; title: string }>;\n };\n\n const targetIndex = direction === \"back\"\n ? history.currentIndex - 1\n : history.currentIndex + 1;\n\n if (targetIndex < 0 || targetIndex >= history.entries.length) {\n return { success: false, url: history.entries[history.currentIndex]?.url };\n }\n\n const entry = history.entries[targetIndex]!;\n await cdp.send(\"Page.navigateToHistoryEntry\", { entryId: entry.id } as unknown as Record<string, unknown>);\n\n return { success: true, url: entry.url };\n}\n","/**\n * browser_press_key tool — dispatches a single key press (keyDown + keyUp)\n * via CDP Input.dispatchKeyEvent. Supports key combinations like \"Control+c\".\n *\n * For modifier combos: sends keyDown for each modifier, then keyDown+keyUp\n * for the main key, then keyUp for modifiers in reverse order.\n * For regular characters without modifiers, also sends a \"char\" event.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Key → virtual key code mapping\n// ---------------------------------------------------------------------------\nconst KEY_CODE_MAP: Record<string, number> = {\n Enter: 13,\n Tab: 9,\n Escape: 27,\n Backspace: 8,\n Delete: 46,\n ArrowLeft: 37,\n ArrowUp: 38,\n ArrowRight: 39,\n ArrowDown: 40,\n Home: 36,\n End: 35,\n PageUp: 33,\n PageDown: 34,\n Insert: 45,\n Space: 32,\n \" \": 32,\n F1: 112,\n F2: 113,\n F3: 114,\n F4: 115,\n F5: 116,\n F6: 117,\n F7: 118,\n F8: 119,\n F9: 120,\n F10: 121,\n F11: 122,\n F12: 123,\n Control: 17,\n Shift: 16,\n Alt: 18,\n Meta: 91,\n};\n\n// Key → code mapping (DOM KeyboardEvent.code)\nconst KEY_TO_CODE: Record<string, string> = {\n Enter: \"Enter\",\n Tab: \"Tab\",\n Escape: \"Escape\",\n Backspace: \"Backspace\",\n Delete: \"Delete\",\n ArrowLeft: \"ArrowLeft\",\n ArrowUp: \"ArrowUp\",\n ArrowRight: \"ArrowRight\",\n ArrowDown: \"ArrowDown\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"PageUp\",\n PageDown: \"PageDown\",\n Insert: \"Insert\",\n Space: \"Space\",\n \" \": \"Space\",\n Control: \"ControlLeft\",\n Shift: \"ShiftLeft\",\n Alt: \"AltLeft\",\n Meta: \"MetaLeft\",\n};\n\n// Special key → text mapping for CDP key events\nconst KEY_TEXT_MAP: Record<string, string> = {\n Enter: \"\\r\",\n Tab: \"\\t\",\n Escape: \"\",\n Space: \" \",\n Backspace: \"\\b\",\n};\n\n// Modifier keys → bitfield values\nconst MODIFIER_BITS: Record<string, number> = {\n Alt: 1,\n Control: 2,\n Meta: 4,\n Shift: 8,\n};\n\nexport interface PressKeyParams {\n key: string;\n}\n\nexport interface PressKeyResult {\n success: boolean;\n}\n\n/**\n * Resolves a key name to its virtual key code.\n */\nfunction getKeyCode(key: string): number {\n if (KEY_CODE_MAP[key] !== undefined) {\n return KEY_CODE_MAP[key];\n }\n // Single character: use its char code (uppercase)\n if (key.length === 1) {\n return key.toUpperCase().charCodeAt(0);\n }\n return 0;\n}\n\n/**\n * Resolves a key name to its DOM code property.\n */\nfunction getCode(key: string): string {\n if (KEY_TO_CODE[key] !== undefined) {\n return KEY_TO_CODE[key];\n }\n if (key.length === 1) {\n const upper = key.toUpperCase();\n if (upper >= \"A\" && upper <= \"Z\") {\n return `Key${upper}`;\n }\n if (upper >= \"0\" && upper <= \"9\") {\n return `Digit${upper}`;\n }\n }\n return key;\n}\n\n/**\n * Resolves the text value for a key press.\n * Returns the text to send with \"char\" and \"keyDown\" events.\n */\nfunction getKeyText(key: string): string | undefined {\n // Check special key text mappings first\n if (KEY_TEXT_MAP[key] !== undefined) {\n return KEY_TEXT_MAP[key] || undefined;\n }\n // Single printable character\n if (key.length === 1) {\n return key;\n }\n return undefined;\n}\n\n/**\n * Dispatches a single key press or key combination via CDP.\n *\n * Key combinations are specified as \"Modifier+key\" (e.g. \"Control+c\", \"Shift+Tab\").\n * Multiple modifiers can be combined: \"Control+Shift+a\".\n *\n * For modifier combos: sends keyDown for each modifier, then keyDown+keyUp\n * for the main key, then keyUp for modifiers in reverse order.\n * For regular characters without modifiers, also sends a \"char\" event with the text.\n */\nexport async function browserPressKey(\n cdp: CDPConnection,\n params: PressKeyParams,\n): Promise<PressKeyResult> {\n const parts = params.key.split(\"+\");\n const mainKey = parts[parts.length - 1];\n const modifierKeys = parts.slice(0, -1);\n\n // Calculate combined modifier bitfield for the main key event\n let modifiers = 0;\n for (const mod of modifierKeys) {\n if (MODIFIER_BITS[mod] !== undefined) {\n modifiers |= MODIFIER_BITS[mod];\n }\n }\n\n // Press modifier keys down first\n for (const mod of modifierKeys) {\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: mod,\n code: getCode(mod),\n windowsVirtualKeyCode: getKeyCode(mod),\n modifiers,\n } as unknown as Record<string, unknown>);\n }\n\n // Resolve text for the main key\n const text = getKeyText(mainKey);\n\n // Dispatch main key down\n const keyDownParams: Record<string, unknown> = {\n type: \"keyDown\",\n key: mainKey,\n code: getCode(mainKey),\n windowsVirtualKeyCode: getKeyCode(mainKey),\n modifiers,\n };\n\n if (text !== undefined) {\n keyDownParams.text = text;\n }\n\n await cdp.send(\"Input.dispatchKeyEvent\", keyDownParams);\n\n // For characters that produce text (without modifiers), send a \"char\" event\n if (text && modifierKeys.length === 0) {\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"char\",\n key: mainKey,\n code: getCode(mainKey),\n windowsVirtualKeyCode: getKeyCode(mainKey),\n modifiers,\n text,\n } as unknown as Record<string, unknown>);\n }\n\n // Dispatch main key up\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: mainKey,\n code: getCode(mainKey),\n windowsVirtualKeyCode: getKeyCode(mainKey),\n modifiers,\n } as unknown as Record<string, unknown>);\n\n // Release modifier keys in reverse order\n for (let i = modifierKeys.length - 1; i >= 0; i--) {\n const mod = modifierKeys[i];\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: mod,\n code: getCode(mod),\n windowsVirtualKeyCode: getKeyCode(mod),\n modifiers: 0,\n } as unknown as Record<string, unknown>);\n }\n\n return { success: true };\n}\n","/**\n * browser_hover tool — hovers over an element by ref or CSS selector.\n *\n * Element resolution follows the same pattern as browser_click:\n * 1. If `ref` provided (@eN) -> extract backendNodeId -> DOM.resolveNode\n * 2. If `selector` provided -> DOM.getDocument -> DOM.querySelector -> nodeId\n * 3. DOM.scrollIntoViewIfNeeded -> DOM.getBoxModel -> center of content quad\n *\n * Hover dispatches a single Input.dispatchMouseEvent of type \"mouseMoved\"\n * to the center of the element. This triggers mouseover/mouseenter events\n * in the browser.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface HoverParams {\n /** @eN ref from accessibility snapshot */\n ref?: string;\n /** CSS selector */\n selector?: string;\n /** Human-readable element description */\n element?: string;\n}\n\nexport interface HoverResult {\n success: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Ref pattern\n// ---------------------------------------------------------------------------\nconst REF_PATTERN = /^@?e(\\d+)$/;\n\n// ---------------------------------------------------------------------------\n// Element resolution helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Calculates the center point of a content quad from DOM.getBoxModel.\n * Content quad is 8 numbers: [x1,y1, x2,y2, x3,y3, x4,y4].\n */\nfunction calculateCenter(content: number[]): { x: number; y: number } {\n const x = (content[0] + content[2] + content[4] + content[6]) / 4;\n const y = (content[1] + content[3] + content[5] + content[7]) / 4;\n return { x: Math.round(x), y: Math.round(y) };\n}\n\n/**\n * Resolves an element to coordinates by ref or selector.\n * Returns the center (x, y) of the element's content box.\n */\nasync function resolveElementCoordinates(\n cdp: CDPConnection,\n params: HoverParams,\n): Promise<{ x: number; y: number }> {\n let backendNodeId: number | undefined;\n let nodeId: number | undefined;\n\n if (params.ref) {\n // Parse @eN ref to extract backendNodeId\n const match = REF_PATTERN.exec(params.ref);\n if (!match) {\n throw new Error(`Invalid ref format: ${params.ref}`);\n }\n backendNodeId = parseInt(match[1], 10);\n\n // Resolve to validate the node exists\n await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n } as unknown as Record<string, unknown>);\n } else if (params.selector) {\n // Resolve via DOM.querySelector\n const docResponse = (await cdp.send(\"DOM.getDocument\")) as {\n root: { nodeId: number };\n };\n\n const queryResponse = (await cdp.send(\"DOM.querySelector\", {\n nodeId: docResponse.root.nodeId,\n selector: params.selector,\n } as unknown as Record<string, unknown>)) as { nodeId: number };\n\n if (!queryResponse.nodeId || queryResponse.nodeId === 0) {\n throw new Error(\n `Element not found: no element matches selector \"${params.selector}\"`,\n );\n }\n\n nodeId = queryResponse.nodeId;\n } else {\n throw new Error(\"Either ref or selector must be provided\");\n }\n\n // Scroll element into view\n const scrollParams: Record<string, unknown> = {};\n if (backendNodeId !== undefined) {\n scrollParams.backendNodeId = backendNodeId;\n } else if (nodeId !== undefined) {\n scrollParams.nodeId = nodeId;\n }\n await cdp.send(\"DOM.scrollIntoViewIfNeeded\", scrollParams);\n\n // Get box model for center coordinates\n const boxParams: Record<string, unknown> = {};\n if (backendNodeId !== undefined) {\n boxParams.backendNodeId = backendNodeId;\n } else if (nodeId !== undefined) {\n boxParams.nodeId = nodeId;\n }\n\n const boxResponse = (await cdp.send(\"DOM.getBoxModel\", boxParams)) as {\n model: {\n content: number[];\n width: number;\n height: number;\n };\n };\n\n return calculateCenter(boxResponse.model.content);\n}\n\n// ---------------------------------------------------------------------------\n// Main hover implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Hovers over an element identified by ref or CSS selector.\n *\n * Dispatches a single mouseMoved event to the element's center,\n * which triggers mouseover/mouseenter browser events.\n *\n * @param cdp - CDP connection with send/on/off methods.\n * @param params - Hover parameters.\n * @returns Result with success status.\n */\nexport async function browserHover(\n cdp: CDPConnection,\n params: HoverParams,\n): Promise<HoverResult> {\n const { x, y } = await resolveElementCoordinates(cdp, params);\n\n // Dispatch mouseMoved to trigger hover\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseMoved\",\n x,\n y,\n } as unknown as Record<string, unknown>);\n\n return { success: true };\n}\n","/**\n * browser_resize tool — Resizes the browser viewport.\n *\n * Supports:\n * - Explicit width/height dimensions\n * - Named presets: \"mobile\", \"tablet\", \"desktop\", \"fullhd\"\n * - Custom device scale factor\n * - Uses Emulation.setDeviceMetricsOverride\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Presets\n// ---------------------------------------------------------------------------\n\nconst PRESETS: Record<string, { width: number; height: number }> = {\n mobile: { width: 375, height: 667 },\n tablet: { width: 768, height: 1024 },\n desktop: { width: 1280, height: 720 },\n fullhd: { width: 1920, height: 1080 },\n};\n\n// ---------------------------------------------------------------------------\n// Parameter types\n// ---------------------------------------------------------------------------\n\nexport interface ResizeParams {\n /** Viewport width in CSS pixels */\n width?: number;\n /** Viewport height in CSS pixels */\n height?: number;\n /** Device scale factor (DPR). Defaults to 0 (use browser default). */\n deviceScaleFactor?: number;\n /** Named preset: \"mobile\", \"tablet\", \"desktop\", \"fullhd\". */\n preset?: string;\n}\n\nexport interface ResizeResult {\n success: boolean;\n width: number;\n height: number;\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Resizes the browser viewport by overriding device metrics.\n *\n * If a preset is given, uses preset dimensions as defaults.\n * Explicit width/height params override preset values.\n * Sets mobile emulation when width < 768.\n */\nexport async function browserResize(\n cdp: CDPConnection,\n params: ResizeParams,\n): Promise<ResizeResult> {\n let width: number;\n let height: number;\n\n // Handle \"reset\" preset — clears device metrics override\n if (params.preset?.toLowerCase() === \"reset\") {\n await cdp.send(\"Emulation.clearDeviceMetricsOverride\");\n return { success: true, width: 0, height: 0 };\n }\n\n // Start with preset dimensions if provided\n if (params.preset) {\n const preset = PRESETS[params.preset.toLowerCase()];\n if (!preset) {\n throw new Error(\n `Unknown preset \"${params.preset}\". Available: ${Object.keys(PRESETS).join(\", \")}, reset`,\n );\n }\n width = preset.width;\n height = preset.height;\n } else {\n // Default to desktop if no preset and no dimensions\n width = params.width ?? 1280;\n height = params.height ?? 720;\n }\n\n // Explicit width/height override preset values\n if (params.width !== undefined) width = params.width;\n if (params.height !== undefined) height = params.height;\n\n const deviceScaleFactor = params.deviceScaleFactor ?? 0;\n const mobile = width < 768;\n\n await cdp.send(\"Emulation.setDeviceMetricsOverride\", {\n width,\n height,\n deviceScaleFactor,\n mobile,\n });\n\n return {\n success: true,\n width,\n height,\n };\n}\n","/**\n * browser_close tool — closes browser tabs/targets via CDP.\n *\n * Supports:\n * - Close all page targets (closeAll)\n * - Close a specific target by targetId\n * - Close the current active tab (default)\n *\n * Returns the count of closed targets.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CloseParams {\n /** Specific target ID to close. */\n targetId?: string;\n /** Whether to force close (skip beforeunload). */\n force?: boolean;\n /** Close all page targets. */\n closeAll?: boolean;\n}\n\nexport interface CloseResult {\n success: boolean;\n closedTargets: number;\n}\n\n// ---------------------------------------------------------------------------\n// Types for CDP responses\n// ---------------------------------------------------------------------------\n\ninterface TargetInfo {\n targetId: string;\n type: string;\n title: string;\n url: string;\n attached?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Main implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Closes browser tabs/targets.\n *\n * - closeAll: closes all page-type targets\n * - targetId: closes that specific target\n * - default: closes the current active tab (attached page target, or first page)\n *\n * @param cdp - CDP connection with send/on/off methods.\n * @param params - Close parameters.\n * @returns Result with success status and count of closed targets.\n */\nexport async function browserClose(\n cdp: CDPConnection,\n params: CloseParams,\n): Promise<CloseResult> {\n let closedCount = 0;\n\n if (params.closeAll) {\n // Close all page targets\n const targetsResponse = (await cdp.send(\"Target.getTargets\")) as {\n targetInfos: TargetInfo[];\n };\n\n const pageTargets = targetsResponse.targetInfos.filter(\n (t) => t.type === \"page\",\n );\n\n for (const target of pageTargets) {\n try {\n await cdp.send(\"Target.closeTarget\", {\n targetId: target.targetId,\n } as unknown as Record<string, unknown>);\n closedCount++;\n } catch {\n // Target may already be closed — ignore\n }\n }\n } else if (params.targetId) {\n // Close a specific target\n await cdp.send(\"Target.closeTarget\", {\n targetId: params.targetId,\n } as unknown as Record<string, unknown>);\n closedCount = 1;\n } else {\n // Close the current active tab\n const targetsResponse = (await cdp.send(\"Target.getTargets\")) as {\n targetInfos: TargetInfo[];\n };\n\n const pageTargets = targetsResponse.targetInfos.filter(\n (t) => t.type === \"page\",\n );\n\n if (pageTargets.length === 0) {\n throw new Error(\"No page targets found to close\");\n }\n\n // Find the attached (active) page target, or fall back to the first page\n const activeTarget =\n pageTargets.find((t) => t.attached) ?? pageTargets[0];\n\n await cdp.send(\"Target.closeTarget\", {\n targetId: activeTarget.targetId,\n } as unknown as Record<string, unknown>);\n closedCount = 1;\n }\n\n return {\n success: true,\n closedTargets: closedCount,\n };\n}\n","/**\n * browser_fill_form tool — fills form fields by ref or CSS selector.\n *\n * Supports field types:\n * - textbox: focus → clear value → insert text → dispatch input/change events\n * - checkbox: click to toggle checked state\n * - radio: click to select\n * - combobox: set value via Runtime.callFunctionOn (select dropdown)\n * - slider: set value via Runtime.callFunctionOn and dispatch events\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FillFormField {\n name: string;\n type: string;\n ref?: string;\n selector?: string;\n value: string;\n}\n\nexport interface FillFormParams {\n fields: FillFormField[];\n}\n\nexport interface FillFormError {\n field: string;\n error: string;\n}\n\nexport interface FillFormResult {\n success: boolean;\n filledCount: number;\n errors?: FillFormError[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts the numeric backendNodeId from an \"@eN\" ref string.\n */\nfunction parseRef(ref: string): number {\n const match = ref.match(/@?e(\\d+)/);\n if (!match) {\n throw new Error(`Invalid ref format: ${ref}`);\n }\n return parseInt(match[1], 10);\n}\n\n/**\n * Resolves a field to its objectId for use with Runtime.callFunctionOn.\n * Supports both @eN refs and CSS selectors.\n */\nasync function resolveObjectId(\n cdp: CDPConnection,\n field: FillFormField,\n): Promise<string> {\n if (field.ref) {\n const backendNodeId = parseRef(field.ref);\n const result = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n } as unknown as Record<string, unknown>)) as {\n object: { objectId: string };\n };\n return result.object.objectId;\n }\n\n if (field.selector) {\n // Use Runtime.evaluate to query the selector\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector(${JSON.stringify(field.selector)})`,\n returnByValue: false,\n } as unknown as Record<string, unknown>)) as {\n result: { objectId?: string };\n };\n\n if (evalResult.result.objectId) {\n return evalResult.result.objectId;\n }\n\n // Fallback: DOM.querySelector approach\n const docResult = (await cdp.send(\n \"DOM.getDocument\",\n {} as Record<string, unknown>,\n )) as { root: { nodeId: number } };\n const queryResult = (await cdp.send(\"DOM.querySelector\", {\n nodeId: docResult.root.nodeId,\n selector: field.selector,\n } as unknown as Record<string, unknown>)) as { nodeId: number };\n\n const resolveResult = (await cdp.send(\"DOM.resolveNode\", {\n nodeId: queryResult.nodeId,\n } as unknown as Record<string, unknown>)) as {\n object: { objectId: string };\n };\n return resolveResult.object.objectId;\n }\n\n throw new Error(`Field \"${field.name}\" has neither ref nor selector`);\n}\n\n/**\n * Checks if an element is readonly or disabled.\n * Returns true if the element cannot be modified.\n */\nasync function isReadonlyOrDisabled(\n cdp: CDPConnection,\n objectId: string,\n): Promise<boolean> {\n const result = (await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() { return this.readOnly || this.disabled; }`,\n returnByValue: true,\n } as unknown as Record<string, unknown>)) as {\n result: { value?: boolean };\n };\n\n return result.result.value === true;\n}\n\n/**\n * Clicks an element by computing its center from the box model.\n */\nasync function clickElement(\n cdp: CDPConnection,\n field: FillFormField,\n): Promise<void> {\n let backendNodeId: number | undefined;\n\n if (field.ref) {\n backendNodeId = parseRef(field.ref);\n }\n\n // Scroll into view\n if (backendNodeId !== undefined) {\n await cdp.send(\"DOM.scrollIntoViewIfNeeded\", {\n backendNodeId,\n } as unknown as Record<string, unknown>);\n }\n\n // Get box model\n const boxResult = (await cdp.send(\"DOM.getBoxModel\", {\n backendNodeId,\n } as unknown as Record<string, unknown>)) as {\n model: { content: number[]; width: number; height: number };\n };\n\n const quad = boxResult.model.content;\n // Compute center of the content quad (4 corner points: x1,y1,x2,y2,x3,y3,x4,y4)\n const centerX = (quad[0] + quad[2] + quad[4] + quad[6]) / 4;\n const centerY = (quad[1] + quad[3] + quad[5] + quad[7]) / 4;\n\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mousePressed\",\n x: centerX,\n y: centerY,\n button: \"left\",\n clickCount: 1,\n } as unknown as Record<string, unknown>);\n\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseReleased\",\n x: centerX,\n y: centerY,\n button: \"left\",\n clickCount: 1,\n } as unknown as Record<string, unknown>);\n}\n\n/**\n * Fills a single textbox field: focus → clear → insert text → dispatch events.\n */\nasync function fillTextbox(\n cdp: CDPConnection,\n field: FillFormField,\n objectId: string,\n): Promise<void> {\n // Focus the element\n if (field.ref) {\n const backendNodeId = parseRef(field.ref);\n await cdp.send(\"DOM.focus\", {\n backendNodeId,\n } as unknown as Record<string, unknown>);\n } else {\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() { this.focus(); }`,\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n }\n\n // Clear existing value and dispatch input event\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() { this.value = ''; this.dispatchEvent(new Event('input', { bubbles: true })); }`,\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n\n // Insert the new text\n await cdp.send(\"Input.insertText\", {\n text: field.value,\n } as unknown as Record<string, unknown>);\n\n // Dispatch input and change events\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() {\n this.dispatchEvent(new Event('input', { bubbles: true }));\n this.dispatchEvent(new Event('change', { bubbles: true }));\n }`,\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n}\n\n/**\n * Fills a combobox (select dropdown) field.\n */\nasync function fillCombobox(\n cdp: CDPConnection,\n _field: FillFormField,\n objectId: string,\n value: string,\n): Promise<void> {\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function(val) {\n this.value = val;\n this.dispatchEvent(new Event('input', { bubbles: true }));\n this.dispatchEvent(new Event('change', { bubbles: true }));\n return [val];\n }`,\n arguments: [{ value }],\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n}\n\n/**\n * Fills a slider (range input) field.\n */\nasync function fillSlider(\n cdp: CDPConnection,\n _field: FillFormField,\n objectId: string,\n value: string,\n): Promise<void> {\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function(val) {\n this.value = val;\n this.dispatchEvent(new Event('input', { bubbles: true }));\n this.dispatchEvent(new Event('change', { bubbles: true }));\n }`,\n arguments: [{ value }],\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Fills one or more form fields via CDP.\n */\nexport async function browserFillForm(\n cdp: CDPConnection,\n params: FillFormParams,\n): Promise<FillFormResult> {\n let filledCount = 0;\n const errors: FillFormError[] = [];\n\n for (const field of params.fields) {\n try {\n const objectId = await resolveObjectId(cdp, field);\n\n switch (field.type) {\n case \"textbox\": {\n // Check readonly/disabled before filling\n const isBlocked = await isReadonlyOrDisabled(cdp, objectId);\n if (isBlocked) {\n errors.push({\n field: field.name,\n error: `Cannot fill readonly or disabled field \"${field.name}\"`,\n });\n continue;\n }\n\n await fillTextbox(cdp, field, objectId);\n filledCount++;\n break;\n }\n\n case \"checkbox\": {\n // Toggle checkbox by clicking\n await clickElement(cdp, field);\n filledCount++;\n break;\n }\n\n case \"radio\": {\n // Select radio button by clicking\n await clickElement(cdp, field);\n filledCount++;\n break;\n }\n\n case \"combobox\": {\n await fillCombobox(cdp, field, objectId, field.value);\n filledCount++;\n break;\n }\n\n case \"slider\": {\n await fillSlider(cdp, field, objectId, field.value);\n filledCount++;\n break;\n }\n\n default: {\n // Default to textbox behavior\n await fillTextbox(cdp, field, objectId);\n filledCount++;\n break;\n }\n }\n } catch (err) {\n errors.push({\n field: field.name,\n error:\n err instanceof Error ? err.message : `Unknown error filling ${field.name}`,\n });\n }\n }\n\n return {\n success: errors.length === 0,\n filledCount,\n errors: errors.length > 0 ? errors : undefined,\n };\n}\n","/**\n * browser_type tool — types text into a focused element or an element\n * identified by ref/selector.\n *\n * Fast mode (default): uses Input.insertText for a single CDP call.\n * Slow mode (slowly=true): dispatches individual keyDown/keyUp events per char.\n * submit=true: presses Enter after typing.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\nexport interface TypeParams {\n text: string;\n ref?: string;\n selector?: string;\n slowly?: boolean;\n submit?: boolean;\n}\n\nexport interface TypeResult {\n success: boolean;\n}\n\n/**\n * Extracts the numeric backendNodeId from an \"@eN\" ref string.\n */\nfunction parseRef(ref: string): number {\n const match = ref.match(/@e(\\d+)/);\n if (!match) {\n throw new Error(`Invalid ref format: ${ref}`);\n }\n return parseInt(match[1], 10);\n}\n\n/**\n * Resolves a key name to its virtual key code.\n */\nfunction getKeyCode(key: string): number {\n if (key.length === 1) {\n return key.toUpperCase().charCodeAt(0);\n }\n const codes: Record<string, number> = {\n Enter: 13,\n Tab: 9,\n Backspace: 8,\n };\n return codes[key] ?? 0;\n}\n\n/**\n * Resolves a key name to its DOM KeyboardEvent.code.\n */\nfunction getCode(key: string): string {\n if (key.length === 1) {\n const upper = key.toUpperCase();\n if (upper >= \"A\" && upper <= \"Z\") return `Key${upper}`;\n if (upper >= \"0\" && upper <= \"9\") return `Digit${upper}`;\n }\n return key;\n}\n\n/**\n * Promise-based delay.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Dispatches a single character via keyDown, char, and keyUp events.\n */\nasync function dispatchCharEvents(\n cdp: CDPConnection,\n char: string,\n): Promise<void> {\n const keyCode = getKeyCode(char);\n const code = getCode(char);\n\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: char,\n code,\n windowsVirtualKeyCode: keyCode,\n text: char,\n } as unknown as Record<string, unknown>);\n\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"char\",\n key: char,\n code,\n windowsVirtualKeyCode: keyCode,\n text: char,\n } as unknown as Record<string, unknown>);\n\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: char,\n code,\n windowsVirtualKeyCode: keyCode,\n } as unknown as Record<string, unknown>);\n}\n\n/**\n * Types text into the currently focused element or an element by ref.\n */\nexport async function browserType(\n cdp: CDPConnection,\n params: TypeParams,\n): Promise<TypeResult> {\n // Focus the target element if ref is provided\n if (params.ref) {\n const backendNodeId = parseRef(params.ref);\n await cdp.send(\"DOM.focus\", {\n backendNodeId,\n } as unknown as Record<string, unknown>);\n }\n\n // Focus by selector if provided (no ref)\n if (!params.ref && params.selector) {\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector(${JSON.stringify(params.selector)})`,\n returnByValue: false,\n } as unknown as Record<string, unknown>)) as {\n result: { objectId?: string };\n };\n\n if (evalResult.result.objectId) {\n await cdp.send(\"Runtime.callFunctionOn\", {\n objectId: evalResult.result.objectId,\n functionDeclaration: `function() { this.focus(); }`,\n returnByValue: true,\n } as unknown as Record<string, unknown>);\n }\n }\n\n if (params.slowly) {\n // Slow mode: dispatch key events for each character with 50ms delay between\n for (let i = 0; i < params.text.length; i++) {\n await dispatchCharEvents(cdp, params.text[i]);\n if (i < params.text.length - 1) {\n await delay(50);\n }\n }\n } else {\n // Fast mode: single Input.insertText call\n await cdp.send(\"Input.insertText\", {\n text: params.text,\n } as unknown as Record<string, unknown>);\n }\n\n // Submit: press Enter after typing\n if (params.submit) {\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: \"Enter\",\n code: \"Enter\",\n windowsVirtualKeyCode: 13,\n } as unknown as Record<string, unknown>);\n\n await cdp.send(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: \"Enter\",\n code: \"Enter\",\n windowsVirtualKeyCode: 13,\n } as unknown as Record<string, unknown>);\n }\n\n return { success: true };\n}\n","/**\n * browser_select_option tool — Selects options in a <select> element.\n *\n * Resolves the element via @eN ref (backendNodeId), sets the selected\n * options, and dispatches input + change events.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Parameter types\n// ---------------------------------------------------------------------------\n\ninterface SelectOptionParams {\n /** @eN ref string, e.g. \"@e4\" */\n ref: string;\n /** Values to select (by value attribute or label text) */\n values: string[];\n /** Human-readable element description */\n element: string;\n}\n\ninterface SelectOptionResult {\n success: boolean;\n selected: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts the backendNodeId from an @eN ref string.\n * In production this would use the RefSystem; for now we parse the number directly.\n */\nfunction refToBackendNodeId(ref: string): number {\n const match = /^@?e(\\d+)$/.exec(ref);\n if (!match) {\n throw new Error(`Invalid ref format: ${ref}`);\n }\n return parseInt(match[1], 10);\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Selects one or more options in a <select> element.\n *\n * - Resolves the @eN ref to a backendNodeId\n * - Uses DOM.resolveNode to get an objectId\n * - Calls Runtime.callFunctionOn to set selected options and dispatch events\n */\nexport async function browserSelectOption(\n cdp: CDPConnection,\n params: SelectOptionParams,\n): Promise<SelectOptionResult> {\n const backendNodeId = refToBackendNodeId(params.ref);\n\n // Resolve the node to get an objectId for Runtime.callFunctionOn\n const resolveResponse = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n })) as { object: { objectId: string } };\n\n const objectId = resolveResponse.object.objectId;\n const valuesJson = JSON.stringify(params.values);\n\n // Select the options and dispatch events\n const result = (await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() {\n var select = this;\n if (select.tagName !== 'SELECT') {\n throw new Error('Element is not a SELECT');\n }\n var values = ${valuesJson};\n var matched = [];\n\n for (var i = 0; i < select.options.length; i++) {\n var option = select.options[i];\n var isMatch = values.indexOf(option.value) !== -1 ||\n values.indexOf(option.textContent.trim()) !== -1;\n option.selected = isMatch;\n if (isMatch) {\n matched.push(option.value);\n }\n }\n\n select.dispatchEvent(new Event('input', { bubbles: true }));\n select.dispatchEvent(new Event('change', { bubbles: true }));\n\n return matched;\n }`,\n returnByValue: true,\n })) as { result: { type: string; value: unknown } };\n\n const selected = (result.result.value as string[]) ?? [];\n\n return {\n success: true,\n selected,\n };\n}\n","/**\n * browser_wait_for tool — waits for various conditions via CDP polling.\n *\n * Supported strategies:\n * - text: poll until text appears in page body\n * - textGone: poll until text disappears from page body\n * - selector: poll until a CSS selector matches an element in the DOM\n * - selector + visible: poll until element is visible\n * - selector + state:\"hidden\": poll until element is hidden\n * - time: simple delay (seconds)\n * - networkIdle: poll until no pending network requests\n * - load: poll until document.readyState === \"complete\"\n * - url: poll until location.href matches a glob pattern\n * - fn: poll until a JS expression evaluates to truthy\n *\n * Default timeout: 30 seconds. Poll interval: ~100ms.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\ninterface WaitForParams {\n text?: string;\n textGone?: string;\n selector?: string;\n visible?: boolean;\n state?: \"hidden\" | \"visible\";\n time?: number;\n networkIdle?: boolean;\n load?: boolean;\n loadState?: string;\n url?: string;\n fn?: string;\n timeout?: number;\n}\n\ninterface WaitForResult {\n success: boolean;\n elapsed: number;\n}\n\nconst DEFAULT_TIMEOUT_S = 30;\nconst POLL_INTERVAL_MS = 100;\n\n/**\n * Normalizes a timeout value to milliseconds.\n *\n * Values <= 60 are treated as seconds (e.g., 5 → 5000ms, 0.5 → 500ms).\n * Values > 60 are treated as milliseconds (e.g., 1000 → 1000ms).\n */\nfunction normalizeTimeoutMs(timeout: number): number {\n if (timeout > 60) {\n return timeout; // Already in ms\n }\n return timeout * 1000; // Convert seconds to ms\n}\n\nexport async function browserWaitFor(\n cdp: CDPConnection,\n params: WaitForParams,\n): Promise<WaitForResult> {\n const timeoutMs = normalizeTimeoutMs(params.timeout ?? DEFAULT_TIMEOUT_S);\n const start = Date.now();\n\n // ---- Simple time delay ----\n if (params.time !== undefined) {\n const delayMs = params.time * 1000;\n await delay(delayMs);\n return { success: true, elapsed: Date.now() - start };\n }\n\n // ---- Determine the polling predicate ----\n const condition = buildCondition(params);\n\n // ---- Poll loop ----\n while (true) {\n const elapsed = Date.now() - start;\n if (elapsed >= timeoutMs) {\n throw new Error(\n `Timeout after ${timeoutMs}ms waiting for condition: ${describeCondition(params)}`,\n );\n }\n\n let met = false;\n try {\n met = await evaluateCondition(cdp, condition);\n } catch {\n // Transient errors (e.g. cross-origin frame) — retry on next poll\n }\n\n if (met) {\n return { success: true, elapsed: Date.now() - start };\n }\n\n await delay(POLL_INTERVAL_MS);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Condition builders\n// ---------------------------------------------------------------------------\n\ninterface Condition {\n kind:\n | \"text\"\n | \"textGone\"\n | \"selector\"\n | \"selectorVisible\"\n | \"selectorHidden\"\n | \"networkIdle\"\n | \"load\"\n | \"loadState\"\n | \"url\"\n | \"fn\";\n expression: string;\n}\n\nfunction buildCondition(params: WaitForParams): Condition {\n if (params.url !== undefined) {\n return { kind: \"url\", expression: params.url };\n }\n\n if (params.fn !== undefined) {\n return {\n kind: \"fn\",\n expression: `Boolean(${params.fn})`,\n };\n }\n\n if (params.selector !== undefined && params.state === \"hidden\") {\n return {\n kind: \"selectorHidden\",\n expression: buildVisibilityCheck(params.selector),\n };\n }\n\n if (params.selector !== undefined && params.visible) {\n return {\n kind: \"selectorVisible\",\n expression: buildVisibilityCheck(params.selector),\n };\n }\n\n if (params.selector !== undefined) {\n return {\n kind: \"selector\",\n expression: `document.querySelector(${JSON.stringify(params.selector)})`,\n };\n }\n\n if (params.text !== undefined) {\n return {\n kind: \"text\",\n expression: `document.body && document.body.innerText.includes(${JSON.stringify(params.text)})`,\n };\n }\n\n if (params.textGone !== undefined) {\n return {\n kind: \"textGone\",\n expression: `document.body && !document.body.innerText.includes(${JSON.stringify(params.textGone)})`,\n };\n }\n\n if (params.networkIdle) {\n return {\n kind: \"networkIdle\",\n expression: \"true\", // Simplified: check via Runtime.evaluate\n };\n }\n\n if (params.loadState !== undefined) {\n return {\n kind: \"loadState\",\n expression: params.loadState,\n };\n }\n\n if (params.load) {\n return {\n kind: \"load\",\n expression: \"document.readyState\",\n };\n }\n\n throw new Error(\"browserWaitFor: no wait condition specified\");\n}\n\nfunction buildVisibilityCheck(selector: string): string {\n const sel = JSON.stringify(selector);\n return `(function() {\n var el = document.querySelector(${sel});\n if (!el) return false;\n var style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden' && el.offsetParent !== null;\n })()`;\n}\n\n// ---------------------------------------------------------------------------\n// Evaluation\n// ---------------------------------------------------------------------------\n\nasync function evaluateCondition(\n cdp: CDPConnection,\n condition: Condition,\n): Promise<boolean> {\n if (condition.kind === \"url\") {\n return evaluateUrlCondition(cdp, condition.expression);\n }\n\n if (condition.kind === \"load\") {\n return evaluateLoadCondition(cdp, condition.expression);\n }\n\n if (condition.kind === \"loadState\") {\n return evaluateLoadStateCondition(cdp, condition.expression);\n }\n\n if (condition.kind === \"selectorHidden\") {\n // Returns true when the element is NOT visible\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: condition.expression,\n returnByValue: true,\n })) as { result: { type: string; value: unknown } };\n\n // Element is hidden when the visibility check returns false\n return response.result.value === false;\n }\n\n if (condition.kind === \"selector\") {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: condition.expression,\n returnByValue: true,\n })) as { result: { type: string; value: unknown; subtype?: string } };\n\n // Element found if result is not null\n return (\n response.result.value !== null && response.result.subtype !== \"null\"\n );\n }\n\n // Generic boolean evaluation\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: condition.expression,\n returnByValue: true,\n })) as { result: { type: string; value: unknown } };\n\n return response.result.value === true;\n}\n\nasync function evaluateLoadCondition(\n cdp: CDPConnection,\n expression: string,\n): Promise<boolean> {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression,\n returnByValue: true,\n })) as { result: { type: string; value: unknown } };\n\n // readyState is a string: \"loading\" | \"interactive\" | \"complete\"\n // Also handle the case where the expression evaluates to a boolean\n if (response.result.type === \"string\") {\n return response.result.value === \"complete\";\n }\n return response.result.value === true;\n}\n\n/**\n * Evaluates a loadState condition.\n * For \"complete\", readyState must be \"complete\".\n * For \"interactive\", readyState must be \"interactive\" or \"complete\".\n */\nasync function evaluateLoadStateCondition(\n cdp: CDPConnection,\n targetState: string,\n): Promise<boolean> {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"document.readyState\",\n returnByValue: true,\n })) as { result: { type: string; value: string } };\n\n const current = response.result.value;\n\n if (targetState === \"complete\") {\n return current === \"complete\";\n }\n if (targetState === \"interactive\") {\n return current === \"interactive\" || current === \"complete\";\n }\n // Exact match for any other value\n return current === targetState;\n}\n\nasync function evaluateUrlCondition(\n cdp: CDPConnection,\n pattern: string,\n): Promise<boolean> {\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"location.href\",\n returnByValue: true,\n })) as { result: { type: string; value: string } };\n\n const currentUrl = response.result.value;\n return globMatch(pattern, currentUrl);\n}\n\n// ---------------------------------------------------------------------------\n// Glob matching (minimal implementation for URL patterns)\n// ---------------------------------------------------------------------------\n\nfunction globMatch(pattern: string, value: string): boolean {\n // Convert glob pattern to regex:\n // ** → match anything (including /)\n // * → match anything except /\n // ? → match single char\n const regexStr = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // escape regex chars (not * and ?)\n .replace(/\\*\\*/g, \"\\u0000\") // placeholder for **\n .replace(/\\*/g, \"[^/]*\") // * matches non-slash\n .replace(/\\u0000/g, \".*\") // ** matches anything\n .replace(/\\?/g, \".\"); // ? matches one char\n\n const regex = new RegExp(regexStr);\n return regex.test(value);\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction describeCondition(params: WaitForParams): string {\n if (params.text) return `text \"${params.text}\" to appear`;\n if (params.textGone) return `text \"${params.textGone}\" to disappear`;\n if (params.selector && params.state === \"hidden\")\n return `selector \"${params.selector}\" to become hidden`;\n if (params.selector && params.visible)\n return `selector \"${params.selector}\" to become visible`;\n if (params.selector) return `selector \"${params.selector}\" to appear`;\n if (params.url) return `URL matching \"${params.url}\"`;\n if (params.fn) return `JS condition: ${params.fn}`;\n if (params.loadState) return `document.readyState === \"${params.loadState}\"`;\n if (params.networkIdle) return \"network idle\";\n if (params.load) return \"page load complete\";\n return \"unknown condition\";\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * browser_drag tool — drags from a source to a target using synthesized mouse events.\n *\n * Element resolution follows the same pattern as browser_click:\n * 1. If `startRef`/`endRef` provided (@eN) -> extract backendNodeId -> DOM.resolveNode\n * 2. If `startX`/`startY` and `endX`/`endY` provided -> use directly\n * 3. DOM.scrollIntoViewIfNeeded -> DOM.getBoxModel -> center of content quad\n *\n * Drag sequence (CDP Input.dispatchMouseEvent):\n * 1. mouseMoved to start position\n * 2. mousePressed at start position (button: \"left\", clickCount: 1)\n * 3. One or more intermediate mouseMoved events toward the target\n * 4. mouseMoved to end position\n * 5. mouseReleased at end position (button: \"left\", clickCount: 1)\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DragParams {\n /** @eN ref for the source element */\n startRef?: string;\n /** Human-readable description of the source element */\n startElement?: string;\n /** @eN ref for the target element */\n endRef?: string;\n /** Human-readable description of the target element */\n endElement?: string;\n /** Direct start x coordinate */\n startX?: number;\n /** Direct start y coordinate */\n startY?: number;\n /** Direct end x coordinate */\n endX?: number;\n /** Direct end y coordinate */\n endY?: number;\n}\n\nexport interface DragResult {\n success: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Ref pattern\n// ---------------------------------------------------------------------------\nconst REF_PATTERN = /^@?e(\\d+)$/;\n\n// ---------------------------------------------------------------------------\n// Element resolution helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Calculates the center point of a content quad from DOM.getBoxModel.\n * Content quad is 8 numbers: [x1,y1, x2,y2, x3,y3, x4,y4].\n */\nfunction calculateCenter(content: number[]): { x: number; y: number } {\n const x = (content[0] + content[2] + content[4] + content[6]) / 4;\n const y = (content[1] + content[3] + content[5] + content[7]) / 4;\n return { x: Math.round(x), y: Math.round(y) };\n}\n\n/**\n * Resolves an element ref to coordinates.\n */\nasync function resolveRefCoordinates(\n cdp: CDPConnection,\n ref: string,\n): Promise<{ x: number; y: number }> {\n const match = REF_PATTERN.exec(ref);\n if (!match) {\n throw new Error(`Invalid ref format: ${ref}`);\n }\n const backendNodeId = parseInt(match[1], 10);\n const opts = { timeout: 5000 };\n\n // Resolve to validate the node exists\n await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n } as unknown as Record<string, unknown>, opts);\n\n // Scroll element into view\n try {\n await cdp.send(\"DOM.scrollIntoViewIfNeeded\", {\n backendNodeId,\n } as unknown as Record<string, unknown>, opts);\n } catch {\n // scrollIntoView can fail for some elements, not critical\n }\n\n // Get box model for center coordinates\n const boxResponse = (await cdp.send(\"DOM.getBoxModel\", {\n backendNodeId,\n } as unknown as Record<string, unknown>, opts)) as {\n model: {\n content: number[];\n width: number;\n height: number;\n };\n };\n\n return calculateCenter(boxResponse.model.content);\n}\n\n// ---------------------------------------------------------------------------\n// Delay helper\n// ---------------------------------------------------------------------------\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Generates intermediate points along a line from start to end.\n * Returns at least one intermediate point (midpoint).\n */\nfunction interpolatePoints(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n steps: number = 8,\n): Array<{ x: number; y: number }> {\n const points: Array<{ x: number; y: number }> = [];\n for (let i = 1; i < steps; i++) {\n const t = i / steps;\n points.push({\n x: Math.round(startX + (endX - startX) * t),\n y: Math.round(startY + (endY - startY) * t),\n });\n }\n return points;\n}\n\n// ---------------------------------------------------------------------------\n// Main drag implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Drags from a source element/coordinates to a target element/coordinates.\n *\n * The drag is performed using a sequence of mouse events:\n * mouseMoved(start) -> mousePressed(start) -> mouseMoved(intermediate)... ->\n * mouseMoved(end) -> mouseReleased(end)\n *\n * @param cdp - CDP connection with send/on/off methods.\n * @param params - Drag parameters.\n * @returns Result with success status.\n */\nexport async function browserDrag(\n cdp: CDPConnection,\n params: DragParams,\n): Promise<DragResult> {\n // Resolve start coordinates\n let startX: number;\n let startY: number;\n\n if (params.startX !== undefined && params.startY !== undefined) {\n startX = params.startX;\n startY = params.startY;\n } else if (params.startRef) {\n const coords = await resolveRefCoordinates(cdp, params.startRef);\n startX = coords.x;\n startY = coords.y;\n } else {\n throw new Error(\"Either startRef or startX/startY must be provided\");\n }\n\n // Resolve end coordinates\n let endX: number;\n let endY: number;\n\n if (params.endX !== undefined && params.endY !== undefined) {\n endX = params.endX;\n endY = params.endY;\n } else if (params.endRef) {\n const coords = await resolveRefCoordinates(cdp, params.endRef);\n endX = coords.x;\n endY = coords.y;\n } else {\n throw new Error(\"Either endRef or endX/endY must be provided\");\n }\n\n const mouseOpts = { timeout: 3000 };\n\n // 1. mouseMoved to start position\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseMoved\",\n x: startX,\n y: startY,\n } as unknown as Record<string, unknown>, mouseOpts);\n\n // 2. mousePressed at start position\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mousePressed\",\n x: startX,\n y: startY,\n button: \"left\",\n clickCount: 1,\n } as unknown as Record<string, unknown>, mouseOpts);\n\n // 3. Intermediate mouseMoved events (4 steps)\n const intermediatePoints = interpolatePoints(startX, startY, endX, endY, 4);\n for (const point of intermediatePoints) {\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseMoved\",\n x: point.x,\n y: point.y,\n } as unknown as Record<string, unknown>, mouseOpts);\n await delay(10);\n }\n\n // 4. mouseMoved to end position\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseMoved\",\n x: endX,\n y: endY,\n } as unknown as Record<string, unknown>, mouseOpts);\n\n // 5. mouseReleased at end position\n await cdp.send(\"Input.dispatchMouseEvent\", {\n type: \"mouseReleased\",\n x: endX,\n y: endY,\n button: \"left\",\n clickCount: 1,\n } as unknown as Record<string, unknown>, mouseOpts);\n\n return { success: true };\n}\n","/**\n * browser_handle_dialog tool — accepts or dismisses a JavaScript dialog via CDP.\n *\n * Uses Page.handleJavaScriptDialog to handle alert, confirm, prompt, and\n * beforeunload dialogs. If no dialog is currently pending, returns a helpful\n * error message.\n *\n * @module browser-handle-dialog\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface HandleDialogParams {\n /** Whether to accept (true) or dismiss (false) the dialog. */\n accept: boolean;\n /** Text to enter in a prompt dialog. Only used when accept is true. */\n promptText?: string;\n}\n\nexport interface HandleDialogResult {\n /** Whether the dialog was handled successfully. */\n success: boolean;\n /** Error message if the operation failed. */\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a pending JavaScript dialog (alert, confirm, prompt, beforeunload).\n *\n * @param cdp - CDP connection.\n * @param params - Dialog handling parameters.\n * @returns Result with success status.\n */\nexport async function browserHandleDialog(\n cdp: CDPConnection,\n params: HandleDialogParams,\n): Promise<HandleDialogResult> {\n const dialogParams: Record<string, unknown> = {\n accept: params.accept,\n };\n\n if (params.promptText !== undefined) {\n dialogParams.promptText = params.promptText;\n }\n\n try {\n await cdp.send(\"Page.handleJavaScriptDialog\", dialogParams);\n return { success: true };\n } catch (err: unknown) {\n const message =\n err instanceof Error ? err.message : String(err);\n\n // CDP returns an error when no dialog is pending\n if (message.includes(\"No dialog is showing\") || message.includes(\"no dialog\")) {\n return {\n success: false,\n error: \"No JavaScript dialog is currently pending. A dialog must be open before it can be handled.\",\n };\n }\n\n return {\n success: false,\n error: `Failed to handle dialog: ${message}`,\n };\n }\n}\n","/**\n * browser_file_upload tool — sets files on a file input element via CDP.\n *\n * Resolution:\n * 1. Parse @eN ref to extract backendNodeId\n * 2. DOM.resolveNode(backendNodeId) to get objectId\n * 3. DOM.setFileInputFiles({ files, objectId }) to set the files\n *\n * @module browser-file-upload\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FileUploadParams {\n /** @eN ref pointing to a file input element. */\n ref: string;\n /** Array of absolute file paths to upload. */\n paths: string[];\n}\n\nexport interface FileUploadResult {\n /** Whether the files were set successfully. */\n success: boolean;\n /** Number of files set on the input. */\n filesCount: number;\n /** Error message if the operation failed. */\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Ref pattern\n// ---------------------------------------------------------------------------\nconst REF_PATTERN = /^@e(\\d+)$/;\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Set files on a file input element identified by @eN ref.\n *\n * @param cdp - CDP connection.\n * @param params - File upload parameters.\n * @returns Result with success status and file count.\n */\nexport async function browserFileUpload(\n cdp: CDPConnection,\n params: FileUploadParams,\n): Promise<FileUploadResult> {\n // Parse @eN ref to extract backendNodeId\n const match = REF_PATTERN.exec(params.ref);\n if (!match) {\n return {\n success: false,\n filesCount: 0,\n error: `Invalid ref format: ${params.ref}. Expected @eN pattern (e.g. @e5).`,\n };\n }\n\n const backendNodeId = parseInt(match[1], 10);\n\n // Resolve backendNodeId to get objectId\n const resolved = (await cdp.send(\"DOM.resolveNode\", {\n backendNodeId,\n } as unknown as Record<string, unknown>)) as {\n object: { objectId: string };\n };\n\n const objectId = resolved.object.objectId;\n\n // Set files on the file input element\n await cdp.send(\"DOM.setFileInputFiles\", {\n files: params.paths,\n objectId,\n } as unknown as Record<string, unknown>);\n\n return {\n success: true,\n filesCount: params.paths.length,\n };\n}\n","/**\n * Fixed-size circular (ring) buffer for browser events.\n *\n * When the buffer reaches capacity the oldest events are silently evicted,\n * keeping memory usage bounded while still giving callers access to the\n * most-recent N events in chronological order.\n */\nexport class EventBuffer<T = unknown> {\n private _buffer: (T | undefined)[];\n private _capacity: number;\n private _head: number; // next write index\n private _size: number;\n private _totalPushed: number;\n\n constructor(capacity: number = 500) {\n this._capacity = capacity;\n this._buffer = new Array(capacity);\n this._head = 0;\n this._size = 0;\n this._totalPushed = 0;\n }\n\n /** Append an event, evicting the oldest if at capacity. */\n push(event: T): void {\n this._buffer[this._head] = event;\n this._head = (this._head + 1) % this._capacity;\n if (this._size < this._capacity) {\n this._size++;\n }\n this._totalPushed++;\n }\n\n /**\n * Return the last `n` events in chronological (oldest-first) order.\n * Defaults to all events when `n` is omitted.\n */\n last(n?: number): T[] {\n const count = n === undefined ? this._size : Math.min(n, this._size);\n if (count === 0) return [];\n\n const result: T[] = new Array(count);\n // The oldest of the `count` events starts at:\n let start = (this._head - count + this._capacity) % this._capacity;\n for (let i = 0; i < count; i++) {\n result[i] = this._buffer[(start + i) % this._capacity] as T;\n }\n return result;\n }\n\n /** Remove all events from the buffer. */\n clear(): void {\n this._buffer = new Array(this._capacity);\n this._head = 0;\n this._size = 0;\n }\n\n /**\n * Return the last `n` events AND remove them from the buffer.\n * Defaults to all events when `n` is omitted.\n */\n drain(n?: number): T[] {\n const events = this.last(n);\n this.clear();\n return events;\n }\n\n /** Current number of events stored. */\n get size(): number {\n return this._size;\n }\n\n /** Maximum number of events this buffer can hold. */\n get capacity(): number {\n return this._capacity;\n }\n\n /** Total number of events ever pushed (including evicted ones). */\n get totalPushed(): number {\n return this._totalPushed;\n }\n\n /** Return events matching `predicate` without modifying the buffer. */\n filter(predicate: (event: T) => boolean): T[] {\n return this.last().filter(predicate);\n }\n\n /** Snapshot of buffer statistics. */\n get stats(): { size: number; capacity: number; totalPushed: number; evicted: number } {\n return {\n size: this._size,\n capacity: this._capacity,\n totalPushed: this._totalPushed,\n evicted: this._totalPushed - this._size,\n };\n }\n}\n","/**\n * Secret redaction utilities for browsirai.\n *\n * Redacts sensitive values from headers, JSON bodies, inline text,\n * and network event objects to prevent secret leakage in logs/output.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface RedactOptions {\n enabled?: boolean;\n}\n\nexport interface NetworkEvent {\n url: string;\n method: string;\n status?: number;\n headers?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n body?: string;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"x-auth-token\",\n \"x-csrf-token\",\n \"x-xsrf-token\",\n \"proxy-authorization\",\n \"x-access-token\",\n \"x-refresh-token\",\n \"x-secret\",\n \"x-token\",\n]);\n\nconst SENSITIVE_BODY_KEYS = new Set([\n \"password\",\n \"secret\",\n \"token\",\n \"api_key\",\n \"apiKey\",\n \"api-key\",\n \"access_token\",\n \"refresh_token\",\n \"client_secret\",\n \"private_key\",\n]);\n\nconst JWT_PATTERN =\n /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]+/g;\n\nconst BEARER_PATTERN = /Bearer\\s+\\S+/gi;\n\nconst REDACTED = \"[REDACTED]\";\nconst REDACTED_JWT = \"[REDACTED_JWT]\";\n\n// ---------------------------------------------------------------------------\n// redactHeaders\n// ---------------------------------------------------------------------------\n\nexport function redactHeaders(\n headers: Record<string, string>,\n opts?: RedactOptions,\n): Record<string, string> {\n if (opts?.enabled === false) {\n return { ...headers };\n }\n\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (SENSITIVE_HEADERS.has(key.toLowerCase())) {\n result[key] = REDACTED;\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// redactBody\n// ---------------------------------------------------------------------------\n\nfunction redactObjectKeys(obj: unknown): unknown {\n if (obj === null || obj === undefined || typeof obj !== \"object\") {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => redactObjectKeys(item));\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_BODY_KEYS.has(key)) {\n result[key] = REDACTED;\n } else if (typeof value === \"object\" && value !== null) {\n result[key] = redactObjectKeys(value);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nexport function redactBody(\n body: string,\n opts?: RedactOptions,\n): string {\n if (opts?.enabled === false) {\n return body;\n }\n\n try {\n const parsed = JSON.parse(body);\n const redacted = redactObjectKeys(parsed);\n return JSON.stringify(redacted);\n } catch {\n // Not valid JSON — return as-is\n return body;\n }\n}\n\n// ---------------------------------------------------------------------------\n// redactInlineSecrets\n// ---------------------------------------------------------------------------\n\nexport function redactInlineSecrets(\n text: string,\n opts?: RedactOptions,\n): string {\n if (opts?.enabled === false) {\n return text;\n }\n\n // Redact JWTs first (before Bearer, since Bearer may contain a JWT)\n let result = text.replace(JWT_PATTERN, REDACTED_JWT);\n\n // Redact Bearer tokens — preserve the original casing of \"Bearer\"\n result = result.replace(BEARER_PATTERN, (match) => {\n const bearerWord = match.split(/\\s+/)[0];\n return `${bearerWord} ${REDACTED}`;\n });\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// redactNetworkEvent\n// ---------------------------------------------------------------------------\n\nexport function redactNetworkEvent(\n event: NetworkEvent,\n opts?: RedactOptions,\n): NetworkEvent {\n if (opts?.enabled === false) {\n return { ...event };\n }\n\n const result: NetworkEvent = { ...event };\n\n if (event.headers) {\n result.headers = redactHeaders(event.headers, opts);\n }\n\n if (event.responseHeaders) {\n result.responseHeaders = redactHeaders(event.responseHeaders, opts);\n }\n\n if (event.body !== undefined) {\n result.body = redactBody(event.body, opts);\n }\n\n return result;\n}\n","/**\n * browser_network_requests tool — captures network requests via CDP events.\n *\n * Uses Network.requestWillBeSent and Network.responseReceived CDP events to\n * capture requests server-side into a bounded EventBuffer. Captures method,\n * status code, headers — data not available via the Performance API.\n *\n * Supports:\n * - URL filtering via substring match\n * - Static resource filtering (Image, Stylesheet, Font, Script)\n * - Result limiting\n * - Secret redaction (JWT/Bearer tokens in URLs)\n *\n * @module browser-network-requests\n */\nimport { EventBuffer } from \"../event-buffer.js\";\nimport { redactInlineSecrets } from \"../redactor.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface NetworkRequestsParams {\n /** Substring filter to match against request URLs. */\n filter?: string;\n /** Maximum number of requests to return. */\n limit?: number;\n /** Whether to include response headers. */\n includeHeaders?: boolean;\n /** Whether to include static resources (images, stylesheets, fonts, scripts). */\n includeStatic?: boolean;\n}\n\nexport interface NetworkRequest {\n /** The request URL. */\n url: string;\n /** HTTP method (GET, POST, etc.). */\n method: string;\n /** HTTP status code. */\n status?: number;\n /** Resource type (e.g. \"Fetch\", \"XHR\", \"Script\", \"Image\"). */\n type?: string;\n}\n\nexport interface NetworkRequestsResult {\n /** List of captured network requests. */\n requests: NetworkRequest[];\n}\n\n// ---------------------------------------------------------------------------\n// Static resource types (CDP uses PascalCase)\n// ---------------------------------------------------------------------------\n\nconst STATIC_TYPES = new Set([\n \"Image\",\n \"Stylesheet\",\n \"Font\",\n \"Script\",\n \"Media\",\n]);\n\n// ---------------------------------------------------------------------------\n// Internal buffer entry (mutable — response enriches it)\n// ---------------------------------------------------------------------------\n\ninterface BufferEntry {\n requestId: string;\n url: string;\n method: string;\n type: string;\n status?: number;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Module-level state\n// ---------------------------------------------------------------------------\n\nconst networkBuffer = new EventBuffer<BufferEntry>(500);\n\n/** Map requestId → buffer index for response correlation */\nconst pendingRequests = new Map<string, BufferEntry>();\n\n// ---------------------------------------------------------------------------\n// Setup & Reset\n// ---------------------------------------------------------------------------\n\ninterface CDPEventSource {\n on(event: string, handler: (params: unknown) => void): void;\n}\n\n/**\n * Register CDP event listeners for Network.requestWillBeSent and\n * Network.responseReceived. Call once after Network.enable.\n */\nexport function setupNetworkCapture(cdp: CDPEventSource): void {\n cdp.on(\"Network.requestWillBeSent\", (params: unknown) => {\n const p = params as {\n requestId: string;\n request: { url: string; method: string };\n type?: string;\n timestamp?: number;\n };\n\n const entry: BufferEntry = {\n requestId: p.requestId,\n url: p.request.url,\n method: p.request.method,\n type: p.type ?? \"Other\",\n timestamp: p.timestamp ? Math.floor(p.timestamp * 1000) : Date.now(),\n };\n\n pendingRequests.set(p.requestId, entry);\n networkBuffer.push(entry);\n });\n\n cdp.on(\"Network.responseReceived\", (params: unknown) => {\n const p = params as {\n requestId: string;\n response: { url: string; status: number; headers?: Record<string, string> };\n };\n\n const entry = pendingRequests.get(p.requestId);\n if (entry) {\n entry.status = p.response.status;\n pendingRequests.delete(p.requestId);\n }\n });\n}\n\n/** Clear the network buffer (call on reconnection). */\nexport function resetNetworkBuffer(): void {\n networkBuffer.clear();\n pendingRequests.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Read network requests from the EventBuffer.\n *\n * @param _cdp - CDP connection (unused — buffer is populated by setupNetworkCapture).\n * @param params - Filter and limit parameters.\n * @returns List of network requests.\n */\nexport async function browserNetworkRequests(\n _cdp: unknown,\n params: NetworkRequestsParams,\n): Promise<NetworkRequestsResult> {\n let entries = networkBuffer.last();\n\n // Filter static resources unless includeStatic is true\n if (!params.includeStatic) {\n entries = entries.filter((e) => !STATIC_TYPES.has(e.type));\n }\n\n // Filter by URL substring\n if (params.filter) {\n const filterLower = params.filter.toLowerCase();\n entries = entries.filter((e) => e.url.toLowerCase().includes(filterLower));\n }\n\n // Apply limit\n const limit = params.limit ?? 100;\n entries = entries.slice(0, limit);\n\n // Map to NetworkRequest format — redact secrets from URLs\n const requests: NetworkRequest[] = entries.map((e) => ({\n url: redactInlineSecrets(e.url),\n method: e.method,\n status: e.status,\n type: e.type,\n }));\n\n return { requests };\n}\n","/**\n * browser_console_messages tool — captures console messages via CDP events.\n *\n * Uses Runtime.consoleAPICalled CDP event to capture messages server-side\n * into a bounded EventBuffer. Messages survive page navigations and never\n * require JS injection.\n *\n * Supports:\n * - Filtering by log level\n * - Result limiting\n * - Secret redaction (JWT/Bearer tokens)\n *\n * @module browser-console-messages\n */\nimport { EventBuffer } from \"../event-buffer.js\";\nimport { redactInlineSecrets } from \"../redactor.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ConsoleMessagesParams {\n /** Maximum number of messages to return. */\n limit?: number;\n /** Filter by log level. */\n level?: \"log\" | \"warn\" | \"error\" | \"info\";\n}\n\nexport interface ConsoleMessage {\n /** Log level: \"log\", \"warn\", \"error\", or \"info\". */\n level: string;\n /** Stringified message text. */\n text: string;\n /** Timestamp when the message was captured. */\n timestamp?: number;\n}\n\nexport interface ConsoleMessagesResult {\n /** List of captured console messages. */\n messages: ConsoleMessage[];\n}\n\n// ---------------------------------------------------------------------------\n// Supported console levels\n// ---------------------------------------------------------------------------\n\nconst SUPPORTED_LEVELS = new Set([\"log\", \"warn\", \"warning\", \"error\", \"info\"]);\n\n// ---------------------------------------------------------------------------\n// Module-level EventBuffer\n// ---------------------------------------------------------------------------\n\nconst consoleBuffer = new EventBuffer<ConsoleMessage>(500);\n\n// ---------------------------------------------------------------------------\n// Setup & Reset\n// ---------------------------------------------------------------------------\n\ninterface CDPEventSource {\n on(event: string, handler: (params: unknown) => void): void;\n}\n\n/**\n * Register a CDP event listener for Runtime.consoleAPICalled.\n * Call once after Runtime.enable.\n */\nexport function setupConsoleCapture(cdp: CDPEventSource): void {\n cdp.on(\"Runtime.consoleAPICalled\", (params: unknown) => {\n const p = params as {\n type: string;\n args?: Array<{ type: string; value?: unknown; description?: string }>;\n timestamp?: number;\n };\n\n if (!SUPPORTED_LEVELS.has(p.type)) return;\n\n const text = (p.args ?? [])\n .map((arg) => {\n if (arg.type === \"string\") return String(arg.value);\n if (arg.type === \"undefined\") return \"undefined\";\n if (arg.value !== undefined) return String(arg.value);\n if (arg.description) return arg.description;\n return \"\";\n })\n .join(\" \");\n\n consoleBuffer.push({\n level: p.type === \"warning\" ? \"warn\" : p.type,\n text,\n timestamp: p.timestamp ? Math.floor(p.timestamp) : Date.now(),\n });\n });\n}\n\n/** Clear the console buffer (call on reconnection). */\nexport function resetConsoleBuffer(): void {\n consoleBuffer.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Read console messages from the EventBuffer.\n *\n * @param _cdp - CDP connection (unused — buffer is populated by setupConsoleCapture).\n * @param params - Filter and limit parameters.\n * @returns List of console messages.\n */\nexport async function browserConsoleMessages(\n _cdp: unknown,\n params: ConsoleMessagesParams,\n): Promise<ConsoleMessagesResult> {\n let messages = consoleBuffer.last();\n\n // Redact secrets from message text\n messages = messages.map((m) => ({ ...m, text: redactInlineSecrets(m.text) }));\n\n // Filter by level\n if (params.level) {\n messages = messages.filter((m) => m.level === params.level);\n }\n\n // Apply limit (return most recent messages)\n const limit = params.limit ?? 100;\n if (messages.length > limit) {\n messages = messages.slice(-limit);\n }\n\n return { messages };\n}\n","/**\n * browser_annotated_screenshot tool — captures an annotated screenshot.\n *\n * Delegates to browserScreenshot with `annotate: true` to produce a\n * screenshot with interactive elements labeled with ref annotations.\n *\n * @module browser-annotated-screenshot\n */\nimport type { CDPConnection } from \"../cdp/connection\";\nimport { browserScreenshot } from \"./browser-screenshot\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface AnnotatedScreenshotParams {\n /** CSS selector to screenshot a specific element. */\n selector?: string;\n}\n\nexport interface AnnotatedScreenshotResult {\n /** Base64-encoded screenshot data. */\n base64: string;\n /** Annotations for interactive elements. */\n annotations: Array<{\n ref: string;\n label: string;\n role?: string;\n name?: string;\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Capture an annotated screenshot of the browser page.\n *\n * @param cdp - CDP connection.\n * @param params - Optional selector to scope the screenshot.\n * @returns Base64-encoded screenshot and annotation metadata.\n */\nexport async function browserAnnotatedScreenshot(\n cdp: CDPConnection,\n params: AnnotatedScreenshotParams,\n): Promise<AnnotatedScreenshotResult> {\n const result = await browserScreenshot(cdp, {\n annotate: true,\n selector: params.selector,\n });\n\n return {\n base64: result.base64,\n annotations: result.annotations ?? [],\n };\n}\n","/**\n * browser_inspect_source tool — maps DOM elements to their source code locations.\n *\n * CDP-native resolution:\n * - React: Walk Fiber tree, parse jsxDEV() calls in Function.toString()\n * to extract fileName/lineNumber embedded by Babel jsx-source plugin.\n * - Vue: Read __vueParentComponent.type.__file\n * - Svelte: Read __svelte_meta.loc\n *\n * Works with React, Vue, Svelte frameworks.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface InspectSourceParams {\n /** @eN ref from accessibility snapshot */\n ref?: string;\n /** CSS selector to find the element */\n selector?: string;\n}\n\nexport interface SourceLocation {\n filePath: string;\n lineNumber: number | null;\n columnNumber: number | null;\n componentName: string | null;\n}\n\nexport interface InspectSourceResult {\n tagName: string;\n componentName: string | null;\n source: SourceLocation | null;\n stack: SourceLocation[];\n}\n\n// ---------------------------------------------------------------------------\n// Ref pattern\n// ---------------------------------------------------------------------------\n\nconst REF_PATTERN = /^@?e(\\d+)$/;\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Inspects a DOM element and returns its source code location.\n *\n * Strategy:\n * 1. Resolve element via ref or selector\n * 2. Walk React Fiber tree → parse Function.toString() for fileName/lineNumber\n * 3. Check Vue (__vueParentComponent) and Svelte (__svelte_meta)\n */\nexport async function browserInspectSource(\n cdp: CDPConnection,\n params: InspectSourceParams,\n): Promise<InspectSourceResult> {\n // 1. Resolve element\n let objectId: string;\n\n if (params.ref) {\n const match = REF_PATTERN.exec(params.ref);\n if (!match) throw new Error(`Invalid ref format: ${params.ref}`);\n const backendNodeId = parseInt(match[1], 10);\n const resolved = (await cdp.send(\"DOM.resolveNode\", { backendNodeId })) as {\n object: { objectId: string };\n };\n objectId = resolved.object.objectId;\n } else if (params.selector) {\n const evalResult = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector(${JSON.stringify(params.selector)})`,\n returnByValue: false,\n })) as { result: { objectId?: string; subtype?: string } };\n\n if (!evalResult.result.objectId || evalResult.result.subtype === \"null\") {\n throw new Error(`Element not found: ${params.selector}`);\n }\n objectId = evalResult.result.objectId;\n } else {\n throw new Error(\"Either ref or selector must be provided\");\n }\n\n // 2. CDP-native: Walk Fiber tree + parse Function.toString()\n const cdpResult = (await cdp.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: `function() {\n var el = this;\n var tagName = (el.tagName || '').toLowerCase();\n\n // Find React Fiber\n var fiberKey = Object.keys(el).find(function(k) { return k.startsWith('__reactFiber'); });\n\n // Also check Vue, Svelte\n var vueComp = el.__vueParentComponent;\n var svelteMeta = el.__svelte_meta;\n\n if (!fiberKey && !vueComp && !svelteMeta) {\n return JSON.stringify({ tagName: tagName, componentName: null, source: null, stack: [], framework: null });\n }\n\n // --- React path ---\n if (fiberKey) {\n var fiber = el[fiberKey];\n var stack = [];\n var current = fiber;\n var firstSource = null;\n var firstName = null;\n\n while (current && stack.length < 15) {\n if (typeof current.type === 'function' && current.type.name) {\n var fn = current.type;\n var fnStr = fn.toString();\n var fileName = null;\n var lineNumber = null;\n var columnNumber = null;\n\n // Parse jsxDEV calls for embedded fileName/lineNumber\n var fileMatch = fnStr.match(/fileName:\\\\s*\"([^\"]+)\"/);\n if (fileMatch) {\n fileName = fileMatch[1];\n var lineMatch = fnStr.match(/lineNumber:\\\\s*(\\\\d+)/);\n if (lineMatch) lineNumber = parseInt(lineMatch[1]);\n var colMatch = fnStr.match(/columnNumber:\\\\s*(\\\\d+)/);\n if (colMatch) columnNumber = parseInt(colMatch[1]);\n }\n\n var entry = {\n filePath: fileName,\n lineNumber: lineNumber,\n columnNumber: columnNumber,\n componentName: fn.name\n };\n\n stack.push(entry);\n\n if (fileName && !firstSource) {\n firstSource = entry;\n }\n if (!firstName && fn.name.length > 1) {\n firstName = fn.name;\n }\n }\n current = current.return;\n }\n\n return JSON.stringify({\n tagName: tagName,\n componentName: firstName || null,\n source: firstSource || (stack.length > 0 ? { filePath: null, lineNumber: null, columnNumber: null, componentName: stack[0].componentName } : null),\n stack: stack.filter(function(s) { return s.filePath; }),\n framework: 'react'\n });\n }\n\n // --- Svelte path ---\n if (svelteMeta) {\n var loc = svelteMeta.loc || {};\n return JSON.stringify({\n tagName: tagName,\n componentName: loc.char ? null : (svelteMeta.component || null),\n source: loc.file ? { filePath: loc.file, lineNumber: loc.line || null, columnNumber: (loc.column || 0) + 1, componentName: null } : null,\n stack: [],\n framework: 'svelte'\n });\n }\n\n // --- Vue path ---\n if (vueComp) {\n var comp = vueComp;\n var vueName = comp.type?.__name || comp.type?.name || null;\n var vueFile = comp.type?.__file || null;\n return JSON.stringify({\n tagName: tagName,\n componentName: vueName,\n source: vueFile ? { filePath: vueFile, lineNumber: null, columnNumber: null, componentName: vueName } : null,\n stack: [],\n framework: 'vue'\n });\n }\n\n return JSON.stringify({ tagName: tagName, componentName: null, source: null, stack: [], framework: null });\n }`,\n returnByValue: true,\n })) as { result: { value: string } };\n\n return JSON.parse(cdpResult.result.value) as InspectSourceResult;\n}\n","/**\n * browser_route, browser_abort, browser_unroute tools — CDP Fetch domain request interception.\n *\n * Manages shared intercept state:\n * - Route rules: respond with custom body/status/headers for matching URLs\n * - Abort rules: block matching requests with BlockedByClient\n * - Unroute: remove specific or all intercept rules\n *\n * Uses glob pattern matching (** for any path, * for single segment).\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface RouteRule {\n urlPattern: string;\n body: string;\n status: number;\n headers: Record<string, string>;\n}\n\ninterface AbortRule {\n urlPattern: string;\n}\n\nexport interface RouteParams {\n url: string;\n body: string | Record<string, unknown>;\n status?: number;\n headers?: Record<string, string>;\n}\n\nexport interface RouteResult {\n url: string;\n status: number;\n activeRoutes: number;\n}\n\nexport interface AbortParams {\n url: string;\n}\n\nexport interface AbortResult {\n url: string;\n activeAborts: number;\n}\n\nexport interface UnrouteParams {\n url?: string;\n all?: boolean;\n}\n\nexport interface UnrouteResult {\n removed: number;\n remaining: number;\n}\n\n// ---------------------------------------------------------------------------\n// Module-level state\n// ---------------------------------------------------------------------------\n\nconst activeRoutes: Map<string, RouteRule> = new Map();\nconst activeAborts: Map<string, AbortRule> = new Map();\nlet fetchEnabled = false;\nlet handlerAttached = false;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Glob matching: convert ** to .* and * to [^/]* for regex matching.\n */\nfunction matchGlob(pattern: string, url: string): boolean {\n const regex = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"___DOUBLESTAR___\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/___DOUBLESTAR___/g, \".*\");\n return new RegExp(`^${regex}$`).test(url);\n}\n\n/**\n * Sync Fetch domain patterns with CDP.\n * Enables/disables Fetch domain and attaches the requestPaused handler once.\n */\nasync function syncFetchPatterns(cdp: CDPConnection): Promise<void> {\n const patterns = [\n ...Array.from(activeRoutes.keys()),\n ...Array.from(activeAborts.keys()),\n ].map((p) => ({ urlPattern: p }));\n\n if (patterns.length === 0) {\n if (fetchEnabled) {\n await cdp.send(\"Fetch.disable\");\n fetchEnabled = false;\n }\n return;\n }\n\n await cdp.send(\"Fetch.enable\", { patterns });\n fetchEnabled = true;\n\n // Attach handler once\n if (!handlerAttached) {\n cdp.on(\"Fetch.requestPaused\", async (params: any) => {\n const url = params.request.url;\n const requestId = params.requestId;\n\n try {\n // Check abort rules first\n for (const [pattern] of activeAborts) {\n if (matchGlob(pattern, url)) {\n await cdp.send(\"Fetch.failRequest\", {\n requestId,\n reason: \"BlockedByClient\",\n });\n return;\n }\n }\n\n // Check route rules\n for (const [pattern, rule] of activeRoutes) {\n if (matchGlob(pattern, url)) {\n const responseHeaders = Object.entries(rule.headers).map(\n ([name, value]) => ({ name, value }),\n );\n await cdp.send(\"Fetch.fulfillRequest\", {\n requestId,\n responseCode: rule.status,\n body: btoa(rule.body),\n responseHeaders,\n });\n return;\n }\n }\n\n // No match: continue request\n await cdp.send(\"Fetch.continueRequest\", { requestId });\n } catch {\n // Request may have been cancelled or navigation occurred — ignore\n }\n });\n handlerAttached = true;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool exports\n// ---------------------------------------------------------------------------\n\n/**\n * Intercept matching requests and respond with a custom body/status/headers.\n */\nexport async function browserRoute(\n cdp: CDPConnection,\n params: RouteParams,\n): Promise<RouteResult> {\n const body =\n typeof params.body === \"object\" && params.body !== null\n ? JSON.stringify(params.body)\n : String(params.body);\n\n const status = params.status ?? 200;\n const headers = params.headers ?? { \"Content-Type\": \"application/json\" };\n\n const rule: RouteRule = {\n urlPattern: params.url,\n body,\n status,\n headers,\n };\n\n activeRoutes.set(params.url, rule);\n await syncFetchPatterns(cdp);\n\n return {\n url: params.url,\n status,\n activeRoutes: activeRoutes.size,\n };\n}\n\n/**\n * Block matching requests with BlockedByClient error.\n */\nexport async function browserAbort(\n cdp: CDPConnection,\n params: AbortParams,\n): Promise<AbortResult> {\n const rule: AbortRule = {\n urlPattern: params.url,\n };\n\n activeAborts.set(params.url, rule);\n await syncFetchPatterns(cdp);\n\n return {\n url: params.url,\n activeAborts: activeAborts.size,\n };\n}\n\n/**\n * Remove intercept rules — specific pattern or all.\n */\nexport async function browserUnroute(\n cdp: CDPConnection,\n params: UnrouteParams,\n): Promise<UnrouteResult> {\n let removed = 0;\n\n if (params.all) {\n removed = activeRoutes.size + activeAborts.size;\n activeRoutes.clear();\n activeAborts.clear();\n } else if (params.url) {\n if (activeRoutes.delete(params.url)) removed++;\n if (activeAborts.delete(params.url)) removed++;\n }\n\n await syncFetchPatterns(cdp);\n\n return {\n removed,\n remaining: activeRoutes.size + activeAborts.size,\n };\n}\n\n/**\n * Reset all intercept state — for testing purposes.\n */\nexport function resetInterceptState(): void {\n activeRoutes.clear();\n activeAborts.clear();\n fetchEnabled = false;\n handlerAttached = false;\n}\n","/**\n * browser_find — Semantic element locators via CDP Accessibility and Runtime APIs.\n *\n * Provides Playwright-style locator functions: findByRole, findByText, findByLabel,\n * findByPlaceholder, findByAlt, findByTitle, findByTestId, findFirst, findLast, findNth.\n *\n * Also provides a unified `browserFind` function for the MCP tool that finds elements\n * by ARIA role, accessible name, or text content and returns @eN refs.\n *\n * Role and text-based locators use the Accessibility tree (Accessibility.getFullAXTree).\n * Attribute-based locators use Runtime.evaluate with CSS selectors.\n * Positional locators (first/last/nth) use querySelectorAll.\n */\nimport type { CDPConnection } from \"../cdp/connection.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FindResult {\n /** Whether an element was found. */\n found: boolean;\n /** The backend node ID of the found element. */\n backendNodeId?: number;\n /** The index of the found element (for positional locators). */\n index?: number;\n}\n\nexport interface FindByRoleParams {\n /** ARIA role to search for (e.g., \"button\", \"link\"). */\n role: string;\n /** Accessible name to match. */\n name?: string;\n}\n\nexport interface FindByTextParams {\n /** Text content to search for. */\n text: string;\n /** If true, require exact text match (not substring). */\n exact?: boolean;\n}\n\nexport interface FindByLabelParams {\n /** Label text to search for. */\n label: string;\n}\n\nexport interface FindByPlaceholderParams {\n /** Placeholder text to search for. */\n placeholder: string;\n}\n\nexport interface FindByAltParams {\n /** Alt text to search for. */\n alt: string;\n}\n\nexport interface FindByTitleParams {\n /** Title attribute to search for. */\n title: string;\n}\n\nexport interface FindByTestIdParams {\n /** data-testid value to search for. */\n testId: string;\n}\n\nexport interface FindPositionalParams {\n /** CSS selector to match elements. */\n selector: string;\n}\n\nexport interface FindNthParams extends FindPositionalParams {\n /** Zero-based index of the element to select. */\n n: number;\n}\n\n// ---------------------------------------------------------------------------\n// Accessibility tree types\n// ---------------------------------------------------------------------------\n\ninterface AXNode {\n nodeId: string;\n role: { type: string; value: string };\n name?: { type: string; value: string };\n backendDOMNodeId?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Exported functions\n// ---------------------------------------------------------------------------\n\n/**\n * Finds an element by ARIA role and optional accessible name.\n *\n * Uses Accessibility.getFullAXTree and filters by role/name.\n */\nexport async function findByRole(\n cdp: CDPConnection,\n params: FindByRoleParams,\n): Promise<FindResult> {\n const response = (await cdp.send(\"Accessibility.getFullAXTree\")) as {\n nodes: AXNode[];\n };\n\n const match = response.nodes.find((node) => {\n if (node.role.value !== params.role) return false;\n if (params.name && node.name?.value !== params.name) return false;\n return true;\n });\n\n if (!match) {\n return { found: false };\n }\n\n return {\n found: true,\n backendNodeId: match.backendDOMNodeId,\n };\n}\n\n/**\n * Finds an element by its text content.\n *\n * Uses Accessibility.getFullAXTree and filters by accessible name.\n */\nexport async function findByText(\n cdp: CDPConnection,\n params: FindByTextParams,\n): Promise<FindResult> {\n const response = (await cdp.send(\"Accessibility.getFullAXTree\")) as {\n nodes: AXNode[];\n };\n\n const match = response.nodes.find((node) => {\n if (!node.name?.value) return false;\n if (params.exact) {\n return node.name.value === params.text;\n }\n return node.name.value.includes(params.text);\n });\n\n if (!match) {\n return { found: false };\n }\n\n return {\n found: true,\n backendNodeId: match.backendDOMNodeId,\n };\n}\n\n/**\n * Finds an element by its associated label text.\n *\n * Uses Runtime.evaluate to find an element via label association.\n */\nexport async function findByLabel(\n cdp: CDPConnection,\n params: FindByLabelParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.label);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `(() => {\n const labels = document.querySelectorAll('label');\n for (const label of labels) {\n if (label.textContent?.trim() === ${escaped}) {\n if (label.htmlFor) {\n return document.getElementById(label.htmlFor);\n }\n return label.querySelector('input, select, textarea');\n }\n }\n return null;\n })()`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds an element by its placeholder attribute.\n *\n * Uses Runtime.evaluate with an attribute selector.\n */\nexport async function findByPlaceholder(\n cdp: CDPConnection,\n params: FindByPlaceholderParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.placeholder);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector('[placeholder=${escaped}]')`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds an element by its alt text attribute.\n *\n * Uses Runtime.evaluate with an attribute selector.\n */\nexport async function findByAlt(\n cdp: CDPConnection,\n params: FindByAltParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.alt);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector('[alt=${escaped}]')`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds an element by its title attribute.\n *\n * Uses Runtime.evaluate with an attribute selector.\n */\nexport async function findByTitle(\n cdp: CDPConnection,\n params: FindByTitleParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.title);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector('[title=${escaped}]')`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds an element by its data-testid attribute.\n *\n * Uses Runtime.evaluate with a data attribute selector.\n */\nexport async function findByTestId(\n cdp: CDPConnection,\n params: FindByTestIdParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.testId);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelector('[data-testid=${escaped}]')`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds the first element matching a CSS selector.\n *\n * @returns FindResult with index=0\n */\nexport async function findFirst(\n cdp: CDPConnection,\n params: FindPositionalParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.selector);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelectorAll(${escaped})[0]`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true, index: 0 };\n}\n\n/**\n * Finds the last element matching a CSS selector.\n *\n * @returns FindResult with the last element's index\n */\nexport async function findLast(\n cdp: CDPConnection,\n params: FindPositionalParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.selector);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `(() => {\n const els = document.querySelectorAll(${escaped});\n return els.length > 0 ? els[els.length - 1] : null;\n })()`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true };\n}\n\n/**\n * Finds the nth element matching a CSS selector.\n *\n * @param params.n - Zero-based index\n * @returns FindResult with the nth element\n */\nexport async function findNth(\n cdp: CDPConnection,\n params: FindNthParams,\n): Promise<FindResult> {\n const escaped = JSON.stringify(params.selector);\n\n const response = (await cdp.send(\"Runtime.evaluate\", {\n expression: `document.querySelectorAll(${escaped})[${params.n}]`,\n returnByValue: false,\n })) as { result: { type: string; objectId?: string } };\n\n if (!response.result.objectId) {\n return { found: false };\n }\n\n return { found: true, index: params.n };\n}\n\n// ---------------------------------------------------------------------------\n// Unified browserFind — MCP tool entry point\n// ---------------------------------------------------------------------------\n\nexport interface BrowserFindParams {\n /** ARIA role to search for (e.g., \"button\", \"link\", \"heading\", \"textbox\"). */\n role?: string;\n /** Accessible name to match (substring, case-sensitive). */\n name?: string;\n /** Text content to search for (substring match on accessible name). */\n text?: string;\n /** Zero-based index to pick the nth match (default 0 = first). */\n nth?: number;\n}\n\nexport interface BrowserFindResult {\n /** Whether a matching element was found. */\n found: boolean;\n /** @eN ref for use with other browsirai tools, or null if not found. */\n ref: string | null;\n /** ARIA role of the matched element, or null if not found. */\n role: string | null;\n /** Accessible name of the matched element, or null if not found. */\n name: string | null;\n /** Total number of matching elements. */\n count: number;\n}\n\n/**\n * Finds elements by ARIA role, accessible name, or text content.\n *\n * Uses Accessibility.getFullAXTree to walk the AX tree, filters by\n * role (case-insensitive) and/or name (substring) and/or text content,\n * and returns the nth match (default first) with an @eN ref.\n */\nexport async function browserFind(\n cdp: CDPConnection,\n params: BrowserFindParams,\n): Promise<BrowserFindResult> {\n const nth = params.nth ?? 0;\n\n // Get the full accessibility tree\n const response = (await cdp.send(\"Accessibility.getFullAXTree\", undefined, {\n timeout: 10000,\n })) as {\n nodes: AXNode[];\n };\n\n // Filter nodes by role, name, and/or text\n const matches = response.nodes.filter((node) => {\n // Filter by role (case-insensitive)\n if (params.role) {\n const nodeRole = node.role?.value ?? \"\";\n if (nodeRole.toLowerCase() !== params.role.toLowerCase()) {\n return false;\n }\n }\n\n // Filter by name (substring match)\n if (params.name) {\n const nodeName = node.name?.value ?? \"\";\n if (!nodeName.includes(params.name)) {\n return false;\n }\n }\n\n // Filter by text content (substring match on accessible name)\n if (params.text) {\n const nodeName = node.name?.value ?? \"\";\n if (!nodeName.includes(params.text)) {\n return false;\n }\n }\n\n return true;\n });\n\n const count = matches.length;\n\n // Pick the nth match\n if (nth >= count || count === 0) {\n return {\n found: false,\n ref: null,\n role: null,\n name: null,\n count,\n };\n }\n\n const match = matches[nth];\n const ref = match.backendDOMNodeId ? `@e${match.backendDOMNodeId}` : null;\n\n return {\n found: true,\n ref,\n role: match.role?.value ?? null,\n name: match.name?.value ?? null,\n count,\n };\n}\n","/**\n * browser_diff tool — pixel-by-pixel comparison of two screenshots via CDP.\n *\n * Supports:\n * - Comparing two base64 PNG screenshots\n * - Capturing \"current\" page state as before/after\n * - Element-scoped comparison via CSS selector\n * - Configurable pixel difference threshold\n * - Visual diff image with red-highlighted changes\n *\n * @module browser-diff\n */\nimport type { CDPConnection } from \"../cdp/connection\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DiffParams {\n /** First screenshot - base64 PNG or \"current\" to capture now */\n before: string;\n /** Second screenshot - base64 PNG or \"current\" to capture now */\n after?: string;\n /** CSS selector to scope comparison */\n selector?: string;\n /** Pixel difference threshold (0-255, default 30) */\n threshold?: number;\n}\n\nexport interface DiffResult {\n /** Percentage of pixels that differ (0-100) */\n diffPercentage: number;\n /** Total pixels compared */\n totalPixels: number;\n /** Number of different pixels */\n diffPixels: number;\n /** Whether images are considered identical (diffPercentage < 0.1) */\n identical: boolean;\n /** Base64 diff image (red highlights on differences) */\n diffImage: string;\n /** Dimensions */\n width: number;\n height: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Capture a screenshot of the current page or a specific element.\n */\nasync function captureScreenshot(\n cdp: CDPConnection,\n selector?: string,\n): Promise<string> {\n const captureParams: Record<string, unknown> = { format: \"png\" };\n\n if (selector) {\n const doc = (await cdp.send(\"DOM.getDocument\", {})) as {\n root: { nodeId: number };\n };\n const queryResult = (await cdp.send(\"DOM.querySelector\", {\n nodeId: doc.root.nodeId,\n selector,\n })) as { nodeId: number };\n\n if (!queryResult.nodeId) {\n throw new Error(`Element not found: ${selector}`);\n }\n\n const boxModel = (await cdp.send(\"DOM.getBoxModel\", {\n nodeId: queryResult.nodeId,\n })) as {\n model: { content: number[] };\n };\n\n const content = boxModel.model.content;\n captureParams.clip = {\n x: content[0],\n y: content[1],\n width: content[2] - content[0],\n height: content[5] - content[1],\n scale: 1,\n };\n }\n\n const screenshot = (await cdp.send(\n \"Page.captureScreenshot\",\n captureParams,\n )) as { data: string };\n\n return screenshot.data;\n}\n\n/**\n * Build the JavaScript expression for in-browser pixel comparison.\n * Uses string concatenation to avoid template literal escaping issues\n * with large base64 strings injected into CDP Runtime.evaluate.\n */\nfunction buildComparisonExpression(threshold: number): string {\n return [\n \"(async () => {\",\n \" const beforeSrc = window._diffBefore;\",\n \" const afterSrc = window._diffAfter;\",\n \"\",\n \" const loadImg = (src) => new Promise((res, rej) => {\",\n \" const img = new Image();\",\n \" img.onload = () => res(img);\",\n \" img.onerror = (e) => rej(new Error('Failed to load image'));\",\n \" img.src = src;\",\n \" });\",\n \"\",\n \" const img1 = await loadImg('data:image/png;base64,' + beforeSrc);\",\n \" const img2 = await loadImg('data:image/png;base64,' + afterSrc);\",\n \"\",\n \" const w = Math.max(img1.width, img2.width);\",\n \" const h = Math.max(img1.height, img2.height);\",\n \"\",\n \" const c1 = document.createElement('canvas');\",\n \" c1.width = w; c1.height = h;\",\n \" const ctx1 = c1.getContext('2d');\",\n \" ctx1.drawImage(img1, 0, 0);\",\n \"\",\n \" const c2 = document.createElement('canvas');\",\n \" c2.width = w; c2.height = h;\",\n \" const ctx2 = c2.getContext('2d');\",\n \" ctx2.drawImage(img2, 0, 0);\",\n \"\",\n \" const d1 = ctx1.getImageData(0, 0, w, h).data;\",\n \" const d2 = ctx2.getImageData(0, 0, w, h).data;\",\n \"\",\n \" const diff = document.createElement('canvas');\",\n \" diff.width = w; diff.height = h;\",\n \" const dCtx = diff.getContext('2d');\",\n \" dCtx.drawImage(img2, 0, 0);\",\n \" const dData = dCtx.getImageData(0, 0, w, h);\",\n \"\",\n \" let diffCount = 0;\",\n \" const threshold = \" + threshold + \";\",\n \" for (let i = 0; i < d1.length; i += 4) {\",\n \" const dr = Math.abs(d1[i] - d2[i]);\",\n \" const dg = Math.abs(d1[i+1] - d2[i+1]);\",\n \" const db = Math.abs(d1[i+2] - d2[i+2]);\",\n \" if (dr > threshold || dg > threshold || db > threshold) {\",\n \" diffCount++;\",\n \" dData.data[i] = 255;\",\n \" dData.data[i+1] = 0;\",\n \" dData.data[i+2] = 0;\",\n \" dData.data[i+3] = 200;\",\n \" }\",\n \" }\",\n \"\",\n \" dCtx.putImageData(dData, 0, 0);\",\n \" const diffBase64 = diff.toDataURL('image/png').split(',')[1];\",\n \"\",\n \" const total = w * h;\",\n \" return JSON.stringify({\",\n \" diffPercentage: parseFloat((diffCount / total * 100).toFixed(4)),\",\n \" totalPixels: total,\",\n \" diffPixels: diffCount,\",\n \" identical: (diffCount / total) < 0.001,\",\n \" diffImage: diffBase64,\",\n \" width: w,\",\n \" height: h\",\n \" });\",\n \"})()\",\n ].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Compare two screenshots pixel-by-pixel.\n *\n * If `before` is \"current\", captures the page now.\n * If `after` is not provided or is \"current\", captures the page now.\n *\n * @param cdp - CDP connection.\n * @param params - Diff parameters.\n * @returns Diff result with percentage, pixel count, and visual diff image.\n */\nexport async function browserDiff(\n cdp: CDPConnection,\n params: DiffParams,\n): Promise<DiffResult> {\n const threshold = params.threshold ?? 30;\n\n // Resolve before image\n let beforeBase64: string;\n if (params.before === \"current\") {\n beforeBase64 = await captureScreenshot(cdp, params.selector);\n } else {\n beforeBase64 = params.before;\n }\n\n // Resolve after image\n let afterBase64: string;\n if (!params.after || params.after === \"current\") {\n afterBase64 = await captureScreenshot(cdp, params.selector);\n } else {\n afterBase64 = params.after;\n }\n\n // Store images in page context to avoid escaping issues with large base64 strings\n await cdp.send(\"Runtime.evaluate\", {\n expression: \"window._diffBefore = \" + JSON.stringify(beforeBase64) + \";\",\n returnByValue: true,\n });\n\n await cdp.send(\"Runtime.evaluate\", {\n expression: \"window._diffAfter = \" + JSON.stringify(afterBase64) + \";\",\n returnByValue: true,\n });\n\n // Run pixel comparison in the browser\n const result = (await cdp.send(\"Runtime.evaluate\", {\n expression: buildComparisonExpression(threshold),\n awaitPromise: true,\n returnByValue: true,\n })) as {\n result: { type: string; value: string };\n exceptionDetails?: { text: string };\n };\n\n if (result.exceptionDetails) {\n throw new Error(\n \"Diff comparison failed: \" + result.exceptionDetails.text,\n );\n }\n\n // Clean up global variables\n await cdp.send(\"Runtime.evaluate\", {\n expression: \"delete window._diffBefore; delete window._diffAfter;\",\n returnByValue: true,\n });\n\n return JSON.parse(result.result.value) as DiffResult;\n}\n","/**\n * browser_save_state / browser_load_state — persist and restore browser session state.\n *\n * Saves cookies, localStorage, and sessionStorage to a named JSON file\n * under ~/.browsirai/states/. Loading restores all three and navigates\n * to the saved (or custom) URL.\n */\nimport type { CDPConnection } from \"../cdp/connection\";\nimport { writeFileSync, readFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SaveStateParams {\n name: string;\n}\n\nexport interface SaveStateResult {\n name: string;\n path: string;\n cookies: number;\n localStorage: number;\n sessionStorage: number;\n}\n\nexport interface LoadStateParams {\n name: string;\n url?: string;\n}\n\nexport interface LoadStateResult {\n name: string;\n cookies: number;\n localStorage: number;\n sessionStorage: number;\n}\n\ninterface StateFile {\n version: 1;\n savedAt: string;\n url: string;\n cookies: unknown[];\n localStorage: Record<string, string>;\n sessionStorage: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getStatesDir(): string {\n return join(homedir(), \".browsirai\", \"states\");\n}\n\nfunction ensureStatesDir(): string {\n const dir = getStatesDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nfunction getStatePath(name: string): string {\n return join(getStatesDir(), `${name}.json`);\n}\n\n// ---------------------------------------------------------------------------\n// browser_save_state\n// ---------------------------------------------------------------------------\n\nexport async function browserSaveState(\n cdp: CDPConnection,\n params: SaveStateParams,\n): Promise<SaveStateResult> {\n const { name } = params;\n\n // 1. Get cookies\n const cookieResponse = (await cdp.send(\"Network.getAllCookies\")) as {\n cookies: unknown[];\n };\n const cookies = cookieResponse.cookies ?? [];\n\n // 2. Get current URL\n const urlResponse = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"window.location.href\",\n returnByValue: true,\n })) as { result: { value?: string } };\n const url = urlResponse.result.value ?? \"\";\n\n // 3. Get localStorage\n const localStorageResponse = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"JSON.stringify(Object.entries(localStorage))\",\n returnByValue: true,\n })) as { result: { value?: string } };\n const localStorageEntries: Array<[string, string]> = JSON.parse(\n localStorageResponse.result.value ?? \"[]\",\n );\n const localStorage: Record<string, string> = Object.fromEntries(localStorageEntries);\n\n // 4. Get sessionStorage\n const sessionStorageResponse = (await cdp.send(\"Runtime.evaluate\", {\n expression: \"JSON.stringify(Object.entries(sessionStorage))\",\n returnByValue: true,\n })) as { result: { value?: string } };\n const sessionStorageEntries: Array<[string, string]> = JSON.parse(\n sessionStorageResponse.result.value ?? \"[]\",\n );\n const sessionStorage: Record<string, string> = Object.fromEntries(sessionStorageEntries);\n\n // 5. Write state file\n const dir = ensureStatesDir();\n const filePath = join(dir, `${name}.json`);\n\n const stateFile: StateFile = {\n version: 1,\n savedAt: new Date().toISOString(),\n url,\n cookies,\n localStorage,\n sessionStorage,\n };\n\n writeFileSync(filePath, JSON.stringify(stateFile, null, 2), \"utf-8\");\n\n return {\n name,\n path: filePath,\n cookies: cookies.length,\n localStorage: localStorageEntries.length,\n sessionStorage: sessionStorageEntries.length,\n };\n}\n\n// ---------------------------------------------------------------------------\n// browser_load_state\n// ---------------------------------------------------------------------------\n\nexport async function browserLoadState(\n cdp: CDPConnection,\n params: LoadStateParams,\n): Promise<LoadStateResult> {\n const { name, url: customUrl } = params;\n\n // 1. Read state file\n const filePath = getStatePath(name);\n if (!existsSync(filePath)) {\n throw new Error(`State file not found: ${filePath}`);\n }\n\n const stateFile: StateFile = JSON.parse(readFileSync(filePath, \"utf-8\"));\n\n // 2. Navigate to URL (custom or saved)\n const targetUrl = customUrl ?? stateFile.url;\n if (targetUrl) {\n await cdp.send(\"Page.enable\");\n await cdp.send(\"Page.navigate\", { url: targetUrl });\n }\n\n // 3. Set cookies\n if (stateFile.cookies.length > 0) {\n await cdp.send(\"Network.setCookies\", { cookies: stateFile.cookies });\n }\n\n // 4. Set localStorage\n const localEntries = Object.entries(stateFile.localStorage);\n if (localEntries.length > 0) {\n const localScript = localEntries\n .map(([k, v]) => `localStorage.setItem(${JSON.stringify(k)}, ${JSON.stringify(v)})`)\n .join(\";\");\n await cdp.send(\"Runtime.evaluate\", {\n expression: localScript,\n returnByValue: true,\n });\n }\n\n // 5. Set sessionStorage\n const sessionEntries = Object.entries(stateFile.sessionStorage);\n if (sessionEntries.length > 0) {\n const sessionScript = sessionEntries\n .map(([k, v]) => `sessionStorage.setItem(${JSON.stringify(k)}, ${JSON.stringify(v)})`)\n .join(\";\");\n await cdp.send(\"Runtime.evaluate\", {\n expression: sessionScript,\n returnByValue: true,\n });\n }\n\n // 6. Reload page to apply state\n await cdp.send(\"Page.reload\");\n\n return {\n name,\n cookies: stateFile.cookies.length,\n localStorage: localEntries.length,\n sessionStorage: sessionEntries.length,\n };\n}\n"],"mappings":"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","names":["require","spawn","execSync","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","join","r","healthy","name","delay","scrollX","scrollY","MODIFIER_BITS","REF_PATTERN","calculateCenter","resolveElementCoordinates","parseRef","getKeyCode","getCode","delay","POLL_INTERVAL_MS","delay","response","REF_PATTERN","calculateCenter","delay","REF_PATTERN","REF_PATTERN","readFileSync","existsSync","mkdirSync","join","homedir","wsUrl","targets","page","attached","session","getUpgradeStatus"]}