@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
@@ -0,0 +1,52 @@
1
+ var import_vitest = require("vitest");
2
+ var import_sdkErrors = require("../lib/v3/types/public/sdkErrors.js");
3
+ (0, import_vitest.describe)("UnderstudyCommandException", () => {
4
+ (0, import_vitest.it)("extends StagehandError", () => {
5
+ const err = new import_sdkErrors.UnderstudyCommandException("test");
6
+ (0, import_vitest.expect)(err).toBeInstanceOf(import_sdkErrors.StagehandError);
7
+ (0, import_vitest.expect)(err).toBeInstanceOf(Error);
8
+ });
9
+ (0, import_vitest.it)("has the correct name", () => {
10
+ const err = new import_sdkErrors.UnderstudyCommandException("test");
11
+ (0, import_vitest.expect)(err.name).toBe("UnderstudyCommandException");
12
+ });
13
+ (0, import_vitest.it)("preserves the message", () => {
14
+ const err = new import_sdkErrors.UnderstudyCommandException("something broke");
15
+ (0, import_vitest.expect)(err.message).toBe("something broke");
16
+ });
17
+ (0, import_vitest.it)("stores the original error as cause when provided", () => {
18
+ const original = new Error("root cause");
19
+ const err = new import_sdkErrors.UnderstudyCommandException("wrapper message", original);
20
+ (0, import_vitest.expect)(err.cause).toBe(original);
21
+ (0, import_vitest.expect)(err.cause.message).toBe("root cause");
22
+ (0, import_vitest.expect)(err.cause.stack).toBeDefined();
23
+ });
24
+ (0, import_vitest.it)("stores non-Error cause values", () => {
25
+ const err = new import_sdkErrors.UnderstudyCommandException("failed", "string cause");
26
+ (0, import_vitest.expect)(err.cause).toBe("string cause");
27
+ });
28
+ (0, import_vitest.it)("has undefined cause when none is provided", () => {
29
+ const err = new import_sdkErrors.UnderstudyCommandException("no cause");
30
+ (0, import_vitest.expect)(err.cause).toBeUndefined();
31
+ });
32
+ (0, import_vitest.it)("generates its own stack trace", () => {
33
+ const err = new import_sdkErrors.UnderstudyCommandException("test");
34
+ (0, import_vitest.expect)(err.stack).toBeDefined();
35
+ (0, import_vitest.expect)(err.stack).toContain("UnderstudyCommandException");
36
+ });
37
+ (0, import_vitest.it)("preserves the original stack via cause for debugging", () => {
38
+ function deepFunction() {
39
+ throw new Error("deep error");
40
+ }
41
+ let original;
42
+ try {
43
+ deepFunction();
44
+ } catch (e) {
45
+ original = e;
46
+ }
47
+ const wrapped = new import_sdkErrors.UnderstudyCommandException(original.message, original);
48
+ (0, import_vitest.expect)(wrapped.stack).toBeDefined();
49
+ (0, import_vitest.expect)(wrapped.cause.stack).toContain("deepFunction");
50
+ });
51
+ });
52
+ //# sourceMappingURL=understudy-command-exception.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../tests/understudy-command-exception.test.ts"],
4
+ "sourcesContent": ["import { describe, expect, it } from \"vitest\";\nimport {\n UnderstudyCommandException,\n StagehandError,\n} from \"../lib/v3/types/public/sdkErrors.js\";\n\ndescribe(\"UnderstudyCommandException\", () => {\n it(\"extends StagehandError\", () => {\n const err = new UnderstudyCommandException(\"test\");\n expect(err).toBeInstanceOf(StagehandError);\n expect(err).toBeInstanceOf(Error);\n });\n\n it(\"has the correct name\", () => {\n const err = new UnderstudyCommandException(\"test\");\n expect(err.name).toBe(\"UnderstudyCommandException\");\n });\n\n it(\"preserves the message\", () => {\n const err = new UnderstudyCommandException(\"something broke\");\n expect(err.message).toBe(\"something broke\");\n });\n\n it(\"stores the original error as cause when provided\", () => {\n const original = new Error(\"root cause\");\n const err = new UnderstudyCommandException(\"wrapper message\", original);\n\n expect(err.cause).toBe(original);\n expect((err.cause as Error).message).toBe(\"root cause\");\n expect((err.cause as Error).stack).toBeDefined();\n });\n\n it(\"stores non-Error cause values\", () => {\n const err = new UnderstudyCommandException(\"failed\", \"string cause\");\n expect(err.cause).toBe(\"string cause\");\n });\n\n it(\"has undefined cause when none is provided\", () => {\n const err = new UnderstudyCommandException(\"no cause\");\n expect(err.cause).toBeUndefined();\n });\n\n it(\"generates its own stack trace\", () => {\n const err = new UnderstudyCommandException(\"test\");\n expect(err.stack).toBeDefined();\n expect(err.stack).toContain(\"UnderstudyCommandException\");\n });\n\n it(\"preserves the original stack via cause for debugging\", () => {\n function deepFunction() {\n throw new Error(\"deep error\");\n }\n\n let original: Error;\n try {\n deepFunction();\n } catch (e) {\n original = e as Error;\n }\n\n const wrapped = new UnderstudyCommandException(original!.message, original);\n\n // The wrapper has its own stack\n expect(wrapped.stack).toBeDefined();\n // The original stack is accessible via cause\n expect((wrapped.cause as Error).stack).toContain(\"deepFunction\");\n });\n});\n"],
5
+ "mappings": "AAAA,oBAAqC;AACrC,uBAGO;AAAA,IAEP,wBAAS,8BAA8B,MAAM;AAC3C,wBAAG,0BAA0B,MAAM;AACjC,UAAM,MAAM,IAAI,4CAA2B,MAAM;AACjD,8BAAO,GAAG,EAAE,eAAe,+BAAc;AACzC,8BAAO,GAAG,EAAE,eAAe,KAAK;AAAA,EAClC,CAAC;AAED,wBAAG,wBAAwB,MAAM;AAC/B,UAAM,MAAM,IAAI,4CAA2B,MAAM;AACjD,8BAAO,IAAI,IAAI,EAAE,KAAK,4BAA4B;AAAA,EACpD,CAAC;AAED,wBAAG,yBAAyB,MAAM;AAChC,UAAM,MAAM,IAAI,4CAA2B,iBAAiB;AAC5D,8BAAO,IAAI,OAAO,EAAE,KAAK,iBAAiB;AAAA,EAC5C,CAAC;AAED,wBAAG,oDAAoD,MAAM;AAC3D,UAAM,WAAW,IAAI,MAAM,YAAY;AACvC,UAAM,MAAM,IAAI,4CAA2B,mBAAmB,QAAQ;AAEtE,8BAAO,IAAI,KAAK,EAAE,KAAK,QAAQ;AAC/B,8BAAQ,IAAI,MAAgB,OAAO,EAAE,KAAK,YAAY;AACtD,8BAAQ,IAAI,MAAgB,KAAK,EAAE,YAAY;AAAA,EACjD,CAAC;AAED,wBAAG,iCAAiC,MAAM;AACxC,UAAM,MAAM,IAAI,4CAA2B,UAAU,cAAc;AACnE,8BAAO,IAAI,KAAK,EAAE,KAAK,cAAc;AAAA,EACvC,CAAC;AAED,wBAAG,6CAA6C,MAAM;AACpD,UAAM,MAAM,IAAI,4CAA2B,UAAU;AACrD,8BAAO,IAAI,KAAK,EAAE,cAAc;AAAA,EAClC,CAAC;AAED,wBAAG,iCAAiC,MAAM;AACxC,UAAM,MAAM,IAAI,4CAA2B,MAAM;AACjD,8BAAO,IAAI,KAAK,EAAE,YAAY;AAC9B,8BAAO,IAAI,KAAK,EAAE,UAAU,4BAA4B;AAAA,EAC1D,CAAC;AAED,wBAAG,wDAAwD,MAAM;AAC/D,aAAS,eAAe;AACtB,YAAM,IAAI,MAAM,YAAY;AAAA,IAC9B;AAEA,QAAI;AACJ,QAAI;AACF,mBAAa;AAAA,IACf,SAAS,GAAG;AACV,iBAAW;AAAA,IACb;AAEA,UAAM,UAAU,IAAI,4CAA2B,SAAU,SAAS,QAAQ;AAG1E,8BAAO,QAAQ,KAAK,EAAE,YAAY;AAElC,8BAAQ,QAAQ,MAAgB,KAAK,EAAE,UAAU,cAAc;AAAA,EACjE,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }
@@ -9,7 +9,7 @@ export interface LoggerOptions {
9
9
  /**
10
10
  * Creates a configured Pino logger instance
11
11
  */
12
- export declare function createLogger(options?: LoggerOptions): import("pino").Logger<never, boolean>;
12
+ export declare function createLogger(options?: LoggerOptions): pino.Logger<never, boolean>;
13
13
  /**
14
14
  * StagehandLogger class that wraps Pino for our specific needs
15
15
  *
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { maybeRunShutdownSupervisorFromArgv } from "./shutdown/supervisor.js";
4
+ // currently the CLI is only used to spawn the shutdown supervisor
5
+ // in the future, we may want to add more CLI commands here
6
+ if (!maybeRunShutdownSupervisorFromArgv(process.argv.slice(2))) {
7
+ console.error("Unsupported stagehand CLI invocation. Expected --supervisor with valid args.");
8
+ process.exit(1);
9
+ }
10
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../lib/v3/cli.js"],"names":[],"mappings":";AAEA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,kCAAkC,EAAE,MAAM,0BAA0B,CAAC;AAE9E,kEAAkE;AAClE,2DAA2D;AAC3D,IAAI,CAAC,kCAAkC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,OAAO,CAAC,KAAK,CACX,8EAA8E,CAC/E,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC","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"]}
File without changes
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerender-index.js","sourceRoot":"","sources":["../../../../../../lib/v3/dom/build/rerender-index.js"],"names":[],"mappings":"AAAA,CAAC,GAAE,EAAE,GAAC,SAAS,CAAC,KAAG,IAAG,CAAC;IAAA,IAAI,CAAC,GAAC,MAAM,CAAC,eAAe,CAAC;IAAA,IAAG,CAAC,CAAC,IAAE,OAAO,CAAC,CAAC,aAAa,IAAE,UAAU;QAAC,OAAO;IAAA,IAAI,CAAC,GAAC,EAAE,EAAC,CAAC,GAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAAA,OAAK,CAAC,CAAC,QAAQ,EAAE,GAAE,CAAC;QAAA,IAAI,CAAC,GAAC,CAAC,CAAC,WAAW,EAAC,CAAC,GAAC,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,IAAE,EAAE,CAAC;QAAA,IAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAE,OAAO,cAAc,EAAE,GAAG,IAAE,UAAU,IAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;YAAC,SAAS;QAAA,IAAI,CAAC,GAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAC,CAAC,GAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAAA,CAAC,IAAE,CAAC,IAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAAA,CAAC;IAAA,KAAI,IAAI,CAAC,IAAI,CAAC;QAAC,IAAG,CAAC;YAAA,IAAI,CAAC,GAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAAA,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QAAA,CAAC;QAAA,MAAK,CAAC,CAAA,CAAC;IAAA,CAAC,CAAC,KAAK,IAAE,CAAC,CAAC,MAAM,IAAE,OAAO,CAAC,IAAI,CAAC,uBAAuB,EAAC,EAAC,KAAK,EAAC,CAAC,CAAC,MAAM,EAAC,CAAC,CAAA;AAAA,CAAC;AAAA,OAAM,CAAC,EAAC,CAAC;IAAA,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAC,EAAC,OAAO,EAAC,MAAM,CAAC,CAAC,IAAE,EAAE,CAAC,EAAC,CAAC,CAAA;AAAA,CAAC,CAAA,CAAC,CAAA,CAAC,EAAE,CAAC,CAAA,CAAC,CAAC,EAAE,CAAC","sourcesContent":["(()=>{function s(){try{let o=window.__stagehandV3__;if(!o||typeof o.getClosedRoot!=\"function\")return;let t=[],r=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT);for(;r.nextNode();){let e=r.currentNode,n=e.tagName?.toLowerCase()??\"\";if(!n.includes(\"-\")||typeof customElements?.get!=\"function\"||!customElements.get(n))continue;let c=!!e.shadowRoot,i=!!o.getClosedRoot(e);c||i||t.push(e)}for(let e of t)try{let n=e.cloneNode(!0);e.replaceWith(n)}catch{}o.stats&&t.length&&console.info(\"[v3-piercer] rerender\",{count:t.length})}catch(o){console.info(\"[v3-piercer] rerender error\",{message:String(o??\"\")})}}s();})();\n"]}
File without changes
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-index.js","sourceRoot":"","sources":["../../../../../../lib/v3/dom/build/v3-index.js"],"names":[],"mappings":"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","sourcesContent":["(()=>{function s(c={}){let r=e=>{let{hostToRoot:o}=e;window.__stagehandV3__={getClosedRoot:a=>o.get(a),stats:()=>({installed:!0,url:location.href,isTop:window.top===window,open:e.openCount,closed:e.closedCount})}},n=Element.prototype.attachShadow;if(n.__v3Patched&&n.__v3State){n.__v3State.debug=!0,r(n.__v3State);return}let t={hostToRoot:new WeakMap,openCount:0,closedCount:0,debug:!0},l=n,d=function(e){let o=e?.mode??\"open\",a=l.call(this,e);try{t.hostToRoot.set(this,a),o===\"closed\"?t.closedCount++:t.openCount++,t.debug&&console.info(\"[v3-piercer] attachShadow\",{tag:this.tagName?.toLowerCase()??\"\",mode:o,url:location.href})}catch{}return a};if(d.__v3Patched=!0,d.__v3State=t,Object.defineProperty(Element.prototype,\"attachShadow\",{configurable:!0,writable:!0,value:d}),c.tagExisting)try{let e=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT);for(;e.nextNode();){let o=e.currentNode;o.shadowRoot&&(t.hostToRoot.set(o,o.shadowRoot),t.openCount++)}}catch{}window.__stagehandV3Injected=!0,r(t),t.debug&&console.info(\"[v3-piercer] installed\",{url:location.href,isTop:window.top===window,readyState:document.readyState})}s({debug:!0,tagExisting:!1});})();\n"]}
@@ -11,6 +11,7 @@ export { V3Evaluator } from "../v3Evaluator.js";
11
11
  export { tool } from "ai";
12
12
  export { getAISDKLanguageModel } from "./llm/LLMProvider.js";
13
13
  export { __internalCreateInMemoryAgentCacheHandle } from "./cache/serverAgentCache.js";
14
+ export { maybeRunShutdownSupervisorFromArgv as __internalMaybeRunShutdownSupervisorFromArgv } from "./shutdown/supervisor.js";
14
15
  export type { ServerAgentCacheHandle } from "./cache/serverAgentCache.js";
15
16
  export type { ChatMessage, ChatMessageContent, ChatMessageImageContent, ChatMessageTextContent, ChatCompletionOptions, LLMResponse, CreateChatCompletionOptions, LLMUsage, LLMParsedResponse, } from "./llm/LLMClient.js";
16
17
  export type { StagehandZodSchema, StagehandZodObject, InferStagehandSchema, JsonSchemaDocument, } from "./zodCompat.js";
@@ -10,4 +10,5 @@ export { V3Evaluator } from "../v3Evaluator.js";
10
10
  export { tool } from "ai";
11
11
  export { getAISDKLanguageModel } from "./llm/LLMProvider.js";
12
12
  export { __internalCreateInMemoryAgentCacheHandle } from "./cache/serverAgentCache.js";
13
+ export { maybeRunShutdownSupervisorFromArgv as __internalMaybeRunShutdownSupervisorFromArgv } from "./shutdown/supervisor.js";
13
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../lib/v3/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EACL,aAAa,EACb,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AASlC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { V3 } from \"./v3.js\";\nexport { V3 as Stagehand } from \"./v3.js\";\n\nexport * from \"./types/public/index.js\";\nexport { AnnotatedScreenshotText, LLMClient } from \"./llm/LLMClient.js\";\n\nexport {\n AgentProvider,\n modelToAgentProviderMap,\n} from \"./agent/AgentProvider.js\";\nexport type {\n AgentTools,\n AgentToolTypesMap,\n AgentUITools,\n AgentToolCall,\n AgentToolResult,\n} from \"./agent/tools/index.js\";\n\nexport {\n validateZodSchema,\n isRunningInBun,\n toGeminiSchema,\n getZodType,\n transformSchema,\n injectUrls,\n providerEnvVarMap,\n loadApiKeyFromEnv,\n trimTrailingTextNode,\n jsonSchemaToZod,\n} from \"../utils.js\";\nexport { isZod4Schema, isZod3Schema, toJsonSchema } from \"./zodCompat.js\";\n\nexport { connectToMCPServer } from \"./mcp/connection.js\";\nexport { V3Evaluator } from \"../v3Evaluator.js\";\nexport { tool } from \"ai\";\nexport { getAISDKLanguageModel } from \"./llm/LLMProvider.js\";\nexport { __internalCreateInMemoryAgentCacheHandle } from \"./cache/serverAgentCache.js\";\nexport type { ServerAgentCacheHandle } from \"./cache/serverAgentCache.js\";\n\nexport type {\n ChatMessage,\n ChatMessageContent,\n ChatMessageImageContent,\n ChatMessageTextContent,\n ChatCompletionOptions,\n LLMResponse,\n CreateChatCompletionOptions,\n LLMUsage,\n LLMParsedResponse,\n} from \"./llm/LLMClient.js\";\n\nexport type {\n StagehandZodSchema,\n StagehandZodObject,\n InferStagehandSchema,\n JsonSchemaDocument,\n} from \"./zodCompat.js\";\n\nexport type { JsonSchema, JsonSchemaProperty } from \"../utils.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../lib/v3/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EACL,aAAa,EACb,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AASlC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAE,kCAAkC,IAAI,4CAA4C,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export { V3 } from \"./v3.js\";\nexport { V3 as Stagehand } from \"./v3.js\";\n\nexport * from \"./types/public/index.js\";\nexport { AnnotatedScreenshotText, LLMClient } from \"./llm/LLMClient.js\";\n\nexport {\n AgentProvider,\n modelToAgentProviderMap,\n} from \"./agent/AgentProvider.js\";\nexport type {\n AgentTools,\n AgentToolTypesMap,\n AgentUITools,\n AgentToolCall,\n AgentToolResult,\n} from \"./agent/tools/index.js\";\n\nexport {\n validateZodSchema,\n isRunningInBun,\n toGeminiSchema,\n getZodType,\n transformSchema,\n injectUrls,\n providerEnvVarMap,\n loadApiKeyFromEnv,\n trimTrailingTextNode,\n jsonSchemaToZod,\n} from \"../utils.js\";\nexport { isZod4Schema, isZod3Schema, toJsonSchema } from \"./zodCompat.js\";\n\nexport { connectToMCPServer } from \"./mcp/connection.js\";\nexport { V3Evaluator } from \"../v3Evaluator.js\";\nexport { tool } from \"ai\";\nexport { getAISDKLanguageModel } from \"./llm/LLMProvider.js\";\nexport { __internalCreateInMemoryAgentCacheHandle } from \"./cache/serverAgentCache.js\";\nexport { maybeRunShutdownSupervisorFromArgv as __internalMaybeRunShutdownSupervisorFromArgv } from \"./shutdown/supervisor.js\";\nexport type { ServerAgentCacheHandle } from \"./cache/serverAgentCache.js\";\n\nexport type {\n ChatMessage,\n ChatMessageContent,\n ChatMessageImageContent,\n ChatMessageTextContent,\n ChatCompletionOptions,\n LLMResponse,\n CreateChatCompletionOptions,\n LLMUsage,\n LLMParsedResponse,\n} from \"./llm/LLMClient.js\";\n\nexport type {\n StagehandZodSchema,\n StagehandZodObject,\n InferStagehandSchema,\n JsonSchemaDocument,\n} from \"./zodCompat.js\";\n\nexport type { JsonSchema, JsonSchemaProperty } from \"../utils.js\";\n"]}
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Shutdown supervisor process.
3
3
  *
4
- * This process watches a lifeline (stdin/IPC). When the parent dies, the
5
- * lifeline closes and the supervisor performs best-effort cleanup:
6
- * - LOCAL: kill Chrome + remove temp profile (when keepAlive is false)
7
- * - STAGEHAND_API: request session release (when keepAlive is false)
4
+ * This process watches a stdin lifeline. When the parent dies, stdin closes
5
+ * and the supervisor performs best-effort cleanup:
6
+ * - LOCAL: kill Chrome + remove temp profile
7
+ * - STAGEHAND_API: request session release
8
8
  */
9
- export {};
9
+ import type { ShutdownSupervisorConfig } from "../types/private/shutdown.js";
10
+ export declare const runShutdownSupervisor: (initialConfig: ShutdownSupervisorConfig) => void;
11
+ export declare const maybeRunShutdownSupervisorFromArgv: (argv?: readonly string[]) => boolean;
@@ -1,19 +1,21 @@
1
1
  /**
2
2
  * Shutdown supervisor process.
3
3
  *
4
- * This process watches a lifeline (stdin/IPC). When the parent dies, the
5
- * lifeline closes and the supervisor performs best-effort cleanup:
6
- * - LOCAL: kill Chrome + remove temp profile (when keepAlive is false)
7
- * - STAGEHAND_API: request session release (when keepAlive is false)
4
+ * This process watches a stdin lifeline. When the parent dies, stdin closes
5
+ * and the supervisor performs best-effort cleanup:
6
+ * - LOCAL: kill Chrome + remove temp profile
7
+ * - STAGEHAND_API: request session release
8
8
  */
9
9
  import Browserbase from "@browserbasehq/sdk";
10
10
  import { cleanupLocalBrowser } from "./cleanupLocal.js";
11
- const SIGKILL_POLL_MS = 500;
12
- const SIGKILL_TIMEOUT_MS = 10_000;
11
+ const SIGKILL_POLL_MS = 250;
12
+ const SIGKILL_TIMEOUT_MS = 7_000;
13
13
  const PID_POLL_INTERVAL_MS = 500;
14
- let armed = false;
14
+ // `cleanupPromise` guarantees we execute cleanup at most once.
15
15
  let config = null;
16
16
  let cleanupPromise = null;
17
+ let started = false;
18
+ let localPidKnownGone = false;
17
19
  const exit = (code = 0) => {
18
20
  try {
19
21
  process.exit(code);
@@ -22,14 +24,18 @@ const exit = (code = 0) => {
22
24
  // ignore
23
25
  }
24
26
  };
25
- const safeKill = async (pid) => {
27
+ // Best-effort two-phase kill: SIGTERM first, then SIGKILL after timeout.
28
+ // Treat only ESRCH as "already gone"; other errors should not imply dead.
29
+ const politeKill = async (pid) => {
26
30
  const isAlive = () => {
27
31
  try {
28
32
  process.kill(pid, 0);
29
33
  return true;
30
34
  }
31
- catch {
32
- return false;
35
+ catch (error) {
36
+ const err = error;
37
+ // ESRCH = "No such process" (PID is already gone).
38
+ return err.code !== "ESRCH";
33
39
  }
34
40
  };
35
41
  if (!isAlive())
@@ -37,8 +43,11 @@ const safeKill = async (pid) => {
37
43
  try {
38
44
  process.kill(pid, "SIGTERM");
39
45
  }
40
- catch {
41
- return;
46
+ catch (error) {
47
+ const err = error;
48
+ // ESRCH = process already exited; no further action needed.
49
+ if (err.code === "ESRCH")
50
+ return;
42
51
  }
43
52
  const deadline = Date.now() + SIGKILL_TIMEOUT_MS;
44
53
  while (Date.now() < deadline) {
@@ -53,40 +62,53 @@ const safeKill = async (pid) => {
53
62
  // best-effort
54
63
  }
55
64
  };
56
- let pidGone = false;
57
65
  let pidPollTimer = null;
66
+ // Local-only fallback: if Chrome dies while parent still lives, run cleanup and exit.
58
67
  const startPidPolling = (pid) => {
59
68
  if (pidPollTimer)
60
69
  return;
61
70
  pidPollTimer = setInterval(() => {
62
71
  try {
63
72
  process.kill(pid, 0);
73
+ return;
64
74
  }
65
- catch {
66
- pidGone = true;
67
- if (pidPollTimer) {
68
- clearInterval(pidPollTimer);
69
- pidPollTimer = null;
70
- }
75
+ catch (error) {
76
+ const err = error;
77
+ // Only ESRCH means the process is definitely gone.
78
+ if (err.code !== "ESRCH")
79
+ return;
80
+ }
81
+ localPidKnownGone = true;
82
+ if (pidPollTimer) {
83
+ clearInterval(pidPollTimer);
84
+ pidPollTimer = null;
71
85
  }
86
+ void runCleanup("Browser process exited").finally(() => exit(0));
72
87
  }, PID_POLL_INTERVAL_MS);
73
88
  };
74
- const cleanupLocal = async (cfg) => {
75
- if (cfg.keepAlive)
76
- return;
89
+ const cleanupLocal = async (cfg, reason) => {
90
+ const deletingUserDataDir = Boolean(cfg.createdTempProfile && !cfg.preserveUserDataDir && cfg.userDataDir);
77
91
  await cleanupLocalBrowser({
78
- killChrome: cfg.pid && !pidGone ? () => safeKill(cfg.pid) : undefined,
92
+ // If polling already observed ESRCH, avoid a follow-up PID kill.
93
+ // The PID could be reused by a different process before cleanup runs.
94
+ killChrome: cfg.pid && !localPidKnownGone
95
+ ? () => {
96
+ console.error(`[shutdown-supervisor] Shutting down Chrome pid=${cfg.pid} ` +
97
+ `(reason=${reason}, deletingUserDataDir=${deletingUserDataDir})`);
98
+ return politeKill(cfg.pid);
99
+ }
100
+ : undefined,
79
101
  userDataDir: cfg.userDataDir,
80
102
  createdTempProfile: cfg.createdTempProfile,
81
103
  preserveUserDataDir: cfg.preserveUserDataDir,
82
104
  });
83
105
  };
84
- const cleanupBrowserbase = async (cfg) => {
85
- if (cfg.keepAlive)
86
- return;
106
+ const cleanupBrowserbase = async (cfg, reason) => {
87
107
  if (!cfg.apiKey || !cfg.projectId || !cfg.sessionId)
88
108
  return;
89
109
  try {
110
+ console.error(`[shutdown-supervisor] Ending Browserbase session ${cfg.sessionId} ` +
111
+ `(reason=${reason})`);
90
112
  const bb = new Browserbase({ apiKey: cfg.apiKey });
91
113
  await bb.sessions.update(cfg.sessionId, {
92
114
  status: "REQUEST_RELEASE",
@@ -97,60 +119,67 @@ const cleanupBrowserbase = async (cfg) => {
97
119
  // best-effort cleanup
98
120
  }
99
121
  };
100
- const runCleanup = () => {
122
+ // Idempotent cleanup entrypoint used by all supervisor shutdown paths.
123
+ const runCleanup = (reason) => {
101
124
  if (!cleanupPromise) {
102
125
  cleanupPromise = (async () => {
103
126
  const cfg = config;
104
- if (!cfg || !armed)
127
+ if (!cfg)
105
128
  return;
106
- armed = false;
107
129
  if (cfg.kind === "LOCAL") {
108
- await cleanupLocal(cfg);
130
+ await cleanupLocal(cfg, reason);
109
131
  return;
110
132
  }
111
133
  if (cfg.kind === "STAGEHAND_API") {
112
- await cleanupBrowserbase(cfg);
134
+ await cleanupBrowserbase(cfg, reason);
113
135
  }
114
136
  })();
115
137
  }
116
138
  return cleanupPromise;
117
139
  };
118
- const onLifelineClosed = () => {
119
- void runCleanup().finally(() => exit(0));
140
+ const applyConfig = (nextConfig) => {
141
+ config = nextConfig;
142
+ localPidKnownGone = false;
143
+ if (config.kind === "LOCAL" && config.pid) {
144
+ startPidPolling(config.pid);
145
+ }
120
146
  };
121
- const onMessage = (raw) => {
122
- if (!raw || typeof raw !== "object")
123
- return;
124
- const msg = raw;
125
- if (msg.type === "config") {
126
- config = msg.config ?? null;
127
- armed = Boolean(config) && config?.keepAlive === false;
128
- if (armed && config?.kind === "LOCAL" && config?.pid) {
129
- startPidPolling(config.pid);
130
- }
131
- try {
132
- process.send?.({ type: "ready" });
133
- }
134
- catch {
135
- // ignore IPC failures
136
- }
147
+ const onLifelineClosed = (reason) => {
148
+ void runCleanup(reason).finally(() => exit(0));
149
+ };
150
+ const parseConfigFromArgv = (argv = process.argv.slice(2)) => {
151
+ const prefix = "--supervisor-config=";
152
+ const raw = argv.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
153
+ if (!argv.includes("--supervisor") || !raw)
154
+ return null;
155
+ try {
156
+ return JSON.parse(raw);
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ };
162
+ export const runShutdownSupervisor = (initialConfig) => {
163
+ if (started)
137
164
  return;
165
+ started = true;
166
+ applyConfig(initialConfig);
167
+ // Stdin is the lifeline; losing it means parent is gone.
168
+ try {
169
+ process.stdin.resume();
170
+ process.stdin.on("end", () => onLifelineClosed("Stagehand process completed"));
171
+ process.stdin.on("close", () => onLifelineClosed("Stagehand process completed"));
172
+ process.stdin.on("error", () => onLifelineClosed("Stagehand process crashed or was killed"));
138
173
  }
139
- if (msg.type === "exit") {
140
- armed = false;
141
- exit(0);
174
+ catch {
175
+ // ignore
142
176
  }
143
177
  };
144
- // Keep stdin open as a lifeline to the parent process.
145
- try {
146
- process.stdin.resume();
147
- process.stdin.on("end", onLifelineClosed);
148
- process.stdin.on("close", onLifelineClosed);
149
- process.stdin.on("error", onLifelineClosed);
150
- }
151
- catch {
152
- // ignore
153
- }
154
- process.on("disconnect", onLifelineClosed);
155
- process.on("message", onMessage);
178
+ export const maybeRunShutdownSupervisorFromArgv = (argv = process.argv.slice(2)) => {
179
+ const parsed = parseConfigFromArgv(argv);
180
+ if (!parsed)
181
+ return false;
182
+ runShutdownSupervisor(parsed);
183
+ return true;
184
+ };
156
185
  //# sourceMappingURL=supervisor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../../../lib/v3/shutdown/supervisor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shutdown supervisor process.\n *\n * This process watches a lifeline (stdin/IPC). When the parent dies, the\n * lifeline closes and the supervisor performs best-effort cleanup:\n * - LOCAL: kill Chrome + remove temp profile (when keepAlive is false)\n * - STAGEHAND_API: request session release (when keepAlive is false)\n */\n\nimport Browserbase from \"@browserbasehq/sdk\";\nimport type {\n ShutdownSupervisorConfig,\n ShutdownSupervisorMessage,\n} from \"../types/private/shutdown.js\";\nimport { cleanupLocalBrowser } from \"./cleanupLocal.js\";\n\nconst SIGKILL_POLL_MS = 500;\nconst SIGKILL_TIMEOUT_MS = 10_000;\nconst PID_POLL_INTERVAL_MS = 500;\n\nlet armed = false;\nlet config: ShutdownSupervisorConfig | null = null;\nlet cleanupPromise: Promise<void> | null = null;\n\nconst exit = (code = 0): void => {\n try {\n process.exit(code);\n } catch {\n // ignore\n }\n};\n\nconst safeKill = async (pid: number): Promise<void> => {\n const isAlive = (): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n };\n\n if (!isAlive()) return;\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n 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 pidGone = false;\nlet pidPollTimer: NodeJS.Timeout | null = null;\n\nconst startPidPolling = (pid: number): void => {\n if (pidPollTimer) return;\n pidPollTimer = setInterval(() => {\n try {\n process.kill(pid, 0);\n } catch {\n pidGone = true;\n if (pidPollTimer) {\n clearInterval(pidPollTimer);\n pidPollTimer = null;\n }\n }\n }, PID_POLL_INTERVAL_MS);\n};\n\nconst cleanupLocal = async (\n cfg: Extract<ShutdownSupervisorConfig, { kind: \"LOCAL\" }>,\n) => {\n if (cfg.keepAlive) return;\n await cleanupLocalBrowser({\n killChrome: cfg.pid && !pidGone ? () => safeKill(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.keepAlive) return;\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\nconst runCleanup = (): Promise<void> => {\n if (!cleanupPromise) {\n cleanupPromise = (async () => {\n const cfg = config;\n if (!cfg || !armed) return;\n armed = false;\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 onLifelineClosed = () => {\n void runCleanup().finally(() => exit(0));\n};\n\nconst onMessage = (raw: unknown) => {\n if (!raw || typeof raw !== \"object\") return;\n const msg = raw as ShutdownSupervisorMessage;\n if (msg.type === \"config\") {\n config = msg.config ?? null;\n armed = Boolean(config) && config?.keepAlive === false;\n if (armed && config?.kind === \"LOCAL\" && config?.pid) {\n startPidPolling(config.pid);\n }\n try {\n process.send?.({ type: \"ready\" });\n } catch {\n // ignore IPC failures\n }\n return;\n }\n if (msg.type === \"exit\") {\n armed = false;\n exit(0);\n }\n};\n\n// Keep stdin open as a lifeline to the parent process.\ntry {\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\nprocess.on(\"disconnect\", onLifelineClosed);\nprocess.on(\"message\", onMessage);\n"]}
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../../../lib/v3/shutdown/supervisor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\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"]}
@@ -6,37 +6,67 @@
6
6
  * session release) when keepAlive is false.
7
7
  */
8
8
  import fs from "node:fs";
9
- import path from "node:path";
10
9
  import { spawn } from "node:child_process";
10
+ import { createRequire } from "node:module";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { ShutdownSupervisorResolveError, ShutdownSupervisorSpawnError, } from "../types/private/shutdownErrors.js";
13
- const READY_TIMEOUT_MS = 500;
14
- // Prefer import.meta.url always correct per-module in ESM.
15
- // __dirname may be an incorrect polyfill in some tsx / loader configurations.
16
- const thisDir = path.dirname(fileURLToPath(import.meta.url));
17
- const resolveSupervisorScript = () => {
18
- const jsPath = path.resolve(thisDir, "supervisor.js");
19
- if (fs.existsSync(jsPath)) {
20
- return { command: process.execPath, args: [jsPath] };
13
+ // Resolve module path for both CJS and ESM builds.
14
+ const normalizedFilename = typeof __filename === "string" ? __filename.replaceAll("\\", "/") : "";
15
+ const hasAbsoluteFilename = normalizedFilename.startsWith("/") || /^[A-Za-z]:\//.test(normalizedFilename);
16
+ const modulePath = hasAbsoluteFilename
17
+ ? __filename
18
+ : fileURLToPath(import.meta.url);
19
+ const normalizedModulePath = modulePath.replaceAll("\\", "/");
20
+ const moduleDir = normalizedModulePath.slice(0, normalizedModulePath.lastIndexOf("/"));
21
+ const require = createRequire(modulePath);
22
+ const isSeaRuntime = () => {
23
+ try {
24
+ const sea = require("node:sea");
25
+ return Boolean(sea.isSea?.());
26
+ }
27
+ catch {
28
+ return false;
21
29
  }
22
- const tsPath = path.resolve(thisDir, "supervisor.ts");
23
- if (fs.existsSync(tsPath)) {
24
- return { command: process.execPath, args: ["--import", "tsx", tsPath] };
30
+ };
31
+ // SEA: re-exec current binary with supervisor args.
32
+ // Non-SEA: execute Stagehand CLI entrypoint with supervisor args.
33
+ const resolveSupervisorCommand = (config) => {
34
+ const baseArgs = ["--supervisor", serializeConfigArg(config)];
35
+ if (isSeaRuntime()) {
36
+ return { command: process.execPath, args: baseArgs };
25
37
  }
26
- return null;
38
+ const cliPathCandidates = [`${moduleDir}/../cli.js`, `${moduleDir}/cli.js`];
39
+ const cliPath = cliPathCandidates.find((candidate) => fs.existsSync(candidate)) ?? null;
40
+ if (!cliPath)
41
+ return null;
42
+ const needsTsxLoader = fs.existsSync(`${moduleDir}/supervisor.ts`) &&
43
+ !fs.existsSync(`${moduleDir}/supervisor.js`);
44
+ return {
45
+ command: process.execPath,
46
+ args: needsTsxLoader
47
+ ? ["--import", "tsx", cliPath, ...baseArgs]
48
+ : [cliPath, ...baseArgs],
49
+ };
27
50
  };
51
+ // Single JSON arg keeps supervisor bootstrap parsing tiny and versionable.
52
+ const serializeConfigArg = (config) => `--supervisor-config=${JSON.stringify({
53
+ ...config,
54
+ parentPid: process.pid,
55
+ })}`;
28
56
  /**
29
57
  * Start a supervisor process for crash cleanup. Returns a handle that can
30
58
  * stop the supervisor during a normal shutdown.
31
59
  */
32
60
  export function startShutdownSupervisor(config, opts) {
33
- const resolved = resolveSupervisorScript();
61
+ const resolved = resolveSupervisorCommand(config);
34
62
  if (!resolved) {
35
- opts?.onError?.(new ShutdownSupervisorResolveError(`Shutdown supervisor script missing (searched ${thisDir} for supervisor.js or supervisor.ts).`), "resolve");
63
+ opts?.onError?.(new ShutdownSupervisorResolveError("Shutdown supervisor entry missing (expected Stagehand CLI entrypoint)."), "resolve");
36
64
  return null;
37
65
  }
38
66
  const child = spawn(resolved.command, resolved.args, {
39
- stdio: ["pipe", "ignore", "ignore", "ipc"],
67
+ // stdin is the parent lifeline.
68
+ // Preserve supervisor stderr so crash-cleanup debug lines are visible.
69
+ stdio: ["pipe", "ignore", "inherit"],
40
70
  detached: true,
41
71
  });
42
72
  child.on("error", (error) => {
@@ -50,48 +80,15 @@ export function startShutdownSupervisor(config, opts) {
50
80
  catch {
51
81
  // best-effort: avoid keeping the event loop alive
52
82
  }
53
- try {
54
- const message = { type: "config", config };
55
- child.send?.(message);
56
- }
57
- catch {
58
- // ignore IPC failures
59
- }
60
- const ready = new Promise((resolve) => {
61
- let resolved = false;
62
- const done = () => {
63
- if (resolved)
64
- return;
65
- resolved = true;
66
- clearTimeout(timer);
67
- child.off("message", onMessage);
68
- resolve();
69
- };
70
- const timer = setTimeout(done, READY_TIMEOUT_MS);
71
- const onMessage = (msg) => {
72
- const payload = msg;
73
- if (payload?.type === "ready") {
74
- done();
75
- }
76
- };
77
- child.on("message", onMessage);
78
- child.on("exit", done);
79
- });
80
83
  const stop = () => {
84
+ // Normal close path: terminate supervisor directly.
81
85
  try {
82
- const message = { type: "exit" };
83
- child.send?.(message);
84
- }
85
- catch {
86
- // ignore
87
- }
88
- try {
89
- child.disconnect?.();
86
+ child.kill("SIGTERM");
90
87
  }
91
88
  catch {
92
89
  // ignore
93
90
  }
94
91
  };
95
- return { stop, ready };
92
+ return { stop };
96
93
  }
97
94
  //# sourceMappingURL=supervisorClient.js.map