简体中文 | 日本語
One conversation. Two engines. Your context.
一个从实际应用中抽取的本地 AI 会话网关:连接 Claude Code 的 stream-json 与 Codex 的 app-server,在同一界面切换引擎,并用应用自有历史保持对话上下文连续。
当前为可运行的作品展示版:包含真实 CLI 适配器、无需账号的模拟演示和自动化测试。默认是 Demo;模拟输出不是模型回答,也不代表真实引擎的性能。
需要 Node.js 22.9+。没有第三方运行时依赖,无需安装依赖。
npm start打开终端显示的 http://127.0.0.1:4317。发送一条消息,再切换引擎继续发送,观察右侧历史重建数量和事件流。刷新页面可以恢复当前会话。
npm test
npm run check:public不同引擎有不同的进程、会话和流式协议。直接切换模型按钮,不能保证新引擎获得完整上下文,也不能保证旧引擎切回来时拥有另一个引擎生成的消息。
Claudex 把 应用历史 与 引擎原生会话 分开管理:应用历史是事实来源,原生会话是可丢弃的缓存。连续使用同一引擎时复用会话;切换引擎、改变模型或发生失败后,用已完成的历史重建新会话。
| 问题 | 实现 |
|---|---|
| 两套底层协议 | Claude 常驻子进程;Codex 长驻 app-server + JSON-RPC 请求关联 |
| 引擎切回来时上下文过期 | 仅复用当前引擎的有效会话,切换时重建历史 |
| 流式消息来源混淆 | 统一 SSE 事件;每条回复独立记录 provider 与 model |
| 提前到达或串线的事件 | Codex 在 turn/start 响应前缓冲事件,按 threadId / turnId 过滤 |
| 中断后半成品污染后续对话 | 保存未完成状态,重建时只使用已完成消息 |
| 同一对话同时发送两次 | 按 chatId 加锁,重复请求返回 409 |
| 进程重启后丢失数据 | JSON 文件原子替换;持久化原生会话编号 |
| 向面试官提供低门槛演示 | 默认确定性 Mock 引擎,不需要账号和额度 |
- 安装并登录 Claude Code 和 Codex CLI,先确认各自在终端可用。
- 复制
.env.example为.env,将GATEWAY_MODE改为live。 - 若命令不在 PATH 中,设置
CLAUDE_PATH/CODEX_PATH;模型名称可通过对应环境变量指定。留空时由 CLI 默认配置决定,消息元数据会保留“未显式指定”的空值。 - 再运行
npm start。界面会显示“真实引擎”。真实请求可能消耗账号额度。
Demo 和 Live 分别存储在 .data/demo 与 .data/live。引擎工作目录为 .runtime,不会使用原应用目录。运行数据和 .env 均被 Git 忽略。
Claude 关闭内置工具、额外 MCP 和自动记忆;Codex 使用只读沙盒。这不是操作系统级隔离:Codex 只读权限仍可能读取宿主允许的文件,CLI 还可能加载用户级配置。请使用专用账号或容器体验真实引擎,不要把私有工作目录作为运行环境。 不要将该本机演示服务直接公开到互联网。
flowchart LR
UI[Browser] -->|POST /api/chat| Gateway
Gateway <-->|atomic JSON| History[Application history]
Gateway --> Context[Context / session policy]
Context --> Claude[Claude adapter]
Context --> Codex[Codex adapter]
Claude -->|NDJSON| CC[claude --print]
Codex -->|JSON-RPC| CS[codex app-server]
CC --> Events[Normalized events]
CS --> Events
Events -->|SSE| UI
src/
gateway.js # 生命周期、锁、上下文、持久化
store.js # 原子存储和会话复用策略
server.js # 本机 HTTP / SSE
providers/
claude.js # Claude stream-json 适配
codex.js # Codex 事件路由与 turn 生命周期
codex-runtime.js # 从原应用抽取的 app-server 客户端
demo.js # 明确标记的模拟引擎
public/ # 无构建步骤的展示界面
test/ # 网关、协议和 HTTP 测试
这是单进程、本机使用的参考实现,不是多租户生产服务。跨引擎共享的是显式消息历史,不能迁移隐藏推理、工具运行环境或模型内部状态。历史重建目前全量发送,无自动摘要或 token 预算管理。没有附件、身份认证、分布式锁、自动故障切换或交互审批界面。生成失败时记录失败状态并允许重试,不静默换模型。
本项目独立开发,与引擎供应商不存在官方隶属关系。