# dsh-token-monitor
็ฎไฝไธญๆ | English
> ๐ค **AI-generated disclaimer**: This project was generated with AI assistance and is provided for learning and technical research only โ no commercial warranty or support is implied. A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that puts a **"ไปๆฅ็จ้ / today's usage" card at the sidebar foot** (next to the Settings gear) showing today's token total in real time (e.g. "ไปๆฅ็จ้๏ผ8888ไธ"); **clicking the card pops the full stats window** โ today / 7-day / 30-day token totals, a 30-day per-model stacked usage chart, a 30-day per-model usage ranking (fixed window), and conversation counts. No settings page involved: one click and you're in.  ## Features - ๐ **One-click access** โ the sidebar "ไปๆฅ็จ้" card shows today's token total live on one line ("ไปๆฅ็จ้๏ผ8888ไธ"; refreshes every 30 s; the 56 px rail keeps only the compact value); clicking opens the stats window (close with Esc / โ / clicking the mask; background scroll is locked while open). - ๐ **30-day model usage** โ a 30-day stacked bar chart: each bar is stacked per model by color (the day's heaviest model at the bottom; colors fixed by the 30-day rank). The chart area has a full grey dot-grid background (exactly two dot columns per day, 13 px row pitch, per `temp/model-usage.png`), with bars drawn on top of the dots; the axis shows only the weekly Monday baseline dates; hover/click a day to pin the breakdown card (date / total / per-model swatch rows, styled after `temp/tooltip.png`) โ no highlight/dim effects and no label above the bar. - ๐ **Model usage ranking** โ fixed 30-day window: top-4 models in a **2ร2** grid (rank number on its own line / model + token total / provider + right-aligned usage share โ no swatch, no growth or unit labels); cards scale slightly on hover. - ๐ฌ **Conversation stats** โ top-level conversations opened today / 7 days / 30 days (subagent sessions excluded), with the corresponding model request counts. - ๐ **Auto refresh** โ 30 s polling (with an idempotent re-scan that self-corrects the numbers) plus manual "ๅทๆฐ / Refresh" and "ๅๅกซๅๅฒ / Backfill" buttons. - ๐พ **Persistence** โ buckets are written to `$DSH_HOME/plugins/token-monitor/data.json` and survive restarts (181-day daily buckets; per-model breakdown inside each bucket). - ๐ **Historical backfill** โ session logs are the single source of truth: on startup, on the "ๅๅกซๅๅฒ / Backfill" button, and on every 30 s poll the plugin folds the whole in-window corpus into a fresh bucket map and swaps it in atomically, so usage from *before* the plugin was installed is included too, repeated runs never double-count, and stale buckets self-heal. - ๐จ **DSH-native styling** โ light theme, 1 px hairline cards, 8โ10 px radii, no shadows; colors use `--dsw-alias-*` theme variables so it follows DSH's dark theme as well. ## Install From this repository: ```bash dsh plugin --profile web add github:zhangzheng25/dsh-token-monitor ``` Or from a local checkout: ```bash dsh plugin --profile web add E:\path\to\dsh-token-monitor ``` `dsh plugin` forwards to pnpm inside the profile directory and reconciles `dsh.profile.bundles`; the package's `dsh.bundle.patch` (`cordis.patch.yml`) inserts the plugin row into the host composition, and `dsh.client.platform: "web"` makes the web shell serve `client/bundle.js`. **Restart DSH**: the "ไปๆฅ็จ้ / today's usage" card appears at the sidebar foot (next to the Settings gear); click it to pop the stats window. ## How it works ``` โโโโโโโโโโโโโโโโโโโโโโโโโโโ Host (Node.js) โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ src/index.js โ โ โข backfill() โ session logs are the SINGLE source โ โ of truth (idempotent full rebuild: sessionQuery is โ โ live-preferred โ in-memory sessions and persisted logs both โ โ count; each run folds every in-window assistant/message โ โ usage event into a FRESH bucket map and swaps it in โ โ atomically, so repeated runs never double-count; runs on โ โ startup / "ๅๅกซๅๅฒ" button / 30 s polls) โ โ (input / output / cache read / cache write / reasoning, โ โ split by day AND model via message.source; 181-day window) โ โ โข buildSessionStats() โ top-level conversation counts โ โ (delegationDepth === 0), 20 s cache โ โ โข persist() โ debounced JSON write (schema v3, โ โ per-day AND per-model buckets, 181 days kept) to โ โ $DSH_HOME/plugins/token-monitor/data.json โ โ โข webServer.register('/token-monitor/today') โ light today read โ โ (no rebuild; polled by the sidebar card every 30 s) โ โ โข webServer.register('/token-monitor/snapshot') โ HTTP route โ โ consumed by the browser half (static-bundle pattern; serves โ โ modelRank aggregates + modelDaily30) โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ fetch('/token-monitor/today' | '/token-monitor/snapshot') โผ โโโโโโโโโโโโโโโโโโโโโโโโโโโ Client (browser) โโโโโโโโโโโโโโโโโโโโโโโโโ โ client/bundle.js โ hand-built web bundle following the โ โ client-modules protocol (window.__ModuleLoader__.load) โ โ โข slots.inject('sidebar.footer.action') โ "ไปๆฅ็จ้" card โ โ (click pops a fixed modal hosting the full dashboard; no โ โ settings.section registration anymore) โ โ โข metric cards + conversation cards + 30-day stacked chart + โ โ model usage ranking โ โ โข card polls /token-monitor/today every 30 s; popup fetches the โ โ snapshot on open and polls every 30 s, refresh / backfill โ โ buttons โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ``` ## Data sources - **Single source of truth = session logs**: `assistant/message` usage events in session logs, attributed via the event's `message.source` (`provider` / `model`), read through `ctx.sessionQuery` (zstd decoding handled internally; sessions are not woken). `sessionQuery` is live-preferred โ in-memory active sessions and persisted logs are both readable, so in-flight calls are counted too and no `llm/stream` hook is needed (older versions ran waterfall live capture *and* log backfill at once; the overlap counted every call twice and inflated the numbers โ v3 removed the live path and rebuilds idempotently instead). - **Conversations**: `sessionQuery.listSessions()` headers (`cwd`, `createdAt`, `delegationDepth`); only top-level sessions (`delegationDepth === 0`) count as conversations. ## Development ```bash node --check src/index.js # host half node --check client/bundle.js # client bundle ``` The client bundle is written by hand to match the `client-modules` bundle protocol โ no build step required. ## License [MIT](./LICENSE)