@xbrowser/cli 1.8.5 → 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.
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-VEDJ5XSQ.js";
6
6
  import {
7
7
  SessionRecorder
8
- } from "./chunk-BALOM2HW.js";
8
+ } from "./chunk-FL6DOSWV.js";
9
9
  import {
10
10
  closeEphemeralContext,
11
11
  closeSessionByName,
@@ -21,7 +21,7 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-TAHJCCME.js";
24
+ } from "./chunk-HXLMMSU3.js";
25
25
  import "./chunk-VJNMAWPZ.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
@@ -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 [];
@@ -6742,9 +6750,14 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6742
6750
  const { _target: _u, ...rest } = params;
6743
6751
  params = rest;
6744
6752
  }
6753
+ const _tabIndex = params._tabIndex;
6754
+ if (_tabIndex !== void 0) {
6755
+ const { _tabIndex: _u, ...rest } = params;
6756
+ params = rest;
6757
+ }
6745
6758
  let targetPageOverride = null;
6746
6759
  if (_target && extraOpts?.cdpEndpoint) {
6747
- const { findTargetPage } = await import("./browser-NSSCH4MJ.js");
6760
+ const { findTargetPage } = await import("./browser-LW2MDJE4.js");
6748
6761
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6749
6762
  if (!targetPageOverride) {
6750
6763
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -6816,6 +6829,16 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6816
6829
  cliName: "xbrowser",
6817
6830
  tips: new TipCollector2()
6818
6831
  };
6832
+ if (_tabIndex !== void 0 && session?.context) {
6833
+ const pages = session.context.pages();
6834
+ if (_tabIndex >= 0 && _tabIndex < pages.length) {
6835
+ const targetPage = pages[_tabIndex];
6836
+ await targetPage.bringToFront().catch(() => {
6837
+ });
6838
+ setActivePage(session, targetPage);
6839
+ ctx.page = targetPage;
6840
+ }
6841
+ }
6819
6842
  const start = Date.now();
6820
6843
  if (session) {
6821
6844
  streamCommandEvent(session.id, {
@@ -6844,7 +6867,17 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6844
6867
  if (hooks.length > 0 && session?.page) {
6845
6868
  await Promise.all(hooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: commandName, params })));
6846
6869
  }
6847
- const raw = await command.handler(params, ctx);
6870
+ let raw;
6871
+ const handlerPromise = command.handler(params, ctx);
6872
+ if (extraOpts?.timeout && extraOpts.timeout > 0) {
6873
+ const timeoutMs = extraOpts.timeout;
6874
+ const timeoutPromise = new Promise(
6875
+ (_, reject) => setTimeout(() => reject(new Error(`Command timed out after ${timeoutMs}ms`)), timeoutMs)
6876
+ );
6877
+ raw = await Promise.race([handlerPromise, timeoutPromise]);
6878
+ } else {
6879
+ raw = await handlerPromise;
6880
+ }
6848
6881
  const end = Date.now();
6849
6882
  const duration = end - start;
6850
6883
  let hookOutputs;
@@ -6948,7 +6981,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6948
6981
  duration,
6949
6982
  timestamp: start
6950
6983
  });
6951
- return { ...fail8(errorMessage), duration };
6984
+ return { ...fail9(errorMessage), duration };
6952
6985
  } finally {
6953
6986
  }
6954
6987
  }
@@ -6989,7 +7022,7 @@ async function executeChain(input, options) {
6989
7022
  results.push({
6990
7023
  command: cmdName,
6991
7024
  raw: cmdStr,
6992
- ...fail8(`Plugin "${cmdName}" requires a sub-command`),
7025
+ ...fail9(`Plugin "${cmdName}" requires a sub-command`),
6993
7026
  duration: 0
6994
7027
  });
6995
7028
  if (type === "and") {
@@ -7008,7 +7041,7 @@ async function executeChain(input, options) {
7008
7041
  results.push({
7009
7042
  command: cmdName,
7010
7043
  raw: cmdStr,
7011
- ...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7044
+ ...fail9(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7012
7045
  duration: 0
7013
7046
  });
7014
7047
  if (type === "and") {
@@ -7140,7 +7173,7 @@ async function executeChain(input, options) {
7140
7173
  results.push({
7141
7174
  command: `${cmdName} ${subCommand}`,
7142
7175
  raw: cmdStr,
7143
- ...fail8(errorMessage),
7176
+ ...fail9(errorMessage),
7144
7177
  duration: duration2
7145
7178
  });
7146
7179
  if (type === "and") {
package/dist/index.d.ts CHANGED
@@ -737,6 +737,7 @@ declare function setWSServer(server: WSServer | null): void;
737
737
  declare function executeCommand(commandName: string, params: Record<string, unknown>, sessionName?: string, extraOpts?: {
738
738
  cdpEndpoint?: string;
739
739
  skipCleanup?: boolean;
740
+ timeout?: number;
740
741
  }): Promise<ExecutionResult>;
741
742
  /**
742
743
  * Execute a chain of browser commands parsed from a string expression.