@canaryai/cli 0.2.2 → 0.2.4

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 (46) hide show
  1. package/dist/bin.js +1 -0
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-DXJNFJ3A.js → chunk-6WWHXWCS.js} +3 -2
  4. package/dist/{chunk-DXJNFJ3A.js.map → chunk-6WWHXWCS.js.map} +1 -1
  5. package/dist/{chunk-HOYYXZPV.js → chunk-DXIAHB72.js} +3 -2
  6. package/dist/{chunk-HOYYXZPV.js.map → chunk-DXIAHB72.js.map} +1 -1
  7. package/dist/{chunk-UEOXNF5X.js → chunk-FIQBGAKW.js} +3 -1
  8. package/dist/{chunk-UEOXNF5X.js.map → chunk-FIQBGAKW.js.map} +1 -1
  9. package/dist/{chunk-TO66FC4R.js → chunk-ILEPYWZX.js} +3 -2
  10. package/dist/{chunk-TO66FC4R.js.map → chunk-ILEPYWZX.js.map} +1 -1
  11. package/dist/{chunk-7R4YFGP6.js → chunk-PWWQGYFG.js} +3 -1
  12. package/dist/{chunk-7R4YFGP6.js.map → chunk-PWWQGYFG.js.map} +1 -1
  13. package/dist/{chunk-DGUM43GV.js → chunk-VLFUCAPZ.js} +2 -1
  14. package/dist/{debug-workflow-EHKNO7BJ.js → debug-workflow-PT3OUR3V.js} +5 -4
  15. package/dist/{debug-workflow-EHKNO7BJ.js.map → debug-workflow-PT3OUR3V.js.map} +1 -1
  16. package/dist/docs-GCYDTEOY.js +270 -0
  17. package/dist/docs-GCYDTEOY.js.map +1 -0
  18. package/dist/{feature-flag-ZDLDYRSF.js → feature-flag-S5B5GLPP.js} +77 -33
  19. package/dist/feature-flag-S5B5GLPP.js.map +1 -0
  20. package/dist/index.js +26 -12
  21. package/dist/index.js.map +1 -1
  22. package/dist/{issues-FI3RIWGV.js → issues-4ZEDHPLW.js} +5 -4
  23. package/dist/{issues-FI3RIWGV.js.map → issues-4ZEDHPLW.js.map} +1 -1
  24. package/dist/{knobs-3MKMOXIV.js → knobs-RKUVK3HC.js} +5 -4
  25. package/dist/{knobs-3MKMOXIV.js.map → knobs-RKUVK3HC.js.map} +1 -1
  26. package/dist/{local-browser-GG5GUXDS.js → local-browser-J6WGFLVD.js} +5 -4
  27. package/dist/{local-browser-GG5GUXDS.js.map → local-browser-J6WGFLVD.js.map} +1 -1
  28. package/dist/mcp-HGYBMDYZ.js +687 -0
  29. package/dist/mcp-HGYBMDYZ.js.map +1 -0
  30. package/dist/{psql-IVAPNYZV.js → psql-WVIHMC6A.js} +4 -3
  31. package/dist/{psql-IVAPNYZV.js.map → psql-WVIHMC6A.js.map} +1 -1
  32. package/dist/record-X4SVNYP3.js +334 -0
  33. package/dist/record-X4SVNYP3.js.map +1 -0
  34. package/dist/{redis-LWY7L6AS.js → redis-RGHECKV5.js} +4 -3
  35. package/dist/{redis-LWY7L6AS.js.map → redis-RGHECKV5.js.map} +1 -1
  36. package/dist/{release-KQFCTAXA.js → release-ZOD4Y2BF.js} +4 -3
  37. package/dist/{release-KQFCTAXA.js.map → release-ZOD4Y2BF.js.map} +1 -1
  38. package/dist/runner/preload.js +3 -2
  39. package/dist/runner/preload.js.map +1 -1
  40. package/dist/test.js +3 -2
  41. package/dist/test.js.map +1 -1
  42. package/package.json +2 -2
  43. package/dist/feature-flag-ZDLDYRSF.js.map +0 -1
  44. package/dist/mcp-AD67OLQM.js +0 -384
  45. package/dist/mcp-AD67OLQM.js.map +0 -1
  46. /package/dist/{chunk-DGUM43GV.js.map → chunk-VLFUCAPZ.js.map} +0 -0
package/dist/bin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
3
 
3
4
  // src/bin.ts
4
5
  import { main } from "./index.js";
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":";;;AAAA,SAAS,YAAY;AAErB,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,6 +1,7 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
1
2
  import {
2
3
  getArgValue
3
- } from "./chunk-7R4YFGP6.js";
4
+ } from "./chunk-PWWQGYFG.js";
4
5
 
5
6
  // src/cli-helpers.ts
6
7
  import process from "process";
@@ -61,4 +62,4 @@ export {
61
62
  apiRequest,
62
63
  fetchList
63
64
  };
64
- //# sourceMappingURL=chunk-DXJNFJ3A.js.map
65
+ //# sourceMappingURL=chunk-6WWHXWCS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport process from \"node:process\";\nimport { getArgValue } from \"./auth.js\";\n\nexport type LifecycleStage = \"active\" | \"deprecated\" | \"ready_for_cleanup\";\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case \"deprecated\":\n return \"deprecated\";\n case \"ready_for_cleanup\":\n return \"ready_for_cleanup\";\n default:\n return \"active\";\n }\n}\n\nexport function parseLifecycleStage(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, \"--stage\");\n if (!stage || ![\"active\", \"deprecated\", \"ready_for_cleanup\"].includes(stage)) {\n console.error(\"Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup\");\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return (await res.json()) as T;\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const res = await fetch(`${apiUrl}${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":";;;;;AAIA,OAAO,aAAa;AAKb,SAAS,iBAAiB,OAA+B;AAC9D,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,oBAAoB,MAAgC;AAClE,QAAM,QAAQ,YAAY,MAAM,SAAS;AACzC,MAAI,CAAC,SAAS,CAAC,CAAC,UAAU,cAAc,mBAAmB,EAAE,SAAS,KAAK,GAAG;AAC5E,YAAQ,MAAM,sFAAsF;AACpG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAsB,WACpB,QACA,OACA,QACA,MACA,MACY;AACZ,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C;AAAA,IACA,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,GAAI,OAAO,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;AAAA,EAC/C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,UACpB,QACA,OACA,MACA,SACc;AACd,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,EAC9C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,UAAU,KAAK,KAAK,EAAE;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAQ,KAAK,OAAO,KAAa,CAAC;AACpC;","names":[]}
1
+ {"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport process from \"node:process\";\nimport { getArgValue } from \"./auth.js\";\n\nexport type LifecycleStage = \"active\" | \"deprecated\" | \"ready_for_cleanup\";\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case \"deprecated\":\n return \"deprecated\";\n case \"ready_for_cleanup\":\n return \"ready_for_cleanup\";\n default:\n return \"active\";\n }\n}\n\nexport function parseLifecycleStage(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, \"--stage\");\n if (!stage || ![\"active\", \"deprecated\", \"ready_for_cleanup\"].includes(stage)) {\n console.error(\"Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup\");\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return (await res.json()) as T;\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const res = await fetch(`${apiUrl}${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":";;;;;;AAIA,OAAO,aAAa;AAKb,SAAS,iBAAiB,OAA+B;AAC9D,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,oBAAoB,MAAgC;AAClE,QAAM,QAAQ,YAAY,MAAM,SAAS;AACzC,MAAI,CAAC,SAAS,CAAC,CAAC,UAAU,cAAc,mBAAmB,EAAE,SAAS,KAAK,GAAG;AAC5E,YAAQ,MAAM,sFAAsF;AACpG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAsB,WACpB,QACA,OACA,QACA,MACA,MACY;AACZ,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C;AAAA,IACA,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,GAAI,OAAO,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;AAAA,EAC/C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,UACpB,QACA,OACA,MACA,SACc;AACd,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,EAC9C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,UAAU,KAAK,KAAK,EAAE;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAQ,KAAK,OAAO,KAAa,CAAC;AACpC;","names":[]}
@@ -1,6 +1,7 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
1
2
  import {
2
3
  readStoredToken
3
- } from "./chunk-7R4YFGP6.js";
4
+ } from "./chunk-PWWQGYFG.js";
4
5
 
5
6
  // src/local-run.ts
6
7
  import process from "process";
@@ -336,4 +337,4 @@ export {
336
337
  createTunnel,
337
338
  connectTunnel
338
339
  };
339
- //# sourceMappingURL=chunk-HOYYXZPV.js.map
340
+ //# sourceMappingURL=chunk-DXIAHB72.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/local-run.ts","../src/tunnel.ts"],"sourcesContent":["import process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\ntype LocalRunOptions = {\n apiUrl: string;\n token?: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n};\n\ntype LocalRunResponse = {\n ok: boolean;\n runId?: string;\n watchUrl?: string;\n error?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nexport async function runLocalTest(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n const title = getArgValue(argv, \"--title\");\n const featureSpec = getArgValue(argv, \"--feature\");\n const startUrl = getArgValue(argv, \"--start-url\");\n const tunnelUrl = getArgValue(argv, \"--tunnel-url\");\n\n if (!tunnelUrl && !startUrl) {\n console.error(\"Missing --tunnel-url or --start-url\");\n process.exit(1);\n }\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const result = await createLocalRun({\n apiUrl,\n token,\n title,\n featureSpec,\n startUrl,\n tunnelUrl,\n });\n\n console.log(`Local test queued: ${result.runId}`);\n if (result.watchUrl) {\n console.log(`Watch: ${result.watchUrl}`);\n }\n}\n\nexport async function createLocalRun(input: {\n apiUrl: string;\n token: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n}): Promise<{ runId: string; watchUrl?: string }> {\n const body = {\n title: input.title ?? null,\n featureSpec: input.featureSpec ?? null,\n startUrl: input.startUrl ?? null,\n tunnelPublicUrl: input.tunnelUrl ?? null,\n };\n\n const response = await fetch(`${input.apiUrl}/local-tests/runs`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify(body),\n });\n\n const json = (await response.json()) as LocalRunResponse;\n if (!response.ok || !json.ok || !json.runId) {\n throw new Error(json.error ?? response.statusText);\n }\n\n return { runId: json.runId, watchUrl: json.watchUrl };\n}\n","import { createHash } from \"node:crypto\";\nimport os from \"node:os\";\nimport process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\nexport type CreateTunnelResponse = {\n ok: boolean;\n tunnelId?: string;\n publicUrl?: string;\n token?: string;\n error?: string;\n};\n\ntype ProxyRequest = {\n type: \"http_request\";\n id: string;\n method: string;\n path: string;\n headers: Record<string, string>;\n bodyBase64?: string | null;\n};\n\ntype ProxyResponse = {\n type: \"http_response\";\n id: string;\n status: number;\n headers?: Record<string, string | string[]>;\n bodyBase64?: string | null;\n};\n\ntype WsOpen = {\n type: \"ws_open\";\n id: string;\n path: string;\n headers: Record<string, string>;\n};\n\ntype WsReady = {\n type: \"ws_ready\";\n id: string;\n};\n\ntype WsMessage = {\n type: \"ws_message\";\n id: string;\n dataBase64: string;\n isBinary: boolean;\n};\n\ntype WsClose = {\n type: \"ws_close\";\n id: string;\n code?: number;\n reason?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nfunction toWebSocketUrl(apiUrl: string): string {\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return url.toString();\n}\n\nfunction createFingerprint(): string {\n const raw = `${os.hostname()}-${os.userInfo().username}-${process.version}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\").slice(0, 16);\n}\n\nexport async function runTunnel(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n const portRaw = getArgValue(argv, \"--port\") ?? process.env.CANARY_LOCAL_PORT;\n\n if (!portRaw) {\n console.error(\"Missing --port\");\n process.exit(1);\n }\n\n const port = Number(portRaw);\n if (Number.isNaN(port) || port <= 0) {\n console.error(\"Invalid --port value\");\n process.exit(1);\n }\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const maxReconnectAttempts = 10;\n const baseReconnectDelayMs = 1000;\n let reconnectAttempts = 0;\n\n const connect = async (): Promise<void> => {\n try {\n const data = await createTunnel({ apiUrl, token, port });\n\n console.log(`Tunnel connected: ${data.publicUrl ?? data.tunnelId}`);\n if (data.publicUrl) {\n console.log(`Public URL: ${data.publicUrl}`);\n console.log(\"\");\n console.log(\"To use this tunnel for sandbox agent callbacks, add to apps/api/.env:\");\n console.log(` SANDBOX_AGENT_API_URL=${data.publicUrl}`);\n console.log(\"\");\n }\n\n const ws = connectTunnel({\n apiUrl,\n tunnelId: data.tunnelId,\n token: data.token,\n port,\n onReady: () => {\n reconnectAttempts = 0;\n },\n });\n\n return new Promise<void>((resolve, reject) => {\n ws.onclose = (event) => {\n console.log(`Tunnel closed (code: ${event.code})`);\n\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n setTimeout(() => {\n connect().then(resolve).catch(reject);\n }, delay);\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error:\", event);\n };\n });\n } catch (error) {\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.error(`Failed to create tunnel: ${error}`);\n console.log(`Retrying in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n return connect();\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n }\n };\n\n await connect();\n}\n\nexport async function createTunnel(input: {\n apiUrl: string;\n token: string;\n port: number;\n}): Promise<{ tunnelId: string; publicUrl?: string; token: string }> {\n const response = await fetch(`${input.apiUrl}/local-tests/tunnels`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify({\n requestedPort: input.port,\n clientFingerprint: createFingerprint(),\n }),\n });\n\n const data = (await response.json()) as CreateTunnelResponse;\n if (!response.ok || !data.ok || !data.tunnelId || !data.token) {\n throw new Error(data.error ?? response.statusText);\n }\n\n return { tunnelId: data.tunnelId, publicUrl: data.publicUrl, token: data.token };\n}\n\n// === Tunnel Message Handlers ===\n\ntype TunnelContext = {\n ws: WebSocket;\n port: number;\n wsConnections: Map<string, WebSocket>;\n wsQueues: Map<string, Array<WsMessage | WsClose>>;\n};\n\nfunction handleHttpRequest(ctx: TunnelContext, request: ProxyRequest): void {\n const targetUrl = `http://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const body = request.bodyBase64 ? Buffer.from(request.bodyBase64, \"base64\") : undefined;\n const headers = { ...request.headers };\n delete headers.host;\n delete headers[\"content-length\"];\n\n fetch(targetUrl, { method: request.method, headers, body: body ?? undefined })\n .then(async (res) => {\n const resBody = await res.arrayBuffer();\n const resHeaders: Record<string, string | string[]> = Object.fromEntries(res.headers.entries());\n delete resHeaders[\"set-cookie\"];\n\n const getSetCookie = (res.headers as Headers & { getSetCookie?: () => string[] }).getSetCookie;\n const setCookieValues = typeof getSetCookie === \"function\" ? getSetCookie.call(res.headers) : [];\n const fallbackSetCookie = res.headers.get(\"set-cookie\");\n if (setCookieValues.length === 0 && fallbackSetCookie) {\n setCookieValues.push(fallbackSetCookie);\n }\n if (setCookieValues.length > 0) {\n resHeaders[\"set-cookie\"] = setCookieValues;\n }\n\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: res.status,\n headers: resHeaders,\n bodyBase64: resBody.byteLength ? Buffer.from(resBody).toString(\"base64\") : null,\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n })\n .catch((error) => {\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: 502,\n headers: { \"content-type\": \"text/plain\" },\n bodyBase64: Buffer.from(`Tunnel error: ${String(error)}`).toString(\"base64\"),\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n });\n}\n\nfunction handleWsOpen(ctx: TunnelContext, request: WsOpen): void {\n const targetUrl = `ws://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const protocolsHeader =\n request.headers[\"sec-websocket-protocol\"] ?? request.headers[\"Sec-WebSocket-Protocol\"];\n const protocols = protocolsHeader\n ? protocolsHeader.split(\",\").map((v) => v.trim()).filter(Boolean)\n : undefined;\n const localWs = new WebSocket(targetUrl, protocols);\n ctx.wsConnections.set(request.id, localWs);\n\n localWs.onopen = () => {\n ctx.ws.send(JSON.stringify({ type: \"ws_ready\", id: request.id } satisfies WsReady));\n const queued = ctx.wsQueues.get(request.id);\n if (queued) {\n for (const message of queued) {\n ctx.ws.send(JSON.stringify(message));\n }\n ctx.wsQueues.delete(request.id);\n }\n };\n\n localWs.onmessage = (event) => {\n const data =\n typeof event.data === \"string\"\n ? Buffer.from(event.data)\n : Buffer.from(event.data as ArrayBuffer);\n const response: WsMessage = {\n type: \"ws_message\",\n id: request.id,\n dataBase64: data.toString(\"base64\"),\n isBinary: typeof event.data !== \"string\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onclose = (event) => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: event.code,\n reason: event.reason,\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onerror = () => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: 1011,\n reason: \"local_ws_error\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n}\n\nfunction handleWsMessage(ctx: TunnelContext, message: WsMessage): void {\n const localWs = ctx.wsConnections.get(message.id);\n const data = Buffer.from(message.dataBase64, \"base64\");\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n if (message.isBinary) {\n localWs.send(data);\n } else {\n localWs.send(data.toString());\n }\n}\n\nfunction handleWsClose(ctx: TunnelContext, message: WsClose): void {\n const localWs = ctx.wsConnections.get(message.id);\n if (!localWs) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n localWs.close(message.code ?? 1000, message.reason ?? \"\");\n ctx.wsConnections.delete(message.id);\n}\n\n// === Connect ===\n\nexport function connectTunnel(input: {\n apiUrl: string;\n tunnelId: string;\n token: string;\n port: number;\n onReady?: () => void;\n}): WebSocket {\n const wsUrl = toWebSocketUrl(\n `${input.apiUrl}/local-tests/tunnels/${input.tunnelId}/connect?token=${input.token}`\n );\n const ws = new WebSocket(wsUrl);\n const ctx: TunnelContext = {\n ws,\n port: input.port,\n wsConnections: new Map(),\n wsQueues: new Map(),\n };\n\n ws.onopen = () => {\n input.onReady?.();\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error\", event);\n };\n\n ws.onclose = () => {\n console.log(\"Tunnel closed\");\n };\n\n ws.onmessage = async (event) => {\n try {\n const raw =\n typeof event.data === \"string\"\n ? event.data\n : Buffer.from(event.data as ArrayBuffer).toString();\n const payload = JSON.parse(raw) as { type: string };\n\n switch (payload.type) {\n case \"http_request\":\n handleHttpRequest(ctx, payload as ProxyRequest);\n break;\n case \"ws_open\":\n handleWsOpen(ctx, payload as WsOpen);\n break;\n case \"ws_message\":\n handleWsMessage(ctx, payload as WsMessage);\n break;\n case \"ws_close\":\n handleWsClose(ctx, payload as WsClose);\n break;\n case \"health_ping\":\n ws.send(JSON.stringify({ type: \"health_pong\" }));\n break;\n }\n } catch (error) {\n console.error(\"Tunnel message error\", error);\n }\n };\n\n return ws;\n}\n"],"mappings":"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","names":["process","getArgValue","process"]}
1
+ {"version":3,"sources":["../src/local-run.ts","../src/tunnel.ts"],"sourcesContent":["import process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\ntype LocalRunOptions = {\n apiUrl: string;\n token?: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n};\n\ntype LocalRunResponse = {\n ok: boolean;\n runId?: string;\n watchUrl?: string;\n error?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nexport async function runLocalTest(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n const title = getArgValue(argv, \"--title\");\n const featureSpec = getArgValue(argv, \"--feature\");\n const startUrl = getArgValue(argv, \"--start-url\");\n const tunnelUrl = getArgValue(argv, \"--tunnel-url\");\n\n if (!tunnelUrl && !startUrl) {\n console.error(\"Missing --tunnel-url or --start-url\");\n process.exit(1);\n }\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const result = await createLocalRun({\n apiUrl,\n token,\n title,\n featureSpec,\n startUrl,\n tunnelUrl,\n });\n\n console.log(`Local test queued: ${result.runId}`);\n if (result.watchUrl) {\n console.log(`Watch: ${result.watchUrl}`);\n }\n}\n\nexport async function createLocalRun(input: {\n apiUrl: string;\n token: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n}): Promise<{ runId: string; watchUrl?: string }> {\n const body = {\n title: input.title ?? null,\n featureSpec: input.featureSpec ?? null,\n startUrl: input.startUrl ?? null,\n tunnelPublicUrl: input.tunnelUrl ?? null,\n };\n\n const response = await fetch(`${input.apiUrl}/local-tests/runs`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify(body),\n });\n\n const json = (await response.json()) as LocalRunResponse;\n if (!response.ok || !json.ok || !json.runId) {\n throw new Error(json.error ?? response.statusText);\n }\n\n return { runId: json.runId, watchUrl: json.watchUrl };\n}\n","import { createHash } from \"node:crypto\";\nimport os from \"node:os\";\nimport process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\nexport type CreateTunnelResponse = {\n ok: boolean;\n tunnelId?: string;\n publicUrl?: string;\n token?: string;\n error?: string;\n};\n\ntype ProxyRequest = {\n type: \"http_request\";\n id: string;\n method: string;\n path: string;\n headers: Record<string, string>;\n bodyBase64?: string | null;\n};\n\ntype ProxyResponse = {\n type: \"http_response\";\n id: string;\n status: number;\n headers?: Record<string, string | string[]>;\n bodyBase64?: string | null;\n};\n\ntype WsOpen = {\n type: \"ws_open\";\n id: string;\n path: string;\n headers: Record<string, string>;\n};\n\ntype WsReady = {\n type: \"ws_ready\";\n id: string;\n};\n\ntype WsMessage = {\n type: \"ws_message\";\n id: string;\n dataBase64: string;\n isBinary: boolean;\n};\n\ntype WsClose = {\n type: \"ws_close\";\n id: string;\n code?: number;\n reason?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nfunction toWebSocketUrl(apiUrl: string): string {\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return url.toString();\n}\n\nfunction createFingerprint(): string {\n const raw = `${os.hostname()}-${os.userInfo().username}-${process.version}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\").slice(0, 16);\n}\n\nexport async function runTunnel(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n const portRaw = getArgValue(argv, \"--port\") ?? process.env.CANARY_LOCAL_PORT;\n\n if (!portRaw) {\n console.error(\"Missing --port\");\n process.exit(1);\n }\n\n const port = Number(portRaw);\n if (Number.isNaN(port) || port <= 0) {\n console.error(\"Invalid --port value\");\n process.exit(1);\n }\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const maxReconnectAttempts = 10;\n const baseReconnectDelayMs = 1000;\n let reconnectAttempts = 0;\n\n const connect = async (): Promise<void> => {\n try {\n const data = await createTunnel({ apiUrl, token, port });\n\n console.log(`Tunnel connected: ${data.publicUrl ?? data.tunnelId}`);\n if (data.publicUrl) {\n console.log(`Public URL: ${data.publicUrl}`);\n console.log(\"\");\n console.log(\"To use this tunnel for sandbox agent callbacks, add to apps/api/.env:\");\n console.log(` SANDBOX_AGENT_API_URL=${data.publicUrl}`);\n console.log(\"\");\n }\n\n const ws = connectTunnel({\n apiUrl,\n tunnelId: data.tunnelId,\n token: data.token,\n port,\n onReady: () => {\n reconnectAttempts = 0;\n },\n });\n\n return new Promise<void>((resolve, reject) => {\n ws.onclose = (event) => {\n console.log(`Tunnel closed (code: ${event.code})`);\n\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n setTimeout(() => {\n connect().then(resolve).catch(reject);\n }, delay);\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error:\", event);\n };\n });\n } catch (error) {\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.error(`Failed to create tunnel: ${error}`);\n console.log(`Retrying in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n return connect();\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n }\n };\n\n await connect();\n}\n\nexport async function createTunnel(input: {\n apiUrl: string;\n token: string;\n port: number;\n}): Promise<{ tunnelId: string; publicUrl?: string; token: string }> {\n const response = await fetch(`${input.apiUrl}/local-tests/tunnels`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify({\n requestedPort: input.port,\n clientFingerprint: createFingerprint(),\n }),\n });\n\n const data = (await response.json()) as CreateTunnelResponse;\n if (!response.ok || !data.ok || !data.tunnelId || !data.token) {\n throw new Error(data.error ?? response.statusText);\n }\n\n return { tunnelId: data.tunnelId, publicUrl: data.publicUrl, token: data.token };\n}\n\n// === Tunnel Message Handlers ===\n\ntype TunnelContext = {\n ws: WebSocket;\n port: number;\n wsConnections: Map<string, WebSocket>;\n wsQueues: Map<string, Array<WsMessage | WsClose>>;\n};\n\nfunction handleHttpRequest(ctx: TunnelContext, request: ProxyRequest): void {\n const targetUrl = `http://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const body = request.bodyBase64 ? Buffer.from(request.bodyBase64, \"base64\") : undefined;\n const headers = { ...request.headers };\n delete headers.host;\n delete headers[\"content-length\"];\n\n fetch(targetUrl, { method: request.method, headers, body: body ?? undefined })\n .then(async (res) => {\n const resBody = await res.arrayBuffer();\n const resHeaders: Record<string, string | string[]> = Object.fromEntries(res.headers.entries());\n delete resHeaders[\"set-cookie\"];\n\n const getSetCookie = (res.headers as Headers & { getSetCookie?: () => string[] }).getSetCookie;\n const setCookieValues = typeof getSetCookie === \"function\" ? getSetCookie.call(res.headers) : [];\n const fallbackSetCookie = res.headers.get(\"set-cookie\");\n if (setCookieValues.length === 0 && fallbackSetCookie) {\n setCookieValues.push(fallbackSetCookie);\n }\n if (setCookieValues.length > 0) {\n resHeaders[\"set-cookie\"] = setCookieValues;\n }\n\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: res.status,\n headers: resHeaders,\n bodyBase64: resBody.byteLength ? Buffer.from(resBody).toString(\"base64\") : null,\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n })\n .catch((error) => {\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: 502,\n headers: { \"content-type\": \"text/plain\" },\n bodyBase64: Buffer.from(`Tunnel error: ${String(error)}`).toString(\"base64\"),\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n });\n}\n\nfunction handleWsOpen(ctx: TunnelContext, request: WsOpen): void {\n const targetUrl = `ws://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const protocolsHeader =\n request.headers[\"sec-websocket-protocol\"] ?? request.headers[\"Sec-WebSocket-Protocol\"];\n const protocols = protocolsHeader\n ? protocolsHeader.split(\",\").map((v) => v.trim()).filter(Boolean)\n : undefined;\n const localWs = new WebSocket(targetUrl, protocols);\n ctx.wsConnections.set(request.id, localWs);\n\n localWs.onopen = () => {\n ctx.ws.send(JSON.stringify({ type: \"ws_ready\", id: request.id } satisfies WsReady));\n const queued = ctx.wsQueues.get(request.id);\n if (queued) {\n for (const message of queued) {\n ctx.ws.send(JSON.stringify(message));\n }\n ctx.wsQueues.delete(request.id);\n }\n };\n\n localWs.onmessage = (event) => {\n const data =\n typeof event.data === \"string\"\n ? Buffer.from(event.data)\n : Buffer.from(event.data as ArrayBuffer);\n const response: WsMessage = {\n type: \"ws_message\",\n id: request.id,\n dataBase64: data.toString(\"base64\"),\n isBinary: typeof event.data !== \"string\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onclose = (event) => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: event.code,\n reason: event.reason,\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onerror = () => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: 1011,\n reason: \"local_ws_error\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n}\n\nfunction handleWsMessage(ctx: TunnelContext, message: WsMessage): void {\n const localWs = ctx.wsConnections.get(message.id);\n const data = Buffer.from(message.dataBase64, \"base64\");\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n if (message.isBinary) {\n localWs.send(data);\n } else {\n localWs.send(data.toString());\n }\n}\n\nfunction handleWsClose(ctx: TunnelContext, message: WsClose): void {\n const localWs = ctx.wsConnections.get(message.id);\n if (!localWs) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n localWs.close(message.code ?? 1000, message.reason ?? \"\");\n ctx.wsConnections.delete(message.id);\n}\n\n// === Connect ===\n\nexport function connectTunnel(input: {\n apiUrl: string;\n tunnelId: string;\n token: string;\n port: number;\n onReady?: () => void;\n}): WebSocket {\n const wsUrl = toWebSocketUrl(\n `${input.apiUrl}/local-tests/tunnels/${input.tunnelId}/connect?token=${input.token}`\n );\n const ws = new WebSocket(wsUrl);\n const ctx: TunnelContext = {\n ws,\n port: input.port,\n wsConnections: new Map(),\n wsQueues: new Map(),\n };\n\n ws.onopen = () => {\n input.onReady?.();\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error\", event);\n };\n\n ws.onclose = () => {\n console.log(\"Tunnel closed\");\n };\n\n ws.onmessage = async (event) => {\n try {\n const raw =\n typeof event.data === \"string\"\n ? event.data\n : Buffer.from(event.data as ArrayBuffer).toString();\n const payload = JSON.parse(raw) as { type: string };\n\n switch (payload.type) {\n case \"http_request\":\n handleHttpRequest(ctx, payload as ProxyRequest);\n break;\n case \"ws_open\":\n handleWsOpen(ctx, payload as WsOpen);\n break;\n case \"ws_message\":\n handleWsMessage(ctx, payload as WsMessage);\n break;\n case \"ws_close\":\n handleWsClose(ctx, payload as WsClose);\n break;\n case \"health_ping\":\n ws.send(JSON.stringify({ type: \"health_pong\" }));\n break;\n }\n } catch (error) {\n console.error(\"Tunnel message error\", error);\n }\n };\n\n return ws;\n}\n"],"mappings":"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","names":["process","getArgValue","process"]}
@@ -1,3 +1,5 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
+
1
3
  // src/local-browser/host.ts
2
4
  import {
3
5
  PlaywrightClient
@@ -368,4 +370,4 @@ var LocalBrowserHost = class {
368
370
  export {
369
371
  LocalBrowserHost
370
372
  };
371
- //# sourceMappingURL=chunk-UEOXNF5X.js.map
373
+ //# sourceMappingURL=chunk-FIQBGAKW.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. Delegates all browser automation to PlaywrightClient from\n * @chatsdet/browser-core, ensuring the agent experience is identical\n * regardless of whether the browser is local or cloud.\n *\n * @module local-browser-host\n */\n\nimport {\n PlaywrightClient,\n type IBrowserClient,\n type BrowserLogger,\n} from \"@chatsdet/browser-core\";\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\n/**\n * LocalBrowserHost manages the WebSocket connection to the cloud API and\n * delegates all browser operations to a shared PlaywrightClient instance.\n */\nexport class LocalBrowserHost {\n private options: LocalBrowserHostOptions;\n private ws: WebSocket | null = null;\n private client: PlaywrightClient;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private reconnectAttempts = 0;\n private isShuttingDown = false;\n\n constructor(options: LocalBrowserHostOptions) {\n this.options = options;\n const logger: BrowserLogger = {\n debug: (msg: string, data?: Record<string, unknown>) => this.log(\"debug\", msg, data),\n info: (msg: string, data?: Record<string, unknown>) => this.log(\"info\", msg, data),\n warn: (msg: string, data?: Record<string, unknown>) => this.log(\"warn\", msg, data),\n error: (msg: string, data?: Record<string, unknown>) => this.log(\"error\", msg, data),\n };\n this.client = new PlaywrightClient({ logger });\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 // Launch browser via PlaywrightClient\n const { browserMode, cdpUrl, headless = true, storageStatePath } = this.options;\n await this.client.connect({\n browserMode: headless ? \"headless\" : \"headed\",\n cdpUrl: browserMode === \"cdp\" ? cdpUrl : undefined,\n storageStatePath,\n });\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 await this.client.disconnect();\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 this.sendSessionEvent(\"browser_ready\");\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 // 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 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 contextId: command.contextId,\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 contextId: command.contextId,\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 — Delegate to PlaywrightClient\n // =========================================================================\n\n /**\n * Maps incoming WebSocket command method names to PlaywrightClient methods.\n * The client implements IBrowserClient, so all standard browser operations\n * are available and behave identically to the cloud environment.\n */\n private async executeMethod(method: string, args: unknown[]): Promise<unknown> {\n const client = this.client as IBrowserClient;\n\n switch (method) {\n // Lifecycle\n case \"connect\":\n return client.connect(args[0] as any);\n case \"disconnect\":\n return client.disconnect();\n\n // Navigation\n case \"navigate\":\n return client.navigate(args[0] as string, args[1] as any);\n case \"navigateBack\":\n return client.navigateBack(args[0] as any);\n\n // Page Inspection\n case \"snapshot\":\n return client.snapshot(args[0] as any);\n case \"takeScreenshot\":\n return client.takeScreenshot(args[0] as any);\n case \"evaluate\":\n return client.evaluate(args[0] as string, args[1] as any);\n case \"runCode\":\n return client.runCode(args[0] as string, args[1] as any);\n case \"consoleMessages\":\n return client.consoleMessages(args[0] as any);\n case \"networkRequests\":\n return client.networkRequests(args[0] as any);\n\n // Interaction\n case \"click\":\n return client.click(args[0] as string, args[1] as string, args[2] as any);\n case \"clickAtCoordinates\":\n return client.clickAtCoordinates(\n args[0] as number, args[1] as number, args[2] as string, args[3] as any\n );\n case \"moveToCoordinates\":\n return client.moveToCoordinates(\n args[0] as number, args[1] as number, args[2] as string, args[3] as any\n );\n case \"dragCoordinates\":\n return client.dragCoordinates(\n args[0] as number, args[1] as number, args[2] as number,\n args[3] as number, args[4] as string, args[5] as any\n );\n case \"hover\":\n return client.hover(args[0] as string, args[1] as string, args[2] as any);\n case \"drag\":\n return client.drag(\n args[0] as string, args[1] as string,\n args[2] as string, args[3] as string, args[4] as any\n );\n case \"type\":\n return client.type(\n args[0] as string, args[1] as string, args[2] as string,\n args[3] as boolean, args[4] as any\n );\n case \"pressKey\":\n return client.pressKey(args[0] as string, args[1] as any);\n case \"fillForm\":\n return client.fillForm(args[0] as any[], args[1] as any);\n case \"selectOption\":\n return client.selectOption(\n args[0] as string, args[1] as string, args[2] as string, args[3] as any\n );\n case \"fileUpload\":\n return client.fileUpload(args[0] as string[], args[1] as any);\n\n // Scroll\n case \"scroll\":\n return client.scroll(args[0] as any, args[1] as any, args[2] as any, args[3] as any, args[4] as any);\n\n // Dialogs\n case \"handleDialog\":\n return client.handleDialog(args[0] as \"accept\" | \"dismiss\", args[1] as string, args[2] as any);\n\n // Waiting\n case \"waitFor\":\n return client.waitFor(args[0] as any);\n\n // Browser Management\n case \"close\":\n return client.close(args[0] as any);\n case \"resize\":\n return client.resize(args[0] as number, args[1] as number, args[2] as any);\n case \"tabs\":\n return client.tabs(args[0] as \"list\" | \"new\" | \"close\" | \"select\", args[1] as number, args[2] as any);\n\n // Context Management\n case \"swapContext\":\n return client.swapContext?.(args[0] as any);\n\n // Storage & Page Info\n case \"getStorageState\":\n return client.getStorageState(args[0] as any);\n case \"getCurrentUrl\":\n return client.getCurrentUrl(args[0] as any);\n case \"getTitle\":\n return client.getTitle(args[0] as any);\n case \"getLinks\":\n return client.getLinks(args[0] as any);\n case \"getElementBoundingBox\":\n return client.getElementBoundingBox(args[0] as string, args[1] as any);\n\n // Tracing\n case \"startTracing\":\n return client.startTracing?.(args[0] as any);\n case \"stopTracing\":\n return client.stopTracing?.(args[0] as any);\n\n // Video\n case \"isVideoRecordingEnabled\":\n return client.isVideoRecordingEnabled?.() ?? false;\n case \"saveVideo\":\n return client.saveVideo?.() ?? null;\n case \"getVideoPath\":\n return null;\n\n // Screencast\n case \"startScreencast\":\n return client.startScreencast?.(args[0] as any, args[1] as any);\n case \"stopScreencast\":\n return client.stopScreencast?.();\n case \"isScreencastActive\":\n return client.isScreencastActive?.() ?? false;\n\n default:\n throw new Error(`Unknown method: ${method}`);\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. Delegates all browser automation to PlaywrightClient from\n * @chatsdet/browser-core, ensuring the agent experience is identical\n * regardless of whether the browser is local or cloud.\n *\n * @module local-browser-host\n */\n\nimport {\n PlaywrightClient,\n type IBrowserClient,\n type BrowserLogger,\n} from \"@chatsdet/browser-core\";\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\n/**\n * LocalBrowserHost manages the WebSocket connection to the cloud API and\n * delegates all browser operations to a shared PlaywrightClient instance.\n */\nexport class LocalBrowserHost {\n private options: LocalBrowserHostOptions;\n private ws: WebSocket | null = null;\n private client: PlaywrightClient;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private reconnectAttempts = 0;\n private isShuttingDown = false;\n\n constructor(options: LocalBrowserHostOptions) {\n this.options = options;\n const logger: BrowserLogger = {\n debug: (msg: string, data?: Record<string, unknown>) => this.log(\"debug\", msg, data),\n info: (msg: string, data?: Record<string, unknown>) => this.log(\"info\", msg, data),\n warn: (msg: string, data?: Record<string, unknown>) => this.log(\"warn\", msg, data),\n error: (msg: string, data?: Record<string, unknown>) => this.log(\"error\", msg, data),\n };\n this.client = new PlaywrightClient({ logger });\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 // Launch browser via PlaywrightClient\n const { browserMode, cdpUrl, headless = true, storageStatePath } = this.options;\n await this.client.connect({\n browserMode: headless ? \"headless\" : \"headed\",\n cdpUrl: browserMode === \"cdp\" ? cdpUrl : undefined,\n storageStatePath,\n });\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 await this.client.disconnect();\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 this.sendSessionEvent(\"browser_ready\");\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 // 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 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 contextId: command.contextId,\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 contextId: command.contextId,\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 — Delegate to PlaywrightClient\n // =========================================================================\n\n /**\n * Maps incoming WebSocket command method names to PlaywrightClient methods.\n * The client implements IBrowserClient, so all standard browser operations\n * are available and behave identically to the cloud environment.\n */\n private async executeMethod(method: string, args: unknown[]): Promise<unknown> {\n const client = this.client as IBrowserClient;\n\n switch (method) {\n // Lifecycle\n case \"connect\":\n return client.connect(args[0] as any);\n case \"disconnect\":\n return client.disconnect();\n\n // Navigation\n case \"navigate\":\n return client.navigate(args[0] as string, args[1] as any);\n case \"navigateBack\":\n return client.navigateBack(args[0] as any);\n\n // Page Inspection\n case \"snapshot\":\n return client.snapshot(args[0] as any);\n case \"takeScreenshot\":\n return client.takeScreenshot(args[0] as any);\n case \"evaluate\":\n return client.evaluate(args[0] as string, args[1] as any);\n case \"runCode\":\n return client.runCode(args[0] as string, args[1] as any);\n case \"consoleMessages\":\n return client.consoleMessages(args[0] as any);\n case \"networkRequests\":\n return client.networkRequests(args[0] as any);\n\n // Interaction\n case \"click\":\n return client.click(args[0] as string, args[1] as string, args[2] as any);\n case \"clickAtCoordinates\":\n return client.clickAtCoordinates(\n args[0] as number, args[1] as number, args[2] as string, args[3] as any\n );\n case \"moveToCoordinates\":\n return client.moveToCoordinates(\n args[0] as number, args[1] as number, args[2] as string, args[3] as any\n );\n case \"dragCoordinates\":\n return client.dragCoordinates(\n args[0] as number, args[1] as number, args[2] as number,\n args[3] as number, args[4] as string, args[5] as any\n );\n case \"hover\":\n return client.hover(args[0] as string, args[1] as string, args[2] as any);\n case \"drag\":\n return client.drag(\n args[0] as string, args[1] as string,\n args[2] as string, args[3] as string, args[4] as any\n );\n case \"type\":\n return client.type(\n args[0] as string, args[1] as string, args[2] as string,\n args[3] as boolean, args[4] as any\n );\n case \"pressKey\":\n return client.pressKey(args[0] as string, args[1] as any);\n case \"fillForm\":\n return client.fillForm(args[0] as any[], args[1] as any);\n case \"selectOption\":\n return client.selectOption(\n args[0] as string, args[1] as string, args[2] as string, args[3] as any\n );\n case \"fileUpload\":\n return client.fileUpload(args[0] as string[], args[1] as any);\n\n // Scroll\n case \"scroll\":\n return client.scroll(args[0] as any, args[1] as any, args[2] as any, args[3] as any, args[4] as any);\n\n // Dialogs\n case \"handleDialog\":\n return client.handleDialog(args[0] as \"accept\" | \"dismiss\", args[1] as string, args[2] as any);\n\n // Waiting\n case \"waitFor\":\n return client.waitFor(args[0] as any);\n\n // Browser Management\n case \"close\":\n return client.close(args[0] as any);\n case \"resize\":\n return client.resize(args[0] as number, args[1] as number, args[2] as any);\n case \"tabs\":\n return client.tabs(args[0] as \"list\" | \"new\" | \"close\" | \"select\", args[1] as number, args[2] as any);\n\n // Context Management\n case \"swapContext\":\n return client.swapContext?.(args[0] as any);\n\n // Storage & Page Info\n case \"getStorageState\":\n return client.getStorageState(args[0] as any);\n case \"getCurrentUrl\":\n return client.getCurrentUrl(args[0] as any);\n case \"getTitle\":\n return client.getTitle(args[0] as any);\n case \"getLinks\":\n return client.getLinks(args[0] as any);\n case \"getElementBoundingBox\":\n return client.getElementBoundingBox(args[0] as string, args[1] as any);\n\n // Tracing\n case \"startTracing\":\n return client.startTracing?.(args[0] as any);\n case \"stopTracing\":\n return client.stopTracing?.(args[0] as any);\n\n // Video\n case \"isVideoRecordingEnabled\":\n return client.isVideoRecordingEnabled?.() ?? false;\n case \"saveVideo\":\n return client.saveVideo?.() ?? null;\n case \"getVideoPath\":\n return null;\n\n // Screencast\n case \"startScreencast\":\n return client.startScreencast?.(args[0] as any, args[1] as any);\n case \"stopScreencast\":\n return client.stopScreencast?.();\n case \"isScreencastActive\":\n return client.isScreencastActive?.() ?? false;\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
1
2
  import {
2
3
  __require
3
- } from "./chunk-DGUM43GV.js";
4
+ } from "./chunk-VLFUCAPZ.js";
4
5
 
5
6
  // src/runner/config.ts
6
7
  import path2 from "path";
@@ -1287,4 +1288,4 @@ export {
1287
1288
  wrapExpect,
1288
1289
  wrapPage
1289
1290
  };
1290
- //# sourceMappingURL=chunk-TO66FC4R.js.map
1291
+ //# sourceMappingURL=chunk-ILEPYWZX.js.map