@xbrowser/cli 1.1.0 → 1.1.2

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.
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-HRTXMFW4.js";
23
+ } from "./chunk-XVZ6NKRJ.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-EUHVZVVL.js";
24
- import "./chunk-IX4JY6OO.js";
23
+ } from "./chunk-OH7CB2P6.js";
24
+ import "./chunk-E5WWMKXB.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-ABXMBNQ6.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-JUNEBEGF.js";
24
- import "./chunk-IX4JY6OO.js";
23
+ } from "./chunk-NDAMCPIJ.js";
24
+ import "./chunk-E5WWMKXB.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-KFQGP6VL.js";
@@ -2188,8 +2188,12 @@ var XBContextImpl = class {
2188
2188
  const result = await this.conn.send("Storage.getCookies", params);
2189
2189
  return result.cookies;
2190
2190
  } catch {
2191
- const result = await this.conn.send("Network.getCookies", params);
2192
- return result.cookies;
2191
+ try {
2192
+ const result = await this.conn.send("Network.getCookies", params);
2193
+ return result.cookies;
2194
+ } catch {
2195
+ return [];
2196
+ }
2193
2197
  }
2194
2198
  }
2195
2199
  async addCookies(cookies) {
@@ -2543,6 +2547,7 @@ var CDPConnection = class extends EventEmitter4 {
2543
2547
  defaultSessionId;
2544
2548
  constructor(wsOrUrl, sessionId) {
2545
2549
  super();
2550
+ this.setMaxListeners(0);
2546
2551
  this.defaultSessionId = sessionId;
2547
2552
  if (typeof wsOrUrl === "string") {
2548
2553
  this.ws = new WebSocket(wsOrUrl);
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-IX4JY6OO.js";
17
+ } from "./chunk-E5WWMKXB.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -2187,8 +2187,12 @@ var XBContextImpl = class {
2187
2187
  const result = await this.conn.send("Storage.getCookies", params);
2188
2188
  return result.cookies;
2189
2189
  } catch {
2190
- const result = await this.conn.send("Network.getCookies", params);
2191
- return result.cookies;
2190
+ try {
2191
+ const result = await this.conn.send("Network.getCookies", params);
2192
+ return result.cookies;
2193
+ } catch {
2194
+ return [];
2195
+ }
2192
2196
  }
2193
2197
  }
2194
2198
  async addCookies(cookies) {
@@ -2542,6 +2546,7 @@ var CDPConnection = class extends EventEmitter4 {
2542
2546
  defaultSessionId;
2543
2547
  constructor(wsOrUrl, sessionId) {
2544
2548
  super();
2549
+ this.setMaxListeners(0);
2545
2550
  this.defaultSessionId = sessionId;
2546
2551
  if (typeof wsOrUrl === "string") {
2547
2552
  this.ws = new WebSocket(wsOrUrl);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-IX4JY6OO.js";
3
+ } from "./chunk-E5WWMKXB.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -1538,7 +1538,7 @@ async function getCDPTargets(cdpEndpoint) {
1538
1538
  }
1539
1539
  async function findTargetPage(cdpEndpoint, target) {
1540
1540
  const targets = await getCDPTargets(cdpEndpoint);
1541
- const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
1541
+ const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
1542
1542
  const byId = pages.find((t) => t.id === target);
1543
1543
  if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
1544
1544
  const lowerTarget = target.toLowerCase();
@@ -1661,7 +1661,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
1661
1661
  const pages = ctx.pages();
1662
1662
  for (const p of pages) {
1663
1663
  const pUrl = p.url();
1664
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
1664
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
1665
1665
  if (targetHostname && pUrl.includes(targetHostname)) {
1666
1666
  page = p;
1667
1667
  break;
@@ -1677,7 +1677,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
1677
1677
  if (!page) {
1678
1678
  const targets = await getCDPTargets(ep);
1679
1679
  const matchTarget = targets.find(
1680
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (targetHostname ? t.url.includes(targetHostname) : true)
1680
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (targetHostname ? t.url.includes(targetHostname) : true)
1681
1681
  );
1682
1682
  if (matchTarget && matchTarget.url) {
1683
1683
  page = await context.newPage();
@@ -1745,7 +1745,7 @@ async function createEphemeralContext(options) {
1745
1745
  const allPages = ctx.pages();
1746
1746
  const existingPages = allPages.filter((p) => {
1747
1747
  const url = p.url();
1748
- return url !== "about:blank" && !url.startsWith("chrome://");
1748
+ return url !== "about:blank" && !url.startsWith("chrome://") && !url.startsWith("chrome-untrusted://") && !url.startsWith("chrome-error://");
1749
1749
  });
1750
1750
  const page2 = existingPages.length > 0 ? existingPages[0] : allPages.length > 0 ? allPages[0] : await ctx.newPage();
1751
1751
  resetIdleTimer();
@@ -1916,7 +1916,7 @@ async function createSession(name, url, options) {
1916
1916
  const pages = ctx.pages();
1917
1917
  for (const p of pages) {
1918
1918
  const pUrl = p.url();
1919
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && pUrl.includes(targetHostname)) {
1919
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
1920
1920
  targetPage = p;
1921
1921
  break;
1922
1922
  }
@@ -1929,7 +1929,7 @@ async function createSession(name, url, options) {
1929
1929
  const pages = ctx.pages();
1930
1930
  for (const p of pages) {
1931
1931
  const pUrl = p.url();
1932
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
1932
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
1933
1933
  targetPage = p;
1934
1934
  break;
1935
1935
  }
@@ -1940,7 +1940,7 @@ async function createSession(name, url, options) {
1940
1940
  if (!targetPage && options?.cdpEndpoint) {
1941
1941
  const targets = await getCDPTargets(options.cdpEndpoint);
1942
1942
  const matchTarget = targets.find(
1943
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (url ? t.url.includes(new URL(url).hostname) : true)
1943
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
1944
1944
  );
1945
1945
  if (matchTarget && matchTarget.url) {
1946
1946
  targetPage = await context.newPage();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-IX4JY6OO.js";
3
+ } from "./chunk-E5WWMKXB.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -173,7 +173,7 @@ async function getCDPTargets(cdpEndpoint) {
173
173
  }
174
174
  async function findTargetPage(cdpEndpoint, target) {
175
175
  const targets = await getCDPTargets(cdpEndpoint);
176
- const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
176
+ const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
177
177
  const byId = pages.find((t) => t.id === target);
178
178
  if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
179
179
  const lowerTarget = target.toLowerCase();
@@ -296,7 +296,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
296
296
  const pages = ctx.pages();
297
297
  for (const p of pages) {
298
298
  const pUrl = p.url();
299
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
299
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
300
300
  if (targetHostname && pUrl.includes(targetHostname)) {
301
301
  page = p;
302
302
  break;
@@ -312,7 +312,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
312
312
  if (!page) {
313
313
  const targets = await getCDPTargets(ep);
314
314
  const matchTarget = targets.find(
315
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (targetHostname ? t.url.includes(targetHostname) : true)
315
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (targetHostname ? t.url.includes(targetHostname) : true)
316
316
  );
317
317
  if (matchTarget && matchTarget.url) {
318
318
  page = await context.newPage();
@@ -380,7 +380,7 @@ async function createEphemeralContext(options) {
380
380
  const allPages = ctx.pages();
381
381
  const existingPages = allPages.filter((p) => {
382
382
  const url = p.url();
383
- return url !== "about:blank" && !url.startsWith("chrome://");
383
+ return url !== "about:blank" && !url.startsWith("chrome://") && !url.startsWith("chrome-untrusted://") && !url.startsWith("chrome-error://");
384
384
  });
385
385
  const page2 = existingPages.length > 0 ? existingPages[0] : allPages.length > 0 ? allPages[0] : await ctx.newPage();
386
386
  resetIdleTimer();
@@ -551,7 +551,7 @@ async function createSession(name, url, options) {
551
551
  const pages = ctx.pages();
552
552
  for (const p of pages) {
553
553
  const pUrl = p.url();
554
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && pUrl.includes(targetHostname)) {
554
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
555
555
  targetPage = p;
556
556
  break;
557
557
  }
@@ -564,7 +564,7 @@ async function createSession(name, url, options) {
564
564
  const pages = ctx.pages();
565
565
  for (const p of pages) {
566
566
  const pUrl = p.url();
567
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
567
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
568
568
  targetPage = p;
569
569
  break;
570
570
  }
@@ -575,7 +575,7 @@ async function createSession(name, url, options) {
575
575
  if (!targetPage && options?.cdpEndpoint) {
576
576
  const targets = await getCDPTargets(options.cdpEndpoint);
577
577
  const matchTarget = targets.find(
578
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (url ? t.url.includes(new URL(url).hostname) : true)
578
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
579
579
  );
580
580
  if (matchTarget && matchTarget.url) {
581
581
  targetPage = await context.newPage();
@@ -2193,8 +2193,12 @@ var XBContextImpl = class {
2193
2193
  const result = await this.conn.send("Storage.getCookies", params);
2194
2194
  return result.cookies;
2195
2195
  } catch {
2196
- const result = await this.conn.send("Network.getCookies", params);
2197
- return result.cookies;
2196
+ try {
2197
+ const result = await this.conn.send("Network.getCookies", params);
2198
+ return result.cookies;
2199
+ } catch {
2200
+ return [];
2201
+ }
2198
2202
  }
2199
2203
  }
2200
2204
  async addCookies(cookies) {
@@ -2548,6 +2552,7 @@ var CDPConnection = class extends EventEmitter4 {
2548
2552
  defaultSessionId;
2549
2553
  constructor(wsOrUrl, sessionId) {
2550
2554
  super();
2555
+ this.setMaxListeners(0);
2551
2556
  this.defaultSessionId = sessionId;
2552
2557
  if (typeof wsOrUrl === "string") {
2553
2558
  this.ws = new WebSocket(wsOrUrl);
@@ -4284,7 +4289,7 @@ async function getCDPTargets2(cdpEndpoint) {
4284
4289
  }
4285
4290
  async function findTargetPage(cdpEndpoint, target) {
4286
4291
  const targets = await getCDPTargets2(cdpEndpoint);
4287
- const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://"));
4292
+ const pages = targets.filter((t) => t.url && !t.url.startsWith("about:blank") && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://"));
4288
4293
  const byId = pages.find((t) => t.id === target);
4289
4294
  if (byId) return { pageId: byId.id, wsUrl: byId.webSocketDebuggerUrl, title: byId.title, url: byId.url };
4290
4295
  const lowerTarget = target.toLowerCase();
@@ -4407,7 +4412,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
4407
4412
  const pages = ctx.pages();
4408
4413
  for (const p of pages) {
4409
4414
  const pUrl = p.url();
4410
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
4415
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
4411
4416
  if (targetHostname && pUrl.includes(targetHostname)) {
4412
4417
  page = p;
4413
4418
  break;
@@ -4423,7 +4428,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
4423
4428
  if (!page) {
4424
4429
  const targets = await getCDPTargets2(ep);
4425
4430
  const matchTarget = targets.find(
4426
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (targetHostname ? t.url.includes(targetHostname) : true)
4431
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (targetHostname ? t.url.includes(targetHostname) : true)
4427
4432
  );
4428
4433
  if (matchTarget && matchTarget.url) {
4429
4434
  page = await context.newPage();
@@ -4491,7 +4496,7 @@ async function createEphemeralContext(options) {
4491
4496
  const allPages = ctx.pages();
4492
4497
  const existingPages = allPages.filter((p) => {
4493
4498
  const url = p.url();
4494
- return url !== "about:blank" && !url.startsWith("chrome://");
4499
+ return url !== "about:blank" && !url.startsWith("chrome://") && !url.startsWith("chrome-untrusted://") && !url.startsWith("chrome-error://");
4495
4500
  });
4496
4501
  const page2 = existingPages.length > 0 ? existingPages[0] : allPages.length > 0 ? allPages[0] : await ctx.newPage();
4497
4502
  resetIdleTimer();
@@ -4662,7 +4667,7 @@ async function createSession(name, url, options) {
4662
4667
  const pages = ctx.pages();
4663
4668
  for (const p of pages) {
4664
4669
  const pUrl = p.url();
4665
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && pUrl.includes(targetHostname)) {
4670
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
4666
4671
  targetPage = p;
4667
4672
  break;
4668
4673
  }
@@ -4675,7 +4680,7 @@ async function createSession(name, url, options) {
4675
4680
  const pages = ctx.pages();
4676
4681
  for (const p of pages) {
4677
4682
  const pUrl = p.url();
4678
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
4683
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
4679
4684
  targetPage = p;
4680
4685
  break;
4681
4686
  }
@@ -4686,7 +4691,7 @@ async function createSession(name, url, options) {
4686
4691
  if (!targetPage && options?.cdpEndpoint) {
4687
4692
  const targets = await getCDPTargets2(options.cdpEndpoint);
4688
4693
  const matchTarget = targets.find(
4689
- (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && (url ? t.url.includes(new URL(url).hostname) : true)
4694
+ (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
4690
4695
  );
4691
4696
  if (matchTarget && matchTarget.url) {
4692
4697
  targetPage = await context.newPage();
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  resolveLaunchOpts,
26
26
  saveSessionDiskMeta,
27
27
  setActivePage
28
- } from "./chunk-HRTXMFW4.js";
28
+ } from "./chunk-XVZ6NKRJ.js";
29
29
  import "./chunk-TNEN6VQ2.js";
30
30
  import {
31
31
  forwardCommandLog,
@@ -337,7 +337,8 @@ var gotoCommand = registerCommand({
337
337
  scope: "page",
338
338
  parameters: z.object({
339
339
  url: z.string(),
340
- waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
340
+ waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
341
+ timeout: z.number().optional()
341
342
  }),
342
343
  result: z.object({
343
344
  url: z.string(),
@@ -355,7 +356,8 @@ var gotoCommand = registerCommand({
355
356
  }
356
357
  }
357
358
  const response = await ctx.page.goto(url, {
358
- waitUntil: p.waitUntil || "domcontentloaded"
359
+ waitUntil: p.waitUntil || "domcontentloaded",
360
+ ...p.timeout ? { timeout: p.timeout } : {}
359
361
  });
360
362
  const ssr = await detectSsr(ctx.page);
361
363
  return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
@@ -6967,7 +6969,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6967
6969
  }
6968
6970
  let targetPageOverride = null;
6969
6971
  if (_target && extraOpts?.cdpEndpoint) {
6970
- const { findTargetPage } = await import("./browser-AN6MKGOD.js");
6972
+ const { findTargetPage } = await import("./browser-DZVIVKOA.js");
6971
6973
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6972
6974
  if (!targetPageOverride) {
6973
6975
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9070,7 +9072,7 @@ var createBuiltin = {
9070
9072
  );
9071
9073
  process.exit(1);
9072
9074
  }
9073
- const targetDir = path2.join(ctx.cwd, projectName);
9075
+ const targetDir = path2.isAbsolute(projectName) ? projectName : path2.join(ctx.cwd, projectName);
9074
9076
  if (fs2.existsSync(targetDir) && !options["force"]) {
9075
9077
  console.error(`Directory "${projectName}" already exists. Use --force to overwrite.`);
9076
9078
  process.exit(1);
@@ -9592,8 +9594,10 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9592
9594
  if (!args[0]) outputError(`Usage: xbrowser ${command} <url>`);
9593
9595
  cmdName = "goto";
9594
9596
  params = {
9595
- url: /^https?:\/\//i.test(args[0]) || /^wss?:\/\//i.test(args[0]) ? args[0] : "https://" + args[0],
9596
- waitUntil: options.waitUntil
9597
+ // Don't prefix if URL already has a scheme (http, file, about, data, etc.)
9598
+ 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],
9599
+ waitUntil: options.waitUntil,
9600
+ ...options.timeout ? { timeout: Number(options.timeout) } : {}
9597
9601
  };
9598
9602
  break;
9599
9603
  case "screenshot":
@@ -9649,11 +9653,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9649
9653
  break;
9650
9654
  }
9651
9655
  case "mouse": {
9652
- const action = options.action || args.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9653
- const actionIdx = action ? args.indexOf(action) : -1;
9654
- const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && args[actionIdx + 1] ? Number(args[actionIdx + 1]) : void 0;
9655
- const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && args[actionIdx + 2] ? Number(args[actionIdx + 2]) : void 0;
9656
- if (!action || x === void 0 || y === void 0) {
9656
+ const flatArgs = args.flatMap((a) => a.split(/\s+/).filter(Boolean));
9657
+ const action = options.action || flatArgs.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9658
+ const actionIdx = action ? flatArgs.indexOf(action) : -1;
9659
+ const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && flatArgs[actionIdx + 1] ? Number(flatArgs[actionIdx + 1]) : void 0;
9660
+ const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && flatArgs[actionIdx + 2] ? Number(flatArgs[actionIdx + 2]) : void 0;
9661
+ if (!action || x === void 0 || y === void 0 || isNaN(x) || isNaN(y)) {
9657
9662
  outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
9658
9663
  }
9659
9664
  cmdName = "mouse";
@@ -9843,6 +9848,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9843
9848
  outputResult(result.data, mode);
9844
9849
  }
9845
9850
  }
9851
+ const outputFile = options.output;
9852
+ if (outputFile && result.success && result.data) {
9853
+ const { writeFileSync: writeFileSync11 } = await import("fs");
9854
+ const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
9855
+ writeFileSync11(outputFile, content, "utf-8");
9856
+ console.log(`
9857
+ \u{1F4C4} Written to ${outputFile}`);
9858
+ }
9846
9859
  }
9847
9860
 
9848
9861
  // src/cli/session-routes.ts
@@ -10639,15 +10652,20 @@ async function handleExtract(args, _mode) {
10639
10652
  console.log(`
10640
10653
  Saved LLM summary: ${outputPath}`);
10641
10654
  }
10642
- async function handleFilter(args, _mode) {
10655
+ async function handleFilter(args, _mode, options) {
10643
10656
  const filePath = args[0];
10644
10657
  const outputPath = args[1];
10645
10658
  if (!filePath || !outputPath) {
10646
- console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude-types=type1,type2]");
10659
+ console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
10647
10660
  process.exit(1);
10648
10661
  }
10649
10662
  const { filterRecording, parseExcludeTypes } = await import("./filter-EDTFGLS5.js");
10650
- const excludeTypes = parseExcludeTypes(args.slice(2));
10663
+ const excludeArgs = args.slice(2).concat(
10664
+ Object.entries(options || {}).flatMap(
10665
+ ([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
10666
+ )
10667
+ );
10668
+ const excludeTypes = parseExcludeTypes(excludeArgs);
10651
10669
  const result = filterRecording(filePath, outputPath, excludeTypes);
10652
10670
  console.log(`Filtered ${filePath} -> ${outputPath}`);
10653
10671
  console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
@@ -12278,7 +12296,7 @@ async function routeCommand(argvIn, stdinCommands) {
12278
12296
  await handleExtract(cmdArgs, mode);
12279
12297
  break;
12280
12298
  case "filter":
12281
- await handleFilter(cmdArgs, mode);
12299
+ await handleFilter(cmdArgs, mode, options);
12282
12300
  break;
12283
12301
  case "run":
12284
12302
  if (!cmdArgs[0]) {
@@ -12727,7 +12745,7 @@ async function main() {
12727
12745
  const command = process.argv[2];
12728
12746
  const isLongRunning = command === "preview" || command === "serve";
12729
12747
  if (!isLongRunning) {
12730
- const { ensureProcessCanExit } = await import("./browser-AN6MKGOD.js");
12748
+ const { ensureProcessCanExit } = await import("./browser-DZVIVKOA.js");
12731
12749
  await ensureProcessCanExit().catch(() => {
12732
12750
  });
12733
12751
  process.exit(exitCode);
@@ -21,8 +21,8 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-JUNEBEGF.js";
25
- import "./chunk-IX4JY6OO.js";
24
+ } from "./chunk-NDAMCPIJ.js";
25
+ import "./chunk-E5WWMKXB.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
28
28
  getDaemonConfig,
@@ -295,7 +295,8 @@ var gotoCommand = registerCommand({
295
295
  scope: "page",
296
296
  parameters: z.object({
297
297
  url: z.string(),
298
- waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
298
+ waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
299
+ timeout: z.number().optional()
299
300
  }),
300
301
  result: z.object({
301
302
  url: z.string(),
@@ -313,7 +314,8 @@ var gotoCommand = registerCommand({
313
314
  }
314
315
  }
315
316
  const response = await ctx.page.goto(url, {
316
- waitUntil: p.waitUntil || "domcontentloaded"
317
+ waitUntil: p.waitUntil || "domcontentloaded",
318
+ ...p.timeout ? { timeout: p.timeout } : {}
317
319
  });
318
320
  const ssr = await detectSsr(ctx.page);
319
321
  return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
@@ -6925,7 +6927,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6925
6927
  }
6926
6928
  let targetPageOverride = null;
6927
6929
  if (_target && extraOpts?.cdpEndpoint) {
6928
- const { findTargetPage } = await import("./browser-MUPES4UY.js");
6930
+ const { findTargetPage } = await import("./browser-ZF4EJ3SK.js");
6929
6931
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6930
6932
  if (!targetPageOverride) {
6931
6933
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8609,7 +8611,7 @@ function createRPCHandler() {
8609
8611
  const isNewFormat = Array.isArray(parsed.actions);
8610
8612
  if (isNewFormat) {
8611
8613
  try {
8612
- const { SessionReplayer } = await import("./session-replayer-YWMSSZWC.js");
8614
+ const { SessionReplayer } = await import("./session-replayer-UHITXIOZ.js");
8613
8615
  const replayer = new SessionReplayer({
8614
8616
  page: session.page,
8615
8617
  stepDelay: slowMo * 500,
package/dist/index.js CHANGED
@@ -81,8 +81,8 @@ import {
81
81
  resolveLaunchOpts,
82
82
  saveSessionDiskMeta,
83
83
  setActivePage
84
- } from "./chunk-EUHVZVVL.js";
85
- import "./chunk-IX4JY6OO.js";
84
+ } from "./chunk-OH7CB2P6.js";
85
+ import "./chunk-E5WWMKXB.js";
86
86
  import "./chunk-TNEN6VQ2.js";
87
87
  import {
88
88
  errMsg
@@ -377,7 +377,8 @@ var gotoCommand = registerCommand({
377
377
  scope: "page",
378
378
  parameters: z.object({
379
379
  url: z.string(),
380
- waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional()
380
+ waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).optional(),
381
+ timeout: z.number().optional()
381
382
  }),
382
383
  result: z.object({
383
384
  url: z.string(),
@@ -395,7 +396,8 @@ var gotoCommand = registerCommand({
395
396
  }
396
397
  }
397
398
  const response = await ctx.page.goto(url, {
398
- waitUntil: p.waitUntil || "domcontentloaded"
399
+ waitUntil: p.waitUntil || "domcontentloaded",
400
+ ...p.timeout ? { timeout: p.timeout } : {}
399
401
  });
400
402
  const ssr = await detectSsr(ctx.page);
401
403
  return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
@@ -7287,7 +7289,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7287
7289
  }
7288
7290
  let targetPageOverride = null;
7289
7291
  if (_target && extraOpts?.cdpEndpoint) {
7290
- const { findTargetPage } = await import("./browser-6QN42A4K.js");
7292
+ const { findTargetPage } = await import("./browser-H55TWH2I.js");
7291
7293
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7292
7294
  if (!targetPageOverride) {
7293
7295
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9405,7 +9407,7 @@ var createBuiltin = {
9405
9407
  );
9406
9408
  process.exit(1);
9407
9409
  }
9408
- const targetDir = path2.join(ctx.cwd, projectName);
9410
+ const targetDir = path2.isAbsolute(projectName) ? projectName : path2.join(ctx.cwd, projectName);
9409
9411
  if (fs2.existsSync(targetDir) && !options["force"]) {
9410
9412
  console.error(`Directory "${projectName}" already exists. Use --force to overwrite.`);
9411
9413
  process.exit(1);
@@ -9932,8 +9934,10 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9932
9934
  if (!args[0]) outputError(`Usage: xbrowser ${command} <url>`);
9933
9935
  cmdName = "goto";
9934
9936
  params = {
9935
- url: /^https?:\/\//i.test(args[0]) || /^wss?:\/\//i.test(args[0]) ? args[0] : "https://" + args[0],
9936
- waitUntil: options.waitUntil
9937
+ // Don't prefix if URL already has a scheme (http, file, about, data, etc.)
9938
+ 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],
9939
+ waitUntil: options.waitUntil,
9940
+ ...options.timeout ? { timeout: Number(options.timeout) } : {}
9937
9941
  };
9938
9942
  break;
9939
9943
  case "screenshot":
@@ -9989,11 +9993,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9989
9993
  break;
9990
9994
  }
9991
9995
  case "mouse": {
9992
- const action = options.action || args.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9993
- const actionIdx = action ? args.indexOf(action) : -1;
9994
- const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && args[actionIdx + 1] ? Number(args[actionIdx + 1]) : void 0;
9995
- const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && args[actionIdx + 2] ? Number(args[actionIdx + 2]) : void 0;
9996
- if (!action || x === void 0 || y === void 0) {
9996
+ const flatArgs = args.flatMap((a) => a.split(/\s+/).filter(Boolean));
9997
+ const action = options.action || flatArgs.find((a) => ["move", "click", "dblclick", "down", "up"].includes(a));
9998
+ const actionIdx = action ? flatArgs.indexOf(action) : -1;
9999
+ const x = options.x !== void 0 ? Number(options.x) : actionIdx >= 0 && flatArgs[actionIdx + 1] ? Number(flatArgs[actionIdx + 1]) : void 0;
10000
+ const y = options.y !== void 0 ? Number(options.y) : actionIdx >= 0 && flatArgs[actionIdx + 2] ? Number(flatArgs[actionIdx + 2]) : void 0;
10001
+ if (!action || x === void 0 || y === void 0 || isNaN(x) || isNaN(y)) {
9997
10002
  outputError("Usage: xbrowser mouse <move|click|dblclick> <x> <y>\n xbrowser mouse --action <action> --x <x> --y <y>");
9998
10003
  }
9999
10004
  cmdName = "mouse";
@@ -10183,6 +10188,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10183
10188
  outputResult(result.data, mode);
10184
10189
  }
10185
10190
  }
10191
+ const outputFile = options.output;
10192
+ if (outputFile && result.success && result.data) {
10193
+ const { writeFileSync: writeFileSync12 } = await import("fs");
10194
+ const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
10195
+ writeFileSync12(outputFile, content, "utf-8");
10196
+ console.log(`
10197
+ \u{1F4C4} Written to ${outputFile}`);
10198
+ }
10186
10199
  }
10187
10200
 
10188
10201
  // src/cli/session-routes.ts
@@ -10979,15 +10992,20 @@ async function handleExtract(args, _mode) {
10979
10992
  console.log(`
10980
10993
  Saved LLM summary: ${outputPath}`);
10981
10994
  }
10982
- async function handleFilter(args, _mode) {
10995
+ async function handleFilter(args, _mode, options) {
10983
10996
  const filePath = args[0];
10984
10997
  const outputPath = args[1];
10985
10998
  if (!filePath || !outputPath) {
10986
- console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude-types=type1,type2]");
10999
+ console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
10987
11000
  process.exit(1);
10988
11001
  }
10989
11002
  const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-3JQWBM5F.js");
10990
- const excludeTypes = parseExcludeTypes2(args.slice(2));
11003
+ const excludeArgs = args.slice(2).concat(
11004
+ Object.entries(options || {}).flatMap(
11005
+ ([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
11006
+ )
11007
+ );
11008
+ const excludeTypes = parseExcludeTypes2(excludeArgs);
10991
11009
  const result = filterRecording2(filePath, outputPath, excludeTypes);
10992
11010
  console.log(`Filtered ${filePath} -> ${outputPath}`);
10993
11011
  console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
@@ -12618,7 +12636,7 @@ async function routeCommand(argvIn, stdinCommands) {
12618
12636
  await handleExtract(cmdArgs, mode);
12619
12637
  break;
12620
12638
  case "filter":
12621
- await handleFilter(cmdArgs, mode);
12639
+ await handleFilter(cmdArgs, mode, options);
12622
12640
  break;
12623
12641
  case "run":
12624
12642
  if (!cmdArgs[0]) {
@@ -15854,7 +15872,7 @@ var DataCollector = class {
15854
15872
  return results;
15855
15873
  }
15856
15874
  async createBrowserContext() {
15857
- const { launch } = await import("./cdp-driver-LKNM6OQI.js");
15875
+ const { launch } = await import("./cdp-driver-WWQBRTPF.js");
15858
15876
  const { browser } = await launch({
15859
15877
  headless: true,
15860
15878
  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-LKNM6OQI.js");
34
+ const { launch } = await import("./cdp-driver-WWQBRTPF.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.1.0",
3
+ "version": "1.1.2",
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": {