@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.
Files changed (35) hide show
  1. package/README.md +1 -0
  2. package/dist/{browser-Y6B3NS4Q.js → browser-BVRFSUXU.js} +3 -2
  3. package/dist/{browser-NVC7MTML.js → browser-DSYUAE7H.js} +5 -5
  4. package/dist/{browser-PBBC7RAV.js → browser-Q67BJTI2.js} +6 -4
  5. package/dist/{cdp-driver-QYFA4QTP.js → cdp-driver-DC7EIUG5.js} +4 -3
  6. package/dist/{cdp-driver-Z2PLAO42.js → cdp-driver-T7D6H2RW.js} +368 -510
  7. package/dist/{cdp-driver-73F5CVH2.js → cdp-driver-WWO2UZDR.js} +4 -3
  8. package/dist/{chunk-76EKW7YJ.js → chunk-2Q3GQRUP.js} +3 -3
  9. package/dist/{chunk-UYEJ66VX.js → chunk-4OU37CXP.js} +387 -540
  10. package/dist/{chunk-H2A5JUK5.js → chunk-75DNUI6M.js} +9 -0
  11. package/dist/{chunk-FDU5BCPV.js → chunk-7OP2ISEY.js} +378 -540
  12. package/dist/{chunk-3FWLW7FS.js → chunk-A7NEA4PA.js} +39 -1
  13. package/dist/chunk-GYB5BOSP.js +602 -0
  14. package/dist/{chunk-T2KUVO4H.js → chunk-IFKJO2UR.js} +393 -546
  15. package/dist/{chunk-NODRQGOK.js → chunk-IR6C24P7.js} +97 -0
  16. package/dist/{chunk-3KZ34AUC.js → chunk-MYH6FIBZ.js} +2 -1
  17. package/dist/{chunk-75TLPVVZ.js → chunk-T4WTKBWI.js} +2 -2
  18. package/dist/{chunk-HBHOKZPN.js → chunk-V6KGBU5J.js} +97 -0
  19. package/dist/{chunk-IWVG4XYZ.js → chunk-VCVDILH6.js} +2 -1
  20. package/dist/{chunk-A6LPGFAL.js → chunk-WYETQ5C7.js} +1 -1
  21. package/dist/cli.js +25 -13
  22. package/dist/daemon-main.js +20 -14
  23. package/dist/index.d.ts +136 -1
  24. package/dist/index.js +29 -18
  25. package/dist/keep-awake-M4KJS4B4.js +29 -0
  26. package/dist/{launcher-44STYRJC.js → launcher-IK2FKW6T.js} +1 -1
  27. package/dist/{launcher-EXJHHAUL.js → launcher-LUOMMGZC.js} +1 -1
  28. package/dist/{proxy-LUR4U5YF.js → proxy-NKCYE76M.js} +2 -2
  29. package/dist/{session-recorder-SLDBENVF.js → session-recorder-563ISSJK.js} +1 -1
  30. package/dist/{session-recorder-3BEVWHOK.js → session-recorder-GEZVQBKW.js} +1 -1
  31. package/dist/session-replayer-XM5L7LFD.js +927 -0
  32. package/dist/stealth-2AA2FSNN.js +29 -0
  33. package/dist/stealth-JOXGV34D.js +29 -0
  34. package/package.json +2 -1
  35. 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-UYEJ66VX.js";
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-3BEVWHOK.js");
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) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createRuleEngine
3
- } from "./chunk-H2A5JUK5.js";
3
+ } from "./chunk-75DNUI6M.js";
4
4
 
5
5
  // src/cdp-interceptor/proxy.ts
6
6
  import { WebSocketServer, WebSocket } from "ws";
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-NODRQGOK.js";
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-T2KUVO4H.js";
76
- import "./chunk-3KZ34AUC.js";
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-Y6B3NS4Q.js");
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-SLDBENVF.js");
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-Y6B3NS4Q.js");
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-Y6B3NS4Q.js");
14724
+ const { ensureProcessCanExit } = await import("./browser-BVRFSUXU.js");
14713
14725
  await ensureProcessCanExit().catch(() => {
14714
14726
  });
14715
14727
  process.exit(process.exitCode || exitCode);
@@ -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-HBHOKZPN.js";
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-75TLPVVZ.js";
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-UYEJ66VX.js";
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-PBBC7RAV.js");
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-SBYJXQZZ.js");
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.