dsh-codex-subscription 1.5.0 → 1.6.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/DIRECTORY.md +3 -3
- package/README.md +12 -8
- package/README.zh-CN.md +11 -7
- package/lib/client.js +140 -67
- package/lib/index.js +107 -37
- 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.6.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.
|
|
@@ -34,13 +34,13 @@ For an official DSH installation run through npm, keep the complete `npx` prefix
|
|
|
34
34
|
run command does not create a global `dsh` command:
|
|
35
35
|
|
|
36
36
|
```sh
|
|
37
|
-
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.6.0
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
With an existing global `dsh` command:
|
|
41
41
|
|
|
42
42
|
```sh
|
|
43
|
-
dsh plugin --profile web add dsh-codex-subscription@1.
|
|
43
|
+
dsh plugin --profile web add dsh-codex-subscription@1.6.0
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Use the same `add` command to update or repair. This is the complete package-changing operation.
|
|
@@ -58,7 +58,7 @@ dsh --profile web --dump-config
|
|
|
58
58
|
|
|
59
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`.
|
|
60
60
|
|
|
61
|
-
1. `dsh-codex-subscription` version `1.
|
|
61
|
+
1. `dsh-codex-subscription` version `1.6.0` appears exactly once.
|
|
62
62
|
2. `codex-subscription` appears exactly once in the composed config.
|
|
63
63
|
3. No unrelated plugin or profile was changed and DSH was not restarted.
|
|
64
64
|
|
package/DIRECTORY.md
CHANGED
|
@@ -4,11 +4,11 @@ Use this factual copy when listing `dsh-codex-subscription` in a DSH plugin dire
|
|
|
4
4
|
|
|
5
5
|
## Short description
|
|
6
6
|
|
|
7
|
-
Use a ChatGPT subscription in DeepSeek Harness through independent OAuth sign-in, with Codex models, backend-reported quota, subscription web search, image generation, and no silent paid fallback.
|
|
7
|
+
Use a ChatGPT subscription in DeepSeek Harness through independent OAuth sign-in, with Codex models, backend-reported quota, subscription web search, Beta image generation/editing, and no silent paid fallback.
|
|
8
8
|
|
|
9
9
|
## 中文短描述
|
|
10
10
|
|
|
11
|
-
通过独立 OAuth 登录在 DeepSeek Harness 中使用 ChatGPT 订阅,提供 Codex
|
|
11
|
+
通过独立 OAuth 登录在 DeepSeek Harness 中使用 ChatGPT 订阅,提供 Codex 模型、服务端实际额度、订阅搜索和 Beta 图片生成与编辑,不会静默切换其他付费路由。
|
|
12
12
|
|
|
13
13
|
## Facts
|
|
14
14
|
|
|
@@ -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 and expiring resets, web search, image preview/download
|
|
21
|
+
- Main capabilities: Codex models, quota and expiring resets, web search, Beta image generation/editing with explicit references and native preview/download, optional composer quota, 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
|
@@ -8,7 +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)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://github.com/WSL043/dsh-codex-subscription/stargazers)
|
|
14
14
|
|
|
@@ -49,8 +49,8 @@ DSH-Portable exposes the same standard plugin command, so the command above also
|
|
|
49
49
|
| **Composer quota** | Choose a compact percentage, progress bar, or no inline quota display |
|
|
50
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** |
|
|
53
|
-
| **Fast mode
|
|
52
|
+
| **Codex image generation and editing (Beta)** | Generate without references, or explicitly edit selected conversation images; preview, zoom, and download the original |
|
|
53
|
+
| **Fast mode** | 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.
|
|
56
56
|
|
|
@@ -139,13 +139,13 @@ Restart DSH manually after installation, then:
|
|
|
139
139
|
## Features
|
|
140
140
|
|
|
141
141
|
- ChatGPT OAuth sign-in with credentials kept on the host;
|
|
142
|
-
- Codex models and image generation directly inside DSH conversations;
|
|
142
|
+
- Codex models and Beta image generation/editing directly inside DSH conversations;
|
|
143
143
|
- A clear choice between DSH default search and Codex subscription search;
|
|
144
144
|
- Actual backend-provided quota, reset time, and freshness;
|
|
145
145
|
- Separate standard Codex, Codex-Spark, Credits, and other independent limits;
|
|
146
146
|
- Visible quota-reset count and earliest expiry, with deliberate early redemption, layered confirmation, and no automatic retry;
|
|
147
147
|
- Optional percentage or progress bar for the selected Codex model (off by default);
|
|
148
|
-
- Standard or Fast mode for supported Codex models directly in the composer
|
|
148
|
+
- Standard or Fast mode for supported Codex models directly in the composer;
|
|
149
149
|
- A copyable support report and direct feedback link in Settings; the report excludes OAuth credentials, account identifiers, and authorization timestamps;
|
|
150
150
|
- Visible errors when subscription routing is unavailable, with no silent paid fallback.
|
|
151
151
|
|
|
@@ -167,10 +167,14 @@ decides whether a window needs resetting and may return **nothing to reset** wit
|
|
|
167
167
|
action requires an acknowledgement checkbox and five-second cooldown. Cancel never consumes a reset, rapid repeated
|
|
168
168
|
clicks are single-flight, and an uncertain network result is never retried automatically.
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
change in the same conversation; the durable conversation image remains available to the next model turn.
|
|
170
|
+
### Image generation and editing (Beta)
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
Generated images open in a DSH-native preview with zoom, fit, dimensions, and **Download original**. A new image request
|
|
173
|
+
does not silently include earlier images. To refine a result, explicitly ask to edit that image in the same conversation;
|
|
174
|
+
the plugin sends only the selected durable image references, up to five. GPT Image 2 can take longer than a normal text
|
|
175
|
+
turn, and detailed text, exact composition, or repeated-character consistency may still need another pass.
|
|
176
|
+
|
|
177
|
+
### Composer speed
|
|
174
178
|
|
|
175
179
|
With a supported Codex model selected, open the composer's model menu to choose Standard or Fast.
|
|
176
180
|
Standard adds no icon; only Fast shows a lightning icon before the model name. Spark does not show the speed entry. Fast mode increases speed and uses more Credits;
|
package/README.zh-CN.md
CHANGED
|
@@ -9,7 +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)
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
[](https://github.com/WSL043/dsh-codex-subscription/stargazers)
|
|
15
15
|
|
|
@@ -50,8 +50,8 @@ DSH-Portable 也提供相同的标准插件命令,因此同样使用上面的
|
|
|
50
50
|
| **输入框额度** | 可选择紧凑百分比、进度条或完全关闭输入框额度显示 |
|
|
51
51
|
| **安全额度重置** | 直接查看最早到期时间,并通过冷静期和知情确认主动尝试重置 |
|
|
52
52
|
| **订阅搜索** | 可在 DSH 默认搜索与 Codex 订阅搜索之间明确切换 |
|
|
53
|
-
| **Codex
|
|
54
|
-
|
|
|
53
|
+
| **Codex 图片生成与编辑(Beta)** | 可无参考图全新生成,也可明确选择会话图片继续编辑;支持预览、缩放和下载原图 |
|
|
54
|
+
| **高速模式** | 直接在输入框切换标准或高速,无需离开当前会话 |
|
|
55
55
|
|
|
56
56
|
这些能力共用同一份本机 ChatGPT 登录。订阅路由失败时会明确报错,不会静默切换到其他付费路由。
|
|
57
57
|
|
|
@@ -139,13 +139,13 @@ dsh --profile web --dump-config
|
|
|
139
139
|
## 功能
|
|
140
140
|
|
|
141
141
|
- ChatGPT OAuth 登录,凭据保留在本机;
|
|
142
|
-
- Codex
|
|
142
|
+
- Codex 模型和 Beta 图片生成与编辑直接出现在 DSH 会话中;
|
|
143
143
|
- DSH 默认搜索与 Codex 订阅搜索可随时切换;
|
|
144
144
|
- 设置页显示服务端返回的额度、重置时间和更新时间;
|
|
145
145
|
- 普通 Codex、Codex-Spark、Credits 等独立额度分开显示;
|
|
146
146
|
- 显示重置卡数量与最早到期时间,也允许在额度未完全用尽时主动尝试,并经过分层确认且不会自动重试;
|
|
147
147
|
- 输入框可用百分比或进度条显示当前 Codex 模型的剩余额度(默认关闭);
|
|
148
|
-
- 输入框可为支持的 Codex
|
|
148
|
+
- 输入框可为支持的 Codex 模型切换标准或高速模式;
|
|
149
149
|
- 设置页可生成并复制无敏感信息的支持诊断,并直接打开反馈入口;报告不包含 OAuth 凭据、账号标识或授权时间;
|
|
150
150
|
- 订阅路由不可用时明确报错,不会静默切换到其他付费路由。
|
|
151
151
|
|
|
@@ -164,9 +164,13 @@ ChatGPT 返回可用重置卡时,设置页会用紧凑的一行显示数量和
|
|
|
164
164
|
也可以主动尝试使用,适合重置卡即将过期的情况;是否需要重置仍由 ChatGPT 判断,服务端可能返回“当前无需重置”且不扣次数。
|
|
165
165
|
最终操作需要勾选知情确认并等待 5 秒。取消不会消耗,快速连续点击只允许一次请求,网络结果不确定时也不会自动重试。
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
### 图片生成与编辑(Beta)
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
生成的图片会在 DSH 原生风格的查看器中打开,支持缩放、适合窗口、查看尺寸和下载原图。全新生成不会静默带入历史图片;
|
|
170
|
+
需要继续修改时,要在同一会话明确说明编辑这张图,插件只会发送明确选中的持久图片引用,最多 5 张。GPT Image 2 的耗时可能明显长于
|
|
171
|
+
文本回复,复杂文字、精确构图和连续角色一致性也可能需要再次调整。
|
|
172
|
+
|
|
173
|
+
### 输入框速度
|
|
170
174
|
|
|
171
175
|
选择支持的 Codex 模型后,可在输入框的模型菜单中切换标准与高速。标准模式不增加图标,
|
|
172
176
|
只有高速模式会在模型名称左侧显示闪电;Spark 不显示速度入口。高速模式会提高速度,也会消耗更多 Credits;具体规则见
|
package/lib/client.js
CHANGED
|
@@ -185,14 +185,16 @@ window.__ModuleLoader__.load({
|
|
|
185
185
|
monthlyCreditLimit: "Credits 月度消费上限",
|
|
186
186
|
resetCredits: "可用额度重置次数",
|
|
187
187
|
resetCreditsValue: "{count} 次",
|
|
188
|
-
resetUse: "
|
|
188
|
+
resetUse: "使用重置",
|
|
189
189
|
resetPreparing: "正在读取重置详情…",
|
|
190
190
|
resetConfirmTitle: "确认使用额度重置",
|
|
191
191
|
resetWarning: "若 ChatGPT 执行重置,将立即消耗 1 次且无法撤销。",
|
|
192
192
|
resetEarlyWarning: "当前模型额度尚未用尽;ChatGPT 可能执行重置,也可能判定暂无需重置且不消耗次数。",
|
|
193
193
|
resetAcknowledge: "我知道这次操作可能立即消耗 1 次重置",
|
|
194
|
-
resetCreditExpires: "
|
|
194
|
+
resetCreditExpires: "到期:{value}",
|
|
195
195
|
resetCreditExpiryUnknown: "到期时间未提供",
|
|
196
|
+
resetCreditExpiryLoading: "正在读取到期时间…",
|
|
197
|
+
resetCreditExpiryFailed: "无法读取到期时间",
|
|
196
198
|
resetWait: "请再等待 {count} 秒",
|
|
197
199
|
resetFinal: "消耗 1 次并重置额度",
|
|
198
200
|
resetUsing: "正在重置…",
|
|
@@ -233,6 +235,7 @@ window.__ModuleLoader__.load({
|
|
|
233
235
|
modelFailed: "模型目录加载失败:{value}",
|
|
234
236
|
groupFailed: "{name}:{value}",
|
|
235
237
|
imageGenerate: "生成图片",
|
|
238
|
+
imageBeta: "Beta",
|
|
236
239
|
imageGenerating: "正在生成…",
|
|
237
240
|
imageGenerated: "已生成",
|
|
238
241
|
imageFailed: "生成失败",
|
|
@@ -244,7 +247,11 @@ window.__ModuleLoader__.load({
|
|
|
244
247
|
imagePreview: "图片预览",
|
|
245
248
|
imageClosePreview: "关闭预览",
|
|
246
249
|
imageDownload: "下载原图",
|
|
247
|
-
|
|
250
|
+
imageZoomOut: "缩小",
|
|
251
|
+
imageZoomIn: "放大",
|
|
252
|
+
imageFit: "适合窗口",
|
|
253
|
+
imageRefineHint: "要修改这张图,请在当前会话明确说“编辑这张图片……”;插件只会把这张图作为参考。",
|
|
254
|
+
imageNewHint: "要重新生成,请直接描述一张新图片;不会自动带入历史图片。"
|
|
248
255
|
};
|
|
249
256
|
const en = {
|
|
250
257
|
nav: "Codex",
|
|
@@ -301,14 +308,16 @@ window.__ModuleLoader__.load({
|
|
|
301
308
|
monthlyCreditLimit: "Monthly Credits spending cap",
|
|
302
309
|
resetCredits: "Available quota resets",
|
|
303
310
|
resetCreditsValue: "{count} available",
|
|
304
|
-
resetUse: "
|
|
311
|
+
resetUse: "Use reset",
|
|
305
312
|
resetPreparing: "Reading reset details…",
|
|
306
313
|
resetConfirmTitle: "Confirm quota reset",
|
|
307
314
|
resetWarning: "If ChatGPT performs the reset, one reset is consumed immediately and cannot be restored.",
|
|
308
315
|
resetEarlyWarning: "No model quota is exhausted. ChatGPT may reset it, or report that nothing needs resetting without consuming a reset.",
|
|
309
316
|
resetAcknowledge: "I understand this may consume one reset now",
|
|
310
|
-
resetCreditExpires: "
|
|
317
|
+
resetCreditExpires: "Expires {value}",
|
|
311
318
|
resetCreditExpiryUnknown: "Expiration time not provided",
|
|
319
|
+
resetCreditExpiryLoading: "Reading expiration…",
|
|
320
|
+
resetCreditExpiryFailed: "Could not read expiration",
|
|
312
321
|
resetWait: "Wait {count} more seconds",
|
|
313
322
|
resetFinal: "Consume one reset",
|
|
314
323
|
resetUsing: "Resetting…",
|
|
@@ -349,6 +358,7 @@ window.__ModuleLoader__.load({
|
|
|
349
358
|
modelFailed: "Could not load models: {value}",
|
|
350
359
|
groupFailed: "{name}: {value}",
|
|
351
360
|
imageGenerate: "Generate image",
|
|
361
|
+
imageBeta: "Beta",
|
|
352
362
|
imageGenerating: "Generating…",
|
|
353
363
|
imageGenerated: "Generated",
|
|
354
364
|
imageFailed: "Generation failed",
|
|
@@ -360,7 +370,11 @@ window.__ModuleLoader__.load({
|
|
|
360
370
|
imagePreview: "Image preview",
|
|
361
371
|
imageClosePreview: "Close preview",
|
|
362
372
|
imageDownload: "Download original",
|
|
363
|
-
|
|
373
|
+
imageZoomOut: "Zoom out",
|
|
374
|
+
imageZoomIn: "Zoom in",
|
|
375
|
+
imageFit: "Fit to window",
|
|
376
|
+
imageRefineHint: "To change this image, say “Edit this image…” in this conversation; only this image is sent as a reference.",
|
|
377
|
+
imageNewHint: "To start over, describe a new image; prior images are not included automatically."
|
|
364
378
|
};
|
|
365
379
|
const STYLE = `
|
|
366
380
|
.codexSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
|
|
@@ -395,7 +409,8 @@ window.__ModuleLoader__.load({
|
|
|
395
409
|
.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)}
|
|
396
410
|
.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}
|
|
397
411
|
.codexModelSelectMenu{overflow:visible}
|
|
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}.
|
|
412
|
+
.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}.codexGeneratedImageModal{width:min(920px,calc(100vw - 32px));max-height:calc(100vh - 32px)}.codexGeneratedImageModalContent{min-height:0;overflow:hidden}.codexGeneratedImageViewer{display:flex;min-width:0;flex-direction:column;gap:12px}.codexGeneratedImageStage{display:grid;place-items:center;min-height:280px;max-height:calc(100vh - 260px);overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-module-platform)}.codexGeneratedImageStage img{display:block;max-width:100%;max-height:calc(100vh - 280px);object-fit:contain;transform-origin:center;transition:transform 120ms ease}.codexGeneratedImageMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.codexGeneratedImageToolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.codexGeneratedImageZoom{display:flex;align-items:center;gap:6px}.codexGeneratedImageZoomValue{min-width:44px;color:var(--dsw-alias-label-secondary);font-size:12px;text-align:center}.codexGeneratedImageDownload{display:inline-flex;align-items:center;gap:6px;min-height:32px;box-sizing:border-box;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:transparent;color:var(--dsw-alias-label-primary);font-size:13px;text-decoration:none}.codexGeneratedImageDownload:hover{background:var(--dsw-alias-interactive-bg-hover)}.codexGeneratedImageGuidance{display:flex;flex-direction:column;gap:2px;padding-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}@keyframes codexImageSpin{to{transform:rotate(360deg)}}
|
|
413
|
+
.codexImageBeta{padding:0 5px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:16px}
|
|
399
414
|
@container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
|
|
400
415
|
@container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
|
|
401
416
|
@media(max-width:640px){.codexSubscriptionCard{padding:14px}}
|
|
@@ -427,13 +442,13 @@ window.__ModuleLoader__.load({
|
|
|
427
442
|
if (cleaned === "") return fallback;
|
|
428
443
|
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
429
444
|
};
|
|
445
|
+
const imageByteSize = (bytes) => bytes < 1024 * 1024 ? `${Math.max(.1, bytes / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} KB` : `${(bytes / 1024 / 1024).toLocaleString(void 0, { maximumFractionDigits: 1 })} MB`;
|
|
430
446
|
function CodexGeneratedImage({ attachment, loadImage, t }) {
|
|
431
447
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
432
448
|
const [error, setError] = (0, react.useState)(false);
|
|
433
449
|
const [open, setOpen] = (0, react.useState)(false);
|
|
434
450
|
const [src, setSrc] = (0, react.useState)();
|
|
435
|
-
const
|
|
436
|
-
const downloadRef = (0, react.useRef)(null);
|
|
451
|
+
const [zoom, setZoom] = (0, react.useState)(1);
|
|
437
452
|
const triggerRef = (0, react.useRef)(null);
|
|
438
453
|
(0, react.useEffect)(() => {
|
|
439
454
|
let live = true;
|
|
@@ -452,28 +467,13 @@ window.__ModuleLoader__.load({
|
|
|
452
467
|
loadImage,
|
|
453
468
|
attempt
|
|
454
469
|
]);
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
event.preventDefault();
|
|
460
|
-
setOpen(false);
|
|
461
|
-
} else if (event.key === "Tab") {
|
|
462
|
-
event.preventDefault();
|
|
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();
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
469
|
-
closeRef.current?.focus();
|
|
470
|
-
return () => {
|
|
471
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
472
|
-
triggerRef.current?.focus();
|
|
473
|
-
};
|
|
474
|
-
}, [open]);
|
|
470
|
+
const close = () => {
|
|
471
|
+
setOpen(false);
|
|
472
|
+
window.requestAnimationFrame(() => triggerRef.current?.focus());
|
|
473
|
+
};
|
|
475
474
|
const label = attachment.name ?? t("imageLabel");
|
|
476
475
|
const downloadName = imageDownloadName(attachment);
|
|
476
|
+
const imageMeta = `${attachment.width} × ${attachment.height} · ${imageByteSize(attachment.bytes)}`;
|
|
477
477
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
478
478
|
type: "button",
|
|
479
479
|
className: "codexGeneratedImageRetry",
|
|
@@ -487,47 +487,84 @@ window.__ModuleLoader__.load({
|
|
|
487
487
|
title: t("imageOpen"),
|
|
488
488
|
"aria-label": fill(t("imageOpenNamed"), { value: label }),
|
|
489
489
|
onClick: () => {
|
|
490
|
-
if (src !== void 0)
|
|
490
|
+
if (src !== void 0) {
|
|
491
|
+
setZoom(1);
|
|
492
|
+
setOpen(true);
|
|
493
|
+
}
|
|
491
494
|
},
|
|
492
495
|
children: src === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
493
496
|
src,
|
|
494
497
|
alt: label
|
|
495
498
|
})
|
|
496
|
-
}),
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
499
|
+
}), src === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
500
|
+
open,
|
|
501
|
+
onClose: close,
|
|
502
|
+
title: t("imagePreview"),
|
|
503
|
+
closeLabel: t("imageClosePreview"),
|
|
504
|
+
className: "codexGeneratedImageModal",
|
|
505
|
+
contentClassName: "codexGeneratedImageModalContent",
|
|
506
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
507
|
+
className: "codexGeneratedImageViewer",
|
|
508
|
+
children: [
|
|
509
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
510
|
+
className: "codexGeneratedImageStage",
|
|
511
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
512
|
+
src,
|
|
513
|
+
alt: label,
|
|
514
|
+
style: { transform: `scale(${zoom})` }
|
|
515
|
+
})
|
|
516
|
+
}),
|
|
517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
518
|
+
className: "codexGeneratedImageMeta",
|
|
519
|
+
children: imageMeta
|
|
520
|
+
}),
|
|
521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
522
|
+
className: "codexGeneratedImageToolbar",
|
|
523
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
524
|
+
className: "codexGeneratedImageZoom",
|
|
525
|
+
children: [
|
|
526
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
527
|
+
type: "button",
|
|
528
|
+
variant: "outline",
|
|
529
|
+
"aria-label": t("imageZoomOut"),
|
|
530
|
+
title: t("imageZoomOut"),
|
|
531
|
+
disabled: zoom <= .5,
|
|
532
|
+
onClick: () => setZoom((value) => Math.max(.5, value - .25)),
|
|
533
|
+
children: "−"
|
|
534
|
+
}),
|
|
535
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
536
|
+
className: "codexGeneratedImageZoomValue",
|
|
537
|
+
children: [Math.round(zoom * 100), "%"]
|
|
538
|
+
}),
|
|
539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
540
|
+
type: "button",
|
|
541
|
+
variant: "outline",
|
|
542
|
+
"aria-label": t("imageZoomIn"),
|
|
543
|
+
title: t("imageZoomIn"),
|
|
544
|
+
disabled: zoom >= 3,
|
|
545
|
+
onClick: () => setZoom((value) => Math.min(3, value + .25)),
|
|
546
|
+
children: "+"
|
|
547
|
+
}),
|
|
548
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
549
|
+
type: "button",
|
|
550
|
+
variant: "outline",
|
|
551
|
+
onClick: () => setZoom(1),
|
|
552
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { "aria-hidden": "true" }), t("imageFit")]
|
|
553
|
+
})
|
|
554
|
+
]
|
|
555
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
556
|
+
className: "codexGeneratedImageDownload",
|
|
557
|
+
href: src,
|
|
558
|
+
download: downloadName,
|
|
559
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { "aria-hidden": "true" }), t("imageDownload")]
|
|
560
|
+
})]
|
|
561
|
+
}),
|
|
562
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
563
|
+
className: "codexGeneratedImageGuidance",
|
|
564
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageRefineHint") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageNewHint") })]
|
|
565
|
+
})
|
|
566
|
+
]
|
|
567
|
+
})
|
|
531
568
|
})] });
|
|
532
569
|
}
|
|
533
570
|
function CodexImageToolRow({ block, loadImage, t }) {
|
|
@@ -552,6 +589,10 @@ window.__ModuleLoader__.load({
|
|
|
552
589
|
className: "codexImageToolTitle",
|
|
553
590
|
children: t("imageGenerate")
|
|
554
591
|
}),
|
|
592
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
593
|
+
className: "codexImageBeta",
|
|
594
|
+
children: t("imageBeta")
|
|
595
|
+
}),
|
|
555
596
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
556
597
|
className: "codexImageToolState",
|
|
557
598
|
children: status
|
|
@@ -1412,6 +1453,31 @@ window.__ModuleLoader__.load({
|
|
|
1412
1453
|
const [resetCountdown, setResetCountdown] = (0, react.useState)(0);
|
|
1413
1454
|
const [resetError, setResetError] = (0, react.useState)();
|
|
1414
1455
|
const [resetResult, setResetResult] = (0, react.useState)();
|
|
1456
|
+
const [inspectedExpiry, setInspectedExpiry] = (0, react.useState)(nextExpiresAt);
|
|
1457
|
+
const [expiryState, setExpiryState] = (0, react.useState)(nextExpiresAt === void 0 ? "loading" : "ready");
|
|
1458
|
+
(0, react.useEffect)(() => {
|
|
1459
|
+
if (nextExpiresAt !== void 0) {
|
|
1460
|
+
setInspectedExpiry(nextExpiresAt);
|
|
1461
|
+
setExpiryState("ready");
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
let live = true;
|
|
1465
|
+
setExpiryState("loading");
|
|
1466
|
+
rpc.call(CHANNEL, "reset-credit/inspect", {}).then(unwrap).then((value) => {
|
|
1467
|
+
if (!live) return;
|
|
1468
|
+
setInspectedExpiry(value.nextExpiresAt);
|
|
1469
|
+
setExpiryState("ready");
|
|
1470
|
+
}).catch(() => {
|
|
1471
|
+
if (live) setExpiryState("error");
|
|
1472
|
+
});
|
|
1473
|
+
return () => {
|
|
1474
|
+
live = false;
|
|
1475
|
+
};
|
|
1476
|
+
}, [
|
|
1477
|
+
rpc,
|
|
1478
|
+
count,
|
|
1479
|
+
nextExpiresAt
|
|
1480
|
+
]);
|
|
1415
1481
|
(0, react.useEffect)(() => {
|
|
1416
1482
|
if (challenge === void 0) {
|
|
1417
1483
|
setResetCountdown(0);
|
|
@@ -1463,8 +1529,15 @@ window.__ModuleLoader__.load({
|
|
|
1463
1529
|
className: "codexSubscriptionResetSummary",
|
|
1464
1530
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1465
1531
|
className: "codexSubscriptionResetMeta",
|
|
1466
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("resetCreditsValue"), { count }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1467
|
-
|
|
1532
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: fill(t("resetCreditsValue"), { count }) }), expiryState === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1533
|
+
className: "codexSubscriptionResetExpiry",
|
|
1534
|
+
role: "status",
|
|
1535
|
+
children: t("resetCreditExpiryLoading")
|
|
1536
|
+
}) : expiryState === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1537
|
+
className: "codexSubscriptionResetExpiry",
|
|
1538
|
+
children: t("resetCreditExpiryFailed")
|
|
1539
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetCreditExpiry, {
|
|
1540
|
+
expiresAt: inspectedExpiry,
|
|
1468
1541
|
t
|
|
1469
1542
|
})]
|
|
1470
1543
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
package/lib/index.js
CHANGED
|
@@ -448,6 +448,7 @@ const normalizeQuickQuotaMode = (value, legacyVisible = false) => [
|
|
|
448
448
|
const supportsCodexFastMode = (modelId) => typeof modelId === "string" && (/^gpt-5\.(?:5|6)(?:$|-)/u.test(modelId) || modelId === "gpt-5.4");
|
|
449
449
|
//#endregion
|
|
450
450
|
//#region src/pi-ai-runtime.js
|
|
451
|
+
const FAST_SERVICE_TIER = "priority";
|
|
451
452
|
/**
|
|
452
453
|
* Preserve pi-ai's native Codex OAuth provider while allowing DSH's generic
|
|
453
454
|
* PiAiAdapter to pass the access token resolved by the host credential store.
|
|
@@ -478,15 +479,15 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
|
|
|
478
479
|
const onPayload = options.onPayload;
|
|
479
480
|
return {
|
|
480
481
|
...options,
|
|
481
|
-
serviceTier:
|
|
482
|
+
serviceTier: FAST_SERVICE_TIER,
|
|
482
483
|
async onPayload(payload, requestModel) {
|
|
483
484
|
const fastPayload = {
|
|
484
485
|
...payload,
|
|
485
|
-
service_tier:
|
|
486
|
+
service_tier: FAST_SERVICE_TIER
|
|
486
487
|
};
|
|
487
488
|
return {
|
|
488
489
|
...await onPayload?.(fastPayload, requestModel) ?? fastPayload,
|
|
489
|
-
service_tier:
|
|
490
|
+
service_tier: FAST_SERVICE_TIER
|
|
490
491
|
};
|
|
491
492
|
}
|
|
492
493
|
};
|
|
@@ -503,7 +504,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
|
|
|
503
504
|
}
|
|
504
505
|
//#endregion
|
|
505
506
|
//#region src/version.js
|
|
506
|
-
const PACKAGE_VERSION = "1.
|
|
507
|
+
const PACKAGE_VERSION = "1.6.0";
|
|
507
508
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
508
509
|
//#endregion
|
|
509
510
|
//#region src/codex-search.js
|
|
@@ -626,7 +627,9 @@ function createCodexSearchProvider(options) {
|
|
|
626
627
|
//#region src/codex-images.js
|
|
627
628
|
const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
|
|
628
629
|
const CODEX_IMAGE_GENERATION_URL = "https://chatgpt.com/backend-api/codex/images/generations";
|
|
630
|
+
const CODEX_IMAGE_EDIT_URL = "https://chatgpt.com/backend-api/codex/images/edits";
|
|
629
631
|
const IMAGE_MODEL = "gpt-image-2";
|
|
632
|
+
const MAX_REFERENCE_IMAGES = 5;
|
|
630
633
|
const RESPONSE_ENVELOPE_BYTES = 1024 * 1024;
|
|
631
634
|
const PNG_SIGNATURE = Buffer.from([
|
|
632
635
|
137,
|
|
@@ -695,6 +698,24 @@ function imageReference(value) {
|
|
|
695
698
|
...value.name === void 0 ? {} : { name: value.name }
|
|
696
699
|
};
|
|
697
700
|
}
|
|
701
|
+
function referenceOf(value, attachments) {
|
|
702
|
+
if (!record$2(value) || typeof value.attachmentId !== "string" || value.attachmentId.length === 0 || value.attachmentId.length > 256 || !attachments.imageLimits.mediaTypes.includes(value.mediaType) || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || !Number.isSafeInteger(value.width) || value.width <= 0 || !Number.isSafeInteger(value.height) || value.height <= 0 || value.name !== void 0 && (typeof value.name !== "string" || value.name.length > 256)) throw new Error("referenceImages contains an invalid image reference");
|
|
703
|
+
return imageReference(value);
|
|
704
|
+
}
|
|
705
|
+
async function editImages(values, attachments, signal) {
|
|
706
|
+
if (!Array.isArray(values) || values.length === 0 || values.length > MAX_REFERENCE_IMAGES) throw new Error(`referenceImages must contain between 1 and ${MAX_REFERENCE_IMAGES} images`);
|
|
707
|
+
const references = values.map((value) => referenceOf(value, attachments));
|
|
708
|
+
if (new Set(references.map((value) => value.attachmentId)).size !== references.length) throw new Error("referenceImages must not contain duplicates");
|
|
709
|
+
const images = [];
|
|
710
|
+
let totalBytes = 0;
|
|
711
|
+
for (const reference of references) {
|
|
712
|
+
const stored = await attachments.readImage(reference, signal);
|
|
713
|
+
totalBytes += stored.data.byteLength;
|
|
714
|
+
if (totalBytes > attachments.imageLimits.maxMessageImageBytes) throw new Error("referenceImages exceed the DSH message image limit");
|
|
715
|
+
images.push({ image_url: `data:${stored.ref.mediaType};base64,${Buffer.from(stored.data).toString("base64")}` });
|
|
716
|
+
}
|
|
717
|
+
return images;
|
|
718
|
+
}
|
|
698
719
|
function imageContent(value) {
|
|
699
720
|
return [{
|
|
700
721
|
type: "text",
|
|
@@ -761,12 +782,45 @@ function createCodexImageTool(options) {
|
|
|
761
782
|
const attachments = options.attachments;
|
|
762
783
|
return defineTool({
|
|
763
784
|
name: CODEX_IMAGE_TOOL_NAME,
|
|
764
|
-
description: "
|
|
765
|
-
parameters: {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
785
|
+
description: "Create a new image or explicitly edit selected prior images using the signed-in Codex subscription. Omit referenceImages for a completely new image. Include only the exact prior image references the user asked to edit; never assume every image in the conversation is a reference.",
|
|
786
|
+
parameters: {
|
|
787
|
+
prompt: {
|
|
788
|
+
type: "string",
|
|
789
|
+
required: true,
|
|
790
|
+
description: "A complete, production-ready description of the image to generate."
|
|
791
|
+
},
|
|
792
|
+
referenceImages: {
|
|
793
|
+
type: "array",
|
|
794
|
+
description: "Optional explicit references to 1-5 prior images to edit. Omit for a new image.",
|
|
795
|
+
items: {
|
|
796
|
+
type: "object",
|
|
797
|
+
additionalProperties: false,
|
|
798
|
+
properties: {
|
|
799
|
+
attachmentId: {
|
|
800
|
+
type: "string",
|
|
801
|
+
required: true
|
|
802
|
+
},
|
|
803
|
+
mediaType: {
|
|
804
|
+
type: "string",
|
|
805
|
+
required: true
|
|
806
|
+
},
|
|
807
|
+
bytes: {
|
|
808
|
+
type: "integer",
|
|
809
|
+
required: true
|
|
810
|
+
},
|
|
811
|
+
width: {
|
|
812
|
+
type: "integer",
|
|
813
|
+
required: true
|
|
814
|
+
},
|
|
815
|
+
height: {
|
|
816
|
+
type: "integer",
|
|
817
|
+
required: true
|
|
818
|
+
},
|
|
819
|
+
name: { type: "string" }
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
},
|
|
770
824
|
output: {
|
|
771
825
|
schema: imageOutputSchema(),
|
|
772
826
|
render: (_args, value) => imageContent(value)
|
|
@@ -783,9 +837,11 @@ function createCodexImageTool(options) {
|
|
|
783
837
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
784
838
|
if (!attachments.imageLimits.mediaTypes.includes("image/png")) throw new Error("This DSH installation does not accept PNG image attachments");
|
|
785
839
|
const maximumBytes = Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
|
|
840
|
+
const editing = args.referenceImages !== void 0;
|
|
841
|
+
const images = editing ? await editImages(args.referenceImages, attachments, exec.signal) : void 0;
|
|
786
842
|
let response;
|
|
787
843
|
try {
|
|
788
|
-
response = await fetchImage(CODEX_IMAGE_GENERATION_URL, {
|
|
844
|
+
response = await fetchImage(editing ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_GENERATION_URL, {
|
|
789
845
|
method: "POST",
|
|
790
846
|
redirect: "error",
|
|
791
847
|
headers: {
|
|
@@ -798,6 +854,7 @@ function createCodexImageTool(options) {
|
|
|
798
854
|
"user-agent": USER_AGENT
|
|
799
855
|
},
|
|
800
856
|
body: JSON.stringify({
|
|
857
|
+
...images === void 0 ? {} : { images },
|
|
801
858
|
prompt,
|
|
802
859
|
background: "auto",
|
|
803
860
|
model: IMAGE_MODEL,
|
|
@@ -808,12 +865,12 @@ function createCodexImageTool(options) {
|
|
|
808
865
|
});
|
|
809
866
|
} catch (error) {
|
|
810
867
|
if (exec.signal.aborted) throw exec.signal.reason;
|
|
811
|
-
throw new Error(
|
|
868
|
+
throw new Error(`Codex image ${editing ? "edit" : "generation"} request failed`, { cause: error });
|
|
812
869
|
}
|
|
813
870
|
if (!response.ok) {
|
|
814
871
|
if (response.status === 401 || response.status === 403) throw new Error("ChatGPT sign-in needs to be renewed");
|
|
815
872
|
if (response.status === 429) throw new Error("Codex image generation quota is unavailable");
|
|
816
|
-
throw new Error(`Codex image generation failed (HTTP ${response.status})`);
|
|
873
|
+
throw new Error(`Codex image ${editing ? "edit" : "generation"} failed (HTTP ${response.status})`);
|
|
817
874
|
}
|
|
818
875
|
const metadata = responseMetadata(await readJsonWithin(response, encodedLimit(maximumBytes) + RESPONSE_ENVELOPE_BYTES));
|
|
819
876
|
const data = decodeCodexPng(metadata.encoded, maximumBytes);
|
|
@@ -1145,29 +1202,42 @@ function createCodexResetCreditService(options) {
|
|
|
1145
1202
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1146
1203
|
const challenges = /* @__PURE__ */ new Map();
|
|
1147
1204
|
const resolveCredentials = async (signal) => credentialsOf(await getAuth({ signal }), await readCredential({ signal }));
|
|
1205
|
+
const readDetails = async (signal) => {
|
|
1206
|
+
const { access, accountId } = await resolveCredentials(signal);
|
|
1207
|
+
const response = await fetchReset(CODEX_RESET_CREDITS_URL, {
|
|
1208
|
+
method: "GET",
|
|
1209
|
+
redirect: "error",
|
|
1210
|
+
headers: {
|
|
1211
|
+
authorization: `Bearer ${access}`,
|
|
1212
|
+
"chatgpt-account-id": accountId,
|
|
1213
|
+
accept: "application/json",
|
|
1214
|
+
"cache-control": "no-store",
|
|
1215
|
+
"user-agent": USER_AGENT
|
|
1216
|
+
},
|
|
1217
|
+
signal: requestSignal(signal, timeoutMs)
|
|
1218
|
+
});
|
|
1219
|
+
if (!response.ok) throw new Error(response.status === 401 || response.status === 403 ? "ChatGPT sign-in needs to be renewed" : `ChatGPT quota reset request failed (HTTP ${response.status})`);
|
|
1220
|
+
let raw;
|
|
1221
|
+
try {
|
|
1222
|
+
raw = await response.json();
|
|
1223
|
+
} catch {
|
|
1224
|
+
throw new Error("ChatGPT returned unreadable quota reset details");
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
accountId,
|
|
1228
|
+
details: parseDetails(raw, now())
|
|
1229
|
+
};
|
|
1230
|
+
};
|
|
1148
1231
|
return Object.freeze({
|
|
1232
|
+
async inspect({ signal } = {}) {
|
|
1233
|
+
const { details } = await readDetails(signal);
|
|
1234
|
+
return {
|
|
1235
|
+
availableCount: details.availableCount,
|
|
1236
|
+
...details.creditExpiresAt === void 0 ? {} : { nextExpiresAt: details.creditExpiresAt }
|
|
1237
|
+
};
|
|
1238
|
+
},
|
|
1149
1239
|
async prepare({ signal } = {}) {
|
|
1150
|
-
const {
|
|
1151
|
-
const response = await fetchReset(CODEX_RESET_CREDITS_URL, {
|
|
1152
|
-
method: "GET",
|
|
1153
|
-
redirect: "error",
|
|
1154
|
-
headers: {
|
|
1155
|
-
authorization: `Bearer ${access}`,
|
|
1156
|
-
"chatgpt-account-id": accountId,
|
|
1157
|
-
accept: "application/json",
|
|
1158
|
-
"cache-control": "no-store",
|
|
1159
|
-
"user-agent": USER_AGENT
|
|
1160
|
-
},
|
|
1161
|
-
signal: requestSignal(signal, timeoutMs)
|
|
1162
|
-
});
|
|
1163
|
-
if (!response.ok) throw new Error(response.status === 401 || response.status === 403 ? "ChatGPT sign-in needs to be renewed" : `ChatGPT quota reset request failed (HTTP ${response.status})`);
|
|
1164
|
-
let raw;
|
|
1165
|
-
try {
|
|
1166
|
-
raw = await response.json();
|
|
1167
|
-
} catch {
|
|
1168
|
-
throw new Error("ChatGPT returned unreadable quota reset details");
|
|
1169
|
-
}
|
|
1170
|
-
const details = parseDetails(raw, now());
|
|
1240
|
+
const { accountId, details } = await readDetails(signal);
|
|
1171
1241
|
const preparedAt = now();
|
|
1172
1242
|
const readyAt = preparedAt + confirmDelayMs;
|
|
1173
1243
|
const expiresAt = Math.min(preparedAt + challengeTtlMs, details.creditExpiresAt ?? Number.MAX_SAFE_INTEGER);
|
|
@@ -1329,11 +1399,11 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1329
1399
|
const message = error instanceof Error && known.has(error.message) ? error.message : "Could not read ChatGPT usage";
|
|
1330
1400
|
return publicError("internal", message);
|
|
1331
1401
|
}
|
|
1332
|
-
if (endpoint === "reset-credit/prepare" || endpoint === "reset-credit/consume") try {
|
|
1402
|
+
if (endpoint === "reset-credit/inspect" || endpoint === "reset-credit/prepare" || endpoint === "reset-credit/consume") try {
|
|
1333
1403
|
signal.throwIfAborted();
|
|
1334
1404
|
return {
|
|
1335
1405
|
ok: true,
|
|
1336
|
-
value: endpoint === "reset-credit/prepare" ? await resetCreditService.prepare({ signal }) : await resetCreditService.consume({
|
|
1406
|
+
value: endpoint === "reset-credit/inspect" ? await resetCreditService.inspect({ signal }) : endpoint === "reset-credit/prepare" ? await resetCreditService.prepare({ signal }) : await resetCreditService.consume({
|
|
1337
1407
|
challengeId: payload?.challengeId,
|
|
1338
1408
|
acknowledged: payload?.acknowledged,
|
|
1339
1409
|
signal
|
|
@@ -1353,7 +1423,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1353
1423
|
"You must acknowledge that one quota reset will be consumed",
|
|
1354
1424
|
"The signed-in ChatGPT account changed"
|
|
1355
1425
|
]);
|
|
1356
|
-
const fallback = endpoint === "reset-credit/prepare" ? "Could not prepare a quota reset" : "Could not use the quota reset";
|
|
1426
|
+
const fallback = endpoint === "reset-credit/inspect" ? "Could not read quota reset details" : endpoint === "reset-credit/prepare" ? "Could not prepare a quota reset" : "Could not use the quota reset";
|
|
1357
1427
|
const message = error instanceof Error && known.has(error.message) ? error.message : fallback;
|
|
1358
1428
|
return publicError("internal", message);
|
|
1359
1429
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|