diff --git a/AGENTS.md b/AGENTS.md index 1a1806bb..9bcf0c52 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文 ## P0 最高优先级:G14 platform-infra 规则 - P0: `platform-infra` 是 G14 k3s 上 UniDesk 运维的平台基础设施 namespace;Sub2API、Codex pool、FRP 暴露、统一消费 API key 和后续平台基础设施迁移的长期边界、路由与探针口径统一见 `docs/reference/platform-infra.md`,Sub2API 日常操作统一见 `$unidesk-sub2api`(`.agents/skills/unidesk-sub2api/SKILL.md`)。 +- P0: Codex pool 账号容量必须从 `config/platform-infra/sub2api-codex-pool.yaml` 进入受控 CLI;HY 当前显式 `capacity: 20`,不得用代码常量、Secret、运行时手补或旧测试断言覆盖。 - P0: `devops-infra` 仅作为既有控制面基础设施逐步迁移来源,不再作为新增平台服务的默认 namespace;新增/迁移必须优先落到 `platform-infra`,并通过 `config/platform-infra/*.yaml` 与 `bun scripts/cli.ts platform-infra ...` 受控。 ## P0 最高优先级:CaseRun 无服务与单步调试规则 diff --git a/config/platform-infra/sub2api-codex-pool.yaml b/config/platform-infra/sub2api-codex-pool.yaml index bdbc1dac..c360a490 100644 --- a/config/platform-infra/sub2api-codex-pool.yaml +++ b/config/platform-infra/sub2api-codex-pool.yaml @@ -51,6 +51,7 @@ profiles: configFile: config.toml.HY authFile: auth.json.HY openaiResponsesWebSocketsV2Mode: passthrough + capacity: 20 priority: 1 - profile: gptclub accountName: unidesk-codex-gptclub diff --git a/docs/reference/platform-infra.md b/docs/reference/platform-infra.md index 4f24fce4..2655df0d 100644 --- a/docs/reference/platform-infra.md +++ b/docs/reference/platform-infra.md @@ -27,6 +27,7 @@ - `pool.apiKeySecretName` and `pool.apiKeySecretKey` name the k3s Secret that stores the single consumer API key. - `pool.defaultTempUnschedulable` declares Sub2API account-level temporary unschedulable rules. Keep 429/overload/capacity failures in this YAML policy so the scheduler can cool down a failing account and choose another candidate instead of hard-pinning one provider. - `profiles.entries` selects local Codex profile files from `~/.codex/` and maps them to Sub2API account names. +- `profiles.entries[].capacity` optionally overrides `pool.defaultAccountCapacity` for one account. Capacity is a YAML-controlled routing input; HY is intentionally declared as `capacity: 20` in this file, and code constants, Secrets, ad-hoc runtime patches, or stale tests must not override that source of truth. - `profiles.entries[].tempUnschedulable` may override the pool default for one account. The CLI renders it into Sub2API credentials as `temp_unschedulable_enabled` and `temp_unschedulable_rules`; rules match HTTP status plus response-body keywords and place only that account into a temporary unschedulable cooldown. - `profiles.entries[].openaiResponsesWebSocketsV2Mode` is the account-level Responses WebSocket v2 switch for OpenAI-compatible upstreams that require WebSocket transport. Allowed values are `off`, `ctx_pool`, and `passthrough`; omit the field unless that upstream needs it. - `profiles.entries[].upstreamUserAgent` is an optional account-level upstream request User-Agent override. Use it only for upstreams that require a Codex CLI compatible User-Agent; keep the value YAML-controlled and newline-free. diff --git a/scripts/platform-infra-sub2api-codex-routing-contract-test.ts b/scripts/platform-infra-sub2api-codex-routing-contract-test.ts index 4947b1de..5a1672ba 100644 --- a/scripts/platform-infra-sub2api-codex-routing-contract-test.ts +++ b/scripts/platform-infra-sub2api-codex-routing-contract-test.ts @@ -24,8 +24,8 @@ const rules = parsed.pool?.defaultTempUnschedulable?.rules ?? []; const overload429 = rules.find((rule) => rule.statusCode === 429); assertCondition(parsed.pool?.defaultAccountCapacity === 5, "Codex pool default capacity should remain explicit YAML", parsed.pool); -assertCondition(hy !== undefined, "HY profile should remain declared as a candidate, not a special scheduler target", entries); -assertCondition(hy?.capacity === undefined, "HY must not carry a hard-coded capacity override", hy); +assertCondition(hy !== undefined, "HY profile should remain declared as a YAML-managed pool candidate", entries); +assertCondition(hy?.capacity === 20, "HY capacity must remain explicitly YAML-controlled at 20", hy); assertCondition(wsEnabled.length >= 2, "Responses WSv2 should be a capability set with multiple candidates", wsEnabled); assertCondition(parsed.pool?.defaultTempUnschedulable?.enabled === true, "temporary unschedulable policy must be enabled from YAML", parsed.pool?.defaultTempUnschedulable); assertCondition(overload429 !== undefined, "temporary unschedulable policy must include 429 handling", rules); @@ -35,7 +35,7 @@ assertCondition((overload429?.durationMinutes ?? 0) > 0, "429 handling must cool console.log(JSON.stringify({ ok: true, checks: [ - "HY is not capacity-pinned", + "HY capacity is explicitly YAML-controlled at 20", "WSv2 routing is represented as a multi-account capability set", "temporary unschedulable rules are YAML-controlled", ],