Back to home@joxmx

dsh-usage

DSH 用量页:本机跨会话 Token 使用统计(Codex 风格),纯只读、不出本机 | Local cross-session token usage statistics for the DSH Web GUI

Stars
0
Language
JavaScript
Created
Aug 18, 2026
Updated
Aug 18, 2026
GitHub repo

Introduction

dsh-usage

Version License: BSD-3-Clause DSH profiles

给 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 现有扩展机制:

半边文件作用
hostlib/index.js + lib/aggregate.js注入 sessionQuery + webServer,经 ctx.sessionQuery.listSessions()/readSession() 折叠全部持久化会话,在 webServer 上注册一个只读 exact 路由(默认 /usage.json
clientlib/client.js浏览器模块加载器格式(window.__ModuleLoader__.load)的 settings 插件,注册 settings.section(id usage),从 /usage.json 拉数据显示

client 半是手写的 ModuleLoader 格式包:不依赖 tsdown/rolldown 等 monorepo 工具链,require("react") 走 shell 静态模块表,其余依赖(slots/locale 服务)走 cordis 注入。

安装

本插件是 DSH bundlepackage.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.jsonJSON 路由路径(须以 / 开头)
cacheTtlMs60000快照新鲜窗口;过期后打开立即返回旧快照并后台重算,?refresh=1 等待重算
heatmapWeeks53热力图周数(4–156)

统计口径

  • 每个会话只计自己产生的事件:fork 会话的日志物理上包含父会话的继承历史(seq < header.seedLength),这部分已由父会话计费,折叠时跳过,避免重复统计(test/fold-test.mjs 有专项断言)
  • 累计 Token = 每个 assistant/messageinputTokens + 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 半热更);全量重算(首次打开/强制刷新)在会话多时约数秒,但不会阻塞页面打开