dsh-codex-subscription 1.4.0 → 1.5.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 +8 -26
- package/DIRECTORY.md +1 -1
- package/README.md +16 -17
- package/README.zh-CN.md +13 -16
- package/lib/client.js +120 -73
- package/lib/index.js +41 -23
- 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.
|
|
9
|
+
- Use the exact `1.5.0` 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.
|
|
@@ -14,23 +14,19 @@ Use this guide when a user asks an Agent to install, update, verify, or remove
|
|
|
14
14
|
|
|
15
15
|
## Locate DSH
|
|
16
16
|
|
|
17
|
-
On Windows, check the
|
|
17
|
+
On Windows, check the standard DSH and official npm entry points without recursively scanning disks:
|
|
18
18
|
|
|
19
19
|
```powershell
|
|
20
20
|
Get-Command dsh -ErrorAction SilentlyContinue
|
|
21
21
|
Get-Command npx -ErrorAction SilentlyContinue
|
|
22
|
-
Test-Path -LiteralPath '.\dsh.exe' -PathType Leaf
|
|
23
|
-
Test-Path -LiteralPath "$env:USERPROFILE\Downloads\DSH-Portable\dsh.exe" -PathType Leaf
|
|
24
22
|
Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" |
|
|
25
23
|
Where-Object { $_.CommandLine -match '@deepseek-ai[\\/]dsh[\\/]lib[\\/]bin\.js' } |
|
|
26
24
|
Select-Object ExecutablePath, CommandLine
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
the Portable root is the parent of `runtime`; use its `dsh.exe`. If several targets remain possible, stop
|
|
33
|
-
and ask the user which one to change.
|
|
27
|
+
DSH-Portable exposes the same standard `dsh plugin` contract. Missing system pnpm is normal; do not
|
|
28
|
+
install it globally for this plugin. If several targets remain possible, stop and ask the user which one
|
|
29
|
+
to change.
|
|
34
30
|
|
|
35
31
|
## Install or update
|
|
36
32
|
|
|
@@ -38,19 +34,13 @@ For an official DSH installation run through npm, keep the complete `npx` prefix
|
|
|
38
34
|
run command does not create a global `dsh` command:
|
|
39
35
|
|
|
40
36
|
```sh
|
|
41
|
-
npx -y @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add dsh-codex-subscription@1.
|
|
37
|
+
npx -y @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add dsh-codex-subscription@1.5.0
|
|
42
38
|
```
|
|
43
39
|
|
|
44
40
|
With an existing global `dsh` command:
|
|
45
41
|
|
|
46
42
|
```sh
|
|
47
|
-
dsh plugin --profile web add dsh-codex-subscription@1.
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
From a DSH-Portable folder:
|
|
51
|
-
|
|
52
|
-
```powershell
|
|
53
|
-
.\dsh.exe plugin --profile web add dsh-codex-subscription@1.4.0
|
|
43
|
+
dsh plugin --profile web add dsh-codex-subscription@1.5.0
|
|
54
44
|
```
|
|
55
45
|
|
|
56
46
|
Use the same `add` command to update or repair. This is the complete package-changing operation.
|
|
@@ -66,11 +56,9 @@ dsh plugin --profile web list dsh-codex-subscription --depth 0
|
|
|
66
56
|
dsh --profile web --dump-config
|
|
67
57
|
```
|
|
68
58
|
|
|
69
|
-
For DSH-Portable, replace `dsh` with `.\dsh.exe`. Static acceptance requires:
|
|
70
|
-
|
|
71
59
|
For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.1-rc.2` in place of `dsh`.
|
|
72
60
|
|
|
73
|
-
1. `dsh-codex-subscription` version `1.
|
|
61
|
+
1. `dsh-codex-subscription` version `1.5.0` appears exactly once.
|
|
74
62
|
2. `codex-subscription` appears exactly once in the composed config.
|
|
75
63
|
3. No unrelated plugin or profile was changed and DSH was not restarted.
|
|
76
64
|
|
|
@@ -85,12 +73,6 @@ the user explicitly asks for a live model, search, or image-generation check.
|
|
|
85
73
|
dsh plugin --profile web remove dsh-codex-subscription
|
|
86
74
|
```
|
|
87
75
|
|
|
88
|
-
For DSH-Portable:
|
|
89
|
-
|
|
90
|
-
```powershell
|
|
91
|
-
.\dsh.exe plugin --profile web remove dsh-codex-subscription
|
|
92
|
-
```
|
|
93
|
-
|
|
94
76
|
For the official npm route:
|
|
95
77
|
|
|
96
78
|
```sh
|
package/DIRECTORY.md
CHANGED
|
@@ -18,7 +18,7 @@ Use a ChatGPT subscription in DeepSeek Harness through independent OAuth sign-in
|
|
|
18
18
|
- Codex CLI required: no
|
|
19
19
|
- OpenAI API key required: no
|
|
20
20
|
- Platforms: official DSH plus DSH-Portable product forms for Windows, macOS, and Linux
|
|
21
|
-
- Main capabilities: Codex models, quota, web search, image
|
|
21
|
+
- Main capabilities: Codex models, quota and expiring resets, web search, image preview/download/refinement, optional composer quota, optional Fast mode, secret-free support diagnostics and direct feedback
|
|
22
22
|
- Safety boundary: subscription failures remain visible and never silently fall back to another paid provider
|
|
23
23
|
|
|
24
24
|
## Install
|
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ With an existing `dsh` command, install with:
|
|
|
37
37
|
dsh plugin --profile web add dsh-codex-subscription
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
DSH-Portable exposes the same standard plugin command, so the command above also applies there. See below for the complete official npm, Agent, update, and uninstall routes.
|
|
41
41
|
|
|
42
42
|
## Why this plugin
|
|
43
43
|
|
|
@@ -47,9 +47,9 @@ From a DSH-Portable product folder, run `./dsh plugin ...` (`.\dsh.exe plugin ..
|
|
|
47
47
|
| **Recoverable and diagnosable** | Sign-in state reconciles automatically; Settings can create a support report without credentials or account identifiers |
|
|
48
48
|
| **Visible quota** | Keep backend-provided standard Codex, Spark, and other limits separate |
|
|
49
49
|
| **Composer quota** | Choose a compact percentage, progress bar, or no inline quota display |
|
|
50
|
-
| **Safe quota reset** |
|
|
50
|
+
| **Safe quota reset** | See the next expiry and deliberately try a reset with a cooldown and acknowledgement |
|
|
51
51
|
| **Subscription search** | Explicitly choose DSH default search or Codex subscription search |
|
|
52
|
-
| **Codex image generation** |
|
|
52
|
+
| **Codex image generation** | Preview or download the original, then describe refinements in the same conversation |
|
|
53
53
|
| **Fast mode (Beta)** | Switch between Standard and Fast directly in the composer |
|
|
54
54
|
|
|
55
55
|
These capabilities reuse the same local ChatGPT sign-in. Subscription routing failures stay visible and never silently switch to another paid route.
|
|
@@ -117,7 +117,7 @@ npx -y @deepseek-ai/dsh@0.1.1-rc.2 --profile web --dump-config
|
|
|
117
117
|
</details>
|
|
118
118
|
|
|
119
119
|
<details>
|
|
120
|
-
<summary>An existing <code>dsh</code> command
|
|
120
|
+
<summary>An existing <code>dsh</code> command</summary>
|
|
121
121
|
|
|
122
122
|
```sh
|
|
123
123
|
dsh plugin --profile web add dsh-codex-subscription
|
|
@@ -125,8 +125,7 @@ dsh plugin --profile web list dsh-codex-subscription --depth 0
|
|
|
125
125
|
dsh --profile web --dump-config
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
one `dsh-codex-subscription`, and the config should contain one `codex-subscription` entry.
|
|
128
|
+
The plugin list should contain one `dsh-codex-subscription`, and the config should contain one `codex-subscription` entry.
|
|
130
129
|
|
|
131
130
|
</details>
|
|
132
131
|
|
|
@@ -144,10 +143,10 @@ Restart DSH manually after installation, then:
|
|
|
144
143
|
- A clear choice between DSH default search and Codex subscription search;
|
|
145
144
|
- Actual backend-provided quota, reset time, and freshness;
|
|
146
145
|
- Separate standard Codex, Codex-Spark, Credits, and other independent limits;
|
|
147
|
-
-
|
|
146
|
+
- Visible quota-reset count and earliest expiry, with deliberate early redemption, layered confirmation, and no automatic retry;
|
|
148
147
|
- Optional percentage or progress bar for the selected Codex model (off by default);
|
|
149
148
|
- Standard or Fast mode for supported Codex models directly in the composer (Beta);
|
|
150
|
-
- A copyable support report in Settings
|
|
149
|
+
- A copyable support report and direct feedback link in Settings; the report excludes OAuth credentials, account identifiers, and authorization timestamps;
|
|
151
150
|
- Visible errors when subscription routing is unavailable, with no silent paid fallback.
|
|
152
151
|
|
|
153
152
|
### Composer quota
|
|
@@ -162,10 +161,14 @@ Standard Codex uses the lowest remaining window returned by the service; Spark u
|
|
|
162
161
|
|
|
163
162
|
### Safe quota reset
|
|
164
163
|
|
|
165
|
-
If ChatGPT reports an available quota reset, Settings shows
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
If ChatGPT reports an available quota reset, Settings shows its count and earliest disclosed expiry in a compact row.
|
|
165
|
+
You may deliberately try it before a quota reaches 100%, which is useful for a reset nearing expiry. ChatGPT still
|
|
166
|
+
decides whether a window needs resetting and may return **nothing to reset** without spending the reset. The final
|
|
167
|
+
action requires an acknowledgement checkbox and five-second cooldown. Cancel never consumes a reset, rapid repeated
|
|
168
|
+
clicks are single-flight, and an uncertain network result is never retried automatically.
|
|
169
|
+
|
|
170
|
+
Generated images open in a plugin-owned preview with **Download original**. To refine one, continue describing the
|
|
171
|
+
change in the same conversation; the durable conversation image remains available to the next model turn.
|
|
169
172
|
|
|
170
173
|
### Composer speed (Beta)
|
|
171
174
|
|
|
@@ -198,14 +201,10 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
198
201
|
|
|
199
202
|
</details>
|
|
200
203
|
|
|
201
|
-
From a DSH-Portable folder, replace `dsh` above with `.\dsh.exe`.
|
|
202
|
-
|
|
203
204
|
## Troubleshooting
|
|
204
205
|
|
|
205
206
|
- **`dsh` is not recognized:** the official npm route does not create a global `dsh` command; use the complete `npx -y @deepseek-ai/dsh@0.1.1-rc.2 ...` command above;
|
|
206
|
-
-
|
|
207
|
-
- **DSH-Portable is not found:** enter its folder and rerun setup, or run `.\dsh.exe plugin --profile web add dsh-codex-subscription` directly;
|
|
208
|
-
- **More than one DSH exists:** the one-line setup combines running copies with Portable folders detected in common locations; enter its number. For an Agent, use the intended folder or pass `-DshPath` explicitly;
|
|
207
|
+
- **More than one DSH exists:** run the standard command from the intended DSH environment. For an Agent, specify the target or pass `-DshPath` explicitly;
|
|
209
208
|
- **Setup still fails:** send the Agent guide above to an Agent. Do not delete the profile or change the system PATH to force an install.
|
|
210
209
|
- **Need to report a problem:** generate a **Support diagnostics** report at the bottom of Settings, then open the [bug report form](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml). The form collects exact DSH/plugin versions and reproduction steps. The generated report excludes credentials and account identifiers; still do not attach sign-in URLs, authorization codes, or browser callback addresses.
|
|
211
210
|
|
package/README.zh-CN.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
dsh plugin --profile web add dsh-codex-subscription
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
DSH-Portable
|
|
41
|
+
DSH-Portable 也提供相同的标准插件命令,因此同样使用上面的命令。完整的官方 npm、Agent 安装、更新和卸载方式见下文。
|
|
42
42
|
|
|
43
43
|
## 核心优势
|
|
44
44
|
|
|
@@ -48,9 +48,9 @@ DSH-Portable 用户在产品目录中运行 `./dsh plugin ...`,Windows PowerSh
|
|
|
48
48
|
| **可恢复、可诊断** | 登录状态会自动对账;设置页可生成不含凭据和账号标识的支持报告 |
|
|
49
49
|
| **额度可见** | 普通 Codex、Spark 等服务端实际返回的额度分开显示 |
|
|
50
50
|
| **输入框额度** | 可选择紧凑百分比、进度条或完全关闭输入框额度显示 |
|
|
51
|
-
| **安全额度重置** |
|
|
51
|
+
| **安全额度重置** | 直接查看最早到期时间,并通过冷静期和知情确认主动尝试重置 |
|
|
52
52
|
| **订阅搜索** | 可在 DSH 默认搜索与 Codex 订阅搜索之间明确切换 |
|
|
53
|
-
| **Codex 图片生成** |
|
|
53
|
+
| **Codex 图片生成** | 预览或下载原图,并在同一会话继续描述修改内容 |
|
|
54
54
|
| **高速模式(Beta)** | 直接在输入框切换标准或高速,无需离开当前会话 |
|
|
55
55
|
|
|
56
56
|
这些能力共用同一份本机 ChatGPT 登录。订阅路由失败时会明确报错,不会静默切换到其他付费路由。
|
|
@@ -117,7 +117,7 @@ npx -y @deepseek-ai/dsh@0.1.1-rc.2 --profile web --dump-config
|
|
|
117
117
|
</details>
|
|
118
118
|
|
|
119
119
|
<details>
|
|
120
|
-
<summary>已经能运行 <code>dsh</code
|
|
120
|
+
<summary>已经能运行 <code>dsh</code></summary>
|
|
121
121
|
|
|
122
122
|
```sh
|
|
123
123
|
dsh plugin --profile web add dsh-codex-subscription
|
|
@@ -125,8 +125,7 @@ dsh plugin --profile web list dsh-codex-subscription --depth 0
|
|
|
125
125
|
dsh --profile web --dump-config
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
`dsh-codex-subscription`,配置中应只有一个 `codex-subscription` 条目。
|
|
128
|
+
安装列表中应只有一个 `dsh-codex-subscription`,配置中应只有一个 `codex-subscription` 条目。
|
|
130
129
|
|
|
131
130
|
</details>
|
|
132
131
|
|
|
@@ -144,10 +143,10 @@ DSH-Portable 请把 `dsh` 换成 `./dsh`(PowerShell 使用 `.\dsh.exe`)。
|
|
|
144
143
|
- DSH 默认搜索与 Codex 订阅搜索可随时切换;
|
|
145
144
|
- 设置页显示服务端返回的额度、重置时间和更新时间;
|
|
146
145
|
- 普通 Codex、Codex-Spark、Credits 等独立额度分开显示;
|
|
147
|
-
-
|
|
146
|
+
- 显示重置卡数量与最早到期时间,也允许在额度未完全用尽时主动尝试,并经过分层确认且不会自动重试;
|
|
148
147
|
- 输入框可用百分比或进度条显示当前 Codex 模型的剩余额度(默认关闭);
|
|
149
148
|
- 输入框可为支持的 Codex 模型切换标准或高速模式(Beta);
|
|
150
|
-
-
|
|
149
|
+
- 设置页可生成并复制无敏感信息的支持诊断,并直接打开反馈入口;报告不包含 OAuth 凭据、账号标识或授权时间;
|
|
151
150
|
- 订阅路由不可用时明确报错,不会静默切换到其他付费路由。
|
|
152
151
|
|
|
153
152
|
### 输入框额度
|
|
@@ -161,9 +160,11 @@ Spark 使用独立额度。插件不会写死“5 小时 + 每周”,也不会
|
|
|
161
160
|
|
|
162
161
|
### 安全使用额度重置
|
|
163
162
|
|
|
164
|
-
ChatGPT
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
ChatGPT 返回可用重置卡时,设置页会用紧凑的一行显示数量和服务端提供的最早到期时间。即使额度尚未到 100%,
|
|
164
|
+
也可以主动尝试使用,适合重置卡即将过期的情况;是否需要重置仍由 ChatGPT 判断,服务端可能返回“当前无需重置”且不扣次数。
|
|
165
|
+
最终操作需要勾选知情确认并等待 5 秒。取消不会消耗,快速连续点击只允许一次请求,网络结果不确定时也不会自动重试。
|
|
166
|
+
|
|
167
|
+
生成的图片可在插件自有预览中下载原图。需要继续修改时,直接在同一会话描述改动;持久化的会话图片会继续提供给下一轮模型。
|
|
167
168
|
|
|
168
169
|
### 输入框速度(Beta)
|
|
169
170
|
|
|
@@ -196,14 +197,10 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
196
197
|
|
|
197
198
|
</details>
|
|
198
199
|
|
|
199
|
-
DSH-Portable 在其目录中把上述 `dsh` 换成 `.\dsh.exe`。
|
|
200
|
-
|
|
201
200
|
## 常见问题
|
|
202
201
|
|
|
203
202
|
- **`dsh` 无法识别**:官方 npm 方式本来就不会创建全局 `dsh` 命令,请使用上面的完整 `npx -y @deepseek-ai/dsh@0.1.1-rc.2 ...` 命令;
|
|
204
|
-
-
|
|
205
|
-
- **找不到 DSH-Portable**:进入它的目录后重新执行安装命令,或直接运行 `.\dsh.exe plugin --profile web add dsh-codex-subscription`;
|
|
206
|
-
- **电脑上有多个 DSH**:单行助手会合并正在运行的副本与常用目录内检测到的 Portable,输入对应编号即可;交给 Agent 时让它进入目标目录,或明确传入 `-DshPath`;
|
|
203
|
+
- **电脑上有多个 DSH**:请在目标 DSH 环境中运行标准命令;交给 Agent 时明确目标,或传入 `-DshPath`;
|
|
207
204
|
- **安装仍然失败**:把上面的 Agent 文档链接发给 Agent,不要删除 profile 或随意修改系统 PATH。
|
|
208
205
|
- **需要提交问题**:在设置页底部生成“支持诊断”,然后打开[使用问题表单](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml)。表单会收集准确的 DSH/插件版本和复现步骤;报告不含凭据和账号标识,仍不要附上登录链接、授权码或浏览器回调地址。
|
|
209
206
|
|
package/lib/client.js
CHANGED
|
@@ -127,6 +127,7 @@ window.__ModuleLoader__.load({
|
|
|
127
127
|
];
|
|
128
128
|
const NS = "settings.codexSubscription";
|
|
129
129
|
const CHANNEL = "/codex-subscription";
|
|
130
|
+
const SUPPORT_ISSUE_URL = "https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml";
|
|
130
131
|
const QUICK_QUOTA_REFRESH_EVENT = "dsh-codex-subscription:refresh-quick-quota";
|
|
131
132
|
const QUICK_QUOTA_REFRESH_MS = 6e4;
|
|
132
133
|
const zh = {
|
|
@@ -153,6 +154,7 @@ window.__ModuleLoader__.load({
|
|
|
153
154
|
diagnosticsCopy: "复制诊断",
|
|
154
155
|
diagnosticsCopied: "已复制",
|
|
155
156
|
diagnosticsFailed: "无法生成诊断信息。",
|
|
157
|
+
feedbackOpen: "反馈问题",
|
|
156
158
|
searchTitle: "搜索来源",
|
|
157
159
|
searchDsh: "DSH 默认",
|
|
158
160
|
searchDshHint: "当前搜索服务",
|
|
@@ -183,13 +185,14 @@ window.__ModuleLoader__.load({
|
|
|
183
185
|
monthlyCreditLimit: "Credits 月度消费上限",
|
|
184
186
|
resetCredits: "可用额度重置次数",
|
|
185
187
|
resetCreditsValue: "{count} 次",
|
|
186
|
-
resetUse: "
|
|
187
|
-
resetUnavailable: "仅当至少一个 Codex 模型额度已用尽时可使用。",
|
|
188
|
+
resetUse: "查看并使用",
|
|
188
189
|
resetPreparing: "正在读取重置详情…",
|
|
189
190
|
resetConfirmTitle: "确认使用额度重置",
|
|
190
|
-
resetWarning: "
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
resetWarning: "若 ChatGPT 执行重置,将立即消耗 1 次且无法撤销。",
|
|
192
|
+
resetEarlyWarning: "当前模型额度尚未用尽;ChatGPT 可能执行重置,也可能判定暂无需重置且不消耗次数。",
|
|
193
|
+
resetAcknowledge: "我知道这次操作可能立即消耗 1 次重置",
|
|
194
|
+
resetCreditExpires: "最早到期:{value}",
|
|
195
|
+
resetCreditExpiryUnknown: "到期时间未提供",
|
|
193
196
|
resetWait: "请再等待 {count} 秒",
|
|
194
197
|
resetFinal: "消耗 1 次并重置额度",
|
|
195
198
|
resetUsing: "正在重置…",
|
|
@@ -202,8 +205,7 @@ window.__ModuleLoader__.load({
|
|
|
202
205
|
resetExpired: "本次确认已失效,请重新开始。",
|
|
203
206
|
resetInProgress: "额度重置正在处理中。",
|
|
204
207
|
resetTooEarly: "请等待冷静期结束后再确认。",
|
|
205
|
-
|
|
206
|
-
resetNotExhausted: "当前 Codex 模型额度尚未用尽。",
|
|
208
|
+
resetAcknowledgeRequired: "请先确认已了解这次操作可能消耗重置次数。",
|
|
207
209
|
resetAccountChanged: "登录账号已变更,请重新开始。",
|
|
208
210
|
creditsNote: "仅显示 Codex 为此账户或工作区实际返回的额外 Credits、消费上限或额度重置次数;三者不是同一项。",
|
|
209
211
|
creditsUsed: "已用 {used} / {limit} credits",
|
|
@@ -240,7 +242,9 @@ window.__ModuleLoader__.load({
|
|
|
240
242
|
imageLoading: "正在加载图片…",
|
|
241
243
|
imageLoadFailed: "图片加载失败,点击重试",
|
|
242
244
|
imagePreview: "图片预览",
|
|
243
|
-
imageClosePreview: "关闭预览"
|
|
245
|
+
imageClosePreview: "关闭预览",
|
|
246
|
+
imageDownload: "下载原图",
|
|
247
|
+
imageRefineHint: "继续在当前会话描述修改内容,即可基于这张图片继续调整。"
|
|
244
248
|
};
|
|
245
249
|
const en = {
|
|
246
250
|
nav: "Codex",
|
|
@@ -266,6 +270,7 @@ window.__ModuleLoader__.load({
|
|
|
266
270
|
diagnosticsCopy: "Copy report",
|
|
267
271
|
diagnosticsCopied: "Copied",
|
|
268
272
|
diagnosticsFailed: "Could not create diagnostics.",
|
|
273
|
+
feedbackOpen: "Report a problem",
|
|
269
274
|
searchTitle: "Search source",
|
|
270
275
|
searchDsh: "DSH default",
|
|
271
276
|
searchDshHint: "Current search service",
|
|
@@ -296,13 +301,14 @@ window.__ModuleLoader__.load({
|
|
|
296
301
|
monthlyCreditLimit: "Monthly Credits spending cap",
|
|
297
302
|
resetCredits: "Available quota resets",
|
|
298
303
|
resetCreditsValue: "{count} available",
|
|
299
|
-
resetUse: "
|
|
300
|
-
resetUnavailable: "Available only when at least one Codex model quota is exhausted.",
|
|
304
|
+
resetUse: "Review and use",
|
|
301
305
|
resetPreparing: "Reading reset details…",
|
|
302
306
|
resetConfirmTitle: "Confirm quota reset",
|
|
303
|
-
resetWarning: "
|
|
304
|
-
|
|
305
|
-
|
|
307
|
+
resetWarning: "If ChatGPT performs the reset, one reset is consumed immediately and cannot be restored.",
|
|
308
|
+
resetEarlyWarning: "No model quota is exhausted. ChatGPT may reset it, or report that nothing needs resetting without consuming a reset.",
|
|
309
|
+
resetAcknowledge: "I understand this may consume one reset now",
|
|
310
|
+
resetCreditExpires: "Earliest expiry: {value}",
|
|
311
|
+
resetCreditExpiryUnknown: "Expiration time not provided",
|
|
306
312
|
resetWait: "Wait {count} more seconds",
|
|
307
313
|
resetFinal: "Consume one reset",
|
|
308
314
|
resetUsing: "Resetting…",
|
|
@@ -315,8 +321,7 @@ window.__ModuleLoader__.load({
|
|
|
315
321
|
resetExpired: "This confirmation expired. Start again.",
|
|
316
322
|
resetInProgress: "A quota reset is already in progress.",
|
|
317
323
|
resetTooEarly: "Wait for the cooldown before confirming.",
|
|
318
|
-
|
|
319
|
-
resetNotExhausted: "The current Codex model quota is not exhausted.",
|
|
324
|
+
resetAcknowledgeRequired: "Confirm that you understand this may consume a reset.",
|
|
320
325
|
resetAccountChanged: "The signed-in account changed. Start again.",
|
|
321
326
|
creditsNote: "Shows only extra Credits, spending caps, or quota resets returned for this account or workspace; these are separate items.",
|
|
322
327
|
creditsUsed: "{used} / {limit} credits used",
|
|
@@ -353,7 +358,9 @@ window.__ModuleLoader__.load({
|
|
|
353
358
|
imageLoading: "Loading image…",
|
|
354
359
|
imageLoadFailed: "Image failed to load. Click to retry",
|
|
355
360
|
imagePreview: "Image preview",
|
|
356
|
-
imageClosePreview: "Close preview"
|
|
361
|
+
imageClosePreview: "Close preview",
|
|
362
|
+
imageDownload: "Download original",
|
|
363
|
+
imageRefineHint: "Describe the changes in this conversation to continue refining this image."
|
|
357
364
|
};
|
|
358
365
|
const STYLE = `
|
|
359
366
|
.codexSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
|
|
@@ -372,7 +379,7 @@ window.__ModuleLoader__.load({
|
|
|
372
379
|
.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}
|
|
373
380
|
.codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}.codexSubscriptionInput{width:100%;box-sizing:border-box}
|
|
374
381
|
.codexSubscriptionRecover{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionRecover .codexSubscriptionError{flex:1}.codexSubscriptionRecover button{flex:0 0 auto}
|
|
375
|
-
.codexSubscriptionDiagnostics pre{max-height:240px;margin:0;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/17px ui-monospace,SFMono-Regular,Consolas,monospace;color:var(--dsw-alias-label-secondary)}
|
|
382
|
+
.codexSubscriptionDiagnostics pre{max-height:240px;margin:0;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/17px ui-monospace,SFMono-Regular,Consolas,monospace;color:var(--dsw-alias-label-secondary)}.codexSubscriptionLink{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:32px;padding:0 13px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:transparent;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;text-decoration:none;white-space:nowrap}.codexSubscriptionLink:hover{background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionLink:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
|
|
376
383
|
.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)}
|
|
377
384
|
.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}
|
|
378
385
|
.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)}
|
|
@@ -382,13 +389,13 @@ window.__ModuleLoader__.load({
|
|
|
382
389
|
.codexSubscriptionLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionLimitMeta{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
383
390
|
.codexSubscriptionCreditSection{display:flex;flex-direction:column;gap:7px}.codexSubscriptionCreditNote{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionCreditRows{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}.codexSubscriptionCreditBalance,.codexSubscriptionSpendLimit{min-width:0;border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-module-platform)}
|
|
384
391
|
.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}
|
|
385
|
-
.codexSubscriptionResetSummary{display:flex;align-items:center;justify-content:space-between;gap:10px}.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:
|
|
392
|
+
.codexSubscriptionResetSummary{display:flex;align-items:center;justify-content:space-between;gap:10px}.codexSubscriptionResetMeta{display:flex;min-width:0;flex-direction:column;gap:1px}.codexSubscriptionResetBalance{display:flex;flex-direction:column;gap:8px}.codexSubscriptionResetBalance .codexSubscriptionActions{justify-content:flex-start}.codexSubscriptionResetFlow{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}.codexSubscriptionResetFlow h4{margin:0;font-size:13px;line-height:20px;font-weight:500}.codexSubscriptionResetWarning{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionResetExpiry{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionResetCheck{display:flex;align-items:flex-start;gap:8px;padding:9px 10px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);cursor:pointer}.codexSubscriptionResetCheck input{margin:3px 0 0;accent-color:var(--dsw-alias-label-primary)}.codexSubscriptionResetFinal{border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.codexSubscriptionResetResult{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary)}
|
|
386
393
|
.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}
|
|
387
394
|
.codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;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}.codexComposerQuotaBar{display:block;width:40px;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-label-secondary);-webkit-appearance:none;appearance:none}.codexComposerQuotaBar::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexComposerQuotaBar::-webkit-progress-value{background:var(--dsw-alias-label-secondary);border-radius:999px}.codexComposerQuotaBar::-moz-progress-bar{background:var(--dsw-alias-label-secondary);border-radius:999px}
|
|
388
395
|
.codexModelSelect{position:relative;min-width:0}.codexModelSelectTrigger{display:flex;align-items:center;gap:4px;min-width:0;max-width:min(360px,45cqw);height:28px;padding:0 4px 0 8px;border:0;border-radius:24px;outline:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;line-height:20px;cursor:pointer}.codexModelSelectTrigger:hover:not(:disabled),.codexModelSelectTrigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexModelSelectTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectBolt{display:block;flex:none;width:14px;height:14px;color:var(--dsw-alias-label-primary)}.codexModelSelectLabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectEffort{flex:none;color:var(--dsw-alias-label-caption)}.codexModelSelectChevron{flex:none;color:var(--dsw-alias-label-caption);transition:transform 120ms}.codexModelSelectTrigger[aria-expanded=true] .codexModelSelectChevron{transform:rotate(180deg)}
|
|
389
396
|
.codexModelSelectMenu,.codexModelSelectSubmenu{position:absolute;z-index:30;box-sizing:border-box;width:max-content;min-width:min(240px,calc(100vw - 32px));max-width:min(420px,calc(100vw - 32px));max-height:min(360px,calc(100vh - 96px));padding:4px;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);overflow:hidden}.codexModelSelectMenu{right:0;bottom:calc(100% + 8px)}.codexModelSelectSubmenu{right:calc(100% + 8px);bottom:0;min-width:min(230px,calc(100vw - 32px))}.codexModelSelectCell{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;height:40px;box-sizing:border-box;padding:0 10px;border:0;border-radius:10px;background:transparent;color:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}.codexModelSelectCell:hover,.codexModelSelectCell:focus-visible,.codexModelSelectCell[data-open=true]{background:var(--dsw-alias-interactive-bg-hover);outline:0}.codexModelSelectCell:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectCellLabel{flex:none;white-space:nowrap}.codexModelSelectCellValue{flex:auto;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary);text-align:right;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCellChevron{flex:none;color:var(--dsw-alias-label-tertiary)}.codexModelSelectGroups{min-height:0;max-height:352px;overflow-y:auto}.codexModelSelectGroup+.codexModelSelectGroup{margin-top:4px}.codexModelSelectGroupTitle{position:sticky;top:0;z-index:1;padding:5px 8px 3px;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:500;line-height:18px}.codexModelSelectOption{display:flex;align-items:center;gap:8px;width:100%;min-width:100%;min-height:38px;box-sizing:border-box;padding:6px 8px;border:0;border-radius:10px;outline:0;background:transparent;color:inherit;text-align:left;cursor:pointer}.codexModelSelectOption:hover:not(:disabled),.codexModelSelectOption:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}.codexModelSelectOption:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.codexModelSelectOptionCopy{display:flex;flex:1;min-width:0;flex-direction:column}.codexModelSelectOptionName{overflow:hidden;color:inherit;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectOptionDescription{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.codexModelSelectCheck{display:grid;place-items:center;flex:0 0 18px;color:var(--dsw-alias-label-primary)}.codexModelSelectStatus,.codexModelSelectEmpty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.codexModelSelectError,.codexModelSelectWarning{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:4px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.codexModelSelectWarning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}.codexModelSelectRetry{flex:none;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}
|
|
390
397
|
.codexModelSelectMenu{overflow:visible}
|
|
391
|
-
.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 -
|
|
398
|
+
.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 - 112px);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}.codexGeneratedImageActions{position:absolute;left:50%;bottom:18px;transform:translateX(-50%);display:flex;align-items:center;gap:10px;max-width:min(90vw,720px);padding:6px 8px 6px 12px;border:1px solid rgba(255,255,255,.2);border-radius:999px;background:rgba(20,20,20,.82);color:rgba(255,255,255,.72);font-size:12px;line-height:18px;backdrop-filter:blur(12px)}.codexGeneratedImageDownload{flex:0 0 auto;padding:5px 10px;border-radius:999px;background:rgba(255,255,255,.12);color:#fff;text-decoration:none;white-space:nowrap}.codexGeneratedImageDownload:hover{background:rgba(255,255,255,.2)}.codexGeneratedImageDownload:focus-visible,.codexGeneratedImageClose:focus-visible{outline:2px solid #fff;outline-offset:2px}.codexGeneratedImageRefineHint{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@keyframes codexImageSpin{to{transform:rotate(360deg)}}
|
|
392
399
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
393
400
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
394
401
|
@media(max-width:640px){.codexSubscriptionCard{padding:14px}}
|
|
@@ -413,12 +420,20 @@ window.__ModuleLoader__.load({
|
|
|
413
420
|
const date = new Date(value);
|
|
414
421
|
return Number.isFinite(date.getTime()) ? date : void 0;
|
|
415
422
|
};
|
|
423
|
+
const imageDownloadName = (attachment) => {
|
|
424
|
+
const fallback = "codex-generated-image.png";
|
|
425
|
+
if (typeof attachment?.name !== "string") return fallback;
|
|
426
|
+
const cleaned = attachment.name.replace(/[<>:"/\\|?*\u0000-\u001f]/gu, "-").replace(/[. ]+$/u, "").trim();
|
|
427
|
+
if (cleaned === "") return fallback;
|
|
428
|
+
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
429
|
+
};
|
|
416
430
|
function CodexGeneratedImage({ attachment, loadImage, t }) {
|
|
417
431
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
418
432
|
const [error, setError] = (0, react.useState)(false);
|
|
419
433
|
const [open, setOpen] = (0, react.useState)(false);
|
|
420
434
|
const [src, setSrc] = (0, react.useState)();
|
|
421
435
|
const closeRef = (0, react.useRef)(null);
|
|
436
|
+
const downloadRef = (0, react.useRef)(null);
|
|
422
437
|
const triggerRef = (0, react.useRef)(null);
|
|
423
438
|
(0, react.useEffect)(() => {
|
|
424
439
|
let live = true;
|
|
@@ -445,7 +460,9 @@ window.__ModuleLoader__.load({
|
|
|
445
460
|
setOpen(false);
|
|
446
461
|
} else if (event.key === "Tab") {
|
|
447
462
|
event.preventDefault();
|
|
448
|
-
closeRef.current
|
|
463
|
+
const controls = [downloadRef.current, closeRef.current].filter(Boolean);
|
|
464
|
+
const current = controls.indexOf(document.activeElement);
|
|
465
|
+
controls[event.shiftKey ? current <= 0 ? controls.length - 1 : current - 1 : (current + 1) % controls.length]?.focus();
|
|
449
466
|
}
|
|
450
467
|
};
|
|
451
468
|
document.addEventListener("keydown", handleKeyDown);
|
|
@@ -456,6 +473,7 @@ window.__ModuleLoader__.load({
|
|
|
456
473
|
};
|
|
457
474
|
}, [open]);
|
|
458
475
|
const label = attachment.name ?? t("imageLabel");
|
|
476
|
+
const downloadName = imageDownloadName(attachment);
|
|
459
477
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
460
478
|
type: "button",
|
|
461
479
|
className: "codexGeneratedImageRetry",
|
|
@@ -481,18 +499,35 @@ window.__ModuleLoader__.load({
|
|
|
481
499
|
"aria-modal": "true",
|
|
482
500
|
"aria-label": t("imagePreview"),
|
|
483
501
|
onClick: () => setOpen(false),
|
|
484
|
-
children: [
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
502
|
+
children: [
|
|
503
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
504
|
+
src,
|
|
505
|
+
alt: label,
|
|
506
|
+
onClick: (event) => event.stopPropagation()
|
|
507
|
+
}),
|
|
508
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
509
|
+
className: "codexGeneratedImageActions",
|
|
510
|
+
onClick: (event) => event.stopPropagation(),
|
|
511
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
512
|
+
className: "codexGeneratedImageRefineHint",
|
|
513
|
+
children: t("imageRefineHint")
|
|
514
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
515
|
+
ref: downloadRef,
|
|
516
|
+
className: "codexGeneratedImageDownload",
|
|
517
|
+
href: src,
|
|
518
|
+
download: downloadName,
|
|
519
|
+
children: t("imageDownload")
|
|
520
|
+
})]
|
|
521
|
+
}),
|
|
522
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
523
|
+
ref: closeRef,
|
|
524
|
+
type: "button",
|
|
525
|
+
className: "codexGeneratedImageClose",
|
|
526
|
+
"aria-label": t("imageClosePreview"),
|
|
527
|
+
onClick: () => setOpen(false),
|
|
528
|
+
children: "×"
|
|
529
|
+
})
|
|
530
|
+
]
|
|
496
531
|
})] });
|
|
497
532
|
}
|
|
498
533
|
function CodexImageToolRow({ block, loadImage, t }) {
|
|
@@ -1311,18 +1346,28 @@ window.__ModuleLoader__.load({
|
|
|
1311
1346
|
})]
|
|
1312
1347
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1313
1348
|
className: "codexSubscriptionActions",
|
|
1314
|
-
children: [
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1349
|
+
children: [
|
|
1350
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1351
|
+
type: "button",
|
|
1352
|
+
variant: "outline",
|
|
1353
|
+
disabled: busy,
|
|
1354
|
+
onClick: load,
|
|
1355
|
+
children: t("diagnosticsLoad")
|
|
1356
|
+
}),
|
|
1357
|
+
report === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1358
|
+
type: "button",
|
|
1359
|
+
variant: "outline",
|
|
1360
|
+
onClick: copy,
|
|
1361
|
+
children: copied ? t("diagnosticsCopied") : t("diagnosticsCopy")
|
|
1362
|
+
}),
|
|
1363
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1364
|
+
className: "codexSubscriptionLink",
|
|
1365
|
+
href: SUPPORT_ISSUE_URL,
|
|
1366
|
+
target: "_blank",
|
|
1367
|
+
rel: "noreferrer",
|
|
1368
|
+
children: t("feedbackOpen")
|
|
1369
|
+
})
|
|
1370
|
+
]
|
|
1326
1371
|
})]
|
|
1327
1372
|
}),
|
|
1328
1373
|
report === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: JSON.stringify(report, null, 2) }),
|
|
@@ -1349,11 +1394,21 @@ window.__ModuleLoader__.load({
|
|
|
1349
1394
|
children: fill(t("resets"), { value })
|
|
1350
1395
|
});
|
|
1351
1396
|
}
|
|
1352
|
-
function
|
|
1397
|
+
function ResetCreditExpiry({ expiresAt, t }) {
|
|
1398
|
+
const date = validDate(expiresAt);
|
|
1399
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1400
|
+
className: "codexSubscriptionResetExpiry",
|
|
1401
|
+
children: date === void 0 ? t("resetCreditExpiryUnknown") : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
1402
|
+
dateTime: date.toISOString(),
|
|
1403
|
+
title: date.toLocaleString(),
|
|
1404
|
+
children: fill(t("resetCreditExpires"), { value: date.toLocaleString() })
|
|
1405
|
+
})
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
function ResetCreditControl({ rpc, t, count, nextExpiresAt, hasExhaustedQuota, onConsumed }) {
|
|
1353
1409
|
const [challenge, setChallenge] = (0, react.useState)();
|
|
1354
1410
|
const [resetBusy, setResetBusy] = (0, react.useState)(false);
|
|
1355
1411
|
const [resetAcknowledged, setResetAcknowledged] = (0, react.useState)(false);
|
|
1356
|
-
const [resetPhrase, setResetPhrase] = (0, react.useState)("");
|
|
1357
1412
|
const [resetCountdown, setResetCountdown] = (0, react.useState)(0);
|
|
1358
1413
|
const [resetError, setResetError] = (0, react.useState)();
|
|
1359
1414
|
const [resetResult, setResetResult] = (0, react.useState)();
|
|
@@ -1368,24 +1423,22 @@ window.__ModuleLoader__.load({
|
|
|
1368
1423
|
return () => window.clearInterval(timer);
|
|
1369
1424
|
}, [challenge]);
|
|
1370
1425
|
const prepareReset = () => {
|
|
1371
|
-
if (resetBusy
|
|
1426
|
+
if (resetBusy) return;
|
|
1372
1427
|
setResetBusy(true);
|
|
1373
1428
|
setResetError(void 0);
|
|
1374
1429
|
setResetResult(void 0);
|
|
1375
1430
|
rpc.call(CHANNEL, "reset-credit/prepare", {}).then(unwrap).then((next) => {
|
|
1376
1431
|
setChallenge(next);
|
|
1377
1432
|
setResetAcknowledged(false);
|
|
1378
|
-
setResetPhrase("");
|
|
1379
1433
|
}).catch((error) => setResetError(resetCreditErrorText(error, t))).finally(() => setResetBusy(false));
|
|
1380
1434
|
};
|
|
1381
1435
|
const cancelReset = () => {
|
|
1382
1436
|
if (resetBusy) return;
|
|
1383
1437
|
setChallenge(void 0);
|
|
1384
1438
|
setResetAcknowledged(false);
|
|
1385
|
-
setResetPhrase("");
|
|
1386
1439
|
setResetError(void 0);
|
|
1387
1440
|
};
|
|
1388
|
-
const resetReady = challenge !== void 0 && resetAcknowledged &&
|
|
1441
|
+
const resetReady = challenge !== void 0 && resetAcknowledged && resetCountdown === 0;
|
|
1389
1442
|
const consumeReset = () => {
|
|
1390
1443
|
if (resetBusy) return;
|
|
1391
1444
|
if (!resetReady) return;
|
|
@@ -1394,11 +1447,10 @@ window.__ModuleLoader__.load({
|
|
|
1394
1447
|
setResetResult(void 0);
|
|
1395
1448
|
rpc.call(CHANNEL, "reset-credit/consume", {
|
|
1396
1449
|
challengeId: challenge.challengeId,
|
|
1397
|
-
|
|
1450
|
+
acknowledged: resetAcknowledged
|
|
1398
1451
|
}).then(unwrap).then((result) => {
|
|
1399
1452
|
setChallenge(void 0);
|
|
1400
1453
|
setResetAcknowledged(false);
|
|
1401
|
-
setResetPhrase("");
|
|
1402
1454
|
const message = result.code === "reset" ? t("resetSuccess") : result.code === "nothing_to_reset" ? t("resetNothing") : result.code === "no_credit" ? t("resetNoCredit") : t("resetAlready");
|
|
1403
1455
|
setResetResult(message);
|
|
1404
1456
|
onConsumed();
|
|
@@ -1409,14 +1461,19 @@ window.__ModuleLoader__.load({
|
|
|
1409
1461
|
children: [
|
|
1410
1462
|
challenge === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1411
1463
|
className: "codexSubscriptionResetSummary",
|
|
1412
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1464
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1465
|
+
className: "codexSubscriptionResetMeta",
|
|
1466
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("resetCreditsValue"), { count }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetCreditExpiry, {
|
|
1467
|
+
expiresAt: nextExpiresAt,
|
|
1468
|
+
t
|
|
1469
|
+
})]
|
|
1470
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1413
1471
|
className: "codexSubscriptionActions",
|
|
1414
1472
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1415
1473
|
type: "button",
|
|
1416
1474
|
variant: "outline",
|
|
1417
|
-
disabled:
|
|
1475
|
+
disabled: resetBusy,
|
|
1418
1476
|
"aria-busy": resetBusy,
|
|
1419
|
-
title: !exhausted ? t("resetUnavailable") : void 0,
|
|
1420
1477
|
onClick: prepareReset,
|
|
1421
1478
|
children: resetBusy ? t("resetPreparing") : t("resetUse")
|
|
1422
1479
|
})
|
|
@@ -1434,9 +1491,13 @@ window.__ModuleLoader__.load({
|
|
|
1434
1491
|
className: "codexSubscriptionResetWarning",
|
|
1435
1492
|
children: challenge.description
|
|
1436
1493
|
}) : null,
|
|
1494
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetCreditExpiry, {
|
|
1495
|
+
expiresAt: challenge.creditExpiresAt,
|
|
1496
|
+
t
|
|
1497
|
+
}),
|
|
1437
1498
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1438
1499
|
className: "codexSubscriptionResetWarning",
|
|
1439
|
-
children: t("resetWarning")
|
|
1500
|
+
children: t(hasExhaustedQuota ? "resetWarning" : "resetEarlyWarning")
|
|
1440
1501
|
}),
|
|
1441
1502
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1442
1503
|
className: "codexSubscriptionResetCheck",
|
|
@@ -1447,20 +1508,6 @@ window.__ModuleLoader__.load({
|
|
|
1447
1508
|
onChange: (event) => setResetAcknowledged(event.target.checked)
|
|
1448
1509
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetAcknowledge") })]
|
|
1449
1510
|
}),
|
|
1450
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1451
|
-
className: "codexSubscriptionPreferenceCopy",
|
|
1452
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1453
|
-
className: "codexSubscriptionCreditLabel",
|
|
1454
|
-
children: fill(t("resetTypeHint"), { phrase: challenge.confirmPhrase })
|
|
1455
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1456
|
-
className: "codexSubscriptionInput codexSubscriptionResetPhrase",
|
|
1457
|
-
value: resetPhrase,
|
|
1458
|
-
disabled: resetBusy,
|
|
1459
|
-
autoComplete: "off",
|
|
1460
|
-
spellCheck: false,
|
|
1461
|
-
onChange: (event) => setResetPhrase(event.target.value)
|
|
1462
|
-
})]
|
|
1463
|
-
}),
|
|
1464
1511
|
resetCountdown > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1465
1512
|
className: "codexSubscriptionCreditNote",
|
|
1466
1513
|
role: "status",
|
|
@@ -1509,8 +1556,7 @@ window.__ModuleLoader__.load({
|
|
|
1509
1556
|
["This quota reset confirmation is no longer valid", "resetExpired"],
|
|
1510
1557
|
["This quota reset is already in progress", "resetInProgress"],
|
|
1511
1558
|
["Wait before confirming this quota reset", "resetTooEarly"],
|
|
1512
|
-
["
|
|
1513
|
-
["The current Codex quota is not exhausted", "resetNotExhausted"],
|
|
1559
|
+
["You must acknowledge that this may consume one quota reset", "resetAcknowledgeRequired"],
|
|
1514
1560
|
["The signed-in ChatGPT account changed", "resetAccountChanged"]
|
|
1515
1561
|
])).get(error instanceof Error ? error.message : "") ?? "resetFailed");
|
|
1516
1562
|
}
|
|
@@ -1648,7 +1694,8 @@ window.__ModuleLoader__.load({
|
|
|
1648
1694
|
rpc,
|
|
1649
1695
|
t,
|
|
1650
1696
|
count: visibleUsage.resetCredits.availableCount,
|
|
1651
|
-
|
|
1697
|
+
nextExpiresAt: visibleUsage.resetCredits.nextExpiresAt,
|
|
1698
|
+
hasExhaustedQuota: exhausted,
|
|
1652
1699
|
onConsumed: () => load(true)
|
|
1653
1700
|
})]
|
|
1654
1701
|
}) : null,
|
package/lib/index.js
CHANGED
|
@@ -503,7 +503,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
|
|
|
503
503
|
}
|
|
504
504
|
//#endregion
|
|
505
505
|
//#region src/version.js
|
|
506
|
-
const PACKAGE_VERSION = "1.
|
|
506
|
+
const PACKAGE_VERSION = "1.5.0";
|
|
507
507
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
508
508
|
//#endregion
|
|
509
509
|
//#region src/codex-search.js
|
|
@@ -643,6 +643,13 @@ const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0
|
|
|
643
643
|
function encodedLimit(decodedBytes) {
|
|
644
644
|
return Math.ceil(decodedBytes / 3) * 4;
|
|
645
645
|
}
|
|
646
|
+
function validBase64Body(value, end) {
|
|
647
|
+
for (let index = 0; index < end; index += 1) {
|
|
648
|
+
const code = value.charCodeAt(index);
|
|
649
|
+
if (!(code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47)) return false;
|
|
650
|
+
}
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
646
653
|
async function readJsonWithin(response, maximumBytes) {
|
|
647
654
|
const contentLength = Number(response.headers.get("content-length"));
|
|
648
655
|
if (Number.isFinite(contentLength) && contentLength > maximumBytes) throw new Error("Codex image response exceeds the image size limit");
|
|
@@ -670,8 +677,8 @@ async function readJsonWithin(response, maximumBytes) {
|
|
|
670
677
|
/** Strictly decode one PNG returned by the subscription backend. */
|
|
671
678
|
function decodeCodexPng(value, maximumBytes) {
|
|
672
679
|
const encoded = nonEmpty(value);
|
|
673
|
-
|
|
674
|
-
|
|
680
|
+
const padding = encoded?.endsWith("==") ? 2 : encoded?.endsWith("=") ? 1 : 0;
|
|
681
|
+
if (encoded === void 0 || encoded.length % 4 !== 0 || !validBase64Body(encoded, encoded.length - padding)) throw new Error("Codex returned an invalid base64 PNG");
|
|
675
682
|
const decodedBytes = encoded.length / 4 * 3 - padding;
|
|
676
683
|
if (decodedBytes > maximumBytes) throw new Error("Codex image exceeds the image size limit");
|
|
677
684
|
const data = Buffer.from(encoded, "base64");
|
|
@@ -941,7 +948,18 @@ function spendControlReachedOf(value) {
|
|
|
941
948
|
function resetCreditsOf(value) {
|
|
942
949
|
if (value === void 0 || value === null) return void 0;
|
|
943
950
|
if (!record$1(value) || !Number.isSafeInteger(value.available_count) || value.available_count < 0) throw new Error("Codex returned malformed reset credit details");
|
|
944
|
-
|
|
951
|
+
if (value.credits !== void 0 && value.credits !== null && !Array.isArray(value.credits)) throw new Error("Codex returned malformed reset credit details");
|
|
952
|
+
const expirations = (value.credits ?? []).filter((credit) => record$1(credit) && credit.status?.toLowerCase?.() === "available").map((credit) => {
|
|
953
|
+
if (Number.isSafeInteger(credit.expires_at) && credit.expires_at > 0) return credit.expires_at * 1e3;
|
|
954
|
+
if (typeof credit.expires_at === "string" && credit.expires_at.length <= 64) {
|
|
955
|
+
const parsed = Date.parse(credit.expires_at);
|
|
956
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
957
|
+
}
|
|
958
|
+
}).filter((expiration) => expiration !== void 0);
|
|
959
|
+
return {
|
|
960
|
+
availableCount: value.available_count,
|
|
961
|
+
...expirations.length === 0 ? {} : { nextExpiresAt: Math.min(...expirations) }
|
|
962
|
+
};
|
|
945
963
|
}
|
|
946
964
|
/** Reduce the provider payload to a browser-safe quota projection. */
|
|
947
965
|
function parseCodexUsage(value) {
|
|
@@ -1047,7 +1065,6 @@ function createCodexUsageReader(options) {
|
|
|
1047
1065
|
//#region src/reset-credits.js
|
|
1048
1066
|
const CODEX_RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";
|
|
1049
1067
|
const CODEX_RESET_CONSUME_URL = `${CODEX_RESET_CREDITS_URL}/consume`;
|
|
1050
|
-
const RESET_CONFIRM_PHRASE = "USE RESET";
|
|
1051
1068
|
const DEFAULT_CONFIRM_DELAY_MS = 5e3;
|
|
1052
1069
|
const DEFAULT_CHALLENGE_TTL_MS = 6e4;
|
|
1053
1070
|
const DEFAULT_TIMEOUT_MS = 15e3;
|
|
@@ -1069,15 +1086,22 @@ function credentialsOf(auth, credential) {
|
|
|
1069
1086
|
accountId
|
|
1070
1087
|
};
|
|
1071
1088
|
}
|
|
1089
|
+
function expirationOf(value) {
|
|
1090
|
+
if (value === void 0 || value === null) return void 0;
|
|
1091
|
+
if (Number.isSafeInteger(value) && value > 0) return value * 1e3;
|
|
1092
|
+
if (typeof value === "string" && value.length > 0 && value.length <= 64) {
|
|
1093
|
+
const parsed = Date.parse(value);
|
|
1094
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
1095
|
+
}
|
|
1096
|
+
throw new Error("ChatGPT returned malformed quota reset details");
|
|
1097
|
+
}
|
|
1072
1098
|
function parseDetails(value, now) {
|
|
1073
1099
|
if (!record(value) || !Number.isSafeInteger(value.available_count) || value.available_count < 0 || !Array.isArray(value.credits)) throw new Error("ChatGPT returned malformed quota reset details");
|
|
1074
1100
|
if (value.available_count === 0) throw new Error("No quota reset is available");
|
|
1075
1101
|
const available = value.credits.filter((credit) => record(credit) && typeof credit.id === "string" && credit.id.length > 0 && credit.id.length <= 256 && typeof credit.status === "string" && credit.status.toLowerCase() === "available").map((credit) => {
|
|
1076
|
-
const expiresAt = credit.expires_at === void 0 || credit.expires_at === null ? void 0 : credit.expires_at * 1e3;
|
|
1077
|
-
if (expiresAt !== void 0 && (!Number.isSafeInteger(credit.expires_at) || credit.expires_at <= 0)) throw new Error("ChatGPT returned malformed quota reset details");
|
|
1078
1102
|
return {
|
|
1079
1103
|
credit,
|
|
1080
|
-
expiresAt
|
|
1104
|
+
expiresAt: expirationOf(credit.expires_at)
|
|
1081
1105
|
};
|
|
1082
1106
|
}).filter(({ expiresAt }) => expiresAt === void 0 || expiresAt > now).sort((a, b) => (a.expiresAt ?? Number.MAX_SAFE_INTEGER) - (b.expiresAt ?? Number.MAX_SAFE_INTEGER));
|
|
1083
1107
|
if (available.length === 0) throw new Error("No usable quota reset is available");
|
|
@@ -1097,14 +1121,13 @@ function parseConsumeResult(value) {
|
|
|
1097
1121
|
"already_redeemed"
|
|
1098
1122
|
].includes(value.code)) throw new Error("ChatGPT returned an unreadable quota reset response");
|
|
1099
1123
|
const windowsReset = Array.isArray(value.windows_reset) ? value.windows_reset.filter((item) => typeof item === "string").slice(0, 16) : [];
|
|
1124
|
+
const windowsResetCount = Number.isSafeInteger(value.windows_reset) && value.windows_reset >= 0 && value.windows_reset <= 16 ? value.windows_reset : void 0;
|
|
1100
1125
|
return {
|
|
1101
1126
|
code: value.code,
|
|
1102
|
-
windowsReset
|
|
1127
|
+
windowsReset,
|
|
1128
|
+
...windowsResetCount === void 0 ? {} : { windowsResetCount }
|
|
1103
1129
|
};
|
|
1104
1130
|
}
|
|
1105
|
-
function quotaIsExhausted(usage) {
|
|
1106
|
-
return Array.isArray(usage?.rateLimits) && usage.rateLimits.some((limit) => limit?.id !== "code_review" && Array.isArray(limit?.windows) && limit.windows.some((window) => Number.isFinite(window?.usedPercent) && window.usedPercent >= 100));
|
|
1107
|
-
}
|
|
1108
1131
|
/**
|
|
1109
1132
|
* Host-only reset redemption. The browser receives an opaque, short-lived
|
|
1110
1133
|
* challenge; account ids, bearer tokens, credit ids, and idempotency keys stay
|
|
@@ -1161,14 +1184,14 @@ function createCodexResetCreditService(options) {
|
|
|
1161
1184
|
return {
|
|
1162
1185
|
challengeId,
|
|
1163
1186
|
availableCount: details.availableCount,
|
|
1164
|
-
confirmPhrase: RESET_CONFIRM_PHRASE,
|
|
1165
1187
|
readyAt,
|
|
1166
1188
|
expiresAt,
|
|
1189
|
+
...details.creditExpiresAt === void 0 ? {} : { creditExpiresAt: details.creditExpiresAt },
|
|
1167
1190
|
...details.title === void 0 ? {} : { title: details.title },
|
|
1168
1191
|
...details.description === void 0 ? {} : { description: details.description }
|
|
1169
1192
|
};
|
|
1170
1193
|
},
|
|
1171
|
-
async consume({ challengeId,
|
|
1194
|
+
async consume({ challengeId, acknowledged, signal } = {}) {
|
|
1172
1195
|
const challenge = typeof challengeId === "string" ? challenges.get(challengeId) : void 0;
|
|
1173
1196
|
if (challenge === void 0) throw new Error("This quota reset confirmation is no longer valid");
|
|
1174
1197
|
if (challenge.state === "pending") throw new Error("This quota reset is already in progress");
|
|
@@ -1177,13 +1200,9 @@ function createCodexResetCreditService(options) {
|
|
|
1177
1200
|
challenges.delete(challengeId);
|
|
1178
1201
|
throw new Error("This quota reset confirmation is no longer valid");
|
|
1179
1202
|
}
|
|
1180
|
-
if (
|
|
1203
|
+
if (acknowledged !== true) throw new Error("You must acknowledge that this may consume one quota reset");
|
|
1181
1204
|
challenge.state = "pending";
|
|
1182
1205
|
try {
|
|
1183
|
-
if (!quotaIsExhausted(await usageReader.read({
|
|
1184
|
-
force: true,
|
|
1185
|
-
signal
|
|
1186
|
-
}))) throw new Error("The current Codex quota is not exhausted");
|
|
1187
1206
|
const { access, accountId } = await resolveCredentials(signal);
|
|
1188
1207
|
if (accountId !== challenge.accountId) throw new Error("The signed-in ChatGPT account changed");
|
|
1189
1208
|
const response = await fetchReset(CODEX_RESET_CONSUME_URL, {
|
|
@@ -1316,7 +1335,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1316
1335
|
ok: true,
|
|
1317
1336
|
value: endpoint === "reset-credit/prepare" ? await resetCreditService.prepare({ signal }) : await resetCreditService.consume({
|
|
1318
1337
|
challengeId: payload?.challengeId,
|
|
1319
|
-
|
|
1338
|
+
acknowledged: payload?.acknowledged,
|
|
1320
1339
|
signal
|
|
1321
1340
|
})
|
|
1322
1341
|
};
|
|
@@ -1331,8 +1350,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1331
1350
|
"This quota reset confirmation is no longer valid",
|
|
1332
1351
|
"This quota reset is already in progress",
|
|
1333
1352
|
"Wait before confirming this quota reset",
|
|
1334
|
-
"
|
|
1335
|
-
"The current Codex quota is not exhausted",
|
|
1353
|
+
"You must acknowledge that one quota reset will be consumed",
|
|
1336
1354
|
"The signed-in ChatGPT account changed"
|
|
1337
1355
|
]);
|
|
1338
1356
|
const fallback = endpoint === "reset-credit/prepare" ? "Could not prepare a quota reset" : "Could not use the quota reset";
|
|
@@ -1476,4 +1494,4 @@ function apply(ctx) {
|
|
|
1476
1494
|
ctx.effect(() => ctx.connection.rpc.handle(CHANNEL, handler, { authority: "loopback" }), "codex-subscription: loopback account RPC");
|
|
1477
1495
|
}
|
|
1478
1496
|
//#endregion
|
|
1479
|
-
export { CODEX_IMAGE_GENERATION_URL, CODEX_IMAGE_TOOL_NAME, CODEX_RESET_CONSUME_URL, CODEX_RESET_CREDITS_URL, CODEX_USAGE_URL, CodexLoginCoordinator, DshOAuthCredentialStore,
|
|
1497
|
+
export { CODEX_IMAGE_GENERATION_URL, CODEX_IMAGE_TOOL_NAME, CODEX_RESET_CONSUME_URL, CODEX_RESET_CREDITS_URL, CODEX_USAGE_URL, CodexLoginCoordinator, DshOAuthCredentialStore, apply, assertCodexAuthUrl, commandForCodexAuthUrl, createCodexAuthService, createCodexImageTool, createCodexResetCreditService, createCodexRpcHandler, createCodexUsageReader, createSearchProviderSwitcher, createSubscriptionDiagnostics, createSubscriptionRpcHandler, decodeCodexPng, inject, name, openCodexAuthUrl, parseCodexUsage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|