@xbrowser/cli 1.9.2 → 1.9.4
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 +81 -374
- package/dist/daemon-main.js +22 -390
- 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 +56 -286
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectAntiBot,
|
|
3
|
+
formatDetectionMessage
|
|
4
|
+
} from "./chunk-DPJNCMLC.js";
|
|
5
|
+
import {
|
|
6
|
+
CaptchaDetector,
|
|
7
|
+
HumanInteractionManager,
|
|
8
|
+
NPM_REGISTRY_URL,
|
|
9
|
+
NPM_SCOPE,
|
|
10
|
+
ScreencastCapturer,
|
|
11
|
+
WebhookNotifier,
|
|
12
|
+
getCaptchaConfig,
|
|
13
|
+
getConfigValue,
|
|
14
|
+
getMarketplaceUrl,
|
|
15
|
+
loadConfig,
|
|
16
|
+
resolveNpmPackageWithFallback,
|
|
17
|
+
setConfigValue
|
|
18
|
+
} from "./chunk-ZJHQPMCY.js";
|
|
1
19
|
import {
|
|
2
20
|
forwardCommandLog,
|
|
3
21
|
forwardNetworkAnalyze,
|
|
@@ -27,20 +45,6 @@ import {
|
|
|
27
45
|
stopDaemonProcess,
|
|
28
46
|
version
|
|
29
47
|
} from "./chunk-53LRNYPG.js";
|
|
30
|
-
import {
|
|
31
|
-
CaptchaDetector,
|
|
32
|
-
HumanInteractionManager,
|
|
33
|
-
NPM_REGISTRY_URL,
|
|
34
|
-
NPM_SCOPE,
|
|
35
|
-
ScreencastCapturer,
|
|
36
|
-
WebhookNotifier,
|
|
37
|
-
getCaptchaConfig,
|
|
38
|
-
getConfigValue,
|
|
39
|
-
getMarketplaceUrl,
|
|
40
|
-
loadConfig,
|
|
41
|
-
resolveNpmPackageWithFallback,
|
|
42
|
-
setConfigValue
|
|
43
|
-
} from "./chunk-ZJHQPMCY.js";
|
|
44
48
|
import {
|
|
45
49
|
generateBashScript,
|
|
46
50
|
generateJSScript,
|
|
@@ -6167,246 +6171,49 @@ function describeSelector(strategy, value, name) {
|
|
|
6167
6171
|
}
|
|
6168
6172
|
}
|
|
6169
6173
|
|
|
6170
|
-
// src/
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
{ text: "please verify you are human", severity: "medium" },
|
|
6184
|
-
{ text: "access denied", severity: "high" },
|
|
6185
|
-
{ text: "blocked", severity: "high" },
|
|
6186
|
-
{ text: "rate limit", severity: "medium" },
|
|
6187
|
-
{ text: "too many requests", severity: "medium" },
|
|
6188
|
-
{ text: "\u9A8C\u8BC1", severity: "low" },
|
|
6189
|
-
{ text: "\u9A8C\u8BC1\u7801", severity: "low" }
|
|
6190
|
-
];
|
|
6191
|
-
var BLOCKED_URL_PATTERNS = [
|
|
6192
|
-
{ name: "Cloudflare Challenge", pattern: /challenge-platform/i },
|
|
6193
|
-
{ name: "Cloudflare Captcha", pattern: /cf-challenge/i },
|
|
6194
|
-
{ name: "AWS WAF", pattern: /aws-waf/i },
|
|
6195
|
-
{ name: "Generic Captcha", pattern: /captcha/i }
|
|
6196
|
-
];
|
|
6197
|
-
async function detectAntiBot(page, config = {}) {
|
|
6198
|
-
const {
|
|
6199
|
-
checkCaptcha = true,
|
|
6200
|
-
checkWarning = true,
|
|
6201
|
-
checkBlocked = true,
|
|
6202
|
-
checkLogin = false,
|
|
6203
|
-
checkWebdriver = true
|
|
6204
|
-
} = config;
|
|
6205
|
-
const result = {
|
|
6206
|
-
detected: false
|
|
6207
|
-
};
|
|
6208
|
-
if (checkCaptcha) {
|
|
6209
|
-
const captchaResult = await detectCaptcha2(page);
|
|
6210
|
-
if (captchaResult.detected) {
|
|
6211
|
-
return captchaResult;
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
if (checkWarning) {
|
|
6215
|
-
const warningResult = await detectWarningText(page);
|
|
6216
|
-
if (warningResult.detected) {
|
|
6217
|
-
return warningResult;
|
|
6218
|
-
}
|
|
6219
|
-
}
|
|
6220
|
-
if (checkBlocked) {
|
|
6221
|
-
const blockedResult = await detectBlockedPage(page);
|
|
6222
|
-
if (blockedResult.detected) {
|
|
6223
|
-
return blockedResult;
|
|
6224
|
-
}
|
|
6225
|
-
}
|
|
6226
|
-
if (checkWebdriver) {
|
|
6227
|
-
const webdriverResult = await detectWebdriverExposure(page);
|
|
6228
|
-
if (webdriverResult.detected) {
|
|
6229
|
-
return webdriverResult;
|
|
6230
|
-
}
|
|
6231
|
-
}
|
|
6232
|
-
if (checkLogin) {
|
|
6233
|
-
const loginResult = await detectLoginRequired(page);
|
|
6234
|
-
if (loginResult.detected) {
|
|
6235
|
-
return loginResult;
|
|
6236
|
-
}
|
|
6237
|
-
}
|
|
6238
|
-
return result;
|
|
6239
|
-
}
|
|
6240
|
-
async function detectCaptcha2(page) {
|
|
6241
|
-
try {
|
|
6242
|
-
const iframes = await page.frames();
|
|
6243
|
-
for (const iframe of iframes) {
|
|
6244
|
-
const src = iframe.url();
|
|
6245
|
-
for (const pattern of CAPTCHA_PATTERNS) {
|
|
6246
|
-
if (pattern.pattern.test(src)) {
|
|
6247
|
-
return {
|
|
6248
|
-
detected: true,
|
|
6249
|
-
type: "captcha",
|
|
6250
|
-
severity: "high",
|
|
6251
|
-
message: `Detected ${pattern.name} CAPTCHA (iframe: ${src})`,
|
|
6252
|
-
selector: `iframe[src*="${pattern.name.toLowerCase()}"]`,
|
|
6253
|
-
actionRequired: "manual"
|
|
6254
|
-
};
|
|
6255
|
-
}
|
|
6256
|
-
}
|
|
6257
|
-
}
|
|
6258
|
-
const captchaSelectors = [
|
|
6259
|
-
".g-recaptcha",
|
|
6260
|
-
"#captcha",
|
|
6261
|
-
'[class*="captcha"]',
|
|
6262
|
-
'[id*="captcha"]',
|
|
6263
|
-
'iframe[src*="recaptcha"]',
|
|
6264
|
-
'iframe[src*="hcaptcha"]',
|
|
6265
|
-
'iframe[src*="turnstile"]'
|
|
6266
|
-
];
|
|
6267
|
-
for (const selector of captchaSelectors) {
|
|
6268
|
-
const element = await page.$(selector).catch(() => null);
|
|
6269
|
-
if (element) {
|
|
6270
|
-
return {
|
|
6271
|
-
detected: true,
|
|
6272
|
-
type: "captcha",
|
|
6273
|
-
severity: "high",
|
|
6274
|
-
message: `CAPTCHA element found: ${selector}`,
|
|
6275
|
-
selector,
|
|
6276
|
-
actionRequired: "manual"
|
|
6277
|
-
};
|
|
6278
|
-
}
|
|
6279
|
-
}
|
|
6280
|
-
return { detected: false };
|
|
6281
|
-
} catch {
|
|
6282
|
-
return { detected: false };
|
|
6174
|
+
// src/context.ts
|
|
6175
|
+
function checkBrowserScope(scope, ctx) {
|
|
6176
|
+
switch (scope) {
|
|
6177
|
+
case "project":
|
|
6178
|
+
return null;
|
|
6179
|
+
case "browser":
|
|
6180
|
+
return ctx.browser ? null : "\u9700\u8981\u6D4F\u89C8\u5668\u5B9E\u4F8B\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
6181
|
+
case "page":
|
|
6182
|
+
return ctx.page ? null : "\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
6183
|
+
case "element":
|
|
6184
|
+
return ctx.page ? null : "\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
6185
|
+
default:
|
|
6186
|
+
return null;
|
|
6283
6187
|
}
|
|
6284
6188
|
}
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
const lowerText = pageText.toLowerCase();
|
|
6289
|
-
for (const { text, severity } of WARNING_TEXTS) {
|
|
6290
|
-
if (lowerText.includes(text.toLowerCase())) {
|
|
6291
|
-
return {
|
|
6292
|
-
detected: true,
|
|
6293
|
-
type: "warning",
|
|
6294
|
-
severity,
|
|
6295
|
-
message: `Anti-bot warning text found: "${text}"`,
|
|
6296
|
-
actionRequired: severity === "high" ? "manual" : "retry"
|
|
6297
|
-
};
|
|
6298
|
-
}
|
|
6299
|
-
}
|
|
6300
|
-
return { detected: false };
|
|
6301
|
-
} catch {
|
|
6302
|
-
return { detected: false };
|
|
6189
|
+
function assertPageScope(ctx) {
|
|
6190
|
+
if (!ctx.page) {
|
|
6191
|
+
throw new Error("\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879");
|
|
6303
6192
|
}
|
|
6304
6193
|
}
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
return {
|
|
6311
|
-
detected: true,
|
|
6312
|
-
type: "blocked",
|
|
6313
|
-
severity: "high",
|
|
6314
|
-
message: `Blocked page detected: ${name} (${url})`,
|
|
6315
|
-
actionRequired: "manual"
|
|
6316
|
-
};
|
|
6317
|
-
}
|
|
6194
|
+
var wsServerCache = /* @__PURE__ */ new WeakMap();
|
|
6195
|
+
function attachWaitForHuman(ctx, getOrCreateWSServer) {
|
|
6196
|
+
ctx.waitForHuman = async (options) => {
|
|
6197
|
+
if (!ctx.page) {
|
|
6198
|
+
throw new Error("waitForHuman requires an active page");
|
|
6318
6199
|
}
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6200
|
+
const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-2DZK4MW7.js");
|
|
6201
|
+
const wsServer2 = await getOrCreateWSServer(ctx.browserContext);
|
|
6202
|
+
const manager = new HumanInteractionManager2(wsServer2, ctx.page);
|
|
6203
|
+
return manager.waitForHuman(options);
|
|
6204
|
+
};
|
|
6323
6205
|
}
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
const
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
webdriverScriptFn: !!window.__webdriver_script_fn,
|
|
6330
|
-
webdriverEvaluate: !!window.__webdriver_evaluate,
|
|
6331
|
-
chrome: !!window.chrome,
|
|
6332
|
-
permissions: navigator.permissions
|
|
6333
|
-
};
|
|
6334
|
-
}).catch(() => null);
|
|
6335
|
-
if (!webdriver) {
|
|
6336
|
-
return { detected: false };
|
|
6337
|
-
}
|
|
6338
|
-
const issues = [];
|
|
6339
|
-
if (webdriver.webdriver === true) {
|
|
6340
|
-
issues.push("navigator.webdriver === true");
|
|
6341
|
-
}
|
|
6342
|
-
if (webdriver.webdriverScriptFn) {
|
|
6343
|
-
issues.push("__webdriver_script_fn present");
|
|
6344
|
-
}
|
|
6345
|
-
if (webdriver.webdriverEvaluate) {
|
|
6346
|
-
issues.push("__webdriver_evaluate present");
|
|
6347
|
-
}
|
|
6348
|
-
if (!webdriver.chrome) {
|
|
6349
|
-
issues.push("window.chrome missing");
|
|
6350
|
-
}
|
|
6351
|
-
if (!webdriver.permissions) {
|
|
6352
|
-
issues.push("navigator.permissions missing");
|
|
6353
|
-
}
|
|
6354
|
-
if (issues.length > 0) {
|
|
6355
|
-
return {
|
|
6356
|
-
detected: true,
|
|
6357
|
-
type: "warning",
|
|
6358
|
-
severity: "high",
|
|
6359
|
-
message: `Automation markers exposed: ${issues.join(", ")}`,
|
|
6360
|
-
actionRequired: "manual"
|
|
6361
|
-
};
|
|
6362
|
-
}
|
|
6363
|
-
return { detected: false };
|
|
6364
|
-
} catch {
|
|
6365
|
-
return { detected: false };
|
|
6366
|
-
}
|
|
6206
|
+
function attachDetectAntiBot(ctx) {
|
|
6207
|
+
ctx.detectAntiBot = async (page, config) => {
|
|
6208
|
+
const { detectAntiBot: detectAntiBot2 } = await import("./anti-bot-MCVM6IFB.js");
|
|
6209
|
+
return detectAntiBot2(page, config);
|
|
6210
|
+
};
|
|
6367
6211
|
}
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
const loginSelectors = [
|
|
6371
|
-
'a[href*="login"]',
|
|
6372
|
-
'a[href*="signin"]',
|
|
6373
|
-
'a[href*="sign-in"]',
|
|
6374
|
-
'button:has-text("Log in")',
|
|
6375
|
-
'button:has-text("Sign in")',
|
|
6376
|
-
'button:has-text("Login")',
|
|
6377
|
-
'button:has-text("\u767B\u5F55")'
|
|
6378
|
-
];
|
|
6379
|
-
for (const selector of loginSelectors) {
|
|
6380
|
-
const element = await page.$(selector).catch(() => null);
|
|
6381
|
-
if (element) {
|
|
6382
|
-
const rect = await element.boundingBox().catch(() => null);
|
|
6383
|
-
if (rect && rect.y < 200) {
|
|
6384
|
-
return {
|
|
6385
|
-
detected: true,
|
|
6386
|
-
type: "login",
|
|
6387
|
-
severity: "medium",
|
|
6388
|
-
message: "Login button detected in page header",
|
|
6389
|
-
selector,
|
|
6390
|
-
actionRequired: "manual"
|
|
6391
|
-
};
|
|
6392
|
-
}
|
|
6393
|
-
}
|
|
6394
|
-
}
|
|
6395
|
-
return { detected: false };
|
|
6396
|
-
} catch {
|
|
6397
|
-
return { detected: false };
|
|
6398
|
-
}
|
|
6212
|
+
function getWSServerFromCache(browserContext) {
|
|
6213
|
+
return wsServerCache.get(browserContext);
|
|
6399
6214
|
}
|
|
6400
|
-
function
|
|
6401
|
-
|
|
6402
|
-
return "\u2705 No anti-bot detection detected.";
|
|
6403
|
-
}
|
|
6404
|
-
const emoji = result.severity === "high" ? "\u{1F6A8}" : result.severity === "medium" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
|
|
6405
|
-
const action = result.actionRequired === "manual" ? "Please handle manually" : result.actionRequired === "retry" ? "Consider retrying with delay" : "Consider switching to a different session";
|
|
6406
|
-
return `${emoji} Detection: ${result.message}
|
|
6407
|
-
Type: ${result.type}
|
|
6408
|
-
Severity: ${result.severity}
|
|
6409
|
-
Action: ${action}`;
|
|
6215
|
+
function setWSServerCache(browserContext, server) {
|
|
6216
|
+
wsServerCache.set(browserContext, server);
|
|
6410
6217
|
}
|
|
6411
6218
|
|
|
6412
6219
|
// src/plugin/loader.ts
|
|
@@ -6868,7 +6675,7 @@ async function checkPluginLoginRequired(options) {
|
|
|
6868
6675
|
const { site, command, commandName, ctx, page, sessionName } = options;
|
|
6869
6676
|
if (commandName === "login" || commandName === "logout") return { ok: true };
|
|
6870
6677
|
const loginConfig = site.config?.loginConfig;
|
|
6871
|
-
const requiresLogin = command.requiresLogin === true || site.config?.requiresLogin === true || loginConfig?.requiresLogin === true;
|
|
6678
|
+
const requiresLogin = command.requiresLogin === true || site.config?.requiresLogin === true || loginConfig?.requiresLogin === true || command.loginRequired === "required";
|
|
6872
6679
|
if (!requiresLogin) return { ok: true };
|
|
6873
6680
|
const pluginName = site.name || "plugin";
|
|
6874
6681
|
if (typeof site.isLoggedIn === "function") {
|
|
@@ -7655,6 +7462,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7655
7462
|
cliName: "xbrowser",
|
|
7656
7463
|
tips: new TipCollector2()
|
|
7657
7464
|
};
|
|
7465
|
+
attachDetectAntiBot(ctx);
|
|
7658
7466
|
if (_tabIndex !== void 0 && session?.context) {
|
|
7659
7467
|
const pages = session.context.pages();
|
|
7660
7468
|
if (_tabIndex >= 0 && _tabIndex < pages.length) {
|
|
@@ -8074,45 +7882,6 @@ function isChainInput(input) {
|
|
|
8074
7882
|
return /\s&&\s|\|\|\s|\s;\s|;\s|\s,\s|\s\+\s|\s->\s/.test(input);
|
|
8075
7883
|
}
|
|
8076
7884
|
|
|
8077
|
-
// src/context.ts
|
|
8078
|
-
function checkBrowserScope(scope, ctx) {
|
|
8079
|
-
switch (scope) {
|
|
8080
|
-
case "project":
|
|
8081
|
-
return null;
|
|
8082
|
-
case "browser":
|
|
8083
|
-
return ctx.browser ? null : "\u9700\u8981\u6D4F\u89C8\u5668\u5B9E\u4F8B\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
8084
|
-
case "page":
|
|
8085
|
-
return ctx.page ? null : "\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
8086
|
-
case "element":
|
|
8087
|
-
return ctx.page ? null : "\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879";
|
|
8088
|
-
default:
|
|
8089
|
-
return null;
|
|
8090
|
-
}
|
|
8091
|
-
}
|
|
8092
|
-
function assertPageScope(ctx) {
|
|
8093
|
-
if (!ctx.page) {
|
|
8094
|
-
throw new Error("\u9700\u8981\u6D3B\u8DC3\u7684\u9875\u9762\uFF0C\u8BF7\u4F7F\u7528 --session \u9009\u9879");
|
|
8095
|
-
}
|
|
8096
|
-
}
|
|
8097
|
-
var wsServerCache = /* @__PURE__ */ new WeakMap();
|
|
8098
|
-
function attachWaitForHuman(ctx, getOrCreateWSServer) {
|
|
8099
|
-
ctx.waitForHuman = async (options) => {
|
|
8100
|
-
if (!ctx.page) {
|
|
8101
|
-
throw new Error("waitForHuman requires an active page");
|
|
8102
|
-
}
|
|
8103
|
-
const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-2DZK4MW7.js");
|
|
8104
|
-
const wsServer2 = await getOrCreateWSServer(ctx.browserContext);
|
|
8105
|
-
const manager = new HumanInteractionManager2(wsServer2, ctx.page);
|
|
8106
|
-
return manager.waitForHuman(options);
|
|
8107
|
-
};
|
|
8108
|
-
}
|
|
8109
|
-
function getWSServerFromCache(browserContext) {
|
|
8110
|
-
return wsServerCache.get(browserContext);
|
|
8111
|
-
}
|
|
8112
|
-
function setWSServerCache(browserContext, server) {
|
|
8113
|
-
wsServerCache.set(browserContext, server);
|
|
8114
|
-
}
|
|
8115
|
-
|
|
8116
7885
|
// src/scope.ts
|
|
8117
7886
|
var BROWSER_SCOPE = {
|
|
8118
7887
|
name: "browser",
|
|
@@ -13657,6 +13426,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13657
13426
|
},
|
|
13658
13427
|
tips: new TipCollector3()
|
|
13659
13428
|
};
|
|
13429
|
+
attachDetectAntiBot(ctx);
|
|
13660
13430
|
try {
|
|
13661
13431
|
const cmdStart = Date.now();
|
|
13662
13432
|
const loginGuard = await checkPluginLoginRequired({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
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": {
|