拆解 ChatGPT Linux 桌面应用

拆解 ChatGPT Linux 桌面应用:扒开壳,里面是一个叫 Codex 的引擎

本文基于对 OpenAI 官方 deb 包(chatgpt 26.924.22138,Linux x64)的静态拆解,全程未逆向混淆代码,所有结论均来自包内明文元数据与可观测的进程/文件结构。

引子:一个叫 codex-launcher 的"ChatGPT"

一切从命令行开始。装完 OpenAI 官方的 ChatGPT deb 包后,/usr/bin/chatgpt 其实是个两行的 shell 脚本,而它的文件名有点露馅:

$ file /usr/bin/chatgpt
/usr/bin/chatgpt: symbolic link to ../lib/chatgpt/codex-launcher

启动器叫 codex-launcher。顺着这条线往下挖,会发现这个 1.5GB 的 "ChatGPT" 应用,内部身份完全是另一个人。app.asar 里的 package.json:

{
  "name": "openai-codex-electron",
  "productName": "Codex",
  "description": "Codex",
  "author": "OpenAI",
  "main": ".vite/build/early-bootstrap.js"
}

更实锤的是元数据文件 owl-electron-app.json:

{
  "packagedFrom": "/home/runner/work/openai/openai/codex/codex-apps/electron/out/ChatGPT-linux-x64",
  "runtimeName": "owl"
}

构建路径指向 CI 上的 openai/codex 单仓库,目录是 codex/codex-apps/electron,产物名 ChatGPT-linux-x64。也就是说:Linux 版 ChatGPT 桌面应用与 Codex 桌面应用出自同一套代码库,"ChatGPT" 只是外层品牌。壳层代号 Owl(owl-app.ini 里 [Owl] UserDataDirectoryName=Codex,连 Chromium 用户数据目录都叫 ~/.config/Codex)。

三条独立的版本线先记下,后文会反复出现:应用 26.924.22138、Electron 42.3.0、内置引擎 codex-cli 0.158.0-alpha.2.1。

总体架构

┌──────────────────────────────────────────────────────────────┐
│            deb: chatgpt 26.924.22138  (/usr/lib/chatgpt)     │
│                                                              │
│   Electron 42.3.0 壳(代号 Owl)                              │
│   ┌──────────────────────────┐   ┌──────────────────────────┐│
│   │ 主进程 .vite/build/*      │   │ 渲染窗口 + 内嵌浏览器侧栏  ││
│   │ main 4.1MB + 服务级 chunks│◄─►│ browser-page-preload     ││
│   │ app-protocol / policy     │IPC│ sandbox-preload          ││
│   └────────┬─────────────────┘   └──────────────────────────┘│
│            │ spawn + 原生管道            账号 / 云流量          │
│   ┌────────▼─────────────────┐        auth.openai.com        │
│   │ codex(272MB Rust 静态)  │        wss://codex-cloud-     │
│   │ app-server 守护进程       │        backend.chatgpt.com    │
│   └────────┬─────────────────┘                               │
│            │ 共享 ~/.codex                                    │
│   ┌────────▼───────────────────────────────────────────────┐ │
│   │ 组件: cua_node · code-mode-host · tectonic · rg         │ │
│   │ 插件: codex-app-tools · browser · chrome · latex · …    │ │
│   └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

主进程经 Vite + rolldown 打包(main、app-protocol、policy、service、startup-requirements、shell-env 等按职责拆分的 chunk),构建链是 electron-forge,原生模块只有两个小文件:hid-topology-watcher.node(外设监控)和 remote-control-device-key.node(远程控制设备密钥)。

引擎:272MB 的 Rust 二进制

resources/codex 是一个静态链接的 ELF,即 Codex CLI 本体(codex-cli 0.158.0-alpha.2.1)。跑一下 --help,会发现一半子命令都是为"被宿主应用驱动"准备的:

  • app-server(experimental)——Run the app server or related tooling,下辖 daemon / proxy / generate-ts;
  • agents——"Browse all agent sessions on the shared local app-server daemon";
  • remote-control、queue、resume、apply……

关键设计在这里:桌面应用不把 codex 当一次性子进程,而是作为常驻的 app-server 守护进程。主进程代码里 codexHome 出现 248 次,codexCliPath、codex-app-server、codex-renderer-window: 等协议前缀密集出现;~/.codex/ipc/ipc.sock 就是那个 Unix 控制 socket——app-server proxy 子命令的注释直接写着 "Proxy stdio bytes to the running app-server control socket"。

换句话说,CLI 和桌面应用是同一个 daemon 的两个客户端。终端里跑过的会话,桌面应用里都在;反过来也一样。

组件弹药库:resources/ 下约 600MB 的"挂件"

组件 体积 说明
codex 272MB Rust Agent 引擎,app-server 模式
codex-code-mode-host 71MB "code mode" 执行宿主(静态 ELF)
cua_node/ 209MB 定制 Node 24.21.0-cua.1 + node_repl 二进制
tectonic/ 26MB LaTeX 引擎
rg — ripgrep
plugins/openai-bundled/ 23MB 七个内置插件
native/*.node — HID 拓扑监听、远程控制设备密钥

cua_node 值得单独说。manifest.json 声明这是 OpenAI 自建的 Node 分支 24.21.0-cua.1,专为 CUA(Computer Use Agent) 提供代码执行环境:lib/node_modules 里带 playwright(浏览器控制)、sharp、@oai 模块,另附一个独立的 node_repl 静态二进制作为 agent 写代码、跑代码的 REPL。主进程里有对应的 computer-use-native-pipe-server,审批消息走 computer-use-approval: 前缀的原生管道,单条消息上限 8MB——鼠标点击屏幕之前,先过一道人肉闸门。

七个内置插件:codex-app-tools、browser、chrome(自带 extension-host 扩展宿主)、deep-research、visualize、unified-computer-use、latex。skills/.curated 里有两个内置技能:onboard-new-user 和 hatch-pet——后文有彩蛋。

可下载的"主运行时"

应用跑文档类能力时会按需拉取 codex-primary-runtime 到 ~/.cache/codex-runtimes/,runtime.json 写得明明白白:

{
  "bundleVersion": "26.904.11930",
  "artifactToolVersion": "2.8.59",
  "libreOfficeVersion": "25.2-headless-codex.1",
  "nativeDependencies": ["libheif", "jxrlib", "libreoffice-headless", "poppler", "git"],
  "nodeVersion": "v24.19.0",
  "pythonVersion": "3.12.14"
}

LibreOffice headless + poppler,这就是 documents/visualize 插件处理 docx/xlsx/slides/PDF 的底气——整套文档工具链独立于应用本体,按 bundle 版本化管理,坏了可以单独重装。

数据层:CLI 和 GUI 共用 ~/.codex

这是整个拆解里最能说明产品意图的证据。桌面应用没有发明私有数据格式,直接把 Codex CLI 的 home 当作唯一数据层:

~/.codex/
├── config.toml        # CLI 配置 + [desktop] 段(GUI 设置写回这里)
├── auth.json          # 登录凭据
├── ipc/ipc.sock       # app-server Unix 控制 socket
├── sessions/  +  session_index.jsonl
├── logs_2.sqlite   goals_1.sqlite   memories_1.sqlite   queue_1.sqlite
├── skills/  plugins/  pets/  node_repl/  browser/  computer-use/
└── dictation-history/  shell_snapshots/

config.toml 的 [desktop] 段(followUpQueueMode、conversationDetailMode、open-in-target-preferences)就是 GUI 偏好设置的落点;[marketplaces] 段注册内置插件源;记忆、目标、跟进队列各自一张 SQLite 表。另一层 ~/.config/Codex 则是标准 Chromium profile(SingletonLock、Local State、browser-sidebar-page-states.json……)。

由此带来的用户体验是:你在终端用 codex,和桌面应用看到的是同一份会话、技能与插件,codex resume 随时接管桌面会话。

顺带一个安全观察:config.toml 支持给自定义 model provider 写 experimental_bearer_token,是明文落盘(0600 权限)。BYOK 用户注意别把 ~/.codex 扫进云备份或同步盘。

两条控制通道

整个应用的骨架,是两条方向相反的通道:

① GUI → Codex:Electron 主进程拉起 app-server daemon,线程/回合/审批走 Unix socket 上的 app-server 协议。协议是版本化的(如 codexAppsMcp20260728),而且提供 generate-ts 子命令直接生成协议的 TypeScript 绑定——官方鼓励第三方宿主接入。

② Codex → GUI:内置插件 codex-app-tools(v0.1.5,自述 "Exposes Codex desktop app tools through one local MCP server")把应用能力注册成 MCP 工具:

"tools": {
  "automation_update":      { "approval_mode": "prompt" },
  "create_thread":          { "approval_mode": "prompt" },
  "send_message_to_thread": { "approval_mode": "prompt" },
  "fork_thread":            { "approval_mode": "prompt" },
  "handoff_thread":         { "approval_mode": "prompt" }
}

通过 CODEX_APP_TOOLS_PIPE_PATH 原生管道回连应用,全部默认人工审批。

两条通道合起来是个闭环:agent 干活干到一半,可以自己"开个新会话"、"把任务移交给另一个线程"、甚至驱动 GUI 自动化——桌面应用自己,也是 agent 的一个 MCP 工具。

云侧

  • wss://codex-cloud-backend.chatgpt.com/:WebSocket 同步线程状态;
  • <codex_delegation> 标签:本地与 chatgpt.com/codex 云端任务互相委托/移交;
  • 深链与装机引导:chatgpt.com/codex、/codex/deeplink、/codex/desktop-session、/codex/install.sh|.ps1;
  • 账号与支付:auth.openai.com、api.openai.com/{auth,mfa,profile}、pay.openai.com;
  • 实验与灰度:Statsig 全家桶(ab.chatgpt.com、api.oaistatsig.com),状态缓存在 ~/.config/Codex/statsig-state.json。

权限与沙箱

  • MCP 工具默认 default_tools_approval_mode: "approve",危险工具(如自动化)单列为 prompt;
  • Computer Use 有独立审批流(computer-use-approval: 原生管道协议);
  • 引擎自带 sandbox 子命令,命令执行在平台沙箱内进行;
  • 渲染层 preload 分沙箱化(sandbox-preload.js / browser-page-preload.js),本地页面走自定义 app-protocol 协议。

结语:为什么这个设计值得琢磨

"把 Agent 引擎嵌进应用"是常见做法,OpenAI 选了另一条路:引擎做成系统级常驻服务,应用只是它的一个客户端。三个收益:

  1. CLI、桌面、未来的 IDE 插件天然共享会话与凭据,"在哪儿开始、在哪儿继续"不再重要;
  2. Rust 引擎与 Electron 壳彻底解耦——应用 26.924.x、引擎 0.158.x-alpha、运行时 bundle 26.904.x,三条版本线独立演进,互不拖累;
  3. 审批策略与安全模型收敛在 daemon 一层,桌面壳再怎么换皮,闸门不动。

代价也直白:组件膨胀(1.5GB,其中引擎、CUA、文档运行时合计约 600MB),以及桌面与 CLI 之间的兼容只能靠 bundle 协议与版本协商兜底。

最后留个彩蛋:翻 ~/.codex 时看到 pets/ 目录,对应内置技能 hatch-pet——这个 1.5GB 的应用里,还养着一只电子宠物。

posted @ 2026-09-28 13:22  悠哉大斌  阅读(20)  评论(0)    收藏  举报