Docker tasks do not inherit the worker process environment, they only get extra_env (-e). SEARXNG_URL in the worker unit EnvironmentFile therefore never reached codex -> MCP inside the container, so the auto chain skipped SearXNG and only DuckDuckGo was tried (live e2e: 'all search providers failed: duckduckgo (DNS)'). _docker_task_context now merges any set SEARXNG_URL / PERPLEXITY_API_KEY / EXA_API_KEY / TAVILY_API_KEY / BRAVE_API_KEY from the worker environment into extra_env (values never logged; paid keys follow the same extra_env path as gateway keys). Tests: 2 new docker-env tests; websearch + tenant isolation = 25 passing; full cloud suite 1038 passing (4 skipped).
330 lines
21 KiB
Markdown
330 lines
21 KiB
Markdown
# CHANGELOG
|
||
|
||
语义化版本见 `VERSION`(当前 0.1.0)。以下按日期倒序,只列已交付
|
||
的大项,每项附实测证据;未建成的东西在“诚实边界”里明说,不夸大。
|
||
|
||
## 2026-10-28:网页搜索 Docker env 透传(e2e 失败后的修复)
|
||
|
||
- 根因:Docker 任务容器不继承 worker 进程环境,只拿
|
||
extra_env(-e);`SEARXNG_URL` 只写进 worker unit 的
|
||
EnvironmentFile 时,容器内 codex -> MCP 根本看不到它,
|
||
auto 链跳过 SearXNG、只剩 DuckDuckGo,实弹 e2e 报错
|
||
“all search providers failed: duckduckgo (DNS)”。
|
||
- 修复:`cloud/worker.py _docker_task_context` 把 worker 环境
|
||
里已设置的 `SEARXNG_URL` / `PERPLEXITY_API_KEY` /
|
||
`EXA_API_KEY` / `TAVILY_API_KEY` / `BRAVE_API_KEY` 合并进
|
||
extra_env(值不进日志;付费 key 与网关 key 同款走法)。
|
||
- 单测:新增 2 个(有/无搜索 env 时的容器 env),连同
|
||
websearch 旧测、租户隔离旧测共 25 个通过;cloud 全量
|
||
1038 个通过(skip 4)。只本地提交,未推、未建 PR、
|
||
未合并、未部署;重跑 e2e 须等本修复部署后。
|
||
|
||
## 2026-10-28:网页搜索接 worker + egress(公开 SearXNG 路)
|
||
|
||
- `cloud/worker.py --websearch-mcp`(默认关):开启后
|
||
`_tenant_dirs` 播种 config.toml 之后自动播种 websearch
|
||
MCP(容器内路径),老租户下一次任务自动合并;
|
||
autoscaler unit 已加该 flag(主机上须单独应用 unit)。
|
||
- `deploy/egress/tinyproxy-filter` 已加 Perplexity、Exa、
|
||
Tavily、Brave、DuckDuckGo 与精确的 `searx.diante.me`
|
||
(不用 diante.me 通配,防任务经代理摸到控制面)。
|
||
- 单测:`cloud/tests/test_websearch_worker.py`(播种开/关、
|
||
老租户合并、filter 的 BASIC 语法检查)与 websearch
|
||
旧测、租户隔离旧测共 23 个全部通过。
|
||
诚实边界:本批只本地代码,未推、未建 PR、未合并、
|
||
未部署;SearXNG 主机部署、DNS、证书、nginx 与
|
||
tinyproxy 重载均在主机步骤里,另见 docs/web-search-mcp.md。
|
||
|
||
## 2026-10-28:网页搜索 MCP v1(oh-my-pi 模式,Python 实现)
|
||
|
||
- 新增 `cloud/websearch.py` + `cloud/websearch_mcp.py`(纯标准库):
|
||
stdio MCP 服务器暴露 `web_search` / `web_fetch`,provider 链
|
||
按 Perplexity → Exa → Tavily → Brave → SearXNG → DuckDuckGo
|
||
的 auto 顺序回退,pin 单个名字时不回退;抓取去脚本样式、
|
||
默认 15000 字符头尾截断并标 `[TRUNCATED]`;key 只走进程
|
||
环境变量,错误串不含 key(单测断言)。
|
||
- 新增 `ensure_codex_home_websearch`:把两个脚本拷进租户
|
||
codex-home,缺失时追加 `[mcp_servers.websearch]`,已有
|
||
section 绝不重写、租户改过的脚本绝不覆盖。
|
||
- 单测:`cloud/tests/test_websearch_mcp.py` 12 个全部通过
|
||
(链式回退、pin 不回退、全部失败、Perplexity 答案与引用、
|
||
Brave/Tavily 映射、DuckDuckGo HTML 解析、抓取截断与拒绝、
|
||
MCP 帧、配置合并幂等),不联网、不需要真实 key。
|
||
诚实边界:付费后端只以 mock 验证请求字段与映射,未用真实
|
||
key 实测;worker 自动播种未接线、生产 egress 白名单未改、
|
||
本批只本地提交,未推、未建 PR、未合并、未部署。
|
||
|
||
## 2026-10-28:长 goal 任务异常退出可断点续跑(不再 terminal failed)
|
||
|
||
- 实弹事故:11:49 认领的 8 轮 goal 长任务跑约 60 分钟后终局
|
||
failed、无 goal 结论;host-state 仍是 goal-running、
|
||
goal_rounds_done=0、turns 为空——第 1 轮 turn 从 11:53 起
|
||
再无进展,600 秒回合超时未触发(同期探针脚本与 autoscaler
|
||
一并静默,12:48:48 全局 OOM 杀掉另一租户的 supermemory-ser
|
||
后系统恢复,12:48:56 任务判 failed)。根因链:宿主机内存
|
||
挤兑致全机停滞 + 恢复时 goal 进程异常退出、无结论行,云端
|
||
旧语义(returncode 非 0 一律 failed)把可续跑的断点判成终局。
|
||
- 修法:host.py `run_goal_loop` 回合异常(turn 抛错、验收起
|
||
不来、检查点落盘失败)不再向上传播,统一记 interrupted、
|
||
打印 "goal interrupted after rounds: <原因>" 标记行、exit 0;
|
||
cloud/sandbox.py 四处 goal 结论解析(local goal、docker
|
||
goal、docker 两处 resume)同步新语义:interrupted 标记 →
|
||
interrupted;returncode 非 0 但无结论 → interrupted(断点在
|
||
盘上,保住 /goal-resume 活路);有 met/unmet 结论的非 0 退出
|
||
仍 failed,rc==0 无结论仍 failed;worker 终局审计名与状态
|
||
对齐(interrupted 记 worker_interrupted,不再误记 failed)。
|
||
- 单测:sandbox 新测 5 个(local 无结论非 0 → interrupted、
|
||
非 0 带 unmet 结论仍 failed、interrupted 标记、docker goal 与
|
||
docker 两处 resume 无结论非 0 → interrupted),host 新测 1 个
|
||
(turn 抛错 → interrupted + 标记行 + 落盘原因),并更新 1 个
|
||
旧测(首轮前落盘断言:旧“异常上传播 + 状态 goal-running”
|
||
改为新语义)。
|
||
诚实边界:导致恢复时退出的确切异常已不可考(任务行与执行
|
||
结果已被探针清理删掉,stderr 无存档);本次只修“异常退出
|
||
的终局语义”,不修宿主机内存挤兑本身(另一租户容器,不动);
|
||
本批只本地提交,未部署、未做生产实弹复验,部署与复验另等
|
||
用户点头。
|
||
|
||
## 2026-10-26:收尾(Docker 沙箱实测 + 云端合并状态)
|
||
|
||
- Docker 沙箱执行器接通并在外部主机上真实跑通:镜像
|
||
`codex-app-server:latest`(codex 0.164.0-alpha.9,codex-resources
|
||
必须放在 codex 二进制旁边、且 bwrap 进 PATH,否则容器内报
|
||
bubblewrap is unavailable)、worker `--executor docker`(额外
|
||
挂载 `/app/work` 与 `/var/lib/codex-app-server`,容器用户取
|
||
宿主服务用户 uid/gid)、Docker 任务 triple 加函数约 18 秒完成、
|
||
容器内合并后主仓库含 add + mul2 + triple、容器经 --rm 清理。
|
||
诚实边界:这台主机/内核禁非特权 user namespace,bwrap 在
|
||
主机和容器内都起不来,因此生产模式是 Docker 隔离 + 容器内
|
||
danger-full-access(host-config-docker.json),不是双层沙箱。
|
||
- 云端合并状态:新增 `POST /tasks/{id}/merge`,只同步状态、
|
||
不执行合并(合并仍由 host.py merge 执行,成功后再调本接口);
|
||
仅 completed 可改为 merged,maintainer 以上(approve_merge),
|
||
其它状态与重复调用返回 409,越权 403,审计记 merge_task。
|
||
- 实测证据:云端测试 219 个、host 测试 27 个、合并状态单测
|
||
4 个,全部通过(`python3 -m unittest discover -s cloud/tests`
|
||
与 `python3 -m unittest discover -s tests`)。
|
||
|
||
## 2026-10-26:缺口补齐(云端控制面第二批)
|
||
|
||
- 记忆 / KB / 成本 / 模型接成 HTTP 端点:记忆写/查/ supersede、
|
||
KB 索引/搜索(按项目隔离)、按任务与按项目/按日的成本(实际与
|
||
估算分开)、模型总览;全部走 `X-API-Key` + 项目 RBAC,
|
||
401 / 403 / 404 语义与跨项目隔离均有测试。
|
||
- SQLite 并发:共享 db 助手(WAL + busy_timeout + 有界重试),
|
||
并注明单机上限,Postgres 是下一阶段,不是本版已做。
|
||
- OAuth 脚手架:`cloud/oauth.py`,state 10 分钟过期、生成授权 URL、
|
||
回调验 state 后用可注入的 HTTP 客户端换 code;secret 只走环境变量,
|
||
没有假登录页,真实 OAuth 应用注册仍缺。
|
||
- 推送订阅与分发:`cloud/push.py`,webhook / slack / webpush /
|
||
apns / fcm 通道,即时/摘要/静默偏好,只发状态变更事件;缺发送方/
|
||
凭证时报 error,绝不静默报成功;真实推送凭证未配、真实推送未发。
|
||
- 实测证据:云端测试从 102 个涨到 157 个,全部通过
|
||
(`python3 -m unittest discover -s cloud/tests`);
|
||
host 测试同期 27 个,全部通过
|
||
(`python3 -m unittest discover -s tests`);
|
||
另有一次 live smoke:记忆写 201/读通、KB 索引/搜索命中、
|
||
OAuth 登录 URL 只返回不跳转、推送分发报“无发送方”错误、
|
||
无 key 查项目返回 401。
|
||
|
||
## 2026-10-26:云端控制面(第一批)
|
||
|
||
- 新 `cloud/` 包,零第三方依赖、只用 Python 标准库:
|
||
`server.py`(ThreadingHTTPServer + SQLite 状态,任务状态机与
|
||
host.py 对齐,含心跳与 stale 回收)、`auth.py`(API key 只存
|
||
SHA-256 哈希、按项目 RBAC owner / maintainer / contributor /
|
||
viewer、追加式审计日志)、`queue.py`(依赖拓扑 + 优先级 +
|
||
预算门)、`sandbox.py`(Local / Docker / Cloud 三种执行器,
|
||
缺 Docker 或缺云账号时明确报错、不静默降级)、`memory.py`
|
||
(线程摘要,可删/可 supersede,只取 top-k)、`kb.py`
|
||
(SQLite FTS,LIKE 兜底,按项目隔离)、`models.py`
|
||
(404 / 429 / 5xx / 超时才 fallback,不对 401 / 400 fallback,
|
||
实际用到的模型如实记录)、`cost.py`(读不到账单时返回
|
||
available=false,估算值明确标 estimated,实际与估算分开)、
|
||
`webhooks.py`(GitHub HMAC + Slack 签名与重放窗口,只解析,
|
||
只在状态变更时推送)。
|
||
- 实测证据:云端测试 102 个、host 测试 27 个,全部通过;
|
||
smoke:health 正常、无 key 查项目返回 401;代码里无密钥。
|
||
- 诚实边界:这是本地可跑的控制面演示,不是已建成的真实云服务;
|
||
仍缺常驻宿主、GitHub 远端 + PAT、Slack App、云容器账号、
|
||
定时/worker 跑模型时的模型 key 供给方式、真实 OAuth 应用注册、
|
||
真实推送凭证;server 本身不跑模型回合。
|
||
|
||
## 2026-10-26:GitHub PR / CI 一条线
|
||
|
||
- `host.py github-workflow`:生成 `.github/workflows/host-verify.yml`
|
||
(checkout + python + unittest),幂等。
|
||
- `host.py pr`:校验任务已完成且有分支,push 分支 + `gh pr create`,
|
||
成功后把 pr_url / pr_number 写回任务记录。
|
||
- `host.py pr-checks`:解析 `gh pr checks`,all_pass 仅在 checks
|
||
非空且全部 pass 时为真,空 checks 不算过。
|
||
- `host.py pr-merge`:先查 checks,未全过拒绝且不调 merge,
|
||
全过才 `gh pr merge --merge` 并标 merged,不动本地主分支
|
||
(与本地 `merge` 是两条线)。
|
||
- 实测证据:全部 gh 调用走可 mock 的 run_gh;测试从 19 个涨到
|
||
27 个,全部通过,含本地 bare 远端 push + 假 gh PR(写回编号 42)
|
||
与 workflow 幂等。
|
||
- 诚实边界:真实 PR 本轮没跑——本仓没有 git remote,gh 未登录;
|
||
真实 PR 仍缺远端与经 Secure Vault 的 PAT,那两样齐了才能跑。
|
||
|
||
## 2026-10-26:第二阶段(daemon / health / 自动恢复)
|
||
|
||
- `host.py daemon`:双 fork 到 PID 1,pidfile + host-daemon.log,
|
||
每轮跑 recover + run-due(各为独立 host.py 子进程);
|
||
`daemon-status` / `daemon-stop` 配套。
|
||
- `host.py health`:不跑模型,只查二进制、配置、state 有效性、
|
||
备份、daemon pid、server initialize + list、缺失 worktree;
|
||
失败时 exit 1。
|
||
- `host.py restore-state`:state 损坏/丢失时从 .bak / .bak.1 /
|
||
.bak.2 恢复,损坏文件改名为 .corrupt.<时间戳> 留证。
|
||
- `host.py recover`:超过 `--older-than`(默认 3600 秒)未更新的
|
||
running 任务标 interrupted 并记日志,可用 `run` 续跑;
|
||
故意不自动重跑模型回合,避免未经批准花 relay 额度。
|
||
- 附带修掉一个静默丢数据洞:旧 state 读取把 JSON 解析错误
|
||
当空 state 吞掉。
|
||
- 实测证据:daemon pid 的 PPID 为 1,测试中每 5 秒 tick、
|
||
stop 后 pidfile 删除;health 先 ok=true(7 个 server 线程),
|
||
损坏 state 后 ok=false / exit 1,restore 后 ok=true(备份版本 9);
|
||
recover 把 2 小时未动的 running 标 interrupted、1 分钟的保留;
|
||
测试从 15 个涨到 19 个,全部通过。
|
||
- 诚实边界:daemon 本身活不过平台 VM 回收(与微信桥 serve 同理);
|
||
VM 上更稳的定时是 Muse cron 调 run-due / health,不是只靠 daemon
|
||
或 crontab;本轮没有自动装 Muse cron 或 crontab,因为 run-due
|
||
到期会花模型额度,装定时需用户明确指定间隔与是否允许自动跑模型。
|
||
|
||
## 2026-10-26:第二类缺口(全部补齐)
|
||
|
||
- 合并自动验测 + 回滚:`merge` 默认仓库有 tests/ 就跑 unittest,
|
||
验测失败 reset 回合并前 HEAD,分支/worktree 保留;
|
||
`--no-verify` 跳过,`--verify` 可指定命令。
|
||
- 任务依赖 + 优先级 + 自动重试:jobs 里 depends_on / priority,
|
||
拓扑排序 + 按波次跑,依赖失败时依赖任务标 skipped 不跑;
|
||
依赖任务从依赖分支建 worktree(有血缘关系),不再文件拷贝;
|
||
dispatch / parallel 里 `--max-retries`,失败后同 prompt 再跑,
|
||
重试前查预算,默认 0、行为不变。
|
||
- 评审评论回流:`review-comment --file --line --comment` 把评论
|
||
存进任务,`review` 列出 open 评论,`fix-review` 按 open 评论
|
||
逐条修、修完标 resolved。
|
||
- server 崩溃重连:Server.ensure_alive,死了重拉并 resume thread
|
||
(thread 在 CODEX_HOME 里不丢);request 遇 BrokenPipe 重试一次;
|
||
单测 kill 进程后重拉 + list 能通。
|
||
- 成本预算 + 失败通知:配置 max_daily_turns,按 turn 数算
|
||
(协议不暴露真实 token 数,这是诚实边界),超预算不派发/
|
||
不重试;usage 记在 state 里,`doctor` 可看;失败时写
|
||
host-run.log + 跑配置的 notify_command(本地 shell,
|
||
模板 {task} {status});`failures` 列失败任务,`doctor` 列
|
||
失败/缺失 worktree/open 评论。
|
||
- 多机器 state 版本:state 加 version / last_writer / updated_at,
|
||
保存时磁盘版本比加载时新就记 STATE-CONFLICT 日志并合并保留
|
||
双方任务,不静默丢;真多机中心同步仍是检测、不是云同步。
|
||
- 合并时不再提交缓存文件:merge 前清 worktree 的
|
||
__pycache__ / *.pyc,主仓的 .pyc 改动先还原;单测覆盖 pyc 不入库。
|
||
- 实测证据:测试从 3 个涨到 15 个,全部通过;模型验证在
|
||
gap2 / gap3 仓上跑通(依赖并行、评审评论修复、带自动验测的合并),
|
||
并在实测中拦下两处真问题:文件拷贝依赖分支导致重复加同一段的
|
||
合并冲突(-X ours 兜底的新增行核对拦下一次 mul2 丢失并回滚,
|
||
后改从依赖分支建 worktree 修复)、merge 的 git add -A 提交
|
||
__pycache__ / *.pyc(已修)。
|
||
- 诚实边界:独立任务在同一文件位置追加仍会合并冲突,这是 git
|
||
自身行为,host abort / 回滚、不猜合并结果;多机共享 state
|
||
现在能检测、不静默丢,但 ChatGPT 云端式中心同步不在本地 host
|
||
范围内。
|
||
|
||
## 2026-10-26:生产加固
|
||
|
||
- state 文件锁(fcntl,只锁短读/写;保存时按任务/自动化名合并
|
||
磁盘状态,多进程并发写不丢对方任务;双进程 0.5 秒窗口竞写实测
|
||
双方任务都保留)。
|
||
- 项目配置 host-config.json(`init` / `config` 命令,CLI > 配置 >
|
||
默认;API key 绝不进配置文件,只走环境变量)。
|
||
- 定时自动化:run-due 在 host-run.log 记派发/跳过,
|
||
`automations --cron-line` 只打印 crontab 行不装机,
|
||
`--install-cron` 幂等装机;本轮测试里没有实际装 crontab,
|
||
避免未经批准的模型花费,且 VM crontab 可能活不过平台回收,
|
||
Muse cron 调 run-due 是更稳的 VM 选项。
|
||
- 诚实边界:锁只防同一台机器,不防两台机器共享同一 state 文件;
|
||
host.py 本身不常驻,仍需外部调度器触发 run-due。
|
||
|
||
## 2026-10-26:真实项目跑一轮(在打包前)
|
||
|
||
- 在 codex-app-server 本仓(非敏感的 wechat-bridge)上 dogfood:
|
||
先 git init(初始提交,.gitignore 排除 367M bin/、work/、
|
||
host-state*.json、host-run.log、本地 host 配置),真实任务用
|
||
单独的 host-state-real.json。
|
||
- 并行真实任务:real-tests 加 tests/test_host.py(3 个 unittest,
|
||
全部通过,host.py 本身未改);real-backup 加 save_state 备份轮转
|
||
(.bak / .bak.1 / .bak.2,在锁内)。
|
||
- 只读委派评审 real-backup 后发现多步轮转非原子、有截断拷贝风险、
|
||
无 fsync、fcntl-less 平台问题;便宜的修复已在 fix 回合里落地
|
||
(with open 读 state / 写临时文件、修掉 real-tests 发现的
|
||
ResourceWarning、备份经临时文件 + os.replace 拷贝)。
|
||
- 两任务都合并进 main,main 测试全过且无警告,手动双保存确认
|
||
.bak 里是上一版。
|
||
- 真实项目才有的教训:非 git 项目要先 git init 才能用
|
||
worktree / 合并门;缺 .gitignore 会把 bin/ 提交进去;
|
||
真实任务与玩具任务必须用不同的 state 文件。
|
||
- 记录在 TEST-REPORT.md;本轮打包前仍欠的下一步是:安装文档、
|
||
host 快速开始、版本/变更日志、合并后主仓自动验测(那一步当时
|
||
仍是手动)——其中合并后自动验测已在“第二类缺口”里补上,
|
||
安装文档与版本/变更日志由本 CHANGELOG 与 install.sh / README
|
||
快速开始补上。
|
||
|
||
## 2026-10-25:host 编排(多线程并行、worktree 隔离、评审/合并门)
|
||
|
||
- 单个 app-server 进程托管多 thread,后台读线程按 threadId 分发
|
||
turn / completed,多 thread 的 turn 真正并发,不是轮流排队。
|
||
- `dispatch` 自动 `git worktree add -b host/<task>`,一任务一
|
||
worktree;非 git 仓库退化为独立目录拷贝。
|
||
- `review`:status + diff + untracked;`merge`:先在 worktree
|
||
提交,再 `--no-ff` 合并进主仓,冲突自动 abort;
|
||
`discard`:删 worktree 和分支。
|
||
- `run`(resume + 新回合)、`fork`、`archive`、`list`;
|
||
`delegate`:子 thread(默认 read-only)跑完,结果可
|
||
`--report-to-parent` 汇总回主 thread。
|
||
- `goal`:同一 thread 多回合,直到 `--verify` 验收命令通过
|
||
或到 `--max-rounds`。
|
||
- `automations --add` + `run-due`:到期任务自动派发,结果进
|
||
host-state.json 评审队列;未到期不重复派发(幂等实测)。
|
||
- 边界实测:read-only thread 的写文件请求被拦下、文件确实没建;
|
||
untrusted + deny 时 agent 收到审批拒绝、没拿到命令输出;
|
||
模型内 3 子代理并行检查视角确实不同;turn/interrupt 后状态
|
||
为 interrupted,`compact` 需先 resume(host.py 的 compact 命令
|
||
已包好这一步);合并冲突时主仓干净、冲突 worktree 留着人工解;
|
||
隔离 CODEX_HOME 下的 MCP 工具能被发现并调用(MCP 审批是
|
||
elicitation 协议、不是通用 decision 字段,host.py 已分流处理,
|
||
修过一轮才通);同一 thread 内换模型续跑、turn 的 model 字段
|
||
随之改变;turn/steer 能把进行中的长任务改短;3 并行 worktree
|
||
互不污染;review 时 diff 为空也要看 untracked;不存在的模型
|
||
导致 task failed 并带上游 404 错误、host 不崩。
|
||
- 工具执行修复(VM 专属):系统裸 codex 二进制缺两个同伴
|
||
(codex-code-mode-host 与 codex-resources/bwrap),工具会
|
||
fail closed;配对目录 `bin/`(codex + codex-code-mode-host +
|
||
codex-resources/bwrap,约 367M,官方 0.164.0-alpha.9)经
|
||
start.sh 优先使用;relay 支持的模型回合实测 10.3 秒内在
|
||
work/xcm-test/test-result.txt 写出“codex-ok”。Mac 上正常
|
||
npm 安装自带这三件套,不需要 bin/。
|
||
- 协议层:start.sh 启动官方 codex app-server(0.164.0-alpha.9),
|
||
零依赖 Python stdio JSON-RPC 客户端(initialize / thread/start /
|
||
turn/start、审批、resume / fork),宿主级并行多 thread 编排,
|
||
配置示例与官方协议 schema(generate-json-schema 导出)。
|
||
隔离 CODEX_HOME 下验证到模型边界为止:initialize / thread/start /
|
||
turn/start 与事件流都通,turn 随后因缺模型登录返回 401 failed,
|
||
这是登录边界、不是协议 bug。
|
||
- 诚实边界:云端派发、推送通知、Slack / GitHub 触发、GUI inline
|
||
diff 评论、Computer Use 都不在开源里、没有复刻;本版是本地
|
||
host + 本地可跑的控制面演示,不是真实云服务。
|
||
|
||
## 诚实边界(贯穿所有版本)
|
||
|
||
- 没有建成真实云服务:cloud/ 是本地可跑的控制面演示,
|
||
生产仍缺常驻机器、GitHub 远端 + PAT、Slack App、云容器账号、
|
||
模型 key 供给方式、真实 OAuth 应用注册、真实推送凭证。
|
||
- 没有真实 PR、没有真实推送:PR 只做到代码 + 本地可测部分
|
||
(假 gh、本地 bare 远端);推送只做到订阅与分发逻辑,
|
||
缺凭证时明确报错。
|
||
- 没有真实 token 成本:协议不暴露真实 token 数,预算按 turn
|
||
数算;成本端点里实际与估算严格分开,估算值标 estimated。
|
||
- 没有在任何文件、state、日志或汇报里写密钥:API key、PAT、
|
||
Slack token、OAuth secret 一律只走环境变量或 Secure Vault,
|
||
配置文件里只放 env_key 这类名字,不放值。
|