dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Lightweight, native sidebar usage panel for DeepSeek Harness: official balance + token usage history from session logs.
A native sidebar usage panel for DeepSeek Harness Web: shows your official DeepSeek balance and token usage history (today / last 7 days) right in the sidebar, with no separate process and no cross-origin calls.
A trigger button appears at the bottom of the left sidebar, above Settings — full-width with a label when the sidebar is expanded, a compact icon when collapsed. Click it to open the panel: live balance, today's input / output / cache tokens, and a 7-day trend computed from your own session logs.
api.deepseek.com/user/balance using the API key DSH already has (DEEPSEEK_API_KEY from your credentials), never stored in the browser.$DSH_HOME/sessions logs and aggregates input / output / cache tokens per day (today, totals, daily average, cache hit rate).sidebar.footer.action trigger and a shell.overlay modal; layout follows the DSH design tokens (light/dark themes included).| Requirement | Version |
|---|---|
| DeepSeek Harness | >= 0.1.0-rc.7 (verified on 0.1.5-rc.2) |
| Node.js | >= 20 |
| pnpm | >= 10 (for dsh plugin installation) |
| API key | an existing DEEPSEEK_API_KEY credential (the one DSH already uses) |
From anywhere, run:
dsh plugin --profile web add dsh-usage-plugin
That's it — the package declares a dsh.bundle patch, so dsh plugin automatically mounts it into the profile layer stack. Then:
dsh web (stop and start the process). The loader tree is composed once at boot, so a plugin installed into a running server is not picked up until then.Cmd/Ctrl+Shift+R).GET /api/dsh-usage/balance and GET /api/dsh-usage/stats?days=N sit behind the same process-token fence as the rest of DSH's /api (dsh >= 0.1.5), so a bare curl gets 401 unauthorized. Use the browser page, or pass the token that dsh web prints at startup:
# dsh web: http://127.0.0.1:3080/?token=XXXXXXXX
node scripts/verify-install.mjs 3080 XXXXXXXX
The verifier ships with the package, so an npm install can run it from ~/.dsh/profiles/web/node_modules/dsh-usage-plugin.
dsh plugin --profile web add github:lurejewel/dsh-usage-plugindsh plugin --profile web add https://github.com/lurejewel/dsh-usage-plugin/archive/refs/tags/v0.1.6.tar.gzdsh plugin forwards its arguments to pnpm through a shell without quoting them, so an unquoted path containing spaces is split into several package specs — D:\Software\DeepSeek Harness\dsh-usage-plugin becomes link:D:/Software/DeepSeek plus a phantom Harness\dsh-usage-plugin dependency, and the plugin is dropped from dsh.profile.bundles:dsh plugin --profile web add '"D:\Software\DeepSeek Harness\dsh-usage-plugin"'
scripts/install-local.ps1 does exactly this.
~/.dsh/profiles/web/cordis.patch.yml, then restart:- insert:
- id: dsh-usage-plugin
name: dsh-usage-plugin
dsh plugin --profile web remove dsh-usage-plugin
Then restart dsh web.
One package, two halves, mounted as a normal Cordis plugin:
lib/index.js Host half: same-origin HTTP routes on the DSH web server
lib/client.js Browser half: __ModuleLoader__ bundle served to the web GUI
lib/usage-history.js Session-log reader (zstd frame scan + per-step dedup + daily aggregation)
GET /api/dsh-usage/balance — live official balance (server-side call, key never leaves the server).GET /api/dsh-usage/stats?days=N&fresh=1 — balance + usage history; N defaults to 7, clamped to 1–90. fresh=1 bypasses the short result cache (the panel's refresh button sends it).Implementation notes worth knowing:
usage-service.js), never on the server's event loop. It reads and inflates every artifact — seconds of synchronous work on a large history — and running that inline stalls the entire harness web server. That is not merely slow: anything that health-checks the server over HTTP reads the stall as "server down". (dsh-remote's bridge watchdog probes http://127.0.0.1:3080/ with a 2s timeout and kills the tunnel when it fails, so an inline scan visibly kills and restarts the remote bridge on every panel click.) Results are cached for 15s and concurrent requests share one scan.session.<generation>.jsonl.zstd) are multiple concatenated zstd frames (one frame per persistence batch); the reader scans frame-by-frame instead of assuming a single frame. One damaged frame costs only its own rows.session.jsonl.zstd is the released v0 root, session.vN.jsonl.zstd a later one, and DSH always writes and reads the highest generation present in a session directory. The reader therefore selects the highest canonical generation per session (never the plain v0 file alone, and never more than one file per session): a lower generation stops at that session's migration point, while reading two of them would double-count.assistant/message and assistant/chunk events report the same usage numbers for the same (turn, step); the reader de-duplicates by (turn, step) so totals are not double-counted.credentials service — the same source the DeepSeek provider uses. Nothing is stored in the browser; all calls are same-origin./plugins/??… request rather than per-package URLs. Neither changes the browser half: it runs inside the authenticated page.api.deepseek.com; the API key never enters the browser.$DSH_HOME/sessions.npm test # unit tests (frame scan / generation pick / dedup /
# worker service / cache / non-blocking scan)
npm run test:client-boot # boot the real client bundle in Node against mock slots
npm run test:standalone # in-process E2E: real cordis + real API + real logs
# (needs a local DSH install with @deepseek-ai packages
# reachable at $DSH_HOME/profiles/node_modules, a
# DEEPSEEK_API_KEY credential, and session logs)
npm run verify -- 3080 <token> # post-restart check against a running dsh web
lib/ is the shipped artifact and doubles as readable source (plain ESM, documented).
scripts/install-local.ps1 — one-click local install as a link: to this repo (quotes the path for the dsh plugin shell hop).scripts/restart-web.ps1 — restart dsh web, read the tokenized URL from the server log, then run the verification.scripts/verify-install.mjs — post-restart check: token exchange, stats/balance routes, materialized client row, combined bundle contents. On dsh >= 0.1.5 it needs <port> <token>.The two .ps1 helpers live in the git checkout only; verify-install.mjs is published in the npm package as well.
peerDependencies now also declares the client-side packages this plugin consumes (dsh-client-ui-primitives, dsh-client-ui-slots, dsh-client-locale), so a future host version the plugin was not validated against is flagged at install time.IconDataOutline16 → IconDataOutlineRegular). createElement(undefined) throws while the slot renders, so the whole button vanished while the routes, the boot manifest and the served bundle all still looked healthy. Icons now resolve through a candidate list with a built-in fallback glyph, so the next rename degrades instead of blanking the entry.dsh.client.inject no longer names @deepseek-ai/dsh-client-runtime (which 0.1.7 does not ship) and now declares dsh-client-ui-sidebar, the owner of the slot this plugin renders into.test/client-boot.test.mjs now renders the components against the real primitives export list read from the installed host, instead of a hand-written mock. The mock listed the old icon name, which is exactly why the rename slipped through.dsh-remote's 2 s bridge watchdog declared the harness offline and restarted the remote tunnel on each click./api/dsh-usage/stats?fresh=1 bypasses the cache; the panel's refresh button sends it, while simply reopening the panel reuses a recent scan.session.vN.jsonl.zstd), not only the released v0 root. A migrated session's v0 file stops at its migration point, so today's usage read as 0 and history under-counted by roughly 11%.ctx.effect, so unload/reload removes them instead of leaving zombie handlers./api/dsh-usage/stats client request (the path had been over-renamed).