@mearl/native-host 2.2.2 → 2.4.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
@@ -10,20 +10,20 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // ../setup/dist/nativeHost.js
13
- import fs9 from "node:fs";
13
+ import fs11 from "node:fs";
14
14
  import os6 from "node:os";
15
- import path10 from "node:path";
15
+ import path12 from "node:path";
16
16
  import { execFileSync } from "node:child_process";
17
17
  function resolveNativeHostDirectory(options = {}) {
18
18
  const env = options.env ?? process.env;
19
19
  const home = (options.homedir ?? os6.homedir)();
20
20
  switch (options.platform ?? process.platform) {
21
21
  case "darwin":
22
- return path10.join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts");
22
+ return path12.join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts");
23
23
  case "linux":
24
- return path10.join(home, ".config/google-chrome/NativeMessagingHosts");
24
+ return path12.join(home, ".config/google-chrome/NativeMessagingHosts");
25
25
  case "win32":
26
- return path10.join(env.PROGRAMDATA || "C:\\ProgramData", "Google\\Chrome\\NativeMessagingHosts");
26
+ return path12.join(env.PROGRAMDATA || "C:\\ProgramData", "Google\\Chrome\\NativeMessagingHosts");
27
27
  default:
28
28
  throw new Error(`Unsupported platform: ${String(options.platform ?? process.platform)}`);
29
29
  }
@@ -46,20 +46,20 @@ function uninstallNativeHostArtifacts(options = {}, dependencies = {}) {
46
46
  writeLine(line);
47
47
  };
48
48
  try {
49
- const existsSync4 = dependencies.existsSync ?? fs9.existsSync;
49
+ const existsSync4 = dependencies.existsSync ?? fs11.existsSync;
50
50
  const platform = dependencies.platform ?? process.platform;
51
51
  const hostDir = dependencies.hostDir ?? resolveNativeHostDirectory({
52
52
  env: dependencies.env,
53
53
  homedir: dependencies.homedir,
54
54
  platform
55
55
  });
56
- const unlinkSync = dependencies.unlinkSync ?? fs9.unlinkSync;
56
+ const unlinkSync = dependencies.unlinkSync ?? fs11.unlinkSync;
57
57
  const unregister = dependencies.unregisterWindowsNativeHost ?? unregisterWindowsNativeHost;
58
58
  const hostName = MEARL_NATIVE_HOST_NAMES[mode];
59
59
  const launcherExtension = platform === "win32" ? "cmd" : "sh";
60
60
  const artifacts = [
61
- path10.join(hostDir, `${hostName}.json`),
62
- path10.join(hostDir, `${hostName}-launcher.${launcherExtension}`)
61
+ path12.join(hostDir, `${hostName}.json`),
62
+ path12.join(hostDir, `${hostName}-launcher.${launcherExtension}`)
63
63
  ];
64
64
  for (const artifact of artifacts) {
65
65
  if (!existsSync4(artifact))
@@ -94,17 +94,17 @@ __export(install_exports, {
94
94
  runCli: () => runCli,
95
95
  uninstallNativeHost: () => uninstallNativeHost
96
96
  });
97
- import fs10 from "node:fs";
98
- import path11 from "node:path";
97
+ import fs12 from "node:fs";
98
+ import path13 from "node:path";
99
99
  import { execFileSync as execFileSync2 } from "node:child_process";
100
100
  import { createRequire } from "node:module";
101
- import { fileURLToPath } from "node:url";
101
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
102
102
  function resolveHostDir() {
103
103
  return resolveNativeHostDirectory();
104
104
  }
105
105
  function resolveNativeHostPath() {
106
- const siblingPath = path11.join(__dirname, "index.js");
107
- if (fs10.existsSync(siblingPath)) {
106
+ const siblingPath = path13.join(__dirname, "index.js");
107
+ if (fs12.existsSync(siblingPath)) {
108
108
  return siblingPath;
109
109
  }
110
110
  try {
@@ -123,16 +123,16 @@ function registerWindowsNativeHost(hostName, manifestPath) {
123
123
  });
124
124
  }
125
125
  function createLauncherScript(hostDir, hostPath, hostName) {
126
- const nodePath = path11.resolve(process.execPath);
126
+ const nodePath = path13.resolve(process.execPath);
127
127
  if (process.platform === "win32") {
128
- const launcherPath2 = path11.join(hostDir, `${hostName}-launcher.cmd`);
128
+ const launcherPath2 = path13.join(hostDir, `${hostName}-launcher.cmd`);
129
129
  const content2 = `@echo off\r
130
130
  "${nodePath}" "${hostPath}" %*\r
131
131
  `;
132
- fs10.writeFileSync(launcherPath2, content2, "utf-8");
132
+ fs12.writeFileSync(launcherPath2, content2, "utf-8");
133
133
  return launcherPath2;
134
134
  }
135
- const launcherPath = path11.join(hostDir, `${hostName}-launcher.sh`);
135
+ const launcherPath = path13.join(hostDir, `${hostName}-launcher.sh`);
136
136
  const content = [
137
137
  "#!/bin/sh",
138
138
  `NODE_BIN=${JSON.stringify(nodePath)}`,
@@ -140,8 +140,8 @@ function createLauncherScript(hostDir, hostPath, hostName) {
140
140
  'exec "$NODE_BIN" "$HOST_SCRIPT" "$@"',
141
141
  ""
142
142
  ].join("\n");
143
- fs10.writeFileSync(launcherPath, content, "utf-8");
144
- fs10.chmodSync(launcherPath, 493);
143
+ fs12.writeFileSync(launcherPath, content, "utf-8");
144
+ fs12.chmodSync(launcherPath, 493);
145
145
  return launcherPath;
146
146
  }
147
147
  async function installNativeHost(options = {}) {
@@ -167,14 +167,14 @@ async function installNativeHost(options = {}) {
167
167
  try {
168
168
  const hostDir = resolveHostDir();
169
169
  if (skipIfExists) {
170
- const manifestPath2 = path11.join(hostDir, `${hostName}.json`);
171
- if (fs10.existsSync(manifestPath2)) {
170
+ const manifestPath2 = path13.join(hostDir, `${hostName}.json`);
171
+ if (fs12.existsSync(manifestPath2)) {
172
172
  log2(`✅ Already installed: ${manifestPath2}`);
173
173
  return true;
174
174
  }
175
175
  }
176
- if (!fs10.existsSync(hostDir)) {
177
- fs10.mkdirSync(hostDir, { recursive: true });
176
+ if (!fs12.existsSync(hostDir)) {
177
+ fs12.mkdirSync(hostDir, { recursive: true });
178
178
  log2(`✅ Created directory: ${hostDir}`);
179
179
  }
180
180
  const resolvedHostPath = resolveNativeHostPath();
@@ -188,13 +188,13 @@ async function installNativeHost(options = {}) {
188
188
  type: "stdio",
189
189
  allowed_origins: [`chrome-extension://${extensionId}/`]
190
190
  };
191
- const manifestPath = path11.join(hostDir, `${hostName}.json`);
192
- fs10.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
191
+ const manifestPath = path13.join(hostDir, `${hostName}.json`);
192
+ fs12.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
193
193
  if (process.platform === "win32") {
194
194
  registerWindowsNativeHost(hostName, manifestPath);
195
195
  log2(`✅ Windows registry key created for: ${hostName}`);
196
196
  } else {
197
- fs10.chmodSync(manifestPath, 420);
197
+ fs12.chmodSync(manifestPath, 420);
198
198
  }
199
199
  log2(`✅ Manifest installed: ${manifestPath}`);
200
200
  log2(`✅ Extension ID: ${extensionId}`);
@@ -306,26 +306,26 @@ var init_install = __esm({
306
306
  "use strict";
307
307
  init_nativeHost();
308
308
  DEFAULT_EXTENSION_ID = "aoehhjnofngknnjefamjbplchbolghkm";
309
- __filename = fileURLToPath(import.meta.url);
310
- __dirname = path11.dirname(__filename);
309
+ __filename = fileURLToPath2(import.meta.url);
310
+ __dirname = path13.dirname(__filename);
311
311
  ALL_MODES = ["skills", "mcp"];
312
312
  }
313
313
  });
314
314
 
315
315
  // src/index.ts
316
316
  import net from "node:net";
317
- import fs11 from "node:fs";
317
+ import fs13 from "node:fs";
318
318
  import http5 from "node:http";
319
319
  import os7 from "node:os";
320
- import path12 from "node:path";
321
- import { fileURLToPath as fileURLToPath2 } from "node:url";
320
+ import path14 from "node:path";
321
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
322
322
 
323
323
  // src/cdp-backend/cdp-session.ts
324
324
  import http3 from "node:http";
325
325
 
326
326
  // src/cdp-backend/client.ts
327
327
  import http from "node:http";
328
- import crypto from "node:crypto";
328
+ import crypto2 from "node:crypto";
329
329
  var CdpChildSession = class {
330
330
  constructor(parent, sessionId) {
331
331
  this.parent = parent;
@@ -406,8 +406,8 @@ var CdpClient = class _CdpClient {
406
406
  return new Promise((resolve, reject) => {
407
407
  const url2 = new URL(wsUrl);
408
408
  const port = parseInt(url2.port || "9222", 10);
409
- const key = crypto.randomBytes(16).toString("base64");
410
- 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");
411
411
  const req = http.request({
412
412
  host: url2.hostname,
413
413
  port,
@@ -559,7 +559,7 @@ var CdpClient = class _CdpClient {
559
559
  sendText(text) {
560
560
  if (this._closed) return;
561
561
  const payload = Buffer.from(text, "utf-8");
562
- const maskKey = crypto.randomBytes(4);
562
+ const maskKey = crypto2.randomBytes(4);
563
563
  const masked = Buffer.from(payload.map((b, i) => b ^ maskKey[i % 4]));
564
564
  let header;
565
565
  if (payload.length < 126) {
@@ -754,6 +754,9 @@ function buildCookieHeaderValue(cookieMap) {
754
754
  var APLUS_URL_PATTERN = /\.mmstat\.com\//;
755
755
  var RUM_URL_PATTERN = /\.rum\.aliyuncs\.com\//;
756
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
+ }
757
760
  var aplusCounter = 0;
758
761
  var armsCounter = 0;
759
762
  var rumCounter = 0;
@@ -1163,13 +1166,13 @@ function queryTrackingEvents(buffer, options = {}) {
1163
1166
  }
1164
1167
 
1165
1168
  // ../browser-core/dist/requestSerializer.js
1166
- function parsePath(path13) {
1167
- if (path13.length > 1e3)
1169
+ function parsePath(path15) {
1170
+ if (path15.length > 1e3)
1168
1171
  throw new Error("Path too long: maximum 1000 characters allowed");
1169
1172
  const keys = [];
1170
1173
  let current = "";
1171
- for (let i = 0; i < path13.length; i++) {
1172
- const char = path13[i];
1174
+ for (let i = 0; i < path15.length; i++) {
1175
+ const char = path15[i];
1173
1176
  if (char === ".") {
1174
1177
  if (current) {
1175
1178
  keys.push(current);
@@ -1181,10 +1184,10 @@ function parsePath(path13) {
1181
1184
  current = "";
1182
1185
  }
1183
1186
  let j = i + 1;
1184
- while (j < path13.length && path13[j] !== "]")
1187
+ while (j < path15.length && path15[j] !== "]")
1185
1188
  j++;
1186
- if (j < path13.length) {
1187
- const index = parseInt(path13.substring(i + 1, j), 10);
1189
+ if (j < path15.length) {
1190
+ const index = parseInt(path15.substring(i + 1, j), 10);
1188
1191
  if (!isNaN(index)) {
1189
1192
  if (index < 0 || index > 1e5)
1190
1193
  throw new Error(`Array index out of safe range: ${index}`);
@@ -1202,8 +1205,8 @@ function parsePath(path13) {
1202
1205
  throw new Error("Path too deep: maximum 50 levels allowed");
1203
1206
  return keys;
1204
1207
  }
1205
- function getValueByPath(obj, path13) {
1206
- const keys = parsePath(path13);
1208
+ function getValueByPath(obj, path15) {
1209
+ const keys = parsePath(path15);
1207
1210
  let current = obj;
1208
1211
  for (const key of keys) {
1209
1212
  if (current === null || current === void 0)
@@ -1212,8 +1215,8 @@ function getValueByPath(obj, path13) {
1212
1215
  }
1213
1216
  return current;
1214
1217
  }
1215
- function setValueByPath(obj, path13, value) {
1216
- const keys = parsePath(path13);
1218
+ function setValueByPath(obj, path15, value) {
1219
+ const keys = parsePath(path15);
1217
1220
  let current = obj;
1218
1221
  for (let i = 0; i < keys.length - 1; i++) {
1219
1222
  const key = keys[i];
@@ -1287,7 +1290,11 @@ function serializeRequest(request, options = {}) {
1287
1290
  status: request.status,
1288
1291
  statusText: request.statusText,
1289
1292
  queryString: request.queryString,
1290
- duration: request.duration
1293
+ duration: request.duration,
1294
+ captureSequence: request.captureSequence,
1295
+ isPending: request.isPending,
1296
+ source: request.source,
1297
+ resourceType: request.resourceType
1291
1298
  };
1292
1299
  if (includeHeaders) {
1293
1300
  result.headers = request.headers;
@@ -1411,10 +1418,14 @@ function applyFieldLimits(request, maxFieldBytes) {
1411
1418
  request.responseBody = limitBufferedField(request.responseBody, maxFieldBytes);
1412
1419
  }
1413
1420
  function trimRequestBuffer(requests, accounting, maxEntries, maxBytes) {
1421
+ const removed = [];
1414
1422
  while (requests.length > 1 && (requests.length > maxEntries || accounting.totalBytes > maxBytes)) {
1415
- requests.shift();
1423
+ const request = requests.shift();
1424
+ if (request)
1425
+ removed.push(request);
1416
1426
  accounting.totalBytes -= accounting.sizes.shift() ?? 0;
1417
1427
  }
1428
+ return removed;
1418
1429
  }
1419
1430
  function appendRequestToBuffer(requests, accounting, request, limits = {}) {
1420
1431
  const { maxEntries = REQUEST_BUFFER_MAX_ENTRIES, maxBytes = REQUEST_BUFFER_MAX_BYTES, maxFieldBytes = REQUEST_BUFFER_MAX_FIELD_BYTES } = limits;
@@ -1424,20 +1435,20 @@ function appendRequestToBuffer(requests, accounting, request, limits = {}) {
1424
1435
  requests.push(request);
1425
1436
  accounting.sizes.push(size);
1426
1437
  accounting.totalBytes += size;
1427
- trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1438
+ return trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1428
1439
  }
1429
1440
  function refreshRequestInBuffer(requests, accounting, request, limits = {}) {
1430
1441
  const { maxEntries = REQUEST_BUFFER_MAX_ENTRIES, maxBytes = REQUEST_BUFFER_MAX_BYTES, maxFieldBytes = REQUEST_BUFFER_MAX_FIELD_BYTES } = limits;
1431
1442
  const index = requests.indexOf(request);
1432
1443
  if (index === -1)
1433
- return;
1444
+ return [];
1434
1445
  synchronizeAccounting(requests, accounting);
1435
1446
  applyFieldLimits(request, maxFieldBytes);
1436
1447
  const previousSize = accounting.sizes[index] ?? 0;
1437
1448
  const nextSize = estimateRequestBufferBytes(request);
1438
1449
  accounting.sizes[index] = nextSize;
1439
1450
  accounting.totalBytes += nextSize - previousSize;
1440
- trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1451
+ return trimRequestBuffer(requests, accounting, maxEntries, maxBytes);
1441
1452
  }
1442
1453
 
1443
1454
  // ../browser-core/dist/proxyPermissions.js
@@ -2741,19 +2752,19 @@ function installPageObserverInPage(maxBatches = 50) {
2741
2752
  }
2742
2753
  }
2743
2754
  }
2744
- const path13 = [];
2755
+ const path15 = [];
2745
2756
  let current = input;
2746
- while (current && current !== document.documentElement && path13.length < 6) {
2757
+ while (current && current !== document.documentElement && path15.length < 6) {
2747
2758
  const parent = current.parentElement;
2748
2759
  const currentTag = current.tagName.toLowerCase();
2749
2760
  if (!parent) {
2750
- path13.unshift(currentTag);
2761
+ path15.unshift(currentTag);
2751
2762
  break;
2752
2763
  }
2753
2764
  const siblings = Array.from(parent.children).filter((item) => item.tagName === current.tagName);
2754
2765
  const position = siblings.indexOf(current) + 1;
2755
- path13.unshift(siblings.length > 1 ? `${currentTag}:nth-of-type(${position})` : currentTag);
2756
- const candidate = path13.join(" > ");
2766
+ path15.unshift(siblings.length > 1 ? `${currentTag}:nth-of-type(${position})` : currentTag);
2767
+ const candidate = path15.join(" > ");
2757
2768
  if (input.isConnected) {
2758
2769
  try {
2759
2770
  if (document.querySelectorAll(candidate).length === 1)
@@ -2763,7 +2774,7 @@ function installPageObserverInPage(maxBatches = 50) {
2763
2774
  }
2764
2775
  current = parent;
2765
2776
  }
2766
- return path13.join(" > ") || void 0;
2777
+ return path15.join(" > ") || void 0;
2767
2778
  };
2768
2779
  const inferredRole = (element) => {
2769
2780
  const explicit = element.getAttribute("role");
@@ -3470,6 +3481,7 @@ var PAGE_ACT_ACTIONS = [
3470
3481
  "page_eval"
3471
3482
  ];
3472
3483
  var BROWSER_COMMAND_ACTIONS = {
3484
+ capture_checkpoint: true,
3473
3485
  get_requests: true,
3474
3486
  get_logs: true,
3475
3487
  get_events: true,
@@ -3512,6 +3524,7 @@ var BROWSER_COMMAND_ACTIONS = {
3512
3524
  run_actions: true
3513
3525
  };
3514
3526
  var EXTENSION_BROWSER_ACTIONS = {
3527
+ capture_checkpoint: true,
3515
3528
  get_requests: true,
3516
3529
  get_logs: true,
3517
3530
  get_events: true,
@@ -3558,24 +3571,35 @@ function isExtensionBrowserAction(value) {
3558
3571
  }
3559
3572
 
3560
3573
  // ../browser-core/dist/pageAct.js
3574
+ var DEFAULT_PAGE_DIAGNOSTICS = {
3575
+ logs: true,
3576
+ requests: true
3577
+ };
3561
3578
  function isPageActAction(action) {
3562
3579
  return PAGE_ACT_ACTIONS.includes(action);
3563
3580
  }
3564
3581
  function shouldObservePageAction(action, data) {
3565
3582
  if (!isPageActAction(action))
3566
3583
  return false;
3584
+ const diagnosticsEnabled = data?.diagnostics !== void 0 && data.diagnostics !== false;
3567
3585
  if (action === "page_eval") {
3568
- 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));
3569
3587
  }
3570
- return data?.observe !== false;
3588
+ return data?.observe !== false || data?.diagnostics !== false;
3571
3589
  }
3572
- function splitObservedActionPayload(data) {
3573
- 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;
3574
3594
  if (observe === false)
3575
- 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
+ }
3576
3599
  return {
3577
3600
  actionData,
3578
- observe: observe && typeof observe === "object" && !Array.isArray(observe) ? observe : {}
3601
+ observe: observe && typeof observe === "object" && !Array.isArray(observe) ? observe : {},
3602
+ diagnostics: resolvedDiagnostics
3579
3603
  };
3580
3604
  }
3581
3605
  function unavailableObservation(checkpoint, error48) {
@@ -4269,6 +4293,20 @@ function assertSendRequestBase64FileLimits(byteLengths) {
4269
4293
  }
4270
4294
  }
4271
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
+
4272
4310
  // ../browser-core/dist/cookie.js
4273
4311
  function normalizeCookieDomain(input, action) {
4274
4312
  const value = input.trim();
@@ -4371,12 +4409,12 @@ function resolveGetCookieTarget(currentUrl, input) {
4371
4409
  const url2 = parseCurrentUrl(currentUrl, action);
4372
4410
  const name = validateCookieName(input?.name, action);
4373
4411
  const domain2 = resolveExplicitDomain(url2, input?.domain, action);
4374
- const path13 = validateCookiePath(input?.path, action);
4412
+ const path15 = validateCookiePath(input?.path, action);
4375
4413
  return {
4376
4414
  cookieName: name,
4377
- cookieUrl: `${url2.protocol}//${url2.hostname}${(path13 ?? url2.pathname) || "/"}`,
4415
+ cookieUrl: `${url2.protocol}//${url2.hostname}${(path15 ?? url2.pathname) || "/"}`,
4378
4416
  ...domain2 ? { domain: domain2 } : {},
4379
- ...path13 ? { path: path13 } : {}
4417
+ ...path15 ? { path: path15 } : {}
4380
4418
  };
4381
4419
  }
4382
4420
  function resolveSetCookieTarget(currentUrl, input) {
@@ -4384,7 +4422,7 @@ function resolveSetCookieTarget(currentUrl, input) {
4384
4422
  const url2 = parseCurrentUrl(currentUrl, action);
4385
4423
  const name = validateCookieName(input?.name, action);
4386
4424
  const value = validateCookieValue(input?.value);
4387
- const path13 = validateCookiePath(input?.path, action) ?? "/";
4425
+ const path15 = validateCookiePath(input?.path, action) ?? "/";
4388
4426
  if (input.reload !== void 0 && typeof input.reload !== "boolean") {
4389
4427
  throw new Error('set_cookie: "reload" must be a boolean');
4390
4428
  }
@@ -4394,9 +4432,9 @@ function resolveSetCookieTarget(currentUrl, input) {
4394
4432
  cookieName: name,
4395
4433
  cookieValue: value,
4396
4434
  ...explicitDomain ? { cookieDomain: explicitDomain.includes(".") ? `.${explicitDomain}` : explicitDomain } : {},
4397
- cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${path13}`,
4435
+ cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${path15}`,
4398
4436
  domain: domain2,
4399
- path: path13,
4437
+ path: path15,
4400
4438
  reload: input.reload === true
4401
4439
  };
4402
4440
  }
@@ -4423,6 +4461,83 @@ ${SOURCE_BADGES[source]}
4423
4461
  return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
4424
4462
  }
4425
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
+
4426
4541
  // src/cdp-backend/domain-manager.ts
4427
4542
  var enabledDomains = /* @__PURE__ */ new WeakMap();
4428
4543
  function ensureCdpDomain(client2, domain2, params = {}) {
@@ -4444,14 +4559,22 @@ function ensureCdpDomain(client2, domain2, params = {}) {
4444
4559
  // src/cdp-backend/buffers.ts
4445
4560
  var MAX_LOGS = 500;
4446
4561
  var MAX_EVENTS = 500;
4447
- function createBuffers() {
4562
+ var COLLECTED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["xhr", "fetch", "document"]);
4563
+ function createBuffers(tabId = 0) {
4448
4564
  return {
4449
4565
  requests: [],
4450
4566
  logs: [],
4451
4567
  events: [],
4452
4568
  pending: /* @__PURE__ */ new Map(),
4453
4569
  requestCounter: 0,
4454
- requestBufferAccounting: createRequestBufferAccounting()
4570
+ requestBufferAccounting: createRequestBufferAccounting(),
4571
+ capture: {
4572
+ tabId,
4573
+ generation: createCaptureGeneration(),
4574
+ currentSequence: 0,
4575
+ droppedRequestsThroughSequence: 0,
4576
+ droppedLogsThroughSequence: 0
4577
+ }
4455
4578
  };
4456
4579
  }
4457
4580
  function parseQueryString(url2) {
@@ -4467,12 +4590,34 @@ function parseQueryString(url2) {
4467
4590
  function headersObjToArray(obj) {
4468
4591
  return Object.entries(obj).map(([name, value]) => ({ name, value }));
4469
4592
  }
4470
- function extractApiName(url2) {
4593
+ function extractMtopApiName(url2, postData) {
4471
4594
  try {
4472
4595
  const u = new URL(url2);
4473
- const match = u.pathname.match(/\/h5\/([^/]+\/[^/]+)/);
4474
- if (match) return match[1];
4475
- 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;
4476
4621
  } catch {
4477
4622
  return url2;
4478
4623
  }
@@ -4503,28 +4648,73 @@ function appendTrackingEvents(buffers, url2, postData, sequence) {
4503
4648
  }
4504
4649
  return true;
4505
4650
  }
4506
- function setupBufferListeners(client2, buffers) {
4507
- ensureCdpDomain(client2, "Network", {
4508
- maxTotalBufferSize: 10 * 1024 * 1024,
4509
- maxPostDataSize: 5 * 1024 * 1024
4510
- }).catch(() => {
4511
- });
4512
- ensureCdpDomain(client2, "Runtime").catch(() => {
4513
- });
4514
- ensureCdpDomain(client2, "Log").catch(() => {
4515
- });
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) {
4516
4681
  client2.on("Network.requestWillBeSent", (params) => {
4517
4682
  const url2 = params.request?.url ?? "";
4518
- 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
+ }
4519
4708
  const id = ++buffers.requestCounter;
4709
+ const captureSequence = nextCaptureSequence(buffers.capture);
4520
4710
  const partial2 = {
4521
4711
  id,
4522
4712
  requestId: params.requestId,
4523
- timestamp: new Date(ts * 1e3).toISOString(),
4524
- timestampMs: Math.floor(ts * 1e3),
4713
+ timestamp: new Date(timestampMs2).toISOString(),
4714
+ timestampMs: timestampMs2,
4525
4715
  method: params.request?.method ?? "GET",
4526
4716
  url: url2,
4527
- apiName: extractApiName(url2),
4717
+ apiName: mtopApiName ?? requestDisplayName(url2),
4528
4718
  headers: headersObjToArray(params.request?.headers ?? {}),
4529
4719
  postData: parsePostData(params.request?.postData),
4530
4720
  queryString: parseQueryString(url2),
@@ -4535,16 +4725,14 @@ function setupBufferListeners(client2, buffers) {
4535
4725
  isProxy: false,
4536
4726
  proxyRequestUrl: null,
4537
4727
  proxyData: null,
4538
- startTs: ts
4728
+ startTs: ts,
4729
+ captureSequence,
4730
+ isPending: true,
4731
+ source: isMtop ? "mtop" : "requests",
4732
+ resourceType
4539
4733
  };
4540
4734
  buffers.pending.set(params.requestId, partial2);
4541
- const captured = appendTrackingEvents(buffers, url2, params.request?.postData, id);
4542
- if (!captured && params.request?.hasPostData && !params.request?.postData) {
4543
- client2.sendCommand("Network.getRequestPostData", { requestId: params.requestId }, 1e4).then((result) => {
4544
- appendTrackingEvents(buffers, url2, result?.postData, id);
4545
- }).catch(() => {
4546
- });
4547
- }
4735
+ appendCapturedRequest(buffers, partial2);
4548
4736
  });
4549
4737
  client2.on("Network.responseReceived", (params) => {
4550
4738
  const partial2 = buffers.pending.get(params.requestId);
@@ -4552,73 +4740,110 @@ function setupBufferListeners(client2, buffers) {
4552
4740
  partial2.status = params.response?.status ?? 0;
4553
4741
  partial2.statusText = params.response?.statusText ?? "";
4554
4742
  partial2.responseHeaders = headersObjToArray(params.response?.headers ?? {});
4743
+ refreshCapturedRequest(buffers, partial2);
4555
4744
  });
4556
4745
  client2.on("Network.loadingFinished", (params) => {
4557
4746
  const partial2 = buffers.pending.get(params.requestId);
4558
4747
  if (!partial2) return;
4559
4748
  buffers.pending.delete(params.requestId);
4560
- const endTs = params.timestamp ?? Date.now() / 1e3;
4561
- const duration3 = Math.round((endTs - partial2.startTs) * 1e3);
4562
- const req = {
4563
- ...partial2,
4564
- duration: duration3,
4565
- isPending: false
4566
- };
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);
4567
4754
  client2.sendCommand("Network.getResponseBody", { requestId: params.requestId }, 1e4).then((result) => {
4568
- req.responseBody = result.base64Encoded ? Buffer.from(result.body, "base64").toString("utf-8") : result.body;
4569
- 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") {
4570
4757
  try {
4571
- req.responseBody = JSON.parse(req.responseBody);
4758
+ partial2.responseBody = JSON.parse(partial2.responseBody);
4572
4759
  } catch {
4573
4760
  }
4574
4761
  }
4575
- refreshRequestInBuffer(buffers.requests, buffers.requestBufferAccounting, req);
4762
+ refreshCapturedRequest(buffers, partial2);
4576
4763
  }).catch(() => {
4577
4764
  });
4578
- appendRequestToBuffer(buffers.requests, buffers.requestBufferAccounting, req);
4579
4765
  });
4580
4766
  client2.on("Network.loadingFailed", (params) => {
4581
4767
  const partial2 = buffers.pending.get(params.requestId);
4582
4768
  if (!partial2) return;
4583
4769
  buffers.pending.delete(params.requestId);
4584
- const req = {
4585
- ...partial2,
4586
- status: 0,
4587
- statusText: params.errorText ?? "Failed",
4588
- duration: 0,
4589
- isPending: false
4590
- };
4591
- 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);
4592
4775
  });
4593
4776
  client2.on("Runtime.consoleAPICalled", (params) => {
4594
- 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";
4595
4785
  const args = (params.args ?? []).map((arg) => {
4596
4786
  if (arg.type === "string") return arg.value;
4597
4787
  if (arg.value !== void 0) return String(arg.value);
4598
4788
  if (arg.description !== void 0) return arg.description;
4599
4789
  return JSON.stringify(arg);
4600
4790
  });
4601
- const entry = {
4791
+ appendCapturedLog(buffers, {
4602
4792
  level,
4603
4793
  message: args.join(" "),
4604
- timestamp: params.timestamp ? Math.floor(params.timestamp * 1e3) : Date.now(),
4605
- source: "console",
4606
- args
4607
- };
4608
- buffers.logs.push(entry);
4609
- 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
+ });
4610
4816
  });
4611
4817
  client2.on("Log.entryAdded", (params) => {
4612
- const entry = {
4613
- level: params.entry?.level ?? "log",
4614
- message: params.entry?.text ?? "",
4615
- timestamp: params.entry?.timestamp ? Math.floor(params.entry.timestamp * 1e3) : Date.now(),
4616
- source: params.entry?.source ?? "other",
4617
- args: [params.entry?.text ?? ""]
4818
+ const entry = params.entry ?? {};
4819
+ const levelMap = {
4820
+ verbose: "log",
4821
+ info: "info",
4822
+ warning: "warn",
4823
+ error: "error"
4618
4824
  };
4619
- buffers.logs.push(entry);
4620
- 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
+ });
4621
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
+ ]);
4622
4847
  }
4623
4848
 
4624
4849
  // src/cdp-backend/discover.ts
@@ -4777,9 +5002,21 @@ var CdpSession = class {
4777
5002
  } catch {
4778
5003
  client2 = await CdpClient.connect(target.webSocketDebuggerUrl);
4779
5004
  }
4780
- const buffers = createBuffers();
4781
- 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);
4782
5018
  const session = {
5019
+ tabId,
4783
5020
  client: client2,
4784
5021
  buffers,
4785
5022
  targetId: target.id,
@@ -6035,6 +6272,143 @@ async function enrichCdpPageChangeSetWithRefs(session, observation) {
6035
6272
  );
6036
6273
  }
6037
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
+
6038
6412
  // src/cdp-backend/composite-handlers.ts
6039
6413
  async function cdpRunActions(session, data, controlSource = "local") {
6040
6414
  const { actions, tabId } = data;
@@ -6110,21 +6484,42 @@ async function cdpRunActions(session, data, controlSource = "local") {
6110
6484
  };
6111
6485
  }
6112
6486
  async function cdpObservedPageAction(session, action, data, controlSource = "local") {
6113
- const { actionData, observe } = splitObservedActionPayload(data);
6114
- if (observe === false) {
6115
- return dispatchCdpAction(session, action, actionData, controlSource);
6116
- }
6487
+ const { actionData, observe, diagnostics } = splitObservedActionPayload(action, data);
6117
6488
  const { tabId } = actionData;
6118
6489
  if (typeof tabId !== "number") throw new Error(`${action} requires a numeric tabId`);
6119
6490
  const pageSession = await session.getPageSession(tabId);
6120
6491
  await ensureControlledIndicator(pageSession, controlSource);
6121
- 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(
6122
6514
  { action, actionData, observe },
6123
6515
  {
6124
6516
  startObservation: () => startPageObservation(pageSession),
6125
6517
  finishObservation: (checkpoint, options) => finishPageObservation(pageSession, checkpoint, options),
6126
6518
  startNavigationObserver: (options) => startCdpNavigationObserver(session, pageSession, options.urlBefore, options),
6127
- 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
+ },
6128
6523
  finalizeObservation: (observation) => {
6129
6524
  if (observation.mode === "delta") {
6130
6525
  return enrichCdpPageChangeSetWithRefs(pageSession, observation);
@@ -6134,6 +6529,11 @@ async function cdpObservedPageAction(session, action, data, controlSource = "loc
6134
6529
  disposeObservation: () => disposePageObservation(pageSession)
6135
6530
  }
6136
6531
  );
6532
+ if (!diagnostics || !diagnosticsCursor) return result;
6533
+ return {
6534
+ ...result,
6535
+ diagnostics: await collectCdpPageDiagnostics(pageSession, diagnosticsCursor, diagnostics)
6536
+ };
6137
6537
  }
6138
6538
  registerActions([{ name: "run_actions", session: "browser", handler: cdpRunActions }]);
6139
6539
 
@@ -6721,112 +7121,6 @@ function listCloudConnectors() {
6721
7121
  return { connectors: pushIfChanged() };
6722
7122
  }
6723
7123
 
6724
- // src/cdp-backend/request-handlers.ts
6725
- function filterRequests(buffers, opts) {
6726
- let requests = [...buffers.requests];
6727
- if (opts.since !== void 0) {
6728
- requests = requests.filter((r) => r.timestampMs >= opts.since);
6729
- }
6730
- if (opts.filter) {
6731
- const pattern = compileTextPattern(opts.filter);
6732
- requests = requests.filter(
6733
- (r) => matchesTextPattern(r.url, pattern) || matchesTextPattern(r.apiName, pattern)
6734
- );
6735
- }
6736
- if (opts.source === "mtop") {
6737
- requests = requests.filter((r) => r.url?.includes("/h5/") || r.url?.includes("mtop"));
6738
- }
6739
- const count = opts.count ?? 10;
6740
- return {
6741
- requests: requests.slice(-count),
6742
- totalMatched: requests.length
6743
- };
6744
- }
6745
- async function cdpGetRequests(session, data) {
6746
- const opts = {
6747
- count: data.count ?? 10,
6748
- filter: data.filter,
6749
- since: data.since,
6750
- source: data.source,
6751
- applyHiddenFilter: data.applyHiddenFilter !== false
6752
- };
6753
- const { requests, totalMatched } = filterRequests(session.buffers, opts);
6754
- const serializeOpts = {
6755
- includeBody: data.includeBody !== false,
6756
- includeHeaders: data.includeHeaders === true,
6757
- maxBodySize: data.maxBodySize ?? 5e4,
6758
- fields: data.fields,
6759
- keysOnly: data.keysOnly === true
6760
- };
6761
- return {
6762
- meta: {
6763
- count: requests.length,
6764
- totalMatched,
6765
- totalInBuffer: session.buffers.requests.length,
6766
- filter: data.filter,
6767
- since: data.since,
6768
- source: "cdp"
6769
- },
6770
- requests: requests.map((r) => serializeRequest(r, serializeOpts))
6771
- };
6772
- }
6773
- async function cdpGetLogs(session, data) {
6774
- const { level, since, limit = 100, filter } = data;
6775
- let logs = [...session.buffers.logs];
6776
- if (since !== void 0) {
6777
- logs = logs.filter((l) => l.timestamp >= since);
6778
- }
6779
- if (level && level !== "all") {
6780
- logs = logs.filter((l) => l.level === level);
6781
- }
6782
- if (filter) {
6783
- const f = filter.toLowerCase();
6784
- logs = logs.filter((l) => l.message?.toLowerCase().includes(f));
6785
- }
6786
- logs = logs.slice(-limit);
6787
- return {
6788
- meta: {
6789
- count: logs.length,
6790
- totalMatched: logs.length,
6791
- level: level || "all",
6792
- since,
6793
- filter,
6794
- source: "cdp"
6795
- },
6796
- logs
6797
- };
6798
- }
6799
- async function cdpGetEvents(session, data) {
6800
- const { source = "all", event_type, since, limit = 20, filter, includeRaw = false } = data;
6801
- const response = queryTrackingEvents(session.buffers.events, {
6802
- source,
6803
- event_type,
6804
- since,
6805
- limit,
6806
- filter
6807
- });
6808
- const captureStatus = response.totalInBuffer === 0 ? "armed" : "active";
6809
- const hint = response.events.length === 0 && captureStatus === "armed" ? TRACKING_CAPTURE_ARMED_HINT : void 0;
6810
- return {
6811
- meta: {
6812
- count: response.events.length,
6813
- totalMatched: response.totalMatched,
6814
- totalInBuffer: response.totalInBuffer,
6815
- source,
6816
- captureStatus,
6817
- captureSource: "cdp",
6818
- includeRaw,
6819
- hint
6820
- },
6821
- events: response.events.map((event) => formatTrackingEvent(event, includeRaw))
6822
- };
6823
- }
6824
- registerActions([
6825
- { name: "get_requests", session: "page", handler: cdpGetRequests },
6826
- { name: "get_logs", session: "page", handler: cdpGetLogs },
6827
- { name: "get_events", session: "page", handler: cdpGetEvents }
6828
- ]);
6829
-
6830
7124
  // src/cdp-backend/send-request.ts
6831
7125
  import { readFile } from "node:fs/promises";
6832
7126
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -6836,6 +7130,10 @@ function setHeader(headers, name, value) {
6836
7130
  if (existing && existing !== name) delete headers[existing];
6837
7131
  headers[name] = value;
6838
7132
  }
7133
+ function removeHeader(headers, name) {
7134
+ const existing = findSendRequestHeaderName(headers, name);
7135
+ if (existing) delete headers[existing];
7136
+ }
6839
7137
  function escapeMultipartQuotedValue(value) {
6840
7138
  return value.replace(/\\/g, "\\\\").replace(/"/g, "%22");
6841
7139
  }
@@ -6944,10 +7242,19 @@ async function executeNodeSendRequest(data, cookieMap = /* @__PURE__ */ new Map(
6944
7242
  Accept: "application/json, text/plain, */*",
6945
7243
  "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
6946
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",
6947
- Referer: targetUrl.origin + "/mearl",
6948
- Origin: targetUrl.origin,
6949
7245
  ...headers
6950
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
+ }
6951
7258
  const cookieHeader = [...cookieMap.entries()].map(([key, value]) => `${key}=${value}`).join("; ");
6952
7259
  if (cookieHeader) setHeader(requestHeaders, "Cookie", cookieHeader);
6953
7260
  const xsrfToken = cookieMap.get("XSRF-TOKEN");
@@ -7305,8 +7612,8 @@ registerActions([
7305
7612
  ]);
7306
7613
 
7307
7614
  // src/cdp-backend/tdbank-handlers.ts
7308
- async function cdpFetchTdbank(session, path13, options = {}) {
7309
- const url2 = new URL(`https://tdbank.alibaba-inc.com${path13}`);
7615
+ async function cdpFetchTdbank(session, path15, options = {}) {
7616
+ const url2 = new URL(`https://tdbank.alibaba-inc.com${path15}`);
7310
7617
  for (const [key, value] of Object.entries(options.params ?? {})) {
7311
7618
  if (value !== void 0 && value !== null) {
7312
7619
  url2.searchParams.set(key, String(value));
@@ -7574,6 +7881,14 @@ async function cdpTdbankAccount(cdpSession2, data) {
7574
7881
  registerActions([{ name: "tdbank_account", session: "browser", handler: cdpTdbankAccount }]);
7575
7882
 
7576
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
+ }
7577
7892
  async function cdpTabOpen(session, data, controlSource = "local") {
7578
7893
  const { url: url2, active = false, emulation, reuse = "never", match } = data;
7579
7894
  if (!url2 || typeof url2 !== "string") {
@@ -7600,12 +7915,14 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7600
7915
  const tabId2 = await session.ensureTargetRegistered(existing.targetId);
7601
7916
  const pageSession2 = await session.getPageSession(tabId2);
7602
7917
  await ensureControlledIndicator(pageSession2, controlSource);
7918
+ const capture2 = currentCapture(pageSession2);
7603
7919
  return {
7604
7920
  tabId: tabId2,
7605
7921
  url: existing.url ?? url2,
7606
7922
  title: existing.title ?? "",
7607
7923
  opened: false,
7608
- reused: true
7924
+ reused: true,
7925
+ capture: capture2
7609
7926
  };
7610
7927
  }
7611
7928
  if (reuse === "require") {
@@ -7620,6 +7937,7 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7620
7937
  const tabId = await session.registerTarget(targetId);
7621
7938
  const pageSession = await session.getPageSession(tabId);
7622
7939
  await ensureControlledIndicator(pageSession, controlSource);
7940
+ const capture = currentCapture(pageSession);
7623
7941
  if (emulationConfig) {
7624
7942
  for (const { method, params } of buildEnableEmulationCommands(emulationConfig)) {
7625
7943
  await pageSession.client.sendCommand(method, params);
@@ -7638,6 +7956,7 @@ async function cdpTabOpen(session, data, controlSource = "local") {
7638
7956
  title: targetInfo?.title ?? "",
7639
7957
  opened: true,
7640
7958
  reused: false,
7959
+ capture,
7641
7960
  ...emulationConfig && {
7642
7961
  emulation: {
7643
7962
  preset: emulationConfig.presetName,
@@ -9433,7 +9752,7 @@ import os2 from "node:os";
9433
9752
  import { randomUUID as randomUUID4 } from "node:crypto";
9434
9753
 
9435
9754
  // src/version.ts
9436
- var NATIVE_HOST_VERSION = true ? "2.2.2" : "unknown";
9755
+ var NATIVE_HOST_VERSION = true ? "2.4.0" : "unknown";
9437
9756
 
9438
9757
  // src/analytics.ts
9439
9758
  var MEASUREMENT_ID = "G-W84LJDZ6PH";
@@ -10294,18 +10613,19 @@ var AgentChatManager = class {
10294
10613
 
10295
10614
  // src/agent/codex-provider.ts
10296
10615
  import os3 from "node:os";
10297
- import path7 from "node:path";
10298
10616
  import readline from "node:readline";
10299
10617
  import { spawn as spawn6 } from "node:child_process";
10300
10618
 
10301
10619
  // src/agent/executable-resolver.ts
10302
10620
  import fs6 from "node:fs";
10303
10621
  import { spawn as spawn5 } from "node:child_process";
10622
+ import path7 from "node:path";
10304
10623
 
10305
10624
  // ../setup/dist/process.js
10306
10625
  import fs5 from "node:fs";
10307
10626
  import path6 from "node:path";
10308
10627
  import { spawn as spawn4 } from "node:child_process";
10628
+ var MAX_CAPTURE_LENGTH = 1e6;
10309
10629
  function isExecutable(filePath, platform = process.platform) {
10310
10630
  try {
10311
10631
  fs5.accessSync(filePath, platform === "win32" ? fs5.constants.F_OK : fs5.constants.X_OK);
@@ -10317,6 +10637,27 @@ function isExecutable(filePath, platform = process.platform) {
10317
10637
  function executableNames(name, platform = process.platform) {
10318
10638
  return platform === "win32" ? [`${name}.cmd`, `${name}.exe`, name] : [name];
10319
10639
  }
10640
+ function versionManagerExecutableCandidates(home, names, options = {}) {
10641
+ const env = options.env ?? process.env;
10642
+ const platform = options.platform ?? process.platform;
10643
+ const readdirSync2 = options.readdirSync ?? fs5.readdirSync;
10644
+ 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");
10645
+ const roots = [
10646
+ path6.join(env.NVM_DIR || path6.join(home, ".nvm"), "versions", "node"),
10647
+ path6.join(env.FNM_DIR || fnmDefaultRoot, "node-versions"),
10648
+ path6.join(home, ".fnm", "node-versions")
10649
+ ].filter((root, index, all) => all.indexOf(root) === index);
10650
+ return roots.flatMap((root) => {
10651
+ try {
10652
+ 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) => [
10653
+ path6.join(root, entry.name, "bin", name),
10654
+ path6.join(root, entry.name, "installation", "bin", name)
10655
+ ]));
10656
+ } catch {
10657
+ return [];
10658
+ }
10659
+ });
10660
+ }
10320
10661
  function resolveSpawnCommand(executable, args, platform = process.platform, commandInterpreter = process.env.ComSpec) {
10321
10662
  if (platform === "win32" && /\.(?:cmd|bat)$/i.test(executable)) {
10322
10663
  return {
@@ -10329,12 +10670,12 @@ function resolveSpawnCommand(executable, args, platform = process.platform, comm
10329
10670
  function prependCommandPath(command, env = process.env) {
10330
10671
  if (!path6.isAbsolute(command))
10331
10672
  return env;
10332
- return prependPath(path6.dirname(command), env);
10673
+ return prependPathDirectory(path6.dirname(command), env);
10333
10674
  }
10334
10675
  function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
10335
- return prependPath(path6.dirname(nodeExecutable), env);
10676
+ return prependPathDirectory(path6.dirname(nodeExecutable), env);
10336
10677
  }
10337
- function prependPath(directory, env) {
10678
+ function prependPathDirectory(directory, env = process.env) {
10338
10679
  const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10339
10680
  const inherited = env[pathKey] || "";
10340
10681
  const entries = inherited.split(path6.delimiter).filter(Boolean);
@@ -10342,8 +10683,101 @@ function prependPath(directory, env) {
10342
10683
  return env;
10343
10684
  return { ...env, [pathKey]: [directory, ...entries].join(path6.delimiter) };
10344
10685
  }
10686
+ function findExecutable(name, options = {}) {
10687
+ const env = options.env ?? process.env;
10688
+ const platform = options.platform ?? process.platform;
10689
+ const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
10690
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10691
+ const directories = [
10692
+ ...options.extraDirectories ?? [],
10693
+ ...(env[pathKey] ?? "").split(path6.delimiter).filter(Boolean)
10694
+ ];
10695
+ for (const directory of directories) {
10696
+ for (const executableName of executableNames(name, platform)) {
10697
+ const candidate = path6.join(directory, executableName);
10698
+ if (existsSync4(candidate))
10699
+ return candidate;
10700
+ }
10701
+ }
10702
+ return null;
10703
+ }
10704
+ var runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
10705
+ const capture = options.capture === true;
10706
+ const launch = resolveSpawnCommand(command, args);
10707
+ const child = spawn4(launch.command, launch.args, {
10708
+ cwd: options.cwd,
10709
+ env: options.env,
10710
+ stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
10711
+ windowsHide: true
10712
+ });
10713
+ let stdout = "";
10714
+ let stderr = "";
10715
+ let settled = false;
10716
+ const append = (current, chunk) => current.length >= MAX_CAPTURE_LENGTH ? current : (current + chunk.toString("utf8")).slice(0, MAX_CAPTURE_LENGTH);
10717
+ const finish = (error48, code = 1) => {
10718
+ if (settled)
10719
+ return;
10720
+ settled = true;
10721
+ if (timer)
10722
+ clearTimeout(timer);
10723
+ if (error48)
10724
+ reject(error48);
10725
+ else
10726
+ resolve({ code, stdout, stderr });
10727
+ };
10728
+ const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
10729
+ child.kill("SIGTERM");
10730
+ finish(new Error(`Command timed out after ${options.timeoutMs}ms`));
10731
+ }, options.timeoutMs);
10732
+ if (capture) {
10733
+ child.stdout?.on("data", (chunk) => stdout = append(stdout, chunk));
10734
+ child.stderr?.on("data", (chunk) => stderr = append(stderr, chunk));
10735
+ }
10736
+ child.once("error", (error48) => finish(error48));
10737
+ child.once("close", (code) => finish(void 0, code ?? 1));
10738
+ });
10345
10739
 
10346
10740
  // src/agent/executable-resolver.ts
10741
+ function agentExecutableCandidatePaths({
10742
+ executableNames: executableNames2,
10743
+ configuredPath,
10744
+ env = process.env,
10745
+ extraUserCandidates = [],
10746
+ home,
10747
+ platform = process.platform,
10748
+ processExecPath = process.execPath,
10749
+ readdirSync: readdirSync2
10750
+ }) {
10751
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10752
+ const pathCandidates = (env[pathKey] || "").split(path7.delimiter).filter(Boolean).flatMap((directory) => executableNames2.map((name) => path7.join(directory, name)));
10753
+ const commonUserDirectories = [
10754
+ path7.join(home, ".local", "bin"),
10755
+ path7.join(home, ".npm-global", "bin"),
10756
+ path7.join(home, ".volta", "bin"),
10757
+ path7.join(home, ".local", "share", "pnpm"),
10758
+ path7.join(home, "Library", "pnpm"),
10759
+ path7.join(home, ".bun", "bin"),
10760
+ ...platform === "win32" && env.APPDATA ? [path7.join(env.APPDATA, "npm")] : []
10761
+ ];
10762
+ const systemDirectories = platform === "win32" ? [] : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin"];
10763
+ return [
10764
+ configuredPath,
10765
+ ...executableNames2.map((name) => path7.join(path7.dirname(processExecPath), name)),
10766
+ ...commonUserDirectories.flatMap(
10767
+ (directory) => executableNames2.map((name) => path7.join(directory, name))
10768
+ ),
10769
+ ...extraUserCandidates,
10770
+ ...versionManagerExecutableCandidates(home, executableNames2, {
10771
+ env,
10772
+ platform,
10773
+ readdirSync: readdirSync2
10774
+ }),
10775
+ ...systemDirectories.flatMap(
10776
+ (directory) => executableNames2.map((name) => path7.join(directory, name))
10777
+ ),
10778
+ ...pathCandidates
10779
+ ].filter((candidate) => typeof candidate === "string");
10780
+ }
10347
10781
  var AgentExecutableResolver = class {
10348
10782
  constructor(options) {
10349
10783
  this.options = options;
@@ -10457,6 +10891,21 @@ var REQUEST_TIMEOUT_MS = 3e4;
10457
10891
  var PROBE_TIMEOUT_MS = 5e3;
10458
10892
  var MAX_MODEL_CHARS = 256;
10459
10893
  var CODEX_NOT_FOUND_ERROR = "Codex CLI was not found. Install @openai/codex or set MEARL_CODEX_PATH.";
10894
+ function codexExecutableCandidatePaths(options = {}) {
10895
+ const env = options.env ?? process.env;
10896
+ const home = (options.homedir ?? os3.homedir)();
10897
+ const platform = options.platform ?? process.platform;
10898
+ const executableName = platform === "win32" ? "codex.cmd" : "codex";
10899
+ return agentExecutableCandidatePaths({
10900
+ executableNames: [executableName],
10901
+ configuredPath: env.MEARL_CODEX_PATH,
10902
+ env,
10903
+ home,
10904
+ platform,
10905
+ processExecPath: options.processExecPath,
10906
+ readdirSync: options.readdirSync
10907
+ });
10908
+ }
10460
10909
  function codexModelInfo(value) {
10461
10910
  if (typeof value !== "string") return void 0;
10462
10911
  const id = value.trim().slice(0, MAX_MODEL_CHARS);
@@ -10938,18 +11387,7 @@ var CodexAgentProvider = class {
10938
11387
  return this.executableResolver.resolve();
10939
11388
  }
10940
11389
  resolveExecutableCandidates() {
10941
- const executableName = process.platform === "win32" ? "codex.cmd" : "codex";
10942
- const configured = process.env.MEARL_CODEX_PATH;
10943
- const pathCandidates = (process.env.PATH || "").split(path7.delimiter).filter(Boolean).map((dir) => path7.join(dir, executableName));
10944
- return executableFileCandidates([
10945
- configured,
10946
- path7.join(path7.dirname(process.execPath), executableName),
10947
- path7.join(os3.homedir(), ".local", "bin", executableName),
10948
- path7.join(os3.homedir(), ".npm-global", "bin", executableName),
10949
- path7.join(os3.homedir(), ".volta", "bin", executableName),
10950
- ...process.platform === "win32" ? [] : ["/opt/homebrew/bin/codex", "/usr/local/bin/codex", "/usr/bin/codex"],
10951
- ...pathCandidates
10952
- ]);
11390
+ return executableFileCandidates(codexExecutableCandidatePaths());
10953
11391
  }
10954
11392
  };
10955
11393
 
@@ -11779,10 +12217,10 @@ function mergeDefs(...defs) {
11779
12217
  function cloneDef(schema) {
11780
12218
  return mergeDefs(schema._zod.def);
11781
12219
  }
11782
- function getElementAtPath(obj, path13) {
11783
- if (!path13)
12220
+ function getElementAtPath(obj, path15) {
12221
+ if (!path15)
11784
12222
  return obj;
11785
- return path13.reduce((acc, key) => acc?.[key], obj);
12223
+ return path15.reduce((acc, key) => acc?.[key], obj);
11786
12224
  }
11787
12225
  function promiseAllObject(promisesObj) {
11788
12226
  const keys = Object.keys(promisesObj);
@@ -12165,11 +12603,11 @@ function aborted(x, startIndex = 0) {
12165
12603
  }
12166
12604
  return false;
12167
12605
  }
12168
- function prefixIssues(path13, issues) {
12606
+ function prefixIssues(path15, issues) {
12169
12607
  return issues.map((iss) => {
12170
12608
  var _a2;
12171
12609
  (_a2 = iss).path ?? (_a2.path = []);
12172
- iss.path.unshift(path13);
12610
+ iss.path.unshift(path15);
12173
12611
  return iss;
12174
12612
  });
12175
12613
  }
@@ -12352,7 +12790,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
12352
12790
  }
12353
12791
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
12354
12792
  const result = { errors: [] };
12355
- const processError = (error49, path13 = []) => {
12793
+ const processError = (error49, path15 = []) => {
12356
12794
  var _a2, _b;
12357
12795
  for (const issue2 of error49.issues) {
12358
12796
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12362,7 +12800,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12362
12800
  } else if (issue2.code === "invalid_element") {
12363
12801
  processError({ issues: issue2.issues }, issue2.path);
12364
12802
  } else {
12365
- const fullpath = [...path13, ...issue2.path];
12803
+ const fullpath = [...path15, ...issue2.path];
12366
12804
  if (fullpath.length === 0) {
12367
12805
  result.errors.push(mapper(issue2));
12368
12806
  continue;
@@ -12394,8 +12832,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12394
12832
  }
12395
12833
  function toDotPath(_path) {
12396
12834
  const segs = [];
12397
- const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12398
- for (const seg of path13) {
12835
+ const path15 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12836
+ for (const seg of path15) {
12399
12837
  if (typeof seg === "number")
12400
12838
  segs.push(`[${seg}]`);
12401
12839
  else if (typeof seg === "symbol")
@@ -24372,13 +24810,13 @@ function resolveRef(ref, ctx) {
24372
24810
  if (!ref.startsWith("#")) {
24373
24811
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24374
24812
  }
24375
- const path13 = ref.slice(1).split("/").filter(Boolean);
24376
- if (path13.length === 0) {
24813
+ const path15 = ref.slice(1).split("/").filter(Boolean);
24814
+ if (path15.length === 0) {
24377
24815
  return ctx.rootSchema;
24378
24816
  }
24379
24817
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24380
- if (path13[0] === defsKey) {
24381
- const key = path13[1];
24818
+ if (path15[0] === defsKey) {
24819
+ const key = path15[1];
24382
24820
  if (!key || !ctx.defs[key]) {
24383
24821
  throw new Error(`Reference not found: ${ref}`);
24384
24822
  }
@@ -28303,10 +28741,10 @@ function parseQoderSkillsList(output, cwd, home = os4.homedir()) {
28303
28741
  finish();
28304
28742
  return skills;
28305
28743
  }
28306
- function qoderManagedExecutableCandidates(home) {
28744
+ function qoderManagedExecutableCandidates(home, readdirSync2 = fs7.readdirSync) {
28307
28745
  const directory = path8.join(home, ".qoder", "bin", "qodercli");
28308
28746
  try {
28309
- return fs7.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));
28747
+ 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));
28310
28748
  } catch {
28311
28749
  return [];
28312
28750
  }
@@ -28321,6 +28759,25 @@ function isPermissionResponse(value) {
28321
28759
  function qoderInstallCommand(platform = process.platform) {
28322
28760
  return platform === "win32" ? "npm install -g @qoder-ai/qodercli" : "curl -fsSL https://qoder.com/install | bash";
28323
28761
  }
28762
+ function qoderExecutableCandidatePaths(options = {}) {
28763
+ const env = options.env ?? process.env;
28764
+ const home = (options.homedir ?? os4.homedir)();
28765
+ const platform = options.platform ?? process.platform;
28766
+ const executableNames2 = platform === "win32" ? ["qodercli.exe", "qodercli.cmd"] : ["qodercli"];
28767
+ return agentExecutableCandidatePaths({
28768
+ executableNames: executableNames2,
28769
+ configuredPath: env.MEARL_QODER_PATH,
28770
+ env,
28771
+ extraUserCandidates: [
28772
+ ...qoderManagedExecutableCandidates(home, options.readdirSync),
28773
+ ...executableNames2.map((name) => path8.join(home, ".qoder", "bin", name))
28774
+ ],
28775
+ home,
28776
+ platform,
28777
+ processExecPath: options.processExecPath,
28778
+ readdirSync: options.readdirSync
28779
+ });
28780
+ }
28324
28781
  function sessionOptions(options) {
28325
28782
  const cwd = path8.resolve(options.cwd || os4.homedir());
28326
28783
  return {
@@ -29112,19 +29569,7 @@ ${message}` : message;
29112
29569
  return this.executableResolver.resolve();
29113
29570
  }
29114
29571
  resolveExecutableCandidates() {
29115
- const executableNames2 = process.platform === "win32" ? ["qodercli.exe", "qodercli.cmd"] : ["qodercli"];
29116
- const configured = process.env.MEARL_QODER_PATH;
29117
- const home = os4.homedir();
29118
- const pathCandidates = (process.env.PATH || "").split(path8.delimiter).filter(Boolean).flatMap((dir) => executableNames2.map((name) => path8.join(dir, name)));
29119
- return executableFileCandidates([
29120
- configured,
29121
- ...executableNames2.map((name) => path8.join(path8.dirname(process.execPath), name)),
29122
- ...executableNames2.map((name) => path8.join(home, ".local", "bin", name)),
29123
- ...qoderManagedExecutableCandidates(home),
29124
- ...executableNames2.map((name) => path8.join(home, ".qoder", "bin", name)),
29125
- ...process.platform === "win32" ? [] : ["/opt/homebrew/bin/qodercli", "/usr/local/bin/qodercli", "/usr/bin/qodercli"],
29126
- ...pathCandidates
29127
- ]);
29572
+ return executableFileCandidates(qoderExecutableCandidatePaths());
29128
29573
  }
29129
29574
  emit(event) {
29130
29575
  for (const listener of this.listeners) listener(event);
@@ -29142,22 +29587,6 @@ var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g")
29142
29587
  function existingExecutable(candidates, existsSync4) {
29143
29588
  return candidates.find((candidate) => candidate && existsSync4(candidate)) ?? null;
29144
29589
  }
29145
- function versionManagerCandidates(home, executableNames2, readdirSync2) {
29146
- const roots = [
29147
- path9.join(home, ".nvm", "versions", "node"),
29148
- path9.join(home, ".fnm", "node-versions")
29149
- ];
29150
- return roots.flatMap((root) => {
29151
- try {
29152
- return readdirSync2(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((left, right) => right.name.localeCompare(left.name, void 0, { numeric: true })).flatMap((entry) => executableNames2.flatMap((name) => [
29153
- path9.join(root, entry.name, "bin", name),
29154
- path9.join(root, entry.name, "installation", "bin", name)
29155
- ]));
29156
- } catch {
29157
- return [];
29158
- }
29159
- });
29160
- }
29161
29590
  function stripAnsi(value) {
29162
29591
  return value.replace(ANSI_ESCAPE_PATTERN, "");
29163
29592
  }
@@ -29179,7 +29608,11 @@ function resolveAliSkillsExecutable(options = {}) {
29179
29608
  ...names.map((name) => path9.join(home, ".volta", "bin", name)),
29180
29609
  ...names.map((name) => path9.join(home, ".local", "share", "pnpm", name)),
29181
29610
  ...names.map((name) => path9.join(home, "Library", "pnpm", name)),
29182
- ...versionManagerCandidates(home, names, readdirSync2),
29611
+ ...versionManagerExecutableCandidates(home, names, {
29612
+ env,
29613
+ platform,
29614
+ readdirSync: readdirSync2
29615
+ }),
29183
29616
  ...platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
29184
29617
  ...pathCandidates
29185
29618
  ];
@@ -29390,6 +29823,409 @@ function createExtensionRequestMessage(id, action, data, trustedInternal = false
29390
29823
  };
29391
29824
  }
29392
29825
 
29826
+ // src/host-updater.ts
29827
+ import fs10 from "node:fs";
29828
+ import path11 from "node:path";
29829
+ import { fileURLToPath } from "node:url";
29830
+
29831
+ // ../setup/dist/packageManager.js
29832
+ import fs9 from "node:fs";
29833
+ import path10 from "node:path";
29834
+ function normalizePath(filePath) {
29835
+ return filePath.replaceAll("\\", "/").replace(/\/+$/, "");
29836
+ }
29837
+ function firstExisting(candidates, existsSync4) {
29838
+ return candidates.find((candidate) => existsSync4(candidate));
29839
+ }
29840
+ function fallbackCommand(name, platform) {
29841
+ if (platform !== "win32")
29842
+ return name;
29843
+ if (name === "bun" || name === "volta")
29844
+ return `${name}.exe`;
29845
+ return `${name}.cmd`;
29846
+ }
29847
+ function resolvePackageManagerInstallation(packageRoot, packageName, options = {}) {
29848
+ const env = options.env ?? process.env;
29849
+ const platform = options.platform ?? process.platform;
29850
+ const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29851
+ const processExecPath = options.processExecPath ?? process.execPath;
29852
+ const normalizedRoot = normalizePath(path10.resolve(packageRoot));
29853
+ const lowerRoot = normalizedRoot.toLowerCase();
29854
+ const lowerPackageName = packageName.toLowerCase();
29855
+ const pnpmGlobalRoot = /\/global\/\d+\/(?:\.pnpm\/.*\/)?node_modules\//.exec(lowerRoot);
29856
+ if (pnpmGlobalRoot && lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)) {
29857
+ const pnpmHome = normalizedRoot.slice(0, pnpmGlobalRoot.index);
29858
+ const command = firstExisting([
29859
+ ...executableNames("pnpm", platform).map((name) => path10.join(pnpmHome, name)),
29860
+ ...executableNames("pnpm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29861
+ ], existsSync4) ?? fallbackCommand("pnpm", platform);
29862
+ return { manager: "pnpm", command, binDirectory: pnpmHome };
29863
+ }
29864
+ const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;
29865
+ if (lowerRoot.endsWith(bunMarker)) {
29866
+ const bunHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.bun/"));
29867
+ const command = firstExisting(executableNames("bun", platform).map((name) => path10.join(bunHome, ".bun", "bin", name)), existsSync4) ?? fallbackCommand("bun", platform);
29868
+ return {
29869
+ manager: "bun",
29870
+ command,
29871
+ binDirectory: path10.join(bunHome, ".bun", "bin")
29872
+ };
29873
+ }
29874
+ if ((lowerRoot.includes("/.config/yarn/global/node_modules/") || lowerRoot.includes("/.yarn/global/node_modules/") || lowerRoot.includes("/yarn/global/node_modules/") || lowerRoot.includes("/yarn/data/global/node_modules/")) && lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)) {
29875
+ const layout = [
29876
+ { marker: "/.config/yarn/global/", binSegments: [".yarn", "bin"] },
29877
+ { marker: "/.yarn/global/", binSegments: [".yarn", "bin"] },
29878
+ { marker: "/yarn/data/global/", binSegments: ["yarn", "bin"] },
29879
+ { marker: "/yarn/global/", binSegments: ["yarn", "bin"] }
29880
+ ].find((candidate) => lowerRoot.includes(candidate.marker));
29881
+ const prefix = normalizedRoot.slice(0, lowerRoot.indexOf(layout.marker));
29882
+ const binDirectory = path10.join(prefix, ...layout.binSegments);
29883
+ const command = firstExisting([
29884
+ ...executableNames("yarn", platform).map((name) => path10.join(binDirectory, name)),
29885
+ ...executableNames("yarn", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29886
+ ], existsSync4) ?? findExecutable("yarn", { env, existsSync: existsSync4, platform }) ?? fallbackCommand("yarn", platform);
29887
+ return { manager: "yarn", command, binDirectory };
29888
+ }
29889
+ if (lowerRoot.includes("/.volta/tools/image/packages/") && lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)) {
29890
+ const voltaHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.volta/"));
29891
+ const command = firstExisting(executableNames("volta", platform).map((name) => path10.join(voltaHome, ".volta", "bin", name)), existsSync4) ?? fallbackCommand("volta", platform);
29892
+ return {
29893
+ manager: "volta",
29894
+ command,
29895
+ binDirectory: path10.join(voltaHome, ".volta", "bin")
29896
+ };
29897
+ }
29898
+ const npmMarker = `/lib/node_modules/${lowerPackageName}`;
29899
+ if (lowerRoot.endsWith(npmMarker)) {
29900
+ const prefix = normalizedRoot.slice(0, lowerRoot.length - npmMarker.length);
29901
+ const command = firstExisting([
29902
+ ...executableNames("npm", platform).map((name) => path10.join(prefix, "bin", name)),
29903
+ ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29904
+ ], existsSync4) ?? fallbackCommand("npm", platform);
29905
+ return { manager: "npm", command, binDirectory: path10.join(prefix, "bin") };
29906
+ }
29907
+ if (platform === "win32" && env.APPDATA) {
29908
+ const npmRoot = normalizePath(path10.join(env.APPDATA, "npm", "node_modules", packageName));
29909
+ if (lowerRoot === npmRoot.toLowerCase()) {
29910
+ const binDirectory = path10.join(env.APPDATA, "npm");
29911
+ const command = firstExisting([
29912
+ ...executableNames("npm", platform).map((name) => path10.join(binDirectory, name)),
29913
+ ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29914
+ ], existsSync4) ?? fallbackCommand("npm", platform);
29915
+ return { manager: "npm", command, binDirectory };
29916
+ }
29917
+ }
29918
+ return null;
29919
+ }
29920
+
29921
+ // ../setup/dist/version.js
29922
+ var MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
29923
+ var RELEASE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/;
29924
+ var RELEASE_LINE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.x$/;
29925
+ var RELEASE_COMPONENT_MAXIMUM = 65535;
29926
+ function parseComponent(value) {
29927
+ const component = Number(value);
29928
+ return Number.isSafeInteger(component) && component <= RELEASE_COMPONENT_MAXIMUM ? component : null;
29929
+ }
29930
+ function parseMearlReleaseVersion(value) {
29931
+ if (typeof value !== "string" || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
29932
+ return null;
29933
+ const match = RELEASE_VERSION_PATTERN.exec(value);
29934
+ if (!match)
29935
+ return null;
29936
+ const major = parseComponent(match[1]);
29937
+ const minor = parseComponent(match[2]);
29938
+ const patch = parseComponent(match[3]);
29939
+ const beta = match[4] === void 0 ? void 0 : parseComponent(match[4]);
29940
+ if (major === null || minor === null || patch === null || beta === null)
29941
+ return null;
29942
+ return {
29943
+ raw: value,
29944
+ major,
29945
+ minor,
29946
+ patch,
29947
+ channel: beta === void 0 ? "stable" : "beta",
29948
+ ...beta === void 0 ? {} : { beta }
29949
+ };
29950
+ }
29951
+ function isMearlReleaseVersion(value) {
29952
+ return parseMearlReleaseVersion(value) !== null;
29953
+ }
29954
+ function parseMearlReleaseLine(value) {
29955
+ if (typeof value !== "string" || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
29956
+ return null;
29957
+ const match = RELEASE_LINE_PATTERN.exec(value);
29958
+ if (!match)
29959
+ return null;
29960
+ const major = parseComponent(match[1]);
29961
+ const minor = parseComponent(match[2]);
29962
+ if (major === null || minor === null)
29963
+ return null;
29964
+ return { raw: value, major, minor };
29965
+ }
29966
+ function mearlReleaseLineForVersion(version2) {
29967
+ const parsed = parseMearlReleaseVersion(version2);
29968
+ if (!parsed)
29969
+ throw new Error("A valid Mearl release version is required");
29970
+ return `${parsed.major}.${parsed.minor}.x`;
29971
+ }
29972
+ function isMearlReleaseVersionInLine(version2, releaseLine) {
29973
+ const parsedVersion = parseMearlReleaseVersion(version2);
29974
+ const parsedLine = parseMearlReleaseLine(releaseLine);
29975
+ return parsedVersion !== null && parsedLine !== null && parsedVersion.major === parsedLine.major && parsedVersion.minor === parsedLine.minor;
29976
+ }
29977
+ function compareMearlReleaseLines(leftVersion, rightVersion) {
29978
+ const left = parseMearlReleaseVersion(leftVersion);
29979
+ const right = parseMearlReleaseVersion(rightVersion);
29980
+ if (!left || !right) {
29981
+ throw new Error("Mearl release-line comparison requires valid release versions");
29982
+ }
29983
+ for (const key of ["major", "minor"]) {
29984
+ if (left[key] < right[key])
29985
+ return -1;
29986
+ if (left[key] > right[key])
29987
+ return 1;
29988
+ }
29989
+ return 0;
29990
+ }
29991
+
29992
+ // src/host-updater.ts
29993
+ var NATIVE_HOST_UPDATE_TIMEOUT_MS = 5 * 6e4;
29994
+ var UPDATE_LOCK_PATH = path11.join(DAEMON_HOME, "native-host-update.lock");
29995
+ var UPDATE_LOCK_POLL_MS = 500;
29996
+ var INVALID_UPDATE_LOCK_GRACE_MS = 1e4;
29997
+ var NativeHostUpdateFailure = class extends Error {
29998
+ constructor(updateError, message) {
29999
+ super(message);
30000
+ this.updateError = updateError;
30001
+ this.name = "NativeHostUpdateFailure";
30002
+ }
30003
+ };
30004
+ function wait(delayMs) {
30005
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
30006
+ }
30007
+ function readLockState(lockPath) {
30008
+ try {
30009
+ const stat2 = fs10.lstatSync(lockPath);
30010
+ if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.size > 4096) {
30011
+ return { modifiedAt: stat2.mtimeMs, owner: null };
30012
+ }
30013
+ try {
30014
+ const value = JSON.parse(fs10.readFileSync(lockPath, "utf8"));
30015
+ const owner = Number.isInteger(value.pid) && value.pid > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && value.startedAt > 0 ? { pid: value.pid, startedAt: value.startedAt } : null;
30016
+ return { modifiedAt: stat2.mtimeMs, owner };
30017
+ } catch {
30018
+ return { modifiedAt: stat2.mtimeMs, owner: null };
30019
+ }
30020
+ } catch {
30021
+ return null;
30022
+ }
30023
+ }
30024
+ async function withNativeHostUpdateLock(lockPath, targetVersion, operation) {
30025
+ fs10.mkdirSync(path11.dirname(lockPath), { recursive: true, mode: 448 });
30026
+ const startedAt = Date.now();
30027
+ const owner = `${JSON.stringify({ pid: process.pid, startedAt, targetVersion })}
30028
+ `;
30029
+ while (true) {
30030
+ try {
30031
+ const handle = fs10.openSync(lockPath, "wx", 384);
30032
+ try {
30033
+ fs10.writeFileSync(handle, owner, "utf8");
30034
+ } finally {
30035
+ fs10.closeSync(handle);
30036
+ }
30037
+ break;
30038
+ } catch (error48) {
30039
+ if (!(error48 instanceof Error) || !("code" in error48) || error48.code !== "EEXIST") throw error48;
30040
+ const existing = readLockState(lockPath);
30041
+ const staleAfter = existing?.owner ? NATIVE_HOST_UPDATE_TIMEOUT_MS : INVALID_UPDATE_LOCK_GRACE_MS;
30042
+ const referenceTime = existing?.owner?.startedAt ?? existing?.modifiedAt;
30043
+ const expired = referenceTime !== void 0 && Date.now() - referenceTime > staleAfter;
30044
+ const ownerAlive = existing?.owner ? isAlive(existing.owner.pid) : false;
30045
+ if (expired && !ownerAlive) {
30046
+ try {
30047
+ fs10.unlinkSync(lockPath);
30048
+ continue;
30049
+ } catch {
30050
+ }
30051
+ }
30052
+ if (Date.now() - startedAt > NATIVE_HOST_UPDATE_TIMEOUT_MS + 1e4) {
30053
+ throw new NativeHostUpdateFailure(
30054
+ "lock-timeout",
30055
+ "Another Mearl Native Host update did not finish in time"
30056
+ );
30057
+ }
30058
+ await wait(UPDATE_LOCK_POLL_MS);
30059
+ }
30060
+ }
30061
+ try {
30062
+ return await operation();
30063
+ } finally {
30064
+ try {
30065
+ if (fs10.readFileSync(lockPath, "utf8") === owner) fs10.unlinkSync(lockPath);
30066
+ } catch {
30067
+ }
30068
+ }
30069
+ }
30070
+ function classifyUpdateFailure(output) {
30071
+ if (/\b(?:E404|ETARGET)\b|no matching version found|404\s+not found|package.+not found/i.test(
30072
+ output
30073
+ )) {
30074
+ return "package-unavailable";
30075
+ }
30076
+ if (/\b(?:EAI_AGAIN|ECONNREFUSED|ECONNRESET|ENETUNREACH|ENOTFOUND|ERR_SOCKET_TIMEOUT)\b|fetch failed|network request/i.test(
30077
+ output
30078
+ )) {
30079
+ return "network";
30080
+ }
30081
+ return "setup-failed";
30082
+ }
30083
+ function nativeHostUpdateCommand(targetVersion) {
30084
+ if (!isMearlReleaseVersion(targetVersion)) {
30085
+ throw new Error("The Native Host update target must be a valid Mearl release");
30086
+ }
30087
+ const releaseLine = mearlReleaseLineForVersion(targetVersion);
30088
+ const packageSpec = `@mearl/setup@${releaseLine}`;
30089
+ const args = [
30090
+ "--yes",
30091
+ packageSpec,
30092
+ "update",
30093
+ "--local",
30094
+ "--yes",
30095
+ "--target-line",
30096
+ releaseLine,
30097
+ "--skip-skill"
30098
+ ];
30099
+ return {
30100
+ args
30101
+ };
30102
+ }
30103
+ function currentNativeHostPackageRoot(entryPath) {
30104
+ let current = path11.dirname(entryPath);
30105
+ for (let depth = 0; depth < 12; depth += 1) {
30106
+ try {
30107
+ const manifest = JSON.parse(fs10.readFileSync(path11.join(current, "package.json"), "utf8"));
30108
+ if (manifest.name === "@mearl/native-host") return current;
30109
+ } catch {
30110
+ }
30111
+ const parent = path11.dirname(current);
30112
+ if (parent === current) break;
30113
+ current = parent;
30114
+ }
30115
+ return null;
30116
+ }
30117
+ function currentPackageManagerInstallation(environment, nodePath, platform) {
30118
+ const packageRoot = currentNativeHostPackageRoot(fileURLToPath(import.meta.url));
30119
+ if (!packageRoot) return null;
30120
+ return resolvePackageManagerInstallation(packageRoot, "@mearl/native-host", {
30121
+ env: environment,
30122
+ platform,
30123
+ processExecPath: nodePath
30124
+ });
30125
+ }
30126
+ async function installedLocalPackagesMatch(targetReleaseLine, environment, runner) {
30127
+ const installedVersions = [];
30128
+ for (const executableName of ["mearl-native-host", "mearl"]) {
30129
+ const executable = findExecutable(executableName, { env: environment });
30130
+ if (!executable) return false;
30131
+ try {
30132
+ const result = await runner(executable, ["--version"], {
30133
+ capture: true,
30134
+ env: environment,
30135
+ timeoutMs: 5e3
30136
+ });
30137
+ const installedVersion = result.stdout.trim();
30138
+ if (result.code !== 0 || !isMearlReleaseVersionInLine(installedVersion, targetReleaseLine)) {
30139
+ return false;
30140
+ }
30141
+ installedVersions.push(installedVersion);
30142
+ } catch {
30143
+ return false;
30144
+ }
30145
+ }
30146
+ return new Set(installedVersions).size === 1;
30147
+ }
30148
+ async function runNativeHostUpdate(targetVersion, options = {}) {
30149
+ const command = nativeHostUpdateCommand(targetVersion);
30150
+ const targetReleaseLine = mearlReleaseLineForVersion(targetVersion);
30151
+ const environment = options.environment ?? process.env;
30152
+ const platform = options.platform ?? process.platform;
30153
+ const nodePath = options.nodePath ?? process.execPath;
30154
+ const currentInstallation = currentPackageManagerInstallation(environment, nodePath, platform);
30155
+ const managerCommand = options.packageManagerCommand === void 0 ? currentInstallation?.command ?? null : options.packageManagerCommand;
30156
+ const packageBinDirectory = options.packageBinDirectory === void 0 ? currentInstallation?.binDirectory ?? null : options.packageBinDirectory;
30157
+ let childEnvironment = prependNodePath(environment, nodePath);
30158
+ if (managerCommand) childEnvironment = prependCommandPath(managerCommand, childEnvironment);
30159
+ if (packageBinDirectory) {
30160
+ childEnvironment = prependPathDirectory(packageBinDirectory, childEnvironment);
30161
+ }
30162
+ const packageRunner = options.packageRunner ?? findExecutable("npx", {
30163
+ env: childEnvironment,
30164
+ extraDirectories: [path11.dirname(nodePath)],
30165
+ platform
30166
+ });
30167
+ if (!packageRunner) {
30168
+ throw new NativeHostUpdateFailure("package-unavailable", "The package runner is unavailable");
30169
+ }
30170
+ const runner = options.runner ?? runCommand;
30171
+ const areTargetsInstalled = options.areTargetsInstalled ?? ((version2, env) => installedLocalPackagesMatch(version2, env, runner));
30172
+ const withUpdateLock = options.withUpdateLock ?? ((version2, operation) => withNativeHostUpdateLock(options.updateLockPath ?? UPDATE_LOCK_PATH, version2, operation));
30173
+ await withUpdateLock(targetVersion, async () => {
30174
+ let result;
30175
+ try {
30176
+ result = await runner(packageRunner, command.args, {
30177
+ capture: true,
30178
+ env: childEnvironment,
30179
+ timeoutMs: options.timeoutMs ?? NATIVE_HOST_UPDATE_TIMEOUT_MS
30180
+ });
30181
+ } catch (error48) {
30182
+ const message = error48 instanceof Error ? error48.message : String(error48);
30183
+ const classified = classifyUpdateFailure(message);
30184
+ throw new NativeHostUpdateFailure(
30185
+ /timed out/i.test(message) ? "timeout" : classified === "setup-failed" ? "unknown" : classified,
30186
+ "The Native Host update process did not complete"
30187
+ );
30188
+ }
30189
+ if (result.code !== 0) {
30190
+ throw new NativeHostUpdateFailure(
30191
+ classifyUpdateFailure(`${result.stderr.slice(0, 8192)}
30192
+ ${result.stdout.slice(0, 8192)}`),
30193
+ "The Mearl setup release line did not complete successfully"
30194
+ );
30195
+ }
30196
+ if (!await areTargetsInstalled(targetReleaseLine, childEnvironment)) {
30197
+ throw new NativeHostUpdateFailure(
30198
+ "verification-failed",
30199
+ "The installed Native Host and client do not match the Extension release line"
30200
+ );
30201
+ }
30202
+ });
30203
+ }
30204
+ var NativeHostUpdateCoordinator = class {
30205
+ constructor(options) {
30206
+ this.options = options;
30207
+ }
30208
+ #automaticAttempted = false;
30209
+ #operation = null;
30210
+ evaluate(extensionVersion2, checkForUpdates) {
30211
+ if (checkForUpdates !== true || this.#automaticAttempted || this.#operation) return;
30212
+ if (!isMearlReleaseVersion(extensionVersion2) || !isMearlReleaseVersion(this.options.hostVersion)) {
30213
+ this.options.log("Skipped Native Host update check because a release version was invalid");
30214
+ return;
30215
+ }
30216
+ const targetVersion = extensionVersion2;
30217
+ if (compareMearlReleaseLines(this.options.hostVersion, targetVersion) >= 0) return;
30218
+ this.#automaticAttempted = true;
30219
+ const operation = this.options.runUpdate(targetVersion).then(() => this.options.requestRestart()).catch((error48) => {
30220
+ const category = error48 instanceof NativeHostUpdateFailure ? error48.updateError : "unknown";
30221
+ this.options.log(`Native Host automatic update failed (${category})`);
30222
+ }).finally(() => {
30223
+ if (this.#operation === operation) this.#operation = null;
30224
+ });
30225
+ this.#operation = operation;
30226
+ }
30227
+ };
30228
+
29393
30229
  // src/index.ts
29394
30230
  var cliArgs = process.argv.slice(2);
29395
30231
  var cliArg = cliArgs[0];
@@ -29456,11 +30292,11 @@ Examples:
29456
30292
  process.exit(0);
29457
30293
  }
29458
30294
  function socketPathFor(suffix) {
29459
- return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` : path12.join(os7.tmpdir(), `mearl-agent${suffix}.sock`);
30295
+ return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` : path14.join(os7.tmpdir(), `mearl-agent${suffix}.sock`);
29460
30296
  }
29461
30297
  var LEGACY_SOCKET_PATH = socketPathFor("");
29462
30298
  var INSTANCE_SOCKET_PATH = socketPathFor(`-${process.pid}`);
29463
- var LOG_FILE = path12.join(os7.tmpdir(), "mearl-native-host.log");
30299
+ var LOG_FILE = path14.join(os7.tmpdir(), "mearl-native-host.log");
29464
30300
  var DEFAULT_TIMEOUT_MS = 15e3;
29465
30301
  var INVENTORY_IDLE_EXIT_MS = 1e3;
29466
30302
  var MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
@@ -29559,7 +30395,7 @@ function log(...args) {
29559
30395
  const msg = `[${(/* @__PURE__ */ new Date()).toISOString()}] [NativeHost] ${args.join(" ")}
29560
30396
  `;
29561
30397
  try {
29562
- fs11.appendFileSync(LOG_FILE, msg);
30398
+ fs13.appendFileSync(LOG_FILE, msg);
29563
30399
  } catch {
29564
30400
  }
29565
30401
  }
@@ -29571,6 +30407,14 @@ function sendToExtension(message) {
29571
30407
  process.stdout.write(header);
29572
30408
  process.stdout.write(json2);
29573
30409
  }
30410
+ var nativeHostUpdateCoordinator = new NativeHostUpdateCoordinator({
30411
+ hostVersion: NATIVE_HOST_VERSION,
30412
+ log,
30413
+ runUpdate: (targetVersion) => runNativeHostUpdate(targetVersion),
30414
+ requestRestart: () => {
30415
+ restartAfterUpdate();
30416
+ }
30417
+ });
29574
30418
  var agentChatManager = null;
29575
30419
  var agentSkillManager = new AgentSkillManager();
29576
30420
  function getAgentChatManager() {
@@ -29681,6 +30525,7 @@ function handleExtensionMessage(message) {
29681
30525
  extensionVersion: extensionVersion ?? void 0
29682
30526
  });
29683
30527
  sendToExtension({ type: "pong", timestamp: Date.now(), version: NATIVE_HOST_VERSION });
30528
+ nativeHostUpdateCoordinator.evaluate(message.version, message.checkForUpdates);
29684
30529
  return;
29685
30530
  }
29686
30531
  if (message && message.type === "browser_focus") {
@@ -29964,7 +30809,7 @@ async function handleSocketLine(socket, line) {
29964
30809
  return result2.cookies ?? [];
29965
30810
  } : (domains) => cdpExportCookies(getCdpSession(), domains);
29966
30811
  const result = await launchManagedBrowser(data, {
29967
- nativeHostScript: fileURLToPath2(import.meta.url),
30812
+ nativeHostScript: fileURLToPath3(import.meta.url),
29968
30813
  prepareTdbankSso,
29969
30814
  exportCookies,
29970
30815
  log
@@ -30093,7 +30938,7 @@ function onSocketConnection(socket) {
30093
30938
  }
30094
30939
  function isPathAlive(socketPath) {
30095
30940
  return new Promise((resolve) => {
30096
- if (process.platform !== "win32" && !fs11.existsSync(socketPath)) {
30941
+ if (process.platform !== "win32" && !fs13.existsSync(socketPath)) {
30097
30942
  resolve(false);
30098
30943
  return;
30099
30944
  }
@@ -30116,9 +30961,9 @@ function isPathAlive(socketPath) {
30116
30961
  }
30117
30962
  function listenOn(socketPath) {
30118
30963
  return new Promise((resolve, reject) => {
30119
- if (process.platform !== "win32" && fs11.existsSync(socketPath)) {
30964
+ if (process.platform !== "win32" && fs13.existsSync(socketPath)) {
30120
30965
  try {
30121
- fs11.unlinkSync(socketPath);
30966
+ fs13.unlinkSync(socketPath);
30122
30967
  } catch {
30123
30968
  }
30124
30969
  }
@@ -30127,11 +30972,11 @@ function listenOn(socketPath) {
30127
30972
  server.listen(socketPath, () => {
30128
30973
  if (process.platform !== "win32") {
30129
30974
  try {
30130
- fs11.chmodSync(socketPath, 384);
30975
+ fs13.chmodSync(socketPath, 384);
30131
30976
  } catch (error48) {
30132
30977
  server.close();
30133
30978
  try {
30134
- fs11.unlinkSync(socketPath);
30979
+ fs13.unlinkSync(socketPath);
30135
30980
  } catch {
30136
30981
  }
30137
30982
  reject(error48);
@@ -30167,7 +31012,7 @@ function releaseLegacySocket() {
30167
31012
  }
30168
31013
  if (process.platform !== "win32") {
30169
31014
  try {
30170
- if (fs11.existsSync(LEGACY_SOCKET_PATH)) fs11.unlinkSync(LEGACY_SOCKET_PATH);
31015
+ if (fs13.existsSync(LEGACY_SOCKET_PATH)) fs13.unlinkSync(LEGACY_SOCKET_PATH);
30171
31016
  } catch {
30172
31017
  }
30173
31018
  }
@@ -30179,8 +31024,8 @@ async function startSocketServer() {
30179
31024
  function cleanupInstanceSocketFile() {
30180
31025
  if (process.platform === "win32") return;
30181
31026
  try {
30182
- if (fs11.existsSync(INSTANCE_SOCKET_PATH)) {
30183
- fs11.unlinkSync(INSTANCE_SOCKET_PATH);
31027
+ if (fs13.existsSync(INSTANCE_SOCKET_PATH)) {
31028
+ fs13.unlinkSync(INSTANCE_SOCKET_PATH);
30184
31029
  }
30185
31030
  } catch (error48) {
30186
31031
  log("cleanup socket file failed:", String(error48));
@@ -30207,6 +31052,11 @@ function cleanupAndExit(code) {
30207
31052
  }
30208
31053
  finish();
30209
31054
  }
31055
+ function restartAfterUpdate() {
31056
+ const forcedExit = setTimeout(() => process.exit(0), 2e3);
31057
+ forcedExit.unref();
31058
+ cleanupAndExit(0);
31059
+ }
30210
31060
  async function main() {
30211
31061
  initBrowserRegistry({ log });
30212
31062
  pruneDeadBrowsers();