# dsh-codex-keychain

[English](README.md)

这是一个独立、非官方的 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
ChatGPT OAuth 插件。它注册 `openai-codex` 模型路由，并把完整 OAuth 凭据严格保存到操作系统原生凭据库：

- macOS Keychain
- Windows Credential Manager
- Linux Secret Service

它不需要 OpenAI Platform API key，不会降级到普通凭据文件，也不会读取或修改
`~/.codex/auth.json`。

> [!IMPORTANT]
> ChatGPT 订阅与 OpenAI Platform API 是不同产品。本插件只连接符合条件的 ChatGPT
> 账户所能使用的 Codex 后端，不代表 OpenAI 或 DeepSeek 官方产品。

## 当前状态

`0.1.0-alpha.1` 是开发者预览版，初始兼容目标为 DeepSeek Harness
`0.1.0-rc.6` 与 `@earendil-works/pi-ai` `0.82.1`。

## 安装

```sh
dsh plugin --profile web add dsh-codex-keychain@next
dsh web
```

打开 **设置 → OpenAI Codex**，点击 **使用浏览器登录**，再点击 **打开授权页面**
并完成 OpenAI 授权。插件不会修改默认模型；登录后请在 DSH 模型选择器中手动选择
`openai-codex` 模型。

无界面服务器或本地回调端口不可用时，使用设备码：

```sh
dsh plugin --profile web exec dsh-codex-keychain login --device-code
```

## CLI

```sh
dsh plugin --profile web exec dsh-codex-keychain login
dsh plugin --profile web exec dsh-codex-keychain login --device-code
dsh plugin --profile web exec dsh-codex-keychain status
dsh plugin --profile web exec dsh-codex-keychain status --json
dsh plugin --profile web exec dsh-codex-keychain logout
```

`status --json` 只输出登录状态、provider、平台、存储后端，以及失败时的固定错误码与
安全文案；不包含 token、账户 ID 或 token 时间戳。

## 安全设计

插件把带版本号的清单与限长 Base64 分块全部保存为系统凭据项。固定 service 为
`io.github.CpfPatrick.dsh-codex-keychain`；根 account 为规范化 `DSH_HOME` 的
SHA-256，分块 account 从该摘要派生，因此不同 Harness home 不会共用可旋转的
refresh token。两个固定分区保证更新安全：先完整写入并校验新分区，再切换小型清单，
最后清理旧分区；每个凭据项也保持在 Windows Credential Manager 的单项上限以内。

刷新和退出登录通过 `$DSH_HOME/.locks` 下不含秘密的跨进程锁串行化。刷新失败会保留
原凭据，供稍后重试或重新登录。如果系统凭据库缺失、被锁定或不可用，插件会报告
`KEYCHAIN_UNAVAILABLE` 并拒绝登录，不会静默写入文件。

Web 认证 RPC 只接受 loopback。远程访问 DSH 时，必须在 DSH 主机上运行 CLI。
OAuth URL 与设备码属于短期交互数据；插件不会把它们写入应用日志或持久存储。CLI 会在
登录所需时显示它们，请勿分享或录制该段终端输出。

更多说明见 [SECURITY.md](SECURITY.md)。

## 故障排查

- **macOS 显示 `KEYCHAIN_UNAVAILABLE`：**解锁登录钥匙串，并允许终端或 DSH 进程
  使用它。插件仍会加载，但在安全存储恢复前会拒绝模型请求。
- **Windows 显示 `KEYCHAIN_UNAVAILABLE`：**确认当前用户可使用 Windows Credential
  Manager，且 DSH 没有运行在另一个服务账户下。
- **Linux 显示 `KEYCHAIN_UNAVAILABLE`：**请在带 D-Bus 会话与 Secret Service
  提供者（如 GNOME Keyring 或 KWallet）的桌面/服务器会话中运行 DSH；插件不会改用
  文件存储或 Linux 内核 keyring。
- **浏览器回调无法监听：**浏览器流程使用本机 1455 端口。停止占用该端口的进程，
  改用**设备码登录**，或在 CLI/设置页提示时粘贴手动回调响应。
- **已有登录正在进行：**DSH Host 与 CLI 进程之间全局只允许一个 OAuth 流程。完成或
  取消当前流程后再试。
- **进程在持锁期间崩溃：**先确认这个 `DSH_HOME` 对应的 DSH 或
  `dsh-codex-keychain` 进程均已停止；然后才可删除孤立的
  `$DSH_HOME/.locks/dsh-codex-keychain-openai-codex.lock` 和/或
  `$DSH_HOME/.locks/dsh-codex-keychain-openai-codex-login.lock`。不要删除仍由存活进程
  持有的锁。
- **`openai-codex` provider 冲突：**停用另一个 Codex/OAuth provider 插件并重启
  DSH；同一路由不能由两个 adapter 同时注册。
- **刷新或授权失败：**重新执行登录。失败的刷新或未完成登录不会替换原有凭据。
- **升级 DSH 后异常：**此 alpha 精确锁定 DSH `0.1.0-rc.6`。请等待兼容版本，或先在
  隔离 profile 中验证新版 Developer Preview。

远程浏览器不能调用认证 RPC，请在 DSH 主机上运行 CLI。卸载前如需删除系统凭据：

```sh
dsh plugin --profile web exec dsh-codex-keychain logout
dsh plugin --profile web remove dsh-codex-keychain
```

## 首版范围

包含浏览器/设备码 OAuth、自动刷新、Codex 模型目录、DSH 原生流式回复/推理/工具调用/
回放，以及 DSH 现有的图片附件/模型视觉输入、Web 设置页与无界面 CLI。首版不包含
额度、网页搜索、独立看图界面或图片生成。

已知 alpha 限制：DSH `0.1.0-rc.6` 会先解析 adapter 认证，再创建单次流式请求的取消
信号与空闲看门狗。正常模型流仍可取消，但网络层卡住的 token 刷新不能由当前请求取消。
不要删除存活进程持有的写锁；如需恢复，请先停止该进程。

## 开发

需要 Node.js `^22.19 || >=24` 与 pnpm `11.7.0`。

```sh
pnpm install
pnpm check
node scripts/native-keyring-smoke.mjs
```

原生 smoke test 会让一份超过 4 KiB 的一次性 OAuth 形状文档走完整生产分块存储，
校验后在 `finally` 中删除全部凭据项；单元测试使用注入的内存驱动，不接触开发者真实
钥匙串。

## 致谢与独立实现边界

DSH 社区已有包括 [`Yan-Zero/dsh-codex`](https://github.com/Yan-Zero/dsh-codex)
在内的 OAuth 插件。本仓库仅依据公开的 DSH 与 pi-ai 接口独立实现，不复制该项目源码；
本项目的核心差异是严格的跨平台系统凭据存储，不提供普通文件回退。

## License

MIT
