fix: 支持动态 provider profile slug
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { BackendProfile, JsonRecord } from "./types.js";
|
||||
|
||||
export const backendProfileIdPattern = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
||||
|
||||
export interface BackendProfileSpec {
|
||||
profile: BackendProfile;
|
||||
backendKind: "codex-app-server-stdio";
|
||||
@@ -13,7 +15,7 @@ export interface BackendProfileSpec {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const backendProfileSpecs: readonly BackendProfileSpec[] = [
|
||||
const builtinBackendProfileSpecs: readonly BackendProfileSpec[] = [
|
||||
{
|
||||
profile: "codex",
|
||||
backendKind: "codex-app-server-stdio",
|
||||
@@ -64,14 +66,40 @@ export const backendProfileSpecs: readonly BackendProfileSpec[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const backendProfiles = backendProfileSpecs.map((item) => item.profile) as readonly BackendProfile[];
|
||||
export const backendProfileSpecs = builtinBackendProfileSpecs;
|
||||
|
||||
export const backendProfiles = builtinBackendProfileSpecs.map((item) => item.profile) as readonly BackendProfile[];
|
||||
|
||||
export function defaultSecretNameForProfile(profile: string): string {
|
||||
return `agentrun-v01-provider-${profile}`;
|
||||
}
|
||||
|
||||
function dynamicBackendProfileSpec(profile: string): BackendProfileSpec {
|
||||
return {
|
||||
profile,
|
||||
backendKind: "codex-app-server-stdio",
|
||||
protocol: "codex-app-server-jsonrpc-stdio",
|
||||
transport: "stdio",
|
||||
command: "codex app-server --listen stdio://",
|
||||
status: "registered",
|
||||
requiredSecretKeys: ["auth.json", "config.toml"],
|
||||
defaultSecretName: defaultSecretNameForProfile(profile),
|
||||
profileIsolation: "profile-scoped-codex-home",
|
||||
description: `Dynamic Codex-compatible profile ${profile}`,
|
||||
};
|
||||
}
|
||||
|
||||
export function backendProfileSpec(profile: string): BackendProfileSpec | null {
|
||||
return backendProfileSpecs.find((item) => item.profile === profile) ?? null;
|
||||
if (!isBackendProfileSlug(profile)) return null;
|
||||
return builtinBackendProfileSpecs.find((item) => item.profile === profile) ?? dynamicBackendProfileSpec(profile);
|
||||
}
|
||||
|
||||
export function isBackendProfileSlug(value: string): boolean {
|
||||
return backendProfileIdPattern.test(value);
|
||||
}
|
||||
|
||||
export function isBackendProfile(value: string): value is BackendProfile {
|
||||
return backendProfileSpec(value) !== null;
|
||||
return isBackendProfileSlug(value);
|
||||
}
|
||||
|
||||
export function backendCapability(spec: BackendProfileSpec): JsonRecord {
|
||||
@@ -111,7 +139,7 @@ export function mergeBackendCapability(profile: string, storedCapabilities: Json
|
||||
}
|
||||
|
||||
export function backendCapabilities(): JsonRecord[] {
|
||||
return backendProfileSpecs.map(backendCapability);
|
||||
return builtinBackendProfileSpecs.map(backendCapability);
|
||||
}
|
||||
|
||||
export function backendCapabilitiesSqlValues(profiles?: readonly BackendProfile[]): string {
|
||||
@@ -121,7 +149,7 @@ export function backendCapabilitiesSqlValues(profiles?: readonly BackendProfile[
|
||||
if (!spec) throw new Error(`unknown backend profile for SQL seed: ${profile}`);
|
||||
return spec;
|
||||
})
|
||||
: backendProfileSpecs;
|
||||
: builtinBackendProfileSpecs;
|
||||
return specs.map((spec) => {
|
||||
const capabilities = JSON.stringify({
|
||||
backendKind: spec.backendKind,
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export type FailureKind =
|
||||
export type RunStatus = "pending" | "claimed" | "running" | "completed" | "failed" | "blocked" | "cancelled";
|
||||
export type CommandState = "pending" | "acknowledged" | "completed" | "failed" | "cancelled";
|
||||
export type TerminalStatus = "completed" | "failed" | "blocked" | "cancelled";
|
||||
export type BackendProfile = "codex" | "deepseek" | "minimax-m3" | "dsflash-go";
|
||||
export type BackendProfile = string;
|
||||
export type QueueTaskState = "pending" | "running" | "completed" | "failed" | "blocked" | "cancelled";
|
||||
export type SessionExecutionState = "idle" | "running" | "terminal";
|
||||
export type SessionAttentionState = "active" | "unread" | "read";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import type { BackendProfile, CreateCommandInput, CreateQueueTaskInput, CreateRunInput, ExecutionPolicy, JsonRecord, JsonValue, QueueTaskState, ResourceBundleRef, SecretRef, SessionListState, SessionRef } from "./types.js";
|
||||
import { AgentRunError } from "./errors.js";
|
||||
import { backendProfileSpec, backendProfiles, isBackendProfile } from "./backend-profiles.js";
|
||||
import { backendProfileIdPattern, backendProfileSpec, isBackendProfile } from "./backend-profiles.js";
|
||||
|
||||
const backendProfilePatternText = String(backendProfileIdPattern);
|
||||
|
||||
const allowedTenants = new Set(["unidesk", "hwlab"]);
|
||||
const allowedToolCredentials = ["github", "unidesk-ssh"] as const;
|
||||
@@ -44,7 +46,7 @@ export function validateCreateRun(input: unknown): CreateRunInput {
|
||||
const tenantId = requiredString(record, "tenantId");
|
||||
if (!allowedTenants.has(tenantId)) throw new AgentRunError("tenant-policy-denied", `tenantId ${tenantId} is not allowed`, { httpStatus: 403 });
|
||||
const backendProfileValue = requiredString(record, "backendProfile");
|
||||
if (!isBackendProfile(backendProfileValue)) throw new AgentRunError("schema-invalid", `backendProfile ${backendProfileValue} is not supported in v0.1`, { httpStatus: 400, details: { allowedBackends: [...backendProfiles] } });
|
||||
if (!isBackendProfile(backendProfileValue)) throw new AgentRunError("schema-invalid", `backendProfile ${backendProfileValue} must be a lowercase slug`, { httpStatus: 400, details: { pattern: backendProfilePatternText } });
|
||||
const backendProfile = backendProfileValue as BackendProfile;
|
||||
const executionPolicy = validateExecutionPolicy(requiredRecord(record, "executionPolicy"));
|
||||
validateBackendSecretScope(backendProfile, executionPolicy);
|
||||
@@ -214,7 +216,7 @@ export function validateExecutionPolicy(record: JsonRecord): ExecutionPolicy {
|
||||
const item = asRecord(credential, "providerCredential");
|
||||
const profile = typeof item.profile === "string" ? item.profile.trim() : "";
|
||||
if (profile.length === 0) throw new AgentRunError("schema-invalid", "provider credential profile is required", { httpStatus: 400 });
|
||||
if (!isBackendProfile(profile)) throw new AgentRunError("schema-invalid", `provider credential profile ${profile} is not supported in v0.1`, { httpStatus: 400, details: { allowedBackends: [...backendProfiles] } });
|
||||
if (!isBackendProfile(profile)) throw new AgentRunError("schema-invalid", `provider credential profile ${profile} must be a lowercase slug`, { httpStatus: 400, details: { pattern: backendProfilePatternText } });
|
||||
const secretRef = asRecord(item.secretRef, "providerCredential.secretRef");
|
||||
if (typeof secretRef.name !== "string" || secretRef.name.length === 0) throw new AgentRunError("schema-invalid", "provider credential secretRef.name is required", { httpStatus: 400 });
|
||||
const keys = Array.isArray(secretRef.keys) ? secretRef.keys : [];
|
||||
@@ -327,7 +329,7 @@ export function validateCreateQueueTask(input: unknown): CreateQueueTaskInput {
|
||||
const tenantId = requiredString(record, "tenantId");
|
||||
if (!allowedTenants.has(tenantId)) throw new AgentRunError("tenant-policy-denied", `tenantId ${tenantId} is not allowed`, { httpStatus: 403 });
|
||||
const backendProfileValue = optionalString(record.backendProfile) ?? "codex";
|
||||
if (!isBackendProfile(backendProfileValue)) throw new AgentRunError("schema-invalid", `backendProfile ${backendProfileValue} is not supported in v0.1`, { httpStatus: 400, details: { allowedBackends: [...backendProfiles] } });
|
||||
if (!isBackendProfile(backendProfileValue)) throw new AgentRunError("schema-invalid", `backendProfile ${backendProfileValue} must be a lowercase slug`, { httpStatus: 400, details: { pattern: backendProfilePatternText } });
|
||||
const queue = optionalString(record.queue) ?? "default";
|
||||
const lane = optionalString(record.lane) ?? "default";
|
||||
const priorityValue = record.priority ?? 0;
|
||||
@@ -367,5 +369,5 @@ export function validateSessionListState(value: string): SessionListState {
|
||||
|
||||
export function validateBackendProfile(value: string): BackendProfile {
|
||||
if (isBackendProfile(value)) return value;
|
||||
throw new AgentRunError("schema-invalid", `backendProfile ${value} is not supported in v0.1`, { httpStatus: 400, details: { allowedBackends: [...backendProfiles] } });
|
||||
throw new AgentRunError("schema-invalid", `backendProfile ${value} must be a lowercase slug`, { httpStatus: 400, details: { pattern: backendProfilePatternText } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user