---
title: Codex CLI 从中转 API 切到 ChatGPT 订阅登录，5000+ 条历史会话一条没丢
slug: codex-switch-to-chatgpt-oauth-keep-history
date: 2026-10-15
author: Frankie 徐
category: ai
tags: ['codex', 'openai', 'oauth', 'cli', 'migration']
description: Codex CLI 原来走第三方中转 API，想改用 ChatGPT 订阅 OAuth 又不想丢历史会话。方法是保留原 provider 标识只改 base_url，再处理后台 daemon 缓存旧鉴权的假象。
permalink: https://www.210k.cc/codex-switch-to-chatgpt-oauth-keep-history
---

## 背景

我的 Codex CLI 一直通过一个 CRS 中转 API 使用，所有历史会话、项目配置、MCP 都在默认的 `~/.codex/` 里。后来开了 ChatGPT 订阅，想把默认命令改成订阅 OAuth 登录。

最省事的做法是新建一个空的 `CODEX_HOME`，重新登录。但这样 `cdx resume` 就看不到原来的会话了。我当时本地有 5,000 多条会话，分布在 200 多个项目目录里，不想丢。

最后的结果：原目录原地切换，迁移前后会话记录逐条比对，ID、项目目录、rollout 路径、provider 字段的缺失和变化都是 0。

环境：macOS，Codex CLI 0.162.x。

## 最终的命令布局

我把三种用法拆成三个独立实例，互不污染：

| 命令 | 数据目录 | 用途 |
| --- | --- | --- |
| `cdx` | `~/.codex/` | 默认入口，ChatGPT 订阅 OAuth，保留全部历史 |
| `cdxs` | `~/.codex-sub/` | 另一套保留的实例 |
| `cx` | `~/.codex-crs/` | 偶尔还要用中转 API 时用，独立 API Key |

别名写在 zsh 配置里：

```zsh
CODEX_NODE=22.23.1
alias cdx="mise exec node@${CODEX_NODE} -- codex --yolo"
alias cdxs="CODEX_HOME=$HOME/.codex-sub mise exec node@${CODEX_NODE} -- codex --yolo"
alias cx="CODEX_HOME=$HOME/.codex-crs mise exec node@${CODEX_NODE} -- codex --yolo"
```

`cdx` 没有显式设 `CODEX_HOME`，用的是默认目录。要注意：如果当前 shell 已经 `export` 过别的 `CODEX_HOME`，`cdx` 会进错实例。不确定的时候写成 `CODEX_HOME="$HOME/.codex" cdx`。

`cx` 是单独配的，没复制 `cdx` 的历史和 MCP。中转模式要回来，用它就行，不用动主实例。

## 保留历史的关键：provider 名字不动，只改地址

翻了一下历史会话，每条记录的 `model_provider` 都是 `crs`。如果把 provider 换成新名字，旧会话和新配置就对不上了。

所以我保留 `crs` 这个标识，只把它指向的地址改成 ChatGPT 官方端点：

```toml
model_provider = "crs"
model = "gpt-6.1-sol"

[model_providers.crs]
name = "crs"
base_url = "https://chatgpt.com/backend-api/codex"
wire_api = "responses"
requires_openai_auth = true
```

这只是相关字段，不要拿去覆盖整份 `config.toml`，其他设置保持原样。另外，顶层字段（`model_provider`、`model`）必须放在第一个 `[表]` 之前，否则 TOML 会把它们归到上一个表里。

改完之后，`crs` 只是个历史兼容的名字，实际请求已经直接发到官方，不再经过中转。

整个过程没改历史数据库，也没重写任何会话文件。

## 操作步骤

### 1. 先备份

完整备份数据目录，再单独存一份 SQLite 一致性快照。活跃的数据库不能只复制主文件，WAL 里可能还有没合并的数据。运行中的 socket 是进程通信端点，不用备份。

```zsh
sqlite3 ~/.codex/state_5.sqlite ".backup '$HOME/codex-backups/state_5.consistent.sqlite'"
```

（数据库文件名以你本机实际为准。）

### 2. 改 provider 地址

按上一节改 `config.toml`。**顺序不能反**：必须先把地址改成官方端点，再做 OAuth 登录。否则 ChatGPT 的 OAuth token 会被发到原来的中转地址。

### 3. 浏览器授权

```zsh
CODEX_HOME="$HOME/.codex" cdx login
CODEX_HOME="$HOME/.codex" cdx login status
```

看到 `Logged in using ChatGPT` 就算成功。`auth.json` 里的鉴权类型也会从 API Key 变成 ChatGPT OAuth tokens。检查这个文件时只看类型和字段是否存在，不要把内容打印出来。

## 坑：登录成功了，新会话还显示 API key

登录成功后，我打开一个新的 `cdx` 会话，`/status` 里依然显示 `API key configured`。

原因是 Codex 有一个本地后台 app-server，新开终端不一定会启动新的 server，它手里还拿着旧的鉴权状态。

先用不走后台 server 的独立进程确认配置本身没问题：

```zsh
CODEX_HOME="$HOME/.codex" cdx --no-daemon
```

独立进程正常，就说明是后台 server 缓存了旧状态。重启它：

```zsh
CODEX_HOME="$HOME/.codex" mise exec node@${CODEX_NODE} -- codex app-server daemon restart
```

重启会中断所有连在这个 server 上的会话，要在没有重要任务跑的时候做。我这次等了大约一分钟，旧进程退出，新 server 起来了。

再开 `cdx`，`/status` 里应该看到：

- provider 仍然是 `crs`
- Account 显示订阅账号和套餐
- 能读到 Weekly limit

最后发一个不调工具的简单请求，返回 `OK`，说明官方端点实际可用。跨项目看历史：

```zsh
cdx resume --all
```

注意：能看到历史列表，不代表每个旧会话都能恢复运行。我只验证了数据库记录完整，没有逐个恢复全部会话。

## 踩过和差点踩的坑

1. **不要给 OAuth 实例随手加 `forced_login_method = "api"`。** 我后来跑一个 DeepSeek 接入脚本时它加了这个字段，结果鉴权冲突，直接被登出。
2. **`login status` 只代表独立 CLI 进程的状态，不代表后台 server。** 要结合新会话里的 `/status`、数据目录和后台进程一起判断。
3. **provider 名字说明不了请求发到哪、怎么计费。** 我这里叫 `crs`，实际地址是官方端点，用的是订阅。
4. **排查时不要打印完整配置、API Key 或 OAuth token。** 备份目录也要收紧权限，里面有登录凭据和私人会话内容。
5. **当前版本会把 `preferred_auth_method` 和 `disable_response_storage` 当成忽略项。** 迁移能成功，靠的是 OAuth 凭据、provider 地址和 `requires_openai_auth` 这三样，不要以为写了那两个字段就能保证鉴权。

## 回滚原则

- 要用中转，优先用已经隔离好的 `cx`，不要改回主实例。
- 确实要回滚 `cdx`：先停掉相关工作，备份迁移后新增的数据，再只恢复必要的配置和鉴权文件。**不要整个目录覆盖回去**，那会把备份之后新产生的会话全部丢掉。

## 参考

- [Codex 鉴权与凭据存储](https://learn.chatgpt.com/docs/auth)
- [Codex 配置参考](https://learn.chatgpt.com/docs/config-file/config-reference)
- [配置目录与 profiles](https://learn.chatgpt.com/docs/config-file/config-advanced)

「保留 `crs` 标识、替换官方地址」是我在当前版本上实际验证过的方案。Codex 升级以后，要重新检查一遍鉴权、请求地址和历史是否可见。