Files
pikasTech-unidesk/.agents/skills/unidesk-sub2api/SKILL.md
T

21 KiB
Raw Blame History

name, description
name description
unidesk-sub2api UniDesk Sub2API 平台运维技能。用户提到 Sub2API、sub2api、platform-infra sub2api、Codex pool、统一 API key、Sub2API FRP 暴露、Sub2API 管理 UI、配置 master ~/.codex 走 Sub2API、添加/删除 Codex 上游账号、校验 Sub2API /v1/models 时使用。

UniDesk Sub2API

UniDesk 在 G14 k3s platform-infra namespace 运维 Sub2API。日常操作统一使用 UniDesk CLI,不直接写 Kubernetes 资源或手工调用 Sub2API 管理 API。

固定入口: cd /root/unidesk && bun scripts/cli.ts platform-infra sub2api ...

先读边界

  • 仓库长期开发边界见 docs/reference/platform-infra.md,本 skill 承担日常操作手册。
  • 配置真相是 YAMLconfig/platform-infra/sub2api.yamlconfig/platform-infra/sub2api-codex-pool.yaml
  • 本 skill 目录下若存在 agents/*.yaml,只作为 skill/agent 展示与调用元数据,不是 Sub2API 或 Codex pool 运行配置;不要在 skill 目录维护第二份账号、capacity、priority、endpoint 或 Secret 配置。
  • Runtime 在 G14:k3splatform-infra namespacemaster server 只是控制端和消费者,不部署 Sub2API/PostgreSQL/Redis。
  • Secret、~/.codex/config.toml*~/.codex/auth.json* 是运行时输入或本地状态,不提交。
  • 默认 ~/.codex/config.toml~/.codex/auth.json 只作为统一 Sub2API consumer 使用;config.toml 必须指向 https://sub2api.74-48-78-17.nip.io/auth.json 必须使用统一 pool API key。新增上游账号不得覆盖这两个默认文件,只能新增 config.toml.<profile> / auth.json.<profile> 并在 YAML 里声明。
  • 输出只能包含 Secret 路径、长度、preview/fingerprint;禁止打印完整 API key、admin password、JWT secret、TOTP key。

部署与状态

bun scripts/cli.ts platform-infra sub2api plan
bun scripts/cli.ts platform-infra sub2api apply --dry-run
bun scripts/cli.ts platform-infra sub2api apply --confirm
bun scripts/cli.ts platform-infra sub2api status
bun scripts/cli.ts platform-infra sub2api validate
  • plan 读取 config/platform-infra/sub2api.yaml,渲染 src/components/platform-infra/sub2api/sub2api.k8s.yaml,检查 no Ingress/NodePort/LoadBalancer/hostPort/hostNetwork/resource limits。
  • apply --confirm 默认创建异步 job;按返回的 job status 命令轮询,再跑 statusvalidate
  • status --full|--raw 只在需要展开远端 stdout/stderr 或原始 JSON 时使用。
  • validate 是按需验收,不是连续可用性探针。

镜像升级

  1. 修改 config/platform-infra/sub2api.yamlimage.repositoryimage.tagpullPolicy
  2. 执行 sub2api plan,确认策略检查通过。
  3. 执行 sub2api apply --confirm,轮询 job 完成。
  4. 执行 sub2api status,确认运行镜像等于 YAML 声明。
  5. 执行 sub2api validatecodex-pool validate 做入口验收。

不要把镜像版本写进脚本常量、JSON 或 manifest 模板。

Codex Pool

bun scripts/cli.ts platform-infra sub2api codex-pool plan
bun scripts/cli.ts platform-infra sub2api codex-pool sync --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool validate
bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --confirm

config/platform-infra/sub2api-codex-pool.yaml 控制:

  • pool.groupName: Sub2API group 名称。
  • pool.apiKeySecretName / pool.apiKeySecretKey: 统一消费 API key 的 k3s Secret 位置,默认 platform-infra/sub2api-codex-pool-api-key.API_KEY
  • pool.minOwnerBalanceUsd: pool key owner 最低余额,sync/validate 会补齐。
  • pool.minOwnerConcurrency: 可选统一消费 API key owner 最低并发;省略时 CLI 自动使用所有已解析账号 capacity 的总和,sync/validate 会补齐。显式 YAML 值只作为 override,仍必须不小于账号 capacity 总和;未显式写 profiles.entries[].capacity 的账号会使用 pool.defaultAccountCapacity 参与求和,不要用提高某个 provider capacity 来掩盖用户并发层 WS 1013。
  • pool.defaultTempUnschedulable: 默认账号级临时下线规则;只声明 Sub2API 已支持的错误路径能力,用于在上游返回容量、限流、overload、service unavailable、gateway timeout、稳定模型路由错误或认证状态异常时,让 Sub2API 冷却该账号并切换到同组其他账号。不要用 YAML、UniDesk CLI、k8s 热补或本地 fork 魔改 Sub2API 不支持的行为。
  • 自动冻结/切号失败时,必须修复 temp_unschedulable 与 failover 机制本身,并用运行时证据证明失败账号被临时冻结且请求切到其他可调度账号;禁止通过手动禁用账号、删除账号、移除 YAML entry、降低 membership 或临时改调度策略来替代自动恢复。只有明确的上游退役或所有权变更才走删除/禁用上游流程。
  • YAML 只选择和配置 Codex 上游,不声明 schedulable 长期字段;schedulable=true 只能作为 codex-pool sync --confirm 的过程控制基线恢复。自动冻结必须表现为 temp_unschedulable_until / temp_unschedulable_reason,避免把永久不可调度误当成自动冻结。
  • profiles.entries: 从 master ~/.codex/ 选择上游 profile 并映射到 Sub2API account。
  • profiles.entries[].capacity: 可选 per-account concurrency override;不写则使用 pool.defaultAccountCapacity。具体数值只以 config/platform-infra/sub2api-codex-pool.yaml 为准,skill 和长期参考只描述规则,不重复写当前值。
  • profiles.entries[].loadFactor: 可选 per-account Sub2API load_factor override;不写则使用 pool.defaultAccountLoadFactor。具体数值只以 config/platform-infra/sub2api-codex-pool.yaml 为准,修改后必须 codex-pool sync --confirmcodex-pool validate
  • 除非用户明确要求修改配置,不要仅凭推断改账号 membership、priority、capacity、loadFactor、WebSocket mode 或其他调度策略;先保留 YAML,完成 provenance/runtime evidence 溯源,并把结论写回相关 issue 或 runbook 后再提出变更。
  • profiles.entries[].tempUnschedulable: 可选 per-account 临时下线规则覆盖;字段语义以 docs/reference/platform-infra.md 为权威。上游 Sub2API 不支持的成功体分类、调度策略或账号冷却行为不要在这里声明。
  • profiles.entries[].openaiResponsesWebSocketsV2Mode: 需要 Responses WebSocket v2 的上游才设置,值为 offctx_poolpassthrough
  • profiles.entries[].upstreamUserAgent: 少数要求 Codex CLI User-Agent 的上游才设置,不能含换行。

sync --confirm 会登录 Sub2API admin、创建/更新 group、创建/更新 YAML 中的 unidesk-codex-* accounts、创建/复用统一 API key Secret,并把 managed account 的 schedulable=true 恢复为过程控制基线;它默认不删除 YAML 中缺席的 managed account。只有明确退役上游时才使用 sync --confirm --prune-removed 删除缺席且 extra.unidesk_managed=trueunidesk-codex-* account。

sync --confirmvalidate 可能超过单次 SSH/runtime 短连接窗口。必须继续使用 bun scripts/cli.ts platform-infra sub2api codex-pool ...,由 CLI 在 G14 远端提交作业并短轮询状态;不要改用裸 trans G14:k3s script 等一个长连接等待完整结果。若看到 UNIDESK_SSH_RUNTIME_TIMEOUT,先按 docs/reference/platform-infra.md 的规则处理为控制面可见性问题,修 CLI/job/poll 或重跑受控命令,不要手工 patch Sub2API credentials 或源码。

不要给 UniDesk-managed Codex accounts 开 Sub2API pool_mode。UniDesk 期望的 failover 是把失败账号临时标记为 unschedulable,让同组其他账号接手;pool_mode 会重试同一个 account path。

WebSocket v2 是账号能力集合,不是调度 pin。openaiResponsesWebSocketsV2Mode 只声明该账号可承担 Codex Responses WSv2 链路;只有 localCodex.supportsWebSockets=true / localCodex.responsesWebSocketsV2=true 时,codex-pool validate 才必须看到至少一个 webSocketsV2.schedulableEnabled 账号。真实可用性仍以 direct Codex WSv2 probe、Sub2API 日志和原入口 Codex smoke 为准。

Codex 启动时反复出现 WebSocket reconnect、HTTPS fallback、websocket closed by server before response.completed,或 Sub2API 日志出现 openai.websocket_proxy_failed / openai.websocket_account_select_failed / 上游 WS handshake 4xx/5xx 时,先按运行证据定位具体 account 和 transport。若账号的 WSv2 握手失败,优先只在 YAML 中把该账号的 openaiResponsesWebSocketsV2Mode 收敛为 off;若没有任何 direct Codex WSv2 probe 通过,则同时把 localCodex.supportsWebSocketslocalCodex.responsesWebSocketsV2 收敛为 false,再 codex-pool sync --confirm。不要顺手改 membership、priority、capacity、Secret 或代码 fallback。

添加上游

  1. 在 master ~/.codex/ 准备带后缀的上游 profile 文件,例如 config.toml.<profile>auth.json.<profile>;禁止覆盖默认 config.toml / auth.json
  2. config/platform-infra/sub2api-codex-pool.yaml 添加 profiles.entries 项,指定 profileaccountNameconfigFileauthFile
  3. 如需要,给该项加 prioritycapacityloadFactortempUnschedulableopenaiResponsesWebSocketsV2ModeupstreamUserAgentcapacity/loadFactor 的具体数值只写在 YAML。
  4. 如果新增账号会提高声明 capacity 总和,默认让省略的 pool.minOwnerConcurrency 继续按 capacity 总和自动解析;只有 YAML 已经显式写了该 override 时,才同步提高到不低于总 capacity,或删除 override 回到自动解析。
  5. codex-pool plan,确认 profile 可读、base_url 和 API key 来源有效,且 stdout 未泄露完整 key。
  6. codex-pool sync --confirm
  7. codex-pool validate

普通新增上游是 YAML 操作,不走 CI/CD,不改代码。只有需要渲染或校验上游 Sub2API 已经存在的可复用能力时才修改 scripts/src/platform-infra-sub2api-codex.ts;Sub2API 本身不支持的能力不在 UniDesk 侧魔改实现。

删除上游

删除上游只用于明确退役、凭据所有权变更或用户明确要求移除 provider;不能作为上游 5xx、compact 失败、限流、模型路由失败或自动冻结/切号缺陷的恢复手段。

  1. config/platform-infra/sub2api-codex-pool.yaml 删除对应 profiles.entries 项。
  2. codex-pool plan 检查 desired 列表。
  3. codex-pool sync --confirm --prune-removed
  4. 确认输出 accounts.pruned 只包含期望删除项。
  5. codex-pool validate

CLI 默认保留缺席账号,避免把可用性问题误处理成删除;只有显式 --prune-removed 才会 prune nameunidesk-codex- 开头且 extra.unidesk_managed=true 的缺席账号。

FRP 暴露

bun scripts/cli.ts platform-infra sub2api codex-pool expose
bun scripts/cli.ts platform-infra sub2api codex-pool expose --confirm
  • publicExposure YAML 控制。默认公共端是 publicBaseUrlmaster 本地消费端是 masterBaseUrl
  • expose --confirm 只为 YAML 指定的 remotePort 补 master frps allow port,并在 G14 创建/更新 sub2api-frpc
  • master Caddy site 也由 publicExposure.masterCaddy 渲染;responseHeaderTimeoutSeconds 必须足够覆盖 Codex /responses/compact 长请求,避免 Caddy 先返回 504 而 Sub2API 后台实际稍后成功。
  • 同一个 FRP TCP 入口同时暴露 OpenAI-compatible API 和 Sub2API 管理 UI /login。不要另开第二个管理端口,除非 YAML 明确声明新的暴露决策。
  • Sub2API Kubernetes Service 继续保持 ClusterIP。

配置 master Codex 消费端

bun scripts/cli.ts platform-infra sub2api codex-pool configure-local
bun scripts/cli.ts platform-infra sub2api codex-pool configure-local --confirm

configure-local --confirm 会:

  • platform-infra/<apiKeySecretName>.<apiKeySecretKey> 读取统一 API key。
  • 把当前 ~/.codex/config.toml~/.codex/auth.json 备份为 .<backupSuffix>,默认 .pre-sub2api
  • 重写默认 ~/.codex 消费端,固定指向 https://sub2api.74-48-78-17.nip.io/provider 名称和 wire API 来自 localCodex
  • localCodex 写入 Codex transport 标记:supports_websockets[features] responses_websockets_v2 必须同开同关。只有至少一个上游通过 direct Codex WSv2 probe 时才启用;否则保持 HTTP Responses,避免每次原入口先经历无效 WS reconnect。
  • 用统一 key 做一次 gateway 验证。

防递归规则:默认 config.toml / auth.json 是 Sub2API consumer,不得作为上游账号导回 pool;上游账号必须使用带后缀 profile 文件,并通过 config/platform-infra/sub2api-codex-pool.yamlprofiles.entries 增删。

验收口径

部署 closeout 至少包含:

  • sub2api statusDeployment/StatefulSet/Service/Secret 可见,运行镜像与 YAML 一致。
  • sub2api validateapp、PostgreSQL、Redis 和 service proxy 基础检查通过。
  • codex-pool validate:统一 key 的 GET /v1/models 成功,并用 localCodex.responsesSmokeModel 跑一次小的 POST /v1/responses smokeowner balance / owner concurrency 已满足 YAML 最小值,capacity、WebSocket v2 和 temporary-unschedulable 运行时状态与 YAML 对齐;validation.gatewayResponsesRecent 汇总最近 6 小时普通 /responses/v1/responses 的 failover、forward failure、最终 4xx/5xx、慢 final error 与 context canceled 证据,validation.gatewayCompactRecent 单独汇总 /responses/compact 证据。若当前 Responses smoke ok=true 但 recent 字段 degraded=true,先区分是历史窗口残留还是新的 request id 正在失败;长期判定见 docs/reference/platform-infra.md
  • publicExposure.enabled=true,确认 FRP path 可用;expose --confirm 会用未带 key 的 public /v1/models 401 作为网关可达性探针。

如果要证明真实模型请求可用,使用最小 /v1/responses 或等价 Codex smoke。不要把 group-level /v1/models 成功解释成每个上游 account 都健康。

排障

  • profile invalid:先修 ~/.codex/config.toml.<profile>base_urlwire_apimodelauth.json.<profile> 的 API key;不要在 YAML 中写密钥。
  • pool key 401:跑 codex-pool sync --confirm 重建 Sub2API key 与 k3s Secret 绑定,再跑 codex-pool validate
  • 运行中过去的验证探针残留:只用 codex-pool cleanup-probes --confirm 清理 unidesk-probe-* 临时资源;不要把真实 managed account 删除当作探针清理或可用性恢复。
  • FRP 不通:先看 codex-pool expose --confirm 输出的 masterFrpsmasterCaddysub2api-frpc 和 public 401 probe;需要低层证据时只用 trans G14:k3s 做 bounded 查询。
  • /responses/compact 约 30 秒后返回 504 但 Sub2API 日志稍后记录 codex.remote_compact.succeeded 时,优先检查 master Caddy response_header_timeout 是否由 YAML publicExposure.masterCaddy.responseHeaderTimeoutSeconds 渲染,修正后跑 codex-pool expose --confirm;这类边缘代理超时不会触发 Sub2API 账号级临时下线。
  • default profile 递归:检查 YAML default entry 是否使用 *.pre-sub2api 备份文件;必要时恢复备份后重新 configure-local --confirm
  • 上游需要 WebSocket v2:先做 direct Codex WSv2 probe;通过后才给该 profile 配 openaiResponsesWebSocketsV2Mode: ctx_pool|passthrough 并跑 sync --confirm;把它当 capability candidate,容量仍以 YAML 中的 capacity 或默认值为准。
  • Codex 启动 WebSocket 回退:用原入口 Codex smoke 复现,再用 bounded Sub2API 日志确认 account;对 WS handshake 4xx/5xx、openai.websocket_account_select_failed 或 close-before-response.completed 的账号关闭 YAML WSv2 能力后同步。若没有剩余 WSv2-capable account,把 localCodex.supportsWebSocketslocalCodex.responsesWebSocketsV2 一起关掉,不把临时可用性推断写成调度配置。
  • 上游要求 Codex User-Agent:只给该 profile 配 upstreamUserAgent,跑 sync --confirm
  • 上游报 capacity/rate-limit/overload/Bad Gateway/Gateway Timeout 后没有切号或频繁先失败再恢复:先确认 codex-pool validatetempUnschedulable.ok=true 且目标 account runtimeEnabled=true、规则数符合 YAML;再看 validation.gatewayResponses.evidence.failovers 的 account/upstream status。若 mismatch,跑 codex-pool sync --confirm;若 runtime 规则已对齐但仍不冻结或不切号,继续修 Sub2API 自动冻结/failover 能力并复测,不要手工 patch Sub2API credentials,也不要手动禁用、删除或从 YAML 移除问题账号来绕过机制缺陷。
  • codex-pool sync --confirmcodex-pool validate 超时:先区分 CLI 传输超时和 Sub2API 运行失败。受控 CLI 应返回远端作业进度和 stdout/stderr tail;如果只是低层 trans 60s 超时,不能据此判定 Sub2API failover 不工作。改用或修复 CLI 的远端 job/poll 路径后重跑,并以最终结构化结果作为证据。
  • Codex 报 weekly-limit、less than 10% of your weekly limit leftRun /status for a breakdown 等账号状态/软配额提示并要求切号:如果上游以 403/429 等错误状态返回,把稳定 body 关键词放进 pool.defaultTempUnschedulable 的对应规则,跑 codex-pool sync --confirm,再用 codex-pool validate 确认每个 managed account 的 runtime 规则包含这些关键词。若该文案是 HTTP 200 成功内容,当前 Sub2API 不支持把它重分类为账号冷却;不要写 YAML 200 规则、不要热补 Sub2API、不要绕过 sync,必要时登记上游能力缺口 issue。
  • 上游 400/503 响应体出现 invalid_encrypted_contentbad_response_status_codeinvalid_request_error + 稳定 unsupported-model 文案、unsupported-model、暂不支持 / 可用模型model_not_foundNo available channel for model ... 或同类稳定模型路由 / Responses encrypted-content 兼容性失败:把稳定 body 关键词放进 pool.defaultTempUnschedulable 的对应 400/503 规则,跑 codex-pool sync --confirm,再用 codex-pool validate 确认目标 account 的 runtime rule 包含这些关键词;不要用 account membership、priority、capacity、loadFactor、WebSocket mode 或 User-Agent 改动掩盖该错误族。
  • 上游错误反复触发:默认错误冷却按严重程度分层;临时问题可从 10 分钟起步,网关/服务不可用/过载/模型路由类应更长,认证/权限/配额/账号状态/账号兼容类使用最长冷却。invalid_encrypted_content、unsupported-model、Recovered upstream error ...Bad GatewayGateway Timeout、Cloudflare 524、Codex-facing Upstream request failedUnknown errorcontext deadline exceededcontext canceledmodel_not_foundNo available channel for model、大上下文 413openai_error 这类稳定包装文案都应留在对应 YAML 冷却政策里,特别是普通 /responses 与 compact 链路里上游兼容性错误或 524 可能最终表现为客户端 502/504 + Unknown error。具体数值只以 YAML 为准,修改后必须 codex-pool sync --confirmcodex-pool validate。长期判定见 docs/reference/platform-infra.md
  • Codex auto compact 后丢上下文:先确认 YAML localCodex 是否声明启用 WSv2;若启用,再确认本机 ~/.codex/config.toml 是否有 supports_websockets = trueresponses_websockets_v2 = true,并看 codex-pool validate 的 WSv2 candidate 和 Sub2API 日志里的 transport=responses_websockets_v2。若 YAML 当前禁用 WSv2,则按 HTTP Responses 稳定性排查,不把旧 WS 口径当成验收要求。
  • Codex smoke 有 reconnect/1013:这是上游并发/可用性问题,和 HTTP-only compact context-loss 分开处理;记录 session/log 证据并关联专项 issue,不要用运行时手补覆盖 YAML 容量。

禁止事项

  • 不用原生 kubectl apply/delete/patch 作为正式操作入口。
  • 不在 master server 部署或运行 Sub2API/PostgreSQL/Redis。
  • 不新增 Ingress、NodePort、LoadBalancer、hostPort、hostNetwork 或宽 FRP 端口段。
  • 不给 Sub2API manifest 添加 CPU/memory limits,除非有新的 YAML 化明确决策。
  • 不打印完整 API key、admin password 或 Secret 明文。
  • 不把普通上游增删做成代码变更、CI/CD、feature flag 或兼容双路径。
  • 不把手动禁用账号、删除账号、移除 YAML entry、降低 membership 或临时改 priority/capacity/loadFactor 当作自动冻结/切号失败的修复。
  • 不魔改 Sub2API:Sub2API 本身不支持的能力就不做,不通过 UniDesk 脚本、k8s 原地热补、本地 fork、YAML 伪声明或隐藏 fallback 代替上游实现。