@xbrowser/cli 1.13.0 → 1.15.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/dist/{browser-T7OVLAEC.js → browser-4FKHBUBJ.js} +2 -2
- package/dist/{browser-OMGJMSBB.js → browser-JNT2I73V.js} +1 -1
- package/dist/{browser-AQ2AT2YZ.js → browser-YYMKZWTU.js} +2 -2
- package/dist/{cdp-driver-VS7Z7W7Y.js → cdp-driver-7RW4NY2X.js} +1 -1
- package/dist/{cdp-driver-ALDNOZYD.js → cdp-driver-FTGIYCFO.js} +224 -44
- package/dist/{cdp-driver-HYCLE4YP.js → cdp-driver-QX72T7SU.js} +1 -1
- package/dist/{chunk-BY5TT6WZ.js → chunk-HFP4QCZL.js} +244 -53
- package/dist/{chunk-CW7L53JE.js → chunk-K5E4CWRV.js} +1 -1
- package/dist/{chunk-R2POF2GP.js → chunk-NTTFKS6T.js} +244 -53
- package/dist/{chunk-T2CNKWPV.js → chunk-UXDGEDT7.js} +244 -53
- package/dist/{chunk-HD4FX2N5.js → chunk-XQ4KOZ37.js} +1 -1
- package/dist/cli.js +75 -21
- package/dist/daemon-main.js +38 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -21
- package/dist/{session-replayer-UVH5FGY2.js → session-replayer-DKHXF3DZ.js} +1 -1
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
27
27
|
bezierCurvature: [0.35, 0.6],
|
|
28
28
|
noiseAmplitude: 5.5,
|
|
29
29
|
overshootRange: [6, 14],
|
|
30
|
-
aimPause: [
|
|
30
|
+
aimPause: [80, 280],
|
|
31
31
|
pressDuration: [60, 140],
|
|
32
32
|
releaseDrift: [0.8, 2.5],
|
|
33
33
|
landingOffsetSmall: [0.3, 2.5],
|
|
@@ -48,6 +48,9 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
48
48
|
function rand(min, max) {
|
|
49
49
|
return min + Math.random() * (max - min);
|
|
50
50
|
}
|
|
51
|
+
function sleep(ms) {
|
|
52
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
53
|
+
}
|
|
51
54
|
function cosineEase(t) {
|
|
52
55
|
return 0.5 - 0.5 * Math.cos(Math.PI * t);
|
|
53
56
|
}
|
|
@@ -95,6 +98,18 @@ function landingOffset(width, height, config = DEFAULT_STEALTH_CONFIG) {
|
|
|
95
98
|
const dy = rand(...range) * (Math.random() < 0.5 ? -1 : 1);
|
|
96
99
|
return { dx, dy };
|
|
97
100
|
}
|
|
101
|
+
function typingDelay(config = DEFAULT_STEALTH_CONFIG) {
|
|
102
|
+
const roll = Math.random();
|
|
103
|
+
const r = config.typingRhythm;
|
|
104
|
+
if (roll < r.pauseProb) return rand(...r.pauseRange);
|
|
105
|
+
if (roll < r.pauseProb + r.fastProb) return rand(...r.fastRange);
|
|
106
|
+
return rand(...r.normalRange);
|
|
107
|
+
}
|
|
108
|
+
function wheelDelta(step, config = DEFAULT_STEALTH_CONFIG) {
|
|
109
|
+
return Math.round(
|
|
110
|
+
config.wheelPeak * Math.exp(-step * config.wheelDecayRate) * rand(0.85, 1.15)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
98
113
|
var KEY_MAP = {};
|
|
99
114
|
for (let i = 97; i <= 122; i++) {
|
|
100
115
|
const ch = String.fromCharCode(i);
|
|
@@ -118,6 +133,7 @@ Object.assign(KEY_MAP, {
|
|
|
118
133
|
function buildStealthInitScript() {
|
|
119
134
|
return [
|
|
120
135
|
"(function(){",
|
|
136
|
+
' window.__xbStealthVer="57";',
|
|
121
137
|
// 1. AEL event proxy
|
|
122
138
|
" var o=EventTarget.prototype.addEventListener;",
|
|
123
139
|
" var fc=new InputDeviceCapabilities({firesTouchEvents:false});",
|
|
@@ -125,10 +141,10 @@ function buildStealthInitScript() {
|
|
|
125
141
|
" var op=arguments[2];",
|
|
126
142
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
127
143
|
" var w=function(e){",
|
|
128
|
-
" if(!e
|
|
144
|
+
" if(!e)return f.call(this,e);",
|
|
129
145
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
130
146
|
' if(p==="sourceCapabilities")return fc;',
|
|
131
|
-
' if(p==="isTrusted")return k
|
|
147
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
132
148
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
133
149
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
134
150
|
" return k[p]+_f;",
|
|
@@ -280,6 +296,70 @@ function buildStealthInitScript() {
|
|
|
280
296
|
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
281
297
|
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
282
298
|
" }catch(e){}",
|
|
299
|
+
// 3f. getCoalescedEvents 合成(d47):真实鼠标 125Hz 采样被浏览器按帧合并,
|
|
300
|
+
// 快速移动时 pointermove.getCoalescedEvents() 返回 2~6 个事件(群内
|
|
301
|
+
// ≈8ms);CDP Input 逐事件派发不走合并管线,coalesced>1 恒为 0(结构性
|
|
302
|
+
// 暴露,间隔模拟救不了——实测 Chrome 帧对齐时 CDP 连发事件被直接丢弃
|
|
303
|
+
// 而非合并)。按 125Hz 物理插值合成 coalesced 群:期望样本数 = dt/8ms-1,
|
|
304
|
+
// 合成事件用真 PointerEvent 构造 + 实例级 isTrusted/timeStamp 遮蔽。
|
|
305
|
+
" try{",
|
|
306
|
+
" if(window.PointerEvent&&PointerEvent.prototype.getCoalescedEvents){",
|
|
307
|
+
" var _gce=PointerEvent.prototype.getCoalescedEvents;",
|
|
308
|
+
" var _lm=null;",
|
|
309
|
+
" var _gceH=function(){",
|
|
310
|
+
" var list=_gce.call(this);",
|
|
311
|
+
' if(this.type!=="pointermove"||!this.isTrusted)return list;',
|
|
312
|
+
" var cur={x:this.clientX,y:this.clientY,ts:this.timeStamp};",
|
|
313
|
+
" var out=null;",
|
|
314
|
+
" if((!list||list.length<2)&&_lm){",
|
|
315
|
+
" var dt=cur.ts-_lm.ts,dx=cur.x-_lm.x,dy=cur.y-_lm.y;",
|
|
316
|
+
// dt 长(帧丢弃后)不代表群覆盖整个 dt —— 真实 coalesced 群只覆盖
|
|
317
|
+
// 最后一帧窗口(≤16.7ms,群内 ≈8ms=125Hz)。合成群从自身往回推 8ms
|
|
318
|
+
// 链,位移取末端占比(span/dt,dt 远大于 span 时位移趋零=丢弃后实况)。
|
|
319
|
+
" var expN=Math.floor(dt/8)-1;",
|
|
320
|
+
// dt<12ms(不足一帧+采样周期)时真实浏览器不会产生合并群 ——
|
|
321
|
+
// 单采样帧 coalesced=1,强行合成会出现 ~2ms 的超物理群内间隔。
|
|
322
|
+
" if(expN>0&&dt>=12&&Math.random()>0.15){",
|
|
323
|
+
" var n=Math.min(4,expN+(Math.random()<0.3?1:0));",
|
|
324
|
+
" var span=Math.min(dt,n*8.3);",
|
|
325
|
+
" var frac=Math.min(1,span/Math.max(dt,1));",
|
|
326
|
+
" out=[];",
|
|
327
|
+
" for(var i=1;i<=n;i++){",
|
|
328
|
+
// k=距自身的步数(含自身共 n+1 个事件,相邻恒 ≈8.3ms=125Hz 周期)
|
|
329
|
+
" var k=n+1-i;",
|
|
330
|
+
' var ev=new PointerEvent("pointermove",{',
|
|
331
|
+
' pointerId:this.pointerId,pointerType:"mouse",isPrimary:this.isPrimary,',
|
|
332
|
+
" clientX:cur.x-dx*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
333
|
+
" clientY:cur.y-dy*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
334
|
+
" screenX:this.screenX,screenY:this.screenY,",
|
|
335
|
+
" buttons:this.buttons,button:this.button,",
|
|
336
|
+
" bubbles:true,cancelable:true,composed:true,",
|
|
337
|
+
" width:this.width,height:this.height,pressure:this.pressure,",
|
|
338
|
+
" tiltX:this.tiltX,tiltY:this.tiltY,twist:this.twist});",
|
|
339
|
+
" var tsV=cur.ts-k*8.3-Math.random()*1.2;",
|
|
340
|
+
// isTrusted/timeStamp 是实例不可配置属性(defineProperty 抛
|
|
341
|
+
// "Cannot redefine"),改 Proxy 包装:getPrototypeOf/ownKeys 透传,
|
|
342
|
+
// instanceof 与属性枚举行为与真事件一致。IIFE 捕获本次循环的 tsV
|
|
343
|
+
// 快照 —— var 提升会让所有 Proxy 闭包共享最后一次赋值(实测四个
|
|
344
|
+
// 合成事件同时间戳、群内间隔塌到 ~2ms)。
|
|
345
|
+
" out.push((function(e2,t2){return new Proxy(e2,{get:function(t,p){",
|
|
346
|
+
' if(p==="isTrusted")return true;',
|
|
347
|
+
' if(p==="timeStamp")return t2;',
|
|
348
|
+
' var v=Reflect.get(t,p);return typeof v==="function"?v.bind(t):v;',
|
|
349
|
+
" }});})(ev,tsV));",
|
|
350
|
+
" }",
|
|
351
|
+
" out.push(this);",
|
|
352
|
+
" }",
|
|
353
|
+
" }",
|
|
354
|
+
" _lm=cur;",
|
|
355
|
+
" return out||list;",
|
|
356
|
+
" };",
|
|
357
|
+
" PointerEvent.prototype.getCoalescedEvents=_gceH;",
|
|
358
|
+
// name 反查伪装:var _gceH=fn 的具名推断会暴露 hook(原生 name 是
|
|
359
|
+
// "getCoalescedEvents")——toString 白名单管不到 name 属性。
|
|
360
|
+
' try{Object.defineProperty(_gceH,"name",{value:"getCoalescedEvents"});}catch(e){}',
|
|
361
|
+
" }",
|
|
362
|
+
" }catch(e){}",
|
|
283
363
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
284
364
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
285
365
|
" try{",
|
|
@@ -314,11 +394,12 @@ function buildStealthInitScript() {
|
|
|
314
394
|
' if(this===HTMLCanvasElement.prototype.toDataURL)return"function toDataURL() { [native code] }";',
|
|
315
395
|
' if(this===AnalyserNode.prototype.getFloatFrequencyData)return"function getFloatFrequencyData() { [native code] }";',
|
|
316
396
|
' if(this===AudioBuffer.prototype.getChannelData)return"function getChannelData() { [native code] }";',
|
|
397
|
+
' if(this===_gceH)return"function getCoalescedEvents() { [native code] }";',
|
|
317
398
|
" return _ts.call(this);",
|
|
318
399
|
" };",
|
|
319
400
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
320
401
|
" var _ba=function(k,p){",
|
|
321
|
-
' if(p==="isTrusted")return k
|
|
402
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
322
403
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
323
404
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
324
405
|
" }",
|
|
@@ -374,26 +455,26 @@ var XBMouseImpl = class {
|
|
|
374
455
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: p.x, y: p.y, button: this._button });
|
|
375
456
|
this._x = p.x;
|
|
376
457
|
this._y = p.y;
|
|
377
|
-
await
|
|
458
|
+
await sleep2(p.delay);
|
|
378
459
|
}
|
|
379
460
|
this._x = tx;
|
|
380
461
|
this._y = ty;
|
|
381
462
|
if (_truncated) {
|
|
382
463
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: tx, y: ty, button: this._button });
|
|
383
464
|
}
|
|
384
|
-
await
|
|
465
|
+
await sleep2(rand(...DEFAULT_STEALTH_CONFIG.aimPause));
|
|
385
466
|
} else {
|
|
386
467
|
await this.move(tx, ty);
|
|
387
468
|
}
|
|
388
469
|
await this.down({ button });
|
|
389
|
-
await
|
|
470
|
+
await sleep2(stealth ? rand(...DEFAULT_STEALTH_CONFIG.pressDuration) : opts.delay ?? 0);
|
|
390
471
|
const rx = stealth ? this._x + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._x;
|
|
391
472
|
const ry = stealth ? this._y + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._y;
|
|
392
473
|
this._x = rx;
|
|
393
474
|
this._y = ry;
|
|
394
475
|
await this.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: rx, y: ry, button, clickCount: opts.clickCount ?? 1 });
|
|
395
476
|
for (let i = 1; i < (opts.clickCount ?? 1); i++) {
|
|
396
|
-
if (opts.delay) await
|
|
477
|
+
if (opts.delay) await sleep2(opts.delay);
|
|
397
478
|
await this.down({ button });
|
|
398
479
|
await this.up({ button });
|
|
399
480
|
}
|
|
@@ -424,12 +505,14 @@ var XBMouseImpl = class {
|
|
|
424
505
|
});
|
|
425
506
|
}
|
|
426
507
|
async move(x, y, opts = {}) {
|
|
508
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
427
509
|
const steps = Math.max(1, opts.steps ?? 1);
|
|
428
510
|
const fromX = this._x;
|
|
429
511
|
const fromY = this._y;
|
|
430
512
|
const dx = x - fromX;
|
|
431
513
|
const dy = y - fromY;
|
|
432
514
|
for (let i = 1; i <= steps; i++) {
|
|
515
|
+
if (stealth) await sleep2(rand(16, 28));
|
|
433
516
|
const t = i / steps;
|
|
434
517
|
this._x = fromX + dx * t;
|
|
435
518
|
this._y = fromY + dy * t;
|
|
@@ -456,7 +539,7 @@ var XBMouseImpl = class {
|
|
|
456
539
|
await this.conn.send(method, params, this.sessionId);
|
|
457
540
|
}
|
|
458
541
|
};
|
|
459
|
-
function
|
|
542
|
+
function sleep2(ms) {
|
|
460
543
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
461
544
|
}
|
|
462
545
|
|
|
@@ -486,17 +569,17 @@ var XBKeyboardImpl = class {
|
|
|
486
569
|
await this.dispatchKeyEvent(downParams);
|
|
487
570
|
if (mapping.text) {
|
|
488
571
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
489
|
-
await
|
|
572
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
490
573
|
}
|
|
491
574
|
await this.dispatchKeyEvent({
|
|
492
575
|
type: "char",
|
|
493
576
|
text: mapping.text
|
|
494
577
|
});
|
|
495
578
|
}
|
|
496
|
-
if (delay > 0) await
|
|
579
|
+
if (delay > 0) await sleep3(delay);
|
|
497
580
|
else {
|
|
498
581
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
499
|
-
await
|
|
582
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
500
583
|
}
|
|
501
584
|
}
|
|
502
585
|
const upParams = {
|
|
@@ -538,37 +621,61 @@ var XBKeyboardImpl = class {
|
|
|
538
621
|
await this.dispatchKeyEvent(params);
|
|
539
622
|
}
|
|
540
623
|
async type(text, opts = {}) {
|
|
541
|
-
const
|
|
624
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
625
|
+
const fixedDelay = opts.delay ?? 0;
|
|
542
626
|
for (const char of text) {
|
|
543
|
-
if (
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
key: mapping.key,
|
|
548
|
-
code: mapping.code
|
|
549
|
-
};
|
|
550
|
-
if (mapping.text) {
|
|
551
|
-
downParams.text = mapping.text;
|
|
552
|
-
downParams.unmodifiedText = mapping.text;
|
|
627
|
+
if (fixedDelay > 0) {
|
|
628
|
+
await sleep3(fixedDelay);
|
|
629
|
+
} else if (stealth) {
|
|
630
|
+
await sleep3(typingDelay());
|
|
553
631
|
}
|
|
554
|
-
if (
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
text: mapping.text
|
|
562
|
-
});
|
|
632
|
+
if (stealth && char.length === 1 && Math.random() < DEFAULT_STEALTH_CONFIG.typoProbability) {
|
|
633
|
+
const wrong = NEIGHBOR_KEYS[char];
|
|
634
|
+
if (wrong && wrong !== char) {
|
|
635
|
+
await this.dispatchKeySequence(resolveKeyMapping(wrong), stealth);
|
|
636
|
+
await sleep3(rand(120, 420));
|
|
637
|
+
await this.dispatchKeySequence(KEY_MAP2.Backspace, stealth);
|
|
638
|
+
}
|
|
563
639
|
}
|
|
564
|
-
await this.
|
|
565
|
-
type: "keyUp",
|
|
566
|
-
key: mapping.key,
|
|
567
|
-
code: mapping.code,
|
|
568
|
-
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
569
|
-
});
|
|
640
|
+
await this.dispatchKeySequence(resolveKeyMapping(char), stealth);
|
|
570
641
|
}
|
|
571
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Single-key event sequence: rawKeyDown → (char) → keyUp.
|
|
645
|
+
* Shared by press() and type() — two separate implementations drifted
|
|
646
|
+
* apart once already (S60: type() lost the keyPressDuration that press()
|
|
647
|
+
* had; d50 caught it as 33/33 keys with down→up of 2ms).
|
|
648
|
+
*/
|
|
649
|
+
async dispatchKeySequence(mapping, stealth) {
|
|
650
|
+
const downParams = {
|
|
651
|
+
type: "rawKeyDown",
|
|
652
|
+
key: mapping.key,
|
|
653
|
+
code: mapping.code
|
|
654
|
+
};
|
|
655
|
+
if (mapping.text) {
|
|
656
|
+
downParams.text = mapping.text;
|
|
657
|
+
downParams.unmodifiedText = mapping.text;
|
|
658
|
+
}
|
|
659
|
+
if (mapping.keyCode) {
|
|
660
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
661
|
+
}
|
|
662
|
+
await this.dispatchKeyEvent(downParams);
|
|
663
|
+
if (mapping.text) {
|
|
664
|
+
await this.dispatchKeyEvent({ type: "char", text: mapping.text });
|
|
665
|
+
}
|
|
666
|
+
if (stealth) {
|
|
667
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
668
|
+
}
|
|
669
|
+
const upParams = {
|
|
670
|
+
type: "keyUp",
|
|
671
|
+
key: mapping.key,
|
|
672
|
+
code: mapping.code
|
|
673
|
+
};
|
|
674
|
+
if (mapping.keyCode) {
|
|
675
|
+
upParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
676
|
+
}
|
|
677
|
+
await this.dispatchKeyEvent(upParams);
|
|
678
|
+
}
|
|
572
679
|
async insertText(text) {
|
|
573
680
|
await this.conn.send(
|
|
574
681
|
"Input.insertText",
|
|
@@ -576,6 +683,35 @@ var XBKeyboardImpl = class {
|
|
|
576
683
|
this.sessionId
|
|
577
684
|
);
|
|
578
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Navigation key press using CDP type 'keyDown' (NOT rawKeyDown).
|
|
688
|
+
* rawKeyDown skips browser default actions — select option navigation,
|
|
689
|
+
* arrow-key scrolling etc. never fire under it (d53: ArrowDown on a
|
|
690
|
+
* focused <select> left the value unchanged). keyDown carries the
|
|
691
|
+
* default action.
|
|
692
|
+
*/
|
|
693
|
+
async pressNav(key) {
|
|
694
|
+
const mapping = resolveKeyMapping(key);
|
|
695
|
+
const downParams = {
|
|
696
|
+
type: "keyDown",
|
|
697
|
+
key: mapping.key,
|
|
698
|
+
code: mapping.code
|
|
699
|
+
};
|
|
700
|
+
if (mapping.keyCode) {
|
|
701
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
702
|
+
downParams.nativeVirtualKeyCode = mapping.keyCode;
|
|
703
|
+
}
|
|
704
|
+
await this.dispatchKeyEvent(downParams);
|
|
705
|
+
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
706
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
707
|
+
}
|
|
708
|
+
await this.dispatchKeyEvent({
|
|
709
|
+
type: "keyUp",
|
|
710
|
+
key: mapping.key,
|
|
711
|
+
code: mapping.code,
|
|
712
|
+
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
713
|
+
});
|
|
714
|
+
}
|
|
579
715
|
async dispatchKeyEvent(params) {
|
|
580
716
|
await this.conn.send("Input.dispatchKeyEvent", params, this.sessionId);
|
|
581
717
|
}
|
|
@@ -630,7 +766,44 @@ var KEY_MAP2 = {
|
|
|
630
766
|
F11: { key: "F11", code: "F11", keyCode: 122 },
|
|
631
767
|
F12: { key: "F12", code: "F12", keyCode: 123 }
|
|
632
768
|
};
|
|
633
|
-
|
|
769
|
+
var NEIGHBOR_KEYS = {
|
|
770
|
+
a: "s",
|
|
771
|
+
b: "v",
|
|
772
|
+
c: "x",
|
|
773
|
+
d: "f",
|
|
774
|
+
e: "r",
|
|
775
|
+
f: "g",
|
|
776
|
+
g: "h",
|
|
777
|
+
h: "j",
|
|
778
|
+
i: "o",
|
|
779
|
+
j: "k",
|
|
780
|
+
k: "l",
|
|
781
|
+
l: "k",
|
|
782
|
+
m: "n",
|
|
783
|
+
n: "m",
|
|
784
|
+
o: "p",
|
|
785
|
+
p: "o",
|
|
786
|
+
q: "w",
|
|
787
|
+
r: "t",
|
|
788
|
+
s: "d",
|
|
789
|
+
t: "y",
|
|
790
|
+
u: "i",
|
|
791
|
+
v: "b",
|
|
792
|
+
w: "e",
|
|
793
|
+
x: "c",
|
|
794
|
+
y: "u",
|
|
795
|
+
z: "x",
|
|
796
|
+
"1": "2",
|
|
797
|
+
"2": "3",
|
|
798
|
+
"3": "4",
|
|
799
|
+
"4": "5",
|
|
800
|
+
"5": "6",
|
|
801
|
+
"6": "7",
|
|
802
|
+
"7": "8",
|
|
803
|
+
"8": "9",
|
|
804
|
+
"9": "0"
|
|
805
|
+
};
|
|
806
|
+
function sleep3(ms) {
|
|
634
807
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
635
808
|
}
|
|
636
809
|
|
|
@@ -845,23 +1018,13 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
845
1018
|
async press(key, opts = {}) {
|
|
846
1019
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
847
1020
|
await scrollIntoView(this.page, this.selector);
|
|
848
|
-
await this.
|
|
849
|
-
(function() {
|
|
850
|
-
const el = ${this._q(this.selector)};
|
|
851
|
-
if (el) el.focus();
|
|
852
|
-
})()
|
|
853
|
-
`);
|
|
1021
|
+
await this.click({ timeout: opts.timeout });
|
|
854
1022
|
await this.page.keyboard.press(key);
|
|
855
1023
|
}
|
|
856
1024
|
async pressSequentially(text, opts = {}) {
|
|
857
1025
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
858
1026
|
await scrollIntoView(this.page, this.selector);
|
|
859
|
-
await this.
|
|
860
|
-
(function() {
|
|
861
|
-
const el = ${this._q(this.selector)};
|
|
862
|
-
if (el) el.focus();
|
|
863
|
-
})()
|
|
864
|
-
`);
|
|
1027
|
+
await this.click({ timeout: opts.timeout });
|
|
865
1028
|
await this.page.keyboard.type(text, { delay: opts.delay });
|
|
866
1029
|
}
|
|
867
1030
|
async hover(opts = {}) {
|
|
@@ -916,11 +1079,36 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
916
1079
|
async selectOption(value) {
|
|
917
1080
|
await waitForActionable(this.page, this.selector);
|
|
918
1081
|
const values = Array.isArray(value) ? value : [value];
|
|
919
|
-
const
|
|
1082
|
+
const info = await this.page.evaluate(`
|
|
920
1083
|
(function() {
|
|
921
1084
|
const el = ${this._q(this.selector)};
|
|
922
1085
|
if (!el || el.tagName !== 'SELECT') throw new Error('Not a select element');
|
|
923
|
-
|
|
1086
|
+
const values = ${JSON.stringify(values)};
|
|
1087
|
+
let target = -1, targetValue = '';
|
|
1088
|
+
outer:
|
|
1089
|
+
for (let i = 0; i < el.options.length; i++) {
|
|
1090
|
+
const opt = el.options[i];
|
|
1091
|
+
for (const v of values) {
|
|
1092
|
+
const hit = typeof v === 'object'
|
|
1093
|
+
? (v.label !== undefined ? opt.label === v.label
|
|
1094
|
+
: v.value !== undefined ? opt.value === v.value
|
|
1095
|
+
: opt.index === v.index)
|
|
1096
|
+
: (opt.value === v || opt.label === v);
|
|
1097
|
+
if (hit) { target = i; targetValue = opt.value; break outer; }
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return { cur: el.selectedIndex, target: target, targetValue: targetValue, multiple: el.multiple };
|
|
1101
|
+
})()
|
|
1102
|
+
`);
|
|
1103
|
+
if (info.target < 0) {
|
|
1104
|
+
throw new Error(`Option not found: ${JSON.stringify(values)}`);
|
|
1105
|
+
}
|
|
1106
|
+
if (!info.multiple) {
|
|
1107
|
+
await this.click({ timeout: 5e3 });
|
|
1108
|
+
}
|
|
1109
|
+
const selected = await this.page.evaluate(`
|
|
1110
|
+
(function() {
|
|
1111
|
+
const el = ${this._q(this.selector)};
|
|
924
1112
|
const values = ${JSON.stringify(values)};
|
|
925
1113
|
const selectedValues = [];
|
|
926
1114
|
|
|
@@ -4556,6 +4744,9 @@ async function launch(options = {}) {
|
|
|
4556
4744
|
}
|
|
4557
4745
|
|
|
4558
4746
|
export {
|
|
4747
|
+
rand,
|
|
4748
|
+
sleep,
|
|
4749
|
+
wheelDelta,
|
|
4559
4750
|
XBMouseImpl,
|
|
4560
4751
|
XBKeyboardImpl,
|
|
4561
4752
|
waitForActionable,
|
package/dist/cli.js
CHANGED
|
@@ -56,10 +56,13 @@ import {
|
|
|
56
56
|
getAllSessions,
|
|
57
57
|
getBrowser,
|
|
58
58
|
getSessionById,
|
|
59
|
+
rand,
|
|
59
60
|
resolveLaunchOpts,
|
|
60
61
|
saveSessionDiskMeta,
|
|
61
|
-
setActivePage
|
|
62
|
-
|
|
62
|
+
setActivePage,
|
|
63
|
+
sleep,
|
|
64
|
+
wheelDelta
|
|
65
|
+
} from "./chunk-HFP4QCZL.js";
|
|
63
66
|
import "./chunk-TNEN6VQ2.js";
|
|
64
67
|
import {
|
|
65
68
|
errMsg
|
|
@@ -352,7 +355,8 @@ var gotoCommand = registerCommand({
|
|
|
352
355
|
parameters: z.object({
|
|
353
356
|
url: z.string(),
|
|
354
357
|
waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
|
|
355
|
-
timeout: z.number().optional()
|
|
358
|
+
timeout: z.number().optional(),
|
|
359
|
+
referrer: z.string().optional()
|
|
356
360
|
}),
|
|
357
361
|
result: z.object({
|
|
358
362
|
url: z.string(),
|
|
@@ -373,7 +377,9 @@ var gotoCommand = registerCommand({
|
|
|
373
377
|
try {
|
|
374
378
|
response = await ctx.page.goto(url, {
|
|
375
379
|
waitUntil: p.waitUntil || "domcontentloaded",
|
|
376
|
-
...p.timeout ? { timeout: p.timeout } : {}
|
|
380
|
+
...p.timeout ? { timeout: p.timeout } : {},
|
|
381
|
+
// referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
|
|
382
|
+
...p.referrer ? { referer: p.referrer } : {}
|
|
377
383
|
});
|
|
378
384
|
} catch (err) {
|
|
379
385
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -900,21 +906,34 @@ var scrollCommand = registerCommand({
|
|
|
900
906
|
}),
|
|
901
907
|
handler: async (p, ctx) => {
|
|
902
908
|
const distance = p.distance ?? 500;
|
|
903
|
-
const
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
right: [distance, 0],
|
|
907
|
-
left: [-distance, 0]
|
|
908
|
-
};
|
|
909
|
-
const [dx, dy] = deltas[p.direction];
|
|
909
|
+
const sign = { down: 1, up: -1, right: 1, left: -1 };
|
|
910
|
+
const vertical = p.direction === "down" || p.direction === "up";
|
|
911
|
+
const s = sign[p.direction];
|
|
910
912
|
if (p.selector) {
|
|
911
913
|
const element = ctx.page.locator(p.selector).first();
|
|
912
914
|
await element.evaluate((el, args) => {
|
|
913
915
|
const [dxx, dyy] = args;
|
|
914
916
|
el.scrollBy(dxx, dyy);
|
|
915
|
-
}, [
|
|
917
|
+
}, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
|
|
918
|
+
} else if (process.env.XBROWSER_STEALTH !== "off") {
|
|
919
|
+
let acc = 0;
|
|
920
|
+
let step = 0;
|
|
921
|
+
while (acc < distance && step < 60) {
|
|
922
|
+
const d = Math.min(wheelDelta(step), distance - acc);
|
|
923
|
+
if (d < 1) break;
|
|
924
|
+
await ctx.page.mouse.wheel(
|
|
925
|
+
vertical ? 0 : d * s,
|
|
926
|
+
vertical ? d * s : 0
|
|
927
|
+
);
|
|
928
|
+
acc += d;
|
|
929
|
+
step++;
|
|
930
|
+
await sleep(rand(16, 28));
|
|
931
|
+
}
|
|
916
932
|
} else {
|
|
917
|
-
await ctx.page.mouse.wheel(
|
|
933
|
+
await ctx.page.mouse.wheel(
|
|
934
|
+
vertical ? 0 : distance * s,
|
|
935
|
+
vertical ? distance * s : 0
|
|
936
|
+
);
|
|
918
937
|
}
|
|
919
938
|
return ok5({ direction: p.direction, distance });
|
|
920
939
|
}
|
|
@@ -7523,7 +7542,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7523
7542
|
}
|
|
7524
7543
|
let targetPageOverride = null;
|
|
7525
7544
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7526
|
-
const { findTargetPage } = await import("./browser-
|
|
7545
|
+
const { findTargetPage } = await import("./browser-JNT2I73V.js");
|
|
7527
7546
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7528
7547
|
if (!targetPageOverride) {
|
|
7529
7548
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -7803,6 +7822,9 @@ async function executeChain(input, options) {
|
|
|
7803
7822
|
for (const pipeline of pipelines) {
|
|
7804
7823
|
const { type, pipeline: commands } = pipeline;
|
|
7805
7824
|
for (const cmdStr of commands) {
|
|
7825
|
+
if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
|
|
7826
|
+
await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
|
|
7827
|
+
}
|
|
7806
7828
|
const parts = splitCommand(cmdStr);
|
|
7807
7829
|
if (parts.length === 0) continue;
|
|
7808
7830
|
const cmdName = parts[0];
|
|
@@ -10173,12 +10195,33 @@ function normalizeSelector(input) {
|
|
|
10173
10195
|
|
|
10174
10196
|
// src/cli/browser-routes.ts
|
|
10175
10197
|
import { helpGenerator } from "@dyyz1993/xcli-core";
|
|
10198
|
+
function autoCompleteParams(cmdName, params, options) {
|
|
10199
|
+
try {
|
|
10200
|
+
const cmd = getCommand(cmdName);
|
|
10201
|
+
if (!cmd?.parameters) return params;
|
|
10202
|
+
const schema = asZodSchema(cmd.parameters);
|
|
10203
|
+
const shape = schema?.shape ?? schema?._def?.shape;
|
|
10204
|
+
if (!shape) return params;
|
|
10205
|
+
for (const key of Object.keys(shape)) {
|
|
10206
|
+
if (params[key] !== void 0) continue;
|
|
10207
|
+
const v = options[key];
|
|
10208
|
+
if (v === void 0 || v === true && key === "json" || key === "yaml") continue;
|
|
10209
|
+
if (typeof v === "string" && v === "") continue;
|
|
10210
|
+
params[key] = v;
|
|
10211
|
+
}
|
|
10212
|
+
return params;
|
|
10213
|
+
} catch {
|
|
10214
|
+
return params;
|
|
10215
|
+
}
|
|
10216
|
+
}
|
|
10176
10217
|
function parseSelectorFlags(args, options) {
|
|
10177
|
-
const
|
|
10178
|
-
const
|
|
10218
|
+
const rawSelector = options.s ?? options.selector;
|
|
10219
|
+
const rawValue = options.v ?? options.value;
|
|
10220
|
+
const selector = typeof rawSelector === "string" ? normalizeSelector(rawSelector) : void 0;
|
|
10221
|
+
const value = typeof rawValue === "string" ? rawValue : void 0;
|
|
10179
10222
|
const remaining = args.filter((a) => !a.startsWith("-"));
|
|
10180
10223
|
return {
|
|
10181
|
-
selector
|
|
10224
|
+
selector,
|
|
10182
10225
|
value,
|
|
10183
10226
|
remaining
|
|
10184
10227
|
};
|
|
@@ -10266,7 +10309,11 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10266
10309
|
if (!sel || !txt)
|
|
10267
10310
|
outputError("Usage: xbrowser type <selector> <text>\n xbrowser type -s <selector> -v <text>");
|
|
10268
10311
|
cmdName = "type";
|
|
10269
|
-
params = {
|
|
10312
|
+
params = {
|
|
10313
|
+
selector: sel,
|
|
10314
|
+
text: txt,
|
|
10315
|
+
...options.delay !== void 0 ? { delay: Number(options.delay) } : {}
|
|
10316
|
+
};
|
|
10270
10317
|
break;
|
|
10271
10318
|
}
|
|
10272
10319
|
case "press": {
|
|
@@ -10408,7 +10455,13 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10408
10455
|
outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
|
|
10409
10456
|
}
|
|
10410
10457
|
cmdName = "mouse";
|
|
10411
|
-
params = {
|
|
10458
|
+
params = {
|
|
10459
|
+
action,
|
|
10460
|
+
x,
|
|
10461
|
+
y,
|
|
10462
|
+
...options.button ? { button: options.button } : {},
|
|
10463
|
+
...options.steps !== void 0 ? { steps: Number(options.steps) } : {}
|
|
10464
|
+
};
|
|
10412
10465
|
break;
|
|
10413
10466
|
}
|
|
10414
10467
|
case "html":
|
|
@@ -10610,6 +10663,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10610
10663
|
if (target) {
|
|
10611
10664
|
params = { ...params, _target: target };
|
|
10612
10665
|
}
|
|
10666
|
+
params = autoCompleteParams(cmdName, params, options);
|
|
10613
10667
|
const tabIndex = options.tab;
|
|
10614
10668
|
if (tabIndex !== void 0) {
|
|
10615
10669
|
params = { ...params, _tabIndex: Number(tabIndex) };
|
|
@@ -13663,7 +13717,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13663
13717
|
const targetPage = pages[cmdTabIndex];
|
|
13664
13718
|
await targetPage.bringToFront().catch(() => {
|
|
13665
13719
|
});
|
|
13666
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
13720
|
+
const { setActivePage: setActivePage2 } = await import("./browser-JNT2I73V.js");
|
|
13667
13721
|
setActivePage2(session, targetPage);
|
|
13668
13722
|
}
|
|
13669
13723
|
}
|
|
@@ -13939,7 +13993,7 @@ async function main() {
|
|
|
13939
13993
|
const command = process.argv[2];
|
|
13940
13994
|
const isLongRunning = command === "preview" || command === "serve";
|
|
13941
13995
|
if (!isLongRunning) {
|
|
13942
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
13996
|
+
const { ensureProcessCanExit } = await import("./browser-JNT2I73V.js");
|
|
13943
13997
|
await ensureProcessCanExit().catch(() => {
|
|
13944
13998
|
});
|
|
13945
13999
|
process.exit(process.exitCode || exitCode);
|