dsh-codex-subscription 2.6.3 → 2.6.5

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
@@ -106,7 +106,7 @@ This plugin supports the latest DeepSeek Harness release recorded in its package
106
106
  2. Paste this package name, with its version, into the **Package name or address** field:
107
107
 
108
108
  ```text
109
- dsh-codex-subscription@2.6.3
109
+ dsh-codex-subscription@2.6.5
110
110
  ```
111
111
 
112
112
  3. Click **Install** and wait for completion. Follow the page instructions; save your work before restarting if requested.
@@ -118,7 +118,7 @@ Why the version is included: DSH's plugin page only installs versions that are a
118
118
  <summary>Terminal installation (with an existing dsh command)</summary>
119
119
 
120
120
  ```sh
121
- dsh plugin --profile web add dsh-codex-subscription@2.6.3
121
+ dsh plugin --profile web add dsh-codex-subscription@2.6.5
122
122
  ```
123
123
 
124
124
  Follow the restart instructions, then sign in under **Settings → Codex**. Both the Plugins page and the terminal use DSH's installation management.
@@ -131,7 +131,7 @@ Follow the restart instructions, then sign in under **Settings → Codex**. Both
131
131
  After signing in and selecting a Codex model in Web, install the same plugin in the Headless profile:
132
132
 
133
133
  ```sh
134
- dsh plugin --profile headless add dsh-codex-subscription@2.6.3
134
+ dsh plugin --profile headless add dsh-codex-subscription@2.6.5
135
135
  dsh --profile headless "Reply with only the word: ok"
136
136
  ```
137
137
 
@@ -266,7 +266,7 @@ Find this plugin on the DSH **Plugins** page and use its update or uninstall act
266
266
  <summary>Terminal commands</summary>
267
267
 
268
268
  ```sh
269
- dsh plugin --profile web update dsh-codex-subscription@2.6.3
269
+ dsh plugin --profile web update dsh-codex-subscription@2.6.5
270
270
  ```
271
271
 
272
272
  Run only when you want to uninstall:
@@ -282,7 +282,7 @@ dsh plugin --profile web remove dsh-codex-subscription
282
282
  <details>
283
283
  <summary>DSH 0.2.0-rc.2 reports the plugin as incompatible?</summary>
284
284
 
285
- DSH 0.2.0-rc.2 did not exist when any release before 2.2.10 was published, and a published package's compatibility declaration cannot be changed. Install 2.2.10 or later (a bare package name can resolve to an older release during the first 24 hours, so enter `dsh-codex-subscription@2.2.10`) from the **Plugins** page, or run `dsh plugin --profile web update dsh-codex-subscription@2.6.3`, then restart DSH.
285
+ DSH 0.2.0-rc.2 did not exist when any release before 2.2.10 was published, and a published package's compatibility declaration cannot be changed. Install 2.2.10 or later (a bare package name can resolve to an older release during the first 24 hours, so enter `dsh-codex-subscription@2.2.10`) from the **Plugins** page, or run `dsh plugin --profile web update dsh-codex-subscription@2.6.5`, then restart DSH.
286
286
 
287
287
  </details>
288
288
 
package/README.md CHANGED
@@ -106,7 +106,7 @@
106
106
  2. 在 **包名或地址** 输入框中粘贴下面的包名(已带版本号):
107
107
 
108
108
  ```text
109
- dsh-codex-subscription@2.6.3
109
+ dsh-codex-subscription@2.6.5
110
110
  ```
111
111
 
112
112
  3. 点击 **安装**,等待安装完成;按页面提示操作,需要重启时先保存工作。
@@ -118,7 +118,7 @@
118
118
  <summary>终端安装(已能运行 dsh 命令)</summary>
119
119
 
120
120
  ```sh
121
- dsh plugin --profile web add dsh-codex-subscription@2.6.3
121
+ dsh plugin --profile web add dsh-codex-subscription@2.6.5
122
122
  ```
123
123
 
124
124
  安装完成后按提示重启 DSH,再到 **设置 → Codex 订阅** 登录。插件页面和终端均由 DSH 管理安装。
@@ -131,7 +131,7 @@ dsh plugin --profile web add dsh-codex-subscription@2.6.3
131
131
  先在 Web 中完成登录并选择一次 Codex 模型,再把同一个插件安装到 Headless profile:
132
132
 
133
133
  ```sh
134
- dsh plugin --profile headless add dsh-codex-subscription@2.6.3
134
+ dsh plugin --profile headless add dsh-codex-subscription@2.6.5
135
135
  dsh --profile headless "只回复:ok"
136
136
  ```
137
137
 
@@ -295,7 +295,7 @@ DSH 实机设置截图。
295
295
  <summary>终端方式</summary>
296
296
 
297
297
  ```sh
298
- dsh plugin --profile web update dsh-codex-subscription@2.6.3
298
+ dsh plugin --profile web update dsh-codex-subscription@2.6.5
299
299
  ```
300
300
 
301
301
  仅在需要卸载时运行:
@@ -311,7 +311,7 @@ dsh plugin --profile web remove dsh-codex-subscription
311
311
  <details>
312
312
  <summary>DSH 0.2.0-rc.2 提示插件不兼容?</summary>
313
313
 
314
- 2.2.10 之前的所有版本发布时 DSH 0.2.0-rc.2 还不存在,其兼容声明无法事后修改。请安装 2.2.10 或更高版本(只填包名时,发布满 24 小时前可能装到旧版,请直接填 `dsh-codex-subscription@2.2.10`):在 **插件** 页面更新,或运行 `dsh plugin --profile web update dsh-codex-subscription@2.6.3`,然后重启 DSH。
314
+ 2.2.10 之前的所有版本发布时 DSH 0.2.0-rc.2 还不存在,其兼容声明无法事后修改。请安装 2.2.10 或更高版本(只填包名时,发布满 24 小时前可能装到旧版,请直接填 `dsh-codex-subscription@2.2.10`):在 **插件** 页面更新,或运行 `dsh plugin --profile web update dsh-codex-subscription@2.6.5`,然后重启 DSH。
315
315
 
316
316
  </details>
317
317
 
package/lib/client.js CHANGED
@@ -5960,6 +5960,10 @@ window.__ModuleLoader__.load({
5960
5960
  deviceHint: "在登录页输入此设备代码:",
5961
5961
  waiting: "正在等待登录完成…",
5962
5962
  failed: "登录失败,请重试。",
5963
+ failed_account_claim: "登录已授权,但这个账号的令牌里没有 ChatGPT 账号 ID(常见于只用手机号注册的账号)。请改用邮箱账号登录,或先在 ChatGPT 里为账号绑定邮箱后重试。",
5964
+ failed_network: "登录时网络连接失败。请检查网络或代理后重试。",
5965
+ failed_token_exchange: "授权已完成,但换取令牌失败。请重试;反复出现时请检查网络或代理。",
5966
+ failed_callback: "没有收到浏览器的授权回调。请重试,或改用设备码登录。",
5963
5967
  accountRetry: "重试",
5964
5968
  accountRetrying: "正在重试账户状态…",
5965
5969
  accountCredentialUnavailable: "登录凭据暂时不可用。请重试;不会删除已保存的登录信息。",
@@ -6562,6 +6566,10 @@ window.__ModuleLoader__.load({
6562
6566
  deviceHint: "Enter this device code on the sign-in page:",
6563
6567
  waiting: "Waiting for sign-in to finish…",
6564
6568
  failed: "Sign-in failed. Try again.",
6569
+ failed_account_claim: "Sign-in was authorized, but the token of this account has no ChatGPT account ID (common for accounts created with only a phone number). Sign in with an email account, or add an email to the account in ChatGPT first, then try again.",
6570
+ failed_network: "The network connection failed during sign-in. Check the network or proxy and try again.",
6571
+ failed_token_exchange: "Authorization finished, but exchanging it for a token failed. Try again; if it keeps happening, check the network or proxy.",
6572
+ failed_callback: "The browser did not send the authorization back. Try again, or use device code sign-in.",
6565
6573
  accountRetry: "Retry",
6566
6574
  accountRetrying: "Retrying account status…",
6567
6575
  accountCredentialUnavailable: "The saved sign-in credentials are temporarily unavailable. Retry; saved sign-in information will not be deleted.",
@@ -9145,7 +9153,7 @@ window.__ModuleLoader__.load({
9145
9153
  }
9146
9154
  //#endregion
9147
9155
  //#region src/version.js
9148
- const PACKAGE_VERSION = "2.6.3";
9156
+ const PACKAGE_VERSION = "2.6.5";
9149
9157
  //#endregion
9150
9158
  //#region src/client-recovery.js
9151
9159
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
@@ -11143,6 +11151,12 @@ window.__ModuleLoader__.load({
11143
11151
  }
11144
11152
  //#endregion
11145
11153
  //#region src/client-account.jsx
11154
+ const LOGIN_FAILURES = [
11155
+ "account-claim",
11156
+ "network",
11157
+ "token-exchange",
11158
+ "callback"
11159
+ ];
11146
11160
  function AccountEmail({ candidate, fallback, t, emailVisible, onClick }) {
11147
11161
  if (typeof candidate?.email !== "string" || candidate.email.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11148
11162
  title: t("emailUnavailable"),
@@ -11502,7 +11516,7 @@ window.__ModuleLoader__.load({
11502
11516
  flow?.phase === "failed" || error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
11503
11517
  className: "codexSubscriptionError",
11504
11518
  role: "alert",
11505
- children: error ?? t("failed")
11519
+ children: error ?? (LOGIN_FAILURES.includes(flow?.failure) ? t(`failed_${flow.failure.replace("-", "_")}`) : t("failed"))
11506
11520
  }) : null
11507
11521
  ]
11508
11522
  });
package/lib/index.js CHANGED
@@ -2080,7 +2080,7 @@ function inheritedOriginalImageRef(session, assetId) {
2080
2080
  }
2081
2081
  //#endregion
2082
2082
  //#region src/version.js
2083
- const PACKAGE_VERSION = "2.6.3";
2083
+ const PACKAGE_VERSION = "2.6.5";
2084
2084
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
2085
2085
  //#endregion
2086
2086
  //#region src/codex-images.js
@@ -3358,6 +3358,13 @@ const publicPrompt = (prompt) => ({
3358
3358
  message: String(prompt.message ?? ""),
3359
3359
  ...typeof prompt.placeholder === "string" ? { placeholder: prompt.placeholder } : {}
3360
3360
  });
3361
+ const PUBLIC_FAILURES = /* @__PURE__ */ new Set([
3362
+ "account-claim",
3363
+ "network",
3364
+ "token-exchange",
3365
+ "callback"
3366
+ ]);
3367
+ const publicFailure = (kind) => PUBLIC_FAILURES.has(kind) ? kind : void 0;
3361
3368
  function classifyLoginFailure(error) {
3362
3369
  const message = error instanceof Error ? error.message : "";
3363
3370
  if (/token exchange failed/iu.test(message)) return "token-exchange";
@@ -3517,7 +3524,8 @@ var CodexLoginCoordinator = class {
3517
3524
  method,
3518
3525
  phase: "failed",
3519
3526
  authenticated: false,
3520
- error: "Codex login failed"
3527
+ error: "Codex login failed",
3528
+ failure: publicFailure(classifyLoginFailure(error))
3521
3529
  };
3522
3530
  session.hostError = error;
3523
3531
  }).finally(publishReady);
@@ -5369,7 +5377,7 @@ function createWakeSignal() {
5369
5377
  };
5370
5378
  }
5371
5379
  function shortResetDelay(usage, nowMs, maxWaitMs, resetMarginMs) {
5372
- if (typeof usage?.rateLimitReachedType === "string" && /^(?:workspace_|.*credits_depleted)/u.test(usage.rateLimitReachedType)) return void 0;
5380
+ if (typeof usage?.rateLimitReachedType === "string" && /^workspace_[a-z_]*usage_limit_reached$/u.test(usage.rateLimitReachedType)) return void 0;
5373
5381
  let latestDelayMs;
5374
5382
  for (const limit of usage?.rateLimits ?? []) {
5375
5383
  if (limit?.id !== "codex") continue;
@@ -5433,6 +5441,29 @@ async function waitForResetOrWake(delayMs, signal, wait, wakeSignal, revision) {
5433
5441
  controller.abort();
5434
5442
  return result;
5435
5443
  }
5444
+ async function modelsReadyOrCancelled(modelsReady, signal) {
5445
+ if (signal?.aborted) return "cancelled";
5446
+ let onAbort;
5447
+ const cancelled = new Promise((resolve) => {
5448
+ onAbort = () => resolve("cancelled");
5449
+ signal?.addEventListener("abort", onAbort, { once: true });
5450
+ });
5451
+ const ready = Promise.resolve().then(modelsReady).then((value) => value === true, () => false);
5452
+ try {
5453
+ return await Promise.race([ready, cancelled]);
5454
+ } finally {
5455
+ signal?.removeEventListener("abort", onAbort);
5456
+ }
5457
+ }
5458
+ /**
5459
+ * Model readiness for a retry after an account switch. The refresh joins the
5460
+ * switch's own catalog reload and is not tied to the turn, so cancelling one
5461
+ * turn never aborts the reload other consumers share.
5462
+ */
5463
+ const catalogModelsReady = (modelCatalog) => async () => {
5464
+ await modelCatalog.refresh();
5465
+ return modelCatalog.getModels().length > 0;
5466
+ };
5436
5467
  /**
5437
5468
  * Recover a Codex subscription RATE_LIMIT only when the backend confirms an
5438
5469
  * exhausted short quota window whose reset is close enough to wait out.
@@ -5441,9 +5472,9 @@ async function waitForResetOrWake(delayMs, signal, wait, wakeSignal, revision) {
5441
5472
  * the host's standard model-retry surface event so the turn shows the reason,
5442
5473
  * countdown, and normal turn cancellation affordance while it is parked.
5443
5474
  */
5444
- function createCodexQuotaRetryHandler({ usageReader, provider = "openai-codex", enabled = () => true, now = Date.now, wait = cancellableDelay, maxWaitMs = DEFAULT_MAX_WAIT_MS, resetMarginMs = DEFAULT_RESET_MARGIN_MS } = {}) {
5475
+ function createCodexQuotaRetryHandler({ usageReader, provider = "openai-codex", enabled = () => true, now = Date.now, wait = cancellableDelay, modelsReady = () => true, maxWaitMs = DEFAULT_MAX_WAIT_MS, resetMarginMs = DEFAULT_RESET_MARGIN_MS } = {}) {
5445
5476
  if (usageReader === void 0 || typeof usageReader.read !== "function") throw new TypeError("quota retry requires a usage reader");
5446
- if (typeof enabled !== "function" || typeof now !== "function" || typeof wait !== "function") throw new TypeError("quota retry requires enablement, clock, and wait functions");
5477
+ if (typeof enabled !== "function" || typeof now !== "function" || typeof wait !== "function" || typeof modelsReady !== "function") throw new TypeError("quota retry requires enablement, clock, wait, and model readiness functions");
5447
5478
  if (!Number.isFinite(maxWaitMs) || maxWaitMs <= 0 || !Number.isFinite(resetMarginMs) || resetMarginMs < 0) throw new TypeError("quota retry requires a positive wait cap and non-negative reset margin");
5448
5479
  const wakeSignal = createWakeSignal();
5449
5480
  const clearCache = async () => {
@@ -5483,6 +5514,12 @@ function createCodexQuotaRetryHandler({ usageReader, provider = "openai-codex",
5483
5514
  const reason = await waitForResetOrWake(resetDelayMs + resetMarginMs, signal, wait, wakeSignal, wakeRevision);
5484
5515
  if (reason === "cancelled" || signal?.aborted) return void 0;
5485
5516
  if (!enabled()) return next();
5517
+ if (reason === "account") {
5518
+ const ready = await modelsReadyOrCancelled(modelsReady, signal);
5519
+ if (ready === "cancelled" || signal?.aborted) return void 0;
5520
+ if (!ready) return next();
5521
+ break;
5522
+ }
5486
5523
  if (reason !== "configuration") break;
5487
5524
  wakeRevision = wakeSignal.revision();
5488
5525
  resetDelayMs = shortResetDelay(usage, now(), maxWaitMs, resetMarginMs);
@@ -6763,7 +6800,8 @@ function apply(ctx, config = {}) {
6763
6800
  const activeQuotaRetries = /* @__PURE__ */ new Set();
6764
6801
  const quotaRetryHandler = createCodexQuotaRetryHandler({
6765
6802
  usageReader,
6766
- enabled: () => normalizeAutoQuotaRetry(settings.get()[AUTO_QUOTA_RETRY_FIELD])
6803
+ enabled: () => normalizeAutoQuotaRetry(settings.get()[AUTO_QUOTA_RETRY_FIELD]),
6804
+ modelsReady: catalogModelsReady(modelCatalog)
6767
6805
  });
6768
6806
  const disposeQuotaRetry = ctx.on("agent/request-error", (payload, next) => {
6769
6807
  if (quotaRetryLifetime.signal.aborted) return Promise.resolve(void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
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",