dsh-codex-oauth 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +80 -34
- package/README.zh.md +75 -42
- package/SPEC.md +39 -57
- package/bin/dsh-openai-codex.js +47 -22
- package/client/index.jsx +72 -24
- package/lib/adapter.js +128 -41
- package/lib/auth.js +230 -11
- package/lib/client.js +106 -21
- package/lib/index.js +9 -2
- package/lib/models.js +53 -0
- package/lib/openai-http.js +64 -0
- package/lib/quota-lookup.js +114 -0
- package/lib/quota-ui.js +24 -0
- package/lib/quota.js +111 -0
- package/lib/responses-request.js +125 -0
- package/lib/responses-stream.js +197 -0
- package/lib/safe-message.js +1 -1
- package/lib/store.js +82 -29
- package/lib/web-auth.js +59 -45
- package/package.json +8 -9
- package/scripts/patch-tui-quota-placement.mjs +35 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 — 2026-10-13
|
|
4
|
+
|
|
5
|
+
### Upgrade requirements
|
|
6
|
+
|
|
7
|
+
- Requires Node.js 22+ and the DSH 0.2.0-rc.2 adapter contract.
|
|
8
|
+
- Sign in again with the native Sign in with ChatGPT flow. The plugin keeps legacy credentials but cannot use them for native inference.
|
|
9
|
+
- Device-code login is no longer supported. Configure supported request options as described in the README.
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
- Implement DSH's official `LlmAdapter` directly, removing the pi-ai dependency. Use native OAuth, the account's live model catalog, and the public Responses API.
|
|
14
|
+
- Add account registration, selection, credential renewal and revocation, with verified ID tokens and serialized rotating refreshes.
|
|
15
|
+
- Preserve reasoning/tool replay and normalize historical tool call IDs to the API's 64-character limit.
|
|
16
|
+
- Supply stable DSH session headers for prompt-cache reuse and allow DSH's automatic compaction budget without sending an unsupported API output cap.
|
|
17
|
+
- Add English Web quota bars, compact TUI quota summaries, and `/codex-usage` details. Prefer response quota data; fall back to WHAM with the plugin's retained, unexpired legacy Codex credentials. Quota checks do not call a model.
|
|
18
|
+
- Include an optional, manually applied dsh-tui 0.14.0 patch that places quota alongside model and reasoning effort. Narrow terminals truncate fields without an extra quota row.
|
|
19
|
+
|
|
20
|
+
### Quota limitations
|
|
21
|
+
|
|
22
|
+
WHAM reports Codex subscription limits; equivalence to native SIWC application limits is unconfirmed. Matching email claims are required but do not prove the same workspace. Legacy credentials are neither imported from Pi/Codex apps nor refreshed by the quota lookup. Missing or expired credentials leave quota unavailable. These source qualifications are documented rather than added to the UI.
|
|
23
|
+
|
|
24
|
+
### Validation
|
|
25
|
+
|
|
26
|
+
33 automated behavior tests pass. Native inference, cache affinity, quota lookup, and TUI display were also exercised during local development. The inline layout was checked at normal and 40-column widths.
|
package/README.md
CHANGED
|
@@ -2,74 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use ChatGPT plan models in [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) through [OpenAI's direct OAuth integration](https://developers.openai.com/siwc/token-sharing-open-source). Version 0.3 implements DSH's official `LlmAdapter` directly and has no `pi-ai` or `dsh-llm-pi-ai` dependency.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The plugin handles browser authorization, account selection, rotating token refresh, current account model discovery, and Responses streaming. It translates DSH tools, images and conversation history, and preserves completed encrypted reasoning for subsequent turns. Filesystem access, tool execution, permissions and context compaction remain DSH responsibilities.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Requirements and installation
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Requires Node.js 22 or newer and DSH `0.2.0-rc.2` compatible `dsh-llm` and `dsh-atomic-write` packages. Version 0.3 no longer supports the old DSH 0.1 adapter contract.
|
|
12
|
+
|
|
13
|
+
Install into the profile you actually run:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
dsh plugin --profile
|
|
15
|
-
dsh
|
|
16
|
+
dsh plugin --profile dsh-tui add -w dsh-codex-oauth@^0.3.0
|
|
17
|
+
dsh --profile dsh-tui
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
For
|
|
20
|
+
For a source checkout:
|
|
19
21
|
|
|
20
22
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
npm install
|
|
24
|
+
npm run build
|
|
25
|
+
npm pack --ignore-scripts
|
|
26
|
+
dsh plugin --profile dsh-tui add -w "file:$PWD/dsh-codex-oauth-0.3.0.tgz"
|
|
27
|
+
dsh --profile dsh-tui
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
For the Web surface, use `--profile web` and launch `dsh web`.
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
Installing the archive also avoids pnpm reusing an older local directory's file list when upgrading an existing source installation.
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
If the TUI profile also loads its built-in subscription OAuth module, exclude `openai-codex` from `dsh-tui-auth` so this plugin owns that provider. In `$DSH_HOME/profiles/dsh-tui/cordis.patch.yml` (default `~/.dsh/profiles/dsh-tui/cordis.patch.yml`):
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
```yaml
|
|
37
|
+
- id: dsh-tui-auth
|
|
38
|
+
config:
|
|
39
|
+
providers: [anthropic, xai, meta]
|
|
40
|
+
```
|
|
32
41
|
|
|
33
|
-
|
|
42
|
+
Preserve any existing `credentialsFile` and `modelOverrides` in that block. Retain only providers supported by your installed TUI. Restart the profile after upgrading the plugin.
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
dsh plugin --profile web exec dsh-openai-codex login
|
|
37
|
-
# headless machines:
|
|
38
|
-
dsh plugin --profile web exec dsh-openai-codex login --device-code
|
|
44
|
+
## Sign in and choose a model
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
dsh plugin --profile
|
|
46
|
+
```bash
|
|
47
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex login
|
|
48
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex status
|
|
49
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex models --refresh
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
Authorize the application in the browser and allow ChatGPT plan usage. The local callback binds to `127.0.0.1:1455`, falling back to another loopback port when necessary. This flow requires browser authorization; `--device-code` is unavailable.
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
In the Web UI, open **Settings → OpenAI Codex**, select **Continue with ChatGPT**, and return after authorization. The section offers account selection, cancellation, sign-out, model refresh and a [manage usage link](https://chatgpt.com/settings/usage). Identity-only authorization is shown separately; sign in again to authorize plan usage.
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
Choose provider `openai-codex` in DSH's model picker. Models come from the selected account's live catalog, including new releases such as GPT-6.1 Sol when the account has access. The plugin preserves the server's display names and ordering, caches the catalog for five minutes, and supports explicit refresh. It does not maintain a fixed model list.
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
## Accounts and credentials
|
|
51
59
|
|
|
52
|
-
```
|
|
53
|
-
|
|
60
|
+
```bash
|
|
61
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex login --new-account
|
|
62
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex accounts
|
|
63
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex use <client-id>
|
|
64
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex logout
|
|
54
65
|
```
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
Each account retains its issued application registration. Reauthorization reuses that registration and verifies the same identity. Sign-out attempts remote revocation, clears the selected account's local tokens and keeps the registration for later sign-in. It leaves other accounts intact. Cancelling a pending login preserves the previous account.
|
|
68
|
+
|
|
69
|
+
Credentials are stored in `$DSH_HOME/.openai-codex-auth.v2.json`, defaulting to `~/.dsh/.openai-codex-auth.v2.json`. The versioned document keeps a stable host identifier, account registrations and the active selection. Writes are atomic; the directory is owner-only and the file is `0600` on POSIX. A cross-process lock serializes token rotation. Temporary refresh failures preserve credentials; terminal refresh failures clear expired authorization.
|
|
70
|
+
|
|
71
|
+
**Upgrade from 0.2:** sign in again using the new authorization flow. Legacy `.openai-codex-auth.json` credentials cannot authorize this API route and are left untouched. The plugin never imports or changes `~/.pi/agent/auth.json` or `~/.codex/auth.json`.
|
|
72
|
+
|
|
73
|
+
## Supported behavior and limits
|
|
74
|
+
|
|
75
|
+
Requests use `https://api.openai.com/v1/responses` with OAuth, `store: false`, `stream: true` and the required history in `input`. No API key is required. Usage is subject to ChatGPT plan eligibility and application quotas; the plugin never falls back to API key billing.
|
|
76
|
+
|
|
77
|
+
DSH's session identity supplies stable `session-id` and `x-client-request-id` headers for ordinary and compaction calls. This preserves the gateway's session affinity for prompt-cache reuse; cache hits are still not guaranteed. Calls without a session identity omit these headers.
|
|
78
|
+
|
|
79
|
+
- DSH function tools run locally, grouped in a Responses namespace. All offered definitions are sent; native Responses `tool_search` is unavailable on this route.
|
|
80
|
+
- Text and images use DSH attachments. Files and offloaded images are represented by local handles for DSH tools; this plugin does not upload files to OpenAI's Files API.
|
|
81
|
+
- Reasoning effort and context metadata come from the live catalog when advertised. Replay state is reused only for unchanged blocks from the same model and account. Compacted or edited messages use the remaining DSH history.
|
|
82
|
+
- `temperature`, `maxTokens` and `stop` fail with `UNSUPPORTED_OPTION` when set, because this ChatGPT plan route does not support them. Leave them unset in your agent configuration.
|
|
83
|
+
- DSH history summarization (`purpose: compaction`) accepts the `maxTokens` budget supplied automatically by its compaction backend and omits it from the API request. Automatic and manual DSH compaction can run, but the backend's summary output cap is not enforced by this route. The existing history prefix is preserved before the appended summarization instruction.
|
|
84
|
+
- Only a completed response is successful. Usage-limit failures, incomplete responses and interrupted streams have distinct failure semantics. Account quotas stop retries; transient provider failures use DSH's retry policy.
|
|
85
|
+
- Hosted Responses image generation, file search, Code Interpreter, computer use, MCP and audio/video are outside this adapter. Local DSH capabilities remain available as function tools.
|
|
86
|
+
|
|
87
|
+
See OpenAI's [current preview limitations](https://developers.openai.com/siwc/token-sharing-open-source/preview-limitations).
|
|
57
88
|
|
|
58
89
|
## Development
|
|
59
90
|
|
|
91
|
+
### Quota display
|
|
92
|
+
|
|
93
|
+
Web settings show English remaining-quota bars and reset timestamps. The TUI status line shows a compact summary; `/codex-usage` expands all available windows and their last-update times. Windows are labeled by the server's duration (300 minutes is 5h, 10,080 minutes is Weekly), not their primary/secondary position.
|
|
94
|
+
|
|
95
|
+
The compact summary is `codex 81% 4h36m 50% 5d4m`, with shorter windows first. `*` marks stale data; `?` means the reset time is unavailable. For dsh-tui 0.14.0, `node scripts/patch-tui-quota-placement.mjs [path/to/Chat.js]` moves just this contribution into the model/effort footer row below the input box and saves sibling backups of Chat.js and StatusLine.js. The existing field layout truncates narrow rows rather than adding a quota row. This is a local host patch because the public status API only supports placement above the prompt. Reinstalling/upgrading the TUI may remove it; the patch refuses unsupported versions.
|
|
96
|
+
|
|
97
|
+
For an npm-installed TUI profile, apply the optional patch with:
|
|
98
|
+
|
|
60
99
|
```bash
|
|
61
|
-
|
|
100
|
+
node "${DSH_HOME:-$HOME/.dsh}/profiles/dsh-tui/node_modules/dsh-codex-oauth/scripts/patch-tui-quota-placement.mjs"
|
|
62
101
|
```
|
|
63
102
|
|
|
64
|
-
|
|
103
|
+
Restart the TUI afterward. Without this patch the summary uses the standard position above the input box. Installation never applies the patch automatically.
|
|
65
104
|
|
|
66
|
-
The
|
|
105
|
+
The adapter observes `x-codex-*` response headers and `codex.rate_limits` stream events from normal requests. When these do not provide fresh data, the display queries `https://chatgpt.com/backend-api/wham/usage` with the plugin's retained legacy Codex credentials in `.openai-codex-auth.json`, beside its native auth file. It does not read Pi or Codex app credentials, change the inference route, or call a model. Successful lookups are cached for 60 seconds; failures back off for 30 seconds. Concurrent displays share one request.
|
|
67
106
|
|
|
68
|
-
|
|
107
|
+
This fallback reports Codex subscription limits, which have not been proven identical to the native SIWC application's limits. The saved credentials must have matching email claims; matching email does not establish the same workspace. This qualification belongs to the documentation, not the UI. Missing, expired, unrelated, or rejected legacy credentials leave quota unavailable. The lookup does not refresh or modify the legacy login: renew that login separately when it expires.
|
|
108
|
+
|
|
109
|
+
Snapshots stay in memory, clear on account changes/sign-out, and become stale after five minutes or a reported reset. Failed queries preserve the previous snapshot; passing a reset timestamp never fabricates a fresh 100% allowance. Source formats are parsed independently rather than merging windows from different endpoints.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install
|
|
113
|
+
npm test
|
|
114
|
+
# Optional: requires a signed-in test account; uses ChatGPT plan allowance.
|
|
115
|
+
DSH_HOME=/path/to/test-home npm run test:live -- gpt-6.1-sol
|
|
116
|
+
```
|
|
69
117
|
|
|
70
|
-
|
|
71
|
-
- Codex endpoints are not public OpenAI Platform API endpoints.
|
|
72
|
-
- Filesystem, shell, MCP, permissions, attachments, and other DSH capabilities come from the active profile.
|
|
118
|
+
`npm test` builds the Web bundle and checks OAuth identity validation, concurrent refresh, account changes, Web route boundaries, streamed errors, tool and reasoning replay, attachments and integration with DSH's official runtime. The live check performs a three-turn tool round trip and verifies history replay.
|
|
73
119
|
|
|
74
120
|
## License
|
|
75
121
|
|
package/README.zh.md
CHANGED
|
@@ -2,87 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
通过 [OpenAI 官方的直接 OAuth 接入](https://developers.openai.com/siwc/token-sharing-open-source),在 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 中使用 ChatGPT 套餐模型。0.3 版本直接实现 DSH 官方 `LlmAdapter`,不再依赖 `pi-ai` 或 `dsh-llm-pi-ai`。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
插件负责浏览器授权、账户选择、自动刷新凭据、查询当前账户的模型,以及转换 Responses 流式响应。支持 DSH 工具调用、图片、对话历史和完整加密推理状态的回传。文件系统、工具执行、权限和上下文压缩由 DSH 提供。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## 要求与安装
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
要求 Node.js 22 及以上,以及与 DSH `0.2.0-rc.2` 兼容的 `dsh-llm` 和 `dsh-atomic-write`。0.3 不再兼容 DSH 0.1 的旧适配器接口。
|
|
12
|
+
|
|
13
|
+
安装到实际使用的 profile:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
dsh plugin --profile
|
|
15
|
-
dsh
|
|
16
|
+
dsh plugin --profile dsh-tui add -w dsh-codex-oauth@^0.3.0
|
|
17
|
+
dsh --profile dsh-tui
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
从源码目录构建安装:
|
|
19
21
|
|
|
20
22
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
npm install
|
|
24
|
+
npm run build
|
|
25
|
+
npm pack --ignore-scripts
|
|
26
|
+
dsh plugin --profile dsh-tui add -w "file:$PWD/dsh-codex-oauth-0.3.0.tgz"
|
|
27
|
+
dsh --profile dsh-tui
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
Web 界面使用 `--profile web` 安装,再运行 `dsh web`。
|
|
31
|
+
|
|
32
|
+
使用安装包也能避免升级已有源码安装时,pnpm 继续复用旧本地目录的文件列表。
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
如果 TUI 同时加载了内置订阅 OAuth 模块,需要从 `dsh-tui-auth` 中排除 `openai-codex`,由本插件提供该 provider。在 `$DSH_HOME/profiles/dsh-tui/cordis.patch.yml`(默认 `~/.dsh/profiles/dsh-tui/cordis.patch.yml`)中配置:
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
```yaml
|
|
37
|
+
- id: dsh-tui-auth
|
|
38
|
+
config:
|
|
39
|
+
providers: [anthropic, xai, meta]
|
|
40
|
+
```
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
保留该配置块已有的 `credentialsFile` 和 `modelOverrides`;provider 列表只保留当前 TUI 支持的条目。更新插件后重启 profile。
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
## 登录与选择模型
|
|
34
45
|
|
|
35
46
|
```bash
|
|
36
|
-
dsh plugin --profile
|
|
47
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex login
|
|
48
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex status
|
|
49
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex models --refresh
|
|
37
50
|
```
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
在浏览器中授权应用,并允许使用 ChatGPT 套餐。回调监听 `127.0.0.1:1455`,端口被占用时改用其他本机端口。新流程需要浏览器授权,不支持 `--device-code`。
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
dsh plugin --profile web exec dsh-openai-codex login --device-code
|
|
43
|
-
```
|
|
54
|
+
Web 界面打开 **设置 → OpenAI Codex**,点击 **Continue with ChatGPT**,授权后返回。设置页支持账户选择、取消登录、退出登录、刷新模型列表和[管理用量](https://chatgpt.com/settings/usage)。如果只授权了账户身份,会单独显示未授权套餐使用;再次登录即可请求该授权。
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
在 DSH 模型选择器中选择 `openai-codex`。模型列表直接来自当前账户,因此账户支持 GPT-6.1 Sol 等新模型时,无需更新插件内的静态列表。插件保留服务端名称与排序,缓存五分钟,也可手动刷新。
|
|
57
|
+
|
|
58
|
+
## 多账户与凭据
|
|
46
59
|
|
|
47
60
|
```bash
|
|
48
|
-
dsh plugin --profile
|
|
49
|
-
dsh plugin --profile
|
|
61
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex login --new-account
|
|
62
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex accounts
|
|
63
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex use <client-id>
|
|
64
|
+
dsh plugin --profile dsh-tui exec dsh-openai-codex logout
|
|
50
65
|
```
|
|
51
66
|
|
|
52
|
-
|
|
67
|
+
每个账户保留官方签发的应用注册信息,再次授权复用该注册并校验账户身份。退出登录会尝试撤销远程会话、清除当前账户的本地 token,并保留注册信息供下次登录使用;其他账户不受影响。取消正在进行的登录会保留原先的账户。
|
|
53
68
|
|
|
54
|
-
|
|
69
|
+
凭据存储在 `$DSH_HOME/.openai-codex-auth.v2.json`,默认 `~/.dsh/.openai-codex-auth.v2.json`。版本化文件保存稳定的主机标识、账户注册和当前账户。采用原子写入;POSIX 系统下目录仅所有者可访问,文件权限为 `0600`。跨进程文件锁确保刷新 token 时不会重复使用已轮换的凭据。临时刷新失败保留凭据,明确失效的授权会被清除。
|
|
55
70
|
|
|
56
|
-
|
|
71
|
+
**从 0.2 升级:需要重新登录。** 旧 `.openai-codex-auth.json` 中的凭据无法用于新的 API 路由,插件会保留旧文件。插件不导入或修改 `~/.pi/agent/auth.json` 与 `~/.codex/auth.json`。
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
~/.dsh/.openai-codex-auth.json
|
|
60
|
-
```
|
|
73
|
+
## 支持范围与限制
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
普通调用和历史压缩调用都使用 DSH 会话标识设置稳定的 `session-id`、`x-client-request-id` 请求头,以保持网关的会话关联并支持提示缓存复用;缓存命中仍不保证。没有会话标识的调用不发送这两个请求头。
|
|
63
76
|
|
|
64
|
-
|
|
77
|
+
使用 OAuth 请求 `https://api.openai.com/v1/responses`,每次设置 `store: false`、`stream: true`,并通过 `input` 发送所需历史。无需 OpenAI API Key。资格和应用额度由 ChatGPT 套餐控制,插件不会自动切换到 API Key 计费。
|
|
65
78
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
- DSH 函数工具在本地执行,工具定义放在 Responses namespace 中。所有提供的工具定义都会发送;此路由不支持原生 Responses `tool_search`。
|
|
80
|
+
- 文本和图片通过 DSH 附件服务处理。文件和已卸载图片以本地句柄提供给 DSH 工具,不会上传到 OpenAI Files API。
|
|
81
|
+
- 推理档位和上下文信息使用模型目录提供的元数据。只有同一账户、同一模型且内容未改变的块才复用原生 replay 状态;编辑或压缩后的消息使用剩余的 DSH 历史。
|
|
82
|
+
- 设置 `temperature`、`maxTokens` 或 `stop` 会返回 `UNSUPPORTED_OPTION`,因为 ChatGPT 套餐路由不支持这些参数。请在 agent 配置中留空。
|
|
83
|
+
- DSH 历史摘要调用(`purpose: compaction`)接受压缩器自动传入的 `maxTokens` 预算,但不将它发送给 API。自动和手动 DSH 压缩可以执行;此路由不保证压缩器配置的摘要输出上限。摘要指令追加在原有历史之后,以保留可复用的请求前缀。
|
|
84
|
+
- 仅完整完成的响应视为成功。额度不足、未完整完成和连接中断分别处理;账户额度耗尽不会自动重试,临时服务故障遵循 DSH 重试策略。
|
|
85
|
+
- 本适配器不提供 Responses 托管的图片生成、文件搜索、Code Interpreter、电脑操作、MCP 或音视频能力。DSH 本地能力仍可通过函数工具使用。
|
|
86
|
+
|
|
87
|
+
官方限制见 [Preview limitations](https://developers.openai.com/siwc/token-sharing-open-source/preview-limitations)。
|
|
88
|
+
|
|
89
|
+
## 开发与验证
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
### 额度显示
|
|
92
|
+
|
|
93
|
+
Web 设置页提供英文额度条和重置时间。TUI 状态栏显示简短摘要,`/codex-usage` 展开额度窗口及更新时间。按服务端窗口长度识别 5h(300 分钟)和 Weekly(10,080 分钟),不固定假定 primary/secondary 的含义。
|
|
94
|
+
|
|
95
|
+
摘要格式为 `codex 81% 4h36m 50% 5d4m`,较短窗口在前;`*` 表示旧数据,`?` 表示缺少重置时间。dsh-tui 0.14.0 可运行 `node scripts/patch-tui-quota-placement.mjs [Chat.js路径]`,只将这条状态移至输入框下方的模型/推理档位同一行,并保存 Chat.js 和 StatusLine.js 原文件备份。窄终端使用已有字段截断布局,不另加额度行。公开状态 API 固定在输入框上方,因此位置调整需要本机 TUI 补丁;重新安装或升级 TUI 可能覆盖补丁,脚本会拒绝不支持的版本。
|
|
96
|
+
|
|
97
|
+
npm 安装的 TUI profile 可手动应用可选补丁:
|
|
72
98
|
|
|
73
99
|
```bash
|
|
74
|
-
|
|
100
|
+
node "${DSH_HOME:-$HOME/.dsh}/profiles/dsh-tui/node_modules/dsh-codex-oauth/scripts/patch-tui-quota-placement.mjs"
|
|
75
101
|
```
|
|
76
102
|
|
|
77
|
-
|
|
103
|
+
之后重启 TUI。未应用补丁时,摘要使用标准的输入框上方位置;安装插件不会自动修改 TUI。
|
|
104
|
+
|
|
105
|
+
优先使用正常请求的 `x-codex-*` 响应头和 `codex.rate_limits` 流事件。没有新鲜数据时,读取原生登录文件旁保留的 `.openai-codex-auth.json`,用插件自己的旧版 Codex 凭据查询 `https://chatgpt.com/backend-api/wham/usage`。不读取 Pi 或 Codex 应用的登录文件,不改变推理路由,也不调用模型。成功结果缓存 60 秒,查询失败退避 30 秒,并发显示共享一次请求。
|
|
78
106
|
|
|
79
|
-
|
|
107
|
+
此备用来源返回 Codex 订阅额度,尚未证明与原生 SIWC 应用的额度完全相同。两套凭据的邮箱必须一致,但相同邮箱不能证明工作区相同;这些限制只记录在代码和文档里,UI 不加来源标签。旧版凭据缺失、过期、邮箱不匹配或被接口拒绝时保持额度不可用。查询不会刷新或修改旧版登录;过期后需要单独更新该登录。
|
|
80
108
|
|
|
81
|
-
|
|
109
|
+
快照仅保存在当前进程内存中,切换账户或登出时清除;超过五分钟或已过重置时间标记 stale。失败保留上次快照,不推算额度已恢复至 100%;不同接口的数据独立解析,不混合拼接额度窗口。
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install
|
|
113
|
+
npm test
|
|
114
|
+
# 可选:需要已登录的测试账户,会使用 ChatGPT 套餐额度。
|
|
115
|
+
DSH_HOME=/path/to/test-home npm run test:live -- gpt-6.1-sol
|
|
116
|
+
```
|
|
82
117
|
|
|
83
|
-
|
|
84
|
-
- Codex 端点不是公开的 OpenAI Platform API 端点;
|
|
85
|
-
- 文件系统、Shell、MCP、权限、附件等能力仍由当前 DSH profile 提供。
|
|
118
|
+
`npm test` 构建 Web bundle,并验证 OAuth 身份校验、并发刷新、账户切换、Web 路由边界、流式错误、工具及推理 replay、附件,以及与 DSH 官方运行时的集成。真实调用检查执行三轮工具对话并验证历史回传。
|
|
86
119
|
|
|
87
120
|
## 许可证
|
|
88
121
|
|
package/SPEC.md
CHANGED
|
@@ -1,73 +1,55 @@
|
|
|
1
|
-
# dsh-codex-oauth
|
|
1
|
+
# dsh-codex-oauth 0.3 原生适配器规格
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 目标与依赖
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
在 DSH 中使用 OpenAI 官方 Sign in with ChatGPT 直接接入方案。插件直接实现官方 `LlmAdapter`,依赖 DSH `0.2.0-rc.2` 兼容接口、Cordis 和 atomic-write,不依赖 pi-ai,不修改 DSH 源码,不提供 API Key 计费回退。Node.js 最低版本为 22。
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 授权与账户
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- 初次授权使用 `dynamic_agent_client`、稳定的 `ext_agent_host_id`、应用名、state、nonce 和 S256 PKCE;取得签发的 client ID 后,复用该 ID 进行同一账户的再次授权。
|
|
10
|
+
- 本机 HTTP 回调只监听 `127.0.0.1`;校验 state、回调 client ID、ID token 的签名、issuer、audience、有效期和 nonce。再次授权必须保持原账户 subject。
|
|
11
|
+
- 使用官方公开 Responses 资源与 `chatgpt.tokens.use.direct` scope;仅获得身份授权时显示已登录、未授权套餐,禁止推理调用。
|
|
12
|
+
- 提供 `login [--new-account]`、`status`、`accounts`、`use <client-id>`、`models [--refresh]`、`logout`。不支持旧 device-code 流程。
|
|
13
|
+
- Web 设置提供登录、取消、账户选择、退出、模型刷新和官方用量链接。所有控制路由要求本机请求与同源,返回 `no-store`;状态和挑战不含 token。
|
|
14
|
+
- 取消授权不得撤销原有登录。退出登录尝试远程撤销后清除当前账户 token,保留注册与其他账户;未确认远程撤销时向用户说明。
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
2. 执行 `dsh-openai-codex login`;
|
|
13
|
-
3. 插件打开 OpenAI OAuth 授权页;
|
|
14
|
-
4. 用户在浏览器中批准;
|
|
15
|
-
5. localhost 回调完成登录;
|
|
16
|
-
6. DSH 使用 `openai-codex` 模型。
|
|
16
|
+
## 凭据与并发
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
- 文件为 `$DSH_HOME/.openai-codex-auth.v2.json`,包含 version、hostId、registrations、activeClientId 和账户变化 generation。
|
|
19
|
+
- 原子写入,POSIX 文件权限 `0600`、目录 `0700`;拒绝凭据文件符号链接。
|
|
20
|
+
- 跨进程锁保护登录落盘、账户选择、退出和 rotating refresh,避免重复使用旧 refresh token。
|
|
21
|
+
- 临时刷新错误保留凭据,明确失效的 refresh 清除 token,保留注册。
|
|
22
|
+
- 账户切换或退出使准备中的调用失效。已发送的同进程请求会取消;其他进程的已发送请求不由本进程控制。
|
|
23
|
+
- 旧 v1 凭据保留,新路由需要重新授权。不读取或修改 Pi、Codex 的凭据文件。
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
## 模型目录
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
通过与推理相同的 OAuth token 查询 `GET https://api.openai.com/v1/models`。展示 `visibility: list` 的条目,保持服务端排序,以 display_name 展示、slug 请求。上下文、输入模态和推理档位使用服务器提供的信息;未知模型可按显式 ID 请求,最终权限由服务器判定。
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
目录按当前授权缓存五分钟,账户变化后失效,可手动刷新。目录请求过程中切换账户,即使随后切回,也不得将旧结果绑定到新授权。
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
## 请求、输出和历史
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
- `POST https://api.openai.com/v1/responses`,固定 `store: false`、`stream: true`,input 数组携带所需历史;不使用 backend-api 或 previous_response_id。
|
|
34
|
+
- system 转为 instructions 或 developer 消息。函数工具置于 namespace,保留原始参数文本和 call ID,回传 function_call_output。工具执行由 DSH 负责。
|
|
35
|
+
- 不发送此路由不支持的 sampling、输出上限、托管工具或原生 tool_search。设置 temperature、maxTokens、stop 时明确报 UNSUPPORTED_OPTION。
|
|
36
|
+
- DSH 的 purpose: compaction 调用接受压缩器自动传入的 maxTokens,但不发送输出上限字段,避免阻断历史压缩;此通道不保证摘要输出上限。普通调用仍拒绝显式 maxTokens,压缩调用也仍拒绝 temperature 和 stop。
|
|
37
|
+
- 图片使用 DSH 附件请求表示,单张编码目标 1 MiB、约 4M 像素,整次请求 base64 预算 32 MiB。超预算要求 DSH 卸载图片;文件与已卸载图片提供本地工具句柄。
|
|
38
|
+
- SSE 转换为 DSH 的 text、reasoning、tool-call 块、usage 和 finish。以 response.completed 判定成功;失败、未完成、无终态中断分别处理。缺失 Content-Type 的有效 SSE 仍可读取。
|
|
39
|
+
- 未闭合 UTF-8/SSE 块、取消和空响应不得伪装成成功。五分钟无数据则终止流。
|
|
40
|
+
- 保存 output_item.done 的完整 reasoning(含 encrypted_content)和工具项;与 DSH 可见块指纹绑定。同账户、同模型且块未改变时回传完整原生项;编辑、模型切换、账户变化后退化为 DSH 通用历史。上下文压缩由 DSH 处理。
|
|
41
|
+
- 用量区分普通输入与缓存输入;输出已包含推理 token。账户应用额度耗尽不自动重试,临时服务不可用交给 DSH 重试策略。
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
## 额度显示
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
- 优先解析正常响应的 `x-codex-*` 头和 `codex.rate_limits` 事件;缺少新鲜数据时用插件保留的旧 v1 Codex 凭据只读查询 WHAM,不用原生 SIWC token 查询该接口。
|
|
46
|
+
- 旧凭据文件需满足私有文件权限,两套凭据邮箱匹配且 access token 未过期;不刷新、不修改、不读取其他应用的凭据。此来源与原生 SIWC 应用额度的等价关系未确认,文档说明来源限制。
|
|
47
|
+
- 成功缓存 60 秒,失败退避 30 秒,并发共享请求;账户变化、登出和卸载阻止旧查询更新。无数据不伪造额度,过期快照标记 stale。
|
|
48
|
+
- Web 显示英文额度条;TUI 摘要为 `codex 81% 4h36m 50% 5d4m`,`/codex-usage` 展开详情。不调用模型。
|
|
49
|
+
- 模型/推理档位同一行的位置通过可选的本机 dsh-tui 0.14.0 补丁实现;默认公开接口仍位于输入框上方,安装不自动应用布局补丁。
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
- 格式:版本化 JSON,字段为 `version` 和 `credential`;
|
|
40
|
-
- `credential` 保留 `type`, `access`, `refresh`, `expires`, `accountId`;
|
|
41
|
-
- 原子写入,文件权限 `0600`,目录权限 `0700`;
|
|
42
|
-
- 刷新、登录、登出使用跨进程文件锁;
|
|
43
|
-
- 不读取、复制、修改 `~/.pi/agent/auth.json` 或 `~/.codex/auth.json`。
|
|
51
|
+
## 验收
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
通过行为边界测试验证独立注册和签名校验、轮换刷新并发、临时/永久刷新失败、多账户退出、取消授权、Web 边界、目录刷新及账户变化、SSE 分块与中断、工具/推理回传、附件和 DSH 官方运行时集成。
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
- 使用 DSH 公共 `PiAiAdapter`;
|
|
49
|
-
- 使用 Codex Responses endpoint,不伪装成普通 OpenAI Platform API;
|
|
50
|
-
- 保留流式输出、工具调用、推理、图片、replay 和 compaction;
|
|
51
|
-
- 新建 agent 默认模型沿用 Codex catalog 中的可用模型,不硬编码不存在的模型。
|
|
52
|
-
|
|
53
|
-
## 安全边界
|
|
54
|
-
|
|
55
|
-
- OAuth 密码、授权码、access token、refresh token 不进入日志、session、Git 或错误信息;
|
|
56
|
-
- Web 登录回调只接受 localhost 同源请求,并返回 `no-store`;
|
|
57
|
-
- 插件移除不自动删除凭据;登出必须由用户明确执行;
|
|
58
|
-
- 不实现普通 OpenAI API Key 兼容层。
|
|
59
|
-
|
|
60
|
-
## 明确不做
|
|
61
|
-
|
|
62
|
-
- 不依赖 Pi;
|
|
63
|
-
- 不导入 Pi 的 auth 文件;
|
|
64
|
-
- 不修改 DSH 源码;
|
|
65
|
-
- 第一版不加入独立搜索、图片生成和额度面板;这些属于后续功能。
|
|
66
|
-
|
|
67
|
-
## 验收标准
|
|
68
|
-
|
|
69
|
-
- 干净环境安装插件后,`status` 返回 signed out;
|
|
70
|
-
- CLI OAuth 登录后,`status` 返回 signed in,且不打印 token;
|
|
71
|
-
- access token 过期后请求会自动刷新并持久化;
|
|
72
|
-
- 删除 Pi auth 文件不影响 DSH 插件;
|
|
73
|
-
- Web profile 能加载 `openai-codex`,且公共 DSH adapter 的图片、工具和 replay 测试继续通过。
|
|
55
|
+
在隔离 profile 中进行真实 OAuth 授权、动态模型查询、GPT-6.1 Sol 流式调用、工具执行与后续历史回传,再更新本机使用的 profile。发布、Git 提交和推送需另行授权。
|
package/bin/dsh-openai-codex.js
CHANGED
|
@@ -1,46 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { accounts, authPath, login, logout, status } from "../lib/auth.js";
|
|
4
|
+
import { CodexCredentialStore } from "../lib/store.js";
|
|
5
5
|
import { safeMessage } from "../lib/safe-message.js";
|
|
6
6
|
|
|
7
7
|
function openBrowser(raw) {
|
|
8
8
|
const url = new URL(raw);
|
|
9
|
-
if (url.
|
|
9
|
+
if (url.origin !== "https://auth.openai.com") throw new Error("Refusing to open an unexpected OAuth URL");
|
|
10
10
|
const command = process.platform === "darwin" ? ["open", url.href] : process.platform === "win32" ? ["rundll32.exe", "url.dll,FileProtocolHandler", url.href] : ["xdg-open", url.href];
|
|
11
11
|
const child = spawn(command[0], command.slice(1), { detached: true, stdio: "ignore", windowsHide: true });
|
|
12
12
|
child.on("error", () => {}); child.unref();
|
|
13
13
|
}
|
|
14
14
|
function help() {
|
|
15
|
-
console.log("Usage: dsh-openai-codex <
|
|
15
|
+
console.log("Usage: dsh-openai-codex login [--new-account]\n dsh-openai-codex <logout|status|accounts>\n dsh-openai-codex use <client-id>\n dsh-openai-codex models [--refresh]");
|
|
16
16
|
}
|
|
17
|
-
|
|
18
17
|
const [action, ...flags] = process.argv.slice(2);
|
|
19
18
|
if (!action || action === "--help" || action === "-h") { help(); process.exit(0); }
|
|
20
|
-
if (
|
|
19
|
+
if (flags.includes("--device-code")) {
|
|
20
|
+
console.error("ChatGPT plan OAuth requires browser authorization with a local callback; --device-code is unavailable. Run login on your local computer.");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const valid = action === "login" ? flags.length <= 1 && (!flags.length || flags[0] === "--new-account")
|
|
24
|
+
: action === "models" ? flags.length <= 1 && (!flags.length || flags[0] === "--refresh")
|
|
25
|
+
: action === "use" ? flags.length === 1 && !flags[0].startsWith("-")
|
|
26
|
+
: ["logout", "status", "accounts"].includes(action) && !flags.length;
|
|
27
|
+
if (!valid) { help(); process.exit(1); }
|
|
28
|
+
const store = new CodexCredentialStore();
|
|
21
29
|
try {
|
|
22
30
|
if (action === "status") {
|
|
23
|
-
const value = await status();
|
|
24
|
-
if (!value.authenticated) { console.log("OpenAI Codex for dsh: signed out"); process.exitCode = 1; }
|
|
25
|
-
else console.log(`OpenAI Codex for dsh: signed in; refresh is automatic`);
|
|
31
|
+
const value = await status(store);
|
|
32
|
+
if (!value.authenticated) { console.log("OpenAI Codex for dsh: signed out; run login to authorize ChatGPT plan usage"); process.exitCode = 1; }
|
|
33
|
+
else console.log(`OpenAI Codex for dsh: signed in as ${value.label}; ChatGPT plan usage ${value.planUsage ? "enabled" : "not authorized"}; refresh is automatic`);
|
|
26
34
|
} else if (action === "logout") {
|
|
27
|
-
|
|
35
|
+
const result = await logout(store);
|
|
36
|
+
console.log("OpenAI Codex for dsh: signed out; account registration retained for later sign-in");
|
|
37
|
+
if (!result.remoteRevoked) console.log("Remote session revocation could not be confirmed. You can disconnect the app at https://chatgpt.com/settings/usage");
|
|
38
|
+
} else if (action === "accounts") {
|
|
39
|
+
const list = await accounts(store);
|
|
40
|
+
for (const account of list) console.log(`${account.active ? "*" : " "} ${account.clientId}\t${account.label}\t${account.authenticated ? "signed in" : "signed out"}`);
|
|
41
|
+
if (!list.length) console.log("No saved ChatGPT account registrations");
|
|
42
|
+
} else if (action === "use") {
|
|
43
|
+
await store.select(flags[0]);
|
|
44
|
+
console.log("Selected ChatGPT account registration");
|
|
45
|
+
} else if (action === "models") {
|
|
46
|
+
const { createCodexAdapter } = await import("../lib/adapter.js");
|
|
47
|
+
const adapter = createCodexAdapter(store);
|
|
48
|
+
try {
|
|
49
|
+
for (const model of await adapter.listModels("openai-codex", { refresh: flags.includes("--refresh") })) console.log(`${model.id}\t${model.name}`);
|
|
50
|
+
} finally { adapter.dispose(); }
|
|
28
51
|
} else {
|
|
29
|
-
const
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const cancel = () => controller.abort(new Error("Sign-in cancelled"));
|
|
54
|
+
process.once("SIGINT", cancel);
|
|
30
55
|
try {
|
|
31
|
-
await login(
|
|
32
|
-
|
|
33
|
-
if (prompt.type === "select") return flags.includes("--device-code") ? "device_code" : "browser";
|
|
34
|
-
return readline.question(`${prompt.message}: `, { signal: prompt.signal });
|
|
35
|
-
},
|
|
56
|
+
const value = await login(store, {
|
|
57
|
+
signal: controller.signal,
|
|
36
58
|
notify: (event) => {
|
|
37
|
-
if (event.type
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
if (event.type !== "auth_url") return;
|
|
60
|
+
const display = new URL(event.url);
|
|
61
|
+
display.searchParams.delete("id_token_hint");
|
|
62
|
+
console.log(`Open this URL to sign in:\n${display.href}`);
|
|
63
|
+
openBrowser(event.url);
|
|
40
64
|
},
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
}, { newAccount: flags.includes("--new-account") });
|
|
66
|
+
console.log(`OpenAI Codex for dsh: signed in; credentials saved to ${authPath()}`);
|
|
67
|
+
console.log(value.planUsage ? "Eligible requests use your ChatGPT plan. Manage usage: https://chatgpt.com/settings/usage" : "ChatGPT plan usage was not authorized. Run login again to enable it.");
|
|
68
|
+
} finally { process.removeListener("SIGINT", cancel); }
|
|
44
69
|
}
|
|
45
70
|
} catch (error) {
|
|
46
71
|
console.error(`dsh-openai-codex: ${safeMessage(error)}`);
|