← 返回列表
未验证
一个 DeepSeek Harness 插件,在侧边栏中、紧挨着 Settings 上方,显示某个 API key…
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/24 · 已提供中文文档
DeepSeek Harness 插件:一个针对此 DSH 主目录和 opencode 的日历月 token 计数器,仅使用本地数据,在每月 1 日 00:00 重置。
综合分
30.8
GitHub 分
30.8
用户评分
—
★ Stars
1
周下载量
—
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/greyhackintoch-dev/dsh-month-tokens.git信任档位:已验证本站已于 1 天前真实安装成功(L4 · 真实安装)
- 是什么
- 生态应用(桌面端 / Web 外壳,不以 dsh plugin add 安装)
- 装得上吗
- 本站已真实安装成功(L4 · 真实安装,非静态推断)
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 1 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
🟢实装验证通过· 2026/9/25
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/24(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-month-tokens
一个 DeepSeek Harness 插件,在侧边栏中、紧挨着 Settings 上方,显示某个 API key 在一个自然月内的 token 用量。它统计此 DSH home、opencode、Pen 和 WorkBuddy,只读取本地数据,并在每月 1 日 00:00 重置。
配置 trackKeys 后,这个数字就不再是某台机器的数字,而是某个 key 的数字:同一份凭据在这台笔记本、桌面 shell 和第二台机器上使用时,会汇总成一个数字——通过 key 的指纹进行关联,因此 key 本身永远不会离开其中任何一台机器。
覆盖范围是明确声明的,绝不隐含。 判断标准是这个调用方是否在这台机器上留下了真实的用量记录,而不是这个调用方是不是 DSH。参与统计的 DSH home、opencode、Pen 和 WorkBuddy 符合条件并会被读取;同一个 key 在浏览器 IDE、脚本或他人的客户端中使用则不符合条件,任何不保留本地记录的情况也不符合条件。因此这个数字是一个下限,而非总量——这一点在本文件、DESIGN.md 和 docs/cross-machine-setup.md 中都有说明,而不是在每次渲染时都显示,因为把固定说明文字放在数字旁边会被当作噪音。
有一个盲区是被测量出来的,而不是靠猜测:网页搜索路径会直接向提供商发出自己的请求,并且不在任何地方记录用量,因此无论是本插件还是 DSH 自己的 tokenUsage 投影都无法衡量它。诊断工具会统计这些调用(node tools/tracked-report.mjs),从而让这个下限有一个具体的边界,并且不会用任何估算来替代它们。
┌──────────────────────────────┐
│ ▮▮ 我的 key · 本月 3.61亿 │ ← 本插件
│ ⚙ Settings │
└──────────────────────────────┘
点击该行可查看明细:哪台机器贡献了多少、平台拆分(此 DSH home、opencode、Pen、WorkBuddy——仅当本月有记录时才显示),以及——仅当确实有内容被遗漏时——一条指明该内容的警告。按模型的拆分和该机器的历史累计桶拆分虽然会计算,但不会显示:这个面板只回答“有多少,以及来自哪里”,仅此而已。
安装
dsh plugin --profile web add github:greyhackintoch-dev/dsh-month-tokens
无需构建步骤,因此没有需要应答的 allowBuilds 审批。之后重启 dsh web。
改为通过 agent 安装
不想碰终端?把下面这段粘贴到任意 DSH 会话中:
Install the dsh-month-tokens plugin by running:
dsh plugin --profile web add github:greyhackintoch-dev/dsh-month-tokens
Then confirm dsh-month-tokens appears in ~/.dsh/profiles/web/package.json
under dsh.profile.bundles, and remind me to restart dsh web for it to take
effect.
该命令会写入 agent 工作区沙箱之外的位置,因此预计会出现一次审批提示。dsh plugin 会自行将该包协调进 dsh.profile.bundles——该 bundle 声明了 dsh.bundle,因此其 cordis.patch.yml 会被应用,无需进一步配置。
需要浏览器 shell 的 DSH(web profile)
浏览器那一半运行在一个具有真实源(origin)的页面中。由此产生两个后果:
官方 DSH Desktop 应用不受支持。 dsh plugin 会直接拒绝其保留的配置文件:
$ dsh plugin --profile desktop add github:greyhackintoch-dev/dsh-month-tokens
error: profile "desktop" is managed exclusively by the Electron application
通过其他方式在其中安装也无济于事。浏览器那一半通过一个普通的 HTTP 路由(GET /token-ledger/stream)进行流式传输,而 Electron 外壳则从 file:// 加载前端,并通过 window.__DSH_TRANSPORT__ 承载 DSH 自身客户端的所有 I/O。在 file:// 下,相对 URL 没有可供解析的主机——外壳自身的代码会检测到这一点,因为 location.origin 是字符串 "null"——而且没有任何东西将回环端口暴露给渲染进程,因此也没有可回退的绝对 URL。
嵌入标准 Web 配置文件的第三方桌面封装器则可以正常工作。 如果客户端是一个指向本地 Web GUI 的 BrowserWindow,那么它就是一个普通的浏览器上下文,上面的命令就是所需的全部操作。
它有何不同
大多数用量插件只给你显示一个数字。而这个插件还会告诉你本月有多少用量它无法归属,并且拒绝凭空捏造出这个差额。
DSH 没有提供任何按天分桶统计用量的投影——每一个随附的单元要么是累计值(tokenUsage),要么是当前状态的快照(contextPressure、contextBreakdown、sessionStats)。一个按会话累计的数字无法被还原为“其中有多少发生在 1 号之后”。因此,每个会话由第一条适用的规则决定,并且会报告所使用的规则:
| 规则 | 条件 | 结果 | 准确性 |
| --- | --- | --- | --- |
| ledger | 一个可选的按天账本覆盖了本月 | 将带有本月前缀的各天相加 | 精确 |
| born | 创建于 1 号或之后 | 其全部总量 | 精确 |
| idle | 创建于更早,最后一次用户提示在 1 号之前 | 0 | 精确 |
| split | 创建于更早且本月有过提示 | 0,并计入 unattributed | 未知 |
一个跨越边界的长时间运行会话会落入 split,而这恰恰是朴素计数器会悄然少报的情况。当任何会话落入该类别时,local.exact 为 false,local.unattributed 会说明有多少个,并且面板会以警告颜色显示这一点。
为了精确解决剩余的情况,可以添加一个按天活动投影。社区插件 dsh-context 贡献了一个名为 contextActivity 的投影;当它被挂载后,ledger 规则会自动接管,不再有任何会话处于模糊状态。在一个真实的 79 会话环境中测量,它精确覆盖了 78 个会话,唯一的例外是在该插件存在之前就已建立检查点的会话。本插件不依赖它——它是一个可选升级,没有它一切也能正常工作。
它统计什么
| 来源 | 来自何处 | 延迟 |
| --- | --- | --- |
| 此 DSH 主目录 | 按会话的 tokenUsage 投影 | 实时——每个已结算回合推送 |
| 此密钥,此主目录 | 会话日志,逐帧增量读取 | 轮询,最多滞后约 1 分钟 |
| opencode | 其自有的 SQLite 数据库,只读 | 轮询,最多滞后约 1 分钟 |
| Pen | ~/.pencil/pi-sessions/.jsonl | 轮询,最多滞后约 1 分钟 |
| WorkBuddy | ~/.workbuddy/projects//.jsonl | 轮询,最多滞后约 1 分钟 |
| 对等机器 | 它们向你的聚合器发送的报告(如果你运行了一个) | 它们的轮询间隔 |
除对等机器那一行外,其余全部为本地,并且除非你配置了报告器(role: reporter/both 并带有 aggregatorUrl),该插件不会发出任何出站 HTTP 请求。那三个客户端的调用都不会经过 DSH,因此读取它们写入的内容是查看它们的唯一方式。
为什么要读取会话日志
随附的 tokenUsage 投影无法说明哪个密钥花费了什么:其状态是四个桶和一个 (turn, step) 槽位,其中没有提供方、模型或凭据。而持久会话日志可以——request/context 记录了提供方和模型,并且每个已结算的 assistant/message 都带有自己的用量和时间戳——因此它被增量读取(日志是一系列独立的 zstd 帧,扫描会从它尚未消费的第一个字节处恢复),并按照投影自身的替换规则进行折叠,重试也包含在内。
有两个值得了解的后果:
- 月份归属会变得更好,而不是更差。 为每个事件标注日期意味着跨越 1 号的会话会被正确拆分,而不是被报告为无法归属。
- 路由名称被有意宽泛匹配。 提供方路由是一个开放集合:随附的适配器注册了 deepseek-official,而插件会注册更多(vision-toolkit-deepseek-、modlens-deepseek 都被测量为花费同一个密钥)。白名单会悄悄漏掉下一个添加路由的插件——把这件事做错的实测代价:一个月内 1,564,298 个 token。宽泛是安全的,因为桶是由密钥指纹决定的,而不是路由名称;并且任何没有目标认领的内容都会在面板中列为 uncovered,而不是被假定不存在。
一个密钥,所有机器
给两台或更多机器相同的 trackKeys,并将其他机器指向其中一台:
聚合器(一台常开机器)
- insert:
- id: token-ledger
name: dsh-month-tokens
config:
role: both
collectorHost: 0.0.0.0
collectorPort: 3939
collectorToken: !!js process.env.DSH_TOKEN_LEDGER_TOKEN
trackKeys:
- ref: DEEPSEEK_API_KEY
providers: [deepseek-official]
providerPatterns: ['deepseek']
其他所有机器
- insert:
- id: token-ledger
name: dsh-month-tokens
config:
role: reporter
aggregatorUrl: http://192.168.1.244:3939
collectorToken: !!js process.env.DSH_TOKEN_LEDGER_TOKENmarkdown
trackKeys: [{ ref: DEEPSEEK_API_KEY, providerPatterns: ['deepseek'] }]
每台机器上报自己各个桶的完整当前值,而不是增量,聚合器按 (machine, key) 保留最新的快照。因此,重发、重复、乱序到达或聚合器重启都不会虚增总量——而一台被关闭的机器会保留其最后的数值,标记为 stale,而不是从总和中消失。
聚合器监听自己的端口并使用自己的 bearer token,绝不监听 GUI 的端口。启用此功能不得要求 networkExposure,否则会连同其他所有路由一起暴露;没有 token 的收集器会拒绝启动,而不是公开提供服务。
分步部署(包括验证和故障排查表)见 docs/cross-machine-setup.md。若想完全不启动 DSH 就查看数字:
sh
node tools/tracked-report.mjs # per day, per model, uncovered routes
opencode
opencode 会为每条助手消息向 ~/.local/share/opencode/opencode.db 写入一行,其 data JSON 携带 tokens: { input, output, reasoning, cache: { read, write }, total }。这些列与本账本的术语几乎完全对应:
| 账本桶 | opencode 字段 |
| --- | --- |
| uncachedInputTokens | tokens.input — 未缓存;缓存读取单独计算 |
| cacheReadTokens | tokens.cache.read |
| cacheWriteTokens | tokens.cache.write |
| outputTokens | tokens.output + tokens.reasoning |
reasoning 是唯一需要转换的项:opencode 将其与 output 分开上报,而 DSH 将其合并计入,因此两者相加以保持各行可比。
行按 providerID(默认为 deepseek)以及 time_created >= 本月 1 日 过滤。数据库以只读方式打开,并在每次轮询后关闭——它是在活跃 WAL 写入下的第三方 schema,因此持有句柄毫无好处,反而有获取过期快照的风险。
配置了 trackKeys 后,opencode 按密钥而非名称归属。 opencode 在其自己的 auth.json 中按提供商保存原始密钥,因此两台机器可能都声称是 deepseek,而其中只有一台持有被跟踪的密钥——这在共享公司账户上是常见情况。插件会对存储的密钥进行指纹识别,仅当指纹匹配时,才将 opencode 的行归入被跟踪密钥自己的按日和按模型桶中。持有不同密钥的提供商会报告为 tracked.opencode.state: 'otherKey' 并被排除,而不是悄悄虚增你的数字。如果存储位置在其他地方,可用 opencodeAuthPath(或 DSH_TOKEN_LEDGER_OPENCODE_AUTH)覆盖。
要求: 需要 Node 22.5+ 以使用 node:sqlite。在较旧的运行时上,插件仍会运行;它会报告无法读取 opencode,而不是悄悄计为零。
Pen 和 WorkBuddy
另外两个客户端保留了真实的本地记录,读取方式相同:找到
他们存储的凭证,对其进行指纹识别,并且只读取该凭证所支付的记录行。
| | 凭证(用于指纹识别) | 使用记录 | 格式 |
| --- | --- | --- | --- |
| Pen | ~/.pencil/agent-auth — { provider: { type, key } } | ~/.pencil/pi-sessions/.jsonl | pi-ai |
| WorkBuddy | ~/.workbuddy/models.json — [{ id, apiKey, … }] | ~/.workbuddy/projects/*/.jsonl | DeepSeek wire |
Pen 的存储与 opencode 的格式相同,因此由同一份代码解析,而不是另写一个可能与之产生偏差的解析器。
WorkBuddy 的存储是其自身已配置提供者的列表,而其使用记录行完全不携带密钥——因此关联是结构性的,分两步进行:
providerData.requestModelId == 'custom-local:' + models.json[].id
└─▶ that entry's apiKey ─▶ fingerprint ─▶ is it the tracked key?
WorkBuddy 自身的网关路由(auto、hy3)从不出现在 models.json 中,因此它们会自行从该关联中排除。在一个真实存储上实测:122 行中有 100 行是网关调用——占该月的 30.6%——且没有一行进入统计数字。这种排除是结构性的;不查阅任何字段名来做出该判定。
推理 token 有三种不同的计数方式,只有 opencode 的是可累加的。 三个读取器都针对同一份测试夹具进行断言,因此这种差异不会被意外继承:
| | 原因 | 结果 |
| --- | --- | --- |
| opencode | 单独报告 reasoning | 累加到输出中 |
| Pen | input + output + cacheRead + cacheWrite === totalTokens,因此推理已包含在 output 中 | 不累加 |
| WorkBuddy | completion_tokens_details.reasoning_tokens 是 completion_tokens 的子集 | 不累加 |
同样,prompt_tokens 和 WorkBuddy 的 usage.inputTokens 包含缓存命中,因此二者都从不被用作未缓存输入;各分桶来自 prompt_cache_ 字段。
三个值得了解的局限:
1. 非实时。 opencode 在消息完成时记录使用量,因此可用的最佳频率是 60 秒轮询。DSH 数字则是真正实时的。
2. 私有 schema。 message.data 是 opencode 的实现细节,任何版本都可能更改。一个仍能查询但不产生任何 token 字段的 schema 会被报告为 drift,而绝不会被报告为一个更小的月份。
3. 提供者,而非密钥。 providerID: 'deepseek' 命名的是提供者,而不是凭证。两个工具中使用相同密钥 → 正确。在 opencode 中换入一个不同的 DeepSeek 密钥,二者就会被静默合并。
在插件的条目中覆盖位置或提供者列表:
yaml
- insert:
- id: token-ledger
name: 'dsh-month-tokens'
config:
opencodeDbPath: /custom/path/opencode.db
opencodeProviders: [deepseek]
penAuthPath: /custom/path/agent-auth
penSessionsDir: /custom/path/pi-sessions
workbuddyModelsPath: /custom/path/models.json
workbuddyProjectsDir: /custom/path/projects
DSH_TOKEN_LEDGER_OPENCODE_DB 在没有给出配置时会覆盖该路径。
面板
点击侧边栏行会打开一个面板,它回答两个问题——多少,以及来自哪里:
- 标题行:你的密钥的月度总计,汇总自所有上报该密钥的机器;
- 其下是该密钥的历史累计数字,以及其每日用量的 7 天图表;
- 然后是每台机器在本月的贡献,过期的也会保留并标注;
- 再往下,在 本月消耗 下,是本机自身用量的去向:DSH、opencode、Pen、WorkBuddy——后三者仅在确实有记录时显示。
图表使用与面板其余部分相同的、按月限定的 days 绘制,横轴为日历轴——是七天,而不是“恰好有记录的七天”。在本月内,缺失的一天是真正的零,并按零绘制;在本月开始之前,它是未知的,会被略去而不是置零,因此在月初窗口较短并会如实说明(Last 3 days、10/1–10/3)。刻度只显示日期,因为按约定 days 是按月限定的,所以轴上的每个点都在同一个月内。
它是一条手写的 SVG 路径——没有图表库,也不会为此额外传输任何数据。
路由
| 路由 | 正文 |
| --- | --- |
| GET /token-ledger/stream | SSE;每次变动发送一个 ledger 事件,另每 25 秒发送 : keep-alive |
| GET /token-ledger/summary | 与上述相同载荷的单个 JSON 文档 |
json
{
"revision": 6,
"period": { "kind": "month", "key": "2026-09", "start": 1788192000000, "end": 1790294400000 },
"month": 361262338,
"totals": { "uncachedInputTokens": 3944544, "outputTokens": 2456933, "cacheReadTokens": 269710720, "cacheWriteTokens": 0 },
"local": { "total": 346169232, "month": 346169232, "monthSource": "mixed", "exact": true, "unattributed": 0 },
"tools": {
"opencode": { "state": "ok", "messages": 98, "fetchedAt": 1790231906819,
"totals": { "uncachedInputTokens": 1700628, "outputTokens": 77406, "cacheReadTokens": 13315072, "cacheWriteTokens": 0 } }
},
"sessions": { "counted": 79, "live": 2, "skippedSeeded": 0, "scannedAt": 1790229233573 }
}
month 是标题行数字。totals 是历史累计的 DSH 分桶明细,在面板中显示在 本机历史累计(仅 DSH) 标题下。monthSource 为 ledger、born、idle、mixed 或 none;每个 tools..state 为 loading、ok、absent、drift、unavailable 或 error。
sh
curl -s http://127.0.0.1:3080/token-ledger/summary | python3 -m json.tool
为什么该周期不需要存储计数器
窗口在每次读取时都根据时钟推导:
start = 本月 1 日 00:00(本地时间)
end = 明天 00:00
没有累计总数,也没有重置任务,因此一个在 1 日午夜休眠的进程,下次查看时仍会立即报告新月份,也不会错过任何重置。此外,边界处还会触发一个定时器,使打开的面板立即切换,而不必等待下一次轮询或重新扫描。
开发
sh
npm test # node test/host.test.mjs && node test/client.test.mjs
该测试套件无依赖、离线且封闭:它将插件指向不可能存在的路径,因此它永远无法读取你真实的 opencode、Pen 或 WorkBuddy 存储。
若要挂载一个工作副本而非已安装的包,请通过绝对路径插入它——dsh 会将 insert 行内的绝对路径转换为文件 URL,而客户端模块扫描器会从入口文件向上查找最近的 package.json:
yaml
- insert:
- id: token-ledger
name: /absolute/path/to/dsh-month-tokens/lib/index.js
请使用打包产物或路径插入中的一种,绝不要同时使用两者——token-ledger 这个 id 会发生冲突,重复的路由注册会大声报错。
编辑宿主端后请重启 dsh web。 带有 patchReload: live 的配置文件会在补丁编辑时重新组合,但宿主模块是普通的 ESM 导入,会留在模块缓存中。浏览器端是从磁盘读取的,因此刷新页面就足够了。
它是如何组成的
- lib/index.js —— 宿主端。折叠 DSH 的投影,读取三个第三方存储,并提供两个路由。除 Node 内置模块外无任何依赖。
- client/client.js —— 浏览器端,为 DSH 客户端模块加载器手工打包(window.__ModuleLoader__.load({ id, factory }))。它只要求平台种子词 react 和 react-dom,向 sidebar.footer.action 列表槽注册一个条目,打开一个 EventSource,并渲染宿主计算出的完整值。浏览器中不发生任何领域折叠。
计数以中文万进制单位渲染——999、2.95万、2.77亿——悬停时显示精确整数。
成本
- 启动时以及每 60 秒进行一次冷扫描:一次 sessionPersistence.list()(一次目录遍历加上每个会话一次 stat),以及每个冷会话一次内存中的 cachedSnapshot。从不读取任何会话日志。
- 每分钟对 opencode 的 message 表执行一次只读的 COUNT/SUM。
- 空闲的重新扫描不会重新发布,因此安静的账本永远不会重新渲染已打开的面板。fetchedAt 被有意排除在变更比较之外——包含它会导致修订号每分钟永远递增一次。
安全
这两个路由无需 dsh web 的启动令牌交换即可应答,因为 webserver 的命名路由表不受该防护保护。这在这里是可以接受的:webserver 默认绑定到 127.0.0.1,负载是聚合计数而非会话内容,并且不发送 access-control-allow-origin 头,因此跨源页面无法读取响应体。如果你有意通过 networkExposure: 0.0.0.0 暴露 GUI,那么这两个路由将变得可被任何能访问该端口的人访问。
该插件从磁盘读取四样东西:DSH 自身的会话投影、会话日志(增量读取,用于密钥归属)、opencode 的 message 表,以及——当配置了 trackKeys 时——opencode 的 auth.json,它从中
它获取存储的密钥仅用于对其进行哈希。它计算的是指纹;密钥从不被返回、记录、写入或放入载荷中。没有任何东西会以写入方式打开数据库,也没有任何凭据或消息正文会进入载荷或日志行——只有计数。
运行聚合器时,通过网络传输的内容
实例标签、密钥的 sha256 指纹、分桶计数和时间戳。没有密钥,没有提示词,没有模型输出,没有会话内容。指纹仍然是派生自密钥的值,因此聚合器只记录其前 8 个字符。解析失败会以原因形式报告(missing、empty、illegalCharacters、resolveFailed、invalidPattern)——绝不会报告为更小的月份,也绝不会报告为失败的值。
许可证
MIT