@xbrowser/cli 1.20.0 → 1.22.0
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 +1 -0
- package/dist/{browser-Y6B3NS4Q.js → browser-BVRFSUXU.js} +3 -2
- package/dist/{browser-NVC7MTML.js → browser-DSYUAE7H.js} +5 -5
- package/dist/{browser-PBBC7RAV.js → browser-Q67BJTI2.js} +6 -4
- package/dist/{cdp-driver-QYFA4QTP.js → cdp-driver-DC7EIUG5.js} +4 -3
- package/dist/{cdp-driver-Z2PLAO42.js → cdp-driver-T7D6H2RW.js} +368 -510
- package/dist/{cdp-driver-73F5CVH2.js → cdp-driver-WWO2UZDR.js} +4 -3
- package/dist/{chunk-76EKW7YJ.js → chunk-2Q3GQRUP.js} +3 -3
- package/dist/{chunk-UYEJ66VX.js → chunk-4OU37CXP.js} +387 -540
- package/dist/{chunk-H2A5JUK5.js → chunk-75DNUI6M.js} +9 -0
- package/dist/{chunk-FDU5BCPV.js → chunk-7OP2ISEY.js} +378 -540
- package/dist/{chunk-3FWLW7FS.js → chunk-A7NEA4PA.js} +39 -1
- package/dist/chunk-GYB5BOSP.js +602 -0
- package/dist/{chunk-T2KUVO4H.js → chunk-IFKJO2UR.js} +393 -546
- package/dist/{chunk-NODRQGOK.js → chunk-IR6C24P7.js} +97 -0
- package/dist/{chunk-3KZ34AUC.js → chunk-MYH6FIBZ.js} +2 -1
- package/dist/{chunk-75TLPVVZ.js → chunk-T4WTKBWI.js} +2 -2
- package/dist/{chunk-HBHOKZPN.js → chunk-V6KGBU5J.js} +97 -0
- package/dist/{chunk-IWVG4XYZ.js → chunk-VCVDILH6.js} +2 -1
- package/dist/{chunk-A6LPGFAL.js → chunk-WYETQ5C7.js} +1 -1
- package/dist/cli.js +25 -13
- package/dist/daemon-main.js +20 -14
- package/dist/index.d.ts +136 -1
- package/dist/index.js +29 -18
- package/dist/keep-awake-M4KJS4B4.js +29 -0
- package/dist/{launcher-44STYRJC.js → launcher-IK2FKW6T.js} +1 -1
- package/dist/{launcher-EXJHHAUL.js → launcher-LUOMMGZC.js} +1 -1
- package/dist/{proxy-LUR4U5YF.js → proxy-NKCYE76M.js} +2 -2
- package/dist/{session-recorder-SLDBENVF.js → session-recorder-563ISSJK.js} +1 -1
- package/dist/{session-recorder-3BEVWHOK.js → session-recorder-GEZVQBKW.js} +1 -1
- package/dist/session-replayer-XM5L7LFD.js +927 -0
- package/dist/stealth-2AA2FSNN.js +29 -0
- package/dist/stealth-JOXGV34D.js +29 -0
- package/package.json +2 -1
- package/dist/session-replayer-SBYJXQZZ.js +0 -413
|
@@ -478,6 +478,55 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
478
478
|
} catch(e) { /* skip */ }
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
+
// Ordinal address (r6): ancestor form's ordinal among sibling forms + element's
|
|
482
|
+
// ordinal among same-tag direct children of that form. Direct children only \u2014
|
|
483
|
+
// nested elements have no stable single-level address.
|
|
484
|
+
var ordinal = null;
|
|
485
|
+
try {
|
|
486
|
+
var ordForm = el.closest ? el.closest('form') : null;
|
|
487
|
+
if (ordForm && el.parentElement === ordForm) {
|
|
488
|
+
var ordParent = ordForm.parentElement;
|
|
489
|
+
var formSibs = ordParent
|
|
490
|
+
? Array.prototype.filter.call(ordParent.children, function(c) { return c.tagName === 'FORM'; })
|
|
491
|
+
: [ordForm];
|
|
492
|
+
var tagSibs = Array.prototype.filter.call(ordForm.children, function(c) { return c.tagName === el.tagName; });
|
|
493
|
+
ordinal = {
|
|
494
|
+
formNth: formSibs.indexOf(ordForm) + 1,
|
|
495
|
+
tagNth: tagSibs.indexOf(el) + 1,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
} catch(e) { /* skip */ }
|
|
499
|
+
|
|
500
|
+
// Label text (r16): the <label> associated with the element (for= association or ancestor wrapper). In virtual list /
|
|
501
|
+
// form-row reordering scenarios, structural ordinals fail, but the label text moves in the same line as the
|
|
502
|
+
// control\u2014content moves, and the anchor follows the content.
|
|
503
|
+
var labelText = '';
|
|
504
|
+
try {
|
|
505
|
+
var lbl = null;
|
|
506
|
+
if (el.id) lbl = el.ownerDocument.querySelector('label[for="' + el.id + '"]');
|
|
507
|
+
if (!lbl && el.closest) lbl = el.closest('label');
|
|
508
|
+
if (lbl) labelText = (lbl.textContent || '').trim().substring(0, 40);
|
|
509
|
+
} catch(e) { /* skip */ }
|
|
510
|
+
|
|
511
|
+
// Element size (r25): rounded rect at record time \u2014 scoring signal for
|
|
512
|
+
// fingerprint disambiguation between same-fingerprint decoys.
|
|
513
|
+
var size = null;
|
|
514
|
+
try {
|
|
515
|
+
var szR = el.getBoundingClientRect();
|
|
516
|
+
if (szR.width > 0 || szR.height > 0) {
|
|
517
|
+
size = { w: Math.round(szR.width), h: Math.round(szR.height) };
|
|
518
|
+
}
|
|
519
|
+
} catch(e) { /* skip */ }
|
|
520
|
+
|
|
521
|
+
// Row text (r27): textContent of the closest table/list row. In virtual
|
|
522
|
+
// lists and sortable tables the row re-indexes \u2014 the row TEXT travels
|
|
523
|
+
// with the content, giving inputs a content anchor that survives reorder.
|
|
524
|
+
var rowText = '';
|
|
525
|
+
try {
|
|
526
|
+
var row = el.closest ? el.closest('tr,li') : null;
|
|
527
|
+
if (row) rowText = (row.textContent || '').trim().replace(/s+/g, ' ').substring(0, 40);
|
|
528
|
+
} catch(e) { /* skip */ }
|
|
529
|
+
|
|
481
530
|
return {
|
|
482
531
|
tag: tag,
|
|
483
532
|
selector: selector,
|
|
@@ -491,6 +540,10 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
491
540
|
placeholder: el.getAttribute('placeholder') || undefined,
|
|
492
541
|
ariaLabel: el.getAttribute('aria-label') || undefined,
|
|
493
542
|
href: el.getAttribute('href') ? el.getAttribute('href').substring(0, 80) : undefined,
|
|
543
|
+
ordinal: ordinal || undefined,
|
|
544
|
+
labelText: labelText || undefined,
|
|
545
|
+
size: size || undefined,
|
|
546
|
+
rowText: rowText || undefined,
|
|
494
547
|
};
|
|
495
548
|
}
|
|
496
549
|
|
|
@@ -1128,6 +1181,50 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
1128
1181
|
__xb_drag_start_pos = { x: e.clientX, y: e.clientY };
|
|
1129
1182
|
}, true);
|
|
1130
1183
|
document.addEventListener('drop', function(e) {
|
|
1184
|
+
// sup-s5: OS \u6587\u4EF6\u62D6\u5165\u6355\u83B7\u2014\u2014Dropzone/Uppy \u7C7B\u4E0A\u4F20\u533A\u65E0 input[type=file]\uFF0C
|
|
1185
|
+
// change \u6355\u83B7\u591F\u4E0D\u7740\uFF1BdataTransfer.files \u7ECF FileReader \u5185\u8054\uFF08\u4E0E
|
|
1186
|
+
// filechooser \u540C\u6784\uFF09\uFF0C\u56DE\u653E\u7AEF 'drop' \u52A8\u4F5C\u7ECF dropFiles \u91CD\u653E\u3002
|
|
1187
|
+
try {
|
|
1188
|
+
var dropped = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0)
|
|
1189
|
+
? Array.prototype.slice.call(e.dataTransfer.files)
|
|
1190
|
+
: [];
|
|
1191
|
+
if (dropped.length > 0) {
|
|
1192
|
+
var zone = describe(resolveMeaningful(e));
|
|
1193
|
+
var names = dropped.map(function(f) { return f.name; });
|
|
1194
|
+
var readers = [];
|
|
1195
|
+
for (var i = 0; i < dropped.length; i++) {
|
|
1196
|
+
readers.push(new Promise(function(resolve) {
|
|
1197
|
+
var reader = new FileReader();
|
|
1198
|
+
reader.onload = function() { resolve(reader.result); };
|
|
1199
|
+
reader.onerror = function() { resolve(null); };
|
|
1200
|
+
reader.readAsDataURL(dropped[i]);
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
Promise.all(readers).then(function(contents) {
|
|
1204
|
+
var fileData = [];
|
|
1205
|
+
for (var j = 0; j < dropped.length; j++) {
|
|
1206
|
+
fileData.push({
|
|
1207
|
+
name: dropped[j].name,
|
|
1208
|
+
type: dropped[j].type,
|
|
1209
|
+
size: dropped[j].size,
|
|
1210
|
+
dataUrl: contents[j],
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
pushAction('drop', {
|
|
1214
|
+
x: e.clientX,
|
|
1215
|
+
y: e.clientY,
|
|
1216
|
+
element: zone,
|
|
1217
|
+
value: names.join(', '),
|
|
1218
|
+
files: {
|
|
1219
|
+
names: names,
|
|
1220
|
+
count: dropped.length,
|
|
1221
|
+
isMultiple: dropped.length > 1,
|
|
1222
|
+
fileData: fileData,
|
|
1223
|
+
},
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
} catch(_) { /* capture must not break the drag path */ }
|
|
1131
1228
|
if (__xb_drag_source && __xb_drag_start_pos) {
|
|
1132
1229
|
pushAction('drag', {
|
|
1133
1230
|
x: e.clientX,
|
|
@@ -93,10 +93,11 @@ async function launchChrome(options = {}) {
|
|
|
93
93
|
"--no-sandbox",
|
|
94
94
|
"--no-first-run",
|
|
95
95
|
"--no-default-browser-check",
|
|
96
|
+
"--disable-session-crashed-bubble",
|
|
96
97
|
"--disable-background-timer-throttling",
|
|
97
98
|
"--disable-backgrounding-occluded-windows",
|
|
98
99
|
"--disable-renderer-backgrounding",
|
|
99
|
-
"--disable-features=Translate",
|
|
100
|
+
"--disable-features=Translate,PasswordLeakDetection",
|
|
100
101
|
"--disable-popup-blocking"
|
|
101
102
|
];
|
|
102
103
|
if (headless) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createRuleEngine,
|
|
3
3
|
launch
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OU37CXP.js";
|
|
5
5
|
import {
|
|
6
6
|
errMsg
|
|
7
7
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -1227,7 +1227,7 @@ async function closeSessionByName(name) {
|
|
|
1227
1227
|
} catch {
|
|
1228
1228
|
}
|
|
1229
1229
|
try {
|
|
1230
|
-
const { SessionRecorder } = await import("./session-recorder-
|
|
1230
|
+
const { SessionRecorder } = await import("./session-recorder-GEZVQBKW.js");
|
|
1231
1231
|
SessionRecorder.cleanup(session.name);
|
|
1232
1232
|
} catch {
|
|
1233
1233
|
}
|
|
@@ -875,6 +875,55 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
875
875
|
} catch(e) { /* skip */ }
|
|
876
876
|
}
|
|
877
877
|
|
|
878
|
+
// Ordinal address (r6): ancestor form's ordinal among sibling forms + element's
|
|
879
|
+
// ordinal among same-tag direct children of that form. Direct children only \u2014
|
|
880
|
+
// nested elements have no stable single-level address.
|
|
881
|
+
var ordinal = null;
|
|
882
|
+
try {
|
|
883
|
+
var ordForm = el.closest ? el.closest('form') : null;
|
|
884
|
+
if (ordForm && el.parentElement === ordForm) {
|
|
885
|
+
var ordParent = ordForm.parentElement;
|
|
886
|
+
var formSibs = ordParent
|
|
887
|
+
? Array.prototype.filter.call(ordParent.children, function(c) { return c.tagName === 'FORM'; })
|
|
888
|
+
: [ordForm];
|
|
889
|
+
var tagSibs = Array.prototype.filter.call(ordForm.children, function(c) { return c.tagName === el.tagName; });
|
|
890
|
+
ordinal = {
|
|
891
|
+
formNth: formSibs.indexOf(ordForm) + 1,
|
|
892
|
+
tagNth: tagSibs.indexOf(el) + 1,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
} catch(e) { /* skip */ }
|
|
896
|
+
|
|
897
|
+
// Label text (r16): the <label> associated with the element (for= association or ancestor wrapper). In virtual list /
|
|
898
|
+
// form-row reordering scenarios, structural ordinals fail, but the label text moves in the same line as the
|
|
899
|
+
// control\u2014content moves, and the anchor follows the content.
|
|
900
|
+
var labelText = '';
|
|
901
|
+
try {
|
|
902
|
+
var lbl = null;
|
|
903
|
+
if (el.id) lbl = el.ownerDocument.querySelector('label[for="' + el.id + '"]');
|
|
904
|
+
if (!lbl && el.closest) lbl = el.closest('label');
|
|
905
|
+
if (lbl) labelText = (lbl.textContent || '').trim().substring(0, 40);
|
|
906
|
+
} catch(e) { /* skip */ }
|
|
907
|
+
|
|
908
|
+
// Element size (r25): rounded rect at record time \u2014 scoring signal for
|
|
909
|
+
// fingerprint disambiguation between same-fingerprint decoys.
|
|
910
|
+
var size = null;
|
|
911
|
+
try {
|
|
912
|
+
var szR = el.getBoundingClientRect();
|
|
913
|
+
if (szR.width > 0 || szR.height > 0) {
|
|
914
|
+
size = { w: Math.round(szR.width), h: Math.round(szR.height) };
|
|
915
|
+
}
|
|
916
|
+
} catch(e) { /* skip */ }
|
|
917
|
+
|
|
918
|
+
// Row text (r27): textContent of the closest table/list row. In virtual
|
|
919
|
+
// lists and sortable tables the row re-indexes \u2014 the row TEXT travels
|
|
920
|
+
// with the content, giving inputs a content anchor that survives reorder.
|
|
921
|
+
var rowText = '';
|
|
922
|
+
try {
|
|
923
|
+
var row = el.closest ? el.closest('tr,li') : null;
|
|
924
|
+
if (row) rowText = (row.textContent || '').trim().replace(/s+/g, ' ').substring(0, 40);
|
|
925
|
+
} catch(e) { /* skip */ }
|
|
926
|
+
|
|
878
927
|
return {
|
|
879
928
|
tag: tag,
|
|
880
929
|
selector: selector,
|
|
@@ -888,6 +937,10 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
888
937
|
placeholder: el.getAttribute('placeholder') || undefined,
|
|
889
938
|
ariaLabel: el.getAttribute('aria-label') || undefined,
|
|
890
939
|
href: el.getAttribute('href') ? el.getAttribute('href').substring(0, 80) : undefined,
|
|
940
|
+
ordinal: ordinal || undefined,
|
|
941
|
+
labelText: labelText || undefined,
|
|
942
|
+
size: size || undefined,
|
|
943
|
+
rowText: rowText || undefined,
|
|
891
944
|
};
|
|
892
945
|
}
|
|
893
946
|
|
|
@@ -1525,6 +1578,50 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
1525
1578
|
__xb_drag_start_pos = { x: e.clientX, y: e.clientY };
|
|
1526
1579
|
}, true);
|
|
1527
1580
|
document.addEventListener('drop', function(e) {
|
|
1581
|
+
// sup-s5: OS \u6587\u4EF6\u62D6\u5165\u6355\u83B7\u2014\u2014Dropzone/Uppy \u7C7B\u4E0A\u4F20\u533A\u65E0 input[type=file]\uFF0C
|
|
1582
|
+
// change \u6355\u83B7\u591F\u4E0D\u7740\uFF1BdataTransfer.files \u7ECF FileReader \u5185\u8054\uFF08\u4E0E
|
|
1583
|
+
// filechooser \u540C\u6784\uFF09\uFF0C\u56DE\u653E\u7AEF 'drop' \u52A8\u4F5C\u7ECF dropFiles \u91CD\u653E\u3002
|
|
1584
|
+
try {
|
|
1585
|
+
var dropped = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0)
|
|
1586
|
+
? Array.prototype.slice.call(e.dataTransfer.files)
|
|
1587
|
+
: [];
|
|
1588
|
+
if (dropped.length > 0) {
|
|
1589
|
+
var zone = describe(resolveMeaningful(e));
|
|
1590
|
+
var names = dropped.map(function(f) { return f.name; });
|
|
1591
|
+
var readers = [];
|
|
1592
|
+
for (var i = 0; i < dropped.length; i++) {
|
|
1593
|
+
readers.push(new Promise(function(resolve) {
|
|
1594
|
+
var reader = new FileReader();
|
|
1595
|
+
reader.onload = function() { resolve(reader.result); };
|
|
1596
|
+
reader.onerror = function() { resolve(null); };
|
|
1597
|
+
reader.readAsDataURL(dropped[i]);
|
|
1598
|
+
}));
|
|
1599
|
+
}
|
|
1600
|
+
Promise.all(readers).then(function(contents) {
|
|
1601
|
+
var fileData = [];
|
|
1602
|
+
for (var j = 0; j < dropped.length; j++) {
|
|
1603
|
+
fileData.push({
|
|
1604
|
+
name: dropped[j].name,
|
|
1605
|
+
type: dropped[j].type,
|
|
1606
|
+
size: dropped[j].size,
|
|
1607
|
+
dataUrl: contents[j],
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
pushAction('drop', {
|
|
1611
|
+
x: e.clientX,
|
|
1612
|
+
y: e.clientY,
|
|
1613
|
+
element: zone,
|
|
1614
|
+
value: names.join(', '),
|
|
1615
|
+
files: {
|
|
1616
|
+
names: names,
|
|
1617
|
+
count: dropped.length,
|
|
1618
|
+
isMultiple: dropped.length > 1,
|
|
1619
|
+
fileData: fileData,
|
|
1620
|
+
},
|
|
1621
|
+
});
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
} catch(_) { /* capture must not break the drag path */ }
|
|
1528
1625
|
if (__xb_drag_source && __xb_drag_start_pos) {
|
|
1529
1626
|
pushAction('drag', {
|
|
1530
1627
|
x: e.clientX,
|
|
@@ -97,10 +97,11 @@ async function launchChrome(options = {}) {
|
|
|
97
97
|
"--no-sandbox",
|
|
98
98
|
"--no-first-run",
|
|
99
99
|
"--no-default-browser-check",
|
|
100
|
+
"--disable-session-crashed-bubble",
|
|
100
101
|
"--disable-background-timer-throttling",
|
|
101
102
|
"--disable-backgrounding-occluded-windows",
|
|
102
103
|
"--disable-renderer-backgrounding",
|
|
103
|
-
"--disable-features=Translate",
|
|
104
|
+
"--disable-features=Translate,PasswordLeakDetection",
|
|
104
105
|
"--disable-popup-blocking"
|
|
105
106
|
];
|
|
106
107
|
if (headless) {
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
detectAntiBot,
|
|
4
|
+
formatDetectionMessage
|
|
5
|
+
} from "./chunk-JKVUFP3G.js";
|
|
2
6
|
import {
|
|
3
7
|
forwardCommandLog,
|
|
4
8
|
forwardNetworkAnalyze,
|
|
@@ -45,7 +49,7 @@ import {
|
|
|
45
49
|
} from "./chunk-Q2DFJQGS.js";
|
|
46
50
|
import {
|
|
47
51
|
SessionRecorder
|
|
48
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-IR6C24P7.js";
|
|
49
53
|
import {
|
|
50
54
|
addKnownIssue,
|
|
51
55
|
getKnowledgePath,
|
|
@@ -66,21 +70,19 @@ import {
|
|
|
66
70
|
getAllSessions,
|
|
67
71
|
getBrowser,
|
|
68
72
|
getSessionById,
|
|
69
|
-
rand,
|
|
70
73
|
resolveLaunchOpts,
|
|
71
74
|
saveSessionDiskMeta,
|
|
72
|
-
setActivePage
|
|
75
|
+
setActivePage
|
|
76
|
+
} from "./chunk-IFKJO2UR.js";
|
|
77
|
+
import {
|
|
78
|
+
rand,
|
|
73
79
|
sleep,
|
|
74
80
|
wheelDelta
|
|
75
|
-
} from "./chunk-
|
|
76
|
-
import "./chunk-
|
|
81
|
+
} from "./chunk-GYB5BOSP.js";
|
|
82
|
+
import "./chunk-MYH6FIBZ.js";
|
|
77
83
|
import {
|
|
78
84
|
errMsg
|
|
79
85
|
} from "./chunk-GDKLH7ZY.js";
|
|
80
|
-
import {
|
|
81
|
-
detectAntiBot,
|
|
82
|
-
formatDetectionMessage
|
|
83
|
-
} from "./chunk-JKVUFP3G.js";
|
|
84
86
|
import "./chunk-KFQGP6VL.js";
|
|
85
87
|
|
|
86
88
|
// src/router.ts
|
|
@@ -8248,7 +8250,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
8248
8250
|
}
|
|
8249
8251
|
let targetPageOverride = null;
|
|
8250
8252
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
8251
|
-
const { findTargetPage } = await import("./browser-
|
|
8253
|
+
const { findTargetPage } = await import("./browser-BVRFSUXU.js");
|
|
8252
8254
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
8253
8255
|
if (!targetPageOverride) {
|
|
8254
8256
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -12292,6 +12294,16 @@ async function handleReplay(args, options, mode) {
|
|
|
12292
12294
|
}
|
|
12293
12295
|
return;
|
|
12294
12296
|
}
|
|
12297
|
+
if (result.ok && mode !== "json" && mode !== "yaml") {
|
|
12298
|
+
const healed = typeof result.healed === "number" ? result.healed : 0;
|
|
12299
|
+
if (healed > 0) {
|
|
12300
|
+
console.log(`
|
|
12301
|
+
Self-healed ${healed} action(s):`);
|
|
12302
|
+
for (const d of result.healedDetails ?? []) {
|
|
12303
|
+
console.log(` - step ${d.index + 1}: ${d.strategy}`);
|
|
12304
|
+
}
|
|
12305
|
+
}
|
|
12306
|
+
}
|
|
12295
12307
|
outputResult(result, mode);
|
|
12296
12308
|
}
|
|
12297
12309
|
async function handleConvert(args, _mode) {
|
|
@@ -12393,7 +12405,7 @@ async function handleFilter(args, _mode, options) {
|
|
|
12393
12405
|
}
|
|
12394
12406
|
}
|
|
12395
12407
|
async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
|
|
12396
|
-
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-
|
|
12408
|
+
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-563ISSJK.js");
|
|
12397
12409
|
const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
|
|
12398
12410
|
const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync12 } = await import("fs");
|
|
12399
12411
|
const { join: join15 } = await import("path");
|
|
@@ -14433,7 +14445,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
14433
14445
|
const targetPage = pages[cmdTabIndex];
|
|
14434
14446
|
await targetPage.bringToFront().catch(() => {
|
|
14435
14447
|
});
|
|
14436
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
14448
|
+
const { setActivePage: setActivePage2 } = await import("./browser-BVRFSUXU.js");
|
|
14437
14449
|
setActivePage2(session, targetPage);
|
|
14438
14450
|
}
|
|
14439
14451
|
}
|
|
@@ -14709,7 +14721,7 @@ async function main() {
|
|
|
14709
14721
|
const command = process.argv[2];
|
|
14710
14722
|
const isLongRunning = command === "preview" || command === "serve";
|
|
14711
14723
|
if (!isLongRunning) {
|
|
14712
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
14724
|
+
const { ensureProcessCanExit } = await import("./browser-BVRFSUXU.js");
|
|
14713
14725
|
await ensureProcessCanExit().catch(() => {
|
|
14714
14726
|
});
|
|
14715
14727
|
process.exit(process.exitCode || exitCode);
|
package/dist/daemon-main.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectAntiBot,
|
|
3
|
+
formatDetectionMessage
|
|
4
|
+
} from "./chunk-JKVUFP3G.js";
|
|
1
5
|
import {
|
|
2
6
|
getPluginLoader
|
|
3
7
|
} from "./chunk-PIWNMC36.js";
|
|
@@ -19,7 +23,7 @@ import {
|
|
|
19
23
|
} from "./chunk-VEDJ5XSQ.js";
|
|
20
24
|
import {
|
|
21
25
|
SessionRecorder
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-V6KGBU5J.js";
|
|
23
27
|
import {
|
|
24
28
|
closeEphemeralContext,
|
|
25
29
|
closeSessionByName,
|
|
@@ -34,24 +38,22 @@ import {
|
|
|
34
38
|
resolveLaunchOpts,
|
|
35
39
|
saveSessionDiskMeta,
|
|
36
40
|
setActivePage
|
|
37
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-T4WTKBWI.js";
|
|
42
|
+
import {
|
|
43
|
+
createRuleEngine
|
|
44
|
+
} from "./chunk-4OU37CXP.js";
|
|
45
|
+
import {
|
|
46
|
+
queryJS
|
|
47
|
+
} from "./chunk-A7NEA4PA.js";
|
|
38
48
|
import {
|
|
39
|
-
createRuleEngine,
|
|
40
49
|
rand,
|
|
41
50
|
sleep,
|
|
42
51
|
wheelDelta
|
|
43
|
-
} from "./chunk-
|
|
44
|
-
import
|
|
45
|
-
queryJS
|
|
46
|
-
} from "./chunk-3FWLW7FS.js";
|
|
47
|
-
import "./chunk-3KZ34AUC.js";
|
|
52
|
+
} from "./chunk-GYB5BOSP.js";
|
|
53
|
+
import "./chunk-MYH6FIBZ.js";
|
|
48
54
|
import {
|
|
49
55
|
errMsg
|
|
50
56
|
} from "./chunk-GDKLH7ZY.js";
|
|
51
|
-
import {
|
|
52
|
-
detectAntiBot,
|
|
53
|
-
formatDetectionMessage
|
|
54
|
-
} from "./chunk-JKVUFP3G.js";
|
|
55
57
|
import "./chunk-KFQGP6VL.js";
|
|
56
58
|
|
|
57
59
|
// src/daemon/daemon-main.ts
|
|
@@ -7766,7 +7768,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7766
7768
|
}
|
|
7767
7769
|
let targetPageOverride = null;
|
|
7768
7770
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7769
|
-
const { findTargetPage } = await import("./browser-
|
|
7771
|
+
const { findTargetPage } = await import("./browser-DSYUAE7H.js");
|
|
7770
7772
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7771
7773
|
if (!targetPageOverride) {
|
|
7772
7774
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -9588,7 +9590,7 @@ function createRPCHandler() {
|
|
|
9588
9590
|
if (isNewFormat) {
|
|
9589
9591
|
try {
|
|
9590
9592
|
const replayErrors = [];
|
|
9591
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
9593
|
+
const { SessionReplayer } = await import("./session-replayer-XM5L7LFD.js");
|
|
9592
9594
|
const replayer = new SessionReplayer({
|
|
9593
9595
|
page: session.page,
|
|
9594
9596
|
stepDelay: slowMo * 500,
|
|
@@ -9613,6 +9615,8 @@ function createRPCHandler() {
|
|
|
9613
9615
|
duration,
|
|
9614
9616
|
eventsPlayed: result2.success,
|
|
9615
9617
|
totalEvents: result2.success + result2.failed + result2.skipped,
|
|
9618
|
+
healed: result2.healed,
|
|
9619
|
+
healedDetails: result2.healedDetails,
|
|
9616
9620
|
errors: replayErrors
|
|
9617
9621
|
});
|
|
9618
9622
|
} catch (e) {
|
|
@@ -9645,6 +9649,8 @@ function createRPCHandler() {
|
|
|
9645
9649
|
duration: typeof input.duration === "number" ? input.duration : 0,
|
|
9646
9650
|
eventsPlayed,
|
|
9647
9651
|
totalEvents,
|
|
9652
|
+
healed: typeof input.healed === "number" ? input.healed : 0,
|
|
9653
|
+
healedDetails: Array.isArray(input.healedDetails) ? input.healedDetails : [],
|
|
9648
9654
|
errors
|
|
9649
9655
|
};
|
|
9650
9656
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,103 @@ import { ZodType, ZodTypeDef, z } from 'zod';
|
|
|
6
6
|
|
|
7
7
|
declare const version: any;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* stealth.ts — Behavioral stealth layer for CDP driver
|
|
11
|
+
*
|
|
12
|
+
* Enhances mouse/keyboard with human-like patterns:
|
|
13
|
+
* • Bezier trajectory with cosine easing + noise + overshoot correction
|
|
14
|
+
* • Landing offset from element center (no dead-center clicks)
|
|
15
|
+
* • Press/release drift (finger micro-movement)
|
|
16
|
+
* • Three-tier typing rhythm with random pauses
|
|
17
|
+
* • Typo + backspace correction
|
|
18
|
+
* • Wheel exponential decay (momentum)
|
|
19
|
+
*
|
|
20
|
+
* Also provides init script for page-level stealth hooks:
|
|
21
|
+
*
|
|
22
|
+
* ── 维护红线(S183 站点观测实证)────────────────────────────
|
|
23
|
+
* 1. 永不覆写 navigator.webdriver —— 原生 false 即最佳伪装。
|
|
24
|
+
* 掘金等站点以 ~5s/次轮询 Navigator.prototype.webdriver 的
|
|
25
|
+
* descriptor(getter native + 实例无覆写 = 正常),任何覆写
|
|
26
|
+
* 都会在 descriptor 检测下露馅。
|
|
27
|
+
* 2. 实例覆写必须原型层同步(S172)—— 实例 defineProperty 可被
|
|
28
|
+
* X.prototype.p.call(target) 一行逃逸,见 stealth-audit.test.ts。
|
|
29
|
+
* 3. 覆写函数必须进 toString native 伪装名单(S172b 审计规则 R2)。
|
|
30
|
+
* ────────────────────────────────────────────────────────
|
|
31
|
+
* Also provides init script for page-level stealth hooks:
|
|
32
|
+
* • AEL event proxy (sourceCapabilities / isTrusted / coordinate floatification)
|
|
33
|
+
* • onclick prototype hijack (dual-stream consistency)
|
|
34
|
+
* • Screen/hasFocus override + toString disguise
|
|
35
|
+
*
|
|
36
|
+
* 124 rounds of attack-defense validated (output/cdp-duel-s5/)
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
interface UaChProfile {
|
|
40
|
+
/** GREASE brands 形状,例:Not=A?Brand:99(从真实环境采样) */
|
|
41
|
+
brands: Array<{
|
|
42
|
+
brand: string;
|
|
43
|
+
version: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** 平台(macOS / Windows / Linux) */
|
|
46
|
+
platform: string;
|
|
47
|
+
/** 平台版本(高熵) */
|
|
48
|
+
platformVersion: string;
|
|
49
|
+
/** 架构(arm / x86) */
|
|
50
|
+
architecture: string;
|
|
51
|
+
/** 位宽(64 / 32) */
|
|
52
|
+
bitness: string;
|
|
53
|
+
/** 设备型号(桌面端通常空) */
|
|
54
|
+
model: string;
|
|
55
|
+
/** 完整版本号(uaFullVersion,例:151.0.7922.175) */
|
|
56
|
+
uaFullVersion: string;
|
|
57
|
+
/** 完整版本列表(fullVersionList,与 brands 同形状+全版本) */
|
|
58
|
+
fullVersionList: Array<{
|
|
59
|
+
brand: string;
|
|
60
|
+
version: string;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
interface StealthConfig {
|
|
64
|
+
/**
|
|
65
|
+
* S191: UA-CH 真实档案——垫片在 navigator.userAgentData 缺失时
|
|
66
|
+
* 以此生成 API 表面(值来自真实有头 Chrome 采样,形状一致)。
|
|
67
|
+
* 未提供时从 UA 字符串派生兜底值。
|
|
68
|
+
*/
|
|
69
|
+
uaChProfile?: UaChProfile;
|
|
70
|
+
/** Bezier curvature range [min, max] as fraction of distance */
|
|
71
|
+
bezierCurvature: [number, number];
|
|
72
|
+
/** Trajectory noise amplitude ±px */
|
|
73
|
+
noiseAmplitude: number;
|
|
74
|
+
/** Overshoot distance range [min, max] px */
|
|
75
|
+
overshootRange: [number, number];
|
|
76
|
+
/** Aim pause before click [min, max] ms */
|
|
77
|
+
aimPause: [number, number];
|
|
78
|
+
/** Press duration [min, max] ms */
|
|
79
|
+
pressDuration: [number, number];
|
|
80
|
+
/** Release coordinate drift [min, max] px */
|
|
81
|
+
releaseDrift: [number, number];
|
|
82
|
+
/** Landing offset for small elements [min, max] px */
|
|
83
|
+
landingOffsetSmall: [number, number];
|
|
84
|
+
/** Landing offset for large elements [min, max] px */
|
|
85
|
+
landingOffsetLarge: [number, number];
|
|
86
|
+
/** Elements smaller than this (px) use small offset */
|
|
87
|
+
smallElementThreshold: number;
|
|
88
|
+
/** Typing rhythm probabilities */
|
|
89
|
+
typingRhythm: {
|
|
90
|
+
fastProb: number;
|
|
91
|
+
fastRange: [number, number];
|
|
92
|
+
normalRange: [number, number];
|
|
93
|
+
pauseProb: number;
|
|
94
|
+
pauseRange: [number, number];
|
|
95
|
+
};
|
|
96
|
+
/** Key press duration [min, max] ms */
|
|
97
|
+
keyPressDuration: [number, number];
|
|
98
|
+
/** Typo probability per field (0-1) */
|
|
99
|
+
typoProbability: number;
|
|
100
|
+
/** Wheel peak delta */
|
|
101
|
+
wheelPeak: number;
|
|
102
|
+
/** Wheel decay rate */
|
|
103
|
+
wheelDecayRate: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
9
106
|
/**
|
|
10
107
|
* XBrowser CDP Driver — Abstract Interface Types
|
|
11
108
|
*
|
|
@@ -38,6 +135,14 @@ interface XBContextOptions {
|
|
|
38
135
|
userAgent?: string;
|
|
39
136
|
extraHTTPHeaders?: Record<string, string>;
|
|
40
137
|
ignoreHTTPSErrors?: boolean;
|
|
138
|
+
/** S194: stealth 伪装配置(UA-CH 档案等)——经 context 传至 page 的注入层 */
|
|
139
|
+
stealthConfig?: Partial<StealthConfig>;
|
|
140
|
+
/** sup-s9: HTTP Basic/Digest 凭证——Fetch.authRequired 自动 ProvideCredentials;
|
|
141
|
+
* 缺省 CancelAuth(快速失败,不弹认证框不挂起) */
|
|
142
|
+
httpCredentials?: {
|
|
143
|
+
username: string;
|
|
144
|
+
password: string;
|
|
145
|
+
};
|
|
41
146
|
}
|
|
42
147
|
interface XBContext {
|
|
43
148
|
newPage(): Promise<XBPage>;
|
|
@@ -171,6 +276,8 @@ interface XBPage {
|
|
|
171
276
|
width: number;
|
|
172
277
|
height: number;
|
|
173
278
|
}): Promise<void>;
|
|
279
|
+
/** env-E2: 缩放变异——deviceScaleFactor 切换(CSS 视口尺寸不变) */
|
|
280
|
+
setDeviceScaleFactor(dsf: number): Promise<void>;
|
|
174
281
|
addInitScript(script: string): Promise<void>;
|
|
175
282
|
/** 覆盖当前页会话的 User-Agent(Network.setUserAgentOverride,会话级持久) */
|
|
176
283
|
setUserAgent(userAgent: string): Promise<void>;
|
|
@@ -187,6 +294,10 @@ interface XBPage {
|
|
|
187
294
|
route(url: string | RegExp, handler: (route: XBRoute) => Promise<void> | void): Promise<void>;
|
|
188
295
|
unroute(url: string | RegExp, handler?: (route: XBRoute) => Promise<void> | void): Promise<void>;
|
|
189
296
|
setInputFiles(selector: string, files: XBFilePayload | XBFilePayload[]): Promise<void>;
|
|
297
|
+
/** sup-s4: Dropzone 拖拽上传模拟——DataTransfer+File 派发 dragenter/dragover/drop */
|
|
298
|
+
dropFiles(selector: string, payload: XBFilePayload): Promise<void>;
|
|
299
|
+
/** sup-s11: beforeunload 自动应答语义(accept=离开默认 / dismiss=留守) */
|
|
300
|
+
setBeforeUnloadBehavior(mode: 'accept' | 'dismiss'): void;
|
|
190
301
|
dragAndDrop(source: string, target: string): Promise<void>;
|
|
191
302
|
on(event: string, handler: Function): void;
|
|
192
303
|
off(event: string, handler: Function): void;
|
|
@@ -1754,7 +1865,7 @@ interface HoverContext {
|
|
|
1754
1865
|
}
|
|
1755
1866
|
interface UserAction {
|
|
1756
1867
|
id: number;
|
|
1757
|
-
type: 'click' | 'input' | 'change' | 'keydown' | 'submit' | 'scroll' | 'navigation' | 'goto' | 'cdp-fill' | 'cdp-click' | 'cdp-eval' | 'filechooser' | 'dblclick' | 'contextmenu' | 'hover' | 'drag' | 'resize' | 'clipboard' | 'touch' | 'focus' | 'visibility' | 'popup_appear' | 'discovered_filters';
|
|
1868
|
+
type: 'click' | 'input' | 'change' | 'keydown' | 'submit' | 'scroll' | 'navigation' | 'goto' | 'cdp-fill' | 'cdp-click' | 'cdp-eval' | 'filechooser' | 'dblclick' | 'contextmenu' | 'hover' | 'drag' | 'resize' | 'clipboard' | 'touch' | 'focus' | 'visibility' | 'drop' | 'popup_appear' | 'discovered_filters';
|
|
1758
1869
|
timestamp: number;
|
|
1759
1870
|
url: string;
|
|
1760
1871
|
pageTitle: string;
|
|
@@ -1778,6 +1889,24 @@ interface UserAction {
|
|
|
1778
1889
|
placeholder?: string;
|
|
1779
1890
|
ariaLabel?: string;
|
|
1780
1891
|
href?: string;
|
|
1892
|
+
/** 结构地址(r6):祖先 form 在同级 form 中的序 + 元素在 form 内同 tag
|
|
1893
|
+
* 兄弟中的序(仅 form 直接子元素捕获)。布局位移后坐标失效时仍可定位。 */
|
|
1894
|
+
ordinal?: {
|
|
1895
|
+
formNth: number;
|
|
1896
|
+
tagNth: number;
|
|
1897
|
+
};
|
|
1898
|
+
/** 关联 label 的文本(r16):for= 关联或祖先 label 包裹。行重排/虚拟
|
|
1899
|
+
* 列表重建后结构序失效时的内容锚。 */
|
|
1900
|
+
labelText?: string;
|
|
1901
|
+
/** 元素尺寸(r25):录制时 rounded rect。同指纹诱饵消歧的评分信号
|
|
1902
|
+
* (仅加分不否决——响应式布局合法改变尺寸)。 */
|
|
1903
|
+
size?: {
|
|
1904
|
+
w: number;
|
|
1905
|
+
h: number;
|
|
1906
|
+
};
|
|
1907
|
+
/** 所在行文本(r27):closest('tr,li') 的 textContent 前 40 字符。
|
|
1908
|
+
* 表格/列表行重排后结构序失效时的内容锚(label 锚的行推广)。 */
|
|
1909
|
+
rowText?: string;
|
|
1781
1910
|
};
|
|
1782
1911
|
value?: string;
|
|
1783
1912
|
key?: string;
|
|
@@ -2178,6 +2307,12 @@ interface PlaybackResult {
|
|
|
2178
2307
|
event?: RecordedEvent;
|
|
2179
2308
|
error: string;
|
|
2180
2309
|
}>;
|
|
2310
|
+
/** r13: 自愈步数与明细(SessionReplayer 路径填充) */
|
|
2311
|
+
healed?: number;
|
|
2312
|
+
healedDetails?: Array<{
|
|
2313
|
+
index: number;
|
|
2314
|
+
strategy: string;
|
|
2315
|
+
}>;
|
|
2181
2316
|
}
|
|
2182
2317
|
/**
|
|
2183
2318
|
* Engine for playing back a recorded browser session.
|