@mearl/native-host 2.3.0 → 2.5.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/agentbay-browser.d.ts +91 -0
- package/dist/browser-inventory.d.ts +2 -1
- package/dist/cdp-backend/cdp-session.d.ts +10 -4
- package/dist/cdp-backend/client.d.ts +8 -21
- package/dist/cdp-backend/cookie-transfer.d.ts +5 -0
- package/dist/host-updater.d.ts +39 -0
- package/dist/index.js +1265 -414
- package/dist/managed-browser.d.ts +6 -0
- package/package.json +9 -4
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
|
|
13
|
+
import fs11 from "node:fs";
|
|
14
14
|
import os6 from "node:os";
|
|
15
|
-
import
|
|
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
|
|
22
|
+
return path12.join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts");
|
|
23
23
|
case "linux":
|
|
24
|
-
return
|
|
24
|
+
return path12.join(home, ".config/google-chrome/NativeMessagingHosts");
|
|
25
25
|
case "win32":
|
|
26
|
-
return
|
|
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,23 +46,23 @@ function uninstallNativeHostArtifacts(options = {}, dependencies = {}) {
|
|
|
46
46
|
writeLine(line);
|
|
47
47
|
};
|
|
48
48
|
try {
|
|
49
|
-
const
|
|
49
|
+
const existsSync5 = 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 ??
|
|
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
|
-
|
|
62
|
-
|
|
61
|
+
path12.join(hostDir, `${hostName}.json`),
|
|
62
|
+
path12.join(hostDir, `${hostName}-launcher.${launcherExtension}`)
|
|
63
63
|
];
|
|
64
64
|
for (const artifact of artifacts) {
|
|
65
|
-
if (!
|
|
65
|
+
if (!existsSync5(artifact))
|
|
66
66
|
continue;
|
|
67
67
|
unlinkSync(artifact);
|
|
68
68
|
log2(`✅ Removed: ${artifact}`);
|
|
@@ -94,17 +94,17 @@ __export(install_exports, {
|
|
|
94
94
|
runCli: () => runCli,
|
|
95
95
|
uninstallNativeHost: () => uninstallNativeHost
|
|
96
96
|
});
|
|
97
|
-
import
|
|
98
|
-
import
|
|
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 fileURLToPath3 } from "node:url";
|
|
102
102
|
function resolveHostDir() {
|
|
103
103
|
return resolveNativeHostDirectory();
|
|
104
104
|
}
|
|
105
105
|
function resolveNativeHostPath() {
|
|
106
|
-
const siblingPath =
|
|
107
|
-
if (
|
|
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 =
|
|
126
|
+
const nodePath = path13.resolve(process.execPath);
|
|
127
127
|
if (process.platform === "win32") {
|
|
128
|
-
const launcherPath2 =
|
|
128
|
+
const launcherPath2 = path13.join(hostDir, `${hostName}-launcher.cmd`);
|
|
129
129
|
const content2 = `@echo off\r
|
|
130
130
|
"${nodePath}" "${hostPath}" %*\r
|
|
131
131
|
`;
|
|
132
|
-
|
|
132
|
+
fs12.writeFileSync(launcherPath2, content2, "utf-8");
|
|
133
133
|
return launcherPath2;
|
|
134
134
|
}
|
|
135
|
-
const launcherPath =
|
|
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
|
-
|
|
144
|
-
|
|
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 =
|
|
171
|
-
if (
|
|
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 (!
|
|
177
|
-
|
|
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 =
|
|
192
|
-
|
|
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
|
-
|
|
197
|
+
fs12.chmodSync(manifestPath, 420);
|
|
198
198
|
}
|
|
199
199
|
log2(`✅ Manifest installed: ${manifestPath}`);
|
|
200
200
|
log2(`✅ Extension ID: ${extensionId}`);
|
|
@@ -306,26 +306,25 @@ var init_install = __esm({
|
|
|
306
306
|
"use strict";
|
|
307
307
|
init_nativeHost();
|
|
308
308
|
DEFAULT_EXTENSION_ID = "aoehhjnofngknnjefamjbplchbolghkm";
|
|
309
|
-
__filename =
|
|
310
|
-
__dirname =
|
|
309
|
+
__filename = fileURLToPath3(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
|
|
318
|
-
import
|
|
317
|
+
import fs13 from "node:fs";
|
|
318
|
+
import http4 from "node:http";
|
|
319
319
|
import os7 from "node:os";
|
|
320
|
-
import
|
|
321
|
-
import { fileURLToPath as
|
|
320
|
+
import path14 from "node:path";
|
|
321
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
322
322
|
|
|
323
323
|
// src/cdp-backend/cdp-session.ts
|
|
324
|
-
import
|
|
324
|
+
import http2 from "node:http";
|
|
325
325
|
|
|
326
326
|
// src/cdp-backend/client.ts
|
|
327
|
-
import
|
|
328
|
-
import crypto2 from "node:crypto";
|
|
327
|
+
import WebSocket from "ws";
|
|
329
328
|
var CdpChildSession = class {
|
|
330
329
|
constructor(parent, sessionId) {
|
|
331
330
|
this.parent = parent;
|
|
@@ -344,199 +343,89 @@ var CdpChildSession = class {
|
|
|
344
343
|
}
|
|
345
344
|
};
|
|
346
345
|
var CdpClient = class _CdpClient {
|
|
347
|
-
socket
|
|
348
|
-
|
|
346
|
+
constructor(socket) {
|
|
347
|
+
this.socket = socket;
|
|
348
|
+
socket.on("message", (data) => this.consumeMessage(data));
|
|
349
|
+
socket.on("close", () => this.handleClosed());
|
|
350
|
+
socket.on("error", () => this.handleClosed());
|
|
351
|
+
}
|
|
349
352
|
nextId = 1;
|
|
350
353
|
pending = /* @__PURE__ */ new Map();
|
|
351
354
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
352
|
-
/** sessionId → method → callbacks (for multiplexed page sessions) */
|
|
353
355
|
sessionEventHandlers = /* @__PURE__ */ new Map();
|
|
354
356
|
_closed = false;
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
for (const [id, pending] of this.pending) {
|
|
364
|
-
clearTimeout(pending.timer);
|
|
365
|
-
pending.reject(new Error("CDP connection closed"));
|
|
366
|
-
this.pending.delete(id);
|
|
357
|
+
static connect(wsUrl) {
|
|
358
|
+
return new Promise((resolve2, reject) => {
|
|
359
|
+
let url2;
|
|
360
|
+
try {
|
|
361
|
+
url2 = new URL(wsUrl);
|
|
362
|
+
} catch {
|
|
363
|
+
reject(new Error("Invalid CDP WebSocket URL"));
|
|
364
|
+
return;
|
|
367
365
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
366
|
+
if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
|
|
367
|
+
reject(new Error(`Unsupported CDP WebSocket protocol: ${url2.protocol}`));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const socket = new WebSocket(url2, {
|
|
371
|
+
handshakeTimeout: 5e3,
|
|
372
|
+
maxPayload: 64 * 1024 * 1024,
|
|
373
|
+
perMessageDeflate: false
|
|
374
|
+
});
|
|
375
|
+
let settled = false;
|
|
376
|
+
const fail = (error48) => {
|
|
377
|
+
if (settled) return;
|
|
378
|
+
settled = true;
|
|
379
|
+
socket.terminate();
|
|
380
|
+
reject(error48);
|
|
381
|
+
};
|
|
382
|
+
socket.once("open", () => {
|
|
383
|
+
if (settled) return;
|
|
384
|
+
settled = true;
|
|
385
|
+
resolve2(new _CdpClient(socket));
|
|
386
|
+
});
|
|
387
|
+
socket.once("error", fail);
|
|
388
|
+
socket.once("unexpected-response", (_request, response) => {
|
|
389
|
+
response.resume();
|
|
390
|
+
fail(new Error(`CDP WebSocket upgrade failed with status ${response.statusCode ?? 0}`));
|
|
391
|
+
});
|
|
371
392
|
});
|
|
372
393
|
}
|
|
373
|
-
/** Create a child session client that routes through this connection via sessionId. */
|
|
374
394
|
createChildSession(sessionId) {
|
|
375
395
|
return new CdpChildSession(this, sessionId);
|
|
376
396
|
}
|
|
377
|
-
/** Send a command within a specific CDP session (multiplexed on this connection). */
|
|
378
397
|
sendCommandInSession(sessionId, method, params, timeoutMs = 3e4) {
|
|
379
|
-
return
|
|
380
|
-
if (this._closed) {
|
|
381
|
-
reject(new Error("CDP client is closed"));
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
const id = this.nextId++;
|
|
385
|
-
const timer = setTimeout(() => {
|
|
386
|
-
this.pending.delete(id);
|
|
387
|
-
reject(new Error(`CDP command timeout: ${method}`));
|
|
388
|
-
}, timeoutMs);
|
|
389
|
-
this.pending.set(id, { resolve, reject, timer, sessionId });
|
|
390
|
-
this.sendText(JSON.stringify({ id, method, params: params ?? {}, sessionId }));
|
|
391
|
-
});
|
|
398
|
+
return this.sendRequest(method, params, timeoutMs, sessionId);
|
|
392
399
|
}
|
|
393
|
-
/** Subscribe to events from a specific CDP session. */
|
|
394
400
|
onSession(sessionId, method, callback) {
|
|
395
401
|
if (!this.sessionEventHandlers.has(sessionId)) {
|
|
396
402
|
this.sessionEventHandlers.set(sessionId, /* @__PURE__ */ new Map());
|
|
397
403
|
}
|
|
398
404
|
const sessionMap = this.sessionEventHandlers.get(sessionId);
|
|
399
|
-
if (!sessionMap.has(method))
|
|
400
|
-
sessionMap.set(method, /* @__PURE__ */ new Set());
|
|
401
|
-
}
|
|
405
|
+
if (!sessionMap.has(method)) sessionMap.set(method, /* @__PURE__ */ new Set());
|
|
402
406
|
sessionMap.get(method).add(callback);
|
|
403
407
|
return () => sessionMap.get(method)?.delete(callback);
|
|
404
408
|
}
|
|
405
|
-
|
|
406
|
-
return
|
|
407
|
-
const url2 = new URL(wsUrl);
|
|
408
|
-
const port = parseInt(url2.port || "9222", 10);
|
|
409
|
-
const key = crypto2.randomBytes(16).toString("base64");
|
|
410
|
-
const expectedAccept = crypto2.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
|
|
411
|
-
const req = http.request({
|
|
412
|
-
host: url2.hostname,
|
|
413
|
-
port,
|
|
414
|
-
path: url2.pathname,
|
|
415
|
-
headers: {
|
|
416
|
-
Connection: "Upgrade",
|
|
417
|
-
Upgrade: "websocket",
|
|
418
|
-
"Sec-WebSocket-Key": key,
|
|
419
|
-
"Sec-WebSocket-Version": "13",
|
|
420
|
-
Host: url2.host
|
|
421
|
-
// No Origin header — Chrome allows connections without Origin,
|
|
422
|
-
// but rejects any Origin not in --remote-allow-origins allowlist.
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
let settled = false;
|
|
426
|
-
const fail = (err) => {
|
|
427
|
-
if (settled) return;
|
|
428
|
-
settled = true;
|
|
429
|
-
reject(err);
|
|
430
|
-
};
|
|
431
|
-
req.on("upgrade", (res, socket, head) => {
|
|
432
|
-
if (settled) return;
|
|
433
|
-
if (res.statusCode !== 101) {
|
|
434
|
-
settled = true;
|
|
435
|
-
socket.destroy();
|
|
436
|
-
reject(new Error(`WebSocket upgrade failed with status ${res.statusCode}`));
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const acceptHeader = res.headers["sec-websocket-accept"];
|
|
440
|
-
if (acceptHeader !== expectedAccept) {
|
|
441
|
-
settled = true;
|
|
442
|
-
socket.destroy();
|
|
443
|
-
reject(new Error("WebSocket handshake key mismatch"));
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
settled = true;
|
|
447
|
-
const client2 = new _CdpClient(socket);
|
|
448
|
-
if (head && head.length > 0) {
|
|
449
|
-
client2.buffer = Buffer.concat([client2.buffer, head]);
|
|
450
|
-
client2.consumeFrames();
|
|
451
|
-
}
|
|
452
|
-
resolve(client2);
|
|
453
|
-
});
|
|
454
|
-
req.on("error", fail);
|
|
455
|
-
req.setTimeout(5e3, () => {
|
|
456
|
-
req.destroy();
|
|
457
|
-
fail(new Error("CDP WebSocket connect timeout"));
|
|
458
|
-
});
|
|
459
|
-
req.end();
|
|
460
|
-
});
|
|
409
|
+
sendCommand(method, params, timeoutMs = 3e4) {
|
|
410
|
+
return this.sendRequest(method, params, timeoutMs);
|
|
461
411
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const masked = (byte1 & 128) !== 0;
|
|
467
|
-
let payloadLen = byte1 & 127;
|
|
468
|
-
let headerLen = 2;
|
|
469
|
-
if (payloadLen === 126) {
|
|
470
|
-
if (this.buffer.length < 4) return;
|
|
471
|
-
payloadLen = this.buffer.readUInt16BE(2);
|
|
472
|
-
headerLen = 4;
|
|
473
|
-
} else if (payloadLen === 127) {
|
|
474
|
-
if (this.buffer.length < 10) return;
|
|
475
|
-
payloadLen = Number(this.buffer.readBigUInt64BE(2));
|
|
476
|
-
headerLen = 10;
|
|
477
|
-
}
|
|
478
|
-
if (masked) headerLen += 4;
|
|
479
|
-
if (this.buffer.length < headerLen + payloadLen) return;
|
|
480
|
-
const opcode = this.buffer[0] & 15;
|
|
481
|
-
let payload;
|
|
482
|
-
if (masked) {
|
|
483
|
-
const maskKey = this.buffer.subarray(headerLen - 4, headerLen);
|
|
484
|
-
payload = Buffer.from(
|
|
485
|
-
this.buffer.subarray(headerLen, headerLen + payloadLen).map((b, i) => b ^ maskKey[i % 4])
|
|
486
|
-
);
|
|
487
|
-
} else {
|
|
488
|
-
payload = this.buffer.subarray(headerLen, headerLen + payloadLen);
|
|
489
|
-
}
|
|
490
|
-
this.buffer = this.buffer.subarray(headerLen + payloadLen);
|
|
491
|
-
if (opcode === 1 || opcode === 2) {
|
|
492
|
-
try {
|
|
493
|
-
const msg = JSON.parse(payload.toString("utf-8"));
|
|
494
|
-
this.handleMessage(msg);
|
|
495
|
-
} catch {
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
412
|
+
on(method, callback) {
|
|
413
|
+
if (!this.eventHandlers.has(method)) this.eventHandlers.set(method, /* @__PURE__ */ new Set());
|
|
414
|
+
this.eventHandlers.get(method).add(callback);
|
|
415
|
+
return () => this.eventHandlers.get(method)?.delete(callback);
|
|
499
416
|
}
|
|
500
|
-
|
|
501
|
-
if (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
this.pending.delete(msg.id);
|
|
506
|
-
if (msg.error) {
|
|
507
|
-
pending.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
|
|
508
|
-
} else {
|
|
509
|
-
pending.resolve(msg.result ?? {});
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
} else if (msg.method) {
|
|
513
|
-
if (msg.sessionId) {
|
|
514
|
-
const sessionMap = this.sessionEventHandlers.get(msg.sessionId);
|
|
515
|
-
const handlers = sessionMap?.get(msg.method);
|
|
516
|
-
if (handlers) {
|
|
517
|
-
for (const cb of handlers) {
|
|
518
|
-
try {
|
|
519
|
-
cb(msg.params);
|
|
520
|
-
} catch {
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
} else {
|
|
525
|
-
const handlers = this.eventHandlers.get(msg.method);
|
|
526
|
-
if (handlers) {
|
|
527
|
-
for (const cb of handlers) {
|
|
528
|
-
try {
|
|
529
|
-
cb(msg.params);
|
|
530
|
-
} catch {
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
417
|
+
close() {
|
|
418
|
+
if (this._closed) return;
|
|
419
|
+
this._closed = true;
|
|
420
|
+
this.socket.terminate();
|
|
421
|
+
this.rejectPending("CDP connection closed");
|
|
536
422
|
}
|
|
537
|
-
|
|
538
|
-
return
|
|
539
|
-
|
|
423
|
+
get closed() {
|
|
424
|
+
return this._closed;
|
|
425
|
+
}
|
|
426
|
+
sendRequest(method, params, timeoutMs, sessionId) {
|
|
427
|
+
return new Promise((resolve2, reject) => {
|
|
428
|
+
if (this._closed || this.socket.readyState !== WebSocket.OPEN) {
|
|
540
429
|
reject(new Error("CDP client is closed"));
|
|
541
430
|
return;
|
|
542
431
|
}
|
|
@@ -545,51 +434,61 @@ var CdpClient = class _CdpClient {
|
|
|
545
434
|
this.pending.delete(id);
|
|
546
435
|
reject(new Error(`CDP command timeout: ${method}`));
|
|
547
436
|
}, timeoutMs);
|
|
548
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
549
|
-
this.
|
|
437
|
+
this.pending.set(id, { resolve: resolve2, reject, timer, sessionId });
|
|
438
|
+
this.socket.send(
|
|
439
|
+
JSON.stringify({ id, method, params: params ?? {}, ...sessionId ? { sessionId } : {} }),
|
|
440
|
+
(error48) => {
|
|
441
|
+
if (!error48) return;
|
|
442
|
+
const pending = this.pending.get(id);
|
|
443
|
+
if (!pending) return;
|
|
444
|
+
clearTimeout(pending.timer);
|
|
445
|
+
this.pending.delete(id);
|
|
446
|
+
pending.reject(error48);
|
|
447
|
+
}
|
|
448
|
+
);
|
|
550
449
|
});
|
|
551
450
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
451
|
+
consumeMessage(data) {
|
|
452
|
+
try {
|
|
453
|
+
const text = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.isBuffer(data) ? data.toString("utf8") : Buffer.from(data).toString("utf8");
|
|
454
|
+
this.handleMessage(JSON.parse(text));
|
|
455
|
+
} catch {
|
|
555
456
|
}
|
|
556
|
-
this.eventHandlers.get(method).add(callback);
|
|
557
|
-
return () => this.eventHandlers.get(method)?.delete(callback);
|
|
558
457
|
}
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
header = Buffer.alloc(8);
|
|
572
|
-
header[0] = 129;
|
|
573
|
-
header[1] = 128 | 126;
|
|
574
|
-
header.writeUInt16BE(payload.length, 2);
|
|
575
|
-
maskKey.copy(header, 4);
|
|
576
|
-
} else {
|
|
577
|
-
header = Buffer.alloc(14);
|
|
578
|
-
header[0] = 129;
|
|
579
|
-
header[1] = 128 | 127;
|
|
580
|
-
header.writeBigUInt64BE(BigInt(payload.length), 2);
|
|
581
|
-
maskKey.copy(header, 10);
|
|
458
|
+
handleMessage(msg) {
|
|
459
|
+
if (msg.id !== void 0) {
|
|
460
|
+
const pending = this.pending.get(msg.id);
|
|
461
|
+
if (!pending) return;
|
|
462
|
+
clearTimeout(pending.timer);
|
|
463
|
+
this.pending.delete(msg.id);
|
|
464
|
+
if (msg.error) {
|
|
465
|
+
pending.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
|
|
466
|
+
} else {
|
|
467
|
+
pending.resolve(msg.result ?? {});
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
582
470
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
471
|
+
if (!msg.method) return;
|
|
472
|
+
const handlers = msg.sessionId ? this.sessionEventHandlers.get(msg.sessionId)?.get(msg.method) : this.eventHandlers.get(msg.method);
|
|
473
|
+
if (!handlers) return;
|
|
474
|
+
for (const callback of handlers) {
|
|
475
|
+
try {
|
|
476
|
+
callback(msg.params);
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
589
479
|
}
|
|
590
480
|
}
|
|
591
|
-
|
|
592
|
-
|
|
481
|
+
handleClosed() {
|
|
482
|
+
if (this._closed) return;
|
|
483
|
+
this._closed = true;
|
|
484
|
+
this.rejectPending("CDP connection closed");
|
|
485
|
+
}
|
|
486
|
+
rejectPending(message) {
|
|
487
|
+
for (const [id, pending] of this.pending) {
|
|
488
|
+
clearTimeout(pending.timer);
|
|
489
|
+
pending.reject(new Error(message));
|
|
490
|
+
this.pending.delete(id);
|
|
491
|
+
}
|
|
593
492
|
}
|
|
594
493
|
};
|
|
595
494
|
|
|
@@ -1166,13 +1065,13 @@ function queryTrackingEvents(buffer, options = {}) {
|
|
|
1166
1065
|
}
|
|
1167
1066
|
|
|
1168
1067
|
// ../browser-core/dist/requestSerializer.js
|
|
1169
|
-
function parsePath(
|
|
1170
|
-
if (
|
|
1068
|
+
function parsePath(path15) {
|
|
1069
|
+
if (path15.length > 1e3)
|
|
1171
1070
|
throw new Error("Path too long: maximum 1000 characters allowed");
|
|
1172
1071
|
const keys = [];
|
|
1173
1072
|
let current = "";
|
|
1174
|
-
for (let i = 0; i <
|
|
1175
|
-
const char =
|
|
1073
|
+
for (let i = 0; i < path15.length; i++) {
|
|
1074
|
+
const char = path15[i];
|
|
1176
1075
|
if (char === ".") {
|
|
1177
1076
|
if (current) {
|
|
1178
1077
|
keys.push(current);
|
|
@@ -1184,10 +1083,10 @@ function parsePath(path13) {
|
|
|
1184
1083
|
current = "";
|
|
1185
1084
|
}
|
|
1186
1085
|
let j = i + 1;
|
|
1187
|
-
while (j <
|
|
1086
|
+
while (j < path15.length && path15[j] !== "]")
|
|
1188
1087
|
j++;
|
|
1189
|
-
if (j <
|
|
1190
|
-
const index = parseInt(
|
|
1088
|
+
if (j < path15.length) {
|
|
1089
|
+
const index = parseInt(path15.substring(i + 1, j), 10);
|
|
1191
1090
|
if (!isNaN(index)) {
|
|
1192
1091
|
if (index < 0 || index > 1e5)
|
|
1193
1092
|
throw new Error(`Array index out of safe range: ${index}`);
|
|
@@ -1205,8 +1104,8 @@ function parsePath(path13) {
|
|
|
1205
1104
|
throw new Error("Path too deep: maximum 50 levels allowed");
|
|
1206
1105
|
return keys;
|
|
1207
1106
|
}
|
|
1208
|
-
function getValueByPath(obj,
|
|
1209
|
-
const keys = parsePath(
|
|
1107
|
+
function getValueByPath(obj, path15) {
|
|
1108
|
+
const keys = parsePath(path15);
|
|
1210
1109
|
let current = obj;
|
|
1211
1110
|
for (const key of keys) {
|
|
1212
1111
|
if (current === null || current === void 0)
|
|
@@ -1215,8 +1114,8 @@ function getValueByPath(obj, path13) {
|
|
|
1215
1114
|
}
|
|
1216
1115
|
return current;
|
|
1217
1116
|
}
|
|
1218
|
-
function setValueByPath(obj,
|
|
1219
|
-
const keys = parsePath(
|
|
1117
|
+
function setValueByPath(obj, path15, value) {
|
|
1118
|
+
const keys = parsePath(path15);
|
|
1220
1119
|
let current = obj;
|
|
1221
1120
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
1222
1121
|
const key = keys[i];
|
|
@@ -2752,19 +2651,19 @@ function installPageObserverInPage(maxBatches = 50) {
|
|
|
2752
2651
|
}
|
|
2753
2652
|
}
|
|
2754
2653
|
}
|
|
2755
|
-
const
|
|
2654
|
+
const path15 = [];
|
|
2756
2655
|
let current = input;
|
|
2757
|
-
while (current && current !== document.documentElement &&
|
|
2656
|
+
while (current && current !== document.documentElement && path15.length < 6) {
|
|
2758
2657
|
const parent = current.parentElement;
|
|
2759
2658
|
const currentTag = current.tagName.toLowerCase();
|
|
2760
2659
|
if (!parent) {
|
|
2761
|
-
|
|
2660
|
+
path15.unshift(currentTag);
|
|
2762
2661
|
break;
|
|
2763
2662
|
}
|
|
2764
2663
|
const siblings = Array.from(parent.children).filter((item) => item.tagName === current.tagName);
|
|
2765
2664
|
const position = siblings.indexOf(current) + 1;
|
|
2766
|
-
|
|
2767
|
-
const candidate =
|
|
2665
|
+
path15.unshift(siblings.length > 1 ? `${currentTag}:nth-of-type(${position})` : currentTag);
|
|
2666
|
+
const candidate = path15.join(" > ");
|
|
2768
2667
|
if (input.isConnected) {
|
|
2769
2668
|
try {
|
|
2770
2669
|
if (document.querySelectorAll(candidate).length === 1)
|
|
@@ -2774,7 +2673,7 @@ function installPageObserverInPage(maxBatches = 50) {
|
|
|
2774
2673
|
}
|
|
2775
2674
|
current = parent;
|
|
2776
2675
|
}
|
|
2777
|
-
return
|
|
2676
|
+
return path15.join(" > ") || void 0;
|
|
2778
2677
|
};
|
|
2779
2678
|
const inferredRole = (element) => {
|
|
2780
2679
|
const explicit = element.getAttribute("role");
|
|
@@ -3056,7 +2955,7 @@ function installPageObserverInPage(maxBatches = 50) {
|
|
|
3056
2955
|
});
|
|
3057
2956
|
state.waitSince = (checkpoint, checkpointFocus, quietMs, firstChangeTimeoutMs, timeoutMs) => {
|
|
3058
2957
|
const startedAt = performance.now();
|
|
3059
|
-
return new Promise((
|
|
2958
|
+
return new Promise((resolve2) => {
|
|
3060
2959
|
let completed = false;
|
|
3061
2960
|
let quietTimer;
|
|
3062
2961
|
let firstChangeTimer;
|
|
@@ -3121,7 +3020,7 @@ function installPageObserverInPage(maxBatches = 50) {
|
|
|
3121
3020
|
if (structuralMutationCount > 0 && unclassifiedStructuralMutationCount > 0) {
|
|
3122
3021
|
snapshotReasons.push("unclassified-structure");
|
|
3123
3022
|
}
|
|
3124
|
-
|
|
3023
|
+
resolve2({
|
|
3125
3024
|
mode: "delta",
|
|
3126
3025
|
scope: "main-document",
|
|
3127
3026
|
checkpoint,
|
|
@@ -3360,14 +3259,14 @@ function createNavigationSettlementTracker(options) {
|
|
|
3360
3259
|
return Promise.resolve(true);
|
|
3361
3260
|
if (timeoutMs <= 0 || disposed)
|
|
3362
3261
|
return Promise.resolve(false);
|
|
3363
|
-
return new Promise((
|
|
3262
|
+
return new Promise((resolve2) => {
|
|
3364
3263
|
const initialDeadline = performance.now() + timeoutMs;
|
|
3365
3264
|
const hardDeadline = initialDeadline + 4e3;
|
|
3366
3265
|
let timer;
|
|
3367
3266
|
const waiter = (didStart) => {
|
|
3368
3267
|
clearTimeout(timer);
|
|
3369
3268
|
startWaiters.delete(waiter);
|
|
3370
|
-
|
|
3269
|
+
resolve2(didStart);
|
|
3371
3270
|
};
|
|
3372
3271
|
const check2 = () => {
|
|
3373
3272
|
if (started) {
|
|
@@ -3389,26 +3288,26 @@ function createNavigationSettlementTracker(options) {
|
|
|
3389
3288
|
const waitForResult = (timeoutMs) => {
|
|
3390
3289
|
if (completed)
|
|
3391
3290
|
return Promise.resolve(completed);
|
|
3392
|
-
return new Promise((
|
|
3291
|
+
return new Promise((resolve2) => {
|
|
3393
3292
|
const waiter = (result) => {
|
|
3394
3293
|
clearTimeout(timer);
|
|
3395
3294
|
resultWaiters.delete(waiter);
|
|
3396
|
-
|
|
3295
|
+
resolve2(result);
|
|
3397
3296
|
};
|
|
3398
3297
|
const timer = setTimeout(() => {
|
|
3399
3298
|
resultWaiters.delete(waiter);
|
|
3400
3299
|
finish(documentComplete, false, "timeout");
|
|
3401
3300
|
if (completed)
|
|
3402
|
-
|
|
3301
|
+
resolve2(completed);
|
|
3403
3302
|
}, timeoutMs);
|
|
3404
3303
|
resultWaiters.add(waiter);
|
|
3405
3304
|
});
|
|
3406
3305
|
};
|
|
3407
3306
|
const waitForNetworkIdle = (timeoutMs) => {
|
|
3408
3307
|
const waitStartedAt = performance.now();
|
|
3409
|
-
return new Promise((
|
|
3308
|
+
return new Promise((resolve2) => {
|
|
3410
3309
|
const finishWait = (ready, settledBy) => {
|
|
3411
|
-
|
|
3310
|
+
resolve2({
|
|
3412
3311
|
ready,
|
|
3413
3312
|
settledBy,
|
|
3414
3313
|
pendingRequests: pendingRequests2.size,
|
|
@@ -4409,12 +4308,12 @@ function resolveGetCookieTarget(currentUrl, input) {
|
|
|
4409
4308
|
const url2 = parseCurrentUrl(currentUrl, action);
|
|
4410
4309
|
const name = validateCookieName(input?.name, action);
|
|
4411
4310
|
const domain2 = resolveExplicitDomain(url2, input?.domain, action);
|
|
4412
|
-
const
|
|
4311
|
+
const path15 = validateCookiePath(input?.path, action);
|
|
4413
4312
|
return {
|
|
4414
4313
|
cookieName: name,
|
|
4415
|
-
cookieUrl: `${url2.protocol}//${url2.hostname}${(
|
|
4314
|
+
cookieUrl: `${url2.protocol}//${url2.hostname}${(path15 ?? url2.pathname) || "/"}`,
|
|
4416
4315
|
...domain2 ? { domain: domain2 } : {},
|
|
4417
|
-
...
|
|
4316
|
+
...path15 ? { path: path15 } : {}
|
|
4418
4317
|
};
|
|
4419
4318
|
}
|
|
4420
4319
|
function resolveSetCookieTarget(currentUrl, input) {
|
|
@@ -4422,7 +4321,7 @@ function resolveSetCookieTarget(currentUrl, input) {
|
|
|
4422
4321
|
const url2 = parseCurrentUrl(currentUrl, action);
|
|
4423
4322
|
const name = validateCookieName(input?.name, action);
|
|
4424
4323
|
const value = validateCookieValue(input?.value);
|
|
4425
|
-
const
|
|
4324
|
+
const path15 = validateCookiePath(input?.path, action) ?? "/";
|
|
4426
4325
|
if (input.reload !== void 0 && typeof input.reload !== "boolean") {
|
|
4427
4326
|
throw new Error('set_cookie: "reload" must be a boolean');
|
|
4428
4327
|
}
|
|
@@ -4432,9 +4331,9 @@ function resolveSetCookieTarget(currentUrl, input) {
|
|
|
4432
4331
|
cookieName: name,
|
|
4433
4332
|
cookieValue: value,
|
|
4434
4333
|
...explicitDomain ? { cookieDomain: explicitDomain.includes(".") ? `.${explicitDomain}` : explicitDomain } : {},
|
|
4435
|
-
cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${
|
|
4334
|
+
cookieUrl: `${url2.protocol}//${url2.hostname.toLowerCase()}${path15}`,
|
|
4436
4335
|
domain: domain2,
|
|
4437
|
-
path:
|
|
4336
|
+
path: path15,
|
|
4438
4337
|
reload: input.reload === true
|
|
4439
4338
|
};
|
|
4440
4339
|
}
|
|
@@ -4848,7 +4747,7 @@ async function setupBufferListeners(client2, buffers) {
|
|
|
4848
4747
|
|
|
4849
4748
|
// src/cdp-backend/discover.ts
|
|
4850
4749
|
import fs from "node:fs";
|
|
4851
|
-
import
|
|
4750
|
+
import http from "node:http";
|
|
4852
4751
|
import os from "node:os";
|
|
4853
4752
|
import path from "node:path";
|
|
4854
4753
|
function chromeUserDataDir() {
|
|
@@ -4876,13 +4775,13 @@ function readDevToolsActivePort(userDataDir = chromeUserDataDir()) {
|
|
|
4876
4775
|
}
|
|
4877
4776
|
}
|
|
4878
4777
|
async function discoverTargets(port = 9222) {
|
|
4879
|
-
return new Promise((
|
|
4880
|
-
const req =
|
|
4778
|
+
return new Promise((resolve2, reject) => {
|
|
4779
|
+
const req = http.get(`http://localhost:${port}/json`, (res) => {
|
|
4881
4780
|
let data = "";
|
|
4882
4781
|
res.on("data", (chunk) => data += chunk);
|
|
4883
4782
|
res.on("end", () => {
|
|
4884
4783
|
try {
|
|
4885
|
-
|
|
4784
|
+
resolve2(JSON.parse(data));
|
|
4886
4785
|
} catch (e) {
|
|
4887
4786
|
reject(new Error(`Failed to parse CDP targets: ${e}`));
|
|
4888
4787
|
}
|
|
@@ -4909,6 +4808,8 @@ async function isCdpAvailable(port = 9222, userDataDir) {
|
|
|
4909
4808
|
var CdpSession = class {
|
|
4910
4809
|
port;
|
|
4911
4810
|
userDataDir;
|
|
4811
|
+
endpointUrl;
|
|
4812
|
+
endpointResolver;
|
|
4912
4813
|
browserClient = null;
|
|
4913
4814
|
pageSessions = /* @__PURE__ */ new Map();
|
|
4914
4815
|
// targetId → PageSession
|
|
@@ -4922,6 +4823,8 @@ var CdpSession = class {
|
|
|
4922
4823
|
}
|
|
4923
4824
|
this.port = options.port ?? 9222;
|
|
4924
4825
|
this.userDataDir = options.userDataDir;
|
|
4826
|
+
this.endpointUrl = options.endpointUrl;
|
|
4827
|
+
this.endpointResolver = options.endpointResolver;
|
|
4925
4828
|
}
|
|
4926
4829
|
// ── Browser-level ──────────────────────────────────────────────────────────
|
|
4927
4830
|
async getBrowserClient() {
|
|
@@ -4999,7 +4902,13 @@ var CdpSession = class {
|
|
|
4999
4902
|
5e3
|
|
5000
4903
|
);
|
|
5001
4904
|
client2 = browser.createChildSession(sessionId);
|
|
5002
|
-
} catch {
|
|
4905
|
+
} catch (error48) {
|
|
4906
|
+
if (this.isRemote()) {
|
|
4907
|
+
throw new Error(
|
|
4908
|
+
`Failed to attach to remote CDP target ${target.id}: ${error48 instanceof Error ? error48.message : String(error48)}`,
|
|
4909
|
+
{ cause: error48 }
|
|
4910
|
+
);
|
|
4911
|
+
}
|
|
5003
4912
|
client2 = await CdpClient.connect(target.webSocketDebuggerUrl);
|
|
5004
4913
|
}
|
|
5005
4914
|
let tabId;
|
|
@@ -5029,6 +4938,7 @@ var CdpSession = class {
|
|
|
5029
4938
|
return session;
|
|
5030
4939
|
}
|
|
5031
4940
|
async findTargetById(targetId) {
|
|
4941
|
+
if (this.isRemote()) return this.findTargetByIdViaCdp(targetId);
|
|
5032
4942
|
try {
|
|
5033
4943
|
const targets = await discoverTargets(this.effectivePort());
|
|
5034
4944
|
return targets.find((t) => t.id === targetId && t.webSocketDebuggerUrl) ?? null;
|
|
@@ -5037,6 +4947,7 @@ var CdpSession = class {
|
|
|
5037
4947
|
}
|
|
5038
4948
|
}
|
|
5039
4949
|
async findDefaultPageTarget() {
|
|
4950
|
+
if (this.isRemote()) return this.findDefaultPageTargetViaCdp();
|
|
5040
4951
|
try {
|
|
5041
4952
|
const targets = await discoverTargets(this.effectivePort());
|
|
5042
4953
|
return targets.find(
|
|
@@ -5056,6 +4967,14 @@ var CdpSession = class {
|
|
|
5056
4967
|
* Priority: DevToolsActivePort file → HTTP /json/version
|
|
5057
4968
|
*/
|
|
5058
4969
|
async resolveBrowserWsUrl() {
|
|
4970
|
+
const configuredEndpoint = this.endpointResolver ? await this.endpointResolver() : this.endpointUrl;
|
|
4971
|
+
if (configuredEndpoint) {
|
|
4972
|
+
const url2 = new URL(configuredEndpoint);
|
|
4973
|
+
if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
|
|
4974
|
+
throw new Error(`Unsupported CDP endpoint protocol: ${url2.protocol}`);
|
|
4975
|
+
}
|
|
4976
|
+
return url2.toString();
|
|
4977
|
+
}
|
|
5059
4978
|
const fileInfo = readDevToolsActivePort(this.userDataDir);
|
|
5060
4979
|
if (fileInfo) {
|
|
5061
4980
|
return `ws://localhost:${fileInfo.port}${fileInfo.browserWsPath}`;
|
|
@@ -5075,6 +4994,9 @@ var CdpSession = class {
|
|
|
5075
4994
|
const port = this.effectivePort();
|
|
5076
4995
|
return `ws://localhost:${port}/devtools/page/${targetId}`;
|
|
5077
4996
|
}
|
|
4997
|
+
isRemote() {
|
|
4998
|
+
return Boolean(this.endpointUrl || this.endpointResolver);
|
|
4999
|
+
}
|
|
5078
5000
|
async findTargetByIdViaCdp(targetId) {
|
|
5079
5001
|
try {
|
|
5080
5002
|
const browser = await this.getBrowserClient();
|
|
@@ -5114,13 +5036,13 @@ var CdpSession = class {
|
|
|
5114
5036
|
}
|
|
5115
5037
|
}
|
|
5116
5038
|
fetchVersionInfo() {
|
|
5117
|
-
return new Promise((
|
|
5118
|
-
const req =
|
|
5039
|
+
return new Promise((resolve2, reject) => {
|
|
5040
|
+
const req = http2.get(`http://localhost:${this.port}/json/version`, (res) => {
|
|
5119
5041
|
let data = "";
|
|
5120
5042
|
res.on("data", (chunk) => data += chunk);
|
|
5121
5043
|
res.on("end", () => {
|
|
5122
5044
|
try {
|
|
5123
|
-
|
|
5045
|
+
resolve2(JSON.parse(data));
|
|
5124
5046
|
} catch (e) {
|
|
5125
5047
|
reject(new Error(`Failed to parse CDP version info: ${e}`));
|
|
5126
5048
|
}
|
|
@@ -5829,15 +5751,15 @@ async function releaseCdpControl(session) {
|
|
|
5829
5751
|
|
|
5830
5752
|
// src/cdp-backend/cdp-utils.ts
|
|
5831
5753
|
import https from "node:https";
|
|
5832
|
-
import
|
|
5754
|
+
import http3 from "node:http";
|
|
5833
5755
|
var TYPE_INTO_ELEMENT_SRC = typeIntoElement.toString();
|
|
5834
5756
|
var CLICK_ELEMENT_VIA_JS_SRC = clickElementViaJS.toString();
|
|
5835
5757
|
var GET_CLICK_COORDS_FROM_NODE_SRC = getClickCoordsFromNode.toString();
|
|
5836
5758
|
function nodeFetch(urlStr, options = {}) {
|
|
5837
|
-
return new Promise((
|
|
5759
|
+
return new Promise((resolve2, reject) => {
|
|
5838
5760
|
const url2 = new URL(urlStr);
|
|
5839
5761
|
const isHttps = url2.protocol === "https:";
|
|
5840
|
-
const lib = isHttps ? https :
|
|
5762
|
+
const lib = isHttps ? https : http3;
|
|
5841
5763
|
const { method = "GET", headers = {}, body, timeoutMs = 3e4 } = options;
|
|
5842
5764
|
const req = lib.request(
|
|
5843
5765
|
{
|
|
@@ -5857,7 +5779,7 @@ function nodeFetch(urlStr, options = {}) {
|
|
|
5857
5779
|
res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
5858
5780
|
res.on("end", () => {
|
|
5859
5781
|
const bodyBuffer = Buffer.concat(chunks);
|
|
5860
|
-
|
|
5782
|
+
resolve2({
|
|
5861
5783
|
status: res.statusCode ?? 0,
|
|
5862
5784
|
statusText: res.statusMessage ?? "",
|
|
5863
5785
|
headers: responseHeaders,
|
|
@@ -5914,7 +5836,7 @@ ${value}\r
|
|
|
5914
5836
|
return { contentType: `multipart/form-data; boundary=${boundary}`, body };
|
|
5915
5837
|
}
|
|
5916
5838
|
async function waitForPageLoad(session, timeoutMs) {
|
|
5917
|
-
return new Promise((
|
|
5839
|
+
return new Promise((resolve2, reject) => {
|
|
5918
5840
|
let settled = false;
|
|
5919
5841
|
let removeLoadListener = () => {
|
|
5920
5842
|
};
|
|
@@ -5924,7 +5846,7 @@ async function waitForPageLoad(session, timeoutMs) {
|
|
|
5924
5846
|
clearTimeout(timer);
|
|
5925
5847
|
removeLoadListener();
|
|
5926
5848
|
if (error48) reject(error48);
|
|
5927
|
-
else
|
|
5849
|
+
else resolve2();
|
|
5928
5850
|
};
|
|
5929
5851
|
const timer = setTimeout(() => {
|
|
5930
5852
|
finish(new Error(`Page load timeout after ${timeoutMs}ms`));
|
|
@@ -7017,12 +6939,12 @@ function resolveCloudConnectorCommand() {
|
|
|
7017
6939
|
};
|
|
7018
6940
|
}
|
|
7019
6941
|
function startCloudConnector(serverUrl) {
|
|
7020
|
-
return new Promise((
|
|
6942
|
+
return new Promise((resolve2) => {
|
|
7021
6943
|
const existingPid = findLiveConnector(serverUrl);
|
|
7022
6944
|
if (existingPid) {
|
|
7023
6945
|
logFn(`Reusing existing cloud connector for ${serverUrl} (pid=${existingPid})`);
|
|
7024
6946
|
const entry = registerConnector(serverUrl, existingPid);
|
|
7025
|
-
|
|
6947
|
+
resolve2({
|
|
7026
6948
|
success: true,
|
|
7027
6949
|
pid: existingPid,
|
|
7028
6950
|
...entry.connectorId ? { connectorId: entry.connectorId } : {},
|
|
@@ -7055,14 +6977,14 @@ function startCloudConnector(serverUrl) {
|
|
|
7055
6977
|
clearTimeout(startupTimer);
|
|
7056
6978
|
if (ok && child.pid) {
|
|
7057
6979
|
const entry = registerConnector(serverUrl, child.pid, child);
|
|
7058
|
-
|
|
6980
|
+
resolve2({
|
|
7059
6981
|
success: true,
|
|
7060
6982
|
pid: child.pid,
|
|
7061
6983
|
...entry.connectorId ? { connectorId: entry.connectorId } : {},
|
|
7062
6984
|
...entry.name ? { name: entry.name } : {}
|
|
7063
6985
|
});
|
|
7064
6986
|
} else {
|
|
7065
|
-
|
|
6987
|
+
resolve2({ success: false, error: result.error || "Failed to spawn cloud connector" });
|
|
7066
6988
|
}
|
|
7067
6989
|
};
|
|
7068
6990
|
child.once("error", (error48) => {
|
|
@@ -7612,8 +7534,8 @@ registerActions([
|
|
|
7612
7534
|
]);
|
|
7613
7535
|
|
|
7614
7536
|
// src/cdp-backend/tdbank-handlers.ts
|
|
7615
|
-
async function cdpFetchTdbank(session,
|
|
7616
|
-
const url2 = new URL(`https://tdbank.alibaba-inc.com${
|
|
7537
|
+
async function cdpFetchTdbank(session, path15, options = {}) {
|
|
7538
|
+
const url2 = new URL(`https://tdbank.alibaba-inc.com${path15}`);
|
|
7617
7539
|
for (const [key, value] of Object.entries(options.params ?? {})) {
|
|
7618
7540
|
if (value !== void 0 && value !== null) {
|
|
7619
7541
|
url2.searchParams.set(key, String(value));
|
|
@@ -7674,7 +7596,7 @@ async function cdpWaitForTracknick(session, options = {}) {
|
|
|
7674
7596
|
latest = await cdpGetTracknick(session);
|
|
7675
7597
|
if (expected && latest === expected) return latest;
|
|
7676
7598
|
if (!expected && latest && latest !== previous) return latest;
|
|
7677
|
-
await new Promise((
|
|
7599
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
7678
7600
|
}
|
|
7679
7601
|
return latest || await cdpGetTracknick(session);
|
|
7680
7602
|
}
|
|
@@ -7684,7 +7606,7 @@ async function cdpOpenAndCloseTarget(cdpSession2, url2) {
|
|
|
7684
7606
|
url: url2,
|
|
7685
7607
|
background: true
|
|
7686
7608
|
});
|
|
7687
|
-
await new Promise((
|
|
7609
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3500));
|
|
7688
7610
|
await browser.sendCommand("Target.closeTarget", { targetId }).catch(() => {
|
|
7689
7611
|
});
|
|
7690
7612
|
}
|
|
@@ -8434,7 +8356,7 @@ async function cdpPageWait(session, data) {
|
|
|
8434
8356
|
const options = resolvePageWaitInput(data);
|
|
8435
8357
|
const startedAt = Date.now();
|
|
8436
8358
|
if (options.mode === "time") {
|
|
8437
|
-
await new Promise((
|
|
8359
|
+
await new Promise((resolve2) => setTimeout(resolve2, options.time));
|
|
8438
8360
|
return { success: true, waited: true, elapsed: options.time, mode: options.mode };
|
|
8439
8361
|
}
|
|
8440
8362
|
const expression = options.mode === "selector" ? `!!document.querySelector(${JSON.stringify(options.selector)})` : options.condition;
|
|
@@ -8459,7 +8381,7 @@ async function cdpPageWait(session, data) {
|
|
|
8459
8381
|
value
|
|
8460
8382
|
};
|
|
8461
8383
|
}
|
|
8462
|
-
await new Promise((
|
|
8384
|
+
await new Promise((resolve2) => setTimeout(resolve2, options.interval));
|
|
8463
8385
|
}
|
|
8464
8386
|
if (options.mode === "selector") {
|
|
8465
8387
|
throw new Error(
|
|
@@ -8530,7 +8452,7 @@ async function waitForHistoryIndex(session, targetIndex, timeoutMs) {
|
|
|
8530
8452
|
}
|
|
8531
8453
|
} catch {
|
|
8532
8454
|
}
|
|
8533
|
-
await new Promise((
|
|
8455
|
+
await new Promise((resolve2) => setTimeout(resolve2, 50));
|
|
8534
8456
|
}
|
|
8535
8457
|
throw new Error(`Page history navigation timeout after ${timeoutMs}ms`);
|
|
8536
8458
|
}
|
|
@@ -8678,7 +8600,7 @@ async function readCurrentUrl(session, action) {
|
|
|
8678
8600
|
return url2;
|
|
8679
8601
|
}
|
|
8680
8602
|
function reloadPageAndWait(session, timeoutMs) {
|
|
8681
|
-
return new Promise((
|
|
8603
|
+
return new Promise((resolve2, reject) => {
|
|
8682
8604
|
let settled = false;
|
|
8683
8605
|
let removeListener = () => {
|
|
8684
8606
|
};
|
|
@@ -8688,7 +8610,7 @@ function reloadPageAndWait(session, timeoutMs) {
|
|
|
8688
8610
|
clearTimeout(timer);
|
|
8689
8611
|
removeListener();
|
|
8690
8612
|
if (error48) reject(error48);
|
|
8691
|
-
else
|
|
8613
|
+
else resolve2();
|
|
8692
8614
|
};
|
|
8693
8615
|
const timer = setTimeout(() => {
|
|
8694
8616
|
finish(new Error(`Page load timeout after ${timeoutMs}ms`));
|
|
@@ -9137,6 +9059,10 @@ async function cdpExportCookies(session, domains) {
|
|
|
9137
9059
|
async function cdpImportCookies(session, cookies) {
|
|
9138
9060
|
if (cookies.length === 0) return 0;
|
|
9139
9061
|
const browser = await session.getBrowserClient();
|
|
9062
|
+
return cdpImportCookiesWithClient(browser, cookies);
|
|
9063
|
+
}
|
|
9064
|
+
async function cdpImportCookiesWithClient(browser, cookies) {
|
|
9065
|
+
if (cookies.length === 0) return 0;
|
|
9140
9066
|
await browser.sendCommand("Storage.setCookies", { cookies });
|
|
9141
9067
|
return cookies.length;
|
|
9142
9068
|
}
|
|
@@ -9263,6 +9189,7 @@ function browserSummary(record2, running) {
|
|
|
9263
9189
|
return {
|
|
9264
9190
|
browserId: record2.browserId,
|
|
9265
9191
|
name: record2.name,
|
|
9192
|
+
provider: "local",
|
|
9266
9193
|
headless: record2.headless,
|
|
9267
9194
|
persistent: record2.persistent,
|
|
9268
9195
|
cdpPort: record2.cdpPort,
|
|
@@ -9329,12 +9256,15 @@ function findManagedRecord(selector) {
|
|
|
9329
9256
|
({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
|
|
9330
9257
|
)?.record ?? null;
|
|
9331
9258
|
}
|
|
9259
|
+
function hasManagedBrowser(selector) {
|
|
9260
|
+
return findManagedRecord(selector) !== null;
|
|
9261
|
+
}
|
|
9332
9262
|
async function waitForActivePort(userDataDir) {
|
|
9333
9263
|
const deadline = Date.now() + LAUNCH_TIMEOUT_MS;
|
|
9334
9264
|
while (Date.now() < deadline) {
|
|
9335
9265
|
const info = readDevToolsActivePort(userDataDir);
|
|
9336
9266
|
if (info) return info.port;
|
|
9337
|
-
await new Promise((
|
|
9267
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
9338
9268
|
}
|
|
9339
9269
|
throw new Error(`Chrome did not expose a CDP port within ${LAUNCH_TIMEOUT_MS}ms`);
|
|
9340
9270
|
}
|
|
@@ -9344,7 +9274,7 @@ async function waitForBrowserRegistration(browserId, expectedPid) {
|
|
|
9344
9274
|
while (Date.now() < deadline) {
|
|
9345
9275
|
const record2 = readRecord(file2);
|
|
9346
9276
|
if (record2?.pid === expectedPid && isAlive(expectedPid)) return;
|
|
9347
|
-
await new Promise((
|
|
9277
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
9348
9278
|
}
|
|
9349
9279
|
throw new Error(`Managed browser ${browserId} did not register within ${LAUNCH_TIMEOUT_MS}ms`);
|
|
9350
9280
|
}
|
|
@@ -9366,7 +9296,7 @@ async function stopProcess(pid, commandMarker) {
|
|
|
9366
9296
|
signalProcess(pid, "SIGTERM", commandMarker);
|
|
9367
9297
|
const deadline = Date.now() + 3e3;
|
|
9368
9298
|
while (Date.now() < deadline && isManagedProcess(pid, commandMarker)) {
|
|
9369
|
-
await new Promise((
|
|
9299
|
+
await new Promise((resolve2) => setTimeout(resolve2, 50));
|
|
9370
9300
|
}
|
|
9371
9301
|
if (isManagedProcess(pid, commandMarker)) signalProcess(pid, "SIGKILL", commandMarker);
|
|
9372
9302
|
}
|
|
@@ -9435,6 +9365,9 @@ function acquireLaunchLock(browserId) {
|
|
|
9435
9365
|
};
|
|
9436
9366
|
}
|
|
9437
9367
|
async function launchManagedBrowser(input, deps) {
|
|
9368
|
+
if (input.imageId !== void 0) {
|
|
9369
|
+
throw new Error("imageId is only supported by the AgentBay browser provider");
|
|
9370
|
+
}
|
|
9438
9371
|
const name = validateName(input.name);
|
|
9439
9372
|
const browserId = managedBrowserId(name);
|
|
9440
9373
|
validateTdbankSelector(input);
|
|
@@ -9470,9 +9403,9 @@ async function launchManagedBrowser(input, deps) {
|
|
|
9470
9403
|
stdio: ["ignore", "ignore", logFd],
|
|
9471
9404
|
windowsHide: headless
|
|
9472
9405
|
});
|
|
9473
|
-
await new Promise((
|
|
9406
|
+
await new Promise((resolve2, reject) => {
|
|
9474
9407
|
chrome.once("error", reject);
|
|
9475
|
-
chrome.once("spawn",
|
|
9408
|
+
chrome.once("spawn", resolve2);
|
|
9476
9409
|
});
|
|
9477
9410
|
chromePid = chrome.pid ?? 0;
|
|
9478
9411
|
if (!chromePid) throw new Error("Chrome launched without a process id");
|
|
@@ -9511,9 +9444,9 @@ async function launchManagedBrowser(input, deps) {
|
|
|
9511
9444
|
],
|
|
9512
9445
|
{ detached: true, stdio: "ignore", windowsHide: true }
|
|
9513
9446
|
);
|
|
9514
|
-
await new Promise((
|
|
9447
|
+
await new Promise((resolve2, reject) => {
|
|
9515
9448
|
host.once("error", reject);
|
|
9516
|
-
host.once("spawn",
|
|
9449
|
+
host.once("spawn", resolve2);
|
|
9517
9450
|
});
|
|
9518
9451
|
hostPid = host.pid ?? 0;
|
|
9519
9452
|
if (!hostPid) throw new Error("Native host launched without a process id");
|
|
@@ -9566,6 +9499,360 @@ async function closeManagedBrowser(selector, options = {}) {
|
|
|
9566
9499
|
return { browserId: record2.browserId, closed: true, profileDeleted: deleteProfile };
|
|
9567
9500
|
}
|
|
9568
9501
|
|
|
9502
|
+
// src/agentbay-browser.ts
|
|
9503
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
9504
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
9505
|
+
import { homedir as homedir2 } from "node:os";
|
|
9506
|
+
import { dirname as dirname3, join as join4, parse, resolve } from "node:path";
|
|
9507
|
+
import { fileURLToPath } from "node:url";
|
|
9508
|
+
var AGENTBAY_BROWSERS_SUBDIR = "agentbay-browsers";
|
|
9509
|
+
var DEFAULT_AGENTBAY_IMAGE_ID = "imgc-0aae4rgl8kvagdv1j";
|
|
9510
|
+
var sdkPromise = null;
|
|
9511
|
+
async function loadSdk() {
|
|
9512
|
+
sdkPromise ??= import("wuying-agentbay-sdk").then((sdk) => {
|
|
9513
|
+
sdk.setupLogger({ level: "ERROR", enableConsole: false });
|
|
9514
|
+
return sdk;
|
|
9515
|
+
});
|
|
9516
|
+
return sdkPromise;
|
|
9517
|
+
}
|
|
9518
|
+
async function createClient(apiKey) {
|
|
9519
|
+
const { AgentBay } = await loadSdk();
|
|
9520
|
+
return new AgentBay({ apiKey });
|
|
9521
|
+
}
|
|
9522
|
+
function agentBayBrowserId(name) {
|
|
9523
|
+
return `managed:agentbay:${name.toLocaleLowerCase("en-US")}`;
|
|
9524
|
+
}
|
|
9525
|
+
function envValue(text, key) {
|
|
9526
|
+
for (const line of text.split(/\r?\n/)) {
|
|
9527
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/.exec(line);
|
|
9528
|
+
if (!match || match[1] !== key) continue;
|
|
9529
|
+
let value = (match[2] ?? "").trim();
|
|
9530
|
+
if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
|
|
9531
|
+
value = value.slice(1, -1);
|
|
9532
|
+
} else {
|
|
9533
|
+
value = value.replace(/\s+#.*$/, "").trim();
|
|
9534
|
+
}
|
|
9535
|
+
return value || void 0;
|
|
9536
|
+
}
|
|
9537
|
+
return void 0;
|
|
9538
|
+
}
|
|
9539
|
+
function envFilesUpward(start) {
|
|
9540
|
+
const files = [];
|
|
9541
|
+
let current = resolve(start);
|
|
9542
|
+
while (true) {
|
|
9543
|
+
files.push(join4(current, ".env"));
|
|
9544
|
+
const parent = dirname3(current);
|
|
9545
|
+
if (parent === current || current === parse(current).root) break;
|
|
9546
|
+
current = parent;
|
|
9547
|
+
}
|
|
9548
|
+
return files;
|
|
9549
|
+
}
|
|
9550
|
+
function resolveAgentBayApiKey() {
|
|
9551
|
+
const inherited = process.env.AGENTBAY_KEY?.trim() || process.env.AGENTBAY_API_KEY?.trim();
|
|
9552
|
+
if (inherited) return inherited;
|
|
9553
|
+
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
9554
|
+
const candidates = [
|
|
9555
|
+
...process.env.MEARL_ENV_FILE ? [resolve(process.env.MEARL_ENV_FILE)] : [],
|
|
9556
|
+
...envFilesUpward(process.cwd()),
|
|
9557
|
+
...envFilesUpward(moduleDir),
|
|
9558
|
+
join4(homedir2(), ".mearl", ".env")
|
|
9559
|
+
];
|
|
9560
|
+
for (const file2 of new Set(candidates)) {
|
|
9561
|
+
if (!existsSync4(file2)) continue;
|
|
9562
|
+
try {
|
|
9563
|
+
const text = readFileSync4(file2, "utf8");
|
|
9564
|
+
const value = envValue(text, "AGENTBAY_KEY") ?? envValue(text, "AGENTBAY_API_KEY");
|
|
9565
|
+
if (value) return value;
|
|
9566
|
+
} catch {
|
|
9567
|
+
}
|
|
9568
|
+
}
|
|
9569
|
+
throw new Error(
|
|
9570
|
+
"AgentBay API key not found. Set AGENTBAY_KEY in the environment, project .env, or ~/.mearl/.env."
|
|
9571
|
+
);
|
|
9572
|
+
}
|
|
9573
|
+
function validateAgentBayInput(input) {
|
|
9574
|
+
if (input.headless === true) {
|
|
9575
|
+
throw new Error("headless=true is not supported by the AgentBay browser provider");
|
|
9576
|
+
}
|
|
9577
|
+
if (input.persistent === true) {
|
|
9578
|
+
throw new Error("persistent is not supported by the AgentBay CDP provider yet");
|
|
9579
|
+
}
|
|
9580
|
+
if (input.executablePath) {
|
|
9581
|
+
throw new Error("executablePath is only supported by the local browser provider");
|
|
9582
|
+
}
|
|
9583
|
+
if (input.accountId !== void 0 || input.havanaId !== void 0 || input.site !== void 0 || typeof input.query === "string" && input.query.trim()) {
|
|
9584
|
+
throw new Error("TDBank login parameters are only supported by the local browser provider");
|
|
9585
|
+
}
|
|
9586
|
+
}
|
|
9587
|
+
function resolveAgentBayImageId(value) {
|
|
9588
|
+
if (value === void 0) return DEFAULT_AGENTBAY_IMAGE_ID;
|
|
9589
|
+
const imageId = typeof value === "string" ? value.trim() : "";
|
|
9590
|
+
if (!imageId || imageId.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(imageId)) {
|
|
9591
|
+
throw new Error("imageId must be a valid AgentBay image alias or image ID");
|
|
9592
|
+
}
|
|
9593
|
+
return imageId;
|
|
9594
|
+
}
|
|
9595
|
+
function normalizeDesktopUserAgent(value) {
|
|
9596
|
+
const userAgent = typeof value === "string" ? value.trim() : "";
|
|
9597
|
+
if (!userAgent) throw new Error("Could not determine the control browser user agent");
|
|
9598
|
+
return userAgent.replace(/\bHeadlessChrome\//g, "Chrome/");
|
|
9599
|
+
}
|
|
9600
|
+
function normalizeAgentBayAccessUrl(value) {
|
|
9601
|
+
const candidate = typeof value === "string" ? value.trim() : "";
|
|
9602
|
+
if (!candidate) return void 0;
|
|
9603
|
+
try {
|
|
9604
|
+
const url2 = new URL(candidate);
|
|
9605
|
+
return url2.protocol === "https:" || url2.protocol === "http:" ? url2.toString() : void 0;
|
|
9606
|
+
} catch {
|
|
9607
|
+
return void 0;
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
function browserSummary2(record2, running) {
|
|
9611
|
+
const accessUrl = normalizeAgentBayAccessUrl(record2.accessUrl);
|
|
9612
|
+
return {
|
|
9613
|
+
browserId: record2.browserId,
|
|
9614
|
+
name: record2.name,
|
|
9615
|
+
provider: "agentbay",
|
|
9616
|
+
headless: false,
|
|
9617
|
+
persistent: false,
|
|
9618
|
+
agentbaySessionId: record2.sessionId,
|
|
9619
|
+
...record2.imageId ? { agentbayImageId: record2.imageId } : {},
|
|
9620
|
+
...accessUrl ? { agentbayAccessUrl: accessUrl } : {},
|
|
9621
|
+
createdAt: record2.createdAt,
|
|
9622
|
+
...record2.copiedCookies ? { copiedCookies: record2.copiedCookies } : {},
|
|
9623
|
+
running
|
|
9624
|
+
};
|
|
9625
|
+
}
|
|
9626
|
+
function listRecords2() {
|
|
9627
|
+
const records = [];
|
|
9628
|
+
for (const file2 of listRecordFiles(keyedDir(AGENTBAY_BROWSERS_SUBDIR))) {
|
|
9629
|
+
const record2 = readRecord(file2);
|
|
9630
|
+
if (!record2) {
|
|
9631
|
+
removeRecord(file2);
|
|
9632
|
+
continue;
|
|
9633
|
+
}
|
|
9634
|
+
const running = isManagedProcess(record2.pid, `--browser-id ${record2.browserId}`);
|
|
9635
|
+
if (!running) removeRecord(keyedConfigFile("browsers", record2.browserId));
|
|
9636
|
+
records.push({ record: record2, running });
|
|
9637
|
+
}
|
|
9638
|
+
return records;
|
|
9639
|
+
}
|
|
9640
|
+
function findRecord(selector) {
|
|
9641
|
+
const normalized = selector.trim().toLowerCase();
|
|
9642
|
+
return listRecords2().find(
|
|
9643
|
+
({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
|
|
9644
|
+
)?.record ?? null;
|
|
9645
|
+
}
|
|
9646
|
+
function hasAgentBayBrowser(selector) {
|
|
9647
|
+
return findRecord(selector) !== null;
|
|
9648
|
+
}
|
|
9649
|
+
function listAgentBayBrowsers() {
|
|
9650
|
+
return listRecords2().map(({ record: record2, running }) => browserSummary2(record2, running));
|
|
9651
|
+
}
|
|
9652
|
+
async function requireSession(sessionId, apiKey) {
|
|
9653
|
+
const agentBay = await createClient(apiKey);
|
|
9654
|
+
const result = await agentBay.get(sessionId);
|
|
9655
|
+
if (!result.success || !result.session) {
|
|
9656
|
+
throw new Error(result.errorMessage || `AgentBay session not found: ${sessionId}`);
|
|
9657
|
+
}
|
|
9658
|
+
return result.session;
|
|
9659
|
+
}
|
|
9660
|
+
async function resolveAgentBayEndpoint(sessionId) {
|
|
9661
|
+
const apiKey = resolveAgentBayApiKey();
|
|
9662
|
+
const { AgentBay, GetCdpLinkRequest } = await loadSdk();
|
|
9663
|
+
const agentBay = new AgentBay({ apiKey });
|
|
9664
|
+
const response = await agentBay.client.getCdpLink(
|
|
9665
|
+
new GetCdpLinkRequest({ authorization: `Bearer ${apiKey}`, sessionId })
|
|
9666
|
+
);
|
|
9667
|
+
const endpoint = response.body?.success ? response.body.data?.url : void 0;
|
|
9668
|
+
if (!endpoint) {
|
|
9669
|
+
throw new Error(response.body?.message || `Failed to get AgentBay CDP link: ${sessionId}`);
|
|
9670
|
+
}
|
|
9671
|
+
const url2 = new URL(endpoint);
|
|
9672
|
+
if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
|
|
9673
|
+
throw new Error(`AgentBay returned an unsupported CDP endpoint: ${url2.protocol}`);
|
|
9674
|
+
}
|
|
9675
|
+
return url2.toString();
|
|
9676
|
+
}
|
|
9677
|
+
async function keepAgentBaySessionAlive(sessionId) {
|
|
9678
|
+
const session = await requireSession(sessionId, resolveAgentBayApiKey());
|
|
9679
|
+
const result = await session.keepAlive();
|
|
9680
|
+
if (result.success === false) throw new Error(result.errorMessage || "AgentBay keepalive failed");
|
|
9681
|
+
}
|
|
9682
|
+
async function initializeAgentBayBrowserSession(session, input, cookies, options = {}) {
|
|
9683
|
+
const initialized = await session.browser.initializeAsync(
|
|
9684
|
+
options.userAgent ? { userAgent: options.userAgent } : {}
|
|
9685
|
+
);
|
|
9686
|
+
if (!initialized) throw new Error("AgentBay browser initialization failed");
|
|
9687
|
+
const endpoint = await session.browser.getEndpointUrl();
|
|
9688
|
+
const cdp = await (options.connectCdp ?? ((url2) => CdpClient.connect(url2)))(endpoint);
|
|
9689
|
+
try {
|
|
9690
|
+
await cdp.sendCommand("Browser.getVersion", {}, 1e4);
|
|
9691
|
+
const copiedCookieCount = await cdpImportCookiesWithClient(cdp, cookies);
|
|
9692
|
+
if (input.url) {
|
|
9693
|
+
await cdp.sendCommand("Target.createTarget", { url: input.url, background: false });
|
|
9694
|
+
}
|
|
9695
|
+
return copiedCookieCount;
|
|
9696
|
+
} finally {
|
|
9697
|
+
cdp.close();
|
|
9698
|
+
}
|
|
9699
|
+
}
|
|
9700
|
+
async function launchAgentBayBrowser(input, deps) {
|
|
9701
|
+
const name = validateName(input.name);
|
|
9702
|
+
const browserId = agentBayBrowserId(name);
|
|
9703
|
+
validateAgentBayInput(input);
|
|
9704
|
+
const imageId = resolveAgentBayImageId(input.imageId);
|
|
9705
|
+
const userAgentMode = resolveUserAgentMode(input.userAgentMode);
|
|
9706
|
+
const copyCookieDomains = input.copyCookieDomains === void 0 ? [] : normalizeCookieDomains(input.copyCookieDomains);
|
|
9707
|
+
const apiKey = (deps.resolveApiKey ?? resolveAgentBayApiKey)();
|
|
9708
|
+
const releaseLock = acquireLaunchLock(browserId);
|
|
9709
|
+
const persistBrowserRecord = deps.persistBrowserRecord ?? ((record2) => writeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId), record2));
|
|
9710
|
+
let session = null;
|
|
9711
|
+
let hostPid = 0;
|
|
9712
|
+
let recordWritten = false;
|
|
9713
|
+
try {
|
|
9714
|
+
if (findRecord(browserId)) throw new Error(`AgentBay browser already exists: ${browserId}`);
|
|
9715
|
+
const [cookies, userAgent] = await Promise.all([
|
|
9716
|
+
copyCookieDomains.length > 0 ? deps.exportCookies(copyCookieDomains) : Promise.resolve([]),
|
|
9717
|
+
userAgentMode === "desktop" ? deps.getBrowserUserAgent().then(normalizeDesktopUserAgent) : Promise.resolve(void 0)
|
|
9718
|
+
]);
|
|
9719
|
+
const agentBay = await (deps.createAgentBayClient ?? createClient)(apiKey);
|
|
9720
|
+
const created = await agentBay.create({ imageId, framework: "mearl" });
|
|
9721
|
+
if (!created.success || !created.session) {
|
|
9722
|
+
throw new Error(created.errorMessage || "AgentBay session creation failed");
|
|
9723
|
+
}
|
|
9724
|
+
session = created.session;
|
|
9725
|
+
const copiedCookieCount = await initializeAgentBayBrowserSession(session, input, cookies, {
|
|
9726
|
+
userAgent
|
|
9727
|
+
});
|
|
9728
|
+
const host = spawn4(
|
|
9729
|
+
process.execPath,
|
|
9730
|
+
[
|
|
9731
|
+
deps.nativeHostScript,
|
|
9732
|
+
"--daemon",
|
|
9733
|
+
"--agentbay-session-id",
|
|
9734
|
+
session.sessionId,
|
|
9735
|
+
"--browser-id",
|
|
9736
|
+
browserId,
|
|
9737
|
+
"--browser-name",
|
|
9738
|
+
name
|
|
9739
|
+
],
|
|
9740
|
+
{
|
|
9741
|
+
detached: true,
|
|
9742
|
+
stdio: "ignore",
|
|
9743
|
+
windowsHide: true
|
|
9744
|
+
}
|
|
9745
|
+
);
|
|
9746
|
+
await new Promise((resolveSpawn, rejectSpawn) => {
|
|
9747
|
+
host.once("error", rejectSpawn);
|
|
9748
|
+
host.once("spawn", resolveSpawn);
|
|
9749
|
+
});
|
|
9750
|
+
hostPid = host.pid ?? 0;
|
|
9751
|
+
if (!hostPid) throw new Error("AgentBay native host launched without a process id");
|
|
9752
|
+
host.unref();
|
|
9753
|
+
const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
|
|
9754
|
+
const record2 = {
|
|
9755
|
+
pid: hostPid,
|
|
9756
|
+
browserId,
|
|
9757
|
+
name,
|
|
9758
|
+
sessionId: session.sessionId,
|
|
9759
|
+
imageId,
|
|
9760
|
+
...accessUrl ? { accessUrl } : {},
|
|
9761
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9762
|
+
...copyCookieDomains.length > 0 ? { copiedCookies: { domains: copyCookieDomains, count: copiedCookieCount } } : {}
|
|
9763
|
+
};
|
|
9764
|
+
persistBrowserRecord(record2);
|
|
9765
|
+
recordWritten = true;
|
|
9766
|
+
await waitForBrowserRegistration(browserId, hostPid);
|
|
9767
|
+
deps.log(`AgentBay browser launched: ${browserId}, host=${hostPid}`);
|
|
9768
|
+
return browserSummary2(record2, true);
|
|
9769
|
+
} catch (error48) {
|
|
9770
|
+
if (hostPid) await stopProcess(hostPid, `--browser-id ${browserId}`);
|
|
9771
|
+
if (recordWritten) {
|
|
9772
|
+
removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, browserId));
|
|
9773
|
+
removeRecord(keyedConfigFile("browsers", browserId));
|
|
9774
|
+
}
|
|
9775
|
+
if (session) {
|
|
9776
|
+
let cleanupError;
|
|
9777
|
+
try {
|
|
9778
|
+
const deleted = await session.delete(false);
|
|
9779
|
+
if (!deleted.success) {
|
|
9780
|
+
cleanupError = new Error(deleted.errorMessage || "AgentBay session deletion failed");
|
|
9781
|
+
}
|
|
9782
|
+
} catch (deleteError) {
|
|
9783
|
+
cleanupError = deleteError;
|
|
9784
|
+
}
|
|
9785
|
+
if (cleanupError) {
|
|
9786
|
+
const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
|
|
9787
|
+
const recoveryRecord = {
|
|
9788
|
+
pid: hostPid,
|
|
9789
|
+
browserId,
|
|
9790
|
+
name,
|
|
9791
|
+
sessionId: session.sessionId,
|
|
9792
|
+
imageId,
|
|
9793
|
+
...accessUrl ? { accessUrl } : {},
|
|
9794
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9795
|
+
};
|
|
9796
|
+
let recoverySaved = false;
|
|
9797
|
+
let recoveryError;
|
|
9798
|
+
try {
|
|
9799
|
+
persistBrowserRecord(recoveryRecord);
|
|
9800
|
+
recoverySaved = true;
|
|
9801
|
+
} catch (writeError) {
|
|
9802
|
+
recoveryError = writeError;
|
|
9803
|
+
}
|
|
9804
|
+
deps.log(
|
|
9805
|
+
`AgentBay launch cleanup failed for ${session.sessionId}:`,
|
|
9806
|
+
cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
9807
|
+
);
|
|
9808
|
+
if (recoveryError) {
|
|
9809
|
+
deps.log(
|
|
9810
|
+
`AgentBay recovery metadata could not be saved for ${session.sessionId}:`,
|
|
9811
|
+
recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
|
|
9812
|
+
);
|
|
9813
|
+
}
|
|
9814
|
+
throw new AggregateError(
|
|
9815
|
+
recoveryError ? [error48, cleanupError, recoveryError] : [error48, cleanupError],
|
|
9816
|
+
`${error48 instanceof Error ? error48.message : String(error48)}; ` + (recoverySaved ? "AgentBay session cleanup failed and recovery metadata was retained for browser_close" : `AgentBay session cleanup failed; session ${session.sessionId} may require manual deletion`),
|
|
9817
|
+
{ cause: error48 }
|
|
9818
|
+
);
|
|
9819
|
+
}
|
|
9820
|
+
}
|
|
9821
|
+
throw error48;
|
|
9822
|
+
} finally {
|
|
9823
|
+
releaseLock();
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
function isMissingSessionError(error48) {
|
|
9827
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
9828
|
+
return /not[ -]?found|does not exist|already deleted|invalid session/i.test(message);
|
|
9829
|
+
}
|
|
9830
|
+
async function closeAgentBayBrowser(selector) {
|
|
9831
|
+
const record2 = findRecord(selector);
|
|
9832
|
+
if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
|
|
9833
|
+
try {
|
|
9834
|
+
const session = await requireSession(record2.sessionId, resolveAgentBayApiKey());
|
|
9835
|
+
const result = await session.delete(false);
|
|
9836
|
+
if (!result.success) throw new Error(result.errorMessage || "AgentBay session deletion failed");
|
|
9837
|
+
} catch (error48) {
|
|
9838
|
+
if (!isMissingSessionError(error48)) throw error48;
|
|
9839
|
+
}
|
|
9840
|
+
const closingOwnHost = record2.pid === process.pid;
|
|
9841
|
+
if (!closingOwnHost) await stopProcess(record2.pid, `--browser-id ${record2.browserId}`);
|
|
9842
|
+
removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId));
|
|
9843
|
+
removeRecord(keyedConfigFile("browsers", record2.browserId));
|
|
9844
|
+
if (closingOwnHost) {
|
|
9845
|
+
const timer = setTimeout(() => process.kill(process.pid, "SIGTERM"), 250);
|
|
9846
|
+
timer.unref();
|
|
9847
|
+
}
|
|
9848
|
+
return {
|
|
9849
|
+
browserId: record2.browserId,
|
|
9850
|
+
provider: "agentbay",
|
|
9851
|
+
closed: true,
|
|
9852
|
+
sessionDeleted: true
|
|
9853
|
+
};
|
|
9854
|
+
}
|
|
9855
|
+
|
|
9569
9856
|
// src/browser-registry.ts
|
|
9570
9857
|
var BROWSERS_SUBDIR = "browsers";
|
|
9571
9858
|
var STALE_RECORD_TTL_MS = 5 * 6e4;
|
|
@@ -9715,10 +10002,14 @@ function mergeBrowserInventory(connected, managed) {
|
|
|
9715
10002
|
...connectionFields(record2),
|
|
9716
10003
|
headless: managedBrowser.headless,
|
|
9717
10004
|
persistent: managedBrowser.persistent,
|
|
9718
|
-
|
|
10005
|
+
provider: managedBrowser.provider,
|
|
10006
|
+
..."cdpPort" in managedBrowser ? { cdpPort: managedBrowser.cdpPort } : {},
|
|
10007
|
+
..."agentbaySessionId" in managedBrowser ? { agentbaySessionId: managedBrowser.agentbaySessionId } : {},
|
|
10008
|
+
..."agentbayImageId" in managedBrowser && managedBrowser.agentbayImageId ? { agentbayImageId: managedBrowser.agentbayImageId } : {},
|
|
10009
|
+
..."agentbayAccessUrl" in managedBrowser && managedBrowser.agentbayAccessUrl ? { agentbayAccessUrl: managedBrowser.agentbayAccessUrl } : {},
|
|
9719
10010
|
createdAt: managedBrowser.createdAt,
|
|
9720
|
-
account: managedBrowser.account,
|
|
9721
|
-
copiedCookies: managedBrowser.copiedCookies
|
|
10011
|
+
..."account" in managedBrowser ? { account: managedBrowser.account } : {},
|
|
10012
|
+
..."copiedCookies" in managedBrowser ? { copiedCookies: managedBrowser.copiedCookies } : {}
|
|
9722
10013
|
};
|
|
9723
10014
|
});
|
|
9724
10015
|
const disconnected = [...managedById.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map((browser) => ({
|
|
@@ -9728,10 +10019,14 @@ function mergeBrowserInventory(connected, managed) {
|
|
|
9728
10019
|
status: browser.running ? "running_disconnected" : "stopped",
|
|
9729
10020
|
headless: browser.headless,
|
|
9730
10021
|
persistent: browser.persistent,
|
|
9731
|
-
|
|
10022
|
+
provider: browser.provider,
|
|
10023
|
+
..."cdpPort" in browser ? { cdpPort: browser.cdpPort } : {},
|
|
10024
|
+
..."agentbaySessionId" in browser ? { agentbaySessionId: browser.agentbaySessionId } : {},
|
|
10025
|
+
..."agentbayImageId" in browser && browser.agentbayImageId ? { agentbayImageId: browser.agentbayImageId } : {},
|
|
10026
|
+
..."agentbayAccessUrl" in browser && browser.agentbayAccessUrl ? { agentbayAccessUrl: browser.agentbayAccessUrl } : {},
|
|
9732
10027
|
createdAt: browser.createdAt,
|
|
9733
|
-
account: browser.account,
|
|
9734
|
-
copiedCookies: browser.copiedCookies
|
|
10028
|
+
..."account" in browser ? { account: browser.account } : {},
|
|
10029
|
+
..."copiedCookies" in browser ? { copiedCookies: browser.copiedCookies } : {}
|
|
9735
10030
|
}));
|
|
9736
10031
|
browsers.push(...disconnected);
|
|
9737
10032
|
return {
|
|
@@ -9741,7 +10036,7 @@ function mergeBrowserInventory(connected, managed) {
|
|
|
9741
10036
|
};
|
|
9742
10037
|
}
|
|
9743
10038
|
function listBrowserInventory() {
|
|
9744
|
-
const managed = listManagedBrowsers();
|
|
10039
|
+
const managed = [...listManagedBrowsers(), ...listAgentBayBrowsers()];
|
|
9745
10040
|
return mergeBrowserInventory(listRegisteredBrowsers(), managed);
|
|
9746
10041
|
}
|
|
9747
10042
|
|
|
@@ -9752,7 +10047,7 @@ import os2 from "node:os";
|
|
|
9752
10047
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
9753
10048
|
|
|
9754
10049
|
// src/version.ts
|
|
9755
|
-
var NATIVE_HOST_VERSION = true ? "2.
|
|
10050
|
+
var NATIVE_HOST_VERSION = true ? "2.5.0" : "unknown";
|
|
9756
10051
|
|
|
9757
10052
|
// src/analytics.ts
|
|
9758
10053
|
var MEASUREMENT_ID = "G-W84LJDZ6PH";
|
|
@@ -9936,14 +10231,14 @@ function selectedPath(stdout) {
|
|
|
9936
10231
|
const value = stdout.replace(/[\r\n]+$/, "");
|
|
9937
10232
|
return value || null;
|
|
9938
10233
|
}
|
|
9939
|
-
var runDirectoryPickerCommand = (command, args) => new Promise((
|
|
10234
|
+
var runDirectoryPickerCommand = (command, args) => new Promise((resolve2, reject) => {
|
|
9940
10235
|
execFile(command, args, { encoding: "utf8", windowsHide: true }, (error48, stdout) => {
|
|
9941
10236
|
if (!error48) {
|
|
9942
|
-
|
|
10237
|
+
resolve2(selectedPath(stdout));
|
|
9943
10238
|
return;
|
|
9944
10239
|
}
|
|
9945
10240
|
if (error48.code === 1) {
|
|
9946
|
-
|
|
10241
|
+
resolve2(null);
|
|
9947
10242
|
return;
|
|
9948
10243
|
}
|
|
9949
10244
|
reject(error48);
|
|
@@ -10614,17 +10909,18 @@ var AgentChatManager = class {
|
|
|
10614
10909
|
// src/agent/codex-provider.ts
|
|
10615
10910
|
import os3 from "node:os";
|
|
10616
10911
|
import readline from "node:readline";
|
|
10617
|
-
import { spawn as
|
|
10912
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
10618
10913
|
|
|
10619
10914
|
// src/agent/executable-resolver.ts
|
|
10620
10915
|
import fs6 from "node:fs";
|
|
10621
|
-
import { spawn as
|
|
10916
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
10622
10917
|
import path7 from "node:path";
|
|
10623
10918
|
|
|
10624
10919
|
// ../setup/dist/process.js
|
|
10625
10920
|
import fs5 from "node:fs";
|
|
10626
10921
|
import path6 from "node:path";
|
|
10627
|
-
import { spawn as
|
|
10922
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
10923
|
+
var MAX_CAPTURE_LENGTH = 1e6;
|
|
10628
10924
|
function isExecutable(filePath, platform = process.platform) {
|
|
10629
10925
|
try {
|
|
10630
10926
|
fs5.accessSync(filePath, platform === "win32" ? fs5.constants.F_OK : fs5.constants.X_OK);
|
|
@@ -10669,12 +10965,12 @@ function resolveSpawnCommand(executable, args, platform = process.platform, comm
|
|
|
10669
10965
|
function prependCommandPath(command, env = process.env) {
|
|
10670
10966
|
if (!path6.isAbsolute(command))
|
|
10671
10967
|
return env;
|
|
10672
|
-
return
|
|
10968
|
+
return prependPathDirectory(path6.dirname(command), env);
|
|
10673
10969
|
}
|
|
10674
10970
|
function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
|
|
10675
|
-
return
|
|
10971
|
+
return prependPathDirectory(path6.dirname(nodeExecutable), env);
|
|
10676
10972
|
}
|
|
10677
|
-
function
|
|
10973
|
+
function prependPathDirectory(directory, env = process.env) {
|
|
10678
10974
|
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
|
|
10679
10975
|
const inherited = env[pathKey] || "";
|
|
10680
10976
|
const entries = inherited.split(path6.delimiter).filter(Boolean);
|
|
@@ -10682,6 +10978,59 @@ function prependPath(directory, env) {
|
|
|
10682
10978
|
return env;
|
|
10683
10979
|
return { ...env, [pathKey]: [directory, ...entries].join(path6.delimiter) };
|
|
10684
10980
|
}
|
|
10981
|
+
function findExecutable(name, options = {}) {
|
|
10982
|
+
const env = options.env ?? process.env;
|
|
10983
|
+
const platform = options.platform ?? process.platform;
|
|
10984
|
+
const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
|
|
10985
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
|
|
10986
|
+
const directories = [
|
|
10987
|
+
...options.extraDirectories ?? [],
|
|
10988
|
+
...(env[pathKey] ?? "").split(path6.delimiter).filter(Boolean)
|
|
10989
|
+
];
|
|
10990
|
+
for (const directory of directories) {
|
|
10991
|
+
for (const executableName of executableNames(name, platform)) {
|
|
10992
|
+
const candidate = path6.join(directory, executableName);
|
|
10993
|
+
if (existsSync5(candidate))
|
|
10994
|
+
return candidate;
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
return null;
|
|
10998
|
+
}
|
|
10999
|
+
var runCommand = (command, args, options = {}) => new Promise((resolve2, reject) => {
|
|
11000
|
+
const capture = options.capture === true;
|
|
11001
|
+
const launch = resolveSpawnCommand(command, args);
|
|
11002
|
+
const child = spawn5(launch.command, launch.args, {
|
|
11003
|
+
cwd: options.cwd,
|
|
11004
|
+
env: options.env,
|
|
11005
|
+
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
11006
|
+
windowsHide: true
|
|
11007
|
+
});
|
|
11008
|
+
let stdout = "";
|
|
11009
|
+
let stderr = "";
|
|
11010
|
+
let settled = false;
|
|
11011
|
+
const append = (current, chunk) => current.length >= MAX_CAPTURE_LENGTH ? current : (current + chunk.toString("utf8")).slice(0, MAX_CAPTURE_LENGTH);
|
|
11012
|
+
const finish = (error48, code = 1) => {
|
|
11013
|
+
if (settled)
|
|
11014
|
+
return;
|
|
11015
|
+
settled = true;
|
|
11016
|
+
if (timer)
|
|
11017
|
+
clearTimeout(timer);
|
|
11018
|
+
if (error48)
|
|
11019
|
+
reject(error48);
|
|
11020
|
+
else
|
|
11021
|
+
resolve2({ code, stdout, stderr });
|
|
11022
|
+
};
|
|
11023
|
+
const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
11024
|
+
child.kill("SIGTERM");
|
|
11025
|
+
finish(new Error(`Command timed out after ${options.timeoutMs}ms`));
|
|
11026
|
+
}, options.timeoutMs);
|
|
11027
|
+
if (capture) {
|
|
11028
|
+
child.stdout?.on("data", (chunk) => stdout = append(stdout, chunk));
|
|
11029
|
+
child.stderr?.on("data", (chunk) => stderr = append(stderr, chunk));
|
|
11030
|
+
}
|
|
11031
|
+
child.once("error", (error48) => finish(error48));
|
|
11032
|
+
child.once("close", (code) => finish(void 0, code ?? 1));
|
|
11033
|
+
});
|
|
10685
11034
|
|
|
10686
11035
|
// src/agent/executable-resolver.ts
|
|
10687
11036
|
function agentExecutableCandidatePaths({
|
|
@@ -10791,11 +11140,11 @@ function executableFileCandidates(candidates) {
|
|
|
10791
11140
|
return executableCandidates;
|
|
10792
11141
|
}
|
|
10793
11142
|
function probeAgentExecutable(executable, options) {
|
|
10794
|
-
return new Promise((
|
|
11143
|
+
return new Promise((resolve2) => {
|
|
10795
11144
|
let settled = false;
|
|
10796
11145
|
let stderr = "";
|
|
10797
11146
|
const launch = resolveSpawnCommand(executable, options.args);
|
|
10798
|
-
const child =
|
|
11147
|
+
const child = spawn6(launch.command, launch.args, {
|
|
10799
11148
|
stdio: ["ignore", "ignore", "pipe"],
|
|
10800
11149
|
windowsHide: true,
|
|
10801
11150
|
env: prependNodePath()
|
|
@@ -10804,7 +11153,7 @@ function probeAgentExecutable(executable, options) {
|
|
|
10804
11153
|
if (settled) return;
|
|
10805
11154
|
settled = true;
|
|
10806
11155
|
clearTimeout(timer);
|
|
10807
|
-
|
|
11156
|
+
resolve2(error48);
|
|
10808
11157
|
};
|
|
10809
11158
|
const timer = setTimeout(() => {
|
|
10810
11159
|
child.kill("SIGTERM");
|
|
@@ -10935,13 +11284,13 @@ function externalOpenCommand(url2, platform = process.platform) {
|
|
|
10935
11284
|
}
|
|
10936
11285
|
function openExternalUrl(url2) {
|
|
10937
11286
|
const { command, args } = externalOpenCommand(url2);
|
|
10938
|
-
return new Promise((
|
|
10939
|
-
const child =
|
|
11287
|
+
return new Promise((resolve2, reject) => {
|
|
11288
|
+
const child = spawn7(command, args, { stdio: "ignore", windowsHide: true });
|
|
10940
11289
|
child.once("error", (error48) => {
|
|
10941
11290
|
reject(new Error(`Unable to open Codex App: ${error48.message}`));
|
|
10942
11291
|
});
|
|
10943
11292
|
child.once("close", (code) => {
|
|
10944
|
-
if (code === 0)
|
|
11293
|
+
if (code === 0) resolve2();
|
|
10945
11294
|
else reject(new Error("Unable to open Codex App. Make sure it is installed."));
|
|
10946
11295
|
});
|
|
10947
11296
|
});
|
|
@@ -11108,7 +11457,7 @@ var CodexAgentProvider = class {
|
|
|
11108
11457
|
url: `data:${image.mimeType};base64,${image.data}`
|
|
11109
11458
|
}))
|
|
11110
11459
|
],
|
|
11111
|
-
summary: "
|
|
11460
|
+
summary: "detailed"
|
|
11112
11461
|
});
|
|
11113
11462
|
const turnId = result?.turn?.id;
|
|
11114
11463
|
if (typeof turnId !== "string" || !turnId) {
|
|
@@ -11170,7 +11519,7 @@ var CodexAgentProvider = class {
|
|
|
11170
11519
|
const launch = resolveSpawnCommand(executable, ["app-server", "--listen", "stdio://"]);
|
|
11171
11520
|
const childEnv = prependNodePath();
|
|
11172
11521
|
if (this.mearlSocketPath) childEnv.MEARL_SOCKET_PATH = this.mearlSocketPath;
|
|
11173
|
-
const child =
|
|
11522
|
+
const child = spawn7(launch.command, launch.args, {
|
|
11174
11523
|
stdio: ["pipe", "pipe", "pipe"],
|
|
11175
11524
|
windowsHide: true,
|
|
11176
11525
|
env: childEnv
|
|
@@ -11262,12 +11611,12 @@ var CodexAgentProvider = class {
|
|
|
11262
11611
|
}
|
|
11263
11612
|
request(method, params) {
|
|
11264
11613
|
const id = this.nextRequestId++;
|
|
11265
|
-
return new Promise((
|
|
11614
|
+
return new Promise((resolve2, reject) => {
|
|
11266
11615
|
const timer = setTimeout(() => {
|
|
11267
11616
|
this.pendingRequests.delete(id);
|
|
11268
11617
|
reject(new Error(`Codex request timed out: ${method}`));
|
|
11269
11618
|
}, REQUEST_TIMEOUT_MS);
|
|
11270
|
-
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
11619
|
+
this.pendingRequests.set(id, { resolve: resolve2, reject, timer });
|
|
11271
11620
|
try {
|
|
11272
11621
|
this.write({ id, method, params });
|
|
11273
11622
|
} catch (error48) {
|
|
@@ -11341,7 +11690,7 @@ var CodexAgentProvider = class {
|
|
|
11341
11690
|
import fs7 from "node:fs";
|
|
11342
11691
|
import os4 from "node:os";
|
|
11343
11692
|
import path8 from "node:path";
|
|
11344
|
-
import { spawn as
|
|
11693
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
11345
11694
|
import { Readable, Writable } from "node:stream";
|
|
11346
11695
|
|
|
11347
11696
|
// ../../node_modules/.pnpm/@agentclientprotocol+sdk@1.2.1_zod@4.3.6/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -11577,7 +11926,7 @@ __export(external_exports, {
|
|
|
11577
11926
|
object: () => object,
|
|
11578
11927
|
optional: () => optional,
|
|
11579
11928
|
overwrite: () => _overwrite,
|
|
11580
|
-
parse: () =>
|
|
11929
|
+
parse: () => parse3,
|
|
11581
11930
|
parseAsync: () => parseAsync2,
|
|
11582
11931
|
partialRecord: () => partialRecord,
|
|
11583
11932
|
pipe: () => pipe,
|
|
@@ -11896,7 +12245,7 @@ __export(core_exports2, {
|
|
|
11896
12245
|
isValidJWT: () => isValidJWT,
|
|
11897
12246
|
locales: () => locales_exports,
|
|
11898
12247
|
meta: () => meta,
|
|
11899
|
-
parse: () =>
|
|
12248
|
+
parse: () => parse2,
|
|
11900
12249
|
parseAsync: () => parseAsync,
|
|
11901
12250
|
prettifyError: () => prettifyError,
|
|
11902
12251
|
process: () => process2,
|
|
@@ -12163,10 +12512,10 @@ function mergeDefs(...defs) {
|
|
|
12163
12512
|
function cloneDef(schema) {
|
|
12164
12513
|
return mergeDefs(schema._zod.def);
|
|
12165
12514
|
}
|
|
12166
|
-
function getElementAtPath(obj,
|
|
12167
|
-
if (!
|
|
12515
|
+
function getElementAtPath(obj, path15) {
|
|
12516
|
+
if (!path15)
|
|
12168
12517
|
return obj;
|
|
12169
|
-
return
|
|
12518
|
+
return path15.reduce((acc, key) => acc?.[key], obj);
|
|
12170
12519
|
}
|
|
12171
12520
|
function promiseAllObject(promisesObj) {
|
|
12172
12521
|
const keys = Object.keys(promisesObj);
|
|
@@ -12549,11 +12898,11 @@ function aborted(x, startIndex = 0) {
|
|
|
12549
12898
|
}
|
|
12550
12899
|
return false;
|
|
12551
12900
|
}
|
|
12552
|
-
function prefixIssues(
|
|
12901
|
+
function prefixIssues(path15, issues) {
|
|
12553
12902
|
return issues.map((iss) => {
|
|
12554
12903
|
var _a2;
|
|
12555
12904
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
12556
|
-
iss.path.unshift(
|
|
12905
|
+
iss.path.unshift(path15);
|
|
12557
12906
|
return iss;
|
|
12558
12907
|
});
|
|
12559
12908
|
}
|
|
@@ -12736,7 +13085,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12736
13085
|
}
|
|
12737
13086
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
12738
13087
|
const result = { errors: [] };
|
|
12739
|
-
const processError = (error49,
|
|
13088
|
+
const processError = (error49, path15 = []) => {
|
|
12740
13089
|
var _a2, _b;
|
|
12741
13090
|
for (const issue2 of error49.issues) {
|
|
12742
13091
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -12746,7 +13095,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12746
13095
|
} else if (issue2.code === "invalid_element") {
|
|
12747
13096
|
processError({ issues: issue2.issues }, issue2.path);
|
|
12748
13097
|
} else {
|
|
12749
|
-
const fullpath = [...
|
|
13098
|
+
const fullpath = [...path15, ...issue2.path];
|
|
12750
13099
|
if (fullpath.length === 0) {
|
|
12751
13100
|
result.errors.push(mapper(issue2));
|
|
12752
13101
|
continue;
|
|
@@ -12778,8 +13127,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
12778
13127
|
}
|
|
12779
13128
|
function toDotPath(_path) {
|
|
12780
13129
|
const segs = [];
|
|
12781
|
-
const
|
|
12782
|
-
for (const seg of
|
|
13130
|
+
const path15 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
13131
|
+
for (const seg of path15) {
|
|
12783
13132
|
if (typeof seg === "number")
|
|
12784
13133
|
segs.push(`[${seg}]`);
|
|
12785
13134
|
else if (typeof seg === "symbol")
|
|
@@ -12819,7 +13168,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
|
12819
13168
|
}
|
|
12820
13169
|
return result.value;
|
|
12821
13170
|
};
|
|
12822
|
-
var
|
|
13171
|
+
var parse2 = /* @__PURE__ */ _parse($ZodRealError);
|
|
12823
13172
|
var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
|
|
12824
13173
|
const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
|
|
12825
13174
|
let result = schema._zod.run({ value, issues: [] }, ctx);
|
|
@@ -15492,10 +15841,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
15492
15841
|
throw new Error("implement() must be called with a function");
|
|
15493
15842
|
}
|
|
15494
15843
|
return function(...args) {
|
|
15495
|
-
const parsedArgs = inst._def.input ?
|
|
15844
|
+
const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
|
|
15496
15845
|
const result = Reflect.apply(func, this, parsedArgs);
|
|
15497
15846
|
if (inst._def.output) {
|
|
15498
|
-
return
|
|
15847
|
+
return parse2(inst._def.output, result);
|
|
15499
15848
|
}
|
|
15500
15849
|
return result;
|
|
15501
15850
|
};
|
|
@@ -23544,7 +23893,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
|
|
|
23544
23893
|
});
|
|
23545
23894
|
|
|
23546
23895
|
// ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/parse.js
|
|
23547
|
-
var
|
|
23896
|
+
var parse3 = /* @__PURE__ */ _parse(ZodRealError);
|
|
23548
23897
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
23549
23898
|
var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
23550
23899
|
var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
@@ -23587,7 +23936,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
23587
23936
|
reg.add(inst, meta3);
|
|
23588
23937
|
return inst;
|
|
23589
23938
|
});
|
|
23590
|
-
inst.parse = (data, params) =>
|
|
23939
|
+
inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
|
|
23591
23940
|
inst.safeParse = (data, params) => safeParse2(inst, data, params);
|
|
23592
23941
|
inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
|
|
23593
23942
|
inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
|
|
@@ -24756,13 +25105,13 @@ function resolveRef(ref, ctx) {
|
|
|
24756
25105
|
if (!ref.startsWith("#")) {
|
|
24757
25106
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24758
25107
|
}
|
|
24759
|
-
const
|
|
24760
|
-
if (
|
|
25108
|
+
const path15 = ref.slice(1).split("/").filter(Boolean);
|
|
25109
|
+
if (path15.length === 0) {
|
|
24761
25110
|
return ctx.rootSchema;
|
|
24762
25111
|
}
|
|
24763
25112
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24764
|
-
if (
|
|
24765
|
-
const key =
|
|
25113
|
+
if (path15[0] === defsKey) {
|
|
25114
|
+
const key = path15[1];
|
|
24766
25115
|
if (!key || !ctx.defs[key]) {
|
|
24767
25116
|
throw new Error(`Reference not found: ${ref}`);
|
|
24768
25117
|
}
|
|
@@ -27146,12 +27495,12 @@ var Connection = class {
|
|
|
27146
27495
|
const id = this.nextRequestId++;
|
|
27147
27496
|
let cancel = () => {
|
|
27148
27497
|
};
|
|
27149
|
-
const responsePromise = new Promise((
|
|
27498
|
+
const responsePromise = new Promise((resolve2, reject) => {
|
|
27150
27499
|
const pendingResponse = {
|
|
27151
27500
|
resolve: (response) => {
|
|
27152
27501
|
try {
|
|
27153
27502
|
const value = mapResponse ? mapResponse(response) : response;
|
|
27154
|
-
|
|
27503
|
+
resolve2(value);
|
|
27155
27504
|
} catch (error48) {
|
|
27156
27505
|
reject(error48);
|
|
27157
27506
|
}
|
|
@@ -27229,8 +27578,8 @@ var Connection = class {
|
|
|
27229
27578
|
initialize(stream, handlers) {
|
|
27230
27579
|
this.stream = stream;
|
|
27231
27580
|
this.staticHandlers = handlers;
|
|
27232
|
-
this.closedPromise = new Promise((
|
|
27233
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
27581
|
+
this.closedPromise = new Promise((resolve2) => {
|
|
27582
|
+
this.abortController.signal.addEventListener("abort", () => resolve2());
|
|
27234
27583
|
});
|
|
27235
27584
|
void this.receive();
|
|
27236
27585
|
}
|
|
@@ -27455,13 +27804,13 @@ var ConnectionBuilder = class {
|
|
|
27455
27804
|
/**
|
|
27456
27805
|
* Adds a typed request handler for one method.
|
|
27457
27806
|
*/
|
|
27458
|
-
onReceiveRequest(method,
|
|
27807
|
+
onReceiveRequest(method, parse4, handler) {
|
|
27459
27808
|
return this.withHandler({
|
|
27460
27809
|
handleMessage: async (message, cx) => {
|
|
27461
27810
|
if (message.kind !== "request" || message.method !== method) {
|
|
27462
27811
|
return Handled.no(message);
|
|
27463
27812
|
}
|
|
27464
|
-
const request =
|
|
27813
|
+
const request = parse4(message.params);
|
|
27465
27814
|
return await handler(request, message.responder, cx) ?? Handled.yes();
|
|
27466
27815
|
},
|
|
27467
27816
|
describe: () => `${this.connectionName ?? "request"}:${method}`
|
|
@@ -27470,13 +27819,13 @@ var ConnectionBuilder = class {
|
|
|
27470
27819
|
/**
|
|
27471
27820
|
* Adds a typed notification handler for one method.
|
|
27472
27821
|
*/
|
|
27473
|
-
onReceiveNotification(method,
|
|
27822
|
+
onReceiveNotification(method, parse4, handler) {
|
|
27474
27823
|
return this.withHandler({
|
|
27475
27824
|
handleMessage: async (message, cx) => {
|
|
27476
27825
|
if (message.kind !== "notification" || message.method !== method) {
|
|
27477
27826
|
return Handled.no(message);
|
|
27478
27827
|
}
|
|
27479
|
-
const notification =
|
|
27828
|
+
const notification = parse4(message.params);
|
|
27480
27829
|
return await handler(notification, cx) ?? Handled.yes();
|
|
27481
27830
|
},
|
|
27482
27831
|
describe: () => `${this.connectionName ?? "notification"}:${method}`
|
|
@@ -28018,8 +28367,8 @@ var AsyncQueue = class {
|
|
|
28018
28367
|
if (this.failed) {
|
|
28019
28368
|
return Promise.reject(this.failure);
|
|
28020
28369
|
}
|
|
28021
|
-
return new Promise((
|
|
28022
|
-
this.waiters.push({ resolve, reject });
|
|
28370
|
+
return new Promise((resolve2, reject) => {
|
|
28371
|
+
this.waiters.push({ resolve: resolve2, reject });
|
|
28023
28372
|
});
|
|
28024
28373
|
}
|
|
28025
28374
|
};
|
|
@@ -29130,7 +29479,7 @@ ${message}` : message;
|
|
|
29130
29479
|
}
|
|
29131
29480
|
const { executable } = resolution;
|
|
29132
29481
|
const launch = resolveSpawnCommand(executable, ["--acp"]);
|
|
29133
|
-
const child =
|
|
29482
|
+
const child = spawn8(launch.command, launch.args, {
|
|
29134
29483
|
stdio: ["pipe", "pipe", "pipe"],
|
|
29135
29484
|
windowsHide: true,
|
|
29136
29485
|
env: prependNodePath()
|
|
@@ -29398,10 +29747,10 @@ ${message}` : message;
|
|
|
29398
29747
|
if (requestId === null) {
|
|
29399
29748
|
return Promise.resolve({ outcome: { outcome: "cancelled" } });
|
|
29400
29749
|
}
|
|
29401
|
-
return new Promise((
|
|
29750
|
+
return new Promise((resolve2) => {
|
|
29402
29751
|
this.pendingPermissions.set(requestId, {
|
|
29403
29752
|
providerSessionId: request.sessionId,
|
|
29404
|
-
resolve
|
|
29753
|
+
resolve: resolve2
|
|
29405
29754
|
});
|
|
29406
29755
|
this.emit({
|
|
29407
29756
|
providerSessionId: request.sessionId,
|
|
@@ -29448,7 +29797,7 @@ ${message}` : message;
|
|
|
29448
29797
|
}
|
|
29449
29798
|
}
|
|
29450
29799
|
withTimeout(promise2, operation) {
|
|
29451
|
-
return new Promise((
|
|
29800
|
+
return new Promise((resolve2, reject) => {
|
|
29452
29801
|
const timer = setTimeout(
|
|
29453
29802
|
() => reject(new Error(`${operation} timed out`)),
|
|
29454
29803
|
REQUEST_TIMEOUT_MS2
|
|
@@ -29456,7 +29805,7 @@ ${message}` : message;
|
|
|
29456
29805
|
promise2.then(
|
|
29457
29806
|
(result) => {
|
|
29458
29807
|
clearTimeout(timer);
|
|
29459
|
-
|
|
29808
|
+
resolve2(result);
|
|
29460
29809
|
},
|
|
29461
29810
|
(error48) => {
|
|
29462
29811
|
clearTimeout(timer);
|
|
@@ -29475,9 +29824,9 @@ ${message}` : message;
|
|
|
29475
29824
|
});
|
|
29476
29825
|
}
|
|
29477
29826
|
runSkillsList(executable, cwd) {
|
|
29478
|
-
return new Promise((
|
|
29827
|
+
return new Promise((resolve2, reject) => {
|
|
29479
29828
|
const launch = resolveSpawnCommand(executable, ["skill", "list", "--all"]);
|
|
29480
|
-
const child =
|
|
29829
|
+
const child = spawn8(launch.command, launch.args, {
|
|
29481
29830
|
cwd,
|
|
29482
29831
|
stdio: ["ignore", "pipe", "pipe"],
|
|
29483
29832
|
windowsHide: true,
|
|
@@ -29491,7 +29840,7 @@ ${message}` : message;
|
|
|
29491
29840
|
settled = true;
|
|
29492
29841
|
clearTimeout(timer);
|
|
29493
29842
|
if (error48) reject(error48);
|
|
29494
|
-
else
|
|
29843
|
+
else resolve2(stdout);
|
|
29495
29844
|
};
|
|
29496
29845
|
const timer = setTimeout(() => {
|
|
29497
29846
|
child.kill("SIGTERM");
|
|
@@ -29526,12 +29875,12 @@ ${message}` : message;
|
|
|
29526
29875
|
import fs8 from "node:fs";
|
|
29527
29876
|
import os5 from "node:os";
|
|
29528
29877
|
import path9 from "node:path";
|
|
29529
|
-
import { spawn as
|
|
29878
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
29530
29879
|
var COMMAND_TIMEOUT_MS = 12e4;
|
|
29531
29880
|
var MAX_OUTPUT_LENGTH = 1e6;
|
|
29532
29881
|
var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g");
|
|
29533
|
-
function existingExecutable(candidates,
|
|
29534
|
-
return candidates.find((candidate) => candidate &&
|
|
29882
|
+
function existingExecutable(candidates, existsSync5) {
|
|
29883
|
+
return candidates.find((candidate) => candidate && existsSync5(candidate)) ?? null;
|
|
29535
29884
|
}
|
|
29536
29885
|
function stripAnsi(value) {
|
|
29537
29886
|
return value.replace(ANSI_ESCAPE_PATTERN, "");
|
|
@@ -29540,7 +29889,7 @@ function resolveAliSkillsExecutable(options = {}) {
|
|
|
29540
29889
|
const env = options.env ?? process.env;
|
|
29541
29890
|
const home = (options.homedir ?? os5.homedir)();
|
|
29542
29891
|
const platform = options.platform ?? process.platform;
|
|
29543
|
-
const
|
|
29892
|
+
const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
|
|
29544
29893
|
const processExecPath = options.processExecPath ?? process.execPath;
|
|
29545
29894
|
const readdirSync2 = options.readdirSync ?? fs8.readdirSync;
|
|
29546
29895
|
const names = executableNames("ali-skills", platform);
|
|
@@ -29562,11 +29911,11 @@ function resolveAliSkillsExecutable(options = {}) {
|
|
|
29562
29911
|
...platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
|
|
29563
29912
|
...pathCandidates
|
|
29564
29913
|
];
|
|
29565
|
-
return existingExecutable(candidates,
|
|
29914
|
+
return existingExecutable(candidates, existsSync5);
|
|
29566
29915
|
}
|
|
29567
|
-
var runAliSkillsCli = (executable, args, cwd) => new Promise((
|
|
29916
|
+
var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve2, reject) => {
|
|
29568
29917
|
const launch = resolveSpawnCommand(executable, args);
|
|
29569
|
-
const child =
|
|
29918
|
+
const child = spawn9(launch.command, launch.args, {
|
|
29570
29919
|
cwd,
|
|
29571
29920
|
env: prependNodePath(prependCommandPath(executable)),
|
|
29572
29921
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -29584,7 +29933,7 @@ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) =
|
|
|
29584
29933
|
if (error48)
|
|
29585
29934
|
reject(error48);
|
|
29586
29935
|
else
|
|
29587
|
-
|
|
29936
|
+
resolve2({ stdout, stderr });
|
|
29588
29937
|
};
|
|
29589
29938
|
const timer = setTimeout(() => {
|
|
29590
29939
|
child.kill("SIGTERM");
|
|
@@ -29769,6 +30118,409 @@ function createExtensionRequestMessage(id, action, data, trustedInternal = false
|
|
|
29769
30118
|
};
|
|
29770
30119
|
}
|
|
29771
30120
|
|
|
30121
|
+
// src/host-updater.ts
|
|
30122
|
+
import fs10 from "node:fs";
|
|
30123
|
+
import path11 from "node:path";
|
|
30124
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
30125
|
+
|
|
30126
|
+
// ../setup/dist/packageManager.js
|
|
30127
|
+
import fs9 from "node:fs";
|
|
30128
|
+
import path10 from "node:path";
|
|
30129
|
+
function normalizePath(filePath) {
|
|
30130
|
+
return filePath.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
30131
|
+
}
|
|
30132
|
+
function firstExisting(candidates, existsSync5) {
|
|
30133
|
+
return candidates.find((candidate) => existsSync5(candidate));
|
|
30134
|
+
}
|
|
30135
|
+
function fallbackCommand(name, platform) {
|
|
30136
|
+
if (platform !== "win32")
|
|
30137
|
+
return name;
|
|
30138
|
+
if (name === "bun" || name === "volta")
|
|
30139
|
+
return `${name}.exe`;
|
|
30140
|
+
return `${name}.cmd`;
|
|
30141
|
+
}
|
|
30142
|
+
function resolvePackageManagerInstallation(packageRoot, packageName, options = {}) {
|
|
30143
|
+
const env = options.env ?? process.env;
|
|
30144
|
+
const platform = options.platform ?? process.platform;
|
|
30145
|
+
const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
|
|
30146
|
+
const processExecPath = options.processExecPath ?? process.execPath;
|
|
30147
|
+
const normalizedRoot = normalizePath(path10.resolve(packageRoot));
|
|
30148
|
+
const lowerRoot = normalizedRoot.toLowerCase();
|
|
30149
|
+
const lowerPackageName = packageName.toLowerCase();
|
|
30150
|
+
const pnpmGlobalRoot = /\/global\/\d+\/(?:\.pnpm\/.*\/)?node_modules\//.exec(lowerRoot);
|
|
30151
|
+
if (pnpmGlobalRoot && lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)) {
|
|
30152
|
+
const pnpmHome = normalizedRoot.slice(0, pnpmGlobalRoot.index);
|
|
30153
|
+
const command = firstExisting([
|
|
30154
|
+
...executableNames("pnpm", platform).map((name) => path10.join(pnpmHome, name)),
|
|
30155
|
+
...executableNames("pnpm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
|
|
30156
|
+
], existsSync5) ?? fallbackCommand("pnpm", platform);
|
|
30157
|
+
return { manager: "pnpm", command, binDirectory: pnpmHome };
|
|
30158
|
+
}
|
|
30159
|
+
const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;
|
|
30160
|
+
if (lowerRoot.endsWith(bunMarker)) {
|
|
30161
|
+
const bunHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.bun/"));
|
|
30162
|
+
const command = firstExisting(executableNames("bun", platform).map((name) => path10.join(bunHome, ".bun", "bin", name)), existsSync5) ?? fallbackCommand("bun", platform);
|
|
30163
|
+
return {
|
|
30164
|
+
manager: "bun",
|
|
30165
|
+
command,
|
|
30166
|
+
binDirectory: path10.join(bunHome, ".bun", "bin")
|
|
30167
|
+
};
|
|
30168
|
+
}
|
|
30169
|
+
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}`)) {
|
|
30170
|
+
const layout = [
|
|
30171
|
+
{ marker: "/.config/yarn/global/", binSegments: [".yarn", "bin"] },
|
|
30172
|
+
{ marker: "/.yarn/global/", binSegments: [".yarn", "bin"] },
|
|
30173
|
+
{ marker: "/yarn/data/global/", binSegments: ["yarn", "bin"] },
|
|
30174
|
+
{ marker: "/yarn/global/", binSegments: ["yarn", "bin"] }
|
|
30175
|
+
].find((candidate) => lowerRoot.includes(candidate.marker));
|
|
30176
|
+
const prefix = normalizedRoot.slice(0, lowerRoot.indexOf(layout.marker));
|
|
30177
|
+
const binDirectory = path10.join(prefix, ...layout.binSegments);
|
|
30178
|
+
const command = firstExisting([
|
|
30179
|
+
...executableNames("yarn", platform).map((name) => path10.join(binDirectory, name)),
|
|
30180
|
+
...executableNames("yarn", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
|
|
30181
|
+
], existsSync5) ?? findExecutable("yarn", { env, existsSync: existsSync5, platform }) ?? fallbackCommand("yarn", platform);
|
|
30182
|
+
return { manager: "yarn", command, binDirectory };
|
|
30183
|
+
}
|
|
30184
|
+
if (lowerRoot.includes("/.volta/tools/image/packages/") && lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)) {
|
|
30185
|
+
const voltaHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.volta/"));
|
|
30186
|
+
const command = firstExisting(executableNames("volta", platform).map((name) => path10.join(voltaHome, ".volta", "bin", name)), existsSync5) ?? fallbackCommand("volta", platform);
|
|
30187
|
+
return {
|
|
30188
|
+
manager: "volta",
|
|
30189
|
+
command,
|
|
30190
|
+
binDirectory: path10.join(voltaHome, ".volta", "bin")
|
|
30191
|
+
};
|
|
30192
|
+
}
|
|
30193
|
+
const npmMarker = `/lib/node_modules/${lowerPackageName}`;
|
|
30194
|
+
if (lowerRoot.endsWith(npmMarker)) {
|
|
30195
|
+
const prefix = normalizedRoot.slice(0, lowerRoot.length - npmMarker.length);
|
|
30196
|
+
const command = firstExisting([
|
|
30197
|
+
...executableNames("npm", platform).map((name) => path10.join(prefix, "bin", name)),
|
|
30198
|
+
...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
|
|
30199
|
+
], existsSync5) ?? fallbackCommand("npm", platform);
|
|
30200
|
+
return { manager: "npm", command, binDirectory: path10.join(prefix, "bin") };
|
|
30201
|
+
}
|
|
30202
|
+
if (platform === "win32" && env.APPDATA) {
|
|
30203
|
+
const npmRoot = normalizePath(path10.join(env.APPDATA, "npm", "node_modules", packageName));
|
|
30204
|
+
if (lowerRoot === npmRoot.toLowerCase()) {
|
|
30205
|
+
const binDirectory = path10.join(env.APPDATA, "npm");
|
|
30206
|
+
const command = firstExisting([
|
|
30207
|
+
...executableNames("npm", platform).map((name) => path10.join(binDirectory, name)),
|
|
30208
|
+
...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
|
|
30209
|
+
], existsSync5) ?? fallbackCommand("npm", platform);
|
|
30210
|
+
return { manager: "npm", command, binDirectory };
|
|
30211
|
+
}
|
|
30212
|
+
}
|
|
30213
|
+
return null;
|
|
30214
|
+
}
|
|
30215
|
+
|
|
30216
|
+
// ../setup/dist/version.js
|
|
30217
|
+
var MEARL_RELEASE_VERSION_MAX_LENGTH = 64;
|
|
30218
|
+
var RELEASE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/;
|
|
30219
|
+
var RELEASE_LINE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.x$/;
|
|
30220
|
+
var RELEASE_COMPONENT_MAXIMUM = 65535;
|
|
30221
|
+
function parseComponent(value) {
|
|
30222
|
+
const component = Number(value);
|
|
30223
|
+
return Number.isSafeInteger(component) && component <= RELEASE_COMPONENT_MAXIMUM ? component : null;
|
|
30224
|
+
}
|
|
30225
|
+
function parseMearlReleaseVersion(value) {
|
|
30226
|
+
if (typeof value !== "string" || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
|
|
30227
|
+
return null;
|
|
30228
|
+
const match = RELEASE_VERSION_PATTERN.exec(value);
|
|
30229
|
+
if (!match)
|
|
30230
|
+
return null;
|
|
30231
|
+
const major = parseComponent(match[1]);
|
|
30232
|
+
const minor = parseComponent(match[2]);
|
|
30233
|
+
const patch = parseComponent(match[3]);
|
|
30234
|
+
const beta = match[4] === void 0 ? void 0 : parseComponent(match[4]);
|
|
30235
|
+
if (major === null || minor === null || patch === null || beta === null)
|
|
30236
|
+
return null;
|
|
30237
|
+
return {
|
|
30238
|
+
raw: value,
|
|
30239
|
+
major,
|
|
30240
|
+
minor,
|
|
30241
|
+
patch,
|
|
30242
|
+
channel: beta === void 0 ? "stable" : "beta",
|
|
30243
|
+
...beta === void 0 ? {} : { beta }
|
|
30244
|
+
};
|
|
30245
|
+
}
|
|
30246
|
+
function isMearlReleaseVersion(value) {
|
|
30247
|
+
return parseMearlReleaseVersion(value) !== null;
|
|
30248
|
+
}
|
|
30249
|
+
function parseMearlReleaseLine(value) {
|
|
30250
|
+
if (typeof value !== "string" || value.length > MEARL_RELEASE_VERSION_MAX_LENGTH)
|
|
30251
|
+
return null;
|
|
30252
|
+
const match = RELEASE_LINE_PATTERN.exec(value);
|
|
30253
|
+
if (!match)
|
|
30254
|
+
return null;
|
|
30255
|
+
const major = parseComponent(match[1]);
|
|
30256
|
+
const minor = parseComponent(match[2]);
|
|
30257
|
+
if (major === null || minor === null)
|
|
30258
|
+
return null;
|
|
30259
|
+
return { raw: value, major, minor };
|
|
30260
|
+
}
|
|
30261
|
+
function mearlReleaseLineForVersion(version2) {
|
|
30262
|
+
const parsed = parseMearlReleaseVersion(version2);
|
|
30263
|
+
if (!parsed)
|
|
30264
|
+
throw new Error("A valid Mearl release version is required");
|
|
30265
|
+
return `${parsed.major}.${parsed.minor}.x`;
|
|
30266
|
+
}
|
|
30267
|
+
function isMearlReleaseVersionInLine(version2, releaseLine) {
|
|
30268
|
+
const parsedVersion = parseMearlReleaseVersion(version2);
|
|
30269
|
+
const parsedLine = parseMearlReleaseLine(releaseLine);
|
|
30270
|
+
return parsedVersion !== null && parsedLine !== null && parsedVersion.major === parsedLine.major && parsedVersion.minor === parsedLine.minor;
|
|
30271
|
+
}
|
|
30272
|
+
function compareMearlReleaseLines(leftVersion, rightVersion) {
|
|
30273
|
+
const left = parseMearlReleaseVersion(leftVersion);
|
|
30274
|
+
const right = parseMearlReleaseVersion(rightVersion);
|
|
30275
|
+
if (!left || !right) {
|
|
30276
|
+
throw new Error("Mearl release-line comparison requires valid release versions");
|
|
30277
|
+
}
|
|
30278
|
+
for (const key of ["major", "minor"]) {
|
|
30279
|
+
if (left[key] < right[key])
|
|
30280
|
+
return -1;
|
|
30281
|
+
if (left[key] > right[key])
|
|
30282
|
+
return 1;
|
|
30283
|
+
}
|
|
30284
|
+
return 0;
|
|
30285
|
+
}
|
|
30286
|
+
|
|
30287
|
+
// src/host-updater.ts
|
|
30288
|
+
var NATIVE_HOST_UPDATE_TIMEOUT_MS = 5 * 6e4;
|
|
30289
|
+
var UPDATE_LOCK_PATH = path11.join(DAEMON_HOME, "native-host-update.lock");
|
|
30290
|
+
var UPDATE_LOCK_POLL_MS = 500;
|
|
30291
|
+
var INVALID_UPDATE_LOCK_GRACE_MS = 1e4;
|
|
30292
|
+
var NativeHostUpdateFailure = class extends Error {
|
|
30293
|
+
constructor(updateError, message) {
|
|
30294
|
+
super(message);
|
|
30295
|
+
this.updateError = updateError;
|
|
30296
|
+
this.name = "NativeHostUpdateFailure";
|
|
30297
|
+
}
|
|
30298
|
+
};
|
|
30299
|
+
function wait(delayMs) {
|
|
30300
|
+
return new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
30301
|
+
}
|
|
30302
|
+
function readLockState(lockPath) {
|
|
30303
|
+
try {
|
|
30304
|
+
const stat2 = fs10.lstatSync(lockPath);
|
|
30305
|
+
if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.size > 4096) {
|
|
30306
|
+
return { modifiedAt: stat2.mtimeMs, owner: null };
|
|
30307
|
+
}
|
|
30308
|
+
try {
|
|
30309
|
+
const value = JSON.parse(fs10.readFileSync(lockPath, "utf8"));
|
|
30310
|
+
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;
|
|
30311
|
+
return { modifiedAt: stat2.mtimeMs, owner };
|
|
30312
|
+
} catch {
|
|
30313
|
+
return { modifiedAt: stat2.mtimeMs, owner: null };
|
|
30314
|
+
}
|
|
30315
|
+
} catch {
|
|
30316
|
+
return null;
|
|
30317
|
+
}
|
|
30318
|
+
}
|
|
30319
|
+
async function withNativeHostUpdateLock(lockPath, targetVersion, operation) {
|
|
30320
|
+
fs10.mkdirSync(path11.dirname(lockPath), { recursive: true, mode: 448 });
|
|
30321
|
+
const startedAt = Date.now();
|
|
30322
|
+
const owner = `${JSON.stringify({ pid: process.pid, startedAt, targetVersion })}
|
|
30323
|
+
`;
|
|
30324
|
+
while (true) {
|
|
30325
|
+
try {
|
|
30326
|
+
const handle = fs10.openSync(lockPath, "wx", 384);
|
|
30327
|
+
try {
|
|
30328
|
+
fs10.writeFileSync(handle, owner, "utf8");
|
|
30329
|
+
} finally {
|
|
30330
|
+
fs10.closeSync(handle);
|
|
30331
|
+
}
|
|
30332
|
+
break;
|
|
30333
|
+
} catch (error48) {
|
|
30334
|
+
if (!(error48 instanceof Error) || !("code" in error48) || error48.code !== "EEXIST") throw error48;
|
|
30335
|
+
const existing = readLockState(lockPath);
|
|
30336
|
+
const staleAfter = existing?.owner ? NATIVE_HOST_UPDATE_TIMEOUT_MS : INVALID_UPDATE_LOCK_GRACE_MS;
|
|
30337
|
+
const referenceTime = existing?.owner?.startedAt ?? existing?.modifiedAt;
|
|
30338
|
+
const expired = referenceTime !== void 0 && Date.now() - referenceTime > staleAfter;
|
|
30339
|
+
const ownerAlive = existing?.owner ? isAlive(existing.owner.pid) : false;
|
|
30340
|
+
if (expired && !ownerAlive) {
|
|
30341
|
+
try {
|
|
30342
|
+
fs10.unlinkSync(lockPath);
|
|
30343
|
+
continue;
|
|
30344
|
+
} catch {
|
|
30345
|
+
}
|
|
30346
|
+
}
|
|
30347
|
+
if (Date.now() - startedAt > NATIVE_HOST_UPDATE_TIMEOUT_MS + 1e4) {
|
|
30348
|
+
throw new NativeHostUpdateFailure(
|
|
30349
|
+
"lock-timeout",
|
|
30350
|
+
"Another Mearl Native Host update did not finish in time"
|
|
30351
|
+
);
|
|
30352
|
+
}
|
|
30353
|
+
await wait(UPDATE_LOCK_POLL_MS);
|
|
30354
|
+
}
|
|
30355
|
+
}
|
|
30356
|
+
try {
|
|
30357
|
+
return await operation();
|
|
30358
|
+
} finally {
|
|
30359
|
+
try {
|
|
30360
|
+
if (fs10.readFileSync(lockPath, "utf8") === owner) fs10.unlinkSync(lockPath);
|
|
30361
|
+
} catch {
|
|
30362
|
+
}
|
|
30363
|
+
}
|
|
30364
|
+
}
|
|
30365
|
+
function classifyUpdateFailure(output) {
|
|
30366
|
+
if (/\b(?:E404|ETARGET)\b|no matching version found|404\s+not found|package.+not found/i.test(
|
|
30367
|
+
output
|
|
30368
|
+
)) {
|
|
30369
|
+
return "package-unavailable";
|
|
30370
|
+
}
|
|
30371
|
+
if (/\b(?:EAI_AGAIN|ECONNREFUSED|ECONNRESET|ENETUNREACH|ENOTFOUND|ERR_SOCKET_TIMEOUT)\b|fetch failed|network request/i.test(
|
|
30372
|
+
output
|
|
30373
|
+
)) {
|
|
30374
|
+
return "network";
|
|
30375
|
+
}
|
|
30376
|
+
return "setup-failed";
|
|
30377
|
+
}
|
|
30378
|
+
function nativeHostUpdateCommand(targetVersion) {
|
|
30379
|
+
if (!isMearlReleaseVersion(targetVersion)) {
|
|
30380
|
+
throw new Error("The Native Host update target must be a valid Mearl release");
|
|
30381
|
+
}
|
|
30382
|
+
const releaseLine = mearlReleaseLineForVersion(targetVersion);
|
|
30383
|
+
const packageSpec = `@mearl/setup@${releaseLine}`;
|
|
30384
|
+
const args = [
|
|
30385
|
+
"--yes",
|
|
30386
|
+
packageSpec,
|
|
30387
|
+
"update",
|
|
30388
|
+
"--local",
|
|
30389
|
+
"--yes",
|
|
30390
|
+
"--target-line",
|
|
30391
|
+
releaseLine,
|
|
30392
|
+
"--skip-skill"
|
|
30393
|
+
];
|
|
30394
|
+
return {
|
|
30395
|
+
args
|
|
30396
|
+
};
|
|
30397
|
+
}
|
|
30398
|
+
function currentNativeHostPackageRoot(entryPath) {
|
|
30399
|
+
let current = path11.dirname(entryPath);
|
|
30400
|
+
for (let depth = 0; depth < 12; depth += 1) {
|
|
30401
|
+
try {
|
|
30402
|
+
const manifest = JSON.parse(fs10.readFileSync(path11.join(current, "package.json"), "utf8"));
|
|
30403
|
+
if (manifest.name === "@mearl/native-host") return current;
|
|
30404
|
+
} catch {
|
|
30405
|
+
}
|
|
30406
|
+
const parent = path11.dirname(current);
|
|
30407
|
+
if (parent === current) break;
|
|
30408
|
+
current = parent;
|
|
30409
|
+
}
|
|
30410
|
+
return null;
|
|
30411
|
+
}
|
|
30412
|
+
function currentPackageManagerInstallation(environment, nodePath, platform) {
|
|
30413
|
+
const packageRoot = currentNativeHostPackageRoot(fileURLToPath2(import.meta.url));
|
|
30414
|
+
if (!packageRoot) return null;
|
|
30415
|
+
return resolvePackageManagerInstallation(packageRoot, "@mearl/native-host", {
|
|
30416
|
+
env: environment,
|
|
30417
|
+
platform,
|
|
30418
|
+
processExecPath: nodePath
|
|
30419
|
+
});
|
|
30420
|
+
}
|
|
30421
|
+
async function installedLocalPackagesMatch(targetReleaseLine, environment, runner) {
|
|
30422
|
+
const installedVersions = [];
|
|
30423
|
+
for (const executableName of ["mearl-native-host", "mearl"]) {
|
|
30424
|
+
const executable = findExecutable(executableName, { env: environment });
|
|
30425
|
+
if (!executable) return false;
|
|
30426
|
+
try {
|
|
30427
|
+
const result = await runner(executable, ["--version"], {
|
|
30428
|
+
capture: true,
|
|
30429
|
+
env: environment,
|
|
30430
|
+
timeoutMs: 5e3
|
|
30431
|
+
});
|
|
30432
|
+
const installedVersion = result.stdout.trim();
|
|
30433
|
+
if (result.code !== 0 || !isMearlReleaseVersionInLine(installedVersion, targetReleaseLine)) {
|
|
30434
|
+
return false;
|
|
30435
|
+
}
|
|
30436
|
+
installedVersions.push(installedVersion);
|
|
30437
|
+
} catch {
|
|
30438
|
+
return false;
|
|
30439
|
+
}
|
|
30440
|
+
}
|
|
30441
|
+
return new Set(installedVersions).size === 1;
|
|
30442
|
+
}
|
|
30443
|
+
async function runNativeHostUpdate(targetVersion, options = {}) {
|
|
30444
|
+
const command = nativeHostUpdateCommand(targetVersion);
|
|
30445
|
+
const targetReleaseLine = mearlReleaseLineForVersion(targetVersion);
|
|
30446
|
+
const environment = options.environment ?? process.env;
|
|
30447
|
+
const platform = options.platform ?? process.platform;
|
|
30448
|
+
const nodePath = options.nodePath ?? process.execPath;
|
|
30449
|
+
const currentInstallation = currentPackageManagerInstallation(environment, nodePath, platform);
|
|
30450
|
+
const managerCommand = options.packageManagerCommand === void 0 ? currentInstallation?.command ?? null : options.packageManagerCommand;
|
|
30451
|
+
const packageBinDirectory = options.packageBinDirectory === void 0 ? currentInstallation?.binDirectory ?? null : options.packageBinDirectory;
|
|
30452
|
+
let childEnvironment = prependNodePath(environment, nodePath);
|
|
30453
|
+
if (managerCommand) childEnvironment = prependCommandPath(managerCommand, childEnvironment);
|
|
30454
|
+
if (packageBinDirectory) {
|
|
30455
|
+
childEnvironment = prependPathDirectory(packageBinDirectory, childEnvironment);
|
|
30456
|
+
}
|
|
30457
|
+
const packageRunner = options.packageRunner ?? findExecutable("npx", {
|
|
30458
|
+
env: childEnvironment,
|
|
30459
|
+
extraDirectories: [path11.dirname(nodePath)],
|
|
30460
|
+
platform
|
|
30461
|
+
});
|
|
30462
|
+
if (!packageRunner) {
|
|
30463
|
+
throw new NativeHostUpdateFailure("package-unavailable", "The package runner is unavailable");
|
|
30464
|
+
}
|
|
30465
|
+
const runner = options.runner ?? runCommand;
|
|
30466
|
+
const areTargetsInstalled = options.areTargetsInstalled ?? ((version2, env) => installedLocalPackagesMatch(version2, env, runner));
|
|
30467
|
+
const withUpdateLock = options.withUpdateLock ?? ((version2, operation) => withNativeHostUpdateLock(options.updateLockPath ?? UPDATE_LOCK_PATH, version2, operation));
|
|
30468
|
+
await withUpdateLock(targetVersion, async () => {
|
|
30469
|
+
let result;
|
|
30470
|
+
try {
|
|
30471
|
+
result = await runner(packageRunner, command.args, {
|
|
30472
|
+
capture: true,
|
|
30473
|
+
env: childEnvironment,
|
|
30474
|
+
timeoutMs: options.timeoutMs ?? NATIVE_HOST_UPDATE_TIMEOUT_MS
|
|
30475
|
+
});
|
|
30476
|
+
} catch (error48) {
|
|
30477
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
30478
|
+
const classified = classifyUpdateFailure(message);
|
|
30479
|
+
throw new NativeHostUpdateFailure(
|
|
30480
|
+
/timed out/i.test(message) ? "timeout" : classified === "setup-failed" ? "unknown" : classified,
|
|
30481
|
+
"The Native Host update process did not complete"
|
|
30482
|
+
);
|
|
30483
|
+
}
|
|
30484
|
+
if (result.code !== 0) {
|
|
30485
|
+
throw new NativeHostUpdateFailure(
|
|
30486
|
+
classifyUpdateFailure(`${result.stderr.slice(0, 8192)}
|
|
30487
|
+
${result.stdout.slice(0, 8192)}`),
|
|
30488
|
+
"The Mearl setup release line did not complete successfully"
|
|
30489
|
+
);
|
|
30490
|
+
}
|
|
30491
|
+
if (!await areTargetsInstalled(targetReleaseLine, childEnvironment)) {
|
|
30492
|
+
throw new NativeHostUpdateFailure(
|
|
30493
|
+
"verification-failed",
|
|
30494
|
+
"The installed Native Host and client do not match the Extension release line"
|
|
30495
|
+
);
|
|
30496
|
+
}
|
|
30497
|
+
});
|
|
30498
|
+
}
|
|
30499
|
+
var NativeHostUpdateCoordinator = class {
|
|
30500
|
+
constructor(options) {
|
|
30501
|
+
this.options = options;
|
|
30502
|
+
}
|
|
30503
|
+
#automaticAttempted = false;
|
|
30504
|
+
#operation = null;
|
|
30505
|
+
evaluate(extensionVersion2, checkForUpdates) {
|
|
30506
|
+
if (checkForUpdates !== true || this.#automaticAttempted || this.#operation) return;
|
|
30507
|
+
if (!isMearlReleaseVersion(extensionVersion2) || !isMearlReleaseVersion(this.options.hostVersion)) {
|
|
30508
|
+
this.options.log("Skipped Native Host update check because a release version was invalid");
|
|
30509
|
+
return;
|
|
30510
|
+
}
|
|
30511
|
+
const targetVersion = extensionVersion2;
|
|
30512
|
+
if (compareMearlReleaseLines(this.options.hostVersion, targetVersion) >= 0) return;
|
|
30513
|
+
this.#automaticAttempted = true;
|
|
30514
|
+
const operation = this.options.runUpdate(targetVersion).then(() => this.options.requestRestart()).catch((error48) => {
|
|
30515
|
+
const category = error48 instanceof NativeHostUpdateFailure ? error48.updateError : "unknown";
|
|
30516
|
+
this.options.log(`Native Host automatic update failed (${category})`);
|
|
30517
|
+
}).finally(() => {
|
|
30518
|
+
if (this.#operation === operation) this.#operation = null;
|
|
30519
|
+
});
|
|
30520
|
+
this.#operation = operation;
|
|
30521
|
+
}
|
|
30522
|
+
};
|
|
30523
|
+
|
|
29772
30524
|
// src/index.ts
|
|
29773
30525
|
var cliArgs = process.argv.slice(2);
|
|
29774
30526
|
var cliArg = cliArgs[0];
|
|
@@ -29788,6 +30540,7 @@ function positivePort(value, fallback) {
|
|
|
29788
30540
|
}
|
|
29789
30541
|
var CDP_PORT = positivePort(cliOption("--cdp-port"), 9222);
|
|
29790
30542
|
var CDP_USER_DATA_DIR = cliOption("--cdp-user-data-dir");
|
|
30543
|
+
var AGENTBAY_SESSION_ID = cliOption("--agentbay-session-id");
|
|
29791
30544
|
var CDP_BROWSER_ID = cliOption("--browser-id") ?? "cdp";
|
|
29792
30545
|
var CDP_BROWSER_NAME = cliOption("--browser-name") ?? "CDP";
|
|
29793
30546
|
if (cliArg === "--init" || cliArg === "--uninstall") {
|
|
@@ -29814,6 +30567,7 @@ Options:
|
|
|
29814
30567
|
--daemon Start as standalone CDP daemon (no extension needed)
|
|
29815
30568
|
--cdp-port <port> CDP port for daemon mode (default 9222)
|
|
29816
30569
|
--cdp-user-data-dir <dir> Read DevToolsActivePort from this Chrome profile
|
|
30570
|
+
--agentbay-session-id <id> Connect to an AgentBay browser session over CDP
|
|
29817
30571
|
--browser-id <id> Registry id for this CDP browser
|
|
29818
30572
|
--browser-name <name> Human-readable registry name
|
|
29819
30573
|
--version, -v Show version
|
|
@@ -29835,11 +30589,11 @@ Examples:
|
|
|
29835
30589
|
process.exit(0);
|
|
29836
30590
|
}
|
|
29837
30591
|
function socketPathFor(suffix) {
|
|
29838
|
-
return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` :
|
|
30592
|
+
return process.platform === "win32" ? `\\\\.\\pipe\\mearl-agent${suffix}` : path14.join(os7.tmpdir(), `mearl-agent${suffix}.sock`);
|
|
29839
30593
|
}
|
|
29840
30594
|
var LEGACY_SOCKET_PATH = socketPathFor("");
|
|
29841
30595
|
var INSTANCE_SOCKET_PATH = socketPathFor(`-${process.pid}`);
|
|
29842
|
-
var LOG_FILE =
|
|
30596
|
+
var LOG_FILE = path14.join(os7.tmpdir(), "mearl-native-host.log");
|
|
29843
30597
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
29844
30598
|
var INVENTORY_IDLE_EXIT_MS = 1e3;
|
|
29845
30599
|
var MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
|
|
@@ -29857,28 +30611,53 @@ function extractChromeVersion(input) {
|
|
|
29857
30611
|
const parts = match[1].split(".");
|
|
29858
30612
|
return parts.slice(0, 3).join(".");
|
|
29859
30613
|
}
|
|
29860
|
-
function fetchCdpBrowserVersion() {
|
|
30614
|
+
async function fetchCdpBrowserVersion() {
|
|
30615
|
+
if (AGENTBAY_SESSION_ID) {
|
|
30616
|
+
try {
|
|
30617
|
+
const browser = await getCdpSession().getBrowserClient();
|
|
30618
|
+
const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
|
|
30619
|
+
return result.product ?? null;
|
|
30620
|
+
} catch {
|
|
30621
|
+
return null;
|
|
30622
|
+
}
|
|
30623
|
+
}
|
|
29861
30624
|
const port = readDevToolsActivePort(CDP_USER_DATA_DIR)?.port ?? CDP_PORT;
|
|
29862
|
-
return new Promise((
|
|
29863
|
-
const req =
|
|
30625
|
+
return new Promise((resolve2) => {
|
|
30626
|
+
const req = http4.get(`http://localhost:${port}/json/version`, (res) => {
|
|
29864
30627
|
let data = "";
|
|
29865
30628
|
res.on("data", (chunk) => data += chunk);
|
|
29866
30629
|
res.on("end", () => {
|
|
29867
30630
|
try {
|
|
29868
30631
|
const info = JSON.parse(data);
|
|
29869
|
-
|
|
30632
|
+
resolve2(info.Browser ?? null);
|
|
29870
30633
|
} catch {
|
|
29871
|
-
|
|
30634
|
+
resolve2(null);
|
|
29872
30635
|
}
|
|
29873
30636
|
});
|
|
29874
30637
|
});
|
|
29875
|
-
req.on("error", () =>
|
|
30638
|
+
req.on("error", () => resolve2(null));
|
|
29876
30639
|
req.setTimeout(2e3, () => {
|
|
29877
30640
|
req.destroy();
|
|
29878
|
-
|
|
30641
|
+
resolve2(null);
|
|
29879
30642
|
});
|
|
29880
30643
|
});
|
|
29881
30644
|
}
|
|
30645
|
+
async function getCurrentBrowserUserAgent() {
|
|
30646
|
+
if (extensionVersion !== null) {
|
|
30647
|
+
try {
|
|
30648
|
+
const result = await sendRequestToExtension("get_browser_info", {}, 3e3);
|
|
30649
|
+
if (result.userAgent?.trim()) return result.userAgent;
|
|
30650
|
+
} catch {
|
|
30651
|
+
}
|
|
30652
|
+
}
|
|
30653
|
+
try {
|
|
30654
|
+
const browser = await getCdpSession().getBrowserClient();
|
|
30655
|
+
const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
|
|
30656
|
+
if (result.userAgent?.trim()) return result.userAgent;
|
|
30657
|
+
} catch {
|
|
30658
|
+
}
|
|
30659
|
+
throw new Error("Could not determine the control browser user agent for userAgentMode=desktop");
|
|
30660
|
+
}
|
|
29882
30661
|
async function collectVersions() {
|
|
29883
30662
|
const info = {
|
|
29884
30663
|
nativeHost: NATIVE_HOST_VERSION,
|
|
@@ -29930,7 +30709,9 @@ function scheduleInventoryExit() {
|
|
|
29930
30709
|
var cdpSession = null;
|
|
29931
30710
|
function getCdpSession() {
|
|
29932
30711
|
if (!cdpSession) {
|
|
29933
|
-
cdpSession = new CdpSession({
|
|
30712
|
+
cdpSession = AGENTBAY_SESSION_ID ? new CdpSession({
|
|
30713
|
+
endpointResolver: () => resolveAgentBayEndpoint(AGENTBAY_SESSION_ID)
|
|
30714
|
+
}) : new CdpSession({ port: CDP_PORT, userDataDir: CDP_USER_DATA_DIR });
|
|
29934
30715
|
}
|
|
29935
30716
|
return cdpSession;
|
|
29936
30717
|
}
|
|
@@ -29938,7 +30719,7 @@ function log(...args) {
|
|
|
29938
30719
|
const msg = `[${(/* @__PURE__ */ new Date()).toISOString()}] [NativeHost] ${args.join(" ")}
|
|
29939
30720
|
`;
|
|
29940
30721
|
try {
|
|
29941
|
-
|
|
30722
|
+
fs13.appendFileSync(LOG_FILE, msg);
|
|
29942
30723
|
} catch {
|
|
29943
30724
|
}
|
|
29944
30725
|
}
|
|
@@ -29950,6 +30731,14 @@ function sendToExtension(message) {
|
|
|
29950
30731
|
process.stdout.write(header);
|
|
29951
30732
|
process.stdout.write(json2);
|
|
29952
30733
|
}
|
|
30734
|
+
var nativeHostUpdateCoordinator = new NativeHostUpdateCoordinator({
|
|
30735
|
+
hostVersion: NATIVE_HOST_VERSION,
|
|
30736
|
+
log,
|
|
30737
|
+
runUpdate: (targetVersion) => runNativeHostUpdate(targetVersion),
|
|
30738
|
+
requestRestart: () => {
|
|
30739
|
+
restartAfterUpdate();
|
|
30740
|
+
}
|
|
30741
|
+
});
|
|
29953
30742
|
var agentChatManager = null;
|
|
29954
30743
|
var agentSkillManager = new AgentSkillManager();
|
|
29955
30744
|
function getAgentChatManager() {
|
|
@@ -30060,6 +30849,7 @@ function handleExtensionMessage(message) {
|
|
|
30060
30849
|
extensionVersion: extensionVersion ?? void 0
|
|
30061
30850
|
});
|
|
30062
30851
|
sendToExtension({ type: "pong", timestamp: Date.now(), version: NATIVE_HOST_VERSION });
|
|
30852
|
+
nativeHostUpdateCoordinator.evaluate(message.version, message.checkForUpdates);
|
|
30063
30853
|
return;
|
|
30064
30854
|
}
|
|
30065
30855
|
if (message && message.type === "browser_focus") {
|
|
@@ -30125,12 +30915,12 @@ function setupNativeMessageInput() {
|
|
|
30125
30915
|
}
|
|
30126
30916
|
function sendRequestToExtension(action, data, timeoutMs, trustedInternal = false, controlSource = "local") {
|
|
30127
30917
|
const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
30128
|
-
return new Promise((
|
|
30918
|
+
return new Promise((resolve2, reject) => {
|
|
30129
30919
|
const timer = setTimeout(() => {
|
|
30130
30920
|
pendingRequests.delete(id);
|
|
30131
30921
|
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
30132
30922
|
}, timeoutMs);
|
|
30133
|
-
pendingRequests.set(id, { resolve, reject, timer });
|
|
30923
|
+
pendingRequests.set(id, { resolve: resolve2, reject, timer });
|
|
30134
30924
|
sendToExtension(
|
|
30135
30925
|
createExtensionRequestMessage(id, action, data, trustedInternal, controlSource)
|
|
30136
30926
|
);
|
|
@@ -30240,7 +31030,14 @@ async function handleSocketLine(socket, line) {
|
|
|
30240
31030
|
try {
|
|
30241
31031
|
const selector = String(data?.browser ?? data?.browserId ?? data?.name ?? "").trim();
|
|
30242
31032
|
if (!selector) throw new Error("browser is required");
|
|
30243
|
-
const
|
|
31033
|
+
const localExists = hasManagedBrowser(selector);
|
|
31034
|
+
const agentBayExists = hasAgentBayBrowser(selector);
|
|
31035
|
+
if (localExists && agentBayExists) {
|
|
31036
|
+
throw new Error(
|
|
31037
|
+
`Managed browser name is ambiguous: ${selector}. Use the full browserId from browser_list.`
|
|
31038
|
+
);
|
|
31039
|
+
}
|
|
31040
|
+
const result = agentBayExists ? await closeAgentBayBrowser(selector) : await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
|
|
30244
31041
|
socket.write(JSON.stringify({ id, success: true, data: result }) + "\n");
|
|
30245
31042
|
} catch (error48) {
|
|
30246
31043
|
socket.write(
|
|
@@ -30332,7 +31129,6 @@ async function handleSocketLine(socket, line) {
|
|
|
30332
31129
|
}
|
|
30333
31130
|
if (action === "browser_launch") {
|
|
30334
31131
|
try {
|
|
30335
|
-
const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
|
|
30336
31132
|
const exportCookies = extensionVersion !== null ? async (domains) => {
|
|
30337
31133
|
const result2 = await sendRequestToExtension(
|
|
30338
31134
|
"__export_cookies",
|
|
@@ -30342,8 +31138,34 @@ async function handleSocketLine(socket, line) {
|
|
|
30342
31138
|
);
|
|
30343
31139
|
return result2.cookies ?? [];
|
|
30344
31140
|
} : (domains) => cdpExportCookies(getCdpSession(), domains);
|
|
31141
|
+
if (data?.provider === "agentbay") {
|
|
31142
|
+
const name2 = typeof data?.name === "string" ? data.name.trim() : "";
|
|
31143
|
+
if (name2 && hasManagedBrowser(name2)) {
|
|
31144
|
+
throw new Error(
|
|
31145
|
+
`Managed browser name is already used by the local provider: ${name2}. Choose a unique name.`
|
|
31146
|
+
);
|
|
31147
|
+
}
|
|
31148
|
+
const result2 = await launchAgentBayBrowser(data, {
|
|
31149
|
+
nativeHostScript: fileURLToPath4(import.meta.url),
|
|
31150
|
+
exportCookies,
|
|
31151
|
+
getBrowserUserAgent: getCurrentBrowserUserAgent,
|
|
31152
|
+
log
|
|
31153
|
+
});
|
|
31154
|
+
socket.write(JSON.stringify({ id, success: true, data: result2 }) + "\n");
|
|
31155
|
+
return;
|
|
31156
|
+
}
|
|
31157
|
+
if (data?.provider !== void 0 && data.provider !== "local") {
|
|
31158
|
+
throw new Error('provider must be "local" or "agentbay"');
|
|
31159
|
+
}
|
|
31160
|
+
const name = typeof data?.name === "string" ? data.name.trim() : "";
|
|
31161
|
+
if (name && hasAgentBayBrowser(name)) {
|
|
31162
|
+
throw new Error(
|
|
31163
|
+
`Managed browser name is already used by the AgentBay provider: ${name}. Choose a unique name.`
|
|
31164
|
+
);
|
|
31165
|
+
}
|
|
31166
|
+
const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
|
|
30345
31167
|
const result = await launchManagedBrowser(data, {
|
|
30346
|
-
nativeHostScript:
|
|
31168
|
+
nativeHostScript: fileURLToPath4(import.meta.url),
|
|
30347
31169
|
prepareTdbankSso,
|
|
30348
31170
|
exportCookies,
|
|
30349
31171
|
log
|
|
@@ -30435,8 +31257,21 @@ async function handleViaCdp(action, data, _timeoutMs, controlSource) {
|
|
|
30435
31257
|
`${action} requires the Mearl browser extension. Install it from https://chromewebstore.google.com/detail/mearl/aoehhjnofngknnjefamjbplchbolghkm`
|
|
30436
31258
|
);
|
|
30437
31259
|
}
|
|
30438
|
-
|
|
31260
|
+
if (AGENTBAY_SESSION_ID && action === "page_upload") {
|
|
31261
|
+
throw new Error(
|
|
31262
|
+
"page_upload is not available for AgentBay CDP sessions because file paths resolve on the remote browser host."
|
|
31263
|
+
);
|
|
31264
|
+
}
|
|
31265
|
+
const available = AGENTBAY_SESSION_ID ? await getCdpSession().getBrowserClient().then(
|
|
31266
|
+
() => true,
|
|
31267
|
+
() => false
|
|
31268
|
+
) : await isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
|
|
30439
31269
|
if (!available) {
|
|
31270
|
+
if (AGENTBAY_SESSION_ID) {
|
|
31271
|
+
throw new Error(
|
|
31272
|
+
`AgentBay CDP endpoint is not available for session ${AGENTBAY_SESSION_ID}. Check the session status and AgentBay plan permissions.`
|
|
31273
|
+
);
|
|
31274
|
+
}
|
|
30440
31275
|
throw new Error(
|
|
30441
31276
|
`Chrome DevTools remote debugging is not available on port ${CDP_PORT}. Enable it via chrome://inspect/#remote-debugging (Chrome 145+) or install the Mearl extension.`
|
|
30442
31277
|
);
|
|
@@ -30471,16 +31306,16 @@ function onSocketConnection(socket) {
|
|
|
30471
31306
|
});
|
|
30472
31307
|
}
|
|
30473
31308
|
function isPathAlive(socketPath) {
|
|
30474
|
-
return new Promise((
|
|
30475
|
-
if (process.platform !== "win32" && !
|
|
30476
|
-
|
|
31309
|
+
return new Promise((resolve2) => {
|
|
31310
|
+
if (process.platform !== "win32" && !fs13.existsSync(socketPath)) {
|
|
31311
|
+
resolve2(false);
|
|
30477
31312
|
return;
|
|
30478
31313
|
}
|
|
30479
31314
|
const client2 = net.createConnection(socketPath);
|
|
30480
31315
|
const done = (alive) => {
|
|
30481
31316
|
client2.removeAllListeners();
|
|
30482
31317
|
client2.destroy();
|
|
30483
|
-
|
|
31318
|
+
resolve2(alive);
|
|
30484
31319
|
};
|
|
30485
31320
|
const timer = setTimeout(() => done(false), 500);
|
|
30486
31321
|
client2.on("connect", () => {
|
|
@@ -30494,10 +31329,10 @@ function isPathAlive(socketPath) {
|
|
|
30494
31329
|
});
|
|
30495
31330
|
}
|
|
30496
31331
|
function listenOn(socketPath) {
|
|
30497
|
-
return new Promise((
|
|
30498
|
-
if (process.platform !== "win32" &&
|
|
31332
|
+
return new Promise((resolve2, reject) => {
|
|
31333
|
+
if (process.platform !== "win32" && fs13.existsSync(socketPath)) {
|
|
30499
31334
|
try {
|
|
30500
|
-
|
|
31335
|
+
fs13.unlinkSync(socketPath);
|
|
30501
31336
|
} catch {
|
|
30502
31337
|
}
|
|
30503
31338
|
}
|
|
@@ -30506,11 +31341,11 @@ function listenOn(socketPath) {
|
|
|
30506
31341
|
server.listen(socketPath, () => {
|
|
30507
31342
|
if (process.platform !== "win32") {
|
|
30508
31343
|
try {
|
|
30509
|
-
|
|
31344
|
+
fs13.chmodSync(socketPath, 384);
|
|
30510
31345
|
} catch (error48) {
|
|
30511
31346
|
server.close();
|
|
30512
31347
|
try {
|
|
30513
|
-
|
|
31348
|
+
fs13.unlinkSync(socketPath);
|
|
30514
31349
|
} catch {
|
|
30515
31350
|
}
|
|
30516
31351
|
reject(error48);
|
|
@@ -30520,7 +31355,7 @@ function listenOn(socketPath) {
|
|
|
30520
31355
|
server.removeListener("error", reject);
|
|
30521
31356
|
server.on("error", (error48) => log("socket server error:", String(error48)));
|
|
30522
31357
|
log(`listening on ${socketPath}`);
|
|
30523
|
-
|
|
31358
|
+
resolve2(server);
|
|
30524
31359
|
});
|
|
30525
31360
|
});
|
|
30526
31361
|
}
|
|
@@ -30546,7 +31381,7 @@ function releaseLegacySocket() {
|
|
|
30546
31381
|
}
|
|
30547
31382
|
if (process.platform !== "win32") {
|
|
30548
31383
|
try {
|
|
30549
|
-
if (
|
|
31384
|
+
if (fs13.existsSync(LEGACY_SOCKET_PATH)) fs13.unlinkSync(LEGACY_SOCKET_PATH);
|
|
30550
31385
|
} catch {
|
|
30551
31386
|
}
|
|
30552
31387
|
}
|
|
@@ -30558,8 +31393,8 @@ async function startSocketServer() {
|
|
|
30558
31393
|
function cleanupInstanceSocketFile() {
|
|
30559
31394
|
if (process.platform === "win32") return;
|
|
30560
31395
|
try {
|
|
30561
|
-
if (
|
|
30562
|
-
|
|
31396
|
+
if (fs13.existsSync(INSTANCE_SOCKET_PATH)) {
|
|
31397
|
+
fs13.unlinkSync(INSTANCE_SOCKET_PATH);
|
|
30563
31398
|
}
|
|
30564
31399
|
} catch (error48) {
|
|
30565
31400
|
log("cleanup socket file failed:", String(error48));
|
|
@@ -30586,6 +31421,11 @@ function cleanupAndExit(code) {
|
|
|
30586
31421
|
}
|
|
30587
31422
|
finish();
|
|
30588
31423
|
}
|
|
31424
|
+
function restartAfterUpdate() {
|
|
31425
|
+
const forcedExit = setTimeout(() => process.exit(0), 2e3);
|
|
31426
|
+
forcedExit.unref();
|
|
31427
|
+
cleanupAndExit(0);
|
|
31428
|
+
}
|
|
30589
31429
|
async function main() {
|
|
30590
31430
|
initBrowserRegistry({ log });
|
|
30591
31431
|
pruneDeadBrowsers();
|
|
@@ -30616,6 +31456,17 @@ async function main() {
|
|
|
30616
31456
|
lastFocusedAt: CDP_BROWSER_ID.startsWith("managed:") ? 0 : void 0
|
|
30617
31457
|
});
|
|
30618
31458
|
}
|
|
31459
|
+
if (AGENTBAY_SESSION_ID) {
|
|
31460
|
+
const keepAliveTimer = setInterval(() => {
|
|
31461
|
+
keepAgentBaySessionAlive(AGENTBAY_SESSION_ID).catch((error48) => {
|
|
31462
|
+
log(
|
|
31463
|
+
`AgentBay keepalive failed for ${AGENTBAY_SESSION_ID}:`,
|
|
31464
|
+
error48 instanceof Error ? error48.message : String(error48)
|
|
31465
|
+
);
|
|
31466
|
+
});
|
|
31467
|
+
}, 6e4);
|
|
31468
|
+
keepAliveTimer.unref();
|
|
31469
|
+
}
|
|
30619
31470
|
process.on("SIGINT", () => cleanupAndExit(0));
|
|
30620
31471
|
process.on("SIGTERM", () => cleanupAndExit(0));
|
|
30621
31472
|
process.on("uncaughtException", (error48) => {
|