READMESource: main@ae4285cb
dsh-codex
给 DeepSeek Harness(DSH)接入 OpenAI Codex(ChatGPT Plus/Pro OAuth)账号。安装后模型选择器中会出现 OpenAI Codex Provider,可直接使用 Codex 模型。
- Provider 路由:
openai-codex - API 类型:
openai-codex-responses(Codex Responses API,不是/v1/chat/completions) - 兼容版本:DSH
>= 0.1.0-rc.6 - License:MIT
功能特性
- 通过 ChatGPT Plus/Pro OAuth 接入 Codex 模型,不依赖 DeepSeek API Key。
- 支持浏览器 OAuth 登录和 Device Code 无浏览器登录。
- 模型选择器自动发现 Codex 模型,支持文本流、工具调用和图片输入(仅限模型目录声明支持 image 的模型)。
- 支持 Codex Native Web Search:
openai-codex当前 turn 暴露web_search时,自动转换为 Codex Responses hostedweb_search。 - 凭证使用 DSH 自身凭证服务保存,自动刷新、不写入日志。
安装与启用
方式 A(推荐):dsh plugin
# 直接从 GitHub 安装(推荐)
dsh plugin --profile web add github:ddll8023/dsh-codex
# 或先 clone 到本地,再安装本地路径
git clone https://github.com/ddll8023/dsh-codex.git
dsh plugin --profile web add /绝对路径/dsh-codex
# 已发布到 npm registry 后也可使用包名:
dsh plugin --profile web add dsh-codex
dsh plugin 需要 pnpm(可通过 corepack enable pnpm 启用)。安装后重启 dsh web,插件随 profile 启动。
方式 B:手动
- 将插件包放入
$DSH_HOME/profiles/web/node_modules/dsh-codex(或执行npm install <路径>)。 - 在
$DSH_HOME/profiles/web/package.json的dsh.profile.bundles追加"dsh-codex"。 - 重启
dsh web。
验证是否启用
dsh --profile web --dump-config | grep -A2 llm-codex
# - id: llm-codex
# name: dsh-codex
启用后模型选择器中会出现 OpenAI Codex Provider 及其模型(gpt-5.3-codex-spark、gpt-5.4、gpt-5.4-mini、gpt-5.5、gpt-5.6-luna 等,来自 pi-ai 的 Codex 目录)。
登录与使用
| 命令 | 说明 |
|---|---|
/codex login |
浏览器 OAuth 登录(Authorization Code + PKCE,回调 http://localhost:1455/auth/callback)。命令返回授权 URL,在浏览器完成登录即可,流程在后台继续。 |
/codex login --device |
无浏览器环境的 Device Code 流程(显示设备码与验证 URL)。 |
/codex logout |
删除本地 OAuth 凭证。 |
/codex status |
登录状态、账号(accountId)、token 有效期(不显示任何 token)。 |
登录后在模型选择器中选择 openai-codex 下的任意 Codex 模型即可对话;支持文本流、工具调用和图片输入,事件格式与现有 Provider 一致。
Codex Native Web Search
搜索调用仍使用 Harness 原有的 web_search 工具语义:
- 使用
openai-codex时,dsh-codex在发送 Codex Responses 请求前,通过 pi-ai 的onPayload做 request-local 转换:删除普通 functionweb_search,保留其他工具,并追加 Codex hosted{ type: "web_search", ... }。 - 只有当前
options.tools确实包含web_search时才转换;没有该工具不会额外授予联网能力。 - 使用 DeepSeek、Claude、Gemini 或其他 Provider 时,仍是
dsh-tool-web -> ctx.web -> Harness 配置的 Search Provider。 - 插件不会修改
ctx.web.searchProvider、DSH_WEB_SEARCH_PROVIDER、Exa、Perplexity 或 DeepSeek Search,也不需要为 Codex 配置 Harness Search Provider。
默认配置为 nativeWebSearch: true、webSearchMode: live。Codex 公开 Responses 请求格式支持以下模式:
live:{ type: "web_search", external_web_access: true }cached:{ type: "web_search", external_web_access: false }indexed:{ type: "web_search", external_web_access: true, indexed_web_access: true }disabled:不注入 hosted search;等同于关闭本 turn 的 Native Search
是否可用仍取决于实际 Codex backend、模型和 ChatGPT 账号能力;不支持时不会静默切换第三方 Search Provider。
配置(可选,非密钥)
# $DSH_HOME/settings.yaml 的 llm-codex 段,或 cordis.patch.yml 中该行的 config
llm-codex:
baseURL: https://chatgpt.com/backend-api # 端点(默认)
transport: sse # sse | websocket | websocket-cached | auto
cacheRetention: short # none | short | long
nativeWebSearch: true # 仅替换已授权的 web_search 工具
webSearchMode: live # live | cached | indexed | disabled
refreshLeadTimeMs: 300000 # 提前刷新阈值
streamIdleTimeoutMs: 300000
retryPolicy:
mode: normal
maxRetries: 2
安全与凭证
- 凭证保存在 DSH 自身凭证服务(
ctx.credentials,即$DSH_HOME/.credentials.yaml,文件权限 0600,热加载、串行写入)的插件命名空间OPENAI_CODEX_OAUTH下,一条 JSON 记录:{type, access, refresh, expires, accountId}。 accountId从 access token JWT 的https://api.openai.com/auth.chatgpt_account_id声明解析,仅用于请求头chatgpt-account-id与/codex status展示。- 刷新策略:剩余约 5 分钟(
refreshLeadTimeMs,默认 300000ms)时提前刷新;后台每 5 分钟检查一次 + 每次请求前检查;并发刷新由凭证存储的按 provider 串行队列 + 双检锁保证只刷新一次;刷新失败保留旧凭证并返回明确错误(AUTH)。 - token 永不进入日志、session 事件、telemetry 或错误信息;
/codex命令设置recordInput: false,防止粘贴的授权码落入会话日志。 - DeepSeek 的 API key 在
DEEPSEEK_API_KEY等自有 ref 下,二者互不干扰;Codex token 永远不会被 DeepSeek 请求使用。
兼容性与风险
originator固定为"pi":请求头与 OAuth authorize URL 的originator由 pi-ai 的 codex 实现硬编码(originator: "pi",即 pi-ai 自身的标识)。插件复用该实现,因此无法在不复制协议的情况下改写为其它值;ChatGPT 后端可能按 originator 做白名单/风控,改动有风险,故保持 pi-ai 官方值。- User-Agent 由 pi-ai codex 传输层设置(
pi (platform; arch)),会覆盖 harness 默认 attribution 的 UA;attribution 头仍按契约传入(与 dsh-llm-pi-ai 行为一致)。 - Codex 后端协议为社区逆向/维护(pi-ai 维护),
chatgpt.com/backend-api的字段、限流、风控可能随 ChatGPT 前端变化;若后端收紧,需要 pi-ai 升级适配。 - 当前
@earendil-works/pi-ai@0.82.1的openai-codex-responses已能透传 hosted search 请求,但openai-responses-shared会忽略web_search_call事件,并丢弃output_text.annotations;由于 pi-ai 与 HarnessStreamChunk当前没有结构化 citation 字段,source metadata 不能在插件层完整恢复。普通文本流和未知搜索事件仍会继续到终态。 - 浏览器流程依赖本地
127.0.0.1:1455端口可用;端口被占用时 pi-ai 会走手工粘贴授权码的降级路径,本插件在 Web GUI 下以错误信息提示。 - 用量上限(quota)等错误映射基于消息文本分类,OpenAI 侧文案变化可能影响分类(回退为
PI_AI_ERROR,不影响请求本身)。
开发与测试
插件文件
dsh-codex/
├── package.json # 插件清单:dsh.bundle 声明、依赖、测试脚本
├── cordis.patch.yml # 插件清单:bundle patch(注册 llm-codex 一行)
├── lib/
│ ├── index.js # 插件入口:注册 Provider/命令/设置/timer
│ ├── constants.js # 常量:Provider id、默认地址、凭证命名空间
│ ├── config.js # 配置 schema(baseURL/transport/refreshLeadTime/retryPolicy…)
│ ├── models.js # pi-ai Models 集合构建(含 baseURL 重定向)
│ ├── credentials.js # 凭证存储:credentials seam 适配 + 提前刷新(双检锁)
│ ├── oauth.js # /codex 命令的登录/登出/状态编排与交互适配
│ ├── adapter.js # CodexAdapter(dsh LlmAdapter 实现)
│ ├── context.js # harness 消息 → pi-ai Context
│ ├── web-search.js # request-local Hosted Web Search payload 转换
│ ├── replay.js # pi-ai 回放状态(多轮签名透传)
│ └── stream.js # pi-ai 事件 → harness StreamChunk
└── test/ # 插件自身测试(mock HTTP,不执行真实登录)
├── helpers.js
├── credentials.test.js
├── refresh.test.js
├── oauth.test.js
├── adapter.test.js
├── web-search.test.js
└── plugin.test.js
运行测试
cd dsh-codex
npm test
测试覆盖(41 项,全绿):
- Codex Native Web Search:权限存在时 function → hosted 转换、其他工具保留、无权限不注入、配置关闭、live/cached/indexed/disabled 模式
web_search_call/未知搜索事件与带 annotation 的文本流不崩溃;不支持搜索错误映射为CODEX_WEB_SEARCH_UNSUPPORTED- JWT accountId 解析(含缺失/畸形 token)
- 凭证记录的校验与存取(credentials seam 往返、损坏记录、并发 modify 串行化)
- 提前刷新:阈值判断、旋转持久化、并发只刷新一次、失败保留旧凭证并报
AUTH - OAuth:Device Code 全流程(mock auth.openai.com)、token 响应字段校验、浏览器流程(PKCE
code_challenge校验、错误 state 被回调服务器拒绝、正确 state 完成登录) - Codex 请求头与请求体:
Authorization: Bearer、chatgpt-account-id、originator、openai-beta: responses=experimental、instructions/input/tools/stream - SSE 文本流与 tool call 流到 harness StreamChunk 的翻译(含 usage、finish)
- 图片附件读取与 Responses
input_image请求体转换;不支持图片的文本模型仍返回UNSUPPORTED_CONTENT - 无凭证 →
MISSING_CREDENTIAL;未知模型 →UNKNOWN_MODEL;用量上限 →QUOTA - 插件加载/卸载:Provider 路由、可配置 Provider 目录、命令注册与卸载清理
- 真实 harness boot 验证(dsh-app-boot + loader + 真实服务)
尚未验证的部分
- 真实 ChatGPT 账号登录(按约束未执行;OAuth 各环节在 mock HTTP 下验证)。
transport: websocket*:默认走 SSE;websocket 路径未在 mock 中覆盖。- 与最新 pi-ai 目录的模型清单同步(模型来自 pi-ai 目录,非本插件固化)。
No comments yet. Be the first to write one.