@xbrowser/cli 1.8.6 → 1.8.7

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
@@ -182,7 +182,7 @@ function asZodSchema(value) {
182
182
  // src/executor.ts
183
183
  import {
184
184
  ok as ok25,
185
- fail as fail8,
185
+ fail as fail9,
186
186
  isCommandResult,
187
187
  CompositeStorage as CompositeStorage2,
188
188
  TipCollector as TipCollector2,
@@ -2316,7 +2316,7 @@ var scrapeCommand = registerCommand({
2316
2316
  scope: "project",
2317
2317
  selectorParams: ["selector"],
2318
2318
  parameters: z15.object({
2319
- url: z15.string(),
2319
+ url: z15.string().optional(),
2320
2320
  selector: z15.string().optional(),
2321
2321
  timeout: z15.number().default(3e4),
2322
2322
  format: z15.enum(["markdown", "html", "text"]).default("markdown"),
@@ -2329,10 +2329,14 @@ var scrapeCommand = registerCommand({
2329
2329
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2330
2330
  const maxAttempts = p.retries + 1;
2331
2331
  try {
2332
+ const targetUrl = p.url || page.url();
2333
+ if (!targetUrl || targetUrl === "about:blank") {
2334
+ return fail3("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2335
+ }
2332
2336
  let lastError;
2333
2337
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2334
2338
  try {
2335
- await page.goto(p.url, { waitUntil: "commit", timeout: p.timeout });
2339
+ await page.goto(targetUrl, { waitUntil: "commit", timeout: p.timeout });
2336
2340
  await page.waitForSelector("body", { timeout: p.timeout }).catch(() => {
2337
2341
  });
2338
2342
  await page.waitForLoadState("networkidle", Math.min(p.timeout, 8e3)).catch(() => {
@@ -2393,7 +2397,7 @@ var scrapeCommand = registerCommand({
2393
2397
  return { url: location.href, title: document.title, navigation, tables, forms: forms.slice(0, 20), links: links.slice(0, 30), mainText };
2394
2398
  });
2395
2399
  try {
2396
- persistFromScrape(p.url, structured);
2400
+ persistFromScrape(targetUrl, structured);
2397
2401
  } catch {
2398
2402
  }
2399
2403
  if (p.mode === "smart") {
@@ -2494,7 +2498,7 @@ var scrapeCommand = registerCommand({
2494
2498
 
2495
2499
  // src/commands/map.ts
2496
2500
  import { z as z16 } from "zod";
2497
- import { ok as ok16 } from "@dyyz1993/xcli-core";
2501
+ import { ok as ok16, fail as fail4 } from "@dyyz1993/xcli-core";
2498
2502
 
2499
2503
  // src/utils/url.ts
2500
2504
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2730,7 +2734,7 @@ var mapCommand = registerCommand({
2730
2734
  description: "Discover all URLs on a website via sitemap and page link extraction",
2731
2735
  scope: "project",
2732
2736
  parameters: z16.object({
2733
- url: z16.string(),
2737
+ url: z16.string().optional(),
2734
2738
  search: z16.string().optional(),
2735
2739
  sitemap: z16.enum(["include", "only"]).optional(),
2736
2740
  includeSubdomains: z16.boolean().optional(),
@@ -2741,7 +2745,11 @@ var mapCommand = registerCommand({
2741
2745
  handler: async (p, ctx) => {
2742
2746
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2743
2747
  try {
2744
- const links = await discoverUrls(page, p.url, {
2748
+ const targetUrl = p.url || page.url();
2749
+ if (!targetUrl || targetUrl === "about:blank") {
2750
+ return fail4("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2751
+ }
2752
+ const links = await discoverUrls(page, targetUrl, {
2745
2753
  sitemap: p.sitemap,
2746
2754
  includeSubdomains: p.includeSubdomains,
2747
2755
  allowExternalLinks: p.allowExternalLinks,
@@ -3584,7 +3592,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3584
3592
 
3585
3593
  // src/commands/network.ts
3586
3594
  import { z as z19 } from "zod";
3587
- import { ok as ok19, fail as fail4 } from "@dyyz1993/xcli-core";
3595
+ import { ok as ok19, fail as fail5 } from "@dyyz1993/xcli-core";
3588
3596
  function extractPath2(url) {
3589
3597
  try {
3590
3598
  const u = new URL(url);
@@ -3749,7 +3757,7 @@ var networkCommand = registerCommand({
3749
3757
  };
3750
3758
  if (p.listen) {
3751
3759
  const page2 = ctx.page;
3752
- if (!page2) return fail4("No active page. Use --cdp to connect first.");
3760
+ if (!page2) return fail5("No active page. Use --cdp to connect first.");
3753
3761
  const captures = [];
3754
3762
  const consoleMessages = [];
3755
3763
  const wsCaptures = [];
@@ -4129,7 +4137,7 @@ var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
4129
4137
 
4130
4138
  // src/commands/snapshot.ts
4131
4139
  import { z as z21 } from "zod";
4132
- import { ok as ok20, fail as fail5, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4140
+ import { ok as ok20, fail as fail6, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4133
4141
 
4134
4142
  // src/runtime/ref-store.ts
4135
4143
  var sessions = /* @__PURE__ */ new Map();
@@ -5100,7 +5108,7 @@ var snapshotCommand = registerCommand({
5100
5108
  persistSemantics(url, aria);
5101
5109
  return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5102
5110
  }
5103
- return fail5(`Unknown snapshot type: ${p.type}`);
5111
+ return fail6(`Unknown snapshot type: ${p.type}`);
5104
5112
  }
5105
5113
  });
5106
5114
  function persistSemantics(url, aria) {
@@ -5269,7 +5277,7 @@ var waitForCommand = registerCommand({
5269
5277
 
5270
5278
  // src/commands/tab.ts
5271
5279
  import { z as z23 } from "zod";
5272
- import { ok as ok22, fail as fail6 } from "@dyyz1993/xcli-core";
5280
+ import { ok as ok22, fail as fail7 } from "@dyyz1993/xcli-core";
5273
5281
  var TabParams = z23.object({
5274
5282
  subcommand: z23.enum(["list", "new", "close", "switch"]),
5275
5283
  url: z23.string().optional(),
@@ -5286,7 +5294,7 @@ var tabCommand = registerCommand({
5286
5294
  }),
5287
5295
  handler: async (p, ctx) => {
5288
5296
  if (!ctx.browserContext) {
5289
- return fail6("No browser context available. Use --cdp to connect to a browser first.");
5297
+ return fail7("No browser context available. Use --cdp to connect to a browser first.");
5290
5298
  }
5291
5299
  const pages = ctx.browserContext.pages();
5292
5300
  switch (p.subcommand) {
@@ -5299,7 +5307,7 @@ var tabCommand = registerCommand({
5299
5307
  case "switch":
5300
5308
  return handleSwitch(p, pages, ctx);
5301
5309
  default:
5302
- return fail6(`Unknown subcommand: ${p.subcommand}`);
5310
+ return fail7(`Unknown subcommand: ${p.subcommand}`);
5303
5311
  }
5304
5312
  }
5305
5313
  });
@@ -5356,11 +5364,11 @@ async function handleNew(p, _pages, ctx) {
5356
5364
  }
5357
5365
  async function handleClose(p, pages, ctx) {
5358
5366
  if (pages.length <= 1) {
5359
- return fail6("Cannot close the last remaining tab");
5367
+ return fail7("Cannot close the last remaining tab");
5360
5368
  }
5361
5369
  const closeIndex = p.index ?? pages.indexOf(ctx.page);
5362
5370
  if (closeIndex < 0 || closeIndex >= pages.length) {
5363
- return fail6(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5371
+ return fail7(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5364
5372
  }
5365
5373
  const pageToClose = pages[closeIndex];
5366
5374
  const isActivePage = pageToClose === ctx.page;
@@ -5383,10 +5391,10 @@ async function handleClose(p, pages, ctx) {
5383
5391
  }
5384
5392
  async function handleSwitch(p, pages, ctx) {
5385
5393
  if (p.index === void 0) {
5386
- return fail6("Parameter --index is required for switch subcommand");
5394
+ return fail7("Parameter --index is required for switch subcommand");
5387
5395
  }
5388
5396
  if (p.index < 0 || p.index >= pages.length) {
5389
- return fail6(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5397
+ return fail7(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5390
5398
  }
5391
5399
  const targetPage = pages[p.index];
5392
5400
  await targetPage.bringToFront().catch(() => {
@@ -5638,7 +5646,7 @@ registerCommandDefinition("addinitscript", ["script"]);
5638
5646
 
5639
5647
  // src/commands/find.ts
5640
5648
  import { z as z25 } from "zod";
5641
- import { ok as ok24, fail as fail7, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5649
+ import { ok as ok24, fail as fail8, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5642
5650
  var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
5643
5651
  var findCommand = registerCommand({
5644
5652
  name: "find",
@@ -5679,7 +5687,7 @@ var findCommand = registerCommand({
5679
5687
  });
5680
5688
  const count = await locator.count();
5681
5689
  if (count === 0) {
5682
- return fail7(`No element found with ${p.strategy}="${p.value}"`);
5690
+ return fail8(`No element found with ${p.strategy}="${p.value}"`);
5683
5691
  }
5684
5692
  const tips = [];
5685
5693
  const target = selectTarget(locator, p.strategy);
@@ -5691,15 +5699,15 @@ var findCommand = registerCommand({
5691
5699
  await target.click({ timeout: p.timeout, force: true });
5692
5700
  return okWithTips({ matched: count, selector, action: "click" }, tips);
5693
5701
  } else if (actionName === "fill") {
5694
- if (actionValue === void 0) return fail7("find fill requires a value");
5702
+ if (actionValue === void 0) return fail8("find fill requires a value");
5695
5703
  await target.fill(actionValue, { timeout: p.timeout, force: true });
5696
5704
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
5697
5705
  } else if (actionName === "type") {
5698
- if (actionValue === void 0) return fail7("find type requires a value");
5706
+ if (actionValue === void 0) return fail8("find type requires a value");
5699
5707
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
5700
5708
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
5701
5709
  } else if (actionName === "select") {
5702
- if (actionValue === void 0) return fail7("find select requires a value");
5710
+ if (actionValue === void 0) return fail8("find select requires a value");
5703
5711
  await target.selectOption(actionValue);
5704
5712
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
5705
5713
  } else if (actionName === "hover") {
@@ -7135,7 +7143,7 @@ async function guardCheck(commandName) {
7135
7143
  }
7136
7144
  }
7137
7145
  function errorResult(message) {
7138
- return { ...fail8(message), duration: 0 };
7146
+ return { ...fail9(message), duration: 0 };
7139
7147
  }
7140
7148
  function tipsToMessages(tips) {
7141
7149
  if (!tips || tips.length === 0) return [];
@@ -7436,7 +7444,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7436
7444
  duration,
7437
7445
  timestamp: start
7438
7446
  });
7439
- return { ...fail8(errorMessage), duration };
7447
+ return { ...fail9(errorMessage), duration };
7440
7448
  } finally {
7441
7449
  }
7442
7450
  }
@@ -7477,7 +7485,7 @@ async function executeChain(input, options) {
7477
7485
  results.push({
7478
7486
  command: cmdName,
7479
7487
  raw: cmdStr,
7480
- ...fail8(`Plugin "${cmdName}" requires a sub-command`),
7488
+ ...fail9(`Plugin "${cmdName}" requires a sub-command`),
7481
7489
  duration: 0
7482
7490
  });
7483
7491
  if (type === "and") {
@@ -7496,7 +7504,7 @@ async function executeChain(input, options) {
7496
7504
  results.push({
7497
7505
  command: cmdName,
7498
7506
  raw: cmdStr,
7499
- ...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7507
+ ...fail9(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7500
7508
  duration: 0
7501
7509
  });
7502
7510
  if (type === "and") {
@@ -7628,7 +7636,7 @@ async function executeChain(input, options) {
7628
7636
  results.push({
7629
7637
  command: `${cmdName} ${subCommand}`,
7630
7638
  raw: cmdStr,
7631
- ...fail8(errorMessage),
7639
+ ...fail9(errorMessage),
7632
7640
  duration: duration2
7633
7641
  });
7634
7642
  if (type === "and") {
@@ -55,7 +55,7 @@ import {
55
55
  // src/executor.ts
56
56
  import {
57
57
  ok as ok25,
58
- fail as fail8,
58
+ fail as fail9,
59
59
  isCommandResult,
60
60
  CompositeStorage as CompositeStorage2,
61
61
  TipCollector as TipCollector2,
@@ -2283,7 +2283,7 @@ var scrapeCommand = registerCommand({
2283
2283
  scope: "project",
2284
2284
  selectorParams: ["selector"],
2285
2285
  parameters: z15.object({
2286
- url: z15.string(),
2286
+ url: z15.string().optional(),
2287
2287
  selector: z15.string().optional(),
2288
2288
  timeout: z15.number().default(3e4),
2289
2289
  format: z15.enum(["markdown", "html", "text"]).default("markdown"),
@@ -2296,10 +2296,14 @@ var scrapeCommand = registerCommand({
2296
2296
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2297
2297
  const maxAttempts = p.retries + 1;
2298
2298
  try {
2299
+ const targetUrl = p.url || page.url();
2300
+ if (!targetUrl || targetUrl === "about:blank") {
2301
+ return fail3("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2302
+ }
2299
2303
  let lastError;
2300
2304
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2301
2305
  try {
2302
- await page.goto(p.url, { waitUntil: "commit", timeout: p.timeout });
2306
+ await page.goto(targetUrl, { waitUntil: "commit", timeout: p.timeout });
2303
2307
  await page.waitForSelector("body", { timeout: p.timeout }).catch(() => {
2304
2308
  });
2305
2309
  await page.waitForLoadState("networkidle", Math.min(p.timeout, 8e3)).catch(() => {
@@ -2360,7 +2364,7 @@ var scrapeCommand = registerCommand({
2360
2364
  return { url: location.href, title: document.title, navigation, tables, forms: forms.slice(0, 20), links: links.slice(0, 30), mainText };
2361
2365
  });
2362
2366
  try {
2363
- persistFromScrape(p.url, structured);
2367
+ persistFromScrape(targetUrl, structured);
2364
2368
  } catch {
2365
2369
  }
2366
2370
  if (p.mode === "smart") {
@@ -2461,7 +2465,7 @@ var scrapeCommand = registerCommand({
2461
2465
 
2462
2466
  // src/commands/map.ts
2463
2467
  import { z as z16 } from "zod";
2464
- import { ok as ok16 } from "@dyyz1993/xcli-core";
2468
+ import { ok as ok16, fail as fail4 } from "@dyyz1993/xcli-core";
2465
2469
 
2466
2470
  // src/utils/url.ts
2467
2471
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2697,7 +2701,7 @@ var mapCommand = registerCommand({
2697
2701
  description: "Discover all URLs on a website via sitemap and page link extraction",
2698
2702
  scope: "project",
2699
2703
  parameters: z16.object({
2700
- url: z16.string(),
2704
+ url: z16.string().optional(),
2701
2705
  search: z16.string().optional(),
2702
2706
  sitemap: z16.enum(["include", "only"]).optional(),
2703
2707
  includeSubdomains: z16.boolean().optional(),
@@ -2708,7 +2712,11 @@ var mapCommand = registerCommand({
2708
2712
  handler: async (p, ctx) => {
2709
2713
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2710
2714
  try {
2711
- const links = await discoverUrls(page, p.url, {
2715
+ const targetUrl = p.url || page.url();
2716
+ if (!targetUrl || targetUrl === "about:blank") {
2717
+ return fail4("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2718
+ }
2719
+ const links = await discoverUrls(page, targetUrl, {
2712
2720
  sitemap: p.sitemap,
2713
2721
  includeSubdomains: p.includeSubdomains,
2714
2722
  allowExternalLinks: p.allowExternalLinks,
@@ -3551,7 +3559,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3551
3559
 
3552
3560
  // src/commands/network.ts
3553
3561
  import { z as z19 } from "zod";
3554
- import { ok as ok19, fail as fail4 } from "@dyyz1993/xcli-core";
3562
+ import { ok as ok19, fail as fail5 } from "@dyyz1993/xcli-core";
3555
3563
  function extractPath2(url) {
3556
3564
  try {
3557
3565
  const u = new URL(url);
@@ -3716,7 +3724,7 @@ var networkCommand = registerCommand({
3716
3724
  };
3717
3725
  if (p.listen) {
3718
3726
  const page2 = ctx.page;
3719
- if (!page2) return fail4("No active page. Use --cdp to connect first.");
3727
+ if (!page2) return fail5("No active page. Use --cdp to connect first.");
3720
3728
  const captures = [];
3721
3729
  const consoleMessages = [];
3722
3730
  const wsCaptures = [];
@@ -4096,7 +4104,7 @@ var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
4096
4104
 
4097
4105
  // src/commands/snapshot.ts
4098
4106
  import { z as z21 } from "zod";
4099
- import { ok as ok20, fail as fail5, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4107
+ import { ok as ok20, fail as fail6, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4100
4108
 
4101
4109
  // src/runtime/ref-store.ts
4102
4110
  var sessions = /* @__PURE__ */ new Map();
@@ -5067,7 +5075,7 @@ var snapshotCommand = registerCommand({
5067
5075
  persistSemantics(url, aria);
5068
5076
  return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5069
5077
  }
5070
- return fail5(`Unknown snapshot type: ${p.type}`);
5078
+ return fail6(`Unknown snapshot type: ${p.type}`);
5071
5079
  }
5072
5080
  });
5073
5081
  function persistSemantics(url, aria) {
@@ -5236,7 +5244,7 @@ var waitForCommand = registerCommand({
5236
5244
 
5237
5245
  // src/commands/tab.ts
5238
5246
  import { z as z23 } from "zod";
5239
- import { ok as ok22, fail as fail6 } from "@dyyz1993/xcli-core";
5247
+ import { ok as ok22, fail as fail7 } from "@dyyz1993/xcli-core";
5240
5248
  var TabParams = z23.object({
5241
5249
  subcommand: z23.enum(["list", "new", "close", "switch"]),
5242
5250
  url: z23.string().optional(),
@@ -5253,7 +5261,7 @@ var tabCommand = registerCommand({
5253
5261
  }),
5254
5262
  handler: async (p, ctx) => {
5255
5263
  if (!ctx.browserContext) {
5256
- return fail6("No browser context available. Use --cdp to connect to a browser first.");
5264
+ return fail7("No browser context available. Use --cdp to connect to a browser first.");
5257
5265
  }
5258
5266
  const pages = ctx.browserContext.pages();
5259
5267
  switch (p.subcommand) {
@@ -5266,7 +5274,7 @@ var tabCommand = registerCommand({
5266
5274
  case "switch":
5267
5275
  return handleSwitch(p, pages, ctx);
5268
5276
  default:
5269
- return fail6(`Unknown subcommand: ${p.subcommand}`);
5277
+ return fail7(`Unknown subcommand: ${p.subcommand}`);
5270
5278
  }
5271
5279
  }
5272
5280
  });
@@ -5323,11 +5331,11 @@ async function handleNew(p, _pages, ctx) {
5323
5331
  }
5324
5332
  async function handleClose(p, pages, ctx) {
5325
5333
  if (pages.length <= 1) {
5326
- return fail6("Cannot close the last remaining tab");
5334
+ return fail7("Cannot close the last remaining tab");
5327
5335
  }
5328
5336
  const closeIndex = p.index ?? pages.indexOf(ctx.page);
5329
5337
  if (closeIndex < 0 || closeIndex >= pages.length) {
5330
- return fail6(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5338
+ return fail7(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5331
5339
  }
5332
5340
  const pageToClose = pages[closeIndex];
5333
5341
  const isActivePage = pageToClose === ctx.page;
@@ -5350,10 +5358,10 @@ async function handleClose(p, pages, ctx) {
5350
5358
  }
5351
5359
  async function handleSwitch(p, pages, ctx) {
5352
5360
  if (p.index === void 0) {
5353
- return fail6("Parameter --index is required for switch subcommand");
5361
+ return fail7("Parameter --index is required for switch subcommand");
5354
5362
  }
5355
5363
  if (p.index < 0 || p.index >= pages.length) {
5356
- return fail6(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5364
+ return fail7(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5357
5365
  }
5358
5366
  const targetPage = pages[p.index];
5359
5367
  await targetPage.bringToFront().catch(() => {
@@ -5605,7 +5613,7 @@ registerCommandDefinition("addinitscript", ["script"]);
5605
5613
 
5606
5614
  // src/commands/find.ts
5607
5615
  import { z as z25 } from "zod";
5608
- import { ok as ok24, fail as fail7, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5616
+ import { ok as ok24, fail as fail8, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5609
5617
  var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
5610
5618
  var findCommand = registerCommand({
5611
5619
  name: "find",
@@ -5646,7 +5654,7 @@ var findCommand = registerCommand({
5646
5654
  });
5647
5655
  const count = await locator.count();
5648
5656
  if (count === 0) {
5649
- return fail7(`No element found with ${p.strategy}="${p.value}"`);
5657
+ return fail8(`No element found with ${p.strategy}="${p.value}"`);
5650
5658
  }
5651
5659
  const tips = [];
5652
5660
  const target = selectTarget(locator, p.strategy);
@@ -5658,15 +5666,15 @@ var findCommand = registerCommand({
5658
5666
  await target.click({ timeout: p.timeout, force: true });
5659
5667
  return okWithTips({ matched: count, selector, action: "click" }, tips);
5660
5668
  } else if (actionName === "fill") {
5661
- if (actionValue === void 0) return fail7("find fill requires a value");
5669
+ if (actionValue === void 0) return fail8("find fill requires a value");
5662
5670
  await target.fill(actionValue, { timeout: p.timeout, force: true });
5663
5671
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
5664
5672
  } else if (actionName === "type") {
5665
- if (actionValue === void 0) return fail7("find type requires a value");
5673
+ if (actionValue === void 0) return fail8("find type requires a value");
5666
5674
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
5667
5675
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
5668
5676
  } else if (actionName === "select") {
5669
- if (actionValue === void 0) return fail7("find select requires a value");
5677
+ if (actionValue === void 0) return fail8("find select requires a value");
5670
5678
  await target.selectOption(actionValue);
5671
5679
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
5672
5680
  } else if (actionName === "hover") {
@@ -6672,7 +6680,7 @@ async function guardCheck(commandName) {
6672
6680
  }
6673
6681
  }
6674
6682
  function errorResult(message) {
6675
- return { ...fail8(message), duration: 0 };
6683
+ return { ...fail9(message), duration: 0 };
6676
6684
  }
6677
6685
  function tipsToMessages(tips) {
6678
6686
  if (!tips || tips.length === 0) return [];
@@ -6973,7 +6981,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6973
6981
  duration,
6974
6982
  timestamp: start
6975
6983
  });
6976
- return { ...fail8(errorMessage), duration };
6984
+ return { ...fail9(errorMessage), duration };
6977
6985
  } finally {
6978
6986
  }
6979
6987
  }
@@ -7014,7 +7022,7 @@ async function executeChain(input, options) {
7014
7022
  results.push({
7015
7023
  command: cmdName,
7016
7024
  raw: cmdStr,
7017
- ...fail8(`Plugin "${cmdName}" requires a sub-command`),
7025
+ ...fail9(`Plugin "${cmdName}" requires a sub-command`),
7018
7026
  duration: 0
7019
7027
  });
7020
7028
  if (type === "and") {
@@ -7033,7 +7041,7 @@ async function executeChain(input, options) {
7033
7041
  results.push({
7034
7042
  command: cmdName,
7035
7043
  raw: cmdStr,
7036
- ...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7044
+ ...fail9(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7037
7045
  duration: 0
7038
7046
  });
7039
7047
  if (type === "and") {
@@ -7165,7 +7173,7 @@ async function executeChain(input, options) {
7165
7173
  results.push({
7166
7174
  command: `${cmdName} ${subCommand}`,
7167
7175
  raw: cmdStr,
7168
- ...fail8(errorMessage),
7176
+ ...fail9(errorMessage),
7169
7177
  duration: duration2
7170
7178
  });
7171
7179
  if (type === "and") {
package/dist/index.js CHANGED
@@ -109,7 +109,7 @@ import {
109
109
  // src/executor.ts
110
110
  import {
111
111
  ok as ok25,
112
- fail as fail8,
112
+ fail as fail9,
113
113
  isCommandResult,
114
114
  CompositeStorage as CompositeStorage2,
115
115
  TipCollector as TipCollector2,
@@ -2356,7 +2356,7 @@ var scrapeCommand = registerCommand({
2356
2356
  scope: "project",
2357
2357
  selectorParams: ["selector"],
2358
2358
  parameters: z15.object({
2359
- url: z15.string(),
2359
+ url: z15.string().optional(),
2360
2360
  selector: z15.string().optional(),
2361
2361
  timeout: z15.number().default(3e4),
2362
2362
  format: z15.enum(["markdown", "html", "text"]).default("markdown"),
@@ -2369,10 +2369,14 @@ var scrapeCommand = registerCommand({
2369
2369
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2370
2370
  const maxAttempts = p.retries + 1;
2371
2371
  try {
2372
+ const targetUrl = p.url || page.url();
2373
+ if (!targetUrl || targetUrl === "about:blank") {
2374
+ return fail3("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2375
+ }
2372
2376
  let lastError;
2373
2377
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2374
2378
  try {
2375
- await page.goto(p.url, { waitUntil: "commit", timeout: p.timeout });
2379
+ await page.goto(targetUrl, { waitUntil: "commit", timeout: p.timeout });
2376
2380
  await page.waitForSelector("body", { timeout: p.timeout }).catch(() => {
2377
2381
  });
2378
2382
  await page.waitForLoadState("networkidle", Math.min(p.timeout, 8e3)).catch(() => {
@@ -2433,7 +2437,7 @@ var scrapeCommand = registerCommand({
2433
2437
  return { url: location.href, title: document.title, navigation, tables, forms: forms.slice(0, 20), links: links.slice(0, 30), mainText };
2434
2438
  });
2435
2439
  try {
2436
- persistFromScrape(p.url, structured);
2440
+ persistFromScrape(targetUrl, structured);
2437
2441
  } catch {
2438
2442
  }
2439
2443
  if (p.mode === "smart") {
@@ -2534,7 +2538,7 @@ var scrapeCommand = registerCommand({
2534
2538
 
2535
2539
  // src/commands/map.ts
2536
2540
  import { z as z16 } from "zod";
2537
- import { ok as ok16 } from "@dyyz1993/xcli-core";
2541
+ import { ok as ok16, fail as fail4 } from "@dyyz1993/xcli-core";
2538
2542
 
2539
2543
  // src/utils/url.ts
2540
2544
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2770,7 +2774,7 @@ var mapCommand = registerCommand({
2770
2774
  description: "Discover all URLs on a website via sitemap and page link extraction",
2771
2775
  scope: "project",
2772
2776
  parameters: z16.object({
2773
- url: z16.string(),
2777
+ url: z16.string().optional(),
2774
2778
  search: z16.string().optional(),
2775
2779
  sitemap: z16.enum(["include", "only"]).optional(),
2776
2780
  includeSubdomains: z16.boolean().optional(),
@@ -2781,7 +2785,11 @@ var mapCommand = registerCommand({
2781
2785
  handler: async (p, ctx) => {
2782
2786
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2783
2787
  try {
2784
- const links = await discoverUrls(page, p.url, {
2788
+ const targetUrl = p.url || page.url();
2789
+ if (!targetUrl || targetUrl === "about:blank") {
2790
+ return fail4("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2791
+ }
2792
+ const links = await discoverUrls(page, targetUrl, {
2785
2793
  sitemap: p.sitemap,
2786
2794
  includeSubdomains: p.includeSubdomains,
2787
2795
  allowExternalLinks: p.allowExternalLinks,
@@ -3624,7 +3632,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3624
3632
 
3625
3633
  // src/commands/network.ts
3626
3634
  import { z as z19 } from "zod";
3627
- import { ok as ok19, fail as fail4 } from "@dyyz1993/xcli-core";
3635
+ import { ok as ok19, fail as fail5 } from "@dyyz1993/xcli-core";
3628
3636
  function extractPath2(url) {
3629
3637
  try {
3630
3638
  const u = new URL(url);
@@ -3789,7 +3797,7 @@ var networkCommand = registerCommand({
3789
3797
  };
3790
3798
  if (p.listen) {
3791
3799
  const page2 = ctx.page;
3792
- if (!page2) return fail4("No active page. Use --cdp to connect first.");
3800
+ if (!page2) return fail5("No active page. Use --cdp to connect first.");
3793
3801
  const captures = [];
3794
3802
  const consoleMessages = [];
3795
3803
  const wsCaptures = [];
@@ -4446,7 +4454,7 @@ function parseMarkdownResults(rawText) {
4446
4454
 
4447
4455
  // src/commands/snapshot.ts
4448
4456
  import { z as z21 } from "zod";
4449
- import { ok as ok20, fail as fail5, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4457
+ import { ok as ok20, fail as fail6, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4450
4458
 
4451
4459
  // src/runtime/ref-store.ts
4452
4460
  var sessions = /* @__PURE__ */ new Map();
@@ -5417,7 +5425,7 @@ var snapshotCommand = registerCommand({
5417
5425
  persistSemantics(url, aria);
5418
5426
  return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5419
5427
  }
5420
- return fail5(`Unknown snapshot type: ${p.type}`);
5428
+ return fail6(`Unknown snapshot type: ${p.type}`);
5421
5429
  }
5422
5430
  });
5423
5431
  function persistSemantics(url, aria) {
@@ -5586,7 +5594,7 @@ var waitForCommand = registerCommand({
5586
5594
 
5587
5595
  // src/commands/tab.ts
5588
5596
  import { z as z23 } from "zod";
5589
- import { ok as ok22, fail as fail6 } from "@dyyz1993/xcli-core";
5597
+ import { ok as ok22, fail as fail7 } from "@dyyz1993/xcli-core";
5590
5598
  var TabParams = z23.object({
5591
5599
  subcommand: z23.enum(["list", "new", "close", "switch"]),
5592
5600
  url: z23.string().optional(),
@@ -5603,7 +5611,7 @@ var tabCommand = registerCommand({
5603
5611
  }),
5604
5612
  handler: async (p, ctx) => {
5605
5613
  if (!ctx.browserContext) {
5606
- return fail6("No browser context available. Use --cdp to connect to a browser first.");
5614
+ return fail7("No browser context available. Use --cdp to connect to a browser first.");
5607
5615
  }
5608
5616
  const pages = ctx.browserContext.pages();
5609
5617
  switch (p.subcommand) {
@@ -5616,7 +5624,7 @@ var tabCommand = registerCommand({
5616
5624
  case "switch":
5617
5625
  return handleSwitch(p, pages, ctx);
5618
5626
  default:
5619
- return fail6(`Unknown subcommand: ${p.subcommand}`);
5627
+ return fail7(`Unknown subcommand: ${p.subcommand}`);
5620
5628
  }
5621
5629
  }
5622
5630
  });
@@ -5673,11 +5681,11 @@ async function handleNew(p, _pages, ctx) {
5673
5681
  }
5674
5682
  async function handleClose(p, pages, ctx) {
5675
5683
  if (pages.length <= 1) {
5676
- return fail6("Cannot close the last remaining tab");
5684
+ return fail7("Cannot close the last remaining tab");
5677
5685
  }
5678
5686
  const closeIndex = p.index ?? pages.indexOf(ctx.page);
5679
5687
  if (closeIndex < 0 || closeIndex >= pages.length) {
5680
- return fail6(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5688
+ return fail7(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
5681
5689
  }
5682
5690
  const pageToClose = pages[closeIndex];
5683
5691
  const isActivePage = pageToClose === ctx.page;
@@ -5700,10 +5708,10 @@ async function handleClose(p, pages, ctx) {
5700
5708
  }
5701
5709
  async function handleSwitch(p, pages, ctx) {
5702
5710
  if (p.index === void 0) {
5703
- return fail6("Parameter --index is required for switch subcommand");
5711
+ return fail7("Parameter --index is required for switch subcommand");
5704
5712
  }
5705
5713
  if (p.index < 0 || p.index >= pages.length) {
5706
- return fail6(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5714
+ return fail7(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5707
5715
  }
5708
5716
  const targetPage = pages[p.index];
5709
5717
  await targetPage.bringToFront().catch(() => {
@@ -5955,7 +5963,7 @@ registerCommandDefinition("addinitscript", ["script"]);
5955
5963
 
5956
5964
  // src/commands/find.ts
5957
5965
  import { z as z25 } from "zod";
5958
- import { ok as ok24, fail as fail7, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5966
+ import { ok as ok24, fail as fail8, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5959
5967
  var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
5960
5968
  var findCommand = registerCommand({
5961
5969
  name: "find",
@@ -5996,7 +6004,7 @@ var findCommand = registerCommand({
5996
6004
  });
5997
6005
  const count = await locator.count();
5998
6006
  if (count === 0) {
5999
- return fail7(`No element found with ${p.strategy}="${p.value}"`);
6007
+ return fail8(`No element found with ${p.strategy}="${p.value}"`);
6000
6008
  }
6001
6009
  const tips = [];
6002
6010
  const target = selectTarget(locator, p.strategy);
@@ -6008,15 +6016,15 @@ var findCommand = registerCommand({
6008
6016
  await target.click({ timeout: p.timeout, force: true });
6009
6017
  return okWithTips({ matched: count, selector, action: "click" }, tips);
6010
6018
  } else if (actionName === "fill") {
6011
- if (actionValue === void 0) return fail7("find fill requires a value");
6019
+ if (actionValue === void 0) return fail8("find fill requires a value");
6012
6020
  await target.fill(actionValue, { timeout: p.timeout, force: true });
6013
6021
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
6014
6022
  } else if (actionName === "type") {
6015
- if (actionValue === void 0) return fail7("find type requires a value");
6023
+ if (actionValue === void 0) return fail8("find type requires a value");
6016
6024
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
6017
6025
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
6018
6026
  } else if (actionName === "select") {
6019
- if (actionValue === void 0) return fail7("find select requires a value");
6027
+ if (actionValue === void 0) return fail8("find select requires a value");
6020
6028
  await target.selectOption(actionValue);
6021
6029
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
6022
6030
  } else if (actionName === "hover") {
@@ -7452,7 +7460,7 @@ async function guardCheck(commandName) {
7452
7460
  }
7453
7461
  }
7454
7462
  function errorResult(message) {
7455
- return { ...fail8(message), duration: 0 };
7463
+ return { ...fail9(message), duration: 0 };
7456
7464
  }
7457
7465
  function tipsToMessages(tips) {
7458
7466
  if (!tips || tips.length === 0) return [];
@@ -7756,7 +7764,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7756
7764
  duration,
7757
7765
  timestamp: start
7758
7766
  });
7759
- return { ...fail8(errorMessage), duration };
7767
+ return { ...fail9(errorMessage), duration };
7760
7768
  } finally {
7761
7769
  }
7762
7770
  }
@@ -7797,7 +7805,7 @@ async function executeChain(input, options) {
7797
7805
  results.push({
7798
7806
  command: cmdName,
7799
7807
  raw: cmdStr,
7800
- ...fail8(`Plugin "${cmdName}" requires a sub-command`),
7808
+ ...fail9(`Plugin "${cmdName}" requires a sub-command`),
7801
7809
  duration: 0
7802
7810
  });
7803
7811
  if (type === "and") {
@@ -7816,7 +7824,7 @@ async function executeChain(input, options) {
7816
7824
  results.push({
7817
7825
  command: cmdName,
7818
7826
  raw: cmdStr,
7819
- ...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7827
+ ...fail9(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7820
7828
  duration: 0
7821
7829
  });
7822
7830
  if (type === "and") {
@@ -7948,7 +7956,7 @@ async function executeChain(input, options) {
7948
7956
  results.push({
7949
7957
  command: `${cmdName} ${subCommand}`,
7950
7958
  raw: cmdStr,
7951
- ...fail8(errorMessage),
7959
+ ...fail9(errorMessage),
7952
7960
  duration: duration2
7953
7961
  });
7954
7962
  if (type === "and") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.8.6",
3
+ "version": "1.8.7",
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": {