一个桌面应用,如何把 7 个 AI 编程工具全部"中间人"掉——CC Switch 深度拆解(上)

不改一行代码,零网络劫持,它凭什么能接管 Claude Code、Codex 和 Gemini CLI 的全部 API 请求?

一句话介绍:CC Switch 是一款基于 Tauri 2 的桌面应用,核心功能是统一管理 Claude Code、Codex、Gemini CLI 等 7 个 AI 编程工具的 API 供应商,支持一键切换、自动故障转移、用量统计和 Skill/MCP 多端同步。

听起来平平无奇?但当你知道它没有用任何网络层劫持技术,却能让所有 AI 工具的 API 请求从它这里过一遍时,事情就变得有意思了。

本文是第一篇,聚焦两个核心问题:它是怎么拦截请求的?它怎么做到不拦截也能统计用量?

一、秘密藏在一个字段里

Claude Code 的配置文件在 ~/.claude/settings.json,里面有一个关键的字段:

json 复制代码
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.anthropic.com"
  }
}

Codex 的配置在 ~/.codex/config.toml:

ini 复制代码
base_url = "https://api.openai.com/v1"

Gemini CLI 的配置在 ~/.gemini/.env:

ini 复制代码
GOOGLE_GEMINI_BASE_URL=https://generativelanguage.googleapis.com

你大概已经猜到关键了------这些 AI 工具都有一个共同的设计:BASE_URL 写在配置文件里,而不是硬编码进二进制。 这不是巧合,是企业市场的要求:大公司需要通过自己的代理访问 API,不提供这个配置项就没法卖企业客户。

而 CC Switch 的做法朴素到令人意外:

找到每个工具的配置文件,把 BASE_URL 改成 http://127.0.0.1:代理端口,然后在自己本地跑一个 HTTP 代理来转发请求。

零网络劫持,零 hosts 篡改,零进程注入。就是改配置文件。

二、逐个拆解:对每个工具做了什么

2.1 Claude Code

项目

细节

被修改的文件

~/.claude/settings.json

改了什么

ANTHROPIC_BASE_URL → http://127.0.0.1:47832

API Key

替换为 PROXY_MANAGED 占位符,真实 Key 存在 CC Switch 的 SQLite 里

模型名

写入 Claude 角色别名(claude-sonnet-4-6、claude-opus-4-8[1M] 等),由代理映射到供应商实际模型 ID

删除的字段

ANTHROPIC_MODEL、ANTHROPIC_REASONING_MODEL、ANTHROPIC_DEFAULT_SONNET_MODEL_NAME 等 12 个旧字段,避免冲突

2.2 Claude Desktop

与 Claude Code 类似,但走独立路由前缀 /claude-desktop/,以便区分两个客户端的请求来源。Claude Desktop 的模型列表需要单独处理,代理要模拟 /claude-desktop/v1/models 端点。

2.3 Codex (OpenAI)

项目

细节

被修改的文件

~/.codex/auth.json + ~/.codex/config.toml

改了什么

TOMP 中的 base_url → http://127.0.0.1:47832

额外操作

生成 model catalog JSON 文件,列出当前供应商支持的模型列表

Codex 的配置是 TOML 格式,CC Switch 用了 toml_edit 库来修改------这个库保留了原文件中的注释和格式,不会"格式化暴力覆盖"。

2.4 Gemini CLI

项目

细节

被修改的文件

~/.gemini/.env

改了什么

GOOGLE_GEMINI_BASE_URL → http://127.0.0.1:47832

Gemini 还有一个 ~/.gemini/settings.json,CC Switch 会修改其中的 security.auth.selectedType 字段,在 gemini-api-key 和 oauth-personal 之间切换,以适配不同供应商的认证模式。

2.5 代理服务器做了什么

CC Switch 内嵌了一个基于 Axum(Rust HTTP 框架)的代理服务器,监听 127.0.0.1:代理端口。路由表覆盖了所有主流 API:

bash 复制代码
POST /v1/messages           → Anthropic Messages API
POST /v1/chat/completions   → OpenAI Chat API
ANY  /v1beta/*path          → Gemini API
POST /v1/responses          → OpenAI Responses API
POST /codex/v1/chat/completions  → Codex 专用路由
...

每个请求到达后的处理流程:

  1. 识别来源:根据 URL 路径前缀判断来自哪个应用
  2. 选供应商:从 SQLite 数据库查当前应用绑定的供应商
  3. 熔断检查:检查该供应商是否处于熔断状态
  4. 替换认证信息 :把 PROXY_MANAGED 占位符替换为真实 API Key
  5. 模型名映射 :把 claude-sonnet-4-6 映射成供应商的实际模型 ID
  6. 构建上游请求:用供应商的真实地址和 Key 发 HTTP 请求
  7. 透明返回:把上游响应原样传回给客户端

三、接管流程:不只是改文件

代码里的 set_takeover_for_app 方法是一个多步骤的严谨流程(services/proxy.rs:624):

  1. 检查代理是否运行 → 没启动则自动启动
  2. 幂等检查 → 如果已经是接管状态,直接返回
  3. 备份原始配置 → 把 settings.json / config.toml / .env 存到 SQLite 的 live_backup 表
  4. 同步 API Key → 把当前配置文件中的真实 Key 提取到供应商配置中
  5. 写入接管配置 → 改 BASE_URL、替换 Key、写入模型别名
  6. 标记已接管 → 数据库记录状态,支持后续"关闭接管"时恢复
  7. 失败回滚 → 如果任一步骤失败,自动从备份恢复原始配置

关闭接管时反过来:从 live_backup 表取出备份,写回原始配置文件,删除备份记录。整个过程对用户来说是一个开关的操作。

四、更惊艳的部分:不接管也能统计用量

前面说的全是"接管"模式------改配置文件,让请求经过本地代理。但如果用户没用代理模式呢?假设 Codex 直连 OpenAI,CC Switch 还能统计到用量吗?

能。而且不需要任何拦截。

4.1 Codex 自己会写日志

Codex CLI 在运行过程中,会把每个会话的详细事件写入 ~/.codex/sessions/ 下:

javascript 复制代码
~/.codex/sessions/
├── 2026/
│   └── 07/
│       └── 14/
│           ├── abc123.jsonl
│           └── def456.jsonl

这些 .jsonl 文件里记录了完整的事件流:

css 复制代码
{"type": "session_meta", "payload": {"id": "thread-abc", ...}}
{"type": "turn_context", "payload": {"model": "claude-sonnet-4-6", ...}}
{"type": "event_msg", "type": "token_count", "total_token_usage": {"input": 15000, "output": 3200, ...}}

每次 API 调用完成后,Codex 自己就把 token 用量写进去了。

4.2 CC Switch 的做法:纯读文件,不拦截

services/session_usage_codex.rs 的注释写得很直白:

javascript 复制代码
从 ~/.codex/sessions/ 下的 JSONL 会话文件中提取精确 token 使用数据,
替代原有的 state_5.sqlite 估算方案。

数据流:
~/.codex/sessions/YYYY/MM/DD/*.jsonl → 增量解析 → delta 计算 → 费用计算 → proxy_request_logs 表

流程:

  1. 扫描 ~/.codex/sessions/ 下所有 JSONL 文件
  2. 查 sync_state 表:每个文件上次读到了第几行?文件修改时间变了没?
  3. 增量解析:从上次断点继续读,只解析新增的行
  4. 找 token_count 事件:提取累计 token 用量
  5. 算 delta:当前累计值 - 上次累计值 = 本次调用的消耗
  6. 算费用:根据当前供应商的模型定价,计算美元费用
  7. 写日志 :写入 CC Switch 的 proxy_request_logs 表
  8. 更新 sync_state:记录当前行号和文件 mtime
  9. 去重 :同一个 message_id 不重复写入

4.3 不止 Codex,Claude Code 和 Gemini 也一样

应用

被读取的目录

解析方式

Claude Code

~/.claude/projects/*/ JSONL 文件

解析 assistant message 中的 usage 字段

Codex

~/.codex/sessions/YYYY/MM/DD/*.jsonl

解析 event_msg(type=token_count)

Gemini CLI

Gemini 原生日志

解析 Gemini 格式的 token usage

OpenCode

OpenCode 会话目录

增量子代理处理

4.4 同步频率

代码在 lib.rs:1080-1139,应用启动时 spawn 一个后台 tokio 任务:

  1. 启动时立即执行一次全量同步
  2. 之后每 60 秒执行一次增量同步
  3. 用户也可以在前端手动点击刷新触发同步

每次同步只读增量部分,不会重复统计。

五、这背后要求什么?

要做到以上这些,CC Switch 的开发团队需要彻底搞清楚 7 个 AI 工具的以下细节:

  • 配置文件在哪、什么格式(JSON / TOML / YAML / .env / 混用)
  • BASE_URL 和环境变量的确切键名(每个工具都不一样)
  • 认证机制的差异(API Key / OAuth / 托管账号)
  • 模型名的命名约定和映射规则
  • 会话日志的目录结构、文件格式、事件语义、字段含义
  • 子代理 session 的父子关系(session_id 指向父线程)
  • Codex 版本升级后 SQLite 文件名的变化(state_5.sqlite)
  • 不同操作系统下的路径差异(Hermes 在 Windows 下是 %LOCALAPPDATA%\hermes)

这些都不是官方文档会写清楚的东西。 每一个字段、每一条路径、每一种特殊情况,都是逆向工程和踩坑踩出来的。版本号到了 3.17,issue 数量可想而知。

六、一个关键的技术判断

CC Switch 之所以能存在,依赖于一个行业共识:所有 AI 编程工具都通过配置文件暴露 BASE_URL。

这个共识不是为了让中间人工具存活------是为了企业市场。Anthropic、OpenAI、Google 都有自己的企业版 API Gateway,大公司内部的开发者需要走内网代理,不支持自定义 BASE_URL 就没法做企业生意。

CC Switch 只是利用了这个设计,做了一套企业级的多供应商管理工具。它的竞争对手不是某个小工具,而是"每次手动改配置文件"这种原始操作。

下一篇将深入 Skill 和 MCP 模块:一个 3100 行的 Rust 代码如何实现跨 5 个 AI 工具的 Skill 包管理,以及 MCP 配置的 JSON/TOML/YAML 多格式适配器是怎么设计的。

相关推荐
独隅8 小时前
Claude API 实战教程:从入门到完整项目
android·java·数据库·claude·claude code
独隅12 小时前
Claude 高效使用指南与效果展示
claude·claude code
ZzT16 小时前
Claude Duet 安全设计拆解:替你回邮件、付账单的 agent,碰不到你的密码
ai编程·claude
小七-七牛开发者17 小时前
当 Agent 接手更多工作,我们还能把好关吗?
ai·openai·agent·claude
独隅17 小时前
Claude 高效使用指南深度评测
java·开发语言·claude·claude code
yjm00217 小时前
如何用 Claude Opus 5.5 做一支音乐 MV?代码逐帧渲染完整教程(2026)
ai编程·claude
独隅18 小时前
Claude 新手入门与高效调用指南
claude
独隅19 小时前
Claude 高效应用实战指南
android·java·开发语言·claude·claude code
技术小甜甜21 小时前
【Copilot】能选 Claude 和 GPT-6 了,Excel 分析该交给谁
gpt·excel·copilot·claude·microsoft365·gpt-6