refactor: normalize sub2api yaml envelope
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user