browsirai 0.1.0 → 0.2.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/cli/run.ts","../../../src/chrome-launcher.ts","../../../src/tools/browser-click.ts","../../../src/tools/browser-fill-form.ts","../../../src/tools/browser-type.ts","../../../src/tools/browser-press-key.ts","../../../src/tools/browser-hover.ts","../../../src/tools/browser-drag.ts","../../../src/tools/browser-select-option.ts","../../../src/tools/browser-file-upload.ts","../../../src/tools/browser-handle-dialog.ts","../../../src/cli/commands/act.ts"],"sourcesContent":["/**\n * CLI runner for browsirai.\n *\n * Parses `browsirai <command> [args...]`, connects to Chrome via CDP,\n * looks up the command in a registry, and executes it.\n */\n\nimport pc from \"picocolors\";\nimport { connectChrome } from \"../chrome-launcher.js\";\nimport { CDPConnection } from \"../cdp/connection.js\";\nimport type { CLICommand } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Flag parsing utility\n// ---------------------------------------------------------------------------\n\n/**\n * Parses CLI flags from an args array.\n *\n * Supports:\n * --key=value → { key: \"value\" }\n * --key value → { key: \"value\" }\n * --flag → { flag: \"true\" }\n * -i → { i: \"true\" } (short boolean)\n * -d 5 → { d: \"5\" } (short with value)\n * -ic → { i: \"true\", c: \"true\" } (combined short booleans)\n * positional → { _0: \"positional\", _1: ... }\n *\n * @returns Record of parsed flags and positional args keyed as _0, _1, etc.\n */\nexport function parseFlags(args: string[]): Record<string, string> {\n const flags: Record<string, string> = {};\n let positionalIndex = 0;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg.startsWith(\"--\")) {\n const eqIdx = arg.indexOf(\"=\");\n if (eqIdx !== -1) {\n // --key=value\n const key = arg.slice(2, eqIdx);\n const value = arg.slice(eqIdx + 1);\n flags[key] = value;\n } else {\n const key = arg.slice(2);\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n // --key value\n flags[key] = next;\n i++;\n } else {\n // --flag (boolean)\n flags[key] = \"true\";\n }\n }\n } else if (arg.startsWith(\"-\") && arg.length > 1 && !/^-\\d/.test(arg)) {\n // Short flags: -i, -c, -d 5, -ic\n const chars = arg.slice(1);\n if (chars.length === 1) {\n // Single short flag: -i or -d 5\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n flags[chars] = next;\n i++;\n } else {\n flags[chars] = \"true\";\n }\n } else {\n // Combined short flags: -ic → i=true, c=true\n for (const ch of chars) {\n flags[ch] = \"true\";\n }\n }\n } else {\n flags[`_${positionalIndex}`] = arg;\n positionalIndex++;\n }\n }\n\n return flags;\n}\n\n// ---------------------------------------------------------------------------\n// Result printer\n// ---------------------------------------------------------------------------\n\n/**\n * Pretty-prints a command result to stdout.\n * Objects/arrays are JSON-formatted; primitives are printed as-is.\n */\nexport function printResult(data: unknown): void {\n if (data === undefined || data === null) return;\n\n if (typeof data === \"string\") {\n console.log(data);\n } else if (typeof data === \"object\") {\n console.log(JSON.stringify(data, null, 2));\n } else {\n console.log(String(data));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command registry\n// ---------------------------------------------------------------------------\n\ninterface CommandCategory {\n name: string;\n commands: CLICommand[];\n}\n\nasync function loadCommands(): Promise<CommandCategory[]> {\n const categories: CommandCategory[] = [];\n\n const imports: Array<{\n name: string;\n path: string;\n key: string;\n }> = [\n { name: \"Navigation\", path: \"./commands/nav.js\", key: \"navCommands\" },\n { name: \"Observation\", path: \"./commands/obs.js\", key: \"obsCommands\" },\n { name: \"Actions\", path: \"./commands/act.js\", key: \"actCommands\" },\n { name: \"Network\", path: \"./commands/net.js\", key: \"netCommands\" },\n ];\n\n const base = new URL(\".\", import.meta.url);\n for (const entry of imports) {\n try {\n const url = new URL(entry.path, base).href;\n const mod = (await import(url)) as Record<string, CLICommand[]>;\n const commands = mod[entry.key];\n if (commands && Array.isArray(commands) && commands.length > 0) {\n categories.push({ name: entry.name, commands });\n }\n } catch {\n // Command file not yet created — skip silently\n }\n }\n\n return categories;\n}\n\nfunction buildRegistry(\n categories: CommandCategory[],\n): Map<string, CLICommand> {\n const registry = new Map<string, CLICommand>();\n for (const cat of categories) {\n for (const cmd of cat.commands) {\n registry.set(cmd.name, cmd);\n if (cmd.aliases) {\n for (const alias of cmd.aliases) {\n registry.set(alias, cmd);\n }\n }\n }\n }\n return registry;\n}\n\n// ---------------------------------------------------------------------------\n// Help output\n// ---------------------------------------------------------------------------\n\nfunction printHelp(categories: CommandCategory[]): void {\n console.log();\n console.log(pc.bold(\"browsirai\") + \" — Browser automation from the terminal\");\n console.log();\n console.log(pc.dim(\"Usage:\") + \" browsirai <command> [args...] [--flags]\");\n console.log();\n\n if (categories.length === 0) {\n console.log(\n pc.yellow(\" No commands available yet. Command modules have not been installed.\"),\n );\n console.log();\n return;\n }\n\n for (const cat of categories) {\n console.log(pc.cyan(pc.bold(` ${cat.name}`)));\n for (const cmd of cat.commands) {\n const aliasStr = cmd.aliases?.length\n ? pc.dim(` (${cmd.aliases.join(\", \")})`)\n : \"\";\n const name = pc.green(cmd.name.padEnd(20));\n console.log(` ${name} ${pc.dim(cmd.description)}${aliasStr}`);\n }\n console.log();\n }\n\n console.log(pc.dim(\" Examples:\"));\n console.log(pc.dim(' browsirai open example.com'));\n console.log(pc.dim(' browsirai snapshot -i'));\n console.log(pc.dim(' browsirai click @e5'));\n console.log(pc.dim(' browsirai fill @e2 \"hello world\"'));\n console.log(pc.dim(' browsirai press Enter'));\n console.log(pc.dim(' browsirai eval \"document.title\"'));\n console.log();\n}\n\n// ---------------------------------------------------------------------------\n// CDP connection helper\n// ---------------------------------------------------------------------------\n\nasync function connectCDP(): Promise<CDPConnection> {\n const result = await connectChrome({ autoLaunch: true });\n\n if (!result.success) {\n const msg = result.error ?? \"Could not connect to Chrome via CDP.\";\n throw new Error(msg);\n }\n\n const wsUrl = result.wsEndpoint ?? `ws://127.0.0.1:${result.port}/devtools/browser`;\n const browser = new CDPConnection(wsUrl);\n await browser.connect();\n\n // Find a page target and attach to it (same as MCP server)\n const targets = await browser.send(\"Target.getTargets\") as {\n targetInfos: Array<{ targetId: string; type: string; url: 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 const created = await browser.send(\"Target.createTarget\", { url: \"about:blank\" }) as { targetId: string };\n page = { targetId: created.targetId, type: \"page\", url: \"about:blank\" };\n }\n\n const attached = await browser.send(\"Target.attachToTarget\", {\n targetId: page.targetId,\n flatten: true,\n }) as { sessionId: string };\n\n // Return a session-scoped proxy that sends commands with the sessionId\n const sessionId = attached.sessionId;\n const session = Object.create(browser) as CDPConnection;\n const originalSend = browser.send.bind(browser);\n session.send = (method: string, params?: Record<string, unknown>, options?: { timeout?: number; sessionId?: string }) => {\n return originalSend(method, params, {\n ...options,\n sessionId: options?.sessionId ?? sessionId,\n });\n };\n session.close = () => browser.close();\n\n await Promise.all([\n session.send(\"Page.enable\"),\n session.send(\"Runtime.enable\"),\n ]).catch(() => {});\n\n return session;\n}\n\n\n// ---------------------------------------------------------------------------\n// Main CLI runner\n// ---------------------------------------------------------------------------\n\nexport async function runCLI(args: string[]): Promise<void> {\n const commandName = args[0];\n const remainingArgs = args.slice(1);\n\n // Load available commands\n const categories = await loadCommands();\n const registry = buildRegistry(categories);\n\n // No command or --help → show help\n if (!commandName || commandName === \"--help\" || commandName === \"-h\") {\n printHelp(categories);\n return;\n }\n\n // Look up the command\n const command = registry.get(commandName);\n if (!command) {\n console.error(\n pc.red(`Unknown command: ${pc.bold(commandName)}`),\n );\n console.log();\n console.log(\n pc.dim(\"Run \") + pc.bold(\"browsirai --help\") + pc.dim(\" to see available commands.\"),\n );\n\n // Suggest similar commands\n const similar = findSimilar(commandName, registry);\n if (similar.length > 0) {\n console.log();\n console.log(pc.dim(\"Did you mean?\"));\n for (const s of similar) {\n console.log(` ${pc.green(s)}`);\n }\n }\n\n console.log();\n process.exit(1);\n }\n\n // Connect to Chrome and run the command\n let cdp: CDPConnection | null = null;\n try {\n cdp = await connectCDP();\n await command.run(cdp, remainingArgs);\n } catch (err) {\n const message =\n err instanceof Error ? err.message : String(err);\n console.error(pc.red(`Error: ${message}`));\n process.exit(1);\n } finally {\n if (cdp?.isConnected) {\n cdp.close();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Fuzzy matching helper\n// ---------------------------------------------------------------------------\n\nfunction findSimilar(\n input: string,\n registry: Map<string, CLICommand>,\n): string[] {\n const names = Array.from(registry.keys());\n return names\n .filter((name) => {\n // Simple substring or prefix match\n return (\n name.includes(input) ||\n input.includes(name) ||\n levenshtein(input, name) <= 3\n );\n })\n .slice(0, 3);\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array(n + 1).fill(0) as number[],\n );\n\n for (let i = 0; i <= m; i++) dp[i]![0] = i;\n for (let j = 0; j <= n; j++) dp[0]![j] = j;\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n dp[i]![j] = Math.min(\n dp[i - 1]![j]! + 1,\n dp[i]![j - 1]! + 1,\n dp[i - 1]![j - 1]! + cost,\n );\n }\n }\n\n return dp[m]![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_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_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_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_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_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_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 * 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 * Interaction CLI commands for browsirai.\n *\n * Commands: click, fill, type, key, hover, drag, select, upload, dialog\n *\n * Each command wraps the corresponding tool function from src/tools/,\n * parsing CLI args into the expected params and printing human-readable output.\n */\n\nimport type { CLICommand } from \"../types.js\";\nimport { parseFlags } from \"../run.js\";\nimport { browserClick } from \"../../tools/browser-click.js\";\nimport { browserFillForm } from \"../../tools/browser-fill-form.js\";\nimport { browserType } from \"../../tools/browser-type.js\";\nimport { browserPressKey } from \"../../tools/browser-press-key.js\";\nimport { browserHover } from \"../../tools/browser-hover.js\";\nimport { browserDrag } from \"../../tools/browser-drag.js\";\nimport { browserSelectOption } from \"../../tools/browser-select-option.js\";\nimport { browserFileUpload } from \"../../tools/browser-file-upload.js\";\nimport { browserHandleDialog } from \"../../tools/browser-handle-dialog.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalises a user-supplied reference to the canonical `@eN` format.\n * Accepts: `@e5`, `e5`, `@E5`, `E5`.\n */\nfunction normaliseRef(input: string): string {\n const cleaned = input.startsWith(\"@\") ? input.slice(1) : input;\n const match = /^e(\\d+)$/i.exec(cleaned);\n if (!match) {\n throw new Error(`Invalid ref format: ${input}. Expected @eN (e.g. @e5).`);\n }\n return `@e${match[1]}`;\n}\n\n/**\n * Determines whether an argument looks like a ref (`@eN` or `eN`)\n * rather than a CSS selector.\n */\nfunction looksLikeRef(arg: string): boolean {\n return /^@?e\\d+$/i.test(arg);\n}\n\n// ---------------------------------------------------------------------------\n// click\n// ---------------------------------------------------------------------------\n\nconst click: CLICommand = {\n name: \"click\",\n description: \"Click an element by ref or CSS selector\",\n usage: \"browsirai click <ref-or-selector> [--newTab]\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const target = flags._0;\n\n if (!target) {\n console.error(\"Usage: browsirai click <ref-or-selector> [--newTab]\");\n console.error(\" Provide an @eN ref or CSS selector as the first argument.\");\n process.exit(1);\n }\n\n const newTab = flags.newTab === \"true\";\n\n try {\n if (looksLikeRef(target)) {\n const ref = normaliseRef(target);\n await browserClick(cdp, { ref, newTab });\n console.log(`Clicked ${ref}`);\n } else {\n await browserClick(cdp, { selector: target, newTab });\n console.log(`Clicked ${target}`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Click failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// fill\n// ---------------------------------------------------------------------------\n\nconst fill: CLICommand = {\n name: \"fill\",\n description: \"Fill a form field with a value\",\n usage: \"browsirai fill <ref-or-selector> <value>\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const target = flags._0;\n const value = flags._1;\n\n if (!target || value === undefined) {\n console.error(\"Usage: browsirai fill <ref-or-selector> <value>\");\n console.error(\" Provide an @eN ref or CSS selector and a value.\");\n process.exit(1);\n }\n\n try {\n const isRef = looksLikeRef(target);\n const ref = isRef ? normaliseRef(target) : undefined;\n const selector = isRef ? undefined : target;\n const label = isRef ? normaliseRef(target) : target;\n\n await browserFillForm(cdp, {\n fields: [\n {\n name: label,\n type: \"textbox\",\n ref,\n selector,\n value,\n },\n ],\n });\n\n const displayValue = value.length > 40 ? value.slice(0, 40) + \"...\" : value;\n console.log(`Filled ${label} with '${displayValue}'`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Fill failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// type\n// ---------------------------------------------------------------------------\n\nconst type: CLICommand = {\n name: \"type\",\n description: \"Type text into the focused or specified element\",\n usage: \"browsirai type <text> [--ref=@e3] [--submit] [--slowly]\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const text = flags._0;\n\n if (!text) {\n console.error(\"Usage: browsirai type <text> [--ref=@e3] [--submit] [--slowly]\");\n console.error(\" Provide the text to type as the first argument.\");\n process.exit(1);\n }\n\n const ref = flags.ref ? normaliseRef(flags.ref) : undefined;\n const selector = flags.selector;\n const submit = flags.submit === \"true\";\n const slowly = flags.slowly === \"true\";\n\n try {\n await browserType(cdp, { text, ref, selector, slowly, submit });\n\n const displayText = text.length > 40 ? text.slice(0, 40) + \"...\" : text;\n const target = ref ?? selector ?? \"focused element\";\n console.log(`Typed '${displayText}' into ${target}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Type failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// key\n// ---------------------------------------------------------------------------\n\nconst key: CLICommand = {\n name: \"press\",\n aliases: [\"key\"],\n description: \"Press a key or key combination\",\n usage: \"browsirai press <key-combo>\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const keyCombo = flags._0;\n\n if (!keyCombo) {\n console.error(\"Usage: browsirai key <key-combo>\");\n console.error(\" Examples: Enter, Tab, Control+c, Shift+Tab, Meta+a\");\n process.exit(1);\n }\n\n try {\n await browserPressKey(cdp, { key: keyCombo });\n console.log(`Pressed ${keyCombo}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Key press failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// hover\n// ---------------------------------------------------------------------------\n\nconst hover: CLICommand = {\n name: \"hover\",\n description: \"Hover over an element by ref or selector\",\n usage: \"browsirai hover <ref-or-selector>\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const target = flags._0;\n\n if (!target) {\n console.error(\"Usage: browsirai hover <ref-or-selector>\");\n console.error(\" Provide an @eN ref or CSS selector.\");\n process.exit(1);\n }\n\n try {\n if (looksLikeRef(target)) {\n const ref = normaliseRef(target);\n await browserHover(cdp, { ref });\n console.log(`Hovered ${ref}`);\n } else {\n await browserHover(cdp, { selector: target });\n console.log(`Hovered ${target}`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Hover failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// drag\n// ---------------------------------------------------------------------------\n\nconst drag: CLICommand = {\n name: \"drag\",\n description: \"Drag from one element to another\",\n usage: \"browsirai drag <startRef> <endRef>\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const startArg = flags._0;\n const endArg = flags._1;\n\n if (!startArg || !endArg) {\n console.error(\"Usage: browsirai drag <startRef> <endRef>\");\n console.error(\" Provide two @eN refs (source and target).\");\n process.exit(1);\n }\n\n try {\n const startRef = normaliseRef(startArg);\n const endRef = normaliseRef(endArg);\n\n await browserDrag(cdp, { startRef, endRef });\n console.log(`Dragged ${startRef} \\u2192 ${endRef}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Drag failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// select\n// ---------------------------------------------------------------------------\n\nconst select: CLICommand = {\n name: \"select\",\n description: \"Select option(s) in a <select> element\",\n usage: \"browsirai select <ref> <value1> [value2...]\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const refArg = flags._0;\n\n if (!refArg) {\n console.error(\"Usage: browsirai select <ref> <value1> [value2...]\");\n console.error(\" Provide an @eN ref and one or more values to select.\");\n process.exit(1);\n }\n\n // Collect all positional args after the ref as values\n const values: string[] = [];\n let i = 1;\n while (flags[`_${i}`] !== undefined) {\n values.push(flags[`_${i}`]);\n i++;\n }\n\n if (values.length === 0) {\n console.error(\"Usage: browsirai select <ref> <value1> [value2...]\");\n console.error(\" Provide at least one value to select.\");\n process.exit(1);\n }\n\n try {\n const ref = normaliseRef(refArg);\n const result = await browserSelectOption(cdp, {\n ref,\n values,\n element: ref,\n });\n\n const displayValues = result.selected.length > 0\n ? result.selected.map((v) => `'${v}'`).join(\", \")\n : values.map((v) => `'${v}'`).join(\", \");\n console.log(`Selected ${displayValues} in ${ref}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Select failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// upload\n// ---------------------------------------------------------------------------\n\nconst upload: CLICommand = {\n name: \"upload\",\n description: \"Upload file(s) to a file input element\",\n usage: \"browsirai upload <ref> <file1> [file2...]\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const refArg = flags._0;\n\n if (!refArg) {\n console.error(\"Usage: browsirai upload <ref> <file1> [file2...]\");\n console.error(\" Provide an @eN ref and one or more file paths.\");\n process.exit(1);\n }\n\n // Collect all positional args after the ref as file paths\n const paths: string[] = [];\n let i = 1;\n while (flags[`_${i}`] !== undefined) {\n paths.push(flags[`_${i}`]);\n i++;\n }\n\n if (paths.length === 0) {\n console.error(\"Usage: browsirai upload <ref> <file1> [file2...]\");\n console.error(\" Provide at least one file path to upload.\");\n process.exit(1);\n }\n\n try {\n const ref = normaliseRef(refArg);\n const result = await browserFileUpload(cdp, { ref, paths });\n\n if (result.success) {\n console.log(`Uploaded ${result.filesCount} file(s) to ${ref}`);\n } else {\n console.error(`Upload failed: ${result.error}`);\n process.exit(1);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Upload failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// dialog\n// ---------------------------------------------------------------------------\n\nconst dialog: CLICommand = {\n name: \"dialog\",\n description: \"Accept or dismiss a JavaScript dialog\",\n usage: \"browsirai dialog <accept|dismiss> [--text=...]\",\n async run(cdp, args) {\n const flags = parseFlags(args);\n const action = flags._0;\n\n if (!action || (action !== \"accept\" && action !== \"dismiss\")) {\n console.error(\"Usage: browsirai dialog <accept|dismiss> [--text=...]\");\n console.error(\" First argument must be 'accept' or 'dismiss'.\");\n process.exit(1);\n }\n\n const accept = action === \"accept\";\n const promptText = flags.text;\n\n try {\n const result = await browserHandleDialog(cdp, {\n accept,\n promptText,\n });\n\n if (result.success) {\n console.log(`Dialog ${accept ? \"accepted\" : \"dismissed\"}`);\n } else {\n console.error(`Dialog handling failed: ${result.error}`);\n process.exit(1);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Dialog failed: ${msg}`);\n process.exit(1);\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Export\n// ---------------------------------------------------------------------------\n\nexport const actCommands: CLICommand[] = [\n click,\n fill,\n type,\n key,\n hover,\n drag,\n select,\n upload,\n dialog,\n];\n"],"mappings":"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","names":["key","parseRef","key","delay","MODIFIER_BITS","getKeyCode","key","getCode","REF_PATTERN","calculateCenter","resolveElementCoordinates","REF_PATTERN","calculateCenter","delay","REF_PATTERN"]}