docs(cleanup): remove 'current' as special provider; align UI/messages and migration naming to 'default' and one-time import rule

- App: update auto-import message to '默认供应商'
- README: clarify default import only when providers are empty
- Plan doc: replace 'current entry' wording with current pointer (manager.current)
- Migration: name live-imported item 'default' instead of 'current'
This commit is contained in:
Jason
2025-09-05 15:16:03 +08:00
parent a1dfdf4e68
commit ab6be1d510
4 changed files with 13 additions and 13 deletions

View File

@@ -69,7 +69,7 @@
- 供应商副本:`auth-<name>.json``config-<name>.toml` - 供应商副本:`auth-<name>.json``config-<name>.toml`
- API Key 字段:`auth.json` 中使用 `OPENAI_API_KEY` - API Key 字段:`auth.json` 中使用 `OPENAI_API_KEY`
- 切换策略:将选中供应商的副本覆盖到主配置(`auth.json``config.toml`)。若供应商没有 `config-*.toml`,会创建空的 `config.toml` - 切换策略:将选中供应商的副本覆盖到主配置(`auth.json``config.toml`)。若供应商没有 `config-*.toml`,会创建空的 `config.toml`
- 导入默认:`~/.codex/auth.json` 存在,会将当前主配置导入为 `current` 供应商`config.toml` 不存在时按空处理。 - 导入默认:仅当该应用无任何供应商时,从现有主配置创建一条默认项并设为当前`config.toml` 不存在时按空处理。
- 官方登录可切换到预设“Codex 官方登录”,重启终端后可选择使用 ChatGPT 账号完成登录。 - 官方登录可切换到预设“Codex 官方登录”,重启终端后可选择使用 ChatGPT 账号完成登录。
### Claude Code 说明 ### Claude Code 说明
@@ -79,7 +79,7 @@
- 供应商副本:`settings-<name>.json` - 供应商副本:`settings-<name>.json`
- API Key 字段:`env.ANTHROPIC_AUTH_TOKEN` - API Key 字段:`env.ANTHROPIC_AUTH_TOKEN`
- 切换策略:将选中供应商的副本覆盖到主配置(`settings.json`/`claude.json`)。如当前有配置且存在“当前供应商”,会先将主配置备份回该供应商的副本文件。 - 切换策略:将选中供应商的副本覆盖到主配置(`settings.json`/`claude.json`)。如当前有配置且存在“当前供应商”,会先将主配置备份回该供应商的副本文件。
- 导入默认:`~/.claude/settings.json``~/.claude/claude.json` 存在,会将当前主配置导入为 `current` 供应商副本 - 导入默认:仅当该应用无任何供应商时,从现有主配置创建一条默认项并设为当前
- 官方登录可切换到预设“Claude 官方登录”,重启终端后可使用 `/login` 完成登录。 - 官方登录可切换到预设“Claude 官方登录”,重启终端后可使用 `/login` 完成登录。
### 迁移与备份 ### 迁移与备份

View File

@@ -14,13 +14,13 @@
- 当前: - 当前:
- 全局配置:`~/.cc-switch/config.json`v2`MultiAppConfig`,含多个 `ProviderManager`)。 - 全局配置:`~/.cc-switch/config.json`v2`MultiAppConfig`,含多个 `ProviderManager`)。
- 切换依赖“供应商副本文件”Claude`~/.claude/settings-<name>.json`Codex`~/.codex/auth-<name>.json``config-<name>.toml`)→ 恢复到主配置。 - 切换依赖“供应商副本文件”Claude`~/.claude/settings-<name>.json`Codex`~/.codex/auth-<name>.json``config-<name>.toml`)→ 恢复到主配置。
- 启动:若检测到现有主配置自动导入为 `current` 供应商 - 启动:若对应 App 的供应商列表为空,可从现有主配置自动创建一条“默认项”并设为当前
- 问题:存在“副本 ↔ 总配置”双来源,可能不一致;明文落盘有泄露风险。 - 问题:存在“副本 ↔ 总配置”双来源,可能不一致;明文落盘有泄露风险。
## 3. 总体方案 ## 3. 总体方案
- 以加密文件 `~/.cc-switch/config.enc.json` 替代明文存储;进程启动时解密一次加载到内存,后续以内存为准;保存时加密写盘。 - 以加密文件 `~/.cc-switch/config.enc.json` 替代明文存储;进程启动时解密一次加载到内存,后续以内存为准;保存时加密写盘。
- 切换时:直接从内存 `Provider.settings_config` 写入目标应用主配置;切换前回填当前 live 配置到 `current` 供应商,保留外部修改。 - 切换时:直接从内存 `Provider.settings_config` 写入目标应用主配置;切换前回填当前 live 配置到当前选中供应商(由 `manager.current` 指向),保留外部修改。
- 明文兼容:若无加密文件,读取旧 `config.json`(含 v1→v2 迁移),首次保存写加密文件,并备份旧明文。 - 明文兼容:若无加密文件,读取旧 `config.json`(含 v1→v2 迁移),首次保存写加密文件,并备份旧明文。
- 旧文件清理:提供“可回滚归档”而非删除。扫描 `~/.cc-switch/config.json`v1/v2与 Claude/Codex 的历史副本文件,用户确认后移动到 `~/.cc-switch/archive/<ts>/`,生成 `manifest.json` 以便恢复;默认不做静默清理。 - 旧文件清理:提供“可回滚归档”而非删除。扫描 `~/.cc-switch/config.json`v1/v2与 Claude/Codex 的历史副本文件,用户确认后移动到 `~/.cc-switch/archive/<ts>/`,生成 `manifest.json` 以便恢复;默认不做静默清理。
@@ -66,10 +66,10 @@
- `MultiAppConfig::save()`:统一调用 `write_encrypted_config()`;若检测到旧 `config.json`,首次保存时备份为 `config.v1.backup.<ts>.json`(或保留为只读,视实现选择)。 - `MultiAppConfig::save()`:统一调用 `write_encrypted_config()`;若检测到旧 `config.json`,首次保存时备份为 `config.v1.backup.<ts>.json`(或保留为只读,视实现选择)。
- 调整 `src-tauri/src/commands.rs::switch_provider` - 调整 `src-tauri/src/commands.rs::switch_provider`
- Claude - Claude
1. 回填:若 `~/.claude/settings.json` 存在且 `current` 非空 → 读取 JSON写回 `manager.providers[current].settings_config`。 1. 回填:若 `~/.claude/settings.json` 存在且存在当前指针 → 读取 JSON写回 `manager.providers[manager.current].settings_config`。
2. 切换:从目标 `provider.settings_config` 直接写 `~/.claude/settings.json`(确保父目录存在)。 2. 切换:从目标 `provider.settings_config` 直接写 `~/.claude/settings.json`(确保父目录存在)。
- Codex - Codex
1. 回填:读取 `~/.codex/auth.json`JSON与 `~/.codex/config.toml`(字符串;非空做 TOML 校验)→ 合成为 `{auth, config}` → 写回 `manager.providers[current].settings_config`。 1. 回填:读取 `~/.codex/auth.json`JSON与 `~/.codex/config.toml`(字符串;非空做 TOML 校验)→ 合成为 `{auth, config}` → 写回 `manager.providers[manager.current].settings_config`。
2. 切换:从目标 `provider.settings_config` 中取 `auth`(必需)与 `config`(可空)写入对应主配置(非空 `config` 校验 TOML 2. 切换:从目标 `provider.settings_config` 中取 `auth`(必需)与 `config`(可空)写入对应主配置(非空 `config` 校验 TOML
- 更新 `manager.current = id``state.save()` → 触发加密保存。 - 更新 `manager.current = id``state.save()` → 触发加密保存。
- 保留/清理: - 保留/清理:
@@ -80,7 +80,7 @@
- 启动:`AppState::new()` → `MultiAppConfig::load()`(优先加密)→ 进程内持有解密后的配置。 - 启动:`AppState::new()` → `MultiAppConfig::load()`(优先加密)→ 进程内持有解密后的配置。
- 添加/编辑/删除:更新内存中的 `ProviderManager` → `state.save()`(加密写盘)。 - 添加/编辑/删除:更新内存中的 `ProviderManager` → `state.save()`(加密写盘)。
- 切换:回填 live → 以目标供应商内存配置写入主配置 → 更新 `current` → `state.save()`。 - 切换:回填 live → 以目标供应商内存配置写入主配置 → 更新当前指针(`manager.current`→ `state.save()`。
- 迁移后提醒:若首次从旧明文迁移成功,弹出“发现旧配置,可归档”提示;用户可进入“存储与清理”页面查看并执行归档。 - 迁移后提醒:若首次从旧明文迁移成功,弹出“发现旧配置,可归档”提示;用户可进入“存储与清理”页面查看并执行归档。
## 8. 迁移策略 ## 8. 迁移策略

View File

@@ -164,7 +164,7 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
let settings_path = crate::config::get_claude_settings_path(); let settings_path = crate::config::get_claude_settings_path();
if settings_path.exists() { if settings_path.exists() {
match crate::config::read_json_file::<Value>(&settings_path) { match crate::config::read_json_file::<Value>(&settings_path) {
Ok(val) => Some(("current".to_string(), val)), Ok(val) => Some(("default".to_string(), val)),
Err(e) => { Err(e) => {
log::warn!("读取 Claude live 配置失败: {}", e); log::warn!("读取 Claude live 配置失败: {}", e);
None None
@@ -269,7 +269,7 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
} else { } else {
String::new() String::new()
}; };
Some(("current".to_string(), serde_json::json!({"auth": auth, "config": cfg}))) Some(("default".to_string(), serde_json::json!({"auth": auth, "config": cfg})))
} }
Err(e) => { Err(e) => {
log::warn!("读取 Codex live auth.json 失败: {}", e); log::warn!("读取 Codex live auth.json 失败: {}", e);
@@ -350,7 +350,7 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result<bool, S
} }
} }
// 若 current 为空,将 live 导入项设为 current // 若当前为空,将 live 导入项设为当前
{ {
let manager = config.get_manager_mut(&AppType::Claude).unwrap(); let manager = config.get_manager_mut(&AppType::Claude).unwrap();
if manager.current.is_empty() { if manager.current.is_empty() {

View File

@@ -80,7 +80,7 @@ function App() {
setProviders(loadedProviders); setProviders(loadedProviders);
setCurrentProviderId(currentId); setCurrentProviderId(currentId);
// 如果供应商列表为空,尝试自动导入现有配置为"current"供应商 // 如果供应商列表为空,尝试自动从 live 导入一条默认供应商
if (Object.keys(loadedProviders).length === 0) { if (Object.keys(loadedProviders).length === 0) {
await handleAutoImportDefault(); await handleAutoImportDefault();
} }
@@ -154,14 +154,14 @@ function App() {
} }
}; };
// 自动导入现有配置为"current"供应商 // 自动从 live 导入一条默认供应商(仅首次初始化时)
const handleAutoImportDefault = async () => { const handleAutoImportDefault = async () => {
try { try {
const result = await window.api.importCurrentConfigAsDefault(activeApp); const result = await window.api.importCurrentConfigAsDefault(activeApp);
if (result.success) { if (result.success) {
await loadProviders(); await loadProviders();
showNotification("已自动导入现有配置为 current 供应商", "success", 3000); showNotification("已从现有配置创建默认供应商", "success", 3000);
} }
// 如果导入失败(比如没有现有配置),静默处理,不显示错误 // 如果导入失败(比如没有现有配置),静默处理,不显示错误
} catch (error) { } catch (error) {