@algosuite/vo-mcp 0.2.0-beta.67 → 0.2.0-beta.68
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/runner-cli.js +364 -281
- package/dist/runner-cli.js.map +3 -3
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -314,21 +314,21 @@ function backupConfigOnce(configPath) {
|
|
|
314
314
|
copyFileSync(configPath, backupPath);
|
|
315
315
|
return backupPath;
|
|
316
316
|
}
|
|
317
|
-
function writeFileAtomic(
|
|
318
|
-
sweepStaleTempFiles(
|
|
319
|
-
const temp = `${
|
|
317
|
+
function writeFileAtomic(path23, content) {
|
|
318
|
+
sweepStaleTempFiles(path23);
|
|
319
|
+
const temp = `${path23}.vo-mcp-tmp-${process.pid}-${Date.now()}`;
|
|
320
320
|
try {
|
|
321
321
|
writeFileSync2(temp, content, { encoding: "utf8", mode: 384 });
|
|
322
|
-
if (existsSync3(
|
|
322
|
+
if (existsSync3(path23)) {
|
|
323
323
|
try {
|
|
324
|
-
chmodSync2(temp, statSync(
|
|
324
|
+
chmodSync2(temp, statSync(path23).mode & 511);
|
|
325
325
|
} catch {
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
let lastErr = null;
|
|
329
329
|
for (let attempt = 0; attempt < RENAME_RETRIES; attempt += 1) {
|
|
330
330
|
try {
|
|
331
|
-
renameSync(temp,
|
|
331
|
+
renameSync(temp, path23);
|
|
332
332
|
return;
|
|
333
333
|
} catch (err) {
|
|
334
334
|
lastErr = err;
|
|
@@ -337,7 +337,7 @@ function writeFileAtomic(path22, content) {
|
|
|
337
337
|
sleepSync(RENAME_RETRY_MS);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
-
writeFileSync2(
|
|
340
|
+
writeFileSync2(path23, content, "utf8");
|
|
341
341
|
try {
|
|
342
342
|
unlinkSync2(temp);
|
|
343
343
|
} catch {
|
|
@@ -503,8 +503,8 @@ function tablePath(line) {
|
|
|
503
503
|
function tableSections(lines) {
|
|
504
504
|
const starts = [];
|
|
505
505
|
for (let index = 0; index < lines.length; index += 1) {
|
|
506
|
-
const
|
|
507
|
-
if (
|
|
506
|
+
const path23 = tablePath(lines[index] ?? "");
|
|
507
|
+
if (path23) starts.push({ path: path23, start: index });
|
|
508
508
|
}
|
|
509
509
|
return starts.map((section, index) => ({
|
|
510
510
|
...section,
|
|
@@ -696,11 +696,11 @@ function resolveLinuxConfigHome(home, env2) {
|
|
|
696
696
|
const configured = env2["XDG_CONFIG_HOME"]?.trim();
|
|
697
697
|
return configured && isAbsolute2(configured) ? configured : join5(home, ".config");
|
|
698
698
|
}
|
|
699
|
-
function launcherIsCurrent(
|
|
700
|
-
if (!existsSync6(
|
|
701
|
-
if (readFileSync5(
|
|
702
|
-
const backupPath = `${
|
|
703
|
-
copyFileSync2(
|
|
699
|
+
function launcherIsCurrent(path23, desiredContent, label, log2) {
|
|
700
|
+
if (!existsSync6(path23)) return false;
|
|
701
|
+
if (readFileSync5(path23, "utf8") === desiredContent) return true;
|
|
702
|
+
const backupPath = `${path23}.backup-${Date.now()}`;
|
|
703
|
+
copyFileSync2(path23, backupPath);
|
|
704
704
|
log2(` Backed up existing ${label} to: ${backupPath}`);
|
|
705
705
|
return false;
|
|
706
706
|
}
|
|
@@ -916,17 +916,17 @@ function resolveDesktopConfigPath(home, plat, appData) {
|
|
|
916
916
|
}
|
|
917
917
|
return join6(home, ".config", "Claude", "claude_desktop_config.json");
|
|
918
918
|
}
|
|
919
|
-
function readClaudeConfig(
|
|
920
|
-
if (!existsSync7(
|
|
919
|
+
function readClaudeConfig(path23) {
|
|
920
|
+
if (!existsSync7(path23)) return { kind: "absent", config: {}, mtimeMs: null };
|
|
921
921
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
922
|
-
const before = statSync3(
|
|
922
|
+
const before = statSync3(path23).mtimeMs;
|
|
923
923
|
let raw;
|
|
924
924
|
try {
|
|
925
|
-
raw = readFileSync6(
|
|
925
|
+
raw = readFileSync6(path23, "utf8");
|
|
926
926
|
} catch {
|
|
927
927
|
return { kind: "invalid", config: {}, mtimeMs: before };
|
|
928
928
|
}
|
|
929
|
-
if (!existsSync7(
|
|
929
|
+
if (!existsSync7(path23) || statSync3(path23).mtimeMs !== before) continue;
|
|
930
930
|
const text = raw.replace(/^\uFEFF/u, "");
|
|
931
931
|
if (!text.trim()) return { kind: "empty", config: {}, mtimeMs: before };
|
|
932
932
|
try {
|
|
@@ -938,9 +938,9 @@ function readClaudeConfig(path22) {
|
|
|
938
938
|
}
|
|
939
939
|
return { kind: "invalid", config: {}, mtimeMs: null };
|
|
940
940
|
}
|
|
941
|
-
function writeClaudeConfig(
|
|
942
|
-
mkdirSync6(dirname4(
|
|
943
|
-
writeFileAtomic(
|
|
941
|
+
function writeClaudeConfig(path23, config) {
|
|
942
|
+
mkdirSync6(dirname4(path23), { recursive: true });
|
|
943
|
+
writeFileAtomic(path23, `${JSON.stringify(config, null, 2)}
|
|
944
944
|
`);
|
|
945
945
|
}
|
|
946
946
|
function carriedEntryKeys(entry) {
|
|
@@ -1344,6 +1344,7 @@ var init_pnpm_link_detach = __esm({
|
|
|
1344
1344
|
|
|
1345
1345
|
// src/runner/process-runner.mjs
|
|
1346
1346
|
import { spawn } from "node:child_process";
|
|
1347
|
+
import path2 from "node:path";
|
|
1347
1348
|
function sleepMs(ms) {
|
|
1348
1349
|
if (ms <= 0) return Promise.resolve();
|
|
1349
1350
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
@@ -1351,15 +1352,25 @@ function sleepMs(ms) {
|
|
|
1351
1352
|
async function spawnUtility(command, args, timeoutMs) {
|
|
1352
1353
|
return await new Promise((resolve3) => {
|
|
1353
1354
|
let settled = false;
|
|
1355
|
+
let stdout = "";
|
|
1356
|
+
let stderr = "";
|
|
1354
1357
|
const finish = (result) => {
|
|
1355
1358
|
if (settled) return;
|
|
1356
1359
|
settled = true;
|
|
1357
|
-
resolve3(result);
|
|
1360
|
+
resolve3({ stdout, stderr, ...result });
|
|
1358
1361
|
};
|
|
1359
1362
|
const child = spawn(command, args, {
|
|
1360
|
-
stdio: "ignore",
|
|
1363
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1361
1364
|
windowsHide: true
|
|
1362
1365
|
});
|
|
1366
|
+
child.stdout?.setEncoding("utf8");
|
|
1367
|
+
child.stderr?.setEncoding("utf8");
|
|
1368
|
+
child.stdout?.on("data", (chunk) => {
|
|
1369
|
+
stdout = `${stdout}${chunk}`.slice(-MAX_UTILITY_OUTPUT_CHARS);
|
|
1370
|
+
});
|
|
1371
|
+
child.stderr?.on("data", (chunk) => {
|
|
1372
|
+
stderr = `${stderr}${chunk}`.slice(-MAX_UTILITY_OUTPUT_CHARS);
|
|
1373
|
+
});
|
|
1363
1374
|
let timer = null;
|
|
1364
1375
|
if (timeoutMs > 0) {
|
|
1365
1376
|
timer = setTimeout(() => {
|
|
@@ -1381,27 +1392,48 @@ async function spawnUtility(command, args, timeoutMs) {
|
|
|
1381
1392
|
});
|
|
1382
1393
|
});
|
|
1383
1394
|
}
|
|
1395
|
+
function windowsTaskkillExe(env2 = process.env) {
|
|
1396
|
+
const systemRoot = env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
1397
|
+
return path2.win32.join(systemRoot, "System32", "taskkill.exe");
|
|
1398
|
+
}
|
|
1384
1399
|
async function killProcessTreeDefault(pid, options = {}) {
|
|
1385
1400
|
if (!Number.isInteger(pid) || pid <= 0) return;
|
|
1386
|
-
if (process.platform === "win32") {
|
|
1387
|
-
|
|
1388
|
-
|
|
1401
|
+
if ((options.platform || process.platform) === "win32") {
|
|
1402
|
+
const utilityRunner = options.spawnUtility || spawnUtility;
|
|
1403
|
+
return await utilityRunner(
|
|
1404
|
+
windowsTaskkillExe(options.env || process.env),
|
|
1389
1405
|
["/pid", String(pid), "/t", "/f"],
|
|
1390
1406
|
options.timeoutMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS
|
|
1391
1407
|
);
|
|
1392
|
-
return;
|
|
1393
1408
|
}
|
|
1394
1409
|
try {
|
|
1395
1410
|
process.kill(pid, "SIGKILL");
|
|
1396
1411
|
} catch {
|
|
1397
1412
|
}
|
|
1398
1413
|
}
|
|
1414
|
+
function cleanupResultError(result) {
|
|
1415
|
+
if (!result || typeof result !== "object") return null;
|
|
1416
|
+
if (result.error) {
|
|
1417
|
+
return result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
1418
|
+
}
|
|
1419
|
+
const detail = String(result.stderr || result.stdout || "").trim().replace(/\s+/g, " ").slice(-400);
|
|
1420
|
+
if (result.timedOut) {
|
|
1421
|
+
return new Error(`process-tree cleanup utility timed out${detail ? `: ${detail}` : ""}`);
|
|
1422
|
+
}
|
|
1423
|
+
if ("status" in result && result.status !== 0) {
|
|
1424
|
+
return new Error(
|
|
1425
|
+
`process-tree cleanup utility exited with status ${String(result.status)}${detail ? `: ${detail}` : ""}`
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
return null;
|
|
1429
|
+
}
|
|
1399
1430
|
async function runProcess(command, args = [], options = {}) {
|
|
1400
1431
|
const timeoutMs = options.timeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS;
|
|
1401
1432
|
const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;
|
|
1402
1433
|
const forceKillTimeoutMs = options.forceKillTimeoutMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS;
|
|
1434
|
+
const platform4 = options.platform || process.platform;
|
|
1403
1435
|
const spawnImpl = options.spawnImpl || spawn;
|
|
1404
|
-
const killProcessTree2 = options.killProcessTree || killProcessTreeDefault;
|
|
1436
|
+
const killProcessTree2 = options.killProcessTree || ((pid, killOptions) => killProcessTreeDefault(pid, { ...killOptions, platform: platform4 }));
|
|
1405
1437
|
const spawnOptions = {
|
|
1406
1438
|
cwd: options.cwd,
|
|
1407
1439
|
env: options.env,
|
|
@@ -1417,6 +1449,9 @@ async function runProcess(command, args = [], options = {}) {
|
|
|
1417
1449
|
let graceTimeout = null;
|
|
1418
1450
|
let forceKillTimeout = null;
|
|
1419
1451
|
let child = null;
|
|
1452
|
+
let parentExit = null;
|
|
1453
|
+
let cleanupInFlight = false;
|
|
1454
|
+
let pendingTerminalResult = null;
|
|
1420
1455
|
const clearTimers = () => {
|
|
1421
1456
|
if (timeout) clearTimeout(timeout);
|
|
1422
1457
|
if (graceTimeout) clearTimeout(graceTimeout);
|
|
@@ -1428,8 +1463,33 @@ async function runProcess(command, args = [], options = {}) {
|
|
|
1428
1463
|
clearTimers();
|
|
1429
1464
|
resolve3({ stdout, stderr, timedOut, ...result });
|
|
1430
1465
|
};
|
|
1466
|
+
const handleTerminalResult = (result) => {
|
|
1467
|
+
const normalizedResult = timedOut && typeof result.status === "number" ? { ...result, exitStatus: result.status, status: null } : result;
|
|
1468
|
+
if (timedOut && cleanupInFlight) {
|
|
1469
|
+
pendingTerminalResult = normalizedResult;
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
finish(normalizedResult);
|
|
1473
|
+
};
|
|
1474
|
+
const exitedParentOwnershipError = () => {
|
|
1475
|
+
return new Error(
|
|
1476
|
+
`process parent exited before timeout cleanup, but inherited stdio remained open; PID ${String(child?.pid ?? "unknown")} may no longer belong to the spawned process`
|
|
1477
|
+
);
|
|
1478
|
+
};
|
|
1431
1479
|
const beginForceKill = () => {
|
|
1432
|
-
if (settled
|
|
1480
|
+
if (settled) return;
|
|
1481
|
+
if (parentExit) {
|
|
1482
|
+
finish({
|
|
1483
|
+
status: null,
|
|
1484
|
+
error: exitedParentOwnershipError()
|
|
1485
|
+
});
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (!Number.isInteger(child?.pid) || child.pid <= 0) {
|
|
1489
|
+
finish({ status: null, error: new Error("process timed out before a process-tree cleanup PID was available") });
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
cleanupInFlight = true;
|
|
1433
1493
|
forceKillTimeout = setTimeout(() => {
|
|
1434
1494
|
finish({
|
|
1435
1495
|
status: null,
|
|
@@ -1437,17 +1497,36 @@ async function runProcess(command, args = [], options = {}) {
|
|
|
1437
1497
|
});
|
|
1438
1498
|
}, forceKillTimeoutMs);
|
|
1439
1499
|
forceKillTimeout.unref?.();
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1500
|
+
const cleanupPromise = Promise.resolve().then(
|
|
1501
|
+
() => {
|
|
1502
|
+
if (settled) return void 0;
|
|
1503
|
+
if (parentExit) throw exitedParentOwnershipError();
|
|
1504
|
+
return killProcessTree2(child.pid, { timeoutMs: forceKillTimeoutMs });
|
|
1505
|
+
}
|
|
1506
|
+
);
|
|
1507
|
+
void cleanupPromise.then(
|
|
1508
|
+
(result) => {
|
|
1509
|
+
const error = cleanupResultError(result);
|
|
1510
|
+
cleanupInFlight = false;
|
|
1511
|
+
if (error) {
|
|
1512
|
+
finish({ status: null, error });
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
if (pendingTerminalResult) finish(pendingTerminalResult);
|
|
1516
|
+
},
|
|
1517
|
+
(error) => {
|
|
1518
|
+
cleanupInFlight = false;
|
|
1444
1519
|
finish({ status: null, error });
|
|
1445
1520
|
}
|
|
1446
|
-
|
|
1521
|
+
);
|
|
1447
1522
|
};
|
|
1448
1523
|
const beginTimeout = () => {
|
|
1449
1524
|
if (settled || !child) return;
|
|
1450
1525
|
timedOut = true;
|
|
1526
|
+
if (platform4 === "win32") {
|
|
1527
|
+
beginForceKill();
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1451
1530
|
try {
|
|
1452
1531
|
child.kill("SIGTERM");
|
|
1453
1532
|
} catch (error) {
|
|
@@ -1473,8 +1552,11 @@ async function runProcess(command, args = [], options = {}) {
|
|
|
1473
1552
|
stderr += chunk;
|
|
1474
1553
|
if (typeof options.onStderr === "function") options.onStderr(chunk);
|
|
1475
1554
|
});
|
|
1476
|
-
child.on("error", (error) =>
|
|
1477
|
-
child.on("
|
|
1555
|
+
child.on("error", (error) => handleTerminalResult({ status: null, error }));
|
|
1556
|
+
child.on("exit", (code, signal) => {
|
|
1557
|
+
parentExit = { code, signal };
|
|
1558
|
+
});
|
|
1559
|
+
child.on("close", (code, signal) => handleTerminalResult({ status: code, signal }));
|
|
1478
1560
|
if (timeoutMs > 0) {
|
|
1479
1561
|
timeout = setTimeout(beginTimeout, timeoutMs);
|
|
1480
1562
|
timeout.unref?.();
|
|
@@ -1499,13 +1581,14 @@ function summarizeProcessFailure(result, options = {}) {
|
|
|
1499
1581
|
const timedOutText = result?.timedOut ? "timed out" : "";
|
|
1500
1582
|
return [errorText, timedOutText, tail].filter(Boolean).join("; ").slice(0, limit) || `command exited with status ${String(result?.status ?? "unknown")}`;
|
|
1501
1583
|
}
|
|
1502
|
-
var DEFAULT_PROCESS_TIMEOUT_MS, DEFAULT_KILL_GRACE_MS, DEFAULT_FORCE_KILL_TIMEOUT_MS;
|
|
1584
|
+
var DEFAULT_PROCESS_TIMEOUT_MS, DEFAULT_KILL_GRACE_MS, DEFAULT_FORCE_KILL_TIMEOUT_MS, MAX_UTILITY_OUTPUT_CHARS;
|
|
1503
1585
|
var init_process_runner = __esm({
|
|
1504
1586
|
"src/runner/process-runner.mjs"() {
|
|
1505
1587
|
"use strict";
|
|
1506
1588
|
DEFAULT_PROCESS_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
1507
1589
|
DEFAULT_KILL_GRACE_MS = 5e3;
|
|
1508
1590
|
DEFAULT_FORCE_KILL_TIMEOUT_MS = 15e3;
|
|
1591
|
+
MAX_UTILITY_OUTPUT_CHARS = 4096;
|
|
1509
1592
|
}
|
|
1510
1593
|
});
|
|
1511
1594
|
|
|
@@ -1648,7 +1731,7 @@ var init_worktree_add = __esm({
|
|
|
1648
1731
|
// src/runner/pnpm-canonical-health.mjs
|
|
1649
1732
|
import fs from "node:fs";
|
|
1650
1733
|
import fsp3 from "node:fs/promises";
|
|
1651
|
-
import
|
|
1734
|
+
import path3 from "node:path";
|
|
1652
1735
|
function readJsonFile(file) {
|
|
1653
1736
|
if (!fs.existsSync(file)) return null;
|
|
1654
1737
|
try {
|
|
@@ -1658,7 +1741,7 @@ function readJsonFile(file) {
|
|
|
1658
1741
|
}
|
|
1659
1742
|
}
|
|
1660
1743
|
function packageJsonAt(root) {
|
|
1661
|
-
return readJsonFile(
|
|
1744
|
+
return readJsonFile(path3.join(root, "package.json"));
|
|
1662
1745
|
}
|
|
1663
1746
|
function dependencyNamesForPackage(root) {
|
|
1664
1747
|
const manifest = packageJsonAt(root);
|
|
@@ -1670,7 +1753,7 @@ function dependencyNamesForPackage(root) {
|
|
|
1670
1753
|
])].sort();
|
|
1671
1754
|
}
|
|
1672
1755
|
function packageEntryPath(nodeModulesDir, packageName) {
|
|
1673
|
-
return
|
|
1756
|
+
return path3.join(nodeModulesDir, ...String(packageName || "").split("/"));
|
|
1674
1757
|
}
|
|
1675
1758
|
function rootFsApi() {
|
|
1676
1759
|
return {
|
|
@@ -1699,7 +1782,7 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
|
|
|
1699
1782
|
} catch {
|
|
1700
1783
|
return { issue: `missing or dangling dependency link: ${entry}` };
|
|
1701
1784
|
}
|
|
1702
|
-
const packageJsonPath =
|
|
1785
|
+
const packageJsonPath = path3.join(resolved, "package.json");
|
|
1703
1786
|
if (!await pathExists3(packageJsonPath, fsApi)) {
|
|
1704
1787
|
return { issue: `dependency target is missing package.json: ${entry} -> ${resolved}` };
|
|
1705
1788
|
}
|
|
@@ -1711,11 +1794,11 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
|
|
|
1711
1794
|
return { issue: null };
|
|
1712
1795
|
}
|
|
1713
1796
|
function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
|
|
1714
|
-
const packages = [{ nodeModulesDir:
|
|
1797
|
+
const packages = [{ nodeModulesDir: path3.join(root, "node_modules"), packageRoot: root }];
|
|
1715
1798
|
for (const relativeDir of linkedWorkspaceDirs) {
|
|
1716
1799
|
packages.push({
|
|
1717
|
-
nodeModulesDir:
|
|
1718
|
-
packageRoot:
|
|
1800
|
+
nodeModulesDir: path3.join(root, relativeDir, "node_modules"),
|
|
1801
|
+
packageRoot: path3.join(root, relativeDir)
|
|
1719
1802
|
});
|
|
1720
1803
|
}
|
|
1721
1804
|
const representatives = [];
|
|
@@ -1727,7 +1810,7 @@ function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
|
|
|
1727
1810
|
return representatives;
|
|
1728
1811
|
}
|
|
1729
1812
|
function canonicalNodeModulesQuarantineRoot(root) {
|
|
1730
|
-
return
|
|
1813
|
+
return path3.join(root, ".agent-worktrees", QUARANTINE_DIRNAME);
|
|
1731
1814
|
}
|
|
1732
1815
|
async function inspectCanonicalNodeModulesHealth({
|
|
1733
1816
|
root,
|
|
@@ -1738,8 +1821,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1738
1821
|
sampleLimit = DEFAULT_SAMPLE_LIMIT
|
|
1739
1822
|
}) {
|
|
1740
1823
|
const issues = [];
|
|
1741
|
-
const nodeModulesDir =
|
|
1742
|
-
const markerPath =
|
|
1824
|
+
const nodeModulesDir = path3.join(root, "node_modules");
|
|
1825
|
+
const markerPath = path3.join(nodeModulesDir, ".vo-deps-state.json");
|
|
1743
1826
|
if (!await pathExists3(nodeModulesDir, fsApi)) {
|
|
1744
1827
|
issues.push(`missing root node_modules: ${nodeModulesDir}`);
|
|
1745
1828
|
} else {
|
|
@@ -1748,8 +1831,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1748
1831
|
issues.push(`root node_modules must be a real directory: ${nodeModulesDir}`);
|
|
1749
1832
|
}
|
|
1750
1833
|
}
|
|
1751
|
-
if (!await pathExists3(
|
|
1752
|
-
issues.push(`missing root .pnpm store view: ${
|
|
1834
|
+
if (!await pathExists3(path3.join(nodeModulesDir, ".pnpm"), fsApi)) {
|
|
1835
|
+
issues.push(`missing root .pnpm store view: ${path3.join(nodeModulesDir, ".pnpm")}`);
|
|
1753
1836
|
}
|
|
1754
1837
|
if (requireMarker) {
|
|
1755
1838
|
try {
|
|
@@ -1762,7 +1845,7 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1762
1845
|
}
|
|
1763
1846
|
}
|
|
1764
1847
|
for (const relativeDir of linkedWorkspaceDirs) {
|
|
1765
|
-
const workspaceNodeModules =
|
|
1848
|
+
const workspaceNodeModules = path3.join(root, relativeDir, "node_modules");
|
|
1766
1849
|
if (!await pathExists3(workspaceNodeModules, fsApi)) {
|
|
1767
1850
|
issues.push(`missing workspace node_modules: ${workspaceNodeModules}`);
|
|
1768
1851
|
continue;
|
|
@@ -1785,17 +1868,17 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1785
1868
|
async function quarantineCanonicalNodeModules(root, issues, options = {}) {
|
|
1786
1869
|
const fsApi = options.fsApi || rootFsApi();
|
|
1787
1870
|
const logger = options.logger || console.error;
|
|
1788
|
-
const nodeModulesDir =
|
|
1871
|
+
const nodeModulesDir = path3.join(root, "node_modules");
|
|
1789
1872
|
const quarantineRoot = canonicalNodeModulesQuarantineRoot(root);
|
|
1790
1873
|
if (!await pathExists3(nodeModulesDir, fsApi)) return null;
|
|
1791
1874
|
await fsApi.mkdir(quarantineRoot, { recursive: true });
|
|
1792
1875
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1793
1876
|
let attempt = 0;
|
|
1794
1877
|
for (; ; ) {
|
|
1795
|
-
const quarantinePath =
|
|
1878
|
+
const quarantinePath = path3.join(quarantineRoot, `node_modules-${stamp}-${process.pid}-${attempt}`);
|
|
1796
1879
|
try {
|
|
1797
1880
|
await fsApi.rename(nodeModulesDir, quarantinePath);
|
|
1798
|
-
const metadataPath =
|
|
1881
|
+
const metadataPath = path3.join(quarantinePath, ".vo-runner-quarantine.json");
|
|
1799
1882
|
await fsApi.writeFile(metadataPath, `${JSON.stringify({
|
|
1800
1883
|
issues,
|
|
1801
1884
|
originalPath: nodeModulesDir,
|
|
@@ -1821,22 +1904,22 @@ var init_pnpm_canonical_health = __esm({
|
|
|
1821
1904
|
|
|
1822
1905
|
// src/runner/pnpm-command.mjs
|
|
1823
1906
|
import fs2 from "node:fs";
|
|
1824
|
-
import
|
|
1907
|
+
import path4 from "node:path";
|
|
1825
1908
|
function isWindowsDriveOrUnc(value) {
|
|
1826
1909
|
return WINDOWS_DRIVE_OR_UNC_RE.test(String(value || ""));
|
|
1827
1910
|
}
|
|
1828
1911
|
function portableDirname(value) {
|
|
1829
|
-
if (isWindowsDriveOrUnc(value)) return
|
|
1830
|
-
if (
|
|
1831
|
-
return
|
|
1912
|
+
if (isWindowsDriveOrUnc(value)) return path4.win32.dirname(value);
|
|
1913
|
+
if (path4.posix.isAbsolute(value)) return path4.posix.dirname(value);
|
|
1914
|
+
return path4.dirname(value);
|
|
1832
1915
|
}
|
|
1833
1916
|
function portableJoin(root, ...segments) {
|
|
1834
|
-
if (isWindowsDriveOrUnc(root)) return
|
|
1835
|
-
if (
|
|
1836
|
-
return
|
|
1917
|
+
if (isWindowsDriveOrUnc(root)) return path4.win32.join(root, ...segments);
|
|
1918
|
+
if (path4.posix.isAbsolute(root)) return path4.posix.join(root, ...segments);
|
|
1919
|
+
return path4.join(root, ...segments);
|
|
1837
1920
|
}
|
|
1838
1921
|
function readPackageManager(root) {
|
|
1839
|
-
const packagePath =
|
|
1922
|
+
const packagePath = path4.join(root, "package.json");
|
|
1840
1923
|
if (!fs2.existsSync(packagePath)) return "";
|
|
1841
1924
|
try {
|
|
1842
1925
|
return String(JSON.parse(fs2.readFileSync(packagePath, "utf8"))?.packageManager || "").trim();
|
|
@@ -1856,13 +1939,13 @@ function pnpmSelector(root) {
|
|
|
1856
1939
|
const packageManager = readPackageManager(root);
|
|
1857
1940
|
const version = validatedPnpmVersionToken(root);
|
|
1858
1941
|
if (packageManager && !version) {
|
|
1859
|
-
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${
|
|
1942
|
+
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path4.join(root, "package.json")}`);
|
|
1860
1943
|
}
|
|
1861
1944
|
return version ? `pnpm@${version}` : "pnpm";
|
|
1862
1945
|
}
|
|
1863
1946
|
function whereResults(result) {
|
|
1864
1947
|
if (result?.status !== 0) return [];
|
|
1865
|
-
return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) =>
|
|
1948
|
+
return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path4.win32.isAbsolute(line));
|
|
1866
1949
|
}
|
|
1867
1950
|
async function resolveWindowsNativeCommand(command, runner) {
|
|
1868
1951
|
const result = await runner("where", [command], { timeoutMs: 1e4 });
|
|
@@ -1874,7 +1957,7 @@ function trustedCorepackCandidates(options) {
|
|
|
1874
1957
|
const roots = [portableDirname(execPath)];
|
|
1875
1958
|
for (const key of ["ProgramW6432", "ProgramFiles", "ProgramFiles(x86)"]) {
|
|
1876
1959
|
const programFiles = String(env2[key] || "").trim();
|
|
1877
|
-
if (isWindowsDriveOrUnc(programFiles) ||
|
|
1960
|
+
if (isWindowsDriveOrUnc(programFiles) || path4.posix.isAbsolute(programFiles)) {
|
|
1878
1961
|
roots.push(portableJoin(programFiles, "nodejs"));
|
|
1879
1962
|
}
|
|
1880
1963
|
}
|
|
@@ -1953,7 +2036,7 @@ var init_pnpm_command = __esm({
|
|
|
1953
2036
|
|
|
1954
2037
|
// src/runner/pnpm-materialize.mjs
|
|
1955
2038
|
import fsp4 from "node:fs/promises";
|
|
1956
|
-
import
|
|
2039
|
+
import path5 from "node:path";
|
|
1957
2040
|
function linkType() {
|
|
1958
2041
|
return process.platform === "win32" ? "junction" : "dir";
|
|
1959
2042
|
}
|
|
@@ -1965,13 +2048,13 @@ async function realpathOrThrow(target, fsApi) {
|
|
|
1965
2048
|
}
|
|
1966
2049
|
}
|
|
1967
2050
|
function shouldMapToWorktree(resolvedTarget, canonicalRoot) {
|
|
1968
|
-
const relative =
|
|
2051
|
+
const relative = path5.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
|
|
1969
2052
|
return Boolean(relative && !relative.startsWith("..") && !relative.split("/").includes("node_modules"));
|
|
1970
2053
|
}
|
|
1971
2054
|
async function resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, fsApi) {
|
|
1972
2055
|
const resolved = await realpathOrThrow(sourceEntry, fsApi);
|
|
1973
2056
|
if (!shouldMapToWorktree(resolved, canonicalRoot)) return resolved;
|
|
1974
|
-
const mapped =
|
|
2057
|
+
const mapped = path5.join(worktreeRoot, path5.relative(canonicalRoot, resolved));
|
|
1975
2058
|
if (!await fsApi.pathExists(mapped)) {
|
|
1976
2059
|
throw new Error(`[vo-mcp runner] task-local workspace target is missing for dependency link: ${mapped}`);
|
|
1977
2060
|
}
|
|
@@ -1982,7 +2065,7 @@ async function ensureLinkedDirectory(source, target, fsApi) {
|
|
|
1982
2065
|
if (await fsApi.realpath(target) === await fsApi.realpath(source)) return;
|
|
1983
2066
|
throw new Error(`[vo-mcp runner] refusing to overwrite existing dependency path: ${target}`);
|
|
1984
2067
|
}
|
|
1985
|
-
await fsApi.mkdir(
|
|
2068
|
+
await fsApi.mkdir(path5.dirname(target), { recursive: true });
|
|
1986
2069
|
await fsApi.symlink(source, target, linkType());
|
|
1987
2070
|
if (await fsApi.realpath(target) !== await fsApi.realpath(source)) {
|
|
1988
2071
|
throw new Error(`[vo-mcp runner] dependency link validation failed for ${target}`);
|
|
@@ -1996,8 +2079,8 @@ async function maybeYield2(state) {
|
|
|
1996
2079
|
async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
1997
2080
|
await fsApi.mkdir(targetDir, { recursive: true });
|
|
1998
2081
|
for (const entry of await fsApi.readdir(sourceDir, { withFileTypes: true })) {
|
|
1999
|
-
const source =
|
|
2000
|
-
const target =
|
|
2082
|
+
const source = path5.join(sourceDir, entry.name);
|
|
2083
|
+
const target = path5.join(targetDir, entry.name);
|
|
2001
2084
|
await maybeYield2(yieldState);
|
|
2002
2085
|
if (entry.isDirectory()) {
|
|
2003
2086
|
await copyDirRecursive(source, target, fsApi, yieldState);
|
|
@@ -2009,17 +2092,17 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
|
2009
2092
|
async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
|
|
2010
2093
|
await options.beforeEntry?.(sourceEntry, targetEntry);
|
|
2011
2094
|
const stat3 = await options.fsApi.lstat(sourceEntry);
|
|
2012
|
-
if (stat3.isDirectory() && !stat3.isSymbolicLink() &&
|
|
2095
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path5.basename(sourceEntry) === ".bin") {
|
|
2013
2096
|
await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
|
|
2014
2097
|
return;
|
|
2015
2098
|
}
|
|
2016
|
-
if (stat3.isDirectory() && !stat3.isSymbolicLink() &&
|
|
2099
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path5.basename(sourceEntry).startsWith("@")) {
|
|
2017
2100
|
await options.fsApi.mkdir(targetEntry, { recursive: true });
|
|
2018
2101
|
for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
|
|
2019
2102
|
await maybeYield2(options.yieldState);
|
|
2020
2103
|
await materializeEntry(
|
|
2021
|
-
|
|
2022
|
-
|
|
2104
|
+
path5.join(sourceEntry, nested.name),
|
|
2105
|
+
path5.join(targetEntry, nested.name),
|
|
2023
2106
|
canonicalRoot,
|
|
2024
2107
|
worktreeRoot,
|
|
2025
2108
|
options
|
|
@@ -2032,7 +2115,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
|
|
|
2032
2115
|
await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
|
|
2033
2116
|
return;
|
|
2034
2117
|
}
|
|
2035
|
-
await options.fsApi.mkdir(
|
|
2118
|
+
await options.fsApi.mkdir(path5.dirname(targetEntry), { recursive: true });
|
|
2036
2119
|
await options.fsApi.copyFile(sourceEntry, targetEntry);
|
|
2037
2120
|
}
|
|
2038
2121
|
function createPnpmFsApi(pathExists6) {
|
|
@@ -2056,8 +2139,8 @@ async function materializeNodeModulesForest(sourceNodeModules, targetNodeModules
|
|
|
2056
2139
|
for (const entry of await options.fsApi.readdir(sourceNodeModules, { withFileTypes: true })) {
|
|
2057
2140
|
await maybeYield2(options.yieldState);
|
|
2058
2141
|
await materializeEntry(
|
|
2059
|
-
|
|
2060
|
-
|
|
2142
|
+
path5.join(sourceNodeModules, entry.name),
|
|
2143
|
+
path5.join(targetNodeModules, entry.name),
|
|
2061
2144
|
canonicalRoot,
|
|
2062
2145
|
worktreeRoot,
|
|
2063
2146
|
options
|
|
@@ -2074,15 +2157,15 @@ var init_pnpm_materialize = __esm({
|
|
|
2074
2157
|
import { createHash as createHash2 } from "node:crypto";
|
|
2075
2158
|
import fs3 from "node:fs";
|
|
2076
2159
|
import fsp5 from "node:fs/promises";
|
|
2077
|
-
import
|
|
2160
|
+
import path6 from "node:path";
|
|
2078
2161
|
function hashText(text) {
|
|
2079
2162
|
return createHash2("sha256").update(String(text)).digest("hex");
|
|
2080
2163
|
}
|
|
2081
2164
|
function statePath(root) {
|
|
2082
|
-
return
|
|
2165
|
+
return path6.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
|
|
2083
2166
|
}
|
|
2084
2167
|
function packageJson(root) {
|
|
2085
|
-
const packagePath =
|
|
2168
|
+
const packagePath = path6.join(root, "package.json");
|
|
2086
2169
|
if (!fs3.existsSync(packagePath)) return null;
|
|
2087
2170
|
try {
|
|
2088
2171
|
return JSON.parse(fs3.readFileSync(packagePath, "utf8"));
|
|
@@ -2099,7 +2182,7 @@ async function pathExists4(target) {
|
|
|
2099
2182
|
}
|
|
2100
2183
|
}
|
|
2101
2184
|
function lockfileHash(root) {
|
|
2102
|
-
const lockPath =
|
|
2185
|
+
const lockPath = path6.join(root, "pnpm-lock.yaml");
|
|
2103
2186
|
if (!fs3.existsSync(lockPath)) return "";
|
|
2104
2187
|
return hashText(fs3.readFileSync(lockPath, "utf8"));
|
|
2105
2188
|
}
|
|
@@ -2114,7 +2197,7 @@ function readHydrationState(root) {
|
|
|
2114
2197
|
}
|
|
2115
2198
|
function writeHydrationState(root, state) {
|
|
2116
2199
|
const file = statePath(root);
|
|
2117
|
-
fs3.mkdirSync(
|
|
2200
|
+
fs3.mkdirSync(path6.dirname(file), { recursive: true });
|
|
2118
2201
|
fs3.writeFileSync(file, `${JSON.stringify({
|
|
2119
2202
|
...state,
|
|
2120
2203
|
stateVersion: 2,
|
|
@@ -2123,7 +2206,7 @@ function writeHydrationState(root, state) {
|
|
|
2123
2206
|
`, "utf8");
|
|
2124
2207
|
}
|
|
2125
2208
|
function voDepsStatePath(root) {
|
|
2126
|
-
return
|
|
2209
|
+
return path6.join(root, "node_modules", ".vo-deps-state.json");
|
|
2127
2210
|
}
|
|
2128
2211
|
async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
2129
2212
|
const marker = voDepsStatePath(root);
|
|
@@ -2133,7 +2216,7 @@ async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
|
2133
2216
|
} catch {
|
|
2134
2217
|
}
|
|
2135
2218
|
const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
|
|
2136
|
-
await fsApi.mkdir(
|
|
2219
|
+
await fsApi.mkdir(path6.dirname(marker), { recursive: true });
|
|
2137
2220
|
await fsApi.writeFile(temp, `${JSON.stringify({ lockfileHash: expectedHash, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
2138
2221
|
`, "utf8");
|
|
2139
2222
|
await fsApi.rename(temp, marker);
|
|
@@ -2160,7 +2243,7 @@ function workspacePatternsFromPackageJson(root) {
|
|
|
2160
2243
|
return [];
|
|
2161
2244
|
}
|
|
2162
2245
|
function workspacePatternsFromPnpmWorkspace(root) {
|
|
2163
|
-
const workspacePath =
|
|
2246
|
+
const workspacePath = path6.join(root, "pnpm-workspace.yaml");
|
|
2164
2247
|
if (!fs3.existsSync(workspacePath)) return [];
|
|
2165
2248
|
const lines = fs3.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
|
|
2166
2249
|
const patterns = [];
|
|
@@ -2219,8 +2302,8 @@ function collectPackageDirs(root, options = {}) {
|
|
|
2219
2302
|
while (stack.length > 0) {
|
|
2220
2303
|
const current = stack.pop();
|
|
2221
2304
|
if (!current) continue;
|
|
2222
|
-
const relativeDir =
|
|
2223
|
-
if (relativeDir && fs3.existsSync(
|
|
2305
|
+
const relativeDir = path6.relative(root, current.dir).replace(/\\/g, "/");
|
|
2306
|
+
if (relativeDir && fs3.existsSync(path6.join(current.dir, "package.json"))) {
|
|
2224
2307
|
found.push(relativeDir);
|
|
2225
2308
|
if (found.length >= maxDirs) break;
|
|
2226
2309
|
}
|
|
@@ -2228,7 +2311,7 @@ function collectPackageDirs(root, options = {}) {
|
|
|
2228
2311
|
for (const entry of fs3.readdirSync(current.dir, { withFileTypes: true })) {
|
|
2229
2312
|
if (!entry.isDirectory()) continue;
|
|
2230
2313
|
if (IGNORED_SCAN_DIRS.has(entry.name)) continue;
|
|
2231
|
-
stack.push({ dir:
|
|
2314
|
+
stack.push({ dir: path6.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
2232
2315
|
}
|
|
2233
2316
|
}
|
|
2234
2317
|
return found.sort();
|
|
@@ -2252,8 +2335,8 @@ async function runInstall(root, options = {}) {
|
|
|
2252
2335
|
}
|
|
2253
2336
|
}
|
|
2254
2337
|
async function hasReadyNodeModules(root) {
|
|
2255
|
-
const nodeModules =
|
|
2256
|
-
return await pathExists4(
|
|
2338
|
+
const nodeModules = path6.join(root, "node_modules");
|
|
2339
|
+
return await pathExists4(path6.join(nodeModules, ".modules.yaml")) || await pathExists4(path6.join(nodeModules, ".pnpm"));
|
|
2257
2340
|
}
|
|
2258
2341
|
function hydrationFsApi(overrides = {}) {
|
|
2259
2342
|
return { ...createPnpmFsApi(pathExists4), ...overrides };
|
|
@@ -2290,7 +2373,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
2290
2373
|
expectedHash: hash,
|
|
2291
2374
|
requireMarker: false
|
|
2292
2375
|
});
|
|
2293
|
-
if (!health.healthy && await fsApi.pathExists(
|
|
2376
|
+
if (!health.healthy && await fsApi.pathExists(path6.join(root, "node_modules"))) {
|
|
2294
2377
|
quarantine = await quarantineCanonicalNodeModules(root, health.issues, {
|
|
2295
2378
|
fsApi,
|
|
2296
2379
|
logger: options.logger
|
|
@@ -2302,7 +2385,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
2302
2385
|
}
|
|
2303
2386
|
const linkedWorkspaceDirs = [];
|
|
2304
2387
|
for (const relativeDir of workspaceDirs) {
|
|
2305
|
-
if (await pathExists4(
|
|
2388
|
+
if (await pathExists4(path6.join(root, relativeDir, "node_modules"))) {
|
|
2306
2389
|
linkedWorkspaceDirs.push(relativeDir);
|
|
2307
2390
|
}
|
|
2308
2391
|
}
|
|
@@ -2338,18 +2421,18 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
|
|
|
2338
2421
|
yieldEvery: Math.max(1, options.yieldEvery ?? DEFAULT_YIELD_EVERY2)
|
|
2339
2422
|
}
|
|
2340
2423
|
};
|
|
2341
|
-
recordOwnedNodeModulesRoot(dependencyOwnership,
|
|
2424
|
+
recordOwnedNodeModulesRoot(dependencyOwnership, path6.join(worktreeDir, "node_modules"));
|
|
2342
2425
|
await materializeNodeModulesForest(
|
|
2343
|
-
|
|
2344
|
-
|
|
2426
|
+
path6.join(root, "node_modules"),
|
|
2427
|
+
path6.join(worktreeDir, "node_modules"),
|
|
2345
2428
|
root,
|
|
2346
2429
|
worktreeDir,
|
|
2347
2430
|
materializeOptions
|
|
2348
2431
|
);
|
|
2349
2432
|
for (const relativeDir of hydration.linkedWorkspaceDirs) {
|
|
2350
|
-
const sourceNodeModules =
|
|
2351
|
-
const targetNodeModules =
|
|
2352
|
-
if (!await fsApi.pathExists(
|
|
2433
|
+
const sourceNodeModules = path6.join(root, relativeDir, "node_modules");
|
|
2434
|
+
const targetNodeModules = path6.join(worktreeDir, relativeDir, "node_modules");
|
|
2435
|
+
if (!await fsApi.pathExists(path6.join(worktreeDir, relativeDir))) continue;
|
|
2353
2436
|
recordOwnedNodeModulesRoot(dependencyOwnership, targetNodeModules);
|
|
2354
2437
|
await materializeNodeModulesForest(sourceNodeModules, targetNodeModules, root, worktreeDir, materializeOptions);
|
|
2355
2438
|
}
|
|
@@ -2387,28 +2470,28 @@ var init_pnpm_hydration = __esm({
|
|
|
2387
2470
|
|
|
2388
2471
|
// src/runner/worktree-paths.mjs
|
|
2389
2472
|
import { createHash as createHash3 } from "node:crypto";
|
|
2390
|
-
import
|
|
2473
|
+
import path7 from "node:path";
|
|
2391
2474
|
function samePath2(left, right) {
|
|
2392
|
-
const a =
|
|
2393
|
-
const b =
|
|
2475
|
+
const a = path7.resolve(left);
|
|
2476
|
+
const b = path7.resolve(right);
|
|
2394
2477
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
2395
2478
|
}
|
|
2396
2479
|
function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
|
|
2397
|
-
const canonicalRoot =
|
|
2480
|
+
const canonicalRoot = path7.resolve(root);
|
|
2398
2481
|
if (clonesRootDir) {
|
|
2399
|
-
const clonePool =
|
|
2400
|
-
if (samePath2(
|
|
2401
|
-
return
|
|
2482
|
+
const clonePool = path7.resolve(clonesRootDir);
|
|
2483
|
+
if (samePath2(path7.dirname(canonicalRoot), clonePool)) {
|
|
2484
|
+
return path7.join(clonePool, ".agent-worktrees", path7.basename(canonicalRoot));
|
|
2402
2485
|
}
|
|
2403
2486
|
}
|
|
2404
|
-
return
|
|
2487
|
+
return path7.join(canonicalRoot, ".agent-worktrees");
|
|
2405
2488
|
}
|
|
2406
2489
|
function worktreeDirForName(root, worktreeName, options = {}) {
|
|
2407
2490
|
const leaf = createHash3("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
|
|
2408
|
-
return
|
|
2491
|
+
return path7.join(worktreePoolForRoot(root, options), leaf);
|
|
2409
2492
|
}
|
|
2410
2493
|
function recoveryLedgerPathForRoot(root, options = {}) {
|
|
2411
|
-
return
|
|
2494
|
+
return path7.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
|
|
2412
2495
|
}
|
|
2413
2496
|
var init_worktree_paths = __esm({
|
|
2414
2497
|
"src/runner/worktree-paths.mjs"() {
|
|
@@ -2419,7 +2502,7 @@ var init_worktree_paths = __esm({
|
|
|
2419
2502
|
// src/runner/worktree-cleanup.mjs
|
|
2420
2503
|
import fs4 from "node:fs";
|
|
2421
2504
|
import fsp6 from "node:fs/promises";
|
|
2422
|
-
import
|
|
2505
|
+
import path8 from "node:path";
|
|
2423
2506
|
function stateFromEntry(entry) {
|
|
2424
2507
|
return {
|
|
2425
2508
|
root: entry.root,
|
|
@@ -2460,14 +2543,14 @@ function pruneSuccessfulStates(nowMs = Date.now()) {
|
|
|
2460
2543
|
function assertTrackedCleanupPath(entry) {
|
|
2461
2544
|
const poolRoot = worktreePoolForRoot(entry.root);
|
|
2462
2545
|
const expected = worktreeDirForName(entry.root, entry.worktreeName);
|
|
2463
|
-
const resolvedPool =
|
|
2464
|
-
const resolvedTarget =
|
|
2465
|
-
if (!resolvedTarget.startsWith(`${resolvedPool}${
|
|
2546
|
+
const resolvedPool = path8.resolve(poolRoot);
|
|
2547
|
+
const resolvedTarget = path8.resolve(entry.worktreeDir);
|
|
2548
|
+
if (!resolvedTarget.startsWith(`${resolvedPool}${path8.sep}`)) {
|
|
2466
2549
|
const error = new Error(`cleanup refused outside managed pool: ${entry.worktreeDir}`);
|
|
2467
2550
|
error.cleanupFatal = true;
|
|
2468
2551
|
throw error;
|
|
2469
2552
|
}
|
|
2470
|
-
if (resolvedTarget !==
|
|
2553
|
+
if (resolvedTarget !== path8.resolve(expected)) {
|
|
2471
2554
|
const error = new Error(`cleanup refused for unexpected tracked path: ${entry.worktreeDir}`);
|
|
2472
2555
|
error.cleanupFatal = true;
|
|
2473
2556
|
throw error;
|
|
@@ -2481,8 +2564,8 @@ async function worktreeStillRegistered2(root, worktreeDir, gitRunner) {
|
|
|
2481
2564
|
if (result.status !== 0) {
|
|
2482
2565
|
throw new Error(`git worktree list --porcelain failed during cleanup verification: ${summarizeProcessFailure(result)}`);
|
|
2483
2566
|
}
|
|
2484
|
-
const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) =>
|
|
2485
|
-
return registered.includes(
|
|
2567
|
+
const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path8.resolve(line.slice("worktree ".length).trim()));
|
|
2568
|
+
return registered.includes(path8.resolve(worktreeDir));
|
|
2486
2569
|
}
|
|
2487
2570
|
function cleanupBackoff(attempt) {
|
|
2488
2571
|
return 250 * attempt;
|
|
@@ -2592,7 +2675,7 @@ function scheduleTrackedCleanup(entry, options = {}) {
|
|
|
2592
2675
|
}
|
|
2593
2676
|
function pendingCleanupDirs() {
|
|
2594
2677
|
return new Set(
|
|
2595
|
-
[...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) =>
|
|
2678
|
+
[...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path8.resolve(state.worktreeDir))
|
|
2596
2679
|
);
|
|
2597
2680
|
}
|
|
2598
2681
|
var CLEANUP_STATES, CLEANUP_PROMISES, SUCCESS_HISTORY_LIMIT, SUCCESS_HISTORY_TTL_MS, DEFAULT_CLEANUP_ATTEMPTS;
|
|
@@ -2613,13 +2696,13 @@ var init_worktree_cleanup = __esm({
|
|
|
2613
2696
|
// src/runner/task-root-prepare.mjs
|
|
2614
2697
|
import fs5 from "node:fs";
|
|
2615
2698
|
import fsp7 from "node:fs/promises";
|
|
2616
|
-
import
|
|
2699
|
+
import path9 from "node:path";
|
|
2617
2700
|
function prepLockDir(root) {
|
|
2618
|
-
return
|
|
2701
|
+
return path9.join(root, ".agent-worktrees", "runner-root-prep.lock");
|
|
2619
2702
|
}
|
|
2620
2703
|
function readLockMeta(lockDir) {
|
|
2621
2704
|
try {
|
|
2622
|
-
return JSON.parse(fs5.readFileSync(
|
|
2705
|
+
return JSON.parse(fs5.readFileSync(path9.join(lockDir, "owner.json"), "utf8"));
|
|
2623
2706
|
} catch {
|
|
2624
2707
|
return null;
|
|
2625
2708
|
}
|
|
@@ -2639,9 +2722,9 @@ async function acquirePrepLock(root, options = {}) {
|
|
|
2639
2722
|
const staleMs = options.lockStaleMs ?? PREP_LOCK_STALE_MS;
|
|
2640
2723
|
const sleep3 = options.sleep || sleepMs;
|
|
2641
2724
|
const lockDir = prepLockDir(root);
|
|
2642
|
-
const ownerPath =
|
|
2725
|
+
const ownerPath = path9.join(lockDir, "owner.json");
|
|
2643
2726
|
const deadline = nowMs() + waitMs;
|
|
2644
|
-
fs5.mkdirSync(
|
|
2727
|
+
fs5.mkdirSync(path9.dirname(lockDir), { recursive: true });
|
|
2645
2728
|
for (; ; ) {
|
|
2646
2729
|
try {
|
|
2647
2730
|
fs5.mkdirSync(lockDir);
|
|
@@ -2691,16 +2774,16 @@ async function gitText(root, args, options = {}) {
|
|
|
2691
2774
|
}
|
|
2692
2775
|
function canonicalRecoveryDir(root, options = {}) {
|
|
2693
2776
|
const now = options.now || (() => /* @__PURE__ */ new Date());
|
|
2694
|
-
return
|
|
2695
|
-
options.managedPool ||
|
|
2777
|
+
return path9.join(
|
|
2778
|
+
options.managedPool || path9.join(root, ".agent-worktrees"),
|
|
2696
2779
|
".canonical-recovery",
|
|
2697
2780
|
`preexisting-${now().toISOString().replace(/[:.]/gu, "-")}`
|
|
2698
2781
|
);
|
|
2699
2782
|
}
|
|
2700
2783
|
function canonicalPath(root, relative) {
|
|
2701
|
-
const resolvedRoot =
|
|
2702
|
-
const target =
|
|
2703
|
-
const prefix = `${resolvedRoot}${
|
|
2784
|
+
const resolvedRoot = path9.resolve(root);
|
|
2785
|
+
const target = path9.resolve(root, relative);
|
|
2786
|
+
const prefix = `${resolvedRoot}${path9.sep}`;
|
|
2704
2787
|
if (!target.startsWith(prefix)) {
|
|
2705
2788
|
throw new Error(`canonical recovery path escaped the runner clone: ${relative}`);
|
|
2706
2789
|
}
|
|
@@ -2728,7 +2811,7 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
|
|
|
2728
2811
|
if (patchResult.status !== 0) {
|
|
2729
2812
|
throw new Error(`could not preserve canonical tracked changes: ${summarizeProcessFailure(patchResult)}`);
|
|
2730
2813
|
}
|
|
2731
|
-
await fsp7.writeFile(
|
|
2814
|
+
await fsp7.writeFile(path9.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
|
|
2732
2815
|
const symlinks = [];
|
|
2733
2816
|
for (const relative of paths.untracked) {
|
|
2734
2817
|
const source = canonicalPath(root, relative);
|
|
@@ -2740,13 +2823,13 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
|
|
|
2740
2823
|
if (!stat3.isFile()) {
|
|
2741
2824
|
throw new Error(`canonical recovery refuses unsupported untracked entry: ${relative}`);
|
|
2742
2825
|
}
|
|
2743
|
-
const target = canonicalPath(
|
|
2744
|
-
await fsp7.mkdir(
|
|
2826
|
+
const target = canonicalPath(path9.join(quarantineDir, "untracked"), relative);
|
|
2827
|
+
await fsp7.mkdir(path9.dirname(target), { recursive: true });
|
|
2745
2828
|
await fsp7.copyFile(source, target);
|
|
2746
2829
|
}
|
|
2747
|
-
await fsp7.writeFile(
|
|
2830
|
+
await fsp7.writeFile(path9.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
2748
2831
|
recoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2749
|
-
canonicalRoot:
|
|
2832
|
+
canonicalRoot: path9.resolve(root),
|
|
2750
2833
|
canonicalHead: headSha,
|
|
2751
2834
|
tracked: paths.tracked,
|
|
2752
2835
|
untracked: paths.untracked,
|
|
@@ -2818,11 +2901,11 @@ async function registeredWorktreeDirs(root, options = {}) {
|
|
|
2818
2901
|
throw new Error(`git worktree list --porcelain failed while checking managed residue: ${summarizeProcessFailure(listed)}`);
|
|
2819
2902
|
}
|
|
2820
2903
|
return new Set(
|
|
2821
|
-
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) =>
|
|
2904
|
+
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path9.resolve(line.slice("worktree ".length).trim()))
|
|
2822
2905
|
);
|
|
2823
2906
|
}
|
|
2824
2907
|
async function reportLegacyResiduals(root, options = {}) {
|
|
2825
|
-
const managedRoot =
|
|
2908
|
+
const managedRoot = path9.join(root, ".agent-worktrees");
|
|
2826
2909
|
if (!fs5.existsSync(managedRoot)) return [];
|
|
2827
2910
|
const registered = await registeredWorktreeDirs(root, options);
|
|
2828
2911
|
const pending = pendingCleanupDirs();
|
|
@@ -2830,7 +2913,7 @@ async function reportLegacyResiduals(root, options = {}) {
|
|
|
2830
2913
|
for (const entry of fs5.readdirSync(managedRoot, { withFileTypes: true })) {
|
|
2831
2914
|
if (!entry.isDirectory()) continue;
|
|
2832
2915
|
if (shouldIgnoreManagedEntry(entry.name)) continue;
|
|
2833
|
-
const absolute =
|
|
2916
|
+
const absolute = path9.resolve(path9.join(managedRoot, entry.name));
|
|
2834
2917
|
if (registered.has(absolute)) continue;
|
|
2835
2918
|
if (pending.has(absolute)) continue;
|
|
2836
2919
|
found.push(absolute);
|
|
@@ -2899,7 +2982,7 @@ var init_worktree_github_auth = __esm({
|
|
|
2899
2982
|
|
|
2900
2983
|
// src/runner/worktree-recovery-start.mjs
|
|
2901
2984
|
import fsp8 from "node:fs/promises";
|
|
2902
|
-
import
|
|
2985
|
+
import path10 from "node:path";
|
|
2903
2986
|
async function recordWorktreeStarted(worktreeTarget, meta = {}) {
|
|
2904
2987
|
if (!worktreeTarget?.worktreeName || !meta.taskId) return null;
|
|
2905
2988
|
const entry = {
|
|
@@ -2914,7 +2997,7 @@ async function recordWorktreeStarted(worktreeTarget, meta = {}) {
|
|
|
2914
2997
|
reason: "worktree allocated before execution"
|
|
2915
2998
|
};
|
|
2916
2999
|
const ledger = recoveryLedgerPathForRoot(worktreeTarget.root || process.cwd());
|
|
2917
|
-
await fsp8.mkdir(
|
|
3000
|
+
await fsp8.mkdir(path10.dirname(ledger), { recursive: true });
|
|
2918
3001
|
await fsp8.appendFile(ledger, `${JSON.stringify(entry)}
|
|
2919
3002
|
`, "utf8");
|
|
2920
3003
|
return { entry, ledger };
|
|
@@ -2929,7 +3012,7 @@ var init_worktree_recovery_start = __esm({
|
|
|
2929
3012
|
// src/runner/worktree-helper.mjs
|
|
2930
3013
|
import fs6 from "node:fs";
|
|
2931
3014
|
import fsp9 from "node:fs/promises";
|
|
2932
|
-
import
|
|
3015
|
+
import path11 from "node:path";
|
|
2933
3016
|
function repoRoot() {
|
|
2934
3017
|
return process.env.VO_CODE_RUNNER_REPO || process.cwd();
|
|
2935
3018
|
}
|
|
@@ -2945,7 +3028,7 @@ function cloneDirForSlug(repoSlug, clonesRootDir) {
|
|
|
2945
3028
|
const [owner, name] = String(repoSlug).split("/");
|
|
2946
3029
|
if (owner === "." || owner === ".." || name === "." || name === "..") return null;
|
|
2947
3030
|
if (owner.startsWith("-") || name.startsWith("-")) return null;
|
|
2948
|
-
return
|
|
3031
|
+
return path11.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
|
|
2949
3032
|
}
|
|
2950
3033
|
function cloneLockDir(dir) {
|
|
2951
3034
|
return `${dir}.clone-lock`;
|
|
@@ -2960,7 +3043,7 @@ async function pathExists5(target) {
|
|
|
2960
3043
|
}
|
|
2961
3044
|
async function readLockMeta2(lockDir) {
|
|
2962
3045
|
try {
|
|
2963
|
-
return JSON.parse(await fsp9.readFile(
|
|
3046
|
+
return JSON.parse(await fsp9.readFile(path11.join(lockDir, "owner.json"), "utf8"));
|
|
2964
3047
|
} catch {
|
|
2965
3048
|
return null;
|
|
2966
3049
|
}
|
|
@@ -2981,11 +3064,11 @@ async function acquireCloneLock(dir, options = {}) {
|
|
|
2981
3064
|
const sleep3 = options.sleep || sleepMs;
|
|
2982
3065
|
const lockDir = cloneLockDir(dir);
|
|
2983
3066
|
const deadline = nowMs() + waitMs;
|
|
2984
|
-
await fsp9.mkdir(
|
|
3067
|
+
await fsp9.mkdir(path11.dirname(lockDir), { recursive: true });
|
|
2985
3068
|
for (; ; ) {
|
|
2986
3069
|
try {
|
|
2987
3070
|
await fsp9.mkdir(lockDir);
|
|
2988
|
-
await fsp9.writeFile(
|
|
3071
|
+
await fsp9.writeFile(path11.join(lockDir, "owner.json"), `${JSON.stringify({
|
|
2989
3072
|
pid: process.pid,
|
|
2990
3073
|
createdAt: new Date(nowMs()).toISOString(),
|
|
2991
3074
|
dir
|
|
@@ -3014,7 +3097,7 @@ async function acquireCloneLock(dir, options = {}) {
|
|
|
3014
3097
|
}
|
|
3015
3098
|
}
|
|
3016
3099
|
async function isUsableGitClone(dir, runner = runProcess) {
|
|
3017
|
-
if (!await pathExists5(
|
|
3100
|
+
if (!await pathExists5(path11.join(dir, ".git"))) return false;
|
|
3018
3101
|
const result = await runner("git", ["-C", dir, "rev-parse", "HEAD"], { timeoutMs: 1e4 });
|
|
3019
3102
|
return result.status === 0 && Boolean(String(result.stdout || "").trim());
|
|
3020
3103
|
}
|
|
@@ -3035,7 +3118,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
3035
3118
|
const maxAttempts = options.maxAttempts || 5;
|
|
3036
3119
|
const raceWaitMs = options.raceWaitMs ?? 1e4;
|
|
3037
3120
|
let lastError = null;
|
|
3038
|
-
await fsp9.mkdir(
|
|
3121
|
+
await fsp9.mkdir(path11.dirname(dir), { recursive: true });
|
|
3039
3122
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
3040
3123
|
if (await pathExists5(dir)) {
|
|
3041
3124
|
if (await waitForUsableClone(dir, runner, sleep3, raceWaitMs)) return dir;
|
|
@@ -3047,7 +3130,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
3047
3130
|
["clone", "--no-tags", `https://github.com/${owner}/${name}.git`, tmpDir],
|
|
3048
3131
|
{ timeoutMs: 6e5, env: githubGitAuthEnv(options.githubToken) }
|
|
3049
3132
|
);
|
|
3050
|
-
if (clone.status !== 0 || !await pathExists5(
|
|
3133
|
+
if (clone.status !== 0 || !await pathExists5(path11.join(tmpDir, ".git"))) {
|
|
3051
3134
|
await fsp9.rm(tmpDir, { recursive: true, force: true });
|
|
3052
3135
|
lastError = new Error(`[vo-mcp runner] clone failed for ${repoSlug}: ${describeGitFailure(clone)}`);
|
|
3053
3136
|
continue;
|
|
@@ -3074,7 +3157,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
3074
3157
|
}
|
|
3075
3158
|
async function resolveTaskRoot(repoSlug, options = {}) {
|
|
3076
3159
|
const root = clonesRoot();
|
|
3077
|
-
if (root && !
|
|
3160
|
+
if (root && !path11.isAbsolute(root)) {
|
|
3078
3161
|
throw new Error(`[vo-mcp runner] VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${root}')`);
|
|
3079
3162
|
}
|
|
3080
3163
|
const dir = cloneDirForSlug(repoSlug, root);
|
|
@@ -3123,7 +3206,7 @@ async function createFixWorktree(kind, error = {}, options = {}) {
|
|
|
3123
3206
|
const dependencyOwnership = createDependencyOwnershipTracker(worktreeDir);
|
|
3124
3207
|
const prep = await prepare(root, {
|
|
3125
3208
|
recoverDirtyCanonical: multiRepo,
|
|
3126
|
-
managedPool:
|
|
3209
|
+
managedPool: path11.dirname(worktreeDir)
|
|
3127
3210
|
});
|
|
3128
3211
|
await processRunner("git", ["config", "core.longpaths", "true"], { cwd: root, timeoutMs: 3e4 });
|
|
3129
3212
|
const add = await addWorktree({ root, branchName, worktreeDir });
|
|
@@ -3189,7 +3272,7 @@ function preserveFailedWorktree(worktreeName, meta = {}) {
|
|
|
3189
3272
|
};
|
|
3190
3273
|
try {
|
|
3191
3274
|
const ledger = recoveryLedgerPathForRoot(root);
|
|
3192
|
-
fs6.mkdirSync(
|
|
3275
|
+
fs6.mkdirSync(path11.dirname(ledger), { recursive: true });
|
|
3193
3276
|
fs6.appendFileSync(ledger, `${JSON.stringify(entry)}
|
|
3194
3277
|
`, "utf8");
|
|
3195
3278
|
console.error(`[vo-mcp runner] Preserved worktree ${worktreeName} (reason: ${entry.reason})`);
|
|
@@ -3235,9 +3318,9 @@ function partitionRecoveryLedger(lines, { nowMs, ttlMs = PRESERVED_WORKTREE_TTL_
|
|
|
3235
3318
|
return decisions;
|
|
3236
3319
|
}
|
|
3237
3320
|
function isManagedPreservedDir(dir, root = repoRoot()) {
|
|
3238
|
-
const normalized =
|
|
3239
|
-
const pool =
|
|
3240
|
-
return normalized.startsWith(pool +
|
|
3321
|
+
const normalized = path11.resolve(String(dir || ""));
|
|
3322
|
+
const pool = path11.resolve(worktreePoolForRoot(root));
|
|
3323
|
+
return normalized.startsWith(pool + path11.sep);
|
|
3241
3324
|
}
|
|
3242
3325
|
function mergeAppendedSinceRead(originalRaw, currentRaw, keptLines) {
|
|
3243
3326
|
const originalSet = new Set(String(originalRaw || "").split(/\r?\n/u).map((l) => l.trim()).filter(Boolean));
|
|
@@ -3762,13 +3845,13 @@ async function getTaskKnowledgeContextRequest(req, taskId, { query } = {}, {
|
|
|
3762
3845
|
} = {}) {
|
|
3763
3846
|
const body = {};
|
|
3764
3847
|
if (typeof query === "string" && query.trim()) body.query = query;
|
|
3765
|
-
const
|
|
3848
|
+
const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/knowledge-context`;
|
|
3766
3849
|
const timeoutMs = Math.max(Number(taskRequestTimeoutMs) || 0, MIN_KNOWLEDGE_CONTEXT_TIMEOUT_MS);
|
|
3767
3850
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
3768
3851
|
let res;
|
|
3769
3852
|
let cause;
|
|
3770
3853
|
try {
|
|
3771
|
-
res = await req("POST",
|
|
3854
|
+
res = await req("POST", path23, body, { timeoutMs });
|
|
3772
3855
|
} catch (err) {
|
|
3773
3856
|
cause = err;
|
|
3774
3857
|
}
|
|
@@ -3835,10 +3918,10 @@ async function getPreparedJobRequest(req, taskId, options = {}, invalidateToken
|
|
|
3835
3918
|
if (typeof taskId !== "string" || taskId.length === 0) {
|
|
3836
3919
|
return { ok: false, reason: "missing_task_id", status: 0, ...envMeta };
|
|
3837
3920
|
}
|
|
3838
|
-
const
|
|
3921
|
+
const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/prepared-job?${query}`;
|
|
3839
3922
|
let res;
|
|
3840
3923
|
try {
|
|
3841
|
-
res = await req("GET",
|
|
3924
|
+
res = await req("GET", path23, void 0, { timeoutMs });
|
|
3842
3925
|
} catch (err) {
|
|
3843
3926
|
return { ok: false, reason: `transport: ${err?.message || String(err)}`, status: 0, ...envMeta };
|
|
3844
3927
|
}
|
|
@@ -3937,11 +4020,11 @@ function createControlPlaneClient({
|
|
|
3937
4020
|
const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
|
|
3938
4021
|
if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
3939
4022
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
3940
|
-
async function req(method,
|
|
4023
|
+
async function req(method, path23, body, { timeoutMs } = {}) {
|
|
3941
4024
|
const bearer = await resolveBearer(env2);
|
|
3942
4025
|
const controller = timeoutMs ? new AbortController() : null;
|
|
3943
4026
|
let timeoutId;
|
|
3944
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
4027
|
+
const request = Promise.resolve(fetchImpl(`${root}${path23}`, {
|
|
3945
4028
|
method,
|
|
3946
4029
|
headers: {
|
|
3947
4030
|
"content-type": "application/json",
|
|
@@ -3954,7 +4037,7 @@ function createControlPlaneClient({
|
|
|
3954
4037
|
const timeout = new Promise((_, reject) => {
|
|
3955
4038
|
timeoutId = setTimeout(() => {
|
|
3956
4039
|
controller.abort();
|
|
3957
|
-
reject(new Error(`control-plane ${
|
|
4040
|
+
reject(new Error(`control-plane ${path23} timed out after ${timeoutMs}ms`));
|
|
3958
4041
|
}, timeoutMs);
|
|
3959
4042
|
});
|
|
3960
4043
|
try {
|
|
@@ -3963,7 +4046,7 @@ function createControlPlaneClient({
|
|
|
3963
4046
|
clearTimeout(timeoutId);
|
|
3964
4047
|
}
|
|
3965
4048
|
}
|
|
3966
|
-
const taskReq = (method,
|
|
4049
|
+
const taskReq = (method, path23, body, options = {}) => req(method, path23, body, { timeoutMs: taskRequestTimeoutMs, ...options });
|
|
3967
4050
|
const claimGate = makeClaimGateNotice({ log: (m) => console.warn(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${m}`) });
|
|
3968
4051
|
return {
|
|
3969
4052
|
getClaimGate: () => claimGate.current(),
|
|
@@ -4107,8 +4190,8 @@ function createControlPlaneClient({
|
|
|
4107
4190
|
return listAllPrOpenedTasks(taskReq);
|
|
4108
4191
|
},
|
|
4109
4192
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
4110
|
-
const
|
|
4111
|
-
const res = await taskReq("GET",
|
|
4193
|
+
const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
4194
|
+
const res = await taskReq("GET", path23);
|
|
4112
4195
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
4113
4196
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
4114
4197
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -4266,7 +4349,7 @@ var init_control_plane_client = __esm({
|
|
|
4266
4349
|
|
|
4267
4350
|
// ../../scripts/virtual-office/code-runner/windows-claude-launch.mjs
|
|
4268
4351
|
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
4269
|
-
import { win32 as
|
|
4352
|
+
import { win32 as path12 } from "node:path";
|
|
4270
4353
|
import { spawnSync } from "node:child_process";
|
|
4271
4354
|
function pathValue(env2) {
|
|
4272
4355
|
for (const key of ["Path", "PATH", "path"]) {
|
|
@@ -4287,38 +4370,38 @@ function envValue(env2, name) {
|
|
|
4287
4370
|
function userClaudeCandidates(bin, env2) {
|
|
4288
4371
|
if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
|
|
4289
4372
|
const userProfile = envValue(env2, "USERPROFILE");
|
|
4290
|
-
const appData = envValue(env2, "APPDATA") || (userProfile ?
|
|
4291
|
-
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ?
|
|
4373
|
+
const appData = envValue(env2, "APPDATA") || (userProfile ? path12.join(userProfile, "AppData", "Roaming") : "");
|
|
4374
|
+
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path12.join(userProfile, "AppData", "Local") : "");
|
|
4292
4375
|
const candidates = [];
|
|
4293
4376
|
if (appData) {
|
|
4294
|
-
const npmBin =
|
|
4377
|
+
const npmBin = path12.join(appData, "npm");
|
|
4295
4378
|
candidates.push(
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4379
|
+
path12.join(npmBin, "claude.exe"),
|
|
4380
|
+
path12.join(npmBin, "claude.cmd"),
|
|
4381
|
+
path12.join(npmBin, "claude.ps1"),
|
|
4382
|
+
path12.join(npmBin, "claude"),
|
|
4383
|
+
path12.join(npmBin, ...NATIVE_CLAUDE_PARTS)
|
|
4301
4384
|
);
|
|
4302
4385
|
}
|
|
4303
|
-
if (userProfile) candidates.push(
|
|
4386
|
+
if (userProfile) candidates.push(path12.join(userProfile, ".local", "bin", "claude.exe"));
|
|
4304
4387
|
if (localAppData) {
|
|
4305
4388
|
candidates.push(
|
|
4306
|
-
|
|
4307
|
-
|
|
4389
|
+
path12.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
|
|
4390
|
+
path12.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
|
|
4308
4391
|
);
|
|
4309
4392
|
}
|
|
4310
4393
|
return candidates;
|
|
4311
4394
|
}
|
|
4312
4395
|
function pathCandidates(bin, env2) {
|
|
4313
|
-
if (
|
|
4314
|
-
return [
|
|
4315
|
-
}
|
|
4316
|
-
const extension =
|
|
4317
|
-
const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4396
|
+
if (path12.isAbsolute(bin) || /[\\/]/u.test(bin)) {
|
|
4397
|
+
return [path12.resolve(bin)];
|
|
4398
|
+
}
|
|
4399
|
+
const extension = path12.extname(bin);
|
|
4400
|
+
const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path12.join(directory, bin)] : [
|
|
4401
|
+
path12.join(directory, `${bin}.exe`),
|
|
4402
|
+
path12.join(directory, `${bin}.cmd`),
|
|
4403
|
+
path12.join(directory, `${bin}.ps1`),
|
|
4404
|
+
path12.join(directory, bin)
|
|
4322
4405
|
]);
|
|
4323
4406
|
const seen = /* @__PURE__ */ new Set();
|
|
4324
4407
|
return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
|
|
@@ -4349,8 +4432,8 @@ function resolveWindowsClaudeExecutable({
|
|
|
4349
4432
|
for (const candidate of pathCandidates(requested, env2)) {
|
|
4350
4433
|
const found = canonicalExistingPath(candidate, exists, canonicalize);
|
|
4351
4434
|
if (!found) continue;
|
|
4352
|
-
if (
|
|
4353
|
-
const native =
|
|
4435
|
+
if (path12.extname(found).toLowerCase() === ".exe") return found;
|
|
4436
|
+
const native = path12.join(path12.dirname(found), ...NATIVE_CLAUDE_PARTS);
|
|
4354
4437
|
const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
|
|
4355
4438
|
if (resolvedNative) return resolvedNative;
|
|
4356
4439
|
}
|
|
@@ -4847,12 +4930,12 @@ var init_terminal_process_cleanup = __esm({
|
|
|
4847
4930
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4848
4931
|
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4849
4932
|
import os from "node:os";
|
|
4850
|
-
import
|
|
4933
|
+
import path13 from "node:path";
|
|
4851
4934
|
function registryRoot(tmp = os.tmpdir()) {
|
|
4852
|
-
return
|
|
4935
|
+
return path13.join(tmp, REGISTRY_ROOT_NAME);
|
|
4853
4936
|
}
|
|
4854
4937
|
function instanceDir(root, instanceId) {
|
|
4855
|
-
return
|
|
4938
|
+
return path13.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
|
|
4856
4939
|
}
|
|
4857
4940
|
function registerDaemonInstance({
|
|
4858
4941
|
root = registryRoot(),
|
|
@@ -4863,7 +4946,7 @@ function registerDaemonInstance({
|
|
|
4863
4946
|
if (!instanceId) return null;
|
|
4864
4947
|
const dir = instanceDir(root, instanceId);
|
|
4865
4948
|
mkdirSync7(dir, { recursive: true });
|
|
4866
|
-
const file =
|
|
4949
|
+
const file = path13.join(dir, DAEMON_RECORD);
|
|
4867
4950
|
writeFileSync5(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
|
|
4868
4951
|
encoding: "utf8",
|
|
4869
4952
|
mode: 384
|
|
@@ -4882,7 +4965,7 @@ function recordAgentPid({
|
|
|
4882
4965
|
const dir = instanceDir(root, instanceId);
|
|
4883
4966
|
mkdirSync7(dir, { recursive: true });
|
|
4884
4967
|
writeFileSync5(
|
|
4885
|
-
|
|
4968
|
+
path13.join(dir, `${pid}.json`),
|
|
4886
4969
|
JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
|
|
4887
4970
|
{ encoding: "utf8", mode: 384 }
|
|
4888
4971
|
);
|
|
@@ -4898,7 +4981,7 @@ function unrecordAgentPid({
|
|
|
4898
4981
|
} = {}) {
|
|
4899
4982
|
if (!instanceId || !Number.isInteger(pid)) return false;
|
|
4900
4983
|
try {
|
|
4901
|
-
rmSync2(
|
|
4984
|
+
rmSync2(path13.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
|
|
4902
4985
|
return true;
|
|
4903
4986
|
} catch {
|
|
4904
4987
|
return false;
|
|
@@ -4926,7 +5009,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
4926
5009
|
for (const dirent of dirents) {
|
|
4927
5010
|
if (!dirent.isDirectory()) continue;
|
|
4928
5011
|
if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
|
|
4929
|
-
const dir =
|
|
5012
|
+
const dir = path13.join(root, dirent.name);
|
|
4930
5013
|
let daemon = null;
|
|
4931
5014
|
const agents = [];
|
|
4932
5015
|
let files;
|
|
@@ -4938,7 +5021,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
4938
5021
|
for (const name of files) {
|
|
4939
5022
|
let parsed;
|
|
4940
5023
|
try {
|
|
4941
|
-
parsed = JSON.parse(readFileSync7(
|
|
5024
|
+
parsed = JSON.parse(readFileSync7(path13.join(dir, name), "utf8"));
|
|
4942
5025
|
} catch {
|
|
4943
5026
|
continue;
|
|
4944
5027
|
}
|
|
@@ -4986,7 +5069,7 @@ function windowsSystemRoot(env2 = process.env) {
|
|
|
4986
5069
|
return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
4987
5070
|
}
|
|
4988
5071
|
function windowsPowershellExe(env2 = process.env) {
|
|
4989
|
-
return
|
|
5072
|
+
return path13.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
4990
5073
|
}
|
|
4991
5074
|
function listProcessCreationTimes({ platform: platform4 = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
|
|
4992
5075
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -5031,7 +5114,7 @@ function parsePosixPsLine(line) {
|
|
|
5031
5114
|
function killProcessTree(pid, { platform: platform4 = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
|
|
5032
5115
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
5033
5116
|
if (platform4 === "win32") {
|
|
5034
|
-
const taskkill =
|
|
5117
|
+
const taskkill = path13.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
|
|
5035
5118
|
const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
5036
5119
|
return !r.error && r.status === 0;
|
|
5037
5120
|
}
|
|
@@ -6937,9 +7020,9 @@ var init_rate_limit_detector_core = __esm({
|
|
|
6937
7020
|
|
|
6938
7021
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
|
|
6939
7022
|
import fsp10 from "node:fs/promises";
|
|
6940
|
-
import
|
|
7023
|
+
import path14 from "node:path";
|
|
6941
7024
|
async function atomicWrite(file, content) {
|
|
6942
|
-
await fsp10.mkdir(
|
|
7025
|
+
await fsp10.mkdir(path14.dirname(file), { recursive: true });
|
|
6943
7026
|
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
6944
7027
|
const handle = await fsp10.open(temp, "wx");
|
|
6945
7028
|
try {
|
|
@@ -7000,7 +7083,7 @@ function writeResumeAttempts(file, store) {
|
|
|
7000
7083
|
}
|
|
7001
7084
|
async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
|
|
7002
7085
|
const deadline = now() + LOCK_WAIT_MS;
|
|
7003
|
-
await fsp10.mkdir(
|
|
7086
|
+
await fsp10.mkdir(path14.dirname(lockFile), { recursive: true });
|
|
7004
7087
|
for (; ; ) {
|
|
7005
7088
|
let handle;
|
|
7006
7089
|
try {
|
|
@@ -7458,14 +7541,14 @@ function parsePorcelainZ(out) {
|
|
|
7458
7541
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
7459
7542
|
const token2 = tokens[i];
|
|
7460
7543
|
if (!token2) continue;
|
|
7461
|
-
const
|
|
7462
|
-
if (
|
|
7544
|
+
const path23 = token2.slice(3);
|
|
7545
|
+
if (path23) files.push(path23);
|
|
7463
7546
|
if (token2[0] === "R" || token2[0] === "C") i += 1;
|
|
7464
7547
|
}
|
|
7465
7548
|
return files;
|
|
7466
7549
|
}
|
|
7467
|
-
function isAgentScratch(
|
|
7468
|
-
const normalized = String(
|
|
7550
|
+
function isAgentScratch(path23) {
|
|
7551
|
+
const normalized = String(path23 || "");
|
|
7469
7552
|
return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
7470
7553
|
}
|
|
7471
7554
|
var SCRATCH_PATTERNS;
|
|
@@ -7722,7 +7805,7 @@ var init_executor = __esm({
|
|
|
7722
7805
|
|
|
7723
7806
|
// ../../scripts/virtual-office/code-runner/test-gen-gate.mjs
|
|
7724
7807
|
import fs7 from "node:fs";
|
|
7725
|
-
import
|
|
7808
|
+
import path15 from "node:path";
|
|
7726
7809
|
async function postFailed(client, id, message, result) {
|
|
7727
7810
|
try {
|
|
7728
7811
|
await client.postProgress(id, {
|
|
@@ -7758,7 +7841,7 @@ async function gateTestGenTaskOrFail({ client, id, task, files, worktreeDir, env
|
|
|
7758
7841
|
}
|
|
7759
7842
|
let testSource = "";
|
|
7760
7843
|
try {
|
|
7761
|
-
testSource = fs7.readFileSync(
|
|
7844
|
+
testSource = fs7.readFileSync(path15.join(worktreeDir, testFile), "utf8");
|
|
7762
7845
|
} catch (err) {
|
|
7763
7846
|
await postFailed(client, id, `could not read generated test ${testFile}: ${err.message}`, "gate_test_unreadable");
|
|
7764
7847
|
return true;
|
|
@@ -7808,7 +7891,7 @@ var init_test_gen_gate = __esm({
|
|
|
7808
7891
|
// ../../scripts/virtual-office/code-runner/completion-gate.mjs
|
|
7809
7892
|
import { execFile } from "node:child_process";
|
|
7810
7893
|
import fs8 from "node:fs";
|
|
7811
|
-
import
|
|
7894
|
+
import path16 from "node:path";
|
|
7812
7895
|
function resolveCompletionGate(task) {
|
|
7813
7896
|
const raw = task?.completion_gate;
|
|
7814
7897
|
if (raw === void 0 || raw === null) return null;
|
|
@@ -7846,14 +7929,14 @@ function workspaceFingerprint(worktreeDir, execFileImpl = execFile) {
|
|
|
7846
7929
|
}
|
|
7847
7930
|
function readState(worktreeDir) {
|
|
7848
7931
|
try {
|
|
7849
|
-
return JSON.parse(fs8.readFileSync(
|
|
7932
|
+
return JSON.parse(fs8.readFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
|
|
7850
7933
|
} catch {
|
|
7851
7934
|
return null;
|
|
7852
7935
|
}
|
|
7853
7936
|
}
|
|
7854
7937
|
function writeState(worktreeDir, state) {
|
|
7855
7938
|
try {
|
|
7856
|
-
fs8.writeFileSync(
|
|
7939
|
+
fs8.writeFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
|
|
7857
7940
|
`, "utf8");
|
|
7858
7941
|
} catch {
|
|
7859
7942
|
}
|
|
@@ -9441,7 +9524,7 @@ var init_task_prompt = __esm({
|
|
|
9441
9524
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
9442
9525
|
import { chmod, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
9443
9526
|
import os2 from "node:os";
|
|
9444
|
-
import
|
|
9527
|
+
import path17 from "node:path";
|
|
9445
9528
|
function safeTaskToken(taskId) {
|
|
9446
9529
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
9447
9530
|
}
|
|
@@ -9454,9 +9537,9 @@ function hasGeneratedPrefix(name) {
|
|
|
9454
9537
|
return name.startsWith(DIRECTORY_PREFIX) || name.startsWith(LEGACY_DIRECTORY_PREFIX);
|
|
9455
9538
|
}
|
|
9456
9539
|
function assertGeneratedDirectory(directory, containmentRoot) {
|
|
9457
|
-
const resolvedDirectory =
|
|
9458
|
-
const resolvedRoot =
|
|
9459
|
-
if (
|
|
9540
|
+
const resolvedDirectory = path17.resolve(directory);
|
|
9541
|
+
const resolvedRoot = path17.resolve(containmentRoot);
|
|
9542
|
+
if (path17.dirname(resolvedDirectory) !== resolvedRoot || !hasGeneratedPrefix(path17.basename(resolvedDirectory))) {
|
|
9460
9543
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
9461
9544
|
}
|
|
9462
9545
|
return resolvedDirectory;
|
|
@@ -9465,7 +9548,7 @@ async function resolveContainmentRoot(worktreeDir) {
|
|
|
9465
9548
|
if (typeof worktreeDir !== "string" || !worktreeDir.trim()) {
|
|
9466
9549
|
throw new Error("refusing to materialize task attachments outside an agent-readable worktree: no worktreeDir given");
|
|
9467
9550
|
}
|
|
9468
|
-
const root =
|
|
9551
|
+
const root = path17.resolve(worktreeDir);
|
|
9469
9552
|
const stats = await stat(root).catch(() => null);
|
|
9470
9553
|
if (!stats?.isDirectory()) {
|
|
9471
9554
|
throw new Error(`refusing to materialize task attachments: agent worktree root is not a directory (${root})`);
|
|
@@ -9474,15 +9557,15 @@ async function resolveContainmentRoot(worktreeDir) {
|
|
|
9474
9557
|
}
|
|
9475
9558
|
async function createAttachmentDirectory(taskId, containmentRoot) {
|
|
9476
9559
|
const root = await resolveContainmentRoot(containmentRoot);
|
|
9477
|
-
const directory = await mkdtemp(
|
|
9560
|
+
const directory = await mkdtemp(path17.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
9478
9561
|
const [realRoot, realDirectory] = await Promise.all([realpath(root), realpath(directory)]);
|
|
9479
|
-
if (
|
|
9562
|
+
if (path17.dirname(realDirectory) !== realRoot) {
|
|
9480
9563
|
await rm(directory, { recursive: true, force: true }).catch(() => void 0);
|
|
9481
9564
|
throw new Error("task-attachment directory escaped the agent worktree root");
|
|
9482
9565
|
}
|
|
9483
|
-
await writeFile(
|
|
9484
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory:
|
|
9485
|
-
await writeFile(
|
|
9566
|
+
await writeFile(path17.join(directory, GITIGNORE_FILE), GITIGNORE_BODY, { encoding: "utf8", mode: 384 });
|
|
9567
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory: path17.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
9568
|
+
await writeFile(path17.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
9486
9569
|
return { directory, marker, root, cleaned: false };
|
|
9487
9570
|
}
|
|
9488
9571
|
async function cleanupGeneratedDirectory(state) {
|
|
@@ -9496,7 +9579,7 @@ async function cleanupGeneratedDirectory(state) {
|
|
|
9496
9579
|
state.cleaned = true;
|
|
9497
9580
|
return;
|
|
9498
9581
|
}
|
|
9499
|
-
const marker = await readFile(
|
|
9582
|
+
const marker = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
9500
9583
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
9501
9584
|
await rm(directory, { recursive: true, force: true });
|
|
9502
9585
|
state.cleaned = true;
|
|
@@ -9515,7 +9598,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
9515
9598
|
now = Date.now(),
|
|
9516
9599
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
9517
9600
|
} = {}) {
|
|
9518
|
-
const root =
|
|
9601
|
+
const root = path17.resolve(tempRoot);
|
|
9519
9602
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
9520
9603
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
9521
9604
|
if (error?.code === "ENOENT") return [];
|
|
@@ -9524,8 +9607,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
9524
9607
|
let removed = 0;
|
|
9525
9608
|
for (const entry of entries) {
|
|
9526
9609
|
if (!entry.isDirectory() || !hasGeneratedPrefix(entry.name)) continue;
|
|
9527
|
-
const directory = assertGeneratedDirectory(
|
|
9528
|
-
const markerRaw = await readFile(
|
|
9610
|
+
const directory = assertGeneratedDirectory(path17.join(root, entry.name), root);
|
|
9611
|
+
const markerRaw = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
9529
9612
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
9530
9613
|
if (!marker) continue;
|
|
9531
9614
|
const directoryStat = await stat(directory);
|
|
@@ -9582,8 +9665,8 @@ async function materializeTaskAttachments(client, task, { worktreeDir } = {}) {
|
|
|
9582
9665
|
const sha2562 = createHash4("sha256").update(content).digest("hex");
|
|
9583
9666
|
if (sha2562 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
9584
9667
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
9585
|
-
const filePath =
|
|
9586
|
-
if (
|
|
9668
|
+
const filePath = path17.resolve(state.directory, name);
|
|
9669
|
+
if (path17.dirname(filePath) !== state.directory) throw new Error(`attachment ${ref.attachment_id} resolved outside its task directory`);
|
|
9587
9670
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
9588
9671
|
await chmod(filePath, 384);
|
|
9589
9672
|
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256: sha2562, path: filePath });
|
|
@@ -9668,9 +9751,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
9668
9751
|
}
|
|
9669
9752
|
return out;
|
|
9670
9753
|
}
|
|
9671
|
-
async function readCloudMap(
|
|
9754
|
+
async function readCloudMap(path23) {
|
|
9672
9755
|
try {
|
|
9673
|
-
return JSON.parse(await readFile2(
|
|
9756
|
+
return JSON.parse(await readFile2(path23, "utf8"));
|
|
9674
9757
|
} catch {
|
|
9675
9758
|
return {};
|
|
9676
9759
|
}
|
|
@@ -9971,9 +10054,9 @@ function backoffMs(streak, baseMs) {
|
|
|
9971
10054
|
if (streak <= 0) return 0;
|
|
9972
10055
|
return Math.min(baseMs * 2 ** Math.min(streak - 1, 20), MAX_BACKOFF_MS);
|
|
9973
10056
|
}
|
|
9974
|
-
async function loadState(
|
|
10057
|
+
async function loadState(path23) {
|
|
9975
10058
|
try {
|
|
9976
|
-
const parsed = JSON.parse(await readFile3(
|
|
10059
|
+
const parsed = JSON.parse(await readFile3(path23, "utf8"));
|
|
9977
10060
|
if (parsed && typeof parsed === "object" && Number.isInteger(parsed.byte_offset) && parsed.byte_offset >= 0) {
|
|
9978
10061
|
return { ...parsed, byte_offset: parsed.byte_offset };
|
|
9979
10062
|
}
|
|
@@ -9981,15 +10064,15 @@ async function loadState(path22) {
|
|
|
9981
10064
|
}
|
|
9982
10065
|
return { byte_offset: 0, last_event_id: null, forwarded_total: 0, rejected_total: 0, rejected_event_ids: [] };
|
|
9983
10066
|
}
|
|
9984
|
-
async function saveState(
|
|
9985
|
-
await mkdir2(dirname9(
|
|
9986
|
-
await writeFile3(
|
|
10067
|
+
async function saveState(path23, state) {
|
|
10068
|
+
await mkdir2(dirname9(path23), { recursive: true });
|
|
10069
|
+
await writeFile3(path23, JSON.stringify(state, null, 2), "utf8");
|
|
9987
10070
|
}
|
|
9988
|
-
async function readNewBytes(
|
|
9989
|
-
const st = await stat2(
|
|
10071
|
+
async function readNewBytes(path23, offset, max) {
|
|
10072
|
+
const st = await stat2(path23);
|
|
9990
10073
|
if (st.size <= offset) return { buf: Buffer.alloc(0), size: st.size };
|
|
9991
10074
|
const length = Math.min(st.size - offset, max);
|
|
9992
|
-
const fh = await open(
|
|
10075
|
+
const fh = await open(path23, "r");
|
|
9993
10076
|
try {
|
|
9994
10077
|
const buf = Buffer.alloc(length);
|
|
9995
10078
|
const { bytesRead } = await fh.read(buf, 0, length, offset);
|
|
@@ -11082,10 +11165,10 @@ function formatShadowLogLine(record) {
|
|
|
11082
11165
|
const loud = record.unexplained_fields?.length ? "!! " : "";
|
|
11083
11166
|
return `${loud}[prepared-job-shadow] ${parts.join(" ")}`;
|
|
11084
11167
|
}
|
|
11085
|
-
function appendShadowRecord(record, { path:
|
|
11168
|
+
function appendShadowRecord(record, { path: path23 = PREPARED_JOB_SHADOW_SINK, append = appendFileSync, mkdir: mkdir5 = mkdirSync8 } = {}) {
|
|
11086
11169
|
try {
|
|
11087
|
-
mkdir5(dirname10(
|
|
11088
|
-
append(
|
|
11170
|
+
mkdir5(dirname10(path23), { recursive: true });
|
|
11171
|
+
append(path23, `${JSON.stringify(record)}
|
|
11089
11172
|
`, "utf8");
|
|
11090
11173
|
return true;
|
|
11091
11174
|
} catch {
|
|
@@ -11405,7 +11488,7 @@ var init_shared = __esm({
|
|
|
11405
11488
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
11406
11489
|
import fs10 from "node:fs";
|
|
11407
11490
|
import os3 from "node:os";
|
|
11408
|
-
import
|
|
11491
|
+
import path18 from "node:path";
|
|
11409
11492
|
function fileCaptureTime(filePath, explicit, statFn) {
|
|
11410
11493
|
if (typeof explicit === "string" && explicit) return explicit;
|
|
11411
11494
|
try {
|
|
@@ -11419,7 +11502,7 @@ function usageBaseUrl(env2 = process.env) {
|
|
|
11419
11502
|
return String(raw).replace(/\/+$/, "");
|
|
11420
11503
|
}
|
|
11421
11504
|
function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.now() } = {}) {
|
|
11422
|
-
const creds = read(
|
|
11505
|
+
const creds = read(path18.join(homeDir, ".claude", ".credentials.json"));
|
|
11423
11506
|
const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
|
|
11424
11507
|
if (!oauth || typeof oauth !== "object") return null;
|
|
11425
11508
|
const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
|
|
@@ -11429,7 +11512,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.
|
|
|
11429
11512
|
return token2;
|
|
11430
11513
|
}
|
|
11431
11514
|
function readAccountId({ homeDir = os3.homedir(), read = readJson2 } = {}) {
|
|
11432
|
-
const cfg = read(
|
|
11515
|
+
const cfg = read(path18.join(homeDir, ".claude.json"));
|
|
11433
11516
|
const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
|
|
11434
11517
|
return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
|
|
11435
11518
|
}
|
|
@@ -11539,7 +11622,7 @@ function readClaudeFileUsage({
|
|
|
11539
11622
|
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
11540
11623
|
return row;
|
|
11541
11624
|
};
|
|
11542
|
-
const statusPath =
|
|
11625
|
+
const statusPath = path18.join(homeDir, ".claude", "claude-usage.json");
|
|
11543
11626
|
const status = read(statusPath);
|
|
11544
11627
|
if (status && (status.seven_day || status.five_hour)) {
|
|
11545
11628
|
const row = fresh(makeUsageRow({
|
|
@@ -11554,7 +11637,7 @@ function readClaudeFileUsage({
|
|
|
11554
11637
|
}));
|
|
11555
11638
|
if (row) return row;
|
|
11556
11639
|
}
|
|
11557
|
-
const weeklyPath =
|
|
11640
|
+
const weeklyPath = path18.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
11558
11641
|
const weekly = read(weeklyPath);
|
|
11559
11642
|
if (weekly) {
|
|
11560
11643
|
const row = fresh(makeUsageRow({
|
|
@@ -12500,7 +12583,7 @@ function noteCiViaRest(log2) {
|
|
|
12500
12583
|
log2("watch: CI status read via REST check-runs/status (gh's GraphQL rollup needs actions:read for checkSuite.workflowRun, which the read scope does not carry)");
|
|
12501
12584
|
}
|
|
12502
12585
|
async function readCommitCiViaRest(repo, sha, { run, env: env2 }) {
|
|
12503
|
-
const api = async (
|
|
12586
|
+
const api = async (path23) => JSON.parse(await run("gh", ["api", path23], { timeout: 3e4, env: env2 }) || "{}");
|
|
12504
12587
|
const rollup = [];
|
|
12505
12588
|
let total = null;
|
|
12506
12589
|
for (let page = 1; page <= REST_MAX_PAGES && (total === null || rollup.length < total); page += 1) {
|
|
@@ -13164,9 +13247,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
13164
13247
|
res.end();
|
|
13165
13248
|
return;
|
|
13166
13249
|
}
|
|
13167
|
-
const
|
|
13250
|
+
const path23 = String(req.url || "").split("?")[0];
|
|
13168
13251
|
res.setHeader("content-type", "application/json");
|
|
13169
|
-
if (req.method === "GET" &&
|
|
13252
|
+
if (req.method === "GET" && path23 === "/status") {
|
|
13170
13253
|
let status;
|
|
13171
13254
|
try {
|
|
13172
13255
|
status = getStatus();
|
|
@@ -13177,7 +13260,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
13177
13260
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
13178
13261
|
return;
|
|
13179
13262
|
}
|
|
13180
|
-
if (req.method === "POST" &&
|
|
13263
|
+
if (req.method === "POST" && path23 === "/stop") {
|
|
13181
13264
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
13182
13265
|
res.statusCode = 403;
|
|
13183
13266
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -13371,22 +13454,22 @@ var init_effort_mode_config = __esm({
|
|
|
13371
13454
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
13372
13455
|
import fs11 from "node:fs";
|
|
13373
13456
|
import os4 from "node:os";
|
|
13374
|
-
import
|
|
13457
|
+
import path19 from "node:path";
|
|
13375
13458
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
13376
13459
|
function userCacheRoot() {
|
|
13377
13460
|
try {
|
|
13378
13461
|
const home = os4.homedir();
|
|
13379
|
-
if (home) return
|
|
13462
|
+
if (home) return path19.join(home, ".claude");
|
|
13380
13463
|
} catch {
|
|
13381
13464
|
}
|
|
13382
|
-
return
|
|
13465
|
+
return path19.join(os4.tmpdir(), `vo-model-registry-${randomUUID6()}`);
|
|
13383
13466
|
}
|
|
13384
13467
|
function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
|
|
13385
13468
|
if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
|
|
13386
13469
|
if (env2.VO_RUNNER_RUNTIME_ROOT) return env2.VO_RUNNER_RUNTIME_ROOT;
|
|
13387
|
-
const segments = moduleDir.split(
|
|
13470
|
+
const segments = moduleDir.split(path19.sep);
|
|
13388
13471
|
const isRepoCheckout2 = segments.at(-1) === "virtual-office" && segments.at(-2) === "scripts";
|
|
13389
|
-
return isRepoCheckout2 ?
|
|
13472
|
+
return isRepoCheckout2 ? path19.resolve(moduleDir, "..", "..") : userCacheRoot();
|
|
13390
13473
|
}
|
|
13391
13474
|
function uniqueModels(models = []) {
|
|
13392
13475
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
@@ -13509,7 +13592,7 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
13509
13592
|
}
|
|
13510
13593
|
}
|
|
13511
13594
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
13512
|
-
fs11.mkdirSync(
|
|
13595
|
+
fs11.mkdirSync(path19.dirname(cacheFile), { recursive: true });
|
|
13513
13596
|
fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
13514
13597
|
}
|
|
13515
13598
|
async function fetchRegistryCatalog({
|
|
@@ -13567,13 +13650,13 @@ var __dirname, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC
|
|
|
13567
13650
|
var init_model_registry = __esm({
|
|
13568
13651
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
13569
13652
|
"use strict";
|
|
13570
|
-
__dirname =
|
|
13571
|
-
DEFAULT_CACHE_DIR =
|
|
13653
|
+
__dirname = path19.dirname(fileURLToPath6(import.meta.url));
|
|
13654
|
+
DEFAULT_CACHE_DIR = path19.join(
|
|
13572
13655
|
resolveCacheBaseDir(),
|
|
13573
13656
|
".virtual-office-cache",
|
|
13574
13657
|
"model-registry"
|
|
13575
13658
|
);
|
|
13576
|
-
DEFAULT_CACHE_FILE =
|
|
13659
|
+
DEFAULT_CACHE_FILE = path19.join(DEFAULT_CACHE_DIR, "catalog.json");
|
|
13577
13660
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
13578
13661
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
13579
13662
|
FAMILY_DEFINITIONS = {
|
|
@@ -14212,9 +14295,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
14212
14295
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
14213
14296
|
return base;
|
|
14214
14297
|
}
|
|
14215
|
-
function readCodexModelsCache({ path:
|
|
14298
|
+
function readCodexModelsCache({ path: path23 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync9 } = {}) {
|
|
14216
14299
|
try {
|
|
14217
|
-
const parsed = JSON.parse(read(
|
|
14300
|
+
const parsed = JSON.parse(read(path23, "utf8"));
|
|
14218
14301
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
14219
14302
|
} catch {
|
|
14220
14303
|
return null;
|
|
@@ -14471,15 +14554,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
14471
14554
|
const s = `[${decision.routerVersion}] ${decision.taskClass} d=${decision.difficulty} c=${decision.confidence} \u2192 ${decision.rung}/${decision.tier}${decision.effort ? ` effort=${decision.effort}` : ""} turns=${decision.maxTurns} $${decision.maxBudgetUsd}${decision.flags.length ? ` [${decision.flags.join(",")}]` : ""} :: ${decision.reasons.join("; ")}`;
|
|
14472
14555
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
14473
14556
|
}
|
|
14474
|
-
function appendDecisionFallback(decision, { path:
|
|
14557
|
+
function appendDecisionFallback(decision, { path: path23 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir5 = mkdirSync9, task, thresholds, roleCostInputs } = {}) {
|
|
14475
14558
|
try {
|
|
14476
|
-
mkdir5(dirname12(
|
|
14477
|
-
append(
|
|
14559
|
+
mkdir5(dirname12(path23), { recursive: true });
|
|
14560
|
+
append(path23, `${JSON.stringify(decision)}
|
|
14478
14561
|
`, "utf8");
|
|
14479
14562
|
if (isRouterDecision(decision)) {
|
|
14480
14563
|
try {
|
|
14481
14564
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
14482
|
-
for (const record of records) append(
|
|
14565
|
+
for (const record of records) append(path23, `${JSON.stringify(record)}
|
|
14483
14566
|
`, "utf8");
|
|
14484
14567
|
} catch {
|
|
14485
14568
|
}
|
|
@@ -15533,7 +15616,7 @@ var init_inference_task_runner = __esm({
|
|
|
15533
15616
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
15534
15617
|
import fs12 from "node:fs";
|
|
15535
15618
|
import fsp11 from "node:fs/promises";
|
|
15536
|
-
import
|
|
15619
|
+
import path20 from "node:path";
|
|
15537
15620
|
async function defaultRun3(command, args, cwd, options = {}) {
|
|
15538
15621
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
15539
15622
|
}
|
|
@@ -15546,7 +15629,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
15546
15629
|
"--path-format=absolute",
|
|
15547
15630
|
"--git-common-dir"
|
|
15548
15631
|
])).trim();
|
|
15549
|
-
const root =
|
|
15632
|
+
const root = path20.dirname(commonDir);
|
|
15550
15633
|
return samePath3(root, worktreeDir) ? null : root;
|
|
15551
15634
|
}
|
|
15552
15635
|
async function snapshot(root, run) {
|
|
@@ -15588,21 +15671,21 @@ async function changedPaths(root, run) {
|
|
|
15588
15671
|
}
|
|
15589
15672
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
15590
15673
|
const paths = await changedPaths(baseline.root, run);
|
|
15591
|
-
const quarantineDir =
|
|
15592
|
-
|
|
15674
|
+
const quarantineDir = path20.join(
|
|
15675
|
+
path20.dirname(worktreeDir),
|
|
15593
15676
|
".canonical-recovery",
|
|
15594
15677
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
15595
15678
|
);
|
|
15596
15679
|
await fsp11.mkdir(quarantineDir, { recursive: true });
|
|
15597
15680
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
15598
|
-
await fsp11.writeFile(
|
|
15681
|
+
await fsp11.writeFile(path20.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
15599
15682
|
for (const relative of paths.untracked) {
|
|
15600
|
-
const source =
|
|
15601
|
-
const target =
|
|
15602
|
-
await fsp11.mkdir(
|
|
15683
|
+
const source = path20.join(baseline.root, relative);
|
|
15684
|
+
const target = path20.join(quarantineDir, "untracked", relative);
|
|
15685
|
+
await fsp11.mkdir(path20.dirname(target), { recursive: true });
|
|
15603
15686
|
await fsp11.copyFile(source, target);
|
|
15604
15687
|
}
|
|
15605
|
-
await fsp11.writeFile(
|
|
15688
|
+
await fsp11.writeFile(path20.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
15606
15689
|
taskId,
|
|
15607
15690
|
canonicalRoot: baseline.root,
|
|
15608
15691
|
canonicalHead: baseline.head,
|
|
@@ -15624,8 +15707,8 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
15624
15707
|
]);
|
|
15625
15708
|
}
|
|
15626
15709
|
for (const relative of evidence.untracked) {
|
|
15627
|
-
const target =
|
|
15628
|
-
const prefix = `${
|
|
15710
|
+
const target = path20.resolve(baseline.root, relative);
|
|
15711
|
+
const prefix = `${path20.resolve(baseline.root)}${path20.sep}`;
|
|
15629
15712
|
if (!target.startsWith(prefix) || !fs12.existsSync(target)) continue;
|
|
15630
15713
|
await fsp11.rm(target, { force: true });
|
|
15631
15714
|
}
|
|
@@ -15662,7 +15745,7 @@ var init_isolation_audit = __esm({
|
|
|
15662
15745
|
init_process_runner2();
|
|
15663
15746
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
15664
15747
|
samePath3 = (left, right) => {
|
|
15665
|
-
const [a, b] = [left, right].map((value) =>
|
|
15748
|
+
const [a, b] = [left, right].map((value) => path20.resolve(value));
|
|
15666
15749
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
15667
15750
|
};
|
|
15668
15751
|
}
|
|
@@ -16110,7 +16193,7 @@ var init_publication_outcome = __esm({
|
|
|
16110
16193
|
|
|
16111
16194
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
16112
16195
|
import fsp12 from "node:fs/promises";
|
|
16113
|
-
import
|
|
16196
|
+
import path21 from "node:path";
|
|
16114
16197
|
function defaultRun4(command, args, cwd, options = {}) {
|
|
16115
16198
|
return runProcess2(command, args, { cwd, ...options });
|
|
16116
16199
|
}
|
|
@@ -16118,13 +16201,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
16118
16201
|
if (!isAgentScratch(file)) {
|
|
16119
16202
|
throw new Error(`refusing to remove non-scratch publication path: ${file}`);
|
|
16120
16203
|
}
|
|
16121
|
-
const root =
|
|
16122
|
-
const target =
|
|
16123
|
-
const relative =
|
|
16124
|
-
if (!relative || relative.startsWith(`..${
|
|
16204
|
+
const root = path21.resolve(worktreeDir);
|
|
16205
|
+
const target = path21.resolve(root, file);
|
|
16206
|
+
const relative = path21.relative(root, target);
|
|
16207
|
+
if (!relative || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
|
|
16125
16208
|
throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
|
|
16126
16209
|
}
|
|
16127
|
-
for (let cursor = target; cursor !== root; cursor =
|
|
16210
|
+
for (let cursor = target; cursor !== root; cursor = path21.dirname(cursor)) {
|
|
16128
16211
|
try {
|
|
16129
16212
|
if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
|
|
16130
16213
|
throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
|
|
@@ -16246,7 +16329,7 @@ var init_publication_scope = __esm({
|
|
|
16246
16329
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
16247
16330
|
import fs13 from "node:fs";
|
|
16248
16331
|
import fsp13 from "node:fs/promises";
|
|
16249
|
-
import
|
|
16332
|
+
import path22 from "node:path";
|
|
16250
16333
|
function recoveryTaskId(prompt) {
|
|
16251
16334
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
16252
16335
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -16260,10 +16343,10 @@ function cloneLeaf(repo) {
|
|
|
16260
16343
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
16261
16344
|
const leaf = cloneLeaf(repo);
|
|
16262
16345
|
if (!leaf || !clonesRoot2) return [];
|
|
16263
|
-
const canonical =
|
|
16346
|
+
const canonical = path22.join(clonesRoot2, leaf);
|
|
16264
16347
|
return [
|
|
16265
|
-
|
|
16266
|
-
|
|
16348
|
+
path22.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
16349
|
+
path22.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
16267
16350
|
];
|
|
16268
16351
|
}
|
|
16269
16352
|
async function readLedger(file, readFile6) {
|