@xbrowser/cli 1.3.0 → 1.3.1

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/index.js CHANGED
@@ -49,11 +49,11 @@ import {
49
49
  extractAndSave,
50
50
  extractRecording,
51
51
  printExtractSummary
52
- } from "./chunk-L53IDAWK.js";
52
+ } from "./chunk-MJFYLKGL.js";
53
53
  import {
54
54
  filterRecording,
55
55
  parseExcludeTypes
56
- } from "./chunk-AT4PHAJY.js";
56
+ } from "./chunk-GJAV3QGG.js";
57
57
  import {
58
58
  SessionRecorder
59
59
  } from "./chunk-ACFE6PKF.js";
@@ -81,8 +81,8 @@ import {
81
81
  resolveLaunchOpts,
82
82
  saveSessionDiskMeta,
83
83
  setActivePage
84
- } from "./chunk-6V57JME6.js";
85
- import "./chunk-QFROODUU.js";
84
+ } from "./chunk-UZQYMXR5.js";
85
+ import "./chunk-N4PIGZDG.js";
86
86
  import "./chunk-TNEN6VQ2.js";
87
87
  import {
88
88
  errMsg
@@ -114,7 +114,7 @@ var version = pkg.version;
114
114
  // src/executor.ts
115
115
  import {
116
116
  ok as ok25,
117
- fail as fail7,
117
+ fail as fail8,
118
118
  isCommandResult,
119
119
  CompositeStorage as CompositeStorage2,
120
120
  TipCollector as TipCollector2,
@@ -300,7 +300,7 @@ function createStubContext(pluginName) {
300
300
 
301
301
  // src/commands/navigation.ts
302
302
  import { z } from "zod";
303
- import { ok } from "@dyyz1993/xcli-core";
303
+ import { ok, fail } from "@dyyz1993/xcli-core";
304
304
 
305
305
  // src/commands/command-registry.ts
306
306
  var registry = /* @__PURE__ */ new Map();
@@ -392,13 +392,19 @@ var gotoCommand = registerCommand({
392
392
  if (/^[\w-]+(\.[\w-]+)+/.test(url) || url.startsWith("localhost")) {
393
393
  url = "https://" + url;
394
394
  } else {
395
- throw new Error(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
395
+ return fail(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
396
396
  }
397
397
  }
398
- const response = await ctx.page.goto(url, {
399
- waitUntil: p.waitUntil || "domcontentloaded",
400
- ...p.timeout ? { timeout: p.timeout } : {}
401
- });
398
+ let response;
399
+ try {
400
+ response = await ctx.page.goto(url, {
401
+ waitUntil: p.waitUntil || "domcontentloaded",
402
+ ...p.timeout ? { timeout: p.timeout } : {}
403
+ });
404
+ } catch (err) {
405
+ const msg = err instanceof Error ? err.message : String(err);
406
+ return fail(`Navigation failed: ${msg}`);
407
+ }
402
408
  const ssr = await detectSsr(ctx.page);
403
409
  return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
404
410
  }
@@ -713,6 +719,22 @@ var checkCommand = registerCommand({
713
719
  return ok2({ selector: p.selector });
714
720
  }
715
721
  });
722
+ var uncheckCommand = registerCommand({
723
+ name: "uncheck",
724
+ description: "Uncheck checkbox or radio",
725
+ scope: "element",
726
+ selectorParams: ["selector"],
727
+ parameters: z2.object({
728
+ selector: z2.string()
729
+ }),
730
+ result: z2.object({
731
+ selector: z2.string()
732
+ }),
733
+ handler: async (p, ctx) => {
734
+ await ctx.page.uncheck(p.selector, { timeout: 1e4 });
735
+ return ok2({ selector: p.selector });
736
+ }
737
+ });
716
738
  var hoverCommand = registerCommand({
717
739
  name: "hover",
718
740
  description: "Hover over element",
@@ -1254,7 +1276,7 @@ var setViewportCommand = registerCommand({
1254
1276
 
1255
1277
  // src/commands/frame.ts
1256
1278
  import { z as z12 } from "zod";
1257
- import { ok as ok12, fail } from "@dyyz1993/xcli-core";
1279
+ import { ok as ok12, fail as fail2 } from "@dyyz1993/xcli-core";
1258
1280
  var framesCommand = registerCommand({
1259
1281
  name: "frames",
1260
1282
  description: "List all frames in the current page",
@@ -1267,7 +1289,9 @@ var framesCommand = registerCommand({
1267
1289
  }))
1268
1290
  }),
1269
1291
  handler: async (_p, ctx) => {
1270
- const frameList = ctx.page.frames().map((frame, index) => ({
1292
+ const discover = ctx.page.discoverFrames;
1293
+ const rawFrames = discover ? await discover.call(ctx.page) : ctx.page.frames();
1294
+ const frameList = rawFrames.map((frame, index) => ({
1271
1295
  index,
1272
1296
  name: frame.name(),
1273
1297
  url: frame.url()
@@ -1289,17 +1313,18 @@ var frameCommand = registerCommand({
1289
1313
  error: z12.string().optional()
1290
1314
  }),
1291
1315
  handler: async (p, ctx) => {
1292
- const allFrames = ctx.page.frames();
1316
+ const discover = ctx.page.discoverFrames;
1317
+ const rawFrames = discover ? await discover.call(ctx.page) : ctx.page.frames();
1293
1318
  let targetFrame;
1294
1319
  if (p.index !== void 0) {
1295
- targetFrame = allFrames[p.index];
1320
+ targetFrame = rawFrames[p.index];
1296
1321
  } else if (p.name !== void 0) {
1297
- targetFrame = allFrames.find((f) => f.name() === p.name);
1322
+ targetFrame = rawFrames.find((f) => f.name() === p.name);
1298
1323
  } else {
1299
- return fail("Must provide index or name");
1324
+ return fail2("Must provide index or name");
1300
1325
  }
1301
1326
  if (!targetFrame) {
1302
- return fail("Frame not found");
1327
+ return fail2("Frame not found");
1303
1328
  }
1304
1329
  return ok12({
1305
1330
  name: targetFrame.name(),
@@ -1945,7 +1970,7 @@ var actionsCommand = registerCommand({
1945
1970
 
1946
1971
  // src/commands/scrape.ts
1947
1972
  import { z as z15 } from "zod";
1948
- import { ok as ok15, fail as fail2 } from "@dyyz1993/xcli-core";
1973
+ import { ok as ok15, fail as fail3 } from "@dyyz1993/xcli-core";
1949
1974
 
1950
1975
  // src/lib/html-to-markdown.ts
1951
1976
  import * as cheerio from "cheerio";
@@ -2461,7 +2486,7 @@ var scrapeCommand = registerCommand({
2461
2486
  }
2462
2487
  }
2463
2488
  }
2464
- return fail2(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2489
+ return fail3(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2465
2490
  } finally {
2466
2491
  await closeEphemeralContext(context);
2467
2492
  }
@@ -3525,7 +3550,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3525
3550
 
3526
3551
  // src/commands/network.ts
3527
3552
  import { z as z19 } from "zod";
3528
- import { ok as ok19, fail as fail3 } from "@dyyz1993/xcli-core";
3553
+ import { ok as ok19, fail as fail4 } from "@dyyz1993/xcli-core";
3529
3554
  function extractPath2(url) {
3530
3555
  try {
3531
3556
  const u = new URL(url);
@@ -3690,7 +3715,7 @@ var networkCommand = registerCommand({
3690
3715
  };
3691
3716
  if (p.listen) {
3692
3717
  const page2 = ctx.page;
3693
- if (!page2) return fail3("No active page. Use --cdp to connect first.");
3718
+ if (!page2) return fail4("No active page. Use --cdp to connect first.");
3694
3719
  const captures = [];
3695
3720
  const consoleMessages = [];
3696
3721
  const wsCaptures = [];
@@ -4347,7 +4372,7 @@ function parseMarkdownResults(rawText) {
4347
4372
 
4348
4373
  // src/commands/snapshot.ts
4349
4374
  import { z as z21 } from "zod";
4350
- import { ok as ok20, fail as fail4, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4375
+ import { ok as ok20, fail as fail5, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4351
4376
 
4352
4377
  // src/runtime/ref-store.ts
4353
4378
  var sessions = /* @__PURE__ */ new Map();
@@ -5303,7 +5328,7 @@ var snapshotCommand = registerCommand({
5303
5328
  persistSemantics(url, aria);
5304
5329
  return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5305
5330
  }
5306
- return fail4(`Unknown snapshot type: ${p.type}`);
5331
+ return fail5(`Unknown snapshot type: ${p.type}`);
5307
5332
  }
5308
5333
  });
5309
5334
  function persistSemantics(url, aria) {
@@ -5472,7 +5497,7 @@ var waitForCommand = registerCommand({
5472
5497
 
5473
5498
  // src/commands/tab.ts
5474
5499
  import { z as z23 } from "zod";
5475
- import { ok as ok22, fail as fail5 } from "@dyyz1993/xcli-core";
5500
+ import { ok as ok22, fail as fail6 } from "@dyyz1993/xcli-core";
5476
5501
  var TabParams = z23.object({
5477
5502
  subcommand: z23.enum(["list", "new", "close", "switch"]),
5478
5503
  url: z23.string().optional(),
@@ -5499,7 +5524,7 @@ var tabCommand = registerCommand({
5499
5524
  case "switch":
5500
5525
  return handleSwitch(p, pages, ctx);
5501
5526
  default:
5502
- return fail5(`Unknown subcommand: ${p.subcommand}`);
5527
+ return fail6(`Unknown subcommand: ${p.subcommand}`);
5503
5528
  }
5504
5529
  }
5505
5530
  });
@@ -5556,11 +5581,11 @@ async function handleNew(p, _pages, ctx) {
5556
5581
  }
5557
5582
  async function handleClose(p, pages, ctx) {
5558
5583
  if (pages.length <= 1) {
5559
- return fail5("Cannot close the last remaining tab");
5584
+ return fail6("Cannot close the last remaining tab");
5560
5585
  }
5561
5586
  const closeIndex = p.index ?? pages.indexOf(ctx.page);
5562
5587
  if (closeIndex < 0 || closeIndex >= pages.length) {
5563
- return fail5(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5588
+ return fail6(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5564
5589
  }
5565
5590
  const pageToClose = pages[closeIndex];
5566
5591
  const isActivePage = pageToClose === ctx.page;
@@ -5583,10 +5608,10 @@ async function handleClose(p, pages, ctx) {
5583
5608
  }
5584
5609
  async function handleSwitch(p, pages, ctx) {
5585
5610
  if (p.index === void 0) {
5586
- return fail5("Parameter --index is required for switch subcommand");
5611
+ return fail6("Parameter --index is required for switch subcommand");
5587
5612
  }
5588
5613
  if (p.index < 0 || p.index >= pages.length) {
5589
- return fail5(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5614
+ return fail6(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5590
5615
  }
5591
5616
  const targetPage = pages[p.index];
5592
5617
  await targetPage.bringToFront().catch(() => {
@@ -5832,7 +5857,7 @@ registerCommandDefinition("addinitscript", ["script"]);
5832
5857
 
5833
5858
  // src/commands/find.ts
5834
5859
  import { z as z25 } from "zod";
5835
- import { ok as ok24, fail as fail6, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5860
+ import { ok as ok24, fail as fail7, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5836
5861
  var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
5837
5862
  var findCommand = registerCommand({
5838
5863
  name: "find",
@@ -5873,7 +5898,7 @@ var findCommand = registerCommand({
5873
5898
  });
5874
5899
  const count = await locator.count();
5875
5900
  if (count === 0) {
5876
- return fail6(`No element found with ${p.strategy}="${p.value}"`);
5901
+ return fail7(`No element found with ${p.strategy}="${p.value}"`);
5877
5902
  }
5878
5903
  const tips = [];
5879
5904
  const target = selectTarget(locator, p.strategy);
@@ -5885,15 +5910,15 @@ var findCommand = registerCommand({
5885
5910
  await target.click({ timeout: p.timeout, force: true });
5886
5911
  return okWithTips({ matched: count, selector, action: "click" }, tips);
5887
5912
  } else if (actionName === "fill") {
5888
- if (actionValue === void 0) return fail6("find fill requires a value");
5913
+ if (actionValue === void 0) return fail7("find fill requires a value");
5889
5914
  await target.fill(actionValue, { timeout: p.timeout, force: true });
5890
5915
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
5891
5916
  } else if (actionName === "type") {
5892
- if (actionValue === void 0) return fail6("find type requires a value");
5917
+ if (actionValue === void 0) return fail7("find type requires a value");
5893
5918
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
5894
5919
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
5895
5920
  } else if (actionName === "select") {
5896
- if (actionValue === void 0) return fail6("find select requires a value");
5921
+ if (actionValue === void 0) return fail7("find select requires a value");
5897
5922
  await target.selectOption(actionValue);
5898
5923
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
5899
5924
  } else if (actionName === "hover") {
@@ -7317,7 +7342,7 @@ async function guardCheck(commandName) {
7317
7342
  }
7318
7343
  }
7319
7344
  function errorResult(message) {
7320
- return { ...fail7(message), duration: 0 };
7345
+ return { ...fail8(message), duration: 0 };
7321
7346
  }
7322
7347
  function tipsToMessages(tips) {
7323
7348
  if (!tips || tips.length === 0) return [];
@@ -7353,7 +7378,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7353
7378
  }
7354
7379
  let targetPageOverride = null;
7355
7380
  if (_target && extraOpts?.cdpEndpoint) {
7356
- const { findTargetPage } = await import("./browser-JP2LFPR2.js");
7381
+ const { findTargetPage } = await import("./browser-WMQRPYXX.js");
7357
7382
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7358
7383
  if (!targetPageOverride) {
7359
7384
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7557,7 +7582,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7557
7582
  duration,
7558
7583
  timestamp: start
7559
7584
  });
7560
- return { ...fail7(errorMessage), duration };
7585
+ return { ...fail8(errorMessage), duration };
7561
7586
  } finally {
7562
7587
  }
7563
7588
  }
@@ -7598,7 +7623,7 @@ async function executeChain(input, options) {
7598
7623
  results.push({
7599
7624
  command: cmdName,
7600
7625
  raw: cmdStr,
7601
- ...fail7(`Plugin "${cmdName}" requires a sub-command`),
7626
+ ...fail8(`Plugin "${cmdName}" requires a sub-command`),
7602
7627
  duration: 0
7603
7628
  });
7604
7629
  if (type === "and") {
@@ -7617,7 +7642,7 @@ async function executeChain(input, options) {
7617
7642
  results.push({
7618
7643
  command: cmdName,
7619
7644
  raw: cmdStr,
7620
- ...fail7(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7645
+ ...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7621
7646
  duration: 0
7622
7647
  });
7623
7648
  if (type === "and") {
@@ -7749,7 +7774,7 @@ async function executeChain(input, options) {
7749
7774
  results.push({
7750
7775
  command: `${cmdName} ${subCommand}`,
7751
7776
  raw: cmdStr,
7752
- ...fail7(errorMessage),
7777
+ ...fail8(errorMessage),
7753
7778
  duration: duration2
7754
7779
  });
7755
7780
  if (type === "and") {
@@ -7821,7 +7846,7 @@ async function executeChain(input, options) {
7821
7846
  }
7822
7847
  }
7823
7848
  function isChainInput(input) {
7824
- return /\s&&\s|\s;\s|\s,\s|\s\+\s|\s->\s/.test(input);
7849
+ return /\s&&\s|\|\|\s|\s;\s|\s,\s|\s\+\s|\s->\s/.test(input);
7825
7850
  }
7826
7851
 
7827
7852
  // src/context.ts
@@ -10313,6 +10338,17 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10313
10338
  };
10314
10339
  break;
10315
10340
  }
10341
+ case "tab": {
10342
+ const tabSub = args[0];
10343
+ const validSubs = ["list", "new", "close", "switch"];
10344
+ cmdName = "tab";
10345
+ params = {
10346
+ subcommand: validSubs.includes(tabSub) ? tabSub : options.subcommand,
10347
+ url: args[1] || options.url,
10348
+ index: options.index ? Number(options.index) : void 0
10349
+ };
10350
+ break;
10351
+ }
10316
10352
  default:
10317
10353
  cmdName = command;
10318
10354
  params = { ...options };
@@ -11135,6 +11171,7 @@ async function handleConvert(args, _mode) {
11135
11171
  const { generateJSScript: generateJSScript2, generatePythonScript: generatePythonScript2, generateBashScript: generateBashScript2 } = await import("./convert-R3XXYKC6.js");
11136
11172
  const content = fs6.readFileSync(filePath, "utf-8");
11137
11173
  const recording = yaml3.parse(content);
11174
+ if (recording.actions && !recording.events) recording.events = recording.actions;
11138
11175
  const ext = path5.extname(outputPath).toLowerCase();
11139
11176
  let script;
11140
11177
  if (ext === ".py") {
@@ -11146,7 +11183,7 @@ async function handleConvert(args, _mode) {
11146
11183
  }
11147
11184
  fs6.writeFileSync(outputPath, script);
11148
11185
  fs6.chmodSync(outputPath, 493);
11149
- const eventCount = (recording.events || []).length;
11186
+ const eventCount = (recording.events || recording.actions || []).length;
11150
11187
  console.log(`Converted ${filePath} -> ${outputPath}`);
11151
11188
  console.log(` Events: ${eventCount}, Start URL: ${recording.startUrl}`);
11152
11189
  console.log(` Run: ${ext === ".py" ? "python" : ext === ".sh" ? "./" : "node"} ${outputPath}`);
@@ -11157,7 +11194,7 @@ async function handleExtract(args, _mode) {
11157
11194
  console.error("Usage: xbrowser extract <recording.yaml>");
11158
11195
  process.exit(1);
11159
11196
  }
11160
- const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-2ZFW2MX7.js");
11197
+ const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-RM62AJXW.js");
11161
11198
  const { summary, outputPath } = extractAndSave2(filePath);
11162
11199
  printExtractSummary2(summary);
11163
11200
  console.log(`
@@ -11170,7 +11207,7 @@ async function handleFilter(args, _mode, options) {
11170
11207
  console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
11171
11208
  process.exit(1);
11172
11209
  }
11173
- const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-3JQWBM5F.js");
11210
+ const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-K6FGRJQU.js");
11174
11211
  const excludeArgs = args.slice(2).concat(
11175
11212
  Object.entries(options || {}).flatMap(
11176
11213
  ([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
@@ -12625,7 +12662,7 @@ function extractCdpFromArgv(argv) {
12625
12662
  return process.env.XBROWSER_CDP;
12626
12663
  }
12627
12664
  async function handleStdinMode(stdinCommands, argv) {
12628
- const chain = stdinCommands.join(" && ");
12665
+ const chain = stdinCommands.join(" ; ");
12629
12666
  const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
12630
12667
  const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
12631
12668
  const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
@@ -12888,6 +12925,13 @@ async function routeCommand(argvIn, stdinCommands) {
12888
12925
  for (const step of chainResult.steps) {
12889
12926
  if (step.success) {
12890
12927
  console.log(`[OK] ${step.raw}`);
12928
+ if (step.data && typeof step.data === "object") {
12929
+ const d = step.data;
12930
+ for (const [k, v] of Object.entries(d)) {
12931
+ if (k !== "ok" && k !== "success")
12932
+ console.log(` ${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`);
12933
+ }
12934
+ }
12891
12935
  if (step.tips?.length) {
12892
12936
  for (const tip of step.tips) {
12893
12937
  console.log(` \u{1F4A1} ${tip}`);
@@ -12897,6 +12941,9 @@ async function routeCommand(argvIn, stdinCommands) {
12897
12941
  console.error(`[FAIL] ${step.raw}: ${step.message}`);
12898
12942
  }
12899
12943
  }
12944
+ if (chainResult.stoppedReason) {
12945
+ console.error(`Stopped: ${chainResult.stoppedReason}`);
12946
+ }
12900
12947
  if (!chainResult.success) throw new Error("Command failed");
12901
12948
  return;
12902
12949
  }
@@ -16076,7 +16123,7 @@ var DataCollector = class {
16076
16123
  return results;
16077
16124
  }
16078
16125
  async createBrowserContext() {
16079
- const { launch } = await import("./cdp-driver-S5STYUZZ.js");
16126
+ const { launch } = await import("./cdp-driver-72HOBP4C.js");
16080
16127
  const { browser } = await launch({
16081
16128
  headless: true,
16082
16129
  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-S5STYUZZ.js");
34
+ const { launch } = await import("./cdp-driver-72HOBP4C.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.3.0",
3
+ "version": "1.3.1",
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": {