@algosuite/vo-mcp 0.2.0-beta.60 → 0.2.0-beta.61
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/ci/check-local-pr-overlap.js +117 -7
- package/dist/install-cli.js +68 -6
- package/dist/install-cli.js.map +3 -3
- package/dist/runner-cli.js +758 -319
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
|
@@ -106643,6 +106643,81 @@ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.ar
|
|
|
106643
106643
|
process.exitCode = exitCode;
|
|
106644
106644
|
}
|
|
106645
106645
|
|
|
106646
|
+
// ../../scripts/virtual-office/code-runner/self-claim-identity.mjs
|
|
106647
|
+
var CODE_TASK_BRANCH_RE = /^(?:refs\/heads\/)?vo\/code-task-([0-9a-z]{8,})-/iu;
|
|
106648
|
+
var CODE_TASK_MARKER_RE = /(?:^|[\s[(])code-task:([0-9a-z][0-9a-z-]{7,63})/iu;
|
|
106649
|
+
var MIN_TASK_ID_TOKEN_LENGTH = 8;
|
|
106650
|
+
var MAX_TASK_ID_LENGTH = 36;
|
|
106651
|
+
var TASK_ID_CHARS_RE = /^[0-9a-z][0-9a-z-]*$/u;
|
|
106652
|
+
function normalizeTaskId(value) {
|
|
106653
|
+
return String(value ?? "").trim().toLowerCase();
|
|
106654
|
+
}
|
|
106655
|
+
function taskIdFromCodeTaskBranch(branch) {
|
|
106656
|
+
const match = CODE_TASK_BRANCH_RE.exec(String(branch ?? "").trim());
|
|
106657
|
+
return match ? match[1].toLowerCase() : "";
|
|
106658
|
+
}
|
|
106659
|
+
function taskIdFromClaimMarker(currentTask) {
|
|
106660
|
+
const match = CODE_TASK_MARKER_RE.exec(String(currentTask ?? ""));
|
|
106661
|
+
return match ? match[1].toLowerCase() : "";
|
|
106662
|
+
}
|
|
106663
|
+
function resolvePublishingTaskId({ taskId = "", branch = "", env = {} } = {}) {
|
|
106664
|
+
const none = { taskId: "", token: "", source: "none", exact: false };
|
|
106665
|
+
const fromBranch = taskIdFromCodeTaskBranch(branch);
|
|
106666
|
+
const branchToken = fromBranch.length >= MIN_TASK_ID_TOKEN_LENGTH ? fromBranch : "";
|
|
106667
|
+
const explicit = normalizeTaskId(taskId);
|
|
106668
|
+
if (explicit.length >= MIN_TASK_ID_TOKEN_LENGTH && TASK_ID_CHARS_RE.test(explicit)) {
|
|
106669
|
+
return { taskId: explicit, token: explicit.slice(0, MIN_TASK_ID_TOKEN_LENGTH), source: "explicit", exact: true };
|
|
106670
|
+
}
|
|
106671
|
+
const fromEnv = normalizeTaskId(env?.VO_CODE_TASK_ID);
|
|
106672
|
+
if (fromEnv.length >= MIN_TASK_ID_TOKEN_LENGTH && TASK_ID_CHARS_RE.test(fromEnv)) {
|
|
106673
|
+
if (branchToken && !fromEnv.startsWith(branchToken)) {
|
|
106674
|
+
return { ...none, source: "conflict" };
|
|
106675
|
+
}
|
|
106676
|
+
return { taskId: fromEnv, token: fromEnv.slice(0, MIN_TASK_ID_TOKEN_LENGTH), source: "env", exact: true };
|
|
106677
|
+
}
|
|
106678
|
+
if (branchToken) return { taskId: branchToken, token: branchToken, source: "branch", exact: false };
|
|
106679
|
+
return none;
|
|
106680
|
+
}
|
|
106681
|
+
function matchesPublishingTask(candidate, publishing) {
|
|
106682
|
+
const value = normalizeTaskId(candidate);
|
|
106683
|
+
if (!publishing?.token) return false;
|
|
106684
|
+
if (value.length < MIN_TASK_ID_TOKEN_LENGTH || value.length > MAX_TASK_ID_LENGTH) return false;
|
|
106685
|
+
if (!TASK_ID_CHARS_RE.test(value)) return false;
|
|
106686
|
+
if (!value.startsWith(publishing.token)) return false;
|
|
106687
|
+
if (publishing.exact) return publishing.taskId.startsWith(value) || value.startsWith(publishing.taskId);
|
|
106688
|
+
return true;
|
|
106689
|
+
}
|
|
106690
|
+
function claimTaskId(conflict, publishing) {
|
|
106691
|
+
if (!publishing?.token) return "";
|
|
106692
|
+
const marked = taskIdFromClaimMarker(conflict?.currentTask);
|
|
106693
|
+
if (marked && matchesPublishingTask(marked, publishing)) return marked;
|
|
106694
|
+
const agentId = String(conflict?.agentId ?? "").trim().toLowerCase();
|
|
106695
|
+
if (!agentId) return "";
|
|
106696
|
+
const index = agentId.lastIndexOf(`-${publishing.token}`);
|
|
106697
|
+
if (index < 0) return "";
|
|
106698
|
+
const tail = agentId.slice(index + 1);
|
|
106699
|
+
return matchesPublishingTask(tail, publishing) ? tail : "";
|
|
106700
|
+
}
|
|
106701
|
+
function claimBelongsToTask(conflict, publishing) {
|
|
106702
|
+
return claimTaskId(conflict, publishing) !== "";
|
|
106703
|
+
}
|
|
106704
|
+
function partitionSelfClaims(conflicts = [], publishing = { token: "" }) {
|
|
106705
|
+
const self2 = [];
|
|
106706
|
+
const foreign = [];
|
|
106707
|
+
for (const conflict of conflicts) {
|
|
106708
|
+
(claimBelongsToTask(conflict, publishing) ? self2 : foreign).push(conflict);
|
|
106709
|
+
}
|
|
106710
|
+
return { self: self2, foreign };
|
|
106711
|
+
}
|
|
106712
|
+
function describeSelfClaimExclusion(self2 = [], publishing = { token: "" }) {
|
|
106713
|
+
if (self2.length === 0) return "";
|
|
106714
|
+
const files = [...new Set(self2.map((conflict) => conflict?.matchedFile || conflict?.file || "?"))];
|
|
106715
|
+
return [
|
|
106716
|
+
`Self-claims ignored: ${self2.length} live whiteboard claim(s) held by this publishing task (task ${publishing.taskId || publishing.token}, matched via ${publishing.source}).`,
|
|
106717
|
+
...files.map((file) => `- ${file}`)
|
|
106718
|
+
].join("\n");
|
|
106719
|
+
}
|
|
106720
|
+
|
|
106646
106721
|
// ../../scripts/ci/check-local-pr-overlap.mjs
|
|
106647
106722
|
function normalizeFileList(files = []) {
|
|
106648
106723
|
return [...new Set(files.flatMap((file) => String(file || "").split(/\r?\n/)).map((file) => file.trim().replaceAll("\\", "/")).filter(Boolean))];
|
|
@@ -106728,7 +106803,10 @@ function evaluateOverlapGate({
|
|
|
106728
106803
|
whiteboardReason = "",
|
|
106729
106804
|
requireWhiteboard = true,
|
|
106730
106805
|
allowOverlap = false,
|
|
106731
|
-
prLabel = "local branch"
|
|
106806
|
+
prLabel = "local branch",
|
|
106807
|
+
// Never silent: when the publishing task's OWN claims were dropped, say so and
|
|
106808
|
+
// name them. A gate that quietly ignores claims cannot be told from a broken one.
|
|
106809
|
+
selfClaimNote = ""
|
|
106732
106810
|
} = {}) {
|
|
106733
106811
|
const files = normalizeFileList(changedFiles);
|
|
106734
106812
|
const overlaps = classifyOverlaps(files, otherPrs || []);
|
|
@@ -106740,6 +106818,7 @@ function evaluateOverlapGate({
|
|
|
106740
106818
|
lines.push(`Local overlap gate for ${prLabel}`);
|
|
106741
106819
|
lines.push(`Changed files: ${files.length}`);
|
|
106742
106820
|
if (otherPrs?.length) lines.push(`Compared against open PRs: ${otherPrs.length}`);
|
|
106821
|
+
if (selfClaimNote) lines.push(selfClaimNote);
|
|
106743
106822
|
if (directOverlap || overlaps.packageOverlaps.length || overlaps.sharedDepOverlaps.length) {
|
|
106744
106823
|
lines.push("");
|
|
106745
106824
|
lines.push(buildOverlapReport(overlaps, 0));
|
|
@@ -106780,6 +106859,32 @@ function evaluateOverlapGate({
|
|
|
106780
106859
|
report: lines.join("\n")
|
|
106781
106860
|
};
|
|
106782
106861
|
}
|
|
106862
|
+
function decideOverlapGate({
|
|
106863
|
+
changedFiles,
|
|
106864
|
+
otherPrs = [],
|
|
106865
|
+
busResult = { available: true, conflicts: [] },
|
|
106866
|
+
taskId = "",
|
|
106867
|
+
branch = "",
|
|
106868
|
+
env = process.env,
|
|
106869
|
+
requireWhiteboard = true,
|
|
106870
|
+
allowOverlap = false,
|
|
106871
|
+
prLabel = branch || "local branch"
|
|
106872
|
+
} = {}) {
|
|
106873
|
+
const publishing = resolvePublishingTaskId({ taskId, branch, env });
|
|
106874
|
+
const { self: self2, foreign } = partitionSelfClaims(busResult?.conflicts || [], publishing);
|
|
106875
|
+
const evaluated = evaluateOverlapGate({
|
|
106876
|
+
changedFiles,
|
|
106877
|
+
otherPrs,
|
|
106878
|
+
busConflicts: foreign,
|
|
106879
|
+
whiteboardAvailable: busResult?.available !== false,
|
|
106880
|
+
whiteboardReason: busResult?.reason || "",
|
|
106881
|
+
requireWhiteboard,
|
|
106882
|
+
allowOverlap,
|
|
106883
|
+
prLabel,
|
|
106884
|
+
selfClaimNote: describeSelfClaimExclusion(self2, publishing)
|
|
106885
|
+
});
|
|
106886
|
+
return { ...evaluated, publishing, selfClaims: self2 };
|
|
106887
|
+
}
|
|
106783
106888
|
async function main() {
|
|
106784
106889
|
const { values: args } = parseArgs({
|
|
106785
106890
|
options: {
|
|
@@ -106788,7 +106893,10 @@ async function main() {
|
|
|
106788
106893
|
branch: { type: "string", default: "" },
|
|
106789
106894
|
stdin: { type: "boolean", default: false },
|
|
106790
106895
|
"exclude-pr": { type: "string", default: "" },
|
|
106791
|
-
"agent-id": { type: "string", default: process.env.AGENT_ID || "" }
|
|
106896
|
+
"agent-id": { type: "string", default: process.env.AGENT_ID || "" },
|
|
106897
|
+
// The publishing code task. Explicit beats VO_CODE_TASK_ID beats the
|
|
106898
|
+
// `vo/code-task-<id8>-…` branch; absent everywhere ⇒ nothing is excluded.
|
|
106899
|
+
"task-id": { type: "string", default: "" }
|
|
106792
106900
|
}
|
|
106793
106901
|
});
|
|
106794
106902
|
const changedFiles = args.stdin ? readFilesFromStdin() : readChangedFiles({ base: args.base, head: args.head });
|
|
@@ -106815,12 +106923,12 @@ async function main() {
|
|
|
106815
106923
|
return 2;
|
|
106816
106924
|
}
|
|
106817
106925
|
const busResult = await checkForConflicts(changedFiles, { excludeAgentId: args["agent-id"] });
|
|
106818
|
-
const result =
|
|
106926
|
+
const result = decideOverlapGate({
|
|
106819
106927
|
changedFiles,
|
|
106820
106928
|
otherPrs,
|
|
106821
|
-
|
|
106822
|
-
|
|
106823
|
-
|
|
106929
|
+
busResult,
|
|
106930
|
+
taskId: args["task-id"],
|
|
106931
|
+
branch: args.branch,
|
|
106824
106932
|
requireWhiteboard: process.env.VO_ALLOW_WHITEBOARD_UNAVAILABLE !== "1",
|
|
106825
106933
|
allowOverlap: process.env.VO_ALLOW_PR_OVERLAP === "1",
|
|
106826
106934
|
prLabel: args.branch || "local branch"
|
|
@@ -106831,6 +106939,7 @@ async function main() {
|
|
|
106831
106939
|
var __test3 = {
|
|
106832
106940
|
HOSTILE_WORKTREE_GIT_GUARDS,
|
|
106833
106941
|
gitText,
|
|
106942
|
+
decideOverlapGate,
|
|
106834
106943
|
evaluateOverlapGate,
|
|
106835
106944
|
isPrDiffTooLargeError,
|
|
106836
106945
|
normalizeFileList,
|
|
@@ -106842,7 +106951,8 @@ if (process.argv[1] && import.meta.url === pathToFileURL2(path2.resolve(process.
|
|
|
106842
106951
|
process.exit(await main());
|
|
106843
106952
|
}
|
|
106844
106953
|
export {
|
|
106845
|
-
__test3 as __test
|
|
106954
|
+
__test3 as __test,
|
|
106955
|
+
decideOverlapGate
|
|
106846
106956
|
};
|
|
106847
106957
|
/*! Bundled license information:
|
|
106848
106958
|
|
package/dist/install-cli.js
CHANGED
|
@@ -546,6 +546,43 @@ function installCodexMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, mana
|
|
|
546
546
|
log(`\u2713 Wrote AlgoHQ MCP to Codex: ${configPath}`);
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
+
// src/remote-mcp-entry.ts
|
|
550
|
+
var REMOTE_MCP_SERVER_KEY = "vo-mcp-remote";
|
|
551
|
+
var REMOTE_MCP_TOKEN_ENV = "VO_MCP_REMOTE_TOKEN";
|
|
552
|
+
var REMOTE_MCP_ENDPOINT_PATH = "/api/v1/mcp";
|
|
553
|
+
var REMOTE_MCP_AUTH_HEADER = `Bearer \${${REMOTE_MCP_TOKEN_ENV}}`;
|
|
554
|
+
function remoteMcpOptIn(env) {
|
|
555
|
+
return (env["VO_REMOTE_MCP"] ?? "").trim() === "1";
|
|
556
|
+
}
|
|
557
|
+
function remoteMcpUrl(controlPlaneUrl) {
|
|
558
|
+
return `${controlPlaneUrl.trim().replace(/\/+$/, "")}${REMOTE_MCP_ENDPOINT_PATH}`;
|
|
559
|
+
}
|
|
560
|
+
function buildRemoteMcpEntry(controlPlaneUrl) {
|
|
561
|
+
return {
|
|
562
|
+
type: "http",
|
|
563
|
+
url: remoteMcpUrl(controlPlaneUrl),
|
|
564
|
+
headers: { Authorization: REMOTE_MCP_AUTH_HEADER }
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function asRecord(value) {
|
|
568
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
569
|
+
}
|
|
570
|
+
function isManagedRemoteMcpEntry(value) {
|
|
571
|
+
const entry = asRecord(value);
|
|
572
|
+
if (!entry) return false;
|
|
573
|
+
if (entry["type"] !== "http") return false;
|
|
574
|
+
const url = entry["url"];
|
|
575
|
+
if (typeof url !== "string" || !url.endsWith(REMOTE_MCP_ENDPOINT_PATH)) return false;
|
|
576
|
+
const headers = asRecord(entry["headers"]);
|
|
577
|
+
return headers?.["Authorization"] === REMOTE_MCP_AUTH_HEADER;
|
|
578
|
+
}
|
|
579
|
+
function remoteMcpEntryIsCurrent(value, desired) {
|
|
580
|
+
const entry = asRecord(value);
|
|
581
|
+
if (!entry) return false;
|
|
582
|
+
const headers = asRecord(entry["headers"]);
|
|
583
|
+
return entry["type"] === desired.type && entry["url"] === desired.url && headers?.["Authorization"] === desired.headers["Authorization"];
|
|
584
|
+
}
|
|
585
|
+
|
|
549
586
|
// src/autostart.ts
|
|
550
587
|
import { homedir as homedir3, platform as platform2 } from "node:os";
|
|
551
588
|
import { isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
@@ -810,7 +847,20 @@ function resolveVoMcpCliPath() {
|
|
|
810
847
|
}
|
|
811
848
|
var INSTALL_LAUNCHER = { sticky: false, onlyExisting: false };
|
|
812
849
|
var HEAL_LAUNCHER = { sticky: true, onlyExisting: true };
|
|
813
|
-
function
|
|
850
|
+
function reconcileRemoteEntry(mcpServers, remote) {
|
|
851
|
+
const existing = mcpServers[REMOTE_MCP_SERVER_KEY];
|
|
852
|
+
if (remote) {
|
|
853
|
+
return {
|
|
854
|
+
fragment: { [REMOTE_MCP_SERVER_KEY]: remote },
|
|
855
|
+
current: remoteMcpEntryIsCurrent(existing, remote)
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
if (existing !== void 0 && isManagedRemoteMcpEntry(existing)) {
|
|
859
|
+
return { fragment: { [REMOTE_MCP_SERVER_KEY]: void 0 }, current: false };
|
|
860
|
+
}
|
|
861
|
+
return { fragment: {}, current: true };
|
|
862
|
+
}
|
|
863
|
+
function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label, launcher = { launcherPath: null, ...INSTALL_LAUNCHER }, remote = null) {
|
|
814
864
|
if (launcher.onlyExisting && !existsSync6(configPath)) return;
|
|
815
865
|
const read = readClaudeConfig(configPath);
|
|
816
866
|
if (read.kind === "invalid" || read.kind === "empty" && launcher.onlyExisting) {
|
|
@@ -824,8 +874,9 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label, la
|
|
|
824
874
|
const voEntry = managedEntry ?? mcpServers["vo"];
|
|
825
875
|
const { launcherPath } = launcher;
|
|
826
876
|
const fallbackCli = chooseFallbackCli(voEntry?.env?.[MCP_FALLBACK_CLI_ENV], cliPath, launcher.sticky, launcherPath ? dirname4(launcherPath) : null);
|
|
827
|
-
const
|
|
828
|
-
|
|
877
|
+
const localCurrent = launcherPath ? !isStaleVoMcpEntry(voEntry, launcherPath, fallbackCli) : Boolean(voEntry?.args?.some((a) => a.includes(cliPath)));
|
|
878
|
+
const remoteState = reconcileRemoteEntry(mcpServers, remote);
|
|
879
|
+
if (localCurrent && remoteState.current) {
|
|
829
880
|
log(` ${label} already current: ${configPath}`);
|
|
830
881
|
return;
|
|
831
882
|
}
|
|
@@ -845,7 +896,11 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label, la
|
|
|
845
896
|
...preservedEnv,
|
|
846
897
|
...launcherPath && fallbackCli ? { [MCP_FALLBACK_CLI_ENV]: fallbackCli } : {}
|
|
847
898
|
}
|
|
848
|
-
}
|
|
899
|
+
},
|
|
900
|
+
// Additive remote entry (11.3 slice A) — or the removal of one we wrote,
|
|
901
|
+
// when the operator has opted back out. Spread LAST so it can delete its
|
|
902
|
+
// own key; it never names 'vo-mcp', so the stdio entry above is safe.
|
|
903
|
+
...remoteState.fragment
|
|
849
904
|
}
|
|
850
905
|
};
|
|
851
906
|
if (readMtime !== null && (!existsSync6(configPath) || statSync3(configPath).mtimeMs !== readMtime)) {
|
|
@@ -854,6 +909,12 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label, la
|
|
|
854
909
|
}
|
|
855
910
|
writeClaudeConfig(configPath, merged);
|
|
856
911
|
log(`\u2713 Wrote vo-mcp to ${label}: ${configPath}`);
|
|
912
|
+
if (remote) {
|
|
913
|
+
log(` + remote MCP entry '${REMOTE_MCP_SERVER_KEY}' \u2192 ${remote.url} (serves vo_skill_list; every other tool stays on the local vo-mcp entry)`);
|
|
914
|
+
log(` Set ${REMOTE_MCP_TOKEN_ENV} in your environment to authenticate it \u2014 no token is written to this file.`);
|
|
915
|
+
} else if (!remoteState.current) {
|
|
916
|
+
log(` - removed the remote MCP entry '${REMOTE_MCP_SERVER_KEY}' (VO_REMOTE_MCP is not set to 1)`);
|
|
917
|
+
}
|
|
857
918
|
}
|
|
858
919
|
function installMcpConfig(log, env, mode = "install") {
|
|
859
920
|
const home = env["HOME"]?.trim() || env["USERPROFILE"]?.trim() || homedir4();
|
|
@@ -861,6 +922,7 @@ function installMcpConfig(log, env, mode = "install") {
|
|
|
861
922
|
const plat = platform3();
|
|
862
923
|
const cliPath = resolveVoMcpCliPath();
|
|
863
924
|
const controlPlaneUrl = env["VO_CONTROL_PLANE_URL"]?.trim() || DEFAULT_CONTROL_PLANE_URL2;
|
|
925
|
+
const remote = remoteMcpOptIn(env) ? buildRemoteMcpEntry(controlPlaneUrl) : null;
|
|
864
926
|
const launcherPath = writeMcpLauncherForEnv(env, log, { platform: plat, home });
|
|
865
927
|
const launcher = { launcherPath, ...mode === "heal" ? HEAL_LAUNCHER : INSTALL_LAUNCHER };
|
|
866
928
|
const leg = (label, run) => {
|
|
@@ -871,8 +933,8 @@ function installMcpConfig(log, env, mode = "install") {
|
|
|
871
933
|
log(` \u26A0 ${label}: could not update the MCP registration \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
872
934
|
}
|
|
873
935
|
};
|
|
874
|
-
leg("Claude Code CLI", () => installMcpConfigAt(resolveCodeConfigPath(home), cliPath, controlPlaneUrl, log, "Claude Code CLI", launcher));
|
|
875
|
-
leg("Claude Desktop", () => installMcpConfigAt(resolveDesktopConfigPath(home, plat, appData), cliPath, controlPlaneUrl, log, "Claude Desktop", launcher));
|
|
936
|
+
leg("Claude Code CLI", () => installMcpConfigAt(resolveCodeConfigPath(home), cliPath, controlPlaneUrl, log, "Claude Code CLI", launcher, remote));
|
|
937
|
+
leg("Claude Desktop", () => installMcpConfigAt(resolveDesktopConfigPath(home, plat, appData), cliPath, controlPlaneUrl, log, "Claude Desktop", launcher, remote));
|
|
876
938
|
const codexPath = resolveCodexConfigPath(home);
|
|
877
939
|
if (launcher.onlyExisting && !existsSync6(codexPath)) return;
|
|
878
940
|
leg("Codex", () => {
|