日常使用 Codex 时,我遇到了一个需求:保留官方账号登录的桌面版,同时再开一个接入第三方 API 的 Codex,两边的配置、登录凭据和任务记录分别保存。
一开始想到的是复制应用、改个名字。但实际排查后发现,关键在于启动时指定独立的数据目录。本文记录一次 macOS 上的实际配置过程,包括启动入口、API 配置、验证和错误排查。
验证环境:macOS,应用版本
26.908.40834,本机应用路径为/Applications/ChatGPT.app,使用其中的 Codex 功能。本文方案依赖该版本的桌面启动实现,后续版本升级后建议重新验证。第三方接口仅作为配置示例,不代表官方支持或服务推荐。
一、最终实现了什么
配置完成后,保留原来的官方应用,新增一个"Codex 代理版"启动入口。
| 项目 | 官方版 | 代理版 |
|---|---|---|
| 启动入口 | 原来的 ChatGPT/Codex 应用 | Codex 代理版.app |
| 模型认证 | 原有官方账号登录 | 第三方 API Key |
| Codex 配置与本地状态 | 原来的 ~/.codex |
~/.codex-proxy |
| 桌面应用数据 | 原来的默认目录 | ~/Library/Application Support/Codex-Proxy |
| 程序文件 | 已安装的官方应用 | 复用同一份官方应用 |
这里创建的是两个独立运行的实例和两个启动入口,程序本体仍然只有一份。因此无需复制整个应用,也无需修改官方应用的签名或内部代码。
这个方案隔离的是本地配置、凭据和应用状态。两个实例仍然运行在同一个 macOS 用户下,可以访问相同的项目文件。如果它们同时修改同一个仓库,仍可能产生文件冲突;需要并行开发时,可以分别使用独立工作目录或 Git worktree。
二、为什么只复制 App 或传一个参数不够
Codex 的配置和本地状态默认保存在 ~/.codex。如果两个实例都使用这个目录,即使应用名称不同,也可能读取同一份模型配置和登录信息。
本次排查还遇到了一个旧启动脚本,它只传了:
bash
--user-data-dir="$HOME/.codex-proxy"
这个参数没有明确隔离 Codex 自身的配置目录。而且在本次验证版本中,桌面程序会在启动时设置自己的 Electron userData 路径,因此不能只靠这个参数判断隔离成功。
实际使用的关键设置是:
| 设置 | 作用 |
|---|---|
CODEX_HOME |
指定 Codex 配置、文件凭据和本地状态目录 |
CODEX_ELECTRON_USER_DATA_PATH |
指定当前版本的桌面应用数据目录 |
open -n |
请求启动一个新的应用实例 |
--user-data-dir |
与上述桌面数据目录保持一致的启动参数 |
官方文档说明了 CODEX_HOME 的作用。CODEX_ELECTRON_USER_DATA_PATH 则是本次检查已安装应用代码时确认的实现入口,不应当把它视为永久稳定的公开接口。
在当前版本中,还能看到程序在显式指定桌面数据目录时保留传入的 CODEX_HOME,避免加载登录 Shell 环境后丢失这个设置。
三、创建独立的代理配置目录
以下命令以首次配置为例。如果目录中已经有配置,先备份,并按现有内容合并,不要直接覆盖。
bash
mkdir -p "$HOME/.codex-proxy"
mkdir -p "$HOME/Library/Application Support/Codex-Proxy"
chmod 700 "$HOME/.codex-proxy"
chmod 700 "$HOME/Library/Application Support/Codex-Proxy"
代理版的配置文件为:
text
~/.codex-proxy/config.toml
首次创建可以执行:
bash
cat > "$HOME/.codex-proxy/config.toml" <<'EOF'
model_provider = "aicoding"
model = "gpt-5.5"
model_reasoning_effort = "high"
cli_auth_credentials_store = "file"
[model_providers.aicoding]
name = "AI Proxy"
base_url = "https://aicoding.com/v1"
wire_api = "responses"
requires_openai_auth = true
EOF
chmod 600 "$HOME/.codex-proxy/config.toml"
这里有几个容易混淆的地方:
model_provider必须与下面[model_providers.aicoding]的标识一致。base_url是 API 服务地址,本例最终请求的是/v1/responses。wire_api = "responses"要求服务商兼容 Responses API,不能仅凭"兼容 OpenAI"几个字判断兼容性。cli_auth_credentials_store = "file"明确使用文件凭据,代理 Key 保存在独立目录内。requires_openai_auth = true沿用服务商的认证配置,在本例中使用auth.json中的 API Key,不需要复制官方账号的登录令牌。
模型名称应按自己的服务商和 Key 权限设置。本次最终使用 gpt-5.5,原因是它实际调用成功;这不代表所有账号、分组或时间段都支持该模型。
四、在本地输入 API Key
API Key 写入:
text
~/.codex-proxy/auth.json
文件结构如下,示例不含真实密钥:
json
{
"OPENAI_API_KEY": "YOUR_API_KEY"
}
为了避免将 Key 直接写进 Shell 命令历史,可以在终端通过 Python 的隐藏输入保存。需要本机已安装 Python 3。
bash
python3 - <<'PY'
import getpass
import json
import os
import tempfile
from pathlib import Path
profile = Path.home() / ".codex-proxy"
profile.mkdir(mode=0o700, exist_ok=True)
key = getpass.getpass("请输入代理 API Key(输入不显示): ").strip()
if not key:
raise SystemExit("未输入 Key,配置未修改。")
fd, temporary = tempfile.mkstemp(dir=profile)
with os.fdopen(fd, "w") as file:
json.dump({"OPENAI_API_KEY": key}, file)
os.chmod(temporary, 0o600)
os.replace(temporary, profile / "auth.json")
print("已保存到代理版独立目录。")
PY
这个步骤只负责保存,还没有验证 Key 是否有效。不要把真实 Key 放进博客正文、截图或代码仓库,也不要将官方版的整个 auth.json 复制过来。
五、启动第二个 Codex 实例
确认应用实际安装路径后,在终端执行:
bash
/usr/bin/open -n \
--env "CODEX_HOME=$HOME/.codex-proxy" \
--env "CODEX_ELECTRON_USER_DATA_PATH=$HOME/Library/Application Support/Codex-Proxy" \
/Applications/ChatGPT.app \
--args \
"--user-data-dir=$HOME/Library/Application Support/Codex-Proxy"
如果你的应用叫 Codex.app,将命令中的应用路径换成实际路径。旧版本是否支持同样的环境变量,需要单独验证。
这里的环境变量只传给新启动的应用,不需要写入 .zshrc,也不需要修改系统全局环境。官方版继续从原入口启动。
六、做成可以双击的桌面入口
每次复制终端命令不够方便,可以用 macOS 自带的"脚本编辑器"创建启动应用。
打开"脚本编辑器",粘贴以下 AppleScript:
applescript
on run
set userHome to POSIX path of (path to home folder)
set profilePath to userHome & ".codex-proxy"
set desktopDataPath to userHome & "Library/Application Support/Codex-Proxy"
set appPath to "/Applications/ChatGPT.app"
set launchCommand to "/usr/bin/open -n --env " & ¬
quoted form of ("CODEX_HOME=" & profilePath) & ¬
" --env " & quoted form of ("CODEX_ELECTRON_USER_DATA_PATH=" & desktopDataPath) & ¬
" " & quoted form of appPath & ¬
" --args " & quoted form of ("--user-data-dir=" & desktopDataPath)
do shell script launchCommand
end run
选择"文件 → 导出":
- 文件格式选择"应用程序"。
- 名称填写"Codex 代理版"。
- 保存到"应用程序"目录或自己的
~/Applications目录。
以后通过这个入口打开代理版。它是一个启动器,启动后实际运行的仍是官方程序,因此运行窗口、菜单或 Dock 图标可能仍显示官方应用名称。不要只根据窗口外观判断当前使用的是哪套配置。
重复点击启动入口是否复用窗口,取决于应用版本的单实例处理;建议先确认代理版是否已经运行。
七、怎么验证隔离真的生效
1. 检查两个主进程
在终端执行:
bash
ps -axo pid=,command= | grep '[C]hatGPT.app/Contents/MacOS/ChatGPT'
本次实测可以看到两个主进程:原来的官方进程,以及带有以下参数的代理进程:
text
--user-data-dir=.../Library/Application Support/Codex-Proxy
进程存在只能证明启动成功,还需要继续验证目录和接口。
2. 检查独立目录
代理版启动后,~/.codex-proxy 中会生成数据库、缓存和其他本地状态文件;桌面数据则写入指定的 Codex-Proxy 目录。
本次配置过程中,还比较了官方 config.toml 和 auth.json 修改前后的 SHA-256,确认它们没有被本次操作改变。
需要注意:应用正常运行时也可能刷新认证文件,因此未来校验值变化本身不能直接证明发生了串用,应结合时间和实际修改内容判断。
3. 验证代理模型调用
在代理版中新建任务,选择配置好的模型,发送一条简单测试消息,例如:
text
请只回复 OK。
如果失败,重点看请求地址、HTTP 状态和错误正文。请求确实到达代理域名、认证通过、模型完整返回结果,是不同层次的验证。
本次通过独立的最小 Responses API 请求,观察到 gpt-5.5 返回 OK,并收到 response.completed 事件。随后保存配置并重新启动了代理版。
如需重启,请退出对应的代理实例,而不是只关闭窗口;关闭窗口可能仍保留后台进程。不要使用 killall ChatGPT,它可能把官方版一起退出。
八、这次遇到的两个错误
401 Unauthorized:INVALID_API_KEY
最初复用本机已有的代理 Key,服务端返回:
json
{
"code": "INVALID_API_KEY",
"message": "Invalid API key"
}
这说明该次请求已到达代理服务,但服务端不接受提交的凭据。排查时应确认:
- Key 是否完整、有效,是否被撤销。
- Key 是否属于当前接口服务。
- 分组是否用于 Codex/GPT,而不是其他客户端或产品。
- 应用是否仍使用缓存中的旧凭据,或启动到了另一套配置目录。
本次更换 Key 后错误发生变化,后续使用 gpt-5.5 成功返回结果,说明新 Key 能用于这条已验证的调用路径。
Key 更新后,gpt-5.4 返回上游错误
新 Key 调用 gpt-5.4 时,代理返回了 upstream_error。错误文字还提到了与请求模型不一致的其他模型名。
这类信息不能简单理解成"客户端填写的模型一定错误"。也可能涉及服务商路由、上游可用性或参数兼容问题。
本次改用 gpt-5.5 后成功,所以将它设为默认模型。这里只能得出"当次测试成功"的结论,不能据此断言 gpt-5.4 永久不可用。
九、实测结论与边界
本次已经确认:
- 两个 Codex 桌面进程可以同时运行。
- 代理实例使用独立的 Codex 状态目录和桌面数据目录。
- 官方配置和登录文件没有被本次操作修改。
- 代理 Key 与
gpt-5.5的最小 Responses API 调用成功。
本次未通过自动化工具完成代理桌面内的完整对话操作验证:电脑控制工具禁止读取 Codex 自身界面。因此,进程、目录隔离和 API 验证已经完成,实际桌面交互仍应按前面的步骤手动确认。也没有验证所有插件、系统权限和全局快捷键在双实例下的行为。
官方程序升级后,两个入口会共同使用升级后的程序文件。由于本方案包含版本相关的桌面启动设置,升级后应再次检查双实例启动、独立目录和代理调用。