@canaryai/cli 0.1.6 → 0.1.11

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.
Files changed (37) hide show
  1. package/README.md +52 -1
  2. package/dist/bin.js +1 -6
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-7OCVIDC7.js → chunk-DGUM43GV.js} +1 -2
  5. package/dist/{chunk-55MFLJD7.js → chunk-G2X3H7AM.js} +1 -3
  6. package/dist/{chunk-55MFLJD7.js.map → chunk-G2X3H7AM.js.map} +1 -1
  7. package/dist/chunk-HJ2JWIJ7.js +91 -0
  8. package/dist/chunk-HJ2JWIJ7.js.map +1 -0
  9. package/dist/{chunk-7AP5KRVU.js → chunk-ROTCL5WO.js} +1 -3
  10. package/dist/{chunk-7AP5KRVU.js.map → chunk-ROTCL5WO.js.map} +1 -1
  11. package/dist/{chunk-Z6I3ZXZL.js → chunk-VYBCH4ZP.js} +2 -3
  12. package/dist/{chunk-Z6I3ZXZL.js.map → chunk-VYBCH4ZP.js.map} +1 -1
  13. package/dist/feature-flag-PN5IFFQR.js +226 -0
  14. package/dist/feature-flag-PN5IFFQR.js.map +1 -0
  15. package/dist/index.js +1207 -8
  16. package/dist/index.js.map +1 -1
  17. package/dist/knobs-DAG7HD2F.js +286 -0
  18. package/dist/knobs-DAG7HD2F.js.map +1 -0
  19. package/dist/{local-browser-5LJ7UPOH.js → local-browser-VOBIUIGT.js} +4 -5
  20. package/dist/{local-browser-5LJ7UPOH.js.map → local-browser-VOBIUIGT.js.map} +1 -1
  21. package/dist/{mcp-P2B24MTM.js → mcp-I6FCGDDR.js} +5 -6
  22. package/dist/{mcp-P2B24MTM.js.map → mcp-I6FCGDDR.js.map} +1 -1
  23. package/dist/psql-A3BADRQN.js +124 -0
  24. package/dist/psql-A3BADRQN.js.map +1 -0
  25. package/dist/redis-N2DSDDQU.js +130 -0
  26. package/dist/redis-N2DSDDQU.js.map +1 -0
  27. package/dist/runner/preload.js +2 -3
  28. package/dist/runner/preload.js.map +1 -1
  29. package/dist/test.js +2 -3
  30. package/dist/test.js.map +1 -1
  31. package/package.json +3 -4
  32. package/dist/bin.d.ts +0 -2
  33. package/dist/chunk-UBYYNMML.js +0 -21
  34. package/dist/chunk-UBYYNMML.js.map +0 -1
  35. package/dist/chunk-YA43CE6P.js +0 -781
  36. package/dist/chunk-YA43CE6P.js.map +0 -1
  37. /package/dist/{chunk-7OCVIDC7.js.map → chunk-DGUM43GV.js.map} +0 -0
package/README.md CHANGED
@@ -13,9 +13,17 @@ bun add -g @canaryai/cli
13
13
  ## Login
14
14
 
15
15
  ```bash
16
- canary login
16
+ canary login # production (default)
17
+ canary login --env dev # dev environment
18
+ canary login --env local # local development
17
19
  ```
18
20
 
21
+ Options:
22
+ - `--env <env>` - Environment to login to: `prod`, `dev`, or `local`
23
+ - `--api-url <url>` - Custom API URL (overrides --env)
24
+ - `--app-url <url>` - Custom app URL (overrides --env)
25
+ - `--no-open` - Don't auto-open browser
26
+
19
27
  ## Quickstart (local testing)
20
28
 
21
29
  1) Start your app locally.
@@ -43,6 +51,49 @@ Tools:
43
51
  - `local_run_tests` (port, instructions, title)
44
52
  - `local_wait_for_results` (runId)
45
53
 
54
+ ## PSQL (superadmin only)
55
+
56
+ Execute read-only SQL queries against the production database. Requires superadmin privileges and the `cli.psql.enabled` knob to be enabled.
57
+
58
+ ```bash
59
+ canary psql "SELECT id, status FROM jobs LIMIT 5"
60
+ canary psql "SELECT * FROM jobs WHERE status = 'running'" --json
61
+ ```
62
+
63
+ Options:
64
+ - `--json` - Output results as JSON instead of a table
65
+ - `--query <sql>` - Alternative to positional query argument
66
+
67
+ Limits:
68
+ - Query size: 10KB max (for larger queries, use psql directly)
69
+ - Query timeout: 30s default (configurable via `cli.psql.timeout_ms` knob)
70
+ - Result rows: 10K max (results truncated if exceeded)
71
+
72
+ ### Security Model
73
+
74
+ The read-only PostgreSQL user (`debug_agent`) provides the **primary security layer** - it has SELECT-only privileges enforced at the database level. Any modification attempts will fail at the database regardless of other controls.
75
+
76
+ Keyword validation serves as a **secondary defense-in-depth** measure that:
77
+ 1. Prevents modification attempts from reaching the database
78
+ 2. Triggers Slack alerts and auto-disables the feature on suspicious activity
79
+ 3. Provides an audit trail of attempted misuse
80
+
81
+ Blocked keywords include: INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, GRANT, REVOKE, VACUUM, REINDEX, COPY, EXECUTE, CALL, DO, PREPARE, SET, RESET, LOCK, COMMIT, ROLLBACK, LISTEN, NOTIFY.
82
+
83
+ ### Security Controls Summary
84
+
85
+ | Control | Purpose |
86
+ |---------|---------|
87
+ | Superadmin auth | Only trusted operators can access |
88
+ | `cli.psql.enabled` knob | Feature disabled by default, requires explicit enablement |
89
+ | Read-only DB user | Database-level protection against modifications |
90
+ | Keyword detection | Early blocking + alerting on suspicious queries |
91
+ | Auto-disable | Feature self-disables on modification attempts |
92
+ | Slack alerts | Immediate notification to security team |
93
+ | Query timeout | Prevents long-running queries from impacting production |
94
+ | Row limits | Prevents accidental full table dumps |
95
+ | RDS query logging | Infrastructure-level audit logging of all queries
96
+
46
97
  ## Environment variables
47
98
 
48
99
  - `CANARY_API_URL` (default `https://api.trycanary.ai`)
package/dist/bin.js CHANGED
@@ -1,11 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- main
4
- } from "./chunk-YA43CE6P.js";
5
- import "./chunk-Z6I3ZXZL.js";
6
- import "./chunk-UBYYNMML.js";
7
- import "./chunk-7OCVIDC7.js";
8
2
 
9
3
  // src/bin.ts
4
+ import { main } from "./index.js";
10
5
  void main(process.argv.slice(2));
11
6
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["import { main } from \"./index.js\";\n\nvoid main(process.argv.slice(2));\n"],"mappings":";;;;;;;;;AAEA,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["import { main } from \"./index.js\";\n\nvoid main(process.argv.slice(2));\n"],"mappings":";;;AAAA,SAAS,YAAY;AAErB,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC;","names":[]}
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
2
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
3
  }) : x)(function(x) {
@@ -9,4 +8,4 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
8
  export {
10
9
  __require
11
10
  };
12
- //# sourceMappingURL=chunk-7OCVIDC7.js.map
11
+ //# sourceMappingURL=chunk-DGUM43GV.js.map
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
1
  // src/local-browser/host.ts
4
2
  import { chromium } from "playwright";
5
3
  var HEARTBEAT_INTERVAL_MS = 3e4;
@@ -670,4 +668,4 @@ var LocalBrowserHost = class {
670
668
  export {
671
669
  LocalBrowserHost
672
670
  };
673
- //# sourceMappingURL=chunk-55MFLJD7.js.map
671
+ //# sourceMappingURL=chunk-G2X3H7AM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/local-browser/host.ts"],"sourcesContent":["/**\n * Local Browser Host\n *\n * Manages a local browser instance and handles commands from the cloud API\n * via WebSocket. This enables cloud agents to control a browser running\n * on the user's local machine.\n */\n\nimport { chromium, type Browser, type BrowserContext, type Page, type Dialog } from \"playwright\";\nimport type {\n BrowserCommand,\n BrowserResponse,\n HeartbeatMessage,\n SessionMessage,\n LocalBrowserMode,\n} from \"./protocol\";\n\nconst HEARTBEAT_INTERVAL_MS = 30_000;\nconst RECONNECT_DELAY_MS = 1000;\nconst MAX_RECONNECT_DELAY_MS = 30_000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\n\nexport interface LocalBrowserHostOptions {\n apiUrl: string;\n wsToken: string;\n sessionId: string;\n browserMode: LocalBrowserMode;\n cdpUrl?: string;\n headless?: boolean;\n storageStatePath?: string;\n onLog?: (level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) => void;\n}\n\ninterface PageWithAISnapshot extends Page {\n _snapshotForAI(options: { mode: \"full\" | \"compact\" }): Promise<string>;\n}\n\n/**\n * LocalBrowserHost manages the browser and WebSocket connection to the cloud.\n */\nexport class LocalBrowserHost {\n private options: LocalBrowserHostOptions;\n private ws: WebSocket | null = null;\n private browser: Browser | null = null;\n private context: BrowserContext | null = null;\n private page: Page | null = null;\n private pendingDialogs: Dialog[] = [];\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private reconnectAttempts = 0;\n private isShuttingDown = false;\n private lastSnapshotYaml = \"\";\n\n constructor(options: LocalBrowserHostOptions) {\n this.options = options;\n }\n\n private log(level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) {\n if (this.options.onLog) {\n this.options.onLog(level, message, data);\n } else {\n const fn = level === \"error\" ? console.error : level === \"warn\" ? console.warn : console.log;\n fn(`[LocalBrowserHost] ${message}`, data ?? \"\");\n }\n }\n\n // =========================================================================\n // Lifecycle\n // =========================================================================\n\n async start(): Promise<void> {\n this.log(\"info\", \"Starting local browser host\", {\n browserMode: this.options.browserMode,\n sessionId: this.options.sessionId,\n });\n\n // Connect to WebSocket first\n await this.connectWebSocket();\n\n // Then launch browser\n await this.launchBrowser();\n\n // Notify cloud that browser is ready\n this.sendSessionEvent(\"browser_ready\");\n }\n\n async stop(): Promise<void> {\n this.isShuttingDown = true;\n this.log(\"info\", \"Stopping local browser host\");\n\n this.stopHeartbeat();\n\n if (this.ws) {\n try {\n this.ws.close(1000, \"Shutdown\");\n } catch {}\n this.ws = null;\n }\n\n if (this.context) {\n try {\n await this.context.close();\n } catch {}\n this.context = null;\n }\n\n if (this.browser) {\n try {\n await this.browser.close();\n } catch {}\n this.browser = null;\n }\n\n this.page = null;\n this.log(\"info\", \"Local browser host stopped\");\n }\n\n // =========================================================================\n // WebSocket Connection\n // =========================================================================\n\n private async connectWebSocket(): Promise<void> {\n return new Promise((resolve, reject) => {\n const wsUrl = `${this.options.apiUrl.replace(\"http\", \"ws\")}/local-browser/sessions/${this.options.sessionId}/connect?token=${this.options.wsToken}`;\n\n this.log(\"info\", \"Connecting to cloud API\", { url: wsUrl.replace(/token=.*/, \"token=***\") });\n\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n this.log(\"info\", \"Connected to cloud API\");\n this.ws = ws;\n this.reconnectAttempts = 0;\n this.startHeartbeat();\n resolve();\n };\n\n ws.onmessage = (event) => {\n this.handleMessage(event.data as string);\n };\n\n ws.onerror = (event) => {\n this.log(\"error\", \"WebSocket error\", event);\n };\n\n ws.onclose = () => {\n this.log(\"info\", \"WebSocket closed\");\n this.stopHeartbeat();\n this.ws = null;\n\n if (!this.isShuttingDown) {\n this.scheduleReconnect();\n }\n };\n\n // Timeout after 30 seconds\n setTimeout(() => {\n if (!this.ws) {\n reject(new Error(\"WebSocket connection timeout\"));\n }\n }, 30_000);\n });\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n this.log(\"error\", \"Max reconnection attempts reached, giving up\");\n this.stop();\n return;\n }\n\n const delay = Math.min(\n RECONNECT_DELAY_MS * Math.pow(2, this.reconnectAttempts),\n MAX_RECONNECT_DELAY_MS\n );\n\n this.reconnectAttempts++;\n this.log(\"info\", `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n\n setTimeout(async () => {\n try {\n await this.connectWebSocket();\n this.sendSessionEvent(\"connected\");\n if (this.page) {\n this.sendSessionEvent(\"browser_ready\");\n }\n } catch (error) {\n this.log(\"error\", \"Reconnection failed\", error);\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n // =========================================================================\n // Heartbeat\n // =========================================================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const ping: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws.send(JSON.stringify(ping));\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // =========================================================================\n // Browser Management\n // =========================================================================\n\n private async launchBrowser(): Promise<void> {\n const { browserMode, cdpUrl, headless = true, storageStatePath } = this.options;\n\n if (browserMode === \"cdp\" && cdpUrl) {\n this.log(\"info\", \"Connecting to existing Chrome via CDP\", { cdpUrl });\n this.browser = await chromium.connectOverCDP(cdpUrl);\n const contexts = this.browser.contexts();\n this.context = contexts[0] ?? (await this.browser.newContext());\n const pages = this.context.pages();\n this.page = pages[0] ?? (await this.context.newPage());\n } else {\n this.log(\"info\", \"Launching new Playwright browser\", { headless });\n this.browser = await chromium.launch({\n headless,\n args: [\"--no-sandbox\"],\n });\n\n const contextOptions: { viewport: { width: number; height: number }; storageState?: string } =\n {\n viewport: { width: 1920, height: 1080 },\n };\n\n if (storageStatePath) {\n try {\n await Bun.file(storageStatePath).exists();\n contextOptions.storageState = storageStatePath;\n this.log(\"info\", \"Loading storage state\", { storageStatePath });\n } catch {\n this.log(\"debug\", \"Storage state file not found, starting fresh\");\n }\n }\n\n this.context = await this.browser.newContext(contextOptions);\n this.page = await this.context.newPage();\n }\n\n // Setup dialog handler\n this.page.on(\"dialog\", (dialog) => {\n this.pendingDialogs.push(dialog);\n });\n\n this.log(\"info\", \"Browser ready\");\n }\n\n // =========================================================================\n // Message Handling\n // =========================================================================\n\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data);\n\n if (message.type === \"heartbeat\" && message.direction === \"ping\") {\n // Respond to ping with pong\n const pong: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws?.send(JSON.stringify(pong));\n return;\n }\n\n if (message.type === \"command\") {\n this.handleCommand(message as BrowserCommand);\n return;\n }\n\n this.log(\"debug\", \"Received unknown message type\", message);\n } catch (error) {\n this.log(\"error\", \"Failed to parse message\", { error, data });\n }\n }\n\n private async handleCommand(command: BrowserCommand): Promise<void> {\n const startTime = Date.now();\n this.log(\"debug\", `Executing command: ${command.method}`, { id: command.id });\n\n try {\n const result = await this.executeMethod(command.method, command.args);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: true,\n result,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"debug\", `Command completed: ${command.method}`, {\n id: command.id,\n durationMs: Date.now() - startTime,\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: false,\n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"error\", `Command failed: ${command.method}`, {\n id: command.id,\n error: errorMessage,\n });\n }\n }\n\n private sendSessionEvent(\n event: \"connected\" | \"disconnected\" | \"browser_ready\" | \"browser_closed\" | \"error\",\n error?: string\n ): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n\n const message: SessionMessage = {\n type: \"session\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n event,\n browserMode: this.options.browserMode,\n error,\n };\n this.ws.send(JSON.stringify(message));\n }\n\n // =========================================================================\n // Method Execution\n // =========================================================================\n\n private async executeMethod(method: string, args: unknown[]): Promise<unknown> {\n // Route to appropriate handler\n switch (method) {\n // Lifecycle\n case \"connect\":\n return this.connect(args[0] as any);\n case \"disconnect\":\n return this.disconnect();\n\n // Navigation\n case \"navigate\":\n return this.navigate(args[0] as string, args[1] as any);\n case \"navigateBack\":\n return this.navigateBack(args[0] as any);\n\n // Page Inspection\n case \"snapshot\":\n return this.snapshot(args[0] as any);\n case \"takeScreenshot\":\n return this.takeScreenshot(args[0] as any);\n case \"evaluate\":\n return this.evaluate(args[0] as string, args[1] as any);\n case \"runCode\":\n return this.runCode(args[0] as string, args[1] as any);\n case \"consoleMessages\":\n return this.consoleMessages(args[0] as any);\n case \"networkRequests\":\n return this.networkRequests(args[0] as any);\n\n // Interaction\n case \"click\":\n return this.click(args[0] as string, args[1] as string, args[2] as any);\n case \"clickAtCoordinates\":\n return this.clickAtCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"moveToCoordinates\":\n return this.moveToCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"dragCoordinates\":\n return this.dragCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as number,\n args[3] as number,\n args[4] as string,\n args[5] as any\n );\n case \"hover\":\n return this.hover(args[0] as string, args[1] as string, args[2] as any);\n case \"drag\":\n return this.drag(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as string,\n args[4] as any\n );\n case \"type\":\n return this.type(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as boolean,\n args[4] as any\n );\n case \"pressKey\":\n return this.pressKey(args[0] as string, args[1] as any);\n case \"fillForm\":\n return this.fillForm(args[0] as any[], args[1] as any);\n case \"selectOption\":\n return this.selectOption(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as any\n );\n case \"fileUpload\":\n return this.fileUpload(args[0] as string[], args[1] as any);\n\n // Dialogs\n case \"handleDialog\":\n return this.handleDialog(args[0] as \"accept\" | \"dismiss\", args[1] as string, args[2] as any);\n\n // Waiting\n case \"waitFor\":\n return this.waitFor(args[0] as any);\n\n // Browser Management\n case \"close\":\n return this.closePage(args[0] as any);\n case \"resize\":\n return this.resize(args[0] as number, args[1] as number, args[2] as any);\n case \"tabs\":\n return this.tabs(args[0] as any, args[1] as number, args[2] as any);\n\n // Storage\n case \"getStorageState\":\n return this.getStorageState(args[0] as any);\n case \"getCurrentUrl\":\n return this.getCurrentUrl(args[0] as any);\n case \"getTitle\":\n return this.getTitle(args[0] as any);\n case \"getLinks\":\n return this.getLinks(args[0] as any);\n case \"getElementBoundingBox\":\n return this.getElementBoundingBox(args[0] as string, args[1] as any);\n\n // Tracing\n case \"startTracing\":\n return this.startTracing(args[0] as any);\n case \"stopTracing\":\n return this.stopTracing(args[0] as any);\n\n // Video\n case \"isVideoRecordingEnabled\":\n return false; // Video not supported in CLI host currently\n case \"saveVideo\":\n return null;\n case \"getVideoPath\":\n return null;\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n }\n\n // =========================================================================\n // IBrowserClient Method Implementations\n // =========================================================================\n\n private getPage(): Page {\n if (!this.page) throw new Error(\"No page available\");\n return this.page;\n }\n\n private resolveRef(ref: string) {\n return this.getPage().locator(`aria-ref=${ref}`);\n }\n\n private async connect(_options: any): Promise<void> {\n // Browser already launched in start()\n return;\n }\n\n private async disconnect(): Promise<void> {\n await this.stop();\n }\n\n private async navigate(url: string, _opts?: any): Promise<string> {\n const page = this.getPage();\n await page.goto(url, { waitUntil: \"domcontentloaded\" });\n await page.waitForLoadState(\"load\", { timeout: 5000 }).catch(() => {});\n return this.captureSnapshot();\n }\n\n private async navigateBack(_opts?: any): Promise<string> {\n await this.getPage().goBack();\n return this.captureSnapshot();\n }\n\n private async snapshot(_opts?: any): Promise<string> {\n return this.captureSnapshot();\n }\n\n private async captureSnapshot(): Promise<string> {\n const page = this.getPage() as PageWithAISnapshot;\n this.lastSnapshotYaml = await page._snapshotForAI({ mode: \"full\" });\n return this.lastSnapshotYaml;\n }\n\n private async takeScreenshot(opts?: any): Promise<string | null> {\n const page = this.getPage();\n const buffer = await page.screenshot({\n type: opts?.type ?? \"jpeg\",\n fullPage: opts?.fullPage ?? false,\n });\n const mime = opts?.type === \"png\" ? \"image/png\" : \"image/jpeg\";\n return `data:${mime};base64,${buffer.toString(\"base64\")}`;\n }\n\n private async evaluate<T>(fn: string, _opts?: any): Promise<T> {\n const page = this.getPage();\n return page.evaluate(new Function(`return (${fn})()`) as () => T);\n }\n\n private async runCode(code: string, _opts?: any): Promise<unknown> {\n const page = this.getPage();\n const fn = new Function(\"page\", `return (async () => { ${code} })()`) as (\n p: Page\n ) => Promise<unknown>;\n return fn(page);\n }\n\n private async consoleMessages(_opts?: any): Promise<string> {\n // Console messages would need to be captured over time\n return \"Console message capture not implemented in CLI host\";\n }\n\n private async networkRequests(_opts?: any): Promise<string> {\n // Network requests would need to be captured over time\n return \"Network request capture not implemented in CLI host\";\n }\n\n private async click(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.scrollIntoViewIfNeeded({ timeout: 5000 }).catch(() => {});\n\n const box = await locator.boundingBox();\n if (box) {\n const centerX = box.x + box.width / 2;\n const centerY = box.y + box.height / 2;\n const page = this.getPage();\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.down(mod);\n }\n }\n\n if (opts?.doubleClick) {\n await page.mouse.dblclick(centerX, centerY);\n } else {\n await page.mouse.click(centerX, centerY);\n }\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.up(mod);\n }\n }\n } else {\n // Fallback to locator click\n if (opts?.doubleClick) {\n await locator.dblclick({ timeout: opts?.timeoutMs ?? 30000 });\n } else {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async clickAtCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n opts?: any\n ): Promise<void> {\n const page = this.getPage();\n if (opts?.doubleClick) {\n await page.mouse.dblclick(x, y);\n } else {\n await page.mouse.click(x, y);\n }\n }\n\n private async moveToCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n await this.getPage().mouse.move(x, y);\n }\n\n private async dragCoordinates(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n const page = this.getPage();\n await page.mouse.move(startX, startY);\n await page.mouse.down();\n await page.mouse.move(endX, endY);\n await page.mouse.up();\n }\n\n private async hover(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n await this.resolveRef(ref).hover({ timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async drag(\n startRef: string,\n _startElement: string,\n endRef: string,\n _endElement: string,\n opts?: any\n ): Promise<void> {\n const startLocator = this.resolveRef(startRef);\n const endLocator = this.resolveRef(endRef);\n await startLocator.dragTo(endLocator, { timeout: opts?.timeoutMs ?? 60000 });\n }\n\n private async type(\n ref: string,\n text: string,\n _elementDesc?: string,\n submit?: boolean,\n opts?: any\n ): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.clear();\n await locator.pressSequentially(text, {\n delay: opts?.delay ?? 0,\n timeout: opts?.timeoutMs ?? 30000,\n });\n if (submit) {\n await locator.press(\"Enter\");\n }\n }\n\n private async pressKey(key: string, _opts?: any): Promise<void> {\n await this.getPage().keyboard.press(key);\n }\n\n private async fillForm(fields: any[], opts?: any): Promise<void> {\n for (const field of fields) {\n const locator = this.resolveRef(field.ref);\n const fieldType = field.type ?? \"textbox\";\n\n switch (fieldType) {\n case \"checkbox\": {\n const isChecked = await locator.isChecked();\n const shouldBeChecked = field.value === \"true\";\n if (shouldBeChecked !== isChecked) {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n break;\n }\n case \"radio\":\n await locator.check({ timeout: opts?.timeoutMs ?? 30000 });\n break;\n case \"combobox\":\n await locator.selectOption(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n break;\n default:\n await locator.fill(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async selectOption(\n ref: string,\n value: string,\n _elementDesc?: string,\n opts?: any\n ): Promise<void> {\n await this.resolveRef(ref).selectOption(value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async fileUpload(paths: string[], opts?: any): Promise<void> {\n const fileChooser = await this.getPage().waitForEvent(\"filechooser\", {\n timeout: opts?.timeoutMs ?? 30000,\n });\n await fileChooser.setFiles(paths);\n }\n\n private async handleDialog(\n action: \"accept\" | \"dismiss\",\n promptText?: string,\n _opts?: any\n ): Promise<void> {\n const dialog = this.pendingDialogs.shift();\n if (dialog) {\n if (action === \"accept\") {\n await dialog.accept(promptText);\n } else {\n await dialog.dismiss();\n }\n }\n }\n\n private async waitFor(opts?: any): Promise<void> {\n const page = this.getPage();\n const timeout = opts?.timeout ?? opts?.timeoutMs ?? 30000;\n\n if (opts?.timeSec) {\n await page.waitForTimeout(opts.timeSec * 1000);\n return;\n }\n if (opts?.text) {\n await page.getByText(opts.text).first().waitFor({ state: \"visible\", timeout });\n return;\n }\n if (opts?.textGone) {\n await page.getByText(opts.textGone).first().waitFor({ state: \"hidden\", timeout });\n return;\n }\n if (opts?.selector) {\n await page.locator(opts.selector).waitFor({\n state: opts.state ?? \"visible\",\n timeout,\n });\n }\n }\n\n private async closePage(_opts?: any): Promise<void> {\n await this.getPage().close();\n this.page = null;\n }\n\n private async resize(width: number, height: number, _opts?: any): Promise<void> {\n await this.getPage().setViewportSize({ width, height });\n }\n\n private async tabs(action: string, index?: number, _opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n const pages = this.context.pages();\n\n switch (action) {\n case \"list\":\n return Promise.all(\n pages.map(async (p, i) => ({\n index: i,\n url: p.url(),\n title: await p.title().catch(() => \"\"),\n }))\n );\n case \"new\": {\n const newPage = await this.context.newPage();\n this.page = newPage;\n newPage.on(\"dialog\", (dialog) => this.pendingDialogs.push(dialog));\n return { index: pages.length };\n }\n case \"close\":\n if (index !== undefined && pages[index]) {\n await pages[index].close();\n } else {\n await this.page?.close();\n }\n this.page = this.context.pages()[0] ?? null;\n break;\n case \"select\":\n if (index !== undefined && pages[index]) {\n this.page = pages[index];\n }\n break;\n }\n return null;\n }\n\n private async getStorageState(_opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n return this.context.storageState();\n }\n\n private async getCurrentUrl(_opts?: any): Promise<string> {\n return this.getPage().url();\n }\n\n private async getTitle(_opts?: any): Promise<string> {\n return this.getPage().title();\n }\n\n private async getLinks(_opts?: any): Promise<string[]> {\n const page = this.getPage();\n return page.$$eval(\"a[href]\", (links) =>\n links\n .map((a) => (a as HTMLAnchorElement).href)\n .filter((h): h is string => !!h && (h.startsWith(\"http://\") || h.startsWith(\"https://\")))\n );\n }\n\n private async getElementBoundingBox(\n ref: string,\n _opts?: any\n ): Promise<{ x: number; y: number; width: number; height: number } | null> {\n const locator = this.resolveRef(ref);\n const box = await locator.boundingBox();\n if (!box) return null;\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n private async startTracing(_opts?: any): Promise<void> {\n if (!this.context) throw new Error(\"No context available\");\n await this.context.tracing.start({ screenshots: true, snapshots: true });\n }\n\n private async stopTracing(_opts?: any): Promise<{\n trace: string;\n network: string;\n resources: string;\n directory: string | null;\n legend: string | null;\n }> {\n if (!this.context) throw new Error(\"No context available\");\n const tracePath = `/tmp/trace-${Date.now()}.zip`;\n await this.context.tracing.stop({ path: tracePath });\n return {\n trace: tracePath,\n network: \"\",\n resources: \"\",\n directory: null,\n legend: `Trace saved to ${tracePath}`,\n };\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/local-browser/host.ts"],"sourcesContent":["/**\n * Local Browser Host\n *\n * Manages a local browser instance and handles commands from the cloud API\n * via WebSocket. This enables cloud agents to control a browser running\n * on the user's local machine.\n */\n\nimport { chromium, type Browser, type BrowserContext, type Page, type Dialog } from \"playwright\";\nimport type {\n BrowserCommand,\n BrowserResponse,\n HeartbeatMessage,\n SessionMessage,\n LocalBrowserMode,\n} from \"./protocol\";\n\nconst HEARTBEAT_INTERVAL_MS = 30_000;\nconst RECONNECT_DELAY_MS = 1000;\nconst MAX_RECONNECT_DELAY_MS = 30_000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\n\nexport interface LocalBrowserHostOptions {\n apiUrl: string;\n wsToken: string;\n sessionId: string;\n browserMode: LocalBrowserMode;\n cdpUrl?: string;\n headless?: boolean;\n storageStatePath?: string;\n onLog?: (level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) => void;\n}\n\ninterface PageWithAISnapshot extends Page {\n _snapshotForAI(options: { mode: \"full\" | \"compact\" }): Promise<string>;\n}\n\n/**\n * LocalBrowserHost manages the browser and WebSocket connection to the cloud.\n */\nexport class LocalBrowserHost {\n private options: LocalBrowserHostOptions;\n private ws: WebSocket | null = null;\n private browser: Browser | null = null;\n private context: BrowserContext | null = null;\n private page: Page | null = null;\n private pendingDialogs: Dialog[] = [];\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private reconnectAttempts = 0;\n private isShuttingDown = false;\n private lastSnapshotYaml = \"\";\n\n constructor(options: LocalBrowserHostOptions) {\n this.options = options;\n }\n\n private log(level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) {\n if (this.options.onLog) {\n this.options.onLog(level, message, data);\n } else {\n const fn = level === \"error\" ? console.error : level === \"warn\" ? console.warn : console.log;\n fn(`[LocalBrowserHost] ${message}`, data ?? \"\");\n }\n }\n\n // =========================================================================\n // Lifecycle\n // =========================================================================\n\n async start(): Promise<void> {\n this.log(\"info\", \"Starting local browser host\", {\n browserMode: this.options.browserMode,\n sessionId: this.options.sessionId,\n });\n\n // Connect to WebSocket first\n await this.connectWebSocket();\n\n // Then launch browser\n await this.launchBrowser();\n\n // Notify cloud that browser is ready\n this.sendSessionEvent(\"browser_ready\");\n }\n\n async stop(): Promise<void> {\n this.isShuttingDown = true;\n this.log(\"info\", \"Stopping local browser host\");\n\n this.stopHeartbeat();\n\n if (this.ws) {\n try {\n this.ws.close(1000, \"Shutdown\");\n } catch {}\n this.ws = null;\n }\n\n if (this.context) {\n try {\n await this.context.close();\n } catch {}\n this.context = null;\n }\n\n if (this.browser) {\n try {\n await this.browser.close();\n } catch {}\n this.browser = null;\n }\n\n this.page = null;\n this.log(\"info\", \"Local browser host stopped\");\n }\n\n // =========================================================================\n // WebSocket Connection\n // =========================================================================\n\n private async connectWebSocket(): Promise<void> {\n return new Promise((resolve, reject) => {\n const wsUrl = `${this.options.apiUrl.replace(\"http\", \"ws\")}/local-browser/sessions/${this.options.sessionId}/connect?token=${this.options.wsToken}`;\n\n this.log(\"info\", \"Connecting to cloud API\", { url: wsUrl.replace(/token=.*/, \"token=***\") });\n\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n this.log(\"info\", \"Connected to cloud API\");\n this.ws = ws;\n this.reconnectAttempts = 0;\n this.startHeartbeat();\n resolve();\n };\n\n ws.onmessage = (event) => {\n this.handleMessage(event.data as string);\n };\n\n ws.onerror = (event) => {\n this.log(\"error\", \"WebSocket error\", event);\n };\n\n ws.onclose = () => {\n this.log(\"info\", \"WebSocket closed\");\n this.stopHeartbeat();\n this.ws = null;\n\n if (!this.isShuttingDown) {\n this.scheduleReconnect();\n }\n };\n\n // Timeout after 30 seconds\n setTimeout(() => {\n if (!this.ws) {\n reject(new Error(\"WebSocket connection timeout\"));\n }\n }, 30_000);\n });\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n this.log(\"error\", \"Max reconnection attempts reached, giving up\");\n this.stop();\n return;\n }\n\n const delay = Math.min(\n RECONNECT_DELAY_MS * Math.pow(2, this.reconnectAttempts),\n MAX_RECONNECT_DELAY_MS\n );\n\n this.reconnectAttempts++;\n this.log(\"info\", `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n\n setTimeout(async () => {\n try {\n await this.connectWebSocket();\n this.sendSessionEvent(\"connected\");\n if (this.page) {\n this.sendSessionEvent(\"browser_ready\");\n }\n } catch (error) {\n this.log(\"error\", \"Reconnection failed\", error);\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n // =========================================================================\n // Heartbeat\n // =========================================================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const ping: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws.send(JSON.stringify(ping));\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // =========================================================================\n // Browser Management\n // =========================================================================\n\n private async launchBrowser(): Promise<void> {\n const { browserMode, cdpUrl, headless = true, storageStatePath } = this.options;\n\n if (browserMode === \"cdp\" && cdpUrl) {\n this.log(\"info\", \"Connecting to existing Chrome via CDP\", { cdpUrl });\n this.browser = await chromium.connectOverCDP(cdpUrl);\n const contexts = this.browser.contexts();\n this.context = contexts[0] ?? (await this.browser.newContext());\n const pages = this.context.pages();\n this.page = pages[0] ?? (await this.context.newPage());\n } else {\n this.log(\"info\", \"Launching new Playwright browser\", { headless });\n this.browser = await chromium.launch({\n headless,\n args: [\"--no-sandbox\"],\n });\n\n const contextOptions: { viewport: { width: number; height: number }; storageState?: string } =\n {\n viewport: { width: 1920, height: 1080 },\n };\n\n if (storageStatePath) {\n try {\n await Bun.file(storageStatePath).exists();\n contextOptions.storageState = storageStatePath;\n this.log(\"info\", \"Loading storage state\", { storageStatePath });\n } catch {\n this.log(\"debug\", \"Storage state file not found, starting fresh\");\n }\n }\n\n this.context = await this.browser.newContext(contextOptions);\n this.page = await this.context.newPage();\n }\n\n // Setup dialog handler\n this.page.on(\"dialog\", (dialog) => {\n this.pendingDialogs.push(dialog);\n });\n\n this.log(\"info\", \"Browser ready\");\n }\n\n // =========================================================================\n // Message Handling\n // =========================================================================\n\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data);\n\n if (message.type === \"heartbeat\" && message.direction === \"ping\") {\n // Respond to ping with pong\n const pong: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws?.send(JSON.stringify(pong));\n return;\n }\n\n if (message.type === \"command\") {\n this.handleCommand(message as BrowserCommand);\n return;\n }\n\n this.log(\"debug\", \"Received unknown message type\", message);\n } catch (error) {\n this.log(\"error\", \"Failed to parse message\", { error, data });\n }\n }\n\n private async handleCommand(command: BrowserCommand): Promise<void> {\n const startTime = Date.now();\n this.log(\"debug\", `Executing command: ${command.method}`, { id: command.id });\n\n try {\n const result = await this.executeMethod(command.method, command.args);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: true,\n result,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"debug\", `Command completed: ${command.method}`, {\n id: command.id,\n durationMs: Date.now() - startTime,\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: false,\n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"error\", `Command failed: ${command.method}`, {\n id: command.id,\n error: errorMessage,\n });\n }\n }\n\n private sendSessionEvent(\n event: \"connected\" | \"disconnected\" | \"browser_ready\" | \"browser_closed\" | \"error\",\n error?: string\n ): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n\n const message: SessionMessage = {\n type: \"session\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n event,\n browserMode: this.options.browserMode,\n error,\n };\n this.ws.send(JSON.stringify(message));\n }\n\n // =========================================================================\n // Method Execution\n // =========================================================================\n\n private async executeMethod(method: string, args: unknown[]): Promise<unknown> {\n // Route to appropriate handler\n switch (method) {\n // Lifecycle\n case \"connect\":\n return this.connect(args[0] as any);\n case \"disconnect\":\n return this.disconnect();\n\n // Navigation\n case \"navigate\":\n return this.navigate(args[0] as string, args[1] as any);\n case \"navigateBack\":\n return this.navigateBack(args[0] as any);\n\n // Page Inspection\n case \"snapshot\":\n return this.snapshot(args[0] as any);\n case \"takeScreenshot\":\n return this.takeScreenshot(args[0] as any);\n case \"evaluate\":\n return this.evaluate(args[0] as string, args[1] as any);\n case \"runCode\":\n return this.runCode(args[0] as string, args[1] as any);\n case \"consoleMessages\":\n return this.consoleMessages(args[0] as any);\n case \"networkRequests\":\n return this.networkRequests(args[0] as any);\n\n // Interaction\n case \"click\":\n return this.click(args[0] as string, args[1] as string, args[2] as any);\n case \"clickAtCoordinates\":\n return this.clickAtCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"moveToCoordinates\":\n return this.moveToCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"dragCoordinates\":\n return this.dragCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as number,\n args[3] as number,\n args[4] as string,\n args[5] as any\n );\n case \"hover\":\n return this.hover(args[0] as string, args[1] as string, args[2] as any);\n case \"drag\":\n return this.drag(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as string,\n args[4] as any\n );\n case \"type\":\n return this.type(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as boolean,\n args[4] as any\n );\n case \"pressKey\":\n return this.pressKey(args[0] as string, args[1] as any);\n case \"fillForm\":\n return this.fillForm(args[0] as any[], args[1] as any);\n case \"selectOption\":\n return this.selectOption(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as any\n );\n case \"fileUpload\":\n return this.fileUpload(args[0] as string[], args[1] as any);\n\n // Dialogs\n case \"handleDialog\":\n return this.handleDialog(args[0] as \"accept\" | \"dismiss\", args[1] as string, args[2] as any);\n\n // Waiting\n case \"waitFor\":\n return this.waitFor(args[0] as any);\n\n // Browser Management\n case \"close\":\n return this.closePage(args[0] as any);\n case \"resize\":\n return this.resize(args[0] as number, args[1] as number, args[2] as any);\n case \"tabs\":\n return this.tabs(args[0] as any, args[1] as number, args[2] as any);\n\n // Storage\n case \"getStorageState\":\n return this.getStorageState(args[0] as any);\n case \"getCurrentUrl\":\n return this.getCurrentUrl(args[0] as any);\n case \"getTitle\":\n return this.getTitle(args[0] as any);\n case \"getLinks\":\n return this.getLinks(args[0] as any);\n case \"getElementBoundingBox\":\n return this.getElementBoundingBox(args[0] as string, args[1] as any);\n\n // Tracing\n case \"startTracing\":\n return this.startTracing(args[0] as any);\n case \"stopTracing\":\n return this.stopTracing(args[0] as any);\n\n // Video\n case \"isVideoRecordingEnabled\":\n return false; // Video not supported in CLI host currently\n case \"saveVideo\":\n return null;\n case \"getVideoPath\":\n return null;\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n }\n\n // =========================================================================\n // IBrowserClient Method Implementations\n // =========================================================================\n\n private getPage(): Page {\n if (!this.page) throw new Error(\"No page available\");\n return this.page;\n }\n\n private resolveRef(ref: string) {\n return this.getPage().locator(`aria-ref=${ref}`);\n }\n\n private async connect(_options: any): Promise<void> {\n // Browser already launched in start()\n return;\n }\n\n private async disconnect(): Promise<void> {\n await this.stop();\n }\n\n private async navigate(url: string, _opts?: any): Promise<string> {\n const page = this.getPage();\n await page.goto(url, { waitUntil: \"domcontentloaded\" });\n await page.waitForLoadState(\"load\", { timeout: 5000 }).catch(() => {});\n return this.captureSnapshot();\n }\n\n private async navigateBack(_opts?: any): Promise<string> {\n await this.getPage().goBack();\n return this.captureSnapshot();\n }\n\n private async snapshot(_opts?: any): Promise<string> {\n return this.captureSnapshot();\n }\n\n private async captureSnapshot(): Promise<string> {\n const page = this.getPage() as PageWithAISnapshot;\n this.lastSnapshotYaml = await page._snapshotForAI({ mode: \"full\" });\n return this.lastSnapshotYaml;\n }\n\n private async takeScreenshot(opts?: any): Promise<string | null> {\n const page = this.getPage();\n const buffer = await page.screenshot({\n type: opts?.type ?? \"jpeg\",\n fullPage: opts?.fullPage ?? false,\n });\n const mime = opts?.type === \"png\" ? \"image/png\" : \"image/jpeg\";\n return `data:${mime};base64,${buffer.toString(\"base64\")}`;\n }\n\n private async evaluate<T>(fn: string, _opts?: any): Promise<T> {\n const page = this.getPage();\n return page.evaluate(new Function(`return (${fn})()`) as () => T);\n }\n\n private async runCode(code: string, _opts?: any): Promise<unknown> {\n const page = this.getPage();\n const fn = new Function(\"page\", `return (async () => { ${code} })()`) as (\n p: Page\n ) => Promise<unknown>;\n return fn(page);\n }\n\n private async consoleMessages(_opts?: any): Promise<string> {\n // Console messages would need to be captured over time\n return \"Console message capture not implemented in CLI host\";\n }\n\n private async networkRequests(_opts?: any): Promise<string> {\n // Network requests would need to be captured over time\n return \"Network request capture not implemented in CLI host\";\n }\n\n private async click(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.scrollIntoViewIfNeeded({ timeout: 5000 }).catch(() => {});\n\n const box = await locator.boundingBox();\n if (box) {\n const centerX = box.x + box.width / 2;\n const centerY = box.y + box.height / 2;\n const page = this.getPage();\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.down(mod);\n }\n }\n\n if (opts?.doubleClick) {\n await page.mouse.dblclick(centerX, centerY);\n } else {\n await page.mouse.click(centerX, centerY);\n }\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.up(mod);\n }\n }\n } else {\n // Fallback to locator click\n if (opts?.doubleClick) {\n await locator.dblclick({ timeout: opts?.timeoutMs ?? 30000 });\n } else {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async clickAtCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n opts?: any\n ): Promise<void> {\n const page = this.getPage();\n if (opts?.doubleClick) {\n await page.mouse.dblclick(x, y);\n } else {\n await page.mouse.click(x, y);\n }\n }\n\n private async moveToCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n await this.getPage().mouse.move(x, y);\n }\n\n private async dragCoordinates(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n const page = this.getPage();\n await page.mouse.move(startX, startY);\n await page.mouse.down();\n await page.mouse.move(endX, endY);\n await page.mouse.up();\n }\n\n private async hover(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n await this.resolveRef(ref).hover({ timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async drag(\n startRef: string,\n _startElement: string,\n endRef: string,\n _endElement: string,\n opts?: any\n ): Promise<void> {\n const startLocator = this.resolveRef(startRef);\n const endLocator = this.resolveRef(endRef);\n await startLocator.dragTo(endLocator, { timeout: opts?.timeoutMs ?? 60000 });\n }\n\n private async type(\n ref: string,\n text: string,\n _elementDesc?: string,\n submit?: boolean,\n opts?: any\n ): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.clear();\n await locator.pressSequentially(text, {\n delay: opts?.delay ?? 0,\n timeout: opts?.timeoutMs ?? 30000,\n });\n if (submit) {\n await locator.press(\"Enter\");\n }\n }\n\n private async pressKey(key: string, _opts?: any): Promise<void> {\n await this.getPage().keyboard.press(key);\n }\n\n private async fillForm(fields: any[], opts?: any): Promise<void> {\n for (const field of fields) {\n const locator = this.resolveRef(field.ref);\n const fieldType = field.type ?? \"textbox\";\n\n switch (fieldType) {\n case \"checkbox\": {\n const isChecked = await locator.isChecked();\n const shouldBeChecked = field.value === \"true\";\n if (shouldBeChecked !== isChecked) {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n break;\n }\n case \"radio\":\n await locator.check({ timeout: opts?.timeoutMs ?? 30000 });\n break;\n case \"combobox\":\n await locator.selectOption(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n break;\n default:\n await locator.fill(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async selectOption(\n ref: string,\n value: string,\n _elementDesc?: string,\n opts?: any\n ): Promise<void> {\n await this.resolveRef(ref).selectOption(value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async fileUpload(paths: string[], opts?: any): Promise<void> {\n const fileChooser = await this.getPage().waitForEvent(\"filechooser\", {\n timeout: opts?.timeoutMs ?? 30000,\n });\n await fileChooser.setFiles(paths);\n }\n\n private async handleDialog(\n action: \"accept\" | \"dismiss\",\n promptText?: string,\n _opts?: any\n ): Promise<void> {\n const dialog = this.pendingDialogs.shift();\n if (dialog) {\n if (action === \"accept\") {\n await dialog.accept(promptText);\n } else {\n await dialog.dismiss();\n }\n }\n }\n\n private async waitFor(opts?: any): Promise<void> {\n const page = this.getPage();\n const timeout = opts?.timeout ?? opts?.timeoutMs ?? 30000;\n\n if (opts?.timeSec) {\n await page.waitForTimeout(opts.timeSec * 1000);\n return;\n }\n if (opts?.text) {\n await page.getByText(opts.text).first().waitFor({ state: \"visible\", timeout });\n return;\n }\n if (opts?.textGone) {\n await page.getByText(opts.textGone).first().waitFor({ state: \"hidden\", timeout });\n return;\n }\n if (opts?.selector) {\n await page.locator(opts.selector).waitFor({\n state: opts.state ?? \"visible\",\n timeout,\n });\n }\n }\n\n private async closePage(_opts?: any): Promise<void> {\n await this.getPage().close();\n this.page = null;\n }\n\n private async resize(width: number, height: number, _opts?: any): Promise<void> {\n await this.getPage().setViewportSize({ width, height });\n }\n\n private async tabs(action: string, index?: number, _opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n const pages = this.context.pages();\n\n switch (action) {\n case \"list\":\n return Promise.all(\n pages.map(async (p, i) => ({\n index: i,\n url: p.url(),\n title: await p.title().catch(() => \"\"),\n }))\n );\n case \"new\": {\n const newPage = await this.context.newPage();\n this.page = newPage;\n newPage.on(\"dialog\", (dialog) => this.pendingDialogs.push(dialog));\n return { index: pages.length };\n }\n case \"close\":\n if (index !== undefined && pages[index]) {\n await pages[index].close();\n } else {\n await this.page?.close();\n }\n this.page = this.context.pages()[0] ?? null;\n break;\n case \"select\":\n if (index !== undefined && pages[index]) {\n this.page = pages[index];\n }\n break;\n }\n return null;\n }\n\n private async getStorageState(_opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n return this.context.storageState();\n }\n\n private async getCurrentUrl(_opts?: any): Promise<string> {\n return this.getPage().url();\n }\n\n private async getTitle(_opts?: any): Promise<string> {\n return this.getPage().title();\n }\n\n private async getLinks(_opts?: any): Promise<string[]> {\n const page = this.getPage();\n return page.$$eval(\"a[href]\", (links) =>\n links\n .map((a) => (a as HTMLAnchorElement).href)\n .filter((h): h is string => !!h && (h.startsWith(\"http://\") || h.startsWith(\"https://\")))\n );\n }\n\n private async getElementBoundingBox(\n ref: string,\n _opts?: any\n ): Promise<{ x: number; y: number; width: number; height: number } | null> {\n const locator = this.resolveRef(ref);\n const box = await locator.boundingBox();\n if (!box) return null;\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n private async startTracing(_opts?: any): Promise<void> {\n if (!this.context) throw new Error(\"No context available\");\n await this.context.tracing.start({ screenshots: true, snapshots: true });\n }\n\n private async stopTracing(_opts?: any): Promise<{\n trace: string;\n network: string;\n resources: string;\n directory: string | null;\n legend: string | null;\n }> {\n if (!this.context) throw new Error(\"No context available\");\n const tracePath = `/tmp/trace-${Date.now()}.zip`;\n await this.context.tracing.stop({ path: tracePath });\n return {\n trace: tracePath,\n network: \"\",\n resources: \"\",\n directory: null,\n legend: `Trace saved to ${tracePath}`,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,91 @@
1
+ // src/auth.ts
2
+ import fs from "fs/promises";
3
+ import os from "os";
4
+ import path from "path";
5
+ var AUTH_DIR = path.join(os.homedir(), ".config", "canary-cli");
6
+ var AUTH_FILE = path.join(AUTH_DIR, "auth.json");
7
+ var ENV_URLS = {
8
+ prod: {
9
+ api: "https://api.trycanary.ai",
10
+ app: "https://app.trycanary.ai"
11
+ },
12
+ production: {
13
+ api: "https://api.trycanary.ai",
14
+ app: "https://app.trycanary.ai"
15
+ },
16
+ dev: {
17
+ api: "https://api.dev.trycanary.ai",
18
+ app: "https://app.dev.trycanary.ai"
19
+ },
20
+ local: {
21
+ api: "http://localhost:3000",
22
+ app: "http://localhost:5173"
23
+ }
24
+ };
25
+ function isMultiProfile(data) {
26
+ return typeof data === "object" && data !== null && "profiles" in data;
27
+ }
28
+ async function readAuthFile() {
29
+ try {
30
+ const content = await fs.readFile(AUTH_FILE, "utf8");
31
+ const data = JSON.parse(content);
32
+ if (isMultiProfile(data)) {
33
+ return data;
34
+ }
35
+ const legacy = data;
36
+ return {
37
+ profiles: { default: legacy },
38
+ default: "default"
39
+ };
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ async function readStoredAuth(profile) {
45
+ const auth = await readAuthFile();
46
+ if (!auth) return null;
47
+ const key = profile ?? auth.default;
48
+ return auth.profiles[key] ?? null;
49
+ }
50
+ async function readStoredToken(profile) {
51
+ const auth = await readStoredAuth(profile);
52
+ return auth?.token ?? null;
53
+ }
54
+ async function readStoredApiUrl(profile) {
55
+ const auth = await readStoredAuth(profile);
56
+ return auth?.apiUrl ?? null;
57
+ }
58
+ async function saveAuth(auth, profile) {
59
+ await fs.mkdir(AUTH_DIR, { recursive: true, mode: 448 });
60
+ let existing;
61
+ try {
62
+ const content = await fs.readFile(AUTH_FILE, "utf8");
63
+ const data = JSON.parse(content);
64
+ if (isMultiProfile(data)) {
65
+ existing = data;
66
+ } else {
67
+ existing = {
68
+ profiles: { default: data },
69
+ default: "default"
70
+ };
71
+ }
72
+ } catch {
73
+ existing = { profiles: {}, default: "default" };
74
+ }
75
+ const key = profile ?? "default";
76
+ existing.profiles[key] = auth;
77
+ if (!existing.profiles[existing.default]) {
78
+ existing.default = key;
79
+ }
80
+ await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: "utf8", mode: 384 });
81
+ return AUTH_FILE;
82
+ }
83
+
84
+ export {
85
+ ENV_URLS,
86
+ readStoredAuth,
87
+ readStoredToken,
88
+ readStoredApiUrl,
89
+ saveAuth
90
+ };
91
+ //# sourceMappingURL=chunk-HJ2JWIJ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), \".config\", \"canary-cli\");\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\");\n\n/** Environment URL mappings shared across CLI commands */\nexport const ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === \"object\" && data !== null && \"profiles\" in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: \"default\",\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: \"default\",\n };\n }\n } catch {\n existing = { profiles: {}, default: \"default\" };\n }\n\n const key = profile ?? \"default\";\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return AUTH_FILE;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAkBjB,IAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY;AAChE,IAAM,YAAY,KAAK,KAAK,UAAU,WAAW;AAG1C,IAAM,WAAyD;AAAA,EACpE,MAAM;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,YAAY;AAAA,IACV,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,OAAO;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACF;AAEA,SAAS,eAAe,MAAyC;AAC/D,SAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc;AACpE;AAGA,eAAe,eAAiD;AAC9D,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,aAAO;AAAA,IACT;AAGA,UAAM,SAAS;AACf,WAAO;AAAA,MACL,UAAU,EAAE,SAAS,OAAO;AAAA,MAC5B,SAAS;AAAA,IACX;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eAAe,SAA8C;AACjF,QAAM,OAAO,MAAM,aAAa;AAChC,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,MAAM,WAAW,KAAK;AAC5B,SAAO,KAAK,SAAS,GAAG,KAAK;AAC/B;AAEA,eAAsB,gBAAgB,SAA0C;AAC9E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,SAAS;AACxB;AAEA,eAAsB,iBAAiB,SAA0C;AAC/E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,UAAU;AACzB;AAEA,eAAsB,SAAS,MAAkB,SAAmC;AAClF,QAAM,GAAG,MAAM,UAAU,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAGzD,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,iBAAW;AAAA,IACb,OAAO;AAEL,iBAAW;AAAA,QACT,UAAU,EAAE,SAAS,KAAmB;AAAA,QACxC,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,QAAQ;AACN,eAAW,EAAE,UAAU,CAAC,GAAG,SAAS,UAAU;AAAA,EAChD;AAEA,QAAM,MAAM,WAAW;AACvB,WAAS,SAAS,GAAG,IAAI;AAGzB,MAAI,CAAC,SAAS,SAAS,SAAS,OAAO,GAAG;AACxC,aAAS,UAAU;AAAA,EACrB;AAEA,QAAM,GAAG,UAAU,WAAW,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAClG,SAAO;AACT;","names":[]}
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
1
  // src/runner/config.ts
4
2
  import path2 from "path";
5
3
 
@@ -1080,4 +1078,4 @@ export {
1080
1078
  classifyFailure,
1081
1079
  executeHealActions
1082
1080
  };
1083
- //# sourceMappingURL=chunk-7AP5KRVU.js.map
1081
+ //# sourceMappingURL=chunk-ROTCL5WO.js.map