@chenmiao8563/dsh-token-ledger 0.1.0
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 +35 -0
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/README.zh.md +208 -0
- package/bin/dsh-token-ledger.mjs +12 -0
- package/cordis.patch.yml +20 -0
- package/docs/VERIFICATION.md +173 -0
- package/lib/cli.js +443 -0
- package/lib/index.js +253 -0
- package/lib/ledger.js +653 -0
- package/lib/session-log.js +142 -0
- package/lib/store.js +101 -0
- package/package.json +75 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
Nothing yet.
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-09-10
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Host plugin `token-ledger` that folds the durable session event stream into
|
|
16
|
+
per-session, per-day and per-model token totals.
|
|
17
|
+
- Four disjoint buckets (uncached input, output, cache read, cache write) with
|
|
18
|
+
`totalTokens` derived from them, plus reasoning tokens reported separately as
|
|
19
|
+
a subset of output.
|
|
20
|
+
- Fork-aware folding: an inherited prefix is cut only when the session has a
|
|
21
|
+
parent, so a fork is not double counted and a resume is not under counted.
|
|
22
|
+
- Backfill of every stored session on startup, so a fresh install shows real
|
|
23
|
+
history rather than starting from zero.
|
|
24
|
+
- Restart-safe cursors: the ledger is written atomically to
|
|
25
|
+
`<DSH_HOME>/token-ledger/ledger.json` and resumes instead of recounting.
|
|
26
|
+
- `/tokens [summary|export|json|path]` conversation command.
|
|
27
|
+
- `dsh-token-ledger` CLI with `summary`, `audit`, `rebuild` and `export`
|
|
28
|
+
commands, `--json` output, and an audit that distinguishes a ledger merely
|
|
29
|
+
behind a live session from a genuinely wrong ledger.
|
|
30
|
+
- CSV export for the daily, session and model tables.
|
|
31
|
+
- Zero runtime dependencies, zero peer dependencies and no install scripts, so
|
|
32
|
+
the package installs without a build step.
|
|
33
|
+
|
|
34
|
+
[Unreleased]: https://github.com/chenmiao8563/dsh-token-ledger/compare/v0.1.0...HEAD
|
|
35
|
+
[0.1.0]: https://github.com/chenmiao8563/dsh-token-ledger/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chenmiao8563
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# dsh-token-ledger
|
|
2
|
+
|
|
3
|
+
Transparent, auditable token accounting for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
4
|
+
|
|
5
|
+
[](https://github.com/chenmiao8563/dsh-token-ledger/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@chenmiao8563/dsh-token-ledger)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
[English](README.md) | [中文](README.zh.md)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What this is
|
|
14
|
+
|
|
15
|
+
A ledger, not a dashboard. It folds the durable DSH session log into token
|
|
16
|
+
totals, keeps them restart-safe, and — the part that matters — lets you
|
|
17
|
+
**recompute the exact same numbers from the raw logs and diff them** against
|
|
18
|
+
what the running host recorded.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
$ dsh-token-ledger audit
|
|
22
|
+
scanned 139 session log(s), 344203 events, 29 fork(s), 0 unreadable
|
|
23
|
+
|
|
24
|
+
stored 6901 calls 1205685663 tokens
|
|
25
|
+
recomputed 6901 calls 1205685663 tokens
|
|
26
|
+
|
|
27
|
+
audit: match — the stored ledger equals a fresh fold of the raw logs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If you only want a pretty chart, there are a dozen plugins for that. This one is
|
|
31
|
+
for when you need to *defend* the number.
|
|
32
|
+
|
|
33
|
+
## Why it installs where others do not
|
|
34
|
+
|
|
35
|
+
| Property | Why it matters |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| **Zero dependencies, zero peer dependencies** | Nothing to resolve, so a version drift in a DSH package cannot break the install. |
|
|
38
|
+
| **Zero install scripts** | `dsh plugin add` from a git URL works immediately — pnpm has no build to block and no `allowBuilds` entry to add. |
|
|
39
|
+
| **Only `node:` imports** | The host half loads from any profile (web, desktop, headless, TUI) without resolving a single package. |
|
|
40
|
+
| **No model-facing surface** | It registers no prompt section, no message and no tool, so it cannot change a request prefix or hurt KV-cache reuse. |
|
|
41
|
+
| **Fails soft** | Every hook is guarded. A ledger fault logs a warning; it never fails a session. |
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# from npm
|
|
47
|
+
dsh plugin --profile web add @chenmiao8563/dsh-token-ledger
|
|
48
|
+
|
|
49
|
+
# from a git URL (no build step is involved)
|
|
50
|
+
dsh plugin --profile web add github:chenmiao8563/dsh-token-ledger
|
|
51
|
+
|
|
52
|
+
# from a local checkout
|
|
53
|
+
dsh plugin --profile web add /absolute/path/to/dsh-token-ledger
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The npm package is scoped because npm rejects the unscoped name
|
|
57
|
+
`dsh-token-ledger` as too similar to an existing package once separators are
|
|
58
|
+
normalized away. The CLI command is still `dsh-token-ledger`.
|
|
59
|
+
|
|
60
|
+
Restart DSH, then check the row landed:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
dsh --profile web --dump-config | grep token-ledger
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
In the conversation, `/tokens` prints the ledger:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
/tokens
|
|
70
|
+
/tokens export # write CSV + JSON under <DSH_HOME>/token-ledger/exports/
|
|
71
|
+
/tokens json # the raw snapshot
|
|
72
|
+
/tokens path # where the ledger file is
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The ledger is written to `<DSH_HOME>/token-ledger/ledger.json`.
|
|
76
|
+
|
|
77
|
+
## Counting rules
|
|
78
|
+
|
|
79
|
+
The numbers are only useful if you know exactly what they count.
|
|
80
|
+
|
|
81
|
+
| Rule | Behaviour |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| **Successful anchors only** | Usage is taken from `assistant/message` (a completed step) and `compaction/summary` (one compaction call). A failed or cancelled attempt never appends either, so it is never counted. |
|
|
84
|
+
| **Streaming samples are replaced, not added** | When a step emits a `usage` chunk and then its final message, the final value replaces the earlier sample — one call either way. |
|
|
85
|
+
| **`totalTokens` is derived** | It is the sum of the four buckets, never the provider's own total field. Across 15,778 real usage reports the two agreed every single time, and deriving it keeps the buckets and the total consistent by construction. |
|
|
86
|
+
| **Reasoning tokens are a subset** | Reported separately, never added into the total, because they are already inside `outputTokens`. |
|
|
87
|
+
| **Local calendar days** | Days are your days, not UTC days. |
|
|
88
|
+
| **Forks are cut, resumes are not** | See below. |
|
|
89
|
+
|
|
90
|
+
### Forks versus resumes
|
|
91
|
+
|
|
92
|
+
A stored log can begin with a prefix of history that was already recorded. Two
|
|
93
|
+
different situations produce one, and getting them confused is the most common
|
|
94
|
+
way a usage plugin silently gets the wrong answer:
|
|
95
|
+
|
|
96
|
+
- **Fork** (`parentSession` set): the prefix is the *parent's* history, counted
|
|
97
|
+
in the parent's own log. Counting it again here would double count. It is cut.
|
|
98
|
+
- **Resume** (no parent): the prefix is *this session's own* earlier history,
|
|
99
|
+
stored once. Cutting it would lose tokens. Nothing is cut.
|
|
100
|
+
|
|
101
|
+
This is not a guess. Against real logs, every forked session whose parent log was
|
|
102
|
+
still present had its pre-marker usage fingerprints contained in the parent's,
|
|
103
|
+
while the non-forked logs carrying the same marker never repeated their prefix
|
|
104
|
+
later in the file. On one real 139-session home the distinction was worth
|
|
105
|
+
79.9 million tokens of double counting that a naive "count every log" pass
|
|
106
|
+
reports.
|
|
107
|
+
|
|
108
|
+
## CLI
|
|
109
|
+
|
|
110
|
+
Works without DSH running — it reads the raw logs directly.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
dsh-token-ledger [summary] [options] print the stored ledger (default)
|
|
114
|
+
dsh-token-ledger audit [options] recompute from raw logs and diff
|
|
115
|
+
dsh-token-ledger rebuild [options] recompute from raw logs
|
|
116
|
+
dsh-token-ledger export [options] write CSV and JSON exports
|
|
117
|
+
|
|
118
|
+
--home <path> DSH home to read (default: $DSH_HOME, else ~/.dsh)
|
|
119
|
+
--ledger <path> ledger file to read or write
|
|
120
|
+
--out <path> export destination directory
|
|
121
|
+
--days <n> days in the summary (default 7)
|
|
122
|
+
--models <n> models in the summary (default 5)
|
|
123
|
+
--write with rebuild: replace the stored ledger
|
|
124
|
+
--json machine-readable output
|
|
125
|
+
--quiet suppress the human summary, keep the exit code
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Exit codes: `0` success or audit match, `1` audit found a real difference,
|
|
129
|
+
`2` bad usage or an unreadable input. That makes it usable as a scheduled check.
|
|
130
|
+
|
|
131
|
+
### The audit distinguishes two kinds of difference
|
|
132
|
+
|
|
133
|
+
A running host writes its ledger on a debounce, so a live session is routinely a
|
|
134
|
+
little ahead of the stored file. Reporting that as corruption would make the
|
|
135
|
+
audit useless. The ledger's own `updatedAt` settles it:
|
|
136
|
+
|
|
137
|
+
- a differing session whose newest event is **newer than the ledger** has simply
|
|
138
|
+
kept running → *match*, with the unflushed amount reported;
|
|
139
|
+
- a differing session whose newest event **predates the ledger**, or a day/model
|
|
140
|
+
row that contradicts the fold it should sum to → *mismatch*, exit 1.
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
audit: match — 1 session(s) advanced after the ledger was written
|
|
144
|
+
(1 calls, 5100 tokens not yet flushed)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Configuration
|
|
148
|
+
|
|
149
|
+
Override the composition entry by its `id`:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
- id: token-ledger
|
|
153
|
+
config:
|
|
154
|
+
ledgerPath: 'D:/dsh/ledger.json' # default: <DSH_HOME>/token-ledger/ledger.json
|
|
155
|
+
backfill: false # default: true — fold stored history on startup
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## What it deliberately does not do
|
|
159
|
+
|
|
160
|
+
- **No pricing.** It counts tokens; turning them into money needs a price
|
|
161
|
+
catalogue that goes stale. Pair it with a billing plugin if you want money.
|
|
162
|
+
- **No model-facing tool.** A tool schema costs prompt tokens on every request
|
|
163
|
+
and shifts the cache prefix — a strange thing for a token-accounting plugin to
|
|
164
|
+
do. `/tokens` and the CLI cover the human cases.
|
|
165
|
+
- **No UI in 0.1.0.** The host half and the CLI are the contract; a browser half
|
|
166
|
+
is planned for 0.2 as a pure addition.
|
|
167
|
+
|
|
168
|
+
## Compatibility
|
|
169
|
+
|
|
170
|
+
- **Node:** ≥ 22.15.0 (the CLI decodes Zstandard frames). The host half itself
|
|
171
|
+
needs nothing version-specific.
|
|
172
|
+
- **DSH:** verified on `0.1.2-rc.1`. The surface used — `ctx.on`, `ctx.inject`,
|
|
173
|
+
`ctx.get`, `ctx.effect`, `commands.register`, and
|
|
174
|
+
`sessionPersistence.list()/inspect()` — is the same across the `0.1.2` line.
|
|
175
|
+
- **Profiles:** any. There is no profile-specific code.
|
|
176
|
+
|
|
177
|
+
## Uninstall
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
dsh plugin --profile web remove @chenmiao8563/dsh-token-ledger
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The ledger file is left alone on purpose — delete
|
|
184
|
+
`<DSH_HOME>/token-ledger/` yourself to reset the history.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm test # 49 tests, no dependencies to install
|
|
190
|
+
npm run verify # packaging invariants (dependency-free, no install scripts, no bare imports)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`npm test` uses Node's built-in test runner. In a restricted environment where
|
|
194
|
+
spawning a child process per test file is blocked, use
|
|
195
|
+
`npm run test:single-process`.
|
|
196
|
+
|
|
197
|
+
See [docs/VERIFICATION.md](docs/VERIFICATION.md) for what was actually verified
|
|
198
|
+
and how, including the evidence behind the fork rule.
|
|
199
|
+
|
|
200
|
+
## Releasing
|
|
201
|
+
|
|
202
|
+
npm requires two-factor authentication for every publish, so a version's first
|
|
203
|
+
release is interactive:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npm login
|
|
207
|
+
npm publish --access public --otp=<six digits from your authenticator>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
After that first publish, configure a **Trusted Publisher** for the package on
|
|
211
|
+
npmjs.com (package → Settings → Trusted Publisher → GitHub Actions, repository
|
|
212
|
+
`chenmiao8563/dsh-token-ledger`, workflow `release.yml`). OIDC cannot be set up
|
|
213
|
+
before the package exists, which is why the first release is manual. From then
|
|
214
|
+
on, a tag push publishes with no stored token at all:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
git tag v0.1.1 && git push origin v0.1.1
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`release.yml` skips the publish step when the version is already on the
|
|
221
|
+
registry and creates the GitHub release regardless, so re-running it is safe.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# dsh-token-ledger
|
|
2
|
+
|
|
3
|
+
为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 提供透明、可审计的 token 记账。
|
|
4
|
+
|
|
5
|
+
[](https://github.com/chenmiao8563/dsh-token-ledger/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@chenmiao8563/dsh-token-ledger)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
[English](README.md) | 中文
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 这是什么
|
|
14
|
+
|
|
15
|
+
这是一本**账本**,不是一块仪表盘。它把 DSH 的持久会话日志折叠成 token 用量,保证重启不丢,
|
|
16
|
+
而且——这是关键——允许你**从原始日志重算同一批数字并与之对账**。
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
$ dsh-token-ledger audit
|
|
20
|
+
scanned 139 session log(s), 344203 events, 29 fork(s), 0 unreadable
|
|
21
|
+
|
|
22
|
+
stored 6901 calls 1205685663 tokens
|
|
23
|
+
recomputed 6901 calls 1205685663 tokens
|
|
24
|
+
|
|
25
|
+
audit: match — the stored ledger equals a fresh fold of the raw logs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
只想看漂亮的图表,市面上有十几个同类插件。这个插件是给你**需要为这个数字辩护**的时候用的。
|
|
29
|
+
|
|
30
|
+
## 为什么别的插件装不上时它能装上
|
|
31
|
+
|
|
32
|
+
| 特性 | 为什么重要 |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| **零依赖、零 peer 依赖** | 没有东西需要解析,DSH 内部包的版本漂移搞不坏安装。 |
|
|
35
|
+
| **零安装脚本** | 直接用 git URL `dsh plugin add` 即可——pnpm 没有构建要拦,你也不必去 `allowBuilds` 里加白名单。 |
|
|
36
|
+
| **只 import `node:`** | 宿主端可以从任意 profile(web / desktop / headless / TUI)加载,不需要解析任何包。 |
|
|
37
|
+
| **不碰模型可见面** | 它不注册任何提示词段、消息或工具,因此不会改变请求前缀,也不会损害 KV cache 复用。 |
|
|
38
|
+
| **故障降级** | 每个钩子都有保护。账本出问题只记一条警告,绝不让会话失败。 |
|
|
39
|
+
|
|
40
|
+
## 安装
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 从 npm
|
|
44
|
+
dsh plugin --profile web add @chenmiao8563/dsh-token-ledger
|
|
45
|
+
|
|
46
|
+
# 从 git URL(不涉及任何构建步骤)
|
|
47
|
+
dsh plugin --profile web add github:chenmiao8563/dsh-token-ledger
|
|
48
|
+
|
|
49
|
+
# 从本地仓库
|
|
50
|
+
dsh plugin --profile web add /absolute/path/to/dsh-token-ledger
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
npm 包名带 scope,是因为 npm 会把分隔符归一化后比较,无 scope 的
|
|
54
|
+
`dsh-token-ledger` 被判为与已有包过于相似而拒绝发布。CLI 命令名仍然是
|
|
55
|
+
`dsh-token-ledger`。
|
|
56
|
+
|
|
57
|
+
重启 DSH,然后确认那一行进去了:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
dsh --profile web --dump-config | grep token-ledger
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
在对话里用 `/tokens`:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
/tokens
|
|
67
|
+
/tokens export # 把 CSV 与 JSON 写到 <DSH_HOME>/token-ledger/exports/
|
|
68
|
+
/tokens json # 原始快照
|
|
69
|
+
/tokens path # 账本文件位置
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
账本写在 `<DSH_HOME>/token-ledger/ledger.json`。
|
|
73
|
+
|
|
74
|
+
## 计数规则
|
|
75
|
+
|
|
76
|
+
只有知道它到底在数什么,这些数字才有用。
|
|
77
|
+
|
|
78
|
+
| 规则 | 行为 |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| **只认成功锚点** | 用量取自 `assistant/message`(已完成的步)与 `compaction/summary`(一次压缩调用)。失败或被取消的尝试不会追加这两种事件,因此永远不计入。 |
|
|
81
|
+
| **流式样本是替换,不是相加** | 同一步先出 `usage` chunk、后出最终消息时,最终值**替换**早期样本——无论哪种情况都算一次调用。 |
|
|
82
|
+
| **`totalTokens` 是推导出来的** | 它是四个桶之和,绝不采信提供方自己的总量字段。在 15,778 份真实用量报告上两者完全一致,而推导能让桶与总量在构造上永远自洽。 |
|
|
83
|
+
| **推理 token 是子集** | 单独报告,绝不相加进总量,因为它本来就在 `outputTokens` 里面。 |
|
|
84
|
+
| **按本地日历日** | 是你所在时区的日,不是 UTC 日。 |
|
|
85
|
+
| **fork 切、resume 不切** | 见下。 |
|
|
86
|
+
|
|
87
|
+
### fork 与 resume 的区别
|
|
88
|
+
|
|
89
|
+
存储的日志可能以一段"已经记录过的历史"开头。有两种完全不同的情况会产生这种前缀,
|
|
90
|
+
把它们搞混是用量插件静默算错的最常见原因:
|
|
91
|
+
|
|
92
|
+
- **fork**(有 `parentSession`):前缀是**父会话**的历史,已经在父会话自己的日志里计过。
|
|
93
|
+
在这里再计一次就是重复计入——必须切掉。
|
|
94
|
+
- **resume**(没有父会话):前缀是**这个会话自己**更早的历史,只存了一次。
|
|
95
|
+
切掉就是漏计——不能切。
|
|
96
|
+
|
|
97
|
+
这不是猜的。对照真实日志:所有父日志仍在磁盘上的 fork 会话,其边界标记之前的用量指纹
|
|
98
|
+
都包含在父会话里;而带同样标记但没有父会话的日志,其前缀在文件后半段从未重复出现。
|
|
99
|
+
在一个真实的 139 会话 home 上,这个区别意味着 **7,992 万 token** 的重复计入——那是
|
|
100
|
+
"把每个日志都完整计一遍"的天真做法会报出来的数字。
|
|
101
|
+
|
|
102
|
+
## 命令行
|
|
103
|
+
|
|
104
|
+
不需要 DSH 在运行——它直接读原始日志。
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
dsh-token-ledger [summary] [选项] 打印已存账本(默认)
|
|
108
|
+
dsh-token-ledger audit [选项] 从原始日志重算并对比
|
|
109
|
+
dsh-token-ledger rebuild [选项] 从原始日志重算
|
|
110
|
+
dsh-token-ledger export [选项] 导出 CSV 与 JSON
|
|
111
|
+
|
|
112
|
+
--home <path> DSH home(默认 $DSH_HOME,其次 ~/.dsh)
|
|
113
|
+
--ledger <path> 要读写的账本文件
|
|
114
|
+
--out <path> 导出目录
|
|
115
|
+
--days <n> 摘要显示天数(默认 7)
|
|
116
|
+
--models <n> 摘要显示模型数(默认 5)
|
|
117
|
+
--write 配合 rebuild:覆盖已存账本
|
|
118
|
+
--json 机器可读输出
|
|
119
|
+
--quiet 抑制人类可读摘要,只保留退出码
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
退出码:`0` 成功或审计通过,`1` 审计发现真实差异,`2` 用法错误或输入不可读。
|
|
123
|
+
因此它可以挂进定时任务。
|
|
124
|
+
|
|
125
|
+
### 审计能区分两类差异
|
|
126
|
+
|
|
127
|
+
运行中的宿主是按防抖写入账本的,所以活跃会话比文件"新一点"是常态。
|
|
128
|
+
把这种情况报成数据损坏,审计就废了。账本自己的 `updatedAt` 可以裁决:
|
|
129
|
+
|
|
130
|
+
- 有差异的会话,其最新事件**比账本更新** → 只是还在跑 → **通过**,并报出尚未落盘的量;
|
|
131
|
+
- 有差异的会话,其最新事件**早于账本**,或者某条日/模型行与它本该汇总的折叠结果矛盾
|
|
132
|
+
→ **不通过**,退出码 1。
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
audit: match — 1 session(s) advanced after the ledger was written
|
|
136
|
+
(1 calls, 5100 tokens not yet flushed)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 配置
|
|
140
|
+
|
|
141
|
+
按 `id` 覆盖组合条目:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- id: token-ledger
|
|
145
|
+
config:
|
|
146
|
+
ledgerPath: 'D:/dsh/ledger.json' # 默认 <DSH_HOME>/token-ledger/ledger.json
|
|
147
|
+
backfill: false # 默认 true——启动时折叠已存历史
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 它刻意不做的事
|
|
151
|
+
|
|
152
|
+
- **不做定价。** 它只数 token;折算成钱需要一份会过期的价目表。要钱就配一个计费插件。
|
|
153
|
+
- **不注册面向模型的工具。** 工具 schema 会在每次请求上花提示词 token 并改变缓存前缀——
|
|
154
|
+
对一个 token 记账插件来说这很荒唐。人类场景由 `/tokens` 与 CLI 覆盖。
|
|
155
|
+
- **0.1.0 不带 UI。** 宿主端与 CLI 就是契约;浏览器端计划在 0.2 作为纯增量加入。
|
|
156
|
+
|
|
157
|
+
## 兼容性
|
|
158
|
+
|
|
159
|
+
- **Node:** ≥ 22.15.0(CLI 需要解码 Zstandard 帧)。宿主端本身没有版本相关要求。
|
|
160
|
+
- **DSH:** 在 `0.1.2-rc.1` 上验证。所用到的接口面——`ctx.on`、`ctx.inject`、`ctx.get`、
|
|
161
|
+
`ctx.effect`、`commands.register`、`sessionPersistence.list()/inspect()`——在 `0.1.2` 线上一致。
|
|
162
|
+
- **Profile:** 任意。没有 profile 相关代码。
|
|
163
|
+
|
|
164
|
+
## 卸载
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
dsh plugin --profile web remove @chenmiao8563/dsh-token-ledger
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
账本文件是**故意**不删的——要清空历史请自行删除 `<DSH_HOME>/token-ledger/`。
|
|
171
|
+
|
|
172
|
+
## 开发
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npm test # 49 个测试,无需安装任何依赖
|
|
176
|
+
npm run verify # 打包不变式(零依赖、无安装脚本、无裸模块说明符)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`npm test` 使用 Node 内置测试运行器。在禁止逐文件 spawn 子进程的受限环境里,
|
|
180
|
+
改用 `npm run test:single-process`。
|
|
181
|
+
|
|
182
|
+
实际验证了什么、怎么验证的(包括 fork 规则背后的证据)见
|
|
183
|
+
[docs/VERIFICATION.md](docs/VERIFICATION.md)。
|
|
184
|
+
|
|
185
|
+
## 发布
|
|
186
|
+
|
|
187
|
+
npm 现在强制每次发布都要 2FA,所以一个版本的首发必须交互式完成:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm login
|
|
191
|
+
npm publish --access public --otp=<认证器里的 6 位数字>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
首发之后,在 npmjs.com 上给这个包配 **Trusted Publisher**(包 → Settings →
|
|
195
|
+
Trusted Publisher → GitHub Actions,仓库填 `chenmiao8563/dsh-token-ledger`,
|
|
196
|
+
workflow 填 `release.yml`)。OIDC 没法在包存在之前配置,这就是首发必须手动的
|
|
197
|
+
原因。配好之后,打 tag 即可发布,**不需要存放任何 token**:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
git tag v0.1.1 && git push origin v0.1.1
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`release.yml` 会在版本已存在于 registry 时跳过发布步骤、但仍创建 GitHub
|
|
204
|
+
Release,所以重复运行是安全的。
|
|
205
|
+
|
|
206
|
+
## 许可证
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Executable entry point for `dsh-token-ledger`.
|
|
4
|
+
*
|
|
5
|
+
* Kept as a thin wrapper so the CLI logic stays importable and testable.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-token-ledger/bin
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { run } from '../lib/cli.js'
|
|
11
|
+
|
|
12
|
+
process.exitCode = run(process.argv.slice(2))
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Bundle patch for `dsh plugin add @chenmiao8563/dsh-token-ledger`.
|
|
2
|
+
#
|
|
3
|
+
# The package is scoped because npm rejects the unscoped name as too similar to
|
|
4
|
+
# an existing package once separators are normalized away. The plugin's Cordis
|
|
5
|
+
# entry id stays `token-ledger`, and the CLI command stays `dsh-token-ledger`.
|
|
6
|
+
#
|
|
7
|
+
# This adds exactly one entry and touches nothing else: no stock component is
|
|
8
|
+
# disabled or replaced, so the ledger can coexist with any other usage,
|
|
9
|
+
# billing or statistics plugin already in the profile.
|
|
10
|
+
#
|
|
11
|
+
# The row's `id` is what a profile-level override would target, e.g.
|
|
12
|
+
#
|
|
13
|
+
# - id: token-ledger
|
|
14
|
+
# config:
|
|
15
|
+
# ledgerPath: /custom/path/ledger.json
|
|
16
|
+
# backfill: false
|
|
17
|
+
#
|
|
18
|
+
- insert:
|
|
19
|
+
- id: token-ledger
|
|
20
|
+
name: '@chenmiao8563/dsh-token-ledger'
|