Files
pikasTech-agentrun/src/common/backend-profiles.ts
T

189 lines
7.0 KiB
TypeScript

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";
protocol: "codex-app-server-jsonrpc-stdio";
transport: "stdio";
command: "codex app-server --listen stdio://";
status: "registered";
requiredSecretKeys: readonly string[];
defaultSecretName: string;
profileIsolation: "profile-scoped-codex-home";
description: string;
}
const builtinBackendProfileSpecs: readonly BackendProfileSpec[] = [
{
profile: "codex",
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: "agentrun-v01-provider-codex",
profileIsolation: "profile-scoped-codex-home",
description: "Default Codex-compatible profile",
},
{
profile: "deepseek",
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: "agentrun-v01-provider-deepseek",
profileIsolation: "profile-scoped-codex-home",
description: "DeepSeek-compatible profile through Codex app-server stdio",
},
{
profile: "minimax-m3",
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: "agentrun-v01-provider-minimax-m3",
profileIsolation: "profile-scoped-codex-home",
description: "MiniMax M3 OpenAI-compatible profile through Codex app-server stdio",
},
{
profile: "dsflash-go",
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", "model-catalog.json"],
defaultSecretName: "agentrun-v01-provider-dsflash-go",
profileIsolation: "profile-scoped-codex-home",
description: "DeepSeek V4 Flash profile through OpenCode Zen Go Moon Bridge",
},
];
export const backendProfileSpecs = builtinBackendProfileSpecs;
export const backendProfiles = builtinBackendProfileSpecs.map((item) => item.profile) as readonly BackendProfile[];
export function compareBackendProfiles(left: string, right: string): number {
const leftIndex = builtinBackendProfileSpecs.findIndex((item) => item.profile === left);
const rightIndex = builtinBackendProfileSpecs.findIndex((item) => item.profile === right);
if (leftIndex >= 0 || rightIndex >= 0) {
if (leftIndex < 0) return 1;
if (rightIndex < 0) return -1;
if (leftIndex !== rightIndex) return leftIndex - rightIndex;
}
return left.localeCompare(right);
}
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 {
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 isBackendProfileSlug(value);
}
export function backendCapability(spec: BackendProfileSpec): JsonRecord {
const defaultSecretRef = { name: spec.defaultSecretName, keys: [...spec.requiredSecretKeys], valuesPrinted: false };
return {
profile: spec.profile,
backendKind: spec.backendKind,
protocol: spec.protocol,
transport: spec.transport,
command: spec.command,
status: spec.status,
requiredSecretKeys: [...spec.requiredSecretKeys],
defaultSecretRef,
preflight: {
readiness: "requires-profile-secret-ref",
defaultSecretRef,
requiredSecretKeys: [...spec.requiredSecretKeys],
profileIsolation: spec.profileIsolation,
credentialValuesPrinted: false,
valuesPrinted: false,
},
profileIsolation: spec.profileIsolation,
description: spec.description,
};
}
export function mergeBackendCapability(profile: string, storedCapabilities: JsonRecord): JsonRecord {
const spec = backendProfileSpec(profile);
if (!spec) return { profile, ...storedCapabilities, valuesPrinted: false };
const base = backendCapability(spec);
return {
...base,
...storedCapabilities,
defaultSecretRef: base.defaultSecretRef,
preflight: base.preflight,
};
}
export function backendCapabilities(profiles: readonly string[] = backendProfiles): JsonRecord[] {
const profileIds = new Set<BackendProfile>(backendProfiles);
for (const profile of profiles) {
if (isBackendProfileSlug(profile)) profileIds.add(profile as BackendProfile);
}
return [...profileIds]
.sort(compareBackendProfiles)
.map((profile) => backendCapability(backendProfileSpec(profile) as BackendProfileSpec));
}
export function backendCapabilitiesSqlValues(profiles?: readonly BackendProfile[], options: { requiredSecretKeysByProfile?: Record<string, readonly string[]> } = {}): string {
const specs = profiles
? profiles.map((profile) => {
const spec = backendProfileSpec(profile);
if (!spec) throw new Error(`unknown backend profile for SQL seed: ${profile}`);
return spec;
})
: builtinBackendProfileSpecs;
return specs.map((spec) => {
const requiredSecretKeys = options.requiredSecretKeysByProfile?.[spec.profile] ?? spec.requiredSecretKeys;
const capabilities = JSON.stringify({
backendKind: spec.backendKind,
protocol: spec.protocol,
transport: spec.transport,
command: spec.command,
requiredSecretKeys,
defaultSecretRef: { name: spec.defaultSecretName, keys: requiredSecretKeys },
profileIsolation: spec.profileIsolation,
description: spec.description,
});
return `('${sqlString(spec.profile)}', '${sqlString(capabilities)}'::jsonb, '{"mode":"manual-runner-v0.1"}'::jsonb, '{"status":"${sqlString(spec.status)}"}'::jsonb, now())`;
}).join(",\n");
}
function sqlString(value: string): string {
return value.replace(/'/gu, "''");
}