feat: 装配 UniDesk SSH 工具凭证

This commit is contained in:
Codex
2026-06-02 15:40:48 +08:00
parent 16b32af9b5
commit 458d814fa2
7 changed files with 99 additions and 21 deletions
+13 -1
View File
@@ -4,6 +4,7 @@ import { AgentRunError } from "./errors.js";
import { backendProfileSpec, backendProfiles, isBackendProfile } from "./backend-profiles.js";
const allowedTenants = new Set(["unidesk", "hwlab"]);
const allowedToolCredentials = ["github", "unidesk-ssh"] as const;
export function nowIso(): string {
return new Date().toISOString();
@@ -162,7 +163,7 @@ function validateToolCredentials(value: unknown): NonNullable<ExecutionPolicy["s
return value.map((credential, index) => {
const item = asRecord(credential, `toolCredentials[${index}]`);
const tool = requiredString(item, "tool");
if (tool !== "github") throw new AgentRunError("schema-invalid", `tool credential ${tool} is not supported in v0.1`, { httpStatus: 400, details: { allowedTools: ["github"] } });
if (!allowedToolCredentials.includes(tool as (typeof allowedToolCredentials)[number])) throw new AgentRunError("schema-invalid", `tool credential ${tool} is not supported in v0.1`, { httpStatus: 400, details: { allowedTools: [...allowedToolCredentials] } });
const purpose = optionalString(item.purpose);
const secretRef = validateSecretRef(asRecord(item.secretRef, `toolCredentials[${index}].secretRef`));
const keys = secretRef.keys ?? [];
@@ -174,6 +175,7 @@ function validateToolCredentials(value: unknown): NonNullable<ExecutionPolicy["s
validateEnvName(envName, `toolCredentials[${index}].projection.envName`);
const secretKey = optionalString(projection.secretKey) ?? keys[0];
if (!secretKey || !keys.includes(secretKey)) throw new AgentRunError("schema-invalid", `tool credential ${tool} projection.secretKey must be included in secretRef.keys`, { httpStatus: 400 });
validateToolCredentialProjection(tool, envName, secretKey, keys, index);
const identity = `${tool}:${purpose ?? ""}:${envName}`;
if (seen.has(identity)) throw new AgentRunError("schema-invalid", `tool credential projection ${identity} is duplicated`, { httpStatus: 400 });
seen.add(identity);
@@ -181,6 +183,16 @@ function validateToolCredentials(value: unknown): NonNullable<ExecutionPolicy["s
});
}
function validateToolCredentialProjection(tool: string, envName: string, secretKey: string, keys: string[], index: number): void {
if (tool !== "unidesk-ssh") return;
if (!keys.includes("UNIDESK_SSH_CLIENT_TOKEN")) {
throw new AgentRunError("schema-invalid", `toolCredentials[${index}] unidesk-ssh secretRef.keys must include UNIDESK_SSH_CLIENT_TOKEN`, { httpStatus: 400 });
}
if (envName !== "UNIDESK_SSH_CLIENT_TOKEN" || secretKey !== "UNIDESK_SSH_CLIENT_TOKEN") {
throw new AgentRunError("schema-invalid", `toolCredentials[${index}] unidesk-ssh must project UNIDESK_SSH_CLIENT_TOKEN from the same Secret key`, { httpStatus: 400 });
}
}
export function validateEnvName(name: string, fieldName: string): void {
if (!/^[A-Z_][A-Z0-9_]{0,63}$/u.test(name)) throw new AgentRunError("schema-invalid", `${fieldName} must be an uppercase env name`, { httpStatus: 400 });
}