Skip to content

Releases: satan9394/dsh-usage-unified

v0.5.0 — today and 14-day windows, start-to-now spans, an honest custom range

Choose a tag to compare

@satan9394 satan9394 released this 19 Sep 08:28
  • Ranges: added Today (range=1d) and Last 14 days (range=14d) beside the existing
    7d/30d/all, so the window set is now 1/7/14/30/all.
  • Custom spans gained a second mode. Alongside start–end, a from with no to means
    start → now: the host resolves the upper bound against today, so the window keeps extending as
    the calendar moves instead of freezing on the day it was chosen. to without a from is
    rejected — an upper bound alone has no sane default — as is range=custom, which is a reporting
    id rather than a request value.
  • The panel no longer calls a hand-picked span "last 30 days". A bounded span reports
    range.id = "custom" instead of the placeholder it used to claim.
  • Today plots by hour. One day bucket says nothing a daily curve can show, so range=1d draws
    the host's 24 hourly buckets. Those carry totals only (TimeBucket is {tokens, messages}), so
    the hourly view deliberately offers the total series alone rather than drawing three empty lines.
  • The open panel refreshes itself every 60s; the host re-scans its index every 30s. Both the
    snapshot and the call table re-read through their existing code paths, so nothing new can drift.
  • Tests: 77, nine of them new (window bounds, 1d aggregation, the range/custom wire rules, hour
    ticks). The local smoke run now covers 1d, 14d and start-to-now end to end.

v0.4.2 — how to update, per install kind

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 17:58

Both READMEs gained an Updating subsection, because the question that actually comes up — "how do I move to a newer build?" — was never answered.

The runtime is loaded when the harness starts, so a new build needs a restart of the Web profile, and what "update" means depends on how the plugin was installed:

  • From a checkout or a link: — pull or edit the checkout, rebuild if the source changed, restart. A link: dependency is a directory junction, so there is no copy to refresh: the profile reads the checkout itself.
  • From GitHub or npm — re-run dsh plugin --profile web add <same target>. That install is pinned to a commit, so re-adding is what moves it.

Documentation and scripts/ changes need no restart at all. npm run check: 68 tests pass.

v0.4.1 — standard READMEs in both languages, plus a changelog

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 17:09

Documentation rewritten in both languages to a conventional shape, plus a changelog.

The READMEs had grown essay-first: the feature matrix preceded any statement of what the plugin is, there was no table of contents, no requirements, no configuration reference, and the facts a reader most needs — what the runtime reads and writes, what it never sends, what it cannot know — were split between an architecture section and a separate document. Both languages now follow the same outline: pitch, contents, features, requirements, install, usage, configuration table with every key and default, data and privacy, cost estimate, known limitations, architecture, development, compatibility, opt-in leaderboard, credits.

Two things are now stated plainly instead of left to be discovered:

  • DSH STORE status. The marketplace marks this entry blocked: its automatic admission policy refuses any plugin whose runtime touches the filesystem or the network, and this one must — the host half reads session logs from disk, the browser half fetches its own loopback route. The manual install path is kept, and docs/SECURITY.md states exactly what those capabilities are used for.
  • The limitations that make the numbers what they are. Retried steps keep only the final usage (both attempts were billed); a bounded range can total less than all-time because clock-skewed tokens belong in a total but not on a calendar; days are keyed in the host process's timezone; a cold build takes minutes on a large history — ~1,380 log files / ~790 MB compressed here, measured.

CHANGELOG.md collects all nine releases in order and ships with the package. npm run check: 68 tests pass.

v0.4.0 — verified DSH compatibility declaration; the year range retires quietly

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 15:37

Two things in this release.

dsh.compatibility now exists. DSH STORE will not evaluate a listing without an explicit declaration, so package.json carries:

"compatibility": {
  "dsh": ">=0.1.5-rc.2 <0.1.6",
  "dshReleases": { "0.1.5-rc.2": "compatible" }
}

A caret range is deliberately avoided — ^0.1.5-rc.2 would also claim rc.3+, and only rc.2 has been exercised on this machine. Later stable rc releases get their own precise entry once verified.

The retired year range is gone. It existed for the activity heatmap, which both halves dropped; no test mentioned it and the static report no longer asked for it. range=year now degrades to all-time instead of failing, because a browser holding a cached client bundle would otherwise receive a 400 that breaks the whole panel. Other unknown values still return 400, and a test pins both behaviours.

npm run check: 68 tests pass. smoke:local: 9/9.

v0.3.3 — vitest 5, and a store-watch that keeps its polls

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 14:56

Two follow-ups found while checking the GitHub side.

Dependabot #2 could not be merged as-is. It branched from before the 0.3.x work, so its package.json still carried 0.2.0, the old description and the already-deleted tokscale:export script — merging it would have reverted all of that. The upgrade is redone on current main instead, plus the explicit vite devDependency vitest 5 now needs (without it, every run dies with ERR_MODULE_NOT_FOUND before collecting a single test). #2 is closed with that explanation.

scripts/store-watch.ps1 no longer loses a poll. It folded gh's stderr into the JSON stream and then failed with an opaque parse error, which is what left the 2026-09-17 17:13 gap in logs/dsh-store-806.log. It now checks the exit code, parses from the first brace to the last, and records the raw output when it cannot parse.

npm run check: 67 tests pass on vitest 5.0.1; smoke:local 9/9 against real data.

v0.3.2 — retire the DSH exporter (tokscale reads versioned logs itself)

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 09:30

Removes the DSH supplement that tokscale no longer needs.

tokscale 4.17.0 ships junhoyeo/tokscale#1328, so its own dsh scanner reads session.v<N>.jsonl.zstd. Measured on this machine, keeping the converted copies and their scanner.extraScanPaths.dsh entry reports DSH at 24.80B against the 15.38B tokscale reads by itself — the extra root is not deduped against the default scan.

  • Deleted scripts/tokscale-export.mjs and its .tokscale-home/ output (both to the recycle bin), the tokscale:export npm script, the daily refresh step, and the dsh extra-scan-path entry.
  • setup-leaderboard.ps1 now warns when the installed tokscale is older than 4.17.0, since below that DSH is under-counted.
  • The CC Switch supplement stays: tokscale still cannot see proxy-side Claude usage by itself.

Why the leaderboard number moved. Four states, same files: 4.16.0 legacy-only 9.30B; the old 4.16.0 + exporter 18.73B (+7.1% over truth); 4.17.0 native 15.38B (−12.1%); 4.17.0 + exporter 24.80B. Truth is 17.49B, corroborated by DSH's own projection cache (17.56B, −0.42%). So the previous submission was inflated and the new one is low; the user chose the honest figure, resubmitted after delete-submitted-data (server 366.82亿 → 334.27亿 / $4,212.39, rank #425 → #464), and the remaining 12% gap is filed upstream as junhoyeo/tokscale#1348.

v0.3.1 — the static report now reads the same as the panel

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 08:46

The static report had the same defect the panel just fixed: its headline cards read snapshot.allTime.totals, so --range 7d still showed the all-time total and tucked the real window into a second card row underneath.

  • The cards now read the selected window; the two streaks stay all-time and say so; the reconciliation line is labelled all-time.
  • Mirrored from the panel: one merged model panel (share + four-bucket stack + calls + cost, tail behind a toggle), a session ranking, a cost card with the peak-blend disclosure, and pricingPath wired up so the numbers match the plugin exactly.
  • The activity heatmap is gone for the same reason the panel dropped it — it duplicated the per-day bars. Sections collapse with native <details>.

Verified on this machine: report-all hero 174.91亿 / cost $154.55; report-30d 160.89亿; report-7d 40.33亿 / cost $34.95 — identical to the panel's routes, with per-model cost ($33.69 for deepseek-v4.1-flash) and the all-time reconciliation unchanged across all three.

v0.3.0 — the range governs every card; DeepSeek off-peak/peak pricing

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 08:29

Two reported problems.

1. The time-range control looked inert. The host was right — range=7d returns 7 days and range=all returns every day — but eight of the eleven headline cards read snapshot.allTime.*, which is deliberately range-independent, so the numbers never moved. They now read snapshot.totals.* and snapshot.mostUsedModel; the two streak cards stay all-time (the host always computes them all-time) and are tagged all-time instead of silently ignoring the filter. A caption under the range control and in the trend panel's header spells out the resolved window, and smoke:local now asserts range isolation so this cannot regress.

2. Some models had no price. Cross-checking DeepSeek's pricing page against OpenCode Go's and Command Code's per-model tables showed only deepseek-v4.1-flash (the busiest model here, 21.7% of all tokens) and a zero-usage muse-spark-1.2-contributor were missing — and that DeepSeek's whole Flash/Pro line is priced by time of day.

  • A price entry may now carry a peak tier, folded into the effective rate by peakShare (default 0.2083 = the documented 35/168h peak window) and disclosed on the cost card. Neither the optimistic off-peak bound nor the peak one.
  • scripts/pricing.override.json (in git) holds the hand-checked entries and is merged over CC Switch's table on every pricing:setup, so a models.dev re-sync cannot drop them.
  • CC Switch's own table was updated too, so tokscale's custom pricing agrees (backed up first).

Also: zero-token model rows are filtered out of the panel — they leaked display names and failed requests into the unpriced list — and the pricing module is re-exported from the host entry.

Measured on this machine: cost .61 -> .55, unpriced models 3 -> 0, headlines 7d/30d/all = 40.3亿 / 160.9亿 / 174.9亿, tests 62 -> 67, smoke assertions 8 -> 9.

v0.2.1 — cost estimate correctness (no double count, nested model ids)

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 07:53

Patch on top of v0.2.0, found by verifying the live plugin against this machine's real data.

Cost estimate was roughly 2x too high. The summary summed the per-model rows and the session rows, which describe the same tokens. Only the per-model rows partition a window's tokens, so they now alone feed the total; session rows and the most-used-model card are decorated with a per-row cost for display only. The smoke test asserts priced + unpriced == window tokens so this cannot regress silently.

Nested model ids never matched the price table. Ids whose own name contains a slash (command/deepseek/deepseek-v4.1-flash — the bulk of this machine's usage) were looked up with only the first segment treated as the provider. lookupPrice now also tries the segment after the last slash.

Measured on the author's machine: cost .79 -> .61, and priced model rows went from effectively 0 to 27 of 30.

npm run check: 62 tests pass. npm run smoke:local: 8/8.

v0.2.0 — merged model panel, session ranking, optional cost, faster index

Choose a tag to compare

@satan9394 satan9394 released this 18 Sep 07:45

Dashboard

  • One model panel replaces the two duplicate ones: each row carries the share, the stacked input/cache-read/cache-write/output bar, the call count and (when priced) the cost. Only the first six models show until you ask for the rest.
  • Every large panel is now collapsible (trend, models, breakdown, session ranking, call details); the flag survives the overlay closing.
  • New session ranking — sessions ranked by tokens for the selected window, with one click drilling through to that session's calls.
  • New cost estimate card, shown only when a local pricing table exists; models missing a price are disclosed as unpriced, never counted as free.

Cost table (opt-in)

  • npm run pricing:setup writes \/usage-unified/pricing.json from CC Switch's ~/.cc-switch/model-pricing.json (or any file in this project's own shape). Edit prices without reinstalling: the table is re-read on each index scan.

Performance (measured, not guessed)

  • Bounded-concurrency decoding (indexConcurrency, default 4): cold-cache 250 files went 37.5s -> 23.1s; 2-4 lanes is the sweet spot, 8 regresses.
  • A resume read now seeks to the appended tail instead of re-reading the whole log.
  • The index persists only when it changed — it used to rewrite a ~20 MB cache every refresh interval.
  • Async zlib.zstdDecompress was benchmarked and rejected (per-frame await made it ~2x slower than the sync path).

Cleanup

  • Removed the dead heatmap/bar-chart CSS (-61 lines) and 7 unused dictionary keys.
  • scripts/tokscale-export.mjs is deprecated: junhoyeo/tokscale#1328 shipped in tokscale v4.17.0, which reads versioned DSH logs natively. Migration steps are in the script header; keeping it past the upgrade would double-count.

Verification

  • npm run check: typecheck + 62 tests + build.
  • npm run verify:realdata: 1,369 sessions, both log formats, folded totals match an independent reconstruction with a zero-token delta.
  • npm run smoke:local: 8/8 HTTP assertions, including session ranking + drill-down.