DSH 本地 Token 用量统计插件——以 Token 为第一视角:不只回答"花了多少钱、用了多少量",还回答…
30/100
综合分
1
GitHub 星标
216
npm 周下载
—/5
用户评分
数据截至 2026/9/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @fufuf-c/dsh-tokennpm 源 · @fufuf-c/dsh-token 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 本地Token用量统计
- 成本面板与预算
- 按会话下钻筛选
DSH 本地 token 用量与成本面板:4 段 token 明细、缓存节省、按会话下钻、全局筛选、月度预算。零上传。
最近更新
在 GitHub 查看全部 →v0.9.62026-09-22
主题:修 dsh 0.1.7-alpha.1 上"插件时好时坏、打开面板要等很久才出数"的根因 —— 宿主把遗留会话的 revision 换成了"文件身份 + 整库语料哈希",而本插件用 revision 当水位线,于是任一会话变动都让全部遗留会话失效、每轮整库重读。 无存储格式变更(STORE_VERSION 仍为 v8),升级不需要迁移动作;已在 0.1.7 上跑过的库第一轮就是零重读。
· 修复
1. 水位线只认"本会话文件身份",不再受整库语料哈希牵连(lib/session-source.mjs)。宿主 @deepseek-ai/dsh-session-persistence-jsonl 0.1.7-alpha.1 起,list() 对 sourceVersion :;后者是整库所有会话文件路径 + 各自 stat 身份的 sha256。旧实现在 scanStore 里直接做 wm.rev !== String(s.revision),粒度错配:
| 场景(70 会话 / 65 个 v3,仅改 1 个文件 mtime,内容零改动) | 0.9.5 | 0.9.6 |
|---|---|---|
| 无变化的一轮 | 88 ms | 88 ms |
| 只改 1 个文件的 mtime | 8 512 ms(changed=65) | ~90 ms(changed=0) |
而页面「进入界面时先扫描」(默认开启)意味着打开面板要等整轮扫完才出数据 —— 这正是"有时候不显示、过一会又能显示"。现在 ownRevisionToken() 只取前五段(文件身份);对新旧宿主都安全(≤0.1.6 本来就是五段,内存会话 memory:: 段数不足则原样返回)。
2. 遗留会话的兄弟依赖改为精确追踪,而不是丢弃(同文件)。整库哈希原本承载的语义是"遗留会话解码时可能读兄弟文件"(宿主 prepareStoredMigration → children() 会读 origin === 'subagent' && parentSession === id 的子会话参与解码)。直接删掉这层依赖会让父会话在子会话变化后不再重折(数据静默少算)。新增 relatedFingerprints():按宿主真正读的那批文件算"父会话 id → 子会话 id+文件身份"指纹,并存入水位线的 rel 字段。于是子会话增删改只让该父会话重读,不波及别的父会话 —— 语义不弱于整库哈希,影响面从 O(全库) 收到 O(该父的子树)。
3. 升级路径不会触发一次整库重读(同文件)。已在 0.1.7 上跑过的库里,水位线存的是六段形态(含整库哈希)。比较时对已存值与当前值都做归一化,所以升级后第一轮就是零重读,而不是把全部遗留会话判为"变了"。
4. dsh.engines.dsh 补上 >=0.1.7-0(package.json / README.md)。按 SemVer 规则预发布不落在普通范围里:0.1.7-alpha.1 既不满足 >=0.1.5-rc.1 也不满足 >=0.1.6-0,旧声明把本插件实际支持并已在跑的 0.1.7 预发布判成不兼容(插件市场会据此显示"不兼容")。同时把 0.1.7-alpha.1 记进 compatibility.dshReleases。
· 测试
5. test/v096-hostrev.test.mjs(V73–V73e)。用宿主 0.1.7 的真实 revision 形态(含"20 个会话共享同一语料哈希")锁住:任一会话变动只重读它自己;语料哈希变化本身零重读;老库六段水位线零重读;子会话变化只波及它的父;以及两个纯函数的边界。这四条行为断言在旧逻辑下实测有 4 条失败,不是事后补的空测试。
Full Changelog:
v0.9.52026-09-22
主题:补掉唯一一处"与改动量脱钩"的计算 —— 扫描收尾的聚合重建从"只要有会话变化就
整库重折"改成"只重折被改动的会话"。 无存储格式变更(STORE_VERSION 仍为 v8),
升级不需要迁移动作;重启后第一轮扫描走完整重建(与旧行为一致),之后进入增量。
· 性能
1. 聚合收尾改为增量(lib/session-source.mjs 的收尾分支 + lib/core.mjs 的
unfoldSessionInto / incrementalRebuildInto)。
旧行为:dirty 为真就 rebuildAllChunked(全库) —— 代价 O(全库记录数),与"改了多少"
完全脱钩,20 万请求的库改 1 个会话也要重折 20 万条(作者实测 447 ms)。
新行为分三条路:
| 情形 | 走哪条 |
|---|---|
| 数据变了(会话记录被替换 / 删除 / 修剪) | 增量:只重折被改动的会话 |
| 改价格 / 高峰时段 / 保留期 | 整库重折(价格一改,每条记录的 cost 都过期) |
| force 全量扫描 / 冷启动 / 聚合不可用 | 整库重折(没有"旧账"可减) |
端到端实测(真实 scanStore 路径,6 万请求 / 200 会话 / Node 24):
| 场景 | 0.9.4 | 0.9.5 |
|---|---|---|
| 无变化扫描 | 0.2 ms | 0.2 ms |
| 改 2 个会话 | 66.6 ms(整库重折) | 2.7 ms |
| 冷启动首扫 | 整库重折 | 整库重折(同路径) |
即"有变更的那一轮"约 25×,且库越大差距越大(O(改动量) vs O(全库))。
两条路都会在 summary.aggMode 里如实报出 full / incremental / none,
所以"这一轮到底重折了什么"是可观测的,不必靠计时猜。
> 足量说明(别把这条读成"扫描不再花钱了")。 增量只省掉"重折"那一段;读取与
> 折叠会话日志本身的开销一点没减 —— 变更的会话仍要被完整读出来并 foldSession。
> 冷启动、force、以及任何改价的时刻仍是整库重折。本版没有做"日志的增量解码"。
2. 等价性是硬门禁(test/v095-incremental.test.mjs 的 V67 / V67e)。
增量与全量在 25 轮随机改动(追加 / 缩短 / 删除 / 新增四种混跑)后,days / months /
逐记录派生字段与 sessions 必须逐值相同;查询与导出的 CSV 必须逐字节相同。
一个补充口径:聚合桶里的汇总额允许 1e-9 元的浮点公差(浮点减法不可逆,实测末位
差 ~1e-17),而四段用量与请求数是精确判定 —— 这个区别本身由 V69 锁定。
· 修复
3. newRequests 统计虚高(lib/session-source.mjs)。此前是
+= 本会话全量条数,于是每次会话增长都把它整段再加一遍 —— 一个 3000 条的会话被改
10 次就虚报 3 万。现在是净新增条数。自测统计失真比没有统计更糟:它看起来像个
真实指标。
4. 隔离清单在设置页从未渲染(web/src/app/80-tab-settings.js)。服务端一直把
quarantinedCount / quarantined 放在 meta 里(README 也说"设置页可见"),
但页面里一处都没用到 —— "有些会话统计不到"这件事对用户完全不可见。
现在新增「数据完整性」一节:总数、每个被跳过会话的 id / 错误类型 / 命中次数 / 错误
消息,并写明"保留上一次成功扫描的统计,不会静默清零"。超过服务端 50 条上报上限时
显示"仅列出前 50 个 · 共 N 个"。错误消息来自宿主解码器,按外部数据处理并转义。
· 文档
5. README 修正"改外观"指引。原文让用户改 web/src/ 后跑
node scripts/build-web.mjs,但 package.json 的 files 只发 web/index.html ——
web/src/ 与 scripts/ 不在包里(由 V49/V50 锁定)。npm / git / tarball
三种安装方式下照做会扑空。已写明"这条只适用于 clone 仓库"。
6. README 规模表补三行:有变更的扫描周期已改走增量(附上表实测数据)、
store.json 体积随会话数线性增长(它每个脏周期整体重写,本机 64 会话约 32 KB)、
以及 aggM
v0.7.12026-09-12
主题:把"抄一份值"改成"接宿主变量"。 0.7.0 及以前,面板的字体、字号、底色都是从宿主
样式里复制出来的固定值 —— 复制值今天看着对,但用户一改设置、或宿主一换主题就静默失效,
而界面仍然"看起来正常",没人会发现。
· 修复
· 会话面板不再无视「外观」里的正文字号。 面板原来写死 font:15px/1.47,全文件不引用
--dsh-content-font-size。会话区里 chat / conversation / tool / goal / workflow-run 等
视图全部走宿主的字号变量,只有本面板不走。现在字号接 --dsh-content-font-size、行高接
calc(24px + var(--dsh-content-font-delta,0px)),与相邻页签同一个口径。
(影响:把字号设成 12px 的用户,面板从 15px 变为 12px/22px。)
· 面板底色不再与相邻页签有色差。 原来写死 #000(暗) / #f2f2f7(亮);会话根节点
(ConversationRoot) 实际画的是 --dsw-alias-bg-base(#151517 / #fff)。
现在底色接该 token,--d-bg 退居兜底,切页签不再看到一整块异色。
· 字体族改接宿主钩子。 面板与仪表盘页原来各自抄了一份字体栈(且两份不同)。现在都写
var(--dsw-font-family, ) —— --dsw-font-family 是 DSH 留给主题
覆写的钩子,宿主换字体时两处一起跟随。
· 仪表盘页的「自动」主题改为跟随 DSH 外观,而不是只跟随操作系统。 该页是独立文档,
看不到宿主 DOM 上的 data-ds-dark-theme,因此由宿主在发送页面时注入
window.__DSH_TOKEN_THEME__(读同一个 ui-theme 设置段)。此前当用户把 DSH 钉成深色而
系统是浅色(或反之)时,仪表盘会和外壳反着来。
· 外观偏好纳入页面缓存键。 页面缓存原来只按 (path, mtime) 失效;只改主题设置而不动
页面文件时,注入的旧偏好会被一直发出去。现在缓存键含偏好,改设置立即生效。
· 主题切换的提示文案由含糊的「跟随系统」改为说明实际来源(如「跟随 DSH(深色)」)。
· 系统深浅变化的重绘收窄:DSH 把外观钉成浅色/深色时,系统深浅与本页无关,不再触发重绘。
· 测试
· 新增两条宿主适配不回退断言:面板必须接宿主字体族/正文字号/底色,且不得再用 font
简写写死字号。这两条会在有人把绑定改回固定值时直接失败。
· 引用闭合检查的豁免从 --dsw-* 扩展到宿主的整个命名空间(含 --dsh-*,即内容字号这类
外壳运行参数),并在注释里写清两个前缀各自代表什么。
· e2e 新增两条:页面必须带注入的外观偏好引导脚本(且位于 内、先于页面自身脚本);
只改设置、不动页面文件时注入值必须立刻换新。
· 说明
· 无破坏性改动:三个入口(侧边栏 / 对话区页签 / 设置页)的名称、插槽与 API 契约不变,
store.json 结构版本不变。
· 侧边栏入口与设置页面板本来就正确使用 --dsw-alias-*,本次未改动。
Full Changelog:
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-token
License: MIT
DSH 本地 Token 用量统计插件——以 Token 为第一视角:不只回答"花了多少钱、用了多少量",还回答 Token 从哪来、缓存省了多少、哪些会话在烧 Token。数据全部来自本地会话日志,零上传、无在线依赖。仪表盘为 Apple 风格四 Tab 界面,开箱即用。
亮点
- 四段 Token 构成:未命中 / 缓存命中 / 缓存写入 / 输出,并给出缓存省下的钱;
- 成本诚实:无内置价且无自定义价的模型成本计 0,但 kpi.totals 显式报出 unpricedTokens / unpricedModelCount,界面与导出都标注"未计入" ——估算成本是"已定价部分的成本 + 未定价 token 数",不是完整账单;
- 全局筛选:时间(今天/3天/7天/30天/自定义)+ 模型 + 会话 + 工作目录,URL 可分享;
- 会话下钻:四联统计、逐模型构成、逐请求明细、上下文增长曲线、🔥 异常激增标记、?
展开完整文档(README 剩余 1 节)
dsh-token
License: MIT
DSH 本地 Token 用量统计插件——以 Token 为第一视角:不只回答"花了多少钱、用了多少量",还回答 Token 从哪来、缓存省了多少、哪些会话在烧 Token。数据全部来自本地会话日志,零上传、无在线依赖。仪表盘为 Apple 风格四 Tab 界面,开箱即用。
亮点
- 四段 Token 构成:未命中 / 缓存命中 / 缓存写入 / 输出,并给出缓存省下的钱;
- 成本诚实:无内置价且无自定义价的模型成本计 0,但 kpi.totals 显式报出 unpricedTokens / unpricedModelCount,界面与导出都标注"未计入" ——估算成本是"已定价部分的成本 + 未定价 token 数",不是完整账单;
- 全局筛选:时间(今天/3天/7天/30天/自定义)+ 模型 + 会话 + 工作目录,URL 可分享;
- 会话下钻:四联统计、逐模型构成、逐请求明细、上下文增长曲线、🔥 异常激增标记、?session= 深链;
- 成本与预算:单价表(内置 DeepSeek 官方价目,高峰/空闲分档,时段可改)、月度预算环形进度 + 月底外推 + 80%/100% 阈值、按模型/按会话归因;
- 图表:多模型叠加趋势(图例可逐个关)、日/周/月粒度、24 桶小时直方图、活跃度月历热力图;
- 快路径聚合:无 session/wd 筛选时查询读 day/month 聚合索引,复杂度 O(天数);
带这两个筛选时整会话跳过(它们是会话级前缀判定)。两条路径输出等价由单测锁定;
- 落盘分片:store.json 只存元数据,逐会话记录在 shards/;分片自带模型表, 局部重写不会让整数下标错位;单会话损坏只影响它自己;
- 增量扫描:未变化会话按 revision 跳过;扫描周期无变化时跳过重建与落盘; retention.days 修剪超龄记录,默认 0 = 永久保留;
- 刷新可配:refresh.pageSec(页面重取,默认 60s)、scanSec(后台重扫,默认 300s)、
onOpen(进入界面先扫一次,默认开);填 0 关闭;
- store 损坏不静默清零:形态体检不合格就改名 store.json.corrupt- 留证再从日志重建;
- 跨 DSH 版本自适应:sessionPersistence 两代 API 按能力探测适配,不绑宿主版本号;
- 失败会话隔离:被宿主确定性拒绝的老日志在冷却窗口内跳过,瞬时错误始终重试。
性能与实现取舍的逐条论证见 scripts/invariants.mjs(95 条编号不变量, 每条写明"锁什么、为什么、被哪个测试锁住")。
界面
六个视图,截图取自一份全虚构的示例库(18 个会话 / 474 次请求,由
scripts/screenshot-harness.mjs 用固定种子生成),不含任何真实使用记录。点图看原图。
图片挂在 Release 资产上而不是用仓库相对路径:README 同时是 npm 页,相对路径在 npmjs.com 上会裂图,而 raw.githubusercontent.com 在部分网络环境下不可达。商店侧以仓库里的 screenshots.json 为准,与这里无关。
| 今天今天 · 四段构成与小时分布 | 活动活动 · 趋势 / 逐模型 / 粒度切换 |
| 会话会话 · 排序 / 搜索 / 激增标记 | 会话详情会话详情 · 指标 / 构成 / 增长曲线 |
| 逐请求时间线逐请求时间线 · 明细表 | 设置设置 · 预算 / 单价 / 高峰时段 |
架构
web/index.html 拼接产物(交付物)仪表盘单页 · Apple 风格(默认页;四 Tab:iOS 大标题 / 玻璃材质 / 活动圆环 hero / 屏幕使用时间式图表;纯原生 JS + Canvas,单文件无外部资源) lib/client.js 宿主直供的浏览器模块(exports["./client"],手写、不经打包器): 四个入口 —— 侧边栏「Token 统计」= 全局面板(应用内整屏仪表盘, 注册在 sidebar.panellist + main,本体是 iframe 嵌 /dsh-token), 会话内「本会话用量」= 原生 React 单会话面板(不是 iframe), 设置页「Token 统计」= 插件信息面板。
只 require 平台种子模块 react(宿主模块表提供,非 npm 依赖) lib/core.mjs 数据层核心(纯数据:store 进、结果出,不碰文件也不认识 Cordis 服务): 折叠、聚合、筛选、导出、价格、异常检测、
会话标题提取(session/title → 首条用户消息回退)、
day/month 聚合快路径(O(天数) 查询)+ 惰性重建、store 形态体检 (聚合运行时状态挂 store 的非枚举字段 __agg,单一入口 aggState(store),不落盘) lib/session-source.mjs 会话日志读取与扫描层(唯一需要 sessionPersistence 服务的一层): sessionPersistence 新旧两代 API 适配、增量水位线、确定性失败会话隔离 + retention 修剪 (依赖方向:index → session-source → core;core 不认识前两者) lib/design-tokens.mjs 设计 token 单一来源:两处界面的调色板/语义色/文本色/阴影由 scripts/build-tokens.mjs 注入 web/src/style.css 与 lib/client.js (注入源文件,再由 build-web 带进产物),LOCAL 里显式登记"有意不一致"的项仅构建期使用:不在 package.json 的 files 里,运行时无任何 import (由 V63b 守卫 —— 运行时 import 它会让用户侧 MODULE_NOT_FOUND) scripts/invariants.mjs 不变量登记表:112 条防退化守卫的"锁什么 / 为什么 / 被哪个测试锁定" (V1–V73e),机器可读。test/invariants.test.mjs 双向强制: 登记了没测试 → 失败;有 V 编号测试没登记 → 失败;缺号必须在 GAPS 里解释 scripts/web-parts.mjs 网页源文件的拼接清单(按序排列的文件名) scripts/build-web.mjs 把 web/src/ 拼成 web/index.html;--check 逐字节校验(漂移即失败) web/src/ 仪表盘源文件(HTML 外壳 + style.css + app/.js 共 20 份) web/index.html 拼接产物(交付物):仪表盘单页 · Apple 风格 lib/index.js Host 插件外壳:扫描(sessionPersistence)、/dsh-token 路由、 /token-stats 命令、5 分钟增量扫描(无变化不落盘)、
页面缓存(按 mtime+size+外观偏好失效)+ gzip、发送时注入外观偏好、
POST 同源校验 + 1MB body 上限、 store 形态体检与损坏留证、启动自检; store 载入与分片读取为异步(0.9.4,并发 8),
查询入口有空库兜底(载入完成前不返回 500)
test/ node:test 断言(397 项):数据层、价格/时段、扫描与隔离、客户端注册、
页面渲染、宿主语义(时区口径/未定价/惰性重建/store 体检)、
界面回归(配色按模型名稳定/占比口径/单桶趋势/监听幂等/自定义范围)、
设计 token 一致性与引用闭合、宿主适配不回退 (面板必须"接"宿主字体族/正文字号/底色,不允许抄成固定值)、
画布绘制(2D 调用序列录制:渐变档位/路径闭合/坐标有限/同输入可复现)、
结构化转义(tpl/raw 语义 + 恶意输入实测 + 源码级裸拼接守卫)、
异步载入与分片重建的等价性(与同步路径逐字段相同)、
不变量登记表强制(登记 ↔ 测试双向一致)
scripts/build.mjs 发布前完整性校验(文件、dsh 字段、各模块可解析、内联脚本语法) scripts/build-tokens.mjs 设计 token 注入器(--check 用于 CI/测试,漂移即失败) scripts/e2e-smoke.mjs 真实 http 栈端到端冒烟(零依赖,临时 DSH_HOME;含外观偏好注入与「偏好进页面缓存键」:只改设置、不动页面文件,注入值也必须立刻换新) scripts/verify-tarball.mjs 打包产物验收:把 tarball 解到全新目录当"新装"启动一遍 scripts/settings-snapshot.mjs / canvas-snapshot.mjs 重构安全网:把“设置页输出”与“画布 2D 调用序列”打成快照, 改动前后 diff 为 0 才算没改行为(还能当变异测试用:故意改坏一处, 确认对应的网真能抓到) scripts/realstore-equivalence.mjs / realstore-coldscan.mjs 拿本机真实 store 比对旧版/新版全部查询,以及真实 zstd 会话日志
的冷启动全量重扫(多帧解码按 DSH 的 scanZstdFrames 口径逐帧解)
scripts/bench-refactor.mjs / bench-one.mjs / gen-stress-store.mjs
性能对照:交错采样取中位数 + 配对符号检验,可生成 20 万请求压力库 (这些 scripts/ 与 test/ 不打进 npm 包,只在仓库里)
数据源 = $DSH_HOME/sessions(默认 ~/.dsh/sessions),经 DSH 官方 sessionPersistence 服务解码(session.jsonl.zstd 为多帧 Zstd + chunk 打包)。持久化在 $DSH_HOME/dsh-token/:store.json 存配置与元数据(水位线 / 隔离清单 / 统计 / 会话 meta),shards/.json 存逐会话记录(0.9.0 起;备份请连目录一起备)。
三个入口各有明确分工
同一个插件在 DSH 界面上有三个入口,名字不同、界面也不同 —— 避免“从不同地方点开、长得一模一样、显示的数据却不一样”的困惑:
| 入口 | 插槽 | 名字 | 打开后是什么 |
| 侧边栏(全局面板区) | sidebar.panellist + main | Token 统计 | 全量仪表盘:在应用内整屏打开(四个 Tab、图表、全局筛选、导出)。图标/行样式由外壳画,面板本体 iframe 嵌 /dsh-token |
| 对话区域上方的页签 | conversation.view | 本会话用量 | 只服务当前对话的原生面板(见下)。不嵌 iframe,走 /api/session?id=…&brief=1 只取汇总 |
| 设置页 | settings.section | Token 统计 | 数据源路径、缓存文件、会话/请求数、最近扫描统计 |
/dsh-token 单页仍可独立打开(同样的内容、同样的 API),面板只是把它嵌进来 ——两者共用同一份页面,不存在“面板里一套、页面里另一套”。
为什么侧边栏入口在 sidebar.panellist 而不是 sidebar.footer.action:后者被外壳渲染成 display:flex 且不换行的一行,任何写死 width:100% 的入口都会与别的插件各占一半、挤在设置上方;那半截宽度由外壳算出,从入口内部修不掉。sidebar.panellist 是面板图标位, 行样式全部由外壳负责,天然与内置面板同区同款。
视觉规则:外壳跟随宿主,整屏视图跟随产品 —— 但凡是宿主已经表达过的偏好,一律“接”而不是“抄”。
侧边栏图标位、设置页那一段 —— 它们是 DSH 外壳的一部分,用 DSH 的设计变量
(--dsw-alias-)画;图标位更是整个交给外壳(行高/内边距/底色/选中态都由它画),
和旁边的面板、设置项浑然一体,不抢戏。
对话区域里的「本会话用量」是一整屏视图 —— 它跟随产品:复用 /dsh-token 仪表盘那一套 iOS 设计语言(液态玻璃卡片、大数字 hero、彩色图标小卡、分类条形列表),调色板与语义色
跟仪表盘逐一对齐(缓存命中=绿、未命中=红、输出=蓝、缓存写入=灰,高峰=橙、空闲=蓝)。
* 但“跟随产品”只到识别度为止,不越过宿主已经表达过的偏好。以下四件事是绑定关系, 不是抄写关系 —— 抄一份固定值会在用户改设置或宿主换主题时静默失效:
| 维度 | 面板接的宿主变量 | 效果 |
| 字体族 | --dsw-font-family(DSH 留给主题覆写的钩子) | 宿主的主题换了字体,面板与仪表盘一起跟着换 |
| 正文字号 | --dsh-content-font-size(「外观」里的内容字号,12–17px) | 面板与相邻页签同一个字号;行高走 --dsh-content-font-delta 同步变化 |
| 底色 | --dsw-alias-bg-base(会话根节点 ConversationRoot 的底色) | 面板与相邻页签无缝,切页签不会看到一大块异色 |
| 深浅色 | body[data-ds-dark-theme] | 跟宿主外壳同一个开关 |
面板自有的调色板(--d-)退居兜底:宿主 token 缺失时(如单独打开 web/index.html)仍有产品底色。
/dsh-token 是独立文档,看不到宿主 DOM 上的 data-ds-dark-theme,所以由宿主在发送页面时注入 window.__DSH_TOKEN_THEME__(读的是同一个 ui-theme 设置段)。页面的「自动」因此跟随 DSH 外观,而不是只跟随操作系统 —— 用户把 DSH 钉成深色、系统却是浅色时,两者不会反着来。
注入缺失(例如直接以文件打开本页)时退回“只跟随系统”。注入结果进页面缓存键,改设置立即生效。
面板内容:
| 区块 | 说明 |
| 顶部 | 「复制摘要」(把本会话用量打成纯文本进剪贴板,成功/失败都有反馈)/「刷新」/「完整仪表盘 ↗」(强调色实心胶囊) |
| hero | 42px 大数字 + 费用 + 汇总行(请求数 · 活跃天数 · 起止时刻)+ 逐小时柱 |
| 时段分布 | 24 根柱(北京时间):蓝=空闲价、橙=高峰价,并给出高峰/空闲的 token 与花费,以及生效的规则文字 |
| 构成 | 细堆叠条 + 双列图例,每段带 token、占比与费用 —— 直接看出“钱花在哪一段”(输出单价是缓存命中的数百倍) |
| 亮点 | 四块彩色图标小卡:缓存省下 / 缓存命中率 / 高峰时段 / 平均每次请求 |
| 按模型 | 分类条形列表:tokens、费用、请求数、占比;未定价模型明确标注 |
| 异常提示 | 有异常增长时给出次数、解释与「查看逐请求明细」深链(?session=) |
| 脚注 | 计价口径 + 高峰规则出处 |
会话面板在库中查不到这条会话时会自动补一次增量扫描再复查(刚建的对话不必等 5 分钟周期)。
高峰规则文字来自服务端下发的生效配置,不在客户端写死官方时段 —— 用户改了高峰时段,面板文字跟着变。
DSH 版本兼容
sessionPersistence 在 DSH 0.1.5-rc.2 从“按 id 直读”改写为“句柄式”,本插件按能力探测同时支持两代,无需按宿主版本分发:
| 能力 | ≤ 0.1.4 | ≥ 0.1.5-rc.2 |
| 列举会话 | listSnapshots() | list() |
| 读取事件 | readFrom(id, 0) | open(id,'read') → handle.read(0) → handle.close() |
| 会话头 | 返回值 meta | handle.header |
| 路径推导 | locate(meta) | locate(meta)(仍存在,仅作兜底) |
同时,TokenUsage 口径在 0.1.5-rc.2 明确为互斥口径:inputTokens 只计未命中输入,
totalTokens = input + read + write + out。插件据此按等式判据(totalTokens === input + out
且带缓存)识别“折叠口径”的极老日志并回退相减,miss 因此不再被“相减启发式”少算。
store 结构版本随口径修正升到 v4;v4 → v5 是单价改为官方分档 + 高峰/空闲双价; v5 → v6 是小时桶从“宿主本地时区”改为“北京时间”(与高峰取档同口径)。
旧 store 首次加载会自动触发一次全量重扫,之后回到秒级增量。
关于 dsh.engines / dsh.compatibility
这两个字段不是门禁:DSH 宿主不读取它们(dsh plugin add 只检查 dsh.bundle), DshManifest 类型里也没有 compatibility。它们的作用是给插件市场/人工核对留一份 “测过哪些宿主版本”的记录,dsh.engines.dsh 是下限而非精确集合。改宿主版本时请一并更新这里,别让它烂成过期文档。
当前声明为 >=0.1.5-rc.1 || >=0.1.6-0 || >=0.1.7-0。后两段不是冗余:按 SemVer
规则,预发布版本不落在普通范围里 —— 0.1.6-alpha.1 不满足 >=0.1.5-rc.1, 0.1.7-alpha.1 既不满足 >=0.1.5-rc.1 也不满足 >=0.1.6-0。只写前面几条会把这两个预发布判成不兼容(本插件实际支持它们)。每加一条 >=0.x-0 才能把“0.x 及其预发布” 纳入范围。这仍然只是声明,不构成门禁。
0.1.7-alpha.1:宿主 revision 粒度回归(0.9.6 修复)
宿主 @deepseek-ai/dsh-session-persistence-jsonl 在 0.1.7-alpha.1 改了 list() 里 revision 的构造(对比 0.1.6-alpha.1 的同名函数):
| 宿主版本 | 遗留会话(v3)的 revision |
| ≤ 0.1.6 | = dev:ino:size:mtimeNs:ctimeNs |
| ≥ 0.1.7 | :(仅当来源版本 本包没有打包器:lib/.js、lib/core.mjs 是直接发布的源文件,浏览器端
lib/client.js 同样由宿主直接提供。唯一的“构建”是 web/index.html ——它由 web/src/(20 份源文件)经 scripts/build-web.mjs 拼接而成,拼接结果与源逐字节一致(内联脚本是单个 IIFE + 'use strict',按行区间切片再按同序拼回不改变语义)。交付物始终是那个单文件 web/index.html,插件原样返回它,测试也直接读它 —— 拆分只为好维护,不引入多文件请求。
tarball 与 git 安装拿到的是同一份字节。发布前的把关靠 npm run verify(完整性/语法校验)与
npm test(397 项断言 + token/产物一致性校验),两者都挂在 prepack 上,所以 npm pack 不会漏跑。
安装后打开 http://127.0.0.1:3080,开箱即用:
- 仪表盘默认即为 Apple 风格四 Tab 页(今天 / 活动 / 会话 / 设置,底部悬浮 Tab 栏);
- 首次启动会做一次全量扫描(自动提取每个会话的标题),之后每次重启均为秒级增量;
- 侧边栏出现 Token 统计 全局面板入口(在「技能中心」等内置面板同一区,图标与行样式由外壳画);点击在应用内整屏打开仪表盘,/dsh-token 单页仍可独立打开;
- 对话区域上方出现 本会话用量 页签:只服务当前对话的原生面板(不嵌 iframe,内容见上表);
- 设置 → Token 统计 页展示数据源、扫描统计、模型清单、被跳过(隔离)的会话清单,并管理单价表与高峰时段;
- 斜杠命令 /token-stats [today|3d|7d|30d|month|all]。
- 想自定义仪表盘外观:改 web/src/(源文件)后跑 node scripts/build-web.mjs 重新拼接出 web/index.html,Host 按 (mtime, size, 外观偏好) 失效缓存,刷新即生效,免重启。
⚠ 这条只适用于 clone 仓库的场景。 npm / git / Release tarball 三种安装方式拿到的是发布产物:package.json 的 files 只列 web/index.html,web/src/ 与 scripts/ 不在包里(由 V49/V50 锁定“产物在、源不在”)。想改外观请先
git clone https://github.com/fufuf-c/dsh-token,再按 scripts/web-parts.mjs 的
拼接清单改。
0.8.9 起不再支持 webPagePath 指向外部文件 —— 那条配置面只校验扩展名,等于给本机任意进程一个“读盘上任意 .html”的原语(UNC 路径还会触发对外 SMB 连接、外泄 NTLM 凭证),而页面字节又是同源返回的,构成脚本执行面。
与 dsh-usage-vista 系列可共存(不同 URL 前缀 / bundle id / 入口 id)。
路由与 API 契约
全部同源,Cache-Control: no-store。
| 端点 | 方法 | 说明 |
| /dsh-token | GET | 仪表盘单页(见“页面替换”) |
| /dsh-token/api/kpi | GET | KPI:?range=&models=&session=&wd=&from=&to= |
| /dsh-token/api/series | GET | 序列:?granularity=day|week|month&range=… |
| /dsh-token/api/hours | GET | 24 桶小时分布(同上筛选) |
| /dsh-token/api/heatmap | GET | 12 个月日总量:?months=12&… |
| /dsh-token/api/sessions | GET | 会话排行:?sort=recent|cost|tokens|requests&limit=&q=&…(q= 标题/工作目录/工作区/会话 ID 子串,服务端过滤,不受 limit 截断) |
| /dsh-token/api/session | GET | 单会话下钻:?id=;&brief=1 省掉逐请求数组(会话内面板用,大会话可省成 MB 载荷)。两种模式都带节奏字段:hours/hoursPeak(24 桶,北京时间,后者是按高峰价计费的部分)、tiers(高峰/空闲各自的 tokens/费用/请求数)、costParts(四段各自花掉的钱)、unpriced(未定价模型的 tokens)、activeDays、schedule(生效的高峰规则) |
| /dsh-token/api/meta | GET | 数据源路径/会话/请求/模型/扫描统计/价格族 |
| /dsh-token/api/config | GET/POST | 配置:{prices?, resetPrices?, budget?:{monthly}, retention?:{days}, refresh?:{pageSec?, scanSec?, onOpen?}, peakHours?("9-12, 14-18" 或 [[9,12],[14,18]];null=官方默认,[]=无高峰), peakDays?("1-5" 或 [1,2,3,4,5];null=默认,[]=无)};GET 额外返回 defaults(模型键 → 官方默认价,含 peak/idle)、归一化后的 prices、生效时段 schedule/peakHours/peakDays 及其官方默认值、以及 refresh(归一化后)+ defaultsRefresh + refreshBounds。webPagePath 已在 0.8.9 移除(传了会 400) |
| /dsh-token/api/scan | POST | 触发增量扫描 |
| /dsh-token/api/export.csv | GET | 日×模型明细(当前筛选) |
| /dsh-token/api/export.json | GET | 逐请求原始明细(当前筛选);超过 10 万条时返回 413 + 可操作的原因(避免单次 68MB 响应体卡死标签页) |
主机/来源栅栏(0.8.9 起对 GET 与 POST 一视同仁):请求的 Host 头必须落在“本机可达”白名单内 —— localhost/.localhost、IPv4 回环(127.0.0.0/8)、IPv6 回环(::1、::ffff:127.0.0.0/8)、RFC1918 私网、链路本地、CGNAT、IPv6 唯一本地/链路本地放行;域名与公网 IP 字面量一律拒绝。域名拒绝是因为 DNS rebinding 只能借域名生效(rebind 后 Origin.host 与 Host 会同时是攻击者域名,同源等式恒真);公网 IP 字面量拒绝是因为 bind 0.0.0.0 时任何人都能以裸 IP 访问,而浏览器发起的跨站请求的 Origin 必然是域名 —— 拒掉不伤任何正常用法。带 Origin 头时还须与 Host 完全一致;另拒绝 Sec-Fetch-Site: cross-site(与 Host 检查是两条独立线索)。主机名比较前会规范化(去尾部点、统一小写),所以 127.0.0.1. 与 LOCALHOST 不会被误拒。经 LAN 的私网 IP 访问(反代/多机)仍可写入配置,curl 无 Origin 调用照旧放行。请求体上限 1MB(超限排空剩余 body,keep-alive 不串流)。页面响应带 Content-Security-Policy(仅内联脚本/样式 + 同源连接)与 X-Content-Type-Options: nosniff;CSV 导出对 =/+/-/@ 开头的单元格做公式注入防护。筛选参数(贯穿所有数据端点):range=today|3d|7d|30d|month|all|custom(custom 配 from/to 日键 YYYY-MM-DD)、models=(逗号分隔 provider:model 键)、session=(会话 ID 前缀)、wd=(工作目录前缀)。
kpi 响应: {
"range": { "label", "fromDay", "toDay" },
"totals": { "miss", "read", "write", "out", "total", "requests", "cost", "saved",
"unpricedTokens": 650, // 无内置价且无自定义价的 tokens(cost 里记 0) "unpricedModelCount": 1 }, // 涉及几个模型 "hitRate": 0.986, // read / (miss+read+write) "activeSessions": 110,
"peakDay": { "day", "tokens" }, // 无任何用量时为 null(不是 tokens 为 0 的伪日期)
"delta": { "tokens", "cost" }, // 今日环比昨日 / 本月环比上月
"streakDays": 9,
"models": [{ "key", "provider", "model", "totals" }] // 按量降序
}
时区口径(两条并存,别混):日/月桶按宿主本地时区(“今天”= 用户所在时区的今天);小时桶(24 桶)与高峰/空闲取档按北京时间 UTC+8,因为官方按时段计费、同一份价目必须在任何宿主时区下落在同一个桶里。/dsh-token/api/hours 的 24 桶与 /dsh-token/api/session 的 hours/hoursPeak 现在是同一口径;旧版本里 /hours 用的是本地 getHours(),非 UTC+8 宿主会与高峰高亮错开 8 小时(storage v5 → v6 已修正)。
session 响应:{ id, meta{id,createdAt,cwd,parentSession,origin,delegationDepth,agentPreset,workspace}, totals, models[], requests[], requestCount, anomalies, flagged, flags[] };requests[i] = {seq,t,m,miss,read,write,out,r(推理),i(中断),cum(累计输入),cost,saved,priced};flags[i] 0=正常 1=🔥激增 2=中断。
页面替换(UI 重设计指南)
仪表盘与插件壳完全解耦——重设计只改网页源文件,不碰数据层:
1. 编辑 web/src/(外壳 HTML / style.css / app/.js),然后 node scripts/build-web.mjs 拼出 web/index.html;Host 按 (mtime, size, 外观偏好) 缓存页面,产物一变即失效,刷新页面即生效(免重启)。npm test 会在产物与源不一致时失败(build-web --check)。
页面响应自带 gzip(Accept-Encoding 协商,实测 176854 B 单页 gzip 后 47903 B(降 73%)); 0.8.9 起不再支持 webPagePath 指向包外文件:那条配置面只校验扩展名,等于一个“读盘上任意 .html”的原语(UNC 还会外泄 NTLM 凭证)。要换皮肤请直接改源文件;想保留改动请在 git 里维护你自己的分叉。
2. 数据获取:页面用 fetch('/dsh-token/api/…'),前端契约见上表(稳定,不会变);
3. 主题:页面自带 data-theme=light|dark + prefers-color-scheme 跟随,可嵌入时与 DSH 主题一致(变量名可自行约定;深度集成可读 DSH 主题 localStorage 偏好);
4. 深链约定(保持不变):?range=&models=&session=&wd=&from=&to= 全量可分享; ?session= 打开会话详情弹窗。
新增面板时,优先复用现有端点;若确需新数据,请在 lib/core.mjs 增加纯查询函数并在 apiDispatch 挂一个端点 —— 保持“core 纯函数 + 路由薄壳”结构即可。
价格口径内置价目按官方页落地:模型 & 价格(单位 ¥ / 1M tokens)。
| 模型 | 高峰未命中 / 命中 / 输出 | 空闲未命中 / 命中 / 输出 |
| deepseek-flash | 2 / 0.04 / 8 | 1 / 0.02 / 4 |
| deepseek-v4-pro | 9 / 0.30 / 27 | 4.5 / 0.15 / 13.5 |
时段规则与倍率都是“当前官方策略”,不是恒等式 —— 官方随时会改,所以插件里一律当作可配置数据,不把任何一条写进逻辑:
- 高峰时段/高峰日可改,存 store.config 的 peakHours / peakDays:
{ "peakHours": [[9, 12], [14, 18]], "peakDays": [1, 2, 3, 4, 5] } // 0=周日 … 6=周六
设置页是点选式:24 个小时格 + 7 个星期胶囊,点一下即时保存(去抖 500ms),没有“保存”按钮;下方实时回显成一句人话(如「工作日 09:00–12:00、14:00–18:00 为高峰 · 每周 35 小时」),并带「官方默认 / 全天 / 清空」三个预设。API/curl 另外接受紧凑文本(9-12, 14-18 / 1-5,支持跨周 5-1);null 恢复官方默认,显式空数组 [] = 没有高峰时段(字符串空则视为格式错误,避免输入框被清空时误清规则)。判定固定按 UTC+8(北京时间)换算,不受宿主时区影响;
- 两档价各自独立存放({peak:{…}, idle:{…}}),代码里没有“空闲 = 高峰 ÷ 2” 这类推导 —— 官方哪天改了比例甚至改成三档,只需改数据,不必改逻辑。当前内置价目恰好是半价,那只是数据如此;
- 计费逐请求取档:每条记录按其自身时间戳落在高峰还是空闲选价,所以跨时段的日/月汇总自然正确(高峰时段的小时柱会明显更高);
- 缓存写入费为 0:官方不单独收取(同样是内置数据,可覆盖);
- 按模型名分档(仅官方 provider):deepseek-v4-pro 走 pro 档,其余(含旧名 deepseek-v4-flash、 deepseek-v4-flash-vision-exp,以及 deepseek-v4.1-flash-expires-on-0910)走 Flash 档 ——官方说明旧名仍可调用但由 V4.1-Flash 承接、按 Flash 价计费。认不出的名字回退 Flash,不猜 pro;
- 定价规则:只有 DSH 内置的官方模型(deepseek-official)享受内置价目;其余一切 provider(第三方中转 / 自建 / OpenRouter 等)一律不计价,成本按 ¥0 计,并由 kpi.totals.unpricedTokens / unpricedModelCount 显式报出“未定价”。
之所以不再按模型名套官方价估算:中转的真实计费与官方价无关(常见高于官方),拿官方价乘第三方用量得到的数字像账单但不是账单,比老实说“没价”更容易误导。
这些模型要精确成本,请在单价表录入自定义单价 —— 那条路径不受此规则影响;
- 自定义单价两种写法都吃:新的 {peak:{…}, idle:{…}} 分时段;旧的扁平 {miss,hit,write,output} 仍可读且两档同价(设置页保存时自动升级为两档写法)。
只填高峰一档时,空闲自动跟随 —— 想要“不分时段”就只填一档。
/dsh-token/api/config 的 defaults 字段即模型键 → 官方默认价(含两档)的映射,缺席 = 该模型无内置价,页面据此显示“未定价 · 按 ¥0 计”,避免把“没价”误标成“官方价”; prices 在读取侧已归一化为两档,UI 无需分支;peakHours / peakDays 为生效值的紧凑文本, defaultsPeakHours / defaultsPeakDays 供页面“恢复默认”按钮对照。
改动价目表属于语义变更:必须同时 +1 STORE_VERSION(当前 v8),否则水位线会按 revision 跳过未变化会话,存量成本永远沿用旧价。测试 test/price.test.mjs 把官方数字与默认时段都写死为断言(改价目/改时段会立刻失败),并单独断言“这两者都能被配置覆盖”;时区口径与惰性重建由 test/host-semantics.test.mjs 锁定。
已知限制
- 规模上限参考(20 万请求 / 400 会话 / 120 模型的合成库,Node 24,2026-09 实测):落盘 = store.json 元数据约 142 KB + shards/ 约 10 MB(逐会话一个分片),请求记录全量驻留内存,堆占用约 59 MB;冷启动 JSON.parse + 分片载入约 0.12 s,rebuildAll 约 0.42 s; 0.9.5 起,有会话变更的扫描周期只重折被改动的会话(unfold 旧列表 + fold 新列表),不再整库重折,同时只重写变化的分片(改 2 个会话时约 0.19 s 的写量,而 0.8.x 是整库 19.98 MB / 0.17 s)。
端到端实测(真实 scanStore 路径,6 万请求 / 200 会话):改 2 个会话 2.7 ms,而整库重折 71.6 ms(约 27×);无变化仍是 0.2 ms。
两条路都会在 summary.aggMode 里如实报出(full / incremental / none)。
冷启动与“聚合不可用”仍走整库重折 —— 没有旧账可减时增量只会多做一轮簿记。
稳态增量扫描实测 34-52 ms(无变化时只 list 一遍比对 revision,不解码任何会话),冷启全量约 2.1 s;扫描周期可用 refresh.scanSec 调整或关掉(0)。
默认 retention.days=0 永久保留,长期大库建议开启保留期
(如 {"retention":{"days":365}})控制体积 —— 开启后稳态扫描是零写放大的。
store.json 的体积随会话数线性增长(它每个脏周期整体重写):本机 64 会话约 32 KB,即每会话约 0.5 KB;数千会话时该文件会到 MB 级,这是选分片布局换来的代价,已记录在案。
分片布局的取舍:单会话损坏只影响一个分片(其余照旧,该会话下轮从日志重折),代价是文件数为会话数(数千会话即数千个小文件)。逐请求导出(JSON)另有 10 万行上限,超过会拒绝(见路由表);
- store.json 不再含逐请求记录(0.9.0 起):它是元数据 + 索引,记录在 shards/.json。备份时请连 $DSH_HOME/dsh-token/ 整个目录一起备份(只备份 store.json 会丢掉全部用量记录,只留下配置与标题);
- 逐请求 JSON 导出的行数上限是 10 万条:20 万条会生成 68 MB 单个响应体,足以卡死标签页。需要全量请缩小时间范围,或开启保留期;
- cost/saved/priced 是普通数据属性,不是访问器(0.8.9 起):改完单价/时段后,下一次查询就会用新价(所有查询入口与落盘前都先 flush),但直接读
store.requests[id][i].cost 的代码需要自己先 flushAggregates(store);
- 通过域名反代访问仪表盘时,设置类 POST 会被拒绝(防 DNS rebinding 的代价):Host 白名单只放行回环名、私网/链路本地/CGNAT/唯一本地 IP 与回环 IPv6,域名与公网 IP 字面量一律拒绝。私网 IP 直连(如 http://192.168.1.5:3080)与 curl(无 Origin)可用;
- 仪表盘价格估算内置 DeepSeek 官方价目(¥/M),按官方页口径分档并区分时段 ——见下方「价格口径」。只有 DSH 内置官方模型(deepseek-official)套用该价目;中继/自建/第三方路由一律无内置价,成本计 0,需手动为它们录入自定义单价。未定价部分不会被藏起来 —— kpi.totals.unpricedTokens / unpricedModelCount 显式给出,单价表显示“未定价 · 按 ¥0 计”,会话面板与 KPI 卡片都会标注“含未定价模型,费用偏低”。因此“估算成本”要读成 “已定价部分的成本 + 未定价 token 数”,不是完整账单; 0.8.5 起收紧了这条口径:此前第三方模型会按名字套用官方价估算,但中转实际计费与官方价无关,那个数字像账单却不是账单。现在改为一律不计价,由“未定价”显式报出。
- 默认永久保留全部原始请求记录(会话下钻/导出依赖);设置 retention.days 可按天修剪,修剪在扫描期执行,超龄会话整段清理;
- 被宿主拒绝的老会话无法统计:部分 v0 老日志(插件注入过非标准事件成员,或子代理描述符为 v2)连 DSH 自身都拒绝迁移,插件只能跳过。这类会话进入隔离清单并计入 meta.quarantinedCount(设置页//dsh-token/api/meta 可见),可用 POST /dsh-token/api/scan 或重启触发 force 重试。它们上一次成功扫描的记录会保留在 store 里(不会静默清零),只是不再更新 —— 本机实测为 18 个会话 / 549 条记录;
- store 损坏时以“留证 + 重建”处理,不尝试原地修复:形态体检只做结构判定(字段/类型/NaN),不合格就改名成 store.json.corrupt- 再从会话日志重建。
这样不会丢数据、也不会丢证据,但配置(自定义单价、预算、保留期)会随之回默认 —— 因为它们只存在 store 里,日志里没有。想保住配置,请把 $DSH_HOME/dsh-token/ 整个目录一起备份(0.9.0 起用量记录在 shards/,store.json 只是元数据与索引;单独备份 store.json 会丢掉全部用量记录);
- 小时桶/高峰判定固定北京时间(UTC+8),不跟随宿主时区,也不可配置;日/月桶按宿主本地时区。两者口径不同是有意的:前者对齐官方计费,后者对齐用户对“今天”的直觉;
- 单价表按 DSH settings 里的模型目录过滤。命名空间形态已知两种(llm-pi-ai 的
providers..models[],以及适配器自带命名空间的顶层 models[],provider 名靠一张
小映射表补全)。任何解析不出来时一律关闭过滤显示全部,并在启动日志里打印所见的命名空间名(不含任何值)—— 所以“读不到配置”只会退回旧行为,不会把界面清空;
- 配置在 $DSH_HOME/dsh-token/store.json,用量记录在同目录 shards/(均未走 settings.register,动态插件沙箱无法构造 schemastery schema);单价表只读 DSH settings,从不写它,且只取 provider:model 标识,settings 原文(含凭据相关字段)绝不进入 API 载荷;
- 会话标题取 DSH 的自动生成标题(session/title 事件),缺省回退首条用户消息。
开发与验证
npm test # 397 项断言 + token/产物一致性检查
npm run verify # 发布前完整性校验(文件 / dsh 字段 / 模块可解析 / 内联脚本语法)
npm run e2e # 真实 http 栈端到端冒烟(临时 DSH_HOME,零依赖)
npm run tokens # 改完 lib/design-tokens.mjs 后重新注入两处界面
npm pack && npm run verify:tarball # 打包后把 tarball 解到全新目录当"新装"再验一遍
npm run verify:profile # 建干净 profile → pnpm add → 用宿主 app-boot 解析成 bundle 层
#(唯一能验 exports["./client"] 子路径可解析的一步)
node scripts/screenshot-harness.mjs # 起合成数据实例(全虚构会话),重拍商店截图用
assets/ 里的商店截图由 scripts/screenshot-harness.mjs 起一个全虚构会话数据的
临时实例拍摄(screenshots.json 按展示顺序列出)。截图永远不取自真实使用记录 ——
重拍不会带出任何真实会话标题或用量的风险。
npm test 与 npm run verify 都挂在 prepack 上,因此 npm pack / npm publish
不会漏跑。CI 除 Node 20/22 双版本外,还另有一条跨时区任务 (America/New_York / UTC / Asia/Shanghai / Pacific/Kiritimati)——
本插件有两条并存的时区口径(日/月桶按宿主本地,小时桶与高峰取档按北京时间),
只在本机(UTC+8)跑测不出任何东西:历史上真出过一次"小时图与高峰高亮在非 UTC+8
宿主上错开 8 小时",而当时断言全绿。
改配色请只改 lib/design-tokens.mjs(它注入 web/src/style.css 与 lib/client.js 的 token 块是生成物,--check 会挡住手改;web/index.html 是拼接产物,同样不要手改)。
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
fufuf-c/dsh-token
DSH 本地 Token 用量统计插件——以 Token 为第一视角:不只回答"花了多少钱、用了多少量",还回答…
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,362已汉化DSH
71.4
终端编码代理esengine/DeepSeek-Reasonix
· 指南 · ACP · 扩展开发 · 规格 · 官方网站 · Discord · QQ
★ 36,076已汉化DSH
71.2
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,814已汉化DSH
71.1
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。