@xbrowser/cli 1.9.1 → 1.9.3
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/anti-bot-MCVM6IFB.js +9 -0
- package/dist/chunk-7ZDWM6T2.js +145 -0
- package/dist/chunk-DPJNCMLC.js +246 -0
- package/dist/chunk-IIM5GOD7.js +93 -0
- package/dist/cli.js +80 -373
- package/dist/daemon-main.js +21 -389
- package/dist/human-interaction-HJTXFVQS.js +312 -0
- package/dist/human-interaction-OU7LZ6OZ.js +434 -0
- package/dist/index.d.ts +32 -1
- package/dist/index.js +55 -285
- package/package.json +1 -1
package/dist/daemon-main.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPluginLoader
|
|
3
|
+
} from "./chunk-PIWNMC36.js";
|
|
4
|
+
import {
|
|
5
|
+
getDaemonConfig,
|
|
6
|
+
getDaemonProcessStatus,
|
|
7
|
+
version
|
|
8
|
+
} from "./chunk-24SY6PE5.js";
|
|
1
9
|
import {
|
|
2
10
|
commandLogStore,
|
|
3
11
|
networkStore,
|
|
@@ -24,17 +32,16 @@ import {
|
|
|
24
32
|
} from "./chunk-OOJ2H7IL.js";
|
|
25
33
|
import "./chunk-VJNMAWPZ.js";
|
|
26
34
|
import "./chunk-TNEN6VQ2.js";
|
|
27
|
-
import {
|
|
28
|
-
getPluginLoader
|
|
29
|
-
} from "./chunk-PIWNMC36.js";
|
|
30
|
-
import {
|
|
31
|
-
getDaemonConfig,
|
|
32
|
-
getDaemonProcessStatus,
|
|
33
|
-
version
|
|
34
|
-
} from "./chunk-24SY6PE5.js";
|
|
35
35
|
import {
|
|
36
36
|
errMsg
|
|
37
37
|
} from "./chunk-GDKLH7ZY.js";
|
|
38
|
+
import {
|
|
39
|
+
detectAntiBot,
|
|
40
|
+
formatDetectionMessage
|
|
41
|
+
} from "./chunk-DPJNCMLC.js";
|
|
42
|
+
import {
|
|
43
|
+
ScreencastCapturer
|
|
44
|
+
} from "./chunk-7ZDWM6T2.js";
|
|
38
45
|
import "./chunk-KFQGP6VL.js";
|
|
39
46
|
|
|
40
47
|
// src/daemon/daemon-main.ts
|
|
@@ -5815,246 +5822,12 @@ function describeSelector(strategy, value, name) {
|
|
|
5815
5822
|
}
|
|
5816
5823
|
}
|
|
5817
5824
|
|
|
5818
|
-
// src/
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
{ name: "Cloudflare", pattern: /cloudflare/i },
|
|
5824
|
-
{ name: "FunCaptcha", pattern: /funcaptcha/i },
|
|
5825
|
-
{ name: "AWS WAF", pattern: /aws.*challenge/i }
|
|
5826
|
-
];
|
|
5827
|
-
var WARNING_TEXTS = [
|
|
5828
|
-
{ text: "detected as bot", severity: "high" },
|
|
5829
|
-
{ text: "suspicious activity", severity: "high" },
|
|
5830
|
-
{ text: "unusual traffic", severity: "high" },
|
|
5831
|
-
{ text: "please verify you are human", severity: "medium" },
|
|
5832
|
-
{ text: "access denied", severity: "high" },
|
|
5833
|
-
{ text: "blocked", severity: "high" },
|
|
5834
|
-
{ text: "rate limit", severity: "medium" },
|
|
5835
|
-
{ text: "too many requests", severity: "medium" },
|
|
5836
|
-
{ text: "\u9A8C\u8BC1", severity: "low" },
|
|
5837
|
-
{ text: "\u9A8C\u8BC1\u7801", severity: "low" }
|
|
5838
|
-
];
|
|
5839
|
-
var BLOCKED_URL_PATTERNS = [
|
|
5840
|
-
{ name: "Cloudflare Challenge", pattern: /challenge-platform/i },
|
|
5841
|
-
{ name: "Cloudflare Captcha", pattern: /cf-challenge/i },
|
|
5842
|
-
{ name: "AWS WAF", pattern: /aws-waf/i },
|
|
5843
|
-
{ name: "Generic Captcha", pattern: /captcha/i }
|
|
5844
|
-
];
|
|
5845
|
-
async function detectAntiBot(page, config = {}) {
|
|
5846
|
-
const {
|
|
5847
|
-
checkCaptcha = true,
|
|
5848
|
-
checkWarning = true,
|
|
5849
|
-
checkBlocked = true,
|
|
5850
|
-
checkLogin = false,
|
|
5851
|
-
checkWebdriver = true
|
|
5852
|
-
} = config;
|
|
5853
|
-
const result = {
|
|
5854
|
-
detected: false
|
|
5825
|
+
// src/context.ts
|
|
5826
|
+
function attachDetectAntiBot(ctx) {
|
|
5827
|
+
ctx.detectAntiBot = async (page, config) => {
|
|
5828
|
+
const { detectAntiBot: detectAntiBot2 } = await import("./anti-bot-MCVM6IFB.js");
|
|
5829
|
+
return detectAntiBot2(page, config);
|
|
5855
5830
|
};
|
|
5856
|
-
if (checkCaptcha) {
|
|
5857
|
-
const captchaResult = await detectCaptcha2(page);
|
|
5858
|
-
if (captchaResult.detected) {
|
|
5859
|
-
return captchaResult;
|
|
5860
|
-
}
|
|
5861
|
-
}
|
|
5862
|
-
if (checkWarning) {
|
|
5863
|
-
const warningResult = await detectWarningText(page);
|
|
5864
|
-
if (warningResult.detected) {
|
|
5865
|
-
return warningResult;
|
|
5866
|
-
}
|
|
5867
|
-
}
|
|
5868
|
-
if (checkBlocked) {
|
|
5869
|
-
const blockedResult = await detectBlockedPage(page);
|
|
5870
|
-
if (blockedResult.detected) {
|
|
5871
|
-
return blockedResult;
|
|
5872
|
-
}
|
|
5873
|
-
}
|
|
5874
|
-
if (checkWebdriver) {
|
|
5875
|
-
const webdriverResult = await detectWebdriverExposure(page);
|
|
5876
|
-
if (webdriverResult.detected) {
|
|
5877
|
-
return webdriverResult;
|
|
5878
|
-
}
|
|
5879
|
-
}
|
|
5880
|
-
if (checkLogin) {
|
|
5881
|
-
const loginResult = await detectLoginRequired(page);
|
|
5882
|
-
if (loginResult.detected) {
|
|
5883
|
-
return loginResult;
|
|
5884
|
-
}
|
|
5885
|
-
}
|
|
5886
|
-
return result;
|
|
5887
|
-
}
|
|
5888
|
-
async function detectCaptcha2(page) {
|
|
5889
|
-
try {
|
|
5890
|
-
const iframes = await page.frames();
|
|
5891
|
-
for (const iframe of iframes) {
|
|
5892
|
-
const src = iframe.url();
|
|
5893
|
-
for (const pattern of CAPTCHA_PATTERNS) {
|
|
5894
|
-
if (pattern.pattern.test(src)) {
|
|
5895
|
-
return {
|
|
5896
|
-
detected: true,
|
|
5897
|
-
type: "captcha",
|
|
5898
|
-
severity: "high",
|
|
5899
|
-
message: `Detected ${pattern.name} CAPTCHA (iframe: ${src})`,
|
|
5900
|
-
selector: `iframe[src*="${pattern.name.toLowerCase()}"]`,
|
|
5901
|
-
actionRequired: "manual"
|
|
5902
|
-
};
|
|
5903
|
-
}
|
|
5904
|
-
}
|
|
5905
|
-
}
|
|
5906
|
-
const captchaSelectors = [
|
|
5907
|
-
".g-recaptcha",
|
|
5908
|
-
"#captcha",
|
|
5909
|
-
'[class*="captcha"]',
|
|
5910
|
-
'[id*="captcha"]',
|
|
5911
|
-
'iframe[src*="recaptcha"]',
|
|
5912
|
-
'iframe[src*="hcaptcha"]',
|
|
5913
|
-
'iframe[src*="turnstile"]'
|
|
5914
|
-
];
|
|
5915
|
-
for (const selector of captchaSelectors) {
|
|
5916
|
-
const element = await page.$(selector).catch(() => null);
|
|
5917
|
-
if (element) {
|
|
5918
|
-
return {
|
|
5919
|
-
detected: true,
|
|
5920
|
-
type: "captcha",
|
|
5921
|
-
severity: "high",
|
|
5922
|
-
message: `CAPTCHA element found: ${selector}`,
|
|
5923
|
-
selector,
|
|
5924
|
-
actionRequired: "manual"
|
|
5925
|
-
};
|
|
5926
|
-
}
|
|
5927
|
-
}
|
|
5928
|
-
return { detected: false };
|
|
5929
|
-
} catch {
|
|
5930
|
-
return { detected: false };
|
|
5931
|
-
}
|
|
5932
|
-
}
|
|
5933
|
-
async function detectWarningText(page) {
|
|
5934
|
-
try {
|
|
5935
|
-
const pageText = await page.textContent("body").catch(() => "") || "";
|
|
5936
|
-
const lowerText = pageText.toLowerCase();
|
|
5937
|
-
for (const { text, severity } of WARNING_TEXTS) {
|
|
5938
|
-
if (lowerText.includes(text.toLowerCase())) {
|
|
5939
|
-
return {
|
|
5940
|
-
detected: true,
|
|
5941
|
-
type: "warning",
|
|
5942
|
-
severity,
|
|
5943
|
-
message: `Anti-bot warning text found: "${text}"`,
|
|
5944
|
-
actionRequired: severity === "high" ? "manual" : "retry"
|
|
5945
|
-
};
|
|
5946
|
-
}
|
|
5947
|
-
}
|
|
5948
|
-
return { detected: false };
|
|
5949
|
-
} catch {
|
|
5950
|
-
return { detected: false };
|
|
5951
|
-
}
|
|
5952
|
-
}
|
|
5953
|
-
async function detectBlockedPage(page) {
|
|
5954
|
-
try {
|
|
5955
|
-
const url = page.url();
|
|
5956
|
-
for (const { name, pattern } of BLOCKED_URL_PATTERNS) {
|
|
5957
|
-
if (pattern.test(url)) {
|
|
5958
|
-
return {
|
|
5959
|
-
detected: true,
|
|
5960
|
-
type: "blocked",
|
|
5961
|
-
severity: "high",
|
|
5962
|
-
message: `Blocked page detected: ${name} (${url})`,
|
|
5963
|
-
actionRequired: "manual"
|
|
5964
|
-
};
|
|
5965
|
-
}
|
|
5966
|
-
}
|
|
5967
|
-
return { detected: false };
|
|
5968
|
-
} catch {
|
|
5969
|
-
return { detected: false };
|
|
5970
|
-
}
|
|
5971
|
-
}
|
|
5972
|
-
async function detectWebdriverExposure(page) {
|
|
5973
|
-
try {
|
|
5974
|
-
const webdriver = await page.evaluate(() => {
|
|
5975
|
-
return {
|
|
5976
|
-
webdriver: navigator.webdriver,
|
|
5977
|
-
webdriverScriptFn: !!window.__webdriver_script_fn,
|
|
5978
|
-
webdriverEvaluate: !!window.__webdriver_evaluate,
|
|
5979
|
-
chrome: !!window.chrome,
|
|
5980
|
-
permissions: navigator.permissions
|
|
5981
|
-
};
|
|
5982
|
-
}).catch(() => null);
|
|
5983
|
-
if (!webdriver) {
|
|
5984
|
-
return { detected: false };
|
|
5985
|
-
}
|
|
5986
|
-
const issues = [];
|
|
5987
|
-
if (webdriver.webdriver === true) {
|
|
5988
|
-
issues.push("navigator.webdriver === true");
|
|
5989
|
-
}
|
|
5990
|
-
if (webdriver.webdriverScriptFn) {
|
|
5991
|
-
issues.push("__webdriver_script_fn present");
|
|
5992
|
-
}
|
|
5993
|
-
if (webdriver.webdriverEvaluate) {
|
|
5994
|
-
issues.push("__webdriver_evaluate present");
|
|
5995
|
-
}
|
|
5996
|
-
if (!webdriver.chrome) {
|
|
5997
|
-
issues.push("window.chrome missing");
|
|
5998
|
-
}
|
|
5999
|
-
if (!webdriver.permissions) {
|
|
6000
|
-
issues.push("navigator.permissions missing");
|
|
6001
|
-
}
|
|
6002
|
-
if (issues.length > 0) {
|
|
6003
|
-
return {
|
|
6004
|
-
detected: true,
|
|
6005
|
-
type: "warning",
|
|
6006
|
-
severity: "high",
|
|
6007
|
-
message: `Automation markers exposed: ${issues.join(", ")}`,
|
|
6008
|
-
actionRequired: "manual"
|
|
6009
|
-
};
|
|
6010
|
-
}
|
|
6011
|
-
return { detected: false };
|
|
6012
|
-
} catch {
|
|
6013
|
-
return { detected: false };
|
|
6014
|
-
}
|
|
6015
|
-
}
|
|
6016
|
-
async function detectLoginRequired(page) {
|
|
6017
|
-
try {
|
|
6018
|
-
const loginSelectors = [
|
|
6019
|
-
'a[href*="login"]',
|
|
6020
|
-
'a[href*="signin"]',
|
|
6021
|
-
'a[href*="sign-in"]',
|
|
6022
|
-
'button:has-text("Log in")',
|
|
6023
|
-
'button:has-text("Sign in")',
|
|
6024
|
-
'button:has-text("Login")',
|
|
6025
|
-
'button:has-text("\u767B\u5F55")'
|
|
6026
|
-
];
|
|
6027
|
-
for (const selector of loginSelectors) {
|
|
6028
|
-
const element = await page.$(selector).catch(() => null);
|
|
6029
|
-
if (element) {
|
|
6030
|
-
const rect = await element.boundingBox().catch(() => null);
|
|
6031
|
-
if (rect && rect.y < 200) {
|
|
6032
|
-
return {
|
|
6033
|
-
detected: true,
|
|
6034
|
-
type: "login",
|
|
6035
|
-
severity: "medium",
|
|
6036
|
-
message: "Login button detected in page header",
|
|
6037
|
-
selector,
|
|
6038
|
-
actionRequired: "manual"
|
|
6039
|
-
};
|
|
6040
|
-
}
|
|
6041
|
-
}
|
|
6042
|
-
}
|
|
6043
|
-
return { detected: false };
|
|
6044
|
-
} catch {
|
|
6045
|
-
return { detected: false };
|
|
6046
|
-
}
|
|
6047
|
-
}
|
|
6048
|
-
function formatDetectionMessage(result) {
|
|
6049
|
-
if (!result.detected) {
|
|
6050
|
-
return "\u2705 No anti-bot detection detected.";
|
|
6051
|
-
}
|
|
6052
|
-
const emoji = result.severity === "high" ? "\u{1F6A8}" : result.severity === "medium" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
|
|
6053
|
-
const action = result.actionRequired === "manual" ? "Please handle manually" : result.actionRequired === "retry" ? "Consider retrying with delay" : "Consider switching to a different session";
|
|
6054
|
-
return `${emoji} Detection: ${result.message}
|
|
6055
|
-
Type: ${result.type}
|
|
6056
|
-
Severity: ${result.severity}
|
|
6057
|
-
Action: ${action}`;
|
|
6058
5831
|
}
|
|
6059
5832
|
|
|
6060
5833
|
// src/utils/viewer-url.ts
|
|
@@ -6858,6 +6631,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6858
6631
|
cliName: "xbrowser",
|
|
6859
6632
|
tips: new TipCollector2()
|
|
6860
6633
|
};
|
|
6634
|
+
attachDetectAntiBot(ctx);
|
|
6861
6635
|
if (_tabIndex !== void 0 && session?.context) {
|
|
6862
6636
|
const pages = session.context.pages();
|
|
6863
6637
|
if (_tabIndex >= 0 && _tabIndex < pages.length) {
|
|
@@ -8780,148 +8554,6 @@ var StreamCoordinator = class {
|
|
|
8780
8554
|
// src/ws/session-manager.ts
|
|
8781
8555
|
import { EventEmitter } from "events";
|
|
8782
8556
|
|
|
8783
|
-
// src/screencast.ts
|
|
8784
|
-
var ScreencastCapturer = class {
|
|
8785
|
-
interval;
|
|
8786
|
-
quality;
|
|
8787
|
-
type;
|
|
8788
|
-
maxWidth;
|
|
8789
|
-
maxHeight;
|
|
8790
|
-
isCapturing = false;
|
|
8791
|
-
frameCallback = null;
|
|
8792
|
-
// CDP Cast state
|
|
8793
|
-
cdpSession = null;
|
|
8794
|
-
sessionId = "";
|
|
8795
|
-
// Fallback polling state
|
|
8796
|
-
fallbackTimer = null;
|
|
8797
|
-
constructor(options = {}) {
|
|
8798
|
-
this.interval = options.interval || 100;
|
|
8799
|
-
this.quality = options.quality || 60;
|
|
8800
|
-
this.type = options.type || "jpeg";
|
|
8801
|
-
this.maxWidth = options.width || 1920;
|
|
8802
|
-
this.maxHeight = options.height || 1080;
|
|
8803
|
-
}
|
|
8804
|
-
/**
|
|
8805
|
-
* Start screencast capture using CDP Page.startScreencast.
|
|
8806
|
-
*
|
|
8807
|
-
* If CDP session creation fails (non-Chromium, restricted access),
|
|
8808
|
-
* automatically falls back to `page.screenshot()` polling.
|
|
8809
|
-
*/
|
|
8810
|
-
async startCapture(page, sessionId, onFrame) {
|
|
8811
|
-
if (this.isCapturing) {
|
|
8812
|
-
throw new Error("Screencast is already capturing");
|
|
8813
|
-
}
|
|
8814
|
-
this.isCapturing = true;
|
|
8815
|
-
this.frameCallback = onFrame;
|
|
8816
|
-
this.sessionId = sessionId;
|
|
8817
|
-
try {
|
|
8818
|
-
const cdp = await page.context().newCDPSession(page);
|
|
8819
|
-
this.cdpSession = cdp;
|
|
8820
|
-
cdp.on("Page.screencastFrame", async (params) => {
|
|
8821
|
-
if (!this.frameCallback) return;
|
|
8822
|
-
try {
|
|
8823
|
-
await cdp.send("Page.screencastFrameAck", { sessionId: params.sessionId });
|
|
8824
|
-
} catch {
|
|
8825
|
-
}
|
|
8826
|
-
const viewport = {
|
|
8827
|
-
width: params.metadata?.deviceWidth || this.maxWidth,
|
|
8828
|
-
height: params.metadata?.deviceHeight || this.maxHeight
|
|
8829
|
-
};
|
|
8830
|
-
this.frameCallback({
|
|
8831
|
-
id: crypto.randomUUID(),
|
|
8832
|
-
sessionId: this.sessionId,
|
|
8833
|
-
timestamp: Date.now(),
|
|
8834
|
-
data: Buffer.from(params.data, "base64"),
|
|
8835
|
-
url: page.url(),
|
|
8836
|
-
viewport
|
|
8837
|
-
});
|
|
8838
|
-
});
|
|
8839
|
-
await cdp.send("Page.startScreencast", {
|
|
8840
|
-
format: this.type === "png" ? "png" : "jpeg",
|
|
8841
|
-
quality: this.type === "jpeg" ? this.quality : void 0,
|
|
8842
|
-
maxWidth: this.maxWidth,
|
|
8843
|
-
maxHeight: this.maxHeight
|
|
8844
|
-
});
|
|
8845
|
-
} catch {
|
|
8846
|
-
this.cdpSession = null;
|
|
8847
|
-
this.startFallbackPolling(page, sessionId);
|
|
8848
|
-
}
|
|
8849
|
-
}
|
|
8850
|
-
/**
|
|
8851
|
-
* Fallback: periodic page.screenshot() polling when CDP Cast is unavailable.
|
|
8852
|
-
*/
|
|
8853
|
-
startFallbackPolling(page, sessionId) {
|
|
8854
|
-
const captureLoop = async () => {
|
|
8855
|
-
if (!this.frameCallback) return;
|
|
8856
|
-
try {
|
|
8857
|
-
const frame = await this.captureFrame(page, sessionId);
|
|
8858
|
-
this.frameCallback(frame);
|
|
8859
|
-
} catch {
|
|
8860
|
-
}
|
|
8861
|
-
};
|
|
8862
|
-
captureLoop();
|
|
8863
|
-
this.fallbackTimer = setInterval(captureLoop, this.interval);
|
|
8864
|
-
}
|
|
8865
|
-
/**
|
|
8866
|
-
* Capture a single screenshot frame from the page (fallback mode).
|
|
8867
|
-
*/
|
|
8868
|
-
async captureFrame(page, sessionId) {
|
|
8869
|
-
let viewport = page.viewportSize();
|
|
8870
|
-
if (!viewport) {
|
|
8871
|
-
try {
|
|
8872
|
-
viewport = await page.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight }));
|
|
8873
|
-
} catch {
|
|
8874
|
-
viewport = { width: 1920, height: 1080 };
|
|
8875
|
-
}
|
|
8876
|
-
}
|
|
8877
|
-
const screenshot = await page.screenshot({
|
|
8878
|
-
type: this.type,
|
|
8879
|
-
quality: this.type === "jpeg" ? this.quality : void 0
|
|
8880
|
-
});
|
|
8881
|
-
return {
|
|
8882
|
-
id: crypto.randomUUID(),
|
|
8883
|
-
sessionId,
|
|
8884
|
-
timestamp: Date.now(),
|
|
8885
|
-
data: screenshot,
|
|
8886
|
-
url: page.url(),
|
|
8887
|
-
viewport: viewport || { width: 0, height: 0 }
|
|
8888
|
-
};
|
|
8889
|
-
}
|
|
8890
|
-
/**
|
|
8891
|
-
* Stop the current screencast capture.
|
|
8892
|
-
*/
|
|
8893
|
-
async stopCapture() {
|
|
8894
|
-
if (this.cdpSession) {
|
|
8895
|
-
try {
|
|
8896
|
-
this.cdpSession.off("Page.screencastFrame", () => {
|
|
8897
|
-
});
|
|
8898
|
-
await this.cdpSession.send("Page.stopScreencast");
|
|
8899
|
-
} catch {
|
|
8900
|
-
}
|
|
8901
|
-
try {
|
|
8902
|
-
await this.cdpSession.detach();
|
|
8903
|
-
} catch {
|
|
8904
|
-
}
|
|
8905
|
-
this.cdpSession = null;
|
|
8906
|
-
}
|
|
8907
|
-
if (this.fallbackTimer) {
|
|
8908
|
-
clearInterval(this.fallbackTimer);
|
|
8909
|
-
this.fallbackTimer = null;
|
|
8910
|
-
}
|
|
8911
|
-
this.isCapturing = false;
|
|
8912
|
-
this.frameCallback = null;
|
|
8913
|
-
}
|
|
8914
|
-
isActive() {
|
|
8915
|
-
return this.isCapturing;
|
|
8916
|
-
}
|
|
8917
|
-
setInterval(interval) {
|
|
8918
|
-
this.interval = interval;
|
|
8919
|
-
}
|
|
8920
|
-
setQuality(quality) {
|
|
8921
|
-
this.quality = quality;
|
|
8922
|
-
}
|
|
8923
|
-
};
|
|
8924
|
-
|
|
8925
8557
|
// src/ws/element-monitor.ts
|
|
8926
8558
|
var ElementMonitor = class {
|
|
8927
8559
|
constructor(page, onFocusChange, onViewsUpdate, getClientCount) {
|