- 添加完整的项目基础结构,包括配置、类型定义和常量 - 实现OAuth认证流程和令牌管理 - 开发请求转换和响应处理逻辑 - 添加SSE流处理和ChatCompletions API转换 - 实现模型映射和提示指令系统 - 包含Docker部署配置和快速启动文档 - 添加自动登录功能和测试脚本
21 KiB
21 KiB
chatgpt-codex-router 项目计划
项目概览
项目目标: 创建一个独立的 OpenAI 兼容 API 服务器,通过 OAuth 认证将请求转发到 ChatGPT 后端,支持流式传输和详细的日志记录。
支持的模型(GPT-5.x 系列):
gpt-5.1(none/low/medium/high)gpt-5.2(none/low/medium/high/xhigh)gpt-5.1-codex(low/medium/high)gpt-5.1-codex-max(low/medium/high/xhigh)gpt-5.1-codex-mini(medium/high)gpt-5.2-codex(low/medium/high/xhigh)
项目结构
chatgpt-codex-router/
├── src/
│ ├── index.ts # 服务器入口
│ ├── server.ts # Hono 服务器配置
│ ├── router.ts # API 路由定义
│ ├── config.ts # 配置管理
│ ├── logger.ts # 日志系统
│ ├── auth/ # 认证模块
│ │ ├── oauth.ts # OAuth 流程逻辑
│ │ ├── token-storage.ts # Token 本地 JSON 存储
│ │ ├── token-refresh.ts # Token 刷新逻辑
│ │ ├── server.ts # 本地 OAuth 回调服务器
│ │ └── browser.ts # 浏览器打开工具
│ ├── request/ # 请求处理
│ │ ├── transformer.ts # 请求体转换
│ │ ├── headers.ts # Header 生成
│ │ ├── validator.ts # 请求验证
│ │ ├── model-map.ts # 模型映射
│ │ └── reasoning.ts # 推理参数配置
│ ├── response/ # 响应处理
│ │ ├── handler.ts # 响应处理器
│ │ ├── sse-parser.ts # SSE 流解析
│ │ ├── converter.ts # 响应格式转换
│ │ └── chat-completions.ts # Chat Completions 格式转换器
│ ├── prompts/ # 内置 Codex Prompts
│ │ ├── gpt-5-1.md # GPT-5.1 系统提示
│ │ ├── gpt-5-2.md # GPT-5.2 系统提示
│ │ ├── gpt-5-1-codex.md # GPT-5.1 Codex 系统提示
│ │ ├── gpt-5-1-codex-max.md # GPT-5.1 Codex Max 系统提示
│ │ ├── gpt-5-1-codex-mini.md # GPT-5.1 Codex Mini 系统提示
│ │ ├── gpt-5-2-codex.md # GPT-5.2 Codex 系统提示
│ │ └── index.ts # Prompt 加载器
│ ├── constants.ts # 常量定义
│ └── types.ts # TypeScript 类型定义
├── public/
│ └── oauth-success.html # OAuth 成功页面
├── docker/
│ ├── Dockerfile # Docker 镜像配置
│ ├── docker-compose.yml # Docker Compose 配置
│ └── .dockerignore # Docker 忽略文件
├── logs/ # 日志输出目录(.gitignore)
├── data/ # 数据目录(.gitignore,存储 tokens)
├── package.json
├── tsconfig.json
├── .gitignore
└── README.md
核心功能模块详解
1. 认证模块 (src/auth/)
1.1 OAuth 流程 (oauth.ts)
功能:
- PKCE challenge 生成
- 授权 URL 构建
- Authorization code 交换为 tokens
- JWT 解析获取 account_id
关键常量:
CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize"
TOKEN_URL = "https://auth.openai.com/oauth/token"
REDIRECT_URI = "http://localhost:1455/auth/callback"
SCOPE = "openid profile email offline_access"
1.2 Token 存储 (token-storage.ts)
功能:
- 读取/保存/删除 tokens
- 存储路径:
data/tokens.json - 存储格式:
{ "access_token": "...", "refresh_token": "...", "expires_at": 1234567890, "account_id": "...", "updated_at": 1234567890 }
1.3 Token 刷新 (token-refresh.ts)
功能:
- 检查 token 是否过期(提前 5 分钟刷新)
- 自动刷新过期的 token
- 更新本地存储
- 刷新失败时抛出错误
1.4 本地 OAuth 服务器 (server.ts)
功能:
- 监听
http://127.0.0.1:1455/auth/callback - 接收并验证 authorization code
- 返回 OAuth 成功页面
- Polling 机制(最多等待 60 秒)
1.5 浏览器工具 (browser.ts)
功能:
- 跨平台浏览器打开(macOS/Linux/Windows)
- 静默失败(用户可手动复制 URL)
2. 请求处理模块 (src/request/)
2.1 请求体转换 (transformer.ts)
核心转换逻辑:
// 原始请求
{
"model": "gpt-5.2-codex",
"messages": [...],
"stream": false,
"temperature": 0.7
}
// 转换后请求
{
"model": "gpt-5.2-codex",
"input": [...], // messages 转换为 input 格式
"stream": true, // 强制 stream=true
"store": false, // 添加 store=false
"instructions": "...", // 添加 Codex 系统提示
"reasoning": {
"effort": "high",
"summary": "auto"
},
"text": {
"verbosity": "medium"
},
"include": ["reasoning.encrypted_content"]
}
转换步骤:
- 模型名称标准化(使用 model-map)
messages→input格式转换- 过滤
item_reference和 IDs - 添加
store: false,stream: true - 添加 Codex 系统提示(从内置 prompts 加载)
- 配置 reasoning 参数(根据模型类型)
- 配置 text verbosity
- 添加 include 参数
- 移除不支持的参数(
max_output_tokens,max_completion_tokens)
2.2 模型映射 (model-map.ts)
支持的模型:
const MODEL_MAP: Record<string, string> = {
// GPT-5.1
"gpt-5.1": "gpt-5.1",
"gpt-5.1-none": "gpt-5.1",
"gpt-5.1-low": "gpt-5.1",
"gpt-5.1-medium": "gpt-5.1",
"gpt-5.1-high": "gpt-5.1",
// GPT-5.2
"gpt-5.2": "gpt-5.2",
"gpt-5.2-none": "gpt-5.2",
"gpt-5.2-low": "gpt-5.2",
"gpt-5.2-medium": "gpt-5.2",
"gpt-5.2-high": "gpt-5.2",
"gpt-5.2-xhigh": "gpt-5.2",
// GPT-5.1 Codex
"gpt-5.1-codex": "gpt-5.1-codex",
"gpt-5.1-codex-low": "gpt-5.1-codex",
"gpt-5.1-codex-medium": "gpt-5.1-codex",
"gpt-5.1-codex-high": "gpt-5.1-codex",
// GPT-5.1 Codex Max
"gpt-5.1-codex-max": "gpt-5.1-codex-max",
"gpt-5.1-codex-max-low": "gpt-5.1-codex-max",
"gpt-5.1-codex-max-medium": "gpt-5.1-codex-max",
"gpt-5.1-codex-max-high": "gpt-5.1-codex-max",
"gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max",
// GPT-5.1 Codex Mini
"gpt-5.1-codex-mini": "gpt-5.1-codex-mini",
"gpt-5.1-codex-mini-medium": "gpt-5.1-codex-mini",
"gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini",
// GPT-5.2 Codex
"gpt-5.2-codex": "gpt-5.2-codex",
"gpt-5.2-codex-low": "gpt-5.2-codex",
"gpt-5.2-codex-medium": "gpt-5.2-codex",
"gpt-5.2-codex-high": "gpt-5.2-codex",
"gpt-5.2-codex-xhigh": "gpt-5.2-codex"
};
2.3 Header 生成 (headers.ts)
Headers 列表:
{
"Authorization": `Bearer ${accessToken}`,
"chatgpt-account-id": accountId,
"OpenAI-Beta": "responses=experimental",
"originator": "codex_cli_rs",
"session_id": promptCacheKey,
"conversation_id": promptCacheKey,
"accept": "text/event-stream",
"content-type": "application/json"
}
2.4 Reasoning 配置 (reasoning.ts)
Reasoning effort 配置:
gpt-5.2,gpt-5.1: 支持none/low/medium/high(默认none)gpt-5.2-codex,gpt-5.1-codex-max: 支持low/medium/high/xhigh(默认high)gpt-5.1-codex: 支持low/medium/high(默认medium)gpt-5.1-codex-mini: 支持medium/high(默认medium)
2.5 请求验证 (validator.ts)
验证内容:
model参数是否存在且有效messages或input是否存在- 消息格式是否正确
- 必需字段是否存在
3. 响应处理模块 (src/response/)
3.1 响应处理器 (handler.ts)
处理流程:
async function handleResponse(response: Response, isStreaming: boolean): Promise<Response> {
if (isStreaming) {
// 流式响应:直接转发 SSE
return forwardStream(response);
} else {
// 非流式:解析 SSE 并转换为 JSON
return parseSseToJson(response);
}
}
3.2 SSE 解析器 (sse-parser.ts)
解析逻辑:
function parseSseStream(sseText: string): Response | null {
const lines = sseText.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.substring(6));
if (data.type === 'response.done' || data.type === 'response.completed') {
return data.response;
}
}
}
return null;
}
3.3 响应格式转换 (converter.ts & chat-completions.ts)
ChatGPT Responses API 格式 → OpenAI Chat Completions 格式:
原始格式(ChatGPT):
{
"type": "response.done",
"response": {
"id": "resp_...",
"status": "completed",
"output": [
{
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Hello, world!"
}
]
}
],
"usage": {
"input_tokens": 100,
"output_tokens": 50,
"total_tokens": 150
}
}
}
转换后格式(OpenAI):
{
"id": "resp_...",
"object": "chat.completion",
"created": 1736153600,
"model": "gpt-5.2-codex",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello, world!"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 100,
"completion_tokens": 50,
"total_tokens": 150
}
}
流式响应格式转换:
原始 SSE 事件(ChatGPT):
data: {"type": "response.output_item.add.delta", "delta": {"content": [{"type": "output_text", "text": "Hello"}]}}
data: {"type": "response.output_item.add.delta", "delta": {"content": [{"type": "output_text", "text": ", world!"}]}}
data: {"type": "response.done", "response": {...}}
转换后 SSE 事件(OpenAI):
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {"content": "Hello"}, "finish_reason": null}]}
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {"content": ", world!"}, "finish_reason": null}]}
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}
4. 内置 Prompts (src/prompts/)
文件结构:
gpt-5-1.md- GPT-5.1 通用模型系统提示gpt-5-2.md- GPT-5.2 通用模型系统提示gpt-5-1-codex.md- GPT-5.1 Codex 系统提示gpt-5-1-codex-max.md- GPT-5.1 Codex Max 系统提示gpt-5-1-codex-mini.md- GPT-5.1 Codex Mini 系统提示gpt-5-2-codex.md- GPT-5.2 Codex 系统提示
Prompt 加载器 (index.ts):
export function getPrompt(modelFamily: ModelFamily): string {
const promptFile = PROMPT_FILES[modelFamily];
return readFileSync(join(__dirname, promptFile), 'utf-8');
}
Prompts 内容: 需要从 Codex CLI GitHub 仓库下载最新的 prompts 并嵌入到项目中。
5. 日志系统 (src/logger.ts)
日志级别:
ERROR- 错误日志(始终记录)WARN- 警告日志(始终记录)INFO- 信息日志(通过LOG_LEVEL=info启用)DEBUG- 调试日志(通过LOG_LEVEL=debug启用)
日志配置:
- 日志目录:
logs/ - 日志文件格式:
{date}-{level}.log - 日志格式:
[timestamp] [level] [request-id] message - 控制台输出:带颜色和结构化数据
日志内容:
- 请求开始/结束
- Token 刷新
- 请求转换前后
- 响应状态
- 错误详情(请求/响应体)
示例日志:
[2025-01-06 10:30:00] [INFO] [req-001] POST /v1/chat/completions
[2025-01-06 10:30:00] [DEBUG] [req-001] Before transform: {"model": "gpt-5.2-codex", "messages": [...]}
[2025-01-06 10:30:00] [DEBUG] [req-001] After transform: {"model": "gpt-5.2-codex", "input": [...], "stream": true, "store": false}
[2025-01-06 10:30:01] [INFO] [req-001] Response: 200 OK, 150 tokens
[2025-01-06 10:30:01] [INFO] [req-001] Request completed in 1234ms
6. API 端点
6.1 POST /v1/chat/completions
请求: OpenAI Chat Completions 格式 响应: OpenAI Chat Completions 格式(转换后)
流程:
- 验证请求体
- 检查 token,自动刷新
- 转换请求体(messages → input)
- 生成 Headers
- 转发到
https://chatgpt.com/backend-api/codex/responses - 处理响应(流式/非流式)
- 转换响应格式(ChatGPT → OpenAI)
6.2 POST /v1/responses
请求: OpenAI Responses API 格式 响应: OpenAI Responses API 格式(部分转换)
流程:
- 验证请求体
- 检查并刷新 token
- 转换请求体(添加必需字段)
- 生成 Headers
- 转发到
https://chatgpt.com/backend-api/codex/responses - 处理响应(流式/非流式)
- 返回转换后的 Responses API 格式
6.3 POST /auth/login
请求: 无需参数 响应: 授权 URL 和说明
流程:
- 生成 PKCE challenge 和 state
- 构建授权 URL
- 启动本地 OAuth 服务器
- 尝试打开浏览器
- 返回授权信息
6.4 POST /auth/callback
内部端点: 仅用于本地 OAuth 服务器
- 接收 authorization code
- 验证 state
- 交换 tokens
- 保存到
data/tokens.json - 返回成功页面
7. 配置管理 (src/config.ts)
配置文件: ~/.chatgpt-codex-router/config.json
默认配置:
{
"server": {
"port": 3000,
"host": "0.0.0.0"
},
"oauth": {
"clientId": "app_EMoamEEZ73f0CkXaXp7hrann",
"redirectUri": "http://localhost:1455/auth/callback",
"localServerPort": 1455
},
"backend": {
"url": "https://chatgpt.com/backend-api",
"timeout": 120000
},
"logging": {
"level": "info",
"dir": "logs",
"enableRequestLogging": false
},
"codex": {
"mode": true,
"defaultReasoningEffort": "medium",
"defaultTextVerbosity": "medium"
}
}
环境变量:
PORT- 服务器端口(默认 3000)CONFIG_PATH- 配置文件路径LOG_LEVEL- 日志级别(error/warn/info/debug)ENABLE_REQUEST_LOGGING- 启用请求日志(true/false)
8. Docker 支持 (docker/)
8.1 Dockerfile
FROM node:20-alpine
WORKDIR /app
# 安装依赖
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 复制源代码
COPY src/ ./src/
COPY public/ ./public/
COPY tsconfig.json ./
# 构建项目
RUN npm run build
# 创建数据和日志目录
RUN mkdir -p /app/data /app/logs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# 启动服务
CMD ["npm", "start"]
8.2 docker-compose.yml
version: '3.8'
services:
chatgpt-codex-router:
build: .
container_name: chatgpt-codex-router
ports:
- "3000:3000"
- "1455:1455"
volumes:
- ./data:/app/data
- ./logs:/app/logs
- ./config.json:/app/.chatgpt-codex-router/config.json:ro
environment:
- PORT=3000
- LOG_LEVEL=info
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
8.3 .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.dockerignore
Dockerfile
docker-compose.yml
test
vitest.config.ts
logs/*
data/*
!data/.gitkeep
开发步骤
阶段 1:项目初始化(第 1-2 天)
- 创建项目结构
- 配置 TypeScript
- 配置 package.json(依赖、脚本)
- 创建 .gitignore
- 设置开发环境(ESLint、Prettier)
阶段 2:基础设施(第 3-4 天)
- 实现日志系统 (
logger.ts) - 实现配置管理 (
config.ts) - 实现常量定义 (
constants.ts) - 实现类型定义 (
types.ts) - 创建基础服务器框架 (
server.ts,index.ts)
阶段 3:认证模块(第 5-7 天)
- 实现 OAuth 流程 (
oauth.ts) - 实现 Token 存储 (
token-storage.ts) - 实现 Token 刷新 (
token-refresh.ts) - 实现本地 OAuth 服务器 (
server.ts) - 实现浏览器工具 (
browser.ts) - 创建 OAuth 成功页面 (
public/oauth-success.html)
阶段 4:请求处理(第 8-10 天)
- 实现模型映射 (
model-map.ts) - 实现 Reasoning 配置 (
reasoning.ts) - 实现请求体转换 (
transformer.ts) - 实现 Header 生成 (
headers.ts) - 实现请求验证 (
validator.ts) - 从 GitHub 下载并内置 Codex prompts
阶段 5:响应处理(第 11-13 天)
- 实现 SSE 解析器 (
sse-parser.ts) - 实现响应格式转换 (
converter.ts) - 实现 Chat Completions 格式转换器 (
chat-completions.ts) - 实现响应处理器 (
handler.ts) - 实现流式/非流式响应处理
阶段 6:API 端点(第 14-16 天)
- 实现
/v1/chat/completions - 实现
/v1/responses - 实现
/auth/login - 实现
/health健康检查端点 - 测试所有端点
阶段 7:Docker 支持(第 17-18 天)
- 创建 Dockerfile
- 创建 docker-compose.yml
- 创建 .dockerignore
- 测试 Docker 构建
- 编写 Docker 使用文档
阶段 8:测试和优化(第 19-21 天)
- 单元测试
- 集成测试
- 性能测试
- 日志优化
- 错误处理优化
阶段 9:文档和发布(第 22-23 天)
- 编写 README
- 编写 API 文档
- 编写 Docker 文档
- 准备 NPM 发布
依赖项
生产依赖
{
"hono": "^4.10.4",
"@openauthjs/openauth": "^0.4.3",
"dotenv": "^16.4.5"
}
开发依赖
{
"@types/node": "^24.6.2",
"typescript": "^5.9.3",
"vitest": "^3.2.4",
"eslint": "^9.15.0",
"prettier": "^3.4.2",
"@typescript-eslint/eslint-plugin": "^8.15.0"
}
关键实现细节
1. Messages → Input 转换
OpenAI Chat Completions 格式:
{
"messages": [
{"role": "user", "content": "Hello"}
]
}
ChatGPT Responses API 格式:
{
"input": [
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Hello"}]}
]
}
转换逻辑:
function messagesToInput(messages: Message[]): InputItem[] {
return messages.map(msg => ({
type: "message",
role: msg.role,
content: Array.isArray(msg.content)
? msg.content.map(c => ({ type: "input_text", text: c.text }))
: [{ type: "input_text", text: msg.content }]
}));
}
2. 流式 SSE 转换
转换逻辑:
async function transformSseStream(
reader: ReadableStreamDefaultReader,
model: string
): ReadableStream {
const encoder = new TextEncoder();
return new ReadableStream({
async start(controller) {
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.substring(6));
const transformed = transformChunk(data, model);
controller.enqueue(encoder.encode(`data: ${JSON.stringify(transformed)}\n\n`));
}
}
}
controller.close();
}
});
}
3. Token 刷新时机
刷新策略:
- 提前 5 分钟刷新(
expires_at - 300000 < Date.now()) - 每次请求前检查
- 刷新失败时返回 401
- 刷新成功后更新本地存储
测试计划
单元测试
- OAuth 流程测试
- Token 存储测试
- 请求转换测试
- 响应转换测试
- SSE 解析测试
集成测试
- 完整 OAuth 流程测试
- Chat Completions 端点测试(流式/非流式)
- Responses API 端点测试(流式/非流式)
- Token 自动刷新测试
- 错误处理测试
手动测试
- 使用 curl 测试所有端点
- 使用 OpenAI SDK 测试兼容性
- 使用不同模型测试
- Docker 部署测试