@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,10 +1,11 @@
1
1
  import {
2
2
  connectToCDP,
3
+ errMsg,
3
4
  findChrome,
4
5
  getCDPTargets,
5
6
  killChrome,
6
7
  launchChrome
7
- } from "./chunk-T4J4C2NZ.js";
8
+ } from "./chunk-AMI64BSD.js";
8
9
  import {
9
10
  __require
10
11
  } from "./chunk-3RG5ZIWI.js";
@@ -247,36 +248,36 @@ function resolveKeyMapping(key) {
247
248
  return { key, code: key };
248
249
  }
249
250
  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" }
251
+ Enter: { key: "Enter", code: "Enter", text: "\r", keyCode: 13 },
252
+ Tab: { key: "Tab", code: "Tab", text: " ", keyCode: 9 },
253
+ Escape: { key: "Escape", code: "Escape", keyCode: 27 },
254
+ Backspace: { key: "Backspace", code: "Backspace", keyCode: 8 },
255
+ Delete: { key: "Delete", code: "Delete", keyCode: 46 },
256
+ Space: { key: " ", code: "Space", text: " ", keyCode: 32 },
257
+ ArrowUp: { key: "ArrowUp", code: "ArrowUp", keyCode: 38 },
258
+ ArrowDown: { key: "ArrowDown", code: "ArrowDown", keyCode: 40 },
259
+ ArrowLeft: { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37 },
260
+ ArrowRight: { key: "ArrowRight", code: "ArrowRight", keyCode: 39 },
261
+ Home: { key: "Home", code: "Home", keyCode: 36 },
262
+ End: { key: "End", code: "End", keyCode: 35 },
263
+ PageUp: { key: "PageUp", code: "PageUp", keyCode: 33 },
264
+ PageDown: { key: "PageDown", code: "PageDown", keyCode: 34 },
265
+ Control: { key: "Control", code: "ControlLeft", keyCode: 17 },
266
+ Shift: { key: "Shift", code: "ShiftLeft", keyCode: 16 },
267
+ Alt: { key: "Alt", code: "AltLeft", keyCode: 18 },
268
+ Meta: { key: "Meta", code: "MetaLeft", keyCode: 91 },
269
+ F1: { key: "F1", code: "F1", keyCode: 112 },
270
+ F2: { key: "F2", code: "F2", keyCode: 113 },
271
+ F3: { key: "F3", code: "F3", keyCode: 114 },
272
+ F4: { key: "F4", code: "F4", keyCode: 115 },
273
+ F5: { key: "F5", code: "F5", keyCode: 116 },
274
+ F6: { key: "F6", code: "F6", keyCode: 117 },
275
+ F7: { key: "F7", code: "F7", keyCode: 118 },
276
+ F8: { key: "F8", code: "F8", keyCode: 119 },
277
+ F9: { key: "F9", code: "F9", keyCode: 120 },
278
+ F10: { key: "F10", code: "F10", keyCode: 121 },
279
+ F11: { key: "F11", code: "F11", keyCode: 122 },
280
+ F12: { key: "F12", code: "F12", keyCode: 123 }
280
281
  };
281
282
  function sleep2(ms) {
282
283
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -971,7 +972,7 @@ function createXBRequest(page, data) {
971
972
  }
972
973
  };
973
974
  }
974
- function createXBRouteFetch(conn, sessionId, params) {
975
+ function createXBRouteFetch(conn, sessionId, params, emitter) {
975
976
  const request = createXBRequest(null, {
976
977
  requestId: params.requestId,
977
978
  url: params.request.url,
@@ -1009,6 +1010,16 @@ function createXBRouteFetch(conn, sessionId, params) {
1009
1010
  responseHeaders: Object.entries(headers).map(([k, v]) => ({ name: k, value: v })),
1010
1011
  body: bodyBytes.toString("base64")
1011
1012
  }, sessionId);
1013
+ if (emitter) {
1014
+ const responseData = {
1015
+ requestId: params.requestId,
1016
+ status: opts.status ?? 200,
1017
+ url: params.request.url,
1018
+ headers
1019
+ };
1020
+ const response = createXBResponse(responseData, conn, sessionId);
1021
+ emitter.emit("response", response);
1022
+ }
1012
1023
  }
1013
1024
  };
1014
1025
  }
@@ -1190,7 +1201,7 @@ var XBPageImpl = class _XBPageImpl {
1190
1201
  );
1191
1202
  if (result) return result;
1192
1203
  } catch (err) {
1193
- lastError = err;
1204
+ lastError = err instanceof Error ? err : new Error(errMsg(err));
1194
1205
  }
1195
1206
  const pollMs = typeof polling === "number" ? polling : 16;
1196
1207
  await this.waitForTimeout(pollMs);
@@ -1230,6 +1241,25 @@ Last error: ${lastError.message}` : "";
1230
1241
  }
1231
1242
  return result.result?.value;
1232
1243
  }
1244
+ /** evaluateHandle — evaluates fn and returns a handle for element bounding box */
1245
+ async evaluateHandle(fn, ...args) {
1246
+ let expression;
1247
+ if (typeof fn === "string") {
1248
+ expression = fn;
1249
+ } else {
1250
+ const argStr = args.length > 0 ? `...${JSON.stringify(args)}` : "";
1251
+ 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;})()`;
1252
+ }
1253
+ const result = await this.conn.send("Runtime.evaluate", { expression, returnByValue: true }).catch(() => ({ result: { value: null } }));
1254
+ let box = null;
1255
+ try {
1256
+ box = JSON.parse(result.result?.value);
1257
+ } catch {
1258
+ }
1259
+ return {
1260
+ asElement: () => box ? { boundingBox: async () => box } : null
1261
+ };
1262
+ }
1233
1263
  async $eval(selector, fn, ...args) {
1234
1264
  const fnBody = typeof fn === "function" ? fn.toString() : fn;
1235
1265
  return this.evaluate(
@@ -1445,6 +1475,26 @@ Last error: ${lastError.message}` : "";
1445
1475
  off(event, handler) {
1446
1476
  this._emitter.off(event, handler);
1447
1477
  }
1478
+ /**
1479
+ * Wait for a one-shot event (Playwright-compatible subset).
1480
+ * Used to listen for 'filechooser', 'dialog', 'popup', 'framenavigated', etc.
1481
+ */
1482
+ async waitForEvent(event, opts = {}) {
1483
+ const timeout = opts.timeout ?? 3e4;
1484
+ return new Promise((resolve, reject) => {
1485
+ const timer = setTimeout(() => {
1486
+ this._emitter.off(event, handler);
1487
+ reject(new Error(`waitForEvent('${event}') timeout after ${timeout}ms`));
1488
+ }, timeout);
1489
+ const handler = (...args) => {
1490
+ if (opts.predicate && !opts.predicate(...args)) return;
1491
+ clearTimeout(timer);
1492
+ this._emitter.off(event, handler);
1493
+ resolve(args.length === 1 ? args[0] : args);
1494
+ };
1495
+ this._emitter.on(event, handler);
1496
+ });
1497
+ }
1448
1498
  // ── Lifecycle ───────────────────────────────────────────────
1449
1499
  async close() {
1450
1500
  if (this._closed) return;
@@ -1561,6 +1611,19 @@ Last error: ${lastError.message}` : "";
1561
1611
  async _cdpSend(method, params) {
1562
1612
  return this.conn.send(method, params, this.sessionId);
1563
1613
  }
1614
+ /**
1615
+ * 开启/关闭原生文件选择框拦截(CDP stateful 开关)。
1616
+ *
1617
+ * - enabled=true(执行期默认):点击上传按钮时不弹系统文件框,改发 Page.fileChooserOpened 事件,
1618
+ * page 的 'filechooser' 监听器拿到 chooser 后用 setFiles/setInputFiles 注入文件。
1619
+ * - enabled=false(录制期默认):真实文件选择框正常弹出,用户手动选文件;
1620
+ * 前端 input[type=file] 的 change 事件由 action signal 脚本捕获记录。
1621
+ *
1622
+ * 可多次调用切换状态(CDP 协议是 stateful 的)。
1623
+ */
1624
+ async setFileDialogInterception(enabled) {
1625
+ await this.conn.send("Page.setInterceptFileChooserDialog", { enabled }, this.sessionId).catch((e) => console.error("[XBPage] setInterceptFileChooserDialog failed:", errMsg(e)));
1626
+ }
1564
1627
  /** Subscribe to a CDP event on this page's session. Returns unsubscribe function. */
1565
1628
  _subscribe(event, handler) {
1566
1629
  return this.conn.subscribe(event, this.sessionId, handler);
@@ -1608,6 +1671,40 @@ Last error: ${lastError.message}` : "";
1608
1671
  this._emit("dialog", dialog);
1609
1672
  })
1610
1673
  );
1674
+ this._subscriptions.push(
1675
+ this.conn.subscribe("Page.fileChooserOpened", this.sessionId, async (params) => {
1676
+ const p = params;
1677
+ let selector = "";
1678
+ try {
1679
+ const result = await this.conn.send("DOM.describeNode", { backendNodeId: p.backendNodeId }, this.sessionId);
1680
+ const attrs = result.node?.attributes || [];
1681
+ const idIdx = attrs.indexOf("id");
1682
+ if (idIdx >= 0) selector = "#" + attrs[idIdx + 1];
1683
+ } catch {
1684
+ }
1685
+ if (!selector) {
1686
+ try {
1687
+ const result = await this.conn.send("DOM.resolveNode", { backendNodeId: p.backendNodeId }, this.sessionId);
1688
+ const evalResult = await this.conn.send("Runtime.callFunctionOn", {
1689
+ objectId: result.objectId,
1690
+ functionDeclaration: 'function() { return this.id || this.name || "" }',
1691
+ returnByValue: true
1692
+ }, this.sessionId);
1693
+ if (evalResult.result?.value) selector = "#" + evalResult.result.value;
1694
+ } catch {
1695
+ }
1696
+ }
1697
+ const fileChooser = {
1698
+ selector,
1699
+ isMultiple: p.mode === "selectMultiple",
1700
+ setFiles: async (files) => {
1701
+ const fileArray = Array.isArray(files) ? files : [files];
1702
+ await this.setInputFiles(selector || 'input[type="file"]', fileArray);
1703
+ }
1704
+ };
1705
+ this._emit("filechooser", fileChooser);
1706
+ })
1707
+ );
1611
1708
  }
1612
1709
  setupNetworkEvents() {
1613
1710
  this._subscriptions.push(
@@ -1621,7 +1718,17 @@ Last error: ${lastError.message}` : "";
1621
1718
  postData: p.request.postData ?? null,
1622
1719
  resourceType: p.type
1623
1720
  });
1624
- this._emit("request", p);
1721
+ this._emit("request", createXBRequest(
1722
+ null,
1723
+ {
1724
+ requestId: p.requestId,
1725
+ url: p.request.url,
1726
+ method: p.request.method,
1727
+ headers: p.request.headers,
1728
+ postData: p.request.postData ?? null,
1729
+ resourceType: p.type
1730
+ }
1731
+ ));
1625
1732
  this.checkNetworkIdle();
1626
1733
  })
1627
1734
  );
@@ -1633,7 +1740,11 @@ Last error: ${lastError.message}` : "";
1633
1740
  url: p.response.url,
1634
1741
  headers: p.response.headers
1635
1742
  });
1636
- this._emit("response", p);
1743
+ this._emit("response", createXBResponse(
1744
+ { requestId: p.requestId, status: p.response.status, url: p.response.url, headers: p.response.headers },
1745
+ this.conn,
1746
+ this.sessionId
1747
+ ));
1637
1748
  })
1638
1749
  );
1639
1750
  this._subscriptions.push(
@@ -1719,14 +1830,21 @@ Last error: ${lastError.message}` : "";
1719
1830
  reject(new Error(`waitForResponse timed out after ${timeout}ms`));
1720
1831
  }, timeout);
1721
1832
  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);
1833
+ let response;
1834
+ const respObj = params;
1835
+ if (respObj.response) {
1836
+ const data = {
1837
+ requestId: respObj.requestId || "",
1838
+ status: respObj.response.status || 0,
1839
+ url: respObj.response.url || "",
1840
+ headers: respObj.response.headers || {}
1841
+ };
1842
+ response = createXBResponse(data, this.conn, this.sessionId);
1843
+ } else if (typeof params.status === "function") {
1844
+ response = params;
1845
+ } else {
1846
+ return;
1847
+ }
1730
1848
  if (predicate(response)) {
1731
1849
  clearTimeout(timer);
1732
1850
  this._emitter.removeListener("response", handler);
@@ -1750,16 +1868,22 @@ Last error: ${lastError.message}` : "";
1750
1868
  reject(new Error(`waitForRequest timed out after ${timeout}ms`));
1751
1869
  }, timeout);
1752
1870
  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);
1871
+ let request;
1872
+ const reqObj = params;
1873
+ if (reqObj.request) {
1874
+ request = createXBRequest(this, {
1875
+ requestId: reqObj.requestId || "",
1876
+ url: reqObj.request.url || "",
1877
+ method: reqObj.request.method || "",
1878
+ headers: reqObj.request.headers || {},
1879
+ postData: reqObj.request.postData ?? null,
1880
+ resourceType: reqObj.type || ""
1881
+ });
1882
+ } else if (typeof params.url === "function") {
1883
+ request = params;
1884
+ } else {
1885
+ return;
1886
+ }
1763
1887
  if (predicate(request)) {
1764
1888
  clearTimeout(timer);
1765
1889
  this._emitter.removeListener("request", handler);
@@ -1820,7 +1944,18 @@ Last error: ${lastError.message}` : "";
1820
1944
  const requestUrl = params.request.url;
1821
1945
  for (const { regex, handler } of this._routeHandlers) {
1822
1946
  if (regex.test(requestUrl)) {
1823
- const route = createXBRouteFetch(this.conn, this.sessionId, params);
1947
+ this._emit("request", createXBRequest(
1948
+ null,
1949
+ {
1950
+ requestId: params.requestId,
1951
+ url: params.request.url,
1952
+ method: params.request.method,
1953
+ headers: params.request.headers,
1954
+ postData: params.request.postData ?? null,
1955
+ resourceType: params.resourceType
1956
+ }
1957
+ ));
1958
+ const route = createXBRouteFetch(this.conn, this.sessionId, params, this._emitter);
1824
1959
  try {
1825
1960
  await handler(route);
1826
1961
  } catch {
@@ -1987,6 +2122,7 @@ var XBContextImpl = class {
1987
2122
  const page = new XBPageImpl(this.conn, sessionId, targetId, this, this._browser);
1988
2123
  await page._init();
1989
2124
  this._pages.push(page);
2125
+ this.forwardPageEvents(page);
1990
2126
  if (this.options.viewport) {
1991
2127
  await page.setViewportSize(this.options.viewport).catch(() => {
1992
2128
  });
@@ -2028,7 +2164,10 @@ var XBContextImpl = class {
2028
2164
  }
2029
2165
  this._browser._removeContext(this.contextId);
2030
2166
  }
2031
- async newCDPSession(_page) {
2167
+ async newCDPSession(page) {
2168
+ if (page instanceof XBPageImpl) {
2169
+ return new XBCDPSessionImpl(this.conn, page.sessionId);
2170
+ }
2032
2171
  return new XBCDPSessionImpl(this.conn);
2033
2172
  }
2034
2173
  async addInitScript(script) {
@@ -2065,7 +2204,31 @@ var XBContextImpl = class {
2065
2204
  off(event, handler) {
2066
2205
  this._emitter.off(event, handler);
2067
2206
  }
2207
+ /**
2208
+ * Register a page that was attached to an existing target (discovered via
2209
+ * Target.getTargets). Used by XBBrowserImpl.discoverContexts() to wire up
2210
+ * pages from the user's existing browser session into the context wrapper
2211
+ * so they appear in `context.pages()` and can be reused by plugins.
2212
+ */
2213
+ _addDiscoveredPage(page) {
2214
+ const exists = this._pages.some((p) => p._targetId === page._targetId);
2215
+ if (exists) return;
2216
+ this._pages.push(page);
2217
+ this.forwardPageEvents(page);
2218
+ }
2068
2219
  // ── Private ─────────────────────────────────────────────────
2220
+ /** Forward page-level events (request, response, etc.) to context listeners */
2221
+ forwardPageEvents(page) {
2222
+ const forward = (event) => {
2223
+ page.on(event, (...args) => {
2224
+ this._emitter.emit(event, ...args);
2225
+ });
2226
+ };
2227
+ forward("request");
2228
+ forward("response");
2229
+ forward("requestfailed");
2230
+ forward("requestfinished");
2231
+ }
2069
2232
  setupAutoAttach() {
2070
2233
  this.targetAttachedHandler = (paramsRaw) => {
2071
2234
  const params = paramsRaw;
@@ -2090,6 +2253,7 @@ var XBContextImpl = class {
2090
2253
  });
2091
2254
  }
2092
2255
  this._pages.push(page);
2256
+ this.forwardPageEvents(page);
2093
2257
  this._emitter.emit("page", page);
2094
2258
  });
2095
2259
  };
@@ -2106,10 +2270,17 @@ var XBBrowserImpl = class {
2106
2270
  childProcess = null;
2107
2271
  tmpDir;
2108
2272
  _exitHandler = null;
2109
- constructor(conn, childProcess, tmpDir) {
2273
+ /**
2274
+ * Original CDP endpoint (HTTP or ws URL) used to construct this browser.
2275
+ * Used by discoverContexts() as a fallback to HTTP /json/list when
2276
+ * Target.getTargets doesn't return page-type targets (e.g. cdp-tunnel proxy).
2277
+ */
2278
+ cdpEndpoint;
2279
+ constructor(conn, childProcess, tmpDir, cdpEndpoint) {
2110
2280
  this.conn = conn;
2111
2281
  this.childProcess = childProcess ?? null;
2112
2282
  this.tmpDir = tmpDir;
2283
+ this.cdpEndpoint = cdpEndpoint;
2113
2284
  conn.on("disconnect", () => {
2114
2285
  this._disconnected = true;
2115
2286
  this._emitter.emit("disconnected");
@@ -2153,7 +2324,7 @@ var XBBrowserImpl = class {
2153
2324
  this._exitHandler = null;
2154
2325
  }
2155
2326
  if (this.childProcess) {
2156
- const { killChrome: killChrome2 } = await import("./launcher-KA7J32K5.js");
2327
+ const { killChrome: killChrome2 } = await import("./launcher-OZXJQPNG.js");
2157
2328
  await killChrome2(this.childProcess, this.tmpDir);
2158
2329
  }
2159
2330
  await this.conn.close();
@@ -2183,6 +2354,10 @@ var XBBrowserImpl = class {
2183
2354
  contextId,
2184
2355
  context
2185
2356
  });
2357
+ if (this.childProcess) {
2358
+ this._enableAutoAttach().catch(() => {
2359
+ });
2360
+ }
2186
2361
  return context;
2187
2362
  }
2188
2363
  contexts() {
@@ -2211,6 +2386,23 @@ var XBBrowserImpl = class {
2211
2386
  async _detachFromTarget(sessionId) {
2212
2387
  await this.conn.send("Target.detachFromTarget", { sessionId });
2213
2388
  }
2389
+ /**
2390
+ * Derive the HTTP /json base URL from the original cdpEndpoint for use
2391
+ * as a fallback when Target.getTargets doesn't return page targets.
2392
+ * Supports both http:// and ws:// input formats.
2393
+ */
2394
+ _httpFallbackURL() {
2395
+ if (!this.cdpEndpoint) return void 0;
2396
+ if (this.cdpEndpoint.startsWith("http://") || this.cdpEndpoint.startsWith("https://")) {
2397
+ return this.cdpEndpoint;
2398
+ }
2399
+ if (this.cdpEndpoint.startsWith("ws://") || this.cdpEndpoint.startsWith("wss://")) {
2400
+ const url = this.cdpEndpoint.replace(/^ws/, "http");
2401
+ const slashIdx = url.indexOf("/", url.indexOf("//") + 2);
2402
+ return slashIdx >= 0 ? url.substring(0, slashIdx) : url;
2403
+ }
2404
+ return void 0;
2405
+ }
2214
2406
  /** Create a new page target within a browser context */
2215
2407
  async _createTarget(contextId, url = "about:blank") {
2216
2408
  const params = { url };
@@ -2227,10 +2419,97 @@ var XBBrowserImpl = class {
2227
2419
  async _enableAutoAttach() {
2228
2420
  await this.conn.send("Target.setAutoAttach", {
2229
2421
  autoAttach: true,
2230
- waitForDebuggerOnStart: true,
2422
+ waitForDebuggerOnStart: false,
2231
2423
  flatten: true
2232
2424
  });
2233
2425
  }
2426
+ /**
2427
+ * Discover existing browser contexts and pages via Target.getTargets.
2428
+ *
2429
+ * For CDP tunnel connections (cdp-tunnel, attach scenarios), the
2430
+ * Target.attachedToTarget auto-attach flow is unreliable. Without this
2431
+ * call, `b.contexts()` would return [] and callers would fall back to
2432
+ * `b.newContext()` — which creates an isolated context with NO cookies
2433
+ * shared with the user's existing browser session (causing login failures).
2434
+ *
2435
+ * This method:
2436
+ * 1. Queries Target.getTargets to enumerate all page targets
2437
+ * 2. Groups them by browserContextId
2438
+ * 3. Attaches to each existing page via Target.attachToTarget
2439
+ * 4. Wraps the discovered pages in a XBContextImpl and registers it in
2440
+ * this._contexts so `contexts()` returns the user's actual contexts
2441
+ * 5. Enables Target.setAutoAttach for future pages
2442
+ *
2443
+ * No-op for self-launched browsers (they already populated contexts via
2444
+ * newContext() + childProcess-gated auto-attach).
2445
+ */
2446
+ async discoverContexts() {
2447
+ if (this._disconnected) return;
2448
+ let targetInfos = [];
2449
+ try {
2450
+ const result = await this.conn.send(
2451
+ "Target.getTargets"
2452
+ );
2453
+ targetInfos = result.targetInfos ?? [];
2454
+ } catch {
2455
+ return;
2456
+ }
2457
+ const pageTargets = targetInfos.filter((t) => t.type === "page");
2458
+ const httpFallbackUrl = this._httpFallbackURL();
2459
+ if (pageTargets.length === 0 && httpFallbackUrl) {
2460
+ console.log(`[discoverContexts] Target.getTargets returned ${targetInfos.length} targets (0 page type). Falling back to HTTP /json/list at ${httpFallbackUrl}`);
2461
+ try {
2462
+ const { getCDPTargets: getCDPTargets2 } = await import("./launcher-OZXJQPNG.js");
2463
+ const httpPages = await getCDPTargets2(httpFallbackUrl);
2464
+ console.log(`[discoverContexts] HTTP /json/list returned ${httpPages.length} pages`);
2465
+ for (const p of httpPages) {
2466
+ if (p.type !== "page") continue;
2467
+ if (!p.url || p.url.startsWith("chrome://") || p.url.startsWith("devtools://")) continue;
2468
+ targetInfos.push({
2469
+ targetId: p.id,
2470
+ type: "page",
2471
+ url: p.url,
2472
+ title: p.title
2473
+ });
2474
+ }
2475
+ console.log(`[discoverContexts] After HTTP fallback: ${targetInfos.length} total targets, ${targetInfos.filter((t) => t.type === "page").length} pages`);
2476
+ } catch (err) {
2477
+ console.log(`[discoverContexts] HTTP fallback failed: ${errMsg(err)}`);
2478
+ }
2479
+ }
2480
+ const pagesByContext = /* @__PURE__ */ new Map();
2481
+ for (const t of targetInfos) {
2482
+ if (t.type !== "page") continue;
2483
+ if (!t.url || t.url.startsWith("chrome://") || t.url.startsWith("devtools://")) {
2484
+ continue;
2485
+ }
2486
+ const ctxId = t.browserContextId || "default";
2487
+ if (!pagesByContext.has(ctxId)) pagesByContext.set(ctxId, []);
2488
+ pagesByContext.get(ctxId).push(t);
2489
+ }
2490
+ for (const [ctxId, pages] of pagesByContext) {
2491
+ if (this._contexts.has(ctxId)) continue;
2492
+ const context = new XBContextImpl(this.conn, ctxId, this, {});
2493
+ for (const p of pages) {
2494
+ try {
2495
+ const sessionId = await this._attachToTarget(p.targetId);
2496
+ const page = new XBPageImpl(this.conn, sessionId, p.targetId, context, this);
2497
+ await page._init();
2498
+ context._addDiscoveredPage(page);
2499
+ } catch {
2500
+ }
2501
+ }
2502
+ this._contexts.set(ctxId, { contextId: ctxId, context });
2503
+ }
2504
+ try {
2505
+ await this.conn.send("Target.setAutoAttach", {
2506
+ autoAttach: true,
2507
+ waitForDebuggerOnStart: false,
2508
+ flatten: true
2509
+ });
2510
+ } catch {
2511
+ }
2512
+ }
2234
2513
  };
2235
2514
 
2236
2515
  // src/cdp-driver/connection.ts
@@ -2510,7 +2789,8 @@ async function launch(options = {}) {
2510
2789
  }
2511
2790
  const conn = new CDPConnection(wsEndpoint);
2512
2791
  await conn.ready();
2513
- const browser = new XBBrowserImpl(conn, childProcess, tmpDir);
2792
+ const httpEndpoint = options.cdpEndpoint && !options.cdpEndpoint.startsWith("ws") ? options.cdpEndpoint : void 0;
2793
+ const browser = new XBBrowserImpl(conn, childProcess, tmpDir, httpEndpoint);
2514
2794
  return { browser, wsEndpoint };
2515
2795
  }
2516
2796
  export {
@@ -14,15 +14,16 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-42RPMJ76.js";
17
+ } from "./chunk-N2JFPWMI.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-TNEN6VQ2.js";
25
+ import "./chunk-GDKLH7ZY.js";
26
+ import "./chunk-KFQGP6VL.js";
26
27
  export {
27
28
  CDPConnection,
28
29
  CDPProtocolError,