DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

SanChou0627 /

SanChou0627/dsh-token-bill

Verified

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.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@3856aa3b

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 amount is 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 cache
  • outputTokens — 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 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.
  • 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_status is 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 models entry.

License

MIT.

—/ 5

No ratings yet

Verified DSH bundle

Commit 3856aa3bf1ce

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout