多用户生产级缺口批:sweep 鉴权、默认配额、默认 executor、 无价 token 上限

- POST /admin/sweep 改走任一项目 maintainer+ 门槛(与备份管理同口径),否则 403,记 admin_sweep 审计;threshold 下限钳 60 秒
- OAuth 建号 created=True 时写默认配额行(默认 $10/月,CLOUD_DEFAULT_QUOTA_USD_MICROS 可调,INSERT OR IGNORE 幂等);老用户无配额行不受影响
- 建任务未指定且无环境继承时默认 executor=docker(分支继承为空时同理);CLOUD_DEFAULT_EXECUTOR 可覆盖,空字符串恢复旧行为;命令台下拉默认改为 docker
- 无价模型 token 上限:价目表外模型(默认 terra)成本 usd 为 NULL,USD 配额永不生效;有配额行的用户当月无价 token 达 CLOUD_QUOTA_UNPRICED_TOKEN_CAP(默认 2000 万/月)即停,服务端 402、worker 认领/重试/后续消息同口径;报表仍记 unpriced,不折假美元
- 测试:新增 test_multiuser_gaps 11 个 + test_worker_quota 3 个;全量 cloud 668、host 64、deploy 19 全过
This commit is contained in:
host committed 2026-10-27 17:15:09 +08:00
1 parent c1f9313c10
commit 7e081211db
7 files changed
+580 -13

No files matched your search

+21 -3
View File
@@ -88,7 +88,10 @@ client_secret(只走环境变量),不是无认证后门。
心跳即注册)、`POST /tasks/{id}/pr`(已 merged 任务的自动 PR,
默认关闭,缺条件时返回 pr_skipped 而不是失败;见下文“云端派发与
自动 PR”一节)。`POST /tasks` 新增可选 `executor`
(local/docker/cloud,未传表示任意 worker 可认领);
(local/docker/cloud;未传且无环境继承时默认 docker——空
executor(任意 worker 可认领)会让任务落到隔离较弱的 local
worker,是多用户下的隔离缺口;`CLOUD_DEFAULT_EXECUTOR` 可
覆盖,空字符串恢复旧行为);
`POST /projects` 新增可选 `auto_pr`(0/1,默认 0)。
### OAuth 真实浏览器登录(已实现,部署前置仍需用户动手)
@@ -373,7 +376,7 @@ psycopg,写明“需安装 psycopg”),绝不静默回落到 SQLite,
| `POST /tasks/{id}/cancel` | 该项目 `create_task` | `queued` / `running` 改为 `interrupted` 并记审计 `cancel_task`;已是终态(completed / failed / interrupted / merged / merge-failed)返回 `400`;不存在 `404` |
| `POST /tasks/{id}/merge` | 该项目 `approve_merge`(maintainer 以上) | 只同步状态,不执行合并:仅 `completed` 可改为 `merged` 并记审计 `merge_task`,其它状态(含已 merged 的重复调用)返回 `409`;不存在 `404`,越权 `403`,无 key `401` |
| `POST /tasks/{id}/heartbeat` | 该项目 `create_task` | 更新 `last_heartbeat = now`;状态为 `queued` 则改为 `running`;返回任务 |
| `POST /admin/sweep` | 认证即可 | 调 `sweep_stale_tasks(db_path, threshold_seconds=300)`(body 可传 `threshold_seconds` 覆盖默认值);把 `running` 且 `last_heartbeat`(无心跳则用 `updated_at`)早于 `now - threshold` 的任务改为 `interrupted` 并记审计 `sweep_interrupted`;返回 `{"swept": [ids]}` |
| `POST /admin/sweep` | 任一项目 maintainer+(与备份管理同口径),否则 `403`;扫完记审计 `admin_sweep` | 调 `sweep_stale_tasks(db_path, threshold_seconds=300)`(body 可传 `threshold_seconds` 覆盖默认值,下限钳到 60 秒);把 `running` 且 `last_heartbeat`(无心跳则用 `updated_at`)早于 `now - threshold` 的任务改为 `interrupted` 并记审计 `sweep_interrupted`;返回 `{"swept": [ids]}` |
| `POST /admin/backup` | maintainer 以上 | Postgres 时调 `backup_postgres`、SQLite 时调 `backup_db`(body 可传 `out_dir`,否则用 `CLOUD_BACKUP_DIR` 或默认 `cloud-backups`);返回 `{backend, path, size_bytes, task_count}`,绝不返回数据库密码或备份文件内容 |
| `GET /tasks/{id}/cost` | 该任务所属项目 `read` | 成本聚合:`{task_id, project_id, total_tokens, usd_micros, turns, fallback_count, actual_tokens, estimated_tokens, cached_input_tokens, unpriced_actual_tokens}`;真数与估算分开,无价 actual 行进 `unpriced_actual_tokens`;不存在 `404`,越权 `403` |
| `POST /projects/{id}/memory` | 该项目 `create_task` | 写线程摘要 body `{thread_id?, goal, conclusion, files_changed?, open_issues?}`,返回 `201 {id}`,并记审计 `create_memory` |
@@ -956,11 +959,26 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
- 准入:建任务、会话分支、重试在权限校验通过后、写库之前查
配额,本月用量 >= 配额时回 402 `quota_exceeded`(附 month、
quota_usd_micros、used_usd_micros),不建任务、不改状态。
老用户与未设配额的用户默认不限额,行为与过去一致。重试按
老用户与未设配额的用户默认不限额,行为与过去一致。但
OAuth 新用户不再是“建号即不限额”:建号(created=True)时
写一行默认配额(`CLOUD_DEFAULT_QUOTA_USD_MICROS`,默认
10_000_000,即 $10/月;`INSERT OR IGNORE` 幂等,不覆盖管理
员后来改过的额度),并记 `quota_default` 审计。重试按
操作人查配额、费用仍记到原任务创建者名下(建任务与分支的
操作人就是创建者,没有这个错位)。系统任务(GitHub webhook
的 /fix-review,created_by 是 'system')不记到任何用户名下、
不占配额;要把它算到项目所有者头上需另做归属。
- 无价模型 token 上限:价目表目前只有两个 sol 模型有价,默认
模型 gpt-5.6-terra 等中转模型无价、成本行 usd 为 NULL,USD
配额对它们永远不生效(按 0 计)。因此有配额行的用户另受一
道体积护栏:当月无价模型 token(input + output,已记账 +
本轮,usd_micros IS NULL 的行)达到
`CLOUD_QUOTA_UNPRICED_TOKEN_CAP`(默认 20_000_000/月)即
停——服务端建任务回 402 `quota_exceeded`(附
unpriced_token_cap、unpriced_tokens_used),worker 侧认领/
重试/后续消息轮次同口径拦截。这道是防滥用的体积上限,不
是计价:成本报表里无价 token 仍进 unpriced_actual_tokens,
绝不折算成假美元;无配额行的用户不受限。
- 执行时加固(worker 侧,`Worker._quota_exceeded`,口径与准入
一致:当月已记账 + 本轮已累计用量,按价目表换算):认领后
开跑前再查一次(排队期间配额被花完/调低时不跑,任务标