fix: 收窄 Artificer pika 配置范围
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { readFileSync, rmSync } from "node:fs";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { parseAgentRunClientConfigYaml, resolveAgentRunAuth, runAgentRunCommand, stripAgentRunResourceWrapperArgs } from "./agentrun";
|
||||
import { agentRunProviderCredentialRefs, agentRunProviderCredentialRefsForSelection, resolveAgentRunLaneTarget } from "./agentrun-lanes";
|
||||
import { resolveAgentRunLaneTarget } from "./agentrun-lanes";
|
||||
import { agentRunToolCredentialRefs } from "./agentrun/config";
|
||||
import { placeholderAgentRunImage, renderAgentRunGitopsFiles } from "./agentrun-manifests";
|
||||
import { collectLaneSecretSources, secretSyncScript } from "./agentrun/secrets";
|
||||
@@ -979,101 +979,6 @@ describe("AgentRun default transport contract", () => {
|
||||
});
|
||||
|
||||
describe("AgentRun YAML tool credential binding", () => {
|
||||
test("maps the YAML source profile to one canonical AgentRun backend profile", async () => {
|
||||
const { spec } = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" });
|
||||
const pikaSecrets = spec.secrets.filter((secret) => secret.providerCredentialSourceProfile === "gpt.pika");
|
||||
expect(pikaSecrets.map((secret) => ({
|
||||
id: secret.id,
|
||||
sourceMode: secret.sourceMode,
|
||||
sourceRef: secret.sourceRef,
|
||||
sourceKey: secret.sourceKey,
|
||||
sourceFormat: secret.sourceFormat,
|
||||
transform: secret.transform,
|
||||
codexConfig: secret.codexConfig,
|
||||
jsonValue: secret.jsonValue,
|
||||
sourceProfile: secret.providerCredentialSourceProfile,
|
||||
effectiveBackendProfile: secret.providerCredentialProfile,
|
||||
targetRef: secret.targetRef,
|
||||
}))).toEqual([
|
||||
{
|
||||
id: "provider-gpt-pika-auth-json",
|
||||
sourceMode: "file",
|
||||
sourceRef: "/root/.codex/auth.json.pika",
|
||||
sourceKey: null,
|
||||
sourceFormat: null,
|
||||
transform: null,
|
||||
codexConfig: null,
|
||||
jsonValue: null,
|
||||
sourceProfile: "gpt.pika",
|
||||
effectiveBackendProfile: "gpt-pika",
|
||||
targetRef: { namespace: "agentrun-v02", name: "agentrun-v02-provider-gpt-pika", key: "auth.json" },
|
||||
},
|
||||
{
|
||||
id: "provider-gpt-pika-config",
|
||||
sourceMode: "file",
|
||||
sourceRef: "/root/.codex/config.toml.pika",
|
||||
sourceKey: null,
|
||||
sourceFormat: null,
|
||||
transform: null,
|
||||
codexConfig: null,
|
||||
jsonValue: null,
|
||||
sourceProfile: "gpt.pika",
|
||||
effectiveBackendProfile: "gpt-pika",
|
||||
targetRef: { namespace: "agentrun-v02", name: "agentrun-v02-provider-gpt-pika", key: "config.toml" },
|
||||
},
|
||||
]);
|
||||
const renderedPikaSources = collectLaneSecretSources(spec).filter((source) => source.providerCredentialSourceProfile === "gpt.pika");
|
||||
expect(renderedPikaSources.map((source) => ({
|
||||
id: source.id,
|
||||
sourceMode: source.sourceMode,
|
||||
sourceRef: source.sourceRef,
|
||||
sourceKey: source.sourceKey,
|
||||
sourceFormat: source.sourceFormat,
|
||||
transform: source.transform,
|
||||
codexConfig: source.codexConfig,
|
||||
jsonValue: source.jsonValue,
|
||||
sourceProfile: source.providerCredentialSourceProfile,
|
||||
effectiveBackendProfile: source.providerCredentialProfile,
|
||||
targetRef: source.targetRef,
|
||||
}))).toEqual(pikaSecrets.map((secret) => ({
|
||||
id: secret.id,
|
||||
sourceMode: secret.sourceMode,
|
||||
sourceRef: secret.sourceRef,
|
||||
sourceKey: secret.sourceKey,
|
||||
sourceFormat: secret.sourceFormat,
|
||||
transform: secret.transform,
|
||||
codexConfig: secret.codexConfig,
|
||||
jsonValue: secret.jsonValue,
|
||||
sourceProfile: secret.providerCredentialSourceProfile,
|
||||
effectiveBackendProfile: secret.providerCredentialProfile,
|
||||
targetRef: secret.targetRef,
|
||||
})));
|
||||
const expected = [{
|
||||
sourceProfile: "gpt.pika",
|
||||
profile: "gpt-pika",
|
||||
secretRef: {
|
||||
namespace: "agentrun-v02",
|
||||
name: "agentrun-v02-provider-gpt-pika",
|
||||
keys: ["auth.json", "config.toml"],
|
||||
},
|
||||
}];
|
||||
expect(agentRunProviderCredentialRefsForSelection(spec, "gpt.pika")).toEqual(expected);
|
||||
expect(agentRunProviderCredentialRefs(spec, "gpt-pika")).toEqual(expected);
|
||||
expect(agentRunProviderCredentialRefs(spec, "gpt.pika")).toEqual([]);
|
||||
|
||||
const invalidPath = `/tmp/unidesk-agentrun-invalid-profile-${process.pid}-${Date.now()}.yaml`;
|
||||
try {
|
||||
const invalid = readFileSync("config/agentrun.yaml", "utf8").replaceAll(" profile: gpt-pika", " profile: gpt.pika");
|
||||
await Bun.write(invalidPath, invalid);
|
||||
expect(() => resolveAgentRunLaneTarget(
|
||||
{ node: "NC01", lane: "nc01-v02" },
|
||||
{ ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: invalidPath },
|
||||
)).toThrow("canonical AgentRun backend profile slug");
|
||||
} finally {
|
||||
rmSync(invalidPath, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
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");
|
||||
@@ -1115,290 +1020,6 @@ describe("AgentRun YAML tool credential binding", () => {
|
||||
expect(JSON.stringify({ githubSsh, sources })).not.toContain("/root/.ssh");
|
||||
});
|
||||
|
||||
test("shares model overrides across Aipod render, create, and send without overriding provider authority", async () => {
|
||||
const renderBodies: Record<string, any>[] = [];
|
||||
const submittedBodies: Record<string, any>[] = [];
|
||||
const sendBodies: Record<string, any>[] = [];
|
||||
let renderedBackendProfile = "gpt-pika";
|
||||
let renderedCredentialProfile = "gpt-pika";
|
||||
let renderedSecretName = "agentrun-v02-provider-gpt-pika";
|
||||
let resolutionModelOverride: string | null = null;
|
||||
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") {
|
||||
const input = await request.json() as Record<string, any>;
|
||||
renderBodies.push(input);
|
||||
const model = input.model ?? "gpt-5.6-terra";
|
||||
const reasoningEffort = input.reasoningEffort ?? "xhigh";
|
||||
const modelSource = input.model === undefined ? "aipod-spec-default" : "render-input-override";
|
||||
const reasoningEffortSource = input.reasoningEffort === undefined ? "aipod-spec-default" : "render-input-override";
|
||||
return Response.json({
|
||||
ok: true,
|
||||
data: {
|
||||
queueTask: {
|
||||
tenantId: "unidesk",
|
||||
projectId: "pikasTech/unidesk",
|
||||
queue: "commander",
|
||||
lane: "v0.2",
|
||||
providerId: "NC01",
|
||||
backendProfile: renderedBackendProfile,
|
||||
workspaceRef: input.workspaceRef ?? { kind: "opaque", path: "." },
|
||||
executionPolicy: {
|
||||
sandbox: "workspace-write",
|
||||
approval: "never",
|
||||
timeoutMs: 1_800_000,
|
||||
network: "enabled",
|
||||
secretScope: {
|
||||
allowCredentialEcho: false,
|
||||
providerCredentials: [{
|
||||
profile: renderedCredentialProfile,
|
||||
secretRef: {
|
||||
namespace: "agentrun-v02",
|
||||
name: renderedSecretName,
|
||||
keys: ["auth.json", "config.toml"],
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
payload: {
|
||||
prompt: input.prompt ?? "fixture",
|
||||
model,
|
||||
reasoningEffort,
|
||||
modelConfig: { model, reasoningEffort, modelSource, reasoningEffortSource, valuesPrinted: false },
|
||||
},
|
||||
},
|
||||
dispatchDefaults: {
|
||||
runnerJob: { namespace: "agentrun-v02" },
|
||||
},
|
||||
modelResolution: {
|
||||
model: resolutionModelOverride ?? model,
|
||||
reasoningEffort,
|
||||
modelSource,
|
||||
reasoningEffortSource,
|
||||
modelOverridden: input.model !== undefined,
|
||||
reasoningEffortOverridden: input.reasoningEffort !== undefined,
|
||||
backendProfile: "gpt-pika",
|
||||
backendProfileSource: "aipod-spec",
|
||||
providerCredential: {
|
||||
profile: "gpt-pika",
|
||||
secretRef: {
|
||||
namespace: "agentrun-v02",
|
||||
name: "agentrun-v02-provider-gpt-pika",
|
||||
keys: ["auth.json", "config.toml"],
|
||||
valuesPrinted: false,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (url.pathname === "/api/v1/queue/tasks") {
|
||||
submittedBodies.push(await request.json() as Record<string, any>);
|
||||
return Response.json({ ok: true, data: { id: "qt_gpt_pika", state: "pending" } });
|
||||
}
|
||||
if (url.pathname === "/api/v1/sessions/ses_gpt_pika/send") {
|
||||
const body = await request.json() as Record<string, any>;
|
||||
sendBodies.push(body);
|
||||
return Response.json({
|
||||
ok: true,
|
||||
data: {
|
||||
action: "session-send-plan",
|
||||
dryRun: body.dryRun,
|
||||
mutation: false,
|
||||
sessionId: "ses_gpt_pika",
|
||||
decision: "turn",
|
||||
internalCommandType: "turn",
|
||||
request: { method: "POST", path: url.pathname, createRunnerJob: true, valuesPrinted: false },
|
||||
valuesPrinted: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
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-gpt-pika-${process.pid}-${Date.now()}.yaml`;
|
||||
const tempInputPath = `/tmp/unidesk-agentrun-gpt-pika-input-${process.pid}-${Date.now()}.json`;
|
||||
try {
|
||||
process.env.AGENTRUN_CLIENT_CONFIG = tempConfigPath;
|
||||
await Bun.write(tempConfigPath, agentRunMinimalClientYaml.replace("http://agentrun.example.local:8080", server.url.href.replace(/\/$/u, "")));
|
||||
|
||||
const defaultPlan = JSON.parse(renderedTextOf(await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "default fixture", "--dry-run", "-o", "json",
|
||||
]))) as Record<string, any>;
|
||||
expect(defaultPlan.aipodBinding).toMatchObject({
|
||||
sourceProfile: "gpt.pika",
|
||||
effectiveBackendProfile: "gpt-pika",
|
||||
backendProfileSource: "aipod-spec",
|
||||
model: "gpt-5.6-terra",
|
||||
reasoningEffort: "xhigh",
|
||||
modelSource: "aipod-spec-default",
|
||||
reasoningEffortSource: "aipod-spec-default",
|
||||
modelOverridden: false,
|
||||
reasoningEffortOverridden: false,
|
||||
valuesPrinted: false,
|
||||
});
|
||||
expect(defaultPlan.aipodBinding.providerCredentials).toEqual([{
|
||||
profile: "gpt-pika",
|
||||
secretRef: { name: "agentrun-v02-provider-gpt-pika", keys: ["auth.json", "config.toml"] },
|
||||
valuesPrinted: false,
|
||||
}]);
|
||||
expect(renderBodies[0]).toEqual({ prompt: "default fixture" });
|
||||
|
||||
const override = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "override fixture",
|
||||
"--model", "gpt-5.6-terra-alt", "--reasoning-effort", "xhigh", "-o", "json",
|
||||
]);
|
||||
expect(override.ok).toBe(true);
|
||||
expect(renderBodies[1]).toMatchObject({
|
||||
prompt: "override fixture",
|
||||
model: "gpt-5.6-terra-alt",
|
||||
reasoningEffort: "xhigh",
|
||||
});
|
||||
expect(renderBodies[1]?.backendProfile).toBeUndefined();
|
||||
expect(renderBodies[1]?.executionPolicy).toBeUndefined();
|
||||
expect(submittedBodies[0]).toMatchObject({
|
||||
backendProfile: "gpt-pika",
|
||||
payload: { model: "gpt-5.6-terra-alt", reasoningEffort: "xhigh" },
|
||||
executionPolicy: {
|
||||
secretScope: {
|
||||
providerCredentials: [{
|
||||
profile: "gpt-pika",
|
||||
secretRef: { name: "agentrun-v02-provider-gpt-pika", keys: ["auth.json", "config.toml"] },
|
||||
}],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const send = await runAgentRunCommand(null, [
|
||||
"send", "session/ses_gpt_pika", "--aipod", "Artificer", "--prompt", "send fixture",
|
||||
"--model", "gpt-5.6-terra-send", "--reasoning-effort", "medium", "--dry-run", "-o", "json",
|
||||
]);
|
||||
expect(send.ok).toBe(true);
|
||||
const sendProjection = JSON.parse(renderedTextOf(send)) as Record<string, any>;
|
||||
expect(sendProjection.aipodBinding).toMatchObject({
|
||||
sourceProfile: "gpt.pika",
|
||||
effectiveBackendProfile: "gpt-pika",
|
||||
model: "gpt-5.6-terra-send",
|
||||
reasoningEffort: "medium",
|
||||
modelSource: "render-input-override",
|
||||
reasoningEffortSource: "render-input-override",
|
||||
modelOverridden: true,
|
||||
reasoningEffortOverridden: true,
|
||||
valuesPrinted: false,
|
||||
});
|
||||
expect(renderBodies[2]).toMatchObject({ model: "gpt-5.6-terra-send", reasoningEffort: "medium" });
|
||||
expect(sendBodies[0]).toMatchObject({
|
||||
dryRun: true,
|
||||
run: { backendProfile: "gpt-pika" },
|
||||
payload: { model: "gpt-5.6-terra-send", reasoningEffort: "medium" },
|
||||
});
|
||||
|
||||
const render = await runAgentRunCommand(null, [
|
||||
"aipod-specs", "render", "Artificer", "--model", "gpt-5.6-terra-render", "--reasoning-effort", "low", "-o", "json",
|
||||
]);
|
||||
expect(render.ok).toBe(true);
|
||||
expect(renderBodies[3]).toEqual({ model: "gpt-5.6-terra-render", reasoningEffort: "low" });
|
||||
|
||||
const createHuman = renderedTextOf(await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "human create", "--dry-run",
|
||||
]));
|
||||
const sendHuman = renderedTextOf(await runAgentRunCommand(null, [
|
||||
"send", "session/ses_gpt_pika", "--aipod", "Artificer", "--prompt", "human send",
|
||||
"--model", "gpt-5.6-terra-human", "--reasoning-effort", "medium", "--dry-run",
|
||||
]));
|
||||
const renderHuman = renderedTextOf(await runAgentRunCommand(null, [
|
||||
"aipod-specs", "render", "Artificer", "--model", "gpt-5.6-terra-human", "--reasoning-effort", "medium",
|
||||
]));
|
||||
for (const human of [createHuman, sendHuman, renderHuman]) {
|
||||
expect(human).toContain("sourceProfile=gpt.pika");
|
||||
expect(human).toContain("effectiveBackendProfile=gpt-pika");
|
||||
expect(human).toContain("model=");
|
||||
expect(human).toContain("reasoningEffort=");
|
||||
expect(human).toContain("valuesPrinted=false");
|
||||
expect(human).not.toContain("secret-value");
|
||||
}
|
||||
expect(createHuman).toContain("modelSource=aipod-spec-default");
|
||||
expect(sendHuman).toContain("modelSource=render-input-override");
|
||||
expect(renderHuman).toContain("modelSource=render-input-override");
|
||||
|
||||
for (const helpArgs of [["create", "--help"], ["send", "--help"], ["aipod-specs", "render", "--help"]]) {
|
||||
const help = renderedTextOf(await runAgentRunCommand(null, helpArgs));
|
||||
expect(help).toContain("--model <name>");
|
||||
expect(help).toContain("--reasoning-effort <level>");
|
||||
}
|
||||
|
||||
resolutionModelOverride = "gpt-5.6-terra-conflict";
|
||||
const resolutionRejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "resolution conflict", "-o", "json",
|
||||
]);
|
||||
expect(resolutionRejected.ok).toBe(false);
|
||||
expect(renderedTextOf(resolutionRejected)).toContain("queueTask.payload.model must match modelResolution.model");
|
||||
expect(submittedBodies).toHaveLength(1);
|
||||
resolutionModelOverride = null;
|
||||
|
||||
const requestCountBeforeRejections = renderBodies.length;
|
||||
for (const forbidden of [
|
||||
["--backend-profile", "gpt-pika"],
|
||||
["--profile", "gpt-pika"],
|
||||
["--execution-policy-json", "{}"],
|
||||
]) {
|
||||
const rejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "rejected", ...forbidden, "-o", "json",
|
||||
]);
|
||||
expect(rejected.ok).toBe(false);
|
||||
expect(renderedTextOf(rejected)).toContain("AipodSpec owns backendProfile");
|
||||
}
|
||||
await Bun.write(tempInputPath, JSON.stringify({ payload: { prompt: "nested", model: "gpt-5.6-terra-nested" } }));
|
||||
const nestedRejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--json-file", tempInputPath, "-o", "json",
|
||||
]);
|
||||
expect(nestedRejected.ok).toBe(false);
|
||||
expect(renderedTextOf(nestedRejected)).toContain("payload.model");
|
||||
await Bun.write(tempInputPath, JSON.stringify({ modelConfig: { model: "gpt-5.6-terra-nested" } }));
|
||||
const modelConfigRejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--json-file", tempInputPath, "-o", "json",
|
||||
]);
|
||||
expect(modelConfigRejected.ok).toBe(false);
|
||||
expect(renderedTextOf(modelConfigRejected)).toContain("modelConfig");
|
||||
expect(renderBodies).toHaveLength(requestCountBeforeRejections);
|
||||
|
||||
renderedBackendProfile = "gpt.pika";
|
||||
renderedCredentialProfile = "gpt.pika";
|
||||
const dottedRejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "dotted", "-o", "json",
|
||||
]);
|
||||
expect(dottedRejected.ok).toBe(false);
|
||||
expect(renderedTextOf(dottedRejected)).toContain("backendProfile=gpt.pika");
|
||||
expect(submittedBodies).toHaveLength(1);
|
||||
|
||||
renderedBackendProfile = "gpt-pika";
|
||||
renderedCredentialProfile = "gpt-pika";
|
||||
renderedSecretName = "agentrun-v01-provider-codex";
|
||||
const secretRejected = await runAgentRunCommand(null, [
|
||||
"create", "task", "--aipod", "Artificer", "--prompt", "wrong secret", "-o", "json",
|
||||
]);
|
||||
expect(secretRejected.ok).toBe(false);
|
||||
expect(renderedTextOf(secretRejected)).toContain("is not the unique YAML binding");
|
||||
expect(submittedBodies).toHaveLength(1);
|
||||
} 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 });
|
||||
rmSync(tempInputPath, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("preserves explicit credential subsets across Aipod admission and rejects unknown identities or conflicts", async () => {
|
||||
const renderBodies: Record<string, any>[] = [];
|
||||
const submittedBodies: Record<string, any>[] = [];
|
||||
@@ -1460,40 +1081,8 @@ describe("AgentRun YAML tool credential binding", () => {
|
||||
toolCredentials: renderedToolCredentials(),
|
||||
},
|
||||
},
|
||||
payload: {
|
||||
prompt: "fixture",
|
||||
model: "gpt-5.5",
|
||||
reasoningEffort: "high",
|
||||
modelConfig: {
|
||||
model: "gpt-5.5",
|
||||
reasoningEffort: "high",
|
||||
modelSource: "aipod-spec-default",
|
||||
reasoningEffortSource: "aipod-spec-default",
|
||||
valuesPrinted: false,
|
||||
},
|
||||
},
|
||||
payload: { prompt: "fixture" },
|
||||
});
|
||||
const modelResolution = {
|
||||
model: "gpt-5.5",
|
||||
reasoningEffort: "high",
|
||||
modelSource: "aipod-spec-default",
|
||||
reasoningEffortSource: "aipod-spec-default",
|
||||
modelOverridden: false,
|
||||
reasoningEffortOverridden: false,
|
||||
backendProfile: "codex",
|
||||
backendProfileSource: "aipod-spec",
|
||||
providerCredential: {
|
||||
profile: "codex",
|
||||
secretRef: {
|
||||
namespace: "agentrun-v02",
|
||||
name: "agentrun-v01-provider-codex",
|
||||
keys: ["auth.json", "config.toml"],
|
||||
valuesPrinted: false,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(request) {
|
||||
@@ -1501,7 +1090,7 @@ describe("AgentRun YAML tool credential binding", () => {
|
||||
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(), modelResolution } });
|
||||
return Response.json({ ok: true, data: { queueTask: renderedTask() } });
|
||||
}
|
||||
if (url.pathname === "/api/v1/queue/tasks") {
|
||||
submittedBodies.push(await request.json() as Record<string, any>);
|
||||
@@ -1518,54 +1107,22 @@ describe("AgentRun YAML tool credential binding", () => {
|
||||
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"]);
|
||||
const accepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "--model", "gpt-explicit", "--reasoning-effort", "high", "-o", "json"]);
|
||||
expect(accepted.ok).toBe(true);
|
||||
expect(renderBodies).toHaveLength(1);
|
||||
expect(submittedBodies).toHaveLength(1);
|
||||
expect(renderBodies[0]?.backendProfile).toBeUndefined();
|
||||
expect(renderBodies[0]?.executionPolicy).toBeUndefined();
|
||||
expect(submittedBodies[0]?.executionPolicy?.secretScope?.toolCredentials?.map((credential: Record<string, unknown>) => `${credential.tool}/${credential.purpose}`)).toEqual([
|
||||
"github/github-pr",
|
||||
"unidesk-ssh/ssh-passthrough",
|
||||
"github/github-ssh",
|
||||
]);
|
||||
const githubSsh = submittedBodies[0]?.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" },
|
||||
}]);
|
||||
expect(renderBodies[0]?.model).toBe("gpt-explicit");
|
||||
expect(renderBodies[0]?.reasoningEffort).toBe("high");
|
||||
expect(submittedBodies[0]).toEqual(renderedTask());
|
||||
|
||||
credentialMode = "subset";
|
||||
const subsetAccepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]);
|
||||
expect(subsetAccepted.ok).toBe(true);
|
||||
expect(renderBodies).toHaveLength(2);
|
||||
expect(submittedBodies).toHaveLength(2);
|
||||
expect(submittedBodies[1]?.executionPolicy?.secretScope?.toolCredentials).toEqual([{
|
||||
tool: "github",
|
||||
purpose: "github-pr",
|
||||
secretRef: { name: "agentrun-v01-tool-github-pr", keys: ["GH_TOKEN"] },
|
||||
projection: { kind: "env", envName: "GH_TOKEN", secretKey: "GH_TOKEN" },
|
||||
}]);
|
||||
|
||||
credentialMode = "conflict";
|
||||
const rejected = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]);
|
||||
expect(rejected.ok).toBe(false);
|
||||
expect(submittedBodies).toHaveLength(2);
|
||||
const rejectedText = renderedTextOf(rejected);
|
||||
expect(rejectedText).toContain("validation-failed");
|
||||
expect(rejectedText).toContain("github/github-ssh keys conflict");
|
||||
expect(Buffer.byteLength(rejectedText, "utf8")).toBeLessThan(2400);
|
||||
|
||||
credentialMode = "unknown";
|
||||
const unknownRejected = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]);
|
||||
expect(unknownRejected.ok).toBe(false);
|
||||
expect(submittedBodies).toHaveLength(2);
|
||||
const unknownText = renderedTextOf(unknownRejected);
|
||||
expect(unknownText).toContain("validation-failed");
|
||||
expect(unknownText).toContain("artifact-store/publish is not declared by YAML lane");
|
||||
expect(Buffer.byteLength(unknownText, "utf8")).toBeLessThan(2400);
|
||||
expect(submittedBodies[1]).toEqual(renderedTask());
|
||||
} finally {
|
||||
server.stop(true);
|
||||
if (previousConfig === undefined) delete process.env.AGENTRUN_CLIENT_CONFIG;
|
||||
|
||||
Reference in New Issue
Block a user