@xbrowser/cli 1.1.1 → 1.2.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
@@ -25,7 +25,7 @@ import {
25
25
  resolveLaunchOpts,
26
26
  saveSessionDiskMeta,
27
27
  setActivePage
28
- } from "./chunk-ISOSRTTV.js";
28
+ } from "./chunk-MNFOCOL6.js";
29
29
  import "./chunk-TNEN6VQ2.js";
30
30
  import {
31
31
  forwardCommandLog,
@@ -800,6 +800,21 @@ var waitForSelectorDef = {
800
800
  }
801
801
  };
802
802
  var waitCommand = registerCommand({ name: "wait", selectorParams: ["selector"], ...waitForSelectorDef });
803
+ var waitForTimeoutCommand = registerCommand({
804
+ name: "waitForTimeout",
805
+ description: "Wait for a specified number of milliseconds",
806
+ scope: "project",
807
+ parameters: z4.object({
808
+ timeout: z4.number().describe("Milliseconds to wait").default(1e3)
809
+ }),
810
+ result: z4.object({
811
+ waited: z4.number()
812
+ }),
813
+ handler: async (p) => {
814
+ await new Promise((r) => setTimeout(r, p.timeout));
815
+ return ok4({ waited: p.timeout });
816
+ }
817
+ });
803
818
 
804
819
  // src/commands/scroll.ts
805
820
  import { z as z5 } from "zod";
@@ -1665,7 +1680,7 @@ var healthCheckCommand = registerCommand({
1665
1680
  issues.push({
1666
1681
  severity: "error",
1667
1682
  category: "links",
1668
- message: `Broken link (fetch error): ${href} \u2014 ${errMsg(err) || "unknown"}`
1683
+ message: `Broken link (fetch error): ${href} \u2014 ${(err instanceof Error ? err.message : String(err)) || "unknown"}`
1669
1684
  });
1670
1685
  }
1671
1686
  }
@@ -2341,9 +2356,53 @@ var scrapeCommand = registerCommand({
2341
2356
  }
2342
2357
  let content;
2343
2358
  switch (p.format) {
2344
- case "markdown":
2359
+ case "markdown": {
2360
+ const tablesMd = await page.evaluate(() => {
2361
+ const tableSelectors = [
2362
+ "table",
2363
+ '[role="table"]',
2364
+ '[role="grid"]',
2365
+ '[class*="el-table"]',
2366
+ // Element UI
2367
+ '[class*="ant-table"]',
2368
+ // Ant Design
2369
+ '[class*="MuiTable"]',
2370
+ // Material UI
2371
+ '[class*="table"]'
2372
+ // Generic table-like
2373
+ ].join(",");
2374
+ const tables = document.querySelectorAll(tableSelectors);
2375
+ if (tables.length === 0) return "";
2376
+ return Array.from(tables).map((table) => {
2377
+ const rows = table.querySelectorAll('tr, [role="row"], [class*="row"]');
2378
+ if (rows.length === 0) return "";
2379
+ const mdRows = Array.from(rows).map((row) => {
2380
+ const cells = row.querySelectorAll('th, td, [role="columnheader"], [role="cell"], [class*="cell"], [class*="col"]');
2381
+ return "| " + Array.from(cells).map((c) => {
2382
+ const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
2383
+ return cellText.replace(/\|/g, "\\|") || "";
2384
+ }).join(" | ") + " |";
2385
+ }).join("\n");
2386
+ const headerRow = rows[0];
2387
+ const headerCells = headerRow.querySelectorAll('th, [role="columnheader"], [class*="header"]');
2388
+ const hasHeader = headerCells.length > 0;
2389
+ if (hasHeader && mdRows) {
2390
+ const headerCount = headerCells.length;
2391
+ const sep = "| " + Array(headerCount).fill("---").join(" | ") + " |";
2392
+ return mdRows.split("\n").map((line, i) => {
2393
+ if (i === 0) return line + "\n" + sep;
2394
+ return line;
2395
+ }).join("\n");
2396
+ }
2397
+ return mdRows;
2398
+ }).join("\n\n");
2399
+ });
2345
2400
  content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
2401
+ if (tablesMd) {
2402
+ content = tablesMd + "\n\n" + content;
2403
+ }
2346
2404
  break;
2405
+ }
2347
2406
  case "html":
2348
2407
  content = html;
2349
2408
  break;
@@ -5283,6 +5342,7 @@ function parseCommandChain(input, options) {
5283
5342
  continue;
5284
5343
  }
5285
5344
  if (char === ";") {
5345
+ lastOperator = "sequence";
5286
5346
  flushPipeline();
5287
5347
  continue;
5288
5348
  }
@@ -5337,6 +5397,7 @@ registerCommandDefinition("uncheck", ["selector"]);
5337
5397
  registerCommandDefinition("hover", ["selector"]);
5338
5398
  registerCommandDefinition("dblclick", ["selector"]);
5339
5399
  registerCommandDefinition("wait", ["selector"]);
5400
+ registerCommandDefinition("waitForTimeout", ["timeout"]);
5340
5401
  registerCommandDefinition("screenshot", []);
5341
5402
  registerCommandDefinition("eval", ["expression"]);
5342
5403
  registerCommandDefinition("scroll", ["direction"]);
@@ -6969,7 +7030,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6969
7030
  }
6970
7031
  let targetPageOverride = null;
6971
7032
  if (_target && extraOpts?.cdpEndpoint) {
6972
- const { findTargetPage } = await import("./browser-CFHOD5GY.js");
7033
+ const { findTargetPage } = await import("./browser-PZX7PO23.js");
6973
7034
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6974
7035
  if (!targetPageOverride) {
6975
7036
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9477,7 +9538,17 @@ var SELECTOR_COMMANDS = /* @__PURE__ */ new Set([
9477
9538
  "dblclick",
9478
9539
  "wait"
9479
9540
  ]);
9541
+ var CAMEL_TO_KEBAB = {
9542
+ getCookies: "get-cookies",
9543
+ setCookie: "set-cookie",
9544
+ clearCookies: "clear-cookies",
9545
+ getLocalStorage: "get-local-storage",
9546
+ setLocalStorage: "set-local-storage",
9547
+ clearLocalStorage: "clear-local-storage",
9548
+ setViewport: "set-viewport"
9549
+ };
9480
9550
  async function handleBrowserCommand(command, args, options, sessionName, mode, cdpEndpoint) {
9551
+ command = CAMEL_TO_KEBAB[command] || command;
9481
9552
  if (args.includes("--help") || args.includes("-h") || options.help || options.h) {
9482
9553
  const cmdDef = getCommand(command);
9483
9554
  if (cmdDef) {
@@ -9594,7 +9665,8 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9594
9665
  if (!args[0]) outputError(`Usage: xbrowser ${command} <url>`);
9595
9666
  cmdName = "goto";
9596
9667
  params = {
9597
- url: /^https?:\/\//i.test(args[0]) || /^wss?:\/\//i.test(args[0]) ? args[0] : "https://" + args[0],
9668
+ // Don't prefix if URL already has a scheme (http, file, about, data, etc.)
9669
+ url: /^(https?|wss?|file|about|data|chrome|blob):/i.test(args[0]) ? args[0] : /^[\w-]+(\.[\w-]+)+/.test(args[0]) || args[0].startsWith("localhost") ? "https://" + args[0] : args[0],
9598
9670
  waitUntil: options.waitUntil,
9599
9671
  ...options.timeout ? { timeout: Number(options.timeout) } : {}
9600
9672
  };
@@ -9606,7 +9678,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9606
9678
  type: options.type,
9607
9679
  selector: options.selector || options.s,
9608
9680
  base64: !!options.base64,
9609
- output: options.output || options.o
9681
+ output: options.output || options.o || args[0]
9610
9682
  };
9611
9683
  break;
9612
9684
  case "eval":
@@ -9652,11 +9724,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9652
9724
  break;
9653
9725
  }
9654
9726
  case "mouse": {
9655
- const action = options.action || args.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9656
- const actionIdx = action ? args.indexOf(action) : -1;
9657
- const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && args[actionIdx + 1] ? Number(args[actionIdx + 1]) : void 0;
9658
- const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && args[actionIdx + 2] ? Number(args[actionIdx + 2]) : void 0;
9659
- if (!action || x === void 0 || y === void 0) {
9727
+ const flatArgs = args.flatMap((a) => a.split(/\s+/).filter(Boolean));
9728
+ const action = options.action || flatArgs.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9729
+ const actionIdx = action ? flatArgs.indexOf(action) : -1;
9730
+ const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && flatArgs[actionIdx + 1] ? Number(flatArgs[actionIdx + 1]) : void 0;
9731
+ const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && flatArgs[actionIdx + 2] ? Number(flatArgs[actionIdx + 2]) : void 0;
9732
+ if (!action || x === void 0 || y === void 0 || isNaN(x) || isNaN(y)) {
9660
9733
  outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
9661
9734
  }
9662
9735
  cmdName = "mouse";
@@ -9842,6 +9915,7 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9842
9915
  outputResult(result.data, mode);
9843
9916
  console.error(`
9844
9917
  \u26A0\uFE0F ${hint}`);
9918
+ process.exit(1);
9845
9919
  } else {
9846
9920
  outputResult(result.data, mode);
9847
9921
  }
@@ -9936,11 +10010,6 @@ async function handleSession(args, options, mode, _cdpEndpoint) {
9936
10010
  }
9937
10011
 
9938
10012
  // src/cli/plugin-routes.ts
9939
- var pluginLoader3 = null;
9940
- function getPluginLoader2() {
9941
- if (!pluginLoader3) pluginLoader3 = new XBrowserPluginLoader();
9942
- return pluginLoader3;
9943
- }
9944
10013
  async function buildRuntimePluginInfo() {
9945
10014
  const loader = await getPluginLoader();
9946
10015
  const sites = loader.getCore().loader.getSites();
@@ -10198,6 +10267,10 @@ async function handlePlugin(args, options, mode) {
10198
10267
  } else {
10199
10268
  result = await installer.installWithMarketplaceFallback(source, installOpts);
10200
10269
  }
10270
+ try {
10271
+ await (await getPluginLoader()).reloadPlugin(result.name);
10272
+ } catch {
10273
+ }
10201
10274
  outputResult(
10202
10275
  { ok: true, name: result.name, source: result.source, path: result.path },
10203
10276
  mode
@@ -10213,6 +10286,11 @@ async function handlePlugin(args, options, mode) {
10213
10286
  outputError(`Plugin "${name}" is not installed. Use 'xbrowser plugin list' to see installed plugins.`);
10214
10287
  }
10215
10288
  await installer.uninstall(name);
10289
+ try {
10290
+ const loader = await getPluginLoader();
10291
+ await loader.reloadPlugin(name);
10292
+ } catch {
10293
+ }
10216
10294
  outputResult({ ok: true, name }, mode);
10217
10295
  break;
10218
10296
  }
@@ -10266,7 +10344,7 @@ Total: ${enrichedPlugins.length} plugins`);
10266
10344
  case "reload": {
10267
10345
  const name = subArgs[0];
10268
10346
  if (!name) outputError("Usage: xbrowser plugin reload <name>");
10269
- await getPluginLoader2().reloadPlugin(name);
10347
+ (await getPluginLoader()).reloadPlugin(name);
10270
10348
  outputResult({ ok: true, name }, mode);
10271
10349
  break;
10272
10350
  }
@@ -10650,15 +10728,20 @@ async function handleExtract(args, _mode) {
10650
10728
  console.log(`
10651
10729
  Saved LLM summary: ${outputPath}`);
10652
10730
  }
10653
- async function handleFilter(args, _mode) {
10731
+ async function handleFilter(args, _mode, options) {
10654
10732
  const filePath = args[0];
10655
10733
  const outputPath = args[1];
10656
10734
  if (!filePath || !outputPath) {
10657
- console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude-types=type1,type2]");
10735
+ console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
10658
10736
  process.exit(1);
10659
10737
  }
10660
10738
  const { filterRecording, parseExcludeTypes } = await import("./filter-EDTFGLS5.js");
10661
- const excludeTypes = parseExcludeTypes(args.slice(2));
10739
+ const excludeArgs = args.slice(2).concat(
10740
+ Object.entries(options || {}).flatMap(
10741
+ ([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
10742
+ )
10743
+ );
10744
+ const excludeTypes = parseExcludeTypes(excludeArgs);
10662
10745
  const result = filterRecording(filePath, outputPath, excludeTypes);
10663
10746
  console.log(`Filtered ${filePath} -> ${outputPath}`);
10664
10747
  console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
@@ -12109,10 +12192,18 @@ function extractCdpFromArgv(argv) {
12109
12192
  async function handleStdinMode(stdinCommands, argv) {
12110
12193
  const chain = stdinCommands.join(" && ");
12111
12194
  const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
12112
- const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint });
12195
+ const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
12196
+ const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
12113
12197
  printChainResult(chainResult);
12114
12198
  if (!chainResult.success) throw new Error("Command failed");
12115
12199
  }
12200
+ function extractSessionNameFromArgv(argv) {
12201
+ for (let i = 0; i < argv.length; i++) {
12202
+ if (argv[i] === "--session" && argv[i + 1]) return argv[i + 1];
12203
+ if (typeof argv[i] === "string" && argv[i].startsWith("--session=")) return argv[i].slice(10);
12204
+ }
12205
+ return process.env.XBROWSER_SESSION || "default";
12206
+ }
12116
12207
  async function handleEvalMode(argv) {
12117
12208
  const evalCommands = parseEvalFlags(argv);
12118
12209
  if (evalCommands.length === 0) return;
@@ -12252,6 +12343,31 @@ async function routeCommand(argvIn, stdinCommands) {
12252
12343
  }
12253
12344
  return;
12254
12345
  }
12346
+ const SUBCOMMAND_HELP = {
12347
+ session: "session open|close|kill|list [--session <name>] [--cdp <endpoint>]",
12348
+ plugin: "plugin install|uninstall|list|reload|schema|search|info <name>",
12349
+ record: "record start|stop|status [--url <url>] [--name <flow>]",
12350
+ daemon: "daemon status [--port <port>]",
12351
+ replay: "replay <file> [--slow-mo <ms>] [--stop-on-error]",
12352
+ create: "create <name> [--template static|dynamic|login|api]",
12353
+ run: "run <file>",
12354
+ serve: "serve [--port <port>] [--token <token>]",
12355
+ remote: "remote <url> [command] [--token <token>]",
12356
+ convert: "convert <file> [--to js|py|sh]",
12357
+ extract: "extract <file> [--format json|yaml]",
12358
+ filter: "filter <file> [--include <type>] [--exclude <type>]",
12359
+ test: "test <name> [--cdp <endpoint>]",
12360
+ viewer: "viewer [--session <name>]",
12361
+ kill: "kill [--all]",
12362
+ net: "net [--cdp <endpoint>]"
12363
+ };
12364
+ const subHelp = SUBCOMMAND_HELP[command];
12365
+ if (subHelp) {
12366
+ console.log(`
12367
+ Usage: xbrowser ${subHelp}
12368
+ `);
12369
+ return;
12370
+ }
12255
12371
  showMainHelp();
12256
12372
  return;
12257
12373
  }
@@ -12289,7 +12405,7 @@ async function routeCommand(argvIn, stdinCommands) {
12289
12405
  await handleExtract(cmdArgs, mode);
12290
12406
  break;
12291
12407
  case "filter":
12292
- await handleFilter(cmdArgs, mode);
12408
+ await handleFilter(cmdArgs, mode, options);
12293
12409
  break;
12294
12410
  case "run":
12295
12411
  if (!cmdArgs[0]) {
@@ -12738,7 +12854,7 @@ async function main() {
12738
12854
  const command = process.argv[2];
12739
12855
  const isLongRunning = command === "preview" || command === "serve";
12740
12856
  if (!isLongRunning) {
12741
- const { ensureProcessCanExit } = await import("./browser-CFHOD5GY.js");
12857
+ const { ensureProcessCanExit } = await import("./browser-PZX7PO23.js");
12742
12858
  await ensureProcessCanExit().catch(() => {
12743
12859
  });
12744
12860
  process.exit(exitCode);
@@ -21,8 +21,8 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-GOKTOYWM.js";
25
- import "./chunk-IX4JY6OO.js";
24
+ } from "./chunk-SLQR57XZ.js";
25
+ import "./chunk-QFROODUU.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
28
28
  getDaemonConfig,
@@ -758,6 +758,21 @@ var waitForSelectorDef = {
758
758
  }
759
759
  };
760
760
  var waitCommand = registerCommand({ name: "wait", selectorParams: ["selector"], ...waitForSelectorDef });
761
+ var waitForTimeoutCommand = registerCommand({
762
+ name: "waitForTimeout",
763
+ description: "Wait for a specified number of milliseconds",
764
+ scope: "project",
765
+ parameters: z4.object({
766
+ timeout: z4.number().describe("Milliseconds to wait").default(1e3)
767
+ }),
768
+ result: z4.object({
769
+ waited: z4.number()
770
+ }),
771
+ handler: async (p) => {
772
+ await new Promise((r) => setTimeout(r, p.timeout));
773
+ return ok4({ waited: p.timeout });
774
+ }
775
+ });
761
776
 
762
777
  // src/commands/scroll.ts
763
778
  import { z as z5 } from "zod";
@@ -1623,7 +1638,7 @@ var healthCheckCommand = registerCommand({
1623
1638
  issues.push({
1624
1639
  severity: "error",
1625
1640
  category: "links",
1626
- message: `Broken link (fetch error): ${href} \u2014 ${errMsg(err) || "unknown"}`
1641
+ message: `Broken link (fetch error): ${href} \u2014 ${(err instanceof Error ? err.message : String(err)) || "unknown"}`
1627
1642
  });
1628
1643
  }
1629
1644
  }
@@ -2299,9 +2314,53 @@ var scrapeCommand = registerCommand({
2299
2314
  }
2300
2315
  let content;
2301
2316
  switch (p.format) {
2302
- case "markdown":
2317
+ case "markdown": {
2318
+ const tablesMd = await page.evaluate(() => {
2319
+ const tableSelectors = [
2320
+ "table",
2321
+ '[role="table"]',
2322
+ '[role="grid"]',
2323
+ '[class*="el-table"]',
2324
+ // Element UI
2325
+ '[class*="ant-table"]',
2326
+ // Ant Design
2327
+ '[class*="MuiTable"]',
2328
+ // Material UI
2329
+ '[class*="table"]'
2330
+ // Generic table-like
2331
+ ].join(",");
2332
+ const tables = document.querySelectorAll(tableSelectors);
2333
+ if (tables.length === 0) return "";
2334
+ return Array.from(tables).map((table) => {
2335
+ const rows = table.querySelectorAll('tr, [role="row"], [class*="row"]');
2336
+ if (rows.length === 0) return "";
2337
+ const mdRows = Array.from(rows).map((row) => {
2338
+ const cells = row.querySelectorAll('th, td, [role="columnheader"], [role="cell"], [class*="cell"], [class*="col"]');
2339
+ return "| " + Array.from(cells).map((c) => {
2340
+ const cellText = c.innerText?.trim().replace(/\n/g, " ") || "";
2341
+ return cellText.replace(/\|/g, "\\|") || "";
2342
+ }).join(" | ") + " |";
2343
+ }).join("\n");
2344
+ const headerRow = rows[0];
2345
+ const headerCells = headerRow.querySelectorAll('th, [role="columnheader"], [class*="header"]');
2346
+ const hasHeader = headerCells.length > 0;
2347
+ if (hasHeader && mdRows) {
2348
+ const headerCount = headerCells.length;
2349
+ const sep = "| " + Array(headerCount).fill("---").join(" | ") + " |";
2350
+ return mdRows.split("\n").map((line, i) => {
2351
+ if (i === 0) return line + "\n" + sep;
2352
+ return line;
2353
+ }).join("\n");
2354
+ }
2355
+ return mdRows;
2356
+ }).join("\n\n");
2357
+ });
2303
2358
  content = htmlToMarkdown(html, { onlyMainContent: p.onlyMainContent });
2359
+ if (tablesMd) {
2360
+ content = tablesMd + "\n\n" + content;
2361
+ }
2304
2362
  break;
2363
+ }
2305
2364
  case "html":
2306
2365
  content = html;
2307
2366
  break;
@@ -5241,6 +5300,7 @@ function parseCommandChain(input, options) {
5241
5300
  continue;
5242
5301
  }
5243
5302
  if (char === ";") {
5303
+ lastOperator = "sequence";
5244
5304
  flushPipeline();
5245
5305
  continue;
5246
5306
  }
@@ -5295,6 +5355,7 @@ registerCommandDefinition("uncheck", ["selector"]);
5295
5355
  registerCommandDefinition("hover", ["selector"]);
5296
5356
  registerCommandDefinition("dblclick", ["selector"]);
5297
5357
  registerCommandDefinition("wait", ["selector"]);
5358
+ registerCommandDefinition("waitForTimeout", ["timeout"]);
5298
5359
  registerCommandDefinition("screenshot", []);
5299
5360
  registerCommandDefinition("eval", ["expression"]);
5300
5361
  registerCommandDefinition("scroll", ["direction"]);
@@ -6927,7 +6988,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6927
6988
  }
6928
6989
  let targetPageOverride = null;
6929
6990
  if (_target && extraOpts?.cdpEndpoint) {
6930
- const { findTargetPage } = await import("./browser-BX4HZOUT.js");
6991
+ const { findTargetPage } = await import("./browser-CWI6BXYK.js");
6931
6992
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6932
6993
  if (!targetPageOverride) {
6933
6994
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8611,7 +8672,7 @@ function createRPCHandler() {
8611
8672
  const isNewFormat = Array.isArray(parsed.actions);
8612
8673
  if (isNewFormat) {
8613
8674
  try {
8614
- const { SessionReplayer } = await import("./session-replayer-YWMSSZWC.js");
8675
+ const { SessionReplayer } = await import("./session-replayer-IXLSCF5U.js");
8615
8676
  const replayer = new SessionReplayer({
8616
8677
  page: session.page,
8617
8678
  stepDelay: slowMo * 500,