dsh-token-use 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/LICENSE +21 -0
- package/README.en.md +105 -0
- package/README.md +105 -0
- package/client/client.js +428 -0
- package/cordis.patch.yml +7 -0
- package/lib/index.js +727 -0
- package/lib/scan-worker.js +12 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 huangyuheng
|
|
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.en.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# dsh-token-use
|
|
2
|
+
|
|
3
|
+
[中文](README.md) | English
|
|
4
|
+
|
|
5
|
+
[](https://awesome-dsh-plugin.com)
|
|
6
|
+
|
|
7
|
+
A real-time token usage plugin for DeepSeek Harness: install it, then read your usage in **Settings → Token usage** (the plugin adds its own top-level Settings entry). The page refreshes every 5 seconds and stays quiet while the tab is hidden — no wasted polling.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Totals card** — total (input + output + cache), input, output, cache read, cache write, reasoning, calls.
|
|
12
|
+
- **Range tabs** — by day / by month / all, with a date or month picker; defaults to by day = today.
|
|
13
|
+
- **Model filter** — the dropdown beside the range tabs narrows every breakdown to one model (including that model's project and per-day rows); defaults to all models.
|
|
14
|
+
- **Trend chart** — five series (total, input, output, cache read, calls) over the last 30 days, drawn with a tree-shaken ECharts bundle that ships inside the plugin (no CDN). The token series share the left axis, calls use the right one, and the axis tooltip shows every dimension for the hovered day. Rendering is imperative, so moving the mouse never re-renders React.
|
|
15
|
+
- **Unit switch** — 亿 / 万 / 千 in Chinese, B / M / K in English; remembered per browser.
|
|
16
|
+
- **Detail tables** — by model, by day and by project, each with a total column.
|
|
17
|
+
|
|
18
|
+
## Why it is worth installing
|
|
19
|
+
|
|
20
|
+
You are burning tokens, but you cannot say where: which project costs the most, which model leans hardest on the cache, how much this week grew over last week.
|
|
21
|
+
|
|
22
|
+
**dsh-token-use turns that into numbers you can read at a glance.** It folds every call as it happens, then lays totals, input, output, cache hits, reasoning and call counts out in the settings page, with filters by model, day, month or project and a smooth trend line that tells the story. Install it and you are done: nothing to configure, no session restart, and it never pops up while you are coding.
|
|
23
|
+
|
|
24
|
+
The most expensive cost is the one you cannot see — make it visible.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
dsh plugin --profile web add github:huangyuheng/dsh-token-use
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Restart `dsh web` afterwards. To install from an unpacked zip instead:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
dsh plugin --profile web add /path/to/dsh-token-use
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Performance design
|
|
41
|
+
|
|
42
|
+
- The host side **never polls, never writes to disk and sets no timers**: it folds usage in O(1) increments from the `session/event` bus (one dictionary addition per `assistant/message`).
|
|
43
|
+
- History is rebuilt **once** at boot by streaming `$DSH_HOME/sessions/**/session.jsonl.zstd` (native zstd from `node:zlib`), yielding the event loop between files (`scheduler.yield()`) so session handling is never blocked. A per-session sequence watermark de-duplicates the rebuild against live events, whatever order they arrive in.
|
|
44
|
+
- One read-only JSON endpoint is exposed: `GET /dsh-token-use` (loopback only, in-memory snapshot, `no-store`).
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
# everything
|
|
48
|
+
curl http://127.0.0.1:3080/dsh-token-use
|
|
49
|
+
# a date / a month
|
|
50
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'
|
|
51
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?day=2026-09-10'
|
|
52
|
+
# a model (combinable with day/month)
|
|
53
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?model=deepseek-v4-flash'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pnpm install # development only (esbuild + echarts)
|
|
60
|
+
pnpm run build # regenerate client/client.js (= tree-shaken ECharts + client/src.js)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`client/client.js` is a committed build artifact, so users install nothing and build nothing.
|
|
64
|
+
|
|
65
|
+
npm metadata (`repository` / `files` / `LICENSE`) is ready but **publishing is deferred**: the plugin currently installs from GitHub (`dsh plugin --profile web add github:huangyuheng/dsh-token-use`). Run `npm publish` whenever that changes.
|
|
66
|
+
|
|
67
|
+
## Field definitions
|
|
68
|
+
|
|
69
|
+
- `input` / `output` — input and output tokens as reported by the API.
|
|
70
|
+
- `cacheRead` / `cacheWrite` — prompt cache read/write tokens (the API counts cache reads on the input side for billing).
|
|
71
|
+
- `reasoning` — reasoning tokens.
|
|
72
|
+
- Model attribution — the model of the session's most recent `request/header`; small calls that carry no usage record (title generation, for example) are not counted.
|
|
73
|
+
- Project attribution — the `cwd` in the session's creation header (live events read `session.header.cwd`, history reads the log header); subagent and forked sessions inherit their parent's project; usage that cannot be attributed yet is held and back-filled as soon as it resolves, only reaching `(no cwd)` after 30 seconds. |
|
|
74
|
+
|
|
75
|
+
## Compatibility
|
|
76
|
+
|
|
77
|
+
These differences are already handled before anyone else installs the plugin:
|
|
78
|
+
|
|
79
|
+
| Dimension | Notes |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| Runtime | Requires **dsh web ≥ 0.1.0-rc.6** (the settings sidebar `settings.section` slot). The host half uses Node built-ins only; zstd decoding uses `node:zlib` (built in from Node 22.15, and dsh itself requires ^22.19 \|\| >=24, so it is always present). |
|
|
82
|
+
| Data directory | Resolved from `$DSH_HOME` (environment variable, defaulting to `~/.dsh`), matching dsh's own `dsh-home-paths` rules; a custom home works too. The plugin is **read-only** — it writes nothing. |
|
|
83
|
+
| Session formats | Handles `session.jsonl[.zstd]` (multi-frame zstd with checksums), the versioned next generation `session.v<N>.jsonl[.zstd]` that dsh leaves alongside the old file after a migration, and plaintext `.jsonl`. A session is read from its **highest-version generation only**, so a migration never double counts; `.bak`, `.corrupt-*` and `session.lock` are skipped, and a single corrupt frame only raises `scan.skipped`. |
|
|
84
|
+
| Directory layout | Follows the official JSONL persistence layout `sessions/<project dir>/<session dir>/`, reading every session independently. |
|
|
85
|
+
| Accounting | Some providers (the pi-ai adapter, for instance) fold reasoning tokens into output, so a `reasoning` column of 0 is normal there; calls that record no `usage` (title generation, web search) are not counted; model attribution uses the session's most recent `request/header`. |
|
|
86
|
+
| Network | The endpoint is **loopback-only** by default. On a LAN deployment (trustedHosts configured) set `allowRemote: true` in the profile patch; it still accepts same-origin requests only, and the client names the reason when it sees a 403. |
|
|
87
|
+
| Performance | The history rebuild runs on a **worker thread**, so the host event loop is never blocked; events arriving meanwhile are buffered and replayed after the scan, with the session sequence watermark keeping the two folds distinct. Only event increments happen afterwards. |
|
|
88
|
+
| Multiple instances | Every `$DSH_HOME` is counted separately; several profiles under one home share the `sessions` directory, so their usage is merged. |
|
|
89
|
+
|
|
90
|
+
## Configuration (overridable in cordis.patch.yml)
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
- id: dsh-token-use
|
|
94
|
+
name: 'dsh-token-use'
|
|
95
|
+
config:
|
|
96
|
+
endpoint: /dsh-token-use
|
|
97
|
+
scanAtBoot: true # false counts only usage recorded after the plugin starts
|
|
98
|
+
allowRemote: false # set true for LAN (non-loopback) access; same-origin only
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## After installing
|
|
102
|
+
|
|
103
|
+
1. Restart `dsh web` (a bundle membership change only takes effect on restart);
|
|
104
|
+
2. Open **Settings → Token usage**;
|
|
105
|
+
3. Or from the command line: `curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'`.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# dsh-token-use
|
|
2
|
+
|
|
3
|
+
[English](README.en.md) | 中文
|
|
4
|
+
|
|
5
|
+
[](https://awesome-dsh-plugin.com)
|
|
6
|
+
|
|
7
|
+
DeepSeek Harness 实时 Token 用量插件:安装后在 **设置 → Token 用量** 查看用量(自带设置侧边栏一级入口),页面每 5 秒刷新(页面隐藏时暂停,零轮询浪费)。
|
|
8
|
+
|
|
9
|
+
## 功能
|
|
10
|
+
|
|
11
|
+
- **总量卡片**:总计(输入+输出+缓存)、输入、输出、缓存读、缓存写、推理、调用次数。
|
|
12
|
+
- **范围筛选**:按天 / 按月 / 全部三个 tab,默认「按天=今天」;可选日期、月份。
|
|
13
|
+
- **模型筛选**:时间筛选旁的下拉框按模型过滤(含该模型的项目/按天维度明细),默认全部模型。
|
|
14
|
+
- **用量趋势折线图**:基于 ECharts(按需打包、随插件离线分发,不依赖 CDN)展示最近 30 天总计、输入、输出、缓存命中、调用次数五条折线;token 维度共用左轴,调用次数用右轴,悬停轴提示显示当日全部明细。图表用命令式渲染,鼠标移动不触发 React 重渲染。
|
|
15
|
+
- **单位切换**:中文(亿 / 万 / 千)与英文(B / M / K)一键切换,记忆选择。
|
|
16
|
+
- **明细表**:按模型、按日期、按项目三张表,含总计列。
|
|
17
|
+
|
|
18
|
+
## 为什么值得装
|
|
19
|
+
|
|
20
|
+
你在烧 token,但你说不清烧在哪:哪个项目最贵、哪个模型最吃缓存、这周比上周涨了多少。
|
|
21
|
+
|
|
22
|
+
**dsh-token-use 把这些变成一眼看得懂的数字。** 它跟着每一次调用实时累加,把总量、输入、输出、缓存命中、推理和调用次数摊开在设置页里,按模型、按天、按月、按项目随手切换,趋势用一条平滑曲线讲清楚。装完即用:不用配置、不用重启会话、不会在你写代码的时候跳出来打扰你。
|
|
23
|
+
|
|
24
|
+
看不见的成本最贵——把它变成看得见的。
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
## 安装
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
dsh plugin --profile web add github:huangyuheng/dsh-token-use
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
重启 `dsh web` 后生效。下载 zip 解压后的本地目录安装:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
dsh plugin --profile web add /解压路径/dsh-token-use
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 性能设计
|
|
41
|
+
|
|
42
|
+
- 宿主侧 **不轮询、不写盘、不加定时器**:通过 `session/event` 事件总线做 O(1) 增量累加(每条 `assistant/message` 一次字典加法)。
|
|
43
|
+
- 启动时做 **一次性**历史重建:流式解压 `$DSH_HOME/sessions/**/session.jsonl.zstd`(`node:zlib` 原生 zstd),每读一个文件主动让出事件循环(`scheduler.yield()`),不阻塞会话处理;重建与实时事件用「会话 seq 水位」去重,任意先后顺序都不会重复计数。
|
|
44
|
+
- 只暴露一个只读 JSON 接口 `GET /dsh-token-use`(仅回环可访问,内存快照,`no-store`)。
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
# 全部
|
|
48
|
+
curl http://127.0.0.1:3080/dsh-token-use
|
|
49
|
+
# 指定日期 / 月份
|
|
50
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'
|
|
51
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?day=2026-09-10'
|
|
52
|
+
# 指定模型(可与 day/month 组合)
|
|
53
|
+
curl 'http://127.0.0.1:3080/dsh-token-use?model=deepseek-v4-flash'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 开发
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pnpm install # 仅开发需要(esbuild + echarts)
|
|
60
|
+
pnpm run build # 重新生成 client/client.js(= 精简 ECharts + client/src.js)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`client/client.js` 是已提交的构建产物,使用者无需安装依赖或构建。
|
|
64
|
+
|
|
65
|
+
npm 发布元数据(`repository` / `files` / `LICENSE`)已备好但**暂不发布**:当前只通过 GitHub 安装(`dsh plugin --profile web add github:huangyuheng/dsh-token-use`)。需要时执行 `npm publish` 即可。
|
|
66
|
+
|
|
67
|
+
## 字段口径
|
|
68
|
+
|
|
69
|
+
- `input` / `output`:API 上报的输入/输出 tokens。
|
|
70
|
+
- `cacheRead` / `cacheWrite`:提示词缓存读/写 tokens(API 计费口径中缓存读也计入输入侧)。
|
|
71
|
+
- `reasoning`:推理 tokens。
|
|
72
|
+
- 模型归属:该会话最近一次 `request/header` 的 model;标题生成等无 usage 记录的小调用不在统计内。
|
|
73
|
+
- 项目归属:取会话创建头里的 `cwd`(实时事件读 `session.header.cwd`,历史扫描读日志头);子会话/分叉会话继承父会话的项目;极少数暂时无法归属的调用先挂起,归属明确后自动回填,30 秒仍未明确才记入 `(no cwd)`。
|
|
74
|
+
|
|
75
|
+
## 兼容性
|
|
76
|
+
|
|
77
|
+
给他人安装前,这些差异都已处理:
|
|
78
|
+
|
|
79
|
+
| 维度 | 说明 |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| 运行环境 | 需要 **dsh web ≥ 0.1.0-rc.6**(设置侧边栏 `settings.section` 槽位)。宿主侧只用 Node 内置模块;zstd 解压依赖 `node:zlib`(Node ≥ 22.15 内置,dsh 自身要求 ^22.19 \|\| >=24,故必然满足)。 |
|
|
82
|
+
| 数据目录 | 按 `$DSH_HOME`(环境变量,缺省 `~/.dsh`)解析,与 dsh 官方 `dsh-home-paths` 规则一致;自定义 home 同样有效。插件**只读**,不写任何文件。 |
|
|
83
|
+
| 会话格式 | 同时支持 `session.jsonl[.zstd]`(多帧 zstd,含 checksum)、带版本号的新一代 `session.v<N>.jsonl[.zstd]`(dsh 迁移后会与旧文件并存),以及明文 `.jsonl`;同一会话**只读最高版本那一代**,不会重复计数;`.bak`/`.corrupt-*`/`session.lock` 自动跳过,个别损坏帧只计入 `scan.skipped`。 |
|
|
84
|
+
| 目录布局 | 兼容官方 JSONL 持久层的 `sessions/<项目目录>/<会话目录>/` 结构;会话按项目目录、会话 ID 独立读取。 |
|
|
85
|
+
| 统计口径 | 部分 provider(如 pi-ai 适配)会把推理 token 并入输出,此时「推理」列为 0 属正常;标题生成、联网搜索等不落 `usage` 的调用不计入;模型归属取该会话最近一次 `request/header`。 |
|
|
86
|
+
| 网络访问 | 接口默认**仅回环**。局域网部署(配置了 trustedHosts)时在 profile patch 里开 `allowRemote: true`,且仍只接受同源请求;客户端遇到 403 会直接提示原因。 |
|
|
87
|
+
| 性能 | 历史重建在 **Worker 线程**执行,不阻塞宿主事件循环;期间新事件先缓冲、扫描完成后回放,靠会话 seq 水位去重。之后只做事件增量。 |
|
|
88
|
+
| 多实例 | 每个 `$DSH_HOME` 独立统计;同一 home 下的多个 profile 共享 `sessions` 目录,统计会合并显示。 |
|
|
89
|
+
|
|
90
|
+
## 配置(cordis.patch.yml 可覆盖)
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
- id: dsh-token-use
|
|
94
|
+
name: 'dsh-token-use'
|
|
95
|
+
config:
|
|
96
|
+
endpoint: /dsh-token-use
|
|
97
|
+
scanAtBoot: true # false 则只统计插件启动之后的实时用量
|
|
98
|
+
allowRemote: false # 局域网(非回环)访问时设为 true,仅接受同源请求
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 安装后
|
|
102
|
+
|
|
103
|
+
1. 重启 `dsh web`(bundle 成员变化必须重启才生效);
|
|
104
|
+
2. 打开 **设置 → Token 用量**;
|
|
105
|
+
3. 也可以用命令行:`curl 'http://127.0.0.1:3080/dsh-token-use?month=2026-09'`。
|