# dsh-token-usage-dashboard — Codex-style token usage dashboard [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![dsh-plugin](https://img.shields.io/badge/topic-dsh--plugin-blue)](https://github.com/topics/dsh-plugin) [![deepseek-harness](https://img.shields.io/badge/topic-deepseek--harness-blue)](https://github.com/topics/deepseek-harness) English | [简体中文](README.zh.md) A Codex-style token usage dashboard for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) Web UI: 5 stat cards + a GitHub-style activity heatmap (daily / weekly) + insights + per-model usage ranking. The UI follows the DSH language setting: Chinese interface → Chinese dashboard, anything else → English. ![dsh-token-usage-dashboard screenshot](assets/dsh-token-usage-dashboard.png) ## ✨ Features - **5 stat cards**: total tokens / peak tokens (single day) / longest chat / current streak / longest streak; auto-shrinking one-line values - **Heatmap**: 53×7 rolling grid (today pinned to the bottom-right corner), hover for `Aug 15, 2026: 273k tokens · 34 requests`; **Daily / Weekly** view toggle (weekly = bottom-up 7-cell bars, hover for the week total) - **Insights**: total turns, LLM requests, sessions, active days, cache hit rate, avg tokens & duration per turn - **Favorite models**: provider/model token ranking (Top 5) with ratio bars - **Data source**: session logs (`assistant/message` usage + `request/header` model attribution + `turn/start`/`turn/end` durations), live incremental updates, persisted snapshot + incremental sync for fast restarts ## 🏗️ Architecture The plugin has two runnable forms that share the same aggregation logic: - **Bundle form** (recommended for end users): installed via `dsh plugin add`; the npm package contains `lib/index.js` (host half) and `lib/client.js` (client half). The host half registers `GET /api/token-stats` and `POST /api/token-stats/rescan` through the web server; the client half registers a "Settings → Stats" section and fetches the API. - **Dynamic-plugin form** (for maintainers/iteration): `host.js` and `client.js` are pasted into `cordis_define` for in-session deployment without restarting the profile. Data flow: 1. Host subscribes to `session/event` / `session/created` and also performs an initial backfill over historical session logs. 2. It folds `assistant/message.usage`, `request/header` model attribution, and `turn/start`/`turn/end` durations into in-memory aggregates. 3. Aggregates are periodically flushed to `~/.dsh/storages/token-stats/snapshot.json`; on restart the snapshot is loaded first, then only new events are incrementally folded. 4. The client polls `GET /api/token-stats` (2s while scanning, 30s after ready) and renders the dashboard. ## 🚀 Install (bundle) ```sh dsh plugin --profile web add github:solstice621/dsh-token-usage-dashboard dsh --profile web # restart to apply ``` Then open **Settings → Stats** for the full dashboard. Local install: `dsh plugin --profile web add file:/path/to/dsh-token-usage-dashboard`. > On first open the plugin scans all historical sessions (seconds to a minute), then updates > in real time via `session/event`; the dashboard auto-refreshes every 30s — no manual action needed. ## 🧑‍💻 Dynamic-plugin deployment (maintainers) This repo also maintains a dynamic Cordis plugin (`toksta-5`, deployed in-session via `cordis_define`, handy for iterating without restarting the profile): 1. Verify contracts with `cordis_inspect_list` / `cordis_inspect_query` (sessionQuery, session/event, session/created, harness, React/host/styles builtins, settings.section, timer, locale); 2. `cordis_define`: `plugin.kind: "new"`, `idPrefix: "toksta"`, `code.host` = `host.js`, `code.client` = `client.js`; 3. `cordis_run` (mode=`run`/`update`) to activate. > ⚠️ The bundle and the dynamic plugin register the same settings section id (`token-stats`); > don't run both at once — stop one before enabling the other. ## 📁 Files | File | Description | | --- | --- | | `lib/index.js` | **Bundle host half**: folder + backfill + live listeners + `GET /api/token-stats` (`POST /api/token-stats/rescan` as fallback) | | `lib/client.js` | **Bundle client half**: `window.__ModuleLoader__.load` factory, registers "Settings → Stats" | | `cordis.patch.yml` | bundle patch: inserts the `id: token-stats` plugin row | | `package.json` | npm package manifest (`dsh.bundle` / `dsh.client`) | | `host.js` / `client.js` | Dynamic-plugin host/client function bodies (paste into `cordis_define`) | | `plugin.json` | Plugin metadata & package history (pkg-9 … pkg-27) | | `plan.md` / `progress.md` | Design docs & progress log (incl. every bug/fix) | | `assets/dsh-token-usage-dashboard.png` | Dashboard screenshot | ## ✅ Acceptance checklist - [ ] Settings → Stats: 5 equal-width cards, one-line values, aligned labels - [ ] 53-column heatmap, today at bottom-right, year-month-day hover tips; Daily/Weekly toggle works - [ ] Month axis aligned to columns; no horizontal scrollbar - [ ] Insights (7 rows) and Top-5 model ranking with bars correct - [ ] New conversations show up within 30s - [ ] Language switch (zh ↔ en) relabels the whole dashboard on the fly ## 🩺 Troubleshooting | Symptom | Cause / fix | | --- | --- | | `pnpm not found on PATH` | `dsh plugin` needs pnpm: `corepack enable pnpm` (bundled with Node ≥16.10) | | Two "Stats" sections in Settings | bundle + dynamic plugin (toksta-5) both active; stop one | | Plain text, no styles at all | CSS not injected: dynamic `styles` is a Client Builtin (`styles.insert(css)`), not `ctx.get('styles')`; bundle injects via `