dsh-usage-state 0.4.5 → 0.4.7
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 +28 -1
- package/README.en.md +6 -4
- package/README.md +6 -4
- package/docs/adapters.md +5 -2
- package/lib/client.js +19 -5
- package/lib/index.js +73 -20
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.7] - 2026-10-17
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- 每次读取/描述凭据时获取当前宿主服务,修复插件早于凭据服务启动时永久缓存“服务不可用”的问题;服务激活或重载后不再继续使用直接回退凭据。DSH 0.2.1-rc.1 的已保存凭据优先级和可写状态因此得到保留。
|
|
14
|
+
|
|
15
|
+
### Compatibility
|
|
16
|
+
|
|
17
|
+
- 明确声明 DSH `^0.2.0-rc.2 || ^0.2.1-rc.1`,同步 dsh-settings peer;保留原开发锁定基线。新旧宿主的隔离 tarball 安装、启动、客户端设置装配及 RPC 均复验。
|
|
18
|
+
- CI 增加精确 0.2.1-rc.1 的真实 Typert host/browser 校验;测试 helper 显式选取 browser 条件,兼容上游新增的 Node/browser 双入口。
|
|
19
|
+
- 333 项本地测试通过,包括先失败后修复的服务延迟激活/替换回归;新旧平台各 18 项贡献/产物/宿主契约测试通过。真实账户与厂商接口不属于本次兼容性测试,余额使用本地 fixture。
|
|
20
|
+
|
|
21
|
+
## [0.4.6] - 2026-10-14
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Kimi Code provider(kimi-code/kimi-coding/kimi-for-coding)和api.kimi.com/api.kimi.ai自动选择Coding Plan用量,Moonshot保持API余额;显式模式选择优先。
|
|
26
|
+
- Kimi用量兼容官方客户端usages比例池(5h/7d/月总额度/编程月用量)及旧usage/limits计数;TIME_UNIT_MINUTE的300分钟归一为5h,新套餐无周额度时不虚构7d。
|
|
27
|
+
- Kimi Code完整API地址不再重复拼接coding路径;Moonshot与Kimi Code的provider凭据按模式筛选,HTTP200错误信封及非法读数不会覆盖成功值。
|
|
28
|
+
|
|
29
|
+
### Validation
|
|
30
|
+
|
|
31
|
+
- 新增provider→凭据→请求→RPC回归、月额度状态行渲染及真实tarball的Kimi预构建运行冒烟,纳入现有CI/Release的Node24与Node20门禁。
|
|
32
|
+
- 使用既有KIMI_CODING_API_KEY对官方接口完成只读实测,成功读取5h/7d窗口;未将密钥或账户响应写入仓库。月额度形态依官方客户端固定源码和去身份fixture验证。
|
|
33
|
+
|
|
9
34
|
## [0.4.5] - 2026-10-10
|
|
10
35
|
|
|
11
36
|
### Fixed
|
|
@@ -102,7 +127,9 @@
|
|
|
102
127
|
|
|
103
128
|
0.4.5起建立版本tag/Release;更早发布没有保存这些引用。0.3.0至0.4.4的比较端点来自各npm版本的gitHead;0.2系列引用对应实现提交,不能替代当时没有保存的正式发布产物。早期日期沿用历史版本记录。今后版本应按发布流程建立稳定引用。
|
|
104
129
|
|
|
105
|
-
[Unreleased]: https://github.com/takboo/dsh-usage-state/compare/v0.4.
|
|
130
|
+
[Unreleased]: https://github.com/takboo/dsh-usage-state/compare/v0.4.7...HEAD
|
|
131
|
+
[0.4.7]: https://github.com/takboo/dsh-usage-state/compare/v0.4.6...v0.4.7
|
|
132
|
+
[0.4.6]: https://github.com/takboo/dsh-usage-state/compare/v0.4.5...v0.4.6
|
|
106
133
|
[0.4.5]: https://github.com/takboo/dsh-usage-state/compare/3ad9a71e2f7e4b9a05c69bc529e396a364ae6aeb...v0.4.5
|
|
107
134
|
[0.4.4]: https://github.com/takboo/dsh-usage-state/compare/885541638ffa4da05b39bd1864085ba6d9acc288...3ad9a71e2f7e4b9a05c69bc529e396a364ae6aeb
|
|
108
135
|
[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
|
[](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml) [](https://www.npmjs.com/package/dsh-usage-state)
|
|
6
6
|
|
|
7
|
-
> **
|
|
7
|
+
> **Release status:** Current version **0.4.7** verifies compatibility with DSH **0.2.1-rc.1** and fixes credential-service lookup across startup ordering. See the [GitHub Release](https://github.com/takboo/dsh-usage-state/releases/tag/v0.4.7) for the tarball and checksum and the [Changelog](CHANGELOG.md#047---2026-10-17) 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
|
|
|
@@ -23,7 +23,7 @@ DeepSeek · ¥58.13
|
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
26
|
-
Requires DSH matching
|
|
26
|
+
Requires DSH matching `^0.2.0-rc.2 || ^0.2.1-rc.1` (both candidates revalidated). The prebuilt plugin declares Node ≥20. Install into the `web` profile you actually use.
|
|
27
27
|
|
|
28
28
|
Install from npm:
|
|
29
29
|
|
|
@@ -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
|
|
@@ -81,7 +83,7 @@ Provider settings and advanced controls:
|
|
|
81
83
|
|
|
82
84
|
The host schedules refreshes 2 seconds after a turn ends and has a 5-minute fallback timer. The browser reads the RPC mirror every 30 seconds. Ordinary polling initializes a missing or changed account identity, otherwise it does not shorten idle queries to once a minute. Successful requests have a default 60-second minimum; explicit refresh can bypass it, and active retries after failure are not throttled by that successful-request interval. Editing the idle interval reschedules the timer.
|
|
83
85
|
|
|
84
|
-
The plugin queries balances/quotas and reads the selected model and provider catalog. It writes no session logs and reports no readings to third parties. The host does not return stored key values to the browser; a key the user actively pastes is sent through DSH's credential service to the credential file in the selected home.
|
|
86
|
+
The plugin queries balances/quotas and reads the selected model and provider catalog. It writes no session logs and reports no readings to third parties. The host does not return stored key values to the browser; a key the user actively pastes is sent through DSH's credential service to the credential file in the selected home. Credential precedence follows the host: DSH 0.2.1-rc.1 Web/Desktop prefers saved credentials, with environment values as fallback; 0.2.0-rc.2 prefers the environment. The settings page reflects the host’s actual write permissions.
|
|
85
87
|
|
|
86
88
|
## Known limitations
|
|
87
89
|
|
|
@@ -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
|
|
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
|
[](https://github.com/takboo/dsh-usage-state/actions/workflows/ci.yml) [](https://www.npmjs.com/package/dsh-usage-state)
|
|
6
6
|
|
|
7
|
-
>
|
|
7
|
+
> **发布状态**:当前版本 **0.4.7**,已验证 DSH **0.2.1-rc.1** 兼容性,并修复启动顺序导致的凭据服务读取问题。安装包及校验文件见 [GitHub Release](https://github.com/takboo/dsh-usage-state/releases/tag/v0.4.7),详情见 [更新日志](CHANGELOG.md#047---2026-10-17)。
|
|
8
8
|
|
|
9
9
|
在 [DSH(DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness) 输入框上方显示当前模型供应商的**账户余额**或**套餐额度**。状态行与输入卡片同宽,保留原生统计行和上下文计量器。
|
|
10
10
|
|
|
@@ -23,7 +23,7 @@ DeepSeek · ¥58.13
|
|
|
23
23
|
|
|
24
24
|
## 安装
|
|
25
25
|
|
|
26
|
-
要求:DSH版本满足
|
|
26
|
+
要求:DSH版本满足 `^0.2.0-rc.2 || ^0.2.1-rc.1`(已复验这两个候选版本),运行预构建插件声明Node≥20,安装到正在使用的 `web` profile。
|
|
27
27
|
|
|
28
28
|
从npm安装:
|
|
29
29
|
|
|
@@ -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
|
## 截图
|
|
@@ -81,7 +83,7 @@ OpenCode Zen Go三个窗口,独立状态行位于输入框上方:
|
|
|
81
83
|
|
|
82
84
|
宿主默认在回合结束2秒后调度刷新,并有5分钟兜底定时器;浏览器每30秒拉取RPC镜像。普通轮询仅在读数缺失或有效身份变化时初始化查询,不把idle查询改成每分钟一次。成功请求的默认最小间隔为60秒,显式刷新可绕过,失败的主动重试不受该成功间隔限制;修改空闲间隔会重调定时器。
|
|
83
85
|
|
|
84
|
-
插件查询余额/额度并读取当前模型及供应商目录,不写会话日志,也不向第三方上报这些读数。宿主不把已保存的密钥值返回浏览器;用户主动粘贴的草稿经DSH凭据服务写入所用home
|
|
86
|
+
插件查询余额/额度并读取当前模型及供应商目录,不写会话日志,也不向第三方上报这些读数。宿主不把已保存的密钥值返回浏览器;用户主动粘贴的草稿经DSH凭据服务写入所用home的凭据文件。凭据优先级跟随宿主:DSH 0.2.1-rc.1 的 Web/桌面端优先使用已保存凭据,环境变量作为回退;0.2.0-rc.2 则优先使用环境变量。凭据权限以设置页实际返回为准。
|
|
85
87
|
|
|
86
88
|
## 已知限制
|
|
87
89
|
|
|
@@ -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
|
|
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
|
|
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 |
|
|
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
|
|
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
|
|
532
|
-
*
|
|
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
|
|
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
|
-
|
|
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;
|
|
@@ -1753,22 +1803,23 @@ function readNamespace(ctx, namespace) {
|
|
|
1753
1803
|
* refresh loop.
|
|
1754
1804
|
*/
|
|
1755
1805
|
function createCredentialLookup(ctx, fallback = {}) {
|
|
1756
|
-
const provider = ctx.get("credentials");
|
|
1757
1806
|
const platform = {
|
|
1758
1807
|
resolve: async (ref) => {
|
|
1759
|
-
if (provider === void 0) return void 0;
|
|
1760
1808
|
try {
|
|
1809
|
+
const provider = ctx.get("credentials");
|
|
1810
|
+
if (provider === void 0) return void 0;
|
|
1761
1811
|
return await provider.resolve(ref);
|
|
1762
1812
|
} catch {
|
|
1763
1813
|
return;
|
|
1764
1814
|
}
|
|
1765
1815
|
},
|
|
1766
1816
|
describe: async (ref) => {
|
|
1767
|
-
if (provider === void 0) return {
|
|
1768
|
-
configured: false,
|
|
1769
|
-
writable: false
|
|
1770
|
-
};
|
|
1771
1817
|
try {
|
|
1818
|
+
const provider = ctx.get("credentials");
|
|
1819
|
+
if (provider === void 0) return {
|
|
1820
|
+
configured: false,
|
|
1821
|
+
writable: false
|
|
1822
|
+
};
|
|
1772
1823
|
return await provider.describe(ref);
|
|
1773
1824
|
} catch {
|
|
1774
1825
|
return {
|
|
@@ -1850,6 +1901,8 @@ function createUsageState(ctx, deps = {}) {
|
|
|
1850
1901
|
...target,
|
|
1851
1902
|
preferredRefs: providerCredentialRefs({
|
|
1852
1903
|
sourceId: target.sourceId,
|
|
1904
|
+
mode: target.mode,
|
|
1905
|
+
config,
|
|
1853
1906
|
deepseekSettings: readNamespace(ctx, "llm-deepseek"),
|
|
1854
1907
|
piAiSettings: readNamespace(ctx, "llm-pi-ai")
|
|
1855
1908
|
})
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-usage-state",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
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",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=20",
|
|
10
|
-
"dsh": "
|
|
10
|
+
"dsh": "^0.2.0-rc.2 || ^0.2.1-rc.1"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"test": "node --import ./tests/support/client-render-hook.mjs --test",
|
|
@@ -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": "^
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"unrun": "^0.3.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@deepseek-ai/dsh-settings": "^0.2.0-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-settings": "^0.2.0-rc.2 || ^0.2.1-rc.1",
|
|
60
60
|
"@deepseek-ai/schemastery": "^3.18.3"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|