@xbrowser/cli 1.14.0 → 1.16.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/cli.js CHANGED
@@ -26,6 +26,16 @@ import {
26
26
  import {
27
27
  buildViewerUrl
28
28
  } from "./chunk-3OD76SUE.js";
29
+ import {
30
+ NPM_REGISTRY_URL,
31
+ NPM_SCOPE,
32
+ getConfigValue,
33
+ getMarketplaceUrl,
34
+ loadConfig,
35
+ resolveNpmPackageWithFallback,
36
+ resolveScreenshotsDir,
37
+ setConfigValue
38
+ } from "./chunk-ZTHE5RBZ.js";
29
39
  import {
30
40
  getDaemonConfig,
31
41
  getDaemonProcessStatus,
@@ -56,10 +66,13 @@ import {
56
66
  getAllSessions,
57
67
  getBrowser,
58
68
  getSessionById,
69
+ rand,
59
70
  resolveLaunchOpts,
60
71
  saveSessionDiskMeta,
61
- setActivePage
62
- } from "./chunk-BTFKJD7Z.js";
72
+ setActivePage,
73
+ sleep,
74
+ wheelDelta
75
+ } from "./chunk-5EYOEB4R.js";
63
76
  import "./chunk-TNEN6VQ2.js";
64
77
  import {
65
78
  errMsg
@@ -68,16 +81,6 @@ import {
68
81
  detectAntiBot,
69
82
  formatDetectionMessage
70
83
  } from "./chunk-JKVUFP3G.js";
71
- import {
72
- NPM_REGISTRY_URL,
73
- NPM_SCOPE,
74
- getConfigValue,
75
- getMarketplaceUrl,
76
- loadConfig,
77
- resolveNpmPackageWithFallback,
78
- resolveScreenshotsDir,
79
- setConfigValue
80
- } from "./chunk-ZTHE5RBZ.js";
81
84
  import "./chunk-KFQGP6VL.js";
82
85
 
83
86
  // src/router.ts
@@ -352,7 +355,8 @@ var gotoCommand = registerCommand({
352
355
  parameters: z.object({
353
356
  url: z.string(),
354
357
  waitUntil: z.enum(["load", "domcontentloaded", "networkidle", "commit"]).optional(),
355
- timeout: z.number().optional()
358
+ timeout: z.number().optional(),
359
+ referrer: z.string().optional()
356
360
  }),
357
361
  result: z.object({
358
362
  url: z.string(),
@@ -373,7 +377,9 @@ var gotoCommand = registerCommand({
373
377
  try {
374
378
  response = await ctx.page.goto(url, {
375
379
  waitUntil: p.waitUntil || "domcontentloaded",
376
- ...p.timeout ? { timeout: p.timeout } : {}
380
+ ...p.timeout ? { timeout: p.timeout } : {},
381
+ // referrer(d54):模拟从源页面点链接进入 —— document.referrer 非空
382
+ ...p.referrer ? { referer: p.referrer } : {}
377
383
  });
378
384
  } catch (err) {
379
385
  const msg = err instanceof Error ? err.message : String(err);
@@ -900,21 +906,34 @@ var scrollCommand = registerCommand({
900
906
  }),
901
907
  handler: async (p, ctx) => {
902
908
  const distance = p.distance ?? 500;
903
- const deltas = {
904
- down: [0, distance],
905
- up: [0, -distance],
906
- right: [distance, 0],
907
- left: [-distance, 0]
908
- };
909
- const [dx, dy] = deltas[p.direction];
909
+ const sign = { down: 1, up: -1, right: 1, left: -1 };
910
+ const vertical = p.direction === "down" || p.direction === "up";
911
+ const s = sign[p.direction];
910
912
  if (p.selector) {
911
913
  const element = ctx.page.locator(p.selector).first();
912
914
  await element.evaluate((el, args) => {
913
915
  const [dxx, dyy] = args;
914
916
  el.scrollBy(dxx, dyy);
915
- }, [dx, dy]);
917
+ }, [vertical ? 0 : distance * s, vertical ? distance * s : 0]);
918
+ } else if (process.env.XBROWSER_STEALTH !== "off") {
919
+ let acc = 0;
920
+ let step = 0;
921
+ while (acc < distance && step < 60) {
922
+ const d = Math.min(wheelDelta(step), distance - acc);
923
+ if (d < 1) break;
924
+ await ctx.page.mouse.wheel(
925
+ vertical ? 0 : d * s,
926
+ vertical ? d * s : 0
927
+ );
928
+ acc += d;
929
+ step++;
930
+ await sleep(rand(16, 28));
931
+ }
916
932
  } else {
917
- await ctx.page.mouse.wheel(dx, dy);
933
+ await ctx.page.mouse.wheel(
934
+ vertical ? 0 : distance * s,
935
+ vertical ? distance * s : 0
936
+ );
918
937
  }
919
938
  return ok5({ direction: p.direction, distance });
920
939
  }
@@ -7523,7 +7542,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7523
7542
  }
7524
7543
  let targetPageOverride = null;
7525
7544
  if (_target && extraOpts?.cdpEndpoint) {
7526
- const { findTargetPage } = await import("./browser-DI6UQN6Q.js");
7545
+ const { findTargetPage } = await import("./browser-KT4FOWBO.js");
7527
7546
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7528
7547
  if (!targetPageOverride) {
7529
7548
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7737,7 +7756,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7737
7756
  const errorMessage = errMsg(err);
7738
7757
  if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
7739
7758
  try {
7740
- const { attemptRecovery } = await import("./recovery-FTZGV6VY.js");
7759
+ const { attemptRecovery } = await import("./recovery-NXC35EQN.js");
7741
7760
  const recovery = await attemptRecovery(
7742
7761
  session.page,
7743
7762
  sessionName,
@@ -7803,6 +7822,9 @@ async function executeChain(input, options) {
7803
7822
  for (const pipeline of pipelines) {
7804
7823
  const { type, pipeline: commands } = pipeline;
7805
7824
  for (const cmdStr of commands) {
7825
+ if (process.env.XBROWSER_CHAIN_PACE === "human" && results.length > 0) {
7826
+ await new Promise((r) => setTimeout(r, 800 + Math.random() * 1700));
7827
+ }
7806
7828
  const parts = splitCommand(cmdStr);
7807
7829
  if (parts.length === 0) continue;
7808
7830
  const cmdName = parts[0];
@@ -10173,12 +10195,33 @@ function normalizeSelector(input) {
10173
10195
 
10174
10196
  // src/cli/browser-routes.ts
10175
10197
  import { helpGenerator } from "@dyyz1993/xcli-core";
10198
+ function autoCompleteParams(cmdName, params, options) {
10199
+ try {
10200
+ const cmd = getCommand(cmdName);
10201
+ if (!cmd?.parameters) return params;
10202
+ const schema = asZodSchema(cmd.parameters);
10203
+ const shape = schema?.shape ?? schema?._def?.shape;
10204
+ if (!shape) return params;
10205
+ for (const key of Object.keys(shape)) {
10206
+ if (params[key] !== void 0) continue;
10207
+ const v = options[key];
10208
+ if (v === void 0 || v === true && key === "json" || key === "yaml") continue;
10209
+ if (typeof v === "string" && v === "") continue;
10210
+ params[key] = v;
10211
+ }
10212
+ return params;
10213
+ } catch {
10214
+ return params;
10215
+ }
10216
+ }
10176
10217
  function parseSelectorFlags(args, options) {
10177
- const selector = options.s || options.selector || options["selector"];
10178
- const value = options.v || options.value;
10218
+ const rawSelector = options.s ?? options.selector;
10219
+ const rawValue = options.v ?? options.value;
10220
+ const selector = typeof rawSelector === "string" ? normalizeSelector(rawSelector) : void 0;
10221
+ const value = typeof rawValue === "string" ? rawValue : void 0;
10179
10222
  const remaining = args.filter((a) => !a.startsWith("-"));
10180
10223
  return {
10181
- selector: selector ? normalizeSelector(selector) : void 0,
10224
+ selector,
10182
10225
  value,
10183
10226
  remaining
10184
10227
  };
@@ -10266,7 +10309,11 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10266
10309
  if (!sel || !txt)
10267
10310
  outputError("Usage: xbrowser type <selector> <text>\n xbrowser type -s <selector> -v <text>");
10268
10311
  cmdName = "type";
10269
- params = { selector: sel, text: txt };
10312
+ params = {
10313
+ selector: sel,
10314
+ text: txt,
10315
+ ...options.delay !== void 0 ? { delay: Number(options.delay) } : {}
10316
+ };
10270
10317
  break;
10271
10318
  }
10272
10319
  case "press": {
@@ -10408,7 +10455,13 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10408
10455
  outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
10409
10456
  }
10410
10457
  cmdName = "mouse";
10411
- params = { action, x, y, ...options.button ? { button: options.button } : {} };
10458
+ params = {
10459
+ action,
10460
+ x,
10461
+ y,
10462
+ ...options.button ? { button: options.button } : {},
10463
+ ...options.steps !== void 0 ? { steps: Number(options.steps) } : {}
10464
+ };
10412
10465
  break;
10413
10466
  }
10414
10467
  case "html":
@@ -10610,6 +10663,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10610
10663
  if (target) {
10611
10664
  params = { ...params, _target: target };
10612
10665
  }
10666
+ params = autoCompleteParams(cmdName, params, options);
10613
10667
  const tabIndex = options.tab;
10614
10668
  if (tabIndex !== void 0) {
10615
10669
  params = { ...params, _tabIndex: Number(tabIndex) };
@@ -13663,7 +13717,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13663
13717
  const targetPage = pages[cmdTabIndex];
13664
13718
  await targetPage.bringToFront().catch(() => {
13665
13719
  });
13666
- const { setActivePage: setActivePage2 } = await import("./browser-DI6UQN6Q.js");
13720
+ const { setActivePage: setActivePage2 } = await import("./browser-KT4FOWBO.js");
13667
13721
  setActivePage2(session, targetPage);
13668
13722
  }
13669
13723
  }
@@ -13939,7 +13993,7 @@ async function main() {
13939
13993
  const command = process.argv[2];
13940
13994
  const isLongRunning = command === "preview" || command === "serve";
13941
13995
  if (!isLongRunning) {
13942
- const { ensureProcessCanExit } = await import("./browser-DI6UQN6Q.js");
13996
+ const { ensureProcessCanExit } = await import("./browser-KT4FOWBO.js");
13943
13997
  await ensureProcessCanExit().catch(() => {
13944
13998
  });
13945
13999
  process.exit(process.exitCode || exitCode);
@@ -0,0 +1,58 @@
1
+ import "./chunk-KFQGP6VL.js";
2
+
3
+ // src/utils/clipboard.ts
4
+ import { exec, execSync } from "child_process";
5
+ function writeClipboard(text) {
6
+ const plat = process.platform;
7
+ if (plat === "darwin") {
8
+ const p = exec("pbcopy");
9
+ if (!p.stdin) throw new Error("pbcopy stdin unavailable");
10
+ p.stdin.write(text);
11
+ p.stdin.end();
12
+ execSync("sleep 0.05");
13
+ } else if (plat === "linux") {
14
+ execSync("command -v xclip >/dev/null 2>&1 && echo ok", { stdio: "ignore" });
15
+ const p = exec("xclip -selection clipboard -in");
16
+ if (!p.stdin) throw new Error("xclip stdin unavailable");
17
+ p.stdin.write(text);
18
+ p.stdin.end();
19
+ execSync("sleep 0.05");
20
+ } else if (plat === "win32") {
21
+ const p = exec("clip");
22
+ if (!p.stdin) throw new Error("clip stdin unavailable");
23
+ p.stdin.write(text);
24
+ p.stdin.end();
25
+ execSync("timeout /t 1 /nobreak >nul");
26
+ } else {
27
+ throw new Error(`Unsupported platform for clipboard: ${plat}`);
28
+ }
29
+ }
30
+ async function pasteViaClipboard(page, text) {
31
+ writeClipboard(text);
32
+ const kb = page.keyboard;
33
+ const mod = process.platform === "darwin" ? "Meta" : "Control";
34
+ await kb.pressCombo("v", mod);
35
+ await new Promise((r) => setTimeout(r, 150));
36
+ }
37
+ async function syntheticPaste(page, selector, text) {
38
+ const result = await page.evaluate(`
39
+ (function() {
40
+ const el = ${"{SELECTOR}"};
41
+ if (!el) return false;
42
+ el.focus();
43
+ if (el.value) { el.select(); document.execCommand('delete'); }
44
+ try {
45
+ const dt = new DataTransfer();
46
+ dt.setData('text/plain', ${JSON.stringify(text)});
47
+ el.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }));
48
+ } catch (e) { /* ClipboardEvent ctor guard */ }
49
+ const ok = document.execCommand('insertText', false, ${JSON.stringify(text)});
50
+ return ok === true && (el.value || '') === ${JSON.stringify(text)};
51
+ })()
52
+ `.replace("{SELECTOR}", selector));
53
+ return result === true;
54
+ }
55
+ export {
56
+ pasteViaClipboard,
57
+ syntheticPaste
58
+ };
@@ -0,0 +1,58 @@
1
+ import "./chunk-3RG5ZIWI.js";
2
+
3
+ // src/utils/clipboard.ts
4
+ import { exec, execSync } from "child_process";
5
+ function writeClipboard(text) {
6
+ const plat = process.platform;
7
+ if (plat === "darwin") {
8
+ const p = exec("pbcopy");
9
+ if (!p.stdin) throw new Error("pbcopy stdin unavailable");
10
+ p.stdin.write(text);
11
+ p.stdin.end();
12
+ execSync("sleep 0.05");
13
+ } else if (plat === "linux") {
14
+ execSync("command -v xclip >/dev/null 2>&1 && echo ok", { stdio: "ignore" });
15
+ const p = exec("xclip -selection clipboard -in");
16
+ if (!p.stdin) throw new Error("xclip stdin unavailable");
17
+ p.stdin.write(text);
18
+ p.stdin.end();
19
+ execSync("sleep 0.05");
20
+ } else if (plat === "win32") {
21
+ const p = exec("clip");
22
+ if (!p.stdin) throw new Error("clip stdin unavailable");
23
+ p.stdin.write(text);
24
+ p.stdin.end();
25
+ execSync("timeout /t 1 /nobreak >nul");
26
+ } else {
27
+ throw new Error(`Unsupported platform for clipboard: ${plat}`);
28
+ }
29
+ }
30
+ async function pasteViaClipboard(page, text) {
31
+ writeClipboard(text);
32
+ const kb = page.keyboard;
33
+ const mod = process.platform === "darwin" ? "Meta" : "Control";
34
+ await kb.pressCombo("v", mod);
35
+ await new Promise((r) => setTimeout(r, 150));
36
+ }
37
+ async function syntheticPaste(page, selector, text) {
38
+ const result = await page.evaluate(`
39
+ (function() {
40
+ const el = ${"{SELECTOR}"};
41
+ if (!el) return false;
42
+ el.focus();
43
+ if (el.value) { el.select(); document.execCommand('delete'); }
44
+ try {
45
+ const dt = new DataTransfer();
46
+ dt.setData('text/plain', ${JSON.stringify(text)});
47
+ el.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }));
48
+ } catch (e) { /* ClipboardEvent ctor guard */ }
49
+ const ok = document.execCommand('insertText', false, ${JSON.stringify(text)});
50
+ return ok === true && (el.value || '') === ${JSON.stringify(text)};
51
+ })()
52
+ `.replace("{SELECTOR}", selector));
53
+ return result === true;
54
+ }
55
+ export {
56
+ pasteViaClipboard,
57
+ syntheticPaste
58
+ };
@@ -7,6 +7,10 @@ import {
7
7
  import {
8
8
  buildViewerUrl
9
9
  } from "./chunk-TZPKFUBT.js";
10
+ import {
11
+ ScreencastCapturer,
12
+ resolveScreenshotsDir
13
+ } from "./chunk-HBMEFSTB.js";
10
14
  import "./chunk-CG3D3CHY.js";
11
15
  import {
12
16
  commandLogStore,
@@ -30,10 +34,13 @@ import {
30
34
  resolveLaunchOpts,
31
35
  saveSessionDiskMeta,
32
36
  setActivePage
33
- } from "./chunk-ZD4OSYOX.js";
37
+ } from "./chunk-GWQ5NTVE.js";
34
38
  import {
35
- createRuleEngine
36
- } from "./chunk-UPAWITVM.js";
39
+ createRuleEngine,
40
+ rand,
41
+ sleep,
42
+ wheelDelta
43
+ } from "./chunk-LTBNQERK.js";
37
44
  import {
38
45
  queryJS
39
46
  } from "./chunk-3FWLW7FS.js";
@@ -45,10 +52,6 @@ import {
45
52
  detectAntiBot,
46
53
  formatDetectionMessage
47
54
  } from "./chunk-JKVUFP3G.js";
48
- import {
49
- ScreencastCapturer,
50
- resolveScreenshotsDir
51
- } from "./chunk-HBMEFSTB.js";
52
55
  import "./chunk-KFQGP6VL.js";
53
56
 
54
57
  // src/daemon/daemon-main.ts
@@ -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-AOVQTGJ7.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.`);
@@ -7255,7 +7274,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7255
7274
  const errorMessage = errMsg(err);
7256
7275
  if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
7257
7276
  try {
7258
- const { attemptRecovery } = await import("./recovery-Z3RDZENA.js");
7277
+ const { attemptRecovery } = await import("./recovery-MDWAVXE4.js");
7259
7278
  const recovery = await attemptRecovery(
7260
7279
  session.page,
7261
7280
  sessionName,
@@ -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];
@@ -8292,7 +8314,7 @@ function createRPCHandler() {
8292
8314
  return result;
8293
8315
  } catch (err) {
8294
8316
  const errorMessage = errMsg(err);
8295
- const { attemptRecovery } = await import("./recovery-Z3RDZENA.js");
8317
+ const { attemptRecovery } = await import("./recovery-MDWAVXE4.js");
8296
8318
  const recovery = await attemptRecovery(
8297
8319
  session?.page,
8298
8320
  sessionName,
@@ -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-MJH5W7BB.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,10 @@ 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>;
321
+ /** Shortcut combo (modifier+key) with explicit per-event modifiers bitmask */
322
+ pressCombo(key: string, modifier: 'Meta' | 'Control' | 'Alt' | 'Shift'): Promise<void>;
319
323
  down(key: string): Promise<void>;
320
324
  up(key: string): Promise<void>;
321
325
  type(text: string, opts?: {