chore(blueprint): plan Codex model switch

This commit is contained in:
lanrtop 2026-09-21 12:53:25 +09:00
parent 6e5d3d89bc
commit 99d41d5939
2 changed files with 147 additions and 1 deletions

View File

@ -6,7 +6,7 @@ description: >
成为每个项目的贴身导师。炼境本身亦被纳入其中, 成为每个项目的贴身导师。炼境本身亦被纳入其中,
形成可自我进化的开发智能闭环。 形成可自我进化的开发智能闭环。
iteration: 1 iteration: 1
updated: 2026-09-11 updated: 2026-09-20
agent_button_carrier: tauri agent_button_carrier: tauri
@ -444,6 +444,13 @@ modules:
note: "T1-T6 全部完成并端到端终验(2026-07-06):单一 lian-jing 服务器上线,~/.claude.json 12 条旧注册清零,3 项目抽查新格式通过,旧路由 404。T4 暴露并修复 git-bash 硬编码存量 bug(0d1427f)。遗留:同路径重复登记待清理(见模块 done_note)" note: "T1-T6 全部完成并端到端终验(2026-07-06):单一 lian-jing 服务器上线,~/.claude.json 12 条旧注册清零,3 项目抽查新格式通过,旧路由 404。T4 暴露并修复 git-bash 硬编码存量 bug(0d1427f)。遗留:同路径重复登记待清理(见模块 done_note)"
position: [1750, 500] position: [1750, 500]
- id: codex-model-switch
name: Codex 模型切换
area: frontend
status: planned
progress: 0
position: [750, 800]
edges: edges:
# 前端依赖 # 前端依赖
- from: workspace-mgmt - from: workspace-mgmt
@ -720,3 +727,9 @@ edges:
- from: onboarding-pack - from: onboarding-pack
to: governance-feedback-loop to: governance-feedback-loop
type: dependency type: dependency
- from: codex-model-switch
to: devtools-scan
type: related
- from: codex-model-switch
to: ui-restructure
type: related

View File

@ -0,0 +1,133 @@
---
id: codex-model-switch
name: Codex 模型切换
status: planned
progress: 0
---
## 概述
在 DeepSeek 与官方模型之间安全切换 Codex 的模型提供方,并管理 DeepSeek API Key。
炼境侧**只读**:展示当前档位、Key 提示、冲突告警,所有写入动作转发给独立二进制 `codex-switch` 执行。
## 决策记录
- **写入职责不放炼境**:`claude-autonomy-config` 已立下规则「炼境只读,不写第三方工具配置,避免意外覆盖用户的 settings.json」。模型切换必须写 `~/.codex/config.toml`,若放进炼境核心就与该规则直接冲突。故写入交给独立 CLI,炼境只做展示与转发。
- **不引入插件系统**:炼境「工具」页(`ToolsPage.tsx`)本就是「低频独立页合并为 tabs」的容器,`v2rayn-import`、`devtools-scan` 已确立「感知宿主开发工具状态」这一模块族。新需求是该族成员,加一个 tab 即可,无需先造扩展机制。
- **Key 归 CLI 管理,落炼境 SQLite**:写配置的是 CLI,密钥就该在写入方一侧,否则 CLI 独立使用时没有密钥来源。炼境因此不新增数据表,也不接触 DeepSeek 明文密钥。
- **只物化一处**:Key 只写 `[model_providers.deepseek].experimental_bearer_token`,并停止向 `[shell_environment_policy.set]` 写 `DEEPSEEK_API_KEY`。现状两处值不一致(`sk-599a66…` vs `sk-c12beb…`)正源于此。
---
## 设计决策:Codex 模型切换工具
### 背景
- 关联现有模块:`devtools-scan`(宿主开发工具扫描)、`wsl-proxy`(第三方工具配置同步)、`claude-autonomy-config`(外部 AI CLI 配置只读展示先例)、`api-keys`(密钥保管,职责不同不复用存储)
- 现状事实:`~/.codex/config.toml` 已被 DeepSeek 官方脚本改写(`model = "deepseek-flash"`、`model_provider = "deepseek"`);`[model_providers.code-switch]` 由 cc-studio 托管且处于 `disabled_alias`;DeepSeek Key 同时存在于 provider 段与 `shell_environment_policy.set` 两处且值不同
- 用户痛点:官方脚本是一次性安装器(只有「装 flash / 装 v4-pro / 恢复到安装前」),无状态查询、无 Key 体检、无失败回滚
### 方案选择
| 方案 | 说明 | 结论 |
| --- | --- | --- |
| A 纯炼境内置模块 | Rust 直接写 `config.toml` | 否决:违反 `claude-autonomy-config` 既有规则,且炼境承担第三方配置写坏的责任 |
| B 独立 CLI + 炼境只读面板 | CLI 拥有写入与密钥;炼境只读探测 + 转发 | **选定** |
| C 完整插件系统 | 先定义 manifest / 发现 / 生命周期 | 否决:当前仅一个候选插件,属过度设计 |
### 受影响文件
新增:
- `src-tauri/src/commands/codex_switch.rs` — 只读探测 Codex 配置状态 + 定位并调用 `codex-switch` CLI
- `src/components/tools/CodexSwitchPanel.tsx` — 工具页面板
修改:
- `src-tauri/src/commands/mod.rs` — 声明 `pub mod codex_switch`
- `src-tauri/src/lib.rs` — 注册 command(静态 invoke_handler)
- `src/lib/commands.ts` — 前端封装(AGENTS.md 硬约定:新增 command 必须同步)
- `src/components/tools/ToolsPage.tsx` — 新增 `codex` tab
同仓库内(独立子项目,自带 Cargo workspace,不纳入 src-tauri 构建):
- `codex-switch/crates/core` — 纯逻辑:detect / config_edit / catalog / secret / verify / backup
- `codex-switch/crates/cli` — 命令入口:status / preview / switch / undo / set-key / doctor
### 系统边界变更
- **不新增数据表**(Key 归 CLI,炼境不落密钥)
- 新增 Tauri command:
- `get_codex_switch_status() -> CodexSwitchStatus` — 只读;含当前档位、provider、Key hint(脱敏)、CLI 是否已安装、cc-studio 冲突、残留旧条目
- `run_codex_switch_action(action, target?) -> ActionResult` — 转发给 CLI,回传 stdout/stderr 与退出码
- CLI 契约(炼境与 CLI 之间的稳定接口):
- `codex-switch status --json`
- `codex-switch preview --to <target> --json`
- `codex-switch switch --to <target>`
- `codex-switch undo`
- `codex-switch set-key --stdin`
- `codex-switch doctor --json`
### 风险点
- cc-studio 同时托管 `model_provider` / `preferred_auth_method` / `model_providers.code-switch`:写入会互相覆盖 → CLI preflight 检测托管文件,拒绝静默覆盖并告警
- CLI 未安装:面板降级为「未安装 + 获取引导」,不报错
- 配置被 CLI 改写后面板缓存陈旧 → 每次进入面板重新探测,不做前端缓存
- 二进制分发:CLI 需随炼境打包(Tauri xternalBin)或落 PATH。P0 阶段先按「PATH 查找 + 可配置路径」实现,打包留到发布前处理
- `toml_edit` 对含大量手写内容的 `config.toml` 格式保真:非拥有字段在写入前后做哈希断言,并补 fixture 回归测试
- 切换 provider 后历史会话按登录方式分组隐藏(官方文档已说明):必须由面板显式提示,否则新手误判为数据丢失
### 拆解建议
建议拆为 5 张任务卡,依赖顺序:
1. **core 骨架**(M)— `codex-switch/crates/core`:detect + config_edit(toml_edit)+ backup
2. **CLI 切换**(M)— 依赖 1:status / preview / switch / undo
3. **CLI 密钥与体检**(M)— 依赖 2:set-key(凭据管理器)+ doctor 连通性验证
4. **炼境只读探测与转发**(M)— 依赖 2 的 CLI 契约:`codex_switch.rs` + `commands.ts`
5. **炼境工具页面板**(M)— 依赖 4:`ToolsPage` 新增 tab + 面板 UI + 新手护栏文案
---
## 任务卡
> `codex-switch/` 指炼境仓库内的独立子目录,自带 Cargo workspace,不并入 `src-tauri` 的构建(对炼境现有编译零影响)。
### 📋 core:配置探测与精细改写骨架
- status: todo
- complexity: M
- files: codex-switch/crates/core/Cargo.toml, codex-switch/crates/core/src/lib.rs, codex-switch/crates/core/src/detect.rs, codex-switch/crates/core/src/config_edit.rs, codex-switch/crates/core/src/backup.rs, codex-switch/crates/core/tests/fixtures/config.toml
- acceptance: 对 fixture 执行档位切换后,非拥有字段(mcp_servers / plugins / projects / notify / shell_environment_policy)逐字节不变;config.toml 解析失败时中止且零文件修改;备份可原样还原
核心 crate 不得依赖 Tauri。`config_edit` 用 `toml_edit` 保留注释与字段顺序,写入走临时文件 + 同目录原子 rename。
### 📋 CLI:status / preview / switch / undo
- status: todo
- complexity: M
- files: codex-switch/crates/cli/Cargo.toml, codex-switch/crates/cli/src/main.rs, codex-switch/crates/cli/src/commands/status.rs, codex-switch/crates/cli/src/commands/preview.rs, codex-switch/crates/cli/src/commands/switch.rs, codex-switch/crates/cli/src/commands/undo.rs
- depends: core:配置探测与精细改写骨架
- acceptance: `status --json` 报出当前档位与 provider;`switch --to official` 与 `switch --to deepseek-flash` 可往返切换;`undo` 回到上一状态;`preview --to X --json` 输出字段级 diff 且不含明文密钥
### 📋 CLI:密钥托管与连通性体检
- status: todo
- complexity: M
- files: codex-switch/crates/core/src/secret.rs, codex-switch/crates/cli/src/commands/set_key.rs, codex-switch/crates/cli/src/commands/doctor.rs
- depends: CLI:status / preview / switch / undo
- acceptance: `set-key --stdin` 后密钥只物化到 `[model_providers.deepseek].experimental_bearer_token` 一处,且不再写入环境变量段;任何输出与日志中只出现 `sk-` + 前 6 + … + 后 4;`doctor --json` 能区分 Key 无效 / 限流 / 网络不通 / 模型名不存在
密钥存系统凭据管理器;`set-key` 接受 stdout 管道输入,不接受命令行明文参数。
### 🔵 炼境:只读探测与 CLI 转发
- status: in_progress
- complexity: M
- files: src-tauri/src/commands/codex_switch.rs, src-tauri/src/commands/mod.rs, src-tauri/src/lib.rs, src/lib/commands.ts
- depends: CLI:status / preview / switch / undo
- acceptance: `get_codex_switch_status` 返回当前档位 / provider / 脱敏 Key hint / CLI 是否已安装 / cc-studio 托管冲突 / 旧条目残留;`run_codex_switch_action` 转发 CLI 并回传 stdout、stderr 与退出码;全流程炼境不写入任何 Codex 配置文件
CLI 调用统一走 `crate::silent_cmd`(Windows 隐藏窗口)。新增 command 必须同步 `src/lib/commands.ts`。
### 📋 炼境:工具页面板与新手护栏
- status: todo
- complexity: M
- files: src/components/tools/CodexSwitchPanel.tsx, src/components/tools/ToolsPage.tsx
- depends: 炼境:只读探测与 CLI 转发
- acceptance: 「工具」页新增 Codex 模型切换 tab;CLI 未安装时显示获取引导而非报错;切换成功后提示「历史会话按登录方式分组、未删除」与「桌面端需重启」;检测到 cc-studio 托管时给出告警
面板所有写入动作经确认弹窗后转发 CLI;每次进入面板重新探测,不做前端缓存。