@algosuite/vo-mcp 0.2.0-beta.29 → 0.2.0-beta.33
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 +187 -6
- package/dist/autostart-cli.js +62 -46
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +952 -229
- package/dist/cli.js.map +4 -4
- package/dist/index.js +891 -198
- 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 +1403 -381
- package/dist/runner-cli.js.map +4 -4
- package/package.json +2 -2
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);
|
|
@@ -2476,6 +2582,11 @@ var init_control_plane_auth_stub = __esm({
|
|
|
2476
2582
|
});
|
|
2477
2583
|
|
|
2478
2584
|
// ../../scripts/virtual-office/code-runner/control-plane-client.mjs
|
|
2585
|
+
var control_plane_client_exports = {};
|
|
2586
|
+
__export(control_plane_client_exports, {
|
|
2587
|
+
ClaimAuthorityChangedError: () => ClaimAuthorityChangedError,
|
|
2588
|
+
createControlPlaneClient: () => createControlPlaneClient
|
|
2589
|
+
});
|
|
2479
2590
|
async function resolveBearer(env2) {
|
|
2480
2591
|
const adminToken = env2.VO_CONTROL_PLANE_ADMIN_TOKEN;
|
|
2481
2592
|
if (adminToken) return adminToken;
|
|
@@ -2508,11 +2619,11 @@ function createControlPlaneClient({
|
|
|
2508
2619
|
const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
|
|
2509
2620
|
if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
2510
2621
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
2511
|
-
async function req(method,
|
|
2622
|
+
async function req(method, path23, body, { timeoutMs } = {}) {
|
|
2512
2623
|
const bearer = await resolveBearer(env2);
|
|
2513
2624
|
const controller = timeoutMs ? new AbortController() : null;
|
|
2514
2625
|
let timeoutId;
|
|
2515
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
2626
|
+
const request = Promise.resolve(fetchImpl(`${root}${path23}`, {
|
|
2516
2627
|
method,
|
|
2517
2628
|
headers: {
|
|
2518
2629
|
"content-type": "application/json",
|
|
@@ -2525,7 +2636,7 @@ function createControlPlaneClient({
|
|
|
2525
2636
|
const timeout = new Promise((_, reject) => {
|
|
2526
2637
|
timeoutId = setTimeout(() => {
|
|
2527
2638
|
controller.abort();
|
|
2528
|
-
reject(new Error(`control-plane ${
|
|
2639
|
+
reject(new Error(`control-plane ${path23} timed out after ${timeoutMs}ms`));
|
|
2529
2640
|
}, timeoutMs);
|
|
2530
2641
|
});
|
|
2531
2642
|
try {
|
|
@@ -2534,7 +2645,7 @@ function createControlPlaneClient({
|
|
|
2534
2645
|
clearTimeout(timeoutId);
|
|
2535
2646
|
}
|
|
2536
2647
|
}
|
|
2537
|
-
const taskReq = (method,
|
|
2648
|
+
const taskReq = (method, path23, body, options = {}) => req(method, path23, body, { timeoutMs: taskRequestTimeoutMs, ...options });
|
|
2538
2649
|
return {
|
|
2539
2650
|
...makeAutonomousDispatchAdmissionClient(
|
|
2540
2651
|
req,
|
|
@@ -2657,8 +2768,8 @@ function createControlPlaneClient({
|
|
|
2657
2768
|
return listAllPrOpenedTasks(taskReq);
|
|
2658
2769
|
},
|
|
2659
2770
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
2660
|
-
const
|
|
2661
|
-
const res = await taskReq("GET",
|
|
2771
|
+
const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
2772
|
+
const res = await taskReq("GET", path23);
|
|
2662
2773
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
2663
2774
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
2664
2775
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -2852,8 +2963,8 @@ var init_control_plane_client = __esm({
|
|
|
2852
2963
|
});
|
|
2853
2964
|
|
|
2854
2965
|
// ../../scripts/virtual-office/code-runner/windows-claude-launch.mjs
|
|
2855
|
-
import { existsSync as
|
|
2856
|
-
import { win32 as
|
|
2966
|
+
import { existsSync as existsSync3, realpathSync } from "node:fs";
|
|
2967
|
+
import { win32 as path11 } from "node:path";
|
|
2857
2968
|
import { spawnSync } from "node:child_process";
|
|
2858
2969
|
function pathValue(env2) {
|
|
2859
2970
|
for (const key of ["Path", "PATH", "path"]) {
|
|
@@ -2874,38 +2985,38 @@ function envValue(env2, name) {
|
|
|
2874
2985
|
function userClaudeCandidates(bin, env2) {
|
|
2875
2986
|
if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
|
|
2876
2987
|
const userProfile = envValue(env2, "USERPROFILE");
|
|
2877
|
-
const appData = envValue(env2, "APPDATA") || (userProfile ?
|
|
2878
|
-
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ?
|
|
2988
|
+
const appData = envValue(env2, "APPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Roaming") : "");
|
|
2989
|
+
const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Local") : "");
|
|
2879
2990
|
const candidates = [];
|
|
2880
2991
|
if (appData) {
|
|
2881
|
-
const npmBin =
|
|
2992
|
+
const npmBin = path11.join(appData, "npm");
|
|
2882
2993
|
candidates.push(
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2994
|
+
path11.join(npmBin, "claude.exe"),
|
|
2995
|
+
path11.join(npmBin, "claude.cmd"),
|
|
2996
|
+
path11.join(npmBin, "claude.ps1"),
|
|
2997
|
+
path11.join(npmBin, "claude"),
|
|
2998
|
+
path11.join(npmBin, ...NATIVE_CLAUDE_PARTS)
|
|
2888
2999
|
);
|
|
2889
3000
|
}
|
|
2890
|
-
if (userProfile) candidates.push(
|
|
3001
|
+
if (userProfile) candidates.push(path11.join(userProfile, ".local", "bin", "claude.exe"));
|
|
2891
3002
|
if (localAppData) {
|
|
2892
3003
|
candidates.push(
|
|
2893
|
-
|
|
2894
|
-
|
|
3004
|
+
path11.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
|
|
3005
|
+
path11.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
|
|
2895
3006
|
);
|
|
2896
3007
|
}
|
|
2897
3008
|
return candidates;
|
|
2898
3009
|
}
|
|
2899
3010
|
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
|
-
|
|
3011
|
+
if (path11.isAbsolute(bin) || /[\\/]/u.test(bin)) {
|
|
3012
|
+
return [path11.resolve(bin)];
|
|
3013
|
+
}
|
|
3014
|
+
const extension = path11.extname(bin);
|
|
3015
|
+
const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path11.join(directory, bin)] : [
|
|
3016
|
+
path11.join(directory, `${bin}.exe`),
|
|
3017
|
+
path11.join(directory, `${bin}.cmd`),
|
|
3018
|
+
path11.join(directory, `${bin}.ps1`),
|
|
3019
|
+
path11.join(directory, bin)
|
|
2909
3020
|
]);
|
|
2910
3021
|
const seen = /* @__PURE__ */ new Set();
|
|
2911
3022
|
return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
|
|
@@ -2926,7 +3037,7 @@ function canonicalExistingPath(candidate, exists, canonicalize) {
|
|
|
2926
3037
|
function resolveWindowsClaudeExecutable({
|
|
2927
3038
|
bin = "claude",
|
|
2928
3039
|
env: env2 = process.env,
|
|
2929
|
-
exists =
|
|
3040
|
+
exists = existsSync3,
|
|
2930
3041
|
canonicalize = realpathSync
|
|
2931
3042
|
} = {}) {
|
|
2932
3043
|
const requested = String(bin || "").trim();
|
|
@@ -2936,8 +3047,8 @@ function resolveWindowsClaudeExecutable({
|
|
|
2936
3047
|
for (const candidate of pathCandidates(requested, env2)) {
|
|
2937
3048
|
const found = canonicalExistingPath(candidate, exists, canonicalize);
|
|
2938
3049
|
if (!found) continue;
|
|
2939
|
-
if (
|
|
2940
|
-
const native =
|
|
3050
|
+
if (path11.extname(found).toLowerCase() === ".exe") return found;
|
|
3051
|
+
const native = path11.join(path11.dirname(found), ...NATIVE_CLAUDE_PARTS);
|
|
2941
3052
|
const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
|
|
2942
3053
|
if (resolvedNative) return resolvedNative;
|
|
2943
3054
|
}
|
|
@@ -3404,14 +3515,14 @@ var init_terminal_process_cleanup = __esm({
|
|
|
3404
3515
|
|
|
3405
3516
|
// ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
|
|
3406
3517
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3407
|
-
import { existsSync as
|
|
3518
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3408
3519
|
import os from "node:os";
|
|
3409
|
-
import
|
|
3520
|
+
import path12 from "node:path";
|
|
3410
3521
|
function registryRoot(tmp = os.tmpdir()) {
|
|
3411
|
-
return
|
|
3522
|
+
return path12.join(tmp, REGISTRY_ROOT_NAME);
|
|
3412
3523
|
}
|
|
3413
3524
|
function instanceDir(root, instanceId) {
|
|
3414
|
-
return
|
|
3525
|
+
return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
|
|
3415
3526
|
}
|
|
3416
3527
|
function registerDaemonInstance({
|
|
3417
3528
|
root = registryRoot(),
|
|
@@ -3421,8 +3532,8 @@ function registerDaemonInstance({
|
|
|
3421
3532
|
} = {}) {
|
|
3422
3533
|
if (!instanceId) return null;
|
|
3423
3534
|
const dir = instanceDir(root, instanceId);
|
|
3424
|
-
|
|
3425
|
-
const file =
|
|
3535
|
+
mkdirSync3(dir, { recursive: true });
|
|
3536
|
+
const file = path12.join(dir, DAEMON_RECORD);
|
|
3426
3537
|
writeFileSync2(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
|
|
3427
3538
|
encoding: "utf8",
|
|
3428
3539
|
mode: 384
|
|
@@ -3439,9 +3550,9 @@ function recordAgentPid({
|
|
|
3439
3550
|
if (!instanceId || !Number.isInteger(pid) || pid <= 0) return false;
|
|
3440
3551
|
try {
|
|
3441
3552
|
const dir = instanceDir(root, instanceId);
|
|
3442
|
-
|
|
3553
|
+
mkdirSync3(dir, { recursive: true });
|
|
3443
3554
|
writeFileSync2(
|
|
3444
|
-
|
|
3555
|
+
path12.join(dir, `${pid}.json`),
|
|
3445
3556
|
JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
|
|
3446
3557
|
{ encoding: "utf8", mode: 384 }
|
|
3447
3558
|
);
|
|
@@ -3457,7 +3568,7 @@ function unrecordAgentPid({
|
|
|
3457
3568
|
} = {}) {
|
|
3458
3569
|
if (!instanceId || !Number.isInteger(pid)) return false;
|
|
3459
3570
|
try {
|
|
3460
|
-
rmSync2(
|
|
3571
|
+
rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
|
|
3461
3572
|
return true;
|
|
3462
3573
|
} catch {
|
|
3463
3574
|
return false;
|
|
@@ -3475,7 +3586,7 @@ function bootstrapOrphanReaper({ instanceId, log: log2 = () => {
|
|
|
3475
3586
|
}
|
|
3476
3587
|
function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
3477
3588
|
const instances = [];
|
|
3478
|
-
if (!
|
|
3589
|
+
if (!existsSync4(root)) return instances;
|
|
3479
3590
|
let dirents;
|
|
3480
3591
|
try {
|
|
3481
3592
|
dirents = readdirSync(root, { withFileTypes: true });
|
|
@@ -3485,7 +3596,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3485
3596
|
for (const dirent of dirents) {
|
|
3486
3597
|
if (!dirent.isDirectory()) continue;
|
|
3487
3598
|
if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
|
|
3488
|
-
const dir =
|
|
3599
|
+
const dir = path12.join(root, dirent.name);
|
|
3489
3600
|
let daemon = null;
|
|
3490
3601
|
const agents = [];
|
|
3491
3602
|
let files;
|
|
@@ -3497,7 +3608,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
|
|
|
3497
3608
|
for (const name of files) {
|
|
3498
3609
|
let parsed;
|
|
3499
3610
|
try {
|
|
3500
|
-
parsed = JSON.parse(readFileSync2(
|
|
3611
|
+
parsed = JSON.parse(readFileSync2(path12.join(dir, name), "utf8"));
|
|
3501
3612
|
} catch {
|
|
3502
3613
|
continue;
|
|
3503
3614
|
}
|
|
@@ -3545,7 +3656,7 @@ function windowsSystemRoot(env2 = process.env) {
|
|
|
3545
3656
|
return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
3546
3657
|
}
|
|
3547
3658
|
function windowsPowershellExe(env2 = process.env) {
|
|
3548
|
-
return
|
|
3659
|
+
return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
3549
3660
|
}
|
|
3550
3661
|
function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
|
|
3551
3662
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3590,7 +3701,7 @@ function parsePosixPsLine(line) {
|
|
|
3590
3701
|
function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
|
|
3591
3702
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3592
3703
|
if (platform === "win32") {
|
|
3593
|
-
const taskkill =
|
|
3704
|
+
const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
|
|
3594
3705
|
const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
3595
3706
|
return !r.error && r.status === 0;
|
|
3596
3707
|
}
|
|
@@ -4379,15 +4490,15 @@ var init_flat_token_usage = __esm({
|
|
|
4379
4490
|
|
|
4380
4491
|
// ../../scripts/virtual-office/code-runner/codex-runner.mjs
|
|
4381
4492
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
4382
|
-
import { existsSync as
|
|
4383
|
-
import { win32 } from "node:path";
|
|
4493
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4494
|
+
import { win32 as win322 } from "node:path";
|
|
4384
4495
|
function isTruthyFlag2(value) {
|
|
4385
4496
|
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
|
4386
4497
|
}
|
|
4387
4498
|
function resolveCodexBinary({
|
|
4388
4499
|
env: env2 = process.env,
|
|
4389
4500
|
platform = process.platform,
|
|
4390
|
-
exists =
|
|
4501
|
+
exists = existsSync5
|
|
4391
4502
|
} = {}) {
|
|
4392
4503
|
if (platform !== "win32") return "codex";
|
|
4393
4504
|
const appData = String(env2.APPDATA || "").trim();
|
|
@@ -4395,7 +4506,7 @@ function resolveCodexBinary({
|
|
|
4395
4506
|
const localAppData = String(env2.LOCALAPPDATA || "").trim();
|
|
4396
4507
|
const candidates = [];
|
|
4397
4508
|
if (appData) {
|
|
4398
|
-
candidates.push(
|
|
4509
|
+
candidates.push(win322.join(
|
|
4399
4510
|
appData,
|
|
4400
4511
|
"npm",
|
|
4401
4512
|
"node_modules",
|
|
@@ -4411,11 +4522,11 @@ function resolveCodexBinary({
|
|
|
4411
4522
|
));
|
|
4412
4523
|
}
|
|
4413
4524
|
if (userProfile) {
|
|
4414
|
-
candidates.push(
|
|
4415
|
-
candidates.push(
|
|
4525
|
+
candidates.push(win322.join(userProfile, ".local", "bin", "codex.exe"));
|
|
4526
|
+
candidates.push(win322.join(userProfile, ".codex", "bin", "codex.exe"));
|
|
4416
4527
|
}
|
|
4417
4528
|
if (localAppData) {
|
|
4418
|
-
candidates.push(
|
|
4529
|
+
candidates.push(win322.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
|
|
4419
4530
|
}
|
|
4420
4531
|
const absolute = candidates.find((candidate) => exists(candidate));
|
|
4421
4532
|
if (absolute) return absolute;
|
|
@@ -4764,6 +4875,188 @@ var init_cursor_runner = __esm({
|
|
|
4764
4875
|
}
|
|
4765
4876
|
});
|
|
4766
4877
|
|
|
4878
|
+
// ../../scripts/virtual-office/code-runner/ollama-agent-tools.mjs
|
|
4879
|
+
var MAX_READ_BYTES, MAX_WRITE_BYTES, TOOL_DEFS, TOOL_NAMES, READ_ONLY_TOOL_NAMES, READ_ONLY_TOOL_DEFS;
|
|
4880
|
+
var init_ollama_agent_tools = __esm({
|
|
4881
|
+
"../../scripts/virtual-office/code-runner/ollama-agent-tools.mjs"() {
|
|
4882
|
+
"use strict";
|
|
4883
|
+
MAX_READ_BYTES = 64 * 1024;
|
|
4884
|
+
MAX_WRITE_BYTES = 512 * 1024;
|
|
4885
|
+
TOOL_DEFS = [
|
|
4886
|
+
{
|
|
4887
|
+
type: "function",
|
|
4888
|
+
function: {
|
|
4889
|
+
name: "read_file",
|
|
4890
|
+
description: "Read a UTF-8 text file inside the working directory. Returns up to 64 KiB.",
|
|
4891
|
+
parameters: {
|
|
4892
|
+
type: "object",
|
|
4893
|
+
properties: {
|
|
4894
|
+
path: { type: "string", description: "File path relative to the working directory." }
|
|
4895
|
+
},
|
|
4896
|
+
required: ["path"]
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4899
|
+
},
|
|
4900
|
+
{
|
|
4901
|
+
type: "function",
|
|
4902
|
+
function: {
|
|
4903
|
+
name: "list_files",
|
|
4904
|
+
description: "List entries in a directory inside the working directory (files and subdirs).",
|
|
4905
|
+
parameters: {
|
|
4906
|
+
type: "object",
|
|
4907
|
+
properties: {
|
|
4908
|
+
path: { type: "string", description: 'Directory path relative to the working directory. Default ".".' }
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
},
|
|
4913
|
+
{
|
|
4914
|
+
type: "function",
|
|
4915
|
+
function: {
|
|
4916
|
+
name: "write_file",
|
|
4917
|
+
description: "Create or overwrite a UTF-8 text file inside the working directory. Parent dirs are created.",
|
|
4918
|
+
parameters: {
|
|
4919
|
+
type: "object",
|
|
4920
|
+
properties: {
|
|
4921
|
+
path: { type: "string", description: "File path relative to the working directory." },
|
|
4922
|
+
content: { type: "string", description: "Full new file contents." }
|
|
4923
|
+
},
|
|
4924
|
+
required: ["path", "content"]
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
];
|
|
4929
|
+
TOOL_NAMES = TOOL_DEFS.map((t) => t.function.name);
|
|
4930
|
+
READ_ONLY_TOOL_NAMES = Object.freeze(["read_file", "list_files"]);
|
|
4931
|
+
READ_ONLY_TOOL_DEFS = Object.freeze(
|
|
4932
|
+
TOOL_DEFS.filter((tool) => READ_ONLY_TOOL_NAMES.includes(tool.function.name))
|
|
4933
|
+
);
|
|
4934
|
+
}
|
|
4935
|
+
});
|
|
4936
|
+
|
|
4937
|
+
// ../../scripts/virtual-office/code-runner/ollama-agent-core.mjs
|
|
4938
|
+
function parseOllamaAgentEvent(line) {
|
|
4939
|
+
const trimmed = String(line || "").trim();
|
|
4940
|
+
if (!trimmed) return null;
|
|
4941
|
+
let evt;
|
|
4942
|
+
try {
|
|
4943
|
+
evt = JSON.parse(trimmed);
|
|
4944
|
+
} catch {
|
|
4945
|
+
return null;
|
|
4946
|
+
}
|
|
4947
|
+
if (!evt || typeof evt !== "object") return null;
|
|
4948
|
+
if (evt.type === "progress") {
|
|
4949
|
+
const text = String(evt.text || "").trim();
|
|
4950
|
+
return text ? { kind: "progress", text } : null;
|
|
4951
|
+
}
|
|
4952
|
+
if (evt.type === "tool") {
|
|
4953
|
+
const via = evt.recovered ? " (recovered from text)" : "";
|
|
4954
|
+
const label = `${evt.ok === false ? "tool failed" : "tool"}: ${evt.name}${evt.path ? ` ${evt.path}` : ""}${via}`;
|
|
4955
|
+
return { kind: "progress", text: label };
|
|
4956
|
+
}
|
|
4957
|
+
if (evt.type === "result") {
|
|
4958
|
+
const usage = evt.usage || null;
|
|
4959
|
+
const tokenUsage = usage ? { inputTokens: usage.inputTokens ?? null, outputTokens: usage.outputTokens ?? null, totalTokens: usage.totalTokens ?? null } : void 0;
|
|
4960
|
+
return {
|
|
4961
|
+
kind: "result",
|
|
4962
|
+
isError: Boolean(evt.isError),
|
|
4963
|
+
costUsd: 0,
|
|
4964
|
+
// sovereign local inference is free — no meter.
|
|
4965
|
+
summary: String(evt.summary || (evt.isError ? "local run failed" : "completed")),
|
|
4966
|
+
numTurns: Number.isInteger(evt.numTurns) ? evt.numTurns : null,
|
|
4967
|
+
...tokenUsage ? { tokenUsage } : {},
|
|
4968
|
+
// Pass the sovereign receipt through to the daemon. Omitted entirely when
|
|
4969
|
+
// absent so the event shape is unchanged for every other transport.
|
|
4970
|
+
...evt.receipt ? { receipt: evt.receipt } : {}
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
return null;
|
|
4974
|
+
}
|
|
4975
|
+
var MAX_TURNS_DEFAULT, NUM_CTX_DEFAULT;
|
|
4976
|
+
var init_ollama_agent_core = __esm({
|
|
4977
|
+
"../../scripts/virtual-office/code-runner/ollama-agent-core.mjs"() {
|
|
4978
|
+
"use strict";
|
|
4979
|
+
init_ollama_agent_tools();
|
|
4980
|
+
init_ollama_agent_tools();
|
|
4981
|
+
MAX_TURNS_DEFAULT = 20;
|
|
4982
|
+
NUM_CTX_DEFAULT = 16384;
|
|
4983
|
+
}
|
|
4984
|
+
});
|
|
4985
|
+
|
|
4986
|
+
// ../../scripts/virtual-office/code-runner/ollama-native-transport.mjs
|
|
4987
|
+
import { fileURLToPath } from "node:url";
|
|
4988
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
4989
|
+
function resolveLocalNativeProfile(env2 = process.env) {
|
|
4990
|
+
const profile = String(env2.VO_CODE_RUNNER_LOCAL_PROFILE || "").trim().toLowerCase() || DEFAULT_LOCAL_NATIVE_PROFILE;
|
|
4991
|
+
if (!LOCAL_NATIVE_PROFILES.includes(profile)) {
|
|
4992
|
+
throw new Error(`local-model runner (native): unknown profile "${profile}" (coding|verification).`);
|
|
4993
|
+
}
|
|
4994
|
+
return profile;
|
|
4995
|
+
}
|
|
4996
|
+
function resolveLocalTransport(env2 = process.env) {
|
|
4997
|
+
return String(env2.VO_CODE_RUNNER_LOCAL_TRANSPORT || "").trim().toLowerCase() === "native" ? "native" : DEFAULT_LOCAL_TRANSPORT;
|
|
4998
|
+
}
|
|
4999
|
+
function ollamaAgentScriptPath() {
|
|
5000
|
+
return join2(dirname2(fileURLToPath(import.meta.url)), "ollama-agent.mjs");
|
|
5001
|
+
}
|
|
5002
|
+
function posIntOr(raw, fallback) {
|
|
5003
|
+
const n = Number(String(raw ?? "").trim());
|
|
5004
|
+
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
5005
|
+
}
|
|
5006
|
+
function buildOllamaAgentArgs({ model, numCtx, maxTurns, profile = DEFAULT_LOCAL_NATIVE_PROFILE } = {}) {
|
|
5007
|
+
return [
|
|
5008
|
+
ollamaAgentScriptPath(),
|
|
5009
|
+
"--model",
|
|
5010
|
+
String(model),
|
|
5011
|
+
"--profile",
|
|
5012
|
+
String(profile),
|
|
5013
|
+
"--num-ctx",
|
|
5014
|
+
String(posIntOr(numCtx, NUM_CTX_DEFAULT)),
|
|
5015
|
+
"--max-turns",
|
|
5016
|
+
String(posIntOr(maxTurns, MAX_TURNS_DEFAULT))
|
|
5017
|
+
];
|
|
5018
|
+
}
|
|
5019
|
+
function buildLocalNativeArgs(opts = {}, env2 = process.env) {
|
|
5020
|
+
const provider = resolveLocalProvider(env2);
|
|
5021
|
+
if (provider !== "ollama") {
|
|
5022
|
+
throw new Error(
|
|
5023
|
+
`local-model runner (native): the native tool-loop executor speaks Ollama's /api/chat; provider "${provider}" is not supported on native transport. Set VO_CODE_RUNNER_LOCAL_PROVIDER=ollama, or use VO_CODE_RUNNER_LOCAL_TRANSPORT=codex for LM Studio.`
|
|
5024
|
+
);
|
|
5025
|
+
}
|
|
5026
|
+
const model = resolveLocalModel(env2);
|
|
5027
|
+
if (!model) {
|
|
5028
|
+
throw new Error(
|
|
5029
|
+
"local-model runner (native): set VO_CODE_RUNNER_LOCAL_MODEL to a coding model your Ollama server already has (e.g. qwen2.5-coder:7b). Refusing to run with no explicit model (fail-closed)."
|
|
5030
|
+
);
|
|
5031
|
+
}
|
|
5032
|
+
if (!isValidLocalModel(model)) {
|
|
5033
|
+
throw new Error(`local-model runner (native): "${model}" is not a valid local model id.`);
|
|
5034
|
+
}
|
|
5035
|
+
const baseUrl = resolveLocalBaseUrl(env2);
|
|
5036
|
+
if (baseUrl && !isLoopbackBaseUrl(baseUrl)) {
|
|
5037
|
+
throw new Error(
|
|
5038
|
+
"local-model runner (native): VO_CODE_RUNNER_LOCAL_BASE_URL must be a loopback http(s) URL (localhost / 127.0.0.1 / [::1]). Remote endpoints are refused \u2014 use the Model Firewall lanes."
|
|
5039
|
+
);
|
|
5040
|
+
}
|
|
5041
|
+
return buildOllamaAgentArgs({
|
|
5042
|
+
model,
|
|
5043
|
+
profile: resolveLocalNativeProfile(env2),
|
|
5044
|
+
numCtx: env2.VO_CODE_RUNNER_LOCAL_NUM_CTX,
|
|
5045
|
+
maxTurns: env2.VO_CODE_RUNNER_LOCAL_MAX_TURNS
|
|
5046
|
+
});
|
|
5047
|
+
}
|
|
5048
|
+
var DEFAULT_LOCAL_TRANSPORT, LOCAL_NATIVE_PROFILES, DEFAULT_LOCAL_NATIVE_PROFILE;
|
|
5049
|
+
var init_ollama_native_transport = __esm({
|
|
5050
|
+
"../../scripts/virtual-office/code-runner/ollama-native-transport.mjs"() {
|
|
5051
|
+
"use strict";
|
|
5052
|
+
init_ollama_agent_core();
|
|
5053
|
+
init_local_model_runner();
|
|
5054
|
+
DEFAULT_LOCAL_TRANSPORT = "codex";
|
|
5055
|
+
LOCAL_NATIVE_PROFILES = Object.freeze(["coding", "verification"]);
|
|
5056
|
+
DEFAULT_LOCAL_NATIVE_PROFILE = "coding";
|
|
5057
|
+
}
|
|
5058
|
+
});
|
|
5059
|
+
|
|
4767
5060
|
// ../../scripts/virtual-office/code-runner/local-model-runner.mjs
|
|
4768
5061
|
function resolveLocalProvider(env2 = process.env) {
|
|
4769
5062
|
return String(env2.VO_CODE_RUNNER_LOCAL_PROVIDER || "").trim().toLowerCase() || DEFAULT_LOCAL_PROVIDER;
|
|
@@ -4837,20 +5130,37 @@ function buildLocalArgs(opts = {}, env2 = process.env) {
|
|
|
4837
5130
|
}
|
|
4838
5131
|
function applyLocalAuthEnv(baseEnv = process.env, configEnv = process.env) {
|
|
4839
5132
|
const out = withAgentKey("local", baseEnv);
|
|
5133
|
+
for (const key of Object.keys(out)) {
|
|
5134
|
+
if (FORBIDDEN_LOCAL_CHILD_CREDENTIALS.has(key.toUpperCase())) delete out[key];
|
|
5135
|
+
}
|
|
4840
5136
|
const baseUrl = resolveLocalBaseUrl(configEnv);
|
|
4841
5137
|
if (baseUrl && isLoopbackBaseUrl(baseUrl) && resolveLocalProvider(configEnv) === "ollama" && !String(out.OLLAMA_HOST || "").trim()) {
|
|
4842
5138
|
out.OLLAMA_HOST = baseUrl.replace(/\/+$/, "");
|
|
4843
5139
|
}
|
|
4844
5140
|
return out;
|
|
4845
5141
|
}
|
|
4846
|
-
var LOCAL_API_KEY_ENV, LOCAL_PROVIDERS, DEFAULT_LOCAL_PROVIDER, LOCAL_PROBE_URLS, remoteDesiredLocalModel, LOCAL_MODEL_RE, LocalModelRunner, localModelRunner;
|
|
5142
|
+
var LOCAL_API_KEY_ENV, FORBIDDEN_LOCAL_CHILD_CREDENTIALS, LOCAL_PROVIDERS, DEFAULT_LOCAL_PROVIDER, LOCAL_PROBE_URLS, remoteDesiredLocalModel, LOCAL_MODEL_RE, LocalModelRunner, localModelRunner;
|
|
4847
5143
|
var init_local_model_runner = __esm({
|
|
4848
5144
|
"../../scripts/virtual-office/code-runner/local-model-runner.mjs"() {
|
|
4849
5145
|
"use strict";
|
|
4850
5146
|
init_codex_runner();
|
|
4851
5147
|
init_agent_key_store();
|
|
4852
5148
|
init_agent_auth_tier();
|
|
5149
|
+
init_ollama_agent_core();
|
|
5150
|
+
init_ollama_native_transport();
|
|
4853
5151
|
LOCAL_API_KEY_ENV = "VO_CODE_RUNNER_LOCAL_API_KEY";
|
|
5152
|
+
FORBIDDEN_LOCAL_CHILD_CREDENTIALS = /* @__PURE__ */ new Set([
|
|
5153
|
+
"ANTHROPIC_API_KEY",
|
|
5154
|
+
"AWS_ACCESS_KEY_ID",
|
|
5155
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
5156
|
+
"AWS_SESSION_TOKEN",
|
|
5157
|
+
"FIREBASE_TOKEN",
|
|
5158
|
+
"GH_TOKEN",
|
|
5159
|
+
"GITHUB_TOKEN",
|
|
5160
|
+
"GOOGLE_API_KEY",
|
|
5161
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
5162
|
+
"OPENAI_API_KEY"
|
|
5163
|
+
]);
|
|
4854
5164
|
LOCAL_PROVIDERS = ["ollama", "lmstudio"];
|
|
4855
5165
|
DEFAULT_LOCAL_PROVIDER = "ollama";
|
|
4856
5166
|
LOCAL_PROBE_URLS = {
|
|
@@ -4871,19 +5181,25 @@ var init_local_model_runner = __esm({
|
|
|
4871
5181
|
this.env = env2;
|
|
4872
5182
|
this.fetchImpl = fetchImpl;
|
|
4873
5183
|
}
|
|
4874
|
-
/**
|
|
5184
|
+
/**
|
|
5185
|
+
* Transport binary. codex transport → the codex CLI. native transport →
|
|
5186
|
+
* this daemon's own node (process.execPath), which runs ollama-agent.mjs; no
|
|
5187
|
+
* external CLI is involved on the native path.
|
|
5188
|
+
*/
|
|
4875
5189
|
get binary() {
|
|
4876
|
-
return this.resolveBinary();
|
|
5190
|
+
return resolveLocalTransport(this.env) === "native" ? process.execPath : this.resolveBinary();
|
|
4877
5191
|
}
|
|
4878
5192
|
buildArgs(opts = {}) {
|
|
4879
|
-
return buildLocalArgs(opts, this.env);
|
|
5193
|
+
return resolveLocalTransport(this.env) === "native" ? buildLocalNativeArgs(opts, this.env) : buildLocalArgs(opts, this.env);
|
|
4880
5194
|
}
|
|
4881
5195
|
/**
|
|
4882
|
-
*
|
|
4883
|
-
*
|
|
4884
|
-
*
|
|
5196
|
+
* native transport → parse the executor's own JSONL contract. codex transport
|
|
5197
|
+
* → codex JSONL maps identically, except a sovereign local model has no vendor
|
|
5198
|
+
* bill: codex omits total_cost_usd for OSS runs, so turn that known fact into a
|
|
5199
|
+
* measured zero at the producer. (The native parser already stamps costUsd:0.)
|
|
4885
5200
|
*/
|
|
4886
5201
|
parseEvent(line) {
|
|
5202
|
+
if (resolveLocalTransport(this.env) === "native") return parseOllamaAgentEvent(line);
|
|
4887
5203
|
const event = parseCodexEvent(line);
|
|
4888
5204
|
return event?.kind === "result" ? { ...event, costUsd: 0 } : event;
|
|
4889
5205
|
}
|
|
@@ -5244,9 +5560,9 @@ var init_rate_limit_detector_core = __esm({
|
|
|
5244
5560
|
|
|
5245
5561
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
|
|
5246
5562
|
import fsp10 from "node:fs/promises";
|
|
5247
|
-
import
|
|
5563
|
+
import path13 from "node:path";
|
|
5248
5564
|
async function atomicWrite(file, content) {
|
|
5249
|
-
await fsp10.mkdir(
|
|
5565
|
+
await fsp10.mkdir(path13.dirname(file), { recursive: true });
|
|
5250
5566
|
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
5251
5567
|
const handle = await fsp10.open(temp, "wx");
|
|
5252
5568
|
try {
|
|
@@ -5307,7 +5623,7 @@ function writeResumeAttempts(file, store) {
|
|
|
5307
5623
|
}
|
|
5308
5624
|
async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
|
|
5309
5625
|
const deadline = now() + LOCK_WAIT_MS;
|
|
5310
|
-
await fsp10.mkdir(
|
|
5626
|
+
await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
|
|
5311
5627
|
for (; ; ) {
|
|
5312
5628
|
let handle;
|
|
5313
5629
|
try {
|
|
@@ -5381,10 +5697,10 @@ var init_rate_limit_resume_state = __esm({
|
|
|
5381
5697
|
});
|
|
5382
5698
|
|
|
5383
5699
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
|
|
5384
|
-
import { homedir as
|
|
5385
|
-
import { join as
|
|
5700
|
+
import { homedir as homedir3 } from "node:os";
|
|
5701
|
+
import { join as join3 } from "node:path";
|
|
5386
5702
|
function resumeQueuePath() {
|
|
5387
|
-
return
|
|
5703
|
+
return join3(homedir3(), ".claude", "resume-queue.jsonl");
|
|
5388
5704
|
}
|
|
5389
5705
|
function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
|
|
5390
5706
|
return {
|
|
@@ -5585,8 +5901,8 @@ var init_auto_merge = __esm({
|
|
|
5585
5901
|
|
|
5586
5902
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
5587
5903
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
5588
|
-
import { existsSync as
|
|
5589
|
-
import { fileURLToPath } from "node:url";
|
|
5904
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
5905
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5590
5906
|
function stripCredentials(env2 = process.env) {
|
|
5591
5907
|
const safe = { ...env2 };
|
|
5592
5908
|
for (const key of CREDENTIAL_ENV_KEYS) delete safe[key];
|
|
@@ -5596,7 +5912,7 @@ function resolveOverlapScript({
|
|
|
5596
5912
|
worktreeDir,
|
|
5597
5913
|
trustedPath = null,
|
|
5598
5914
|
trustedPaths = TRUSTED_OVERLAP_CANDIDATES,
|
|
5599
|
-
existsFn =
|
|
5915
|
+
existsFn = existsSync6,
|
|
5600
5916
|
joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
|
|
5601
5917
|
} = {}) {
|
|
5602
5918
|
const candidates = trustedPath ? [trustedPath] : trustedPaths;
|
|
@@ -5612,7 +5928,7 @@ var init_pr_overlap_gate = __esm({
|
|
|
5612
5928
|
TRUSTED_OVERLAP_CANDIDATES = [
|
|
5613
5929
|
new URL("../../ci/check-local-pr-overlap.mjs", import.meta.url),
|
|
5614
5930
|
new URL("./ci/check-local-pr-overlap.js", import.meta.url)
|
|
5615
|
-
].map((candidate) =>
|
|
5931
|
+
].map((candidate) => fileURLToPath2(candidate));
|
|
5616
5932
|
CREDENTIAL_ENV_KEYS = Object.freeze([
|
|
5617
5933
|
"GH_TOKEN",
|
|
5618
5934
|
"GITHUB_TOKEN",
|
|
@@ -5665,14 +5981,14 @@ function parsePorcelainZ(out) {
|
|
|
5665
5981
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
5666
5982
|
const token2 = tokens[i];
|
|
5667
5983
|
if (!token2) continue;
|
|
5668
|
-
const
|
|
5669
|
-
if (
|
|
5984
|
+
const path23 = token2.slice(3);
|
|
5985
|
+
if (path23) files.push(path23);
|
|
5670
5986
|
if (token2[0] === "R" || token2[0] === "C") i += 1;
|
|
5671
5987
|
}
|
|
5672
5988
|
return files;
|
|
5673
5989
|
}
|
|
5674
|
-
function isAgentScratch(
|
|
5675
|
-
const normalized = String(
|
|
5990
|
+
function isAgentScratch(path23) {
|
|
5991
|
+
const normalized = String(path23 || "");
|
|
5676
5992
|
return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
5677
5993
|
}
|
|
5678
5994
|
var SCRATCH_PATTERNS;
|
|
@@ -5739,6 +6055,540 @@ var init_publish = __esm({
|
|
|
5739
6055
|
}
|
|
5740
6056
|
});
|
|
5741
6057
|
|
|
6058
|
+
// ../../scripts/virtual-office/test-gen/auto-tier.mjs
|
|
6059
|
+
function lower(s) {
|
|
6060
|
+
return typeof s === "string" ? s.toLowerCase() : "";
|
|
6061
|
+
}
|
|
6062
|
+
function isAlgoProduct(product) {
|
|
6063
|
+
return /^algo/i.test(String(product || ""));
|
|
6064
|
+
}
|
|
6065
|
+
function isAdminRoute(route) {
|
|
6066
|
+
return /(^|\/)admin(\/|$)/i.test(String(route || ""));
|
|
6067
|
+
}
|
|
6068
|
+
function importsMatch(imports, re) {
|
|
6069
|
+
return Array.isArray(imports) && imports.some((i) => re.test(String(i || "")));
|
|
6070
|
+
}
|
|
6071
|
+
function classifyTier(contract = {}) {
|
|
6072
|
+
const product = lower(contract.product);
|
|
6073
|
+
const callable = String(contract.callable || "");
|
|
6074
|
+
const route = String(contract.route || "");
|
|
6075
|
+
const imports = contract.imports;
|
|
6076
|
+
const ov = contract.tier_override;
|
|
6077
|
+
if (ov === 1 || ov === 2 || ov === 3 || ov === 4) {
|
|
6078
|
+
return { tier: ov, rationale: `operator override \u2192 Tier ${ov}`, tier_override: true };
|
|
6079
|
+
}
|
|
6080
|
+
const algo = isAlgoProduct(product);
|
|
6081
|
+
let tier;
|
|
6082
|
+
let why;
|
|
6083
|
+
const legalDomain = LEGAL_PRODUCTS.has(product);
|
|
6084
|
+
const hasGovernedSource = Boolean(contract.authoritative_source_url) || importsMatch(imports, LEGAL_IMPORT_RE);
|
|
6085
|
+
if (legalDomain && hasGovernedSource) {
|
|
6086
|
+
tier = 4;
|
|
6087
|
+
why = `legal-correctness domain (${product}) with a governed source \u2192 source-grounded Tier 4`;
|
|
6088
|
+
} else if (isAdminRoute(route) || MONEY_OR_SAFETY_RE.test(callable)) {
|
|
6089
|
+
tier = 3;
|
|
6090
|
+
why = isAdminRoute(route) ? "admin/safety-critical route \u2192 real-time monitor Tier 3" : `money-moving or grading callable (${callable}) \u2192 real-time monitor Tier 3`;
|
|
6091
|
+
} else if (importsMatch(imports, GENERATIVE_RE) || GENERATIVE_CALLABLE_RE.test(callable)) {
|
|
6092
|
+
const long = Number(contract.workflow_seconds) > 30;
|
|
6093
|
+
tier = long ? 3 : 2;
|
|
6094
|
+
why = `generative/AI feature \u2192 E2E with verified results${long ? " + monitor (long workflow) Tier 3" : " Tier 2"}`;
|
|
6095
|
+
} else if (callable || route) {
|
|
6096
|
+
tier = 2;
|
|
6097
|
+
why = "standard business logic \u2192 E2E with verified results (read-back) Tier 2";
|
|
6098
|
+
} else {
|
|
6099
|
+
tier = 1;
|
|
6100
|
+
why = "pure navigation/surface, no backend effect \u2192 surface smoke Tier 1";
|
|
6101
|
+
}
|
|
6102
|
+
if (algo && tier < 2) {
|
|
6103
|
+
return {
|
|
6104
|
+
tier: 2,
|
|
6105
|
+
rationale: `${why}; raised to Tier 2 (E2E floor: Algo* apps never auto-pick surface-smoke-only)`,
|
|
6106
|
+
tier_override: false
|
|
6107
|
+
};
|
|
6108
|
+
}
|
|
6109
|
+
return { tier, rationale: why, tier_override: false };
|
|
6110
|
+
}
|
|
6111
|
+
function tierLabel(tier) {
|
|
6112
|
+
return {
|
|
6113
|
+
1: "Tier 1 \u2014 surface & navigation smoke",
|
|
6114
|
+
2: "Tier 2 \u2014 E2E with verified results",
|
|
6115
|
+
3: "Tier 3 \u2014 real-time monitor / sentinel",
|
|
6116
|
+
4: "Tier 4 \u2014 source-grounded governed-fact verification"
|
|
6117
|
+
}[tier] || `Tier ${tier}`;
|
|
6118
|
+
}
|
|
6119
|
+
var LEGAL_PRODUCTS, LEGAL_IMPORT_RE, GENERATIVE_RE, GENERATIVE_CALLABLE_RE, MONEY_OR_SAFETY_RE;
|
|
6120
|
+
var init_auto_tier = __esm({
|
|
6121
|
+
"../../scripts/virtual-office/test-gen/auto-tier.mjs"() {
|
|
6122
|
+
"use strict";
|
|
6123
|
+
LEGAL_PRODUCTS = /* @__PURE__ */ new Set(["algotax", "algolaw", "algoteach", "algolegal"]);
|
|
6124
|
+
LEGAL_IMPORT_RE = /functions-core-(tax|law)|cornell|irs|\bstatute\b/i;
|
|
6125
|
+
GENERATIVE_RE = /@google\/generative-ai|@anthropic|anthropic|openai|@google\/genai|consensus|\bllm\b/i;
|
|
6126
|
+
GENERATIVE_CALLABLE_RE = /generate|summari[sz]e|\bai\b|consensus|draft|classify/i;
|
|
6127
|
+
MONEY_OR_SAFETY_RE = /grade|payment|transfer|trade|charge|refund|payout|disburse|withdraw|remit/i;
|
|
6128
|
+
}
|
|
6129
|
+
});
|
|
6130
|
+
|
|
6131
|
+
// ../../scripts/virtual-office/test-gen/dispatch.mjs
|
|
6132
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
6133
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6134
|
+
import path14 from "node:path";
|
|
6135
|
+
function buildGenerationPrompt({ contract = {}, tier }) {
|
|
6136
|
+
const label = tierLabel(tier);
|
|
6137
|
+
const target = contract.callable ? `the \`${contract.callable}\` callable` : contract.route ? `the \`${contract.route}\` route` : contract.feature_name || "the feature";
|
|
6138
|
+
return [
|
|
6139
|
+
`Write a ${label} test for ${target} in product "${contract.product || "unknown"}".`,
|
|
6140
|
+
contract.source_file ? `Source: ${contract.source_file}.` : "",
|
|
6141
|
+
contract.expected_behavior ? `Expected behavior: ${contract.expected_behavior}.` : "",
|
|
6142
|
+
"",
|
|
6143
|
+
`TIER REQUIREMENT \u2014 ${TIER_GUIDANCE[tier] || TIER_GUIDANCE[2]}`,
|
|
6144
|
+
"",
|
|
6145
|
+
"Follow the AlgoSuite test-honesty standard: NO fake-green (no bare toBeTruthy, no broad",
|
|
6146
|
+
"try/catch that swallows failures, no treating INVALID_ARGUMENT / empty / null / SKIP as a",
|
|
6147
|
+
"pass). Use a real fixture (smoke@algosuite.ai) and real data shapes.",
|
|
6148
|
+
"",
|
|
6149
|
+
"After writing the test, it will be GATED server-side: deterministic ratchets, then",
|
|
6150
|
+
"multi-model consensus that it proves the behavior with VERIFIED-CORRECT expected values.",
|
|
6151
|
+
"A test that does not pass BOTH gates will NOT ship \u2014 so make the assertions real and the",
|
|
6152
|
+
"expected values known-correct. Leave the test file UNCOMMITTED; the runner opens the PR."
|
|
6153
|
+
].filter((l) => l !== "").join("\n");
|
|
6154
|
+
}
|
|
6155
|
+
function planTestGenDispatch({ contract = {} }) {
|
|
6156
|
+
const { tier, rationale } = classifyTier(contract);
|
|
6157
|
+
return {
|
|
6158
|
+
contract,
|
|
6159
|
+
tier,
|
|
6160
|
+
tier_label: tierLabel(tier),
|
|
6161
|
+
tier_rationale: rationale,
|
|
6162
|
+
generation_prompt: buildGenerationPrompt({ contract, tier })
|
|
6163
|
+
};
|
|
6164
|
+
}
|
|
6165
|
+
function buildTestGenTaskPrompt(dispatch = {}) {
|
|
6166
|
+
const contractJson = JSON.stringify(dispatch.contract ?? {});
|
|
6167
|
+
return `${TEST_GEN_MARKER} ${contractJson}
|
|
6168
|
+
|
|
6169
|
+
${dispatch.generation_prompt ?? ""}`;
|
|
6170
|
+
}
|
|
6171
|
+
function pickNextTarget({ gaps = [] }) {
|
|
6172
|
+
if (!Array.isArray(gaps) || gaps.length === 0) return null;
|
|
6173
|
+
const ranked = [...gaps].sort((a, b) => {
|
|
6174
|
+
const al = LEGAL.has(String(a.product)) ? 0 : 1;
|
|
6175
|
+
const bl = LEGAL.has(String(b.product)) ? 0 : 1;
|
|
6176
|
+
return al - bl;
|
|
6177
|
+
});
|
|
6178
|
+
return ranked[0];
|
|
6179
|
+
}
|
|
6180
|
+
function resolveRepo(argv, env2) {
|
|
6181
|
+
const repoArg = argv.indexOf("--repo");
|
|
6182
|
+
if (repoArg >= 0 && argv[repoArg + 1]) return argv[repoArg + 1];
|
|
6183
|
+
if (env2.VO_TEST_GEN_REPO) return env2.VO_TEST_GEN_REPO;
|
|
6184
|
+
const remote = spawnSync10("git", ["remote", "get-url", "origin"], { encoding: "utf8" });
|
|
6185
|
+
if (remote.status === 0) {
|
|
6186
|
+
const m = String(remote.stdout).trim().match(/[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
6187
|
+
if (m) return m[1];
|
|
6188
|
+
}
|
|
6189
|
+
return null;
|
|
6190
|
+
}
|
|
6191
|
+
async function main(argv = process.argv.slice(2)) {
|
|
6192
|
+
const countArg = argv.indexOf("--count");
|
|
6193
|
+
const count3 = countArg >= 0 ? Math.max(1, Number(argv[countArg + 1]) || 1) : 1;
|
|
6194
|
+
const enqueue = argv.includes("--enqueue");
|
|
6195
|
+
const here = path14.dirname(fileURLToPath3(import.meta.url));
|
|
6196
|
+
const scan = spawnSync10("node", [path14.join(here, "coverage-scan.mjs"), "--limit", String(count3 * 8)], {
|
|
6197
|
+
encoding: "utf8",
|
|
6198
|
+
maxBuffer: 64 * 1024 * 1024
|
|
6199
|
+
});
|
|
6200
|
+
if (scan.status !== 0) {
|
|
6201
|
+
console.error(`[dispatch] coverage-scan failed: ${scan.stderr || scan.stdout}`);
|
|
6202
|
+
process.exit(1);
|
|
6203
|
+
}
|
|
6204
|
+
let gaps = [];
|
|
6205
|
+
try {
|
|
6206
|
+
gaps = JSON.parse(scan.stdout).gaps || [];
|
|
6207
|
+
} catch (err) {
|
|
6208
|
+
console.error(`[dispatch] could not parse scan output: ${err.message}`);
|
|
6209
|
+
process.exit(1);
|
|
6210
|
+
}
|
|
6211
|
+
const dispatches = [];
|
|
6212
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6213
|
+
let pool = gaps;
|
|
6214
|
+
while (dispatches.length < count3 && pool.length > 0) {
|
|
6215
|
+
const target = pickNextTarget({ gaps: pool });
|
|
6216
|
+
if (!target) break;
|
|
6217
|
+
const key = `${target.product}:${target.callable}`;
|
|
6218
|
+
if (!seen.has(key)) {
|
|
6219
|
+
seen.add(key);
|
|
6220
|
+
dispatches.push(planTestGenDispatch({ contract: target }));
|
|
6221
|
+
}
|
|
6222
|
+
pool = pool.filter((g) => `${g.product}:${g.callable}` !== key);
|
|
6223
|
+
}
|
|
6224
|
+
if (!enqueue) {
|
|
6225
|
+
console.log(JSON.stringify({ requested: count3, emitted: dispatches.length, dispatches }, null, 2));
|
|
6226
|
+
return;
|
|
6227
|
+
}
|
|
6228
|
+
const repo = resolveRepo(argv, process.env);
|
|
6229
|
+
if (!repo) {
|
|
6230
|
+
console.error("[dispatch] --enqueue needs a repo: pass --repo owner/name or set VO_TEST_GEN_REPO (could not derive from git origin)");
|
|
6231
|
+
process.exit(1);
|
|
6232
|
+
}
|
|
6233
|
+
const { createControlPlaneClient: createControlPlaneClient2 } = await Promise.resolve().then(() => (init_control_plane_client(), control_plane_client_exports));
|
|
6234
|
+
const client = createControlPlaneClient2({ env: process.env });
|
|
6235
|
+
const enqueued = [];
|
|
6236
|
+
for (const d of dispatches) {
|
|
6237
|
+
const task = await client.enqueueCodeTask({
|
|
6238
|
+
repo,
|
|
6239
|
+
prompt: buildTestGenTaskPrompt(d),
|
|
6240
|
+
max_turns: 40
|
|
6241
|
+
});
|
|
6242
|
+
const taskId = task && task.code_task_id ? task.code_task_id : "(unknown)";
|
|
6243
|
+
enqueued.push({ task_id: taskId, product: d.contract.product, callable: d.contract.callable, tier: d.tier_label });
|
|
6244
|
+
console.error(`[dispatch] enqueued ${d.tier_label} test-gen for ${d.contract.product}:${d.contract.callable} \u2192 task ${taskId}`);
|
|
6245
|
+
}
|
|
6246
|
+
console.log(JSON.stringify({ requested: count3, enqueued: enqueued.length, repo, tasks: enqueued }, null, 2));
|
|
6247
|
+
}
|
|
6248
|
+
var TEST_GEN_MARKER, TIER_GUIDANCE, LEGAL, invokedDirectly;
|
|
6249
|
+
var init_dispatch = __esm({
|
|
6250
|
+
"../../scripts/virtual-office/test-gen/dispatch.mjs"() {
|
|
6251
|
+
"use strict";
|
|
6252
|
+
init_auto_tier();
|
|
6253
|
+
TEST_GEN_MARKER = "[VO-TEST-GEN]";
|
|
6254
|
+
TIER_GUIDANCE = {
|
|
6255
|
+
1: "Surface/navigation smoke: assert the surface renders and the key controls are present. No backend effect needed.",
|
|
6256
|
+
2: "E2E with VERIFIED RESULTS: drive the real user/callable workflow, then READ BACK the produced artifact and assert KNOWN-CORRECT expected values (not just a 200 / truthiness).",
|
|
6257
|
+
3: "Real-time monitor / sentinel: drive the workflow AND verify the state transitions + safety rails (no money moved / graded / admin action without the guard). Assert the post-conditions, not just the response.",
|
|
6258
|
+
4: "Source-grounded governed-fact: assert expected values that are GROUNDED in the cited authoritative source (the contract's authoritative_source_url). The fixture and the expected number/string must trace to that source."
|
|
6259
|
+
};
|
|
6260
|
+
LEGAL = /* @__PURE__ */ new Set(["algotax", "algolaw", "algoteach", "algolegal"]);
|
|
6261
|
+
invokedDirectly = process.argv[1] && fileURLToPath3(import.meta.url) === path14.resolve(process.argv[1]);
|
|
6262
|
+
if (invokedDirectly) {
|
|
6263
|
+
main().catch((err) => {
|
|
6264
|
+
console.error(`[dispatch] ${err.message}`);
|
|
6265
|
+
process.exit(1);
|
|
6266
|
+
});
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6269
|
+
});
|
|
6270
|
+
|
|
6271
|
+
// ../../scripts/virtual-office/test-gen/executor.mjs
|
|
6272
|
+
function consensusQuestion(tier) {
|
|
6273
|
+
const tierAsk = tier >= 4 ? " For this governed-fact (Tier 4) test, the expected values MUST be grounded in the cited authoritative source." : tier >= 3 ? " For this safety-critical (Tier 3) test, it must verify state transitions and the safety rails, not just a 200." : "";
|
|
6274
|
+
return "Does this test PROVE the stated behavior with VERIFIED-CORRECT expected values (not fake-green)? It must exercise the real product path (no stub/mock that bypasses the feature) and assert known-correct values, not placeholders." + tierAsk;
|
|
6275
|
+
}
|
|
6276
|
+
function buildContractSummary(contract, tier) {
|
|
6277
|
+
const parts = [
|
|
6278
|
+
`Feature: ${contract.feature_name || contract.callable || contract.route || "unnamed"}`,
|
|
6279
|
+
`Product: ${contract.product || "unknown"}`,
|
|
6280
|
+
contract.route ? `Route: ${contract.route}` : "",
|
|
6281
|
+
contract.callable ? `Callable: ${contract.callable}` : "",
|
|
6282
|
+
`Auto-selected tier: ${tierLabel(tier)}`,
|
|
6283
|
+
contract.expected_behavior ? `Expected behavior: ${contract.expected_behavior}` : "",
|
|
6284
|
+
contract.authoritative_source_url ? `Authoritative source: ${contract.authoritative_source_url}` : ""
|
|
6285
|
+
];
|
|
6286
|
+
return parts.filter(Boolean).join("\n");
|
|
6287
|
+
}
|
|
6288
|
+
async function gateGeneratedTest({ contract = {}, testSource = "", verify, taskId = "autotest" }) {
|
|
6289
|
+
if (typeof verify !== "function") throw new Error("gateGeneratedTest requires a `verify` transport");
|
|
6290
|
+
const { tier, rationale } = classifyTier(contract);
|
|
6291
|
+
const label = tierLabel(tier);
|
|
6292
|
+
const filename = typeof contract.test_filename === "string" ? contract.test_filename : void 0;
|
|
6293
|
+
const ratchets = await verify({
|
|
6294
|
+
task_id: taskId,
|
|
6295
|
+
gate_type: "ratchets",
|
|
6296
|
+
excerpt: testSource,
|
|
6297
|
+
...filename ? { context: { filename } } : {}
|
|
6298
|
+
});
|
|
6299
|
+
if (!ratchets || ratchets.approved !== true) {
|
|
6300
|
+
return {
|
|
6301
|
+
ship: false,
|
|
6302
|
+
tier,
|
|
6303
|
+
tierLabel: label,
|
|
6304
|
+
stage: "ratchets",
|
|
6305
|
+
reason: ratchets?.reason || "ratchets gate did not approve",
|
|
6306
|
+
ratchets: ratchets || null,
|
|
6307
|
+
consensus: null
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6310
|
+
const consensus = await verify({
|
|
6311
|
+
task_id: taskId,
|
|
6312
|
+
gate_type: CONSENSUS_GATE_TYPE,
|
|
6313
|
+
excerpt: `${buildContractSummary(contract, tier)}
|
|
6314
|
+
|
|
6315
|
+
--- TEST ---
|
|
6316
|
+
${testSource}`,
|
|
6317
|
+
question: consensusQuestion(tier)
|
|
6318
|
+
});
|
|
6319
|
+
const consensusBlocks = consensus?.available === true && consensus?.approved !== true;
|
|
6320
|
+
if (consensusBlocks) {
|
|
6321
|
+
return {
|
|
6322
|
+
ship: false,
|
|
6323
|
+
tier,
|
|
6324
|
+
tierLabel: label,
|
|
6325
|
+
stage: "consensus",
|
|
6326
|
+
reason: consensus?.reason || "consensus did not confirm verified-correct expected values",
|
|
6327
|
+
ratchets,
|
|
6328
|
+
consensus
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
return {
|
|
6332
|
+
ship: true,
|
|
6333
|
+
tier,
|
|
6334
|
+
tierLabel: label,
|
|
6335
|
+
stage: "passed",
|
|
6336
|
+
reason: consensus?.available === true ? `Passed ratchets + consensus (${label}). ${rationale}` : `Passed ratchets; consensus unavailable so not blocking (${label}). ${rationale}`,
|
|
6337
|
+
ratchets,
|
|
6338
|
+
consensus
|
|
6339
|
+
};
|
|
6340
|
+
}
|
|
6341
|
+
function makeHttpVerify({ baseUrl, token: token2, fetchImpl = fetch }) {
|
|
6342
|
+
if (!baseUrl) throw new Error("makeHttpVerify requires a baseUrl");
|
|
6343
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/v1/verify`;
|
|
6344
|
+
return async function httpVerify(req) {
|
|
6345
|
+
let res;
|
|
6346
|
+
try {
|
|
6347
|
+
res = await fetchImpl(url, {
|
|
6348
|
+
method: "POST",
|
|
6349
|
+
headers: { "Content-Type": "application/json", ...token2 ? { Authorization: `Bearer ${token2}` } : {} },
|
|
6350
|
+
body: JSON.stringify(req)
|
|
6351
|
+
});
|
|
6352
|
+
} catch (err) {
|
|
6353
|
+
return { ok: false, available: false, approved: false, reason: `transport error: ${err?.message || err}` };
|
|
6354
|
+
}
|
|
6355
|
+
let body = {};
|
|
6356
|
+
try {
|
|
6357
|
+
body = await res.json();
|
|
6358
|
+
} catch {
|
|
6359
|
+
}
|
|
6360
|
+
if (!res.ok) {
|
|
6361
|
+
return { ok: false, available: false, approved: false, reason: body?.error || `http ${res.status}` };
|
|
6362
|
+
}
|
|
6363
|
+
return body;
|
|
6364
|
+
};
|
|
6365
|
+
}
|
|
6366
|
+
var CONSENSUS_GATE_TYPE;
|
|
6367
|
+
var init_executor = __esm({
|
|
6368
|
+
"../../scripts/virtual-office/test-gen/executor.mjs"() {
|
|
6369
|
+
"use strict";
|
|
6370
|
+
init_auto_tier();
|
|
6371
|
+
CONSENSUS_GATE_TYPE = "test_correctness_consensus";
|
|
6372
|
+
}
|
|
6373
|
+
});
|
|
6374
|
+
|
|
6375
|
+
// ../../scripts/virtual-office/code-runner/test-gen-gate.mjs
|
|
6376
|
+
import fs7 from "node:fs";
|
|
6377
|
+
import path15 from "node:path";
|
|
6378
|
+
async function postFailed(client, id, message, result) {
|
|
6379
|
+
try {
|
|
6380
|
+
await client.postProgress(id, {
|
|
6381
|
+
status: "failed",
|
|
6382
|
+
message: String(message).slice(0, 1500),
|
|
6383
|
+
result
|
|
6384
|
+
});
|
|
6385
|
+
} catch {
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
async function gateTestGenTaskOrFail({ client, id, task, files, worktreeDir, env: env2 = process.env, log: log2 = () => {
|
|
6389
|
+
}, verify: verifyInjected } = {}) {
|
|
6390
|
+
const prompt = String(task && task.prompt || "");
|
|
6391
|
+
if (!prompt.startsWith(TEST_GEN_MARKER)) return false;
|
|
6392
|
+
let contract;
|
|
6393
|
+
try {
|
|
6394
|
+
const after = prompt.slice(TEST_GEN_MARKER.length).trimStart();
|
|
6395
|
+
contract = JSON.parse(after.split("\n\n")[0]);
|
|
6396
|
+
} catch (err) {
|
|
6397
|
+
log2(`task ${id}: test-gen contract parse failed: ${err.message}`);
|
|
6398
|
+
await postFailed(client, id, `test-gen contract parse failed: ${err.message}`, "gate_contract_unparseable");
|
|
6399
|
+
return true;
|
|
6400
|
+
}
|
|
6401
|
+
const testFile = (files || []).find((f) => TEST_FILE_RE.test(f));
|
|
6402
|
+
if (!testFile) {
|
|
6403
|
+
await postFailed(client, id, "test-gen task produced no *.test.* file", "gate_no_test_file");
|
|
6404
|
+
return true;
|
|
6405
|
+
}
|
|
6406
|
+
const baseUrl = env2.VO_MOAT_PLANE_URL;
|
|
6407
|
+
if (!verifyInjected && !baseUrl) {
|
|
6408
|
+
log2(`task ${id}: VO_MOAT_PLANE_URL unset \u2014 skipping test-gen gate (fail-safe, ADR-002 opt-out)`);
|
|
6409
|
+
return false;
|
|
6410
|
+
}
|
|
6411
|
+
let testSource = "";
|
|
6412
|
+
try {
|
|
6413
|
+
testSource = fs7.readFileSync(path15.join(worktreeDir, testFile), "utf8");
|
|
6414
|
+
} catch (err) {
|
|
6415
|
+
await postFailed(client, id, `could not read generated test ${testFile}: ${err.message}`, "gate_test_unreadable");
|
|
6416
|
+
return true;
|
|
6417
|
+
}
|
|
6418
|
+
const verify = verifyInjected || makeHttpVerify({ baseUrl: String(baseUrl).replace(/\/$/, ""), token: env2.VO_MOAT_PLANE_TOKEN || "" });
|
|
6419
|
+
let verdict;
|
|
6420
|
+
try {
|
|
6421
|
+
verdict = await gateGeneratedTest({
|
|
6422
|
+
contract: { ...contract, test_filename: testFile },
|
|
6423
|
+
testSource,
|
|
6424
|
+
verify,
|
|
6425
|
+
taskId: id
|
|
6426
|
+
});
|
|
6427
|
+
} catch (err) {
|
|
6428
|
+
log2(`task ${id}: test-gen gate could not run: ${err.message}`);
|
|
6429
|
+
await postFailed(client, id, `test-gen gate could not run (${err.message}) \u2014 not shipping un-gated`, "gate_transport_error");
|
|
6430
|
+
return true;
|
|
6431
|
+
}
|
|
6432
|
+
if (!verdict.ship) {
|
|
6433
|
+
log2(`task ${id}: test-gen gate REJECTED at ${verdict.stage}`);
|
|
6434
|
+
await postFailed(
|
|
6435
|
+
client,
|
|
6436
|
+
id,
|
|
6437
|
+
`test-gen gate rejected (${verdict.stage}): ${verdict.reason || "did not pass ratchets + consensus"}`,
|
|
6438
|
+
"gate_rejected"
|
|
6439
|
+
);
|
|
6440
|
+
return true;
|
|
6441
|
+
}
|
|
6442
|
+
try {
|
|
6443
|
+
await client.postProgress(id, {
|
|
6444
|
+
message: `test-gen gate PASSED (${verdict.tierLabel || "tier"}): ${verdict.reason || "ratchets + consensus"}`
|
|
6445
|
+
});
|
|
6446
|
+
} catch {
|
|
6447
|
+
}
|
|
6448
|
+
return false;
|
|
6449
|
+
}
|
|
6450
|
+
var TEST_FILE_RE;
|
|
6451
|
+
var init_test_gen_gate = __esm({
|
|
6452
|
+
"../../scripts/virtual-office/code-runner/test-gen-gate.mjs"() {
|
|
6453
|
+
"use strict";
|
|
6454
|
+
init_dispatch();
|
|
6455
|
+
init_executor();
|
|
6456
|
+
TEST_FILE_RE = /\.(test|spec)\.(ts|tsx|mjs|js)$/;
|
|
6457
|
+
}
|
|
6458
|
+
});
|
|
6459
|
+
|
|
6460
|
+
// ../../scripts/virtual-office/code-runner/completion-gate.mjs
|
|
6461
|
+
import { execFile } from "node:child_process";
|
|
6462
|
+
import fs8 from "node:fs";
|
|
6463
|
+
import path16 from "node:path";
|
|
6464
|
+
function resolveCompletionGate(task) {
|
|
6465
|
+
const raw = task?.completion_gate;
|
|
6466
|
+
if (raw === void 0 || raw === null) return null;
|
|
6467
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
6468
|
+
return { invalid: true, reason: "completion_gate must be a non-empty string" };
|
|
6469
|
+
}
|
|
6470
|
+
const trimmed = raw.trim();
|
|
6471
|
+
if (trimmed.length > 500) {
|
|
6472
|
+
return { invalid: true, reason: "completion_gate exceeds 500 characters" };
|
|
6473
|
+
}
|
|
6474
|
+
if (SHELL_METACHARACTERS.test(trimmed)) {
|
|
6475
|
+
return { invalid: true, reason: "completion_gate contains shell metacharacters (no-shell contract: plain argv only, no pipes/redirects/quotes)" };
|
|
6476
|
+
}
|
|
6477
|
+
const [command, ...args] = trimmed.split(/\s+/);
|
|
6478
|
+
const allowedCommand = ALLOWED_GATE_COMMANDS.get(command);
|
|
6479
|
+
if (allowedCommand === void 0) {
|
|
6480
|
+
const shown = command.length > 40 ? `${command.slice(0, 40)}...` : command;
|
|
6481
|
+
return {
|
|
6482
|
+
invalid: true,
|
|
6483
|
+
reason: `completion_gate command ${shown} is not an allowed gate runner (${[...ALLOWED_GATE_COMMANDS.keys()].join(", ")})`
|
|
6484
|
+
};
|
|
6485
|
+
}
|
|
6486
|
+
return { argv: [allowedCommand, ...args] };
|
|
6487
|
+
}
|
|
6488
|
+
function boundedTail(text) {
|
|
6489
|
+
const s = String(text ?? "");
|
|
6490
|
+
return s.length <= COMPLETION_GATE_OUTPUT_CAP ? s : s.slice(s.length - COMPLETION_GATE_OUTPUT_CAP);
|
|
6491
|
+
}
|
|
6492
|
+
function workspaceFingerprint(worktreeDir, execFileImpl = execFile) {
|
|
6493
|
+
return new Promise((resolve2) => {
|
|
6494
|
+
execFileImpl("git", ["rev-parse", "HEAD^{tree}"], { cwd: worktreeDir }, (err, stdout) => {
|
|
6495
|
+
resolve2(err ? null : String(stdout).trim() || null);
|
|
6496
|
+
});
|
|
6497
|
+
});
|
|
6498
|
+
}
|
|
6499
|
+
function readState(worktreeDir) {
|
|
6500
|
+
try {
|
|
6501
|
+
return JSON.parse(fs8.readFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
|
|
6502
|
+
} catch {
|
|
6503
|
+
return null;
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
function writeState(worktreeDir, state) {
|
|
6507
|
+
try {
|
|
6508
|
+
fs8.writeFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
|
|
6509
|
+
`, "utf8");
|
|
6510
|
+
} catch {
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
function runGateCommand({ argv, worktreeDir, timeoutMs = COMPLETION_GATE_TIMEOUT_MS, execFileImpl = execFile }) {
|
|
6514
|
+
return new Promise((resolve2) => {
|
|
6515
|
+
const command = ALLOWED_GATE_COMMANDS.get(argv?.[0]);
|
|
6516
|
+
if (command === void 0) {
|
|
6517
|
+
return resolve2({
|
|
6518
|
+
exitCode: 1,
|
|
6519
|
+
output: `completion_gate executable is not an allowed gate runner (${[...ALLOWED_GATE_COMMANDS.keys()].join(", ")})`,
|
|
6520
|
+
timedOut: false
|
|
6521
|
+
});
|
|
6522
|
+
}
|
|
6523
|
+
execFileImpl(
|
|
6524
|
+
command,
|
|
6525
|
+
argv.slice(1),
|
|
6526
|
+
{ cwd: worktreeDir, timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, windowsHide: true },
|
|
6527
|
+
(err, stdout, stderr) => {
|
|
6528
|
+
const output = boundedTail(`${stdout ?? ""}
|
|
6529
|
+
${stderr ?? ""}`.trim());
|
|
6530
|
+
if (!err) return resolve2({ exitCode: 0, output, timedOut: false });
|
|
6531
|
+
const timedOut = err.killed === true || err.signal === "SIGTERM";
|
|
6532
|
+
const exitCode = typeof err.code === "number" ? err.code : 1;
|
|
6533
|
+
resolve2({ exitCode, output: output || boundedTail(err.message), timedOut });
|
|
6534
|
+
}
|
|
6535
|
+
);
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
async function postFailed2(client, id, message, result) {
|
|
6539
|
+
try {
|
|
6540
|
+
await client.postProgress(id, { status: "failed", message: String(message).slice(0, 1500), result });
|
|
6541
|
+
} catch {
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
function messageExcerpt(output) {
|
|
6545
|
+
const s = String(output ?? "");
|
|
6546
|
+
return s.length > 1200 ? `...${s.slice(-1200)}` : s;
|
|
6547
|
+
}
|
|
6548
|
+
async function enforceCompletionGateOrFail({ client, id, task, worktreeDir, log: log2 = () => {
|
|
6549
|
+
}, execFileImpl = execFile } = {}) {
|
|
6550
|
+
const resolved = resolveCompletionGate(task);
|
|
6551
|
+
if (resolved === null) return false;
|
|
6552
|
+
if (resolved.invalid) {
|
|
6553
|
+
log2(`task ${id}: completion_gate invalid \u2014 ${resolved.reason}`);
|
|
6554
|
+
await postFailed2(client, id, `completion_gate invalid (${resolved.reason}) \u2014 failing closed, not publishing`, "completion_gate_invalid");
|
|
6555
|
+
return true;
|
|
6556
|
+
}
|
|
6557
|
+
const fingerprint = await workspaceFingerprint(worktreeDir, execFileImpl);
|
|
6558
|
+
const cached2 = readState(worktreeDir);
|
|
6559
|
+
if (fingerprint && cached2 && cached2.fingerprint === fingerprint && cached2.exitCode !== 0) {
|
|
6560
|
+
log2(`task ${id}: completion gate skip-on-unchanged (tree ${fingerprint.slice(0, 12)}) \u2014 reusing recorded failure exit ${cached2.exitCode}`);
|
|
6561
|
+
await postFailed2(client, id, `completion gate '${task.completion_gate}' previously failed (exit ${cached2.exitCode}) and the workspace is unchanged:
|
|
6562
|
+
${messageExcerpt(cached2.output)}`, "completion_gate_failed");
|
|
6563
|
+
return true;
|
|
6564
|
+
}
|
|
6565
|
+
log2(`task ${id}: running completion gate: ${resolved.argv.join(" ")}`);
|
|
6566
|
+
const outcome = await runGateCommand({ argv: resolved.argv, worktreeDir, execFileImpl });
|
|
6567
|
+
if (fingerprint) writeState(worktreeDir, { fingerprint, exitCode: outcome.exitCode, output: outcome.output, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6568
|
+
if (outcome.exitCode === 0) {
|
|
6569
|
+
log2(`task ${id}: completion gate PASSED`);
|
|
6570
|
+
return false;
|
|
6571
|
+
}
|
|
6572
|
+
const kind = outcome.timedOut ? `timed out after ${COMPLETION_GATE_TIMEOUT_MS}ms` : `exited ${outcome.exitCode}`;
|
|
6573
|
+
log2(`task ${id}: completion gate FAILED (${kind}) \u2014 not publishing`);
|
|
6574
|
+
await postFailed2(client, id, `completion gate '${task.completion_gate}' ${kind} \u2014 task may not claim completion:
|
|
6575
|
+
${messageExcerpt(outcome.output)}`, "completion_gate_failed");
|
|
6576
|
+
return true;
|
|
6577
|
+
}
|
|
6578
|
+
var COMPLETION_GATE_TIMEOUT_MS, COMPLETION_GATE_OUTPUT_CAP, COMPLETION_GATE_STATE_FILE, SHELL_METACHARACTERS, ALLOWED_GATE_COMMANDS;
|
|
6579
|
+
var init_completion_gate = __esm({
|
|
6580
|
+
"../../scripts/virtual-office/code-runner/completion-gate.mjs"() {
|
|
6581
|
+
"use strict";
|
|
6582
|
+
COMPLETION_GATE_TIMEOUT_MS = 5 * 6e4;
|
|
6583
|
+
COMPLETION_GATE_OUTPUT_CAP = 8 * 1024;
|
|
6584
|
+
COMPLETION_GATE_STATE_FILE = ".vo-completion-gate-state.json";
|
|
6585
|
+
SHELL_METACHARACTERS = /[|&;<>$`(){}[\]*?~#\\'"]/;
|
|
6586
|
+
ALLOWED_GATE_COMMANDS = new Map(
|
|
6587
|
+
["pnpm", "npm", "yarn", "node", "git", "make", "python", "python3", "cargo", "go"].map((name) => [name, name])
|
|
6588
|
+
);
|
|
6589
|
+
}
|
|
6590
|
+
});
|
|
6591
|
+
|
|
5742
6592
|
// ../../scripts/virtual-office/code-runner/process-runner.mjs
|
|
5743
6593
|
import { spawn as spawn3 } from "node:child_process";
|
|
5744
6594
|
function buildStepLabel(cmd, args = []) {
|
|
@@ -6232,8 +7082,8 @@ var init_publish_async = __esm({
|
|
|
6232
7082
|
|
|
6233
7083
|
// ../../scripts/virtual-office/code-runner/skill-catalog.mjs
|
|
6234
7084
|
import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
6235
|
-
import { dirname as
|
|
6236
|
-
import { fileURLToPath as
|
|
7085
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
7086
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
6237
7087
|
function parseFrontmatterNameDescription(raw) {
|
|
6238
7088
|
const text = String(raw).replace(/\r\n/g, "\n");
|
|
6239
7089
|
if (!text.startsWith("---\n")) return null;
|
|
@@ -6252,15 +7102,15 @@ function parseFrontmatterNameDescription(raw) {
|
|
|
6252
7102
|
return name && description ? { name, description } : null;
|
|
6253
7103
|
}
|
|
6254
7104
|
function resolveDefaultRepoRoot() {
|
|
6255
|
-
const starts = [
|
|
7105
|
+
const starts = [dirname3(fileURLToPath4(import.meta.url)), process.cwd()];
|
|
6256
7106
|
for (const start of starts) {
|
|
6257
7107
|
let dir = start;
|
|
6258
7108
|
for (let i = 0; i < 8; i += 1) {
|
|
6259
7109
|
try {
|
|
6260
|
-
if (statSync(
|
|
7110
|
+
if (statSync(join4(dir, ".claude", "skills")).isDirectory()) return dir;
|
|
6261
7111
|
} catch {
|
|
6262
7112
|
}
|
|
6263
|
-
const parent =
|
|
7113
|
+
const parent = dirname3(dir);
|
|
6264
7114
|
if (parent === dir) break;
|
|
6265
7115
|
dir = parent;
|
|
6266
7116
|
}
|
|
@@ -6269,14 +7119,14 @@ function resolveDefaultRepoRoot() {
|
|
|
6269
7119
|
}
|
|
6270
7120
|
function loadSkillCatalog({ repoRoot: repoRoot2 = resolveDefaultRepoRoot() } = {}) {
|
|
6271
7121
|
try {
|
|
6272
|
-
const skillsDir =
|
|
7122
|
+
const skillsDir = join4(repoRoot2, ".claude", "skills");
|
|
6273
7123
|
const catalog = [];
|
|
6274
7124
|
for (const entry of readdirSync2(skillsDir)) {
|
|
6275
|
-
const dir =
|
|
7125
|
+
const dir = join4(skillsDir, entry);
|
|
6276
7126
|
try {
|
|
6277
7127
|
if (!statSync(dir).isDirectory()) continue;
|
|
6278
7128
|
const parsed = parseFrontmatterNameDescription(
|
|
6279
|
-
readFileSync3(
|
|
7129
|
+
readFileSync3(join4(dir, "SKILL.md"), "utf8")
|
|
6280
7130
|
);
|
|
6281
7131
|
if (parsed) catalog.push(parsed);
|
|
6282
7132
|
} catch {
|
|
@@ -6505,10 +7355,10 @@ var init_task_prompt = __esm({
|
|
|
6505
7355
|
});
|
|
6506
7356
|
|
|
6507
7357
|
// ../../scripts/virtual-office/code-runner/task-attachments.mjs
|
|
6508
|
-
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
7358
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
6509
7359
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
6510
7360
|
import os2 from "node:os";
|
|
6511
|
-
import
|
|
7361
|
+
import path17 from "node:path";
|
|
6512
7362
|
function safeTaskToken(taskId) {
|
|
6513
7363
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
6514
7364
|
}
|
|
@@ -6518,25 +7368,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
|
|
|
6518
7368
|
return `${String(index + 1).padStart(2, "0")}-${normalized}`;
|
|
6519
7369
|
}
|
|
6520
7370
|
function assertGeneratedDirectory(directory, tempRoot) {
|
|
6521
|
-
const resolvedDirectory =
|
|
6522
|
-
const resolvedRoot =
|
|
6523
|
-
if (
|
|
7371
|
+
const resolvedDirectory = path17.resolve(directory);
|
|
7372
|
+
const resolvedRoot = path17.resolve(tempRoot);
|
|
7373
|
+
if (path17.dirname(resolvedDirectory) !== resolvedRoot || !path17.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
|
|
6524
7374
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
6525
7375
|
}
|
|
6526
7376
|
return resolvedDirectory;
|
|
6527
7377
|
}
|
|
6528
7378
|
async function createAttachmentDirectory(taskId, tempRoot) {
|
|
6529
|
-
const root =
|
|
7379
|
+
const root = path17.resolve(tempRoot);
|
|
6530
7380
|
await mkdir(root, { recursive: true });
|
|
6531
|
-
const directory = await mkdtemp(
|
|
6532
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token:
|
|
6533
|
-
await writeFile(
|
|
7381
|
+
const directory = await mkdtemp(path17.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
7382
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path17.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7383
|
+
await writeFile(path17.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
6534
7384
|
return { directory, marker, tempRoot: root };
|
|
6535
7385
|
}
|
|
6536
7386
|
async function cleanupGeneratedDirectory(state) {
|
|
6537
7387
|
if (!state || state.cleaned) return;
|
|
6538
7388
|
const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
|
|
6539
|
-
const marker = await readFile(
|
|
7389
|
+
const marker = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
6540
7390
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
6541
7391
|
await rm(directory, { recursive: true, force: true });
|
|
6542
7392
|
state.cleaned = true;
|
|
@@ -6555,7 +7405,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6555
7405
|
now = Date.now(),
|
|
6556
7406
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
6557
7407
|
} = {}) {
|
|
6558
|
-
const root =
|
|
7408
|
+
const root = path17.resolve(tempRoot);
|
|
6559
7409
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
6560
7410
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
6561
7411
|
if (error?.code === "ENOENT") return [];
|
|
@@ -6564,8 +7414,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
6564
7414
|
let removed = 0;
|
|
6565
7415
|
for (const entry of entries) {
|
|
6566
7416
|
if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
|
|
6567
|
-
const directory = assertGeneratedDirectory(
|
|
6568
|
-
const markerRaw = await readFile(
|
|
7417
|
+
const directory = assertGeneratedDirectory(path17.join(root, entry.name), root);
|
|
7418
|
+
const markerRaw = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
6569
7419
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
6570
7420
|
if (!marker) continue;
|
|
6571
7421
|
const directoryStat = await stat(directory);
|
|
@@ -6608,10 +7458,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
6608
7458
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
6609
7459
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
6610
7460
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
6611
|
-
const filePath =
|
|
7461
|
+
const filePath = path17.join(state.directory, name);
|
|
6612
7462
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
6613
7463
|
await chmod(filePath, 384);
|
|
6614
|
-
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path:
|
|
7464
|
+
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path17.resolve(filePath) });
|
|
6615
7465
|
}
|
|
6616
7466
|
return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
|
|
6617
7467
|
} catch (error) {
|
|
@@ -6633,8 +7483,8 @@ var init_task_attachments = __esm({
|
|
|
6633
7483
|
});
|
|
6634
7484
|
|
|
6635
7485
|
// ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
|
|
6636
|
-
import { homedir as
|
|
6637
|
-
import { join as
|
|
7486
|
+
import { homedir as homedir4 } from "node:os";
|
|
7487
|
+
import { join as join5 } from "node:path";
|
|
6638
7488
|
import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
6639
7489
|
import { createHash as createHash4 } from "node:crypto";
|
|
6640
7490
|
function deriveUuid(seed) {
|
|
@@ -6666,18 +7516,18 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
6666
7516
|
for (const f of files) {
|
|
6667
7517
|
if (!f.endsWith(".json")) continue;
|
|
6668
7518
|
try {
|
|
6669
|
-
const record = JSON.parse(await readFile2(
|
|
7519
|
+
const record = JSON.parse(await readFile2(join5(spoolDir, f), "utf8"));
|
|
6670
7520
|
if (record && typeof record.session_key === "string") {
|
|
6671
|
-
out.push({ full:
|
|
7521
|
+
out.push({ full: join5(spoolDir, f), record });
|
|
6672
7522
|
}
|
|
6673
7523
|
} catch {
|
|
6674
7524
|
}
|
|
6675
7525
|
}
|
|
6676
7526
|
return out;
|
|
6677
7527
|
}
|
|
6678
|
-
async function readCloudMap(
|
|
7528
|
+
async function readCloudMap(path23) {
|
|
6679
7529
|
try {
|
|
6680
|
-
return JSON.parse(await readFile2(
|
|
7530
|
+
return JSON.parse(await readFile2(path23, "utf8"));
|
|
6681
7531
|
} catch {
|
|
6682
7532
|
return {};
|
|
6683
7533
|
}
|
|
@@ -6750,8 +7600,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
|
|
|
6750
7600
|
var init_session_spool_forwarder = __esm({
|
|
6751
7601
|
"../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
|
|
6752
7602
|
"use strict";
|
|
6753
|
-
SPOOL_DIR =
|
|
6754
|
-
CLOUD_MAP_FILE =
|
|
7603
|
+
SPOOL_DIR = join5(homedir4(), ".vo", "session-spool");
|
|
7604
|
+
CLOUD_MAP_FILE = join5(homedir4(), ".vo", "session-cloud-map.json");
|
|
6755
7605
|
STALE_MS = 60 * 60 * 1e3;
|
|
6756
7606
|
ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
|
|
6757
7607
|
}
|
|
@@ -6822,7 +7672,7 @@ var init_rate_limit_resume_scheduler_core = __esm({
|
|
|
6822
7672
|
});
|
|
6823
7673
|
|
|
6824
7674
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-scheduler.mjs
|
|
6825
|
-
import { dirname as
|
|
7675
|
+
import { dirname as dirname4, join as join6, resolve } from "node:path";
|
|
6826
7676
|
function defaultLog(message) {
|
|
6827
7677
|
console.log(`[rate-limit-scheduler ${(/* @__PURE__ */ new Date()).toISOString()}] ${message}`);
|
|
6828
7678
|
}
|
|
@@ -6904,7 +7754,7 @@ async function runLockedScheduler({
|
|
|
6904
7754
|
async function runScheduler({
|
|
6905
7755
|
env: env2 = process.env,
|
|
6906
7756
|
queuePath = resumeQueuePath(),
|
|
6907
|
-
attemptsPath =
|
|
7757
|
+
attemptsPath = join6(dirname4(queuePath), "resume-attempts.json"),
|
|
6908
7758
|
client,
|
|
6909
7759
|
now,
|
|
6910
7760
|
log: log2 = defaultLog
|
|
@@ -7196,7 +8046,7 @@ var init_runner_capacity = __esm({
|
|
|
7196
8046
|
});
|
|
7197
8047
|
|
|
7198
8048
|
// ../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs
|
|
7199
|
-
import { fileURLToPath as
|
|
8049
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
7200
8050
|
async function probeAgentInChild(agent, timeoutMs) {
|
|
7201
8051
|
const stdout = await runProcess2(process.execPath, [probeCli, agent], {
|
|
7202
8052
|
timeout: timeoutMs,
|
|
@@ -7209,7 +8059,7 @@ var init_agent_auth_probe_process = __esm({
|
|
|
7209
8059
|
"../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs"() {
|
|
7210
8060
|
"use strict";
|
|
7211
8061
|
init_process_runner2();
|
|
7212
|
-
probeCli =
|
|
8062
|
+
probeCli = fileURLToPath5(new URL("./agent-auth-probe-cli.mjs", import.meta.url));
|
|
7213
8063
|
}
|
|
7214
8064
|
});
|
|
7215
8065
|
|
|
@@ -7434,7 +8284,7 @@ var init_local_model_remote_config = __esm({
|
|
|
7434
8284
|
|
|
7435
8285
|
// ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
|
|
7436
8286
|
import crypto from "node:crypto";
|
|
7437
|
-
import
|
|
8287
|
+
import fs9 from "node:fs";
|
|
7438
8288
|
function accountKey(agent, rawId) {
|
|
7439
8289
|
const id = typeof rawId === "string" ? rawId.trim() : "";
|
|
7440
8290
|
if (!id) return null;
|
|
@@ -7498,7 +8348,7 @@ var init_shared = __esm({
|
|
|
7498
8348
|
};
|
|
7499
8349
|
readJson = (p) => {
|
|
7500
8350
|
try {
|
|
7501
|
-
return JSON.parse(
|
|
8351
|
+
return JSON.parse(fs9.readFileSync(p, "utf8"));
|
|
7502
8352
|
} catch {
|
|
7503
8353
|
return null;
|
|
7504
8354
|
}
|
|
@@ -7508,9 +8358,9 @@ var init_shared = __esm({
|
|
|
7508
8358
|
});
|
|
7509
8359
|
|
|
7510
8360
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
7511
|
-
import
|
|
8361
|
+
import fs10 from "node:fs";
|
|
7512
8362
|
import os3 from "node:os";
|
|
7513
|
-
import
|
|
8363
|
+
import path18 from "node:path";
|
|
7514
8364
|
function fileCaptureTime(filePath, explicit, statFn) {
|
|
7515
8365
|
if (typeof explicit === "string" && explicit) return explicit;
|
|
7516
8366
|
try {
|
|
@@ -7524,7 +8374,7 @@ function usageBaseUrl(env2 = process.env) {
|
|
|
7524
8374
|
return String(raw).replace(/\/+$/, "");
|
|
7525
8375
|
}
|
|
7526
8376
|
function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
|
|
7527
|
-
const creds = read(
|
|
8377
|
+
const creds = read(path18.join(homeDir, ".claude", ".credentials.json"));
|
|
7528
8378
|
const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
|
|
7529
8379
|
if (!oauth || typeof oauth !== "object") return null;
|
|
7530
8380
|
const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
|
|
@@ -7534,7 +8384,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
|
|
|
7534
8384
|
return token2;
|
|
7535
8385
|
}
|
|
7536
8386
|
function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
|
|
7537
|
-
const cfg = read(
|
|
8387
|
+
const cfg = read(path18.join(homeDir, ".claude.json"));
|
|
7538
8388
|
const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
|
|
7539
8389
|
return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
|
|
7540
8390
|
}
|
|
@@ -7627,7 +8477,7 @@ async function readClaudeOAuthUsage({
|
|
|
7627
8477
|
function readClaudeFileUsage({
|
|
7628
8478
|
homeDir = os3.homedir(),
|
|
7629
8479
|
read: rawRead = readJson,
|
|
7630
|
-
statFn =
|
|
8480
|
+
statFn = fs10.statSync,
|
|
7631
8481
|
now = () => Date.now()
|
|
7632
8482
|
} = {}) {
|
|
7633
8483
|
const read = (p) => {
|
|
@@ -7644,7 +8494,7 @@ function readClaudeFileUsage({
|
|
|
7644
8494
|
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
7645
8495
|
return row;
|
|
7646
8496
|
};
|
|
7647
|
-
const statusPath =
|
|
8497
|
+
const statusPath = path18.join(homeDir, ".claude", "claude-usage.json");
|
|
7648
8498
|
const status = read(statusPath);
|
|
7649
8499
|
if (status && (status.seven_day || status.five_hour)) {
|
|
7650
8500
|
const row = fresh(makeUsageRow({
|
|
@@ -7659,7 +8509,7 @@ function readClaudeFileUsage({
|
|
|
7659
8509
|
}));
|
|
7660
8510
|
if (row) return row;
|
|
7661
8511
|
}
|
|
7662
|
-
const weeklyPath =
|
|
8512
|
+
const weeklyPath = path18.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
7663
8513
|
const weekly = read(weeklyPath);
|
|
7664
8514
|
if (weekly) {
|
|
7665
8515
|
const row = fresh(makeUsageRow({
|
|
@@ -8098,9 +8948,9 @@ var init_watcher_coordination = __esm({
|
|
|
8098
8948
|
});
|
|
8099
8949
|
|
|
8100
8950
|
// ../../scripts/virtual-office/code-runner/watcher-state.mjs
|
|
8101
|
-
import { randomUUID as
|
|
8951
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
8102
8952
|
import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
|
|
8103
|
-
import { dirname as
|
|
8953
|
+
import { dirname as dirname5 } from "node:path";
|
|
8104
8954
|
async function readWatcherState(stateFile) {
|
|
8105
8955
|
let raw;
|
|
8106
8956
|
try {
|
|
@@ -8116,9 +8966,9 @@ async function readWatcherState(stateFile) {
|
|
|
8116
8966
|
return parsed;
|
|
8117
8967
|
}
|
|
8118
8968
|
async function writeWatcherState(stateFile, state) {
|
|
8119
|
-
const directory =
|
|
8969
|
+
const directory = dirname5(stateFile);
|
|
8120
8970
|
await mkdir2(directory, { recursive: true });
|
|
8121
|
-
const temp = `${stateFile}.${process.pid}.${
|
|
8971
|
+
const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
|
|
8122
8972
|
let handle;
|
|
8123
8973
|
try {
|
|
8124
8974
|
handle = await open(temp, "wx");
|
|
@@ -8329,7 +9179,7 @@ var init_pr_watcher_github = __esm({
|
|
|
8329
9179
|
});
|
|
8330
9180
|
|
|
8331
9181
|
// ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
|
|
8332
|
-
import { randomUUID as
|
|
9182
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8333
9183
|
async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
8334
9184
|
}) {
|
|
8335
9185
|
const requestedBudgetUsd = task?.max_budget_usd;
|
|
@@ -8343,7 +9193,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
|
8343
9193
|
if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
|
|
8344
9194
|
throw new Error("autonomous dispatch admission client unavailable");
|
|
8345
9195
|
}
|
|
8346
|
-
const reservationId =
|
|
9196
|
+
const reservationId = randomUUID4();
|
|
8347
9197
|
const admission = await client.reserveAutonomousDispatchBudget({
|
|
8348
9198
|
requestedBudgetUsd,
|
|
8349
9199
|
reservationId,
|
|
@@ -8361,8 +9211,8 @@ var init_enqueue_autonomous_code_task = __esm({
|
|
|
8361
9211
|
});
|
|
8362
9212
|
|
|
8363
9213
|
// ../../scripts/virtual-office/code-runner/pr-watcher.mjs
|
|
8364
|
-
import { homedir as
|
|
8365
|
-
import { join as
|
|
9214
|
+
import { homedir as homedir5 } from "node:os";
|
|
9215
|
+
import { join as join7 } from "node:path";
|
|
8366
9216
|
function parsePrCiStatus(view) {
|
|
8367
9217
|
const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
|
|
8368
9218
|
const rollup = view && Array.isArray(view.statusCheckRollup) ? view.statusCheckRollup : [];
|
|
@@ -8705,7 +9555,7 @@ var init_pr_watcher = __esm({
|
|
|
8705
9555
|
init_watcher_state();
|
|
8706
9556
|
init_superseded_pr_source();
|
|
8707
9557
|
init_ci_fix_prompt();
|
|
8708
|
-
DEFAULT_STATE_FILE =
|
|
9558
|
+
DEFAULT_STATE_FILE = join7(homedir5(), ".vo", "dispatched-prs.json");
|
|
8709
9559
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
8710
9560
|
"FAILURE",
|
|
8711
9561
|
"TIMED_OUT",
|
|
@@ -8935,9 +9785,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8935
9785
|
res.end();
|
|
8936
9786
|
return;
|
|
8937
9787
|
}
|
|
8938
|
-
const
|
|
9788
|
+
const path23 = String(req.url || "").split("?")[0];
|
|
8939
9789
|
res.setHeader("content-type", "application/json");
|
|
8940
|
-
if (req.method === "GET" &&
|
|
9790
|
+
if (req.method === "GET" && path23 === "/status") {
|
|
8941
9791
|
let status;
|
|
8942
9792
|
try {
|
|
8943
9793
|
status = getStatus();
|
|
@@ -8948,7 +9798,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
8948
9798
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
8949
9799
|
return;
|
|
8950
9800
|
}
|
|
8951
|
-
if (req.method === "POST" &&
|
|
9801
|
+
if (req.method === "POST" && path23 === "/stop") {
|
|
8952
9802
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
8953
9803
|
res.statusCode = 403;
|
|
8954
9804
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -9111,18 +9961,35 @@ var init_effort_mode_config = __esm({
|
|
|
9111
9961
|
});
|
|
9112
9962
|
|
|
9113
9963
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
9114
|
-
import
|
|
9115
|
-
import
|
|
9116
|
-
import
|
|
9964
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
9965
|
+
import fs11 from "node:fs";
|
|
9966
|
+
import os4 from "node:os";
|
|
9967
|
+
import path19 from "node:path";
|
|
9968
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
9969
|
+
function userCacheRoot() {
|
|
9970
|
+
try {
|
|
9971
|
+
const home = os4.homedir();
|
|
9972
|
+
if (home) return path19.join(home, ".claude");
|
|
9973
|
+
} catch {
|
|
9974
|
+
}
|
|
9975
|
+
return path19.join(os4.tmpdir(), `vo-model-registry-${randomUUID5()}`);
|
|
9976
|
+
}
|
|
9977
|
+
function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
|
|
9978
|
+
if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
|
|
9979
|
+
if (env2.VO_RUNNER_RUNTIME_ROOT) return env2.VO_RUNNER_RUNTIME_ROOT;
|
|
9980
|
+
const segments = moduleDir.split(path19.sep);
|
|
9981
|
+
const isRepoCheckout = segments.at(-1) === "virtual-office" && segments.at(-2) === "scripts";
|
|
9982
|
+
return isRepoCheckout ? path19.resolve(moduleDir, "..", "..") : userCacheRoot();
|
|
9983
|
+
}
|
|
9117
9984
|
function uniqueModels(models = []) {
|
|
9118
9985
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
9119
9986
|
}
|
|
9120
9987
|
function normalizeProvider(value = "") {
|
|
9121
|
-
const
|
|
9122
|
-
if (
|
|
9123
|
-
if (
|
|
9124
|
-
if (
|
|
9125
|
-
return
|
|
9988
|
+
const lower2 = String(value || "").trim().toLowerCase();
|
|
9989
|
+
if (lower2.includes("anthropic")) return "anthropic";
|
|
9990
|
+
if (lower2.includes("openai")) return "openai";
|
|
9991
|
+
if (lower2.includes("google") || lower2.includes("gemini")) return "google";
|
|
9992
|
+
return lower2;
|
|
9126
9993
|
}
|
|
9127
9994
|
function stripProviderPrefix(id = "") {
|
|
9128
9995
|
const raw = String(id || "").trim();
|
|
@@ -9164,15 +10031,15 @@ function normalizeCatalogModel(model = {}) {
|
|
|
9164
10031
|
};
|
|
9165
10032
|
}
|
|
9166
10033
|
function parseVersionScore(id = "") {
|
|
9167
|
-
const
|
|
9168
|
-
const numbers = [...
|
|
10034
|
+
const lower2 = String(id || "").toLowerCase();
|
|
10035
|
+
const numbers = [...lower2.matchAll(/\d+/g)].map((match) => Number(match[0])).filter(Number.isFinite);
|
|
9169
10036
|
let score = 0;
|
|
9170
10037
|
for (let i = 0; i < numbers.length; i++) score += numbers[i] / Math.pow(1e3, i);
|
|
9171
|
-
if (/opus|pro|flagship/.test(
|
|
9172
|
-
if (/sonnet/.test(
|
|
9173
|
-
if (/preview|latest/.test(
|
|
9174
|
-
if (/\[1m\]|\(1m\)/i.test(
|
|
9175
|
-
if (/mini|nano|haiku|lite/.test(
|
|
10038
|
+
if (/opus|pro|flagship/.test(lower2)) score += 10;
|
|
10039
|
+
if (/sonnet/.test(lower2)) score += 5;
|
|
10040
|
+
if (/preview|latest/.test(lower2)) score += 0.25;
|
|
10041
|
+
if (/\[1m\]|\(1m\)/i.test(lower2)) score += 1;
|
|
10042
|
+
if (/mini|nano|haiku|lite/.test(lower2)) score -= 20;
|
|
9176
10043
|
return score;
|
|
9177
10044
|
}
|
|
9178
10045
|
function familyMatches(model, family) {
|
|
@@ -9224,9 +10091,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
|
|
|
9224
10091
|
return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
|
|
9225
10092
|
}
|
|
9226
10093
|
function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
|
|
9227
|
-
if (!
|
|
10094
|
+
if (!fs11.existsSync(cacheFile)) return null;
|
|
9228
10095
|
try {
|
|
9229
|
-
const parsed = JSON.parse(
|
|
10096
|
+
const parsed = JSON.parse(fs11.readFileSync(cacheFile, "utf-8"));
|
|
9230
10097
|
if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
|
|
9231
10098
|
if (!Array.isArray(parsed.models)) return null;
|
|
9232
10099
|
return parsed;
|
|
@@ -9235,8 +10102,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
9235
10102
|
}
|
|
9236
10103
|
}
|
|
9237
10104
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
9238
|
-
|
|
9239
|
-
|
|
10105
|
+
fs11.mkdirSync(path19.dirname(cacheFile), { recursive: true });
|
|
10106
|
+
fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
9240
10107
|
}
|
|
9241
10108
|
async function fetchRegistryCatalog({
|
|
9242
10109
|
fetchImpl = fetch,
|
|
@@ -9289,14 +10156,17 @@ async function resolveModelFamily(family, options = {}) {
|
|
|
9289
10156
|
const resolved = selectBestFamilyModel(catalog.models || [], family);
|
|
9290
10157
|
return resolved || def.fallbacks[0];
|
|
9291
10158
|
}
|
|
9292
|
-
var __dirname,
|
|
10159
|
+
var __dirname, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC_API_VERSION, FAMILY_DEFINITIONS, memoryCache;
|
|
9293
10160
|
var init_model_registry = __esm({
|
|
9294
10161
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
9295
10162
|
"use strict";
|
|
9296
|
-
__dirname =
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
10163
|
+
__dirname = path19.dirname(fileURLToPath6(import.meta.url));
|
|
10164
|
+
DEFAULT_CACHE_DIR = path19.join(
|
|
10165
|
+
resolveCacheBaseDir(),
|
|
10166
|
+
".virtual-office-cache",
|
|
10167
|
+
"model-registry"
|
|
10168
|
+
);
|
|
10169
|
+
DEFAULT_CACHE_FILE = path19.join(DEFAULT_CACHE_DIR, "catalog.json");
|
|
9300
10170
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
9301
10171
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
9302
10172
|
FAMILY_DEFINITIONS = {
|
|
@@ -9401,17 +10271,17 @@ function modelCompatibleWithAgent(agent, model) {
|
|
|
9401
10271
|
if (!model) return true;
|
|
9402
10272
|
return AGENT_MODEL_COMPATIBILITY[agent]?.(model) ?? false;
|
|
9403
10273
|
}
|
|
9404
|
-
function
|
|
10274
|
+
function classifyTier2(prompt) {
|
|
9405
10275
|
const text = String(prompt || "").trim();
|
|
9406
10276
|
if (!text) return "mid";
|
|
9407
|
-
const
|
|
10277
|
+
const lower2 = text.toLowerCase();
|
|
9408
10278
|
if (/lint|format|typo|missing import|update deps|chore|maintenance|runner|daemon/.test(
|
|
9409
|
-
|
|
10279
|
+
lower2
|
|
9410
10280
|
)) {
|
|
9411
10281
|
return "cheap";
|
|
9412
10282
|
}
|
|
9413
10283
|
if (/generate roadmap|new feature|implement .* feature|major refactor|strategic/.test(
|
|
9414
|
-
|
|
10284
|
+
lower2
|
|
9415
10285
|
) || text.length > 1500) {
|
|
9416
10286
|
return "best";
|
|
9417
10287
|
}
|
|
@@ -9432,7 +10302,7 @@ async function resolveModelForTier(tier, { agent = DEFAULT_AGENT2, resolveModelF
|
|
|
9432
10302
|
return fallbacks[effectiveTier];
|
|
9433
10303
|
}
|
|
9434
10304
|
async function resolveTaskModel(task, { agent = DEFAULT_AGENT2 } = {}) {
|
|
9435
|
-
const tier = task.tier && task.tier !== "auto" ? task.tier :
|
|
10305
|
+
const tier = task.tier && task.tier !== "auto" ? task.tier : classifyTier2(task.prompt);
|
|
9436
10306
|
const pinned = typeof task.model === "string" ? task.model.trim() : "";
|
|
9437
10307
|
if (pinned && modelCompatibleWithAgent(normalizeAgent(agent), pinned)) {
|
|
9438
10308
|
return { tier, model: pinned };
|
|
@@ -9904,8 +10774,8 @@ var init_classify_task = __esm({
|
|
|
9904
10774
|
|
|
9905
10775
|
// ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
|
|
9906
10776
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
9907
|
-
import { homedir as
|
|
9908
|
-
import { join as
|
|
10777
|
+
import { homedir as homedir6 } from "node:os";
|
|
10778
|
+
import { join as join8 } from "node:path";
|
|
9909
10779
|
function difficultyToRung(difficulty, thresholds) {
|
|
9910
10780
|
const b = thresholds.rungBounds;
|
|
9911
10781
|
if (difficulty >= b.R5) return "R5";
|
|
@@ -9930,9 +10800,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
9930
10800
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
9931
10801
|
return base;
|
|
9932
10802
|
}
|
|
9933
|
-
function readCodexModelsCache({ path:
|
|
10803
|
+
function readCodexModelsCache({ path: path23 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
|
|
9934
10804
|
try {
|
|
9935
|
-
const parsed = JSON.parse(read(
|
|
10805
|
+
const parsed = JSON.parse(read(path23, "utf8"));
|
|
9936
10806
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
9937
10807
|
} catch {
|
|
9938
10808
|
return null;
|
|
@@ -9982,7 +10852,7 @@ var init_effort_policy = __esm({
|
|
|
9982
10852
|
init_meta_model_catalog();
|
|
9983
10853
|
RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
|
|
9984
10854
|
rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
|
|
9985
|
-
DEFAULT_CODEX_MODELS_CACHE =
|
|
10855
|
+
DEFAULT_CODEX_MODELS_CACHE = join8(homedir6(), ".codex", "models_cache.json");
|
|
9986
10856
|
}
|
|
9987
10857
|
});
|
|
9988
10858
|
|
|
@@ -10112,18 +10982,18 @@ var init_role_cost_shadow = __esm({
|
|
|
10112
10982
|
});
|
|
10113
10983
|
|
|
10114
10984
|
// ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
|
|
10115
|
-
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as
|
|
10116
|
-
import { homedir as
|
|
10117
|
-
import { join as
|
|
10118
|
-
import { fileURLToPath as
|
|
10985
|
+
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
10986
|
+
import { homedir as homedir7 } from "node:os";
|
|
10987
|
+
import { join as join9, dirname as dirname6 } from "node:path";
|
|
10988
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
10119
10989
|
function getAutoRouterMode(env2 = process.env) {
|
|
10120
10990
|
const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
|
|
10121
10991
|
return MODES.has(raw) ? raw : "off";
|
|
10122
10992
|
}
|
|
10123
10993
|
function loadThresholds() {
|
|
10124
10994
|
if (!cachedThresholds) {
|
|
10125
|
-
const here =
|
|
10126
|
-
cachedThresholds = JSON.parse(readFileSync5(
|
|
10995
|
+
const here = dirname6(fileURLToPath7(import.meta.url));
|
|
10996
|
+
cachedThresholds = JSON.parse(readFileSync5(join9(here, "thresholds.json"), "utf8"));
|
|
10127
10997
|
}
|
|
10128
10998
|
return cachedThresholds;
|
|
10129
10999
|
}
|
|
@@ -10189,15 +11059,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
10189
11059
|
const s = `[${decision.routerVersion}] ${decision.taskClass} d=${decision.difficulty} c=${decision.confidence} \u2192 ${decision.rung}/${decision.tier}${decision.effort ? ` effort=${decision.effort}` : ""} turns=${decision.maxTurns} $${decision.maxBudgetUsd}${decision.flags.length ? ` [${decision.flags.join(",")}]` : ""} :: ${decision.reasons.join("; ")}`;
|
|
10190
11060
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
10191
11061
|
}
|
|
10192
|
-
function appendDecisionFallback(decision, { path:
|
|
11062
|
+
function appendDecisionFallback(decision, { path: path23 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
|
|
10193
11063
|
try {
|
|
10194
|
-
mkdir4(
|
|
10195
|
-
append(
|
|
11064
|
+
mkdir4(dirname6(path23), { recursive: true });
|
|
11065
|
+
append(path23, `${JSON.stringify(decision)}
|
|
10196
11066
|
`, "utf8");
|
|
10197
11067
|
if (isRouterDecision(decision)) {
|
|
10198
11068
|
try {
|
|
10199
11069
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
10200
|
-
for (const record of records) append(
|
|
11070
|
+
for (const record of records) append(path23, `${JSON.stringify(record)}
|
|
10201
11071
|
`, "utf8");
|
|
10202
11072
|
} catch {
|
|
10203
11073
|
}
|
|
@@ -10215,7 +11085,7 @@ var init_auto_router = __esm({
|
|
|
10215
11085
|
init_effort_policy();
|
|
10216
11086
|
init_role_cost_shadow();
|
|
10217
11087
|
ROUTER_VERSION = "0.1.0";
|
|
10218
|
-
DECISION_FALLBACK_PATH =
|
|
11088
|
+
DECISION_FALLBACK_PATH = join9(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
|
|
10219
11089
|
MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
10220
11090
|
cachedThresholds = null;
|
|
10221
11091
|
isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
|
|
@@ -11188,9 +12058,9 @@ var init_inference_task_runner = __esm({
|
|
|
11188
12058
|
});
|
|
11189
12059
|
|
|
11190
12060
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
11191
|
-
import
|
|
12061
|
+
import fs12 from "node:fs";
|
|
11192
12062
|
import fsp11 from "node:fs/promises";
|
|
11193
|
-
import
|
|
12063
|
+
import path20 from "node:path";
|
|
11194
12064
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
11195
12065
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
11196
12066
|
}
|
|
@@ -11203,7 +12073,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
11203
12073
|
"--path-format=absolute",
|
|
11204
12074
|
"--git-common-dir"
|
|
11205
12075
|
])).trim();
|
|
11206
|
-
const root =
|
|
12076
|
+
const root = path20.dirname(commonDir);
|
|
11207
12077
|
return samePath2(root, worktreeDir) ? null : root;
|
|
11208
12078
|
}
|
|
11209
12079
|
async function snapshot(root, run) {
|
|
@@ -11245,21 +12115,21 @@ async function changedPaths(root, run) {
|
|
|
11245
12115
|
}
|
|
11246
12116
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
11247
12117
|
const paths = await changedPaths(baseline.root, run);
|
|
11248
|
-
const quarantineDir =
|
|
11249
|
-
|
|
12118
|
+
const quarantineDir = path20.join(
|
|
12119
|
+
path20.dirname(worktreeDir),
|
|
11250
12120
|
".canonical-recovery",
|
|
11251
12121
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
11252
12122
|
);
|
|
11253
12123
|
await fsp11.mkdir(quarantineDir, { recursive: true });
|
|
11254
12124
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
11255
|
-
await fsp11.writeFile(
|
|
12125
|
+
await fsp11.writeFile(path20.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
11256
12126
|
for (const relative of paths.untracked) {
|
|
11257
|
-
const source =
|
|
11258
|
-
const target =
|
|
11259
|
-
await fsp11.mkdir(
|
|
12127
|
+
const source = path20.join(baseline.root, relative);
|
|
12128
|
+
const target = path20.join(quarantineDir, "untracked", relative);
|
|
12129
|
+
await fsp11.mkdir(path20.dirname(target), { recursive: true });
|
|
11260
12130
|
await fsp11.copyFile(source, target);
|
|
11261
12131
|
}
|
|
11262
|
-
await fsp11.writeFile(
|
|
12132
|
+
await fsp11.writeFile(path20.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
11263
12133
|
taskId,
|
|
11264
12134
|
canonicalRoot: baseline.root,
|
|
11265
12135
|
canonicalHead: baseline.head,
|
|
@@ -11281,9 +12151,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
11281
12151
|
]);
|
|
11282
12152
|
}
|
|
11283
12153
|
for (const relative of evidence.untracked) {
|
|
11284
|
-
const target =
|
|
11285
|
-
const prefix = `${
|
|
11286
|
-
if (!target.startsWith(prefix) || !
|
|
12154
|
+
const target = path20.resolve(baseline.root, relative);
|
|
12155
|
+
const prefix = `${path20.resolve(baseline.root)}${path20.sep}`;
|
|
12156
|
+
if (!target.startsWith(prefix) || !fs12.existsSync(target)) continue;
|
|
11287
12157
|
await fsp11.rm(target, { force: true });
|
|
11288
12158
|
}
|
|
11289
12159
|
}
|
|
@@ -11319,7 +12189,7 @@ var init_isolation_audit = __esm({
|
|
|
11319
12189
|
init_process_runner2();
|
|
11320
12190
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
11321
12191
|
samePath2 = (left, right) => {
|
|
11322
|
-
const [a, b] = [left, right].map((value) =>
|
|
12192
|
+
const [a, b] = [left, right].map((value) => path20.resolve(value));
|
|
11323
12193
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
11324
12194
|
};
|
|
11325
12195
|
}
|
|
@@ -11679,7 +12549,7 @@ var init_publication_outcome = __esm({
|
|
|
11679
12549
|
|
|
11680
12550
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
11681
12551
|
import fsp12 from "node:fs/promises";
|
|
11682
|
-
import
|
|
12552
|
+
import path21 from "node:path";
|
|
11683
12553
|
function defaultRun2(command, args, cwd, options = {}) {
|
|
11684
12554
|
return runProcess2(command, args, { cwd, ...options });
|
|
11685
12555
|
}
|
|
@@ -11687,13 +12557,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
11687
12557
|
if (!isAgentScratch(file)) {
|
|
11688
12558
|
throw new Error(`refusing to remove non-scratch publication path: ${file}`);
|
|
11689
12559
|
}
|
|
11690
|
-
const root =
|
|
11691
|
-
const target =
|
|
11692
|
-
const relative =
|
|
11693
|
-
if (!relative || relative.startsWith(`..${
|
|
12560
|
+
const root = path21.resolve(worktreeDir);
|
|
12561
|
+
const target = path21.resolve(root, file);
|
|
12562
|
+
const relative = path21.relative(root, target);
|
|
12563
|
+
if (!relative || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
|
|
11694
12564
|
throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
|
|
11695
12565
|
}
|
|
11696
|
-
for (let cursor = target; cursor !== root; cursor =
|
|
12566
|
+
for (let cursor = target; cursor !== root; cursor = path21.dirname(cursor)) {
|
|
11697
12567
|
try {
|
|
11698
12568
|
if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
|
|
11699
12569
|
throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
|
|
@@ -11813,9 +12683,9 @@ var init_publication_scope = __esm({
|
|
|
11813
12683
|
});
|
|
11814
12684
|
|
|
11815
12685
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
11816
|
-
import
|
|
12686
|
+
import fs13 from "node:fs";
|
|
11817
12687
|
import fsp13 from "node:fs/promises";
|
|
11818
|
-
import
|
|
12688
|
+
import path22 from "node:path";
|
|
11819
12689
|
function recoveryTaskId(prompt) {
|
|
11820
12690
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
11821
12691
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -11829,10 +12699,10 @@ function cloneLeaf(repo) {
|
|
|
11829
12699
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
11830
12700
|
const leaf = cloneLeaf(repo);
|
|
11831
12701
|
if (!leaf || !clonesRoot2) return [];
|
|
11832
|
-
const canonical =
|
|
12702
|
+
const canonical = path22.join(clonesRoot2, leaf);
|
|
11833
12703
|
return [
|
|
11834
|
-
|
|
11835
|
-
|
|
12704
|
+
path22.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
12705
|
+
path22.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
11836
12706
|
];
|
|
11837
12707
|
}
|
|
11838
12708
|
async function readLedger(file, readFile5) {
|
|
@@ -11851,7 +12721,7 @@ async function readLedger(file, readFile5) {
|
|
|
11851
12721
|
async function findPreservedRecovery(task, {
|
|
11852
12722
|
clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
|
|
11853
12723
|
readFile: readFile5 = fsp13.readFile,
|
|
11854
|
-
exists =
|
|
12724
|
+
exists = fs13.existsSync
|
|
11855
12725
|
} = {}) {
|
|
11856
12726
|
const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
|
|
11857
12727
|
const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
|
|
@@ -12165,8 +13035,8 @@ var init_cancellation_probe = __esm({
|
|
|
12165
13035
|
});
|
|
12166
13036
|
|
|
12167
13037
|
// ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
|
|
12168
|
-
import { homedir as
|
|
12169
|
-
import { dirname as
|
|
13038
|
+
import { homedir as homedir8 } from "node:os";
|
|
13039
|
+
import { dirname as dirname7, join as join10 } from "node:path";
|
|
12170
13040
|
import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
|
|
12171
13041
|
function withLock(operation) {
|
|
12172
13042
|
const result = serialized.then(operation, operation);
|
|
@@ -12184,7 +13054,7 @@ async function readEntries(file) {
|
|
|
12184
13054
|
}
|
|
12185
13055
|
}
|
|
12186
13056
|
async function writeEntries(file, entries) {
|
|
12187
|
-
await mkdir3(
|
|
13057
|
+
await mkdir3(dirname7(file), { recursive: true });
|
|
12188
13058
|
const temp = `${file}.${process.pid}.tmp`;
|
|
12189
13059
|
await writeFile3(temp, `${JSON.stringify(entries)}
|
|
12190
13060
|
`, "utf8");
|
|
@@ -12230,13 +13100,13 @@ var DEFAULT_FILE, serialized;
|
|
|
12230
13100
|
var init_detached_economics_spool = __esm({
|
|
12231
13101
|
"../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
|
|
12232
13102
|
"use strict";
|
|
12233
|
-
DEFAULT_FILE =
|
|
13103
|
+
DEFAULT_FILE = join10(homedir8(), ".vo", "detached-run-economics.json");
|
|
12234
13104
|
serialized = Promise.resolve();
|
|
12235
13105
|
}
|
|
12236
13106
|
});
|
|
12237
13107
|
|
|
12238
13108
|
// ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
|
|
12239
|
-
import { randomUUID as
|
|
13109
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
12240
13110
|
async function handleKilledRun({
|
|
12241
13111
|
client,
|
|
12242
13112
|
id,
|
|
@@ -12273,7 +13143,7 @@ async function handleKilledRun({
|
|
|
12273
13143
|
};
|
|
12274
13144
|
}
|
|
12275
13145
|
if (reason === "claim_authority_changed") {
|
|
12276
|
-
const occurrenceId =
|
|
13146
|
+
const occurrenceId = randomUUID6();
|
|
12277
13147
|
const economics = {
|
|
12278
13148
|
occurrence_id: occurrenceId,
|
|
12279
13149
|
runner_id: runnerId,
|
|
@@ -12373,13 +13243,13 @@ var init_runner_runtime_limits = __esm({
|
|
|
12373
13243
|
});
|
|
12374
13244
|
|
|
12375
13245
|
// ../../scripts/virtual-office/code-runner/daemon-config.mjs
|
|
12376
|
-
import
|
|
13246
|
+
import os5 from "node:os";
|
|
12377
13247
|
function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
|
|
12378
13248
|
} } = {}) {
|
|
12379
13249
|
const servedOperators = parseList(env2.VO_CODE_RUNNER_OPERATOR_IDS);
|
|
12380
13250
|
const allowAmbientGithub = env2.VO_CODE_RUNNER_ALLOW_AMBIENT_GH === "1";
|
|
12381
13251
|
return {
|
|
12382
|
-
runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${
|
|
13252
|
+
runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${os5.hostname()}`,
|
|
12383
13253
|
...resolveRunner(env2, { warn: (message) => log2(`agent-select: ${message}`) }),
|
|
12384
13254
|
permissionMode: env2.VO_CODE_RUNNER_PERMISSION_MODE || "acceptEdits",
|
|
12385
13255
|
maxConcurrency: Math.max(1, Number(env2.VO_CODE_TASK_MAX_CONCURRENCY || 2) || 2),
|
|
@@ -12389,7 +13259,7 @@ function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
|
|
|
12389
13259
|
requireGithubAppAuth: !allowAmbientGithub && servedOperators.length > 0,
|
|
12390
13260
|
allowAmbientGithub,
|
|
12391
13261
|
sessionForwardSec: Math.max(0, Number(env2.VO_SESSION_FORWARD_SEC ?? 30) || 0),
|
|
12392
|
-
operatorSeed: env2.VO_LOCAL_OPERATOR_SEED || env2.VO_CODE_RUNNER_ID || `local-${
|
|
13262
|
+
operatorSeed: env2.VO_LOCAL_OPERATOR_SEED || env2.VO_CODE_RUNNER_ID || `local-${os5.hostname()}`,
|
|
12393
13263
|
cancelPollMs: Math.max(1e3, Number(env2.VO_CODE_RUNNER_CANCEL_POLL_MS || 2500) || 2500),
|
|
12394
13264
|
maxWallClockMs: resolveMaxWallClockMs(env2.VO_CODE_RUNNER_MAX_WALL_CLOCK_MS),
|
|
12395
13265
|
watchEnabled: env2.VO_CODE_RUNNER_WATCH !== "0",
|
|
@@ -12568,10 +13438,10 @@ var init_task_worktree_preparation = __esm({
|
|
|
12568
13438
|
// ../../scripts/virtual-office/code-runner-daemon.mjs
|
|
12569
13439
|
var code_runner_daemon_exports = {};
|
|
12570
13440
|
__export(code_runner_daemon_exports, {
|
|
12571
|
-
main: () =>
|
|
13441
|
+
main: () => main2
|
|
12572
13442
|
});
|
|
12573
|
-
import { randomUUID as
|
|
12574
|
-
import { fileURLToPath as
|
|
13443
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
13444
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
12575
13445
|
function log(msg) {
|
|
12576
13446
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
12577
13447
|
}
|
|
@@ -12713,16 +13583,12 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
12713
13583
|
return;
|
|
12714
13584
|
}
|
|
12715
13585
|
if (await taskWasCancelled({ client, id, run, safeProgress, log })) return;
|
|
13586
|
+
if (await gateTestGenTaskOrFail({ client, id, task, files, worktreeDir: wt.worktreeDir, log })) return;
|
|
13587
|
+
if (await enforceCompletionGateOrFail({ client, id, task, worktreeDir: wt.worktreeDir, log })) return;
|
|
12716
13588
|
const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
|
|
12717
13589
|
const localBranch = await resolveOrCreateBranchAsync(wt.worktreeDir, "vo/code-task");
|
|
12718
13590
|
const publicationBranch = publicationTarget.targetBranch || localBranch;
|
|
12719
|
-
if (!await recordPublicationIntent({
|
|
12720
|
-
client,
|
|
12721
|
-
id,
|
|
12722
|
-
branch: publicationBranch,
|
|
12723
|
-
safeProgress,
|
|
12724
|
-
log
|
|
12725
|
-
})) {
|
|
13591
|
+
if (!await recordPublicationIntent({ client, id, branch: publicationBranch, safeProgress, log })) {
|
|
12726
13592
|
await reportCancelledRun({ client, id, run, safeProgress, log });
|
|
12727
13593
|
return;
|
|
12728
13594
|
}
|
|
@@ -12797,10 +13663,10 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
|
12797
13663
|
}
|
|
12798
13664
|
}
|
|
12799
13665
|
}
|
|
12800
|
-
async function
|
|
13666
|
+
async function main2({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
12801
13667
|
const cfg = loadCodeRunnerConfig(env2, { log });
|
|
12802
13668
|
await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
|
|
12803
|
-
const runnerInstanceId =
|
|
13669
|
+
const runnerInstanceId = randomUUID7();
|
|
12804
13670
|
const client = createControlPlaneClient({
|
|
12805
13671
|
env: env2,
|
|
12806
13672
|
runnerId: cfg.runnerId,
|
|
@@ -12943,7 +13809,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
12943
13809
|
if (controlServer) controlServer.close();
|
|
12944
13810
|
log("stopped");
|
|
12945
13811
|
}
|
|
12946
|
-
var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress,
|
|
13812
|
+
var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress, invokedDirectly2;
|
|
12947
13813
|
var init_code_runner_daemon = __esm({
|
|
12948
13814
|
"../../scripts/virtual-office/code-runner-daemon.mjs"() {
|
|
12949
13815
|
"use strict";
|
|
@@ -12954,6 +13820,8 @@ var init_code_runner_daemon = __esm({
|
|
|
12954
13820
|
init_resolve_runner();
|
|
12955
13821
|
init_rate_limit_resume();
|
|
12956
13822
|
init_publish();
|
|
13823
|
+
init_test_gen_gate();
|
|
13824
|
+
init_completion_gate();
|
|
12957
13825
|
init_orphan_agent_reaper();
|
|
12958
13826
|
init_publish_async();
|
|
12959
13827
|
init_task_prompt();
|
|
@@ -12991,11 +13859,11 @@ var init_code_runner_daemon = __esm({
|
|
|
12991
13859
|
RATE_LIMIT_RESUME_ENABLED = process.env.VO_RATE_LIMIT_RESUME !== "0";
|
|
12992
13860
|
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12993
13861
|
safeProgress = makeSafeProgress(log);
|
|
12994
|
-
|
|
13862
|
+
invokedDirectly2 = process.argv[1] && fileURLToPath8(import.meta.url) === process.argv[1] && // Bundle-safe: self-start only when THIS file is the real entry (not inlined into vo-mcp's runner-cli.js ⇒ double-claim).
|
|
12995
13863
|
import.meta.url.endsWith("code-runner-daemon.mjs");
|
|
12996
|
-
if (
|
|
13864
|
+
if (invokedDirectly2) {
|
|
12997
13865
|
const once2 = process.argv.includes("--once");
|
|
12998
|
-
|
|
13866
|
+
main2({ once: once2 }).catch((err) => {
|
|
12999
13867
|
console.error("[code-runner] fatal:", err);
|
|
13000
13868
|
process.exit(1);
|
|
13001
13869
|
});
|
|
@@ -13129,6 +13997,98 @@ function pairedOperatorScope(readiness) {
|
|
|
13129
13997
|
return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
|
|
13130
13998
|
}
|
|
13131
13999
|
|
|
14000
|
+
// src/runner/root-config.mjs
|
|
14001
|
+
import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
|
|
14002
|
+
import { homedir } from "node:os";
|
|
14003
|
+
import { posix, win32 } from "node:path";
|
|
14004
|
+
import { randomUUID } from "node:crypto";
|
|
14005
|
+
var APP_IDENTIFIER = "ai.algosuite.vo-runner";
|
|
14006
|
+
var CLONES_DIR = "clones";
|
|
14007
|
+
function pathsFor(platform) {
|
|
14008
|
+
return platform === "win32" ? win32 : posix;
|
|
14009
|
+
}
|
|
14010
|
+
function absoluteOrNull(value, pathApi) {
|
|
14011
|
+
const normalized = String(value || "").trim();
|
|
14012
|
+
return normalized && pathApi.isAbsolute(normalized) ? pathApi.resolve(normalized) : null;
|
|
14013
|
+
}
|
|
14014
|
+
function defaultClonesRoot({
|
|
14015
|
+
platform = process.platform,
|
|
14016
|
+
env: env2 = process.env,
|
|
14017
|
+
home = homedir()
|
|
14018
|
+
} = {}) {
|
|
14019
|
+
const pathApi = pathsFor(platform);
|
|
14020
|
+
if (platform === "win32") {
|
|
14021
|
+
const appData = absoluteOrNull(env2.APPDATA, pathApi);
|
|
14022
|
+
return appData ? pathApi.join(appData, APP_IDENTIFIER, CLONES_DIR) : null;
|
|
14023
|
+
}
|
|
14024
|
+
const absoluteHome = absoluteOrNull(home, pathApi);
|
|
14025
|
+
if (!absoluteHome) return null;
|
|
14026
|
+
if (platform === "darwin") {
|
|
14027
|
+
return pathApi.join(absoluteHome, "Library", "Application Support", APP_IDENTIFIER, CLONES_DIR);
|
|
14028
|
+
}
|
|
14029
|
+
const xdg = absoluteOrNull(env2.XDG_CONFIG_HOME, pathApi);
|
|
14030
|
+
return pathApi.join(xdg || pathApi.join(absoluteHome, ".config"), APP_IDENTIFIER, CLONES_DIR);
|
|
14031
|
+
}
|
|
14032
|
+
function findGitRoot(cwd, { platform, exists = existsSync }) {
|
|
14033
|
+
const pathApi = pathsFor(platform);
|
|
14034
|
+
let cursor = pathApi.resolve(cwd);
|
|
14035
|
+
for (; ; ) {
|
|
14036
|
+
if (exists(pathApi.join(cursor, ".git"))) return cursor;
|
|
14037
|
+
const parent = pathApi.dirname(cursor);
|
|
14038
|
+
if (parent === cursor) return null;
|
|
14039
|
+
cursor = parent;
|
|
14040
|
+
}
|
|
14041
|
+
}
|
|
14042
|
+
function resolveRunnerRootConfig({
|
|
14043
|
+
env: env2 = process.env,
|
|
14044
|
+
cwd = process.cwd(),
|
|
14045
|
+
platform = process.platform,
|
|
14046
|
+
home = homedir(),
|
|
14047
|
+
exists = existsSync
|
|
14048
|
+
} = {}) {
|
|
14049
|
+
const pathApi = pathsFor(platform);
|
|
14050
|
+
const explicitRepoValue = String(env2.VO_CODE_RUNNER_REPO || "").trim();
|
|
14051
|
+
const explicitClonesValue = String(env2.VO_CODE_RUNNER_CLONES_ROOT || "").trim();
|
|
14052
|
+
if (explicitRepoValue && !pathApi.isAbsolute(explicitRepoValue)) {
|
|
14053
|
+
throw new Error(`VO_CODE_RUNNER_REPO must be an absolute path (got '${explicitRepoValue}')`);
|
|
14054
|
+
}
|
|
14055
|
+
if (explicitClonesValue && !pathApi.isAbsolute(explicitClonesValue)) {
|
|
14056
|
+
throw new Error(`VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${explicitClonesValue}')`);
|
|
14057
|
+
}
|
|
14058
|
+
const repoRoot2 = explicitRepoValue ? pathApi.resolve(explicitRepoValue) : findGitRoot(cwd, { platform, exists });
|
|
14059
|
+
const clonesRoot2 = explicitClonesValue ? pathApi.resolve(explicitClonesValue) : repoRoot2 ? null : defaultClonesRoot({ platform, env: env2, home });
|
|
14060
|
+
if (!repoRoot2 && !clonesRoot2) {
|
|
14061
|
+
throw new Error(
|
|
14062
|
+
"No safe runner root is available. Set VO_CODE_RUNNER_REPO or VO_CODE_RUNNER_CLONES_ROOT to an absolute path."
|
|
14063
|
+
);
|
|
14064
|
+
}
|
|
14065
|
+
return { repoRoot: repoRoot2, clonesRoot: clonesRoot2 };
|
|
14066
|
+
}
|
|
14067
|
+
function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }) {
|
|
14068
|
+
const root = repoRoot2 || clonesRoot2;
|
|
14069
|
+
if (!root) throw new Error("Runner root configuration has no working directory");
|
|
14070
|
+
return root;
|
|
14071
|
+
}
|
|
14072
|
+
function assertWritableRunnerDirectory(root) {
|
|
14073
|
+
mkdirSync(root, { recursive: true });
|
|
14074
|
+
const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID()}`);
|
|
14075
|
+
let handle;
|
|
14076
|
+
try {
|
|
14077
|
+
handle = openSync(probe, "wx", 384);
|
|
14078
|
+
} catch (error) {
|
|
14079
|
+
throw new Error(
|
|
14080
|
+
`Runner clones root is not writable: ${root} (${error instanceof Error ? error.message : String(error)})`,
|
|
14081
|
+
{ cause: error }
|
|
14082
|
+
);
|
|
14083
|
+
} finally {
|
|
14084
|
+
if (handle !== void 0) closeSync(handle);
|
|
14085
|
+
try {
|
|
14086
|
+
unlinkSync(probe);
|
|
14087
|
+
} catch {
|
|
14088
|
+
}
|
|
14089
|
+
}
|
|
14090
|
+
}
|
|
14091
|
+
|
|
13132
14092
|
// src/runner-cli.mjs
|
|
13133
14093
|
var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
13134
14094
|
function packageVersion() {
|
|
@@ -13143,11 +14103,45 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
|
13143
14103
|
`);
|
|
13144
14104
|
process.exit(0);
|
|
13145
14105
|
}
|
|
14106
|
+
var USAGE = `vo-mcp runner \u2014 bring-your-own agent runner daemon
|
|
14107
|
+
|
|
14108
|
+
Usage:
|
|
14109
|
+
vo-mcp runner poll for tasks forever (default)
|
|
14110
|
+
vo-mcp runner --once claim + run one task, then exit
|
|
14111
|
+
vo-mcp runner --status print pairing/readiness JSON, then exit
|
|
14112
|
+
vo-mcp runner --version print the version, then exit
|
|
14113
|
+
vo-mcp runner --help print this help, then exit
|
|
14114
|
+
|
|
14115
|
+
Env (all optional):
|
|
14116
|
+
VO_CONTROL_PLANE_ADMIN_TOKEN explicit bearer (wins over the stored credential)
|
|
14117
|
+
VO_CONTROL_PLANE_URL control-plane base URL (default: production)
|
|
14118
|
+
VO_CODE_RUNNER_REPO path to your repo clone (default: Git cwd)
|
|
14119
|
+
VO_CODE_RUNNER_CLONES_ROOT managed clone directory (default outside Git cwd)
|
|
14120
|
+
VO_CODE_RUNNER_OPERATOR_IDS operator id(s) this runner serves (your own)
|
|
14121
|
+
VO_CODE_RUNNER_REPOS owner/name repo(s) this runner builds
|
|
14122
|
+
|
|
14123
|
+
Pair this computer first with \`vo-mcp login\`.
|
|
14124
|
+
`;
|
|
14125
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
14126
|
+
process.stdout.write(USAGE);
|
|
14127
|
+
process.exit(0);
|
|
14128
|
+
}
|
|
13146
14129
|
var { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
13147
14130
|
var storedCredential = readStoredCredential2();
|
|
13148
14131
|
var explicitAdminToken = process.env.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
|
|
13149
14132
|
var token = explicitAdminToken || storedCredential?.vo_credential;
|
|
13150
14133
|
var statusOnly = process.argv.includes("--status");
|
|
14134
|
+
function configureRunnerFilesystem() {
|
|
14135
|
+
const config = resolveRunnerRootConfig();
|
|
14136
|
+
if (config.repoRoot) process.env.VO_CODE_RUNNER_REPO = config.repoRoot;
|
|
14137
|
+
else delete process.env.VO_CODE_RUNNER_REPO;
|
|
14138
|
+
if (config.clonesRoot) {
|
|
14139
|
+
assertWritableRunnerDirectory(config.clonesRoot);
|
|
14140
|
+
process.env.VO_CODE_RUNNER_CLONES_ROOT = config.clonesRoot;
|
|
14141
|
+
} else delete process.env.VO_CODE_RUNNER_CLONES_ROOT;
|
|
14142
|
+
process.chdir(runnerWorkingDirectory(config));
|
|
14143
|
+
return config;
|
|
14144
|
+
}
|
|
13151
14145
|
if (statusOnly) {
|
|
13152
14146
|
if (!storedCredential?.vo_credential) {
|
|
13153
14147
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -13156,6 +14150,7 @@ if (statusOnly) {
|
|
|
13156
14150
|
operatorId: null,
|
|
13157
14151
|
tenantId: null,
|
|
13158
14152
|
githubReady: null,
|
|
14153
|
+
filesystemReady: null,
|
|
13159
14154
|
error: "credential_missing",
|
|
13160
14155
|
message: "This computer is not paired. Pair it to your AlgoHQ account first."
|
|
13161
14156
|
})}
|
|
@@ -13166,9 +14161,28 @@ if (statusOnly) {
|
|
|
13166
14161
|
controlPlaneUrl: process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL,
|
|
13167
14162
|
token: storedCredential.vo_credential
|
|
13168
14163
|
});
|
|
13169
|
-
|
|
14164
|
+
if (!readiness.ok) {
|
|
14165
|
+
process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: null })}
|
|
13170
14166
|
`);
|
|
13171
|
-
|
|
14167
|
+
process.exit(1);
|
|
14168
|
+
}
|
|
14169
|
+
try {
|
|
14170
|
+
configureRunnerFilesystem();
|
|
14171
|
+
process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: true })}
|
|
14172
|
+
`);
|
|
14173
|
+
process.exit(0);
|
|
14174
|
+
} catch (error) {
|
|
14175
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
14176
|
+
process.stdout.write(`${JSON.stringify({
|
|
14177
|
+
...readiness,
|
|
14178
|
+
ok: false,
|
|
14179
|
+
filesystemReady: false,
|
|
14180
|
+
error: "filesystem_not_ready",
|
|
14181
|
+
message: `Filesystem readiness failed: ${detail}`
|
|
14182
|
+
})}
|
|
14183
|
+
`);
|
|
14184
|
+
process.exit(1);
|
|
14185
|
+
}
|
|
13172
14186
|
}
|
|
13173
14187
|
if (!token) {
|
|
13174
14188
|
console.error("[vo-mcp runner] No credential found. Run `vo-mcp login` first.");
|
|
@@ -13193,6 +14207,13 @@ if (!explicitAdminToken) {
|
|
|
13193
14207
|
process.exit(1);
|
|
13194
14208
|
}
|
|
13195
14209
|
}
|
|
14210
|
+
var rootConfig;
|
|
14211
|
+
try {
|
|
14212
|
+
rootConfig = configureRunnerFilesystem();
|
|
14213
|
+
} catch (error) {
|
|
14214
|
+
console.error(`[vo-mcp runner] Filesystem readiness failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
14215
|
+
process.exit(1);
|
|
14216
|
+
}
|
|
13196
14217
|
var env = {
|
|
13197
14218
|
...process.env,
|
|
13198
14219
|
VO_CONTROL_PLANE_ADMIN_TOKEN: token,
|
|
@@ -13201,15 +14222,16 @@ var env = {
|
|
|
13201
14222
|
// VO_CODE_RUNNER_VERSION, which a desktop host may set to its own shell
|
|
13202
14223
|
// release even after runner-control updates this package in place.
|
|
13203
14224
|
VO_CODE_RUNNER_DAEMON_VERSION: `vo-mcp/${packageVersion()}`,
|
|
13204
|
-
VO_CODE_RUNNER_REPO:
|
|
14225
|
+
...rootConfig.repoRoot ? { VO_CODE_RUNNER_REPO: rootConfig.repoRoot } : {},
|
|
14226
|
+
...rootConfig.clonesRoot ? { VO_CODE_RUNNER_CLONES_ROOT: rootConfig.clonesRoot } : {},
|
|
13205
14227
|
// Server-resolved identity replaces stale/hardcoded desktop scope. Besides
|
|
13206
14228
|
// filtering heartbeat/claims, this keeps GitHub App auth required for the
|
|
13207
14229
|
// entire task lifecycle (never ambient-gh fallback after a paired preflight).
|
|
13208
14230
|
...pairedOperatorId ? { VO_CODE_RUNNER_OPERATOR_IDS: pairedOperatorId } : {}
|
|
13209
14231
|
};
|
|
13210
14232
|
var once = process.argv.includes("--once");
|
|
13211
|
-
var { main:
|
|
13212
|
-
|
|
14233
|
+
var { main: main3 } = await Promise.resolve().then(() => (init_code_runner_daemon(), code_runner_daemon_exports));
|
|
14234
|
+
main3({ env, once }).catch((err) => {
|
|
13213
14235
|
console.error("[vo-mcp runner] fatal:", err);
|
|
13214
14236
|
process.exit(1);
|
|
13215
14237
|
});
|