@browserbasehq/orca 3.1.0-patch.2 → 3.1.0-patch.3

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 (107) hide show
  1. package/dist/cjs/cli.js +45 -17
  2. package/dist/cjs/cli.js.map +2 -2
  3. package/dist/cjs/index.js +192 -123
  4. package/dist/cjs/index.js.map +3 -3
  5. package/dist/cjs/lib/logger.d.ts +1 -1
  6. package/dist/cjs/lib/modelUtils.d.ts +3 -0
  7. package/dist/cjs/lib/v3/agent/tools/act.d.ts +2 -1
  8. package/dist/cjs/lib/v3/agent/tools/extract.d.ts +2 -1
  9. package/dist/cjs/lib/v3/agent/tools/fillform.d.ts +2 -1
  10. package/dist/cjs/lib/v3/agent/tools/index.d.ts +2 -2
  11. package/dist/cjs/lib/v3/api.d.ts +16 -1
  12. package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.d.ts +0 -3
  13. package/dist/cjs/lib/v3/handlers/v3AgentHandler.d.ts +2 -2
  14. package/dist/cjs/lib/v3/tests/agent-callbacks.spec.js +12 -12
  15. package/dist/cjs/lib/v3/tests/agent-callbacks.spec.js.map +1 -1
  16. package/dist/cjs/lib/v3/tests/context-addInitScript.spec.js +2 -2
  17. package/dist/cjs/lib/v3/tests/context-addInitScript.spec.js.map +1 -1
  18. package/dist/cjs/lib/v3/tests/locator-content-methods.spec.js +1 -0
  19. package/dist/cjs/lib/v3/tests/locator-content-methods.spec.js.map +1 -1
  20. package/dist/cjs/lib/v3/tests/locator-fill.spec.js +1 -0
  21. package/dist/cjs/lib/v3/tests/locator-fill.spec.js.map +1 -1
  22. package/dist/cjs/lib/v3/tests/locator-input-methods.spec.js +1 -0
  23. package/dist/cjs/lib/v3/tests/locator-input-methods.spec.js.map +1 -1
  24. package/dist/cjs/lib/v3/tests/locator-select-option.spec.js +1 -0
  25. package/dist/cjs/lib/v3/tests/locator-select-option.spec.js.map +1 -1
  26. package/dist/cjs/lib/v3/tests/page-addInitScript.spec.js +2 -2
  27. package/dist/cjs/lib/v3/tests/page-addInitScript.spec.js.map +1 -1
  28. package/dist/cjs/lib/v3/types/public/api.d.ts +8 -0
  29. package/dist/cjs/lib/v3/types/public/index.d.ts +1 -0
  30. package/dist/cjs/lib/v3/types/public/sdkErrors.d.ts +3 -0
  31. package/dist/cjs/tests/agent-execution-model.test.js +139 -0
  32. package/dist/cjs/tests/agent-execution-model.test.js.map +7 -0
  33. package/dist/cjs/tests/api-multiregion.test.js +73 -0
  34. package/dist/cjs/tests/api-multiregion.test.js.map +7 -0
  35. package/dist/cjs/tests/model-utils.test.js +43 -0
  36. package/dist/cjs/tests/model-utils.test.js.map +7 -0
  37. package/dist/cjs/tests/public-api/export-surface.test.js +1 -0
  38. package/dist/cjs/tests/public-api/export-surface.test.js.map +1 -1
  39. package/dist/cjs/tests/public-api/llm-and-agents.test.js +1 -0
  40. package/dist/cjs/tests/public-api/llm-and-agents.test.js.map +1 -1
  41. package/dist/cjs/tests/public-api/public-error-types.test.js +3 -1
  42. package/dist/cjs/tests/public-api/public-error-types.test.js.map +2 -2
  43. package/dist/cjs/tests/public-api/v3-core.test.js +1 -0
  44. package/dist/cjs/tests/public-api/v3-core.test.js.map +1 -1
  45. package/dist/cjs/tests/snapshot-capture-orchestration.test.js +2 -0
  46. package/dist/cjs/tests/snapshot-capture-orchestration.test.js.map +1 -1
  47. package/dist/cjs/tests/understudy-command-exception.test.js +52 -0
  48. package/dist/cjs/tests/understudy-command-exception.test.js.map +7 -0
  49. package/dist/esm/lib/logger.d.ts +1 -1
  50. package/dist/esm/lib/v3/cli.d.ts +2 -0
  51. package/dist/esm/lib/v3/cli.js +10 -0
  52. package/dist/esm/lib/v3/cli.js.map +1 -0
  53. package/dist/esm/lib/v3/dom/build/rerender-index.d.ts +0 -0
  54. package/dist/esm/lib/v3/dom/build/rerender-index.js.map +1 -0
  55. package/dist/esm/lib/v3/dom/build/v3-index.d.ts +0 -0
  56. package/dist/esm/lib/v3/dom/build/v3-index.js.map +1 -0
  57. package/dist/esm/lib/v3/index.d.ts +1 -0
  58. package/dist/esm/lib/v3/index.js +1 -0
  59. package/dist/esm/lib/v3/index.js.map +1 -1
  60. package/dist/esm/lib/v3/shutdown/supervisor.d.ts +7 -5
  61. package/dist/esm/lib/v3/shutdown/supervisor.js +93 -64
  62. package/dist/esm/lib/v3/shutdown/supervisor.js.map +1 -1
  63. package/dist/esm/lib/v3/shutdown/supervisorClient.js +49 -52
  64. package/dist/esm/lib/v3/shutdown/supervisorClient.js.map +1 -1
  65. package/dist/esm/lib/v3/tests/agent-callbacks.spec.js +12 -12
  66. package/dist/esm/lib/v3/tests/agent-callbacks.spec.js.map +1 -1
  67. package/dist/esm/lib/v3/tests/click-count.spec.js +47 -12
  68. package/dist/esm/lib/v3/tests/click-count.spec.js.map +2 -2
  69. package/dist/esm/lib/v3/tests/context-addInitScript.spec.js +2 -2
  70. package/dist/esm/lib/v3/tests/context-addInitScript.spec.js.map +1 -1
  71. package/dist/esm/lib/v3/tests/iframe-ctx-addInitScript.spec.js +67 -21
  72. package/dist/esm/lib/v3/tests/iframe-ctx-addInitScript.spec.js.map +2 -2
  73. package/dist/esm/lib/v3/tests/locator-content-methods.spec.js +1 -0
  74. package/dist/esm/lib/v3/tests/locator-content-methods.spec.js.map +1 -1
  75. package/dist/esm/lib/v3/tests/locator-fill.spec.js +1 -0
  76. package/dist/esm/lib/v3/tests/locator-fill.spec.js.map +1 -1
  77. package/dist/esm/lib/v3/tests/locator-input-methods.spec.js +1 -0
  78. package/dist/esm/lib/v3/tests/locator-input-methods.spec.js.map +1 -1
  79. package/dist/esm/lib/v3/tests/locator-select-option.spec.js +1 -0
  80. package/dist/esm/lib/v3/tests/locator-select-option.spec.js.map +1 -1
  81. package/dist/esm/lib/v3/tests/page-addInitScript.spec.js +2 -2
  82. package/dist/esm/lib/v3/tests/page-addInitScript.spec.js.map +1 -1
  83. package/dist/esm/lib/v3/tests/v3.playwright.config.js +3 -60
  84. package/dist/esm/lib/v3/tests/v3.playwright.config.js.map +2 -2
  85. package/dist/esm/lib/v3/types/private/shutdown.d.ts +1 -13
  86. package/dist/esm/lib/v3/types/private/shutdown.js.map +1 -1
  87. package/dist/esm/lib/v3/understudy/context.js +10 -1
  88. package/dist/esm/lib/v3/understudy/context.js.map +1 -1
  89. package/dist/esm/lib/v3/understudy/locator.js +2 -2
  90. package/dist/esm/lib/v3/understudy/locator.js.map +1 -1
  91. package/dist/esm/lib/v3/understudy/page.js +2 -1
  92. package/dist/esm/lib/v3/understudy/page.js.map +1 -1
  93. package/dist/esm/lib/v3/v3.js +2 -6
  94. package/dist/esm/lib/v3/v3.js.map +1 -1
  95. package/dist/esm/tests/public-api/export-surface.test.js +2 -0
  96. package/dist/esm/tests/public-api/export-surface.test.js.map +2 -2
  97. package/dist/esm/tests/public-api/llm-and-agents.test.js +1 -0
  98. package/dist/esm/tests/public-api/llm-and-agents.test.js.map +1 -1
  99. package/dist/esm/tests/public-api/public-error-types.test.js +1 -0
  100. package/dist/esm/tests/public-api/public-error-types.test.js.map +1 -1
  101. package/dist/esm/tests/public-api/v3-core.test.js +1 -0
  102. package/dist/esm/tests/public-api/v3-core.test.js.map +1 -1
  103. package/dist/esm/tests/snapshot-capture-orchestration.test.js +2 -0
  104. package/dist/esm/tests/snapshot-capture-orchestration.test.js.map +1 -1
  105. package/package.json +13 -11
  106. package/dist/esm/lib/v3/tests/envReporter.js +0 -57
  107. package/dist/esm/lib/v3/tests/envReporter.js.map +0 -7
package/dist/cjs/cli.js CHANGED
@@ -52,6 +52,7 @@ var PID_POLL_INTERVAL_MS = 500;
52
52
  var config = null;
53
53
  var cleanupPromise = null;
54
54
  var started = false;
55
+ var localPidKnownGone = false;
55
56
  var exit = (code = 0) => {
56
57
  try {
57
58
  process.exit(code);
@@ -91,26 +92,43 @@ var startPidPolling = (pid) => {
91
92
  pidPollTimer = setInterval(() => {
92
93
  try {
93
94
  process.kill(pid, 0);
94
- } catch {
95
- if (pidPollTimer) {
96
- clearInterval(pidPollTimer);
97
- pidPollTimer = null;
98
- }
99
- void runCleanup().finally(() => exit(0));
95
+ return;
96
+ } catch (error) {
97
+ const err = error;
98
+ if (err.code !== "ESRCH") return;
99
+ }
100
+ localPidKnownGone = true;
101
+ if (pidPollTimer) {
102
+ clearInterval(pidPollTimer);
103
+ pidPollTimer = null;
100
104
  }
105
+ void runCleanup("Browser process exited").finally(() => exit(0));
101
106
  }, PID_POLL_INTERVAL_MS);
102
107
  };
103
- var cleanupLocal = async (cfg) => {
108
+ var cleanupLocal = async (cfg, reason) => {
109
+ const deletingUserDataDir = Boolean(
110
+ cfg.createdTempProfile && !cfg.preserveUserDataDir && cfg.userDataDir
111
+ );
104
112
  await cleanupLocalBrowser({
105
- killChrome: cfg.pid ? () => politeKill(cfg.pid) : void 0,
113
+ // If polling already observed ESRCH, avoid a follow-up PID kill.
114
+ // The PID could be reused by a different process before cleanup runs.
115
+ killChrome: cfg.pid && !localPidKnownGone ? () => {
116
+ console.error(
117
+ `[shutdown-supervisor] Shutting down Chrome pid=${cfg.pid} (reason=${reason}, deletingUserDataDir=${deletingUserDataDir})`
118
+ );
119
+ return politeKill(cfg.pid);
120
+ } : void 0,
106
121
  userDataDir: cfg.userDataDir,
107
122
  createdTempProfile: cfg.createdTempProfile,
108
123
  preserveUserDataDir: cfg.preserveUserDataDir
109
124
  });
110
125
  };
111
- var cleanupBrowserbase = async (cfg) => {
126
+ var cleanupBrowserbase = async (cfg, reason) => {
112
127
  if (!cfg.apiKey || !cfg.projectId || !cfg.sessionId) return;
113
128
  try {
129
+ console.error(
130
+ `[shutdown-supervisor] Ending Browserbase session ${cfg.sessionId} (reason=${reason})`
131
+ );
114
132
  const bb = new import_sdk.default({ apiKey: cfg.apiKey });
115
133
  await bb.sessions.update(cfg.sessionId, {
116
134
  status: "REQUEST_RELEASE",
@@ -119,17 +137,17 @@ var cleanupBrowserbase = async (cfg) => {
119
137
  } catch {
120
138
  }
121
139
  };
122
- var runCleanup = () => {
140
+ var runCleanup = (reason) => {
123
141
  if (!cleanupPromise) {
124
142
  cleanupPromise = (async () => {
125
143
  const cfg = config;
126
144
  if (!cfg) return;
127
145
  if (cfg.kind === "LOCAL") {
128
- await cleanupLocal(cfg);
146
+ await cleanupLocal(cfg, reason);
129
147
  return;
130
148
  }
131
149
  if (cfg.kind === "STAGEHAND_API") {
132
- await cleanupBrowserbase(cfg);
150
+ await cleanupBrowserbase(cfg, reason);
133
151
  }
134
152
  })();
135
153
  }
@@ -137,12 +155,13 @@ var runCleanup = () => {
137
155
  };
138
156
  var applyConfig = (nextConfig) => {
139
157
  config = nextConfig;
158
+ localPidKnownGone = false;
140
159
  if (config.kind === "LOCAL" && config.pid) {
141
160
  startPidPolling(config.pid);
142
161
  }
143
162
  };
144
- var onLifelineClosed = () => {
145
- void runCleanup().finally(() => exit(0));
163
+ var onLifelineClosed = (reason) => {
164
+ void runCleanup(reason).finally(() => exit(0));
146
165
  };
147
166
  var parseConfigFromArgv = (argv = process.argv.slice(2)) => {
148
167
  const prefix = "--supervisor-config=";
@@ -160,9 +179,18 @@ var runShutdownSupervisor = (initialConfig) => {
160
179
  applyConfig(initialConfig);
161
180
  try {
162
181
  process.stdin.resume();
163
- process.stdin.on("end", onLifelineClosed);
164
- process.stdin.on("close", onLifelineClosed);
165
- process.stdin.on("error", onLifelineClosed);
182
+ process.stdin.on(
183
+ "end",
184
+ () => onLifelineClosed("Stagehand process completed")
185
+ );
186
+ process.stdin.on(
187
+ "close",
188
+ () => onLifelineClosed("Stagehand process completed")
189
+ );
190
+ process.stdin.on(
191
+ "error",
192
+ () => onLifelineClosed("Stagehand process crashed or was killed")
193
+ );
166
194
  } catch {
167
195
  }
168
196
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../lib/v3/cli.js", "../../lib/v3/shutdown/supervisor.ts", "../../lib/v3/shutdown/cleanupLocal.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\nimport process from \"node:process\";\nimport { maybeRunShutdownSupervisorFromArgv } from \"./shutdown/supervisor.js\";\n\n// currently the CLI is only used to spawn the shutdown supervisor\n// in the future, we may want to add more CLI commands here\nif (!maybeRunShutdownSupervisorFromArgv(process.argv.slice(2))) {\n console.error(\n \"Unsupported stagehand CLI invocation. Expected --supervisor with valid args.\",\n );\n process.exit(1);\n}\n", "/**\n * Shutdown supervisor process.\n *\n * This process watches a stdin lifeline. When the parent dies, stdin closes\n * and the supervisor performs best-effort cleanup:\n * - LOCAL: kill Chrome + remove temp profile\n * - STAGEHAND_API: request session release\n */\n\nimport Browserbase from \"@browserbasehq/sdk\";\nimport type { ShutdownSupervisorConfig } from \"../types/private/shutdown.js\";\nimport { cleanupLocalBrowser } from \"./cleanupLocal.js\";\n\nconst SIGKILL_POLL_MS = 250;\nconst SIGKILL_TIMEOUT_MS = 7_000;\nconst PID_POLL_INTERVAL_MS = 500;\n\n// `cleanupPromise` guarantees we execute cleanup at most once.\nlet config: ShutdownSupervisorConfig | null = null;\nlet cleanupPromise: Promise<void> | null = null;\nlet started = false;\n\nconst exit = (code = 0): void => {\n try {\n process.exit(code);\n } catch {\n // ignore\n }\n};\n\n// Best-effort two-phase kill: SIGTERM first, then SIGKILL after timeout.\n// Treat only ESRCH as \"already gone\"; other errors should not imply dead.\nconst politeKill = async (pid: number): Promise<void> => {\n const isAlive = (): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n // ESRCH = \"No such process\" (PID is already gone).\n return err.code !== \"ESRCH\";\n }\n };\n\n if (!isAlive()) return;\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n // ESRCH = process already exited; no further action needed.\n if (err.code === \"ESRCH\") return;\n }\n\n const deadline = Date.now() + SIGKILL_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, SIGKILL_POLL_MS));\n if (!isAlive()) return;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // best-effort\n }\n};\n\nlet pidPollTimer: NodeJS.Timeout | null = null;\n\n// Local-only fallback: if Chrome dies while parent still lives, run cleanup and exit.\nconst startPidPolling = (pid: number): void => {\n if (pidPollTimer) return;\n pidPollTimer = setInterval(() => {\n try {\n process.kill(pid, 0);\n } catch {\n if (pidPollTimer) {\n clearInterval(pidPollTimer);\n pidPollTimer = null;\n }\n void runCleanup().finally(() => exit(0));\n }\n }, PID_POLL_INTERVAL_MS);\n};\n\nconst cleanupLocal = async (\n cfg: Extract<ShutdownSupervisorConfig, { kind: \"LOCAL\" }>,\n) => {\n await cleanupLocalBrowser({\n killChrome: cfg.pid ? () => politeKill(cfg.pid) : undefined,\n userDataDir: cfg.userDataDir,\n createdTempProfile: cfg.createdTempProfile,\n preserveUserDataDir: cfg.preserveUserDataDir,\n });\n};\n\nconst cleanupBrowserbase = async (\n cfg: Extract<ShutdownSupervisorConfig, { kind: \"STAGEHAND_API\" }>,\n) => {\n if (!cfg.apiKey || !cfg.projectId || !cfg.sessionId) return;\n try {\n const bb = new Browserbase({ apiKey: cfg.apiKey });\n await bb.sessions.update(cfg.sessionId, {\n status: \"REQUEST_RELEASE\",\n projectId: cfg.projectId,\n });\n } catch {\n // best-effort cleanup\n }\n};\n\n// Idempotent cleanup entrypoint used by all supervisor shutdown paths.\nconst runCleanup = (): Promise<void> => {\n if (!cleanupPromise) {\n cleanupPromise = (async () => {\n const cfg = config;\n if (!cfg) return;\n if (cfg.kind === \"LOCAL\") {\n await cleanupLocal(cfg);\n return;\n }\n if (cfg.kind === \"STAGEHAND_API\") {\n await cleanupBrowserbase(cfg);\n }\n })();\n }\n return cleanupPromise;\n};\n\nconst applyConfig = (nextConfig: ShutdownSupervisorConfig): void => {\n config = nextConfig;\n if (config.kind === \"LOCAL\" && config.pid) {\n startPidPolling(config.pid);\n }\n};\n\nconst onLifelineClosed = () => {\n void runCleanup().finally(() => exit(0));\n};\n\nconst parseConfigFromArgv = (\n argv: readonly string[] = process.argv.slice(2),\n): ShutdownSupervisorConfig | null => {\n const prefix = \"--supervisor-config=\";\n const raw = argv.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);\n if (!argv.includes(\"--supervisor\") || !raw) return null;\n try {\n return JSON.parse(raw) as ShutdownSupervisorConfig;\n } catch {\n return null;\n }\n};\n\nexport const runShutdownSupervisor = (\n initialConfig: ShutdownSupervisorConfig,\n): void => {\n if (started) return;\n started = true;\n applyConfig(initialConfig);\n\n // Stdin is the lifeline; losing it means parent is gone.\n try {\n process.stdin.resume();\n process.stdin.on(\"end\", onLifelineClosed);\n process.stdin.on(\"close\", onLifelineClosed);\n process.stdin.on(\"error\", onLifelineClosed);\n } catch {\n // ignore\n }\n};\n\nexport const maybeRunShutdownSupervisorFromArgv = (\n argv: readonly string[] = process.argv.slice(2),\n): boolean => {\n const parsed = parseConfigFromArgv(argv);\n if (!parsed) return false;\n runShutdownSupervisor(parsed);\n return true;\n};\n", "import fs from \"node:fs\";\n\n/**\n * Shared cleanup logic for locally launched Chrome.\n *\n * Used by both `V3.close()` (normal shutdown) and the supervisor process\n * (crash cleanup). The caller provides a `killChrome` callback since the\n * kill mechanism differs: chrome-launcher's `chrome.kill()` in-process\n * vs raw `process.kill(pid)` from the supervisor.\n */\nexport async function cleanupLocalBrowser(opts: {\n killChrome?: () => Promise<void> | void;\n userDataDir?: string;\n createdTempProfile?: boolean;\n preserveUserDataDir?: boolean;\n}): Promise<void> {\n if (opts.killChrome) {\n try {\n await opts.killChrome();\n } catch {\n // best-effort\n }\n }\n if (\n opts.createdTempProfile &&\n !opts.preserveUserDataDir &&\n opts.userDataDir\n ) {\n try {\n fs.rmSync(opts.userDataDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport process from \"node:process\";\nimport { maybeRunShutdownSupervisorFromArgv } from \"./shutdown/supervisor.js\";\n\n// currently the CLI is only used to spawn the shutdown supervisor\n// in the future, we may want to add more CLI commands here\nif (!maybeRunShutdownSupervisorFromArgv(process.argv.slice(2))) {\n console.error(\n \"Unsupported stagehand CLI invocation. Expected --supervisor with valid args.\",\n );\n process.exit(1);\n}\n", "/**\n * Shutdown supervisor process.\n *\n * This process watches a stdin lifeline. When the parent dies, stdin closes\n * and the supervisor performs best-effort cleanup:\n * - LOCAL: kill Chrome + remove temp profile\n * - STAGEHAND_API: request session release\n */\n\nimport Browserbase from \"@browserbasehq/sdk\";\nimport type { ShutdownSupervisorConfig } from \"../types/private/shutdown.js\";\nimport { cleanupLocalBrowser } from \"./cleanupLocal.js\";\n\nconst SIGKILL_POLL_MS = 250;\nconst SIGKILL_TIMEOUT_MS = 7_000;\nconst PID_POLL_INTERVAL_MS = 500;\n\n// `cleanupPromise` guarantees we execute cleanup at most once.\nlet config: ShutdownSupervisorConfig | null = null;\nlet cleanupPromise: Promise<void> | null = null;\nlet started = false;\nlet localPidKnownGone = false;\n\nconst exit = (code = 0): void => {\n try {\n process.exit(code);\n } catch {\n // ignore\n }\n};\n\n// Best-effort two-phase kill: SIGTERM first, then SIGKILL after timeout.\n// Treat only ESRCH as \"already gone\"; other errors should not imply dead.\nconst politeKill = async (pid: number): Promise<void> => {\n const isAlive = (): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n // ESRCH = \"No such process\" (PID is already gone).\n return err.code !== \"ESRCH\";\n }\n };\n\n if (!isAlive()) return;\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n // ESRCH = process already exited; no further action needed.\n if (err.code === \"ESRCH\") return;\n }\n\n const deadline = Date.now() + SIGKILL_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, SIGKILL_POLL_MS));\n if (!isAlive()) return;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // best-effort\n }\n};\n\nlet pidPollTimer: NodeJS.Timeout | null = null;\n\n// Local-only fallback: if Chrome dies while parent still lives, run cleanup and exit.\nconst startPidPolling = (pid: number): void => {\n if (pidPollTimer) return;\n pidPollTimer = setInterval(() => {\n try {\n process.kill(pid, 0);\n return;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n // Only ESRCH means the process is definitely gone.\n if (err.code !== \"ESRCH\") return;\n }\n\n localPidKnownGone = true;\n if (pidPollTimer) {\n clearInterval(pidPollTimer);\n pidPollTimer = null;\n }\n void runCleanup(\"Browser process exited\").finally(() => exit(0));\n }, PID_POLL_INTERVAL_MS);\n};\n\nconst cleanupLocal = async (\n cfg: Extract<ShutdownSupervisorConfig, { kind: \"LOCAL\" }>,\n reason: string,\n) => {\n const deletingUserDataDir = Boolean(\n cfg.createdTempProfile && !cfg.preserveUserDataDir && cfg.userDataDir,\n );\n await cleanupLocalBrowser({\n // If polling already observed ESRCH, avoid a follow-up PID kill.\n // The PID could be reused by a different process before cleanup runs.\n killChrome:\n cfg.pid && !localPidKnownGone\n ? () => {\n console.error(\n `[shutdown-supervisor] Shutting down Chrome pid=${cfg.pid} ` +\n `(reason=${reason}, deletingUserDataDir=${deletingUserDataDir})`,\n );\n return politeKill(cfg.pid);\n }\n : undefined,\n userDataDir: cfg.userDataDir,\n createdTempProfile: cfg.createdTempProfile,\n preserveUserDataDir: cfg.preserveUserDataDir,\n });\n};\n\nconst cleanupBrowserbase = async (\n cfg: Extract<ShutdownSupervisorConfig, { kind: \"STAGEHAND_API\" }>,\n reason: string,\n) => {\n if (!cfg.apiKey || !cfg.projectId || !cfg.sessionId) return;\n try {\n console.error(\n `[shutdown-supervisor] Ending Browserbase session ${cfg.sessionId} ` +\n `(reason=${reason})`,\n );\n const bb = new Browserbase({ apiKey: cfg.apiKey });\n await bb.sessions.update(cfg.sessionId, {\n status: \"REQUEST_RELEASE\",\n projectId: cfg.projectId,\n });\n } catch {\n // best-effort cleanup\n }\n};\n\n// Idempotent cleanup entrypoint used by all supervisor shutdown paths.\nconst runCleanup = (reason: string): Promise<void> => {\n if (!cleanupPromise) {\n cleanupPromise = (async () => {\n const cfg = config;\n if (!cfg) return;\n if (cfg.kind === \"LOCAL\") {\n await cleanupLocal(cfg, reason);\n return;\n }\n if (cfg.kind === \"STAGEHAND_API\") {\n await cleanupBrowserbase(cfg, reason);\n }\n })();\n }\n return cleanupPromise;\n};\n\nconst applyConfig = (nextConfig: ShutdownSupervisorConfig): void => {\n config = nextConfig;\n localPidKnownGone = false;\n if (config.kind === \"LOCAL\" && config.pid) {\n startPidPolling(config.pid);\n }\n};\n\nconst onLifelineClosed = (reason: string) => {\n void runCleanup(reason).finally(() => exit(0));\n};\n\nconst parseConfigFromArgv = (\n argv: readonly string[] = process.argv.slice(2),\n): ShutdownSupervisorConfig | null => {\n const prefix = \"--supervisor-config=\";\n const raw = argv.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);\n if (!argv.includes(\"--supervisor\") || !raw) return null;\n try {\n return JSON.parse(raw) as ShutdownSupervisorConfig;\n } catch {\n return null;\n }\n};\n\nexport const runShutdownSupervisor = (\n initialConfig: ShutdownSupervisorConfig,\n): void => {\n if (started) return;\n started = true;\n applyConfig(initialConfig);\n\n // Stdin is the lifeline; losing it means parent is gone.\n try {\n process.stdin.resume();\n process.stdin.on(\"end\", () =>\n onLifelineClosed(\"Stagehand process completed\"),\n );\n process.stdin.on(\"close\", () =>\n onLifelineClosed(\"Stagehand process completed\"),\n );\n process.stdin.on(\"error\", () =>\n onLifelineClosed(\"Stagehand process crashed or was killed\"),\n );\n } catch {\n // ignore\n }\n};\n\nexport const maybeRunShutdownSupervisorFromArgv = (\n argv: readonly string[] = process.argv.slice(2),\n): boolean => {\n const parsed = parseConfigFromArgv(argv);\n if (!parsed) return false;\n runShutdownSupervisor(parsed);\n return true;\n};\n", "import fs from \"node:fs\";\n\n/**\n * Shared cleanup logic for locally launched Chrome.\n *\n * Used by both `V3.close()` (normal shutdown) and the supervisor process\n * (crash cleanup). The caller provides a `killChrome` callback since the\n * kill mechanism differs: chrome-launcher's `chrome.kill()` in-process\n * vs raw `process.kill(pid)` from the supervisor.\n */\nexport async function cleanupLocalBrowser(opts: {\n killChrome?: () => Promise<void> | void;\n userDataDir?: string;\n createdTempProfile?: boolean;\n preserveUserDataDir?: boolean;\n}): Promise<void> {\n if (opts.killChrome) {\n try {\n await opts.killChrome();\n } catch {\n // best-effort\n }\n }\n if (\n opts.createdTempProfile &&\n !opts.preserveUserDataDir &&\n opts.userDataDir\n ) {\n try {\n fs.rmSync(opts.userDataDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAEA,0BAAoB;;;ACOpB,iBAAwB;;;ACTxB,qBAAe;AAUf,eAAsB,oBAAoB,MAKxB;AAChB,MAAI,KAAK,YAAY;AACnB,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,IACxB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,MACE,KAAK,sBACL,CAAC,KAAK,uBACN,KAAK,aACL;AACA,QAAI;AACF,qBAAAA,QAAG,OAAO,KAAK,aAAa,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IAC9D,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;ADrBA,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAG7B,IAAI,SAA0C;AAC9C,IAAI,iBAAuC;AAC3C,IAAI,UAAU;AACd,IAAI,oBAAoB;AAExB,IAAM,OAAO,CAAC,OAAO,MAAY;AAC/B,MAAI;AACF,YAAQ,KAAK,IAAI;AAAA,EACnB,QAAQ;AAAA,EAER;AACF;AAIA,IAAM,aAAa,OAAO,QAA+B;AACvD,QAAM,UAAU,MAAe;AAC7B,QAAI;AACF,cAAQ,KAAK,KAAK,CAAC;AACnB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,MAAM;AAEZ,aAAO,IAAI,SAAS;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,EAAG;AAChB,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAAA,EAC7B,SAAS,OAAO;AACd,UAAM,MAAM;AAEZ,QAAI,IAAI,SAAS,QAAS;AAAA,EAC5B;AAEA,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,eAAe,CAAC;AACnE,QAAI,CAAC,QAAQ,EAAG;AAAA,EAClB;AACA,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAAA,EAC7B,QAAQ;AAAA,EAER;AACF;AAEA,IAAI,eAAsC;AAG1C,IAAM,kBAAkB,CAAC,QAAsB;AAC7C,MAAI,aAAc;AAClB,iBAAe,YAAY,MAAM;AAC/B,QAAI;AACF,cAAQ,KAAK,KAAK,CAAC;AACnB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,MAAM;AAEZ,UAAI,IAAI,SAAS,QAAS;AAAA,IAC5B;AAEA,wBAAoB;AACpB,QAAI,cAAc;AAChB,oBAAc,YAAY;AAC1B,qBAAe;AAAA,IACjB;AACA,SAAK,WAAW,wBAAwB,EAAE,QAAQ,MAAM,KAAK,CAAC,CAAC;AAAA,EACjE,GAAG,oBAAoB;AACzB;AAEA,IAAM,eAAe,OACnB,KACA,WACG;AACH,QAAM,sBAAsB;AAAA,IAC1B,IAAI,sBAAsB,CAAC,IAAI,uBAAuB,IAAI;AAAA,EAC5D;AACA,QAAM,oBAAoB;AAAA;AAAA;AAAA,IAGxB,YACE,IAAI,OAAO,CAAC,oBACR,MAAM;AACJ,cAAQ;AAAA,QACN,kDAAkD,IAAI,GAAG,YAC5C,MAAM,yBAAyB,mBAAmB;AAAA,MACjE;AACA,aAAO,WAAW,IAAI,GAAG;AAAA,IAC3B,IACA;AAAA,IACN,aAAa,IAAI;AAAA,IACjB,oBAAoB,IAAI;AAAA,IACxB,qBAAqB,IAAI;AAAA,EAC3B,CAAC;AACH;AAEA,IAAM,qBAAqB,OACzB,KACA,WACG;AACH,MAAI,CAAC,IAAI,UAAU,CAAC,IAAI,aAAa,CAAC,IAAI,UAAW;AACrD,MAAI;AACF,YAAQ;AAAA,MACN,oDAAoD,IAAI,SAAS,YACpD,MAAM;AAAA,IACrB;AACA,UAAM,KAAK,IAAI,WAAAC,QAAY,EAAE,QAAQ,IAAI,OAAO,CAAC;AACjD,UAAM,GAAG,SAAS,OAAO,IAAI,WAAW;AAAA,MACtC,QAAQ;AAAA,MACR,WAAW,IAAI;AAAA,IACjB,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAGA,IAAM,aAAa,CAAC,WAAkC;AACpD,MAAI,CAAC,gBAAgB;AACnB,sBAAkB,YAAY;AAC5B,YAAM,MAAM;AACZ,UAAI,CAAC,IAAK;AACV,UAAI,IAAI,SAAS,SAAS;AACxB,cAAM,aAAa,KAAK,MAAM;AAC9B;AAAA,MACF;AACA,UAAI,IAAI,SAAS,iBAAiB;AAChC,cAAM,mBAAmB,KAAK,MAAM;AAAA,MACtC;AAAA,IACF,GAAG;AAAA,EACL;AACA,SAAO;AACT;AAEA,IAAM,cAAc,CAAC,eAA+C;AAClE,WAAS;AACT,sBAAoB;AACpB,MAAI,OAAO,SAAS,WAAW,OAAO,KAAK;AACzC,oBAAgB,OAAO,GAAG;AAAA,EAC5B;AACF;AAEA,IAAM,mBAAmB,CAAC,WAAmB;AAC3C,OAAK,WAAW,MAAM,EAAE,QAAQ,MAAM,KAAK,CAAC,CAAC;AAC/C;AAEA,IAAM,sBAAsB,CAC1B,OAA0B,QAAQ,KAAK,MAAM,CAAC,MACV;AACpC,QAAM,SAAS;AACf,QAAM,MAAM,KAAK,KAAK,CAAC,QAAQ,IAAI,WAAW,MAAM,CAAC,GAAG,MAAM,OAAO,MAAM;AAC3E,MAAI,CAAC,KAAK,SAAS,cAAc,KAAK,CAAC,IAAK,QAAO;AACnD,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,wBAAwB,CACnC,kBACS;AACT,MAAI,QAAS;AACb,YAAU;AACV,cAAY,aAAa;AAGzB,MAAI;AACF,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM;AAAA,MAAG;AAAA,MAAO,MACtB,iBAAiB,6BAA6B;AAAA,IAChD;AACA,YAAQ,MAAM;AAAA,MAAG;AAAA,MAAS,MACxB,iBAAiB,6BAA6B;AAAA,IAChD;AACA,YAAQ,MAAM;AAAA,MAAG;AAAA,MAAS,MACxB,iBAAiB,yCAAyC;AAAA,IAC5D;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEO,IAAM,qCAAqC,CAChD,OAA0B,QAAQ,KAAK,MAAM,CAAC,MAClC;AACZ,QAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,CAAC,OAAQ,QAAO;AACpB,wBAAsB,MAAM;AAC5B,SAAO;AACT;;;AD3MA,IAAI,CAAC,mCAAmC,oBAAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG;AAC9D,UAAQ;AAAA,IACN;AAAA,EACF;AACA,sBAAAA,QAAQ,KAAK,CAAC;AAChB;",
6
6
  "names": ["fs", "Browserbase", "process"]
7
7
  }