Compare commits
5 Commits
codex-adap
...
v3.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3665a79e50 | ||
|
|
4dce31aff7 | ||
|
|
451ca949ec | ||
|
|
a9ff8ce01c | ||
|
|
7848248df7 |
38
CHANGELOG.md
38
CHANGELOG.md
@@ -5,6 +5,44 @@ All notable changes to CC Switch will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [3.1.1] - 2025-09-03
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- Fixed the default codex config.toml to match the latest modifications
|
||||||
|
- Improved provider configuration UX with custom option
|
||||||
|
|
||||||
|
### 📝 Documentation
|
||||||
|
- Updated README with latest information
|
||||||
|
|
||||||
|
## [3.1.0] - 2025-09-01
|
||||||
|
|
||||||
|
### ✨ New Features
|
||||||
|
- **Added Codex application support** - Now supports both Claude Code and Codex configuration management
|
||||||
|
- Manage auth.json and config.toml for Codex
|
||||||
|
- Support for backup and restore operations
|
||||||
|
- Preset providers for Codex (Official, PackyCode)
|
||||||
|
- API Key auto-write to auth.json when using presets
|
||||||
|
- **New UI components**
|
||||||
|
- App switcher with segmented control design
|
||||||
|
- Dual editor form for Codex configuration
|
||||||
|
- Pills-style app switcher with consistent button widths
|
||||||
|
- **Enhanced configuration management**
|
||||||
|
- Multi-app config v2 structure (claude/codex)
|
||||||
|
- Automatic v1→v2 migration with backup
|
||||||
|
- OPENAI_API_KEY validation for non-official presets
|
||||||
|
- TOML syntax validation for config.toml
|
||||||
|
|
||||||
|
### 🔧 Technical Improvements
|
||||||
|
- Unified Tauri command API with app_type parameter
|
||||||
|
- Backward compatibility for app/appType parameters
|
||||||
|
- Added get_config_status/open_config_folder/open_external commands
|
||||||
|
- Improved error handling for empty config.toml
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- Fixed config path reporting and folder opening for Codex
|
||||||
|
- Corrected default import behavior when main config is missing
|
||||||
|
- Fixed non_snake_case warnings in commands.rs
|
||||||
|
|
||||||
## [3.0.0] - 2025-08-27
|
## [3.0.0] - 2025-08-27
|
||||||
|
|
||||||
### 🚀 Major Changes
|
### 🚀 Major Changes
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -1,4 +1,4 @@
|
|||||||
# Claude Code 供应商切换器
|
# Claude Code & Codex 供应商切换器
|
||||||
|
|
||||||
[](https://github.com/jasonyoung/cc-switch/releases)
|
[](https://github.com/jasonyoung/cc-switch/releases)
|
||||||
[](https://github.com/jasonyoung/cc-switch/releases)
|
[](https://github.com/jasonyoung/cc-switch/releases)
|
||||||
@@ -6,12 +6,15 @@
|
|||||||
|
|
||||||
一个用于管理和切换 Claude Code 与 Codex 不同供应商配置的桌面应用。
|
一个用于管理和切换 Claude Code 与 Codex 不同供应商配置的桌面应用。
|
||||||
|
|
||||||
> **v3.0.0 重大更新**:从 Electron 完全迁移到 Tauri 2.0,应用体积减少 85%(从 ~80MB 降至 ~12MB),启动速度提升 10 倍!
|
> v3.1.0 :新增 Codex 供应商管理与一键切换,支持导入当前 Codex 配置为默认供应商,并在内部配置从 v1 → v2 迁移前自动备份(详见下文““迁移与备份”)。
|
||||||
|
|
||||||
|
> v3.0.0 重大更新:从 Electron 完全迁移到 Tauri 2.0,应用体积减少 85%(从 ~80MB 降至 ~12MB),启动速度提升 10 倍!
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
- **极速启动** - 基于 Tauri 2.0,原生性能,秒开应用
|
- **极速启动** - 基于 Tauri 2.0,原生性能,秒开应用
|
||||||
- 一键切换不同供应商
|
- 一键切换不同供应商
|
||||||
|
- 同时支持 Claude Code 与 Codex 的供应商切换与导入
|
||||||
- Qwen coder、kimi k2、智谱 GLM、DeepSeek v3.1、packycode 等预设供应商只需要填写 key 即可一键配置
|
- Qwen coder、kimi k2、智谱 GLM、DeepSeek v3.1、packycode 等预设供应商只需要填写 key 即可一键配置
|
||||||
- 支持添加自定义供应商
|
- 支持添加自定义供应商
|
||||||
- 随时切换官方登录
|
- 随时切换官方登录
|
||||||
@@ -67,6 +70,22 @@
|
|||||||
- 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` 存在,会将当前主配置导入为 `default` 供应商;`config.toml` 不存在时按空处理。
|
- 导入默认:若 `~/.codex/auth.json` 存在,会将当前主配置导入为 `default` 供应商;`config.toml` 不存在时按空处理。
|
||||||
|
- 官方登录:可切换到预设“Codex 官方登录”,重启终端后可选择使用 ChatGPT 账号完成登录。
|
||||||
|
|
||||||
|
### Claude Code 说明
|
||||||
|
|
||||||
|
- 配置目录:`~/.claude/`
|
||||||
|
- 主配置文件:`settings.json`(推荐)或 `claude.json`(旧版兼容,若存在则继续使用)
|
||||||
|
- 供应商副本:`settings-<name>.json`
|
||||||
|
- API Key 字段:`env.ANTHROPIC_AUTH_TOKEN`
|
||||||
|
- 切换策略:将选中供应商的副本覆盖到主配置(`settings.json`/`claude.json`)。如当前有配置且存在“当前供应商”,会先将主配置备份回该供应商的副本文件。
|
||||||
|
- 导入默认:若 `~/.claude/settings.json` 或 `~/.claude/claude.json` 存在,会将当前主配置导入为 `default` 供应商副本。
|
||||||
|
- 官方登录:可切换到预设“Claude 官方登录”,重启终端后可使用 `/login` 完成登录。
|
||||||
|
|
||||||
|
### 迁移与备份
|
||||||
|
|
||||||
|
- cc-switch 自身配置从 v1 → v2 迁移时,将在 `~/.cc-switch/` 目录自动创建时间戳备份:`config.v1.backup.<timestamp>.json`。
|
||||||
|
- 实际生效的应用配置文件(如 `~/.claude/settings.json`、`~/.codex/auth.json`/`config.toml`)不会被修改,切换仅在用户点击“切换”时按副本覆盖到主配置。
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "cc-switch",
|
"name": "cc-switch",
|
||||||
"version": "3.0.0",
|
"version": "3.1.1",
|
||||||
"description": "Claude Code 供应商切换工具",
|
"description": "Claude Code & Codex 供应商切换工具",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "pnpm tauri dev",
|
"dev": "pnpm tauri dev",
|
||||||
"build": "pnpm tauri build",
|
"build": "pnpm tauri build",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 247 KiB |
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cc-switch"
|
name = "cc-switch"
|
||||||
version = "3.0.0"
|
version = "3.1.1"
|
||||||
description = "Claude Code MCP 服务器配置管理工具"
|
description = "Claude Code & Codex 供应商配置管理工具"
|
||||||
authors = ["Jason Young"]
|
authors = ["Jason Young"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/jasonyoung/cc-switch"
|
repository = "https://github.com/jasonyoung/cc-switch"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "CC Switch",
|
"productName": "CC Switch",
|
||||||
"version": "3.0.0",
|
"version": "3.1.1",
|
||||||
"identifier": "com.ccswitch.desktop",
|
"identifier": "com.ccswitch.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../dist",
|
"frontendDist": "../dist",
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
const [codexAuth, setCodexAuth] = useState("");
|
const [codexAuth, setCodexAuth] = useState("");
|
||||||
const [codexConfig, setCodexConfig] = useState("");
|
const [codexConfig, setCodexConfig] = useState("");
|
||||||
const [codexApiKey, setCodexApiKey] = useState("");
|
const [codexApiKey, setCodexApiKey] = useState("");
|
||||||
|
// -1 表示自定义,null 表示未选择,>= 0 表示预设索引
|
||||||
const [selectedCodexPreset, setSelectedCodexPreset] = useState<number | null>(
|
const [selectedCodexPreset, setSelectedCodexPreset] = useState<number | null>(
|
||||||
null,
|
showPresets && isCodex ? -1 : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 初始化 Codex 配置
|
// 初始化 Codex 配置
|
||||||
@@ -71,7 +72,10 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
}, [isCodex, initialData]);
|
}, [isCodex, initialData]);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [disableCoAuthored, setDisableCoAuthored] = useState(false);
|
const [disableCoAuthored, setDisableCoAuthored] = useState(false);
|
||||||
const [selectedPreset, setSelectedPreset] = useState<number | null>(null);
|
// -1 表示自定义,null 表示未选择,>= 0 表示预设索引
|
||||||
|
const [selectedPreset, setSelectedPreset] = useState<number | null>(
|
||||||
|
showPresets ? -1 : null
|
||||||
|
);
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
|
|
||||||
// 初始化时检查禁用签名状态
|
// 初始化时检查禁用签名状态
|
||||||
@@ -216,6 +220,18 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
setDisableCoAuthored(hasCoAuthoredDisabled);
|
setDisableCoAuthored(hasCoAuthoredDisabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理点击自定义按钮
|
||||||
|
const handleCustomClick = () => {
|
||||||
|
setSelectedPreset(-1);
|
||||||
|
setFormData({
|
||||||
|
name: "",
|
||||||
|
websiteUrl: "",
|
||||||
|
settingsConfig: "",
|
||||||
|
});
|
||||||
|
setApiKey("");
|
||||||
|
setDisableCoAuthored(false);
|
||||||
|
};
|
||||||
|
|
||||||
// Codex: 应用预设
|
// Codex: 应用预设
|
||||||
const applyCodexPreset = (
|
const applyCodexPreset = (
|
||||||
preset: (typeof codexProviderPresets)[0],
|
preset: (typeof codexProviderPresets)[0],
|
||||||
@@ -237,6 +253,19 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
setCodexApiKey("");
|
setCodexApiKey("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Codex: 处理点击自定义按钮
|
||||||
|
const handleCodexCustomClick = () => {
|
||||||
|
setSelectedCodexPreset(-1);
|
||||||
|
setFormData({
|
||||||
|
name: "",
|
||||||
|
websiteUrl: "",
|
||||||
|
settingsConfig: "",
|
||||||
|
});
|
||||||
|
setCodexAuth("");
|
||||||
|
setCodexConfig("");
|
||||||
|
setCodexApiKey("");
|
||||||
|
};
|
||||||
|
|
||||||
// 处理 API Key 输入并自动更新配置
|
// 处理 API Key 输入并自动更新配置
|
||||||
const handleApiKeyChange = (key: string) => {
|
const handleApiKeyChange = (key: string) => {
|
||||||
setApiKey(key);
|
setApiKey(key);
|
||||||
@@ -244,7 +273,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
const configString = setApiKeyInConfig(
|
const configString = setApiKeyInConfig(
|
||||||
formData.settingsConfig,
|
formData.settingsConfig,
|
||||||
key.trim(),
|
key.trim(),
|
||||||
{ createIfMissing: selectedPreset !== null },
|
{ createIfMissing: selectedPreset !== null && selectedPreset !== -1 },
|
||||||
);
|
);
|
||||||
|
|
||||||
// 更新表单配置
|
// 更新表单配置
|
||||||
@@ -271,12 +300,15 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 根据当前配置决定是否展示 API Key 输入框
|
// 根据当前配置决定是否展示 API Key 输入框
|
||||||
|
// 自定义模式(-1)不显示独立的 API Key 输入框
|
||||||
const showApiKey =
|
const showApiKey =
|
||||||
selectedPreset !== null || hasApiKeyField(formData.settingsConfig);
|
(selectedPreset !== null && selectedPreset !== -1) ||
|
||||||
|
(!showPresets && hasApiKeyField(formData.settingsConfig));
|
||||||
|
|
||||||
// 判断当前选中的预设是否是官方
|
// 判断当前选中的预设是否是官方
|
||||||
const isOfficialPreset =
|
const isOfficialPreset =
|
||||||
selectedPreset !== null &&
|
selectedPreset !== null &&
|
||||||
|
selectedPreset >= 0 &&
|
||||||
providerPresets[selectedPreset]?.isOfficial === true;
|
providerPresets[selectedPreset]?.isOfficial === true;
|
||||||
|
|
||||||
// Codex: 控制显示 API Key 与官方标记
|
// Codex: 控制显示 API Key 与官方标记
|
||||||
@@ -288,10 +320,13 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// 自定义模式(-1)不显示独立的 API Key 输入框
|
||||||
const showCodexApiKey =
|
const showCodexApiKey =
|
||||||
selectedCodexPreset !== null || getCodexAuthApiKey(codexAuth) !== "";
|
(selectedCodexPreset !== null && selectedCodexPreset !== -1) ||
|
||||||
|
(!showPresets && getCodexAuthApiKey(codexAuth) !== "");
|
||||||
const isCodexOfficialPreset =
|
const isCodexOfficialPreset =
|
||||||
selectedCodexPreset !== null &&
|
selectedCodexPreset !== null &&
|
||||||
|
selectedCodexPreset >= 0 &&
|
||||||
codexProviderPresets[selectedCodexPreset]?.isOfficial === true;
|
codexProviderPresets[selectedCodexPreset]?.isOfficial === true;
|
||||||
|
|
||||||
// 初始时从配置中同步 API Key(编辑模式)
|
// 初始时从配置中同步 API Key(编辑模式)
|
||||||
@@ -347,8 +382,17 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
|
|
||||||
{showPresets && !isCodex && (
|
{showPresets && !isCodex && (
|
||||||
<div className="presets">
|
<div className="presets">
|
||||||
<label>一键导入(只需要填写 key)</label>
|
<label>选择配置类型</label>
|
||||||
<div className="preset-buttons">
|
<div className="preset-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`preset-btn ${
|
||||||
|
selectedPreset === -1 ? "selected" : ""
|
||||||
|
}`}
|
||||||
|
onClick={handleCustomClick}
|
||||||
|
>
|
||||||
|
自定义
|
||||||
|
</button>
|
||||||
{providerPresets.map((preset, index) => {
|
{providerPresets.map((preset, index) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@@ -364,13 +408,34 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{selectedPreset === -1 && (
|
||||||
|
<small className="field-hint" style={{ marginTop: "8px", display: "block" }}>
|
||||||
|
手动配置供应商,需要填写完整的配置信息
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
{selectedPreset !== -1 && selectedPreset !== null && (
|
||||||
|
<small className="field-hint" style={{ marginTop: "8px", display: "block" }}>
|
||||||
|
{isOfficialPreset
|
||||||
|
? "Claude 官方登录,不需要填写 API Key"
|
||||||
|
: "使用预设配置,只需填写 API Key"}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showPresets && isCodex && (
|
{showPresets && isCodex && (
|
||||||
<div className="presets">
|
<div className="presets">
|
||||||
<label>一键导入(只需要填写 key)</label>
|
<label>选择配置类型</label>
|
||||||
<div className="preset-buttons">
|
<div className="preset-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`preset-btn ${
|
||||||
|
selectedCodexPreset === -1 ? "selected" : ""
|
||||||
|
}`}
|
||||||
|
onClick={handleCodexCustomClick}
|
||||||
|
>
|
||||||
|
自定义
|
||||||
|
</button>
|
||||||
{codexProviderPresets.map((preset, index) => (
|
{codexProviderPresets.map((preset, index) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
@@ -384,6 +449,18 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{selectedCodexPreset === -1 && (
|
||||||
|
<small className="field-hint" style={{ marginTop: "8px", display: "block" }}>
|
||||||
|
手动配置供应商,需要填写完整的配置信息
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
{selectedCodexPreset !== -1 && selectedCodexPreset !== null && (
|
||||||
|
<small className="field-hint" style={{ marginTop: "8px", display: "block" }}>
|
||||||
|
{isCodexOfficialPreset
|
||||||
|
? "Codex 官方登录,不需要填写 API Key"
|
||||||
|
: "使用预设配置,只需填写 API Key"}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -448,7 +525,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
}
|
}
|
||||||
disabled={isCodexOfficialPreset}
|
disabled={isCodexOfficialPreset}
|
||||||
required={
|
required={
|
||||||
selectedCodexPreset !== null && !isCodexOfficialPreset
|
selectedCodexPreset !== null && selectedCodexPreset >= 0 && !isCodexOfficialPreset
|
||||||
}
|
}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
style={
|
style={
|
||||||
@@ -521,7 +598,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
style={{ fontFamily: "monospace", fontSize: "14px" }}
|
style={{ fontFamily: "monospace", fontSize: "14px" }}
|
||||||
/>
|
/>
|
||||||
<small className="field-hint">
|
<small className="field-hint">
|
||||||
Codex config.toml 配置内容(可留空)
|
Codex config.toml 配置内容
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const codexProviderPresets: CodexProviderPreset[] = [
|
|||||||
config: `model_provider = "packycode"
|
config: `model_provider = "packycode"
|
||||||
model = "gpt-5"
|
model = "gpt-5"
|
||||||
model_reasoning_effort = "high"
|
model_reasoning_effort = "high"
|
||||||
|
disable_response_storage = true
|
||||||
|
|
||||||
[model_providers.packycode]
|
[model_providers.packycode]
|
||||||
name = "packycode"
|
name = "packycode"
|
||||||
|
|||||||
Reference in New Issue
Block a user