@mearl/native-host 2.2.1 → 2.3.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.
package/dist/index.js CHANGED
@@ -9,40 +9,102 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // ../setup/dist/nativeHost.js
13
+ import fs9 from "node:fs";
14
+ import os6 from "node:os";
15
+ import path10 from "node:path";
16
+ import { execFileSync } from "node:child_process";
17
+ function resolveNativeHostDirectory(options = {}) {
18
+ const env = options.env ?? process.env;
19
+ const home = (options.homedir ?? os6.homedir)();
20
+ switch (options.platform ?? process.platform) {
21
+ case "darwin":
22
+ return path10.join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts");
23
+ case "linux":
24
+ return path10.join(home, ".config/google-chrome/NativeMessagingHosts");
25
+ case "win32":
26
+ return path10.join(env.PROGRAMDATA || "C:\\ProgramData", "Google\\Chrome\\NativeMessagingHosts");
27
+ default:
28
+ throw new Error(`Unsupported platform: ${String(options.platform ?? process.platform)}`);
29
+ }
30
+ }
31
+ function unregisterWindowsNativeHost(hostName) {
32
+ const registryKey = `HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;
33
+ try {
34
+ execFileSync("reg", ["delete", registryKey, "/f"], { stdio: "pipe" });
35
+ } catch {
36
+ }
37
+ }
38
+ function uninstallNativeHostArtifacts(options = {}, dependencies = {}) {
39
+ const { mode = "both", silent = false } = options;
40
+ if (mode === "both") {
41
+ return ["skills", "mcp"].map((integrationMode) => uninstallNativeHostArtifacts({ ...options, mode: integrationMode }, dependencies)).every(Boolean);
42
+ }
43
+ const writeLine = dependencies.writeLine ?? ((line) => console.error(line));
44
+ const log2 = (line) => {
45
+ if (!silent)
46
+ writeLine(line);
47
+ };
48
+ try {
49
+ const existsSync4 = dependencies.existsSync ?? fs9.existsSync;
50
+ const platform = dependencies.platform ?? process.platform;
51
+ const hostDir = dependencies.hostDir ?? resolveNativeHostDirectory({
52
+ env: dependencies.env,
53
+ homedir: dependencies.homedir,
54
+ platform
55
+ });
56
+ const unlinkSync = dependencies.unlinkSync ?? fs9.unlinkSync;
57
+ const unregister = dependencies.unregisterWindowsNativeHost ?? unregisterWindowsNativeHost;
58
+ const hostName = MEARL_NATIVE_HOST_NAMES[mode];
59
+ const launcherExtension = platform === "win32" ? "cmd" : "sh";
60
+ const artifacts = [
61
+ path10.join(hostDir, `${hostName}.json`),
62
+ path10.join(hostDir, `${hostName}-launcher.${launcherExtension}`)
63
+ ];
64
+ for (const artifact of artifacts) {
65
+ if (!existsSync4(artifact))
66
+ continue;
67
+ unlinkSync(artifact);
68
+ log2(`✅ Removed: ${artifact}`);
69
+ }
70
+ if (platform === "win32")
71
+ unregister(hostName);
72
+ return true;
73
+ } catch (error48) {
74
+ log2(`❌ Uninstall failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
75
+ return false;
76
+ }
77
+ }
78
+ var MEARL_NATIVE_HOST_NAMES;
79
+ var init_nativeHost = __esm({
80
+ "../setup/dist/nativeHost.js"() {
81
+ "use strict";
82
+ MEARL_NATIVE_HOST_NAMES = {
83
+ skills: "com.alibaba.mearl.skills",
84
+ mcp: "com.alibaba.mearl.mcp"
85
+ };
86
+ }
87
+ });
88
+
12
89
  // src/install.ts
13
90
  var install_exports = {};
14
91
  __export(install_exports, {
15
92
  installNativeHost: () => installNativeHost,
16
93
  resolveHostDir: () => resolveHostDir,
17
- runCli: () => runCli
94
+ runCli: () => runCli,
95
+ uninstallNativeHost: () => uninstallNativeHost
18
96
  });
19
- import fs8 from "node:fs";
20
- import path10 from "node:path";
21
- import os6 from "node:os";
22
- import { execFileSync } from "node:child_process";
97
+ import fs10 from "node:fs";
98
+ import path11 from "node:path";
99
+ import { execFileSync as execFileSync2 } from "node:child_process";
23
100
  import { createRequire } from "node:module";
24
101
  import { fileURLToPath } from "node:url";
25
102
  function resolveHostDir() {
26
- switch (process.platform) {
27
- case "darwin":
28
- return path10.join(
29
- os6.homedir(),
30
- "Library/Application Support/Google/Chrome/NativeMessagingHosts"
31
- );
32
- case "linux":
33
- return path10.join(os6.homedir(), ".config/google-chrome/NativeMessagingHosts");
34
- case "win32":
35
- return path10.join(
36
- process.env.PROGRAMDATA || "C:\\ProgramData",
37
- "Google\\Chrome\\NativeMessagingHosts"
38
- );
39
- default:
40
- throw new Error(`Unsupported platform: ${process.platform}`);
41
- }
103
+ return resolveNativeHostDirectory();
42
104
  }
43
105
  function resolveNativeHostPath() {
44
- const siblingPath = path10.join(__dirname, "index.js");
45
- if (fs8.existsSync(siblingPath)) {
106
+ const siblingPath = path11.join(__dirname, "index.js");
107
+ if (fs10.existsSync(siblingPath)) {
46
108
  return siblingPath;
47
109
  }
48
110
  try {
@@ -56,21 +118,21 @@ function resolveNativeHostPath() {
56
118
  }
57
119
  function registerWindowsNativeHost(hostName, manifestPath) {
58
120
  const regKey = `HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;
59
- execFileSync("reg", ["add", regKey, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], {
121
+ execFileSync2("reg", ["add", regKey, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], {
60
122
  stdio: "pipe"
61
123
  });
62
124
  }
63
125
  function createLauncherScript(hostDir, hostPath, hostName) {
64
- const nodePath = path10.resolve(process.execPath);
126
+ const nodePath = path11.resolve(process.execPath);
65
127
  if (process.platform === "win32") {
66
- const launcherPath2 = path10.join(hostDir, `${hostName}-launcher.cmd`);
128
+ const launcherPath2 = path11.join(hostDir, `${hostName}-launcher.cmd`);
67
129
  const content2 = `@echo off\r
68
130
  "${nodePath}" "${hostPath}" %*\r
69
131
  `;
70
- fs8.writeFileSync(launcherPath2, content2, "utf-8");
132
+ fs10.writeFileSync(launcherPath2, content2, "utf-8");
71
133
  return launcherPath2;
72
134
  }
73
- const launcherPath = path10.join(hostDir, `${hostName}-launcher.sh`);
135
+ const launcherPath = path11.join(hostDir, `${hostName}-launcher.sh`);
74
136
  const content = [
75
137
  "#!/bin/sh",
76
138
  `NODE_BIN=${JSON.stringify(nodePath)}`,
@@ -78,8 +140,8 @@ function createLauncherScript(hostDir, hostPath, hostName) {
78
140
  'exec "$NODE_BIN" "$HOST_SCRIPT" "$@"',
79
141
  ""
80
142
  ].join("\n");
81
- fs8.writeFileSync(launcherPath, content, "utf-8");
82
- fs8.chmodSync(launcherPath, 493);
143
+ fs10.writeFileSync(launcherPath, content, "utf-8");
144
+ fs10.chmodSync(launcherPath, 493);
83
145
  return launcherPath;
84
146
  }
85
147
  async function installNativeHost(options = {}) {
@@ -95,7 +157,7 @@ async function installNativeHost(options = {}) {
95
157
  );
96
158
  return results.every(Boolean);
97
159
  }
98
- const hostName = HOST_NAME_BY_MODE[mode];
160
+ const hostName = MEARL_NATIVE_HOST_NAMES[mode];
99
161
  if (!hostName) {
100
162
  throw new Error(`Unknown mode: ${mode}. Expected 'skills' or 'mcp'.`);
101
163
  }
@@ -105,14 +167,14 @@ async function installNativeHost(options = {}) {
105
167
  try {
106
168
  const hostDir = resolveHostDir();
107
169
  if (skipIfExists) {
108
- const manifestPath2 = path10.join(hostDir, `${hostName}.json`);
109
- if (fs8.existsSync(manifestPath2)) {
170
+ const manifestPath2 = path11.join(hostDir, `${hostName}.json`);
171
+ if (fs10.existsSync(manifestPath2)) {
110
172
  log2(`✅ Already installed: ${manifestPath2}`);
111
173
  return true;
112
174
  }
113
175
  }
114
- if (!fs8.existsSync(hostDir)) {
115
- fs8.mkdirSync(hostDir, { recursive: true });
176
+ if (!fs10.existsSync(hostDir)) {
177
+ fs10.mkdirSync(hostDir, { recursive: true });
116
178
  log2(`✅ Created directory: ${hostDir}`);
117
179
  }
118
180
  const resolvedHostPath = resolveNativeHostPath();
@@ -126,13 +188,13 @@ async function installNativeHost(options = {}) {
126
188
  type: "stdio",
127
189
  allowed_origins: [`chrome-extension://${extensionId}/`]
128
190
  };
129
- const manifestPath = path10.join(hostDir, `${hostName}.json`);
130
- fs8.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
191
+ const manifestPath = path11.join(hostDir, `${hostName}.json`);
192
+ fs10.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
131
193
  if (process.platform === "win32") {
132
194
  registerWindowsNativeHost(hostName, manifestPath);
133
195
  log2(`✅ Windows registry key created for: ${hostName}`);
134
196
  } else {
135
- fs8.chmodSync(manifestPath, 420);
197
+ fs10.chmodSync(manifestPath, 420);
136
198
  }
137
199
  log2(`✅ Manifest installed: ${manifestPath}`);
138
200
  log2(`✅ Extension ID: ${extensionId}`);
@@ -146,14 +208,18 @@ async function installNativeHost(options = {}) {
146
208
  return false;
147
209
  }
148
210
  }
211
+ async function uninstallNativeHost(options = {}, dependencies = {}) {
212
+ return uninstallNativeHostArtifacts(options, dependencies);
213
+ }
149
214
  function parseCliArgs(argv) {
150
215
  let mode = "both";
151
216
  let extensionId;
152
217
  let skipIfExists = false;
218
+ let uninstall = false;
153
219
  for (let i = 0; i < argv.length; i += 1) {
154
220
  const arg = argv[i];
155
221
  if (arg === "--help" || arg === "-h") {
156
- return { showHelp: true, mode, skipIfExists };
222
+ return { showHelp: true, uninstall, mode, skipIfExists };
157
223
  }
158
224
  if (arg === "--mode" || arg.startsWith("--mode=")) {
159
225
  const value = arg.includes("=") ? arg.slice("--mode=".length) : argv[++i];
@@ -172,24 +238,31 @@ function parseCliArgs(argv) {
172
238
  skipIfExists = true;
173
239
  continue;
174
240
  }
241
+ if (arg === "--uninstall") {
242
+ uninstall = true;
243
+ continue;
244
+ }
175
245
  if (arg === "--init") {
176
246
  continue;
177
247
  }
178
248
  }
179
- return { showHelp: false, mode, extensionId, skipIfExists };
249
+ return { showHelp: false, uninstall, mode, extensionId, skipIfExists };
180
250
  }
181
251
  function printHelp() {
182
252
  console.error(`Usage:
183
253
  mearl-native-host --init [options]
254
+ mearl-native-host --uninstall [options]
184
255
 
185
256
  Options:
186
257
  --mode <skills|mcp|both> Integration mode (default: both — installs both hosts)
187
258
  --extension-id <id> Chrome extension ID (default: ${DEFAULT_EXTENSION_ID})
259
+ --uninstall Remove the selected Native Messaging manifests
188
260
  -h, --help Show this help message
189
261
 
190
262
  Examples:
191
263
  mearl-native-host --init
192
264
  mearl-native-host --init --mode mcp
265
+ mearl-native-host --uninstall --mode skills
193
266
  mearl-native-host --init --extension-id abcdefghijklmnopqrstuvwxyzabcdef`);
194
267
  }
195
268
  async function runCli(argv) {
@@ -203,6 +276,11 @@ async function runCli(argv) {
203
276
  console.error(" Must be a 32-character lowercase string (see chrome://extensions/)");
204
277
  process.exit(1);
205
278
  }
279
+ if (parsed.uninstall) {
280
+ const success3 = await uninstallNativeHost({ mode: parsed.mode });
281
+ if (success3) console.error("\n✅ Uninstall complete!\n");
282
+ process.exit(success3 ? 0 : 1);
283
+ }
206
284
  const success2 = await installNativeHost({
207
285
  mode: parsed.mode,
208
286
  extensionId: parsed.extensionId,
@@ -222,27 +300,24 @@ Verify:
222
300
  }
223
301
  process.exit(success2 ? 0 : 1);
224
302
  }
225
- var DEFAULT_EXTENSION_ID, HOST_NAME_BY_MODE, __filename, __dirname, ALL_MODES;
303
+ var DEFAULT_EXTENSION_ID, __filename, __dirname, ALL_MODES;
226
304
  var init_install = __esm({
227
305
  "src/install.ts"() {
228
306
  "use strict";
307
+ init_nativeHost();
229
308
  DEFAULT_EXTENSION_ID = "aoehhjnofngknnjefamjbplchbolghkm";
230
- HOST_NAME_BY_MODE = {
231
- skills: "com.alibaba.mearl.skills",
232
- mcp: "com.alibaba.mearl.mcp"
233
- };
234
309
  __filename = fileURLToPath(import.meta.url);
235
- __dirname = path10.dirname(__filename);
310
+ __dirname = path11.dirname(__filename);
236
311
  ALL_MODES = ["skills", "mcp"];
237
312
  }
238
313
  });
239
314
 
240
315
  // src/index.ts
241
316
  import net from "node:net";
242
- import fs9 from "node:fs";
317
+ import fs11 from "node:fs";
243
318
  import http5 from "node:http";
244
319
  import os7 from "node:os";
245
- import path11 from "node:path";
320
+ import path12 from "node:path";
246
321
  import { fileURLToPath as fileURLToPath2 } from "node:url";
247
322
 
248
323
  // src/cdp-backend/cdp-session.ts
@@ -250,7 +325,7 @@ import http3 from "node:http";
250
325
 
251
326
  // src/cdp-backend/client.ts
252
327
  import http from "node:http";
253
- import crypto from "node:crypto";
328
+ import crypto2 from "node:crypto";
254
329
  var CdpChildSession = class {
255
330
  constructor(parent, sessionId) {
256
331
  this.parent = parent;
@@ -331,8 +406,8 @@ var CdpClient = class _CdpClient {
331
406
  return new Promise((resolve, reject) => {
332
407
  const url2 = new URL(wsUrl);
333
408
  const port = parseInt(url2.port || "9222", 10);
334
- const key = crypto.randomBytes(16).toString("base64");
335
- const expectedAccept = crypto.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
409
+ const key = crypto2.randomBytes(16).toString("base64");
410
+ const expectedAccept = crypto2.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
336
411
  const req = http.request({
337
412
  host: url2.hostname,
338
413
  port,
@@ -484,7 +559,7 @@ var CdpClient = class _CdpClient {
484
559
  sendText(text) {
485
560
  if (this._closed) return;
486
561
  const payload = Buffer.from(text, "utf-8");
487
- const maskKey = crypto.randomBytes(4);
562
+ const maskKey = crypto2.randomBytes(4);
488
563
  const masked = Buffer.from(payload.map((b, i) => b ^ maskKey[i % 4]));
489
564
  let header;
490
565
  if (payload.length < 126) {
@@ -679,6 +754,9 @@ function buildCookieHeaderValue(cookieMap) {
679
754
  var APLUS_URL_PATTERN = /\.mmstat\.com\//;
680
755
  var RUM_URL_PATTERN = /\.rum\.aliyuncs\.com\//;
681
756
  var ARMS_URL_PATTERN = /arms-retcode\.aliyuncs\.com\//;
757
+ function isTrackingUrl(url2) {
758
+ return APLUS_URL_PATTERN.test(url2) || RUM_URL_PATTERN.test(url2) || ARMS_URL_PATTERN.test(url2) || /retcode\.alicdn\.com|beacon\.taobao\.com|tbsec\./i.test(url2);
759
+ }
682
760
  var aplusCounter = 0;
683
761
  var armsCounter = 0;
684
762
  var rumCounter = 0;
@@ -1088,13 +1166,13 @@ function queryTrackingEvents(buffer, options = {}) {
1088
1166
  }
1089
1167
 
1090
1168
  // ../browser-core/dist/requestSerializer.js
1091
- function parsePath(path12) {
1092
- if (path12.length > 1e3)
1169
+ function parsePath(path13) {
1170
+ if (path13.length > 1e3)
1093
1171
  throw new Error("Path too long: maximum 1000 characters allowed");
1094
1172
  const keys = [];
1095
1173
  let current = "";
1096
- for (let i = 0; i < path12.length; i++) {
1097
- const char = path12[i];
1174
+ for (let i = 0; i < path13.length; i++) {
1175
+ const char = path13[i];
1098
1176
  if (char === ".") {
1099
1177
  if (current) {
1100
1178
  keys.push(current);
@@ -1106,10 +1184,10 @@ function parsePath(path12) {
1106
1184
  current = "";
1107
1185
  }
1108
1186
  let j = i + 1;
1109
- while (j < path12.length && path12[j] !== "]")
1187
+ while (j < path13.length && path13[j] !== "]")
1110
1188
  j++;
1111
- if (j < path12.length) {
1112
- const index = parseInt(path12.substring(i + 1, j), 10);
1189
+ if (j < path13.length) {
1190
+ const index = parseInt(path13.substring(i + 1, j), 10);
1113
1191
  if (!isNaN(index)) {
1114
1192
  if (index < 0 || index > 1e5)
1115
1193
  throw new Error(`Array index out of safe range: ${index}`);
@@ -1127,8 +1205,8 @@ function parsePath(path12) {
1127
1205
  throw new Error("Path too deep: maximum 50 levels allowed");
1128
1206
  return keys;
1129
1207
  }
1130
- function getValueByPath(obj, path12) {
1131
- const keys = parsePath(path12);
1208
+ function getValueByPath(obj, path13) {
1209
+ const keys = parsePath(path13);
1132
1210
  let current = obj;
1133
1211
  for (const key of keys) {
1134
1212
  if (current === null || current === void 0)
@@ -1137,8 +1215,8 @@ function getValueByPath(obj, path12) {
1137
1215
  }
1138
1216
  return current;
1139
1217
  }
1140
- function setValueByPath(obj, path12, value) {
1141
- const keys = parsePath(path12);
1218
+ function setValueByPath(obj, path13, value) {
1219
+ const keys = parsePath(path13);
1142
1220
  let current = obj;
1143
1221
  for (let i = 0; i < keys.length - 1; i++) {
1144
1222
  const key = keys[i];
@@ -1212,7 +1290,11 @@ function serializeRequest(request, options = {}) {
1212
1290
  status: request.status,
1213
1291
  statusText: request.statusText,
1214
1292
  queryString: request.queryString,
1215
- duration: request.duration
1293
+ duration: request.duration,
1294
+ captureSequence: request.captureSequence,
1295
+ isPending: request.isPending,
1296
+ source: request.source,
1297
+ resourceType: request.resourceType
1216
1298
  };
1217
1299
  if (includeHeaders) {
1218
1300
  result.headers = request.headers;
@@ -1336,10 +1418,14 @@ function applyFieldLimits(request, maxFieldBytes) {
1336
1418
  request.responseBody = limitBufferedField(request.responseBody, maxFieldBytes);
1337
1419
  }
1338
1420
  function trimRequestBuffer(requests, accounting, maxEntries, maxBytes) {
1421
+ const removed = [];
1339
1422
  while (requests.length > 1 && (requests.length > maxEntries || accounting.totalBytes > maxBytes)) {
1340
- requests.shift();
1423
+ const request = requests.shift();
1424
+ if (request)
1425
+ removed.push(request);
1341
1426
  accounting.totalBytes -= accounting.sizes.shift() ?? 0;
1342
1427
  }
1428
+ return removed;
1343
1429
  }
1344
1430
  function appendRequestToBuffer(requests, accounting, request, limits = {}) {
1345
1431
  const { maxEntries = REQUEST_BUFFER_MAX_ENTRIES, maxBytes = REQUEST_BUFFER_MAX_BYTES, maxFieldBytes = REQUEST_BUFFER_MAX_FIELD_BYTES } = limits;
@@ -1349,20 +1435,20 @@ function appendRequestToBuffer(requests, accounting, request, limits = {}) {
1349
1435
  requests.push(request);
1350
1436
  accounting.sizes.push(size);
1351
1437
  accounting.totalBytes += size;
1352
- trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1438
+ return trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1353
1439
  }
1354
1440
  function refreshRequestInBuffer(requests, accounting, request, limits = {}) {
1355
1441
  const { maxEntries = REQUEST_BUFFER_MAX_ENTRIES, maxBytes = REQUEST_BUFFER_MAX_BYTES, maxFieldBytes = REQUEST_BUFFER_MAX_FIELD_BYTES } = limits;
1356
1442
  const index = requests.indexOf(request);
1357
1443
  if (index === -1)
1358
- return;
1444
+ return [];
1359
1445
  synchronizeAccounting(requests, accounting);
1360
1446
  applyFieldLimits(request, maxFieldBytes);
1361
1447
  const previousSize = accounting.sizes[index] ?? 0;
1362
1448
  const nextSize = estimateRequestBufferBytes(request);
1363
1449
  accounting.sizes[index] = nextSize;
1364
1450
  accounting.totalBytes += nextSize - previousSize;
1365
- trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1451
+ return trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1366
1452
  }
1367
1453
 
1368
1454
  // ../browser-core/dist/proxyPermissions.js
@@ -2141,6 +2227,24 @@ var CHECKABLE_AX_ROLES = /* @__PURE__ */ new Set([
2141
2227
  "radio",
2142
2228
  "switch"
2143
2229
  ]);
2230
+ function resolveDOMSnapshotViewportBounds(metrics) {
2231
+ const viewport = metrics.visualViewport ?? metrics.cssVisualViewport;
2232
+ return normalizePageViewportBounds({
2233
+ left: viewport?.pageX ?? 0,
2234
+ top: viewport?.pageY ?? 0,
2235
+ width: viewport?.clientWidth,
2236
+ height: viewport?.clientHeight
2237
+ });
2238
+ }
2239
+ function normalizePageViewportBounds(value) {
2240
+ if (!value || typeof value !== "object")
2241
+ return null;
2242
+ const { left, top, width, height } = value;
2243
+ if (typeof left !== "number" || typeof top !== "number" || typeof width !== "number" || typeof height !== "number" || !Number.isFinite(left) || !Number.isFinite(top) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
2244
+ return null;
2245
+ }
2246
+ return { left, top, width, height };
2247
+ }
2144
2248
  function readDOMCheckedState() {
2145
2249
  const tag = String(this && this.tagName || "").toLowerCase();
2146
2250
  const type = String(this && this.type || "").toLowerCase();
@@ -2648,19 +2752,19 @@ function installPageObserverInPage(maxBatches = 50) {
2648
2752
  }
2649
2753
  }
2650
2754
  }
2651
- const path12 = [];
2755
+ const path13 = [];
2652
2756
  let current = input;
2653
- while (current && current !== document.documentElement && path12.length < 6) {
2757
+ while (current && current !== document.documentElement && path13.length < 6) {
2654
2758
  const parent = current.parentElement;
2655
2759
  const currentTag = current.tagName.toLowerCase();
2656
2760
  if (!parent) {
2657
- path12.unshift(currentTag);
2761
+ path13.unshift(currentTag);
2658
2762
  break;
2659
2763
  }
2660
2764
  const siblings = Array.from(parent.children).filter((item) => item.tagName === current.tagName);
2661
2765
  const position = siblings.indexOf(current) + 1;
2662
- path12.unshift(siblings.length > 1 ? `${currentTag}:nth-of-type(${position})` : currentTag);
2663
- const candidate = path12.join(" > ");
2766
+ path13.unshift(siblings.length > 1 ? `${currentTag}:nth-of-type(${position})` : currentTag);
2767
+ const candidate = path13.join(" > ");
2664
2768
  if (input.isConnected) {
2665
2769
  try {
2666
2770
  if (document.querySelectorAll(candidate).length === 1)
@@ -2670,7 +2774,7 @@ function installPageObserverInPage(maxBatches = 50) {
2670
2774
  }
2671
2775
  current = parent;
2672
2776
  }
2673
- return path12.join(" > ") || void 0;
2777
+ return path13.join(" > ") || void 0;
2674
2778
  };
2675
2779
  const inferredRole = (element) => {
2676
2780
  const explicit = element.getAttribute("role");
@@ -3377,6 +3481,7 @@ var PAGE_ACT_ACTIONS = [
3377
3481
  "page_eval"
3378
3482
  ];
3379
3483
  var BROWSER_COMMAND_ACTIONS = {
3484
+ capture_checkpoint: true,
3380
3485
  get_requests: true,
3381
3486
  get_logs: true,
3382
3487
  get_events: true,
@@ -3419,6 +3524,7 @@ var BROWSER_COMMAND_ACTIONS = {
3419
3524
  run_actions: true
3420
3525
  };
3421
3526
  var EXTENSION_BROWSER_ACTIONS = {
3527
+ capture_checkpoint: true,
3422
3528
  get_requests: true,
3423
3529
  get_logs: true,
3424
3530
  get_events: true,
@@ -3465,24 +3571,35 @@ function isExtensionBrowserAction(value) {
3465
3571
  }
3466
3572
 
3467
3573
  // ../browser-core/dist/pageAct.js
3574
+ var DEFAULT_PAGE_DIAGNOSTICS = {
3575
+ logs: true,
3576
+ requests: true
3577
+ };
3468
3578
  function isPageActAction(action) {
3469
3579
  return PAGE_ACT_ACTIONS.includes(action);
3470
3580
  }
3471
3581
  function shouldObservePageAction(action, data) {
3472
3582
  if (!isPageActAction(action))
3473
3583
  return false;
3584
+ const diagnosticsEnabled = data?.diagnostics !== void 0 && data.diagnostics !== false;
3474
3585
  if (action === "page_eval") {
3475
- return Boolean(data?.observe && typeof data.observe === "object" && !Array.isArray(data.observe));
3586
+ return Boolean(diagnosticsEnabled || data?.observe && typeof data.observe === "object" && !Array.isArray(data.observe));
3476
3587
  }
3477
- return data?.observe !== false;
3588
+ return data?.observe !== false || data?.diagnostics !== false;
3478
3589
  }
3479
- function splitObservedActionPayload(data) {
3480
- const { observe, ...actionData } = data ?? {};
3590
+ function splitObservedActionPayload(action, data) {
3591
+ const { observe, diagnostics, ...actionData } = data ?? {};
3592
+ const setting = diagnostics;
3593
+ const resolvedDiagnostics = setting === false ? void 0 : setting === true ? DEFAULT_PAGE_DIAGNOSTICS : setting && typeof setting === "object" && !Array.isArray(setting) ? setting : action === "page_eval" ? void 0 : DEFAULT_PAGE_DIAGNOSTICS;
3481
3594
  if (observe === false)
3482
- return { actionData, observe: false };
3595
+ return { actionData, observe: false, diagnostics: resolvedDiagnostics };
3596
+ if (action === "page_eval" && (!observe || typeof observe !== "object")) {
3597
+ return { actionData, observe: false, diagnostics: resolvedDiagnostics };
3598
+ }
3483
3599
  return {
3484
3600
  actionData,
3485
- observe: observe && typeof observe === "object" && !Array.isArray(observe) ? observe : {}
3601
+ observe: observe && typeof observe === "object" && !Array.isArray(observe) ? observe : {},
3602
+ diagnostics: resolvedDiagnostics
3486
3603
  };
3487
3604
  }
3488
3605
  function unavailableObservation(checkpoint, error48) {
@@ -4176,6 +4293,20 @@ function assertSendRequestBase64FileLimits(byteLengths) {
4176
4293
  }
4177
4294
  }
4178
4295
 
4296
+ // ../browser-core/dist/sendRequestHeaders.js
4297
+ function resolveHeader(headers, name, fallback) {
4298
+ const key = findSendRequestHeaderName(headers, name);
4299
+ if (!key)
4300
+ return fallback;
4301
+ return headers[key] === "" ? void 0 : headers[key];
4302
+ }
4303
+ function getSendRequestSourceHeaders(targetUrl, headers = {}) {
4304
+ return {
4305
+ referer: resolveHeader(headers, "Referer", `${targetUrl.origin}/mearl`),
4306
+ origin: resolveHeader(headers, "Origin", targetUrl.origin)
4307
+ };
4308
+ }
4309
+
4179
4310
  // ../browser-core/dist/cookie.js
4180
4311
  function normalizeCookieDomain(input, action) {
4181
4312
  const value = input.trim();
@@ -4278,12 +4409,12 @@ function resolveGetCookieTarget(currentUrl, input) {
4278
4409
  const url2 = parseCurrentUrl(currentUrl, action);
4279
4410
  const name = validateCookieName(input?.name, action);
4280
4411
  const domain2 = resolveExplicitDomain(url2, input?.domain, action);
4281
- const path12 = validateCookiePath(input?.path, action);
4412
+ const path13 = validateCookiePath(input?.path, action);
4282
4413
  return {
4283
4414
  cookieName: name,
4284
- cookieUrl: `${url2.protocol}//${url2.hostname}${(path12 ?? url2.pathname) || "/"}`,
4415
+ cookieUrl: `${url2.protocol}//${url2.hostname}${(path13 ?? url2.pathname) || "/"}`,
4285
4416
  ...domain2 ? { domain: domain2 } : {},
4286
- ...path12 ? { path: path12 } : {}
4417
+ ...path13 ? { path: path13 } : {}
4287
4418
  };
4288
4419
  }
4289
4420
  function resolveSetCookieTarget(currentUrl, input) {
@@ -4291,7 +4422,7 @@ function resolveSetCookieTarget(currentUrl, input) {
4291
4422
  const url2 = parseCurrentUrl(currentUrl, action);
4292
4423
  const name = validateCookieName(input?.name, action);
4293
4424
  const value = validateCookieValue(input?.value);
4294
- const path12 = validateCookiePath(input?.path, action) ?? "/";
4425
+ const path13 = validateCookiePath(input?.path, action) ?? "/";
4295
4426
  if (input.reload !== void 0 && typeof input.reload !== "boolean") {
4296
4427
  throw new Error('set_cookie: "reload" must be a boolean');
4297
4428
  }
@@ -4301,9 +4432,9 @@ function resolveSetCookieTarget(currentUrl, input) {
4301
4432
  cookieName: name,
4302
4433
  cookieValue: value,
4303
4434
  ...explicitDomain ? { cookieDomain: explicitDomain.includes(".") ? `.${explicitDomain}` : explicitDomain } : {},
4304
- cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${path12}`,
4435
+ cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${path13}`,
4305
4436
  domain: domain2,
4306
- path: path12,
4437
+ path: path13,
4307
4438
  reload: input.reload === true
4308
4439
  };
4309
4440
  }
@@ -4319,10 +4450,7 @@ var SOURCE_BADGES = {
4319
4450
  <circle cx="104" cy="104" r="21" fill="#16A34A" stroke="#FFFFFF" stroke-width="6"/>
4320
4451
  `,
4321
4452
  cloud: `
4322
- <path d="M91 116H116C123 116 128 111 128 104C128 97 123 92 116 92H114
4323
- C111 85 106 82 100 82C91 82 85 89 84 97C78 99 75 104 75 109
4324
- C75 116 81 121 89 121H91Z" fill="#7C3AED" stroke="#FFFFFF" stroke-width="6"
4325
- stroke-linejoin="round"/>
4453
+ <circle cx="104" cy="104" r="21" fill="#FF7A00" stroke="#FFFFFF" stroke-width="6"/>
4326
4454
  `
4327
4455
  };
4328
4456
  function createControlledFaviconDataUrl(source) {
@@ -4333,6 +4461,83 @@ ${SOURCE_BADGES[source]}
4333
4461
  return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
4334
4462
  }
4335
4463
 
4464
+ // ../browser-core/dist/capture.js
4465
+ var CURSOR_PREFIX = "mearl-capture-v1";
4466
+ function createCaptureGeneration() {
4467
+ return crypto.randomUUID();
4468
+ }
4469
+ function encodeCaptureCursor(cursor) {
4470
+ return `${CURSOR_PREFIX}:${cursor.tabId}:${cursor.generation}:${cursor.scope}:${cursor.sequence}`;
4471
+ }
4472
+ function decodeCaptureCursor(value) {
4473
+ const match = value.match(/^mearl-capture-v1:(\d+):([^:]+):(all|logs|requests):(\d+)$/);
4474
+ if (!match)
4475
+ throw new Error("Invalid capture cursor");
4476
+ const tabId = Number(match[1]);
4477
+ const sequence = Number(match[4]);
4478
+ if (!Number.isSafeInteger(tabId) || !Number.isSafeInteger(sequence)) {
4479
+ throw new Error("Invalid capture cursor");
4480
+ }
4481
+ return {
4482
+ tabId,
4483
+ generation: match[2],
4484
+ scope: match[3],
4485
+ sequence
4486
+ };
4487
+ }
4488
+ function captureCheckpoint(state) {
4489
+ return encodeCaptureCursor({
4490
+ tabId: state.tabId,
4491
+ generation: state.generation,
4492
+ sequence: state.currentSequence,
4493
+ scope: "all"
4494
+ });
4495
+ }
4496
+ function nextCaptureSequence(state) {
4497
+ state.currentSequence += 1;
4498
+ return state.currentSequence;
4499
+ }
4500
+ function resolveAfterSequence(after, state, scope) {
4501
+ if (!after)
4502
+ return 0;
4503
+ const cursor = decodeCaptureCursor(after);
4504
+ if (cursor.tabId !== state.tabId) {
4505
+ throw new Error(`Capture cursor belongs to tab ${cursor.tabId}, not tab ${state.tabId}`);
4506
+ }
4507
+ if (cursor.generation !== state.generation) {
4508
+ throw new Error("Capture cursor expired because the tab collector was restarted");
4509
+ }
4510
+ if (cursor.scope !== "all" && cursor.scope !== scope) {
4511
+ throw new Error(`Capture cursor is scoped to ${cursor.scope}, not ${scope}`);
4512
+ }
4513
+ if (cursor.sequence > state.currentSequence) {
4514
+ throw new Error("Capture cursor is ahead of the current collector state");
4515
+ }
4516
+ return cursor.sequence;
4517
+ }
4518
+ function queryCapturedEntries(buffer, state, options, matches, droppedThroughSequence = 0, scope = "all") {
4519
+ const { after, since, now = Date.now() } = options;
4520
+ const limit = Math.max(1, Math.floor(options.limit ?? 100));
4521
+ const afterSequence = resolveAfterSequence(after, state, scope);
4522
+ const sinceTimestampMs = since === void 0 ? void 0 : now - since * 1e3;
4523
+ const matched = buffer.filter((entry) => entry.captureSequence > afterSequence && (sinceTimestampMs === void 0 || entry.timestampMs >= sinceTimestampMs) && matches(entry));
4524
+ const hasMore = matched.length > limit;
4525
+ const entries = after ? matched.slice(0, limit) : matched.slice(-limit);
4526
+ const nextSequence = after && hasMore && entries.length > 0 ? entries[entries.length - 1].captureSequence : state.currentSequence;
4527
+ return {
4528
+ entries,
4529
+ totalMatched: matched.length,
4530
+ nextCursor: encodeCaptureCursor({
4531
+ tabId: state.tabId,
4532
+ generation: state.generation,
4533
+ sequence: nextSequence,
4534
+ scope
4535
+ }),
4536
+ hasMore,
4537
+ gap: after !== void 0 && afterSequence < droppedThroughSequence
4538
+ };
4539
+ }
4540
+
4336
4541
  // src/cdp-backend/domain-manager.ts
4337
4542
  var enabledDomains = /* @__PURE__ */ new WeakMap();
4338
4543
  function ensureCdpDomain(client2, domain2, params = {}) {
@@ -4354,14 +4559,22 @@ function ensureCdpDomain(client2, domain2, params = {}) {
4354
4559
  // src/cdp-backend/buffers.ts
4355
4560
  var MAX_LOGS = 500;
4356
4561
  var MAX_EVENTS = 500;
4357
- function createBuffers() {
4562
+ var COLLECTED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["xhr", "fetch", "document"]);
4563
+ function createBuffers(tabId = 0) {
4358
4564
  return {
4359
4565
  requests: [],
4360
4566
  logs: [],
4361
4567
  events: [],
4362
4568
  pending: /* @__PURE__ */ new Map(),
4363
4569
  requestCounter: 0,
4364
- requestBufferAccounting: createRequestBufferAccounting()
4570
+ requestBufferAccounting: createRequestBufferAccounting(),
4571
+ capture: {
4572
+ tabId,
4573
+ generation: createCaptureGeneration(),
4574
+ currentSequence: 0,
4575
+ droppedRequestsThroughSequence: 0,
4576
+ droppedLogsThroughSequence: 0
4577
+ }
4365
4578
  };
4366
4579
  }
4367
4580
  function parseQueryString(url2) {
@@ -4377,12 +4590,34 @@ function parseQueryString(url2) {
4377
4590
  function headersObjToArray(obj) {
4378
4591
  return Object.entries(obj).map(([name, value]) => ({ name, value }));
4379
4592
  }
4380
- function extractApiName(url2) {
4593
+ function extractMtopApiName(url2, postData) {
4381
4594
  try {
4382
4595
  const u = new URL(url2);
4383
- const match = u.pathname.match(/\/h5\/([^/]+\/[^/]+)/);
4384
- if (match) return match[1];
4385
- return u.pathname;
4596
+ const pathMatch = u.pathname.match(/\/h5\/([\w.]+)\//);
4597
+ if (pathMatch) return pathMatch[1];
4598
+ const requestUrl = u.searchParams.get("requestUrl");
4599
+ const proxyMatch = requestUrl?.match(/\b(?:mtop|mopen)\.[\w.]+/);
4600
+ if (proxyMatch) return proxyMatch[0];
4601
+ } catch {
4602
+ }
4603
+ const urlMatch = url2.match(/\b(?:mtop|mopen)\.[\w.]+/);
4604
+ if (urlMatch) return urlMatch[0];
4605
+ if (!postData) return null;
4606
+ try {
4607
+ const parsed = JSON.parse(postData);
4608
+ const requestUrl = typeof parsed?.requestUrl === "string" ? parsed.requestUrl : void 0;
4609
+ const proxyMatch = requestUrl?.match(/\b(?:mtop|mopen)\.[\w.]+/);
4610
+ if (proxyMatch) return proxyMatch[0];
4611
+ } catch {
4612
+ const requestUrl = new URLSearchParams(postData).get("requestUrl");
4613
+ const proxyMatch = requestUrl?.match(/\b(?:mtop|mopen)\.[\w.]+/);
4614
+ if (proxyMatch) return proxyMatch[0];
4615
+ }
4616
+ return null;
4617
+ }
4618
+ function requestDisplayName(url2) {
4619
+ try {
4620
+ return new URL(url2).pathname;
4386
4621
  } catch {
4387
4622
  return url2;
4388
4623
  }
@@ -4413,28 +4648,73 @@ function appendTrackingEvents(buffers, url2, postData, sequence) {
4413
4648
  }
4414
4649
  return true;
4415
4650
  }
4416
- function setupBufferListeners(client2, buffers) {
4417
- ensureCdpDomain(client2, "Network", {
4418
- maxTotalBufferSize: 10 * 1024 * 1024,
4419
- maxPostDataSize: 5 * 1024 * 1024
4420
- }).catch(() => {
4421
- });
4422
- ensureCdpDomain(client2, "Runtime").catch(() => {
4423
- });
4424
- ensureCdpDomain(client2, "Log").catch(() => {
4425
- });
4651
+ function recordDroppedRequests(buffers, removed) {
4652
+ for (const request of removed) {
4653
+ if (request.captureSequence !== void 0) {
4654
+ buffers.capture.droppedRequestsThroughSequence = Math.max(
4655
+ buffers.capture.droppedRequestsThroughSequence,
4656
+ request.captureSequence
4657
+ );
4658
+ }
4659
+ }
4660
+ }
4661
+ function appendCapturedRequest(buffers, request) {
4662
+ recordDroppedRequests(
4663
+ buffers,
4664
+ appendRequestToBuffer(buffers.requests, buffers.requestBufferAccounting, request)
4665
+ );
4666
+ }
4667
+ function refreshCapturedRequest(buffers, request) {
4668
+ recordDroppedRequests(
4669
+ buffers,
4670
+ refreshRequestInBuffer(buffers.requests, buffers.requestBufferAccounting, request)
4671
+ );
4672
+ }
4673
+ function appendCapturedLog(buffers, log2) {
4674
+ buffers.logs.push({ ...log2, captureSequence: nextCaptureSequence(buffers.capture) });
4675
+ if (buffers.logs.length > MAX_LOGS) {
4676
+ const removed = buffers.logs.shift();
4677
+ if (removed) buffers.capture.droppedLogsThroughSequence = removed.captureSequence;
4678
+ }
4679
+ }
4680
+ async function setupBufferListeners(client2, buffers) {
4426
4681
  client2.on("Network.requestWillBeSent", (params) => {
4427
4682
  const url2 = params.request?.url ?? "";
4428
- const ts = params.timestamp ?? Date.now() / 1e3;
4683
+ if (!url2.startsWith("http")) return;
4684
+ if (isTrackingUrl(url2)) {
4685
+ const sequence = ++buffers.requestCounter;
4686
+ const captured = appendTrackingEvents(buffers, url2, params.request?.postData, sequence);
4687
+ if (!captured && params.request?.hasPostData && !params.request?.postData) {
4688
+ client2.sendCommand("Network.getRequestPostData", { requestId: params.requestId }, 1e4).then((result) => appendTrackingEvents(buffers, url2, result?.postData, sequence)).catch(() => {
4689
+ });
4690
+ }
4691
+ return;
4692
+ }
4693
+ const resourceType = String(params.type ?? "").toLowerCase();
4694
+ const mtopApiName = extractMtopApiName(url2, params.request?.postData);
4695
+ const isMtop = mtopApiName !== null;
4696
+ if (!isMtop && !COLLECTED_RESOURCE_TYPES.has(resourceType)) return;
4697
+ const ts = params.timestamp;
4698
+ const timestampMs2 = params.wallTime !== void 0 ? Math.floor(params.wallTime * 1e3) : Date.now();
4699
+ const redirected = buffers.pending.get(params.requestId);
4700
+ if (redirected && params.redirectResponse) {
4701
+ redirected.status = params.redirectResponse.status ?? 0;
4702
+ redirected.statusText = params.redirectResponse.statusText ?? "";
4703
+ redirected.duration = ts !== void 0 && redirected.startTs !== void 0 ? Math.round((ts - redirected.startTs) * 1e3) : void 0;
4704
+ redirected.isPending = false;
4705
+ refreshCapturedRequest(buffers, redirected);
4706
+ buffers.pending.delete(params.requestId);
4707
+ }
4429
4708
  const id = ++buffers.requestCounter;
4709
+ const captureSequence = nextCaptureSequence(buffers.capture);
4430
4710
  const partial2 = {
4431
4711
  id,
4432
4712
  requestId: params.requestId,
4433
- timestamp: new Date(ts * 1e3).toISOString(),
4434
- timestampMs: Math.floor(ts * 1e3),
4713
+ timestamp: new Date(timestampMs2).toISOString(),
4714
+ timestampMs: timestampMs2,
4435
4715
  method: params.request?.method ?? "GET",
4436
4716
  url: url2,
4437
- apiName: extractApiName(url2),
4717
+ apiName: mtopApiName ?? requestDisplayName(url2),
4438
4718
  headers: headersObjToArray(params.request?.headers ?? {}),
4439
4719
  postData: parsePostData(params.request?.postData),
4440
4720
  queryString: parseQueryString(url2),
@@ -4445,16 +4725,14 @@ function setupBufferListeners(client2, buffers) {
4445
4725
  isProxy: false,
4446
4726
  proxyRequestUrl: null,
4447
4727
  proxyData: null,
4448
- startTs: ts
4728
+ startTs: ts,
4729
+ captureSequence,
4730
+ isPending: true,
4731
+ source: isMtop ? "mtop" : "requests",
4732
+ resourceType
4449
4733
  };
4450
4734
  buffers.pending.set(params.requestId, partial2);
4451
- const captured = appendTrackingEvents(buffers, url2, params.request?.postData, id);
4452
- if (!captured && params.request?.hasPostData && !params.request?.postData) {
4453
- client2.sendCommand("Network.getRequestPostData", { requestId: params.requestId }, 1e4).then((result) => {
4454
- appendTrackingEvents(buffers, url2, result?.postData, id);
4455
- }).catch(() => {
4456
- });
4457
- }
4735
+ appendCapturedRequest(buffers, partial2);
4458
4736
  });
4459
4737
  client2.on("Network.responseReceived", (params) => {
4460
4738
  const partial2 = buffers.pending.get(params.requestId);
@@ -4462,73 +4740,110 @@ function setupBufferListeners(client2, buffers) {
4462
4740
  partial2.status = params.response?.status ?? 0;
4463
4741
  partial2.statusText = params.response?.statusText ?? "";
4464
4742
  partial2.responseHeaders = headersObjToArray(params.response?.headers ?? {});
4743
+ refreshCapturedRequest(buffers, partial2);
4465
4744
  });
4466
4745
  client2.on("Network.loadingFinished", (params) => {
4467
4746
  const partial2 = buffers.pending.get(params.requestId);
4468
4747
  if (!partial2) return;
4469
4748
  buffers.pending.delete(params.requestId);
4470
- const endTs = params.timestamp ?? Date.now() / 1e3;
4471
- const duration3 = Math.round((endTs - partial2.startTs) * 1e3);
4472
- const req = {
4473
- ...partial2,
4474
- duration: duration3,
4475
- isPending: false
4476
- };
4749
+ const endTs = params.timestamp;
4750
+ const duration3 = endTs !== void 0 && partial2.startTs !== void 0 ? Math.round((endTs - partial2.startTs) * 1e3) : void 0;
4751
+ partial2.duration = duration3;
4752
+ partial2.isPending = false;
4753
+ refreshCapturedRequest(buffers, partial2);
4477
4754
  client2.sendCommand("Network.getResponseBody", { requestId: params.requestId }, 1e4).then((result) => {
4478
- req.responseBody = result.base64Encoded ? Buffer.from(result.body, "base64").toString("utf-8") : result.body;
4479
- if (typeof req.responseBody === "string") {
4755
+ partial2.responseBody = result.base64Encoded ? Buffer.from(result.body, "base64").toString("utf-8") : result.body;
4756
+ if (typeof partial2.responseBody === "string") {
4480
4757
  try {
4481
- req.responseBody = JSON.parse(req.responseBody);
4758
+ partial2.responseBody = JSON.parse(partial2.responseBody);
4482
4759
  } catch {
4483
4760
  }
4484
4761
  }
4485
- refreshRequestInBuffer(buffers.requests, buffers.requestBufferAccounting, req);
4762
+ refreshCapturedRequest(buffers, partial2);
4486
4763
  }).catch(() => {
4487
4764
  });
4488
- appendRequestToBuffer(buffers.requests, buffers.requestBufferAccounting, req);
4489
4765
  });
4490
4766
  client2.on("Network.loadingFailed", (params) => {
4491
4767
  const partial2 = buffers.pending.get(params.requestId);
4492
4768
  if (!partial2) return;
4493
4769
  buffers.pending.delete(params.requestId);
4494
- const req = {
4495
- ...partial2,
4496
- status: 0,
4497
- statusText: params.errorText ?? "Failed",
4498
- duration: 0,
4499
- isPending: false
4500
- };
4501
- appendRequestToBuffer(buffers.requests, buffers.requestBufferAccounting, req);
4770
+ partial2.status = 0;
4771
+ partial2.statusText = params.errorText ?? "Failed";
4772
+ partial2.duration = params.timestamp !== void 0 && partial2.startTs !== void 0 ? Math.round((params.timestamp - partial2.startTs) * 1e3) : void 0;
4773
+ partial2.isPending = false;
4774
+ refreshCapturedRequest(buffers, partial2);
4502
4775
  });
4503
4776
  client2.on("Runtime.consoleAPICalled", (params) => {
4504
- const level = params.type ?? "log";
4777
+ const levelMap = {
4778
+ warning: "warn",
4779
+ debug: "debug",
4780
+ info: "info",
4781
+ error: "error",
4782
+ assert: "error"
4783
+ };
4784
+ const level = levelMap[params.type] ?? "log";
4505
4785
  const args = (params.args ?? []).map((arg) => {
4506
4786
  if (arg.type === "string") return arg.value;
4507
4787
  if (arg.value !== void 0) return String(arg.value);
4508
4788
  if (arg.description !== void 0) return arg.description;
4509
4789
  return JSON.stringify(arg);
4510
4790
  });
4511
- const entry = {
4791
+ appendCapturedLog(buffers, {
4512
4792
  level,
4513
4793
  message: args.join(" "),
4514
- timestamp: params.timestamp ? Math.floor(params.timestamp * 1e3) : Date.now(),
4515
- source: "console",
4516
- args
4517
- };
4518
- buffers.logs.push(entry);
4519
- if (buffers.logs.length > MAX_LOGS) buffers.logs.shift();
4794
+ timestampMs: params.timestamp ?? Date.now(),
4795
+ source: params.stackTrace?.callFrames?.[0]?.url ? `${params.stackTrace.callFrames[0].url.split("/").pop() || params.stackTrace.callFrames[0].url}:${params.stackTrace.callFrames[0].lineNumber + 1}` : "console",
4796
+ args,
4797
+ origin: "console"
4798
+ });
4799
+ });
4800
+ client2.on("Runtime.exceptionThrown", (params) => {
4801
+ const details = params.exceptionDetails ?? {};
4802
+ const exception = details.exception;
4803
+ const message = exception?.description ?? (exception?.value !== void 0 ? String(exception.value) : void 0) ?? details.text ?? "Uncaught exception";
4804
+ const topFrame = details.stackTrace?.callFrames?.[0];
4805
+ const url2 = topFrame?.url || details.url;
4806
+ const lineNumber = topFrame?.lineNumber ?? details.lineNumber;
4807
+ const source = url2 ? `${url2.split("/").pop() || url2}${lineNumber !== void 0 ? `:${lineNumber + 1}` : ""}` : void 0;
4808
+ appendCapturedLog(buffers, {
4809
+ level: "error",
4810
+ message,
4811
+ timestampMs: params.timestamp ?? Date.now(),
4812
+ ...source ? { source } : {},
4813
+ args: [message],
4814
+ origin: "exception"
4815
+ });
4520
4816
  });
4521
4817
  client2.on("Log.entryAdded", (params) => {
4522
- const entry = {
4523
- level: params.entry?.level ?? "log",
4524
- message: params.entry?.text ?? "",
4525
- timestamp: params.entry?.timestamp ? Math.floor(params.entry.timestamp * 1e3) : Date.now(),
4526
- source: params.entry?.source ?? "other",
4527
- args: [params.entry?.text ?? ""]
4818
+ const entry = params.entry ?? {};
4819
+ const levelMap = {
4820
+ verbose: "log",
4821
+ info: "info",
4822
+ warning: "warn",
4823
+ error: "error"
4528
4824
  };
4529
- buffers.logs.push(entry);
4530
- if (buffers.logs.length > MAX_LOGS) buffers.logs.shift();
4825
+ const args = (entry.args ?? []).map((arg) => {
4826
+ if (arg.value !== void 0) return String(arg.value);
4827
+ return arg.description ?? `[${arg.type || "Object"}]`;
4828
+ });
4829
+ const message = entry.text || args.join(" ");
4830
+ appendCapturedLog(buffers, {
4831
+ level: levelMap[entry.level] ?? entry.level ?? "log",
4832
+ message,
4833
+ timestampMs: entry.timestamp ?? Date.now(),
4834
+ source: entry.url ? `${entry.url.split("/").pop() || entry.url}${entry.lineNumber !== void 0 ? `:${entry.lineNumber + 1}` : ""}` : entry.source ?? "other",
4835
+ args: args.length > 0 ? args : [message],
4836
+ origin: "browser"
4837
+ });
4531
4838
  });
4839
+ await Promise.all([
4840
+ ensureCdpDomain(client2, "Network", {
4841
+ maxTotalBufferSize: 10 * 1024 * 1024,
4842
+ maxPostDataSize: 5 * 1024 * 1024
4843
+ }),
4844
+ ensureCdpDomain(client2, "Runtime"),
4845
+ ensureCdpDomain(client2, "Log")
4846
+ ]);
4532
4847
  }
4533
4848
 
4534
4849
  // src/cdp-backend/discover.ts
@@ -4687,9 +5002,21 @@ var CdpSession = class {
4687
5002
  } catch {
4688
5003
  client2 = await CdpClient.connect(target.webSocketDebuggerUrl);
4689
5004
  }
4690
- const buffers = createBuffers();
4691
- setupBufferListeners(client2, buffers);
5005
+ let tabId;
5006
+ for (const [virtualId, registeredTargetId] of this.tabIdMap) {
5007
+ if (registeredTargetId === target.id) {
5008
+ tabId = virtualId;
5009
+ break;
5010
+ }
5011
+ }
5012
+ if (tabId === void 0) {
5013
+ tabId = this.nextTabId++;
5014
+ this.tabIdMap.set(tabId, target.id);
5015
+ }
5016
+ const buffers = createBuffers(tabId);
5017
+ await setupBufferListeners(client2, buffers);
4692
5018
  const session = {
5019
+ tabId,
4693
5020
  client: client2,
4694
5021
  buffers,
4695
5022
  targetId: target.id,
@@ -5945,6 +6272,143 @@ async function enrichCdpPageChangeSetWithRefs(session, observation) {
5945
6272
  );
5946
6273
  }
5947
6274
 
6275
+ // src/cdp-backend/request-handlers.ts
6276
+ function filterRequests(buffers, opts) {
6277
+ const pattern = opts.filter ? compileTextPattern(opts.filter) : void 0;
6278
+ return queryCapturedEntries(
6279
+ buffers.requests.filter(
6280
+ (request) => Number.isSafeInteger(request.captureSequence)
6281
+ ),
6282
+ buffers.capture,
6283
+ { after: opts.after, since: opts.since, limit: opts.count ?? 10 },
6284
+ (request) => (!opts.source || opts.source === "all" || request.source === opts.source) && (!pattern || matchesTextPattern(request.url, pattern) || matchesTextPattern(request.apiName, pattern)),
6285
+ buffers.capture.droppedRequestsThroughSequence,
6286
+ "requests"
6287
+ );
6288
+ }
6289
+ async function cdpGetRequests(session, data) {
6290
+ const opts = {
6291
+ count: data.count ?? 10,
6292
+ filter: data.filter,
6293
+ since: data.since,
6294
+ after: data.after,
6295
+ source: data.source,
6296
+ applyHiddenFilter: data.applyHiddenFilter !== false
6297
+ };
6298
+ const captured = filterRequests(session.buffers, opts);
6299
+ const serializeOpts = {
6300
+ includeBody: data.includeBody !== false,
6301
+ includeHeaders: data.includeHeaders === true,
6302
+ maxBodySize: data.maxBodySize ?? 5e4,
6303
+ fields: data.fields,
6304
+ keysOnly: data.keysOnly === true
6305
+ };
6306
+ return {
6307
+ meta: {
6308
+ count: captured.entries.length,
6309
+ totalMatched: captured.totalMatched,
6310
+ totalInBuffer: session.buffers.requests.length,
6311
+ filter: data.filter,
6312
+ since: data.since,
6313
+ after: data.after,
6314
+ nextCursor: captured.nextCursor,
6315
+ hasMore: captured.hasMore,
6316
+ gap: captured.gap,
6317
+ source: "cdp"
6318
+ },
6319
+ requests: captured.entries.map((r) => serializeRequest(r, serializeOpts))
6320
+ };
6321
+ }
6322
+ async function cdpGetLogs(session, data) {
6323
+ const { level, since, after, limit = 100, filter } = data;
6324
+ const pattern = filter ? compileTextPattern(filter) : void 0;
6325
+ const captured = queryCapturedEntries(
6326
+ session.buffers.logs,
6327
+ session.buffers.capture,
6328
+ { after, since, limit },
6329
+ (log2) => (!level || level === "all" || log2.level === level) && (!pattern || matchesTextPattern(log2.message, pattern) || log2.args.some((arg) => matchesTextPattern(arg, pattern))),
6330
+ session.buffers.capture.droppedLogsThroughSequence,
6331
+ "logs"
6332
+ );
6333
+ const captureStatus = session.buffers.logs.length === 0 ? "armed" : "active";
6334
+ return {
6335
+ meta: {
6336
+ count: captured.entries.length,
6337
+ totalMatched: captured.totalMatched,
6338
+ totalInBuffer: session.buffers.logs.length,
6339
+ level: level || "all",
6340
+ since,
6341
+ after,
6342
+ filter,
6343
+ nextCursor: captured.nextCursor,
6344
+ hasMore: captured.hasMore,
6345
+ gap: captured.gap,
6346
+ captureStatus,
6347
+ ...captureStatus === "armed" && {
6348
+ hint: "Log capture is active, but this tab has no captured history yet. Reload the current tab or repeat the triggering action, then call get_logs again."
6349
+ },
6350
+ source: "cdp"
6351
+ },
6352
+ logs: captured.entries
6353
+ };
6354
+ }
6355
+ async function cdpCaptureCheckpoint(session, _data) {
6356
+ return {
6357
+ tabId: session.tabId,
6358
+ cursor: captureCheckpoint(session.buffers.capture),
6359
+ timestampMs: Date.now(),
6360
+ captureStatus: session.buffers.capture.currentSequence === 0 ? "armed" : "active"
6361
+ };
6362
+ }
6363
+ async function collectCdpPageDiagnostics(session, after, diagnostics) {
6364
+ const result = {};
6365
+ if (diagnostics.logs) {
6366
+ const options = diagnostics.logs === true ? { level: "error", limit: 100 } : { level: "error", limit: 100, ...diagnostics.logs };
6367
+ result.logs = await cdpGetLogs(session, { ...options, after });
6368
+ }
6369
+ if (diagnostics.requests) {
6370
+ const options = diagnostics.requests === true ? { source: "all", includeBody: false, count: 100 } : {
6371
+ source: "all",
6372
+ includeBody: false,
6373
+ count: 100,
6374
+ ...diagnostics.requests
6375
+ };
6376
+ result.requests = await cdpGetRequests(session, { ...options, after });
6377
+ }
6378
+ return result;
6379
+ }
6380
+ async function cdpGetEvents(session, data) {
6381
+ const { source = "all", event_type, since, limit = 20, filter, includeRaw = false } = data;
6382
+ const response = queryTrackingEvents(session.buffers.events, {
6383
+ source,
6384
+ event_type,
6385
+ since,
6386
+ limit,
6387
+ filter
6388
+ });
6389
+ const captureStatus = response.totalInBuffer === 0 ? "armed" : "active";
6390
+ const hint = response.events.length === 0 && captureStatus === "armed" ? TRACKING_CAPTURE_ARMED_HINT : void 0;
6391
+ return {
6392
+ meta: {
6393
+ count: response.events.length,
6394
+ totalMatched: response.totalMatched,
6395
+ totalInBuffer: response.totalInBuffer,
6396
+ source,
6397
+ captureStatus,
6398
+ captureSource: "cdp",
6399
+ includeRaw,
6400
+ hint
6401
+ },
6402
+ events: response.events.map((event) => formatTrackingEvent(event, includeRaw))
6403
+ };
6404
+ }
6405
+ registerActions([
6406
+ { name: "capture_checkpoint", session: "page", handler: cdpCaptureCheckpoint },
6407
+ { name: "get_requests", session: "page", handler: cdpGetRequests },
6408
+ { name: "get_logs", session: "page", handler: cdpGetLogs },
6409
+ { name: "get_events", session: "page", handler: cdpGetEvents }
6410
+ ]);
6411
+
5948
6412
  // src/cdp-backend/composite-handlers.ts
5949
6413
  async function cdpRunActions(session, data, controlSource = "local") {
5950
6414
  const { actions, tabId } = data;
@@ -6020,21 +6484,42 @@ async function cdpRunActions(session, data, controlSource = "local") {
6020
6484
  };
6021
6485
  }
6022
6486
  async function cdpObservedPageAction(session, action, data, controlSource = "local") {
6023
- const { actionData, observe } = splitObservedActionPayload(data);
6024
- if (observe === false) {
6025
- return dispatchCdpAction(session, action, actionData, controlSource);
6026
- }
6487
+ const { actionData, observe, diagnostics } = splitObservedActionPayload(action, data);
6027
6488
  const { tabId } = actionData;
6028
6489
  if (typeof tabId !== "number") throw new Error(`${action} requires a numeric tabId`);
6029
6490
  const pageSession = await session.getPageSession(tabId);
6030
6491
  await ensureControlledIndicator(pageSession, controlSource);
6031
- return runPageAct(
6492
+ if (observe === false) {
6493
+ if (!diagnostics) return dispatchCdpAction(session, action, actionData, controlSource);
6494
+ const cursor = captureCheckpoint(pageSession.buffers.capture);
6495
+ let actionResult;
6496
+ let actionError;
6497
+ try {
6498
+ actionResult = await dispatchCdpAction(session, action, actionData, controlSource);
6499
+ } catch (error48) {
6500
+ actionError = error48 instanceof Error ? error48.message : String(error48);
6501
+ }
6502
+ return {
6503
+ action: {
6504
+ name: action,
6505
+ success: actionError === void 0,
6506
+ ...actionResult !== void 0 ? { data: actionResult } : {},
6507
+ ...actionError ? { error: actionError } : {}
6508
+ },
6509
+ diagnostics: await collectCdpPageDiagnostics(pageSession, cursor, diagnostics)
6510
+ };
6511
+ }
6512
+ let diagnosticsCursor;
6513
+ const result = await runPageAct(
6032
6514
  { action, actionData, observe },
6033
6515
  {
6034
6516
  startObservation: () => startPageObservation(pageSession),
6035
6517
  finishObservation: (checkpoint, options) => finishPageObservation(pageSession, checkpoint, options),
6036
6518
  startNavigationObserver: (options) => startCdpNavigationObserver(session, pageSession, options.urlBefore, options),
6037
- executeAction: (name, params) => dispatchCdpAction(session, name, { ...params, tabId }, controlSource),
6519
+ executeAction: (name, params) => {
6520
+ if (diagnostics) diagnosticsCursor = captureCheckpoint(pageSession.buffers.capture);
6521
+ return dispatchCdpAction(session, name, { ...params, tabId }, controlSource);
6522
+ },
6038
6523
  finalizeObservation: (observation) => {
6039
6524
  if (observation.mode === "delta") {
6040
6525
  return enrichCdpPageChangeSetWithRefs(pageSession, observation);
@@ -6044,6 +6529,11 @@ async function cdpObservedPageAction(session, action, data, controlSource = "loc
6044
6529
  disposeObservation: () => disposePageObservation(pageSession)
6045
6530
  }
6046
6531
  );
6532
+ if (!diagnostics || !diagnosticsCursor) return result;
6533
+ return {
6534
+ ...result,
6535
+ diagnostics: await collectCdpPageDiagnostics(pageSession, diagnosticsCursor, diagnostics)
6536
+ };
6047
6537
  }
6048
6538
  registerActions([{ name: "run_actions", session: "browser", handler: cdpRunActions }]);
6049
6539
 
@@ -6631,112 +7121,6 @@ function listCloudConnectors() {
6631
7121
  return { connectors: pushIfChanged() };
6632
7122
  }
6633
7123
 
6634
- // src/cdp-backend/request-handlers.ts
6635
- function filterRequests(buffers, opts) {
6636
- let requests = [...buffers.requests];
6637
- if (opts.since !== void 0) {
6638
- requests = requests.filter((r) => r.timestampMs >= opts.since);
6639
- }
6640
- if (opts.filter) {
6641
- const pattern = compileTextPattern(opts.filter);
6642
- requests = requests.filter(
6643
- (r) => matchesTextPattern(r.url, pattern) || matchesTextPattern(r.apiName, pattern)
6644
- );
6645
- }
6646
- if (opts.source === "mtop") {
6647
- requests = requests.filter((r) => r.url?.includes("/h5/") || r.url?.includes("mtop"));
6648
- }
6649
- const count = opts.count ?? 10;
6650
- return {
6651
- requests: requests.slice(-count),
6652
- totalMatched: requests.length
6653
- };
6654
- }
6655
- async function cdpGetRequests(session, data) {
6656
- const opts = {
6657
- count: data.count ?? 10,
6658
- filter: data.filter,
6659
- since: data.since,
6660
- source: data.source,
6661
- applyHiddenFilter: data.applyHiddenFilter !== false
6662
- };
6663
- const { requests, totalMatched } = filterRequests(session.buffers, opts);
6664
- const serializeOpts = {
6665
- includeBody: data.includeBody !== false,
6666
- includeHeaders: data.includeHeaders === true,
6667
- maxBodySize: data.maxBodySize ?? 5e4,
6668
- fields: data.fields,
6669
- keysOnly: data.keysOnly === true
6670
- };
6671
- return {
6672
- meta: {
6673
- count: requests.length,
6674
- totalMatched,
6675
- totalInBuffer: session.buffers.requests.length,
6676
- filter: data.filter,
6677
- since: data.since,
6678
- source: "cdp"
6679
- },
6680
- requests: requests.map((r) => serializeRequest(r, serializeOpts))
6681
- };
6682
- }
6683
- async function cdpGetLogs(session, data) {
6684
- const { level, since, limit = 100, filter } = data;
6685
- let logs = [...session.buffers.logs];
6686
- if (since !== void 0) {
6687
- logs = logs.filter((l) => l.timestamp >= since);
6688
- }
6689
- if (level && level !== "all") {
6690
- logs = logs.filter((l) => l.level === level);
6691
- }
6692
- if (filter) {
6693
- const f = filter.toLowerCase();
6694
- logs = logs.filter((l) => l.message?.toLowerCase().includes(f));
6695
- }
6696
- logs = logs.slice(-limit);
6697
- return {
6698
- meta: {
6699
- count: logs.length,
6700
- totalMatched: logs.length,
6701
- level: level || "all",
6702
- since,
6703
- filter,
6704
- source: "cdp"
6705
- },
6706
- logs
6707
- };
6708
- }
6709
- async function cdpGetEvents(session, data) {
6710
- const { source = "all", event_type, since, limit = 20, filter, includeRaw = false } = data;
6711
- const response = queryTrackingEvents(session.buffers.events, {
6712
- source,
6713
- event_type,
6714
- since,
6715
- limit,
6716
- filter
6717
- });
6718
- const captureStatus = response.totalInBuffer === 0 ? "armed" : "active";
6719
- const hint = response.events.length === 0 && captureStatus === "armed" ? TRACKING_CAPTURE_ARMED_HINT : void 0;
6720
- return {
6721
- meta: {
6722
- count: response.events.length,
6723
- totalMatched: response.totalMatched,
6724
- totalInBuffer: response.totalInBuffer,
6725
- source,
6726
- captureStatus,
6727
- captureSource: "cdp",
6728
- includeRaw,
6729
- hint
6730
- },
6731
- events: response.events.map((event) => formatTrackingEvent(event, includeRaw))
6732
- };
6733
- }
6734
- registerActions([
6735
- { name: "get_requests", session: "page", handler: cdpGetRequests },
6736
- { name: "get_logs", session: "page", handler: cdpGetLogs },
6737
- { name: "get_events", session: "page", handler: cdpGetEvents }
6738
- ]);
6739
-
6740
7124
  // src/cdp-backend/send-request.ts
6741
7125
  import { readFile } from "node:fs/promises";
6742
7126
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -6746,6 +7130,10 @@ function setHeader(headers, name, value) {
6746
7130
  if (existing && existing !== name) delete headers[existing];
6747
7131
  headers[name] = value;
6748
7132
  }
7133
+ function removeHeader(headers, name) {
7134
+ const existing = findSendRequestHeaderName(headers, name);
7135
+ if (existing) delete headers[existing];
7136
+ }
6749
7137
  function escapeMultipartQuotedValue(value) {
6750
7138
  return value.replace(/\\/g, "\\\\").replace(/"/g, "%22");
6751
7139
  }
@@ -6854,10 +7242,19 @@ async function executeNodeSendRequest(data, cookieMap = /* @__PURE__ */ new Map(
6854
7242
  Accept: "application/json, text/plain, */*",
6855
7243
  "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
6856
7244
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
6857
- Referer: targetUrl.origin + "/mearl",
6858
- Origin: targetUrl.origin,
6859
7245
  ...headers
6860
7246
  };
7247
+ const resolvedSourceHeaders = getSendRequestSourceHeaders(targetUrl, headers);
7248
+ if (resolvedSourceHeaders.referer) {
7249
+ setHeader(requestHeaders, "Referer", resolvedSourceHeaders.referer);
7250
+ } else {
7251
+ removeHeader(requestHeaders, "Referer");
7252
+ }
7253
+ if (resolvedSourceHeaders.origin) {
7254
+ setHeader(requestHeaders, "Origin", resolvedSourceHeaders.origin);
7255
+ } else {
7256
+ removeHeader(requestHeaders, "Origin");
7257
+ }
6861
7258
  const cookieHeader = [...cookieMap.entries()].map(([key, value]) => `${key}=${value}`).join("; ");
6862
7259
  if (cookieHeader) setHeader(requestHeaders, "Cookie", cookieHeader);
6863
7260
  const xsrfToken = cookieMap.get("XSRF-TOKEN");
@@ -7215,8 +7612,8 @@ registerActions([
7215
7612
  ]);
7216
7613
 
7217
7614
  // src/cdp-backend/tdbank-handlers.ts
7218
- async function cdpFetchTdbank(session, path12, options = {}) {
7219
- const url2 = new URL(`https://tdbank.alibaba-inc.com${path12}`);
7615
+ async function cdpFetchTdbank(session, path13, options = {}) {
7616
+ const url2 = new URL(`https://tdbank.alibaba-inc.com${path13}`);
7220
7617
  for (const [key, value] of Object.entries(options.params ?? {})) {
7221
7618
  if (value !== void 0 && value !== null) {
7222
7619
  url2.searchParams.set(key, String(value));
@@ -7484,6 +7881,14 @@ async function cdpTdbankAccount(cdpSession2, data) {
7484
7881
  registerActions([{ name: "tdbank_account", session: "browser", handler: cdpTdbankAccount }]);
7485
7882
 
7486
7883
  // src/cdp-backend/tab-handlers.ts
7884
+ function currentCapture(pageSession) {
7885
+ return {
7886
+ tabId: pageSession.tabId,
7887
+ cursor: captureCheckpoint(pageSession.buffers.capture),
7888
+ timestampMs: Date.now(),
7889
+ captureStatus: pageSession.buffers.capture.currentSequence === 0 ? "armed" : "active"
7890
+ };
7891
+ }
7487
7892
  async function cdpTabOpen(session, data, controlSource = "local") {
7488
7893
  const { url: url2, active = false, emulation, reuse = "never", match } = data;
7489
7894
  if (!url2 || typeof url2 !== "string") {
@@ -7510,12 +7915,14 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7510
7915
  const tabId2 = await session.ensureTargetRegistered(existing.targetId);
7511
7916
  const pageSession2 = await session.getPageSession(tabId2);
7512
7917
  await ensureControlledIndicator(pageSession2, controlSource);
7918
+ const capture2 = currentCapture(pageSession2);
7513
7919
  return {
7514
7920
  tabId: tabId2,
7515
7921
  url: existing.url ?? url2,
7516
7922
  title: existing.title ?? "",
7517
7923
  opened: false,
7518
- reused: true
7924
+ reused: true,
7925
+ capture: capture2
7519
7926
  };
7520
7927
  }
7521
7928
  if (reuse === "require") {
@@ -7530,6 +7937,7 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7530
7937
  const tabId = await session.registerTarget(targetId);
7531
7938
  const pageSession = await session.getPageSession(tabId);
7532
7939
  await ensureControlledIndicator(pageSession, controlSource);
7940
+ const capture = currentCapture(pageSession);
7533
7941
  if (emulationConfig) {
7534
7942
  for (const { method, params } of buildEnableEmulationCommands(emulationConfig)) {
7535
7943
  await pageSession.client.sendCommand(method, params);
@@ -7548,6 +7956,7 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7548
7956
  title: targetInfo?.title ?? "",
7549
7957
  opened: true,
7550
7958
  reused: false,
7959
+ capture,
7551
7960
  ...emulationConfig && {
7552
7961
  emulation: {
7553
7962
  preset: emulationConfig.presetName,
@@ -8506,12 +8915,9 @@ async function getViewportBackendNodeIds(session) {
8506
8915
  includePaintOrder: false
8507
8916
  })
8508
8917
  ]);
8509
- const viewport = metrics.cssVisualViewport ?? metrics.visualViewport;
8510
- const left = viewport?.pageX ?? 0;
8511
- const top = viewport?.pageY ?? 0;
8512
- const width = viewport?.clientWidth;
8513
- const height = viewport?.clientHeight;
8514
- if (!width || !height) throw new Error("page_snapshot: viewport metrics are unavailable");
8918
+ const viewport = resolveDOMSnapshotViewportBounds(metrics);
8919
+ if (!viewport) throw new Error("page_snapshot: viewport metrics are unavailable");
8920
+ const { left, top, width, height } = viewport;
8515
8921
  const right = left + width;
8516
8922
  const bottom = top + height;
8517
8923
  const visible = /* @__PURE__ */ new Set();
@@ -9346,7 +9752,7 @@ import os2 from "node:os";
9346
9752
  import { randomUUID as randomUUID4 } from "node:crypto";
9347
9753
 
9348
9754
  // src/version.ts
9349
- var NATIVE_HOST_VERSION = true ? "2.2.1" : "unknown";
9755
+ var NATIVE_HOST_VERSION = true ? "2.3.0" : "unknown";
9350
9756
 
9351
9757
  // src/analytics.ts
9352
9758
  var MEASUREMENT_ID = "G-W84LJDZ6PH";
@@ -10207,17 +10613,51 @@ var AgentChatManager = class {
10207
10613
 
10208
10614
  // src/agent/codex-provider.ts
10209
10615
  import os3 from "node:os";
10210
- import path7 from "node:path";
10211
10616
  import readline from "node:readline";
10212
- import { spawn as spawn5 } from "node:child_process";
10617
+ import { spawn as spawn6 } from "node:child_process";
10213
10618
 
10214
10619
  // src/agent/executable-resolver.ts
10215
- import fs5 from "node:fs";
10216
- import { spawn as spawn4 } from "node:child_process";
10620
+ import fs6 from "node:fs";
10621
+ import { spawn as spawn5 } from "node:child_process";
10622
+ import path7 from "node:path";
10217
10623
 
10218
- // src/agent/process-env.ts
10624
+ // ../setup/dist/process.js
10625
+ import fs5 from "node:fs";
10219
10626
  import path6 from "node:path";
10220
- function agentSpawnCommand(executable, args, platform = process.platform, commandInterpreter = process.env.ComSpec) {
10627
+ import { spawn as spawn4 } from "node:child_process";
10628
+ function isExecutable(filePath, platform = process.platform) {
10629
+ try {
10630
+ fs5.accessSync(filePath, platform === "win32" ? fs5.constants.F_OK : fs5.constants.X_OK);
10631
+ return true;
10632
+ } catch {
10633
+ return false;
10634
+ }
10635
+ }
10636
+ function executableNames(name, platform = process.platform) {
10637
+ return platform === "win32" ? [`${name}.cmd`, `${name}.exe`, name] : [name];
10638
+ }
10639
+ function versionManagerExecutableCandidates(home, names, options = {}) {
10640
+ const env = options.env ?? process.env;
10641
+ const platform = options.platform ?? process.platform;
10642
+ const readdirSync2 = options.readdirSync ?? fs5.readdirSync;
10643
+ const fnmDefaultRoot = env.XDG_DATA_HOME ? path6.join(env.XDG_DATA_HOME, "fnm") : platform === "darwin" ? path6.join(home, "Library", "Application Support", "fnm") : platform === "win32" && env.APPDATA ? path6.join(env.APPDATA, "fnm") : path6.join(home, ".local", "share", "fnm");
10644
+ const roots = [
10645
+ path6.join(env.NVM_DIR || path6.join(home, ".nvm"), "versions", "node"),
10646
+ path6.join(env.FNM_DIR || fnmDefaultRoot, "node-versions"),
10647
+ path6.join(home, ".fnm", "node-versions")
10648
+ ].filter((root, index, all) => all.indexOf(root) === index);
10649
+ return roots.flatMap((root) => {
10650
+ try {
10651
+ return readdirSync2(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((left, right) => right.name.localeCompare(left.name, void 0, { numeric: true })).flatMap((entry) => names.flatMap((name) => [
10652
+ path6.join(root, entry.name, "bin", name),
10653
+ path6.join(root, entry.name, "installation", "bin", name)
10654
+ ]));
10655
+ } catch {
10656
+ return [];
10657
+ }
10658
+ });
10659
+ }
10660
+ function resolveSpawnCommand(executable, args, platform = process.platform, commandInterpreter = process.env.ComSpec) {
10221
10661
  if (platform === "win32" && /\.(?:cmd|bat)$/i.test(executable)) {
10222
10662
  return {
10223
10663
  command: commandInterpreter || "cmd.exe",
@@ -10226,16 +10666,64 @@ function agentSpawnCommand(executable, args, platform = process.platform, comman
10226
10666
  }
10227
10667
  return { command: executable, args };
10228
10668
  }
10229
- function createAgentChildEnv(sourceEnv = process.env, nodeExecutable = process.execPath) {
10230
- const pathKey = Object.keys(sourceEnv).find((key) => key.toLowerCase() === "path") || "PATH";
10231
- const nodeDirectory = path6.dirname(nodeExecutable);
10232
- const inheritedPath = sourceEnv[pathKey] || "";
10233
- const pathEntries = inheritedPath.split(path6.delimiter).filter(Boolean);
10234
- const nextPath = pathEntries.includes(nodeDirectory) ? inheritedPath : [nodeDirectory, ...pathEntries].join(path6.delimiter);
10235
- return { ...sourceEnv, [pathKey]: nextPath };
10669
+ function prependCommandPath(command, env = process.env) {
10670
+ if (!path6.isAbsolute(command))
10671
+ return env;
10672
+ return prependPath(path6.dirname(command), env);
10673
+ }
10674
+ function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
10675
+ return prependPath(path6.dirname(nodeExecutable), env);
10676
+ }
10677
+ function prependPath(directory, env) {
10678
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10679
+ const inherited = env[pathKey] || "";
10680
+ const entries = inherited.split(path6.delimiter).filter(Boolean);
10681
+ if (entries.includes(directory))
10682
+ return env;
10683
+ return { ...env, [pathKey]: [directory, ...entries].join(path6.delimiter) };
10236
10684
  }
10237
10685
 
10238
10686
  // src/agent/executable-resolver.ts
10687
+ function agentExecutableCandidatePaths({
10688
+ executableNames: executableNames2,
10689
+ configuredPath,
10690
+ env = process.env,
10691
+ extraUserCandidates = [],
10692
+ home,
10693
+ platform = process.platform,
10694
+ processExecPath = process.execPath,
10695
+ readdirSync: readdirSync2
10696
+ }) {
10697
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10698
+ const pathCandidates = (env[pathKey] || "").split(path7.delimiter).filter(Boolean).flatMap((directory) => executableNames2.map((name) => path7.join(directory, name)));
10699
+ const commonUserDirectories = [
10700
+ path7.join(home, ".local", "bin"),
10701
+ path7.join(home, ".npm-global", "bin"),
10702
+ path7.join(home, ".volta", "bin"),
10703
+ path7.join(home, ".local", "share", "pnpm"),
10704
+ path7.join(home, "Library", "pnpm"),
10705
+ path7.join(home, ".bun", "bin"),
10706
+ ...platform === "win32" && env.APPDATA ? [path7.join(env.APPDATA, "npm")] : []
10707
+ ];
10708
+ const systemDirectories = platform === "win32" ? [] : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin"];
10709
+ return [
10710
+ configuredPath,
10711
+ ...executableNames2.map((name) => path7.join(path7.dirname(processExecPath), name)),
10712
+ ...commonUserDirectories.flatMap(
10713
+ (directory) => executableNames2.map((name) => path7.join(directory, name))
10714
+ ),
10715
+ ...extraUserCandidates,
10716
+ ...versionManagerExecutableCandidates(home, executableNames2, {
10717
+ env,
10718
+ platform,
10719
+ readdirSync: readdirSync2
10720
+ }),
10721
+ ...systemDirectories.flatMap(
10722
+ (directory) => executableNames2.map((name) => path7.join(directory, name))
10723
+ ),
10724
+ ...pathCandidates
10725
+ ].filter((candidate) => typeof candidate === "string");
10726
+ }
10239
10727
  var AgentExecutableResolver = class {
10240
10728
  constructor(options) {
10241
10729
  this.options = options;
@@ -10306,11 +10794,11 @@ function probeAgentExecutable(executable, options) {
10306
10794
  return new Promise((resolve) => {
10307
10795
  let settled = false;
10308
10796
  let stderr = "";
10309
- const launch = agentSpawnCommand(executable, options.args);
10310
- const child = spawn4(launch.command, launch.args, {
10797
+ const launch = resolveSpawnCommand(executable, options.args);
10798
+ const child = spawn5(launch.command, launch.args, {
10311
10799
  stdio: ["ignore", "ignore", "pipe"],
10312
10800
  windowsHide: true,
10313
- env: createAgentChildEnv()
10801
+ env: prependNodePath()
10314
10802
  });
10315
10803
  const finish = (error48) => {
10316
10804
  if (settled) return;
@@ -10337,8 +10825,8 @@ function probeAgentExecutable(executable, options) {
10337
10825
  }
10338
10826
  function isExecutableFile(candidate) {
10339
10827
  try {
10340
- fs5.accessSync(candidate, process.platform === "win32" ? fs5.constants.F_OK : fs5.constants.X_OK);
10341
- return fs5.statSync(candidate).isFile();
10828
+ fs6.accessSync(candidate, process.platform === "win32" ? fs6.constants.F_OK : fs6.constants.X_OK);
10829
+ return fs6.statSync(candidate).isFile();
10342
10830
  } catch {
10343
10831
  return false;
10344
10832
  }
@@ -10349,6 +10837,21 @@ var REQUEST_TIMEOUT_MS = 3e4;
10349
10837
  var PROBE_TIMEOUT_MS = 5e3;
10350
10838
  var MAX_MODEL_CHARS = 256;
10351
10839
  var CODEX_NOT_FOUND_ERROR = "Codex CLI was not found. Install @openai/codex or set MEARL_CODEX_PATH.";
10840
+ function codexExecutableCandidatePaths(options = {}) {
10841
+ const env = options.env ?? process.env;
10842
+ const home = (options.homedir ?? os3.homedir)();
10843
+ const platform = options.platform ?? process.platform;
10844
+ const executableName = platform === "win32" ? "codex.cmd" : "codex";
10845
+ return agentExecutableCandidatePaths({
10846
+ executableNames: [executableName],
10847
+ configuredPath: env.MEARL_CODEX_PATH,
10848
+ env,
10849
+ home,
10850
+ platform,
10851
+ processExecPath: options.processExecPath,
10852
+ readdirSync: options.readdirSync
10853
+ });
10854
+ }
10352
10855
  function codexModelInfo(value) {
10353
10856
  if (typeof value !== "string") return void 0;
10354
10857
  const id = value.trim().slice(0, MAX_MODEL_CHARS);
@@ -10433,7 +10936,7 @@ function externalOpenCommand(url2, platform = process.platform) {
10433
10936
  function openExternalUrl(url2) {
10434
10937
  const { command, args } = externalOpenCommand(url2);
10435
10938
  return new Promise((resolve, reject) => {
10436
- const child = spawn5(command, args, { stdio: "ignore", windowsHide: true });
10939
+ const child = spawn6(command, args, { stdio: "ignore", windowsHide: true });
10437
10940
  child.once("error", (error48) => {
10438
10941
  reject(new Error(`Unable to open Codex App: ${error48.message}`));
10439
10942
  });
@@ -10664,10 +11167,10 @@ var CodexAgentProvider = class {
10664
11167
  throw new Error(resolution.error);
10665
11168
  }
10666
11169
  const { executable } = resolution;
10667
- const launch = agentSpawnCommand(executable, ["app-server", "--listen", "stdio://"]);
10668
- const childEnv = createAgentChildEnv();
11170
+ const launch = resolveSpawnCommand(executable, ["app-server", "--listen", "stdio://"]);
11171
+ const childEnv = prependNodePath();
10669
11172
  if (this.mearlSocketPath) childEnv.MEARL_SOCKET_PATH = this.mearlSocketPath;
10670
- const child = spawn5(launch.command, launch.args, {
11173
+ const child = spawn6(launch.command, launch.args, {
10671
11174
  stdio: ["pipe", "pipe", "pipe"],
10672
11175
  windowsHide: true,
10673
11176
  env: childEnv
@@ -10830,26 +11333,15 @@ var CodexAgentProvider = class {
10830
11333
  return this.executableResolver.resolve();
10831
11334
  }
10832
11335
  resolveExecutableCandidates() {
10833
- const executableName = process.platform === "win32" ? "codex.cmd" : "codex";
10834
- const configured = process.env.MEARL_CODEX_PATH;
10835
- const pathCandidates = (process.env.PATH || "").split(path7.delimiter).filter(Boolean).map((dir) => path7.join(dir, executableName));
10836
- return executableFileCandidates([
10837
- configured,
10838
- path7.join(path7.dirname(process.execPath), executableName),
10839
- path7.join(os3.homedir(), ".local", "bin", executableName),
10840
- path7.join(os3.homedir(), ".npm-global", "bin", executableName),
10841
- path7.join(os3.homedir(), ".volta", "bin", executableName),
10842
- ...process.platform === "win32" ? [] : ["/opt/homebrew/bin/codex", "/usr/local/bin/codex", "/usr/bin/codex"],
10843
- ...pathCandidates
10844
- ]);
11336
+ return executableFileCandidates(codexExecutableCandidatePaths());
10845
11337
  }
10846
11338
  };
10847
11339
 
10848
11340
  // src/agent/qoder-provider.ts
10849
- import fs6 from "node:fs";
11341
+ import fs7 from "node:fs";
10850
11342
  import os4 from "node:os";
10851
11343
  import path8 from "node:path";
10852
- import { spawn as spawn6 } from "node:child_process";
11344
+ import { spawn as spawn7 } from "node:child_process";
10853
11345
  import { Readable, Writable } from "node:stream";
10854
11346
 
10855
11347
  // ../../node_modules/.pnpm/@agentclientprotocol+sdk@1.2.1_zod@4.3.6/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
@@ -11671,10 +12163,10 @@ function mergeDefs(...defs) {
11671
12163
  function cloneDef(schema) {
11672
12164
  return mergeDefs(schema._zod.def);
11673
12165
  }
11674
- function getElementAtPath(obj, path12) {
11675
- if (!path12)
12166
+ function getElementAtPath(obj, path13) {
12167
+ if (!path13)
11676
12168
  return obj;
11677
- return path12.reduce((acc, key) => acc?.[key], obj);
12169
+ return path13.reduce((acc, key) => acc?.[key], obj);
11678
12170
  }
11679
12171
  function promiseAllObject(promisesObj) {
11680
12172
  const keys = Object.keys(promisesObj);
@@ -12057,11 +12549,11 @@ function aborted(x, startIndex = 0) {
12057
12549
  }
12058
12550
  return false;
12059
12551
  }
12060
- function prefixIssues(path12, issues) {
12552
+ function prefixIssues(path13, issues) {
12061
12553
  return issues.map((iss) => {
12062
12554
  var _a2;
12063
12555
  (_a2 = iss).path ?? (_a2.path = []);
12064
- iss.path.unshift(path12);
12556
+ iss.path.unshift(path13);
12065
12557
  return iss;
12066
12558
  });
12067
12559
  }
@@ -12244,7 +12736,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
12244
12736
  }
12245
12737
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
12246
12738
  const result = { errors: [] };
12247
- const processError = (error49, path12 = []) => {
12739
+ const processError = (error49, path13 = []) => {
12248
12740
  var _a2, _b;
12249
12741
  for (const issue2 of error49.issues) {
12250
12742
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12254,7 +12746,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12254
12746
  } else if (issue2.code === "invalid_element") {
12255
12747
  processError({ issues: issue2.issues }, issue2.path);
12256
12748
  } else {
12257
- const fullpath = [...path12, ...issue2.path];
12749
+ const fullpath = [...path13, ...issue2.path];
12258
12750
  if (fullpath.length === 0) {
12259
12751
  result.errors.push(mapper(issue2));
12260
12752
  continue;
@@ -12286,8 +12778,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12286
12778
  }
12287
12779
  function toDotPath(_path) {
12288
12780
  const segs = [];
12289
- const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12290
- for (const seg of path12) {
12781
+ const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12782
+ for (const seg of path13) {
12291
12783
  if (typeof seg === "number")
12292
12784
  segs.push(`[${seg}]`);
12293
12785
  else if (typeof seg === "symbol")
@@ -24264,13 +24756,13 @@ function resolveRef(ref, ctx) {
24264
24756
  if (!ref.startsWith("#")) {
24265
24757
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24266
24758
  }
24267
- const path12 = ref.slice(1).split("/").filter(Boolean);
24268
- if (path12.length === 0) {
24759
+ const path13 = ref.slice(1).split("/").filter(Boolean);
24760
+ if (path13.length === 0) {
24269
24761
  return ctx.rootSchema;
24270
24762
  }
24271
24763
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24272
- if (path12[0] === defsKey) {
24273
- const key = path12[1];
24764
+ if (path13[0] === defsKey) {
24765
+ const key = path13[1];
24274
24766
  if (!key || !ctx.defs[key]) {
24275
24767
  throw new Error(`Reference not found: ${ref}`);
24276
24768
  }
@@ -28195,10 +28687,10 @@ function parseQoderSkillsList(output, cwd, home = os4.homedir()) {
28195
28687
  finish();
28196
28688
  return skills;
28197
28689
  }
28198
- function qoderManagedExecutableCandidates(home) {
28690
+ function qoderManagedExecutableCandidates(home, readdirSync2 = fs7.readdirSync) {
28199
28691
  const directory = path8.join(home, ".qoder", "bin", "qodercli");
28200
28692
  try {
28201
- return fs6.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith("qodercli-")).sort((left, right) => right.name.localeCompare(left.name, void 0, { numeric: true })).map((entry) => path8.join(directory, entry.name));
28693
+ return readdirSync2(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith("qodercli-")).sort((left, right) => right.name.localeCompare(left.name, void 0, { numeric: true })).map((entry) => path8.join(directory, entry.name));
28202
28694
  } catch {
28203
28695
  return [];
28204
28696
  }
@@ -28213,6 +28705,25 @@ function isPermissionResponse(value) {
28213
28705
  function qoderInstallCommand(platform = process.platform) {
28214
28706
  return platform === "win32" ? "npm install -g @qoder-ai/qodercli" : "curl -fsSL https://qoder.com/install | bash";
28215
28707
  }
28708
+ function qoderExecutableCandidatePaths(options = {}) {
28709
+ const env = options.env ?? process.env;
28710
+ const home = (options.homedir ?? os4.homedir)();
28711
+ const platform = options.platform ?? process.platform;
28712
+ const executableNames2 = platform === "win32" ? ["qodercli.exe", "qodercli.cmd"] : ["qodercli"];
28713
+ return agentExecutableCandidatePaths({
28714
+ executableNames: executableNames2,
28715
+ configuredPath: env.MEARL_QODER_PATH,
28716
+ env,
28717
+ extraUserCandidates: [
28718
+ ...qoderManagedExecutableCandidates(home, options.readdirSync),
28719
+ ...executableNames2.map((name) => path8.join(home, ".qoder", "bin", name))
28720
+ ],
28721
+ home,
28722
+ platform,
28723
+ processExecPath: options.processExecPath,
28724
+ readdirSync: options.readdirSync
28725
+ });
28726
+ }
28216
28727
  function sessionOptions(options) {
28217
28728
  const cwd = path8.resolve(options.cwd || os4.homedir());
28218
28729
  return {
@@ -28618,11 +29129,11 @@ ${message}` : message;
28618
29129
  throw new Error(resolution.error);
28619
29130
  }
28620
29131
  const { executable } = resolution;
28621
- const launch = agentSpawnCommand(executable, ["--acp"]);
28622
- const child = spawn6(launch.command, launch.args, {
29132
+ const launch = resolveSpawnCommand(executable, ["--acp"]);
29133
+ const child = spawn7(launch.command, launch.args, {
28623
29134
  stdio: ["pipe", "pipe", "pipe"],
28624
29135
  windowsHide: true,
28625
- env: createAgentChildEnv()
29136
+ env: prependNodePath()
28626
29137
  });
28627
29138
  this.process = child;
28628
29139
  child.stderr.on("data", (chunk) => {
@@ -28965,12 +29476,12 @@ ${message}` : message;
28965
29476
  }
28966
29477
  runSkillsList(executable, cwd) {
28967
29478
  return new Promise((resolve, reject) => {
28968
- const launch = agentSpawnCommand(executable, ["skill", "list", "--all"]);
28969
- const child = spawn6(launch.command, launch.args, {
29479
+ const launch = resolveSpawnCommand(executable, ["skill", "list", "--all"]);
29480
+ const child = spawn7(launch.command, launch.args, {
28970
29481
  cwd,
28971
29482
  stdio: ["ignore", "pipe", "pipe"],
28972
29483
  windowsHide: true,
28973
- env: createAgentChildEnv()
29484
+ env: prependNodePath()
28974
29485
  });
28975
29486
  let stdout = "";
28976
29487
  let stderr = "";
@@ -29004,89 +29515,60 @@ ${message}` : message;
29004
29515
  return this.executableResolver.resolve();
29005
29516
  }
29006
29517
  resolveExecutableCandidates() {
29007
- const executableNames = process.platform === "win32" ? ["qodercli.exe", "qodercli.cmd"] : ["qodercli"];
29008
- const configured = process.env.MEARL_QODER_PATH;
29009
- const home = os4.homedir();
29010
- const pathCandidates = (process.env.PATH || "").split(path8.delimiter).filter(Boolean).flatMap((dir) => executableNames.map((name) => path8.join(dir, name)));
29011
- return executableFileCandidates([
29012
- configured,
29013
- ...executableNames.map((name) => path8.join(path8.dirname(process.execPath), name)),
29014
- ...executableNames.map((name) => path8.join(home, ".local", "bin", name)),
29015
- ...qoderManagedExecutableCandidates(home),
29016
- ...executableNames.map((name) => path8.join(home, ".qoder", "bin", name)),
29017
- ...process.platform === "win32" ? [] : ["/opt/homebrew/bin/qodercli", "/usr/local/bin/qodercli", "/usr/bin/qodercli"],
29018
- ...pathCandidates
29019
- ]);
29518
+ return executableFileCandidates(qoderExecutableCandidatePaths());
29020
29519
  }
29021
29520
  emit(event) {
29022
29521
  for (const listener of this.listeners) listener(event);
29023
29522
  }
29024
29523
  };
29025
29524
 
29026
- // src/agent/skill-manager.ts
29027
- import fs7 from "node:fs";
29525
+ // ../setup/dist/aliSkills.js
29526
+ import fs8 from "node:fs";
29028
29527
  import os5 from "node:os";
29029
29528
  import path9 from "node:path";
29030
- import { spawn as spawn7 } from "node:child_process";
29529
+ import { spawn as spawn8 } from "node:child_process";
29031
29530
  var COMMAND_TIMEOUT_MS = 12e4;
29032
29531
  var MAX_OUTPUT_LENGTH = 1e6;
29033
29532
  var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g");
29034
- var SETUP_COMMAND = "npm install -g ali-skills";
29035
- function existingExecutable(candidates) {
29036
- for (const candidate of candidates) {
29037
- if (!candidate) continue;
29038
- try {
29039
- fs7.accessSync(
29040
- candidate,
29041
- process.platform === "win32" ? fs7.constants.F_OK : fs7.constants.X_OK
29042
- );
29043
- return candidate;
29044
- } catch {
29045
- }
29046
- }
29047
- return null;
29533
+ function existingExecutable(candidates, existsSync4) {
29534
+ return candidates.find((candidate) => candidate && existsSync4(candidate)) ?? null;
29048
29535
  }
29049
- function versionManagerCandidates(home, executableNames) {
29050
- const roots = [
29051
- path9.join(home, ".nvm", "versions", "node"),
29052
- path9.join(home, ".fnm", "node-versions")
29053
- ];
29054
- return roots.flatMap((root) => {
29055
- try {
29056
- return fs7.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((left, right) => right.name.localeCompare(left.name, void 0, { numeric: true })).flatMap(
29057
- (entry) => executableNames.flatMap((name) => [
29058
- path9.join(root, entry.name, "bin", name),
29059
- path9.join(root, entry.name, "installation", "bin", name)
29060
- ])
29061
- );
29062
- } catch {
29063
- return [];
29064
- }
29065
- });
29536
+ function stripAnsi(value) {
29537
+ return value.replace(ANSI_ESCAPE_PATTERN, "");
29066
29538
  }
29067
- function resolveAliSkillsExecutable() {
29068
- const executableNames = process.platform === "win32" ? ["ali-skills.cmd", "ali-skills.exe", "ali-skills"] : ["ali-skills"];
29069
- const home = os5.homedir();
29070
- const pathCandidates = (process.env.PATH || "").split(path9.delimiter).filter(Boolean).flatMap((directory) => executableNames.map((name) => path9.join(directory, name)));
29539
+ function resolveAliSkillsExecutable(options = {}) {
29540
+ const env = options.env ?? process.env;
29541
+ const home = (options.homedir ?? os5.homedir)();
29542
+ const platform = options.platform ?? process.platform;
29543
+ const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29544
+ const processExecPath = options.processExecPath ?? process.execPath;
29545
+ const readdirSync2 = options.readdirSync ?? fs8.readdirSync;
29546
+ const names = executableNames("ali-skills", platform);
29547
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
29548
+ const pathCandidates = (env[pathKey] || "").split(path9.delimiter).filter(Boolean).flatMap((directory) => names.map((name) => path9.join(directory, name)));
29071
29549
  const candidates = [
29072
- process.env.MEARL_ALI_SKILLS_PATH,
29073
- ...executableNames.map((name) => path9.join(path9.dirname(process.execPath), name)),
29074
- ...executableNames.map((name) => path9.join(home, ".local", "bin", name)),
29075
- ...executableNames.map((name) => path9.join(home, ".npm-global", "bin", name)),
29076
- ...executableNames.map((name) => path9.join(home, ".volta", "bin", name)),
29077
- ...executableNames.map((name) => path9.join(home, ".local", "share", "pnpm", name)),
29078
- ...executableNames.map((name) => path9.join(home, "Library", "pnpm", name)),
29079
- ...versionManagerCandidates(home, executableNames),
29080
- ...process.platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
29550
+ env.MEARL_ALI_SKILLS_PATH,
29551
+ ...names.map((name) => path9.join(path9.dirname(processExecPath), name)),
29552
+ ...names.map((name) => path9.join(home, ".local", "bin", name)),
29553
+ ...names.map((name) => path9.join(home, ".npm-global", "bin", name)),
29554
+ ...names.map((name) => path9.join(home, ".volta", "bin", name)),
29555
+ ...names.map((name) => path9.join(home, ".local", "share", "pnpm", name)),
29556
+ ...names.map((name) => path9.join(home, "Library", "pnpm", name)),
29557
+ ...versionManagerExecutableCandidates(home, names, {
29558
+ env,
29559
+ platform,
29560
+ readdirSync: readdirSync2
29561
+ }),
29562
+ ...platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
29081
29563
  ...pathCandidates
29082
29564
  ];
29083
- return existingExecutable(candidates);
29565
+ return existingExecutable(candidates, existsSync4);
29084
29566
  }
29085
- var runSkillCli = (executable, args, cwd) => new Promise((resolve, reject) => {
29086
- const launch = agentSpawnCommand(executable, args);
29087
- const child = spawn7(launch.command, launch.args, {
29567
+ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) => {
29568
+ const launch = resolveSpawnCommand(executable, args);
29569
+ const child = spawn8(launch.command, launch.args, {
29088
29570
  cwd,
29089
- env: createAgentChildEnv(),
29571
+ env: prependNodePath(prependCommandPath(executable)),
29090
29572
  stdio: ["ignore", "pipe", "pipe"],
29091
29573
  windowsHide: true
29092
29574
  });
@@ -29095,11 +29577,14 @@ var runSkillCli = (executable, args, cwd) => new Promise((resolve, reject) => {
29095
29577
  let settled = false;
29096
29578
  const append = (current, chunk) => current.length >= MAX_OUTPUT_LENGTH ? current : (current + chunk.toString("utf8")).slice(0, MAX_OUTPUT_LENGTH);
29097
29579
  const finish = (error48) => {
29098
- if (settled) return;
29580
+ if (settled)
29581
+ return;
29099
29582
  settled = true;
29100
29583
  clearTimeout(timer);
29101
- if (error48) reject(error48);
29102
- else resolve({ stdout, stderr });
29584
+ if (error48)
29585
+ reject(error48);
29586
+ else
29587
+ resolve({ stdout, stderr });
29103
29588
  };
29104
29589
  const timer = setTimeout(() => {
29105
29590
  child.kill("SIGTERM");
@@ -29117,9 +29602,10 @@ var runSkillCli = (executable, args, cwd) => new Promise((resolve, reject) => {
29117
29602
  finish(new Error(detail || `Ali Skills command exited with code ${String(code)}`));
29118
29603
  });
29119
29604
  });
29120
- function stripAnsi(value) {
29121
- return value.replace(ANSI_ESCAPE_PATTERN, "");
29122
- }
29605
+
29606
+ // src/agent/skill-manager.ts
29607
+ var SETUP_COMMAND = "npm install -g ali-skills";
29608
+ var runSkillCli = runAliSkillsCli;
29123
29609
  function validateProvider(value) {
29124
29610
  if (value !== "codex" && value !== "qoder") {
29125
29611
  throw new Error("Skill provider must be codex or qoder");
@@ -29304,7 +29790,7 @@ var CDP_PORT = positivePort(cliOption("--cdp-port"), 9222);
29304
29790
  var CDP_USER_DATA_DIR = cliOption("--cdp-user-data-dir");
29305
29791
  var CDP_BROWSER_ID = cliOption("--browser-id") ?? "cdp";
29306
29792
  var CDP_BROWSER_NAME = cliOption("--browser-name") ?? "CDP";
29307
- if (cliArg === "--init") {
29793
+ if (cliArg === "--init" || cliArg === "--uninstall") {
29308
29794
  const { runCli: runCli2 } = await Promise.resolve().then(() => (init_install(), install_exports));
29309
29795
  await runCli2(process.argv.slice(2));
29310
29796
  process.exit(0);
@@ -29323,6 +29809,8 @@ Options:
29323
29809
  --init [--mode <skills|mcp|both>] [--extension-id <id>]
29324
29810
  Initialize Native Messaging Host
29325
29811
  (default: both — installs the Skills and MCP hosts)
29812
+ --uninstall [--mode <skills|mcp|both>]
29813
+ Remove Native Messaging Host registration
29326
29814
  --daemon Start as standalone CDP daemon (no extension needed)
29327
29815
  --cdp-port <port> CDP port for daemon mode (default 9222)
29328
29816
  --cdp-user-data-dir <dir> Read DevToolsActivePort from this Chrome profile
@@ -29340,17 +29828,18 @@ Connection modes (auto-detected at runtime):
29340
29828
  Examples:
29341
29829
  mearl-native-host --init
29342
29830
  mearl-native-host --init --mode skills
29831
+ mearl-native-host --uninstall --mode skills
29343
29832
  mearl-native-host --init --extension-id abcdefghijklmnopqrstuvwxyzabcdef
29344
29833
  mearl-native-host --daemon
29345
29834
  mearl-native-host --version`);
29346
29835
  process.exit(0);
29347
29836
  }
29348
29837
  function socketPathFor(suffix) {
29349
- return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` : path11.join(os7.tmpdir(), `mearl-agent${suffix}.sock`);
29838
+ return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` : path12.join(os7.tmpdir(), `mearl-agent${suffix}.sock`);
29350
29839
  }
29351
29840
  var LEGACY_SOCKET_PATH = socketPathFor("");
29352
29841
  var INSTANCE_SOCKET_PATH = socketPathFor(`-${process.pid}`);
29353
- var LOG_FILE = path11.join(os7.tmpdir(), "mearl-native-host.log");
29842
+ var LOG_FILE = path12.join(os7.tmpdir(), "mearl-native-host.log");
29354
29843
  var DEFAULT_TIMEOUT_MS = 15e3;
29355
29844
  var INVENTORY_IDLE_EXIT_MS = 1e3;
29356
29845
  var MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
@@ -29449,7 +29938,7 @@ function log(...args) {
29449
29938
  const msg = `[${(/* @__PURE__ */ new Date()).toISOString()}] [NativeHost] ${args.join(" ")}
29450
29939
  `;
29451
29940
  try {
29452
- fs9.appendFileSync(LOG_FILE, msg);
29941
+ fs11.appendFileSync(LOG_FILE, msg);
29453
29942
  } catch {
29454
29943
  }
29455
29944
  }
@@ -29983,7 +30472,7 @@ function onSocketConnection(socket) {
29983
30472
  }
29984
30473
  function isPathAlive(socketPath) {
29985
30474
  return new Promise((resolve) => {
29986
- if (process.platform !== "win32" && !fs9.existsSync(socketPath)) {
30475
+ if (process.platform !== "win32" && !fs11.existsSync(socketPath)) {
29987
30476
  resolve(false);
29988
30477
  return;
29989
30478
  }
@@ -30006,9 +30495,9 @@ function isPathAlive(socketPath) {
30006
30495
  }
30007
30496
  function listenOn(socketPath) {
30008
30497
  return new Promise((resolve, reject) => {
30009
- if (process.platform !== "win32" && fs9.existsSync(socketPath)) {
30498
+ if (process.platform !== "win32" && fs11.existsSync(socketPath)) {
30010
30499
  try {
30011
- fs9.unlinkSync(socketPath);
30500
+ fs11.unlinkSync(socketPath);
30012
30501
  } catch {
30013
30502
  }
30014
30503
  }
@@ -30017,11 +30506,11 @@ function listenOn(socketPath) {
30017
30506
  server.listen(socketPath, () => {
30018
30507
  if (process.platform !== "win32") {
30019
30508
  try {
30020
- fs9.chmodSync(socketPath, 384);
30509
+ fs11.chmodSync(socketPath, 384);
30021
30510
  } catch (error48) {
30022
30511
  server.close();
30023
30512
  try {
30024
- fs9.unlinkSync(socketPath);
30513
+ fs11.unlinkSync(socketPath);
30025
30514
  } catch {
30026
30515
  }
30027
30516
  reject(error48);
@@ -30057,7 +30546,7 @@ function releaseLegacySocket() {
30057
30546
  }
30058
30547
  if (process.platform !== "win32") {
30059
30548
  try {
30060
- if (fs9.existsSync(LEGACY_SOCKET_PATH)) fs9.unlinkSync(LEGACY_SOCKET_PATH);
30549
+ if (fs11.existsSync(LEGACY_SOCKET_PATH)) fs11.unlinkSync(LEGACY_SOCKET_PATH);
30061
30550
  } catch {
30062
30551
  }
30063
30552
  }
@@ -30069,8 +30558,8 @@ async function startSocketServer() {
30069
30558
  function cleanupInstanceSocketFile() {
30070
30559
  if (process.platform === "win32") return;
30071
30560
  try {
30072
- if (fs9.existsSync(INSTANCE_SOCKET_PATH)) {
30073
- fs9.unlinkSync(INSTANCE_SOCKET_PATH);
30561
+ if (fs11.existsSync(INSTANCE_SOCKET_PATH)) {
30562
+ fs11.unlinkSync(INSTANCE_SOCKET_PATH);
30074
30563
  }
30075
30564
  } catch (error48) {
30076
30565
  log("cleanup socket file failed:", String(error48));