fix: 保真渲染 AgentRun 多键工具凭据

This commit is contained in:
Codex
2026-07-12 09:23:08 +02:00
parent 9947210f63
commit 54f4379511
4 changed files with 392 additions and 40 deletions
+151
View File
@@ -2,7 +2,9 @@ import { readFileSync, rmSync } from "node:fs";
import { describe, expect, test } from "bun:test";
import { parseAgentRunClientConfigYaml, resolveAgentRunAuth, runAgentRunCommand, stripAgentRunResourceWrapperArgs } from "./agentrun";
import { resolveAgentRunLaneTarget } from "./agentrun-lanes";
import { agentRunToolCredentialRefs } from "./agentrun/config";
import { placeholderAgentRunImage, renderAgentRunGitopsFiles } from "./agentrun-manifests";
import { collectLaneSecretSources, secretSyncScript } from "./agentrun/secrets";
function renderedTextOf(value: unknown): string {
if (typeof value !== "object" || value === null) throw new Error("expected rendered CLI result");
@@ -808,6 +810,155 @@ describe("AgentRun default transport contract", () => {
});
});
describe("AgentRun YAML tool credential binding", () => {
test("aggregates managed GitHub SSH source keys into one volume credential and one sync target", () => {
const { spec } = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" });
const githubSsh = agentRunToolCredentialRefs(spec).filter((credential) => credential.tool === "github" && credential.purpose === "github-ssh");
expect(githubSsh).toEqual([{
sourceId: "github-ssh",
tool: "github",
purpose: "github-ssh",
secretRef: {
namespace: "agentrun-v02",
name: "agentrun-v01-tool-github-ssh",
keys: ["id_ed25519", "known_hosts"],
},
projection: { kind: "volume", mountPath: "/home/agentrun/.ssh" },
valuesPrinted: false,
}]);
const sources = collectLaneSecretSources(spec).filter((source) => source.targetRef.name === "agentrun-v01-tool-github-ssh");
expect(sources.map((source) => ({ id: source.id, sourceMode: source.sourceMode, sourceRef: source.sourceRef, targetRef: source.targetRef }))).toEqual([
{
id: "tool-github-ssh-private-key",
sourceMode: "file",
sourceRef: "deploy-ssh/github.com/id_ed25519",
targetRef: { namespace: "agentrun-v02", name: "agentrun-v01-tool-github-ssh", key: "id_ed25519" },
},
{
id: "tool-github-ssh-known-hosts",
sourceMode: "file",
sourceRef: "deploy-ssh/github.com/known_hosts",
targetRef: { namespace: "agentrun-v02", name: "agentrun-v01-tool-github-ssh", key: "known_hosts" },
},
]);
const syncScript = secretSyncScript(spec, sources.map((source, index) => ({ targetRef: source.targetRef, value: `fixture-${index}` })));
const payloadMatch = syncScript.match(/^payload_b64='([^']+)'$/mu);
expect(payloadMatch).not.toBeNull();
const syncManifest = JSON.parse(Buffer.from(payloadMatch?.[1] ?? "", "base64").toString("utf8")) as Array<{ targetRef: { namespace: string; name: string; key: string } }>;
expect(syncManifest.map((item) => item.targetRef)).toEqual(sources.map((source) => source.targetRef));
expect(new Set(syncManifest.map((item) => `${item.targetRef.namespace}/${item.targetRef.name}`))).toEqual(new Set(["agentrun-v02/agentrun-v01-tool-github-ssh"]));
expect(syncScript).toContain("groups.setdefault(key");
expect(JSON.stringify({ githubSsh, sources })).not.toContain("/root/.ssh");
});
test("preserves multi-key credentials across Aipod render and queue admission and rejects conflicts", async () => {
const renderBodies: Record<string, any>[] = [];
const submittedBodies: Record<string, any>[] = [];
let conflictingKeys = false;
const renderedTask = () => ({
tenantId: "unidesk",
projectId: "pikasTech/unidesk",
queue: "commander",
lane: "v0.1",
backendProfile: "codex",
providerId: "NC01",
workspaceRef: { kind: "opaque", path: "." },
executionPolicy: {
sandbox: "workspace-write",
approval: "never",
timeoutMs: 1_800_000,
network: "enabled",
secretScope: {
allowCredentialEcho: false,
providerCredentials: [{
profile: "codex",
secretRef: { name: "agentrun-v01-provider-codex", keys: ["auth.json", "config.toml"] },
}],
toolCredentials: [
{
tool: "github",
purpose: "github-pr",
secretRef: { name: "agentrun-v01-tool-github-pr", keys: ["GH_TOKEN"] },
projection: { kind: "env", envName: "GH_TOKEN", secretKey: "GH_TOKEN" },
},
{
tool: "unidesk-ssh",
purpose: "ssh-passthrough",
secretRef: { name: "agentrun-v01-tool-unidesk-ssh", keys: ["UNIDESK_SSH_CLIENT_TOKEN"] },
projection: { kind: "env", envName: "UNIDESK_SSH_CLIENT_TOKEN", secretKey: "UNIDESK_SSH_CLIENT_TOKEN" },
},
{
tool: "github",
purpose: "github-ssh",
secretRef: {
name: "agentrun-v01-tool-github-ssh",
keys: conflictingKeys ? ["id_ed25519", "known_hosts", "config"] : ["id_ed25519", "known_hosts"],
},
projection: { kind: "volume", mountPath: "/home/agentrun/.ssh" },
},
],
},
},
payload: { prompt: "fixture" },
});
const server = Bun.serve({
port: 0,
async fetch(request) {
const url = new URL(request.url);
expect(request.headers.get("authorization")).toBe("Bearer secret-value");
if (url.pathname === "/api/v1/aipod-specs/Artificer/render") {
renderBodies.push(await request.json() as Record<string, any>);
return Response.json({ ok: true, data: { queueTask: renderedTask() } });
}
if (url.pathname === "/api/v1/queue/tasks") {
submittedBodies.push(await request.json() as Record<string, any>);
return Response.json({ ok: true, data: { id: "qt_fixture", state: "queued" } });
}
return new Response("not found", { status: 404 });
},
});
const previousConfig = process.env.AGENTRUN_CLIENT_CONFIG;
const previousKey = process.env.HWLAB_API_KEY;
process.env.HWLAB_API_KEY = "secret-value";
const tempConfigPath = `/tmp/unidesk-agentrun-tool-credential-test-${process.pid}-${Date.now()}.yaml`;
try {
process.env.AGENTRUN_CLIENT_CONFIG = tempConfigPath;
await Bun.write(tempConfigPath, agentRunMinimalClientYaml.replace("http://agentrun.example.local:8080", server.url.href.replace(/\/$/u, "")));
const accepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]);
expect(accepted.ok).toBe(true);
expect(renderBodies).toHaveLength(1);
expect(submittedBodies).toHaveLength(1);
for (const body of [renderBodies[0], submittedBodies[0]]) {
const githubSsh = body?.executionPolicy?.secretScope?.toolCredentials?.filter((credential: Record<string, unknown>) => credential.tool === "github" && credential.purpose === "github-ssh");
expect(githubSsh).toEqual([{
tool: "github",
purpose: "github-ssh",
secretRef: { name: "agentrun-v01-tool-github-ssh", keys: ["id_ed25519", "known_hosts"] },
projection: { kind: "volume", mountPath: "/home/agentrun/.ssh" },
}]);
}
conflictingKeys = true;
const rejected = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]);
expect(rejected.ok).toBe(false);
expect(submittedBodies).toHaveLength(1);
const rejectedText = renderedTextOf(rejected);
expect(rejectedText).toContain("validation-failed");
expect(rejectedText).toContain("github/github-ssh keys conflict");
expect(Buffer.byteLength(rejectedText, "utf8")).toBeLessThan(2400);
} finally {
server.stop(true);
if (previousConfig === undefined) delete process.env.AGENTRUN_CLIENT_CONFIG;
else process.env.AGENTRUN_CLIENT_CONFIG = previousConfig;
if (previousKey === undefined) delete process.env.HWLAB_API_KEY;
else process.env.HWLAB_API_KEY = previousKey;
rmSync(tempConfigPath, { force: true });
}
});
});
describe("AgentRun runner API key SecretRef", () => {
test("renders manager runtime authority from the YAML runner SecretRef", () => {
const { spec } = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" });