dsh-token-bill
Manifest validLive DeepSeek balance and per-hour token costing for DSH: two agent tools read the live balance and the ledger-observed daily charge, then write Markdown/HTML/JSON statements.
dsh-token-bill
English | 中文
Real-time token billing for DeepSeek Harness (DSH): the live account balance, today's actual spend, per-hour token counts folded into cost bars, and a Markdown / HTML / JSON statement you can keep.
Two model-facing tools, no UI required, no extra daemon:
token_status— live balance, today's observed spend, today's token buckets.token_bill— hourly bar charts, peak/valley split, per-hour detail table, and a written statement.
📊 2026-09-28 token bill
Balance ¥52.9100 · Today ¥4.6900 (ledger-observed)
Tokens 79.65M (input 1.02M · cache hit 78.14M · output 492.7K) · 98.1% cache hit · 716 calls
Busiest hour 15:00 (29.00M tokens) · Costliest hour 15:00 (¥2.3380)
Tokens per hour (6 hours with usage)
00:00 │█████████▃ │ 9.89M
01:00 │███████▂ │ 7.72M
11:00 │██▆ │ 2.92M
12:00 │██████████████▇ │ 15.80M
13:00 │█████████████▄ │ 14.33M
15:00 │██████████████████████████│ 29.00M
Cost per hour (calibrated to the observed daily charge)
00:00 │██████▃ │ 0.5607
01:00 │██▅ │ 0.2259
...
Why this plugin exists
DSH deliberately treats token counts as measurement, not as a billing record: ctx.tokenMeter reports request pressure, nothing in the harness converts tokens into money, and it ships no price table at all. Meanwhile the three things you actually want are scattered:
| What you want | Where it lives | Authority |
|---|---|---|
| Account balance | GET https://api.deepseek.com/user/balance (API-key auth) | live, authoritative |
| What today actually cost | $DSH_HOME/.dshw-usage.json balance ledger | observed, authoritative |
| Tokens per hour | $DSH_HOME/sessions/**/session.v*.jsonl.zstd | provider-reported, exact |
dsh-token-bill joins them under one rule: money is observed, tokens are measured. The day's charge comes from the account's own balance movement, and the price table is used only to spread that observed total across the day's hours.
Features
- 💰 Live balance from the official balance endpoint, falling back to the ledger's last observation (explicitly labelled) when the network fails.
- 📊 Per-hour token counts read from durable session logs — the four disjoint provider buckets (
inputTokens,cacheReadTokens,cacheWriteTokens,outputTokens), not a character heuristic. - 🧾 Statements on disk: self-contained HTML with inline SVG bar charts, Markdown for archiving, raw JSON for scripting.
- ⛰️ Peak/valley aware: Beijing-time peak windows, weekend and statutory-holiday valley rates, and a peak-vs-valley split in the report.
- 🎯 Exact arithmetic: money is carried as integer 1e-8 units, so
sum(hourly amounts) === daily amountis an identity rather than a rounding accident. - 🔒 Read-only: the plugin never writes to your ledger, session logs, or credentials.
Install
# from a registry, once published
dsh plugin --profile web add dsh-token-bill
# from a local checkout
dsh plugin --profile web add link:/absolute/path/to/dsh-token-bill
Both tools then appear to the model in new sessions. If your profile does not reload on its own, restart the DSH web server afterwards.
Requirements: Node ≥ 22.12 (the plugin resolves its DSH peers through require(esm)), and a DSH profile with the tools service mounted (any standard Web profile).
The balance ledger
Today's actual charge comes from a ledger at $DSH_HOME/.dshw-usage.json, written by the excellent dsh-whale-widget balance widget — not by this plugin. Each day row records an opening balance, the last observed balance, and accumulated debits and credits:
{
"openingUnits": 760000000,
"lastUnits": 5525000000,
"debitUnits": 388000000,
"creditUnits": 5000000000,
"firstAt": 1790529962822,
"lastAt": 1790580387568
}
Amounts are integer 1e-8 CNY units. The observed charge is the sum of balance decreases, so a top-up adds to creditUnits and can never inflate your spend. If the row also closes (opening − debits + credits = last), a top-up is reported as information; if it does not close, something moved the balance that the ledger could not classify, and the plugin says so rather than quietly reporting a wrong number.
If you do not run that widget, the ledger is simply absent and the plugin degrades to price-table estimates, clearly labelled as estimates — the balance still comes from the live endpoint.
Tools
token_status
| Parameter | Type | Default | Meaning |
|---|---|---|---|
includeLedger | boolean | true | Append the ledger's path, age, and event count. |
includePricing | boolean | false | Append the peak/valley price table. |
Returns day, balance, currency, balanceSource (api or ledger), balanceStale, todayObserved, todayTokenEstimate, totalTokens, inputTokens, cacheReadTokens, outputTokens, calls, ledgerPath, text.
token_bill
| Parameter | Type | Default | Meaning |
|---|---|---|---|
day | string | today | Statement date, YYYY-MM-DD, Beijing time. |
days | integer | 1 | Also include a per-day summary for the last N days. |
costMode | ledger | token | both | ledger | See Cost model. |
write | boolean | true | Write the statement files to disk. |
formats | array of html | md | json | ["html","md"] | Which statement files to write. |
Returns day, amount, amountSource (ledger or token-estimate), currency, balance, totalTokens, calls, activeHourCount, peakTokens, valleyTokens, files, and the full text rendering.
Files are written to $DSH_HOME/token-bill/ as token-bill-<day>.{html,md,json}. The host process's working directory is the DSH installation itself, so the default is deliberately not process.cwd(); override it with DSH_TOKEN_BILL_DIR or the outDir config.
Data sources
api.deepseek.com/user/balance ──► live balance ─┐
├──► report ──► bar charts + statement
$DSH_HOME/.dshw-usage.json ─────► observed ¥ ──┤
$DSH_HOME/sessions/**/*.zstd ───► exact tokens ─┘
Balance — GET https://api.deepseek.com/user/balance with Authorization: Bearer <DEEPSEEK_API_KEY>, returning balance_infos[] with total_balance / granted_balance / topped_up_balance per currency. This needs no browser session. The key is read from the DEEPSEEK_API_KEY environment variable (also DSH_DEEPSEEK_API_KEY, DEEPSEEK_KEY) or from refs.DEEPSEEK_API_KEY in $DSH_HOME/.credentials.yaml.
Token usage — every durable assistant/message event carries the adapter-reported usage alongside an epoch-millisecond time, which is what makes hourly accounting possible. Buckets are disjoint exactly as the provider reports them:
inputTokens— uncached prompt input (a cache miss)cacheReadTokens— prompt input served from cache (a cache hit)cacheWriteTokens— prompt tokens written into the cacheoutputTokens— completion tokens, reasoning already included
Session logs are session.v<generation>.jsonl.zstd under $DSH_HOME/sessions/<projectKey>/<sessionId>/, header line first, with each append written as its own checksummed Zstandard frame. Older uncompressed session.v<generation>.jsonl logs are read too.
Cost model
costMode decides where the money number comes from:
| Mode | Daily amount | Per-hour amounts | token estimate column |
|---|---|---|---|
ledger (default) | observed charge from the ledger | apportioned, calibrated to that total | shown |
token | price-table estimate | price-table estimate | n/a |
both | observed charge from the ledger | apportioned, calibrated to that total | shown |
In ledger mode the per-hour amounts are computed by largest-remainder apportionment at the statement's own precision, so the hours a statement prints always add up to the total it prints. When the ledger has no observation for a day, the plugin falls back to a price-table estimate and labels it as such — it never presents an estimate as an observed charge.
Peak / valley pricing
Beijing time. Peak (standard) rates apply Mon–Fri 09:00–12:00 and 14:00–18:00. Every other hour, plus all weekend days and all statutory holidays, bills at the valley rate (half the peak rate). Prices are CNY per million tokens, shown as valley / peak:
| Model | Cache hit | Cache miss | Output |
|---|---|---|---|
deepseek-flash, deepseek-v4-flash, deepseek-v4-flash-vision-exp, deepseek-chat | 0.02 / 0.04 | 1 / 2 | 4 / 8 |
deepseek-pro, deepseek-v4-pro, deepseek-reasoner | 0.15 / 0.3 | 4.5 / 9 | 13.5 / 27 |
An unrecognised model name falls back to the Flash series so a new model still produces a usable estimate. The statutory holiday list (HOLIDAYS_2026, 34 days) lives in lib/pricing.mjs and can be extended through config when a new State Council calendar is published.
Configuration
Add a config block to the plugin's row in your profile's cordis.patch.yml:
- id: token-bill
name: dsh-token-bill
config:
costMode: ledger # ledger | token | both
decimals: 4 # decimals used in printed amounts
chartWidth: 26 # bar width in terminal charts
historyDays: 1 # days included in the per-day summary
writeFiles: true # set false to never write statements
timeoutMs: 10000 # live balance request timeout
outDir: !!js dshHomePath('token-bill')
sessionsRoot: !!js dshHomePath('sessions')
# Override or extend the price table (CNY per million tokens):
# models:
# my-model: { hit: { off: 0.01, peak: 0.02 }, miss: { off: 1, peak: 2 }, out: { off: 4, peak: 8 } }
# Extra statutory holidays, billed at the valley rate all day:
# holidays: ['2027-01-01']
| Field | Default | Meaning |
|---|---|---|
dshHome | $DSH_HOME or ~/.dsh | DSH home directory. |
profile | $DSH_PROFILE or web | Profile whose in-profile ledger path is also checked. |
sessionsRoot | <dshHome>/sessions | Root of the session logs. |
outDir | $DSH_TOKEN_BILL_DIR or <dshHome>/token-bill | Statement output directory. |
decimals | 4 | Decimal places in printed amounts. |
chartWidth | 26 | Bar width for terminal charts. |
costMode | ledger | Money source, as above. |
historyDays | 1 | Days in the per-day summary. |
maxBytes | 134217728 (128 MB) | Skip any single session log larger than this. |
timeoutMs | 10000 | Balance request timeout. |
writeFiles | true | Whether statements may be written. |
models | built-in table | Price table override. |
holidays | HOLIDAYS_2026 | Statutory holiday day keys billed at the valley rate. |
Architecture
lib/index.js Plugin entry: name / inject / Config / apply, registers both tools
lib/service.mjs Orchestration: resolve config -> gather -> fold -> report -> write
lib/account.mjs API-key discovery, balance endpoint, live-to-ledger fallback policy
lib/ledger.mjs Read-only reader for $DSH_HOME/.dshw-usage.json
lib/logs.mjs Session enumeration, multi-frame zstd decode, hourly/daily folds
lib/pricing.mjs Peak/valley schedule and the DeepSeek price table
lib/money.mjs Exact decimal money (integer 1e-8 units)
lib/report.mjs Report assembly, terminal charts, HTML statement
lib/markdown.mjs Markdown statement
lib/chart.mjs Terminal bar charts
Implementation notes worth knowing:
- Money never touches a binary float. Every amount is an integer number of 1e-8 units. Model unit prices are tiny and accumulate over thousands of calls — exactly where floats drift — which is why the reconciliation identity holds.
- Session logs are multi-frame Zstandard. Node's
zstdDecompressSyncdecodes only the first frame and reports no consumed length, so a naive read silently truncates a log to its header line.decodeZstdFrames()locates frames by their magic sequence and validates each slice by decoding it; a torn final append simply ends the scan, which is the correct behaviour for a log being written while it is read. - The ledger is read, not polled. A balance widget is already observing your account on a timer; reusing its record avoids two writers racing over one file, and only the balance display makes a network call.
- Only recently touched logs are opened, filtered by mtime, so a one-day statement does not pay to decompress the whole history.
Privacy
Everything stays local. The only network request is the balance query to api.deepseek.com, authenticated with your own API key and sent directly from the DSH host process. Statements are written under $DSH_HOME/token-bill/. Nothing is uploaded to the plugin author, and the plugin contains no telemetry.
Testing
node test/unit.mjs # 21 pure-function tests: money precision, peak hours, zstd frames, ledger rows
node test/report.mjs # 8 report tests: apportionment identity, precision grids, HTML escaping
node test/smoke.mjs # end-to-end against your real ledger, balance endpoint, and session logs
node test/verify.mjs # drives the real execute(), checks output schemas and HTML structure
npm test runs the three dependency-free suites (docs, unit, report). smoke and verify read your real DSH data and write statements to the package-local .dsh-token-bill/ (gitignored), so they need a working DSH home and are best run from a source checkout — test/ is not part of the published package (files ships lib/, the patch, the READMEs, and the license).
Known limitations
- The ledger depends on the balance widget. Without it, the money columns become price-table estimates rather than observed charges. The balance itself is always live.
- Session logs are appended to disk, so the newest turn may not be written yet.
token_statusis as fresh as the log and the ledger, not instantaneous. - Per-hour amounts assume one price table for the whole day. On a day the provider changes prices, the hourly split is an apportioned share of the observed total, not a per-call reconciliation.
- The holiday calendar is maintained by hand. A statutory holiday missing from the list is billed at the peak rate.
- Only DeepSeek is supported. The balance source and price tables are DeepSeek-specific; another provider would need its own balance source plus a
modelsentry.
License
MIT.
Comments
Loading…
Similar plugins
by qwert702
Developer tool: live token usage & cost monitoring for DeepSeek Harness - consumed tokens for the current session and across all sessions, read from token-meter projections. No model calls.
★ 7
MIT
TypeScript
Sep 19, 2026
dsh plugin --profile web add dsh-token-viewerReal-time DeepSeek API balance (official endpoint) and today-spend estimate (this DSH instance session-log usage x per-model price table) for the DSH web: composer dock strip, always-on floating badge
★ 0
dsh plugin --profile web add dsh-deepseek-cost-liveby dog-lin
Real-time session cost meter for the DeepSeek Harness web GUI: folds provider token usage into a per-session cost projection and displays it live under the composer.
★ 0
MIT
JavaScript
Aug 18, 2026
dsh plugin --profile web add dsh-session-costby EasyTZ
余额与费用:DeepSeek 余额、日/周/月花费、各模型用量与实时单价。Balance and cost panel for DeepSeek Harness: balance, spend, per-model usage, live pricing.
★ 0
MIT
JavaScript
Sep 10, 2026
dsh plugin --profile web add @easytz/dsh-ui-balanceby nonewind
Token usage & cost monitor for DeepSeek Harness — floating widget with multi-dimensional stats, time-series charts, auto-detected billing plans (Code/Token) and estimated spend.
★ 8
↓ 256/wk
MIT
JavaScript
Sep 7, 2026
dsh plugin --profile web add dsh-spendby YZz-S
Community plugins for DeepSeek Harness (DSH) Web GUI: session token cost meter with official dynamic pricing, DeepSeek & Volcengine billing balance, and update checker. Plain JavaScript, no build requ
★ 3
MIT
JavaScript
Aug 15, 2026
dsh plugin --profile web add dsh-token-cost-meter