@algosuite/vo-mcp 0.2.0-beta.29 → 0.2.0-beta.30
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/autostart-cli.js +62 -46
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +887 -223
- package/dist/cli.js.map +4 -4
- package/dist/index.js +826 -192
- package/dist/index.js.map +4 -4
- package/dist/install-cli.js +56 -42
- package/dist/install-cli.js.map +3 -3
- package/dist/runner-cli.js +568 -308
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -77,11 +77,11 @@ __export(credential_store_exports, {
|
|
|
77
77
|
readStoredCredentialKeychainOnly: () => readStoredCredentialKeychainOnly,
|
|
78
78
|
writeStoredCredential: () => writeStoredCredential
|
|
79
79
|
});
|
|
80
|
-
import { homedir } from "node:os";
|
|
80
|
+
import { homedir as homedir2 } from "node:os";
|
|
81
81
|
import { join, dirname } from "node:path";
|
|
82
82
|
import {
|
|
83
|
-
existsSync,
|
|
84
|
-
mkdirSync,
|
|
83
|
+
existsSync as existsSync2,
|
|
84
|
+
mkdirSync as mkdirSync2,
|
|
85
85
|
readFileSync,
|
|
86
86
|
writeFileSync,
|
|
87
87
|
chmodSync,
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
function credentialPath(env2 = process.env) {
|
|
91
91
|
const override = env2["VO_MCP_CREDENTIALS_PATH"]?.trim();
|
|
92
92
|
if (override) return override;
|
|
93
|
-
return join(
|
|
93
|
+
return join(homedir2(), ".config", "vo-mcp", "credentials.json");
|
|
94
94
|
}
|
|
95
95
|
function keychainEnabled(env2, keychain) {
|
|
96
96
|
const disabled = (env2["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
|
|
@@ -119,7 +119,7 @@ function deserialize(raw) {
|
|
|
119
119
|
function readFromFile(env2) {
|
|
120
120
|
try {
|
|
121
121
|
const p = credentialPath(env2);
|
|
122
|
-
if (!
|
|
122
|
+
if (!existsSync2(p)) return null;
|
|
123
123
|
return deserialize(readFileSync(p, "utf8"));
|
|
124
124
|
} catch {
|
|
125
125
|
return null;
|
|
@@ -146,7 +146,7 @@ function deleteFile(env2) {
|
|
|
146
146
|
}
|
|
147
147
|
function writeToFile(payload, env2) {
|
|
148
148
|
const p = credentialPath(env2);
|
|
149
|
-
|
|
149
|
+
mkdirSync2(dirname(p), { recursive: true });
|
|
150
150
|
writeFileSync(p, `${JSON.stringify(payload, null, 2)}
|
|
151
151
|
`, { mode: 384 });
|
|
152
152
|
try {
|
|
@@ -505,7 +505,8 @@ async function runProcess(command, args = [], options = {}) {
|
|
|
505
505
|
});
|
|
506
506
|
}
|
|
507
507
|
async function commandExists(command, options = {}) {
|
|
508
|
-
const
|
|
508
|
+
const platform = options.platform || process.platform;
|
|
509
|
+
const checker = platform === "win32" ? "where" : "which";
|
|
509
510
|
const result = await (options.runner || runProcess)(checker, [command], {
|
|
510
511
|
timeoutMs: 1e4
|
|
511
512
|
});
|
|
@@ -841,9 +842,141 @@ var init_pnpm_canonical_health = __esm({
|
|
|
841
842
|
}
|
|
842
843
|
});
|
|
843
844
|
|
|
845
|
+
// src/runner/pnpm-command.mjs
|
|
846
|
+
import fs2 from "node:fs";
|
|
847
|
+
import path3 from "node:path";
|
|
848
|
+
function isWindowsDriveOrUnc(value) {
|
|
849
|
+
return WINDOWS_DRIVE_OR_UNC_RE.test(String(value || ""));
|
|
850
|
+
}
|
|
851
|
+
function portableDirname(value) {
|
|
852
|
+
if (isWindowsDriveOrUnc(value)) return path3.win32.dirname(value);
|
|
853
|
+
if (path3.posix.isAbsolute(value)) return path3.posix.dirname(value);
|
|
854
|
+
return path3.dirname(value);
|
|
855
|
+
}
|
|
856
|
+
function portableJoin(root, ...segments) {
|
|
857
|
+
if (isWindowsDriveOrUnc(root)) return path3.win32.join(root, ...segments);
|
|
858
|
+
if (path3.posix.isAbsolute(root)) return path3.posix.join(root, ...segments);
|
|
859
|
+
return path3.join(root, ...segments);
|
|
860
|
+
}
|
|
861
|
+
function readPackageManager(root) {
|
|
862
|
+
const packagePath = path3.join(root, "package.json");
|
|
863
|
+
if (!fs2.existsSync(packagePath)) return "";
|
|
864
|
+
try {
|
|
865
|
+
return String(JSON.parse(fs2.readFileSync(packagePath, "utf8"))?.packageManager || "").trim();
|
|
866
|
+
} catch {
|
|
867
|
+
return "";
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function validatedPnpmVersionToken(root) {
|
|
871
|
+
const raw = readPackageManager(root);
|
|
872
|
+
const match = /^pnpm@(.+)$/iu.exec(raw);
|
|
873
|
+
if (!match) return "";
|
|
874
|
+
const version = match[1].trim();
|
|
875
|
+
if (!version || /\s/u.test(version) || !SAFE_PNPM_VERSION_RE.test(version)) return "";
|
|
876
|
+
return version;
|
|
877
|
+
}
|
|
878
|
+
function pnpmSelector(root) {
|
|
879
|
+
const packageManager = readPackageManager(root);
|
|
880
|
+
const version = validatedPnpmVersionToken(root);
|
|
881
|
+
if (packageManager && !version) {
|
|
882
|
+
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path3.join(root, "package.json")}`);
|
|
883
|
+
}
|
|
884
|
+
return version ? `pnpm@${version}` : "pnpm";
|
|
885
|
+
}
|
|
886
|
+
function whereResults(result) {
|
|
887
|
+
if (result?.status !== 0) return [];
|
|
888
|
+
return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path3.win32.isAbsolute(line));
|
|
889
|
+
}
|
|
890
|
+
async function resolveWindowsNativeCommand(command, runner) {
|
|
891
|
+
const result = await runner("where", [command], { timeoutMs: 1e4 });
|
|
892
|
+
return whereResults(result).find((candidate) => WINDOWS_NATIVE_EXECUTABLE_RE.test(candidate)) || "";
|
|
893
|
+
}
|
|
894
|
+
function trustedCorepackCandidates(options) {
|
|
895
|
+
const env2 = options.env || process.env;
|
|
896
|
+
const execPath = options.execPath || process.execPath;
|
|
897
|
+
const roots = [portableDirname(execPath)];
|
|
898
|
+
for (const key of ["ProgramW6432", "ProgramFiles", "ProgramFiles(x86)"]) {
|
|
899
|
+
const programFiles = String(env2[key] || "").trim();
|
|
900
|
+
if (isWindowsDriveOrUnc(programFiles) || path3.posix.isAbsolute(programFiles)) {
|
|
901
|
+
roots.push(portableJoin(programFiles, "nodejs"));
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
return [...new Set(roots)].map((root) => portableJoin(root, "node_modules", "corepack", "dist", "corepack.js"));
|
|
905
|
+
}
|
|
906
|
+
function resolveTrustedCorepackJs(options) {
|
|
907
|
+
const existsSync7 = options.existsSync || fs2.existsSync;
|
|
908
|
+
return trustedCorepackCandidates(options).find((candidate) => existsSync7(candidate)) || "";
|
|
909
|
+
}
|
|
910
|
+
async function resolvePnpmInstallCommand(root, options = {}) {
|
|
911
|
+
const runner = options.runner || runProcess;
|
|
912
|
+
const platform = options.platform || process.platform;
|
|
913
|
+
const selector = pnpmSelector(root);
|
|
914
|
+
if (platform !== "win32") {
|
|
915
|
+
if (await commandExists("pnpm", { runner, platform })) {
|
|
916
|
+
return {
|
|
917
|
+
command: "pnpm",
|
|
918
|
+
args: [...DEFAULT_PNPM_INSTALL_ARGS],
|
|
919
|
+
displayCommand: ["pnpm", ...DEFAULT_PNPM_INSTALL_ARGS]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
if (await commandExists("corepack", { runner, platform })) {
|
|
923
|
+
return {
|
|
924
|
+
command: "corepack",
|
|
925
|
+
args: [selector, ...DEFAULT_PNPM_INSTALL_ARGS],
|
|
926
|
+
displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
throw new Error("[vo-mcp runner] pnpm hydration requires `pnpm` or `corepack` on PATH.");
|
|
930
|
+
}
|
|
931
|
+
const pnpmExecutable = await resolveWindowsNativeCommand("pnpm", runner);
|
|
932
|
+
if (pnpmExecutable) {
|
|
933
|
+
return {
|
|
934
|
+
command: pnpmExecutable,
|
|
935
|
+
args: [...DEFAULT_PNPM_INSTALL_ARGS],
|
|
936
|
+
displayCommand: ["pnpm", ...DEFAULT_PNPM_INSTALL_ARGS]
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
const corepackExecutable = await resolveWindowsNativeCommand("corepack", runner);
|
|
940
|
+
if (corepackExecutable) {
|
|
941
|
+
return {
|
|
942
|
+
command: corepackExecutable,
|
|
943
|
+
args: [selector, ...DEFAULT_PNPM_INSTALL_ARGS],
|
|
944
|
+
displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
const corepackJs = resolveTrustedCorepackJs(options);
|
|
948
|
+
if (corepackJs) {
|
|
949
|
+
return {
|
|
950
|
+
command: options.execPath || process.execPath,
|
|
951
|
+
args: [corepackJs, selector, ...DEFAULT_PNPM_INSTALL_ARGS],
|
|
952
|
+
displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
throw new Error(
|
|
956
|
+
"[vo-mcp runner] pnpm hydration on Windows requires a native pnpm/corepack executable or a trusted Corepack installation."
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
var DEFAULT_PNPM_INSTALL_ARGS, SAFE_PNPM_VERSION_RE, WINDOWS_NATIVE_EXECUTABLE_RE, WINDOWS_DRIVE_OR_UNC_RE;
|
|
960
|
+
var init_pnpm_command = __esm({
|
|
961
|
+
"src/runner/pnpm-command.mjs"() {
|
|
962
|
+
"use strict";
|
|
963
|
+
init_process_runner();
|
|
964
|
+
DEFAULT_PNPM_INSTALL_ARGS = Object.freeze([
|
|
965
|
+
"install",
|
|
966
|
+
"--frozen-lockfile",
|
|
967
|
+
"--prefer-offline",
|
|
968
|
+
"--ignore-scripts",
|
|
969
|
+
"--config.confirmModulesPurge=false"
|
|
970
|
+
]);
|
|
971
|
+
SAFE_PNPM_VERSION_RE = /^[0-9A-Za-z._+-]+$/u;
|
|
972
|
+
WINDOWS_NATIVE_EXECUTABLE_RE = /\.(?:com|exe)$/iu;
|
|
973
|
+
WINDOWS_DRIVE_OR_UNC_RE = /^(?:[A-Za-z]:[\\/]|\\\\)/u;
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
|
|
844
977
|
// src/runner/pnpm-materialize.mjs
|
|
845
978
|
import fsp4 from "node:fs/promises";
|
|
846
|
-
import
|
|
979
|
+
import path4 from "node:path";
|
|
847
980
|
function linkType() {
|
|
848
981
|
return process.platform === "win32" ? "junction" : "dir";
|
|
849
982
|
}
|
|
@@ -855,13 +988,13 @@ async function realpathOrThrow(target, fsApi) {
|
|
|
855
988
|
}
|
|
856
989
|
}
|
|
857
990
|
function shouldMapToWorktree(resolvedTarget, canonicalRoot) {
|
|
858
|
-
const relative =
|
|
991
|
+
const relative = path4.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
|
|
859
992
|
return Boolean(relative && !relative.startsWith("..") && !relative.split("/").includes("node_modules"));
|
|
860
993
|
}
|
|
861
994
|
async function resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, fsApi) {
|
|
862
995
|
const resolved = await realpathOrThrow(sourceEntry, fsApi);
|
|
863
996
|
if (!shouldMapToWorktree(resolved, canonicalRoot)) return resolved;
|
|
864
|
-
const mapped =
|
|
997
|
+
const mapped = path4.join(worktreeRoot, path4.relative(canonicalRoot, resolved));
|
|
865
998
|
if (!await fsApi.pathExists(mapped)) {
|
|
866
999
|
throw new Error(`[vo-mcp runner] task-local workspace target is missing for dependency link: ${mapped}`);
|
|
867
1000
|
}
|
|
@@ -872,7 +1005,7 @@ async function ensureLinkedDirectory(source, target, fsApi) {
|
|
|
872
1005
|
if (await fsApi.realpath(target) === await fsApi.realpath(source)) return;
|
|
873
1006
|
throw new Error(`[vo-mcp runner] refusing to overwrite existing dependency path: ${target}`);
|
|
874
1007
|
}
|
|
875
|
-
await fsApi.mkdir(
|
|
1008
|
+
await fsApi.mkdir(path4.dirname(target), { recursive: true });
|
|
876
1009
|
await fsApi.symlink(source, target, linkType());
|
|
877
1010
|
if (await fsApi.realpath(target) !== await fsApi.realpath(source)) {
|
|
878
1011
|
throw new Error(`[vo-mcp runner] dependency link validation failed for ${target}`);
|
|
@@ -886,8 +1019,8 @@ async function maybeYield2(state) {
|
|
|
886
1019
|
async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
887
1020
|
await fsApi.mkdir(targetDir, { recursive: true });
|
|
888
1021
|
for (const entry of await fsApi.readdir(sourceDir, { withFileTypes: true })) {
|
|
889
|
-
const source =
|
|
890
|
-
const target =
|
|
1022
|
+
const source = path4.join(sourceDir, entry.name);
|
|
1023
|
+
const target = path4.join(targetDir, entry.name);
|
|
891
1024
|
await maybeYield2(yieldState);
|
|
892
1025
|
if (entry.isDirectory()) {
|
|
893
1026
|
await copyDirRecursive(source, target, fsApi, yieldState);
|
|
@@ -899,17 +1032,17 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
|
899
1032
|
async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
|
|
900
1033
|
await options.beforeEntry?.(sourceEntry, targetEntry);
|
|
901
1034
|
const stat2 = await options.fsApi.lstat(sourceEntry);
|
|
902
|
-
if (stat2.isDirectory() && !stat2.isSymbolicLink() &&
|
|
1035
|
+
if (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry) === ".bin") {
|
|
903
1036
|
await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
|
|
904
1037
|
return;
|
|
905
1038
|
}
|
|
906
|
-
if (stat2.isDirectory() && !stat2.isSymbolicLink() &&
|
|
1039
|
+
if (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry).startsWith("@")) {
|
|
907
1040
|
await options.fsApi.mkdir(targetEntry, { recursive: true });
|
|
908
1041
|
for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
|
|
909
1042
|
await maybeYield2(options.yieldState);
|
|
910
1043
|
await materializeEntry(
|
|
911
|
-
|
|
912
|
-
|
|
1044
|
+
path4.join(sourceEntry, nested.name),
|
|
1045
|
+
path4.join(targetEntry, nested.name),
|
|
913
1046
|
canonicalRoot,
|
|
914
1047
|
worktreeRoot,
|
|
915
1048
|
options
|
|
@@ -922,7 +1055,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
|
|
|
922
1055
|
await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
|
|
923
1056
|
return;
|
|
924
1057
|
}
|
|
925
|
-
await options.fsApi.mkdir(
|
|
1058
|
+
await options.fsApi.mkdir(path4.dirname(targetEntry), { recursive: true });
|
|
926
1059
|
await options.fsApi.copyFile(sourceEntry, targetEntry);
|
|
927
1060
|
}
|
|
928
1061
|
function createPnpmFsApi(pathExists6) {
|
|
@@ -946,8 +1079,8 @@ async function materializeNodeModulesForest(sourceNodeModules, targetNodeModules
|
|
|
946
1079
|
for (const entry of await options.fsApi.readdir(sourceNodeModules, { withFileTypes: true })) {
|
|
947
1080
|
await maybeYield2(options.yieldState);
|
|
948
1081
|
await materializeEntry(
|
|
949
|
-
|
|
950
|
-
|
|
1082
|
+
path4.join(sourceNodeModules, entry.name),
|
|
1083
|
+
path4.join(targetNodeModules, entry.name),
|
|
951
1084
|
canonicalRoot,
|
|
952
1085
|
worktreeRoot,
|
|
953
1086
|
options
|
|
@@ -962,20 +1095,20 @@ var init_pnpm_materialize = __esm({
|
|
|
962
1095
|
|
|
963
1096
|
// src/runner/pnpm-hydration.mjs
|
|
964
1097
|
import { createHash } from "node:crypto";
|
|
965
|
-
import
|
|
1098
|
+
import fs3 from "node:fs";
|
|
966
1099
|
import fsp5 from "node:fs/promises";
|
|
967
|
-
import
|
|
1100
|
+
import path5 from "node:path";
|
|
968
1101
|
function hashText(text) {
|
|
969
1102
|
return createHash("sha256").update(String(text)).digest("hex");
|
|
970
1103
|
}
|
|
971
1104
|
function statePath(root) {
|
|
972
|
-
return
|
|
1105
|
+
return path5.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
|
|
973
1106
|
}
|
|
974
1107
|
function packageJson(root) {
|
|
975
|
-
const packagePath =
|
|
976
|
-
if (!
|
|
1108
|
+
const packagePath = path5.join(root, "package.json");
|
|
1109
|
+
if (!fs3.existsSync(packagePath)) return null;
|
|
977
1110
|
try {
|
|
978
|
-
return JSON.parse(
|
|
1111
|
+
return JSON.parse(fs3.readFileSync(packagePath, "utf8"));
|
|
979
1112
|
} catch {
|
|
980
1113
|
return null;
|
|
981
1114
|
}
|
|
@@ -989,23 +1122,23 @@ async function pathExists4(target) {
|
|
|
989
1122
|
}
|
|
990
1123
|
}
|
|
991
1124
|
function lockfileHash(root) {
|
|
992
|
-
const lockPath =
|
|
993
|
-
if (!
|
|
994
|
-
return hashText(
|
|
1125
|
+
const lockPath = path5.join(root, "pnpm-lock.yaml");
|
|
1126
|
+
if (!fs3.existsSync(lockPath)) return "";
|
|
1127
|
+
return hashText(fs3.readFileSync(lockPath, "utf8"));
|
|
995
1128
|
}
|
|
996
1129
|
function readHydrationState(root) {
|
|
997
1130
|
const file = statePath(root);
|
|
998
|
-
if (!
|
|
1131
|
+
if (!fs3.existsSync(file)) return null;
|
|
999
1132
|
try {
|
|
1000
|
-
return JSON.parse(
|
|
1133
|
+
return JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
1001
1134
|
} catch {
|
|
1002
1135
|
return null;
|
|
1003
1136
|
}
|
|
1004
1137
|
}
|
|
1005
1138
|
function writeHydrationState(root, state) {
|
|
1006
1139
|
const file = statePath(root);
|
|
1007
|
-
|
|
1008
|
-
|
|
1140
|
+
fs3.mkdirSync(path5.dirname(file), { recursive: true });
|
|
1141
|
+
fs3.writeFileSync(file, `${JSON.stringify({
|
|
1009
1142
|
...state,
|
|
1010
1143
|
stateVersion: 2,
|
|
1011
1144
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -1013,7 +1146,7 @@ function writeHydrationState(root, state) {
|
|
|
1013
1146
|
`, "utf8");
|
|
1014
1147
|
}
|
|
1015
1148
|
function voDepsStatePath(root) {
|
|
1016
|
-
return
|
|
1149
|
+
return path5.join(root, "node_modules", ".vo-deps-state.json");
|
|
1017
1150
|
}
|
|
1018
1151
|
async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
1019
1152
|
const marker = voDepsStatePath(root);
|
|
@@ -1023,7 +1156,7 @@ async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
|
1023
1156
|
} catch {
|
|
1024
1157
|
}
|
|
1025
1158
|
const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
|
|
1026
|
-
await fsApi.mkdir(
|
|
1159
|
+
await fsApi.mkdir(path5.dirname(marker), { recursive: true });
|
|
1027
1160
|
await fsApi.writeFile(temp, `${JSON.stringify({ lockfileHash: expectedHash, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
1028
1161
|
`, "utf8");
|
|
1029
1162
|
await fsApi.rename(temp, marker);
|
|
@@ -1043,14 +1176,6 @@ async function assertVoDepsState(root, expectedHash, fsApi = fsp5) {
|
|
|
1043
1176
|
}
|
|
1044
1177
|
throw new Error(`[vo-mcp runner] linked vo:deps state marker mismatch for ${root}`);
|
|
1045
1178
|
}
|
|
1046
|
-
function validatedPnpmVersionToken(root) {
|
|
1047
|
-
const raw = String(packageJson(root)?.packageManager || "").trim();
|
|
1048
|
-
const match = /^pnpm@(.+)$/iu.exec(raw);
|
|
1049
|
-
if (!match) return "";
|
|
1050
|
-
const version = match[1].trim();
|
|
1051
|
-
if (!version || /\s/u.test(version) || !SAFE_PNPM_VERSION_RE.test(version)) return "";
|
|
1052
|
-
return version;
|
|
1053
|
-
}
|
|
1054
1179
|
function workspacePatternsFromPackageJson(root) {
|
|
1055
1180
|
const workspaces = packageJson(root)?.workspaces;
|
|
1056
1181
|
if (Array.isArray(workspaces)) return workspaces.map(String);
|
|
@@ -1058,9 +1183,9 @@ function workspacePatternsFromPackageJson(root) {
|
|
|
1058
1183
|
return [];
|
|
1059
1184
|
}
|
|
1060
1185
|
function workspacePatternsFromPnpmWorkspace(root) {
|
|
1061
|
-
const workspacePath =
|
|
1062
|
-
if (!
|
|
1063
|
-
const lines =
|
|
1186
|
+
const workspacePath = path5.join(root, "pnpm-workspace.yaml");
|
|
1187
|
+
if (!fs3.existsSync(workspacePath)) return [];
|
|
1188
|
+
const lines = fs3.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
|
|
1064
1189
|
const patterns = [];
|
|
1065
1190
|
let inPackages = false;
|
|
1066
1191
|
for (const rawLine of lines) {
|
|
@@ -1117,16 +1242,16 @@ function collectPackageDirs(root, options = {}) {
|
|
|
1117
1242
|
while (stack.length > 0) {
|
|
1118
1243
|
const current = stack.pop();
|
|
1119
1244
|
if (!current) continue;
|
|
1120
|
-
const relativeDir =
|
|
1121
|
-
if (relativeDir &&
|
|
1245
|
+
const relativeDir = path5.relative(root, current.dir).replace(/\\/g, "/");
|
|
1246
|
+
if (relativeDir && fs3.existsSync(path5.join(current.dir, "package.json"))) {
|
|
1122
1247
|
found.push(relativeDir);
|
|
1123
1248
|
if (found.length >= maxDirs) break;
|
|
1124
1249
|
}
|
|
1125
1250
|
if (current.depth >= maxDepth) continue;
|
|
1126
|
-
for (const entry of
|
|
1251
|
+
for (const entry of fs3.readdirSync(current.dir, { withFileTypes: true })) {
|
|
1127
1252
|
if (!entry.isDirectory()) continue;
|
|
1128
1253
|
if (IGNORED_SCAN_DIRS.has(entry.name)) continue;
|
|
1129
|
-
stack.push({ dir:
|
|
1254
|
+
stack.push({ dir: path5.join(current.dir, entry.name), depth: current.depth + 1 });
|
|
1130
1255
|
}
|
|
1131
1256
|
}
|
|
1132
1257
|
return found.sort();
|
|
@@ -1136,35 +1261,22 @@ function discoverWorkspacePackageDirs(root, options = {}) {
|
|
|
1136
1261
|
if (patterns.length === 0) return [];
|
|
1137
1262
|
return collectPackageDirs(root, options).filter((relativeDir) => patterns.some((pattern) => matchesWorkspacePattern(relativeDir, pattern)));
|
|
1138
1263
|
}
|
|
1139
|
-
async function resolveInstallCommand(root, runner) {
|
|
1140
|
-
if (await commandExists("pnpm", { runner })) {
|
|
1141
|
-
return { command: "pnpm", args: [...DEFAULT_INSTALL_ARGS] };
|
|
1142
|
-
}
|
|
1143
|
-
if (await commandExists("corepack", { runner })) {
|
|
1144
|
-
const version = validatedPnpmVersionToken(root);
|
|
1145
|
-
if (String(packageJson(root)?.packageManager || "").trim() && !version) {
|
|
1146
|
-
throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path4.join(root, "package.json")}`);
|
|
1147
|
-
}
|
|
1148
|
-
return { command: "corepack", args: [version ? `pnpm@${version}` : "pnpm", ...DEFAULT_INSTALL_ARGS] };
|
|
1149
|
-
}
|
|
1150
|
-
throw new Error("[vo-mcp runner] pnpm hydration requires `pnpm` or `corepack` on PATH.");
|
|
1151
|
-
}
|
|
1152
1264
|
async function runInstall(root, options = {}) {
|
|
1153
1265
|
const runner = options.runner || runProcess;
|
|
1154
|
-
const tuple = await
|
|
1266
|
+
const tuple = await resolvePnpmInstallCommand(root, { ...options, runner });
|
|
1155
1267
|
const result = await runner(tuple.command, tuple.args, {
|
|
1156
1268
|
cwd: root,
|
|
1157
1269
|
timeoutMs: options.timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS
|
|
1158
1270
|
});
|
|
1159
1271
|
if (result.status !== 0) {
|
|
1160
1272
|
throw new Error(
|
|
1161
|
-
`[vo-mcp runner] pnpm hydration failed for ${root}: ${summarizeProcessFailure(result)} (command: ${
|
|
1273
|
+
`[vo-mcp runner] pnpm hydration failed for ${root}: ${summarizeProcessFailure(result)} (command: ${tuple.displayCommand.join(" ")})`
|
|
1162
1274
|
);
|
|
1163
1275
|
}
|
|
1164
1276
|
}
|
|
1165
1277
|
async function hasReadyNodeModules(root) {
|
|
1166
|
-
const nodeModules =
|
|
1167
|
-
return await pathExists4(
|
|
1278
|
+
const nodeModules = path5.join(root, "node_modules");
|
|
1279
|
+
return await pathExists4(path5.join(nodeModules, ".modules.yaml")) || await pathExists4(path5.join(nodeModules, ".pnpm"));
|
|
1168
1280
|
}
|
|
1169
1281
|
function hydrationFsApi(overrides = {}) {
|
|
1170
1282
|
return { ...createPnpmFsApi(pathExists4), ...overrides };
|
|
@@ -1201,7 +1313,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
1201
1313
|
expectedHash: hash,
|
|
1202
1314
|
requireMarker: false
|
|
1203
1315
|
});
|
|
1204
|
-
if (!health.healthy && await fsApi.pathExists(
|
|
1316
|
+
if (!health.healthy && await fsApi.pathExists(path5.join(root, "node_modules"))) {
|
|
1205
1317
|
quarantine = await quarantineCanonicalNodeModules(root, health.issues, {
|
|
1206
1318
|
fsApi,
|
|
1207
1319
|
logger: options.logger
|
|
@@ -1213,7 +1325,7 @@ async function ensurePnpmHydration(root, options = {}) {
|
|
|
1213
1325
|
}
|
|
1214
1326
|
const linkedWorkspaceDirs = [];
|
|
1215
1327
|
for (const relativeDir of workspaceDirs) {
|
|
1216
|
-
if (await pathExists4(
|
|
1328
|
+
if (await pathExists4(path5.join(root, relativeDir, "node_modules"))) {
|
|
1217
1329
|
linkedWorkspaceDirs.push(relativeDir);
|
|
1218
1330
|
}
|
|
1219
1331
|
}
|
|
@@ -1249,18 +1361,18 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
|
|
|
1249
1361
|
yieldEvery: Math.max(1, options.yieldEvery ?? DEFAULT_YIELD_EVERY2)
|
|
1250
1362
|
}
|
|
1251
1363
|
};
|
|
1252
|
-
recordOwnedNodeModulesRoot(dependencyOwnership,
|
|
1364
|
+
recordOwnedNodeModulesRoot(dependencyOwnership, path5.join(worktreeDir, "node_modules"));
|
|
1253
1365
|
await materializeNodeModulesForest(
|
|
1254
|
-
|
|
1255
|
-
|
|
1366
|
+
path5.join(root, "node_modules"),
|
|
1367
|
+
path5.join(worktreeDir, "node_modules"),
|
|
1256
1368
|
root,
|
|
1257
1369
|
worktreeDir,
|
|
1258
1370
|
materializeOptions
|
|
1259
1371
|
);
|
|
1260
1372
|
for (const relativeDir of hydration.linkedWorkspaceDirs) {
|
|
1261
|
-
const sourceNodeModules =
|
|
1262
|
-
const targetNodeModules =
|
|
1263
|
-
if (!await fsApi.pathExists(
|
|
1373
|
+
const sourceNodeModules = path5.join(root, relativeDir, "node_modules");
|
|
1374
|
+
const targetNodeModules = path5.join(worktreeDir, relativeDir, "node_modules");
|
|
1375
|
+
if (!await fsApi.pathExists(path5.join(worktreeDir, relativeDir))) continue;
|
|
1264
1376
|
recordOwnedNodeModulesRoot(dependencyOwnership, targetNodeModules);
|
|
1265
1377
|
await materializeNodeModulesForest(sourceNodeModules, targetNodeModules, root, worktreeDir, materializeOptions);
|
|
1266
1378
|
}
|
|
@@ -1271,22 +1383,16 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
|
|
|
1271
1383
|
workspaceCount: hydration.linkedWorkspaceDirs.length
|
|
1272
1384
|
};
|
|
1273
1385
|
}
|
|
1274
|
-
var DEFAULT_INSTALL_TIMEOUT_MS,
|
|
1386
|
+
var DEFAULT_INSTALL_TIMEOUT_MS, DEFAULT_YIELD_EVERY2, IGNORED_SCAN_DIRS;
|
|
1275
1387
|
var init_pnpm_hydration = __esm({
|
|
1276
1388
|
"src/runner/pnpm-hydration.mjs"() {
|
|
1277
1389
|
"use strict";
|
|
1278
1390
|
init_pnpm_canonical_health();
|
|
1391
|
+
init_pnpm_command();
|
|
1279
1392
|
init_pnpm_materialize();
|
|
1280
1393
|
init_process_runner();
|
|
1281
1394
|
init_pnpm_link_detach();
|
|
1282
1395
|
DEFAULT_INSTALL_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
1283
|
-
DEFAULT_INSTALL_ARGS = Object.freeze([
|
|
1284
|
-
"install",
|
|
1285
|
-
"--frozen-lockfile",
|
|
1286
|
-
"--prefer-offline",
|
|
1287
|
-
"--ignore-scripts",
|
|
1288
|
-
"--config.confirmModulesPurge=false"
|
|
1289
|
-
]);
|
|
1290
1396
|
DEFAULT_YIELD_EVERY2 = 25;
|
|
1291
1397
|
IGNORED_SCAN_DIRS = /* @__PURE__ */ new Set([
|
|
1292
1398
|
".agent-worktrees",
|
|
@@ -1299,34 +1405,33 @@ var init_pnpm_hydration = __esm({
|
|
|
1299
1405
|
"dist",
|
|
1300
1406
|
"node_modules"
|
|
1301
1407
|
]);
|
|
1302
|
-
SAFE_PNPM_VERSION_RE = /^[0-9A-Za-z._+-]+$/u;
|
|
1303
1408
|
}
|
|
1304
1409
|
});
|
|
1305
1410
|
|
|
1306
1411
|
// src/runner/worktree-paths.mjs
|
|
1307
1412
|
import { createHash as createHash2 } from "node:crypto";
|
|
1308
|
-
import
|
|
1413
|
+
import path6 from "node:path";
|
|
1309
1414
|
function samePath(left, right) {
|
|
1310
|
-
const a =
|
|
1311
|
-
const b =
|
|
1415
|
+
const a = path6.resolve(left);
|
|
1416
|
+
const b = path6.resolve(right);
|
|
1312
1417
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
1313
1418
|
}
|
|
1314
1419
|
function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
|
|
1315
|
-
const canonicalRoot =
|
|
1420
|
+
const canonicalRoot = path6.resolve(root);
|
|
1316
1421
|
if (clonesRootDir) {
|
|
1317
|
-
const clonePool =
|
|
1318
|
-
if (samePath(
|
|
1319
|
-
return
|
|
1422
|
+
const clonePool = path6.resolve(clonesRootDir);
|
|
1423
|
+
if (samePath(path6.dirname(canonicalRoot), clonePool)) {
|
|
1424
|
+
return path6.join(clonePool, ".agent-worktrees", path6.basename(canonicalRoot));
|
|
1320
1425
|
}
|
|
1321
1426
|
}
|
|
1322
|
-
return
|
|
1427
|
+
return path6.join(canonicalRoot, ".agent-worktrees");
|
|
1323
1428
|
}
|
|
1324
1429
|
function worktreeDirForName(root, worktreeName, options = {}) {
|
|
1325
1430
|
const leaf = createHash2("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
|
|
1326
|
-
return
|
|
1431
|
+
return path6.join(worktreePoolForRoot(root, options), leaf);
|
|
1327
1432
|
}
|
|
1328
1433
|
function recoveryLedgerPathForRoot(root, options = {}) {
|
|
1329
|
-
return
|
|
1434
|
+
return path6.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
|
|
1330
1435
|
}
|
|
1331
1436
|
var init_worktree_paths = __esm({
|
|
1332
1437
|
"src/runner/worktree-paths.mjs"() {
|
|
@@ -1335,9 +1440,9 @@ var init_worktree_paths = __esm({
|
|
|
1335
1440
|
});
|
|
1336
1441
|
|
|
1337
1442
|
// src/runner/worktree-cleanup.mjs
|
|
1338
|
-
import
|
|
1443
|
+
import fs4 from "node:fs";
|
|
1339
1444
|
import fsp6 from "node:fs/promises";
|
|
1340
|
-
import
|
|
1445
|
+
import path7 from "node:path";
|
|
1341
1446
|
function stateFromEntry(entry) {
|
|
1342
1447
|
return {
|
|
1343
1448
|
root: entry.root,
|
|
@@ -1378,14 +1483,14 @@ function pruneSuccessfulStates(nowMs = Date.now()) {
|
|
|
1378
1483
|
function assertTrackedCleanupPath(entry) {
|
|
1379
1484
|
const poolRoot = worktreePoolForRoot(entry.root);
|
|
1380
1485
|
const expected = worktreeDirForName(entry.root, entry.worktreeName);
|
|
1381
|
-
const resolvedPool =
|
|
1382
|
-
const resolvedTarget =
|
|
1383
|
-
if (!resolvedTarget.startsWith(`${resolvedPool}${
|
|
1486
|
+
const resolvedPool = path7.resolve(poolRoot);
|
|
1487
|
+
const resolvedTarget = path7.resolve(entry.worktreeDir);
|
|
1488
|
+
if (!resolvedTarget.startsWith(`${resolvedPool}${path7.sep}`)) {
|
|
1384
1489
|
const error = new Error(`cleanup refused outside managed pool: ${entry.worktreeDir}`);
|
|
1385
1490
|
error.cleanupFatal = true;
|
|
1386
1491
|
throw error;
|
|
1387
1492
|
}
|
|
1388
|
-
if (resolvedTarget !==
|
|
1493
|
+
if (resolvedTarget !== path7.resolve(expected)) {
|
|
1389
1494
|
const error = new Error(`cleanup refused for unexpected tracked path: ${entry.worktreeDir}`);
|
|
1390
1495
|
error.cleanupFatal = true;
|
|
1391
1496
|
throw error;
|
|
@@ -1399,8 +1504,8 @@ async function worktreeStillRegistered2(root, worktreeDir, gitRunner) {
|
|
|
1399
1504
|
if (result.status !== 0) {
|
|
1400
1505
|
throw new Error(`git worktree list --porcelain failed during cleanup verification: ${summarizeProcessFailure(result)}`);
|
|
1401
1506
|
}
|
|
1402
|
-
const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) =>
|
|
1403
|
-
return registered.includes(
|
|
1507
|
+
const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path7.resolve(line.slice("worktree ".length).trim()));
|
|
1508
|
+
return registered.includes(path7.resolve(worktreeDir));
|
|
1404
1509
|
}
|
|
1405
1510
|
function cleanupBackoff(attempt) {
|
|
1406
1511
|
return 250 * attempt;
|
|
@@ -1419,7 +1524,7 @@ async function runCleanupCycle(entry, options) {
|
|
|
1419
1524
|
await (options.assertDetachedDependencyLinks || assertDetachedDependencyLinks)(entry.dependencyOwnership);
|
|
1420
1525
|
}
|
|
1421
1526
|
const gitRunner = options.gitRunner || runProcess;
|
|
1422
|
-
const pathExists6 = options.pathExists || ((target) =>
|
|
1527
|
+
const pathExists6 = options.pathExists || ((target) => fs4.existsSync(target));
|
|
1423
1528
|
const removeResult = await gitRunner("git", ["worktree", "remove", "--force", entry.worktreeDir], {
|
|
1424
1529
|
cwd: entry.root,
|
|
1425
1530
|
timeoutMs: 12e4
|
|
@@ -1510,7 +1615,7 @@ function scheduleTrackedCleanup(entry, options = {}) {
|
|
|
1510
1615
|
}
|
|
1511
1616
|
function pendingCleanupDirs() {
|
|
1512
1617
|
return new Set(
|
|
1513
|
-
[...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) =>
|
|
1618
|
+
[...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path7.resolve(state.worktreeDir))
|
|
1514
1619
|
);
|
|
1515
1620
|
}
|
|
1516
1621
|
var CLEANUP_STATES, CLEANUP_PROMISES, SUCCESS_HISTORY_LIMIT, SUCCESS_HISTORY_TTL_MS, DEFAULT_CLEANUP_ATTEMPTS;
|
|
@@ -1529,15 +1634,15 @@ var init_worktree_cleanup = __esm({
|
|
|
1529
1634
|
});
|
|
1530
1635
|
|
|
1531
1636
|
// src/runner/task-root-prepare.mjs
|
|
1532
|
-
import
|
|
1637
|
+
import fs5 from "node:fs";
|
|
1533
1638
|
import fsp7 from "node:fs/promises";
|
|
1534
|
-
import
|
|
1639
|
+
import path8 from "node:path";
|
|
1535
1640
|
function prepLockDir(root) {
|
|
1536
|
-
return
|
|
1641
|
+
return path8.join(root, ".agent-worktrees", "runner-root-prep.lock");
|
|
1537
1642
|
}
|
|
1538
1643
|
function readLockMeta(lockDir) {
|
|
1539
1644
|
try {
|
|
1540
|
-
return JSON.parse(
|
|
1645
|
+
return JSON.parse(fs5.readFileSync(path8.join(lockDir, "owner.json"), "utf8"));
|
|
1541
1646
|
} catch {
|
|
1542
1647
|
return null;
|
|
1543
1648
|
}
|
|
@@ -1557,13 +1662,13 @@ async function acquirePrepLock(root, options = {}) {
|
|
|
1557
1662
|
const staleMs = options.lockStaleMs ?? PREP_LOCK_STALE_MS;
|
|
1558
1663
|
const sleep3 = options.sleep || sleepMs;
|
|
1559
1664
|
const lockDir = prepLockDir(root);
|
|
1560
|
-
const ownerPath =
|
|
1665
|
+
const ownerPath = path8.join(lockDir, "owner.json");
|
|
1561
1666
|
const deadline = nowMs() + waitMs;
|
|
1562
|
-
|
|
1667
|
+
fs5.mkdirSync(path8.dirname(lockDir), { recursive: true });
|
|
1563
1668
|
for (; ; ) {
|
|
1564
1669
|
try {
|
|
1565
|
-
|
|
1566
|
-
|
|
1670
|
+
fs5.mkdirSync(lockDir);
|
|
1671
|
+
fs5.writeFileSync(ownerPath, `${JSON.stringify({
|
|
1567
1672
|
pid: process.pid,
|
|
1568
1673
|
createdAt: new Date(nowMs()).toISOString(),
|
|
1569
1674
|
root
|
|
@@ -1609,16 +1714,16 @@ async function gitText(root, args, options = {}) {
|
|
|
1609
1714
|
}
|
|
1610
1715
|
function canonicalRecoveryDir(root, options = {}) {
|
|
1611
1716
|
const now = options.now || (() => /* @__PURE__ */ new Date());
|
|
1612
|
-
return
|
|
1613
|
-
options.managedPool ||
|
|
1717
|
+
return path8.join(
|
|
1718
|
+
options.managedPool || path8.join(root, ".agent-worktrees"),
|
|
1614
1719
|
".canonical-recovery",
|
|
1615
1720
|
`preexisting-${now().toISOString().replace(/[:.]/gu, "-")}`
|
|
1616
1721
|
);
|
|
1617
1722
|
}
|
|
1618
1723
|
function canonicalPath(root, relative) {
|
|
1619
|
-
const resolvedRoot =
|
|
1620
|
-
const target =
|
|
1621
|
-
const prefix = `${resolvedRoot}${
|
|
1724
|
+
const resolvedRoot = path8.resolve(root);
|
|
1725
|
+
const target = path8.resolve(root, relative);
|
|
1726
|
+
const prefix = `${resolvedRoot}${path8.sep}`;
|
|
1622
1727
|
if (!target.startsWith(prefix)) {
|
|
1623
1728
|
throw new Error(`canonical recovery path escaped the runner clone: ${relative}`);
|
|
1624
1729
|
}
|
|
@@ -1646,7 +1751,7 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
|
|
|
1646
1751
|
if (patchResult.status !== 0) {
|
|
1647
1752
|
throw new Error(`could not preserve canonical tracked changes: ${summarizeProcessFailure(patchResult)}`);
|
|
1648
1753
|
}
|
|
1649
|
-
await fsp7.writeFile(
|
|
1754
|
+
await fsp7.writeFile(path8.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
|
|
1650
1755
|
const symlinks = [];
|
|
1651
1756
|
for (const relative of paths.untracked) {
|
|
1652
1757
|
const source = canonicalPath(root, relative);
|
|
@@ -1658,13 +1763,13 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
|
|
|
1658
1763
|
if (!stat2.isFile()) {
|
|
1659
1764
|
throw new Error(`canonical recovery refuses unsupported untracked entry: ${relative}`);
|
|
1660
1765
|
}
|
|
1661
|
-
const target = canonicalPath(
|
|
1662
|
-
await fsp7.mkdir(
|
|
1766
|
+
const target = canonicalPath(path8.join(quarantineDir, "untracked"), relative);
|
|
1767
|
+
await fsp7.mkdir(path8.dirname(target), { recursive: true });
|
|
1663
1768
|
await fsp7.copyFile(source, target);
|
|
1664
1769
|
}
|
|
1665
|
-
await fsp7.writeFile(
|
|
1770
|
+
await fsp7.writeFile(path8.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
1666
1771
|
recoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1667
|
-
canonicalRoot:
|
|
1772
|
+
canonicalRoot: path8.resolve(root),
|
|
1668
1773
|
canonicalHead: headSha,
|
|
1669
1774
|
tracked: paths.tracked,
|
|
1670
1775
|
untracked: paths.untracked,
|
|
@@ -1736,19 +1841,19 @@ async function registeredWorktreeDirs(root, options = {}) {
|
|
|
1736
1841
|
throw new Error(`git worktree list --porcelain failed while checking managed residue: ${summarizeProcessFailure(listed)}`);
|
|
1737
1842
|
}
|
|
1738
1843
|
return new Set(
|
|
1739
|
-
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) =>
|
|
1844
|
+
String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path8.resolve(line.slice("worktree ".length).trim()))
|
|
1740
1845
|
);
|
|
1741
1846
|
}
|
|
1742
1847
|
async function reportLegacyResiduals(root, options = {}) {
|
|
1743
|
-
const managedRoot =
|
|
1744
|
-
if (!
|
|
1848
|
+
const managedRoot = path8.join(root, ".agent-worktrees");
|
|
1849
|
+
if (!fs5.existsSync(managedRoot)) return [];
|
|
1745
1850
|
const registered = await registeredWorktreeDirs(root, options);
|
|
1746
1851
|
const pending = pendingCleanupDirs();
|
|
1747
1852
|
const found = [];
|
|
1748
|
-
for (const entry of
|
|
1853
|
+
for (const entry of fs5.readdirSync(managedRoot, { withFileTypes: true })) {
|
|
1749
1854
|
if (!entry.isDirectory()) continue;
|
|
1750
1855
|
if (shouldIgnoreManagedEntry(entry.name)) continue;
|
|
1751
|
-
const absolute =
|
|
1856
|
+
const absolute = path8.resolve(path8.join(managedRoot, entry.name));
|
|
1752
1857
|
if (registered.has(absolute)) continue;
|
|
1753
1858
|
if (pending.has(absolute)) continue;
|
|
1754
1859
|
found.push(absolute);
|
|
@@ -1781,6 +1886,7 @@ var init_task_root_prepare = __esm({
|
|
|
1781
1886
|
"src/runner/task-root-prepare.mjs"() {
|
|
1782
1887
|
"use strict";
|
|
1783
1888
|
init_pnpm_hydration();
|
|
1889
|
+
init_pnpm_command();
|
|
1784
1890
|
init_process_runner();
|
|
1785
1891
|
init_worktree_cleanup();
|
|
1786
1892
|
PREP_LOCK_WAIT_MS = 20 * 60 * 1e3;
|
|
@@ -1816,7 +1922,7 @@ var init_worktree_github_auth = __esm({
|
|
|
1816
1922
|
|
|
1817
1923
|
// src/runner/worktree-recovery-start.mjs
|
|
1818
1924
|
import fsp8 from "node:fs/promises";
|
|
1819
|
-
import
|
|
1925
|
+
import path9 from "node:path";
|
|
1820
1926
|
async function recordWorktreeStarted(worktreeTarget, meta = {}) {
|
|
1821
1927
|
if (!worktreeTarget?.worktreeName || !meta.taskId) return null;
|
|
1822
1928
|
const entry = {
|
|
@@ -1831,7 +1937,7 @@ async function recordWorktreeStarted(worktreeTarget, meta = {}) {
|
|
|
1831
1937
|
reason: "worktree allocated before execution"
|
|
1832
1938
|
};
|
|
1833
1939
|
const ledger = recoveryLedgerPathForRoot(worktreeTarget.root || process.cwd());
|
|
1834
|
-
await fsp8.mkdir(
|
|
1940
|
+
await fsp8.mkdir(path9.dirname(ledger), { recursive: true });
|
|
1835
1941
|
await fsp8.appendFile(ledger, `${JSON.stringify(entry)}
|
|
1836
1942
|
`, "utf8");
|
|
1837
1943
|
return { entry, ledger };
|
|
@@ -1844,9 +1950,9 @@ var init_worktree_recovery_start = __esm({
|
|
|
1844
1950
|
});
|
|
1845
1951
|
|
|
1846
1952
|
// src/runner/worktree-helper.mjs
|
|
1847
|
-
import
|
|
1953
|
+
import fs6 from "node:fs";
|
|
1848
1954
|
import fsp9 from "node:fs/promises";
|
|
1849
|
-
import
|
|
1955
|
+
import path10 from "node:path";
|
|
1850
1956
|
function repoRoot() {
|
|
1851
1957
|
return process.env.VO_CODE_RUNNER_REPO || process.cwd();
|
|
1852
1958
|
}
|
|
@@ -1862,7 +1968,7 @@ function cloneDirForSlug(repoSlug, clonesRootDir) {
|
|
|
1862
1968
|
const [owner, name] = String(repoSlug).split("/");
|
|
1863
1969
|
if (owner === "." || owner === ".." || name === "." || name === "..") return null;
|
|
1864
1970
|
if (owner.startsWith("-") || name.startsWith("-")) return null;
|
|
1865
|
-
return
|
|
1971
|
+
return path10.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
|
|
1866
1972
|
}
|
|
1867
1973
|
function cloneLockDir(dir) {
|
|
1868
1974
|
return `${dir}.clone-lock`;
|
|
@@ -1877,7 +1983,7 @@ async function pathExists5(target) {
|
|
|
1877
1983
|
}
|
|
1878
1984
|
async function readLockMeta2(lockDir) {
|
|
1879
1985
|
try {
|
|
1880
|
-
return JSON.parse(await fsp9.readFile(
|
|
1986
|
+
return JSON.parse(await fsp9.readFile(path10.join(lockDir, "owner.json"), "utf8"));
|
|
1881
1987
|
} catch {
|
|
1882
1988
|
return null;
|
|
1883
1989
|
}
|
|
@@ -1898,11 +2004,11 @@ async function acquireCloneLock(dir, options = {}) {
|
|
|
1898
2004
|
const sleep3 = options.sleep || sleepMs;
|
|
1899
2005
|
const lockDir = cloneLockDir(dir);
|
|
1900
2006
|
const deadline = nowMs() + waitMs;
|
|
1901
|
-
await fsp9.mkdir(
|
|
2007
|
+
await fsp9.mkdir(path10.dirname(lockDir), { recursive: true });
|
|
1902
2008
|
for (; ; ) {
|
|
1903
2009
|
try {
|
|
1904
2010
|
await fsp9.mkdir(lockDir);
|
|
1905
|
-
await fsp9.writeFile(
|
|
2011
|
+
await fsp9.writeFile(path10.join(lockDir, "owner.json"), `${JSON.stringify({
|
|
1906
2012
|
pid: process.pid,
|
|
1907
2013
|
createdAt: new Date(nowMs()).toISOString(),
|
|
1908
2014
|
dir
|
|
@@ -1931,7 +2037,7 @@ async function acquireCloneLock(dir, options = {}) {
|
|
|
1931
2037
|
}
|
|
1932
2038
|
}
|
|
1933
2039
|
async function isUsableGitClone(dir, runner = runProcess) {
|
|
1934
|
-
if (!await pathExists5(
|
|
2040
|
+
if (!await pathExists5(path10.join(dir, ".git"))) return false;
|
|
1935
2041
|
const result = await runner("git", ["-C", dir, "rev-parse", "HEAD"], { timeoutMs: 1e4 });
|
|
1936
2042
|
return result.status === 0 && Boolean(String(result.stdout || "").trim());
|
|
1937
2043
|
}
|
|
@@ -1952,7 +2058,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
1952
2058
|
const maxAttempts = options.maxAttempts || 5;
|
|
1953
2059
|
const raceWaitMs = options.raceWaitMs ?? 1e4;
|
|
1954
2060
|
let lastError = null;
|
|
1955
|
-
await fsp9.mkdir(
|
|
2061
|
+
await fsp9.mkdir(path10.dirname(dir), { recursive: true });
|
|
1956
2062
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1957
2063
|
if (await pathExists5(dir)) {
|
|
1958
2064
|
if (await waitForUsableClone(dir, runner, sleep3, raceWaitMs)) return dir;
|
|
@@ -1964,7 +2070,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
1964
2070
|
["clone", "--no-tags", `https://github.com/${owner}/${name}.git`, tmpDir],
|
|
1965
2071
|
{ timeoutMs: 6e5, env: githubGitAuthEnv(options.githubToken) }
|
|
1966
2072
|
);
|
|
1967
|
-
if (clone.status !== 0 || !await pathExists5(
|
|
2073
|
+
if (clone.status !== 0 || !await pathExists5(path10.join(tmpDir, ".git"))) {
|
|
1968
2074
|
await fsp9.rm(tmpDir, { recursive: true, force: true });
|
|
1969
2075
|
lastError = new Error(`[vo-mcp runner] clone failed for ${repoSlug}: ${describeGitFailure(clone)}`);
|
|
1970
2076
|
continue;
|
|
@@ -1991,7 +2097,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
|
|
|
1991
2097
|
}
|
|
1992
2098
|
async function resolveTaskRoot(repoSlug, options = {}) {
|
|
1993
2099
|
const root = clonesRoot();
|
|
1994
|
-
if (root && !
|
|
2100
|
+
if (root && !path10.isAbsolute(root)) {
|
|
1995
2101
|
throw new Error(`[vo-mcp runner] VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${root}')`);
|
|
1996
2102
|
}
|
|
1997
2103
|
const dir = cloneDirForSlug(repoSlug, root);
|
|
@@ -2040,7 +2146,7 @@ async function createFixWorktree(kind, error = {}, options = {}) {
|
|
|
2040
2146
|
const dependencyOwnership = createDependencyOwnershipTracker(worktreeDir);
|
|
2041
2147
|
const prep = await prepare(root, {
|
|
2042
2148
|
recoverDirtyCanonical: multiRepo,
|
|
2043
|
-
managedPool:
|
|
2149
|
+
managedPool: path10.dirname(worktreeDir)
|
|
2044
2150
|
});
|
|
2045
2151
|
await processRunner("git", ["config", "core.longpaths", "true"], { cwd: root, timeoutMs: 3e4 });
|
|
2046
2152
|
const add = await addWorktree({ root, branchName, worktreeDir });
|
|
@@ -2106,8 +2212,8 @@ function preserveFailedWorktree(worktreeName, meta = {}) {
|
|
|
2106
2212
|
};
|
|
2107
2213
|
try {
|
|
2108
2214
|
const ledger = recoveryLedgerPathForRoot(root);
|
|
2109
|
-
|
|
2110
|
-
|
|
2215
|
+
fs6.mkdirSync(path10.dirname(ledger), { recursive: true });
|
|
2216
|
+
fs6.appendFileSync(ledger, `${JSON.stringify(entry)}
|
|
2111
2217
|
`, "utf8");
|
|
2112
2218
|
console.error(`[vo-mcp runner] Preserved worktree ${worktreeName} (reason: ${entry.reason})`);
|
|
2113
2219
|
} catch (error) {
|
|
@@ -2152,9 +2258,9 @@ function partitionRecoveryLedger(lines, { nowMs, ttlMs = PRESERVED_WORKTREE_TTL_
|
|
|
2152
2258
|
return decisions;
|
|
2153
2259
|
}
|
|
2154
2260
|
function isManagedPreservedDir(dir, root = repoRoot()) {
|
|
2155
|
-
const normalized =
|
|
2156
|
-
const pool =
|
|
2157
|
-
return normalized.startsWith(pool +
|
|
2261
|
+
const normalized = path10.resolve(String(dir || ""));
|
|
2262
|
+
const pool = path10.resolve(worktreePoolForRoot(root));
|
|
2263
|
+
return normalized.startsWith(pool + path10.sep);
|
|
2158
2264
|
}
|
|
2159
2265
|
function mergeAppendedSinceRead(originalRaw, currentRaw, keptLines) {
|
|
2160
2266
|
const originalSet = new Set(String(originalRaw || "").split(/\r?\n/u).map((l) => l.trim()).filter(Boolean));
|
|
@@ -2173,15 +2279,15 @@ function writeLedgerAtomic(ledger, lines, logger) {
|
|
|
2173
2279
|
const payload = lines.length ? `${lines.join("\n")}
|
|
2174
2280
|
` : "";
|
|
2175
2281
|
const tmp = `${ledger}.tmp-${process.pid}-${Date.now()}`;
|
|
2176
|
-
|
|
2282
|
+
fs6.writeFileSync(tmp, payload, "utf8");
|
|
2177
2283
|
for (let attempt = 1; ; attempt += 1) {
|
|
2178
2284
|
try {
|
|
2179
|
-
|
|
2285
|
+
fs6.renameSync(tmp, ledger);
|
|
2180
2286
|
return true;
|
|
2181
2287
|
} catch (error) {
|
|
2182
2288
|
if (attempt >= 3) {
|
|
2183
2289
|
try {
|
|
2184
|
-
|
|
2290
|
+
fs6.rmSync(tmp, { force: true });
|
|
2185
2291
|
} catch {
|
|
2186
2292
|
}
|
|
2187
2293
|
logger(`[vo-mcp runner] GC could not compact recovery ledger: ${String(error?.message || error)}`);
|
|
@@ -2203,14 +2309,14 @@ async function sweepPreservedWorktrees({
|
|
|
2203
2309
|
const ledger = recoveryLedgerPathForRoot(root);
|
|
2204
2310
|
let raw;
|
|
2205
2311
|
try {
|
|
2206
|
-
raw =
|
|
2312
|
+
raw = fs6.readFileSync(ledger, "utf8");
|
|
2207
2313
|
} catch {
|
|
2208
2314
|
return { removed: 0, compacted: 0 };
|
|
2209
2315
|
}
|
|
2210
2316
|
const decisions = partitionRecoveryLedger(raw.split(/\r?\n/u), {
|
|
2211
2317
|
nowMs,
|
|
2212
2318
|
ttlMs,
|
|
2213
|
-
dirExists: (dir) =>
|
|
2319
|
+
dirExists: (dir) => fs6.existsSync(dir)
|
|
2214
2320
|
});
|
|
2215
2321
|
let removed = 0;
|
|
2216
2322
|
let compacted = 0;
|
|
@@ -2250,7 +2356,7 @@ async function sweepPreservedWorktrees({
|
|
|
2250
2356
|
if (compacted > 0) {
|
|
2251
2357
|
let currentRaw = raw;
|
|
2252
2358
|
try {
|
|
2253
|
-
currentRaw =
|
|
2359
|
+
currentRaw = fs6.readFileSync(ledger, "utf8");
|
|
2254
2360
|
} catch {
|
|
2255
2361
|
}
|
|
2256
2362
|
writeLedgerAtomic(ledger, mergeAppendedSinceRead(raw, currentRaw, output), logger);
|
|
@@ -2508,11 +2614,11 @@ function createControlPlaneClient({
|
|
|
2508
2614
|
const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
|
|
2509
2615
|
if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
2510
2616
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
2511
|
-
async function req(method,
|
|
2617
|
+
async function req(method, path20, body, { timeoutMs } = {}) {
|
|
2512
2618
|
const bearer = await resolveBearer(env2);
|
|
2513
2619
|
const controller = timeoutMs ? new AbortController() : null;
|
|
2514
2620
|
let timeoutId;
|
|
2515
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
2621
|
+
const request = Promise.resolve(fetchImpl(`${root}${path20}`, {
|
|
2516
2622
|
method,
|
|
2517
2623
|
headers: {
|
|
2518
2624
|
"content-type": "application/json",
|
|
@@ -2525,7 +2631,7 @@ function createControlPlaneClient({
|
|
|
2525
2631
|
const timeout = new Promise((_, reject) => {
|
|
2526
2632
|
timeoutId = setTimeout(() => {
|
|
2527
2633
|
controller.abort();
|
|
2528
|
-
reject(new Error(`control-plane ${
|
|
2634
|
+
reject(new Error(`control-plane ${path20} timed out after ${timeoutMs}ms`));
|
|
2529
2635
|
}, timeoutMs);
|
|
2530
2636
|
});
|
|
2531
2637
|
try {
|
|
@@ -2534,7 +2640,7 @@ function createControlPlaneClient({
|
|
|
2534
2640
|
clearTimeout(timeoutId);
|
|
2535
2641
|
}
|
|
2536
2642
|
}
|
|
2537
|
-
const taskReq = (method,
|
|
2643
|
+
const taskReq = (method, path20, body, options = {}) => req(method, path20, body, { timeoutMs: taskRequestTimeoutMs, ...options });
|
|
2538
2644
|
return {
|
|
2539
2645
|
...makeAutonomousDispatchAdmissionClient(
|
|
2540
2646
|
req,
|
|
@@ -2657,8 +2763,8 @@ function createControlPlaneClient({
|
|
|
2657
2763
|
return listAllPrOpenedTasks(taskReq);
|
|
2658
2764
|
},
|
|
2659
2765
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
2660
|
-
const
|
|
2661
|
-
const res = await taskReq("GET",
|
|
2766
|
+
const path20 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
2767
|
+
const res = await taskReq("GET", path20);
|
|
2662
2768
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
2663
2769
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
2664
2770
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -2852,8 +2958,8 @@ var init_control_plane_client = __esm({
|
|
|
2852
2958
|
});
|
|
2853
2959
|
|
|
2854
2960
|
// ../../scripts/virtual-office/code-runner/windows-claude-launch.mjs
|
|
2855
|
-
import { existsSync as
|
|
2856
|
-
import { win32 as
|
|
2961
|
+
import { existsSync as existsSync3, realpathSync } from "node:fs";
|
|
2962
|
+
import { win32 as path11 } from "node:path";
|
|
2857
2963
|
import { spawnSync } from "node:child_process";
|
|
2858
2964
|
function pathValue(env2) {
|
|
2859
2965
|
for (const key of ["Path", "PATH", "path"]) {
|
|
@@ -2874,38 +2980,38 @@ function envValue(env2, name) {
|
|
|
2874
2980
|
function userClaudeCandidates(bin, env2) {
|
|
2875
2981
|
if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
|
|
2876
2982
|
const userProfile = envValue(env2, "USERPROFILE");
|
|
2877
|
-
const appData = envValue(env2, "APPDATA") || (userProfile ?
|
|
2878
|
-
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ?
|
|
2983
|
+
const appData = envValue(env2, "APPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Roaming") : "");
|
|
2984
|
+
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Local") : "");
|
|
2879
2985
|
const candidates = [];
|
|
2880
2986
|
if (appData) {
|
|
2881
|
-
const npmBin =
|
|
2987
|
+
const npmBin = path11.join(appData, "npm");
|
|
2882
2988
|
candidates.push(
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2989
|
+
path11.join(npmBin, "claude.exe"),
|
|
2990
|
+
path11.join(npmBin, "claude.cmd"),
|
|
2991
|
+
path11.join(npmBin, "claude.ps1"),
|
|
2992
|
+
path11.join(npmBin, "claude"),
|
|
2993
|
+
path11.join(npmBin, ...NATIVE_CLAUDE_PARTS)
|
|
2888
2994
|
);
|
|
2889
2995
|
}
|
|
2890
|
-
if (userProfile) candidates.push(
|
|
2996
|
+
if (userProfile) candidates.push(path11.join(userProfile, ".local", "bin", "claude.exe"));
|
|
2891
2997
|
if (localAppData) {
|
|
2892
2998
|
candidates.push(
|
|
2893
|
-
|
|
2894
|
-
|
|
2999
|
+
path11.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
|
|
3000
|
+
path11.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
|
|
2895
3001
|
);
|
|
2896
3002
|
}
|
|
2897
3003
|
return candidates;
|
|
2898
3004
|
}
|
|
2899
3005
|
function pathCandidates(bin, env2) {
|
|
2900
|
-
if (
|
|
2901
|
-
return [
|
|
2902
|
-
}
|
|
2903
|
-
const extension =
|
|
2904
|
-
const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
3006
|
+
if (path11.isAbsolute(bin) || /[\\/]/u.test(bin)) {
|
|
3007
|
+
return [path11.resolve(bin)];
|
|
3008
|
+
}
|
|
3009
|
+
const extension = path11.extname(bin);
|
|
3010
|
+
const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path11.join(directory, bin)] : [
|
|
3011
|
+
path11.join(directory, `${bin}.exe`),
|
|
3012
|
+
path11.join(directory, `${bin}.cmd`),
|
|
3013
|
+
path11.join(directory, `${bin}.ps1`),
|
|
3014
|
+
path11.join(directory, bin)
|
|
2909
3015
|
]);
|
|
2910
3016
|
const seen = /* @__PURE__ */ new Set();
|
|
2911
3017
|
return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
|
|
@@ -2926,7 +3032,7 @@ function canonicalExistingPath(candidate, exists, canonicalize) {
|
|
|
2926
3032
|
function resolveWindowsClaudeExecutable({
|
|
2927
3033
|
bin = "claude",
|
|
2928
3034
|
env: env2 = process.env,
|
|
2929
|
-
exists =
|
|
3035
|
+
exists = existsSync3,
|
|
2930
3036
|
canonicalize = realpathSync
|
|
2931
3037
|
} = {}) {
|
|
2932
3038
|
const requested = String(bin || "").trim();
|
|
@@ -2936,8 +3042,8 @@ function resolveWindowsClaudeExecutable({
|
|
|
2936
3042
|
for (const candidate of pathCandidates(requested, env2)) {
|
|
2937
3043
|
const found = canonicalExistingPath(candidate, exists, canonicalize);
|
|
2938
3044
|
if (!found) continue;
|
|
2939
|
-
if (
|
|
2940
|
-
const native =
|
|
3045
|
+
if (path11.extname(found).toLowerCase() === ".exe") return found;
|
|
3046
|
+
const native = path11.join(path11.dirname(found), ...NATIVE_CLAUDE_PARTS);
|
|
2941
3047
|
const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
|
|
2942
3048
|
if (resolvedNative) return resolvedNative;
|
|
2943
3049
|
}
|
|
@@ -3404,14 +3510,14 @@ var init_terminal_process_cleanup = __esm({
|
|
|
3404
3510
|
|
|
3405
3511
|
// ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
|
|
3406
3512
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3407
|
-
import { existsSync as
|
|
3513
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3408
3514
|
import os from "node:os";
|
|
3409
|
-
import
|
|
3515
|
+
import path12 from "node:path";
|
|
3410
3516
|
function registryRoot(tmp = os.tmpdir()) {
|
|
3411
|
-
return
|
|
3517
|
+
return path12.join(tmp, REGISTRY_ROOT_NAME);
|
|
3412
3518
|
}
|
|
3413
3519
|
function instanceDir(root, instanceId) {
|
|
3414
|
-
return
|
|
3520
|
+
return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
|
|
3415
3521
|
}
|
|
3416
3522
|
function registerDaemonInstance({
|
|
3417
3523
|
root = registryRoot(),
|
|
@@ -3421,8 +3527,8 @@ function registerDaemonInstance({
|
|
|
3421
3527
|
} = {}) {
|
|
3422
3528
|
if (!instanceId) return null;
|
|
3423
3529
|
const dir = instanceDir(root, instanceId);
|
|
3424
|
-
|
|
3425
|
-
const file =
|
|
3530
|
+
mkdirSync3(dir, { recursive: true });
|
|
3531
|
+
const file = path12.join(dir, DAEMON_RECORD);
|
|
3426
3532
|
writeFileSync2(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
|
|
3427
3533
|
encoding: "utf8",
|
|
3428
3534
|
mode: 384
|
|
@@ -3439,9 +3545,9 @@ function recordAgentPid({
|
|
|
3439
3545
|
if (!instanceId || !Number.isInteger(pid) || pid <= 0) return false;
|
|
3440
3546
|
try {
|
|
3441
3547
|
const dir = instanceDir(root, instanceId);
|
|
3442
|
-
|
|
3548
|
+
mkdirSync3(dir, { recursive: true });
|
|
3443
3549
|
writeFileSync2(
|
|
3444
|
-
|
|
3550
|
+
path12.join(dir, `${pid}.json`),
|
|
3445
3551
|
JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
|
|
3446
3552
|
{ encoding: "utf8", mode: 384 }
|
|
3447
3553
|
);
|
|
@@ -3457,7 +3563,7 @@ function unrecordAgentPid({
|
|
|
3457
3563
|
} = {}) {
|
|
3458
3564
|
if (!instanceId || !Number.isInteger(pid)) return false;
|
|
3459
3565
|
try {
|
|
3460
|
-
rmSync2(
|
|
3566
|
+
rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
|
|
3461
3567
|
return true;
|
|
3462
3568
|
} catch {
|
|
3463
3569
|
return false;
|
|
@@ -3475,7 +3581,7 @@ function bootstrapOrphanReaper({ instanceId, log: log2 = () => {
|
|
|
3475
3581
|
}
|
|
3476
3582
|
function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
3477
3583
|
const instances = [];
|
|
3478
|
-
if (!
|
|
3584
|
+
if (!existsSync4(root)) return instances;
|
|
3479
3585
|
let dirents;
|
|
3480
3586
|
try {
|
|
3481
3587
|
dirents = readdirSync(root, { withFileTypes: true });
|
|
@@ -3485,7 +3591,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3485
3591
|
for (const dirent of dirents) {
|
|
3486
3592
|
if (!dirent.isDirectory()) continue;
|
|
3487
3593
|
if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
|
|
3488
|
-
const dir =
|
|
3594
|
+
const dir = path12.join(root, dirent.name);
|
|
3489
3595
|
let daemon = null;
|
|
3490
3596
|
const agents = [];
|
|
3491
3597
|
let files;
|
|
@@ -3497,7 +3603,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3497
3603
|
for (const name of files) {
|
|
3498
3604
|
let parsed;
|
|
3499
3605
|
try {
|
|
3500
|
-
parsed = JSON.parse(readFileSync2(
|
|
3606
|
+
parsed = JSON.parse(readFileSync2(path12.join(dir, name), "utf8"));
|
|
3501
3607
|
} catch {
|
|
3502
3608
|
continue;
|
|
3503
3609
|
}
|
|
@@ -3545,7 +3651,7 @@ function windowsSystemRoot(env2 = process.env) {
|
|
|
3545
3651
|
return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
3546
3652
|
}
|
|
3547
3653
|
function windowsPowershellExe(env2 = process.env) {
|
|
3548
|
-
return
|
|
3654
|
+
return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
3549
3655
|
}
|
|
3550
3656
|
function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
|
|
3551
3657
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3590,7 +3696,7 @@ function parsePosixPsLine(line) {
|
|
|
3590
3696
|
function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
|
|
3591
3697
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3592
3698
|
if (platform === "win32") {
|
|
3593
|
-
const taskkill =
|
|
3699
|
+
const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
|
|
3594
3700
|
const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
3595
3701
|
return !r.error && r.status === 0;
|
|
3596
3702
|
}
|
|
@@ -4379,15 +4485,15 @@ var init_flat_token_usage = __esm({
|
|
|
4379
4485
|
|
|
4380
4486
|
// ../../scripts/virtual-office/code-runner/codex-runner.mjs
|
|
4381
4487
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
4382
|
-
import { existsSync as
|
|
4383
|
-
import { win32 } from "node:path";
|
|
4488
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4489
|
+
import { win32 as win322 } from "node:path";
|
|
4384
4490
|
function isTruthyFlag2(value) {
|
|
4385
4491
|
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
|
4386
4492
|
}
|
|
4387
4493
|
function resolveCodexBinary({
|
|
4388
4494
|
env: env2 = process.env,
|
|
4389
4495
|
platform = process.platform,
|
|
4390
|
-
exists =
|
|
4496
|
+
exists = existsSync5
|
|
4391
4497
|
} = {}) {
|
|
4392
4498
|
if (platform !== "win32") return "codex";
|
|
4393
4499
|
const appData = String(env2.APPDATA || "").trim();
|
|
@@ -4395,7 +4501,7 @@ function resolveCodexBinary({
|
|
|
4395
4501
|
const localAppData = String(env2.LOCALAPPDATA || "").trim();
|
|
4396
4502
|
const candidates = [];
|
|
4397
4503
|
if (appData) {
|
|
4398
|
-
candidates.push(
|
|
4504
|
+
candidates.push(win322.join(
|
|
4399
4505
|
appData,
|
|
4400
4506
|
"npm",
|
|
4401
4507
|
"node_modules",
|
|
@@ -4411,11 +4517,11 @@ function resolveCodexBinary({
|
|
|
4411
4517
|
));
|
|
4412
4518
|
}
|
|
4413
4519
|
if (userProfile) {
|
|
4414
|
-
candidates.push(
|
|
4415
|
-
candidates.push(
|
|
4520
|
+
candidates.push(win322.join(userProfile, ".local", "bin", "codex.exe"));
|
|
4521
|
+
candidates.push(win322.join(userProfile, ".codex", "bin", "codex.exe"));
|
|
4416
4522
|
}
|
|
4417
4523
|
if (localAppData) {
|
|
4418
|
-
candidates.push(
|
|
4524
|
+
candidates.push(win322.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
|
|
4419
4525
|
}
|
|
4420
4526
|
const absolute = candidates.find((candidate) => exists(candidate));
|
|
4421
4527
|
if (absolute) return absolute;
|
|
@@ -5244,9 +5350,9 @@ var init_rate_limit_detector_core = __esm({
|
|
|
5244
5350
|
|
|
5245
5351
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
|
|
5246
5352
|
import fsp10 from "node:fs/promises";
|
|
5247
|
-
import
|
|
5353
|
+
import path13 from "node:path";
|
|
5248
5354
|
async function atomicWrite(file, content) {
|
|
5249
|
-
await fsp10.mkdir(
|
|
5355
|
+
await fsp10.mkdir(path13.dirname(file), { recursive: true });
|
|
5250
5356
|
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
5251
5357
|
const handle = await fsp10.open(temp, "wx");
|
|
5252
5358
|
try {
|
|
@@ -5307,7 +5413,7 @@ function writeResumeAttempts(file, store) {
|
|
|
5307
5413
|
}
|
|
5308
5414
|
async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
|
|
5309
5415
|
const deadline = now() + LOCK_WAIT_MS;
|
|
5310
|
-
await fsp10.mkdir(
|
|
5416
|
+
await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
|
|
5311
5417
|
for (; ; ) {
|
|
5312
5418
|
let handle;
|
|
5313
5419
|
try {
|
|
@@ -5381,10 +5487,10 @@ var init_rate_limit_resume_state = __esm({
|
|
|
5381
5487
|
});
|
|
5382
5488
|
|
|
5383
5489
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
|
|
5384
|
-
import { homedir as
|
|
5490
|
+
import { homedir as homedir3 } from "node:os";
|
|
5385
5491
|
import { join as join2 } from "node:path";
|
|
5386
5492
|
function resumeQueuePath() {
|
|
5387
|
-
return join2(
|
|
5493
|
+
return join2(homedir3(), ".claude", "resume-queue.jsonl");
|
|
5388
5494
|
}
|
|
5389
5495
|
function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
|
|
5390
5496
|
return {
|
|
@@ -5585,7 +5691,7 @@ var init_auto_merge = __esm({
|
|
|
5585
5691
|
|
|
5586
5692
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
5587
5693
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
5588
|
-
import { existsSync as
|
|
5694
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
5589
5695
|
import { fileURLToPath } from "node:url";
|
|
5590
5696
|
function stripCredentials(env2 = process.env) {
|
|
5591
5697
|
const safe = { ...env2 };
|
|
@@ -5596,7 +5702,7 @@ function resolveOverlapScript({
|
|
|
5596
5702
|
worktreeDir,
|
|
5597
5703
|
trustedPath = null,
|
|
5598
5704
|
trustedPaths = TRUSTED_OVERLAP_CANDIDATES,
|
|
5599
|
-
existsFn =
|
|
5705
|
+
existsFn = existsSync6,
|
|
5600
5706
|
joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
|
|
5601
5707
|
} = {}) {
|
|
5602
5708
|
const candidates = trustedPath ? [trustedPath] : trustedPaths;
|
|
@@ -5665,14 +5771,14 @@ function parsePorcelainZ(out) {
|
|
|
5665
5771
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
5666
5772
|
const token2 = tokens[i];
|
|
5667
5773
|
if (!token2) continue;
|
|
5668
|
-
const
|
|
5669
|
-
if (
|
|
5774
|
+
const path20 = token2.slice(3);
|
|
5775
|
+
if (path20) files.push(path20);
|
|
5670
5776
|
if (token2[0] === "R" || token2[0] === "C") i += 1;
|
|
5671
5777
|
}
|
|
5672
5778
|
return files;
|
|
5673
5779
|
}
|
|
5674
|
-
function isAgentScratch(
|
|
5675
|
-
const normalized = String(
|
|
5780
|
+
function isAgentScratch(path20) {
|
|
5781
|
+
const normalized = String(path20 || "");
|
|
5676
5782
|
return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
5677
5783
|
}
|
|
5678
5784
|
var SCRATCH_PATTERNS;
|
|
@@ -6505,10 +6611,10 @@ var init_task_prompt = __esm({
|
|
|
6505
6611
|
});
|
|
6506
6612
|
|
|
6507
6613
|
// ../../scripts/virtual-office/code-runner/task-attachments.mjs
|
|
6508
|
-
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
6614
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
6509
6615
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
6510
6616
|
import os2 from "node:os";
|
|
6511
|
-
import
|
|
6617
|
+
import path14 from "node:path";
|
|
6512
6618
|
function safeTaskToken(taskId) {
|
|
6513
6619
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
6514
6620
|
}
|
|
@@ -6518,25 +6624,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
|
|
|
6518
6624
|
return `${String(index + 1).padStart(2, "0")}-${normalized}`;
|
|
6519
6625
|
}
|
|
6520
6626
|
function assertGeneratedDirectory(directory, tempRoot) {
|
|
6521
|
-
const resolvedDirectory =
|
|
6522
|
-
const resolvedRoot =
|
|
6523
|
-
if (
|
|
6627
|
+
const resolvedDirectory = path14.resolve(directory);
|
|
6628
|
+
const resolvedRoot = path14.resolve(tempRoot);
|
|
6629
|
+
if (path14.dirname(resolvedDirectory) !== resolvedRoot || !path14.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
|
|
6524
6630
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
6525
6631
|
}
|
|
6526
6632
|
return resolvedDirectory;
|
|
6527
6633
|
}
|
|
6528
6634
|
async function createAttachmentDirectory(taskId, tempRoot) {
|
|
6529
|
-
const root =
|
|
6635
|
+
const root = path14.resolve(tempRoot);
|
|
6530
6636
|
await mkdir(root, { recursive: true });
|
|
6531
|
-
const directory = await mkdtemp(
|
|
6532
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token:
|
|
6533
|
-
await writeFile(
|
|
6637
|
+
const directory = await mkdtemp(path14.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
6638
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path14.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6639
|
+
await writeFile(path14.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
6534
6640
|
return { directory, marker, tempRoot: root };
|
|
6535
6641
|
}
|
|
6536
6642
|
async function cleanupGeneratedDirectory(state) {
|
|
6537
6643
|
if (!state || state.cleaned) return;
|
|
6538
6644
|
const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
|
|
6539
|
-
const marker = await readFile(
|
|
6645
|
+
const marker = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
6540
6646
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
6541
6647
|
await rm(directory, { recursive: true, force: true });
|
|
6542
6648
|
state.cleaned = true;
|
|
@@ -6555,7 +6661,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6555
6661
|
now = Date.now(),
|
|
6556
6662
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
6557
6663
|
} = {}) {
|
|
6558
|
-
const root =
|
|
6664
|
+
const root = path14.resolve(tempRoot);
|
|
6559
6665
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
6560
6666
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
6561
6667
|
if (error?.code === "ENOENT") return [];
|
|
@@ -6564,8 +6670,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6564
6670
|
let removed = 0;
|
|
6565
6671
|
for (const entry of entries) {
|
|
6566
6672
|
if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
|
|
6567
|
-
const directory = assertGeneratedDirectory(
|
|
6568
|
-
const markerRaw = await readFile(
|
|
6673
|
+
const directory = assertGeneratedDirectory(path14.join(root, entry.name), root);
|
|
6674
|
+
const markerRaw = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
6569
6675
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
6570
6676
|
if (!marker) continue;
|
|
6571
6677
|
const directoryStat = await stat(directory);
|
|
@@ -6608,10 +6714,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
6608
6714
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
6609
6715
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
6610
6716
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
6611
|
-
const filePath =
|
|
6717
|
+
const filePath = path14.join(state.directory, name);
|
|
6612
6718
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
6613
6719
|
await chmod(filePath, 384);
|
|
6614
|
-
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path:
|
|
6720
|
+
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path14.resolve(filePath) });
|
|
6615
6721
|
}
|
|
6616
6722
|
return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
|
|
6617
6723
|
} catch (error) {
|
|
@@ -6633,7 +6739,7 @@ var init_task_attachments = __esm({
|
|
|
6633
6739
|
});
|
|
6634
6740
|
|
|
6635
6741
|
// ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
|
|
6636
|
-
import { homedir as
|
|
6742
|
+
import { homedir as homedir4 } from "node:os";
|
|
6637
6743
|
import { join as join4 } from "node:path";
|
|
6638
6744
|
import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
6639
6745
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -6675,9 +6781,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
6675
6781
|
}
|
|
6676
6782
|
return out;
|
|
6677
6783
|
}
|
|
6678
|
-
async function readCloudMap(
|
|
6784
|
+
async function readCloudMap(path20) {
|
|
6679
6785
|
try {
|
|
6680
|
-
return JSON.parse(await readFile2(
|
|
6786
|
+
return JSON.parse(await readFile2(path20, "utf8"));
|
|
6681
6787
|
} catch {
|
|
6682
6788
|
return {};
|
|
6683
6789
|
}
|
|
@@ -6750,8 +6856,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
|
|
|
6750
6856
|
var init_session_spool_forwarder = __esm({
|
|
6751
6857
|
"../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
|
|
6752
6858
|
"use strict";
|
|
6753
|
-
SPOOL_DIR = join4(
|
|
6754
|
-
CLOUD_MAP_FILE = join4(
|
|
6859
|
+
SPOOL_DIR = join4(homedir4(), ".vo", "session-spool");
|
|
6860
|
+
CLOUD_MAP_FILE = join4(homedir4(), ".vo", "session-cloud-map.json");
|
|
6755
6861
|
STALE_MS = 60 * 60 * 1e3;
|
|
6756
6862
|
ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
|
|
6757
6863
|
}
|
|
@@ -7434,7 +7540,7 @@ var init_local_model_remote_config = __esm({
|
|
|
7434
7540
|
|
|
7435
7541
|
// ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
|
|
7436
7542
|
import crypto from "node:crypto";
|
|
7437
|
-
import
|
|
7543
|
+
import fs7 from "node:fs";
|
|
7438
7544
|
function accountKey(agent, rawId) {
|
|
7439
7545
|
const id = typeof rawId === "string" ? rawId.trim() : "";
|
|
7440
7546
|
if (!id) return null;
|
|
@@ -7498,7 +7604,7 @@ var init_shared = __esm({
|
|
|
7498
7604
|
};
|
|
7499
7605
|
readJson = (p) => {
|
|
7500
7606
|
try {
|
|
7501
|
-
return JSON.parse(
|
|
7607
|
+
return JSON.parse(fs7.readFileSync(p, "utf8"));
|
|
7502
7608
|
} catch {
|
|
7503
7609
|
return null;
|
|
7504
7610
|
}
|
|
@@ -7508,9 +7614,9 @@ var init_shared = __esm({
|
|
|
7508
7614
|
});
|
|
7509
7615
|
|
|
7510
7616
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
7511
|
-
import
|
|
7617
|
+
import fs8 from "node:fs";
|
|
7512
7618
|
import os3 from "node:os";
|
|
7513
|
-
import
|
|
7619
|
+
import path15 from "node:path";
|
|
7514
7620
|
function fileCaptureTime(filePath, explicit, statFn) {
|
|
7515
7621
|
if (typeof explicit === "string" && explicit) return explicit;
|
|
7516
7622
|
try {
|
|
@@ -7524,7 +7630,7 @@ function usageBaseUrl(env2 = process.env) {
|
|
|
7524
7630
|
return String(raw).replace(/\/+$/, "");
|
|
7525
7631
|
}
|
|
7526
7632
|
function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
|
|
7527
|
-
const creds = read(
|
|
7633
|
+
const creds = read(path15.join(homeDir, ".claude", ".credentials.json"));
|
|
7528
7634
|
const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
|
|
7529
7635
|
if (!oauth || typeof oauth !== "object") return null;
|
|
7530
7636
|
const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
|
|
@@ -7534,7 +7640,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
|
|
|
7534
7640
|
return token2;
|
|
7535
7641
|
}
|
|
7536
7642
|
function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
|
|
7537
|
-
const cfg = read(
|
|
7643
|
+
const cfg = read(path15.join(homeDir, ".claude.json"));
|
|
7538
7644
|
const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
|
|
7539
7645
|
return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
|
|
7540
7646
|
}
|
|
@@ -7627,7 +7733,7 @@ async function readClaudeOAuthUsage({
|
|
|
7627
7733
|
function readClaudeFileUsage({
|
|
7628
7734
|
homeDir = os3.homedir(),
|
|
7629
7735
|
read: rawRead = readJson,
|
|
7630
|
-
statFn =
|
|
7736
|
+
statFn = fs8.statSync,
|
|
7631
7737
|
now = () => Date.now()
|
|
7632
7738
|
} = {}) {
|
|
7633
7739
|
const read = (p) => {
|
|
@@ -7644,7 +7750,7 @@ function readClaudeFileUsage({
|
|
|
7644
7750
|
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
7645
7751
|
return row;
|
|
7646
7752
|
};
|
|
7647
|
-
const statusPath =
|
|
7753
|
+
const statusPath = path15.join(homeDir, ".claude", "claude-usage.json");
|
|
7648
7754
|
const status = read(statusPath);
|
|
7649
7755
|
if (status && (status.seven_day || status.five_hour)) {
|
|
7650
7756
|
const row = fresh(makeUsageRow({
|
|
@@ -7659,7 +7765,7 @@ function readClaudeFileUsage({
|
|
|
7659
7765
|
}));
|
|
7660
7766
|
if (row) return row;
|
|
7661
7767
|
}
|
|
7662
|
-
const weeklyPath =
|
|
7768
|
+
const weeklyPath = path15.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
7663
7769
|
const weekly = read(weeklyPath);
|
|
7664
7770
|
if (weekly) {
|
|
7665
7771
|
const row = fresh(makeUsageRow({
|
|
@@ -8098,7 +8204,7 @@ var init_watcher_coordination = __esm({
|
|
|
8098
8204
|
});
|
|
8099
8205
|
|
|
8100
8206
|
// ../../scripts/virtual-office/code-runner/watcher-state.mjs
|
|
8101
|
-
import { randomUUID as
|
|
8207
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
8102
8208
|
import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
|
|
8103
8209
|
import { dirname as dirname4 } from "node:path";
|
|
8104
8210
|
async function readWatcherState(stateFile) {
|
|
@@ -8118,7 +8224,7 @@ async function readWatcherState(stateFile) {
|
|
|
8118
8224
|
async function writeWatcherState(stateFile, state) {
|
|
8119
8225
|
const directory = dirname4(stateFile);
|
|
8120
8226
|
await mkdir2(directory, { recursive: true });
|
|
8121
|
-
const temp = `${stateFile}.${process.pid}.${
|
|
8227
|
+
const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
|
|
8122
8228
|
let handle;
|
|
8123
8229
|
try {
|
|
8124
8230
|
handle = await open(temp, "wx");
|
|
@@ -8329,7 +8435,7 @@ var init_pr_watcher_github = __esm({
|
|
|
8329
8435
|
});
|
|
8330
8436
|
|
|
8331
8437
|
// ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
|
|
8332
|
-
import { randomUUID as
|
|
8438
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8333
8439
|
async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
8334
8440
|
}) {
|
|
8335
8441
|
const requestedBudgetUsd = task?.max_budget_usd;
|
|
@@ -8343,7 +8449,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
|
8343
8449
|
if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
|
|
8344
8450
|
throw new Error("autonomous dispatch admission client unavailable");
|
|
8345
8451
|
}
|
|
8346
|
-
const reservationId =
|
|
8452
|
+
const reservationId = randomUUID4();
|
|
8347
8453
|
const admission = await client.reserveAutonomousDispatchBudget({
|
|
8348
8454
|
requestedBudgetUsd,
|
|
8349
8455
|
reservationId,
|
|
@@ -8361,7 +8467,7 @@ var init_enqueue_autonomous_code_task = __esm({
|
|
|
8361
8467
|
});
|
|
8362
8468
|
|
|
8363
8469
|
// ../../scripts/virtual-office/code-runner/pr-watcher.mjs
|
|
8364
|
-
import { homedir as
|
|
8470
|
+
import { homedir as homedir5 } from "node:os";
|
|
8365
8471
|
import { join as join6 } from "node:path";
|
|
8366
8472
|
function parsePrCiStatus(view) {
|
|
8367
8473
|
const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
|
|
@@ -8705,7 +8811,7 @@ var init_pr_watcher = __esm({
|
|
|
8705
8811
|
init_watcher_state();
|
|
8706
8812
|
init_superseded_pr_source();
|
|
8707
8813
|
init_ci_fix_prompt();
|
|
8708
|
-
DEFAULT_STATE_FILE = join6(
|
|
8814
|
+
DEFAULT_STATE_FILE = join6(homedir5(), ".vo", "dispatched-prs.json");
|
|
8709
8815
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
8710
8816
|
"FAILURE",
|
|
8711
8817
|
"TIMED_OUT",
|
|
@@ -8935,9 +9041,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8935
9041
|
res.end();
|
|
8936
9042
|
return;
|
|
8937
9043
|
}
|
|
8938
|
-
const
|
|
9044
|
+
const path20 = String(req.url || "").split("?")[0];
|
|
8939
9045
|
res.setHeader("content-type", "application/json");
|
|
8940
|
-
if (req.method === "GET" &&
|
|
9046
|
+
if (req.method === "GET" && path20 === "/status") {
|
|
8941
9047
|
let status;
|
|
8942
9048
|
try {
|
|
8943
9049
|
status = getStatus();
|
|
@@ -8948,7 +9054,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8948
9054
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
8949
9055
|
return;
|
|
8950
9056
|
}
|
|
8951
|
-
if (req.method === "POST" &&
|
|
9057
|
+
if (req.method === "POST" && path20 === "/stop") {
|
|
8952
9058
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
8953
9059
|
res.statusCode = 403;
|
|
8954
9060
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -9111,8 +9217,8 @@ var init_effort_mode_config = __esm({
|
|
|
9111
9217
|
});
|
|
9112
9218
|
|
|
9113
9219
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
9114
|
-
import
|
|
9115
|
-
import
|
|
9220
|
+
import fs9 from "node:fs";
|
|
9221
|
+
import path16 from "node:path";
|
|
9116
9222
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9117
9223
|
function uniqueModels(models = []) {
|
|
9118
9224
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
@@ -9224,9 +9330,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
|
|
|
9224
9330
|
return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
|
|
9225
9331
|
}
|
|
9226
9332
|
function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
|
|
9227
|
-
if (!
|
|
9333
|
+
if (!fs9.existsSync(cacheFile)) return null;
|
|
9228
9334
|
try {
|
|
9229
|
-
const parsed = JSON.parse(
|
|
9335
|
+
const parsed = JSON.parse(fs9.readFileSync(cacheFile, "utf-8"));
|
|
9230
9336
|
if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
|
|
9231
9337
|
if (!Array.isArray(parsed.models)) return null;
|
|
9232
9338
|
return parsed;
|
|
@@ -9235,8 +9341,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
9235
9341
|
}
|
|
9236
9342
|
}
|
|
9237
9343
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
9238
|
-
|
|
9239
|
-
|
|
9344
|
+
fs9.mkdirSync(path16.dirname(cacheFile), { recursive: true });
|
|
9345
|
+
fs9.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
9240
9346
|
}
|
|
9241
9347
|
async function fetchRegistryCatalog({
|
|
9242
9348
|
fetchImpl = fetch,
|
|
@@ -9293,10 +9399,10 @@ var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANT
|
|
|
9293
9399
|
var init_model_registry = __esm({
|
|
9294
9400
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
9295
9401
|
"use strict";
|
|
9296
|
-
__dirname =
|
|
9297
|
-
ROOT =
|
|
9298
|
-
DEFAULT_CACHE_DIR =
|
|
9299
|
-
DEFAULT_CACHE_FILE =
|
|
9402
|
+
__dirname = path16.dirname(fileURLToPath4(import.meta.url));
|
|
9403
|
+
ROOT = path16.resolve(__dirname, "..", "..");
|
|
9404
|
+
DEFAULT_CACHE_DIR = path16.join(ROOT, ".virtual-office-cache", "model-registry");
|
|
9405
|
+
DEFAULT_CACHE_FILE = path16.join(DEFAULT_CACHE_DIR, "catalog.json");
|
|
9300
9406
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
9301
9407
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
9302
9408
|
FAMILY_DEFINITIONS = {
|
|
@@ -9904,7 +10010,7 @@ var init_classify_task = __esm({
|
|
|
9904
10010
|
|
|
9905
10011
|
// ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
|
|
9906
10012
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
9907
|
-
import { homedir as
|
|
10013
|
+
import { homedir as homedir6 } from "node:os";
|
|
9908
10014
|
import { join as join7 } from "node:path";
|
|
9909
10015
|
function difficultyToRung(difficulty, thresholds) {
|
|
9910
10016
|
const b = thresholds.rungBounds;
|
|
@@ -9930,9 +10036,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
9930
10036
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
9931
10037
|
return base;
|
|
9932
10038
|
}
|
|
9933
|
-
function readCodexModelsCache({ path:
|
|
10039
|
+
function readCodexModelsCache({ path: path20 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
|
|
9934
10040
|
try {
|
|
9935
|
-
const parsed = JSON.parse(read(
|
|
10041
|
+
const parsed = JSON.parse(read(path20, "utf8"));
|
|
9936
10042
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
9937
10043
|
} catch {
|
|
9938
10044
|
return null;
|
|
@@ -9982,7 +10088,7 @@ var init_effort_policy = __esm({
|
|
|
9982
10088
|
init_meta_model_catalog();
|
|
9983
10089
|
RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
|
|
9984
10090
|
rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
|
|
9985
|
-
DEFAULT_CODEX_MODELS_CACHE = join7(
|
|
10091
|
+
DEFAULT_CODEX_MODELS_CACHE = join7(homedir6(), ".codex", "models_cache.json");
|
|
9986
10092
|
}
|
|
9987
10093
|
});
|
|
9988
10094
|
|
|
@@ -10112,8 +10218,8 @@ var init_role_cost_shadow = __esm({
|
|
|
10112
10218
|
});
|
|
10113
10219
|
|
|
10114
10220
|
// ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
|
|
10115
|
-
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as
|
|
10116
|
-
import { homedir as
|
|
10221
|
+
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
10222
|
+
import { homedir as homedir7 } from "node:os";
|
|
10117
10223
|
import { join as join8, dirname as dirname5 } from "node:path";
|
|
10118
10224
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
10119
10225
|
function getAutoRouterMode(env2 = process.env) {
|
|
@@ -10189,15 +10295,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
10189
10295
|
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("; ")}`;
|
|
10190
10296
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
10191
10297
|
}
|
|
10192
|
-
function appendDecisionFallback(decision, { path:
|
|
10298
|
+
function appendDecisionFallback(decision, { path: path20 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
|
|
10193
10299
|
try {
|
|
10194
|
-
mkdir4(dirname5(
|
|
10195
|
-
append(
|
|
10300
|
+
mkdir4(dirname5(path20), { recursive: true });
|
|
10301
|
+
append(path20, `${JSON.stringify(decision)}
|
|
10196
10302
|
`, "utf8");
|
|
10197
10303
|
if (isRouterDecision(decision)) {
|
|
10198
10304
|
try {
|
|
10199
10305
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
10200
|
-
for (const record of records) append(
|
|
10306
|
+
for (const record of records) append(path20, `${JSON.stringify(record)}
|
|
10201
10307
|
`, "utf8");
|
|
10202
10308
|
} catch {
|
|
10203
10309
|
}
|
|
@@ -10215,7 +10321,7 @@ var init_auto_router = __esm({
|
|
|
10215
10321
|
init_effort_policy();
|
|
10216
10322
|
init_role_cost_shadow();
|
|
10217
10323
|
ROUTER_VERSION = "0.1.0";
|
|
10218
|
-
DECISION_FALLBACK_PATH = join8(
|
|
10324
|
+
DECISION_FALLBACK_PATH = join8(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
|
|
10219
10325
|
MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
10220
10326
|
cachedThresholds = null;
|
|
10221
10327
|
isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
|
|
@@ -11188,9 +11294,9 @@ var init_inference_task_runner = __esm({
|
|
|
11188
11294
|
});
|
|
11189
11295
|
|
|
11190
11296
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
11191
|
-
import
|
|
11297
|
+
import fs10 from "node:fs";
|
|
11192
11298
|
import fsp11 from "node:fs/promises";
|
|
11193
|
-
import
|
|
11299
|
+
import path17 from "node:path";
|
|
11194
11300
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
11195
11301
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
11196
11302
|
}
|
|
@@ -11203,7 +11309,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
11203
11309
|
"--path-format=absolute",
|
|
11204
11310
|
"--git-common-dir"
|
|
11205
11311
|
])).trim();
|
|
11206
|
-
const root =
|
|
11312
|
+
const root = path17.dirname(commonDir);
|
|
11207
11313
|
return samePath2(root, worktreeDir) ? null : root;
|
|
11208
11314
|
}
|
|
11209
11315
|
async function snapshot(root, run) {
|
|
@@ -11245,21 +11351,21 @@ async function changedPaths(root, run) {
|
|
|
11245
11351
|
}
|
|
11246
11352
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
11247
11353
|
const paths = await changedPaths(baseline.root, run);
|
|
11248
|
-
const quarantineDir =
|
|
11249
|
-
|
|
11354
|
+
const quarantineDir = path17.join(
|
|
11355
|
+
path17.dirname(worktreeDir),
|
|
11250
11356
|
".canonical-recovery",
|
|
11251
11357
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
11252
11358
|
);
|
|
11253
11359
|
await fsp11.mkdir(quarantineDir, { recursive: true });
|
|
11254
11360
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
11255
|
-
await fsp11.writeFile(
|
|
11361
|
+
await fsp11.writeFile(path17.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
11256
11362
|
for (const relative of paths.untracked) {
|
|
11257
|
-
const source =
|
|
11258
|
-
const target =
|
|
11259
|
-
await fsp11.mkdir(
|
|
11363
|
+
const source = path17.join(baseline.root, relative);
|
|
11364
|
+
const target = path17.join(quarantineDir, "untracked", relative);
|
|
11365
|
+
await fsp11.mkdir(path17.dirname(target), { recursive: true });
|
|
11260
11366
|
await fsp11.copyFile(source, target);
|
|
11261
11367
|
}
|
|
11262
|
-
await fsp11.writeFile(
|
|
11368
|
+
await fsp11.writeFile(path17.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
11263
11369
|
taskId,
|
|
11264
11370
|
canonicalRoot: baseline.root,
|
|
11265
11371
|
canonicalHead: baseline.head,
|
|
@@ -11281,9 +11387,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
11281
11387
|
]);
|
|
11282
11388
|
}
|
|
11283
11389
|
for (const relative of evidence.untracked) {
|
|
11284
|
-
const target =
|
|
11285
|
-
const prefix = `${
|
|
11286
|
-
if (!target.startsWith(prefix) || !
|
|
11390
|
+
const target = path17.resolve(baseline.root, relative);
|
|
11391
|
+
const prefix = `${path17.resolve(baseline.root)}${path17.sep}`;
|
|
11392
|
+
if (!target.startsWith(prefix) || !fs10.existsSync(target)) continue;
|
|
11287
11393
|
await fsp11.rm(target, { force: true });
|
|
11288
11394
|
}
|
|
11289
11395
|
}
|
|
@@ -11319,7 +11425,7 @@ var init_isolation_audit = __esm({
|
|
|
11319
11425
|
init_process_runner2();
|
|
11320
11426
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
11321
11427
|
samePath2 = (left, right) => {
|
|
11322
|
-
const [a, b] = [left, right].map((value) =>
|
|
11428
|
+
const [a, b] = [left, right].map((value) => path17.resolve(value));
|
|
11323
11429
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
11324
11430
|
};
|
|
11325
11431
|
}
|
|
@@ -11679,7 +11785,7 @@ var init_publication_outcome = __esm({
|
|
|
11679
11785
|
|
|
11680
11786
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
11681
11787
|
import fsp12 from "node:fs/promises";
|
|
11682
|
-
import
|
|
11788
|
+
import path18 from "node:path";
|
|
11683
11789
|
function defaultRun2(command, args, cwd, options = {}) {
|
|
11684
11790
|
return runProcess2(command, args, { cwd, ...options });
|
|
11685
11791
|
}
|
|
@@ -11687,13 +11793,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
11687
11793
|
if (!isAgentScratch(file)) {
|
|
11688
11794
|
throw new Error(`refusing to remove non-scratch publication path: ${file}`);
|
|
11689
11795
|
}
|
|
11690
|
-
const root =
|
|
11691
|
-
const target =
|
|
11692
|
-
const relative =
|
|
11693
|
-
if (!relative || relative.startsWith(`..${
|
|
11796
|
+
const root = path18.resolve(worktreeDir);
|
|
11797
|
+
const target = path18.resolve(root, file);
|
|
11798
|
+
const relative = path18.relative(root, target);
|
|
11799
|
+
if (!relative || relative.startsWith(`..${path18.sep}`) || path18.isAbsolute(relative)) {
|
|
11694
11800
|
throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
|
|
11695
11801
|
}
|
|
11696
|
-
for (let cursor = target; cursor !== root; cursor =
|
|
11802
|
+
for (let cursor = target; cursor !== root; cursor = path18.dirname(cursor)) {
|
|
11697
11803
|
try {
|
|
11698
11804
|
if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
|
|
11699
11805
|
throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
|
|
@@ -11813,9 +11919,9 @@ var init_publication_scope = __esm({
|
|
|
11813
11919
|
});
|
|
11814
11920
|
|
|
11815
11921
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
11816
|
-
import
|
|
11922
|
+
import fs11 from "node:fs";
|
|
11817
11923
|
import fsp13 from "node:fs/promises";
|
|
11818
|
-
import
|
|
11924
|
+
import path19 from "node:path";
|
|
11819
11925
|
function recoveryTaskId(prompt) {
|
|
11820
11926
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
11821
11927
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -11829,10 +11935,10 @@ function cloneLeaf(repo) {
|
|
|
11829
11935
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
11830
11936
|
const leaf = cloneLeaf(repo);
|
|
11831
11937
|
if (!leaf || !clonesRoot2) return [];
|
|
11832
|
-
const canonical =
|
|
11938
|
+
const canonical = path19.join(clonesRoot2, leaf);
|
|
11833
11939
|
return [
|
|
11834
|
-
|
|
11835
|
-
|
|
11940
|
+
path19.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
11941
|
+
path19.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
11836
11942
|
];
|
|
11837
11943
|
}
|
|
11838
11944
|
async function readLedger(file, readFile5) {
|
|
@@ -11851,7 +11957,7 @@ async function readLedger(file, readFile5) {
|
|
|
11851
11957
|
async function findPreservedRecovery(task, {
|
|
11852
11958
|
clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
|
|
11853
11959
|
readFile: readFile5 = fsp13.readFile,
|
|
11854
|
-
exists =
|
|
11960
|
+
exists = fs11.existsSync
|
|
11855
11961
|
} = {}) {
|
|
11856
11962
|
const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
|
|
11857
11963
|
const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
|
|
@@ -12165,7 +12271,7 @@ var init_cancellation_probe = __esm({
|
|
|
12165
12271
|
});
|
|
12166
12272
|
|
|
12167
12273
|
// ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
|
|
12168
|
-
import { homedir as
|
|
12274
|
+
import { homedir as homedir8 } from "node:os";
|
|
12169
12275
|
import { dirname as dirname6, join as join9 } from "node:path";
|
|
12170
12276
|
import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
|
|
12171
12277
|
function withLock(operation) {
|
|
@@ -12230,13 +12336,13 @@ var DEFAULT_FILE, serialized;
|
|
|
12230
12336
|
var init_detached_economics_spool = __esm({
|
|
12231
12337
|
"../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
|
|
12232
12338
|
"use strict";
|
|
12233
|
-
DEFAULT_FILE = join9(
|
|
12339
|
+
DEFAULT_FILE = join9(homedir8(), ".vo", "detached-run-economics.json");
|
|
12234
12340
|
serialized = Promise.resolve();
|
|
12235
12341
|
}
|
|
12236
12342
|
});
|
|
12237
12343
|
|
|
12238
12344
|
// ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
|
|
12239
|
-
import { randomUUID as
|
|
12345
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
12240
12346
|
async function handleKilledRun({
|
|
12241
12347
|
client,
|
|
12242
12348
|
id,
|
|
@@ -12273,7 +12379,7 @@ async function handleKilledRun({
|
|
|
12273
12379
|
};
|
|
12274
12380
|
}
|
|
12275
12381
|
if (reason === "claim_authority_changed") {
|
|
12276
|
-
const occurrenceId =
|
|
12382
|
+
const occurrenceId = randomUUID5();
|
|
12277
12383
|
const economics = {
|
|
12278
12384
|
occurrence_id: occurrenceId,
|
|
12279
12385
|
runner_id: runnerId,
|
|
@@ -12570,7 +12676,7 @@ var code_runner_daemon_exports = {};
|
|
|
12570
12676
|
__export(code_runner_daemon_exports, {
|
|
12571
12677
|
main: () => main
|
|
12572
12678
|
});
|
|
12573
|
-
import { randomUUID as
|
|
12679
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
12574
12680
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
12575
12681
|
function log(msg) {
|
|
12576
12682
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
@@ -12800,7 +12906,7 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
|
12800
12906
|
async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
12801
12907
|
const cfg = loadCodeRunnerConfig(env2, { log });
|
|
12802
12908
|
await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
|
|
12803
|
-
const runnerInstanceId =
|
|
12909
|
+
const runnerInstanceId = randomUUID6();
|
|
12804
12910
|
const client = createControlPlaneClient({
|
|
12805
12911
|
env: env2,
|
|
12806
12912
|
runnerId: cfg.runnerId,
|
|
@@ -13129,6 +13235,98 @@ function pairedOperatorScope(readiness) {
|
|
|
13129
13235
|
return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
|
|
13130
13236
|
}
|
|
13131
13237
|
|
|
13238
|
+
// src/runner/root-config.mjs
|
|
13239
|
+
import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
|
|
13240
|
+
import { homedir } from "node:os";
|
|
13241
|
+
import { posix, win32 } from "node:path";
|
|
13242
|
+
import { randomUUID } from "node:crypto";
|
|
13243
|
+
var APP_IDENTIFIER = "ai.algosuite.vo-runner";
|
|
13244
|
+
var CLONES_DIR = "clones";
|
|
13245
|
+
function pathsFor(platform) {
|
|
13246
|
+
return platform === "win32" ? win32 : posix;
|
|
13247
|
+
}
|
|
13248
|
+
function absoluteOrNull(value, pathApi) {
|
|
13249
|
+
const normalized = String(value || "").trim();
|
|
13250
|
+
return normalized && pathApi.isAbsolute(normalized) ? pathApi.resolve(normalized) : null;
|
|
13251
|
+
}
|
|
13252
|
+
function defaultClonesRoot({
|
|
13253
|
+
platform = process.platform,
|
|
13254
|
+
env: env2 = process.env,
|
|
13255
|
+
home = homedir()
|
|
13256
|
+
} = {}) {
|
|
13257
|
+
const pathApi = pathsFor(platform);
|
|
13258
|
+
if (platform === "win32") {
|
|
13259
|
+
const appData = absoluteOrNull(env2.APPDATA, pathApi);
|
|
13260
|
+
return appData ? pathApi.join(appData, APP_IDENTIFIER, CLONES_DIR) : null;
|
|
13261
|
+
}
|
|
13262
|
+
const absoluteHome = absoluteOrNull(home, pathApi);
|
|
13263
|
+
if (!absoluteHome) return null;
|
|
13264
|
+
if (platform === "darwin") {
|
|
13265
|
+
return pathApi.join(absoluteHome, "Library", "Application Support", APP_IDENTIFIER, CLONES_DIR);
|
|
13266
|
+
}
|
|
13267
|
+
const xdg = absoluteOrNull(env2.XDG_CONFIG_HOME, pathApi);
|
|
13268
|
+
return pathApi.join(xdg || pathApi.join(absoluteHome, ".config"), APP_IDENTIFIER, CLONES_DIR);
|
|
13269
|
+
}
|
|
13270
|
+
function findGitRoot(cwd, { platform, exists = existsSync }) {
|
|
13271
|
+
const pathApi = pathsFor(platform);
|
|
13272
|
+
let cursor = pathApi.resolve(cwd);
|
|
13273
|
+
for (; ; ) {
|
|
13274
|
+
if (exists(pathApi.join(cursor, ".git"))) return cursor;
|
|
13275
|
+
const parent = pathApi.dirname(cursor);
|
|
13276
|
+
if (parent === cursor) return null;
|
|
13277
|
+
cursor = parent;
|
|
13278
|
+
}
|
|
13279
|
+
}
|
|
13280
|
+
function resolveRunnerRootConfig({
|
|
13281
|
+
env: env2 = process.env,
|
|
13282
|
+
cwd = process.cwd(),
|
|
13283
|
+
platform = process.platform,
|
|
13284
|
+
home = homedir(),
|
|
13285
|
+
exists = existsSync
|
|
13286
|
+
} = {}) {
|
|
13287
|
+
const pathApi = pathsFor(platform);
|
|
13288
|
+
const explicitRepoValue = String(env2.VO_CODE_RUNNER_REPO || "").trim();
|
|
13289
|
+
const explicitClonesValue = String(env2.VO_CODE_RUNNER_CLONES_ROOT || "").trim();
|
|
13290
|
+
if (explicitRepoValue && !pathApi.isAbsolute(explicitRepoValue)) {
|
|
13291
|
+
throw new Error(`VO_CODE_RUNNER_REPO must be an absolute path (got '${explicitRepoValue}')`);
|
|
13292
|
+
}
|
|
13293
|
+
if (explicitClonesValue && !pathApi.isAbsolute(explicitClonesValue)) {
|
|
13294
|
+
throw new Error(`VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${explicitClonesValue}')`);
|
|
13295
|
+
}
|
|
13296
|
+
const repoRoot2 = explicitRepoValue ? pathApi.resolve(explicitRepoValue) : findGitRoot(cwd, { platform, exists });
|
|
13297
|
+
const clonesRoot2 = explicitClonesValue ? pathApi.resolve(explicitClonesValue) : repoRoot2 ? null : defaultClonesRoot({ platform, env: env2, home });
|
|
13298
|
+
if (!repoRoot2 && !clonesRoot2) {
|
|
13299
|
+
throw new Error(
|
|
13300
|
+
"No safe runner root is available. Set VO_CODE_RUNNER_REPO or VO_CODE_RUNNER_CLONES_ROOT to an absolute path."
|
|
13301
|
+
);
|
|
13302
|
+
}
|
|
13303
|
+
return { repoRoot: repoRoot2, clonesRoot: clonesRoot2 };
|
|
13304
|
+
}
|
|
13305
|
+
function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }) {
|
|
13306
|
+
const root = repoRoot2 || clonesRoot2;
|
|
13307
|
+
if (!root) throw new Error("Runner root configuration has no working directory");
|
|
13308
|
+
return root;
|
|
13309
|
+
}
|
|
13310
|
+
function assertWritableRunnerDirectory(root) {
|
|
13311
|
+
mkdirSync(root, { recursive: true });
|
|
13312
|
+
const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID()}`);
|
|
13313
|
+
let handle;
|
|
13314
|
+
try {
|
|
13315
|
+
handle = openSync(probe, "wx", 384);
|
|
13316
|
+
} catch (error) {
|
|
13317
|
+
throw new Error(
|
|
13318
|
+
`Runner clones root is not writable: ${root} (${error instanceof Error ? error.message : String(error)})`,
|
|
13319
|
+
{ cause: error }
|
|
13320
|
+
);
|
|
13321
|
+
} finally {
|
|
13322
|
+
if (handle !== void 0) closeSync(handle);
|
|
13323
|
+
try {
|
|
13324
|
+
unlinkSync(probe);
|
|
13325
|
+
} catch {
|
|
13326
|
+
}
|
|
13327
|
+
}
|
|
13328
|
+
}
|
|
13329
|
+
|
|
13132
13330
|
// src/runner-cli.mjs
|
|
13133
13331
|
var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
13134
13332
|
function packageVersion() {
|
|
@@ -13143,11 +13341,45 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
|
13143
13341
|
`);
|
|
13144
13342
|
process.exit(0);
|
|
13145
13343
|
}
|
|
13344
|
+
var USAGE = `vo-mcp runner \u2014 bring-your-own agent runner daemon
|
|
13345
|
+
|
|
13346
|
+
Usage:
|
|
13347
|
+
vo-mcp runner poll for tasks forever (default)
|
|
13348
|
+
vo-mcp runner --once claim + run one task, then exit
|
|
13349
|
+
vo-mcp runner --status print pairing/readiness JSON, then exit
|
|
13350
|
+
vo-mcp runner --version print the version, then exit
|
|
13351
|
+
vo-mcp runner --help print this help, then exit
|
|
13352
|
+
|
|
13353
|
+
Env (all optional):
|
|
13354
|
+
VO_CONTROL_PLANE_ADMIN_TOKEN explicit bearer (wins over the stored credential)
|
|
13355
|
+
VO_CONTROL_PLANE_URL control-plane base URL (default: production)
|
|
13356
|
+
VO_CODE_RUNNER_REPO path to your repo clone (default: Git cwd)
|
|
13357
|
+
VO_CODE_RUNNER_CLONES_ROOT managed clone directory (default outside Git cwd)
|
|
13358
|
+
VO_CODE_RUNNER_OPERATOR_IDS operator id(s) this runner serves (your own)
|
|
13359
|
+
VO_CODE_RUNNER_REPOS owner/name repo(s) this runner builds
|
|
13360
|
+
|
|
13361
|
+
Pair this computer first with \`vo-mcp login\`.
|
|
13362
|
+
`;
|
|
13363
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
13364
|
+
process.stdout.write(USAGE);
|
|
13365
|
+
process.exit(0);
|
|
13366
|
+
}
|
|
13146
13367
|
var { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
13147
13368
|
var storedCredential = readStoredCredential2();
|
|
13148
13369
|
var explicitAdminToken = process.env.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
|
|
13149
13370
|
var token = explicitAdminToken || storedCredential?.vo_credential;
|
|
13150
13371
|
var statusOnly = process.argv.includes("--status");
|
|
13372
|
+
function configureRunnerFilesystem() {
|
|
13373
|
+
const config = resolveRunnerRootConfig();
|
|
13374
|
+
if (config.repoRoot) process.env.VO_CODE_RUNNER_REPO = config.repoRoot;
|
|
13375
|
+
else delete process.env.VO_CODE_RUNNER_REPO;
|
|
13376
|
+
if (config.clonesRoot) {
|
|
13377
|
+
assertWritableRunnerDirectory(config.clonesRoot);
|
|
13378
|
+
process.env.VO_CODE_RUNNER_CLONES_ROOT = config.clonesRoot;
|
|
13379
|
+
} else delete process.env.VO_CODE_RUNNER_CLONES_ROOT;
|
|
13380
|
+
process.chdir(runnerWorkingDirectory(config));
|
|
13381
|
+
return config;
|
|
13382
|
+
}
|
|
13151
13383
|
if (statusOnly) {
|
|
13152
13384
|
if (!storedCredential?.vo_credential) {
|
|
13153
13385
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -13156,6 +13388,7 @@ if (statusOnly) {
|
|
|
13156
13388
|
operatorId: null,
|
|
13157
13389
|
tenantId: null,
|
|
13158
13390
|
githubReady: null,
|
|
13391
|
+
filesystemReady: null,
|
|
13159
13392
|
error: "credential_missing",
|
|
13160
13393
|
message: "This computer is not paired. Pair it to your AlgoHQ account first."
|
|
13161
13394
|
})}
|
|
@@ -13166,9 +13399,28 @@ if (statusOnly) {
|
|
|
13166
13399
|
controlPlaneUrl: process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL,
|
|
13167
13400
|
token: storedCredential.vo_credential
|
|
13168
13401
|
});
|
|
13169
|
-
|
|
13402
|
+
if (!readiness.ok) {
|
|
13403
|
+
process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: null })}
|
|
13170
13404
|
`);
|
|
13171
|
-
|
|
13405
|
+
process.exit(1);
|
|
13406
|
+
}
|
|
13407
|
+
try {
|
|
13408
|
+
configureRunnerFilesystem();
|
|
13409
|
+
process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: true })}
|
|
13410
|
+
`);
|
|
13411
|
+
process.exit(0);
|
|
13412
|
+
} catch (error) {
|
|
13413
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13414
|
+
process.stdout.write(`${JSON.stringify({
|
|
13415
|
+
...readiness,
|
|
13416
|
+
ok: false,
|
|
13417
|
+
filesystemReady: false,
|
|
13418
|
+
error: "filesystem_not_ready",
|
|
13419
|
+
message: `Filesystem readiness failed: ${detail}`
|
|
13420
|
+
})}
|
|
13421
|
+
`);
|
|
13422
|
+
process.exit(1);
|
|
13423
|
+
}
|
|
13172
13424
|
}
|
|
13173
13425
|
if (!token) {
|
|
13174
13426
|
console.error("[vo-mcp runner] No credential found. Run `vo-mcp login` first.");
|
|
@@ -13193,6 +13445,13 @@ if (!explicitAdminToken) {
|
|
|
13193
13445
|
process.exit(1);
|
|
13194
13446
|
}
|
|
13195
13447
|
}
|
|
13448
|
+
var rootConfig;
|
|
13449
|
+
try {
|
|
13450
|
+
rootConfig = configureRunnerFilesystem();
|
|
13451
|
+
} catch (error) {
|
|
13452
|
+
console.error(`[vo-mcp runner] Filesystem readiness failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
13453
|
+
process.exit(1);
|
|
13454
|
+
}
|
|
13196
13455
|
var env = {
|
|
13197
13456
|
...process.env,
|
|
13198
13457
|
VO_CONTROL_PLANE_ADMIN_TOKEN: token,
|
|
@@ -13201,7 +13460,8 @@ var env = {
|
|
|
13201
13460
|
// VO_CODE_RUNNER_VERSION, which a desktop host may set to its own shell
|
|
13202
13461
|
// release even after runner-control updates this package in place.
|
|
13203
13462
|
VO_CODE_RUNNER_DAEMON_VERSION: `vo-mcp/${packageVersion()}`,
|
|
13204
|
-
VO_CODE_RUNNER_REPO:
|
|
13463
|
+
...rootConfig.repoRoot ? { VO_CODE_RUNNER_REPO: rootConfig.repoRoot } : {},
|
|
13464
|
+
...rootConfig.clonesRoot ? { VO_CODE_RUNNER_CLONES_ROOT: rootConfig.clonesRoot } : {},
|
|
13205
13465
|
// Server-resolved identity replaces stale/hardcoded desktop scope. Besides
|
|
13206
13466
|
// filtering heartbeat/claims, this keeps GitHub App auth required for the
|
|
13207
13467
|
// entire task lifecycle (never ambient-gh fallback after a paired preflight).
|