@xbrowser/cli 1.4.3 → 1.4.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xbrowser
2
2
 
3
- > **Browser automation CLI** for web scraping, headless browsing, SEO analysis, and AI agent workflows. 50 commands, 20+ plugins. A command-line alternative to Playwright, Puppeteer, and Selenium — **no code required**.
3
+ > **Browser automation CLI** for web scraping, headless browsing, SEO analysis, and AI agent workflows. 51 commands, 70+ plugins. A command-line alternative to Playwright, Puppeteer, and Selenium — **no code required**.
4
4
 
5
5
  [![CI Status](https://github.com/dyyz1993/xbrowser/workflows/CI/badge.svg)](https://github.com/dyyz1993/xbrowser/actions)
6
6
  [![codecov](https://codecov.io/gh/dyyz1993/xbrowser/branch/master/graph/badge.svg)](https://codecov.io/gh/dyyz1993/xbrowser)
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-JLSKMNMN.js";
24
- import "./chunk-WI3XE4W3.js";
23
+ } from "./chunk-Z6GGTJL5.js";
24
+ import "./chunk-IDJ5NILK.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-ABXMBNQ6.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-O4HCWMU2.js";
23
+ } from "./chunk-NW3WE5JK.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-J3VNBG25.js";
24
- import "./chunk-WI3XE4W3.js";
23
+ } from "./chunk-SMRIC22G.js";
24
+ import "./chunk-IDJ5NILK.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-KFQGP6VL.js";
@@ -1167,11 +1167,21 @@ var XBPageImpl = class _XBPageImpl {
1167
1167
  }
1168
1168
  async goBack(opts = {}) {
1169
1169
  await this.evaluate("() => history.back()");
1170
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1170
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1171
+ });
1172
+ try {
1173
+ this._url = await this.evaluate("location.href");
1174
+ } catch {
1175
+ }
1171
1176
  }
1172
1177
  async goForward(opts = {}) {
1173
1178
  await this.evaluate("() => history.forward()");
1174
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1179
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1180
+ });
1181
+ try {
1182
+ this._url = await this.evaluate("location.href");
1183
+ } catch {
1184
+ }
1175
1185
  }
1176
1186
  async reload(opts = {}) {
1177
1187
  this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-WI3XE4W3.js";
17
+ } from "./chunk-IDJ5NILK.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -1166,11 +1166,21 @@ var XBPageImpl = class _XBPageImpl {
1166
1166
  }
1167
1167
  async goBack(opts = {}) {
1168
1168
  await this.evaluate("() => history.back()");
1169
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1169
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1170
+ });
1171
+ try {
1172
+ this._url = await this.evaluate("location.href");
1173
+ } catch {
1174
+ }
1170
1175
  }
1171
1176
  async goForward(opts = {}) {
1172
1177
  await this.evaluate("() => history.forward()");
1173
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1178
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1179
+ });
1180
+ try {
1181
+ this._url = await this.evaluate("location.href");
1182
+ } catch {
1183
+ }
1174
1184
  }
1175
1185
  async reload(opts = {}) {
1176
1186
  this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
@@ -1172,11 +1172,21 @@ var XBPageImpl = class _XBPageImpl {
1172
1172
  }
1173
1173
  async goBack(opts = {}) {
1174
1174
  await this.evaluate("() => history.back()");
1175
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1175
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1176
+ });
1177
+ try {
1178
+ this._url = await this.evaluate("location.href");
1179
+ } catch {
1180
+ }
1176
1181
  }
1177
1182
  async goForward(opts = {}) {
1178
1183
  await this.evaluate("() => history.forward()");
1179
- await this.waitForLoadState(opts.waitUntil ?? "load", opts.timeout);
1184
+ await this.waitForLoadState(opts.waitUntil ?? "domcontentloaded", opts.timeout ?? 5e3).catch(() => {
1185
+ });
1186
+ try {
1187
+ this._url = await this.evaluate("location.href");
1188
+ } catch {
1189
+ }
1180
1190
  }
1181
1191
  async reload(opts = {}) {
1182
1192
  this._loadState = { loadFired: false, domContentFired: false, networkIdle: false };
@@ -4264,21 +4274,27 @@ async function fetchNoProxy(url) {
4264
4274
  }
4265
4275
  async function resolveCDPEndpoint(raw) {
4266
4276
  if (raw === "auto") {
4267
- const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
4268
- if (!httpResp.ok) {
4269
- throw new Error(
4270
- `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
4277
+ const ports = [9222, 9221, 9223, 9224];
4278
+ for (const port of ports) {
4279
+ try {
4280
+ const httpResp = await fetchNoProxy(`http://localhost:${port}/json/version`);
4281
+ if (httpResp.ok) {
4282
+ const data = await httpResp.json();
4283
+ if (data.webSocketDebuggerUrl) {
4284
+ return data.webSocketDebuggerUrl;
4285
+ }
4286
+ }
4287
+ } catch {
4288
+ }
4289
+ }
4290
+ throw new Error(
4291
+ `Could not auto-discover CDP endpoint. Tried ports: ${ports.join(", ")}.
4292
+ \u53EF\u80FD\u539F\u56E0\uFF1A\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
4271
4293
  \u89E3\u51B3\u65B9\u6CD5\uFF1A
4272
- 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
4273
- 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
4294
+ 1. \u542F\u52A8 Chrome: google-chrome --remote-debugging-port=9222
4295
+ 2. \u6216\u7528 cdp-tunnel: npx cdp-tunnel setup
4274
4296
  3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
4275
- );
4276
- }
4277
- const data = await httpResp.json();
4278
- if (!data.webSocketDebuggerUrl) {
4279
- throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
4280
- }
4281
- return data.webSocketDebuggerUrl;
4297
+ );
4282
4298
  }
4283
4299
  if (/^\d+$/.test(raw)) {
4284
4300
  const port = raw;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-WI3XE4W3.js";
3
+ } from "./chunk-IDJ5NILK.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -1400,21 +1400,27 @@ async function fetchNoProxy(url) {
1400
1400
  }
1401
1401
  async function resolveCDPEndpoint(raw) {
1402
1402
  if (raw === "auto") {
1403
- const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
1404
- if (!httpResp.ok) {
1405
- throw new Error(
1406
- `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
1403
+ const ports = [9222, 9221, 9223, 9224];
1404
+ for (const port of ports) {
1405
+ try {
1406
+ const httpResp = await fetchNoProxy(`http://localhost:${port}/json/version`);
1407
+ if (httpResp.ok) {
1408
+ const data = await httpResp.json();
1409
+ if (data.webSocketDebuggerUrl) {
1410
+ return data.webSocketDebuggerUrl;
1411
+ }
1412
+ }
1413
+ } catch {
1414
+ }
1415
+ }
1416
+ throw new Error(
1417
+ `Could not auto-discover CDP endpoint. Tried ports: ${ports.join(", ")}.
1418
+ \u53EF\u80FD\u539F\u56E0\uFF1A\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
1407
1419
  \u89E3\u51B3\u65B9\u6CD5\uFF1A
1408
- 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
1409
- 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
1420
+ 1. \u542F\u52A8 Chrome: google-chrome --remote-debugging-port=9222
1421
+ 2. \u6216\u7528 cdp-tunnel: npx cdp-tunnel setup
1410
1422
  3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
1411
- );
1412
- }
1413
- const data = await httpResp.json();
1414
- if (!data.webSocketDebuggerUrl) {
1415
- throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
1416
- }
1417
- return data.webSocketDebuggerUrl;
1423
+ );
1418
1424
  }
1419
1425
  if (/^\d+$/.test(raw)) {
1420
1426
  const port = raw;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-WI3XE4W3.js";
3
+ } from "./chunk-IDJ5NILK.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -35,21 +35,27 @@ async function fetchNoProxy(url) {
35
35
  }
36
36
  async function resolveCDPEndpoint(raw) {
37
37
  if (raw === "auto") {
38
- const httpResp = await fetchNoProxy("http://localhost:9222/json/version");
39
- if (!httpResp.ok) {
40
- throw new Error(
41
- `CDP port 9222 responded with ${httpResp.status} ${httpResp.statusText}. \u53EF\u80FD\u539F\u56E0\uFF1A9222 \u88AB\u50F5\u6B7B\u7684 Chrome \u5360\u7528\uFF0C\u6216\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
38
+ const ports = [9222, 9221, 9223, 9224];
39
+ for (const port of ports) {
40
+ try {
41
+ const httpResp = await fetchNoProxy(`http://localhost:${port}/json/version`);
42
+ if (httpResp.ok) {
43
+ const data = await httpResp.json();
44
+ if (data.webSocketDebuggerUrl) {
45
+ return data.webSocketDebuggerUrl;
46
+ }
47
+ }
48
+ } catch {
49
+ }
50
+ }
51
+ throw new Error(
52
+ `Could not auto-discover CDP endpoint. Tried ports: ${ports.join(", ")}.
53
+ \u53EF\u80FD\u539F\u56E0\uFF1A\u6CA1\u6709 Chrome \u4EE5 --remote-debugging-port \u542F\u52A8\u3002
42
54
  \u89E3\u51B3\u65B9\u6CD5\uFF1A
43
- 1. \u6740\u6389\u6B8B\u7559 Chrome: pkill -f "remote-debugging-port"
44
- 2. \u91CD\u542F Chrome: npx cdp-tunnel setup
55
+ 1. \u542F\u52A8 Chrome: google-chrome --remote-debugging-port=9222
56
+ 2. \u6216\u7528 cdp-tunnel: npx cdp-tunnel setup
45
57
  3. \u6216\u6307\u5B9A\u7AEF\u53E3: --cdp <port>`
46
- );
47
- }
48
- const data = await httpResp.json();
49
- if (!data.webSocketDebuggerUrl) {
50
- throw new Error("Could not auto-discover CDP endpoint from localhost:9222");
51
- }
52
- return data.webSocketDebuggerUrl;
58
+ );
53
59
  }
54
60
  if (/^\d+$/.test(raw)) {
55
61
  const port = raw;
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  resolveLaunchOpts,
26
26
  saveSessionDiskMeta,
27
27
  setActivePage
28
- } from "./chunk-O4HCWMU2.js";
28
+ } from "./chunk-NW3WE5JK.js";
29
29
  import "./chunk-TNEN6VQ2.js";
30
30
  import {
31
31
  forwardCommandLog,
@@ -376,7 +376,8 @@ var backCommand = registerCommand({
376
376
  result: z.object({ url: z.string() }),
377
377
  handler: async (_p, ctx) => {
378
378
  await ctx.page.goBack();
379
- return ok({ url: ctx.page.url() });
379
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
380
+ return ok({ url });
380
381
  }
381
382
  });
382
383
  var forwardCommand = registerCommand({
@@ -386,7 +387,8 @@ var forwardCommand = registerCommand({
386
387
  result: z.object({ url: z.string() }),
387
388
  handler: async (_p, ctx) => {
388
389
  await ctx.page.goForward();
389
- return ok({ url: ctx.page.url() });
390
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
391
+ return ok({ url });
390
392
  }
391
393
  });
392
394
  var refreshCommand = registerCommand({
@@ -395,8 +397,13 @@ var refreshCommand = registerCommand({
395
397
  scope: "page",
396
398
  result: z.object({ url: z.string() }),
397
399
  handler: async (_p, ctx) => {
398
- await ctx.page.reload();
399
- return ok({ url: ctx.page.url() });
400
+ try {
401
+ await ctx.page.reload();
402
+ } catch (err) {
403
+ return fail(`Refresh failed: ${err instanceof Error ? err.message : String(err)}`);
404
+ }
405
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
406
+ return ok({ url });
400
407
  }
401
408
  });
402
409
  var titleCommand = registerCommand({
@@ -415,7 +422,8 @@ var urlCommand = registerCommand({
415
422
  scope: "page",
416
423
  result: z.object({ url: z.string() }),
417
424
  handler: async (_p, ctx) => {
418
- return ok({ url: ctx.page.url() || "about:blank" });
425
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url() || "about:blank");
426
+ return ok({ url });
419
427
  }
420
428
  });
421
429
  registerCommand({
@@ -7075,7 +7083,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7075
7083
  }
7076
7084
  let targetPageOverride = null;
7077
7085
  if (_target && extraOpts?.cdpEndpoint) {
7078
- const { findTargetPage } = await import("./browser-B5W577GO.js");
7086
+ const { findTargetPage } = await import("./browser-5CM4GUZU.js");
7079
7087
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7080
7088
  if (!targetPageOverride) {
7081
7089
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -10026,6 +10034,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10026
10034
  };
10027
10035
  break;
10028
10036
  }
10037
+ case "waitForTimeout":
10038
+ cmdName = "waitForTimeout";
10039
+ params = {
10040
+ timeout: args[0] ? Number(args[0]) : options.timeout ? Number(options.timeout) : 1e3
10041
+ };
10042
+ break;
10029
10043
  default:
10030
10044
  cmdName = command;
10031
10045
  params = { ...options };
@@ -11838,18 +11852,6 @@ function printChainResult(chainResult) {
11838
11852
  console.error(`Stopped: ${chainResult.stoppedReason}`);
11839
11853
  }
11840
11854
  }
11841
- function printChainResultBrief(chainResult) {
11842
- for (const step of chainResult.steps) {
11843
- if (step.success) {
11844
- console.log(`[OK] ${step.raw}`);
11845
- } else {
11846
- console.error(`[FAIL] ${step.raw}: ${step.message}`);
11847
- }
11848
- }
11849
- if (chainResult.stoppedReason) {
11850
- console.error(`Stopped: ${chainResult.stoppedReason}`);
11851
- }
11852
- }
11853
11855
 
11854
11856
  // src/server/http-server.ts
11855
11857
  import { createServer } from "http";
@@ -12378,10 +12380,10 @@ function extractSessionNameFromArgv(argv) {
12378
12380
  async function handleEvalMode(argv) {
12379
12381
  const evalCommands = parseEvalFlags(argv);
12380
12382
  if (evalCommands.length === 0) return;
12381
- const chain = evalCommands.join(" && ");
12383
+ const chain = evalCommands.join(" ; ");
12382
12384
  const cdpEndpoint = extractCdpFromArgv(argv);
12383
12385
  const chainResult = await executeChain(chain, { cdpEndpoint });
12384
- printChainResultBrief(chainResult);
12386
+ printChainResult(chainResult);
12385
12387
  if (!chainResult.success) throw new Error("Command failed");
12386
12388
  }
12387
12389
  async function handleChainInput(input, argv) {
@@ -12425,12 +12427,8 @@ async function routeCommand(argvIn, stdinCommands) {
12425
12427
  const possibleCmd = argv[0].substring(0, spaceIdx);
12426
12428
  if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12427
12429
  const remainder = argv[0].substring(spaceIdx + 1);
12428
- if (remainder.includes("--")) {
12429
- const remainderParts = remainder.split(/\s+/).filter(Boolean);
12430
- argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12431
- } else {
12432
- argv = [possibleCmd, remainder, ...argv.slice(1)];
12433
- }
12430
+ const remainderParts = remainder.split(/\s+/).filter(Boolean);
12431
+ argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12434
12432
  }
12435
12433
  }
12436
12434
  } catch (e) {
@@ -12444,7 +12442,7 @@ async function routeCommand(argvIn, stdinCommands) {
12444
12442
  const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
12445
12443
  const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
12446
12444
  const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
12447
- if (options.version || options.v) {
12445
+ if (options.version) {
12448
12446
  console.log(`xbrowser v${version}`);
12449
12447
  return;
12450
12448
  }
@@ -13041,7 +13039,7 @@ async function main() {
13041
13039
  const command = process.argv[2];
13042
13040
  const isLongRunning = command === "preview" || command === "serve";
13043
13041
  if (!isLongRunning) {
13044
- const { ensureProcessCanExit } = await import("./browser-B5W577GO.js");
13042
+ const { ensureProcessCanExit } = await import("./browser-5CM4GUZU.js");
13045
13043
  await ensureProcessCanExit().catch(() => {
13046
13044
  });
13047
13045
  process.exit(exitCode);
@@ -21,8 +21,8 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-J3VNBG25.js";
25
- import "./chunk-WI3XE4W3.js";
24
+ } from "./chunk-SMRIC22G.js";
25
+ import "./chunk-IDJ5NILK.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
28
28
  getPluginLoader
@@ -337,7 +337,8 @@ var backCommand = registerCommand({
337
337
  result: z.object({ url: z.string() }),
338
338
  handler: async (_p, ctx) => {
339
339
  await ctx.page.goBack();
340
- return ok({ url: ctx.page.url() });
340
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
341
+ return ok({ url });
341
342
  }
342
343
  });
343
344
  var forwardCommand = registerCommand({
@@ -347,7 +348,8 @@ var forwardCommand = registerCommand({
347
348
  result: z.object({ url: z.string() }),
348
349
  handler: async (_p, ctx) => {
349
350
  await ctx.page.goForward();
350
- return ok({ url: ctx.page.url() });
351
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
352
+ return ok({ url });
351
353
  }
352
354
  });
353
355
  var refreshCommand = registerCommand({
@@ -356,8 +358,13 @@ var refreshCommand = registerCommand({
356
358
  scope: "page",
357
359
  result: z.object({ url: z.string() }),
358
360
  handler: async (_p, ctx) => {
359
- await ctx.page.reload();
360
- return ok({ url: ctx.page.url() });
361
+ try {
362
+ await ctx.page.reload();
363
+ } catch (err) {
364
+ return fail(`Refresh failed: ${err instanceof Error ? err.message : String(err)}`);
365
+ }
366
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
367
+ return ok({ url });
361
368
  }
362
369
  });
363
370
  var titleCommand = registerCommand({
@@ -376,7 +383,8 @@ var urlCommand = registerCommand({
376
383
  scope: "page",
377
384
  result: z.object({ url: z.string() }),
378
385
  handler: async (_p, ctx) => {
379
- return ok({ url: ctx.page.url() || "about:blank" });
386
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url() || "about:blank");
387
+ return ok({ url });
380
388
  }
381
389
  });
382
390
  registerCommand({
@@ -6606,7 +6614,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6606
6614
  }
6607
6615
  let targetPageOverride = null;
6608
6616
  if (_target && extraOpts?.cdpEndpoint) {
6609
- const { findTargetPage } = await import("./browser-VWKGCFA5.js");
6617
+ const { findTargetPage } = await import("./browser-V3VHXQQF.js");
6610
6618
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6611
6619
  if (!targetPageOverride) {
6612
6620
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8322,7 +8330,7 @@ function createRPCHandler() {
8322
8330
  const isNewFormat = Array.isArray(parsed.actions);
8323
8331
  if (isNewFormat) {
8324
8332
  try {
8325
- const { SessionReplayer } = await import("./session-replayer-X3S47AA3.js");
8333
+ const { SessionReplayer } = await import("./session-replayer-F4ORJMCL.js");
8326
8334
  const replayer = new SessionReplayer({
8327
8335
  page: session.page,
8328
8336
  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-JLSKMNMN.js";
85
- import "./chunk-WI3XE4W3.js";
84
+ } from "./chunk-Z6GGTJL5.js";
85
+ import "./chunk-IDJ5NILK.js";
86
86
  import "./chunk-TNEN6VQ2.js";
87
87
  import {
88
88
  errMsg
@@ -416,7 +416,8 @@ var backCommand = registerCommand({
416
416
  result: z.object({ url: z.string() }),
417
417
  handler: async (_p, ctx) => {
418
418
  await ctx.page.goBack();
419
- return ok({ url: ctx.page.url() });
419
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
420
+ return ok({ url });
420
421
  }
421
422
  });
422
423
  var forwardCommand = registerCommand({
@@ -426,7 +427,8 @@ var forwardCommand = registerCommand({
426
427
  result: z.object({ url: z.string() }),
427
428
  handler: async (_p, ctx) => {
428
429
  await ctx.page.goForward();
429
- return ok({ url: ctx.page.url() });
430
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
431
+ return ok({ url });
430
432
  }
431
433
  });
432
434
  var refreshCommand = registerCommand({
@@ -435,8 +437,13 @@ var refreshCommand = registerCommand({
435
437
  scope: "page",
436
438
  result: z.object({ url: z.string() }),
437
439
  handler: async (_p, ctx) => {
438
- await ctx.page.reload();
439
- return ok({ url: ctx.page.url() });
440
+ try {
441
+ await ctx.page.reload();
442
+ } catch (err) {
443
+ return fail(`Refresh failed: ${err instanceof Error ? err.message : String(err)}`);
444
+ }
445
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url());
446
+ return ok({ url });
440
447
  }
441
448
  });
442
449
  var titleCommand = registerCommand({
@@ -455,7 +462,8 @@ var urlCommand = registerCommand({
455
462
  scope: "page",
456
463
  result: z.object({ url: z.string() }),
457
464
  handler: async (_p, ctx) => {
458
- return ok({ url: ctx.page.url() || "about:blank" });
465
+ const url = await ctx.page.evaluate("location.href").catch(() => ctx.page.url() || "about:blank");
466
+ return ok({ url });
459
467
  }
460
468
  });
461
469
  registerCommand({
@@ -7395,7 +7403,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7395
7403
  }
7396
7404
  let targetPageOverride = null;
7397
7405
  if (_target && extraOpts?.cdpEndpoint) {
7398
- const { findTargetPage } = await import("./browser-MPMDAGZY.js");
7406
+ const { findTargetPage } = await import("./browser-2OALOLR2.js");
7399
7407
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7400
7408
  if (!targetPageOverride) {
7401
7409
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -10366,6 +10374,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10366
10374
  };
10367
10375
  break;
10368
10376
  }
10377
+ case "waitForTimeout":
10378
+ cmdName = "waitForTimeout";
10379
+ params = {
10380
+ timeout: args[0] ? Number(args[0]) : options.timeout ? Number(options.timeout) : 1e3
10381
+ };
10382
+ break;
10369
10383
  default:
10370
10384
  cmdName = command;
10371
10385
  params = { ...options };
@@ -12178,18 +12192,6 @@ function printChainResult(chainResult) {
12178
12192
  console.error(`Stopped: ${chainResult.stoppedReason}`);
12179
12193
  }
12180
12194
  }
12181
- function printChainResultBrief(chainResult) {
12182
- for (const step of chainResult.steps) {
12183
- if (step.success) {
12184
- console.log(`[OK] ${step.raw}`);
12185
- } else {
12186
- console.error(`[FAIL] ${step.raw}: ${step.message}`);
12187
- }
12188
- }
12189
- if (chainResult.stoppedReason) {
12190
- console.error(`Stopped: ${chainResult.stoppedReason}`);
12191
- }
12192
- }
12193
12195
 
12194
12196
  // src/server/http-server.ts
12195
12197
  import { createServer } from "http";
@@ -12718,10 +12720,10 @@ function extractSessionNameFromArgv(argv) {
12718
12720
  async function handleEvalMode(argv) {
12719
12721
  const evalCommands = parseEvalFlags(argv);
12720
12722
  if (evalCommands.length === 0) return;
12721
- const chain = evalCommands.join(" && ");
12723
+ const chain = evalCommands.join(" ; ");
12722
12724
  const cdpEndpoint = extractCdpFromArgv(argv);
12723
12725
  const chainResult = await executeChain(chain, { cdpEndpoint });
12724
- printChainResultBrief(chainResult);
12726
+ printChainResult(chainResult);
12725
12727
  if (!chainResult.success) throw new Error("Command failed");
12726
12728
  }
12727
12729
  async function handleChainInput(input, argv) {
@@ -12765,12 +12767,8 @@ async function routeCommand(argvIn, stdinCommands) {
12765
12767
  const possibleCmd = argv[0].substring(0, spaceIdx);
12766
12768
  if (/^[a-zA-Z][\w-]*$/.test(possibleCmd)) {
12767
12769
  const remainder = argv[0].substring(spaceIdx + 1);
12768
- if (remainder.includes("--")) {
12769
- const remainderParts = remainder.split(/\s+/).filter(Boolean);
12770
- argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12771
- } else {
12772
- argv = [possibleCmd, remainder, ...argv.slice(1)];
12773
- }
12770
+ const remainderParts = remainder.split(/\s+/).filter(Boolean);
12771
+ argv = [possibleCmd, ...remainderParts, ...argv.slice(1)];
12774
12772
  }
12775
12773
  }
12776
12774
  } catch (e) {
@@ -12784,7 +12782,7 @@ async function routeCommand(argvIn, stdinCommands) {
12784
12782
  const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
12785
12783
  const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
12786
12784
  const cdpEndpoint = options.cdp || process.env.XBROWSER_CDP;
12787
- if (options.version || options.v) {
12785
+ if (options.version) {
12788
12786
  console.log(`xbrowser v${version}`);
12789
12787
  return;
12790
12788
  }
@@ -16168,7 +16166,7 @@ var DataCollector = class {
16168
16166
  return results;
16169
16167
  }
16170
16168
  async createBrowserContext() {
16171
- const { launch } = await import("./cdp-driver-RCRYKHVQ.js");
16169
+ const { launch } = await import("./cdp-driver-VRXHK6P6.js");
16172
16170
  const { browser } = await launch({
16173
16171
  headless: true,
16174
16172
  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-RCRYKHVQ.js");
34
+ const { launch } = await import("./cdp-driver-VRXHK6P6.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.4.3",
3
+ "version": "1.4.5",
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": {