HwFee/HwFee-dsh

usage-stats

空白会话首页的用量面板:会话数、消息数、总 token、活跃天数、高峰时段、常用模型与热力图,范围可选全部、30 天或 7 天。

用量与计费deepseek-harnessdshdsh-market

@hwfee/dsh-usage-stats

DeepSeek Harness (DSH) 的本地会话用量看板插件。

装上之后,每次打开新对话空白页,问候语下方会多出一张统计卡片:看看你一共开了多少次会话、发了多少条消息、烧了多少 Token、最常在几点干活、最爱用哪个模型,外加一张活跃度热力图和一句按经典小说字数折算的对比。

所有数据都从你本机 ~/.dsh/sessions 下的会话日志里聚合,纯本地计算,不发任何外部请求。

安装与卸载

在命令行运行,或在 DSH 的 设置 → 插件 → 添加插件 里直接输入 @hwfee/dsh-usage-stats:

dsh plugin --profile desktop add @hwfee/dsh-usage-stats

装完或卸完后必须彻底退出并重开 DSH。

卸载:

dsh plugin --profile desktop remove @hwfee/dsh-usage-stats

看板里有什么

  1. 两个标签页 + 三个时间范围
  • 左上角切换 「总览」 和 「模型」(按 Token 消耗从高到低列出每个模型的用量和占比条)。
  • 右上角切换 「全部 / 30 天 / 7 天」,实时重算六张卡片和模型列表的数字。
  1. 六张核心指标卡
  • 会话数(只算真正发过 user/message 的会话,空会话不算;已删除的会话仍然算,见下文第 3 条)、消息总数、总 Token、活跃天数、高峰时段(12 小时制)、最常用模型。
  1. 活跃度热力图
  • 7 行 × N 列,右下角那一格永远锚定「今天」,从今天往前回溯填满整周,不会出现缺半截的尾列或未来日期。鼠标悬停在格子上会显示当天的日期与消息数。
  1. 小说字数折算
  • 底部每次打开新会话随机抽一本书(《红楼梦》73 万字、《三国演义》63 万字、《围城》23 万字、《活着》12 万字、《骆驼祥子》10 万字),算算你用掉的 Token 大概能印几本。挂载后固定,切换时间范围不会乱跳。

设置项(高度与数字单位)

如果同时装了 dsh-cds-theme,下面这两行设置会自动出现在 设置 → 主题定制 最底部;没装主题插件时,它们会自动落回 设置 → 通用设置:

  • 用量面板高度:滑块 240–520 px(步进 4px,默认 344px)。调小高度时,总览页会按「先缩热力图格子 → 再收间距 → 最后微调卡文字号」的顺序等比缩放,绝不会出现竖向滚动条;总览和模型两个标签页共用同一个高度,切换标签时底下的输入框不会上下乱跳。
  • 数字单位:开关切换 万/亿(1.2万、1.23亿)或 K/M/B(1.2K、123M),默认跟随界面语言(中文用万/亿,英文用 K/M/B)。

几个容易踩坑的实现细节

1. 为什么要自己按魔数切 zstd 帧

DSH 的会话日志(*.zstd)是多个 zstd 压缩帧首尾直接拼接写进去的。如果直接拿 Node 自带的 zlib.zstdDecompressSync 去解整个文件,它只会解出第一帧,后面追加的所有对话都会被静默丢掉,算出来的 Token 少一大截。

lib/usage-store.js 的做法是按 zstd 帧魔数(28 B5 2F FD)把文件切开,逐帧解压后再按行解析 JSON。

2. 为什么第一次打开要 2 秒,之后只要 20 毫秒

百来个会话文件大约有 50 MB 压缩数据、两万多个 zstd 帧。第一次冷启动全量解压需要约 2 秒;解析完后,每个文件的摘要会按 (路径, mtimeMs, size) 持久化写进 ~/.dsh/cache/usage-stats/(故意不放系统临时目录,防止被 Windows 存储感知定期清掉)。之后每次重启 DSH 并在后台分批预热,打开新会话几乎是瞬开。

3. 为什么删了会话,统计不会变少

看板的数字不是「磁盘上现在有什么」,而是一本累计账本:每个会话一行,存在 ~/.dsh/cache/usage-stats/ledger-.json。会话继续写的时候整行替换,日志被删掉时行不删。

  • 归档:只往宿主的 workspace.json 里加一个 id,日志原地不动——有没有账本都会被统计。
  • 删除:真的删掉 ~/.dsh/sessions/...//session.v4.jsonl.zstd,只有账本还记得它。
  • 数字因此只增不减。想清零就删掉 ledger-.json(下一次打开会从现存日志重建);?refresh=1 只重读日志,不动账本。
  • 账本只存计数(消息数、Token、按天/小时/模型),不存提示词、不存路径。

4. 为什么完全跟随宿主令牌

面板样式全部使用 --dsw-alias-* 和 --dsw-radius-* 宿主变量,不写死任何色值。不管你用 DSH 原生明暗主题,还是装了 dsh-cds-theme 切陶土橙、调圆角,看板都会自动跟着变。

面板的每一层都是半透明的,空白页的海面动画能从面板、卡片和热力图后面透出来:外壳 52%、热力图底 20%,热力格按活跃度 12/22/32/42% 递进(最热的一格也仍然透光)。六张指标卡只铺 6% 的淡色底纹,靠内描边(.5px border-l2,用 inset 阴影画,不占布局)区分彼此——六格清清楚楚分得开,同时不挡后面的画面。文字不跟着变淡:只是底色透,字还是宿主原色。

本地开发与测试

npm ci           # 仅安装开发期打包工具 esbuild,零运行时依赖
npm run build    # 编译客户端产物:client/src/* -> client/client.js
npm test         # 产物新鲜度检查 + 清单契约 + 8 个测试文件(条数以输出为准)

详细的接口与缓存设计契约见仓库根目录的 docs/usage-stats-contract.md。