
这是什么
一条把一次编码会话拆给两个模型的桥。ChatGPT 网页版负责规划与审查,Codex 保留 shell、测试和提交。这套分工能成立,是因为两半从不共用同一条通道:Codex 用自己应用内的浏览器,把极小的 [C2C] 控制消息敲进 ChatGPT 标签页;ChatGPT 则通过一条 OAuth 保护的只读 MCP 连接,连到开发者本机上的一条回环桥,缺哪几行代码就自己拉哪几行。九个工具全是读的——workspace_info、list_directory、read_file、search_workspace、git_status、git_diff、test_status、execution_summary、execution_output——所以仓库从不上传,提示注入也无处可去,因为写工具根本不存在。对外的半边是一条 Cloudflare 隧道;唯一会出现在浏览器里的秘密,是一次性配对码。界面不是 GUI 而是 Codex Skill:用户用一句话交代要实现什么,agent 自己完成安装、配对并跑起整个循环。
谁做的这个仓库用自己的 issue 区当成服务台,由一个 AI agent 逐条回复,落款一律是「[Agent · codex-with-chatgpt]」:登记反馈、转达作者,并告诉报告者作者在校,预计 48 小时内会看到。51 次提交分在两个身份下:20 次署名「小多」(xiaoduo_@xiaoduodeMacBook-Air.local),19 次署名 XiaoDuoYa 账号,合计 39 次;另有十位贡献者,其中三位提交不止一次。从第一次提交到最后一次共十六天,仓库拿到 6,943 个星和 642 个 fork。
它是怎么搭起来的
组成 · 6每个工作区一条 Express 桥,只绑 127.0.0.1,里面装着一个 OAuth 2.1 授权服务器、一个配对码管理器、一个隧道管理器、一个只读 MCP 服务,以及每次迭代的记录。ChatGPT 那一侧什么都跑不了:它只有一个连接器和一个 Project,能碰仓库的唯一途径就是那九个读工具。写的一切——shell、测试、git——都归 Codex,而它用自己应用内的浏览器,以不到 1 KB 的控制消息驱动 ChatGPT 标签页;这也解释了为什么仓库里最大的手写文件是 CLI,第二大的是那份教 agent 怎么用它的 Skill 文档。工作区就是安全边界:一条桥只服务一个工作区,每个令牌都绑在它上面,别的工作区的令牌拿到的是 403。持久状态——配对、会话、隧道、偏好——都放在系统应用状态目录,从不进项目;项目里最多只有一个可选的 .c2c.json 和一个 .c2cignore。
- src/cli/
index.ts有 48,389 字节,是仓库里最大的手写文件:c2c的全部命令——setup、doctor、pair、unpair、status、logs、session、prefs、record、sandbox-allow、update-check、workspace与tunnel——凡是 Skill 要解析的都提供--json。- skill/SKILL.md
- 42,600 字节、634 行,README 直接把它称作真正的 UX 层:golden rules、应用内浏览器的操作纪律、带自动与手把手两条路的首次配置、编码循环,以及一张从症状到命令的恢复表。
- src/mcp/ 与 src/bridge/
server.ts(16,351 字节)在无状态 Streamable HTTP 传输上注册九个只读工具,每个请求一个新实例。桥本身是server.ts(8,529)加runtime.ts(3,644):只监听回环、端口自动回退、运行时状态,以及一个本地管理 API。- src/auth/ 与 src/pairing/
- 一个 OAuth 2.1 授权服务器——
oauth.ts14,340、store.ts8,505、middleware.ts2,271、配对页html.ts695——带 PKCE S256、动态客户端注册、refresh 轮换与吊销;旁边是管一次性配对码的manager.ts(4,965)。 - src/workspace/、src/execution/ 与 src/session/
- 读取与收敛:
manager.ts12,784、git.ts10,319、search.ts6,307、ignore.ts2,416。接着是审查闭环的记录——output.ts3,493、sanitize.ts2,359、records.ts2,090——以及session/state.ts(9,152),它保存对话形态与检查点。 - src/tunnel/、docs/ 与 tests/
TunnelProvider接口后面是八个隧道文件、31 KB,从 Cloudflare Quick 到按工作区配置的固定隧道。docs/下四份文档 23 KB;tests/下十八个测试文件 107 KB,最大的是 16,289 字节的 MCP 集成测试。
取舍,以及它替代了什么
执行权继续留在 Codex,而不是另写一套 harness 替代 在桥里自己实现编码循环
docs/architecture.md把它列成第一条原则并给了理由:「ChatGPT thinks. Codex works. The bridge never re-implements a coding harness.」桥做的因此只剩传输、认证和读取。只读 MCP,完全不设写入面 替代 加上由审批拦一道的写入或 shell 工具
docs/security.md写的不是意图而是后果:「Write files, delete files, run shell commands, commit, install packages — these tools do not exist on the server, so no prompt injection, scope bug, or UI confusion can enable them.」README 补了一句:知道公网地址不等于有权限,因为每个请求仍然要令牌。两条通道互不承载对方的内容 替代 把 diff、日志和文件内容粘进对话里
docs/protocol.md:「Never mix the two: control messages carry state, never content」,控制消息限制在 1 KB 以内的键值头与分节。数据面只拉不推,于是 ChatGPT 取到的正好是它要的那几行,仓库也从不被上传。用本地检查点,而不是新增一个协议状态 替代 一个 resume 状态,或者用日志重放上一段对话
协议里写着「There is no
STATE: RESUME」:重启靠读检查点,对话没了就发由检查点拼出的HANDOFF,「never from logs」。docs/security.md把这条边界记成了规矩:不新增协议状态、不粘日志、不重新配对。默认 Cloudflare Quick Tunnel,固定域名按需选择 替代 要求用户先有一个域名才能用起来
docs/architecture.md:默认地址每次启动都会变,所以c2c doctor负责重启它并让 Skill 删掉该工作区的连接器再重建;固定域名是每个工作区选一次的事,cloudflared tunnel login是唯一多出来的用户动作——而且固定隧道配置失败时会退回 Quick。
依据docs/architecture.md、docs/protocol.md、docs/security.md、skill/SKILL.md、README.md、src/session/state.ts、src/pairing/manager.ts、src/auth/store.ts、src/config/ui-prefs.ts、src/tunnel/cloudflared.ts, ,以及 70 个文件的完整树与体积。
制作过程
6 个阶段- 01
十六天、四个版本,和一个替作者回 issue 的 agent
第一次提交是 2026-08-28 的
Initial V1: C2C Bridge with read-only MCP, OAuth+pairing, tunnel, CLI and Skill,最后一次是 2026-09-13 的Accept leftover -w on machine-wide c2c commands.:51 次提交,8 月 35 次、9 月 16 次;四个 release 全挤在头十三天里——2026-08-30 的v0.1.0 — First Public Release、第二天的v0.1.1 — Setup Choice, Checkpoints & Sanitized Output、2026-09-04 的v0.1.2 — Structured MCP, Safer Tunnels & Windows Polish,以及 2026-09-11 的v0.1.3 — Connector Recovery, Hidden git_status & HTTP/2。动机只用一句话就写清了:付过费的 ChatGPT 网页版额度大量闲置,而编码 agent 却在烧紧张的 API 额度做规划和 Review,于是「思考」交给订阅、「执行」留在本机,不用 API Key,也不搞逆向代理。这十六天周围是 6,943 个星、642 个 fork 和 34 个未关的 issue。仓库还自己管自己:几乎每条 issue 都会收到一条落款[Agent · codex-with-chatgpt]的回复,说反馈已登记转达,作者在校,预计 48 小时内看到。侦察报告完整读了最新的三十个 issue 与 pull request,读下来更像一条客服队列——而最难的部分居然是认证,不是模型。 - 02
两个模型、两条通道,以及绝不混用的规矩
这套分工被写成了协议。控制消息以
[C2C]开头,带键值头和分节,长度必须压在 1 KB 以内:Codex 发INIT要方案,ChatGPT 回PLAN,内容包含理由、动作、可能涉及的文件、测试与成功标准;Codex 再发EXECUTED带上本次迭代的元数据,然后是REVIEW、DONE或BLOCKED。docs/protocol.md把边界说了两遍——「Never mix the two: control messages carry state, never content」,以及「No diffs, no logs, no file bodies」——而 Skill 的第一条 golden rule 对 agent 再重复一次:绝不把文件内容、diff 或日志粘进 ChatGPT,它要看就走 MCP。这就是数据面:九个只读工具跑在无状态的 Streamable HTTP 传输上,而审查环节正建立在它们之上——EXECUTED之后,ChatGPT 亲自去看真实的git diff和记录下来的测试状态,因为 boot prompt 里写着「Do not assume an implementation succeeded just because Codex says so」。架构文档解释了这座桥为什么能保持这么小:「The bridge never re-implements a coding harness.」 - 03
一次会话怎么活下来:检查点、交接与 Project
连续性被刻意做成本地的。
c2c session按工作区在系统应用状态目录里存一个 JSON,里面是protocolState、waitingFor、原始目标、已完成子任务、已知问题与下一步预期,每个字段都有上限——500、800、800、400 字符。协议里没有 resume 状态,而且明说:「There is noSTATE: RESUME。」Codex 中途重启就读检查点;如果连那个对话都没了,就发HANDOFF——一份由检查点拼出来的简报,「never from logs」——新对话再通过 MCP 把代码读回来。对话有两种形态:每个工作区一条长对话,或者每个工作区一个 ChatGPT Project、每次新对话都开在这个合集里;后者把长期身份放进项目指令,而且只写连接器名字,绝不写会变的地址。信任顺序也写出来了:连接器里的当前代码、本次任务的交接简报、项目指令、项目记忆,过期的记忆排最后。Skill 把同一套纪律带进浏览器:等回复是每 20 到 30 秒做一次便宜的 DOM 检查,而不是waitFor五分钟;引入这条规则的提交标题就叫Poll ChatGPT replies in short DOM checks so a 5-minute browser wait is not treated as failure。 - 04
额度这件事,藏在哪些细节里
这个项目本来就是被额度不对称逼出来的,所以额度不在设置页里,而在细节里。循环有
maxIterations上限,默认 12,写在可选的.c2c.json里;控制消息压在 1 KB 以下;Skill 在每个流程开头跑c2c update-check和c2c sandbox-allow,两条都带缓存,没有变化就只字不提。配对码有效期五分钟、允许五次尝试、每个 IP 每分钟十次、用后即毁;OAuth 访问令牌一小时,refresh 令牌三十天且每次使用都轮换,权限被拆成workspace.read、workspace.search、git.read、execution.read和offline_access。公网地址是 Cloudflare Quick Tunnel,它有 45 秒时间变成 ready;网络把 UDP 挡掉时,README 给的出口是一个环境变量C2C_TUNNEL_PROTOCOL=http2,而不是让你去架代理。最能说明这是「额度问题」的证据反而在 issue 区:pull request 453 上的审查机器人留下一句「You have reached your Codex usage limits for code reviews」;而一位外部贡献者的 pull request 441 提出了缺的那块——c2c quota与c2c route,去读登录账号的剩余额度,低于 20% 就把规划切给 ChatGPT,执行仍归 Codex。那条至今还开着。 - 05
作者改了什么,以及这批提交是谁写的
提交史同时也是「哪个 agent 写了它」的记录。51 次提交里有 36 次带至少一条共同作者尾注,这 36 次共 46 行;其中 28 次署名 Cursor(
cursoragent@cursor.com),连第一次提交也在内。其余是人:moonjun 与 pi 各三次、AtlaxTech 两次,再加十位各一次的名字,修-w的 hbhuyt 就在其中。改动清单读起来像一份「真实用户踩坏了什么」:Whitelist the C2C state directory in the Codex sandbox on macOS and Windows so new chats stop needing elevation、Keep the ChatGPT in-app tab visible and reused so first-time setup stops stalling、Isolate one ChatGPT connector per workspace so two projects can stay connected at once、Stop opening ChatGPT or sending C2C until doctor reports a healthy local bridge、Unify git_diff sensitive-file rules with MCP reads so secrets cannot leak through renames、Offer an optional Cloudflare named hostname so ChatGPT connectors survive restarts、Remember ChatGPT developer mode and let users pick auto or guided setup,以及 2026-09-02 连着三次不让桥、临时隧道和固定隧道的控制台窗口在 Windows 上闪出来。首次配置只问用户一次:自动配置(可能连续失败两次再降级)还是引导手把手配置(提示里说大约三分钟)。更新也是自助的:每天检查一次,然后git pull --ff-only、corepack pnpm install && corepack pnpm build、重装 Skill、重启桥,接着继续原来那件事。 - 06
社区报了什么,以及那个其实是个参数位的 bug
反馈集中在不是代码的那部分。macOS 上是配对成功之后卡住:ChatGPT Desktop 的回环回调走不完,持久化的令牌数一直是 0,再点一次 Connect 会得到「This authorization request has expired」,因为第一次点击已经把待处理的请求消耗掉了(457)。别处还有:连接器一直转圈(450);本地检查全绿但每次 MCP 调用都返回内部错误(449);桥是健康的,Windows 上走代理的出口能到
cloudflared却到不了chatgpt.com和 Cloudflare 的校验域名,于是照旧报fetch failed(459);新分到的*.trycloudflare.com域名在那 45 秒窗口里解析不出来(437)。最长的一条串是「每天都要重新授权」,而答案就在代码里:临时隧道会随终端一起死掉,想扛住重启就得有自己的域名(447)。VS Code 里根本没有应用内浏览器,于是那条禁止第三方浏览器的 golden rule 直接把首次配置堵住,直到用户坚持(456)。最好的一条是从误诊开始的:一位用 Clash 的用户报告「更新 Codex with ChatGPT」失败,模型写了一份把 Node 和沙箱都怪上的分析,真正的原因是update-check不认 Skill 被要求到处都带的-w。作者因此重写了 Skill,写明哪些命令要带-w、哪些不要带,让整机级命令接受并忽略它,给了报告者一个共同作者署名,还在串里道了谢。
相关档案
全部档案 →第 070 号
OpenChatCut
一个本地优先的视频剪辑器,剪辑方式是跟它说话:内置 agent 与外部 Codex、Claude Code 会话调用的是界面自己在用的同一套剪辑工具,于是每一处改动都落在一条真实的多轨时间线上——是片段、转场、字幕、特效或音频,仍然能拖、能撤销、能导出。工程与素材留在本机,预览与最终渲染都出自 Remotion。
第 081 号
pgbot
一个静态 Go 二进制,只读地连上 PostgreSQL,读服务器自己的统计视图,打出一份以 finding 为先的体检报告——因为每次运行都会在本地存一份基线,它还能说出「与上次相比变了什么」;同一批确定性 finding 通过 MCP 交给 AI agent,而可选的 AI 层只被允许解释它们。
第 077 号
Lody
一个让团队共用他们本来就在跑的编码 agent 的工作台:连上一台机器,把 Claude Code、Codex、Kimi 或任何其他说同一套协议的 agent 接进来,然后从桌面端、手机、网页或终端派活;会话之间可以互相派活,而代码始终留在机器主人自己连上来的那台机器上。