dsh-all-usage 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to `dsh-all-usage` are documented here.
4
4
 
5
+ ## [1.1.18] - 2026-10-07
6
+
7
+ ### Added
8
+
9
+ - **A seven-day range.** The time-range presets gain 近 7 天 / Last 7 Days next to today, 30 days, 90 days and all time; it scopes the summary, trend, model tables, audit log, filters and CSV export like every other preset (seven calendar days, today included), and the choice persists in the browser like the others.
10
+ - **A switchable heatmap span.** The activity heatmap no longer forces 53 columns: 30 天 / 90 天 / 12 个月 buttons redraw it with 5, 13 or 53 week columns. The grid follows the span (each column is a week and every cell keeps a 1:1 aspect ratio), so a shorter span also gets taller, readable cells instead of 10px squares. The span is stored with the other UI preferences and is independent of the time range above: changing the range never resizes the heatmap and vice versa.
11
+ - **A version indicator in the header.** The dashboard title now carries the running plugin version: “已是最新 / Up to date” in small text when it matches the newest release on npm, “最新 vX.Y.Z / Latest vX.Y.Z” highlighted when it is behind, and clicking it opens the GitHub repository. The host performs the check (`GET /api/all-usage/version`, one read-only request to `registry.npmjs.org`, cached for 6 hours, never initiated by the page itself), so a desktop shell that cannot reach the network still shows its running version, and the refresh button forces a fresh check. An unreachable registry or an unparseable tag degrades to “unknown” (the chip shows the version alone) rather than claiming either verdict.
12
+
13
+ ## [1.1.17] - 2026-10-01
14
+
15
+ ### Fixed
16
+
17
+ - **Writes work in the DSH desktop client again.** The write routes (price save, immediate sync, holiday fetch, alias) required an `Origin` header equal to the loopback host. The desktop shell serves the UI on its own scheme and forwards page requests through Electron, which strips `Origin` along with `Host`, `Cookie` and `Sec-Fetch-*`, so every desktop write was rejected with 403: saving prices reported "no permission", the models.dev catalog could never be synced, and the auto-sync checkbox rolled back. Writes are now authorized by the process capability alone — an unguessable secret that a cross-origin page can neither read (the read routes send no CORS headers) nor attach to a form POST — while a present `Origin` must still be loopback or the shell's own scheme, and `Host` must still be loopback so a rebound DNS name cannot reach the API.
18
+ - **A missing `usageBacked` no longer reads as a verdict.** A host that predates the merged price table sends no `usageBacked`, and the panel rendered that as "no ledger usage" even for rows with recorded calls; it now degrades to "usage unknown" (distinct flag, row style and footer count), and says so when the payload carries no model list at all.
19
+ - **A rotated write capability heals itself.** The status poll now carries a derived `capabilityId` (never the capability itself), the page treats a rotation as a full refresh, and a write rejected with 403 re-reads the capability from the full snapshot and retries once before reporting anything; the message now names a stale capability instead of claiming a permission problem.
20
+
21
+ ### Added
22
+
23
+ - `GET /api/all-usage/status` reports the running plugin version and the derived capability id, so a page served by a newer package than the running host can say "host plugin vX / this page vY differ — restart DSH" instead of leaving that mismatch invisible.
24
+
5
25
  ## [1.1.16] - 2026-09-29
6
26
 
7
27
  ### Added
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # dsh-all-usage
2
2
 
3
+ [![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
3
4
  [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
4
5
  [![npm](https://img.shields.io/npm/v/dsh-all-usage?label=npm&color=4D6BFE)](https://www.npmjs.com/package/dsh-all-usage)
5
6
  [![downloads](https://img.shields.io/npm/dt/dsh-all-usage?label=downloads&color=4D6BFE)](https://www.npmjs.com/package/dsh-all-usage)
@@ -17,14 +18,15 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
17
18
 
18
19
  ### 功能
19
20
 
20
- - **热力图**:53 周使用热力图;按工作区筛选并查看每日回合与 Token 明细
21
+ - **热力图**:使用热力图可选**最近 30 天 / 90 天 / 12 个月**三种跨度(格子随跨度自适应,跨度越短格子越大);按工作区筛选并查看每日回合与 Token 明细
21
22
  - **模型统计**:支持混合查看、按模型合并、按供应商汇总三种维度,展示调用次数、各类 Token 与缓存命中率;模型行与筛选下拉显示真实厂商品牌 SVG 图标(未知/混牌保持中性)
22
23
  - **摘要与工作区**:Token 用量、缓存命中、估算成本、账户余额、连续使用、工作区 Token 分布和明细
23
24
  - **成本统计 / 价格表**:从 models.dev 同步模型价格;按输入、输出、缓存读取和缓存写入四个桶计算,保存价格快照,明确区分已计价、免费模型和未计价调用。成本设置是一张可编辑的价格表:每行一个账本模型,价格框直接编辑即写入该模型的手工价,「官方模型」列选定映射后该行自动改用官方目录价,并可为每行配置**上下文费率档位**与**分时段(UTC 峰谷)计费**;配置里存在但账本暂无用量的行同样可见、可改、可删。峰谷规则可额外开启「中国法定节假日全天按谷时计价」,节假日日期以显式列表保存在该模型的峰谷计划里(按北京时间 UTC+8 日历日判定,支持 2026-10-01..2026-10-07 区间写法,也可一键「从公开日历载入」当年放假安排后冻结进策略)。内置 DeepSeek 峰谷表已自带 2026 年官方放假安排(随插件版本更新),官方直连或已映射到官方条目的行无需任何配置即按节假日谷价计价;非官方直连的中转行仍按静态价,映射后生效
24
25
  - **导出**:按当前时间范围和模型聚合方式导出 CSV
25
- - **时间范围**:今日、近 30 天、近 90 天、全部,或在全部可扫描历史日数据中自定义起止日期;热力图始终展示最近 53 周
26
+ - **时间范围**:今日、**近 7 天**、近 30 天、近 90 天、全部,或在全部可扫描历史日数据中自定义起止日期;热力图跨度独立切换(30 天 / 90 天 / 12 个月),与时间范围互不影响
26
27
  - **工作区别名**:在侧栏入口打开看板后管理,持久化保存到 $DSH_HOME/storages 的 KV 单元 `all_usage_aliases`
27
28
  - **界面语言**:在看板顶部切换中文与 English;选择会保存到浏览器本地
29
+ - **版本指示**:标题旁显示当前运行版本号——与 npm 上最新发布版一致时小字标注「已是最新」,落后时标出「最新 vX.Y.Z」并高亮,点击任意状态都打开 GitHub 仓库;检查由 Host 缓存(默认 6 小时),只有点「刷新统计数据」才会强制重新检查
28
30
  - **完整历史与增量重建**:基线扫描全部可读历史会话;独立用量账本同时作为每会话游标——未变化的会话直接复用账本,新增事件只增量回填,长历史重启不再全量重建
29
31
  - **重启免读**:用持久化日志的 revision 作为每会话的变更信号(只读头部行 + stat,不读全量)——日志未变的会话重启时连事件都不读,直接从账本复用;仅日志变化(新增/修改)的会话才做增量读取
30
32
  - **数据健康与按需刷新**:扫描完成后浏览器只检查轻量状态版本,只有用量、别名或同步状态变化时才拉完整历史;显示本次数据更新时间、历史扫描健康、revision 免读、实际读取、账本恢复和失败,网络异常保留上次成功数据并可重试
@@ -40,7 +42,7 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
40
42
  - **运行环境**:需要 Node.js `>=22 <25`;CI 会在 Node 22 和 Node 24 上运行测试、语法检查和 npm 包内容检查。
41
43
  - **DSH 兼容**:`package.json` 声明 DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.2 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0`,已使用 `0.2.0-rc.2`、`0.1.7-rc.2`、`0.1.5-rc.2`、`0.1.5-rc.1`、`0.1.1-rc.2` 和 `0.1.1-rc.1` 的真实 Cordis 服务链验证,且全部纳入 CI smoke 矩阵(Node 22/24 双档);`0.1.2-rc.1` 已通过实际使用验证兼容,但未纳入 CI smoke 矩阵。声明按元组拆成四段而非写成单一区间,是因为 node-semver 只有在范围里存在与目标版本同 `major.minor.patch` 且自身带预发布标签的比较符时,才会放行该预发布版本 —— 例如必须写成 `>=0.2.0-rc.2` 才能让 `0.2.0-rc.2` 进入范围。
42
44
  - **桌面客户端(Electron,Windows)**:本插件在 DSH 桌面客户端上实测运行正常(实测 `@deepseek-ai/dsh-desktop` `0.1.7-rc.2`)。桌面客户端会在窗口顶部为自身的最小化 / 最大化 / 关闭按钮保留一条 40px 顶栏,并在文档上标记 `data-windows-titlebar` 与 `--dsh-windows-titlebar-height`;面板据此从该条**下方**开始绘制,所以桌面端自己的关闭按钮永远不会被面板盖住。浏览器端没有该标记,不留白。顶栏高度优先取宿主声明,其次取 Window Controls Overlay 矩形,最后退回 UA;需要微调时可在 DevTools 执行 `localStorage.setItem('dsh-all-usage:topInset', '48')` 后刷新(`0` 表示不留白),无需重新构建。桌面客户端自带运行时,其运行时 `0.1.7-rc.2` 已纳入声明区间与 CI smoke 矩阵(见下表)。
43
- - **Web 服务依赖**:Host 将 `webServer` 声明为必需依赖,确保服务晚挂载时由 DSH 等待后再执行插件;该包面向 DSH Web profile,不提供无 WebServer 的 headless 路由。HTTP 守卫还会检查真实 socket peer,反向代理只有在连接本身来自 loopback 时才会被接受。
45
+ - **Web 服务依赖**:Host 将 `webServer` 声明为必需依赖,确保服务晚挂载时由 DSH 等待后再执行插件;该包面向 DSH Web profile,不提供无 WebServer 的 headless 路由。HTTP 守卫还会检查真实 socket peer,反向代理只有在连接本身来自 loopback 时才会被接受。写接口(价格保存、立即同步、节假日抓取、别名)以**进程令牌**为授权凭据,不强制要求 `Origin`:DSH 桌面端把 UI 跑在自定义协议上、请求由 Electron 主进程转发,转发时会删掉 `Origin`(连同 `Host`/`Cookie`/`Sec-Fetch-*`),强制 Origin 会让桌面端所有写入 403;而 `Origin` 若存在仍必须是 loopback 或桌面端自身 scheme,`Host` 仍必须是 loopback(防 DNS rebinding)。
44
46
 
45
47
  | DSH runtime | Node.js 支持 | 真实 Cordis smoke | 结论 |
46
48
  | --- | --- | --- | --- |
@@ -116,10 +118,11 @@ node scripts/replay-fixture.mjs fixtures/usage-events.json
116
118
 
117
119
  ### 最近更新
118
120
 
119
- **v1.1.16**
121
+ **v1.1.18**
120
122
 
121
- - **正式支持 DSH 0.2.0-rc.2**:真实 Cordis 服务链 smoke 在该运行时上全部通过(全部路由、session 钩子、账本 flush、revision 快路径、dispose),声明区间新增 `>=0.2.0-rc.2 <0.2.1-0`,已验证列表增至 6 个运行时,CI smoke 矩阵扩到 **6 个版本 × Node 22/24 = 12 个作业**;该运行时线沿用 0.1.7 的 Cordis 组合(cordis 4.0.4 / loader 1.0.5 / timer 1.1.6)与抽象 settings seam,插件无需代码改动。
122
- - 本版只改**兼容性声明、CI 矩阵与文档**(README 中英同步:声明引用、验证表、徽章)。上一个版本的功能(1.1.15:可编辑价格表、中国法定节假日全天谷价、面板性能修复)见 CHANGELOG。
123
+ - **新增「近 7 天」时间范围**:预设行变为 今日 / **近 7 天** / 近 30 天 / 近 90 天 / 全部 / 自定义;口径是含今天的 7 个日历日,摘要、趋势、模型表、请求日志、筛选与 CSV 导出同步生效,选择会记在浏览器里。
124
+ - **热力图跨度可切换(30 天 / 90 天 / 12 个月)**:列数随跨度变化(5 / 13 / 53 列),每列一周、格子保持 1:1,所以跨度越短格子越大(实测 12 个月 18×18px、30/90 天 34×34px,并做了上限避免少数几列被拉成巨块);默认仍是 12 个月,与上方时间范围互不影响。
125
+ - **标题旁新增版本指示**:显示当前运行版本,与 npm 最新版一致时小字「已是最新」,落后时高亮「最新 vX.Y.Z」,点击打开 GitHub 仓库;检查由宿主发起(只读一次 npm registry,缓存 6 小时,同一进程 30 秒内不重复请求),拿不到结论时降级为「未知」而不是猜。
123
126
 
124
127
  完整版本记录见 [CHANGELOG.md](CHANGELOG.md)。
125
128
 
@@ -165,9 +168,10 @@ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
165
168
 
166
169
  - **Host 端**(入口 `lib/index.js`,组装 `lib/plugin.js`):按职责拆分为 `aggregation.js`(聚合与查询)、`ledger.js`(持久账本)、`session-sync.js`(历史/实时同步)、`pricing-runtime.js`(运行时定价)、`balance.js`(余额)、`http.js`(安全路由);扫描 `turn/end`、`assistant/chunk` usage 和最终 `assistant/message.usage`,监听 `session/event` 实时折叠,并通过 `webServer` 服务注册数据路由:
167
170
  - `GET /api/all-usage` — 兼容统计快照
168
- - `GET /api/all-usage/status` — 轻量 revision 与同步健康状态,并附带最近一次客户端环境报告(`clientEnv`)
171
+ - `GET /api/all-usage/status` — 轻量 revision 与同步健康状态,并附带最近一次客户端环境报告(`clientEnv`)、运行中插件的版本号(`pluginVersion`)与写入令牌的派生 ID(`capabilityId`,令牌轮换时变化,用于让页面察觉宿主被重新加载;令牌本身只在完整快照里下发)
169
172
  - `GET /api/all-usage/query` — 按 scope 返回聚合、daily/hourly 趋势和 heatmap 数据;单日 scope 填充 `hourly`,跨日 scope 的 `hourly` 为空
170
173
  - `GET /api/all-usage/records` — 按 scope 分页返回脱敏 canonical usage rows
174
+ - `GET /api/all-usage/version?force=1` — 版本检查:返回运行中版本与 npm 上最新版本、比较结论(`latest` / `outdated` / `unknown`)、检查时间与来源;Host 侧缓存 6 小时,同一进程内对上游的请求最多每 30 秒一次(`force=1` 也受此下限约束),`force=1` 来自刷新按钮
171
175
  - `GET /api/all-usage/balance?force=1` — 账户余额(复用 `llm-deepseek` 的 API Key 配置)
172
176
  - `POST /api/all-usage/alias` — 设置工作区别名
173
177
  - `GET /api/all-usage/pricing` — 读取可编辑价格表所需的完整配置:逐模型的生效费率与状态、映射、手工价、峰谷计划(含内置表来源与完整规则)以及仅存在于配置中的行
@@ -181,10 +185,11 @@ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
181
185
  ### 数据说明
182
186
 
183
187
  - 使用次数与 Token 来自 DSH 会话日志;`session/flush` 只在存在新的相关事件时重建并将派生账本写入异步队列,同一 session 的 pending record 会合并,插件退出时 drain;插件激活时会回填日志与账本历史,插件卸载/重启后已成功持久化的数据不丢
184
- - 按日范围统计会保留全部可读取历史会话的有使用记录日期;热力图仅作为最近 53 周的固定视图窗口
188
+ - 按日范围统计会保留全部可读取历史会话的有使用记录日期;热力图是可切换跨度的视图窗口(最近 30 天 / 90 天 / 12 个月,默认 12 个月)
185
189
  - 会话删除后,已成功 flush 的用量仍从独立账本恢复;工作区删除同样不会丢数据——其历史用量汇总为一行「已删除」(含未落账的实时用量)。会话销毁提示和周期对账只负责触发重建,不会删除账本记录
186
190
  - 同一会话的同一 `turn / step` 只保留一份最终 usage;重试或替换消息会替换旧贡献,不重复累计
187
191
  - 输入 Token 按「未含缓存命中」计(缓存命中 / 写入独立成桶);全 0 用量的重放事件不会覆盖已记录的真实用量,纯缓存命中的请求仍会计入
192
+ - 版本检查会向 `registry.npmjs.org` 发一次只读 GET(仅读取 `dsh-all-usage` 的版本号,无请求体、无 Cookie;由 Host 发起并缓存 6 小时,页面自身不联网),失败时降级为「未知」而非猜测
188
193
  - 轻量状态接口只公开 Host 实例、统计 revision、扫描进度、同步计数与最近一次客户端环境报告(UA、视口、预留顶栏值及其来源、面板矩形——由本机客户端上报且只存内存),不公开会话 ID、工作区路径、提示词或回复正文;完整快照仅在状态变化或手动刷新时获取
189
194
  - scope query 将回合(turns)、模型调用(calls)和去重会话(sessions)分开统计;Provider/模型筛选缺少路由信息时明确归为 Unknown,不从展示字符串猜测
190
195
  - records 接口只返回短 hash、时间、工作区 ID、结构化模型身份、turn/step、Token buckets 和当前物化来源,不返回原始 session ID、路径、提示词、回复或凭据
@@ -211,12 +216,12 @@ A full usage dashboard for DeepSeek Harness. Analyze tokens, cache behavior, est
211
216
 
212
217
  ### Features
213
218
 
214
- - **Heatmap**: a 53-week activity heatmap with workspace filters and daily turn/token details
219
+ - **Heatmap**: an activity heatmap with a switchable span (**last 30 days / 90 days / 12 months**; shorter spans get larger cells), workspace filters and daily turn/token details
215
220
  - **Model analytics**: mixed view, model-merged view, and provider summary with calls, token categories, and cache hit rate; model rows and the model filter dropdown render vendor brand SVG icons (neutral for unknown or mixed brands)
216
221
  - **Summary and workspaces**: processed tokens, cache hits, estimated cost, account balance, usage streaks, workspace distribution, and details
217
222
  - **Cost statistics / price table**: sync model prices from models.dev, calculate four cost buckets, persist price snapshots, and distinguish priced, free, ambiguous, and unpriced calls. Cost Settings is one editable price table: each row is a ledger model, its price boxes write that model's manual price, the official-model column switches the row to the catalog price, and every row can carry context rate bands and time-of-day (UTC peak/off-peak) rules, including an optional "Chinese statutory holidays price as off-peak all day" switch whose explicit date list lives in that model's peak plan (China Standard Time calendar days, 2026-10-01..2026-10-07 ranges supported, and one click loads the year's arrangement from a public calendar before it is frozen into the policy); rows that exist only in the saved configuration stay visible, editable, and removable
218
223
  - **CSV export**: export data using the selected time range and aggregation mode
219
- - **Time ranges**: today, last 30 days, last 90 days, all time, or a custom start/end date across all available historical daily data; the heatmap always shows the latest 53 weeks
224
+ - **Time ranges**: today, **last 7 days**, last 30 days, last 90 days, all time, or a custom start/end date across all available historical daily data; the heatmap span switches independently (30 days / 90 days / 12 months)
220
225
  - **Workspace aliases**: manage aliases from the sidebar dashboard; values persist in the $DSH_HOME/storages KV cell `all_usage_aliases`
221
226
  - **Interface language**: switch between Chinese and English from the dashboard header; your choice persists locally in the browser
222
227
  - **Full history & incremental rebuild**: the baseline scans every readable historical session; the durable usage ledger doubles as a per-session cursor, so unchanged sessions are reused straight from the ledger and only newly appended events are folded — long histories restart without a full rebuild
@@ -225,6 +230,7 @@ A full usage dashboard for DeepSeek Harness. Analyze tokens, cache behavior, est
225
230
  - **Data health and on-demand refresh**: after a scan completes, the browser polls only a lightweight status revision and fetches full history only after usage, alias, or sync state changes; it shows the latest full-data update, historical scan health, revision skips, rereads, ledger recovery, and failures while preserving last-good data on network errors
226
231
  - **Performance**: Host maintains ingest-time local/UTC day, workspace, model-identity cubes and single-day hour buckets; scope queries merge buckets, exact costs use BigInt decimal accumulators, and the 53-week heatmap emits only the fields it consumes, while revision-scoped snapshot/records caches and recyclable live-event queues remain in place. Client isolates the heatmap, tooltip, trend, donuts, request records, and pricing dialog behind memoized boundaries; pointer coordinates update through refs plus requestAnimationFrame instead of rerendering the page, and the browser entry is deterministically minified before packing
227
232
  - **Trend line chart**: show input, cache read/write, output, reasoning, and total processed tokens for the active range, timezone, workspace, provider, and model scope; use hourly buckets for a single-day scope and daily buckets for cross-day scopes, with smooth monotone curves, staged entrance animation, hover for exact values, and click a point to inspect that day
233
+ - **Version indicator**: the header shows the running version next to the title — “Up to date” in small text when it matches the newest release on npm, “Latest vX.Y.Z” highlighted when it is behind, and a click opens the GitHub repository; the host caches the check (6 hours by default) and only the refresh button forces a new one
228
234
  - **Unified filters and audit**: workspace, provider, model, and date filters apply to the summary, heatmap, trend, tables, and CSV; workspace, provider, and model filters remain independent and can be combined freely, while workspace, provider, and model options are limited to values used in the selected date range and stale selections clear automatically; request logs stay visible as a compact paginated table with grouped Token details for the selected row
229
235
  - **Token accounting semantics**: input tokens are fresh (exclude cache hits/writes, which sit in separate buckets along with reasoning); all-zero usage replays never overwrite recorded usage, while cache-only requests still count
230
236
  - **Cost semantics**: prices come from the models.dev USD per 1M token catalog; DSH-normalized fresh input and the four cost buckets are snapshotted at calculation time, the multiplier applies only to final total, and existing positive historical costs are not recalculated; matching uses the model's official vendor entry and ignores the DSH provider, while missing official prices stay unpriced
@@ -310,10 +316,11 @@ The command loads the real plugin Host, calls its compatible APIs, checks the do
310
316
 
311
317
  ### Latest Update
312
318
 
313
- **v1.1.16**
319
+ **v1.1.18**
314
320
 
315
- - **DSH 0.2.0-rc.2 is a supported runtime**: the real Cordis service chain smoke passes on it (every route, the session hooks, the ledger flush, the revision fast path and disposal), so the declaration gains `>=0.2.0-rc.2 <0.2.1-0`, the verified list grows to six runtimes and the CI smoke matrix to **six versions × Node 22/24 (twelve jobs)**. The line keeps the 0.1.7 Cordis pairing (cordis 4.0.4 / loader 1.0.5 / timer 1.1.6) and the abstract settings seam, so no plugin code change was needed.
316
- - This release only touches the **compatibility declaration, the CI matrix and the documentation** (README in both languages: quoted range, verified table, badge). The feature work of v1.1.15 (the editable price table, Chinese statutory holiday pricing, panel performance fixes) is recorded in the changelog.
321
+ - **A new “Last 7 Days” range**: the presets are now Today / **Last 7 Days** / Last 30 Days / Last 90 Days / All Time / Custom; it covers seven calendar days including today and applies to the summary, trend, model tables, request log, filters and CSV export, with the choice remembered in the browser.
322
+ - **A switchable heatmap span (30 days / 90 days / 12 months)**: the column count follows the span (5 / 13 / 53 columns); each column is a week and the cells keep a 1:1 aspect ratio, so a shorter span also gets larger cells (18×18px at twelve months, 34×34px at 30/90 days, capped so a few columns cannot stretch into huge tiles). Twelve months stays the default and the span is independent of the time range above.
323
+ - **A version indicator next to the title**: it shows the running version, “Up to date” in small text when it matches the newest release on npm, a highlighted “Latest vX.Y.Z” when it is behind, and a click opens the GitHub repository. The host performs the check (one read-only request to the npm registry, cached for six hours, at most one every 30 seconds per process) and degrades to “unknown” rather than guessing.
317
324
 
318
325
  See [CHANGELOG.md](CHANGELOG.md) for the complete version history.
319
326
 
@@ -351,9 +358,10 @@ The profile patch layer hot-reloads; save the file and refresh the page.
351
358
 
352
359
  - **Host** (entry `lib/index.js`, assembled by `lib/plugin.js`): split by responsibility across `aggregation.js` (aggregation/query), `ledger.js` (durable ledger), `session-sync.js` (history/live sync), `pricing-runtime.js` (runtime pricing), `balance.js` (balance), and `http.js` (protected routes); aggregates `turn/end`, `assistant/chunk` usage, and final `assistant/message.usage`, folds live `session/event` updates, and exposes data routes through `webServer`:
353
360
  - `GET /api/all-usage` — compatible usage snapshot
354
- - `GET /api/all-usage/status` — lightweight revision and sync health, plus the last client environment report (`clientEnv`)
361
+ - `GET /api/all-usage/status` — lightweight revision and sync health, plus the last client environment report (`clientEnv`), the running plugin version (`pluginVersion`) and a derived id of the write capability (`capabilityId`, changes when the capability rotates so the page can notice a plugin reload; the capability itself is only ever sent in the full snapshot)
355
362
  - `GET /api/all-usage/query` — scoped aggregate, daily/hourly trend, and heatmap data; single-day scopes populate `hourly`, while cross-day scopes return an empty `hourly` array
356
363
  - `GET /api/all-usage/records` — paginated privacy-safe canonical usage rows
364
+ - `GET /api/all-usage/version?force=1` — version check: the running version, the newest published version, the verdict (`latest` / `outdated` / `unknown`), when it was checked and where it came from; cached on the host for 6 hours with at most one upstream request every 30 seconds per process (the floor applies to `force=1` too), `force=1` comes from the refresh button
357
365
  - `GET /api/all-usage/balance?force=1` — account balance using the configured `llm-deepseek` API key
358
366
  - `POST /api/all-usage/alias` — update workspace aliases
359
367
  - `GET /api/all-usage/pricing` — read the full editable price table: per-row effective rates and status, mappings, manual prices, peak plans (with their built-in/explicit origin and full rules), and rows that exist only in the configuration
@@ -362,15 +370,16 @@ The profile patch layer hot-reloads; save the file and refresh the page.
362
370
  - `POST /api/all-usage/pricing/sync` — sync models.dev and backfill unpriced calls
363
371
  - `POST /api/all-usage/pricing/holidays` — fetch one year of the Chinese holiday arrangement (the holiday-cn dataset, jsDelivr first with a GitHub raw fallback, cached host-side for 24 hours) and return only the off-day list plus its source; **nothing is written to the configuration** — the client freezes the dates into a peak plan
364
372
  - `GET /api/all-usage/client-env` — client environment report (user agent, window-controls overlay height, the reserved strip **and its source**, viewport, panel rectangles); the client reports it at load and whenever the sheet opens, kept in memory only
365
- - **Client**: readable source lives in `src/client.js`; `npm run build:client` uses the pinned Terser version to generate the `window.__ModuleLoader__` bundle at `lib/client.js`, which registers the “Usage statistics” sidebar entry through the `sidebar.footer.action` slot. All API routes accept loopback requests and reject an explicit cross-origin Origin; balance reads and alias writes also require a process-scoped token generated when the plugin starts (the balance GET tolerates browsers omitting Origin).
373
+ - **Client**: readable source lives in `src/client.js`; `npm run build:client` uses the pinned Terser version to generate the `window.__ModuleLoader__` bundle at `lib/client.js`, which registers the “Usage statistics” sidebar entry through the `sidebar.footer.action` slot. All API routes accept loopback requests and reject an explicit cross-origin Origin; balance reads and alias writes also require a process-scoped token generated when the plugin starts (the balance GET tolerates browsers omitting Origin). **Writes are authorized by that token, not by an Origin**: the DSH desktop shell runs the UI on its own scheme and forwards requests through Electron, which strips `Origin` (with `Host`/`Cookie`/`Sec-Fetch-*`), so requiring one made every desktop save fail with 403. A present `Origin` must still be loopback or the shell's own scheme, and `Host` must still be loopback (DNS-rebinding guard).
366
374
 
367
375
  ### Data semantics
368
376
 
369
377
  - Calls and tokens come from DSH session logs; `session/flush` rebuilds and queues the derived ledger only when related events are dirty, coalescing the latest pending record per session and draining on plugin disposal. Readable logs and ledger history are backfilled when the plugin activates, so successfully persisted data survives reloads or session deletion; deleting a workspace likewise keeps its history, summed into one "Deleted" row together with usage that had not reached the ledger yet
370
- - Day-level range data retains every readable historical session date with tracked usage; the heatmap is only a fixed latest-53-week view
378
+ - Day-level range data retains every readable historical session date with tracked usage; the heatmap is a switchable-span view (last 30 days / 90 days / 12 months, 12 months by default), not a fixed latest-53-week view
371
379
  - After a session is deleted, successfully flushed usage is restored from the separate ledger; disposal hints and periodic reconciliation trigger rebuilds without deleting ledger rows
372
380
  - For each session and logical `turn / step`, only the final usage contribution is kept; retries or replaced messages do not double-count
373
381
  - Input tokens are fresh (exclude cache hits/writes, which sit in their own buckets); all-zero usage replays do not overwrite recorded usage and pure cache-read requests still count
382
+ - The version check is a single read-only GET to `registry.npmjs.org` (the version number of `dsh-all-usage` only: no body, no cookies; issued by the host and cached for 6 hours, the page itself never reaches the network), and a failed check degrades to “unknown” instead of guessing
374
383
  - The lightweight status endpoint exposes only the Host instance, stats revision, scan progress, sync counters, and the last client environment report (user agent, viewport, reserved strip and its source, panel rectangles — reported by a local client and kept in memory only). It does not expose session IDs, workspace paths, prompts, or reply bodies; full snapshots are fetched only after status changes or a manual refresh
375
384
  - Scoped results keep turns, model calls, and distinct sessions as separate metrics; missing route identity is explicitly Unknown rather than inferred from a display label
376
385
  - The records endpoint returns only a short hash, time, workspace ID, structured model identity, turn/step, token buckets, and current materialization source. It omits raw session IDs, paths, prompts, replies, and credentials
@@ -601,7 +601,20 @@ export function createAggregation(host) {
601
601
  }
602
602
  function statusSnapshot() {
603
603
  commitPendingStats()
604
- return { instanceId: state.instanceId, ...revisionSnapshot(), updatedAt: state.statsUpdatedAt, scan: scanSnapshot(), sync: syncSnapshot(), clientEnv: state.clientEnv }
604
+ return {
605
+ instanceId: state.instanceId,
606
+ // The write capability is minted per plugin apply, so a reload rotates it
607
+ // without moving the process instance id or any revision. The status poll
608
+ // carries only this derived id (never the capability itself), which is enough
609
+ // for the page to notice the rotation and re-fetch the full snapshot.
610
+ capabilityId: state.capabilityId,
611
+ pluginVersion: state.pluginVersion,
612
+ ...revisionSnapshot(),
613
+ updatedAt: state.statsUpdatedAt,
614
+ scan: scanSnapshot(),
615
+ sync: syncSnapshot(),
616
+ clientEnv: state.clientEnv,
617
+ }
605
618
  }
606
619
  function serializeIdentity(identity) {
607
620
  const value = coerceIdentity(identity)