From 2bcd368762b3cf9bdfd99a2df2e756fb02482ef0 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 16 May 2026 10:03:33 +0000 Subject: [PATCH] fix(code-queue): sync codex auth into runtime home --- docs/reference/microservices.md | 2 +- .../code-queue/docker-compose.d601.yml | 1 + .../microservices/code-queue/src/index.ts | 21 +++++++++++++++---- .../v3sctl-adapter/v3s/code-queue.k8s.yaml | 14 +++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/docs/reference/microservices.md b/docs/reference/microservices.md index 37057842..54bc06f1 100644 --- a/docs/reference/microservices.md +++ b/docs/reference/microservices.md @@ -154,7 +154,7 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度 - 上线纪律:Code Queue 相关的前端或后端改进必须在同一任务内正式上线并验证公网 frontend 或 live API,不能只停留在源码、构建产物或“后续再上线”。修改 Code Queue 自身时不得等待当前 Code Queue task 结束、等待 queue idle 或等待 `0 running` 后才重启;D601 active 实例的正式后端部署入口是 `bun scripts/cli.ts codex deploy `,它按已 push 的 remote commit 做 build-first 镜像替换、k3s image import、manifest apply、rollout 和健康验证,并用 v3s adapter、Code Queue live API 或公网 frontend 证明任务和队列仍可读可继续。 - 期望状态部署:新的通用入口是 `bun scripts/cli.ts deploy apply --service code-queue`,它从 `deploy.json` 读取 repo 与 commit,再按 `docs/reference/deploy.md` 的 target-side build 规范在 D601 构建、导入 k3s、rollout 并验证 live commit。`codex deploy ` 是兼容入口,后续实现应复用同一个 reconciler,不得维护第二套部署语义。 - 更名与灾备恢复:旧版 Codex 队列服务名只允许作为兼容诊断和一次性迁移来源;`code-queue-backend` 容器自身 `/health` 正常但 `microservice health code-queue` 返回 provider 直连错误时,优先判定为 backend-core 仍加载旧 `MICROSERVICES_JSON` 或 adapter manifest 未刷新,必须刷新 `.state/docker-compose.env`、重建/替换 `backend-core` 与 `v3sctl-adapter`,随后用 `microservice list` 验证 `code-queue` 的 `runtime.orchestrator=v3sctl`、`backend.proxyMode=v3sctl-adapter-http` 和无业务容器直连摘要。 -- Codex 认证:容器只从 D601 的 `/home/ubuntu/.codex/config.toml` 同步 Codex provider 配置到 D601 `.state/code-queue/codex-home`,并通过 D601 `.state/code-queue-d601.env` 透传 `OPENAI_API_KEY`、`CRS_OAI_KEY` 等 provider 所需变量;这些 provider 环境变量不得写入仓库,必须由 D601 Compose env-file 注入,确保容器重建和重启后不会丢失认证。新增 provider 的 `env_key` 时必须增加同类运行时透传和 Compose env 持久化,禁止把 Codex 或 MiniMax 密钥写入仓库文件。Code Queue 容器必须只读挂载 D601 host 的 SSH 目录到 `/root/.ssh`(默认 `/home/ubuntu/.ssh`),让容器内 `git push`、`ssh -T git@github.com` 与 host 使用同一套 GitHub SSH key/known_hosts;不得把私钥复制进镜像或仓库。 +- Codex 认证:容器必须从 D601 的 `/home/ubuntu/.codex/config.toml` 同步 Codex provider 配置到 D601 `.state/code-queue/codex-home/config.toml`,并只读挂载 `/home/ubuntu/.codex/auth.json` 到容器 `/root/.codex/auth.json` 后同步到 `.state/code-queue/codex-home/auth.json`,让 `codex app-server` 使用与 host 一致的 provider 登录态;同时通过 D601 `.state/code-queue-d601.env` 或 k8s `code-queue-env` secret 透传 `OPENAI_API_KEY`、`CRS_OAI_KEY` 等 provider 所需变量。这些 provider 环境变量和 auth 文件不得写入仓库,必须由 D601 运行时文件或 k8s secret 注入,确保容器重建和重启后不会丢失认证。新增 provider 的 `env_key` 时必须增加同类运行时透传和 Compose/k8s 持久化,禁止把 Codex 或 MiniMax 密钥写入仓库文件。Code Queue 容器必须只读挂载 D601 host 的 SSH 目录到 `/root/.ssh`(默认 `/home/ubuntu/.ssh`),让容器内 `git push`、`ssh -T git@github.com` 与 host 使用同一套 GitHub SSH key/known_hosts;不得把私钥复制进镜像或仓库。 - Develop-ready 镜像:Code Queue 镜像必须在启动前预装 UniDesk/Pipeline 调试所需工具,至少包含 `codex`、`bun`、`node`、`npm`/`npx`、`git`、`rg`、`curl`、`python3`/`pip3`、`docker`、`docker compose`、`docker-compose`、`jq`、`ssh`、`rsync`、`make`、`gcc`/`g++`、`iptables`、`tar`、`gzip` 和 `unzip`;不得依赖 Codex 任务运行时再 `apt-get install` 这些基础环境。 - 远程开发容器与任务执行 Provider:Code Queue 必须能通过 live API 拉起 D601 等计算节点上的开发容器,入口为 `POST /api/dev-containers//start`,默认 Provider 为 `D601`。该流程由 Code Queue 调用 UniDesk `ssh ` 维护桥在目标节点创建 `unidesk-codex-dev-`,并在 Code Queue 所在节点与开发容器之间建立 `ssh -w` TUN 点对点链路;服务所在节点负责对开发容器的 TUN 源地址做 NAT/MASQUERADE,开发容器默认路由和 DNS 改走该 TUN,从而让 `ping google.com`、DNS、HTTP(S) 等出网都经主 server 全局代理,而不是依赖 D601 本地网络。提交 Code Queue 任务时必须支持选择执行 Provider:`D601` 在 D601 `code-queue-backend` 容器中本机执行,默认工作目录为 `/workspace`;其他 Provider 在对应 `unidesk-codex-dev-` 容器中执行,默认工作目录为 `/home/ubuntu`,可按任务覆盖 `cwd`。远程任务启动前必须自动复用或拉起该 Provider 的开发容器、同步 Codex 配置和允许的运行时 provider 环境变量,并通过同一 master TUN/NAT 链路出网;目标 host 存在 `/mnt` 时,开发容器必须挂载 host `/mnt:/mnt`,确保 D601 这类 WSL 节点的 Windows 盘符路径如 `/mnt/f/Work/ConStart` 在任务容器内可见,避免 agent 因缺少真实工作区而搜索到无关项目。TUN 建立必须幂等处理 stale 状态:启动前清理旧 `tun`、默认路由、旧 tunnel SSH 进程和旧 OUTPUT 跳转,缺失旧设备不能导致失败,冷启动运行时准备要有有界但足够的 timeout。TUN 建立后必须创建 `UD-CQ-EGRESS-` OUTPUT 链,规则只允许 loopback、既有连接、`tun` 出口以及到 master server 的 SSH tunnel 控制连接,随后 reject 其他 IPv4/IPv6 出站包;这条网络层封口是开发/执行容器的权威外网边界,不能用 `HTTP_PROXY`/`NO_PROXY` 环境变量替代,容器镜像也必须使用已解析出的唯一 `unidesk-code-queue:` 或显式 `image`,缺失时直接失败,禁止 provider-gateway image、`latest` 或其他隐式镜像 fallback。验收必须保留三类日志:容器建隧道后 `ping google.com` 成功、强制指定原 Docker 网卡直连外网被 `sealed_direct_ping=blocked_expected` 拦截、服务所在节点上对应 `UNIDESK-CODEX-DEV-` NAT 链或 `tun` 计数在 ping 前后增长;涉及 WSL 工作区任务时还必须在开发容器内验证目标 `/mnt/...` 路径可读。`GET /api/dev-containers//status` 必须展示默认路由、`route_8_8_8_8`、`egressFirewallChain` 和 OUTPUT 链跳转。开发容器代理密钥只生成到 `.state/code-queue/dev-proxy/` 与目标节点用户目录,不得提交到仓库。 - 远程维护桥调用:Code Queue 已迁移到 D601 后,`code-queue-backend` 容器内没有主 server 的 `unidesk-backend-core` 容器,不能再把 `bun scripts/cli.ts ssh ...` 实现为本地 `docker exec unidesk-backend-core`。Code Queue 后端发起的 provider 维护命令必须通过主 server frontend `/api/dispatch` 进入 backend-core,再由目标 provider-gateway 执行 `host.ssh`;需要传递脚本时必须使用 base64 临时文件,超过 Host SSH 单命令长度上限时分块上传到目标 `/tmp` 后再执行,避免恢复到本地 Docker broker、交互 stdin 或手工 shell fallback。 diff --git a/src/components/microservices/code-queue/docker-compose.d601.yml b/src/components/microservices/code-queue/docker-compose.d601.yml index d9ab37f0..014685f1 100644 --- a/src/components/microservices/code-queue/docker-compose.d601.yml +++ b/src/components/microservices/code-queue/docker-compose.d601.yml @@ -77,6 +77,7 @@ services: - ${CODE_QUEUE_WORKSPACE_PATH:-../../../..}:/root/unidesk - ${CODE_QUEUE_WORKSPACE_PATH:-../../../..}:/workspace - ${CODE_QUEUE_CODEX_CONFIG_PATH:-/home/ubuntu/.codex/config.toml}:/root/.codex/config.toml:ro + - ${CODE_QUEUE_CODEX_AUTH_PATH:-/home/ubuntu/.codex/auth.json}:/root/.codex/auth.json:ro - ${CODE_QUEUE_SSH_DIR:-/home/ubuntu/.ssh}:/root/.ssh:ro - ${CODE_QUEUE_LOG_DIR:-../../../../.state/code-queue/logs}:/var/log/unidesk - ${CODE_QUEUE_STATE_DIR:-../../../../.state/code-queue}:/var/lib/unidesk/code-queue diff --git a/src/components/microservices/code-queue/src/index.ts b/src/components/microservices/code-queue/src/index.ts index 1b1fd949..e58a44dc 100644 --- a/src/components/microservices/code-queue/src/index.ts +++ b/src/components/microservices/code-queue/src/index.ts @@ -1,7 +1,7 @@ // 重构前 index.ts 只读参考:commit 6a04144d3f5103014f75b637d7e6bc2f45bf007f,blob 56e590c1a6b5ca7ad128bf2c992f60e46c355a58;可用 `git show 6a04144d3f5103014f75b637d7e6bc2f45bf007f:src/components/microservices/code-queue/src/index.ts` 查看。 import { spawnSync } from "node:child_process"; import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "node:fs"; -import { resolve } from "node:path"; +import { dirname, resolve } from "node:path"; import { Database } from "bun:sqlite"; import postgres from "postgres"; import { createHourlyJsonlWriter, logRetentionBytesForService } from "../../../shared/src/rotating-jsonl"; @@ -1702,6 +1702,12 @@ function prepareCodexHome(): void { } else { logger("warn", "codex_config_source_missing", { sourceCodexConfig: config.sourceCodexConfig }); } + const sourceCodexAuth = resolve(dirname(config.sourceCodexConfig), "auth.json"); + if (existsSync(sourceCodexAuth)) { + copyFileSync(sourceCodexAuth, resolve(config.codexHome, "auth.json")); + } else { + logger("warn", "codex_auth_source_missing", { sourceCodexAuth }); + } } function openCodeXdgEnv(root = config.opencodeXdgDir): Record { @@ -1769,7 +1775,10 @@ function collectDevReady(): JsonValue { const composeProbe = runProbe("docker", ["compose", "version"]); const workdirExists = existsSync(config.defaultWorkdir); const dockerSocketExists = existsSync("/var/run/docker.sock"); - const codexConfigReady = existsSync(config.sourceCodexConfig) || existsSync(resolve(config.codexHome, "config.toml")); + const homeCodexConfig = resolve(config.codexHome, "config.toml"); + const sourceCodexAuth = resolve(dirname(config.sourceCodexConfig), "auth.json"); + const homeCodexAuth = resolve(config.codexHome, "auth.json"); + const codexConfigReady = existsSync(config.sourceCodexConfig) || existsSync(homeCodexConfig); const sshKeyProbe = runProbe("sh", ["-lc", "test -d /root/.ssh && find /root/.ssh -maxdepth 1 -type f \\( -name 'id_*' ! -name '*.pub' \\) -perm -400 -print -quit"]); const githubKnownHostProbe = runProbe("ssh-keygen", ["-F", "github.com", "-f", "/root/.ssh/known_hosts"]); const sshSharedReady = existsSync("/root/.ssh") && sshKeyProbe.ok && sshKeyProbe.output.trim().length > 0; @@ -1790,8 +1799,12 @@ function collectDevReady(): JsonValue { codexConfig: { sourcePath: config.sourceCodexConfig, sourceExists: existsSync(config.sourceCodexConfig), - homeConfigPath: resolve(config.codexHome, "config.toml"), - homeConfigExists: existsSync(resolve(config.codexHome, "config.toml")), + homeConfigPath: homeCodexConfig, + homeConfigExists: existsSync(homeCodexConfig), + sourceAuthPath: sourceCodexAuth, + sourceAuthExists: existsSync(sourceCodexAuth), + homeAuthPath: homeCodexAuth, + homeAuthExists: existsSync(homeCodexAuth), ready: codexConfigReady, }, ssh: { diff --git a/src/components/microservices/v3sctl-adapter/v3s/code-queue.k8s.yaml b/src/components/microservices/v3sctl-adapter/v3s/code-queue.k8s.yaml index d761712a..d49fc5e5 100644 --- a/src/components/microservices/v3sctl-adapter/v3s/code-queue.k8s.yaml +++ b/src/components/microservices/v3sctl-adapter/v3s/code-queue.k8s.yaml @@ -393,6 +393,9 @@ spec: - name: codex-config mountPath: /root/.codex/config.toml readOnly: true + - name: codex-auth + mountPath: /root/.codex/auth.json + readOnly: true - name: ssh-dir mountPath: /root/.ssh readOnly: true @@ -440,6 +443,10 @@ spec: hostPath: path: /home/ubuntu/.codex/config.toml type: File + - name: codex-auth + hostPath: + path: /home/ubuntu/.codex/auth.json + type: File - name: ssh-dir hostPath: path: /home/ubuntu/.ssh @@ -619,6 +626,9 @@ spec: - name: codex-config mountPath: /root/.codex/config.toml readOnly: true + - name: codex-auth + mountPath: /root/.codex/auth.json + readOnly: true - name: ssh-dir mountPath: /root/.ssh readOnly: true @@ -666,6 +676,10 @@ spec: hostPath: path: /home/ubuntu/.codex/config.toml type: File + - name: codex-auth + hostPath: + path: /home/ubuntu/.codex/auth.json + type: File - name: ssh-dir hostPath: path: /home/ubuntu/.ssh