dsh-burn
Token spend, peak/off-peak pricing, and account balance for the DeepSeek Harness web UI.
中文
dsh-burn
What it does
- Per-reply cost on hover — every settled assistant message shows
↑in · ↓out ¥cost in its action row (copy/like/share), priced at the billing tier in effect at that reply's own timestamp against the live price book.
- Cumulative spend + balance in the conversation header —
累计 ¥xx · 余额 ¥xx sits right of the agent-preset label (Standard mode). The cumulative figure is folded live from the session snapshot (updates the moment a reply settles); the balance polls the DeepSeek /user/balance API (host-cached, 60 s).
- Settings panel (费用 / Cost) — edit per-model prices, pick the display tier (
auto / peak / offPeak / average), load the official DeepSeek price table, and customize the peak/off-peak schedule. Tier switches re-price instantly.
Pricing follows DeepSeek's billing: each reply is charged at the tier in effect at its own generation time — weekdays peak 09:00–12:00 and 14:00–18:00 Beijing time, everything else off-peak at half price, and weekends off-peak all day (2026-08-23 rule). auto (the default) and average both price that actual per-reply split (the time-weighted figure); peak / offPeak pin an explicit tier for manual inspection. The schedule is customizable. Prices default to the built-in official table, so the plugin works with zero configuration.
Install
Requires DeepSeek Harness 0.1.0-rc.7+. The plugin is a bundle: dsh plugin add mounts it automatically, no manual patch needed.
From GitHub
dsh plugin --profile web add github:yishan/dsh-burn
dsh web # restart the web service for the profile change to take effect
From npm (once published)
dsh plugin --profile web add dsh-burn
dsh web
From a local directory
dsh plugin --profile web add /path/to/dsh-burn
dsh web
Uninstall
dsh plugin --profile web remove dsh-burn
dsh web
Configuration
Zero-config by default: the built-in official price table (deepseek-v4-flash / deepseek-v4-pro) is back-filled on boot, and everything is editable live in 设置 → 费用 (persisted to a per-profile settings file — <profileDir>/plugins/dsh-burn/settings.json, derived from the Loader's base URL — which overrides the profile config on restart). A legacy global $DSH_HOME/plugins/dsh-burn/settings.json migrates once on first boot; settingsPath in the profile patch overrides the location explicitly.
The balance readout needs a DeepSeek API key resolved from DEEPSEEK_API_KEY.
Advanced: override via profile patch
A profile-level patch targeting id: dsh-burn replaces the bundle row's config (prices, display tier, schedule, balance endpoint):
# $DSH_HOME/profiles/web/cordis.patch.yml
- id: dsh-burn
config:
displayMode: auto # auto | peak | offPeak | average
tierSchedule: default # default | custom
customWindows: [] # [{ start: 540, end: 720, tier: 'peak' }, ...]
defaultTier: offPeak # tier for minutes not covered by customWindows
prices:
deepseek-v4-flash:
peak: { input: 3.0, cacheRead: 0.1, cacheWrite: 3.0, output: 9.0 }
offPeak: { input: 1.5, cacheRead: 0.05, cacheWrite: 1.5, output: 4.5 }
deepseek-v4-pro:
peak: { input: 9.0, cacheRead: 0.3, cacheWrite: 9.0, output: 27.0 }
offPeak: { input: 4.5, cacheRead: 0.15, cacheWrite: 4.5, output: 13.5 }
defaultPrice:
peak: { input: 3.0, cacheRead: 0.1, cacheWrite: 3.0, output: 9.0 }
offPeak: { input: 1.5, cacheRead: 0.05, cacheWrite: 1.5, output: 4.5 }
apiKeyEnv: DEEPSEEK_API_KEY # credential ref for the balance endpoint
balanceRefreshMs: 60000
balanceBaseURL: 'https://api.deepseek.com'
# settingsPath: /abs/path/to/settings.json # optional override of the profile-isolated default
Prices are CNY per 1M tokens. cacheWrite mirrors the cache-miss rate for providers that don't bill cache writes separately.
How it works
- Host (
src/index.ts, src/projection.ts): a CostMeter service folds the session log into per-model, per-tier token buckets (cost session projection — each message bucketed by the billing tier at its own timestamp), serves GET/POST /api/cost/settings, and proxies GET /api/cost/balance through the credentials seam (401 without a key, 502 on upstream failure, 60 s cache with concurrent-request dedup). The projection's totalCost is computed live from the buckets against the current price book at the current display tier, so price/tier changes apply to the whole log without a re-fold.
- Client (
src/client/): three slot seats — conversation.session.header.actions (cumulative spend + balance, right of the agent-preset label), conversation.chat.assistant-actions (per-reply cost), settings.section (the 费用 page). Token buckets are re-priced at render time against the current price book (per-reply at the reply's own tier), so tier/price changes apply instantly without a restart. The session fold is incremental (append-only O(Δ), O(1) per-message lookups via an index).
- Runtime settings persist to the per-profile settings file (see Configuration), which overrides the profile config on boot; models missing from the book are back-filled from the official price table.
POST /api/cost/settings rejects unknown keys, invalid values, overlapping custom windows (400) and oversized bodies (413), and reports the disk-write result in persisted so the UI surfaces a failed save.
Development
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm run test # vitest (pricing logic)
pnpm run build # esbuild: lib/index.js (host) + lib/client.js (browser)
The client bundle is a classic-script plugin registered on window.__ModuleLoader__ and may import only the platform module table (see build.mjs).
License
MIT