Bundle
dsh-usage-display
在 dsh(DeepSeek Harness)会话头部展示模型厂商余额/用量徽标的插件:内置 DeepSeek 余额、MiniMax Token Plan 与智谱 GLM Coding Plan 配额,适配器架构支持接入更多厂商;host 侧按轮次取数,经 SSE 同步到浏览器。
- Source
- deluo
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 4 days ago
Readme
# dsh-usage-display
[](https://www.npmjs.com/package/dsh-usage-display)
[](LICENSE)
[](https://github.com/topics/dsh-plugin)
在 dsh Web 界面显示模型厂商余额 / 套餐用量的会话头部徽标插件。host 进程从各家
厂商官方接口取数并缓存,浏览器侧只读本地路由渲染;API key 始终留在 host,
不会下发到浏览器。
内置三家厂商,按多厂商适配器架构组织,新增厂商只需实现一个 adapter:
| 厂商 | `providerId` | 展示内容 |
| -------- | ------------ | -------------------------------------------------- |
| DeepSeek | `deepseek` | 多币种账户余额(granted / topped-up 拆分) |
| MiniMax | `minimax` | Token Plan 配额(5 小时窗口与周窗口) |
| 智谱 GLM | `zhipu` | Coding Plan 配额(5h、周限额、工具用量三个百分比) |
## 效果预览
设置页(Settings → Plugins → “用量与余额”)可按通用显示 / DeepSeek / MiniMax / 智谱 GLM 分区调整展示偏好与告警阈值,保存后立即生效:

## 特性
- 会话头部用量徽标:展示当前模型对应厂商的主指标,点击展开明细面板并支持手动刷新。
- 可配置告警阈值:余额低于阈值、配额用量超过阈值时,徽标状态点与进度条按
warn(黄)/ critical(红)两级染色。
- 可配置进度可视化:percent 类指标在徽标与面板中支持文本、环形图、条形图三种
形态(`display.panelStyle`),徽标可用 `auto` 按指标类型自动选择。
- 设置页热更新:Settings → Plugins → “用量与余额”页可即时调整展示偏好与告警
阈值,保存后 host 重建运行时并重新取数,无需重启;接入类字段仍走 cordis 配置。
- 真正产生模型调用后自动取数:`turn/start` 与 `turn/end` 各全量刷新一次,切换模型时
只定向刷新新选中的厂商。
- host 刷新落定后通过 SSE 通知浏览器重读本地缓存;浏览器绝不直连厂商 API。
- 每家厂商独立缓存与故障隔离:一家失败不影响其他家,失败时保留上次成功值并标注更新时间。
- 凭证只以引用名出现在配置中,每次取数由 dsh 凭证服务实时解析,轮换后下次请求即生效。
## 前置要求
- Node.js(与运行中的 dsh 相同的版本即可)
- pnpm
- dsh CLI ≥ 0.1.0-rc.6
## 安装
### 从源码安装(开发)
```powershell
# 1. 安装依赖并构建(产出 lib/index.js 与 lib/client.js)
pnpm install
pnpm run build
# 2. 安装进 web profile(在插件目录内执行 `add .` 等价;-w 按 workspace 链接)
dsh plugin --profile web add -w D:/Code/dsh-usage-display
# 3. 验证组合层,然后启动
dsh web --dump-config
dsh web
```
Windows 下路径务必用正斜杠,反斜杠会被解析成非法包名(如 `Codedsh-usage-display`)。
`-w` 需要保留:profile 自带 `pnpm-workspace.yaml`。
卸载:`dsh plugin --profile web remove dsh-usage-display`。
### 分发形式
- **npm**:`pnpm add dsh-usage-display`(或 `npm i dsh-usage-display`)后执行
`dsh plugin --profile web add dsh-usage-display`;`prepare` 脚本会在安装时构建。
- **tarball**:`pnpm pack` 后执行 `dsh plugin --profile web add ./dsh-usage-display-<version>.tgz`,
分发的是预构建产物,用户侧无需构建授权。
- **Git 安装**:`dsh plugin --profile web add github:deluo/dsh-usage-display`。源码包由 `prepare`
脚本构建;pnpm ≥ 10 默认拒绝,需按提示在该 profile 的 `pnpm-workspace.yaml` 中给
`allowBuilds` 授权后重试。
## 配置
默认配置见 [cordis.patch.yml](cordis.patch.yml),用户可在 profile 或 home 级的
`cordis.patch.yml` 中覆盖(后应用层整行替换):
```yaml
dsh-usage-display:
display:
badgeStyle: 'auto' # auto | text | ring | bar;徽标上的进度形态
panelStyle: 'ring' # text | ring | bar;面板里 percent 指标的形态
showResetCountdown: true # 徽标配额文案是否带“距重置”倒计时
providers:
deepseek:
enabled: true # 关闭后显示“已停用”,不再取数
routeIds: ['deepseek-official'] # dsh provider 路由 id → 本插件 providerId(providerId 自动补入)
apiKeyEnv: 'DEEPSEEK_API_KEY' # 凭证引用名,不是 key
baseURL: 'https://api.deepseek.com'
badgeCurrency: 'CNY' # 徽标主币种;账户无此币种时保持接口返回顺序
warnBelow: 10 # 余额低于此值 → warn;'off' 关闭
criticalBelow: 5 # 余额低于此值 → critical;'off' 关闭
minimax:
enabled: true
routeIds: ['minimax', 'minimax-cn', 'minimaxi', 'minimax-coding-plan', 'minimax-token-plan']
apiKeyEnv: 'MINIMAX_API_KEY' # 只存引用名,key 由 host 运行时解析
apiKeyAliases:
[
'MINIMAX_TOKEN_PLAN_API_KEY',
'MINIMAX_CODING_PLAN_API_KEY',
'MINIMAX_CODING_API_KEY',
'MINIMAX_CN_API_KEY',
'MINIMAX_API_KEY',
'MINIMAX_API_TOKEN',
]
baseURL: 'https://api.minimaxi.com' # 含 minimaxi.com 走国内站,否则走 api.minimax.io
badgeMetric: '5h' # 徽标主指标:5h | weekly
resetTimeStyle: 'countdown' # 重置时间:countdown 倒计时 | time 本地时间点
warnAbovePercent: 80 # 用量超过此百分比 → warn;'off' 关闭
criticalAbovePercent: 90 # 用量超过此百分比 → critical;'off' 关闭
zhipu:
enabled: true
routeIds: ['zai-coding-cn', 'zai-coding', 'zai', 'glm', 'zhipu', 'bigmodel', 'zhipuai']
apiKeyEnv: 'ZHIPU_API_KEY'
apiKeyAliases:
[
'ZAI_CODING_CN_API_KEY',
'ZAI_CODING_API_KEY',
'GLM_API_KEY',
'ZAI_API_KEY',
'BIGMODEL_API_KEY',
]
baseURL: 'https://open.bigmodel.cn'
authStyle: 'raw' # 'raw' | 'bearer'
badgeMetric: '5h' # 徽标主指标:5h | weekly | tools
resetTimeStyle: 'countdown' # 重置时间:countdown 倒计时 | time 本地时间点
warnAbovePercent: 80 # 用量超过此百分比 → warn;'off' 关闭
criticalAbovePercent: 90 # 用量超过此百分比 → critical;'off' 关闭
```
公共字段:
| 字段 | 类型 | 默认 | 说明 |
| ---------- | ---------- | ------ | --------------------------------------------------------------------------------------------- |
| `enabled` | `boolean` | `true` | 关闭后徽标显示“用量已停用”,不再取数 |
| `routeIds` | `string[]` | `[]` | dsh provider 路由 id → 本插件 providerId,用于模型切换联动;`providerId` 本身总会自动加入映射 |
展示偏好(插件级 `display`):
| 字段 | 类型 | 默认 | 说明 |
| -------------------- | ------------------------------------- | -------- | ---------------------------------------------------------------- |
| `badgeStyle` | `'auto' \| 'text' \| 'ring' \| 'bar'` | `'auto'` | 徽标进度形态;`auto` 对 percent 指标用迷你条形图、金额保持纯文本 |
| `panelStyle` | `'text' \| 'ring' \| 'bar'` | `'ring'` | 面板中 percent 指标的形态 |
| `showResetCountdown` | `boolean` | `true` | 徽标配额文案是否带“距重置”倒计时 |
DeepSeek 专属:
| 字段 | 类型 | 默认 | 说明 |
| --------------- | ----------------- | -------------------------- | -------------------------------------------------------------------------------------------- |
| `routeIds` | `string[]` | `['deepseek-official']` | DeepSeek 官方 harness 适配器上报的 provider 路由 id;`deepseek` 作为 providerId 仍会自动加入 |
| `apiKeyEnv` | `string` | `DEEPSEEK_API_KEY` | 凭证引用名,请求 `GET {baseURL}/user/balance` 时使用 |
| `baseURL` | `string` | `https://api.deepseek.com` | 余额接口前缀 |
| `badgeCurrency` | `string` | `CNY` | 徽标主币种(按接口返回的 `currency` 匹配,大小写不敏感);无此币种时保持接口顺序 |
| `warnBelow` | `number \| 'off'` | `10` | 余额低于此值徽标变黄 |
| `criticalBelow` | `number \| 'off'` | `5` | 余额低于此值徽标变红 |
智谱专属:
| 字段 | 类型 | 默认 | 说明 |
| ---------------------- | ----------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `apiKeyEnv` | `string` | `ZHIPU_API_KEY` | 首选凭证引用名 |
| `apiKeyAliases` | `string[]` | 见上 | 首选未配置时按顺序回退;指向模型适配器已在用的引用名即可直接复用其 key |
| `baseURL` | `string` | `https://open.bigmodel.cn` | 实际请求 host 按域名路由:含 `bigmodel.cn` → `open.bigmodel.cn`,否则 `api.z.ai` |
| `authStyle` | `'raw' \| 'bearer'` | `'raw'` | 首选鉴权头风格;401/403 自动换另一种重试,非法值在配置层直接报错 |
| `badgeMetric` | `'5h' \| 'weekly' \| 'tools'` | `'5h'` | 徽标主指标 |
| `resetTimeStyle` | `'countdown' \| 'time'` | `'countdown'` | 重置时间展示:`countdown` 显示紧凑倒计时(如 `2h13m`、`2d3h`);`time` 显示本地时间点(如 `15:30`、`明天 08:30`) |
| `warnAbovePercent` | `number \| 'off'` | `80` | 用量超过此百分比徽标变黄 |
| `criticalAbovePercent` | `number \| 'off'` | `90` | 用量超过此百分比徽标变红 |
MiniMax Token Plan 专属:
| 字段 | 类型 | 默认 | 说明 |
| ---------------------- | ----------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `routeIds` | `string[]` | `['minimax', 'minimax-cn', 'minimaxi', 'minimax-coding-plan', 'minimax-token-plan']` | dsh provider 路由 id → 本插件 providerId |
| `apiKeyEnv` | `string` | `MINIMAX_API_KEY` | 首选凭证引用名 |
| `apiKeyAliases` | `string[]` | 多个 Token Plan / Coding Plan / CN 别名 | 首选未配置时按顺序回退 |
| `baseURL` | `string` | `https://api.minimaxi.com` | 区域识别基准地址;含 `minimaxi.com` 走国内站,否则走 `api.minimax.io` |
| `badgeMetric` | `'5h' \| 'weekly'` | `'5h'` | 徽标主指标 |
| `resetTimeStyle` | `'countdown' \| 'time'` | `'countdown'` | 重置时间展示形态 |
| `warnAbovePercent` | `number \| 'off'` | `80` | 用量超过此百分比徽标变黄 |
| `criticalAbovePercent` | `number \| 'off'` | `90` | 用量超过此百分比徽标变红 |
`warn*` / `critical*` 配反(如 `warnBelow` 小于 `criticalBelow`)时按更严格的方向归一;
全部设为 `'off'` 关闭该厂商的告警染色。
配置里从不出现 key 本身。`apiKeyEnv` / `apiKeyAliases` 都是凭证引用名,host 每次取数时
经 `ctx.credentials.resolve()` 解析:进程环境优先,其次 `$DSH_HOME/.credentials.yaml`
(Models 页 / `dsh credentials set` 写入),再以项目与用户的 `.env` 回退。模型侧已配置的
智谱 key 可以直接复用——把 `apiKeyEnv` 指向模型适配器所用的引用名(`dsh web --dump-config`
可查到);托管存储里的 key 变更下次取数即生效,进程 env 的快照在启动时冻结。
## 使用
- 打开会话后,头部操作区出现用量徽标:DeepSeek 显示余额金额,MiniMax 与智谱显示
配额窗口的已用百分比与重置时间;命中告警阈值时状态点与进度条变黄/红。
- 徽标左侧的状态点只在纯文本/金额模式与异常状态时出现;有迷你条形图或环形图时
颜色信息已由图形表达,状态点自动隐藏。
- 点击徽标展开当前模型对应厂商的明细面板,含状态、指标明细(金额行 / 配额表)、
更新时间与“刷新”按钮;配额指标按 `display.panelStyle` 渲染为环形图、条形图或
纯数字表格。手动刷新会等待真实取数落定。
- MiniMax 与 GLM 配额重置时间都支持两种形态:`countdown` 显示 `2h13m` / `2d3h` 等
倒计时,`time` 显示 `15:30`、`明天 08:30`、`周三 08:30` 等本地时间点。
- 在 Settings → Plugins → “用量与余额”页按“通用显示 / DeepSeek / MiniMax / 智谱 GLM”分区,
保存后立即生效。该页写入的是用户设置文档;`enabled` / `routeIds` /
`apiKeyEnv` / `baseURL` / `authStyle` 等接入类字段不在此暴露,仍在
cordis.patch.yml 中维护。
- 切换模型时徽标高亮立即跟随(读模型选择目录 store);取数仍由轮次事件驱动,
切换后尚未发消息时展示的是缓存快照。
- 当前模型的厂商未接入(`routeIds` 未覆盖)时徽标进入中性态显示“其他厂商”,面板列出
全部已接入厂商。
- 每家厂商有五态:`loading` / `ok` / `unconfigured` / `disabled` / `error`;失败时展示
上次成功值并标注原更新时间。
## 本地 HTTP 接口
调试与二次集成用:
```text
GET /plugins/dsh-usage-display/balance[?refresh=1] # 聚合快照;refresh=1 强制真实取数
GET /plugins/dsh-usage-display/events # SSE:balance-updated 事件 + 15s 心跳
```
`/balance` 只接受 GET,其余方法返回 `405`。响应示例:
```json
{
"providers": [
{
"providerId": "deepseek",
"displayName": "DeepSeek",
"kind": "balance",
"status": "ok",
"isAvailable": true,
"metrics": [
{
"key": "cny",
"label": "CNY 余额",
"kind": "amount",
"remaining": 110.0,
"unit": "CNY",
"detail": { "granted": "10.00", "toppedUp": "100.00" },
"severity": "ok"
}
],
"fetchedAt": "2026-08-18T10:00:00.000Z",
"cached": false,
"severity": "ok"
},
{
"providerId": "minimax",
"displayName": "MiniMax Token Plan",
"kind": "quota",
"status": "ok",
"isAvailable": true,
"plan": "Max",
"metrics": [
{
"key": "5h",
"label": "5h限额",
"kind": "percent",
"used": 28,
"resetsAt": "2026-08-18T13:00:00.000Z"
},
{ "key": "weekly", "label": "周限额", "kind": "percent", "used": 45 }
],
"fetchedAt": "2026-08-18T10:00:00.000Z",
"cached": false
},
{
"providerId": "zhipu",
"displayName": "智谱 GLM",
"kind": "quota",
"status": "ok",
"isAvailable": true,
"plan": "pro",
"metrics": [
{
"key": "5h",
"label": "5h限额",
"kind": "percent",
"used": 28,
"resetsAt": "2026-08-18T13:00:00.000Z"
},
{ "key": "weekly", "label": "周限额", "kind": "percent", "used": 12 },
{ "key": "tools", "label": "工具用量", "kind": "percent", "used": 8 }
],
"fetchedAt": "2026-08-18T10:00:00.000Z",
"cached": false
}
],
"activeBySession": { "<会话id>": { "providerId": "zhipu", "model": "glm-4.7" } },
"routes": {
"deepseek-official": "deepseek",
"deepseek": "deepseek",
"minimax": "minimax",
"minimax-cn": "minimax",
"zai": "zhipu",
"glm": "zhipu"
},
"display": { "badgeStyle": "auto", "panelStyle": "ring", "showResetCountdown": true },
"providerDisplay": {
"minimax": { "resetTimeStyle": "countdown" },
"zhipu": { "resetTimeStyle": "countdown" }
}
}
```
普通读只返回本地缓存(`cached: true`),不会触发厂商 API;`?refresh=1` 等待取数并返回
新鲜快照(`cached: false`)。
## 项目结构
```text
dsh-usage-display/
├── package.json # 双面插件声明(dsh.bundle + dsh.client)
├── cordis.patch.yml # host loader 行与默认配置
├── tsconfig.json # 类型检查配置
├── tsdown.config.ts # host / client 双 bundle 构建
├── scripts/
│ ├── smoke-client.mjs # 客户端 bundle 冒烟测试
│ └── smoke-host.mjs # host 编排冒烟测试
└── src/
├── index.ts # host 入口:HTTP 路由 + SSE + 会话事件编排
├── types.ts # 两侧共用的快照 / 指标契约
├── settings.ts # 设置页可调子集目录(各厂商 tunable 类型聚合)
├── core/
│ ├── registry.ts # 厂商注册表:schema / tunable 抽取合并 / 展示偏好聚合
│ └── usage-service.ts # 缓存 / in-flight 去重 / 故障隔离
├── providers/
│ ├── types.ts # ProviderAdapter SPI
│ ├── deepseek.ts # DeepSeek 余额适配器
│ ├── minimax.ts # MiniMax Token Plan 配额适配器
│ └── zhipu.ts # 智谱 GLM 配额适配器
└── client/
├── index.ts # 浏览器入口:读取快照 + 注册徽标
├── routes.ts # 本地路由常量(与 host 侧对齐)
├── usage-store.ts
├── UsageBadge.tsx
└── SettingsTab.tsx # 设置页(Plugins 分区 tab)
```
## 接入新厂商
1. 在 `src/providers/<id>.ts` 实现 `ProviderModule`,`fetch()` 负责取数并把 wire 格式
归一化为 `ProviderSnapshot`(异常在内部吞掉并返回 `error` 快照,正常路径不 throw);
指标 `kind` 支持 `amount` / `window` / `percent`。如需设置页热更新,模块里声明
`tunable`(schema + extract + merge)与可选的 `display` 偏好,字段默认值只在模块内
维护一份。
2. 在 `src/index.ts` 的 `createRegistry([deepseek, minimax, zhipu])` 中登记模块,并在
`SETTINGS_SCHEMA` 里补一行该厂商的 `tunable.schema`。
3. 在 `src/settings.ts` 补该厂商的可调类型,客户端 `SettingsTab.tsx` 按需要增加分区。
4. 在 `cordis.patch.yml` 补该厂商的默认配置(只写需要覆盖的字段,其余交给 schema 默认值)。
5. `pnpm run build` 后按开发流程验证。
最小骨架:
```ts
// src/providers/acme.ts
// (z 来自 @deepseek-ai/schemastery;withCommonConfig / ProviderModule / ProviderAdapter
// 来自 providers/types.ts,此处省略 import 语句)
export const acme: ProviderModule<AcmeConfig, AcmeTunable, 'acme'> = {
id: 'acme',
displayName: 'Acme',
kind: 'balance', // 或 'quota'
config: withCommonConfig({
apiKeyEnv: z.string().default('ACME_API_KEY'),
baseURL: z.string().default('https://api.acme.example'),
// 如需设置页热更新:把可调字段与 tunable schema 共用一份定义
}),
// tunable / display 可选,缺省即没有设置页热更新与专属展示偏好
create: (ctx, config) => new AcmeAdapter(ctx, config),
}
class AcmeAdapter implements ProviderAdapter {
readonly id = 'acme'
readonly displayName = 'Acme'
readonly kind = 'balance' as const
async fetch(): Promise<ProviderSnapshot> {
// 取数 + 归一化;错误时返回 status: 'error' 的快照
}
}
```
## 开发
```powershell
pnpm install
pnpm run build # 产出 lib/index.js(host ESM)与 lib/client.js(浏览器 loader 闭包)
pnpm run typecheck # tsc --noEmit(基于 npm 发布的 @deepseek-ai 类型)
pnpm run format # prettier 统一格式化(提交前执行;format:check 仅检查)
pnpm run smoke # 客户端 bundle 冒烟:mock 模块加载器 + mock ctx,无需浏览器
pnpm run smoke:host # host 冒烟:多厂商编排、模型切换联动、鉴权重试,无需 key
```
迭代循环:改 host 半 → `pnpm run build` → 重启 `dsh web`;改 client 半 → `pnpm run build`
→ 刷新页面。本地 `node_modules` 里的 `@deepseek-ai/*` 来自 npm 发布版本,若运行中的 dsh
版本接口有变化,请同步 `peerDependencies` 后重新 `pnpm install`。
## 已知限制
- 余额为异步结算,徽标展示的是近实时快照,面板标注原更新时间。
- 本地路由与 SSE 端点不带鉴权(dsh webServer 的设计如此);dsh web 默认绑定
`127.0.0.1`,若绑定 `0.0.0.0` 会把这些只读端点暴露到网络。
- `turn/end` 对空轮次(输入被拒等)也会发出,多一次无害的余额查询。
- 模型切换没有“点击即触发”的持久事件:高亮点击即生效,数据在下一轮次消息时才刷新。
## 参考
- [DeepSeek 余额接口文档](https://api-docs.deepseek.com/api/get-user-balance/)
- [GLM Coding Plan 配额接口的字段实测参考(cc-switch)](https://github.com/farion1231/cc-switch)
## License
MIT
Install
dsh plugin --profile web add github:deluo/dsh-usage-display#86dc42f52fa04f963d7fab9bdb01cf92ceeb8025
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-usage-display from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.