dsh-usage
给 DSH Web GUI 加一个 Codex 风格的本机「用量」页:累计/峰值 Token、连续天数、Token 活动热力图、活动洞察(推理强度/模型/技能/任务/会话/回合)、最常用工具。
纯只读、纯本机:只扫描 ~/.dsh/sessions 下的会话日志做统计,不修改任何数据采集,不上传任何数据。Token 数值是 provider 上报 + 启发式计费的估算值,不是账单。
效果
打开 dsh web → 左下角设置 → 导航里出现「用量」(Usage)一节:
- 顶部 5 张卡片:累计 Token 数(附输入/输出/缓存拆分)、今日 Token 数、峰值 Token 数(单次请求)、当前连续天数、最长连续天数
- Token 活动:近 53 周逐日热力图(GitHub 风格),最新日期排在最左(今天无需滚动即可见;未来日期半透明置灰),hover 显示日期与数值;点击任意日期在下方展示当日用量(含占峰值日百分比),再点一次或点「清除选择」取消
- 活动洞察:最常用推理强度、最常用模型、已探索技能、使用的技能总数、任务总数(顶层会话数)、会话总数、回合总数
- 最常用工具:Top 8 工具调用(bash/read/… 带比例条)
- 右上角「刷新」按钮强制重算(
?refresh=1)
秒开设计:浏览器端把上次快照存进 localStorage,打开页面立即渲染、后台刷新;服务端把聚合快照缓存在内存(默认 60 秒窗口内打开直接命中,过期后在后台重算,永不阻塞打开),并对每个会话按日志 revision 做增量折叠——重算只重读变化过的会话,冷启动之后几乎零成本。
架构
两个半脸一个包(dual-face),完全走 DSH 现有扩展机制:
| 半边 | 文件 | 作用 |
|---|---|---|
| host | lib/index.js + lib/aggregate.js |
注入 sessionQuery + webServer,经 ctx.sessionQuery.listSessions()/readSession() 折叠全部持久化会话,在 webServer 上注册一个只读 exact 路由(默认 /usage.json) |
| client | lib/client.js |
浏览器模块加载器格式(window.__ModuleLoader__.load)的 settings 插件,注册 settings.section(id usage),从 /usage.json 拉数据显示 |
client 半是手写的 ModuleLoader 格式包:不依赖 tsdown/rolldown 等 monorepo 工具链,require("react") 走 shell 静态模块表,其余依赖(slots/locale 服务)走 cordis 注入。
安装
本插件是 DSH bundle(package.json 声明 dsh.bundle + dsh.client),通过标准的 dsh plugin 机制安装到 web profile,无需修改 DSH 源码:
dsh plugin --profile web add github:joxmx/dsh-usage
# 或本地 checkout:
dsh plugin --profile web add /path/to/dsh-usage
仓库包含构建产物(lib/ 已提交),安装后无需另外构建。安装后重启 Web UI 并刷新浏览器页面,左下角设置导航里即出现「用量」一节。
升级 / 卸载
dsh plugin --profile web update github:joxmx/dsh-usage
dsh plugin --profile web remove @dsh-external/dsh-usage
配置覆盖
如需覆盖默认配置,在 ~/.dsh/profiles/web/cordis.patch.yml 中对本插件条目追加 config(按 id 替换,host+client 同时生效):
- insert:
- id: dsh-usage
name: '@dsh-external/dsh-usage'
config: { routePath: /usage.json, cacheTtlMs: 5000, heatmapWeeks: 53 }
旧版 README 的 symlink +
watchUserPatches手动挂载方式已过时,改用官方 profile/bundle 模型后无需手动建链;profile patch 变更仍可热应用,但改 host 半边(lib/index.js/lib/aggregate.js)需要重启dsh web(ESM 缓存不会因 config 变更重新 import),client 半在开发时可被 client-hmr 热更。
配置
| 键 | 默认 | 说明 |
|---|---|---|
routePath |
/usage.json |
JSON 路由路径(须以 / 开头) |
cacheTtlMs |
60000 |
快照新鲜窗口;过期后打开立即返回旧快照并后台重算,?refresh=1 等待重算 |
heatmapWeeks |
53 |
热力图周数(4–156) |
统计口径
- 每个会话只计自己产生的事件:fork 会话的日志物理上包含父会话的继承历史(
seq < header.seedLength),这部分已由父会话计费,折叠时跳过,避免重复统计(test/fold-test.mjs有专项断言) - 累计 Token = 每个
assistant/message的inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens之和(reasoning 已含在 output 内,不重复计);今日 Token = 本机当天(按本地时区)同一口径 - 峰值 Token = 单次请求的最大计费量
- 连续天数 = 有 token 活动的本地自然日连续段(今天无活动时从昨天起算)
- 推理强度/模型 =
request/header直方图;技能 =skill工具调用(探索数=去重、使用数=调用次数);任务 = 顶层会话数(无父会话、非 subagent);工具 =tool/call直方图 - 缓存读(cacheReadTokens)按面值计入(与 DSH 聊天统计条口径一致);长会话中它通常占大头——DeepSeek 对缓存命中 token 按折扣计费,若要与账单对比可按折扣折算
测试
node test/fold-test.mjs # 对真实 ~/.dsh/sessions 跑聚合(自带 zstd 多帧解码)
node test/client-smoke.mjs # Node 里加载真实 client bundle,SSR 渲染 loading+ready 分支
限制
- 只统计本机
DSH_HOME下的会话;没有云端账号/跨设备口径 - Token 是估算值(DSH 的 token-meter 口径),不是账单级数字
- 热力图只有「每日」档;「每周/累计」切换未做
- host 半改动需重启
dsh web才生效(client 半热更);全量重算(首次打开/强制刷新)在会话多时约数秒,但不会阻塞页面打开
No comments yet. Be the first to write one.