@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.
- package/dist/{BunHttpServer-C0oPP_3A.cjs → BunHttpServer-CXndtc1j.cjs} +2 -2
- package/dist/{BunHttpServer-FbbQmsl_.mjs → BunHttpServer-DjilmGOM.mjs} +2 -2
- package/dist/{BunPTY-uyreNRGp.cjs → BunPTY-BiTlUj63.cjs} +1 -1
- package/dist/{BunPTY-DkAIzLIx.mjs → BunPTY-Csgwhbi5.mjs} +1 -1
- package/dist/{BunServices-DC7nhA3w.mjs → BunServices-BZKzHdrG.mjs} +1 -1
- package/dist/{BunServices-DkenwD7S.cjs → BunServices-DsWG7VM1.cjs} +1 -1
- package/dist/{NodeHttpServer-Cjd3bFrD.cjs → NodeHttpServer-B4dCKqYY.cjs} +1 -1
- package/dist/{NodeHttpServer-BJ4l15f-.mjs → NodeHttpServer-Ds3Gp1Gp.mjs} +1 -1
- package/dist/{NodePTY-BehazUMi.cjs → NodePTY-BJ7874MO.cjs} +1 -1
- package/dist/{NodePTY-BhezYy9H.mjs → NodePTY-DgXWLtWp.mjs} +1 -1
- package/dist/{NodeSqliteClient-CfsdC3AB.cjs → NodeSqliteClient-CPsJxxdD.cjs} +1 -1
- package/dist/{NodeSqliteClient-md8xd9hY.mjs → NodeSqliteClient-D29w60zZ.mjs} +1 -1
- package/dist/{SqliteClient-BKB695RF.mjs → SqliteClient-Bgo_s5sA.mjs} +1 -1
- package/dist/{SqliteClient-O1ABIngw.cjs → SqliteClient-k0pbfeIC.cjs} +1 -1
- package/dist/bin.cjs +432 -1012
- package/dist/bin.mjs +452 -1031
- package/dist/client/assets/{DiffPanel-CmdI2N7U.js → DiffPanel-u3cympMJ.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-DtFJMr9U.js → DiffPanel.logic-Im6HMxhw.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-DStZuS4s.js → DiffPanelShell-BckZpJGo.js} +102 -102
- package/dist/client/assets/{DiffWorkerPoolProvider-CG8qkZ21.js → DiffWorkerPoolProvider-Bwjf7Bmf.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-pc6WtcMu.js → PullRequestCodePanel-VADCfzOQ.js} +2 -2
- package/dist/client/assets/{Virtualizer-DQWCMRz2.js → Virtualizer-Dp4FFzsC.js} +2 -2
- package/dist/client/assets/{index-AZc0SrFH.js → index-CiD_w5sq.js} +7 -7
- package/dist/client/index.html +4 -4
- package/dist/{execAsync-BUcaFQ1D.cjs → execAsync--H9ZZOiC.cjs} +1 -1
- package/dist/{execAsync-BNNdR88o.mjs → execAsync-BJ4g1UuR.mjs} +1 -1
- package/dist/forge-build.json +1 -1
- package/dist/{getMachineId-bsd-3FzKrfVS.cjs → getMachineId-bsd-CJJWgsvW.cjs} +2 -2
- package/dist/{getMachineId-bsd-BuYwT36r.mjs → getMachineId-bsd-CvzjzhCJ.mjs} +2 -2
- package/dist/{getMachineId-darwin-RZKhGQ6C.mjs → getMachineId-darwin-BkjZ6NK7.mjs} +2 -2
- package/dist/{getMachineId-darwin-J2wNbebX.cjs → getMachineId-darwin-hZeIAq-t.cjs} +2 -2
- package/dist/{getMachineId-linux-BDLTrARB.cjs → getMachineId-linux-B4Pn5gp9.cjs} +1 -1
- package/dist/{getMachineId-linux-D5HSP0rH.mjs → getMachineId-linux-BaBxzaf3.mjs} +1 -1
- package/dist/{getMachineId-unsupported-DRh5MFwr.cjs → getMachineId-unsupported-B3jtoOL0.cjs} +1 -1
- package/dist/{getMachineId-unsupported-IaplPZnC.mjs → getMachineId-unsupported-EhCzrOph.mjs} +1 -1
- package/dist/{getMachineId-win-D220ursp.mjs → getMachineId-win-BFwtIsaZ.mjs} +2 -2
- package/dist/{getMachineId-win-BA8dHcjg.cjs → getMachineId-win-Bb4lJE8e.cjs} +2 -2
- package/dist/{open-FIgTDosf.mjs → open-2-reBrpv.mjs} +3 -3
- package/dist/{open-BAsPVCWX.cjs → open-CghsF25U.cjs} +1 -1
- package/dist/playwright-backend-BONxTCSA.cjs +643 -0
- package/dist/playwright-backend-DVQOhqRO.mjs +642 -0
- package/dist/settle-B6Rn65UV.cjs +28 -0
- package/dist/settle-BJcmpbvq.mjs +23 -0
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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-
|
|
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-
|
|
118474
|
-
node: () => Promise.resolve().then(() => require("./NodeSqliteClient-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
285169
|
-
|
|
285170
|
-
|
|
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
|
|
286148
|
-
|
|
286149
|
-
|
|
286150
|
-
|
|
286151
|
-
|
|
286152
|
-
|
|
286153
|
-
|
|
286154
|
-
|
|
286155
|
-
|
|
286156
|
-
|
|
286157
|
-
|
|
286158
|
-
|
|
286159
|
-
|
|
286160
|
-
|
|
286161
|
-
|
|
286162
|
-
|
|
286163
|
-
|
|
286164
|
-
|
|
286165
|
-
|
|
286166
|
-
|
|
286167
|
-
|
|
286168
|
-
|
|
286169
|
-
runtime
|
|
286170
|
-
|
|
286171
|
-
|
|
286172
|
-
|
|
286173
|
-
|
|
286174
|
-
|
|
286175
|
-
|
|
286176
|
-
|
|
286177
|
-
|
|
286178
|
-
|
|
286179
|
-
|
|
286180
|
-
|
|
286181
|
-
|
|
286182
|
-
|
|
286183
|
-
|
|
286184
|
-
|
|
286185
|
-
|
|
286186
|
-
|
|
286187
|
-
|
|
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
|
-
}
|
|
286190
|
-
|
|
286191
|
-
|
|
286192
|
-
|
|
286193
|
-
|
|
286194
|
-
|
|
286195
|
-
|
|
286196
|
-
|
|
286197
|
-
|
|
286198
|
-
|
|
286199
|
-
|
|
286200
|
-
|
|
286201
|
-
|
|
286202
|
-
|
|
286203
|
-
|
|
286204
|
-
|
|
286205
|
-
|
|
286206
|
-
|
|
286207
|
-
|
|
286208
|
-
|
|
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
|
-
|
|
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 "
|
|
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-
|
|
321876
|
-
else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-
|
|
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-
|
|
321881
|
-
const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-
|
|
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-
|
|
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-
|
|
322312
|
+
const open = (await Promise.resolve().then(() => require("./open-CghsF25U.cjs"))).default;
|
|
322893
322313
|
await open(url);
|
|
322894
322314
|
return true;
|
|
322895
322315
|
},
|