@browserbasehq/orca 3.0.0-test.1 → 3.0.1-zod4
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/README.md +164 -0
- package/dist/index.d.ts +253 -11
- package/dist/index.js +1513 -63
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -603,6 +603,19 @@ function captureHybridSnapshot(page, options) {
|
|
|
603
603
|
const perFrameMaps = /* @__PURE__ */ new Map();
|
|
604
604
|
const requestedFocus = (_b = options == null ? void 0 : options.focusSelector) == null ? void 0 : _b.trim();
|
|
605
605
|
if (requestedFocus) {
|
|
606
|
+
const logScopeFallback = () => {
|
|
607
|
+
var _a2;
|
|
608
|
+
v3Logger({
|
|
609
|
+
message: `Unable to narrow scope with selector. Falling back to using full DOM`,
|
|
610
|
+
level: 1,
|
|
611
|
+
auxiliary: {
|
|
612
|
+
arguments: {
|
|
613
|
+
value: `selector: ${(_a2 = options == null ? void 0 : options.focusSelector) == null ? void 0 : _a2.trim()}`,
|
|
614
|
+
type: "string"
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
};
|
|
606
619
|
try {
|
|
607
620
|
let targetFrameId;
|
|
608
621
|
let tailSelector;
|
|
@@ -641,7 +654,7 @@ function captureHybridSnapshot(page, options) {
|
|
|
641
654
|
/*attemptOwnerLookup=*/
|
|
642
655
|
sameSessionAsParent
|
|
643
656
|
);
|
|
644
|
-
const { outline, urlMap } = yield a11yForFrame(
|
|
657
|
+
const { outline, urlMap, scopeApplied } = yield a11yForFrame(
|
|
645
658
|
owningSess,
|
|
646
659
|
targetFrameId,
|
|
647
660
|
{
|
|
@@ -663,7 +676,7 @@ function captureHybridSnapshot(page, options) {
|
|
|
663
676
|
}
|
|
664
677
|
}
|
|
665
678
|
const combinedUrlMap2 = __spreadValues({}, urlMap);
|
|
666
|
-
|
|
679
|
+
const snapshot = {
|
|
667
680
|
combinedTree: outline,
|
|
668
681
|
combinedXpathMap: combinedXpathMap2,
|
|
669
682
|
combinedUrlMap: combinedUrlMap2,
|
|
@@ -676,7 +689,12 @@ function captureHybridSnapshot(page, options) {
|
|
|
676
689
|
}
|
|
677
690
|
]
|
|
678
691
|
};
|
|
692
|
+
if (scopeApplied) {
|
|
693
|
+
return snapshot;
|
|
694
|
+
}
|
|
695
|
+
logScopeFallback();
|
|
679
696
|
} catch (e) {
|
|
697
|
+
logScopeFallback();
|
|
680
698
|
}
|
|
681
699
|
}
|
|
682
700
|
function buildSessionDomIndex(session, pierce2) {
|
|
@@ -1144,6 +1162,7 @@ function a11yForFrame(session, frameId, opts) {
|
|
|
1144
1162
|
const enc = opts.encode(be);
|
|
1145
1163
|
urlMap[enc] = url;
|
|
1146
1164
|
}
|
|
1165
|
+
let scopeApplied = false;
|
|
1147
1166
|
const nodesForOutline = yield (() => __async(null, null, function* () {
|
|
1148
1167
|
var _a2, _b2, _c;
|
|
1149
1168
|
const sel = (_a2 = opts.focusSelector) == null ? void 0 : _a2.trim();
|
|
@@ -1160,6 +1179,7 @@ function a11yForFrame(session, frameId, opts) {
|
|
|
1160
1179
|
if (typeof be !== "number") return nodes;
|
|
1161
1180
|
const target = nodes.find((n) => n.backendDOMNodeId === be);
|
|
1162
1181
|
if (!target) return nodes;
|
|
1182
|
+
scopeApplied = true;
|
|
1163
1183
|
const keep = /* @__PURE__ */ new Set([target.nodeId]);
|
|
1164
1184
|
const queue = [target];
|
|
1165
1185
|
while (queue.length) {
|
|
@@ -1181,7 +1201,7 @@ function a11yForFrame(session, frameId, opts) {
|
|
|
1181
1201
|
const decorated = decorateRoles(nodesForOutline, opts);
|
|
1182
1202
|
const { tree } = yield buildHierarchicalTree(decorated, opts);
|
|
1183
1203
|
const simplified = tree.map((n) => formatTreeLine(n)).join("\n");
|
|
1184
|
-
return { outline: simplified.trimEnd(), urlMap };
|
|
1204
|
+
return { outline: simplified.trimEnd(), urlMap, scopeApplied };
|
|
1185
1205
|
});
|
|
1186
1206
|
}
|
|
1187
1207
|
function resolveObjectIdForXPath(session, xpath, frameId) {
|
|
@@ -1532,6 +1552,7 @@ var IFRAME_STEP_RE;
|
|
|
1532
1552
|
var init_snapshot = __esm({
|
|
1533
1553
|
"lib/v3/understudy/a11y/snapshot.ts"() {
|
|
1534
1554
|
init_executionContextRegistry();
|
|
1555
|
+
init_logger();
|
|
1535
1556
|
IFRAME_STEP_RE = /^iframe(?:\[\d+])?$/i;
|
|
1536
1557
|
}
|
|
1537
1558
|
});
|
|
@@ -3908,11 +3929,11 @@ var require_src = __commonJS({
|
|
|
3908
3929
|
var require_is_docker = __commonJS({
|
|
3909
3930
|
"../../node_modules/.pnpm/is-docker@2.2.1/node_modules/is-docker/index.js"(exports2, module2) {
|
|
3910
3931
|
"use strict";
|
|
3911
|
-
var
|
|
3932
|
+
var fs8 = require("fs");
|
|
3912
3933
|
var isDocker;
|
|
3913
3934
|
function hasDockerEnv() {
|
|
3914
3935
|
try {
|
|
3915
|
-
|
|
3936
|
+
fs8.statSync("/.dockerenv");
|
|
3916
3937
|
return true;
|
|
3917
3938
|
} catch (_) {
|
|
3918
3939
|
return false;
|
|
@@ -3920,7 +3941,7 @@ var require_is_docker = __commonJS({
|
|
|
3920
3941
|
}
|
|
3921
3942
|
function hasDockerCGroup() {
|
|
3922
3943
|
try {
|
|
3923
|
-
return
|
|
3944
|
+
return fs8.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
3924
3945
|
} catch (_) {
|
|
3925
3946
|
return false;
|
|
3926
3947
|
}
|
|
@@ -3939,7 +3960,7 @@ var require_is_wsl = __commonJS({
|
|
|
3939
3960
|
"../../node_modules/.pnpm/is-wsl@2.2.0/node_modules/is-wsl/index.js"(exports2, module2) {
|
|
3940
3961
|
"use strict";
|
|
3941
3962
|
var os3 = require("os");
|
|
3942
|
-
var
|
|
3963
|
+
var fs8 = require("fs");
|
|
3943
3964
|
var isDocker = require_is_docker();
|
|
3944
3965
|
var isWsl3 = () => {
|
|
3945
3966
|
if (process.platform !== "linux") {
|
|
@@ -3952,7 +3973,7 @@ var require_is_wsl = __commonJS({
|
|
|
3952
3973
|
return true;
|
|
3953
3974
|
}
|
|
3954
3975
|
try {
|
|
3955
|
-
return
|
|
3976
|
+
return fs8.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isDocker() : false;
|
|
3956
3977
|
} catch (_) {
|
|
3957
3978
|
return false;
|
|
3958
3979
|
}
|
|
@@ -3965,6 +3986,342 @@ var require_is_wsl = __commonJS({
|
|
|
3965
3986
|
}
|
|
3966
3987
|
});
|
|
3967
3988
|
|
|
3989
|
+
// lib/v3/understudy/consoleMessage.ts
|
|
3990
|
+
function formatRemoteObject(obj) {
|
|
3991
|
+
var _a;
|
|
3992
|
+
if (!obj) return "";
|
|
3993
|
+
if ("value" in obj) {
|
|
3994
|
+
const value = obj.value;
|
|
3995
|
+
if (value === void 0) return "";
|
|
3996
|
+
if (typeof value === "string") return value;
|
|
3997
|
+
try {
|
|
3998
|
+
return JSON.stringify(value);
|
|
3999
|
+
} catch (e) {
|
|
4000
|
+
return String(value);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
if (obj.unserializableValue) return obj.unserializableValue;
|
|
4004
|
+
if (obj.description) return obj.description;
|
|
4005
|
+
return (_a = obj.type) != null ? _a : "";
|
|
4006
|
+
}
|
|
4007
|
+
var ConsoleMessage;
|
|
4008
|
+
var init_consoleMessage = __esm({
|
|
4009
|
+
"lib/v3/understudy/consoleMessage.ts"() {
|
|
4010
|
+
ConsoleMessage = class {
|
|
4011
|
+
constructor(event, pageRef) {
|
|
4012
|
+
this.event = event;
|
|
4013
|
+
this.pageRef = pageRef;
|
|
4014
|
+
}
|
|
4015
|
+
type() {
|
|
4016
|
+
return this.event.type;
|
|
4017
|
+
}
|
|
4018
|
+
text() {
|
|
4019
|
+
const args = this.args();
|
|
4020
|
+
if (!args.length) return "";
|
|
4021
|
+
return args.map((arg) => formatRemoteObject(arg)).filter((chunk) => chunk.length > 0).join(" ");
|
|
4022
|
+
}
|
|
4023
|
+
args() {
|
|
4024
|
+
return this.event.args ? [...this.event.args] : [];
|
|
4025
|
+
}
|
|
4026
|
+
location() {
|
|
4027
|
+
var _a, _b;
|
|
4028
|
+
const frame = (_b = (_a = this.event.stackTrace) == null ? void 0 : _a.callFrames) == null ? void 0 : _b[0];
|
|
4029
|
+
return {
|
|
4030
|
+
url: frame == null ? void 0 : frame.url,
|
|
4031
|
+
lineNumber: frame == null ? void 0 : frame.lineNumber,
|
|
4032
|
+
columnNumber: frame == null ? void 0 : frame.columnNumber
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
4035
|
+
page() {
|
|
4036
|
+
return this.pageRef;
|
|
4037
|
+
}
|
|
4038
|
+
timestamp() {
|
|
4039
|
+
return this.event.timestamp;
|
|
4040
|
+
}
|
|
4041
|
+
raw() {
|
|
4042
|
+
return this.event;
|
|
4043
|
+
}
|
|
4044
|
+
toString() {
|
|
4045
|
+
return this.text();
|
|
4046
|
+
}
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
});
|
|
4050
|
+
|
|
4051
|
+
// lib/v3/understudy/response.ts
|
|
4052
|
+
function createDeferred() {
|
|
4053
|
+
let resolve3;
|
|
4054
|
+
let reject;
|
|
4055
|
+
const promise = new Promise((res, rej) => {
|
|
4056
|
+
resolve3 = res;
|
|
4057
|
+
reject = rej;
|
|
4058
|
+
});
|
|
4059
|
+
return { promise, resolve: resolve3, reject };
|
|
4060
|
+
}
|
|
4061
|
+
function normaliseHeaderName(name) {
|
|
4062
|
+
return name.toLowerCase();
|
|
4063
|
+
}
|
|
4064
|
+
function splitHeaderValues(value) {
|
|
4065
|
+
return value.split(/\r?\n/).map((part) => part.trim()).filter(Boolean);
|
|
4066
|
+
}
|
|
4067
|
+
function parseHeadersText(headersText) {
|
|
4068
|
+
if (!headersText) return [];
|
|
4069
|
+
const lines = headersText.split(/\r?\n/);
|
|
4070
|
+
const entries = [];
|
|
4071
|
+
for (const line of lines) {
|
|
4072
|
+
if (!line || line.startsWith("HTTP/")) continue;
|
|
4073
|
+
const index = line.indexOf(":");
|
|
4074
|
+
if (index === -1) continue;
|
|
4075
|
+
const name = line.slice(0, index).trim();
|
|
4076
|
+
const value = line.slice(index + 1).trim();
|
|
4077
|
+
entries.push({ name, value });
|
|
4078
|
+
}
|
|
4079
|
+
return entries;
|
|
4080
|
+
}
|
|
4081
|
+
var Response;
|
|
4082
|
+
var init_response = __esm({
|
|
4083
|
+
"lib/v3/understudy/response.ts"() {
|
|
4084
|
+
Response = class {
|
|
4085
|
+
/**
|
|
4086
|
+
* Build a response wrapper from the CDP notification associated with a
|
|
4087
|
+
* navigation. The constructor captures the owning page/session so follow-up
|
|
4088
|
+
* methods (body/text/json) can query CDP on-demand. The `response` payload is
|
|
4089
|
+
* the raw `Protocol.Network.Response` object emitted by Chrome.
|
|
4090
|
+
*/
|
|
4091
|
+
constructor(params) {
|
|
4092
|
+
this.headersArrayCache = null;
|
|
4093
|
+
this.allHeadersCache = null;
|
|
4094
|
+
this.headerValuesMap = /* @__PURE__ */ new Map();
|
|
4095
|
+
this.finishedDeferred = createDeferred();
|
|
4096
|
+
this.finishedSettled = false;
|
|
4097
|
+
this.extraInfoHeaders = null;
|
|
4098
|
+
var _a;
|
|
4099
|
+
this.page = params.page;
|
|
4100
|
+
this.session = params.session;
|
|
4101
|
+
this.requestId = params.requestId;
|
|
4102
|
+
this.frameId = params.frameId;
|
|
4103
|
+
this.loaderId = params.loaderId;
|
|
4104
|
+
this.response = params.response;
|
|
4105
|
+
this.fromServiceWorkerFlag = params.fromServiceWorker;
|
|
4106
|
+
if (params.response.remoteIPAddress && params.response.remotePort !== void 0) {
|
|
4107
|
+
this.serverAddress = {
|
|
4108
|
+
ipAddress: params.response.remoteIPAddress,
|
|
4109
|
+
port: params.response.remotePort
|
|
4110
|
+
};
|
|
4111
|
+
} else {
|
|
4112
|
+
this.serverAddress = null;
|
|
4113
|
+
}
|
|
4114
|
+
this.headersObject = {};
|
|
4115
|
+
for (const [name, value] of Object.entries((_a = this.response.headers) != null ? _a : {})) {
|
|
4116
|
+
const lower = normaliseHeaderName(name);
|
|
4117
|
+
if (value === void 0) continue;
|
|
4118
|
+
const values = splitHeaderValues(String(value));
|
|
4119
|
+
this.headerValuesMap.set(lower, values);
|
|
4120
|
+
this.headersObject[lower] = values.join(", ");
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
/** URL associated with the navigation request. */
|
|
4124
|
+
url() {
|
|
4125
|
+
return this.response.url;
|
|
4126
|
+
}
|
|
4127
|
+
/** HTTP status code reported by Chrome. */
|
|
4128
|
+
status() {
|
|
4129
|
+
return this.response.status;
|
|
4130
|
+
}
|
|
4131
|
+
/** Human-readable status text that accompanied the response. */
|
|
4132
|
+
statusText() {
|
|
4133
|
+
return this.response.statusText;
|
|
4134
|
+
}
|
|
4135
|
+
/** Convenience predicate that checks for 2xx statuses. */
|
|
4136
|
+
ok() {
|
|
4137
|
+
const status = this.status();
|
|
4138
|
+
return status >= 200 && status <= 299;
|
|
4139
|
+
}
|
|
4140
|
+
/** Returns the Stagehand frame object that initiated the navigation. */
|
|
4141
|
+
frame() {
|
|
4142
|
+
if (!this.frameId) return null;
|
|
4143
|
+
try {
|
|
4144
|
+
return this.page.frameForId(this.frameId);
|
|
4145
|
+
} catch (e) {
|
|
4146
|
+
return null;
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
/** Indicates whether the response was serviced by a Service Worker. */
|
|
4150
|
+
fromServiceWorker() {
|
|
4151
|
+
return this.fromServiceWorkerFlag;
|
|
4152
|
+
}
|
|
4153
|
+
/**
|
|
4154
|
+
* Returns TLS security metadata when provided by the browser. In practice
|
|
4155
|
+
* this includes certificate issuer, protocol, and validity interval.
|
|
4156
|
+
*/
|
|
4157
|
+
securityDetails() {
|
|
4158
|
+
return __async(this, null, function* () {
|
|
4159
|
+
var _a;
|
|
4160
|
+
return (_a = this.response.securityDetails) != null ? _a : null;
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
/** Returns the resolved server address for the navigation when available. */
|
|
4164
|
+
serverAddr() {
|
|
4165
|
+
return __async(this, null, function* () {
|
|
4166
|
+
var _a;
|
|
4167
|
+
return (_a = this.serverAddress) != null ? _a : null;
|
|
4168
|
+
});
|
|
4169
|
+
}
|
|
4170
|
+
/**
|
|
4171
|
+
* Returns the response headers normalised to lowercase keys. Matches the
|
|
4172
|
+
* behaviour of Playwright's `headers()` by eliding duplicate header entries.
|
|
4173
|
+
*/
|
|
4174
|
+
headers() {
|
|
4175
|
+
return __spreadValues({}, this.headersObject);
|
|
4176
|
+
}
|
|
4177
|
+
/**
|
|
4178
|
+
* Returns all headers including those only surfaced through
|
|
4179
|
+
* `responseReceivedExtraInfo` such as `set-cookie`. Values are reported as the
|
|
4180
|
+
* browser sends them (no further splitting or concatenation).
|
|
4181
|
+
*/
|
|
4182
|
+
allHeaders() {
|
|
4183
|
+
return __async(this, null, function* () {
|
|
4184
|
+
var _a, _b;
|
|
4185
|
+
if (this.allHeadersCache) return __spreadValues({}, this.allHeadersCache);
|
|
4186
|
+
const source = (_b = (_a = this.extraInfoHeaders) != null ? _a : this.response.headers) != null ? _b : {};
|
|
4187
|
+
const map = {};
|
|
4188
|
+
for (const [name, value] of Object.entries(source)) {
|
|
4189
|
+
map[name] = String(value);
|
|
4190
|
+
}
|
|
4191
|
+
this.allHeadersCache = map;
|
|
4192
|
+
return __spreadValues({}, map);
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
/** Returns a concatenated header string for the supplied header name. */
|
|
4196
|
+
headerValue(name) {
|
|
4197
|
+
return __async(this, null, function* () {
|
|
4198
|
+
const values = yield this.headerValues(name);
|
|
4199
|
+
if (!values.length) return null;
|
|
4200
|
+
return values.join(", ");
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
/** Returns all values for a header (case-insensitive lookup). */
|
|
4204
|
+
headerValues(name) {
|
|
4205
|
+
return __async(this, null, function* () {
|
|
4206
|
+
var _a;
|
|
4207
|
+
const lower = normaliseHeaderName(name);
|
|
4208
|
+
if (this.extraInfoHeaders) {
|
|
4209
|
+
const raw = (_a = this.extraInfoHeaders[name]) != null ? _a : this.extraInfoHeaders[lower];
|
|
4210
|
+
if (raw !== void 0) {
|
|
4211
|
+
return splitHeaderValues(String(raw));
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
const values = this.headerValuesMap.get(lower);
|
|
4215
|
+
return values ? [...values] : [];
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
/**
|
|
4219
|
+
* Returns header entries preserving their original wire casing and ordering.
|
|
4220
|
+
* Falls back to the CDP object when the raw header text is unavailable.
|
|
4221
|
+
*/
|
|
4222
|
+
headersArray() {
|
|
4223
|
+
return __async(this, null, function* () {
|
|
4224
|
+
var _a, _b;
|
|
4225
|
+
if (this.headersArrayCache) return [...this.headersArrayCache];
|
|
4226
|
+
const entriesFromText = parseHeadersText(this.extraInfoHeadersText);
|
|
4227
|
+
if (entriesFromText.length > 0) {
|
|
4228
|
+
this.headersArrayCache = entriesFromText;
|
|
4229
|
+
return [...entriesFromText];
|
|
4230
|
+
}
|
|
4231
|
+
const entries = [];
|
|
4232
|
+
const source = (_b = (_a = this.extraInfoHeaders) != null ? _a : this.response.headers) != null ? _b : {};
|
|
4233
|
+
for (const [name, value] of Object.entries(source)) {
|
|
4234
|
+
const values = splitHeaderValues(String(value));
|
|
4235
|
+
for (const val of values) {
|
|
4236
|
+
entries.push({ name, value: val });
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
this.headersArrayCache = entries;
|
|
4240
|
+
return [...entries];
|
|
4241
|
+
});
|
|
4242
|
+
}
|
|
4243
|
+
/**
|
|
4244
|
+
* Requests the raw response body from Chrome DevTools Protocol. The method is
|
|
4245
|
+
* intentionally lazy because not every caller needs the payload, and CDP only
|
|
4246
|
+
* allows retrieving it once the response completes.
|
|
4247
|
+
*/
|
|
4248
|
+
body() {
|
|
4249
|
+
return __async(this, null, function* () {
|
|
4250
|
+
const result = yield this.session.send(
|
|
4251
|
+
"Network.getResponseBody",
|
|
4252
|
+
{ requestId: this.requestId }
|
|
4253
|
+
).catch((error) => {
|
|
4254
|
+
throw new Error(`Failed to retrieve response body: ${String(error)}`);
|
|
4255
|
+
});
|
|
4256
|
+
if (result.base64Encoded) {
|
|
4257
|
+
return Buffer.from(result.body, "base64");
|
|
4258
|
+
}
|
|
4259
|
+
return Buffer.from(result.body, "utf-8");
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
/** Decodes the response body as UTF-8 text. */
|
|
4263
|
+
text() {
|
|
4264
|
+
return __async(this, null, function* () {
|
|
4265
|
+
const buffer = yield this.body();
|
|
4266
|
+
return buffer.toString("utf-8");
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
/** Parses the response body as JSON and throws if parsing fails. */
|
|
4270
|
+
json() {
|
|
4271
|
+
return __async(this, null, function* () {
|
|
4272
|
+
const text = yield this.text();
|
|
4273
|
+
try {
|
|
4274
|
+
return JSON.parse(text);
|
|
4275
|
+
} catch (error) {
|
|
4276
|
+
throw new Error(`Failed to parse JSON response: ${String(error)}`);
|
|
4277
|
+
}
|
|
4278
|
+
});
|
|
4279
|
+
}
|
|
4280
|
+
/**
|
|
4281
|
+
* Resolves once the underlying network request completes or fails. Mirrors
|
|
4282
|
+
* Playwright's behaviour by resolving to `null` on success and to an `Error`
|
|
4283
|
+
* instance when Chrome reports `Network.loadingFailed`.
|
|
4284
|
+
*/
|
|
4285
|
+
finished() {
|
|
4286
|
+
return __async(this, null, function* () {
|
|
4287
|
+
return this.finishedDeferred.promise;
|
|
4288
|
+
});
|
|
4289
|
+
}
|
|
4290
|
+
/**
|
|
4291
|
+
* Internal helper invoked by the navigation tracker when CDP reports extra
|
|
4292
|
+
* header information. This keeps the cached header views in sync with the
|
|
4293
|
+
* richer metadata.
|
|
4294
|
+
*/
|
|
4295
|
+
applyExtraInfo(event) {
|
|
4296
|
+
var _a;
|
|
4297
|
+
this.extraInfoHeaders = event.headers;
|
|
4298
|
+
this.extraInfoHeadersText = event.headersText;
|
|
4299
|
+
this.allHeadersCache = null;
|
|
4300
|
+
this.headersArrayCache = null;
|
|
4301
|
+
this.headersObject = {};
|
|
4302
|
+
this.headerValuesMap.clear();
|
|
4303
|
+
const source = (_a = event.headers) != null ? _a : {};
|
|
4304
|
+
for (const [name, value] of Object.entries(source)) {
|
|
4305
|
+
const lower = normaliseHeaderName(name);
|
|
4306
|
+
const segments = splitHeaderValues(String(value));
|
|
4307
|
+
this.headerValuesMap.set(lower, segments);
|
|
4308
|
+
this.headersObject[lower] = segments.join(", ");
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
/** Marks the response as finished and resolves the `finished()` promise. */
|
|
4312
|
+
markFinished(error) {
|
|
4313
|
+
if (this.finishedSettled) return;
|
|
4314
|
+
this.finishedSettled = true;
|
|
4315
|
+
if (error) {
|
|
4316
|
+
this.finishedDeferred.resolve(error);
|
|
4317
|
+
} else {
|
|
4318
|
+
this.finishedDeferred.resolve(null);
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
};
|
|
4322
|
+
}
|
|
4323
|
+
});
|
|
4324
|
+
|
|
3968
4325
|
// lib/v3/understudy/frame.ts
|
|
3969
4326
|
var Frame;
|
|
3970
4327
|
var init_frame = __esm({
|
|
@@ -4084,12 +4441,32 @@ var init_frame = __esm({
|
|
|
4084
4441
|
/** Page.captureScreenshot (frame-scoped session) */
|
|
4085
4442
|
screenshot(options) {
|
|
4086
4443
|
return __async(this, null, function* () {
|
|
4444
|
+
var _a;
|
|
4087
4445
|
yield this.session.send("Page.enable");
|
|
4446
|
+
const format = (_a = options == null ? void 0 : options.type) != null ? _a : "png";
|
|
4088
4447
|
const params = {
|
|
4089
|
-
format
|
|
4448
|
+
format,
|
|
4449
|
+
fromSurface: true,
|
|
4090
4450
|
captureBeyondViewport: options == null ? void 0 : options.fullPage
|
|
4091
4451
|
};
|
|
4092
|
-
|
|
4452
|
+
const clampScale = (value) => Math.min(2, Math.max(0.1, value));
|
|
4453
|
+
const normalizedScale = typeof (options == null ? void 0 : options.scale) === "number" ? clampScale(options.scale) : void 0;
|
|
4454
|
+
if (options == null ? void 0 : options.clip) {
|
|
4455
|
+
const clip = {
|
|
4456
|
+
x: options.clip.x,
|
|
4457
|
+
y: options.clip.y,
|
|
4458
|
+
width: options.clip.width,
|
|
4459
|
+
height: options.clip.height,
|
|
4460
|
+
scale: normalizedScale != null ? normalizedScale : 1
|
|
4461
|
+
};
|
|
4462
|
+
params.clip = clip;
|
|
4463
|
+
} else if (normalizedScale !== void 0 && normalizedScale !== 1) {
|
|
4464
|
+
params.scale = normalizedScale;
|
|
4465
|
+
}
|
|
4466
|
+
if (format === "jpeg" && typeof (options == null ? void 0 : options.quality) === "number") {
|
|
4467
|
+
const q = Math.round(options.quality);
|
|
4468
|
+
params.quality = Math.min(100, Math.max(0, q));
|
|
4469
|
+
}
|
|
4093
4470
|
const { data } = yield this.session.send(
|
|
4094
4471
|
"Page.captureScreenshot",
|
|
4095
4472
|
params
|
|
@@ -4925,14 +5302,529 @@ var init_lifecycleWatcher = __esm({
|
|
|
4925
5302
|
}
|
|
4926
5303
|
});
|
|
4927
5304
|
|
|
5305
|
+
// lib/v3/understudy/navigationResponseTracker.ts
|
|
5306
|
+
var NavigationResponseTracker;
|
|
5307
|
+
var init_navigationResponseTracker = __esm({
|
|
5308
|
+
"lib/v3/understudy/navigationResponseTracker.ts"() {
|
|
5309
|
+
init_response();
|
|
5310
|
+
NavigationResponseTracker = class {
|
|
5311
|
+
/**
|
|
5312
|
+
* Create a tracker bound to a specific navigation command. The tracker begins
|
|
5313
|
+
* listening for network events immediately so it should be constructed before
|
|
5314
|
+
* the navigation request is dispatched.
|
|
5315
|
+
*/
|
|
5316
|
+
constructor(params) {
|
|
5317
|
+
this.selectedRequestId = null;
|
|
5318
|
+
this.selectedResponse = null;
|
|
5319
|
+
this.acceptNextWithoutLoader = false;
|
|
5320
|
+
this.responseResolved = false;
|
|
5321
|
+
this.pendingResponsesByLoader = /* @__PURE__ */ new Map();
|
|
5322
|
+
this.pendingExtraInfo = /* @__PURE__ */ new Map();
|
|
5323
|
+
this.listeners = [];
|
|
5324
|
+
this.page = params.page;
|
|
5325
|
+
this.session = params.session;
|
|
5326
|
+
this.navigationCommandId = params.navigationCommandId;
|
|
5327
|
+
this.responsePromise = new Promise((resolve3) => {
|
|
5328
|
+
this.resolveResponse = (value) => {
|
|
5329
|
+
if (this.responseResolved) return;
|
|
5330
|
+
this.responseResolved = true;
|
|
5331
|
+
resolve3(value);
|
|
5332
|
+
};
|
|
5333
|
+
});
|
|
5334
|
+
this.installListeners();
|
|
5335
|
+
}
|
|
5336
|
+
/** Stop listening for CDP events and release any pending bookkeeping. */
|
|
5337
|
+
dispose() {
|
|
5338
|
+
for (const { event, handler } of this.listeners) {
|
|
5339
|
+
this.session.off(event, handler);
|
|
5340
|
+
}
|
|
5341
|
+
this.listeners.length = 0;
|
|
5342
|
+
this.pendingResponsesByLoader.clear();
|
|
5343
|
+
this.pendingExtraInfo.clear();
|
|
5344
|
+
}
|
|
5345
|
+
/**
|
|
5346
|
+
* Hint the tracker with the loader id returned by `Page.navigate`. Chrome only
|
|
5347
|
+
* emits this once the browser begins navigating, so we store early responses
|
|
5348
|
+
* and match them once the loader id is known.
|
|
5349
|
+
*/
|
|
5350
|
+
setExpectedLoaderId(loaderId) {
|
|
5351
|
+
if (!loaderId) return;
|
|
5352
|
+
this.expectedLoaderId = loaderId;
|
|
5353
|
+
const pending = this.pendingResponsesByLoader.get(loaderId);
|
|
5354
|
+
if (pending) {
|
|
5355
|
+
this.pendingResponsesByLoader.delete(loaderId);
|
|
5356
|
+
this.selectResponse(pending);
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
/**
|
|
5360
|
+
* Some navigation APIs (reload/history traversal) do not provide a loader id
|
|
5361
|
+
* up front. This flag instructs the tracker to accept the next qualifying
|
|
5362
|
+
* document response even if no loader id has been announced yet.
|
|
5363
|
+
*/
|
|
5364
|
+
expectNavigationWithoutKnownLoader() {
|
|
5365
|
+
this.acceptNextWithoutLoader = true;
|
|
5366
|
+
}
|
|
5367
|
+
/**
|
|
5368
|
+
* Returns a promise that resolves with the matched response (or `null` when
|
|
5369
|
+
* no document response was observed).
|
|
5370
|
+
*/
|
|
5371
|
+
navigationCompleted() {
|
|
5372
|
+
return __async(this, null, function* () {
|
|
5373
|
+
if (!this.responseResolved) {
|
|
5374
|
+
queueMicrotask(() => {
|
|
5375
|
+
if (!this.responseResolved) this.resolveResponse(null);
|
|
5376
|
+
});
|
|
5377
|
+
}
|
|
5378
|
+
return this.responsePromise;
|
|
5379
|
+
});
|
|
5380
|
+
}
|
|
5381
|
+
/** Expose the raw response promise (mainly for tests). */
|
|
5382
|
+
response() {
|
|
5383
|
+
return __async(this, null, function* () {
|
|
5384
|
+
return this.responsePromise;
|
|
5385
|
+
});
|
|
5386
|
+
}
|
|
5387
|
+
/** Register all CDP listeners relevant to navigation tracking. */
|
|
5388
|
+
installListeners() {
|
|
5389
|
+
this.addListener("Network.responseReceived", (event) => {
|
|
5390
|
+
this.onResponseReceived(event);
|
|
5391
|
+
});
|
|
5392
|
+
this.addListener("Network.responseReceivedExtraInfo", (event) => {
|
|
5393
|
+
this.onResponseReceivedExtraInfo(
|
|
5394
|
+
event
|
|
5395
|
+
);
|
|
5396
|
+
});
|
|
5397
|
+
this.addListener("Network.loadingFinished", (event) => {
|
|
5398
|
+
this.onLoadingFinished(event);
|
|
5399
|
+
});
|
|
5400
|
+
this.addListener("Network.loadingFailed", (event) => {
|
|
5401
|
+
this.onLoadingFailed(event);
|
|
5402
|
+
});
|
|
5403
|
+
}
|
|
5404
|
+
/** Attach a CDP listener and track it for later disposal. */
|
|
5405
|
+
addListener(event, handler) {
|
|
5406
|
+
this.session.on(event, handler);
|
|
5407
|
+
this.listeners.push({ event, handler });
|
|
5408
|
+
}
|
|
5409
|
+
/** Handle the initial response payload for document navigations. */
|
|
5410
|
+
onResponseReceived(event) {
|
|
5411
|
+
var _a;
|
|
5412
|
+
if (!this.page.isCurrentNavigationCommand(this.navigationCommandId)) return;
|
|
5413
|
+
if (!event || !event.response) return;
|
|
5414
|
+
if (event.type !== "Document") return;
|
|
5415
|
+
if (event.frameId !== this.page.mainFrameId()) return;
|
|
5416
|
+
const loaderId = (_a = event.loaderId) != null ? _a : "";
|
|
5417
|
+
if (this.acceptNextWithoutLoader) {
|
|
5418
|
+
this.acceptNextWithoutLoader = false;
|
|
5419
|
+
this.selectResponse(event);
|
|
5420
|
+
return;
|
|
5421
|
+
}
|
|
5422
|
+
if (this.expectedLoaderId) {
|
|
5423
|
+
if (loaderId && loaderId !== this.expectedLoaderId) {
|
|
5424
|
+
this.pendingResponsesByLoader.set(loaderId, event);
|
|
5425
|
+
return;
|
|
5426
|
+
}
|
|
5427
|
+
this.selectResponse(event);
|
|
5428
|
+
return;
|
|
5429
|
+
}
|
|
5430
|
+
if (loaderId) {
|
|
5431
|
+
this.pendingResponsesByLoader.set(loaderId, event);
|
|
5432
|
+
return;
|
|
5433
|
+
}
|
|
5434
|
+
this.selectResponse(event);
|
|
5435
|
+
}
|
|
5436
|
+
/** Merge auxiliary header information once Chrome exposes it. */
|
|
5437
|
+
onResponseReceivedExtraInfo(event) {
|
|
5438
|
+
var _a;
|
|
5439
|
+
if (!event || !event.requestId) return;
|
|
5440
|
+
if (this.selectedRequestId && event.requestId === this.selectedRequestId) {
|
|
5441
|
+
(_a = this.selectedResponse) == null ? void 0 : _a.applyExtraInfo(event);
|
|
5442
|
+
return;
|
|
5443
|
+
}
|
|
5444
|
+
this.pendingExtraInfo.set(event.requestId, event);
|
|
5445
|
+
}
|
|
5446
|
+
/** Resolve the response's finished promise when the request completes. */
|
|
5447
|
+
onLoadingFinished(event) {
|
|
5448
|
+
var _a;
|
|
5449
|
+
if (!event || !event.requestId) return;
|
|
5450
|
+
if (event.requestId !== this.selectedRequestId) return;
|
|
5451
|
+
(_a = this.selectedResponse) == null ? void 0 : _a.markFinished(null);
|
|
5452
|
+
}
|
|
5453
|
+
/** Resolve the response's finished promise with an error on failure. */
|
|
5454
|
+
onLoadingFailed(event) {
|
|
5455
|
+
var _a;
|
|
5456
|
+
if (!event || !event.requestId) return;
|
|
5457
|
+
if (event.requestId !== this.selectedRequestId) return;
|
|
5458
|
+
const errorText = event.errorText || "Navigation request failed";
|
|
5459
|
+
(_a = this.selectedResponse) == null ? void 0 : _a.markFinished(new Error(errorText));
|
|
5460
|
+
}
|
|
5461
|
+
/**
|
|
5462
|
+
* Create the `Response` wrapper for the chosen document response and
|
|
5463
|
+
* resolve awaiting consumers. Subsequent events flesh out the header/body
|
|
5464
|
+
* helpers and mark the request as finished.
|
|
5465
|
+
*/
|
|
5466
|
+
selectResponse(event) {
|
|
5467
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5468
|
+
if (event.loaderId) {
|
|
5469
|
+
this.pendingResponsesByLoader.delete(event.loaderId);
|
|
5470
|
+
}
|
|
5471
|
+
if (this.responseResolved) return;
|
|
5472
|
+
if (this.selectedResponse) return;
|
|
5473
|
+
const protocol = (_c = (_b = (_a = event.response) == null ? void 0 : _a.protocol) == null ? void 0 : _b.toLowerCase()) != null ? _c : "";
|
|
5474
|
+
const url = (_e = (_d = event.response) == null ? void 0 : _d.url) != null ? _e : "";
|
|
5475
|
+
const isDataUrl = protocol === "data" || url.startsWith("data:");
|
|
5476
|
+
const isAboutUrl = protocol === "about" || url.startsWith("about:");
|
|
5477
|
+
if (isDataUrl || isAboutUrl) {
|
|
5478
|
+
this.pendingExtraInfo.delete(event.requestId);
|
|
5479
|
+
this.selectedRequestId = null;
|
|
5480
|
+
this.selectedResponse = null;
|
|
5481
|
+
this.resolveResponse(null);
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5484
|
+
const response = new Response({
|
|
5485
|
+
page: this.page,
|
|
5486
|
+
session: this.session,
|
|
5487
|
+
requestId: event.requestId,
|
|
5488
|
+
frameId: event.frameId,
|
|
5489
|
+
loaderId: event.loaderId,
|
|
5490
|
+
response: event.response,
|
|
5491
|
+
fromServiceWorker: Boolean((_f = event.response) == null ? void 0 : _f.fromServiceWorker)
|
|
5492
|
+
});
|
|
5493
|
+
this.selectedRequestId = event.requestId;
|
|
5494
|
+
this.selectedResponse = response;
|
|
5495
|
+
const extraInfo = this.pendingExtraInfo.get(event.requestId);
|
|
5496
|
+
if (extraInfo) {
|
|
5497
|
+
response.applyExtraInfo(extraInfo);
|
|
5498
|
+
this.pendingExtraInfo.delete(event.requestId);
|
|
5499
|
+
}
|
|
5500
|
+
this.resolveResponse(response);
|
|
5501
|
+
}
|
|
5502
|
+
};
|
|
5503
|
+
}
|
|
5504
|
+
});
|
|
5505
|
+
|
|
5506
|
+
// lib/v3/understudy/screenshotUtils.ts
|
|
5507
|
+
function collectFramesForScreenshot(page) {
|
|
5508
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5509
|
+
const main = page.mainFrame();
|
|
5510
|
+
seen.set(main.frameId, main);
|
|
5511
|
+
for (const frame of page.frames()) {
|
|
5512
|
+
seen.set(frame.frameId, frame);
|
|
5513
|
+
}
|
|
5514
|
+
return Array.from(seen.values());
|
|
5515
|
+
}
|
|
5516
|
+
function normalizeScreenshotClip(clip) {
|
|
5517
|
+
const x = Number(clip.x);
|
|
5518
|
+
const y = Number(clip.y);
|
|
5519
|
+
const width = Number(clip.width);
|
|
5520
|
+
const height = Number(clip.height);
|
|
5521
|
+
for (const [key, value] of Object.entries({ x, y, width, height })) {
|
|
5522
|
+
if (!Number.isFinite(value)) {
|
|
5523
|
+
throw new Error(`screenshot: clip.${key} must be a finite number`);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
if (width <= 0 || height <= 0) {
|
|
5527
|
+
throw new Error("screenshot: clip width/height must be positive");
|
|
5528
|
+
}
|
|
5529
|
+
return { x, y, width, height };
|
|
5530
|
+
}
|
|
5531
|
+
function computeScreenshotScale(page, mode) {
|
|
5532
|
+
return __async(this, null, function* () {
|
|
5533
|
+
if (mode !== "css") return void 0;
|
|
5534
|
+
try {
|
|
5535
|
+
const frame = page.mainFrame();
|
|
5536
|
+
const dpr = yield frame.evaluate(() => {
|
|
5537
|
+
const ratio = Number(window.devicePixelRatio || 1);
|
|
5538
|
+
return Number.isFinite(ratio) && ratio > 0 ? ratio : 1;
|
|
5539
|
+
}).catch(() => 1);
|
|
5540
|
+
const safeRatio = Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
5541
|
+
return Math.min(2, Math.max(0.1, 1 / safeRatio));
|
|
5542
|
+
} catch (e) {
|
|
5543
|
+
return 1;
|
|
5544
|
+
}
|
|
5545
|
+
});
|
|
5546
|
+
}
|
|
5547
|
+
function setTransparentBackground(session) {
|
|
5548
|
+
return __async(this, null, function* () {
|
|
5549
|
+
yield session.send("Emulation.setDefaultBackgroundColorOverride", {
|
|
5550
|
+
color: { r: 0, g: 0, b: 0, a: 0 }
|
|
5551
|
+
}).catch(() => {
|
|
5552
|
+
});
|
|
5553
|
+
return () => __async(null, null, function* () {
|
|
5554
|
+
yield session.send("Emulation.setDefaultBackgroundColorOverride", {}).catch(() => {
|
|
5555
|
+
});
|
|
5556
|
+
});
|
|
5557
|
+
});
|
|
5558
|
+
}
|
|
5559
|
+
function applyStyleToFrames(frames, css, label) {
|
|
5560
|
+
return __async(this, null, function* () {
|
|
5561
|
+
const trimmed = css.trim();
|
|
5562
|
+
if (!trimmed) return () => __async(null, null, function* () {
|
|
5563
|
+
});
|
|
5564
|
+
const token = `__v3_style_${label}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
5565
|
+
yield Promise.all(
|
|
5566
|
+
frames.map(
|
|
5567
|
+
(frame) => frame.evaluate(
|
|
5568
|
+
({ css: css2, token: token2 }) => {
|
|
5569
|
+
try {
|
|
5570
|
+
const doc = document;
|
|
5571
|
+
if (!doc) return;
|
|
5572
|
+
const style = doc.createElement("style");
|
|
5573
|
+
style.setAttribute("data-stagehand-style", token2);
|
|
5574
|
+
style.textContent = css2;
|
|
5575
|
+
const parent = doc.head || doc.documentElement || doc.body;
|
|
5576
|
+
parent == null ? void 0 : parent.appendChild(style);
|
|
5577
|
+
} catch (e) {
|
|
5578
|
+
}
|
|
5579
|
+
},
|
|
5580
|
+
{ css: trimmed, token }
|
|
5581
|
+
).catch(() => {
|
|
5582
|
+
})
|
|
5583
|
+
)
|
|
5584
|
+
);
|
|
5585
|
+
return () => __async(null, null, function* () {
|
|
5586
|
+
yield Promise.all(
|
|
5587
|
+
frames.map(
|
|
5588
|
+
(frame) => frame.evaluate((token2) => {
|
|
5589
|
+
try {
|
|
5590
|
+
const doc = document;
|
|
5591
|
+
if (!doc) return;
|
|
5592
|
+
const nodes = doc.querySelectorAll(
|
|
5593
|
+
`[data-stagehand-style="${token2}"]`
|
|
5594
|
+
);
|
|
5595
|
+
nodes.forEach((node) => node.remove());
|
|
5596
|
+
} catch (e) {
|
|
5597
|
+
}
|
|
5598
|
+
}, token).catch(() => {
|
|
5599
|
+
})
|
|
5600
|
+
)
|
|
5601
|
+
);
|
|
5602
|
+
});
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
function disableAnimations(frames) {
|
|
5606
|
+
return __async(this, null, function* () {
|
|
5607
|
+
const css = `
|
|
5608
|
+
*,
|
|
5609
|
+
*::before,
|
|
5610
|
+
*::after {
|
|
5611
|
+
animation-delay: 0s !important;
|
|
5612
|
+
animation-duration: 0s !important;
|
|
5613
|
+
animation-iteration-count: 1 !important;
|
|
5614
|
+
animation-play-state: paused !important;
|
|
5615
|
+
transition-property: none !important;
|
|
5616
|
+
transition-duration: 0s !important;
|
|
5617
|
+
transition-delay: 0s !important;
|
|
5618
|
+
}`;
|
|
5619
|
+
const cleanup = yield applyStyleToFrames(frames, css, "animations");
|
|
5620
|
+
yield Promise.all(
|
|
5621
|
+
frames.map(
|
|
5622
|
+
(frame) => frame.evaluate(() => {
|
|
5623
|
+
var _a, _b, _c, _d, _e;
|
|
5624
|
+
try {
|
|
5625
|
+
const animations = typeof document.getAnimations === "function" ? document.getAnimations() : [];
|
|
5626
|
+
for (const animation of animations) {
|
|
5627
|
+
try {
|
|
5628
|
+
const details = (_b = (_a = animation.effect) == null ? void 0 : _a.getComputedTiming) == null ? void 0 : _b.call(_a);
|
|
5629
|
+
if (details && details.iterations !== Infinity) {
|
|
5630
|
+
(_c = animation.finish) == null ? void 0 : _c.call(animation);
|
|
5631
|
+
} else {
|
|
5632
|
+
(_d = animation.cancel) == null ? void 0 : _d.call(animation);
|
|
5633
|
+
}
|
|
5634
|
+
} catch (e) {
|
|
5635
|
+
(_e = animation.cancel) == null ? void 0 : _e.call(animation);
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
} catch (e) {
|
|
5639
|
+
}
|
|
5640
|
+
}).catch(() => {
|
|
5641
|
+
})
|
|
5642
|
+
)
|
|
5643
|
+
);
|
|
5644
|
+
return cleanup;
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
function hideCaret(frames) {
|
|
5648
|
+
return __async(this, null, function* () {
|
|
5649
|
+
const css = `
|
|
5650
|
+
input,
|
|
5651
|
+
textarea,
|
|
5652
|
+
[contenteditable],
|
|
5653
|
+
[contenteditable=""],
|
|
5654
|
+
[contenteditable="true"],
|
|
5655
|
+
[contenteditable="plaintext-only"],
|
|
5656
|
+
*:focus {
|
|
5657
|
+
caret-color: transparent !important;
|
|
5658
|
+
}`;
|
|
5659
|
+
return applyStyleToFrames(frames, css, "caret");
|
|
5660
|
+
});
|
|
5661
|
+
}
|
|
5662
|
+
function applyMaskOverlays(locators, color) {
|
|
5663
|
+
return __async(this, null, function* () {
|
|
5664
|
+
var _a;
|
|
5665
|
+
const rectsByFrame = /* @__PURE__ */ new Map();
|
|
5666
|
+
for (const locator of locators) {
|
|
5667
|
+
try {
|
|
5668
|
+
const info = yield resolveMaskRect(locator);
|
|
5669
|
+
if (!info) continue;
|
|
5670
|
+
const list = (_a = rectsByFrame.get(info.frame)) != null ? _a : [];
|
|
5671
|
+
list.push(info.rect);
|
|
5672
|
+
rectsByFrame.set(info.frame, list);
|
|
5673
|
+
} catch (e) {
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
if (rectsByFrame.size === 0) {
|
|
5677
|
+
return () => __async(null, null, function* () {
|
|
5678
|
+
});
|
|
5679
|
+
}
|
|
5680
|
+
const token = `__v3_mask_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
5681
|
+
yield Promise.all(
|
|
5682
|
+
Array.from(rectsByFrame.entries()).map(
|
|
5683
|
+
([frame, rects]) => frame.evaluate(
|
|
5684
|
+
({ rects: rects2, color: color2, token: token2 }) => {
|
|
5685
|
+
try {
|
|
5686
|
+
const doc = document;
|
|
5687
|
+
if (!doc) return;
|
|
5688
|
+
const root = doc.documentElement || doc.body;
|
|
5689
|
+
if (!root) return;
|
|
5690
|
+
for (const rect of rects2) {
|
|
5691
|
+
const el = doc.createElement("div");
|
|
5692
|
+
el.setAttribute("data-stagehand-mask", token2);
|
|
5693
|
+
el.style.position = "absolute";
|
|
5694
|
+
el.style.left = `${rect.x}px`;
|
|
5695
|
+
el.style.top = `${rect.y}px`;
|
|
5696
|
+
el.style.width = `${rect.width}px`;
|
|
5697
|
+
el.style.height = `${rect.height}px`;
|
|
5698
|
+
el.style.backgroundColor = color2;
|
|
5699
|
+
el.style.pointerEvents = "none";
|
|
5700
|
+
el.style.zIndex = "2147483647";
|
|
5701
|
+
el.style.opacity = "1";
|
|
5702
|
+
el.style.mixBlendMode = "normal";
|
|
5703
|
+
root.appendChild(el);
|
|
5704
|
+
}
|
|
5705
|
+
} catch (e) {
|
|
5706
|
+
}
|
|
5707
|
+
},
|
|
5708
|
+
{ rects, color, token }
|
|
5709
|
+
).catch(() => {
|
|
5710
|
+
})
|
|
5711
|
+
)
|
|
5712
|
+
);
|
|
5713
|
+
return () => __async(null, null, function* () {
|
|
5714
|
+
yield Promise.all(
|
|
5715
|
+
Array.from(rectsByFrame.keys()).map(
|
|
5716
|
+
(frame) => frame.evaluate((token2) => {
|
|
5717
|
+
try {
|
|
5718
|
+
const doc = document;
|
|
5719
|
+
if (!doc) return;
|
|
5720
|
+
const nodes = doc.querySelectorAll(
|
|
5721
|
+
`[data-stagehand-mask="${token2}"]`
|
|
5722
|
+
);
|
|
5723
|
+
nodes.forEach((node) => node.remove());
|
|
5724
|
+
} catch (e) {
|
|
5725
|
+
}
|
|
5726
|
+
}, token).catch(() => {
|
|
5727
|
+
})
|
|
5728
|
+
)
|
|
5729
|
+
);
|
|
5730
|
+
});
|
|
5731
|
+
});
|
|
5732
|
+
}
|
|
5733
|
+
function resolveMaskRect(locator) {
|
|
5734
|
+
return __async(this, null, function* () {
|
|
5735
|
+
const frame = locator.getFrame();
|
|
5736
|
+
const session = frame.session;
|
|
5737
|
+
let objectId = null;
|
|
5738
|
+
try {
|
|
5739
|
+
const resolved = yield locator.resolveNode();
|
|
5740
|
+
objectId = resolved.objectId;
|
|
5741
|
+
const result = yield session.send(
|
|
5742
|
+
"Runtime.callFunctionOn",
|
|
5743
|
+
{
|
|
5744
|
+
objectId,
|
|
5745
|
+
functionDeclaration: `function() {
|
|
5746
|
+
if (!this || typeof this.getBoundingClientRect !== 'function') return null;
|
|
5747
|
+
const rect = this.getBoundingClientRect();
|
|
5748
|
+
if (!rect) return null;
|
|
5749
|
+
const style = window.getComputedStyle(this);
|
|
5750
|
+
if (!style) return null;
|
|
5751
|
+
if (style.visibility === 'hidden' || style.display === 'none') return null;
|
|
5752
|
+
if (rect.width <= 0 || rect.height <= 0) return null;
|
|
5753
|
+
return {
|
|
5754
|
+
x: rect.left + window.scrollX,
|
|
5755
|
+
y: rect.top + window.scrollY,
|
|
5756
|
+
width: rect.width,
|
|
5757
|
+
height: rect.height,
|
|
5758
|
+
};
|
|
5759
|
+
}`,
|
|
5760
|
+
returnByValue: true
|
|
5761
|
+
}
|
|
5762
|
+
);
|
|
5763
|
+
if (result.exceptionDetails) {
|
|
5764
|
+
return null;
|
|
5765
|
+
}
|
|
5766
|
+
const rect = result.result.value;
|
|
5767
|
+
if (!rect) return null;
|
|
5768
|
+
const { x, y, width, height } = rect;
|
|
5769
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
5770
|
+
return null;
|
|
5771
|
+
}
|
|
5772
|
+
return { frame, rect: { x, y, width, height } };
|
|
5773
|
+
} catch (e) {
|
|
5774
|
+
return null;
|
|
5775
|
+
} finally {
|
|
5776
|
+
if (objectId) {
|
|
5777
|
+
yield session.send("Runtime.releaseObject", { objectId }).catch(() => {
|
|
5778
|
+
});
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
});
|
|
5782
|
+
}
|
|
5783
|
+
function runScreenshotCleanups(cleanups) {
|
|
5784
|
+
return __async(this, null, function* () {
|
|
5785
|
+
for (let i = cleanups.length - 1; i >= 0; i -= 1) {
|
|
5786
|
+
const fn = cleanups[i];
|
|
5787
|
+
if (!fn) continue;
|
|
5788
|
+
try {
|
|
5789
|
+
const result = fn();
|
|
5790
|
+
if (result && typeof result.then === "function") {
|
|
5791
|
+
yield result;
|
|
5792
|
+
}
|
|
5793
|
+
} catch (e) {
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
});
|
|
5797
|
+
}
|
|
5798
|
+
function withScreenshotTimeout(timeoutMs, task) {
|
|
5799
|
+
return __async(this, null, function* () {
|
|
5800
|
+
if (!timeoutMs || timeoutMs <= 0) return task();
|
|
5801
|
+
let timer = null;
|
|
5802
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
5803
|
+
timer = setTimeout(() => {
|
|
5804
|
+
reject(new Error(`screenshot: timeout of ${timeoutMs}ms exceeded`));
|
|
5805
|
+
}, timeoutMs);
|
|
5806
|
+
});
|
|
5807
|
+
try {
|
|
5808
|
+
return yield Promise.race([task(), timeoutPromise]);
|
|
5809
|
+
} finally {
|
|
5810
|
+
if (timer) clearTimeout(timer);
|
|
5811
|
+
}
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5814
|
+
var init_screenshotUtils = __esm({
|
|
5815
|
+
"lib/v3/understudy/screenshotUtils.ts"() {
|
|
5816
|
+
}
|
|
5817
|
+
});
|
|
5818
|
+
|
|
4928
5819
|
// lib/v3/understudy/page.ts
|
|
4929
5820
|
var page_exports = {};
|
|
4930
5821
|
__export(page_exports, {
|
|
4931
5822
|
Page: () => Page
|
|
4932
5823
|
});
|
|
4933
|
-
var LIFECYCLE_NAME, Page;
|
|
5824
|
+
var import_fs5, LIFECYCLE_NAME, Page;
|
|
4934
5825
|
var init_page = __esm({
|
|
4935
5826
|
"lib/v3/understudy/page.ts"() {
|
|
5827
|
+
import_fs5 = require("fs");
|
|
4936
5828
|
init_logger();
|
|
4937
5829
|
init_frame();
|
|
4938
5830
|
init_frameLocator();
|
|
@@ -4941,6 +5833,9 @@ var init_page = __esm({
|
|
|
4941
5833
|
init_frameRegistry();
|
|
4942
5834
|
init_networkManager();
|
|
4943
5835
|
init_lifecycleWatcher();
|
|
5836
|
+
init_navigationResponseTracker();
|
|
5837
|
+
init_consoleMessage();
|
|
5838
|
+
init_screenshotUtils();
|
|
4944
5839
|
LIFECYCLE_NAME = {
|
|
4945
5840
|
load: "load",
|
|
4946
5841
|
domcontentloaded: "DOMContentLoaded",
|
|
@@ -4964,6 +5859,8 @@ var init_page = __esm({
|
|
|
4964
5859
|
this.latestNavigationCommandId = 0;
|
|
4965
5860
|
/** Optional API client for routing page operations to the API */
|
|
4966
5861
|
this.apiClient = null;
|
|
5862
|
+
this.consoleListeners = /* @__PURE__ */ new Set();
|
|
5863
|
+
this.consoleHandlers = /* @__PURE__ */ new Map();
|
|
4967
5864
|
// --- Optional visual cursor overlay management ---
|
|
4968
5865
|
this.cursorEnabled = false;
|
|
4969
5866
|
// Track pressed modifier keys
|
|
@@ -5165,6 +6062,9 @@ var init_page = __esm({
|
|
|
5165
6062
|
var _a;
|
|
5166
6063
|
if (childSession.id) this.sessions.set(childSession.id, childSession);
|
|
5167
6064
|
this.networkManager.trackSession(childSession);
|
|
6065
|
+
if (this.consoleListeners.size > 0) {
|
|
6066
|
+
this.installConsoleTap(childSession);
|
|
6067
|
+
}
|
|
5168
6068
|
this.registry.adoptChildSession(
|
|
5169
6069
|
(_a = childSession.id) != null ? _a : "child",
|
|
5170
6070
|
childMainFrameId
|
|
@@ -5218,6 +6118,7 @@ var init_page = __esm({
|
|
|
5218
6118
|
this.registry.onFrameDetached(fid, "remove");
|
|
5219
6119
|
this.frameCache.delete(fid);
|
|
5220
6120
|
}
|
|
6121
|
+
this.teardownConsoleTap(sessionId);
|
|
5221
6122
|
this.sessions.delete(sessionId);
|
|
5222
6123
|
this.networkManager.untrackSession(sessionId);
|
|
5223
6124
|
}
|
|
@@ -5248,10 +6149,65 @@ var init_page = __esm({
|
|
|
5248
6149
|
unregisterSessionForNetwork(sessionId) {
|
|
5249
6150
|
this.networkManager.untrackSession(sessionId);
|
|
5250
6151
|
}
|
|
6152
|
+
on(event, listener) {
|
|
6153
|
+
if (event !== "console") {
|
|
6154
|
+
throw new Error(`Unsupported event: ${event}`);
|
|
6155
|
+
}
|
|
6156
|
+
const firstListener = this.consoleListeners.size === 0;
|
|
6157
|
+
this.consoleListeners.add(listener);
|
|
6158
|
+
if (firstListener) {
|
|
6159
|
+
this.ensureConsoleTaps();
|
|
6160
|
+
}
|
|
6161
|
+
return this;
|
|
6162
|
+
}
|
|
6163
|
+
once(event, listener) {
|
|
6164
|
+
if (event !== "console") {
|
|
6165
|
+
throw new Error(`Unsupported event: ${event}`);
|
|
6166
|
+
}
|
|
6167
|
+
const wrapper = (message) => {
|
|
6168
|
+
this.off("console", wrapper);
|
|
6169
|
+
listener(message);
|
|
6170
|
+
};
|
|
6171
|
+
return this.on("console", wrapper);
|
|
6172
|
+
}
|
|
6173
|
+
off(event, listener) {
|
|
6174
|
+
if (event !== "console") {
|
|
6175
|
+
throw new Error(`Unsupported event: ${event}`);
|
|
6176
|
+
}
|
|
6177
|
+
this.consoleListeners.delete(listener);
|
|
6178
|
+
if (this.consoleListeners.size === 0) {
|
|
6179
|
+
this.removeAllConsoleTaps();
|
|
6180
|
+
}
|
|
6181
|
+
return this;
|
|
6182
|
+
}
|
|
5251
6183
|
// ---------------- MAIN APIs ----------------
|
|
5252
6184
|
targetId() {
|
|
5253
6185
|
return this._targetId;
|
|
5254
6186
|
}
|
|
6187
|
+
/**
|
|
6188
|
+
* Send a CDP command through the main session.
|
|
6189
|
+
* Allows external consumers to execute arbitrary Chrome DevTools Protocol commands.
|
|
6190
|
+
*
|
|
6191
|
+
* @param method - The CDP method name (e.g., "Page.enable", "Runtime.evaluate")
|
|
6192
|
+
* @param params - Optional parameters for the CDP command
|
|
6193
|
+
* @returns Promise resolving to the typed CDP response
|
|
6194
|
+
*
|
|
6195
|
+
* @example
|
|
6196
|
+
* // Enable the Runtime domain
|
|
6197
|
+
* await page.sendCDP("Runtime.enable");
|
|
6198
|
+
*
|
|
6199
|
+
* @example
|
|
6200
|
+
* // Evaluate JavaScript with typed response
|
|
6201
|
+
* const result = await page.sendCDP<Protocol.Runtime.EvaluateResponse>(
|
|
6202
|
+
* "Runtime.evaluate",
|
|
6203
|
+
* { expression: "1 + 1" }
|
|
6204
|
+
* );
|
|
6205
|
+
*/
|
|
6206
|
+
sendCDP(method, params) {
|
|
6207
|
+
return __async(this, null, function* () {
|
|
6208
|
+
return this.mainSession.send(method, params);
|
|
6209
|
+
});
|
|
6210
|
+
}
|
|
5255
6211
|
/** Seed the cached URL before navigation events converge. */
|
|
5256
6212
|
seedCurrentUrl(url) {
|
|
5257
6213
|
if (!url) return;
|
|
@@ -5290,6 +6246,8 @@ var init_page = __esm({
|
|
|
5290
6246
|
yield new Promise((r) => setTimeout(r, 25));
|
|
5291
6247
|
}
|
|
5292
6248
|
this.networkManager.dispose();
|
|
6249
|
+
this.removeAllConsoleTaps();
|
|
6250
|
+
this.consoleListeners.clear();
|
|
5293
6251
|
});
|
|
5294
6252
|
}
|
|
5295
6253
|
getFullFrameTree() {
|
|
@@ -5312,6 +6270,71 @@ var init_page = __esm({
|
|
|
5312
6270
|
listAllFrameIds() {
|
|
5313
6271
|
return this.registry.listAllFrames();
|
|
5314
6272
|
}
|
|
6273
|
+
ensureConsoleTaps() {
|
|
6274
|
+
if (this.consoleListeners.size === 0) return;
|
|
6275
|
+
this.installConsoleTap(this.mainSession);
|
|
6276
|
+
for (const session of this.sessions.values()) {
|
|
6277
|
+
this.installConsoleTap(session);
|
|
6278
|
+
}
|
|
6279
|
+
}
|
|
6280
|
+
installConsoleTap(session) {
|
|
6281
|
+
const key = this.sessionKey(session);
|
|
6282
|
+
if (this.consoleHandlers.has(key)) return;
|
|
6283
|
+
void session.send("Runtime.enable").catch(() => {
|
|
6284
|
+
});
|
|
6285
|
+
const handler = (evt) => {
|
|
6286
|
+
this.emitConsole(evt);
|
|
6287
|
+
};
|
|
6288
|
+
session.on(
|
|
6289
|
+
"Runtime.consoleAPICalled",
|
|
6290
|
+
handler
|
|
6291
|
+
);
|
|
6292
|
+
this.consoleHandlers.set(key, handler);
|
|
6293
|
+
}
|
|
6294
|
+
sessionKey(session) {
|
|
6295
|
+
var _a;
|
|
6296
|
+
return (_a = session.id) != null ? _a : "__root__";
|
|
6297
|
+
}
|
|
6298
|
+
resolveSessionByKey(key) {
|
|
6299
|
+
if (this.mainSession.id) {
|
|
6300
|
+
if (this.mainSession.id === key) return this.mainSession;
|
|
6301
|
+
} else if (key === "__root__") {
|
|
6302
|
+
return this.mainSession;
|
|
6303
|
+
}
|
|
6304
|
+
return this.sessions.get(key);
|
|
6305
|
+
}
|
|
6306
|
+
teardownConsoleTap(key) {
|
|
6307
|
+
const handler = this.consoleHandlers.get(key);
|
|
6308
|
+
if (!handler) return;
|
|
6309
|
+
const session = this.resolveSessionByKey(key);
|
|
6310
|
+
session == null ? void 0 : session.off("Runtime.consoleAPICalled", handler);
|
|
6311
|
+
this.consoleHandlers.delete(key);
|
|
6312
|
+
}
|
|
6313
|
+
removeAllConsoleTaps() {
|
|
6314
|
+
for (const key of [...this.consoleHandlers.keys()]) {
|
|
6315
|
+
this.teardownConsoleTap(key);
|
|
6316
|
+
}
|
|
6317
|
+
}
|
|
6318
|
+
emitConsole(evt) {
|
|
6319
|
+
if (this.consoleListeners.size === 0) return;
|
|
6320
|
+
const message = new ConsoleMessage(evt, this);
|
|
6321
|
+
const listeners = [...this.consoleListeners];
|
|
6322
|
+
for (const listener of listeners) {
|
|
6323
|
+
try {
|
|
6324
|
+
listener(message);
|
|
6325
|
+
} catch (error) {
|
|
6326
|
+
v3Logger({
|
|
6327
|
+
category: "page",
|
|
6328
|
+
message: "Console listener threw",
|
|
6329
|
+
level: 2,
|
|
6330
|
+
auxiliary: {
|
|
6331
|
+
error: { value: String(error), type: "string" },
|
|
6332
|
+
type: { value: evt.type, type: "string" }
|
|
6333
|
+
}
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
5315
6338
|
// -------- Convenience APIs delegated to the current main frame --------
|
|
5316
6339
|
/**
|
|
5317
6340
|
* Navigate the page; optionally wait for a lifecycle state.
|
|
@@ -5323,6 +6346,11 @@ var init_page = __esm({
|
|
|
5323
6346
|
const waitUntil = (_a = options == null ? void 0 : options.waitUntil) != null ? _a : "domcontentloaded";
|
|
5324
6347
|
const timeout = (_b = options == null ? void 0 : options.timeoutMs) != null ? _b : 15e3;
|
|
5325
6348
|
const navigationCommandId = this.beginNavigationCommand();
|
|
6349
|
+
const tracker = new NavigationResponseTracker({
|
|
6350
|
+
page: this,
|
|
6351
|
+
session: this.mainSession,
|
|
6352
|
+
navigationCommandId
|
|
6353
|
+
});
|
|
5326
6354
|
const watcher = new LifecycleWatcher({
|
|
5327
6355
|
page: this,
|
|
5328
6356
|
mainSession: this.mainSession,
|
|
@@ -5339,17 +6367,22 @@ var init_page = __esm({
|
|
|
5339
6367
|
this.mainFrameId()
|
|
5340
6368
|
);
|
|
5341
6369
|
this._currentUrl = url;
|
|
5342
|
-
return;
|
|
6370
|
+
return null;
|
|
5343
6371
|
}
|
|
5344
6372
|
const response = yield this.mainSession.send(
|
|
5345
6373
|
"Page.navigate",
|
|
5346
6374
|
{ url }
|
|
5347
6375
|
);
|
|
5348
6376
|
this._currentUrl = url;
|
|
5349
|
-
if (response == null ? void 0 : response.loaderId)
|
|
6377
|
+
if (response == null ? void 0 : response.loaderId) {
|
|
6378
|
+
watcher.setExpectedLoaderId(response.loaderId);
|
|
6379
|
+
tracker.setExpectedLoaderId(response.loaderId);
|
|
6380
|
+
}
|
|
5350
6381
|
yield watcher.wait();
|
|
6382
|
+
return yield tracker.navigationCompleted();
|
|
5351
6383
|
} finally {
|
|
5352
6384
|
watcher.dispose();
|
|
6385
|
+
tracker.dispose();
|
|
5353
6386
|
}
|
|
5354
6387
|
});
|
|
5355
6388
|
}
|
|
@@ -5362,6 +6395,12 @@ var init_page = __esm({
|
|
|
5362
6395
|
const waitUntil = options == null ? void 0 : options.waitUntil;
|
|
5363
6396
|
const timeout = (_a = options == null ? void 0 : options.timeoutMs) != null ? _a : 15e3;
|
|
5364
6397
|
const navigationCommandId = this.beginNavigationCommand();
|
|
6398
|
+
const tracker = new NavigationResponseTracker({
|
|
6399
|
+
page: this,
|
|
6400
|
+
session: this.mainSession,
|
|
6401
|
+
navigationCommandId
|
|
6402
|
+
});
|
|
6403
|
+
tracker.expectNavigationWithoutKnownLoader();
|
|
5365
6404
|
const watcher = waitUntil ? new LifecycleWatcher({
|
|
5366
6405
|
page: this,
|
|
5367
6406
|
mainSession: this.mainSession,
|
|
@@ -5377,8 +6416,10 @@ var init_page = __esm({
|
|
|
5377
6416
|
if (watcher) {
|
|
5378
6417
|
yield watcher.wait();
|
|
5379
6418
|
}
|
|
6419
|
+
return yield tracker.navigationCompleted();
|
|
5380
6420
|
} finally {
|
|
5381
6421
|
watcher == null ? void 0 : watcher.dispose();
|
|
6422
|
+
tracker.dispose();
|
|
5382
6423
|
}
|
|
5383
6424
|
});
|
|
5384
6425
|
}
|
|
@@ -5392,10 +6433,16 @@ var init_page = __esm({
|
|
|
5392
6433
|
"Page.getNavigationHistory"
|
|
5393
6434
|
);
|
|
5394
6435
|
const prev = entries[currentIndex - 1];
|
|
5395
|
-
if (!prev) return;
|
|
6436
|
+
if (!prev) return null;
|
|
5396
6437
|
const waitUntil = options == null ? void 0 : options.waitUntil;
|
|
5397
6438
|
const timeout = (_a = options == null ? void 0 : options.timeoutMs) != null ? _a : 15e3;
|
|
5398
6439
|
const navigationCommandId = this.beginNavigationCommand();
|
|
6440
|
+
const tracker = new NavigationResponseTracker({
|
|
6441
|
+
page: this,
|
|
6442
|
+
session: this.mainSession,
|
|
6443
|
+
navigationCommandId
|
|
6444
|
+
});
|
|
6445
|
+
tracker.expectNavigationWithoutKnownLoader();
|
|
5399
6446
|
const watcher = waitUntil ? new LifecycleWatcher({
|
|
5400
6447
|
page: this,
|
|
5401
6448
|
mainSession: this.mainSession,
|
|
@@ -5412,8 +6459,10 @@ var init_page = __esm({
|
|
|
5412
6459
|
if (watcher) {
|
|
5413
6460
|
yield watcher.wait();
|
|
5414
6461
|
}
|
|
6462
|
+
return yield tracker.navigationCompleted();
|
|
5415
6463
|
} finally {
|
|
5416
6464
|
watcher == null ? void 0 : watcher.dispose();
|
|
6465
|
+
tracker.dispose();
|
|
5417
6466
|
}
|
|
5418
6467
|
});
|
|
5419
6468
|
}
|
|
@@ -5427,10 +6476,16 @@ var init_page = __esm({
|
|
|
5427
6476
|
"Page.getNavigationHistory"
|
|
5428
6477
|
);
|
|
5429
6478
|
const next = entries[currentIndex + 1];
|
|
5430
|
-
if (!next) return;
|
|
6479
|
+
if (!next) return null;
|
|
5431
6480
|
const waitUntil = options == null ? void 0 : options.waitUntil;
|
|
5432
6481
|
const timeout = (_a = options == null ? void 0 : options.timeoutMs) != null ? _a : 15e3;
|
|
5433
6482
|
const navigationCommandId = this.beginNavigationCommand();
|
|
6483
|
+
const tracker = new NavigationResponseTracker({
|
|
6484
|
+
page: this,
|
|
6485
|
+
session: this.mainSession,
|
|
6486
|
+
navigationCommandId
|
|
6487
|
+
});
|
|
6488
|
+
tracker.expectNavigationWithoutKnownLoader();
|
|
5434
6489
|
const watcher = waitUntil ? new LifecycleWatcher({
|
|
5435
6490
|
page: this,
|
|
5436
6491
|
mainSession: this.mainSession,
|
|
@@ -5447,8 +6502,10 @@ var init_page = __esm({
|
|
|
5447
6502
|
if (watcher) {
|
|
5448
6503
|
yield watcher.wait();
|
|
5449
6504
|
}
|
|
6505
|
+
return yield tracker.navigationCompleted();
|
|
5450
6506
|
} finally {
|
|
5451
6507
|
watcher == null ? void 0 : watcher.dispose();
|
|
6508
|
+
tracker.dispose();
|
|
5452
6509
|
}
|
|
5453
6510
|
});
|
|
5454
6511
|
}
|
|
@@ -5500,11 +6557,99 @@ var init_page = __esm({
|
|
|
5500
6557
|
});
|
|
5501
6558
|
}
|
|
5502
6559
|
/**
|
|
5503
|
-
* Capture a screenshot
|
|
6560
|
+
* Capture a screenshot with Playwright-style options.
|
|
6561
|
+
*
|
|
6562
|
+
* @param options Optional screenshot configuration.
|
|
6563
|
+
* @param options.animations Control CSS/Web animations during capture. Use
|
|
6564
|
+
* "disabled" to fast-forward finite animations and pause infinite ones.
|
|
6565
|
+
* @param options.caret Either hide the text caret (default) or leave it
|
|
6566
|
+
* visible via "initial".
|
|
6567
|
+
* @param options.clip Restrict capture to a specific rectangle (in CSS
|
|
6568
|
+
* pixels). Cannot be combined with `fullPage`.
|
|
6569
|
+
* @param options.fullPage Capture the full scrollable page instead of the
|
|
6570
|
+
* current viewport.
|
|
6571
|
+
* @param options.mask Array of locators that should be covered with an
|
|
6572
|
+
* overlay while the screenshot is taken.
|
|
6573
|
+
* @param options.maskColor CSS color used for the mask overlay (default
|
|
6574
|
+
* `#FF00FF`).
|
|
6575
|
+
* @param options.omitBackground Make the default page background transparent
|
|
6576
|
+
* (PNG only).
|
|
6577
|
+
* @param options.path File path to write the screenshot to. The file extension
|
|
6578
|
+
* determines the image type when `type` is not explicitly provided.
|
|
6579
|
+
* @param options.quality JPEG quality (0–100). Only applies when
|
|
6580
|
+
* `type === "jpeg"`.
|
|
6581
|
+
* @param options.scale Render scale: use "css" for one pixel per CSS pixel,
|
|
6582
|
+
* otherwise the default "device" leverages the current device pixel ratio.
|
|
6583
|
+
* @param options.style Additional CSS text injected into every frame before
|
|
6584
|
+
* capture (removed afterwards).
|
|
6585
|
+
* @param options.timeout Maximum capture duration in milliseconds before a
|
|
6586
|
+
* timeout error is thrown.
|
|
6587
|
+
* @param options.type Image format (`"png"` by default).
|
|
5504
6588
|
*/
|
|
5505
6589
|
screenshot(options) {
|
|
5506
6590
|
return __async(this, null, function* () {
|
|
5507
|
-
|
|
6591
|
+
var _a, _b, _c, _d, _e;
|
|
6592
|
+
const opts = options != null ? options : {};
|
|
6593
|
+
const type = (_a = opts.type) != null ? _a : "png";
|
|
6594
|
+
if (type !== "png" && type !== "jpeg") {
|
|
6595
|
+
throw new Error(`screenshot: unsupported image type "${type}"`);
|
|
6596
|
+
}
|
|
6597
|
+
if (opts.fullPage && opts.clip) {
|
|
6598
|
+
throw new Error("screenshot: clip and fullPage cannot be used together");
|
|
6599
|
+
}
|
|
6600
|
+
if (type === "png" && typeof opts.quality === "number") {
|
|
6601
|
+
throw new Error(
|
|
6602
|
+
'screenshot: quality option is only valid for type="jpeg"'
|
|
6603
|
+
);
|
|
6604
|
+
}
|
|
6605
|
+
const caretMode = (_b = opts.caret) != null ? _b : "hide";
|
|
6606
|
+
const animationsMode = (_c = opts.animations) != null ? _c : "allow";
|
|
6607
|
+
const scaleMode = (_d = opts.scale) != null ? _d : "device";
|
|
6608
|
+
const frames = collectFramesForScreenshot(this);
|
|
6609
|
+
const clip = opts.clip ? normalizeScreenshotClip(opts.clip) : void 0;
|
|
6610
|
+
const captureScale = yield computeScreenshotScale(this, scaleMode);
|
|
6611
|
+
const maskLocators = ((_e = opts.mask) != null ? _e : []).filter(
|
|
6612
|
+
(locator) => Boolean(locator)
|
|
6613
|
+
);
|
|
6614
|
+
const cleanupTasks = [];
|
|
6615
|
+
const exec = () => __async(this, null, function* () {
|
|
6616
|
+
var _a2;
|
|
6617
|
+
try {
|
|
6618
|
+
if (opts.omitBackground) {
|
|
6619
|
+
cleanupTasks.push(yield setTransparentBackground(this.mainSession));
|
|
6620
|
+
}
|
|
6621
|
+
if (animationsMode === "disabled") {
|
|
6622
|
+
cleanupTasks.push(yield disableAnimations(frames));
|
|
6623
|
+
}
|
|
6624
|
+
if (caretMode === "hide") {
|
|
6625
|
+
cleanupTasks.push(yield hideCaret(frames));
|
|
6626
|
+
}
|
|
6627
|
+
if (opts.style && opts.style.trim()) {
|
|
6628
|
+
cleanupTasks.push(
|
|
6629
|
+
yield applyStyleToFrames(frames, opts.style, "custom")
|
|
6630
|
+
);
|
|
6631
|
+
}
|
|
6632
|
+
if (maskLocators.length > 0) {
|
|
6633
|
+
cleanupTasks.push(
|
|
6634
|
+
yield applyMaskOverlays(maskLocators, (_a2 = opts.maskColor) != null ? _a2 : "#FF00FF")
|
|
6635
|
+
);
|
|
6636
|
+
}
|
|
6637
|
+
const buffer = yield this.mainFrameWrapper.screenshot({
|
|
6638
|
+
fullPage: opts.fullPage,
|
|
6639
|
+
clip,
|
|
6640
|
+
type,
|
|
6641
|
+
quality: type === "jpeg" ? opts.quality : void 0,
|
|
6642
|
+
scale: captureScale
|
|
6643
|
+
});
|
|
6644
|
+
if (opts.path) {
|
|
6645
|
+
yield import_fs5.promises.writeFile(opts.path, buffer);
|
|
6646
|
+
}
|
|
6647
|
+
return buffer;
|
|
6648
|
+
} finally {
|
|
6649
|
+
yield runScreenshotCleanups(cleanupTasks);
|
|
6650
|
+
}
|
|
6651
|
+
});
|
|
6652
|
+
return withScreenshotTimeout(opts.timeout, exec);
|
|
5508
6653
|
});
|
|
5509
6654
|
}
|
|
5510
6655
|
/**
|
|
@@ -6222,12 +7367,14 @@ var init_page = __esm({
|
|
|
6222
7367
|
// lib/v3/index.ts
|
|
6223
7368
|
var v3_exports = {};
|
|
6224
7369
|
__export(v3_exports, {
|
|
7370
|
+
AISdkClient: () => AISdkClient2,
|
|
6225
7371
|
AVAILABLE_CUA_MODELS: () => AVAILABLE_CUA_MODELS,
|
|
6226
7372
|
AgentProvider: () => AgentProvider,
|
|
6227
7373
|
AgentScreenshotProviderError: () => AgentScreenshotProviderError,
|
|
6228
7374
|
AnnotatedScreenshotText: () => AnnotatedScreenshotText,
|
|
6229
7375
|
BrowserbaseSessionNotFoundError: () => BrowserbaseSessionNotFoundError,
|
|
6230
7376
|
CaptchaTimeoutError: () => CaptchaTimeoutError,
|
|
7377
|
+
ConsoleMessage: () => ConsoleMessage,
|
|
6231
7378
|
ContentFrameNotFoundError: () => ContentFrameNotFoundError,
|
|
6232
7379
|
CreateChatCompletionResponseError: () => CreateChatCompletionResponseError,
|
|
6233
7380
|
ExperimentalApiConflictError: () => ExperimentalApiConflictError,
|
|
@@ -6240,6 +7387,7 @@ __export(v3_exports, {
|
|
|
6240
7387
|
MCPConnectionError: () => MCPConnectionError,
|
|
6241
7388
|
MissingEnvironmentVariableError: () => MissingEnvironmentVariableError,
|
|
6242
7389
|
MissingLLMConfigurationError: () => MissingLLMConfigurationError,
|
|
7390
|
+
Response: () => Response,
|
|
6243
7391
|
Stagehand: () => V3,
|
|
6244
7392
|
StagehandAPIError: () => StagehandAPIError,
|
|
6245
7393
|
StagehandAPIUnauthorizedError: () => StagehandAPIUnauthorizedError,
|
|
@@ -6289,13 +7437,13 @@ module.exports = __toCommonJS(v3_exports);
|
|
|
6289
7437
|
|
|
6290
7438
|
// lib/v3/v3.ts
|
|
6291
7439
|
var import_dotenv = __toESM(require("dotenv"));
|
|
6292
|
-
var
|
|
7440
|
+
var import_fs6 = __toESM(require("fs"));
|
|
6293
7441
|
var import_os2 = __toESM(require("os"));
|
|
6294
7442
|
var import_path5 = __toESM(require("path"));
|
|
6295
7443
|
var import_process2 = __toESM(require("process"));
|
|
6296
7444
|
|
|
6297
7445
|
// lib/version.ts
|
|
6298
|
-
var STAGEHAND_VERSION = "3.0.
|
|
7446
|
+
var STAGEHAND_VERSION = "3.0.1-zod4";
|
|
6299
7447
|
|
|
6300
7448
|
// lib/v3/types/public/sdkErrors.ts
|
|
6301
7449
|
var StagehandError = class extends Error {
|
|
@@ -7030,7 +8178,18 @@ function isTestEnvironment() {
|
|
|
7030
8178
|
var _StagehandLogger = class _StagehandLogger {
|
|
7031
8179
|
constructor(options = {}, externalLogger) {
|
|
7032
8180
|
this.isTest = isTestEnvironment();
|
|
7033
|
-
this.
|
|
8181
|
+
this.externalLogger = externalLogger;
|
|
8182
|
+
const externalProvided = typeof externalLogger === "function";
|
|
8183
|
+
const explicitUsePino = options.usePino;
|
|
8184
|
+
if (this.isTest) {
|
|
8185
|
+
this.usePino = false;
|
|
8186
|
+
} else if (explicitUsePino === true) {
|
|
8187
|
+
this.usePino = true;
|
|
8188
|
+
} else if (explicitUsePino === false) {
|
|
8189
|
+
this.usePino = false;
|
|
8190
|
+
} else {
|
|
8191
|
+
this.usePino = !externalProvided;
|
|
8192
|
+
}
|
|
7034
8193
|
if (this.usePino) {
|
|
7035
8194
|
if (!_StagehandLogger.sharedPinoLogger) {
|
|
7036
8195
|
_StagehandLogger.sharedPinoLogger = createLogger(options);
|
|
@@ -7038,7 +8197,6 @@ var _StagehandLogger = class _StagehandLogger {
|
|
|
7038
8197
|
this.logger = _StagehandLogger.sharedPinoLogger;
|
|
7039
8198
|
}
|
|
7040
8199
|
this.verbose = 1;
|
|
7041
|
-
this.externalLogger = externalLogger;
|
|
7042
8200
|
}
|
|
7043
8201
|
/**
|
|
7044
8202
|
* Set the verbosity level
|
|
@@ -7307,7 +8465,7 @@ var ActCache = class {
|
|
|
7307
8465
|
}
|
|
7308
8466
|
}
|
|
7309
8467
|
});
|
|
7310
|
-
return yield this.replayCachedActions(entry, page, timeout);
|
|
8468
|
+
return yield this.replayCachedActions(context, entry, page, timeout);
|
|
7311
8469
|
});
|
|
7312
8470
|
}
|
|
7313
8471
|
store(context, result) {
|
|
@@ -7357,7 +8515,7 @@ var ActCache = class {
|
|
|
7357
8515
|
});
|
|
7358
8516
|
return (0, import_crypto.createHash)("sha256").update(payload).digest("hex");
|
|
7359
8517
|
}
|
|
7360
|
-
replayCachedActions(entry, page, timeout) {
|
|
8518
|
+
replayCachedActions(context, entry, page, timeout) {
|
|
7361
8519
|
return __async(this, null, function* () {
|
|
7362
8520
|
const handler = this.getActHandler();
|
|
7363
8521
|
if (!handler) {
|
|
@@ -7393,6 +8551,13 @@ var ActCache = class {
|
|
|
7393
8551
|
});
|
|
7394
8552
|
const message = actionResults.map((r) => r.message).filter((m) => m && m.trim().length > 0).join(" \u2192 ") || entry.message || `Replayed ${entry.actions.length} cached action${entry.actions.length === 1 ? "" : "s"}.`;
|
|
7395
8553
|
const actionDescription = entry.actionDescription || ((_b = actionResults[actionResults.length - 1]) == null ? void 0 : _b.actionDescription) || ((_c = entry.actions[entry.actions.length - 1]) == null ? void 0 : _c.description) || entry.instruction;
|
|
8554
|
+
if (success && actions.length > 0 && this.haveActionsChanged(entry.actions, actions)) {
|
|
8555
|
+
yield this.refreshCacheEntry(context, __spreadProps(__spreadValues({}, entry), {
|
|
8556
|
+
actions,
|
|
8557
|
+
message,
|
|
8558
|
+
actionDescription
|
|
8559
|
+
}));
|
|
8560
|
+
}
|
|
7396
8561
|
return {
|
|
7397
8562
|
success,
|
|
7398
8563
|
message,
|
|
@@ -7403,6 +8568,67 @@ var ActCache = class {
|
|
|
7403
8568
|
return yield this.runWithTimeout(execute, timeout);
|
|
7404
8569
|
});
|
|
7405
8570
|
}
|
|
8571
|
+
haveActionsChanged(original, updated) {
|
|
8572
|
+
var _a, _b, _c, _d;
|
|
8573
|
+
if (original.length !== updated.length) {
|
|
8574
|
+
return true;
|
|
8575
|
+
}
|
|
8576
|
+
for (let i = 0; i < original.length; i += 1) {
|
|
8577
|
+
const orig = original[i];
|
|
8578
|
+
const next = updated[i];
|
|
8579
|
+
if (!next) {
|
|
8580
|
+
return true;
|
|
8581
|
+
}
|
|
8582
|
+
if (orig.selector !== next.selector) {
|
|
8583
|
+
return true;
|
|
8584
|
+
}
|
|
8585
|
+
if (orig.description !== next.description) {
|
|
8586
|
+
return true;
|
|
8587
|
+
}
|
|
8588
|
+
if (((_a = orig.method) != null ? _a : "") !== ((_b = next.method) != null ? _b : "")) {
|
|
8589
|
+
return true;
|
|
8590
|
+
}
|
|
8591
|
+
const origArgs = (_c = orig.arguments) != null ? _c : [];
|
|
8592
|
+
const nextArgs = (_d = next.arguments) != null ? _d : [];
|
|
8593
|
+
if (origArgs.length !== nextArgs.length) {
|
|
8594
|
+
return true;
|
|
8595
|
+
}
|
|
8596
|
+
for (let j = 0; j < origArgs.length; j += 1) {
|
|
8597
|
+
if (origArgs[j] !== nextArgs[j]) {
|
|
8598
|
+
return true;
|
|
8599
|
+
}
|
|
8600
|
+
}
|
|
8601
|
+
}
|
|
8602
|
+
return false;
|
|
8603
|
+
}
|
|
8604
|
+
refreshCacheEntry(context, entry) {
|
|
8605
|
+
return __async(this, null, function* () {
|
|
8606
|
+
const { error, path: path6 } = yield this.storage.writeJson(
|
|
8607
|
+
`${context.cacheKey}.json`,
|
|
8608
|
+
entry
|
|
8609
|
+
);
|
|
8610
|
+
if (error && path6) {
|
|
8611
|
+
this.logger({
|
|
8612
|
+
category: "cache",
|
|
8613
|
+
message: "failed to update act cache entry after self-heal",
|
|
8614
|
+
level: 0,
|
|
8615
|
+
auxiliary: {
|
|
8616
|
+
error: { value: String(error), type: "string" }
|
|
8617
|
+
}
|
|
8618
|
+
});
|
|
8619
|
+
return;
|
|
8620
|
+
}
|
|
8621
|
+
this.logger({
|
|
8622
|
+
category: "cache",
|
|
8623
|
+
message: "act cache entry updated after self-heal",
|
|
8624
|
+
level: 2,
|
|
8625
|
+
auxiliary: {
|
|
8626
|
+
instruction: { value: context.instruction, type: "string" },
|
|
8627
|
+
url: { value: context.pageUrl, type: "string" }
|
|
8628
|
+
}
|
|
8629
|
+
});
|
|
8630
|
+
});
|
|
8631
|
+
}
|
|
7406
8632
|
runWithTimeout(run, timeout) {
|
|
7407
8633
|
return __async(this, null, function* () {
|
|
7408
8634
|
if (!timeout) {
|
|
@@ -10045,25 +11271,37 @@ function evaluateZodSchema(schemaStr, logger) {
|
|
|
10045
11271
|
message: `Failed to evaluate schema: ${(_a = e == null ? void 0 : e.message) != null ? _a : String(e)}`,
|
|
10046
11272
|
level: 0
|
|
10047
11273
|
});
|
|
10048
|
-
|
|
10049
|
-
"Invalid schema: Ensure you're passing a valid Zod schema expression, e.g. z.object({ title: z.string() })"
|
|
10050
|
-
);
|
|
11274
|
+
return import_zod14.z.any();
|
|
10051
11275
|
}
|
|
10052
11276
|
}
|
|
10053
11277
|
var createExtractTool = (v3, executionModel, logger) => (0, import_ai10.tool)({
|
|
10054
|
-
description:
|
|
11278
|
+
description: `Extract structured data from the current page based on a provided schema.
|
|
11279
|
+
|
|
11280
|
+
USAGE GUIDELINES:
|
|
11281
|
+
- Keep schemas MINIMAL - only include fields essential for the task
|
|
11282
|
+
- IMPORANT: only use this if explicitly asked for structured output. In most scenarios, you should use the aria tree tool over this.
|
|
11283
|
+
- If you need to extract a link, make sure the type defintion follows the format of z.string().url()
|
|
11284
|
+
EXAMPLES:
|
|
11285
|
+
1. Extract a single value:
|
|
11286
|
+
instruction: "extract the product price"
|
|
11287
|
+
schema: "z.object({ price: z.number()})"
|
|
11288
|
+
|
|
11289
|
+
2. Extract multiple fields:
|
|
11290
|
+
instruction: "extract product name and price"
|
|
11291
|
+
schema: "z.object({ name: z.string(), price: z.number() })"
|
|
11292
|
+
|
|
11293
|
+
3. Extract arrays:
|
|
11294
|
+
instruction: "extract all product names and prices"
|
|
11295
|
+
schema: "z.object({ products: z.array(z.object({ name: z.string(), price: z.number() })) })"`,
|
|
10055
11296
|
inputSchema: import_zod14.z.object({
|
|
10056
|
-
instruction: import_zod14.z.string()
|
|
10057
|
-
schema: import_zod14.z.string().optional().describe("Zod schema as code, e.g. z.object({ title: z.string() })")
|
|
10058
|
-
selector: import_zod14.z.string().optional()
|
|
11297
|
+
instruction: import_zod14.z.string(),
|
|
11298
|
+
schema: import_zod14.z.string().optional().describe("Zod schema as code, e.g. z.object({ title: z.string() })")
|
|
10059
11299
|
}),
|
|
10060
|
-
execute: (_0) => __async(null, [_0], function* ({ instruction, schema
|
|
11300
|
+
execute: (_0) => __async(null, [_0], function* ({ instruction, schema }) {
|
|
10061
11301
|
var _a;
|
|
10062
11302
|
try {
|
|
10063
11303
|
const parsedSchema = schema ? evaluateZodSchema(schema, logger) : void 0;
|
|
10064
|
-
const result = yield v3.extract(instruction, parsedSchema,
|
|
10065
|
-
selector
|
|
10066
|
-
}));
|
|
11304
|
+
const result = yield v3.extract(instruction, parsedSchema, __spreadValues({}, executionModel ? { model: executionModel } : {}));
|
|
10067
11305
|
return { success: true, result };
|
|
10068
11306
|
} catch (error) {
|
|
10069
11307
|
return { success: false, error: (_a = error == null ? void 0 : error.message) != null ? _a : String(error) };
|
|
@@ -10224,12 +11462,12 @@ function mapToolResultToActions({
|
|
|
10224
11462
|
case "fillForm":
|
|
10225
11463
|
return mapFillFormToolResult(toolResult, args, reasoning);
|
|
10226
11464
|
default:
|
|
10227
|
-
return [createStandardAction(toolCallName, args, reasoning)];
|
|
11465
|
+
return [createStandardAction(toolCallName, toolResult, args, reasoning)];
|
|
10228
11466
|
}
|
|
10229
11467
|
}
|
|
10230
11468
|
function mapActToolResult(toolResult, args, reasoning) {
|
|
10231
11469
|
if (!toolResult || typeof toolResult !== "object") {
|
|
10232
|
-
return [createStandardAction("act", args, reasoning)];
|
|
11470
|
+
return [createStandardAction("act", toolResult, args, reasoning)];
|
|
10233
11471
|
}
|
|
10234
11472
|
const result = toolResult;
|
|
10235
11473
|
const output = result.output || result;
|
|
@@ -10245,7 +11483,7 @@ function mapActToolResult(toolResult, args, reasoning) {
|
|
|
10245
11483
|
}
|
|
10246
11484
|
function mapFillFormToolResult(toolResult, args, reasoning) {
|
|
10247
11485
|
if (!toolResult || typeof toolResult !== "object") {
|
|
10248
|
-
return [createStandardAction("fillForm", args, reasoning)];
|
|
11486
|
+
return [createStandardAction("fillForm", toolResult, args, reasoning)];
|
|
10249
11487
|
}
|
|
10250
11488
|
const result = toolResult;
|
|
10251
11489
|
const output = result.output || result;
|
|
@@ -10266,12 +11504,17 @@ function mapFillFormToolResult(toolResult, args, reasoning) {
|
|
|
10266
11504
|
}
|
|
10267
11505
|
return actions;
|
|
10268
11506
|
}
|
|
10269
|
-
function createStandardAction(toolCallName, args, reasoning) {
|
|
10270
|
-
|
|
11507
|
+
function createStandardAction(toolCallName, toolResult, args, reasoning) {
|
|
11508
|
+
const action = __spreadValues({
|
|
10271
11509
|
type: toolCallName,
|
|
10272
11510
|
reasoning,
|
|
10273
11511
|
taskCompleted: toolCallName === "close" ? args == null ? void 0 : args.taskComplete : false
|
|
10274
11512
|
}, args);
|
|
11513
|
+
if (toolCallName !== "ariaTree" && toolResult) {
|
|
11514
|
+
const { output } = toolResult;
|
|
11515
|
+
Object.assign(action, output);
|
|
11516
|
+
}
|
|
11517
|
+
return action;
|
|
10275
11518
|
}
|
|
10276
11519
|
|
|
10277
11520
|
// lib/v3/handlers/v3AgentHandler.ts
|
|
@@ -10416,7 +11659,7 @@ var V3AgentHandler = class {
|
|
|
10416
11659
|
buildSystemPrompt(executionInstruction, systemInstructions) {
|
|
10417
11660
|
if (systemInstructions) {
|
|
10418
11661
|
return `${systemInstructions}
|
|
10419
|
-
Your current goal: ${executionInstruction}`;
|
|
11662
|
+
Your current goal: ${executionInstruction} when the task is complete, use the "close" tool with taskComplete: true`;
|
|
10420
11663
|
}
|
|
10421
11664
|
return `You are a web automation assistant using browser automation tools to accomplish the user's goal.
|
|
10422
11665
|
|
|
@@ -37344,6 +38587,111 @@ var LOG_LEVEL_NAMES = {
|
|
|
37344
38587
|
2: "debug"
|
|
37345
38588
|
};
|
|
37346
38589
|
|
|
38590
|
+
// lib/v3/types/public/page.ts
|
|
38591
|
+
init_consoleMessage();
|
|
38592
|
+
init_response();
|
|
38593
|
+
|
|
38594
|
+
// examples/external_clients/aisdk.ts
|
|
38595
|
+
var import_ai14 = require("ai");
|
|
38596
|
+
var AISdkClient2 = class extends LLMClient {
|
|
38597
|
+
constructor({ model }) {
|
|
38598
|
+
super(model.modelId);
|
|
38599
|
+
this.type = "aisdk";
|
|
38600
|
+
this.model = model;
|
|
38601
|
+
}
|
|
38602
|
+
createChatCompletion(_0) {
|
|
38603
|
+
return __async(this, arguments, function* ({
|
|
38604
|
+
options
|
|
38605
|
+
}) {
|
|
38606
|
+
var _a, _b, _c, _d, _e, _f;
|
|
38607
|
+
const formattedMessages = options.messages.map(
|
|
38608
|
+
(message) => {
|
|
38609
|
+
if (Array.isArray(message.content)) {
|
|
38610
|
+
if (message.role === "system") {
|
|
38611
|
+
const systemMessage = {
|
|
38612
|
+
role: "system",
|
|
38613
|
+
content: message.content.map((c) => "text" in c ? c.text : "").join("\n")
|
|
38614
|
+
};
|
|
38615
|
+
return systemMessage;
|
|
38616
|
+
}
|
|
38617
|
+
const contentParts = message.content.map((content) => {
|
|
38618
|
+
if ("image_url" in content) {
|
|
38619
|
+
const imageContent = {
|
|
38620
|
+
type: "image",
|
|
38621
|
+
image: content.image_url.url
|
|
38622
|
+
};
|
|
38623
|
+
return imageContent;
|
|
38624
|
+
} else {
|
|
38625
|
+
const textContent = {
|
|
38626
|
+
type: "text",
|
|
38627
|
+
text: content.text
|
|
38628
|
+
};
|
|
38629
|
+
return textContent;
|
|
38630
|
+
}
|
|
38631
|
+
});
|
|
38632
|
+
if (message.role === "user") {
|
|
38633
|
+
const userMessage = {
|
|
38634
|
+
role: "user",
|
|
38635
|
+
content: contentParts
|
|
38636
|
+
};
|
|
38637
|
+
return userMessage;
|
|
38638
|
+
} else {
|
|
38639
|
+
const textOnlyParts = contentParts.map((part) => ({
|
|
38640
|
+
type: "text",
|
|
38641
|
+
text: part.type === "image" ? "[Image]" : part.text
|
|
38642
|
+
}));
|
|
38643
|
+
const assistantMessage = {
|
|
38644
|
+
role: "assistant",
|
|
38645
|
+
content: textOnlyParts
|
|
38646
|
+
};
|
|
38647
|
+
return assistantMessage;
|
|
38648
|
+
}
|
|
38649
|
+
}
|
|
38650
|
+
return {
|
|
38651
|
+
role: message.role,
|
|
38652
|
+
content: message.content
|
|
38653
|
+
};
|
|
38654
|
+
}
|
|
38655
|
+
);
|
|
38656
|
+
if (options.response_model) {
|
|
38657
|
+
const response2 = yield (0, import_ai14.generateObject)({
|
|
38658
|
+
model: this.model,
|
|
38659
|
+
messages: formattedMessages,
|
|
38660
|
+
schema: options.response_model.schema
|
|
38661
|
+
});
|
|
38662
|
+
return {
|
|
38663
|
+
data: response2.object,
|
|
38664
|
+
usage: {
|
|
38665
|
+
prompt_tokens: (_a = response2.usage.inputTokens) != null ? _a : 0,
|
|
38666
|
+
completion_tokens: (_b = response2.usage.outputTokens) != null ? _b : 0,
|
|
38667
|
+
total_tokens: (_c = response2.usage.totalTokens) != null ? _c : 0
|
|
38668
|
+
}
|
|
38669
|
+
};
|
|
38670
|
+
}
|
|
38671
|
+
const tools = {};
|
|
38672
|
+
for (const rawTool of options.tools) {
|
|
38673
|
+
tools[rawTool.name] = {
|
|
38674
|
+
description: rawTool.description,
|
|
38675
|
+
inputSchema: rawTool.parameters
|
|
38676
|
+
};
|
|
38677
|
+
}
|
|
38678
|
+
const response = yield (0, import_ai14.generateText)({
|
|
38679
|
+
model: this.model,
|
|
38680
|
+
messages: formattedMessages,
|
|
38681
|
+
tools
|
|
38682
|
+
});
|
|
38683
|
+
return {
|
|
38684
|
+
data: response.text,
|
|
38685
|
+
usage: {
|
|
38686
|
+
prompt_tokens: (_d = response.usage.inputTokens) != null ? _d : 0,
|
|
38687
|
+
completion_tokens: (_e = response.usage.outputTokens) != null ? _e : 0,
|
|
38688
|
+
total_tokens: (_f = response.usage.totalTokens) != null ? _f : 0
|
|
38689
|
+
}
|
|
38690
|
+
};
|
|
38691
|
+
});
|
|
38692
|
+
}
|
|
38693
|
+
};
|
|
38694
|
+
|
|
37347
38695
|
// lib/v3/understudy/context.ts
|
|
37348
38696
|
init_logger();
|
|
37349
38697
|
|
|
@@ -38351,6 +39699,85 @@ var StagehandAPIClient = class {
|
|
|
38351
39699
|
return response;
|
|
38352
39700
|
});
|
|
38353
39701
|
}
|
|
39702
|
+
getReplayMetrics() {
|
|
39703
|
+
return __async(this, null, function* () {
|
|
39704
|
+
if (!this.sessionId) {
|
|
39705
|
+
throw new StagehandAPIError("sessionId is required to fetch metrics.");
|
|
39706
|
+
}
|
|
39707
|
+
const response = yield this.request(`/sessions/${this.sessionId}/replay`, {
|
|
39708
|
+
method: "GET"
|
|
39709
|
+
});
|
|
39710
|
+
if (response.status !== 200) {
|
|
39711
|
+
const errorText = yield response.text();
|
|
39712
|
+
this.logger({
|
|
39713
|
+
category: "api",
|
|
39714
|
+
message: `Failed to fetch metrics. Status ${response.status}: ${errorText}`,
|
|
39715
|
+
level: 0
|
|
39716
|
+
});
|
|
39717
|
+
throw new StagehandHttpError(
|
|
39718
|
+
`Failed to fetch metrics with status ${response.status}: ${errorText}`
|
|
39719
|
+
);
|
|
39720
|
+
}
|
|
39721
|
+
const data = yield response.json();
|
|
39722
|
+
if (!data.success) {
|
|
39723
|
+
throw new StagehandAPIError(
|
|
39724
|
+
`Failed to fetch metrics: ${data.error || "Unknown error"}`
|
|
39725
|
+
);
|
|
39726
|
+
}
|
|
39727
|
+
const apiData = data.data || {};
|
|
39728
|
+
const metrics = {
|
|
39729
|
+
actPromptTokens: 0,
|
|
39730
|
+
actCompletionTokens: 0,
|
|
39731
|
+
actInferenceTimeMs: 0,
|
|
39732
|
+
extractPromptTokens: 0,
|
|
39733
|
+
extractCompletionTokens: 0,
|
|
39734
|
+
extractInferenceTimeMs: 0,
|
|
39735
|
+
observePromptTokens: 0,
|
|
39736
|
+
observeCompletionTokens: 0,
|
|
39737
|
+
observeInferenceTimeMs: 0,
|
|
39738
|
+
agentPromptTokens: 0,
|
|
39739
|
+
agentCompletionTokens: 0,
|
|
39740
|
+
agentInferenceTimeMs: 0,
|
|
39741
|
+
totalPromptTokens: 0,
|
|
39742
|
+
totalCompletionTokens: 0,
|
|
39743
|
+
totalInferenceTimeMs: 0
|
|
39744
|
+
};
|
|
39745
|
+
const pages = apiData.pages || [];
|
|
39746
|
+
for (const page of pages) {
|
|
39747
|
+
const actions = page.actions || [];
|
|
39748
|
+
for (const action of actions) {
|
|
39749
|
+
const method = (action.method || "").toLowerCase();
|
|
39750
|
+
const tokenUsage = action.tokenUsage;
|
|
39751
|
+
if (tokenUsage) {
|
|
39752
|
+
const inputTokens = tokenUsage.inputTokens || 0;
|
|
39753
|
+
const outputTokens = tokenUsage.outputTokens || 0;
|
|
39754
|
+
const timeMs = tokenUsage.timeMs || 0;
|
|
39755
|
+
if (method === "act") {
|
|
39756
|
+
metrics.actPromptTokens += inputTokens;
|
|
39757
|
+
metrics.actCompletionTokens += outputTokens;
|
|
39758
|
+
metrics.actInferenceTimeMs += timeMs;
|
|
39759
|
+
} else if (method === "extract") {
|
|
39760
|
+
metrics.extractPromptTokens += inputTokens;
|
|
39761
|
+
metrics.extractCompletionTokens += outputTokens;
|
|
39762
|
+
metrics.extractInferenceTimeMs += timeMs;
|
|
39763
|
+
} else if (method === "observe") {
|
|
39764
|
+
metrics.observePromptTokens += inputTokens;
|
|
39765
|
+
metrics.observeCompletionTokens += outputTokens;
|
|
39766
|
+
metrics.observeInferenceTimeMs += timeMs;
|
|
39767
|
+
} else if (method === "agent") {
|
|
39768
|
+
metrics.agentPromptTokens += inputTokens;
|
|
39769
|
+
metrics.agentCompletionTokens += outputTokens;
|
|
39770
|
+
metrics.agentInferenceTimeMs += timeMs;
|
|
39771
|
+
}
|
|
39772
|
+
metrics.totalPromptTokens += inputTokens;
|
|
39773
|
+
metrics.totalCompletionTokens += outputTokens;
|
|
39774
|
+
metrics.totalInferenceTimeMs += timeMs;
|
|
39775
|
+
}
|
|
39776
|
+
}
|
|
39777
|
+
}
|
|
39778
|
+
return metrics;
|
|
39779
|
+
});
|
|
39780
|
+
}
|
|
38354
39781
|
execute(_0) {
|
|
38355
39782
|
return __async(this, arguments, function* ({
|
|
38356
39783
|
method,
|
|
@@ -38531,15 +39958,15 @@ var _V3 = class _V3 {
|
|
|
38531
39958
|
this.externalLogger = opts.logger;
|
|
38532
39959
|
this.verbose = (_a = opts.verbose) != null ? _a : 1;
|
|
38533
39960
|
this.instanceId = (_d = (_c = (_b = globalThis.crypto) == null ? void 0 : _b.randomUUID) == null ? void 0 : _c.call(_b)) != null ? _d : `${Date.now()}-${Math.floor(Math.random() * 1e9)}`;
|
|
38534
|
-
|
|
38535
|
-
|
|
38536
|
-
|
|
38537
|
-
|
|
38538
|
-
|
|
38539
|
-
|
|
38540
|
-
|
|
38541
|
-
|
|
38542
|
-
);
|
|
39961
|
+
const loggerOptions = {
|
|
39962
|
+
pretty: true,
|
|
39963
|
+
level: "info"
|
|
39964
|
+
// Most permissive - filtering happens at instance level
|
|
39965
|
+
};
|
|
39966
|
+
if (opts.disablePino !== void 0) {
|
|
39967
|
+
loggerOptions.usePino = !opts.disablePino;
|
|
39968
|
+
}
|
|
39969
|
+
this.stagehandLogger = new StagehandLogger(loggerOptions, opts.logger);
|
|
38543
39970
|
this.stagehandLogger.setVerbosity(this.verbose);
|
|
38544
39971
|
try {
|
|
38545
39972
|
if (this.externalLogger) {
|
|
@@ -38557,11 +39984,12 @@ var _V3 = class _V3 {
|
|
|
38557
39984
|
this.logInferenceToFile = (_f = opts.logInferenceToFile) != null ? _f : false;
|
|
38558
39985
|
this.llmProvider = new LLMProvider(this.logger);
|
|
38559
39986
|
this.domSettleTimeoutMs = opts.domSettleTimeout;
|
|
38560
|
-
this.disableAPI = (_g = opts.disableAPI) != null ? _g :
|
|
39987
|
+
this.disableAPI = (_g = opts.disableAPI) != null ? _g : false;
|
|
38561
39988
|
const baseClientOptions = clientOptions ? __spreadValues({}, clientOptions) : {};
|
|
38562
39989
|
if (opts.llmClient) {
|
|
38563
39990
|
this.llmClient = opts.llmClient;
|
|
38564
39991
|
this.modelClientOptions = baseClientOptions;
|
|
39992
|
+
this.disableAPI = true;
|
|
38565
39993
|
} else {
|
|
38566
39994
|
let apiKey = baseClientOptions.apiKey;
|
|
38567
39995
|
if (!apiKey) {
|
|
@@ -38612,11 +40040,24 @@ var _V3 = class _V3 {
|
|
|
38612
40040
|
this.opts = opts;
|
|
38613
40041
|
_V3._instances.add(this);
|
|
38614
40042
|
}
|
|
40043
|
+
get browserbaseSessionID() {
|
|
40044
|
+
return this.browserbaseSessionId;
|
|
40045
|
+
}
|
|
38615
40046
|
/**
|
|
38616
40047
|
* Async property for metrics so callers can `await v3.metrics`.
|
|
38617
|
-
*
|
|
40048
|
+
* When using API mode, fetches metrics from the API. Otherwise returns local metrics.
|
|
38618
40049
|
*/
|
|
38619
40050
|
get metrics() {
|
|
40051
|
+
if (this.apiClient) {
|
|
40052
|
+
return this.apiClient.getReplayMetrics().catch((error) => {
|
|
40053
|
+
this.logger({
|
|
40054
|
+
category: "metrics",
|
|
40055
|
+
message: `Failed to fetch metrics from API: ${error}`,
|
|
40056
|
+
level: 0
|
|
40057
|
+
});
|
|
40058
|
+
return this.stagehandMetrics;
|
|
40059
|
+
});
|
|
40060
|
+
}
|
|
38620
40061
|
return Promise.resolve(this.stagehandMetrics);
|
|
38621
40062
|
}
|
|
38622
40063
|
resolveLlmClient(model) {
|
|
@@ -38891,8 +40332,8 @@ var _V3 = class _V3 {
|
|
|
38891
40332
|
let createdTemp = false;
|
|
38892
40333
|
if (!userDataDir) {
|
|
38893
40334
|
const base = import_path5.default.join(import_os2.default.tmpdir(), "stagehand-v3");
|
|
38894
|
-
|
|
38895
|
-
userDataDir =
|
|
40335
|
+
import_fs6.default.mkdirSync(base, { recursive: true });
|
|
40336
|
+
userDataDir = import_fs6.default.mkdtempSync(import_path5.default.join(base, "profile-"));
|
|
38896
40337
|
createdTemp = true;
|
|
38897
40338
|
}
|
|
38898
40339
|
const defaults2 = [
|
|
@@ -39321,7 +40762,7 @@ var _V3 = class _V3 {
|
|
|
39321
40762
|
}
|
|
39322
40763
|
try {
|
|
39323
40764
|
if (this.state.createdTempProfile && !this.state.preserveUserDataDir && this.state.userDataDir) {
|
|
39324
|
-
|
|
40765
|
+
import_fs6.default.rmSync(this.state.userDataDir, { recursive: true, force: true });
|
|
39325
40766
|
}
|
|
39326
40767
|
} catch (e) {
|
|
39327
40768
|
}
|
|
@@ -39461,23 +40902,29 @@ var _V3 = class _V3 {
|
|
|
39461
40902
|
* Mirrors the v2 Stagehand.agent() tool mode (no CUA provider here).
|
|
39462
40903
|
*/
|
|
39463
40904
|
agent(options) {
|
|
39464
|
-
var _a, _b
|
|
40905
|
+
var _a, _b;
|
|
39465
40906
|
this.logger({
|
|
39466
40907
|
category: "agent",
|
|
39467
|
-
message:
|
|
40908
|
+
message: `Creating v3 agent instance with options: ${JSON.stringify(options)}`,
|
|
39468
40909
|
level: 1,
|
|
39469
|
-
auxiliary: {
|
|
40910
|
+
auxiliary: __spreadValues({
|
|
39470
40911
|
cua: { value: (options == null ? void 0 : options.cua) ? "true" : "false", type: "boolean" },
|
|
39471
|
-
model: typeof (options == null ? void 0 : options.model) === "string" ? { value: options.model, type: "string" } : { value: options.model.modelName, type: "string" },
|
|
40912
|
+
model: (options == null ? void 0 : options.model) ? typeof (options == null ? void 0 : options.model) === "string" ? { value: options.model, type: "string" } : { value: options.model.modelName, type: "string" } : { value: this.llmClient.modelName, type: "string" },
|
|
39472
40913
|
systemPrompt: { value: (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : "", type: "string" },
|
|
39473
|
-
tools: { value: JSON.stringify((_b = options == null ? void 0 : options.tools) != null ? _b : {}), type: "object" }
|
|
40914
|
+
tools: { value: JSON.stringify((_b = options == null ? void 0 : options.tools) != null ? _b : {}), type: "object" }
|
|
40915
|
+
}, (options == null ? void 0 : options.integrations) && {
|
|
39474
40916
|
integrations: {
|
|
39475
|
-
value: JSON.stringify(
|
|
40917
|
+
value: JSON.stringify(options.integrations),
|
|
39476
40918
|
type: "object"
|
|
39477
40919
|
}
|
|
39478
|
-
}
|
|
40920
|
+
})
|
|
39479
40921
|
});
|
|
39480
40922
|
if (options == null ? void 0 : options.cua) {
|
|
40923
|
+
if (((options == null ? void 0 : options.integrations) || (options == null ? void 0 : options.tools)) && !this.experimental) {
|
|
40924
|
+
throw new Error(
|
|
40925
|
+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options."
|
|
40926
|
+
);
|
|
40927
|
+
}
|
|
39481
40928
|
const modelToUse = (options == null ? void 0 : options.model) || __spreadValues({
|
|
39482
40929
|
modelName: this.modelName
|
|
39483
40930
|
}, this.modelClientOptions);
|
|
@@ -39575,9 +41022,9 @@ Do not ask follow up questions, the user will trust your judgement.`
|
|
|
39575
41022
|
execute: (instructionOrOptions) => __async(this, null, function* () {
|
|
39576
41023
|
return withInstanceLogContext(this.instanceId, () => __async(this, null, function* () {
|
|
39577
41024
|
var _a2, _b2;
|
|
39578
|
-
if ((options == null ? void 0 : options.integrations) && !this.experimental) {
|
|
41025
|
+
if (((options == null ? void 0 : options.integrations) || (options == null ? void 0 : options.tools)) && !this.experimental) {
|
|
39579
41026
|
throw new Error(
|
|
39580
|
-
"MCP integrations are experimental. Enable experimental: true in V3 options."
|
|
41027
|
+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options."
|
|
39581
41028
|
);
|
|
39582
41029
|
}
|
|
39583
41030
|
const tools = (options == null ? void 0 : options.integrations) ? yield resolveTools(options.integrations, options.tools) : (_a2 = options == null ? void 0 : options.tools) != null ? _a2 : {};
|
|
@@ -39882,12 +41329,14 @@ I'm providing ${screenshots.length} screenshots showing the progression of the t
|
|
|
39882
41329
|
};
|
|
39883
41330
|
// Annotate the CommonJS export names for ESM import in node:
|
|
39884
41331
|
0 && (module.exports = {
|
|
41332
|
+
AISdkClient,
|
|
39885
41333
|
AVAILABLE_CUA_MODELS,
|
|
39886
41334
|
AgentProvider,
|
|
39887
41335
|
AgentScreenshotProviderError,
|
|
39888
41336
|
AnnotatedScreenshotText,
|
|
39889
41337
|
BrowserbaseSessionNotFoundError,
|
|
39890
41338
|
CaptchaTimeoutError,
|
|
41339
|
+
ConsoleMessage,
|
|
39891
41340
|
ContentFrameNotFoundError,
|
|
39892
41341
|
CreateChatCompletionResponseError,
|
|
39893
41342
|
ExperimentalApiConflictError,
|
|
@@ -39900,6 +41349,7 @@ I'm providing ${screenshots.length} screenshots showing the progression of the t
|
|
|
39900
41349
|
MCPConnectionError,
|
|
39901
41350
|
MissingEnvironmentVariableError,
|
|
39902
41351
|
MissingLLMConfigurationError,
|
|
41352
|
+
Response,
|
|
39903
41353
|
Stagehand,
|
|
39904
41354
|
StagehandAPIError,
|
|
39905
41355
|
StagehandAPIUnauthorizedError,
|