dsh-all-usage 1.1.9 → 1.1.12
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 +38 -0
- package/README.md +16 -38
- package/lib/aggregation.js +1 -1
- package/lib/client.js +1 -1
- package/lib/http.js +32 -0
- package/lib/plugin.js +19 -0
- package/lib/session-sync.js +70 -34
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `dsh-all-usage` are documented here.
|
|
4
4
|
|
|
5
|
+
## [1.1.12] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **The runtime smoke test no longer counts routes by hand.** It asserted that dispose removed exactly 9 exact routes, so v1.1.11 — which adds `/api/all-usage/client-env` — failed every Cordis smoke job and, with it, its own npm publication, while the published package itself was correct. The expected count is now derived from the route list the test already checks, so adding a route can no longer leave a stale number behind.
|
|
10
|
+
|
|
11
|
+
### Note
|
|
12
|
+
|
|
13
|
+
- v1.1.11 was published to GitHub Releases but never reached npm: the publish workflow checks out the release tag and verifies it, and a published tag is never moved. The desktop caption-strip fix reaches npm users with this release; the shipped runtime code is identical to v1.1.11.
|
|
14
|
+
|
|
15
|
+
## [1.1.11] - 2026-09-25
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **The desktop client's own close button is no longer covered.** The usage sheet is a full-window overlay, so its backdrop and grabber bar were painted straight across the 40px caption strip the DSH desktop client keeps for its window buttons (minimise / maximise / close), dimming the client's close button behind the panel. When the host marks the document with `data-windows-titlebar`, the sheet now starts below `--dsh-windows-titlebar-height` (40px on Windows), so the strip stays clear. Browsers are unaffected: without the marker nothing changes.
|
|
20
|
+
- **The strip reservation never actually applied.** `Number(localStorage.getItem('dsh-all-usage:topInset'))` reads an unset key as `0`, which passed the `>= 0` check and returned early — the host marker, the Window Controls Overlay geometry and the user-agent fallback were dead code, so the panel reserved nothing on any desktop host even though the logic was in place. An unset key is now distinguished from an explicit `0`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- The reserved strip is taken from the host's own declaration first (`data-windows-titlebar` plus `--dsh-windows-titlebar-height`), then the Window Controls Overlay rectangle, then the user agent. `localStorage['dsh-all-usage:topInset']` still overrides everything, so the strip can be tuned without a rebuild.
|
|
25
|
+
- The client reports its environment through the new `GET /api/all-usage/client-env` route, surfaced as `clientEnv` in `/api/all-usage/status`: user agent, overlay height, the reserved strip **and where that number came from**, viewport, and the panel's own rectangles. It reports at load and again whenever the sheet opens — this is what exposed the reservation bug from outside the desktop app.
|
|
26
|
+
|
|
27
|
+
## [1.1.10] - 2026-09-14
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **The ledger fast path is back.** DSH 0.1.5 renamed `sessionPersistence.listSnapshots()` to `list()`, so the plugin never obtained a per-session revision: `sessionsSkippedByRevision` stayed at 0 and every baseline re-read every session log (431 reads / ~13 minutes for 479 sessions on the maintainer's host). Both spellings are now accepted; against a real 0.1.5 service the plugin reports `persistenceSnapshotsAvailable: true` and skips unchanged sessions again.
|
|
32
|
+
- **A registry change during a baseline is no longer dropped.** `synchronizeWorkspaceRegistry()` returned early while `scan.done` was false, so a workspace added during the (previously very long) scan stayed invisible until the next restart. Such a change is now remembered and replayed as soon as the scan settles.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- The workspace registry is additionally polled every 30 seconds, so a missed `domain/changed` event can no longer leave the workspace list stale until a restart.
|
|
37
|
+
- `/api/all-usage/status` exposes `workspaceProbeEvents`, `workspaceSyncRuns`, `workspaceSyncAdded`, `workspaceSyncRemoved` and `workspaceSyncDeferred` (cumulative, not reset by a baseline) so the probe can be verified on a live host.
|
|
38
|
+
|
|
39
|
+
### Note
|
|
40
|
+
|
|
41
|
+
- The first start after upgrading still reads every log once: ledger records written before this release carry no `lastRevision`, so the revision comparison has nothing to match against. From the next start on, unchanged sessions are applied from the ledger without re-reading their logs.
|
|
42
|
+
|
|
5
43
|
## [1.1.9] - 2026-09-12
|
|
6
44
|
|
|
7
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,11 +31,12 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
|
|
|
31
31
|
### 兼容性与已知限制
|
|
32
32
|
|
|
33
33
|
- **运行环境**:需要 Node.js `>=22 <25`;CI 会在 Node 22 和 Node 24 上运行测试、语法检查和 npm 包内容检查。
|
|
34
|
-
- **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.5-rc.1`、`0.1.1-rc.2` 和 `0.1.1-rc.1` 的真实 Cordis 服务链验证;`0.1.2-rc.1` 已通过实际使用验证兼容,但未纳入 CI smoke 矩阵。声明按元组拆成两段而非写成单一区间,是因为 node-semver 只有在范围里存在与目标版本同 `major.minor.patch` 且自身带预发布标签的比较符时,才会放行该预发布版本。
|
|
34
|
+
- **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.5-rc.2`、`0.1.5-rc.1`、`0.1.1-rc.2` 和 `0.1.1-rc.1` 的真实 Cordis 服务链验证;`0.1.2-rc.1` 已通过实际使用验证兼容,但未纳入 CI smoke 矩阵。声明按元组拆成两段而非写成单一区间,是因为 node-semver 只有在范围里存在与目标版本同 `major.minor.patch` 且自身带预发布标签的比较符时,才会放行该预发布版本。
|
|
35
35
|
- **Web 服务依赖**:Host 将 `webServer` 声明为必需依赖,确保服务晚挂载时由 DSH 等待后再执行插件;该包面向 DSH Web profile,不提供无 WebServer 的 headless 路由。HTTP 守卫还会检查真实 socket peer,反向代理只有在连接本身来自 loopback 时才会被接受。
|
|
36
36
|
|
|
37
37
|
| DSH runtime | Node.js 支持 | 真实 Cordis smoke | 结论 |
|
|
38
38
|
| --- | --- | --- | --- |
|
|
39
|
+
| `0.1.5-rc.2` | `>=22 <25` | 通过(真实 Cordis 服务链,Node 24) | 已实测通过(尚未纳入 CI 矩阵) |
|
|
39
40
|
| `0.1.5-rc.1` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,Node 24) | 已声明、已验证 |
|
|
40
41
|
| `0.1.2-rc.1` | `>=22 <25` | 通过(实际使用验证,未纳入 CI) | 已实际验证兼容 |
|
|
41
42
|
| `0.1.1-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(当前 Node 24) | 已声明、已验证 |
|
|
@@ -105,25 +106,13 @@ node scripts/replay-fixture.mjs fixtures/usage-events.json
|
|
|
105
106
|
|
|
106
107
|
### 最近更新
|
|
107
108
|
|
|
108
|
-
**v1.1.
|
|
109
|
+
**v1.1.12**
|
|
109
110
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- **模型品牌图标**:请求日志、选中调用详情、模型统计表、成本设置匹配表、环形图图例与模型筛选下拉现在显示真实厂商品牌 SVG(DeepSeek、OpenAI、Claude、Gemini、Meta、Zhipu、xAI、Qwen、Doubao、Kimi、MiniMax);模型命名空间优先于 DSH 供应商名,未知或混牌行保持中性。图标在构建期以 data URI 内嵌并由 scripts/svg-guard.mjs 校验(实体解码、CSS 转义/CDO/命名空间、url()/image-set() 均覆盖,注释与文本不误杀),运行时零网络请求。
|
|
117
|
-
- **构建安全加固**:补齐 CDO/CDC 邻接 at-import、命名空间 style 元素、CSS 转义 url() 与外部 image-set() 四类绕过,并把 CSS 检查收窄到 style 属性与 style 体;对抗用例现在断言拒绝原因。
|
|
118
|
-
- 三张用量仪表盘截图刷新为当前 UI。
|
|
119
|
-
|
|
120
|
-
**v1.1.3**
|
|
121
|
-
|
|
122
|
-
- 成本统计支持经验证的 context-tiered 官方费率、可展开费率表和显式价格覆盖。
|
|
123
|
-
- **性能优化**:账本采用稳定分片、dirty flush 合并和 revision 快路径;查询、趋势和固定 53 周热力图改用写入时聚合索引,减少历史扫描、存储写放大和 Dashboard 重渲染。
|
|
124
|
-
- 定价目录、价格同步、模型检索和映射编辑强化确定性与并发刷新边界。
|
|
125
|
-
- 加强非法序列、旧账本、工作区重建、滞后 persistence revision 与复合账本键的恢复保护。
|
|
126
|
-
- 增加 Node 22/24、DSH rc.1/rc.2 runtime smoke、脱敏 fixture replay 和包内容发布门禁。
|
|
111
|
+
- **桌面端自己的关闭按钮不再被面板盖住**:用量面板是全窗口遮罩,它的半透明背景和抓手条以前会铺满桌面客户端留给窗口按钮的那条 40px 顶栏,把桌面端的关闭按钮压在面板后面。现在只要宿主声明了 `data-windows-titlebar`,抽屉就从 `--dsh-windows-titlebar-height`(Windows 为 40px)下方开始绘制,顶栏保持干净;浏览器端没有该标记,行为不变。
|
|
112
|
+
- **顶栏留白此前其实一直没生效**:`Number(localStorage.getItem('dsh-all-usage:topInset'))` 会把「未设置」读成 `0` 并提前返回,宿主标记 / Window Controls Overlay 几何 / UA 兜底全是死代码 —— 这就是上一版「改了却毫无变化」的原因。现在「未设置」与显式 `0` 已区分。
|
|
113
|
+
- 顶栏高度优先取宿主自己声明的值,其次取 Window Controls Overlay 矩形,最后退回 UA;`localStorage['dsh-all-usage:topInset']` 仍可覆盖,免重构建微调。
|
|
114
|
+
- `/api/all-usage/status` 新增 `clientEnv`(客户端经新的 `GET /api/all-usage/client-env` 上报):UA、遮罩高度、留白值**及其来源**、视口、面板自身矩形,加载时与打开面板时各一次 —— 桌面端内部正是靠它在外部核对。
|
|
115
|
+
- v1.1.11 只到了 GitHub Releases、没有到 npm(其发布流程会检出 release tag 并跑真实 Cordis smoke,而那条 smoke 的 route 计数写死为 9,被新路由打破;已发布的 tag 不移动)。桌面端修复随本版本进入 npm,运行时代码与 v1.1.11 完全相同。
|
|
127
116
|
|
|
128
117
|
完整版本记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
129
118
|
|
|
@@ -232,11 +221,12 @@ A full usage dashboard for DeepSeek Harness. Analyze tokens, cache behavior, est
|
|
|
232
221
|
### Compatibility and Known Limitations
|
|
233
222
|
|
|
234
223
|
- **Runtime**: Node.js `>=22 <25` is required. CI runs the test suite, syntax checks, and package-content checks on Node 22 and Node 24.
|
|
235
|
-
- **DSH compatibility**: `package.json` declares DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0`; the real Cordis service chain is verified on `0.1.5-rc.1`, `0.1.1-rc.2` and `0.1.1-rc.1`. `0.1.2-rc.1` has also been verified compatible through real-world use, but is not covered by the CI smoke matrix. The declaration is split per tuple rather than written as one interval because node-semver only admits a prerelease version when some comparator shares its exact `major.minor.patch` tuple and itself carries a prerelease tag.
|
|
224
|
+
- **DSH compatibility**: `package.json` declares DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0`; the real Cordis service chain is verified on `0.1.5-rc.2`, `0.1.5-rc.1`, `0.1.1-rc.2` and `0.1.1-rc.1`. `0.1.2-rc.1` has also been verified compatible through real-world use, but is not covered by the CI smoke matrix. The declaration is split per tuple rather than written as one interval because node-semver only admits a prerelease version when some comparator shares its exact `major.minor.patch` tuple and itself carries a prerelease tag.
|
|
236
225
|
- **Web service dependency**: the Host declares `webServer` as a required dependency, so DSH waits for a late-mounted service before applying the plugin; this package targets the DSH Web profile and does not expose routes without WebServer. The HTTP guard also checks the actual socket peer, so a reverse proxy is accepted only when the connection itself is loopback.
|
|
237
226
|
|
|
238
227
|
| DSH runtime | Node.js support | Real Cordis smoke | Conclusion |
|
|
239
228
|
| --- | --- | --- | --- |
|
|
229
|
+
| `0.1.5-rc.2` | `>=22 <25` | Passed (real Cordis service chain, Node 24) | Verified locally (not yet in the CI matrix) |
|
|
240
230
|
| `0.1.5-rc.1` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, Node 24) | Declared and verified |
|
|
241
231
|
| `0.1.2-rc.1` | `>=22 <25` | Passed through real-world use (not in CI) | Verified compatible in real-world use |
|
|
242
232
|
| `0.1.1-rc.2` | `>=22 <25`, CI covers 22/24 | Passed (current Node 24) | Declared and verified |
|
|
@@ -306,25 +296,13 @@ The command loads the real plugin Host, calls its compatible APIs, checks the do
|
|
|
306
296
|
|
|
307
297
|
### Latest Update
|
|
308
298
|
|
|
309
|
-
**v1.1.
|
|
310
|
-
|
|
311
|
-
- **Deleting a workspace no longer loses data**: usage already recorded for a removed workspace is no longer subtracted; it is summed with every other removed workspace into one "Deleted" row (including usage folded from the live feed that had not been persisted yet). Persisted ledger rows keep their original workspace id and cwd, so the mapping stays reversible and the same bucket is rebuilt after a restart.
|
|
312
|
-
- **Workspace registry probe**: all-usage follows DSH's workspace registry automatically through the `domain/changed` event; only added/removed workspaces are reprocessed incrementally while unchanged workspaces reuse their ledger with zero rescan. The manual Refresh workspaces control and its `POST /api/all-usage/workspaces/refresh` route were removed.
|
|
313
|
-
- **Strict registration boundary**: only sessions whose cwd maps to a registered workspace are counted; unregistered directories (even existing ones) and legacy `unregistered:` ledger rows never re-enter statistics. Usage already recorded for a deleted workspace is kept in the "Deleted" row, but its directory never accepts new sessions again.
|
|
314
|
-
|
|
315
|
-
**v1.1.4**
|
|
316
|
-
|
|
317
|
-
- **Vendor brand icons**: request logs, selected-call details, the model summary table, the cost-settings match table, donut legends, and the model filter dropdown now render real vendor brand SVGs (DeepSeek, OpenAI, Claude, Gemini, Meta, Zhipu, xAI, Qwen, Doubao, Kimi, MiniMax); the model namespace wins over the DSH provider name, and unknown or mixed-brand rows stay neutral. Icons are embedded as data URIs at build time and validated by scripts/svg-guard.mjs (entity decoding, CSS escapes/CDO/namespaced styles, url()/image-set() all covered, comments and text stay inert), with zero runtime network access.
|
|
318
|
-
- **Build hardening**: per the independent audit, closed four bypass classes (CDO/CDC-adjacent at-import, namespaced style elements, CSS-escaped url(), external image-set()) and scoped the CSS checks to style attributes and bodies; adversarial fixtures now assert the rejection reason.
|
|
319
|
-
- Refreshed the three usage-dashboard screenshots to the current UI.
|
|
320
|
-
|
|
321
|
-
**v1.1.3**
|
|
299
|
+
**v1.1.12**
|
|
322
300
|
|
|
323
|
-
-
|
|
324
|
-
- **
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
-
|
|
301
|
+
- **The desktop client's own close button is no longer covered**: the usage sheet is a full-window overlay, so its backdrop and grabber bar were painted across the 40px caption strip the desktop client keeps for its window buttons, dimming the client's close button behind the panel. When the host marks the document with `data-windows-titlebar`, the sheet now starts below `--dsh-windows-titlebar-height` (40px on Windows) and the strip stays clear. Browsers have no such marker and are unchanged.
|
|
302
|
+
- **The strip reservation never actually applied**: `Number(localStorage.getItem('dsh-all-usage:topInset'))` read an unset key as `0` and returned early, leaving the host marker, the Window Controls Overlay geometry and the user-agent fallback as dead code — which is why the previous attempt changed nothing at all. An unset key is now distinguished from an explicit `0`.
|
|
303
|
+
- The strip height comes from the host's own declaration first, then the Window Controls Overlay rectangle, then the user agent; `localStorage['dsh-all-usage:topInset']` still overrides it, so no rebuild is needed to tune it.
|
|
304
|
+
- `/api/all-usage/status` now exposes `clientEnv` (reported through the new `GET /api/all-usage/client-env` route): user agent, overlay height, the reserved strip **and its source**, viewport, and the panel's own rectangles, sent at load and whenever the sheet opens — which is how the reservation bug was pinned down from outside the desktop app.
|
|
305
|
+
- v1.1.11 reached GitHub Releases but never npm: its publish workflow checks out the release tag and runs the real Cordis smoke, whose route count was hard-coded to 9 and broke on the new route (a published tag is never moved). The desktop fix reaches npm with this release; the runtime code is identical to v1.1.11.
|
|
328
306
|
|
|
329
307
|
See [CHANGELOG.md](CHANGELOG.md) for the complete version history.
|
|
330
308
|
|
package/lib/aggregation.js
CHANGED
|
@@ -601,7 +601,7 @@ 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() }
|
|
604
|
+
return { instanceId: state.instanceId, ...revisionSnapshot(), updatedAt: state.statsUpdatedAt, scan: scanSnapshot(), sync: syncSnapshot(), clientEnv: state.clientEnv }
|
|
605
605
|
}
|
|
606
606
|
function serializeIdentity(identity) {
|
|
607
607
|
const value = coerceIdentity(identity)
|