@xbrowser/cli 1.0.0 → 1.0.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.
Files changed (46) hide show
  1. package/README.md +17 -26
  2. package/dist/{browser-GURRY444.js → browser-GITRHHFO.js} +4 -3
  3. package/dist/{browser-DSVV4GHS.js → browser-R56O3CW6.js} +3 -3
  4. package/dist/{browser-53KUFEEM.js → browser-ZJOZB5CR.js} +4 -4
  5. package/dist/{cdp-driver-MNPR3HZH.js → cdp-driver-BE3FOMRN.js} +324 -58
  6. package/dist/{cdp-driver-SSXUGXP6.js → cdp-driver-TOPYJIFL.js} +3 -3
  7. package/dist/chunk-2SVQTI2O.js +2794 -0
  8. package/dist/{chunk-2MFXKN32.js → chunk-ACFE6PKF.js} +1013 -119
  9. package/dist/chunk-BBMRDUYQ.js +260 -0
  10. package/dist/{chunk-E4O5ZU3H.js → chunk-CAFNSGYM.js} +393 -95
  11. package/dist/{chunk-DTJRVA76.js → chunk-ETCO4SNK.js} +2 -2
  12. package/dist/{chunk-YKOHDEFV.js → chunk-JPA2ZT2R.js} +69 -36
  13. package/dist/{chunk-T4J4C2NZ.js → chunk-JPHCY4TC.js} +12 -2
  14. package/dist/chunk-KFQGP6VL.js +33 -0
  15. package/dist/{chunk-ITKPSIP7.js → chunk-MDAPTB7C.js} +6 -25
  16. package/dist/chunk-OZKD3W4X.js +417 -0
  17. package/dist/{chunk-42RPMJ76.js → chunk-PPG4D2EW.js} +325 -59
  18. package/dist/{chunk-IDVD44ED.js → chunk-Q4IGYTKR.js} +19 -7
  19. package/dist/{chunk-2BQZIT3S.js → chunk-QIK2I3VQ.js} +86 -2501
  20. package/dist/chunk-WJRE55TN.js +83 -0
  21. package/dist/cli.js +1435 -1077
  22. package/dist/{convert-EGFYNICZ.js → convert-LB3GJTLR.js} +3 -3
  23. package/dist/{convert-EKQVHKB4.js → convert-R3XXYKC6.js} +2 -2
  24. package/dist/{daemon-client-3VM7VU7O.js → daemon-client-DRCUMNHK.js} +25 -74
  25. package/dist/{daemon-client-YAVQ343A.js → daemon-client-UZZEHHIV.js} +2 -2
  26. package/dist/daemon-main.js +2200 -1691
  27. package/dist/{extract-JUOQQX4V.js → extract-2ZFW2MX7.js} +1 -1
  28. package/dist/{extract-L2IW3IUB.js → extract-BSYBM4MR.js} +1 -1
  29. package/dist/{filter-HC4RA7JY.js → filter-KCFO4RSV.js} +1 -1
  30. package/dist/{filter-VID2GGZ7.js → filter-T7DSZ2X7.js} +1 -1
  31. package/dist/{human-interaction-W753RVJB.js → human-interaction-UKAS5ZXV.js} +2 -2
  32. package/dist/index.d.ts +165 -108
  33. package/dist/index.js +2531 -1680
  34. package/dist/launcher-QUJ4M2VS.js +19 -0
  35. package/dist/{launcher-KA7J32K5.js → launcher-YARP45UY.js} +1 -1
  36. package/dist/{network-store-66A2RATI.js → network-store-XGZ25FFC.js} +1 -1
  37. package/dist/{network-store-BN6QEZ7R.js → network-store-YVDNUREI.js} +1 -1
  38. package/dist/{parse-action-dsl-T3DYC33D.js → parse-action-dsl-UM333TL2.js} +1 -1
  39. package/dist/{proxy-WKGUCH2C.js → proxy-LV4BJ5RC.js} +1 -1
  40. package/dist/session-recorder-RTDGURIJ.js +8 -0
  41. package/dist/session-recorder-YI7YYM36.js +7 -0
  42. package/dist/session-replayer-GLTUICSD.js +276 -0
  43. package/dist/site-knowledge-SYC6VCDB.js +23 -0
  44. package/package.json +5 -4
  45. package/dist/screenshot-CWAWMXVA.js +0 -28
  46. package/dist/session-recorder-MA75PKTQ.js +0 -7
@@ -9,12 +9,12 @@ function generateJSScript(recording) {
9
9
  // Start URL: ${recording.startUrl}
10
10
  // Events: ${events.length}
11
11
 
12
- import { chromium } from 'playwright';
12
+ import { launch } from '@xbrowser/cli';
13
13
 
14
14
  const START_URL = '${escapeString(recording.startUrl)}';
15
15
 
16
16
  async function main() {
17
- const browser = await chromium.launch({ headless: true });
17
+ const { browser } = await launch({ headless: true });
18
18
  const context = await browser.newContext();
19
19
  const page = await context.newPage();
20
20
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-42RPMJ76.js";
3
+ } from "./chunk-PPG4D2EW.js";
4
4
  import {
5
5
  CDPInterceptorProxy
6
6
  } from "./chunk-ZZ2TFWIV.js";
@@ -65,6 +65,7 @@ async function resolveCDPEndpoint(raw) {
65
65
  }
66
66
 
67
67
  // src/browser.ts
68
+ import { SessionStore } from "@dyyz1993/xcli-core";
68
69
  function logSessionEvent(event, details) {
69
70
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
70
71
  const pid = process.pid;
@@ -77,7 +78,7 @@ function sessionFile(name) {
77
78
  function ensureSessionDir() {
78
79
  mkdirSync(SESSION_DIR, { recursive: true });
79
80
  }
80
- var sessions = /* @__PURE__ */ new Map();
81
+ var sessions = new SessionStore();
81
82
  var _sharedBrowser = null;
82
83
  var _sharedCdpProxy = null;
83
84
  var IDLE_TIMEOUT_MS = (process.env.XBROWSER_IDLE_TIMEOUT ? parseInt(process.env.XBROWSER_IDLE_TIMEOUT, 10) : 30) * 60 * 1e3;
@@ -88,7 +89,7 @@ function resetIdleTimer() {
88
89
  const now = Date.now();
89
90
  let allIdle = true;
90
91
  const idleSessions = [];
91
- for (const [, s] of sessions) {
92
+ for (const s of sessions) {
92
93
  if (now - s.lastActivityAt < IDLE_TIMEOUT_MS) {
93
94
  allIdle = false;
94
95
  } else {
@@ -111,7 +112,7 @@ function touchSession(id) {
111
112
  resetIdleTimer();
112
113
  }
113
114
  process.on("exit", () => {
114
- for (const session of sessions.values()) {
115
+ for (const session of sessions.list()) {
115
116
  if (session.isCDP) {
116
117
  logSessionEvent("process_exit", `Session "${session.name}": CDP connection (not closing external browser).`);
117
118
  } else {
@@ -201,6 +202,9 @@ async function createBrowser(options) {
201
202
  return browser3;
202
203
  }
203
204
  const { browser: browser2 } = await launch({ cdpEndpoint: realEndpoint });
205
+ await browser2.discoverContexts().catch((err) => {
206
+ console.error(`[browser] discoverContexts failed: ${err.message}`);
207
+ });
204
208
  return browser2;
205
209
  }
206
210
  const executablePath = options?.executablePath || process.env.XBROWSER_CHROMIUM_PATH || discoverChromiumPath();
@@ -215,10 +219,7 @@ async function getBrowser(options) {
215
219
  return _sharedBrowser;
216
220
  }
217
221
  function findSession(name) {
218
- for (const [, session] of sessions) {
219
- if (session.name === name) return session;
220
- }
221
- return void 0;
222
+ return sessions.find(name);
222
223
  }
223
224
  function getSessionById(id) {
224
225
  return sessions.get(id);
@@ -322,9 +323,14 @@ async function findOrRestoreSession(name, cdpEndpoint) {
322
323
  return void 0;
323
324
  }
324
325
  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
- });
326
+ if (targetUrl && page.url() !== targetUrl) {
327
+ try {
328
+ if (!page.url().includes(new URL(targetUrl).hostname)) {
329
+ await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 3e4 }).catch(() => {
330
+ });
331
+ }
332
+ } catch {
333
+ }
328
334
  }
329
335
  const session = {
330
336
  id: meta.id || randomUUID(),
@@ -337,13 +343,13 @@ async function findOrRestoreSession(name, cdpEndpoint) {
337
343
  isCDP: true,
338
344
  cdpEndpoint: ep
339
345
  };
340
- for (const [existingId, existingSession] of sessions) {
346
+ for (const existingSession of sessions.list()) {
341
347
  if (existingSession.name === name) {
342
- logSessionEvent("remove_stale", `Removing stale session name="${name}" id="${existingId}" during restore`);
343
- sessions.delete(existingId);
348
+ logSessionEvent("remove_stale", `Removing stale session name="${name}" id="${existingSession.id}" during restore`);
349
+ sessions.removeById(existingSession.id);
344
350
  }
345
351
  }
346
- sessions.set(session.id, session);
352
+ sessions.set(session);
347
353
  resetIdleTimer();
348
354
  await installNetworkCapture(page, name);
349
355
  return session;
@@ -359,7 +365,12 @@ async function createEphemeralContext(options) {
359
365
  const { browser: b2 } = await launch({ cdpEndpoint: endpoint });
360
366
  const contexts = b2.contexts();
361
367
  const ctx = contexts[0] || await b2.newContext();
362
- const page2 = await ctx.newPage();
368
+ const allPages = ctx.pages();
369
+ const existingPages = allPages.filter((p) => {
370
+ const url = p.url();
371
+ return url !== "about:blank" && !url.startsWith("chrome://");
372
+ });
373
+ const page2 = existingPages.length > 0 ? existingPages[0] : allPages.length > 0 ? allPages[0] : await ctx.newPage();
363
374
  resetIdleTimer();
364
375
  ephemeralConnections.set(page2, b2);
365
376
  return { context: ctx, page: page2 };
@@ -391,11 +402,11 @@ async function closeEphemeralContext(context) {
391
402
  }
392
403
  }
393
404
  function getAllSessions() {
394
- return Array.from(sessions.values());
405
+ return sessions.list();
395
406
  }
396
407
  async function installNetworkCapture(page, sessionName) {
397
408
  if (process.env.XBROWSER_DAEMON_WORKER !== "1") return;
398
- const { networkStore } = await import("./network-store-BN6QEZ7R.js");
409
+ const { networkStore } = await import("./network-store-YVDNUREI.js");
399
410
  const requestData = /* @__PURE__ */ new Map();
400
411
  const responseMeta = /* @__PURE__ */ new Map();
401
412
  const xbPage = page;
@@ -516,16 +527,38 @@ async function createSession(name, url, options) {
516
527
  }
517
528
  context = contexts[0] || await b.newContext();
518
529
  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;
530
+ const targetHostname = url ? (() => {
531
+ try {
532
+ return new URL(url).hostname;
533
+ } catch {
534
+ return "";
535
+ }
536
+ })() : "";
537
+ if (targetHostname) {
538
+ for (const ctx of contexts) {
539
+ const pages = ctx.pages();
540
+ for (const p of pages) {
541
+ const pUrl = p.url();
542
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://") && pUrl.includes(targetHostname)) {
543
+ targetPage = p;
544
+ break;
545
+ }
546
+ }
547
+ if (targetPage) break;
548
+ }
549
+ }
550
+ if (!targetPage) {
551
+ for (const ctx of contexts) {
552
+ const pages = ctx.pages();
553
+ for (const p of pages) {
554
+ const pUrl = p.url();
555
+ if (pUrl && pUrl !== "about:blank" && !pUrl.startsWith("chrome://")) {
556
+ targetPage = p;
557
+ break;
558
+ }
526
559
  }
560
+ if (targetPage) break;
527
561
  }
528
- if (targetPage) break;
529
562
  }
530
563
  if (!targetPage && options?.cdpEndpoint) {
531
564
  const targets = await getCDPTargets(options.cdpEndpoint);
@@ -566,14 +599,14 @@ async function createSession(name, url, options) {
566
599
  isCDP,
567
600
  cdpEndpoint: options?.cdpEndpoint
568
601
  };
569
- sessions.set(session.id, session);
602
+ sessions.set(session);
570
603
  logSessionEvent("create_session", `name="${name}" id="${session.id}" url="${url || "(no url)"}" isCDP=${isCDP} cdpEndpoint=${options?.cdpEndpoint || "(none)"}`);
571
604
  resetIdleTimer();
572
605
  await installNetworkCapture(page, name);
573
606
  return session;
574
607
  }
575
608
  async function closeSessionByName(name) {
576
- for (const [id, session] of sessions) {
609
+ for (const session of sessions) {
577
610
  if (session.name === name || session.id === name) {
578
611
  logSessionEvent("close_session", `name="${session.name}" id="${session.id}" url="${session.page.url()}"`);
579
612
  if (session.isCDP) {
@@ -592,20 +625,20 @@ async function closeSessionByName(name) {
592
625
  });
593
626
  }
594
627
  }
595
- sessions.delete(id);
628
+ sessions.removeById(session.id);
596
629
  const file2 = sessionFile(session.name);
597
630
  try {
598
631
  unlinkSync(file2);
599
632
  } catch {
600
633
  }
601
634
  try {
602
- const { networkStore, commandLogStore } = await import("./network-store-BN6QEZ7R.js");
635
+ const { networkStore, commandLogStore } = await import("./network-store-YVDNUREI.js");
603
636
  networkStore.clear(session.name);
604
637
  commandLogStore.clear(session.name);
605
638
  } catch {
606
639
  }
607
640
  try {
608
- const { SessionRecorder } = await import("./session-recorder-MA75PKTQ.js");
641
+ const { SessionRecorder } = await import("./session-recorder-RTDGURIJ.js");
609
642
  SessionRecorder.cleanup(session.name);
610
643
  } catch {
611
644
  }
@@ -620,9 +653,9 @@ async function closeSessionByName(name) {
620
653
  return false;
621
654
  }
622
655
  async function closeAllSessions() {
623
- const names = [...sessions.values()].map((s) => `${s.name}(${s.page.url()})`).join(", ");
656
+ const names = sessions.list().map((s) => `${s.name}(${s.page.url()})`).join(", ");
624
657
  if (names) logSessionEvent("close_all_sessions", `Closing ${sessions.size} sessions: ${names}`);
625
- for (const [id, session] of sessions) {
658
+ for (const session of sessions.list()) {
626
659
  try {
627
660
  if (!session.isCDP) {
628
661
  await session.context.close();
@@ -631,9 +664,9 @@ async function closeAllSessions() {
631
664
  await session.browser.close().catch(() => {
632
665
  });
633
666
  }
634
- sessions.delete(id);
667
+ sessions.removeById(session.id);
635
668
  } catch {
636
- sessions.delete(id);
669
+ sessions.removeById(session.id);
637
670
  }
638
671
  }
639
672
  }
@@ -671,7 +704,7 @@ async function ensureProcessCanExit() {
671
704
  clearTimeout(idleTimer);
672
705
  idleTimer = null;
673
706
  }
674
- for (const session of sessions.values()) {
707
+ for (const session of sessions.list()) {
675
708
  if (session.browser) {
676
709
  if (session.isCDP) {
677
710
  await session.browser.close().catch(() => {
@@ -71,7 +71,16 @@ async function launchChrome(options = {}) {
71
71
  const chromePath = executablePath ?? findChrome();
72
72
  if (!chromePath) {
73
73
  throw new Error(
74
- "Chrome/Chromium not found. Set executablePath or install Chrome to a default location."
74
+ [
75
+ "Chrome/Chromium not found.",
76
+ "",
77
+ "\u63A8\u8350\uFF1A\u7528 cdp-tunnel \u590D\u7528\u4F60\u5DF2\u6709\u7684 Chrome\uFF08\u542B\u767B\u5F55\u6001\u3001\u53CD\u722C\u53CB\u597D\uFF09",
78
+ " npx cdp-tunnel setup # \u96F6\u5B89\u88C5\u4E00\u952E\u542F\u52A8\u4EE3\u7406 + \u52A0\u8F7D Chrome \u6269\u5C55",
79
+ " xbrowser goto https://example.com --cdp http://localhost:9221",
80
+ "",
81
+ "\u6216\u6307\u5B9A Chrome \u8DEF\u5F84\uFF1A",
82
+ ' xbrowser config set browser.executablePath "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"'
83
+ ].join("\n")
75
84
  );
76
85
  }
77
86
  const port = await findFreePort();
@@ -84,7 +93,8 @@ async function launchChrome(options = {}) {
84
93
  "--disable-background-timer-throttling",
85
94
  "--disable-backgrounding-occluded-windows",
86
95
  "--disable-renderer-backgrounding",
87
- "--disable-features=Translate"
96
+ "--disable-features=Translate",
97
+ "--disable-popup-blocking"
88
98
  ];
89
99
  if (headless) {
90
100
  allArgs.push("--headless", "--hide-scrollbars", "--mute-audio");
@@ -0,0 +1,33 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ export {
29
+ __require,
30
+ __esm,
31
+ __export,
32
+ __toCommonJS
33
+ };
@@ -276,35 +276,17 @@ var WebhookNotifier = class {
276
276
  };
277
277
 
278
278
  // src/config.ts
279
- import { existsSync, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
280
- import { join } from "path";
281
279
  import { homedir, tmpdir } from "os";
282
-
283
- // src/utils/json-file.ts
284
- import { readFileSync, writeFileSync } from "fs";
285
- function readJsonFile(filePath, defaultValue) {
286
- try {
287
- const content = readFileSync(filePath, "utf-8");
288
- return JSON.parse(content);
289
- } catch {
290
- return defaultValue;
291
- }
292
- }
293
-
294
- // src/config.ts
295
- function getConfigFile() {
296
- return join(homedir() || tmpdir(), ".xbrowser", "config.json");
280
+ import { join } from "path";
281
+ import { loadConfig as coreLoadConfig, saveConfig as coreSaveConfig } from "@dyyz1993/xcli-core";
282
+ function getConfigSource() {
283
+ return { configDir: join(homedir() || tmpdir(), ".xbrowser") };
297
284
  }
298
285
  function loadConfig() {
299
- const configFile = getConfigFile();
300
- if (!existsSync(configFile)) return {};
301
- return readJsonFile(configFile, {});
286
+ return coreLoadConfig(getConfigSource());
302
287
  }
303
288
  function saveConfig(config) {
304
- const dir = join(homedir() || tmpdir(), ".xbrowser");
305
- const configFile = getConfigFile();
306
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
307
- writeFileSync2(configFile, JSON.stringify(config, null, 2), "utf-8");
289
+ coreSaveConfig(getConfigSource(), config);
308
290
  }
309
291
  function getConfigValue(key) {
310
292
  return loadConfig()[key];
@@ -510,7 +492,6 @@ var HumanInteractionManager = class {
510
492
  };
511
493
 
512
494
  export {
513
- readJsonFile,
514
495
  ScreencastCapturer,
515
496
  CaptchaDetector,
516
497
  WebhookNotifier,