跳到主要内容

dsh-token

已验证

@zoytown/dsh-token · v0.1.3 · MIT · Web 界面

DeepSeek Harness (dsh) plugin for machine-wide token usage statistics — a Settings page with totals, a contribution heatmap, streaks, and a per-model breakdown. Reads session logs from every dsh home; no cost estimates.

安装

dsh plugin add @zoytown/dsh-token

dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南

源码

标签

说明文档

@zoytown/dsh-token

English | 中文

@zoytown/dsh-token 是一个 DeepSeek Harnessdsh)插件,统计本机全部 token 用量。它把每一个 dsh home——~/.dsh 以及各个 ~/.dsh_desktop/<version>——的会话日志折叠成设置里的一个 Token 页:总量指标卡、GitHub 式贡献热力图、当前与最长连续天数、高峰时段,以及按模型拆分的四个互斥 token 桶。它不注册任何模型可见的工具不追加任何会话事件,所以挂上它对对话本身零开销。

DeepSeek Harness 设置对话框里的 Token 页:总览 / 模型 切换与 全部 / 30天 / 7天 区间按钮,指标卡显示会话数 2、消息数 17、总 tokens 499K(含缓存读)、活跃天数 2、当前与最长连续各 2 天、高峰时段 0:00、常用模型 deepseek-v4-pro,下方是 2025-08-17 至 2026-08-22 的 GitHub 式贡献热力图

展示什么

  • 会话数消息数总 tokens活跃天数当前 / 最长连续天数高峰时段常用模型
  • 最近 53 周的贡献热力图;选 7 天 / 30 天时改为单行日条
  • 模型视图:把每个模型的用量拆成输入 / 缓存读 / 缓存写 / 输出

全程本地、只读:不写任何会话,不联网,且只答复 loopback 调用方——载荷里含工作目录路径,等于一份你的项目清单。

刻意不展示什么

费用。 harness 里根本没有价格表——@deepseek-ai/dsh-llm-pi-aiNO_COST 写死,其源码注释直言 harness 从不读 provider 的 cost 元数据。在这里给出任何金额,都只是把本地猜测包装成事实。账户余额是另一件事,由 @zoytown/dsh-billing 负责。

安装

dsh plugin --profile web add @zoytown/dsh-token

需要 Node ^22.19 || >=24,且 PATH 上有 pnpmdsh plugin 是 pnpm 的薄转发层)。装完打开 设置 → Token

PATH 上没有全局 dsh?两种写法都行:

npx -y @deepseek-ai/dsh plugin --profile web add @zoytown/dsh-token   # 发布版 CLI
pnpm dsh plugin --profile web add @zoytown/dsh-token                  # 在 harness 检出目录里

选一种并固定下来——两个 CLI 每次启动都会把 <DSH_HOME>/profiles/node_modules 那个软链农场重新指向自己那份安装。

卸载:

dsh plugin --profile web remove @zoytown/dsh-token

不支持桌面壳

本插件面向 dsh web。包装 harness 的 Electron 壳可能把裸模块解析锚定在自己的 bundle 上,导致装进 profile 的插件解析不到——而且整棵插件树会直接加载失败而不是降级,App 起不来。不要把本插件装进这类壳的 DSH_HOME。也不需要:记录在 ~/.dsh_desktop/<version>/ 下的会话,从 dsh web 一样读得到。

配置

写在 profile 的 cordis.patch.ymldsh-token 那一行下:

默认值 含义
extraSessionRoots [] 额外扫描的 dsh home 目录。自动发现覆盖 ~/.dsh~/.dsh_desktop/<version>;曾经通过某个 $DSH_HOME 用过、但现在没有设置的 home 需要写在这里。
includeCompaction true 是否计入压缩摘要消耗的 token——那是真实花费,而 harness 自带的 tokenUsage 投影看不见它。设为 false 可与之 1:1 对账。
refreshIntervalMs 30000 重新扫描新增会话的间隔。
indexChunkYieldMs 16 扫描时让出事件循环的间隔。

口径说明

规则比代码更重要,所以明写在这里:

  • 总 tokens 含缓存读。 provider 报的四个桶互斥,相加就是真实吞吐量。模型页把它们拆开——一个被缓存读支配的总量,应该一眼能看出来。
  • 会话数不含子会话,但子会话的 token 计入总量。 委派出去的子会话不是你开的对话,它的数量以副标题单列。
  • 消息数 = 人类消息 + 非空助手消息,不是日志条数。单个步骤会产生上百条流式增量事件,按条数统计会差 2–3 个数量级。
  • 同一步的用量会上报两次——流式期间一次、最终消息上一次。样本按 (turn, step) 键控并替换,绝不累加,与 harness 自己的投影一致。
  • 日 / 小时按本机日历分桶,用 Intl 而不是 UTC 毫秒除法;连续天数按正午锚点判定,所以夏令时切换不会误判为断签。
  • 重试步骤会少算。 一个步骤内重试时,日志只保留最终那份用量报告;失败那次已经计费,但数字已经不在日志里。

计量覆盖率、重试步骤数、仍在写入的会话、被跳过的日志,全部在页面底部诚实披露,而不是悄悄吸收掉。

实现要点

读路径上做全量扫描不可行:折叠约 1,200 个会话 / 约 319 MB 压缩日志要 4 秒左右,且成本是帧数绑定而非字节绑定,换个更快的解压器也解决不了。所以 host 半在后台折叠一次,之后只读新追加的字节,靠 (dev, ino, size, mtime) 判定新鲜度。浏览器半永远看不到日志字节——它渲染的是从一条 loopback JSON 路由取回的 view model。

会话日志是多个独立可解码的 zstd 帧拼接而成的容器,这既是 Node 自带 zstd API 读不了它的原因(只能解出第一帧),也是从存储的字节游标处续读成立的原因。

完整架构与每条计数规则的上游出处见 DESIGN.md

开发

npm install
npm run typecheck
npm run test
npm run build
npm run test        # 再跑一次:产物契约测试只在构建之后才真正执行

工作规则见 AGENTS.md

许可证

MIT