@algosuite/vo-mcp 0.2.0-beta.16 → 0.2.0-beta.18
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/cli.js +151 -58
- package/dist/cli.js.map +4 -4
- package/dist/index.js +146 -54
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +758 -318
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +258 -38
- package/dist/runner-supervisor.js.map +4 -4
- package/package.json +1 -1
|
@@ -66,11 +66,11 @@ function createControlPlaneClient({
|
|
|
66
66
|
throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
67
67
|
}
|
|
68
68
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
69
|
-
async function req(method,
|
|
69
|
+
async function req(method, path2, body, { timeoutMs } = {}) {
|
|
70
70
|
const bearer = await resolveBearer(env);
|
|
71
71
|
const controller = timeoutMs ? new AbortController() : null;
|
|
72
72
|
let timeoutId;
|
|
73
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
73
|
+
const request = Promise.resolve(fetchImpl(`${root}${path2}`, {
|
|
74
74
|
method,
|
|
75
75
|
headers: {
|
|
76
76
|
"content-type": "application/json",
|
|
@@ -83,7 +83,7 @@ function createControlPlaneClient({
|
|
|
83
83
|
const timeout = new Promise((_, reject) => {
|
|
84
84
|
timeoutId = setTimeout(() => {
|
|
85
85
|
controller.abort();
|
|
86
|
-
reject(new Error(`control-plane ${
|
|
86
|
+
reject(new Error(`control-plane ${path2} timed out after ${timeoutMs}ms`));
|
|
87
87
|
}, timeoutMs);
|
|
88
88
|
});
|
|
89
89
|
try {
|
|
@@ -202,8 +202,8 @@ function createControlPlaneClient({
|
|
|
202
202
|
return json ? json.task : null;
|
|
203
203
|
},
|
|
204
204
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
205
|
-
const
|
|
206
|
-
const res = await req("GET",
|
|
205
|
+
const path2 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
206
|
+
const res = await req("GET", path2);
|
|
207
207
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
208
208
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
209
209
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -511,7 +511,7 @@ import {
|
|
|
511
511
|
rmSync as rmSync2,
|
|
512
512
|
writeFileSync as writeFileSync2
|
|
513
513
|
} from "node:fs";
|
|
514
|
-
import { basename, isAbsolute as isAbsolute3, join as join3, resolve as resolve4 } from "node:path";
|
|
514
|
+
import { basename, isAbsolute as isAbsolute3, join as join3, relative as relative3, resolve as resolve4, sep as sep2 } from "node:path";
|
|
515
515
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
516
516
|
|
|
517
517
|
// ../../scripts/virtual-office/runner-bootstrap/runtime-authorization.mjs
|
|
@@ -647,10 +647,10 @@ function validateLock(lock, authorization) {
|
|
|
647
647
|
}
|
|
648
648
|
return { actual, expected };
|
|
649
649
|
}
|
|
650
|
-
function checkPathKind(
|
|
651
|
-
if (!fsOps.exists(
|
|
652
|
-
const stats = fsOps.lstat(
|
|
653
|
-
if (isReparseStat(stats) || fsOps.isReparsePoint(
|
|
650
|
+
function checkPathKind(path2, expectedKind, fsOps, label) {
|
|
651
|
+
if (!fsOps.exists(path2)) throw new Error(`staged runtime ${label} missing`);
|
|
652
|
+
const stats = fsOps.lstat(path2);
|
|
653
|
+
if (isReparseStat(stats) || fsOps.isReparsePoint(path2, stats)) {
|
|
654
654
|
throw new Error(`staged runtime ${label} is a reparse point`);
|
|
655
655
|
}
|
|
656
656
|
if (expectedKind === "directory" && !stats.isDirectory()) {
|
|
@@ -668,30 +668,30 @@ function verifyInstalledPackages(payloadRoot, packageRecords, platform, fsOps) {
|
|
|
668
668
|
const omitted = [];
|
|
669
669
|
let installed = 0;
|
|
670
670
|
for (const [key, entry] of Object.entries(packageRecords)) {
|
|
671
|
-
const
|
|
672
|
-
assertContained(payloadRoot,
|
|
671
|
+
const path2 = resolve(payloadRoot, key);
|
|
672
|
+
assertContained(payloadRoot, path2, "package path");
|
|
673
673
|
const shouldOmit = isOmittedOptionalPackage(entry, platform);
|
|
674
674
|
if (shouldOmit) {
|
|
675
675
|
omitted.push(key);
|
|
676
|
-
if (fsOps.exists(
|
|
676
|
+
if (fsOps.exists(path2)) throw new Error(`staged runtime optional package should be omitted: ${key}`);
|
|
677
677
|
continue;
|
|
678
678
|
}
|
|
679
|
-
checkPathKind(
|
|
679
|
+
checkPathKind(path2, "directory", fsOps, `installed package ${key}`);
|
|
680
680
|
installed += 1;
|
|
681
681
|
}
|
|
682
682
|
return { installed, omitted: omitted.sort() };
|
|
683
683
|
}
|
|
684
|
-
function treeRecord(kind,
|
|
685
|
-
if (kind === "d") return `d ${
|
|
684
|
+
function treeRecord(kind, path2, stats, fileHash = "") {
|
|
685
|
+
if (kind === "d") return `d ${path2}\r
|
|
686
686
|
`;
|
|
687
|
-
return `f ${
|
|
687
|
+
return `f ${path2} ${stats.size} ${fileHash}\r
|
|
688
688
|
`;
|
|
689
689
|
}
|
|
690
690
|
function computeInstalledTree(nodeModulesRoot, fsOps = {}) {
|
|
691
691
|
const ops = {
|
|
692
692
|
exists: existsSync2,
|
|
693
693
|
lstat: lstatSync,
|
|
694
|
-
readdir: (
|
|
694
|
+
readdir: (path2) => readdirSync(path2, { withFileTypes: true }),
|
|
695
695
|
readFile: readFileSync,
|
|
696
696
|
isReparsePoint: () => false,
|
|
697
697
|
listReparsePoints: listWindowsReparsePoints,
|
|
@@ -947,6 +947,7 @@ import {
|
|
|
947
947
|
rmSync,
|
|
948
948
|
writeFileSync
|
|
949
949
|
} from "node:fs";
|
|
950
|
+
import { homedir } from "node:os";
|
|
950
951
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve3 } from "node:path";
|
|
951
952
|
var SLOT_ID_RE = /^vo-mcp-[0-9A-Za-z._-]{1,96}$/u;
|
|
952
953
|
var ACTION_ID_RE = /^[0-9A-Za-z._-]{1,128}$/u;
|
|
@@ -962,9 +963,26 @@ function within(parent, candidate) {
|
|
|
962
963
|
const rel = relative2(resolve3(parent), resolve3(candidate));
|
|
963
964
|
return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
|
|
964
965
|
}
|
|
965
|
-
|
|
966
|
+
var APP_IDENTIFIER = "ai.algosuite.vo-runner";
|
|
967
|
+
var RUNNER_RUNTIME_DIR = "runner-runtime";
|
|
968
|
+
function defaultRuntimeRoot({ platform = process.platform, env = process.env, home = homedir() } = {}) {
|
|
969
|
+
if (platform === "win32") {
|
|
970
|
+
const appData = String(env.APPDATA || "").trim();
|
|
971
|
+
return appData && isAbsolute2(appData) ? join2(appData, APP_IDENTIFIER, RUNNER_RUNTIME_DIR) : null;
|
|
972
|
+
}
|
|
973
|
+
if (!home) return null;
|
|
974
|
+
if (platform === "darwin") {
|
|
975
|
+
return join2(home, "Library", "Application Support", APP_IDENTIFIER, RUNNER_RUNTIME_DIR);
|
|
976
|
+
}
|
|
977
|
+
const xdg = String(env.XDG_CONFIG_HOME || "").trim();
|
|
978
|
+
const base = xdg && isAbsolute2(xdg) ? xdg : join2(home, ".config");
|
|
979
|
+
return join2(base, APP_IDENTIFIER, RUNNER_RUNTIME_DIR);
|
|
980
|
+
}
|
|
981
|
+
function runtimeRootFromEnv(env = process.env, { platform, home } = {}) {
|
|
966
982
|
const value = String(env.VO_RUNNER_RUNTIME_ROOT || "").trim();
|
|
967
|
-
|
|
983
|
+
if (value) return isAbsolute2(value) ? resolve3(value) : null;
|
|
984
|
+
const derived = defaultRuntimeRoot({ platform, env, home });
|
|
985
|
+
return derived ? resolve3(derived) : null;
|
|
968
986
|
}
|
|
969
987
|
function hashFileSha512(file) {
|
|
970
988
|
return `sha512-${createHash3("sha512").update(readFileSync3(file)).digest("base64")}`;
|
|
@@ -1328,6 +1346,39 @@ function validateDependencyLock(payloadRoot, expected) {
|
|
|
1328
1346
|
}
|
|
1329
1347
|
if (!foundPackage) throw new Error("installed runner package does not match registry integrity");
|
|
1330
1348
|
}
|
|
1349
|
+
function writeAuthorizedInstallSeed(payloadRoot, tarball, runtimeAuthorization) {
|
|
1350
|
+
const authorization = validateRuntimeAuthorization(runtimeAuthorization);
|
|
1351
|
+
const stagingRoot = resolve4(payloadRoot, "..", "..");
|
|
1352
|
+
const tarballFromStaging = relative3(stagingRoot, resolve4(tarball));
|
|
1353
|
+
if (!tarballFromStaging || tarballFromStaging === ".." || tarballFromStaging.startsWith(`..${sep2}`) || isAbsolute3(tarballFromStaging)) {
|
|
1354
|
+
throw new Error("authorized runtime tarball escaped staging root");
|
|
1355
|
+
}
|
|
1356
|
+
const relativeTarball = relative3(payloadRoot, resolve4(tarball)).replaceAll("\\", "/");
|
|
1357
|
+
if (!relativeTarball || isAbsolute3(relativeTarball) || relativeTarball.includes("\n") || relativeTarball.includes("\r")) {
|
|
1358
|
+
throw new Error("authorized runtime tarball path invalid");
|
|
1359
|
+
}
|
|
1360
|
+
const fileSpec = `file:${relativeTarball}`;
|
|
1361
|
+
const packageRecord = {
|
|
1362
|
+
name: "algohq-runner-runtime",
|
|
1363
|
+
version: "0.0.0",
|
|
1364
|
+
private: true,
|
|
1365
|
+
dependencies: { [PACKAGE_NAME]: fileSpec }
|
|
1366
|
+
};
|
|
1367
|
+
const packages = structuredClone(authorization.dependency_lock.packages);
|
|
1368
|
+
packages[`node_modules/${PACKAGE_NAME}`].resolved = fileSpec;
|
|
1369
|
+
const lock = {
|
|
1370
|
+
name: packageRecord.name,
|
|
1371
|
+
version: packageRecord.version,
|
|
1372
|
+
lockfileVersion: authorization.dependency_lock.source_lockfile_version,
|
|
1373
|
+
requires: true,
|
|
1374
|
+
packages: { "": packageRecord, ...packages }
|
|
1375
|
+
};
|
|
1376
|
+
writeFileSync2(join3(payloadRoot, "package.json"), `${JSON.stringify(packageRecord)}
|
|
1377
|
+
`, { mode: 384 });
|
|
1378
|
+
writeFileSync2(join3(payloadRoot, "package-lock.json"), `${JSON.stringify(lock)}
|
|
1379
|
+
`, { mode: 384 });
|
|
1380
|
+
return { fileSpec, lock };
|
|
1381
|
+
}
|
|
1331
1382
|
function buildActive(slotId, metadata, paths) {
|
|
1332
1383
|
return {
|
|
1333
1384
|
slot_id: slotId,
|
|
@@ -1371,19 +1422,40 @@ function installSlot({
|
|
|
1371
1422
|
let installedSlot = false;
|
|
1372
1423
|
try {
|
|
1373
1424
|
mkdirSync2(payload, { recursive: true });
|
|
1374
|
-
|
|
1425
|
+
let installArgs;
|
|
1426
|
+
if (runtimeAuthorization) {
|
|
1427
|
+
writeAuthorizedInstallSeed(payload, tarball, runtimeAuthorization);
|
|
1428
|
+
installArgs = [
|
|
1429
|
+
"ci",
|
|
1430
|
+
"--ignore-scripts",
|
|
1431
|
+
"--no-bin-links",
|
|
1432
|
+
"--no-audit",
|
|
1433
|
+
"--no-fund",
|
|
1434
|
+
`--registry=${PUBLIC_REGISTRY}`
|
|
1435
|
+
];
|
|
1436
|
+
} else {
|
|
1437
|
+
writeFileSync2(join3(payload, "package.json"), `${JSON.stringify({
|
|
1438
|
+
name: "algohq-runner-runtime",
|
|
1439
|
+
version: "0.0.0",
|
|
1440
|
+
private: true
|
|
1441
|
+
})}
|
|
1375
1442
|
`);
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1443
|
+
installArgs = [
|
|
1444
|
+
"install",
|
|
1445
|
+
"--ignore-scripts",
|
|
1446
|
+
"--no-bin-links",
|
|
1447
|
+
"--no-audit",
|
|
1448
|
+
"--no-fund",
|
|
1449
|
+
"--package-lock=true",
|
|
1450
|
+
"--save-exact",
|
|
1451
|
+
`--registry=${PUBLIC_REGISTRY}`,
|
|
1452
|
+
tarball
|
|
1453
|
+
];
|
|
1454
|
+
}
|
|
1455
|
+
const install = runner.npm(
|
|
1456
|
+
installArgs,
|
|
1457
|
+
{ ...runOptions, cwd: payload, env: npmEnv, timeout: 18e4 }
|
|
1458
|
+
);
|
|
1387
1459
|
if (install.status !== 0) throw new Error(`npm install failed: ${install.stderr || install.error?.message || install.status}`);
|
|
1388
1460
|
assertNoLinks(payload);
|
|
1389
1461
|
validateDependencyLock(payload, metadata);
|
|
@@ -1504,6 +1576,151 @@ function stageAndActivateBundledUpdate(options) {
|
|
|
1504
1576
|
}
|
|
1505
1577
|
}
|
|
1506
1578
|
|
|
1579
|
+
// ../../scripts/virtual-office/code-runner/legacy-orphan-sweep.mjs
|
|
1580
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
1581
|
+
|
|
1582
|
+
// ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
|
|
1583
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1584
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1585
|
+
import os from "node:os";
|
|
1586
|
+
import path from "node:path";
|
|
1587
|
+
function killProcessTree(pid, { platform = process.platform, spawn: spawn2 = spawnSync3 } = {}) {
|
|
1588
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
1589
|
+
if (platform === "win32") {
|
|
1590
|
+
const r = spawn2("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
1591
|
+
return !r.error && r.status === 0;
|
|
1592
|
+
}
|
|
1593
|
+
try {
|
|
1594
|
+
process.kill(-pid, "SIGKILL");
|
|
1595
|
+
return true;
|
|
1596
|
+
} catch {
|
|
1597
|
+
try {
|
|
1598
|
+
process.kill(pid, "SIGKILL");
|
|
1599
|
+
return true;
|
|
1600
|
+
} catch {
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// ../../scripts/virtual-office/code-runner/legacy-orphan-sweep.mjs
|
|
1607
|
+
var MAX_LEGACY_KILLS = 50;
|
|
1608
|
+
var SWEEP_RECENCY_BUFFER_MS = 5e3;
|
|
1609
|
+
var SIGNATURES = [
|
|
1610
|
+
{
|
|
1611
|
+
signature: "claude-headless",
|
|
1612
|
+
// claude-args.mjs always emits `-p --output-format stream-json --verbose`.
|
|
1613
|
+
test: (cl) => /(?:^|[\\/"\s])claude(?:\.exe|\.cmd|\.ps1)?(?:"|\s)/iu.test(cl) && /--output-format[\s"=]+stream-json/iu.test(cl)
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
signature: "codex-headless",
|
|
1617
|
+
// openai-compatible-runner always emits `exec --json`.
|
|
1618
|
+
test: (cl) => /(?:^|[\\/"\s])codex(?:\.exe|\.cmd|\.ps1)?(?:"|\s)/iu.test(cl) && /\bexec\b/u.test(cl) && /--json\b/u.test(cl)
|
|
1619
|
+
}
|
|
1620
|
+
];
|
|
1621
|
+
function matchAgentSignature(commandLine) {
|
|
1622
|
+
if (typeof commandLine !== "string" || !commandLine) return null;
|
|
1623
|
+
for (const { signature, test } of SIGNATURES) {
|
|
1624
|
+
if (test(commandLine)) return signature;
|
|
1625
|
+
}
|
|
1626
|
+
return null;
|
|
1627
|
+
}
|
|
1628
|
+
function selectLegacyOrphans({ processes, cutoffMs, protectedPids = /* @__PURE__ */ new Set() }) {
|
|
1629
|
+
const byPid = /* @__PURE__ */ new Map();
|
|
1630
|
+
for (const proc of processes) {
|
|
1631
|
+
if (Number.isInteger(proc?.pid) && proc.pid > 0) byPid.set(proc.pid, proc);
|
|
1632
|
+
}
|
|
1633
|
+
const kills = [];
|
|
1634
|
+
for (const proc of byPid.values()) {
|
|
1635
|
+
if (protectedPids.has(proc.pid)) continue;
|
|
1636
|
+
if (!(Number.isFinite(proc.creationMs) && proc.creationMs < cutoffMs)) continue;
|
|
1637
|
+
const signature = matchAgentSignature(proc.commandLine);
|
|
1638
|
+
if (!signature) continue;
|
|
1639
|
+
const parent = Number.isInteger(proc.ppid) && proc.ppid > 0 ? byPid.get(proc.ppid) : void 0;
|
|
1640
|
+
const parentDead = !parent || Number.isFinite(parent.creationMs) && parent.creationMs > proc.creationMs;
|
|
1641
|
+
if (!parentDead) continue;
|
|
1642
|
+
kills.push({ pid: proc.pid, creationMs: proc.creationMs, signature, commandLine: proc.commandLine });
|
|
1643
|
+
}
|
|
1644
|
+
kills.sort((a, b) => a.creationMs - b.creationMs);
|
|
1645
|
+
return { kills: kills.slice(0, MAX_LEGACY_KILLS) };
|
|
1646
|
+
}
|
|
1647
|
+
function parsePosixSweepLine(line, nowMs) {
|
|
1648
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/u.exec(line ?? "");
|
|
1649
|
+
if (!match) return null;
|
|
1650
|
+
const pid = Number(match[1]);
|
|
1651
|
+
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
1652
|
+
return {
|
|
1653
|
+
pid,
|
|
1654
|
+
ppid: Number(match[2]),
|
|
1655
|
+
creationMs: nowMs - Number(match[3]) * 1e3,
|
|
1656
|
+
commandLine: match[4]
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
function listProcessesForSweep({ platform = process.platform, spawn: spawn2 = spawnSync4, nowMs = Date.now() } = {}) {
|
|
1660
|
+
const rows = [];
|
|
1661
|
+
if (platform === "win32") {
|
|
1662
|
+
const ps = "Get-CimInstance Win32_Process | Where-Object { $_.CreationDate } | ForEach-Object { @{ p = $_.ProcessId; pp = $_.ParentProcessId; c = (([DateTimeOffset]$_.CreationDate.ToUniversalTime()).ToUnixTimeMilliseconds()); cl = [string]$_.CommandLine } | ConvertTo-Json -Compress }";
|
|
1663
|
+
const result2 = spawn2("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], {
|
|
1664
|
+
windowsHide: true,
|
|
1665
|
+
encoding: "utf8",
|
|
1666
|
+
timeout: 3e4,
|
|
1667
|
+
maxBuffer: 64 * 1024 * 1024
|
|
1668
|
+
});
|
|
1669
|
+
if (result2.error || result2.status !== 0) return rows;
|
|
1670
|
+
for (const line of String(result2.stdout ?? "").split(/\r?\n/u)) {
|
|
1671
|
+
if (!line.trim()) continue;
|
|
1672
|
+
try {
|
|
1673
|
+
const parsed = JSON.parse(line);
|
|
1674
|
+
const pid = Number(parsed?.p);
|
|
1675
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
1676
|
+
rows.push({
|
|
1677
|
+
pid,
|
|
1678
|
+
ppid: Number(parsed.pp),
|
|
1679
|
+
creationMs: Number(parsed.c),
|
|
1680
|
+
commandLine: typeof parsed.cl === "string" ? parsed.cl : ""
|
|
1681
|
+
});
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return rows;
|
|
1686
|
+
}
|
|
1687
|
+
const result = spawn2("ps", ["-eo", "pid=,ppid=,etimes=,args="], { encoding: "utf8", timeout: 3e4, maxBuffer: 64 * 1024 * 1024 });
|
|
1688
|
+
if (result.error || result.status !== 0) return rows;
|
|
1689
|
+
for (const line of String(result.stdout ?? "").split("\n")) {
|
|
1690
|
+
const row = parsePosixSweepLine(line, nowMs);
|
|
1691
|
+
if (row) rows.push(row);
|
|
1692
|
+
}
|
|
1693
|
+
return rows;
|
|
1694
|
+
}
|
|
1695
|
+
function runLegacyOrphanSweep({
|
|
1696
|
+
nowMs = Date.now(),
|
|
1697
|
+
protectedPids = [process.pid],
|
|
1698
|
+
listProcesses = listProcessesForSweep,
|
|
1699
|
+
killTree = killProcessTree,
|
|
1700
|
+
log = () => {
|
|
1701
|
+
}
|
|
1702
|
+
} = {}) {
|
|
1703
|
+
try {
|
|
1704
|
+
const processes = listProcesses({ nowMs });
|
|
1705
|
+
const { kills } = selectLegacyOrphans({
|
|
1706
|
+
processes,
|
|
1707
|
+
cutoffMs: nowMs - SWEEP_RECENCY_BUFFER_MS,
|
|
1708
|
+
protectedPids: new Set(protectedPids)
|
|
1709
|
+
});
|
|
1710
|
+
const killed = [];
|
|
1711
|
+
for (const kill of kills) {
|
|
1712
|
+
const done = killTree(kill.pid);
|
|
1713
|
+
log(`legacy-orphan-sweep ${done ? "killed" : "FAILED to kill"} pid=${kill.pid} sig=${kill.signature} cmd=${String(kill.commandLine).slice(0, 200)}`);
|
|
1714
|
+
if (done) killed.push({ pid: kill.pid, signature: kill.signature });
|
|
1715
|
+
}
|
|
1716
|
+
const failed2 = kills.length - killed.length;
|
|
1717
|
+
const detail = kills.length === 0 ? `no orphaned agent processes matched the sweep criteria (${processes.length} scanned)` : `purged ${killed.length} of ${kills.length} orphaned agent process tree(s)${failed2 > 0 ? ` (${failed2} kill(s) failed)` : ""}: ${killed.map((k) => `${k.pid}:${k.signature}`).join(", ").slice(0, 700)}`;
|
|
1718
|
+
return { ok: true, status: 0, detail, killed };
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
return { ok: false, status: 1, detail: `legacy sweep error: ${error instanceof Error ? error.message : String(error)}`.slice(0, 500), killed: [] };
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1507
1724
|
// src/runner/supervisor-activation.mjs
|
|
1508
1725
|
var MAX_ACK_ATTEMPTS = 3;
|
|
1509
1726
|
var delay = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
@@ -1859,18 +2076,18 @@ async function prepareSupervisorAuth({
|
|
|
1859
2076
|
}
|
|
1860
2077
|
|
|
1861
2078
|
// src/runner/supervisor-credential-reader.mjs
|
|
1862
|
-
import { spawnSync as
|
|
1863
|
-
import { existsSync as
|
|
2079
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
2080
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1864
2081
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1865
2082
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1866
2083
|
function defaultCredentialHelperPath(metaUrl = import.meta.url) {
|
|
1867
2084
|
const moduleDir = dirname3(fileURLToPath2(metaUrl));
|
|
1868
2085
|
const bundled = join4(moduleDir, "supervisor-credential-helper.js");
|
|
1869
2086
|
const source = join4(moduleDir, "..", "supervisor-credential-helper.mjs");
|
|
1870
|
-
return
|
|
2087
|
+
return existsSync6(source) ? source : bundled;
|
|
1871
2088
|
}
|
|
1872
2089
|
function readStoredCredentialIsolated({
|
|
1873
|
-
spawn: spawn2 =
|
|
2090
|
+
spawn: spawn2 = spawnSync5,
|
|
1874
2091
|
execPath = process.execPath,
|
|
1875
2092
|
helperPath = defaultCredentialHelperPath(),
|
|
1876
2093
|
helperArgs = [],
|
|
@@ -1899,7 +2116,7 @@ function readStoredCredentialIsolated({
|
|
|
1899
2116
|
var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
1900
2117
|
var POLL_MS = 5e3;
|
|
1901
2118
|
var CHILD_START_MS = 1500;
|
|
1902
|
-
var SUPERVISOR_CAPABILITIES = ["bundled-runtime-slots-v1"];
|
|
2119
|
+
var SUPERVISOR_CAPABILITIES = ["bundled-runtime-slots-v1", "legacy-orphan-purge-v1"];
|
|
1903
2120
|
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
1904
2121
|
var selfPath = fileURLToPath3(import.meta.url);
|
|
1905
2122
|
var childEntry = join5(dirname4(selfPath), "runner-cli.js");
|
|
@@ -2062,6 +2279,9 @@ async function main() {
|
|
|
2062
2279
|
},
|
|
2063
2280
|
env: process.env,
|
|
2064
2281
|
force: action.kind === "reinstall"
|
|
2282
|
+
}) : action.kind === "purge-orphans" ? runLegacyOrphanSweep({
|
|
2283
|
+
protectedPids: [process.pid],
|
|
2284
|
+
log: (message) => console.warn(`[vo-runner supervisor] ${message}`)
|
|
2065
2285
|
}) : runHostMaintenance(action.kind, {
|
|
2066
2286
|
env: clientEnv,
|
|
2067
2287
|
log: (message) => console.warn(`[vo-runner supervisor] ${message}`)
|
|
@@ -2078,7 +2298,7 @@ async function main() {
|
|
|
2078
2298
|
...operatorId ? { operatorId } : {},
|
|
2079
2299
|
...supervisorControlIdentity,
|
|
2080
2300
|
status: result.ok ? "succeeded" : "failed",
|
|
2081
|
-
detail: result.ok ? `runner ${packageVersion()} reconnected` : `maintenance exited ${result.status}${result.detail ? `: ${result.detail}` : ""}`
|
|
2301
|
+
detail: result.ok ? result.detail ? `${result.detail}; runner ${packageVersion()} reconnected`.slice(0, 1e3) : `runner ${packageVersion()} reconnected` : `maintenance exited ${result.status}${result.detail ? `: ${result.detail}` : ""}`
|
|
2082
2302
|
});
|
|
2083
2303
|
handling = false;
|
|
2084
2304
|
} catch (error) {
|