dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Live 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.
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
...
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.
inputTokens, cacheReadTokens, cacheWriteTokens, outputTokens), not a character heuristic.sum(hourly amounts) === daily amount is an identity rather than a rounding accident.# 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).
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.
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.
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 includedSession 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.
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.
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.
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. |
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:
zstdDecompressSync decodes 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.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.
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).
token_status is as fresh as the log and the ledger, not instantaneous.models entry.MIT.