@cmmd-center/forge 0.13.28 → 0.13.30

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 (44) hide show
  1. package/dist/{BunHttpServer-C0oPP_3A.cjs → BunHttpServer-CXndtc1j.cjs} +2 -2
  2. package/dist/{BunHttpServer-FbbQmsl_.mjs → BunHttpServer-DjilmGOM.mjs} +2 -2
  3. package/dist/{BunPTY-uyreNRGp.cjs → BunPTY-BiTlUj63.cjs} +1 -1
  4. package/dist/{BunPTY-DkAIzLIx.mjs → BunPTY-Csgwhbi5.mjs} +1 -1
  5. package/dist/{BunServices-DC7nhA3w.mjs → BunServices-BZKzHdrG.mjs} +1 -1
  6. package/dist/{BunServices-DkenwD7S.cjs → BunServices-DsWG7VM1.cjs} +1 -1
  7. package/dist/{NodeHttpServer-Cjd3bFrD.cjs → NodeHttpServer-B4dCKqYY.cjs} +1 -1
  8. package/dist/{NodeHttpServer-BJ4l15f-.mjs → NodeHttpServer-Ds3Gp1Gp.mjs} +1 -1
  9. package/dist/{NodePTY-BehazUMi.cjs → NodePTY-BJ7874MO.cjs} +1 -1
  10. package/dist/{NodePTY-BhezYy9H.mjs → NodePTY-DgXWLtWp.mjs} +1 -1
  11. package/dist/{NodeSqliteClient-CfsdC3AB.cjs → NodeSqliteClient-CPsJxxdD.cjs} +1 -1
  12. package/dist/{NodeSqliteClient-md8xd9hY.mjs → NodeSqliteClient-D29w60zZ.mjs} +1 -1
  13. package/dist/{SqliteClient-BKB695RF.mjs → SqliteClient-Bgo_s5sA.mjs} +1 -1
  14. package/dist/{SqliteClient-O1ABIngw.cjs → SqliteClient-k0pbfeIC.cjs} +1 -1
  15. package/dist/bin.cjs +432 -1012
  16. package/dist/bin.mjs +452 -1031
  17. package/dist/client/assets/{DiffPanel-CmdI2N7U.js → DiffPanel-u3cympMJ.js} +2 -2
  18. package/dist/client/assets/{DiffPanel.logic-DtFJMr9U.js → DiffPanel.logic-Im6HMxhw.js} +2 -2
  19. package/dist/client/assets/{DiffPanelShell-DStZuS4s.js → DiffPanelShell-BckZpJGo.js} +102 -102
  20. package/dist/client/assets/{DiffWorkerPoolProvider-CG8qkZ21.js → DiffWorkerPoolProvider-Bwjf7Bmf.js} +2 -2
  21. package/dist/client/assets/{PullRequestCodePanel-pc6WtcMu.js → PullRequestCodePanel-VADCfzOQ.js} +2 -2
  22. package/dist/client/assets/{Virtualizer-DQWCMRz2.js → Virtualizer-Dp4FFzsC.js} +2 -2
  23. package/dist/client/assets/{index-AZc0SrFH.js → index-CiD_w5sq.js} +7 -7
  24. package/dist/client/index.html +4 -4
  25. package/dist/{execAsync-BUcaFQ1D.cjs → execAsync--H9ZZOiC.cjs} +1 -1
  26. package/dist/{execAsync-BNNdR88o.mjs → execAsync-BJ4g1UuR.mjs} +1 -1
  27. package/dist/forge-build.json +1 -1
  28. package/dist/{getMachineId-bsd-3FzKrfVS.cjs → getMachineId-bsd-CJJWgsvW.cjs} +2 -2
  29. package/dist/{getMachineId-bsd-BuYwT36r.mjs → getMachineId-bsd-CvzjzhCJ.mjs} +2 -2
  30. package/dist/{getMachineId-darwin-RZKhGQ6C.mjs → getMachineId-darwin-BkjZ6NK7.mjs} +2 -2
  31. package/dist/{getMachineId-darwin-J2wNbebX.cjs → getMachineId-darwin-hZeIAq-t.cjs} +2 -2
  32. package/dist/{getMachineId-linux-BDLTrARB.cjs → getMachineId-linux-B4Pn5gp9.cjs} +1 -1
  33. package/dist/{getMachineId-linux-D5HSP0rH.mjs → getMachineId-linux-BaBxzaf3.mjs} +1 -1
  34. package/dist/{getMachineId-unsupported-DRh5MFwr.cjs → getMachineId-unsupported-B3jtoOL0.cjs} +1 -1
  35. package/dist/{getMachineId-unsupported-IaplPZnC.mjs → getMachineId-unsupported-EhCzrOph.mjs} +1 -1
  36. package/dist/{getMachineId-win-D220ursp.mjs → getMachineId-win-BFwtIsaZ.mjs} +2 -2
  37. package/dist/{getMachineId-win-BA8dHcjg.cjs → getMachineId-win-Bb4lJE8e.cjs} +2 -2
  38. package/dist/{open-FIgTDosf.mjs → open-2-reBrpv.mjs} +3 -3
  39. package/dist/{open-BAsPVCWX.cjs → open-CghsF25U.cjs} +1 -1
  40. package/dist/playwright-backend-BONxTCSA.cjs +643 -0
  41. package/dist/playwright-backend-DVQOhqRO.mjs +642 -0
  42. package/dist/settle-B6Rn65UV.cjs +28 -0
  43. package/dist/settle-BJcmpbvq.mjs +23 -0
  44. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -14,6 +14,7 @@ const require_Mime = require("./Mime-Bilnh8zE.cjs");
14
14
  const require_esm = require("./esm-BZhXwX5p.cjs");
15
15
  const require_runtimeControlAuthority = require("./runtimeControlAuthority-DCitZFTk.cjs");
16
16
  const require_PTY = require("./PTY-B4ZctbwQ.cjs");
17
+ const require_settle = require("./settle-B6Rn65UV.cjs");
17
18
  let node_child_process = require("node:child_process");
18
19
  node_child_process = require_chunk.__toESM(node_child_process);
19
20
  let node_stream = require("node:stream");
@@ -89,7 +90,6 @@ http2 = require_chunk.__toESM(http2);
89
90
  let fs_promises = require("fs/promises");
90
91
  let readline = require("readline");
91
92
  let async_hooks = require("async_hooks");
92
- let playwright = require("playwright");
93
93
  let node_dgram = require("node:dgram");
94
94
  let dgram = require("dgram");
95
95
  let timers_promises = require("timers/promises");
@@ -53192,19 +53192,19 @@ var require_getMachineId = /* @__PURE__ */ require_chunk.__commonJSMin(((exports
53192
53192
  async function getMachineId() {
53193
53193
  if (!getMachineIdImpl) switch (process$3.platform) {
53194
53194
  case "darwin":
53195
- getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-darwin-J2wNbebX.cjs").default))).getMachineId;
53195
+ getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-darwin-hZeIAq-t.cjs").default))).getMachineId;
53196
53196
  break;
53197
53197
  case "linux":
53198
- getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-linux-BDLTrARB.cjs").default))).getMachineId;
53198
+ getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-linux-B4Pn5gp9.cjs").default))).getMachineId;
53199
53199
  break;
53200
53200
  case "freebsd":
53201
- getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-bsd-3FzKrfVS.cjs").default))).getMachineId;
53201
+ getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-bsd-CJJWgsvW.cjs").default))).getMachineId;
53202
53202
  break;
53203
53203
  case "win32":
53204
- getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-win-BA8dHcjg.cjs").default))).getMachineId;
53204
+ getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-win-Bb4lJE8e.cjs").default))).getMachineId;
53205
53205
  break;
53206
53206
  default:
53207
- getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-unsupported-DRh5MFwr.cjs").default))).getMachineId;
53207
+ getMachineIdImpl = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./getMachineId-unsupported-B3jtoOL0.cjs").default))).getMachineId;
53208
53208
  break;
53209
53209
  }
53210
53210
  return getMachineIdImpl();
@@ -66232,7 +66232,7 @@ function normalizeNumberish(value) {
66232
66232
  }
66233
66233
  //#endregion
66234
66234
  //#region package.json
66235
- var version$1 = "0.13.28";
66235
+ var version$1 = "0.13.30";
66236
66236
  //#endregion
66237
66237
  //#region src/sentry.ts
66238
66238
  const SERVER_APP_NAME = "forge-server";
@@ -93877,7 +93877,7 @@ const launchDetached = (launch) => require_Schema$1.gen(function* () {
93877
93877
  });
93878
93878
  const make$18 = require_Schema$1.gen(function* () {
93879
93879
  const open = yield* require_Schema$1.tryPromise({
93880
- try: () => Promise.resolve().then(() => require("./open-BAsPVCWX.cjs")),
93880
+ try: () => Promise.resolve().then(() => require("./open-CghsF25U.cjs")),
93881
93881
  catch: (cause) => new OpenError$1({
93882
93882
  message: "failed to load browser opener",
93883
93883
  cause
@@ -118470,8 +118470,8 @@ require_Schema$1.effectDiscard(runMigrations());
118470
118470
  //#endregion
118471
118471
  //#region src/persistence/Layers/Sqlite.ts
118472
118472
  const defaultSqliteClientLoaders = {
118473
- bun: () => Promise.resolve().then(() => require("./SqliteClient-O1ABIngw.cjs")),
118474
- node: () => Promise.resolve().then(() => require("./NodeSqliteClient-CfsdC3AB.cjs"))
118473
+ bun: () => Promise.resolve().then(() => require("./SqliteClient-k0pbfeIC.cjs")),
118474
+ node: () => Promise.resolve().then(() => require("./NodeSqliteClient-CPsJxxdD.cjs"))
118475
118475
  };
118476
118476
  const makeRuntimeSqliteLayer = require_Schema$1.fn("makeRuntimeSqliteLayer")(function* (config) {
118477
118477
  const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
@@ -239171,24 +239171,6 @@ var BrowserActionEventRecorder = class {
239171
239171
  }
239172
239172
  };
239173
239173
  //#endregion
239174
- //#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/core/settle.js
239175
- async function settleValueWithin(promise, timeoutMs) {
239176
- let timer;
239177
- try {
239178
- return await Promise.race([promise.then((value) => ({
239179
- status: "fulfilled",
239180
- value
239181
- }), (error) => ({
239182
- error,
239183
- status: "rejected"
239184
- })), new Promise((resolve) => {
239185
- timer = setTimeout(() => resolve({ status: "timed-out" }), timeoutMs);
239186
- })]);
239187
- } finally {
239188
- clearTimeout(timer);
239189
- }
239190
- }
239191
- //#endregion
239192
239174
  //#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/safety/policy.js
239193
239175
  var DefaultSafetyPolicy = class {
239194
239176
  evaluate(action) {
@@ -239241,7 +239223,7 @@ function isBoundedControlAction(action) {
239241
239223
  return action.kind === "cua" || action.kind === "dom_cua" || action.kind === "viewport";
239242
239224
  }
239243
239225
  async function settleControlOperation(promise, action) {
239244
- const result = await settleValueWithin(promise, CONTROL_ACTION_TIMEOUT_MS);
239226
+ const result = await require_settle.settleValueWithin(promise, CONTROL_ACTION_TIMEOUT_MS);
239245
239227
  if (result.status === "rejected") throw result.error;
239246
239228
  if (result.status === "timed-out") console.warn(`[agent-browser] ${action.kind} action did not settle within ${CONTROL_ACTION_TIMEOUT_MS}ms; continuing so the shared browser control channel stays live.`);
239247
239229
  }
@@ -239403,7 +239385,7 @@ var AgentBrowser = class {
239403
239385
  async clickAction(action) {
239404
239386
  await this.run({
239405
239387
  kind: "click",
239406
- selector: locatorActionLabel$1(action)
239388
+ selector: locatorActionLabel(action)
239407
239389
  }, async (tab) => {
239408
239390
  await tab.click(action);
239409
239391
  });
@@ -239415,7 +239397,7 @@ var AgentBrowser = class {
239415
239397
  async doubleClickAction(action) {
239416
239398
  await this.run({
239417
239399
  kind: "click",
239418
- selector: locatorActionLabel$1(action)
239400
+ selector: locatorActionLabel(action)
239419
239401
  }, async (tab) => {
239420
239402
  await tab.doubleClick(action);
239421
239403
  });
@@ -239427,7 +239409,7 @@ var AgentBrowser = class {
239427
239409
  async fillAction(action, value) {
239428
239410
  await this.run({
239429
239411
  kind: "fill",
239430
- selector: locatorActionLabel$1(action),
239412
+ selector: locatorActionLabel(action),
239431
239413
  value
239432
239414
  }, async (tab) => {
239433
239415
  await tab.fill(action, value);
@@ -239440,7 +239422,7 @@ var AgentBrowser = class {
239440
239422
  async typeAction(action, value) {
239441
239423
  await this.run({
239442
239424
  kind: "fill",
239443
- selector: locatorActionLabel$1(action),
239425
+ selector: locatorActionLabel(action),
239444
239426
  value
239445
239427
  }, async (tab) => {
239446
239428
  await tab.type(action, value);
@@ -239453,7 +239435,7 @@ var AgentBrowser = class {
239453
239435
  async pressAction(action, key) {
239454
239436
  await this.run({
239455
239437
  kind: "press",
239456
- selector: locatorActionLabel$1(action),
239438
+ selector: locatorActionLabel(action),
239457
239439
  key
239458
239440
  }, async (tab) => {
239459
239441
  await tab.press(action, key);
@@ -239466,7 +239448,7 @@ var AgentBrowser = class {
239466
239448
  async selectAction(action, value) {
239467
239449
  await this.run({
239468
239450
  kind: "select",
239469
- selector: locatorActionLabel$1(action),
239451
+ selector: locatorActionLabel(action),
239470
239452
  value
239471
239453
  }, async (tab) => {
239472
239454
  await tab.select(action, value);
@@ -239479,7 +239461,7 @@ var AgentBrowser = class {
239479
239461
  async checkAction(action, checked) {
239480
239462
  await this.run({
239481
239463
  kind: "click",
239482
- selector: locatorActionLabel$1(action),
239464
+ selector: locatorActionLabel(action),
239483
239465
  value: String(checked)
239484
239466
  }, async (tab) => {
239485
239467
  await tab.check(action, checked);
@@ -239655,7 +239637,7 @@ var AgentBrowser = class {
239655
239637
  }
239656
239638
  async observeCurrentTab() {
239657
239639
  const tab = await this.requireTab();
239658
- const [info, snapshot] = await Promise.all([settleValueWithin(Promise.resolve(tab.info()), POST_ACTION_OBSERVATION_TIMEOUT_MS), settleValueWithin(Promise.resolve(tab.domSnapshot()), POST_ACTION_OBSERVATION_TIMEOUT_MS)]);
239640
+ const [info, snapshot] = await Promise.all([require_settle.settleValueWithin(Promise.resolve(tab.info()), POST_ACTION_OBSERVATION_TIMEOUT_MS), require_settle.settleValueWithin(Promise.resolve(tab.domSnapshot()), POST_ACTION_OBSERVATION_TIMEOUT_MS)]);
239659
239641
  return {
239660
239642
  snapshot: snapshot.status === "fulfilled" ? snapshot.value : "",
239661
239643
  tab: info.status === "fulfilled" ? info.value : { id: tab.id }
@@ -239715,7 +239697,7 @@ function locatorAction(selector, timeoutMs) {
239715
239697
  timeoutMs
239716
239698
  };
239717
239699
  }
239718
- function locatorActionLabel$1(action) {
239700
+ function locatorActionLabel(action) {
239719
239701
  return action.selector ?? action.query?.kind ?? "locator";
239720
239702
  }
239721
239703
  //#endregion
@@ -240822,640 +240804,6 @@ function isPromiseLike(value) {
240822
240804
  return !!value && typeof value.then === "function";
240823
240805
  }
240824
240806
  //#endregion
240825
- //#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/backends/playwright-locators.js
240826
- function locatorForAction(page, action) {
240827
- return action.query ? locatorFromQuery(page, action.query) : page.locator(action.selector ?? "");
240828
- }
240829
- function locatorFromQuery(page, query) {
240830
- switch (query.kind) {
240831
- case "and": return locatorFromQuery(page, query.left).and(locatorFromQuery(page, query.right));
240832
- case "css": return page.locator(query.selector);
240833
- case "filter": return locatorFromQuery(page, query.base).filter(filterOptions(page, query.options));
240834
- case "first": return locatorFromQuery(page, query.base).first();
240835
- case "frame": return locatorInFrame(page, page.frameLocator(query.selector), query.child);
240836
- case "label": return page.getByLabel(query.text, query.options);
240837
- case "last": return locatorFromQuery(page, query.base).last();
240838
- case "locator": return locatorFromQuery(page, query.base).locator(query.selector, filterOptions(page, query.options));
240839
- case "nth": return locatorFromQuery(page, query.base).nth(query.index);
240840
- case "or": return locatorFromQuery(page, query.left).or(locatorFromQuery(page, query.right));
240841
- case "placeholder": return page.getByPlaceholder(query.text, query.options);
240842
- case "role": return page.getByRole(query.role, query.options);
240843
- case "testId": return page.getByTestId(query.testId);
240844
- case "text": return page.getByText(query.text, query.options);
240845
- }
240846
- }
240847
- function locatorInFrame(page, frame, query) {
240848
- switch (query.kind) {
240849
- case "css": return frame.locator(query.selector);
240850
- case "filter": return locatorInFrame(page, frame, query.base).filter(filterOptions(page, query.options));
240851
- case "first": return locatorInFrame(page, frame, query.base).first();
240852
- case "label": return frame.getByLabel(query.text, query.options);
240853
- case "last": return locatorInFrame(page, frame, query.base).last();
240854
- case "locator": return locatorInFrame(page, frame, query.base).locator(query.selector, filterOptions(page, query.options));
240855
- case "nth": return locatorInFrame(page, frame, query.base).nth(query.index);
240856
- case "placeholder": return frame.getByPlaceholder(query.text, query.options);
240857
- case "role": return frame.getByRole(query.role, query.options);
240858
- case "testId": return frame.getByTestId(query.testId);
240859
- case "text": return frame.getByText(query.text, query.options);
240860
- case "and":
240861
- case "frame":
240862
- case "or": throw new Error(`Unsupported frame locator query kind: ${query.kind}`);
240863
- }
240864
- }
240865
- function filterOptions(page, options) {
240866
- if (!options) return void 0;
240867
- return {
240868
- ...options.has === void 0 ? {} : { has: locatorFromQuery(page, options.has) },
240869
- ...options.hasNot === void 0 ? {} : { hasNot: locatorFromQuery(page, options.hasNot) },
240870
- ...options.hasNotText === void 0 ? {} : { hasNotText: options.hasNotText },
240871
- ...options.hasText === void 0 ? {} : { hasText: options.hasText }
240872
- };
240873
- }
240874
- //#endregion
240875
- //#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/backends/playwright-backend.js
240876
- const TAB_METADATA_TIMEOUT_MS = 250;
240877
- const TAB_CONTROL_TIMEOUT_MS = 500;
240878
- var PlaywrightBackend = class {
240879
- options;
240880
- browser;
240881
- context;
240882
- pageIds = /* @__PURE__ */ new WeakMap();
240883
- logs = /* @__PURE__ */ new WeakMap();
240884
- registeredPages = /* @__PURE__ */ new WeakSet();
240885
- constructor(options = {}) {
240886
- this.options = options;
240887
- }
240888
- info() {
240889
- return {
240890
- id: "playwright",
240891
- kind: "playwright",
240892
- name: this.options.name ?? "Playwright Chromium"
240893
- };
240894
- }
240895
- async close() {
240896
- /* v8 ignore next -- defensive cleanup for already-closed Playwright contexts. */
240897
- await this.context?.close().catch(() => void 0);
240898
- await this.browser?.close();
240899
- this.browser = void 0;
240900
- this.context = void 0;
240901
- }
240902
- async listTabs() {
240903
- const context = await this.getContext();
240904
- return Promise.all(context.pages().map((page) => this.toTab(page).info()));
240905
- }
240906
- async selectedTab() {
240907
- const page = (await this.getContext()).pages().at(-1);
240908
- if (page) await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
240909
- return page ? this.toTab(page) : void 0;
240910
- }
240911
- async newTab() {
240912
- const page = await (await this.getContext()).newPage();
240913
- await this.preparePage(page);
240914
- await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
240915
- return this.toTab(page);
240916
- }
240917
- async getTab(id) {
240918
- const page = (await this.getContext()).pages().find((candidate) => this.idFor(candidate) === id);
240919
- if (!page) throw new Error(`Tab not found: ${id}`);
240920
- await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
240921
- return this.toTab(page);
240922
- }
240923
- async getContext() {
240924
- /* v8 ignore next -- persistent headed/default launch combinations are covered by smoke, not unit coverage. */
240925
- if (this.options.userDataDir && !this.context) {
240926
- this.context = await playwright.chromium.launchPersistentContext(this.options.userDataDir, {
240927
- ...this.options.launchOptions,
240928
- ...this.options.contextOptions,
240929
- headless: this.options.headless ?? false
240930
- });
240931
- this.browser = this.context.browser() ?? void 0;
240932
- return this.context;
240933
- }
240934
- /* v8 ignore next -- requires a live externally managed Chrome CDP endpoint */
240935
- if (!this.browser && this.options.cdpEndpoint) {
240936
- /* v8 ignore start -- requires a live externally managed Chrome CDP endpoint */
240937
- this.browser = await playwright.chromium.connectOverCDP(this.options.cdpEndpoint);
240938
- this.context = this.browser.contexts()[0] ?? await this.browser.newContext(this.options.contextOptions);
240939
- return this.context;
240940
- }
240941
- /* v8 ignore next -- headed/default launch is covered by browser parity smoke. */
240942
- if (!this.browser) this.browser = await playwright.chromium.launch({
240943
- ...this.options.launchOptions,
240944
- headless: this.options.headless ?? false
240945
- });
240946
- if (!this.context) this.context = await this.browser.newContext(this.options.contextOptions);
240947
- return this.context;
240948
- }
240949
- toTab(page) {
240950
- this.registerPage(page);
240951
- return new PlaywrightTabHandle(page, this.idFor(page), this.logsFor(page));
240952
- }
240953
- async preparePage(page) {
240954
- this.registerPage(page);
240955
- const bounds = this.options.windowBounds;
240956
- if (!bounds) return;
240957
- await settleValueWithin(page.setViewportSize({
240958
- width: bounds.width,
240959
- height: bounds.height
240960
- }), TAB_CONTROL_TIMEOUT_MS);
240961
- const sessionResult = await settleValueWithin(page.context().newCDPSession(page), TAB_CONTROL_TIMEOUT_MS);
240962
- /* v8 ignore next -- Playwright CDP attachment failures are bounded and covered by the shared timeout primitive. */
240963
- if (sessionResult.status !== "fulfilled") return;
240964
- const session = sessionResult.value;
240965
- try {
240966
- const windowResult = await settleValueWithin(session.send("Browser.getWindowForTarget"), TAB_CONTROL_TIMEOUT_MS);
240967
- /* v8 ignore next -- Chromium may decline window control in unsupported headless/CDP environments. */
240968
- if (windowResult.status !== "fulfilled" || typeof windowResult.value.windowId !== "number") return;
240969
- await settleValueWithin(session.send("Browser.setWindowBounds", {
240970
- bounds: {
240971
- height: bounds.height,
240972
- left: bounds.left ?? 0,
240973
- top: bounds.top ?? 0,
240974
- width: bounds.width,
240975
- windowState: "normal"
240976
- },
240977
- windowId: windowResult.value.windowId
240978
- }), TAB_CONTROL_TIMEOUT_MS);
240979
- } finally {
240980
- await settleValueWithin(session.detach(), TAB_CONTROL_TIMEOUT_MS);
240981
- }
240982
- }
240983
- registerPage(page) {
240984
- this.idFor(page);
240985
- this.logsFor(page);
240986
- if (this.registeredPages.has(page)) return;
240987
- this.registeredPages.add(page);
240988
- page.on("console", (message) => {
240989
- this.logsFor(page).push(toConsoleLog(message, page.url()));
240990
- });
240991
- }
240992
- idFor(page) {
240993
- let id = this.pageIds.get(page);
240994
- if (!id) {
240995
- id = `tab_${(0, node_crypto.randomUUID)()}`;
240996
- this.pageIds.set(page, id);
240997
- }
240998
- return id;
240999
- }
241000
- logsFor(page) {
241001
- let entries = this.logs.get(page);
241002
- if (!entries) {
241003
- entries = [];
241004
- this.logs.set(page, entries);
241005
- }
241006
- return entries;
241007
- }
241008
- };
241009
- var PlaywrightTabHandle = class {
241010
- page;
241011
- id;
241012
- logEntries;
241013
- constructor(page, id, logEntries) {
241014
- this.page = page;
241015
- this.id = id;
241016
- this.logEntries = logEntries;
241017
- }
241018
- async info() {
241019
- const [title, history] = await Promise.all([settleValueWithin(this.page.title(), TAB_METADATA_TIMEOUT_MS), settleValueWithin(this.historyState(), TAB_METADATA_TIMEOUT_MS)]);
241020
- const base = title.status !== "fulfilled" ? {
241021
- id: this.id,
241022
- url: this.page.url()
241023
- } : {
241024
- id: this.id,
241025
- title: title.value,
241026
- url: this.page.url()
241027
- };
241028
- /* v8 ignore next -- metadata timeout fallback is covered through AgentBrowser's bounded observation contract. */
241029
- return history.status === "fulfilled" ? {
241030
- ...base,
241031
- ...history.value
241032
- } : base;
241033
- }
241034
- async historyState() {
241035
- const sessionResult = await settleValueWithin(this.page.context().newCDPSession(this.page), TAB_METADATA_TIMEOUT_MS);
241036
- /* v8 ignore next -- Playwright CDP attachment failures are bounded and covered by the shared timeout primitive. */
241037
- if (sessionResult.status !== "fulfilled") return {};
241038
- const session = sessionResult.value;
241039
- try {
241040
- const historyResult = await settleValueWithin(session.send("Page.getNavigationHistory"), TAB_METADATA_TIMEOUT_MS);
241041
- /* v8 ignore next -- Chromium navigation-history failures are bounded external CDP behavior. */
241042
- if (historyResult.status !== "fulfilled") return {};
241043
- const history = historyResult.value;
241044
- return {
241045
- canGoBack: history.currentIndex > 0,
241046
- canGoForward: history.currentIndex < history.entries.length - 1
241047
- };
241048
- } finally {
241049
- await settleValueWithin(session.detach(), TAB_METADATA_TIMEOUT_MS);
241050
- }
241051
- }
241052
- async goto(url) {
241053
- await this.page.goto(url, { waitUntil: "domcontentloaded" });
241054
- }
241055
- async reload() {
241056
- await this.page.reload({ waitUntil: "domcontentloaded" });
241057
- }
241058
- async forceReload() {
241059
- const client = await this.page.context().newCDPSession(this.page);
241060
- try {
241061
- await client.send("Page.reload", { ignoreCache: true });
241062
- /* v8 ignore next -- defensive fallback for navigations that do not settle after CDP reload. */
241063
- await this.page.waitForLoadState("domcontentloaded", { timeout: 3e4 }).catch(() => void 0);
241064
- } finally {
241065
- await client.detach();
241066
- }
241067
- }
241068
- async back() {
241069
- await this.page.goBack({ waitUntil: "domcontentloaded" });
241070
- }
241071
- async forward() {
241072
- await this.page.goForward({ waitUntil: "domcontentloaded" });
241073
- }
241074
- async close() {
241075
- await this.page.close();
241076
- }
241077
- async screenshot(options = {}) {
241078
- const bytes = await this.page.screenshot({
241079
- ...options.fullPage === void 0 ? {} : { fullPage: options.fullPage },
241080
- ...options.clip === void 0 ? {} : { clip: options.clip },
241081
- ...options.mimeType === void 0 ? {} : { type: options.mimeType === "image/jpeg" ? "jpeg" : "png" },
241082
- ...options.quality === void 0 ? {} : { quality: options.quality }
241083
- });
241084
- if (options.maxBytes !== void 0 && bytes.byteLength > options.maxBytes) throw new Error(`Browser screenshot exceeded maxBytes=${options.maxBytes}. size=${bytes.byteLength}`);
241085
- return bytes;
241086
- }
241087
- async setViewport(size) {
241088
- await this.page.setViewportSize({
241089
- width: Math.round(size.width),
241090
- height: Math.round(size.height)
241091
- });
241092
- }
241093
- async setZoom(zoom) {
241094
- /* v8 ignore next 3 -- browser-executed callback runs outside Node coverage */
241095
- await this.page.evaluate((level) => {
241096
- document.documentElement.style.zoom = String(level);
241097
- }, zoom.level);
241098
- }
241099
- async clearCookies() {
241100
- await this.page.context().clearCookies();
241101
- }
241102
- async clearCache() {
241103
- const client = await this.page.context().newCDPSession(this.page);
241104
- try {
241105
- await client.send("Network.clearBrowserCache");
241106
- } finally {
241107
- await client.detach();
241108
- }
241109
- }
241110
- async domSnapshot() {
241111
- return this.page.locator("body").ariaSnapshot({ timeout: 5e3 });
241112
- }
241113
- async evaluate(expression, arg) {
241114
- /* v8 ignore next 4 -- browser-executed callback runs outside Node coverage */
241115
- return this.page.evaluate((input) => {
241116
- return new Function("arg", `return (${input.expression})(arg);`)(input.arg);
241117
- }, {
241118
- arg,
241119
- expression
241120
- });
241121
- }
241122
- async waitForLoadState(state = "load", timeoutMs) {
241123
- await this.page.waitForLoadState(state, timeoutOptions(timeoutMs));
241124
- }
241125
- async waitForURL(url, timeoutMs) {
241126
- await this.page.waitForURL(url, timeoutOptions(timeoutMs));
241127
- }
241128
- async waitForDownload(timeoutMs) {
241129
- return toBrowserDownload(await this.page.waitForEvent("download", timeoutOptions(timeoutMs)));
241130
- }
241131
- async elementInfo(options) {
241132
- /* v8 ignore start -- browser-executed DOM inspection runs outside Node coverage */
241133
- return this.page.evaluate(({ x, y, includeNonInteractable }) => {
241134
- const element = document.elementFromPoint(x, y);
241135
- if (!element || element === document.documentElement || element === document.body) return;
241136
- const rect = element.getBoundingClientRect();
241137
- if (rect.width <= 0 || rect.height <= 0) return;
241138
- const style = window.getComputedStyle(element);
241139
- if (!includeNonInteractable && (style.visibility === "hidden" || style.display === "none" || style.pointerEvents === "none")) return;
241140
- const tagName = element.tagName.toLowerCase();
241141
- const visibleText = visibleElementText(element);
241142
- const ariaName = element.getAttribute("aria-label") ?? element.getAttribute("alt") ?? void 0;
241143
- const role = element.getAttribute("role") ?? inferRole(element);
241144
- const candidates = selectorCandidates(element);
241145
- const testId = element.getAttribute("data-testid") ?? element.getAttribute("data-test-id") ?? void 0;
241146
- const nodeId = ensureNodeId(element);
241147
- const preview = compactText(`${tagName}${role ? ` role=${role}` : ""}${ariaName ? ` aria="${ariaName}"` : ""}${visibleText ? ` text="${visibleText}"` : ""}`);
241148
- return {
241149
- ...ariaName ? { ariaName } : {},
241150
- boundingBox: {
241151
- x: rect.x,
241152
- y: rect.y,
241153
- width: rect.width,
241154
- height: rect.height
241155
- },
241156
- nodeId,
241157
- preview,
241158
- ...role ? { role } : {},
241159
- selector: {
241160
- candidates,
241161
- primary: candidates[0] ?? null
241162
- },
241163
- tagName,
241164
- ...testId ? { testId } : {},
241165
- ...visibleText ? { visibleText } : {}
241166
- };
241167
- function compactText(value) {
241168
- return value.replace(/\s+/g, " ").trim().slice(0, 180);
241169
- }
241170
- function visibleElementText(node) {
241171
- if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) return compactText(node.value || node.placeholder || "");
241172
- if (node instanceof HTMLSelectElement) return compactText(node.selectedOptions[0]?.textContent ?? node.value);
241173
- return compactText(node.textContent ?? "") || void 0;
241174
- }
241175
- function inferRole(node) {
241176
- if (node instanceof HTMLAnchorElement && node.href) return "link";
241177
- if (node instanceof HTMLButtonElement) return "button";
241178
- if (node instanceof HTMLInputElement) return node.type || "textbox";
241179
- if (node instanceof HTMLSelectElement) return "combobox";
241180
- if (node instanceof HTMLTextAreaElement) return "textbox";
241181
- if (/^h[1-6]$/i.test(node.tagName)) return "heading";
241182
- }
241183
- function ensureNodeId(node) {
241184
- const existing = node.getAttribute("data-agent-browser-node-id");
241185
- if (existing) return Number(existing);
241186
- const next = Number(document.documentElement.getAttribute("data-agent-browser-next-node-id") ?? "1");
241187
- document.documentElement.setAttribute("data-agent-browser-next-node-id", String(next + 1));
241188
- node.setAttribute("data-agent-browser-node-id", String(next));
241189
- return next;
241190
- }
241191
- function selectorCandidates(node) {
241192
- const candidates = [];
241193
- if (node.id) candidates.push(`#${cssEscape(node.id)}`);
241194
- const testId = node.getAttribute("data-testid") ?? node.getAttribute("data-test-id");
241195
- if (testId) candidates.push(`[data-testid="${cssString(testId)}"]`);
241196
- const href = node.getAttribute("href");
241197
- if (href) candidates.push(`${node.tagName.toLowerCase()}[href="${cssString(href)}"]`);
241198
- const aria = node.getAttribute("aria-label");
241199
- if (aria) candidates.push(`${node.tagName.toLowerCase()}[aria-label="${cssString(aria)}"]`);
241200
- const name = node.getAttribute("name");
241201
- if (name) candidates.push(`${node.tagName.toLowerCase()}[name="${cssString(name)}"]`);
241202
- const parts = [];
241203
- let current = node;
241204
- while (current && current !== document.body && current !== document.documentElement && parts.length < 5) {
241205
- let part = current.tagName.toLowerCase();
241206
- const name = current.getAttribute("name");
241207
- if (name) part += `[name="${cssString(name)}"]`;
241208
- else {
241209
- const className = Array.from(current.classList).find(Boolean);
241210
- if (className) part += `.${cssEscape(className)}`;
241211
- }
241212
- const parent = current.parentElement;
241213
- if (parent) {
241214
- const siblings = Array.from(parent.children).filter((sibling) => sibling.tagName === current?.tagName);
241215
- if (siblings.length > 1) part += `:nth-of-type(${siblings.indexOf(current) + 1})`;
241216
- }
241217
- parts.unshift(part);
241218
- const candidate = parts.join(" > ");
241219
- if (document.querySelectorAll(candidate).length === 1) {
241220
- candidates.push(candidate);
241221
- break;
241222
- }
241223
- current = parent;
241224
- }
241225
- const fallback = parts.join(" > ") || node.tagName.toLowerCase();
241226
- candidates.push(fallback);
241227
- return Array.from(new Set(candidates));
241228
- }
241229
- function cssEscape(value) {
241230
- return CSS.escape(value);
241231
- }
241232
- function cssString(value) {
241233
- return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
241234
- }
241235
- }, options);
241236
- /* v8 ignore stop */
241237
- }
241238
- async elementScreenshot(options) {
241239
- const element = await this.elementInfo(options);
241240
- if (!element?.boundingBox) return void 0;
241241
- return this.screenshot({ clip: element.boundingBox });
241242
- }
241243
- async click(action) {
241244
- const locator = this.locatorFor(action);
241245
- await assertSingle(locator.count(), locatorActionLabel(action));
241246
- await locator.click(timeoutOptions(action.timeoutMs));
241247
- }
241248
- async doubleClick(action) {
241249
- const locator = this.locatorFor(action);
241250
- await assertSingle(locator.count(), locatorActionLabel(action));
241251
- await locator.dblclick(timeoutOptions(action.timeoutMs));
241252
- }
241253
- async fill(action, value) {
241254
- const locator = this.locatorFor(action);
241255
- await assertSingle(locator.count(), locatorActionLabel(action));
241256
- await locator.fill(value, timeoutOptions(action.timeoutMs));
241257
- }
241258
- async type(action, value) {
241259
- const locator = this.locatorFor(action);
241260
- await assertSingle(locator.count(), locatorActionLabel(action));
241261
- await locator.pressSequentially(value, timeoutOptions(action.timeoutMs));
241262
- }
241263
- async press(action, key) {
241264
- const locator = this.locatorFor(action);
241265
- await assertSingle(locator.count(), locatorActionLabel(action));
241266
- await locator.press(key, timeoutOptions(action.timeoutMs));
241267
- }
241268
- async select(action, value) {
241269
- const locator = this.locatorFor(action);
241270
- await assertSingle(locator.count(), locatorActionLabel(action));
241271
- await locator.selectOption(value, timeoutOptions(action.timeoutMs));
241272
- }
241273
- async check(action, checked) {
241274
- const locator = this.locatorFor(action);
241275
- await assertSingle(locator.count(), locatorActionLabel(action));
241276
- await locator.setChecked(checked, timeoutOptions(action.timeoutMs));
241277
- }
241278
- async text(action) {
241279
- const locator = this.locatorFor(action);
241280
- await assertSingle(locator.count(), locatorActionLabel(action));
241281
- return locator.textContent(timeoutOptions(action.timeoutMs));
241282
- }
241283
- async innerText(action) {
241284
- const locator = this.locatorFor(action);
241285
- await assertSingle(locator.count(), locatorActionLabel(action));
241286
- return locator.innerText(timeoutOptions(action.timeoutMs));
241287
- }
241288
- async allTextContents(action) {
241289
- return this.locatorFor(action).allTextContents();
241290
- }
241291
- async attribute(action, name) {
241292
- const locator = this.locatorFor(action);
241293
- await assertSingle(locator.count(), locatorActionLabel(action));
241294
- return locator.getAttribute(name, timeoutOptions(action.timeoutMs));
241295
- }
241296
- async isVisible(action) {
241297
- const locator = this.locatorFor(action);
241298
- await assertSingle(locator.count(), locatorActionLabel(action));
241299
- return locator.isVisible(timeoutOptions(action.timeoutMs));
241300
- }
241301
- async isEnabled(action) {
241302
- const locator = this.locatorFor(action);
241303
- await assertSingle(locator.count(), locatorActionLabel(action));
241304
- return locator.isEnabled(timeoutOptions(action.timeoutMs));
241305
- }
241306
- async locatorCount(action) {
241307
- return this.locatorFor(action).count();
241308
- }
241309
- async waitForLocator(action, state) {
241310
- await this.locatorFor(action).waitFor({
241311
- ...state === void 0 ? {} : { state },
241312
- ...timeoutOptions(action.timeoutMs)
241313
- });
241314
- }
241315
- async cuaMove(point) {
241316
- await this.page.mouse.move(point.x, point.y);
241317
- }
241318
- async cuaClick(point) {
241319
- await this.page.mouse.click(point.x, point.y, { button: mouseButton(point.button) });
241320
- }
241321
- async cuaDoubleClick(point) {
241322
- await this.page.mouse.dblclick(point.x, point.y);
241323
- }
241324
- async cuaDrag(path) {
241325
- const [start, ...rest] = path;
241326
- if (!start) return;
241327
- await this.page.mouse.move(start.x, start.y);
241328
- await this.page.mouse.down();
241329
- for (const point of rest) await this.page.mouse.move(point.x, point.y);
241330
- await this.page.mouse.up();
241331
- }
241332
- async cuaScroll(input) {
241333
- await this.page.mouse.move(input.x, input.y);
241334
- await this.page.mouse.wheel(input.scrollX, input.scrollY);
241335
- }
241336
- async cuaType(text) {
241337
- await this.page.keyboard.type(text);
241338
- }
241339
- async cuaKeypress(keys) {
241340
- await this.page.keyboard.press(keys.join("+"));
241341
- }
241342
- async visibleDom() {
241343
- return this.page.evaluate(`(() => {
241344
- let nextId = Number(document.documentElement.getAttribute("data-agent-browser-next-node-id") || "1");
241345
- function walk(element) {
241346
- const rect = element.getBoundingClientRect();
241347
- const style = window.getComputedStyle(element);
241348
- if (rect.width <= 0 || rect.height <= 0 || style.display === "none" || style.visibility === "hidden") {
241349
- return undefined;
241350
- }
241351
- let nodeId = element.getAttribute("data-agent-browser-node-id");
241352
- if (!nodeId) {
241353
- nodeId = String(nextId++);
241354
- element.setAttribute("data-agent-browser-node-id", nodeId);
241355
- }
241356
- const tagName = element.tagName.toLowerCase();
241357
- const visibleText = (element.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 120) || undefined;
241358
- const role = element.getAttribute("role") ?? undefined;
241359
- const ariaName = element.getAttribute("aria-label") ?? undefined;
241360
- const children = Array.from(element.children).map(walk).filter(Boolean);
241361
- return {
241362
- ...(ariaName ? { ariaName } : {}),
241363
- boundingBox: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
241364
- ...(children.length ? { children } : {}),
241365
- nodeId,
241366
- preview: (tagName + (ariaName ? ' "' + ariaName + '"' : "") + (visibleText ? " " + visibleText : "")).slice(0, 180),
241367
- ...(role ? { role } : {}),
241368
- tagName,
241369
- ...(visibleText ? { visibleText } : {})
241370
- };
241371
- }
241372
- document.documentElement.setAttribute("data-agent-browser-next-node-id", String(nextId));
241373
- return walk(document.body) ?? { nodeId: "body", preview: "body", tagName: "body" };
241374
- })()`);
241375
- }
241376
- async domClick(nodeId) {
241377
- await this.byNodeId(nodeId).click();
241378
- }
241379
- async domDoubleClick(nodeId) {
241380
- await this.byNodeId(nodeId).dblclick();
241381
- }
241382
- async domScroll(input) {
241383
- if (input.nodeId) {
241384
- /* v8 ignore next -- browser-executed DOM callback runs outside Node coverage */
241385
- await this.byNodeId(input.nodeId).evaluate((element, delta) => element.scrollBy(delta.x, delta.y), {
241386
- x: input.x,
241387
- y: input.y
241388
- });
241389
- return;
241390
- }
241391
- await this.page.mouse.wheel(input.x, input.y);
241392
- }
241393
- async domType(text) {
241394
- await this.page.keyboard.type(text);
241395
- }
241396
- async domKeypress(keys) {
241397
- await this.page.keyboard.press(keys.join("+"));
241398
- }
241399
- async clipboardReadText() {
241400
- /* v8 ignore next -- browser-executed callback runs outside Node coverage */
241401
- return this.page.evaluate(() => navigator.clipboard.readText());
241402
- }
241403
- async clipboardWriteText(text) {
241404
- /* v8 ignore next -- browser-executed callback runs outside Node coverage */
241405
- await this.page.evaluate((value) => navigator.clipboard.writeText(value), text);
241406
- }
241407
- async logs(limit = 100) {
241408
- return this.logEntries.slice(-limit);
241409
- }
241410
- byNodeId(nodeId) {
241411
- return this.page.locator(`[data-agent-browser-node-id="${cssStringForSelector(nodeId)}"]`);
241412
- }
241413
- locatorFor(action) {
241414
- return locatorForAction(this.page, action);
241415
- }
241416
- };
241417
- function locatorActionLabel(action) {
241418
- /* v8 ignore next -- valid public calls always supply selector or query; malformed calls are rejected downstream. */
241419
- return action.selector ?? action.query?.kind ?? "locator";
241420
- }
241421
- function mouseButton(button) {
241422
- if (button === 2) return "middle";
241423
- if (button === 3) return "right";
241424
- return "left";
241425
- }
241426
- function cssStringForSelector(value) {
241427
- return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
241428
- }
241429
- function timeoutOptions(timeoutMs) {
241430
- return timeoutMs === void 0 ? {} : { timeout: timeoutMs };
241431
- }
241432
- function toBrowserDownload(download) {
241433
- return {
241434
- failure: () => download.failure(),
241435
- path: () => download.path().catch(() => null),
241436
- saveAs: (path) => download.saveAs(path),
241437
- suggestedFilename: () => download.suggestedFilename(),
241438
- url: () => download.url()
241439
- };
241440
- }
241441
- async function assertSingle(countPromise, selector) {
241442
- const count = await countPromise;
241443
- if (count !== 1) throw new Error(`Selector must resolve to exactly one element. selector=${selector} count=${count}`);
241444
- }
241445
- function toConsoleLog(message, url) {
241446
- return {
241447
- level: normalizeLevel(message.type()),
241448
- message: message.text(),
241449
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
241450
- url
241451
- };
241452
- }
241453
- function normalizeLevel(level) {
241454
- if (level === "debug" || level === "info" || level === "log" || level === "warn" || level === "error") return level;
241455
- /* v8 ignore next -- Playwright currently normalizes known console message types */
241456
- return "log";
241457
- }
241458
- //#endregion
241459
240807
  //#region ../../node_modules/.bun/debug@4.4.0+2b91fc17bf64bdfd/node_modules/debug/src/common.js
241460
240808
  var require_common$2 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
241461
240809
  /**
@@ -284599,84 +283947,6 @@ function browserProfileSessionKey(sessionId) {
284599
283947
  function browserProfileDirForSession(sessionId, root = AGENT_BROWSER_PROFILE_ROOT) {
284600
283948
  return `${root}/${browserProfileSessionKey(sessionId)}`;
284601
283949
  }
284602
- const HOST_BRIDGE_RECONNECT_MAX_MS = 1e3;
284603
- /**
284604
- * Exponential backoff, capped.
284605
- *
284606
- * Pulled out of the retry loop so the curve can be asserted directly. Inline, it
284607
- * was only ever exercised by waiting for a real reconnect.
284608
- */
284609
- function hostBridgeReconnectDelayMs(attempt, baseMs = 25, maxMs = HOST_BRIDGE_RECONNECT_MAX_MS) {
284610
- return Math.min(baseMs * 2 ** attempt, maxMs);
284611
- }
284612
- /** Bearer header, in one place so no call site invents its own casing. */
284613
- function hostBridgeAuthHeaders(token) {
284614
- return { authorization: `Bearer ${token}` };
284615
- }
284616
- /** Join a path onto a base URL, tolerating a base with or without a trailing slash. */
284617
- function hostBridgePathUrl(baseUrl, path) {
284618
- return new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
284619
- }
284620
- function hostBridgeQueryUrl(baseUrl, path, query) {
284621
- const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
284622
- for (const [key, value] of Object.entries(query)) if (value) url.searchParams.set(key, value);
284623
- return url.toString();
284624
- }
284625
- /**
284626
- * Decode an error body from a fetch Response.
284627
- *
284628
- * Prefers a JSON `error` field, falls back to the raw body, then to the status
284629
- * text. A server that returns HTML on failure would otherwise surface as an
284630
- * empty message.
284631
- */
284632
- async function readHostBridgeError(response) {
284633
- const body = await response.text().catch(() => "");
284634
- if (!body) return response.statusText || "Request failed.";
284635
- try {
284636
- const parsed = JSON.parse(body);
284637
- if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
284638
- } catch {}
284639
- return body.trim() || response.statusText || "Request failed.";
284640
- }
284641
- /** Collect a Node response body. Buffers because the bodies here are error text. */
284642
- async function readHostBridgeNodeResponseBody(response) {
284643
- const chunks = [];
284644
- for await (const chunk of response) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
284645
- return Buffer.concat(chunks).toString("utf8");
284646
- }
284647
- function formatHostBridgeNodeResponseError(statusCode, statusMessage, body, prefix) {
284648
- const trimmedBody = body.trim();
284649
- if (trimmedBody.length > 0) try {
284650
- const parsed = JSON.parse(trimmedBody);
284651
- if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
284652
- } catch {}
284653
- const fallback = trimmedBody.length > 0 ? trimmedBody : statusMessage?.trim() || "Request failed.";
284654
- return `${prefix}: ${statusCode ?? 0} ${fallback}`;
284655
- }
284656
- /** An abort that callers can recognise by name rather than by message matching. */
284657
- function hostBridgeAbortError(message) {
284658
- const error = new Error(message);
284659
- error.name = "AbortError";
284660
- return error;
284661
- }
284662
- /**
284663
- * Drain complete NDJSON lines, returning what is left over.
284664
- *
284665
- * The remainder is the whole point: a stream chunk ends wherever the socket
284666
- * decides, so the trailing line is usually incomplete and must be carried into
284667
- * the next chunk rather than parsed.
284668
- */
284669
- function drainHostBridgeNdjsonBuffer(buffer, callback, parseLine = (line) => JSON.parse(line)) {
284670
- let nextBuffer = buffer;
284671
- let newlineIndex = nextBuffer.indexOf("\n");
284672
- while (newlineIndex >= 0) {
284673
- const line = nextBuffer.slice(0, newlineIndex).trim();
284674
- nextBuffer = nextBuffer.slice(newlineIndex + 1);
284675
- if (line.length > 0) callback(parseLine(line));
284676
- newlineIndex = nextBuffer.indexOf("\n");
284677
- }
284678
- return nextBuffer;
284679
- }
284680
283950
  //#endregion
284681
283951
  //#region src/browser/Layers/BrowserOrphanReaper.ts
284682
283952
  /**
@@ -285016,6 +284286,301 @@ function browserControlRejectionDetail(decision) {
285016
284286
  case "incomplete_metadata": return "Browser control ordering requires both sessionEpoch and controlSequence.";
285017
284287
  }
285018
284288
  }
284289
+ const HOST_BRIDGE_RECONNECT_MAX_MS = 1e3;
284290
+ /**
284291
+ * Exponential backoff, capped.
284292
+ *
284293
+ * Pulled out of the retry loop so the curve can be asserted directly. Inline, it
284294
+ * was only ever exercised by waiting for a real reconnect.
284295
+ */
284296
+ function hostBridgeReconnectDelayMs(attempt, baseMs = 25, maxMs = HOST_BRIDGE_RECONNECT_MAX_MS) {
284297
+ return Math.min(baseMs * 2 ** attempt, maxMs);
284298
+ }
284299
+ /** Bearer header, in one place so no call site invents its own casing. */
284300
+ function hostBridgeAuthHeaders(token) {
284301
+ return { authorization: `Bearer ${token}` };
284302
+ }
284303
+ /** Join a path onto a base URL, tolerating a base with or without a trailing slash. */
284304
+ function hostBridgePathUrl(baseUrl, path) {
284305
+ return new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
284306
+ }
284307
+ function hostBridgeQueryUrl(baseUrl, path, query) {
284308
+ const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
284309
+ for (const [key, value] of Object.entries(query)) if (value) url.searchParams.set(key, value);
284310
+ return url.toString();
284311
+ }
284312
+ /**
284313
+ * Decode an error body from a fetch Response.
284314
+ *
284315
+ * Prefers a JSON `error` field, falls back to the raw body, then to the status
284316
+ * text. A server that returns HTML on failure would otherwise surface as an
284317
+ * empty message.
284318
+ */
284319
+ async function readHostBridgeError(response) {
284320
+ const body = await response.text().catch(() => "");
284321
+ if (!body) return response.statusText || "Request failed.";
284322
+ try {
284323
+ const parsed = JSON.parse(body);
284324
+ if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
284325
+ } catch {}
284326
+ return body.trim() || response.statusText || "Request failed.";
284327
+ }
284328
+ /** Collect a Node response body. Buffers because the bodies here are error text. */
284329
+ async function readHostBridgeNodeResponseBody(response) {
284330
+ const chunks = [];
284331
+ for await (const chunk of response) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
284332
+ return Buffer.concat(chunks).toString("utf8");
284333
+ }
284334
+ function formatHostBridgeNodeResponseError(statusCode, statusMessage, body, prefix) {
284335
+ const trimmedBody = body.trim();
284336
+ if (trimmedBody.length > 0) try {
284337
+ const parsed = JSON.parse(trimmedBody);
284338
+ if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
284339
+ } catch {}
284340
+ const fallback = trimmedBody.length > 0 ? trimmedBody : statusMessage?.trim() || "Request failed.";
284341
+ return `${prefix}: ${statusCode ?? 0} ${fallback}`;
284342
+ }
284343
+ /** An abort that callers can recognise by name rather than by message matching. */
284344
+ function hostBridgeAbortError(message) {
284345
+ const error = new Error(message);
284346
+ error.name = "AbortError";
284347
+ return error;
284348
+ }
284349
+ /**
284350
+ * Drain complete NDJSON lines, returning what is left over.
284351
+ *
284352
+ * The remainder is the whole point: a stream chunk ends wherever the socket
284353
+ * decides, so the trailing line is usually incomplete and must be carried into
284354
+ * the next chunk rather than parsed.
284355
+ */
284356
+ function drainHostBridgeNdjsonBuffer(buffer, callback, parseLine = (line) => JSON.parse(line)) {
284357
+ let nextBuffer = buffer;
284358
+ let newlineIndex = nextBuffer.indexOf("\n");
284359
+ while (newlineIndex >= 0) {
284360
+ const line = nextBuffer.slice(0, newlineIndex).trim();
284361
+ nextBuffer = nextBuffer.slice(newlineIndex + 1);
284362
+ if (line.length > 0) callback(parseLine(line));
284363
+ newlineIndex = nextBuffer.indexOf("\n");
284364
+ }
284365
+ return nextBuffer;
284366
+ }
284367
+ //#endregion
284368
+ //#region src/browser/NativeHostBrowserBridgeClient.ts
284369
+ const NATIVE_HOST_EVENT_RECONNECT_BASE_MS = 25;
284370
+ const NATIVE_HOST_EVENT_RECONNECT_MAX_MS = HOST_BRIDGE_RECONNECT_MAX_MS;
284371
+ function nativeHostBridgeUrl(baseUrl) {
284372
+ return hostBridgePathUrl(baseUrl, "/agent-browser/host-bridge");
284373
+ }
284374
+ function nativeHostBridgeEventsUrl(baseUrl, input) {
284375
+ return hostBridgeQueryUrl(baseUrl, "/agent-browser/host-events", {
284376
+ browserId: input.browserId,
284377
+ sessionId: input.sessionId
284378
+ });
284379
+ }
284380
+ async function readNativeHostBridgeError(response) {
284381
+ return readHostBridgeError(response);
284382
+ }
284383
+ async function readNativeHostBridgeNodeResponseBody(response) {
284384
+ return readHostBridgeNodeResponseBody(response);
284385
+ }
284386
+ function formatNativeHostBridgeNodeResponseError(statusCode, statusMessage, body) {
284387
+ return formatHostBridgeNodeResponseError(statusCode, statusMessage, body, "Native browser host bridge event stream failed");
284388
+ }
284389
+ function nativeHostBridgeAbortError() {
284390
+ return hostBridgeAbortError("Native browser host bridge event stream aborted.");
284391
+ }
284392
+ function parseNativeHostBridgeEventLine(line) {
284393
+ return JSON.parse(line);
284394
+ }
284395
+ function drainNativeHostBridgeNdjsonBuffer(buffer, callback) {
284396
+ return drainHostBridgeNdjsonBuffer(buffer, callback, parseNativeHostBridgeEventLine);
284397
+ }
284398
+ function subscribeToNativeHostBridgeEventsViaNodeHttp(input) {
284399
+ return new Promise((resolve, reject) => {
284400
+ if (input.abortSignal.aborted) {
284401
+ resolve();
284402
+ return;
284403
+ }
284404
+ const url = new URL(nativeHostBridgeEventsUrl(input.options.baseUrl, input.input));
284405
+ const transport = url.protocol === "https:" ? node_https : node_http;
284406
+ let settled = false;
284407
+ const finalize = (handler) => {
284408
+ if (settled) return;
284409
+ settled = true;
284410
+ input.abortSignal.removeEventListener("abort", onAbort);
284411
+ handler();
284412
+ };
284413
+ const onAbort = () => {
284414
+ request.destroy(nativeHostBridgeAbortError());
284415
+ finalize(resolve);
284416
+ };
284417
+ const request = transport.request({
284418
+ protocol: url.protocol,
284419
+ hostname: url.hostname,
284420
+ port: url.port,
284421
+ path: `${url.pathname}${url.search}`,
284422
+ method: "GET",
284423
+ headers: hostBridgeAuthHeaders(input.options.token)
284424
+ }, (response) => {
284425
+ const statusCode = response.statusCode ?? 0;
284426
+ if (statusCode < 200 || statusCode >= 300) {
284427
+ readNativeHostBridgeNodeResponseBody(response).then((body) => finalize(() => reject(new Error(formatNativeHostBridgeNodeResponseError(response.statusCode, response.statusMessage, body))))).catch((error) => finalize(() => reject(error)));
284428
+ return;
284429
+ }
284430
+ response.setEncoding("utf8");
284431
+ let buffer = "";
284432
+ const rejectStream = (error) => {
284433
+ const streamError = error instanceof Error ? error : new Error(String(error ?? "Unknown stream failure"));
284434
+ response.destroy(streamError);
284435
+ finalize(() => reject(streamError));
284436
+ };
284437
+ response.on("data", (chunk) => {
284438
+ try {
284439
+ buffer = drainNativeHostBridgeNdjsonBuffer(buffer + (typeof chunk === "string" ? chunk : chunk.toString("utf8")), input.callback);
284440
+ } catch (error) {
284441
+ rejectStream(error);
284442
+ }
284443
+ });
284444
+ response.on("end", () => finalize(resolve));
284445
+ response.on("close", () => finalize(resolve));
284446
+ response.on("error", (error) => finalize(() => reject(error)));
284447
+ });
284448
+ request.on("socket", (socket) => {
284449
+ socket.setKeepAlive(true, 1e3);
284450
+ });
284451
+ request.on("error", (error) => {
284452
+ if (input.abortSignal.aborted && error.name === "AbortError") {
284453
+ finalize(resolve);
284454
+ return;
284455
+ }
284456
+ finalize(() => reject(error));
284457
+ });
284458
+ input.abortSignal.addEventListener("abort", onAbort, { once: true });
284459
+ request.end();
284460
+ });
284461
+ }
284462
+ async function requestNativeHostBridge(input) {
284463
+ const abortController = new AbortController();
284464
+ const timeout = setTimeout(() => abortController.abort(), input.method === "closeSession" ? input.deps.closeSessionTimeoutMs() : 3e4);
284465
+ const response = await input.fetchImpl(nativeHostBridgeUrl(input.options.baseUrl), {
284466
+ method: "POST",
284467
+ headers: {
284468
+ authorization: `Bearer ${input.options.token}`,
284469
+ "content-type": "application/json"
284470
+ },
284471
+ body: JSON.stringify({
284472
+ method: input.method,
284473
+ params: input.params
284474
+ }),
284475
+ signal: abortController.signal
284476
+ }).finally(() => {
284477
+ clearTimeout(timeout);
284478
+ });
284479
+ if (!response.ok) {
284480
+ const message = await readNativeHostBridgeError(response);
284481
+ throw new Error(`Native browser host bridge request failed: ${response.status} ${message}`);
284482
+ }
284483
+ return (await response.json()).result;
284484
+ }
284485
+ async function readNativeHostBridgeEventsViaFetch(input) {
284486
+ const response = await input.fetchImpl(nativeHostBridgeEventsUrl(input.options.baseUrl, input.identity), {
284487
+ headers: { authorization: `Bearer ${input.options.token}` },
284488
+ signal: input.abortSignal
284489
+ });
284490
+ if (!response.ok) {
284491
+ const message = await readNativeHostBridgeError(response);
284492
+ throw new Error(`Native browser host bridge event stream failed: ${response.status} ${message}`);
284493
+ }
284494
+ const reader = response.body?.getReader();
284495
+ if (!reader) throw new Error("Native browser host bridge event stream is unavailable.");
284496
+ await readNativeHostBridgeEventReader(reader, input);
284497
+ }
284498
+ async function readNativeHostBridgeEventReader(reader, input) {
284499
+ const decoder = new TextDecoder();
284500
+ let buffer = "";
284501
+ while (!input.abortSignal.aborted) {
284502
+ const { done, value } = await reader.read();
284503
+ if (done) return;
284504
+ buffer = drainNativeHostBridgeNdjsonBuffer(buffer + decoder.decode(value, { stream: true }), input.callback);
284505
+ }
284506
+ }
284507
+ async function readNativeHostBridgeEvents(input) {
284508
+ if (!input.options.fetch) {
284509
+ await subscribeToNativeHostBridgeEventsViaNodeHttp({
284510
+ abortSignal: input.abortSignal,
284511
+ callback: input.callback,
284512
+ input: input.identity,
284513
+ options: input.options
284514
+ });
284515
+ return;
284516
+ }
284517
+ await readNativeHostBridgeEventsViaFetch(input);
284518
+ }
284519
+ function abortableDelay(ms, signal) {
284520
+ if (signal.aborted) return Promise.resolve();
284521
+ return new Promise((resolve) => {
284522
+ const timeout = setTimeout(resolve, ms);
284523
+ signal.addEventListener("abort", () => {
284524
+ clearTimeout(timeout);
284525
+ resolve();
284526
+ }, { once: true });
284527
+ });
284528
+ }
284529
+ async function maintainNativeHostBridgeEventSubscription(input) {
284530
+ let attempt = 0;
284531
+ while (!input.abortSignal.aborted) {
284532
+ try {
284533
+ await readNativeHostBridgeEvents(input);
284534
+ attempt = 0;
284535
+ } catch (error) {
284536
+ if (input.abortSignal.aborted) return;
284537
+ console.warn("[browser] native host bridge event stream failed", error);
284538
+ }
284539
+ if (!input.abortSignal.aborted) {
284540
+ const delayMs = hostBridgeReconnectDelayMs(attempt, NATIVE_HOST_EVENT_RECONNECT_BASE_MS, NATIVE_HOST_EVENT_RECONNECT_MAX_MS);
284541
+ attempt += 1;
284542
+ await abortableDelay(delayMs, input.abortSignal);
284543
+ }
284544
+ }
284545
+ }
284546
+ function subscribeToNativeHostBridgeEvents(input) {
284547
+ const abortController = new AbortController();
284548
+ maintainNativeHostBridgeEventSubscription({
284549
+ abortSignal: abortController.signal,
284550
+ callback: input.callback,
284551
+ fetchImpl: input.fetchImpl,
284552
+ identity: input.identity,
284553
+ options: input.options
284554
+ });
284555
+ return () => {
284556
+ abortController.abort();
284557
+ };
284558
+ }
284559
+ function createNativeHostBrowserBridgeClientImplementation(options, deps) {
284560
+ const fetchImpl = options.fetch ?? fetch;
284561
+ const request = (method, params) => requestNativeHostBridge({
284562
+ deps,
284563
+ fetchImpl,
284564
+ method,
284565
+ options,
284566
+ params
284567
+ });
284568
+ const subscribe = (identity, callback) => subscribeToNativeHostBridgeEvents({
284569
+ callback,
284570
+ fetchImpl,
284571
+ identity,
284572
+ options
284573
+ });
284574
+ return {
284575
+ closeSession: (input = {}) => request("closeSession", input),
284576
+ command: (input) => request("command", input),
284577
+ getSession: (input = {}) => request("getSession", input),
284578
+ listSessions: (input = {}) => request("listSessions", input),
284579
+ onEvent: subscribe,
284580
+ openSession: (input = {}) => request("openSession", input),
284581
+ subscribe: async (input, callback) => subscribe(input, callback)
284582
+ };
284583
+ }
285019
284584
  //#endregion
285020
284585
  //#region src/browser/AgentBrowserHostBridgeAdapter.ts
285021
284586
  const DEFAULT_URL = "about:blank";
@@ -285025,8 +284590,6 @@ const DEFAULT_AGENT_BROWSER_VIEWPORT = {
285025
284590
  width: 1280,
285026
284591
  height: 720
285027
284592
  };
285028
- const NATIVE_HOST_EVENT_RECONNECT_BASE_MS = 25;
285029
- const NATIVE_HOST_EVENT_RECONNECT_MAX_MS = HOST_BRIDGE_RECONNECT_MAX_MS;
285030
284593
  const ABSOLUTE_WEB_URL_PATTERN = /^[a-z][a-z\d+\-.]*:\/\//i;
285031
284594
  const SPECIAL_URL_PATTERN = /^(about|blob|chrome|data|devtools|file):/i;
285032
284595
  const LOCAL_URL_LIKE_PATTERN = /^(localhost|127\.0\.0\.1|\[::1\]|::1|[a-z0-9.-]+\.localhost)(?::\d+)?(?:[/?#].*)?$/i;
@@ -285165,11 +284728,26 @@ async function closeAgentBrowserBackendWithTimeout(closeBackend, env = process.e
285165
284728
  console.warn("[browser] Agent Browser Playwright backend close timed out; continuing", { timeoutMs: agentBrowserPlaywrightBackendCloseTimeoutMs(env) });
285166
284729
  }
285167
284730
  }
285168
- var ForgeAgentBrowserPlaywrightBackend = class extends PlaywrightBackend {
285169
- async close() {
285170
- await closeAgentBrowserBackendWithTimeout(() => super.close());
284731
+ function createNativeHostBrowserBridgeClient(options) {
284732
+ return createNativeHostBrowserBridgeClientImplementation(options, { closeSessionTimeoutMs: agentBrowserCloseTimeoutMs });
284733
+ }
284734
+ /**
284735
+ * Builds the Playwright-backed backend from the lazily loaded module. The
284736
+ * `playwright` package (and everything that statically imports it) must stay
284737
+ * out of the standalone bundle's eager import graph: the bundle keeps
284738
+ * `playwright` external, so a static import would hoist it to the top of
284739
+ * `dist/bin.mjs` and the server would fail to boot on any machine that has
284740
+ * not provisioned playwright yet. Deferring the import to backend construction
284741
+ * keeps boot playwright-free and lets a later provision satisfy first use.
284742
+ */
284743
+ function createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, options) {
284744
+ class ForgeAgentBrowserPlaywrightBackend extends PlaywrightBackend {
284745
+ async close() {
284746
+ await closeAgentBrowserBackendWithTimeout(() => super.close());
284747
+ }
285171
284748
  }
285172
- };
284749
+ return new ForgeAgentBrowserPlaywrightBackend(options);
284750
+ }
285173
284751
  function toBrowserError(error, fallback) {
285174
284752
  console.warn("[browser] Agent Browser host bridge operation failed", {
285175
284753
  operation: fallback,
@@ -285292,193 +284870,6 @@ function resolveDisplay(snapshot, options) {
285292
284870
  if (typeof options?.display === "function") return options.display(snapshot);
285293
284871
  return options?.display ?? DEFAULT_AGENT_BROWSER_DISPLAY;
285294
284872
  }
285295
- function nativeHostBridgeUrl(baseUrl) {
285296
- return hostBridgePathUrl(baseUrl, "/agent-browser/host-bridge");
285297
- }
285298
- function nativeHostBridgeEventsUrl(baseUrl, input) {
285299
- return hostBridgeQueryUrl(baseUrl, "/agent-browser/host-events", {
285300
- browserId: input.browserId,
285301
- sessionId: input.sessionId
285302
- });
285303
- }
285304
- async function readNativeHostBridgeError(response) {
285305
- return readHostBridgeError(response);
285306
- }
285307
- async function readNativeHostBridgeNodeResponseBody(response) {
285308
- return readHostBridgeNodeResponseBody(response);
285309
- }
285310
- function formatNativeHostBridgeNodeResponseError(statusCode, statusMessage, body) {
285311
- return formatHostBridgeNodeResponseError(statusCode, statusMessage, body, "Native browser host bridge event stream failed");
285312
- }
285313
- function nativeHostBridgeAbortError() {
285314
- return hostBridgeAbortError("Native browser host bridge event stream aborted.");
285315
- }
285316
- function parseNativeHostBridgeEventLine(line) {
285317
- return JSON.parse(line);
285318
- }
285319
- function drainNativeHostBridgeNdjsonBuffer(buffer, callback) {
285320
- return drainHostBridgeNdjsonBuffer(buffer, callback, parseNativeHostBridgeEventLine);
285321
- }
285322
- function subscribeToNativeHostBridgeEventsViaNodeHttp(input) {
285323
- return new Promise((resolve, reject) => {
285324
- if (input.abortSignal.aborted) {
285325
- resolve();
285326
- return;
285327
- }
285328
- const url = new URL(nativeHostBridgeEventsUrl(input.options.baseUrl, input.input));
285329
- const transport = url.protocol === "https:" ? node_https : node_http;
285330
- let settled = false;
285331
- const finalize = (handler) => {
285332
- if (settled) return;
285333
- settled = true;
285334
- input.abortSignal.removeEventListener("abort", onAbort);
285335
- handler();
285336
- };
285337
- const onAbort = () => {
285338
- request.destroy(nativeHostBridgeAbortError());
285339
- finalize(resolve);
285340
- };
285341
- const request = transport.request({
285342
- protocol: url.protocol,
285343
- hostname: url.hostname,
285344
- port: url.port,
285345
- path: `${url.pathname}${url.search}`,
285346
- method: "GET",
285347
- headers: hostBridgeAuthHeaders(input.options.token)
285348
- }, (response) => {
285349
- const statusCode = response.statusCode ?? 0;
285350
- if (statusCode < 200 || statusCode >= 300) {
285351
- readNativeHostBridgeNodeResponseBody(response).then((body) => finalize(() => reject(new Error(formatNativeHostBridgeNodeResponseError(response.statusCode, response.statusMessage, body))))).catch((error) => finalize(() => reject(error)));
285352
- return;
285353
- }
285354
- response.setEncoding("utf8");
285355
- let buffer = "";
285356
- const rejectStream = (error) => {
285357
- const streamError = error instanceof Error ? error : new Error(String(error ?? "Unknown stream failure"));
285358
- response.destroy(streamError);
285359
- finalize(() => reject(streamError));
285360
- };
285361
- response.on("data", (chunk) => {
285362
- try {
285363
- buffer = drainNativeHostBridgeNdjsonBuffer(buffer + (typeof chunk === "string" ? chunk : chunk.toString("utf8")), input.callback);
285364
- } catch (error) {
285365
- rejectStream(error);
285366
- }
285367
- });
285368
- response.on("end", () => finalize(resolve));
285369
- response.on("close", () => finalize(resolve));
285370
- response.on("error", (error) => finalize(() => reject(error)));
285371
- });
285372
- request.on("socket", (socket) => {
285373
- socket.setKeepAlive(true, 1e3);
285374
- });
285375
- request.on("error", (error) => {
285376
- if (input.abortSignal.aborted && error.name === "AbortError") {
285377
- finalize(resolve);
285378
- return;
285379
- }
285380
- finalize(() => reject(error));
285381
- });
285382
- input.abortSignal.addEventListener("abort", onAbort, { once: true });
285383
- request.end();
285384
- });
285385
- }
285386
- function abortableDelay(ms, signal) {
285387
- if (signal.aborted) return Promise.resolve();
285388
- return new Promise((resolve) => {
285389
- const timeout = setTimeout(resolve, ms);
285390
- signal.addEventListener("abort", () => {
285391
- clearTimeout(timeout);
285392
- resolve();
285393
- }, { once: true });
285394
- });
285395
- }
285396
- function createNativeHostBrowserBridgeClient(options) {
285397
- const fetchImpl = options.fetch ?? fetch;
285398
- const request = async (method, params) => {
285399
- const abortController = new AbortController();
285400
- const timeout = setTimeout(() => abortController.abort(), method === "closeSession" ? agentBrowserCloseTimeoutMs() : 3e4);
285401
- const response = await fetchImpl(nativeHostBridgeUrl(options.baseUrl), {
285402
- method: "POST",
285403
- headers: {
285404
- authorization: `Bearer ${options.token}`,
285405
- "content-type": "application/json"
285406
- },
285407
- body: JSON.stringify({
285408
- method,
285409
- params
285410
- }),
285411
- signal: abortController.signal
285412
- }).finally(() => {
285413
- clearTimeout(timeout);
285414
- });
285415
- if (!response.ok) {
285416
- const message = await readNativeHostBridgeError(response);
285417
- throw new Error(`Native browser host bridge request failed: ${response.status} ${message}`);
285418
- }
285419
- return (await response.json()).result;
285420
- };
285421
- const subscribe = (input, callback) => {
285422
- const abortController = new AbortController();
285423
- const readStream = async () => {
285424
- if (!options.fetch) {
285425
- await subscribeToNativeHostBridgeEventsViaNodeHttp({
285426
- abortSignal: abortController.signal,
285427
- callback,
285428
- input,
285429
- options
285430
- });
285431
- return;
285432
- }
285433
- const response = await fetchImpl(nativeHostBridgeEventsUrl(options.baseUrl, input), {
285434
- headers: { authorization: `Bearer ${options.token}` },
285435
- signal: abortController.signal
285436
- });
285437
- if (!response.ok) {
285438
- const message = await readNativeHostBridgeError(response);
285439
- throw new Error(`Native browser host bridge event stream failed: ${response.status} ${message}`);
285440
- }
285441
- const reader = response.body?.getReader();
285442
- if (!reader) throw new Error("Native browser host bridge event stream is unavailable.");
285443
- const decoder = new TextDecoder();
285444
- let buffer = "";
285445
- while (!abortController.signal.aborted) {
285446
- const { done, value } = await reader.read();
285447
- if (done) return;
285448
- buffer = drainNativeHostBridgeNdjsonBuffer(buffer + decoder.decode(value, { stream: true }), callback);
285449
- }
285450
- };
285451
- (async () => {
285452
- let attempt = 0;
285453
- while (!abortController.signal.aborted) {
285454
- try {
285455
- await readStream();
285456
- attempt = 0;
285457
- } catch (error) {
285458
- if (abortController.signal.aborted) return;
285459
- console.warn("[browser] native host bridge event stream failed", error);
285460
- }
285461
- if (!abortController.signal.aborted) {
285462
- const delayMs = hostBridgeReconnectDelayMs(attempt, NATIVE_HOST_EVENT_RECONNECT_BASE_MS, NATIVE_HOST_EVENT_RECONNECT_MAX_MS);
285463
- attempt += 1;
285464
- await abortableDelay(delayMs, abortController.signal);
285465
- }
285466
- }
285467
- })();
285468
- return () => {
285469
- abortController.abort();
285470
- };
285471
- };
285472
- return {
285473
- closeSession: (input = {}) => request("closeSession", input),
285474
- command: (input) => request("command", input),
285475
- getSession: (input = {}) => request("getSession", input),
285476
- listSessions: (input = {}) => request("listSessions", input),
285477
- onEvent: subscribe,
285478
- openSession: (input = {}) => request("openSession", input),
285479
- subscribe: async (input, callback) => subscribe(input, callback)
285480
- };
285481
- }
285482
284873
  function toHostCommand(command) {
285483
284874
  switch (command.type) {
285484
284875
  case "activateTab": return {
@@ -286144,71 +285535,100 @@ const AgentBrowserManagerLive = require_Schema$1.effect(BrowserManager, require_
286144
285535
  yield* require_Schema$1.addFinalizer(() => require_Schema$1.promise(() => displayTaskHoldManager?.releaseAll() ?? Promise.resolve()));
286145
285536
  const baseBackendOptions = displayCaptureRuntime.backendOptions;
286146
285537
  const sessionInitialUrls = /* @__PURE__ */ new Map();
286147
- const runtime = createAgentBrowserRuntime({
286148
- backends: { [DEFAULT_AGENT_BROWSER_ID]: new ForgeAgentBrowserPlaywrightBackend({
286149
- ...browserBackendOptionsForSession({
286150
- baseOptions: baseBackendOptions,
286151
- sessionId: "default"
286152
- }),
286153
- name: "Forge Agent Browser"
286154
- }) },
286155
- backendFactory: (input) => new ForgeAgentBrowserPlaywrightBackend({
286156
- ...browserBackendOptionsForSession({
286157
- baseOptions: baseBackendOptions,
286158
- initialUrl: sessionInitialUrls.get(input.sessionId),
286159
- sessionId: input.sessionId
286160
- }),
286161
- name: `Forge Agent Browser ${input.sessionId}`
286162
- })
286163
- });
286164
- yield* require_Schema$1.addFinalizer(() => require_Schema$1.promise(() => runtime.close()).pipe(require_Schema$1.orDie));
286165
- const hostBridge = createHostBrowserBridge({
286166
- browserId: DEFAULT_AGENT_BROWSER_ID,
286167
- closeTabsOnSessionClose: false,
286168
- defaultViewport: DEFAULT_AGENT_BROWSER_VIEWPORT,
286169
- runtime,
286170
- tabLifecycle: {
286171
- activateNextTabOnClose: true,
286172
- createFallbackTabOnLastClose: true,
286173
- fallbackUrl: DEFAULT_URL
286174
- }
286175
- });
286176
- const reaperConfig = browserOrphanReaperConfigFromEnv(process.env, AGENT_BROWSER_PROFILE_ROOT);
286177
- if (reaperConfig.enabled && process.platform !== "win32") {
286178
- const reaper = createBrowserOrphanReaper(createSystemBrowserOrphanReaperDeps({
286179
- profileRoot: AGENT_BROWSER_PROFILE_ROOT,
286180
- listSessions: () => hostBridge.listSessions(),
286181
- closeSession: async (sessionId) => {
286182
- await hostBridge.closeSession({ sessionId: BrowserSessionId.make(sessionId) });
286183
- }
286184
- }), reaperConfig);
286185
- const reaperContext = yield* require_Schema$1.context();
286186
- const runReaperFork = require_Schema$1.runForkWith(reaperContext);
286187
- const reaperInterval = setInterval(() => {
285538
+ const reaperContext = yield* require_Schema$1.context();
285539
+ let runtimeBuild = null;
285540
+ const lazyDisposers = [];
285541
+ const buildPlaywrightRuntime = async () => {
285542
+ const { PlaywrightBackend } = await Promise.resolve().then(() => require("./playwright-backend-BONxTCSA.cjs"));
285543
+ const runtime = createAgentBrowserRuntime({
285544
+ backends: { [DEFAULT_AGENT_BROWSER_ID]: createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, {
285545
+ ...browserBackendOptionsForSession({
285546
+ baseOptions: baseBackendOptions,
285547
+ sessionId: "default"
285548
+ }),
285549
+ name: "Forge Agent Browser"
285550
+ }) },
285551
+ backendFactory: (input) => createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, {
285552
+ ...browserBackendOptionsForSession({
285553
+ baseOptions: baseBackendOptions,
285554
+ initialUrl: sessionInitialUrls.get(input.sessionId),
285555
+ sessionId: input.sessionId
285556
+ }),
285557
+ name: `Forge Agent Browser ${input.sessionId}`
285558
+ })
285559
+ });
285560
+ lazyDisposers.push(() => runtime.close());
285561
+ const hostBridge = createHostBrowserBridge({
285562
+ browserId: DEFAULT_AGENT_BROWSER_ID,
285563
+ closeTabsOnSessionClose: false,
285564
+ defaultViewport: DEFAULT_AGENT_BROWSER_VIEWPORT,
285565
+ runtime,
285566
+ tabLifecycle: {
285567
+ activateNextTabOnClose: true,
285568
+ createFallbackTabOnLastClose: true,
285569
+ fallbackUrl: DEFAULT_URL
285570
+ }
285571
+ });
285572
+ const reaperConfig = browserOrphanReaperConfigFromEnv(process.env, AGENT_BROWSER_PROFILE_ROOT);
285573
+ if (reaperConfig.enabled && process.platform !== "win32") {
285574
+ const reaper = createBrowserOrphanReaper(createSystemBrowserOrphanReaperDeps({
285575
+ profileRoot: AGENT_BROWSER_PROFILE_ROOT,
285576
+ listSessions: () => hostBridge.listSessions(),
285577
+ closeSession: async (sessionId) => {
285578
+ await hostBridge.closeSession({ sessionId: BrowserSessionId.make(sessionId) });
285579
+ }
285580
+ }), reaperConfig);
285581
+ const runReaperFork = require_Schema$1.runForkWith(reaperContext);
285582
+ const reaperInterval = setInterval(() => {
285583
+ runReaperFork(reaper.sweep);
285584
+ }, reaperConfig.intervalMs);
285585
+ reaperInterval.unref?.();
285586
+ lazyDisposers.push(() => clearInterval(reaperInterval));
286188
285587
  runReaperFork(reaper.sweep);
286189
- }, reaperConfig.intervalMs);
286190
- reaperInterval.unref?.();
286191
- yield* require_Schema$1.addFinalizer(() => require_Schema$1.sync(() => clearInterval(reaperInterval)));
286192
- runReaperFork(reaper.sweep);
286193
- }
286194
- return createAgentBrowserHostBridgeManager(hostBridge, {
286195
- display: (snapshot) => agentBrowserDisplayForRuntimeMode({
286196
- mode: serverConfig.mode,
286197
- iceServers: serverConfig.browserWebRtcIceServerResolver.getCachedIceServers().length > 0 ? serverConfig.browserWebRtcIceServerResolver.getCachedIceServers() : serverConfig.browserWebRtcIceServers,
286198
- requiresRelay: requiresWebRtcRelay,
286199
- viewport: snapshot.viewport
286200
- }),
286201
- displayIceServers: serverConfig.browserWebRtcIceServers,
286202
- resolveDisplayIceServers: serverConfig.browserWebRtcIceServerResolver.resolveIceServers,
286203
- displayPeers,
286204
- prepareRuntimeSession: (input) => {
286205
- sessionInitialUrls.set(input.sessionId, input.initialUrl);
286206
- },
286207
- forgetRuntimeSession: (input) => {
286208
- sessionInitialUrls.delete(input.sessionId);
285588
+ }
285589
+ return createAgentBrowserHostBridgeManager(hostBridge, {
285590
+ display: (snapshot) => agentBrowserDisplayForRuntimeMode({
285591
+ mode: serverConfig.mode,
285592
+ iceServers: serverConfig.browserWebRtcIceServerResolver.getCachedIceServers().length > 0 ? serverConfig.browserWebRtcIceServerResolver.getCachedIceServers() : serverConfig.browserWebRtcIceServers,
285593
+ requiresRelay: requiresWebRtcRelay,
285594
+ viewport: snapshot.viewport
285595
+ }),
285596
+ displayIceServers: serverConfig.browserWebRtcIceServers,
285597
+ resolveDisplayIceServers: serverConfig.browserWebRtcIceServerResolver.resolveIceServers,
285598
+ displayPeers,
285599
+ prepareRuntimeSession: (input) => {
285600
+ sessionInitialUrls.set(input.sessionId, input.initialUrl);
285601
+ },
285602
+ forgetRuntimeSession: (input) => {
285603
+ sessionInitialUrls.delete(input.sessionId);
285604
+ },
285605
+ requiresWebRtcRelay
285606
+ });
285607
+ };
285608
+ yield* require_Schema$1.addFinalizer(() => require_Schema$1.promise(async () => {
285609
+ for (const dispose of lazyDisposers.toReversed()) await dispose();
285610
+ }).pipe(require_Schema$1.orDie));
285611
+ const ensurePlaywrightRuntime = () => require_Schema$1.tryPromise({
285612
+ try: () => {
285613
+ runtimeBuild ??= buildPlaywrightRuntime();
285614
+ return runtimeBuild;
286209
285615
  },
286210
- requiresWebRtcRelay
285616
+ catch: (cause) => {
285617
+ runtimeBuild = null;
285618
+ return toBrowserError(cause, "Browser backend could not be loaded. Browser packages load on first browser use and must be provisioned on this runtime.");
285619
+ }
286211
285620
  });
285621
+ return {
285622
+ openSession: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.openSession(input)),
285623
+ closeSession: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.closeSession(input)),
285624
+ clearBrowsingData: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.clearBrowsingData(input)),
285625
+ getSession: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.getSession(input)),
285626
+ listSessions: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.listSessions(input)),
285627
+ command: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.command(input)),
285628
+ attachDisplay: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.attachDisplay(input)),
285629
+ addDisplayIceCandidate: (input) => require_Schema$1.flatMap(ensurePlaywrightRuntime(), (m) => m.addDisplayIceCandidate(input)),
285630
+ subscribe: (input, listener) => require_Schema$1.flatMap(require_Schema$1.orDie(ensurePlaywrightRuntime()), (m) => m.subscribe(input, listener))
285631
+ };
286212
285632
  }));
286213
285633
  //#endregion
286214
285634
  //#region src/devTunnel/http.ts
@@ -298247,7 +297667,7 @@ function resolveBuildCommitFromEnv(env) {
298247
297667
  * environment descriptor down instead of reporting an honest "unknown".
298248
297668
  */
298249
297669
  function readBakedBuildCommit() {
298250
- return "e64ae9956bf528e3ffc346a89f325c7b57e8ec56";
297670
+ return "e0ef2b5174f2f50934814b1865cabaa9c7de0ce7";
298251
297671
  }
298252
297672
  async function resolveServerBuildCommit(input) {
298253
297673
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -321872,13 +321292,13 @@ const gitUrlCloneRouteLayer = add$1("POST", "/api/cloud/git/clone-url", require_
321872
321292
  //#endregion
321873
321293
  //#region src/server.ts
321874
321294
  const PtyAdapterLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
321875
- if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-uyreNRGp.cjs")))).layer;
321876
- else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-BehazUMi.cjs")))).layer;
321295
+ if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-BiTlUj63.cjs")))).layer;
321296
+ else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-BJ7874MO.cjs")))).layer;
321877
321297
  }));
321878
321298
  const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
321879
321299
  const config = yield* ServerConfig;
321880
- if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-C0oPP_3A.cjs")))).layer(bunHttpServerOptions(config));
321881
- const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-Cjd3bFrD.cjs"))), require_Schema$1.promise(() => import("node:http"))]);
321300
+ if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-CXndtc1j.cjs")))).layer(bunHttpServerOptions(config));
321301
+ const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-B4dCKqYY.cjs"))), require_Schema$1.promise(() => import("node:http"))]);
321882
321302
  return NodeHttpServer.layer(NodeHttp.createServer, {
321883
321303
  host: config.host,
321884
321304
  port: config.port
@@ -321886,7 +321306,7 @@ const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* ()
321886
321306
  }));
321887
321307
  const PlatformServicesLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
321888
321308
  if (typeof Bun !== "undefined") {
321889
- const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-DkenwD7S.cjs")));
321309
+ const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-DsWG7VM1.cjs")));
321890
321310
  return layer;
321891
321311
  } else {
321892
321312
  const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeServices-BvdIc9Vh.cjs")));
@@ -322889,7 +322309,7 @@ const connectLoginCommand = make$34("login", {
322889
322309
  })).json();
322890
322310
  },
322891
322311
  openBrowser: async (url) => {
322892
- const open = (await Promise.resolve().then(() => require("./open-BAsPVCWX.cjs"))).default;
322312
+ const open = (await Promise.resolve().then(() => require("./open-CghsF25U.cjs"))).default;
322893
322313
  await open(url);
322894
322314
  return true;
322895
322315
  },