@xbrowser/cli 0.15.1 → 1.0.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.
Files changed (30) hide show
  1. package/dist/{browser-R7B255ML.js → browser-53KUFEEM.js} +5 -1
  2. package/dist/{browser-I2HJZ7IP.js → browser-DSVV4GHS.js} +2 -2
  3. package/dist/{browser-GWBH6OJK.js → browser-GURRY444.js} +3 -1
  4. package/dist/cdp-driver-MNPR3HZH.js +2537 -0
  5. package/dist/cdp-driver-SSXUGXP6.js +47 -0
  6. package/dist/{chunk-2ONMTDLK.js → chunk-2BQZIT3S.js} +2535 -50
  7. package/dist/{chunk-KDYXFLAC.js → chunk-2MFXKN32.js} +2 -2
  8. package/dist/chunk-42RPMJ76.js +2530 -0
  9. package/dist/{chunk-F3ZWFCJJ.js → chunk-E4O5ZU3H.js} +2535 -50
  10. package/dist/{chunk-ATFTAKMN.js → chunk-IDVD44ED.js} +20 -0
  11. package/dist/chunk-T4J4C2NZ.js +250 -0
  12. package/dist/{chunk-RS6YYWTK.js → chunk-YKOHDEFV.js} +73 -38
  13. package/dist/cli.js +1176 -281
  14. package/dist/{convert-4DUWZIKH.js → convert-EGFYNICZ.js} +2 -0
  15. package/dist/{daemon-client-GX2UYIW4.js → daemon-client-3VM7VU7O.js} +22 -0
  16. package/dist/{daemon-client-3IJD6X4B.js → daemon-client-YAVQ343A.js} +7 -1
  17. package/dist/daemon-main.js +1004 -161
  18. package/dist/{extract-EGRXZSSK.js → extract-L2IW3IUB.js} +2 -0
  19. package/dist/{filter-OLAE26HN.js → filter-HC4RA7JY.js} +2 -0
  20. package/dist/index.d.ts +581 -41
  21. package/dist/index.js +1220 -321
  22. package/dist/launcher-KA7J32K5.js +19 -0
  23. package/dist/{network-store-YAF5OIBH.js → network-store-66A2RATI.js} +1 -0
  24. package/dist/{session-recorder-XET3DNML.js → session-recorder-MA75PKTQ.js} +1 -1
  25. package/package.json +3 -4
  26. package/dist/daemon-client-XWSSQBEA.js +0 -58
  27. package/dist/network-store-2S5HATEV.js +0 -194
  28. package/dist/parse-action-dsl-DRSPBALP.js +0 -72
  29. package/dist/screenshot-MB6R7RSS.js +0 -26
  30. package/dist/session-recorder-ILSSV2UC.js +0 -6
@@ -170,6 +170,23 @@ async function forwardChain(input, session = "default", cdpEndpoint) {
170
170
  return { success: false, steps: [], totalDuration: 0, stoppedReason: "Daemon error" };
171
171
  }
172
172
  }
173
+ async function forwardAgentObserve(session = "default", options) {
174
+ const params = { session };
175
+ if (options?.cdpEndpoint) params.cdpEndpoint = options.cdpEndpoint;
176
+ if (options?.includeHidden !== void 0) params.includeHidden = options.includeHidden;
177
+ if (options?.limit !== void 0) params.limit = options.limit;
178
+ return rpcCall("agent:observe", params, 3e4);
179
+ }
180
+ async function forwardAgentAct(session = "default", params, cdpEndpoint) {
181
+ const rpcParams = { ...params, session };
182
+ if (cdpEndpoint) rpcParams.cdpEndpoint = cdpEndpoint;
183
+ return rpcCall("agent:act", rpcParams, 3e4);
184
+ }
185
+ async function forwardAgentWait(session = "default", params, cdpEndpoint, timeoutMs = 3e4) {
186
+ const rpcParams = { ...params, session };
187
+ if (cdpEndpoint) rpcParams.cdpEndpoint = cdpEndpoint;
188
+ return rpcCall("agent:wait", rpcParams, timeoutMs + 5e3);
189
+ }
173
190
  async function forwardNetworkList(sessionName, options) {
174
191
  return rpcCall("network:list", { session: sessionName, ...options }, 3e4);
175
192
  }
@@ -244,6 +261,9 @@ export {
244
261
  forwardSessionList,
245
262
  forwardExec,
246
263
  forwardChain,
264
+ forwardAgentObserve,
265
+ forwardAgentAct,
266
+ forwardAgentWait,
247
267
  forwardNetworkList,
248
268
  forwardNetworkClear,
249
269
  forwardNetworkTop,
@@ -0,0 +1,250 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-3RG5ZIWI.js";
4
+
5
+ // src/cdp-driver/launcher.ts
6
+ import { spawn } from "child_process";
7
+ import { existsSync as fsExistsSync } from "fs";
8
+ var DEFAULT_CHROME_PATHS = {
9
+ darwin: [
10
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
11
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
12
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
13
+ "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
14
+ ],
15
+ linux: [
16
+ "/usr/bin/google-chrome",
17
+ "/usr/bin/google-chrome-stable",
18
+ "/usr/bin/chromium",
19
+ "/usr/bin/chromium-browser",
20
+ "/usr/bin/microsoft-edge"
21
+ ],
22
+ win32: [
23
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
24
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
25
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
26
+ ]
27
+ };
28
+ var DEFAULT_ARGS = [
29
+ "--no-first-run",
30
+ "--no-default-browser-check",
31
+ "--no-sandbox",
32
+ "--disable-background-networking",
33
+ "--disable-background-timer-throttling",
34
+ "--disable-backgrounding-occluded-windows",
35
+ "--disable-breakpad",
36
+ "--disable-client-side-phishing-detection",
37
+ "--disable-default-apps",
38
+ "--disable-extensions",
39
+ "--disable-hang-monitor",
40
+ "--disable-ipc-flood-protection",
41
+ "--disable-popup-blocking",
42
+ "--disable-prompt-on-repost",
43
+ "--disable-renderer-backgrounding",
44
+ "--disable-sync",
45
+ "--disable-translate",
46
+ "--metrics-recording-only",
47
+ "--password-store=basic",
48
+ "--use-mock-keychain"
49
+ ];
50
+ var ANTI_DETECT_ARGS = [
51
+ "--disable-blink-features=AutomationControlled",
52
+ "--disable-features=IsolateOrigins,site-per-process"
53
+ ];
54
+ function findChrome() {
55
+ const platform = process.platform;
56
+ const paths = DEFAULT_CHROME_PATHS[platform] ?? [];
57
+ for (const p of paths) {
58
+ if (fsExistsSync(p)) return p;
59
+ }
60
+ return null;
61
+ }
62
+ async function launchChrome(options = {}) {
63
+ const {
64
+ executablePath,
65
+ headless = true,
66
+ args: extraArgs = [],
67
+ userDataDir,
68
+ timeout = 3e4,
69
+ env
70
+ } = options;
71
+ const chromePath = executablePath ?? findChrome();
72
+ if (!chromePath) {
73
+ throw new Error(
74
+ "Chrome/Chromium not found. Set executablePath or install Chrome to a default location."
75
+ );
76
+ }
77
+ const port = await findFreePort();
78
+ const allArgs = [
79
+ `--remote-debugging-port=${port}`,
80
+ "--remote-allow-origins=*",
81
+ "--no-sandbox",
82
+ "--no-first-run",
83
+ "--no-default-browser-check",
84
+ "--disable-background-timer-throttling",
85
+ "--disable-backgrounding-occluded-windows",
86
+ "--disable-renderer-backgrounding",
87
+ "--disable-features=Translate"
88
+ ];
89
+ if (headless) {
90
+ allArgs.push("--headless", "--hide-scrollbars", "--mute-audio");
91
+ }
92
+ let tmpDir;
93
+ if (userDataDir) {
94
+ allArgs.push(`--user-data-dir=${userDataDir}`);
95
+ } else {
96
+ const { mkdirSync } = await import("fs");
97
+ tmpDir = `/tmp/xbrowser-chrome-${process.pid}-${Date.now()}`;
98
+ mkdirSync(tmpDir, { recursive: true });
99
+ allArgs.push(`--user-data-dir=${tmpDir}`);
100
+ }
101
+ allArgs.push(...extraArgs, "about:blank");
102
+ const childEnv = {
103
+ ...process.env,
104
+ ...env
105
+ };
106
+ const quotedPath = chromePath.includes(" ") ? `"${chromePath}"` : chromePath;
107
+ const quotedArgs = allArgs.map((a) => {
108
+ if (a.includes(" ")) return `"${a}"`;
109
+ return a;
110
+ }).join(" ");
111
+ const fullCmd = `${quotedPath} ${quotedArgs}`;
112
+ const child = process.platform === "darwin" ? spawn("/bin/sh", ["-c", fullCmd], {
113
+ stdio: ["ignore", "pipe", "pipe"],
114
+ env: childEnv
115
+ }) : spawn(chromePath, allArgs, {
116
+ stdio: ["ignore", "pipe", "pipe"],
117
+ env: childEnv
118
+ });
119
+ const stderrLines = [];
120
+ child.stderr?.on("data", (data) => {
121
+ const line = data.toString().trim();
122
+ if (line) stderrLines.push(line);
123
+ });
124
+ child.stdout?.on("data", (data) => {
125
+ const line = data.toString().trim();
126
+ if (line) stderrLines.push(`[stdout] ${line}`);
127
+ });
128
+ child.on("error", (err) => {
129
+ if (!child.killed) {
130
+ console.error(`Chrome process error: ${err.message}`);
131
+ }
132
+ });
133
+ try {
134
+ const wsEndpoint = await waitForCDPReady(port, timeout, child);
135
+ return { process: child, wsEndpoint, port, tmpDir };
136
+ } catch (err) {
137
+ const stderr = stderrLines.slice(-20).join("\n");
138
+ const exitInfo = child.exitCode !== null ? ` (exit code: ${child.exitCode})` : " (still running)";
139
+ throw new Error(`${err.message}${exitInfo}
140
+ Chrome stderr:
141
+ ${stderr || "(empty)"}`);
142
+ }
143
+ }
144
+ async function connectToCDP(rawEndpoint) {
145
+ if (rawEndpoint.startsWith("ws://") || rawEndpoint.startsWith("wss://")) {
146
+ return rawEndpoint;
147
+ }
148
+ return resolveEndpointFromHTTP(rawEndpoint);
149
+ }
150
+ async function findFreePort() {
151
+ const { createServer } = await import("net");
152
+ return new Promise((resolve, reject) => {
153
+ const srv = createServer();
154
+ srv.listen(0, "127.0.0.1", () => {
155
+ const addr = srv.address();
156
+ if (addr && typeof addr === "object") {
157
+ const port = addr.port;
158
+ srv.close(() => resolve(port));
159
+ } else {
160
+ srv.close();
161
+ reject(new Error("Failed to find free port"));
162
+ }
163
+ });
164
+ srv.on("error", reject);
165
+ });
166
+ }
167
+ async function waitForCDPReady(port, timeoutMs, child) {
168
+ const deadline = Date.now() + timeoutMs;
169
+ while (Date.now() < deadline) {
170
+ if (child.exitCode !== null && child.exitCode !== 0) {
171
+ throw new Error(`Chrome exited with code ${child.exitCode} before CDP became ready`);
172
+ }
173
+ try {
174
+ const wsEndpoint = await resolveEndpointFromHTTP(`http://127.0.0.1:${port}`);
175
+ return wsEndpoint;
176
+ } catch {
177
+ await sleep(200);
178
+ }
179
+ }
180
+ throw new Error(`Chrome CDP not ready after ${timeoutMs}ms (port ${port})`);
181
+ }
182
+ async function resolveEndpointFromHTTP(baseURL) {
183
+ const url = `${baseURL}/json/version`;
184
+ const resp = await fetch(url, { signal: AbortSignal.timeout(3e3) });
185
+ if (!resp.ok) {
186
+ throw new Error(`CDP HTTP ${resp.status}: ${url}`);
187
+ }
188
+ const data = await resp.json();
189
+ if (!data.webSocketDebuggerUrl) {
190
+ throw new Error("No webSocketDebuggerUrl in CDP response");
191
+ }
192
+ return data.webSocketDebuggerUrl;
193
+ }
194
+ async function getCDPTargets(baseURL) {
195
+ const url = `${baseURL}/json/list`;
196
+ const resp = await fetch(url, { signal: AbortSignal.timeout(5e3) });
197
+ if (!resp.ok) {
198
+ throw new Error(`CDP list HTTP ${resp.status}: ${url}`);
199
+ }
200
+ const targets = await resp.json();
201
+ return targets;
202
+ }
203
+ async function killChrome(child, tmpDir) {
204
+ if (child.exitCode !== null) {
205
+ if (tmpDir) cleanupTmpDir(tmpDir);
206
+ return;
207
+ }
208
+ return new Promise((resolve) => {
209
+ const timer = setTimeout(() => {
210
+ try {
211
+ child.kill("SIGKILL");
212
+ } catch {
213
+ }
214
+ if (tmpDir) cleanupTmpDir(tmpDir);
215
+ resolve();
216
+ }, 5e3);
217
+ child.once("exit", () => {
218
+ clearTimeout(timer);
219
+ if (tmpDir) cleanupTmpDir(tmpDir);
220
+ resolve();
221
+ });
222
+ try {
223
+ child.kill("SIGTERM");
224
+ } catch {
225
+ clearTimeout(timer);
226
+ if (tmpDir) cleanupTmpDir(tmpDir);
227
+ resolve();
228
+ }
229
+ });
230
+ }
231
+ function cleanupTmpDir(dir) {
232
+ try {
233
+ const { rmSync } = __require("fs");
234
+ rmSync(dir, { recursive: true, force: true });
235
+ } catch {
236
+ }
237
+ }
238
+ function sleep(ms) {
239
+ return new Promise((resolve) => setTimeout(resolve, ms));
240
+ }
241
+
242
+ export {
243
+ DEFAULT_ARGS,
244
+ ANTI_DETECT_ARGS,
245
+ findChrome,
246
+ launchChrome,
247
+ connectToCDP,
248
+ getCDPTargets,
249
+ killChrome
250
+ };
@@ -1,3 +1,6 @@
1
+ import {
2
+ launch
3
+ } from "./chunk-42RPMJ76.js";
1
4
  import {
2
5
  CDPInterceptorProxy
3
6
  } from "./chunk-ZZ2TFWIV.js";
@@ -7,7 +10,6 @@ import { randomUUID } from "crypto";
7
10
  import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
8
11
  import { join } from "path";
9
12
  import { homedir } from "os";
10
- import { chromium } from "playwright";
11
13
 
12
14
  // src/utils/cdp.ts
13
15
  async function fetchNoProxy(url) {
@@ -192,15 +194,18 @@ async function createBrowser(options) {
192
194
  const realEndpoint = await resolveCDPEndpoint(options.cdpEndpoint);
193
195
  if (options.intercept) {
194
196
  const config = typeof options.intercept === "object" ? { ...options.intercept, cdpEndpoint: realEndpoint } : { cdpEndpoint: realEndpoint };
195
- const proxy = new CDPInterceptorProxy(config);
196
- const proxyPort = await proxy.start();
197
+ _sharedCdpProxy = new CDPInterceptorProxy(config);
198
+ const proxyPort = await _sharedCdpProxy.start();
197
199
  console.error(`[CDP Interceptor] Proxy running on ws://localhost:${proxyPort}, forwarding to ${realEndpoint}`);
198
- return await chromium.connectOverCDP(`ws://localhost:${proxyPort}`);
200
+ const { browser: browser3 } = await launch({ cdpEndpoint: `ws://localhost:${proxyPort}` });
201
+ return browser3;
199
202
  }
200
- return await chromium.connectOverCDP(realEndpoint);
203
+ const { browser: browser2 } = await launch({ cdpEndpoint: realEndpoint });
204
+ return browser2;
201
205
  }
202
206
  const executablePath = options?.executablePath || process.env.XBROWSER_CHROMIUM_PATH || discoverChromiumPath();
203
- return await chromium.launch({ executablePath, headless: options?.headless ?? true });
207
+ const { browser } = await launch({ executablePath, headless: options?.headless ?? true });
208
+ return browser;
204
209
  }
205
210
  async function getBrowser(options) {
206
211
  if (_sharedBrowser) return _sharedBrowser;
@@ -351,7 +356,7 @@ async function findOrRestoreSession(name, cdpEndpoint) {
351
356
  async function createEphemeralContext(options) {
352
357
  if (options?.cdpEndpoint) {
353
358
  const endpoint = await resolveCDPEndpoint(options.cdpEndpoint);
354
- const b2 = await chromium.connectOverCDP(endpoint);
359
+ const { browser: b2 } = await launch({ cdpEndpoint: endpoint });
355
360
  const contexts = b2.contexts();
356
361
  const ctx = contexts[0] || await b2.newContext();
357
362
  const page2 = await ctx.newPage();
@@ -391,33 +396,54 @@ function getAllSessions() {
391
396
  async function installNetworkCapture(page, sessionName) {
392
397
  if (process.env.XBROWSER_DAEMON_WORKER !== "1") return;
393
398
  const { networkStore } = await import("./network-store-BN6QEZ7R.js");
394
- page.on("response", async (response) => {
399
+ const requestData = /* @__PURE__ */ new Map();
400
+ const responseMeta = /* @__PURE__ */ new Map();
401
+ const xbPage = page;
402
+ xbPage.on("request", (params) => {
395
403
  try {
396
- const request = response.request();
397
- const url = response.url();
398
- const contentType = response.headers()["content-type"] || "";
399
- const headers = {};
400
- for (const [k, v] of Object.entries(response.headers())) {
401
- headers[k] = v;
402
- }
403
- const requestHeaders = {};
404
- for (const [k, v] of Object.entries(request.headers())) {
405
- requestHeaders[k] = v;
406
- }
404
+ const p = params;
405
+ requestData.set(p.requestId, {
406
+ method: p.request.method,
407
+ headers: p.request.headers,
408
+ postData: p.request.postData ?? null,
409
+ resourceType: p.type
410
+ });
411
+ } catch {
412
+ }
413
+ });
414
+ xbPage.on("response", (params) => {
415
+ try {
416
+ const p = params;
417
+ responseMeta.set(p.requestId, {
418
+ status: p.response.status,
419
+ url: p.response.url,
420
+ headers: p.response.headers,
421
+ mimeType: p.response.mimeType,
422
+ type: p.type
423
+ });
424
+ } catch {
425
+ }
426
+ });
427
+ xbPage.on("requestfinished", async (params) => {
428
+ try {
429
+ const p = params;
430
+ const meta = responseMeta.get(p.requestId);
431
+ if (!meta) return;
432
+ const req = requestData.get(p.requestId);
433
+ const method = req?.method ?? "GET";
434
+ const contentType = meta.headers["content-type"] || meta.headers["Content-Type"] || "";
435
+ const resourceType = req?.resourceType ?? meta.type;
436
+ const requestHeaders = req?.headers ?? {};
407
437
  let requestBody = void 0;
408
- const method = request.method();
409
438
  const isPostLike = ["POST", "PATCH", "PUT"].includes(method);
410
439
  if (isPostLike && requestHeaders["content-type"]?.includes("application/json")) {
411
- try {
412
- const postData = request.postData();
413
- if (postData) {
414
- try {
415
- requestBody = JSON.parse(postData);
416
- } catch {
417
- requestBody = postData;
418
- }
440
+ const postData = req?.postData;
441
+ if (postData) {
442
+ try {
443
+ requestBody = JSON.parse(postData);
444
+ } catch {
445
+ requestBody = postData;
419
446
  }
420
- } catch {
421
447
  }
422
448
  }
423
449
  let responseBody = void 0;
@@ -425,7 +451,11 @@ async function installNetworkCapture(page, sessionName) {
425
451
  const isJsonish = contentType.includes("json") || contentType.includes("javascript") || contentType.includes("text/");
426
452
  if (isJsonish) {
427
453
  try {
428
- const text = await response.text();
454
+ const bodyResult = await xbPage._cdpSend(
455
+ "Network.getResponseBody",
456
+ { requestId: p.requestId }
457
+ );
458
+ const text = bodyResult.body ?? "";
429
459
  size = text.length;
430
460
  if (size <= 10240) {
431
461
  try {
@@ -438,8 +468,11 @@ async function installNetworkCapture(page, sessionName) {
438
468
  }
439
469
  } else {
440
470
  try {
441
- const text = await response.text();
442
- size = text.length;
471
+ const bodyResult = await xbPage._cdpSend(
472
+ "Network.getResponseBody",
473
+ { requestId: p.requestId }
474
+ );
475
+ size = bodyResult.body?.length ?? 0;
443
476
  } catch {
444
477
  size = 0;
445
478
  }
@@ -447,17 +480,19 @@ async function installNetworkCapture(page, sessionName) {
447
480
  networkStore.add(sessionName, {
448
481
  timestamp: Date.now(),
449
482
  method,
450
- url,
451
- path: new URL(url).pathname,
452
- status: response.status(),
483
+ url: meta.url,
484
+ path: new URL(meta.url).pathname,
485
+ status: meta.status,
453
486
  contentType,
454
487
  size,
455
- headers,
488
+ headers: meta.headers,
456
489
  body: responseBody,
457
490
  requestHeaders,
458
491
  requestBody,
459
- resourceType: request.resourceType()
492
+ resourceType
460
493
  });
494
+ requestData.delete(p.requestId);
495
+ responseMeta.delete(p.requestId);
461
496
  } catch {
462
497
  }
463
498
  });
@@ -570,7 +605,7 @@ async function closeSessionByName(name) {
570
605
  } catch {
571
606
  }
572
607
  try {
573
- const { SessionRecorder } = await import("./session-recorder-XET3DNML.js");
608
+ const { SessionRecorder } = await import("./session-recorder-MA75PKTQ.js");
574
609
  SessionRecorder.cleanup(session.name);
575
610
  } catch {
576
611
  }