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