browser-pilot 0.0.13 → 0.0.14
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 +59 -3
- package/dist/actions.cjs +418 -14
- package/dist/actions.d.cts +13 -3
- package/dist/actions.d.ts +13 -3
- package/dist/actions.mjs +1 -1
- package/dist/browser-LZTEHUDI.mjs +9 -0
- package/dist/browser.cjs +600 -20
- package/dist/browser.d.cts +12 -3
- package/dist/browser.d.ts +12 -3
- package/dist/browser.mjs +3 -3
- package/dist/cdp.cjs +31 -2
- package/dist/cdp.d.cts +1 -1
- package/dist/cdp.d.ts +1 -1
- package/dist/cdp.mjs +3 -1
- package/dist/chunk-7NDR6V7S.mjs +7788 -0
- package/dist/{chunk-VDAMDOS6.mjs → chunk-IN5HPAPB.mjs} +147 -7
- package/dist/{chunk-HP6R3W32.mjs → chunk-KIFB526Y.mjs} +44 -2
- package/dist/chunk-LUGLEMVR.mjs +11 -0
- package/dist/chunk-SPSZZH22.mjs +308 -0
- package/dist/{chunk-A2ZRAEO3.mjs → chunk-XMJABKCF.mjs} +408 -14
- package/dist/cli.mjs +1063 -7746
- package/dist/client-3AFV2IAF.mjs +10 -0
- package/dist/{client-DRqxBdHv.d.ts → client-Ck2nQksT.d.cts} +8 -6
- package/dist/{client-DRqxBdHv.d.cts → client-Ck2nQksT.d.ts} +8 -6
- package/dist/index.cjs +600 -20
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/transport-WHEBAZUP.mjs +83 -0
- package/dist/{types-CzgQjai9.d.ts → types-BSoh5v1Y.d.cts} +62 -2
- package/dist/{types-BXMGFtnB.d.cts → types-CjT0vClo.d.ts} +62 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -62,6 +72,230 @@ __export(src_exports, {
|
|
|
62
72
|
});
|
|
63
73
|
module.exports = __toCommonJS(src_exports);
|
|
64
74
|
|
|
75
|
+
// src/actions/executor.ts
|
|
76
|
+
var fs = __toESM(require("fs"), 1);
|
|
77
|
+
var import_node_path = require("path");
|
|
78
|
+
|
|
79
|
+
// src/recording/redaction.ts
|
|
80
|
+
var REDACTED_VALUE = "[REDACTED]";
|
|
81
|
+
var SENSITIVE_AUTOCOMPLETE_TOKENS = [
|
|
82
|
+
"current-password",
|
|
83
|
+
"new-password",
|
|
84
|
+
"one-time-code",
|
|
85
|
+
"cc-number",
|
|
86
|
+
"cc-csc",
|
|
87
|
+
"cc-exp",
|
|
88
|
+
"cc-exp-month",
|
|
89
|
+
"cc-exp-year"
|
|
90
|
+
];
|
|
91
|
+
function autocompleteTokens(autocomplete) {
|
|
92
|
+
if (!autocomplete) return [];
|
|
93
|
+
return autocomplete.toLowerCase().split(/\s+/).map((token) => token.trim()).filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
function isSensitiveFieldMetadata(metadata) {
|
|
96
|
+
if (!metadata) return false;
|
|
97
|
+
if (metadata.sensitiveValue) return true;
|
|
98
|
+
const inputType = metadata.inputType?.toLowerCase();
|
|
99
|
+
if (inputType === "password" || inputType === "hidden") {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const sensitiveAutocompleteTokens = new Set(SENSITIVE_AUTOCOMPLETE_TOKENS);
|
|
103
|
+
return autocompleteTokens(metadata.autocomplete).some(
|
|
104
|
+
(token) => sensitiveAutocompleteTokens.has(token)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function redactValueForRecording(value, metadata) {
|
|
108
|
+
if (value === void 0) return void 0;
|
|
109
|
+
return isSensitiveFieldMetadata(metadata) ? REDACTED_VALUE : value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/browser/action-highlight.ts
|
|
113
|
+
var HIGHLIGHT_STYLES = {
|
|
114
|
+
click: { outline: "3px solid rgba(229,57,53,0.8)", badge: "#e53935", marker: "crosshair" },
|
|
115
|
+
fill: { outline: "3px solid rgba(33,150,243,0.8)", badge: "#2196f3" },
|
|
116
|
+
type: { outline: "3px solid rgba(33,150,243,0.6)", badge: "#2196f3" },
|
|
117
|
+
select: { outline: "3px solid rgba(156,39,176,0.8)", badge: "#9c27b0" },
|
|
118
|
+
hover: { outline: "2px dashed rgba(158,158,158,0.5)", badge: "#9e9e9e" },
|
|
119
|
+
scroll: { outline: "none", badge: "#607d8b", marker: "arrow" },
|
|
120
|
+
navigate: { outline: "none", badge: "#4caf50" },
|
|
121
|
+
submit: { outline: "3px solid rgba(255,152,0,0.8)", badge: "#ff9800" },
|
|
122
|
+
"assert-pass": { outline: "3px solid rgba(76,175,80,0.8)", badge: "#4caf50", marker: "check" },
|
|
123
|
+
"assert-fail": { outline: "3px solid rgba(244,67,54,0.8)", badge: "#f44336", marker: "cross" },
|
|
124
|
+
evaluate: { outline: "none", badge: "#ffc107" },
|
|
125
|
+
focus: { outline: "3px dotted rgba(33,150,243,0.6)", badge: "#2196f3" }
|
|
126
|
+
};
|
|
127
|
+
function buildHighlightScript(options) {
|
|
128
|
+
const style = HIGHLIGHT_STYLES[options.kind];
|
|
129
|
+
const label = options.label ? options.label.slice(0, 80) : void 0;
|
|
130
|
+
const escapedLabel = label ? label.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n") : "";
|
|
131
|
+
return `(function() {
|
|
132
|
+
// Remove any existing highlight
|
|
133
|
+
var existing = document.getElementById('__bp-action-highlight');
|
|
134
|
+
if (existing) existing.remove();
|
|
135
|
+
|
|
136
|
+
var container = document.createElement('div');
|
|
137
|
+
container.id = '__bp-action-highlight';
|
|
138
|
+
container.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:99999;';
|
|
139
|
+
|
|
140
|
+
${options.bbox ? `
|
|
141
|
+
// Element outline
|
|
142
|
+
var outline = document.createElement('div');
|
|
143
|
+
outline.style.cssText = 'position:fixed;' +
|
|
144
|
+
'left:${options.bbox.x}px;top:${options.bbox.y}px;' +
|
|
145
|
+
'width:${options.bbox.width}px;height:${options.bbox.height}px;' +
|
|
146
|
+
'${style.outline !== "none" ? `outline:${style.outline};outline-offset:-1px;` : ""}' +
|
|
147
|
+
'pointer-events:none;box-sizing:border-box;';
|
|
148
|
+
container.appendChild(outline);
|
|
149
|
+
` : ""}
|
|
150
|
+
|
|
151
|
+
${options.point && style.marker === "crosshair" ? `
|
|
152
|
+
// Crosshair at click point
|
|
153
|
+
var hLine = document.createElement('div');
|
|
154
|
+
hLine.style.cssText = 'position:fixed;left:${options.point.x - 12}px;top:${options.point.y}px;' +
|
|
155
|
+
'width:24px;height:2px;background:${style.badge};pointer-events:none;';
|
|
156
|
+
var vLine = document.createElement('div');
|
|
157
|
+
vLine.style.cssText = 'position:fixed;left:${options.point.x}px;top:${options.point.y - 12}px;' +
|
|
158
|
+
'width:2px;height:24px;background:${style.badge};pointer-events:none;';
|
|
159
|
+
// Dot at center
|
|
160
|
+
var dot = document.createElement('div');
|
|
161
|
+
dot.style.cssText = 'position:fixed;left:${options.point.x - 4}px;top:${options.point.y - 4}px;' +
|
|
162
|
+
'width:8px;height:8px;border-radius:50%;background:${style.badge};pointer-events:none;';
|
|
163
|
+
container.appendChild(hLine);
|
|
164
|
+
container.appendChild(vLine);
|
|
165
|
+
container.appendChild(dot);
|
|
166
|
+
` : ""}
|
|
167
|
+
|
|
168
|
+
${label ? `
|
|
169
|
+
// Badge with label
|
|
170
|
+
var badge = document.createElement('div');
|
|
171
|
+
badge.style.cssText = 'position:fixed;' +
|
|
172
|
+
${options.bbox ? `'left:${options.bbox.x}px;top:${Math.max(0, options.bbox.y - 28)}px;'` : options.kind === "navigate" ? "'left:50%;top:8px;transform:translateX(-50%);'" : "'right:8px;top:8px;'"} +
|
|
173
|
+
'background:${style.badge};color:white;padding:4px 8px;' +
|
|
174
|
+
'font-family:monospace;font-size:12px;font-weight:bold;' +
|
|
175
|
+
'border-radius:3px;white-space:nowrap;max-width:400px;overflow:hidden;text-overflow:ellipsis;' +
|
|
176
|
+
'pointer-events:none;';
|
|
177
|
+
badge.textContent = '${escapedLabel}';
|
|
178
|
+
container.appendChild(badge);
|
|
179
|
+
` : ""}
|
|
180
|
+
|
|
181
|
+
${style.marker === "check" && options.bbox ? `
|
|
182
|
+
// Checkmark
|
|
183
|
+
var check = document.createElement('div');
|
|
184
|
+
check.style.cssText = 'position:fixed;left:${options.bbox.x + options.bbox.width / 2 - 10}px;' +
|
|
185
|
+
'top:${options.bbox.y + options.bbox.height / 2 - 10}px;' +
|
|
186
|
+
'width:20px;height:20px;font-size:18px;color:${style.badge};pointer-events:none;text-align:center;line-height:20px;';
|
|
187
|
+
check.textContent = '\\u2713';
|
|
188
|
+
container.appendChild(check);
|
|
189
|
+
` : ""}
|
|
190
|
+
|
|
191
|
+
${style.marker === "cross" && options.bbox ? `
|
|
192
|
+
// Cross mark
|
|
193
|
+
var cross = document.createElement('div');
|
|
194
|
+
cross.style.cssText = 'position:fixed;left:${options.bbox.x + options.bbox.width / 2 - 10}px;' +
|
|
195
|
+
'top:${options.bbox.y + options.bbox.height / 2 - 10}px;' +
|
|
196
|
+
'width:20px;height:20px;font-size:18px;color:${style.badge};pointer-events:none;text-align:center;line-height:20px;font-weight:bold;';
|
|
197
|
+
cross.textContent = '\\u2717';
|
|
198
|
+
container.appendChild(cross);
|
|
199
|
+
` : ""}
|
|
200
|
+
|
|
201
|
+
document.body.appendChild(container);
|
|
202
|
+
window.__bpRemoveActionHighlight = function() {
|
|
203
|
+
var el = document.getElementById('__bp-action-highlight');
|
|
204
|
+
if (el) el.remove();
|
|
205
|
+
delete window.__bpRemoveActionHighlight;
|
|
206
|
+
};
|
|
207
|
+
})();`;
|
|
208
|
+
}
|
|
209
|
+
async function injectActionHighlight(page, options) {
|
|
210
|
+
try {
|
|
211
|
+
await page.evaluate(buildHighlightScript(options));
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function removeActionHighlight(page) {
|
|
216
|
+
try {
|
|
217
|
+
await page.evaluate(`(function() {
|
|
218
|
+
if (window.__bpRemoveActionHighlight) {
|
|
219
|
+
window.__bpRemoveActionHighlight();
|
|
220
|
+
}
|
|
221
|
+
})()`);
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function stepToHighlightKind(step) {
|
|
226
|
+
switch (step.action) {
|
|
227
|
+
case "click":
|
|
228
|
+
return "click";
|
|
229
|
+
case "fill":
|
|
230
|
+
return "fill";
|
|
231
|
+
case "type":
|
|
232
|
+
return "type";
|
|
233
|
+
case "select":
|
|
234
|
+
return "select";
|
|
235
|
+
case "hover":
|
|
236
|
+
return "hover";
|
|
237
|
+
case "scroll":
|
|
238
|
+
return "scroll";
|
|
239
|
+
case "goto":
|
|
240
|
+
return "navigate";
|
|
241
|
+
case "submit":
|
|
242
|
+
return "submit";
|
|
243
|
+
case "focus":
|
|
244
|
+
return "focus";
|
|
245
|
+
case "evaluate":
|
|
246
|
+
case "press":
|
|
247
|
+
case "shortcut":
|
|
248
|
+
return "evaluate";
|
|
249
|
+
case "assertVisible":
|
|
250
|
+
case "assertExists":
|
|
251
|
+
case "assertText":
|
|
252
|
+
case "assertUrl":
|
|
253
|
+
case "assertValue":
|
|
254
|
+
return step.success ? "assert-pass" : "assert-fail";
|
|
255
|
+
// Observation-only actions — no highlight
|
|
256
|
+
case "wait":
|
|
257
|
+
case "snapshot":
|
|
258
|
+
case "forms":
|
|
259
|
+
case "text":
|
|
260
|
+
case "screenshot":
|
|
261
|
+
case "newTab":
|
|
262
|
+
case "closeTab":
|
|
263
|
+
case "switchFrame":
|
|
264
|
+
case "switchToMain":
|
|
265
|
+
return null;
|
|
266
|
+
default:
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function getHighlightLabel(step, result, targetMetadata) {
|
|
271
|
+
switch (step.action) {
|
|
272
|
+
case "fill":
|
|
273
|
+
case "type":
|
|
274
|
+
return typeof step.value === "string" ? `"${redactValueForRecording(step.value, targetMetadata)}"` : void 0;
|
|
275
|
+
case "select":
|
|
276
|
+
return redactValueForRecording(
|
|
277
|
+
typeof step.value === "string" ? step.value : void 0,
|
|
278
|
+
targetMetadata
|
|
279
|
+
);
|
|
280
|
+
case "goto":
|
|
281
|
+
return step.url;
|
|
282
|
+
case "evaluate":
|
|
283
|
+
return "JS";
|
|
284
|
+
case "press":
|
|
285
|
+
return step.key;
|
|
286
|
+
case "shortcut":
|
|
287
|
+
return step.combo;
|
|
288
|
+
case "assertText":
|
|
289
|
+
case "assertUrl":
|
|
290
|
+
case "assertValue":
|
|
291
|
+
case "assertVisible":
|
|
292
|
+
case "assertExists":
|
|
293
|
+
return result.success ? "\u2713" : "\u2717";
|
|
294
|
+
default:
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
65
299
|
// src/browser/actionability.ts
|
|
66
300
|
var ActionabilityError = class extends Error {
|
|
67
301
|
failureType;
|
|
@@ -688,6 +922,13 @@ var CDPError = class extends Error {
|
|
|
688
922
|
|
|
689
923
|
// src/actions/executor.ts
|
|
690
924
|
var DEFAULT_TIMEOUT = 3e4;
|
|
925
|
+
var DEFAULT_RECORDING_SKIP_ACTIONS = [
|
|
926
|
+
"wait",
|
|
927
|
+
"snapshot",
|
|
928
|
+
"forms",
|
|
929
|
+
"text",
|
|
930
|
+
"screenshot"
|
|
931
|
+
];
|
|
691
932
|
function classifyFailure(error) {
|
|
692
933
|
if (error instanceof ElementNotFoundError) {
|
|
693
934
|
return { reason: "missing" };
|
|
@@ -767,6 +1008,9 @@ var BatchExecutor = class {
|
|
|
767
1008
|
const { timeout = DEFAULT_TIMEOUT, onFail = "stop" } = options;
|
|
768
1009
|
const results = [];
|
|
769
1010
|
const startTime = Date.now();
|
|
1011
|
+
const recording = options.record ? this.createRecordingContext(options.record) : null;
|
|
1012
|
+
const startUrl = recording ? await this.getPageUrlSafe() : "";
|
|
1013
|
+
let stoppedAtIndex;
|
|
770
1014
|
for (let i = 0; i < steps.length; i++) {
|
|
771
1015
|
const step = steps[i];
|
|
772
1016
|
const stepStart = Date.now();
|
|
@@ -779,8 +1023,9 @@ var BatchExecutor = class {
|
|
|
779
1023
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
780
1024
|
}
|
|
781
1025
|
try {
|
|
1026
|
+
this.page.resetLastActionPosition();
|
|
782
1027
|
const result = await this.executeStep(step, timeout);
|
|
783
|
-
|
|
1028
|
+
const stepResult = {
|
|
784
1029
|
index: i,
|
|
785
1030
|
action: step.action,
|
|
786
1031
|
selector: step.selector,
|
|
@@ -788,8 +1033,15 @@ var BatchExecutor = class {
|
|
|
788
1033
|
success: true,
|
|
789
1034
|
durationMs: Date.now() - stepStart,
|
|
790
1035
|
result: result.value,
|
|
791
|
-
text: result.text
|
|
792
|
-
|
|
1036
|
+
text: result.text,
|
|
1037
|
+
timestamp: Date.now(),
|
|
1038
|
+
coordinates: this.page.getLastActionCoordinates() ?? void 0,
|
|
1039
|
+
boundingBox: this.page.getLastActionBoundingBox() ?? void 0
|
|
1040
|
+
};
|
|
1041
|
+
if (recording && !recording.skipActions.has(step.action)) {
|
|
1042
|
+
await this.captureRecordingFrame(step, stepResult, recording);
|
|
1043
|
+
}
|
|
1044
|
+
results.push(stepResult);
|
|
793
1045
|
succeeded = true;
|
|
794
1046
|
break;
|
|
795
1047
|
} catch (error) {
|
|
@@ -810,7 +1062,7 @@ var BatchExecutor = class {
|
|
|
810
1062
|
} catch {
|
|
811
1063
|
}
|
|
812
1064
|
}
|
|
813
|
-
|
|
1065
|
+
const failedResult = {
|
|
814
1066
|
index: i,
|
|
815
1067
|
action: step.action,
|
|
816
1068
|
selector: step.selector,
|
|
@@ -820,25 +1072,177 @@ var BatchExecutor = class {
|
|
|
820
1072
|
hints,
|
|
821
1073
|
failureReason: reason,
|
|
822
1074
|
coveringElement,
|
|
823
|
-
suggestion: getSuggestion(reason)
|
|
824
|
-
|
|
1075
|
+
suggestion: getSuggestion(reason),
|
|
1076
|
+
timestamp: Date.now()
|
|
1077
|
+
};
|
|
1078
|
+
if (recording && !recording.skipActions.has(step.action)) {
|
|
1079
|
+
await this.captureRecordingFrame(step, failedResult, recording);
|
|
1080
|
+
}
|
|
1081
|
+
results.push(failedResult);
|
|
825
1082
|
if (onFail === "stop" && !step.optional) {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
stoppedAtIndex: i,
|
|
829
|
-
steps: results,
|
|
830
|
-
totalDurationMs: Date.now() - startTime
|
|
831
|
-
};
|
|
1083
|
+
stoppedAtIndex = i;
|
|
1084
|
+
break;
|
|
832
1085
|
}
|
|
833
1086
|
}
|
|
834
1087
|
}
|
|
835
|
-
const
|
|
1088
|
+
const totalDurationMs = Date.now() - startTime;
|
|
1089
|
+
const allSuccess = stoppedAtIndex === void 0 && results.every((result) => result.success || steps[result.index]?.optional);
|
|
1090
|
+
let recordingManifest;
|
|
1091
|
+
if (recording) {
|
|
1092
|
+
recordingManifest = await this.writeRecordingManifest(
|
|
1093
|
+
recording,
|
|
1094
|
+
startTime,
|
|
1095
|
+
startUrl,
|
|
1096
|
+
allSuccess
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
836
1099
|
return {
|
|
837
1100
|
success: allSuccess,
|
|
1101
|
+
stoppedAtIndex,
|
|
838
1102
|
steps: results,
|
|
839
|
-
totalDurationMs
|
|
1103
|
+
totalDurationMs,
|
|
1104
|
+
recordingManifest
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
createRecordingContext(record) {
|
|
1108
|
+
const baseDir = record.outputDir ?? (0, import_node_path.join)(process.cwd(), ".browser-pilot");
|
|
1109
|
+
const screenshotDir = (0, import_node_path.join)(baseDir, "screenshots");
|
|
1110
|
+
const manifestPath = (0, import_node_path.join)(baseDir, "recording.json");
|
|
1111
|
+
let existingFrames = [];
|
|
1112
|
+
try {
|
|
1113
|
+
const existing = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
1114
|
+
if (existing.frames && Array.isArray(existing.frames)) {
|
|
1115
|
+
existingFrames = existing.frames;
|
|
1116
|
+
}
|
|
1117
|
+
} catch {
|
|
1118
|
+
}
|
|
1119
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
1120
|
+
return {
|
|
1121
|
+
baseDir,
|
|
1122
|
+
screenshotDir,
|
|
1123
|
+
sessionId: record.sessionId ?? this.page.targetId,
|
|
1124
|
+
frames: existingFrames,
|
|
1125
|
+
format: record.format ?? "webp",
|
|
1126
|
+
quality: Math.max(0, Math.min(100, record.quality ?? 40)),
|
|
1127
|
+
highlights: record.highlights !== false,
|
|
1128
|
+
skipActions: new Set(record.skipActions ?? DEFAULT_RECORDING_SKIP_ACTIONS)
|
|
840
1129
|
};
|
|
841
1130
|
}
|
|
1131
|
+
async getPageUrlSafe() {
|
|
1132
|
+
try {
|
|
1133
|
+
return await this.page.url();
|
|
1134
|
+
} catch {
|
|
1135
|
+
return "";
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Capture a recording screenshot frame with optional highlight overlay
|
|
1140
|
+
*/
|
|
1141
|
+
async captureRecordingFrame(step, stepResult, recording) {
|
|
1142
|
+
const targetMetadata = this.page.getLastActionTargetMetadata();
|
|
1143
|
+
let highlightInjected = false;
|
|
1144
|
+
try {
|
|
1145
|
+
const ts = Date.now();
|
|
1146
|
+
const seq = String(recording.frames.length + 1).padStart(4, "0");
|
|
1147
|
+
const filename = `${seq}-${ts}-${stepResult.action}.${recording.format}`;
|
|
1148
|
+
const filepath = (0, import_node_path.join)(recording.screenshotDir, filename);
|
|
1149
|
+
if (recording.highlights) {
|
|
1150
|
+
const kind = stepToHighlightKind(stepResult);
|
|
1151
|
+
if (kind) {
|
|
1152
|
+
await injectActionHighlight(this.page, {
|
|
1153
|
+
kind,
|
|
1154
|
+
bbox: stepResult.boundingBox,
|
|
1155
|
+
point: stepResult.coordinates,
|
|
1156
|
+
label: getHighlightLabel(step, stepResult, targetMetadata)
|
|
1157
|
+
});
|
|
1158
|
+
highlightInjected = true;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
const base64 = await this.page.screenshot({
|
|
1162
|
+
format: recording.format,
|
|
1163
|
+
quality: recording.quality
|
|
1164
|
+
});
|
|
1165
|
+
const buffer = Buffer.from(base64, "base64");
|
|
1166
|
+
fs.writeFileSync(filepath, buffer);
|
|
1167
|
+
stepResult.screenshotPath = filepath;
|
|
1168
|
+
let pageUrl;
|
|
1169
|
+
let pageTitle;
|
|
1170
|
+
try {
|
|
1171
|
+
pageUrl = await this.page.url();
|
|
1172
|
+
pageTitle = await this.page.title();
|
|
1173
|
+
} catch {
|
|
1174
|
+
}
|
|
1175
|
+
recording.frames.push({
|
|
1176
|
+
seq: recording.frames.length + 1,
|
|
1177
|
+
timestamp: ts,
|
|
1178
|
+
action: stepResult.action,
|
|
1179
|
+
selector: stepResult.selectorUsed ?? (Array.isArray(step.selector) ? step.selector[0] : step.selector),
|
|
1180
|
+
value: redactValueForRecording(
|
|
1181
|
+
typeof step.value === "string" ? step.value : void 0,
|
|
1182
|
+
targetMetadata
|
|
1183
|
+
),
|
|
1184
|
+
url: step.url,
|
|
1185
|
+
coordinates: stepResult.coordinates,
|
|
1186
|
+
boundingBox: stepResult.boundingBox,
|
|
1187
|
+
success: stepResult.success,
|
|
1188
|
+
durationMs: stepResult.durationMs,
|
|
1189
|
+
error: stepResult.error,
|
|
1190
|
+
screenshot: filename,
|
|
1191
|
+
pageUrl,
|
|
1192
|
+
pageTitle
|
|
1193
|
+
});
|
|
1194
|
+
} catch {
|
|
1195
|
+
} finally {
|
|
1196
|
+
if (recording.highlights || highlightInjected) {
|
|
1197
|
+
await removeActionHighlight(this.page);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Write recording manifest to disk
|
|
1203
|
+
*/
|
|
1204
|
+
async writeRecordingManifest(recording, startTime, startUrl, success) {
|
|
1205
|
+
let endUrl = startUrl;
|
|
1206
|
+
let viewport = { width: 1280, height: 720 };
|
|
1207
|
+
try {
|
|
1208
|
+
endUrl = await this.page.url();
|
|
1209
|
+
} catch {
|
|
1210
|
+
}
|
|
1211
|
+
try {
|
|
1212
|
+
const metrics = await this.page.cdpClient.send("Page.getLayoutMetrics");
|
|
1213
|
+
viewport = {
|
|
1214
|
+
width: metrics.cssVisualViewport.clientWidth,
|
|
1215
|
+
height: metrics.cssVisualViewport.clientHeight
|
|
1216
|
+
};
|
|
1217
|
+
} catch {
|
|
1218
|
+
}
|
|
1219
|
+
const manifestPath = (0, import_node_path.join)(recording.baseDir, "recording.json");
|
|
1220
|
+
let recordedAt = new Date(startTime).toISOString();
|
|
1221
|
+
let originalStartUrl = startUrl;
|
|
1222
|
+
try {
|
|
1223
|
+
const existing = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
1224
|
+
if (existing.recordedAt) recordedAt = existing.recordedAt;
|
|
1225
|
+
if (existing.startUrl) originalStartUrl = existing.startUrl;
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
const firstFrameTime = recording.frames[0]?.timestamp ?? startTime;
|
|
1229
|
+
const totalDurationMs = Date.now() - Math.min(firstFrameTime, startTime);
|
|
1230
|
+
const manifest = {
|
|
1231
|
+
version: 1,
|
|
1232
|
+
recordedAt,
|
|
1233
|
+
sessionId: recording.sessionId,
|
|
1234
|
+
startUrl: originalStartUrl,
|
|
1235
|
+
endUrl,
|
|
1236
|
+
viewport,
|
|
1237
|
+
format: recording.format,
|
|
1238
|
+
quality: recording.quality,
|
|
1239
|
+
totalDurationMs,
|
|
1240
|
+
success,
|
|
1241
|
+
frames: recording.frames
|
|
1242
|
+
};
|
|
1243
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1244
|
+
return manifestPath;
|
|
1245
|
+
}
|
|
842
1246
|
/**
|
|
843
1247
|
* Execute a single step
|
|
844
1248
|
*/
|
|
@@ -3189,6 +3593,24 @@ Content-Type: ${contentType}\r
|
|
|
3189
3593
|
parts.push(data);
|
|
3190
3594
|
}
|
|
3191
3595
|
|
|
3596
|
+
// src/utils/json.ts
|
|
3597
|
+
function isRecord(value) {
|
|
3598
|
+
return typeof value === "object" && value !== null;
|
|
3599
|
+
}
|
|
3600
|
+
function stringifyUnknown(value) {
|
|
3601
|
+
if (typeof value === "string") return value;
|
|
3602
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
3603
|
+
return String(value);
|
|
3604
|
+
}
|
|
3605
|
+
if (value === null) return "null";
|
|
3606
|
+
if (value === void 0) return "undefined";
|
|
3607
|
+
try {
|
|
3608
|
+
return JSON.stringify(value);
|
|
3609
|
+
} catch {
|
|
3610
|
+
return Object.prototype.toString.call(value);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3192
3614
|
// src/cdp/transport.ts
|
|
3193
3615
|
function createTransport(wsUrl, options = {}) {
|
|
3194
3616
|
const { timeout = 3e4 } = options;
|
|
@@ -3292,8 +3714,12 @@ function getReadyStateString(state) {
|
|
|
3292
3714
|
|
|
3293
3715
|
// src/cdp/client.ts
|
|
3294
3716
|
async function createCDPClient(wsUrl, options = {}) {
|
|
3295
|
-
const {
|
|
3717
|
+
const { timeout = 3e4 } = options;
|
|
3296
3718
|
const transport = await createTransport(wsUrl, { timeout });
|
|
3719
|
+
return buildCDPClient(transport, options);
|
|
3720
|
+
}
|
|
3721
|
+
function buildCDPClient(transport, options = {}) {
|
|
3722
|
+
const { debug = false, timeout = 3e4 } = options;
|
|
3297
3723
|
let messageId = 0;
|
|
3298
3724
|
let currentSessionId;
|
|
3299
3725
|
let connected = true;
|
|
@@ -3303,7 +3729,19 @@ async function createCDPClient(wsUrl, options = {}) {
|
|
|
3303
3729
|
transport.onMessage((raw) => {
|
|
3304
3730
|
let msg;
|
|
3305
3731
|
try {
|
|
3306
|
-
|
|
3732
|
+
const parsed = JSON.parse(raw);
|
|
3733
|
+
if (!isRecord(parsed)) {
|
|
3734
|
+
if (debug) console.error("[CDP] Ignoring non-object message:", raw);
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3737
|
+
if ("id" in parsed && typeof parsed["id"] === "number") {
|
|
3738
|
+
msg = parsed;
|
|
3739
|
+
} else if ("method" in parsed && typeof parsed["method"] === "string") {
|
|
3740
|
+
msg = parsed;
|
|
3741
|
+
} else {
|
|
3742
|
+
if (debug) console.error("[CDP] Ignoring invalid message shape:", raw);
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3307
3745
|
} catch {
|
|
3308
3746
|
if (debug) console.error("[CDP] Failed to parse message:", raw);
|
|
3309
3747
|
return;
|
|
@@ -3416,6 +3854,9 @@ async function createCDPClient(wsUrl, options = {}) {
|
|
|
3416
3854
|
onAny(handler) {
|
|
3417
3855
|
anyEventHandlers.add(handler);
|
|
3418
3856
|
},
|
|
3857
|
+
offAny(handler) {
|
|
3858
|
+
anyEventHandlers.delete(handler);
|
|
3859
|
+
},
|
|
3419
3860
|
async close() {
|
|
3420
3861
|
connected = false;
|
|
3421
3862
|
await transport.close();
|
|
@@ -4666,6 +5107,9 @@ var Page = class {
|
|
|
4666
5107
|
brokenFrame = null;
|
|
4667
5108
|
/** Last matched selector from findElement (for selectorUsed tracking) */
|
|
4668
5109
|
_lastMatchedSelector;
|
|
5110
|
+
_lastActionCoordinates = null;
|
|
5111
|
+
_lastActionBoundingBox = null;
|
|
5112
|
+
_lastActionTargetMetadata = null;
|
|
4669
5113
|
/** Last snapshot for stale ref recovery */
|
|
4670
5114
|
lastSnapshot;
|
|
4671
5115
|
/** Audio input controller (lazy-initialized) */
|
|
@@ -4697,6 +5141,76 @@ var Page = class {
|
|
|
4697
5141
|
getLastMatchedSelector() {
|
|
4698
5142
|
return this._lastMatchedSelector;
|
|
4699
5143
|
}
|
|
5144
|
+
async getActionTargetMetadata(identifiers) {
|
|
5145
|
+
try {
|
|
5146
|
+
const objectId = identifiers.objectId ?? (identifiers.nodeId ? await this.resolveObjectId(identifiers.nodeId) : void 0);
|
|
5147
|
+
if (!objectId) return null;
|
|
5148
|
+
const response = await this.cdp.send("Runtime.callFunctionOn", {
|
|
5149
|
+
objectId,
|
|
5150
|
+
functionDeclaration: `function() {
|
|
5151
|
+
const tagName = this.tagName?.toLowerCase?.() || '';
|
|
5152
|
+
const inputType =
|
|
5153
|
+
tagName === 'input' && typeof this.type === 'string' ? this.type.toLowerCase() : '';
|
|
5154
|
+
const autocomplete =
|
|
5155
|
+
typeof this.autocomplete === 'string' ? this.autocomplete.toLowerCase() : '';
|
|
5156
|
+
return { tagName, inputType, autocomplete };
|
|
5157
|
+
}`,
|
|
5158
|
+
returnByValue: true
|
|
5159
|
+
});
|
|
5160
|
+
return response.result.value ?? null;
|
|
5161
|
+
} catch {
|
|
5162
|
+
return null;
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
async getElementPosition(identifiers) {
|
|
5166
|
+
try {
|
|
5167
|
+
const { quads } = await this.cdp.send(
|
|
5168
|
+
"DOM.getContentQuads",
|
|
5169
|
+
identifiers
|
|
5170
|
+
);
|
|
5171
|
+
if (quads?.length > 0) {
|
|
5172
|
+
const q = quads[0];
|
|
5173
|
+
const minX = Math.min(q[0], q[2], q[4], q[6]);
|
|
5174
|
+
const maxX = Math.max(q[0], q[2], q[4], q[6]);
|
|
5175
|
+
const minY = Math.min(q[1], q[3], q[5], q[7]);
|
|
5176
|
+
const maxY = Math.max(q[1], q[3], q[5], q[7]);
|
|
5177
|
+
return {
|
|
5178
|
+
center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 },
|
|
5179
|
+
bbox: { x: minX, y: minY, width: maxX - minX, height: maxY - minY }
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
} catch {
|
|
5183
|
+
}
|
|
5184
|
+
if (identifiers.nodeId) {
|
|
5185
|
+
const box = await this.getBoxModel(identifiers.nodeId);
|
|
5186
|
+
if (box) {
|
|
5187
|
+
return {
|
|
5188
|
+
center: { x: box.content[0] + box.width / 2, y: box.content[1] + box.height / 2 },
|
|
5189
|
+
bbox: { x: box.content[0], y: box.content[1], width: box.width, height: box.height }
|
|
5190
|
+
};
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
return null;
|
|
5194
|
+
}
|
|
5195
|
+
setLastActionPosition(coords, bbox) {
|
|
5196
|
+
this._lastActionCoordinates = coords;
|
|
5197
|
+
this._lastActionBoundingBox = bbox;
|
|
5198
|
+
}
|
|
5199
|
+
getLastActionCoordinates() {
|
|
5200
|
+
return this._lastActionCoordinates;
|
|
5201
|
+
}
|
|
5202
|
+
getLastActionBoundingBox() {
|
|
5203
|
+
return this._lastActionBoundingBox;
|
|
5204
|
+
}
|
|
5205
|
+
getLastActionTargetMetadata() {
|
|
5206
|
+
return this._lastActionTargetMetadata;
|
|
5207
|
+
}
|
|
5208
|
+
/** Reset position tracking (call before each executor step) */
|
|
5209
|
+
resetLastActionPosition() {
|
|
5210
|
+
this._lastActionCoordinates = null;
|
|
5211
|
+
this._lastActionBoundingBox = null;
|
|
5212
|
+
this._lastActionTargetMetadata = null;
|
|
5213
|
+
}
|
|
4700
5214
|
/**
|
|
4701
5215
|
* Initialize the page (enable required CDP domains)
|
|
4702
5216
|
*/
|
|
@@ -4864,6 +5378,14 @@ var Page = class {
|
|
|
4864
5378
|
const quad = quads[0];
|
|
4865
5379
|
clickX = (quad[0] + quad[2] + quad[4] + quad[6]) / 4;
|
|
4866
5380
|
clickY = (quad[1] + quad[3] + quad[5] + quad[7]) / 4;
|
|
5381
|
+
const minX = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
|
5382
|
+
const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]);
|
|
5383
|
+
const minY = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
|
5384
|
+
const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]);
|
|
5385
|
+
this.setLastActionPosition(
|
|
5386
|
+
{ x: clickX, y: clickY },
|
|
5387
|
+
{ x: minX, y: minY, width: maxX - minX, height: maxY - minY }
|
|
5388
|
+
);
|
|
4867
5389
|
} else {
|
|
4868
5390
|
throw new Error("No quads");
|
|
4869
5391
|
}
|
|
@@ -4872,6 +5394,10 @@ var Page = class {
|
|
|
4872
5394
|
if (!box) throw new Error("Could not get element position");
|
|
4873
5395
|
clickX = box.content[0] + box.width / 2;
|
|
4874
5396
|
clickY = box.content[1] + box.height / 2;
|
|
5397
|
+
this.setLastActionPosition(
|
|
5398
|
+
{ x: clickX, y: clickY },
|
|
5399
|
+
{ x: box.content[0], y: box.content[1], width: box.width, height: box.height }
|
|
5400
|
+
);
|
|
4875
5401
|
}
|
|
4876
5402
|
const hitTargetCoordinates = this.currentFrame ? void 0 : { x: clickX, y: clickY };
|
|
4877
5403
|
const HIT_TARGET_RETRIES = 3;
|
|
@@ -4922,13 +5448,20 @@ var Page = class {
|
|
|
4922
5448
|
if (options.optional) return false;
|
|
4923
5449
|
throw e;
|
|
4924
5450
|
}
|
|
5451
|
+
const fillPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5452
|
+
if (fillPos) this.setLastActionPosition(fillPos.center, fillPos.bbox);
|
|
4925
5453
|
const tagInfo = await this.cdp.send("Runtime.callFunctionOn", {
|
|
4926
5454
|
objectId,
|
|
4927
5455
|
functionDeclaration: `function() {
|
|
4928
|
-
return {
|
|
5456
|
+
return {
|
|
5457
|
+
tagName: this.tagName?.toLowerCase() || '',
|
|
5458
|
+
inputType: (this.type || '').toLowerCase(),
|
|
5459
|
+
autocomplete: typeof this.autocomplete === 'string' ? this.autocomplete.toLowerCase() : '',
|
|
5460
|
+
};
|
|
4929
5461
|
}`,
|
|
4930
5462
|
returnByValue: true
|
|
4931
5463
|
});
|
|
5464
|
+
this._lastActionTargetMetadata = tagInfo.result.value;
|
|
4932
5465
|
const { tagName, inputType } = tagInfo.result.value;
|
|
4933
5466
|
const specialInputTypes = /* @__PURE__ */ new Set([
|
|
4934
5467
|
"date",
|
|
@@ -5010,6 +5543,9 @@ var Page = class {
|
|
|
5010
5543
|
if (options.optional) return false;
|
|
5011
5544
|
throw e;
|
|
5012
5545
|
}
|
|
5546
|
+
const typePos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5547
|
+
if (typePos) this.setLastActionPosition(typePos.center, typePos.bbox);
|
|
5548
|
+
this._lastActionTargetMetadata = await this.getActionTargetMetadata({ objectId });
|
|
5013
5549
|
await this.cdp.send("DOM.focus", { nodeId: element.nodeId });
|
|
5014
5550
|
for (const char of text) {
|
|
5015
5551
|
const def = US_KEYBOARD[char];
|
|
@@ -5089,6 +5625,9 @@ var Page = class {
|
|
|
5089
5625
|
if (options.optional) return false;
|
|
5090
5626
|
throw e;
|
|
5091
5627
|
}
|
|
5628
|
+
const selectPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5629
|
+
if (selectPos) this.setLastActionPosition(selectPos.center, selectPos.bbox);
|
|
5630
|
+
this._lastActionTargetMetadata = await this.getActionTargetMetadata({ objectId });
|
|
5092
5631
|
const metadata = await this.getNativeSelectMetadata(objectId, values);
|
|
5093
5632
|
if (!metadata.isSelect) {
|
|
5094
5633
|
throw new Error("select() target must be a native <select> element");
|
|
@@ -5225,6 +5764,8 @@ var Page = class {
|
|
|
5225
5764
|
if (options.optional) return false;
|
|
5226
5765
|
throw e;
|
|
5227
5766
|
}
|
|
5767
|
+
const checkPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5768
|
+
if (checkPos) this.setLastActionPosition(checkPos.center, checkPos.bbox);
|
|
5228
5769
|
const before = await this.cdp.send("Runtime.callFunctionOn", {
|
|
5229
5770
|
objectId: object.objectId,
|
|
5230
5771
|
functionDeclaration: "function() { return !!this.checked; }",
|
|
@@ -5273,6 +5814,8 @@ var Page = class {
|
|
|
5273
5814
|
if (options.optional) return false;
|
|
5274
5815
|
throw e;
|
|
5275
5816
|
}
|
|
5817
|
+
const uncheckPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5818
|
+
if (uncheckPos) this.setLastActionPosition(uncheckPos.center, uncheckPos.bbox);
|
|
5276
5819
|
const isRadio = await this.cdp.send(
|
|
5277
5820
|
"Runtime.callFunctionOn",
|
|
5278
5821
|
{
|
|
@@ -5328,6 +5871,8 @@ var Page = class {
|
|
|
5328
5871
|
throw new ElementNotFoundError(selector, hints);
|
|
5329
5872
|
}
|
|
5330
5873
|
const objectId = await this.resolveObjectId(element.nodeId);
|
|
5874
|
+
const submitPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5875
|
+
if (submitPos) this.setLastActionPosition(submitPos.center, submitPos.bbox);
|
|
5331
5876
|
const isFormElement = await this.cdp.send(
|
|
5332
5877
|
"Runtime.callFunctionOn",
|
|
5333
5878
|
{
|
|
@@ -5424,6 +5969,8 @@ var Page = class {
|
|
|
5424
5969
|
const hints = await generateHints(this, selectorList, "focus");
|
|
5425
5970
|
throw new ElementNotFoundError(selector, hints);
|
|
5426
5971
|
}
|
|
5972
|
+
const focusPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
5973
|
+
if (focusPos) this.setLastActionPosition(focusPos.center, focusPos.bbox);
|
|
5427
5974
|
await this.cdp.send("DOM.focus", { nodeId: element.nodeId });
|
|
5428
5975
|
return true;
|
|
5429
5976
|
}
|
|
@@ -5459,6 +6006,14 @@ var Page = class {
|
|
|
5459
6006
|
const quad = quads[0];
|
|
5460
6007
|
x = (quad[0] + quad[2] + quad[4] + quad[6]) / 4;
|
|
5461
6008
|
y = (quad[1] + quad[3] + quad[5] + quad[7]) / 4;
|
|
6009
|
+
const minX = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
|
6010
|
+
const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]);
|
|
6011
|
+
const minY = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
|
6012
|
+
const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]);
|
|
6013
|
+
this.setLastActionPosition(
|
|
6014
|
+
{ x, y },
|
|
6015
|
+
{ x: minX, y: minY, width: maxX - minX, height: maxY - minY }
|
|
6016
|
+
);
|
|
5462
6017
|
} else {
|
|
5463
6018
|
throw new Error("No quads");
|
|
5464
6019
|
}
|
|
@@ -5470,6 +6025,10 @@ var Page = class {
|
|
|
5470
6025
|
}
|
|
5471
6026
|
x = box.content[0] + box.width / 2;
|
|
5472
6027
|
y = box.content[1] + box.height / 2;
|
|
6028
|
+
this.setLastActionPosition(
|
|
6029
|
+
{ x, y },
|
|
6030
|
+
{ x: box.content[0], y: box.content[1], width: box.width, height: box.height }
|
|
6031
|
+
);
|
|
5473
6032
|
}
|
|
5474
6033
|
await this.cdp.send("Input.dispatchMouseEvent", {
|
|
5475
6034
|
type: "mouseMoved",
|
|
@@ -5495,6 +6054,8 @@ var Page = class {
|
|
|
5495
6054
|
if (options.optional) return false;
|
|
5496
6055
|
throw new ElementNotFoundError(selector);
|
|
5497
6056
|
}
|
|
6057
|
+
const scrollPos = await this.getElementPosition({ nodeId: element.nodeId });
|
|
6058
|
+
if (scrollPos) this.setLastActionPosition(scrollPos.center, scrollPos.bbox);
|
|
5498
6059
|
await this.scrollIntoView(element.nodeId);
|
|
5499
6060
|
return true;
|
|
5500
6061
|
}
|
|
@@ -6256,7 +6817,7 @@ var Page = class {
|
|
|
6256
6817
|
return {
|
|
6257
6818
|
role,
|
|
6258
6819
|
name,
|
|
6259
|
-
value: value !== void 0 ?
|
|
6820
|
+
value: value !== void 0 ? stringifyUnknown(value) : void 0,
|
|
6260
6821
|
ref,
|
|
6261
6822
|
children: children.length > 0 ? children : void 0,
|
|
6262
6823
|
disabled,
|
|
@@ -6318,7 +6879,7 @@ var Page = class {
|
|
|
6318
6879
|
selector,
|
|
6319
6880
|
disabled,
|
|
6320
6881
|
checked,
|
|
6321
|
-
value: value !== void 0 ?
|
|
6882
|
+
value: value !== void 0 ? stringifyUnknown(value) : void 0
|
|
6322
6883
|
});
|
|
6323
6884
|
}
|
|
6324
6885
|
}
|
|
@@ -6788,7 +7349,7 @@ var Page = class {
|
|
|
6788
7349
|
*/
|
|
6789
7350
|
formatConsoleArgs(args) {
|
|
6790
7351
|
return args.map((arg) => {
|
|
6791
|
-
if (arg.value !== void 0) return
|
|
7352
|
+
if (arg.value !== void 0) return stringifyUnknown(arg.value);
|
|
6792
7353
|
if (arg.description) return arg.description;
|
|
6793
7354
|
return "[object]";
|
|
6794
7355
|
}).join(" ");
|
|
@@ -7577,6 +8138,25 @@ var Browser = class _Browser {
|
|
|
7577
8138
|
this.cdp = cdp;
|
|
7578
8139
|
this.providerSession = providerSession;
|
|
7579
8140
|
}
|
|
8141
|
+
/**
|
|
8142
|
+
* Create a Browser from an existing CDPClient (used by daemon fast-path).
|
|
8143
|
+
* The caller is responsible for the CDP connection lifecycle.
|
|
8144
|
+
*/
|
|
8145
|
+
static fromCDP(cdp, sessionInfo) {
|
|
8146
|
+
const providerSession = {
|
|
8147
|
+
wsUrl: sessionInfo.wsUrl,
|
|
8148
|
+
sessionId: sessionInfo.sessionId,
|
|
8149
|
+
async close() {
|
|
8150
|
+
}
|
|
8151
|
+
};
|
|
8152
|
+
const provider = {
|
|
8153
|
+
name: sessionInfo.provider ?? "daemon",
|
|
8154
|
+
async createSession() {
|
|
8155
|
+
return providerSession;
|
|
8156
|
+
}
|
|
8157
|
+
};
|
|
8158
|
+
return new _Browser(cdp, provider, providerSession, { provider: "generic" });
|
|
8159
|
+
}
|
|
7580
8160
|
/**
|
|
7581
8161
|
* Connect to a browser instance
|
|
7582
8162
|
*/
|