feat: add aipod spec Artificer assembly
This commit is contained in:
+49
-10
@@ -89,7 +89,8 @@ export function validateResourceBundleRef(value: unknown): ResourceBundleRef | n
|
||||
if (commitId) validateCommitId(commitId, "resourceBundleRef.commitId");
|
||||
const ref = validateGitRef(record.ref, "resourceBundleRef.ref");
|
||||
rejectLegacyResourceBundleFields(record);
|
||||
const result: ResourceBundleRef = { kind: "gitbundle", repoUrl, ...(commitId ? { commitId } : {}), ...(ref ? { ref } : {}), bundles: validateResourceGitBundles(record.bundles, repoUrl, commitId, ref) };
|
||||
const gitMirror = validateGitMirror(record.gitMirror);
|
||||
const result: ResourceBundleRef = { kind: "gitbundle", repoUrl, ...(commitId ? { commitId } : {}), ...(ref ? { ref } : {}), ...(gitMirror ? { gitMirror } : {}), bundles: validateResourceGitBundles(record.bundles, repoUrl, commitId, ref) };
|
||||
if (record.promptRefs !== undefined) result.promptRefs = validateResourcePromptRefs(record.promptRefs);
|
||||
if (record.requiredSkills !== undefined) result.requiredSkills = validateResourceRequiredSkills(record.requiredSkills);
|
||||
if (record.submodules !== undefined && record.submodules !== false) throw new AgentRunError("schema-invalid", "resourceBundleRef.submodules can only be false in v0.1", { httpStatus: 400 });
|
||||
@@ -100,6 +101,27 @@ export function validateResourceBundleRef(value: unknown): ResourceBundleRef | n
|
||||
return result;
|
||||
}
|
||||
|
||||
function validateGitMirror(value: unknown): NonNullable<ResourceBundleRef["gitMirror"]> | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const record = asRecord(value, "resourceBundleRef.gitMirror");
|
||||
const enabled = record.enabled === undefined ? true : record.enabled;
|
||||
if (typeof enabled !== "boolean") throw new AgentRunError("schema-invalid", "resourceBundleRef.gitMirror.enabled must be boolean", { httpStatus: 400 });
|
||||
const baseUrl = optionalString(record.baseUrl);
|
||||
if (baseUrl) validateGitMirrorBaseUrl(baseUrl);
|
||||
return { enabled, ...(baseUrl ? { baseUrl } : {}) };
|
||||
}
|
||||
|
||||
function validateGitMirrorBaseUrl(value: string): void {
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(value);
|
||||
} catch {
|
||||
throw new AgentRunError("schema-invalid", "resourceBundleRef.gitMirror.baseUrl must be an http(s) URL", { httpStatus: 400 });
|
||||
}
|
||||
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new AgentRunError("schema-invalid", "resourceBundleRef.gitMirror.baseUrl must use http or https", { httpStatus: 400 });
|
||||
if (parsed.username || parsed.password || parsed.search || parsed.hash) throw new AgentRunError("schema-invalid", "resourceBundleRef.gitMirror.baseUrl must not include credentials, query, or fragment", { httpStatus: 400 });
|
||||
}
|
||||
|
||||
function rejectLegacyResourceBundleFields(record: JsonRecord): void {
|
||||
for (const field of ["toolAliases", "skillRefs", "workspaceFiles", "subdir", "sparsePaths"] as const) {
|
||||
if (record[field] !== undefined) throw new AgentRunError("schema-invalid", `resourceBundleRef.${field} is removed; use resourceBundleRef.bundles[] with kind=gitbundle`, { httpStatus: 400 });
|
||||
@@ -241,21 +263,38 @@ function validateToolCredentials(value: unknown): NonNullable<ExecutionPolicy["s
|
||||
if (keys.length === 0) throw new AgentRunError("schema-invalid", `tool credential ${tool} secretRef.keys must not be empty`, { httpStatus: 400 });
|
||||
const projection = asRecord(item.projection, `toolCredentials[${index}].projection`);
|
||||
const kind = requiredString(projection, "kind");
|
||||
if (kind !== "env") throw new AgentRunError("schema-invalid", "toolCredentials[].projection.kind must be env in v0.1", { httpStatus: 400 });
|
||||
const normalizedProjection = validateToolCredentialProjection(tool, projection, keys, index);
|
||||
const identity = `${tool}:${purpose ?? ""}:${kind}:${normalizedProjection.kind === "env" ? normalizedProjection.envName : normalizedProjection.mountPath}`;
|
||||
if (seen.has(identity)) throw new AgentRunError("schema-invalid", `tool credential projection ${identity} is duplicated`, { httpStatus: 400 });
|
||||
seen.add(identity);
|
||||
return { tool, ...(purpose ? { purpose } : {}), secretRef, projection: normalizedProjection };
|
||||
});
|
||||
}
|
||||
|
||||
function validateToolCredentialProjection(tool: string, projection: JsonRecord, keys: string[], index: number): NonNullable<ExecutionPolicy["secretScope"]["toolCredentials"]>[number]["projection"] {
|
||||
const kind = requiredString(projection, "kind");
|
||||
if (kind === "env") {
|
||||
const envName = requiredString(projection, "envName");
|
||||
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);
|
||||
return { tool, ...(purpose ? { purpose } : {}), secretRef, projection: { kind: "env", envName, secretKey } };
|
||||
});
|
||||
if (tool === "unidesk-ssh") validateUnideskSshProjection(envName, secretKey, keys, index);
|
||||
return { kind: "env", envName, secretKey };
|
||||
}
|
||||
|
||||
if (kind === "volume") {
|
||||
const mountPath = requiredString(projection, "mountPath");
|
||||
if (!mountPath.startsWith("/home/agentrun/") || mountPath.includes("..")) {
|
||||
throw new AgentRunError("schema-invalid", `toolCredentials[${index}].projection.mountPath must stay under /home/agentrun`, { httpStatus: 400 });
|
||||
}
|
||||
if (tool === "unidesk-ssh") throw new AgentRunError("schema-invalid", `toolCredentials[${index}] unidesk-ssh must use env projection`, { httpStatus: 400 });
|
||||
return { kind: "volume", mountPath };
|
||||
}
|
||||
|
||||
throw new AgentRunError("schema-invalid", "toolCredentials[].projection.kind must be env or volume in v0.1", { httpStatus: 400 });
|
||||
}
|
||||
|
||||
function validateToolCredentialProjection(tool: string, envName: string, secretKey: string, keys: string[], index: number): void {
|
||||
if (tool !== "unidesk-ssh") return;
|
||||
function validateUnideskSshProjection(envName: string, secretKey: string, keys: string[], index: number): void {
|
||||
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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user