@xbrowser/cli 1.0.0 → 1.0.3

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.
Files changed (48) hide show
  1. package/README.md +17 -26
  2. package/dist/{browser-DSVV4GHS.js → browser-5CTOA2WS.js} +4 -3
  3. package/dist/{browser-53KUFEEM.js → browser-ITLZZDHJ.js} +5 -5
  4. package/dist/{browser-GURRY444.js → browser-IUJXXNBT.js} +6 -3
  5. package/dist/{cdp-driver-MNPR3HZH.js → cdp-driver-4X3DK6PS.js} +339 -59
  6. package/dist/{cdp-driver-SSXUGXP6.js → cdp-driver-D6WMSMWX.js} +4 -3
  7. package/dist/chunk-2SVQTI2O.js +2794 -0
  8. package/dist/{chunk-IDVD44ED.js → chunk-6WOSXSCQ.js} +23 -7
  9. package/dist/{chunk-ZZ2TFWIV.js → chunk-ABXMBNQ6.js} +1 -1
  10. package/dist/{chunk-2MFXKN32.js → chunk-ACFE6PKF.js} +1013 -119
  11. package/dist/chunk-AMI64BSD.js +268 -0
  12. package/dist/{chunk-E4O5ZU3H.js → chunk-DKWR54XQ.js} +412 -98
  13. package/dist/{chunk-DTJRVA76.js → chunk-ETCO4SNK.js} +2 -2
  14. package/dist/chunk-GDKLH7ZY.js +8 -0
  15. package/dist/chunk-KFQGP6VL.js +33 -0
  16. package/dist/{chunk-2BQZIT3S.js → chunk-LRBSUKUZ.js} +85 -2497
  17. package/dist/{chunk-ITKPSIP7.js → chunk-MDAPTB7C.js} +6 -25
  18. package/dist/{chunk-42RPMJ76.js → chunk-N2JFPWMI.js} +342 -60
  19. package/dist/chunk-OZKD3W4X.js +417 -0
  20. package/dist/{chunk-T4J4C2NZ.js → chunk-TNEN6VQ2.js} +17 -4
  21. package/dist/{chunk-YKOHDEFV.js → chunk-TWWOIJM7.js} +74 -38
  22. package/dist/chunk-WJRE55TN.js +83 -0
  23. package/dist/cli.js +1558 -1122
  24. package/dist/{convert-EGFYNICZ.js → convert-LB3GJTLR.js} +3 -3
  25. package/dist/{convert-EKQVHKB4.js → convert-R3XXYKC6.js} +2 -2
  26. package/dist/{daemon-client-YAVQ343A.js → daemon-client-3JOKX2L2.js} +3 -2
  27. package/dist/{daemon-client-3VM7VU7O.js → daemon-client-DIEHGP5B.js} +28 -74
  28. package/dist/daemon-main.js +2296 -1722
  29. package/dist/{extract-JUOQQX4V.js → extract-2ZFW2MX7.js} +1 -1
  30. package/dist/{extract-L2IW3IUB.js → extract-BSYBM4MR.js} +1 -1
  31. package/dist/{filter-HC4RA7JY.js → filter-KCFO4RSV.js} +1 -1
  32. package/dist/{filter-VID2GGZ7.js → filter-T7DSZ2X7.js} +1 -1
  33. package/dist/{human-interaction-W753RVJB.js → human-interaction-UKAS5ZXV.js} +2 -2
  34. package/dist/index.d.ts +166 -109
  35. package/dist/index.js +2668 -1742
  36. package/dist/launcher-L2JNDB2H.js +20 -0
  37. package/dist/{launcher-KA7J32K5.js → launcher-OZXJQPNG.js} +1 -1
  38. package/dist/{network-store-66A2RATI.js → network-store-XGZ25FFC.js} +1 -1
  39. package/dist/{network-store-BN6QEZ7R.js → network-store-YVDNUREI.js} +1 -1
  40. package/dist/{parse-action-dsl-T3DYC33D.js → parse-action-dsl-UM333TL2.js} +1 -1
  41. package/dist/{proxy-WKGUCH2C.js → proxy-C6CK3UH5.js} +2 -2
  42. package/dist/session-recorder-RTDGURIJ.js +8 -0
  43. package/dist/session-recorder-YI7YYM36.js +7 -0
  44. package/dist/session-replayer-MY27H4DX.js +276 -0
  45. package/dist/site-knowledge-SYC6VCDB.js +23 -0
  46. package/package.json +5 -4
  47. package/dist/screenshot-CWAWMXVA.js +0 -28
  48. package/dist/session-recorder-MA75PKTQ.js +0 -7
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-42RPMJ76.js";
3
+ } from "./chunk-N2JFPWMI.js";
4
+ import {
5
+ errMsg
6
+ } from "./chunk-GDKLH7ZY.js";
4
7
  import {
5
8
  CDPInterceptorProxy
6
- } from "./chunk-ZZ2TFWIV.js";
9
+ } from "./chunk-ABXMBNQ6.js";
7
10
 
8
11
  // src/browser.ts
9
12
  import { randomUUID } from "crypto";
@@ -65,6 +68,7 @@ async function resolveCDPEndpoint(raw) {
65
68
  }
66
69
 
67
70
  // src/browser.ts
71
+ import { SessionStore } from "@dyyz1993/xcli-core";
68
72
  function logSessionEvent(event, details) {
69
73
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
70
74
  const pid = process.pid;
@@ -77,7 +81,7 @@ function sessionFile(name) {
77
81
  function ensureSessionDir() {
78
82
  mkdirSync(SESSION_DIR, { recursive: true });
79
83
  }
80
- var sessions = /* @__PURE__ */ new Map();
84
+ var sessions = new SessionStore();
81
85
  var _sharedBrowser = null;
82
86
  var _sharedCdpProxy = null;
83
87
  var IDLE_TIMEOUT_MS = (process.env.XBROWSER_IDLE_TIMEOUT ? parseInt(process.env.XBROWSER_IDLE_TIMEOUT, 10) : 30) * 60 * 1e3;
@@ -88,7 +92,7 @@ function resetIdleTimer() {
88
92
  const now = Date.now();
89
93
  let allIdle = true;
90
94
  const idleSessions = [];
91
- for (const [, s] of sessions) {
95
+ for (const s of sessions) {
92
96
  if (now - s.lastActivityAt < IDLE_TIMEOUT_MS) {
93
97
  allIdle = false;
94
98
  } else {
@@ -111,7 +115,7 @@ function touchSession(id) {
111
115
  resetIdleTimer();
112
116
  }
113
117
  process.on("exit", () => {
114
- for (const session of sessions.values()) {
118
+ for (const session of sessions.list()) {
115
119
  if (session.isCDP) {
116
120
  logSessionEvent("process_exit", `Session "${session.name}": CDP connection (not closing external browser).`);
117
121
  } else {
@@ -201,6 +205,9 @@ async function createBrowser(options) {
201
205
  return browser3;
202
206
  }
203
207
  const { browser: browser2 } = await launch({ cdpEndpoint: realEndpoint });
208
+ await browser2.discoverContexts().catch((err) => {
209
+ console.error(`[browser] discoverContexts failed: ${errMsg(err)}`);
210
+ });
204
211
  return browser2;
205
212
  }
206
213
  const executablePath = options?.executablePath || process.env.XBROWSER_CHROMIUM_PATH || discoverChromiumPath();
@@ -215,10 +222,7 @@ async function getBrowser(options) {
215
222
  return _sharedBrowser;
216
223
  }
217
224
  function findSession(name) {
218
- for (const [, session] of sessions) {
219
- if (session.name === name) return session;
220
- }
221
- return void 0;
225
+ return sessions.find(name);
222
226
  }
223
227
  function getSessionById(id) {
224
228
  return sessions.get(id);
@@ -322,9 +326,14 @@ async function findOrRestoreSession(name, cdpEndpoint) {
322
326
  return void 0;
323
327
  }
324
328
  const targetUrl = meta.conversationUrl || meta.url;
325
- if (targetUrl && page.url() !== targetUrl && !page.url().includes(new URL(targetUrl).hostname)) {
326
- await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 3e4 }).catch(() => {
327
- });
329
+ if (targetUrl && page.url() !== targetUrl) {
330
+ try {
331
+ if (!page.url().includes(new URL(targetUrl).hostname)) {
332
+ await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 3e4 }).catch(() => {
333
+ });
334
+ }
335
+ } catch {
336
+ }
328
337
  }
329
338
  const session = {
330
339
  id: meta.id || randomUUID(),
@@ -337,18 +346,18 @@ async function findOrRestoreSession(name, cdpEndpoint) {
337
346
  isCDP: true,
338
347
  cdpEndpoint: ep
339
348
  };
340
- for (const [existingId, existingSession] of sessions) {
349
+ for (const existingSession of sessions.list()) {
341
350
  if (existingSession.name === name) {
342
- logSessionEvent("remove_stale", `Removing stale session name="${name}" id="${existingId}" during restore`);
343
- sessions.delete(existingId);
351
+ logSessionEvent("remove_stale", `Removing stale session name="${name}" id="${existingSession.id}" during restore`);
352
+ sessions.removeById(existingSession.id);
344
353
  }
345
354
  }
346
- sessions.set(session.id, session);
355
+ sessions.set(session);
347
356
  resetIdleTimer();
348
357
  await installNetworkCapture(page, name);
349
358
  return session;
350
359
  } catch (e) {
351
- console.error(`[Session Restore] Failed for "${name}":`, e.message);
360
+ console.error(`[Session Restore] Failed for "${name}":`, errMsg(e));
352
361
  deleteSessionDiskMeta(name);
353
362
  return void 0;
354
363
  }
@@ -359,7 +368,12 @@ async function createEphemeralContext(options) {
359
368
  const { browser: b2 } = await launch({ cdpEndpoint: endpoint });
360
369
  const contexts = b2.contexts();
361
370
  const ctx = contexts[0] || await b2.newContext();
362
- const page2 = await ctx.newPage();
371
+ const allPages = ctx.pages();
372
+ const existingPages = allPages.filter((p) => {
373
+ const url = p.url();
374
+ return url !== "about:blank" && !url.startsWith("chrome://");
375
+ });
376
+ const page2 = existingPages.length > 0 ? existingPages[0] : allPages.length > 0 ? allPages[0] : await ctx.newPage();
363
377
  resetIdleTimer();
364
378
  ephemeralConnections.set(page2, b2);
365
379
  return { context: ctx, page: page2 };
@@ -391,11 +405,11 @@ async function closeEphemeralContext(context) {
391
405
  }
392
406
  }
393
407
  function getAllSessions() {
394
- return Array.from(sessions.values());
408
+ return sessions.list();
395
409
  }
396
410
  async function installNetworkCapture(page, sessionName) {
397
411
  if (process.env.XBROWSER_DAEMON_WORKER !== "1") return;
398
- const { networkStore } = await import("./network-store-BN6QEZ7R.js");
412
+ const { networkStore } = await import("./network-store-YVDNUREI.js");
399
413
  const requestData = /* @__PURE__ */ new Map();
400
414
  const responseMeta = /* @__PURE__ */ new Map();
401
415
  const xbPage = page;
@@ -516,16 +530,38 @@ async function createSession(name, url, options) {
516
530
  }
517
531
  context = contexts[0] || await b.newContext();
518
532
  let targetPage = null;
519
- for (const ctx of contexts) {
520
- const pages = ctx.pages();
521
- for (const p of pages) {
522
- const pUrl = p.url();
523
- if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
524
- targetPage = p;
525
- break;
533
+ const targetHostname = url ? (() => {
534
+ try {
535
+ return new URL(url).hostname;
536
+ } catch {
537
+ return "";
538
+ }
539
+ })() : "";
540
+ if (targetHostname) {
541
+ for (const ctx of contexts) {
542
+ const pages = ctx.pages();
543
+ for (const p of pages) {
544
+ const pUrl = p.url();
545
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && pUrl.includes(targetHostname)) {
546
+ targetPage = p;
547
+ break;
548
+ }
549
+ }
550
+ if (targetPage) break;
551
+ }
552
+ }
553
+ if (!targetPage) {
554
+ for (const ctx of contexts) {
555
+ const pages = ctx.pages();
556
+ for (const p of pages) {
557
+ const pUrl = p.url();
558
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
559
+ targetPage = p;
560
+ break;
561
+ }
526
562
  }
563
+ if (targetPage) break;
527
564
  }
528
- if (targetPage) break;
529
565
  }
530
566
  if (!targetPage && options?.cdpEndpoint) {
531
567
  const targets = await getCDPTargets(options.cdpEndpoint);
@@ -566,14 +602,14 @@ async function createSession(name, url, options) {
566
602
  isCDP,
567
603
  cdpEndpoint: options?.cdpEndpoint
568
604
  };
569
- sessions.set(session.id, session);
605
+ sessions.set(session);
570
606
  logSessionEvent("create_session", `name="${name}" id="${session.id}" url="${url || "(no url)"}" isCDP=${isCDP} cdpEndpoint=${options?.cdpEndpoint || "(none)"}`);
571
607
  resetIdleTimer();
572
608
  await installNetworkCapture(page, name);
573
609
  return session;
574
610
  }
575
611
  async function closeSessionByName(name) {
576
- for (const [id, session] of sessions) {
612
+ for (const session of sessions) {
577
613
  if (session.name === name || session.id === name) {
578
614
  logSessionEvent("close_session", `name="${session.name}" id="${session.id}" url="${session.page.url()}"`);
579
615
  if (session.isCDP) {
@@ -592,20 +628,20 @@ async function closeSessionByName(name) {
592
628
  });
593
629
  }
594
630
  }
595
- sessions.delete(id);
631
+ sessions.removeById(session.id);
596
632
  const file2 = sessionFile(session.name);
597
633
  try {
598
634
  unlinkSync(file2);
599
635
  } catch {
600
636
  }
601
637
  try {
602
- const { networkStore, commandLogStore } = await import("./network-store-BN6QEZ7R.js");
638
+ const { networkStore, commandLogStore } = await import("./network-store-YVDNUREI.js");
603
639
  networkStore.clear(session.name);
604
640
  commandLogStore.clear(session.name);
605
641
  } catch {
606
642
  }
607
643
  try {
608
- const { SessionRecorder } = await import("./session-recorder-MA75PKTQ.js");
644
+ const { SessionRecorder } = await import("./session-recorder-RTDGURIJ.js");
609
645
  SessionRecorder.cleanup(session.name);
610
646
  } catch {
611
647
  }
@@ -620,9 +656,9 @@ async function closeSessionByName(name) {
620
656
  return false;
621
657
  }
622
658
  async function closeAllSessions() {
623
- const names = [...sessions.values()].map((s) => `${s.name}(${s.page.url()})`).join(", ");
659
+ const names = sessions.list().map((s) => `${s.name}(${s.page.url()})`).join(", ");
624
660
  if (names) logSessionEvent("close_all_sessions", `Closing ${sessions.size} sessions: ${names}`);
625
- for (const [id, session] of sessions) {
661
+ for (const session of sessions.list()) {
626
662
  try {
627
663
  if (!session.isCDP) {
628
664
  await session.context.close();
@@ -631,9 +667,9 @@ async function closeAllSessions() {
631
667
  await session.browser.close().catch(() => {
632
668
  });
633
669
  }
634
- sessions.delete(id);
670
+ sessions.removeById(session.id);
635
671
  } catch {
636
- sessions.delete(id);
672
+ sessions.removeById(session.id);
637
673
  }
638
674
  }
639
675
  }
@@ -671,7 +707,7 @@ async function ensureProcessCanExit() {
671
707
  clearTimeout(idleTimer);
672
708
  idleTimer = null;
673
709
  }
674
- for (const session of sessions.values()) {
710
+ for (const session of sessions.list()) {
675
711
  if (session.browser) {
676
712
  if (session.isCDP) {
677
713
  await session.browser.close().catch(() => {
@@ -0,0 +1,83 @@
1
+ // src/daemon/daemon.ts
2
+ import { spawn } from "child_process";
3
+ import { join, dirname } from "path";
4
+ import { homedir } from "os";
5
+ import { fileURLToPath } from "url";
6
+ import { stopDaemon as xcliStopDaemon, isDaemonRunning, getDaemonStatus, killAllDaemon } from "@dyyz1993/xcli-core";
7
+ var CONFIG_DIR = join(homedir(), ".xbrowser");
8
+ var __dirname = dirname(fileURLToPath(import.meta.url));
9
+ var WORKER_PATH = join(__dirname, "daemon-main.js");
10
+ function getDaemonConfig() {
11
+ return {
12
+ configDir: CONFIG_DIR,
13
+ workerEntryPath: WORKER_PATH,
14
+ basePort: 9224
15
+ };
16
+ }
17
+ async function startDaemonProcess(port = 9224) {
18
+ const config = getDaemonConfig();
19
+ if (isDaemonRunning(config)) {
20
+ const status = getDaemonStatus(config);
21
+ if (status.port === port && status.pid) {
22
+ return { pid: status.pid, port: status.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
23
+ }
24
+ await xcliStopDaemon(config);
25
+ }
26
+ const child = spawn("node", [WORKER_PATH], {
27
+ detached: true,
28
+ stdio: "ignore",
29
+ env: {
30
+ ...process.env,
31
+ XBROWSER_DAEMON_PORT: String(port)
32
+ }
33
+ });
34
+ child.unref();
35
+ return new Promise((resolve, reject) => {
36
+ let resolved = false;
37
+ const timeout = setTimeout(() => {
38
+ if (!resolved) {
39
+ resolved = true;
40
+ reject(new Error("Daemon start timeout after 15s"));
41
+ }
42
+ }, 15e3);
43
+ const checkInterval = setInterval(() => {
44
+ if (isDaemonRunning(config)) {
45
+ const s = getDaemonStatus(config);
46
+ if (s.port === port && s.pid) {
47
+ resolved = true;
48
+ clearTimeout(timeout);
49
+ clearInterval(checkInterval);
50
+ resolve({ pid: s.pid, port: s.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
51
+ }
52
+ }
53
+ }, 200);
54
+ child.on("error", (err) => {
55
+ if (!resolved) {
56
+ resolved = true;
57
+ clearTimeout(timeout);
58
+ clearInterval(checkInterval);
59
+ reject(err);
60
+ }
61
+ });
62
+ });
63
+ }
64
+ function getDaemonProcessStatus() {
65
+ const config = getDaemonConfig();
66
+ const running = isDaemonRunning(config);
67
+ if (!running) {
68
+ return { running: false, pid: 0, port: 0, info: null };
69
+ }
70
+ const status = getDaemonStatus(config);
71
+ return {
72
+ running: true,
73
+ pid: status.pid,
74
+ port: status.port,
75
+ info: { pid: status.pid, port: status.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
76
+ };
77
+ }
78
+
79
+ export {
80
+ getDaemonConfig,
81
+ startDaemonProcess,
82
+ getDaemonProcessStatus
83
+ };