@algosuite/vo-mcp 0.2.0-beta.28 → 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/agent-auth-probe-cli.mjs +65 -35
- package/dist/autostart-cli.js +62 -46
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +1336 -238
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1275 -207
- 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 +802 -355
- 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
|
}
|
|
@@ -3001,6 +3107,62 @@ var init_windows_claude_launch = __esm({
|
|
|
3001
3107
|
}
|
|
3002
3108
|
});
|
|
3003
3109
|
|
|
3110
|
+
// ../../scripts/virtual-office/code-runner/claude-credential-choice.mjs
|
|
3111
|
+
function isTruthyFlag(v) {
|
|
3112
|
+
const s = String(v ?? "").trim().toLowerCase();
|
|
3113
|
+
return s === "1" || s === "true" || s === "yes" || s === "on";
|
|
3114
|
+
}
|
|
3115
|
+
function wantsLogin(env2) {
|
|
3116
|
+
return isTruthyFlag(env2[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env2[PREFER_LOGIN_ENV]);
|
|
3117
|
+
}
|
|
3118
|
+
function wantsKey(env2) {
|
|
3119
|
+
return isTruthyFlag(env2[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env2[PREFER_KEY_ENV]);
|
|
3120
|
+
}
|
|
3121
|
+
function classifyClaudeCredential(baseEnv = {}, { getKey, probeLogin } = {}) {
|
|
3122
|
+
const preferKey = wantsKey(baseEnv);
|
|
3123
|
+
if (!preferKey && wantsLogin(baseEnv)) {
|
|
3124
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN, key: null };
|
|
3125
|
+
}
|
|
3126
|
+
if (baseEnv.ANTHROPIC_API_KEY) {
|
|
3127
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.ENV_KEY, key: null };
|
|
3128
|
+
}
|
|
3129
|
+
const key = getKey();
|
|
3130
|
+
if (!key) {
|
|
3131
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.NO_KEY, key: null };
|
|
3132
|
+
}
|
|
3133
|
+
if (preferKey) {
|
|
3134
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN_PREFER_KEY, key };
|
|
3135
|
+
}
|
|
3136
|
+
if (probeLogin() === true) {
|
|
3137
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.SUBSCRIPTION_WINS, key: null };
|
|
3138
|
+
}
|
|
3139
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN, key };
|
|
3140
|
+
}
|
|
3141
|
+
var PREFER_LOGIN_ENV, CLAUDE_PREFER_LOGIN_ENV, PREFER_KEY_ENV, CLAUDE_PREFER_KEY_ENV, CLAUDE_CREDENTIAL_SOURCE;
|
|
3142
|
+
var init_claude_credential_choice = __esm({
|
|
3143
|
+
"../../scripts/virtual-office/code-runner/claude-credential-choice.mjs"() {
|
|
3144
|
+
"use strict";
|
|
3145
|
+
PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
|
|
3146
|
+
CLAUDE_PREFER_LOGIN_ENV = "VO_RUNNER_CLAUDE_PREFER_LOGIN";
|
|
3147
|
+
PREFER_KEY_ENV = "VO_RUNNER_PREFER_KEY";
|
|
3148
|
+
CLAUDE_PREFER_KEY_ENV = "VO_RUNNER_CLAUDE_PREFER_KEY";
|
|
3149
|
+
CLAUDE_CREDENTIAL_SOURCE = Object.freeze({
|
|
3150
|
+
/** PREFER_LOGIN set (and not overridden): any API key is ignored. */
|
|
3151
|
+
PREFER_LOGIN: "prefer_login",
|
|
3152
|
+
/** An explicit ANTHROPIC_API_KEY in the environment — the manual override. */
|
|
3153
|
+
ENV_KEY: "env_key",
|
|
3154
|
+
/** No key anywhere; the spawn falls through to the login session. */
|
|
3155
|
+
NO_KEY: "no_key",
|
|
3156
|
+
/** A stored key, used because the operator explicitly opted out of tier 1. */
|
|
3157
|
+
KEYCHAIN_PREFER_KEY: "keychain_prefer_key",
|
|
3158
|
+
/** A stored key exists but a proven live subscription outranks it. */
|
|
3159
|
+
SUBSCRIPTION_WINS: "subscription_wins",
|
|
3160
|
+
/** A stored key, used because no live subscription was proven. */
|
|
3161
|
+
KEYCHAIN: "keychain"
|
|
3162
|
+
});
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
|
|
3004
3166
|
// ../../scripts/virtual-office/code-runner/anthropic-key-store.mjs
|
|
3005
3167
|
import { createRequire as createRequire2 } from "node:module";
|
|
3006
3168
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -3022,46 +3184,22 @@ function getAnthropicKey({ EntryCtor = defaultEntryCtor() } = {}) {
|
|
|
3022
3184
|
return null;
|
|
3023
3185
|
}
|
|
3024
3186
|
}
|
|
3025
|
-
function isTruthyFlag(v) {
|
|
3026
|
-
const s = String(v ?? "").trim().toLowerCase();
|
|
3027
|
-
return s === "1" || s === "true" || s === "yes" || s === "on";
|
|
3028
|
-
}
|
|
3029
|
-
function wantsLogin(env2) {
|
|
3030
|
-
return isTruthyFlag(env2[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env2[PREFER_LOGIN_ENV]);
|
|
3031
|
-
}
|
|
3032
|
-
function wantsKey(env2) {
|
|
3033
|
-
return isTruthyFlag(env2[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env2[PREFER_KEY_ENV]);
|
|
3034
|
-
}
|
|
3035
3187
|
function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
3036
|
-
const
|
|
3037
|
-
|
|
3038
|
-
|
|
3188
|
+
const { source, key } = classifyClaudeCredential(baseEnv, { getKey, probeLogin });
|
|
3189
|
+
const next = { ...baseEnv };
|
|
3190
|
+
if (source === CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN) {
|
|
3039
3191
|
delete next.ANTHROPIC_API_KEY;
|
|
3040
3192
|
return next;
|
|
3041
3193
|
}
|
|
3042
|
-
if (
|
|
3043
|
-
|
|
3044
|
-
if (!key) return { ...baseEnv };
|
|
3045
|
-
if (!preferKey && probeLogin() === true) return { ...baseEnv };
|
|
3046
|
-
return { ...baseEnv, ANTHROPIC_API_KEY: key };
|
|
3194
|
+
if (key !== null) next.ANTHROPIC_API_KEY = key;
|
|
3195
|
+
return next;
|
|
3047
3196
|
}
|
|
3048
3197
|
function claudeCostBasis(env2 = process.env) {
|
|
3049
3198
|
return String(env2.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
|
|
3050
3199
|
}
|
|
3051
3200
|
function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
3052
|
-
const
|
|
3053
|
-
|
|
3054
|
-
return "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)";
|
|
3055
|
-
}
|
|
3056
|
-
if (baseEnv.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY from environment";
|
|
3057
|
-
if (!getKey()) return "claude auth login session (no API key set)";
|
|
3058
|
-
if (preferKey) {
|
|
3059
|
-
return "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)";
|
|
3060
|
-
}
|
|
3061
|
-
if (probeLogin() === true) {
|
|
3062
|
-
return "claude auth login session (subscription beats the stored keychain key)";
|
|
3063
|
-
}
|
|
3064
|
-
return "ANTHROPIC_API_KEY from OS keychain";
|
|
3201
|
+
const { source } = classifyClaudeCredential(baseEnv, { getKey, probeLogin });
|
|
3202
|
+
return AUTH_SOURCE_DESCRIPTION[source];
|
|
3065
3203
|
}
|
|
3066
3204
|
function augmentAuthError(summary) {
|
|
3067
3205
|
const s = String(summary ?? "");
|
|
@@ -3083,19 +3221,25 @@ function probeClaudeLoginState({
|
|
|
3083
3221
|
return null;
|
|
3084
3222
|
}
|
|
3085
3223
|
}
|
|
3086
|
-
var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried,
|
|
3224
|
+
var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried, AUTH_SOURCE_DESCRIPTION, AUTH_ERROR_RE;
|
|
3087
3225
|
var init_anthropic_key_store = __esm({
|
|
3088
3226
|
"../../scripts/virtual-office/code-runner/anthropic-key-store.mjs"() {
|
|
3089
3227
|
"use strict";
|
|
3090
3228
|
init_windows_claude_launch();
|
|
3229
|
+
init_claude_credential_choice();
|
|
3230
|
+
init_claude_credential_choice();
|
|
3091
3231
|
require2 = createRequire2(import.meta.url);
|
|
3092
3232
|
KEY_SERVICE = "algosuite-vo";
|
|
3093
3233
|
KEY_ACCOUNT = "anthropic-api-key";
|
|
3094
3234
|
_loadTried = false;
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3235
|
+
AUTH_SOURCE_DESCRIPTION = Object.freeze({
|
|
3236
|
+
[CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN]: "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)",
|
|
3237
|
+
[CLAUDE_CREDENTIAL_SOURCE.ENV_KEY]: "ANTHROPIC_API_KEY from environment",
|
|
3238
|
+
[CLAUDE_CREDENTIAL_SOURCE.NO_KEY]: "claude auth login session (no API key set)",
|
|
3239
|
+
[CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN_PREFER_KEY]: "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)",
|
|
3240
|
+
[CLAUDE_CREDENTIAL_SOURCE.SUBSCRIPTION_WINS]: "claude auth login session (subscription beats the stored keychain key)",
|
|
3241
|
+
[CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN]: "ANTHROPIC_API_KEY from OS keychain"
|
|
3242
|
+
});
|
|
3099
3243
|
AUTH_ERROR_RE = /\b401\b|invalid[^.]{0,24}(authentication|credential)|authentication_error|unauthorized|not[ _-]?authenticated/i;
|
|
3100
3244
|
}
|
|
3101
3245
|
});
|
|
@@ -3366,14 +3510,14 @@ var init_terminal_process_cleanup = __esm({
|
|
|
3366
3510
|
|
|
3367
3511
|
// ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
|
|
3368
3512
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3369
|
-
import { existsSync as
|
|
3513
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3370
3514
|
import os from "node:os";
|
|
3371
|
-
import
|
|
3515
|
+
import path12 from "node:path";
|
|
3372
3516
|
function registryRoot(tmp = os.tmpdir()) {
|
|
3373
|
-
return
|
|
3517
|
+
return path12.join(tmp, REGISTRY_ROOT_NAME);
|
|
3374
3518
|
}
|
|
3375
3519
|
function instanceDir(root, instanceId) {
|
|
3376
|
-
return
|
|
3520
|
+
return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
|
|
3377
3521
|
}
|
|
3378
3522
|
function registerDaemonInstance({
|
|
3379
3523
|
root = registryRoot(),
|
|
@@ -3383,8 +3527,8 @@ function registerDaemonInstance({
|
|
|
3383
3527
|
} = {}) {
|
|
3384
3528
|
if (!instanceId) return null;
|
|
3385
3529
|
const dir = instanceDir(root, instanceId);
|
|
3386
|
-
|
|
3387
|
-
const file =
|
|
3530
|
+
mkdirSync3(dir, { recursive: true });
|
|
3531
|
+
const file = path12.join(dir, DAEMON_RECORD);
|
|
3388
3532
|
writeFileSync2(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
|
|
3389
3533
|
encoding: "utf8",
|
|
3390
3534
|
mode: 384
|
|
@@ -3401,9 +3545,9 @@ function recordAgentPid({
|
|
|
3401
3545
|
if (!instanceId || !Number.isInteger(pid) || pid <= 0) return false;
|
|
3402
3546
|
try {
|
|
3403
3547
|
const dir = instanceDir(root, instanceId);
|
|
3404
|
-
|
|
3548
|
+
mkdirSync3(dir, { recursive: true });
|
|
3405
3549
|
writeFileSync2(
|
|
3406
|
-
|
|
3550
|
+
path12.join(dir, `${pid}.json`),
|
|
3407
3551
|
JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
|
|
3408
3552
|
{ encoding: "utf8", mode: 384 }
|
|
3409
3553
|
);
|
|
@@ -3419,7 +3563,7 @@ function unrecordAgentPid({
|
|
|
3419
3563
|
} = {}) {
|
|
3420
3564
|
if (!instanceId || !Number.isInteger(pid)) return false;
|
|
3421
3565
|
try {
|
|
3422
|
-
rmSync2(
|
|
3566
|
+
rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
|
|
3423
3567
|
return true;
|
|
3424
3568
|
} catch {
|
|
3425
3569
|
return false;
|
|
@@ -3437,7 +3581,7 @@ function bootstrapOrphanReaper({ instanceId, log: log2 = () => {
|
|
|
3437
3581
|
}
|
|
3438
3582
|
function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
3439
3583
|
const instances = [];
|
|
3440
|
-
if (!
|
|
3584
|
+
if (!existsSync4(root)) return instances;
|
|
3441
3585
|
let dirents;
|
|
3442
3586
|
try {
|
|
3443
3587
|
dirents = readdirSync(root, { withFileTypes: true });
|
|
@@ -3447,7 +3591,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3447
3591
|
for (const dirent of dirents) {
|
|
3448
3592
|
if (!dirent.isDirectory()) continue;
|
|
3449
3593
|
if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
|
|
3450
|
-
const dir =
|
|
3594
|
+
const dir = path12.join(root, dirent.name);
|
|
3451
3595
|
let daemon = null;
|
|
3452
3596
|
const agents = [];
|
|
3453
3597
|
let files;
|
|
@@ -3459,7 +3603,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3459
3603
|
for (const name of files) {
|
|
3460
3604
|
let parsed;
|
|
3461
3605
|
try {
|
|
3462
|
-
parsed = JSON.parse(readFileSync2(
|
|
3606
|
+
parsed = JSON.parse(readFileSync2(path12.join(dir, name), "utf8"));
|
|
3463
3607
|
} catch {
|
|
3464
3608
|
continue;
|
|
3465
3609
|
}
|
|
@@ -3507,7 +3651,7 @@ function windowsSystemRoot(env2 = process.env) {
|
|
|
3507
3651
|
return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
3508
3652
|
}
|
|
3509
3653
|
function windowsPowershellExe(env2 = process.env) {
|
|
3510
|
-
return
|
|
3654
|
+
return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
3511
3655
|
}
|
|
3512
3656
|
function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
|
|
3513
3657
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3552,7 +3696,7 @@ function parsePosixPsLine(line) {
|
|
|
3552
3696
|
function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
|
|
3553
3697
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3554
3698
|
if (platform === "win32") {
|
|
3555
|
-
const taskkill =
|
|
3699
|
+
const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
|
|
3556
3700
|
const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
3557
3701
|
return !r.error && r.status === 0;
|
|
3558
3702
|
}
|
|
@@ -4341,15 +4485,15 @@ var init_flat_token_usage = __esm({
|
|
|
4341
4485
|
|
|
4342
4486
|
// ../../scripts/virtual-office/code-runner/codex-runner.mjs
|
|
4343
4487
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
4344
|
-
import { existsSync as
|
|
4345
|
-
import { win32 } from "node:path";
|
|
4488
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4489
|
+
import { win32 as win322 } from "node:path";
|
|
4346
4490
|
function isTruthyFlag2(value) {
|
|
4347
4491
|
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
|
4348
4492
|
}
|
|
4349
4493
|
function resolveCodexBinary({
|
|
4350
4494
|
env: env2 = process.env,
|
|
4351
4495
|
platform = process.platform,
|
|
4352
|
-
exists =
|
|
4496
|
+
exists = existsSync5
|
|
4353
4497
|
} = {}) {
|
|
4354
4498
|
if (platform !== "win32") return "codex";
|
|
4355
4499
|
const appData = String(env2.APPDATA || "").trim();
|
|
@@ -4357,7 +4501,7 @@ function resolveCodexBinary({
|
|
|
4357
4501
|
const localAppData = String(env2.LOCALAPPDATA || "").trim();
|
|
4358
4502
|
const candidates = [];
|
|
4359
4503
|
if (appData) {
|
|
4360
|
-
candidates.push(
|
|
4504
|
+
candidates.push(win322.join(
|
|
4361
4505
|
appData,
|
|
4362
4506
|
"npm",
|
|
4363
4507
|
"node_modules",
|
|
@@ -4373,11 +4517,11 @@ function resolveCodexBinary({
|
|
|
4373
4517
|
));
|
|
4374
4518
|
}
|
|
4375
4519
|
if (userProfile) {
|
|
4376
|
-
candidates.push(
|
|
4377
|
-
candidates.push(
|
|
4520
|
+
candidates.push(win322.join(userProfile, ".local", "bin", "codex.exe"));
|
|
4521
|
+
candidates.push(win322.join(userProfile, ".codex", "bin", "codex.exe"));
|
|
4378
4522
|
}
|
|
4379
4523
|
if (localAppData) {
|
|
4380
|
-
candidates.push(
|
|
4524
|
+
candidates.push(win322.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
|
|
4381
4525
|
}
|
|
4382
4526
|
const absolute = candidates.find((candidate) => exists(candidate));
|
|
4383
4527
|
if (absolute) return absolute;
|
|
@@ -5206,9 +5350,9 @@ var init_rate_limit_detector_core = __esm({
|
|
|
5206
5350
|
|
|
5207
5351
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
|
|
5208
5352
|
import fsp10 from "node:fs/promises";
|
|
5209
|
-
import
|
|
5353
|
+
import path13 from "node:path";
|
|
5210
5354
|
async function atomicWrite(file, content) {
|
|
5211
|
-
await fsp10.mkdir(
|
|
5355
|
+
await fsp10.mkdir(path13.dirname(file), { recursive: true });
|
|
5212
5356
|
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
5213
5357
|
const handle = await fsp10.open(temp, "wx");
|
|
5214
5358
|
try {
|
|
@@ -5269,7 +5413,7 @@ function writeResumeAttempts(file, store) {
|
|
|
5269
5413
|
}
|
|
5270
5414
|
async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
|
|
5271
5415
|
const deadline = now() + LOCK_WAIT_MS;
|
|
5272
|
-
await fsp10.mkdir(
|
|
5416
|
+
await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
|
|
5273
5417
|
for (; ; ) {
|
|
5274
5418
|
let handle;
|
|
5275
5419
|
try {
|
|
@@ -5343,10 +5487,10 @@ var init_rate_limit_resume_state = __esm({
|
|
|
5343
5487
|
});
|
|
5344
5488
|
|
|
5345
5489
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
|
|
5346
|
-
import { homedir as
|
|
5490
|
+
import { homedir as homedir3 } from "node:os";
|
|
5347
5491
|
import { join as join2 } from "node:path";
|
|
5348
5492
|
function resumeQueuePath() {
|
|
5349
|
-
return join2(
|
|
5493
|
+
return join2(homedir3(), ".claude", "resume-queue.jsonl");
|
|
5350
5494
|
}
|
|
5351
5495
|
function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
|
|
5352
5496
|
return {
|
|
@@ -5547,7 +5691,7 @@ var init_auto_merge = __esm({
|
|
|
5547
5691
|
|
|
5548
5692
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
5549
5693
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
5550
|
-
import { existsSync as
|
|
5694
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
5551
5695
|
import { fileURLToPath } from "node:url";
|
|
5552
5696
|
function stripCredentials(env2 = process.env) {
|
|
5553
5697
|
const safe = { ...env2 };
|
|
@@ -5558,7 +5702,7 @@ function resolveOverlapScript({
|
|
|
5558
5702
|
worktreeDir,
|
|
5559
5703
|
trustedPath = null,
|
|
5560
5704
|
trustedPaths = TRUSTED_OVERLAP_CANDIDATES,
|
|
5561
|
-
existsFn =
|
|
5705
|
+
existsFn = existsSync6,
|
|
5562
5706
|
joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
|
|
5563
5707
|
} = {}) {
|
|
5564
5708
|
const candidates = trustedPath ? [trustedPath] : trustedPaths;
|
|
@@ -5627,14 +5771,14 @@ function parsePorcelainZ(out) {
|
|
|
5627
5771
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
5628
5772
|
const token2 = tokens[i];
|
|
5629
5773
|
if (!token2) continue;
|
|
5630
|
-
const
|
|
5631
|
-
if (
|
|
5774
|
+
const path20 = token2.slice(3);
|
|
5775
|
+
if (path20) files.push(path20);
|
|
5632
5776
|
if (token2[0] === "R" || token2[0] === "C") i += 1;
|
|
5633
5777
|
}
|
|
5634
5778
|
return files;
|
|
5635
5779
|
}
|
|
5636
|
-
function isAgentScratch(
|
|
5637
|
-
const normalized = String(
|
|
5780
|
+
function isAgentScratch(path20) {
|
|
5781
|
+
const normalized = String(path20 || "");
|
|
5638
5782
|
return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
5639
5783
|
}
|
|
5640
5784
|
var SCRATCH_PATTERNS;
|
|
@@ -6467,10 +6611,10 @@ var init_task_prompt = __esm({
|
|
|
6467
6611
|
});
|
|
6468
6612
|
|
|
6469
6613
|
// ../../scripts/virtual-office/code-runner/task-attachments.mjs
|
|
6470
|
-
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
6614
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
6471
6615
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
6472
6616
|
import os2 from "node:os";
|
|
6473
|
-
import
|
|
6617
|
+
import path14 from "node:path";
|
|
6474
6618
|
function safeTaskToken(taskId) {
|
|
6475
6619
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
6476
6620
|
}
|
|
@@ -6480,25 +6624,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
|
|
|
6480
6624
|
return `${String(index + 1).padStart(2, "0")}-${normalized}`;
|
|
6481
6625
|
}
|
|
6482
6626
|
function assertGeneratedDirectory(directory, tempRoot) {
|
|
6483
|
-
const resolvedDirectory =
|
|
6484
|
-
const resolvedRoot =
|
|
6485
|
-
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)) {
|
|
6486
6630
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
6487
6631
|
}
|
|
6488
6632
|
return resolvedDirectory;
|
|
6489
6633
|
}
|
|
6490
6634
|
async function createAttachmentDirectory(taskId, tempRoot) {
|
|
6491
|
-
const root =
|
|
6635
|
+
const root = path14.resolve(tempRoot);
|
|
6492
6636
|
await mkdir(root, { recursive: true });
|
|
6493
|
-
const directory = await mkdtemp(
|
|
6494
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token:
|
|
6495
|
-
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 });
|
|
6496
6640
|
return { directory, marker, tempRoot: root };
|
|
6497
6641
|
}
|
|
6498
6642
|
async function cleanupGeneratedDirectory(state) {
|
|
6499
6643
|
if (!state || state.cleaned) return;
|
|
6500
6644
|
const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
|
|
6501
|
-
const marker = await readFile(
|
|
6645
|
+
const marker = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
6502
6646
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
6503
6647
|
await rm(directory, { recursive: true, force: true });
|
|
6504
6648
|
state.cleaned = true;
|
|
@@ -6517,7 +6661,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6517
6661
|
now = Date.now(),
|
|
6518
6662
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
6519
6663
|
} = {}) {
|
|
6520
|
-
const root =
|
|
6664
|
+
const root = path14.resolve(tempRoot);
|
|
6521
6665
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
6522
6666
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
6523
6667
|
if (error?.code === "ENOENT") return [];
|
|
@@ -6526,8 +6670,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6526
6670
|
let removed = 0;
|
|
6527
6671
|
for (const entry of entries) {
|
|
6528
6672
|
if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
|
|
6529
|
-
const directory = assertGeneratedDirectory(
|
|
6530
|
-
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(() => "");
|
|
6531
6675
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
6532
6676
|
if (!marker) continue;
|
|
6533
6677
|
const directoryStat = await stat(directory);
|
|
@@ -6570,10 +6714,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
6570
6714
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
6571
6715
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
6572
6716
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
6573
|
-
const filePath =
|
|
6717
|
+
const filePath = path14.join(state.directory, name);
|
|
6574
6718
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
6575
6719
|
await chmod(filePath, 384);
|
|
6576
|
-
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) });
|
|
6577
6721
|
}
|
|
6578
6722
|
return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
|
|
6579
6723
|
} catch (error) {
|
|
@@ -6595,7 +6739,7 @@ var init_task_attachments = __esm({
|
|
|
6595
6739
|
});
|
|
6596
6740
|
|
|
6597
6741
|
// ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
|
|
6598
|
-
import { homedir as
|
|
6742
|
+
import { homedir as homedir4 } from "node:os";
|
|
6599
6743
|
import { join as join4 } from "node:path";
|
|
6600
6744
|
import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
6601
6745
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -6637,9 +6781,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
6637
6781
|
}
|
|
6638
6782
|
return out;
|
|
6639
6783
|
}
|
|
6640
|
-
async function readCloudMap(
|
|
6784
|
+
async function readCloudMap(path20) {
|
|
6641
6785
|
try {
|
|
6642
|
-
return JSON.parse(await readFile2(
|
|
6786
|
+
return JSON.parse(await readFile2(path20, "utf8"));
|
|
6643
6787
|
} catch {
|
|
6644
6788
|
return {};
|
|
6645
6789
|
}
|
|
@@ -6712,8 +6856,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
|
|
|
6712
6856
|
var init_session_spool_forwarder = __esm({
|
|
6713
6857
|
"../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
|
|
6714
6858
|
"use strict";
|
|
6715
|
-
SPOOL_DIR = join4(
|
|
6716
|
-
CLOUD_MAP_FILE = join4(
|
|
6859
|
+
SPOOL_DIR = join4(homedir4(), ".vo", "session-spool");
|
|
6860
|
+
CLOUD_MAP_FILE = join4(homedir4(), ".vo", "session-cloud-map.json");
|
|
6717
6861
|
STALE_MS = 60 * 60 * 1e3;
|
|
6718
6862
|
ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
|
|
6719
6863
|
}
|
|
@@ -7396,7 +7540,7 @@ var init_local_model_remote_config = __esm({
|
|
|
7396
7540
|
|
|
7397
7541
|
// ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
|
|
7398
7542
|
import crypto from "node:crypto";
|
|
7399
|
-
import
|
|
7543
|
+
import fs7 from "node:fs";
|
|
7400
7544
|
function accountKey(agent, rawId) {
|
|
7401
7545
|
const id = typeof rawId === "string" ? rawId.trim() : "";
|
|
7402
7546
|
if (!id) return null;
|
|
@@ -7443,6 +7587,13 @@ function makeUsageRow({
|
|
|
7443
7587
|
}
|
|
7444
7588
|
return row;
|
|
7445
7589
|
}
|
|
7590
|
+
function readingAgeMs(row, nowMs = Date.now()) {
|
|
7591
|
+
const at = row && typeof row.captured_at === "string" ? row.captured_at : null;
|
|
7592
|
+
if (!at) return null;
|
|
7593
|
+
const ms = new Date(at).getTime();
|
|
7594
|
+
if (!Number.isFinite(ms)) return null;
|
|
7595
|
+
return Math.max(0, nowMs - ms);
|
|
7596
|
+
}
|
|
7446
7597
|
var clampPct, readJson, ACCOUNT_KEY_SALT;
|
|
7447
7598
|
var init_shared = __esm({
|
|
7448
7599
|
"../../scripts/virtual-office/code-runner/account-usage/shared.mjs"() {
|
|
@@ -7453,7 +7604,7 @@ var init_shared = __esm({
|
|
|
7453
7604
|
};
|
|
7454
7605
|
readJson = (p) => {
|
|
7455
7606
|
try {
|
|
7456
|
-
return JSON.parse(
|
|
7607
|
+
return JSON.parse(fs7.readFileSync(p, "utf8"));
|
|
7457
7608
|
} catch {
|
|
7458
7609
|
return null;
|
|
7459
7610
|
}
|
|
@@ -7463,14 +7614,23 @@ var init_shared = __esm({
|
|
|
7463
7614
|
});
|
|
7464
7615
|
|
|
7465
7616
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
7617
|
+
import fs8 from "node:fs";
|
|
7466
7618
|
import os3 from "node:os";
|
|
7467
|
-
import
|
|
7619
|
+
import path15 from "node:path";
|
|
7620
|
+
function fileCaptureTime(filePath, explicit, statFn) {
|
|
7621
|
+
if (typeof explicit === "string" && explicit) return explicit;
|
|
7622
|
+
try {
|
|
7623
|
+
return statFn(filePath).mtime.toISOString();
|
|
7624
|
+
} catch {
|
|
7625
|
+
return null;
|
|
7626
|
+
}
|
|
7627
|
+
}
|
|
7468
7628
|
function usageBaseUrl(env2 = process.env) {
|
|
7469
7629
|
const raw = env2.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
|
|
7470
7630
|
return String(raw).replace(/\/+$/, "");
|
|
7471
7631
|
}
|
|
7472
7632
|
function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
|
|
7473
|
-
const creds = read(
|
|
7633
|
+
const creds = read(path15.join(homeDir, ".claude", ".credentials.json"));
|
|
7474
7634
|
const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
|
|
7475
7635
|
if (!oauth || typeof oauth !== "object") return null;
|
|
7476
7636
|
const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
|
|
@@ -7480,7 +7640,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
|
|
|
7480
7640
|
return token2;
|
|
7481
7641
|
}
|
|
7482
7642
|
function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
|
|
7483
|
-
const cfg = read(
|
|
7643
|
+
const cfg = read(path15.join(homeDir, ".claude.json"));
|
|
7484
7644
|
const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
|
|
7485
7645
|
return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
|
|
7486
7646
|
}
|
|
@@ -7570,7 +7730,12 @@ async function readClaudeOAuthUsage({
|
|
|
7570
7730
|
clearTimeout(timer);
|
|
7571
7731
|
}
|
|
7572
7732
|
}
|
|
7573
|
-
function readClaudeFileUsage({
|
|
7733
|
+
function readClaudeFileUsage({
|
|
7734
|
+
homeDir = os3.homedir(),
|
|
7735
|
+
read: rawRead = readJson,
|
|
7736
|
+
statFn = fs8.statSync,
|
|
7737
|
+
now = () => Date.now()
|
|
7738
|
+
} = {}) {
|
|
7574
7739
|
const read = (p) => {
|
|
7575
7740
|
try {
|
|
7576
7741
|
return rawRead(p);
|
|
@@ -7579,31 +7744,39 @@ function readClaudeFileUsage({ homeDir = os3.homedir(), read: rawRead = readJson
|
|
|
7579
7744
|
}
|
|
7580
7745
|
};
|
|
7581
7746
|
const accountId = readAccountId({ homeDir, read });
|
|
7582
|
-
const
|
|
7747
|
+
const fresh = (row) => {
|
|
7748
|
+
if (!row) return null;
|
|
7749
|
+
const age = readingAgeMs(row, now());
|
|
7750
|
+
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
7751
|
+
return row;
|
|
7752
|
+
};
|
|
7753
|
+
const statusPath = path15.join(homeDir, ".claude", "claude-usage.json");
|
|
7754
|
+
const status = read(statusPath);
|
|
7583
7755
|
if (status && (status.seven_day || status.five_hour)) {
|
|
7584
|
-
const row = makeUsageRow({
|
|
7756
|
+
const row = fresh(makeUsageRow({
|
|
7585
7757
|
agent: "claude",
|
|
7586
7758
|
source: "statusline",
|
|
7587
|
-
capturedAt:
|
|
7759
|
+
capturedAt: fileCaptureTime(statusPath, status.capturedAt, statFn),
|
|
7588
7760
|
accountId,
|
|
7589
7761
|
sevenDay: status.seven_day?.used_percentage,
|
|
7590
7762
|
fiveHour: status.five_hour?.used_percentage,
|
|
7591
7763
|
sevenDayResetsAt: status.seven_day?.resets_at ?? null,
|
|
7592
7764
|
fiveHourResetsAt: status.five_hour?.resets_at ?? null
|
|
7593
|
-
});
|
|
7765
|
+
}));
|
|
7594
7766
|
if (row) return row;
|
|
7595
7767
|
}
|
|
7596
|
-
const
|
|
7768
|
+
const weeklyPath = path15.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
7769
|
+
const weekly = read(weeklyPath);
|
|
7597
7770
|
if (weekly) {
|
|
7598
|
-
const row = makeUsageRow({
|
|
7771
|
+
const row = fresh(makeUsageRow({
|
|
7599
7772
|
agent: "claude",
|
|
7600
7773
|
source: "file",
|
|
7601
|
-
capturedAt:
|
|
7774
|
+
capturedAt: fileCaptureTime(weeklyPath, weekly.capturedAt, statFn),
|
|
7602
7775
|
accountId,
|
|
7603
7776
|
sevenDay: weekly.sevenDayPct,
|
|
7604
7777
|
fiveHour: weekly.fiveHourPct,
|
|
7605
7778
|
sevenDayResetsAt: weekly.sevenDayResetsAt ?? null
|
|
7606
|
-
});
|
|
7779
|
+
}));
|
|
7607
7780
|
if (row) return row;
|
|
7608
7781
|
}
|
|
7609
7782
|
return null;
|
|
@@ -7616,11 +7789,12 @@ async function readClaudeUsage(opts = {}) {
|
|
|
7616
7789
|
}
|
|
7617
7790
|
return readClaudeFileUsage(opts);
|
|
7618
7791
|
}
|
|
7619
|
-
var USAGE_PATH, OAUTH_BETA, DEFAULT_TIMEOUT_MS;
|
|
7792
|
+
var MAX_FILE_AGE_MS, USAGE_PATH, OAUTH_BETA, DEFAULT_TIMEOUT_MS;
|
|
7620
7793
|
var init_claude = __esm({
|
|
7621
7794
|
"../../scripts/virtual-office/code-runner/account-usage/claude.mjs"() {
|
|
7622
7795
|
"use strict";
|
|
7623
7796
|
init_shared();
|
|
7797
|
+
MAX_FILE_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
7624
7798
|
USAGE_PATH = "/api/oauth/usage";
|
|
7625
7799
|
OAUTH_BETA = "oauth-2025-04-20";
|
|
7626
7800
|
DEFAULT_TIMEOUT_MS = 5e3;
|
|
@@ -8030,7 +8204,7 @@ var init_watcher_coordination = __esm({
|
|
|
8030
8204
|
});
|
|
8031
8205
|
|
|
8032
8206
|
// ../../scripts/virtual-office/code-runner/watcher-state.mjs
|
|
8033
|
-
import { randomUUID as
|
|
8207
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
8034
8208
|
import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
|
|
8035
8209
|
import { dirname as dirname4 } from "node:path";
|
|
8036
8210
|
async function readWatcherState(stateFile) {
|
|
@@ -8050,7 +8224,7 @@ async function readWatcherState(stateFile) {
|
|
|
8050
8224
|
async function writeWatcherState(stateFile, state) {
|
|
8051
8225
|
const directory = dirname4(stateFile);
|
|
8052
8226
|
await mkdir2(directory, { recursive: true });
|
|
8053
|
-
const temp = `${stateFile}.${process.pid}.${
|
|
8227
|
+
const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
|
|
8054
8228
|
let handle;
|
|
8055
8229
|
try {
|
|
8056
8230
|
handle = await open(temp, "wx");
|
|
@@ -8261,7 +8435,7 @@ var init_pr_watcher_github = __esm({
|
|
|
8261
8435
|
});
|
|
8262
8436
|
|
|
8263
8437
|
// ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
|
|
8264
|
-
import { randomUUID as
|
|
8438
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8265
8439
|
async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
8266
8440
|
}) {
|
|
8267
8441
|
const requestedBudgetUsd = task?.max_budget_usd;
|
|
@@ -8275,7 +8449,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
|
8275
8449
|
if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
|
|
8276
8450
|
throw new Error("autonomous dispatch admission client unavailable");
|
|
8277
8451
|
}
|
|
8278
|
-
const reservationId =
|
|
8452
|
+
const reservationId = randomUUID4();
|
|
8279
8453
|
const admission = await client.reserveAutonomousDispatchBudget({
|
|
8280
8454
|
requestedBudgetUsd,
|
|
8281
8455
|
reservationId,
|
|
@@ -8293,7 +8467,7 @@ var init_enqueue_autonomous_code_task = __esm({
|
|
|
8293
8467
|
});
|
|
8294
8468
|
|
|
8295
8469
|
// ../../scripts/virtual-office/code-runner/pr-watcher.mjs
|
|
8296
|
-
import { homedir as
|
|
8470
|
+
import { homedir as homedir5 } from "node:os";
|
|
8297
8471
|
import { join as join6 } from "node:path";
|
|
8298
8472
|
function parsePrCiStatus(view) {
|
|
8299
8473
|
const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
|
|
@@ -8637,7 +8811,7 @@ var init_pr_watcher = __esm({
|
|
|
8637
8811
|
init_watcher_state();
|
|
8638
8812
|
init_superseded_pr_source();
|
|
8639
8813
|
init_ci_fix_prompt();
|
|
8640
|
-
DEFAULT_STATE_FILE = join6(
|
|
8814
|
+
DEFAULT_STATE_FILE = join6(homedir5(), ".vo", "dispatched-prs.json");
|
|
8641
8815
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
8642
8816
|
"FAILURE",
|
|
8643
8817
|
"TIMED_OUT",
|
|
@@ -8867,9 +9041,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8867
9041
|
res.end();
|
|
8868
9042
|
return;
|
|
8869
9043
|
}
|
|
8870
|
-
const
|
|
9044
|
+
const path20 = String(req.url || "").split("?")[0];
|
|
8871
9045
|
res.setHeader("content-type", "application/json");
|
|
8872
|
-
if (req.method === "GET" &&
|
|
9046
|
+
if (req.method === "GET" && path20 === "/status") {
|
|
8873
9047
|
let status;
|
|
8874
9048
|
try {
|
|
8875
9049
|
status = getStatus();
|
|
@@ -8880,7 +9054,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8880
9054
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
8881
9055
|
return;
|
|
8882
9056
|
}
|
|
8883
|
-
if (req.method === "POST" &&
|
|
9057
|
+
if (req.method === "POST" && path20 === "/stop") {
|
|
8884
9058
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
8885
9059
|
res.statusCode = 403;
|
|
8886
9060
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -9043,8 +9217,8 @@ var init_effort_mode_config = __esm({
|
|
|
9043
9217
|
});
|
|
9044
9218
|
|
|
9045
9219
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
9046
|
-
import
|
|
9047
|
-
import
|
|
9220
|
+
import fs9 from "node:fs";
|
|
9221
|
+
import path16 from "node:path";
|
|
9048
9222
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9049
9223
|
function uniqueModels(models = []) {
|
|
9050
9224
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
@@ -9156,9 +9330,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
|
|
|
9156
9330
|
return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
|
|
9157
9331
|
}
|
|
9158
9332
|
function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
|
|
9159
|
-
if (!
|
|
9333
|
+
if (!fs9.existsSync(cacheFile)) return null;
|
|
9160
9334
|
try {
|
|
9161
|
-
const parsed = JSON.parse(
|
|
9335
|
+
const parsed = JSON.parse(fs9.readFileSync(cacheFile, "utf-8"));
|
|
9162
9336
|
if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
|
|
9163
9337
|
if (!Array.isArray(parsed.models)) return null;
|
|
9164
9338
|
return parsed;
|
|
@@ -9167,8 +9341,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
9167
9341
|
}
|
|
9168
9342
|
}
|
|
9169
9343
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
9170
|
-
|
|
9171
|
-
|
|
9344
|
+
fs9.mkdirSync(path16.dirname(cacheFile), { recursive: true });
|
|
9345
|
+
fs9.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
9172
9346
|
}
|
|
9173
9347
|
async function fetchRegistryCatalog({
|
|
9174
9348
|
fetchImpl = fetch,
|
|
@@ -9225,10 +9399,10 @@ var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANT
|
|
|
9225
9399
|
var init_model_registry = __esm({
|
|
9226
9400
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
9227
9401
|
"use strict";
|
|
9228
|
-
__dirname =
|
|
9229
|
-
ROOT =
|
|
9230
|
-
DEFAULT_CACHE_DIR =
|
|
9231
|
-
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");
|
|
9232
9406
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
9233
9407
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
9234
9408
|
FAMILY_DEFINITIONS = {
|
|
@@ -9836,7 +10010,7 @@ var init_classify_task = __esm({
|
|
|
9836
10010
|
|
|
9837
10011
|
// ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
|
|
9838
10012
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
9839
|
-
import { homedir as
|
|
10013
|
+
import { homedir as homedir6 } from "node:os";
|
|
9840
10014
|
import { join as join7 } from "node:path";
|
|
9841
10015
|
function difficultyToRung(difficulty, thresholds) {
|
|
9842
10016
|
const b = thresholds.rungBounds;
|
|
@@ -9862,9 +10036,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
9862
10036
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
9863
10037
|
return base;
|
|
9864
10038
|
}
|
|
9865
|
-
function readCodexModelsCache({ path:
|
|
10039
|
+
function readCodexModelsCache({ path: path20 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
|
|
9866
10040
|
try {
|
|
9867
|
-
const parsed = JSON.parse(read(
|
|
10041
|
+
const parsed = JSON.parse(read(path20, "utf8"));
|
|
9868
10042
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
9869
10043
|
} catch {
|
|
9870
10044
|
return null;
|
|
@@ -9914,7 +10088,7 @@ var init_effort_policy = __esm({
|
|
|
9914
10088
|
init_meta_model_catalog();
|
|
9915
10089
|
RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
|
|
9916
10090
|
rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
|
|
9917
|
-
DEFAULT_CODEX_MODELS_CACHE = join7(
|
|
10091
|
+
DEFAULT_CODEX_MODELS_CACHE = join7(homedir6(), ".codex", "models_cache.json");
|
|
9918
10092
|
}
|
|
9919
10093
|
});
|
|
9920
10094
|
|
|
@@ -10044,8 +10218,8 @@ var init_role_cost_shadow = __esm({
|
|
|
10044
10218
|
});
|
|
10045
10219
|
|
|
10046
10220
|
// ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
|
|
10047
|
-
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as
|
|
10048
|
-
import { homedir as
|
|
10221
|
+
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
10222
|
+
import { homedir as homedir7 } from "node:os";
|
|
10049
10223
|
import { join as join8, dirname as dirname5 } from "node:path";
|
|
10050
10224
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
10051
10225
|
function getAutoRouterMode(env2 = process.env) {
|
|
@@ -10121,15 +10295,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
10121
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("; ")}`;
|
|
10122
10296
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
10123
10297
|
}
|
|
10124
|
-
function appendDecisionFallback(decision, { path:
|
|
10298
|
+
function appendDecisionFallback(decision, { path: path20 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
|
|
10125
10299
|
try {
|
|
10126
|
-
mkdir4(dirname5(
|
|
10127
|
-
append(
|
|
10300
|
+
mkdir4(dirname5(path20), { recursive: true });
|
|
10301
|
+
append(path20, `${JSON.stringify(decision)}
|
|
10128
10302
|
`, "utf8");
|
|
10129
10303
|
if (isRouterDecision(decision)) {
|
|
10130
10304
|
try {
|
|
10131
10305
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
10132
|
-
for (const record of records) append(
|
|
10306
|
+
for (const record of records) append(path20, `${JSON.stringify(record)}
|
|
10133
10307
|
`, "utf8");
|
|
10134
10308
|
} catch {
|
|
10135
10309
|
}
|
|
@@ -10147,7 +10321,7 @@ var init_auto_router = __esm({
|
|
|
10147
10321
|
init_effort_policy();
|
|
10148
10322
|
init_role_cost_shadow();
|
|
10149
10323
|
ROUTER_VERSION = "0.1.0";
|
|
10150
|
-
DECISION_FALLBACK_PATH = join8(
|
|
10324
|
+
DECISION_FALLBACK_PATH = join8(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
|
|
10151
10325
|
MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
10152
10326
|
cachedThresholds = null;
|
|
10153
10327
|
isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
|
|
@@ -10440,6 +10614,93 @@ var init_task_helpers = __esm({
|
|
|
10440
10614
|
}
|
|
10441
10615
|
});
|
|
10442
10616
|
|
|
10617
|
+
// ../../scripts/virtual-office/code-runner/swarm-admission.mjs
|
|
10618
|
+
function canLaunchSuccessorAgent(agent, platform = process.platform) {
|
|
10619
|
+
const name = typeof agent === "string" ? agent.trim() : "";
|
|
10620
|
+
if (!Object.prototype.hasOwnProperty.call(SUCCESSOR_LAUNCH_SHAPES, name)) return false;
|
|
10621
|
+
if (platform === "win32" && !SUCCESSOR_LAUNCH_SHAPES[name]) return false;
|
|
10622
|
+
return true;
|
|
10623
|
+
}
|
|
10624
|
+
function isSubscriptionExhausted(usage) {
|
|
10625
|
+
if (!usage || typeof usage !== "object") return false;
|
|
10626
|
+
const readings = [usage.seven_day_used_pct, usage.five_hour_used_pct, usage.monthly_used_pct];
|
|
10627
|
+
return readings.some((v) => typeof v === "number" && Number.isFinite(v) && v >= SUBSCRIPTION_EXHAUSTED_PCT);
|
|
10628
|
+
}
|
|
10629
|
+
function clampSubagents(requested) {
|
|
10630
|
+
if (!Number.isFinite(requested) || requested < 1) return 0;
|
|
10631
|
+
return Math.min(Math.floor(requested), MAX_BOUND_SUBAGENTS);
|
|
10632
|
+
}
|
|
10633
|
+
function usageFor(accountUsage, agent) {
|
|
10634
|
+
if (!Array.isArray(accountUsage)) return null;
|
|
10635
|
+
return accountUsage.find((row) => row && row.agent === agent) ?? null;
|
|
10636
|
+
}
|
|
10637
|
+
function exhaustedAgents(availableAgents, accountUsage) {
|
|
10638
|
+
if (!Array.isArray(availableAgents)) return [];
|
|
10639
|
+
return availableAgents.filter((row) => row && row.installed === true && row.authenticated === true && row.auth_tier === AUTH_TIER_SUBSCRIPTION && isSubscriptionExhausted(usageFor(accountUsage, row.agent))).map((row) => row.agent);
|
|
10640
|
+
}
|
|
10641
|
+
function resolveRunnerSwarmBinding({
|
|
10642
|
+
swarmId,
|
|
10643
|
+
agent,
|
|
10644
|
+
availableAgents,
|
|
10645
|
+
accountUsage = [],
|
|
10646
|
+
requestedSubagents = MAX_BOUND_SUBAGENTS,
|
|
10647
|
+
nowIso,
|
|
10648
|
+
platform = process.platform
|
|
10649
|
+
} = {}) {
|
|
10650
|
+
const id = typeof swarmId === "string" ? swarmId.trim() : "";
|
|
10651
|
+
const boundAgent = typeof agent === "string" ? agent.trim() : "";
|
|
10652
|
+
if (id.length === 0 || boundAgent.length === 0) return null;
|
|
10653
|
+
if (!canLaunchSuccessorAgent(boundAgent, platform)) return null;
|
|
10654
|
+
const budget = clampSubagents(requestedSubagents);
|
|
10655
|
+
if (budget === 0) return null;
|
|
10656
|
+
const rows = Array.isArray(availableAgents) ? availableAgents : [];
|
|
10657
|
+
const row = rows.find((r) => r && r.agent === boundAgent) ?? null;
|
|
10658
|
+
if (!row || row.installed !== true || row.authenticated !== true) return null;
|
|
10659
|
+
const tier = AUTH_TIER_TO_SWARM_TIER[row.auth_tier];
|
|
10660
|
+
if (!tier) return null;
|
|
10661
|
+
const exhausted = exhaustedAgents(rows, accountUsage);
|
|
10662
|
+
const boundExhausted = tier === "tier1_subscription" && isSubscriptionExhausted(usageFor(accountUsage, boundAgent));
|
|
10663
|
+
const effectiveBudget = boundExhausted ? Math.min(budget, EXHAUSTED_SUBAGENT_BUDGET) : budget;
|
|
10664
|
+
const basis = `code-task dispatch admitted on '${boundAgent}' at auth tier '${row.auth_tier}' (runner-local capability probe)`;
|
|
10665
|
+
return {
|
|
10666
|
+
schema_version: 1,
|
|
10667
|
+
swarm_id: id,
|
|
10668
|
+
tier,
|
|
10669
|
+
agent: boundAgent,
|
|
10670
|
+
reason: boundExhausted ? `${basis}; that subscription window is >=${SUBSCRIPTION_EXHAUSTED_PCT}% spent, so the fan-out is bound at a REDUCED ceiling of ${effectiveBudget} instead of being left unbound and uncapped` : basis,
|
|
10671
|
+
exhausted_agents: exhausted,
|
|
10672
|
+
subagent_budget: effectiveBudget,
|
|
10673
|
+
// Never a platform-billed fan-out in this lane — see the module header.
|
|
10674
|
+
spend_cap_usd: null,
|
|
10675
|
+
resolved_at: typeof nowIso === "string" && nowIso ? nowIso : (/* @__PURE__ */ new Date()).toISOString()
|
|
10676
|
+
};
|
|
10677
|
+
}
|
|
10678
|
+
function mintSwarmTierBindingEnv(input) {
|
|
10679
|
+
const binding = resolveRunnerSwarmBinding(input);
|
|
10680
|
+
if (!binding) return {};
|
|
10681
|
+
return { [SWARM_TIER_BINDING_ENV]: JSON.stringify(binding) };
|
|
10682
|
+
}
|
|
10683
|
+
var SWARM_TIER_BINDING_ENV, MAX_BOUND_SUBAGENTS, SUBSCRIPTION_EXHAUSTED_PCT, EXHAUSTED_SUBAGENT_BUDGET, SUCCESSOR_LAUNCH_SHAPES, AUTH_TIER_TO_SWARM_TIER;
|
|
10684
|
+
var init_swarm_admission = __esm({
|
|
10685
|
+
"../../scripts/virtual-office/code-runner/swarm-admission.mjs"() {
|
|
10686
|
+
"use strict";
|
|
10687
|
+
init_agent_auth_tier();
|
|
10688
|
+
SWARM_TIER_BINDING_ENV = "VO_SWARM_TIER_BINDING";
|
|
10689
|
+
MAX_BOUND_SUBAGENTS = 20;
|
|
10690
|
+
SUBSCRIPTION_EXHAUSTED_PCT = 95;
|
|
10691
|
+
EXHAUSTED_SUBAGENT_BUDGET = 1;
|
|
10692
|
+
SUCCESSOR_LAUNCH_SHAPES = Object.freeze({
|
|
10693
|
+
claude: true,
|
|
10694
|
+
codex: false
|
|
10695
|
+
});
|
|
10696
|
+
AUTH_TIER_TO_SWARM_TIER = Object.freeze({
|
|
10697
|
+
[AUTH_TIER_SUBSCRIPTION]: "tier1_subscription",
|
|
10698
|
+
[AUTH_TIER_LOCAL]: "tier1_local",
|
|
10699
|
+
[AUTH_TIER_API_KEY]: "tier2_user_key"
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
});
|
|
10703
|
+
|
|
10443
10704
|
// ../../scripts/virtual-office/code-runner/agent-process-env.mjs
|
|
10444
10705
|
function safeIdentityPart(value, fallback) {
|
|
10445
10706
|
const normalized = String(value || "").trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -10452,8 +10713,14 @@ function safeBaseEnv(env2 = {}) {
|
|
|
10452
10713
|
}
|
|
10453
10714
|
return result;
|
|
10454
10715
|
}
|
|
10455
|
-
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null } = {}) {
|
|
10716
|
+
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null, swarmAdmission = null } = {}) {
|
|
10456
10717
|
const base = safeBaseEnv(env2);
|
|
10718
|
+
if (swarmAdmission) {
|
|
10719
|
+
for (const key of Object.keys(base)) {
|
|
10720
|
+
if (key.toUpperCase() === SWARM_TIER_BINDING_ENV) delete base[key];
|
|
10721
|
+
}
|
|
10722
|
+
Object.assign(base, mintSwarmTierBindingEnv({ ...swarmAdmission, agent, swarmId: taskId }));
|
|
10723
|
+
}
|
|
10457
10724
|
if (typeof githubReadToken === "string" && githubReadToken) {
|
|
10458
10725
|
base.GH_TOKEN = githubReadToken;
|
|
10459
10726
|
base.GITHUB_TOKEN = githubReadToken;
|
|
@@ -10471,6 +10738,7 @@ var SAFE_ENV_NAMES;
|
|
|
10471
10738
|
var init_agent_process_env = __esm({
|
|
10472
10739
|
"../../scripts/virtual-office/code-runner/agent-process-env.mjs"() {
|
|
10473
10740
|
"use strict";
|
|
10741
|
+
init_swarm_admission();
|
|
10474
10742
|
SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
10475
10743
|
"AGENT_ID",
|
|
10476
10744
|
"APPDATA",
|
|
@@ -10511,7 +10779,31 @@ var init_agent_process_env = __esm({
|
|
|
10511
10779
|
// it. #9242 added VO_RUNNER_PREFER_KEY without this line, which left the
|
|
10512
10780
|
// escape hatch inert — an operator who set it still got the subscription.
|
|
10513
10781
|
"VO_RUNNER_PREFER_KEY",
|
|
10514
|
-
"VO_RUNNER_CLAUDE_PREFER_KEY"
|
|
10782
|
+
"VO_RUNNER_CLAUDE_PREFER_KEY",
|
|
10783
|
+
// The swarm tier binding (SWARM_TIER_BINDING_ENV in
|
|
10784
|
+
// packages/vo-mcp/src/swarm/tier-binding.ts). A fan-out resolves its billing
|
|
10785
|
+
// tier ONCE at admission and exports the binding so every subagent inherits
|
|
10786
|
+
// the same answer instead of re-resolving its own. This line is what lets it
|
|
10787
|
+
// cross the process boundary at all: the daemon builds each child env with
|
|
10788
|
+
// buildAgentProcessEnv(process.env) at
|
|
10789
|
+
// scripts/virtual-office/code-runner-daemon.mjs:117 (sibling dir, not this
|
|
10790
|
+
// one), so a name absent from this set is stripped and the binding binds
|
|
10791
|
+
// NOTHING —
|
|
10792
|
+
// exactly how #9242's VO_RUNNER_PREFER_KEY shipped inert until #9247.
|
|
10793
|
+
//
|
|
10794
|
+
// NOT a credential and NOT an authorization input: it names a tier, it never
|
|
10795
|
+
// grants one, and it carries no key material (see the module's header rule).
|
|
10796
|
+
"VO_SWARM_TIER_BINDING",
|
|
10797
|
+
// Where the swarm SPAWN LEDGER lives (SWARM_LEDGER_DIR_ENV in
|
|
10798
|
+
// packages/vo-mcp/src/swarm/spawn-ledger.ts). The ledger is the durable,
|
|
10799
|
+
// host-shared counter that bounds the TOTAL spawns under one swarm_id; the
|
|
10800
|
+
// binding above only bounds the depth of one chain. If a parent's override
|
|
10801
|
+
// were stripped here, the child would ledger into a DIFFERENT directory,
|
|
10802
|
+
// claim slot 0 again, and the shared ceiling would silently degrade back to a
|
|
10803
|
+
// per-process quota — which is precisely the defect the ledger closes.
|
|
10804
|
+
//
|
|
10805
|
+
// A path, not a credential. Absent means the default ~/.vo/swarm-ledger.
|
|
10806
|
+
"VO_SWARM_LEDGER_DIR"
|
|
10515
10807
|
]);
|
|
10516
10808
|
}
|
|
10517
10809
|
});
|
|
@@ -11002,9 +11294,9 @@ var init_inference_task_runner = __esm({
|
|
|
11002
11294
|
});
|
|
11003
11295
|
|
|
11004
11296
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
11005
|
-
import
|
|
11297
|
+
import fs10 from "node:fs";
|
|
11006
11298
|
import fsp11 from "node:fs/promises";
|
|
11007
|
-
import
|
|
11299
|
+
import path17 from "node:path";
|
|
11008
11300
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
11009
11301
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
11010
11302
|
}
|
|
@@ -11017,7 +11309,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
11017
11309
|
"--path-format=absolute",
|
|
11018
11310
|
"--git-common-dir"
|
|
11019
11311
|
])).trim();
|
|
11020
|
-
const root =
|
|
11312
|
+
const root = path17.dirname(commonDir);
|
|
11021
11313
|
return samePath2(root, worktreeDir) ? null : root;
|
|
11022
11314
|
}
|
|
11023
11315
|
async function snapshot(root, run) {
|
|
@@ -11059,21 +11351,21 @@ async function changedPaths(root, run) {
|
|
|
11059
11351
|
}
|
|
11060
11352
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
11061
11353
|
const paths = await changedPaths(baseline.root, run);
|
|
11062
|
-
const quarantineDir =
|
|
11063
|
-
|
|
11354
|
+
const quarantineDir = path17.join(
|
|
11355
|
+
path17.dirname(worktreeDir),
|
|
11064
11356
|
".canonical-recovery",
|
|
11065
11357
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
11066
11358
|
);
|
|
11067
11359
|
await fsp11.mkdir(quarantineDir, { recursive: true });
|
|
11068
11360
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
11069
|
-
await fsp11.writeFile(
|
|
11361
|
+
await fsp11.writeFile(path17.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
11070
11362
|
for (const relative of paths.untracked) {
|
|
11071
|
-
const source =
|
|
11072
|
-
const target =
|
|
11073
|
-
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 });
|
|
11074
11366
|
await fsp11.copyFile(source, target);
|
|
11075
11367
|
}
|
|
11076
|
-
await fsp11.writeFile(
|
|
11368
|
+
await fsp11.writeFile(path17.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
11077
11369
|
taskId,
|
|
11078
11370
|
canonicalRoot: baseline.root,
|
|
11079
11371
|
canonicalHead: baseline.head,
|
|
@@ -11095,9 +11387,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
11095
11387
|
]);
|
|
11096
11388
|
}
|
|
11097
11389
|
for (const relative of evidence.untracked) {
|
|
11098
|
-
const target =
|
|
11099
|
-
const prefix = `${
|
|
11100
|
-
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;
|
|
11101
11393
|
await fsp11.rm(target, { force: true });
|
|
11102
11394
|
}
|
|
11103
11395
|
}
|
|
@@ -11133,7 +11425,7 @@ var init_isolation_audit = __esm({
|
|
|
11133
11425
|
init_process_runner2();
|
|
11134
11426
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
11135
11427
|
samePath2 = (left, right) => {
|
|
11136
|
-
const [a, b] = [left, right].map((value) =>
|
|
11428
|
+
const [a, b] = [left, right].map((value) => path17.resolve(value));
|
|
11137
11429
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
11138
11430
|
};
|
|
11139
11431
|
}
|
|
@@ -11493,7 +11785,7 @@ var init_publication_outcome = __esm({
|
|
|
11493
11785
|
|
|
11494
11786
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
11495
11787
|
import fsp12 from "node:fs/promises";
|
|
11496
|
-
import
|
|
11788
|
+
import path18 from "node:path";
|
|
11497
11789
|
function defaultRun2(command, args, cwd, options = {}) {
|
|
11498
11790
|
return runProcess2(command, args, { cwd, ...options });
|
|
11499
11791
|
}
|
|
@@ -11501,13 +11793,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
11501
11793
|
if (!isAgentScratch(file)) {
|
|
11502
11794
|
throw new Error(`refusing to remove non-scratch publication path: ${file}`);
|
|
11503
11795
|
}
|
|
11504
|
-
const root =
|
|
11505
|
-
const target =
|
|
11506
|
-
const relative =
|
|
11507
|
-
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)) {
|
|
11508
11800
|
throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
|
|
11509
11801
|
}
|
|
11510
|
-
for (let cursor = target; cursor !== root; cursor =
|
|
11802
|
+
for (let cursor = target; cursor !== root; cursor = path18.dirname(cursor)) {
|
|
11511
11803
|
try {
|
|
11512
11804
|
if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
|
|
11513
11805
|
throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
|
|
@@ -11627,9 +11919,9 @@ var init_publication_scope = __esm({
|
|
|
11627
11919
|
});
|
|
11628
11920
|
|
|
11629
11921
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
11630
|
-
import
|
|
11922
|
+
import fs11 from "node:fs";
|
|
11631
11923
|
import fsp13 from "node:fs/promises";
|
|
11632
|
-
import
|
|
11924
|
+
import path19 from "node:path";
|
|
11633
11925
|
function recoveryTaskId(prompt) {
|
|
11634
11926
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
11635
11927
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -11643,10 +11935,10 @@ function cloneLeaf(repo) {
|
|
|
11643
11935
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
11644
11936
|
const leaf = cloneLeaf(repo);
|
|
11645
11937
|
if (!leaf || !clonesRoot2) return [];
|
|
11646
|
-
const canonical =
|
|
11938
|
+
const canonical = path19.join(clonesRoot2, leaf);
|
|
11647
11939
|
return [
|
|
11648
|
-
|
|
11649
|
-
|
|
11940
|
+
path19.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
11941
|
+
path19.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
11650
11942
|
];
|
|
11651
11943
|
}
|
|
11652
11944
|
async function readLedger(file, readFile5) {
|
|
@@ -11665,7 +11957,7 @@ async function readLedger(file, readFile5) {
|
|
|
11665
11957
|
async function findPreservedRecovery(task, {
|
|
11666
11958
|
clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
|
|
11667
11959
|
readFile: readFile5 = fsp13.readFile,
|
|
11668
|
-
exists =
|
|
11960
|
+
exists = fs11.existsSync
|
|
11669
11961
|
} = {}) {
|
|
11670
11962
|
const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
|
|
11671
11963
|
const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
|
|
@@ -11979,7 +12271,7 @@ var init_cancellation_probe = __esm({
|
|
|
11979
12271
|
});
|
|
11980
12272
|
|
|
11981
12273
|
// ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
|
|
11982
|
-
import { homedir as
|
|
12274
|
+
import { homedir as homedir8 } from "node:os";
|
|
11983
12275
|
import { dirname as dirname6, join as join9 } from "node:path";
|
|
11984
12276
|
import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
|
|
11985
12277
|
function withLock(operation) {
|
|
@@ -12044,13 +12336,13 @@ var DEFAULT_FILE, serialized;
|
|
|
12044
12336
|
var init_detached_economics_spool = __esm({
|
|
12045
12337
|
"../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
|
|
12046
12338
|
"use strict";
|
|
12047
|
-
DEFAULT_FILE = join9(
|
|
12339
|
+
DEFAULT_FILE = join9(homedir8(), ".vo", "detached-run-economics.json");
|
|
12048
12340
|
serialized = Promise.resolve();
|
|
12049
12341
|
}
|
|
12050
12342
|
});
|
|
12051
12343
|
|
|
12052
12344
|
// ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
|
|
12053
|
-
import { randomUUID as
|
|
12345
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
12054
12346
|
async function handleKilledRun({
|
|
12055
12347
|
client,
|
|
12056
12348
|
id,
|
|
@@ -12087,7 +12379,7 @@ async function handleKilledRun({
|
|
|
12087
12379
|
};
|
|
12088
12380
|
}
|
|
12089
12381
|
if (reason === "claim_authority_changed") {
|
|
12090
|
-
const occurrenceId =
|
|
12382
|
+
const occurrenceId = randomUUID5();
|
|
12091
12383
|
const economics = {
|
|
12092
12384
|
occurrence_id: occurrenceId,
|
|
12093
12385
|
runner_id: runnerId,
|
|
@@ -12384,12 +12676,12 @@ var code_runner_daemon_exports = {};
|
|
|
12384
12676
|
__export(code_runner_daemon_exports, {
|
|
12385
12677
|
main: () => main
|
|
12386
12678
|
});
|
|
12387
|
-
import { randomUUID as
|
|
12679
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
12388
12680
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
12389
12681
|
function log(msg) {
|
|
12390
12682
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
12391
12683
|
}
|
|
12392
|
-
async function processOneTask(client, task, cfg, runnerInstanceId) {
|
|
12684
|
+
async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmission = null) {
|
|
12393
12685
|
const id = task.code_task_id;
|
|
12394
12686
|
let worktreeName = "";
|
|
12395
12687
|
let preserveReason = null;
|
|
@@ -12439,7 +12731,8 @@ async function processOneTask(client, task, cfg, runnerInstanceId) {
|
|
|
12439
12731
|
model,
|
|
12440
12732
|
effort: effectiveEffort,
|
|
12441
12733
|
maxBudgetUsd: sel.agent === "claude" ? effectiveMaxBudgetUsd : void 0,
|
|
12442
|
-
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken }),
|
|
12734
|
+
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken, swarmAdmission }),
|
|
12735
|
+
// swarmAdmission mints VO_SWARM_TIER_BINDING: ONE tier decision for this task's whole agent tree
|
|
12443
12736
|
sandbox,
|
|
12444
12737
|
onProgress: (text, checkpoint) => {
|
|
12445
12738
|
const usage = checkpoint?.tokenUsage ? { token_usage: checkpoint.tokenUsage } : {};
|
|
@@ -12613,7 +12906,7 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
|
12613
12906
|
async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
12614
12907
|
const cfg = loadCodeRunnerConfig(env2, { log });
|
|
12615
12908
|
await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
|
|
12616
|
-
const runnerInstanceId =
|
|
12909
|
+
const runnerInstanceId = randomUUID6();
|
|
12617
12910
|
const client = createControlPlaneClient({
|
|
12618
12911
|
env: env2,
|
|
12619
12912
|
runnerId: cfg.runnerId,
|
|
@@ -12725,7 +13018,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
12725
13018
|
}
|
|
12726
13019
|
log(`claimed task ${task.code_task_id} (${task.repo})`);
|
|
12727
13020
|
active += 1;
|
|
12728
|
-
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log }) : processOneTask(client, task, cfg, runnerInstanceId);
|
|
13021
|
+
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log }) : processOneTask(client, task, cfg, runnerInstanceId, { availableAgents: claimAgents.availableAgents, accountUsage: accountUsage.get() });
|
|
12729
13022
|
const done = runTask.catch(async (error) => {
|
|
12730
13023
|
log(`task ${task.code_task_id} unhandled runner error: ${error.message}`);
|
|
12731
13024
|
if (task.kind === "inference") await deliverTerminalRun({
|
|
@@ -12942,6 +13235,98 @@ function pairedOperatorScope(readiness) {
|
|
|
12942
13235
|
return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
|
|
12943
13236
|
}
|
|
12944
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
|
+
|
|
12945
13330
|
// src/runner-cli.mjs
|
|
12946
13331
|
var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
12947
13332
|
function packageVersion() {
|
|
@@ -12956,11 +13341,45 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
|
12956
13341
|
`);
|
|
12957
13342
|
process.exit(0);
|
|
12958
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
|
+
}
|
|
12959
13367
|
var { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
12960
13368
|
var storedCredential = readStoredCredential2();
|
|
12961
13369
|
var explicitAdminToken = process.env.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
|
|
12962
13370
|
var token = explicitAdminToken || storedCredential?.vo_credential;
|
|
12963
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
|
+
}
|
|
12964
13383
|
if (statusOnly) {
|
|
12965
13384
|
if (!storedCredential?.vo_credential) {
|
|
12966
13385
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -12969,6 +13388,7 @@ if (statusOnly) {
|
|
|
12969
13388
|
operatorId: null,
|
|
12970
13389
|
tenantId: null,
|
|
12971
13390
|
githubReady: null,
|
|
13391
|
+
filesystemReady: null,
|
|
12972
13392
|
error: "credential_missing",
|
|
12973
13393
|
message: "This computer is not paired. Pair it to your AlgoHQ account first."
|
|
12974
13394
|
})}
|
|
@@ -12979,9 +13399,28 @@ if (statusOnly) {
|
|
|
12979
13399
|
controlPlaneUrl: process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL,
|
|
12980
13400
|
token: storedCredential.vo_credential
|
|
12981
13401
|
});
|
|
12982
|
-
|
|
13402
|
+
if (!readiness.ok) {
|
|
13403
|
+
process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: null })}
|
|
12983
13404
|
`);
|
|
12984
|
-
|
|
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
|
+
}
|
|
12985
13424
|
}
|
|
12986
13425
|
if (!token) {
|
|
12987
13426
|
console.error("[vo-mcp runner] No credential found. Run `vo-mcp login` first.");
|
|
@@ -13006,6 +13445,13 @@ if (!explicitAdminToken) {
|
|
|
13006
13445
|
process.exit(1);
|
|
13007
13446
|
}
|
|
13008
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
|
+
}
|
|
13009
13455
|
var env = {
|
|
13010
13456
|
...process.env,
|
|
13011
13457
|
VO_CONTROL_PLANE_ADMIN_TOKEN: token,
|
|
@@ -13014,7 +13460,8 @@ var env = {
|
|
|
13014
13460
|
// VO_CODE_RUNNER_VERSION, which a desktop host may set to its own shell
|
|
13015
13461
|
// release even after runner-control updates this package in place.
|
|
13016
13462
|
VO_CODE_RUNNER_DAEMON_VERSION: `vo-mcp/${packageVersion()}`,
|
|
13017
|
-
VO_CODE_RUNNER_REPO:
|
|
13463
|
+
...rootConfig.repoRoot ? { VO_CODE_RUNNER_REPO: rootConfig.repoRoot } : {},
|
|
13464
|
+
...rootConfig.clonesRoot ? { VO_CODE_RUNNER_CLONES_ROOT: rootConfig.clonesRoot } : {},
|
|
13018
13465
|
// Server-resolved identity replaces stale/hardcoded desktop scope. Besides
|
|
13019
13466
|
// filtering heartbeat/claims, this keeps GitHub App auth required for the
|
|
13020
13467
|
// entire task lifecycle (never ambient-gh fallback after a paired preflight).
|