dsh-codex-subscription 1.8.0 → 1.10.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 +5 -5
- package/README.md +8 -12
- package/README.zh-CN.md +8 -12
- package/lib/client.js +176 -30
- package/lib/index.js +323 -38
- 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.10.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.10.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.10.0
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Use the same `add` command to update or repair. This is the complete package-changing operation.
|
|
@@ -58,12 +58,12 @@ 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.10.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
|
|
|
65
65
|
With permission to restart DSH, open **Settings -> Codex** and verify the settings page loads. Confirm
|
|
66
|
-
that search offers
|
|
66
|
+
that search offers **Auto**, **DSH default**, and **Codex subscription**, the composer quota switch defaults
|
|
67
67
|
to off, and `codex_image_generate` is available. Do not consume quota merely to test installation unless
|
|
68
68
|
the user explicitly asks for a live model, search, or image-generation check.
|
|
69
69
|
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ DSH-Portable exposes the same standard plugin command, so the command above also
|
|
|
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
50
|
| **Safe quota reset** | See the next expiry and deliberately try a reset with a cooldown and acknowledgement |
|
|
51
|
-
| **Subscription search** | Explicitly
|
|
51
|
+
| **Subscription search** | Explicitly route search globally through DSH default search or the signed-in Codex subscription |
|
|
52
52
|
| **Codex image generation and editing (Beta)** | Generate without references, or explicitly edit one selected image; preview, zoom, annotate regions, download the original, and continue in the same composer |
|
|
53
53
|
| **Fast mode** | Switch between Standard and Fast directly in the composer |
|
|
54
54
|
| **Model-aware context** | Keep catalog defaults, use each model's supported extended window, or enter a full numeric token limit for each model |
|
|
@@ -58,21 +58,16 @@ These capabilities reuse the same local ChatGPT sign-in. Subscription routing fa
|
|
|
58
58
|
## Product screen
|
|
59
59
|
|
|
60
60
|
<p align="center">
|
|
61
|
-
<img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/settings-
|
|
61
|
+
<img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/context-settings-en.png" width="820" alt="Current Codex subscription settings in DeepSeek Harness with search, model-aware context, composer quota, and support diagnostics">
|
|
62
62
|
</p>
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
<summary>View the complete settings screen</summary>
|
|
66
|
-
|
|
67
|
-

|
|
68
|
-
|
|
69
|
-
</details>
|
|
64
|
+
Captured from the installed official DeepSeek Harness `0.1.1-rc.2` product with the current plugin build.
|
|
70
65
|
|
|
71
66
|
## Prepare DSH
|
|
72
67
|
|
|
73
|
-
This plugin
|
|
68
|
+
This plugin supports the latest DeepSeek Harness release recorded in its package metadata and requires a ChatGPT account that currently has Codex access.
|
|
74
69
|
|
|
75
|
-
- Do not want to configure Node.js? Use [DSH-Portable](https://github.com/WSL043/DSH-Portable), a community desktop distribution
|
|
70
|
+
- Do not want to configure Node.js? Use [DSH-Portable](https://github.com/WSL043/DSH-Portable), a community portable desktop distribution for Windows, macOS, and Linux.
|
|
76
71
|
- Prefer the official route? Follow the [DeepSeek Harness run guide](https://github.com/deepseek-ai/deepseek-harness#run).
|
|
77
72
|
|
|
78
73
|
## Install
|
|
@@ -142,7 +137,7 @@ Restart DSH manually after installation, then:
|
|
|
142
137
|
|
|
143
138
|
- ChatGPT OAuth sign-in with credentials kept on the host;
|
|
144
139
|
- Codex models and Beta image generation/editing directly inside DSH conversations;
|
|
145
|
-
- A clear choice between DSH default search and Codex subscription search;
|
|
140
|
+
- A clear global choice between DSH default search and Codex subscription search; it applies to every model and session rather than following the selected model;
|
|
146
141
|
- Actual backend-provided quota, reset time, and freshness;
|
|
147
142
|
- Separate standard Codex, Codex-Spark, Credits, and other independent limits;
|
|
148
143
|
- Visible quota-reset count and earliest expiry, with deliberate early redemption, layered confirmation, and no automatic retry;
|
|
@@ -224,9 +219,10 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
224
219
|
The ChatGPT Codex backend and DSH can change independently. This community project is not affiliated with or endorsed by DeepSeek or OpenAI.
|
|
225
220
|
|
|
226
221
|
Use the [bug report form](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml) for project feedback.
|
|
222
|
+
Use the [feature request form](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=feature-request.yml) for focused product suggestions.
|
|
227
223
|
For DSH plugin discussion, visit [DeepSeek Harness Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions).
|
|
228
224
|
Read [SECURITY.md](SECURITY.md) before reporting sensitive issues.
|
|
229
225
|
|
|
230
226
|
If this project is useful, the [Star button](https://github.com/WSL043/dsh-codex-subscription/stargazers) helps more DSH users find it.
|
|
231
227
|
|
|
232
|
-
[简体中文](README.md) · [MIT](LICENSE)
|
|
228
|
+
[简体中文](README.zh-CN.md) · [MIT](LICENSE)
|
package/README.zh-CN.md
CHANGED
|
@@ -49,8 +49,8 @@ DSH-Portable 也提供相同的标准插件命令,因此同样使用上面的
|
|
|
49
49
|
| **额度可见** | 普通 Codex、Spark 等服务端实际返回的额度分开显示 |
|
|
50
50
|
| **输入框额度** | 可选择紧凑百分比、进度条或完全关闭输入框额度显示 |
|
|
51
51
|
| **安全额度重置** | 直接查看最早到期时间,并通过冷静期和知情确认主动尝试重置 |
|
|
52
|
-
| **订阅搜索** |
|
|
53
|
-
| **Codex 图片生成与编辑(Beta)** |
|
|
52
|
+
| **订阅搜索** | 可将全部模型的搜索明确路由到 DSH 默认搜索或已登录的 Codex 订阅 |
|
|
53
|
+
| **Codex 图片生成与编辑(Beta)** | 可无参考图全新生成,也可明确选择会话图片继续编辑;支持预览、缩放、区域备注、下载原图并衔接回当前输入框 |
|
|
54
54
|
| **高速模式** | 直接在输入框切换标准或高速,无需离开当前会话 |
|
|
55
55
|
| **模型感知上下文** | 可保留目录默认值、按模型启用扩展窗口,或为每个模型填写完整数字 Token 上限 |
|
|
56
56
|
|
|
@@ -59,21 +59,16 @@ DSH-Portable 也提供相同的标准插件命令,因此同样使用上面的
|
|
|
59
59
|
## 实际界面
|
|
60
60
|
|
|
61
61
|
<p align="center">
|
|
62
|
-
<img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/settings
|
|
62
|
+
<img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/context-settings.png" width="820" alt="当前 DeepSeek Harness Codex 订阅设置,包含搜索来源、模型感知上下文、输入框额度和支持诊断">
|
|
63
63
|
</p>
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
<summary>查看完整设置界面</summary>
|
|
67
|
-
|
|
68
|
-

|
|
69
|
-
|
|
70
|
-
</details>
|
|
65
|
+
图片来自已安装的官方 DeepSeek Harness `0.1.1-rc.2` 与当前插件构建。
|
|
71
66
|
|
|
72
67
|
## 准备 DSH
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
本插件支持软件包元数据中记录的最新版 DeepSeek Harness,并需要一个当前具有 Codex 使用资格的 ChatGPT 账户。
|
|
75
70
|
|
|
76
|
-
- 不想配置 Node.js:使用 [DSH-Portable](https://github.com/WSL043/DSH-Portable)
|
|
71
|
+
- 不想配置 Node.js:使用 [DSH-Portable](https://github.com/WSL043/DSH-Portable)。这是面向 Windows、macOS 和 Linux 的社区便携桌面分发;
|
|
77
72
|
- 想按官方方式运行:查看 [DeepSeek Harness 官方说明](https://github.com/deepseek-ai/deepseek-harness#run)。
|
|
78
73
|
|
|
79
74
|
## 安装
|
|
@@ -142,7 +137,7 @@ dsh --profile web --dump-config
|
|
|
142
137
|
|
|
143
138
|
- ChatGPT OAuth 登录,凭据保留在本机;
|
|
144
139
|
- Codex 模型和 Beta 图片生成与编辑直接出现在 DSH 会话中;
|
|
145
|
-
- DSH 默认搜索与 Codex
|
|
140
|
+
- 搜索来源是全局设置,可在 DSH 默认搜索与 Codex 订阅搜索之间切换;它对所有模型和会话生效,不会随当前模型自动切换;
|
|
146
141
|
- 设置页显示服务端返回的额度、重置时间和更新时间;
|
|
147
142
|
- 普通 Codex、Codex-Spark、Credits 等独立额度分开显示;
|
|
148
143
|
- 显示重置卡数量与最早到期时间,也允许在额度未完全用尽时主动尝试,并经过分层确认且不会自动重试;
|
|
@@ -222,6 +217,7 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
222
217
|
ChatGPT Codex 后端和 DSH 可能独立变化;本项目为社区项目,与 DeepSeek、OpenAI 无隶属或背书关系。
|
|
223
218
|
|
|
224
219
|
本项目的问题反馈请使用[使用问题表单](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=install-problem.yml);
|
|
220
|
+
明确的产品建议请使用[功能建议表单](https://github.com/WSL043/dsh-codex-subscription/issues/new?template=feature-request.yml);
|
|
225
221
|
DSH 插件交流可前往 [DeepSeek Harness Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions)。
|
|
226
222
|
敏感问题请先阅读 [SECURITY.md](SECURITY.md)。
|
|
227
223
|
|
package/lib/client.js
CHANGED
|
@@ -49,17 +49,35 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
}
|
|
50
50
|
const ForwardRef = /*#__PURE__*/ react.forwardRef(BoltIcon);
|
|
51
51
|
//#endregion
|
|
52
|
+
//#region src/image-edit.js
|
|
53
|
+
function buildImageEditDraft({ prompt = "", annotations = [], translate }) {
|
|
54
|
+
if (typeof translate !== "function") throw new TypeError("translate must be a function");
|
|
55
|
+
const notes = annotations.map((annotation, index) => ({
|
|
56
|
+
number: index + 1,
|
|
57
|
+
note: typeof annotation?.note === "string" ? annotation.note.trim() : ""
|
|
58
|
+
})).filter((annotation) => annotation.note !== "").map((annotation) => String(annotation.number) + ". " + annotation.note);
|
|
59
|
+
const base = prompt.trim() === "" ? translate("imageEditDefault") : prompt.trim();
|
|
60
|
+
return notes.length === 0 ? base : base + "\n\n" + translate("imageRegionNotes") + "\n" + notes.join("\n");
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
52
63
|
//#region src/settings-contract.js
|
|
53
64
|
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
54
65
|
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
55
66
|
const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
56
67
|
const QUICK_QUOTA_MODE_PERCENT = "percent";
|
|
57
68
|
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
69
|
+
const SEARCH_PROVIDER_AUTO = "auto";
|
|
58
70
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
71
|
+
const DEFAULT_SEARCH_PROVIDER = SEARCH_PROVIDER_AUTO;
|
|
59
72
|
const SPEED_MODE_FIELD = "speedMode";
|
|
60
73
|
const SPEED_MODE_STANDARD = "standard";
|
|
61
74
|
const SPEED_MODE_FAST = "fast";
|
|
62
75
|
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
76
|
+
const OUTPUT_VERBOSITY_FIELD = "outputVerbosity";
|
|
77
|
+
const OUTPUT_VERBOSITY_DEFAULT = "default";
|
|
78
|
+
const OUTPUT_VERBOSITY_MEDIUM = "medium";
|
|
79
|
+
const OUTPUT_VERBOSITY_HIGH = "high";
|
|
80
|
+
const DEFAULT_OUTPUT_VERBOSITY = OUTPUT_VERBOSITY_DEFAULT;
|
|
63
81
|
const CONTEXT_MODE_FIELD = "contextMode";
|
|
64
82
|
const CONTEXT_MODE_STANDARD = "standard";
|
|
65
83
|
const CONTEXT_MODE_EXTENDED = "extended";
|
|
@@ -87,7 +105,17 @@ window.__ModuleLoader__.load({
|
|
|
87
105
|
"gpt-5.5": 272e3,
|
|
88
106
|
"gpt-5.6": 272e3
|
|
89
107
|
});
|
|
90
|
-
const normalizeSearchProvider = (value) => [
|
|
108
|
+
const normalizeSearchProvider = (value) => [
|
|
109
|
+
"auto",
|
|
110
|
+
"dsh",
|
|
111
|
+
"codex"
|
|
112
|
+
].includes(value) ? value : DEFAULT_SEARCH_PROVIDER;
|
|
113
|
+
const normalizeOutputVerbosity = (value) => [
|
|
114
|
+
"default",
|
|
115
|
+
"low",
|
|
116
|
+
"medium",
|
|
117
|
+
"high"
|
|
118
|
+
].includes(value) ? value : DEFAULT_OUTPUT_VERBOSITY;
|
|
91
119
|
const normalizeSpeedMode = (value) => ["standard", "fast"].includes(value) ? value : DEFAULT_SPEED_MODE;
|
|
92
120
|
const normalizeContextMode = (value) => [
|
|
93
121
|
"standard",
|
|
@@ -212,10 +240,13 @@ window.__ModuleLoader__.load({
|
|
|
212
240
|
diagnosticsFailed: "无法生成诊断信息。",
|
|
213
241
|
feedbackOpen: "反馈问题",
|
|
214
242
|
searchTitle: "搜索来源",
|
|
243
|
+
searchScope: "自动按当前会话模型分流;手动选择会覆盖所有模型和会话。",
|
|
244
|
+
searchAuto: "自动",
|
|
245
|
+
searchAutoHint: "Codex 模型用订阅搜索,其他模型用 DSH",
|
|
215
246
|
searchDsh: "DSH 默认",
|
|
216
|
-
searchDshHint: "当前搜索服务",
|
|
247
|
+
searchDshHint: "所有模型使用 DSH 当前搜索服务",
|
|
217
248
|
searchCodex: "Codex 订阅",
|
|
218
|
-
searchCodexHint: "ChatGPT 订阅搜索",
|
|
249
|
+
searchCodexHint: "所有模型通过已登录的 ChatGPT 订阅搜索",
|
|
219
250
|
preferenceFailed: "设置未保存。",
|
|
220
251
|
preferenceRetry: "重试",
|
|
221
252
|
usage: "订阅额度",
|
|
@@ -289,7 +320,16 @@ window.__ModuleLoader__.load({
|
|
|
289
320
|
speedStandardHint: "标准速度",
|
|
290
321
|
speedFast: "高速",
|
|
291
322
|
speedFastHint: "1.5 倍,消耗更多 Credits",
|
|
292
|
-
|
|
323
|
+
verbosityTitle: "输出详略",
|
|
324
|
+
verbosityDefault: "模型默认",
|
|
325
|
+
verbosityDefaultHint: "使用官方模型目录推荐值",
|
|
326
|
+
verbosityLow: "简洁",
|
|
327
|
+
verbosityLowHint: "更短、更直接",
|
|
328
|
+
verbosityMedium: "均衡",
|
|
329
|
+
verbosityMediumHint: "兼顾完整性与长度",
|
|
330
|
+
verbosityHigh: "详细",
|
|
331
|
+
verbosityHighHint: "更充分的说明与结构",
|
|
332
|
+
modelMenuAria: "模型、推理等级、速度与输出详略",
|
|
293
333
|
modelLabel: "模型",
|
|
294
334
|
effortLabel: "推理等级",
|
|
295
335
|
providerDefault: "Default",
|
|
@@ -354,10 +394,13 @@ window.__ModuleLoader__.load({
|
|
|
354
394
|
diagnosticsFailed: "Could not create diagnostics.",
|
|
355
395
|
feedbackOpen: "Report a problem",
|
|
356
396
|
searchTitle: "Search source",
|
|
397
|
+
searchScope: "Auto follows the current session model; an explicit choice overrides every model and session.",
|
|
398
|
+
searchAuto: "Auto",
|
|
399
|
+
searchAutoHint: "Codex models use subscription search; other models use DSH",
|
|
357
400
|
searchDsh: "DSH default",
|
|
358
|
-
searchDshHint: "
|
|
401
|
+
searchDshHint: "Use DSH's current search service for every model",
|
|
359
402
|
searchCodex: "Codex subscription",
|
|
360
|
-
searchCodexHint: "ChatGPT subscription
|
|
403
|
+
searchCodexHint: "Search through the signed-in ChatGPT subscription for every model",
|
|
361
404
|
preferenceFailed: "The setting was not saved.",
|
|
362
405
|
preferenceRetry: "Retry",
|
|
363
406
|
usage: "Subscription quota",
|
|
@@ -431,7 +474,16 @@ window.__ModuleLoader__.load({
|
|
|
431
474
|
speedStandardHint: "Standard speed",
|
|
432
475
|
speedFast: "Fast",
|
|
433
476
|
speedFastHint: "1.5x; higher Credits use",
|
|
434
|
-
|
|
477
|
+
verbosityTitle: "Output detail",
|
|
478
|
+
verbosityDefault: "Model default",
|
|
479
|
+
verbosityDefaultHint: "Use the official model catalog recommendation",
|
|
480
|
+
verbosityLow: "Concise",
|
|
481
|
+
verbosityLowHint: "Shorter and more direct",
|
|
482
|
+
verbosityMedium: "Balanced",
|
|
483
|
+
verbosityMediumHint: "Balance completeness and length",
|
|
484
|
+
verbosityHigh: "Detailed",
|
|
485
|
+
verbosityHighHint: "More explanation and structure",
|
|
486
|
+
modelMenuAria: "Model, effort, speed, and output detail",
|
|
435
487
|
modelLabel: "Model",
|
|
436
488
|
effortLabel: "Effort",
|
|
437
489
|
providerDefault: "Default",
|
|
@@ -471,6 +523,7 @@ window.__ModuleLoader__.load({
|
|
|
471
523
|
imageRemoveAnnotation: "Remove note"
|
|
472
524
|
};
|
|
473
525
|
const STYLE = `
|
|
526
|
+
.codexSubscriptionSearchHead{display:flex;flex-direction:column;gap:1px}.codexSubscriptionSearchScope{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
|
|
474
527
|
.codexSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
|
|
475
528
|
.codexSubscription h2,.codexSubscription h3,.codexSubscription p{margin:0}.codexSubscriptionHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
476
529
|
.codexSubscription h2{font-size:16px;line-height:24px;font-weight:500}.codexSubscription h3{font-size:14px;line-height:22px;font-weight:500}
|
|
@@ -481,7 +534,7 @@ window.__ModuleLoader__.load({
|
|
|
481
534
|
.codexSubscriptionQuotaModes{display:flex;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}.codexSubscriptionQuotaMode{position:relative;display:flex;align-items:center;justify-content:center;min-height:26px;padding:0 9px;border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;cursor:pointer}.codexSubscriptionQuotaMode:has(input:checked){background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:0 0 0 1px var(--dsw-alias-border-l3)}.codexSubscriptionQuotaMode:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:1px}.codexSubscriptionQuotaMode:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionQuotaMode input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
|
|
482
535
|
.codexSubscriptionContext{display:flex;flex-direction:column;gap:8px}.codexSubscriptionContextHead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.codexSubscriptionContextCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.codexSubscriptionContextHint{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionContextTrigger{height:32px;min-width:108px;display:inline-flex;align-items:center;justify-content:space-between;gap:10px;padding:0 10px 0 12px;border:0;border-radius:999px;outline:0;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;cursor:pointer}.codexSubscriptionContextTrigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.codexSubscriptionContextTrigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.codexSubscriptionContextTrigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:not-allowed}.codexSubscriptionContextTrigger svg{color:var(--dsw-alias-label-tertiary);transition:transform 120ms var(--ds-ease-in-out)}.codexSubscriptionContextTrigger[aria-expanded=true] svg{transform:rotate(180deg)}.codexSubscriptionContextModels{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.codexSubscriptionContextModel{min-height:42px;display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.codexSubscriptionContextModel:last-child{border-bottom:0}.codexSubscriptionContextModelCopy{display:flex;min-width:0;flex-direction:column}.codexSubscriptionContextModelCopy strong{font-size:12px;line-height:18px;font-weight:500}.codexSubscriptionContextModelCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionContextInput{width:116px}
|
|
483
536
|
.codexSubscriptionSwitch{position:relative;flex:0 0 auto;width:32px;height:18px;padding:0;border:1px solid var(--dsw-alias-border-l3);border-radius:999px;background:var(--dsw-alias-bg-module-platform);cursor:pointer}.codexSubscriptionSwitch:disabled{cursor:not-allowed;opacity:.5}.codexSubscriptionSwitch[aria-checked=true]{background:var(--dsw-alias-label-secondary);border-color:var(--dsw-alias-label-secondary)}.codexSubscriptionSwitchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--dsw-alias-bg-layer-1);transition:transform 120ms var(--ds-ease-in-out)}.codexSubscriptionSwitch[aria-checked=true] .codexSubscriptionSwitchKnob{transform:translateX(14px)}
|
|
484
|
-
.codexSubscriptionSearch{display:flex;flex-direction:column;gap:7px}.codexSubscriptionSearchChoices{display:grid;grid-template-columns:repeat(
|
|
537
|
+
.codexSubscriptionSearch{display:flex;flex-direction:column;gap:7px}.codexSubscriptionSearchChoices{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:6px}.codexSubscriptionSearchChoice{display:grid;grid-template-columns:14px minmax(0,1fr);align-items:center;column-gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);padding:9px 10px;text-align:left;cursor:pointer}.codexSubscriptionSearchChoice:has(input:disabled){cursor:not-allowed;opacity:.5}.codexSubscriptionSearchChoice:has(input:checked){border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.codexSubscriptionSearchChoice:has(input:focus-visible){outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}.codexSubscriptionSearchInput{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-label-primary);cursor:inherit}.codexSubscriptionSearchCopy{display:block;min-width:0;pointer-events:none}.codexSubscriptionSearchCopy strong,.codexSubscriptionSearchCopy span{display:block}.codexSubscriptionSearchCopy strong{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary)}.codexSubscriptionSearchChoice:has(input:checked) strong{color:var(--dsw-alias-label-primary)}.codexSubscriptionSearchCopy span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionDivider{height:1px;background:var(--dsw-alias-border-l2)}
|
|
485
538
|
.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionStatus{display:flex;align-items:center;gap:8px;font-size:14px;line-height:22px;font-weight:500}
|
|
486
539
|
.codexSubscriptionDot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-dimmed)}.codexSubscriptionDot[data-state=connected]{background:var(--dsw-alias-state-success-primary)}.codexSubscriptionDot[data-state=disconnected]{background:var(--dsw-alias-state-error-primary)}
|
|
487
540
|
.codexSubscriptionActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.codexSubscriptionFlow{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-module-platform)}
|
|
@@ -552,15 +605,7 @@ window.__ModuleLoader__.load({
|
|
|
552
605
|
return cleaned.toLowerCase().endsWith(".png") ? cleaned : `${cleaned}.png`;
|
|
553
606
|
};
|
|
554
607
|
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`;
|
|
555
|
-
function
|
|
556
|
-
const notes = annotations.map((annotation, index) => ({
|
|
557
|
-
number: index + 1,
|
|
558
|
-
note: annotation.note.trim()
|
|
559
|
-
})).filter((annotation) => annotation.note !== "").map((annotation) => String(annotation.number) + ". " + annotation.note);
|
|
560
|
-
const base = prompt.trim() === "" ? t("imageEditDefault") : prompt.trim();
|
|
561
|
-
return notes.length === 0 ? base : base + "\n\n" + t("imageRegionNotes") + "\n" + notes.join("\n");
|
|
562
|
-
}
|
|
563
|
-
function CodexGeneratedImage({ attachment, loadImage, attachForEdit, t }) {
|
|
608
|
+
function CodexGeneratedImage({ attachment, loadImage, attachForEdit, getImageViewer, t }) {
|
|
564
609
|
const [attempt, setAttempt] = (0, react.useState)(0);
|
|
565
610
|
const [error, setError] = (0, react.useState)(false);
|
|
566
611
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -646,7 +691,11 @@ window.__ModuleLoader__.load({
|
|
|
646
691
|
setEditBusy(true);
|
|
647
692
|
setEditError(false);
|
|
648
693
|
try {
|
|
649
|
-
await attachForEdit(src, downloadName,
|
|
694
|
+
await attachForEdit(src, downloadName, buildImageEditDraft({
|
|
695
|
+
prompt,
|
|
696
|
+
annotations,
|
|
697
|
+
translate: t
|
|
698
|
+
}));
|
|
650
699
|
close();
|
|
651
700
|
} catch {
|
|
652
701
|
setEditError(true);
|
|
@@ -654,6 +703,35 @@ window.__ModuleLoader__.load({
|
|
|
654
703
|
setEditBusy(false);
|
|
655
704
|
}
|
|
656
705
|
};
|
|
706
|
+
const openImage = () => {
|
|
707
|
+
if (src === void 0) return;
|
|
708
|
+
if ((getImageViewer?.())?.open?.({
|
|
709
|
+
items: [{
|
|
710
|
+
id: attachment.attachmentId ?? downloadName,
|
|
711
|
+
src,
|
|
712
|
+
name: label,
|
|
713
|
+
width: attachment.width,
|
|
714
|
+
height: attachment.height,
|
|
715
|
+
bytes: attachment.bytes
|
|
716
|
+
}],
|
|
717
|
+
opener: triggerRef.current,
|
|
718
|
+
source: "codex-generated",
|
|
719
|
+
annotations: true,
|
|
720
|
+
editor: {
|
|
721
|
+
label: t("imageEdit"),
|
|
722
|
+
busyLabel: t("imageEditPreparing"),
|
|
723
|
+
errorLabel: t("imageEditFailed"),
|
|
724
|
+
placeholder: t("imageEditPrompt"),
|
|
725
|
+
onSubmit: ({ prompt: nextPrompt, annotations: nextAnnotations }) => attachForEdit(src, downloadName, buildImageEditDraft({
|
|
726
|
+
prompt: nextPrompt,
|
|
727
|
+
annotations: nextAnnotations,
|
|
728
|
+
translate: t
|
|
729
|
+
}))
|
|
730
|
+
}
|
|
731
|
+
}) === true) return;
|
|
732
|
+
setZoom(1);
|
|
733
|
+
setOpen(true);
|
|
734
|
+
};
|
|
657
735
|
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
658
736
|
type: "button",
|
|
659
737
|
className: "codexGeneratedImageRetry",
|
|
@@ -819,19 +897,14 @@ window.__ModuleLoader__.load({
|
|
|
819
897
|
className: "codexGeneratedImageFrame",
|
|
820
898
|
title: t("imageOpen"),
|
|
821
899
|
"aria-label": fill(t("imageOpenNamed"), { value: label }),
|
|
822
|
-
onClick:
|
|
823
|
-
if (src !== void 0) {
|
|
824
|
-
setZoom(1);
|
|
825
|
-
setOpen(true);
|
|
826
|
-
}
|
|
827
|
-
},
|
|
900
|
+
onClick: openImage,
|
|
828
901
|
children: src === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
829
902
|
src,
|
|
830
903
|
alt: label
|
|
831
904
|
})
|
|
832
905
|
}), lightbox] });
|
|
833
906
|
}
|
|
834
|
-
function CodexImageToolRow({ block, loadImage, attachForEdit, t }) {
|
|
907
|
+
function CodexImageToolRow({ block, loadImage, attachForEdit, getImageViewer, t }) {
|
|
835
908
|
const settled = block?.kind === "tool-result";
|
|
836
909
|
const image = settled ? block.content.find((item) => item?.type === "image" && item.attachment !== void 0) : void 0;
|
|
837
910
|
const failed = settled && block.isError === true;
|
|
@@ -869,6 +942,7 @@ window.__ModuleLoader__.load({
|
|
|
869
942
|
attachment: image.attachment,
|
|
870
943
|
loadImage,
|
|
871
944
|
attachForEdit,
|
|
945
|
+
getImageViewer,
|
|
872
946
|
t
|
|
873
947
|
})
|
|
874
948
|
}),
|
|
@@ -887,6 +961,7 @@ window.__ModuleLoader__.load({
|
|
|
887
961
|
let failedPatch;
|
|
888
962
|
let generation = 0;
|
|
889
963
|
let contextModels = [];
|
|
964
|
+
let verbosityModels = [];
|
|
890
965
|
const nativeSnapshot = () => scope.getSnapshot();
|
|
891
966
|
const read = () => {
|
|
892
967
|
const native = nativeSnapshot();
|
|
@@ -896,10 +971,12 @@ window.__ModuleLoader__.load({
|
|
|
896
971
|
quickQuotaMode: normalizeQuickQuotaMode(current.value?.[QUICK_QUOTA_MODE_FIELD], current.value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
897
972
|
searchProvider: normalizeSearchProvider(current.value?.[SEARCH_PROVIDER_FIELD]),
|
|
898
973
|
speedMode: normalizeSpeedMode(current.value?.[SPEED_MODE_FIELD]),
|
|
974
|
+
outputVerbosity: normalizeOutputVerbosity(current.value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
899
975
|
contextMode: normalizeContextMode(current.value?.[CONTEXT_MODE_FIELD]),
|
|
900
976
|
customContextWindow: normalizeCustomContextWindow(current.value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
901
977
|
customContextWindows: Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [modelKey, normalizeCustomContextWindow(current.value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
|
|
902
978
|
contextModels,
|
|
979
|
+
verbosityModels,
|
|
903
980
|
writable: !updating && current.status === "ready" && current.writable === true,
|
|
904
981
|
error
|
|
905
982
|
});
|
|
@@ -917,6 +994,7 @@ window.__ModuleLoader__.load({
|
|
|
917
994
|
});
|
|
918
995
|
const acceptFallback = (value) => {
|
|
919
996
|
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
997
|
+
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
920
998
|
fallbackStatus = "ready";
|
|
921
999
|
fallback = {
|
|
922
1000
|
status: "ready",
|
|
@@ -924,6 +1002,7 @@ window.__ModuleLoader__.load({
|
|
|
924
1002
|
[QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
|
|
925
1003
|
[SEARCH_PROVIDER_FIELD]: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
|
|
926
1004
|
[SPEED_MODE_FIELD]: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
|
|
1005
|
+
[OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(value?.[OUTPUT_VERBOSITY_FIELD]),
|
|
927
1006
|
[CONTEXT_MODE_FIELD]: normalizeContextMode(value?.[CONTEXT_MODE_FIELD]),
|
|
928
1007
|
[CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(value?.[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
929
1008
|
...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(value?.[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])]))
|
|
@@ -942,8 +1021,10 @@ window.__ModuleLoader__.load({
|
|
|
942
1021
|
try {
|
|
943
1022
|
const value = unwrap(await rpc.call(CHANNEL, "preferences/status", {}));
|
|
944
1023
|
if (current !== generation) return;
|
|
945
|
-
if (nativeSnapshot().status === "ready")
|
|
946
|
-
|
|
1024
|
+
if (nativeSnapshot().status === "ready") {
|
|
1025
|
+
contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
|
|
1026
|
+
verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
|
|
1027
|
+
} else acceptFallback(value);
|
|
947
1028
|
publish();
|
|
948
1029
|
} catch {
|
|
949
1030
|
if (current !== generation || nativeSnapshot().status === "ready") return;
|
|
@@ -1100,11 +1181,21 @@ window.__ModuleLoader__.load({
|
|
|
1100
1181
|
});
|
|
1101
1182
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1102
1183
|
className: "codexSubscriptionSearch",
|
|
1103
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1184
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1185
|
+
className: "codexSubscriptionSearchHead",
|
|
1186
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("searchTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1187
|
+
className: "codexSubscriptionSearchScope",
|
|
1188
|
+
children: t("searchScope")
|
|
1189
|
+
})]
|
|
1190
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1104
1191
|
className: "codexSubscriptionSearchChoices",
|
|
1105
1192
|
role: "radiogroup",
|
|
1106
1193
|
"aria-label": t("searchTitle"),
|
|
1107
|
-
children: [
|
|
1194
|
+
children: [
|
|
1195
|
+
choice(SEARCH_PROVIDER_AUTO, t("searchAuto"), t("searchAutoHint")),
|
|
1196
|
+
choice("dsh", t("searchDsh"), t("searchDshHint")),
|
|
1197
|
+
choice(SEARCH_PROVIDER_CODEX, t("searchCodex"), t("searchCodexHint"))
|
|
1198
|
+
]
|
|
1108
1199
|
})]
|
|
1109
1200
|
});
|
|
1110
1201
|
}
|
|
@@ -1322,6 +1413,31 @@ window.__ModuleLoader__.load({
|
|
|
1322
1413
|
const speedSupported = state.current?.provider === "openai-codex" && supportsCodexFastMode(state.current?.model);
|
|
1323
1414
|
const speedWritable = preferenceSnapshot.status === "ready" && preferenceSnapshot.writable === true;
|
|
1324
1415
|
const fast = speedSupported && preferenceSnapshot.speedMode === "fast";
|
|
1416
|
+
const verbositySupported = state.current?.provider === "openai-codex" && preferenceSnapshot.verbosityModels.includes(state.current?.model);
|
|
1417
|
+
const verbosityWritable = preferenceSnapshot.status === "ready" && preferenceSnapshot.writable === true;
|
|
1418
|
+
const verbosityItems = [
|
|
1419
|
+
{
|
|
1420
|
+
id: OUTPUT_VERBOSITY_DEFAULT,
|
|
1421
|
+
label: t("verbosityDefault"),
|
|
1422
|
+
description: t("verbosityDefaultHint")
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
id: "low",
|
|
1426
|
+
label: t("verbosityLow"),
|
|
1427
|
+
description: t("verbosityLowHint")
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
id: OUTPUT_VERBOSITY_MEDIUM,
|
|
1431
|
+
label: t("verbosityMedium"),
|
|
1432
|
+
description: t("verbosityMediumHint")
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
id: OUTPUT_VERBOSITY_HIGH,
|
|
1436
|
+
label: t("verbosityHigh"),
|
|
1437
|
+
description: t("verbosityHighHint")
|
|
1438
|
+
}
|
|
1439
|
+
];
|
|
1440
|
+
const verbosityLabel = verbosityItems.find((item) => item.id === preferenceSnapshot.outputVerbosity)?.label ?? t("verbosityDefault");
|
|
1325
1441
|
const busy = state.status === "selecting";
|
|
1326
1442
|
(0, react.useEffect)(() => {
|
|
1327
1443
|
if (available) load();
|
|
@@ -1339,7 +1455,12 @@ window.__ModuleLoader__.load({
|
|
|
1339
1455
|
}, [open]);
|
|
1340
1456
|
(0, react.useEffect)(() => {
|
|
1341
1457
|
if (!speedSupported && pane === "speed") setPane("root");
|
|
1342
|
-
|
|
1458
|
+
if (!verbositySupported && pane === "verbosity") setPane("root");
|
|
1459
|
+
}, [
|
|
1460
|
+
pane,
|
|
1461
|
+
speedSupported,
|
|
1462
|
+
verbositySupported
|
|
1463
|
+
]);
|
|
1343
1464
|
if (!available) return null;
|
|
1344
1465
|
const close = (restoreFocus = false) => {
|
|
1345
1466
|
setOpen(false);
|
|
@@ -1372,6 +1493,10 @@ window.__ModuleLoader__.load({
|
|
|
1372
1493
|
close(true);
|
|
1373
1494
|
preference.set({ [SPEED_MODE_FIELD]: speedMode });
|
|
1374
1495
|
};
|
|
1496
|
+
const chooseVerbosity = (outputVerbosity) => {
|
|
1497
|
+
close(true);
|
|
1498
|
+
preference.set({ [OUTPUT_VERBOSITY_FIELD]: outputVerbosity });
|
|
1499
|
+
};
|
|
1375
1500
|
const option = ({ key, label, description, selected, disabled, onClick }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1376
1501
|
type: "button",
|
|
1377
1502
|
role: "menuitemradio",
|
|
@@ -1504,6 +1629,19 @@ window.__ModuleLoader__.load({
|
|
|
1504
1629
|
onClick: () => chooseSpeed(SPEED_MODE_FAST)
|
|
1505
1630
|
})]
|
|
1506
1631
|
});
|
|
1632
|
+
else if (pane === "verbosity") submenu = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1633
|
+
className: "codexModelSelectSubmenu",
|
|
1634
|
+
role: "menu",
|
|
1635
|
+
"aria-label": t("verbosityTitle"),
|
|
1636
|
+
children: verbosityItems.map((item) => option({
|
|
1637
|
+
key: item.id,
|
|
1638
|
+
label: item.label,
|
|
1639
|
+
description: item.description,
|
|
1640
|
+
selected: preferenceSnapshot.outputVerbosity === item.id,
|
|
1641
|
+
disabled: !verbosityWritable,
|
|
1642
|
+
onClick: () => chooseVerbosity(item.id)
|
|
1643
|
+
}))
|
|
1644
|
+
});
|
|
1507
1645
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1508
1646
|
className: "codexModelSelect",
|
|
1509
1647
|
ref: rootRef,
|
|
@@ -1549,6 +1687,7 @@ window.__ModuleLoader__.load({
|
|
|
1549
1687
|
cell("model", t("modelLabel"), modelLabel),
|
|
1550
1688
|
reasoning === void 0 ? null : cell("effort", t("effortLabel"), effortLabel),
|
|
1551
1689
|
speedSupported && cell("speed", t("speedTitle"), t(fast ? "speedFast" : "speedStandard")),
|
|
1690
|
+
verbositySupported && cell("verbosity", t("verbosityTitle"), verbosityLabel),
|
|
1552
1691
|
submenu
|
|
1553
1692
|
]
|
|
1554
1693
|
}) : null]
|
|
@@ -2337,6 +2476,13 @@ window.__ModuleLoader__.load({
|
|
|
2337
2476
|
inject: (sessionId) => ({
|
|
2338
2477
|
t,
|
|
2339
2478
|
loadImage: (attachment) => conversation.resolveImage(sessionId, attachment),
|
|
2479
|
+
getImageViewer: () => {
|
|
2480
|
+
try {
|
|
2481
|
+
return ctx.get("nativeImageViewer");
|
|
2482
|
+
} catch {
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
},
|
|
2340
2486
|
attachForEdit: async (src, filename, draft) => {
|
|
2341
2487
|
const actx = sessions.scope(sessionId);
|
|
2342
2488
|
if (actx === void 0 || typeof conversation.createDraftImages !== "function" || conversation.input?.for === void 0) throw new Error("This DSH version does not provide the image composer bridge");
|
package/lib/index.js
CHANGED
|
@@ -726,11 +726,18 @@ const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
|
726
726
|
const LEGACY_QUICK_QUOTA_FIELD = "quickQuotaVisible";
|
|
727
727
|
const QUICK_QUOTA_MODE_PERCENT = "percent";
|
|
728
728
|
const SEARCH_PROVIDER_FIELD = "searchProvider";
|
|
729
|
+
const SEARCH_PROVIDER_AUTO = "auto";
|
|
729
730
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
731
|
+
const DEFAULT_SEARCH_PROVIDER = SEARCH_PROVIDER_AUTO;
|
|
730
732
|
const SPEED_MODE_FIELD = "speedMode";
|
|
731
733
|
const SPEED_MODE_STANDARD = "standard";
|
|
732
734
|
const SPEED_MODE_FAST = "fast";
|
|
733
735
|
const DEFAULT_SPEED_MODE = SPEED_MODE_STANDARD;
|
|
736
|
+
const OUTPUT_VERBOSITY_FIELD = "outputVerbosity";
|
|
737
|
+
const OUTPUT_VERBOSITY_DEFAULT = "default";
|
|
738
|
+
const OUTPUT_VERBOSITY_MEDIUM = "medium";
|
|
739
|
+
const OUTPUT_VERBOSITY_HIGH = "high";
|
|
740
|
+
const DEFAULT_OUTPUT_VERBOSITY = OUTPUT_VERBOSITY_DEFAULT;
|
|
734
741
|
const CONTEXT_MODE_FIELD = "contextMode";
|
|
735
742
|
const CONTEXT_MODE_STANDARD = "standard";
|
|
736
743
|
const CONTEXT_MODE_EXTENDED = "extended";
|
|
@@ -758,6 +765,12 @@ const CUSTOM_CONTEXT_MODEL_DEFAULTS = Object.freeze({
|
|
|
758
765
|
"gpt-5.5": 272e3,
|
|
759
766
|
"gpt-5.6": 272e3
|
|
760
767
|
});
|
|
768
|
+
const normalizeOutputVerbosity = (value) => [
|
|
769
|
+
"default",
|
|
770
|
+
"low",
|
|
771
|
+
"medium",
|
|
772
|
+
"high"
|
|
773
|
+
].includes(value) ? value : DEFAULT_OUTPUT_VERBOSITY;
|
|
761
774
|
const normalizeContextMode = (value) => [
|
|
762
775
|
"standard",
|
|
763
776
|
"extended",
|
|
@@ -829,7 +842,7 @@ const EXTENDED_CONTEXT_WINDOWS = Object.freeze({
|
|
|
829
842
|
"gpt-5.6-sol": 1e6,
|
|
830
843
|
"gpt-5.6-terra": 1e6
|
|
831
844
|
});
|
|
832
|
-
function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, resolveContextMode = () => void 0, resolveCustomContextWindow = () => void 0, runNetwork = (_area, operation) => operation() } = {}) {
|
|
845
|
+
function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, resolveOutputVerbosity = () => OUTPUT_VERBOSITY_DEFAULT, resolveContextMode = () => void 0, resolveCustomContextWindow = () => void 0, catalog, runNetwork = (_area, operation) => operation() } = {}) {
|
|
833
846
|
const provider = createOpenAICodexProvider();
|
|
834
847
|
const requestToken = Object.freeze({
|
|
835
848
|
name: "DSH-managed Codex OAuth request token",
|
|
@@ -842,25 +855,40 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
|
|
|
842
855
|
};
|
|
843
856
|
}
|
|
844
857
|
});
|
|
845
|
-
const
|
|
846
|
-
|
|
858
|
+
const modelMetadata = (model) => catalog?.metadata(model?.id);
|
|
859
|
+
const supportsVerbosity = (model) => modelMetadata(model)?.supportVerbosity ?? model?.id !== "gpt-5.3-codex-spark";
|
|
860
|
+
const withPreferences = (model, options = {}) => {
|
|
861
|
+
const metadata = modelMetadata(model);
|
|
862
|
+
const requestedVerbosity = resolveOutputVerbosity();
|
|
863
|
+
const textVerbosity = supportsVerbosity(model) ? requestedVerbosity === "default" ? metadata?.defaultVerbosity ?? "medium" : requestedVerbosity : void 0;
|
|
864
|
+
const fast = resolveSpeedMode() === "fast" && (metadata?.supportsFast ?? supportsCodexFastMode(model?.id));
|
|
847
865
|
const onPayload = options.onPayload;
|
|
848
866
|
return {
|
|
849
867
|
...options,
|
|
850
|
-
|
|
868
|
+
...textVerbosity === void 0 ? {} : { textVerbosity },
|
|
869
|
+
...fast ? { serviceTier: FAST_SERVICE_TIER } : {},
|
|
851
870
|
async onPayload(payload, requestModel) {
|
|
852
|
-
const
|
|
871
|
+
const preferred = {
|
|
853
872
|
...payload,
|
|
854
|
-
|
|
873
|
+
...textVerbosity === void 0 ? {} : { text: {
|
|
874
|
+
...payload.text ?? {},
|
|
875
|
+
verbosity: textVerbosity
|
|
876
|
+
} },
|
|
877
|
+
...fast ? { service_tier: FAST_SERVICE_TIER } : {}
|
|
855
878
|
};
|
|
879
|
+
const next = await onPayload?.(preferred, requestModel);
|
|
856
880
|
return {
|
|
857
|
-
...
|
|
858
|
-
|
|
881
|
+
...next ?? preferred,
|
|
882
|
+
...textVerbosity === void 0 ? {} : { text: {
|
|
883
|
+
...(next ?? preferred).text ?? {},
|
|
884
|
+
verbosity: textVerbosity
|
|
885
|
+
} },
|
|
886
|
+
...fast ? { service_tier: FAST_SERVICE_TIER } : {}
|
|
859
887
|
};
|
|
860
888
|
}
|
|
861
889
|
};
|
|
862
890
|
};
|
|
863
|
-
const getModels = () => provider.getModels().map((model) => {
|
|
891
|
+
const getModels = () => (catalog?.getModels(provider.getModels()) ?? provider.getModels()).map((model) => {
|
|
864
892
|
const maximum = EXTENDED_CONTEXT_WINDOWS[model.id];
|
|
865
893
|
const mode = resolveContextMode();
|
|
866
894
|
if (maximum === void 0 || !["extended", "custom"].includes(mode)) return model;
|
|
@@ -896,17 +924,147 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
|
|
|
896
924
|
apiKey: requestToken
|
|
897
925
|
}),
|
|
898
926
|
getModels,
|
|
899
|
-
stream: (model, context, options) => networkIterable(() => provider.stream(model, context,
|
|
900
|
-
streamSimple: (model, context, options) => networkIterable(() => provider.streamSimple(model, context,
|
|
927
|
+
stream: (model, context, options) => networkIterable(() => provider.stream(model, context, withPreferences(model, options))),
|
|
928
|
+
streamSimple: (model, context, options) => networkIterable(() => provider.streamSimple(model, context, withPreferences(model, options)))
|
|
901
929
|
});
|
|
902
930
|
}
|
|
903
931
|
//#endregion
|
|
904
932
|
//#region src/version.js
|
|
905
|
-
const PACKAGE_VERSION = "1.
|
|
933
|
+
const PACKAGE_VERSION = "1.10.0";
|
|
906
934
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
907
935
|
//#endregion
|
|
936
|
+
//#region src/model-catalog.js
|
|
937
|
+
const CODEX_MODELS_URL = `https://chatgpt.com/backend-api/codex/models?client_version=${encodeURIComponent(PACKAGE_VERSION)}`;
|
|
938
|
+
const LEVELS = [
|
|
939
|
+
"off",
|
|
940
|
+
"minimal",
|
|
941
|
+
"low",
|
|
942
|
+
"medium",
|
|
943
|
+
"high",
|
|
944
|
+
"xhigh",
|
|
945
|
+
"max"
|
|
946
|
+
];
|
|
947
|
+
const record$4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
948
|
+
const nonEmpty$2 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
949
|
+
const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
950
|
+
function reasoningMap(levels) {
|
|
951
|
+
const supported = new Set((Array.isArray(levels) ? levels : []).map((level) => nonEmpty$2(record$4(level) ? level.effort : void 0)).filter(Boolean));
|
|
952
|
+
const map = Object.fromEntries(LEVELS.map((level) => [level, null]));
|
|
953
|
+
if (supported.has("none")) map.off = "none";
|
|
954
|
+
for (const level of LEVELS.slice(1)) if (supported.has(level)) map[level] = level;
|
|
955
|
+
return map;
|
|
956
|
+
}
|
|
957
|
+
function visibleModel(value) {
|
|
958
|
+
if (!record$4(value)) return void 0;
|
|
959
|
+
const id = nonEmpty$2(value.slug);
|
|
960
|
+
if (id === void 0 || value.supported_in_api !== true || value.visibility !== "list") return void 0;
|
|
961
|
+
const supported = Array.isArray(value.supported_reasoning_levels) ? value.supported_reasoning_levels : [];
|
|
962
|
+
const input = Array.isArray(value.input_modalities) ? value.input_modalities.filter((item) => ["text", "image"].includes(item)) : ["text", "image"];
|
|
963
|
+
return {
|
|
964
|
+
id,
|
|
965
|
+
name: nonEmpty$2(value.display_name) ?? id,
|
|
966
|
+
description: nonEmpty$2(value.description),
|
|
967
|
+
priority: Number.isFinite(value.priority) ? value.priority : 0,
|
|
968
|
+
input: input.length > 0 ? input : ["text"],
|
|
969
|
+
contextWindow: positiveInteger(value.context_window) ?? positiveInteger(value.max_context_window),
|
|
970
|
+
reasoning: supported.length > 0,
|
|
971
|
+
thinkingLevelMap: reasoningMap(supported),
|
|
972
|
+
supportVerbosity: value.support_verbosity === true,
|
|
973
|
+
defaultVerbosity: [
|
|
974
|
+
"low",
|
|
975
|
+
"medium",
|
|
976
|
+
"high"
|
|
977
|
+
].includes(value.default_verbosity) ? value.default_verbosity : void 0,
|
|
978
|
+
supportsFast: [...Array.isArray(value.additional_speed_tiers) ? value.additional_speed_tiers : [], ...Array.isArray(value.service_tiers) ? value.service_tiers.map((tier) => tier?.id) : []].some((tier) => tier === "fast" || tier === "priority")
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function parseOfficialModelCatalog(value) {
|
|
982
|
+
if (!record$4(value) || !Array.isArray(value.models)) throw new Error("Codex returned a malformed model catalog");
|
|
983
|
+
const seen = /* @__PURE__ */ new Set();
|
|
984
|
+
return value.models.map(visibleModel).filter((model) => model !== void 0 && !seen.has(model.id) && seen.add(model.id)).sort((left, right) => right.priority - left.priority);
|
|
985
|
+
}
|
|
986
|
+
function mergeModel(baseModels, remote) {
|
|
987
|
+
const base = baseModels.find((model) => model.id === remote.id) ?? baseModels.find((model) => model.id !== "gpt-5.3-codex-spark") ?? baseModels[0];
|
|
988
|
+
if (base === void 0) return void 0;
|
|
989
|
+
return {
|
|
990
|
+
...base,
|
|
991
|
+
id: remote.id,
|
|
992
|
+
name: remote.name,
|
|
993
|
+
input: remote.input,
|
|
994
|
+
reasoning: remote.reasoning,
|
|
995
|
+
thinkingLevelMap: remote.thinkingLevelMap,
|
|
996
|
+
...remote.contextWindow === void 0 ? {} : { contextWindow: remote.contextWindow },
|
|
997
|
+
...base.id === remote.id ? {} : { cost: {
|
|
998
|
+
input: 0,
|
|
999
|
+
output: 0,
|
|
1000
|
+
cacheRead: 0,
|
|
1001
|
+
cacheWrite: 0
|
|
1002
|
+
} }
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function createOfficialModelCatalog(options = {}) {
|
|
1006
|
+
const fetchCatalog = options.fetch ?? fetch;
|
|
1007
|
+
let models;
|
|
1008
|
+
let metadata = /* @__PURE__ */ new Map();
|
|
1009
|
+
let etag;
|
|
1010
|
+
let revision = 0;
|
|
1011
|
+
let refreshing;
|
|
1012
|
+
const refresh = async ({ signal } = {}) => {
|
|
1013
|
+
if (refreshing !== void 0) return refreshing;
|
|
1014
|
+
refreshing = (async () => {
|
|
1015
|
+
const auth = await options.getAuth({ signal });
|
|
1016
|
+
const credential = await options.readCredential({ signal });
|
|
1017
|
+
const access = auth?.auth?.apiKey;
|
|
1018
|
+
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
1019
|
+
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) return false;
|
|
1020
|
+
const headers = {
|
|
1021
|
+
authorization: `Bearer ${access}`,
|
|
1022
|
+
"chatgpt-account-id": accountId,
|
|
1023
|
+
accept: "application/json",
|
|
1024
|
+
originator: "pi",
|
|
1025
|
+
"user-agent": USER_AGENT,
|
|
1026
|
+
...etag === void 0 ? {} : { "if-none-match": etag }
|
|
1027
|
+
};
|
|
1028
|
+
const response = await fetchCatalog(CODEX_MODELS_URL, {
|
|
1029
|
+
method: "GET",
|
|
1030
|
+
redirect: "error",
|
|
1031
|
+
headers,
|
|
1032
|
+
signal
|
|
1033
|
+
});
|
|
1034
|
+
if (response.status === 304) return false;
|
|
1035
|
+
if (!response.ok) throw new Error(`Codex model catalog failed (HTTP ${response.status})`);
|
|
1036
|
+
const remote = parseOfficialModelCatalog(await response.json());
|
|
1037
|
+
if (remote.length === 0) throw new Error("Codex returned an empty model catalog");
|
|
1038
|
+
const baseModels = options.baseModels();
|
|
1039
|
+
const next = remote.map((model) => mergeModel(baseModels, model)).filter(Boolean);
|
|
1040
|
+
if (next.length === 0) throw new Error("Codex model catalog has no compatible models");
|
|
1041
|
+
models = next;
|
|
1042
|
+
metadata = new Map(remote.map((model) => [model.id, model]));
|
|
1043
|
+
etag = nonEmpty$2(response.headers.get("etag")) ?? etag;
|
|
1044
|
+
revision += 1;
|
|
1045
|
+
return true;
|
|
1046
|
+
})().finally(() => {
|
|
1047
|
+
refreshing = void 0;
|
|
1048
|
+
});
|
|
1049
|
+
return refreshing;
|
|
1050
|
+
};
|
|
1051
|
+
return Object.freeze({
|
|
1052
|
+
refresh,
|
|
1053
|
+
getModels: (fallback) => models ?? fallback,
|
|
1054
|
+
metadata: (modelId) => metadata.get(modelId),
|
|
1055
|
+
revision: () => revision,
|
|
1056
|
+
clear() {
|
|
1057
|
+
models = void 0;
|
|
1058
|
+
metadata = /* @__PURE__ */ new Map();
|
|
1059
|
+
etag = void 0;
|
|
1060
|
+
revision += 1;
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
//#endregion
|
|
908
1065
|
//#region src/codex-search.js
|
|
909
1066
|
const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
|
|
1067
|
+
const CODEX_AUTO_SEARCH_PROVIDER_ID = "codex-subscription-auto";
|
|
910
1068
|
const CODEX_SEARCH_URL = "https://chatgpt.com/backend-api/codex/alpha/search";
|
|
911
1069
|
const DEFAULT_MODEL = "gpt-5.6-luna";
|
|
912
1070
|
const MAX_OUTPUT_TOKENS = 4096;
|
|
@@ -1021,6 +1179,19 @@ function createCodexSearchProvider(options) {
|
|
|
1021
1179
|
}
|
|
1022
1180
|
});
|
|
1023
1181
|
}
|
|
1182
|
+
/** Route each request by its initiating model without changing the user's explicit overrides. */
|
|
1183
|
+
function createCodexAutoSearchProvider(options) {
|
|
1184
|
+
return Object.freeze({
|
|
1185
|
+
id: CODEX_AUTO_SEARCH_PROVIDER_ID,
|
|
1186
|
+
available: () => true,
|
|
1187
|
+
async search(request, signal) {
|
|
1188
|
+
if (options.resolveModelProvider?.() === "openai-codex") return options.codex.search(request, signal);
|
|
1189
|
+
const provider = options.resolveDshProvider?.();
|
|
1190
|
+
if (provider === void 0 || provider.id === "codex-subscription-auto" || provider.id === "codex-subscription" || provider.available() !== true) throw new WebError("DSH default search is unavailable", "WEB_PROVIDER_UNAVAILABLE");
|
|
1191
|
+
return provider.search(request, signal);
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1024
1195
|
//#endregion
|
|
1025
1196
|
//#region src/codex-images.js
|
|
1026
1197
|
const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
|
|
@@ -1029,6 +1200,17 @@ const CODEX_IMAGE_EDIT_URL = "https://chatgpt.com/backend-api/codex/images/edits
|
|
|
1029
1200
|
const IMAGE_MODEL = "gpt-image-2";
|
|
1030
1201
|
const MAX_REFERENCE_IMAGES = 5;
|
|
1031
1202
|
const RESPONSE_ENVELOPE_BYTES = 1024 * 1024;
|
|
1203
|
+
const IMAGE_QUALITIES = /* @__PURE__ */ new Set([
|
|
1204
|
+
"auto",
|
|
1205
|
+
"low",
|
|
1206
|
+
"medium",
|
|
1207
|
+
"high"
|
|
1208
|
+
]);
|
|
1209
|
+
const IMAGE_BACKGROUNDS = /* @__PURE__ */ new Set([
|
|
1210
|
+
"auto",
|
|
1211
|
+
"transparent",
|
|
1212
|
+
"opaque"
|
|
1213
|
+
]);
|
|
1032
1214
|
const PNG_SIGNATURE = Buffer.from([
|
|
1033
1215
|
137,
|
|
1034
1216
|
80,
|
|
@@ -1041,6 +1223,27 @@ const PNG_SIGNATURE = Buffer.from([
|
|
|
1041
1223
|
]);
|
|
1042
1224
|
const record$2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1043
1225
|
const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1226
|
+
function normalizeImageOptions(args) {
|
|
1227
|
+
const quality = nonEmpty(args?.quality) ?? "auto";
|
|
1228
|
+
const background = nonEmpty(args?.background) ?? "auto";
|
|
1229
|
+
const size = nonEmpty(args?.size) ?? "auto";
|
|
1230
|
+
if (!IMAGE_QUALITIES.has(quality)) throw new Error("quality must be auto, low, medium, or high");
|
|
1231
|
+
if (!IMAGE_BACKGROUNDS.has(background)) throw new Error("background must be auto, transparent, or opaque");
|
|
1232
|
+
if (size !== "auto") {
|
|
1233
|
+
const match = /^(\d+)x(\d+)$/u.exec(size);
|
|
1234
|
+
const width = Number(match?.[1]);
|
|
1235
|
+
const height = Number(match?.[2]);
|
|
1236
|
+
const short = Math.min(width, height);
|
|
1237
|
+
const long = Math.max(width, height);
|
|
1238
|
+
const pixels = width * height;
|
|
1239
|
+
if (match === null || width % 16 !== 0 || height % 16 !== 0 || long > 3840 || long > short * 3 || pixels < 655360 || pixels > 8294400) throw new Error("size must be auto or a valid GPT Image 2 widthxheight resolution");
|
|
1240
|
+
}
|
|
1241
|
+
return {
|
|
1242
|
+
quality,
|
|
1243
|
+
background,
|
|
1244
|
+
size
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1044
1247
|
function encodedLimit(decodedBytes) {
|
|
1045
1248
|
return Math.ceil(decodedBytes / 3) * 4;
|
|
1046
1249
|
}
|
|
@@ -1187,6 +1390,29 @@ function createCodexImageTool(options) {
|
|
|
1187
1390
|
required: true,
|
|
1188
1391
|
description: "A complete, production-ready description of the image to generate."
|
|
1189
1392
|
},
|
|
1393
|
+
size: {
|
|
1394
|
+
type: "string",
|
|
1395
|
+
description: "Optional GPT Image 2 output size. Use auto unless the user requests an exact valid widthxheight resolution."
|
|
1396
|
+
},
|
|
1397
|
+
quality: {
|
|
1398
|
+
type: "string",
|
|
1399
|
+
enum: [
|
|
1400
|
+
"auto",
|
|
1401
|
+
"low",
|
|
1402
|
+
"medium",
|
|
1403
|
+
"high"
|
|
1404
|
+
],
|
|
1405
|
+
description: "Optional rendering quality. Use auto unless the user requests draft speed or final quality."
|
|
1406
|
+
},
|
|
1407
|
+
background: {
|
|
1408
|
+
type: "string",
|
|
1409
|
+
enum: [
|
|
1410
|
+
"auto",
|
|
1411
|
+
"transparent",
|
|
1412
|
+
"opaque"
|
|
1413
|
+
],
|
|
1414
|
+
description: "Optional background mode. Request transparent only when the user needs transparency."
|
|
1415
|
+
},
|
|
1190
1416
|
referenceImages: {
|
|
1191
1417
|
type: "array",
|
|
1192
1418
|
description: "Optional explicit references to 1-5 prior images to edit. Omit for a new image.",
|
|
@@ -1228,6 +1454,7 @@ function createCodexImageTool(options) {
|
|
|
1228
1454
|
async execute(args, exec) {
|
|
1229
1455
|
const prompt = nonEmpty(args.prompt);
|
|
1230
1456
|
if (prompt === void 0) throw new Error("prompt must be a non-empty string");
|
|
1457
|
+
const imageOptions = normalizeImageOptions(args);
|
|
1231
1458
|
const auth = await options.getAuth({ signal: exec.signal });
|
|
1232
1459
|
const credential = await options.readCredential({ signal: exec.signal });
|
|
1233
1460
|
const access = auth?.auth?.apiKey;
|
|
@@ -1254,10 +1481,10 @@ function createCodexImageTool(options) {
|
|
|
1254
1481
|
body: JSON.stringify({
|
|
1255
1482
|
...images === void 0 ? {} : { images },
|
|
1256
1483
|
prompt,
|
|
1257
|
-
background:
|
|
1484
|
+
background: imageOptions.background,
|
|
1258
1485
|
model: IMAGE_MODEL,
|
|
1259
|
-
quality:
|
|
1260
|
-
size:
|
|
1486
|
+
quality: imageOptions.quality,
|
|
1487
|
+
size: imageOptions.size
|
|
1261
1488
|
}),
|
|
1262
1489
|
signal: exec.signal
|
|
1263
1490
|
});
|
|
@@ -1367,6 +1594,7 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
1367
1594
|
configuration: {
|
|
1368
1595
|
contextMode: preference.contextMode,
|
|
1369
1596
|
quickQuotaMode: preference.quickQuotaMode,
|
|
1597
|
+
...typeof preference.outputVerbosity === "string" ? { outputVerbosity: preference.outputVerbosity } : {},
|
|
1370
1598
|
searchProvider: preference.searchProvider,
|
|
1371
1599
|
speedMode: preference.speedMode,
|
|
1372
1600
|
writable: preference.writable === true
|
|
@@ -1783,7 +2011,7 @@ const publicError = (code, message) => ({
|
|
|
1783
2011
|
details: { issues: [] }
|
|
1784
2012
|
}
|
|
1785
2013
|
});
|
|
1786
|
-
function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, diagnosticsReader }) {
|
|
2014
|
+
function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, diagnosticsReader, modelCatalog }) {
|
|
1787
2015
|
return async (endpoint, payload, signal) => {
|
|
1788
2016
|
if (endpoint === "diagnostics") try {
|
|
1789
2017
|
signal.throwIfAborted();
|
|
@@ -1808,13 +2036,26 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1808
2036
|
patch[QUICK_QUOTA_MODE_FIELD] = payload[QUICK_QUOTA_MODE_FIELD];
|
|
1809
2037
|
}
|
|
1810
2038
|
if (Object.hasOwn(payload ?? {}, "searchProvider")) {
|
|
1811
|
-
if (![
|
|
2039
|
+
if (![
|
|
2040
|
+
"auto",
|
|
2041
|
+
"dsh",
|
|
2042
|
+
"codex"
|
|
2043
|
+
].includes(payload["searchProvider"])) return publicError("internal", "Invalid search provider preference");
|
|
1812
2044
|
patch[SEARCH_PROVIDER_FIELD] = payload[SEARCH_PROVIDER_FIELD];
|
|
1813
2045
|
}
|
|
1814
2046
|
if (Object.hasOwn(payload ?? {}, "speedMode")) {
|
|
1815
2047
|
if (!["standard", "fast"].includes(payload["speedMode"])) return publicError("internal", "Invalid speed mode preference");
|
|
1816
2048
|
patch[SPEED_MODE_FIELD] = payload[SPEED_MODE_FIELD];
|
|
1817
2049
|
}
|
|
2050
|
+
if (Object.hasOwn(payload ?? {}, "outputVerbosity")) {
|
|
2051
|
+
if (![
|
|
2052
|
+
"default",
|
|
2053
|
+
"low",
|
|
2054
|
+
"medium",
|
|
2055
|
+
"high"
|
|
2056
|
+
].includes(payload["outputVerbosity"])) return publicError("internal", "Invalid output verbosity preference");
|
|
2057
|
+
patch[OUTPUT_VERBOSITY_FIELD] = payload[OUTPUT_VERBOSITY_FIELD];
|
|
2058
|
+
}
|
|
1818
2059
|
if (Object.hasOwn(payload ?? {}, "contextMode")) {
|
|
1819
2060
|
if (![
|
|
1820
2061
|
"standard",
|
|
@@ -1890,26 +2131,34 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
1890
2131
|
if (endpoint === "logout" && result.ok === true) {
|
|
1891
2132
|
usageReader.clear();
|
|
1892
2133
|
resetCreditService.clear();
|
|
1893
|
-
|
|
2134
|
+
modelCatalog?.clear();
|
|
2135
|
+
} else if (result.ok === true && (endpoint === "status" || result.value?.authenticated === true)) modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
|
|
1894
2136
|
return result;
|
|
1895
2137
|
};
|
|
1896
2138
|
}
|
|
1897
2139
|
function createSearchProviderSwitcher(loader) {
|
|
1898
2140
|
const webEntry = () => [...loader.entries()].find((entry) => entry.options?.id === WEB_ENTRY_ID);
|
|
1899
|
-
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2141
|
+
const dshProviderId = () => {
|
|
2142
|
+
const baseConfig = webEntry()?.options?.config ?? {};
|
|
2143
|
+
return typeof baseConfig.searchProvider === "string" && baseConfig.searchProvider.length > 0 ? baseConfig.searchProvider : DSH_SEARCH_PROVIDER_FALLBACK;
|
|
2144
|
+
};
|
|
2145
|
+
return Object.freeze({
|
|
2146
|
+
dshProviderId,
|
|
2147
|
+
async select(selection) {
|
|
2148
|
+
const entry = webEntry();
|
|
2149
|
+
const fiber = entry?.fiber;
|
|
2150
|
+
if (entry === void 0 || fiber === void 0 || typeof fiber.update !== "function") throw new Error("DSH web runtime is unavailable");
|
|
2151
|
+
const baseConfig = entry.options?.config ?? {};
|
|
2152
|
+
const currentConfig = fiber.config ?? baseConfig;
|
|
2153
|
+
const dshProvider = dshProviderId();
|
|
2154
|
+
const provider = selection === "codex" ? CODEX_SEARCH_PROVIDER_ID : selection === "auto" ? CODEX_AUTO_SEARCH_PROVIDER_ID : dshProvider;
|
|
2155
|
+
if (currentConfig.searchProvider === provider) return;
|
|
2156
|
+
await fiber.update({
|
|
2157
|
+
...currentConfig,
|
|
2158
|
+
searchProvider: provider
|
|
2159
|
+
}, true);
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
1913
2162
|
}
|
|
1914
2163
|
function apply(ctx) {
|
|
1915
2164
|
const settings = ctx.settings.register(settingsNamespace(SETTINGS_NAMESPACE), z.object({
|
|
@@ -1919,8 +2168,18 @@ function apply(ctx) {
|
|
|
1919
2168
|
"bar"
|
|
1920
2169
|
]),
|
|
1921
2170
|
[LEGACY_QUICK_QUOTA_FIELD]: z.boolean(),
|
|
1922
|
-
[SEARCH_PROVIDER_FIELD]: z.union([
|
|
2171
|
+
[SEARCH_PROVIDER_FIELD]: z.union([
|
|
2172
|
+
SEARCH_PROVIDER_AUTO,
|
|
2173
|
+
"dsh",
|
|
2174
|
+
SEARCH_PROVIDER_CODEX
|
|
2175
|
+
]).default(DEFAULT_SEARCH_PROVIDER),
|
|
1923
2176
|
[SPEED_MODE_FIELD]: z.union([SPEED_MODE_STANDARD, SPEED_MODE_FAST]).default(DEFAULT_SPEED_MODE),
|
|
2177
|
+
[OUTPUT_VERBOSITY_FIELD]: z.union([
|
|
2178
|
+
OUTPUT_VERBOSITY_DEFAULT,
|
|
2179
|
+
"low",
|
|
2180
|
+
OUTPUT_VERBOSITY_MEDIUM,
|
|
2181
|
+
OUTPUT_VERBOSITY_HIGH
|
|
2182
|
+
]).default(DEFAULT_OUTPUT_VERBOSITY),
|
|
1924
2183
|
[CONTEXT_MODE_FIELD]: z.union([
|
|
1925
2184
|
CONTEXT_MODE_STANDARD,
|
|
1926
2185
|
CONTEXT_MODE_EXTENDED,
|
|
@@ -1931,13 +2190,24 @@ function apply(ctx) {
|
|
|
1931
2190
|
}));
|
|
1932
2191
|
const searchProvider = createSearchProviderSwitcher(ctx.loader);
|
|
1933
2192
|
const network = createCodexNetworkTransport();
|
|
2193
|
+
const store = new DshOAuthCredentialStore(ctx.credentials, CREDENTIAL_REF, [LEGACY_CREDENTIAL_REF]);
|
|
2194
|
+
const baseProvider = createOpenAICodexProvider();
|
|
2195
|
+
let resolveAuth = async () => void 0;
|
|
2196
|
+
const modelCatalog = createOfficialModelCatalog({
|
|
2197
|
+
getAuth: (options) => resolveAuth(options),
|
|
2198
|
+
readCredential: (options) => store.read(PROVIDER, options),
|
|
2199
|
+
baseModels: () => baseProvider.getModels(),
|
|
2200
|
+
fetch: (input, init) => network.fetch("catalog", input, init)
|
|
2201
|
+
});
|
|
1934
2202
|
const provider = openaiCodexSubscriptionProvider({
|
|
1935
2203
|
resolveSpeedMode: () => settings.get()[SPEED_MODE_FIELD],
|
|
2204
|
+
resolveOutputVerbosity: () => normalizeOutputVerbosity(settings.get()[OUTPUT_VERBOSITY_FIELD]),
|
|
1936
2205
|
resolveContextMode: () => normalizeContextMode(settings.get()[CONTEXT_MODE_FIELD]),
|
|
1937
2206
|
resolveCustomContextWindow: (modelKey) => {
|
|
1938
2207
|
const field = CUSTOM_CONTEXT_MODEL_FIELDS[modelKey];
|
|
1939
2208
|
return normalizeCustomContextWindow(settings.get()[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey]);
|
|
1940
2209
|
},
|
|
2210
|
+
catalog: modelCatalog,
|
|
1941
2211
|
runNetwork: network.run
|
|
1942
2212
|
});
|
|
1943
2213
|
const preferences = {
|
|
@@ -1945,15 +2215,16 @@ function apply(ctx) {
|
|
|
1945
2215
|
[QUICK_QUOTA_MODE_FIELD]: normalizeQuickQuotaMode(settings.get()[QUICK_QUOTA_MODE_FIELD], settings.get()[LEGACY_QUICK_QUOTA_FIELD]),
|
|
1946
2216
|
[SEARCH_PROVIDER_FIELD]: settings.get()[SEARCH_PROVIDER_FIELD],
|
|
1947
2217
|
[SPEED_MODE_FIELD]: settings.get()[SPEED_MODE_FIELD],
|
|
2218
|
+
[OUTPUT_VERBOSITY_FIELD]: normalizeOutputVerbosity(settings.get()[OUTPUT_VERBOSITY_FIELD]),
|
|
1948
2219
|
[CONTEXT_MODE_FIELD]: normalizeContextMode(settings.get()[CONTEXT_MODE_FIELD]),
|
|
1949
2220
|
[CUSTOM_CONTEXT_WINDOW_FIELD]: normalizeCustomContextWindow(settings.get()[CUSTOM_CONTEXT_WINDOW_FIELD]),
|
|
1950
2221
|
...Object.fromEntries(Object.entries(CUSTOM_CONTEXT_MODEL_FIELDS).map(([modelKey, field]) => [field, normalizeCustomContextWindow(settings.get()[field] ?? CUSTOM_CONTEXT_MODEL_DEFAULTS[modelKey], CUSTOM_CONTEXT_MODEL_CAPS[modelKey])])),
|
|
1951
2222
|
contextModels: contextModelGroups(provider.getModels()),
|
|
2223
|
+
verbosityModels: provider.getModels().filter((model) => modelCatalog.metadata(model.id)?.supportVerbosity ?? model.id !== "gpt-5.3-codex-spark").map((model) => model.id),
|
|
1952
2224
|
writable: ctx.settings.writable
|
|
1953
2225
|
}),
|
|
1954
2226
|
update: (patch) => settings.update(patch)
|
|
1955
2227
|
};
|
|
1956
|
-
const store = new DshOAuthCredentialStore(ctx.credentials, CREDENTIAL_REF, [LEGACY_CREDENTIAL_REF]);
|
|
1957
2228
|
const authModels = createModels({ credentials: store });
|
|
1958
2229
|
authModels.setProvider(provider);
|
|
1959
2230
|
const profile = Object.freeze({
|
|
@@ -1971,14 +2242,18 @@ function apply(ctx) {
|
|
|
1971
2242
|
let profileKey;
|
|
1972
2243
|
let profileSnapshot;
|
|
1973
2244
|
const profiles = () => {
|
|
1974
|
-
const key = [
|
|
2245
|
+
const key = [
|
|
2246
|
+
modelCatalog.revision(),
|
|
2247
|
+
normalizeContextMode(settings.get()[CONTEXT_MODE_FIELD]),
|
|
2248
|
+
...Object.values(CUSTOM_CONTEXT_MODEL_FIELDS).map((field) => settings.get()[field])
|
|
2249
|
+
].join(":");
|
|
1975
2250
|
if (key !== profileKey) {
|
|
1976
2251
|
profileKey = key;
|
|
1977
2252
|
profileSnapshot = /* @__PURE__ */ new Map([[PROVIDER, profile]]);
|
|
1978
2253
|
}
|
|
1979
2254
|
return profileSnapshot;
|
|
1980
2255
|
};
|
|
1981
|
-
|
|
2256
|
+
resolveAuth = () => authModels.getAuth(PROVIDER);
|
|
1982
2257
|
const adapterAuth = Object.freeze({
|
|
1983
2258
|
credentials: store,
|
|
1984
2259
|
authContext: Object.freeze({
|
|
@@ -2009,7 +2284,7 @@ function apply(ctx) {
|
|
|
2009
2284
|
});
|
|
2010
2285
|
ctx.llm.registerAdapter([PROVIDER], adapter);
|
|
2011
2286
|
const currentAgent = () => ctx.get?.("agents")?.currentInitiator?.();
|
|
2012
|
-
|
|
2287
|
+
const codexSearch = createCodexSearchProvider({
|
|
2013
2288
|
getAuth: resolveAuth,
|
|
2014
2289
|
readCredential: (options) => store.read(PROVIDER, options),
|
|
2015
2290
|
resolveModel: () => {
|
|
@@ -2018,6 +2293,12 @@ function apply(ctx) {
|
|
|
2018
2293
|
},
|
|
2019
2294
|
resolveSessionId: () => currentAgent()?.session.id,
|
|
2020
2295
|
fetch: (input, init) => network.fetch("search", input, init)
|
|
2296
|
+
});
|
|
2297
|
+
ctx.web.registerSearchProvider(codexSearch);
|
|
2298
|
+
ctx.web.registerSearchProvider(createCodexAutoSearchProvider({
|
|
2299
|
+
codex: codexSearch,
|
|
2300
|
+
resolveModelProvider: () => currentAgent()?.session.requestContext?.()?.provider,
|
|
2301
|
+
resolveDshProvider: () => ctx.web.searchProviders?.get(searchProvider.dshProviderId())
|
|
2021
2302
|
}));
|
|
2022
2303
|
ctx.effect(() => {
|
|
2023
2304
|
const select = async (value) => {
|
|
@@ -2053,8 +2334,12 @@ function apply(ctx) {
|
|
|
2053
2334
|
preferences,
|
|
2054
2335
|
login: coordinator.supportState(),
|
|
2055
2336
|
network
|
|
2056
|
-
})
|
|
2337
|
+
}),
|
|
2338
|
+
modelCatalog
|
|
2057
2339
|
});
|
|
2340
|
+
ctx.effect(() => {
|
|
2341
|
+
modelCatalog.refresh().catch((error) => ctx.logger?.debug?.("could not refresh Codex model catalog: %s", error.message));
|
|
2342
|
+
}, "codex-subscription: official model catalog");
|
|
2058
2343
|
ctx.effect(() => ctx.connection.rpc.handle(CHANNEL, handler, { authority: "loopback" }), "codex-subscription: loopback account RPC");
|
|
2059
2344
|
}
|
|
2060
2345
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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",
|