dsh-usage-dashboard
中文 | English
A DeepSeek Harness (DSH) plugin: shows your DeepSeek account balance, estimated spend for the current conversation, per-conversation details (tokens & cost), and estimates remaining turns per model based on historical average spend.
Features
- Balance: calls the official DeepSeek
GET /user/balanceendpoint and shows the total balance (CNY or USD). - Current-conversation estimated spend + remaining turns: below the input box, on its own line after the built-in token stats line, live-updates "estimated spend · balance · estimated remaining rounds" (rounds = ⌊balance ÷ current-conversation average cost per turn⌋).
- Conversation details (global board): lists every historical conversation's last-active time, input tokens, output tokens, and cost in CNY (newest-first, empty/subagent sessions skipped; the current conversation is tagged).
- Remaining turns per model: aggregates historical sessions by model,
remaining = ⌊balance ÷ average cost per session⌋, plus session count, average cost, total cost, and token details.
Placement
- Line below the input box (
conversation.composer.dock, order 200): current-conversation estimated spend, current balance, estimated remaining rounds; auto-refreshes every 4s while chatting. - Settings → Usage & Balance board (
settings.section): balance card + conversation-details table + per-model remaining turns.
Install
The plugin must be resolvable by the Cordis loader from the profile directory. The DSH desktop (Electron) and web builds share the same plugin system but use different profiles (desktop / web); install steps are identical — just swap the profile name below (use web for the web build). Default Windows paths:
- Home (
$DSH_HOME):C:\Users\<you>\.dsh - Desktop profile directory:
C:\Users\<you>\.dsh\profiles\desktop\ - Web profile directory:
C:\Users\<you>\.dsh\profiles\web\ - Plugin resolution:
$DSH_HOME\profiles\<profile>\node_modules(pnpm-managed) + flat fallback$DSH_HOME\profiles\node_modules
The examples below use desktop; for the web build use web instead.
Option 1 (recommended): dsh plugin add
This package declares dsh.bundle.patch (i.e. cordis.patch.yml), so dsh plugin add does pnpm add and auto-appends it to the profile's dsh.profile.bundles, completing both install and composition:
dsh plugin --profile desktop add @gongshiyun/dsh-usage-dashboardThen restart or refresh DSH Desktop (web: refresh the page). Verify with dsh --profile desktop --dump-config that the usage-dashboard entry is active.
> Note: the unscoped npm package dsh-usage-dashboard belongs to a different, unrelated project — installing it will not give you this plugin. Always use the full scoped name @gongshiyun/dsh-usage-dashboard.
Option 2: manual install (non-bundle or offline)
1. Install the package into the profile's node_modules:
``bash cd "$DSH_HOME/profiles/desktop" pnpm add <package-path-or-name> ``
2. Edit $DSH_HOME\profiles\desktop\cordis.patch.yml and insert a top-level entry (do not edit the desktop root cordis.yml — it is overwritten to [] on every launch):
``yaml - insert: - id: usage-dashboard name: '@gongshiyun/dsh-usage-dashboard' config: currency: CNY pricing: - model: deepseek-v4-flash inputPerM: 1 cacheReadPerM: 0.02 outputPerM: 2 - model: deepseek-v4-pro inputPerM: 3 cacheReadPerM: 0.025 outputPerM: 6 ``
3. Restart DSH Desktop.
Configuration
| Key | Type | Default | Description | |---|---|---|---| | apiKeyEnv | credential-ref | DEEPSEEK_API_KEY | credential-ref name for the API key | | baseURL | string | https://api.deepseek.com | DeepSeek API base URL | | currency | CNY \| USD | CNY | pricing & balance display currency (must match pricing units) | | balanceCacheMs | number | 60000 | balance cache TTL (ms) | | pricing | array | see below | pricing history (list of eras), each era has an effective time and per-model rates |
pricing is a pricing history (sorted by effective time; cost uses the era in effect at each model call's event timestamp):
pricing:
- effective: 2026-07-24T00:00:00Z # era effective time (ISO 8601 UTC)
models:
- model: deepseek-v4-flash
inputPerM: 1 # off-peak: input (cache miss)
cacheReadPerM: 0.02 # off-peak: input (cache hit)
outputPerM: 2 # off-peak: output
- effective: 2026-08-16T16:00:00Z # official 8/17 peak/off-peak pricing
models:
- model: deepseek-v4-flash
inputPerM: 1.5
cacheReadPerM: 0.05
outputPerM: 4.5
peak: # peak-hour rates (only used when offPeak is also present)
inputPerM: 3
cacheReadPerM: 0.1
outputPerM: 9
offPeak: # off-peak rates
inputPerM: 1.5
cacheReadPerM: 0.05
outputPerM: 4.5- Events before the first era use the first era; models without an entry fall back to the era's
model: "*"entry (if any), else priced at 0. - When the provider changes prices, append a new era with a newer
effective— old sessions are priced at historical rates, new sessions at the new rates. - Legacy format is supported: a flat list of model entries (no
effective/models) is treated as a single era.
Default pricing history (CNY per 1M tokens)
| Effective | Model | Period | Input (miss) | Input (hit) | Output |
|---|---|---|---|---|---|
| 2026-07-24 (V4 launch) | deepseek-v4-flash | Base | ¥1 | ¥0.02 | ¥2 |
| 2026-07-24 | deepseek-v4-pro | Base | ¥3 | ¥0.025 | ¥6 |
| 2026-08-16T16:00Z (CN 8/17 onward) | deepseek-v4-flash | Peak | ¥3 | ¥0.10 | ¥9 |
| 2026-08-16T16:00Z | deepseek-v4-flash | Off-peak | ¥1.5 | ¥0.05 | ¥4.5 |
| 2026-08-16T16:00Z | deepseek-v4-pro | Peak | ¥9 | ¥0.30 | ¥27 |
| 2026-08-16T16:00Z | deepseek-v4-pro | Off-peak | ¥4.5 | ¥0.15 | ¥13.5 |
Peak hours: UTC 01:00–04:00 and 06:00–10:00 (Beijing 09:00–12:00 and 14:00–18:00); other hours are off-peak (peak price ÷ 2).
Source: <https://api-docs.deepseek.com/quick_start/pricing/> (CNY: <https://api-docs.deepseek.com/zh-cn/quick_start/pricing/>). Note: the default rates are baked in and do not auto-follow official price changes — after another change, add a new era (or wait for a plugin release). The plugin does not crawl the official page.
Data model & assumptions
- Token accounting: reads provider
usagefromassistant/messageevents
(inputTokens = cache-miss input, cacheReadTokens = cache-hit input, outputTokens = output, matching DeepSeek's prompt_cache_miss_tokens / prompt_cache_hit_tokens / completion_tokens).
- Retry dedup: on the same
(turn, step), only the lastassistant/message(afterllm/retry) counts — consistent with the token meter. - Cost formula:
cost = (inputTokens×inputPerM + cacheReadTokens×cacheReadPerM + outputTokens×outputPerM) / 1e6. - Session source: uses
ctx.sessionQuery(merges in-memory live sessions + persisted cold sessions); without it, falls back to currently-loaded sessions. Persisted session usage is cached per id and rebuilt on restart. - Conversation details / latest:
conversationsis newest-by-last-activity, only includes sessions with real token usage (skips empty and subagent sessions); the first non-current entry is "latest". - Remaining turns (model):
⌊balance ÷ that model's average cost per session⌋;—when no history or zero average cost. - Estimated remaining rounds (current):
⌊balance ÷ (current-session total ÷ turns)⌋;—when the current session has no completed turns yet. - Balance amounts are strings: DeepSeek returns
total_balanceetc. as strings; the plugin converts them to numbers for display and math.
Architecture (for contributors)
- host (
lib/index.js): a Cordis plugin{ name, inject, apply, Config }whose core is a
TypertRemoteService subclass UsageDashboardGateway exposing the SRC Remote endpoints balance() and overview(sessionId?). SRC mode is derived at runtime from @Remote markers by dsh-api-gateway, so no Typert-compiler-generated strict descriptors are needed — hence the installRemote() helper that manually expands the decorators (plain-JS has no decorator syntax).
- client (
lib/client.js): thewindow.__ModuleLoader__.load({ id, factory })form, registering
React components via ctx.slots.inject(...) into settings.section and conversation.composer.dock; data is fetched via ctx.connection.rpc.call('/api', 'usageDashboard/<method>', { args }).
Limitations
- Balance is polled on demand (composer 4s, board on open), not pushed.
- With no network or no configured API key, balance shows an error; cost stats still work (from local session data).
- Cross-session history averaging depends on the
dsh-session-queryservice (included in the default DSH composition).