@xbrowser/cli 1.5.1 → 1.5.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.
@@ -140,6 +140,7 @@ async function ensureDaemonRunning() {
140
140
  if (healthOk) return;
141
141
  if (attempt < 2) await new Promise((r) => setTimeout(r, 500));
142
142
  }
143
+ console.error("\u{1F504} Starting daemon...");
143
144
  _ensurePromise = startDaemonProcess(DAEMON_PORT).then(() => {
144
145
  });
145
146
  _ensurePromise.catch(() => {
@@ -153,7 +154,10 @@ async function ensureDaemonRunning() {
153
154
  }
154
155
  for (let attempt = 0; attempt < 20; attempt++) {
155
156
  const ready = await fetch(`${DAEMON_BASE}/health`, { signal: AbortSignal.timeout(1e3) }).then((r) => r.ok ? r.json() : null).then((d) => d?.status === "ok").catch(() => false);
156
- if (ready) return;
157
+ if (ready) {
158
+ console.error("\u2705 Daemon ready");
159
+ return;
160
+ }
157
161
  await new Promise((r) => setTimeout(r, 200));
158
162
  }
159
163
  throw new Error("Daemon HTTP server not ready after 4s");
package/dist/cli.js CHANGED
@@ -54,7 +54,7 @@ import {
54
54
  killAllDaemonProcesses,
55
55
  startDaemonProcess,
56
56
  stopDaemonProcess
57
- } from "./chunk-JPSFUFPG.js";
57
+ } from "./chunk-L35D5DAY.js";
58
58
  import {
59
59
  errMsg
60
60
  } from "./chunk-GDKLH7ZY.js";
@@ -1126,7 +1126,7 @@ var screenshotCommand = registerCommand({
1126
1126
  buffer = await ctx.page.screenshot(options);
1127
1127
  }
1128
1128
  if (p.output) {
1129
- writeFileSync(p.output, buffer);
1129
+ writeFileSync(p.output, buffer, "binary");
1130
1130
  return ok9({
1131
1131
  output: p.output,
1132
1132
  format,
@@ -1142,7 +1142,7 @@ var screenshotCommand = registerCommand({
1142
1142
  }
1143
1143
  ensureScreenshotsDir();
1144
1144
  const screenshotPath = generateScreenshotPath(format);
1145
- writeFileSync(screenshotPath, buffer);
1145
+ writeFileSync(screenshotPath, buffer, "binary");
1146
1146
  return ok9({
1147
1147
  output: screenshotPath,
1148
1148
  format,
@@ -5401,19 +5401,19 @@ function parseCommandChain(input, options) {
5401
5401
  continue;
5402
5402
  }
5403
5403
  if (char === "-" && input[i + 1] === ">" && isSpaceAround(input, i, 2)) {
5404
- pushCommand();
5405
- lastOperator = "and";
5404
+ lastOperator = "sequence";
5405
+ flushPipeline();
5406
5406
  i++;
5407
5407
  continue;
5408
5408
  }
5409
5409
  if (char === "," && isSpaceAdjacent(input, i)) {
5410
- pushCommand();
5411
- lastOperator = "and";
5410
+ lastOperator = "sequence";
5411
+ flushPipeline();
5412
5412
  continue;
5413
5413
  }
5414
5414
  if (char === "+" && isSpaceAdjacent(input, i)) {
5415
- pushCommand();
5416
- lastOperator = "and";
5415
+ lastOperator = "sequence";
5416
+ flushPipeline();
5417
5417
  continue;
5418
5418
  }
5419
5419
  if (options?.fileMode && char === "|" && input[i + 1] !== "|") {
@@ -7015,6 +7015,18 @@ async function loadHooks() {
7015
7015
  // src/executor.ts
7016
7016
  import { homedir as homedir6 } from "os";
7017
7017
  import { join as join6 } from "path";
7018
+ function levenshtein(a, b) {
7019
+ const m = a.length, n = b.length;
7020
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
7021
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
7022
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
7023
+ for (let i = 1; i <= m; i++) {
7024
+ for (let j = 1; j <= n; j++) {
7025
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
7026
+ }
7027
+ }
7028
+ return dp[m][n];
7029
+ }
7018
7030
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
7019
7031
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
7020
7032
  var CONFIG_DIR2 = join6(homedir6(), ".xbrowser");
@@ -7075,8 +7087,10 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7075
7087
  const command = getCommand(commandName);
7076
7088
  if (!command) {
7077
7089
  const available = getAllCommands().map((c) => c.name);
7090
+ const suggestions = available.map((name) => ({ name, dist: levenshtein(commandName, name) })).filter((s) => s.dist <= 3).sort((a, b) => a.dist - b.dist).slice(0, 3).map((s) => s.name);
7091
+ const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(" or ")}?` : "";
7078
7092
  return errorResult(
7079
- `Unknown command: ${commandName}. Available: ${available.join(", ")}`
7093
+ `Unknown command: ${commandName}.${hint} Available: ${available.join(", ")}`
7080
7094
  );
7081
7095
  }
7082
7096
  const _target = params._target;
@@ -7103,7 +7117,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7103
7117
  params = result.data;
7104
7118
  }
7105
7119
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
7106
- const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
7120
+ const { forwardExec } = await import("./daemon-client-S3EUTRC6.js");
7107
7121
  const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
7108
7122
  if (result) return result;
7109
7123
  }
@@ -8920,7 +8934,7 @@ var pluginSearchBuiltin = {
8920
8934
  const query = args[0] || "";
8921
8935
  try {
8922
8936
  const searchOptions = {
8923
- query,
8937
+ query: query || (options["tag"] || ""),
8924
8938
  tag: options["tag"],
8925
8939
  site: options["site"],
8926
8940
  limit: options["limit"] ? Number.parseInt(String(options["limit"])) : 20
@@ -10441,7 +10455,7 @@ async function handlePlugin(args, options, mode) {
10441
10455
  } catch {
10442
10456
  }
10443
10457
  try {
10444
- const { daemonPing } = await import("./daemon-client-XXKMJZZ7.js");
10458
+ const { daemonPing } = await import("./daemon-client-S3EUTRC6.js");
10445
10459
  if (await daemonPing()) {
10446
10460
  await fetch("http://localhost:9224/rpc", {
10447
10461
  method: "POST",
@@ -10525,7 +10539,11 @@ Total: ${enrichedPlugins.length} plugins`);
10525
10539
  case "reload": {
10526
10540
  const name = subArgs[0];
10527
10541
  if (!name) outputError("Usage: xbrowser plugin reload <name>");
10528
- (await getPluginLoader()).reloadPlugin(name);
10542
+ try {
10543
+ await (await getPluginLoader()).reloadPlugin(name);
10544
+ } catch {
10545
+ outputError(`Plugin "${name}" not found. Use 'xbrowser plugin list' to see installed plugins.`);
10546
+ }
10529
10547
  outputResult({ ok: true, name }, mode);
10530
10548
  break;
10531
10549
  }
@@ -12485,13 +12503,24 @@ async function routeCommand(argvIn, stdinCommands) {
12485
12503
  await handleChainInput(argv[0], argv);
12486
12504
  return;
12487
12505
  }
12488
- if (argv[0] && argv[0].includes(" ")) {
12489
- const spaceIdx = argv[0].indexOf(" ");
12490
- const possibleCmd = argv[0].substring(0, spaceIdx);
12506
+ const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
12507
+ let chainArgIdx = -1;
12508
+ for (let i = 0; i < argv.length; i++) {
12509
+ if (globalFlags.has(argv[i])) continue;
12510
+ if (globalFlags.has(argv[i]) || i > 0 && globalFlags.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
12511
+ if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
12512
+ chainArgIdx = i;
12513
+ break;
12514
+ }
12515
+ }
12516
+ if (chainArgIdx >= 0) {
12517
+ const chainArg = argv[chainArgIdx];
12518
+ const spaceIdx = chainArg.indexOf(" ");
12519
+ const possibleCmd = chainArg.substring(0, spaceIdx);
12491
12520
  if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12492
- const remainder = argv[0].substring(spaceIdx + 1);
12521
+ const remainder = chainArg.substring(spaceIdx + 1);
12493
12522
  const remainderParts = remainder.split(/\s+/).filter(Boolean);
12494
- argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12523
+ argv = [...argv.slice(0, chainArgIdx), possibleCmd, ...remainderParts, ...argv.slice(chainArgIdx + 1)];
12495
12524
  }
12496
12525
  }
12497
12526
  } catch (e) {
@@ -12505,7 +12534,7 @@ async function routeCommand(argvIn, stdinCommands) {
12505
12534
  const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
12506
12535
  const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
12507
12536
  const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
12508
- if (options.version) {
12537
+ if (options.version || options.v && positional.length === 0) {
12509
12538
  console.log(`xbrowser v${version}`);
12510
12539
  return;
12511
12540
  }
@@ -12685,9 +12714,33 @@ async function routeCommand(argvIn, stdinCommands) {
12685
12714
  const internalLoader = loader.getCore().loader;
12686
12715
  const site = internalLoader.getSite(command);
12687
12716
  if (!site) {
12688
- const fullInput = argv.join(" ");
12717
+ const globalFlagSet = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--help", "-h", "--version", "--output", "-o"]);
12718
+ const cleanParts = [];
12719
+ for (let i = 0; i < argv.length; i++) {
12720
+ if (globalFlagSet.has(argv[i])) continue;
12721
+ if (i > 0 && globalFlagSet.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
12722
+ if (argv[i].startsWith("--session=") || argv[i].startsWith("--cdp=")) continue;
12723
+ cleanParts.push(argv[i]);
12724
+ }
12725
+ const fullInput = cleanParts.join(" ");
12689
12726
  if (isChainInput(fullInput)) {
12690
12727
  const chainResult = await executeChain(fullInput, { cdpEndpoint, sessionName });
12728
+ if (mode === "json" || mode === "yaml") {
12729
+ const output = {
12730
+ success: chainResult.success,
12731
+ steps: chainResult.steps.map((s) => ({
12732
+ command: s.raw,
12733
+ success: s.success,
12734
+ data: s.data,
12735
+ duration: s.duration
12736
+ })),
12737
+ totalDuration: chainResult.totalDuration,
12738
+ ...chainResult.stoppedReason ? { stoppedReason: chainResult.stoppedReason } : {}
12739
+ };
12740
+ outputResult(output, mode);
12741
+ if (!chainResult.success) throw new Error("Command failed");
12742
+ return;
12743
+ }
12691
12744
  for (const step of chainResult.steps) {
12692
12745
  if (step.success) {
12693
12746
  console.log(`[OK] ${step.raw}`);
@@ -12797,7 +12850,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
12797
12850
  }
12798
12851
  const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
12799
12852
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
12800
- const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
12853
+ const { forwardExec } = await import("./daemon-client-S3EUTRC6.js");
12801
12854
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
12802
12855
  const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
12803
12856
  const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
@@ -29,7 +29,7 @@ import {
29
29
  forwardSessionList,
30
30
  forwardViewerCheckSelector,
31
31
  isDaemonRunning
32
- } from "./chunk-JPSFUFPG.js";
32
+ } from "./chunk-L35D5DAY.js";
33
33
  import "./chunk-GDKLH7ZY.js";
34
34
  import "./chunk-KFQGP6VL.js";
35
35
  export {
@@ -23,6 +23,7 @@ async function ensureDaemonRunning() {
23
23
  if (healthOk) return;
24
24
  if (attempt < 2) await new Promise((r) => setTimeout(r, 500));
25
25
  }
26
+ console.error("\u{1F504} Starting daemon...");
26
27
  _ensurePromise = startDaemonProcess(DAEMON_PORT).then(() => {
27
28
  });
28
29
  _ensurePromise.catch(() => {
@@ -36,7 +37,10 @@ async function ensureDaemonRunning() {
36
37
  }
37
38
  for (let attempt = 0; attempt < 20; attempt++) {
38
39
  const ready = await fetch(`${DAEMON_BASE}/health`, { signal: AbortSignal.timeout(1e3) }).then((r) => r.ok ? r.json() : null).then((d) => d?.status === "ok").catch(() => false);
39
- if (ready) return;
40
+ if (ready) {
41
+ console.error("\u2705 Daemon ready");
42
+ return;
43
+ }
40
44
  await new Promise((r) => setTimeout(r, 200));
41
45
  }
42
46
  throw new Error("Daemon HTTP server not ready after 4s");
@@ -1087,7 +1087,7 @@ var screenshotCommand = registerCommand({
1087
1087
  buffer = await ctx.page.screenshot(options);
1088
1088
  }
1089
1089
  if (p.output) {
1090
- writeFileSync(p.output, buffer);
1090
+ writeFileSync(p.output, buffer, "binary");
1091
1091
  return ok9({
1092
1092
  output: p.output,
1093
1093
  format,
@@ -1103,7 +1103,7 @@ var screenshotCommand = registerCommand({
1103
1103
  }
1104
1104
  ensureScreenshotsDir();
1105
1105
  const screenshotPath = generateScreenshotPath(format);
1106
- writeFileSync(screenshotPath, buffer);
1106
+ writeFileSync(screenshotPath, buffer, "binary");
1107
1107
  return ok9({
1108
1108
  output: screenshotPath,
1109
1109
  format,
@@ -5362,19 +5362,19 @@ function parseCommandChain(input, options) {
5362
5362
  continue;
5363
5363
  }
5364
5364
  if (char === "-" && input[i + 1] === ">" && isSpaceAround(input, i, 2)) {
5365
- pushCommand();
5366
- lastOperator = "and";
5365
+ lastOperator = "sequence";
5366
+ flushPipeline();
5367
5367
  i++;
5368
5368
  continue;
5369
5369
  }
5370
5370
  if (char === "," && isSpaceAdjacent(input, i)) {
5371
- pushCommand();
5372
- lastOperator = "and";
5371
+ lastOperator = "sequence";
5372
+ flushPipeline();
5373
5373
  continue;
5374
5374
  }
5375
5375
  if (char === "+" && isSpaceAdjacent(input, i)) {
5376
- pushCommand();
5377
- lastOperator = "and";
5376
+ lastOperator = "sequence";
5377
+ flushPipeline();
5378
5378
  continue;
5379
5379
  }
5380
5380
  if (options?.fileMode && char === "|" && input[i + 1] !== "|") {
@@ -6546,6 +6546,18 @@ async function loadHooks() {
6546
6546
  // src/executor.ts
6547
6547
  import { homedir as homedir5 } from "os";
6548
6548
  import { join as join5 } from "path";
6549
+ function levenshtein(a, b) {
6550
+ const m = a.length, n = b.length;
6551
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
6552
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
6553
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
6554
+ for (let i = 1; i <= m; i++) {
6555
+ for (let j = 1; j <= n; j++) {
6556
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
6557
+ }
6558
+ }
6559
+ return dp[m][n];
6560
+ }
6549
6561
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
6550
6562
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
6551
6563
  var CONFIG_DIR2 = join5(homedir5(), ".xbrowser");
@@ -6606,8 +6618,10 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6606
6618
  const command = getCommand(commandName);
6607
6619
  if (!command) {
6608
6620
  const available = getAllCommands().map((c) => c.name);
6621
+ const suggestions = available.map((name) => ({ name, dist: levenshtein(commandName, name) })).filter((s) => s.dist <= 3).sort((a, b) => a.dist - b.dist).slice(0, 3).map((s) => s.name);
6622
+ const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(" or ")}?` : "";
6609
6623
  return errorResult(
6610
- `Unknown command: ${commandName}. Available: ${available.join(", ")}`
6624
+ `Unknown command: ${commandName}.${hint} Available: ${available.join(", ")}`
6611
6625
  );
6612
6626
  }
6613
6627
  const _target = params._target;
@@ -6634,7 +6648,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6634
6648
  params = result.data;
6635
6649
  }
6636
6650
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
6637
- const { forwardExec } = await import("./daemon-client-COJQESU2.js");
6651
+ const { forwardExec } = await import("./daemon-client-WT7PTGYQ.js");
6638
6652
  const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
6639
6653
  if (result) return result;
6640
6654
  }
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  killAllDaemonProcesses,
26
26
  startDaemonProcess,
27
27
  stopDaemonProcess
28
- } from "./chunk-JPSFUFPG.js";
28
+ } from "./chunk-L35D5DAY.js";
29
29
  import {
30
30
  CaptchaDetector,
31
31
  HumanInteractionManager,
@@ -1166,7 +1166,7 @@ var screenshotCommand = registerCommand({
1166
1166
  buffer = await ctx.page.screenshot(options);
1167
1167
  }
1168
1168
  if (p.output) {
1169
- writeFileSync(p.output, buffer);
1169
+ writeFileSync(p.output, buffer, "binary");
1170
1170
  return ok9({
1171
1171
  output: p.output,
1172
1172
  format,
@@ -1182,7 +1182,7 @@ var screenshotCommand = registerCommand({
1182
1182
  }
1183
1183
  ensureScreenshotsDir();
1184
1184
  const screenshotPath = generateScreenshotPath(format);
1185
- writeFileSync(screenshotPath, buffer);
1185
+ writeFileSync(screenshotPath, buffer, "binary");
1186
1186
  return ok9({
1187
1187
  output: screenshotPath,
1188
1188
  format,
@@ -5718,19 +5718,19 @@ function parseCommandChain(input, options) {
5718
5718
  continue;
5719
5719
  }
5720
5720
  if (char === "-" && input[i + 1] === ">" && isSpaceAround(input, i, 2)) {
5721
- pushCommand();
5722
- lastOperator = "and";
5721
+ lastOperator = "sequence";
5722
+ flushPipeline();
5723
5723
  i++;
5724
5724
  continue;
5725
5725
  }
5726
5726
  if (char === "," && isSpaceAdjacent(input, i)) {
5727
- pushCommand();
5728
- lastOperator = "and";
5727
+ lastOperator = "sequence";
5728
+ flushPipeline();
5729
5729
  continue;
5730
5730
  }
5731
5731
  if (char === "+" && isSpaceAdjacent(input, i)) {
5732
- pushCommand();
5733
- lastOperator = "and";
5732
+ lastOperator = "sequence";
5733
+ flushPipeline();
5734
5734
  continue;
5735
5735
  }
5736
5736
  if (options?.fileMode && char === "|" && input[i + 1] !== "|") {
@@ -7332,6 +7332,18 @@ async function loadHooks() {
7332
7332
  // src/executor.ts
7333
7333
  import { homedir as homedir6 } from "os";
7334
7334
  import { join as join6 } from "path";
7335
+ function levenshtein(a, b) {
7336
+ const m = a.length, n = b.length;
7337
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
7338
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
7339
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
7340
+ for (let i = 1; i <= m; i++) {
7341
+ for (let j = 1; j <= n; j++) {
7342
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
7343
+ }
7344
+ }
7345
+ return dp[m][n];
7346
+ }
7335
7347
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
7336
7348
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
7337
7349
  var CONFIG_DIR2 = join6(homedir6(), ".xbrowser");
@@ -7395,8 +7407,10 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7395
7407
  const command = getCommand(commandName);
7396
7408
  if (!command) {
7397
7409
  const available = getAllCommands().map((c) => c.name);
7410
+ const suggestions = available.map((name) => ({ name, dist: levenshtein(commandName, name) })).filter((s) => s.dist <= 3).sort((a, b) => a.dist - b.dist).slice(0, 3).map((s) => s.name);
7411
+ const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(" or ")}?` : "";
7398
7412
  return errorResult(
7399
- `Unknown command: ${commandName}. Available: ${available.join(", ")}`
7413
+ `Unknown command: ${commandName}.${hint} Available: ${available.join(", ")}`
7400
7414
  );
7401
7415
  }
7402
7416
  const _target = params._target;
@@ -7423,7 +7437,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7423
7437
  params = result.data;
7424
7438
  }
7425
7439
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
7426
- const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
7440
+ const { forwardExec } = await import("./daemon-client-S3EUTRC6.js");
7427
7441
  const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
7428
7442
  if (result) return result;
7429
7443
  }
@@ -9255,7 +9269,7 @@ var pluginSearchBuiltin = {
9255
9269
  const query = args[0] || "";
9256
9270
  try {
9257
9271
  const searchOptions = {
9258
- query,
9272
+ query: query || (options["tag"] || ""),
9259
9273
  tag: options["tag"],
9260
9274
  site: options["site"],
9261
9275
  limit: options["limit"] ? Number.parseInt(String(options["limit"])) : 20
@@ -10781,7 +10795,7 @@ async function handlePlugin(args, options, mode) {
10781
10795
  } catch {
10782
10796
  }
10783
10797
  try {
10784
- const { daemonPing } = await import("./daemon-client-XXKMJZZ7.js");
10798
+ const { daemonPing } = await import("./daemon-client-S3EUTRC6.js");
10785
10799
  if (await daemonPing()) {
10786
10800
  await fetch("http://localhost:9224/rpc", {
10787
10801
  method: "POST",
@@ -10865,7 +10879,11 @@ Total: ${enrichedPlugins.length} plugins`);
10865
10879
  case "reload": {
10866
10880
  const name = subArgs[0];
10867
10881
  if (!name) outputError("Usage: xbrowser plugin reload <name>");
10868
- (await getPluginLoader()).reloadPlugin(name);
10882
+ try {
10883
+ await (await getPluginLoader()).reloadPlugin(name);
10884
+ } catch {
10885
+ outputError(`Plugin "${name}" not found. Use 'xbrowser plugin list' to see installed plugins.`);
10886
+ }
10869
10887
  outputResult({ ok: true, name }, mode);
10870
10888
  break;
10871
10889
  }
@@ -12825,13 +12843,24 @@ async function routeCommand(argvIn, stdinCommands) {
12825
12843
  await handleChainInput(argv[0], argv);
12826
12844
  return;
12827
12845
  }
12828
- if (argv[0] && argv[0].includes(" ")) {
12829
- const spaceIdx = argv[0].indexOf(" ");
12830
- const possibleCmd = argv[0].substring(0, spaceIdx);
12846
+ const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
12847
+ let chainArgIdx = -1;
12848
+ for (let i = 0; i < argv.length; i++) {
12849
+ if (globalFlags.has(argv[i])) continue;
12850
+ if (globalFlags.has(argv[i]) || i > 0 && globalFlags.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
12851
+ if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
12852
+ chainArgIdx = i;
12853
+ break;
12854
+ }
12855
+ }
12856
+ if (chainArgIdx >= 0) {
12857
+ const chainArg = argv[chainArgIdx];
12858
+ const spaceIdx = chainArg.indexOf(" ");
12859
+ const possibleCmd = chainArg.substring(0, spaceIdx);
12831
12860
  if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12832
- const remainder = argv[0].substring(spaceIdx + 1);
12861
+ const remainder = chainArg.substring(spaceIdx + 1);
12833
12862
  const remainderParts = remainder.split(/\s+/).filter(Boolean);
12834
- argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12863
+ argv = [...argv.slice(0, chainArgIdx), possibleCmd, ...remainderParts, ...argv.slice(chainArgIdx + 1)];
12835
12864
  }
12836
12865
  }
12837
12866
  } catch (e) {
@@ -12845,7 +12874,7 @@ async function routeCommand(argvIn, stdinCommands) {
12845
12874
  const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
12846
12875
  const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
12847
12876
  const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
12848
- if (options.version) {
12877
+ if (options.version || options.v && positional.length === 0) {
12849
12878
  console.log(`xbrowser v${version}`);
12850
12879
  return;
12851
12880
  }
@@ -13025,9 +13054,33 @@ async function routeCommand(argvIn, stdinCommands) {
13025
13054
  const internalLoader = loader.getCore().loader;
13026
13055
  const site = internalLoader.getSite(command);
13027
13056
  if (!site) {
13028
- const fullInput = argv.join(" ");
13057
+ const globalFlagSet = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--help", "-h", "--version", "--output", "-o"]);
13058
+ const cleanParts = [];
13059
+ for (let i = 0; i < argv.length; i++) {
13060
+ if (globalFlagSet.has(argv[i])) continue;
13061
+ if (i > 0 && globalFlagSet.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
13062
+ if (argv[i].startsWith("--session=") || argv[i].startsWith("--cdp=")) continue;
13063
+ cleanParts.push(argv[i]);
13064
+ }
13065
+ const fullInput = cleanParts.join(" ");
13029
13066
  if (isChainInput(fullInput)) {
13030
13067
  const chainResult = await executeChain(fullInput, { cdpEndpoint, sessionName });
13068
+ if (mode === "json" || mode === "yaml") {
13069
+ const output = {
13070
+ success: chainResult.success,
13071
+ steps: chainResult.steps.map((s) => ({
13072
+ command: s.raw,
13073
+ success: s.success,
13074
+ data: s.data,
13075
+ duration: s.duration
13076
+ })),
13077
+ totalDuration: chainResult.totalDuration,
13078
+ ...chainResult.stoppedReason ? { stoppedReason: chainResult.stoppedReason } : {}
13079
+ };
13080
+ outputResult(output, mode);
13081
+ if (!chainResult.success) throw new Error("Command failed");
13082
+ return;
13083
+ }
13031
13084
  for (const step of chainResult.steps) {
13032
13085
  if (step.success) {
13033
13086
  console.log(`[OK] ${step.raw}`);
@@ -13137,7 +13190,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13137
13190
  }
13138
13191
  const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
13139
13192
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
13140
- const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
13193
+ const { forwardExec } = await import("./daemon-client-S3EUTRC6.js");
13141
13194
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
13142
13195
  const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
13143
13196
  const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
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": {