dsh-usage-state 0.4.4 → 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/README.md CHANGED
@@ -2,210 +2,113 @@
2
2
 
3
3
  **中文** | [English](README.en.md)
4
4
 
5
- 在 [DSH(DeepSeek Harness)](https://github.com/deepseek-ai) 里一眼看到你的**账户余额**或**套餐额度**——就在输入框上方、与输入卡片同宽的一行(原生统计行与其弹窗原样保留、不做替换)。
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
- > A minimal DSH plugin that shows your account **balance** (API mode) or **coding-plan quota** (5h / 7d / 30d) for the model you are using, on its own line right above the composer input.
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
- ```
10
- 输入框上方独立一行: z.ai / GLM · ◔ 5h 12% (4h0m) · ◔ 7d 59% (3d17h)
11
- DeepSeek · ¥58.13
12
- 悬停任意一段: Source DeepSeek · Mode API balance · Granted 0 · Topped up 58.13
9
+ 在 [DSH(DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness) 输入框上方显示当前模型供应商的**账户余额**或**套餐额度**。状态行与输入卡片同宽,保留原生统计行和上下文计量器。
10
+
11
+ ```text
12
+ z.ai / GLM · ◔ 5h 12% (4h0m) · ◔ 7d 59% (3d17h)
13
+ DeepSeek · ¥58.13
13
14
  ```
14
15
 
15
- ## 特性
16
+ ## 功能
16
17
 
17
- - **零配置可用**:按 provider 自动识别该用哪个数据源与模式,DSH 里配过的密钥会被自动复用,不填任何东西就能看到读数。
18
- - **账户级配置**:余额与额度是账户级的,所以每个供应商只配一次(`自动` / `API` / `Coding Plan` / `隐藏`),模型清单只作展示。
19
- - **始终可见一行**:独占一行、位于输入框上方(DSH 0.2 把它挂在 `conversation.input.dock`,与输入卡片同宽居中;原生统计行与上下文计量器原样保留、不做替换),不需要悬停或点击。
20
- - **失败不撒谎**:请求失败时保留上次成功值并标明「多久之前 + ⚠」,**绝不显示 0 或伪造数据**;密钥无效、接口报错、网络不可达会分别给出可读原因。
21
- - **悬浮提示**:每段文字悬停显示一行放不下的信息——数据源与模式、窗口的绝对重置时刻、余额的赠送/充值构成、失败原因与原始消息。
22
- - **中英双语**:跟随 DSH 的语言设置(`locale.preference`),设置页与状态行都不含硬编码文案。
23
- - **只做显示**:只有余额与额度,没有成本统计、价格目录、历史账单、预算、峰谷计价。
18
+ - 已识别的供应商默认使用“自动”,复用DSH已有凭据;每个provider配置一次,模型列表用于参考。
19
+ - 显示余额、已用百分比、重置倒计时和SVG进度环;默认≥80%琥珀、≥95%红,可调整阈值和隐藏进度环。
20
+ - 同一有效账户身份的源请求失败时保留上次成功读数并标记陈旧、年龄和原因;RPC传输失败也会明确标出旧值。
21
+ - 悬停查看数据源、模式、重置时刻及余额构成;中英界面跟随DSH语言设置。
22
+ - 只显示账户当前读数,不做成本归因、价格目录、预算或历史账单。
24
23
 
25
24
  ## 安装
26
25
 
27
- **前置条件**:DSH `0.2.0-rc.2` 或更新的 0.2 线、Node ≥ 20,装进 `web` profile。包内**自带预构建的 `lib/`**,安装时没有构建步骤。
28
- **用 0.1 线请装 `0.3.2`**:DSH 0.2 换掉了整套设置 API(`settingsScope` / `settings.register` → `configForms` / 插件自带 `Config`),两套 API 没有重叠,所以 `0.4.0` 起只支持 0.2 线(原因见 [`docs/design-changelog.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/design-changelog.md) 修订 17)。
26
+ 要求:DSH版本满足 `>=0.2.0-rc.2 <0.3.0-0`,运行预构建插件声明Node≥20,安装到正在使用的 `web` profile。
27
+
28
+ 从npm安装:
29
29
 
30
30
  ```bash
31
- # 1) 安装(npm 包)
32
31
  dsh plugin --profile web add dsh-usage-state
33
-
34
- # 2) 重启 DSH —— 插件的 bundle patch 只在启动时读取
35
-
36
- # 3) 卸载
37
- dsh plugin --profile web remove dsh-usage-state
32
+ # 安装后重启DSH;bundle patch在启动时读取
38
33
  ```
39
34
 
40
- 直接从 GitHub 安装(内容与 npm 上的一致):
35
+ 也可从GitHub安装:
41
36
 
42
37
  ```bash
43
38
  dsh plugin --profile web add github:takboo/dsh-usage-state
44
39
  ```
45
40
 
46
- 本地开发时可直接装目录(宿主半边改完同样要重启 DSH):
41
+ 本仓库提交预构建产物,供DSH的GitHub安装路径直接使用。浮动GitHub分支可能领先于npm;复现问题时记录安装来源和版本/提交。不要把这一行为推广到所有npm Git安装。
47
42
 
48
- ```bash
49
- dsh plugin --profile web add /path/to/dsh-usage-state
50
- ```
43
+ 也可通过 [dsh-market](https://github.com/dsh-market/dsh-market) 搜索 `usage state` 或 `takboo` 安装;安装后核对实际来源与版本。本插件已进入 [精选列表](https://awesome-dsh-plugin.com);目录更新的可见时间取决于所用区域与成功构建。
51
44
 
52
- 也可以在 [dsh-market](https://github.com/dsh-market/dsh-market) 里搜索 `usage state`(或 `takboo`)一键安装——本插件已收录在精选列表 [awesome-dsh-plugin](https://awesome-dsh-plugin.com) 的「用量与计费」分类。
53
-
54
- 装好后界面没有出现?见 [`docs/adapters.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md) 末尾的排查表。
45
+ DSH0.1使用历史版本0.3.2;该版本不适配0.2。0.4.0有RPC挂载缺陷,应升级至**最新兼容版**。完整版本记录见 [更新日志](CHANGELOG.md)。
55
46
 
56
47
  ## 快速开始
57
48
 
58
- 1. 打开 **设置 → 用量状态**:DSH 里配置的每个供应商一行。
59
- 2. 保持默认的 **自动** 即可(它会识别数据源与主模式);需要时改成 `API` / `Coding Plan` / `隐藏`,或用 ↑↓ 调整顺序。
60
- 3. 该供应商的账户读数会出现在输入框上方、与输入卡片同宽的一行。
61
-
62
- 若某个数据源需要端点或密钥(例如自建的 Sub2API、或尚未配置的 z.ai),展开该行的 **高级**:可覆盖数据源、填接口地址、指定凭据名、粘贴密钥(写入 DSH 凭据库)。
49
+ 1. 打开 **设置→用量状态**,查看供应商、模型和检测到的数据源。
50
+ 2. 对已识别供应商保留“自动”,或选择其支持的API、Coding Plan、隐藏。
51
+ 3. 自建Sub2API在高级区填写实例地址;已配置模型的当前读数显示在输入框上方。
52
+ 4. “立即刷新”用于主动查询;出错先查看设置页原因及 [排查指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/troubleshooting.md)。
63
53
 
64
- 供应商名过长时,卡片头部只截断灰色的 provider id(悬停显示全文),右侧的 `↑ ↓ 自动 / Coding Plan / 隐藏` 不会换行。
54
+ 高级区提供数据源、端点、凭据名和密钥写入入口。显式provider覆盖优先于旧source默认;凭据描述与读取共用候选顺序,保存优先写显式ref。固定端点不试镜像,配置或凭据身份变化后清除旧身份读数。只读配置与凭据写入权限分别处理,保存/清除过程会显示失败并禁用重复操作。
65
55
 
66
- ## 截图
67
-
68
- 输入框上方独占一行的账户读数(图示为 OpenCode Zen Go 的三个窗口;卡片下方那排是 DSH 原生统计,两者互不干扰。悬停任意一段可看数据源、模式与绝对重置时刻):
69
-
70
- ![输入框上方独占一行的账户读数](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/status-line.webp)
71
-
72
- 设置页:每个 provider 一行,默认「自动」会识别数据源与主模式:
73
-
74
- ![设置页的供应商列表](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/settings-providers.webp)
56
+ ## 数据源
75
57
 
76
- 展开「高级」可覆盖数据源与端点、指定凭据名,或写入密钥(写入 DSH 凭据库):
77
-
78
- ![高级区](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/settings-advanced.webp)
79
-
80
- ## 支持的数据源
81
-
82
- | 数据源 | API 模式 | Coding Plan 模式 | 凭据 |
58
+ | 来源 | API读数 | Coding Plan读数 | 主要凭据 |
83
59
  |---|---|---|---|
84
- | DeepSeek 官方 | 余额(CNY / USD) | —(官方无 coding plan) | `DEEPSEEK_API_KEY` |
85
- | z.ai / 智谱 GLM | — | 5h / 7d 已用 % | `ZAI_API_KEY` 等 |
86
- | Kimi 国内版 | Moonshot 按量余额 | Kimi Code 订阅窗口 | `MOONSHOT_API_KEY` / `KIMI_CODING_API_KEY` |
87
- | OpenCode Zen Go | — | 5h / 7d / 30d 已用 % | `OPENCODE_GO_API_KEY` / `OPENCODE_API_KEY` |
88
- | Sub2API(自建网关) | 余额 / key 配额 | `rate_limits[]` 的 5h / 7d | `SUB2API_API_KEY` + 实例地址 |
89
-
90
- - **z.ai 分区域**:coding plan 的 key 只在自己区域的站点有效(国内 `open.bigmodel.cn` / 国际 `api.z.ai`)。默认国内站,失败时自动镜像重试;也可在设置里钉死端点。
91
- - **OpenCode Zen Go**:读 `opencode.ai/zen/go/v1/usage` 的 `rolling` / `weekly` / `monthly`;通往同一账户的两条 DSH 路由(内置 `opencode-go` 与自定义 `opencode-go-deepseek`)只产生一个读数、只发一次请求。无订阅或密钥无效时报鉴权失败,而非 0%。
92
- - **其他厂商**(Claude Pro/Max、MiniMax、OpenRouter、Codex、Antigravity、Volcengine Ark…)未实现,但适配器契约与候选清单已备好,见 [`docs/adapters.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md)。
60
+ | DeepSeek官方 | CNY/USD余额 | — | DEEPSEEK_API_KEY |
61
+ | z.ai / 智谱GLM | — | 5h / 7d已用% | ZAI_API_KEY等 |
62
+ | Kimi / Moonshot | 人民币余额 | Kimi Code订阅窗口 | MOONSHOT_API_KEY / KIMI_CODING_API_KEY |
63
+ | OpenCode Zen Go | — | 5h / 7d / 30d已用% | OPENCODE_GO_API_KEY / OPENCODE_API_KEY |
64
+ | Sub2API | 余额或key配额 | 按实例响应,如5h / 1d / 7d / 30d | SUB2API_API_KEY及实例地址 |
93
65
 
94
- ## 显示与交互
66
+ Kimi Code 的 `kimi-code`、`kimi-coding`、`kimi-for-coding` provider,以及声明 `api.kimi.com` / `api.kimi.ai` 的自定义 provider,会自动读取套餐用量。支持 5h、7d、月总额度及编程月用量,仅展示账户实际返回的窗口。Moonshot 国内/国际 API 继续显示人民币余额;可在设置中显式选择模式。
95
67
 
96
- **位置**:输入框**上方**、与输入卡片同宽的独立一行(DSH 0.2 的 `conversation.input.dock` 槽位)。读数**始终可见**,不依赖悬停,也不需要点击;窗口过窄时在段与段之间折行,不会裁切。
68
+ 百分比表示**已用**。z.ai的key分国内/国际区域;默认国内地址,失败可尝试镜像。当前缓存按数据源与模式合并,不能将不同密钥/实例视为已支持独立多账户。厂商端点、字段和扩展步骤见 [适配器指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md)。
97
69
 
98
- **元素**:供应商标签 · 余额金额 + 币种 · 各窗口(5h / 7d / 30d):SVG 进度环 + 已用百分比 + 重置倒计时 · 阈值变色(默认 ≥80% 琥珀、≥95% 红,可在设置里改;颜色作用于整段并传导到圆环)。
99
-
100
- 进度环取平台上下文计量器(`ContextMeter`)同款几何(14×14 圆环、弧从 12 点开始),替代早期的 `█`/`░` 文字条——矢量没有字形度量,不会因字体回落而撑爆排版;设置里可整体关闭。
101
-
102
- **口径**:百分比一律是**已用**(与 z.ai / Claude 官方一致);API 模式显示余额,Coding Plan 模式显示该数据源实际提供的窗口(z.ai 与 Sub2API 是 5h / 7d,OpenCode Zen Go 多一个 30d)。
70
+ ## 截图
103
71
 
104
- **降级**:
72
+ OpenCode Zen Go三个窗口,独立状态行位于输入框上方:
105
73
 
106
- | 情况 | 显示 |
107
- |---|---|
108
- | 未配置 | 灰色「未配置」 |
109
- | 自建源缺端点 | 「需要先填写接口地址」 |
110
- | 请求失败(有旧值) | 旧值 + `12m ago` + `⚠`,悬停给出原因 |
111
- | 首次失败(无旧值) | 只显示本地化原因(不显示 0) |
74
+ ![输入框上方的账户读数](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/status-line.webp)
112
75
 
113
- ## 刷新与网络
76
+ 每个provider一行的设置页,以及高级设置:
114
77
 
115
- - 回合结束后 **2 秒**刷新(等 provider 结算),空闲时每 **5 分钟**兜底。
116
- - 同一数据源 **60 秒**内不重复发真实请求;并发调用共享同一次在途请求;失败的请求不节流(可立即重试)。
117
- - 刷新间隔可在设置里调整。
78
+ ![供应商设置](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/settings-providers.webp)
118
79
 
119
- ## 凭据与隐私
80
+ ![高级设置](https://raw.githubusercontent.com/takboo/dsh-usage-state/main/assets/screenshots/settings-advanced.webp)
120
81
 
121
- - 凭据探测顺序:设置页覆盖 → 供应商声明的 `apiKeyEnv`(`llm-deepseek` / `llm-pi-ai`)→ 数据源内置的 ref 名 → DSH 凭据库。
122
- - 密钥在设置页写入 **DSH 凭据库**(`~/.dsh/.credentials.yaml`);**插件自身不保存明文**。客户端只拿到「是否已配置 / 来源」,永远拿不到密钥值。
123
- - 环境变量提供的密钥是只读的:界面会禁用输入框并说明原因。
124
- - 插件读取的内容只有余额/额度数字与会话当前使用的模型,**不写会话日志**,也不上报任何数据。
82
+ ## 刷新与凭据
125
83
 
126
- ## 兼容性
84
+ 宿主默认在回合结束2秒后调度刷新,并有5分钟兜底定时器;浏览器每30秒拉取RPC镜像。普通轮询仅在读数缺失或有效身份变化时初始化查询,不把idle查询改成每分钟一次。成功请求的默认最小间隔为60秒,显式刷新可绕过,失败的主动重试不受该成功间隔限制;修改空闲间隔会重调定时器。
127
85
 
128
- - **只支持 DSH 0.2 线**:`engines.dsh` = `>=0.2.0-rc.2 <0.3.0-0`(市场读它做徽标与"可安装"判定)。0.2 之前的宿主请装 `0.3.2`。
129
- - `peerDependencies` 里有 `@deepseek-ai/dsh-settings`(`^0.2.0-rc.2`)与 `@deepseek-ai/schemastery`(`^3.18.2`):前者是**故意的**——平台的运行时安装闸门只读 `@deepseek-ai/dsh*` 的 peer,声明它可以让 0.1.x 宿主在**安装时**就拒绝,而不是装上之后把启动搞崩;两者都由平台提供,pnpm 可能为此打一行 `missing peer` 警告,属预期。
130
- - 版本 `0.4.4`:纯文档发布——README 与实际行为对齐、`CHANGELOG.md` 随包发布;`lib/` 与 `0.4.3` 逐字节相同。
131
- - 版本 `0.4.3`:进度显示改为**平台同款 SVG 圆环**(替代 `█`/`░` 文字条,阈值变色经颜色传导到环上)、排版照抄平台统计行字号、折行不再留下孤立的 `·`(修订 21–23)。
132
- - 版本 `0.4.2`:状态行迁到 `conversation.input.dock`,成为**输入框上方的独立一行**(0.1 时代它在输入框之下,而 0.2 把那个位置改成了与平台统计、上下文计量器共排的一排 pill——见修订 19/20)。
133
- - 版本 `0.4.1`:修 `0.4.0` 的读数缺失——客户端 RPC contribution 的参数 codec 少了 0.2 要求的 `create()`,`remote.usageState` 因此从未挂载,界面表现为**没有任何读数**并误报 `Mode not supported`。同一轮还让挂载失败不再被静默吞掉、catalog 未到时改说「读取中」,并把**真实 0.2 registry 契约测试**写进单测(修订 18)。**请用 `0.4.1`,不要用 `0.4.0`。**
134
- - 版本 `0.4.0`:迁到 0.2 原生设置模型(`Config` + `configForms`,宿主侧 `configEditor` 读跨条目配置)。变更明细见 [CHANGELOG](CHANGELOG.md),验证方式与结论见 [`docs/release.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/release.md)、决策见 [`docs/design-changelog.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/design-changelog.md) 修订 17。
135
- - 版本 `0.3.2`(0.1 线末版):仅放宽 `engines.dsh`;**它声称的 0.2 兼容是错的**(0.3.2 在 0.2.0-rc.2 上会因 `settingsScope` 不存在而让整个 web 启动失败)。
136
- - 同名发布在 npm([`dsh-usage-state`](https://www.npmjs.com/package/dsh-usage-state)),也可从 GitHub 直接安装。
86
+ 插件查询余额/额度并读取当前模型及供应商目录,不写会话日志,也不向第三方上报这些读数。宿主不把已保存的密钥值返回浏览器;用户主动粘贴的草稿经DSH凭据服务写入所用home的凭据文件。环境变量值优先,通常不能在设置页修改。
137
87
 
138
88
  ## 已知限制
139
89
 
140
- - **Kimi、Sub2API 未经真机验证**(本机无凭据),代码与单测已就绪;Sub2API 的 `/v1/usage` 属未文档化接口,已按易错接口做容错。
141
- - **阈值变色(琥珀/红)尚无真机视觉样本**:代码已实现且被单测钉住,但真机读数长期低于阈值;把设置里黄色阈值临时改成 10 即可确认。
142
- - **点击状态行不会打开设置**(客户端没有公开的"打开设置面板"服务);细节通过悬浮提示呈现。
143
- - **只在输入框上方展示,不覆盖历史**:状态行给出的是账户**当前**读数;插件不按回合、也不按时间保存历史读数,因此翻看旧回合时看不到"当时的余额"。若将来要做,会是以时间轴(而不是回合)为口径的单独决定。
144
- - 完整清单见 [`docs/backlog.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md)。
90
+ 完整整改状态及验证范围见 [待办清单](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md)。
145
91
 
146
- ## 开发
92
+ - **旧版npm0.4.4及更早版本**:仍有provider凭据覆盖、pin/缓存身份、legacy隐藏、默认排序和失败展示缺陷;Moonshot≥100元仍会被除以100,DeepSeek非法金额仍可能变0。这些已在0.4.5修复;使用旧版请升级。旧版金额应与 [官方接口](https://platform.kimi.com/docs/api/balance.md) 核对。
93
+ - **账户范围**:仍按source+mode选择一个目标;配置身份隔离不等于新增独立多账户支持。
94
+ - **未验证范围**:Kimi Code月额度真实账户、Sub2API真实账户、高阈值视觉、真实凭据写入和OpenCode非零路径仍缺活体验证;本地交互测试不代替这些验收。
95
+ - **范围**:无模型选择或隐藏时可以不显示;点击状态行不打开设置;不保存历史读数。
147
96
 
148
- ```bash
149
- npm install # 若 ~/.npm 不可写:npm install --cache /tmp/npm-cache
150
- npm test # node:test 直接跑 .ts / .tsx(需要 Node >= 22.6)
151
- npm run typecheck # tsc --noEmit
152
- npm run build # tsdown → lib/(宿主 index.js + typert.js,浏览器 client.js)
153
- npm run watch # 只重建 client.js;客户端会被 HMR 热替换,无需刷新页面
154
- ```
97
+ ## 开发与文档
155
98
 
156
- **本地回路(不需要发版)**:`scripts/dev-local.sh` 会在 `/tmp/dsh-dev` 建一个**一次性 profile**,把本仓库以 `link:` 装进去并起一个独立端口的宿主;你的真实 profile 完全不受影响。
99
+ 开发兼容下界为Node22.18+的22线或Node24.11+;规范构建固定Node **24.21.0**([.node-version](https://github.com/takboo/dsh-usage-state/blob/main/.node-version))和npm **11.19.1**(packageManager),高于运行产物声明。基本检查:
157
100
 
158
101
  ```bash
159
- npm run build # 先有 lib/
160
- scripts/dev-local.sh # 建/复用 dev profile + 起宿主,打印带 token 的 URL
161
- # 另一个终端:
162
- npm run watch # 保存即重建 client.js
102
+ npm ci
103
+ npm run typecheck
104
+ npm run build
105
+ npm test
163
106
  ```
164
107
 
165
- 客户端改动由 `dsh-client-hmr` 热替换(宿主每 500ms 轮询 bundle,经 `/plugins/events` 通知浏览器重载模块)——**不用重启、不用发版**;宿主改动(`src/host/**`、`src/index.ts`、`cordis.patch.yml`)需要重启该脚本。
166
-
167
- ⚠️ **这个 profile 没有凭据**:`$DSH_HOME/.credentials.yaml` 是 home 级的,全新 home 里没有 key,也没有你其它插件(**字体插件会改变排版所依赖的字形度量**)。所以它只适合**结构/宿主侧**核对。要看**实际观感**,把同一份构建装进已有 key 与插件的 profile,同样不用发版:
168
-
169
- ```bash
170
- npm pack --pack-destination /tmp --cache /tmp/npm-cache
171
- dsh plugin --profile web add /tmp/dsh-usage-state-<version>.tgz # file: 安装
172
- dsh plugin --profile web add "$PWD" # 想热迭代就用 link:
173
- dsh plugin --profile web add dsh-usage-state@<已发布版本> # 回到线上版本
174
- ```
175
-
176
- 只有当你**亲眼确认**改动可用之后,才值得动版本号与 npm 发布。
177
-
178
- 宿主机改动需要重启 DSH;客户端改动 `npm run watch` 即可。`lib/` 产物**必须提交进仓库**——`dsh plugin add github:...` 直接装仓库、没有构建步骤(`npm test` 里的构建守卫会检查信封、require 白名单与 `exports` 指向)。
179
-
180
- ```
181
- src/host/ 宿主:数据源适配器、缓存调度、凭据、设置、RPC
182
- src/client/ 浏览器:词典、状态行、设置页、状态镜像
183
- src/shared/ 两端共用:类型、配置、provider 解析、显示逻辑
184
- lib/ 构建产物(提交,供 github 安装)
185
- tests/ 与 src 对应;tests/build 校验的是产物本身
186
- ```
187
-
188
- ## 文档
189
-
190
- | 文档 | 内容 |
191
- |---|---|
192
- | [CHANGELOG](CHANGELOG.md) | 按版本的变更记录 |
193
- | [`docs/architecture.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/architecture.md) | 代码地图、决策→实现→测试→验证追溯 |
194
- | [`docs/adapters.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/adapters.md) | 添加数据源:契约、四步流程、约定与坑、排查表 |
195
- | [`docs/design-consensus.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/design-consensus.md) | 当前有效的设计共识 |
196
- | [`docs/design-changelog.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/design-changelog.md) | 设计修订记录(每条决策变更的来龙去脉) |
197
- | [`docs/release.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/release.md) | 发布流程、端到端验证、人工验收清单、市场收录 |
198
- | [`docs/platform-notes.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/platform-notes.md) | DSH 平台行为的实测事实 |
199
- | [`docs/backlog.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/backlog.md) | 待办、候选数据源、明确不做的否决护栏 |
200
- | [`docs/research/README.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/research/README.md) | 只读侦察报告索引(各厂商接口、被替代插件剖析、DSH RPC 契约) |
201
-
202
- ## 致谢与参考
108
+ 本地profile、watch和HMR前提见 [开发指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/development.md)。按任务查找架构、设计、发布、审计和研究材料见 [文档导航](https://github.com/takboo/dsh-usage-state/blob/main/docs/index.md)。
203
109
 
204
- - **[`dsh-cost-meter`](https://github.com/Han-1413141/dsh-cost-meter)**(作者 Han-1413141,MIT 许可):本插件是它的**简化替代品**——只保留「看余额 / 看 Coding Plan 额度」这个展示需求,砍掉计费、价格目录、历史账单、预算与峰谷提醒等全部逻辑(见上面的「只做显示」)。
205
- 数据源端点、响应字段语义与若干兼容陷阱(OpenCode Zen Go 必须带浏览器 UA、z.ai 用 HTTP 200 + `{success:false}` 表达鉴权失败、旧 `coding_plan/usage` 兜底路径、sub2api 的 `rate_limits[]` 形态等)来自对 `dsh-cost-meter@1.7.28` 的**只读分析**,记录见 [`docs/research/dsh-cost-meter-analysis.md`](https://github.com/takboo/dsh-usage-state/blob/main/docs/research/dsh-cost-meter-analysis.md)。本仓库的实现是独立编写的 TypeScript,不是对其源码的照搬;但那些行为语义确实源自上述分析,应归功于上游。
206
- 若上游作者认为某处需要更明确的署名或授权,请提 issue,我会立刻调整。
207
- - **[DSH(DeepSeek Harness)](https://github.com/deepseek-ai)**:宿主平台。插件使用它的设置命名空间、凭据库、Typert RPC、插槽系统与 UI 原语(`@deepseek-ai/dsh-client-ui-primitives` 等)。
110
+ ## 致谢与许可
208
111
 
209
- ## 许可
112
+ 感谢 [dsh-cost-meter](https://github.com/Han-1413141/dsh-cost-meter)(Han-1413141,MIT)提供的数据源行为参考。本实现独立编写;端点、字段语义与兼容陷阱的溯源见 [上游分析](https://github.com/takboo/dsh-usage-state/blob/main/docs/research/dsh-cost-meter-analysis.md)。宿主平台为DSH,使用其设置、凭据、RPC、插槽和UI原语。
210
113
 
211
- [MIT](LICENSE)
114
+ [MIT](LICENSE)。问题和署名建议通过 [Issues](https://github.com/takboo/dsh-usage-state/issues) 提交。
package/docs/adapters.md CHANGED
@@ -1,84 +1,72 @@
1
- # 添加一个数据源(Adapter)
1
+ # 适配器指南
2
2
 
3
- `dsh-usage-state` 只实现五家数据源(DeepSeek、z.ai / GLM、Kimi、OpenCode Zen Go、Sub2API)。其余厂商靠 adapter 扩展——加一个适配器只需要两个文件加一行注册,插件其余部分(设置页列出的数据源、轮询目标、状态行渲染、双语文案)会自动跟上。
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
+
7
+ 完整定义以 [sources/types](https://github.com/takboo/dsh-usage-state/blob/main/src/host/sources/types.ts) 为准。下列是主要字段:
6
8
 
7
9
  ```ts
8
- export interface UsageSource {
9
- id: string // 稳定 id,写进设置与快照,如 'deepseek'
10
- displayName: string // 设置页显示名
11
- modes: readonly UsageMode[] // 能服务哪些模式:'api' | 'coding-plan'
12
- credentialRefs(mode): string[] // 按顺序探测的凭据名(POSIX 环境变量风格)
13
- defaultBaseUrl(mode): string | undefined // 无覆盖时使用的端点
14
- requiresBaseUrl?: boolean // true = 必须由用户填端点(自建服务)
15
- request(input: RequestInput): UsageRequest // 纯构造:url + headers
16
- fallbackRequests?(input): UsageRequest[] // 可选:区域镜像,按序重试
17
- parse(payload: unknown, mode): UsageReading // 纯解析:payload → 归一化读数
10
+ interface UsageSource {
11
+ id: string
12
+ displayName: string
13
+ modes: readonly UsageMode[]
14
+ credentialRefs(mode: UsageMode): readonly string[]
15
+ defaultBaseUrl(mode: UsageMode): string | undefined
16
+ requiresBaseUrl?: boolean
17
+ requiresApiKey?: boolean
18
+ request(input: RequestInput): UsageRequest
19
+ fallbackRequests?(input: RequestInput): readonly UsageRequest[]
20
+ parse(payload: unknown, mode: UsageMode): UsageReading
18
21
  }
19
22
  ```
20
23
 
21
- `request` 与 `parse` **刻意分离**:`parse` 不碰网络、不读时钟,所以每个厂商的返回结构都能用 fixture 无网单测。
22
-
23
- **端点优先级**(`resolveProvider` → `UsageTarget.baseUrl` → `read.ts`):
24
-
25
- 1. 插件设置里显式填的端点(`pinnedBaseUrl: true`,**不**再试镜像);
26
- 2. 供应商在 DSH 里声明的 `baseURL` 的 **origin**(例如 `https://api.z.ai/api/paas/v4` → `https://api.z.ai`;各适配器自己拼路径,所以只取 origin),仍会试镜像;
27
- 3. 适配器的 `defaultBaseUrl(mode)`;
28
- 4. 若前两者都没有且 `fallbackRequests` 存在,则按序尝试镜像;全部失败时抛出**最有信息量**的失败("有响应"的优先于"连不上"的)。
29
-
30
- 区域类厂商(z.ai 国内/国际)就该用 `fallbackRequests`:key 只在自家站点有效,而错误站点返回的是**鉴权失败**而不是重定向。
31
-
32
- ## 加一个适配器的步骤
33
-
34
- 1. 复制 `src/host/sources/_template.ts` 为 `<id>.ts`,填四处:`id` / `displayName` / `modes` / `credentialRefs`,以及 `request` 与 `parse`(有区域镜像时再加 `fallbackRequests`)。
35
- 2. 先写测试:`tests/sources/<id>.test.ts`,用真实返回体做 fixture,覆盖
36
- - 正常形态(余额 / 窗口)
37
- - 字段缺失、`null`、字符串数字
38
- - 百分比口径(0..1 还是 0..100)
39
- - 重置时间的三种形态(unix 秒 / 毫秒 / ISO)
40
- - 不可解析时报 `SourceError('parse')`;**若厂商用 HTTP 200 返回错误信封,先识别它**(见下)
41
- 3. 在 `src/host/sources/index.ts` 的 `ALL_SOURCES` 里加一行。
42
- 4. `npm test` 与 `npx tsc --noEmit` 应该全绿;`docs/research/` 里已有的接口侦察可以补充你的注释。
43
-
44
- ## 约定与坑
45
-
46
- - **归一化工具**(`src/host/sources/normalize.ts`):
47
- - `toFiniteNumber` — 数字与数字串 → 有限数;其余 `undefined`(空串也算缺失)。
48
- - `clampPercent` — 已知 0..100 时用它(如 z.ai 的 `percentage`)。
49
- - `normalizePercent` — 口径不明时用它:`<= 1` 视为小数(`1` → 100%),否则视为百分数。**注意这个歧义**,口径明确时不要用它。
50
- - `normalizeResetAt` — unix 秒 / 毫秒 / 数字串 / ISO → epoch 毫秒;非法 → `undefined`(绝不给假日期)。
51
- - `normalizeBaseUrl` — 去尾斜杠与尾部 `/vN`(用户常把 `.../v1` 贴进端点配置)。
52
- - **只抛 `SourceError`**:`kind` 决定界面文案(`config` / `auth` / `http` / `network` / `parse`)。HTTP 状态与网络异常由 `src/host/read.ts` 统一归类,适配器只需负责 `parse`。
53
- - **HTTP 200 里的错误信封**:不少国内厂商这么做(z.ai 是 `{success:false,code:1000,msg:'身份验证失败。'}`,另有 `{error:{code,message}}` 变体)。**不识别它就会把"密钥无效"报成"返回内容无法解析"**——真机上正是这么踩过一次。`code` 1000 视为鉴权失败 → `auth`,其他 → `http`,并把厂商原文放进 `message`(界面会连同本地化文案一起显示)。
54
- - **窗口 id 用规范键**:`5h` / `1d` / `7d` / `30d`。状态行按 5h → 1d → 7d 排序显示,未知 id 原样显示(文案在客户端词典,`window.<id>`,缺键时回退显示 id)。
55
- - **百分比一律是"已用"**(0..100,保留一位小数),与 z.ai / Claude 官方口径一致。
56
- - **不去重、不缓存**:`resolveTargets` 已按 `source + mode` 去重,`UsageStateStore` 已做最小间隔与在途去重。
57
- - **不要 import 平台包**:宿主半边在 `link:` 装载时解析不到 `@deepseek-ai/*`,请用 `ctx` 的结构化类型(见 `src/index.ts` 的 `PluginContextLike`)。
58
-
59
- ## 已实现的数据源与坑
60
-
61
- > 下表各厂商的端点、字段语义与陷阱,主体来自社区侦察与被替代插件 `dsh-cost-meter@1.7.28` 的只读分析([`research/dsh-cost-meter-analysis.md`](research/dsh-cost-meter-analysis.md));实现是独立编写的,署名与出处见 README 的「致谢与参考」。
62
-
63
- | 数据源 | 模式 | 端点 | 关键坑 |
64
- |---|---|---|---|
65
- | DeepSeek 官方 | API | `GET {base}/user/balance` | 多币种返回顺序**不稳定**,固定取首条会让余额在真值与 0 之间跳;按"优先有余额 → 优先 CNY"挑选。官方无 coding plan、无窗口 |
66
- | z.ai / 智谱 GLM | Coding Plan | `GET {base}/api/monitor/usage/quota/limit`,默认 `https://open.bigmodel.cn`,镜像 `https://api.z.ai` | 主形态按 `unit` 映射:`3` → 5h、`6` → 7d;`TIME_LIMIT` 是月度 MCP 额度**不能**当编码窗口;`percentage` 已是 0..100;`unit` 缺失时按 `nextResetTime` 升序补位(0% 滚动窗口不带重置时间)。**鉴权失败是 HTTP 200 + `{success:false,code:1000}`**,且国内/国际的 key 互不通用(实测确认)→ 用 `fallbackRequests` 试镜像。端点非官方文档,社区逆向所得,已保留旧 `plans[]` 与扁平窗口两种兜底形态 |
67
- | Kimi / Moonshot | API + Coding Plan | `GET {base}/v1/users/me/balance`(余额)/ `GET {base}/coding/v1/usages`(订阅) | 一家两种读法:按模式分派端点与密钥。编程套餐端点**必须**带 `user-agent: KimiCLI/1.6`,否则拒绝。Moonshot 余额的"分/元"单位无字段可辨,当前沿用 `>= 100 视为分` 的启发式——**用真实 key 复核过再信任绝对值** |
68
- | OpenCode Zen Go | Coding Plan | `GET https://opencode.ai/zen/go/v1/usage` | **必须带浏览器 UA**(否则 Cloudflare error 1010 → 403,实测)。根对象是 `usage.{rolling,weekly,monthly}`(文档写作 `data.usage`,两种都接受),字段 `percent` 已是 0..100 已用百分比(用 `clampPercent`,**不要**用 `normalizePercent`,否则 `1` 会变成 100%),`resetsAt` 是 ISO 串。窗口名 `rolling/weekly/monthly` → 规范键 `5h/7d/30d`。401/403 = 无订阅或密钥无效(**不是** 0%)。`resolveProvider` 会把 provider 声明的 `baseURL` 归一成 origin、`normalizeBaseUrl` 会剥掉尾部 `/vN`,所以请求前还要再剥掉 `/zen/go` 尾段,否则出现 `/zen/go/zen/go/v1/usage` |
69
- | Sub2API(自建) | API + Coding Plan | `GET {base}/v1/usage` | 未文档化的内部接口,字段曾出现前后端漂移;所有字段可选、未知结构降级。一个接口覆盖两种模式:`quota.remaining` 或钱包 `balance` 视为 USD 余额,`rate_limits[]` 的 `window` 直接是 `5h`/`1d`/`7d`(美元计价,百分比自算 `used/limit`)。`requiresBaseUrl: true` |
70
-
71
- ## 候选数据源
72
-
73
- 以下接口已侦察确认存在但**尚未实现**——候选清单、各家的"为什么还没做"与优先级建议统一维护在 [`backlog.md`](backlog.md) §3,此处不重复。新增适配器前先读它,再回到上面的「加一个适配器的步骤」。
74
-
75
- ## 排查
76
-
77
- | 现象 | 原因 |
78
- |---|---|
79
- | 状态行显示"缺少密钥或接口地址" | 该 source 的所有候选凭据都未配置,或 `requiresBaseUrl` 的源没填端点。设置页每行会显示候选凭据名与配置状态 |
80
- | 显示"密钥无效" | 请求返回 401/403,或厂商在 HTTP 200 里返回鉴权失败信封(z.ai 的 `code:1000`)。**先确认 key 与站点区域匹配**(国内 key 打国际站必然失败) |
81
- | 显示旧值 + ⚠ | 最近一次刷新失败,展示的是上次成功值(鼠标悬停可看原因) |
82
- | 记录里有数据但界面为空 | 该模型在设置页是 `隐藏`,或模型不在 `models` 配置里(未配置 → 状态行不显示) |
83
- | `dsh plugin add github:...` 装完没有界面 | `lib/` 未提交或 `package.json` 的 `dsh` 字段缺失;`npm test` 里的构建守卫会检查这些 |
84
- | 界面显示"未配置"但环境里明明有 key | 宿主行可能拿不到平台的 `credentials` 服务(作用域问题)。插件会退化为直读 env → `~/.dsh/.credentials.yaml`,并把来源标注为 `env (direct)` / `file (direct)`;看到该标注即说明走的兜底 |
24
+ request纯构造url和headers;parse只处理payload,不访问网络、凭据或当前时钟。RequestInput提供mode/apiKey及可选baseUrl/pinnedBaseUrl;读取Module负责执行、15s单次超时、HTTP错误分类和按序镜像。模板见 [新源骨架](https://github.com/takboo/dsh-usage-state/blob/main/src/host/sources/_template.ts)。
25
+
26
+ requiresApiKey在类型中存在,但当前宿主装配仍要求解析到凭据。不能只设置false就宣称已支持无key数据源;若要支持需补完整链路测试。
27
+
28
+ ## 增加数据源
29
+
30
+ 1. 用官方文档或固定commit接口定义查清端点、鉴权、单位、百分比口径、重置时刻及错误信封;没有官方资料时明确标社区观察/未验证。fixture去除key和账户信息。
31
+ 2. 新建Adapter和源测试,覆盖正常余额/窗口、真实0、非法/缺失字段、数字串、不同时间形态,以及HTTP200错误信封。无有效读数应抛SourceError,不填伪造0。
32
+ 3. 在 [注册表](https://github.com/takboo/dsh-usage-state/blob/main/src/host/sources/index.ts) 注册;用目录测试验证元数据可JSON传输。基础设置选项可从该注册表派生。
33
+ 4. 若需要自动发现provider,在 [共享hints](https://github.com/takboo/dsh-usage-state/blob/main/src/shared/config.ts) 维护路由名/host规则并加冲突测试。注册Adapter不会自动识别新provider;宿主会将可用端点的origin提示经RPC传到两UI,扩展时校验不含userinfo/query/key。
34
+ 5. 若新增窗口id或用户文案,同步 [中英词典](https://github.com/takboo/dsh-usage-state/blob/main/src/client/locales.ts)。窗口顺序由Adapter决定,展示层按返回顺序显示。
35
+ 6. 补provider配置到fake fetch和RPC/显示的装配用例,再执行typecheck→build→test→pack。更新两份README、此文字段语义、Backlog验证状态和Unreleased。
36
+
37
+ 开发命令和toolchain见 [开发指南](https://github.com/takboo/dsh-usage-state/blob/main/docs/development.md)。新Adapter不应引入成本计算或历史记录。
38
+
39
+ ## 归一化和错误约定
40
+
41
+ - 金额保留明确币种/单位,使用来源契约换算,不按数值大小猜“元或分”。数值必须有限;缺失与真实0分别处理。
42
+ - percentage已是0..100时用clampPercent。normalizePercent把0..1视为比例,因此1的解释有歧义;只在来源确需这种兼容时使用并注明。不得拿它解析明确的1%。
43
+ - used/limit只在limit有效且>0时换算;remaining需按来源说明反转。unlimited哨兵不能展示成负余额/负额度。
44
+ - normalizeResetAt支持unix秒、毫秒及ISO。不存在的重置时间保持undefined,不猜一个日期。
45
+ - normalizeBaseUrl去尾斜杠和/vN;provider声明端点目前先取origin。若来源路径有额外前缀,必须测试请求路径,不能假定都从根目录挂载。
46
+ - SourceError区分config/auth/http/network/parse;detail不得主动包含密钥。401/403由读取层归auth,JSON语法错归parse、body中断归network;HTTP200错误信封由Adapter识别。宿主快照边界精确替换本次已解析key的原文回显,不可依赖猜测key形状的正则。
47
+ - source+mode仍只选择一个目标,宿主用有效签名、私有凭据摘要和generation隔离身份/在途;不表示新增多账户,也不在Adapter另建缓存。
48
+ - 共享平台实例按DSH指南声明peer+dev,独立第三方依赖放dependencies;不要将旧宿主link解析经验写成所有平台包都不能import的规则。
49
+
50
+ 归一化实现见 [normalize](https://github.com/takboo/dsh-usage-state/blob/main/src/host/sources/normalize.ts),网络实现见 [read](https://github.com/takboo/dsh-usage-state/blob/main/src/host/read.ts)。
51
+
52
+ ## 当前端点和字段语义
53
+
54
+ | 来源 | 请求及鉴权 | 读数语义与证据 |
55
+ |---|---|---|
56
+ | DeepSeek API | GET {base}/user/balance,Bearer | balance_infos多币种;优先有余额/CNY并保留granted/topped_up。有限真实0合法,非法total_balance跳过;无有效行抛parse |
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
+ | 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 | usages.limit_5h/limit_7d/limit_month_total/limit_month_code的used_ratio×100,reset_time归一;兼容usage周计数及limits[].detail/window,枚举分钟归一到小时;国内5h/7d已真实账户验证,月额度仅fixture验证 |
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
+ | Sub2API | GET {instance}/v1/usage,Bearer;必须实例地址 | quota.remaining或wallet balance、rate_limits窗口;subscription.daily/weekly/monthly→1d/7d/30d。单位缺失默认USD;parse目前未按mode过滤,可能同时返回余额和窗口;接口字段随实例版本变化 |
62
+
63
+ [Moonshot官方余额定义](https://platform.kimi.com/docs/api/balance.md)优先于早期社区“分/元不明”的研究。[DeepSeek官方余额接口](https://api-docs.deepseek.com/api/get-user-balance)提供字段契约。其余社区/历史证据见 [研究索引](https://github.com/takboo/dsh-usage-state/blob/main/docs/research/README.md) 和 [上游分析](https://github.com/takboo/dsh-usage-state/blob/main/docs/research/dsh-cost-meter-analysis.md),不能把逆向观察称为厂商保证。
64
+
65
+ ## 端点策略和测试缺口
66
+
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
+
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均可解析;未验证国际区域账户和月额度活体响应。