dsh-codex-subscription 1.0.5 → 1.1.0-beta.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/AGENTS.md +4 -4
- package/README.en.md +16 -1
- package/README.md +15 -0
- package/lib/client.js +143 -35
- package/lib/index.js +42 -11
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ Use this guide when a user asks an Agent to install, update, verify, or remove
|
|
|
6
6
|
## Safety
|
|
7
7
|
|
|
8
8
|
- Confirm the target DSH installation and profile. Use `web` only when it is the user's target.
|
|
9
|
-
- Use the exact `1.0.
|
|
9
|
+
- Use the exact `1.0.6` package below; do not install a moving branch.
|
|
10
10
|
- Never print OAuth credentials, account IDs, authorization callbacks, or credential-store contents.
|
|
11
11
|
- Preserve the DSH profile, unrelated plugins, sessions, and saved sign-in.
|
|
12
12
|
- Do not start, stop, or restart DSH without explicit permission.
|
|
@@ -31,13 +31,13 @@ instead of recreating its package-manager environment in this installer.
|
|
|
31
31
|
With an existing `dsh` command:
|
|
32
32
|
|
|
33
33
|
```sh
|
|
34
|
-
dsh plugin --profile web add dsh-codex-subscription@1.0.
|
|
34
|
+
dsh plugin --profile web add dsh-codex-subscription@1.0.6
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
From a DSH-Portable folder:
|
|
38
38
|
|
|
39
39
|
```powershell
|
|
40
|
-
.\dsh.exe plugin --profile web add dsh-codex-subscription@1.0.
|
|
40
|
+
.\dsh.exe plugin --profile web add dsh-codex-subscription@1.0.6
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
Use the same `add` command to update or repair. This is the complete package-changing operation.
|
|
@@ -61,7 +61,7 @@ dsh --profile web --dump-config
|
|
|
61
61
|
|
|
62
62
|
For DSH-Portable, replace `dsh` with `.\dsh.exe`. Static acceptance requires:
|
|
63
63
|
|
|
64
|
-
1. `dsh-codex-subscription` version `1.0.
|
|
64
|
+
1. `dsh-codex-subscription` version `1.0.6` appears exactly once.
|
|
65
65
|
2. `codex-subscription` appears exactly once in the composed config.
|
|
66
66
|
3. The legacy package is absent after a migration.
|
|
67
67
|
4. No unrelated plugin or profile was changed and DSH was not restarted.
|
package/README.en.md
CHANGED
|
@@ -8,6 +8,7 @@ No OpenAI API key or Codex CLI. Models, search, quota, and image generation stay
|
|
|
8
8
|
|
|
9
9
|
[](https://github.com/WSL043/dsh-codex-subscription/actions/workflows/ci.yml)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-codex-subscription)
|
|
11
|
+
[](https://www.npmjs.com/package/dsh-codex-subscription)
|
|
11
12
|
[](LICENSE)
|
|
12
13
|
[](https://github.com/WSL043/dsh-codex-subscription/stargazers)
|
|
13
14
|
|
|
@@ -27,8 +28,9 @@ No OpenAI API key or Codex CLI. Models, search, quota, and image generation stay
|
|
|
27
28
|
| **Subscription models** | Sign in to ChatGPT and use Codex without an OpenAI API key or Codex CLI |
|
|
28
29
|
| **Subscription search** | Explicitly choose DSH default search or Codex subscription search |
|
|
29
30
|
| **Visible quota** | Keep backend-provided standard Codex, Spark, and other limits separate |
|
|
31
|
+
| **Fast mode (Beta)** | Switch between Standard and Fast directly in the composer |
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
These capabilities reuse the same local ChatGPT sign-in. Subscription routing failures stay visible and never silently switch to another paid route.
|
|
32
34
|
|
|
33
35
|
## Product screen
|
|
34
36
|
|
|
@@ -52,6 +54,12 @@ This plugin supports DeepSeek Harness `0.1.0-rc.6`, `0.1.0-rc.7`, and `0.1.0-rc.
|
|
|
52
54
|
|
|
53
55
|
## Install
|
|
54
56
|
|
|
57
|
+
To preview the composer Fast mode without replacing npm `latest`, install the Beta explicitly:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
dsh plugin --profile web add dsh-codex-subscription@1.1.0-beta.0
|
|
61
|
+
```
|
|
62
|
+
|
|
55
63
|
### Let an Agent install it (recommended)
|
|
56
64
|
|
|
57
65
|
Send this link directly to your Agent:
|
|
@@ -106,6 +114,7 @@ Restart DSH manually after installation, then:
|
|
|
106
114
|
- Actual backend-provided quota, reset time, and freshness;
|
|
107
115
|
- Separate standard Codex, Codex-Spark, Credits, and other independent limits;
|
|
108
116
|
- Optional composer quota for the selected Codex model (Beta, off by default);
|
|
117
|
+
- Standard or Fast mode for supported Codex models directly in the composer (Beta);
|
|
109
118
|
- Visible errors when subscription routing is unavailable, with no silent paid fallback.
|
|
110
119
|
|
|
111
120
|
### Composer quota
|
|
@@ -118,6 +127,12 @@ The compact percentage appears only for a selected Codex model. Standard Codex u
|
|
|
118
127
|
window returned by the service; Spark uses its independent quota. The plugin does not hard-code a
|
|
119
128
|
“5-hour + weekly” layout or invent Credits and spending caps that the service did not return.
|
|
120
129
|
|
|
130
|
+
### Composer speed (Beta)
|
|
131
|
+
|
|
132
|
+
With a supported Codex model selected, `1×` or a lightning icon appears immediately before the model name.
|
|
133
|
+
Click it to choose Standard or Fast. Spark does not show this control. Fast mode increases speed and uses more Credits;
|
|
134
|
+
see the [OpenAI Codex Speed documentation](https://learn.chatgpt.com/docs/agent-configuration/speed) for the current rules.
|
|
135
|
+
|
|
121
136
|
## Update and uninstall
|
|
122
137
|
|
|
123
138
|
On Windows, rerun the one-line setup above to update. Use the official DSH command to uninstall. Both
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
[](https://github.com/WSL043/dsh-codex-subscription/actions/workflows/ci.yml)
|
|
11
11
|
[](https://www.npmjs.com/package/dsh-codex-subscription)
|
|
12
|
+
[](https://www.npmjs.com/package/dsh-codex-subscription)
|
|
12
13
|
[](LICENSE)
|
|
13
14
|
[](https://github.com/WSL043/dsh-codex-subscription/stargazers)
|
|
14
15
|
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
| **订阅模型直连** | 登录 ChatGPT 后直接使用 Codex,不需要 OpenAI API Key 或 Codex CLI |
|
|
29
30
|
| **订阅搜索** | 可在 DSH 默认搜索与 Codex 订阅搜索之间明确切换 |
|
|
30
31
|
| **额度可见** | 普通 Codex、Spark 等服务端实际返回的额度分开显示 |
|
|
32
|
+
| **高速模式(Beta)** | 直接在输入框切换标准或高速,无需离开当前会话 |
|
|
31
33
|
|
|
32
34
|
这些能力共用同一份本机 ChatGPT 登录。订阅路由失败时会明确报错,不会静默切换到其他付费路由。
|
|
33
35
|
|
|
@@ -53,6 +55,12 @@
|
|
|
53
55
|
|
|
54
56
|
## 安装
|
|
55
57
|
|
|
58
|
+
想先体验输入框高速模式,可安装 Beta;它不会替换 npm 的稳定版:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
dsh plugin --profile web add dsh-codex-subscription@1.1.0-beta.0
|
|
62
|
+
```
|
|
63
|
+
|
|
56
64
|
### 交给 Agent(推荐)
|
|
57
65
|
|
|
58
66
|
把这个链接直接发给 Agent:
|
|
@@ -107,6 +115,7 @@ dsh --profile web --dump-config
|
|
|
107
115
|
- 设置页显示服务端返回的额度、重置时间和更新时间;
|
|
108
116
|
- 普通 Codex、Codex-Spark、Credits 等独立额度分开显示;
|
|
109
117
|
- 输入框可显示当前 Codex 模型的剩余额度(Beta,默认关闭);
|
|
118
|
+
- 输入框可为支持的 Codex 模型切换标准或高速模式(Beta);
|
|
110
119
|
- 订阅路由不可用时明确报错,不会静默切换到其他付费路由。
|
|
111
120
|
|
|
112
121
|
### 输入框额度
|
|
@@ -118,6 +127,12 @@ dsh --profile web --dump-config
|
|
|
118
127
|
快捷百分比只在选择 Codex 模型时显示。普通 Codex 使用服务端返回窗口中剩余最少的一项,
|
|
119
128
|
Spark 使用独立额度。插件不会写死“5 小时 + 每周”,也不会虚构服务端没有返回的 Credits 或消费上限。
|
|
120
129
|
|
|
130
|
+
### 输入框速度(Beta)
|
|
131
|
+
|
|
132
|
+
选择支持的 Codex 模型后,模型名称左侧会显示 `1×` 或闪电。点击即可切换标准与高速;
|
|
133
|
+
Spark 不显示这个入口。高速模式会提高速度,也会消耗更多 Credits;具体规则见
|
|
134
|
+
[OpenAI Codex Speed 文档](https://learn.chatgpt.com/docs/agent-configuration/speed)。
|
|
135
|
+
|
|
121
136
|
## 更新与卸载
|
|
122
137
|
|
|
123
138
|
Windows 用户重新运行上面的单行助手即可更新。卸载使用 DSH 官方命令;两种操作都保留
|
package/lib/client.js
CHANGED
|
@@ -12,7 +12,13 @@ window.__ModuleLoader__.load({
|
|
|
12
12
|
const QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
13
13
|
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
14
14
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
15
|
+
const SPEED_MODE_FIELD = "speedMode";
|
|
16
|
+
const SPEED_MODE_STANDARD = "standard";
|
|
17
|
+
const SPEED_MODE_FAST = "fast";
|
|
18
|
+
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
15
19
|
const normalizeSearchProvider = (value) => ["dsh", "codex"].includes(value) ? value : "dsh";
|
|
20
|
+
const normalizeSpeedMode = (value) => ["standard", "fast"].includes(value) ? value : DEFAULT_SPEED_MODE;
|
|
21
|
+
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
16
22
|
//#endregion
|
|
17
23
|
//#region src/sidebar-quota.js
|
|
18
24
|
const isDisplayableWindow = (window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= 100 && Number.isFinite(window?.windowSeconds) && window.windowSeconds > 0;
|
|
@@ -62,13 +68,15 @@ window.__ModuleLoader__.load({
|
|
|
62
68
|
deviceHint: "在登录页输入此设备代码:",
|
|
63
69
|
waiting: "正在等待登录完成…",
|
|
64
70
|
failed: "登录失败,请重试。",
|
|
65
|
-
loadFailed: "
|
|
71
|
+
loadFailed: "无法读取账户状态。",
|
|
72
|
+
accountRetry: "重试",
|
|
66
73
|
searchTitle: "搜索来源",
|
|
67
74
|
searchDsh: "DSH 默认",
|
|
68
75
|
searchDshHint: "当前搜索服务",
|
|
69
76
|
searchCodex: "Codex 订阅",
|
|
70
77
|
searchCodexHint: "ChatGPT 订阅搜索",
|
|
71
|
-
preferenceFailed: "
|
|
78
|
+
preferenceFailed: "设置未保存。",
|
|
79
|
+
preferenceRetry: "重试",
|
|
72
80
|
usage: "订阅额度",
|
|
73
81
|
refresh: "刷新",
|
|
74
82
|
refreshing: "刷新中…",
|
|
@@ -99,6 +107,11 @@ window.__ModuleLoader__.load({
|
|
|
99
107
|
quickQuotaSetting: "输入框额度",
|
|
100
108
|
quickQuotaBeta: "Beta",
|
|
101
109
|
quickQuotaStatus: "Codex 剩余额度 {value}%",
|
|
110
|
+
speedTitle: "速度",
|
|
111
|
+
speedStandard: "标准",
|
|
112
|
+
speedStandardHint: "标准速度",
|
|
113
|
+
speedFast: "高速",
|
|
114
|
+
speedFastHint: "1.5 倍,消耗更多 Credits",
|
|
102
115
|
imageGenerate: "生成图片",
|
|
103
116
|
imageGenerating: "正在生成…",
|
|
104
117
|
imageGenerated: "已生成",
|
|
@@ -127,13 +140,15 @@ window.__ModuleLoader__.load({
|
|
|
127
140
|
deviceHint: "Enter this device code on the sign-in page:",
|
|
128
141
|
waiting: "Waiting for sign-in to finish…",
|
|
129
142
|
failed: "Sign-in failed. Try again.",
|
|
130
|
-
loadFailed: "Could not read
|
|
143
|
+
loadFailed: "Could not read account status.",
|
|
144
|
+
accountRetry: "Retry",
|
|
131
145
|
searchTitle: "Search source",
|
|
132
146
|
searchDsh: "DSH default",
|
|
133
147
|
searchDshHint: "Current search service",
|
|
134
148
|
searchCodex: "Codex subscription",
|
|
135
149
|
searchCodexHint: "ChatGPT subscription search",
|
|
136
|
-
preferenceFailed: "
|
|
150
|
+
preferenceFailed: "The setting was not saved.",
|
|
151
|
+
preferenceRetry: "Retry",
|
|
137
152
|
usage: "Subscription quota",
|
|
138
153
|
refresh: "Refresh",
|
|
139
154
|
refreshing: "Refreshing…",
|
|
@@ -164,6 +179,11 @@ window.__ModuleLoader__.load({
|
|
|
164
179
|
quickQuotaSetting: "Composer quota",
|
|
165
180
|
quickQuotaBeta: "Beta",
|
|
166
181
|
quickQuotaStatus: "Codex quota: {value}% remaining",
|
|
182
|
+
speedTitle: "Speed",
|
|
183
|
+
speedStandard: "Standard",
|
|
184
|
+
speedStandardHint: "Standard speed",
|
|
185
|
+
speedFast: "Fast",
|
|
186
|
+
speedFastHint: "1.5x; higher Credits use",
|
|
167
187
|
imageGenerate: "Generate image",
|
|
168
188
|
imageGenerating: "Generating…",
|
|
169
189
|
imageGenerated: "Generated",
|
|
@@ -191,6 +211,7 @@ window.__ModuleLoader__.load({
|
|
|
191
211
|
.codexSubscriptionActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.codexSubscriptionFlow{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-module-platform)}
|
|
192
212
|
.codexSubscriptionFlow p{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCode{width:max-content;max-width:100%;font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;overflow-wrap:anywhere}
|
|
193
213
|
.codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}.codexSubscriptionInput{width:100%;box-sizing:border-box}
|
|
214
|
+
.codexSubscriptionRecover{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionRecover .codexSubscriptionError{flex:1}.codexSubscriptionRecover button{flex:0 0 auto}
|
|
194
215
|
.codexSubscriptionSectionTitle{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionFreshness{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
195
216
|
.codexSubscriptionRefresh{flex:0 0 auto;min-width:72px;width:max-content;white-space:nowrap!important;word-break:keep-all!important;overflow-wrap:normal!important;writing-mode:horizontal-tb!important}.codexSubscriptionRefresh *{white-space:nowrap!important;word-break:keep-all!important;writing-mode:horizontal-tb!important}
|
|
196
217
|
.codexSubscriptionEmpty{padding:18px;border:1px dashed var(--dsw-alias-border-l3);border-radius:10px;text-align:center;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
|
|
@@ -202,6 +223,7 @@ window.__ModuleLoader__.load({
|
|
|
202
223
|
.codexSubscriptionCreditBalance{display:flex;flex-direction:column;gap:6px}.codexSubscriptionCreditBalance span,.codexSubscriptionCreditLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCreditBalance strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
|
203
224
|
.codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}.codexSubscriptionSpendLimit progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
|
|
204
225
|
.codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;margin-right:-4px;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
|
|
226
|
+
.codexComposerControls{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto}.codexComposerSpeed{display:inline-flex;align-items:center;justify-content:center;min-width:28px;height:28px;box-sizing:border-box;padding:0 7px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);font:500 12px/20px inherit;cursor:pointer;white-space:nowrap}.codexComposerSpeed:hover,.codexComposerSpeed[aria-expanded=true]{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.codexComposerSpeed:disabled{cursor:not-allowed;opacity:.5}.codexComposerSpeedFast{font-size:14px}.codexComposerSpeedChoice{display:flex;min-width:150px;flex-direction:column;gap:1px}.codexComposerSpeedChoice strong{font-size:13px;line-height:18px;font-weight:500}.codexComposerSpeedChoice span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
|
|
205
227
|
.codexImageTool{display:flex;flex-direction:column;gap:8px;margin:4px 0;color:var(--dsw-alias-label-primary)}.codexImageToolRow{display:flex;align-items:center;min-height:24px;gap:8px;font-size:13px;line-height:20px}.codexImageToolIcon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dsw-alias-label-secondary)}.codexImageToolIcon::before{content:'';width:8px;height:8px;border:1.5px solid currentColor;border-radius:3px}.codexImageTool[data-state=running] .codexImageToolIcon::before{border-radius:50%;border-right-color:transparent;animation:codexImageSpin 800ms linear infinite}.codexImageTool[data-state=error] .codexImageToolIcon::before{border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-primary)}.codexImageToolTitle{font-weight:500}.codexImageToolState{color:var(--dsw-alias-label-tertiary)}.codexImageToolError{margin:0 0 0 24px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary)}.codexImageToolGallery{margin-left:24px}.codexGeneratedImageFrame{display:flex;align-items:center;justify-content:center;width:min(240px,100%);min-height:120px;padding:0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);cursor:pointer}.codexGeneratedImageFrame img{display:block;width:100%;max-height:240px;object-fit:contain}.codexGeneratedImageRetry{min-height:36px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);cursor:pointer}.codexGeneratedImageLightbox{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:32px;border:0;background:rgba(0,0,0,.72)}.codexGeneratedImageLightbox img{display:block;max-width:min(1100px,calc(100vw - 64px));max-height:calc(100vh - 64px);object-fit:contain}.codexGeneratedImageClose{position:absolute;top:16px;right:16px;width:36px;height:36px;border:1px solid rgba(255,255,255,.35);border-radius:50%;background:rgba(0,0,0,.48);color:#fff;font-size:20px;line-height:1;cursor:pointer}@keyframes codexImageSpin{to{transform:rotate(360deg)}}
|
|
206
228
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
207
229
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
@@ -357,6 +379,7 @@ window.__ModuleLoader__.load({
|
|
|
357
379
|
let error = false;
|
|
358
380
|
let fallbackStatus = "loading";
|
|
359
381
|
let fallback;
|
|
382
|
+
let failedPatch;
|
|
360
383
|
let generation = 0;
|
|
361
384
|
const nativeSnapshot = () => scope.getSnapshot();
|
|
362
385
|
const read = () => {
|
|
@@ -366,6 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
366
389
|
status: updating ? "updating" : current.status,
|
|
367
390
|
visible: current.value?.[QUICK_QUOTA_FIELD] === true,
|
|
368
391
|
searchProvider: normalizeSearchProvider(current.value?.[SEARCH_PROVIDER_FIELD]),
|
|
392
|
+
speedMode: normalizeSpeedMode(current.value?.[SPEED_MODE_FIELD]),
|
|
369
393
|
writable: !updating && current.status === "ready" && current.writable === true,
|
|
370
394
|
error
|
|
371
395
|
});
|
|
@@ -378,6 +402,7 @@ window.__ModuleLoader__.load({
|
|
|
378
402
|
};
|
|
379
403
|
const disposeScope = scope.subscribe(() => {
|
|
380
404
|
error = false;
|
|
405
|
+
failedPatch = void 0;
|
|
381
406
|
publish();
|
|
382
407
|
});
|
|
383
408
|
const acceptFallback = (value) => {
|
|
@@ -386,7 +411,8 @@ window.__ModuleLoader__.load({
|
|
|
386
411
|
status: "ready",
|
|
387
412
|
value: {
|
|
388
413
|
[QUICK_QUOTA_FIELD]: value?.[QUICK_QUOTA_FIELD] === true,
|
|
389
|
-
[SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD])
|
|
414
|
+
[SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
415
|
+
[SPEED_MODE_FIELD]: normalizeSpeedMode(value?.[SPEED_MODE_FIELD])
|
|
390
416
|
},
|
|
391
417
|
writable: value?.writable === true
|
|
392
418
|
};
|
|
@@ -416,6 +442,7 @@ window.__ModuleLoader__.load({
|
|
|
416
442
|
const entries = Object.entries(patch);
|
|
417
443
|
updating = true;
|
|
418
444
|
error = false;
|
|
445
|
+
failedPatch = void 0;
|
|
419
446
|
publish();
|
|
420
447
|
try {
|
|
421
448
|
if (nativeSnapshot().status === "ready") {
|
|
@@ -432,7 +459,10 @@ window.__ModuleLoader__.load({
|
|
|
432
459
|
acceptFallback(value);
|
|
433
460
|
}
|
|
434
461
|
} catch {
|
|
435
|
-
if (current === generation)
|
|
462
|
+
if (current === generation) {
|
|
463
|
+
error = true;
|
|
464
|
+
failedPatch = patch;
|
|
465
|
+
}
|
|
436
466
|
} finally {
|
|
437
467
|
if (current === generation) {
|
|
438
468
|
updating = false;
|
|
@@ -448,6 +478,7 @@ window.__ModuleLoader__.load({
|
|
|
448
478
|
},
|
|
449
479
|
load,
|
|
450
480
|
set,
|
|
481
|
+
retry: () => failedPatch === void 0 ? load() : set(failedPatch),
|
|
451
482
|
dispose: disposeScope
|
|
452
483
|
};
|
|
453
484
|
}
|
|
@@ -573,28 +604,86 @@ window.__ModuleLoader__.load({
|
|
|
573
604
|
preference,
|
|
574
605
|
t
|
|
575
606
|
}),
|
|
576
|
-
snapshot.error ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
577
|
-
className: "
|
|
607
|
+
snapshot.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
608
|
+
className: "codexSubscriptionRecover",
|
|
578
609
|
role: "alert",
|
|
579
|
-
children:
|
|
610
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
611
|
+
className: "codexSubscriptionError",
|
|
612
|
+
children: t("preferenceFailed")
|
|
613
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
614
|
+
type: "button",
|
|
615
|
+
variant: "outline",
|
|
616
|
+
onClick: () => {
|
|
617
|
+
preference.retry();
|
|
618
|
+
},
|
|
619
|
+
children: t("preferenceRetry")
|
|
620
|
+
})]
|
|
580
621
|
}) : null
|
|
581
622
|
]
|
|
582
623
|
});
|
|
583
624
|
}
|
|
584
|
-
function
|
|
625
|
+
function CodexComposerControls({ preference, rpc, t, directory }) {
|
|
626
|
+
const [speedOpen, setSpeedOpen] = (0, react.useState)(false);
|
|
585
627
|
const preferenceSnapshot = usePreferenceSnapshot(preference);
|
|
586
628
|
const current = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot()).current;
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
const
|
|
629
|
+
const codex = current?.provider === "openai-codex";
|
|
630
|
+
const speedSupported = codex && supportsCodexFastMode(current?.model);
|
|
631
|
+
const speedWritable = preferenceSnapshot.status === "ready" && preferenceSnapshot.writable === true;
|
|
632
|
+
const fast = preferenceSnapshot.speedMode === SPEED_MODE_FAST;
|
|
633
|
+
const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.visible && codex;
|
|
634
|
+
const quota = useQuickQuota(rpc, quotaEnabled, current?.model);
|
|
635
|
+
if (!speedSupported && (!quotaEnabled || quota === void 0)) return null;
|
|
636
|
+
const speedItems = [{
|
|
637
|
+
id: SPEED_MODE_STANDARD,
|
|
638
|
+
label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
639
|
+
className: "codexComposerSpeedChoice",
|
|
640
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("speedStandard") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("speedStandardHint") })]
|
|
641
|
+
})
|
|
642
|
+
}, {
|
|
643
|
+
id: SPEED_MODE_FAST,
|
|
644
|
+
label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
645
|
+
className: "codexComposerSpeedChoice",
|
|
646
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("speedFast") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("speedFastHint") })]
|
|
647
|
+
})
|
|
648
|
+
}];
|
|
649
|
+
const speed = speedSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
650
|
+
open: speedOpen,
|
|
651
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
652
|
+
className: `codexComposerSpeed${fast ? " codexComposerSpeedFast" : ""}`,
|
|
653
|
+
type: "button",
|
|
654
|
+
"aria-label": `${t("speedTitle")}: ${t(fast ? "speedFast" : "speedStandard")}`,
|
|
655
|
+
"aria-expanded": speedOpen,
|
|
656
|
+
disabled: !speedWritable,
|
|
657
|
+
onClick: () => setSpeedOpen((value) => !value),
|
|
658
|
+
children: fast ? "⚡" : "1×"
|
|
659
|
+
}),
|
|
660
|
+
items: speedItems,
|
|
661
|
+
selectedId: preferenceSnapshot.speedMode,
|
|
662
|
+
onSelect: (id) => {
|
|
663
|
+
setSpeedOpen(false);
|
|
664
|
+
preference.set({ [SPEED_MODE_FIELD]: id });
|
|
665
|
+
},
|
|
666
|
+
onClose: () => setSpeedOpen(false),
|
|
667
|
+
align: "end",
|
|
668
|
+
side: "top",
|
|
669
|
+
portal: true,
|
|
670
|
+
compact: true
|
|
671
|
+
}) : null;
|
|
672
|
+
let quotaView = null;
|
|
673
|
+
if (quotaEnabled && quota !== void 0) {
|
|
674
|
+
const value = percent(quota.remainingPercent);
|
|
675
|
+
const label = fill(t("quickQuotaStatus"), { value });
|
|
676
|
+
quotaView = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
677
|
+
className: "codexComposerQuota",
|
|
678
|
+
role: "status",
|
|
679
|
+
"aria-label": label,
|
|
680
|
+
title: label,
|
|
681
|
+
children: [value, "%"]
|
|
682
|
+
});
|
|
683
|
+
}
|
|
592
684
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
593
|
-
className: "
|
|
594
|
-
|
|
595
|
-
"aria-label": label,
|
|
596
|
-
title: label,
|
|
597
|
-
children: [value, "%"]
|
|
685
|
+
className: "codexComposerControls",
|
|
686
|
+
children: [quotaView, speed]
|
|
598
687
|
});
|
|
599
688
|
}
|
|
600
689
|
function AccountCard({ rpc, t, account, setAccount, onSignedOut }) {
|
|
@@ -767,6 +856,21 @@ window.__ModuleLoader__.load({
|
|
|
767
856
|
]
|
|
768
857
|
});
|
|
769
858
|
}
|
|
859
|
+
function AccountFailureCard({ retry, t }) {
|
|
860
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
861
|
+
className: "codexSubscriptionCard codexSubscriptionRecover",
|
|
862
|
+
role: "alert",
|
|
863
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
864
|
+
className: "codexSubscriptionError",
|
|
865
|
+
children: t("loadFailed")
|
|
866
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
867
|
+
type: "button",
|
|
868
|
+
variant: "outline",
|
|
869
|
+
onClick: retry,
|
|
870
|
+
children: t("accountRetry")
|
|
871
|
+
})]
|
|
872
|
+
});
|
|
873
|
+
}
|
|
770
874
|
function ResetTime({ resetsAt, t }) {
|
|
771
875
|
const date = Number.isSafeInteger(resetsAt) ? validDate(resetsAt * 1e3) : void 0;
|
|
772
876
|
if (date === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetUnknown") });
|
|
@@ -948,17 +1052,23 @@ window.__ModuleLoader__.load({
|
|
|
948
1052
|
}
|
|
949
1053
|
function CodexSection({ preference, rpc, t }) {
|
|
950
1054
|
const [account, setAccount] = (0, react.useState)();
|
|
951
|
-
const [
|
|
1055
|
+
const [accountError, setAccountError] = (0, react.useState)();
|
|
952
1056
|
const [resetKey, setResetKey] = (0, react.useState)(0);
|
|
953
|
-
(0, react.
|
|
954
|
-
|
|
1057
|
+
const accountRequest = (0, react.useRef)(0);
|
|
1058
|
+
const loadAccount = () => {
|
|
1059
|
+
const id = ++accountRequest.current;
|
|
1060
|
+
setAccount(void 0);
|
|
1061
|
+
setAccountError(void 0);
|
|
955
1062
|
rpc.call(CHANNEL, "status", {}).then(unwrap).then((next) => {
|
|
956
|
-
if (
|
|
1063
|
+
if (accountRequest.current === id) setAccount(next);
|
|
957
1064
|
}).catch(() => {
|
|
958
|
-
if (
|
|
1065
|
+
if (accountRequest.current === id) setAccountError(true);
|
|
959
1066
|
});
|
|
1067
|
+
};
|
|
1068
|
+
(0, react.useEffect)(() => {
|
|
1069
|
+
loadAccount();
|
|
960
1070
|
return () => {
|
|
961
|
-
|
|
1071
|
+
accountRequest.current += 1;
|
|
962
1072
|
};
|
|
963
1073
|
}, []);
|
|
964
1074
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -968,26 +1078,24 @@ window.__ModuleLoader__.load({
|
|
|
968
1078
|
className: "codexSubscriptionHead",
|
|
969
1079
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("title") })
|
|
970
1080
|
}),
|
|
971
|
-
|
|
972
|
-
className: "codexSubscriptionError",
|
|
973
|
-
role: "alert",
|
|
974
|
-
children: error
|
|
975
|
-
}),
|
|
976
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
|
|
1081
|
+
accountError === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
|
|
977
1082
|
rpc,
|
|
978
1083
|
t,
|
|
979
1084
|
account,
|
|
980
1085
|
setAccount,
|
|
981
1086
|
onSignedOut: () => setResetKey((value) => value + 1)
|
|
1087
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountFailureCard, {
|
|
1088
|
+
retry: loadAccount,
|
|
1089
|
+
t
|
|
982
1090
|
}),
|
|
983
1091
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferencesCard, {
|
|
984
1092
|
preference,
|
|
985
1093
|
t
|
|
986
1094
|
}),
|
|
987
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
|
|
1095
|
+
account === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageCard, {
|
|
988
1096
|
rpc,
|
|
989
1097
|
t,
|
|
990
|
-
signedIn: account
|
|
1098
|
+
signedIn: account.authenticated === true,
|
|
991
1099
|
resetKey
|
|
992
1100
|
})
|
|
993
1101
|
]
|
|
@@ -1041,7 +1149,7 @@ window.__ModuleLoader__.load({
|
|
|
1041
1149
|
t,
|
|
1042
1150
|
directory: ctx.modelDirectories.directoryFor(sessionId).store
|
|
1043
1151
|
})
|
|
1044
|
-
},
|
|
1152
|
+
}, CodexComposerControls));
|
|
1045
1153
|
const conversation = ctx.get("conversation");
|
|
1046
1154
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1047
1155
|
name: "tool.call.toolview",
|
package/lib/index.js
CHANGED
|
@@ -430,6 +430,17 @@ function createCodexRpcHandler(coordinator, options = {}) {
|
|
|
430
430
|
};
|
|
431
431
|
}
|
|
432
432
|
//#endregion
|
|
433
|
+
//#region src/settings-contract.js
|
|
434
|
+
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
435
|
+
const QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
436
|
+
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
437
|
+
const SEARCH_PROVIDER_CODEX = "codex";
|
|
438
|
+
const SPEED_MODE_FIELD = "speedMode";
|
|
439
|
+
const SPEED_MODE_STANDARD = "standard";
|
|
440
|
+
const SPEED_MODE_FAST = "fast";
|
|
441
|
+
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
442
|
+
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
443
|
+
//#endregion
|
|
433
444
|
//#region src/pi-ai-runtime.js
|
|
434
445
|
/**
|
|
435
446
|
* Preserve pi-ai's native Codex OAuth provider while allowing DSH's generic
|
|
@@ -442,7 +453,7 @@ function createCodexRpcHandler(coordinator, options = {}) {
|
|
|
442
453
|
* token for this request; login, refresh, persistence, headers, transport, and
|
|
443
454
|
* model behavior remain owned by the original provider.
|
|
444
455
|
*/
|
|
445
|
-
function openaiCodexSubscriptionProvider() {
|
|
456
|
+
function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {}) {
|
|
446
457
|
const provider = createOpenAICodexProvider();
|
|
447
458
|
const requestToken = Object.freeze({
|
|
448
459
|
name: "DSH-managed Codex OAuth request token",
|
|
@@ -455,15 +466,35 @@ function openaiCodexSubscriptionProvider() {
|
|
|
455
466
|
};
|
|
456
467
|
}
|
|
457
468
|
});
|
|
469
|
+
const withSpeed = (model, options = {}) => {
|
|
470
|
+
if (resolveSpeedMode() !== "fast" || !supportsCodexFastMode(model?.id)) return options;
|
|
471
|
+
const onPayload = options.onPayload;
|
|
472
|
+
return {
|
|
473
|
+
...options,
|
|
474
|
+
serviceTier: "fast",
|
|
475
|
+
async onPayload(payload, requestModel) {
|
|
476
|
+
const fastPayload = {
|
|
477
|
+
...payload,
|
|
478
|
+
service_tier: "fast"
|
|
479
|
+
};
|
|
480
|
+
return {
|
|
481
|
+
...await onPayload?.(fastPayload, requestModel) ?? fastPayload,
|
|
482
|
+
service_tier: "fast"
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
};
|
|
458
487
|
return Object.freeze({
|
|
459
488
|
...provider,
|
|
460
489
|
auth: Object.freeze({
|
|
461
490
|
...provider.auth,
|
|
462
491
|
apiKey: requestToken
|
|
463
|
-
})
|
|
492
|
+
}),
|
|
493
|
+
stream: (model, context, options) => provider.stream(model, context, withSpeed(model, options)),
|
|
494
|
+
streamSimple: (model, context, options) => provider.streamSimple(model, context, withSpeed(model, options))
|
|
464
495
|
});
|
|
465
496
|
}
|
|
466
|
-
const USER_AGENT = `dsh-codex-subscription/1.0.
|
|
497
|
+
const USER_AGENT = `dsh-codex-subscription/1.1.0-beta.0`;
|
|
467
498
|
//#endregion
|
|
468
499
|
//#region src/codex-search.js
|
|
469
500
|
const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
|
|
@@ -791,12 +822,6 @@ function createCodexImageTool(options) {
|
|
|
791
822
|
});
|
|
792
823
|
}
|
|
793
824
|
//#endregion
|
|
794
|
-
//#region src/settings-contract.js
|
|
795
|
-
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
796
|
-
const QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
797
|
-
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
798
|
-
const SEARCH_PROVIDER_CODEX = "codex";
|
|
799
|
-
//#endregion
|
|
800
825
|
//#region src/usage.js
|
|
801
826
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
802
827
|
const DEFAULT_TTL_MS = 6e4;
|
|
@@ -1012,6 +1037,10 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, preferences })
|
|
|
1012
1037
|
if (!["dsh", "codex"].includes(payload["searchProvider"])) return publicError("internal", "Invalid search provider preference");
|
|
1013
1038
|
patch[SEARCH_PROVIDER_FIELD] = payload[SEARCH_PROVIDER_FIELD];
|
|
1014
1039
|
}
|
|
1040
|
+
if (Object.hasOwn(payload ?? {}, "speedMode")) {
|
|
1041
|
+
if (!["standard", "fast"].includes(payload["speedMode"])) return publicError("internal", "Invalid speed mode preference");
|
|
1042
|
+
patch[SPEED_MODE_FIELD] = payload[SPEED_MODE_FIELD];
|
|
1043
|
+
}
|
|
1015
1044
|
if (Object.keys(patch).length === 0) return publicError("internal", "Invalid preference update");
|
|
1016
1045
|
await preferences.update(patch);
|
|
1017
1046
|
}
|
|
@@ -1063,19 +1092,21 @@ function createSearchProviderSwitcher(loader) {
|
|
|
1063
1092
|
function apply(ctx) {
|
|
1064
1093
|
const settings = ctx.settings.register(settingsNamespace(SETTINGS_NAMESPACE), z.object({
|
|
1065
1094
|
[QUICK_QUOTA_FIELD]: z.boolean().default(false),
|
|
1066
|
-
[SEARCH_PROVIDER_FIELD]: z.union(["dsh", SEARCH_PROVIDER_CODEX]).default("dsh")
|
|
1095
|
+
[SEARCH_PROVIDER_FIELD]: z.union(["dsh", SEARCH_PROVIDER_CODEX]).default("dsh"),
|
|
1096
|
+
[SPEED_MODE_FIELD]: z.union([SPEED_MODE_STANDARD, SPEED_MODE_FAST]).default(DEFAULT_SPEED_MODE)
|
|
1067
1097
|
}));
|
|
1068
1098
|
const searchProvider = createSearchProviderSwitcher(ctx.loader);
|
|
1069
1099
|
const preferences = {
|
|
1070
1100
|
status: () => ({
|
|
1071
1101
|
[QUICK_QUOTA_FIELD]: settings.get()[QUICK_QUOTA_FIELD],
|
|
1072
1102
|
[SEARCH_PROVIDER_FIELD]: settings.get()[SEARCH_PROVIDER_FIELD],
|
|
1103
|
+
[SPEED_MODE_FIELD]: settings.get()[SPEED_MODE_FIELD],
|
|
1073
1104
|
writable: ctx.settings.writable
|
|
1074
1105
|
}),
|
|
1075
1106
|
update: (patch) => settings.update(patch)
|
|
1076
1107
|
};
|
|
1077
1108
|
const store = new DshOAuthCredentialStore(ctx.credentials, CREDENTIAL_REF, [LEGACY_CREDENTIAL_REF]);
|
|
1078
|
-
const provider = openaiCodexSubscriptionProvider();
|
|
1109
|
+
const provider = openaiCodexSubscriptionProvider({ resolveSpeedMode: () => settings.get()[SPEED_MODE_FIELD] });
|
|
1079
1110
|
const authModels = createModels({ credentials: store });
|
|
1080
1111
|
authModels.setProvider(provider);
|
|
1081
1112
|
const profile = Object.freeze({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.1.0-beta.0",
|
|
4
4
|
"packageManager": "pnpm@11.19.0",
|
|
5
5
|
"description": "ChatGPT and Codex subscriptions for DeepSeek Harness with OAuth login, image generation, quota, and subscription web search",
|
|
6
6
|
"type": "module",
|