browsirai 0.1.1 → 0.2.1
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.
- package/LICENSE +661 -78
- package/README.md +120 -6
- package/dist/bin.js +7 -17
- package/dist/bin.js.map +1 -1
- package/dist/cli/commands/act.js +1226 -0
- package/dist/cli/commands/act.js.map +1 -0
- package/dist/cli/commands/nav.js +739 -0
- package/dist/cli/commands/nav.js.map +1 -0
- package/dist/cli/commands/net.js +556 -0
- package/dist/cli/commands/net.js.map +1 -0
- package/dist/cli/commands/obs.js +1049 -0
- package/dist/cli/commands/obs.js.map +1 -0
- package/dist/cli/run.js +728 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli.js +7 -17
- package/dist/cli.js.map +1 -1
- package/dist/server.js +4 -2
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/run.ts","../../src/chrome-launcher.ts","../../src/cdp/connection.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 \"--no-sandbox\",\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 \"--no-sandbox\",\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 * 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"],"mappings":"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","names":["r","healthy"]}
|
package/dist/cli.js
CHANGED
|
@@ -457,7 +457,8 @@ async function launchChromeWithDebugging(port = 9222, headless = false) {
|
|
|
457
457
|
}
|
|
458
458
|
const args = [
|
|
459
459
|
`--remote-debugging-port=${targetPort}`,
|
|
460
|
-
"--remote-allow-origins=*"
|
|
460
|
+
"--remote-allow-origins=*",
|
|
461
|
+
"--no-sandbox"
|
|
461
462
|
];
|
|
462
463
|
if (dataDir) {
|
|
463
464
|
args.push(`--user-data-dir=${dataDir}`, "--no-first-run", "--no-default-browser-check", "--disable-extensions");
|
|
@@ -571,7 +572,8 @@ async function launchHeadlessChrome() {
|
|
|
571
572
|
"--no-first-run",
|
|
572
573
|
"--no-default-browser-check",
|
|
573
574
|
"--disable-extensions",
|
|
574
|
-
"--disable-gpu"
|
|
575
|
+
"--disable-gpu",
|
|
576
|
+
"--no-sandbox"
|
|
575
577
|
], {
|
|
576
578
|
detached: true,
|
|
577
579
|
stdio: "ignore"
|
|
@@ -4913,21 +4915,9 @@ async function runCli(args) {
|
|
|
4913
4915
|
break;
|
|
4914
4916
|
}
|
|
4915
4917
|
default: {
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
`browsirai v${VERSION}`,
|
|
4920
|
-
"",
|
|
4921
|
-
"Usage: browsirai [command]",
|
|
4922
|
-
"",
|
|
4923
|
-
"Commands:",
|
|
4924
|
-
" (none) Start the MCP server (default)",
|
|
4925
|
-
" doctor Run diagnostics",
|
|
4926
|
-
" install Install browser integration",
|
|
4927
|
-
" --version Print version",
|
|
4928
|
-
""
|
|
4929
|
-
].join("\n")
|
|
4930
|
-
);
|
|
4918
|
+
const cliUrl = new URL("./cli/run.js", import.meta.url);
|
|
4919
|
+
const { runCLI } = await import(cliUrl.href);
|
|
4920
|
+
await runCLI(args);
|
|
4931
4921
|
break;
|
|
4932
4922
|
}
|
|
4933
4923
|
}
|