Skip to content
dsh.fish
Bundle

dsh-billing-badge

Billing season and account balance chip for the DeepSeek Harness web GUI composer: a native-looking pill after the cache-hit stat, with a panel on click. DeepSeek Harness 计费时段与余额插件

Source
devacc8
License
MIT
Updated
Updated 20 hours ago

Readme

# dsh-billing-badge

[![npm](https://img.shields.io/npm/v/dsh-billing-badge)](https://www.npmjs.com/package/dsh-billing-badge)
[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

Billing season and account balance for the DeepSeek Harness web GUI. A pill in the
composer's statistics row, immediately after the native **Cache hit** reading, that
opens a small panel with the full picture.

```
2288M tok · Cache hit 99.8% · ● Off-peak · 2h13m
```

## What it shows

| Where | What |
|---|---|
| The pill | a coloured dot (amber for peak, green for off-peak), the season, and the time until it flips |
| The panel (click) | billing season, next switch with the Beijing wall clock, current Beijing time, account balance with its currency, granted and topped-up split, and a refresh button |

The season rule is the published one: peak is Beijing time, Monday to Friday,
09:00-12:00 and 14:00-18:00. Everything else, including all of Saturday and Sunday,
is off-peak at half price.

## The balance

The numbers come from the official `GET /user/balance` endpoint, and the three of them
mean different things:

```
total_balance = granted_balance + topped_up_balance
```

- `total_balance`, shown as **Account balance**, is everything you can spend.
- `granted_balance`, shown as **Granted**, is credit DeepSeek gave you. The endpoint
  reports only the part that has not expired, so a lapsed grant disappears from this
  row on its own.
- `topped_up_balance`, shown as **Topped up**, is money you paid in.

When the two upper rows are equal there is no grant on the account. `is_available`
is a top-level field of the response and answers one question: is the balance enough
for API calls. The panel adds a warning row when the answer is no, and stays quiet
otherwise, because the flag is true for every funded account.

The currency is taken from the response, never assumed: an account reporting USD is
not labelled with a CNY sign.

## Why another one

Two community plugins cover parts of this, and both taught something:

- [dsh-price-phase](https://github.com/lijunyu726/dsh-price-phase) shows the season.
  Its countdown once pointed at Saturday 09:00 after Friday close, an event that does
  not happen, and it centres its badge on a hashed CSS class, which collides with the
  model chip when the model name is long. This plugin compares each candidate boundary
  with the instant before it and only counts a real state flip, and its chip is an
  ordinary flex child in the native statistics row.
- [dsh-usage-monitor](https://github.com/liyiersan/dsh-usage-monitor) shows the
  balance, but formats it as CNY whatever the API reports.

This plugin deliberately does **not** do cost or token accounting.

## Install

From npm:

```sh
dsh plugin --profile web add dsh-billing-badge
```

or straight from the repository:

```sh
dsh plugin --profile web add github:devacc8/dsh-billing-badge
```

then restart `dsh web`. The package declares `dsh.bundle.patch`, so the host half is
reconciled into the profile's bundle list automatically.

Working on the plugin itself, install the checkout by path instead:

```sh
dsh plugin --profile web add link:/absolute/path/to/dsh-billing-badge
```

## Security

A balance is a small surface, so it stays small:

- the API key is resolved in the host through the DSH credentials seam
  (`ctx.credentials.resolve('DEEPSEEK_API_KEY')`, environment fallback) and never
  reaches the browser;
- the single route requires the `x-dsh-billing-badge: 1` header and rejects a
  cross-origin `Origin`, so a cross-site page cannot reach it;
- nothing is written to disk, and no endpoint other than `api.deepseek.com` is
  contacted;
- a missing key, an HTTP error or a network failure all degrade to a state the panel
  renders, never to a throw.

## Development

The season logic lives in `lib/season.js` as a plain ESM module so it can be tested
directly. A browser bundle cannot import a sibling file (the loader resolves only
platform seeds, materialized packages and registered factories, and a self-subpath
`require` throws "missed the module table"), so `scripts/inline-season.mjs` copies the
module into `lib/client.js` between two markers with `export ` stripped, and
`test/client-sync.test.mjs` fails if the copy drifts.

```sh
npm test          # 30 tests: season rule, countdown invariant, host route, bundle
npm run sync      # re-inline season.js into the bundle
npm run check     # sync check plus a syntax check of both halves
```

The countdown has an invariant test rather than fixtures: every 13 minutes across nine
days, the reported target must be in the future, must change the season, and the
season must not change before it.

## Layout

```
lib/season.js    season rule, countdown, formatting (source of truth, tested)
lib/index.js     host half: the balance route
lib/client.js    browser half: the pill and its panel, with season.js inlined
cordis.patch.yml mounts the host half into the profile
scripts/         the inliner
test/            season, sync, host and bundle tests
```

GitHub Actions runs `npm test` and `npm run check` on Node 20 and 22.

## 中文说明

DeepSeek Harness 网页界面的计费时段与账户余额插件。它在输入框下方的统计行里、原生 **Cache hit** 之后加一个小胶囊,点击后展开一个小面板。

```
2288M tok · Cache hit 99.8% · ● Off-peak · 2h13m
```

- **胶囊**:一个圆点(高峰为琥珀色,非高峰为绿色)、当前时段,以及距离切换的倒计时。
- **面板**(点击展开):计费时段、下次切换时间与北京时间、当前北京时间、账户余额及其货币、赠送额度与充值额度的拆分,以及刷新按钮。

计费时段采用官方公布的规则:高峰为北京时间周一至周五 09:00-12:00 与 14:00-18:00,其余时间(含整个周六与周日)均为非高峰,价格为半价。

余额来自官方 `GET /user/balance` 接口,其中三个数字含义不同:

```
total_balance = granted_balance + topped_up_balance
```

- `total_balance`(Account balance):可用总额。
- `granted_balance`(Granted):官方赠送的额度,接口只返回尚未过期的部分,过期的赠送额度会自动从这一行消失。
- `topped_up_balance`(Topped up):你自己充值的金额。

前两行数值相等时,说明账户没有赠送额度。`is_available` 是响应顶层的字段,回答一个问题:余额是否足够调用接口。只有接口报告余额不足时,面板才会加一行警告;该字段对任何有余额的账户都是 true,因此平时不显示。货币一律取自接口返回值,不做假设:返回 USD 的账户不会被标上人民币符号。

### 安装

从 npm 安装:

```sh
dsh plugin --profile web add dsh-billing-badge
```

或直接从仓库安装:

```sh
dsh plugin --profile web add github:devacc8/dsh-billing-badge
```

然后重启 `dsh web`。包内声明了 `dsh.bundle.patch`,宿主部分会自动写入 profile 的 bundle 列表。

### 安全

- API key 只在宿主进程中通过 DSH credentials 接口读取(`ctx.credentials.resolve('DEEPSEEK_API_KEY')`,环境变量兜底),不会进入浏览器;
- 唯一的路由要求请求头 `x-dsh-billing-badge: 1`,并拒绝跨站 `Origin`;
- 不写任何文件,除 `api.deepseek.com` 外不访问其他地址;
- 缺少 key、HTTP 错误或网络故障都会降级为面板可显示的状态,不会抛异常。

MIT.

Install

dsh plugin --profile web add github:devacc8/dsh-billing-badge

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source