Peak and off-peak billing season in the composer statistics row, right after the cache-hit reading, with a coloured dot, a countdown to the next switch, and a panel that shows the account balance split into total, granted and topped up in the currency the API reports.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-billing-badge
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:devacc8/dsh-billing-badge
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
English | 中文
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, the granted and topped-up split when the account has one, 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.
The panel names the two parts only when the account is actually split. With no grant the topped-up amount is the whole balance, so the split would repeat the account balance row above it, and that account shows one row instead. Granted appears as soon as the endpoint reports one.
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 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 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:
dsh plugin --profile web add dsh-billing-badge
or straight from the repository:
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.
Works on DeepSeek Harness 0.2.x, prereleases included, and on the 0.1.5 and 0.1.6 builds
that came before it. Older builds render the composer statistics as one labelled row and
the chip joins it; 0.1.6 moved those readings into the composer dock as pill buttons;
0.2.1 grouped them into activity and usage pills, and the chip follows the usage
group. The package declares engines.dsh with the range it supports, so the harness can
report a mismatch instead of loading the plugin silently.
Working on the plugin itself, install the checkout by path instead:
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: 1header and rejects a cross-originOrigin, so a cross-site page cannot reach it; - nothing is written to disk, and no endpoint other than
api.deepseek.comis 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.
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.
MIT.
Links
More in this category
bowenliang123/dsh-context★ 3857
DSH context insight panel: Context dashboard + /context command + Context browser — one-stop context lifecycle management with categorized composition, content details, evolution trends, compaction/injection events, and stats.
Han-1413141/dsh-cost-meter★ 577
Per-session and daily API cost, budget with usage %, official balance, history dashboard, and one-click official price sync with peak/off-peak pricing.
wssfk12138/dsh-damage-pulse★ 279
Tracks DeepSeek token usage, per-call and session costs, and account balance with cache-aware charge animations in the DSH Web UI.
zh667/TokenLedger★ 200
Sidebar usage panel that attributes tokens to the relay site that served each request, read from your existing provider config: today/month/all-time totals, per-site and per-model breakdowns, a year activity heatmap, and New API / Sub2API / DeepSeek balances.
Ychris12138/dsh-usage-stats★ 187
Multi-provider usage dashboard with provider/model token breakdowns, calendar drill-downs, account balances, and OpenCode Go / Z.ai subscription quota tracking.
PolinniZhong/dsh-personal-center★ 114
Personal center for DeepSeek Harness: cross-session usage statistics, per-model cost estimation, global custom instructions, a global font-size adjuster, a data-driven desktop pet with bitmap & vector skins, and a conversation status overview, all local and offline.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.