@mars-sea/dsh-commandcode-provider 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -24,7 +24,7 @@ declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
24
24
  */
25
25
  declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
26
26
  /**
27
- * Models the official CLI's model table (`ZA` in command-code@1.31.0) marks
27
+ * Models the official CLI's model table (command-code@1.32.1) marks
28
28
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
29
29
  * think automatically, with Command Code driving the depth. This is the
30
30
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -32,8 +32,10 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
32
32
  * effort levels, and this snapshot is not surfaced in the picker's compact
33
33
  * description — it exists for programmatic consumers.
34
34
  *
35
- * Source: the command-code@1.31.0 bundled model table (dist/cli.mjs, the `ZA`
36
- * object), cross-checked with https://commandcode.ai/docs/reference/cli/models.
35
+ * Source: the command-code@1.32.1 bundled model table (dist/cli.mjs),
36
+ * cross-checked with https://commandcode.ai/docs/reference/cli/models.
37
+ * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
38
+ * selectable `['low', 'high', 'max']` efforts.)
37
39
  * Keep in sync via the dsh-commandcode-upstream skill.
38
40
  */
39
41
  declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
@@ -61,8 +63,9 @@ declare const PLAN_LABELS: Readonly<Record<string, string>>;
61
63
  */
62
64
  declare const PLAN_ORDER: Readonly<Record<string, number>>;
63
65
  /**
64
- * Comparator for the model picker: sort by plan tier (lowest first), then by
65
- * model name, then by id as a tiebreak. Models with no known plan sort last.
66
+ * Comparator for the model picker: free models first (zero credit cost, usable
67
+ * by every account), then by plan tier (lowest first), then by model name,
68
+ * then by id as a tiebreak. Models with no known plan sort last.
66
69
  */
67
70
  declare function compareByPlan(a: {
68
71
  id: string;
@@ -73,7 +76,8 @@ declare function compareByPlan(a: {
73
76
  }): number;
74
77
  /**
75
78
  * Subscription plan table, synced from the official CLI bundle's plan maps
76
- * (`Nn`/`$n` in command-code@1.31.0 `dist/cli.mjs`): subscription `planId`
79
+ * (`Nn`/`$n` in command-code@1.31.0 `dist/cli.mjs`, re-verified unchanged
80
+ * against 1.32.1 where they appear as `Zn`/`er`): subscription `planId`
77
81
  * prefix → display name and the plan's monthly credit total. This is the
78
82
  * account's own subscription (from `/alpha/billing/subscriptions`) — distinct
79
83
  * from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
@@ -150,10 +154,12 @@ declare const KNOWN_DEALS: Readonly<Record<string, KnownDeal>>;
150
154
  * Models with time-of-day (peak/off-peak) pricing, per the official pricing
151
155
  * page (`/docs/resources/pricing-limits`). Since 2026-08-16 16:00 UTC, DeepSeek
152
156
  * charges by the hour: peak hours are 01:00–04:00 and 06:00–10:00 UTC (7h/day,
153
- * full price); the other 17 hours are off-peak at half price. The picker shows
154
- * the *current* state as a compact label (`Peak`/`Half`) matching the English
155
- * noun style of the other markers (`Image`, `FREE`), so a developer can tell at
156
- * a glance whether calling the model right now is cheap or expensive.
157
+ * full price); the other 17 hours are off-peak at half price. The V4 Flash
158
+ * Vision (exp) variant (command-code@1.32.0) shares the V4 Flash windows and
159
+ * peak prices ($0.44/$1.32). The picker shows the *current* state as a compact
160
+ * label (`Peak`/`Half`) matching the English noun style of the other markers
161
+ * (`Image`, `FREE`), so a developer can tell at a glance whether calling the
162
+ * model right now is cheap or expensive.
157
163
  *
158
164
  * Keep in sync with the official pricing page when the model set or the peak
159
165
  * windows change (see the dsh-commandcode-upstream skill).
@@ -172,7 +178,7 @@ declare function peakPricingState(modelId: string, now?: number): 'peak' | 'off-
172
178
  * for models without time-of-day pricing.
173
179
  */
174
180
  declare function peakPricingLabel(modelId: string, now?: number): string | undefined;
175
- declare const COMMAND_CODE_CLI_VERSION = "1.31.0";
181
+ declare const COMMAND_CODE_CLI_VERSION = "1.32.1";
176
182
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
177
183
  declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
178
184
  declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -313,6 +319,12 @@ interface CommandCodePlan {
313
319
  /** Billing period end in millis; 0 when the endpoint did not report one. */
314
320
  currentPeriodEnd: number;
315
321
  }
322
+ /**
323
+ * Why every account endpoint failed at once (the report then carries no data
324
+ * at all, so the degraded per-endpoint view would hide the root cause behind
325
+ * a generic "partial data" note). Undefined for partial failures.
326
+ */
327
+ type UsageBlockReason = 'invalid-key' | 'service-unavailable' | 'network';
316
328
  /** Everything the usage endpoints report, fetched together. */
317
329
  interface CommandCodeUsageReport {
318
330
  account?: CommandCodeAccount;
@@ -321,6 +333,13 @@ interface CommandCodeUsageReport {
321
333
  plan?: CommandCodePlan;
322
334
  /** Endpoint failures degrade the report instead of failing it. */
323
335
  failures: string[];
336
+ /**
337
+ * The single reason every endpoint failed, when they all did: `invalid-key`
338
+ * (every call rejected with 401 — the stored key is wrong or expired),
339
+ * `service-unavailable` (every call answered 5xx), or `network` (no HTTP
340
+ * response at all). Undefined when any endpoint succeeded.
341
+ */
342
+ blocked?: UsageBlockReason;
324
343
  }
325
344
  declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {
326
345
  private readonly deps;
package/lib/index.js CHANGED
@@ -160,11 +160,11 @@ var CommandCodeAccountPool = class {
160
160
  const revived = selectActiveAccount(await this.resolvedAccounts(), this.deps.preferredId?.());
161
161
  if (revived !== void 0) return this.pick(revived);
162
162
  const latest = await this.resolvedAccounts();
163
- if (latest.filter((account) => account.state?.kind === "disabled").length === latest.length) throw new LlmError(`llm-commandcode: every configured Command Code account (${latest.length}) was rejected with 401 — check the stored API keys (Models page / settings) or the auth file`, "INVALID_CREDENTIAL");
163
+ if (latest.filter((account) => account.state?.kind === "disabled").length === latest.length) throw new LlmError(`llm-commandcode: every configured Command Code account (${latest.length}) was rejected with 401 — check the stored API keys (Models page / settings) or the auth file;已配置的 ${latest.length} 个 Command Code 账户密钥均被拒绝(401)——请在设置页检查存储的 API 密钥,或重新运行 command-code login`, "INVALID_CREDENTIAL");
164
164
  const resets = latest.map((account) => account.state).filter((state) => state !== void 0 && state.kind === "cooldown" && state.until > 0).map((state) => state.until);
165
165
  const earliest = resets.length > 0 ? Math.min(...resets) : 0;
166
166
  const wait = earliest > 0 ? Math.max(1e3, earliest - Date.now()) : 0;
167
- throw new LlmError(`llm-commandcode: all ${latest.length} Command Code account(s) have exhausted their usage window` + (earliest > 0 ? `; the earliest window resets at ${clockLabel(earliest)}` : "") + " — requests will succeed again after the reset (or add another account)", "RATE_LIMIT", wait > 0 && wait <= 9e5 ? { providerRetryAfterMs: wait } : void 0);
167
+ throw new LlmError(`llm-commandcode: all ${latest.length} Command Code account(s) have exhausted their usage window` + (earliest > 0 ? `; the earliest window resets at ${clockLabel(earliest)}` : "") + ` — requests will succeed again after the reset (or add another account);已用尽全部 ${latest.length} 个 Command Code 账户的用量窗口` + (earliest > 0 ? `,最早的重置时间为 ${clockLabel(earliest)}` : "") + "——窗口重置后请求会自动恢复(也可以添加更多账户)", "RATE_LIMIT", wait > 0 && wait <= 9e5 ? { providerRetryAfterMs: wait } : void 0);
168
168
  }
169
169
  /**
170
170
  * Record a rejection against one key. `rate-limit` (429) marks the key
@@ -214,7 +214,7 @@ var CommandCodeAccountPool = class {
214
214
  * and API key or subscription, and Command Code's terms apply.
215
215
  *
216
216
  * Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
217
- * re-verified against command-code@1.31.0 — endpoints, request shape, and
217
+ * re-verified against command-code@1.32.1 — endpoints, request shape, and
218
218
  * stream events unchanged):
219
219
  * POST {apiBase}/alpha/generate
220
220
  * body: { config, memory, taste, skills, params: { model, messages, tools,
@@ -281,6 +281,7 @@ const KNOWN_EFFORTS = {
281
281
  "max"
282
282
  ],
283
283
  "deepseek/deepseek-v4-flash": ["high", "max"],
284
+ "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"],
284
285
  "deepseek/deepseek-v4-pro": ["high", "max"],
285
286
  "google/gemini-3.1-flash-lite": [
286
287
  "low",
@@ -352,6 +353,11 @@ const KNOWN_EFFORTS = {
352
353
  "max"
353
354
  ],
354
355
  "sakana/fugu-ultra": ["high", "xhigh"],
356
+ "stealth/ox-alpha": [
357
+ "low",
358
+ "high",
359
+ "max"
360
+ ],
355
361
  "xai/grok-4.5": [
356
362
  "low",
357
363
  "medium",
@@ -399,6 +405,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
399
405
  "claude-opus-5",
400
406
  "claude-sonnet-4-6",
401
407
  "claude-sonnet-5",
408
+ "deepseek/deepseek-v4-flash-vision-exp",
402
409
  "google/gemini-3.1-flash-lite",
403
410
  "google/gemini-3.5-flash",
404
411
  "google/gemini-3.5-flash-lite",
@@ -428,7 +435,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
428
435
  "xiaomi/mimo-v2.5"
429
436
  ]);
430
437
  /**
431
- * Models the official CLI's model table (`ZA` in command-code@1.31.0) marks
438
+ * Models the official CLI's model table (command-code@1.32.1) marks
432
439
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
433
440
  * think automatically, with Command Code driving the depth. This is the
434
441
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -436,8 +443,10 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
436
443
  * effort levels, and this snapshot is not surfaced in the picker's compact
437
444
  * description — it exists for programmatic consumers.
438
445
  *
439
- * Source: the command-code@1.31.0 bundled model table (dist/cli.mjs, the `ZA`
440
- * object), cross-checked with https://commandcode.ai/docs/reference/cli/models.
446
+ * Source: the command-code@1.32.1 bundled model table (dist/cli.mjs),
447
+ * cross-checked with https://commandcode.ai/docs/reference/cli/models.
448
+ * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
449
+ * selectable `['low', 'high', 'max']` efforts.)
441
450
  * Keep in sync via the dsh-commandcode-upstream skill.
442
451
  */
443
452
  const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
@@ -459,8 +468,7 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
459
468
  "poolside/laguna-s-2.1-free",
460
469
  "meta/muse-spark-1.1",
461
470
  "meta/muse-spark-1.2",
462
- "meta/muse-spark-1.2-contributor",
463
- "stealth/ox-alpha"
471
+ "meta/muse-spark-1.2-contributor"
464
472
  ]);
465
473
  /**
466
474
  * The minimum subscription plan a model is included in, per the official plan
@@ -489,6 +497,7 @@ const KNOWN_PLANS = {
489
497
  "Qwen/Qwen3.8-27B": "go",
490
498
  "Qwen/Qwen3.8-Max": "go",
491
499
  "deepseek/deepseek-v4-flash": "go",
500
+ "deepseek/deepseek-v4-flash-vision-exp": "go",
492
501
  "deepseek/deepseek-v4-pro": "go",
493
502
  "gpt-5.6-luna": "go",
494
503
  "meta/muse-spark-1.2-contributor": "go",
@@ -556,10 +565,22 @@ const PLAN_ORDER = {
556
565
  max: 4
557
566
  };
558
567
  /**
559
- * Comparator for the model picker: sort by plan tier (lowest first), then by
560
- * model name, then by id as a tiebreak. Models with no known plan sort last.
568
+ * Whether a model is free (requests cost no credits), per the pricing page's
569
+ * deals (`KNOWN_DEALS` `free: true`). Free models lead the picker regardless
570
+ * of tier — they are usable by every account, so they are the best default
571
+ * candidates.
572
+ */
573
+ function isFreeModel(modelId) {
574
+ return KNOWN_DEALS[modelId]?.free === true;
575
+ }
576
+ /**
577
+ * Comparator for the model picker: free models first (zero credit cost, usable
578
+ * by every account), then by plan tier (lowest first), then by model name,
579
+ * then by id as a tiebreak. Models with no known plan sort last.
561
580
  */
562
581
  function compareByPlan(a, b) {
582
+ const freeDelta = Number(isFreeModel(b.id)) - Number(isFreeModel(a.id));
583
+ if (freeDelta !== 0) return freeDelta;
563
584
  const pa = PLAN_ORDER[KNOWN_PLANS[a.id] ?? ""] ?? Number.MAX_SAFE_INTEGER;
564
585
  const pb = PLAN_ORDER[KNOWN_PLANS[b.id] ?? ""] ?? Number.MAX_SAFE_INTEGER;
565
586
  if (pa !== pb) return pa - pb;
@@ -569,7 +590,8 @@ function compareByPlan(a, b) {
569
590
  }
570
591
  /**
571
592
  * Subscription plan table, synced from the official CLI bundle's plan maps
572
- * (`Nn`/`$n` in command-code@1.31.0 `dist/cli.mjs`): subscription `planId`
593
+ * (`Nn`/`$n` in command-code@1.31.0 `dist/cli.mjs`, re-verified unchanged
594
+ * against 1.32.1 where they appear as `Zn`/`er`): subscription `planId`
573
595
  * prefix → display name and the plan's monthly credit total. This is the
574
596
  * account's own subscription (from `/alpha/billing/subscriptions`) — distinct
575
597
  * from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
@@ -671,15 +693,21 @@ const KNOWN_DEALS = {
671
693
  * Models with time-of-day (peak/off-peak) pricing, per the official pricing
672
694
  * page (`/docs/resources/pricing-limits`). Since 2026-08-16 16:00 UTC, DeepSeek
673
695
  * charges by the hour: peak hours are 01:00–04:00 and 06:00–10:00 UTC (7h/day,
674
- * full price); the other 17 hours are off-peak at half price. The picker shows
675
- * the *current* state as a compact label (`Peak`/`Half`) matching the English
676
- * noun style of the other markers (`Image`, `FREE`), so a developer can tell at
677
- * a glance whether calling the model right now is cheap or expensive.
696
+ * full price); the other 17 hours are off-peak at half price. The V4 Flash
697
+ * Vision (exp) variant (command-code@1.32.0) shares the V4 Flash windows and
698
+ * peak prices ($0.44/$1.32). The picker shows the *current* state as a compact
699
+ * label (`Peak`/`Half`) matching the English noun style of the other markers
700
+ * (`Image`, `FREE`), so a developer can tell at a glance whether calling the
701
+ * model right now is cheap or expensive.
678
702
  *
679
703
  * Keep in sync with the official pricing page when the model set or the peak
680
704
  * windows change (see the dsh-commandcode-upstream skill).
681
705
  */
682
- const KNOWN_PEAK_PRICING = /* @__PURE__ */ new Set(["deepseek/deepseek-v4-pro", "deepseek/deepseek-v4-flash"]);
706
+ const KNOWN_PEAK_PRICING = /* @__PURE__ */ new Set([
707
+ "deepseek/deepseek-v4-pro",
708
+ "deepseek/deepseek-v4-flash",
709
+ "deepseek/deepseek-v4-flash-vision-exp"
710
+ ]);
683
711
  /** Peak hours (UTC, hour-of-day range end-exclusive): 01–03 and 06–09. */
684
712
  const PEAK_HOUR_RANGES = [[1, 4], [6, 10]];
685
713
  /**
@@ -703,7 +731,7 @@ function peakPricingLabel(modelId, now = Date.now()) {
703
731
  if (state === void 0) return void 0;
704
732
  return state === "peak" ? "Peak" : "Half";
705
733
  }
706
- const COMMAND_CODE_CLI_VERSION = "1.31.0";
734
+ const COMMAND_CODE_CLI_VERSION = "1.32.1";
707
735
  const DEFAULT_API_BASE = "https://api.commandcode.ai";
708
736
  const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
709
737
  const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -1009,6 +1037,8 @@ async function messagesToCC(messages, readImage) {
1009
1037
  }
1010
1038
  return out;
1011
1039
  }
1040
+ /** Account endpoints fetched by one `getUsage()` run (see the classification there). */
1041
+ const USAGE_ENDPOINT_COUNT = 4;
1012
1042
  var CommandCodeAdapter = class extends LlmAdapter {
1013
1043
  deps;
1014
1044
  catalog = [];
@@ -1206,6 +1236,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1206
1236
  const base = this.deps.options().apiBase;
1207
1237
  const headers = await this.accountHeaders(apiKey);
1208
1238
  const failures = [];
1239
+ const failedStatuses = [];
1209
1240
  const getJson = async (path) => {
1210
1241
  try {
1211
1242
  const response = await this.fetchImpl(`${base}${path}`, {
@@ -1214,12 +1245,14 @@ var CommandCodeAdapter = class extends LlmAdapter {
1214
1245
  });
1215
1246
  if (!response.ok) {
1216
1247
  failures.push(`${path}: HTTP ${response.status}`);
1248
+ failedStatuses.push(response.status);
1217
1249
  return;
1218
1250
  }
1219
1251
  const parsed = await response.json();
1220
1252
  return isRecord(parsed) ? parsed : void 0;
1221
1253
  } catch (error) {
1222
1254
  failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
1255
+ failedStatuses.push(void 0);
1223
1256
  return;
1224
1257
  }
1225
1258
  };
@@ -1280,6 +1313,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
1280
1313
  currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
1281
1314
  };
1282
1315
  }
1316
+ if (failures.length === USAGE_ENDPOINT_COUNT) {
1317
+ const codes = failedStatuses.filter((status) => status !== void 0);
1318
+ if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) report.blocked = "invalid-key";
1319
+ else if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) report.blocked = "service-unavailable";
1320
+ else if (codes.length === 0) report.blocked = "network";
1321
+ }
1283
1322
  return report;
1284
1323
  }
1285
1324
  /**
@@ -1400,8 +1439,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
1400
1439
  } catch (error) {
1401
1440
  cleanup();
1402
1441
  if (options.signal?.aborted) throw error;
1403
- if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)}`, "TIMEOUT", { cause: error });
1404
- throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)}`, "TRANSPORT", { cause: error });
1442
+ if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)};Command Code API 请求在 ${connection.requestTimeoutMs} 毫秒内未收到响应——通常是网络或代理问题,请检查后重试`, "TIMEOUT", { cause: error });
1443
+ throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
1405
1444
  }
1406
1445
  if (!response.ok) {
1407
1446
  const errText = await response.text().catch(() => "");
@@ -1620,13 +1659,13 @@ var CommandCodeAdapter = class extends LlmAdapter {
1620
1659
  read = await reader.read();
1621
1660
  } catch (error) {
1622
1661
  if (options.signal?.aborted) throw error;
1623
- throw new LlmError(`Command Code API stream from ${connection.apiBase} failed while reading: ${errorChain(error)}`, "TRANSPORT", { cause: error });
1662
+ throw new LlmError(`Command Code API stream from ${connection.apiBase} failed while reading: ${errorChain(error)};Command Code API 流式响应中途断开——网络波动所致,重试通常可恢复`, "TRANSPORT", { cause: error });
1624
1663
  } finally {
1625
1664
  clearIdle();
1626
1665
  }
1627
1666
  const { done, value } = read;
1628
1667
  if (done) {
1629
- if (idleFired) throw new LlmError(`Command Code API stream from ${connection.apiBase} was idle for ${connection.streamIdleTimeoutMs}ms (no events) and was treated as a dead connection`, "TIMEOUT");
1668
+ if (idleFired) throw new LlmError(`Command Code API stream from ${connection.apiBase} was idle for ${connection.streamIdleTimeoutMs}ms (no events) and was treated as a dead connection;Command Code API 流式响应已 ${connection.streamIdleTimeoutMs} 毫秒无任何事件,被判定为死连接——长思考模型可在设置中调大流空闲超时`, "TIMEOUT");
1630
1669
  if (buffer.trim()) for (const chunk of handleEvent(parseStreamEventLine(buffer))) yield chunk;
1631
1670
  break;
1632
1671
  }
@@ -1645,7 +1684,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1645
1684
  if (!finished) {
1646
1685
  yield* closeText();
1647
1686
  yield* closeReasoning();
1648
- if (!sawContent) throw new LlmError("Command Code returned an empty response", "EMPTY_RESPONSE");
1687
+ if (!sawContent) throw new LlmError("Command Code returned an empty response;Command Code 返回了空响应,重试通常可恢复", "EMPTY_RESPONSE");
1649
1688
  yield {
1650
1689
  type: "finish",
1651
1690
  reason: { kind: "stop" }
@@ -1678,7 +1717,7 @@ function generateHttpError(status, errText, retryAfterMs) {
1678
1717
  if (isRecord(parsed) && isRecord(parsed.error)) providerCode = stringValue(parsed.error.code);
1679
1718
  } catch {}
1680
1719
  const detail = providerCode ?? `HTTP ${status}`;
1681
- if (status === 401) return new LlmError(`Command Code API error 401 (${detail}): the API key is missing or invalid — check the key stored for COMMANDCODE_API_KEY (Models page) or the auth file`, "INVALID_CREDENTIAL", { status: 401 });
1720
+ if (status === 401) return new LlmError(`Command Code API error 401 (${detail}): the API key is missing or invalid — check the key stored for COMMANDCODE_API_KEY (Models page) or the auth file;Command Code API 返回 401:API 密钥缺失或无效——请在设置页检查 COMMANDCODE_API_KEY 存储的密钥,或检查 auth 文件`, "INVALID_CREDENTIAL", { status: 401 });
1682
1721
  return new LlmError(`Command Code API error ${status}${detail === `HTTP ${status}` ? "" : ` (${detail})`}: ${errText.slice(0, 500)}`, status === 429 ? "RATE_LIMIT" : "PROVIDER_HTTP_ERROR", {
1683
1722
  status,
1684
1723
  ...retryAfterMs !== void 0 && retryAfterMs > 0 && retryAfterMs <= 9e5 ? { providerRetryAfterMs: retryAfterMs } : {}
@@ -1719,8 +1758,7 @@ function money(value) {
1719
1758
  function moneyShort(value) {
1720
1759
  return `$${value.toFixed(2)}`;
1721
1760
  }
1722
- /** Format a token count with thousands separators. */
1723
- /** Format a large token count compactly (1.9亿 style). */
1761
+ /** Format a large token count compactly (1.9M style). */
1724
1762
  function tokensCompact(value) {
1725
1763
  if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
1726
1764
  if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
@@ -1754,6 +1792,9 @@ function renderReport(report, title) {
1754
1792
  const lines = [];
1755
1793
  const account = report.account ? ` (${report.account.userName || report.account.name})` : "";
1756
1794
  lines.push(title ?? `📊 Command Code 用量${account}`, "");
1795
+ if (report.blocked === "invalid-key") lines.push("⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置", "");
1796
+ else if (report.blocked === "service-unavailable") lines.push("⚠️ Command Code 服务暂时不可用(5xx),稍后重试", "");
1797
+ else if (report.blocked === "network") lines.push("⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址", "");
1757
1798
  if (report.plan && report.plan.name !== "") {
1758
1799
  const p = report.plan;
1759
1800
  const status = p.status !== "" && p.status !== "active" ? ` (${p.status})` : "";
@@ -1864,6 +1905,11 @@ function parseUsageReport(value) {
1864
1905
  const failures = source.failures;
1865
1906
  if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject("failures");
1866
1907
  const report = { failures };
1908
+ if (source.blocked !== void 0) {
1909
+ const blocked = source.blocked;
1910
+ if (blocked !== "invalid-key" && blocked !== "service-unavailable" && blocked !== "network") reject("blocked");
1911
+ report.blocked = blocked;
1912
+ }
1867
1913
  if (source.account !== void 0) {
1868
1914
  const account = record(source.account, "account");
1869
1915
  report.account = {