npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-peak-balance

v0.6.1

Published

Peak/off-peak billing clock with a provider-neutral account readout (DeepSeek balance, subscription plan windows, relay credits), per-turn spend measured from the provider's own counter, and a /hist token-history scene split per provider for dsh-tui

Readme

dsh-peak-balance

ci

中文 · English

DeepSeek 峰谷计费时钟 · 实时余额 · 每轮花费 · /hist 历史用量方格图,显示在 dsh-TUI 里。

⚡ 峰时 09:00-12:00 · 距谷时 1h23m · 本轮 ¥0.0234 · 余额 ¥42.10

(顺序是 时段 → 倒计时 → 本轮 → 余额:终端一窄,最先被截断的是余额,而每轮花费是运行中一直在变的那一项。模型正在作答时,「本轮」显示的是实时花费,写成 本轮·计费中 ¥…,随每次用量上报刷新;一轮结束后定格为 本轮 ¥…。)

开启峰时警告模式后,处于高峰时段时这一行会变成一个按所选颜色脉动闪烁的边框:

╭──────────────────────────────────────────────────────────╮
│ ⚡ 峰时 · 距谷时 1h23m · 本轮 ¥0.0234 · 余额 ¥42.10   ▂▃▄▅▆▇ │
╰──────────────────────────────────────────────────────────╯

输入 /hist(或 /tokenhistory;/th 也行但要带个空格,见下文)打开历史看板——一个占满终端的场景,形状像 GitHub 的贡献图:

╭─ 🐋 Token 历史  总 token  26w  子代理 计入 ────────────────────────────── ✕ ─╮
│ 更新于 12:04:11 · 341 会话 · 6,706 事件 · 9 活跃天数                          │
│ ───────────────────────────────────────────────────────────────────────────── │
│      6月      7月      8月      9月                                           │
│ Mon  ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢                                               │
│ Wed  ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢                                               │
│ Fri  ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢                                               │
│                       ▲                                                       │
│ 总 token 少 ▢▢▢▢▢ 多 · 峰值 251,442,584                                       │
  ╭─ 2026-09-11 周五 ───────────────────────────────────────────────────────╮
  │ token       未命中输入 2,065,340 · 缓存读 224.3M · 输出 1.5M              │
  │ 花费        ¥18.8494                                                      │
  │ 缓存命中    99.1% · 子代理占比 12.4%                                      │
  │ 模型        deepseek-flash 227.9M · deepseek-v4-pro 3.2M                  │
  ╰───────────────────────────────────────────────────────────────────────────╯
│ 总计        1,079,834,040 · 总花费(估) ¥64.6867 · 缓存命中 99.0%              │
│ 子代理      36/341 会话 · 945 事件 · 峰值日 2026-09-11                        │
│ ───────────────────────────────────────────────────────────────────────────── │
│ 模型                             总 token    花费(估)   命中率 费率来源       │
│ ───────────────────────────────────────────────────────────────────────────── │
│ deepseek-flash                     587.3M      ¥33.94    99.3% 内置价目       │
│ deepseek-v4-flash-vision-exp       412.6M      ¥24.53    98.8% 内置价目       │
│ deepseek-v4.1-flash-expires…        68.2M           —    98.5%     未知       │
│ deepseek-v4-pro                      8.0M       ¥4.28    95.4% 内置价目       │
│ deepseek-v4-flash                  457.2k     ¥0.0975    90.1% 内置价目       │
│ ←/→ 前后周 · ↑/↓ 前后天 · t 今天 · m 指标 · w 跨度 · s 子代理 · r 刷新 · q/Esc │
╰───────────────────────────────────────────────────────────────────────────────╯

功能

| 功能 | 说明 | | --- | --- | | 峰谷时钟 | 显示当前计价时段与切换到下一时段的倒计时(北京时间周一至周五 09:00-12:00、14:00-18:00 为高峰,周末全天谷时)。 | | 账户读数(通用) | 按当前对话实际使用的 provider 显示余额或套餐额度:官方余额、订阅套餐的 5 小时/周/月窗口、中转站的额度与限额。provider 由会话日志的 request/header.config.provider 判定,base URL 与密钥引用经宿主接缝解析;开机恢复的对话在发出第一个请求前不会报告自己的路由(宿主重放历史时不再发事件),此时 auto 跟随宿主持久化的 /model 路由——也就是下一个请求将要走的那条。没有可查接口的 provider 直接不显示,绝不猜数字。 | | 每轮花费 | 刚结束那一轮对话的花费:provider 有消费计数器时显示实测扣减(如 Command Code 的 credits、OpenRouter 的 key 用量、中转站的已用额度),否则按价目估算,都没有就显示「费率未知」。数字跟着你当前聚焦的对话走。 | | 额度诊断 /quota | 一条命令看清算的是什么、谁在应答、上次为什么失败,以及本进程能路由到的全部 provider(/quota check <provider> 现场探测)。 | | 峰时警告 | 可选。高峰时段生效时,状态行变成圆角边框,边框、时段标签与右侧波形按设定颜色脉动。 | | 历史方格图 /hist | 全屏场景:一天一格、按当天用量深浅着色,鼠标悬停出当日明细;键盘按方格挪动(←/→ 前后一周、↑/↓ 前后一天、t 回到今天),另有 m 指标、w 跨度、s 子代理、r 刷新、q/Esc 返回。 | | 总计与模型维度 | 总计:总 token、总花费(估)、缓存命中率、活跃天数、会话数、子代理占比、峰值日;模型表:每个模型的总用量、总花费、总缓存命中率与费率来源。总计行统计全部历史(不随方格显示的跨度变化),行内标注「全部历史」以说明这一点。 | | 自定义费率 /hist price | 内置价目表没收录的模型由你自己补单价;未设置前只显示 token 并标注「费率未知」,绝不猜金额。 | | 跟随界面语言 | 与 dsh-tui 的 /lang 即时联动:状态行、历史场景、命令回执都跟着切(宿主把选择写进 dsh-tui 设置命名空间,插件监听 settings/updated 事件;没挂该命名空间的宿主由 1 秒轮询 ~/.dsh-tui/lang.json 兜底)。设置卡片与命令补全描述本来就中英双语。 | | 设置子页 | /settings 的 Peak & Balance(峰谷与余额) 卡片新增 Token history(历史用量) 子页,共 10 个选项(含场景版式与色阶)。 |

安装

# 从 npm 安装
dsh plugin --profile dsh-tui add dsh-peak-balance

# 或直接从本仓库的本地目录安装(pnpm 会把目录打包复制进 profile)
dsh plugin --profile dsh-tui add file:/到本仓库的绝对路径/dsh-peak-balance

该命令会把 bundle 行追加进 profile 的 dsh.profile.bundles。之后在 dsh-tui 内执行 /restart 让 profile 载入新行;重启后 /settings 即可看到设置卡片。

不建议用符号链接(link: 或目录联接)方式安装:Node 会按插件的真实路径解析依赖,@deepseek-ai/* 必须能从该路径向上找到。file: 与 npm 安装都会在 profile 内留下真实目录,这正是宿主期望的布局。

更新本地 file: 安装:pnpm 会缓存本地目录依赖,只跑 add/update 不会拾取改过的源码。请先升版本号,再卸载重装以刷新 profile 内的副本:

dsh plugin --profile dsh-tui remove dsh-peak-balance
dsh plugin --profile dsh-tui add file:/到本仓库的绝对路径/dsh-peak-balance

设置项(主卡片四项 + 历史用量十项 + provider 额度七项)

主卡片:

| 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | Show balance / 显示余额 | boolean | true | 状态行是否显示账户那一段(余额或套餐额度)。 | | Show per-turn cost / 显示每轮花费 | boolean | true | 是否显示当前这轮的花费:作答进行中显示实时估算,轮次结束后定格(provider 有计数器时显示实测扣减)。 | | Peak-hour warning / 峰时警告模式 | boolean | false | 高峰时段是否把状态行变成闪烁边框。 | | Warning color / 警告色系 | select | red | 边框颜色:红 red、橙 orange、黄 yellow、绿 green、青 cyan、蓝 blue、紫 purple。 |

Provider quota(第三方 provider 额度) 子页:

| 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | Quota provider / 额度来源 | text | auto | auto 跟随聚焦对话的 provider;恢复的对话还没发出第一个请求时,先跟随宿主持久化的 /model 路由;填 provider id(如 commandcode、openrouter)固定其中一个;off 隐藏账户那一段。 | | Quota metric / 额度口径 | select | auto | auto(最紧的窗口)、balance、window5h、windowWeekly、windowDaily、windowMonthly、planRemaining、keyLimit、periodSpend、lifetimeSpend(累计已用)、rotate(轮换,每 8 秒换一个口径)。 | | Turn spend / 每轮花费口径 | select | auto | auto(有计数器就实测)、measured、estimate。 | | Unofficial endpoints / 允许非公开端点 | boolean | false | 是否允许读取厂商未公开文档的额度端点(多为逆向控制台接口)。 | | Provider spec file / provider 声明文件 | text | 空 | 描述本包未内置适配器的 provider 的 JSON 文件;留空用 ~/.dsh-tui/dsh-peak-balance-providers.json。 | | Billing mode / 计费方式 | select | auto | 账户读数的形态:auto 按 provider 的声明(订阅=百分比、按量=金额),也可强制 money(金额)或 plan(百分比)。 | | Percentage base / 百分比基数 | select | meter | 套餐百分比的分母:meter 用状态行当前显示的那个窗口,monthly 用整个月度额度。 |

Token history(历史用量) 子页:

| 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | Grid metric / 方格图口径 | select | tokens | 方格深浅表示什么:tokens 总 token、cost 花费、output 输出 token、cacheMiss 缓存未命中输入。 | | Time span / 时间跨度 | select | 26 | 方格图显示多少周:13 / 26 / 53。 | | Count subagents / 计入子代理 | boolean | true | 子代理会话也真实消耗 token;关掉后只统计你自己跑的对话,总计行会写明排除了多少。 | | Week starts on / 每周起始日 | select | mon | 方格图第一行是 mon 周一还是 sun 周日。 | | Grid palette / 方格色阶 | select | github | github GitHub 绿、blue 蓝色系、theme 跟随当前主题强调色。 | | Scene layout / 场景版式 | select | card | card 圆角边框卡片 + 分区辅助线;plain 无边框(省 2 行 4 列)。终端太小时 card 自动退化为 plain。 | | Hover: tokens / 悬停:token 分项 | boolean | true | 当日明细是否显示 未命中输入 / 缓存读 / 输出 分项。 | | Hover: cost / 悬停:花费 | boolean | true | 是否显示当天估算花费。 | | Hover: cache hit rate / 悬停:缓存命中率 | boolean | true | 是否显示当天缓存命中率。 | | Hover: models / 悬停:模型明细 | boolean | true | 是否显示当天用了哪些模型。 |

设置写入 dsh-peak-balance 命名空间,改完立即生效,无需重启。场景内的 m / w / s 就是改设置(会写回 dsh/settings.yaml 并立即生效,重开 TUI 仍然有效):按下去先本地翻转,写入失败时只保留本次会话内的改动并记一条日志。

/hist 历史用量与花费

/hist                                 # 打开方格图(推荐,不会与内置命令冲突)
/tokenhistory                         # 同上(长别名)
alt+h                                 # 同上(不用打字;聊天状态下生效)
/th                                   # 也行,但要打「/th 」带一个空格,见下
/hist price                           # 列出已自定义的费率 + 费率未知的模型
/hist price set <model> <hit> <miss> <out> [peakHit peakMiss peakOut]
/hist price rm <model>
/hist price clear
/quota                                # 额度诊断:来源、适配器、口径与上次失败原因
/quota check <provider>               # 现场探测一个 provider(provider 省略则用当前目标)
/quota meter windowWeekly             # 切换状态行口径(等于改设置)

为什么 /th 单独回车会去切主题

这是宿主行为,插件改不了,如实说明:dsh-tui 的输入框在斜杠补全菜单打开时,回车执行的是当前高亮的那条建议,不是你打的那行字(PromptInput.js 的 handleEnter);而补全列表是「内置命令在前、插件命令追加在后」,且打字时选中项固定回到第 0 条。打 /th 时匹配到 theme、thinking 和我们的 th,高亮落在 theme → 回车就切了配色。

可用的四条路:

| 入口 | 说明 | | --- | --- | | /hist | 推荐。hist 不是任何内置命令的前缀,打 /hist 时菜单里只有它自己,回车直接执行。 | | /tokenhistory | 同样不冲突。 | | alt+h | 聊天状态下直接打开场景,不用打字。 | | /th + 回车 | /th 后面跟一个空格:菜单会因为「已经是子命令位置」而关闭,回车就走 th 命令。 |

注意:如果将来装了名字以 hist 开头的技能(skill),它也会以同样的方式抢 /hist 的回车——那是同一套宿主补全逻辑,届时换个名字即可(代码里是一个常量)。

键盘

| 按键 | 作用 | | --- | --- | | ← / → | 挪到左边/右边那一格(同一星期几,前后一周) | | ↑ / ↓ | 挪到上边/下边那一格(同一列,前后一天) | | t | 回到今天 | | m / w / s | 切换 指标 / 跨度 / 子代理(会闪烁对应徽章并写回设置) | | r | 重新扫描 | | q / Esc | 返回对话 |

到方格图边界或「未来」的格子会停住(不绕回)。上下左右都是纯几何挪动,不再按「天/周」两个维度跳。选中格会被提亮,并在方格图下方用 ▲ 指出所在列。

子代理开关的反馈:场景标题栏有常驻徽章(子代理 计入 / 子代理 不计入,计入时是绿色),按下 s 会反色闪烁 1.2 秒;关掉时总计行还会写明「已排除 N 会话 / M 次上报」,而不是只让数字变小。

数据来源是本机会话日志($DSH_HOME/sessions/)。日志里每一个 assistant/message 事件都带着 DeepSeek 为该次请求返回的用量(inputTokens / cacheReadTokens / outputTokens / cacheWriteTokens),插件按事件自身的时间戳落到北京时间的自然日与高峰/空闲档,再按模型归档。因此:

  • token 是官方上报的真实数字,不是本地估算;
  • 金额是估算(官方 API 只返回 token,不返回钱),用内置价目或你自定义的单价换算,并明确标注;
  • 只覆盖本机 dsh 的用量,其他客户端或网页版的调用不在其中;
  • 覆盖范围随本机日志保留策略而定(本机现有日志从 2026-09-04 起)。

子代理默认计入(它们花的是真钱),表里可分辨,也可以关掉。

未收录模型(内置价目表没有的)只显示 token,金额显示「—」,等你在 /hist price set 里给定单价后才参与计价。自定义费率立即写入 ~/.dsh-tui/dsh-peak-balance-rates.json,并同时影响历史看板与状态行的每轮花费。<hit> <miss> <out> 是空闲时段的三个单价(元/百万 tokens),高峰时段默认按官方规则取两倍;要给高峰单独定价时再补三个数字。模型名可以写成 provider:model(如 commandcode:deepseek/deepseek-v4.1-flash),这样同一个模型在不同账户下可以有不同单价;不带前缀的写法始终有效。

准确性的两个实现细节(都能在真实日志上复现):

  1. 会话日志是多帧 zstd(每次追加一个独立帧)。zlib.zstdDecompressSync 只解第一帧,而"扫魔数切帧"会在压缩块内部偶然命中魔数——此时截断解码仍会「成功」并返回残缺内容,静默丢事件。本插件改为解析 zstd 帧头与块头精确计算帧长;在本机 341 份日志上,旧写法丢了 282 个事件,新写法全部取回。
  2. fork / rewind 出来的会话日志会物理携带父会话的事件前缀。插件按日志头的 seedLength 截断(首个 session/end-seed 事件即落在此处),否则会把父会话的用量重复计一遍——本机 9 份 seeded 日志里是 292 条用量事件(约 4.5%)。

缓存:插件激活时就读取增量缓存并直接画出方格图,所以打开场景是瞬时的;随后的扫描只刷新变化的部分。缓存分三层增量:按 (路径, 大小, mtime) 跳过没变的日志;日志只被追加时,从缓存记下的「已折到第几帧」(done,附前缀尾部 16 字节签名校验)继续,只解压新增的帧;记录结构升级时由迁移函数就地改写旧缓存,而不再整份重扫。本机实测(437 份日志 / 112 MB / 19 万帧):最大的那份日志追加后,重扫从 314 ms 降到 17 ms;插件升级触发的首次扫描从整份重建降到 81 ms。内容已是最新时整轮约 20~30 ms。缓存写在 ~/.dsh-tui/dsh-peak-balance-history.json(可安全删除,删了会自动重建);真正慢的只有本机第一次全量重建(约 5~6 秒,其中约 4.3 秒是 19 万个小 zstd 帧的逐帧解码,这是 Node 端的硬开销),它会边扫边落盘,并且边扫边出图——最新的日志先扫,打开场景不到一秒就能看到最近数周的真实数据,中途被打断也能续扫而不是重来。场景打开期间每 60 秒在数字背后静默刷新一次(状态行显示「刷新中」),关掉即停。

数字是怎么来的

时段:空闲时段价格为高峰时段的一半;高峰为北京时间(UTC+8)周一至周五 09:00-12:00、14:00-18:00,其余时间(含周六日全天)为空闲时段。

花费:DeepSeek API 只返回 token 用量、不返回金额,因此每轮花费是估算:

费用 = 未命中输入 token × 未命中单价 + 缓存命中 token × 命中单价 + 输出 token × 输出单价

三项输入侧数字是并列不重叠的:inputTokens 是未命中缓存的提示词,cacheReadTokens 是命中缓存的部分,服务商返回的 totalTokens 正是二者相加再加输出。若把命中量当成输入的子集去相减(旧写法),会把带缓存的轮次价格算低数倍 —— 2026-09-10 用真实余额扣减核对:实际扣 ¥0.20 的一轮,旧写法估 ¥0.03,上式估 ¥0.23。

每条用量按其请求发生的时刻落入高峰或空闲桶,跨时段的一轮不会被整体按当前时段计价。内置价目(元/百万 tokens)核对日期 2026-09-10,来源为官方模型 & 价格页:deepseek-flash 空闲 0.02 / 1 / 4,高峰 0.04 / 2 / 8;deepseek-v4-pro 空闲 0.15 / 4.5 / 13.5,高峰 0.30 / 9 / 27。旧模型名 deepseek-v4-flash、deepseek-v4-flash-vision-exp 按 Flash 价计费;deepseek-v4-pro 自北京时间 2026-09-14 12:00 起路由到 V4.1-Flash,按 Flash 价计费。价目表未收录的模型显示「费率未知」,只显示 token 而不给错误金额。

余额:GET https://api.deepseek.com/user/balance(与 dsh-tui 内置 /balance 同一只读接口)。密钥经 credentials 接缝读取 DEEPSEEK_API_KEY(可回退环境变量),仅放入请求头,不写日志、不落盘。

第三方 provider 额度

0.4.0 起,账户那一段不再写死 DeepSeek。插件按下面的顺序决定「问谁、怎么问」,任何一步拿不到就静默降级:

  1. provider —— 当前聚焦对话最近一次 request/header.config.provider(auto 模式;也可用设置固定或关掉)。开机恢复的对话还没上报过任何 header,此时 auto 改用宿主持久化的 /model 路由(~/.dsh-tui/model.json,即选择器在重启后重新应用的那个文件);内置的 deepseek-official 默认只作最后兜底(宿主完全没保存过选择时)。
  2. 端点与密钥 —— provider 自己的设置段(ctx.llm.listConfigurableProviders() 给出的命名空间指针)→ 声明文件 → 内置目录快照。密钥经 credentials 接缝解析,失败回落同名环境变量。
  3. 适配器 —— 按下表选择;都不匹配就显示「无接口」(只有你显式指定了该 provider 时才显示这一行)。

内置适配器

| 适配器 | 覆盖的 provider | 口径 | 是否经真实账户核对 | | --- | --- | --- | --- | | deepseek-balance | deepseek-official / deepseek | 余额(按接口回报的币种) | ✅ 是 | | commandcode-plan | commandcode | 5 小时 / 周窗口、月度套餐余量、本期消费 | ✅ 是(GOAT 套餐实测) | | openrouter | openrouter | 余额(credits − usage)、key 限额、日/周/月用量 | ⚠️ 未核对(按官方文档实现,夹具测试) | | moonshot-balance | moonshotai / moonshotai-cn | 余额(.cn 为 CNY,国际站为 USD) | ⚠️ 未核对 | | siliconflow-balance | siliconflow 路由 | 余额(接口未标注币种,按原值显示) | ⚠️ 未核对 | | openai-billing | 组合里声明的网关/自建路由(One API、New API 等) | soft/hard_limit_usd(两字段同值,是总额度)减去 total_usage(美分);单位跟随网关自己的显示设置 | ⚠️ 未核对 | | declared | 声明文件里的任意 provider | 由声明决定 | — |

OpenRouter 的 /credits 需要管理密钥(普通 key 会 403);插件会同时请求 /key,因此只有普通 key 时仍然显示 key 限额与用量。

套餐按百分比显示

账户那一段的形态跟着 provider 的计费方式走:按量计费(有货币余额、按 token 扣钱)显示金额;订阅套餐(带上限的滚动窗口 + 月度池)显示百分比 —— 状态行给出「还剩多少」,以及「本轮用掉了这个窗口的百分之几」。判定顺序是:设置项 Billing mode 的强制值 → provider 自己的声明(适配器或声明文件里的 billing)→ 按数据推断(有上限窗口且无货币余额即视为套餐)。推断刻意保守:形状不认识的按金额显示,而不是凭空造一个分母。

百分比的分母由 Percentage base 决定:默认是状态行当前显示的那个窗口(最紧的限额,重置倒计时也在那一行),也可以改成整个月度额度;两边互相兜底,所以只报其中一种的 provider 也有可用的分母。没有上限就没有百分比,此时如实退回绝对数值。

🌊 谷时·半价 · 周末 · 距峰时 18h26m · 本轮 1.79%(0.2500) · 套餐 GOAT · 5h 剩 92.9%(1.00/14.00) · 距重置 1h00m

窄终端只显示百分比:每个片段都带一个紧凑写法(本轮 1.79%),整行放不下时从右侧开始逐个换成紧凑写法(宿主正是先截尾)。宽度优先取宿主的终端尺寸钩子,没有就用 process.stdout.columns;两者都没有时按完整写法渲染,与旧版本一致。

百分比精度随量级变化:小于 10% 给两位小数(订阅的一轮常常不到 1%,一位小数会看起来不动),10% 及以上一位小数,更小的显示 <0.01%,真正的零显示 0%。

声明文件(未内置的 provider)

~/.dsh-tui/dsh-peak-balance-providers.json(可安全删除;坏了就当作空配置):

{
  "version": 1,
  "apiBases": { "my-relay": "https://relay.example.com" },
  "allowUnofficial": false,
  "providers": {
    "my-relay": {
      "adapter": "declared",
      "auth": { "kind": "bearer", "apiKeyEnv": "MY_RELAY_KEY" },
      "requests": [
        {
          "path": "/api/user/self",
          "headers": { "New-Api-User": "1" },
          "meters": [
            { "id": "balance", "kind": "money", "currency": "USD", "value": "data.quota", "scale": 0.000002 },
            { "id": "periodSpend", "kind": "money", "currency": "USD", "used": "data.used_quota", "scale": 0.000002 }
          ]
        }
      ],
      "spendCounter": "data.used_quota",
      "spendUnit": { "kind": "money", "currency": "USD" }
    }
  }
}

点路径支持数组下标(data.0.results.0.amount);scale 用来做单位换算;resetAt 支持 ms / s / iso / remainingMs / remainingS。New API / One API 的账户接口用的是网页 access token,而不是 sk- 中转 key,所以要在凭据库里另存一份(上例的 MY_RELAY_KEY),并给 allowUnofficial 或全局的「允许非公开端点」开关打开——那是控制台侧接口。

其他常见写法(同样用声明文件即可,不必改代码):

| 目标 | 关键字段 | | --- | --- | | Anthropic 组织级成本(需 admin key) | GET /v1/organizations/cost_report,金额路径 data.0.results.0.amount,单位是分(scale: 0.01),币种 USD | | MiniMax 编程套餐 | GET https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains,model_remains.0.current_interval_usage_count / ..._total_count,重置 remains_time 用 resetUnit: "remainingMs" | | 智谱余额 | GET https://open.bigmodel.cn/api/biz/account/query-customer-account-report,data.availableBalance(CNY) |

兼容性

| 项 | 值 | | --- | --- | | 宿主 | @deepseek-harness-tui/dsh-tui 0.10.x(ctx.tuiStatus.registerView、ctx.tuiSettingsSections.register、ctx.tuiScenes.register/open、ctx.commands.register、ctx.tuiCommandTrees.register、ctx.tuiShortcuts.register) | | Harness | @deepseek-ai/dsh 0.1.2-rc.1+(session/event、settings、credentials) | | 运行时 | Node ^22.19 \|\| >=24,纯 ESM,无原生依赖(多帧 zstd 用内建 node:zlib) | | Manifest | manifestVersion 0.15 · id com.dsh-tui-ecosystem.dsh-peak-balance · 契约 tui.dsh/v1alpha1#DecisionEvents(optional)+ commands.dsh/v1alpha1#Command(required)· 四条命令贡献(/hist、/th、/tokenhistory、/quota) | | 平台 | dsh-tui 能跑的平台(Windows / macOS / Linux) |

所有宿主接缝都是软探测(ctx.get(name, false)):缺少 TUI 扩展服务、缺少 credentials 服务或没有网络时,插件保持静默而不是让宿主失败;注册会在 profile 组合期间重试 30 秒,所有定时器都由 activation 的 effect 清理。

命令注册走中介面优先:ctx.tuiPluginHost.registerCommand(带已校验的组件身份与调用检查点)→ 拒绝时回退到 ctx.commands.register(生态文档里的 C-070 边界)。以普通 profile 行加载的第三方插件没有 verified identity,所以实际生效的是回退路径;两条路径都失败时只记一条日志,插件其余部分照常工作。

alt+h 走 ctx.tuiShortcuts.register(ctrl/alt 必需、保留组合会被宿主拒绝并返回 no-op):宿主只接受非保留组合,alt+h 未被 alt+v/alt+up 等内置键占用。

语言:插件按宿主正在显示的语言渲染,解析顺序与 dsh-tui 一致 —— DSH_TUI_LANG → 运行时 dsh-tui.lang 设置 → ~/.dsh-tui/lang.json → 系统 locale(locale 缺失时沿用历史默认 zh;存在但不支持的语言一律回落到 en,与 dsh-tui 相同)。/lang 切换通过设置服务的 settings/updated(ns, next, prev, source) 事件即时重绘状态行与已打开的历史场景;宿主没有提供该命名空间时,退化为每秒轮询持久化文件(用 mtime/size 指纹判断,没变就只做一次 stat)。DSH_PEAK_BALANCE_LANG_FILE 可覆盖该文件路径,供测试与诊断使用。

已知限制

  • 输入框边框本身无法由插件改色:dsh-tui 0.10 的输入框边框由其内部 EffortInputBorder 组件独占渲染,没有对插件开放的接缝。因此警告边框是渲染在对话栏正上方的状态贡献——这是不改宿主源码能做到的最接近效果。
  • 花费为基于 token 用量的估算,实际扣费以 DeepSeek 平台账单为准。
  • 价目表内置在包内,官方调价需要插件更新;未收录的模型需要你自己用 /hist price set 补单价。
  • 账户那一段依赖 provider 自己的接口:没有公开额度接口的 provider 不会显示任何数字(OpenAI、Gemini、Anthropic 预付费余额、GLM/Kimi 编程套餐、Claude 订阅等),而不是显示一个猜出来的值。可以用声明文件接入自家部署或未内置的接口。
  • 恢复的对话在发出第一个请求之前,账户数字来自宿主的 /model 路由,而不是这个对话本身:dsh-tui 重放已恢复会话的历史时不发会话事件,所以还没有任何东西报出这个对话自己的 provider。若该对话被单独固定到与 /model 选择不同的 provider,它的第一个请求会把状态行纠正过来。
  • 全新环境、还没发出任何请求时会落到 deepseek-official 路由:既没有 ~/.dsh-tui/model.json 偏好、也还没有任何会话事件时,宿主只报得出这一条路由。所以你若打算用别家 provider、又没配 DeepSeek 密钥,账户那一段会先显示「未配置密钥」,直到你的第一个请求报出真正的 provider——不会出错,那个请求之后状态行自己会纠正。
  • 只有 DeepSeek 官方与 Command Code 两个适配器经过真实账户核对(见上文表格);其余具名适配器按各自官方文档实现,并用文档/源码里的夹具 payload 测试,未经真实账户核对。
  • 非公开端点默认关闭:spec 里声明 "allowUnofficial": true 的 provider,只有在全局的 allowUnofficialQuota 也打开时才会被访问(如逆向的控制台接口)。Command Code 的 /alpha/* 不受该开关限制——那是官方 CLI 自己走的端点。
  • 不内置 Command Code 的价目表:它的模型目录不带价格、价格页是前端渲染的,编一份出来就是猜。订阅套餐里的模型在 /hist 只显示 token,直到你用 /hist price set commandcode:<model> … 给出单价;状态行的「本轮」仍然是实测扣减。
  • /hist 的金额一律是「元」:成本由价目表算出(内置价目或 /th price 自定义费率,单位都是元/百万 token),所以合计行、按 provider 分列与模型表都按 ¥ 显示。credits 是状态行上账户自己的额度口径,不参与成本列。
  • 历史缓存结构升级会被就地迁移,首次扫描仍然要几秒:记录结构变化时插件用迁移函数改写旧缓存(不重扫日志),无迁移路径的旧版本才整份丢弃——0.5.1 的那次丢弃就是「升级后第一次 /hist 要等十几秒」的来源,现在不会再有。真正慢的只有本机第一次全量扫描(本机 437 份 / 112 MB 约 5~6 秒;其中约 4.3 秒是 19 万个小 zstd 帧逐帧解码的固有开销),期间边扫边落盘、边扫边出图:最新的日志先扫,不到一秒就能看到最近数周的真实数据。
  • 新 fork 的会话日志在只有 2 帧时无法做帧级续读:这类日志的第 1 帧是会话头、第 2 帧才是 session/end-seed 截断标记,只看到第 1 帧时无从确定截断点,插件会选择整份重读(代价以百字节计)。
  • /quota 的 provider 列表来自 ctx.llm.listConfigurableProviders()、内置目录快照与声明文件;组合里声明但宿主当前不可路由的 provider 可能不在列表里。
  • 余额按接口回报的币种显示:接口在 currency 里给出币种(CNY / USD …),状态行使用对应符号(¥ / $);未知币种回退成 ISO 代码(如 12.34 CHF),不会把美元当人民币显示。
  • 富状态视图与其他插件共享 6 行预算,本插件占用 3 行,且仅在警告边框显示时占用。
  • 状态行显示的是宿主当前聚焦的那个对话:切换对话后它跟着切。已结算的轮次按对话记录(不是按会话对象),所以切走再切回来仍然能看到那个对话自己的上一轮花费;只有该对话在本进程内确实还没有已结算的轮次时才显示 本轮 —(新开的 /new 会话就是这种情况),而不是拿别的对话的数字顶上。
  • 子代理的花费计入「本轮」:子代理会话(origin: subagent / delegationDepth > 0)的用量会按会话头里的 parentSession 折叠回发起它的那个对话正在进行的轮次;父轮次结束后才回来的后台子代理不再计入(那一轮已经结算)。宿主没有给出父子关系时按旧行为忽略。注意 /hist 的总量与状态行口径不同:历史默认把子代理单独统计(可用「计入子代理」开关切换),状态行则是把它并进父轮次。
  • 空 marker 是稳定状态,不是反复的 /new:宿主在 /new 以及退出一个不可恢复的会话时都会把 ~/.dsh-tui/resume.txt 写成空串,所以插件只在标记内容或 mtime 变化时才采用它。早期版本每秒重放一次该读数,会让已经认领状态行的对话被永久静音(显示停在 本轮 —)。
  • 轮次结束时的瞬时判定:用量报告按其请求开始时刻(step/start)归入峰/谷档;一轮的费率与模型按该轮第一次上报时的快照计价,因此轮内换模型或跨 2026-09-14 的 Pro→Flash 换挡不会把整轮改价。结算发生在轮末,只影响没记录到任何上报的极端情况。
  • 历史只覆盖本机 dsh 的用量:日志里没有的调用(网页版、其他客户端、其他机器)不会出现在方格图里。历史起点取决于本机日志文件,现有日志从 2026-09-04 开始。
  • /th 单独回车会被宿主的补全菜单抢去切主题(见上文「命令名与回车行为」):这是宿主逻辑,插件无法把命令排到补全列表前面;用 /hist、/tokenhistory、alt+h 或 /th 带空格都可以。同理,若日后某个技能名以 hist 开头,也会以同样方式抢走 /hist 的回车。
  • 本机第一次打开 /hist(还没有缓存)需要几秒:全量扫描会话日志(本机 437 份 / 112 MB 约 5~6 秒),期间显示扫描进度,并边扫边出图(最新日志先扫,最近数周不到一秒即可见);扫描会边扫边落盘,中途被打断也能续扫。有缓存之后,打开场景直接由缓存出图。
  • 鼠标悬停依赖全屏(alternate screen):profile 的 fullscreen: true 下可用;inline 模式下请用键盘(←/→/↑/↓)选中日期,明细卡同样会显示。
  • 方格图的深浅按当前可见区间内非零日期的四分位分级,因此数据增长后同一数值的颜色档位可能变化(与 GitHub 一致);图例里始终标着当前区间的最大值。
  • 终端不够宽时裁周而不是缩格:宽度放不下完整跨度时只显示最近的若干周,标题栏徽章会写「显示 27/53 周」;←/→ 挪到边缘时窗口跟着滚一格,选中格不会被藏掉。高度不足时按此优先级逐级降级:模型表 → 明细卡的字段(先砍模型行,再砍命中率/花费/token)→ 总计行;<12 行或 <40 列时改用兜底视图(最近若干天一行一条 + 一行总计),保证不溢出。

开发与验证

pnpm install --frozen-lockfile
pnpm check:encoding      # 无 UTF-8 BOM / 编码损坏(dsh 崩溃的经典元凶)
pnpm validate:manifest   # 准入形状 + 版本一致性
pnpm test                # node:test 单元测试 + 宿主桩集成测试
pnpm pack:verify         # 入口引用到的模块是否都在 files 内
pnpm verify              # 以上四项依次执行

宿主集成探测(无头启动一个一次性 profile,验证宿主是否接受注册——单测看不见这一层):

node ../../tools/probe-plugin.mjs . --wait 15

探测 profile 现在会一并挂载 scenes / plugin-host / command-trees / extensions(含 tuiShortcuts)四行,因此场景、四条命令与 alt+h 快捷键的注册都会被验证;期望输出里能看到 history scene registered、四行 command registered、command tree registered roots=4 与 shortcut registered alt+h,退出码 0。

校验历史数字(真值校验)

聚合逻辑是纯函数、可单测(test/history.test.js),但数据本身要看真实日志。想在同机复核:

  1. 删除缓存 ~/.dsh-tui/dsh-peak-balance-history.json,让下一次 /th 全量重扫;
  2. 用一个独立实现(不 import 本包的模块)按同一份字节折叠一遍,比较逐日/逐模型数字;
  3. 对比时注意会话日志是活文件:一边跑一边写会让总数持续增长,两次快照不可能相等,只有工具与插件对同一批字节的一致才有意义。

本仓库开发时用这种方式核对过:341 份日志、116 个 (模型, 日期, 峰/谷) 桶,两份实现 0 处不一致;同时验证了 0 个重复 seq、0 个乱序 seq、0 行非法 JSON。

预览峰时警告效果

警告边框只在真实高峰时段(周一至周五 09:00-12:00 / 14:00-18:00 北京时间)出现。想在任意时间预览:

DSH_PEAK_BALANCE_FORCE_PEAK=1 dsh --profile dsh-tui   # PowerShell: $env:DSH_PEAK_BALANCE_FORCE_PEAK=1

该开关只改变呈现方式(倒计时仍按真实时钟显示),不设置该变量时完全不生效。

诊断日志

插件在 ~/.dsh-tui/dsh-peak-balance.log 保留一份有上限的生命周期日志:模块被导入一行、apply() 开始一行(含 pid 与实际加载的文件路径)、解析出的配置、可挂载的宿主接缝探测结果、每次注册的结果,以及卸载。有了它就能区分「宿主压根没加载这个文件」和「某个接缝拒绝了注册」,不必给运行中的 TUI 挂调试器。文件超过 128 KiB 时自动裁剪保留最新一半;DSH_TUI_DEBUG=1 会追加每次刷新的细节。跑测试时不会写入该文件。

聚焦对话来自两条独立信号,按优先级:

  1. 宿主托管的 tui/session-switched 决策事件通知——宿主挂载了 plugin-interop 行时可用(日志里 host=1)。manifest 把该契约声明为 optional 并写清兜底方案,缺少它的宿主只会降级,不会被拒绝准入。
  2. 宿主每次切换都会重写的启动器标记 ~/.dsh-tui/resume.txt,每秒读一次。宿主开始新对话(/new)时会把这个标记清空——这是「有信息」而不是「读不到」:状态行会放掉原来那个数字,改由第一个"不是刚离开的那个对话"来接手。只有压根读不到标记时,才回退到「最后一次会话事件」。

DSH_PEAK_BALANCE_FOCUS_FILE 可覆盖该标记路径,供测试与诊断使用——测试运行因此永远不会去读真实标记。

发布与版本

仓库 VviLliAm-qwq/dsh-peak-balance,MIT 许可。版本遵循 SemVer;npm 的 version 与 manifest 的 version 始终保持一致,打一个与该版本同号的 v* tag 即触发发布工作流;该工作流经 npm **可信发布(OIDC)**上传,仓库内不存放令牌。

收录

本插件收录在 dsh-tui 插件市场。市场只做链接罗列,不做代码审查、不代表背书。

给插件作者的坑(本插件踩过的)

  • Cordis 插件入口只能导出 name、Config、apply 三个符号。 若在同一个模块里额外导出工具函数,宿主对 activation 的包装方式会改变,随后所有 tuiStatus / tuiSettingsSections 注册都会被拒(requires a live Cordis activation context)。这个故障是「半死」的:设置命名空间仍会注册成功,于是插件看起来活着,但设置卡片和状态行永远不出现。请把实现放在同目录的另一个模块,入口只做再导出。
  • 可选宿主服务要「严格优先」获取(ctx.get(name));非严格的 ctx.get(name, false) 可能返回影子占位实例,宿主会拒绝其方法调用。非严格形式只作兜底,并且要持续重试 —— 第一拍时接缝行可能仍在激活中。
  • 中介式命令注册需要「已校验的组件身份」,普通 profile 行拿不到。 ctx.tuiPluginHost.registerCommand 会先 requireComponentIdentity,以 file: 装进 profile 的第三方插件不是通过宿主的 admission 通道加载的,于是它会抛 the calling activation has no verified dsh-plugin.json Component identity。manifest 里照样要如实声明 commands.dsh/v1alpha1#Command 与该命令的 contribution id(将来被 admission 接管时才有意义),但代码必须准备好回退到 ctx.commands.register,否则命令会静默消失。
  • 状态行与全屏场景是两种不同的接缝。 tuiStatus.registerView 的富视图上限 3 行且拿不到滚轮/键盘;要画多行界面(例如方格图)必须用 ctx.tuiScenes.register + open,它给的是完整 ui 套件(Box/Text/useInput/useTerminalSize/useTheme)和 close()。
  • 场景里 hooks 必须无条件、按固定顺序调用。 看起来无害的「ui.useTheme 不存在就跳过」会改变 hook 顺序,真实 React 会当场抛 invalid-hook-call。本插件的写法是先取出 useInput/useTerminalSize/useTheme(缺失时给一个返回默认值的桩),再无条件调用。
  • 命令名不要是内置命令的前缀。 宿主的斜杠补全菜单在打开时接管回车,执行的是高亮项,且内置命令排在插件命令之前 —— 名字撞前缀(/th vs theme/thinking)就意味着单独回车永远轮不到你。要么换个不冲突的名字(/hist),要么另给一个快捷键(ctx.tuiShortcuts,需要 ctrl/alt 且避开保留组合)。
  • 全屏场景要自己预算行高。 alternate screen 里溢出不是「被裁剪」而是把整帧推走;每个区块都该先问「还剩几行」,再决定画不画。宽度同理:先算能放几列,再去裁内容,而不是指望宿主截断。

本插件把排查过程写进 ~/.dsh-tui/dsh-peak-balance.log,上面几条就是这样查出来的(见上文「诊断日志」)。

许可

MIT — 见 LICENSE。