@canaryai/cli 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RL5Y6V3C.js → chunk-FK3EZADZ.js} +612 -203
- package/dist/chunk-FK3EZADZ.js.map +1 -0
- package/dist/{chunk-TEHABH2E.js → chunk-K2OB72B6.js} +2 -2
- package/dist/{debug-workflow-ZFRF3JMY.js → debug-workflow-55G4Y6YT.js} +3 -3
- package/dist/index.js +4 -4
- package/dist/{local-browser-I2ANCFFH.js → local-browser-X7J27IGS.js} +3 -3
- package/dist/{mcp-EOWUKFEB.js → mcp-4JVLADZL.js} +3 -3
- package/dist/{record-TSF726OB.js → record-4OX7HXWQ.js} +2 -2
- package/dist/{src-SCKO6YUB.js → src-I4EXB5OD.js} +4 -2
- package/package.json +2 -2
- package/dist/chunk-RL5Y6V3C.js.map +0 -1
- /package/dist/{chunk-TEHABH2E.js.map → chunk-K2OB72B6.js.map} +0 -0
- /package/dist/{debug-workflow-ZFRF3JMY.js.map → debug-workflow-55G4Y6YT.js.map} +0 -0
- /package/dist/{local-browser-I2ANCFFH.js.map → local-browser-X7J27IGS.js.map} +0 -0
- /package/dist/{mcp-EOWUKFEB.js.map → mcp-4JVLADZL.js.map} +0 -0
- /package/dist/{record-TSF726OB.js.map → record-4OX7HXWQ.js.map} +0 -0
- /package/dist/{src-SCKO6YUB.js.map → src-I4EXB5OD.js.map} +0 -0
|
@@ -119,7 +119,7 @@ var require_omggif = __commonJS({
|
|
|
119
119
|
var min_code_size = 0;
|
|
120
120
|
while (num_colors >>= 1) ++min_code_size;
|
|
121
121
|
num_colors = 1 << min_code_size;
|
|
122
|
-
var
|
|
122
|
+
var delay2 = opts.delay === void 0 ? 0 : opts.delay;
|
|
123
123
|
var disposal = opts.disposal === void 0 ? 0 : opts.disposal;
|
|
124
124
|
if (disposal < 0 || disposal > 3)
|
|
125
125
|
throw new Error("Disposal out of range.");
|
|
@@ -131,13 +131,13 @@ var require_omggif = __commonJS({
|
|
|
131
131
|
if (transparent_index < 0 || transparent_index >= num_colors)
|
|
132
132
|
throw new Error("Transparent color index.");
|
|
133
133
|
}
|
|
134
|
-
if (disposal !== 0 || use_transparency ||
|
|
134
|
+
if (disposal !== 0 || use_transparency || delay2 !== 0) {
|
|
135
135
|
buf[p++] = 33;
|
|
136
136
|
buf[p++] = 249;
|
|
137
137
|
buf[p++] = 4;
|
|
138
138
|
buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
|
|
139
|
-
buf[p++] =
|
|
140
|
-
buf[p++] =
|
|
139
|
+
buf[p++] = delay2 & 255;
|
|
140
|
+
buf[p++] = delay2 >> 8 & 255;
|
|
141
141
|
buf[p++] = transparent_index;
|
|
142
142
|
buf[p++] = 0;
|
|
143
143
|
}
|
|
@@ -279,7 +279,7 @@ var require_omggif = __commonJS({
|
|
|
279
279
|
}
|
|
280
280
|
var no_eof = true;
|
|
281
281
|
var frames = [];
|
|
282
|
-
var
|
|
282
|
+
var delay2 = 0;
|
|
283
283
|
var transparent_index = null;
|
|
284
284
|
var disposal = 0;
|
|
285
285
|
var loop_count = null;
|
|
@@ -311,7 +311,7 @@ var require_omggif = __commonJS({
|
|
|
311
311
|
if (buf[p++] !== 4 || buf[p + 4] !== 0)
|
|
312
312
|
throw new Error("Invalid graphics extension block.");
|
|
313
313
|
var pf1 = buf[p++];
|
|
314
|
-
|
|
314
|
+
delay2 = buf[p++] | buf[p++] << 8;
|
|
315
315
|
transparent_index = buf[p++];
|
|
316
316
|
if ((pf1 & 1) === 0) transparent_index = null;
|
|
317
317
|
disposal = pf1 >> 2 & 7;
|
|
@@ -370,7 +370,7 @@ var require_omggif = __commonJS({
|
|
|
370
370
|
data_length: p - data_offset,
|
|
371
371
|
transparent_index,
|
|
372
372
|
interlaced: !!interlace_flag,
|
|
373
|
-
delay,
|
|
373
|
+
delay: delay2,
|
|
374
374
|
disposal
|
|
375
375
|
});
|
|
376
376
|
break;
|
|
@@ -3777,7 +3777,7 @@ var require_gifframe = __commonJS({
|
|
|
3777
3777
|
var require_gifutil = __commonJS({
|
|
3778
3778
|
"../../node_modules/.bun/gifwrap@0.10.1/node_modules/gifwrap/src/gifutil.js"(exports2) {
|
|
3779
3779
|
"use strict";
|
|
3780
|
-
var
|
|
3780
|
+
var fs5 = __require("fs");
|
|
3781
3781
|
var ImageQ = require_image_q();
|
|
3782
3782
|
var BitmapImage2 = require_bitmapimage();
|
|
3783
3783
|
var { GifFrame: GifFrame2 } = require_gifframe();
|
|
@@ -3892,14 +3892,14 @@ var require_gifutil = __commonJS({
|
|
|
3892
3892
|
jimpImage.bitmap.data = bitmapImageToShare.bitmap.data;
|
|
3893
3893
|
return jimpImage;
|
|
3894
3894
|
};
|
|
3895
|
-
exports2.write = function(
|
|
3895
|
+
exports2.write = function(path5, frames, spec, encoder) {
|
|
3896
3896
|
encoder = encoder || defaultCodec;
|
|
3897
|
-
const matches =
|
|
3897
|
+
const matches = path5.match(/\.[a-zA-Z]+$/);
|
|
3898
3898
|
if (matches !== null && INVALID_SUFFIXES.includes(matches[0].toLowerCase())) {
|
|
3899
|
-
throw new Error(`GIF '${
|
|
3899
|
+
throw new Error(`GIF '${path5}' has an unexpected suffix`);
|
|
3900
3900
|
}
|
|
3901
3901
|
return encoder.encodeGif(frames, spec).then((gif2) => {
|
|
3902
|
-
return _writeBinary(
|
|
3902
|
+
return _writeBinary(path5, gif2.buffer).then(() => {
|
|
3903
3903
|
return gif2;
|
|
3904
3904
|
});
|
|
3905
3905
|
});
|
|
@@ -3971,9 +3971,9 @@ var require_gifutil = __commonJS({
|
|
|
3971
3971
|
}
|
|
3972
3972
|
}
|
|
3973
3973
|
}
|
|
3974
|
-
function _readBinary(
|
|
3974
|
+
function _readBinary(path5) {
|
|
3975
3975
|
return new Promise((resolve2, reject2) => {
|
|
3976
|
-
|
|
3976
|
+
fs5.readFile(path5, (err, buffer) => {
|
|
3977
3977
|
if (err) {
|
|
3978
3978
|
return reject2(err);
|
|
3979
3979
|
}
|
|
@@ -3981,9 +3981,9 @@ var require_gifutil = __commonJS({
|
|
|
3981
3981
|
});
|
|
3982
3982
|
});
|
|
3983
3983
|
}
|
|
3984
|
-
function _writeBinary(
|
|
3984
|
+
function _writeBinary(path5, buffer) {
|
|
3985
3985
|
return new Promise((resolve2, reject2) => {
|
|
3986
|
-
|
|
3986
|
+
fs5.writeFile(path5, buffer, (err) => {
|
|
3987
3987
|
if (err) {
|
|
3988
3988
|
return reject2(err);
|
|
3989
3989
|
}
|
|
@@ -5964,9 +5964,9 @@ var require_decoder = __commonJS({
|
|
|
5964
5964
|
return a < 0 ? 0 : a > 255 ? 255 : a;
|
|
5965
5965
|
}
|
|
5966
5966
|
constructor.prototype = {
|
|
5967
|
-
load: function load(
|
|
5967
|
+
load: function load(path5) {
|
|
5968
5968
|
var xhr = new XMLHttpRequest();
|
|
5969
|
-
xhr.open("GET",
|
|
5969
|
+
xhr.open("GET", path5, true);
|
|
5970
5970
|
xhr.responseType = "arraybuffer";
|
|
5971
5971
|
xhr.onload = (function() {
|
|
5972
5972
|
var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
|
|
@@ -18862,11 +18862,11 @@ var require_Mime = __commonJS({
|
|
|
18862
18862
|
}
|
|
18863
18863
|
}
|
|
18864
18864
|
};
|
|
18865
|
-
Mime.prototype.getType = function(
|
|
18866
|
-
|
|
18867
|
-
let last =
|
|
18865
|
+
Mime.prototype.getType = function(path5) {
|
|
18866
|
+
path5 = String(path5);
|
|
18867
|
+
let last = path5.replace(/^.*[/\\]/, "").toLowerCase();
|
|
18868
18868
|
let ext = last.replace(/^.*\./, "").toLowerCase();
|
|
18869
|
-
let hasPath = last.length <
|
|
18869
|
+
let hasPath = last.length < path5.length;
|
|
18870
18870
|
let hasDot = ext.length < last.length - 1;
|
|
18871
18871
|
return (hasDot || !hasPath) && this._types[ext] || null;
|
|
18872
18872
|
};
|
|
@@ -32623,9 +32623,9 @@ function createJimp({ plugins: pluginsArg, formats: formatsArg } = {}) {
|
|
|
32623
32623
|
* await image.write("test/output.png");
|
|
32624
32624
|
* ```
|
|
32625
32625
|
*/
|
|
32626
|
-
async write(
|
|
32627
|
-
const mimeType = import_lite.default.getType(
|
|
32628
|
-
await writeFile(
|
|
32626
|
+
async write(path5, options) {
|
|
32627
|
+
const mimeType = import_lite.default.getType(path5);
|
|
32628
|
+
await writeFile(path5, await this.getBuffer(mimeType, options));
|
|
32629
32629
|
}
|
|
32630
32630
|
/**
|
|
32631
32631
|
* Clone the image into a new Jimp instance.
|
|
@@ -38485,6 +38485,27 @@ function resolveContainerFieldLabel(container) {
|
|
|
38485
38485
|
}
|
|
38486
38486
|
return void 0;
|
|
38487
38487
|
}
|
|
38488
|
+
function shouldDropInheritedFieldLabel(element, inheritedFieldLabel) {
|
|
38489
|
+
if (!inheritedFieldLabel) return false;
|
|
38490
|
+
if (INTERACTIVE_LEAF_ROLES.has(element.role)) return false;
|
|
38491
|
+
if (!isClickableByAttribute(element)) return false;
|
|
38492
|
+
const directText = getElementText(element);
|
|
38493
|
+
if (directText) {
|
|
38494
|
+
return directText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
|
|
38495
|
+
}
|
|
38496
|
+
const childTexts = [];
|
|
38497
|
+
for (const child of element.children) {
|
|
38498
|
+
if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
|
|
38499
|
+
const text = getElementText(child);
|
|
38500
|
+
if (text && TEXT_CARRYING_ROLES.has(child.role)) {
|
|
38501
|
+
childTexts.push(text);
|
|
38502
|
+
}
|
|
38503
|
+
}
|
|
38504
|
+
}
|
|
38505
|
+
const composedText = childTexts.join(" ");
|
|
38506
|
+
if (!composedText) return false;
|
|
38507
|
+
return composedText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
|
|
38508
|
+
}
|
|
38488
38509
|
var TEXT_CARRYING_ROLES = /* @__PURE__ */ new Set([
|
|
38489
38510
|
"generic",
|
|
38490
38511
|
"paragraph",
|
|
@@ -38571,10 +38592,10 @@ function extractDataValue(text) {
|
|
|
38571
38592
|
}
|
|
38572
38593
|
function findRelevanceScope(elements, activeElementRef) {
|
|
38573
38594
|
if (!activeElementRef) return null;
|
|
38574
|
-
const
|
|
38595
|
+
const path5 = [];
|
|
38575
38596
|
function findPath(el) {
|
|
38576
38597
|
if (el.ref) {
|
|
38577
|
-
|
|
38598
|
+
path5.push({ ref: el.ref, role: el.role });
|
|
38578
38599
|
}
|
|
38579
38600
|
if (el.ref === activeElementRef) {
|
|
38580
38601
|
return true;
|
|
@@ -38582,15 +38603,15 @@ function findRelevanceScope(elements, activeElementRef) {
|
|
|
38582
38603
|
for (const child of el.children) {
|
|
38583
38604
|
if (findPath(child)) return true;
|
|
38584
38605
|
}
|
|
38585
|
-
if (el.ref)
|
|
38606
|
+
if (el.ref) path5.pop();
|
|
38586
38607
|
return false;
|
|
38587
38608
|
}
|
|
38588
38609
|
for (const el of elements) {
|
|
38589
38610
|
if (findPath(el)) break;
|
|
38590
38611
|
}
|
|
38591
|
-
for (let i =
|
|
38592
|
-
if (SECTION_BOUNDARY_ROLES.has(
|
|
38593
|
-
return
|
|
38612
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
38613
|
+
if (SECTION_BOUNDARY_ROLES.has(path5[i].role)) {
|
|
38614
|
+
return path5[i].ref;
|
|
38594
38615
|
}
|
|
38595
38616
|
}
|
|
38596
38617
|
return null;
|
|
@@ -38639,9 +38660,9 @@ function searchElements(elements, searchTerms, scopeRef = null) {
|
|
|
38639
38660
|
function findElementInSections(sections, targetRef, ancestors = []) {
|
|
38640
38661
|
for (const section of sections) {
|
|
38641
38662
|
const newAncestors = [...ancestors, section];
|
|
38642
|
-
const
|
|
38663
|
+
const path5 = newAncestors.map((s) => s.heading);
|
|
38643
38664
|
if (section.elements.some((el) => el.ref === targetRef)) {
|
|
38644
|
-
return { section, ancestors: newAncestors, path:
|
|
38665
|
+
return { section, ancestors: newAncestors, path: path5 };
|
|
38645
38666
|
}
|
|
38646
38667
|
const found = findElementInSections(section.subsections, targetRef, newAncestors);
|
|
38647
38668
|
if (found) return found;
|
|
@@ -38652,7 +38673,7 @@ function populateSearchContext(matches, sections) {
|
|
|
38652
38673
|
for (const match of matches) {
|
|
38653
38674
|
const result = findElementInSections(sections, match.ref);
|
|
38654
38675
|
if (result) {
|
|
38655
|
-
const { section, ancestors, path:
|
|
38676
|
+
const { section, ancestors, path: path5 } = result;
|
|
38656
38677
|
const inFocusedDialog = ancestors.some((s) => s.isFocused);
|
|
38657
38678
|
const inBackground = section.isBackground || ancestors.some((s) => s.isBackground);
|
|
38658
38679
|
if (inFocusedDialog) {
|
|
@@ -38663,7 +38684,7 @@ function populateSearchContext(matches, sections) {
|
|
|
38663
38684
|
match.context = "foreground";
|
|
38664
38685
|
}
|
|
38665
38686
|
const SKIP_HEADINGS = ["generic", "main", "iframe"];
|
|
38666
|
-
match.sectionPath =
|
|
38687
|
+
match.sectionPath = path5.filter((p) => !SKIP_HEADINGS.includes(p.toLowerCase())).slice(-3).join(" > ");
|
|
38667
38688
|
}
|
|
38668
38689
|
}
|
|
38669
38690
|
}
|
|
@@ -38825,6 +38846,24 @@ function findPrimaryColumnLabel(columnHeader) {
|
|
|
38825
38846
|
};
|
|
38826
38847
|
return search(columnHeader);
|
|
38827
38848
|
}
|
|
38849
|
+
function isWidgetTable(element) {
|
|
38850
|
+
if (element.role !== "table") return false;
|
|
38851
|
+
const rows = element.children.filter((c2) => c2.role === "row");
|
|
38852
|
+
if (rows.length === 0 || rows.length > 2) return false;
|
|
38853
|
+
let totalCells = 0;
|
|
38854
|
+
let interactiveCells = 0;
|
|
38855
|
+
for (const row of rows) {
|
|
38856
|
+
for (const cell of row.children) {
|
|
38857
|
+
if (cell.role === "cell" || cell.role === "gridcell" || cell.role === "columnheader") {
|
|
38858
|
+
totalCells++;
|
|
38859
|
+
if (hasInteractiveDescendants(cell)) {
|
|
38860
|
+
interactiveCells++;
|
|
38861
|
+
}
|
|
38862
|
+
}
|
|
38863
|
+
}
|
|
38864
|
+
}
|
|
38865
|
+
return totalCells > 0 && interactiveCells >= totalCells * 0.5;
|
|
38866
|
+
}
|
|
38828
38867
|
function detectGrid(element) {
|
|
38829
38868
|
if (element.role !== "grid" && element.role !== "treegrid" && element.role !== "table") {
|
|
38830
38869
|
return null;
|
|
@@ -39150,7 +39189,7 @@ function formatElement(element, options) {
|
|
|
39150
39189
|
const textSource = normalizeIconLabelText(element.role, rawTextSource);
|
|
39151
39190
|
const extracted = extractDataValue(textSource);
|
|
39152
39191
|
const normalizedAttributes = Object.entries(element.attributes).filter(
|
|
39153
|
-
([key]) => key !== "ref" && key !== "cursor" && key !== "aria-controls" && key !== "aria-owns" && key !== "options"
|
|
39192
|
+
([key]) => key !== "ref" && key !== "cursor" && key !== "aria-controls" && key !== "aria-owns" && key !== "options" && key !== "field"
|
|
39154
39193
|
).map(([key, val]) => {
|
|
39155
39194
|
if (key === "aria-haspopup") return "haspopup";
|
|
39156
39195
|
if (key === "aria-expanded") return `expanded=${val}`;
|
|
@@ -39169,7 +39208,7 @@ function formatElement(element, options) {
|
|
|
39169
39208
|
ref: element.ref,
|
|
39170
39209
|
role: element.role,
|
|
39171
39210
|
name: textSource,
|
|
39172
|
-
fieldLabel: options?.fieldLabel,
|
|
39211
|
+
fieldLabel: options?.fieldLabel || element.attributes["field"] || void 0,
|
|
39173
39212
|
label: extracted.label || void 0,
|
|
39174
39213
|
value: extracted.value,
|
|
39175
39214
|
status: extracted.status,
|
|
@@ -39396,22 +39435,23 @@ function buildGridSection(element, gridInfo) {
|
|
|
39396
39435
|
}
|
|
39397
39436
|
function collectInteractiveElements(element, elements, subsections, depth, inheritedFieldLabel) {
|
|
39398
39437
|
if ((INTERACTIVE_LEAF_ROLES.has(element.role) || isClickableByAttribute(element)) && element.ref) {
|
|
39438
|
+
const effectiveFieldLabel = shouldDropInheritedFieldLabel(element, inheritedFieldLabel) ? void 0 : inheritedFieldLabel;
|
|
39399
39439
|
if (INTERACTIVE_LEAF_ROLES.has(element.role) && isExpandedWithActionableChildren(element)) {
|
|
39400
|
-
elements.push(formatElement(element, { overrideLabel: composeTriggerLabel(element), fieldLabel:
|
|
39440
|
+
elements.push(formatElement(element, { overrideLabel: composeTriggerLabel(element), fieldLabel: effectiveFieldLabel }));
|
|
39401
39441
|
collectExpandedChildren(element, elements);
|
|
39402
39442
|
return;
|
|
39403
39443
|
}
|
|
39404
39444
|
if (!isMenuTriggerNoise(element)) {
|
|
39405
|
-
elements.push(formatElement(element, { fieldLabel:
|
|
39445
|
+
elements.push(formatElement(element, { fieldLabel: effectiveFieldLabel }));
|
|
39406
39446
|
}
|
|
39407
39447
|
if (!INTERACTIVE_LEAF_ROLES.has(element.role) && isClickableByAttribute(element)) {
|
|
39408
39448
|
for (const child of element.children) {
|
|
39409
|
-
collectInteractiveElements(child, elements, subsections, depth + 1,
|
|
39449
|
+
collectInteractiveElements(child, elements, subsections, depth + 1, effectiveFieldLabel);
|
|
39410
39450
|
}
|
|
39411
39451
|
}
|
|
39412
39452
|
return;
|
|
39413
39453
|
}
|
|
39414
|
-
if (element.role === "heading" || STRUCTURAL_ROLES.has(element.role) || element.role === "grid" || element.role === "treegrid" || element.role === "table") {
|
|
39454
|
+
if (element.role === "heading" || STRUCTURAL_ROLES.has(element.role) || element.role === "grid" || element.role === "treegrid" || element.role === "table" && !isWidgetTable(element)) {
|
|
39415
39455
|
const section = buildSection(element, depth);
|
|
39416
39456
|
if (section) {
|
|
39417
39457
|
subsections.push(section);
|
|
@@ -39449,7 +39489,8 @@ function collectInteractiveElements(element, elements, subsections, depth, inher
|
|
|
39449
39489
|
collectInteractiveElements(child, elements, subsections, depth + 1);
|
|
39450
39490
|
}
|
|
39451
39491
|
} else if ((INTERACTIVE_LEAF_ROLES.has(child.role) || isClickableByAttribute(child)) && child.ref) {
|
|
39452
|
-
const
|
|
39492
|
+
const rawFieldLabel = findFieldLabelInContext(child, kids, ki) ?? inheritedFieldLabel;
|
|
39493
|
+
const fieldLabel = shouldDropInheritedFieldLabel(child, rawFieldLabel) ? void 0 : rawFieldLabel;
|
|
39453
39494
|
if (!isMenuTriggerNoise(child)) {
|
|
39454
39495
|
if (INTERACTIVE_LEAF_ROLES.has(child.role) && isExpandedWithActionableChildren(child)) {
|
|
39455
39496
|
elements.push(formatElement(child, { overrideLabel: composeTriggerLabel(child), fieldLabel }));
|
|
@@ -39464,7 +39505,7 @@ function collectInteractiveElements(element, elements, subsections, depth, inher
|
|
|
39464
39505
|
}
|
|
39465
39506
|
}
|
|
39466
39507
|
} else {
|
|
39467
|
-
const containerLabel = resolveContainerFieldLabel(child) ?? inheritedFieldLabel;
|
|
39508
|
+
const containerLabel = resolveContainerFieldLabel(child) ?? (child.role === "table" ? findFieldLabelFromSiblings(kids, ki) : void 0) ?? inheritedFieldLabel;
|
|
39468
39509
|
collectInteractiveElements(child, elements, subsections, depth + 1, containerLabel);
|
|
39469
39510
|
}
|
|
39470
39511
|
}
|
|
@@ -39608,7 +39649,7 @@ var COLOR_NAMES = [
|
|
|
39608
39649
|
"teal",
|
|
39609
39650
|
"pink"
|
|
39610
39651
|
];
|
|
39611
|
-
function generateFrontmatter(elementCount, sectionCount, activeElement, dialogStack, searchMatches, totalMatchCount, snapshotFilePath, probeResult) {
|
|
39652
|
+
function generateFrontmatter(elementCount, sectionCount, activeElement, dialogStack, searchMatches, totalMatchCount, snapshotFilePath, probeResult, currentTime, capturedToasts) {
|
|
39612
39653
|
const activeLabel = activeElement ? normalizeIconLabelText(
|
|
39613
39654
|
activeElement.role,
|
|
39614
39655
|
INTERACTIVE_LEAF_ROLES.has(activeElement.role) && isExpandedWithActionableChildren(activeElement) ? composeTriggerLabel(activeElement) : activeElement.text || ""
|
|
@@ -39616,6 +39657,16 @@ function generateFrontmatter(elementCount, sectionCount, activeElement, dialogSt
|
|
|
39616
39657
|
const activeDesc = activeElement ? `${activeElement.ref} ${activeElement.role} "${activeLabel}"` : "None";
|
|
39617
39658
|
const dialogStackLine = dialogStack.length > 0 ? `
|
|
39618
39659
|
DIALOG STACK: ${dialogStack.join(" \u2192 ")}` : "";
|
|
39660
|
+
let toastSection = "";
|
|
39661
|
+
if (capturedToasts && capturedToasts.length > 0) {
|
|
39662
|
+
const toastLines = capturedToasts.map((t) => {
|
|
39663
|
+
const ttl = t.snapshotsRemaining !== void 0 ? ` (expires in ${t.snapshotsRemaining} snapshots)` : "";
|
|
39664
|
+
return ` - [${t.role}] "${t.text}"${ttl}`;
|
|
39665
|
+
});
|
|
39666
|
+
toastSection = `
|
|
39667
|
+
TOAST NOTIFICATIONS:
|
|
39668
|
+
${toastLines.join("\n")}`;
|
|
39669
|
+
}
|
|
39619
39670
|
let searchSection = "";
|
|
39620
39671
|
if (searchMatches && searchMatches.length > 0) {
|
|
39621
39672
|
const hasContextInfo = searchMatches.some((m) => m.context);
|
|
@@ -39665,10 +39716,12 @@ PROBE RESULT:
|
|
|
39665
39716
|
FULL TREE FILE: ${snapshotFilePath}
|
|
39666
39717
|
Use bash or python_execute to grep/parse this file for detailed element analysis.`;
|
|
39667
39718
|
}
|
|
39719
|
+
const browserTime = currentTime ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
39668
39720
|
return `---
|
|
39669
39721
|
SEMANTIC SNAPSHOT - ${elementCount} elements in ${sectionCount} sections
|
|
39722
|
+
BROWSER TIME: ${browserTime} (UTC)
|
|
39670
39723
|
FORMAT: Sections grouped by headings. [ref] tags identify clickable elements.
|
|
39671
|
-
ACTIVE ELEMENT: ${activeDesc}${dialogStackLine}${searchSection}${probeSection}${fullTreeFileSection}
|
|
39724
|
+
ACTIVE ELEMENT: ${activeDesc}${dialogStackLine}${toastSection}${searchSection}${probeSection}${fullTreeFileSection}
|
|
39672
39725
|
ACTIONS:
|
|
39673
39726
|
- Click/type using [ref] values (e.g., browser_click ref=e123)${typeActionHint}
|
|
39674
39727
|
- Click using coordinates from search results (e.g., browser_click x=450 y=320)
|
|
@@ -39738,7 +39791,9 @@ function formatSemanticSnapshot(yaml, options) {
|
|
|
39738
39791
|
// Use re-sorted matches with context
|
|
39739
39792
|
options?.totalMatchCount,
|
|
39740
39793
|
options?.snapshotFilePath,
|
|
39741
|
-
options?.probeResult
|
|
39794
|
+
options?.probeResult,
|
|
39795
|
+
options?.currentTime,
|
|
39796
|
+
options?.capturedToasts
|
|
39742
39797
|
)
|
|
39743
39798
|
);
|
|
39744
39799
|
lines.push("");
|
|
@@ -40137,10 +40192,11 @@ function generateHint(diff2, networkInfo) {
|
|
|
40137
40192
|
}
|
|
40138
40193
|
return hints.join(" ");
|
|
40139
40194
|
}
|
|
40140
|
-
function formatSemanticDiff(diff2, actionDescription, networkInfo) {
|
|
40195
|
+
function formatSemanticDiff(diff2, actionDescription, networkInfo, currentTime) {
|
|
40141
40196
|
const lines = [];
|
|
40142
40197
|
lines.push("---");
|
|
40143
40198
|
lines.push("SEMANTIC DIFF: Page state changes after browser action.");
|
|
40199
|
+
lines.push(`BROWSER TIME: ${currentTime ?? (/* @__PURE__ */ new Date()).toISOString()} (UTC)`);
|
|
40144
40200
|
lines.push("Format: [PREVIOUS] value [CURRENT] value");
|
|
40145
40201
|
lines.push("");
|
|
40146
40202
|
lines.push(`ACTION: ${actionDescription}`);
|
|
@@ -40219,6 +40275,38 @@ function formatValue(value) {
|
|
|
40219
40275
|
return `"${truncate(value, 20)}"`;
|
|
40220
40276
|
}
|
|
40221
40277
|
|
|
40278
|
+
// ../browser-core/src/playwright-client/download-utils.ts
|
|
40279
|
+
import * as path from "path";
|
|
40280
|
+
function formatFileSize(bytes) {
|
|
40281
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
40282
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
40283
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
40284
|
+
}
|
|
40285
|
+
function guessMimeType(filename) {
|
|
40286
|
+
const ext = path.extname(filename).toLowerCase();
|
|
40287
|
+
const mimeMap = {
|
|
40288
|
+
".pdf": "application/pdf",
|
|
40289
|
+
".txt": "text/plain",
|
|
40290
|
+
".csv": "text/csv",
|
|
40291
|
+
".json": "application/json",
|
|
40292
|
+
".xml": "application/xml",
|
|
40293
|
+
".html": "text/html",
|
|
40294
|
+
".htm": "text/html",
|
|
40295
|
+
".md": "text/markdown",
|
|
40296
|
+
".png": "image/png",
|
|
40297
|
+
".jpg": "image/jpeg",
|
|
40298
|
+
".jpeg": "image/jpeg",
|
|
40299
|
+
".gif": "image/gif",
|
|
40300
|
+
".svg": "image/svg+xml",
|
|
40301
|
+
".zip": "application/zip",
|
|
40302
|
+
".doc": "application/msword",
|
|
40303
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
40304
|
+
".xls": "application/vnd.ms-excel",
|
|
40305
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
40306
|
+
};
|
|
40307
|
+
return mimeMap[ext] ?? "application/octet-stream";
|
|
40308
|
+
}
|
|
40309
|
+
|
|
40222
40310
|
// ../browser-core/src/playwright-client/element-inspection.ts
|
|
40223
40311
|
function errorMessage(error) {
|
|
40224
40312
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -40618,7 +40706,7 @@ async function extractElementMetadata(page, locator, ref, logger2) {
|
|
|
40618
40706
|
}
|
|
40619
40707
|
|
|
40620
40708
|
// ../browser-core/src/tools/executor.ts
|
|
40621
|
-
import * as
|
|
40709
|
+
import * as path2 from "path";
|
|
40622
40710
|
import * as fs2 from "fs/promises";
|
|
40623
40711
|
import * as crypto2 from "crypto";
|
|
40624
40712
|
|
|
@@ -40750,7 +40838,7 @@ var BrowserToolExecutor = class {
|
|
|
40750
40838
|
if (!options?.workspaceDir && !options?.tmpBaseDir) {
|
|
40751
40839
|
options?.logger?.debug?.("[BrowserToolExecutor] No tmpBaseDir provided, using flat canary dir (not org-scoped)");
|
|
40752
40840
|
}
|
|
40753
|
-
this.workspaceDir = options?.workspaceDir ??
|
|
40841
|
+
this.workspaceDir = options?.workspaceDir ?? path2.join(options?.tmpBaseDir ?? getCanaryTmpDir(), `browser-workspace-${Date.now()}-${crypto2.randomUUID().slice(0, 8)}`);
|
|
40754
40842
|
this.logger = options?.logger;
|
|
40755
40843
|
fs2.mkdir(this.workspaceDir, { recursive: true }).catch(() => {
|
|
40756
40844
|
});
|
|
@@ -40915,15 +41003,18 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
40915
41003
|
try {
|
|
40916
41004
|
await fs2.mkdir(this.workspaceDir, { recursive: true });
|
|
40917
41005
|
const filename = `snapshot-${Date.now()}-${crypto2.randomUUID().slice(0, 8)}.yaml`;
|
|
40918
|
-
snapshotFilePath =
|
|
41006
|
+
snapshotFilePath = path2.join(this.workspaceDir, filename);
|
|
40919
41007
|
await fs2.writeFile(snapshotFilePath, yaml, "utf-8");
|
|
40920
41008
|
} catch {
|
|
40921
41009
|
}
|
|
41010
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
40922
41011
|
const semanticText = formatSemanticSnapshot(yaml, {
|
|
40923
41012
|
searchMatches: searchMatches.length > 0 ? searchMatches : void 0,
|
|
40924
41013
|
totalMatchCount: totalMatchCount > 0 ? totalMatchCount : void 0,
|
|
40925
41014
|
snapshotFilePath,
|
|
40926
|
-
probeResult
|
|
41015
|
+
probeResult,
|
|
41016
|
+
currentTime: now,
|
|
41017
|
+
capturedToasts: this.client.getCapturedToasts?.() ?? []
|
|
40927
41018
|
});
|
|
40928
41019
|
if (hasImages) {
|
|
40929
41020
|
const resolutionNote = buildResolutionNote(
|
|
@@ -41166,7 +41257,7 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
41166
41257
|
if (!afterModal) {
|
|
41167
41258
|
const afterState2 = captureSnapshotState(afterUrl2, afterYaml2);
|
|
41168
41259
|
const diff3 = compareSnapshots(beforeState, afterState2);
|
|
41169
|
-
return formatSemanticDiff(diff3, `Dismissed overlay using ${strategy}
|
|
41260
|
+
return formatSemanticDiff(diff3, `Dismissed overlay using ${strategy}`, void 0, (/* @__PURE__ */ new Date()).toISOString());
|
|
41170
41261
|
}
|
|
41171
41262
|
} catch (err) {
|
|
41172
41263
|
this.logger?.debug?.(`[BrowserTools] Dismiss strategy ${strategy} failed`, {
|
|
@@ -41182,7 +41273,7 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
41182
41273
|
const diff2 = compareSnapshots(beforeState, afterState);
|
|
41183
41274
|
return `Could not dismiss overlay with strategies: ${strategies.join(", ")}. Modal may require specific interaction.
|
|
41184
41275
|
|
|
41185
|
-
` + formatSemanticDiff(diff2, "Dismiss overlay attempted (failed)");
|
|
41276
|
+
` + formatSemanticDiff(diff2, "Dismiss overlay attempted (failed)", void 0, (/* @__PURE__ */ new Date()).toISOString());
|
|
41186
41277
|
}
|
|
41187
41278
|
// ==================== Waiting ====================
|
|
41188
41279
|
async waitFor(opts) {
|
|
@@ -41296,7 +41387,8 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
41296
41387
|
}
|
|
41297
41388
|
const diff2 = compareSnapshots(beforeState, afterState);
|
|
41298
41389
|
const networkInfo = this.client.getPendingNetworkInfo?.() ?? void 0;
|
|
41299
|
-
const
|
|
41390
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
41391
|
+
const diffText = formatSemanticDiff(diff2, actionDescription, networkInfo, now);
|
|
41300
41392
|
const hasChanges = hasDiffChanges(diff2);
|
|
41301
41393
|
const isStable = !networkInfo || networkInfo.pendingCount === 0;
|
|
41302
41394
|
let autoSnapshot;
|
|
@@ -41306,11 +41398,11 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
41306
41398
|
try {
|
|
41307
41399
|
await fs2.mkdir(this.workspaceDir, { recursive: true });
|
|
41308
41400
|
const filename = `snapshot-${Date.now()}-${crypto2.randomUUID().slice(0, 8)}.yaml`;
|
|
41309
|
-
snapshotFilePath =
|
|
41401
|
+
snapshotFilePath = path2.join(this.workspaceDir, filename);
|
|
41310
41402
|
await fs2.writeFile(snapshotFilePath, afterYaml, "utf-8");
|
|
41311
41403
|
} catch {
|
|
41312
41404
|
}
|
|
41313
|
-
const semanticText = formatSemanticSnapshot(afterYaml, { snapshotFilePath });
|
|
41405
|
+
const semanticText = formatSemanticSnapshot(afterYaml, { snapshotFilePath, currentTime: now, capturedToasts: this.client.getCapturedToasts?.() ?? [] });
|
|
41314
41406
|
const afterImages = isMCPContentWithImages(afterResult) ? afterResult.images : void 0;
|
|
41315
41407
|
if (afterImages?.length && this.onScreenshot) {
|
|
41316
41408
|
const img = afterImages[0];
|
|
@@ -41347,11 +41439,11 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
|
|
|
41347
41439
|
try {
|
|
41348
41440
|
await fs2.mkdir(this.workspaceDir, { recursive: true });
|
|
41349
41441
|
const filename = `snapshot-${Date.now()}-${crypto2.randomUUID().slice(0, 8)}.yaml`;
|
|
41350
|
-
snapshotFilePath =
|
|
41442
|
+
snapshotFilePath = path2.join(this.workspaceDir, filename);
|
|
41351
41443
|
await fs2.writeFile(snapshotFilePath, yaml, "utf-8");
|
|
41352
41444
|
} catch {
|
|
41353
41445
|
}
|
|
41354
|
-
const semanticText = formatSemanticSnapshot(yaml, { snapshotFilePath });
|
|
41446
|
+
const semanticText = formatSemanticSnapshot(yaml, { snapshotFilePath, currentTime: (/* @__PURE__ */ new Date()).toISOString(), capturedToasts: this.client.getCapturedToasts?.() ?? [] });
|
|
41355
41447
|
const yamlBlockPattern = /- Page Snapshot:\n```yaml\n[\s\S]*?```/;
|
|
41356
41448
|
if (yamlBlockPattern.test(result)) {
|
|
41357
41449
|
return result.replace(yamlBlockPattern, `- Page Snapshot (semantic):
|
|
@@ -42586,8 +42678,8 @@ var errors = playwrightLoader.lazyloadExportOrDie("errors");
|
|
|
42586
42678
|
|
|
42587
42679
|
// ../browser-core/src/playwright-client.ts
|
|
42588
42680
|
var import_puppeteer_extra_plugin_stealth = __toESM(require_puppeteer_extra_plugin_stealth(), 1);
|
|
42589
|
-
import * as
|
|
42590
|
-
import * as
|
|
42681
|
+
import * as fs4 from "fs/promises";
|
|
42682
|
+
import * as path4 from "path";
|
|
42591
42683
|
|
|
42592
42684
|
// ../browser-core/src/playwright-client/toast-capture-script.ts
|
|
42593
42685
|
var TOAST_CAPTURE_SCRIPT = `
|
|
@@ -43310,11 +43402,44 @@ async function enrichHiddenClickableElements(page, logger2) {
|
|
|
43310
43402
|
try {
|
|
43311
43403
|
const enrichedCount = await page.evaluate(() => {
|
|
43312
43404
|
let count = 0;
|
|
43313
|
-
const
|
|
43314
|
-
|
|
43405
|
+
const chosenCloseButtons = document.querySelectorAll("abbr.search-choice-close");
|
|
43406
|
+
for (const el of chosenCloseButtons) {
|
|
43407
|
+
if (el.getAttribute("data-enriched-clickable") === "true") continue;
|
|
43408
|
+
if (el.getAttribute("role") === "button") continue;
|
|
43409
|
+
let fieldName;
|
|
43410
|
+
const container = el.closest(".chosen-container");
|
|
43411
|
+
if (container) {
|
|
43412
|
+
const selectEl = container.previousElementSibling;
|
|
43413
|
+
if (selectEl && selectEl.tagName === "SELECT") {
|
|
43414
|
+
const selectId = selectEl.id;
|
|
43415
|
+
if (selectId) {
|
|
43416
|
+
const labelEl = document.querySelector(`label[for="${selectId}"]`);
|
|
43417
|
+
if (labelEl?.textContent?.trim()) {
|
|
43418
|
+
fieldName = labelEl.textContent.trim();
|
|
43419
|
+
}
|
|
43420
|
+
}
|
|
43421
|
+
if (!fieldName) {
|
|
43422
|
+
fieldName = selectEl.getAttribute("aria-label") || void 0;
|
|
43423
|
+
}
|
|
43424
|
+
}
|
|
43425
|
+
if (!fieldName && container.parentElement) {
|
|
43426
|
+
const prev = container.parentElement.previousElementSibling;
|
|
43427
|
+
if (prev && (prev.tagName === "LABEL" || prev.querySelector("label"))) {
|
|
43428
|
+
const labelText = prev.textContent?.trim();
|
|
43429
|
+
if (labelText && labelText.length < 80) fieldName = labelText;
|
|
43430
|
+
}
|
|
43431
|
+
}
|
|
43432
|
+
}
|
|
43433
|
+
const label = fieldName ? `Clear ${fieldName}` : "Clear selection";
|
|
43434
|
+
el.setAttribute("role", "button");
|
|
43435
|
+
el.setAttribute("aria-label", label);
|
|
43436
|
+
el.setAttribute("data-enriched-clickable", "true");
|
|
43437
|
+
count++;
|
|
43438
|
+
}
|
|
43439
|
+
const genericPatterns = [
|
|
43315
43440
|
[".select2-selection__clear", "select2-selection__clear"]
|
|
43316
43441
|
];
|
|
43317
|
-
for (const [selector, fallbackLabel] of
|
|
43442
|
+
for (const [selector, fallbackLabel] of genericPatterns) {
|
|
43318
43443
|
const elements = document.querySelectorAll(selector);
|
|
43319
43444
|
for (const el of elements) {
|
|
43320
43445
|
if (el.getAttribute("data-enriched-clickable") === "true") continue;
|
|
@@ -43326,6 +43451,12 @@ async function enrichHiddenClickableElements(page, logger2) {
|
|
|
43326
43451
|
count++;
|
|
43327
43452
|
}
|
|
43328
43453
|
}
|
|
43454
|
+
const chosenSearchInputs = document.querySelectorAll(
|
|
43455
|
+
".chosen-container:not(.chosen-with-drop) .chosen-search-input"
|
|
43456
|
+
);
|
|
43457
|
+
for (const input of chosenSearchInputs) {
|
|
43458
|
+
input.setAttribute("aria-hidden", "true");
|
|
43459
|
+
}
|
|
43329
43460
|
return count;
|
|
43330
43461
|
});
|
|
43331
43462
|
if (enrichedCount > 0) {
|
|
@@ -43364,10 +43495,29 @@ async function augmentHiddenSelectOptions(page, yaml, logger2) {
|
|
|
43364
43495
|
if (!trigger) continue;
|
|
43365
43496
|
const rect = trigger.getBoundingClientRect();
|
|
43366
43497
|
const triggerText = trigger.textContent?.trim()?.slice(0, 80) || null;
|
|
43498
|
+
let fieldLabel = null;
|
|
43499
|
+
const selectId = sel.id;
|
|
43500
|
+
if (selectId) {
|
|
43501
|
+
const labelEl = document.querySelector(`label[for="${selectId}"]`);
|
|
43502
|
+
if (labelEl?.textContent?.trim()) {
|
|
43503
|
+
fieldLabel = labelEl.textContent.trim();
|
|
43504
|
+
}
|
|
43505
|
+
}
|
|
43506
|
+
if (!fieldLabel) {
|
|
43507
|
+
fieldLabel = sel.getAttribute("aria-label") || null;
|
|
43508
|
+
}
|
|
43509
|
+
if (!fieldLabel && parent.previousElementSibling) {
|
|
43510
|
+
const prev = parent.previousElementSibling;
|
|
43511
|
+
if (prev.tagName === "LABEL" || prev.querySelector("label")) {
|
|
43512
|
+
const labelText = prev.textContent?.trim();
|
|
43513
|
+
if (labelText && labelText.length < 80) fieldLabel = labelText;
|
|
43514
|
+
}
|
|
43515
|
+
}
|
|
43367
43516
|
results.push({
|
|
43368
43517
|
options: opts.slice(0, 15),
|
|
43369
43518
|
triggerText,
|
|
43370
|
-
triggerRect: rect.width > 0 ? { x: rect.x, y: rect.y, width: rect.width, height: rect.height } : null
|
|
43519
|
+
triggerRect: rect.width > 0 ? { x: rect.x, y: rect.y, width: rect.width, height: rect.height } : null,
|
|
43520
|
+
fieldLabel
|
|
43371
43521
|
});
|
|
43372
43522
|
}
|
|
43373
43523
|
return results;
|
|
@@ -43379,7 +43529,7 @@ async function augmentHiddenSelectOptions(page, yaml, logger2) {
|
|
|
43379
43529
|
const elements = parseSnapshot(yaml);
|
|
43380
43530
|
let augmented = yaml;
|
|
43381
43531
|
for (const selectInfo of hiddenSelects) {
|
|
43382
|
-
const { options: opts, triggerText, triggerRect } = selectInfo;
|
|
43532
|
+
const { options: opts, triggerText, triggerRect, fieldLabel } = selectInfo;
|
|
43383
43533
|
if (!opts.length) continue;
|
|
43384
43534
|
let matchedRef = null;
|
|
43385
43535
|
if (triggerText) {
|
|
@@ -43428,11 +43578,13 @@ async function augmentHiddenSelectOptions(page, yaml, logger2) {
|
|
|
43428
43578
|
}
|
|
43429
43579
|
if (!matchedRef) continue;
|
|
43430
43580
|
const optionsValue = opts.join("|");
|
|
43431
|
-
augmented = updateElementLineByRef(
|
|
43432
|
-
|
|
43433
|
-
|
|
43434
|
-
|
|
43435
|
-
|
|
43581
|
+
augmented = updateElementLineByRef(augmented, matchedRef, (line) => {
|
|
43582
|
+
let next = appendAttributeIfMissing(line, "options", optionsValue);
|
|
43583
|
+
if (fieldLabel) {
|
|
43584
|
+
next = appendAttributeIfMissing(next, "field", fieldLabel);
|
|
43585
|
+
}
|
|
43586
|
+
return next;
|
|
43587
|
+
});
|
|
43436
43588
|
}
|
|
43437
43589
|
return augmented;
|
|
43438
43590
|
} catch (err) {
|
|
@@ -43488,38 +43640,6 @@ async function enrichInteractiveSVGElements(page, logger2) {
|
|
|
43488
43640
|
}
|
|
43489
43641
|
}
|
|
43490
43642
|
|
|
43491
|
-
// ../browser-core/src/playwright-client/download-utils.ts
|
|
43492
|
-
import * as path2 from "path";
|
|
43493
|
-
function formatFileSize(bytes) {
|
|
43494
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
43495
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
43496
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
43497
|
-
}
|
|
43498
|
-
function guessMimeType(filename) {
|
|
43499
|
-
const ext = path2.extname(filename).toLowerCase();
|
|
43500
|
-
const mimeMap = {
|
|
43501
|
-
".pdf": "application/pdf",
|
|
43502
|
-
".txt": "text/plain",
|
|
43503
|
-
".csv": "text/csv",
|
|
43504
|
-
".json": "application/json",
|
|
43505
|
-
".xml": "application/xml",
|
|
43506
|
-
".html": "text/html",
|
|
43507
|
-
".htm": "text/html",
|
|
43508
|
-
".md": "text/markdown",
|
|
43509
|
-
".png": "image/png",
|
|
43510
|
-
".jpg": "image/jpeg",
|
|
43511
|
-
".jpeg": "image/jpeg",
|
|
43512
|
-
".gif": "image/gif",
|
|
43513
|
-
".svg": "image/svg+xml",
|
|
43514
|
-
".zip": "application/zip",
|
|
43515
|
-
".doc": "application/msword",
|
|
43516
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
43517
|
-
".xls": "application/vnd.ms-excel",
|
|
43518
|
-
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
43519
|
-
};
|
|
43520
|
-
return mimeMap[ext] ?? "application/octet-stream";
|
|
43521
|
-
}
|
|
43522
|
-
|
|
43523
43643
|
// ../browser-core/src/playwright-client/response-format.ts
|
|
43524
43644
|
var TOAST_SNAPSHOT_TTL = 5;
|
|
43525
43645
|
function formatMcpResponse(input) {
|
|
@@ -43578,10 +43698,172 @@ function formatMcpResponse(input) {
|
|
|
43578
43698
|
};
|
|
43579
43699
|
}
|
|
43580
43700
|
|
|
43701
|
+
// ../browser-core/src/playwright-client/launch-retry.ts
|
|
43702
|
+
var TRANSIENT_BROWSER_LAUNCH_PATTERNS = [
|
|
43703
|
+
/failed to connect/i,
|
|
43704
|
+
/syscall["':\s]+connect/i,
|
|
43705
|
+
/\bENOENT\b/i,
|
|
43706
|
+
/browser has been closed/i,
|
|
43707
|
+
/target page, context or browser has been closed/i,
|
|
43708
|
+
/failed to launch/i,
|
|
43709
|
+
/\bSIGKILL\b/i,
|
|
43710
|
+
/process was terminated/i
|
|
43711
|
+
];
|
|
43712
|
+
function formatBrowserLaunchError(error) {
|
|
43713
|
+
if (error instanceof Error) {
|
|
43714
|
+
const extra = error;
|
|
43715
|
+
const parts = [error.message];
|
|
43716
|
+
if (extra.code) parts.push(`code=${extra.code}`);
|
|
43717
|
+
if (typeof extra.errno === "number") parts.push(`errno=${extra.errno}`);
|
|
43718
|
+
if (extra.syscall) parts.push(`syscall=${extra.syscall}`);
|
|
43719
|
+
if (extra.cause) parts.push(`cause=${formatBrowserLaunchError(extra.cause)}`);
|
|
43720
|
+
return parts.join(" ");
|
|
43721
|
+
}
|
|
43722
|
+
return String(error);
|
|
43723
|
+
}
|
|
43724
|
+
function isTransientBrowserLaunchError(error) {
|
|
43725
|
+
const text = formatBrowserLaunchError(error);
|
|
43726
|
+
return TRANSIENT_BROWSER_LAUNCH_PATTERNS.some((pattern) => pattern.test(text));
|
|
43727
|
+
}
|
|
43728
|
+
async function withBrowserLaunchRetry(operation, options) {
|
|
43729
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
43730
|
+
const initialDelayMs = options.initialDelayMs ?? 150;
|
|
43731
|
+
let lastError;
|
|
43732
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
43733
|
+
try {
|
|
43734
|
+
return await operation(attempt);
|
|
43735
|
+
} catch (error) {
|
|
43736
|
+
lastError = error;
|
|
43737
|
+
if (attempt >= maxAttempts || !isTransientBrowserLaunchError(error)) {
|
|
43738
|
+
throw error;
|
|
43739
|
+
}
|
|
43740
|
+
const delayMs = initialDelayMs * 2 ** (attempt - 1);
|
|
43741
|
+
options.logger.warn("[DirectPlaywright] Browser launch failed transiently, retrying", {
|
|
43742
|
+
label: options.label,
|
|
43743
|
+
attempt,
|
|
43744
|
+
nextAttempt: attempt + 1,
|
|
43745
|
+
delayMs,
|
|
43746
|
+
error: formatBrowserLaunchError(error)
|
|
43747
|
+
});
|
|
43748
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
43749
|
+
}
|
|
43750
|
+
}
|
|
43751
|
+
throw lastError;
|
|
43752
|
+
}
|
|
43753
|
+
|
|
43754
|
+
// ../browser-core/src/playwright-client/test-browser-lock.ts
|
|
43755
|
+
import * as fs3 from "fs/promises";
|
|
43756
|
+
import * as path3 from "path";
|
|
43757
|
+
var TEST_BROWSER_LOCK_DIR = path3.join(getCanaryTmpDir(), "playwright-test-browser-lock");
|
|
43758
|
+
var TEST_BROWSER_LOCK_METADATA_PATH = path3.join(TEST_BROWSER_LOCK_DIR, "owner.json");
|
|
43759
|
+
var TEST_BROWSER_LOCK_POLL_MS = 100;
|
|
43760
|
+
var TEST_BROWSER_LOCK_TIMEOUT_MS = 12e4;
|
|
43761
|
+
var TEST_BROWSER_LOCK_STALE_MS = 15 * 6e4;
|
|
43762
|
+
function shouldUseTestBrowserLock(options) {
|
|
43763
|
+
return process.env.NODE_ENV === "test" && !options.browserLease && !options.cdpUrl && options.browserMode !== "headed";
|
|
43764
|
+
}
|
|
43765
|
+
async function acquireTestBrowserLock(logger2) {
|
|
43766
|
+
const startedAt = Date.now();
|
|
43767
|
+
const owner = {
|
|
43768
|
+
pid: process.pid,
|
|
43769
|
+
acquiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
43770
|
+
};
|
|
43771
|
+
while (true) {
|
|
43772
|
+
try {
|
|
43773
|
+
await fs3.mkdir(TEST_BROWSER_LOCK_DIR);
|
|
43774
|
+
await fs3.writeFile(TEST_BROWSER_LOCK_METADATA_PATH, JSON.stringify(owner), "utf8");
|
|
43775
|
+
logger2.debug("[DirectPlaywright] Acquired test browser lock", {
|
|
43776
|
+
lockDir: TEST_BROWSER_LOCK_DIR,
|
|
43777
|
+
pid: process.pid
|
|
43778
|
+
});
|
|
43779
|
+
let released = false;
|
|
43780
|
+
return async () => {
|
|
43781
|
+
if (released) return;
|
|
43782
|
+
released = true;
|
|
43783
|
+
try {
|
|
43784
|
+
await fs3.rm(TEST_BROWSER_LOCK_DIR, { recursive: true, force: true });
|
|
43785
|
+
logger2.debug("[DirectPlaywright] Released test browser lock", {
|
|
43786
|
+
lockDir: TEST_BROWSER_LOCK_DIR,
|
|
43787
|
+
pid: process.pid
|
|
43788
|
+
});
|
|
43789
|
+
} catch (error) {
|
|
43790
|
+
logger2.warn("[DirectPlaywright] Failed to release test browser lock", {
|
|
43791
|
+
lockDir: TEST_BROWSER_LOCK_DIR,
|
|
43792
|
+
pid: process.pid,
|
|
43793
|
+
error: error instanceof Error ? error.message : String(error)
|
|
43794
|
+
});
|
|
43795
|
+
}
|
|
43796
|
+
};
|
|
43797
|
+
} catch (error) {
|
|
43798
|
+
const code = error.code;
|
|
43799
|
+
if (code !== "EEXIST") {
|
|
43800
|
+
throw error;
|
|
43801
|
+
}
|
|
43802
|
+
await cleanupStaleTestBrowserLock(logger2);
|
|
43803
|
+
if (Date.now() - startedAt >= TEST_BROWSER_LOCK_TIMEOUT_MS) {
|
|
43804
|
+
throw new Error(
|
|
43805
|
+
`Timed out acquiring test browser lock after ${TEST_BROWSER_LOCK_TIMEOUT_MS}ms`
|
|
43806
|
+
);
|
|
43807
|
+
}
|
|
43808
|
+
await delay(TEST_BROWSER_LOCK_POLL_MS);
|
|
43809
|
+
}
|
|
43810
|
+
}
|
|
43811
|
+
}
|
|
43812
|
+
async function cleanupStaleTestBrowserLock(logger2) {
|
|
43813
|
+
const metadata = await readLockMetadata();
|
|
43814
|
+
if (!metadata) {
|
|
43815
|
+
const orphanedLockStats = await fs3.stat(TEST_BROWSER_LOCK_DIR).catch(() => null);
|
|
43816
|
+
if (!orphanedLockStats) {
|
|
43817
|
+
return;
|
|
43818
|
+
}
|
|
43819
|
+
const orphanedAgeMs = Date.now() - orphanedLockStats.mtimeMs;
|
|
43820
|
+
if (orphanedAgeMs < 1e3) {
|
|
43821
|
+
return;
|
|
43822
|
+
}
|
|
43823
|
+
logger2.warn("[DirectPlaywright] Removing orphaned test browser lock", {
|
|
43824
|
+
lockDir: TEST_BROWSER_LOCK_DIR,
|
|
43825
|
+
ageMs: orphanedAgeMs
|
|
43826
|
+
});
|
|
43827
|
+
await fs3.rm(TEST_BROWSER_LOCK_DIR, { recursive: true, force: true });
|
|
43828
|
+
return;
|
|
43829
|
+
}
|
|
43830
|
+
const ageMs = Date.now() - Date.parse(metadata.acquiredAt);
|
|
43831
|
+
if (ageMs < TEST_BROWSER_LOCK_STALE_MS && isProcessAlive(metadata.pid)) {
|
|
43832
|
+
return;
|
|
43833
|
+
}
|
|
43834
|
+
logger2.warn("[DirectPlaywright] Removing stale test browser lock", {
|
|
43835
|
+
lockDir: TEST_BROWSER_LOCK_DIR,
|
|
43836
|
+
ownerPid: metadata.pid,
|
|
43837
|
+
ageMs
|
|
43838
|
+
});
|
|
43839
|
+
await fs3.rm(TEST_BROWSER_LOCK_DIR, { recursive: true, force: true });
|
|
43840
|
+
}
|
|
43841
|
+
async function readLockMetadata() {
|
|
43842
|
+
try {
|
|
43843
|
+
const raw = await fs3.readFile(TEST_BROWSER_LOCK_METADATA_PATH, "utf8");
|
|
43844
|
+
return JSON.parse(raw);
|
|
43845
|
+
} catch {
|
|
43846
|
+
return null;
|
|
43847
|
+
}
|
|
43848
|
+
}
|
|
43849
|
+
function isProcessAlive(pid) {
|
|
43850
|
+
try {
|
|
43851
|
+
process.kill(pid, 0);
|
|
43852
|
+
return true;
|
|
43853
|
+
} catch (error) {
|
|
43854
|
+
const code = error.code;
|
|
43855
|
+
return code !== "ESRCH";
|
|
43856
|
+
}
|
|
43857
|
+
}
|
|
43858
|
+
function delay(ms) {
|
|
43859
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
43860
|
+
}
|
|
43861
|
+
|
|
43581
43862
|
// ../browser-core/src/playwright-client.ts
|
|
43582
43863
|
var PlaywrightClient = class _PlaywrightClient {
|
|
43583
43864
|
disconnectInProgress = false;
|
|
43584
43865
|
browserLease;
|
|
43866
|
+
releaseTestBrowserLock = null;
|
|
43585
43867
|
logger;
|
|
43586
43868
|
screencastEncoderFactory;
|
|
43587
43869
|
tmpBaseDir;
|
|
@@ -43743,8 +44025,8 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
43743
44025
|
const cursorOpts = typeof cursorOverlay === "object" ? cursorOverlay : {};
|
|
43744
44026
|
this.cursorOverlay = new CursorOverlay({ ...cursorOpts, enabled: cursorEnabled });
|
|
43745
44027
|
}
|
|
43746
|
-
this._downloadDir =
|
|
43747
|
-
await
|
|
44028
|
+
this._downloadDir = path4.join(this.tmpBaseDir, `playwright-downloads-${Date.now()}`);
|
|
44029
|
+
await fs4.mkdir(this._downloadDir, { recursive: true });
|
|
43748
44030
|
this.logger.info("[DirectPlaywright] Launching browser", {
|
|
43749
44031
|
browserMode: resolvedBrowserMode,
|
|
43750
44032
|
hasStorageState: !!storageStatePath,
|
|
@@ -43755,82 +44037,100 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
43755
44037
|
highlightEnabled: this.highlightEnabled,
|
|
43756
44038
|
downloadDir: this._downloadDir
|
|
43757
44039
|
});
|
|
43758
|
-
|
|
43759
|
-
|
|
43760
|
-
|
|
43761
|
-
|
|
43762
|
-
|
|
43763
|
-
|
|
43764
|
-
|
|
43765
|
-
|
|
43766
|
-
|
|
43767
|
-
|
|
43768
|
-
|
|
43769
|
-
|
|
43770
|
-
|
|
43771
|
-
|
|
43772
|
-
|
|
43773
|
-
|
|
43774
|
-
|
|
43775
|
-
|
|
43776
|
-
|
|
43777
|
-
|
|
44040
|
+
try {
|
|
44041
|
+
if (shouldUseTestBrowserLock({
|
|
44042
|
+
browserLease: !!this.browserLease,
|
|
44043
|
+
cdpUrl,
|
|
44044
|
+
browserMode: resolvedBrowserMode
|
|
44045
|
+
}) && !this.releaseTestBrowserLock) {
|
|
44046
|
+
this.releaseTestBrowserLock = await acquireTestBrowserLock(this.logger);
|
|
44047
|
+
}
|
|
44048
|
+
if (cdpUrl) {
|
|
44049
|
+
this.logger.info("[DirectPlaywright] Connecting via CDP", { cdpUrl });
|
|
44050
|
+
this.browser = await this.withOperationTimeout(
|
|
44051
|
+
playwrightChromium.connectOverCDP(cdpUrl),
|
|
44052
|
+
3e4,
|
|
44053
|
+
"[DirectPlaywright] connectOverCDP"
|
|
44054
|
+
);
|
|
44055
|
+
const existingContexts = this.browser.contexts();
|
|
44056
|
+
this.context = existingContexts[0] ?? await this.browser.newContext({ viewport: resolvedViewport });
|
|
44057
|
+
} else if (this.browserLease && !this.stealthEnabled && !this.extensionsEnabled && resolvedBrowserMode !== "headed") {
|
|
44058
|
+
const videoConfig2 = this.resolveVideoConfig(recordVideo);
|
|
44059
|
+
const contextOptions = {
|
|
44060
|
+
viewport: resolvedViewport,
|
|
44061
|
+
acceptDownloads: true
|
|
44062
|
+
};
|
|
44063
|
+
if (storageStatePath) {
|
|
44064
|
+
try {
|
|
44065
|
+
await fs4.access(storageStatePath);
|
|
44066
|
+
contextOptions.storageState = storageStatePath;
|
|
44067
|
+
} catch {
|
|
44068
|
+
}
|
|
43778
44069
|
}
|
|
43779
|
-
|
|
43780
|
-
|
|
43781
|
-
|
|
43782
|
-
|
|
43783
|
-
|
|
43784
|
-
|
|
43785
|
-
|
|
43786
|
-
|
|
43787
|
-
|
|
43788
|
-
this.logger.info("[DirectPlaywright] Using pooled browser via lease", {
|
|
43789
|
-
leaseId: this.browserLease.leaseId,
|
|
43790
|
-
browserId: this.browserLease.browserId
|
|
43791
|
-
});
|
|
43792
|
-
this.context = await this.withOperationTimeout(
|
|
43793
|
-
this.browserLease.createContext(contextOptions),
|
|
43794
|
-
3e4,
|
|
43795
|
-
"[DirectPlaywright] browserLease.createContext"
|
|
43796
|
-
);
|
|
43797
|
-
this.browser = null;
|
|
43798
|
-
} else {
|
|
43799
|
-
if (this.browserLease) {
|
|
43800
|
-
this.logger.info("[DirectPlaywright] Releasing pool lease for direct launch", {
|
|
44070
|
+
if (videoConfig2) {
|
|
44071
|
+
await fs4.mkdir(videoConfig2.dir, { recursive: true });
|
|
44072
|
+
contextOptions.recordVideo = videoConfig2;
|
|
44073
|
+
this.videoDir = videoConfig2.dir;
|
|
44074
|
+
}
|
|
44075
|
+
if (extraHTTPHeaders && Object.keys(extraHTTPHeaders).length > 0) {
|
|
44076
|
+
contextOptions.extraHTTPHeaders = extraHTTPHeaders;
|
|
44077
|
+
}
|
|
44078
|
+
this.logger.info("[DirectPlaywright] Using pooled browser via lease", {
|
|
43801
44079
|
leaseId: this.browserLease.leaseId,
|
|
43802
|
-
|
|
44080
|
+
browserId: this.browserLease.browserId
|
|
43803
44081
|
});
|
|
43804
|
-
await this.
|
|
43805
|
-
|
|
44082
|
+
this.context = await this.withOperationTimeout(
|
|
44083
|
+
this.browserLease.createContext(contextOptions),
|
|
44084
|
+
3e4,
|
|
44085
|
+
"[DirectPlaywright] browserLease.createContext"
|
|
44086
|
+
);
|
|
44087
|
+
this.browser = null;
|
|
44088
|
+
} else {
|
|
44089
|
+
if (this.browserLease) {
|
|
44090
|
+
this.logger.info("[DirectPlaywright] Releasing pool lease for direct launch", {
|
|
44091
|
+
leaseId: this.browserLease.leaseId,
|
|
44092
|
+
reason: resolvedBrowserMode === "headed" ? "headed-mode" : "requires-dedicated-browser"
|
|
44093
|
+
});
|
|
44094
|
+
await this.browserLease.release();
|
|
44095
|
+
this.browserLease = null;
|
|
44096
|
+
}
|
|
44097
|
+
const { browser, context } = await withBrowserLaunchRetry(
|
|
44098
|
+
() => this.launchBrowser({
|
|
44099
|
+
browserMode: resolvedBrowserMode,
|
|
44100
|
+
storageStatePath,
|
|
44101
|
+
viewport,
|
|
44102
|
+
recordVideo,
|
|
44103
|
+
stealth,
|
|
44104
|
+
extensions,
|
|
44105
|
+
extraHTTPHeaders
|
|
44106
|
+
}),
|
|
44107
|
+
{
|
|
44108
|
+
logger: this.logger,
|
|
44109
|
+
label: "direct-launch"
|
|
44110
|
+
}
|
|
44111
|
+
);
|
|
44112
|
+
this.browser = browser;
|
|
44113
|
+
this.context = context;
|
|
43806
44114
|
}
|
|
43807
|
-
|
|
44115
|
+
this.attachContextListeners(this.context);
|
|
44116
|
+
if (this.browser) {
|
|
44117
|
+
this.attachBrowserListeners(this.browser);
|
|
44118
|
+
}
|
|
44119
|
+
await this.createPageWithListeners(this.context, "initial_connect");
|
|
44120
|
+
const now = Date.now();
|
|
44121
|
+
const initialPageId = this._assignPageId(this.page);
|
|
44122
|
+
this._pageCreationTimes.set(initialPageId, now);
|
|
44123
|
+
this._tabFocusLog.push({ pageIndex: initialPageId, timestamp: now });
|
|
44124
|
+
this.logger.info("[DirectPlaywright] Browser connected successfully", {
|
|
43808
44125
|
browserMode: resolvedBrowserMode,
|
|
43809
|
-
|
|
43810
|
-
|
|
43811
|
-
|
|
43812
|
-
stealth,
|
|
43813
|
-
extensions,
|
|
43814
|
-
extraHTTPHeaders
|
|
44126
|
+
stealthEnabled: this.stealthEnabled,
|
|
44127
|
+
extensionsEnabled: this.extensionsEnabled,
|
|
44128
|
+
pooled: !!this.browserLease
|
|
43815
44129
|
});
|
|
43816
|
-
|
|
43817
|
-
this.
|
|
43818
|
-
|
|
43819
|
-
this.attachContextListeners(this.context);
|
|
43820
|
-
if (this.browser) {
|
|
43821
|
-
this.attachBrowserListeners(this.browser);
|
|
44130
|
+
} catch (error) {
|
|
44131
|
+
await this.cleanupFailedConnect(error);
|
|
44132
|
+
throw error;
|
|
43822
44133
|
}
|
|
43823
|
-
await this.createPageWithListeners(this.context, "initial_connect");
|
|
43824
|
-
const now = Date.now();
|
|
43825
|
-
const initialPageId = this._assignPageId(this.page);
|
|
43826
|
-
this._pageCreationTimes.set(initialPageId, now);
|
|
43827
|
-
this._tabFocusLog.push({ pageIndex: initialPageId, timestamp: now });
|
|
43828
|
-
this.logger.info("[DirectPlaywright] Browser connected successfully", {
|
|
43829
|
-
browserMode: resolvedBrowserMode,
|
|
43830
|
-
stealthEnabled: this.stealthEnabled,
|
|
43831
|
-
extensionsEnabled: this.extensionsEnabled,
|
|
43832
|
-
pooled: !!this.browserLease
|
|
43833
|
-
});
|
|
43834
44134
|
}
|
|
43835
44135
|
supportsContextSwap() {
|
|
43836
44136
|
return !this.extensionsEnabled && (!!this.browser || !!this.browserLease);
|
|
@@ -43902,7 +44202,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
43902
44202
|
}
|
|
43903
44203
|
if (nextStorageStatePath) {
|
|
43904
44204
|
try {
|
|
43905
|
-
await
|
|
44205
|
+
await fs4.access(nextStorageStatePath);
|
|
43906
44206
|
contextOptions.storageState = nextStorageStatePath;
|
|
43907
44207
|
this.logger.debug("[DirectPlaywright] Loading storage state for swapped context", {
|
|
43908
44208
|
storageStatePath: nextStorageStatePath
|
|
@@ -43923,7 +44223,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
43923
44223
|
this._pageCreationTimes.clear();
|
|
43924
44224
|
this._closedPageVideos = [];
|
|
43925
44225
|
if (videoConfig) {
|
|
43926
|
-
await
|
|
44226
|
+
await fs4.mkdir(videoConfig.dir, { recursive: true });
|
|
43927
44227
|
contextOptions.recordVideo = videoConfig;
|
|
43928
44228
|
this.videoDir = videoConfig.dir;
|
|
43929
44229
|
this.highlightEnabled = true;
|
|
@@ -43980,6 +44280,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
43980
44280
|
this.logger.debug("[DirectPlaywright] Stealth plugin applied");
|
|
43981
44281
|
}
|
|
43982
44282
|
const chromiumLauncher = useStealthMode ? chromium : playwrightChromium;
|
|
44283
|
+
const executablePath = await _PlaywrightClient.resolveChromiumExecutable(this.logger);
|
|
43983
44284
|
const args = ["--no-sandbox", "--disable-dev-shm-usage"];
|
|
43984
44285
|
if (browserMode === "headed" && process.platform === "darwin") {
|
|
43985
44286
|
args.push(
|
|
@@ -44001,7 +44302,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44001
44302
|
}
|
|
44002
44303
|
if (storageStatePath) {
|
|
44003
44304
|
try {
|
|
44004
|
-
await
|
|
44305
|
+
await fs4.access(storageStatePath);
|
|
44005
44306
|
contextOptions.storageState = storageStatePath;
|
|
44006
44307
|
this.logger.debug("[DirectPlaywright] Loading storage state from file", {
|
|
44007
44308
|
storageStatePath
|
|
@@ -44013,7 +44314,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44013
44314
|
}
|
|
44014
44315
|
}
|
|
44015
44316
|
if (videoConfig) {
|
|
44016
|
-
await
|
|
44317
|
+
await fs4.mkdir(videoConfig.dir, { recursive: true });
|
|
44017
44318
|
contextOptions.recordVideo = videoConfig;
|
|
44018
44319
|
this.videoDir = videoConfig.dir;
|
|
44019
44320
|
this.logger.info("[DirectPlaywright] Video recording enabled", {
|
|
@@ -44022,9 +44323,9 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44022
44323
|
});
|
|
44023
44324
|
}
|
|
44024
44325
|
if (hasExtensions) {
|
|
44025
|
-
this.userDataDir = extensions.userDataDir ||
|
|
44326
|
+
this.userDataDir = extensions.userDataDir || path4.join(this.tmpBaseDir, `playwright-userdata-${Date.now()}`);
|
|
44026
44327
|
this.shouldCleanupUserDataDir = !extensions.persistUserData;
|
|
44027
|
-
await
|
|
44328
|
+
await fs4.mkdir(this.userDataDir, { recursive: true });
|
|
44028
44329
|
if (browserMode === "headless") {
|
|
44029
44330
|
args.push("--headless=new");
|
|
44030
44331
|
}
|
|
@@ -44040,6 +44341,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44040
44341
|
headless: false,
|
|
44041
44342
|
// We use --headless=new in args for extension support
|
|
44042
44343
|
args,
|
|
44344
|
+
...executablePath ? { executablePath } : {},
|
|
44043
44345
|
...contextOptions
|
|
44044
44346
|
}),
|
|
44045
44347
|
3e4,
|
|
@@ -44050,7 +44352,8 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44050
44352
|
const browser = await this.withOperationTimeout(
|
|
44051
44353
|
chromiumLauncher.launch({
|
|
44052
44354
|
headless: browserMode === "headless",
|
|
44053
|
-
args
|
|
44355
|
+
args,
|
|
44356
|
+
...executablePath ? { executablePath } : {}
|
|
44054
44357
|
}),
|
|
44055
44358
|
3e4,
|
|
44056
44359
|
"[DirectPlaywright] chromium.launch"
|
|
@@ -44062,6 +44365,68 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44062
44365
|
);
|
|
44063
44366
|
return { browser, context };
|
|
44064
44367
|
}
|
|
44368
|
+
/**
|
|
44369
|
+
* Resolve a working Chromium executable path, handling version mismatches
|
|
44370
|
+
* between the Playwright version bundled in this package and the browser
|
|
44371
|
+
* binaries installed on the system.
|
|
44372
|
+
*
|
|
44373
|
+
* When the exact expected binary doesn't exist (e.g. Playwright expects
|
|
44374
|
+
* chromium-1191 but only chromium-1194 is installed), this finds the
|
|
44375
|
+
* newest available chromium binary as a fallback.
|
|
44376
|
+
*/
|
|
44377
|
+
static async resolveChromiumExecutable(logger2) {
|
|
44378
|
+
const defaultPath = playwrightChromium.executablePath();
|
|
44379
|
+
try {
|
|
44380
|
+
await fs4.access(defaultPath);
|
|
44381
|
+
return void 0;
|
|
44382
|
+
} catch {
|
|
44383
|
+
}
|
|
44384
|
+
const parts = defaultPath.split(path4.sep);
|
|
44385
|
+
const msPlaywrightIdx = parts.findIndex((p) => p === "ms-playwright");
|
|
44386
|
+
if (msPlaywrightIdx === -1) {
|
|
44387
|
+
logger2.warn("[DirectPlaywright] Cannot locate ms-playwright cache directory", {
|
|
44388
|
+
defaultPath
|
|
44389
|
+
});
|
|
44390
|
+
return void 0;
|
|
44391
|
+
}
|
|
44392
|
+
const cacheDir = parts.slice(0, msPlaywrightIdx + 1).join(path4.sep);
|
|
44393
|
+
const relativeBinaryPath = parts.slice(msPlaywrightIdx + 2).join(path4.sep);
|
|
44394
|
+
try {
|
|
44395
|
+
const entries = await fs4.readdir(cacheDir);
|
|
44396
|
+
const chromiumDirs = entries.filter((e) => e.startsWith("chromium-") && !e.includes("headless")).sort((a, b) => {
|
|
44397
|
+
const revA = parseInt(a.split("-")[1] ?? "0", 10);
|
|
44398
|
+
const revB = parseInt(b.split("-")[1] ?? "0", 10);
|
|
44399
|
+
return revB - revA;
|
|
44400
|
+
});
|
|
44401
|
+
for (const dir of chromiumDirs) {
|
|
44402
|
+
const candidate = path4.join(cacheDir, dir, relativeBinaryPath);
|
|
44403
|
+
try {
|
|
44404
|
+
await fs4.access(candidate);
|
|
44405
|
+
logger2.warn(
|
|
44406
|
+
"[DirectPlaywright] Using fallback Chromium binary (version mismatch)",
|
|
44407
|
+
{
|
|
44408
|
+
expected: defaultPath,
|
|
44409
|
+
using: candidate
|
|
44410
|
+
}
|
|
44411
|
+
);
|
|
44412
|
+
return candidate;
|
|
44413
|
+
} catch {
|
|
44414
|
+
continue;
|
|
44415
|
+
}
|
|
44416
|
+
}
|
|
44417
|
+
logger2.warn("[DirectPlaywright] No compatible Chromium binary found", {
|
|
44418
|
+
cacheDir,
|
|
44419
|
+
candidates: chromiumDirs,
|
|
44420
|
+
expectedRelativePath: relativeBinaryPath
|
|
44421
|
+
});
|
|
44422
|
+
} catch (err) {
|
|
44423
|
+
logger2.warn("[DirectPlaywright] Failed to scan browser cache directory", {
|
|
44424
|
+
cacheDir,
|
|
44425
|
+
error: String(err)
|
|
44426
|
+
});
|
|
44427
|
+
}
|
|
44428
|
+
return void 0;
|
|
44429
|
+
}
|
|
44065
44430
|
async disconnect() {
|
|
44066
44431
|
this.logger.info("[DirectPlaywright] Disconnecting");
|
|
44067
44432
|
this.disconnectInProgress = true;
|
|
@@ -44121,7 +44486,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44121
44486
|
this.lastSnapshotYaml = "";
|
|
44122
44487
|
if (this._downloadDir) {
|
|
44123
44488
|
try {
|
|
44124
|
-
await
|
|
44489
|
+
await fs4.rm(this._downloadDir, { recursive: true, force: true });
|
|
44125
44490
|
this.logger.debug("[DirectPlaywright] Cleaned up download directory", {
|
|
44126
44491
|
downloadDir: this._downloadDir
|
|
44127
44492
|
});
|
|
@@ -44133,12 +44498,26 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44133
44498
|
}
|
|
44134
44499
|
this._downloadDir = null;
|
|
44135
44500
|
}
|
|
44501
|
+
if (this.traceDir) {
|
|
44502
|
+
try {
|
|
44503
|
+
await fs4.rm(this.traceDir, { recursive: true, force: true });
|
|
44504
|
+
this.logger.debug("[DirectPlaywright] Cleaned up trace directory", {
|
|
44505
|
+
traceDir: this.traceDir
|
|
44506
|
+
});
|
|
44507
|
+
} catch (err) {
|
|
44508
|
+
this.logger.warn("[DirectPlaywright] Failed to clean up trace directory", {
|
|
44509
|
+
traceDir: this.traceDir,
|
|
44510
|
+
error: err instanceof Error ? err.message : String(err)
|
|
44511
|
+
});
|
|
44512
|
+
}
|
|
44513
|
+
this.traceDir = null;
|
|
44514
|
+
}
|
|
44136
44515
|
this._capturedDownloads = [];
|
|
44137
44516
|
this._downloadCounter = 0;
|
|
44138
44517
|
this._lastReportedDownloadIndex = 0;
|
|
44139
44518
|
if (this.shouldCleanupUserDataDir && this.userDataDir) {
|
|
44140
44519
|
try {
|
|
44141
|
-
await
|
|
44520
|
+
await fs4.rm(this.userDataDir, { recursive: true, force: true });
|
|
44142
44521
|
this.logger.debug("[DirectPlaywright] Cleaned up user data directory", {
|
|
44143
44522
|
userDataDir: this.userDataDir
|
|
44144
44523
|
});
|
|
@@ -44152,7 +44531,7 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44152
44531
|
if (this.snapshotFilePaths.size > 0) {
|
|
44153
44532
|
for (const filePath of this.snapshotFilePaths) {
|
|
44154
44533
|
try {
|
|
44155
|
-
await
|
|
44534
|
+
await fs4.unlink(filePath);
|
|
44156
44535
|
} catch (err) {
|
|
44157
44536
|
this.logger.warn("[DirectPlaywright] Failed to clean up snapshot file", {
|
|
44158
44537
|
filePath,
|
|
@@ -44171,11 +44550,33 @@ var PlaywrightClient = class _PlaywrightClient {
|
|
|
44171
44550
|
this.stealthEnabled = false;
|
|
44172
44551
|
this.logger.info("[DirectPlaywright] Disconnected");
|
|
44173
44552
|
} finally {
|
|
44553
|
+
await this.releaseTestBrowserLockIfHeld();
|
|
44174
44554
|
this.contextClosedIntentionally = false;
|
|
44175
44555
|
this.pageClosedIntentionally = false;
|
|
44176
44556
|
this.disconnectInProgress = false;
|
|
44177
44557
|
}
|
|
44178
44558
|
}
|
|
44559
|
+
async cleanupFailedConnect(error) {
|
|
44560
|
+
this.logger.warn("[DirectPlaywright] Cleaning up after failed connect", {
|
|
44561
|
+
error: error instanceof Error ? error.message : String(error)
|
|
44562
|
+
});
|
|
44563
|
+
try {
|
|
44564
|
+
await this.disconnect();
|
|
44565
|
+
} catch (disconnectError) {
|
|
44566
|
+
this.logger.warn("[DirectPlaywright] Failed cleanup after connect error", {
|
|
44567
|
+
error: disconnectError instanceof Error ? disconnectError.message : String(disconnectError)
|
|
44568
|
+
});
|
|
44569
|
+
await this.releaseTestBrowserLockIfHeld();
|
|
44570
|
+
}
|
|
44571
|
+
}
|
|
44572
|
+
async releaseTestBrowserLockIfHeld() {
|
|
44573
|
+
if (!this.releaseTestBrowserLock) {
|
|
44574
|
+
return;
|
|
44575
|
+
}
|
|
44576
|
+
const release = this.releaseTestBrowserLock;
|
|
44577
|
+
this.releaseTestBrowserLock = null;
|
|
44578
|
+
await release();
|
|
44579
|
+
}
|
|
44179
44580
|
// ================ NAVIGATION ================
|
|
44180
44581
|
async navigate(url, opts) {
|
|
44181
44582
|
const dialogMsg = this.getPendingDialogMessage();
|
|
@@ -44448,8 +44849,8 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
44448
44849
|
}
|
|
44449
44850
|
const sampleUrls = pendingRequests.slice(0, 3).map((url) => {
|
|
44450
44851
|
try {
|
|
44451
|
-
const
|
|
44452
|
-
return
|
|
44852
|
+
const path5 = new URL(url).pathname + new URL(url).search;
|
|
44853
|
+
return path5.length > 60 ? path5.slice(0, 57) + "..." : path5;
|
|
44453
44854
|
} catch {
|
|
44454
44855
|
return url.length > 60 ? url.slice(0, 57) + "..." : url;
|
|
44455
44856
|
}
|
|
@@ -45124,7 +45525,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
45124
45525
|
const video = closingPage.video();
|
|
45125
45526
|
if (video) {
|
|
45126
45527
|
const videoFileName = `video-tab${closePageId}-${Date.now()}.webm`;
|
|
45127
|
-
const savePath =
|
|
45528
|
+
const savePath = path4.join(this.videoDir, videoFileName);
|
|
45128
45529
|
await closingPage.close();
|
|
45129
45530
|
await video.saveAs(savePath);
|
|
45130
45531
|
const createdAt = this._pageCreationTimes.get(closePageId) ?? Date.now();
|
|
@@ -45221,8 +45622,8 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
45221
45622
|
const timestamp = Date.now();
|
|
45222
45623
|
const shortId = crypto.randomUUID().slice(0, 8);
|
|
45223
45624
|
const filename = `snapshot-${timestamp}-${shortId}.yaml`;
|
|
45224
|
-
const filePath =
|
|
45225
|
-
await
|
|
45625
|
+
const filePath = path4.join(this.tmpBaseDir, filename);
|
|
45626
|
+
await fs4.writeFile(filePath, yaml, "utf-8");
|
|
45226
45627
|
this.snapshotFilePaths.add(filePath);
|
|
45227
45628
|
this.logger.debug("[DirectPlaywright] Wrote snapshot to disk", {
|
|
45228
45629
|
filePath,
|
|
@@ -45366,8 +45767,8 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
45366
45767
|
async startTracing(_opts) {
|
|
45367
45768
|
const context = await this.getContext();
|
|
45368
45769
|
this.logger.info("[DirectPlaywright] Starting trace recording");
|
|
45369
|
-
this.traceDir =
|
|
45370
|
-
await
|
|
45770
|
+
this.traceDir = path4.join(this.tmpBaseDir, `playwright-trace-${Date.now()}`);
|
|
45771
|
+
await fs4.mkdir(this.traceDir, { recursive: true });
|
|
45371
45772
|
await context.tracing.start({ screenshots: true, snapshots: true });
|
|
45372
45773
|
this.tracingActive = true;
|
|
45373
45774
|
}
|
|
@@ -45383,7 +45784,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
45383
45784
|
const context = await this.getContext();
|
|
45384
45785
|
if (!this.traceDir) throw new Error("Tracing not started");
|
|
45385
45786
|
this.logger.info("[DirectPlaywright] Stopping trace recording");
|
|
45386
|
-
const tracePath =
|
|
45787
|
+
const tracePath = path4.join(this.traceDir, "trace.zip");
|
|
45387
45788
|
await context.tracing.stop({ path: tracePath });
|
|
45388
45789
|
this.tracingActive = false;
|
|
45389
45790
|
return {
|
|
@@ -46047,18 +46448,18 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46047
46448
|
this.logger.error("[DirectPlaywright] Download failed: no download directory", { id });
|
|
46048
46449
|
return;
|
|
46049
46450
|
}
|
|
46050
|
-
const savePath =
|
|
46451
|
+
const savePath = path4.join(this._downloadDir, `${id}-${suggestedFilename}`);
|
|
46051
46452
|
download.saveAs(savePath).then(
|
|
46052
46453
|
async () => {
|
|
46053
46454
|
try {
|
|
46054
|
-
const
|
|
46455
|
+
const stat3 = await fs4.stat(savePath);
|
|
46055
46456
|
entry.savedPath = savePath;
|
|
46056
|
-
entry.sizeBytes =
|
|
46457
|
+
entry.sizeBytes = stat3.size;
|
|
46057
46458
|
entry.completed = true;
|
|
46058
46459
|
this.logger.info("[DirectPlaywright] Download completed", {
|
|
46059
46460
|
id,
|
|
46060
46461
|
suggestedFilename,
|
|
46061
|
-
sizeBytes:
|
|
46462
|
+
sizeBytes: stat3.size,
|
|
46062
46463
|
savedPath: savePath
|
|
46063
46464
|
});
|
|
46064
46465
|
} catch {
|
|
@@ -46200,13 +46601,13 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46200
46601
|
};
|
|
46201
46602
|
if (ssp) {
|
|
46202
46603
|
try {
|
|
46203
|
-
await
|
|
46604
|
+
await fs4.access(ssp);
|
|
46204
46605
|
ctxOpts.storageState = ssp;
|
|
46205
46606
|
} catch {
|
|
46206
46607
|
}
|
|
46207
46608
|
}
|
|
46208
46609
|
if (videoConfig2) {
|
|
46209
|
-
await
|
|
46610
|
+
await fs4.mkdir(videoConfig2.dir, { recursive: true });
|
|
46210
46611
|
ctxOpts.recordVideo = videoConfig2;
|
|
46211
46612
|
}
|
|
46212
46613
|
if (headers && Object.keys(headers).length > 0) {
|
|
@@ -46232,7 +46633,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46232
46633
|
}
|
|
46233
46634
|
if (this.shouldCleanupUserDataDir && this.userDataDir) {
|
|
46234
46635
|
try {
|
|
46235
|
-
await
|
|
46636
|
+
await fs4.rm(this.userDataDir, { recursive: true, force: true });
|
|
46236
46637
|
} catch {
|
|
46237
46638
|
}
|
|
46238
46639
|
this.userDataDir = null;
|
|
@@ -46321,7 +46722,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46321
46722
|
*/
|
|
46322
46723
|
resolveVideoConfig(recordVideo) {
|
|
46323
46724
|
if (!recordVideo) return null;
|
|
46324
|
-
const dir = recordVideo === true || !recordVideo.dir ?
|
|
46725
|
+
const dir = recordVideo === true || !recordVideo.dir ? path4.join(this.tmpBaseDir, `playwright-video-${Date.now()}`) : recordVideo.dir;
|
|
46325
46726
|
const size = recordVideo !== true && recordVideo.size ? recordVideo.size : DEFAULT_VIEWPORT;
|
|
46326
46727
|
return { dir, size };
|
|
46327
46728
|
}
|
|
@@ -46380,7 +46781,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46380
46781
|
for (const handle of videoHandles) {
|
|
46381
46782
|
try {
|
|
46382
46783
|
const videoFileName = `video-tab${handle.pageIndex}-${timestamp}.webm`;
|
|
46383
|
-
const savePath =
|
|
46784
|
+
const savePath = path4.join(this.videoDir, videoFileName);
|
|
46384
46785
|
await handle.video.saveAs(savePath);
|
|
46385
46786
|
const createdAt = this._pageCreationTimes.get(handle.pageIndex) ?? timestamp;
|
|
46386
46787
|
allPageVideos.push({
|
|
@@ -46446,7 +46847,14 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46446
46847
|
);
|
|
46447
46848
|
return null;
|
|
46448
46849
|
}
|
|
46850
|
+
// ================ TOASTS ================
|
|
46851
|
+
getCapturedToasts() {
|
|
46852
|
+
return this._lastCapturedToasts;
|
|
46853
|
+
}
|
|
46449
46854
|
// ================ DOWNLOADS ================
|
|
46855
|
+
getCapturedDownloads() {
|
|
46856
|
+
return this._capturedDownloads;
|
|
46857
|
+
}
|
|
46450
46858
|
async listDownloads() {
|
|
46451
46859
|
if (this._capturedDownloads.length === 0) {
|
|
46452
46860
|
return "No downloads captured during this session.";
|
|
@@ -46487,7 +46895,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46487
46895
|
const filename = target.suggestedFilename;
|
|
46488
46896
|
const sizeBytes = target.sizeBytes ?? 0;
|
|
46489
46897
|
const mimeType = this.guessMimeType(filename);
|
|
46490
|
-
const ext =
|
|
46898
|
+
const ext = path4.extname(filename).toLowerCase();
|
|
46491
46899
|
if (ext === ".pdf") {
|
|
46492
46900
|
const { extractPdfText: extractPdfText2 } = await import("./pdf-extract-XYDS42VL.js");
|
|
46493
46901
|
const { text } = await extractPdfText2(target.savedPath);
|
|
@@ -46508,7 +46916,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
46508
46916
|
".svg"
|
|
46509
46917
|
]);
|
|
46510
46918
|
if (textExtensions.has(ext)) {
|
|
46511
|
-
const content = await
|
|
46919
|
+
const content = await fs4.readFile(target.savedPath, "utf-8");
|
|
46512
46920
|
return { filename, sizeBytes, mimeType, textContent: content };
|
|
46513
46921
|
}
|
|
46514
46922
|
return { filename, sizeBytes, mimeType, textContent: null };
|
|
@@ -46619,6 +47027,7 @@ export {
|
|
|
46619
47027
|
compareSnapshots,
|
|
46620
47028
|
hasDiffChanges,
|
|
46621
47029
|
formatSemanticDiff,
|
|
47030
|
+
guessMimeType,
|
|
46622
47031
|
captureElementAtPoint,
|
|
46623
47032
|
AUTO_SNAPSHOT_MARKER,
|
|
46624
47033
|
isMCPContentWithImages,
|
|
@@ -46915,4 +47324,4 @@ playwright-extra/dist/index.esm.js:
|
|
|
46915
47324
|
* @license MIT
|
|
46916
47325
|
*)
|
|
46917
47326
|
*/
|
|
46918
|
-
//# sourceMappingURL=chunk-
|
|
47327
|
+
//# sourceMappingURL=chunk-FK3EZADZ.js.map
|