一个桌面应用,如何把 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_URLhttp://127.0.0.1:47832

API Key

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

模型名

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

删除的字段

ANTHROPIC_MODELANTHROPIC_REASONING_MODELANTHROPIC_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_urlhttp://127.0.0.1:47832

额外操作

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

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

2.4 Gemini CLI

项目

细节

被修改的文件

~/.gemini/.env

改了什么

GOOGLE_GEMINI_BASE_URLhttp://127.0.0.1:47832

Gemini 还有一个 ~/.gemini/settings.json,CC Switch 会修改其中的 security.auth.selectedType 字段,在 gemini-api-keyoauth-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 多格式适配器是怎么设计的。

相关推荐
洛卡卡了4 小时前
Claude Code 项目级 Skill:团队协作场景下的使用场景
人工智能·后端·claude
Lazy_zheng1 天前
TDD 实战:Claude Code Superpowers 保姆级教程14 个 Skills 全解析 + 实战开发
前端·claude·vibecoding
plainGeekDev2 天前
多步流水线 + HereDoc:一条脚本完成全自动内容生产
aigc·ai编程·claude
码哥字节2 天前
spec-superflow 上手指南:6 平台安装
claude
小狼1832 天前
实践3|用 Claude Code 从需求文档走到集成测试打通真数据库
claude
冬奇Lab2 天前
开源项目第157期:archify — 用自然语言生成可主题切换、高分辨率导出的架构图
人工智能·架构·claude
码哥字节3 天前
别再纠结 24 万星 Superpowers 还是 5.8 万星 OpenSpec,有人把他们融合成一个工作流在 Github 开源
ai编程·claude
武子康3 天前
OpenAI Workspace Agents 进入信用计费:Coding Agent 从额度体验走向成本治理
人工智能·openai·claude