@xbrowser/cli 1.9.2 → 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.
@@ -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/anti-bot-detection.ts
5819
- var CAPTCHA_PATTERNS = [
5820
- { name: "reCAPTCHA", pattern: /recaptcha/i },
5821
- { name: "hCaptcha", pattern: /hcaptcha/i },
5822
- { name: "Turnstile", pattern: /turnstile/i },
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) {