@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
@@ -4,7 +4,7 @@ import {
4
4
  getCDPTargets,
5
5
  killChrome,
6
6
  launchChrome
7
- } from "./chunk-T4J4C2NZ.js";
7
+ } from "./chunk-JPHCY4TC.js";
8
8
  import {
9
9
  __require
10
10
  } from "./chunk-3RG5ZIWI.js";
@@ -247,36 +247,36 @@ function resolveKeyMapping(key) {
247
247
  return { key, code: key };
248
248
  }
249
249
  var KEY_MAP = {
250
- Enter: { key: "Enter", code: "Enter", text: "\r" },
251
- Tab: { key: "Tab", code: "Tab", text: " " },
252
- Escape: { key: "Escape", code: "Escape" },
253
- Backspace: { key: "Backspace", code: "Backspace" },
254
- Delete: { key: "Delete", code: "Delete" },
255
- Space: { key: " ", code: "Space", text: " " },
256
- ArrowUp: { key: "ArrowUp", code: "ArrowUp" },
257
- ArrowDown: { key: "ArrowDown", code: "ArrowDown" },
258
- ArrowLeft: { key: "ArrowLeft", code: "ArrowLeft" },
259
- ArrowRight: { key: "ArrowRight", code: "ArrowRight" },
260
- Home: { key: "Home", code: "Home" },
261
- End: { key: "End", code: "End" },
262
- PageUp: { key: "PageUp", code: "PageUp" },
263
- PageDown: { key: "PageDown", code: "PageDown" },
264
- Control: { key: "Control", code: "ControlLeft" },
265
- Shift: { key: "Shift", code: "ShiftLeft" },
266
- Alt: { key: "Alt", code: "AltLeft" },
267
- Meta: { key: "Meta", code: "MetaLeft" },
268
- F1: { key: "F1", code: "F1" },
269
- F2: { key: "F2", code: "F2" },
270
- F3: { key: "F3", code: "F3" },
271
- F4: { key: "F4", code: "F4" },
272
- F5: { key: "F5", code: "F5" },
273
- F6: { key: "F6", code: "F6" },
274
- F7: { key: "F7", code: "F7" },
275
- F8: { key: "F8", code: "F8" },
276
- F9: { key: "F9", code: "F9" },
277
- F10: { key: "F10", code: "F10" },
278
- F11: { key: "F11", code: "F11" },
279
- F12: { key: "F12", code: "F12" }
250
+ Enter: { key: "Enter", code: "Enter", text: "\r", keyCode: 13 },
251
+ Tab: { key: "Tab", code: "Tab", text: " ", keyCode: 9 },
252
+ Escape: { key: "Escape", code: "Escape", keyCode: 27 },
253
+ Backspace: { key: "Backspace", code: "Backspace", keyCode: 8 },
254
+ Delete: { key: "Delete", code: "Delete", keyCode: 46 },
255
+ Space: { key: " ", code: "Space", text: " ", keyCode: 32 },
256
+ ArrowUp: { key: "ArrowUp", code: "ArrowUp", keyCode: 38 },
257
+ ArrowDown: { key: "ArrowDown", code: "ArrowDown", keyCode: 40 },
258
+ ArrowLeft: { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37 },
259
+ ArrowRight: { key: "ArrowRight", code: "ArrowRight", keyCode: 39 },
260
+ Home: { key: "Home", code: "Home", keyCode: 36 },
261
+ End: { key: "End", code: "End", keyCode: 35 },
262
+ PageUp: { key: "PageUp", code: "PageUp", keyCode: 33 },
263
+ PageDown: { key: "PageDown", code: "PageDown", keyCode: 34 },
264
+ Control: { key: "Control", code: "ControlLeft", keyCode: 17 },
265
+ Shift: { key: "Shift", code: "ShiftLeft", keyCode: 16 },
266
+ Alt: { key: "Alt", code: "AltLeft", keyCode: 18 },
267
+ Meta: { key: "Meta", code: "MetaLeft", keyCode: 91 },
268
+ F1: { key: "F1", code: "F1", keyCode: 112 },
269
+ F2: { key: "F2", code: "F2", keyCode: 113 },
270
+ F3: { key: "F3", code: "F3", keyCode: 114 },
271
+ F4: { key: "F4", code: "F4", keyCode: 115 },
272
+ F5: { key: "F5", code: "F5", keyCode: 116 },
273
+ F6: { key: "F6", code: "F6", keyCode: 117 },
274
+ F7: { key: "F7", code: "F7", keyCode: 118 },
275
+ F8: { key: "F8", code: "F8", keyCode: 119 },
276
+ F9: { key: "F9", code: "F9", keyCode: 120 },
277
+ F10: { key: "F10", code: "F10", keyCode: 121 },
278
+ F11: { key: "F11", code: "F11", keyCode: 122 },
279
+ F12: { key: "F12", code: "F12", keyCode: 123 }
280
280
  };
281
281
  function sleep2(ms) {
282
282
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -971,7 +971,7 @@ function createXBRequest(page, data) {
971
971
  }
972
972
  };
973
973
  }
974
- function createXBRouteFetch(conn, sessionId, params) {
974
+ function createXBRouteFetch(conn, sessionId, params, emitter) {
975
975
  const request = createXBRequest(null, {
976
976
  requestId: params.requestId,
977
977
  url: params.request.url,
@@ -1009,6 +1009,16 @@ function createXBRouteFetch(conn, sessionId, params) {
1009
1009
  responseHeaders: Object.entries(headers).map(([k, v]) => ({ name: k, value: v })),
1010
1010
  body: bodyBytes.toString("base64")
1011
1011
  }, sessionId);
1012
+ if (emitter) {
1013
+ const responseData = {
1014
+ requestId: params.requestId,
1015
+ status: opts.status ?? 200,
1016
+ url: params.request.url,
1017
+ headers
1018
+ };
1019
+ const response = createXBResponse(responseData, conn, sessionId);
1020
+ emitter.emit("response", response);
1021
+ }
1012
1022
  }
1013
1023
  };
1014
1024
  }
@@ -1230,6 +1240,25 @@ Last error: ${lastError.message}` : "";
1230
1240
  }
1231
1241
  return result.result?.value;
1232
1242
  }
1243
+ /** evaluateHandle — evaluates fn and returns a handle for element bounding box */
1244
+ async evaluateHandle(fn, ...args) {
1245
+ let expression;
1246
+ if (typeof fn === "string") {
1247
+ expression = fn;
1248
+ } else {
1249
+ const argStr = args.length > 0 ? `...${JSON.stringify(args)}` : "";
1250
+ expression = `(()=>{const __fn=(${fn.toString()});const __el=__fn(${argStr});if(__el&&typeof __el.getBoundingClientRect==='function'){const r=__el.getBoundingClientRect();return JSON.parse(JSON.stringify({x:r.x,y:r.y,w:r.width,h:r.height}));}return null;})()`;
1251
+ }
1252
+ const result = await this.conn.send("Runtime.evaluate", { expression, returnByValue: true }).catch(() => ({ result: { value: null } }));
1253
+ let box = null;
1254
+ try {
1255
+ box = JSON.parse(result.result?.value);
1256
+ } catch {
1257
+ }
1258
+ return {
1259
+ asElement: () => box ? { boundingBox: async () => box } : null
1260
+ };
1261
+ }
1233
1262
  async $eval(selector, fn, ...args) {
1234
1263
  const fnBody = typeof fn === "function" ? fn.toString() : fn;
1235
1264
  return this.evaluate(
@@ -1445,6 +1474,26 @@ Last error: ${lastError.message}` : "";
1445
1474
  off(event, handler) {
1446
1475
  this._emitter.off(event, handler);
1447
1476
  }
1477
+ /**
1478
+ * Wait for a one-shot event (Playwright-compatible subset).
1479
+ * Used to listen for 'filechooser', 'dialog', 'popup', 'framenavigated', etc.
1480
+ */
1481
+ async waitForEvent(event, opts = {}) {
1482
+ const timeout = opts.timeout ?? 3e4;
1483
+ return new Promise((resolve, reject) => {
1484
+ const timer = setTimeout(() => {
1485
+ this._emitter.off(event, handler);
1486
+ reject(new Error(`waitForEvent('${event}') timeout after ${timeout}ms`));
1487
+ }, timeout);
1488
+ const handler = (...args) => {
1489
+ if (opts.predicate && !opts.predicate(...args)) return;
1490
+ clearTimeout(timer);
1491
+ this._emitter.off(event, handler);
1492
+ resolve(args.length === 1 ? args[0] : args);
1493
+ };
1494
+ this._emitter.on(event, handler);
1495
+ });
1496
+ }
1448
1497
  // ── Lifecycle ───────────────────────────────────────────────
1449
1498
  async close() {
1450
1499
  if (this._closed) return;
@@ -1608,6 +1657,40 @@ Last error: ${lastError.message}` : "";
1608
1657
  this._emit("dialog", dialog);
1609
1658
  })
1610
1659
  );
1660
+ this._subscriptions.push(
1661
+ this.conn.subscribe("Page.fileChooserOpened", this.sessionId, async (params) => {
1662
+ const p = params;
1663
+ let selector = "";
1664
+ try {
1665
+ const result = await this.conn.send("DOM.describeNode", { backendNodeId: p.backendNodeId }, this.sessionId);
1666
+ const attrs = result.node?.attributes || [];
1667
+ const idIdx = attrs.indexOf("id");
1668
+ if (idIdx >= 0) selector = "#" + attrs[idIdx + 1];
1669
+ } catch {
1670
+ }
1671
+ if (!selector) {
1672
+ try {
1673
+ const result = await this.conn.send("DOM.resolveNode", { backendNodeId: p.backendNodeId }, this.sessionId);
1674
+ const evalResult = await this.conn.send("Runtime.callFunctionOn", {
1675
+ objectId: result.objectId,
1676
+ functionDeclaration: 'function() { return this.id || this.name || "" }',
1677
+ returnByValue: true
1678
+ }, this.sessionId);
1679
+ if (evalResult.result?.value) selector = "#" + evalResult.result.value;
1680
+ } catch {
1681
+ }
1682
+ }
1683
+ const fileChooser = {
1684
+ selector,
1685
+ isMultiple: p.mode === "selectMultiple",
1686
+ setFiles: async (files) => {
1687
+ const fileArray = Array.isArray(files) ? files : [files];
1688
+ await this.setInputFiles(selector || 'input[type="file"]', fileArray);
1689
+ }
1690
+ };
1691
+ this._emit("filechooser", fileChooser);
1692
+ })
1693
+ );
1611
1694
  }
1612
1695
  setupNetworkEvents() {
1613
1696
  this._subscriptions.push(
@@ -1621,7 +1704,17 @@ Last error: ${lastError.message}` : "";
1621
1704
  postData: p.request.postData ?? null,
1622
1705
  resourceType: p.type
1623
1706
  });
1624
- this._emit("request", p);
1707
+ this._emit("request", createXBRequest(
1708
+ null,
1709
+ {
1710
+ requestId: p.requestId,
1711
+ url: p.request.url,
1712
+ method: p.request.method,
1713
+ headers: p.request.headers,
1714
+ postData: p.request.postData ?? null,
1715
+ resourceType: p.type
1716
+ }
1717
+ ));
1625
1718
  this.checkNetworkIdle();
1626
1719
  })
1627
1720
  );
@@ -1633,7 +1726,11 @@ Last error: ${lastError.message}` : "";
1633
1726
  url: p.response.url,
1634
1727
  headers: p.response.headers
1635
1728
  });
1636
- this._emit("response", p);
1729
+ this._emit("response", createXBResponse(
1730
+ { requestId: p.requestId, status: p.response.status, url: p.response.url, headers: p.response.headers },
1731
+ this.conn,
1732
+ this.sessionId
1733
+ ));
1637
1734
  })
1638
1735
  );
1639
1736
  this._subscriptions.push(
@@ -1719,14 +1816,21 @@ Last error: ${lastError.message}` : "";
1719
1816
  reject(new Error(`waitForResponse timed out after ${timeout}ms`));
1720
1817
  }, timeout);
1721
1818
  const handler = (params) => {
1722
- const p = params;
1723
- const data = {
1724
- requestId: p.requestId,
1725
- status: p.response.status,
1726
- url: p.response.url,
1727
- headers: p.response.headers
1728
- };
1729
- const response = createXBResponse(data, this.conn, this.sessionId);
1819
+ let response;
1820
+ const respObj = params;
1821
+ if (respObj.response) {
1822
+ const data = {
1823
+ requestId: respObj.requestId || "",
1824
+ status: respObj.response.status || 0,
1825
+ url: respObj.response.url || "",
1826
+ headers: respObj.response.headers || {}
1827
+ };
1828
+ response = createXBResponse(data, this.conn, this.sessionId);
1829
+ } else if (typeof params.status === "function") {
1830
+ response = params;
1831
+ } else {
1832
+ return;
1833
+ }
1730
1834
  if (predicate(response)) {
1731
1835
  clearTimeout(timer);
1732
1836
  this._emitter.removeListener("response", handler);
@@ -1750,16 +1854,22 @@ Last error: ${lastError.message}` : "";
1750
1854
  reject(new Error(`waitForRequest timed out after ${timeout}ms`));
1751
1855
  }, timeout);
1752
1856
  const handler = (params) => {
1753
- const p = params;
1754
- const data = {
1755
- requestId: p.requestId,
1756
- url: p.request.url,
1757
- method: p.request.method,
1758
- headers: p.request.headers,
1759
- postData: p.request.postData ?? null,
1760
- resourceType: p.type
1761
- };
1762
- const request = createXBRequest(this, data);
1857
+ let request;
1858
+ const reqObj = params;
1859
+ if (reqObj.request) {
1860
+ request = createXBRequest(this, {
1861
+ requestId: reqObj.requestId || "",
1862
+ url: reqObj.request.url || "",
1863
+ method: reqObj.request.method || "",
1864
+ headers: reqObj.request.headers || {},
1865
+ postData: reqObj.request.postData ?? null,
1866
+ resourceType: reqObj.type || ""
1867
+ });
1868
+ } else if (typeof params.url === "function") {
1869
+ request = params;
1870
+ } else {
1871
+ return;
1872
+ }
1763
1873
  if (predicate(request)) {
1764
1874
  clearTimeout(timer);
1765
1875
  this._emitter.removeListener("request", handler);
@@ -1820,7 +1930,18 @@ Last error: ${lastError.message}` : "";
1820
1930
  const requestUrl = params.request.url;
1821
1931
  for (const { regex, handler } of this._routeHandlers) {
1822
1932
  if (regex.test(requestUrl)) {
1823
- const route = createXBRouteFetch(this.conn, this.sessionId, params);
1933
+ this._emit("request", createXBRequest(
1934
+ null,
1935
+ {
1936
+ requestId: params.requestId,
1937
+ url: params.request.url,
1938
+ method: params.request.method,
1939
+ headers: params.request.headers,
1940
+ postData: params.request.postData ?? null,
1941
+ resourceType: params.resourceType
1942
+ }
1943
+ ));
1944
+ const route = createXBRouteFetch(this.conn, this.sessionId, params, this._emitter);
1824
1945
  try {
1825
1946
  await handler(route);
1826
1947
  } catch {
@@ -1987,6 +2108,7 @@ var XBContextImpl = class {
1987
2108
  const page = new XBPageImpl(this.conn, sessionId, targetId, this, this._browser);
1988
2109
  await page._init();
1989
2110
  this._pages.push(page);
2111
+ this.forwardPageEvents(page);
1990
2112
  if (this.options.viewport) {
1991
2113
  await page.setViewportSize(this.options.viewport).catch(() => {
1992
2114
  });
@@ -2028,7 +2150,10 @@ var XBContextImpl = class {
2028
2150
  }
2029
2151
  this._browser._removeContext(this.contextId);
2030
2152
  }
2031
- async newCDPSession(_page) {
2153
+ async newCDPSession(page) {
2154
+ if (page instanceof XBPageImpl) {
2155
+ return new XBCDPSessionImpl(this.conn, page.sessionId);
2156
+ }
2032
2157
  return new XBCDPSessionImpl(this.conn);
2033
2158
  }
2034
2159
  async addInitScript(script) {
@@ -2065,7 +2190,31 @@ var XBContextImpl = class {
2065
2190
  off(event, handler) {
2066
2191
  this._emitter.off(event, handler);
2067
2192
  }
2193
+ /**
2194
+ * Register a page that was attached to an existing target (discovered via
2195
+ * Target.getTargets). Used by XBBrowserImpl.discoverContexts() to wire up
2196
+ * pages from the user's existing browser session into the context wrapper
2197
+ * so they appear in `context.pages()` and can be reused by plugins.
2198
+ */
2199
+ _addDiscoveredPage(page) {
2200
+ const exists = this._pages.some((p) => p._targetId === page._targetId);
2201
+ if (exists) return;
2202
+ this._pages.push(page);
2203
+ this.forwardPageEvents(page);
2204
+ }
2068
2205
  // ── Private ─────────────────────────────────────────────────
2206
+ /** Forward page-level events (request, response, etc.) to context listeners */
2207
+ forwardPageEvents(page) {
2208
+ const forward = (event) => {
2209
+ page.on(event, (...args) => {
2210
+ this._emitter.emit(event, ...args);
2211
+ });
2212
+ };
2213
+ forward("request");
2214
+ forward("response");
2215
+ forward("requestfailed");
2216
+ forward("requestfinished");
2217
+ }
2069
2218
  setupAutoAttach() {
2070
2219
  this.targetAttachedHandler = (paramsRaw) => {
2071
2220
  const params = paramsRaw;
@@ -2090,6 +2239,7 @@ var XBContextImpl = class {
2090
2239
  });
2091
2240
  }
2092
2241
  this._pages.push(page);
2242
+ this.forwardPageEvents(page);
2093
2243
  this._emitter.emit("page", page);
2094
2244
  });
2095
2245
  };
@@ -2106,10 +2256,17 @@ var XBBrowserImpl = class {
2106
2256
  childProcess = null;
2107
2257
  tmpDir;
2108
2258
  _exitHandler = null;
2109
- constructor(conn, childProcess, tmpDir) {
2259
+ /**
2260
+ * Original CDP endpoint (HTTP or ws URL) used to construct this browser.
2261
+ * Used by discoverContexts() as a fallback to HTTP /json/list when
2262
+ * Target.getTargets doesn't return page-type targets (e.g. cdp-tunnel proxy).
2263
+ */
2264
+ cdpEndpoint;
2265
+ constructor(conn, childProcess, tmpDir, cdpEndpoint) {
2110
2266
  this.conn = conn;
2111
2267
  this.childProcess = childProcess ?? null;
2112
2268
  this.tmpDir = tmpDir;
2269
+ this.cdpEndpoint = cdpEndpoint;
2113
2270
  conn.on("disconnect", () => {
2114
2271
  this._disconnected = true;
2115
2272
  this._emitter.emit("disconnected");
@@ -2153,7 +2310,7 @@ var XBBrowserImpl = class {
2153
2310
  this._exitHandler = null;
2154
2311
  }
2155
2312
  if (this.childProcess) {
2156
- const { killChrome: killChrome2 } = await import("./launcher-KA7J32K5.js");
2313
+ const { killChrome: killChrome2 } = await import("./launcher-YARP45UY.js");
2157
2314
  await killChrome2(this.childProcess, this.tmpDir);
2158
2315
  }
2159
2316
  await this.conn.close();
@@ -2183,6 +2340,10 @@ var XBBrowserImpl = class {
2183
2340
  contextId,
2184
2341
  context
2185
2342
  });
2343
+ if (this.childProcess) {
2344
+ this._enableAutoAttach().catch(() => {
2345
+ });
2346
+ }
2186
2347
  return context;
2187
2348
  }
2188
2349
  contexts() {
@@ -2211,6 +2372,23 @@ var XBBrowserImpl = class {
2211
2372
  async _detachFromTarget(sessionId) {
2212
2373
  await this.conn.send("Target.detachFromTarget", { sessionId });
2213
2374
  }
2375
+ /**
2376
+ * Derive the HTTP /json base URL from the original cdpEndpoint for use
2377
+ * as a fallback when Target.getTargets doesn't return page targets.
2378
+ * Supports both http:// and ws:// input formats.
2379
+ */
2380
+ _httpFallbackURL() {
2381
+ if (!this.cdpEndpoint) return void 0;
2382
+ if (this.cdpEndpoint.startsWith("http://") || this.cdpEndpoint.startsWith("https://")) {
2383
+ return this.cdpEndpoint;
2384
+ }
2385
+ if (this.cdpEndpoint.startsWith("ws://") || this.cdpEndpoint.startsWith("wss://")) {
2386
+ const url = this.cdpEndpoint.replace(/^ws/, "http");
2387
+ const slashIdx = url.indexOf("/", url.indexOf("//") + 2);
2388
+ return slashIdx >= 0 ? url.substring(0, slashIdx) : url;
2389
+ }
2390
+ return void 0;
2391
+ }
2214
2392
  /** Create a new page target within a browser context */
2215
2393
  async _createTarget(contextId, url = "about:blank") {
2216
2394
  const params = { url };
@@ -2227,10 +2405,97 @@ var XBBrowserImpl = class {
2227
2405
  async _enableAutoAttach() {
2228
2406
  await this.conn.send("Target.setAutoAttach", {
2229
2407
  autoAttach: true,
2230
- waitForDebuggerOnStart: true,
2408
+ waitForDebuggerOnStart: false,
2231
2409
  flatten: true
2232
2410
  });
2233
2411
  }
2412
+ /**
2413
+ * Discover existing browser contexts and pages via Target.getTargets.
2414
+ *
2415
+ * For CDP tunnel connections (cdp-tunnel, attach scenarios), the
2416
+ * Target.attachedToTarget auto-attach flow is unreliable. Without this
2417
+ * call, `b.contexts()` would return [] and callers would fall back to
2418
+ * `b.newContext()` — which creates an isolated context with NO cookies
2419
+ * shared with the user's existing browser session (causing login failures).
2420
+ *
2421
+ * This method:
2422
+ * 1. Queries Target.getTargets to enumerate all page targets
2423
+ * 2. Groups them by browserContextId
2424
+ * 3. Attaches to each existing page via Target.attachToTarget
2425
+ * 4. Wraps the discovered pages in a XBContextImpl and registers it in
2426
+ * this._contexts so `contexts()` returns the user's actual contexts
2427
+ * 5. Enables Target.setAutoAttach for future pages
2428
+ *
2429
+ * No-op for self-launched browsers (they already populated contexts via
2430
+ * newContext() + childProcess-gated auto-attach).
2431
+ */
2432
+ async discoverContexts() {
2433
+ if (this._disconnected) return;
2434
+ let targetInfos = [];
2435
+ try {
2436
+ const result = await this.conn.send(
2437
+ "Target.getTargets"
2438
+ );
2439
+ targetInfos = result.targetInfos ?? [];
2440
+ } catch {
2441
+ return;
2442
+ }
2443
+ const pageTargets = targetInfos.filter((t) => t.type === "page");
2444
+ const httpFallbackUrl = this._httpFallbackURL();
2445
+ if (pageTargets.length === 0 && httpFallbackUrl) {
2446
+ console.log(`[discoverContexts] Target.getTargets returned ${targetInfos.length} targets (0 page type). Falling back to HTTP /json/list at ${httpFallbackUrl}`);
2447
+ try {
2448
+ const { getCDPTargets: getCDPTargets2 } = await import("./launcher-YARP45UY.js");
2449
+ const httpPages = await getCDPTargets2(httpFallbackUrl);
2450
+ console.log(`[discoverContexts] HTTP /json/list returned ${httpPages.length} pages`);
2451
+ for (const p of httpPages) {
2452
+ if (p.type !== "page") continue;
2453
+ if (!p.url || p.url.startsWith("chrome://") || p.url.startsWith("devtools://")) continue;
2454
+ targetInfos.push({
2455
+ targetId: p.id,
2456
+ type: "page",
2457
+ url: p.url,
2458
+ title: p.title
2459
+ });
2460
+ }
2461
+ console.log(`[discoverContexts] After HTTP fallback: ${targetInfos.length} total targets, ${targetInfos.filter((t) => t.type === "page").length} pages`);
2462
+ } catch (err) {
2463
+ console.log(`[discoverContexts] HTTP fallback failed: ${err.message}`);
2464
+ }
2465
+ }
2466
+ const pagesByContext = /* @__PURE__ */ new Map();
2467
+ for (const t of targetInfos) {
2468
+ if (t.type !== "page") continue;
2469
+ if (!t.url || t.url.startsWith("chrome://") || t.url.startsWith("devtools://")) {
2470
+ continue;
2471
+ }
2472
+ const ctxId = t.browserContextId || "default";
2473
+ if (!pagesByContext.has(ctxId)) pagesByContext.set(ctxId, []);
2474
+ pagesByContext.get(ctxId).push(t);
2475
+ }
2476
+ for (const [ctxId, pages] of pagesByContext) {
2477
+ if (this._contexts.has(ctxId)) continue;
2478
+ const context = new XBContextImpl(this.conn, ctxId, this, {});
2479
+ for (const p of pages) {
2480
+ try {
2481
+ const sessionId = await this._attachToTarget(p.targetId);
2482
+ const page = new XBPageImpl(this.conn, sessionId, p.targetId, context, this);
2483
+ await page._init();
2484
+ context._addDiscoveredPage(page);
2485
+ } catch {
2486
+ }
2487
+ }
2488
+ this._contexts.set(ctxId, { contextId: ctxId, context });
2489
+ }
2490
+ try {
2491
+ await this.conn.send("Target.setAutoAttach", {
2492
+ autoAttach: true,
2493
+ waitForDebuggerOnStart: false,
2494
+ flatten: true
2495
+ });
2496
+ } catch {
2497
+ }
2498
+ }
2234
2499
  };
2235
2500
 
2236
2501
  // src/cdp-driver/connection.ts
@@ -2510,7 +2775,8 @@ async function launch(options = {}) {
2510
2775
  }
2511
2776
  const conn = new CDPConnection(wsEndpoint);
2512
2777
  await conn.ready();
2513
- const browser = new XBBrowserImpl(conn, childProcess, tmpDir);
2778
+ const httpEndpoint = options.cdpEndpoint && !options.cdpEndpoint.startsWith("ws") ? options.cdpEndpoint : void 0;
2779
+ const browser = new XBBrowserImpl(conn, childProcess, tmpDir, httpEndpoint);
2514
2780
  return { browser, wsEndpoint };
2515
2781
  }
2516
2782
  export {
@@ -14,15 +14,15 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-42RPMJ76.js";
17
+ } from "./chunk-PPG4D2EW.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
21
21
  getCDPTargets,
22
22
  killChrome,
23
23
  launchChrome
24
- } from "./chunk-T4J4C2NZ.js";
25
- import "./chunk-3RG5ZIWI.js";
24
+ } from "./chunk-BBMRDUYQ.js";
25
+ import "./chunk-KFQGP6VL.js";
26
26
  export {
27
27
  CDPConnection,
28
28
  CDPProtocolError,