@xbrowser/cli 1.9.9 → 1.10.0

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-RZM5CNIY.js";
23
+ } from "./chunk-WSCP7QCJ.js";
24
24
  import "./chunk-RRBXV7KE.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-GUTBIYFW.js";
23
+ } from "./chunk-NITFVWWS.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-6OZWKXSW.js";
23
+ } from "./chunk-BAHSRZIX.js";
24
24
  import "./chunk-RRBXV7KE.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
@@ -1637,9 +1637,29 @@ function deleteSessionDiskMeta(name) {
1637
1637
  } catch {
1638
1638
  }
1639
1639
  }
1640
+ async function isSessionPageAlive(session) {
1641
+ const page = session.page;
1642
+ if (!page || typeof page.evaluate !== "function") return false;
1643
+ try {
1644
+ await Promise.race([
1645
+ page.evaluate("1"),
1646
+ new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
1647
+ ]);
1648
+ return true;
1649
+ } catch {
1650
+ return false;
1651
+ }
1652
+ }
1640
1653
  async function findOrRestoreSession(name, cdpEndpoint) {
1641
1654
  const inMem = findSession(name);
1642
- if (inMem) return inMem;
1655
+ if (inMem) {
1656
+ if (await isSessionPageAlive(inMem)) return inMem;
1657
+ logSessionEvent("stale_session", `name="${name}" \u2014 page \u63A2\u9488\u5931\u8D25\uFF08\u6D4F\u89C8\u5668\u53EF\u80FD\u5DF2\u91CD\u542F\uFF09\uFF0C\u5C31\u5730\u91CD\u5EFA`);
1658
+ try {
1659
+ await closeSessionByName(name);
1660
+ } catch {
1661
+ }
1662
+ }
1643
1663
  const meta = readSessionDiskMeta(name);
1644
1664
  if (!meta) return void 0;
1645
1665
  const ep = cdpEndpoint || meta.cdpEndpoint;
@@ -1902,14 +1922,6 @@ async function createSession(name, url, options) {
1902
1922
  let context;
1903
1923
  let page;
1904
1924
  if (isCDP) {
1905
- await new Promise((r) => setTimeout(r, 500));
1906
- let contexts = b.contexts();
1907
- if (contexts.length === 0) {
1908
- await new Promise((r) => setTimeout(r, 500));
1909
- contexts = b.contexts();
1910
- }
1911
- context = contexts[0] || await b.newContext();
1912
- let targetPage = null;
1913
1925
  const targetHostname = url ? (() => {
1914
1926
  try {
1915
1927
  return new URL(url).hostname;
@@ -1917,12 +1929,54 @@ async function createSession(name, url, options) {
1917
1929
  return "";
1918
1930
  }
1919
1931
  })() : "";
1932
+ const isRealPageUrl = (u) => !!u && u !== "about:blank" && !u.startsWith("chrome://") && !u.startsWith("chrome-untrusted://") && !u.startsWith("chrome-error://");
1933
+ const resolvePageUrl = async (p) => {
1934
+ const u = p.url();
1935
+ if (u && u !== "about:blank") return u;
1936
+ try {
1937
+ return await p.evaluate("location.href");
1938
+ } catch {
1939
+ return u ?? "";
1940
+ }
1941
+ };
1942
+ const pollStart = Date.now();
1943
+ for (; ; ) {
1944
+ const ctxs = b.contexts();
1945
+ let hostHit = false;
1946
+ let anyHit = false;
1947
+ for (const ctx of ctxs) {
1948
+ for (const p of ctx.pages()) {
1949
+ const u = await resolvePageUrl(p);
1950
+ if (!isRealPageUrl(u)) continue;
1951
+ anyHit = true;
1952
+ if (targetHostname && u.includes(targetHostname)) {
1953
+ hostHit = true;
1954
+ break;
1955
+ }
1956
+ }
1957
+ if (hostHit) break;
1958
+ }
1959
+ if (process.env.XB_DEBUG_SESSION_POLL) {
1960
+ console.error(`[poll] t=${Date.now() - pollStart}ms ctxs=${ctxs.length} hostHit=${hostHit} anyHit=${anyHit}`);
1961
+ }
1962
+ if (hostHit) break;
1963
+ if (anyHit && Date.now() - pollStart >= 2e3) break;
1964
+ if (Date.now() - pollStart >= 5e3) break;
1965
+ await new Promise((r) => setTimeout(r, 200));
1966
+ }
1967
+ let contexts = b.contexts();
1968
+ if (contexts.length === 0) {
1969
+ await new Promise((r) => setTimeout(r, 500));
1970
+ contexts = b.contexts();
1971
+ }
1972
+ context = contexts[0] || await b.newContext();
1973
+ let targetPage = null;
1920
1974
  if (targetHostname) {
1921
1975
  for (const ctx of contexts) {
1922
1976
  const pages = ctx.pages();
1923
1977
  for (const p of pages) {
1924
- const pUrl = p.url();
1925
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
1978
+ const pUrl = await resolvePageUrl(p);
1979
+ if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
1926
1980
  targetPage = p;
1927
1981
  break;
1928
1982
  }
@@ -1934,8 +1988,8 @@ async function createSession(name, url, options) {
1934
1988
  for (const ctx of contexts) {
1935
1989
  const pages = ctx.pages();
1936
1990
  for (const p of pages) {
1937
- const pUrl = p.url();
1938
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
1991
+ const pUrl = await resolvePageUrl(p);
1992
+ if (isRealPageUrl(pUrl)) {
1939
1993
  targetPage = p;
1940
1994
  break;
1941
1995
  }
@@ -4682,9 +4682,29 @@ function deleteSessionDiskMeta(name) {
4682
4682
  } catch {
4683
4683
  }
4684
4684
  }
4685
+ async function isSessionPageAlive(session) {
4686
+ const page = session.page;
4687
+ if (!page || typeof page.evaluate !== "function") return false;
4688
+ try {
4689
+ await Promise.race([
4690
+ page.evaluate("1"),
4691
+ new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
4692
+ ]);
4693
+ return true;
4694
+ } catch {
4695
+ return false;
4696
+ }
4697
+ }
4685
4698
  async function findOrRestoreSession(name, cdpEndpoint) {
4686
4699
  const inMem = findSession(name);
4687
- if (inMem) return inMem;
4700
+ if (inMem) {
4701
+ if (await isSessionPageAlive(inMem)) return inMem;
4702
+ logSessionEvent("stale_session", `name="${name}" \u2014 page \u63A2\u9488\u5931\u8D25\uFF08\u6D4F\u89C8\u5668\u53EF\u80FD\u5DF2\u91CD\u542F\uFF09\uFF0C\u5C31\u5730\u91CD\u5EFA`);
4703
+ try {
4704
+ await closeSessionByName(name);
4705
+ } catch {
4706
+ }
4707
+ }
4688
4708
  const meta = readSessionDiskMeta(name);
4689
4709
  if (!meta) return void 0;
4690
4710
  const ep = cdpEndpoint || meta.cdpEndpoint;
@@ -4947,14 +4967,6 @@ async function createSession(name, url, options) {
4947
4967
  let context;
4948
4968
  let page;
4949
4969
  if (isCDP) {
4950
- await new Promise((r) => setTimeout(r, 500));
4951
- let contexts = b.contexts();
4952
- if (contexts.length === 0) {
4953
- await new Promise((r) => setTimeout(r, 500));
4954
- contexts = b.contexts();
4955
- }
4956
- context = contexts[0] || await b.newContext();
4957
- let targetPage = null;
4958
4970
  const targetHostname = url ? (() => {
4959
4971
  try {
4960
4972
  return new URL(url).hostname;
@@ -4962,12 +4974,54 @@ async function createSession(name, url, options) {
4962
4974
  return "";
4963
4975
  }
4964
4976
  })() : "";
4977
+ const isRealPageUrl = (u) => !!u && u !== "about:blank" && !u.startsWith("chrome://") && !u.startsWith("chrome-untrusted://") && !u.startsWith("chrome-error://");
4978
+ const resolvePageUrl = async (p) => {
4979
+ const u = p.url();
4980
+ if (u && u !== "about:blank") return u;
4981
+ try {
4982
+ return await p.evaluate("location.href");
4983
+ } catch {
4984
+ return u ?? "";
4985
+ }
4986
+ };
4987
+ const pollStart = Date.now();
4988
+ for (; ; ) {
4989
+ const ctxs = b.contexts();
4990
+ let hostHit = false;
4991
+ let anyHit = false;
4992
+ for (const ctx of ctxs) {
4993
+ for (const p of ctx.pages()) {
4994
+ const u = await resolvePageUrl(p);
4995
+ if (!isRealPageUrl(u)) continue;
4996
+ anyHit = true;
4997
+ if (targetHostname && u.includes(targetHostname)) {
4998
+ hostHit = true;
4999
+ break;
5000
+ }
5001
+ }
5002
+ if (hostHit) break;
5003
+ }
5004
+ if (process.env.XB_DEBUG_SESSION_POLL) {
5005
+ console.error(`[poll] t=${Date.now() - pollStart}ms ctxs=${ctxs.length} hostHit=${hostHit} anyHit=${anyHit}`);
5006
+ }
5007
+ if (hostHit) break;
5008
+ if (anyHit && Date.now() - pollStart >= 2e3) break;
5009
+ if (Date.now() - pollStart >= 5e3) break;
5010
+ await new Promise((r) => setTimeout(r, 200));
5011
+ }
5012
+ let contexts = b.contexts();
5013
+ if (contexts.length === 0) {
5014
+ await new Promise((r) => setTimeout(r, 500));
5015
+ contexts = b.contexts();
5016
+ }
5017
+ context = contexts[0] || await b.newContext();
5018
+ let targetPage = null;
4965
5019
  if (targetHostname) {
4966
5020
  for (const ctx of contexts) {
4967
5021
  const pages = ctx.pages();
4968
5022
  for (const p of pages) {
4969
- const pUrl = p.url();
4970
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
5023
+ const pUrl = await resolvePageUrl(p);
5024
+ if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
4971
5025
  targetPage = p;
4972
5026
  break;
4973
5027
  }
@@ -4979,8 +5033,8 @@ async function createSession(name, url, options) {
4979
5033
  for (const ctx of contexts) {
4980
5034
  const pages = ctx.pages();
4981
5035
  for (const p of pages) {
4982
- const pUrl = p.url();
4983
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
5036
+ const pUrl = await resolvePageUrl(p);
5037
+ if (isRealPageUrl(pUrl)) {
4984
5038
  targetPage = p;
4985
5039
  break;
4986
5040
  }
@@ -272,9 +272,29 @@ function deleteSessionDiskMeta(name) {
272
272
  } catch {
273
273
  }
274
274
  }
275
+ async function isSessionPageAlive(session) {
276
+ const page = session.page;
277
+ if (!page || typeof page.evaluate !== "function") return false;
278
+ try {
279
+ await Promise.race([
280
+ page.evaluate("1"),
281
+ new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
282
+ ]);
283
+ return true;
284
+ } catch {
285
+ return false;
286
+ }
287
+ }
275
288
  async function findOrRestoreSession(name, cdpEndpoint) {
276
289
  const inMem = findSession(name);
277
- if (inMem) return inMem;
290
+ if (inMem) {
291
+ if (await isSessionPageAlive(inMem)) return inMem;
292
+ logSessionEvent("stale_session", `name="${name}" \u2014 page \u63A2\u9488\u5931\u8D25\uFF08\u6D4F\u89C8\u5668\u53EF\u80FD\u5DF2\u91CD\u542F\uFF09\uFF0C\u5C31\u5730\u91CD\u5EFA`);
293
+ try {
294
+ await closeSessionByName(name);
295
+ } catch {
296
+ }
297
+ }
278
298
  const meta = readSessionDiskMeta(name);
279
299
  if (!meta) return void 0;
280
300
  const ep = cdpEndpoint || meta.cdpEndpoint;
@@ -537,14 +557,6 @@ async function createSession(name, url, options) {
537
557
  let context;
538
558
  let page;
539
559
  if (isCDP) {
540
- await new Promise((r) => setTimeout(r, 500));
541
- let contexts = b.contexts();
542
- if (contexts.length === 0) {
543
- await new Promise((r) => setTimeout(r, 500));
544
- contexts = b.contexts();
545
- }
546
- context = contexts[0] || await b.newContext();
547
- let targetPage = null;
548
560
  const targetHostname = url ? (() => {
549
561
  try {
550
562
  return new URL(url).hostname;
@@ -552,12 +564,54 @@ async function createSession(name, url, options) {
552
564
  return "";
553
565
  }
554
566
  })() : "";
567
+ const isRealPageUrl = (u) => !!u && u !== "about:blank" && !u.startsWith("chrome://") && !u.startsWith("chrome-untrusted://") && !u.startsWith("chrome-error://");
568
+ const resolvePageUrl = async (p) => {
569
+ const u = p.url();
570
+ if (u && u !== "about:blank") return u;
571
+ try {
572
+ return await p.evaluate("location.href");
573
+ } catch {
574
+ return u ?? "";
575
+ }
576
+ };
577
+ const pollStart = Date.now();
578
+ for (; ; ) {
579
+ const ctxs = b.contexts();
580
+ let hostHit = false;
581
+ let anyHit = false;
582
+ for (const ctx of ctxs) {
583
+ for (const p of ctx.pages()) {
584
+ const u = await resolvePageUrl(p);
585
+ if (!isRealPageUrl(u)) continue;
586
+ anyHit = true;
587
+ if (targetHostname && u.includes(targetHostname)) {
588
+ hostHit = true;
589
+ break;
590
+ }
591
+ }
592
+ if (hostHit) break;
593
+ }
594
+ if (process.env.XB_DEBUG_SESSION_POLL) {
595
+ console.error(`[poll] t=${Date.now() - pollStart}ms ctxs=${ctxs.length} hostHit=${hostHit} anyHit=${anyHit}`);
596
+ }
597
+ if (hostHit) break;
598
+ if (anyHit && Date.now() - pollStart >= 2e3) break;
599
+ if (Date.now() - pollStart >= 5e3) break;
600
+ await new Promise((r) => setTimeout(r, 200));
601
+ }
602
+ let contexts = b.contexts();
603
+ if (contexts.length === 0) {
604
+ await new Promise((r) => setTimeout(r, 500));
605
+ contexts = b.contexts();
606
+ }
607
+ context = contexts[0] || await b.newContext();
608
+ let targetPage = null;
555
609
  if (targetHostname) {
556
610
  for (const ctx of contexts) {
557
611
  const pages = ctx.pages();
558
612
  for (const p of pages) {
559
- const pUrl = p.url();
560
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://") && pUrl.includes(targetHostname)) {
613
+ const pUrl = await resolvePageUrl(p);
614
+ if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
561
615
  targetPage = p;
562
616
  break;
563
617
  }
@@ -569,8 +623,8 @@ async function createSession(name, url, options) {
569
623
  for (const ctx of contexts) {
570
624
  const pages = ctx.pages();
571
625
  for (const p of pages) {
572
- const pUrl = p.url();
573
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && !pUrl.startsWith("chrome-untrusted://") && !pUrl.startsWith("chrome-error://")) {
626
+ const pUrl = await resolvePageUrl(p);
627
+ if (isRealPageUrl(pUrl)) {
574
628
  targetPage = p;
575
629
  break;
576
630
  }
package/dist/cli.js CHANGED
@@ -59,7 +59,7 @@ import {
59
59
  resolveLaunchOpts,
60
60
  saveSessionDiskMeta,
61
61
  setActivePage
62
- } from "./chunk-GUTBIYFW.js";
62
+ } from "./chunk-NITFVWWS.js";
63
63
  import "./chunk-TNEN6VQ2.js";
64
64
  import {
65
65
  errMsg
@@ -7051,7 +7051,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7051
7051
  }
7052
7052
  let targetPageOverride = null;
7053
7053
  if (_target && extraOpts?.cdpEndpoint) {
7054
- const { findTargetPage } = await import("./browser-FFYPFTBV.js");
7054
+ const { findTargetPage } = await import("./browser-T3V3JWVH.js");
7055
7055
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7056
7056
  if (!targetPageOverride) {
7057
7057
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -12698,12 +12698,16 @@ async function routeCommand(argvIn, stdinCommands) {
12698
12698
  }
12699
12699
  const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
12700
12700
  let chainArgIdx = -1;
12701
+ let seenCommandWord = false;
12701
12702
  for (let i = 0; i < argv.length; i++) {
12702
12703
  if (globalFlags.has(argv[i])) continue;
12703
12704
  if (globalFlags.has(argv[i]) || i > 0 && globalFlags.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
12704
- if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
12705
- chainArgIdx = i;
12706
- break;
12705
+ if (!seenCommandWord) {
12706
+ if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
12707
+ chainArgIdx = i;
12708
+ break;
12709
+ }
12710
+ seenCommandWord = true;
12707
12711
  }
12708
12712
  }
12709
12713
  if (chainArgIdx >= 0) {
@@ -13107,7 +13111,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13107
13111
  const targetPage = pages[cmdTabIndex];
13108
13112
  await targetPage.bringToFront().catch(() => {
13109
13113
  });
13110
- const { setActivePage: setActivePage2 } = await import("./browser-FFYPFTBV.js");
13114
+ const { setActivePage: setActivePage2 } = await import("./browser-T3V3JWVH.js");
13111
13115
  setActivePage2(session, targetPage);
13112
13116
  }
13113
13117
  }
@@ -13383,7 +13387,7 @@ async function main() {
13383
13387
  const command = process.argv[2];
13384
13388
  const isLongRunning = command === "preview" || command === "serve";
13385
13389
  if (!isLongRunning) {
13386
- const { ensureProcessCanExit } = await import("./browser-FFYPFTBV.js");
13390
+ const { ensureProcessCanExit } = await import("./browser-T3V3JWVH.js");
13387
13391
  await ensureProcessCanExit().catch(() => {
13388
13392
  });
13389
13393
  process.exit(process.exitCode || exitCode);
@@ -31,7 +31,7 @@ import {
31
31
  resolveLaunchOpts,
32
32
  saveSessionDiskMeta,
33
33
  setActivePage
34
- } from "./chunk-6OZWKXSW.js";
34
+ } from "./chunk-BAHSRZIX.js";
35
35
  import "./chunk-RRBXV7KE.js";
36
36
  import "./chunk-TNEN6VQ2.js";
37
37
  import {
@@ -6565,7 +6565,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6565
6565
  }
6566
6566
  let targetPageOverride = null;
6567
6567
  if (_target && extraOpts?.cdpEndpoint) {
6568
- const { findTargetPage } = await import("./browser-RHWUAMTB.js");
6568
+ const { findTargetPage } = await import("./browser-WQZ3D6AE.js");
6569
6569
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6570
6570
  if (!targetPageOverride) {
6571
6571
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8430,7 +8430,19 @@ function createRPCHandler() {
8430
8430
  import { EventEmitter as EventEmitter2 } from "events";
8431
8431
 
8432
8432
  // src/stream/frame-processor.ts
8433
- import sharp from "sharp";
8433
+ var _sharp = null;
8434
+ async function loadSharp() {
8435
+ if (!_sharp) {
8436
+ try {
8437
+ _sharp = (await import("sharp")).default;
8438
+ } catch (err) {
8439
+ throw new Error(
8440
+ "\u76F4\u64AD\u6D41\u5E27\u5904\u7406\u9700\u8981 sharp \u539F\u751F\u6A21\u5757\uFF0C\u5F53\u524D\u73AF\u5883\u52A0\u8F7D\u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)) + "\u3002\u4E0D\u7528\u76F4\u64AD\u6D41\u529F\u80FD\u53EF\u5FFD\u7565\uFF1B\u9700\u8981\u76F4\u64AD\u8BF7\u5728\u517C\u5BB9\u73AF\u5883\u8FD0\u884C\u6216\u5B89\u88C5\u5BF9\u5E94\u5E73\u53F0\u4F9D\u8D56\u3002"
8441
+ );
8442
+ }
8443
+ }
8444
+ return _sharp;
8445
+ }
8434
8446
  var STATE_CONFIGS = {
8435
8447
  // Full-screen: aggressive compression for speed
8436
8448
  // user_interacting: low quality + downscale — prioritize FPS over clarity
@@ -8582,6 +8594,7 @@ var FrameProcessor = class {
8582
8594
  if (!needsResize && !needsCrop && !needsReencode) {
8583
8595
  return buffer;
8584
8596
  }
8597
+ const sharp = await loadSharp();
8585
8598
  let processed = sharp(buffer);
8586
8599
  if (cropConfig) {
8587
8600
  const meta = await processed.metadata();
package/dist/index.js CHANGED
@@ -91,7 +91,7 @@ import {
91
91
  resolveLaunchOpts,
92
92
  saveSessionDiskMeta,
93
93
  setActivePage
94
- } from "./chunk-RZM5CNIY.js";
94
+ } from "./chunk-WSCP7QCJ.js";
95
95
  import "./chunk-RRBXV7KE.js";
96
96
  import "./chunk-TNEN6VQ2.js";
97
97
  import {
@@ -7399,7 +7399,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7399
7399
  }
7400
7400
  let targetPageOverride = null;
7401
7401
  if (_target && extraOpts?.cdpEndpoint) {
7402
- const { findTargetPage } = await import("./browser-YCO4GJMZ.js");
7402
+ const { findTargetPage } = await import("./browser-HBL72GPZ.js");
7403
7403
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7404
7404
  if (!targetPageOverride) {
7405
7405
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -13084,12 +13084,16 @@ async function routeCommand(argvIn, stdinCommands) {
13084
13084
  }
13085
13085
  const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
13086
13086
  let chainArgIdx = -1;
13087
+ let seenCommandWord = false;
13087
13088
  for (let i = 0; i < argv.length; i++) {
13088
13089
  if (globalFlags.has(argv[i])) continue;
13089
13090
  if (globalFlags.has(argv[i]) || i > 0 && globalFlags.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
13090
- if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
13091
- chainArgIdx = i;
13092
- break;
13091
+ if (!seenCommandWord) {
13092
+ if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
13093
+ chainArgIdx = i;
13094
+ break;
13095
+ }
13096
+ seenCommandWord = true;
13093
13097
  }
13094
13098
  }
13095
13099
  if (chainArgIdx >= 0) {
@@ -13493,7 +13497,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13493
13497
  const targetPage = pages[cmdTabIndex];
13494
13498
  await targetPage.bringToFront().catch(() => {
13495
13499
  });
13496
- const { setActivePage: setActivePage2 } = await import("./browser-YCO4GJMZ.js");
13500
+ const { setActivePage: setActivePage2 } = await import("./browser-HBL72GPZ.js");
13497
13501
  setActivePage2(session, targetPage);
13498
13502
  }
13499
13503
  }
@@ -14096,7 +14100,19 @@ var PlaybackEngine = class _PlaybackEngine {
14096
14100
  import { EventEmitter as EventEmitter2 } from "events";
14097
14101
 
14098
14102
  // src/stream/frame-processor.ts
14099
- import sharp from "sharp";
14103
+ var _sharp = null;
14104
+ async function loadSharp() {
14105
+ if (!_sharp) {
14106
+ try {
14107
+ _sharp = (await import("sharp")).default;
14108
+ } catch (err) {
14109
+ throw new Error(
14110
+ "\u76F4\u64AD\u6D41\u5E27\u5904\u7406\u9700\u8981 sharp \u539F\u751F\u6A21\u5757\uFF0C\u5F53\u524D\u73AF\u5883\u52A0\u8F7D\u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)) + "\u3002\u4E0D\u7528\u76F4\u64AD\u6D41\u529F\u80FD\u53EF\u5FFD\u7565\uFF1B\u9700\u8981\u76F4\u64AD\u8BF7\u5728\u517C\u5BB9\u73AF\u5883\u8FD0\u884C\u6216\u5B89\u88C5\u5BF9\u5E94\u5E73\u53F0\u4F9D\u8D56\u3002"
14111
+ );
14112
+ }
14113
+ }
14114
+ return _sharp;
14115
+ }
14100
14116
  var STATE_CONFIGS = {
14101
14117
  // Full-screen: aggressive compression for speed
14102
14118
  // user_interacting: low quality + downscale — prioritize FPS over clarity
@@ -14248,6 +14264,7 @@ var FrameProcessor = class {
14248
14264
  if (!needsResize && !needsCrop && !needsReencode) {
14249
14265
  return buffer;
14250
14266
  }
14267
+ const sharp = await loadSharp();
14251
14268
  let processed = sharp(buffer);
14252
14269
  if (cropConfig) {
14253
14270
  const meta = await processed.metadata();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.9.9",
3
+ "version": "1.10.0",
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": {