dsh-codex-subscription 2.2.8 → 2.2.9

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/README.en.md CHANGED
@@ -31,7 +31,7 @@ Subscription access, model controls, quota management, and image creation in one
31
31
  | Feature | What you can do | Availability / setup |
32
32
  | --- | --- | --- |
33
33
  | **Subscription sign-in** | Sign in to ChatGPT and use Codex subscription models without an API key | Normal chat needs no Codex CLI |
34
- | **Model catalog sync** | Read available models and reasoning levels from your account, with manual refresh; models marked for retirement show their retirement date next to the model name, without automatically switching your selection | New models appear when your account offers them |
34
+ | **Model catalog sync** | Read available models and reasoning levels from your account, with manual refresh; models marked for retirement show their retirement date next to the model name, without automatically switching your selection | New models appear when your account offers them; the account catalog is the only source, so signed out or when it cannot be read no models are shown and no bundled list stands in |
35
35
  | **Model list** | Show or hide individual models in the composer picker under Models & runtime | Display only; selected models and requests still work |
36
36
  | **Reasoning levels** | Choose supported reasoning effort in the composer | Levels vary by model |
37
37
  | **Fast mode** | Switch supported models between Standard and Fast, with a lightning indicator | Standard by default; Fast increases usage |
package/README.md CHANGED
@@ -31,7 +31,7 @@
31
31
  | 功能 | 你可以做什么 | 使用方式 / 条件 |
32
32
  | --- | --- | --- |
33
33
  | **订阅登录** | 直接登录 ChatGPT,使用 Codex 订阅模型,无需 API Key | 普通聊天无需 Codex CLI |
34
- | **模型目录同步** | 自动读取账号可用模型与推理档位,支持手动刷新;目录标注退役的模型会在模型名称旁显示退役日期,不会自动切换你的选择 | 新型号以账号目录开放情况为准 |
34
+ | **模型目录同步** | 自动读取账号可用模型与推理档位,支持手动刷新;目录标注退役的模型会在模型名称旁显示退役日期,不会自动切换你的选择 | 新型号以账号目录开放情况为准;模型只来自账号目录,未登录或读取失败时不显示任何模型,也不使用内置列表 |
35
35
  | **模型列表** | 在“模型与运行”中逐个显示或隐藏输入框中的模型 | 仅影响列表显示;已选模型和请求不受影响 |
36
36
  | **推理等级** | 在输入框选择模型支持的推理档位 | 档位随模型变化 |
37
37
  | **高速模式** | 在支持的模型上切换标准 / Fast,输入框显示闪电标识 | 默认标准;Fast 会增加用量消耗 |
package/lib/client.js CHANGED
@@ -4537,6 +4537,7 @@ window.__ModuleLoader__.load({
4537
4537
  //#region src/client-primitives.js
4538
4538
  const Input = _deepseek_ai_dsh_client_ui_primitives.Input;
4539
4539
  const Button = _deepseek_ai_dsh_client_ui_primitives.Button;
4540
+ const Switch = _deepseek_ai_dsh_client_ui_primitives.Switch;
4540
4541
  const useAnchoredPosition = _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition;
4541
4542
  const useDismissOnOutsidePointer = _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer;
4542
4543
  const IconCheckOutline16 = _deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16 ?? _deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular;
@@ -4659,7 +4660,7 @@ window.__ModuleLoader__.load({
4659
4660
  "diagnosticReason_inspection-timeout": "检查超时",
4660
4661
  "diagnosticSource_network": "网络请求(非完整回复)",
4661
4662
  "diagnosticReason_catalog-online": "在线目录已载入",
4662
- "diagnosticReason_catalog-fallback": "使用备用目录",
4663
+ "diagnosticReason_catalog-unavailable": "尚未读取账户目录",
4663
4664
  "diagnosticReason_storage-readable": "预测缓存可读",
4664
4665
  "diagnosticReason_tool-registered": "全局工具已注册",
4665
4666
  "diagnosticReason_tool-missing": "未找到全局工具",
@@ -4737,6 +4738,8 @@ window.__ModuleLoader__.load({
4737
4738
  compaction_cloud: "开启",
4738
4739
  compactionHint: "实验功能,默认关闭。长上下文由 Codex 压缩后继续请求,保留 DSH 历史与原生压缩。开启时该请求使用 SSE;历史改变后重新建立压缩状态。",
4739
4740
  advancedModelSearch: "模型与搜索",
4741
+ modelsSectionTitle: "模型",
4742
+ searchSectionTitle: "搜索",
4740
4743
  inputImageDetail: "输入图片精度",
4741
4744
  inputImageDetailHint: "控制发送给模型的图片精度,精度越高输入 token 用量越大。",
4742
4745
  inputImageDetail_auto: "自动",
@@ -4929,7 +4932,9 @@ window.__ModuleLoader__.load({
4929
4932
  searchDomainCount: "个域名",
4930
4933
  catalogRefresh: "刷新模型",
4931
4934
  catalogOnline: "已加载账户在线模型目录",
4932
- catalogFallback: "当前使用内置模型目录;登录后可刷新账户可用模型",
4935
+ catalogSignedOut: "登录后显示账户可用的模型。",
4936
+ catalogLoading: "正在读取账户模型目录…",
4937
+ catalogFailed: "无法读取账户模型目录,请检查网络后重试。",
4933
4938
  pickerModelsTitle: "输入框模型列表",
4934
4939
  pickerModelsHint: "只控制输入框中的模型列表;不会影响已选模型或模型请求。新模型默认显示。",
4935
4940
  searchMode: "订阅搜索模式",
@@ -5148,7 +5153,7 @@ window.__ModuleLoader__.load({
5148
5153
  providerDefault: "Default",
5149
5154
  selectModel: "选择模型",
5150
5155
  modelsLoading: "正在读取模型…",
5151
- modelsEmpty: "没有可用模型。",
5156
+ modelsEmpty: "没有可用模型;请先登录,并确认网络可用。",
5152
5157
  modelsHidden: "所有模型都已从列表中隐藏;可在设置中重新显示。",
5153
5158
  effortsEmpty: "当前模型未提供推理等级。",
5154
5159
  modelRetry: "重试",
@@ -5210,7 +5215,7 @@ window.__ModuleLoader__.load({
5210
5215
  "diagnosticReason_inspection-timeout": "Check timed out",
5211
5216
  "diagnosticSource_network": "Network request (not full reply)",
5212
5217
  "diagnosticReason_catalog-online": "Online catalog loaded",
5213
- "diagnosticReason_catalog-fallback": "Fallback catalog",
5218
+ "diagnosticReason_catalog-unavailable": "Account catalog not loaded",
5214
5219
  "diagnosticReason_storage-readable": "Forecast cache readable",
5215
5220
  "diagnosticReason_tool-registered": "Global tool registered",
5216
5221
  "diagnosticReason_tool-missing": "Global tool not found",
@@ -5288,6 +5293,8 @@ window.__ModuleLoader__.load({
5288
5293
  compaction_cloud: "On",
5289
5294
  compactionHint: "Experimental, off by default. Codex compacts long requests while DSH history and native compaction remain available. These requests use SSE; changed history invalidates previous checkpoints.",
5290
5295
  advancedModelSearch: "Models and search",
5296
+ modelsSectionTitle: "Models",
5297
+ searchSectionTitle: "Search",
5291
5298
  inputImageDetail: "Input image detail",
5292
5299
  inputImageDetailHint: "Controls the detail level of images sent to the model. Higher detail uses more input tokens.",
5293
5300
  inputImageDetail_auto: "Auto",
@@ -5480,7 +5487,9 @@ window.__ModuleLoader__.load({
5480
5487
  searchDomainCount: "domains",
5481
5488
  catalogRefresh: "Refresh models",
5482
5489
  catalogOnline: "Account model catalog loaded",
5483
- catalogFallback: "Using the built-in catalog. Sign in to refresh account models.",
5490
+ catalogSignedOut: "Sign in to show the models your account can use.",
5491
+ catalogLoading: "Reading the account model catalog…",
5492
+ catalogFailed: "Could not read the account model catalog. Check the network and try again.",
5484
5493
  pickerModelsTitle: "Models in picker",
5485
5494
  pickerModelsHint: "Only controls the composer model list. Selected models and requests still work. New models appear by default.",
5486
5495
  searchMode: "Subscription search mode",
@@ -5699,7 +5708,7 @@ window.__ModuleLoader__.load({
5699
5708
  providerDefault: "Default",
5700
5709
  selectModel: "Select model",
5701
5710
  modelsLoading: "Loading models…",
5702
- modelsEmpty: "No models available.",
5711
+ modelsEmpty: "No models available. Sign in and make sure the network is reachable.",
5703
5712
  modelsHidden: "All models are hidden from this list. Show one in settings.",
5704
5713
  effortsEmpty: "This model provides no reasoning effort levels.",
5705
5714
  modelRetry: "Retry",
@@ -5780,12 +5789,6 @@ window.__ModuleLoader__.load({
5780
5789
  .codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}
5781
5790
  .codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}
5782
5791
  .codexSubscriptionPreferenceHint{max-width:300px;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
5783
- .codexSubscriptionSwitch{position:relative;flex:none;width:36px;height:20px;padding:2px;border:0;border-radius:999px;background:var(--dsw-alias-bg-module-platform);cursor:pointer;transition:background 120ms var(--ds-ease-in-out)}
5784
- .codexSubscriptionSwitch[aria-checked=true]{background:var(--dsw-alias-label-primary)}
5785
- .codexSubscriptionSwitch:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
5786
- .codexSubscriptionSwitch:disabled{cursor:not-allowed;opacity:.5}
5787
- .codexSubscriptionSwitchKnob{display:block;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-bg-layer-1);box-shadow:0 1px 2px var(--dsw-alias-border-l3);transition:transform 120ms var(--ds-ease-in-out)}
5788
- .codexSubscriptionSwitch[aria-checked=true] .codexSubscriptionSwitchKnob{transform:translateX(16px)}
5789
5792
  .codexSubscriptionQuotaModes{display:flex;align-items:center;gap:3px;padding:2px;border-radius:9px;background:var(--dsw-alias-bg-module-platform)}
5790
5793
  .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;white-space:nowrap}
5791
5794
  .codexSubscriptionQuotaMode small{margin-left:3px;font-size:9px;line-height:1;color:var(--dsw-alias-label-tertiary)}
@@ -5794,7 +5797,8 @@ window.__ModuleLoader__.load({
5794
5797
  .codexSubscriptionQuotaMode:has(input:disabled){cursor:not-allowed;opacity:.5}
5795
5798
  .codexSubscriptionQuotaMode input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
5796
5799
  .codexSubscriptionContext{display:flex;flex-direction:column;gap:8px}
5797
- .codexSubscriptionContextHead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
5800
+ .codexSubscriptionPickerSetting{display:flex;flex-direction:column;gap:8px}
5801
+ .codexSubscriptionContextHead{font-size:13px;line-height:20px;display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
5798
5802
  .codexSubscriptionContextCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}
5799
5803
  .codexSubscriptionContextHint{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
5800
5804
  .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}
@@ -5858,6 +5862,10 @@ window.__ModuleLoader__.load({
5858
5862
  .codexSubscriptionLimitMeta{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
5859
5863
  .codexSubscriptionLimitPeriod{display:flex;gap:8px;flex-wrap:wrap}
5860
5864
  .codexSubscriptionSearchOptions{margin-top:10px;font-size:12px}
5865
+ .codexSubscriptionDivider+.codexSubscriptionDivider{display:none}
5866
+ .codexSubscriptionPickerModels{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}
5867
+ .codexSubscriptionPickerModel{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:36px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:13px}
5868
+ .codexSubscriptionPickerModel:last-child{border-bottom:0}
5861
5869
  .codexSubscriptionSearchOptions>summary{cursor:pointer;color:var(--dsw-alias-label-primary);padding:6px 0}
5862
5870
  .codexSubscriptionSearchOptions>summary>span{float:right;color:var(--dsw-alias-label-tertiary)}
5863
5871
  .codexSubscriptionSearchOptions[open]>summary{margin-bottom:8px}
@@ -7652,7 +7660,7 @@ window.__ModuleLoader__.load({
7652
7660
  }
7653
7661
  //#endregion
7654
7662
  //#region src/version.js
7655
- const PACKAGE_VERSION = "2.2.8";
7663
+ const PACKAGE_VERSION = "2.2.9";
7656
7664
  //#endregion
7657
7665
  //#region src/client-recovery.js
7658
7666
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
@@ -7921,8 +7929,9 @@ window.__ModuleLoader__.load({
7921
7929
  const hidden = new Set(preferenceSnapshot.disabledModels);
7922
7930
  return state.groups.map((group) => group.id === "openai-codex" ? {
7923
7931
  ...group,
7924
- models: group.models.filter((model) => !hidden.has(model.id))
7925
- } : group).filter((group) => group.models.length > 0);
7932
+ models: group.models.filter((model) => !hidden.has(model.id)),
7933
+ allHidden: group.models.length > 0 && group.models.every((model) => hidden.has(model.id))
7934
+ } : group).filter((group) => group.models.length > 0 || group.allHidden === true);
7926
7935
  }, [state.groups, preferenceSnapshot.disabledModels]);
7927
7936
  const currentChoice = choices.find((choice) => choice.selection.provider === state.current?.provider && choice.selection.model === state.current?.model);
7928
7937
  const reasoning = currentChoice?.model.reasoning;
@@ -8099,21 +8108,28 @@ window.__ModuleLoader__.load({
8099
8108
  className: "codexModelSelectGroup",
8100
8109
  role: "group",
8101
8110
  "aria-labelledby": `${id}-${group.id}`,
8102
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8103
- className: "codexModelSelectGroupTitle",
8104
- id: `${id}-${group.id}`,
8105
- children: group.name
8106
- }), group.models.map((model) => option({
8107
- key: model.id,
8108
- label: model.name,
8109
- description: model.description,
8110
- selected: state.current?.provider === group.id && state.current.model === model.id,
8111
- disabled: busy,
8112
- onClick: () => chooseModel({
8113
- provider: group.id,
8114
- model: model.id
8115
- })
8116
- }))]
8111
+ children: [
8112
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8113
+ className: "codexModelSelectGroupTitle",
8114
+ id: `${id}-${group.id}`,
8115
+ children: group.name
8116
+ }),
8117
+ group.allHidden === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8118
+ className: "codexModelSelectEmpty",
8119
+ children: t("modelsHidden")
8120
+ }) : null,
8121
+ group.models.map((model) => option({
8122
+ key: model.id,
8123
+ label: model.name,
8124
+ description: model.description,
8125
+ selected: state.current?.provider === group.id && state.current.model === model.id,
8126
+ disabled: busy,
8127
+ onClick: () => chooseModel({
8128
+ provider: group.id,
8129
+ model: model.id
8130
+ })
8131
+ }))
8132
+ ]
8117
8133
  }, group.id))
8118
8134
  }),
8119
8135
  state.status === "ready" && visibleGroups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -8706,6 +8722,21 @@ window.__ModuleLoader__.load({
8706
8722
  return next;
8707
8723
  }
8708
8724
  //#endregion
8725
+ //#region src/picker-draft.js
8726
+ const toggleModelId = (draft, modelId) => draft.includes(modelId) ? draft.filter((id) => id !== modelId) : [...draft, modelId];
8727
+ /**
8728
+ * What to do with the draft after a render.
8729
+ * - adopt: nothing pending, follow the saved value
8730
+ * - wait: a change is pending but a write is still in flight
8731
+ * - save: send the draft
8732
+ * - settled: the pending change already equals the saved value
8733
+ */
8734
+ function draftStep({ dirty, writable, draft, saved }) {
8735
+ if (!dirty) return "adopt";
8736
+ if (!writable) return "wait";
8737
+ return JSON.stringify(draft) === JSON.stringify(saved) ? "settled" : "save";
8738
+ }
8739
+ //#endregion
8709
8740
  //#region src/client-preferences.jsx
8710
8741
  function QuickQuotaPreference({ preference, t }) {
8711
8742
  const snapshot = usePreferenceSnapshot(preference);
@@ -8766,17 +8797,13 @@ window.__ModuleLoader__.load({
8766
8797
  className: "codexSubscriptionPreferenceHint",
8767
8798
  children: t("autoQuotaRetryHint")
8768
8799
  })]
8769
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8770
- className: "codexSubscriptionSwitch",
8771
- type: "button",
8772
- role: "switch",
8773
- "aria-label": t("autoQuotaRetry"),
8774
- "aria-checked": snapshot.autoQuotaRetry,
8800
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
8801
+ checked: snapshot.autoQuotaRetry,
8802
+ label: t("autoQuotaRetry"),
8775
8803
  disabled: !writable,
8776
- onClick: () => {
8777
- preference.set({ [AUTO_QUOTA_RETRY_FIELD]: !snapshot.autoQuotaRetry });
8778
- },
8779
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "codexSubscriptionSwitchKnob" })
8804
+ onChange: (next) => {
8805
+ preference.set({ [AUTO_QUOTA_RETRY_FIELD]: next });
8806
+ }
8780
8807
  })]
8781
8808
  });
8782
8809
  }
@@ -8898,47 +8925,77 @@ window.__ModuleLoader__.load({
8898
8925
  })]
8899
8926
  });
8900
8927
  }
8901
- /** Show every account model while saving which entries the composer picker hides. */
8928
+ /**
8929
+ * Show every account model while saving which entries the composer picker hides.
8930
+ * The controller ignores a write while another is saving, so quick toggles are kept
8931
+ * in a local draft and sent as one write once the previous one has settled.
8932
+ */
8902
8933
  function PickerModelsPreference({ preference, t }) {
8903
8934
  const snapshot = usePreferenceSnapshot(preference);
8904
- const writable = snapshot.status === "ready" && snapshot.writable === true;
8905
- const hidden = snapshot.disabledModels;
8935
+ const ready = snapshot.status === "ready" && (snapshot.writable === true || snapshot.saving === true);
8936
+ const saved = snapshot.disabledModels;
8937
+ const [draft, setDraft] = (0, react.useState)(saved);
8938
+ const [open, setOpen] = (0, react.useState)(false);
8939
+ const dirty = (0, react.useRef)(false);
8940
+ (0, react.useEffect)(() => {
8941
+ const step = draftStep({
8942
+ dirty: dirty.current,
8943
+ writable: snapshot.writable === true,
8944
+ draft,
8945
+ saved
8946
+ });
8947
+ if (step === "adopt") setDraft(saved);
8948
+ else if (step === "save" || step === "settled") dirty.current = false;
8949
+ if (step === "save") preference.set({ [DISABLED_MODELS_FIELD]: draft });
8950
+ }, [
8951
+ draft,
8952
+ saved.join(" "),
8953
+ snapshot.writable
8954
+ ]);
8906
8955
  const toggle = (modelId) => {
8907
- const next = hidden.includes(modelId) ? hidden.filter((id) => id !== modelId) : [...hidden, modelId];
8908
- preference.set({ [DISABLED_MODELS_FIELD]: next });
8956
+ dirty.current = true;
8957
+ setDraft((current) => toggleModelId(current, modelId));
8909
8958
  };
8959
+ const shown = snapshot.availableModels.filter((model) => !draft.includes(model.id)).length;
8960
+ if (snapshot.availableModels.length === 0) return null;
8910
8961
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8911
- className: "codexSubscriptionContext",
8962
+ className: "codexSubscriptionPickerSetting",
8912
8963
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8913
- className: "codexSubscriptionContextCopy",
8914
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8915
- className: "codexSubscriptionPreferenceLabel",
8916
- children: t("pickerModelsTitle")
8917
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8918
- className: "codexSubscriptionContextHint",
8919
- children: t("pickerModelsHint")
8964
+ className: "codexSubscriptionPreference",
8965
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8966
+ className: "codexSubscriptionPreferenceCopy",
8967
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8968
+ className: "codexSubscriptionPreferenceLabel",
8969
+ children: t("pickerModelsTitle")
8970
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8971
+ className: "codexSubscriptionPreferenceHint",
8972
+ children: t("pickerModelsHint")
8973
+ })]
8974
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8975
+ className: "codexSubscriptionContextTrigger",
8976
+ type: "button",
8977
+ "aria-expanded": open,
8978
+ onClick: () => setOpen((value) => !value),
8979
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
8980
+ shown,
8981
+ " / ",
8982
+ snapshot.availableModels.length
8983
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})]
8920
8984
  })]
8921
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8922
- className: "codexSubscriptionContextModels",
8985
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8986
+ className: "codexSubscriptionPickerModels",
8923
8987
  role: "group",
8924
8988
  "aria-label": t("pickerModelsTitle"),
8925
8989
  children: snapshot.availableModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8926
- className: "codexSubscriptionContextModel",
8927
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8928
- className: "codexSubscriptionContextModelCopy",
8929
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.name })
8930
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8931
- className: "codexSubscriptionSwitch",
8932
- type: "button",
8933
- role: "switch",
8934
- "aria-label": model.name,
8935
- "aria-checked": !hidden.includes(model.id),
8936
- disabled: !writable,
8937
- onClick: () => toggle(model.id),
8938
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "codexSubscriptionSwitchKnob" })
8990
+ className: "codexSubscriptionPickerModel",
8991
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
8992
+ checked: !draft.includes(model.id),
8993
+ label: model.name,
8994
+ disabled: !ready,
8995
+ onChange: () => toggle(model.id)
8939
8996
  })]
8940
8997
  }, model.id))
8941
- })]
8998
+ }) : null]
8942
8999
  });
8943
9000
  }
8944
9001
  function ContextWindowPreference({ preference, t }) {
@@ -8995,110 +9052,112 @@ window.__ModuleLoader__.load({
8995
9052
  }
8996
9053
  ];
8997
9054
  const selectedMode = contextModeItems.find((item) => item.id === snapshot.contextMode)?.label ?? t("contextStandard");
9055
+ if (snapshot.contextModels.length === 0) return null;
8998
9056
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8999
9057
  className: "codexSubscriptionContext",
9000
- children: [
9001
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9002
- className: "codexSubscriptionContextHead",
9003
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9004
- className: "codexSubscriptionContextCopy",
9005
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9006
- className: "codexSubscriptionPreferenceLabel",
9007
- children: t("contextTitle")
9008
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9009
- className: "codexSubscriptionContextHint",
9010
- children: hint
9011
- })]
9012
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Menu, {
9013
- open: menuOpen,
9014
- items: contextModeItems,
9015
- selectedId: snapshot.contextMode,
9016
- onSelect: (value) => {
9017
- setMenuOpen(false);
9018
- preference.set({ [CONTEXT_MODE_FIELD]: value });
9019
- },
9020
- onClose: () => setMenuOpen(false),
9021
- align: "end",
9022
- side: "bottom",
9023
- portal: true,
9024
- compact: true,
9025
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
9026
- className: "codexSubscriptionContextTrigger",
9027
- type: "button",
9028
- "aria-label": t("contextTitle"),
9029
- "aria-haspopup": "menu",
9030
- "aria-expanded": menuOpen,
9031
- disabled: !writable,
9032
- onClick: () => setMenuOpen((value) => !value),
9033
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})]
9034
- })
9035
- })]
9036
- }),
9037
- snapshot.contextMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9038
- className: "codexSubscriptionContextModels",
9039
- children: [modelRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9040
- className: "codexSubscriptionContextModel",
9041
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9042
- className: "codexSubscriptionContextModelCopy",
9043
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextMaximum"), {
9044
- minimum: String(Math.min(MIN_CUSTOM_CONTEXT_WINDOW, model.maximum)),
9045
- value: String(model.maximum)
9046
- }) })]
9047
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
9048
- "aria-label": `${model.label} ${t("contextTokens")}`,
9049
- className: "codexSubscriptionContextInput",
9050
- type: "number",
9051
- inputMode: "numeric",
9052
- min: Math.min(MIN_CUSTOM_CONTEXT_WINDOW, model.maximum),
9053
- max: model.maximum,
9054
- step: 1,
9055
- value: drafts[model.key] ?? "",
9056
- disabled: !writable,
9057
- onChange: (event) => {
9058
- const nextValue = event.currentTarget.value;
9059
- setDrafts((current) => ({
9060
- ...current,
9061
- [model.key]: nextValue
9062
- }));
9063
- },
9064
- onBlur: () => commit(model.key),
9065
- onKeyDown: (event) => {
9066
- if (event.key === "Enter") event.currentTarget.blur();
9067
- }
9068
- })]
9069
- }, model.key)), fixedRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9070
- className: "codexSubscriptionContextModel",
9071
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9072
- className: "codexSubscriptionContextModelCopy",
9073
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextFixed"), { value: formatContextWindow(model.maximum) }) })]
9074
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9075
- className: "codexSubscriptionContextHint",
9076
- children: formatContextWindow(model.maximum)
9077
- })]
9078
- }, model.key))]
9079
- }) : null,
9080
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9081
- className: "codexSubscriptionPreference",
9058
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9059
+ className: "codexSubscriptionContextHead",
9060
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9061
+ className: "codexSubscriptionContextCopy",
9082
9062
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9083
- className: "codexSubscriptionPreferenceHint",
9084
- children: t(snapshot.catalogStatus?.source === "online" ? "catalogOnline" : "catalogFallback")
9085
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9063
+ className: "codexSubscriptionPreferenceLabel",
9064
+ children: t("contextTitle")
9065
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9066
+ className: "codexSubscriptionContextHint",
9067
+ children: hint
9068
+ })]
9069
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Menu, {
9070
+ open: menuOpen,
9071
+ items: contextModeItems,
9072
+ selectedId: snapshot.contextMode,
9073
+ onSelect: (value) => {
9074
+ setMenuOpen(false);
9075
+ preference.set({ [CONTEXT_MODE_FIELD]: value });
9076
+ },
9077
+ onClose: () => setMenuOpen(false),
9078
+ align: "end",
9079
+ side: "bottom",
9080
+ portal: true,
9081
+ compact: true,
9082
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
9083
+ className: "codexSubscriptionContextTrigger",
9086
9084
  type: "button",
9087
- variant: "outline",
9088
- disabled: snapshot.modelsLoading,
9089
- "aria-busy": snapshot.modelsLoading,
9090
- onClick: () => {
9091
- preference.refreshModels();
9085
+ "aria-label": t("contextTitle"),
9086
+ "aria-haspopup": "menu",
9087
+ "aria-expanded": menuOpen,
9088
+ disabled: !writable,
9089
+ onClick: () => setMenuOpen((value) => !value),
9090
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})]
9091
+ })
9092
+ })]
9093
+ }), snapshot.contextMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9094
+ className: "codexSubscriptionContextModels",
9095
+ children: [modelRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9096
+ className: "codexSubscriptionContextModel",
9097
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9098
+ className: "codexSubscriptionContextModelCopy",
9099
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextMaximum"), {
9100
+ minimum: String(Math.min(MIN_CUSTOM_CONTEXT_WINDOW, model.maximum)),
9101
+ value: String(model.maximum)
9102
+ }) })]
9103
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
9104
+ "aria-label": `${model.label} ${t("contextTokens")}`,
9105
+ className: "codexSubscriptionContextInput",
9106
+ type: "number",
9107
+ inputMode: "numeric",
9108
+ min: Math.min(MIN_CUSTOM_CONTEXT_WINDOW, model.maximum),
9109
+ max: model.maximum,
9110
+ step: 1,
9111
+ value: drafts[model.key] ?? "",
9112
+ disabled: !writable,
9113
+ onChange: (event) => {
9114
+ const nextValue = event.currentTarget.value;
9115
+ setDrafts((current) => ({
9116
+ ...current,
9117
+ [model.key]: nextValue
9118
+ }));
9092
9119
  },
9093
- children: t(snapshot.modelsLoading ? "refreshing" : "catalogRefresh")
9120
+ onBlur: () => commit(model.key),
9121
+ onKeyDown: (event) => {
9122
+ if (event.key === "Enter") event.currentTarget.blur();
9123
+ }
9094
9124
  })]
9095
- }),
9096
- snapshot.modelError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9097
- className: "codexSubscriptionError",
9098
- role: "alert",
9099
- children: t("modelDirectoryFailed")
9100
- }) : null
9101
- ]
9125
+ }, model.key)), fixedRows.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9126
+ className: "codexSubscriptionContextModel",
9127
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9128
+ className: "codexSubscriptionContextModelCopy",
9129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: model.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fill(t("contextFixed"), { value: formatContextWindow(model.maximum) }) })]
9130
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9131
+ className: "codexSubscriptionContextHint",
9132
+ children: formatContextWindow(model.maximum)
9133
+ })]
9134
+ }, model.key))]
9135
+ }) : null]
9136
+ });
9137
+ }
9138
+ /** The account catalog is the only model list: say why it is empty, and offer one refresh. */
9139
+ function ModelCatalogStatus({ preference, t }) {
9140
+ const snapshot = usePreferenceSnapshot(preference);
9141
+ const online = snapshot.catalogStatus?.source === "online";
9142
+ const { refresh: catalogRefresh } = snapshot.catalogStatus ?? {};
9143
+ const failed = snapshot.modelError || catalogRefresh === "failed";
9144
+ const message = snapshot.modelsLoading || catalogRefresh === "refreshing" ? "catalogLoading" : failed && !online ? "catalogFailed" : online ? "catalogOnline" : "catalogSignedOut";
9145
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9146
+ className: "codexSubscriptionPreference",
9147
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9148
+ className: failed && !online ? "codexSubscriptionError" : "codexSubscriptionPreferenceHint",
9149
+ role: failed && !online ? "alert" : void 0,
9150
+ children: t(message)
9151
+ }), online || catalogRefresh !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9152
+ type: "button",
9153
+ variant: "outline",
9154
+ disabled: snapshot.modelsLoading,
9155
+ "aria-busy": snapshot.modelsLoading,
9156
+ onClick: () => {
9157
+ preference.refreshModels();
9158
+ },
9159
+ children: t(snapshot.modelsLoading ? "refreshing" : "catalogRefresh")
9160
+ }) : null]
9102
9161
  });
9103
9162
  }
9104
9163
  function PreferencesCard({ preference, rpc, t, section = "display" }) {
@@ -9108,10 +9167,10 @@ window.__ModuleLoader__.load({
9108
9167
  children: [section === "advanced" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9109
9168
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
9110
9169
  className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
9111
- "aria-label": t("advancedModelSearch"),
9170
+ "aria-label": t("modelsSectionTitle"),
9112
9171
  children: [
9113
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("advancedModelSearch") }),
9114
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchProviderPreference, {
9172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("modelsSectionTitle") }),
9173
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
9115
9174
  preference,
9116
9175
  t
9117
9176
  }),
@@ -9132,6 +9191,14 @@ window.__ModuleLoader__.load({
9132
9191
  })
9133
9192
  ]
9134
9193
  }),
9194
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
9195
+ className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
9196
+ "aria-label": t("searchSectionTitle"),
9197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("searchSectionTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchProviderPreference, {
9198
+ preference,
9199
+ t
9200
+ })]
9201
+ }),
9135
9202
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
9136
9203
  className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
9137
9204
  "aria-label": t("connectionTitle"),
package/lib/index.js CHANGED
@@ -2966,7 +2966,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
2966
2966
  }
2967
2967
  };
2968
2968
  };
2969
- const getModels = () => (catalog?.getModels(provider.getModels()) ?? provider.getModels()).map((model) => {
2969
+ const getModels = () => (catalog?.getModels() ?? provider.getModels()).map((model) => {
2970
2970
  const maximum = modelContextMaximum(model);
2971
2971
  const mode = resolveContextMode();
2972
2972
  if (model.id === "gpt-5.3-codex-spark" || !["extended", "custom"].includes(mode)) return model;
@@ -3046,7 +3046,7 @@ async function readSubscriptionCredentials(getAuth, readCredential, signal) {
3046
3046
  }
3047
3047
  //#endregion
3048
3048
  //#region src/version.js
3049
- const PACKAGE_VERSION = "2.2.8";
3049
+ const PACKAGE_VERSION = "2.2.9";
3050
3050
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
3051
3051
  //#endregion
3052
3052
  //#region src/model-catalog.js
@@ -3303,7 +3303,7 @@ function createOfficialModelCatalog(options = {}) {
3303
3303
  };
3304
3304
  return Object.freeze({
3305
3305
  refresh,
3306
- getModels: (fallback) => models ?? fallback.filter((model) => model.id !== "gpt-5.3-codex-spark"),
3306
+ getModels: () => models ?? [],
3307
3307
  metadata: (modelId) => metadata.get(modelId),
3308
3308
  revision: () => revision,
3309
3309
  capabilityGaps: () => [...metadata.values()].filter((model) => model.unsupported && /^[a-z][a-z0-9._-]{0,79}$/u.test(model.id)).slice(0, 20).map((model) => ({
@@ -3311,7 +3311,7 @@ function createOfficialModelCatalog(options = {}) {
3311
3311
  ...structuredClone(model.unsupported)
3312
3312
  })),
3313
3313
  status: () => ({
3314
- source: models === void 0 ? "fallback" : "online",
3314
+ source: models === void 0 ? "unavailable" : "online",
3315
3315
  refresh: refreshStatus
3316
3316
  }),
3317
3317
  clear() {
@@ -4293,7 +4293,7 @@ function capabilityCoverage({ account, preference, runtime, catalog, tools, stor
4293
4293
  reason = runtime.restartRequired ? "restart-required" : runtime.installed ? "runtime-present" : "runtime-missing";
4294
4294
  } else if (id === "models" && catalog) {
4295
4295
  readiness = catalog.source === "online" ? "pass" : "warn";
4296
- reason = catalog.source === "online" ? "catalog-online" : "catalog-fallback";
4296
+ reason = catalog.source === "online" ? "catalog-online" : "catalog-unavailable";
4297
4297
  } else if (id === "storage" && storage?.readable) {
4298
4298
  readiness = "pass";
4299
4299
  reason = "storage-readable";
@@ -4639,7 +4639,7 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
4639
4639
  ...collected.operations ? { operations: collected.operations } : {},
4640
4640
  ...collected.websocket ? { websocket: collected.websocket } : {},
4641
4641
  ...collected.compaction ? { compaction: collected.compaction } : {},
4642
- ...catalog && ["fallback", "online"].includes(catalog.source) && [
4642
+ ...catalog && ["unavailable", "online"].includes(catalog.source) && [
4643
4643
  "idle",
4644
4644
  "refreshing",
4645
4645
  "ok",
@@ -6072,7 +6072,7 @@ function apply(ctx, config = {}) {
6072
6072
  });
6073
6073
  const preferences = {
6074
6074
  status: () => {
6075
- const models = modelCatalog.getModels(baseProvider.getModels());
6075
+ const models = modelCatalog.getModels();
6076
6076
  return {
6077
6077
  [DISABLED_MODELS_FIELD]: normalizeDisabledModels(settings.get()[DISABLED_MODELS_FIELD]),
6078
6078
  [AUTO_QUOTA_RETRY_FIELD]: normalizeAutoQuotaRetry(settings.get()[AUTO_QUOTA_RETRY_FIELD]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "2.2.8",
3
+ "version": "2.2.9",
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",