Bundle
dsh-quota-pill
Shows the DeepSeek account balance, the current Session's credit spend, and peak/off-peak pricing beside the shipped composer stats.
- Source
- jiangluoyiyi
- License
- MIT
- Updated
- Updated 8 days ago
Readme
# dsh-quota-pill
在 DSH Web 界面的 **composer 下方**常驻显示 DeepSeek 账号余额与「本次会话」额度消耗,位置与 DSH 自带的统计药丸(耗时 / token)并排。
仓库:<https://github.com/jiangluoyiyi/dsh-quota-pill>
这是一个**本地 bundle**:安装进当前 profile 后对所有会话生效,逻辑只有客户端半边,不注册任何 Host 服务、不下发工具、不进入模型上下文。
```
谷 余额¥8.75 │ 消耗¥0.42
```
药丸固定为两个信息格(余额 / 消耗),前面带一个**峰谷标识**,中间竖线分隔:
- `峰` = 高峰时段(价格翻倍,警示色);`谷` = 空闲时段(半价,弱化灰)
- 两个数值都用等宽数字(`tabular-nums`),位数变化时不会左右抖动
- 消耗尚未结算时显示 `—`(如 `余额¥8.75 │ 消耗—`),**不会让药丸变窄或跳宽**
- 版本号不占用药丸,只在展开的卡片里
点击药丸展开明细卡片(只放必要信息:余额分项、本次会话消耗、当前时段与它的三项单价;规则说明留在本文档,不占卡片):
```
余额
充值钱包 ¥8.75
赠送额度 ¥0.00
本次会话 ¥0.00
高峰 1h06m
缓存命中 ¥0.04
缓存未命中 ¥2.00
输出 ¥8.00
单位:元 / 百万 tokens
v0.1.9 刷新
```
排版约定:所有数值等宽数字(`tabular-nums`)右对齐;时段行与三行单价**左端对齐、无缩进**;单价一律两位小数(`¥2.00` 而非 `¥2`),使三行数字宽度稳定;单位用一行灰色小字说明。
## 功能
| 显示项 | 含义 | 数据来源 |
|---|---|---|
| 余额 | 账号**可用余额合计**(充值钱包 + 赠送额度,按币种分别相加) | `ctx.remote.account.getBalance()` |
| 消耗 | 该会话的**余额差值**:进入该会话时记录基线余额,之后与当前余额相减 | 本地计算 |
| 峰 / 谷 | 当前处于高峰还是空闲时段 | 本地按北京时间判定(见下) |
| 充值钱包 / 赠送额度 | 两类钱包分项,保留平台返回的币种(`¥` / `$`) | 同 `getBalance` |
| 时段与单价 | 当前时段、**该时段剩余倒计时**(如 `1h06m`)、三项单价 | 内置价格表 |
| 版本号 | 当前页面实际加载的 bundle 版本(在卡片操作行左侧) | 构建时常量 |
## 峰谷与价格
官方计价规则([模型 & 价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing)):**高峰时段为北京时间周一至周五 9:00–12:00、14:00–18:00**,其余时段(含周末)均为空闲;**空闲价为高峰价的一半**。扣费优先扣减赠送余额。
卡片按当前时段列出**该时段的单价**(元 / 百万 tokens):
| 模型 | 时段 | 缓存命中 | 缓存未命中 | 输出 |
|---|---|---|---|---|
| `deepseek-flash` | 空闲 / 高峰 | 0.02 / 0.04 | 1 / 2 | 4 / 8 |
| `deepseek-v4-pro` | 空闲 / 高峰 | 0.15 / 0.30 | 4.5 / 9 | 13.5 / 27 |
关于这份数据的四点如实说明:
1. **这是官方标价,不是你的实际扣费**。你的真实扣费以平台账单为准;卡片里的单价只用于判断"现在贵不贵、贵一倍"。
2. **模型名默认按 `deepseek-flash`**(当前 profile 的默认模型)。若你在用 `deepseek-v4-pro`,可设 `globalThis.__DSH_QUOTA_PILL_MODEL__ = 'deepseek-v4-pro'` 覆盖;如需跟随会话实际选择的模型,请提需求。**卡片上不显示模型名**(为保持简洁),换模型后单价会随之变化。
3. **法定节假日未计入**:规则把中国法定节假日排除在高峰之外,而时钟判定不出来,所以节假日会显示为"峰"。这一点不在卡片里,记在这里。
4. **价格可能变动**:页面更新后需要同步 `client.js` 里的 `PRICE_TABLE`;验证脚本会校验每一项的"空闲价 = 高峰价 ÷ 2"以及"一律两位小数",防止手抄出错。
余额为 0、未登录、读取失败都会给出各自的可读状态,**不会把失败伪装成 0 余额**。
## 安装
本机开发目录(本仓库已链接进 profile):
```text
plugin_manager { action: "install_bundle", target: "<本目录绝对路径>" }
```
从 GitHub 安装(`install_bundle` 支持 git URL,会在 profile 内克隆):
```text
plugin_manager { action: "install_bundle", target: "https://github.com/jiangluoyiyi/dsh-quota-pill" }
```
安装后**刷新一次页面**(`Ctrl+Shift+R` 或菜单重载)才会看到药丸。之后的代码改动由宿主侧的 bundle 轮询(默认 500ms 比较 mtime/ctime/size)自动发布给已打开的页面。
**判断页面是否加载到最新代码**:**点开卡片**,底部灰字 `vX.Y.Z` 就是凭据(药丸本身不显示版本,以免与金额混淆)。看不到期望的版本号即为页面仍在跑旧 bundle。
## 数据通路
余额取自 Host 的 `account` Remote 命名空间——与「设置 → 账户」页面用的是同一个后端调用,底层是 `GET https://platform.deepseek.com/api/v0/users/get_user_summary`,鉴权票据由 Host 保管、从不进入浏览器。
两个必须注意的契约细节(写错就会静默失败):
1. **入参**:`getBalance` 需要 `AccountClientMetadata`,即 `{ version, locale, timezoneOffsetSeconds }`,且为严格编解码——多字段会被拒绝。
2. **返回值是信封**:Remote 调用返回 `{ ok: true, value }` 或 `{ ok: false, error }`,业务数据在 `value` 里。**直接读返回值上的 `status` 会永远判失败**(信封没有 `status` 字段),这是本插件开发过程中真实踩过的坑,`verify-quota-pill.mjs` 现在专门覆盖了这两种情况。
## 刷新策略
| 场景 | 行为 |
|---|---|
| 首次挂载 | 立即读一次 |
| 空闲 | 每 45s 一次 |
| 会话运行中(`useSession().running`) | 每 20s 一次 |
| 一轮结束 | 4s 后补读一次(此时扣费已落账) |
| 读取失败 | 按 20s → 60s → 120s 退避,避免把平台故障变成请求风暴 |
| 会话切换导致组件重挂载 | 5s 内刚读过的结果直接复用,不重复请求 |
| 展开卡片 | 额外调一次 `getState()` 做通路自检(卡片里的 `RPC:` 行仅在失败时出现) |
同一页面只会存在一个读取循环;切换会话不会重启它。
## 显示状态
| 状态 | 表现 |
|---|---|
| 正常 | `谷 余额¥8.75 │ 消耗¥0.42` |
| 高峰时段 | `峰 余额¥8.75 │ 消耗¥0.42`(`峰` 用警示色) |
| 消耗未结算 | `余额¥8.75 │ 消耗—`(首次读数无可比基线) |
| 未登录账号 | 余额格显示账号状态,消耗格 `—` |
| 未接通账户服务 | `余额未知` + 卡片内 `诊断: no-remote / no-account / no-method` |
| 调用失败 | 保留**上一次成功读数**并标为陈旧(黄色),卡片内显示真实错误码 |
| 读取中 | 余额格显示 `读取余额…`,消耗格 `—` |
## 文件
| 文件 | 作用 |
|---|---|
| `client.js` | 全部逻辑:Slot 注册、余额读取循环、会话基线、卡片渲染、样式 |
| `index.js` | Host 半边,空实现(本插件不需要 Host 服务) |
| `package.json` | `dsh.bundle.patch` + `dsh.client` 声明 |
| `cordis.patch.yml` | 插入一行 loader entry |
| `verify/verify-quota-pill.mjs` | 离线验证脚本(见下节) |
挂载座位是 `conversation.composer.dock`,用**独立 id** `quota`(`order: 10`)追加在自带 `stats` 药丸之后,不替换任何现成 UI。样式只使用 `--dsw-alias-*` 主题 token,不 import 任何 Harness Client 包。
## 本地验证
`verify/verify-quota-pill.mjs` 用真实账号返回的 payload 形状驱动真实 bundle,覆盖 62 项断言:bundle 形状、注入服务、Slot 注册、metadata 字段、钱包解析与格式化、首次读数不虚报 0、差值语义、未登录 / 信封失败 / 传输失败三态、失败后保留上次读数、药丸只有两个信息格且不含版本号、灰色页脚内容、**峰谷时段边界与倒计时**、**两项价格表的半价关系**、卡片对当前时段的报价。
```bash
node verify/verify-quota-pill.mjs
```
它自带一个尊重依赖数组与 effect 清理的 React hook 运行时,因此能测出"状态该不该跨渲染保留"这类问题。**注意它的边界**:它模拟的是 React 与 Remote 的契约,不能替代浏览器实测——渲染结果仍需在页面上确认。
## 已知限制
- **「本次会话」是本地测量,不是平台账单**:平台不提供会话级计费。该数字 = 进入会话时的余额 − 当前余额,因此这段时间内在**其他窗口、其他设备或直接调用 API** 产生的扣费会计入差额。余额是账号级的,无法归属到单个会话。
- **会话基线随页面存活**:刷新页面后基线重新记录,差值从刷新那一刻重新累计。
- **两个会话同时运行时**,余额差值无法归属到具体某一个会话。
- **余额读取依赖账号登录态**:使用 API key 而非账号登录时,`getBalance` 返回 `null`,药丸显示「未登录账号」。
- **未验证项**:会话切换后的现场隔离效果、长时间运行的平台限流表现、浅色主题下的观感。
- **赠送额度为 0 时仍显示该行**,用于确认钱包结构;如需隐藏可改 `client.js` 中对应条件。
Install
dsh plugin --profile web add github:jiangluoyiyi/dsh-quota-pill
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-quota-pill from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.