@algosuite/vo-mcp 0.2.0-beta.21 → 0.2.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runner-cli.js +106 -39
- package/dist/runner-cli.js.map +3 -3
- package/dist/runner-supervisor.js +28 -31
- package/dist/runner-supervisor.js.map +3 -3
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -2241,6 +2241,37 @@ var init_spend_cap_shim = __esm({
|
|
|
2241
2241
|
}
|
|
2242
2242
|
});
|
|
2243
2243
|
|
|
2244
|
+
// ../../scripts/virtual-office/code-runner/installation-token.mjs
|
|
2245
|
+
async function fetchInstallationToken({ req, required = false, readOnly = false, repo = null }) {
|
|
2246
|
+
const fail = (reason) => {
|
|
2247
|
+
if (required) throw new Error(`installation-token required: ${reason}`);
|
|
2248
|
+
return null;
|
|
2249
|
+
};
|
|
2250
|
+
try {
|
|
2251
|
+
const res = await req(
|
|
2252
|
+
"POST",
|
|
2253
|
+
"/api/v1/github/installation-token",
|
|
2254
|
+
readOnly ? { scope: "read", ...repo ? { repo } : {} } : {},
|
|
2255
|
+
readOnly ? { timeoutMs: READ_TOKEN_TIMEOUT_MS } : {}
|
|
2256
|
+
);
|
|
2257
|
+
if (!res.ok) return fail(`HTTP ${res.status}`);
|
|
2258
|
+
const json = await res.json();
|
|
2259
|
+
if (!json || !json.token) return fail("missing token");
|
|
2260
|
+
if (readOnly && json.scope !== "read") return fail("control plane did not confirm a read-only grant");
|
|
2261
|
+
return { token: json.token, expiresAt: json.expires_at || null };
|
|
2262
|
+
} catch (err) {
|
|
2263
|
+
if (required) throw err;
|
|
2264
|
+
return null;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
var READ_TOKEN_TIMEOUT_MS;
|
|
2268
|
+
var init_installation_token = __esm({
|
|
2269
|
+
"../../scripts/virtual-office/code-runner/installation-token.mjs"() {
|
|
2270
|
+
"use strict";
|
|
2271
|
+
READ_TOKEN_TIMEOUT_MS = 15e3;
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
|
|
2244
2275
|
// src/runner/control-plane-auth-stub.mjs
|
|
2245
2276
|
var control_plane_auth_stub_exports = {};
|
|
2246
2277
|
__export(control_plane_auth_stub_exports, {
|
|
@@ -2564,37 +2595,9 @@ function createControlPlaneClient({
|
|
|
2564
2595
|
const json = await res.json();
|
|
2565
2596
|
return json?.action || null;
|
|
2566
2597
|
},
|
|
2567
|
-
/**
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
* authenticated operator (ctx.operator_id), so the token covers only that
|
|
2571
|
-
* operator's installation.
|
|
2572
|
-
*
|
|
2573
|
-
* Returns { token, expiresAt } on success. In legacy/admin mode it returns
|
|
2574
|
-
* null on a miss so the caller may use ambient `gh`. In scoped-operator mode
|
|
2575
|
-
* callers pass `{ required: true }`, which fails closed instead of letting a
|
|
2576
|
-
* missing/mis-scoped installation fall through to the runner machine's `gh`.
|
|
2577
|
-
*
|
|
2578
|
-
* The minted token is only usable for push + PR if the GitHub App grants
|
|
2579
|
-
* BOTH `Contents: write` (git push) AND `Pull requests: write` (gh pr
|
|
2580
|
-
* create) — see docs/vo/github-app-setup-2026-06-18.md. A token missing
|
|
2581
|
-
* either scope fails at push (→ ambient fallback) or at `gh pr create`.
|
|
2582
|
-
*/
|
|
2583
|
-
async getInstallationToken({ required = false } = {}) {
|
|
2584
|
-
const fail = (reason) => {
|
|
2585
|
-
if (required) throw new Error(`installation-token required: ${reason}`);
|
|
2586
|
-
return null;
|
|
2587
|
-
};
|
|
2588
|
-
try {
|
|
2589
|
-
const res = await req("POST", "/api/v1/github/installation-token", {});
|
|
2590
|
-
if (!res.ok) return fail(`HTTP ${res.status}`);
|
|
2591
|
-
const json = await res.json();
|
|
2592
|
-
if (!json || !json.token) return fail("missing token");
|
|
2593
|
-
return { token: json.token, expiresAt: json.expires_at || null };
|
|
2594
|
-
} catch (err) {
|
|
2595
|
-
if (required) throw err;
|
|
2596
|
-
return null;
|
|
2597
|
-
}
|
|
2598
|
+
/** Mint a GitHub App installation token — see installation-token.mjs. */
|
|
2599
|
+
async getInstallationToken({ required = false, readOnly = false, repo = null } = {}) {
|
|
2600
|
+
return fetchInstallationToken({ req, required, readOnly, repo });
|
|
2598
2601
|
},
|
|
2599
2602
|
/**
|
|
2600
2603
|
* Read the operator's dispatch-mode config (Fast→Ultracode effort setting).
|
|
@@ -2617,6 +2620,7 @@ var cachedFirebaseToken;
|
|
|
2617
2620
|
var init_control_plane_client = __esm({
|
|
2618
2621
|
"../../scripts/virtual-office/code-runner/control-plane-client.mjs"() {
|
|
2619
2622
|
"use strict";
|
|
2623
|
+
init_installation_token();
|
|
2620
2624
|
cachedFirebaseToken = null;
|
|
2621
2625
|
}
|
|
2622
2626
|
});
|
|
@@ -3844,6 +3848,11 @@ function itemText(item) {
|
|
|
3844
3848
|
}
|
|
3845
3849
|
return "";
|
|
3846
3850
|
}
|
|
3851
|
+
function parseCodexVersion(stdout) {
|
|
3852
|
+
if (typeof stdout !== "string") return null;
|
|
3853
|
+
const match = stdout.match(/\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\b/u);
|
|
3854
|
+
return match ? match[1] : null;
|
|
3855
|
+
}
|
|
3847
3856
|
function parseCodexEvent(line) {
|
|
3848
3857
|
const trimmed = String(line || "").trim();
|
|
3849
3858
|
if (!trimmed) return null;
|
|
@@ -3938,7 +3947,7 @@ var init_codex_runner = __esm({
|
|
|
3938
3947
|
...this.getSpawnOptions({ bin }),
|
|
3939
3948
|
windowsHide: true,
|
|
3940
3949
|
timeout: 3e3,
|
|
3941
|
-
|
|
3950
|
+
encoding: "utf8"
|
|
3942
3951
|
});
|
|
3943
3952
|
if (version.error) {
|
|
3944
3953
|
return { installed: false, authenticated: false, message: `codex not found on PATH: ${version.error.message}` };
|
|
@@ -3946,6 +3955,8 @@ var init_codex_runner = __esm({
|
|
|
3946
3955
|
if (version.status !== 0) {
|
|
3947
3956
|
return { installed: true, authenticated: false, message: "codex exists but --version failed (auth unclear)" };
|
|
3948
3957
|
}
|
|
3958
|
+
const cliVersion = parseCodexVersion(version.stdout);
|
|
3959
|
+
const versionField = cliVersion ? { version: cliVersion } : {};
|
|
3949
3960
|
const login = this.spawn(bin, ["login", "status"], {
|
|
3950
3961
|
...this.getSpawnOptions({ bin }),
|
|
3951
3962
|
windowsHide: true,
|
|
@@ -3960,18 +3971,21 @@ ${login.stderr || ""}`.trim();
|
|
|
3960
3971
|
return {
|
|
3961
3972
|
installed: true,
|
|
3962
3973
|
authenticated: true,
|
|
3974
|
+
...versionField,
|
|
3963
3975
|
message: "codex API key available (no persisted ChatGPT login)"
|
|
3964
3976
|
};
|
|
3965
3977
|
}
|
|
3966
3978
|
return {
|
|
3967
3979
|
installed: true,
|
|
3968
3980
|
authenticated: false,
|
|
3981
|
+
...versionField,
|
|
3969
3982
|
message: output || login.error?.message || "codex is installed but not logged in"
|
|
3970
3983
|
};
|
|
3971
3984
|
}
|
|
3972
3985
|
return {
|
|
3973
3986
|
installed: true,
|
|
3974
3987
|
authenticated: true,
|
|
3988
|
+
...versionField,
|
|
3975
3989
|
message: output || "codex login status succeeded"
|
|
3976
3990
|
};
|
|
3977
3991
|
} catch (err) {
|
|
@@ -6422,7 +6436,12 @@ async function collectAgentAvailability({
|
|
|
6422
6436
|
const probes = agents.map(async (agent) => {
|
|
6423
6437
|
try {
|
|
6424
6438
|
const r = await runnerFor(agent).checkAuth();
|
|
6425
|
-
return {
|
|
6439
|
+
return {
|
|
6440
|
+
agent,
|
|
6441
|
+
installed: Boolean(r?.installed),
|
|
6442
|
+
authenticated: Boolean(r?.authenticated),
|
|
6443
|
+
...typeof r?.version === "string" && r.version ? { version: r.version } : {}
|
|
6444
|
+
};
|
|
6426
6445
|
} catch {
|
|
6427
6446
|
return { agent, installed: false, authenticated: false };
|
|
6428
6447
|
}
|
|
@@ -8823,11 +8842,41 @@ var init_reconnect_backoff = __esm({
|
|
|
8823
8842
|
}
|
|
8824
8843
|
});
|
|
8825
8844
|
|
|
8845
|
+
// ../../scripts/virtual-office/code-runner/redact-tokens.mjs
|
|
8846
|
+
function redactSecrets(text) {
|
|
8847
|
+
if (typeof text !== "string" || text.length === 0) return text;
|
|
8848
|
+
let out = text;
|
|
8849
|
+
for (const [re, mask] of TOKEN_PATTERNS) out = out.replace(re, mask);
|
|
8850
|
+
return out;
|
|
8851
|
+
}
|
|
8852
|
+
function redactPatch(patch) {
|
|
8853
|
+
if (!patch || typeof patch !== "object") return patch;
|
|
8854
|
+
const out = Array.isArray(patch) ? [...patch] : { ...patch };
|
|
8855
|
+
for (const [k, v] of Object.entries(out)) {
|
|
8856
|
+
if (typeof v === "string") out[k] = redactSecrets(v);
|
|
8857
|
+
else if (v && typeof v === "object") out[k] = redactPatch(v);
|
|
8858
|
+
}
|
|
8859
|
+
return out;
|
|
8860
|
+
}
|
|
8861
|
+
var TOKEN_PATTERNS;
|
|
8862
|
+
var init_redact_tokens = __esm({
|
|
8863
|
+
"../../scripts/virtual-office/code-runner/redact-tokens.mjs"() {
|
|
8864
|
+
"use strict";
|
|
8865
|
+
TOKEN_PATTERNS = [
|
|
8866
|
+
[/\b(?:gh[oprsu]|vocred|npm)_[A-Za-z0-9._-]{10,}\b/gu, "[REDACTED]"],
|
|
8867
|
+
[/\bgithub_pat_[A-Za-z0-9_]{10,}\b/gu, "[REDACTED]"],
|
|
8868
|
+
// Keep the scheme so the line still reads as an auth header, matching
|
|
8869
|
+
// sanitizeMaintenanceDiagnostic's behaviour.
|
|
8870
|
+
[/\bBearer\s+[A-Za-z0-9._~+/-]{10,}=*/giu, "Bearer [REDACTED]"]
|
|
8871
|
+
];
|
|
8872
|
+
}
|
|
8873
|
+
});
|
|
8874
|
+
|
|
8826
8875
|
// ../../scripts/virtual-office/code-runner/task-helpers.mjs
|
|
8827
8876
|
function makeSafeProgress(log3) {
|
|
8828
8877
|
return async (client, id, patch) => {
|
|
8829
8878
|
try {
|
|
8830
|
-
const r = await client.postProgress(id, patch);
|
|
8879
|
+
const r = await client.postProgress(id, redactPatch(patch));
|
|
8831
8880
|
if (r && r.terminal) log3(`task ${id} is terminal server-side; stopping updates`);
|
|
8832
8881
|
return r;
|
|
8833
8882
|
} catch (err) {
|
|
@@ -8853,20 +8902,34 @@ function buildPrBody(task, run, files, { armAutoMerge = false } = {}) {
|
|
|
8853
8902
|
"### Prompt",
|
|
8854
8903
|
"",
|
|
8855
8904
|
"```",
|
|
8856
|
-
String(task.prompt).slice(0, 2e3),
|
|
8905
|
+
redactSecrets(String(task.prompt)).slice(0, 2e3),
|
|
8857
8906
|
"```",
|
|
8858
8907
|
"",
|
|
8859
8908
|
"### Agent summary",
|
|
8860
8909
|
"",
|
|
8861
|
-
String(run.summary || "").slice(0, 2e3),
|
|
8910
|
+
redactSecrets(String(run.summary || "")).slice(0, 2e3),
|
|
8862
8911
|
"",
|
|
8863
8912
|
"---",
|
|
8864
8913
|
armAutoMerge ? "_Opened by the AlgoHQ code-runner daemon. After CI passes, the watcher must obtain a durable consensus receipt and merge the exact verified SHA._" : "_Opened by the AlgoHQ code-runner daemon. This PR awaits the verify-before-act gate / operator review \u2014 it is NOT auto-merged._"
|
|
8865
8914
|
].filter((l) => l !== "").join("\n");
|
|
8866
8915
|
}
|
|
8916
|
+
async function mintRunnerGithubTokens({ client, taskId, log: log3, repo = null, requirePublish = false }) {
|
|
8917
|
+
const publishToken = (await client.getInstallationToken({ required: requirePublish }))?.token ?? null;
|
|
8918
|
+
let agentReadToken = null;
|
|
8919
|
+
let reason = null;
|
|
8920
|
+
try {
|
|
8921
|
+
agentReadToken = (await client.getInstallationToken({ readOnly: true, repo }))?.token ?? null;
|
|
8922
|
+
if (!agentReadToken) reason = "control plane returned no confirmed read-only grant";
|
|
8923
|
+
} catch (err) {
|
|
8924
|
+
reason = err?.message ?? String(err);
|
|
8925
|
+
}
|
|
8926
|
+
if (!agentReadToken) log3(`task ${taskId}: no GitHub read access for the agent (${reason}); it cannot read a private repo`);
|
|
8927
|
+
return { publishToken, agentReadToken };
|
|
8928
|
+
}
|
|
8867
8929
|
var init_task_helpers = __esm({
|
|
8868
8930
|
"../../scripts/virtual-office/code-runner/task-helpers.mjs"() {
|
|
8869
8931
|
"use strict";
|
|
8932
|
+
init_redact_tokens();
|
|
8870
8933
|
}
|
|
8871
8934
|
});
|
|
8872
8935
|
|
|
@@ -8882,8 +8945,12 @@ function safeBaseEnv(env2 = {}) {
|
|
|
8882
8945
|
}
|
|
8883
8946
|
return result;
|
|
8884
8947
|
}
|
|
8885
|
-
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task" } = {}) {
|
|
8948
|
+
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null } = {}) {
|
|
8886
8949
|
const base = safeBaseEnv(env2);
|
|
8950
|
+
if (typeof githubReadToken === "string" && githubReadToken) {
|
|
8951
|
+
base.GH_TOKEN = githubReadToken;
|
|
8952
|
+
base.GITHUB_TOKEN = githubReadToken;
|
|
8953
|
+
}
|
|
8887
8954
|
if (String(env2?.AGENT_ID || "").trim()) return { ...base, AGENT_ID: env2.AGENT_ID };
|
|
8888
8955
|
const generated = [
|
|
8889
8956
|
"vo",
|
|
@@ -9574,7 +9641,7 @@ async function processOneTask(client, task, cfg) {
|
|
|
9574
9641
|
const wt = await Promise.resolve(createFixWorktree("code-task", { source: id.slice(0, 8), repo: task.repo }));
|
|
9575
9642
|
worktreeName = wt.worktreeName;
|
|
9576
9643
|
if (!worktreeName || !wt.worktreeDir) throw new Error("worktree isolation failure \u2014 refusing to run in the main tree");
|
|
9577
|
-
const githubToken =
|
|
9644
|
+
const { publishToken: githubToken, agentReadToken: agentGithubReadToken } = await mintRunnerGithubTokens({ client, taskId: id, log: log2, repo: task.repo, requirePublish: cfg.requireGithubAppAuth });
|
|
9578
9645
|
const parentTask = task.resumed_from ? await client.getTask(task.resumed_from).catch(() => null) : null;
|
|
9579
9646
|
const continuationRestore = await prepareContinuationBranch(wt.worktreeDir, {
|
|
9580
9647
|
task,
|
|
@@ -9609,7 +9676,7 @@ async function processOneTask(client, task, cfg) {
|
|
|
9609
9676
|
model,
|
|
9610
9677
|
effort: effectiveEffort,
|
|
9611
9678
|
maxBudgetUsd: effectiveMaxBudgetUsd,
|
|
9612
|
-
env: buildAgentProcessEnv(process.env, { agent: cfg.agent, runnerId: cfg.runnerId, taskId: id }),
|
|
9679
|
+
env: buildAgentProcessEnv(process.env, { agent: cfg.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken }),
|
|
9613
9680
|
onProgress: (text) => {
|
|
9614
9681
|
void safeProgress(client, id, runnerStagePatch("agent_working", text));
|
|
9615
9682
|
},
|