From 3d45edc8b4877a8eaa500c9a51462044c01db6a2 Mon Sep 17 00:00:00 2001 From: host Date: Tue, 27 Oct 2026 04:07:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=91=BD=E4=BB=A4=E5=8F=B0=E4=BA=91?= =?UTF-8?q?=E7=AB=AF=E7=8E=AF=E5=A2=83=E2=80=94=E2=80=94=E9=80=89=E3=80=81?= =?UTF-8?q?=E5=BB=BA=E3=80=81=E7=AE=A1=20+=20=E4=BB=BB=E5=8A=A1=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 环境是项目仓库的命名基准检出(项目 + 仓库快照 + 分支 + 执行器), 不是官方 Codex 云端环境(无公开调用口,不宣称能建它)。 - DB:environments 表(creating/ready/failed/archived,部分唯一索引 保证同项目未归档名唯一、归档名可重用)+ tasks.environment_id, server/worker/db 三处迁移同步(含 #67 的 goal_mode,不丢新语义)。 - API:POST/GET /environments、GET /environments/{id}、 POST /environments/{id}/archive;任务创建选环境须本项目已 ready、 执行器一致或继承、cloud 执行器拒绝。 - Worker:run_all 每轮开头准备 creating 环境(真实 git clone + checkout 分支,幂等、无 preparing 卡死态,准备期归档 CAS 不覆盖); 环境任务派发时 task[repo] 指到环境检出(租户 Docker 容器内路径 + envs 整目录挂载),任务仍一任务一 worktree。 - Console:环境卡(列表/新建/归档,未 ready 置灰)+ 新建任务环境下拉 + 任务列表 [环境] 标记;公网 nginx 须同批代理 /environments。 - 单测:新增 test_task_environments.py 14 个;本地实弹(真实 git clone octocat/Hello-World,不跑模型)项目→环境→任务→归档全通。 --- cloud/README.md | 48 +++ cloud/console.html | 94 ++++- cloud/db.py | 21 +- cloud/server.py | 330 ++++++++++++++++- cloud/tests/test_task_environments.py | 499 ++++++++++++++++++++++++++ cloud/worker.py | 243 ++++++++++++- 6 files changed, 1224 insertions(+), 11 deletions(-) create mode 100644 cloud/tests/test_task_environments.py diff --git a/cloud/README.md b/cloud/README.md index e94a5d6..6bcc080 100644 --- a/cloud/README.md +++ b/cloud/README.md @@ -782,6 +782,54 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳 任务仍需 worker 认领执行;`CloudExecutor` 仍是占位符;公网 https 入口仍需部署侧反向代理 + TLS。 +## 云端环境(选、建、管) + +环境是项目仓库的一个命名基准检出,不是官方 Codex 的云端环境 +(那是官方产品自己的后端,本控制面没有它的公开调用口,不宣称 +能建它)。本控制面的环境管的是主机/Docker 这边的基准:项目 + +仓库快照 + 分支 + 执行器,任务选了环境后仍是一任务一 worktree +(host.py 从环境检出 HEAD 为任务建分支),任务隔离、合并、提 PR +全都不变。 + +- API:`POST /environments`(project_id、name 必填,branch 可选 + 空=默认分支,executor 可选 local/docker;建完 status=creating)、 + `GET /environments?project_id=`(默认排除已归档, + `include_archived=1` 才含归档)、`GET /environments/{id}`、 + `POST /environments/{id}/archive`。写操作(建/归档)要求项目 + create_task 权限,读要求 read 权限。 +- 创建规则:项目没有 repo_url 时 400(没有仓库就没有环境); + 同项目未归档环境名唯一(409,归档过的名字可重用,部分唯一 + 索引保证);分支名只允许字母数字与 `_ . - /`,不以 `-` 开头、 + 不含 `..`、不以 `.lock` 结尾;executor 只接受 local/docker。 +- 准备:worker 在 `run_all` 每轮开头准备所有 creating 的环境: + 目录不存在时 `git clone`,存在时校验 git 仓库且 origin 与环境 + repo_url 一致再 `fetch`,分支非空时 `checkout` 该分支(不存在 + 则失败,绝不自动建分支),成功变 ready、失败变 failed 并写 + error_text。准备本身幂等,没有 preparing 中间态——worker 中途 + 死了、环境仍是 creating 时下一轮会重新准备;准备期间用户归档 + 了环境时 CAS 写回改不到行,状态保持 archived。 +- 任务绑定:`POST /tasks` 接受可选 `environment_id`,必须是本项目 + 已 ready 的环境(creating/failed/archived 一律 400 并写明状态); + 环境 executor 非空且任务 executor 已指定时必须一致,任务 + executor 未指定时继承环境 executor(认领过滤靠它);cloud + 执行器没有环境检出路径,一律拒绝。worker 派发时环境任务不走 + 项目仓库路径,直接把 `task["repo"]` 指到环境检出(租户 Docker + 为容器内路径 `/var/lib/codex-app-server/envs/`,租户挂载里 + 整目录挂载该项目 envs 目录;非租户为 `repos_dir/envs/`)。 +- 管理:归档幂等(已归档再归档返回 200);有 queued/running + 任务正在用环境时归档 409;归档只改状态,不删检出目录(已完成 + 任务的 worktree 独立,检出留着可查,磁盘清理另行手动做)。 + 重试(retry)只改任务状态,environment_id 原样保留;会话分支 + (fork)不继承 environment_id——分支是 thread/worktree 分叉, + 不是在环境上重开任务。 +- 命令台:项目卡下方新增“环境”卡(列表/新建/归档,creating→ + ready 的过程靠 10 秒自动刷新可见,失败原因直接显示在列表里); + 新建任务栏新增环境下拉(未 ready 的置灰、不可选),任务列表里 + 带环境任务会标 `[环境 <短 id>]`。公网 nginx 若要让页面调通环境 + 接口,须把 `/environments` 同批代理到双机上游(与 `/console`、 + `/tasks`、`/projects`、`/workers`、`/health` 同款,否则页面能打开 + 但环境区永远加载失败)。 + ## 运维批(本轮新增:部署、恢复演练、监控、心跳、多用户验证) 细则在 `deploy/README.md`,这里只记代码侧的接口与默认值: diff --git a/cloud/console.html b/cloud/console.html index 07af8e3..ff60c59 100644 --- a/cloud/console.html +++ b/cloud/console.html @@ -118,9 +118,30 @@ +
+

环境 (选、建、管:当前项目的基准检出)

+
+ + +
+
先选项目
+
+ + + + +
+ +
+

新建任务

+