chou109 / dsh-strength-meter

Listed

强度表是 DSH Web 开源插件:设置页按模型统计 token 用量(环形图、热力图、趋势),数据仅来自本地日志。一条命令安装,重启即用。 dsh-strength-meter: a DSH plugin. Settings shows per-model token usage (donut, heatmap, trend), local logs. One command, restart.

mainModel View source

Installation

npx -y @deepseek-ai/dsh plugin --profile web add github:chou109/dsh-strength-meter

This installation command is an unverified starting point generated from the GitHub repository address.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit ccc454fSynced Aug 17, 2026

⚡ dsh-strength-meter — Model Usage Stats for DSH Web

dsh-strength-meter: a model token-usage statistics plugin for the DeepSeek Harness Web UI — a full "Usage stats" page in Settings (metric cards, per-model donut, activity heatmap, daily trend). All data comes from your local session logs; nothing is uploaded.

LanguageLink
EnglishThis document
中文README.zh.md

Table of Contents


If you are a human, read this

What this plugin does

The plugin adds a full "Usage stats" page under Settings → Usage stats, presented in the following blocks:

Block 1 — six metric cards (overview at the top)

Metric cards

  • Tokens used: total token consumption in the selected range;
  • Sessions: sessions active in the range;
  • Messages: total messages in the range;
  • Active days: days with any activity in the range;
  • Current streak: consecutive active days up to today (GitHub-style);
  • Top model: the model that consumed the most tokens in the range, with its share (%).

The header offers a time-range switch (Last 7 days / Last 30 days) and a Refresh button (forces a fresh recomputation, bypassing the cache).

Block 2 — Model usage (per-model totals)

Model usage

  • A donut of the total token consumption in the range;
  • A legend on the right: model name, tokens and share (%). Same-named models across providers are merged into one row.

Block 3 — Activity heatmap

Activity heatmap

  • A 15-week × 7-day matrix in GitHub-contribution style;
  • Cell shade scales against the highest-token day in history: ≥75% darkest, ≥50% next, ≥25% mid, >0 light blue, no activity blank (legend "Less → More").

Block 4 — Daily token trend

Daily trend

  • A bar chart of tokens per day within the selected range (hover for the exact value), with auto-labeled date ticks.

Statistics are computed from your local persisted session logs (~/.dsh/sessions) — nothing is uploaded.

Manual install

Prerequisites (all must hold):

  1. A working DSH Web (dsh web) reachable at http://127.0.0.1:3080;
  2. You can locate the DSH data dir: %USERPROFILE%\.dsh on Windows, ~/.dsh elsewhere (written as ~/.dsh below);
  3. Network access to this repository (or a local copy).

Option A — one-command install (recommended, no npm, no build)

In mainland China, use the Gitee mirror instead: https://gitee.com/chill109/dsh-strength-meter (swap the repo URL in the commands below).

Windows (PowerShell):

powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest 'https://github.com/chou109/dsh-strength-meter/raw/main/install.ps1' -OutFile install.ps1; .\install.ps1"

macOS / Linux / WSL (bash):

bash <(curl -fsSL https://raw.githubusercontent.com/chou109/dsh-strength-meter/main/install.sh)

The installer does three things (idempotent — safe to re-run):

  1. puts the source into ~/.dsh/plugins/dsh-strength-meter (zip download or git clone);
  2. creates a junction (Windows) / symlink (macOS·Linux) in ~/.dsh/profiles/node_modules;
  3. registers the dsh-strength-meter entry in ~/.dsh/profiles/web/cordis.patch.yml.

Option B — manual, three steps (offline environments / to understand the wiring)

  1. Get the source: git clone https://github.com/chou109/dsh-strength-meter.git (or download the zip) anywhere, e.g. ~/.dsh/plugins/dsh-strength-meter;
  2. Link it (the loader resolves plugins from ~/.dsh/profiles):
    # Windows (junction, no admin rights)
    New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\node_modules\dsh-strength-meter" -Target "$env:USERPROFILE\.dsh\plugins\dsh-strength-meter"
    
    # macOS / Linux (symlink)
    ln -s ~/.dsh/plugins/dsh-strength-meter ~/.dsh/profiles/node_modules/dsh-strength-meter
    
  3. Register: append to ~/.dsh/profiles/web/cordis.patch.yml:
    - insert:
        - id: dsh-strength-meter
          name: 'dsh-strength-meter'
    

Last step: restart the dsh web process — a page refresh is not enough for a newly added plugin (the plugin set is snapshotted at boot; an already-open tab keeps its old boot manifest):

# Ctrl+C the running dsh web, then run it again
dsh web

Verify the install (in order, after the restart):

  1. Settings → Usage stats: the navigation entry appears and the page renders (six metric cards, donut, heatmap, trend);
  2. Browser DevTools → Network: /plugins/dsh-strength-meter/client.js and /plugins/dsh-strength-meter/usage.json both return 200.

If any check fails, see the "If you are an AI, read this" debugging table, or send the screenshot to the AI that deploys it for you.

Let an AI deploy it for you

Paste this prompt to your AI assistant (Gitee mirror is faster in mainland China):

Please deploy the dsh-strength-meter plugin from https://github.com/chou109/dsh-strength-meter.git (or the Gitee mirror, faster in mainland China: https://gitee.com/chill109/dsh-strength-meter.git) to my DeepSeek Harness web profile. Follow the "If you are an AI, read this" section of the README strictly.


If you are an AI, read this

Deployment

1. Get the source

git clone https://github.com/chou109/dsh-strength-meter.git ~/.dsh/plugins/dsh-strength-meter
# or use the installer (zip + symlink + registration in one shot)
bash <(curl -fsSL https://raw.githubusercontent.com/chou109/dsh-strength-meter/main/install.sh)

2. Link it into the profile's node_modules (the loader resolves plugins from ~/.dsh/profiles)

# Windows (junction, no admin rights)
New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\node_modules\dsh-strength-meter" -Target "$env:USERPROFILE\.dsh\plugins\dsh-strength-meter"
# macOS / Linux (symlink)
ln -s ~/.dsh/plugins/dsh-strength-meter ~/.dsh/profiles/node_modules/dsh-strength-meter

3. Register the plugin (append to ~/.dsh/profiles/web/cordis.patch.yml, idempotent)

- insert:
    - id: dsh-strength-meter
      name: 'dsh-strength-meter'

4. Restart dsh web. The cordis plugin set is snapshotted at startup (per client-modules: "plugin-set changes take effect on restart"); HMR only refreshes bundle content of already-registered plugins.

5. Verify

  • GET http://127.0.0.1:3080/plugins/dsh-strength-meter/client.js → 200, body starts with window.__ModuleLoader__.load({;
  • GET http://127.0.0.1:3080/plugins/dsh-strength-meter/usage.json?days=30&force=1 → JSON with total / models[] / sessions / messages / activeDays / streak / topModel / byDay[] / heatmap[] / days;
  • window.__DSH_BOOT__.entries on the page contains { id: "dsh-strength-meter", ... }.

Architecture

One cordis plugin, two faces:

dsh-strength-meter/
├── lib/index.js      # Host face (Node): usage aggregation + web route
├── lib/fold.js       # Pure fold (unit-testable): event log → per-model / per-day stats
├── lib/client.js     # Client face (browser), pre-built, committed to the repo
├── src/client/*.tsx  # Client sources (esbuild build, see scripts/build.mjs)
├── scripts/build.mjs # Self-contained build: esbuild(cjs) + __ModuleLoader__ wrapper
├── install.ps1 / install.sh
└── tests/            # fold unit tests + host/bundle smoke tests

Host face (lib/index.js)

  • Depends on webServer + sessions (declared in the entry inject list, the platform convention); registers an exact route /plugins/dsh-strength-meter/usage.json (exact routes win over client-modules' /plugins prefix route, so no conflict).
  • Data sources, best-effort:
    • ctx.sessionPersistence (full history, optional): listSnapshots() returns { header, revision } (the id lives on header.id) → cold sessions are cached by revision, only changed logs are re-folded;
    • ctx.sessions (fallback): live sessions only.
  • Fold rules (lib/fold.js, aligned with token-meter semantics):
    • request/header data.header.config.{provider,model} decides billing for subsequent samples (recorded at sample time, positionally correct);
    • usage samples: assistant/chunk with chunk.type === "usage", or the usage field of assistant/message; a message sample for the same (turn, step) replaces the chunk sample;
    • sum = input + cacheRead + cacheWrite + output; aggregated per model and per day (local timezone).
  • Query params: days=7|30 scopes totals at the sample level (old data never leaks into the window); force=1 bypasses the 15 s TTL cache.
  • Metrics: tokens / sessions / messages / activeDays / streak (full history) / topModel (with share %) / byDay[] / heatmap[] (15 weeks × 7 days, shade scaled against the peak token day).
  • The payload carries a debug block (live/persisted counts and errors) for diagnostics.

Client face (src/client/)

  • Entry inject: ['slots','locale','sessions'], one slot registration:
    • settings.section (root scope) → StatsSection: the full "Usage stats" page (metrics, donut, heatmap, trend), nav label from the nav.stats key.
  • Same-named models across providers are merged into one row before rendering; shares are recomputed.

Bundle contract (mandatory, or the page boot fails)

window.__ModuleLoader__.load({ id: "dsh-strength-meter", factory: (require) => { /* CJS */ return module.exports } })
  • The artifact must be pre-built (the repo ships lib/client.js; installs never build);
  • Externals are limited to react, react/jsx-runtime and @deepseek-ai/* (resolved by the ModuleLoader's seed/static/factory tables); never bundle platform code;
  • package.json needs exports["./client"] and the dsh.client declaration (copy from this repo's package.json);
  • The bundle's ModuleLoader id is read from package.json's name by the build script, so renames cannot desync it.

Debugging

SymptomCheck
No "Usage stats" entry in SettingsInspect window.__DSH_BOOT__ — if dsh-strength-meter is absent: not restarted, or not registered in cordis.patch.yml. If present: look at Network for /plugins/dsh-strength-meter/client.js (should be 200).
Page blank / all metrics zeroRead the debug block of usage.json: are live/persisted zero? What's in errors? If sessions:0, the host is probably running pre-fix code (old payloads lack the new fields).
usage.json 404Host face didn't start: check the dsh log for errors around dsh-strength-meter: usage route; confirm webServer/sessions exist (they always do in the web profile).
Heatmap fully blankConfirm usage.json's heatmap array is non-empty with some nonzero levels; hard-refresh the page (Ctrl+Shift+R) to load the new bundle.
White screen after editing the bundleRebuilt lib/client.js yourself? Run pnpm install then node scripts/build.mjs, or restore the committed artifact.

Rebuilding the client (after touching src/client)

cd dsh-strength-meter
pnpm install        # first time only (renames/moves can break pnpm junctions — reinstall once)
node scripts/build.mjs
# writes lib/client.js (+ source map, handy for browser debugging)

Changing a registered plugin's bundle does not need a restart — a page refresh suffices (client-modules re-injects by content hash). If a pnpm run dev:web watcher is running on this machine, updates are hot. Host-side (lib/) changes do require a dsh web restart.

Tests

node tests/fold.test.mjs      # fold / window / heatmap unit tests (zero deps)
node tests/host.smoke.mjs     # route + usage.json smoke
node tests/bundle.smoke.mjs   # bundle-contract smoke (mocks __ModuleLoader__)

Operating / extending

  • Copy: src/client/locales.ts (Chinese is the key-set source of truth; en must match the key set).
  • New stat dimensions: extend the fold in lib/fold.js (event types come from the dsh-session log vocabulary), then render in StatsSection; keep the verification field list in this README in sync.
  • Heatmap bucketing: levelOf in lib/fold.js (currently 4 token-relative buckets against the historical peak day).
  • New slots: add another ctx.slots.inject(...) registration in src/client/index.ts; component props are { t, ...injectedFace }.
  • No uploads: aggregation happens only on this machine (the Host face reads ~/.dsh/sessions).

License

MIT — see LICENSE.

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected
DocumentationDetected

Repository information

Language
JavaScript
License
MIT
Last updated
Aug 17, 2026, 8:15 AM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.