dsh-usage-state 0.4.5 → 0.4.6

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/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.6] - 2026-10-14
10
+
11
+ ### Fixed
12
+
13
+ - Kimi Code provider(kimi-code/kimi-coding/kimi-for-coding)和api.kimi.com/api.kimi.ai自动选择Coding Plan用量,Moonshot保持API余额;显式模式选择优先。
14
+ - Kimi用量兼容官方客户端usages比例池(5h/7d/月总额度/编程月用量)及旧usage/limits计数;TIME_UNIT_MINUTE的300分钟归一为5h,新套餐无周额度时不虚构7d。
15
+ - Kimi Code完整API地址不再重复拼接coding路径;Moonshot与Kimi Code的provider凭据按模式筛选,HTTP200错误信封及非法读数不会覆盖成功值。
16
+
17
+ ### Validation
18
+
19
+ - 新增provider→凭据→请求→RPC回归、月额度状态行渲染及真实tarball的Kimi预构建运行冒烟,纳入现有CI/Release的Node24与Node20门禁。
20
+ - 使用既有KIMI_CODING_API_KEY对官方接口完成只读实测,成功读取5h/7d窗口;未将密钥或账户响应写入仓库。月额度形态依官方客户端固定源码和去身份fixture验证。
21
+
9
22
  ## [0.4.5] - 2026-10-10
10
23
 
11
24
  ### Fixed
@@ -102,7 +115,8 @@
102
115
 
103
116
  0.4.5起建立版本tag/Release;更早发布没有保存这些引用。0.3.0至0.4.4的比较端点来自各npm版本的gitHead;0.2系列引用对应实现提交,不能替代当时没有保存的正式发布产物。早期日期沿用历史版本记录。今后版本应按发布流程建立稳定引用。
104
117
 
105
- [Unreleased]: https://github.com/takboo/dsh-usage-state/compare/v0.4.5...HEAD
118
+ [Unreleased]: https://github.com/takboo/dsh-usage-state/compare/v0.4.6...HEAD
119
+ [0.4.6]: https://github.com/takboo/dsh-usage-state/compare/v0.4.5...v0.4.6
106
120
  [0.4.5]: https://github.com/takboo/dsh-usage-state/compare/3ad9a71e2f7e4b9a05c69bc529e396a364ae6aeb...v0.4.5
107
121
  [0.4.4]: https://github.com/takboo/dsh-usage-state/compare/885541638ffa4da05b39bd1864085ba6d9acc288...3ad9a71e2f7e4b9a05c69bc529e396a364ae6aeb
108
122
  [0.4.3]: https://github.com/takboo/dsh-usage-state/compare/0d2d4376d0db990fb36df5d6124e5b7894974d62...885541638ffa4da05b39bd1864085ba6d9acc288
package/README.en.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![CI](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/dsh-usage-state)](https://www.npmjs.com/package/dsh-usage-state)
6
6
 
7
- > **Version note:** **0.4.5** includes the balance parsing, credential override, cache isolation and failure-display fixes previously recorded as Unreleased. See the [Changelog](CHANGELOG.md#045---2026-10-10). Users of 0.4.4 or earlier should upgrade to 0.4.5 or a newer compatible version.
7
+ > **Release status:** Current version **0.4.6** adds automatic Kimi Code provider usage detection, regional endpoints, and current/legacy quota responses. See the [GitHub Release](https://github.com/takboo/dsh-usage-state/releases/tag/v0.4.6) for the tarball and checksum and the [Changelog](CHANGELOG.md#046---2026-10-14) for details.
8
8
 
9
9
  Show the current model provider's **account balance** or **coding-plan quota** above the composer input in [DSH (DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness). The line matches the input card's width and preserves the native statistics and context meter.
10
10
 
@@ -63,6 +63,8 @@ Advanced controls offer source, endpoint, credential name and key writes. Explic
63
63
  | OpenCode Zen Go | — | 5h / 7d / 30d used % | OPENCODE_GO_API_KEY / OPENCODE_API_KEY |
64
64
  | Sub2API | Balance or key allowance | Depends on the instance, e.g. 5h / 1d / 7d / 30d | SUB2API_API_KEY and instance URL |
65
65
 
66
+ Kimi Code routes `kimi-code`, `kimi-coding`, and `kimi-for-coding`, plus custom providers declaring `api.kimi.com` / `api.kimi.ai`, automatically read subscription usage. The plugin displays only returned windows: 5h, 7d, monthly total, and monthly code usage. Moonshot regional APIs retain CNY balance mode. Explicit settings take precedence.
67
+
66
68
  Percentages mean **used**. z.ai keys belong to their China/global region; China is the default endpoint and failures may try the mirror. The current cache merges by source and mode, so separate keys/instances must not be treated as supported independent accounts. Endpoints, field semantics and extension steps are in the [adapter guide (Chinese)](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md).
67
69
 
68
70
  ## Screenshots
@@ -89,7 +91,7 @@ See the [Backlog (Chinese)](https://github.com/takboo/dsh-usage-state/blob/main/
89
91
 
90
92
  - **Older npm 0.4.4 and earlier:** still has credential override, pin/cache identity, legacy hiding, default ordering and failure-display defects. It still divides Moonshot balances ≥100 yuan by 100 and may turn invalid DeepSeek amounts into zero. These are fixed in 0.4.5; upgrade if you use an older version. Cross-check old-version amounts against the [official API](https://platform.kimi.com/docs/api/balance.md).
91
93
  - **Account scope:** one target is still selected per source and mode. Identity isolation does not add independent multi-account support.
92
- - **Live verification:** Kimi Code/Sub2API accounts, high-threshold colors, real credential writes and nonzero OpenCode readings still lack live coverage. Local interaction tests do not replace that acceptance.
94
+ - **Live verification:** Kimi Code monthly quotas, Sub2API accounts, high-threshold colors, real credential writes and nonzero OpenCode readings still lack live coverage. Local interaction tests do not replace that acceptance.
93
95
  - **Scope:** the line can be absent without a selected model or when hidden; clicking it does not open settings; readings are not saved as history.
94
96
 
95
97
  ## Development and docs
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![CI](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/dsh-usage-state)](https://www.npmjs.com/package/dsh-usage-state)
6
6
 
7
- > **版本说明**:**0.4.5** 包含此前的余额解析、凭据覆盖、缓存隔离和失败展示修复,详情见 [更新日志](CHANGELOG.md#045---2026-10-10)。使用0.4.4及更早版本的用户请升级至0.4.5或更新的兼容版本。
7
+ > **发布状态**:当前版本 **0.4.6**,增加 Kimi Code provider 自动用量检测,兼容国内/国际端点和新旧套餐响应。安装包及校验文件见 [GitHub Release](https://github.com/takboo/dsh-usage-state/releases/tag/v0.4.6),详情见 [更新日志](CHANGELOG.md#046---2026-10-14)。
8
8
 
9
9
  在 [DSH(DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness) 输入框上方显示当前模型供应商的**账户余额**或**套餐额度**。状态行与输入卡片同宽,保留原生统计行和上下文计量器。
10
10
 
@@ -63,6 +63,8 @@ DSH0.1使用历史版本0.3.2;该版本不适配0.2。0.4.0有RPC挂载缺陷
63
63
  | OpenCode Zen Go | — | 5h / 7d / 30d已用% | OPENCODE_GO_API_KEY / OPENCODE_API_KEY |
64
64
  | Sub2API | 余额或key配额 | 按实例响应,如5h / 1d / 7d / 30d | SUB2API_API_KEY及实例地址 |
65
65
 
66
+ Kimi Code 的 `kimi-code`、`kimi-coding`、`kimi-for-coding` provider,以及声明 `api.kimi.com` / `api.kimi.ai` 的自定义 provider,会自动读取套餐用量。支持 5h、7d、月总额度及编程月用量,仅展示账户实际返回的窗口。Moonshot 国内/国际 API 继续显示人民币余额;可在设置中显式选择模式。
67
+
66
68
  百分比表示**已用**。z.ai的key分国内/国际区域;默认国内地址,失败可尝试镜像。当前缓存按数据源与模式合并,不能将不同密钥/实例视为已支持独立多账户。厂商端点、字段和扩展步骤见 [适配器指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md)。
67
69
 
68
70
  ## 截图
@@ -89,7 +91,7 @@ OpenCode Zen Go三个窗口,独立状态行位于输入框上方:
89
91
 
90
92
  - **旧版npm0.4.4及更早版本**:仍有provider凭据覆盖、pin/缓存身份、legacy隐藏、默认排序和失败展示缺陷;Moonshot≥100元仍会被除以100,DeepSeek非法金额仍可能变0。这些已在0.4.5修复;使用旧版请升级。旧版金额应与 [官方接口](https://platform.kimi.com/docs/api/balance.md) 核对。
91
93
  - **账户范围**:仍按source+mode选择一个目标;配置身份隔离不等于新增独立多账户支持。
92
- - **未验证范围**:Kimi Code/Sub2API真实账户、高阈值视觉、真实凭据写入和OpenCode非零路径仍缺活体验证;本地交互测试不代替这些验收。
94
+ - **未验证范围**:Kimi Code月额度真实账户、Sub2API真实账户、高阈值视觉、真实凭据写入和OpenCode非零路径仍缺活体验证;本地交互测试不代替这些验收。
93
95
  - **范围**:无模型选择或隐藏时可以不显示;点击状态行不打开设置;不保存历史读数。
94
96
 
95
97
  ## 开发与文档
package/docs/adapters.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 适配器指南
2
2
 
3
- 适用于扩展或调整余额/额度来源。当前代码支持DeepSeek、z.ai、Kimi、OpenCode和Sub2API。用户安装排查请读 [排查指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/troubleshooting.md),候选和整改状态见 [Backlog](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md)。本文件随npm包发布,代码和其他文档链接指向GitHub。下文描述当前Unreleased工作树,npm已发布0.4.4不包含本轮修复。
3
+ 适用于扩展或调整余额/额度来源。当前代码支持DeepSeek、z.ai、Kimi、OpenCode和Sub2API。用户安装排查请读 [排查指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/troubleshooting.md),候选和整改状态见 [Backlog](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md)。本文件随npm包发布,代码和其他文档链接指向GitHub。下文描述0.4.6实现。
4
4
 
5
5
  ## Interface
6
6
 
@@ -56,7 +56,7 @@ requiresApiKey在类型中存在,但当前宿主装配仍要求解析到凭据
56
56
  | DeepSeek API | GET {base}/user/balance,Bearer | balance_infos多币种;优先有余额/CNY并保留granted/topped_up。有限真实0合法,非法total_balance跳过;无有效行抛parse |
57
57
  | z.ai Coding Plan | GET {base}/api/monitor/usage/quota/limit,Bearer;默认open.bigmodel.cn,镜像api.z.ai | unit3→5h、6→7d;TOKENS_LIMIT/CREDIT_LIMIT读percentage,不将TIME_LIMIT混为编码额度;兼容plans/flat形态。HTTP200 success:false/code1000归auth;属社区接口观察,已有历史真机证据 |
58
58
  | Moonshot API | GET api.moonshot.cn/v1/users/me/balance,Bearer | 官方data.available_balance以人民币元计,voucher/cash也为元,cash可为负;当前余额不按大小换单位,保留已有两位小数舍入及兼容字段,不新增负available_balance支持 |
59
- | Kimi Code | GET api.kimi.com/coding/v1/usages,Bearer及KimiCLI/1.6 UA | 当前parse从usage读周窗口、limits[].detail/window读滚动窗口;UA/形态来自社区观察,缺真实账户验证 |
59
+ | Kimi Code | GET api.kimi.com/coding/v1/usages,Bearer及KimiCLI/1.6 UA | usages.limit_5h/limit_7d/limit_month_total/limit_month_code的used_ratio×100,reset_time归一;兼容usage周计数及limits[].detail/window,枚举分钟归一到小时;国内5h/7d已真实账户验证,月额度仅fixture验证 |
60
60
  | OpenCode Zen Go | GET opencode.ai/zen/go/v1/usage,Bearer及浏览器UA | usage或data.usage;rolling/weekly/monthly→5h/7d/30d;percent为0..100;resetsAt归一。历史真机观察UA缺失触发Cloudflare;非零/status非ok未验证 |
61
61
  | Sub2API | GET {instance}/v1/usage,Bearer;必须实例地址 | quota.remaining或wallet balance、rate_limits窗口;subscription.daily/weekly/monthly→1d/7d/30d。单位缺失默认USD;parse目前未按mode过滤,可能同时返回余额和窗口;接口字段随实例版本变化 |
62
62
 
@@ -67,3 +67,6 @@ requiresApiKey在类型中存在,但当前宿主装配仍要求解析到凭据
67
67
  当前共享解析以显式provider覆盖→legacy source默认→provider声明origin/Adapter默认确定有效目标。ref和pin贯通读取/凭据描述;显式pin使fallbackRequests返回空列表。相关回归见 [A01](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md#a01)、[A03](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md#a03)。
68
68
 
69
69
  无pin时首站失败仍按序尝试fallback,不仅在未配置端点时发生。扩展必须同时测Adapter、读取层和入口,覆盖真实HTTP、凭据选择、配置切换及错误边界,不能只证明一个模块接受参数。以上是Unreleased行为,npm0.4.4仍旧实现。
70
+
71
+
72
+ Kimi当前响应和国际端点依据[官方客户端固定源码](https://github.com/MoonshotAI/kimi-code/blob/b510fa9aec85f02849f64121550be2c3fbd4294d/packages/oauth/src/managed-usage.ts)。比例池优先于同名旧计数,缺失周额度不补0;月总额度和编程月用量分别显示,不相加。官方客户端源码是接口观察依据,不视为稳定公开API保证。2026-10-14使用本机既有Kimi Code凭据对api.kimi.com完成只读查询,5h/7d均可解析;未验证国际区域账户和月额度活体响应。
package/lib/client.js CHANGED
@@ -244,7 +244,7 @@ window.__ModuleLoader__.load({
244
244
  },
245
245
  {
246
246
  sourceId: "kimi",
247
- pattern: /(^|\.)(api\.kimi\.com|api\.moonshot\.cn|moonshot\.cn)$/
247
+ pattern: /(^|\.)(api\.kimi\.(com|ai)|api\.moonshot\.(cn|ai)|moonshot\.cn)$/
248
248
  },
249
249
  {
250
250
  sourceId: "opencode",
@@ -285,6 +285,16 @@ window.__ModuleLoader__.load({
285
285
  return;
286
286
  }
287
287
  }
288
+ /** Kimi Code and Moonshot use distinct keys and billing endpoints. */
289
+ function kimiMode(provider, endpoint) {
290
+ const origin = originOf(endpoint);
291
+ if (origin !== void 0) {
292
+ const host = new URL(origin).hostname;
293
+ if (host === "api.kimi.com" || host === "api.kimi.ai") return "coding-plan";
294
+ if (host === "api.moonshot.cn" || host === "api.moonshot.ai") return "api";
295
+ }
296
+ return /(?:^|[^a-z])kimi[-_ ]?(?:for[-_ ]?coding|coding|code)(?:$|[^a-z])/i.test(provider) ? "coding-plan" : "api";
297
+ }
288
298
  function withTarget(resolution) {
289
299
  if (resolution.sourceId === null || resolution.mode === null) return resolution;
290
300
  return {
@@ -350,8 +360,8 @@ window.__ModuleLoader__.load({
350
360
  mode: null,
351
361
  reason: "needs-endpoint"
352
362
  };
353
- const mode = source.modes[0];
354
- if (mode === void 0) return {
363
+ const mode = sourceId === "kimi" ? kimiMode(input.provider, baseUrl) : source.modes[0];
364
+ if (mode === void 0 || !source.modes.includes(mode)) return {
355
365
  ...base,
356
366
  sourceId,
357
367
  mode: null,
@@ -1638,7 +1648,9 @@ window.__ModuleLoader__.load({
1638
1648
  "window.5h": "5h",
1639
1649
  "window.1d": "1d",
1640
1650
  "window.7d": "7d",
1641
- "window.30d": "30d"
1651
+ "window.30d": "30d",
1652
+ "window.month": "月总额度",
1653
+ "window.month-code": "编程月用量"
1642
1654
  };
1643
1655
  const en = {
1644
1656
  nav: "Usage state",
@@ -1714,7 +1726,9 @@ window.__ModuleLoader__.load({
1714
1726
  "window.5h": "5h",
1715
1727
  "window.1d": "1d",
1716
1728
  "window.7d": "7d",
1717
- "window.30d": "30d"
1729
+ "window.30d": "30d",
1730
+ "window.month": "Monthly total",
1731
+ "window.month-code": "Monthly code"
1718
1732
  };
1719
1733
  //#endregion
1720
1734
  //#region src/client/remote.ts
package/lib/index.js CHANGED
@@ -199,7 +199,7 @@ const HOST_HINTS = [
199
199
  },
200
200
  {
201
201
  sourceId: "kimi",
202
- pattern: /(^|\.)(api\.kimi\.com|api\.moonshot\.cn|moonshot\.cn)$/
202
+ pattern: /(^|\.)(api\.kimi\.(com|ai)|api\.moonshot\.(cn|ai)|moonshot\.cn)$/
203
203
  },
204
204
  {
205
205
  sourceId: "opencode",
@@ -240,6 +240,16 @@ function originOf(url) {
240
240
  return;
241
241
  }
242
242
  }
243
+ /** Kimi Code and Moonshot use distinct keys and billing endpoints. */
244
+ function kimiMode(provider, endpoint) {
245
+ const origin = originOf(endpoint);
246
+ if (origin !== void 0) {
247
+ const host = new URL(origin).hostname;
248
+ if (host === "api.kimi.com" || host === "api.kimi.ai") return "coding-plan";
249
+ if (host === "api.moonshot.cn" || host === "api.moonshot.ai") return "api";
250
+ }
251
+ return /(?:^|[^a-z])kimi[-_ ]?(?:for[-_ ]?coding|coding|code)(?:$|[^a-z])/i.test(provider) ? "coding-plan" : "api";
252
+ }
243
253
  function withTarget(resolution) {
244
254
  if (resolution.sourceId === null || resolution.mode === null) return resolution;
245
255
  return {
@@ -305,8 +315,8 @@ function resolveProvider(input) {
305
315
  mode: null,
306
316
  reason: "needs-endpoint"
307
317
  };
308
- const mode = source.modes[0];
309
- if (mode === void 0) return {
318
+ const mode = sourceId === "kimi" ? kimiMode(input.provider, baseUrl) : source.modes[0];
319
+ if (mode === void 0 || !source.modes.includes(mode)) return {
310
320
  ...base,
311
321
  sourceId,
312
322
  mode: null,
@@ -461,7 +471,9 @@ const KIMI_CLI_USER_AGENT = "KimiCLI/1.6";
461
471
  const WINDOW_RANK$2 = {
462
472
  "5h": 0,
463
473
  "1d": 1,
464
- "7d": 2
474
+ "7d": 2,
475
+ month: 3,
476
+ "month-code": 4
465
477
  };
466
478
  function asRecord$4(value) {
467
479
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -485,21 +497,24 @@ function percentFrom(record) {
485
497
  const limit = toFiniteNumber(record.limit);
486
498
  if (limit === void 0 || limit <= 0) return null;
487
499
  const used = toFiniteNumber(record.used);
488
- if (used !== void 0) return clampPercent(used / limit * 100);
500
+ if (used !== void 0 && used >= 0) return clampPercent(Math.min(1, used / limit) * 100);
489
501
  const remaining = toFiniteNumber(record.remaining);
490
- if (remaining !== void 0) return clampPercent((limit - remaining) / limit * 100);
502
+ if (remaining !== void 0 && remaining >= 0 && remaining <= limit) return clampPercent((limit - remaining) / limit * 100);
491
503
  return null;
492
504
  }
493
505
  /** `{ duration: 5, timeUnit: 'hour' }` -> `5h`, and weekly variants -> `7d`. */
494
506
  function windowIdFrom(window) {
495
507
  if (window === void 0) return void 0;
496
- const unit = typeof window.timeUnit === "string" ? window.timeUnit.trim().toLowerCase() : "";
508
+ const unit = typeof window.timeUnit === "string" ? window.timeUnit.trim().toLowerCase().replace(/^time_unit_/, "") : "";
497
509
  const duration = toFiniteNumber(window.duration);
498
510
  if (unit === "" || duration === void 0 || duration <= 0) return void 0;
499
- if (unit.startsWith("hour")) return `${duration}h`;
511
+ if (unit.startsWith("hour")) return duration % 24 === 0 ? `${duration / 24}d` : `${duration}h`;
500
512
  if (unit.startsWith("day")) return duration === 7 ? "7d" : `${duration}d`;
501
513
  if (unit.startsWith("week")) return duration === 1 ? "7d" : `${duration * 7}d`;
502
- if (unit.startsWith("minute")) return `${duration}m`;
514
+ if (unit.startsWith("minute")) return windowIdFrom({
515
+ duration: duration / 60,
516
+ timeUnit: "hour"
517
+ });
503
518
  if (unit.startsWith("month")) return "30d";
504
519
  return `${duration}${unit.slice(0, 4)}`;
505
520
  }
@@ -509,6 +524,7 @@ function sortWindows$2(windows) {
509
524
  /** `GET /v1/users/me/balance` — pay-as-you-go wallet, CNY. */
510
525
  function parseMoonshotBalance(payload) {
511
526
  const root = asRecord$4(payload);
527
+ rejectErrorEnvelope(root);
512
528
  const nested = root === void 0 ? void 0 : asRecord$4(root.data);
513
529
  const value = firstFinite([
514
530
  root?.available_balance,
@@ -526,19 +542,44 @@ function parseMoonshotBalance(payload) {
526
542
  windows: []
527
543
  };
528
544
  }
545
+ function rejectErrorEnvelope(root) {
546
+ if (root?.error != null || root?.status === false || root?.code !== void 0 && root.code !== 0 && root.code !== "0") throw new SourceError("parse", "Kimi response contains an API error");
547
+ }
529
548
  /**
530
549
  * `GET /coding/v1/usages` — Kimi Code subscription. The top-level `usage` is the
531
- * weekly quota; `limits[]` carries the rolling windows (5h among them). Neither is
532
- * officially documented, so every field is optional here.
550
+ * weekly quota in legacy responses; `limits[]` carries rolling windows. Current
551
+ * official client reads ratio pools in `usages` (see docs/adapters.md).
533
552
  */
534
553
  function parseKimiCodeUsage(payload) {
535
554
  const root = asRecord$4(payload);
536
555
  if (root === void 0) throw new SourceError("parse", "Kimi Code usage response is not a JSON object");
556
+ rejectErrorEnvelope(root);
537
557
  const found = /* @__PURE__ */ new Map();
558
+ const usages = asRecord$4(root.usages);
559
+ for (const [field, id] of [
560
+ ["limit_5h", "5h"],
561
+ ["limit_7d", "7d"],
562
+ ["limit_month_total", "month"],
563
+ ["limit_month_code", "month-code"]
564
+ ]) {
565
+ const pool = asRecord$4(usages?.[field]);
566
+ const ratio = toFiniteNumber(pool?.used_ratio);
567
+ if (ratio === void 0 || ratio < 0) continue;
568
+ const usedPercent = clampPercent(Math.min(1, ratio) * 100);
569
+ const resetsAt = normalizeResetAt(pool?.reset_time);
570
+ found.set(id, resetsAt === void 0 ? {
571
+ id,
572
+ usedPercent
573
+ } : {
574
+ id,
575
+ usedPercent,
576
+ resetsAt
577
+ });
578
+ }
538
579
  const usage = asRecord$4(root.usage);
539
580
  if (usage !== void 0) {
540
581
  const usedPercent = percentFrom(usage);
541
- if (usedPercent !== null) {
582
+ if (usedPercent !== null && !found.has("7d")) {
542
583
  const resetsAt = normalizeResetAt(usage.resetTime ?? usage.reset_at ?? usage.resetsAt);
543
584
  found.set("7d", resetsAt === void 0 ? {
544
585
  id: "7d",
@@ -596,7 +637,8 @@ const kimi = {
596
637
  },
597
638
  request(input) {
598
639
  const codingPlan = input.mode === "coding-plan";
599
- const base = normalizeBaseUrl(input.baseUrl) ?? (codingPlan ? KIMI_CODE_BASE_URL : MOONSHOT_BASE_URL);
640
+ const normalized = normalizeBaseUrl(input.baseUrl) ?? (codingPlan ? KIMI_CODE_BASE_URL : MOONSHOT_BASE_URL);
641
+ const base = codingPlan ? normalized.replace(/\/coding$/i, "") : normalized;
600
642
  return {
601
643
  url: codingPlan ? `${base}/coding/v1/usages` : `${base}/v1/users/me/balance`,
602
644
  headers: {
@@ -1224,7 +1266,15 @@ function providerCredentialRefs(input) {
1224
1266
  for (const [providerId, raw] of Object.entries(providers)) {
1225
1267
  const profile = asRecord(raw);
1226
1268
  if (profile === void 0) continue;
1227
- if (suggest(providerId, typeof profile.baseURL === "string" ? profile.baseURL : void 0) !== input.sourceId) continue;
1269
+ const baseUrl = typeof profile.baseURL === "string" ? profile.baseURL : void 0;
1270
+ if (suggest(providerId, baseUrl) !== input.sourceId) continue;
1271
+ if (input.sourceId === "kimi" && input.mode !== void 0) {
1272
+ const entry = input.config?.providers[providerId];
1273
+ const choice = entry?.mode;
1274
+ if (choice === "hidden") continue;
1275
+ const endpoint = entry?.baseUrl ?? input.config?.sources.kimi?.baseUrl ?? baseUrl;
1276
+ if ((choice === "api" || choice === "coding-plan" ? choice : kimiMode(providerId, endpoint)) !== input.mode) continue;
1277
+ }
1228
1278
  push(profile.apiKeyEnv);
1229
1279
  }
1230
1280
  return refs;
@@ -1850,6 +1900,8 @@ function createUsageState(ctx, deps = {}) {
1850
1900
  ...target,
1851
1901
  preferredRefs: providerCredentialRefs({
1852
1902
  sourceId: target.sourceId,
1903
+ mode: target.mode,
1904
+ config,
1853
1905
  deepseekSettings: readNamespace(ctx, "llm-deepseek"),
1854
1906
  piAiSettings: readNamespace(ctx, "llm-pi-ai")
1855
1907
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-usage-state",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "packageManager": "npm@11.19.1",
5
5
  "type": "module",
6
6
  "description": "DSH plugin: show account balance (API mode) or coding-plan quota (5h/7d/30d) for the model in use",
@@ -44,7 +44,7 @@
44
44
  "@deepseek-ai/dsh-typert-loader": "^0.2.0-rc.2",
45
45
  "@deepseek-ai/dsh-typert-registry": "^0.2.0-rc.2",
46
46
  "@deepseek-ai/schemastery": "^3.18.3",
47
- "@types/node": "^24.0.0",
47
+ "@types/node": "^26.6.4",
48
48
  "@types/react": "~18.3.1",
49
49
  "@types/react-dom": "~18.3.1",
50
50
  "@types/react-test-renderer": "18.3.1",