@cmmd-center/forge 0.13.29 → 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 +430 -1009
- package/dist/client/assets/{DiffPanel-CsYeFcEd.js → DiffPanel-u3cympMJ.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-BhrQueD8.js → DiffPanel.logic-Im6HMxhw.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-CssAtWUC.js → DiffPanelShell-BckZpJGo.js} +3 -3
- package/dist/client/assets/{DiffWorkerPoolProvider-C0iUCn9m.js → DiffWorkerPoolProvider-Bwjf7Bmf.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-O-XZJVnK.js → PullRequestCodePanel-VADCfzOQ.js} +2 -2
- package/dist/client/assets/{Virtualizer-Z2t67-w7.js → Virtualizer-Dp4FFzsC.js} +2 -2
- package/dist/client/assets/{index-AMVPSS6N.js → index-CiD_w5sq.js} +6 -6
- 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-D8mGyWmt.mjs → open-2-reBrpv.mjs} +1 -1
- 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.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { r as wrapper_default, t as Mime_default } from "./Mime-Dq9IoNiV.mjs";
|
|
|
13
13
|
import { a as diag, c as SpanKind$1, d as TraceFlags, f as ROOT_CONTEXT, i as propagation, l as isSpanContextValid, m as DiagLogLevel, n as init_esm$1, o as context$1, p as createContextKey, r as trace, s as SpanStatusCode, t as esm_exports$1 } from "./esm-wLIF-u3N.mjs";
|
|
14
14
|
import { a as verifyCmmdAuthRuntimeTicket, c as verifyCmmdRuntimeTicket, d as runtimeGrantToken, f as setRuntimeGrantToken, i as sendRuntimeControlHeartbeat, l as verifyCmmdRuntimeTicketRefreshProof, n as bootstrapRuntimeControlAuthorityFromNarrowGrant, o as verifyCmmdClaudeReadinessTicket, p as createReadOnceFileTokenAccessor, r as readRuntimeControlAuthority, s as verifyCmmdClaudeUsageTicket, t as bootstrapRuntimeControlAuthority, u as verifyCmmdScopedRuntimePayload } from "./runtimeControlAuthority-DpRIoWqP.mjs";
|
|
15
15
|
import { n as PtySpawnError, t as PtyAdapter } from "./PTY-BzdbZ0Ym.mjs";
|
|
16
|
+
import { t as settleValueWithin } from "./settle-BJcmpbvq.mjs";
|
|
16
17
|
import { createRequire } from "node:module";
|
|
17
18
|
import * as HE from "node:child_process";
|
|
18
19
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -73,7 +74,6 @@ import z$ from "http2";
|
|
|
73
74
|
import { access, appendFile, chmod as chmod$1, copyFile, link as link$1, lstat as lstat$1, mkdir as mkdir$1, open, readFile as readFile$2, readdir as readdir$2, readlink, realpath as realpath$1, rename as rename$1, rm as rm$1, rmdir, stat as stat$1, symlink, unlink as unlink$1, writeFile as writeFile$1 } from "fs/promises";
|
|
74
75
|
import { createInterface as createInterface$1 } from "readline";
|
|
75
76
|
import { AsyncLocalStorage as AsyncLocalStorage$1 } from "async_hooks";
|
|
76
|
-
import { chromium } from "playwright";
|
|
77
77
|
import { createSocket } from "node:dgram";
|
|
78
78
|
import { createSocket as createSocket$1 } from "dgram";
|
|
79
79
|
import { setTimeout as setTimeout$1 } from "timers/promises";
|
|
@@ -52897,19 +52897,19 @@ var require_getMachineId = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
52897
52897
|
async function getMachineId() {
|
|
52898
52898
|
if (!getMachineIdImpl) switch (process$3.platform) {
|
|
52899
52899
|
case "darwin":
|
|
52900
|
-
getMachineIdImpl = (await import("./getMachineId-darwin-
|
|
52900
|
+
getMachineIdImpl = (await import("./getMachineId-darwin-BkjZ6NK7.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).getMachineId;
|
|
52901
52901
|
break;
|
|
52902
52902
|
case "linux":
|
|
52903
|
-
getMachineIdImpl = (await import("./getMachineId-linux-
|
|
52903
|
+
getMachineIdImpl = (await import("./getMachineId-linux-BaBxzaf3.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).getMachineId;
|
|
52904
52904
|
break;
|
|
52905
52905
|
case "freebsd":
|
|
52906
|
-
getMachineIdImpl = (await import("./getMachineId-bsd-
|
|
52906
|
+
getMachineIdImpl = (await import("./getMachineId-bsd-CvzjzhCJ.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).getMachineId;
|
|
52907
52907
|
break;
|
|
52908
52908
|
case "win32":
|
|
52909
|
-
getMachineIdImpl = (await import("./getMachineId-win-
|
|
52909
|
+
getMachineIdImpl = (await import("./getMachineId-win-BFwtIsaZ.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).getMachineId;
|
|
52910
52910
|
break;
|
|
52911
52911
|
default:
|
|
52912
|
-
getMachineIdImpl = (await import("./getMachineId-unsupported-
|
|
52912
|
+
getMachineIdImpl = (await import("./getMachineId-unsupported-EhCzrOph.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).getMachineId;
|
|
52913
52913
|
break;
|
|
52914
52914
|
}
|
|
52915
52915
|
return getMachineIdImpl();
|
|
@@ -65937,7 +65937,7 @@ function normalizeNumberish(value) {
|
|
|
65937
65937
|
}
|
|
65938
65938
|
//#endregion
|
|
65939
65939
|
//#region package.json
|
|
65940
|
-
var version$1 = "0.13.
|
|
65940
|
+
var version$1 = "0.13.30";
|
|
65941
65941
|
//#endregion
|
|
65942
65942
|
//#region src/sentry.ts
|
|
65943
65943
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -93516,7 +93516,7 @@ const launchDetached = (launch) => gen(function* () {
|
|
|
93516
93516
|
});
|
|
93517
93517
|
const OpenLive = effect(Open, gen(function* () {
|
|
93518
93518
|
const open = yield* tryPromise({
|
|
93519
|
-
try: () => import("./open-
|
|
93519
|
+
try: () => import("./open-2-reBrpv.mjs"),
|
|
93520
93520
|
catch: (cause) => new OpenError$1({
|
|
93521
93521
|
message: "failed to load browser opener",
|
|
93522
93522
|
cause
|
|
@@ -118053,8 +118053,8 @@ effectDiscard(runMigrations());
|
|
|
118053
118053
|
//#endregion
|
|
118054
118054
|
//#region src/persistence/Layers/Sqlite.ts
|
|
118055
118055
|
const defaultSqliteClientLoaders = {
|
|
118056
|
-
bun: () => import("./SqliteClient-
|
|
118057
|
-
node: () => import("./NodeSqliteClient-
|
|
118056
|
+
bun: () => import("./SqliteClient-Bgo_s5sA.mjs"),
|
|
118057
|
+
node: () => import("./NodeSqliteClient-D29w60zZ.mjs")
|
|
118058
118058
|
};
|
|
118059
118059
|
const makeRuntimeSqliteLayer = fn$1("makeRuntimeSqliteLayer")(function* (config) {
|
|
118060
118060
|
const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
|
|
@@ -238689,24 +238689,6 @@ var BrowserActionEventRecorder = class {
|
|
|
238689
238689
|
}
|
|
238690
238690
|
};
|
|
238691
238691
|
//#endregion
|
|
238692
|
-
//#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/core/settle.js
|
|
238693
|
-
async function settleValueWithin(promise, timeoutMs) {
|
|
238694
|
-
let timer;
|
|
238695
|
-
try {
|
|
238696
|
-
return await Promise.race([promise.then((value) => ({
|
|
238697
|
-
status: "fulfilled",
|
|
238698
|
-
value
|
|
238699
|
-
}), (error) => ({
|
|
238700
|
-
error,
|
|
238701
|
-
status: "rejected"
|
|
238702
|
-
})), new Promise((resolve) => {
|
|
238703
|
-
timer = setTimeout(() => resolve({ status: "timed-out" }), timeoutMs);
|
|
238704
|
-
})]);
|
|
238705
|
-
} finally {
|
|
238706
|
-
clearTimeout(timer);
|
|
238707
|
-
}
|
|
238708
|
-
}
|
|
238709
|
-
//#endregion
|
|
238710
238692
|
//#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/safety/policy.js
|
|
238711
238693
|
var DefaultSafetyPolicy = class {
|
|
238712
238694
|
evaluate(action) {
|
|
@@ -238921,7 +238903,7 @@ var AgentBrowser = class {
|
|
|
238921
238903
|
async clickAction(action) {
|
|
238922
238904
|
await this.run({
|
|
238923
238905
|
kind: "click",
|
|
238924
|
-
selector: locatorActionLabel
|
|
238906
|
+
selector: locatorActionLabel(action)
|
|
238925
238907
|
}, async (tab) => {
|
|
238926
238908
|
await tab.click(action);
|
|
238927
238909
|
});
|
|
@@ -238933,7 +238915,7 @@ var AgentBrowser = class {
|
|
|
238933
238915
|
async doubleClickAction(action) {
|
|
238934
238916
|
await this.run({
|
|
238935
238917
|
kind: "click",
|
|
238936
|
-
selector: locatorActionLabel
|
|
238918
|
+
selector: locatorActionLabel(action)
|
|
238937
238919
|
}, async (tab) => {
|
|
238938
238920
|
await tab.doubleClick(action);
|
|
238939
238921
|
});
|
|
@@ -238945,7 +238927,7 @@ var AgentBrowser = class {
|
|
|
238945
238927
|
async fillAction(action, value) {
|
|
238946
238928
|
await this.run({
|
|
238947
238929
|
kind: "fill",
|
|
238948
|
-
selector: locatorActionLabel
|
|
238930
|
+
selector: locatorActionLabel(action),
|
|
238949
238931
|
value
|
|
238950
238932
|
}, async (tab) => {
|
|
238951
238933
|
await tab.fill(action, value);
|
|
@@ -238958,7 +238940,7 @@ var AgentBrowser = class {
|
|
|
238958
238940
|
async typeAction(action, value) {
|
|
238959
238941
|
await this.run({
|
|
238960
238942
|
kind: "fill",
|
|
238961
|
-
selector: locatorActionLabel
|
|
238943
|
+
selector: locatorActionLabel(action),
|
|
238962
238944
|
value
|
|
238963
238945
|
}, async (tab) => {
|
|
238964
238946
|
await tab.type(action, value);
|
|
@@ -238971,7 +238953,7 @@ var AgentBrowser = class {
|
|
|
238971
238953
|
async pressAction(action, key) {
|
|
238972
238954
|
await this.run({
|
|
238973
238955
|
kind: "press",
|
|
238974
|
-
selector: locatorActionLabel
|
|
238956
|
+
selector: locatorActionLabel(action),
|
|
238975
238957
|
key
|
|
238976
238958
|
}, async (tab) => {
|
|
238977
238959
|
await tab.press(action, key);
|
|
@@ -238984,7 +238966,7 @@ var AgentBrowser = class {
|
|
|
238984
238966
|
async selectAction(action, value) {
|
|
238985
238967
|
await this.run({
|
|
238986
238968
|
kind: "select",
|
|
238987
|
-
selector: locatorActionLabel
|
|
238969
|
+
selector: locatorActionLabel(action),
|
|
238988
238970
|
value
|
|
238989
238971
|
}, async (tab) => {
|
|
238990
238972
|
await tab.select(action, value);
|
|
@@ -238997,7 +238979,7 @@ var AgentBrowser = class {
|
|
|
238997
238979
|
async checkAction(action, checked) {
|
|
238998
238980
|
await this.run({
|
|
238999
238981
|
kind: "click",
|
|
239000
|
-
selector: locatorActionLabel
|
|
238982
|
+
selector: locatorActionLabel(action),
|
|
239001
238983
|
value: String(checked)
|
|
239002
238984
|
}, async (tab) => {
|
|
239003
238985
|
await tab.check(action, checked);
|
|
@@ -239233,7 +239215,7 @@ function locatorAction(selector, timeoutMs) {
|
|
|
239233
239215
|
timeoutMs
|
|
239234
239216
|
};
|
|
239235
239217
|
}
|
|
239236
|
-
function locatorActionLabel
|
|
239218
|
+
function locatorActionLabel(action) {
|
|
239237
239219
|
return action.selector ?? action.query?.kind ?? "locator";
|
|
239238
239220
|
}
|
|
239239
239221
|
//#endregion
|
|
@@ -240340,640 +240322,6 @@ function isPromiseLike(value) {
|
|
|
240340
240322
|
return !!value && typeof value.then === "function";
|
|
240341
240323
|
}
|
|
240342
240324
|
//#endregion
|
|
240343
|
-
//#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/backends/playwright-locators.js
|
|
240344
|
-
function locatorForAction(page, action) {
|
|
240345
|
-
return action.query ? locatorFromQuery(page, action.query) : page.locator(action.selector ?? "");
|
|
240346
|
-
}
|
|
240347
|
-
function locatorFromQuery(page, query) {
|
|
240348
|
-
switch (query.kind) {
|
|
240349
|
-
case "and": return locatorFromQuery(page, query.left).and(locatorFromQuery(page, query.right));
|
|
240350
|
-
case "css": return page.locator(query.selector);
|
|
240351
|
-
case "filter": return locatorFromQuery(page, query.base).filter(filterOptions(page, query.options));
|
|
240352
|
-
case "first": return locatorFromQuery(page, query.base).first();
|
|
240353
|
-
case "frame": return locatorInFrame(page, page.frameLocator(query.selector), query.child);
|
|
240354
|
-
case "label": return page.getByLabel(query.text, query.options);
|
|
240355
|
-
case "last": return locatorFromQuery(page, query.base).last();
|
|
240356
|
-
case "locator": return locatorFromQuery(page, query.base).locator(query.selector, filterOptions(page, query.options));
|
|
240357
|
-
case "nth": return locatorFromQuery(page, query.base).nth(query.index);
|
|
240358
|
-
case "or": return locatorFromQuery(page, query.left).or(locatorFromQuery(page, query.right));
|
|
240359
|
-
case "placeholder": return page.getByPlaceholder(query.text, query.options);
|
|
240360
|
-
case "role": return page.getByRole(query.role, query.options);
|
|
240361
|
-
case "testId": return page.getByTestId(query.testId);
|
|
240362
|
-
case "text": return page.getByText(query.text, query.options);
|
|
240363
|
-
}
|
|
240364
|
-
}
|
|
240365
|
-
function locatorInFrame(page, frame, query) {
|
|
240366
|
-
switch (query.kind) {
|
|
240367
|
-
case "css": return frame.locator(query.selector);
|
|
240368
|
-
case "filter": return locatorInFrame(page, frame, query.base).filter(filterOptions(page, query.options));
|
|
240369
|
-
case "first": return locatorInFrame(page, frame, query.base).first();
|
|
240370
|
-
case "label": return frame.getByLabel(query.text, query.options);
|
|
240371
|
-
case "last": return locatorInFrame(page, frame, query.base).last();
|
|
240372
|
-
case "locator": return locatorInFrame(page, frame, query.base).locator(query.selector, filterOptions(page, query.options));
|
|
240373
|
-
case "nth": return locatorInFrame(page, frame, query.base).nth(query.index);
|
|
240374
|
-
case "placeholder": return frame.getByPlaceholder(query.text, query.options);
|
|
240375
|
-
case "role": return frame.getByRole(query.role, query.options);
|
|
240376
|
-
case "testId": return frame.getByTestId(query.testId);
|
|
240377
|
-
case "text": return frame.getByText(query.text, query.options);
|
|
240378
|
-
case "and":
|
|
240379
|
-
case "frame":
|
|
240380
|
-
case "or": throw new Error(`Unsupported frame locator query kind: ${query.kind}`);
|
|
240381
|
-
}
|
|
240382
|
-
}
|
|
240383
|
-
function filterOptions(page, options) {
|
|
240384
|
-
if (!options) return void 0;
|
|
240385
|
-
return {
|
|
240386
|
-
...options.has === void 0 ? {} : { has: locatorFromQuery(page, options.has) },
|
|
240387
|
-
...options.hasNot === void 0 ? {} : { hasNot: locatorFromQuery(page, options.hasNot) },
|
|
240388
|
-
...options.hasNotText === void 0 ? {} : { hasNotText: options.hasNotText },
|
|
240389
|
-
...options.hasText === void 0 ? {} : { hasText: options.hasText }
|
|
240390
|
-
};
|
|
240391
|
-
}
|
|
240392
|
-
//#endregion
|
|
240393
|
-
//#region ../../node_modules/.bun/@cmmd-center+agent-browser@0.6.2+69e331b0475d65dc/node_modules/@cmmd-center/agent-browser/dist/backends/playwright-backend.js
|
|
240394
|
-
const TAB_METADATA_TIMEOUT_MS = 250;
|
|
240395
|
-
const TAB_CONTROL_TIMEOUT_MS = 500;
|
|
240396
|
-
var PlaywrightBackend = class {
|
|
240397
|
-
options;
|
|
240398
|
-
browser;
|
|
240399
|
-
context;
|
|
240400
|
-
pageIds = /* @__PURE__ */ new WeakMap();
|
|
240401
|
-
logs = /* @__PURE__ */ new WeakMap();
|
|
240402
|
-
registeredPages = /* @__PURE__ */ new WeakSet();
|
|
240403
|
-
constructor(options = {}) {
|
|
240404
|
-
this.options = options;
|
|
240405
|
-
}
|
|
240406
|
-
info() {
|
|
240407
|
-
return {
|
|
240408
|
-
id: "playwright",
|
|
240409
|
-
kind: "playwright",
|
|
240410
|
-
name: this.options.name ?? "Playwright Chromium"
|
|
240411
|
-
};
|
|
240412
|
-
}
|
|
240413
|
-
async close() {
|
|
240414
|
-
/* v8 ignore next -- defensive cleanup for already-closed Playwright contexts. */
|
|
240415
|
-
await this.context?.close().catch(() => void 0);
|
|
240416
|
-
await this.browser?.close();
|
|
240417
|
-
this.browser = void 0;
|
|
240418
|
-
this.context = void 0;
|
|
240419
|
-
}
|
|
240420
|
-
async listTabs() {
|
|
240421
|
-
const context = await this.getContext();
|
|
240422
|
-
return Promise.all(context.pages().map((page) => this.toTab(page).info()));
|
|
240423
|
-
}
|
|
240424
|
-
async selectedTab() {
|
|
240425
|
-
const page = (await this.getContext()).pages().at(-1);
|
|
240426
|
-
if (page) await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
|
|
240427
|
-
return page ? this.toTab(page) : void 0;
|
|
240428
|
-
}
|
|
240429
|
-
async newTab() {
|
|
240430
|
-
const page = await (await this.getContext()).newPage();
|
|
240431
|
-
await this.preparePage(page);
|
|
240432
|
-
await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
|
|
240433
|
-
return this.toTab(page);
|
|
240434
|
-
}
|
|
240435
|
-
async getTab(id) {
|
|
240436
|
-
const page = (await this.getContext()).pages().find((candidate) => this.idFor(candidate) === id);
|
|
240437
|
-
if (!page) throw new Error(`Tab not found: ${id}`);
|
|
240438
|
-
await settleValueWithin(page.bringToFront(), TAB_CONTROL_TIMEOUT_MS);
|
|
240439
|
-
return this.toTab(page);
|
|
240440
|
-
}
|
|
240441
|
-
async getContext() {
|
|
240442
|
-
/* v8 ignore next -- persistent headed/default launch combinations are covered by smoke, not unit coverage. */
|
|
240443
|
-
if (this.options.userDataDir && !this.context) {
|
|
240444
|
-
this.context = await chromium.launchPersistentContext(this.options.userDataDir, {
|
|
240445
|
-
...this.options.launchOptions,
|
|
240446
|
-
...this.options.contextOptions,
|
|
240447
|
-
headless: this.options.headless ?? false
|
|
240448
|
-
});
|
|
240449
|
-
this.browser = this.context.browser() ?? void 0;
|
|
240450
|
-
return this.context;
|
|
240451
|
-
}
|
|
240452
|
-
/* v8 ignore next -- requires a live externally managed Chrome CDP endpoint */
|
|
240453
|
-
if (!this.browser && this.options.cdpEndpoint) {
|
|
240454
|
-
/* v8 ignore start -- requires a live externally managed Chrome CDP endpoint */
|
|
240455
|
-
this.browser = await chromium.connectOverCDP(this.options.cdpEndpoint);
|
|
240456
|
-
this.context = this.browser.contexts()[0] ?? await this.browser.newContext(this.options.contextOptions);
|
|
240457
|
-
return this.context;
|
|
240458
|
-
}
|
|
240459
|
-
/* v8 ignore next -- headed/default launch is covered by browser parity smoke. */
|
|
240460
|
-
if (!this.browser) this.browser = await chromium.launch({
|
|
240461
|
-
...this.options.launchOptions,
|
|
240462
|
-
headless: this.options.headless ?? false
|
|
240463
|
-
});
|
|
240464
|
-
if (!this.context) this.context = await this.browser.newContext(this.options.contextOptions);
|
|
240465
|
-
return this.context;
|
|
240466
|
-
}
|
|
240467
|
-
toTab(page) {
|
|
240468
|
-
this.registerPage(page);
|
|
240469
|
-
return new PlaywrightTabHandle(page, this.idFor(page), this.logsFor(page));
|
|
240470
|
-
}
|
|
240471
|
-
async preparePage(page) {
|
|
240472
|
-
this.registerPage(page);
|
|
240473
|
-
const bounds = this.options.windowBounds;
|
|
240474
|
-
if (!bounds) return;
|
|
240475
|
-
await settleValueWithin(page.setViewportSize({
|
|
240476
|
-
width: bounds.width,
|
|
240477
|
-
height: bounds.height
|
|
240478
|
-
}), TAB_CONTROL_TIMEOUT_MS);
|
|
240479
|
-
const sessionResult = await settleValueWithin(page.context().newCDPSession(page), TAB_CONTROL_TIMEOUT_MS);
|
|
240480
|
-
/* v8 ignore next -- Playwright CDP attachment failures are bounded and covered by the shared timeout primitive. */
|
|
240481
|
-
if (sessionResult.status !== "fulfilled") return;
|
|
240482
|
-
const session = sessionResult.value;
|
|
240483
|
-
try {
|
|
240484
|
-
const windowResult = await settleValueWithin(session.send("Browser.getWindowForTarget"), TAB_CONTROL_TIMEOUT_MS);
|
|
240485
|
-
/* v8 ignore next -- Chromium may decline window control in unsupported headless/CDP environments. */
|
|
240486
|
-
if (windowResult.status !== "fulfilled" || typeof windowResult.value.windowId !== "number") return;
|
|
240487
|
-
await settleValueWithin(session.send("Browser.setWindowBounds", {
|
|
240488
|
-
bounds: {
|
|
240489
|
-
height: bounds.height,
|
|
240490
|
-
left: bounds.left ?? 0,
|
|
240491
|
-
top: bounds.top ?? 0,
|
|
240492
|
-
width: bounds.width,
|
|
240493
|
-
windowState: "normal"
|
|
240494
|
-
},
|
|
240495
|
-
windowId: windowResult.value.windowId
|
|
240496
|
-
}), TAB_CONTROL_TIMEOUT_MS);
|
|
240497
|
-
} finally {
|
|
240498
|
-
await settleValueWithin(session.detach(), TAB_CONTROL_TIMEOUT_MS);
|
|
240499
|
-
}
|
|
240500
|
-
}
|
|
240501
|
-
registerPage(page) {
|
|
240502
|
-
this.idFor(page);
|
|
240503
|
-
this.logsFor(page);
|
|
240504
|
-
if (this.registeredPages.has(page)) return;
|
|
240505
|
-
this.registeredPages.add(page);
|
|
240506
|
-
page.on("console", (message) => {
|
|
240507
|
-
this.logsFor(page).push(toConsoleLog(message, page.url()));
|
|
240508
|
-
});
|
|
240509
|
-
}
|
|
240510
|
-
idFor(page) {
|
|
240511
|
-
let id = this.pageIds.get(page);
|
|
240512
|
-
if (!id) {
|
|
240513
|
-
id = `tab_${randomUUID()}`;
|
|
240514
|
-
this.pageIds.set(page, id);
|
|
240515
|
-
}
|
|
240516
|
-
return id;
|
|
240517
|
-
}
|
|
240518
|
-
logsFor(page) {
|
|
240519
|
-
let entries = this.logs.get(page);
|
|
240520
|
-
if (!entries) {
|
|
240521
|
-
entries = [];
|
|
240522
|
-
this.logs.set(page, entries);
|
|
240523
|
-
}
|
|
240524
|
-
return entries;
|
|
240525
|
-
}
|
|
240526
|
-
};
|
|
240527
|
-
var PlaywrightTabHandle = class {
|
|
240528
|
-
page;
|
|
240529
|
-
id;
|
|
240530
|
-
logEntries;
|
|
240531
|
-
constructor(page, id, logEntries) {
|
|
240532
|
-
this.page = page;
|
|
240533
|
-
this.id = id;
|
|
240534
|
-
this.logEntries = logEntries;
|
|
240535
|
-
}
|
|
240536
|
-
async info() {
|
|
240537
|
-
const [title, history] = await Promise.all([settleValueWithin(this.page.title(), TAB_METADATA_TIMEOUT_MS), settleValueWithin(this.historyState(), TAB_METADATA_TIMEOUT_MS)]);
|
|
240538
|
-
const base = title.status !== "fulfilled" ? {
|
|
240539
|
-
id: this.id,
|
|
240540
|
-
url: this.page.url()
|
|
240541
|
-
} : {
|
|
240542
|
-
id: this.id,
|
|
240543
|
-
title: title.value,
|
|
240544
|
-
url: this.page.url()
|
|
240545
|
-
};
|
|
240546
|
-
/* v8 ignore next -- metadata timeout fallback is covered through AgentBrowser's bounded observation contract. */
|
|
240547
|
-
return history.status === "fulfilled" ? {
|
|
240548
|
-
...base,
|
|
240549
|
-
...history.value
|
|
240550
|
-
} : base;
|
|
240551
|
-
}
|
|
240552
|
-
async historyState() {
|
|
240553
|
-
const sessionResult = await settleValueWithin(this.page.context().newCDPSession(this.page), TAB_METADATA_TIMEOUT_MS);
|
|
240554
|
-
/* v8 ignore next -- Playwright CDP attachment failures are bounded and covered by the shared timeout primitive. */
|
|
240555
|
-
if (sessionResult.status !== "fulfilled") return {};
|
|
240556
|
-
const session = sessionResult.value;
|
|
240557
|
-
try {
|
|
240558
|
-
const historyResult = await settleValueWithin(session.send("Page.getNavigationHistory"), TAB_METADATA_TIMEOUT_MS);
|
|
240559
|
-
/* v8 ignore next -- Chromium navigation-history failures are bounded external CDP behavior. */
|
|
240560
|
-
if (historyResult.status !== "fulfilled") return {};
|
|
240561
|
-
const history = historyResult.value;
|
|
240562
|
-
return {
|
|
240563
|
-
canGoBack: history.currentIndex > 0,
|
|
240564
|
-
canGoForward: history.currentIndex < history.entries.length - 1
|
|
240565
|
-
};
|
|
240566
|
-
} finally {
|
|
240567
|
-
await settleValueWithin(session.detach(), TAB_METADATA_TIMEOUT_MS);
|
|
240568
|
-
}
|
|
240569
|
-
}
|
|
240570
|
-
async goto(url) {
|
|
240571
|
-
await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
|
240572
|
-
}
|
|
240573
|
-
async reload() {
|
|
240574
|
-
await this.page.reload({ waitUntil: "domcontentloaded" });
|
|
240575
|
-
}
|
|
240576
|
-
async forceReload() {
|
|
240577
|
-
const client = await this.page.context().newCDPSession(this.page);
|
|
240578
|
-
try {
|
|
240579
|
-
await client.send("Page.reload", { ignoreCache: true });
|
|
240580
|
-
/* v8 ignore next -- defensive fallback for navigations that do not settle after CDP reload. */
|
|
240581
|
-
await this.page.waitForLoadState("domcontentloaded", { timeout: 3e4 }).catch(() => void 0);
|
|
240582
|
-
} finally {
|
|
240583
|
-
await client.detach();
|
|
240584
|
-
}
|
|
240585
|
-
}
|
|
240586
|
-
async back() {
|
|
240587
|
-
await this.page.goBack({ waitUntil: "domcontentloaded" });
|
|
240588
|
-
}
|
|
240589
|
-
async forward() {
|
|
240590
|
-
await this.page.goForward({ waitUntil: "domcontentloaded" });
|
|
240591
|
-
}
|
|
240592
|
-
async close() {
|
|
240593
|
-
await this.page.close();
|
|
240594
|
-
}
|
|
240595
|
-
async screenshot(options = {}) {
|
|
240596
|
-
const bytes = await this.page.screenshot({
|
|
240597
|
-
...options.fullPage === void 0 ? {} : { fullPage: options.fullPage },
|
|
240598
|
-
...options.clip === void 0 ? {} : { clip: options.clip },
|
|
240599
|
-
...options.mimeType === void 0 ? {} : { type: options.mimeType === "image/jpeg" ? "jpeg" : "png" },
|
|
240600
|
-
...options.quality === void 0 ? {} : { quality: options.quality }
|
|
240601
|
-
});
|
|
240602
|
-
if (options.maxBytes !== void 0 && bytes.byteLength > options.maxBytes) throw new Error(`Browser screenshot exceeded maxBytes=${options.maxBytes}. size=${bytes.byteLength}`);
|
|
240603
|
-
return bytes;
|
|
240604
|
-
}
|
|
240605
|
-
async setViewport(size) {
|
|
240606
|
-
await this.page.setViewportSize({
|
|
240607
|
-
width: Math.round(size.width),
|
|
240608
|
-
height: Math.round(size.height)
|
|
240609
|
-
});
|
|
240610
|
-
}
|
|
240611
|
-
async setZoom(zoom) {
|
|
240612
|
-
/* v8 ignore next 3 -- browser-executed callback runs outside Node coverage */
|
|
240613
|
-
await this.page.evaluate((level) => {
|
|
240614
|
-
document.documentElement.style.zoom = String(level);
|
|
240615
|
-
}, zoom.level);
|
|
240616
|
-
}
|
|
240617
|
-
async clearCookies() {
|
|
240618
|
-
await this.page.context().clearCookies();
|
|
240619
|
-
}
|
|
240620
|
-
async clearCache() {
|
|
240621
|
-
const client = await this.page.context().newCDPSession(this.page);
|
|
240622
|
-
try {
|
|
240623
|
-
await client.send("Network.clearBrowserCache");
|
|
240624
|
-
} finally {
|
|
240625
|
-
await client.detach();
|
|
240626
|
-
}
|
|
240627
|
-
}
|
|
240628
|
-
async domSnapshot() {
|
|
240629
|
-
return this.page.locator("body").ariaSnapshot({ timeout: 5e3 });
|
|
240630
|
-
}
|
|
240631
|
-
async evaluate(expression, arg) {
|
|
240632
|
-
/* v8 ignore next 4 -- browser-executed callback runs outside Node coverage */
|
|
240633
|
-
return this.page.evaluate((input) => {
|
|
240634
|
-
return new Function("arg", `return (${input.expression})(arg);`)(input.arg);
|
|
240635
|
-
}, {
|
|
240636
|
-
arg,
|
|
240637
|
-
expression
|
|
240638
|
-
});
|
|
240639
|
-
}
|
|
240640
|
-
async waitForLoadState(state = "load", timeoutMs) {
|
|
240641
|
-
await this.page.waitForLoadState(state, timeoutOptions(timeoutMs));
|
|
240642
|
-
}
|
|
240643
|
-
async waitForURL(url, timeoutMs) {
|
|
240644
|
-
await this.page.waitForURL(url, timeoutOptions(timeoutMs));
|
|
240645
|
-
}
|
|
240646
|
-
async waitForDownload(timeoutMs) {
|
|
240647
|
-
return toBrowserDownload(await this.page.waitForEvent("download", timeoutOptions(timeoutMs)));
|
|
240648
|
-
}
|
|
240649
|
-
async elementInfo(options) {
|
|
240650
|
-
/* v8 ignore start -- browser-executed DOM inspection runs outside Node coverage */
|
|
240651
|
-
return this.page.evaluate(({ x, y, includeNonInteractable }) => {
|
|
240652
|
-
const element = document.elementFromPoint(x, y);
|
|
240653
|
-
if (!element || element === document.documentElement || element === document.body) return;
|
|
240654
|
-
const rect = element.getBoundingClientRect();
|
|
240655
|
-
if (rect.width <= 0 || rect.height <= 0) return;
|
|
240656
|
-
const style = window.getComputedStyle(element);
|
|
240657
|
-
if (!includeNonInteractable && (style.visibility === "hidden" || style.display === "none" || style.pointerEvents === "none")) return;
|
|
240658
|
-
const tagName = element.tagName.toLowerCase();
|
|
240659
|
-
const visibleText = visibleElementText(element);
|
|
240660
|
-
const ariaName = element.getAttribute("aria-label") ?? element.getAttribute("alt") ?? void 0;
|
|
240661
|
-
const role = element.getAttribute("role") ?? inferRole(element);
|
|
240662
|
-
const candidates = selectorCandidates(element);
|
|
240663
|
-
const testId = element.getAttribute("data-testid") ?? element.getAttribute("data-test-id") ?? void 0;
|
|
240664
|
-
const nodeId = ensureNodeId(element);
|
|
240665
|
-
const preview = compactText(`${tagName}${role ? ` role=${role}` : ""}${ariaName ? ` aria="${ariaName}"` : ""}${visibleText ? ` text="${visibleText}"` : ""}`);
|
|
240666
|
-
return {
|
|
240667
|
-
...ariaName ? { ariaName } : {},
|
|
240668
|
-
boundingBox: {
|
|
240669
|
-
x: rect.x,
|
|
240670
|
-
y: rect.y,
|
|
240671
|
-
width: rect.width,
|
|
240672
|
-
height: rect.height
|
|
240673
|
-
},
|
|
240674
|
-
nodeId,
|
|
240675
|
-
preview,
|
|
240676
|
-
...role ? { role } : {},
|
|
240677
|
-
selector: {
|
|
240678
|
-
candidates,
|
|
240679
|
-
primary: candidates[0] ?? null
|
|
240680
|
-
},
|
|
240681
|
-
tagName,
|
|
240682
|
-
...testId ? { testId } : {},
|
|
240683
|
-
...visibleText ? { visibleText } : {}
|
|
240684
|
-
};
|
|
240685
|
-
function compactText(value) {
|
|
240686
|
-
return value.replace(/\s+/g, " ").trim().slice(0, 180);
|
|
240687
|
-
}
|
|
240688
|
-
function visibleElementText(node) {
|
|
240689
|
-
if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) return compactText(node.value || node.placeholder || "");
|
|
240690
|
-
if (node instanceof HTMLSelectElement) return compactText(node.selectedOptions[0]?.textContent ?? node.value);
|
|
240691
|
-
return compactText(node.textContent ?? "") || void 0;
|
|
240692
|
-
}
|
|
240693
|
-
function inferRole(node) {
|
|
240694
|
-
if (node instanceof HTMLAnchorElement && node.href) return "link";
|
|
240695
|
-
if (node instanceof HTMLButtonElement) return "button";
|
|
240696
|
-
if (node instanceof HTMLInputElement) return node.type || "textbox";
|
|
240697
|
-
if (node instanceof HTMLSelectElement) return "combobox";
|
|
240698
|
-
if (node instanceof HTMLTextAreaElement) return "textbox";
|
|
240699
|
-
if (/^h[1-6]$/i.test(node.tagName)) return "heading";
|
|
240700
|
-
}
|
|
240701
|
-
function ensureNodeId(node) {
|
|
240702
|
-
const existing = node.getAttribute("data-agent-browser-node-id");
|
|
240703
|
-
if (existing) return Number(existing);
|
|
240704
|
-
const next = Number(document.documentElement.getAttribute("data-agent-browser-next-node-id") ?? "1");
|
|
240705
|
-
document.documentElement.setAttribute("data-agent-browser-next-node-id", String(next + 1));
|
|
240706
|
-
node.setAttribute("data-agent-browser-node-id", String(next));
|
|
240707
|
-
return next;
|
|
240708
|
-
}
|
|
240709
|
-
function selectorCandidates(node) {
|
|
240710
|
-
const candidates = [];
|
|
240711
|
-
if (node.id) candidates.push(`#${cssEscape(node.id)}`);
|
|
240712
|
-
const testId = node.getAttribute("data-testid") ?? node.getAttribute("data-test-id");
|
|
240713
|
-
if (testId) candidates.push(`[data-testid="${cssString(testId)}"]`);
|
|
240714
|
-
const href = node.getAttribute("href");
|
|
240715
|
-
if (href) candidates.push(`${node.tagName.toLowerCase()}[href="${cssString(href)}"]`);
|
|
240716
|
-
const aria = node.getAttribute("aria-label");
|
|
240717
|
-
if (aria) candidates.push(`${node.tagName.toLowerCase()}[aria-label="${cssString(aria)}"]`);
|
|
240718
|
-
const name = node.getAttribute("name");
|
|
240719
|
-
if (name) candidates.push(`${node.tagName.toLowerCase()}[name="${cssString(name)}"]`);
|
|
240720
|
-
const parts = [];
|
|
240721
|
-
let current = node;
|
|
240722
|
-
while (current && current !== document.body && current !== document.documentElement && parts.length < 5) {
|
|
240723
|
-
let part = current.tagName.toLowerCase();
|
|
240724
|
-
const name = current.getAttribute("name");
|
|
240725
|
-
if (name) part += `[name="${cssString(name)}"]`;
|
|
240726
|
-
else {
|
|
240727
|
-
const className = Array.from(current.classList).find(Boolean);
|
|
240728
|
-
if (className) part += `.${cssEscape(className)}`;
|
|
240729
|
-
}
|
|
240730
|
-
const parent = current.parentElement;
|
|
240731
|
-
if (parent) {
|
|
240732
|
-
const siblings = Array.from(parent.children).filter((sibling) => sibling.tagName === current?.tagName);
|
|
240733
|
-
if (siblings.length > 1) part += `:nth-of-type(${siblings.indexOf(current) + 1})`;
|
|
240734
|
-
}
|
|
240735
|
-
parts.unshift(part);
|
|
240736
|
-
const candidate = parts.join(" > ");
|
|
240737
|
-
if (document.querySelectorAll(candidate).length === 1) {
|
|
240738
|
-
candidates.push(candidate);
|
|
240739
|
-
break;
|
|
240740
|
-
}
|
|
240741
|
-
current = parent;
|
|
240742
|
-
}
|
|
240743
|
-
const fallback = parts.join(" > ") || node.tagName.toLowerCase();
|
|
240744
|
-
candidates.push(fallback);
|
|
240745
|
-
return Array.from(new Set(candidates));
|
|
240746
|
-
}
|
|
240747
|
-
function cssEscape(value) {
|
|
240748
|
-
return CSS.escape(value);
|
|
240749
|
-
}
|
|
240750
|
-
function cssString(value) {
|
|
240751
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
240752
|
-
}
|
|
240753
|
-
}, options);
|
|
240754
|
-
/* v8 ignore stop */
|
|
240755
|
-
}
|
|
240756
|
-
async elementScreenshot(options) {
|
|
240757
|
-
const element = await this.elementInfo(options);
|
|
240758
|
-
if (!element?.boundingBox) return void 0;
|
|
240759
|
-
return this.screenshot({ clip: element.boundingBox });
|
|
240760
|
-
}
|
|
240761
|
-
async click(action) {
|
|
240762
|
-
const locator = this.locatorFor(action);
|
|
240763
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240764
|
-
await locator.click(timeoutOptions(action.timeoutMs));
|
|
240765
|
-
}
|
|
240766
|
-
async doubleClick(action) {
|
|
240767
|
-
const locator = this.locatorFor(action);
|
|
240768
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240769
|
-
await locator.dblclick(timeoutOptions(action.timeoutMs));
|
|
240770
|
-
}
|
|
240771
|
-
async fill(action, value) {
|
|
240772
|
-
const locator = this.locatorFor(action);
|
|
240773
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240774
|
-
await locator.fill(value, timeoutOptions(action.timeoutMs));
|
|
240775
|
-
}
|
|
240776
|
-
async type(action, value) {
|
|
240777
|
-
const locator = this.locatorFor(action);
|
|
240778
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240779
|
-
await locator.pressSequentially(value, timeoutOptions(action.timeoutMs));
|
|
240780
|
-
}
|
|
240781
|
-
async press(action, key) {
|
|
240782
|
-
const locator = this.locatorFor(action);
|
|
240783
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240784
|
-
await locator.press(key, timeoutOptions(action.timeoutMs));
|
|
240785
|
-
}
|
|
240786
|
-
async select(action, value) {
|
|
240787
|
-
const locator = this.locatorFor(action);
|
|
240788
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240789
|
-
await locator.selectOption(value, timeoutOptions(action.timeoutMs));
|
|
240790
|
-
}
|
|
240791
|
-
async check(action, checked) {
|
|
240792
|
-
const locator = this.locatorFor(action);
|
|
240793
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240794
|
-
await locator.setChecked(checked, timeoutOptions(action.timeoutMs));
|
|
240795
|
-
}
|
|
240796
|
-
async text(action) {
|
|
240797
|
-
const locator = this.locatorFor(action);
|
|
240798
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240799
|
-
return locator.textContent(timeoutOptions(action.timeoutMs));
|
|
240800
|
-
}
|
|
240801
|
-
async innerText(action) {
|
|
240802
|
-
const locator = this.locatorFor(action);
|
|
240803
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240804
|
-
return locator.innerText(timeoutOptions(action.timeoutMs));
|
|
240805
|
-
}
|
|
240806
|
-
async allTextContents(action) {
|
|
240807
|
-
return this.locatorFor(action).allTextContents();
|
|
240808
|
-
}
|
|
240809
|
-
async attribute(action, name) {
|
|
240810
|
-
const locator = this.locatorFor(action);
|
|
240811
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240812
|
-
return locator.getAttribute(name, timeoutOptions(action.timeoutMs));
|
|
240813
|
-
}
|
|
240814
|
-
async isVisible(action) {
|
|
240815
|
-
const locator = this.locatorFor(action);
|
|
240816
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240817
|
-
return locator.isVisible(timeoutOptions(action.timeoutMs));
|
|
240818
|
-
}
|
|
240819
|
-
async isEnabled(action) {
|
|
240820
|
-
const locator = this.locatorFor(action);
|
|
240821
|
-
await assertSingle(locator.count(), locatorActionLabel(action));
|
|
240822
|
-
return locator.isEnabled(timeoutOptions(action.timeoutMs));
|
|
240823
|
-
}
|
|
240824
|
-
async locatorCount(action) {
|
|
240825
|
-
return this.locatorFor(action).count();
|
|
240826
|
-
}
|
|
240827
|
-
async waitForLocator(action, state) {
|
|
240828
|
-
await this.locatorFor(action).waitFor({
|
|
240829
|
-
...state === void 0 ? {} : { state },
|
|
240830
|
-
...timeoutOptions(action.timeoutMs)
|
|
240831
|
-
});
|
|
240832
|
-
}
|
|
240833
|
-
async cuaMove(point) {
|
|
240834
|
-
await this.page.mouse.move(point.x, point.y);
|
|
240835
|
-
}
|
|
240836
|
-
async cuaClick(point) {
|
|
240837
|
-
await this.page.mouse.click(point.x, point.y, { button: mouseButton(point.button) });
|
|
240838
|
-
}
|
|
240839
|
-
async cuaDoubleClick(point) {
|
|
240840
|
-
await this.page.mouse.dblclick(point.x, point.y);
|
|
240841
|
-
}
|
|
240842
|
-
async cuaDrag(path) {
|
|
240843
|
-
const [start, ...rest] = path;
|
|
240844
|
-
if (!start) return;
|
|
240845
|
-
await this.page.mouse.move(start.x, start.y);
|
|
240846
|
-
await this.page.mouse.down();
|
|
240847
|
-
for (const point of rest) await this.page.mouse.move(point.x, point.y);
|
|
240848
|
-
await this.page.mouse.up();
|
|
240849
|
-
}
|
|
240850
|
-
async cuaScroll(input) {
|
|
240851
|
-
await this.page.mouse.move(input.x, input.y);
|
|
240852
|
-
await this.page.mouse.wheel(input.scrollX, input.scrollY);
|
|
240853
|
-
}
|
|
240854
|
-
async cuaType(text) {
|
|
240855
|
-
await this.page.keyboard.type(text);
|
|
240856
|
-
}
|
|
240857
|
-
async cuaKeypress(keys) {
|
|
240858
|
-
await this.page.keyboard.press(keys.join("+"));
|
|
240859
|
-
}
|
|
240860
|
-
async visibleDom() {
|
|
240861
|
-
return this.page.evaluate(`(() => {
|
|
240862
|
-
let nextId = Number(document.documentElement.getAttribute("data-agent-browser-next-node-id") || "1");
|
|
240863
|
-
function walk(element) {
|
|
240864
|
-
const rect = element.getBoundingClientRect();
|
|
240865
|
-
const style = window.getComputedStyle(element);
|
|
240866
|
-
if (rect.width <= 0 || rect.height <= 0 || style.display === "none" || style.visibility === "hidden") {
|
|
240867
|
-
return undefined;
|
|
240868
|
-
}
|
|
240869
|
-
let nodeId = element.getAttribute("data-agent-browser-node-id");
|
|
240870
|
-
if (!nodeId) {
|
|
240871
|
-
nodeId = String(nextId++);
|
|
240872
|
-
element.setAttribute("data-agent-browser-node-id", nodeId);
|
|
240873
|
-
}
|
|
240874
|
-
const tagName = element.tagName.toLowerCase();
|
|
240875
|
-
const visibleText = (element.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 120) || undefined;
|
|
240876
|
-
const role = element.getAttribute("role") ?? undefined;
|
|
240877
|
-
const ariaName = element.getAttribute("aria-label") ?? undefined;
|
|
240878
|
-
const children = Array.from(element.children).map(walk).filter(Boolean);
|
|
240879
|
-
return {
|
|
240880
|
-
...(ariaName ? { ariaName } : {}),
|
|
240881
|
-
boundingBox: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
240882
|
-
...(children.length ? { children } : {}),
|
|
240883
|
-
nodeId,
|
|
240884
|
-
preview: (tagName + (ariaName ? ' "' + ariaName + '"' : "") + (visibleText ? " " + visibleText : "")).slice(0, 180),
|
|
240885
|
-
...(role ? { role } : {}),
|
|
240886
|
-
tagName,
|
|
240887
|
-
...(visibleText ? { visibleText } : {})
|
|
240888
|
-
};
|
|
240889
|
-
}
|
|
240890
|
-
document.documentElement.setAttribute("data-agent-browser-next-node-id", String(nextId));
|
|
240891
|
-
return walk(document.body) ?? { nodeId: "body", preview: "body", tagName: "body" };
|
|
240892
|
-
})()`);
|
|
240893
|
-
}
|
|
240894
|
-
async domClick(nodeId) {
|
|
240895
|
-
await this.byNodeId(nodeId).click();
|
|
240896
|
-
}
|
|
240897
|
-
async domDoubleClick(nodeId) {
|
|
240898
|
-
await this.byNodeId(nodeId).dblclick();
|
|
240899
|
-
}
|
|
240900
|
-
async domScroll(input) {
|
|
240901
|
-
if (input.nodeId) {
|
|
240902
|
-
/* v8 ignore next -- browser-executed DOM callback runs outside Node coverage */
|
|
240903
|
-
await this.byNodeId(input.nodeId).evaluate((element, delta) => element.scrollBy(delta.x, delta.y), {
|
|
240904
|
-
x: input.x,
|
|
240905
|
-
y: input.y
|
|
240906
|
-
});
|
|
240907
|
-
return;
|
|
240908
|
-
}
|
|
240909
|
-
await this.page.mouse.wheel(input.x, input.y);
|
|
240910
|
-
}
|
|
240911
|
-
async domType(text) {
|
|
240912
|
-
await this.page.keyboard.type(text);
|
|
240913
|
-
}
|
|
240914
|
-
async domKeypress(keys) {
|
|
240915
|
-
await this.page.keyboard.press(keys.join("+"));
|
|
240916
|
-
}
|
|
240917
|
-
async clipboardReadText() {
|
|
240918
|
-
/* v8 ignore next -- browser-executed callback runs outside Node coverage */
|
|
240919
|
-
return this.page.evaluate(() => navigator.clipboard.readText());
|
|
240920
|
-
}
|
|
240921
|
-
async clipboardWriteText(text) {
|
|
240922
|
-
/* v8 ignore next -- browser-executed callback runs outside Node coverage */
|
|
240923
|
-
await this.page.evaluate((value) => navigator.clipboard.writeText(value), text);
|
|
240924
|
-
}
|
|
240925
|
-
async logs(limit = 100) {
|
|
240926
|
-
return this.logEntries.slice(-limit);
|
|
240927
|
-
}
|
|
240928
|
-
byNodeId(nodeId) {
|
|
240929
|
-
return this.page.locator(`[data-agent-browser-node-id="${cssStringForSelector(nodeId)}"]`);
|
|
240930
|
-
}
|
|
240931
|
-
locatorFor(action) {
|
|
240932
|
-
return locatorForAction(this.page, action);
|
|
240933
|
-
}
|
|
240934
|
-
};
|
|
240935
|
-
function locatorActionLabel(action) {
|
|
240936
|
-
/* v8 ignore next -- valid public calls always supply selector or query; malformed calls are rejected downstream. */
|
|
240937
|
-
return action.selector ?? action.query?.kind ?? "locator";
|
|
240938
|
-
}
|
|
240939
|
-
function mouseButton(button) {
|
|
240940
|
-
if (button === 2) return "middle";
|
|
240941
|
-
if (button === 3) return "right";
|
|
240942
|
-
return "left";
|
|
240943
|
-
}
|
|
240944
|
-
function cssStringForSelector(value) {
|
|
240945
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
240946
|
-
}
|
|
240947
|
-
function timeoutOptions(timeoutMs) {
|
|
240948
|
-
return timeoutMs === void 0 ? {} : { timeout: timeoutMs };
|
|
240949
|
-
}
|
|
240950
|
-
function toBrowserDownload(download) {
|
|
240951
|
-
return {
|
|
240952
|
-
failure: () => download.failure(),
|
|
240953
|
-
path: () => download.path().catch(() => null),
|
|
240954
|
-
saveAs: (path) => download.saveAs(path),
|
|
240955
|
-
suggestedFilename: () => download.suggestedFilename(),
|
|
240956
|
-
url: () => download.url()
|
|
240957
|
-
};
|
|
240958
|
-
}
|
|
240959
|
-
async function assertSingle(countPromise, selector) {
|
|
240960
|
-
const count = await countPromise;
|
|
240961
|
-
if (count !== 1) throw new Error(`Selector must resolve to exactly one element. selector=${selector} count=${count}`);
|
|
240962
|
-
}
|
|
240963
|
-
function toConsoleLog(message, url) {
|
|
240964
|
-
return {
|
|
240965
|
-
level: normalizeLevel(message.type()),
|
|
240966
|
-
message: message.text(),
|
|
240967
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
240968
|
-
url
|
|
240969
|
-
};
|
|
240970
|
-
}
|
|
240971
|
-
function normalizeLevel(level) {
|
|
240972
|
-
if (level === "debug" || level === "info" || level === "log" || level === "warn" || level === "error") return level;
|
|
240973
|
-
/* v8 ignore next -- Playwright currently normalizes known console message types */
|
|
240974
|
-
return "log";
|
|
240975
|
-
}
|
|
240976
|
-
//#endregion
|
|
240977
240325
|
//#region ../../node_modules/.bun/debug@4.4.0+2b91fc17bf64bdfd/node_modules/debug/src/common.js
|
|
240978
240326
|
var require_common$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
240979
240327
|
/**
|
|
@@ -284114,84 +283462,6 @@ function browserProfileSessionKey(sessionId) {
|
|
|
284114
283462
|
function browserProfileDirForSession(sessionId, root = AGENT_BROWSER_PROFILE_ROOT) {
|
|
284115
283463
|
return `${root}/${browserProfileSessionKey(sessionId)}`;
|
|
284116
283464
|
}
|
|
284117
|
-
const HOST_BRIDGE_RECONNECT_MAX_MS = 1e3;
|
|
284118
|
-
/**
|
|
284119
|
-
* Exponential backoff, capped.
|
|
284120
|
-
*
|
|
284121
|
-
* Pulled out of the retry loop so the curve can be asserted directly. Inline, it
|
|
284122
|
-
* was only ever exercised by waiting for a real reconnect.
|
|
284123
|
-
*/
|
|
284124
|
-
function hostBridgeReconnectDelayMs(attempt, baseMs = 25, maxMs = HOST_BRIDGE_RECONNECT_MAX_MS) {
|
|
284125
|
-
return Math.min(baseMs * 2 ** attempt, maxMs);
|
|
284126
|
-
}
|
|
284127
|
-
/** Bearer header, in one place so no call site invents its own casing. */
|
|
284128
|
-
function hostBridgeAuthHeaders(token) {
|
|
284129
|
-
return { authorization: `Bearer ${token}` };
|
|
284130
|
-
}
|
|
284131
|
-
/** Join a path onto a base URL, tolerating a base with or without a trailing slash. */
|
|
284132
|
-
function hostBridgePathUrl(baseUrl, path) {
|
|
284133
|
-
return new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
284134
|
-
}
|
|
284135
|
-
function hostBridgeQueryUrl(baseUrl, path, query) {
|
|
284136
|
-
const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
284137
|
-
for (const [key, value] of Object.entries(query)) if (value) url.searchParams.set(key, value);
|
|
284138
|
-
return url.toString();
|
|
284139
|
-
}
|
|
284140
|
-
/**
|
|
284141
|
-
* Decode an error body from a fetch Response.
|
|
284142
|
-
*
|
|
284143
|
-
* Prefers a JSON `error` field, falls back to the raw body, then to the status
|
|
284144
|
-
* text. A server that returns HTML on failure would otherwise surface as an
|
|
284145
|
-
* empty message.
|
|
284146
|
-
*/
|
|
284147
|
-
async function readHostBridgeError(response) {
|
|
284148
|
-
const body = await response.text().catch(() => "");
|
|
284149
|
-
if (!body) return response.statusText || "Request failed.";
|
|
284150
|
-
try {
|
|
284151
|
-
const parsed = JSON.parse(body);
|
|
284152
|
-
if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
|
|
284153
|
-
} catch {}
|
|
284154
|
-
return body.trim() || response.statusText || "Request failed.";
|
|
284155
|
-
}
|
|
284156
|
-
/** Collect a Node response body. Buffers because the bodies here are error text. */
|
|
284157
|
-
async function readHostBridgeNodeResponseBody(response) {
|
|
284158
|
-
const chunks = [];
|
|
284159
|
-
for await (const chunk of response) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
284160
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
284161
|
-
}
|
|
284162
|
-
function formatHostBridgeNodeResponseError(statusCode, statusMessage, body, prefix) {
|
|
284163
|
-
const trimmedBody = body.trim();
|
|
284164
|
-
if (trimmedBody.length > 0) try {
|
|
284165
|
-
const parsed = JSON.parse(trimmedBody);
|
|
284166
|
-
if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
|
|
284167
|
-
} catch {}
|
|
284168
|
-
const fallback = trimmedBody.length > 0 ? trimmedBody : statusMessage?.trim() || "Request failed.";
|
|
284169
|
-
return `${prefix}: ${statusCode ?? 0} ${fallback}`;
|
|
284170
|
-
}
|
|
284171
|
-
/** An abort that callers can recognise by name rather than by message matching. */
|
|
284172
|
-
function hostBridgeAbortError(message) {
|
|
284173
|
-
const error = new Error(message);
|
|
284174
|
-
error.name = "AbortError";
|
|
284175
|
-
return error;
|
|
284176
|
-
}
|
|
284177
|
-
/**
|
|
284178
|
-
* Drain complete NDJSON lines, returning what is left over.
|
|
284179
|
-
*
|
|
284180
|
-
* The remainder is the whole point: a stream chunk ends wherever the socket
|
|
284181
|
-
* decides, so the trailing line is usually incomplete and must be carried into
|
|
284182
|
-
* the next chunk rather than parsed.
|
|
284183
|
-
*/
|
|
284184
|
-
function drainHostBridgeNdjsonBuffer(buffer, callback, parseLine = (line) => JSON.parse(line)) {
|
|
284185
|
-
let nextBuffer = buffer;
|
|
284186
|
-
let newlineIndex = nextBuffer.indexOf("\n");
|
|
284187
|
-
while (newlineIndex >= 0) {
|
|
284188
|
-
const line = nextBuffer.slice(0, newlineIndex).trim();
|
|
284189
|
-
nextBuffer = nextBuffer.slice(newlineIndex + 1);
|
|
284190
|
-
if (line.length > 0) callback(parseLine(line));
|
|
284191
|
-
newlineIndex = nextBuffer.indexOf("\n");
|
|
284192
|
-
}
|
|
284193
|
-
return nextBuffer;
|
|
284194
|
-
}
|
|
284195
283465
|
//#endregion
|
|
284196
283466
|
//#region src/browser/Layers/BrowserOrphanReaper.ts
|
|
284197
283467
|
/**
|
|
@@ -284531,6 +283801,301 @@ function browserControlRejectionDetail(decision) {
|
|
|
284531
283801
|
case "incomplete_metadata": return "Browser control ordering requires both sessionEpoch and controlSequence.";
|
|
284532
283802
|
}
|
|
284533
283803
|
}
|
|
283804
|
+
const HOST_BRIDGE_RECONNECT_MAX_MS = 1e3;
|
|
283805
|
+
/**
|
|
283806
|
+
* Exponential backoff, capped.
|
|
283807
|
+
*
|
|
283808
|
+
* Pulled out of the retry loop so the curve can be asserted directly. Inline, it
|
|
283809
|
+
* was only ever exercised by waiting for a real reconnect.
|
|
283810
|
+
*/
|
|
283811
|
+
function hostBridgeReconnectDelayMs(attempt, baseMs = 25, maxMs = HOST_BRIDGE_RECONNECT_MAX_MS) {
|
|
283812
|
+
return Math.min(baseMs * 2 ** attempt, maxMs);
|
|
283813
|
+
}
|
|
283814
|
+
/** Bearer header, in one place so no call site invents its own casing. */
|
|
283815
|
+
function hostBridgeAuthHeaders(token) {
|
|
283816
|
+
return { authorization: `Bearer ${token}` };
|
|
283817
|
+
}
|
|
283818
|
+
/** Join a path onto a base URL, tolerating a base with or without a trailing slash. */
|
|
283819
|
+
function hostBridgePathUrl(baseUrl, path) {
|
|
283820
|
+
return new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
283821
|
+
}
|
|
283822
|
+
function hostBridgeQueryUrl(baseUrl, path, query) {
|
|
283823
|
+
const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
283824
|
+
for (const [key, value] of Object.entries(query)) if (value) url.searchParams.set(key, value);
|
|
283825
|
+
return url.toString();
|
|
283826
|
+
}
|
|
283827
|
+
/**
|
|
283828
|
+
* Decode an error body from a fetch Response.
|
|
283829
|
+
*
|
|
283830
|
+
* Prefers a JSON `error` field, falls back to the raw body, then to the status
|
|
283831
|
+
* text. A server that returns HTML on failure would otherwise surface as an
|
|
283832
|
+
* empty message.
|
|
283833
|
+
*/
|
|
283834
|
+
async function readHostBridgeError(response) {
|
|
283835
|
+
const body = await response.text().catch(() => "");
|
|
283836
|
+
if (!body) return response.statusText || "Request failed.";
|
|
283837
|
+
try {
|
|
283838
|
+
const parsed = JSON.parse(body);
|
|
283839
|
+
if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
|
|
283840
|
+
} catch {}
|
|
283841
|
+
return body.trim() || response.statusText || "Request failed.";
|
|
283842
|
+
}
|
|
283843
|
+
/** Collect a Node response body. Buffers because the bodies here are error text. */
|
|
283844
|
+
async function readHostBridgeNodeResponseBody(response) {
|
|
283845
|
+
const chunks = [];
|
|
283846
|
+
for await (const chunk of response) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
283847
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
283848
|
+
}
|
|
283849
|
+
function formatHostBridgeNodeResponseError(statusCode, statusMessage, body, prefix) {
|
|
283850
|
+
const trimmedBody = body.trim();
|
|
283851
|
+
if (trimmedBody.length > 0) try {
|
|
283852
|
+
const parsed = JSON.parse(trimmedBody);
|
|
283853
|
+
if (typeof parsed.error === "string" && parsed.error.trim().length > 0) return parsed.error.trim();
|
|
283854
|
+
} catch {}
|
|
283855
|
+
const fallback = trimmedBody.length > 0 ? trimmedBody : statusMessage?.trim() || "Request failed.";
|
|
283856
|
+
return `${prefix}: ${statusCode ?? 0} ${fallback}`;
|
|
283857
|
+
}
|
|
283858
|
+
/** An abort that callers can recognise by name rather than by message matching. */
|
|
283859
|
+
function hostBridgeAbortError(message) {
|
|
283860
|
+
const error = new Error(message);
|
|
283861
|
+
error.name = "AbortError";
|
|
283862
|
+
return error;
|
|
283863
|
+
}
|
|
283864
|
+
/**
|
|
283865
|
+
* Drain complete NDJSON lines, returning what is left over.
|
|
283866
|
+
*
|
|
283867
|
+
* The remainder is the whole point: a stream chunk ends wherever the socket
|
|
283868
|
+
* decides, so the trailing line is usually incomplete and must be carried into
|
|
283869
|
+
* the next chunk rather than parsed.
|
|
283870
|
+
*/
|
|
283871
|
+
function drainHostBridgeNdjsonBuffer(buffer, callback, parseLine = (line) => JSON.parse(line)) {
|
|
283872
|
+
let nextBuffer = buffer;
|
|
283873
|
+
let newlineIndex = nextBuffer.indexOf("\n");
|
|
283874
|
+
while (newlineIndex >= 0) {
|
|
283875
|
+
const line = nextBuffer.slice(0, newlineIndex).trim();
|
|
283876
|
+
nextBuffer = nextBuffer.slice(newlineIndex + 1);
|
|
283877
|
+
if (line.length > 0) callback(parseLine(line));
|
|
283878
|
+
newlineIndex = nextBuffer.indexOf("\n");
|
|
283879
|
+
}
|
|
283880
|
+
return nextBuffer;
|
|
283881
|
+
}
|
|
283882
|
+
//#endregion
|
|
283883
|
+
//#region src/browser/NativeHostBrowserBridgeClient.ts
|
|
283884
|
+
const NATIVE_HOST_EVENT_RECONNECT_BASE_MS = 25;
|
|
283885
|
+
const NATIVE_HOST_EVENT_RECONNECT_MAX_MS = HOST_BRIDGE_RECONNECT_MAX_MS;
|
|
283886
|
+
function nativeHostBridgeUrl(baseUrl) {
|
|
283887
|
+
return hostBridgePathUrl(baseUrl, "/agent-browser/host-bridge");
|
|
283888
|
+
}
|
|
283889
|
+
function nativeHostBridgeEventsUrl(baseUrl, input) {
|
|
283890
|
+
return hostBridgeQueryUrl(baseUrl, "/agent-browser/host-events", {
|
|
283891
|
+
browserId: input.browserId,
|
|
283892
|
+
sessionId: input.sessionId
|
|
283893
|
+
});
|
|
283894
|
+
}
|
|
283895
|
+
async function readNativeHostBridgeError(response) {
|
|
283896
|
+
return readHostBridgeError(response);
|
|
283897
|
+
}
|
|
283898
|
+
async function readNativeHostBridgeNodeResponseBody(response) {
|
|
283899
|
+
return readHostBridgeNodeResponseBody(response);
|
|
283900
|
+
}
|
|
283901
|
+
function formatNativeHostBridgeNodeResponseError(statusCode, statusMessage, body) {
|
|
283902
|
+
return formatHostBridgeNodeResponseError(statusCode, statusMessage, body, "Native browser host bridge event stream failed");
|
|
283903
|
+
}
|
|
283904
|
+
function nativeHostBridgeAbortError() {
|
|
283905
|
+
return hostBridgeAbortError("Native browser host bridge event stream aborted.");
|
|
283906
|
+
}
|
|
283907
|
+
function parseNativeHostBridgeEventLine(line) {
|
|
283908
|
+
return JSON.parse(line);
|
|
283909
|
+
}
|
|
283910
|
+
function drainNativeHostBridgeNdjsonBuffer(buffer, callback) {
|
|
283911
|
+
return drainHostBridgeNdjsonBuffer(buffer, callback, parseNativeHostBridgeEventLine);
|
|
283912
|
+
}
|
|
283913
|
+
function subscribeToNativeHostBridgeEventsViaNodeHttp(input) {
|
|
283914
|
+
return new Promise((resolve, reject) => {
|
|
283915
|
+
if (input.abortSignal.aborted) {
|
|
283916
|
+
resolve();
|
|
283917
|
+
return;
|
|
283918
|
+
}
|
|
283919
|
+
const url = new URL(nativeHostBridgeEventsUrl(input.options.baseUrl, input.input));
|
|
283920
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
283921
|
+
let settled = false;
|
|
283922
|
+
const finalize = (handler) => {
|
|
283923
|
+
if (settled) return;
|
|
283924
|
+
settled = true;
|
|
283925
|
+
input.abortSignal.removeEventListener("abort", onAbort);
|
|
283926
|
+
handler();
|
|
283927
|
+
};
|
|
283928
|
+
const onAbort = () => {
|
|
283929
|
+
request.destroy(nativeHostBridgeAbortError());
|
|
283930
|
+
finalize(resolve);
|
|
283931
|
+
};
|
|
283932
|
+
const request = transport.request({
|
|
283933
|
+
protocol: url.protocol,
|
|
283934
|
+
hostname: url.hostname,
|
|
283935
|
+
port: url.port,
|
|
283936
|
+
path: `${url.pathname}${url.search}`,
|
|
283937
|
+
method: "GET",
|
|
283938
|
+
headers: hostBridgeAuthHeaders(input.options.token)
|
|
283939
|
+
}, (response) => {
|
|
283940
|
+
const statusCode = response.statusCode ?? 0;
|
|
283941
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
283942
|
+
readNativeHostBridgeNodeResponseBody(response).then((body) => finalize(() => reject(new Error(formatNativeHostBridgeNodeResponseError(response.statusCode, response.statusMessage, body))))).catch((error) => finalize(() => reject(error)));
|
|
283943
|
+
return;
|
|
283944
|
+
}
|
|
283945
|
+
response.setEncoding("utf8");
|
|
283946
|
+
let buffer = "";
|
|
283947
|
+
const rejectStream = (error) => {
|
|
283948
|
+
const streamError = error instanceof Error ? error : new Error(String(error ?? "Unknown stream failure"));
|
|
283949
|
+
response.destroy(streamError);
|
|
283950
|
+
finalize(() => reject(streamError));
|
|
283951
|
+
};
|
|
283952
|
+
response.on("data", (chunk) => {
|
|
283953
|
+
try {
|
|
283954
|
+
buffer = drainNativeHostBridgeNdjsonBuffer(buffer + (typeof chunk === "string" ? chunk : chunk.toString("utf8")), input.callback);
|
|
283955
|
+
} catch (error) {
|
|
283956
|
+
rejectStream(error);
|
|
283957
|
+
}
|
|
283958
|
+
});
|
|
283959
|
+
response.on("end", () => finalize(resolve));
|
|
283960
|
+
response.on("close", () => finalize(resolve));
|
|
283961
|
+
response.on("error", (error) => finalize(() => reject(error)));
|
|
283962
|
+
});
|
|
283963
|
+
request.on("socket", (socket) => {
|
|
283964
|
+
socket.setKeepAlive(true, 1e3);
|
|
283965
|
+
});
|
|
283966
|
+
request.on("error", (error) => {
|
|
283967
|
+
if (input.abortSignal.aborted && error.name === "AbortError") {
|
|
283968
|
+
finalize(resolve);
|
|
283969
|
+
return;
|
|
283970
|
+
}
|
|
283971
|
+
finalize(() => reject(error));
|
|
283972
|
+
});
|
|
283973
|
+
input.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
283974
|
+
request.end();
|
|
283975
|
+
});
|
|
283976
|
+
}
|
|
283977
|
+
async function requestNativeHostBridge(input) {
|
|
283978
|
+
const abortController = new AbortController();
|
|
283979
|
+
const timeout = setTimeout(() => abortController.abort(), input.method === "closeSession" ? input.deps.closeSessionTimeoutMs() : 3e4);
|
|
283980
|
+
const response = await input.fetchImpl(nativeHostBridgeUrl(input.options.baseUrl), {
|
|
283981
|
+
method: "POST",
|
|
283982
|
+
headers: {
|
|
283983
|
+
authorization: `Bearer ${input.options.token}`,
|
|
283984
|
+
"content-type": "application/json"
|
|
283985
|
+
},
|
|
283986
|
+
body: JSON.stringify({
|
|
283987
|
+
method: input.method,
|
|
283988
|
+
params: input.params
|
|
283989
|
+
}),
|
|
283990
|
+
signal: abortController.signal
|
|
283991
|
+
}).finally(() => {
|
|
283992
|
+
clearTimeout(timeout);
|
|
283993
|
+
});
|
|
283994
|
+
if (!response.ok) {
|
|
283995
|
+
const message = await readNativeHostBridgeError(response);
|
|
283996
|
+
throw new Error(`Native browser host bridge request failed: ${response.status} ${message}`);
|
|
283997
|
+
}
|
|
283998
|
+
return (await response.json()).result;
|
|
283999
|
+
}
|
|
284000
|
+
async function readNativeHostBridgeEventsViaFetch(input) {
|
|
284001
|
+
const response = await input.fetchImpl(nativeHostBridgeEventsUrl(input.options.baseUrl, input.identity), {
|
|
284002
|
+
headers: { authorization: `Bearer ${input.options.token}` },
|
|
284003
|
+
signal: input.abortSignal
|
|
284004
|
+
});
|
|
284005
|
+
if (!response.ok) {
|
|
284006
|
+
const message = await readNativeHostBridgeError(response);
|
|
284007
|
+
throw new Error(`Native browser host bridge event stream failed: ${response.status} ${message}`);
|
|
284008
|
+
}
|
|
284009
|
+
const reader = response.body?.getReader();
|
|
284010
|
+
if (!reader) throw new Error("Native browser host bridge event stream is unavailable.");
|
|
284011
|
+
await readNativeHostBridgeEventReader(reader, input);
|
|
284012
|
+
}
|
|
284013
|
+
async function readNativeHostBridgeEventReader(reader, input) {
|
|
284014
|
+
const decoder = new TextDecoder();
|
|
284015
|
+
let buffer = "";
|
|
284016
|
+
while (!input.abortSignal.aborted) {
|
|
284017
|
+
const { done, value } = await reader.read();
|
|
284018
|
+
if (done) return;
|
|
284019
|
+
buffer = drainNativeHostBridgeNdjsonBuffer(buffer + decoder.decode(value, { stream: true }), input.callback);
|
|
284020
|
+
}
|
|
284021
|
+
}
|
|
284022
|
+
async function readNativeHostBridgeEvents(input) {
|
|
284023
|
+
if (!input.options.fetch) {
|
|
284024
|
+
await subscribeToNativeHostBridgeEventsViaNodeHttp({
|
|
284025
|
+
abortSignal: input.abortSignal,
|
|
284026
|
+
callback: input.callback,
|
|
284027
|
+
input: input.identity,
|
|
284028
|
+
options: input.options
|
|
284029
|
+
});
|
|
284030
|
+
return;
|
|
284031
|
+
}
|
|
284032
|
+
await readNativeHostBridgeEventsViaFetch(input);
|
|
284033
|
+
}
|
|
284034
|
+
function abortableDelay(ms, signal) {
|
|
284035
|
+
if (signal.aborted) return Promise.resolve();
|
|
284036
|
+
return new Promise((resolve) => {
|
|
284037
|
+
const timeout = setTimeout(resolve, ms);
|
|
284038
|
+
signal.addEventListener("abort", () => {
|
|
284039
|
+
clearTimeout(timeout);
|
|
284040
|
+
resolve();
|
|
284041
|
+
}, { once: true });
|
|
284042
|
+
});
|
|
284043
|
+
}
|
|
284044
|
+
async function maintainNativeHostBridgeEventSubscription(input) {
|
|
284045
|
+
let attempt = 0;
|
|
284046
|
+
while (!input.abortSignal.aborted) {
|
|
284047
|
+
try {
|
|
284048
|
+
await readNativeHostBridgeEvents(input);
|
|
284049
|
+
attempt = 0;
|
|
284050
|
+
} catch (error) {
|
|
284051
|
+
if (input.abortSignal.aborted) return;
|
|
284052
|
+
console.warn("[browser] native host bridge event stream failed", error);
|
|
284053
|
+
}
|
|
284054
|
+
if (!input.abortSignal.aborted) {
|
|
284055
|
+
const delayMs = hostBridgeReconnectDelayMs(attempt, NATIVE_HOST_EVENT_RECONNECT_BASE_MS, NATIVE_HOST_EVENT_RECONNECT_MAX_MS);
|
|
284056
|
+
attempt += 1;
|
|
284057
|
+
await abortableDelay(delayMs, input.abortSignal);
|
|
284058
|
+
}
|
|
284059
|
+
}
|
|
284060
|
+
}
|
|
284061
|
+
function subscribeToNativeHostBridgeEvents(input) {
|
|
284062
|
+
const abortController = new AbortController();
|
|
284063
|
+
maintainNativeHostBridgeEventSubscription({
|
|
284064
|
+
abortSignal: abortController.signal,
|
|
284065
|
+
callback: input.callback,
|
|
284066
|
+
fetchImpl: input.fetchImpl,
|
|
284067
|
+
identity: input.identity,
|
|
284068
|
+
options: input.options
|
|
284069
|
+
});
|
|
284070
|
+
return () => {
|
|
284071
|
+
abortController.abort();
|
|
284072
|
+
};
|
|
284073
|
+
}
|
|
284074
|
+
function createNativeHostBrowserBridgeClientImplementation(options, deps) {
|
|
284075
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
284076
|
+
const request = (method, params) => requestNativeHostBridge({
|
|
284077
|
+
deps,
|
|
284078
|
+
fetchImpl,
|
|
284079
|
+
method,
|
|
284080
|
+
options,
|
|
284081
|
+
params
|
|
284082
|
+
});
|
|
284083
|
+
const subscribe = (identity, callback) => subscribeToNativeHostBridgeEvents({
|
|
284084
|
+
callback,
|
|
284085
|
+
fetchImpl,
|
|
284086
|
+
identity,
|
|
284087
|
+
options
|
|
284088
|
+
});
|
|
284089
|
+
return {
|
|
284090
|
+
closeSession: (input = {}) => request("closeSession", input),
|
|
284091
|
+
command: (input) => request("command", input),
|
|
284092
|
+
getSession: (input = {}) => request("getSession", input),
|
|
284093
|
+
listSessions: (input = {}) => request("listSessions", input),
|
|
284094
|
+
onEvent: subscribe,
|
|
284095
|
+
openSession: (input = {}) => request("openSession", input),
|
|
284096
|
+
subscribe: async (input, callback) => subscribe(input, callback)
|
|
284097
|
+
};
|
|
284098
|
+
}
|
|
284534
284099
|
//#endregion
|
|
284535
284100
|
//#region src/browser/AgentBrowserHostBridgeAdapter.ts
|
|
284536
284101
|
const DEFAULT_URL = "about:blank";
|
|
@@ -284540,8 +284105,6 @@ const DEFAULT_AGENT_BROWSER_VIEWPORT = {
|
|
|
284540
284105
|
width: 1280,
|
|
284541
284106
|
height: 720
|
|
284542
284107
|
};
|
|
284543
|
-
const NATIVE_HOST_EVENT_RECONNECT_BASE_MS = 25;
|
|
284544
|
-
const NATIVE_HOST_EVENT_RECONNECT_MAX_MS = HOST_BRIDGE_RECONNECT_MAX_MS;
|
|
284545
284108
|
const ABSOLUTE_WEB_URL_PATTERN = /^[a-z][a-z\d+\-.]*:\/\//i;
|
|
284546
284109
|
const SPECIAL_URL_PATTERN = /^(about|blob|chrome|data|devtools|file):/i;
|
|
284547
284110
|
const LOCAL_URL_LIKE_PATTERN = /^(localhost|127\.0\.0\.1|\[::1\]|::1|[a-z0-9.-]+\.localhost)(?::\d+)?(?:[/?#].*)?$/i;
|
|
@@ -284680,11 +284243,26 @@ async function closeAgentBrowserBackendWithTimeout(closeBackend, env = process.e
|
|
|
284680
284243
|
console.warn("[browser] Agent Browser Playwright backend close timed out; continuing", { timeoutMs: agentBrowserPlaywrightBackendCloseTimeoutMs(env) });
|
|
284681
284244
|
}
|
|
284682
284245
|
}
|
|
284683
|
-
|
|
284684
|
-
|
|
284685
|
-
|
|
284246
|
+
function createNativeHostBrowserBridgeClient(options) {
|
|
284247
|
+
return createNativeHostBrowserBridgeClientImplementation(options, { closeSessionTimeoutMs: agentBrowserCloseTimeoutMs });
|
|
284248
|
+
}
|
|
284249
|
+
/**
|
|
284250
|
+
* Builds the Playwright-backed backend from the lazily loaded module. The
|
|
284251
|
+
* `playwright` package (and everything that statically imports it) must stay
|
|
284252
|
+
* out of the standalone bundle's eager import graph: the bundle keeps
|
|
284253
|
+
* `playwright` external, so a static import would hoist it to the top of
|
|
284254
|
+
* `dist/bin.mjs` and the server would fail to boot on any machine that has
|
|
284255
|
+
* not provisioned playwright yet. Deferring the import to backend construction
|
|
284256
|
+
* keeps boot playwright-free and lets a later provision satisfy first use.
|
|
284257
|
+
*/
|
|
284258
|
+
function createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, options) {
|
|
284259
|
+
class ForgeAgentBrowserPlaywrightBackend extends PlaywrightBackend {
|
|
284260
|
+
async close() {
|
|
284261
|
+
await closeAgentBrowserBackendWithTimeout(() => super.close());
|
|
284262
|
+
}
|
|
284686
284263
|
}
|
|
284687
|
-
|
|
284264
|
+
return new ForgeAgentBrowserPlaywrightBackend(options);
|
|
284265
|
+
}
|
|
284688
284266
|
function toBrowserError(error, fallback) {
|
|
284689
284267
|
console.warn("[browser] Agent Browser host bridge operation failed", {
|
|
284690
284268
|
operation: fallback,
|
|
@@ -284807,193 +284385,6 @@ function resolveDisplay(snapshot, options) {
|
|
|
284807
284385
|
if (typeof options?.display === "function") return options.display(snapshot);
|
|
284808
284386
|
return options?.display ?? DEFAULT_AGENT_BROWSER_DISPLAY;
|
|
284809
284387
|
}
|
|
284810
|
-
function nativeHostBridgeUrl(baseUrl) {
|
|
284811
|
-
return hostBridgePathUrl(baseUrl, "/agent-browser/host-bridge");
|
|
284812
|
-
}
|
|
284813
|
-
function nativeHostBridgeEventsUrl(baseUrl, input) {
|
|
284814
|
-
return hostBridgeQueryUrl(baseUrl, "/agent-browser/host-events", {
|
|
284815
|
-
browserId: input.browserId,
|
|
284816
|
-
sessionId: input.sessionId
|
|
284817
|
-
});
|
|
284818
|
-
}
|
|
284819
|
-
async function readNativeHostBridgeError(response) {
|
|
284820
|
-
return readHostBridgeError(response);
|
|
284821
|
-
}
|
|
284822
|
-
async function readNativeHostBridgeNodeResponseBody(response) {
|
|
284823
|
-
return readHostBridgeNodeResponseBody(response);
|
|
284824
|
-
}
|
|
284825
|
-
function formatNativeHostBridgeNodeResponseError(statusCode, statusMessage, body) {
|
|
284826
|
-
return formatHostBridgeNodeResponseError(statusCode, statusMessage, body, "Native browser host bridge event stream failed");
|
|
284827
|
-
}
|
|
284828
|
-
function nativeHostBridgeAbortError() {
|
|
284829
|
-
return hostBridgeAbortError("Native browser host bridge event stream aborted.");
|
|
284830
|
-
}
|
|
284831
|
-
function parseNativeHostBridgeEventLine(line) {
|
|
284832
|
-
return JSON.parse(line);
|
|
284833
|
-
}
|
|
284834
|
-
function drainNativeHostBridgeNdjsonBuffer(buffer, callback) {
|
|
284835
|
-
return drainHostBridgeNdjsonBuffer(buffer, callback, parseNativeHostBridgeEventLine);
|
|
284836
|
-
}
|
|
284837
|
-
function subscribeToNativeHostBridgeEventsViaNodeHttp(input) {
|
|
284838
|
-
return new Promise((resolve, reject) => {
|
|
284839
|
-
if (input.abortSignal.aborted) {
|
|
284840
|
-
resolve();
|
|
284841
|
-
return;
|
|
284842
|
-
}
|
|
284843
|
-
const url = new URL(nativeHostBridgeEventsUrl(input.options.baseUrl, input.input));
|
|
284844
|
-
const transport = url.protocol === "https:" ? https : http;
|
|
284845
|
-
let settled = false;
|
|
284846
|
-
const finalize = (handler) => {
|
|
284847
|
-
if (settled) return;
|
|
284848
|
-
settled = true;
|
|
284849
|
-
input.abortSignal.removeEventListener("abort", onAbort);
|
|
284850
|
-
handler();
|
|
284851
|
-
};
|
|
284852
|
-
const onAbort = () => {
|
|
284853
|
-
request.destroy(nativeHostBridgeAbortError());
|
|
284854
|
-
finalize(resolve);
|
|
284855
|
-
};
|
|
284856
|
-
const request = transport.request({
|
|
284857
|
-
protocol: url.protocol,
|
|
284858
|
-
hostname: url.hostname,
|
|
284859
|
-
port: url.port,
|
|
284860
|
-
path: `${url.pathname}${url.search}`,
|
|
284861
|
-
method: "GET",
|
|
284862
|
-
headers: hostBridgeAuthHeaders(input.options.token)
|
|
284863
|
-
}, (response) => {
|
|
284864
|
-
const statusCode = response.statusCode ?? 0;
|
|
284865
|
-
if (statusCode < 200 || statusCode >= 300) {
|
|
284866
|
-
readNativeHostBridgeNodeResponseBody(response).then((body) => finalize(() => reject(new Error(formatNativeHostBridgeNodeResponseError(response.statusCode, response.statusMessage, body))))).catch((error) => finalize(() => reject(error)));
|
|
284867
|
-
return;
|
|
284868
|
-
}
|
|
284869
|
-
response.setEncoding("utf8");
|
|
284870
|
-
let buffer = "";
|
|
284871
|
-
const rejectStream = (error) => {
|
|
284872
|
-
const streamError = error instanceof Error ? error : new Error(String(error ?? "Unknown stream failure"));
|
|
284873
|
-
response.destroy(streamError);
|
|
284874
|
-
finalize(() => reject(streamError));
|
|
284875
|
-
};
|
|
284876
|
-
response.on("data", (chunk) => {
|
|
284877
|
-
try {
|
|
284878
|
-
buffer = drainNativeHostBridgeNdjsonBuffer(buffer + (typeof chunk === "string" ? chunk : chunk.toString("utf8")), input.callback);
|
|
284879
|
-
} catch (error) {
|
|
284880
|
-
rejectStream(error);
|
|
284881
|
-
}
|
|
284882
|
-
});
|
|
284883
|
-
response.on("end", () => finalize(resolve));
|
|
284884
|
-
response.on("close", () => finalize(resolve));
|
|
284885
|
-
response.on("error", (error) => finalize(() => reject(error)));
|
|
284886
|
-
});
|
|
284887
|
-
request.on("socket", (socket) => {
|
|
284888
|
-
socket.setKeepAlive(true, 1e3);
|
|
284889
|
-
});
|
|
284890
|
-
request.on("error", (error) => {
|
|
284891
|
-
if (input.abortSignal.aborted && error.name === "AbortError") {
|
|
284892
|
-
finalize(resolve);
|
|
284893
|
-
return;
|
|
284894
|
-
}
|
|
284895
|
-
finalize(() => reject(error));
|
|
284896
|
-
});
|
|
284897
|
-
input.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
284898
|
-
request.end();
|
|
284899
|
-
});
|
|
284900
|
-
}
|
|
284901
|
-
function abortableDelay(ms, signal) {
|
|
284902
|
-
if (signal.aborted) return Promise.resolve();
|
|
284903
|
-
return new Promise((resolve) => {
|
|
284904
|
-
const timeout = setTimeout(resolve, ms);
|
|
284905
|
-
signal.addEventListener("abort", () => {
|
|
284906
|
-
clearTimeout(timeout);
|
|
284907
|
-
resolve();
|
|
284908
|
-
}, { once: true });
|
|
284909
|
-
});
|
|
284910
|
-
}
|
|
284911
|
-
function createNativeHostBrowserBridgeClient(options) {
|
|
284912
|
-
const fetchImpl = options.fetch ?? fetch;
|
|
284913
|
-
const request = async (method, params) => {
|
|
284914
|
-
const abortController = new AbortController();
|
|
284915
|
-
const timeout = setTimeout(() => abortController.abort(), method === "closeSession" ? agentBrowserCloseTimeoutMs() : 3e4);
|
|
284916
|
-
const response = await fetchImpl(nativeHostBridgeUrl(options.baseUrl), {
|
|
284917
|
-
method: "POST",
|
|
284918
|
-
headers: {
|
|
284919
|
-
authorization: `Bearer ${options.token}`,
|
|
284920
|
-
"content-type": "application/json"
|
|
284921
|
-
},
|
|
284922
|
-
body: JSON.stringify({
|
|
284923
|
-
method,
|
|
284924
|
-
params
|
|
284925
|
-
}),
|
|
284926
|
-
signal: abortController.signal
|
|
284927
|
-
}).finally(() => {
|
|
284928
|
-
clearTimeout(timeout);
|
|
284929
|
-
});
|
|
284930
|
-
if (!response.ok) {
|
|
284931
|
-
const message = await readNativeHostBridgeError(response);
|
|
284932
|
-
throw new Error(`Native browser host bridge request failed: ${response.status} ${message}`);
|
|
284933
|
-
}
|
|
284934
|
-
return (await response.json()).result;
|
|
284935
|
-
};
|
|
284936
|
-
const subscribe = (input, callback) => {
|
|
284937
|
-
const abortController = new AbortController();
|
|
284938
|
-
const readStream = async () => {
|
|
284939
|
-
if (!options.fetch) {
|
|
284940
|
-
await subscribeToNativeHostBridgeEventsViaNodeHttp({
|
|
284941
|
-
abortSignal: abortController.signal,
|
|
284942
|
-
callback,
|
|
284943
|
-
input,
|
|
284944
|
-
options
|
|
284945
|
-
});
|
|
284946
|
-
return;
|
|
284947
|
-
}
|
|
284948
|
-
const response = await fetchImpl(nativeHostBridgeEventsUrl(options.baseUrl, input), {
|
|
284949
|
-
headers: { authorization: `Bearer ${options.token}` },
|
|
284950
|
-
signal: abortController.signal
|
|
284951
|
-
});
|
|
284952
|
-
if (!response.ok) {
|
|
284953
|
-
const message = await readNativeHostBridgeError(response);
|
|
284954
|
-
throw new Error(`Native browser host bridge event stream failed: ${response.status} ${message}`);
|
|
284955
|
-
}
|
|
284956
|
-
const reader = response.body?.getReader();
|
|
284957
|
-
if (!reader) throw new Error("Native browser host bridge event stream is unavailable.");
|
|
284958
|
-
const decoder = new TextDecoder();
|
|
284959
|
-
let buffer = "";
|
|
284960
|
-
while (!abortController.signal.aborted) {
|
|
284961
|
-
const { done, value } = await reader.read();
|
|
284962
|
-
if (done) return;
|
|
284963
|
-
buffer = drainNativeHostBridgeNdjsonBuffer(buffer + decoder.decode(value, { stream: true }), callback);
|
|
284964
|
-
}
|
|
284965
|
-
};
|
|
284966
|
-
(async () => {
|
|
284967
|
-
let attempt = 0;
|
|
284968
|
-
while (!abortController.signal.aborted) {
|
|
284969
|
-
try {
|
|
284970
|
-
await readStream();
|
|
284971
|
-
attempt = 0;
|
|
284972
|
-
} catch (error) {
|
|
284973
|
-
if (abortController.signal.aborted) return;
|
|
284974
|
-
console.warn("[browser] native host bridge event stream failed", error);
|
|
284975
|
-
}
|
|
284976
|
-
if (!abortController.signal.aborted) {
|
|
284977
|
-
const delayMs = hostBridgeReconnectDelayMs(attempt, NATIVE_HOST_EVENT_RECONNECT_BASE_MS, NATIVE_HOST_EVENT_RECONNECT_MAX_MS);
|
|
284978
|
-
attempt += 1;
|
|
284979
|
-
await abortableDelay(delayMs, abortController.signal);
|
|
284980
|
-
}
|
|
284981
|
-
}
|
|
284982
|
-
})();
|
|
284983
|
-
return () => {
|
|
284984
|
-
abortController.abort();
|
|
284985
|
-
};
|
|
284986
|
-
};
|
|
284987
|
-
return {
|
|
284988
|
-
closeSession: (input = {}) => request("closeSession", input),
|
|
284989
|
-
command: (input) => request("command", input),
|
|
284990
|
-
getSession: (input = {}) => request("getSession", input),
|
|
284991
|
-
listSessions: (input = {}) => request("listSessions", input),
|
|
284992
|
-
onEvent: subscribe,
|
|
284993
|
-
openSession: (input = {}) => request("openSession", input),
|
|
284994
|
-
subscribe: async (input, callback) => subscribe(input, callback)
|
|
284995
|
-
};
|
|
284996
|
-
}
|
|
284997
284388
|
function toHostCommand(command) {
|
|
284998
284389
|
switch (command.type) {
|
|
284999
284390
|
case "activateTab": return {
|
|
@@ -285659,70 +285050,100 @@ const AgentBrowserManagerLive = effect(BrowserManager, gen(function* () {
|
|
|
285659
285050
|
yield* addFinalizer$2(() => promise(() => displayTaskHoldManager?.releaseAll() ?? Promise.resolve()));
|
|
285660
285051
|
const baseBackendOptions = displayCaptureRuntime.backendOptions;
|
|
285661
285052
|
const sessionInitialUrls = /* @__PURE__ */ new Map();
|
|
285662
|
-
const
|
|
285663
|
-
|
|
285664
|
-
|
|
285665
|
-
|
|
285666
|
-
|
|
285667
|
-
|
|
285668
|
-
|
|
285669
|
-
|
|
285670
|
-
|
|
285671
|
-
|
|
285672
|
-
|
|
285673
|
-
|
|
285674
|
-
|
|
285675
|
-
|
|
285676
|
-
|
|
285677
|
-
|
|
285678
|
-
|
|
285679
|
-
|
|
285680
|
-
|
|
285681
|
-
|
|
285682
|
-
|
|
285683
|
-
|
|
285684
|
-
runtime
|
|
285685
|
-
|
|
285686
|
-
|
|
285687
|
-
|
|
285688
|
-
|
|
285689
|
-
|
|
285690
|
-
|
|
285691
|
-
|
|
285692
|
-
|
|
285693
|
-
|
|
285694
|
-
|
|
285695
|
-
|
|
285696
|
-
|
|
285697
|
-
|
|
285698
|
-
|
|
285699
|
-
|
|
285700
|
-
|
|
285701
|
-
|
|
285053
|
+
const reaperContext = yield* context();
|
|
285054
|
+
let runtimeBuild = null;
|
|
285055
|
+
const lazyDisposers = [];
|
|
285056
|
+
const buildPlaywrightRuntime = async () => {
|
|
285057
|
+
const { PlaywrightBackend } = await import("./playwright-backend-DVQOhqRO.mjs");
|
|
285058
|
+
const runtime = createAgentBrowserRuntime({
|
|
285059
|
+
backends: { [DEFAULT_AGENT_BROWSER_ID]: createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, {
|
|
285060
|
+
...browserBackendOptionsForSession({
|
|
285061
|
+
baseOptions: baseBackendOptions,
|
|
285062
|
+
sessionId: "default"
|
|
285063
|
+
}),
|
|
285064
|
+
name: "Forge Agent Browser"
|
|
285065
|
+
}) },
|
|
285066
|
+
backendFactory: (input) => createForgeAgentBrowserPlaywrightBackend(PlaywrightBackend, {
|
|
285067
|
+
...browserBackendOptionsForSession({
|
|
285068
|
+
baseOptions: baseBackendOptions,
|
|
285069
|
+
initialUrl: sessionInitialUrls.get(input.sessionId),
|
|
285070
|
+
sessionId: input.sessionId
|
|
285071
|
+
}),
|
|
285072
|
+
name: `Forge Agent Browser ${input.sessionId}`
|
|
285073
|
+
})
|
|
285074
|
+
});
|
|
285075
|
+
lazyDisposers.push(() => runtime.close());
|
|
285076
|
+
const hostBridge = createHostBrowserBridge({
|
|
285077
|
+
browserId: DEFAULT_AGENT_BROWSER_ID,
|
|
285078
|
+
closeTabsOnSessionClose: false,
|
|
285079
|
+
defaultViewport: DEFAULT_AGENT_BROWSER_VIEWPORT,
|
|
285080
|
+
runtime,
|
|
285081
|
+
tabLifecycle: {
|
|
285082
|
+
activateNextTabOnClose: true,
|
|
285083
|
+
createFallbackTabOnLastClose: true,
|
|
285084
|
+
fallbackUrl: DEFAULT_URL
|
|
285085
|
+
}
|
|
285086
|
+
});
|
|
285087
|
+
const reaperConfig = browserOrphanReaperConfigFromEnv(process.env, AGENT_BROWSER_PROFILE_ROOT);
|
|
285088
|
+
if (reaperConfig.enabled && process.platform !== "win32") {
|
|
285089
|
+
const reaper = createBrowserOrphanReaper(createSystemBrowserOrphanReaperDeps({
|
|
285090
|
+
profileRoot: AGENT_BROWSER_PROFILE_ROOT,
|
|
285091
|
+
listSessions: () => hostBridge.listSessions(),
|
|
285092
|
+
closeSession: async (sessionId) => {
|
|
285093
|
+
await hostBridge.closeSession({ sessionId: BrowserSessionId.make(sessionId) });
|
|
285094
|
+
}
|
|
285095
|
+
}), reaperConfig);
|
|
285096
|
+
const runReaperFork = runForkWith(reaperContext);
|
|
285097
|
+
const reaperInterval = setInterval(() => {
|
|
285098
|
+
runReaperFork(reaper.sweep);
|
|
285099
|
+
}, reaperConfig.intervalMs);
|
|
285100
|
+
reaperInterval.unref?.();
|
|
285101
|
+
lazyDisposers.push(() => clearInterval(reaperInterval));
|
|
285702
285102
|
runReaperFork(reaper.sweep);
|
|
285703
|
-
}
|
|
285704
|
-
|
|
285705
|
-
|
|
285706
|
-
|
|
285707
|
-
|
|
285708
|
-
|
|
285709
|
-
|
|
285710
|
-
|
|
285711
|
-
|
|
285712
|
-
|
|
285713
|
-
|
|
285714
|
-
|
|
285715
|
-
|
|
285716
|
-
|
|
285717
|
-
|
|
285718
|
-
|
|
285719
|
-
|
|
285720
|
-
|
|
285721
|
-
|
|
285722
|
-
|
|
285103
|
+
}
|
|
285104
|
+
return createAgentBrowserHostBridgeManager(hostBridge, {
|
|
285105
|
+
display: (snapshot) => agentBrowserDisplayForRuntimeMode({
|
|
285106
|
+
mode: serverConfig.mode,
|
|
285107
|
+
iceServers: serverConfig.browserWebRtcIceServerResolver.getCachedIceServers().length > 0 ? serverConfig.browserWebRtcIceServerResolver.getCachedIceServers() : serverConfig.browserWebRtcIceServers,
|
|
285108
|
+
requiresRelay: requiresWebRtcRelay,
|
|
285109
|
+
viewport: snapshot.viewport
|
|
285110
|
+
}),
|
|
285111
|
+
displayIceServers: serverConfig.browserWebRtcIceServers,
|
|
285112
|
+
resolveDisplayIceServers: serverConfig.browserWebRtcIceServerResolver.resolveIceServers,
|
|
285113
|
+
displayPeers,
|
|
285114
|
+
prepareRuntimeSession: (input) => {
|
|
285115
|
+
sessionInitialUrls.set(input.sessionId, input.initialUrl);
|
|
285116
|
+
},
|
|
285117
|
+
forgetRuntimeSession: (input) => {
|
|
285118
|
+
sessionInitialUrls.delete(input.sessionId);
|
|
285119
|
+
},
|
|
285120
|
+
requiresWebRtcRelay
|
|
285121
|
+
});
|
|
285122
|
+
};
|
|
285123
|
+
yield* addFinalizer$2(() => promise(async () => {
|
|
285124
|
+
for (const dispose of lazyDisposers.toReversed()) await dispose();
|
|
285125
|
+
}).pipe(orDie));
|
|
285126
|
+
const ensurePlaywrightRuntime = () => tryPromise({
|
|
285127
|
+
try: () => {
|
|
285128
|
+
runtimeBuild ??= buildPlaywrightRuntime();
|
|
285129
|
+
return runtimeBuild;
|
|
285723
285130
|
},
|
|
285724
|
-
|
|
285131
|
+
catch: (cause) => {
|
|
285132
|
+
runtimeBuild = null;
|
|
285133
|
+
return toBrowserError(cause, "Browser backend could not be loaded. Browser packages load on first browser use and must be provisioned on this runtime.");
|
|
285134
|
+
}
|
|
285725
285135
|
});
|
|
285136
|
+
return {
|
|
285137
|
+
openSession: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.openSession(input)),
|
|
285138
|
+
closeSession: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.closeSession(input)),
|
|
285139
|
+
clearBrowsingData: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.clearBrowsingData(input)),
|
|
285140
|
+
getSession: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.getSession(input)),
|
|
285141
|
+
listSessions: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.listSessions(input)),
|
|
285142
|
+
command: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.command(input)),
|
|
285143
|
+
attachDisplay: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.attachDisplay(input)),
|
|
285144
|
+
addDisplayIceCandidate: (input) => flatMap(ensurePlaywrightRuntime(), (m) => m.addDisplayIceCandidate(input)),
|
|
285145
|
+
subscribe: (input, listener) => flatMap(orDie(ensurePlaywrightRuntime()), (m) => m.subscribe(input, listener))
|
|
285146
|
+
};
|
|
285726
285147
|
}));
|
|
285727
285148
|
//#endregion
|
|
285728
285149
|
//#region src/devTunnel/http.ts
|
|
@@ -297678,7 +297099,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
297678
297099
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
297679
297100
|
*/
|
|
297680
297101
|
function readBakedBuildCommit() {
|
|
297681
|
-
return "
|
|
297102
|
+
return "e0ef2b5174f2f50934814b1865cabaa9c7de0ce7";
|
|
297682
297103
|
}
|
|
297683
297104
|
async function resolveServerBuildCommit(input) {
|
|
297684
297105
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -321180,13 +320601,13 @@ const gitUrlCloneRouteLayer = add$1("POST", "/api/cloud/git/clone-url", gen(func
|
|
|
321180
320601
|
//#endregion
|
|
321181
320602
|
//#region src/server.ts
|
|
321182
320603
|
const PtyAdapterLive = unwrap(gen(function* () {
|
|
321183
|
-
if (typeof Bun !== "undefined") return (yield* promise(() => import("./BunPTY-
|
|
321184
|
-
else return (yield* promise(() => import("./NodePTY-
|
|
320604
|
+
if (typeof Bun !== "undefined") return (yield* promise(() => import("./BunPTY-Csgwhbi5.mjs"))).layer;
|
|
320605
|
+
else return (yield* promise(() => import("./NodePTY-DgXWLtWp.mjs"))).layer;
|
|
321185
320606
|
}));
|
|
321186
320607
|
const HttpServerLive = unwrap(gen(function* () {
|
|
321187
320608
|
const config = yield* ServerConfig;
|
|
321188
|
-
if (typeof Bun !== "undefined") return (yield* promise(() => import("./BunHttpServer-
|
|
321189
|
-
const [NodeHttpServer, NodeHttp] = yield* all([promise(() => import("./NodeHttpServer-
|
|
320609
|
+
if (typeof Bun !== "undefined") return (yield* promise(() => import("./BunHttpServer-DjilmGOM.mjs"))).layer(bunHttpServerOptions(config));
|
|
320610
|
+
const [NodeHttpServer, NodeHttp] = yield* all([promise(() => import("./NodeHttpServer-Ds3Gp1Gp.mjs")), promise(() => import("node:http"))]);
|
|
321190
320611
|
return NodeHttpServer.layer(NodeHttp.createServer, {
|
|
321191
320612
|
host: config.host,
|
|
321192
320613
|
port: config.port
|
|
@@ -321194,7 +320615,7 @@ const HttpServerLive = unwrap(gen(function* () {
|
|
|
321194
320615
|
}));
|
|
321195
320616
|
const PlatformServicesLive = unwrap(gen(function* () {
|
|
321196
320617
|
if (typeof Bun !== "undefined") {
|
|
321197
|
-
const { layer } = yield* promise(() => import("./BunServices-
|
|
320618
|
+
const { layer } = yield* promise(() => import("./BunServices-BZKzHdrG.mjs"));
|
|
321198
320619
|
return layer;
|
|
321199
320620
|
} else {
|
|
321200
320621
|
const { layer } = yield* promise(() => import("./NodeServices-D3IxOzHl.mjs").then((n) => n.t));
|
|
@@ -322192,7 +321613,7 @@ const connectLoginCommand = make$34("login", {
|
|
|
322192
321613
|
})).json();
|
|
322193
321614
|
},
|
|
322194
321615
|
openBrowser: async (url) => {
|
|
322195
|
-
const open = (await import("./open-
|
|
321616
|
+
const open = (await import("./open-2-reBrpv.mjs")).default;
|
|
322196
321617
|
await open(url);
|
|
322197
321618
|
return true;
|
|
322198
321619
|
},
|