@browserbasehq/orca 3.0.0-preview.8 → 3.0.1
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 +1492 -52
- 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";
|
|
6299
7447
|
|
|
6300
7448
|
// lib/v3/types/public/sdkErrors.ts
|
|
6301
7449
|
var StagehandError = class extends Error {
|
|
@@ -7251,7 +8399,7 @@ var ActCache = class {
|
|
|
7251
8399
|
}
|
|
7252
8400
|
}
|
|
7253
8401
|
});
|
|
7254
|
-
return yield this.replayCachedActions(entry, page, timeout);
|
|
8402
|
+
return yield this.replayCachedActions(context, entry, page, timeout);
|
|
7255
8403
|
});
|
|
7256
8404
|
}
|
|
7257
8405
|
store(context, result) {
|
|
@@ -7301,7 +8449,7 @@ var ActCache = class {
|
|
|
7301
8449
|
});
|
|
7302
8450
|
return (0, import_crypto.createHash)("sha256").update(payload).digest("hex");
|
|
7303
8451
|
}
|
|
7304
|
-
replayCachedActions(entry, page, timeout) {
|
|
8452
|
+
replayCachedActions(context, entry, page, timeout) {
|
|
7305
8453
|
return __async(this, null, function* () {
|
|
7306
8454
|
const handler = this.getActHandler();
|
|
7307
8455
|
if (!handler) {
|
|
@@ -7337,6 +8485,13 @@ var ActCache = class {
|
|
|
7337
8485
|
});
|
|
7338
8486
|
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"}.`;
|
|
7339
8487
|
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;
|
|
8488
|
+
if (success && actions.length > 0 && this.haveActionsChanged(entry.actions, actions)) {
|
|
8489
|
+
yield this.refreshCacheEntry(context, __spreadProps(__spreadValues({}, entry), {
|
|
8490
|
+
actions,
|
|
8491
|
+
message,
|
|
8492
|
+
actionDescription
|
|
8493
|
+
}));
|
|
8494
|
+
}
|
|
7340
8495
|
return {
|
|
7341
8496
|
success,
|
|
7342
8497
|
message,
|
|
@@ -7347,6 +8502,67 @@ var ActCache = class {
|
|
|
7347
8502
|
return yield this.runWithTimeout(execute, timeout);
|
|
7348
8503
|
});
|
|
7349
8504
|
}
|
|
8505
|
+
haveActionsChanged(original, updated) {
|
|
8506
|
+
var _a, _b, _c, _d;
|
|
8507
|
+
if (original.length !== updated.length) {
|
|
8508
|
+
return true;
|
|
8509
|
+
}
|
|
8510
|
+
for (let i = 0; i < original.length; i += 1) {
|
|
8511
|
+
const orig = original[i];
|
|
8512
|
+
const next = updated[i];
|
|
8513
|
+
if (!next) {
|
|
8514
|
+
return true;
|
|
8515
|
+
}
|
|
8516
|
+
if (orig.selector !== next.selector) {
|
|
8517
|
+
return true;
|
|
8518
|
+
}
|
|
8519
|
+
if (orig.description !== next.description) {
|
|
8520
|
+
return true;
|
|
8521
|
+
}
|
|
8522
|
+
if (((_a = orig.method) != null ? _a : "") !== ((_b = next.method) != null ? _b : "")) {
|
|
8523
|
+
return true;
|
|
8524
|
+
}
|
|
8525
|
+
const origArgs = (_c = orig.arguments) != null ? _c : [];
|
|
8526
|
+
const nextArgs = (_d = next.arguments) != null ? _d : [];
|
|
8527
|
+
if (origArgs.length !== nextArgs.length) {
|
|
8528
|
+
return true;
|
|
8529
|
+
}
|
|
8530
|
+
for (let j = 0; j < origArgs.length; j += 1) {
|
|
8531
|
+
if (origArgs[j] !== nextArgs[j]) {
|
|
8532
|
+
return true;
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
return false;
|
|
8537
|
+
}
|
|
8538
|
+
refreshCacheEntry(context, entry) {
|
|
8539
|
+
return __async(this, null, function* () {
|
|
8540
|
+
const { error, path: path6 } = yield this.storage.writeJson(
|
|
8541
|
+
`${context.cacheKey}.json`,
|
|
8542
|
+
entry
|
|
8543
|
+
);
|
|
8544
|
+
if (error && path6) {
|
|
8545
|
+
this.logger({
|
|
8546
|
+
category: "cache",
|
|
8547
|
+
message: "failed to update act cache entry after self-heal",
|
|
8548
|
+
level: 0,
|
|
8549
|
+
auxiliary: {
|
|
8550
|
+
error: { value: String(error), type: "string" }
|
|
8551
|
+
}
|
|
8552
|
+
});
|
|
8553
|
+
return;
|
|
8554
|
+
}
|
|
8555
|
+
this.logger({
|
|
8556
|
+
category: "cache",
|
|
8557
|
+
message: "act cache entry updated after self-heal",
|
|
8558
|
+
level: 2,
|
|
8559
|
+
auxiliary: {
|
|
8560
|
+
instruction: { value: context.instruction, type: "string" },
|
|
8561
|
+
url: { value: context.pageUrl, type: "string" }
|
|
8562
|
+
}
|
|
8563
|
+
});
|
|
8564
|
+
});
|
|
8565
|
+
}
|
|
7350
8566
|
runWithTimeout(run, timeout) {
|
|
7351
8567
|
return __async(this, null, function* () {
|
|
7352
8568
|
if (!timeout) {
|
|
@@ -9989,25 +11205,37 @@ function evaluateZodSchema(schemaStr, logger) {
|
|
|
9989
11205
|
message: `Failed to evaluate schema: ${(_a = e == null ? void 0 : e.message) != null ? _a : String(e)}`,
|
|
9990
11206
|
level: 0
|
|
9991
11207
|
});
|
|
9992
|
-
|
|
9993
|
-
"Invalid schema: Ensure you're passing a valid Zod schema expression, e.g. z.object({ title: z.string() })"
|
|
9994
|
-
);
|
|
11208
|
+
return import_v314.z.any();
|
|
9995
11209
|
}
|
|
9996
11210
|
}
|
|
9997
11211
|
var createExtractTool = (v3, executionModel, logger) => (0, import_ai10.tool)({
|
|
9998
|
-
description:
|
|
11212
|
+
description: `Extract structured data from the current page based on a provided schema.
|
|
11213
|
+
|
|
11214
|
+
USAGE GUIDELINES:
|
|
11215
|
+
- Keep schemas MINIMAL - only include fields essential for the task
|
|
11216
|
+
- IMPORANT: only use this if explicitly asked for structured output. In most scenarios, you should use the aria tree tool over this.
|
|
11217
|
+
- If you need to extract a link, make sure the type defintion follows the format of z.string().url()
|
|
11218
|
+
EXAMPLES:
|
|
11219
|
+
1. Extract a single value:
|
|
11220
|
+
instruction: "extract the product price"
|
|
11221
|
+
schema: "z.object({ price: z.number()})"
|
|
11222
|
+
|
|
11223
|
+
2. Extract multiple fields:
|
|
11224
|
+
instruction: "extract product name and price"
|
|
11225
|
+
schema: "z.object({ name: z.string(), price: z.number() })"
|
|
11226
|
+
|
|
11227
|
+
3. Extract arrays:
|
|
11228
|
+
instruction: "extract all product names and prices"
|
|
11229
|
+
schema: "z.object({ products: z.array(z.object({ name: z.string(), price: z.number() })) })"`,
|
|
9999
11230
|
inputSchema: import_v314.z.object({
|
|
10000
|
-
instruction: import_v314.z.string()
|
|
10001
|
-
schema: import_v314.z.string().optional().describe("Zod schema as code, e.g. z.object({ title: z.string() })")
|
|
10002
|
-
selector: import_v314.z.string().optional()
|
|
11231
|
+
instruction: import_v314.z.string(),
|
|
11232
|
+
schema: import_v314.z.string().optional().describe("Zod schema as code, e.g. z.object({ title: z.string() })")
|
|
10003
11233
|
}),
|
|
10004
|
-
execute: (_0) => __async(null, [_0], function* ({ instruction, schema
|
|
11234
|
+
execute: (_0) => __async(null, [_0], function* ({ instruction, schema }) {
|
|
10005
11235
|
var _a;
|
|
10006
11236
|
try {
|
|
10007
11237
|
const parsedSchema = schema ? evaluateZodSchema(schema, logger) : void 0;
|
|
10008
|
-
const result = yield v3.extract(instruction, parsedSchema,
|
|
10009
|
-
selector
|
|
10010
|
-
}));
|
|
11238
|
+
const result = yield v3.extract(instruction, parsedSchema, __spreadValues({}, executionModel ? { model: executionModel } : {}));
|
|
10011
11239
|
return { success: true, result };
|
|
10012
11240
|
} catch (error) {
|
|
10013
11241
|
return { success: false, error: (_a = error == null ? void 0 : error.message) != null ? _a : String(error) };
|
|
@@ -10168,12 +11396,12 @@ function mapToolResultToActions({
|
|
|
10168
11396
|
case "fillForm":
|
|
10169
11397
|
return mapFillFormToolResult(toolResult, args, reasoning);
|
|
10170
11398
|
default:
|
|
10171
|
-
return [createStandardAction(toolCallName, args, reasoning)];
|
|
11399
|
+
return [createStandardAction(toolCallName, toolResult, args, reasoning)];
|
|
10172
11400
|
}
|
|
10173
11401
|
}
|
|
10174
11402
|
function mapActToolResult(toolResult, args, reasoning) {
|
|
10175
11403
|
if (!toolResult || typeof toolResult !== "object") {
|
|
10176
|
-
return [createStandardAction("act", args, reasoning)];
|
|
11404
|
+
return [createStandardAction("act", toolResult, args, reasoning)];
|
|
10177
11405
|
}
|
|
10178
11406
|
const result = toolResult;
|
|
10179
11407
|
const output = result.output || result;
|
|
@@ -10189,7 +11417,7 @@ function mapActToolResult(toolResult, args, reasoning) {
|
|
|
10189
11417
|
}
|
|
10190
11418
|
function mapFillFormToolResult(toolResult, args, reasoning) {
|
|
10191
11419
|
if (!toolResult || typeof toolResult !== "object") {
|
|
10192
|
-
return [createStandardAction("fillForm", args, reasoning)];
|
|
11420
|
+
return [createStandardAction("fillForm", toolResult, args, reasoning)];
|
|
10193
11421
|
}
|
|
10194
11422
|
const result = toolResult;
|
|
10195
11423
|
const output = result.output || result;
|
|
@@ -10210,12 +11438,17 @@ function mapFillFormToolResult(toolResult, args, reasoning) {
|
|
|
10210
11438
|
}
|
|
10211
11439
|
return actions;
|
|
10212
11440
|
}
|
|
10213
|
-
function createStandardAction(toolCallName, args, reasoning) {
|
|
10214
|
-
|
|
11441
|
+
function createStandardAction(toolCallName, toolResult, args, reasoning) {
|
|
11442
|
+
const action = __spreadValues({
|
|
10215
11443
|
type: toolCallName,
|
|
10216
11444
|
reasoning,
|
|
10217
11445
|
taskCompleted: toolCallName === "close" ? args == null ? void 0 : args.taskComplete : false
|
|
10218
11446
|
}, args);
|
|
11447
|
+
if (toolCallName !== "ariaTree" && toolResult) {
|
|
11448
|
+
const { output } = toolResult;
|
|
11449
|
+
Object.assign(action, output);
|
|
11450
|
+
}
|
|
11451
|
+
return action;
|
|
10219
11452
|
}
|
|
10220
11453
|
|
|
10221
11454
|
// lib/v3/handlers/v3AgentHandler.ts
|
|
@@ -10360,7 +11593,7 @@ var V3AgentHandler = class {
|
|
|
10360
11593
|
buildSystemPrompt(executionInstruction, systemInstructions) {
|
|
10361
11594
|
if (systemInstructions) {
|
|
10362
11595
|
return `${systemInstructions}
|
|
10363
|
-
Your current goal: ${executionInstruction}`;
|
|
11596
|
+
Your current goal: ${executionInstruction} when the task is complete, use the "close" tool with taskComplete: true`;
|
|
10364
11597
|
}
|
|
10365
11598
|
return `You are a web automation assistant using browser automation tools to accomplish the user's goal.
|
|
10366
11599
|
|
|
@@ -37288,6 +38521,111 @@ var LOG_LEVEL_NAMES = {
|
|
|
37288
38521
|
2: "debug"
|
|
37289
38522
|
};
|
|
37290
38523
|
|
|
38524
|
+
// lib/v3/types/public/page.ts
|
|
38525
|
+
init_consoleMessage();
|
|
38526
|
+
init_response();
|
|
38527
|
+
|
|
38528
|
+
// examples/external_clients/aisdk.ts
|
|
38529
|
+
var import_ai14 = require("ai");
|
|
38530
|
+
var AISdkClient2 = class extends LLMClient {
|
|
38531
|
+
constructor({ model }) {
|
|
38532
|
+
super(model.modelId);
|
|
38533
|
+
this.type = "aisdk";
|
|
38534
|
+
this.model = model;
|
|
38535
|
+
}
|
|
38536
|
+
createChatCompletion(_0) {
|
|
38537
|
+
return __async(this, arguments, function* ({
|
|
38538
|
+
options
|
|
38539
|
+
}) {
|
|
38540
|
+
var _a, _b, _c, _d, _e, _f;
|
|
38541
|
+
const formattedMessages = options.messages.map(
|
|
38542
|
+
(message) => {
|
|
38543
|
+
if (Array.isArray(message.content)) {
|
|
38544
|
+
if (message.role === "system") {
|
|
38545
|
+
const systemMessage = {
|
|
38546
|
+
role: "system",
|
|
38547
|
+
content: message.content.map((c) => "text" in c ? c.text : "").join("\n")
|
|
38548
|
+
};
|
|
38549
|
+
return systemMessage;
|
|
38550
|
+
}
|
|
38551
|
+
const contentParts = message.content.map((content) => {
|
|
38552
|
+
if ("image_url" in content) {
|
|
38553
|
+
const imageContent = {
|
|
38554
|
+
type: "image",
|
|
38555
|
+
image: content.image_url.url
|
|
38556
|
+
};
|
|
38557
|
+
return imageContent;
|
|
38558
|
+
} else {
|
|
38559
|
+
const textContent = {
|
|
38560
|
+
type: "text",
|
|
38561
|
+
text: content.text
|
|
38562
|
+
};
|
|
38563
|
+
return textContent;
|
|
38564
|
+
}
|
|
38565
|
+
});
|
|
38566
|
+
if (message.role === "user") {
|
|
38567
|
+
const userMessage = {
|
|
38568
|
+
role: "user",
|
|
38569
|
+
content: contentParts
|
|
38570
|
+
};
|
|
38571
|
+
return userMessage;
|
|
38572
|
+
} else {
|
|
38573
|
+
const textOnlyParts = contentParts.map((part) => ({
|
|
38574
|
+
type: "text",
|
|
38575
|
+
text: part.type === "image" ? "[Image]" : part.text
|
|
38576
|
+
}));
|
|
38577
|
+
const assistantMessage = {
|
|
38578
|
+
role: "assistant",
|
|
38579
|
+
content: textOnlyParts
|
|
38580
|
+
};
|
|
38581
|
+
return assistantMessage;
|
|
38582
|
+
}
|
|
38583
|
+
}
|
|
38584
|
+
return {
|
|
38585
|
+
role: message.role,
|
|
38586
|
+
content: message.content
|
|
38587
|
+
};
|
|
38588
|
+
}
|
|
38589
|
+
);
|
|
38590
|
+
if (options.response_model) {
|
|
38591
|
+
const response2 = yield (0, import_ai14.generateObject)({
|
|
38592
|
+
model: this.model,
|
|
38593
|
+
messages: formattedMessages,
|
|
38594
|
+
schema: options.response_model.schema
|
|
38595
|
+
});
|
|
38596
|
+
return {
|
|
38597
|
+
data: response2.object,
|
|
38598
|
+
usage: {
|
|
38599
|
+
prompt_tokens: (_a = response2.usage.inputTokens) != null ? _a : 0,
|
|
38600
|
+
completion_tokens: (_b = response2.usage.outputTokens) != null ? _b : 0,
|
|
38601
|
+
total_tokens: (_c = response2.usage.totalTokens) != null ? _c : 0
|
|
38602
|
+
}
|
|
38603
|
+
};
|
|
38604
|
+
}
|
|
38605
|
+
const tools = {};
|
|
38606
|
+
for (const rawTool of options.tools) {
|
|
38607
|
+
tools[rawTool.name] = {
|
|
38608
|
+
description: rawTool.description,
|
|
38609
|
+
inputSchema: rawTool.parameters
|
|
38610
|
+
};
|
|
38611
|
+
}
|
|
38612
|
+
const response = yield (0, import_ai14.generateText)({
|
|
38613
|
+
model: this.model,
|
|
38614
|
+
messages: formattedMessages,
|
|
38615
|
+
tools
|
|
38616
|
+
});
|
|
38617
|
+
return {
|
|
38618
|
+
data: response.text,
|
|
38619
|
+
usage: {
|
|
38620
|
+
prompt_tokens: (_d = response.usage.inputTokens) != null ? _d : 0,
|
|
38621
|
+
completion_tokens: (_e = response.usage.outputTokens) != null ? _e : 0,
|
|
38622
|
+
total_tokens: (_f = response.usage.totalTokens) != null ? _f : 0
|
|
38623
|
+
}
|
|
38624
|
+
};
|
|
38625
|
+
});
|
|
38626
|
+
}
|
|
38627
|
+
};
|
|
38628
|
+
|
|
37291
38629
|
// lib/v3/understudy/context.ts
|
|
37292
38630
|
init_logger();
|
|
37293
38631
|
|
|
@@ -38295,6 +39633,85 @@ var StagehandAPIClient = class {
|
|
|
38295
39633
|
return response;
|
|
38296
39634
|
});
|
|
38297
39635
|
}
|
|
39636
|
+
getReplayMetrics() {
|
|
39637
|
+
return __async(this, null, function* () {
|
|
39638
|
+
if (!this.sessionId) {
|
|
39639
|
+
throw new StagehandAPIError("sessionId is required to fetch metrics.");
|
|
39640
|
+
}
|
|
39641
|
+
const response = yield this.request(`/sessions/${this.sessionId}/replay`, {
|
|
39642
|
+
method: "GET"
|
|
39643
|
+
});
|
|
39644
|
+
if (response.status !== 200) {
|
|
39645
|
+
const errorText = yield response.text();
|
|
39646
|
+
this.logger({
|
|
39647
|
+
category: "api",
|
|
39648
|
+
message: `Failed to fetch metrics. Status ${response.status}: ${errorText}`,
|
|
39649
|
+
level: 0
|
|
39650
|
+
});
|
|
39651
|
+
throw new StagehandHttpError(
|
|
39652
|
+
`Failed to fetch metrics with status ${response.status}: ${errorText}`
|
|
39653
|
+
);
|
|
39654
|
+
}
|
|
39655
|
+
const data = yield response.json();
|
|
39656
|
+
if (!data.success) {
|
|
39657
|
+
throw new StagehandAPIError(
|
|
39658
|
+
`Failed to fetch metrics: ${data.error || "Unknown error"}`
|
|
39659
|
+
);
|
|
39660
|
+
}
|
|
39661
|
+
const apiData = data.data || {};
|
|
39662
|
+
const metrics = {
|
|
39663
|
+
actPromptTokens: 0,
|
|
39664
|
+
actCompletionTokens: 0,
|
|
39665
|
+
actInferenceTimeMs: 0,
|
|
39666
|
+
extractPromptTokens: 0,
|
|
39667
|
+
extractCompletionTokens: 0,
|
|
39668
|
+
extractInferenceTimeMs: 0,
|
|
39669
|
+
observePromptTokens: 0,
|
|
39670
|
+
observeCompletionTokens: 0,
|
|
39671
|
+
observeInferenceTimeMs: 0,
|
|
39672
|
+
agentPromptTokens: 0,
|
|
39673
|
+
agentCompletionTokens: 0,
|
|
39674
|
+
agentInferenceTimeMs: 0,
|
|
39675
|
+
totalPromptTokens: 0,
|
|
39676
|
+
totalCompletionTokens: 0,
|
|
39677
|
+
totalInferenceTimeMs: 0
|
|
39678
|
+
};
|
|
39679
|
+
const pages = apiData.pages || [];
|
|
39680
|
+
for (const page of pages) {
|
|
39681
|
+
const actions = page.actions || [];
|
|
39682
|
+
for (const action of actions) {
|
|
39683
|
+
const method = (action.method || "").toLowerCase();
|
|
39684
|
+
const tokenUsage = action.tokenUsage;
|
|
39685
|
+
if (tokenUsage) {
|
|
39686
|
+
const inputTokens = tokenUsage.inputTokens || 0;
|
|
39687
|
+
const outputTokens = tokenUsage.outputTokens || 0;
|
|
39688
|
+
const timeMs = tokenUsage.timeMs || 0;
|
|
39689
|
+
if (method === "act") {
|
|
39690
|
+
metrics.actPromptTokens += inputTokens;
|
|
39691
|
+
metrics.actCompletionTokens += outputTokens;
|
|
39692
|
+
metrics.actInferenceTimeMs += timeMs;
|
|
39693
|
+
} else if (method === "extract") {
|
|
39694
|
+
metrics.extractPromptTokens += inputTokens;
|
|
39695
|
+
metrics.extractCompletionTokens += outputTokens;
|
|
39696
|
+
metrics.extractInferenceTimeMs += timeMs;
|
|
39697
|
+
} else if (method === "observe") {
|
|
39698
|
+
metrics.observePromptTokens += inputTokens;
|
|
39699
|
+
metrics.observeCompletionTokens += outputTokens;
|
|
39700
|
+
metrics.observeInferenceTimeMs += timeMs;
|
|
39701
|
+
} else if (method === "agent") {
|
|
39702
|
+
metrics.agentPromptTokens += inputTokens;
|
|
39703
|
+
metrics.agentCompletionTokens += outputTokens;
|
|
39704
|
+
metrics.agentInferenceTimeMs += timeMs;
|
|
39705
|
+
}
|
|
39706
|
+
metrics.totalPromptTokens += inputTokens;
|
|
39707
|
+
metrics.totalCompletionTokens += outputTokens;
|
|
39708
|
+
metrics.totalInferenceTimeMs += timeMs;
|
|
39709
|
+
}
|
|
39710
|
+
}
|
|
39711
|
+
}
|
|
39712
|
+
return metrics;
|
|
39713
|
+
});
|
|
39714
|
+
}
|
|
38298
39715
|
execute(_0) {
|
|
38299
39716
|
return __async(this, arguments, function* ({
|
|
38300
39717
|
method,
|
|
@@ -38501,11 +39918,12 @@ var _V3 = class _V3 {
|
|
|
38501
39918
|
this.logInferenceToFile = (_f = opts.logInferenceToFile) != null ? _f : false;
|
|
38502
39919
|
this.llmProvider = new LLMProvider(this.logger);
|
|
38503
39920
|
this.domSettleTimeoutMs = opts.domSettleTimeout;
|
|
38504
|
-
this.disableAPI = (_g = opts.disableAPI) != null ? _g :
|
|
39921
|
+
this.disableAPI = (_g = opts.disableAPI) != null ? _g : false;
|
|
38505
39922
|
const baseClientOptions = clientOptions ? __spreadValues({}, clientOptions) : {};
|
|
38506
39923
|
if (opts.llmClient) {
|
|
38507
39924
|
this.llmClient = opts.llmClient;
|
|
38508
39925
|
this.modelClientOptions = baseClientOptions;
|
|
39926
|
+
this.disableAPI = true;
|
|
38509
39927
|
} else {
|
|
38510
39928
|
let apiKey = baseClientOptions.apiKey;
|
|
38511
39929
|
if (!apiKey) {
|
|
@@ -38556,11 +39974,24 @@ var _V3 = class _V3 {
|
|
|
38556
39974
|
this.opts = opts;
|
|
38557
39975
|
_V3._instances.add(this);
|
|
38558
39976
|
}
|
|
39977
|
+
get browserbaseSessionID() {
|
|
39978
|
+
return this.browserbaseSessionId;
|
|
39979
|
+
}
|
|
38559
39980
|
/**
|
|
38560
39981
|
* Async property for metrics so callers can `await v3.metrics`.
|
|
38561
|
-
*
|
|
39982
|
+
* When using API mode, fetches metrics from the API. Otherwise returns local metrics.
|
|
38562
39983
|
*/
|
|
38563
39984
|
get metrics() {
|
|
39985
|
+
if (this.apiClient) {
|
|
39986
|
+
return this.apiClient.getReplayMetrics().catch((error) => {
|
|
39987
|
+
this.logger({
|
|
39988
|
+
category: "metrics",
|
|
39989
|
+
message: `Failed to fetch metrics from API: ${error}`,
|
|
39990
|
+
level: 0
|
|
39991
|
+
});
|
|
39992
|
+
return this.stagehandMetrics;
|
|
39993
|
+
});
|
|
39994
|
+
}
|
|
38564
39995
|
return Promise.resolve(this.stagehandMetrics);
|
|
38565
39996
|
}
|
|
38566
39997
|
resolveLlmClient(model) {
|
|
@@ -38835,8 +40266,8 @@ var _V3 = class _V3 {
|
|
|
38835
40266
|
let createdTemp = false;
|
|
38836
40267
|
if (!userDataDir) {
|
|
38837
40268
|
const base = import_path5.default.join(import_os2.default.tmpdir(), "stagehand-v3");
|
|
38838
|
-
|
|
38839
|
-
userDataDir =
|
|
40269
|
+
import_fs6.default.mkdirSync(base, { recursive: true });
|
|
40270
|
+
userDataDir = import_fs6.default.mkdtempSync(import_path5.default.join(base, "profile-"));
|
|
38840
40271
|
createdTemp = true;
|
|
38841
40272
|
}
|
|
38842
40273
|
const defaults2 = [
|
|
@@ -39265,7 +40696,7 @@ var _V3 = class _V3 {
|
|
|
39265
40696
|
}
|
|
39266
40697
|
try {
|
|
39267
40698
|
if (this.state.createdTempProfile && !this.state.preserveUserDataDir && this.state.userDataDir) {
|
|
39268
|
-
|
|
40699
|
+
import_fs6.default.rmSync(this.state.userDataDir, { recursive: true, force: true });
|
|
39269
40700
|
}
|
|
39270
40701
|
} catch (e) {
|
|
39271
40702
|
}
|
|
@@ -39405,23 +40836,29 @@ var _V3 = class _V3 {
|
|
|
39405
40836
|
* Mirrors the v2 Stagehand.agent() tool mode (no CUA provider here).
|
|
39406
40837
|
*/
|
|
39407
40838
|
agent(options) {
|
|
39408
|
-
var _a, _b
|
|
40839
|
+
var _a, _b;
|
|
39409
40840
|
this.logger({
|
|
39410
40841
|
category: "agent",
|
|
39411
|
-
message:
|
|
40842
|
+
message: `Creating v3 agent instance with options: ${JSON.stringify(options)}`,
|
|
39412
40843
|
level: 1,
|
|
39413
|
-
auxiliary: {
|
|
40844
|
+
auxiliary: __spreadValues({
|
|
39414
40845
|
cua: { value: (options == null ? void 0 : options.cua) ? "true" : "false", type: "boolean" },
|
|
39415
|
-
model: typeof (options == null ? void 0 : options.model) === "string" ? { value: options.model, type: "string" } : { value: options.model.modelName, type: "string" },
|
|
40846
|
+
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" },
|
|
39416
40847
|
systemPrompt: { value: (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : "", type: "string" },
|
|
39417
|
-
tools: { value: JSON.stringify((_b = options == null ? void 0 : options.tools) != null ? _b : {}), type: "object" }
|
|
40848
|
+
tools: { value: JSON.stringify((_b = options == null ? void 0 : options.tools) != null ? _b : {}), type: "object" }
|
|
40849
|
+
}, (options == null ? void 0 : options.integrations) && {
|
|
39418
40850
|
integrations: {
|
|
39419
|
-
value: JSON.stringify(
|
|
40851
|
+
value: JSON.stringify(options.integrations),
|
|
39420
40852
|
type: "object"
|
|
39421
40853
|
}
|
|
39422
|
-
}
|
|
40854
|
+
})
|
|
39423
40855
|
});
|
|
39424
40856
|
if (options == null ? void 0 : options.cua) {
|
|
40857
|
+
if (((options == null ? void 0 : options.integrations) || (options == null ? void 0 : options.tools)) && !this.experimental) {
|
|
40858
|
+
throw new Error(
|
|
40859
|
+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options."
|
|
40860
|
+
);
|
|
40861
|
+
}
|
|
39425
40862
|
const modelToUse = (options == null ? void 0 : options.model) || __spreadValues({
|
|
39426
40863
|
modelName: this.modelName
|
|
39427
40864
|
}, this.modelClientOptions);
|
|
@@ -39519,9 +40956,9 @@ Do not ask follow up questions, the user will trust your judgement.`
|
|
|
39519
40956
|
execute: (instructionOrOptions) => __async(this, null, function* () {
|
|
39520
40957
|
return withInstanceLogContext(this.instanceId, () => __async(this, null, function* () {
|
|
39521
40958
|
var _a2, _b2;
|
|
39522
|
-
if ((options == null ? void 0 : options.integrations) && !this.experimental) {
|
|
40959
|
+
if (((options == null ? void 0 : options.integrations) || (options == null ? void 0 : options.tools)) && !this.experimental) {
|
|
39523
40960
|
throw new Error(
|
|
39524
|
-
"MCP integrations are experimental. Enable experimental: true in V3 options."
|
|
40961
|
+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options."
|
|
39525
40962
|
);
|
|
39526
40963
|
}
|
|
39527
40964
|
const tools = (options == null ? void 0 : options.integrations) ? yield resolveTools(options.integrations, options.tools) : (_a2 = options == null ? void 0 : options.tools) != null ? _a2 : {};
|
|
@@ -39826,12 +41263,14 @@ I'm providing ${screenshots.length} screenshots showing the progression of the t
|
|
|
39826
41263
|
};
|
|
39827
41264
|
// Annotate the CommonJS export names for ESM import in node:
|
|
39828
41265
|
0 && (module.exports = {
|
|
41266
|
+
AISdkClient,
|
|
39829
41267
|
AVAILABLE_CUA_MODELS,
|
|
39830
41268
|
AgentProvider,
|
|
39831
41269
|
AgentScreenshotProviderError,
|
|
39832
41270
|
AnnotatedScreenshotText,
|
|
39833
41271
|
BrowserbaseSessionNotFoundError,
|
|
39834
41272
|
CaptchaTimeoutError,
|
|
41273
|
+
ConsoleMessage,
|
|
39835
41274
|
ContentFrameNotFoundError,
|
|
39836
41275
|
CreateChatCompletionResponseError,
|
|
39837
41276
|
ExperimentalApiConflictError,
|
|
@@ -39844,6 +41283,7 @@ I'm providing ${screenshots.length} screenshots showing the progression of the t
|
|
|
39844
41283
|
MCPConnectionError,
|
|
39845
41284
|
MissingEnvironmentVariableError,
|
|
39846
41285
|
MissingLLMConfigurationError,
|
|
41286
|
+
Response,
|
|
39847
41287
|
Stagehand,
|
|
39848
41288
|
StagehandAPIError,
|
|
39849
41289
|
StagehandAPIUnauthorizedError,
|