@mars-sea/dsh-commandcode-provider 0.8.3 → 0.9.1

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 (command-code@1.33.0) marks
27
+ * Models the official CLI's model table (command-code@1.37.0) 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,10 +32,13 @@ 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.33.0 bundled model table (dist/cli.mjs),
35
+ * Source: the command-code@1.37.0 bundled model table (dist/cli.mjs),
36
36
  * cross-checked with https://commandcode.ai/docs/reference/cli/models.
37
37
  * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
38
- * selectable `['low', 'high', 'max']` efforts.)
38
+ * selectable `['low', 'high', 'max']` efforts; the preview then ended in
39
+ * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
40
+ * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
41
+ * context, routed through OpenRouter.)
39
42
  * Keep in sync via the dsh-commandcode-upstream skill.
40
43
  */
41
44
  declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
@@ -186,7 +189,7 @@ declare function peakPricingState(modelId: string, now?: number): 'peak' | 'off-
186
189
  * for models without time-of-day pricing.
187
190
  */
188
191
  declare function peakPricingLabel(modelId: string, now?: number): string | undefined;
189
- declare const COMMAND_CODE_CLI_VERSION = "1.33.0";
192
+ declare const COMMAND_CODE_CLI_VERSION = "1.37.0";
190
193
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
191
194
  declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
192
195
  declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -599,6 +602,23 @@ declare const USAGE_REPORT_ENDPOINT = "commandcode/report";
599
602
  */
600
603
  declare const usageReportSchema: TypertSchema<CommandCodeAccountsReport>;
601
604
  //#endregion
605
+ //#region src/command-locales.d.ts
606
+ /**
607
+ * Locale copy for the `/commandcode` usage command and the friendly
608
+ * image-gate error rewrite. Distinct from `./client/locales.ts` (the
609
+ * settings-page namespace `settings.commandcode`): the command runs on the
610
+ * Host and has no access to the client's `ctx.locale`, so the dictionaries
611
+ * are exposed as plain constants for direct lookup; the resolver lives in
612
+ * `pickCommandLocale()`. The image-gate wrapper also lives on the client
613
+ * but is reached from a non-React path that has no `t` in scope, so the
614
+ * same dictionaries serve both surfaces.
615
+ *
616
+ * zh is the source of truth for the key set; en must carry the exact same
617
+ * keys — a mismatch is a compile error at the lookup site.
618
+ */
619
+ /** Active locale id recognized by the command and the image-gate wrapper. */
620
+ type LocaleId = 'zh' | 'en';
621
+ //#endregion
602
622
  //#region src/commands.d.ts
603
623
  /** Everything the command needs beyond the adapter itself. */
604
624
  interface CommandCodeCommandDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
@@ -610,6 +630,14 @@ interface CommandCodeCommandDeps<C extends CommandCodeConnectionOptions = Comman
610
630
  * `adapter.getUsage()` report.
611
631
  */
612
632
  reports?: () => Promise<CommandCodeAccountsReport>;
633
+ /**
634
+ * Resolve the active locale for one command run. The plugin entry wires
635
+ * this from `Config.lang` and the shell's `LC_ALL`/`LANG`. Absent in
636
+ * programmatic setups (notably the existing test), the command renders
637
+ * with the default locale (`'zh'`) — historically the only language the
638
+ * command ever shipped in.
639
+ */
640
+ getLocale?: () => LocaleId;
613
641
  }
614
642
  /** The one registered `/commandcode` command. */
615
643
  declare function commandDefinition<C extends CommandCodeConnectionOptions>(deps: CommandCodeCommandDeps<C>): CommandDefinition;
@@ -905,6 +933,18 @@ interface Config {
905
933
  * rotation still applies). Unset means "first usable account".
906
934
  */
907
935
  activeAccount?: string;
936
+ /**
937
+ * Language override for the `/commandcode` Host-side command's user-facing
938
+ * copy. Host commands cannot read the client's `ctx.locale`, so this is
939
+ * the explicit knob: `'zh'` or `'en'`. Unset means the command reads
940
+ * `LC_ALL`/`LANG` from the launching shell, falling back to `'zh'`. The
941
+ * web settings page is unaffected — it follows the browser's language
942
+ * preference on its own. Two surfaces, two independent locales. The
943
+ * declared type is `string` (the schemastery `pattern` cannot narrow
944
+ * literal types); an unknown value is treated as "unset" by
945
+ * `pickCommandLocale`.
946
+ */
947
+ lang?: string;
908
948
  }
909
949
  declare const Config: z<Config>;
910
950
  /** One resolution's complete request facts: connection plus credential reference. */
package/lib/index.js CHANGED
@@ -216,7 +216,7 @@ var CommandCodeAccountPool = class {
216
216
  * and API key or subscription, and Command Code's terms apply.
217
217
  *
218
218
  * Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
219
- * re-verified against command-code@1.33.0 — endpoints, request shape, and
219
+ * re-verified against command-code@1.37.0 — endpoints, request shape, and
220
220
  * stream events unchanged):
221
221
  * POST {apiBase}/alpha/generate
222
222
  * body: { config, memory, taste, skills, params: { model, messages, tools,
@@ -240,6 +240,11 @@ const KNOWN_EFFORTS = {
240
240
  "medium",
241
241
  "xhigh"
242
242
  ],
243
+ "Qwen/Qwen3.8-Flash": [
244
+ "low",
245
+ "medium",
246
+ "xhigh"
247
+ ],
243
248
  "claude-fable-5": [
244
249
  "low",
245
250
  "medium",
@@ -355,11 +360,6 @@ const KNOWN_EFFORTS = {
355
360
  "max"
356
361
  ],
357
362
  "sakana/fugu-ultra": ["high", "xhigh"],
358
- "stealth/ox-alpha": [
359
- "low",
360
- "high",
361
- "max"
362
- ],
363
363
  "xai/grok-4.5": [
364
364
  "low",
365
365
  "medium",
@@ -371,6 +371,11 @@ const KNOWN_EFFORTS = {
371
371
  "high",
372
372
  "xhigh"
373
373
  ],
374
+ "z-ai/glm-5.3-flash": [
375
+ "low",
376
+ "high",
377
+ "max"
378
+ ],
374
379
  "zai-org/GLM-5.2": ["high", "max"],
375
380
  "zai-org/GLM-5.3": [
376
381
  "low",
@@ -399,6 +404,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
399
404
  "Qwen/Qwen3.7-Flash",
400
405
  "Qwen/Qwen3.7-Plus",
401
406
  "Qwen/Qwen3.8-27B",
407
+ "Qwen/Qwen3.8-Flash",
402
408
  "Qwen/Qwen3.8-Max",
403
409
  "claude-fable-5",
404
410
  "claude-haiku-4-5-20251001",
@@ -430,15 +436,15 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
430
436
  "moonshotai/Kimi-K2.7-Code-Highspeed",
431
437
  "moonshotai/Kimi-K3",
432
438
  "sakana/fugu-ultra",
433
- "stealth/ox-alpha",
434
439
  "stepfun/Step-3.7-Flash",
435
440
  "thinkingmachines/inkling",
436
441
  "thinkingmachines/inkling-small",
437
442
  "xai/grok-4.5",
438
- "xiaomi/mimo-v2.5"
443
+ "xiaomi/mimo-v2.5",
444
+ "z-ai/glm-5.3-flash"
439
445
  ]);
440
446
  /**
441
- * Models the official CLI's model table (command-code@1.33.0) marks
447
+ * Models the official CLI's model table (command-code@1.37.0) marks
442
448
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
443
449
  * think automatically, with Command Code driving the depth. This is the
444
450
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -446,10 +452,13 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
446
452
  * effort levels, and this snapshot is not surfaced in the picker's compact
447
453
  * description — it exists for programmatic consumers.
448
454
  *
449
- * Source: the command-code@1.33.0 bundled model table (dist/cli.mjs),
455
+ * Source: the command-code@1.37.0 bundled model table (dist/cli.mjs),
450
456
  * cross-checked with https://commandcode.ai/docs/reference/cli/models.
451
457
  * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
452
- * selectable `['low', 'high', 'max']` efforts.)
458
+ * selectable `['low', 'high', 'max']` efforts; the preview then ended in
459
+ * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
460
+ * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
461
+ * context, routed through OpenRouter.)
453
462
  * Keep in sync via the dsh-commandcode-upstream skill.
454
463
  */
455
464
  const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
@@ -466,6 +475,7 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
466
475
  "stepfun/Step-3.5-Flash",
467
476
  "stepfun/Step-3.7-Flash",
468
477
  "tencent/hy3-paid",
478
+ "tencent/hy4-preview",
469
479
  "nvidia/nemotron-3-ultra-550b-a55b",
470
480
  "thinkingmachines/inkling",
471
481
  "thinkingmachines/inkling-small",
@@ -499,6 +509,7 @@ const KNOWN_PLANS = {
499
509
  "Qwen/Qwen3.7-Max": "go",
500
510
  "Qwen/Qwen3.7-Plus": "go",
501
511
  "Qwen/Qwen3.8-27B": "go",
512
+ "Qwen/Qwen3.8-Flash": "go",
502
513
  "Qwen/Qwen3.8-Max": "go",
503
514
  "deepseek/deepseek-v4-flash": "go",
504
515
  "deepseek/deepseek-v4-flash-vision-exp": "go",
@@ -514,15 +525,16 @@ const KNOWN_PLANS = {
514
525
  "moonshotai/Kimi-K3": "go",
515
526
  "nvidia/nemotron-3-ultra-550b-a55b": "go",
516
527
  "poolside/laguna-s-2.1-free": "go",
517
- "stealth/ox-alpha": "go",
518
528
  "stepfun/Step-3.5-Flash": "go",
519
529
  "stepfun/Step-3.7-Flash": "go",
520
530
  "tencent/hy3-paid": "go",
531
+ "tencent/hy4-preview": "go",
521
532
  "thinkingmachines/inkling": "go",
522
533
  "thinkingmachines/inkling-small": "go",
523
534
  "xai/grok-4.5": "go",
524
535
  "xiaomi/mimo-v2.5": "go",
525
536
  "xiaomi/mimo-v2.5-pro": "go",
537
+ "z-ai/glm-5.3-flash": "go",
526
538
  "zai-org/GLM-5": "go",
527
539
  "zai-org/GLM-5.1": "go",
528
540
  "zai-org/GLM-5.2": "go",
@@ -699,10 +711,6 @@ const KNOWN_DEALS = {
699
711
  "poolside/laguna-s-2.1-free": {
700
712
  label: "FREE",
701
713
  free: true
702
- },
703
- "stealth/ox-alpha": {
704
- label: "FREE",
705
- free: true
706
714
  }
707
715
  };
708
716
  /**
@@ -755,7 +763,7 @@ function peakPricingLabel(modelId, now = Date.now()) {
755
763
  if (state === void 0) return void 0;
756
764
  return state === "peak" ? "Peak" : "Half";
757
765
  }
758
- const COMMAND_CODE_CLI_VERSION = "1.33.0";
766
+ const COMMAND_CODE_CLI_VERSION = "1.37.0";
759
767
  const DEFAULT_API_BASE = "https://api.commandcode.ai";
760
768
  const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
761
769
  const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -1786,6 +1794,97 @@ function mapFinishReason(reason) {
1786
1794
  return { kind: "stop" };
1787
1795
  }
1788
1796
  //#endregion
1797
+ //#region src/command-locales.ts
1798
+ const commandcodeCommand = {
1799
+ zh: {
1800
+ title: "📊 Command Code 用量{account}",
1801
+ accountTitle: "📊 {label}{badges}",
1802
+ accountSeparator: "────────────────────",
1803
+ activeBadge: " ✅ 当前使用",
1804
+ invalidCredentialBadge: " ⛔ 密钥无效",
1805
+ cooldownBadge: " ⏳ 限额冷却中,重置 {when}",
1806
+ rateLimitBadge: " ⏳ 已达限额(等待窗口探测)",
1807
+ unconfigured: " (未配置 API 密钥)",
1808
+ blockedInvalidKey: "⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置",
1809
+ blockedServiceUnavailable: "⚠️ Command Code 服务暂时不可用(5xx),稍后重试",
1810
+ blockedNetwork: "⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址",
1811
+ planLine: " 📦 套餐 {name}{status}{period}",
1812
+ planPeriodSuffix: " · 账期截止 {date}",
1813
+ usageHeader: "── 请求 ──────────────────────────────",
1814
+ requestsLine: " 💬 请求 {n} 次 / 失败 {f} 成功率 {r}%",
1815
+ costLine: " 💰 花费 {money} ({credits} credits)",
1816
+ tokensLine: " 🔤 Token {in} 入 / {out} 出",
1817
+ creditsHeader: "── 信用 ──────────────────────────────",
1818
+ monthlyLine: " 💳 月额度 {monthly} (已购 {purchased} / 赠送 {free})",
1819
+ barLine: " └ {bar} {pct}%",
1820
+ windowsHeader: "── 窗口用量 ──────────────────────────",
1821
+ fiveHourLine: " ⏱ 5 小时 {used} / {cap}{warn}",
1822
+ weeklyLine: " 📅 每周 {used} / {cap}{warn}",
1823
+ windowBarLine: " └ {bar} 重置 {when}",
1824
+ exceededWarning: " ⚠️ 超限!",
1825
+ resetSuffix: "重置 {when}",
1826
+ partialFailures: "⚠️ 部分端点失败: {list}",
1827
+ noData: "(no data — check your API key)",
1828
+ errorText: "Could not fetch Command Code usage: {message}",
1829
+ imageGate: "当前会话已包含图片,而模型 {model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。"
1830
+ },
1831
+ en: {
1832
+ title: "📊 Command Code usage{account}",
1833
+ accountTitle: "📊 {label}{badges}",
1834
+ accountSeparator: "────────────────────",
1835
+ activeBadge: " ✅ active",
1836
+ invalidCredentialBadge: " ⛔ invalid key",
1837
+ cooldownBadge: " ⏳ cooling down, resets {when}",
1838
+ rateLimitBadge: " ⏳ rate-limited (waiting for window probe)",
1839
+ unconfigured: " (no API key configured)",
1840
+ blockedInvalidKey: "⛔ API key invalid or expired — the server rejected every request (401); check the key configured for this account",
1841
+ blockedServiceUnavailable: "⚠️ Command Code service temporarily unavailable (5xx); try again later",
1842
+ blockedNetwork: "⚠️ could not reach the Command Code service — check your network or the API base setting",
1843
+ planLine: " 📦 Plan {name}{status}{period}",
1844
+ planPeriodSuffix: " · period ends {date}",
1845
+ usageHeader: "── Requests ──────────────────────────",
1846
+ requestsLine: " 💬 Requests {n} / failed {f} success rate {r}%",
1847
+ costLine: " 💰 Spend {money} ({credits} credits)",
1848
+ tokensLine: " 🔤 Tokens {in} in / {out} out",
1849
+ creditsHeader: "── Credits ───────────────────────────",
1850
+ monthlyLine: " 💳 Monthly {monthly} (purchased {purchased} / free {free})",
1851
+ barLine: " └ {bar} {pct}%",
1852
+ windowsHeader: "── Window usage ──────────────────────",
1853
+ fiveHourLine: " ⏱ 5-hour {used} / {cap}{warn}",
1854
+ weeklyLine: " 📅 Weekly {used} / {cap}{warn}",
1855
+ windowBarLine: " └ {bar} resets {when}",
1856
+ exceededWarning: " ⚠️ exceeded!",
1857
+ resetSuffix: "resets {when}",
1858
+ partialFailures: "⚠️ some endpoints failed: {list}",
1859
+ noData: "(no data — check your API key)",
1860
+ errorText: "Could not fetch Command Code usage: {message}",
1861
+ imageGate: "This session already contains images, and model {model} does not accept image input; please select an image-capable model, or remove the images from the session first."
1862
+ }
1863
+ };
1864
+ /**
1865
+ * Resolve the active locale for a Host-side command run.
1866
+ *
1867
+ * Priority: explicit `override` (from `Config.lang`) → `LC_ALL` → `LANG` →
1868
+ * the conventional fallback (`'zh'`, matching the existing single-language
1869
+ * behavior so unconfigured deployments keep their current output).
1870
+ *
1871
+ * The values are matched on the leading tag only — `zh_CN.UTF-8`,
1872
+ * `zh-Hans`, `zh` all map to `'zh'`; everything starting with `en` maps to
1873
+ * `'en'`; anything else falls back to `'zh'` (a non-`en` shell that
1874
+ * already has Chinese in the terminal is the closest sensible default;
1875
+ * a Western shell that happens to be neither keeps the existing Chinese
1876
+ * output rather than swapping to half-translated English).
1877
+ */
1878
+ function pickCommandLocale(override, env = process.env) {
1879
+ if (override === "zh" || override === "en") return override;
1880
+ if (((env.LC_ALL ?? env.LANG ?? "").toLowerCase().split(/[._-]/)[0] ?? "") === "en") return "en";
1881
+ return "zh";
1882
+ }
1883
+ /** Look up a key in the active locale, with an internal en fallback. */
1884
+ function commandCopy(locale, key) {
1885
+ return commandcodeCommand[locale][key] ?? commandcodeCommand.en[key] ?? key;
1886
+ }
1887
+ //#endregion
1789
1888
  //#region src/commands.ts
1790
1889
  /** Format a dollar amount. */
1791
1890
  function money(value) {
@@ -1802,7 +1901,7 @@ function tokensCompact(value) {
1802
1901
  if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
1803
1902
  return String(value);
1804
1903
  }
1805
- /** Format a millis timestamp as a local date. */
1904
+ /** Format a millis timestamp as a local date; `n/a` when unset. */
1806
1905
  function resetLabel(ms) {
1807
1906
  if (ms <= 0) return "n/a";
1808
1907
  return new Date(ms).toLocaleString();
@@ -1818,37 +1917,37 @@ function bar(used, cap) {
1818
1917
  return "█".repeat(filled) + "░".repeat(10 - filled);
1819
1918
  }
1820
1919
  /** Render one account's rotation mark / cooldown as a short badge. */
1821
- function markLabel(entry) {
1822
- if (entry.mark === "invalid-credential") return " ⛔ 密钥无效";
1823
- if (entry.cooldownUntil > 0) return ` ⏳ 限额冷却中,重置 ${resetLabel(entry.cooldownUntil)}`;
1824
- if (entry.mark === "rate-limit") return " ⏳ 已达限额(等待窗口探测)";
1920
+ function markLabel(entry, locale) {
1921
+ if (entry.mark === "invalid-credential") return commandCopy(locale, "invalidCredentialBadge");
1922
+ if (entry.cooldownUntil > 0) return commandCopy(locale, "cooldownBadge").replace("{when}", resetLabel(entry.cooldownUntil));
1923
+ if (entry.mark === "rate-limit") return commandCopy(locale, "rateLimitBadge");
1825
1924
  return "";
1826
1925
  }
1827
1926
  /** Render the usage report as a structured, aligned, bar-chart text view. */
1828
- function renderReport(report, title) {
1927
+ function renderReport(report, locale, title) {
1829
1928
  const lines = [];
1830
1929
  const account = report.account ? ` (${report.account.userName || report.account.name})` : "";
1831
- lines.push(title ?? `📊 Command Code 用量${account}`, "");
1832
- if (report.blocked === "invalid-key") lines.push("⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置", "");
1833
- else if (report.blocked === "service-unavailable") lines.push("⚠️ Command Code 服务暂时不可用(5xx),稍后重试", "");
1834
- else if (report.blocked === "network") lines.push("⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址", "");
1930
+ lines.push(title ?? commandCopy(locale, "title").replace("{account}", account), "");
1931
+ if (report.blocked === "invalid-key") lines.push(commandCopy(locale, "blockedInvalidKey"), "");
1932
+ else if (report.blocked === "service-unavailable") lines.push(commandCopy(locale, "blockedServiceUnavailable"), "");
1933
+ else if (report.blocked === "network") lines.push(commandCopy(locale, "blockedNetwork"), "");
1835
1934
  if (report.plan && report.plan.name !== "") {
1836
1935
  const p = report.plan;
1837
1936
  const status = p.status !== "" && p.status !== "active" ? ` (${p.status})` : "";
1838
- const period = p.currentPeriodEnd > 0 ? ` · 账期截止 ${new Date(p.currentPeriodEnd).toLocaleDateString()}` : "";
1839
- lines.push(` 📦 套餐 ${p.name}${status}${period}`, "");
1937
+ const period = p.currentPeriodEnd > 0 ? commandCopy(locale, "planPeriodSuffix").replace("{date}", new Date(p.currentPeriodEnd).toLocaleDateString()) : "";
1938
+ lines.push(commandCopy(locale, "planLine").replace("{name}", p.name).replace("{status}", status).replace("{period}", period), "");
1840
1939
  }
1841
1940
  if (report.usage) {
1842
1941
  const u = report.usage;
1843
- lines.push("── 请求 ──────────────────────────────", ` 💬 请求 ${u.completedCount} 次 / 失败 ${u.failedCount} 成功率 ${u.successRate}%`, ` 💰 花费 ${money(u.totalCost)} (${moneyShort(u.totalCredits)} credits)`, ` 🔤 Token ${tokensCompact(u.totalTokensIn)} 入 / ${tokensCompact(u.totalTokensOut)} 出`, "");
1942
+ lines.push(commandCopy(locale, "usageHeader"), commandCopy(locale, "requestsLine").replace("{n}", String(u.completedCount)).replace("{f}", String(u.failedCount)).replace("{r}", String(u.successRate)), commandCopy(locale, "costLine").replace("{money}", money(u.totalCost)).replace("{credits}", moneyShort(u.totalCredits)), commandCopy(locale, "tokensLine").replace("{in}", tokensCompact(u.totalTokensIn)).replace("{out}", tokensCompact(u.totalTokensOut)), "");
1844
1943
  }
1845
1944
  if (report.credits) {
1846
1945
  const c = report.credits;
1847
1946
  const monthlyPct = c.monthlyCredits > 0 ? `${(c.monthlyCredits / (c.monthlyCredits + c.purchasedCredits) * 100).toFixed(0)}%` : "—";
1848
- lines.push("── 信用 ──────────────────────────────", ` 💳 月额度 ${moneyShort(c.monthlyCredits)} (已购 ${moneyShort(c.purchasedCredits)} / 赠送 ${moneyShort(c.freeCredits)})`, ` └ ${bar(c.monthlyCredits, c.monthlyCredits + c.purchasedCredits)} ${monthlyPct}`, "", "── 窗口用量 ──────────────────────────", ` ⏱ 5 小时 ${moneyShort(c.fiveHour.used)} / ${moneyShort(c.fiveHour.cap)}${c.fiveHour.exceeded ? " ⚠️ 超限!" : ""}`, ` └ ${bar(c.fiveHour.used, c.fiveHour.cap)} 重置 ${resetLabel(c.fiveHour.resetAt)}`, ` 📅 每周 ${moneyShort(c.weekly.used)} / ${moneyShort(c.weekly.cap)}${c.weekly.exceeded ? " ⚠️ 超限!" : ""}`, ` └ ${bar(c.weekly.used, c.weekly.cap)} 重置 ${resetLabel(c.weekly.resetAt)}`, "");
1947
+ lines.push(commandCopy(locale, "creditsHeader"), commandCopy(locale, "monthlyLine").replace("{monthly}", moneyShort(c.monthlyCredits)).replace("{purchased}", moneyShort(c.purchasedCredits)).replace("{free}", moneyShort(c.freeCredits)), commandCopy(locale, "barLine").replace("{bar}", bar(c.monthlyCredits, c.monthlyCredits + c.purchasedCredits)).replace("{pct}", monthlyPct), "", commandCopy(locale, "windowsHeader"), commandCopy(locale, "fiveHourLine").replace("{used}", moneyShort(c.fiveHour.used)).replace("{cap}", moneyShort(c.fiveHour.cap)).replace("{warn}", c.fiveHour.exceeded ? commandCopy(locale, "exceededWarning") : ""), commandCopy(locale, "windowBarLine").replace("{bar}", bar(c.fiveHour.used, c.fiveHour.cap)).replace("{when}", resetLabel(c.fiveHour.resetAt)), commandCopy(locale, "weeklyLine").replace("{used}", moneyShort(c.weekly.used)).replace("{cap}", moneyShort(c.weekly.cap)).replace("{warn}", c.weekly.exceeded ? commandCopy(locale, "exceededWarning") : ""), commandCopy(locale, "windowBarLine").replace("{bar}", bar(c.weekly.used, c.weekly.cap)).replace("{when}", resetLabel(c.weekly.resetAt)), "");
1849
1948
  }
1850
- if (report.failures.length > 0) lines.push(`⚠️ 部分端点失败: ${report.failures.join("; ")}`, "");
1851
- if (!report.account && !report.usage && !report.credits) lines.push("(no data — check your API key)", "");
1949
+ if (report.failures.length > 0) lines.push(commandCopy(locale, "partialFailures").replace("{list}", report.failures.join("; ")), "");
1950
+ if (!report.account && !report.usage && !report.credits) lines.push(commandCopy(locale, "noData"), "");
1852
1951
  return lines.join("\n").trimEnd();
1853
1952
  }
1854
1953
  /** The one registered `/commandcode` command. */
@@ -1859,27 +1958,29 @@ function commandDefinition(deps) {
1859
1958
  description: "Command Code account usage dashboard",
1860
1959
  input: { hint: "[status]" },
1861
1960
  handler: async () => {
1961
+ const locale = deps.getLocale?.() ?? "zh";
1862
1962
  try {
1863
1963
  if (deps.reports !== void 0) {
1864
1964
  const { accounts } = await deps.reports();
1865
1965
  return {
1866
1966
  kind: "success",
1867
1967
  text: accounts.map((entry) => {
1868
- const badges = `${entry.active ? " ✅ 当前使用" : ""}${markLabel(entry)}`;
1869
- const title = `📊 ${entry.label}${badges}`;
1870
- if (!entry.configured) return `${title}\n\n (未配置 API 密钥)`;
1871
- return renderReport(entry.report, title);
1872
- }).join("\n\n────────────────────\n\n")
1968
+ const badges = `${entry.active ? commandCopy(locale, "activeBadge") : ""}${markLabel(entry, locale)}`;
1969
+ const title = commandCopy(locale, "accountTitle").replace("{label}", entry.label).replace("{badges}", badges);
1970
+ if (!entry.configured) return `${title}\n\n${commandCopy(locale, "unconfigured")}`;
1971
+ return renderReport(entry.report, locale, title);
1972
+ }).join(`\n\n${commandCopy(locale, "accountSeparator")}\n\n`)
1873
1973
  };
1874
1974
  }
1875
1975
  return {
1876
1976
  kind: "success",
1877
- text: renderReport(await adapter.getUsage())
1977
+ text: renderReport(await adapter.getUsage(), locale)
1878
1978
  };
1879
1979
  } catch (error) {
1980
+ const message = error instanceof Error ? error.message : String(error);
1880
1981
  return {
1881
1982
  kind: "error",
1882
- text: `Could not fetch Command Code usage: ${error instanceof Error ? error.message : String(error)}`
1983
+ text: commandCopy(locale, "errorText").replace("{message}", message)
1883
1984
  };
1884
1985
  }
1885
1986
  }
@@ -2619,7 +2720,8 @@ const Config = z.object({
2619
2720
  apiKeyEnv: z.string().role("credential-ref"),
2620
2721
  apiKey: z.string()
2621
2722
  })),
2622
- activeAccount: z.string()
2723
+ activeAccount: z.string(),
2724
+ lang: z.string().pattern(/^(zh|en)$/).default("zh")
2623
2725
  });
2624
2726
  /**
2625
2727
  * The one explicit resolve step from raw config to validated connection
@@ -2743,10 +2845,12 @@ function apply(ctx, config) {
2743
2845
  };
2744
2846
  })) };
2745
2847
  };
2848
+ const commandLocale = () => pickCommandLocale(current().lang);
2746
2849
  ctx.inject(["commands"], (commandCtx) => {
2747
2850
  applyCommands(commandCtx, {
2748
2851
  adapter,
2749
- reports: usageReports
2852
+ reports: usageReports,
2853
+ getLocale: commandLocale
2750
2854
  });
2751
2855
  });
2752
2856
  const loginFlow = new CommandCodeLoginFlow({