@xbrowser/cli 1.14.0 → 1.16.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-KJWBHGHX.js → browser-3KTEVTMU.js} +2 -2
- package/dist/{browser-5A6AL62M.js → browser-AOVQTGJ7.js} +2 -2
- package/dist/{browser-DI6UQN6Q.js → browser-KT4FOWBO.js} +1 -1
- package/dist/{cdp-driver-QXTXJZH7.js → cdp-driver-ALDP2CZT.js} +248 -49
- package/dist/{cdp-driver-4SFS6GNY.js → cdp-driver-DKNLIA6B.js} +1 -1
- package/dist/{cdp-driver-AHKUT57K.js → cdp-driver-OC37OT4B.js} +1 -1
- package/dist/{chunk-BTFKJD7Z.js → chunk-5EYOEB4R.js} +277 -65
- package/dist/{chunk-B753M33E.js → chunk-7V3B7VPX.js} +9 -7
- package/dist/{chunk-PYQGDBAF.js → chunk-A5HJ6IRE.js} +269 -59
- package/dist/{chunk-ZD4OSYOX.js → chunk-GWQ5NTVE.js} +9 -7
- package/dist/{chunk-UPAWITVM.js → chunk-LTBNQERK.js} +269 -59
- package/dist/cli.js +86 -32
- package/dist/clipboard-BOL2GP2E.js +58 -0
- package/dist/clipboard-XRP54ENE.js +58 -0
- package/dist/daemon-main.js +44 -22
- package/dist/index.d.ts +4 -0
- package/dist/index.js +106 -51
- package/dist/{session-replayer-I3PJFO3H.js → session-replayer-MJH5W7BB.js} +1 -1
- package/package.json +1 -1
- package/dist/{recovery-Z3RDZENA.js → recovery-MDWAVXE4.js} +4 -4
- package/dist/{recovery-FTZGV6VY.js → recovery-NXC35EQN.js} +4 -4
|
@@ -29,7 +29,7 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
29
29
|
bezierCurvature: [0.35, 0.6],
|
|
30
30
|
noiseAmplitude: 5.5,
|
|
31
31
|
overshootRange: [6, 14],
|
|
32
|
-
aimPause: [
|
|
32
|
+
aimPause: [80, 280],
|
|
33
33
|
pressDuration: [60, 140],
|
|
34
34
|
releaseDrift: [0.8, 2.5],
|
|
35
35
|
landingOffsetSmall: [0.3, 2.5],
|
|
@@ -50,6 +50,9 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
50
50
|
function rand(min, max) {
|
|
51
51
|
return min + Math.random() * (max - min);
|
|
52
52
|
}
|
|
53
|
+
function sleep(ms) {
|
|
54
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
|
+
}
|
|
53
56
|
function cosineEase(t) {
|
|
54
57
|
return 0.5 - 0.5 * Math.cos(Math.PI * t);
|
|
55
58
|
}
|
|
@@ -97,6 +100,18 @@ function landingOffset(width, height, config = DEFAULT_STEALTH_CONFIG) {
|
|
|
97
100
|
const dy = rand(...range) * (Math.random() < 0.5 ? -1 : 1);
|
|
98
101
|
return { dx, dy };
|
|
99
102
|
}
|
|
103
|
+
function typingDelay(config = DEFAULT_STEALTH_CONFIG) {
|
|
104
|
+
const roll = Math.random();
|
|
105
|
+
const r = config.typingRhythm;
|
|
106
|
+
if (roll < r.pauseProb) return rand(...r.pauseRange);
|
|
107
|
+
if (roll < r.pauseProb + r.fastProb) return rand(...r.fastRange);
|
|
108
|
+
return rand(...r.normalRange);
|
|
109
|
+
}
|
|
110
|
+
function wheelDelta(step, config = DEFAULT_STEALTH_CONFIG) {
|
|
111
|
+
return Math.round(
|
|
112
|
+
config.wheelPeak * Math.exp(-step * config.wheelDecayRate) * rand(0.85, 1.15)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
100
115
|
var KEY_MAP = {};
|
|
101
116
|
for (let i = 97; i <= 122; i++) {
|
|
102
117
|
const ch = String.fromCharCode(i);
|
|
@@ -120,6 +135,7 @@ Object.assign(KEY_MAP, {
|
|
|
120
135
|
function buildStealthInitScript() {
|
|
121
136
|
return [
|
|
122
137
|
"(function(){",
|
|
138
|
+
' window.__xbStealthVer="57";',
|
|
123
139
|
// 1. AEL event proxy
|
|
124
140
|
" var o=EventTarget.prototype.addEventListener;",
|
|
125
141
|
" var fc=new InputDeviceCapabilities({firesTouchEvents:false});",
|
|
@@ -282,6 +298,70 @@ function buildStealthInitScript() {
|
|
|
282
298
|
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
283
299
|
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
284
300
|
" }catch(e){}",
|
|
301
|
+
// 3f. getCoalescedEvents 合成(d47):真实鼠标 125Hz 采样被浏览器按帧合并,
|
|
302
|
+
// 快速移动时 pointermove.getCoalescedEvents() 返回 2~6 个事件(群内
|
|
303
|
+
// ≈8ms);CDP Input 逐事件派发不走合并管线,coalesced>1 恒为 0(结构性
|
|
304
|
+
// 暴露,间隔模拟救不了——实测 Chrome 帧对齐时 CDP 连发事件被直接丢弃
|
|
305
|
+
// 而非合并)。按 125Hz 物理插值合成 coalesced 群:期望样本数 = dt/8ms-1,
|
|
306
|
+
// 合成事件用真 PointerEvent 构造 + 实例级 isTrusted/timeStamp 遮蔽。
|
|
307
|
+
" try{",
|
|
308
|
+
" if(window.PointerEvent&&PointerEvent.prototype.getCoalescedEvents){",
|
|
309
|
+
" var _gce=PointerEvent.prototype.getCoalescedEvents;",
|
|
310
|
+
" var _lm=null;",
|
|
311
|
+
" var _gceH=function(){",
|
|
312
|
+
" var list=_gce.call(this);",
|
|
313
|
+
' if(this.type!=="pointermove"||!this.isTrusted)return list;',
|
|
314
|
+
" var cur={x:this.clientX,y:this.clientY,ts:this.timeStamp};",
|
|
315
|
+
" var out=null;",
|
|
316
|
+
" if((!list||list.length<2)&&_lm){",
|
|
317
|
+
" var dt=cur.ts-_lm.ts,dx=cur.x-_lm.x,dy=cur.y-_lm.y;",
|
|
318
|
+
// dt 长(帧丢弃后)不代表群覆盖整个 dt —— 真实 coalesced 群只覆盖
|
|
319
|
+
// 最后一帧窗口(≤16.7ms,群内 ≈8ms=125Hz)。合成群从自身往回推 8ms
|
|
320
|
+
// 链,位移取末端占比(span/dt,dt 远大于 span 时位移趋零=丢弃后实况)。
|
|
321
|
+
" var expN=Math.floor(dt/8)-1;",
|
|
322
|
+
// dt<12ms(不足一帧+采样周期)时真实浏览器不会产生合并群 ——
|
|
323
|
+
// 单采样帧 coalesced=1,强行合成会出现 ~2ms 的超物理群内间隔。
|
|
324
|
+
" if(expN>0&&dt>=12&&Math.random()>0.15){",
|
|
325
|
+
" var n=Math.min(4,expN+(Math.random()<0.3?1:0));",
|
|
326
|
+
" var span=Math.min(dt,n*8.3);",
|
|
327
|
+
" var frac=Math.min(1,span/Math.max(dt,1));",
|
|
328
|
+
" out=[];",
|
|
329
|
+
" for(var i=1;i<=n;i++){",
|
|
330
|
+
// k=距自身的步数(含自身共 n+1 个事件,相邻恒 ≈8.3ms=125Hz 周期)
|
|
331
|
+
" var k=n+1-i;",
|
|
332
|
+
' var ev=new PointerEvent("pointermove",{',
|
|
333
|
+
' pointerId:this.pointerId,pointerType:"mouse",isPrimary:this.isPrimary,',
|
|
334
|
+
" clientX:cur.x-dx*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
335
|
+
" clientY:cur.y-dy*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
336
|
+
" screenX:this.screenX,screenY:this.screenY,",
|
|
337
|
+
" buttons:this.buttons,button:this.button,",
|
|
338
|
+
" bubbles:true,cancelable:true,composed:true,",
|
|
339
|
+
" width:this.width,height:this.height,pressure:this.pressure,",
|
|
340
|
+
" tiltX:this.tiltX,tiltY:this.tiltY,twist:this.twist});",
|
|
341
|
+
" var tsV=cur.ts-k*8.3-Math.random()*1.2;",
|
|
342
|
+
// isTrusted/timeStamp 是实例不可配置属性(defineProperty 抛
|
|
343
|
+
// "Cannot redefine"),改 Proxy 包装:getPrototypeOf/ownKeys 透传,
|
|
344
|
+
// instanceof 与属性枚举行为与真事件一致。IIFE 捕获本次循环的 tsV
|
|
345
|
+
// 快照 —— var 提升会让所有 Proxy 闭包共享最后一次赋值(实测四个
|
|
346
|
+
// 合成事件同时间戳、群内间隔塌到 ~2ms)。
|
|
347
|
+
" out.push((function(e2,t2){return new Proxy(e2,{get:function(t,p){",
|
|
348
|
+
' if(p==="isTrusted")return true;',
|
|
349
|
+
' if(p==="timeStamp")return t2;',
|
|
350
|
+
' var v=Reflect.get(t,p);return typeof v==="function"?v.bind(t):v;',
|
|
351
|
+
" }});})(ev,tsV));",
|
|
352
|
+
" }",
|
|
353
|
+
" out.push(this);",
|
|
354
|
+
" }",
|
|
355
|
+
" }",
|
|
356
|
+
" _lm=cur;",
|
|
357
|
+
" return out||list;",
|
|
358
|
+
" };",
|
|
359
|
+
" PointerEvent.prototype.getCoalescedEvents=_gceH;",
|
|
360
|
+
// name 反查伪装:var _gceH=fn 的具名推断会暴露 hook(原生 name 是
|
|
361
|
+
// "getCoalescedEvents")——toString 白名单管不到 name 属性。
|
|
362
|
+
' try{Object.defineProperty(_gceH,"name",{value:"getCoalescedEvents"});}catch(e){}',
|
|
363
|
+
" }",
|
|
364
|
+
" }catch(e){}",
|
|
285
365
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
286
366
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
287
367
|
" try{",
|
|
@@ -316,6 +396,7 @@ function buildStealthInitScript() {
|
|
|
316
396
|
' if(this===HTMLCanvasElement.prototype.toDataURL)return"function toDataURL() { [native code] }";',
|
|
317
397
|
' if(this===AnalyserNode.prototype.getFloatFrequencyData)return"function getFloatFrequencyData() { [native code] }";',
|
|
318
398
|
' if(this===AudioBuffer.prototype.getChannelData)return"function getChannelData() { [native code] }";',
|
|
399
|
+
' if(this===_gceH)return"function getCoalescedEvents() { [native code] }";',
|
|
319
400
|
" return _ts.call(this);",
|
|
320
401
|
" };",
|
|
321
402
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
@@ -376,26 +457,26 @@ var XBMouseImpl = class {
|
|
|
376
457
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: p.x, y: p.y, button: this._button });
|
|
377
458
|
this._x = p.x;
|
|
378
459
|
this._y = p.y;
|
|
379
|
-
await
|
|
460
|
+
await sleep2(p.delay);
|
|
380
461
|
}
|
|
381
462
|
this._x = tx;
|
|
382
463
|
this._y = ty;
|
|
383
464
|
if (_truncated) {
|
|
384
465
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: tx, y: ty, button: this._button });
|
|
385
466
|
}
|
|
386
|
-
await
|
|
467
|
+
await sleep2(rand(...DEFAULT_STEALTH_CONFIG.aimPause));
|
|
387
468
|
} else {
|
|
388
469
|
await this.move(tx, ty);
|
|
389
470
|
}
|
|
390
471
|
await this.down({ button });
|
|
391
|
-
await
|
|
472
|
+
await sleep2(stealth ? rand(...DEFAULT_STEALTH_CONFIG.pressDuration) : opts.delay ?? 0);
|
|
392
473
|
const rx = stealth ? this._x + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._x;
|
|
393
474
|
const ry = stealth ? this._y + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._y;
|
|
394
475
|
this._x = rx;
|
|
395
476
|
this._y = ry;
|
|
396
477
|
await this.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: rx, y: ry, button, clickCount: opts.clickCount ?? 1 });
|
|
397
478
|
for (let i = 1; i < (opts.clickCount ?? 1); i++) {
|
|
398
|
-
if (opts.delay) await
|
|
479
|
+
if (opts.delay) await sleep2(opts.delay);
|
|
399
480
|
await this.down({ button });
|
|
400
481
|
await this.up({ button });
|
|
401
482
|
}
|
|
@@ -426,12 +507,14 @@ var XBMouseImpl = class {
|
|
|
426
507
|
});
|
|
427
508
|
}
|
|
428
509
|
async move(x, y, opts = {}) {
|
|
510
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
429
511
|
const steps = Math.max(1, opts.steps ?? 1);
|
|
430
512
|
const fromX = this._x;
|
|
431
513
|
const fromY = this._y;
|
|
432
514
|
const dx = x - fromX;
|
|
433
515
|
const dy = y - fromY;
|
|
434
516
|
for (let i = 1; i <= steps; i++) {
|
|
517
|
+
if (stealth) await sleep2(rand(16, 28));
|
|
435
518
|
const t = i / steps;
|
|
436
519
|
this._x = fromX + dx * t;
|
|
437
520
|
this._y = fromY + dy * t;
|
|
@@ -458,7 +541,7 @@ var XBMouseImpl = class {
|
|
|
458
541
|
await this.conn.send(method, params, this.sessionId);
|
|
459
542
|
}
|
|
460
543
|
};
|
|
461
|
-
function
|
|
544
|
+
function sleep2(ms) {
|
|
462
545
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
463
546
|
}
|
|
464
547
|
|
|
@@ -488,17 +571,17 @@ var XBKeyboardImpl = class {
|
|
|
488
571
|
await this.dispatchKeyEvent(downParams);
|
|
489
572
|
if (mapping.text) {
|
|
490
573
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
491
|
-
await
|
|
574
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
492
575
|
}
|
|
493
576
|
await this.dispatchKeyEvent({
|
|
494
577
|
type: "char",
|
|
495
578
|
text: mapping.text
|
|
496
579
|
});
|
|
497
580
|
}
|
|
498
|
-
if (delay > 0) await
|
|
581
|
+
if (delay > 0) await sleep3(delay);
|
|
499
582
|
else {
|
|
500
583
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
501
|
-
await
|
|
584
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
502
585
|
}
|
|
503
586
|
}
|
|
504
587
|
const upParams = {
|
|
@@ -544,46 +627,57 @@ var XBKeyboardImpl = class {
|
|
|
544
627
|
const fixedDelay = opts.delay ?? 0;
|
|
545
628
|
for (const char of text) {
|
|
546
629
|
if (fixedDelay > 0) {
|
|
547
|
-
await
|
|
630
|
+
await sleep3(fixedDelay);
|
|
548
631
|
} else if (stealth) {
|
|
549
|
-
|
|
550
|
-
const cfg = DEFAULT_STEALTH_CONFIG.typingRhythm;
|
|
551
|
-
if (roll < cfg.pauseProb) {
|
|
552
|
-
await sleep2(rand(cfg.pauseRange[0], cfg.pauseRange[1]));
|
|
553
|
-
} else if (roll < cfg.pauseProb + cfg.fastProb) {
|
|
554
|
-
await sleep2(rand(cfg.fastRange[0], cfg.fastRange[1]));
|
|
555
|
-
} else {
|
|
556
|
-
await sleep2(rand(cfg.normalRange[0], cfg.normalRange[1]));
|
|
557
|
-
}
|
|
632
|
+
await sleep3(typingDelay());
|
|
558
633
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
downParams.text = mapping.text;
|
|
567
|
-
downParams.unmodifiedText = mapping.text;
|
|
568
|
-
}
|
|
569
|
-
if (mapping.keyCode) {
|
|
570
|
-
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
571
|
-
}
|
|
572
|
-
await this.dispatchKeyEvent(downParams);
|
|
573
|
-
if (mapping.text) {
|
|
574
|
-
await this.dispatchKeyEvent({
|
|
575
|
-
type: "char",
|
|
576
|
-
text: mapping.text
|
|
577
|
-
});
|
|
634
|
+
if (stealth && char.length === 1 && Math.random() < DEFAULT_STEALTH_CONFIG.typoProbability) {
|
|
635
|
+
const wrong = NEIGHBOR_KEYS[char];
|
|
636
|
+
if (wrong && wrong !== char) {
|
|
637
|
+
await this.dispatchKeySequence(resolveKeyMapping(wrong), stealth);
|
|
638
|
+
await sleep3(rand(120, 420));
|
|
639
|
+
await this.dispatchKeySequence(KEY_MAP2.Backspace, stealth);
|
|
640
|
+
}
|
|
578
641
|
}
|
|
579
|
-
await this.
|
|
580
|
-
type: "keyUp",
|
|
581
|
-
key: mapping.key,
|
|
582
|
-
code: mapping.code,
|
|
583
|
-
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
584
|
-
});
|
|
642
|
+
await this.dispatchKeySequence(resolveKeyMapping(char), stealth);
|
|
585
643
|
}
|
|
586
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* Single-key event sequence: rawKeyDown → (char) → keyUp.
|
|
647
|
+
* Shared by press() and type() — two separate implementations drifted
|
|
648
|
+
* apart once already (S60: type() lost the keyPressDuration that press()
|
|
649
|
+
* had; d50 caught it as 33/33 keys with down→up of 2ms).
|
|
650
|
+
*/
|
|
651
|
+
async dispatchKeySequence(mapping, stealth) {
|
|
652
|
+
const downParams = {
|
|
653
|
+
type: "rawKeyDown",
|
|
654
|
+
key: mapping.key,
|
|
655
|
+
code: mapping.code
|
|
656
|
+
};
|
|
657
|
+
if (mapping.text) {
|
|
658
|
+
downParams.text = mapping.text;
|
|
659
|
+
downParams.unmodifiedText = mapping.text;
|
|
660
|
+
}
|
|
661
|
+
if (mapping.keyCode) {
|
|
662
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
663
|
+
}
|
|
664
|
+
await this.dispatchKeyEvent(downParams);
|
|
665
|
+
if (mapping.text) {
|
|
666
|
+
await this.dispatchKeyEvent({ type: "char", text: mapping.text });
|
|
667
|
+
}
|
|
668
|
+
if (stealth) {
|
|
669
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
670
|
+
}
|
|
671
|
+
const upParams = {
|
|
672
|
+
type: "keyUp",
|
|
673
|
+
key: mapping.key,
|
|
674
|
+
code: mapping.code
|
|
675
|
+
};
|
|
676
|
+
if (mapping.keyCode) {
|
|
677
|
+
upParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
678
|
+
}
|
|
679
|
+
await this.dispatchKeyEvent(upParams);
|
|
680
|
+
}
|
|
587
681
|
async insertText(text) {
|
|
588
682
|
await this.conn.send(
|
|
589
683
|
"Input.insertText",
|
|
@@ -591,6 +685,55 @@ var XBKeyboardImpl = class {
|
|
|
591
685
|
this.sessionId
|
|
592
686
|
);
|
|
593
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* Navigation key press using CDP type 'keyDown' (NOT rawKeyDown).
|
|
690
|
+
* rawKeyDown skips browser default actions — select option navigation,
|
|
691
|
+
* arrow-key scrolling etc. never fire under it (d53: ArrowDown on a
|
|
692
|
+
* focused <select> left the value unchanged). keyDown carries the
|
|
693
|
+
* default action.
|
|
694
|
+
*/
|
|
695
|
+
async pressNav(key) {
|
|
696
|
+
const mapping = resolveKeyMapping(key);
|
|
697
|
+
const downParams = {
|
|
698
|
+
type: "keyDown",
|
|
699
|
+
key: mapping.key,
|
|
700
|
+
code: mapping.code
|
|
701
|
+
};
|
|
702
|
+
if (mapping.keyCode) {
|
|
703
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
704
|
+
downParams.nativeVirtualKeyCode = mapping.keyCode;
|
|
705
|
+
}
|
|
706
|
+
await this.dispatchKeyEvent(downParams);
|
|
707
|
+
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
708
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
709
|
+
}
|
|
710
|
+
await this.dispatchKeyEvent({
|
|
711
|
+
type: "keyUp",
|
|
712
|
+
key: mapping.key,
|
|
713
|
+
code: mapping.code,
|
|
714
|
+
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Shortcut combo press (modifier+key) with explicit per-event modifiers
|
|
719
|
+
* bitmask — plain down(mod)+press(key) inserts the raw character because
|
|
720
|
+
* CDP modifiers are per-event fields, not session state (d56: Meta,v
|
|
721
|
+
* typed a literal 'v'). keyDown type carries the default action so the
|
|
722
|
+
* browser's shortcut dispatcher sees the combo (e.g. native paste).
|
|
723
|
+
*/
|
|
724
|
+
async pressCombo(key, modifier) {
|
|
725
|
+
const MODBIT = { Alt: 1, Control: 2, Meta: 4, Shift: 8 };
|
|
726
|
+
const m = resolveKeyMapping(modifier);
|
|
727
|
+
const k = resolveKeyMapping(key);
|
|
728
|
+
const bits = MODBIT[modifier] ?? 0;
|
|
729
|
+
await this.dispatchKeyEvent({ type: "rawKeyDown", key: m.key, code: m.code, ...m.keyCode ? { windowsVirtualKeyCode: m.keyCode } : {}, modifiers: bits });
|
|
730
|
+
await this.dispatchKeyEvent({ type: "keyDown", key: k.key, code: k.code, ...k.keyCode ? { windowsVirtualKeyCode: k.keyCode } : {}, modifiers: bits });
|
|
731
|
+
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
732
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
733
|
+
}
|
|
734
|
+
await this.dispatchKeyEvent({ type: "keyUp", key: k.key, code: k.code, ...k.keyCode ? { windowsVirtualKeyCode: k.keyCode } : {}, modifiers: bits });
|
|
735
|
+
await this.dispatchKeyEvent({ type: "keyUp", key: m.key, code: m.code, ...m.keyCode ? { windowsVirtualKeyCode: m.keyCode } : {}, modifiers: 0 });
|
|
736
|
+
}
|
|
594
737
|
async dispatchKeyEvent(params) {
|
|
595
738
|
await this.conn.send("Input.dispatchKeyEvent", params, this.sessionId);
|
|
596
739
|
}
|
|
@@ -645,7 +788,44 @@ var KEY_MAP2 = {
|
|
|
645
788
|
F11: { key: "F11", code: "F11", keyCode: 122 },
|
|
646
789
|
F12: { key: "F12", code: "F12", keyCode: 123 }
|
|
647
790
|
};
|
|
648
|
-
|
|
791
|
+
var NEIGHBOR_KEYS = {
|
|
792
|
+
a: "s",
|
|
793
|
+
b: "v",
|
|
794
|
+
c: "x",
|
|
795
|
+
d: "f",
|
|
796
|
+
e: "r",
|
|
797
|
+
f: "g",
|
|
798
|
+
g: "h",
|
|
799
|
+
h: "j",
|
|
800
|
+
i: "o",
|
|
801
|
+
j: "k",
|
|
802
|
+
k: "l",
|
|
803
|
+
l: "k",
|
|
804
|
+
m: "n",
|
|
805
|
+
n: "m",
|
|
806
|
+
o: "p",
|
|
807
|
+
p: "o",
|
|
808
|
+
q: "w",
|
|
809
|
+
r: "t",
|
|
810
|
+
s: "d",
|
|
811
|
+
t: "y",
|
|
812
|
+
u: "i",
|
|
813
|
+
v: "b",
|
|
814
|
+
w: "e",
|
|
815
|
+
x: "c",
|
|
816
|
+
y: "u",
|
|
817
|
+
z: "x",
|
|
818
|
+
"1": "2",
|
|
819
|
+
"2": "3",
|
|
820
|
+
"3": "4",
|
|
821
|
+
"4": "5",
|
|
822
|
+
"5": "6",
|
|
823
|
+
"6": "7",
|
|
824
|
+
"7": "8",
|
|
825
|
+
"8": "9",
|
|
826
|
+
"9": "0"
|
|
827
|
+
};
|
|
828
|
+
function sleep3(ms) {
|
|
649
829
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
650
830
|
}
|
|
651
831
|
|
|
@@ -937,6 +1117,18 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
937
1117
|
await waitForActionable(this.page, this.selector, opts);
|
|
938
1118
|
await scrollIntoView(this.page, this.selector);
|
|
939
1119
|
await this.click({ ...opts });
|
|
1120
|
+
if (process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type") {
|
|
1121
|
+
try {
|
|
1122
|
+
const { pasteViaClipboard, syntheticPaste } = await import("./clipboard-BOL2GP2E.js");
|
|
1123
|
+
await pasteViaClipboard(this.page, value);
|
|
1124
|
+
const got = await this.page.evaluate(
|
|
1125
|
+
`(function(){ const el = ${this._q(this.selector)}; return el ? (el.value || '') : ''; })()`
|
|
1126
|
+
);
|
|
1127
|
+
if (got === value) return;
|
|
1128
|
+
if (await syntheticPaste(this.page, this._q(this.selector), value)) return;
|
|
1129
|
+
} catch {
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
940
1132
|
await this.page.keyboard.type(value, { stealth: true });
|
|
941
1133
|
return;
|
|
942
1134
|
await this.page.evaluate(`
|
|
@@ -962,23 +1154,13 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
962
1154
|
async press(key, opts = {}) {
|
|
963
1155
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
964
1156
|
await scrollIntoView(this.page, this.selector);
|
|
965
|
-
await this.
|
|
966
|
-
(function() {
|
|
967
|
-
const el = ${this._q(this.selector)};
|
|
968
|
-
if (el) el.focus();
|
|
969
|
-
})()
|
|
970
|
-
`);
|
|
1157
|
+
await this.click({ timeout: opts.timeout });
|
|
971
1158
|
await this.page.keyboard.press(key);
|
|
972
1159
|
}
|
|
973
1160
|
async pressSequentially(text, opts = {}) {
|
|
974
1161
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
975
1162
|
await scrollIntoView(this.page, this.selector);
|
|
976
|
-
await this.
|
|
977
|
-
(function() {
|
|
978
|
-
const el = ${this._q(this.selector)};
|
|
979
|
-
if (el) el.focus();
|
|
980
|
-
})()
|
|
981
|
-
`);
|
|
1163
|
+
await this.click({ timeout: opts.timeout });
|
|
982
1164
|
await this.page.keyboard.type(text, { delay: opts.delay });
|
|
983
1165
|
}
|
|
984
1166
|
async hover(opts = {}) {
|
|
@@ -1033,11 +1215,36 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
1033
1215
|
async selectOption(value) {
|
|
1034
1216
|
await waitForActionable(this.page, this.selector);
|
|
1035
1217
|
const values = Array.isArray(value) ? value : [value];
|
|
1036
|
-
const
|
|
1218
|
+
const info = await this.page.evaluate(`
|
|
1037
1219
|
(function() {
|
|
1038
1220
|
const el = ${this._q(this.selector)};
|
|
1039
1221
|
if (!el || el.tagName !== 'SELECT') throw new Error('Not a select element');
|
|
1040
|
-
|
|
1222
|
+
const values = ${JSON.stringify(values)};
|
|
1223
|
+
let target = -1, targetValue = '';
|
|
1224
|
+
outer:
|
|
1225
|
+
for (let i = 0; i < el.options.length; i++) {
|
|
1226
|
+
const opt = el.options[i];
|
|
1227
|
+
for (const v of values) {
|
|
1228
|
+
const hit = typeof v === 'object'
|
|
1229
|
+
? (v.label !== undefined ? opt.label === v.label
|
|
1230
|
+
: v.value !== undefined ? opt.value === v.value
|
|
1231
|
+
: opt.index === v.index)
|
|
1232
|
+
: (opt.value === v || opt.label === v);
|
|
1233
|
+
if (hit) { target = i; targetValue = opt.value; break outer; }
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return { cur: el.selectedIndex, target: target, targetValue: targetValue, multiple: el.multiple };
|
|
1237
|
+
})()
|
|
1238
|
+
`);
|
|
1239
|
+
if (info.target < 0) {
|
|
1240
|
+
throw new Error(`Option not found: ${JSON.stringify(values)}`);
|
|
1241
|
+
}
|
|
1242
|
+
if (!info.multiple) {
|
|
1243
|
+
await this.click({ timeout: 5e3 });
|
|
1244
|
+
}
|
|
1245
|
+
const selected = await this.page.evaluate(`
|
|
1246
|
+
(function() {
|
|
1247
|
+
const el = ${this._q(this.selector)};
|
|
1041
1248
|
const values = ${JSON.stringify(values)};
|
|
1042
1249
|
const selectedValues = [];
|
|
1043
1250
|
|
|
@@ -5188,16 +5395,14 @@ process.on("exit", () => {
|
|
|
5188
5395
|
async function getCDPTargets2(cdpEndpoint) {
|
|
5189
5396
|
try {
|
|
5190
5397
|
const ep = String(cdpEndpoint);
|
|
5191
|
-
let
|
|
5192
|
-
let port = "9222";
|
|
5398
|
+
let url = "http://localhost:9222/json/list";
|
|
5193
5399
|
if (ep.startsWith("http://") || ep.startsWith("https://")) {
|
|
5194
5400
|
const u = new URL(ep);
|
|
5195
|
-
|
|
5196
|
-
|
|
5401
|
+
u.pathname = (u.pathname.replace(/\/+$/, "") || "") + "/json/list";
|
|
5402
|
+
url = u.toString();
|
|
5197
5403
|
} else if (/^\d+$/.test(ep)) {
|
|
5198
|
-
|
|
5404
|
+
url = `http://localhost:${ep}/json/list`;
|
|
5199
5405
|
}
|
|
5200
|
-
const url = `http://${host}:${port}/json/list`;
|
|
5201
5406
|
const resp = await fetch(url);
|
|
5202
5407
|
return await resp.json();
|
|
5203
5408
|
} catch {
|
|
@@ -5682,6 +5887,10 @@ async function createSession(name, url, options) {
|
|
|
5682
5887
|
}
|
|
5683
5888
|
}
|
|
5684
5889
|
page = targetPage;
|
|
5890
|
+
if (isCDP) {
|
|
5891
|
+
await Promise.resolve(targetPage.bringToFront?.()).catch(() => {
|
|
5892
|
+
});
|
|
5893
|
+
}
|
|
5685
5894
|
} else {
|
|
5686
5895
|
context = await b.newContext({ viewport: { width: 1920, height: 1080 } });
|
|
5687
5896
|
page = await context.newPage();
|
|
@@ -5830,6 +6039,9 @@ async function ensureProcessCanExit() {
|
|
|
5830
6039
|
}
|
|
5831
6040
|
|
|
5832
6041
|
export {
|
|
6042
|
+
rand,
|
|
6043
|
+
sleep,
|
|
6044
|
+
wheelDelta,
|
|
5833
6045
|
createRuleEngine,
|
|
5834
6046
|
touchSession,
|
|
5835
6047
|
findTargetPage,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
launch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-A5HJ6IRE.js";
|
|
4
4
|
import {
|
|
5
5
|
errMsg
|
|
6
6
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -161,16 +161,14 @@ process.on("exit", () => {
|
|
|
161
161
|
async function getCDPTargets(cdpEndpoint) {
|
|
162
162
|
try {
|
|
163
163
|
const ep = String(cdpEndpoint);
|
|
164
|
-
let
|
|
165
|
-
let port = "9222";
|
|
164
|
+
let url = "http://localhost:9222/json/list";
|
|
166
165
|
if (ep.startsWith("http://") || ep.startsWith("https://")) {
|
|
167
166
|
const u = new URL(ep);
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
u.pathname = (u.pathname.replace(/\/+$/, "") || "") + "/json/list";
|
|
168
|
+
url = u.toString();
|
|
170
169
|
} else if (/^\d+$/.test(ep)) {
|
|
171
|
-
|
|
170
|
+
url = `http://localhost:${ep}/json/list`;
|
|
172
171
|
}
|
|
173
|
-
const url = `http://${host}:${port}/json/list`;
|
|
174
172
|
const resp = await fetch(url);
|
|
175
173
|
return await resp.json();
|
|
176
174
|
} catch {
|
|
@@ -655,6 +653,10 @@ async function createSession(name, url, options) {
|
|
|
655
653
|
}
|
|
656
654
|
}
|
|
657
655
|
page = targetPage;
|
|
656
|
+
if (isCDP) {
|
|
657
|
+
await Promise.resolve(targetPage.bringToFront?.()).catch(() => {
|
|
658
|
+
});
|
|
659
|
+
}
|
|
658
660
|
} else {
|
|
659
661
|
context = await b.newContext({ viewport: { width: 1920, height: 1080 } });
|
|
660
662
|
page = await context.newPage();
|