@xbrowser/cli 1.14.0 → 1.15.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.
@@ -30,10 +30,13 @@ import {
30
30
  resolveLaunchOpts,
31
31
  saveSessionDiskMeta,
32
32
  setActivePage
33
- } from "./chunk-ZD4OSYOX.js";
33
+ } from "./chunk-XQ4KOZ37.js";
34
34
  import {
35
- createRuleEngine
36
- } from "./chunk-UPAWITVM.js";
35
+ createRuleEngine,
36
+ rand,
37
+ sleep,
38
+ wheelDelta
39
+ } from "./chunk-UXDGEDT7.js";
37
40
  import {
38
41
  queryJS
39
42
  } from "./chunk-3FWLW7FS.js";
@@ -312,7 +315,8 @@ var gotoCommand = registerCommand({
312
315
  parameters: z.object({
313
316
  url: z.string(),
314
317
  waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
315
- timeout: z.number().optional()
318
+ timeout: z.number().optional(),
319
+ referrer: z.string().optional()
316
320
  }),
317
321
  result: z.object({
318
322
  url: z.string(),
@@ -333,7 +337,9 @@ var gotoCommand = registerCommand({
333
337
  try {
334
338
  response = await ctx.page.goto(url, {
335
339
  waitUntil: p.waitUntil || "domcontentloaded",
336
- ...p.timeout ? { timeout: p.timeout } : {}
340
+ ...p.timeout ? { timeout: p.timeout } : {},
341
+ // referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
342
+ ...p.referrer ? { referer: p.referrer } : {}
337
343
  });
338
344
  } catch (err) {
339
345
  const msg = err instanceof Error ? err.message : String(err);
@@ -860,21 +866,34 @@ var scrollCommand = registerCommand({
860
866
  }),
861
867
  handler: async (p, ctx) => {
862
868
  const distance = p.distance ?? 500;
863
- const deltas = {
864
- down: [0, distance],
865
- up: [0, -distance],
866
- right: [distance, 0],
867
- left: [-distance, 0]
868
- };
869
- const [dx, dy] = deltas[p.direction];
869
+ const sign = { down: 1, up: -1, right: 1, left: -1 };
870
+ const vertical = p.direction === "down" || p.direction === "up";
871
+ const s = sign[p.direction];
870
872
  if (p.selector) {
871
873
  const element = ctx.page.locator(p.selector).first();
872
874
  await element.evaluate((el, args) => {
873
875
  const [dxx, dyy] = args;
874
876
  el.scrollBy(dxx, dyy);
875
- }, [dx, dy]);
877
+ }, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
878
+ } else if (process.env.XBROWSER_STEALTH !== "off") {
879
+ let acc = 0;
880
+ let step = 0;
881
+ while (acc < distance && step < 60) {
882
+ const d = Math.min(wheelDelta(step), distance - acc);
883
+ if (d < 1) break;
884
+ await ctx.page.mouse.wheel(
885
+ vertical ? 0 : d * s,
886
+ vertical ? d * s : 0
887
+ );
888
+ acc += d;
889
+ step++;
890
+ await sleep(rand(16, 28));
891
+ }
876
892
  } else {
877
- await ctx.page.mouse.wheel(dx, dy);
893
+ await ctx.page.mouse.wheel(
894
+ vertical ? 0 : distance * s,
895
+ vertical ? distance * s : 0
896
+ );
878
897
  }
879
898
  return ok5({ direction: p.direction, distance });
880
899
  }
@@ -7041,7 +7060,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7041
7060
  }
7042
7061
  let targetPageOverride = null;
7043
7062
  if (_target && extraOpts?.cdpEndpoint) {
7044
- const { findTargetPage } = await import("./browser-5A6AL62M.js");
7063
+ const { findTargetPage } = await import("./browser-YYMKZWTU.js");
7045
7064
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7046
7065
  if (!targetPageOverride) {
7047
7066
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7321,6 +7340,9 @@ async function executeChain(input, options) {
7321
7340
  for (const pipeline of pipelines) {
7322
7341
  const { type, pipeline: commands } = pipeline;
7323
7342
  for (const cmdStr of commands) {
7343
+ if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
7344
+ await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
7345
+ }
7324
7346
  const parts = splitCommand(cmdStr);
7325
7347
  if (parts.length === 0) continue;
7326
7348
  const cmdName = parts[0];
@@ -8857,7 +8879,7 @@ function createRPCHandler() {
8857
8879
  if (isNewFormat) {
8858
8880
  try {
8859
8881
  const replayErrors = [];
8860
- const { SessionReplayer } = await import("./session-replayer-I3PJFO3H.js");
8882
+ const { SessionReplayer } = await import("./session-replayer-DKHXF3DZ.js");
8861
8883
  const replayer = new SessionReplayer({
8862
8884
  page: session.page,
8863
8885
  stepDelay: slowMo * 500,
package/dist/index.d.ts CHANGED
@@ -316,6 +316,8 @@ interface XBKeyboard {
316
316
  press(key: string, opts?: {
317
317
  delay?: number;
318
318
  }): Promise<void>;
319
+ /** Navigation key with CDP 'keyDown' type (carries browser default actions) */
320
+ pressNav(key: string): Promise<void>;
319
321
  down(key: string): Promise<void>;
320
322
  up(key: string): Promise<void>;
321
323
  type(text: string, opts?: {
package/dist/index.js CHANGED
@@ -92,8 +92,12 @@ import {
92
92
  resolveLaunchOpts,
93
93
  saveSessionDiskMeta,
94
94
  setActivePage
95
- } from "./chunk-B753M33E.js";
96
- import "./chunk-PYQGDBAF.js";
95
+ } from "./chunk-K5E4CWRV.js";
96
+ import {
97
+ rand,
98
+ sleep,
99
+ wheelDelta
100
+ } from "./chunk-NTTFKS6T.js";
97
101
  import "./chunk-TNEN6VQ2.js";
98
102
  import {
99
103
  errMsg
@@ -385,7 +389,8 @@ var gotoCommand = registerCommand({
385
389
  parameters: z.object({
386
390
  url: z.string(),
387
391
  waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
388
- timeout: z.number().optional()
392
+ timeout: z.number().optional(),
393
+ referrer: z.string().optional()
389
394
  }),
390
395
  result: z.object({
391
396
  url: z.string(),
@@ -406,7 +411,9 @@ var gotoCommand = registerCommand({
406
411
  try {
407
412
  response = await ctx.page.goto(url, {
408
413
  waitUntil: p.waitUntil || "domcontentloaded",
409
- ...p.timeout ? { timeout: p.timeout } : {}
414
+ ...p.timeout ? { timeout: p.timeout } : {},
415
+ // referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
416
+ ...p.referrer ? { referer: p.referrer } : {}
410
417
  });
411
418
  } catch (err) {
412
419
  const msg = err instanceof Error ? err.message : String(err);
@@ -933,21 +940,34 @@ var scrollCommand = registerCommand({
933
940
  }),
934
941
  handler: async (p, ctx) => {
935
942
  const distance = p.distance ?? 500;
936
- const deltas = {
937
- down: [0, distance],
938
- up: [0, -distance],
939
- right: [distance, 0],
940
- left: [-distance, 0]
941
- };
942
- const [dx, dy] = deltas[p.direction];
943
+ const sign = { down: 1, up: -1, right: 1, left: -1 };
944
+ const vertical = p.direction === "down" || p.direction === "up";
945
+ const s = sign[p.direction];
943
946
  if (p.selector) {
944
947
  const element = ctx.page.locator(p.selector).first();
945
948
  await element.evaluate((el, args) => {
946
949
  const [dxx, dyy] = args;
947
950
  el.scrollBy(dxx, dyy);
948
- }, [dx, dy]);
951
+ }, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
952
+ } else if (process.env.XBROWSER_STEALTH !== "off") {
953
+ let acc = 0;
954
+ let step = 0;
955
+ while (acc < distance && step < 60) {
956
+ const d = Math.min(wheelDelta(step), distance - acc);
957
+ if (d < 1) break;
958
+ await ctx.page.mouse.wheel(
959
+ vertical ? 0 : d * s,
960
+ vertical ? d * s : 0
961
+ );
962
+ acc += d;
963
+ step++;
964
+ await sleep(rand(16, 28));
965
+ }
949
966
  } else {
950
- await ctx.page.mouse.wheel(dx, dy);
967
+ await ctx.page.mouse.wheel(
968
+ vertical ? 0 : distance * s,
969
+ vertical ? distance * s : 0
970
+ );
951
971
  }
952
972
  return ok5({ direction: p.direction, distance });
953
973
  }
@@ -7873,7 +7893,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7873
7893
  }
7874
7894
  let targetPageOverride = null;
7875
7895
  if (_target && extraOpts?.cdpEndpoint) {
7876
- const { findTargetPage } = await import("./browser-KJWBHGHX.js");
7896
+ const { findTargetPage } = await import("./browser-4FKHBUBJ.js");
7877
7897
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7878
7898
  if (!targetPageOverride) {
7879
7899
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8153,6 +8173,9 @@ async function executeChain(input, options) {
8153
8173
  for (const pipeline of pipelines) {
8154
8174
  const { type, pipeline: commands } = pipeline;
8155
8175
  for (const cmdStr of commands) {
8176
+ if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
8177
+ await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
8178
+ }
8156
8179
  const parts = splitCommand(cmdStr);
8157
8180
  if (parts.length === 0) continue;
8158
8181
  const cmdName = parts[0];
@@ -10561,12 +10584,33 @@ function normalizeSelector(input) {
10561
10584
 
10562
10585
  // src/cli/browser-routes.ts
10563
10586
  import { helpGenerator } from "@dyyz1993/xcli-core";
10587
+ function autoCompleteParams(cmdName, params, options) {
10588
+ try {
10589
+ const cmd = getCommand(cmdName);
10590
+ if (!cmd?.parameters) return params;
10591
+ const schema = asZodSchema(cmd.parameters);
10592
+ const shape = schema?.shape ?? schema?._def?.shape;
10593
+ if (!shape) return params;
10594
+ for (const key of Object.keys(shape)) {
10595
+ if (params[key] !== void 0) continue;
10596
+ const v = options[key];
10597
+ if (v === void 0 || v === true && key === "json" || key === "yaml") continue;
10598
+ if (typeof v === "string" && v === "") continue;
10599
+ params[key] = v;
10600
+ }
10601
+ return params;
10602
+ } catch {
10603
+ return params;
10604
+ }
10605
+ }
10564
10606
  function parseSelectorFlags(args, options) {
10565
- const selector = options.s || options.selector || options["selector"];
10566
- const value = options.v || options.value;
10607
+ const rawSelector = options.s ?? options.selector;
10608
+ const rawValue = options.v ?? options.value;
10609
+ const selector = typeof rawSelector === "string" ? normalizeSelector(rawSelector) : void 0;
10610
+ const value = typeof rawValue === "string" ? rawValue : void 0;
10567
10611
  const remaining = args.filter((a) => !a.startsWith("-"));
10568
10612
  return {
10569
- selector: selector ? normalizeSelector(selector) : void 0,
10613
+ selector,
10570
10614
  value,
10571
10615
  remaining
10572
10616
  };
@@ -10654,7 +10698,11 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10654
10698
  if (!sel || !txt)
10655
10699
  outputError("Usage: xbrowser type <selector> <text>\n xbrowser type -s <selector> -v <text>");
10656
10700
  cmdName = "type";
10657
- params = { selector: sel, text: txt };
10701
+ params = {
10702
+ selector: sel,
10703
+ text: txt,
10704
+ ...options.delay !== void 0 ? { delay: Number(options.delay) } : {}
10705
+ };
10658
10706
  break;
10659
10707
  }
10660
10708
  case "press": {
@@ -10796,7 +10844,13 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10796
10844
  outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
10797
10845
  }
10798
10846
  cmdName = "mouse";
10799
- params = { action, x, y, ...options.button ? { button: options.button } : {} };
10847
+ params = {
10848
+ action,
10849
+ x,
10850
+ y,
10851
+ ...options.button ? { button: options.button } : {},
10852
+ ...options.steps !== void 0 ? { steps: Number(options.steps) } : {}
10853
+ };
10800
10854
  break;
10801
10855
  }
10802
10856
  case "html":
@@ -10998,6 +11052,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10998
11052
  if (target) {
10999
11053
  params = { ...params, _target: target };
11000
11054
  }
11055
+ params = autoCompleteParams(cmdName, params, options);
11001
11056
  const tabIndex = options.tab;
11002
11057
  if (tabIndex !== void 0) {
11003
11058
  params = { ...params, _tabIndex: Number(tabIndex) };
@@ -14051,7 +14106,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
14051
14106
  const targetPage = pages[cmdTabIndex];
14052
14107
  await targetPage.bringToFront().catch(() => {
14053
14108
  });
14054
- const { setActivePage: setActivePage2 } = await import("./browser-KJWBHGHX.js");
14109
+ const { setActivePage: setActivePage2 } = await import("./browser-4FKHBUBJ.js");
14055
14110
  setActivePage2(session, targetPage);
14056
14111
  }
14057
14112
  }
@@ -17224,7 +17279,7 @@ var DataCollector = class {
17224
17279
  return results;
17225
17280
  }
17226
17281
  async createBrowserContext() {
17227
- const { launch } = await import("./cdp-driver-AHKUT57K.js");
17282
+ const { launch } = await import("./cdp-driver-QX72T7SU.js");
17228
17283
  const { browser } = await launch({
17229
17284
  headless: true,
17230
17285
  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-4SFS6GNY.js");
37
+ const { launch } = await import("./cdp-driver-7RW4NY2X.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.14.0",
3
+ "version": "1.15.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": {