Three isolated real-client attempts were denied before dispatch by approval policy=never. Keep V7 blocked pending an explicitly approved pilot. Rules-Applied: R03,R10,R13
113 lines
11 KiB
Markdown
113 lines
11 KiB
Markdown
---
|
||
id: project-dev-variables
|
||
name: 项目开发变量补给
|
||
status: in_progress
|
||
progress: 86
|
||
---
|
||
|
||
## 设计决策:人填写、Agent 按项目取值(2026-09-26)
|
||
|
||
### 背景与目标
|
||
|
||
开发 Agent 在子项目验收时会反复需要本机专属配置,例如非生产测试账号、沙箱 API Key 或测试端点。用户希望在炼境的项目 UI 填写一次,此后 Agent 能稳定发现并读取,避免把值留在子项目 `.env` 中。首轮只解决 Agent 获取开发变量;子项目进程的 `process.env` 自动注入、CI、生产凭证及部署能力不在范围内。
|
||
|
||
本模块是已完成的 `project-resource-broker` 的**明确例外**:R1–R11 保持 Gitea/API Key/webhook 常态不泄露、按操作代办;只有用户明确放入“Agent 可读开发变量”的值,才允许经专门工具返回明文。不能将旧的 `get_secret` 禁令改写成通用任意取密接口。
|
||
|
||
### 方案选择
|
||
|
||
| 方案 | 收益 | 代价与结论 |
|
||
| --- | --- | --- |
|
||
| 继续各项目 `.env` | 与现有脚本直接兼容 | 需要重复配置,仍有误提交与 Agent 访问不稳定的问题;保留为回退路径 |
|
||
| 炼境保存后由无认证 scoped MCP 返回值 | 最省实现步骤 | scoped URL 仅隔离误操作,不认证调用者;任何能访问本机端口的进程可能取值,否决 |
|
||
| **项目级开发变量 + 认证的 scoped MCP 单项读取** | 人填一次、Agent 跨会话复用、仓库无值 | Agent 会看到明文;需要验证 Codex 的认证头与本机凭据供应链,选定 |
|
||
|
||
### 信任与数据边界
|
||
|
||
- 仅支持登记在炼境的项目、明确标为开发用途且允许 Agent 读取的标量值。用户在对应项目 UI 填写、修改、撤销;Agent 可登记名称/用途和读取状态,但不能通过 MCP 设置值。非生产、低权限、可重置是使用规则,不能靠变量名称自动判断生产性质。
|
||
- SQLite 只保存 `project_id`、规范化变量名、用途、状态所需元数据和随机 `credential_ref`;值保存在现有 Windows Credential Manager adapter。普通列表、readiness、日志、错误与文档不含值。
|
||
- 唯一明文出口是 `read_project_dev_variable(project_id, name)`;必须同时验证真实项目绑定、scoped MCP 连接的项目 ID、客户端认证以及记录已由人配置。全局 `/mcp`、未认证请求和跨项目请求必须拒绝。明文进入 Agent 工具结果与会话,因此本方案不声称对 Agent 保密。
|
||
- MCP 认证材料不得写入项目仓库、URL 或普通日志。Codex `http_headers_helper` 已用两次独立真实 CLI 会话验证:从 Windows Credential Manager 的随机合成项取得 bearer,真实 initialize 请求均带正确 `Authorization`;失败不得降级成无认证接口。正式打包路径尚未验证。
|
||
- 同一 Windows 用户的恶意进程仍可能读取用户可访问的配置或受保护存储;认证只阻断无凭据的本机 HTTP 调用,不提供跨进程强隔离。其他客户端 adapter 未验证前不宣称兼容。
|
||
|
||
### 系统边界与预计文件
|
||
|
||
- 数据:`src-tauri/src/db.rs` 增加兼容 migration;新业务模块 `src-tauri/src/commands/dev_variables.rs` 注入数据库连接和 `CredentialStore`,实现登记、保存、读取、撤销的单点边界。
|
||
- 认证:`src-tauri/src/mcp_server.rs` / `mcp_tools.rs` 在取值调用前验证项目绑定及 bearer;`src-tauri/src/credential_store.rs` 存储随机认证材料;`src-tauri/src/main.rs` 与 `mcp_inject.rs` 评估最小本机 header helper 和配置注入。旧 MCP 工具继续按原契约可用。
|
||
- 人的投影:`src-tauri/src/lib.rs` 注册 Tauri commands,`src/lib/commands.ts` 同步类型与封装;在 `src/components/dashboard/ProjectCard.tsx` 就近入口接入项目级面板,值仅通过显式填写表单提交。
|
||
- Agent 投影:`src-tauri/src/mcp_tools.rs` 暴露登记需求、查询非秘密状态、单项取值;不提供批量导出、任意命令、HTTP/SSH/SQL 或部署代理。
|
||
- 契约同步:`docs/ai-context/project-context.yaml` 与 `docs/ai-context/agent-button-system.md` 说明例外与信任模型;保留旧设计历史,不将项目通用 API Key 改为 Agent 可读。
|
||
|
||
### 验证路径
|
||
|
||
- `[Codex CLI + 合成 MCP]` 用 `http_headers_helper` 提供合成 bearer 后初始化 → 服务端观测认证头;无 header/错误 header → 敏感工具拒绝;未通过则停止实现取值接口。
|
||
- `[cargo test]` 用 in-memory SQLite + 模拟 CredentialStore 登记、保存、重启式重建后读取、撤销 → 状态正确,旧值不可再取,列表与错误不含合成值。
|
||
- `[真实 scoped MCP]` Agent 在 `p1` 登记 `E2E_PASSWORD`,人通过同一本体保存合成值,Agent 单项读取 → 仅 `p1` 的已认证调用得到该值;`p2`、全局路由和无认证调用失败。
|
||
- `human_verify:` 炼境项目 UI 的待填写、已配置、修改和撤销路径可用,默认不显示值;使用者判断是否比重复配置 `.env` 省事。
|
||
|
||
### 风险与阶段闸门
|
||
|
||
- **认证材料供应**:helper 与客户端跨会话读取已用随机合成项验证,正式打包版尚未验证;若后续失败必须回到设计,不把 token 写进受 Git 跟踪的 `.codex/config.toml`。
|
||
- **明文工具结果**:用户选择开发便利,Agent 可能在会话中持有值;先只用合成数据验证,不读取或迁移真实账号。生产和高权限凭证仍用原有代办/CI/运行时机制。
|
||
- **不替代运行时 `.env`**:Agent 读取值不自动设置子进程环境变量;需要时另立任务,不在本迭代暗加 `export_env`。
|
||
- **依赖 PR #22**:从已提交的 R11 基线独立分支开发,先核验 PR #22 状态;不得把新改动混入已有 PR 或原 `master` 脏工作区。
|
||
|
||
## 任务拆解
|
||
|
||
### ✅ V1 — 认证头 helper 真实客户端可行性
|
||
- status: done
|
||
- complexity: M
|
||
- files: src-tauri/src/main.rs, src-tauri/src/lib.rs, src-tauri/src/credential_store.rs, src-tauri/src/mcp_inject.rs
|
||
- depends: project-resource-broker
|
||
- acceptance: "agent_verify: [Codex CLI + 合成 MCP] 通过本机 helper 从模拟受保护存储提供 bearer → initialize 请求含正确 Authorization,重启客户端后仍可读取;helper 缺失/失败时连接或取值 fail closed,项目配置文件不含 token。未通过即停止 V2–V7。"
|
||
- evidence: `cargo test --locked codex_client_reads_windows_helper_header_across_sessions -- --ignored` 双会话通过;真实 exe helper 的读取/缺失 fail closed 测试通过;config 测试断言无 token。
|
||
|
||
### ✅ V2 — 敏感 MCP 调用认证与项目绑定
|
||
- status: done
|
||
- complexity: M
|
||
- files: src-tauri/src/mcp_server.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/credential_store.rs
|
||
- depends: V1
|
||
- acceptance: "[cargo test] 合成 bearer 下 p1 的敏感 tools/call 认证门槛通过;无 header、错误 header、p2 和全局 /mcp 均在 dispatch 前拒绝,现有非敏感 scoped 工具保持可调用;cargo test 全绿。真实取值工具的 HTTP 端到端验收归 V6。"
|
||
- evidence: `sensitive_read_requires_matching_project_bearer_on_both_transports` 通过;完整 `cargo test --locked` 通过。
|
||
|
||
### ✅ V3 — 开发变量元数据与受保护存储本体
|
||
- status: done
|
||
- complexity: M
|
||
- files: src-tauri/src/db.rs, src-tauri/src/commands/dev_variables.rs, src-tauri/src/credential_store.rs
|
||
- depends: V2
|
||
- acceptance: "[cargo test] in-memory DB + 模拟 CredentialStore 完成登记、保存、覆盖、读取、撤销;模拟写入或删除失败时旧值/引用可恢复,列表序列化和错误不含合成值;旧 schema 经 migrate 后可用。"
|
||
- evidence: `dev_variables::tests` 与 `dev_variable_migration_adds_metadata_table_to_existing_database` 通过;数据库重开后仍可读取合成值,删除失败保留旧引用。
|
||
|
||
### ✅ V4 — Tauri 人用命令与前端契约
|
||
- status: done
|
||
- complexity: M
|
||
- files: src-tauri/src/commands/dev_variables.rs, src-tauri/src/lib.rs, src/lib/commands.ts
|
||
- depends: V3
|
||
- acceptance: "[Tauri command/cargo test] 以项目 ID 查询待填写/已配置、保存合成值、撤销 → 状态随真实存储变化;常态返回类型无 value,pnpm typecheck exit 0。"
|
||
- evidence: `tauri_commands_use_same_state_and_never_return_value_in_list` 通过,`mise exec -- pnpm typecheck` exit 0。
|
||
|
||
### ✅ V5 — 项目 UI 填写与撤销投影
|
||
- status: done
|
||
- complexity: M
|
||
- files: src/components/dashboard/ProjectCard.tsx, src/components/dashboard/ProjectDevVariablesPanel.tsx, src/lib/commands.ts
|
||
- depends: V4
|
||
- acceptance: "agent_verify: [Tauri command/cargo test] 填写、覆盖、撤销同一本体状态正确,pnpm typecheck exit 0。human_verify: 在对应项目可见用途与待填写状态,填写/修改/撤销顺手,常态不显示值。"
|
||
- human_verify: pending(未启动隔离分支的真实桌面 UI;交人终验时判断交互摩擦)
|
||
- evidence: Tauri command 真实函数路径测试与 TypeScript 类型检查通过;填写值从 React Query mutation 缓存中移除。
|
||
|
||
### ✅ V6 — Agent 登记、状态和单项读取工具
|
||
- status: done
|
||
- complexity: M
|
||
- files: src-tauri/src/mcp_tools.rs, src-tauri/src/commands/dev_variables.rs, src-tauri/src/mcp_server.rs
|
||
- depends: V4, V5
|
||
- acceptance: "[真实 scoped MCP] p1 Agent 登记 E2E_PASSWORD → UI 待填写;人保存合成值后已认证 Agent 单项读取成功;未认证、p2 和全局路由拒绝;tools/list 无批量导出或任意代理。"
|
||
- evidence: `scoped_dev_variable_http_pilot_denies_missing_wrong_cross_and_global_auth` 真实本机 HTTP 测试通过;撤销后读取失败,工具清单无批量出口。
|
||
|
||
### 🔴 V7 — 契约、合成试点与改动边界验收
|
||
- status: blocked
|
||
- complexity: M
|
||
- files: docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md, .blueprint/modules/project-dev-variables.md
|
||
- depends: V6
|
||
- acceptance: "agent_verify: [真实 Codex CLI + 合成数据] 跨会话登记→填写→读取→撤销后再读失败,项目目录无新 .env 或 token;[CLI] cargo test、pnpm typecheck、git diff --check 均 exit 0。human_verify: 不使用真实生产账号、不触发真实 PR/部署/数据库写入,确认日常开发摩擦是否下降。"
|
||
- blocked_reason: 真实 Codex CLI 三次隔离合成试点均在调用 `register_project_dev_variable` 时被客户端审批策略拒绝(`MCP tool call requires approval, but approval policy is never`),服务端未收到登记请求;未尝试绕过审批。需用户决定是否允许一次受控、可审批的真实客户端试点。
|
||
- pending: 真实 Codex CLI 调新工具完成整条业务链、正式打包版、桌面 UI 人眼便利性终验;V1 的真实 Codex 认证初始化与 V6 的真实 scoped HTTP 业务链已分别验证,不把组合证据冒充本卡端到端完成。
|