bowenliang123/dsh-context
bowenliang123/dsh-contextDSH 上下文洞察面板:Context 仪表盘 + /context命令 + Context 浏览器,查看 Context的分类组成、内容详情、演进趋势、压缩/注入事件、统计等一站式 Context 全生命周期管理。
DSH 的 token 用量面板:输入框上方一条用量条,点开是计费总量、趋势图、按模型/项目/来源的分组明细与自定义日期范围,数据来自本机 DSH / Codex / Claude Code / Trae / WorkBuddy 的会话日志;填了自建服务端地址与 appKey 后,用量事件会实时上报到那个服务端,两者都没配时一个字节都不采集;另提供 token_usage 工具与 ctx.tokenReport 服务。不采集对话内容。
在 DeepSeek Harness(DSH) 里直接查看本机 token 用量:输入框上方一条用量条,点开就是趋势图、 模型排行与自定义日期范围;要往部门看板汇总时,再填「服务端地址 + appKey」开始上报。
不填就不发:未署名 / 未配 appKey 时,插件只读本机日志 —— 不采集上报事件,也不上报。
cacheRead 不是 input 的一部分¥0.00,金额绝不跨币种相加event_id 去重,只算一次token_usage / token_usage_diagnostics 工具,以及供其它插件调用的 ctx.tokenReport 服务dsh plugin --profile web add dsh-plugin-token-report@latest
dsh --profile web --no-open,用终端打印的完整地址打开浏览器不需要改任何 profile 配置:0.9.0 起本插件不再注册 sessionTelemetry 服务(那个名字由官方 OTel 后端占用),改为订阅宿主会话事件流 —— 因此与官方 session-telemetry-otel 可以同时开着,各记各的。桌面端(DSH Desktop)的安装见「在 DSH Desktop(桌面端)上安装」。
| 项 | 要求 |
|---|---|
| DSH 宿主 | 0.1.7-rc.2 或更高、0.3.0 之前(本插件已在 0.1.7-rc.2 与 0.2.0-rc.2 上实测启动) |
| 宿主模块 | 与宿主同代(@deepseek-ai/cordis ~4.0.4)。插件只吃宿主的会话事件流(@deepseek-ai/dsh-session)—— 0.9.0 起不再依赖 @deepseek-ai/dsh-session-telemetry |
| Node.js | ≥ 22.15.0 |
| profile | 需要 web profile(界面面板与 /api 数据通道在那里;headless profile 下只有上报与工具) |
上面那个版本窗口是兼容窗口,不是精确版本:宿主启动时逐个 peer 判定,任一不满足就跳过整个 bundle —— 现象是「面板不见了 + 一条也不上报」,启动日志只有一行
skipping profile bundle …, 不是报错。排查看下方「升级与常见问题」的排查表。
以下截图来自 2026-09-25 本机运行的 DSH Web 与真实会话日志,仅截取插件区域。数值是该机器当时的用量,不是模拟数据,也不代表性能基准。
点击输入框上方 TOKEN 用量 条里的「详情」,即可查看计费总量、未缓存输入、输出、缓存读、缓存命中率、调用数和会话数。趋势支持切换 Token 总量、调用数与命中率,明细支持模型、服务商、项目和会话分组,点击行可展开,超过 10 行可翻页。

除了今天、昨天、本周、最近 7 天、本月、近 30 天和今年,还可以通过双月日历选择开始与结束日期。开始与结束日期既可以点日历选,也可以直接敲进输入框(2026-09-21、2026/9/21、2026年9月21日 都认,失焦后统一成 2026-09-21);格式不对、日期不存在或开始晚于结束时,标题右侧会说明原因,「应用范围」只在区间可用时才可点。范围按本地时区计算,包含起止两天,点击「应用范围」后更新统计;生效期间按钮上直接显示所选区间(如 09-12 – 10-06)。

前置要求(宿主版本窗口、Node 版本)见上方「环境要求」。安装本身只有一条命令:
dsh plugin --profile web add dsh-plugin-token-report@latest
dsh plugin add 会自动在 ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 中登记发布包。检查它只出现一次,且没有 0.9.0 之前的旧仓内源码包名 @ai-token-report/dsh-plugin(那个名字现在只用于清理历史装机);不要再手动 insert 插件。正常列表例如:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-plugin-token-report"
]
}
}
}
不需要额外改 cordis.patch.yml:0.9.0 起本插件不注册 sessionTelemetry 服务,所以官方 session-telemetry-otel 可以继续开着 —— 它只在你主动提交反馈时上传,本插件只看 token 数值与模型名。
⚠️ 0.8.x 及更早是互斥的:那一版把自己注册成第二个 telemetry 后端(cordis 同名服务只能注册一个),所以必须在同一个
cordis.patch.yml里合并- id: session-telemetry-otel与disabled: true,否则 DSH 启动会失败并只报一行service "sessionTelemetry" has been registered at <OpenTelemetrySessionBackend>。 升级到 0.9.0 之后那一行可以删掉(留着也无害 —— 只是官方后端不再工作)。
随后重启 DSH,用终端打印的完整地址打开浏览器:
dsh --profile web --no-open
选择工作区后,输入框上方会出现用量条(0.3.0 起这是默认位置)。想让面板改到会话标题栏右上角、或两个位置都要,见下方「调整面板位置」。安装后无需单独启动本地统计网页。
dsh plugin内部调用宿主自己的包管理器。上面的安装命令用于 DSH profile;本仓开发、构建与发布使用 Bun。
桌面端有两条装法,同一套装配结果:
dsh 与它自己的 pnpm 走一遍 dsh plugin --profile web add …(headless / 排障 / 界面装不上时用,见下方「命令行安装」)。同一套步骤的交付版在仓库的
docs/桌面端安装交付清单.md(逐条命令 + 核对 + 回滚, 文件不在 npm 包里,所以这里只写路径);本节是它的说明版。
这个页面是 Desktop 随包的官方插件管理器
@deepseek-ai/dsh-client-ui-plugin-manager(本机 Desktop 里实测存在,版本0.1.7-rc.2; 侧栏那个「插件」入口与「添加插件」按钮的文案都出自它)。别与社区市场dshmarket搞混 —— 本机 profile 里两者都装着,但后者的入口叫「插件市场」(另一条侧栏项 + 设置里一张卡片)。 管理器的完整行为以它自己的 README 为准 —— 下面是与装本插件相关的那几条,引号里的文案都是从它客户端产物里逐字核出来的。
| 步骤 | 说明 |
|---|---|
| ① 侧栏「插件」 | 页面顶部「官方」是随 Desktop 提供、默认关闭的组合包;「已安装」是当前 profile 持有的组合包。 |
| ② 点「添加插件」 | 对话框标题与按钮同名,输入框标签「包名或地址」,说明是「输入插件的包名、GitHub 仓库地址或本地目录路径」。我们要填的就是包名:dsh-plugin-token-report(要钉版本就写 dsh-plugin-token-report@1.0.0)。旁边「插件安装引导和示例」里的「包名」一栏写得很直白:「插件包名即 npm 包名……社区插件的 README 安装命令中 dsh plugin add 或 pnpm add 之后的部分」,不确定就点「填入示例」。 |
| ③ 「安装源」(可跳过) | 只决定这次去哪个 npm 源问:「默认安装源」/「npm 官方源」/「中国大陆镜像源」/「自定义地址」(私有源要登录就把凭据放在本机 ~/.npmrc)。选择只记在本浏览器的 localStorage 里;dsh plugin 命令走的是 Host 配置的注册表 —— 所以在界面里换过源,不影响命令行。 |
| ④ 点「安装」 | Host 先问清 spec 指向什么(pluginManager.inspect),再跑 pnpm。被拒时输入框下方给一句话,其中与本插件有关的那条是「这个包没有声明组合包,无法作为插件安装」—— 本包自 0.9.0 起带 cordis.patch.yml,不会撞上它(见下方第二个坑)。 |
| ⑤ 点「立即启用」 | 装完给一次启用机会:启用新组合包/直接关闭对话框(关闭 = 已安装但关闭,插件不会生效)。装完点它最省事。 |
| ⑥ 重启 DSH Desktop | 管理器自己就写着「更改将在下次启动生效」。重启后面板出现在输入框上方(默认位置)即装好。 |
升级与回退:界面里没有版本选择器,spec 就是输入框里那一行(@1.0.0 才是钉版本)——
官方管理器的 README 明写:profile 安装的插件通过卸载后重新安装来升级(安装后暂不支持自动更新)。
所以升级 = 「卸载」(会要求确认)→ 「添加插件」填新版 → 「立即启用」→ 重启;命令行 add 同 version 也换不了版本。
一次只能跑一个安装 spec。
装之前请看一眼对话框里那句安全提示(大意:插件在本机以你的权限运行,来源不明的插件可能损坏 DSH 或读取你的数据)—— 这也是为什么本插件只从 npm 上那个固定包名安装,不推荐从第三方链接装同名包。
界面装不了(宿主没有受管 profile、pnpm 被拦、装完没生效要排障)时,用 Desktop 自带的 dsh 与它自己的 pnpm 走一遍。步骤 ①~⑥ 来自仓内实测记录(只在版本号上按本次发布改成 1.0.0):
① 退出 DSH Desktop(要写 profile 与 node_modules)。
② 打开一个 PowerShell,把 home、宿主入口与 Desktop 自己的 pnpm 都指过去:
# 安装目录按实际替换(本机在 D:\Program Files\DSH Desktop)
$desktopRoot = "D:\Program Files\DSH Desktop"
$desktopDsh = "$desktopRoot\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh\lib\bin.js"
$env:DSH_HOME = "$env:APPDATA\dsh-desktop\harness"
$env:PATH = "$env:DSH_HOME\.desktop-bin;$env:PATH" # ★ 用 Desktop 的 pnpm,别用系统里那个
PATH这一行不是可有可无:Desktop 的pnpm.cmd会在 pnpm 跑动期间临时排除 generation 投影, 换成另一个 pnpm 就绕开了这层保护。
③ 装包(两种 spec 二选一):
# npm 稳定版
node $desktopDsh plugin --profile web add dsh-plugin-token-report@latest
# 本地 tarball:未发布的版本 / 离线分发。
# ★ 路径用正斜杠,反斜杠会报 ERR_UNSUPPORTED_ESM_URL_SCHEME;下面的版本号按实际 tarball 替换。
node $desktopDsh plugin --profile web add file:D:/path/to/dsh-plugin-token-report-1.0.0.tgz
add 会自动在 profiles/web/package.json 的 dsh.profile.bundles 里登记包名;不要再手动 insert。
④ 官方 OTel 不需要再禁用(0.9.0 起):本插件不注册 sessionTelemetry 服务,两边可以同时开着。0.8.x 才需要在这一步往 profiles/web/cordis.patch.yml 里写 - id: session-telemetry-otel + disabled: true;升级后那两行可以删掉。
⑤ 核对(只读,不起服务):
node $desktopDsh plugin --profile web list # 期望:dsh-plugin-token-report@1.0.0
node $desktopDsh --profile web --dump-config | Select-String token-report
⑥ 重启 DSH Desktop。 面板出现即装好(默认输入框上方;位置在面板「配置」里改,保存即生效)。
| 命令行版 | DSH Desktop | |
|---|---|---|
| DSH home | ~/.dsh(或 $DSH_HOME) | %APPDATA%\dsh-desktop\harness(macOS:~/Library/Application Support/dsh-desktop/harness) |
| profile | 任意 | 只有 web —— Desktop 只暴露这一个,别的名字直接抛错 |
| 宿主 | 你自己装的 dsh | Desktop 自带一整套 harness,在 <安装目录>\resources\app.asar.unpacked\node_modules\ 下(本机实测 @deepseek-ai/dsh = 0.1.7-rc.2、@deepseek-ai/cordis = 4.0.4、随包 Node v24.9.0) |
| 包操作 | 你调 dsh plugin … | Desktop 启动时会做 profile maintenance:把非市场来源的插件迁成不可变 generation(.generations/live/<...>,一次 rename 上线),并写 dsh.desktop.generationProjection 与 pnpm.overrides;迁移失败会写 profiles/web/.generations-deferred.json 并冻结该迁移 |
其余一律相同:数据目录仍是 ~/.ai-token-report,与命令行版共用(身份 / appKey 填一次两边都生效,见上方「多套 DSH 并存」);会话日志按本机全部 DSH 的并集统计。
启动日志会打印 UI 用量面板数据通道已挂载 → GET /api/tokenReport.stats(面板位置:…)。
桌面端看不到宿主的终端输出(它写进 %APPDATA%\dsh-desktop\logs\harness.log),本次启动的完整地址(含 ?token=)
就在那行 dsh web: http://127.0.0.1:<端口>/?token=… 里。想确认界面数据通道真的在跑,就用它取一次配置路由
(/api 前面有 Host/Origin 栅栏,所以要带 Origin 与上一步拿到的 cookie):
$url = "http://127.0.0.1:<端口>/?token=<启动日志里那串>"
$jar = "$env:TEMP\dsh-cookies.txt"
curl.exe -s -o NUL -c $jar $url
curl.exe -s -b $jar -H "Origin: http://127.0.0.1:<端口>" "http://127.0.0.1:<端口>/api/tokenReport.config"
# 本机实测:200 + {"position":"dock"}
首次使用、填服务端与 appKey、看「上报调试」,都与命令行版一致,见「第一次使用」与「开启团队上报」。
dsh-plugin-token-report(不是仓内历史名 @ai-token-report/dsh-plugin)。名字写错在界面里表现为「所有注册表都没有这个名字」,在命令行里是 cannot resolve profile bundle …。cordis.patch.yml):官方插件管理器安装前会问清 spec 指向什么,没有声明组合包的包当场被拒,原话是「这个包没有声明组合包,无法作为插件安装」。本包自 0.9.0 起带这个文件,发布产物里那份的 name 是发布名。peerDependencies 是 >=0.1.7-rc.2 <0.3.0-0,放宽后的窗口从 0.7.0 起就在 npm 上(0.6.0 及更早那一版钉的是精确 0.1.7-rc.2)。宿主启动时由 dsh-app-boot 的 evaluatePluginCompatibility 逐个 peer 做 semver.satisfies(runtime, range, { includePrerelease: true }),任一不满足就跳过整个 bundle —— 日志只有一行 skipping profile bundle …,表现是「面板不见了 + 一条也不上报」,不是报错。所以:
0.1.7-rc.2(以及 0.2.x)都在窗口内,装 @latest 即可,不需要为了拿放宽窗口去手工打 tarball(tarball 只在「装未发布版本」时才用得上)。0.3.0 及以后会被跳过。那时要么等插件放宽并复验,要么用宿主自己的 allow-version … --accept-risk 强制放行(自担风险,不等于已验证)。package.json:界面里没有版本选择器(也不提供自动更新),命令行 add 同 version 也换不了版本。另外一旦 Desktop 的 generation 迁移成功,插件会被搬进不可变的 .generations/live/<...>,那时只有重新 add 才换得了版本(plugin remove 会走 Desktop 的 generation 下线流程)。不要把仓内源码目录装进 Desktop:发布包是构建产物(
index.js/client.js),而仓内目录要先bun run --filter dsh-plugin-token-report build、且依赖写的是workspace:*(指向两个从未发布的私有包)。宿主跑在 Node 上、也不会替你构建,所以桌面端只用发布包或 tarball;界面里那个「本地绝对路径 / 压缩包」入口同样指的是已构建好的发布产物。
仅查看本机统计不需要署名。未署名时,插件不采集上报事件,也不上报。 页面读取的是 DSH 已有的本机会话日志。
一台机器上同时装着多套 DSH 时(命令行版 ~/.dsh、DSH Desktop 的 %APPDATA%\dsh-desktop\harness……),
插件缺省把它们的会话日志一起统计。发现是结构驱动的 —— 候选目录里只有真的有 sessions 子目录
的才算一个根 —— 所以面板上的数字是多套 DSH 的并集,接入新的第三方客户端不需要改配置。
两套 DSH 的会话经常互为镜像(同一份日志被两边各记一份)。镜像按 event_id = <sessionId>:<seq> 去重、
只算一次,所以「并集小于各自相加」是正确结果,不是漏扫。启动日志与 token_usage_diagnostics
会逐一列出这次实际读到的根 —— 「我的数据到底读了哪几处」不会只给一个数字。
署名、连接配置与本地索引库都在同一个「数据目录」里,缺省 ~/.ai-token-report/,它与会话日志根无关,
所以多套 DSH 缺省就共用同一份身份与配置,Desktop 里不会再出现「尚未署名」,不需要任何配置:
<数据目录>/identity.json ← 署名(token 就是 appKey)
<数据目录>/plugin-connection.json ← 面板里填的服务端地址 / appKey / 间隔 / 位置 / 会话日志根
<数据目录>/project-aliases.txt ← 项目名称别名(「自定义配置」;四种离线形态共用这一份,
刻意**不**塞进 plugin-connection.json)
<数据目录>/usage.sqlite ← 本地增量索引库(日志的派生物,可删可重建)
<数据目录>/outbox/ ← 磁盘 outbox(崩溃不丢数据)
只有两种情况才需要动配置:想钉住统计范围(只看其中几处)用 dshHomes ——
面板里就能改(齿轮「配置」→「会话日志根」,保存即生效),也可以用部署配置
dshHomes 或环境变量 DSH_TOKEN_REPORT_DSH_HOMES 统一钉死;
想让某套 DSH 单独用一份身份 / 库用 dataDir(对应环境变量 DSH_TOKEN_REPORT_DATA_DIR,
只能在部署配置 / 环境变量里给 —— 换掉它等于连身份 / 库 / outbox 一起换,会让人「突然变成另一个人」)。
🚨 不要用日志根去达到「分开身份」的目的:
dshHome/dshHomes换掉的是日志来源, 那会让面板少算另一套 DSH 的会话,而实时上报照常工作 —— 这个错误不会以「完全没数据」的形式暴露。
面板、token_usage 工具与上报都覆盖本机全部已注册来源:DSH、Codex、Claude Code、
Trae(国际版 trae / 国内版 trae-cn,两个发行版算两个来源)、WorkBuddy。
没有开关要打开 —— 本机装了哪个客户端,它的用量就进面板、也会进部门看板;
没装的那些自然不会出现(诊断里会逐项报出解析到的根)。
代价必须知情:第一次取数与第一轮历史补报要冷扫这些日志(本机实测 Codex 就有 1,500 个文件 /
2.8 GB,十几秒到几分钟),之后按文件字节数增量,只解析变化过的文件。
嫌慢就把那个客户端的日志目录挪走,或者用它自己的环境开关关掉
(例如 DSH_TOKEN_REPORT_CODEX=0)—— ⚠️ 这一项刻意不在面板里:
「我不想统计 Codex」是一件部署策略级的事,不该和「我的服务端地址」放在同一个表单里。
为什么不再做成「白名单」:少统计一个来源没有任何下游信号 —— 面板数字看着完全正常, 而它与「我在那台客户端上本来就没用量」长得一模一样。采集范围不是性能偏好,是一句会被读成结论的口径。
全部来源一起算时,一屏数字回答得了「本机用了多少」,回答不了「这些几乎全是 Codex 跑的」。 工具栏上那个**「来源筛选」下拉**(就在「自定义」日期范围右边)就是干这个的:默认**「全部来源」**,选一个客户端之后整块面板只算它。
source 参数都不发 —— 于是它与这个筛选存在之前逐字节相同,老宿主也照常服务。plugin-connection.json,重启后回到「全部来源」)。筛过的这一轮不会改变下一轮「全部来源」看到的东西。sources: ['Codex'](大小写打错)在「取交集」的写法下交集为空,而空交集在 openStats 那边等于不过滤 —— 于是它会安静地返回全部来源的数(数字变大、不报错)。所以未注册的 id 从参数层到查询层一路抛错,并列出本机已注册的清单。等价入口(同一语义,同一实现):
| 入口 | 写法 |
|---|---|
| 面板 | 工具栏「来源筛选」下拉(单选;「全部来源」= 不带参数) |
token_usage 工具 | source 参数,逗号分隔:dsh / codex / claude-code / trae / trae-cn / workbuddy;all 或缺省 = 全部来源 |
| CLI | ai-token-report --source codex --source dsh(可重复,也可 --source codex,dsh;all = 全部;未注册的 id 当场报错) |
ctx.tokenReport 服务 | queryUsage({ sources: ['codex'] }) —— [] 或全量 = 全部来源 |
面板默认出现在输入框上方。想让它出现在会话标题栏右上角、或两个位置都要, 有两种办法 —— 面板内改(推荐,立刻生效),或在 profile 里改部署配置。
办法一(0.6.0 起):面板右上角齿轮「配置」→ 面板位置 → 验证并保存。 保存后面板就地换地方,不必刷新页面、更不必重启 DSH:
| 取值 | 效果 |
|---|---|
输入框上方(用量条) | 只显示输入框上方的用量条(默认) |
会话标题栏右上角(胶囊) | 只显示标题栏右上角的胶囊;点开就是同一个详情面板 |
两处都显示 | 两处都显示 —— 与 0.2.0 的外观一致 |
这一项与下面「部署配置」写的是同一个东西,只是存在本机
(<数据目录>/plugin-connection.json,缺省即 ~/.ai-token-report/,见上方「多套 DSH 并存」),并且优先于部署配置。
办法二:在 profile 的 cordis.patch.yml 里给插件加一段 ui ——
适合「IT 统一规定全公司都用某个位置」:
- id: token-report
config:
ui:
position: dock # dock(默认,输入框上方) | header(右上角) | both(两处都要)
改完刷新页面即可生效。三种取值共用同一个详情面板,数字口径完全一致。
也可以不改 YAML,用环境变量 DSH_TOKEN_REPORT_UI_POSITION 临时覆盖。
写错的值不会让面板消失:只认上面三个值,其它一律回退 dock,并在 DSH 启动日志里告警。
面板内那一栏同样只提供这三个值,选不出非法值。
在详情面板右上角点击齿轮「配置」,六个字段:
| 字段 | 说明 |
|---|---|
| 服务端地址 | 部门平台根地址(例如 https://portal.example.com,或本机自建的 http://127.0.0.1:8787)。上报地址由它推导(<地址>/api/v1/token-usage),不需要自己拼路径 |
| appKey | 管理员在平台「appKey 管理」页签发的那一串。已配置时留空 = 只改下面各项偏好,不会重新校验、也不重写身份文件 |
| 上报间隔 | 5 秒 / 10 秒(默认)/ 30 秒 / 1 分钟 / 5 分钟。这个数字直接决定部门服务端的请求密度,所以只给档位 |
| 面板位置 | 见上一节;保存后就地换地方 |
| 会话日志根 | 每行一个 DSH home;留空 = 自动发现。见上方「多套 DSH 并存」 |
| 项目名称别名(「自定义配置」) | 每行一条 项目名 = 路径或仓库名;只改本机看到的名字,不影响上报数据与部门看板。见下方「自定义配置」 |
点击「验证并保存」后,插件用这个 appKey 向对应服务端的 /api/v1/identity/verify 校验身份,
姓名与分组以服务端返回值为准(面板不再询问姓名 —— 它由 appKey 在服务端绑定的人决定)。
★ 保存后立即生效,不需要重启 DSH。 保存成功后页面会如实回报当前状态 (「已保存并开始上报 → 地址」或「上报仍未启用:原因」),并当场开始补报本机全部历史用量。 已保存的 appKey 不回显。
配置页第二个页签 「上报调试」 是排查「部门看板上没有我的数」的地方。 它每 3 秒刷新一次,把下面这些一次说清:
🚨 页面里看不到 appKey。 调试数据由宿主半的
GET /api/tokenReport.reports提供, 而宿主只保留请求体、不保留请求头 —— appKey 走Authorization: Bearer,天然不在这里。 不要为了「方便排查」把请求头加进去:那会把一个调试页变成凭证泄漏面。
身份与连接保存在数据目录下(缺省 ~/.ai-token-report/;DSH Desktop 与命令行版缺省就共用同一份、不需要任何配置,见上方「多套 DSH 并存」)。插件与本地 Web 共用身份文件。部署侧固定了身份时,页面会提示配置由管理员管理。
启用上报后,插件会在后台扫描上面那些会话日志根(缺省是本机全部 DSH home)以及本机全部已注册来源(Codex / Claude Code / Trae / WorkBuddy)下的全部历史会话,分批补报用量,直到服务器全部确认收到;不需要逐个打开旧会话。实时新用量同时上报,服务端按事件 ID 去重。断网或退出后,下次启动会继续;更换服务端地址或 appKey 后,会向新连接重新全量补报。
历史补报只发送 token 数值、模型和会话归属等统计字段,不发送对话正文。token_usage_diagnostics 会显示历史扫描进度、服务器确认数、重试错误和最近完成时间。对照本地与部门看板时,请选择相同时间范围并筛选 appKey 对应人员。
DSH 升级会保留旧格式日志作为备份;同一会话存在多个规范格式版本时,统计与补报都只读取最高版本,与 DSH 自身一致,避免事件重编号后重复计费。历史补报不会自动删除服务器上的旧数据;已由旧版本重复上报的记录需先对账、备份,再单独修复。
可以在 DSH 会话里要求:
调用 token_usage,查看我今天的 token 用量,按模型分组。
调用 token_usage,查看最近 7 天的用量,按天显示趋势。
调用 token_usage_diagnostics,检查上报是否成功、是否有待发送数据。
工具注册需要宿主提供对应能力并启用 features.tools。查询工具只读本机日志,本身不产生上报。
| 能力 | 使用方式 |
|---|---|
| 界面统计 | 用量条与标题栏入口(挂哪几个由 ui.position 决定,默认只挂输入框上方),共用详情面板 |
| 时间分析 | 预设周期、双月日历、自定义范围、趋势切换 |
| 明细分析 | 模型 / 服务商 / 项目 / 会话分组,展开与分页 |
| 费用(估算) | 面板顶部一行「费用(估算)」+ 明细表每行的金额列;token_usage 工具也给出同样一段。金额是本机按 pricing.json 快照(没有就退回内置种子价)现算的估算,与部门看板可能不同 —— 所以那行口径说明(单价来源 / 未计价比例 / 「估算 ≠ 财务账单」)永远与金额一起出现 |
| 多套 DSH 并集 | 缺省统计本机全部 DSH 的会话日志(互为镜像的会话按 event_id 去重,只算一次) |
| 多客户端来源 | 本机全部已注册来源(Codex / Claude Code / Trae / WorkBuddy)缺省就一起统计、也一起上报;装了哪个就有哪个。要收窄只能用各来源自己的环境开关(如 DSH_TOKEN_REPORT_CODEX=0) |
| 本地增量查询 | SQLite 增量索引;库不可用时自动回退日志扫描并提示 |
| 上报连接 | 面板内填服务端地址 + appKey,验证后立即生效(无需重启) |
| 上报偏好 | 面板内选上报间隔、面板位置与会话日志根;只改偏好时不必重填 appKey,保存后即时生效 |
| 实时上报 | 异步批量发送、磁盘 outbox、失败保留、重启重放 |
| 历史补报 | 独立后台线程扫描全部历史,服务器确认后保存进度,失败持续重试 |
| 上报调试 | 配置页「上报调试」页签:状态与原因、计数、最近请求体原文与回执、补报进度、立即上报 / 不发送预览 |
| Agent 与插件集成 | token_usage、token_usage_diagnostics、ctx.tokenReport |
token 数永远只展示真值;金额(估算)在面板与 token_usage 里也会出现,
但未计价的用量写「未计价」而不是 ¥0.00(「没配上价」与「没花钱」是两件事),
金额一律由宿主算好、格式化好再透传给界面 —— 浏览器半不做任何换算。
趋势图刻意没有金额曲线:多币种绝不跨币种相加,那条判定规则的唯一实现留在部门看板。
只采集用量相关字段(包含模型名、工作目录、轮次等),不采集对话内容。上报失败不会阻塞 DSH 的会话循环;服务端按事件 ID 去重。
| 内容 | |
|---|---|
| 采集 | 计费级的四类 token 数(未缓存输入 / 输出 / 缓存读 / 缓存写)、模型名、供应商、工作目录(项目)、会话与轮次标识、事件时间、来源客户端 |
| 不采集 | 对话正文、提示词、模型回复、附件与文件内容 —— 内容开关恒为关,一个字节都不读 |
| 不署名就不采 | 没有身份文件、没有 appKey,或部署侧关掉上报(features.reporting: false)时,不注册上报后端、一个字节都不发,只在启动日志里提示一次「去哪里填」 |
为什么不做 unknown 兜底 | 那等于未授权的数据采集。宁可一条不报,也不替使用者做这个决定 |
上报走 Authorization: Bearer <appKey>,地址由使用者自己填(缺省指向本机自建的
http://127.0.0.1:8787/api/v1/token-usage)。插件不向任何其它地址发送数据。
面板的「上报调试」页签能看到每一次真实请求的请求体原文与回执 —— 采集范围不必只看承诺,可以自己核。
从旧版升级时,重新运行上方带 @latest 的安装命令即可安装最新稳定版。若曾源码直挂或手动 insert,先执行下方离线修复,再重启 DSH。
从 0.5.0(或更早)升到 0.6.0 时,数据目录换了位置,需要手动搬一次家 —— 见下方「0.6.0 数据目录位置变更」。 0.5.0 的下一个公开版本就是 0.6.0,中间没有需要单独安装的版本。
| 版本 | 使用者能感知的变动 |
|---|---|
| 1.0.0 | ★ 新增「来源筛选」:面板工具栏(「自定义」右边)多一个下拉,默认**「全部来源」(请求与以前逐字节相同**),选中一个客户端之后整块面板只算它;token_usage 工具多了 source 参数、ctx.tokenReport 的查询多了 sources(同一语义,见「按来源筛选」)。未注册 / 写错的来源名当场报错并列出本机已注册清单,绝不静默变回「全部来源」。★ 同时修掉一个只在库不可用时才出现的老问题:直扫降级路径忽略了来源筛选(筛了一个来源却拿到全部来源的数字,且不报错)。另外补上桌面端界面安装(侧栏「插件」→「添加插件」) |
| 0.9.0 | ★ 不再与官方 OTel 后端互斥:插件不再注册 sessionTelemetry 服务(改订阅宿主会话事件流),装了它不必再停用 session-telemetry-otel;cordis.patch.yml 里那两行可以删掉。包名与发布名统一为 dsh-plugin-token-report(旧仓内名 @ai-token-report/dsh-plugin 仍会被修复工具清理)。peer 去掉 @deepseek-ai/dsh-session-telemetry |
| 0.8.0 | ★ 统计与上报范围都改成「本机全部已注册来源」(Codex / Claude Code / Trae / WorkBuddy 缺省就一起算、也一起报);面板里可改「会话日志根」(保存即生效);README 补齐 DSH Desktop 的安装步骤。旧的 extraSources 配置项已废弃(写了会在启动日志里告警) |
| 0.7.0 | peerDependencies 从精确 0.1.7-rc.2 放宽成兼容窗口 >=0.1.7-rc.2 <0.3.0-0 —— 宿主小版本升级不必再等插件跟进 |
| 0.6.0 | ★ 数据目录搬到 ~/.ai-token-report(升级要手动搬一次家,见下一节);面板里可改面板位置;保存即生效,不必重启;面板开始展示费用(估算) |
| 0.4.0 | 随包提供离线修复工具 repair-profile.mjs(清理 duplicate loader entry id;开发期写作 0.3.1,npm 上没有这个版本) |
| 0.3.0 | 面板默认位置改成输入框上方的用量条(ui.position: dock)—— 要保留 0.2.0 的外观就配 both |
这是升级到 0.6.0 唯一需要动手的地方。 数据目录(署名 / 连接配置 / 本地索引库 / outbox / 补报水位)
的缺省位置从 <DSH home>/token-report(通常是 ~/.dsh/token-report)改为 ~/.ai-token-report,
旧目录不会被自动迁移,也不会报错:
| 现象 | 原因 |
|---|---|
| 面板回到「尚未署名」、要求重新填 appKey | 署名与连接配置还留在旧目录里 |
| 面板数字变了(历史少了一块) | 新目录里没有索引库;日志仍在,重扫即可恢复 |
| 磁盘 outbox 里没发完的批留在了旧目录 | 待投递数据不会自己搬过来 |
先把该 profile 的 DSH 停掉,再搬(只做移动,不删除任何东西):
# Windows PowerShell —— 换过 DSH_HOME 的话,按实际路径替换 $old
$old = "$env:USERPROFILE\.dsh\token-report"
$new = "$env:USERPROFILE\.ai-token-report"
New-Item -ItemType Directory -Force $new | Out-Null
Get-ChildItem -Force $old | Move-Item -Destination $new -Force
# macOS / Linux
old=~/.dsh/token-report; new=~/.ai-token-report
mkdir -p "$new" && mv "$old"/* "$new"/
若新目录里已经有同名条目(例如升级后已经跑过一次 DSH、新的空 outbox/ 已建),
Move-Item -Force 对已存在的目录仍会报错 —— 那种情况先把新目录里的空 outbox 删掉,
或者只搬 identity.json、plugin-connection.json、state.json、backfill 这几项
(usage.sqlite 可留可删,它是日志的派生物)。
搬完核对:新目录里应当能看到 identity.json 与 plugin-connection.json;重启 DSH 后面板不再要求重新署名。
想确认生效位置,打开「配置」→「上报调试」——诊断文本会同时打印会话日志根与数据目录。
CLI(ai-token-report)与本插件共用同一个数据目录,所以搬一次两边都恢复。
插件树里重复挂载了相同 ID,Loader 在插件代码执行前就会失败。仅升级 JS 文件不能清理旧 profile。离线修复工具从 0.4.0 起随包提供(开发期写作 0.3.1,但 npm 上并没有这个版本 —— 别去装它);先停止该 profile 的 DSH,再在 Windows PowerShell 运行:
node "$env:USERPROFILE/.dsh/profiles/web/node_modules/dsh-plugin-token-report/repair-profile.mjs" --profile web
node "$env:USERPROFILE/.dsh/profiles/web/node_modules/dsh-plugin-token-report/repair-profile.mjs" --profile web --apply
设置过 DSH_HOME 时,把上面的 USERPROFILE/.dsh 路径替换成实际 DSH_HOME。第一条只检查,需修复时退出码为 1;第二条先备份,再修改。工具去掉重复 bundle 和旧源码包依赖,把插件 insert 转成 ID 配置覆盖;保留其它插件、原配置与 !!js 表达式。YAML 注释在原文备份中保留。
终端会打印备份目录(<dataDir>/plugin-backups/repair-*,缺省 ~/.ai-token-report/plugin-backups/repair-*)。遇到配置冲突、其它插件占用 ID,或全局 $DSH_HOME/cordis.patch.yml 仍重复插入时拒绝写入,需人工合并。不要删除整个 profile、身份文件、数据库或 outbox。修复后重新运行 dsh web。
尚未升级时,也可单独复制仓库构建出的 repair-profile.mjs 到故障电脑,用同样参数运行,无需启动 DSH。
0.2.0 → 0.3.0 的行为变更(唯一一处):用量面板的默认位置改成
ui.position: dock—— 默认只出现输入框上方那条用量条, 会话标题栏右侧的胶囊不再默认出现。要保留 0.2.0 的外观(两处都有), 在插件config里加ui: { position: both }。 位置配错(写了别的值)不会让面板消失:一律回退dock并在启动日志里告警。
| 现象 | 处理 |
|---|---|
启动报 service "sessionTelemetry" has been registered at <…> | 只可能出自 0.8.x 及更早的版本(它要独占那个服务名,与官方 OTel 互斥) |
面板不见了,且一条也不上报(启动日志只有 skipping profile bundle …) | 宿主版本落在兼容窗口之外 —— 见上方「环境要求」。这一行不是报错,是整个 bundle 被跳过了 |
| 没有用量入口 | 确认安装在 web profile、bundle 数组包含发布包名,并已重启;features.ui 不能关闭 |
| 桌面端(DSH Desktop)装不上 | 界面里「插件」→「添加插件」→ 填 dsh-plugin-token-report(包名必须是发布名、且包要带组合包 patch);装完点「立即启用」再重启。排障与 headless 走「在 DSH Desktop(桌面端)上安装」里的命令行步骤 |
| 401 / 未通过宿主鉴权 | 使用本次 DSH 启动时打印的完整地址重新打开 |
| 首次统计较慢 | 等待首次索引完成;如显示降级,检查 SQLite 权限与宿主 Node 版本 |
| 团队看板没有数据 | 先看配置页「上报调试」页签:它会直接说「未上报及原因」并列出最近请求与回执;再用 token_usage_diagnostics 看补报进度 |
| 改完配置没生效 | 0.6.0 起保存即生效(页面会回报状态)。若显示「需重启 DSH」,说明宿主没提供热生效入口(旧版本宿主),重启即可 |
| 升级后面板要求重新署名 / 数字少了一块 | 数据目录换了位置,旧目录要搬一次家 —— 见上方「0.6.0 数据目录位置变更」 |
源码、开发文档与构建方式见 GitHub 仓库
(0.9.0 起仓内包名与 npm 发布名是同一个:dsh-plugin-token-report —— 插件市场的 npm 映射就是按仓内 package.json 的 name 去找包的)。
以下章节针对源码直挂与二次开发,示例里的 dsh-plugin-token-report 既是仓内包名也是发布名(0.9.0 之前仓内叫 @ai-token-report/dsh-plugin)。通过 npm 安装时使用上方的安装步骤。
装在 DSH 里,无人值守地把本机产生的计费级 token 用量实时上报到部门服务端, 同时给同事一个「问一句就能看到自己用量」的工具,以及一块在 DSH 界面里 一直看得见的用量面板。
① 实时上报 订阅宿主 session/event → 账本记录 → 入队 ← 会话进行中,秒级
② 统计工具 token_usage / token_usage_diagnostics ← Agent 可调用
③ 统计服务 ctx.tokenReport ← 其它插件可调用
④ 界面面板 输入框上方的用量条 + 标题栏徽章 ← 人直接看
四种形态与 CLI ai-token、本地页面走同一套聚合与同一套口径,
所以「工具报的数」「面板上的数」「终端里的数」必然一致。
团队铺开时,每个人机器上的差异应当只有「身份」一项。其余全部来自同一份下发配置:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: token-report
# 按正式插件 bundle 已提供的 ID 覆盖配置;不重复 insert,也不覆盖包名。
config:
# ── 全局配置 ────────────────────────────────────────────────
name: ai-token-report # 插件实例名,同时上报为 client.name
appKey: !!js `process.env.ATR_APP_KEY` # ★ 上报凭证,走 Authorization: Bearer
endpoint: https://portal.example.com/api/v1/token-usage # 计费上报地址
batch:
maxRecords: 50 # 单批最大条数
flushIntervalMillis: 10000 # 定时冲刷间隔(turn 结束会额外触发一次)
timeoutMillis: 15000 # 单次请求超时
outbox:
enabled: true # 磁盘 outbox(崩溃不丢)。默认开
dir: ~/.ai-token-report/outbox
maxBytes: 33554432 # 超限丢**最旧**的批并告警
features:
reporting: true # 关掉即完全不上报
tools: true