dev-manager-tauri/.blueprint/modules/codex-model-switch.md
2026-09-21 12:53:25 +09:00

8.8 KiB
Raw Blame History

id name status progress
codex-model-switch Codex 模型切换 planned 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;每次进入面板重新探测,不做前端缓存。