refactor: normalize sub2api yaml envelope

This commit is contained in:
Codex
2026-06-14 01:54:15 +00:00
parent ac4ae5079c
commit a6123b1d48
4 changed files with 122 additions and 51 deletions
+61 -1
View File
@@ -131,6 +131,13 @@ export interface CodexTempUnschedulablePolicy {
}
interface CodexPoolConfig {
version: number;
kind: string;
metadata: {
id: string;
owner: string;
relatedIssues: number[];
};
groupName: string;
apiKeyName: string;
apiKeySecretName: string;
@@ -1209,11 +1216,17 @@ function resolveProfileFiles(codexDir: string, profile: CodexPoolProfileConfig):
function readCodexPoolConfig(): CodexPoolConfig {
const defaults = defaultCodexPoolConfig();
if (!existsSync(codexPoolConfigPath)) return defaults;
if (!existsSync(codexPoolConfigPath)) throw new Error(`${codexPoolConfigPath} is required`);
const parsed = Bun.YAML.parse(readFileSync(codexPoolConfigPath, "utf8")) as unknown;
if (!isRecord(parsed)) throw new Error(`${codexPoolConfigPath} must contain a YAML object`);
const version = integerConfigField(parsed, "version", "");
if (version !== 1) throw new Error(`${codexPoolConfigPath}.version must be 1`);
const kind = requiredStringConfigField(parsed, "kind", "");
if (kind !== "platform-infra-sub2api-codex-pool") throw new Error(`${codexPoolConfigPath}.kind must be platform-infra-sub2api-codex-pool`);
const metadata = requiredRecordConfigField(parsed, "metadata", "");
const pool = parsed.pool;
if (!isRecord(pool)) throw new Error(`${codexPoolConfigPath}.pool must be a YAML object`);
if (!isRecord(parsed.profiles)) throw new Error(`${codexPoolConfigPath}.profiles must be a YAML object`);
rejectSchedulableYamlField(pool, "pool");
const defaultTempUnschedulable = readTempUnschedulablePolicy(pool.defaultTempUnschedulable, "pool.defaultTempUnschedulable", defaults.defaultTempUnschedulable);
const defaultAccountPriorityValue = readAccountPriority(pool.defaultAccountPriority, "pool.defaultAccountPriority");
@@ -1233,6 +1246,13 @@ function readCodexPoolConfig(): CodexPoolConfig {
? Math.max(1, declaredAccountCapacity)
: readOwnerConcurrency(pool.minOwnerConcurrency, "pool.minOwnerConcurrency");
const config: CodexPoolConfig = {
version,
kind,
metadata: {
id: requiredStringConfigField(metadata, "id", "metadata"),
owner: requiredStringConfigField(metadata, "owner", "metadata"),
relatedIssues: integerArrayConfigField(metadata, "relatedIssues", "metadata"),
},
groupName: stringValue(pool.groupName) ?? defaults.groupName,
apiKeyName: stringValue(pool.apiKeyName) ?? defaults.apiKeyName,
apiKeySecretName: stringValue(pool.apiKeySecretName) ?? defaults.apiKeySecretName,
@@ -1264,6 +1284,13 @@ function readCodexPoolConfig(): CodexPoolConfig {
function defaultCodexPoolConfig(): CodexPoolConfig {
return {
version: 1,
kind: "platform-infra-sub2api-codex-pool",
metadata: {
id: "sub2api-codex-pool",
owner: "unidesk",
relatedIssues: [],
},
groupName: defaultPoolGroupName,
apiKeyName: defaultPoolApiKeyName,
apiKeySecretName: defaultPoolApiKeySecretName,
@@ -1853,6 +1880,9 @@ function tempUnschedulableSummary(policy: CodexTempUnschedulablePolicy): Record<
function codexPoolConfigSummary(pool: CodexPoolConfig): Record<string, unknown> {
const accountCapacityTotal = desiredAccountCapacityTotal(pool);
return {
version: pool.version,
kind: pool.kind,
metadata: pool.metadata,
groupName: pool.groupName,
apiKeyName: pool.apiKeyName,
apiKeySecretName: pool.apiKeySecretName,
@@ -3395,6 +3425,36 @@ function stringValue(value: unknown): string | null {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}
function requiredStringConfigField(obj: Record<string, unknown>, key: string, path: string): string {
const value = stringValue(obj[key]);
const prefix = path.length > 0 ? `${path}.` : "";
if (value === null) throw new Error(`${codexPoolConfigPath}.${prefix}${key} must be a non-empty string`);
return value;
}
function requiredRecordConfigField(obj: Record<string, unknown>, key: string, path: string): Record<string, unknown> {
const value = obj[key];
const prefix = path.length > 0 ? `${path}.` : "";
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${prefix}${key} must be a YAML object`);
return value;
}
function integerConfigField(obj: Record<string, unknown>, key: string, path: string): number {
const value = obj[key];
const prefix = path.length > 0 ? `${path}.` : "";
if (typeof value !== "number" || !Number.isInteger(value)) throw new Error(`${codexPoolConfigPath}.${prefix}${key} must be an integer`);
return value;
}
function integerArrayConfigField(obj: Record<string, unknown>, key: string, path: string): number[] {
const value = obj[key];
const prefix = path.length > 0 ? `${path}.` : "";
if (!Array.isArray(value) || !value.every((item) => typeof item === "number" && Number.isInteger(item))) {
throw new Error(`${codexPoolConfigPath}.${prefix}${key} must be an array of integers`);
}
return value;
}
function numberValue(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string" && value.trim().length > 0) {