23 KiB
KimiSwitch 设计文档
1. 项目概述
KimiSwitch 是一个 Windows 桌面配置管理工具,用于简化 Kimi Code CLI 的多供应商配置维护。它通过图形界面让用户增删改查 [providers] 与 [models],支持多套配置**配置文件(Profile)**的保存与一键切换,最终生成符合 Kimi Code CLI 规范的 ~/.kimi-code/config.toml。
2. 问题与目标
2.1 现状痛点
- Kimi Code CLI 的配置文件是 TOML 格式,需要手动编辑。
- 维护多个供应商(Kimi、OpenAI、Anthropic、Gemini、Vertex 等)时,
base_url与api_key容易写错或重复。 - 对 TOML 语法不熟的用户(例如
[models."gpt-4.1"]需要引号)上手成本高。 - 工作、个人、测试等场景需要多套 API 配置时,只能手动备份/替换
config.toml,切换麻烦且容易出错。
2.2 项目目标
- 优先做好 Windows 版本:安装包、路径处理、UI 风格、权限行为均以 Windows 为首要目标平台。
- 提供图形化界面,零 TOML 语法负担地维护供应商与模型别名。
- 支持一键切换当前使用的供应商及其对应模型:选择供应商后自动筛选关联模型,确认后立即将
default_model写入config.toml。 - 支持多配置文件(Profile)管理:保存多份独立配置,一键切换激活某一份到
~/.kimi-code/config.toml。 - 支持 Kimi Code CLI 官方支持的全部供应商类型。
- 自动处理凭证存储方式(直接字段 vs
[providers.<name>.env]子表)。 - 以 Windows MSI 安装包形式分发给 Windows 用户。
3. 功能范围
3.1 In Scope
- 供应商(providers)管理:增删改查。
- 模型别名(models)管理:增删改查。
- 供应商与模型快速切换:选择供应商后只显示使用该供应商的模型,一键设为
default_model。 - Profile(配置快照)管理:新建、重命名、复制、删除、切换 Profile;Profile 保存在
~/.kimi-code/profiles/下,切换时写入~/.kimi-code/config.toml。 - 读取现有
~/.kimi-code/config.toml。 - 保存时自动备份原配置并写入新配置。
- 基础校验:必填项、重复别名、缺失的供应商引用、缺失凭证。
- 生成 Windows MSI 安装包。
3.2 Out of Scope
- 不实现加密存储:API Key 按 Kimi Code CLI 官方方式保存在
config.toml中。 - 不修改
tui.toml、mcp.json、OAuth 凭证等其他文件。 - 不实现远程同步或云存储。
- 不实现网络连通性测试或 API 调用验证。
- 第一版不支持 macOS / Linux 安装包,仅输出 Windows x64 MSI;其他平台待 Windows x64 版本完成后再规划。
4. 技术方案
4.1 推荐方案
Tauri(Rust + TypeScript)
- Tauri v2:Rust 编写的跨平台桌面框架,内置 Windows MSI 打包,产物体积小、启动快。本设计基于 Tauri v2 的权限与命令体系。
- Rust:负责文件 I/O、配置解析、Profile 管理、校验逻辑;类型安全、性能高。
- TypeScript + React + Tailwind CSS:负责前端界面;Tailwind 提供基础样式,无需引入重型 UI 组件库。
- toml_edit:Rust 生态中支持 round-trip 的 TOML 编辑库,保留注释与格式。
- IndexMap:替代
HashMap保留 Provider / Model 的插入顺序。 - Tauri Bundler:一键生成
.msi安装包,无需额外 WiX 手动编写。
4.2 备选方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| Tauri + Rust + Vanilla TS | 依赖更少,构建更快 | 大型表单状态管理需手写 |
| Tauri + Rust + Vue/Svelte | 体积与 Vanilla 相近 | 团队需熟悉对应框架 |
| egui / Iced(纯 Rust) | 无前端构建链 | UI 灵活度与组件生态不如 Web |
选择 Tauri + Rust + React 的原因是:Tauri 内置 MSI 打包且产物体积小;Rust 适合处理文件与配置逻辑;React 表单/表格生态成熟,能快速实现三栏配置界面。
5. 目录结构
D:/AIGC/KimiSwitch
├── src-tauri/
│ ├── Cargo.toml
│ ├── tauri.conf.json # Tauri 应用配置与打包选项
│ └── src/
│ ├── main.rs # 入口
│ ├── lib.rs # 模块导出
│ ├── models.rs # Provider / Model / Config / Profile 结构体
│ ├── config_io.rs # 读写 config.toml 与 Profile 文件
│ ├── profile_manager.rs # Profile 生命周期管理
│ ├── validators.rs # 配置校验
│ └── commands.rs # Tauri 暴露给前端的命令
├── src/ # 前端源码
│ ├── App.tsx
│ ├── components/
│ │ ├── TopBar.tsx # Profile / 供应商 / 模型 / 设为默认
│ │ ├── ProviderList.tsx
│ │ ├── ProviderForm.tsx
│ │ ├── ModelList.tsx
│ │ ├── ModelForm.tsx
│ │ └── BottomToolbar.tsx
│ ├── hooks/
│ │ └── useConfig.ts # 配置状态与 Tauri 命令封装
│ └── types/
│ └── index.ts # 前端类型定义
├── tests/
│ ├── config_io.rs
│ ├── profile_manager.rs
│ └── validators.rs
├── package.json
├── tsconfig.json
├── vite.config.ts
├── Cargo.lock
├── README.md
└── .gitignore
6. 数据模型
6.1 ProviderType 枚举
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProviderType {
Kimi,
Anthropic,
Openai,
#[serde(rename = "openai_responses")]
OpenaiResponses,
#[serde(rename = "google-genai")]
GoogleGenai,
Vertexai,
}
注意:
openai_responses与google-genai包含连字符,无法直接用rename_all = "snake_case",需单独#[serde(rename)]。
6.2 Provider
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Provider {
pub name: String, // 供应商唯一标识
pub provider_type: ProviderType, // 枚举
pub base_url: Option<String>, // 直接字段
pub api_key: Option<String>, // 直接字段
pub env: IndexMap<String, String>, // [providers.<name>.env] 子表,保留顺序
pub raw_other: Table, // 保留本工具不直接编辑的其他字段(如 custom_headers)
}
6.3 Model
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Model {
pub alias: String, // 模型别名
pub provider: String, // 引用的供应商 name
pub model: String, // 实际模型 ID
pub max_context_size: u64,
pub display_name: Option<String>,
pub raw_other: Table, // 保留 capabilities 等本工具不直接编辑的字段
}
6.4 Config
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub default_model: Option<String>,
pub providers: IndexMap<String, Provider>, // 保留顺序
pub models: IndexMap<String, Model>, // 保留顺序
pub raw_other: Table, // 保留 config.toml 中与本工具无关的其他字段
}
6.5 Profile
#[derive(Debug, Clone)]
pub struct Profile {
pub name: String, // Profile 显示名称,可包含中文/空格
pub filename: String, // 安全化的存储文件名,如 "my-profile.toml"
pub config: Config, // 该 Profile 对应的完整配置内容
pub is_active: bool, // 是否当前已激活
}
Profile 文件统一存放在 ~/.kimi-code/profiles/ 目录下。default Profile 特殊处理:它直接对应 ~/.kimi-code/config.toml,不额外在 profiles/ 下生成文件。
文件名安全化:Profile 显示名可包含中文、空格、大小写;存储时自动转译为合法 Windows 文件名。转译规则:保留 ASCII 字母数字,其他字符替换为 -,连续 - 合并,小写化;若结果为空(如纯中文名),则回退为基于名称哈希的短文件名(如 profile-48291.toml)。
7. 配置读写行为
7.1 读取流程
- 定位
~/.kimi-code/config.toml(可通过KIMI_CODE_HOME环境变量覆盖;使用 Ruststd::path::PathBuf处理 Windows 路径)。 - 使用
toml_edit解析 TOML(保留注释、空行与字段顺序)。 - 提取
[providers.*]与[models.*]到 Rust 结构体。 - 保留文件中其他字段(如
[thinking]、[loop_control]等),保存时原样写回。
7.2 写入流程
- 若目标文件已存在,先复制一份带时间戳的备份:
config.toml.bak.YYYYMMDD_HHMMSS_<毫秒>(如 1 秒内多次保存,追加自增序号)。 - 使用
toml_edit加载并修改[providers]、[models]与default_model,保留原文件中的注释、空行与字段顺序。 - 合并保留的其他字段。
- 写回文件。
- 写操作使用临时文件 + 原子重命名(
write to tmp → fsync → rename),降低写损风险。
第一版即使用
toml_edit而非toml/serde_toml,避免用户的既有注释和格式在保存后被清空。
7.3 Tauri 权限配置
Tauri 默认禁止前端访问文件系统,需在 tauri.conf.json(或 capabilities/ 目录)中显式声明:
fs:allow-read-file:~/.kimi-code/config.toml、Profile 文件。fs:allow-write-file:~/.kimi-code/config.toml、Profile 文件。fs:allow-read-dir:~/.kimi-code/、~/.kimi-code/profiles/。fs:allow-create-dir:首次运行时创建~/.kimi-code/profiles/。core:default:基础窗口与事件权限。
所有路径使用 Tauri 的 BaseDirectory::Home 解析,避免硬编码绝对路径。
7.4 Tauri 命令清单
Rust 后端通过 commands.rs 暴露以下命令给前端调用:
| 命令 | 输入 | 输出 | 说明 |
|---|---|---|---|
load_config |
无 | Config |
读取 ~/.kimi-code/config.toml,若不存在则初始化空配置。 |
save_config |
Config |
Result<(), String> |
校验并写入 config.toml,失败返回错误信息。 |
list_profiles |
无 | Vec<ProfileSummary> |
列出 profiles/ 下所有 Profile 及其激活状态。 |
load_profile |
filename: String |
Config |
读取指定 Profile 文件内容。 |
save_profile |
filename: String, config: Config |
Result<(), String> |
保存或覆盖指定 Profile 文件。 |
switch_profile |
filename: String |
Result<Config, String> |
将指定 Profile 内容写入 config.toml(带备份)并返回新配置。 |
rename_profile |
old_filename, new_name |
Result<String, String> |
重命名 Profile,返回新的安全文件名。 |
delete_profile |
filename: String |
Result<(), String> |
删除指定 Profile 文件。 |
open_config_dir |
无 | Result<(), String> |
调用系统文件管理器打开配置目录。 |
get_app_version |
无 | String |
返回当前应用版本号。 |
7.5 凭证存储策略
根据 Kimi Code CLI 配置覆盖文档,凭证解析优先级为:
[providers.<name>].api_key/.base_url[providers.<name>.env]中对应键- 两者都缺失 → CLI 启动报错
KimiSwitch 默认将凭证写入 [providers.<name>.env] 子表,使用官方约定键名,同时提供「使用直接字段」开关,允许用户将 api_key / base_url 写到 [providers.<name>] 顶层。UI 中两种方式互斥:
- env 子表模式:
api_key/base_url输入框的值映射到env中的约定键名;保存时顶层api_key/base_url字段被移除。 - 直接字段模式:
api_key/base_url写入顶层字段;保存时env中的对应约定键名被移除,其他自定义env键保留。
避免直接字段与 env 同时存在导致优先级迷惑。
各供应商的默认 Base URL 与 env 键名如下(参考 Kimi Code CLI 平台与模型):
| 供应商类型 | 默认 Base URL | API Key 键名 | Base URL 键名 | 特殊说明 |
|---|---|---|---|---|
kimi |
https://api.moonshot.ai/v1 |
KIMI_API_KEY |
KIMI_BASE_URL |
支持视频上传 |
anthropic |
Anthropic SDK 默认 | ANTHROPIC_API_KEY |
ANTHROPIC_BASE_URL |
Claude 模型自动识别 thinking / 视觉 / 工具调用 |
openai |
https://api.openai.com/v1 |
OPENAI_API_KEY |
OPENAI_BASE_URL |
兼容 DeepSeek、Qwen 等第三方网关 |
openai_responses |
https://api.openai.com/v1 |
OPENAI_API_KEY |
OPENAI_BASE_URL |
使用 OpenAI Responses API |
google-genai |
https://generativelanguage.googleapis.com |
GOOGLE_API_KEY |
GOOGLE_GEMINI_BASE_URL |
只填主机根地址,不要带 /v1beta |
vertexai |
区域化 *-aiplatform.googleapis.com |
通常不需要(走 ADC) | GOOGLE_VERTEX_BASE_URL |
必须提供 GOOGLE_CLOUD_PROJECT 与 GOOGLE_CLOUD_LOCATION;只填主机根地址 |
创建供应商时,UI 根据所选 type 自动填入默认 Base URL 与推荐 env 键名,用户可手动覆盖。
7.6 Profile 生命周期
Profile 是 ~/.kimi-code/config.toml 的命名快照,存放在 ~/.kimi-code/profiles/ 目录下。
7.6.1 初始化
- 工具首次启动时,若
~/.kimi-code/config.toml已存在,自动生成一个名为default的 Profile,其内容即为当前config.toml。 - 若
config.toml不存在,创建一个空defaultProfile。 defaultProfile 始终直接对应~/.kimi-code/config.toml,不在profiles/下额外生成文件。
文件名安全化:Profile 显示名可包含中文、空格、大小写;存储时自动转译为合法 Windows 文件名。转译规则:保留 ASCII 字母数字,其他字符替换为 -,连续 - 合并,小写化;若结果为空(如纯中文名),则回退为基于名称哈希的短文件名(如 profile-48291.toml)。
7.6.2 操作
| 操作 | 行为 |
|---|---|
| 新建 Profile | 用户输入显示名,自动生成安全文件名;可选择「从当前配置复制」或「空白」;保存到 profiles/<safe-name>.toml。 |
| 重命名 Profile | 仅重命名非 default 的 Profile,修改显示名并重新生成安全文件名;原文件删除,新文件写入。 |
| 复制 Profile | 以 <原显示名> Copy 创建副本,自动生成新的安全文件名。 |
| 删除 Profile | 删除 profiles/<safe-name>.toml;default 不可删除。 |
| 切换 Profile | 将选中的 Profile 内容写入 ~/.kimi-code/config.toml(先备份当前 config.toml),然后重新加载界面。 |
| 保存当前到 Profile | 把当前编辑内容导出为新的 Profile 文件,不影响当前激活状态。 |
7.6.3 激活状态判定
- 工具启动时将
config.toml解析为Config,再与各 Profile 的Config做深度比较(而非字符串比较),语义一致即标记为is_active。 - 若没有任何 Profile 与当前
config.toml语义一致,则defaultProfile 标记为is_active,并在界面上提示「当前 config.toml 与所有 Profile 不完全匹配」。
7.6.4 未保存变更处理
- 切换 Profile 或退出前若存在未保存的编辑,弹出对话框:「保存当前修改 / 丢弃 / 取消」。
- 标题栏或状态栏显示「已修改」指示器。
8. 界面设计
前端使用 React + TypeScript + Tailwind CSS 实现,主窗口采用三栏布局,顶部增加「快速切换」工具条。前端通过 Tauri invoke 调用 Rust 命令完成所有文件操作。
- 窗口约束:最小宽度 1000px,最小高度 700px;标题栏显示
[未保存] Profile 名 - KimiSwitch。 - 快捷键:
Ctrl + S保存当前配置,Ctrl + R重新读取,Ctrl + O打开配置目录。第一版不单独做主题切换,跟随系统亮/暗模式。
8.1 顶部工具条
从左到右依次为:
- Profile 下拉框:列出所有 Profile(含
default),当前激活项高亮显示。 - Profile 操作按钮:「新建」「复制」「重命名」「删除」「切换」。
- 当前供应商 下拉框:列出当前 Profile 中已配置供应商。
- 当前模型 下拉框:根据所选供应商,只列出引用该供应商的模型别名;若该供应商下没有模型,则提示「未绑定模型,请先在右侧添加」。
- 设为默认 按钮:将当前选中的模型写入
default_model字段,并立即保存到当前 Profile 与config.toml。 - 启动时自动高亮当前
default_model对应的供应商和模型。
8.2 左栏:供应商列表
- 显示所有 provider name 与 type。
- 支持「添加」与「删除」按钮。
- 切换左栏选中项时,顶部「当前供应商」下拉框同步联动,右栏模型列表按该供应商过滤。
8.3 中栏:供应商详情表单
type下拉选择框;切换时自动填充默认base_url与推荐env键名。base_url输入框。api_key输入框(默认掩码,可切换明文)。- 凭证存储方式单选:直接字段 / env 子表;两种方式互斥,保存时只写入被选中的那一种。
env键值对表格(可增删改行)。
8.4 右栏:模型别名列表与详情
- 显示当前选中供应商下的 model alias(也可切换显示全部)。
- 表单包含
alias、provider下拉选择、model、max_context_size、display_name。 - 提供「设为默认模型」按钮,等同于顶部快速切换区的「设为默认」。
8.5 状态与错误处理
- 未保存指示:标题栏在配置有未保存变更时显示
*前缀;状态栏显示「已修改」或「已保存」。 - 切换/退出前检查:存在未保存变更时,弹出「保存 / 丢弃 / 取消」三选一对话框。
- 文件不存在:首次启动或配置目录不存在时,自动创建
~/.kimi-code/目录与空config.toml,不报错。 - 文件只读/权限不足:保存失败时给出明确提示,并建议检查目录权限或以管理员身份运行。
- 校验失败:保存前校验不通过时,弹窗列出所有错误项,禁止写入。
- 备份失败:若备份步骤失败(如磁盘满),禁止覆盖原文件。
- 多实例冲突:启动时检测
~/.kimi-code/.kimiswitch.lock;若已存在且进程仍在运行,提示用户「KimiSwitch 已在运行,请勿多开」;若进程已消失则清理旧锁。保存时不强制全局锁,但建议单次会话内顺序执行写操作。
8.6 底部工具栏
- 读取配置:从
~/.kimi-code/config.toml重新加载。 - 保存配置:校验并写入当前 Profile 与
config.toml,失败时弹出错误提示。 - 保存为新 Profile:把当前编辑内容导出为新的 Profile 文件。
- 打开配置目录:调用系统文件管理器定位到数据目录。
9. 校验规则
保存前必须校验通过,否则禁止写入:
- 每个 Provider 的
name非空、不重复,且为合法 TOML 表键(必要时生成带引号的键)。 - 每个 Provider 的
type必须是支持的类型之一。 - 每个 Provider 必须至少提供一种凭证来源,但
vertexai例外(走 Google Cloud ADC,无需静态 API Key):api_key直接字段非空,或env中包含对应供应商的 API Key 键名。
- 凭证存储方式在直接字段与
env子表之间互斥,保存时只写入选中的那一种。 vertexai类型的 Provider 必须在env中提供GOOGLE_CLOUD_PROJECT与GOOGLE_CLOUD_LOCATION,不强制要求 API Key。- 每个 Model 的
alias非空、不重复,且为合法 TOML 表键(含.等字符时自动加引号)。 - 每个 Model 的
provider必须引用一个已存在的 Provider。 - 每个 Model 的
model非空,max_context_size为正整数。
10. MSI 打包
10.1 打包流程
- 安装依赖:
npm install与cargo fetch - 开发/构建前端:
npm run build - 构建并打包:
npm run tauri build- Tauri 自动调用
cargo build --release与 Windows bundler - 输出:
src-tauri/target/release/bundle/msi/KimiSwitch_<version>_x64_en-US.msi
- Tauri 自动调用
10.2 MSI 行为
- 默认安装目录:
C:\Program Files\KimiSwitch( per-machine 安装,需管理员权限)。 - 创建开始菜单快捷方式:
KimiSwitch。 - 安装程序不自动写入
config.toml,仅部署工具本身。 - 支持标准卸载。
- 可在
tauri.conf.json中配置签名、版本、厂商信息、安装目录等。 - 第一版仅构建 x64 架构 MSI,不构建 arm64 或 32 位版本。
- 第一版不启用 Tauri Updater,后续版本通过重新安装 MSI 升级,降低第一版复杂度。
10.3 Windows 专项处理
- 路径解析:使用 Rust
dirs::home_dir()获取用户主目录,拼接.kimi-code,避免依赖$HOME环境变量(Windows 下可能不存在)。 - 配置文件位置:默认
C:\Users\<用户名>\.kimi-code\config.toml;若用户设置KIMI_CODE_HOME,按该变量解析。 - 权限:工具运行时只读写用户目录下的文件,无需以管理员身份运行;仅 MSI 安装本身需要管理员权限。
- 编码与换行:TOML 文件统一使用 UTF-8;Windows 下换行保持为
\r\n或\n均可,toml_edit会保留原文件换行风格。 - 代码签名(可选):如后续分发,建议为 MSI 配置代码签名证书,减少 Windows Defender SmartScreen 提示。
11. 测试策略
- 单元测试(Rust):
config_io.rs:解析与生成 TOML 的 round-trip 测试。profile_manager.rs:Profile 新建、复制、切换、激活状态判定测试。validators.rs:校验规则的边界测试。
- 手动测试:
- 启动 GUI,增删改供应商与模型。
- 创建多个 Profile 并相互切换,验证
config.toml内容正确变化。 - 保存后使用 Kimi Code CLI 读取并验证可用性。
- 在 Windows 上执行 MSI 安装与卸载。
12. 假设与约束
- 第一版目标平台仅为 Windows x64,macOS / Linux 跨平台支持不在第一版范围内,待 Windows x64 版本完成后再规划。
- API Key 按 Kimi Code CLI 官方方式明文存储在
config.toml中,不引入额外加密。 - 开发者已安装 Rust 工具链、Node.js 与 Tauri CLI(仅开发与打包时需要,最终用户不需要)。
- 不覆盖 Kimi Code CLI 的自动更新机制或 OAuth 登录流程。
- 最终用户运行 KimiSwitch 只需标准 Windows 用户权限,安装 MSI 时才需要管理员权限。