@algosuite/vo-mcp 0.2.0-beta.75 → 0.2.0-beta.76
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 +169 -161
- package/dist/runner-cli.js.map +3 -3
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -1614,6 +1614,67 @@ var init_process_runner = __esm({
|
|
|
1614
1614
|
}
|
|
1615
1615
|
});
|
|
1616
1616
|
|
|
1617
|
+
// src/runner/worktree-paths.mjs
|
|
1618
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1619
|
+
import fs from "node:fs";
|
|
1620
|
+
import path3 from "node:path";
|
|
1621
|
+
function canonicalPathKey(target, { cache: cache2 = null, realpath: realpath2 = fs.realpathSync.native } = {}) {
|
|
1622
|
+
const resolved = path3.resolve(String(target || ""));
|
|
1623
|
+
const tail = [];
|
|
1624
|
+
let cursor = resolved;
|
|
1625
|
+
for (; ; ) {
|
|
1626
|
+
let real = cache2 ? cache2.get(cursor) : void 0;
|
|
1627
|
+
if (real === void 0) {
|
|
1628
|
+
try {
|
|
1629
|
+
real = realpath2(cursor);
|
|
1630
|
+
} catch {
|
|
1631
|
+
real = null;
|
|
1632
|
+
}
|
|
1633
|
+
if (cache2) cache2.set(cursor, real);
|
|
1634
|
+
}
|
|
1635
|
+
if (real) {
|
|
1636
|
+
const unprefixed = String(real).replace(/^\\\\\?\\UNC\\/u, "\\\\").replace(/^\\\\\?\\/u, "");
|
|
1637
|
+
const joined = tail.length ? path3.join(unprefixed, ...[...tail].reverse()) : unprefixed;
|
|
1638
|
+
return process.platform === "win32" ? joined.toLowerCase() : joined;
|
|
1639
|
+
}
|
|
1640
|
+
const parent = path3.dirname(cursor);
|
|
1641
|
+
if (parent === cursor) break;
|
|
1642
|
+
tail.push(path3.basename(cursor));
|
|
1643
|
+
cursor = parent;
|
|
1644
|
+
}
|
|
1645
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
1646
|
+
}
|
|
1647
|
+
function samePath2(left, right) {
|
|
1648
|
+
const a = path3.resolve(left);
|
|
1649
|
+
const b = path3.resolve(right);
|
|
1650
|
+
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
1651
|
+
}
|
|
1652
|
+
function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
|
|
1653
|
+
const canonicalRoot = path3.resolve(root);
|
|
1654
|
+
if (clonesRootDir) {
|
|
1655
|
+
const clonePool = path3.resolve(clonesRootDir);
|
|
1656
|
+
if (samePath2(path3.dirname(canonicalRoot), clonePool)) {
|
|
1657
|
+
return path3.join(clonePool, ".agent-worktrees", path3.basename(canonicalRoot));
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return path3.join(canonicalRoot, ".agent-worktrees");
|
|
1661
|
+
}
|
|
1662
|
+
function worktreeDirForName(root, worktreeName, options = {}) {
|
|
1663
|
+
const leaf = createHash2("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
|
|
1664
|
+
return path3.join(worktreePoolForRoot(root, options), leaf);
|
|
1665
|
+
}
|
|
1666
|
+
function recoveryLedgerPathForRoot(root, options = {}) {
|
|
1667
|
+
return path3.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
|
|
1668
|
+
}
|
|
1669
|
+
function legacyRecoveryLedgerPathForRoot(root) {
|
|
1670
|
+
return path3.join(path3.resolve(root), ".agent-worktrees", "recovery-ledger.jsonl");
|
|
1671
|
+
}
|
|
1672
|
+
var init_worktree_paths = __esm({
|
|
1673
|
+
"src/runner/worktree-paths.mjs"() {
|
|
1674
|
+
"use strict";
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1617
1678
|
// src/runner/worktree-add.mjs
|
|
1618
1679
|
import fsp2 from "node:fs/promises";
|
|
1619
1680
|
async function pathExists2(target) {
|
|
@@ -1634,8 +1695,9 @@ async function worktreeStillRegistered(root, worktreeDir, runner) {
|
|
|
1634
1695
|
`git worktree list --porcelain failed during rollback verification: ${summarizeProcessFailure(listed)}`
|
|
1635
1696
|
);
|
|
1636
1697
|
}
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1698
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
1699
|
+
const target = canonicalPathKey(worktreeDir, { cache: cache2 });
|
|
1700
|
+
return String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).some((line) => canonicalPathKey(line.slice("worktree ".length).trim(), { cache: cache2 }) === target);
|
|
1639
1701
|
}
|
|
1640
1702
|
async function branchStillExists(root, branchName, runner) {
|
|
1641
1703
|
const listed = await runner("git", ["branch", "--list", "--format=%(refname:short)", branchName], {
|
|
@@ -1749,25 +1811,26 @@ var init_worktree_add = __esm({
|
|
|
1749
1811
|
"use strict";
|
|
1750
1812
|
init_pnpm_link_detach();
|
|
1751
1813
|
init_process_runner();
|
|
1814
|
+
init_worktree_paths();
|
|
1752
1815
|
DEFAULT_WORKTREE_ADD_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
1753
1816
|
DEFAULT_WORKTREE_ADD_ATTEMPTS = 2;
|
|
1754
1817
|
}
|
|
1755
1818
|
});
|
|
1756
1819
|
|
|
1757
1820
|
// src/runner/pnpm-canonical-health.mjs
|
|
1758
|
-
import
|
|
1821
|
+
import fs2 from "node:fs";
|
|
1759
1822
|
import fsp3 from "node:fs/promises";
|
|
1760
|
-
import
|
|
1823
|
+
import path4 from "node:path";
|
|
1761
1824
|
function readJsonFile(file) {
|
|
1762
|
-
if (!
|
|
1825
|
+
if (!fs2.existsSync(file)) return null;
|
|
1763
1826
|
try {
|
|
1764
|
-
return JSON.parse(
|
|
1827
|
+
return JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
1765
1828
|
} catch {
|
|
1766
1829
|
return null;
|
|
1767
1830
|
}
|
|
1768
1831
|
}
|
|
1769
1832
|
function packageJsonAt(root) {
|
|
1770
|
-
return readJsonFile(
|
|
1833
|
+
return readJsonFile(path4.join(root, "package.json"));
|
|
1771
1834
|
}
|
|
1772
1835
|
function dependencyNamesForPackage(root) {
|
|
1773
1836
|
const manifest = packageJsonAt(root);
|
|
@@ -1779,7 +1842,7 @@ function dependencyNamesForPackage(root) {
|
|
|
1779
1842
|
])].sort();
|
|
1780
1843
|
}
|
|
1781
1844
|
function packageEntryPath(nodeModulesDir, packageName) {
|
|
1782
|
-
return
|
|
1845
|
+
return path4.join(nodeModulesDir, ...String(packageName || "").split("/"));
|
|
1783
1846
|
}
|
|
1784
1847
|
function rootFsApi() {
|
|
1785
1848
|
return {
|
|
@@ -1808,7 +1871,7 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
|
|
|
1808
1871
|
} catch {
|
|
1809
1872
|
return { issue: `missing or dangling dependency link: ${entry}` };
|
|
1810
1873
|
}
|
|
1811
|
-
const packageJsonPath =
|
|
1874
|
+
const packageJsonPath = path4.join(resolved, "package.json");
|
|
1812
1875
|
if (!await pathExists3(packageJsonPath, fsApi)) {
|
|
1813
1876
|
return { issue: `dependency target is missing package.json: ${entry} -> ${resolved}` };
|
|
1814
1877
|
}
|
|
@@ -1820,11 +1883,11 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
|
|
|
1820
1883
|
return { issue: null };
|
|
1821
1884
|
}
|
|
1822
1885
|
function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
|
|
1823
|
-
const packages = [{ nodeModulesDir:
|
|
1886
|
+
const packages = [{ nodeModulesDir: path4.join(root, "node_modules"), packageRoot: root }];
|
|
1824
1887
|
for (const relativeDir of linkedWorkspaceDirs) {
|
|
1825
1888
|
packages.push({
|
|
1826
|
-
nodeModulesDir:
|
|
1827
|
-
packageRoot:
|
|
1889
|
+
nodeModulesDir: path4.join(root, relativeDir, "node_modules"),
|
|
1890
|
+
packageRoot: path4.join(root, relativeDir)
|
|
1828
1891
|
});
|
|
1829
1892
|
}
|
|
1830
1893
|
const representatives = [];
|
|
@@ -1836,7 +1899,7 @@ function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
|
|
|
1836
1899
|
return representatives;
|
|
1837
1900
|
}
|
|
1838
1901
|
function canonicalNodeModulesQuarantineRoot(root) {
|
|
1839
|
-
return
|
|
1902
|
+
return path4.join(root, ".agent-worktrees", QUARANTINE_DIRNAME);
|
|
1840
1903
|
}
|
|
1841
1904
|
async function inspectCanonicalNodeModulesHealth({
|
|
1842
1905
|
root,
|
|
@@ -1847,8 +1910,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1847
1910
|
sampleLimit = DEFAULT_SAMPLE_LIMIT
|
|
1848
1911
|
}) {
|
|
1849
1912
|
const issues = [];
|
|
1850
|
-
const nodeModulesDir =
|
|
1851
|
-
const markerPath =
|
|
1913
|
+
const nodeModulesDir = path4.join(root, "node_modules");
|
|
1914
|
+
const markerPath = path4.join(nodeModulesDir, ".vo-deps-state.json");
|
|
1852
1915
|
if (!await pathExists3(nodeModulesDir, fsApi)) {
|
|
1853
1916
|
issues.push(`missing root node_modules: ${nodeModulesDir}`);
|
|
1854
1917
|
} else {
|
|
@@ -1857,8 +1920,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1857
1920
|
issues.push(`root node_modules must be a real directory: ${nodeModulesDir}`);
|
|
1858
1921
|
}
|
|
1859
1922
|
}
|
|
1860
|
-
if (!await pathExists3(
|
|
1861
|
-
issues.push(`missing root .pnpm store view: ${
|
|
1923
|
+
if (!await pathExists3(path4.join(nodeModulesDir, ".pnpm"), fsApi)) {
|
|
1924
|
+
issues.push(`missing root .pnpm store view: ${path4.join(nodeModulesDir, ".pnpm")}`);
|
|
1862
1925
|
}
|
|
1863
1926
|
if (requireMarker) {
|
|
1864
1927
|
try {
|
|
@@ -1871,7 +1934,7 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1871
1934
|
}
|
|
1872
1935
|
}
|
|
1873
1936
|
for (const relativeDir of linkedWorkspaceDirs) {
|
|
1874
|
-
const workspaceNodeModules =
|
|
1937
|
+
const workspaceNodeModules = path4.join(root, relativeDir, "node_modules");
|
|
1875
1938
|
if (!await pathExists3(workspaceNodeModules, fsApi)) {
|
|
1876
1939
|
issues.push(`missing workspace node_modules: ${workspaceNodeModules}`);
|
|
1877
1940
|
continue;
|
|
@@ -1894,17 +1957,17 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1894
1957
|
async function quarantineCanonicalNodeModules(root, issues, options = {}) {
|
|
1895
1958
|
const fsApi = options.fsApi || rootFsApi();
|
|
1896
1959
|
const logger = options.logger || console.error;
|
|
1897
|
-
const nodeModulesDir =
|
|
1960
|
+
const nodeModulesDir = path4.join(root, "node_modules");
|
|
1898
1961
|
const quarantineRoot = canonicalNodeModulesQuarantineRoot(root);
|
|
1899
1962
|
if (!await pathExists3(nodeModulesDir, fsApi)) return null;
|
|
1900
1963
|
await fsApi.mkdir(quarantineRoot, { recursive: true });
|
|
1901
1964
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1902
1965
|
let attempt = 0;
|
|
1903
1966
|
for (; ; ) {
|
|
1904
|
-
const quarantinePath =
|
|
1967
|
+
const quarantinePath = path4.join(quarantineRoot, `node_modules-${stamp}-${process.pid}-${attempt}`);
|
|
1905
1968
|
try {
|
|
1906
1969
|
await fsApi.rename(nodeModulesDir, quarantinePath);
|
|
1907
|
-
const metadataPath =
|
|
1970
|
+
const metadataPath = path4.join(quarantinePath, ".vo-runner-quarantine.json");
|
|
1908
1971
|
await fsApi.writeFile(metadataPath, `${JSON.stringify({
|
|
1909
1972
|
issues,
|
|
1910
1973
|
originalPath: nodeModulesDir,
|
|
@@ -1929,27 +1992,27 @@ var init_pnpm_canonical_health = __esm({
|
|
|
1929
1992
|
});
|
|
1930
1993
|
|
|
1931
1994
|
// src/runner/pnpm-command.mjs
|
|
1932
|
-
import
|
|
1995
|
+
import fs3 from "node:fs";
|
|
1933
1996
|
import os from "node:os";
|
|
1934
|
-
import
|
|
1997
|
+
import path5 from "node:path";
|
|
1935
1998
|
function isWindowsDriveOrUnc(value) {
|
|
1936
1999
|
return WINDOWS_DRIVE_OR_UNC_RE.test(String(value || ""));
|
|
1937
2000
|
}
|
|
1938
2001
|
function portableDirname(value) {
|
|
1939
|
-
if (isWindowsDriveOrUnc(value)) return
|
|
1940
|
-
if (
|
|
1941
|
-
return
|
|
2002
|
+
if (isWindowsDriveOrUnc(value)) return path5.win32.dirname(value);
|
|
2003
|
+
if (path5.posix.isAbsolute(value)) return path5.posix.dirname(value);
|
|
2004
|
+
return path5.dirname(value);
|
|
1942
2005
|
}
|
|
1943
2006
|
function portableJoin(root, ...segments) {
|
|
1944
|
-
if (isWindowsDriveOrUnc(root)) return
|
|
1945
|
-
if (
|
|
1946
|
-
return
|
|
2007
|
+
if (isWindowsDriveOrUnc(root)) return path5.win32.join(root, ...segments);
|
|
2008
|
+
if (path5.posix.isAbsolute(root)) return path5.posix.join(root, ...segments);
|
|
2009
|
+
return path5.join(root, ...segments);
|
|
1947
2010
|
}
|
|
1948
2011
|
function readPackageManager(root) {
|
|
1949
|
-
const packagePath =
|
|
1950
|
-
if (!
|
|
2012
|
+
const packagePath = path5.join(root, "package.json");
|
|
2013
|
+
if (!fs3.existsSync(packagePath)) return "";
|
|
1951
2014
|
try {
|
|
1952
|
-
return String(JSON.parse(
|
|
2015
|
+
return String(JSON.parse(fs3.readFileSync(packagePath, "utf8"))?.packageManager || "").trim();
|
|
1953
2016
|
} catch {
|
|
1954
2017
|
return "";
|
|
1955
2018
|
}
|
|
@@ -1966,13 +2029,13 @@ function pnpmSelector(root) {
|
|
|
1966
2029
|
const packageManager = readPackageManager(root);
|
|
1967
2030
|
const version = validatedPnpmVersionToken(root);
|
|
1968
2031
|
if (packageManager && !version) {
|
|
1969
|
-
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${
|
|
2032
|
+
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path5.join(root, "package.json")}`);
|
|
1970
2033
|
}
|
|
1971
2034
|
return version ? `pnpm@${version}` : "pnpm";
|
|
1972
2035
|
}
|
|
1973
2036
|
function whereResults(result) {
|
|
1974
2037
|
if (result?.status !== 0) return [];
|
|
1975
|
-
return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) =>
|
|
2038
|
+
return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path5.win32.isAbsolute(line));
|
|
1976
2039
|
}
|
|
1977
2040
|
async function resolveWindowsNativeCommand(command, runner) {
|
|
1978
2041
|
const result = await runner("where", [command], { timeoutMs: 1e4 });
|
|
@@ -1984,29 +2047,29 @@ function trustedCorepackCandidates(options) {
|
|
|
1984
2047
|
const roots = [portableDirname(execPath)];
|
|
1985
2048
|
for (const key of ["ProgramW6432", "ProgramFiles", "ProgramFiles(x86)"]) {
|
|
1986
2049
|
const programFiles = String(env2[key] || "").trim();
|
|
1987
|
-
if (isWindowsDriveOrUnc(programFiles) ||
|
|
2050
|
+
if (isWindowsDriveOrUnc(programFiles) || path5.posix.isAbsolute(programFiles)) {
|
|
1988
2051
|
roots.push(portableJoin(programFiles, "nodejs"));
|
|
1989
2052
|
}
|
|
1990
2053
|
}
|
|
1991
2054
|
return [...new Set(roots)].map((root) => portableJoin(root, "node_modules", "corepack", "dist", "corepack.js"));
|
|
1992
2055
|
}
|
|
1993
2056
|
function resolveTrustedCorepackJs(options) {
|
|
1994
|
-
const existsSync13 = options.existsSync ||
|
|
2057
|
+
const existsSync13 = options.existsSync || fs3.existsSync;
|
|
1995
2058
|
return trustedCorepackCandidates(options).find((candidate) => existsSync13(candidate)) || "";
|
|
1996
2059
|
}
|
|
1997
2060
|
function defaultPnpmShimRoot() {
|
|
1998
|
-
return
|
|
2061
|
+
return path5.join(os.homedir(), ".vo", "pnpm-shim");
|
|
1999
2062
|
}
|
|
2000
2063
|
function materializePnpmShimDir(corepackJs, execPath, options = {}) {
|
|
2001
|
-
const mkdirSync10 = options.mkdirSync ||
|
|
2002
|
-
const writeFileSync6 = options.writeFileSync ||
|
|
2003
|
-
const chmodSync3 = options.chmodSync ||
|
|
2064
|
+
const mkdirSync10 = options.mkdirSync || fs3.mkdirSync;
|
|
2065
|
+
const writeFileSync6 = options.writeFileSync || fs3.writeFileSync;
|
|
2066
|
+
const chmodSync3 = options.chmodSync || fs3.chmodSync;
|
|
2004
2067
|
const dir = options.shimRoot || defaultPnpmShimRoot();
|
|
2005
2068
|
mkdirSync10(dir, { recursive: true });
|
|
2006
|
-
writeFileSync6(
|
|
2069
|
+
writeFileSync6(path5.join(dir, PNPM_SHIM_CMD_NAME), `@echo off\r
|
|
2007
2070
|
"${execPath}" "${corepackJs}" pnpm %*\r
|
|
2008
2071
|
`);
|
|
2009
|
-
const shPath =
|
|
2072
|
+
const shPath = path5.join(dir, PNPM_SHIM_SH_NAME);
|
|
2010
2073
|
writeFileSync6(shPath, `#!/bin/sh
|
|
2011
2074
|
exec "${execPath}" "${corepackJs}" pnpm "$@"
|
|
2012
2075
|
`);
|
|
@@ -2018,8 +2081,8 @@ exec "${execPath}" "${corepackJs}" pnpm "$@"
|
|
|
2018
2081
|
}
|
|
2019
2082
|
function commandDirectory(command) {
|
|
2020
2083
|
if (!command) return "";
|
|
2021
|
-
if (isWindowsDriveOrUnc(command)) return
|
|
2022
|
-
if (
|
|
2084
|
+
if (isWindowsDriveOrUnc(command)) return path5.win32.dirname(command);
|
|
2085
|
+
if (path5.posix.isAbsolute(command)) return path5.posix.dirname(command);
|
|
2023
2086
|
return "";
|
|
2024
2087
|
}
|
|
2025
2088
|
function agentPathDirs(command, execPath) {
|
|
@@ -2108,7 +2171,7 @@ var init_pnpm_command = __esm({
|
|
|
2108
2171
|
|
|
2109
2172
|
// src/runner/pnpm-materialize.mjs
|
|
2110
2173
|
import fsp4 from "node:fs/promises";
|
|
2111
|
-
import
|
|
2174
|
+
import path6 from "node:path";
|
|
2112
2175
|
function linkType() {
|
|
2113
2176
|
return process.platform === "win32" ? "junction" : "dir";
|
|
2114
2177
|
}
|
|
@@ -2119,14 +2182,16 @@ async function realpathOrThrow(target, fsApi) {
|
|
|
2119
2182
|
throw new Error(`[vo-mcp runner] dependency path is missing: ${target}`);
|
|
2120
2183
|
}
|
|
2121
2184
|
}
|
|
2122
|
-
function shouldMapToWorktree(resolvedTarget, canonicalRoot) {
|
|
2123
|
-
const relative =
|
|
2124
|
-
|
|
2185
|
+
function shouldMapToWorktree(resolvedTarget, canonicalRoot, pathApi = path6) {
|
|
2186
|
+
const relative = pathApi.relative(canonicalRoot, resolvedTarget);
|
|
2187
|
+
if (!relative || pathApi.isAbsolute(relative)) return false;
|
|
2188
|
+
const normalized = relative.replace(/\\/g, "/");
|
|
2189
|
+
return !normalized.startsWith("..") && !normalized.split("/").includes("node_modules");
|
|
2125
2190
|
}
|
|
2126
2191
|
async function resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, fsApi) {
|
|
2127
2192
|
const resolved = await realpathOrThrow(sourceEntry, fsApi);
|
|
2128
2193
|
if (!shouldMapToWorktree(resolved, canonicalRoot)) return resolved;
|
|
2129
|
-
const mapped =
|
|
2194
|
+
const mapped = path6.join(worktreeRoot, path6.relative(canonicalRoot, resolved));
|
|
2130
2195
|
if (!await fsApi.pathExists(mapped)) {
|
|
2131
2196
|
throw new Error(`[vo-mcp runner] task-local workspace target is missing for dependency link: ${mapped}`);
|
|
2132
2197
|
}
|
|
@@ -2137,7 +2202,7 @@ async function ensureLinkedDirectory(source, target, fsApi) {
|
|
|
2137
2202
|
if (await fsApi.realpath(target) === await fsApi.realpath(source)) return;
|
|
2138
2203
|
throw new Error(`[vo-mcp runner] refusing to overwrite existing dependency path: ${target}`);
|
|
2139
2204
|
}
|
|
2140
|
-
await fsApi.mkdir(
|
|
2205
|
+
await fsApi.mkdir(path6.dirname(target), { recursive: true });
|
|
2141
2206
|
await fsApi.symlink(source, target, linkType());
|
|
2142
2207
|
if (await fsApi.realpath(target) !== await fsApi.realpath(source)) {
|
|
2143
2208
|
throw new Error(`[vo-mcp runner] dependency link validation failed for ${target}`);
|
|
@@ -2151,8 +2216,8 @@ async function maybeYield2(state) {
|
|
|
2151
2216
|
async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
2152
2217
|
await fsApi.mkdir(targetDir, { recursive: true });
|
|
2153
2218
|
for (const entry of await fsApi.readdir(sourceDir, { withFileTypes: true })) {
|
|
2154
|
-
const source =
|
|
2155
|
-
const target =
|
|
2219
|
+
const source = path6.join(sourceDir, entry.name);
|
|
2220
|
+
const target = path6.join(targetDir, entry.name);
|
|
2156
2221
|
await maybeYield2(yieldState);
|
|
2157
2222
|
if (entry.isDirectory()) {
|
|
2158
2223
|
await copyDirRecursive(source, target, fsApi, yieldState);
|
|
@@ -2164,17 +2229,17 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
|
2164
2229
|
async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
|
|
2165
2230
|
await options.beforeEntry?.(sourceEntry, targetEntry);
|
|
2166
2231
|
const stat3 = await options.fsApi.lstat(sourceEntry);
|
|
2167
|
-
if (stat3.isDirectory() && !stat3.isSymbolicLink() &&
|
|
2232
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path6.basename(sourceEntry) === ".bin") {
|
|
2168
2233
|
await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
|
|
2169
2234
|
return;
|
|
2170
2235
|
}
|
|
2171
|
-
if (stat3.isDirectory() && !stat3.isSymbolicLink() &&
|
|
2236
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path6.basename(sourceEntry).startsWith("@")) {
|
|
2172
2237
|
await options.fsApi.mkdir(targetEntry, { recursive: true });
|
|
2173
2238
|
for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
|
|
2174
2239
|
await maybeYield2(options.yieldState);
|
|
2175
2240
|
await materializeEntry(
|
|
2176
|
-
|
|
2177
|
-
|
|
2241
|
+
path6.join(sourceEntry, nested.name),
|
|
2242
|
+
path6.join(targetEntry, nested.name),
|
|
2178
2243
|
canonicalRoot,
|
|
2179
2244
|
worktreeRoot,
|
|
2180
2245
|
options
|
|
@@ -2187,7 +2252,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
|
|
|
2187
2252
|
await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
|
|
2188
2253
|
return;
|
|
2189
2254
|
}
|
|
2190
|
-
await options.fsApi.mkdir(
|
|
2255
|
+
await options.fsApi.mkdir(path6.dirname(targetEntry), { recursive: true });
|
|
2191
2256
|
await options.fsApi.copyFile(sourceEntry, targetEntry);
|
|
2192
2257
|
}
|
|
2193
2258
|
function createPnpmFsApi(pathExists6) {
|
|
@@ -2207,13 +2272,14 @@ function createPnpmFsApi(pathExists6) {
|
|
|
2207
2272
|
};
|
|
2208
2273
|
}
|
|
2209
2274
|
async function materializeNodeModulesForest(sourceNodeModules, targetNodeModules, canonicalRoot, worktreeRoot, options) {
|
|
2275
|
+
const realCanonicalRoot = await realpathOrThrow(canonicalRoot, options.fsApi);
|
|
2210
2276
|
await options.fsApi.mkdir(targetNodeModules, { recursive: true });
|
|
2211
2277
|
for (const entry of await options.fsApi.readdir(sourceNodeModules, { withFileTypes: true })) {
|
|
2212
2278
|
await maybeYield2(options.yieldState);
|
|
2213
2279
|
await materializeEntry(
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2280
|
+
path6.join(sourceNodeModules, entry.name),
|
|
2281
|
+
path6.join(targetNodeModules, entry.name),
|
|
2282
|
+
realCanonicalRoot,
|
|
2217
2283
|
worktreeRoot,
|
|
2218
2284
|
options
|
|
2219
2285
|
);
|
|
@@ -2226,21 +2292,21 @@ var init_pnpm_materialize = __esm({
|
|
|
2226
2292
|
});
|
|
2227
2293
|
|
|
2228
2294
|
// src/runner/pnpm-hydration.mjs
|
|
2229
|
-
import { createHash as
|
|
2230
|
-
import
|
|
2295
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2296
|
+
import fs4 from "node:fs";
|
|
2231
2297
|
import fsp5 from "node:fs/promises";
|
|
2232
|
-
import
|
|
2298
|
+
import path7 from "node:path";
|
|
2233
2299
|
function hashText(text) {
|
|
2234
|
-
return
|
|
2300
|
+
return createHash3("sha256").update(String(text)).digest("hex");
|
|
2235
2301
|
}
|
|
2236
2302
|
function statePath(root) {
|
|
2237
|
-
return
|
|
2303
|
+
return path7.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
|
|
2238
2304
|
}
|
|
2239
2305
|
function packageJson(root) {
|
|
2240
|
-
const packagePath =
|
|
2241
|
-
if (!
|
|
2306
|
+
const packagePath = path7.join(root, "package.json");
|
|
2307
|
+
if (!fs4.existsSync(packagePath)) return null;
|
|
2242
2308
|
try {
|
|
2243
|
-
return JSON.parse(
|
|
2309
|
+
return JSON.parse(fs4.readFileSync(packagePath, "utf8"));
|
|
2244
2310
|
} catch {
|
|
2245
2311
|
return null;
|
|
2246
2312
|
}
|
|
@@ -2254,23 +2320,23 @@ async function pathExists4(target) {
|
|
|
2254
2320
|
}
|
|
2255
2321
|
}
|
|
2256
2322
|
function lockfileHash(root) {
|
|
2257
|
-
const lockPath =
|
|
2258
|
-
if (!
|
|
2259
|
-
return hashText(
|
|
2323
|
+
const lockPath = path7.join(root, "pnpm-lock.yaml");
|
|
2324
|
+
if (!fs4.existsSync(lockPath)) return "";
|
|
2325
|
+
return hashText(fs4.readFileSync(lockPath, "utf8"));
|
|
2260
2326
|
}
|
|
2261
2327
|
function readHydrationState(root) {
|
|
2262
2328
|
const file = statePath(root);
|
|
2263
|
-
if (!
|
|
2329
|
+
if (!fs4.existsSync(file)) return null;
|
|
2264
2330
|
try {
|
|
2265
|
-
return JSON.parse(
|
|
2331
|
+
return JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
2266
2332
|
} catch {
|
|
2267
2333
|
return null;
|
|
2268
2334
|
}
|
|
2269
2335
|
}
|
|
2270
2336
|
function writeHydrationState(root, state) {
|
|
2271
2337
|
const file = statePath(root);
|
|
2272
|
-
|
|
2273
|
-
|
|
2338
|
+
fs4.mkdirSync(path7.dirname(file), { recursive: true });
|
|
2339
|
+
fs4.writeFileSync(file, `${JSON.stringify({
|
|
2274
2340
|
...state,
|
|
2275
2341
|
stateVersion: 2,
|
|
2276
2342
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2278,7 +2344,7 @@ function writeHydrationState(root, state) {
|
|
|
2278
2344
|
`, "utf8");
|
|
2279
2345
|
}
|
|
2280
2346
|
function voDepsStatePath(root) {
|
|
2281
|
-
return
|
|
2347
|
+
return path7.join(root, "node_modules", ".vo-deps-state.json");
|
|
2282
2348
|
}
|
|
2283
2349
|
async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
2284
2350
|
const marker = voDepsStatePath(root);
|
|
@@ -2288,7 +2354,7 @@ async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
|
2288
2354
|
} catch {
|
|
2289
2355
|
}
|
|
2290
2356
|
const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
|
|
2291
|
-
await fsApi.mkdir(
|
|
2357
|
+
await fsApi.mkdir(path7.dirname(marker), { recursive: true });
|
|
2292
2358
|
await fsApi.writeFile(temp, `${JSON.stringify({ lockfileHash: expectedHash, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
2293
2359
|
`, "utf8");
|
|
2294
2360
|
await fsApi.rename(temp, marker);
|
|
@@ -2315,9 +2381,9 @@ function workspacePatternsFromPackageJson(root) {
|
|
|
2315
2381
|
return [];
|
|
2316
2382
|
}
|
|
2317
2383
|
function workspacePatternsFromPnpmWorkspace(root) {
|
|
2318
|
-
const workspacePath =
|
|
2319
|
-
if (!
|
|
2320
|
-
const lines =
|
|
2384
|
+
const workspacePath = path7.join(root, "pnpm-workspace.yaml");
|
|
2385
|
+
if (!fs4.existsSync(workspacePath)) return [];
|
|
2386
|
+
const lines = fs4.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
|
|
2321
2387
|
const patterns = [];
|
|
2322
2388
|
let inPackages = false;
|
|
2323
2389
|
for (const rawLine of lines) {
|
|
@@ -2374,16 +2440,16 @@ function collectPackageDirs(root, options = {}) {
|
|
|
2374
2440
|
while (stack.length > 0) {
|
|
2375
2441
|
const current = stack.pop();
|
|
2376
2442
|
if (!current) continue;
|
|
2377
|
-
const relativeDir =
|
|
2378
|
-
if (relativeDir &&
|
|
2443
|
+
const relativeDir = path7.relative(root, current.dir).replace(/\\/g, "/");
|
|
2444
|
+
if (relativeDir && fs4.existsSync(path7.join(current.dir, "package.json"))) {
|
|
2379
2445
|
found.push(relativeDir);
|
|
2380
2446
|
if (found.length >= maxDirs) break;
|
|
2381
2447
|
}
|
|
2382
2448
|
if (current.depth >= maxDepth) continue;
|
|
2383
|
-
for (const entry of
|
|
2449
|
+
for (const entry of fs4.readdirSync(current.dir, { withFileTypes: true })) {
|
|
2384
2450
|
if (!entry.isDirectory()) continue;
|
|
2385
2451
|
if (IGNORED_SCAN_DIRS.has(entry.name)) continue;
|
|
2386
|
-
stack.push({ dir:
|
|
2452
|
+
stack.push({ dir: path7.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
2387
2453
|
}
|
|
2388
2454
|
}
|
|
2389
2455
|
return found.sort();
|
|
@@ -2407,8 +2473,8 @@ async function runInstall(root, options = {}) {
|
|
|
2407
2473
|
}
|
|
2408
2474
|
}
|
|
2409
2475
|
async function hasReadyNodeModules(root) {
|
|
2410
|
-
const nodeModules =
|
|
2411
|
-
return await pathExists4(
|
|
2476
|
+
const nodeModules = path7.join(root, "node_modules");
|
|
2477
|
+
return await pathExists4(path7.join(nodeModules, ".modules.yaml")) || await pathExists4(path7.join(nodeModules, ".pnpm"));
|
|
2412
2478
|
}
|
|
2413
2479
|
function hydrationFsApi(overrides = {}) {
|
|
2414
2480
|
return { ...createPnpmFsApi(pathExists4), ...overrides };
|
|
@@ -2445,7 +2511,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
2445
2511
|
expectedHash: hash,
|
|
2446
2512
|
requireMarker: false
|
|
2447
2513
|
});
|
|
2448
|
-
if (!health.healthy && await fsApi.pathExists(
|
|
2514
|
+
if (!health.healthy && await fsApi.pathExists(path7.join(root, "node_modules"))) {
|
|
2449
2515
|
quarantine = await quarantineCanonicalNodeModules(root, health.issues, {
|
|
2450
2516
|
fsApi,
|
|
2451
2517
|
logger: options.logger
|
|
@@ -2457,7 +2523,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
2457
2523
|
}
|
|
2458
2524
|
const linkedWorkspaceDirs = [];
|
|
2459
2525
|
for (const relativeDir of workspaceDirs) {
|
|
2460
|
-
if (await pathExists4(
|
|
2526
|
+
if (await pathExists4(path7.join(root, relativeDir, "node_modules"))) {
|
|
2461
2527
|
linkedWorkspaceDirs.push(relativeDir);
|
|
2462
2528
|
}
|
|
2463
2529
|
}
|
|
@@ -2493,18 +2559,18 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
|
|
|
2493
2559
|
yieldEvery: Math.max(1, options.yieldEvery ?? DEFAULT_YIELD_EVERY2)
|
|
2494
2560
|
}
|
|
2495
2561
|
};
|
|
2496
|
-
recordOwnedNodeModulesRoot(dependencyOwnership,
|
|
2562
|
+
recordOwnedNodeModulesRoot(dependencyOwnership, path7.join(worktreeDir, "node_modules"));
|
|
2497
2563
|
await materializeNodeModulesForest(
|
|
2498
|
-
|
|
2499
|
-
|
|
2564
|
+
path7.join(root, "node_modules"),
|
|
2565
|
+
path7.join(worktreeDir, "node_modules"),
|
|
2500
2566
|
root,
|
|
2501
2567
|
worktreeDir,
|
|
2502
2568
|
materializeOptions
|
|
2503
2569
|
);
|
|
2504
2570
|
for (const relativeDir of hydration.linkedWorkspaceDirs) {
|
|
2505
|
-
const sourceNodeModules =
|
|
2506
|
-
const targetNodeModules =
|
|
2507
|
-
if (!await fsApi.pathExists(
|
|
2571
|
+
const sourceNodeModules = path7.join(root, relativeDir, "node_modules");
|
|
2572
|
+
const targetNodeModules = path7.join(worktreeDir, relativeDir, "node_modules");
|
|
2573
|
+
if (!await fsApi.pathExists(path7.join(worktreeDir, relativeDir))) continue;
|
|
2508
2574
|
recordOwnedNodeModulesRoot(dependencyOwnership, targetNodeModules);
|
|
2509
2575
|
await materializeNodeModulesForest(sourceNodeModules, targetNodeModules, root, worktreeDir, materializeOptions);
|
|
2510
2576
|
}
|
|
@@ -2540,67 +2606,6 @@ var init_pnpm_hydration = __esm({
|
|
|
2540
2606
|
}
|
|
2541
2607
|
});
|
|
2542
2608
|
|
|
2543
|
-
// src/runner/worktree-paths.mjs
|
|
2544
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
2545
|
-
import fs4 from "node:fs";
|
|
2546
|
-
import path7 from "node:path";
|
|
2547
|
-
function canonicalPathKey(target, { cache: cache2 = null, realpath: realpath2 = fs4.realpathSync.native } = {}) {
|
|
2548
|
-
const resolved = path7.resolve(String(target || ""));
|
|
2549
|
-
const tail = [];
|
|
2550
|
-
let cursor = resolved;
|
|
2551
|
-
for (; ; ) {
|
|
2552
|
-
let real = cache2 ? cache2.get(cursor) : void 0;
|
|
2553
|
-
if (real === void 0) {
|
|
2554
|
-
try {
|
|
2555
|
-
real = realpath2(cursor);
|
|
2556
|
-
} catch {
|
|
2557
|
-
real = null;
|
|
2558
|
-
}
|
|
2559
|
-
if (cache2) cache2.set(cursor, real);
|
|
2560
|
-
}
|
|
2561
|
-
if (real) {
|
|
2562
|
-
const unprefixed = String(real).replace(/^\\\\\?\\UNC\\/u, "\\\\").replace(/^\\\\\?\\/u, "");
|
|
2563
|
-
const joined = tail.length ? path7.join(unprefixed, ...[...tail].reverse()) : unprefixed;
|
|
2564
|
-
return process.platform === "win32" ? joined.toLowerCase() : joined;
|
|
2565
|
-
}
|
|
2566
|
-
const parent = path7.dirname(cursor);
|
|
2567
|
-
if (parent === cursor) break;
|
|
2568
|
-
tail.push(path7.basename(cursor));
|
|
2569
|
-
cursor = parent;
|
|
2570
|
-
}
|
|
2571
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
2572
|
-
}
|
|
2573
|
-
function samePath2(left, right) {
|
|
2574
|
-
const a = path7.resolve(left);
|
|
2575
|
-
const b = path7.resolve(right);
|
|
2576
|
-
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
2577
|
-
}
|
|
2578
|
-
function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
|
|
2579
|
-
const canonicalRoot = path7.resolve(root);
|
|
2580
|
-
if (clonesRootDir) {
|
|
2581
|
-
const clonePool = path7.resolve(clonesRootDir);
|
|
2582
|
-
if (samePath2(path7.dirname(canonicalRoot), clonePool)) {
|
|
2583
|
-
return path7.join(clonePool, ".agent-worktrees", path7.basename(canonicalRoot));
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return path7.join(canonicalRoot, ".agent-worktrees");
|
|
2587
|
-
}
|
|
2588
|
-
function worktreeDirForName(root, worktreeName, options = {}) {
|
|
2589
|
-
const leaf = createHash3("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
|
|
2590
|
-
return path7.join(worktreePoolForRoot(root, options), leaf);
|
|
2591
|
-
}
|
|
2592
|
-
function recoveryLedgerPathForRoot(root, options = {}) {
|
|
2593
|
-
return path7.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
|
|
2594
|
-
}
|
|
2595
|
-
function legacyRecoveryLedgerPathForRoot(root) {
|
|
2596
|
-
return path7.join(path7.resolve(root), ".agent-worktrees", "recovery-ledger.jsonl");
|
|
2597
|
-
}
|
|
2598
|
-
var init_worktree_paths = __esm({
|
|
2599
|
-
"src/runner/worktree-paths.mjs"() {
|
|
2600
|
-
"use strict";
|
|
2601
|
-
}
|
|
2602
|
-
});
|
|
2603
|
-
|
|
2604
2609
|
// src/runner/worktree-cleanup.mjs
|
|
2605
2610
|
import fs5 from "node:fs";
|
|
2606
2611
|
import fsp6 from "node:fs/promises";
|
|
@@ -3057,27 +3062,29 @@ async function fetchExactTaskBase(root, baseSha, options = {}) {
|
|
|
3057
3062
|
function shouldIgnoreManagedEntry(entryName) {
|
|
3058
3063
|
return IGNORED_MANAGED_ENTRIES.has(entryName) || entryName.endsWith(".lock") || entryName.startsWith("runner-");
|
|
3059
3064
|
}
|
|
3060
|
-
async function
|
|
3065
|
+
async function registeredWorktreeKeys(root, cache2, options = {}) {
|
|
3061
3066
|
const listed = await git(root, ["worktree", "list", "--porcelain"], options);
|
|
3062
3067
|
if (listed.status !== 0) {
|
|
3063
3068
|
throw new Error(`git worktree list --porcelain failed while checking managed residue: ${summarizeProcessFailure(listed)}`);
|
|
3064
3069
|
}
|
|
3065
3070
|
return new Set(
|
|
3066
|
-
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) =>
|
|
3071
|
+
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => canonicalPathKey(line.slice("worktree ".length).trim(), { cache: cache2 }))
|
|
3067
3072
|
);
|
|
3068
3073
|
}
|
|
3069
3074
|
async function reportLegacyResiduals(root, options = {}) {
|
|
3070
3075
|
const managedRoot = path9.join(root, ".agent-worktrees");
|
|
3071
3076
|
if (!fs6.existsSync(managedRoot)) return [];
|
|
3072
|
-
const
|
|
3073
|
-
const
|
|
3077
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
3078
|
+
const registered = await registeredWorktreeKeys(root, cache2, options);
|
|
3079
|
+
const pending = new Set([...pendingCleanupDirs()].map((dir) => canonicalPathKey(dir, { cache: cache2 })));
|
|
3074
3080
|
const found = [];
|
|
3075
3081
|
for (const entry of fs6.readdirSync(managedRoot, { withFileTypes: true })) {
|
|
3076
3082
|
if (!entry.isDirectory()) continue;
|
|
3077
3083
|
if (shouldIgnoreManagedEntry(entry.name)) continue;
|
|
3078
3084
|
const absolute = path9.resolve(path9.join(managedRoot, entry.name));
|
|
3079
|
-
|
|
3080
|
-
if (
|
|
3085
|
+
const key = canonicalPathKey(absolute, { cache: cache2 });
|
|
3086
|
+
if (registered.has(key)) continue;
|
|
3087
|
+
if (pending.has(key)) continue;
|
|
3081
3088
|
found.push(absolute);
|
|
3082
3089
|
}
|
|
3083
3090
|
if (found.length === 0) return [];
|
|
@@ -3114,6 +3121,7 @@ var init_task_root_prepare = __esm({
|
|
|
3114
3121
|
init_pnpm_command();
|
|
3115
3122
|
init_process_runner();
|
|
3116
3123
|
init_worktree_cleanup();
|
|
3124
|
+
init_worktree_paths();
|
|
3117
3125
|
PREP_LOCK_WAIT_MS = 20 * 60 * 1e3;
|
|
3118
3126
|
PREP_LOCK_STALE_MS = 45 * 60 * 1e3;
|
|
3119
3127
|
REPORTED_RESIDUAL_SNAPSHOTS = /* @__PURE__ */ new Set();
|