dsh-usage-stats
English | 中文
DSH 官方桌面版的「使用统计」插件:在设置页新增「使用统计」分区,照 ZCode 开源版界面呈现 Token 用量——五格终身摘要条、GitHub 式热力图、趋势折线、模型用量环形图。数据全部来自本机(实时事件采集 + 历史会话回填),零上传、零新依赖。
- 插件版本:0.2.0(以
package.json为准;变更记录见 CHANGELOG.md) - 适配宿主:DSH 0.2.0-rc.2(web / headless / desktop 三个 profile 均可安装;面板 UI 在 web / desktop 呈现);事件与文件形状按该版本源码级实证
- 分发:GitHub 仓库 tag tarball 渠道(+ 本地 clone 作开发);升级方式见「升级」(DSH 插件不支持自动更新)
- 数据位置:全部在本地
~/.dsh/usage-stats/(默认,可经storageRoot配置),不上传任何远端 - 网络:仅本机 loopback(API 走内核 webServer 同源反代),无遥测零上传
- 前置条件:已安装 DSH(web / headless / desktop 任一 profile);宿主 Node ≥ 22.15(参考环境 24.x;历史回填用到
node:zlib的 zstd 解压);零运行时依赖(无需npm install) - 许可:MIT License(见 LICENSE)
- 路线图:已交付版本与下一版方向见 ROADMAP.md
目录
功能介绍
面板挂在设置页「使用统计」分区(settings.section order 13,排在其它既有分区之后),标题「使用统计」,副题「查看会话活跃度与模型用量的粗略统计。」以下每条都是用户在设置页实际看到的内容。
五格终身摘要条
一排五格:累计 Token 数 / 单日峰值 Token 数 / 最长聊天时长 / 当前连续天数 / 最长连续天数。大数字用短格式(≥1 万显示「x.x 万」,≥1 亿显示「x.x 亿」)。每格悬停给出精确值与口径说明(峰值是哪一天、累计自哪一天起、连续天数怎么算)。口径:
- 峰值 = 单日四桶之和最大的一天;
- 连续天数 = 每天 Token>0 算活跃,今天有用量从今天往回数,今天没有则从昨天往回数。
Token 活动热力图
GitHub 式格子,最近 52 个自然周(周日起始,末列对齐当前周),下方一行月份标签。右上胶囊三档切换:
- 每日:一格一天,五档色阶;悬停(或键盘游标)显示「2026年10月3日 / 1,234 tokens · 5 轮消息」(token 数千分位)。
- 每周 / 累计:整列变为底部对齐的色块柱(高度 = 该周用量对最大周之比,封顶 7 格);悬停显示该周末位日期,外加同口径的两个数——每周档是「本周合计 tokens · 本周轮数」,累计档是「截至此周累计 tokens · 累计轮数」。
趋势折线
时间范围胶囊(标题「趋势与模型用量」):今日 / 近 7 日 / 近 30 日(默认近 30 日)。
- 今日:按 24 个整点小时分桶(横轴「0时」…「23时」);
- 近 7 日 / 近 30 日:一天一个点,窗口内无数据的日补零,折线连续不断轴;
- 每个模型一条线(六色循环,不封顶不归并),手写单调三次插值曲线(平滑且不过冲),Y 轴隐藏;图例可点击隐藏/恢复某条线(隐藏不改变其余线的颜色与量级);
- 悬停出跟随卡片:标题「10月3日 - 12,345 tokens」+ 分模型明细行(0 用量的模型行不显示;超过 8 个模型时只列前 8 行并注「另有 N 个模型未列出」)。
模型用量环形图
中心显示所选时间范围的 Token 总量(短格式),右侧图例两行式:上行 = 模型名 + 占比(≥10% 取整数、<10% 一位小数),下行 = 该模型 token 数(悬停给精确值)。用量前 6 的模型各占一色,超过 6 个模型归并为灰色「其他模型」(该行悬停列出被并入的模型名)。悬停扇区或图例行弹出数据卡片。今日档同样可用(今日分模型用量从今日趋势数据求和得出,数值等价)。
历史回填
装载即自动回填全部历史会话(~/.dsh/sessions 下的多帧 zstd 会话文件逐帧解压 + fork 继承前缀按内核标记剪切去重),增量幂等:已记账的会话秒级跳过;稳态装载绝大多数历史会话零解压(少数「有水位无 mtime」的会话例外,见「已知限制」)。
- 页脚「回填历史」按钮可手动触发:202 受理制(POST 立即返回,任务后台分片执行,不冻结界面);
- 按钮实时显示进度:「回填中…已扫 X/Y 会话」;完成后提示「回填完成:补了 N 个会话、R 条记录」并自动刷新面板;失败显式报错,不会把失败包装成完成;
- 进度接口为
GET /api/dsh-usage-stats/backfill/status(running / startedAt / scanned / total / backfilled / records / lostFrames / finishedAt / error)。
时效感
- 60 秒轻轮询:面板开着时每分钟静默拉一次摘要 + 当前范围的趋势与模型占比(不闪「加载中」,后台标签页跳拍,切回前台立即补一拍,关页面即停),新对话的用量约一分钟内自动上屏;
- 热力图按需刷新:静默拍只在「累计用量或日期变了」时补拉热力图——空闲时零额外请求,数据变了最多 60 秒内格子跟上(拉失败不推进判定基线,下一拍自动重试,卡片顶部会显示「活动数据更新失败,显示为上次数据。」);
- 页脚左端常驻**「更新于 HH:MM」**,本轮每一路数据都取到才推进;有路没取到时标注「(部分数据未更新)」,不会谎报新鲜度。右下「刷新」按钮随时全量重拉。
键盘可达
热力图与趋势折线各只占一个 Tab 停靠点:聚焦后用 ← → ↑ ↓ 移动游标(热力图为「一周 / 一天」,趋势为「一点」),首次按键的游标默认落在今天 / 最新一点(不从一年前起步),Home/End 跳到两端,游标处弹出与鼠标悬停同一张数据卡片(热力图上游标格带描边,缺勤日也看得见落点),焦点离开图表即收起。档位与范围胶囊带 aria-pressed,图表容器带 role="img" 与一句话概述,读屏可读。趋势图例可点击隐藏/恢复某条线(隐藏项置灰划线,配色不重排)。
健壮性(用户可感知的部分)
- 三态:加载中 / 暂无使用数据 / 加载失败+重试;单路数据失败只降级对应卡片并给行内「重试」按钮,不炸整面板;面板渲染异常有兜底横幅,不影响后台采集;
- 空态会自愈:面板停在「暂无使用数据」时,后台一通数据到达后 60 秒内自动变成有数据(无需手动刷新);
- 回填全程可见:打开设置页时若历史正在回填,空位显示「正在回填历史会话…」+「已扫 X/Y 会话」,按钮同步显示进度;跑完自动刷新面板并汇报成果;
- 单写者锁:同时开两个 DSH 实例时,后装载的降级为只读镜像(统计照常可看,不采集不回填),锁空闲后自动接管并补齐增量;
- 深浅双主题跟随官方设置自动切换。
快速开始
安装
两种渠道任选(零运行时依赖,全程无需 npm install):
① GitHub tag tarball(推荐) —— 从仓库 tag 直接安装,锁定版本:
dsh plugin --profile desktop add https://github.com/axuan-1314/dsh-usage-stats/archive/refs/tags/v0.2.0.tar.gz
# profile 可选 web / headless / desktop
② 本地克隆(开发 / 自用) —— 源码改动即时生效,适合改代码:
git clone https://github.com/axuan-1314/dsh-usage-stats.git
dsh plugin --profile desktop add <克隆到本地的仓库路径>
装完验证 entry 入树:dsh --profile desktop --dump-config。
插件自带 cordis.patch.yml,装载后自动生效。
改代码 / 换版本后要跑新代码:web profile 由 HMR 自动重载;desktop / headless 需重启对应实例(桌面端重启应用;headless 每次启动天然取最新)。本地克隆渠道是
link:软链,源码改动无需重新安装。
升级
DSH 的插件管理器不支持自动更新。所以升级 = 换新版本号重跑一次安装命令:
# tag tarball 渠道:把 URL 里的 v0.2.0 换成新版本号(版本号见 Releases / CHANGELOG.md)
dsh plugin --profile desktop add https://github.com/axuan-1314/dsh-usage-stats/archive/refs/tags/v0.2.1.tar.gz
# 本地克隆渠道:git pull 后无需重装(软链生效)
- 数据不受升级影响:统计全部落在
~/.dsh/usage-stats/——在插件包目录之外,卸载 / 重装都不碰它(除非你手动删除该目录)。数据与代码分离是刻意设计。 - 升级后需重启:替换已安装的包后,进程要重启才会加载新 JS 模块(web profile HMR 可自动重载;desktop 重启应用;headless 下次启动)。
验证生效
- 重启 DSH,打开设置页——分区列表出现「使用统计」;
- 首次装载会后台自动回填全部历史会话,面板可能先显示「暂无使用数据」,属正常(历史越多耗时越长,大历史需几分钟);
- 点页脚「回填历史」看进度:受理后按钮显示「回填中…已扫 X/Y 会话」,跑完显示成果并自动刷新面板(若自动回填仍在进行,会提示「已有一次回填在进行中,正在跟踪进度…」并转入进度跟踪);
- 数据上屏:摘要条与热力图出现全部历史数据;
- 也可直接查本机 loopback API:
GET /api/dsh-usage-stats/summary。
数据与隐私
- 数据只在
~/.dsh/usage-stats/:records-YYYY-MM.jsonl笔级增量流水(按月轮转,唯一真源)、daily/YYYY-MM-DD.json日聚合缓存(可随时从流水全量重建)、backfill-marker.json回填游标、lock单写者锁。 - 唯一网络路径是本机 loopback(API 走内核 webServer 同源反代,loopback + 只读方法守卫——读路由放行 GET/HEAD,仅
/backfill受理 POST),无遥测零上传。 - 采集照抄官方 tokenUsage 投影折叠语义(
@deepseek-ai/dsh-token-meter),可与官方 trajectory「用量」tab 逐项对账。 - 口径:四桶之和(uncachedInput + output + cacheRead + cacheWrite);
uncachedInputTokens= 原始inputTokens(官方桶名如此)。 - 配置键:
enabled(默认 true)/storageRoot/sessionsRoot,未知键报错拒绝。
回滚与卸载
- 停功能不卸包:把 profile 的
cordis.patch.yml(或 bundle patch)里enabled改为false。生效方式分 profile:web profile 由 HMR 自动生效;desktop / headless 改 config 后需重启对应实例。不存在「改完即热生效」的统一行为。 - 整包卸载:
dsh plugin --profile desktop remove dsh-usage-stats。 - 清数据:删
~/.dsh/usage-stats/(自有目录,不影响其他组件)。
FAQ
数据从哪来?
两路合一本账:实时一路来自宿主 session/event 事件流(照官方折叠语义逐事件产出净增量落盘);历史一路来自 ~/.dsh/sessions 会话文件回填(多帧 zstd 逐帧解压;fork 会话按内核 session/end-seed{inherited:true} 标记剪掉继承前缀,不重复计父会话)。两路共用同一折叠状态机与记账水位,不互相双计。
数字为什么能和官方 trajectory「用量」对上? 折叠语义逐位照抄官方 tokenUsage 投影(替换槽 LWW + retry 结转清槽),增量流求和恒等于官方 totals。在参考环境的全量对账中逐条一致,3000 轮随机事件流对官方投影零失配。
重启 / 升级插件后会不会重算、重复计数? 不会重算:流水是唯一真源,回填按会话水位只补增量,文件未变的会话零解压跳过。不会双计:v0.1.6 架构重构时做过新旧对照(数十个会话的记录逐位一致,旧版快照升级零追加)。
性能如何?会不会卡界面? 回填 async 分片(每 4 个会话让出一拍),解压不冻结宿主;稳态装载绝大多数历史会话零解压(mtime 哨兵;少数「有水位无 mtime」会话除外)。仅首次装载需解压全部历史,大历史需几分钟但期间可正常使用。面板开着时每 60 秒只发两三个轻请求(今日档两路:摘要+趋势;近 7/30 日三路:摘要+趋势+模型占比;热力图不随每拍底座请求轮询,仅在累计用量或日期变化时静默补拉)。
开两个 DSH 窗口会怎样? 单写者锁保证只有一个实例采集;另一个是只读镜像——统计面板照常能看,锁空闲后自动接管并补齐镜像期增量。镜像期点「回填历史」会提示「当前是只读镜像(另一窗口持写锁),接管后会自动补齐,无需手动回填」。
想清空统计数据怎么办?
退出 DSH 后整体删除 ~/.dsh/usage-stats/ 即可;下次装载按首次安装全量重新回填。
开发测试
npm test # node --test,110 用例(fold 语义 / 存储与聚合 / 接口形状 / 集成回归 / 客户端纯函数)
npm run check
- 代码五个文件:
lib/index.js(host 接线 + 回填)、lib/fold.js(折叠状态机)、lib/store.js(落盘 + 单写者锁)、lib/aggregate.js(聚合纯函数)、lib/client.js(设置页 UI)。 - API:
GET /api/dsh-usage-stats/summary、/heatmap?days=(默认 366,0=全量)、/trend?days=1|7|30、/models?days=7|30、/backfill/status,以及POST /backfill(202 受理制)。 - 调试日志:
DSH_USAGE_STATS_DEBUG=1时输出到 stderr。
实现细节见 SPEC.md。
已知限制
已知限制如下:
- live 折叠槽在内存,重启即弃:替换窗横跨进程重启时旧样本极罕见双计;
- 回填路径 attempt 样本的模型归属依赖会话内
request/header前置,缺失时记 unknown(参考环境中 0 条 unknown); - 最长聊天时长按(会话, 日)分桶取极差,跨日会话按日截断,是近似口径;
- 少数「有水位无 mtime」的历史会话(生产环境中少数会话如此)每次装载仍会解压(约 0.7~4 秒,可容忍);
- 多帧 zstd 帧边界靠魔数扫描,理论上极低概率误报(在大量帧样本上零误报;解压失败帧跳过并计数留痕);
- 热力图固定回看 52 自然周,更早的用量只计入摘要条等全期口径,不出现在格子里;
- 热力图「每周/累计」档的悬停卡片取该周末位日期(周六)与周合计,是既有口径(照 ZCode);
- 趋势悬停卡片最多列 8 个模型行(超出注「另有 N 个模型未列出」)——单日模型数很多时卡片不再无限长。
相关项目
- dsh-keepsake —— 同为 DSH(DeepSeek Harness)生态的本地优先插件:跨会话记忆留档,本地 markdown 落盘、自动提取 + 注入、零依赖 + 管理面板。一个记「用了多少」,一个记「聊了什么、定过什么」;两者互不依赖,可各自单独安装。
No comments yet. Be the first to write one.