A1|产品家族:六个入口,不是六套 Agent
先别按安装包数仓库
打开 chatgpt.com/codex、VS Code 扩展、终端里的 codex、再 pip install openai-codex,像是六份产品。仓库里却没有 ide/、desktop/、cloud-runtime/。
用户看见六个入口;工程师只该认 两套运行时、一种账号胶水。本地入口(TUI、exec、IDE、桌面、SDK)共用本仓库 harness;chatgpt.com 上的云端任务是另一套闭源 runtime。粘在一起的是 ChatGPT 账号和额度,不是同一份 main.rs。
下面按源码落点对照六个入口,并分清:API Key 能干什么、本地和云端为什么不能互相假装。
1. 六个入口各自是什么,源码在哪
| 入口 | 人怎么用 | 这个仓库里实际是什么 |
|---|---|---|
| CLI TUI | 敲 codex | 交互客户端。无 subcommand 就进 TUI(cli/src/main.rs:117-145)。TUI 自己嵌一份 in-process app-server(tui/src/lib.rs:271-294),不在 widget 里实现 agent。 |
codex exec | CI、脚本、--json | 同一条 turn,无头。默认 AskForApproval::Never(exec/src/lib.rs:563-568),stdout 只许最终答案或 JSONL(exec/src/lib.rs:1-6)。 |
| IDE 扩展 | VS Code / Cursor / Windsurf 等 | UI 不在本仓库。 扩展是 app-server 的 JSON-RPC 客户端。协议默认 SessionSource 甚至是 VSCode(protocol/src/protocol.rs:2765-2769),说明 IDE 从一开始就是一等表面,不是 CLI 的后补。 |
| 桌面 App | codex app 或官网安装 | 本仓库只有「打开或拉安装器」(cli/src/app_cmd.rs:15-24,cli/src/desktop_app/mod.rs:6-12)。原生壳闭源,对话仍走 app-server。 |
| chatgpt.com/codex | 浏览器里丢任务 | 云端沙箱和编排 不在本仓库。这里只有提交/查看/把 diff 拉回本地的客户端。 |
| SDK | TS / Python 嵌进你的程序 | 都不是第二套循环。TypeScript spawn codex exec(sdk/typescript/src/codex.ts:16-17,exec.ts)。Python 拉起 codex app-server --listen stdio://(sdk/python/src/openai_codex/client.py:215-256)。 |
模型怎么被调用不在 tui/src/chatwidget.rs。入口变的是审批 UX 和 I/O 契约,不是 Turn 循环;「前端只适配」见 E1。
没有这一层会怎样?每个表面复制一份工具列表和一份历史,协议一变四处一起炸。本仓库用 app-server 把 IDE、桌面、Python SDK、连 TUI 自己都收成同一类客户端,就是为了阻止这件事。
2. 同一 ChatGPT 账号:产品胶水,不是登录彩蛋
根 README 建议「Sign in with ChatGPT」,把 Codex 算进 Plus / Pro / Business / Edu / Enterprise 的额度。这不是营销口吻在源码里的对应物,是 AuthMode 上的一条硬分叉。
protocol/src/auth.rs:9-63 把登录方式拆开。和「我是 ChatGPT 用户」真正相关的是:
has_chatgpt_account():Chatgpt/ChatgptAuthTokens/PersonalAccessToken为真,API Key 和 Bedrock 为假。uses_codex_backend():走 Codex 后端(ChatGPT 账号、Agent Identity、PAT、headers)为真;ApiKey/ Bedrock 为假。
套餐类型在同文件的 PlanType / KnownPlan(auth.rs:66-110):free、plus、pro、team、enterprise、edu……额度、功能开关、模型目录都挂在这条身份上,而不是挂在「你用的是 TUI 还是 IDE」。
所以六个本地入口能共用历史、共用 CODEX_HOME、共用额度,前提是它们读同一份登录态。TUI 和 codex exec 不是两个产品账号。云端任务也认这套 ChatGPT 后端:拉任务前会检查 uses_codex_backend() 和 account id(chatgpt/src/chatgpt_client.rs:68-81)。
没有「一套账号」会怎样?IDE 里开的 thread,终端 resume 不到;云端做完的 diff,本地 codex apply 没有身份去取。产品家族会裂成六个互不相认的玩具。
企业还可以反过来禁某一种登录:cli/src/login.rs:40-45 准备了「只许 ChatGPT」和「只许 API Key」两套拒绝文案。那是托管约束,不改变「账号是胶水」这件事。
3. API Key 路径:能跑循环,不是完整产品
codex login --api-key 能让本机 turn 跑起来。很多人据此以为「差的只是账单方式」。源码里差的是 后端,不是发票。
AuthMode::ApiKey 的 uses_codex_backend() 为假。后面至少四扇门会关上:
模型目录变窄。 ModelPreset::filter_by_auth(protocol/src/openai_models.rs:885-893):ChatGPT 模式展示全部 preset;否则只留 supported_in_api 的模型。订阅页上的 Codex 专用档,API Key 路径根本看不见。
ChatGPT Apps 被清空。 apps_route_available 要求 uses_codex_backend()(core-plugins/src/app_mcp_routing.rs:6-18)。API Key 登录时,plugin 带来的 App 声明直接 apps.clear()。测试钉死了这一点(app_mcp_routing_tests.rs:34-37)。
远程插件目录进不去。 ensure_chatgpt_auth(core-plugins/src/remote.rs:2297-2303)在没有 Codex 后端身份时返回 UnsupportedAuthMode。市场搜索、远程 skill 详情,不是「没配 marketplace」,是鉴权模式不对。
云端任务客户端直接拒绝。 chatgpt_get_request 在 API Key 下会 ensure 失败(chatgpt/src/chatgpt_client.rs:74-77)。codex cloud / codex apply 这条线不是「用 API 另打一个 URL」,它就是 ChatGPT 后端。
另外还有按 套餐 而不是按 Key 开的实验能力。例如 experimental context 的资格检查:ChatGPT + Plus/Pro 过,Free/Enterprise 不过,API Key 即使声称 Pro 也不过(core/src/session/token_budget.rs:233-246)。手机遥控桌面 host(remote control)同样要求 ChatGPT 登录,测试名就叫 connect_remote_control_websocket_requires_chatgpt_auth。
API Key 仍然有合法位置:自动化、OSS provider、Bedrock、不想把订阅绑在开发机上。只是不要把它读成「完整 Codex 的命令行版」。缺的不是 TUI 皮肤,缺的是 Codex 后端上的那一圈产品:Apps、远程市场、云端任务、部分模型和实验开关。
本地模型走同一条分叉。配置里的 oss_provider(例如 "ollama" / "lmstudio")和自定义 model_providers 只换 本机循环打到哪(ConfigToml,config/src/config_toml.rs)。uses_codex_backend() 仍为假。远程 compact v2 是 ProviderCapabilities.remote_compaction:OpenAI / 部分 Azure 声明 V2,普通自定义 URL 默认 Unsupported(model-provider/src/provider.rs)。不要在 Ollama 上假装有 Apps、云端任务、远程插件市场或远程 compact。能力是 provider 声明的,不是把 ChatGPT 后端抄进 stub。安装步骤不在这本电子书里。
没有这层分叉会怎样?要么 API Key 用户撞上一堆「按钮在、请求 401」的假功能;要么所有 Codex 后端能力被偷偷塞进 Responses API,账号体系和计量全部打穿。代码选择显式关门,比做半套兼容更干净。
4. 本地任务 vs 云端任务:两个 runtime,一个 apply 缝
本地任务:agent 在你的机器(或本机 exec-server)上跑,沙箱是 Seatbelt / bwrap / Windows token,历史写进 CODEX_HOME 的 rollout。TUI、exec、IDE、桌面、SDK 全是这条。
云端任务:agent 在 OpenAI 的隔离环境里跑,对着 GitHub 仓库和云端沙箱。浏览器入口是 chatgpt.com/codex;CLI 侧是实验性 codex cloud(cli/src/main.rs:225-227,子命令在 cloud-tasks/src/cli.rs:16-27:Exec / Status / List / Apply / Diff),以及把已完成任务的 diff 打进当前工作区的 codex apply(chatgpt/src/apply_command.rs:14-36)。
注意 apply 做什么、不做什么。它 get_task 拿到 output_diff,再 git apply。它 不在本地重放 那次云端 turn,也不把云端 sandbox 政策搬进 AskForApproval。缝合点是 git diff,不是 Session。
所以:
- 本地能 interrupt、steer、按工具审批;云端那次运行你不在回路里(除非产品另开的远程查看,那是 E3)。
- 本地 rollout 不能当云端任务的真相源;云端任务 id 也不能
codex resume成一条本机 thread。 - 额度可以共享(同一 ChatGPT 账号),实现不能共享。本仓库没有云端 kernel。
没有这条缝会怎样?不是「少一个按钮」,而是两种隔离模型混在一起:云端以为有完整 VM,本地以为用户会点 Allow。apply 只搬 diff,就是拒绝混写。
codex exec --ephemeral(exec/src/cli.rs:35-37)也容易和云端搞混。它只是本机不落盘 session,仍然是本地 runtime,不是云。
5. 两个 SDK 为什么连入口都不一样
同一「把 Codex 嵌进我的程序」,TypeScript 和 Python 选了产品家族里的 两个不同表面。
- TypeScript:
CodexExecspawn CLI,走codex exec那条无头 I/O(sdk/typescript/src/codex.ts:11-17)。适合脚本、一次性 turn、跟 CI 同构。 - Python:进程参数写死
app-server --listen stdio://(client.py:256),之后打 v2 JSON-RPC:thread/start、turn/start、审批回调。适合要订阅事件、要登录、要 goal 的长生命周期宿主。
没有「一个 SDK 抽象」会怎样?看起来更整齐,但会逼 IDE 和 CI 用同一套 stdio 契约。家族的真实形状是:exec 是管道,app-server 是插座。 SDK 各自插在已经存在的表面上,而不是再发明第七个入口。
6. 结语:带走一句话
入口有六个,运行时只有两套:本仓库 harness 和闭源云端;ChatGPT 账号是胶水,API Key 能跑循环但进不了 Codex 后端产品圈。