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

@dshworks/dsh-meter

v0.4.1

Published

The DeepSeek time-of-use meter for dsh: what this session cost, which tariff is running, when it flips, and the account balance behind it — one line under the composer.

Readme

dsh-meter

English | 中文

DeepSeek 开始按时段计价了,这是配套的电表。

每天两段高峰,空闲时段半价。花费不再是事后翻账本才看的数字,而是你此刻正站在里面的费率

输入框下面一行:这个会话花了多少、当前是哪一档、还有多久换档。悬停展开卡片,里面是时段表、缓存的经济账,以及账户余额。

site ci powered by dsh license: MIT

安装

dsh plugin --profile web add @dshworks/dsh-meter
dsh --profile web

dsh plugin 转发给 pnpm,因此 PATH 里要有 pnpm。除此之外无需配置:会话产生第一次计费请求后,计价行就会出现在输入框下方。

那一行

2 turns · 2 steps | LLM 3.1s | TTFT avg 1.3s · 41 tok/s | Cache hit 50% | Input 44.3K tok
                    ¥0.0672  |  peak  |  off-peak in 48m

上面是 dsh 自带的统计行,原样保留;下面才是本插件。它新增一行,而不是抢占官方那一格——想在官方统计行里追加内容,只能用同 id 更低 priority 把它顶掉,那样每次上游改动都会跟着坏。

悬停或聚焦展开卡片。处在高峰时段时,档位用琥珀色标出,倒计时指向下一个空闲时段:

| 卡片里有什么 | 为什么值得占这个位置 | |---|---| | 会话总价、请求数、模型 | 这个数字只出现一次,用最大的字号 | | 24 小时时段条,按你所在时区绘制,带实时游标 | 官方按 UTC 公布高峰窗口。深夜心算时区不如直接看一条 | | 缓存命中 / 未命中输入 / 输出,各自的 token 与金额 | 命中价只有未命中的 1/30。这一行能看出前缀是否稳定 | | 账户余额,以及其中多少是赠送额度 | 赠送额度会过期,充值余额不会 | | 同样的 token 换到另一档要多少钱 | 调价前:看新价会让这个会话变成什么样;调价后:看等到空闲时段能省多少 | | 缓存省下了多少 | 把每一次命中都按未命中重算出来的对照值 |

设置面板:花钱之前就能看

那一行需要一个会话,而且要这个会话已经产生过计费请求——于是这个插件本该回答的问题, 必须先付钱才问得出来。设置 → 峰谷价在没有会话的情况下直接回答。

它刻意不是一张表单。两个配置项都在插件自己的配置里,harness 本来就有编辑插件配置的地方; 为两个字段再造一条写入路径,那是「设置页」的条件反射,不是内容。这里放的是仪表:

| 面板展示 | 为什么在这里 | |---|---| | 当前档位,大字号,配倒计时和结束时刻 | 你打开这个面板的理由,在读到别的东西之前就已经回答 | | 本周——七个本地日 × 二十四小时,高峰为琥珀色,带实时游标 | 卡片上的条带回答「今天什么时候换挡」。周末转为空闲价之后这变成了一个按周的问题,168 格里 35 格琥珀色,是看清「高峰是每周 35 小时而不是 49 小时」最快的方式 | | 按你账号计价币种显示的公开价目表 | 按 DeepSeek 公布的精度显示——是 ¥1.5,不是 ¥1.50。价目是报价,不是账单 | | 你的余额 | 面板打开时读一次,走的是卡片用的同一条宿主路由 |

网格按你的日期排,不是按北京的日期排,而每一格都问那个给请求定价的同一个函数—— 所以画面和账单不会不一致,你也不用自己做时区换算。

中文界面里,小时格子上藏了个彩蛋。峰、谷本来就是电力峰谷电价的老词,而「峰」那一半, 离卖 token 那家公司创始人的名字只差一个字。把鼠标放到格子上:文峰、文谷。

验证

2026-08-24 对 dsh 0.1.1-rc.2 做过真实验证,DeepSeek-V4-Pro 与 V4-Flash 真实会话,不是 mock:

这次是重跑,因为只读源码不够。 到 rc.8 为止,这一段一直用「rc.7 与 rc.8 的 src/ 逐字节相同」来论证投影契约没动过。后来它动了:0.1.1-rc.1schema 改名为 stateSchema,并把面向客户端的 view 挪进了可选的 wire。 注册表是逐个字段去读定义的,从不校验,所以旧写法不会报错——它被理解成 「这个投影只留在宿主端」,于是在一个完全健康的 harness 上,那一行什么都不渲染。 一个会退化成沉默的契约,靠 diff 源码是查不出来的;下面这张表是重新跑出来的, 不是推出来的。

| 结论 | 怎么验的 | |---|---| | 能在标准 web profile 里加载 | dsh --profile web --dump-config 能列出 @dshworks/dsh-meter/plugins/@dshworks/dsh-meter/client.js 返回 200 | | 那一行渲染出来了 | 输入框下方的 ¥0.2185 \| off-peak \| peak in 14h,直接从活的 DOM 里读出来——正是能抓住 rc.1 那次改名的检查 | | 读数正确 | V4-Flash 与 V4-Pro 合计 36.1K 未命中输入 + 147 输出 = ¥0.2162 + ¥0.0023 = ¥0.2185,与正上方 dsh 自己那行 token 统计一致 | | 重启后仍在 | 重启服务、冷启动重开一个八天前的会话,投影从持久日志重放出同一个数 | | 卡片能打开 | 分模型明细、缓存对照(全高峰 ¥0.2185 / 全低谷 ¥0.1093)、账号余额,页面无报错 | | 币种自动识别 | 真实账号经 /dsh-meter/balance 返回 {"currency":"cny", ...},整个界面无需配置直接切到 ¥ | | 两种主题、两种档位 | 亮色/暗色、统一价/高峰,见上图,摄于 2026-08-15——那次早于 8/16 切换,其中的统一价读数是新会话已不会再出现的状态 | | 130 个测试,CI 绿 | pnpm test — 折叠逻辑、时段时钟、价目表、省钱模式的提示语、余额读取、对着两代注册表替身跑的注册契约,外加生成产物的同步校验 |

两种货币,不做换算

DeepSeek 公布的是两张互相独立的价目表——国际站按美元,中国站按人民币。一个账号只按其中一张计费,两张表也不是彼此的汇率换算,所以用汇率去折算的插件会错两次。

dsh-meter 两张表都算,然后让账号自己决定用哪张。GET /user/balance 会返回该账号的计价币种(真实账号会同时列出两行,只有一行有余额),插件读有余额的那一行并按它显示。不需要配置,也不靠界面语言去猜。

余额请求跑在宿主侧,用的是 LLM adapter 同一套凭据接口;浏览器通过 GET /dsh-meter/balance 只拿到解析好的数字,拿不到 key。它只在计价行挂载和你展开卡片时发出,不做轮询——设 balance: false 可以整个关掉,计价功能不受影响。

价目表

原样写在 lib/core.js 里,单位为每百万 tokens。

| | 缓存命中 | 缓存未命中 | 输出 | |---|---|---|---| | v4-flash 空闲 | $0.007 / ¥0.05 | $0.22 / ¥1.5 | $0.66 / ¥4.5 | | v4-flash 高峰 | $0.014 / ¥0.10 | $0.44 / ¥3 | $1.32 / ¥9 | | v4-pro 空闲 | $0.022 / ¥0.15 | $0.66 / ¥4.5 | $1.98 / ¥13.5 | | v4-pro 高峰 | $0.044 / ¥0.30 | $1.32 / ¥9 | $3.96 / ¥27 |

高峰为 UTC 01:00–04:00 与 06:00–10:00(北京时间 09:00–12:00、14:00–18:00)。其余时间都是空闲时段,包括两个窗口之间那两小时。空闲价正好是高峰价的一半——但仍高于它取代的统一价,输出约为 2.3 倍。

UTC 2026-08-16 16:00 之前全天按此计费。官方页面已不再列出它;电表保留它,是因为切换前记录的会话必须仍按当时真实的价格计算。

| | 缓存命中 | 缓存未命中 | 输出 | |---|---|---|---| | v4-flash 统一价 | $0.0028 / ¥0.02 | $0.14 / ¥1 | $0.28 / ¥2 | | v4-pro 统一价 | $0.003625 / ¥0.025 | $0.435 / ¥3 | $0.87 / ¥6 |

相较于它,v4-pro 缓存命中输入涨到 6 倍(高峰 12 倍)、缓存未命中输入 1.5 倍(高峰 3 倍)、输出 2.3 倍(高峰 4.6 倍)。涨幅最陡的正是最便宜的那种 token,也正是 agent 发得最多的那种。

来源:https://api-docs.deepseek.com/quick_start/pricing,每天与中英文两份页面自动比对(见下节),最近一次确认一致为 2026-08-20——并且核对过真实账单:v4-pro 一次 188,542 缓存未命中 tokens 的调用,空闲时段实扣 ¥0.84,即每百万 ¥4.46,对应公布的 4.5。

价目表也是一份数据源

自己做用量估算?取这份数据,别抄上面表格里的数字。

https://dsh.works/dsh-meter/pricing.json

静态 JSON,无需 key,不限流。两种货币、两个档位、24 小时 UTC 档位表、用于回算历史的已退役统一价,以及三个计费桶的定义——由 scripts/build-feed.mjslib/core.js 生成,所以它不可能报出一个电表自己不认的价。

JavaScript 里可以跳过这次 fetch,直接调同一个模块:

import { costOf, tariffAt } from '@dshworks/dsh-meter/core'

const tokens = { miss: 188_542, hit: 1_204_880, out: 9_310 }
costOf(tokens, 'deepseek-v4-pro', tariffAt(Date.now()), 'cny')

lib/core.js 不依赖任何包。价目表、档位时钟、成本折叠都是纯函数,内部不读时钟,所以历史会话按它当时真实的档位回算。

现在是什么价?

这份数据不包含「现在」,却能回答这个问题:它发布的是 24 小时档位表,由你用当前 UTC 小时去索引。正因为如此,CDN 缓存十分钟、或者把它编进二进制里,都不会让「当前是哪个档位」变旧:

curl -s https://dsh.works/dsh-meter/pricing.json | jq -r '
  (now|gmtime|.[3]) as $h
  | .timeOfUse.scheduleUtc[$h] as $t
  | "\($t) · v4-pro 输出 $\(.models["deepseek-v4-pro"].rates[$t].usd.out)/1M"'

如果换成一个直接返回答案的接口,它在缓存存活期间就是错的——每天四次,而且恰好错在「差一倍」的那条边界上。

两种会静默出错的写法:

  • 用本地小时索引。 scheduleUtc 按 UTC 小时索引,不会旋转到你的时区。new Date().getHours() 会返回一个看着合理、但对地球上大多数人来说是错的档位。
  • broken-down time 的下标。 jq 的 gmtime[年, 月, 日, 时, …],小时是 .[3]。写成 .[2] 取到的是「日」——而它同样是 24 元素数组的合法下标。写这一节时是 UTC 09:59,.[2] 给出 offpeak,真实档位是 peak。看上去毫无破绽。

档位表锚定在北京时间(UTC+8),而中国自 1991 年起不再实行夏令时——正是这个固定偏移,才让「一张 UTC 小时表」可以安全发布。数据里在 timeOfUse.anchor 写明了这一点;verify-pricing分别核对英文页的 UTC 表述与中文页的北京时间表述,再核对两者是否仍然一致。

有一件事任何数据源都救不了:now 用的是你自己机器的时钟。它要是漂了,你的档位也就漂了。

为什么不直接用现成的价格源? 因为没有一个是对的。2026-08-20 复核,也就是切换四天之后,做估算最常用的两个源仍然把 DeepSeek 已退役的统一价当作现价发布:

| 数据源 | v4-pro 缓存未命中输入 | 相对真实账单少算 | |---|---|---| | models.dev | $0.435 | 空闲 1.5 倍,高峰 3.0 倍 | | LiteLLM | $0.435 | 空闲 1.5 倍,高峰 3.0 倍 | | 本数据源 | 空闲 $0.66 / 高峰 $1.32 | — |

在缓存命中输入这一桶——agent 发得最多的那一桶——models.dev 在高峰时段少算 12 倍。而且这不是他们补一次数据就能修的:两家的 schema 都是「每模型每桶一个固定单价」,根本没有放档位的位置。一个每天有七个小时是错的价格,在这两种结构里都表达不出来。

OpenRouter 的 /api/v1/models 是准的,但回答的是另一个问题——它报的是 OpenRouter 转售这个模型的报价,不是 DeepSeek 从你账户里扣的钱。

这些数字没有人手工敲

我们不敲,也不建议你敲。手工维护的价目表一定会烂掉,而最好的证据来自厂商自己:DeepSeek 官方的 pi 集成文档里那段 cost 配置,v4-flash 的缓存读取价是一个 10 倍的小数点错误(写成 0.028,实际是 0.0028),v4-pro 的数字则正好是价目表的 4 倍——那是 Azure 的转售价,被贴进了 DeepSeek 自己的文档,而这一页是给人直接复制到 agent 配置里的。

所以价目表由机器写、由人审:

| | | | |---|---|---| | | scripts/verify-pricing.mjs | 抓取中英文两份页面——价格、高峰窗口、模型列表——与价目表逐项比对 | | | scripts/apply-pricing.mjs | 把抓到的数字写回 lib/core.js,然后用校验脚本重新读一遍自己的输出,读不通就回滚 | | | npm run build | 用改写后的价目表重新生成 bundle、站点和数据源 | | | 你 | 任务只开 PR,从不自己合并 |

每天一次的定时任务把这四步跑完。本地:npm run verify:pricing 只看,npm run sync:pricing 才改。

为什么只开 PR 不自动合并。 CI 能检查的全是结构性不变量——空闲价是高峰价的一半、输出比缓存未命中贵、当前请求不会按已退役的统一价计费——而一个「错得很合理」的解析结果能同时满足所有这些。没有任何测试能把「对的价格」和「像样的价格」区分开,但一个人看一眼 diff 可以。

上一次调价,我们盯着的任何渠道都没有收到通知——这是要装警报的理由;厂商自己那个 10 倍的手误,则是把键盘从所有人手里拿走的理由。

钱是怎么算出来的

| 行为 | 细节 | |---|---| | 数据来源 | 持久会话日志里由供应商上报的 token 数。不采样,不推测 | | 每次请求的档位 | 按发出时刻(step/start)判定,而不是按回答结束的时刻。UTC 00:59 发出的请求即使流式输出跨进高峰窗口,仍按空闲价计;跨档会话的两侧各按各的档位算 | | 计费桶 | inputTokens 按未命中价,cacheReadTokens 按命中价,outputTokens 按输出价。dsh 上报的是互不重叠的三个数(DeepSeek adapter 会从 prompt_tokens 里减掉命中部分),推理 token 已包含在输出里 | | 缓存写入 | 并入未命中桶。DeepSeek 没有单独的写入价,首次进入缓存的 prompt 按未命中计;DeepSeek adapter 也从不上报这一项 | | 失败的请求 | 某一步的 usage chunk 即使请求随后失败也会计入;最终 message 会替换这个样本而不是叠加,模型名与请求头不一致时同样如此 | | 未知模型 | 只计数、只列名,绝不定价。没有公开价格的模型贡献 token 与请求数、金额为零,卡片会明说 | | 持久性 | 一个会话投影(costMeter),从日志折叠而来。翻页、压缩、刷新、重启服务都不影响,走标准投影缓存 |

对模型的影响

默认没有。dsh-meter 不加工具、不加系统提示、不加消息、不发模型请求,完全不碰请求本身。花费是付钱的人该看的东西,不该占 agent 的上下文。唯一的例外要你自己打开:省钱模式savingMode: true)会贡献一段系统提示,说明当前处在哪个计费时段;这段文本渲染为空时,整节都不存在。

KV 缓存影响

默认无。开启省钱模式后,meter:tariff 这一节的文本在同一个计费时段内逐字节相同,所以提示前缀——以及它的缓存——能从一次请求保持到下一次;只有在时段切换的那一刻才变,因此切换后的第一次请求会错过前缀缓存,之后重新命中。

配置

以下都是校验过的配置项,写在 profile 的 cordis.patch.yml 里:

- id: dsh-meter
  config:
    currency: cny        # 固定价目表,不再自动识别
    balance: false       # 完全不调用 /user/balance

| 键 | 默认值 | 含义 | |---|---|---| | currency | auto | 显示哪张价目表:auto(先看账号余额币种,再退到界面语言)、usdcny | | balance | true | 是否把账户余额提供给 Web UI | | apiKeyEnv | DEEPSEEK_API_KEY | 存放 API key 的凭据引用 | | baseUrl | https://api.deepseek.com | 读取余额的接口来源 | | balanceTtlMs | 300000 | 展开卡片时重新拉取余额的最小间隔 | | balanceTimeoutMs | 4000 | 单次余额请求的超时 | | savingMode | false | 贡献一段系统提示,告诉模型当前处在哪个计费时段,好让它相应地调整行为 | | savingPeakPrompt | 内置提示语 | 高峰窗口期间注入的文本 | | savingOffPeakPrompt | '' | 非高峰期间注入的文本;默认为空,意味着整节渲染为空、不花一个提示 token |

省钱模式 — 让电表开口说话

默认关闭,因为一个只读的仪表不该自己长进 agent 的上下文里。把 savingMode 打开,电表就不再只是旁观者:每次组装提示时,它会贡献一节 meter:tariff,告诉模型下一次请求将按哪个时段计费、以及在这个时段里该怎么做。

在高峰窗口内,这一节的内容是 savingPeakPrompt——默认是电表自己的提示语:点名高峰时段、说清账单翻倍、并要求模型答得简洁,优先用已有的上下文而不是新开工具调用,遇到昂贵又能等的任务就说出来、提议等窗口关掉再跑。窗口之外这一节渲染 savingOffPeakPrompt,默认为空,所以没有什么要提醒的时候,电表说话的成本是零 token。想让模型在低谷时段反而放开手脚?把 savingOffPeakPrompt 设成类似「现在是半价时段,可以放心多花 token」的文字。

有四点是刻意为之:

  • 时段在组装时读取,用的是计费折叠同一个时钟。 提示和账单永远不会对「下一次请求落在边界哪一侧」产生分歧——周末也一样:周末整天按低谷计费,因此完全不会触发提示。
  • 提示语里的时段是推导出来的,不是手写的。 peakHoursPhrase()PEAK_WINDOWS_UTCtariffSchedule() 里读,所以排期一改,句子跟着改。一段告诉模型错误高峰时段的提示,就是插件在钱的问题上骗它。
  • 同一个时段内文本恒定。 在这里放倒计时会每分钟变一次,为了一句话滚掉整个会话的提示前缀缓存——这是一个成本插件所能想到的、最昂贵的省钱方式。它只在时段边界翻转(翻转后第一次请求会错过前缀缓存),之后保持不变。
  • 这是提醒,不是限流。 模型可以无视它;请求层面没有任何强制。需要硬上限的话,那是另一个插件。

开发

pnpm install
pnpm test                        # 先校验产物同步,再跑 vitest
pnpm run build                   # 改完 core/ui 后重新生成三份产物
pnpm run verify:pricing          # 与 DeepSeek 官方价格页逐项比对(需联网)

lib/core.js 是价目表、时段时钟和折叠逻辑,lib/balance.js 是账户读取,src/ui.js 是浏览器界面。三份产物都由这一份价目表生成——lib/client.js(dsh 实际分发的 bundle)、docs/index.html(站点)、docs/pricing.json(数据源)——价目表因此只存在于一个文件里,任何一份产物与源码不同步 pnpm test 就会失败。

verify:pricing 是唯一联网的脚本,并且刻意不放进 pnpm test:单元测试不该因为一个文档站点慢而失败,价格警报也不该因为这周没人提 PR 而沉默。它按自己的日程每天跑。

已知限制

  • 这是估算,不是账单。 官方价目乘以上报 token。优惠价格、以及任何挡在 API 前面的中转都看不到。
  • 档位按发出时间戳判定,用的是 dsh 这台机器的时钟。若 DeepSeek 实际在档位边界另一侧收到请求,可能差出一两秒。
  • 非 DeepSeek 线路不定价。 它们的 token 会被计数、模型会被列名,但卡片会标为未定价,而不是把 DeepSeek 的价格悄悄套到别人家的 API 上。
  • 价目表是编译进去的。 DeepSeek 会调价;本插件带的是 2026-08-17 复核过的峰谷价目表,跟进下一次调价需要发新版本。
  • 只有 Web。 投影本身任何界面都能读,但读数是为 Web UI 做的,没有 TUI 版本。

参考与致谢

dsh 插件登记处已经收录了约 50 个花费/用量插件,其中不少就出现在 DeepSeek 公布调价日期的那一周,读它们的实现影响了这一版的取舍。缓存节省的表述来自 deepseek-cli 的本地用量账本。

许可

MIT