Skip to content
dsh-market Browse plugins GitHub 中文

huangyuheng/dsh-token-use

Settings section that reports token usage: totals for input, output, cache read, cache write, reasoning and call count, with breakdowns by model, day, month and project plus a per-day trend chart for the last 30 days; history is rebuilt once on a worker thread and then kept current from live session events.

Stars ★ 1 Category Usage & Billing Listed 2026-09-11 npm dsh-token-use

Install

Inside DeepSeek Harness, with dsh-market

dsh plugin --profile web add dshmarket

Or from the command line

dsh plugin --profile web add dsh-token-use

Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.

README

中文 | English

A real-time token usage and cost plugin for DeepSeek Harness: install it, then read your usage in Settings → Token usage (the plugin adds its own top-level Settings entry). The page refreshes every 5 seconds and stays quiet while the tab is hidden — no wasted polling.

Features

Four tabs across the top of the panel: Overview / By model / By project / Configuration.

  • Overview — the cost and totals cards, the trend chart, and a usage heatmap (last 12 months, one cell per day, coloured by total tokens or by cost; hovering shows that day's tokens, cost and calls instantly, without the native tooltip's one-second delay). Range and model filters apply here.
  • By model — every model's usage and amount, with the same range query (day / month / year / date range / all).
  • By project — every project's usage and amount, same range query.
  • Configuration — versions (dsh-service, dsh, dsh-base and this plugin: installed vs the newest release in the channel you run, rc/alpha/stable), runtime facts (platform version, Node version, PID, port, uptime, DSH_HOME), a health check, a confirmed one-click restart and a confirmed shutdown.
  • Self-check (the health check button) reports three sections: endpoint (latency, version, history scan, price data, buckets, last update), client bundles (each plugin probed at the exact revisioned URL from this page's boot manifest, i.e. what the browser really loads) and harness (profile integrity — node_modules, lockfile, boot graph; plugin package integrity — entry file, dsh.bundle.patch, client bundle; dependency resolution; single cordis instance at one version; service bundle vs the running CLI). When a freshly installed plugin refuses to start, this names the package, the missing file and the fix.
  • Cost card — spend estimated live from DeepSeek's published prices × the usage recorded here, with peak/off-peak rates. Every card (totals, trend, by model, by project) carries an amount.
  • Daily price book — the official pricing page is fetched once a day at 12:00 and cached locally, so restarts keep it and an offline machine falls back to a built-in snapshot.
  • Totals card — estimated cost, total (input + output + cache), input, output, cache read, cache write, reasoning, calls.
  • Per-model pricing — cache-hit input, cache-miss input and output are priced separately per model; anything that is not a DeepSeek model (claude, gpt, …) is never counted and shows —.
  • Range dropdown — by day / by month / by year / date range / all; defaults to by day = today. The date range starts as today → today and keeps its two ends ordered, so a backwards window can never be requested; the year list offers only the years your own logs cover.
  • Model filter — the dropdown beside the range selector on the overview narrows every breakdown to one model (including that model's project and per-day rows); defaults to all models.
  • Trend chart — drawn with a tree-shaken ECharts bundle that ships inside the plugin (no CDN). Cache read / input / output stack into an area composition whose top edge is the total (with a total reference line), each day's amount is drawn as cost bars against a ¥ axis, and calls live in the tooltip. A prominent dashed daily-average reference line (the visible window's total ÷ its days, labelled with the value) shows at a glance whether today runs above or below your usual day. The card switches between 7 / 30 / 90 days (7 by default). A day without usage is a real zero (stacking needs it), and the tooltip still names it; tick labels keep one unit across an axis and monotone smoothing never overshoots. Rendering is imperative, so moving the mouse never re-renders React.
  • Unit switch — 亿 / 万 / 千 in Chinese, B / M / K in English; remembered per browser. The tab bar and the unit switch stay pinned at the top, and the tab you were on is remembered.
  • Detail tables — by model and by project (the day dimension lives in the heatmap), each with an amount column (hover a row for its rates) and a total column.

One-click restart / shutdown

Both actions at the bottom of the configuration tab share one full-screen transition: a power ring with phase copy (preparing → stopping the process → recovering → new instance ready), drawn over the whole page without disturbing the rest of the host.

  • One-click restart — restarts the dsh web process serving the page. It replays the current command line (process.argv plus the working directory), so custom ports and flags survive; it asks for confirmation first, and the page reconnects and reloads itself about 10-30 seconds later. The script lands in $DSH_HOME/dsh-token-use/restart.sh and its output in restart.log.
  • Shutdown — stops that process and does not bring it back; the transition ends on "you can close this page now", and you start dsh web again from a terminal when you need it. Its confirm button is a solid red danger button so it cannot be hit by accident.
  • When a launch cannot be replayed (no executable entry point), the restart button is disabled with a reason instead.

The amounts are estimates

The amount card (the last of the totals cards) carries an ⓘ icon — hover it for the full note:

Amounts are estimates: DeepSeek's published prices × the usage recorded here, priced with the peak/off-peak rate in effect at each call. Official price changes, cache accounting and reporting lag can make this differ from your actual bill — the official settlement prevails.

How it is computed:

  • Priced fields: inputTokens (cache-miss input), cacheReadTokens (cache-hit input), outputTokens (includes reasoning); DeepSeek does not charge for cache writes.
  • Peak/off-peak: the pricing page publishes a peak window (currently 01:00–04:00 and 06:00–10:00 UTC on weekdays, with off-peak at half price). Each record is priced with the rate in effect when it happened, so a later price change never rewrites history.
  • DeepSeek models only: a model whose name does not contain deepseek (claude, gpt, minimax, …) contributes no amount, and neither does a DeepSeek name that matches no official price entry — both are listed in the ⓘ note.
  • What it leaves out: auxiliary calls such as web search and session-title generation are billed, but their tokens stay server-side where nothing local can read them. They are therefore counted by number and never guessed at, and the ⓘ note lists how many happened in the window — which makes this amount a lower bound on the bill. Checked against an official hourly export: every call we do count matches it token for token and tier for tier.
  • Currency defaults to CNY (the Chinese pricing page); set currency: USD to read the English one.

Why it is worth installing

You are burning tokens, but you cannot say where: which project costs the most, which model leans hardest on the cache, how much this week grew over last week.

dsh-token-use turns that into numbers you can read at a glance. It folds every call as it happens, then lays cost, totals, input, output, cache hits, reasoning and call counts out in the settings page, with filters by model, day, month or project and a smooth trend line that tells the story. Install it and you are done: nothing to configure, no session restart, and it never pops up while you are coding.

The most expensive cost is the one you cannot see — make it visible.

Token usage panel, overview: range and model filters, the cost and totals cards, the trend chart with its daily-average line, and the 12-month usage heatmap

Screenshots

Four tabs across the top of the panel; the shots below are the real panel on real data (Chinese number units).

Overview

Cost and totals cards, the usage trend (stacked composition + cost bars + orange daily-average line) and the heatmap (last 12 months, switchable between total tokens and cost).

Overview tab

By model

Every model's usage and amount, with the range query (day / month / year / date range / all).

By model tab

By project

Every project's usage and amount, same range query.

By project tab

Configuration

Versions (compared inside your release channel), runtime facts, the health check, the one-click restart and the shutdown — the last two ask for confirmation and run a full-screen transition.

Configuration tab

The self-check that the health button unfolds: endpoint, client bundles and the harness walk.

The self-check results

Install

dsh plugin --profile web add dsh-token-use
# or from source: dsh plugin --profile web add github:huangyuheng/dsh-token-use

Restart dsh web afterwards. To install from an unpacked zip instead:

dsh plugin --profile web add /path/to/dsh-token-use

Performance design

  • The host side never polls and sets no periodic timer other than the daily price refresh: it folds usage in O(1) increments from the session/event bus (one dictionary addition and one multiplication per assistant/message).
  • History is rebuilt once at boot by streaming $DSH_HOME/sessions/**/session.jsonl.zstd (native zstd from node:zlib), yielding the event loop between files (scheduler.yield()) so session handling is never blocked. A per-session sequence watermark de-duplicates the rebuild against live events, whatever order they arrive in.
  • Price refresh: one HTTPS GET per day at 12:00 local time (15 s timeout, retried an hour later on failure), stored atomically in $DSH_HOME/dsh-token-use/pricing.json (up to 30 snapshots). A restart reads the cache synchronously and does not re-fetch; a failed fetch falls back to the built-in snapshot, so the panel always shows numbers.
  • Five routes are exposed (loopback only, no-store): GET /dsh-token-use for the snapshot, GET /dsh-token-use/config for versions and runtime facts, GET /dsh-token-use/health for the harness self-check, POST /dsh-token-use/restart for the confirmed restart and POST /dsh-token-use/shutdown for the confirmed shutdown (neither POST answers a GET).
  • How the self-check probes: client bundles are fetched at the revisioned URL from this page's boot manifest (a plugin URL embeds a build revision, so only the manifest can address it); the harness walk reads the profile directory, resolves each real entry file through exports["."] / exports["./client"] / main, and walks up from every plugin directory to spot duplicate @deepseek-ai/cordis copies — all read-only, never loading or executing the plugins it inspects. The report carries structured fields plus paths and versions; the panel renders the wording in your language.
  • Version checks read the registry's abbreviated packument (versions + dist-tags) and compare against the highest release in the channel you run (rc / alpha / stable) — @deepseek-ai/dsh-web-app keeps a stale 0.0.1-rc.1 on its latest tag, so a plain latest comparison would report a false upgrade. The answer is cached for 6 hours in $DSH_HOME/dsh-token-use/versions.json.
# everything
curl http://127.0.0.1:3080/dsh-token-use
# a date / a month / a year / a span
curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'
curl 'http://127.0.0.1:3080/dsh-token-use?day=2026-09-10'
curl 'http://127.0.0.1:3080/dsh-token-use?year=2026'
curl 'http://127.0.0.1:3080/dsh-token-use?from=2026-09-01&to=2026-09-15'
# a model (combinable with day/month/year/from+to)
curl 'http://127.0.0.1:3080/dsh-token-use?model=deepseek-v4-flash'
# versions and runtime facts (the configuration tab)
curl http://127.0.0.1:3080/dsh-token-use/config
# harness self-check (the configuration tab's health button)
curl http://127.0.0.1:3080/dsh-token-use/health
# restart the service (the configuration tab's button; degrades after 2 s and replays the same command)
curl -X POST http://127.0.0.1:3080/dsh-token-use/restart
# shut it down (the configuration tab's button; stops and does not come back)
curl -X POST http://127.0.0.1:3080/dsh-token-use/shutdown

Every bucket carries a cost (the estimate); pricing holds the current price book, when it was fetched and when it refreshes next; modelPricing maps each observed model to its rates or to the reason it carries no amount. trend spans the last 366 days — the chart takes the last 7/30/90 of it and the heatmap takes the whole run.

Development

pnpm install          # development only (esbuild + echarts)
pnpm run build        # regenerate client/client.js (= tree-shaken ECharts + client/src.js)

client/client.js is a committed build artifact, so users install nothing and build nothing.

Development loop (with this repo linked into a profile: dsh plugin --profile web add /path/to/dsh-token-use):

  • Edit client/src.js → pnpm run build → the panel in the browser hot-reloads by itself (the host stat-polls every plugin row's client bundle, pushes rebuilt over the SSE channel /plugins/events, and the browser swaps it in) — no dsh web restart and no page refresh; measured swap latency is under a second.
  • Edit lib/*.js (the host half, e.g. pricing.js) → still needs a dsh web restart.
  • Hot reload requires an idempotent apply(): a reload brings a new fiber in, and re-registering the same locale namespace or slot on top of the previous one throws, which takes the whole settings section down until the page is refreshed. The dictionaries, the rail stylesheet and the settings.section registration here all take over from the previous fiber, so reloads are safe.

The UI is built with DSH's own component library @deepseek-ai/dsh-client-ui-primitives (Button / Pill / Input / Menu / Tooltip / StateDot and its icon set): a packaged plugin just requires it, and each control degrades to a native one on a shell that predates it. When previewing a dynamic cordis plugin in creator mode — where external imports are unavailable — use window.__DSH_MODULES__.import("@deepseek-ai/dsh-client-ui-primitives") instead (the module is one of the shell's static modules; a normal Web GUI has no window.__DSH_MODULES__).

Published to npm as dsh-token-use; its repository field points back here, which is how the official market links the package and shows download counts. To cut a release: bump version, run npm publish, and users pick it up with dsh plugin update or the market's update button.

Field definitions

  • input / output — input and output tokens as reported by the API.
  • cacheRead / cacheWrite — prompt cache read/write tokens (the API counts cache reads on the input side for billing).
  • reasoning — reasoning tokens.
  • cost — the estimate from those fields and the rates in effect; cache writes are not billed and reasoning is already inside output, so nothing is counted twice.
  • auxiliary — how many auxiliary calls in the window the amount leaves out (search web search, title session titles).
  • Model attribution — the model of the session's most recent request/header; small calls that carry no usage record (title generation, for example) are not counted.
  • Project attribution — the cwd in the session's creation header (live events read session.header.cwd, history reads the log header); subagent and forked sessions inherit their parent's project; usage that cannot be attributed yet is held and back-filled as soon as it resolves, only reaching (no cwd) after 30 seconds. |

Compatibility

These differences are already handled before anyone else installs the plugin:

Dimension Notes
Runtime Requires dsh web ≥ 0.1.0-rc.6 (the settings sidebar settings.section slot). The host half uses Node built-ins only; zstd decoding uses node:zlib (built in from Node 22.15, and dsh itself requires ^22.19 || >=24, so it is always present).
Settings rail glyph The shell hardcodes the rail glyph per section id and falls back to the same settings gear for every unknown id (ours and the market plugin's alike) — a slot registration cannot name an icon. The plugin masks its own bar-chart glyph over that gear with a structure-only selector anchored on a marker class inside its own label: no dependency on the shell's hashed class names, and if a future shell changes the markup the selector stops matching and the gear simply stays (never two glyphs), with no functional impact.
Data directory Resolved from $DSH_HOME (environment variable, defaulting to ~/.dsh), matching dsh's own dsh-home-paths rules; a custom home works too. Recorded usage is read-only; the only file written is the price cache $DSH_HOME/dsh-token-use/pricing.json (atomic replace), and pricing.enabled: false turns both the fetch and that write off.
Session formats Handles session.jsonl[.zstd] (multi-frame zstd with checksums), the versioned next generation session.v<N>.jsonl[.zstd] that dsh leaves alongside the old file after a migration, and plaintext .jsonl. A session is read from its highest-version generation only, so a migration never double counts; .bak, .corrupt-* and session.lock are skipped, and a single corrupt frame only raises scan.skipped.
Directory layout Follows the official JSONL persistence layout sessions/<project dir>/<session dir>/, reading every session independently.
Accounting Some providers (the pi-ai adapter, for instance) fold reasoning tokens into output, so a reasoning column of 0 is normal there; calls that record no usage (title generation, web search) are not counted; model attribution uses the session's most recent request/header.
Network The endpoint is loopback-only by default. On a LAN deployment (trustedHosts configured) set allowRemote: true in the profile patch; it still accepts same-origin requests only, and the client names the reason when it sees a 403. Outbound traffic has exactly one purpose: fetching the official pricing page from api-docs.deepseek.com once a day (repoint it with pricing.url, or turn it off with pricing.enabled: false).
Performance The history rebuild runs on a worker thread, so the host event loop is never blocked; events arriving meanwhile are buffered and replayed after the scan, with the session sequence watermark keeping the two folds distinct. Only event increments happen afterwards.
Multiple instances Every $DSH_HOME is counted separately; several profiles under one home share the sessions directory, so their usage is merged.

Configuration (overridable in cordis.patch.yml)

- id: dsh-token-use
  name: 'dsh-token-use'
  config:
    endpoint: /dsh-token-use
    scanAtBoot: true    # false counts only usage recorded after the plugin starts
    allowRemote: false  # set true for LAN (non-loopback) access; same-origin only
    pricing:
      enabled: true     # false turns the daily fetch off (built-in/existing snapshots only)
      currency: CNY     # CNY (Chinese pricing page, 元) or USD (English page, $)
      refreshHour: 12   # local hour of the daily fetch
      # url: https://api-docs.deepseek.com/zh-cn/quick_start/pricing  # custom pricing page

After installing

  1. Restart dsh web (a bundle membership change only takes effect on restart);
  2. Open Settings → Token usage;
  3. Or from the command line: curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'.

Content from the project README on GitHub ↗

Comments

Comments live in GitHub Discussions. Sign in with GitHub to post or react.