dsh-peak-balance
Peak/off-peak billing clock, live DeepSeek account balance and per-turn cost — rendered above the prompt in dsh-TUI.
⚡ 峰时 09:00-12:00 · 距谷时 1h23m · 余额 ¥42.10 · 本轮 ¥0.0234
While the peak window is active you can turn the line into a three-row frame that pulses in one of seven colors:
╭──────────────────────────────────────────────────────────╮
│ ⚡ 峰时 · 距谷时 1h23m · 余额 ¥42.10 · 本轮 ¥0.0234 ▂▃▄▅▆▇ │
╰──────────────────────────────────────────────────────────╯
Features
| Feature | What it shows |
|---|---|
| Peak / off-peak clock | The billing window currently in force and a live countdown to the next price switch (09:00-12:00 / 14:00-18:00 Beijing time, Monday–Friday; weekends are off-peak all day). |
| Live balance | Your DeepSeek account balance, refreshed after every completed turn and once a minute in the background. |
| Per-turn cost | What the turn that just finished cost, in CNY, priced from the official rate card — peak and off-peak usage are priced separately, by each request's own timestamp. |
| Peak-hour warning | Optional. While peak pricing is active the status contribution becomes a rounded frame whose border, phase label and travelling waveform pulse in the chosen color. |
| Settings card | A new Peak & Balance card on the /settings screen, with its four switches rendered directly on the card — no subpage to open. |
Install
# from npm
dsh plugin --profile dsh-tui add dsh-peak-balance
# ...or straight from a checkout of this repository (pnpm packs the local
# directory, so the profile keeps a real copy instead of a symlink)
dsh plugin --profile dsh-tui add file:/absolute/path/to/dsh-peak-balance
The command appends the bundle row to the profile's dsh.profile.bundles.
Then restart the TUI (/restart inside dsh-tui) so the profile loads the new
row; the settings card appears under /settings immediately after the restart.
Installing by symlink (
link:or a directory junction) is not recommended: Node resolves a plugin's real path, and@deepseek-ai/*must stay reachable from it.file:and npm installs both leave a real directory inside the profile, which is the layout the host expects.Updating a
file:install: pnpm caches a local directory dependency, soadd/updatealone will not pick up edited sources. Remove and re-add it (after a version bump) to refresh the profile copy:dsh plugin --profile dsh-tui remove dsh-peak-balance dsh plugin --profile dsh-tui add file:/absolute/path/to/dsh-peak-balance
Settings
/settings → the Peak & Balance card. Edits are written live; no restart is
needed.
| Field | Type | Default | Meaning |
|---|---|---|---|
| Show balance | boolean | true |
Show the account balance on the status line. |
| Show per-turn cost | boolean | true |
Show the estimated cost of the last completed turn. |
| Peak-hour warning | boolean | false |
Turn the line into a pulsing frame while peak pricing is active. |
| Warning color | select | red |
Frame color: red orange yellow green cyan blue purple. |
The settings live in the dsh-peak-balance namespace of the dsh settings
document, so they can also be edited there directly.
How the numbers are produced
Peak window. Off-peak pricing is half of peak pricing; peak hours are
Beijing time (UTC+8) Monday–Friday 09:00-12:00 and 14:00-18:00, everything
else — including both weekend days — is off-peak.
Cost. DeepSeek's API returns token counts, never money, so the per-turn figure is an estimate:
cost = inputMissTokens × inputMissRate
+ cacheReadTokens × inputHitRate
+ outputTokens × outputRate
The three input-side figures are disjoint: inputTokens is the prompt that
missed the cache, cacheReadTokens is the prompt served from cache, and the
provider's own totalTokens is their sum plus the output. Treating the cache
read as a subset of the input (clamping one against the other) under-prices a
cached turn by several times — measured against a real balance drop on
2026-09-10, a turn that cost ¥0.20 was estimated at ¥0.03 that way and ¥0.23
with the formula above.
Each provider usage report is filed into the peak or off-peak bucket using the timestamp of the request that produced it, so a turn straddling a price boundary is not priced wholesale at the current window.
Embedded rate card (CNY per million tokens), verified 2026-09-10 against the official Models & Pricing page:
| Model | Bucket | Input (cache hit) | Input (cache miss) | Output |
|---|---|---|---|---|
deepseek-flash |
off-peak | 0.02 | 1 | 4 |
deepseek-flash |
peak | 0.04 | 2 | 8 |
deepseek-v4-pro |
off-peak | 0.15 | 4.5 | 13.5 |
deepseek-v4-pro |
peak | 0.30 | 9 | 27 |
The legacy ids deepseek-v4-flash and deepseek-v4-flash-vision-exp are
served by DeepSeek-V4.1-Flash and priced at the Flash rates;
deepseek-v4-pro is rerated to the Flash card from 2026-09-14 12:00 Beijing,
when that route is retired to V4.1-Flash. A model the card does not know is
reported as 费率未知 / unrated model — the plugin shows tokens instead of a
wrong number.
Balance. GET https://api.deepseek.com/user/balance (the same read-only
endpoint behind dsh-tui's /balance command). The key is resolved through the
credentials seam (DEEPSEEK_API_KEY) with an environment fallback, is sent
only in the request header, and is never logged or stored by this plugin.
Compatibility
| Item | Value |
|---|---|
| Host | @deepseek-harness-tui/dsh-tui 0.10.x (ctx.tuiStatus.registerView, ctx.tuiSettingsSections.register) |
| Harness | @deepseek-ai/dsh 0.1.2-rc.1 or later (session/event, settings, credentials) |
| Runtime | Node ^22.19 || >=24, pure ESM, no native dependencies |
| Manifest | manifestVersion 0.15 · id com.dsh-tui-ecosystem.dsh-peak-balance |
| Platform | Anywhere dsh-tui runs (Windows / macOS / Linux) |
Every host seam is optional and probed softly (ctx.get(name, false)): without
the TUI extension services, without a credentials service, or without network
access the plugin stays inert instead of failing the host. All registrations are
retried for 30 s while the profile composes, and every timer is cleared from the
activation's effect disposer.
Known limitations
- The prompt border itself cannot be recolored by a plugin. dsh-tui 0.10
draws the input frame in its own
EffortInputBordercomponent and exposes no seam for it, so the warning frame is a status contribution rendered directly above the prompt — the closest a plugin can get without patching the host. - Cost figures are estimates from provider-reported tokens; the platform bill is authoritative.
- The rate card is embedded in the package. A price change on DeepSeek's side requires a plugin update.
- The balance endpoint needs a DeepSeek official API key. Other providers are detected and simply show no balance.
- Rich status contributions share a six-row budget with other plugins; this one requests three rows, and only while the warning frame is visible.
- Only the most recently active session is displayed; subagent sessions are ignored on purpose, so a delegated child never rewrites your turn cost.
Publishing and versioning
Released from VviLliAm-qwq/dsh-peak-balance under MIT. Versions follow
SemVer; the npm version and the manifest version are kept identical, and a
v* tag matching the version drives the release workflow.
Development
pnpm install --frozen-lockfile
pnpm check:encoding # no UTF-8 BOM / damaged sequences (the classic dsh crash)
pnpm validate:manifest # admission shape + version agreement
pnpm test # node:test unit + host-stub integration tests
pnpm pack:verify # every module the entry imports ships in "files"
pnpm verify # all four, in order
The tests cover the peak-window maths at fixed instants, the rate card and
bucket pricing, usage normalization, balance-payload parsing (including
failures), the display model, the status component's element tree, and a full
apply() run against a stubbed Cordis context — including the paths where the
host services are missing, refuse, or throw.
Previewing the peak-hour warning
The warning frame only appears inside a real peak window (Mon-Fri
09:00-12:00 / 14:00-18:00 Beijing). To preview it at any hour:
DSH_PEAK_BALANCE_FORCE_PEAK=1 dsh --profile dsh-tui # PowerShell: $env:DSH_PEAK_BALANCE_FORCE_PEAK=1
The override changes presentation only — the countdown still describes the real
clock — and it is off unless the variable is set to 1/true/yes/on.
Diagnostics
The plugin keeps a bounded lifecycle log at ~/.dsh-tui/dsh-peak-balance.log:
one line when the module is imported, one when apply() starts (with pid and
the file path it was loaded from), the resolved config, which host seams were
mountable, the outcome of every registration, and teardown. That is enough to
tell "the host never loaded the file" apart from "a seam refused" without
attaching a debugger to a running TUI. The file trims itself to its newest half
once it passes 128 KiB, and DSH_TUI_DEBUG=1 adds the per-refresh detail. Test
runs never touch it.
Notes for plugin authors
Two host behaviours cost real debugging time here, and both are easy to hit:
- A Cordis entry must export only
name,Configandapply. Exporting helpers from the same module changes how the loader wraps the activation, and everytuiStatus/tuiSettingsSectionsregistration from that activation is then rejected withrequires a live Cordis activation context. The failure is partial and quiet: the settings namespace still registers, so the plugin looks half-alive while the settings card and the status line never appear. Keep the implementation in a sibling module and re-export the three symbols. - Resolve optional host services strictly first (
ctx.get(name)); the non-strictctx.get(name, false)can hand back a shadow placeholder whose method calls the host refuses. Keep the non-strict form only as a fallback, and keep retrying — the seam rows may still be activating on the first tick.
This plugin writes what it learned to ~/.dsh-tui/dsh-peak-balance.log, which
is how both were found; see Diagnostics above.
Listing
This plugin is listed on the dsh-tui plugin market. Market listings are a link directory only; they are not a code review and do not imply endorsement.
License
MIT — see LICENSE.
No comments yet. Be the first to write one.