dsh-codex-subscription 1.11.2 → 1.11.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.11.2` package below; do not install a moving branch.
9
+ - Use the exact `1.11.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.
@@ -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.11.2
37
+ npx -y @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add dsh-codex-subscription@1.11.4
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.11.2
43
+ dsh plugin --profile web add dsh-codex-subscription@1.11.4
44
44
  ```
45
45
 
46
46
  Use the same `add` command to update or repair. This is the complete package-changing operation.
@@ -58,7 +58,7 @@ dsh --profile web --dump-config
58
58
 
59
59
  For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.1-rc.2` in place of `dsh`.
60
60
 
61
- 1. `dsh-codex-subscription` version `1.11.2` appears exactly once.
61
+ 1. `dsh-codex-subscription` version `1.11.4` 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
 
@@ -8,6 +8,7 @@
8
8
  "0.1.1-rc.2"
9
9
  ],
10
10
  "previews": [
11
- "0.1.2-alpha.2"
11
+ "0.1.2-alpha.2",
12
+ "0.1.2-alpha.3"
12
13
  ]
13
14
  }
package/lib/client.js CHANGED
@@ -329,6 +329,7 @@ window.__ModuleLoader__.load({
329
329
  quickQuotaBar: "进度条",
330
330
  quickQuotaForecast: "续航预测",
331
331
  quickQuotaBeta: "Beta",
332
+ quickQuotaForecastHint: "至少观察 30 分钟且额度变化 1% 后显示;重启 DSH 后重新校准。",
332
333
  contextTitle: "上下文窗口",
333
334
  contextStandard: "标准",
334
335
  contextStandardHint: "使用模型目录默认值;官方 Agent 预设会自动管理上下文。",
@@ -341,7 +342,16 @@ window.__ModuleLoader__.load({
341
342
  contextMaximum: "范围 128000–{value}",
342
343
  quickQuotaStatus: "Codex 剩余额度 {value}%",
343
344
  quickQuotaForecastStatus: "Codex 剩余额度 {value}%,按当前速度预计可用 {duration}",
345
+ quickQuotaForecastCalibrating: "校准中",
346
+ quickQuotaForecastCalibratingStatus: "Codex 剩余额度 {value}%,续航预测正在校准",
347
+ quickQuotaForecastIdle: "用量稳定",
348
+ quickQuotaForecastIdleStatus: "Codex 剩余额度 {value}%,当前没有可测量的消耗速度",
349
+ quickQuotaForecastUntilReset: "够用到重置",
350
+ quickQuotaForecastUntilResetStatus: "Codex 剩余额度 {value}%,按当前速度足够用到重置",
344
351
  quotaForecast: "按当前速度 {symbol}{duration}",
352
+ quotaForecastCalibrating: "续航正在校准",
353
+ quotaForecastIdle: "当前用量稳定",
354
+ quotaForecastUntilReset: "按当前速度足够用到重置",
345
355
  runwayDaysHours: "{days} 天 {hours} 小时",
346
356
  runwayDays: "{days} 天",
347
357
  runwayHours: "{hours} 小时",
@@ -495,6 +505,7 @@ window.__ModuleLoader__.load({
495
505
  quickQuotaBar: "Progress bar",
496
506
  quickQuotaForecast: "Runway",
497
507
  quickQuotaBeta: "Beta",
508
+ quickQuotaForecastHint: "Appears after at least 30 minutes and a 1% quota change; restarting DSH recalibrates it.",
498
509
  contextTitle: "Context window",
499
510
  contextStandard: "Standard",
500
511
  contextStandardHint: "Use the model catalog default; official agent presets manage context automatically.",
@@ -507,7 +518,16 @@ window.__ModuleLoader__.load({
507
518
  contextMaximum: "128000–{value}",
508
519
  quickQuotaStatus: "Codex quota: {value}% remaining",
509
520
  quickQuotaForecastStatus: "Codex quota: {value}% remaining; about {duration} at the current pace",
521
+ quickQuotaForecastCalibrating: "Calibrating",
522
+ quickQuotaForecastCalibratingStatus: "Codex quota: {value}% remaining; runway is calibrating",
523
+ quickQuotaForecastIdle: "Usage stable",
524
+ quickQuotaForecastIdleStatus: "Codex quota: {value}% remaining; no measurable consumption pace",
525
+ quickQuotaForecastUntilReset: "Enough until reset",
526
+ quickQuotaForecastUntilResetStatus: "Codex quota: {value}% remaining; enough until reset at the current pace",
510
527
  quotaForecast: "At current pace {symbol}{duration}",
528
+ quotaForecastCalibrating: "Runway calibrating",
529
+ quotaForecastIdle: "Usage currently stable",
530
+ quotaForecastUntilReset: "Enough until reset at current pace",
511
531
  runwayDaysHours: "{days}d {hours}h",
512
532
  runwayDays: "{days}d",
513
533
  runwayHours: "{hours}h",
@@ -576,7 +596,7 @@ window.__ModuleLoader__.load({
576
596
  .codexSubscriptionTag{border:1px solid var(--dsw-alias-border-l3);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary)}
577
597
  .codexSubscriptionNote{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
578
598
  .codexSubscriptionCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px;display:flex;flex-direction:column;gap:12px}
579
- .codexSubscriptionUsageCard{padding:12px 14px;gap:9px}.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}.codexSubscriptionPreference{min-height:32px;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}
599
+ .codexSubscriptionUsageCard{padding:12px 14px;gap:9px}.codexSubscriptionPreferencesCard{padding:12px 14px;gap:10px}.codexSubscriptionPreference{min-height:32px;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}.codexSubscriptionPreferenceCopy{display:flex;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionPreferenceLabel{display:flex;align-items:center;gap:6px}.codexSubscriptionPreferenceHint{max-width:300px;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
580
600
  .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;white-space:nowrap}.codexSubscriptionQuotaMode small{margin-left:3px;font-size:9px;line-height:1;color:var(--dsw-alias-label-tertiary)}.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}
581
601
  .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}
582
602
  .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)}
@@ -1212,6 +1232,17 @@ window.__ModuleLoader__.load({
1212
1232
  if (hours > 0) return fill(t("runwayHours"), { hours });
1213
1233
  return fill(t("runwayMinutes"), { minutes });
1214
1234
  };
1235
+ const formatQuotaForecast = (forecast, t) => {
1236
+ if (forecast?.status === "calibrating") return t("quotaForecastCalibrating");
1237
+ if (forecast?.status === "idle") return t("quotaForecastIdle");
1238
+ if (forecast?.status !== "ready") return void 0;
1239
+ if (forecast.survivesReset) return t("quotaForecastUntilReset");
1240
+ const duration = formatRunway(forecast.runwaySeconds, t);
1241
+ return duration === void 0 ? void 0 : fill(t("quotaForecast"), {
1242
+ symbol: "≈",
1243
+ duration
1244
+ });
1245
+ };
1215
1246
  function useQuickQuota(rpc, enabled, model) {
1216
1247
  const [quota, setQuota] = (0, react.useState)();
1217
1248
  (0, react.useEffect)(() => {
@@ -1274,12 +1305,15 @@ window.__ModuleLoader__.load({
1274
1305
  });
1275
1306
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1276
1307
  className: "codexSubscriptionPreference",
1277
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1308
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1278
1309
  className: "codexSubscriptionPreferenceCopy",
1279
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1310
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1280
1311
  className: "codexSubscriptionPreferenceLabel",
1281
1312
  children: t("quickQuotaSetting")
1282
- })
1313
+ }), snapshot.quickQuotaMode === "forecast" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1314
+ className: "codexSubscriptionPreferenceHint",
1315
+ children: t("quickQuotaForecastHint")
1316
+ }) : null]
1283
1317
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1284
1318
  className: "codexSubscriptionQuotaModes",
1285
1319
  role: "radiogroup",
@@ -1502,8 +1536,9 @@ window.__ModuleLoader__.load({
1502
1536
  if (!quotaEnabled || quota === void 0) return null;
1503
1537
  const value = Math.round(Number(quota.remainingPercent) * 10) / 10;
1504
1538
  const display = percent(value);
1505
- const duration = quota.forecast?.status === "ready" ? formatRunway(quota.forecast.runwaySeconds, t) : void 0;
1506
- const label = duration === void 0 ? fill(t("quickQuotaStatus"), { value: display }) : fill(t("quickQuotaForecastStatus"), {
1539
+ const forecast = forecastMode ? quota.forecast : void 0;
1540
+ const duration = forecast?.status === "ready" && !forecast.survivesReset ? formatRunway(forecast.runwaySeconds, t) : void 0;
1541
+ const label = forecast?.status === "calibrating" ? fill(t("quickQuotaForecastCalibratingStatus"), { value: display }) : forecast?.status === "idle" ? fill(t("quickQuotaForecastIdleStatus"), { value: display }) : forecast?.status === "ready" && forecast.survivesReset ? fill(t("quickQuotaForecastUntilResetStatus"), { value: display }) : duration === void 0 ? fill(t("quickQuotaStatus"), { value: display }) : fill(t("quickQuotaForecastStatus"), {
1507
1542
  value: display,
1508
1543
  duration
1509
1544
  });
@@ -1517,7 +1552,7 @@ window.__ModuleLoader__.load({
1517
1552
  max: 100,
1518
1553
  value,
1519
1554
  "aria-hidden": "true"
1520
- }) : forecastMode && duration !== void 0 ? `${display}% · ${quota.forecast.survivesReset ? "" : "≈"}${duration}` : `${display}%`
1555
+ }) : forecast?.status === "calibrating" ? `${display}% · ${t("quickQuotaForecastCalibrating")}` : forecast?.status === "idle" ? `${display}% · ${t("quickQuotaForecastIdle")}` : forecast?.status === "ready" && forecast.survivesReset ? `${display}% · ${t("quickQuotaForecastUntilReset")}` : forecastMode && duration !== void 0 ? `${display}% · ≈${duration}` : `${display}%`
1521
1556
  });
1522
1557
  }
1523
1558
  function CodexModelSelect({ locked, available, directory, load, select, preference, t }) {
@@ -2424,10 +2459,7 @@ window.__ModuleLoader__.load({
2424
2459
  }),
2425
2460
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2426
2461
  className: "codexSubscriptionLimitMeta",
2427
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.forecast?.status === "ready" ? fill(t("quotaForecast"), {
2428
- symbol: window.forecast.survivesReset ? "≥" : "≈",
2429
- duration: formatRunway(window.forecast.runwaySeconds, t)
2430
- }) : windowLabel(window.windowSeconds, t) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetTime, {
2462
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatQuotaForecast(window.forecast, t) ?? windowLabel(window.windowSeconds, t) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetTime, {
2431
2463
  resetsAt: window.resetsAt,
2432
2464
  t
2433
2465
  })]
package/lib/index.js CHANGED
@@ -44,11 +44,14 @@ function parseOAuthCredential(value) {
44
44
  */
45
45
  var DshOAuthCredentialStore = class {
46
46
  #chains = /* @__PURE__ */ new Map();
47
- constructor(credentials, ref, legacyRefs = []) {
47
+ constructor(credentials, ref, legacyRefs = [], options = {}) {
48
48
  if (credentials === void 0 || credentials === null) throw new Error("Codex OAuth requires the DSH credentials service");
49
+ const expirySkewMs = options.expirySkewMs ?? 0;
50
+ if (!Number.isFinite(expirySkewMs) || expirySkewMs < 0) throw new Error("Codex OAuth expiry skew must be a non-negative finite number");
49
51
  this.credentials = credentials;
50
52
  this.ref = ref;
51
53
  this.legacyRefs = Object.freeze([...legacyRefs]);
54
+ this.expirySkewMs = expirySkewMs;
52
55
  }
53
56
  #enqueue(providerId, operation, options) {
54
57
  assertProvider(providerId);
@@ -78,7 +81,11 @@ var DshOAuthCredentialStore = class {
78
81
  }
79
82
  abortIfNeeded(options);
80
83
  if (hit?.value === void 0 || hit.value === "") return void 0;
81
- return parseOAuthCredential(hit.value);
84
+ const credential = parseOAuthCredential(hit.value);
85
+ return this.expirySkewMs === 0 ? credential : {
86
+ ...credential,
87
+ expires: credential.expires - this.expirySkewMs
88
+ };
82
89
  }
83
90
  read(providerId, options) {
84
91
  return this.#enqueue(providerId, () => this.#read(providerId, options), options);
@@ -935,7 +942,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
935
942
  }
936
943
  //#endregion
937
944
  //#region src/version.js
938
- const PACKAGE_VERSION = "1.11.2";
945
+ const PACKAGE_VERSION = "1.11.4";
939
946
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
940
947
  //#endregion
941
948
  //#region src/model-catalog.js
@@ -1836,6 +1843,8 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
1836
1843
  const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
1837
1844
  const DEFAULT_TTL_MS = 6e4;
1838
1845
  const DEFAULT_TIMEOUT_MS$1 = 15e3;
1846
+ const DEFAULT_FAILURE_TTL_MS = 5e3;
1847
+ const DEFAULT_MAX_RETRY_AFTER_MS = 5 * 6e4;
1839
1848
  const record$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1840
1849
  function windowOf(value) {
1841
1850
  if (value === void 0 || value === null) return void 0;
@@ -1951,6 +1960,15 @@ const requestSignal$1 = (signal, timeoutMs) => {
1951
1960
  const timeout = AbortSignal.timeout(timeoutMs);
1952
1961
  return signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
1953
1962
  };
1963
+ function retryAfterMs(response, now, maximum) {
1964
+ if (response.status !== 429) return void 0;
1965
+ const raw = response.headers?.get?.("retry-after")?.trim();
1966
+ if (!raw) return void 0;
1967
+ const seconds = Number(raw);
1968
+ const delay = Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : Date.parse(raw) - now();
1969
+ if (!Number.isFinite(delay) || delay < 0) return void 0;
1970
+ return Math.min(delay, maximum);
1971
+ }
1954
1972
  /**
1955
1973
  * Read quota through the same refreshable OAuth lifecycle used by model turns.
1956
1974
  * The browser receives only a parsed quota projection; bearer and account id
@@ -1963,7 +1981,10 @@ function createCodexUsageReader(options) {
1963
1981
  const now = options.now ?? Date.now;
1964
1982
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
1965
1983
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS$1;
1984
+ const failureTtlMs = options.failureTtlMs ?? DEFAULT_FAILURE_TTL_MS;
1985
+ const maxRetryAfterMs = options.maxRetryAfterMs ?? DEFAULT_MAX_RETRY_AFTER_MS;
1966
1986
  let cached;
1987
+ let failed;
1967
1988
  let inFlight;
1968
1989
  let generation = 0;
1969
1990
  const load = async (signal) => {
@@ -1984,7 +2005,11 @@ function createCodexUsageReader(options) {
1984
2005
  },
1985
2006
  signal: requestSignal$1(signal, timeoutMs)
1986
2007
  });
1987
- if (!response.ok) throw new Error(response.status === 401 || response.status === 403 ? "ChatGPT sign-in needs to be renewed" : `ChatGPT usage request failed (HTTP ${response.status})`);
2008
+ if (!response.ok) {
2009
+ const error = /* @__PURE__ */ new Error(response.status === 401 || response.status === 403 ? "ChatGPT sign-in needs to be renewed" : `ChatGPT usage request failed (HTTP ${response.status})`);
2010
+ Object.defineProperty(error, "retryAfterMs", { value: retryAfterMs(response, now, maxRetryAfterMs) });
2011
+ throw error;
2012
+ }
1988
2013
  let value;
1989
2014
  try {
1990
2015
  value = await response.json();
@@ -1998,12 +2023,25 @@ function createCodexUsageReader(options) {
1998
2023
  };
1999
2024
  return Object.freeze({
2000
2025
  read({ force = false, signal } = {}) {
2026
+ if (failed !== void 0 && now() < failed.retryAt) return Promise.reject(new Error(failed.message));
2001
2027
  if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return Promise.resolve(structuredClone(cached));
2002
2028
  if (inFlight !== void 0) return inFlight.then(structuredClone);
2003
2029
  const currentGeneration = generation;
2004
2030
  const current = load(signal).then((value) => {
2005
- if (generation === currentGeneration) cached = structuredClone(value);
2031
+ if (generation === currentGeneration) {
2032
+ cached = structuredClone(value);
2033
+ failed = void 0;
2034
+ }
2006
2035
  return structuredClone(value);
2036
+ }).catch((error) => {
2037
+ if (generation === currentGeneration && error?.name !== "AbortError") {
2038
+ const delay = Number.isFinite(error?.retryAfterMs) ? error.retryAfterMs : failureTtlMs;
2039
+ failed = {
2040
+ message: error instanceof Error ? error.message : "ChatGPT usage request failed",
2041
+ retryAt: now() + delay
2042
+ };
2043
+ }
2044
+ throw error;
2007
2045
  }).finally(() => {
2008
2046
  if (inFlight === current) inFlight = void 0;
2009
2047
  });
@@ -2013,6 +2051,7 @@ function createCodexUsageReader(options) {
2013
2051
  clear() {
2014
2052
  generation += 1;
2015
2053
  cached = void 0;
2054
+ failed = void 0;
2016
2055
  inFlight = void 0;
2017
2056
  }
2018
2057
  });
@@ -2404,6 +2443,7 @@ const inject = [
2404
2443
  "attachments"
2405
2444
  ];
2406
2445
  const PROVIDER = "openai-codex";
2446
+ const OAUTH_EXPIRY_SKEW_MS = 6e4;
2407
2447
  const CREDENTIAL_REF = credentialRef("OPENAI_CODEX_SUBSCRIPTION_OAUTH");
2408
2448
  const LEGACY_CREDENTIAL_REF = credentialRef("WSL043_OPENAI_CODEX_OAUTH");
2409
2449
  const CHANNEL = "/codex-subscription";
@@ -2616,7 +2656,7 @@ function apply(ctx) {
2616
2656
  const searchProvider = createSearchProviderSwitcher(ctx.loader);
2617
2657
  const network = createCodexNetworkTransport();
2618
2658
  const originalImages = new OriginalImageStore();
2619
- const store = new DshOAuthCredentialStore(ctx.credentials, CREDENTIAL_REF, [LEGACY_CREDENTIAL_REF]);
2659
+ const store = new DshOAuthCredentialStore(ctx.credentials, CREDENTIAL_REF, [LEGACY_CREDENTIAL_REF], { expirySkewMs: OAUTH_EXPIRY_SKEW_MS });
2620
2660
  const baseProvider = createOpenAICodexProvider();
2621
2661
  let resolveAuth = async () => void 0;
2622
2662
  const modelCatalog = createOfficialModelCatalog({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
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",
@@ -77,22 +77,22 @@
77
77
  },
78
78
  "peerDependencies": {
79
79
  "@deepseek-ai/cordis": "4.0.1 || 4.0.2",
80
- "@deepseek-ai/dsh-api-remotes": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
81
- "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
82
- "@deepseek-ai/dsh-client-locale": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
83
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
84
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
85
- "@deepseek-ai/dsh-client-ui-model-selection": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
86
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
87
- "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
88
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
89
- "@deepseek-ai/dsh-client-ui-tool": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
90
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
91
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
92
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
93
- "@deepseek-ai/dsh-settings": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
94
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
95
- "@deepseek-ai/dsh-web": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2",
80
+ "@deepseek-ai/dsh-api-remotes": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
81
+ "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
82
+ "@deepseek-ai/dsh-client-locale": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
83
+ "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
84
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
85
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
86
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
87
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
88
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
89
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
90
+ "@deepseek-ai/dsh-credentials": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
91
+ "@deepseek-ai/dsh-llm": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
92
+ "@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
93
+ "@deepseek-ai/dsh-settings": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
94
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
95
+ "@deepseek-ai/dsh-web": "0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-alpha.2 || 0.1.2-alpha.3",
96
96
  "@deepseek-ai/schemastery": "3.18.1",
97
97
  "@earendil-works/pi-ai": "0.82.1",
98
98
  "react": "^18.2.0",