dsh-all-usage 1.0.6 → 1.0.7
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 +57 -0
- package/README.md +28 -2
- package/lib/index.js +43 -2
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `dsh-all-usage` are documented here.
|
|
4
|
+
|
|
5
|
+
## [1.0.7] - 2026-08-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added an incremental ledger cursor: baseline rescans reuse the durable ledger as a per-session cursor, so unchanged sessions seed from the ledger instead of re-folding every historical event, and changed sessions fold only the new tail.
|
|
10
|
+
- Added an all-zero usage guard: zero-token usage replays no longer overwrite already-recorded turn/step usage, while pure cache-read requests (billable cache hits with no input/output) are still counted.
|
|
11
|
+
- Added a structured, machine-readable token semantics declaration (`tokenSemantics.semantics`) to the `/api/all-usage` snapshot: input is fresh (excludes cache), cache and reasoning are separate buckets.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Reduced startup scan cost on long usage history: sessions whose events are unchanged since the last ledger write are no longer re-folded or rewritten.
|
|
16
|
+
- Prevented a live retry of an already-recorded step from double-counting after a fresh-instance seed.
|
|
17
|
+
|
|
18
|
+
## [1.0.6] - 2026-08-23
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added a durable per-session usage ledger written at the awaited `session/flush` boundary.
|
|
23
|
+
- Preserved successfully flushed turns and token usage after conversation logs are deleted.
|
|
24
|
+
- Added automatic rebuilds from ledger records after session disposal or reconciliation detects missing logs.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Prevented stale baseline snapshots from overwriting newer flushed ledger records.
|
|
29
|
+
- Normalized duplicate ledger keys during hydration and guarded ledger writes during plugin disposal.
|
|
30
|
+
- Prevented inactive Cordis timer contexts from escalating into fatal DSH load failures.
|
|
31
|
+
- Replaced invalid hyphenated storage unit names with valid underscore names.
|
|
32
|
+
|
|
33
|
+
## [1.0.5] - 2026-08-20
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Added custom start/end date ranges across all available historical daily data.
|
|
38
|
+
- Kept the heatmap as a fixed latest-53-week view while allowing older range aggregation and CSV export.
|
|
39
|
+
- Added distinct session counts for bounded ranges.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Added baseline retry with backoff after transient session registry failures.
|
|
44
|
+
- Stopped in-flight scans and stale polling responses after plugin disposal or refresh races.
|
|
45
|
+
|
|
46
|
+
## [1.0.4] - 2026-08-19
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Hardened loopback, same-origin, method, and process-token checks for dashboard API routes.
|
|
51
|
+
- Allowed same-origin browser balance GET requests that omit `Origin` while retaining token protection.
|
|
52
|
+
- Standardized English date buckets, range filters, streaks, heatmap dates, and export timestamps on UTC.
|
|
53
|
+
|
|
54
|
+
[1.0.7]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.7
|
|
55
|
+
[1.0.6]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.6
|
|
56
|
+
[1.0.5]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.5
|
|
57
|
+
[1.0.4]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.4
|
package/README.md
CHANGED
|
@@ -17,6 +17,18 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
|
|
|
17
17
|
- **时间范围**:今日、近 30 天、近 90 天、全部,或在全部可扫描历史日数据中自定义起止日期;热力图始终展示最近 53 周
|
|
18
18
|
- **工作区别名**:在侧栏入口打开看板后管理,持久化保存到 $DSH_HOME/storages 的 KV 单元 `all_usage_aliases`
|
|
19
19
|
- **界面语言**:在看板顶部切换中文与 English;选择会保存到浏览器本地
|
|
20
|
+
- **完整历史与增量重建**:基线扫描全部可读历史会话;独立用量账本同时作为每会话游标——未变化的会话直接复用账本,新增事件只增量回填,长历史重启不再全量重建
|
|
21
|
+
- **Token 口径**:输入按「未含缓存命中」计,缓存命中 / 写入与推理独立成桶;全 0 用量的重放事件不会覆盖已记录的真实用量,仅缓存命中的请求也会计入
|
|
22
|
+
|
|
23
|
+
### 最近更新
|
|
24
|
+
|
|
25
|
+
**v1.0.7**
|
|
26
|
+
|
|
27
|
+
- 增量游标:启动重扫只增量回填新增事件,未变化的会话直接从独立账本复用,不再全量重建
|
|
28
|
+
- 全 0 用量防护:全 0 用量的重放事件不会覆盖已记录的真实用量;纯缓存命中的请求仍会计入
|
|
29
|
+
- 结构化语义声明:API 返回机读的 token 口径(输入不含缓存命中,缓存与推理独立成桶)
|
|
30
|
+
|
|
31
|
+
完整版本记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
20
32
|
|
|
21
33
|
### 截图 / Screenshots
|
|
22
34
|
|
|
@@ -26,7 +38,7 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
|
|
|
26
38
|
|
|
27
39
|
### 安装
|
|
28
40
|
|
|
29
|
-
本插件是标准的 DSH 社区插件包(声明 `dsh.bundle` manifest 与 Web Client
|
|
41
|
+
本插件是标准的 DSH 社区插件包(声明 `dsh.bundle` manifest 与 Web Client),数据来自持久化会话日志和独立用量账本,安装后自动回填历史。
|
|
30
42
|
|
|
31
43
|
#### 官方插件命令(推荐)
|
|
32
44
|
|
|
@@ -68,6 +80,7 @@ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
|
|
|
68
80
|
- 按日范围统计会保留全部可读取历史会话的有使用记录日期;热力图仅作为最近 53 周的固定视图窗口
|
|
69
81
|
- 会话删除后,已成功 flush 的用量仍从独立账本恢复;会话销毁提示和周期对账只负责触发重建,不会删除账本记录
|
|
70
82
|
- 同一会话的同一 `turn / step` 只保留一份最终 usage;重试或替换消息会替换旧贡献,不重复累计
|
|
83
|
+
- 输入 Token 按「未含缓存命中」计(缓存命中 / 写入独立成桶);全 0 用量的重放事件不会覆盖已记录的真实用量,纯缓存命中的请求仍会计入
|
|
71
84
|
- 看板中的总处理量 = 输入 + 输出 + 缓存读写 + 推理;缓存命中表示复用的上下文 Token,不等于新生成 Token 或实际费用
|
|
72
85
|
- 余额查询走 DeepSeek 官方 `/user/balance` 接口;未配置 API Key 时卡片显示引导文案
|
|
73
86
|
- 仅统计能归属到已注册工作区(按会话 cwd 匹配)的会话
|
|
@@ -90,10 +103,22 @@ A full usage dashboard for DeepSeek Harness. Analyze tokens, cache behavior, acc
|
|
|
90
103
|
- **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
|
|
91
104
|
- **Workspace aliases**: manage aliases from the sidebar dashboard; values persist in the $DSH_HOME/storages KV cell `all_usage_aliases`
|
|
92
105
|
- **Interface language**: switch between Chinese and English from the dashboard header; your choice persists locally in the browser
|
|
106
|
+
- **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
|
|
107
|
+
- **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
|
|
108
|
+
|
|
109
|
+
### Latest Update
|
|
110
|
+
|
|
111
|
+
**v1.0.7**
|
|
112
|
+
|
|
113
|
+
- Incremental scan cursor: restarts re-fold only newly appended events; unchanged sessions are seeded straight from the durable ledger instead of a full rebuild
|
|
114
|
+
- All-zero usage guard: zero-usage replays no longer overwrite recorded usage, while pure cache-read requests still count
|
|
115
|
+
- Structured token semantics: the API now declares a machine-readable accounting contract (input excludes cache; cache and reasoning are separate buckets)
|
|
116
|
+
|
|
117
|
+
See [CHANGELOG.md](CHANGELOG.md) for the complete version history.
|
|
93
118
|
|
|
94
119
|
### Installation
|
|
95
120
|
|
|
96
|
-
This is a standard DSH community bundle. It declares a `dsh.bundle` manifest and a web client, and backfills its data from persisted session logs after installation.
|
|
121
|
+
This is a standard DSH community bundle. It declares a `dsh.bundle` manifest and a web client, and backfills its data from persisted session logs and the durable usage ledger after installation.
|
|
97
122
|
|
|
98
123
|
#### Official plugin command (recommended)
|
|
99
124
|
|
|
@@ -135,6 +160,7 @@ The profile patch layer hot-reloads; save the file and refresh the page.
|
|
|
135
160
|
- Day-level range data retains every readable historical session date with tracked usage; the heatmap is only a fixed latest-53-week view
|
|
136
161
|
- 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
|
|
137
162
|
- For each session and logical `turn / step`, only the final usage contribution is kept; retries or replaced messages do not double-count
|
|
163
|
+
- 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
|
|
138
164
|
- Processed tokens = input + output + cache read/write + reasoning; a cache hit means reused context, not newly generated tokens or actual cost
|
|
139
165
|
- Balance data comes from DeepSeek’s official `/user/balance` endpoint; the card shows guidance when no API key is configured
|
|
140
166
|
- English mode uses UTC for date buckets, range filters, streaks, heatmap dates, and export timestamps; Chinese mode uses local time
|
package/lib/index.js
CHANGED
|
@@ -259,6 +259,10 @@ function apply(ctx) {
|
|
|
259
259
|
const values = usageValues(usage)
|
|
260
260
|
const modelId = typeof model === 'string' && model !== '' ? model : '未知模型(历史记录缺少路由)'
|
|
261
261
|
const eventSeq = typeof seq === 'number' ? seq : -1
|
|
262
|
+
// v1.0.7: a usage event carrying no billable token in any bucket must not add a
|
|
263
|
+
// meaningless row nor wipe previously recorded real usage (cc-switch
|
|
264
|
+
// has_billable_tokens parity). Pure cache-read requests are billable and pass.
|
|
265
|
+
if (noValues(values)) return
|
|
262
266
|
const key = usageStepKey(sid, data, seq)
|
|
263
267
|
const previous = usageByStep.get(key)
|
|
264
268
|
// A late replay of an older raw event cannot replace the canonical later step.
|
|
@@ -320,6 +324,9 @@ function apply(ctx) {
|
|
|
320
324
|
}
|
|
321
325
|
if (event.type !== 'assistant/message' || data === null || typeof data !== 'object' || data.usage === undefined) continue
|
|
322
326
|
const values = usageValues(data.usage)
|
|
327
|
+
// v1.0.7: all-zero usage rows carry no billable tokens and stay out of the
|
|
328
|
+
// durable ledger (cc-switch has_billable_tokens parity).
|
|
329
|
+
if (noValues(values)) continue
|
|
323
330
|
const modelId = typeof modelFromMessage(data, currentModel) === 'string' && modelFromMessage(data, currentModel) !== ''
|
|
324
331
|
? modelFromMessage(data, currentModel) : '未知模型(历史记录缺少路由)'
|
|
325
332
|
const eventSeq = typeof event.seq === 'number' ? event.seq : -1
|
|
@@ -563,15 +570,37 @@ function apply(ctx) {
|
|
|
563
570
|
const snap = await ctx.sessionQuery.readSession(sid)
|
|
564
571
|
if (disposed || generation !== aggregationGeneration) return
|
|
565
572
|
if (snap && Array.isArray(snap.events)) {
|
|
573
|
+
// v1.0.7: incremental seed — the durable ledger doubles as a per-session
|
|
574
|
+
// cursor (cc-switch session_log_sync mtime+offset parity). An unchanged
|
|
575
|
+
// session applies its canonical record directly and never re-folds;
|
|
576
|
+
// a changed session seeds the previous record once, then folds only the
|
|
577
|
+
// new tail (previously every listed session was re-read and fully rebuilt).
|
|
578
|
+
const currentLastSeq = lastSeqOf(snap.events)
|
|
579
|
+
const previous = ledgerRecords.get(sid)
|
|
580
|
+
if (previous !== undefined) {
|
|
581
|
+
applyLedgerRecord(previous)
|
|
582
|
+
// Seed usageByStep so a later live retry of an already-recorded step
|
|
583
|
+
// reverses the ledger contribution instead of double-counting it.
|
|
584
|
+
for (const item of previous.usage) {
|
|
585
|
+
usageByStep.set(item.key, { seq: item.seq, wsId: item.workspaceId, time: item.time, values: item.values, modelId: item.modelId, sid })
|
|
586
|
+
}
|
|
587
|
+
if (previous.lastSeq >= currentLastSeq) {
|
|
588
|
+
sessionSeq.set(sid, previous.lastSeq)
|
|
589
|
+
sessionCount.add(sid)
|
|
590
|
+
return
|
|
591
|
+
}
|
|
592
|
+
foldEvents(wsId, snap.events, previous.lastSeq, sid)
|
|
593
|
+
} else {
|
|
594
|
+
foldEvents(wsId, snap.events, undefined, sid)
|
|
595
|
+
}
|
|
566
596
|
const ledger = buildLedgerRecord({ id: sid, header: record.header, events: snap.events }, wsId)
|
|
567
597
|
const canonical = ledger === null ? ledgerRecords.get(sid) : storeLedgerRecord(ledger)
|
|
568
598
|
if (canonical === ledger) {
|
|
569
599
|
void persistLedgerRecord(ledger)
|
|
570
|
-
foldEvents(wsId, snap.events, undefined, sid)
|
|
571
600
|
} else if (canonical !== undefined) {
|
|
572
601
|
applyLedgerRecord(canonical)
|
|
573
602
|
}
|
|
574
|
-
sessionSeq.set(sid,
|
|
603
|
+
sessionSeq.set(sid, currentLastSeq)
|
|
575
604
|
sessionCount.add(sid)
|
|
576
605
|
}
|
|
577
606
|
} catch (err) {
|
|
@@ -786,6 +815,18 @@ function apply(ctx) {
|
|
|
786
815
|
processedTotal: 'input + output + cacheRead + cacheWrite + reasoning',
|
|
787
816
|
cacheRead: 'reused context tokens; not newly generated output',
|
|
788
817
|
cacheWrite: 'tokens written into a provider cache',
|
|
818
|
+
// v1.0.7: structured, machine-readable accounting semantics (cc-switch
|
|
819
|
+
// input_token_semantics parity). DSH reports input as fresh (cache read /
|
|
820
|
+
// cache write sit in their own buckets) — verified against real ledger data;
|
|
821
|
+
// reasoning is bucketed separately from output and assumed non-overlapping.
|
|
822
|
+
semantics: {
|
|
823
|
+
input: 'fresh (excludes cache-read and cache-write tokens, bucketed separately)',
|
|
824
|
+
buckets: ['input', 'output', 'cacheRead', 'cacheWrite', 'reasoning'],
|
|
825
|
+
inputIncludesCache: false,
|
|
826
|
+
cacheBucketed: true,
|
|
827
|
+
reasoningSeparate: true,
|
|
828
|
+
gate: 'all-zero usage rows are ignored; pure cache-read requests still count',
|
|
829
|
+
},
|
|
789
830
|
},
|
|
790
831
|
totals: { turns: totals.turns, sessions: sessionCount.size, input: totals.input, output: totals.output, cacheRead: totals.cacheRead, cacheWrite: totals.cacheWrite, reasoning: totals.reasoning },
|
|
791
832
|
perWorkspace: Array.from(perWorkspace, (p) => ({ workspaceId: p[0], turns: p[1].turns, input: p[1].input, output: p[1].output, cacheRead: p[1].cacheRead, cacheWrite: p[1].cacheWrite, reasoning: p[1].reasoning })),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-all-usage",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "DeepSeek Harness usage dashboard with model, provider, workspace, cache, balance, and CSV insights",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"files": [
|
|
38
38
|
"lib/index.js",
|
|
39
39
|
"lib/client.js",
|
|
40
|
+
"CHANGELOG.md",
|
|
40
41
|
"cordis.patch.yml",
|
|
41
42
|
"assets"
|
|
42
43
|
],
|