凌晨一点,当终端里的并发测试输出 16/16 requests passed 时,我知道这个东西可以发布了。
从下午开始研究三个开源项目,到深夜在 PyPI 上发布 CopilotX,整个过程不到一天。这篇文章记录这个项目从灵感到上线的完整旅程。
起因:三个项目的启发
事情的起点是一个简单的好奇心——GitHub Copilot 订阅能不能当本地 API 用?
我在 GitHub 上发现了三个试图解决这个问题的项目:
| 项目 | 语言 | 核心思路 |
|---|---|---|
| Router-Maestro | Python | 独立 CLI 工具,OAuth 登录后起 FastAPI 代理 |
| Agent-Maestro | Python | 完整 Agent 框架,内置 MCP Server |
| GateX(我之前写的) | TypeScript | VS Code 扩展,借 vscode.lm API 透传 |
花了一个下午仔细研究它们的代码,我得出几个关键发现:
Router-Maestro 的设计最优雅——它发现了 GitHub Copilot 的一个重要事实:Copilot 后端(api.githubcopilot.com)原生就是 OpenAI 格式。这意味着 OpenAI SDK 的请求只需要换个 Token 就能直接转发,根本不需要复杂的格式转换。而且它用 OAuth Device Flow 做认证,完全不依赖 VS Code。
Agent-Maestro 走了另一条路,想做成"一站式 Agent 平台"。功能丰富但过于庞大,光 MCP Server 就带了十几个工具,对于"我只想有个本地 API"的需求来说太重了。
GateX 是我之前写的 VS Code 扩展,通过 vscode.lm API 调用模型再转成 HTTP。优点是零配置(VS Code 已经处理了认证),缺点是必须在 VS Code 里运行,没法在纯终端环境用。
三个项目各有优缺,但没有一个完美击中我的需求:轻量、独立、双格式(OpenAI + Anthropic)、开箱即用。
那就自己写一个。
设计决策:Less is More
在动手之前,我花了一些时间做设计决策。这些选择后来证明每一个都是对的:
为什么是 Python 独立工具?
❌ VS Code 扩展 → 依赖编辑器,不够独立
❌ Node.js CLI → AI/ML 生态大多在 Python
✅ Python CLI → pip install 一行搞定,生态亲和
为什么同时支持 OpenAI 和 Anthropic 格式?
因为我的主要使用场景就是两个:
- Claude Code → 说 Anthropic 协议(
/v1/messages) - Codex / 其他工具 → 说 OpenAI 协议(
/v1/chat/completions)
Copilot 后端原生是 OpenAI 格式,所以 OpenAI 请求直接透传;Anthropic 请求需要实时翻译。一个代理同时覆盖两大生态,这是 CopilotX 的核心差异化。
为什么用 OAuth Device Flow?
GitHub 的 Device Flow 认证非常优雅:
1. 终端显示一个 8 位码 → AB12-CD34
2. 浏览器打开 github.com/login/device
3. 输入码,授权完成
4. 终端自动获取 Token
不需要在终端里粘贴密码,不需要手动创建 Personal Access Token。而且我用的 client_id 跟 VS Code Copilot Chat 扩展完全一样(Iv1.b507a08c87ecfe98),这样获得的 Token 权限也完全一致。
核心架构:两层 Token + 格式翻译
整个系统的架构可以画成这样:
你的工具 (Claude Code / Codex / Python 脚本)
│
│ OpenAI 或 Anthropic 格式
▼
┌────────────────────────────────────────┐
│ CopilotX (localhost:24680) │
│ │
│ OpenAI 请求 → 直接转发(加 Token) │
│ Anthropic 请求 → 翻译成 OpenAI → 转发 │
│ │
│ Token 管理:GitHub OAuth → Copilot JWT │
│ (JWT ~30分钟,到期前60秒自动刷新) │
└──────────────────┬─────────────────────┘
│ OpenAI 格式 + Copilot JWT
▼
api.githubcopilot.com/chat/completions
→ GPT-4o, Claude, Gemini, o3-mini ...
两层 Token 机制
这是我从 Router-Maestro 学到的最重要的一点:GitHub Copilot 用的是两层 Token 架构。
第一层:GitHub OAuth Token(长期有效)
- 通过 Device Flow 获取
- 存储在
~/.copilotx/auth.json(chmod 600) - 只要用户不撤销授权就一直有效
第二层:Copilot JWT(约 30 分钟有效)
- 用 GitHub Token 向
api.github.com/copilot_internal/v2/token换取 - 实际请求 Copilot API 用的是这个 JWT
- CopilotX 在 JWT 到期前 60 秒自动刷新,对用户完全透明
- 用 GitHub Token 向
async def ensure_copilot_token(self) -> str:
"""确保 Copilot JWT 有效,到期前自动刷新"""
if self._copilot_token and time.time() < self._expires_at - TOKEN_REFRESH_BUFFER:
return self._copilot_token
# 自动用 GitHub Token 换取新的 Copilot JWT
self._copilot_token = await self._fetch_copilot_token()
return self._copilot_token
Anthropic → OpenAI 格式翻译
Copilot 后端只说 OpenAI,但我需要让它也能接受 Anthropic 格式。翻译引擎需要处理的核心差异:
| 差异点 | OpenAI | Anthropic |
|---|---|---|
| System 消息 | messages 数组里 role: "system" | 独立的 system 字段 |
| 内容格式 | content: "string" | content: [{type: "text", text: "..."}] |
| 停止原因 | finish_reason: "stop" | stop_reason: "end_turn" |
| 流式事件 | data: {choices: [{delta}]} | event: content_block_delta + data: {delta} |
其中流式翻译最复杂。Anthropic 的 SSE 有 6 种事件类型需要构造:
message_start → content_block_start → content_block_delta (N次)
→ content_block_stop → message_delta → message_stop
而 OpenAI 只有 data: {...} 和 data: [DONE]。我需要把 OpenAI 的简单流拆解成 Anthropic 的完整事件序列。
实现亮点
22 个模型,开箱即用
copilotx models 一下就能看到 Copilot 后端提供的所有模型:
📋 Available Models
┌──────────────────────────────────┬───────────────────────┬─────────┐
│ Model ID │ Name │ Vendor │
├──────────────────────────────────┼───────────────────────┼─────────┤
│ gpt-4o │ GPT-4o │ Azure │
│ gpt-4o-mini │ GPT-4o Mini │ Azure │
│ o3-mini │ o3-mini │ Azure │
│ claude-sonnet-4 │ Claude Sonnet 4 │ Azure │
│ gemini-2.0-flash │ Gemini 2.0 Flash │ Azure │
│ ... │ ... │ ... │
└──────────────────────────────────┴───────────────────────┴─────────┘
Total: 22 models
GPT-4o、Claude Sonnet 4、Gemini 2.0 Flash、o3-mini……这些模型全都可以通过一个统一的本地 API 调用。
智能端口管理
默认端口选了 24680(偶数递增,好记又冷门)。但真正的亮点在于端口策略的双模式设计:
# 自动模式:24680 被占就自动试 24681、24682…
copilotx serve
# 严格模式:端口冲突直接报错,尊重用户的选择
copilotx serve --port 9999
同时,启动时会写入 ~/.copilotx/server.json:
{
"host": "127.0.0.1",
"port": 24680,
"pid": 12345,
"base_url": "http://127.0.0.1:24680"
}
这样其他脚本可以程序化地发现端口,而不需要硬编码。服务器停止时自动清理这个文件。
并发测试:16/16
这是最让我满意的一个环节。我用 Python asyncio + httpx 写了一个并发测试套件,三组测试全部通过:
============================================================
OpenAI /v1/chat/completions: 5 parallel requests
============================================================
[OK] #1 | 1000ms | Request 1 OK
[OK] #2 | 975ms | Request 2 OK
[OK] #3 | 958ms | Request 3 OK
[OK] #4 | 962ms | Request 4 OK
[OK] #5 | 966ms | Request 5 OK
--- 5/5 succeeded, wall time: 1001ms ---
============================================================
Anthropic /v1/messages: 5 parallel requests
============================================================
[OK] #1 | 1062ms | Request 1 OK
[OK] #2 | 1063ms | Request 2 OK
[OK] #3 | 998ms | Request 3 OK
[OK] #4 | 1189ms | Request 4 OK
[OK] #5 | 1032ms | Request 5 OK
--- 5/5 succeeded, wall time: 1190ms ---
============================================================
Mixed: 3 OpenAI + 3 Anthropic parallel
============================================================
[OK] #1 | 604ms | Hello! How can I assist you today?
[OK] #4 | 2135ms | Hi there! I'm Claude
--- 6/6 succeeded, wall time: 2136ms ---
FINAL: 16/16 requests passed ✅
注意看 wall time——5 个并发请求的总耗时约等于单个请求(~1s),证明 FastAPI async 架构真正实现了并行处理。
三行命令体验
说了这么多技术细节,实际用起来就三行:
pip install copilotx # 安装
copilotx auth login # 浏览器授权
copilotx serve # 起服务
然后你就有了一个本地 API 服务器。搭配 Claude Code:
export ANTHROPIC_BASE_URL=http://localhost:24680
export ANTHROPIC_API_KEY=copilotx
claude
搭配 Codex:
export OPENAI_BASE_URL=http://localhost:24680/v1
export OPENAI_API_KEY=copilotx
codex
搭配任意 Python 脚本:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:24680/v1", api_key="copilotx")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
stream=True,
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="")
api_key 可以填任意字符串——CopilotX 自己管理认证,不需要你提供 key。
技术栈一览
| 组件 | 选择 | 理由 |
|---|---|---|
| HTTP 框架 | FastAPI + Uvicorn | 原生 async,SSE streaming 支持好 |
| HTTP 客户端 | httpx | async 支持,streaming 友好 |
| CLI 框架 | Typer + Rich | 漂亮的终端输出,类型安全 |
| 数据验证 | Pydantic v2 | FastAPI 原生支持 |
| 构建系统 | Hatchling | 现代 Python 打包标准 |
整个项目 21 个文件,1479 行代码。没有一行是多余的。
src/copilotx/
├── auth/ # OAuth + Token 生命周期
│ ├── oauth.py # GitHub Device Flow
│ ├── token.py # Copilot JWT 管理
│ └── storage.py # 凭证持久化
├── proxy/ # 代理核心
│ ├── client.py # Copilot API 客户端
│ ├── translator.py # Anthropic ↔ OpenAI 翻译
│ └── streaming.py # SSE 流式响应
├── server/ # FastAPI 服务
│ ├── app.py # 应用工厂 + lifespan
│ ├── routes_openai.py
│ ├── routes_anthropic.py
│ └── routes_models.py
├── cli.py # Typer CLI
└── config.py # 全局常量
收获与反思
从分析到产品,速度可以很快
这个项目从"我来看看别人怎么做的"到"PyPI 发布 + GitHub 开源",用了不到一天。关键不是写得快,而是先花足够时间研究,想清楚再动手。那个下午分析三个项目的时间,价值远超晚上的编码时间。
站在巨人的肩膀上
Router-Maestro 教会了我 OAuth Device Flow 和两层 Token 机制,Agent-Maestro 让我知道了 MCP 集成的可能性,GateX 则是我自己之前的实践积累。CopilotX 不是凭空创造的,是三个项目最佳实践的融合。
下一步计划
- v0.2: 支持 Google Gemini API 格式(第三大 AI SDK)
- v0.3: 增加
copilotx config命令,支持自定义 Header 和模型别名 - GateX v2: 把 CopilotX 的双格式翻译引擎移植回 VS Code 扩展
链接
- PyPI: pypi.org/project/copilotx
- GitHub: github.com/Polly2014/CopilotX
如果你也有 GitHub Copilot 订阅,不妨试试:
pip install copilotx && copilotx auth login && copilotx serve
三行命令,解锁 22 个模型。🚀

