dsh-codex-subscription 1.1.2 → 1.1.4

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 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.1.2` package below; do not install a moving branch.
9
+ - Use the exact `1.1.4` 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.
@@ -38,19 +38,19 @@ For an official DSH installation run through npm, keep the complete `npx` prefix
38
38
  run command does not create a global `dsh` command:
39
39
 
40
40
  ```sh
41
- npx -y @deepseek-ai/dsh@0.1.0-rc.8 plugin --profile web add dsh-codex-subscription@1.1.2
41
+ npx -y @deepseek-ai/dsh@0.1.0-rc.8 plugin --profile web add dsh-codex-subscription@1.1.4
42
42
  ```
43
43
 
44
44
  With an existing global `dsh` command:
45
45
 
46
46
  ```sh
47
- dsh plugin --profile web add dsh-codex-subscription@1.1.2
47
+ dsh plugin --profile web add dsh-codex-subscription@1.1.4
48
48
  ```
49
49
 
50
50
  From a DSH-Portable folder:
51
51
 
52
52
  ```powershell
53
- .\dsh.exe plugin --profile web add dsh-codex-subscription@1.1.2
53
+ .\dsh.exe plugin --profile web add dsh-codex-subscription@1.1.4
54
54
  ```
55
55
 
56
56
  Use the same `add` command to update or repair. This is the complete package-changing operation.
@@ -70,7 +70,7 @@ For DSH-Portable, replace `dsh` with `.\dsh.exe`. Static acceptance requires:
70
70
 
71
71
  For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.0-rc.8` in place of `dsh`.
72
72
 
73
- 1. `dsh-codex-subscription` version `1.1.2` appears exactly once.
73
+ 1. `dsh-codex-subscription` version `1.1.4` appears exactly once.
74
74
  2. `codex-subscription` appears exactly once in the composed config.
75
75
  3. No unrelated plugin or profile was changed and DSH was not restarted.
76
76
 
package/README.en.md CHANGED
@@ -138,8 +138,8 @@ window returned by the service; Spark uses its independent quota. The plugin doe
138
138
 
139
139
  ### Composer speed (Beta)
140
140
 
141
- With a supported Codex model selected, `1×` or a lightning icon appears immediately before the model name.
142
- Click it to choose Standard or Fast. Spark does not show this control. Fast mode increases speed and uses more Credits;
141
+ With a supported Codex model selected, open the composer's model menu to choose Standard or Fast.
142
+ 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;
143
143
  see the [OpenAI Codex Speed documentation](https://learn.chatgpt.com/docs/agent-configuration/speed) for the current rules.
144
144
 
145
145
  ## Update and uninstall
@@ -174,7 +174,7 @@ From a DSH-Portable folder, replace `dsh` above with `.\dsh.exe`.
174
174
  - **`dsh` is not recognized:** the official npm route does not create a global `dsh` command; use the complete `npx -y @deepseek-ai/dsh@0.1.0-rc.8 ...` command above;
175
175
  - **`dsh.exe` is not recognized:** that file is not in the current folder; enter the DSH-Portable folder first or use the one-line Windows setup;
176
176
  - **DSH-Portable is not found:** enter its folder and rerun setup, or run `.\dsh.exe plugin --profile web add dsh-codex-subscription` directly;
177
- - **More than one DSH exists:** the one-line setup lists running Portable copies; enter its number. For an Agent, use the intended folder or pass `-DshPath` explicitly;
177
+ - **More than one DSH exists:** the one-line setup combines running copies with Portable folders detected in common locations; enter its number. For an Agent, use the intended folder or pass `-DshPath` explicitly;
178
178
  - **Setup still fails:** send the Agent guide above to an Agent. Do not delete the profile or change the system PATH to force an install.
179
179
 
180
180
  The ChatGPT Codex backend and DSH can change independently. This community project is not affiliated with or endorsed by DeepSeek or OpenAI.
package/README.md CHANGED
@@ -137,8 +137,8 @@ Spark 使用独立额度。插件不会写死“5 小时 + 每周”,也不会
137
137
 
138
138
  ### 输入框速度(Beta)
139
139
 
140
- 选择支持的 Codex 模型后,模型名称左侧会显示 `1×` 或闪电。点击即可切换标准与高速;
141
- Spark 不显示这个入口。高速模式会提高速度,也会消耗更多 Credits;具体规则见
140
+ 选择支持的 Codex 模型后,可在输入框的模型菜单中切换标准与高速。标准模式不增加图标,
141
+ 只有高速模式会在模型名称左侧显示闪电;Spark 不显示速度入口。高速模式会提高速度,也会消耗更多 Credits;具体规则见
142
142
  [OpenAI Codex Speed 文档](https://learn.chatgpt.com/docs/agent-configuration/speed)。
143
143
 
144
144
  ## 更新与卸载
@@ -173,7 +173,7 @@ DSH-Portable 在其目录中把上述 `dsh` 换成 `.\dsh.exe`。
173
173
  - **`dsh` 无法识别**:官方 npm 方式本来就不会创建全局 `dsh` 命令,请使用上面的完整 `npx -y @deepseek-ai/dsh@0.1.0-rc.8 ...` 命令;
174
174
  - **`dsh.exe` 无法识别**:当前目录没有该文件。请先进入 DSH-Portable 目录,或使用 Windows 单行助手;
175
175
  - **找不到 DSH-Portable**:进入它的目录后重新执行安装命令,或直接运行 `.\dsh.exe plugin --profile web add dsh-codex-subscription`;
176
- - **电脑上有多个 DSH**:单行助手会列出正在运行的 Portable,输入对应编号即可;交给 Agent 时让它进入目标目录,或明确传入 `-DshPath`;
176
+ - **电脑上有多个 DSH**:单行助手会合并正在运行的副本与常用目录内检测到的 Portable,输入对应编号即可;交给 Agent 时让它进入目标目录,或明确传入 `-DshPath`;
177
177
  - **安装仍然失败**:把上面的 Agent 文档链接发给 Agent,不要删除 profile 或随意修改系统 PATH。
178
178
 
179
179
  ## 边界与支持
@@ -7,6 +7,7 @@ This project depends on software distributed under its own terms. The dependency
7
7
  | DeepSeek Harness packages | Plugin host, client slots, credential and LLM interfaces | MIT | https://github.com/deepseek-ai/deepseek-harness |
8
8
  | `@earendil-works/pi-ai` 0.82.1 | OpenAI Codex OAuth, model catalog, Responses transport, and WebSocket continuation | MIT | https://github.com/earendil-works/pi |
9
9
  | React | DSH settings component runtime | MIT | https://github.com/facebook/react |
10
+ | Heroicons | Composer speed icon | MIT | https://github.com/tailwindlabs/heroicons |
10
11
  | tsdown | Development-time bundler | MIT | https://github.com/rolldown/tsdown |
11
12
 
12
13
  No third-party project endorses this community plugin. See each installed package for its complete license text and transitive dependency notices.
package/lib/client.js CHANGED
@@ -4,9 +4,50 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
7
29
  let react = require("react");
30
+ react = __toESM(react, 1);
8
31
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
32
  let react_jsx_runtime = require("react/jsx-runtime");
33
+ //#region node_modules/.pnpm/@heroicons+react@2.2.0_react@18.3.1/node_modules/@heroicons/react/16/solid/esm/BoltIcon.js
34
+ function BoltIcon({ title, titleId, ...props }, svgRef) {
35
+ return /*#__PURE__*/ react.createElement("svg", Object.assign({
36
+ xmlns: "http://www.w3.org/2000/svg",
37
+ viewBox: "0 0 16 16",
38
+ fill: "currentColor",
39
+ "aria-hidden": "true",
40
+ "data-slot": "icon",
41
+ ref: svgRef,
42
+ "aria-labelledby": titleId
43
+ }, props), title ? /*#__PURE__*/ react.createElement("title", { id: titleId }, title) : null, /*#__PURE__*/ react.createElement("path", {
44
+ fillRule: "evenodd",
45
+ d: "M9.58 1.077a.75.75 0 0 1 .405.82L9.165 6h4.085a.75.75 0 0 1 .567 1.241l-6.5 7.5a.75.75 0 0 1-1.302-.638L6.835 10H2.75a.75.75 0 0 1-.567-1.241l6.5-7.5a.75.75 0 0 1 .897-.182Z",
46
+ clipRule: "evenodd"
47
+ }));
48
+ }
49
+ const ForwardRef = /*#__PURE__*/ react.forwardRef(BoltIcon);
50
+ //#endregion
10
51
  //#region src/settings-contract.js
11
52
  const SETTINGS_NAMESPACE = "codex-subscription";
12
53
  const QUICK_QUOTA_FIELD = "quickQuotaVisible";
@@ -46,7 +87,8 @@ window.__ModuleLoader__.load({
46
87
  "remote",
47
88
  "settingsScope",
48
89
  "modelDirectories",
49
- "conversation"
90
+ "conversation",
91
+ "sessions"
50
92
  ];
51
93
  const NS = "settings.codexSubscription";
52
94
  const CHANNEL = "/codex-subscription";
@@ -112,6 +154,17 @@ window.__ModuleLoader__.load({
112
154
  speedStandardHint: "标准速度",
113
155
  speedFast: "高速",
114
156
  speedFastHint: "1.5 倍,消耗更多 Credits",
157
+ modelMenuAria: "模型、推理等级与速度",
158
+ modelLabel: "模型",
159
+ effortLabel: "推理等级",
160
+ providerDefault: "Default",
161
+ selectModel: "选择模型",
162
+ modelsLoading: "正在读取模型…",
163
+ modelsEmpty: "没有可用模型。",
164
+ effortsEmpty: "当前模型未提供推理等级。",
165
+ modelRetry: "重试",
166
+ modelFailed: "模型目录加载失败:{value}",
167
+ groupFailed: "{name}:{value}",
115
168
  imageGenerate: "生成图片",
116
169
  imageGenerating: "正在生成…",
117
170
  imageGenerated: "已生成",
@@ -184,6 +237,17 @@ window.__ModuleLoader__.load({
184
237
  speedStandardHint: "Standard speed",
185
238
  speedFast: "Fast",
186
239
  speedFastHint: "1.5x; higher Credits use",
240
+ modelMenuAria: "Model, effort, and speed",
241
+ modelLabel: "Model",
242
+ effortLabel: "Effort",
243
+ providerDefault: "Default",
244
+ selectModel: "Select model",
245
+ modelsLoading: "Loading models…",
246
+ modelsEmpty: "No models available.",
247
+ effortsEmpty: "This model provides no reasoning effort levels.",
248
+ modelRetry: "Retry",
249
+ modelFailed: "Could not load models: {value}",
250
+ groupFailed: "{name}: {value}",
187
251
  imageGenerate: "Generate image",
188
252
  imageGenerating: "Generating…",
189
253
  imageGenerated: "Generated",
@@ -222,8 +286,10 @@ window.__ModuleLoader__.load({
222
286
  .codexSubscriptionCreditSection{display:flex;flex-direction:column;gap:7px}.codexSubscriptionCreditNote{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}.codexSubscriptionCreditRows{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}.codexSubscriptionCreditBalance,.codexSubscriptionSpendLimit{min-width:0;border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-module-platform)}
223
287
  .codexSubscriptionCreditBalance{display:flex;flex-direction:column;gap:6px}.codexSubscriptionCreditBalance span,.codexSubscriptionCreditLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCreditBalance strong{font:600 18px/24px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
224
288
  .codexSubscriptionSpendLimit{display:flex;flex-direction:column;gap:8px}.codexSubscriptionSpendTop{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexSubscriptionSpendTop strong{font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}.codexSubscriptionSpendLimit progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-border-l3);accent-color:var(--dsw-alias-brand-primary,#3964fe);-webkit-appearance:none;appearance:none}.codexSubscriptionSpendLimit progress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.codexSubscriptionSpendLimit progress::-webkit-progress-value{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}.codexSubscriptionSpendLimit progress::-moz-progress-bar{background:var(--dsw-alias-brand-primary,#3964fe);border-radius:999px}
225
- .codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;margin-right:-4px;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
226
- .codexComposerControls{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto}.codexComposerSpeed{display:inline-flex;align-items:center;justify-content:center;min-width:28px;height:28px;box-sizing:border-box;padding:0 7px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);font:500 12px/20px inherit;cursor:pointer;white-space:nowrap}.codexComposerSpeed:hover,.codexComposerSpeed[aria-expanded=true]{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.codexComposerSpeed:disabled{cursor:not-allowed;opacity:.5}.codexComposerSpeedFast{font-size:14px}.codexComposerSpeedChoice{display:flex;min-width:150px;flex-direction:column;gap:1px}.codexComposerSpeedChoice strong{font-size:13px;line-height:18px;font-weight:500}.codexComposerSpeedChoice span{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
289
+ .codexComposerQuota{display:inline-flex;align-items:center;flex:0 0 auto;height:28px;box-sizing:border-box;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
290
+ .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)}
291
+ .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}
292
+ .codexModelSelectMenu{overflow:visible}
227
293
  .codexImageTool{display:flex;flex-direction:column;gap:8px;margin:4px 0;color:var(--dsw-alias-label-primary)}.codexImageToolRow{display:flex;align-items:center;min-height:24px;gap:8px;font-size:13px;line-height:20px}.codexImageToolIcon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dsw-alias-label-secondary)}.codexImageToolIcon::before{content:'';width:8px;height:8px;border:1.5px solid currentColor;border-radius:3px}.codexImageTool[data-state=running] .codexImageToolIcon::before{border-radius:50%;border-right-color:transparent;animation:codexImageSpin 800ms linear infinite}.codexImageTool[data-state=error] .codexImageToolIcon::before{border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-primary)}.codexImageToolTitle{font-weight:500}.codexImageToolState{color:var(--dsw-alias-label-tertiary)}.codexImageToolError{margin:0 0 0 24px;font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary)}.codexImageToolGallery{margin-left:24px}.codexGeneratedImageFrame{display:flex;align-items:center;justify-content:center;width:min(240px,100%);min-height:120px;padding:0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);cursor:pointer}.codexGeneratedImageFrame img{display:block;width:100%;max-height:240px;object-fit:contain}.codexGeneratedImageRetry{min-height:36px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);cursor:pointer}.codexGeneratedImageLightbox{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:32px;border:0;background:rgba(0,0,0,.72)}.codexGeneratedImageLightbox img{display:block;max-width:min(1100px,calc(100vw - 64px));max-height:calc(100vh - 64px);object-fit:contain}.codexGeneratedImageClose{position:absolute;top:16px;right:16px;width:36px;height:36px;border:1px solid rgba(255,255,255,.35);border-radius:50%;background:rgba(0,0,0,.48);color:#fff;font-size:20px;line-height:1;cursor:pointer}@keyframes codexImageSpin{to{transform:rotate(360deg)}}
228
294
  @container (max-width:560px){.codexSubscriptionCreditRows{grid-template-columns:1fr}}
229
295
  @container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}.codexSubscriptionActions{width:100%}.codexSubscriptionSearchChoices{grid-template-columns:1fr}}
@@ -622,68 +688,288 @@ window.__ModuleLoader__.load({
622
688
  ]
623
689
  });
624
690
  }
625
- function CodexComposerControls({ preference, rpc, t, directory }) {
626
- const [speedOpen, setSpeedOpen] = (0, react.useState)(false);
691
+ function CodexComposerQuota({ preference, rpc, t, directory }) {
627
692
  const preferenceSnapshot = usePreferenceSnapshot(preference);
628
693
  const current = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot()).current;
629
694
  const codex = current?.provider === "openai-codex";
630
- const speedSupported = codex && supportsCodexFastMode(current?.model);
631
- const speedWritable = preferenceSnapshot.status === "ready" && preferenceSnapshot.writable === true;
632
- const fast = preferenceSnapshot.speedMode === SPEED_MODE_FAST;
633
695
  const quotaEnabled = preferenceSnapshot.status === "ready" && preferenceSnapshot.visible && codex;
634
696
  const quota = useQuickQuota(rpc, quotaEnabled, current?.model);
635
- if (!speedSupported && (!quotaEnabled || quota === void 0)) return null;
636
- const speedItems = [{
637
- id: SPEED_MODE_STANDARD,
638
- label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
639
- className: "codexComposerSpeedChoice",
640
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("speedStandard") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("speedStandardHint") })]
641
- })
642
- }, {
643
- id: SPEED_MODE_FAST,
644
- label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
645
- className: "codexComposerSpeedChoice",
646
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("speedFast") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("speedFastHint") })]
647
- })
648
- }];
649
- const speed = speedSupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
650
- open: speedOpen,
651
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
652
- className: `codexComposerSpeed${fast ? " codexComposerSpeedFast" : ""}`,
653
- type: "button",
654
- "aria-label": `${t("speedTitle")}: ${t(fast ? "speedFast" : "speedStandard")}`,
655
- "aria-expanded": speedOpen,
697
+ if (!quotaEnabled || quota === void 0) return null;
698
+ const value = percent(quota.remainingPercent);
699
+ const label = fill(t("quickQuotaStatus"), { value });
700
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
701
+ className: "codexComposerQuota",
702
+ role: "status",
703
+ "aria-label": label,
704
+ title: label,
705
+ children: [value, "%"]
706
+ });
707
+ }
708
+ function CodexModelSelect({ locked, available, directory, load, select, preference, t }) {
709
+ const state = (0, react.useSyncExternalStore)(directory.subscribe, directory.getSnapshot);
710
+ const preferenceSnapshot = usePreferenceSnapshot(preference);
711
+ const [open, setOpen] = (0, react.useState)(false);
712
+ const [pane, setPane] = (0, react.useState)("root");
713
+ const rootRef = (0, react.useRef)(null);
714
+ const triggerRef = (0, react.useRef)(null);
715
+ const id = (0, react.useId)();
716
+ const choices = (0, react.useMemo)(() => state.groups.flatMap((group) => group.models.map((model) => ({
717
+ group,
718
+ model,
719
+ selection: {
720
+ provider: group.id,
721
+ model: model.id,
722
+ ...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }
723
+ }
724
+ }))), [state.groups]);
725
+ const currentChoice = choices.find((choice) => choice.selection.provider === state.current?.provider && choice.selection.model === state.current?.model);
726
+ const reasoning = currentChoice?.model.reasoning;
727
+ const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
728
+ const effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t("providerDefault") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;
729
+ const effortChoices = (0, react.useMemo)(() => reasoning === void 0 ? [] : [...reasoning.defaultEffort === void 0 ? [{
730
+ key: "provider-default",
731
+ effort: void 0,
732
+ label: t("providerDefault")
733
+ }] : [], ...reasoning.efforts.map((effort) => ({
734
+ key: `effort:${effort.id}`,
735
+ effort: effort.id,
736
+ label: effort.name,
737
+ ...effort.description === void 0 ? {} : { description: effort.description }
738
+ }))], [reasoning, t]);
739
+ const modelLabel = currentChoice?.model.name ?? t("selectModel");
740
+ const speedSupported = state.current?.provider === "openai-codex" && supportsCodexFastMode(state.current?.model);
741
+ const speedWritable = preferenceSnapshot.status === "ready" && preferenceSnapshot.writable === true;
742
+ const fast = speedSupported && preferenceSnapshot.speedMode === "fast";
743
+ const busy = state.status === "selecting";
744
+ (0, react.useEffect)(() => {
745
+ if (available) load();
746
+ }, [available, load]);
747
+ (0, react.useEffect)(() => {
748
+ if (!open) return void 0;
749
+ const closeOutside = (event) => {
750
+ if (!rootRef.current?.contains(event.target)) {
751
+ setOpen(false);
752
+ setPane("root");
753
+ }
754
+ };
755
+ document.addEventListener("mousedown", closeOutside);
756
+ return () => document.removeEventListener("mousedown", closeOutside);
757
+ }, [open]);
758
+ (0, react.useEffect)(() => {
759
+ if (!speedSupported && pane === "speed") setPane("root");
760
+ }, [pane, speedSupported]);
761
+ if (!available) return null;
762
+ const close = (restoreFocus = false) => {
763
+ setOpen(false);
764
+ setPane("root");
765
+ if (restoreFocus) queueMicrotask(() => triggerRef.current?.focus());
766
+ };
767
+ const settleSelection = (accepted) => {
768
+ if (accepted) close(true);
769
+ };
770
+ const chooseModel = (selection) => {
771
+ if (state.current?.provider === selection.provider && state.current.model === selection.model) {
772
+ close(true);
773
+ return;
774
+ }
775
+ select(selection).then(settleSelection);
776
+ };
777
+ const chooseEffort = (effort) => {
778
+ if (state.current === null) return;
779
+ if (effectiveEffort === effort) {
780
+ close(true);
781
+ return;
782
+ }
783
+ select({
784
+ provider: state.current.provider,
785
+ model: state.current.model,
786
+ ...effort === void 0 ? {} : { reasoningEffort: effort }
787
+ }).then(settleSelection);
788
+ };
789
+ const chooseSpeed = (speedMode) => {
790
+ close(true);
791
+ preference.set({ [SPEED_MODE_FIELD]: speedMode });
792
+ };
793
+ const option = ({ key, label, description, selected, disabled, onClick }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
794
+ type: "button",
795
+ role: "menuitemradio",
796
+ "aria-checked": selected,
797
+ className: "codexModelSelectOption",
798
+ disabled,
799
+ onClick,
800
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
801
+ className: "codexModelSelectOptionCopy",
802
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
803
+ className: "codexModelSelectOptionName",
804
+ children: label
805
+ }), description === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
806
+ className: "codexModelSelectOptionDescription",
807
+ children: description
808
+ })]
809
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
810
+ className: "codexModelSelectCheck",
811
+ children: selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
812
+ })]
813
+ }, key);
814
+ const cell = (target, label, value) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
815
+ type: "button",
816
+ role: "menuitem",
817
+ className: "codexModelSelectCell",
818
+ "data-open": pane === target,
819
+ "aria-haspopup": "menu",
820
+ "aria-expanded": pane === target,
821
+ onClick: () => setPane((current) => current === target ? "root" : target),
822
+ children: [
823
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
824
+ className: "codexModelSelectCellLabel",
825
+ children: label
826
+ }),
827
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
828
+ className: "codexModelSelectCellValue",
829
+ children: value
830
+ }),
831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { className: "codexModelSelectCellChevron" })
832
+ ]
833
+ });
834
+ let submenu = null;
835
+ if (pane === "model") submenu = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
836
+ className: "codexModelSelectSubmenu",
837
+ role: "menu",
838
+ "aria-label": t("modelLabel"),
839
+ children: [
840
+ state.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
841
+ className: "codexModelSelectStatus",
842
+ children: t("modelsLoading")
843
+ }) : null,
844
+ state.error === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
845
+ className: "codexModelSelectError",
846
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("modelFailed"), { value: state.error }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
847
+ className: "codexModelSelectRetry",
848
+ type: "button",
849
+ onClick: load,
850
+ children: t("modelRetry")
851
+ })]
852
+ }),
853
+ state.failures.map((failure) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
854
+ className: "codexModelSelectWarning",
855
+ children: fill(t("groupFailed"), {
856
+ name: failure.name,
857
+ value: failure.message
858
+ })
859
+ }, failure.id)),
860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
861
+ className: "codexModelSelectGroups scrollable",
862
+ children: state.groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
863
+ className: "codexModelSelectGroup",
864
+ role: "group",
865
+ "aria-labelledby": `${id}-${group.id}`,
866
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
867
+ className: "codexModelSelectGroupTitle",
868
+ id: `${id}-${group.id}`,
869
+ children: group.name
870
+ }), group.models.map((model) => option({
871
+ key: model.id,
872
+ label: model.name,
873
+ description: model.description,
874
+ selected: state.current?.provider === group.id && state.current.model === model.id,
875
+ disabled: busy,
876
+ onClick: () => chooseModel({
877
+ provider: group.id,
878
+ model: model.id
879
+ })
880
+ }))]
881
+ }, group.id))
882
+ }),
883
+ state.status === "ready" && choices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
884
+ className: "codexModelSelectEmpty",
885
+ children: t("modelsEmpty")
886
+ }) : null
887
+ ]
888
+ });
889
+ else if (pane === "effort") submenu = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
890
+ className: "codexModelSelectSubmenu",
891
+ role: "menu",
892
+ "aria-label": t("effortLabel"),
893
+ children: effortChoices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
894
+ className: "codexModelSelectEmpty",
895
+ children: t("effortsEmpty")
896
+ }) : effortChoices.map((level) => option({
897
+ key: level.key,
898
+ label: level.label,
899
+ description: level.description,
900
+ selected: effectiveEffort === level.effort,
901
+ disabled: busy,
902
+ onClick: () => chooseEffort(level.effort)
903
+ }))
904
+ });
905
+ else if (pane === "speed") submenu = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
906
+ className: "codexModelSelectSubmenu",
907
+ role: "menu",
908
+ "aria-label": t("speedTitle"),
909
+ children: [option({
910
+ key: SPEED_MODE_STANDARD,
911
+ label: t("speedStandard"),
912
+ description: t("speedStandardHint"),
913
+ selected: !fast,
656
914
  disabled: !speedWritable,
657
- onClick: () => setSpeedOpen((value) => !value),
658
- children: fast ? "⚡" : "1×"
659
- }),
660
- items: speedItems,
661
- selectedId: preferenceSnapshot.speedMode,
662
- onSelect: (id) => {
663
- setSpeedOpen(false);
664
- preference.set({ [SPEED_MODE_FIELD]: id });
915
+ onClick: () => chooseSpeed(SPEED_MODE_STANDARD)
916
+ }), option({
917
+ key: SPEED_MODE_FAST,
918
+ label: t("speedFast"),
919
+ description: t("speedFastHint"),
920
+ selected: fast,
921
+ disabled: !speedWritable,
922
+ onClick: () => chooseSpeed(SPEED_MODE_FAST)
923
+ })]
924
+ });
925
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
926
+ className: "codexModelSelect",
927
+ ref: rootRef,
928
+ onKeyDown: (event) => {
929
+ if (event.key !== "Escape" || !open) return;
930
+ event.preventDefault();
931
+ if (pane === "root") close(true);
932
+ else setPane("root");
665
933
  },
666
- onClose: () => setSpeedOpen(false),
667
- align: "end",
668
- side: "top",
669
- portal: true,
670
- compact: true
671
- }) : null;
672
- let quotaView = null;
673
- if (quotaEnabled && quota !== void 0) {
674
- const value = percent(quota.remainingPercent);
675
- const label = fill(t("quickQuotaStatus"), { value });
676
- quotaView = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
677
- className: "codexComposerQuota",
678
- role: "status",
679
- "aria-label": label,
680
- title: label,
681
- children: [value, "%"]
682
- });
683
- }
684
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
685
- className: "codexComposerControls",
686
- children: [quotaView, speed]
934
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
935
+ ref: triggerRef,
936
+ type: "button",
937
+ className: "codexModelSelectTrigger",
938
+ "aria-label": modelLabel,
939
+ "aria-haspopup": "menu",
940
+ "aria-expanded": open,
941
+ "aria-controls": open ? `${id}-menu` : void 0,
942
+ title: modelLabel,
943
+ disabled: locked,
944
+ onClick: () => open ? close() : (setPane("root"), setOpen(true), load()),
945
+ children: [
946
+ fast && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ForwardRef, {
947
+ className: "codexModelSelectBolt",
948
+ "aria-hidden": "true"
949
+ }),
950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
951
+ className: "codexModelSelectLabel",
952
+ children: modelLabel
953
+ }),
954
+ effortLabel === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
955
+ className: "codexModelSelectEffort",
956
+ children: effortLabel
957
+ }),
958
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "codexModelSelectChevron" })
959
+ ]
960
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
961
+ className: "codexModelSelectMenu",
962
+ id: `${id}-menu`,
963
+ role: "menu",
964
+ "aria-label": t("modelMenuAria"),
965
+ "aria-busy": state.status === "loading" || busy,
966
+ children: [
967
+ cell("model", t("modelLabel"), modelLabel),
968
+ reasoning === void 0 ? null : cell("effort", t("effortLabel"), effortLabel),
969
+ speedSupported && cell("speed", t("speedTitle"), t(fast ? "speedFast" : "speedStandard")),
970
+ submenu
971
+ ]
972
+ }) : null]
687
973
  });
688
974
  }
689
975
  function AccountCard({ rpc, t, account, setAccount, onSignedOut }) {
@@ -1149,7 +1435,26 @@ window.__ModuleLoader__.load({
1149
1435
  t,
1150
1436
  directory: ctx.modelDirectories.directoryFor(sessionId).store
1151
1437
  })
1152
- }, CodexComposerControls));
1438
+ }, CodexComposerQuota));
1439
+ const sessions = ctx.get("sessions");
1440
+ ctx.slots.inject("conversation.input.model", () => ctx.slots.register({
1441
+ name: "conversation.input.model",
1442
+ priority: -10,
1443
+ locale: NS,
1444
+ inject: (sessionId) => {
1445
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
1446
+ const available = sessions.subagentAddress(sessionId) === void 0;
1447
+ return {
1448
+ available,
1449
+ directory: directory.store,
1450
+ load: () => {
1451
+ if (available) directory.load();
1452
+ },
1453
+ select: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false),
1454
+ preference
1455
+ };
1456
+ }
1457
+ }, CodexModelSelect));
1153
1458
  const conversation = ctx.get("conversation");
1154
1459
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1155
1460
  name: "tool.call.toolview",
package/lib/index.js CHANGED
@@ -494,7 +494,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
494
494
  streamSimple: (model, context, options) => provider.streamSimple(model, context, withSpeed(model, options))
495
495
  });
496
496
  }
497
- const USER_AGENT = `dsh-codex-subscription/1.1.2`;
497
+ const USER_AGENT = `dsh-codex-subscription/1.1.4`;
498
498
  //#endregion
499
499
  //#region src/codex-search.js
500
500
  const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "packageManager": "pnpm@11.19.0",
5
5
  "description": "ChatGPT and Codex subscriptions for DeepSeek Harness with OAuth login, image generation, quota, and subscription web search",
6
6
  "type": "module",
@@ -119,6 +119,7 @@
119
119
  "@deepseek-ai/dsh-web": "0.1.0-rc.8",
120
120
  "@deepseek-ai/schemastery": "3.18.1",
121
121
  "@earendil-works/pi-ai": "0.82.1",
122
+ "@heroicons/react": "2.2.0",
122
123
  "react": "18.3.1",
123
124
  "tsdown": "0.22.2"
124
125
  }