@xbrowser/cli 1.0.4 → 1.0.6

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.
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-LRBSUKUZ.js";
24
- import "./chunk-N2JFPWMI.js";
23
+ } from "./chunk-MXG2H3HJ.js";
24
+ import "./chunk-SEFIJY2M.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-KFQGP6VL.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-DKWR54XQ.js";
23
+ } from "./chunk-7POCCXIB.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-TWWOIJM7.js";
24
- import "./chunk-N2JFPWMI.js";
23
+ } from "./chunk-5QAYN5EZ.js";
24
+ import "./chunk-SEFIJY2M.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-ABXMBNQ6.js";
@@ -2179,8 +2179,14 @@ var XBContextImpl = class {
2179
2179
  // ── Cookies ─────────────────────────────────────────────────
2180
2180
  async cookies(urls) {
2181
2181
  const urlList = typeof urls === "string" ? [urls] : urls;
2182
- const result = await this.conn.send("Network.getCookies", urlList ? { urls: urlList } : void 0);
2183
- return result.cookies;
2182
+ const params = urlList ? { urls: urlList } : void 0;
2183
+ try {
2184
+ const result = await this.conn.send("Storage.getCookies", params);
2185
+ return result.cookies;
2186
+ } catch {
2187
+ const result = await this.conn.send("Network.getCookies", params);
2188
+ return result.cookies;
2189
+ }
2184
2190
  }
2185
2191
  async addCookies(cookies) {
2186
2192
  const cdpCookies = cookies.map((c) => ({
@@ -2193,10 +2199,18 @@ var XBContextImpl = class {
2193
2199
  secure: c.secure,
2194
2200
  sameSite: c.sameSite
2195
2201
  }));
2196
- await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2202
+ try {
2203
+ await this.conn.send("Storage.setCookies", { cookies: cdpCookies });
2204
+ } catch {
2205
+ await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2206
+ }
2197
2207
  }
2198
2208
  async clearCookies() {
2199
- await this.conn.send("Network.clearBrowserCookies");
2209
+ try {
2210
+ await this.conn.send("Storage.clearCookies");
2211
+ } catch {
2212
+ await this.conn.send("Network.clearBrowserCookies");
2213
+ }
2200
2214
  }
2201
2215
  on(event, handler) {
2202
2216
  this._emitter.on(event, handler);
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-N2JFPWMI.js";
17
+ } from "./chunk-SEFIJY2M.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-N2JFPWMI.js";
3
+ } from "./chunk-SEFIJY2M.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -36,6 +36,15 @@ async function fetchNoProxy(url) {
36
36
  async function resolveCDPEndpoint(raw) {
37
37
  if (raw === "auto") {
38
38
  const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
39
+ if (!httpResp.ok) {
40
+ throw new Error(
41
+ `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
42
+ \u89E3\u51B3\u65B9\u6CD5\uFF1A
43
+ 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
44
+ 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
45
+ 3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
46
+ );
47
+ }
39
48
  const data = await httpResp.json();
40
49
  if (!data.webSocketDebuggerUrl) {
41
50
  throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
@@ -2184,8 +2184,14 @@ var XBContextImpl = class {
2184
2184
  // ── Cookies ─────────────────────────────────────────────────
2185
2185
  async cookies(urls) {
2186
2186
  const urlList = typeof urls === "string" ? [urls] : urls;
2187
- const result = await this.conn.send("Network.getCookies", urlList ? { urls: urlList } : void 0);
2188
- return result.cookies;
2187
+ const params = urlList ? { urls: urlList } : void 0;
2188
+ try {
2189
+ const result = await this.conn.send("Storage.getCookies", params);
2190
+ return result.cookies;
2191
+ } catch {
2192
+ const result = await this.conn.send("Network.getCookies", params);
2193
+ return result.cookies;
2194
+ }
2189
2195
  }
2190
2196
  async addCookies(cookies) {
2191
2197
  const cdpCookies = cookies.map((c) => ({
@@ -2198,10 +2204,18 @@ var XBContextImpl = class {
2198
2204
  secure: c.secure,
2199
2205
  sameSite: c.sameSite
2200
2206
  }));
2201
- await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2207
+ try {
2208
+ await this.conn.send("Storage.setCookies", { cookies: cdpCookies });
2209
+ } catch {
2210
+ await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2211
+ }
2202
2212
  }
2203
2213
  async clearCookies() {
2204
- await this.conn.send("Network.clearBrowserCookies");
2214
+ try {
2215
+ await this.conn.send("Storage.clearCookies");
2216
+ } catch {
2217
+ await this.conn.send("Network.clearBrowserCookies");
2218
+ }
2205
2219
  }
2206
2220
  on(event, handler) {
2207
2221
  this._emitter.on(event, handler);
@@ -4129,6 +4143,15 @@ async function fetchNoProxy(url) {
4129
4143
  async function resolveCDPEndpoint(raw) {
4130
4144
  if (raw === "auto") {
4131
4145
  const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
4146
+ if (!httpResp.ok) {
4147
+ throw new Error(
4148
+ `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
4149
+ \u89E3\u51B3\u65B9\u6CD5\uFF1A
4150
+ 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
4151
+ 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
4152
+ 3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
4153
+ );
4154
+ }
4132
4155
  const data = await httpResp.json();
4133
4156
  if (!data.webSocketDebuggerUrl) {
4134
4157
  throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-N2JFPWMI.js";
3
+ } from "./chunk-SEFIJY2M.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -1401,6 +1401,15 @@ async function fetchNoProxy(url) {
1401
1401
  async function resolveCDPEndpoint(raw) {
1402
1402
  if (raw === "auto") {
1403
1403
  const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
1404
+ if (!httpResp.ok) {
1405
+ throw new Error(
1406
+ `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
1407
+ \u89E3\u51B3\u65B9\u6CD5\uFF1A
1408
+ 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
1409
+ 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
1410
+ 3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
1411
+ );
1412
+ }
1404
1413
  const data = await httpResp.json();
1405
1414
  if (!data.webSocketDebuggerUrl) {
1406
1415
  throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
@@ -2178,8 +2178,14 @@ var XBContextImpl = class {
2178
2178
  // ── Cookies ─────────────────────────────────────────────────
2179
2179
  async cookies(urls) {
2180
2180
  const urlList = typeof urls === "string" ? [urls] : urls;
2181
- const result = await this.conn.send("Network.getCookies", urlList ? { urls: urlList } : void 0);
2182
- return result.cookies;
2181
+ const params = urlList ? { urls: urlList } : void 0;
2182
+ try {
2183
+ const result = await this.conn.send("Storage.getCookies", params);
2184
+ return result.cookies;
2185
+ } catch {
2186
+ const result = await this.conn.send("Network.getCookies", params);
2187
+ return result.cookies;
2188
+ }
2183
2189
  }
2184
2190
  async addCookies(cookies) {
2185
2191
  const cdpCookies = cookies.map((c) => ({
@@ -2192,10 +2198,18 @@ var XBContextImpl = class {
2192
2198
  secure: c.secure,
2193
2199
  sameSite: c.sameSite
2194
2200
  }));
2195
- await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2201
+ try {
2202
+ await this.conn.send("Storage.setCookies", { cookies: cdpCookies });
2203
+ } catch {
2204
+ await this.conn.send("Network.setCookies", { cookies: cdpCookies });
2205
+ }
2196
2206
  }
2197
2207
  async clearCookies() {
2198
- await this.conn.send("Network.clearBrowserCookies");
2208
+ try {
2209
+ await this.conn.send("Storage.clearCookies");
2210
+ } catch {
2211
+ await this.conn.send("Network.clearBrowserCookies");
2212
+ }
2199
2213
  }
2200
2214
  on(event, handler) {
2201
2215
  this._emitter.on(event, handler);
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  resolveLaunchOpts,
26
26
  saveSessionDiskMeta,
27
27
  setActivePage
28
- } from "./chunk-DKWR54XQ.js";
28
+ } from "./chunk-7POCCXIB.js";
29
29
  import "./chunk-TNEN6VQ2.js";
30
30
  import {
31
31
  forwardCommandLog,
@@ -346,8 +346,13 @@ var gotoCommand = registerCommand({
346
346
  }),
347
347
  handler: async (p, ctx) => {
348
348
  let url = p.url;
349
- if (!/^https?:\/\//i.test(url) && !/^wss?:\/\//i.test(url)) {
350
- url = "https://" + url;
349
+ const hasScheme = /^(https?|wss?|file|about|data|chrome|blob):/i.test(url);
350
+ if (!hasScheme) {
351
+ if (/^[\w-]+(\.[\w-]+)+/.test(url) || url.startsWith("localhost")) {
352
+ url = "https://" + url;
353
+ } else {
354
+ throw new Error(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
355
+ }
351
356
  }
352
357
  const response = await ctx.page.goto(url, {
353
358
  waitUntil: p.waitUntil || "domcontentloaded"
@@ -402,7 +407,7 @@ var urlCommand = registerCommand({
402
407
  scope: "page",
403
408
  result: z.object({ url: z.string() }),
404
409
  handler: async (_p, ctx) => {
405
- return ok({ url: ctx.page.url() });
410
+ return ok({ url: ctx.page.url() || "about:blank" });
406
411
  }
407
412
  });
408
413
  registerCommand({
@@ -565,7 +570,8 @@ var fillCommand = registerCommand({
565
570
  }),
566
571
  handler: async (p, ctx) => {
567
572
  const page = ctx.page;
568
- if (p.clear) {
573
+ const shouldClear = p.clear !== false;
574
+ if (shouldClear) {
569
575
  await page.fill(p.selector, "", { force: true, timeout: 1e4 });
570
576
  }
571
577
  const isReact = await page.evaluate(() => {
@@ -591,7 +597,7 @@ var fillCommand = registerCommand({
591
597
  } else {
592
598
  await page.fill(p.selector, p.value, { force: true, timeout: 1e4 });
593
599
  }
594
- return ok2({ selector: p.selector, value: p.value, cleared: p.clear || false, reactMode: !!isReact });
600
+ return ok2({ selector: p.selector, value: p.value, cleared: shouldClear, reactMode: !!isReact });
595
601
  }
596
602
  });
597
603
  var typeCommand = registerCommand({
@@ -6960,7 +6966,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6960
6966
  }
6961
6967
  let targetPageOverride = null;
6962
6968
  if (_target && extraOpts?.cdpEndpoint) {
6963
- const { findTargetPage } = await import("./browser-5CTOA2WS.js");
6969
+ const { findTargetPage } = await import("./browser-U4VWPTS2.js");
6964
6970
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6965
6971
  if (!targetPageOverride) {
6966
6972
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9497,10 +9503,10 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9497
9503
  break;
9498
9504
  }
9499
9505
  case "press": {
9500
- const key = parsed.value || parsed.remaining[1];
9501
- if (!sel && !key) outputError("Usage: xbrowser press [selector] <key>");
9506
+ const key = parsed.value || (sel ? parsed.remaining[1] : parsed.remaining[0]);
9507
+ if (!key) outputError("Usage: xbrowser press [selector] <key>");
9502
9508
  cmdName = "press";
9503
- params = { ...sel ? { selector: sel } : {}, key: key || sel };
9509
+ params = { ...sel ? { selector: sel } : {}, key };
9504
9510
  break;
9505
9511
  }
9506
9512
  case "select": {
@@ -9569,13 +9575,22 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9569
9575
  params = { expression: args.join(" ") };
9570
9576
  break;
9571
9577
  case "scroll": {
9572
- const direction = args[0] || "down";
9573
- if (!["up", "down", "left", "right"].includes(direction))
9574
- outputError("Direction must be: up, down, left, right");
9578
+ let direction = "down";
9579
+ let distance;
9580
+ const firstArg = args[0];
9581
+ if (firstArg && ["up", "down", "left", "right"].includes(firstArg)) {
9582
+ direction = firstArg;
9583
+ if (args[1] && /^\d+$/.test(args[1])) distance = Number(args[1]);
9584
+ } else if (firstArg && /^\d+$/.test(firstArg)) {
9585
+ distance = Number(firstArg);
9586
+ } else if (firstArg) {
9587
+ outputError(`Invalid scroll argument: "${firstArg}". Use up/down/left/right or a pixel number.`);
9588
+ }
9589
+ distance = distance ?? (options.distance ? Number(options.distance) : options.amount ? Number(options.amount) : void 0);
9575
9590
  cmdName = "scroll";
9576
9591
  params = {
9577
9592
  direction,
9578
- distance: options.distance ? Number(options.distance) : options.amount ? Number(options.amount) : void 0,
9593
+ distance,
9579
9594
  selector: options.selector || options.s
9580
9595
  };
9581
9596
  break;
@@ -9588,6 +9603,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9588
9603
  cmdName = "url";
9589
9604
  params = {};
9590
9605
  break;
9606
+ case "set-viewport": {
9607
+ const width = options.width ? Number(options.width) : args[0] ? Number(args[0]) : void 0;
9608
+ const height = options.height ? Number(options.height) : args[1] ? Number(args[1]) : void 0;
9609
+ if (!width || !height) outputError("Usage: xbrowser set-viewport <width> <height>");
9610
+ cmdName = "set-viewport";
9611
+ params = { width, height };
9612
+ break;
9613
+ }
9591
9614
  case "html":
9592
9615
  cmdName = "html";
9593
9616
  params = { selector: options.selector || options.s };
@@ -9760,7 +9783,16 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9760
9783
  } else if (!result.success) {
9761
9784
  outputError(result.message || "Command failed");
9762
9785
  } else {
9763
- outputResult(result.data, mode);
9786
+ const data = result.data;
9787
+ const isEmptyResult = data && typeof data === "object" && Object.values(data).every((v) => v === "" || v === null || v === void 0);
9788
+ if (isEmptyResult) {
9789
+ const hint = cdpEndpoint ? `\u53EF\u80FD\u672A\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\u3002\u8BF7\u786E\u8BA4 ${cdpEndpoint} \u4E0A\u6709 Chrome \u8FD0\u884C\uFF08--remote-debugging-port\uFF09\u3002` : "\u53EF\u80FD\u672A\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\u3002\u8BF7\u4F7F\u7528 --cdp <endpoint> \u8FDE\u63A5\uFF0C\u6216\u5B89\u88C5 cdp-tunnel \u590D\u7528\u5DF2\u6709 Chrome\u3002";
9790
+ outputResult(result.data, mode);
9791
+ console.error(`
9792
+ \u26A0\uFE0F ${hint}`);
9793
+ } else {
9794
+ outputResult(result.data, mode);
9795
+ }
9764
9796
  }
9765
9797
  }
9766
9798
 
@@ -12046,7 +12078,8 @@ async function handleChainInput(input, argv) {
12046
12078
  }
12047
12079
  if (!chainResult.success) throw new Error("Command failed");
12048
12080
  }
12049
- async function routeCommand(argv, stdinCommands) {
12081
+ async function routeCommand(argvIn, stdinCommands) {
12082
+ let argv = argvIn;
12050
12083
  try {
12051
12084
  if (stdinCommands && stdinCommands.length > 0) {
12052
12085
  await handleStdinMode(stdinCommands, argv);
@@ -12060,6 +12093,13 @@ async function routeCommand(argv, stdinCommands) {
12060
12093
  await handleChainInput(argv[0], argv);
12061
12094
  return;
12062
12095
  }
12096
+ if (argv[0] && argv[0].includes(" ")) {
12097
+ const spaceIdx = argv[0].indexOf(" ");
12098
+ const possibleCmd = argv[0].substring(0, spaceIdx);
12099
+ if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12100
+ argv = [possibleCmd, argv[0].substring(spaceIdx + 1), ...argv.slice(1)];
12101
+ }
12102
+ }
12063
12103
  } catch (e) {
12064
12104
  outputError(e instanceof Error ? e.message : String(e));
12065
12105
  return;
@@ -12633,7 +12673,7 @@ async function main() {
12633
12673
  const command = process.argv[2];
12634
12674
  const isLongRunning = command === "preview" || command === "serve";
12635
12675
  if (!isLongRunning) {
12636
- const { ensureProcessCanExit } = await import("./browser-5CTOA2WS.js");
12676
+ const { ensureProcessCanExit } = await import("./browser-U4VWPTS2.js");
12637
12677
  await ensureProcessCanExit().catch(() => {
12638
12678
  });
12639
12679
  process.exit(exitCode);
@@ -21,8 +21,8 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-LRBSUKUZ.js";
25
- import "./chunk-N2JFPWMI.js";
24
+ } from "./chunk-MXG2H3HJ.js";
25
+ import "./chunk-SEFIJY2M.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
28
28
  getDaemonConfig,
@@ -304,8 +304,13 @@ var gotoCommand = registerCommand({
304
304
  }),
305
305
  handler: async (p, ctx) => {
306
306
  let url = p.url;
307
- if (!/^https?:\/\//i.test(url) && !/^wss?:\/\//i.test(url)) {
308
- url = "https://" + url;
307
+ const hasScheme = /^(https?|wss?|file|about|data|chrome|blob):/i.test(url);
308
+ if (!hasScheme) {
309
+ if (/^[\w-]+(\.[\w-]+)+/.test(url) || url.startsWith("localhost")) {
310
+ url = "https://" + url;
311
+ } else {
312
+ throw new Error(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
313
+ }
309
314
  }
310
315
  const response = await ctx.page.goto(url, {
311
316
  waitUntil: p.waitUntil || "domcontentloaded"
@@ -360,7 +365,7 @@ var urlCommand = registerCommand({
360
365
  scope: "page",
361
366
  result: z.object({ url: z.string() }),
362
367
  handler: async (_p, ctx) => {
363
- return ok({ url: ctx.page.url() });
368
+ return ok({ url: ctx.page.url() || "about:blank" });
364
369
  }
365
370
  });
366
371
  registerCommand({
@@ -523,7 +528,8 @@ var fillCommand = registerCommand({
523
528
  }),
524
529
  handler: async (p, ctx) => {
525
530
  const page = ctx.page;
526
- if (p.clear) {
531
+ const shouldClear = p.clear !== false;
532
+ if (shouldClear) {
527
533
  await page.fill(p.selector, "", { force: true, timeout: 1e4 });
528
534
  }
529
535
  const isReact = await page.evaluate(() => {
@@ -549,7 +555,7 @@ var fillCommand = registerCommand({
549
555
  } else {
550
556
  await page.fill(p.selector, p.value, { force: true, timeout: 1e4 });
551
557
  }
552
- return ok2({ selector: p.selector, value: p.value, cleared: p.clear || false, reactMode: !!isReact });
558
+ return ok2({ selector: p.selector, value: p.value, cleared: shouldClear, reactMode: !!isReact });
553
559
  }
554
560
  });
555
561
  var typeCommand = registerCommand({
@@ -6918,7 +6924,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6918
6924
  }
6919
6925
  let targetPageOverride = null;
6920
6926
  if (_target && extraOpts?.cdpEndpoint) {
6921
- const { findTargetPage } = await import("./browser-ITLZZDHJ.js");
6927
+ const { findTargetPage } = await import("./browser-AXCKBSWS.js");
6922
6928
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6923
6929
  if (!targetPageOverride) {
6924
6930
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8602,7 +8608,7 @@ function createRPCHandler() {
8602
8608
  const isNewFormat = Array.isArray(parsed.actions);
8603
8609
  if (isNewFormat) {
8604
8610
  try {
8605
- const { SessionReplayer } = await import("./session-replayer-MY27H4DX.js");
8611
+ const { SessionReplayer } = await import("./session-replayer-GCGY6KFK.js");
8606
8612
  const replayer = new SessionReplayer({
8607
8613
  page: session.page,
8608
8614
  stepDelay: slowMo * 500,
package/dist/index.d.ts CHANGED
@@ -1267,7 +1267,7 @@ interface AISearchResult {
1267
1267
  * @param argv - Raw CLI argument array (typically `process.argv.slice(2)`).
1268
1268
  * @param stdinCommands - Optional array of commands read from stdin.
1269
1269
  */
1270
- declare function routeCommand(argv: string[], stdinCommands?: string[]): Promise<void>;
1270
+ declare function routeCommand(argvIn: string[], stdinCommands?: string[]): Promise<void>;
1271
1271
 
1272
1272
  /**
1273
1273
  * Open a new browser session, navigate to the given URL, and persist session metadata.
package/dist/index.js CHANGED
@@ -81,8 +81,8 @@ import {
81
81
  resolveLaunchOpts,
82
82
  saveSessionDiskMeta,
83
83
  setActivePage
84
- } from "./chunk-TWWOIJM7.js";
85
- import "./chunk-N2JFPWMI.js";
84
+ } from "./chunk-5QAYN5EZ.js";
85
+ import "./chunk-SEFIJY2M.js";
86
86
  import "./chunk-TNEN6VQ2.js";
87
87
  import {
88
88
  errMsg
@@ -386,8 +386,13 @@ var gotoCommand = registerCommand({
386
386
  }),
387
387
  handler: async (p, ctx) => {
388
388
  let url = p.url;
389
- if (!/^https?:\/\//i.test(url) && !/^wss?:\/\//i.test(url)) {
390
- url = "https://" + url;
389
+ const hasScheme = /^(https?|wss?|file|about|data|chrome|blob):/i.test(url);
390
+ if (!hasScheme) {
391
+ if (/^[\w-]+(\.[\w-]+)+/.test(url) || url.startsWith("localhost")) {
392
+ url = "https://" + url;
393
+ } else {
394
+ throw new Error(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
395
+ }
391
396
  }
392
397
  const response = await ctx.page.goto(url, {
393
398
  waitUntil: p.waitUntil || "domcontentloaded"
@@ -442,7 +447,7 @@ var urlCommand = registerCommand({
442
447
  scope: "page",
443
448
  result: z.object({ url: z.string() }),
444
449
  handler: async (_p, ctx) => {
445
- return ok({ url: ctx.page.url() });
450
+ return ok({ url: ctx.page.url() || "about:blank" });
446
451
  }
447
452
  });
448
453
  registerCommand({
@@ -605,7 +610,8 @@ var fillCommand = registerCommand({
605
610
  }),
606
611
  handler: async (p, ctx) => {
607
612
  const page = ctx.page;
608
- if (p.clear) {
613
+ const shouldClear = p.clear !== false;
614
+ if (shouldClear) {
609
615
  await page.fill(p.selector, "", { force: true, timeout: 1e4 });
610
616
  }
611
617
  const isReact = await page.evaluate(() => {
@@ -631,7 +637,7 @@ var fillCommand = registerCommand({
631
637
  } else {
632
638
  await page.fill(p.selector, p.value, { force: true, timeout: 1e4 });
633
639
  }
634
- return ok2({ selector: p.selector, value: p.value, cleared: p.clear || false, reactMode: !!isReact });
640
+ return ok2({ selector: p.selector, value: p.value, cleared: shouldClear, reactMode: !!isReact });
635
641
  }
636
642
  });
637
643
  var typeCommand = registerCommand({
@@ -7280,7 +7286,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7280
7286
  }
7281
7287
  let targetPageOverride = null;
7282
7288
  if (_target && extraOpts?.cdpEndpoint) {
7283
- const { findTargetPage } = await import("./browser-IUJXXNBT.js");
7289
+ const { findTargetPage } = await import("./browser-X7OVRKJH.js");
7284
7290
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7285
7291
  if (!targetPageOverride) {
7286
7292
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9837,10 +9843,10 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9837
9843
  break;
9838
9844
  }
9839
9845
  case "press": {
9840
- const key = parsed.value || parsed.remaining[1];
9841
- if (!sel && !key) outputError("Usage: xbrowser press [selector] <key>");
9846
+ const key = parsed.value || (sel ? parsed.remaining[1] : parsed.remaining[0]);
9847
+ if (!key) outputError("Usage: xbrowser press [selector] <key>");
9842
9848
  cmdName = "press";
9843
- params = { ...sel ? { selector: sel } : {}, key: key || sel };
9849
+ params = { ...sel ? { selector: sel } : {}, key };
9844
9850
  break;
9845
9851
  }
9846
9852
  case "select": {
@@ -9909,13 +9915,22 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9909
9915
  params = { expression: args.join(" ") };
9910
9916
  break;
9911
9917
  case "scroll": {
9912
- const direction = args[0] || "down";
9913
- if (!["up", "down", "left", "right"].includes(direction))
9914
- outputError("Direction must be: up, down, left, right");
9918
+ let direction = "down";
9919
+ let distance;
9920
+ const firstArg = args[0];
9921
+ if (firstArg && ["up", "down", "left", "right"].includes(firstArg)) {
9922
+ direction = firstArg;
9923
+ if (args[1] && /^\d+$/.test(args[1])) distance = Number(args[1]);
9924
+ } else if (firstArg && /^\d+$/.test(firstArg)) {
9925
+ distance = Number(firstArg);
9926
+ } else if (firstArg) {
9927
+ outputError(`Invalid scroll argument: "${firstArg}". Use up/down/left/right or a pixel number.`);
9928
+ }
9929
+ distance = distance ?? (options.distance ? Number(options.distance) : options.amount ? Number(options.amount) : void 0);
9915
9930
  cmdName = "scroll";
9916
9931
  params = {
9917
9932
  direction,
9918
- distance: options.distance ? Number(options.distance) : options.amount ? Number(options.amount) : void 0,
9933
+ distance,
9919
9934
  selector: options.selector || options.s
9920
9935
  };
9921
9936
  break;
@@ -9928,6 +9943,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9928
9943
  cmdName = "url";
9929
9944
  params = {};
9930
9945
  break;
9946
+ case "set-viewport": {
9947
+ const width = options.width ? Number(options.width) : args[0] ? Number(args[0]) : void 0;
9948
+ const height = options.height ? Number(options.height) : args[1] ? Number(args[1]) : void 0;
9949
+ if (!width || !height) outputError("Usage: xbrowser set-viewport <width> <height>");
9950
+ cmdName = "set-viewport";
9951
+ params = { width, height };
9952
+ break;
9953
+ }
9931
9954
  case "html":
9932
9955
  cmdName = "html";
9933
9956
  params = { selector: options.selector || options.s };
@@ -10100,7 +10123,16 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10100
10123
  } else if (!result.success) {
10101
10124
  outputError(result.message || "Command failed");
10102
10125
  } else {
10103
- outputResult(result.data, mode);
10126
+ const data = result.data;
10127
+ const isEmptyResult = data && typeof data === "object" && Object.values(data).every((v) => v === "" || v === null || v === void 0);
10128
+ if (isEmptyResult) {
10129
+ const hint = cdpEndpoint ? `\u53EF\u80FD\u672A\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\u3002\u8BF7\u786E\u8BA4 ${cdpEndpoint} \u4E0A\u6709 Chrome \u8FD0\u884C\uFF08--remote-debugging-port\uFF09\u3002` : "\u53EF\u80FD\u672A\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\u3002\u8BF7\u4F7F\u7528 --cdp <endpoint> \u8FDE\u63A5\uFF0C\u6216\u5B89\u88C5 cdp-tunnel \u590D\u7528\u5DF2\u6709 Chrome\u3002";
10130
+ outputResult(result.data, mode);
10131
+ console.error(`
10132
+ \u26A0\uFE0F ${hint}`);
10133
+ } else {
10134
+ outputResult(result.data, mode);
10135
+ }
10104
10136
  }
10105
10137
  }
10106
10138
 
@@ -12386,7 +12418,8 @@ async function handleChainInput(input, argv) {
12386
12418
  }
12387
12419
  if (!chainResult.success) throw new Error("Command failed");
12388
12420
  }
12389
- async function routeCommand(argv, stdinCommands) {
12421
+ async function routeCommand(argvIn, stdinCommands) {
12422
+ let argv = argvIn;
12390
12423
  try {
12391
12424
  if (stdinCommands && stdinCommands.length > 0) {
12392
12425
  await handleStdinMode(stdinCommands, argv);
@@ -12400,6 +12433,13 @@ async function routeCommand(argv, stdinCommands) {
12400
12433
  await handleChainInput(argv[0], argv);
12401
12434
  return;
12402
12435
  }
12436
+ if (argv[0] && argv[0].includes(" ")) {
12437
+ const spaceIdx = argv[0].indexOf(" ");
12438
+ const possibleCmd = argv[0].substring(0, spaceIdx);
12439
+ if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12440
+ argv = [possibleCmd, argv[0].substring(spaceIdx + 1), ...argv.slice(1)];
12441
+ }
12442
+ }
12403
12443
  } catch (e) {
12404
12444
  outputError(e instanceof Error ? e.message : String(e));
12405
12445
  return;
@@ -15760,7 +15800,7 @@ var DataCollector = class {
15760
15800
  return results;
15761
15801
  }
15762
15802
  async createBrowserContext() {
15763
- const { launch } = await import("./cdp-driver-D6WMSMWX.js");
15803
+ const { launch } = await import("./cdp-driver-ZAVN7GRB.js");
15764
15804
  const { browser } = await launch({
15765
15805
  headless: true,
15766
15806
  args: ["--no-sandbox", "--disable-setuid-sandbox"]
@@ -31,7 +31,7 @@ var SessionReplayer = class {
31
31
  if (this.opts.page) {
32
32
  this.page = this.opts.page;
33
33
  } else if (this.opts.cdpUrl) {
34
- const { launch } = await import("./cdp-driver-D6WMSMWX.js");
34
+ const { launch } = await import("./cdp-driver-ZAVN7GRB.js");
35
35
  const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
36
36
  let contexts = browser.contexts();
37
37
  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.0.4",
3
+ "version": "1.0.6",
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": {