@xbrowser/cli 1.12.0 → 1.14.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-XPDMY2NX.js → browser-5A6AL62M.js} +2 -2
- package/dist/{browser-ILXTEWH4.js → browser-DI6UQN6Q.js} +1 -1
- package/dist/{browser-3HQKDZQ6.js → browser-KJWBHGHX.js} +2 -2
- package/dist/{cdp-driver-ESH3PDE6.js → cdp-driver-4SFS6GNY.js} +1 -1
- package/dist/{cdp-driver-YIKR4BUX.js → cdp-driver-AHKUT57K.js} +1 -1
- package/dist/{cdp-driver-N2E2ZNSC.js → cdp-driver-QXTXJZH7.js} +53 -7
- package/dist/{chunk-R6IJ6PIV.js → chunk-B753M33E.js} +1 -1
- package/dist/{chunk-OVW2UEWN.js → chunk-BTFKJD7Z.js} +53 -7
- package/dist/{chunk-DKM2JOZL.js → chunk-PYQGDBAF.js} +53 -7
- package/dist/{chunk-Z6CUR3VG.js → chunk-UPAWITVM.js} +53 -7
- package/dist/{chunk-KJTABK3Z.js → chunk-ZD4OSYOX.js} +1 -1
- package/dist/cli.js +4 -4
- package/dist/daemon-main.js +4 -4
- package/dist/index.js +5 -5
- package/dist/{session-replayer-K4A6OI4M.js → session-replayer-I3PJFO3H.js} +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-ZD4OSYOX.js";
|
|
24
|
+
import "./chunk-UPAWITVM.js";
|
|
25
25
|
import "./chunk-3FWLW7FS.js";
|
|
26
26
|
import "./chunk-TNEN6VQ2.js";
|
|
27
27
|
import "./chunk-GDKLH7ZY.js";
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-B753M33E.js";
|
|
24
|
+
import "./chunk-PYQGDBAF.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-A6LPGFAL.js";
|
|
@@ -122,10 +122,10 @@ function buildStealthInitScript() {
|
|
|
122
122
|
" var op=arguments[2];",
|
|
123
123
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
124
124
|
" var w=function(e){",
|
|
125
|
-
" if(!e
|
|
125
|
+
" if(!e)return f.call(this,e);",
|
|
126
126
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
127
127
|
' if(p==="sourceCapabilities")return fc;',
|
|
128
|
-
' if(p==="isTrusted")return k
|
|
128
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
129
129
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
130
130
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
131
131
|
" return k[p]+_f;",
|
|
@@ -144,7 +144,9 @@ function buildStealthInitScript() {
|
|
|
144
144
|
' Object.defineProperty(Screen.prototype,"height",{get:_gh,configurable:true});',
|
|
145
145
|
' Object.defineProperty(Screen.prototype,"availWidth",{get:_gw,configurable:true});',
|
|
146
146
|
' Object.defineProperty(Screen.prototype,"availHeight",{get:_gah,configurable:true});',
|
|
147
|
-
|
|
147
|
+
// hasFocus 与 visibilityState 联动(d29):恒 true 在 hidden 标签下暴露
|
|
148
|
+
// —— 真实浏览器失焦/后台时 hasFocus=false
|
|
149
|
+
' document.hasFocus=function(){return document.visibilityState==="visible";};',
|
|
148
150
|
// 4. Canvas/WebGL fingerprint: per-session stable noise (d20).
|
|
149
151
|
// Headless software raster differs subtly from Chrome GPU raster —
|
|
150
152
|
// toDataURL hashes fingerprint the rasterizer. Inject a stable
|
|
@@ -220,9 +222,32 @@ function buildStealthInitScript() {
|
|
|
220
222
|
" var _sv=speechSynthesis.getVoices.bind(speechSynthesis);",
|
|
221
223
|
" speechSynthesis.getVoices=function(){",
|
|
222
224
|
" var real=_sv();",
|
|
223
|
-
" if(real&&real.length)return real;",
|
|
225
|
+
" if(real&&real.length>50)return real;",
|
|
226
|
+
// 注意阈值从 0 改 50:iframe 原生约 10 个 —— 原阈值下 iframe 返回原生 10 个
|
|
227
|
+
// 而不是伪装 180(d33 暴露)。>50 = 主文档伪装列表已生效,其余返回伪装。
|
|
224
228
|
' return _fakeVoices.map(function(n,i){return {name:n,lang:i<2?"en-US":i<6?"en-GB":"zh-TW",localService:true,default:i===0,voiceURI:n};});',
|
|
225
229
|
" };",
|
|
230
|
+
// d33 修复:iframe 的 speechSynthesis 是独立实例(主文档 hook 不可达)——
|
|
231
|
+
// 轮询同源 iframe,对其 contentWindow.speechSynthesis 同样 patch
|
|
232
|
+
" try{",
|
|
233
|
+
" var _piv=function(){",
|
|
234
|
+
' var frs=document.querySelectorAll("iframe");',
|
|
235
|
+
" for(var i=0;i<frs.length;i++){try{",
|
|
236
|
+
" var cw=frs[i].contentWindow;",
|
|
237
|
+
" if(!cw||!cw.speechSynthesis||cw.speechSynthesis.__xbP)continue;",
|
|
238
|
+
" cw.speechSynthesis.__xbP=true;",
|
|
239
|
+
" var _sv2=cw.speechSynthesis.getVoices.bind(cw.speechSynthesis);",
|
|
240
|
+
" cw.speechSynthesis.getVoices=function(){var r=_sv2();if(r&&r.length>50)return r;",
|
|
241
|
+
' return _fakeVoices.map(function(n,j){return{name:n,lang:j<2?"en-US":j<6?"en-GB":"zh-TW",localService:true,default:j===0,voiceURI:n};});};',
|
|
242
|
+
" }catch(e2){}}",
|
|
243
|
+
" };",
|
|
244
|
+
" setInterval(_piv,2000);",
|
|
245
|
+
" var _mo=new MutationObserver(function(muts){",
|
|
246
|
+
" for(var k=0;k<muts.length;k++){var ns=muts[k].addedNodes;",
|
|
247
|
+
' for(var q=0;q<ns.length;q++){if(ns[q].tagName==="IFRAME")setTimeout(_piv,20);}}',
|
|
248
|
+
" });",
|
|
249
|
+
" _mo.observe(document.documentElement,{childList:true,subtree:true});",
|
|
250
|
+
" }catch(e){}",
|
|
226
251
|
" }catch(e){}",
|
|
227
252
|
" try{",
|
|
228
253
|
" var _gb=navigator.getBattery.bind(navigator);",
|
|
@@ -244,6 +269,14 @@ function buildStealthInitScript() {
|
|
|
244
269
|
" return offer;});",
|
|
245
270
|
" };",
|
|
246
271
|
" }catch(e){}",
|
|
272
|
+
// 3d-2. performance.now precision clamp (d34): full-precision timestamps
|
|
273
|
+
// (11 decimals) differ from site-isolation-clamped real Chrome (~100μs).
|
|
274
|
+
" try{",
|
|
275
|
+
" var _pn=performance.now.bind(performance);",
|
|
276
|
+
" performance.now=function(){return Math.round(_pn()*10)/10;};",
|
|
277
|
+
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
278
|
+
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
279
|
+
" }catch(e){}",
|
|
247
280
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
248
281
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
249
282
|
" try{",
|
|
@@ -282,7 +315,7 @@ function buildStealthInitScript() {
|
|
|
282
315
|
" };",
|
|
283
316
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
284
317
|
" var _ba=function(k,p){",
|
|
285
|
-
' if(p==="isTrusted")return k
|
|
318
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
286
319
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
287
320
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
288
321
|
" }",
|
|
@@ -502,9 +535,22 @@ var XBKeyboardImpl = class {
|
|
|
502
535
|
await this.dispatchKeyEvent(params);
|
|
503
536
|
}
|
|
504
537
|
async type(text, opts = {}) {
|
|
505
|
-
const
|
|
538
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
539
|
+
const fixedDelay = opts.delay ?? 0;
|
|
506
540
|
for (const char of text) {
|
|
507
|
-
if (
|
|
541
|
+
if (fixedDelay > 0) {
|
|
542
|
+
await sleep2(fixedDelay);
|
|
543
|
+
} else if (stealth) {
|
|
544
|
+
const roll = Math.random();
|
|
545
|
+
const cfg = DEFAULT_STEALTH_CONFIG.typingRhythm;
|
|
546
|
+
if (roll < cfg.pauseProb) {
|
|
547
|
+
await sleep2(rand(cfg.pauseRange[0], cfg.pauseRange[1]));
|
|
548
|
+
} else if (roll < cfg.pauseProb + cfg.fastProb) {
|
|
549
|
+
await sleep2(rand(cfg.fastRange[0], cfg.fastRange[1]));
|
|
550
|
+
} else {
|
|
551
|
+
await sleep2(rand(cfg.normalRange[0], cfg.normalRange[1]));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
508
554
|
const mapping = resolveKeyMapping(char);
|
|
509
555
|
const downParams = {
|
|
510
556
|
type: "rawKeyDown",
|
|
@@ -127,10 +127,10 @@ function buildStealthInitScript() {
|
|
|
127
127
|
" var op=arguments[2];",
|
|
128
128
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
129
129
|
" var w=function(e){",
|
|
130
|
-
" if(!e
|
|
130
|
+
" if(!e)return f.call(this,e);",
|
|
131
131
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
132
132
|
' if(p==="sourceCapabilities")return fc;',
|
|
133
|
-
' if(p==="isTrusted")return k
|
|
133
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
134
134
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
135
135
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
136
136
|
" return k[p]+_f;",
|
|
@@ -149,7 +149,9 @@ function buildStealthInitScript() {
|
|
|
149
149
|
' Object.defineProperty(Screen.prototype,"height",{get:_gh,configurable:true});',
|
|
150
150
|
' Object.defineProperty(Screen.prototype,"availWidth",{get:_gw,configurable:true});',
|
|
151
151
|
' Object.defineProperty(Screen.prototype,"availHeight",{get:_gah,configurable:true});',
|
|
152
|
-
|
|
152
|
+
// hasFocus 与 visibilityState 联动(d29):恒 true 在 hidden 标签下暴露
|
|
153
|
+
// —— 真实浏览器失焦/后台时 hasFocus=false
|
|
154
|
+
' document.hasFocus=function(){return document.visibilityState==="visible";};',
|
|
153
155
|
// 4. Canvas/WebGL fingerprint: per-session stable noise (d20).
|
|
154
156
|
// Headless software raster differs subtly from Chrome GPU raster —
|
|
155
157
|
// toDataURL hashes fingerprint the rasterizer. Inject a stable
|
|
@@ -225,9 +227,32 @@ function buildStealthInitScript() {
|
|
|
225
227
|
" var _sv=speechSynthesis.getVoices.bind(speechSynthesis);",
|
|
226
228
|
" speechSynthesis.getVoices=function(){",
|
|
227
229
|
" var real=_sv();",
|
|
228
|
-
" if(real&&real.length)return real;",
|
|
230
|
+
" if(real&&real.length>50)return real;",
|
|
231
|
+
// 注意阈值从 0 改 50:iframe 原生约 10 个 —— 原阈值下 iframe 返回原生 10 个
|
|
232
|
+
// 而不是伪装 180(d33 暴露)。>50 = 主文档伪装列表已生效,其余返回伪装。
|
|
229
233
|
' return _fakeVoices.map(function(n,i){return {name:n,lang:i<2?"en-US":i<6?"en-GB":"zh-TW",localService:true,default:i===0,voiceURI:n};});',
|
|
230
234
|
" };",
|
|
235
|
+
// d33 修复:iframe 的 speechSynthesis 是独立实例(主文档 hook 不可达)——
|
|
236
|
+
// 轮询同源 iframe,对其 contentWindow.speechSynthesis 同样 patch
|
|
237
|
+
" try{",
|
|
238
|
+
" var _piv=function(){",
|
|
239
|
+
' var frs=document.querySelectorAll("iframe");',
|
|
240
|
+
" for(var i=0;i<frs.length;i++){try{",
|
|
241
|
+
" var cw=frs[i].contentWindow;",
|
|
242
|
+
" if(!cw||!cw.speechSynthesis||cw.speechSynthesis.__xbP)continue;",
|
|
243
|
+
" cw.speechSynthesis.__xbP=true;",
|
|
244
|
+
" var _sv2=cw.speechSynthesis.getVoices.bind(cw.speechSynthesis);",
|
|
245
|
+
" cw.speechSynthesis.getVoices=function(){var r=_sv2();if(r&&r.length>50)return r;",
|
|
246
|
+
' return _fakeVoices.map(function(n,j){return{name:n,lang:j<2?"en-US":j<6?"en-GB":"zh-TW",localService:true,default:j===0,voiceURI:n};});};',
|
|
247
|
+
" }catch(e2){}}",
|
|
248
|
+
" };",
|
|
249
|
+
" setInterval(_piv,2000);",
|
|
250
|
+
" var _mo=new MutationObserver(function(muts){",
|
|
251
|
+
" for(var k=0;k<muts.length;k++){var ns=muts[k].addedNodes;",
|
|
252
|
+
' for(var q=0;q<ns.length;q++){if(ns[q].tagName==="IFRAME")setTimeout(_piv,20);}}',
|
|
253
|
+
" });",
|
|
254
|
+
" _mo.observe(document.documentElement,{childList:true,subtree:true});",
|
|
255
|
+
" }catch(e){}",
|
|
231
256
|
" }catch(e){}",
|
|
232
257
|
" try{",
|
|
233
258
|
" var _gb=navigator.getBattery.bind(navigator);",
|
|
@@ -249,6 +274,14 @@ function buildStealthInitScript() {
|
|
|
249
274
|
" return offer;});",
|
|
250
275
|
" };",
|
|
251
276
|
" }catch(e){}",
|
|
277
|
+
// 3d-2. performance.now precision clamp (d34): full-precision timestamps
|
|
278
|
+
// (11 decimals) differ from site-isolation-clamped real Chrome (~100μs).
|
|
279
|
+
" try{",
|
|
280
|
+
" var _pn=performance.now.bind(performance);",
|
|
281
|
+
" performance.now=function(){return Math.round(_pn()*10)/10;};",
|
|
282
|
+
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
283
|
+
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
284
|
+
" }catch(e){}",
|
|
252
285
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
253
286
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
254
287
|
" try{",
|
|
@@ -287,7 +320,7 @@ function buildStealthInitScript() {
|
|
|
287
320
|
" };",
|
|
288
321
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
289
322
|
" var _ba=function(k,p){",
|
|
290
|
-
' if(p==="isTrusted")return k
|
|
323
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
291
324
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
292
325
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
293
326
|
" }",
|
|
@@ -507,9 +540,22 @@ var XBKeyboardImpl = class {
|
|
|
507
540
|
await this.dispatchKeyEvent(params);
|
|
508
541
|
}
|
|
509
542
|
async type(text, opts = {}) {
|
|
510
|
-
const
|
|
543
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
544
|
+
const fixedDelay = opts.delay ?? 0;
|
|
511
545
|
for (const char of text) {
|
|
512
|
-
if (
|
|
546
|
+
if (fixedDelay > 0) {
|
|
547
|
+
await sleep2(fixedDelay);
|
|
548
|
+
} else if (stealth) {
|
|
549
|
+
const roll = Math.random();
|
|
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
|
+
}
|
|
558
|
+
}
|
|
513
559
|
const mapping = resolveKeyMapping(char);
|
|
514
560
|
const downParams = {
|
|
515
561
|
type: "rawKeyDown",
|
|
@@ -124,10 +124,10 @@ function buildStealthInitScript() {
|
|
|
124
124
|
" var op=arguments[2];",
|
|
125
125
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
126
126
|
" var w=function(e){",
|
|
127
|
-
" if(!e
|
|
127
|
+
" if(!e)return f.call(this,e);",
|
|
128
128
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
129
129
|
' if(p==="sourceCapabilities")return fc;',
|
|
130
|
-
' if(p==="isTrusted")return k
|
|
130
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
131
131
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
132
132
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
133
133
|
" return k[p]+_f;",
|
|
@@ -146,7 +146,9 @@ function buildStealthInitScript() {
|
|
|
146
146
|
' Object.defineProperty(Screen.prototype,"height",{get:_gh,configurable:true});',
|
|
147
147
|
' Object.defineProperty(Screen.prototype,"availWidth",{get:_gw,configurable:true});',
|
|
148
148
|
' Object.defineProperty(Screen.prototype,"availHeight",{get:_gah,configurable:true});',
|
|
149
|
-
|
|
149
|
+
// hasFocus 与 visibilityState 联动(d29):恒 true 在 hidden 标签下暴露
|
|
150
|
+
// —— 真实浏览器失焦/后台时 hasFocus=false
|
|
151
|
+
' document.hasFocus=function(){return document.visibilityState==="visible";};',
|
|
150
152
|
// 4. Canvas/WebGL fingerprint: per-session stable noise (d20).
|
|
151
153
|
// Headless software raster differs subtly from Chrome GPU raster —
|
|
152
154
|
// toDataURL hashes fingerprint the rasterizer. Inject a stable
|
|
@@ -222,9 +224,32 @@ function buildStealthInitScript() {
|
|
|
222
224
|
" var _sv=speechSynthesis.getVoices.bind(speechSynthesis);",
|
|
223
225
|
" speechSynthesis.getVoices=function(){",
|
|
224
226
|
" var real=_sv();",
|
|
225
|
-
" if(real&&real.length)return real;",
|
|
227
|
+
" if(real&&real.length>50)return real;",
|
|
228
|
+
// 注意阈值从 0 改 50:iframe 原生约 10 个 —— 原阈值下 iframe 返回原生 10 个
|
|
229
|
+
// 而不是伪装 180(d33 暴露)。>50 = 主文档伪装列表已生效,其余返回伪装。
|
|
226
230
|
' return _fakeVoices.map(function(n,i){return {name:n,lang:i<2?"en-US":i<6?"en-GB":"zh-TW",localService:true,default:i===0,voiceURI:n};});',
|
|
227
231
|
" };",
|
|
232
|
+
// d33 修复:iframe 的 speechSynthesis 是独立实例(主文档 hook 不可达)——
|
|
233
|
+
// 轮询同源 iframe,对其 contentWindow.speechSynthesis 同样 patch
|
|
234
|
+
" try{",
|
|
235
|
+
" var _piv=function(){",
|
|
236
|
+
' var frs=document.querySelectorAll("iframe");',
|
|
237
|
+
" for(var i=0;i<frs.length;i++){try{",
|
|
238
|
+
" var cw=frs[i].contentWindow;",
|
|
239
|
+
" if(!cw||!cw.speechSynthesis||cw.speechSynthesis.__xbP)continue;",
|
|
240
|
+
" cw.speechSynthesis.__xbP=true;",
|
|
241
|
+
" var _sv2=cw.speechSynthesis.getVoices.bind(cw.speechSynthesis);",
|
|
242
|
+
" cw.speechSynthesis.getVoices=function(){var r=_sv2();if(r&&r.length>50)return r;",
|
|
243
|
+
' return _fakeVoices.map(function(n,j){return{name:n,lang:j<2?"en-US":j<6?"en-GB":"zh-TW",localService:true,default:j===0,voiceURI:n};});};',
|
|
244
|
+
" }catch(e2){}}",
|
|
245
|
+
" };",
|
|
246
|
+
" setInterval(_piv,2000);",
|
|
247
|
+
" var _mo=new MutationObserver(function(muts){",
|
|
248
|
+
" for(var k=0;k<muts.length;k++){var ns=muts[k].addedNodes;",
|
|
249
|
+
' for(var q=0;q<ns.length;q++){if(ns[q].tagName==="IFRAME")setTimeout(_piv,20);}}',
|
|
250
|
+
" });",
|
|
251
|
+
" _mo.observe(document.documentElement,{childList:true,subtree:true});",
|
|
252
|
+
" }catch(e){}",
|
|
228
253
|
" }catch(e){}",
|
|
229
254
|
" try{",
|
|
230
255
|
" var _gb=navigator.getBattery.bind(navigator);",
|
|
@@ -246,6 +271,14 @@ function buildStealthInitScript() {
|
|
|
246
271
|
" return offer;});",
|
|
247
272
|
" };",
|
|
248
273
|
" }catch(e){}",
|
|
274
|
+
// 3d-2. performance.now precision clamp (d34): full-precision timestamps
|
|
275
|
+
// (11 decimals) differ from site-isolation-clamped real Chrome (~100μs).
|
|
276
|
+
" try{",
|
|
277
|
+
" var _pn=performance.now.bind(performance);",
|
|
278
|
+
" performance.now=function(){return Math.round(_pn()*10)/10;};",
|
|
279
|
+
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
280
|
+
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
281
|
+
" }catch(e){}",
|
|
249
282
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
250
283
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
251
284
|
" try{",
|
|
@@ -284,7 +317,7 @@ function buildStealthInitScript() {
|
|
|
284
317
|
" };",
|
|
285
318
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
286
319
|
" var _ba=function(k,p){",
|
|
287
|
-
' if(p==="isTrusted")return k
|
|
320
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
288
321
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
289
322
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
290
323
|
" }",
|
|
@@ -504,9 +537,22 @@ var XBKeyboardImpl = class {
|
|
|
504
537
|
await this.dispatchKeyEvent(params);
|
|
505
538
|
}
|
|
506
539
|
async type(text, opts = {}) {
|
|
507
|
-
const
|
|
540
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
541
|
+
const fixedDelay = opts.delay ?? 0;
|
|
508
542
|
for (const char of text) {
|
|
509
|
-
if (
|
|
543
|
+
if (fixedDelay > 0) {
|
|
544
|
+
await sleep2(fixedDelay);
|
|
545
|
+
} 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
|
+
}
|
|
510
556
|
const mapping = resolveKeyMapping(char);
|
|
511
557
|
const downParams = {
|
|
512
558
|
type: "rawKeyDown",
|
|
@@ -125,10 +125,10 @@ function buildStealthInitScript() {
|
|
|
125
125
|
" var op=arguments[2];",
|
|
126
126
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
127
127
|
" var w=function(e){",
|
|
128
|
-
" if(!e
|
|
128
|
+
" if(!e)return f.call(this,e);",
|
|
129
129
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
130
130
|
' if(p==="sourceCapabilities")return fc;',
|
|
131
|
-
' if(p==="isTrusted")return k
|
|
131
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
132
132
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
133
133
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
134
134
|
" return k[p]+_f;",
|
|
@@ -147,7 +147,9 @@ function buildStealthInitScript() {
|
|
|
147
147
|
' Object.defineProperty(Screen.prototype,"height",{get:_gh,configurable:true});',
|
|
148
148
|
' Object.defineProperty(Screen.prototype,"availWidth",{get:_gw,configurable:true});',
|
|
149
149
|
' Object.defineProperty(Screen.prototype,"availHeight",{get:_gah,configurable:true});',
|
|
150
|
-
|
|
150
|
+
// hasFocus 与 visibilityState 联动(d29):恒 true 在 hidden 标签下暴露
|
|
151
|
+
// —— 真实浏览器失焦/后台时 hasFocus=false
|
|
152
|
+
' document.hasFocus=function(){return document.visibilityState==="visible";};',
|
|
151
153
|
// 4. Canvas/WebGL fingerprint: per-session stable noise (d20).
|
|
152
154
|
// Headless software raster differs subtly from Chrome GPU raster —
|
|
153
155
|
// toDataURL hashes fingerprint the rasterizer. Inject a stable
|
|
@@ -223,9 +225,32 @@ function buildStealthInitScript() {
|
|
|
223
225
|
" var _sv=speechSynthesis.getVoices.bind(speechSynthesis);",
|
|
224
226
|
" speechSynthesis.getVoices=function(){",
|
|
225
227
|
" var real=_sv();",
|
|
226
|
-
" if(real&&real.length)return real;",
|
|
228
|
+
" if(real&&real.length>50)return real;",
|
|
229
|
+
// 注意阈值从 0 改 50:iframe 原生约 10 个 —— 原阈值下 iframe 返回原生 10 个
|
|
230
|
+
// 而不是伪装 180(d33 暴露)。>50 = 主文档伪装列表已生效,其余返回伪装。
|
|
227
231
|
' return _fakeVoices.map(function(n,i){return {name:n,lang:i<2?"en-US":i<6?"en-GB":"zh-TW",localService:true,default:i===0,voiceURI:n};});',
|
|
228
232
|
" };",
|
|
233
|
+
// d33 修复:iframe 的 speechSynthesis 是独立实例(主文档 hook 不可达)——
|
|
234
|
+
// 轮询同源 iframe,对其 contentWindow.speechSynthesis 同样 patch
|
|
235
|
+
" try{",
|
|
236
|
+
" var _piv=function(){",
|
|
237
|
+
' var frs=document.querySelectorAll("iframe");',
|
|
238
|
+
" for(var i=0;i<frs.length;i++){try{",
|
|
239
|
+
" var cw=frs[i].contentWindow;",
|
|
240
|
+
" if(!cw||!cw.speechSynthesis||cw.speechSynthesis.__xbP)continue;",
|
|
241
|
+
" cw.speechSynthesis.__xbP=true;",
|
|
242
|
+
" var _sv2=cw.speechSynthesis.getVoices.bind(cw.speechSynthesis);",
|
|
243
|
+
" cw.speechSynthesis.getVoices=function(){var r=_sv2();if(r&&r.length>50)return r;",
|
|
244
|
+
' return _fakeVoices.map(function(n,j){return{name:n,lang:j<2?"en-US":j<6?"en-GB":"zh-TW",localService:true,default:j===0,voiceURI:n};});};',
|
|
245
|
+
" }catch(e2){}}",
|
|
246
|
+
" };",
|
|
247
|
+
" setInterval(_piv,2000);",
|
|
248
|
+
" var _mo=new MutationObserver(function(muts){",
|
|
249
|
+
" for(var k=0;k<muts.length;k++){var ns=muts[k].addedNodes;",
|
|
250
|
+
' for(var q=0;q<ns.length;q++){if(ns[q].tagName==="IFRAME")setTimeout(_piv,20);}}',
|
|
251
|
+
" });",
|
|
252
|
+
" _mo.observe(document.documentElement,{childList:true,subtree:true});",
|
|
253
|
+
" }catch(e){}",
|
|
229
254
|
" }catch(e){}",
|
|
230
255
|
" try{",
|
|
231
256
|
" var _gb=navigator.getBattery.bind(navigator);",
|
|
@@ -247,6 +272,14 @@ function buildStealthInitScript() {
|
|
|
247
272
|
" return offer;});",
|
|
248
273
|
" };",
|
|
249
274
|
" }catch(e){}",
|
|
275
|
+
// 3d-2. performance.now precision clamp (d34): full-precision timestamps
|
|
276
|
+
// (11 decimals) differ from site-isolation-clamped real Chrome (~100μs).
|
|
277
|
+
" try{",
|
|
278
|
+
" var _pn=performance.now.bind(performance);",
|
|
279
|
+
" performance.now=function(){return Math.round(_pn()*10)/10;};",
|
|
280
|
+
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
281
|
+
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
282
|
+
" }catch(e){}",
|
|
250
283
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
251
284
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
252
285
|
" try{",
|
|
@@ -285,7 +318,7 @@ function buildStealthInitScript() {
|
|
|
285
318
|
" };",
|
|
286
319
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
287
320
|
" var _ba=function(k,p){",
|
|
288
|
-
' if(p==="isTrusted")return k
|
|
321
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
289
322
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
290
323
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
291
324
|
" }",
|
|
@@ -505,9 +538,22 @@ var XBKeyboardImpl = class {
|
|
|
505
538
|
await this.dispatchKeyEvent(params);
|
|
506
539
|
}
|
|
507
540
|
async type(text, opts = {}) {
|
|
508
|
-
const
|
|
541
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
542
|
+
const fixedDelay = opts.delay ?? 0;
|
|
509
543
|
for (const char of text) {
|
|
510
|
-
if (
|
|
544
|
+
if (fixedDelay > 0) {
|
|
545
|
+
await sleep2(fixedDelay);
|
|
546
|
+
} else if (stealth) {
|
|
547
|
+
const roll = Math.random();
|
|
548
|
+
const cfg = DEFAULT_STEALTH_CONFIG.typingRhythm;
|
|
549
|
+
if (roll < cfg.pauseProb) {
|
|
550
|
+
await sleep2(rand(cfg.pauseRange[0], cfg.pauseRange[1]));
|
|
551
|
+
} else if (roll < cfg.pauseProb + cfg.fastProb) {
|
|
552
|
+
await sleep2(rand(cfg.fastRange[0], cfg.fastRange[1]));
|
|
553
|
+
} else {
|
|
554
|
+
await sleep2(rand(cfg.normalRange[0], cfg.normalRange[1]));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
511
557
|
const mapping = resolveKeyMapping(char);
|
|
512
558
|
const downParams = {
|
|
513
559
|
type: "rawKeyDown",
|
package/dist/cli.js
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
resolveLaunchOpts,
|
|
60
60
|
saveSessionDiskMeta,
|
|
61
61
|
setActivePage
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-BTFKJD7Z.js";
|
|
63
63
|
import "./chunk-TNEN6VQ2.js";
|
|
64
64
|
import {
|
|
65
65
|
errMsg
|
|
@@ -7523,7 +7523,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7523
7523
|
}
|
|
7524
7524
|
let targetPageOverride = null;
|
|
7525
7525
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7526
|
-
const { findTargetPage } = await import("./browser-
|
|
7526
|
+
const { findTargetPage } = await import("./browser-DI6UQN6Q.js");
|
|
7527
7527
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7528
7528
|
if (!targetPageOverride) {
|
|
7529
7529
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -13663,7 +13663,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13663
13663
|
const targetPage = pages[cmdTabIndex];
|
|
13664
13664
|
await targetPage.bringToFront().catch(() => {
|
|
13665
13665
|
});
|
|
13666
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
13666
|
+
const { setActivePage: setActivePage2 } = await import("./browser-DI6UQN6Q.js");
|
|
13667
13667
|
setActivePage2(session, targetPage);
|
|
13668
13668
|
}
|
|
13669
13669
|
}
|
|
@@ -13939,7 +13939,7 @@ async function main() {
|
|
|
13939
13939
|
const command = process.argv[2];
|
|
13940
13940
|
const isLongRunning = command === "preview" || command === "serve";
|
|
13941
13941
|
if (!isLongRunning) {
|
|
13942
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
13942
|
+
const { ensureProcessCanExit } = await import("./browser-DI6UQN6Q.js");
|
|
13943
13943
|
await ensureProcessCanExit().catch(() => {
|
|
13944
13944
|
});
|
|
13945
13945
|
process.exit(process.exitCode || exitCode);
|
package/dist/daemon-main.js
CHANGED
|
@@ -30,10 +30,10 @@ import {
|
|
|
30
30
|
resolveLaunchOpts,
|
|
31
31
|
saveSessionDiskMeta,
|
|
32
32
|
setActivePage
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ZD4OSYOX.js";
|
|
34
34
|
import {
|
|
35
35
|
createRuleEngine
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-UPAWITVM.js";
|
|
37
37
|
import {
|
|
38
38
|
queryJS
|
|
39
39
|
} from "./chunk-3FWLW7FS.js";
|
|
@@ -7041,7 +7041,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7041
7041
|
}
|
|
7042
7042
|
let targetPageOverride = null;
|
|
7043
7043
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7044
|
-
const { findTargetPage } = await import("./browser-
|
|
7044
|
+
const { findTargetPage } = await import("./browser-5A6AL62M.js");
|
|
7045
7045
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7046
7046
|
if (!targetPageOverride) {
|
|
7047
7047
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8857,7 +8857,7 @@ function createRPCHandler() {
|
|
|
8857
8857
|
if (isNewFormat) {
|
|
8858
8858
|
try {
|
|
8859
8859
|
const replayErrors = [];
|
|
8860
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
8860
|
+
const { SessionReplayer } = await import("./session-replayer-I3PJFO3H.js");
|
|
8861
8861
|
const replayer = new SessionReplayer({
|
|
8862
8862
|
page: session.page,
|
|
8863
8863
|
stepDelay: slowMo * 500,
|
package/dist/index.js
CHANGED
|
@@ -92,8 +92,8 @@ import {
|
|
|
92
92
|
resolveLaunchOpts,
|
|
93
93
|
saveSessionDiskMeta,
|
|
94
94
|
setActivePage
|
|
95
|
-
} from "./chunk-
|
|
96
|
-
import "./chunk-
|
|
95
|
+
} from "./chunk-B753M33E.js";
|
|
96
|
+
import "./chunk-PYQGDBAF.js";
|
|
97
97
|
import "./chunk-TNEN6VQ2.js";
|
|
98
98
|
import {
|
|
99
99
|
errMsg
|
|
@@ -7873,7 +7873,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7873
7873
|
}
|
|
7874
7874
|
let targetPageOverride = null;
|
|
7875
7875
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7876
|
-
const { findTargetPage } = await import("./browser-
|
|
7876
|
+
const { findTargetPage } = await import("./browser-KJWBHGHX.js");
|
|
7877
7877
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7878
7878
|
if (!targetPageOverride) {
|
|
7879
7879
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -14051,7 +14051,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
14051
14051
|
const targetPage = pages[cmdTabIndex];
|
|
14052
14052
|
await targetPage.bringToFront().catch(() => {
|
|
14053
14053
|
});
|
|
14054
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
14054
|
+
const { setActivePage: setActivePage2 } = await import("./browser-KJWBHGHX.js");
|
|
14055
14055
|
setActivePage2(session, targetPage);
|
|
14056
14056
|
}
|
|
14057
14057
|
}
|
|
@@ -17224,7 +17224,7 @@ var DataCollector = class {
|
|
|
17224
17224
|
return results;
|
|
17225
17225
|
}
|
|
17226
17226
|
async createBrowserContext() {
|
|
17227
|
-
const { launch } = await import("./cdp-driver-
|
|
17227
|
+
const { launch } = await import("./cdp-driver-AHKUT57K.js");
|
|
17228
17228
|
const { browser } = await launch({
|
|
17229
17229
|
headless: true,
|
|
17230
17230
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -34,7 +34,7 @@ var SessionReplayer = class {
|
|
|
34
34
|
if (this.opts.page) {
|
|
35
35
|
this.page = this.opts.page;
|
|
36
36
|
} else if (this.opts.cdpUrl) {
|
|
37
|
-
const { launch } = await import("./cdp-driver-
|
|
37
|
+
const { launch } = await import("./cdp-driver-4SFS6GNY.js");
|
|
38
38
|
const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
|
|
39
39
|
let contexts = browser.contexts();
|
|
40
40
|
for (let i = 0; i < 10 && contexts.length === 0; i++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Browser automation CLI for web scraping, headless browsing, SEO analysis, and AI agent workflows. A command-line alternative to Playwright, Puppeteer, and Selenium.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|