dsh-deepseek-billing-period
DeepSeek API billing-period chip in the composer dock: shows whether the current time is peak or off-peak with a countdown to the next rate switch, opens the peak/off-peak window rules on click, and during peak hours intercepts sending to ask for confirmation first.
Install
dsh plugin --profile web add dsh-deepseek-billing-period
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-deepseek-billing-period
一个 DeepSeek Harness(dsh)Web 插件:在输入框下方常驻显示当前 DeepSeek API 计费时段 —— 是高峰(全价)还是空闲(半价),以及距离下一次时段切换的倒计时。
- 药丸实时刷新,倒计时精确到分,每秒更新。
- 点击药丸会在其正上方弹出浮动的规则说明卡片,不占用输入框布局(展开前后输入区尺寸逐像素不变);点击卡片外部任意位置或按
Esc关闭。 - 卡片表面与宿主自带的 composer 浮层(如"上下文已用 …%")保持同一套观感:半透明底、发丝描边、柔和投影、
--dsw-radius-lg圆角。 - 浅色 / 深色主题自适应:卡片底色、描边、投影全部取自宿主
--dsw-*令牌(--dsw-specific-menu、--dsw-elevation-prominent等),不写死任何颜色;确认弹窗直接复用宿主自带的Modal/Button/Checkbox组件(宿主未提供时才回退到同套令牌自绘的对话框),观感与主题完全随宿主。 - 高峰时段发送前确认:高峰(全价)时段按下
Enter或点击发送按钮时先弹确认框(取消 / 确认发送),并可在弹窗内勾选"以后不再询问",随时用规则卡片底部的开关恢复;新建的空白会话中同样生效。 - 判定恒按北京时间(UTC+8) 换算,与浏览器所在时区无关;中英文案随界面语言切换,节假日名称也会本地化。
- 节假日不再硬编码:运行时从免费的在线日历 holiday-cn 获取当年与下一年的法定节假日及调休安排,按年自动更新;数据在本地缓存,离线或数据源不可用时自动回退为"仅按周一至周五判断",并在卡片中注明当前数据状态。
截图
· 浅色 · 深色
规则卡片 ·  · 
高峰发送确认 ·  · 
计费规则
时段 · 判定 · 价格
高峰 · 北京时间周一至周五 09:00–12:00、14:00–18:00,且当天不是中国法定节假日 · 全价
空闲 · 其余全部时间,包含周末与法定节假日全天 · 高峰价格的一半
- 两个窗口均为左闭右开:
09:00起进入高峰,12:00/18:00起离开高峰;午休12:00–14:00属空闲。 - 规则与 DeepSeek 官方文档一致:北京时间周一至周五(不含中国法定节假日)的
09:00–12:00、14:00–18:00为高峰时段,其余时间(含周末与法定节假日全天)为空闲时段,见 DeepSeek 价格页。 - 只需收录"放假"的日期:周末本身全天空闲,因此调休补班落在周末也不影响高峰判定(这类周末仍按空闲处理)。
- 未收录年份(例如国务院尚未公布放假安排的年份)自动回退为"仅按周一至周五判断",并在展开卡片中明确写出该提示,不会静默给出错误的"高峰"结论。
节假日数据来源
- 数据源:NateScarlet/holiday-cn(MIT 许可)。该仓库由 CI 每日抓取国务院办公厅的放假通知,按年生成 JSON,包含全部法定节假日与调休补班安排。
- 获取方式:浏览器直接
fetch,不需要 API Key,也不经过 dsh 宿主代理。三个可跨域(CORS)的镜像并发竞速,第一个返回合法数据的镜像胜出,其余请求立即中止:
1. https://cdn.jsdelivr.net/gh/NateScarlet/holiday-cn@master/<年份>.json
2. https://fastly.jsdelivr.net/gh/NateScarlet/holiday-cn@master/<年份>.json
3. https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/<年份>.json
单次请求超时 8 秒;某个镜像返回 404、非 JSON 或格式错误时只是输掉竞速,不影响其他镜像。
- 格式与校验:上游文件形如
{ "year": 2026, "papers": ["…"], "days": [{ "name": "元旦", "date": "2026-01-01", "isOffDay": true }] }。插件会校验年份是否与请求一致、days是否为数组、每个日期是否为真实存在的YYYY-MM-DD。days: []的占位文件视为"当年安排尚未公布",而不是"当年没有节假日"。 - 本地缓存:保存在
localStorage的dsh-deepseek-billing-period.holidays.v1键中。已公布年份缓存 7 天,未公布占位缓存 6 小时;过期后先展示缓存、再后台刷新(stale-while-revalidate)。只缓存当前年份前 1 年到后 2 年,更早或更晚的年份自动清理。 - 刷新时机:插件加载、组件挂载以及每 5 分钟检查一次,始终维护当年与下一年的日历;卡片底部的「刷新节假日数据」按钮可强制重新拉取。
- 失败与回退:全部镜像失败且本地没有可用缓存时,记录一次失败并在 60 秒内不重试,卡片显示"暂时无法访问",此时判定回退为仅按周一至周五;若本地已有缓存,则继续用缓存数据判断(卡片照常显示上次更新时间),并在后台自动重试。数据恢复后自动切回在线日历判定。跨年的 12 月日期可能收录在下一年的通知文件里,插件会合并两年数据;但"该年已收录"仍以该年自己的通知文件为准,未收录时卡片明确提示。
- 隐私:插件只向上述 jsDelivr / GitHub 地址发起 GET 请求,不发送任何 API Key、会话内容或用户数据;节假日数据也不上报到任何地方。
高峰时段发送前确认
高峰时段(全价)内,Enter 与发送按钮都会先被拦下并弹出确认框:
- 取消(或
Esc/ 点遮罩):不发送,草稿原样保留,光标回到输入框。 - 确认发送:按原样把消息发出去(复用的是宿主自己的发送逻辑,不绕过任何宿主行为)。
- 弹窗底部有一个默认不勾选的「以后高峰时段不再询问」。勾选后再点"确认发送",以后高峰时段就直接发送、不再弹窗;偏好存在浏览器
localStorage的dsh-deepseek-billing-period.prefs里。 - 想重新开启确认:展开计费时段规则卡片,打开底部的「高峰时段发送前确认」开关(点开关或旁边的文字均可)即可,无需刷新页面;开关会实时反映弹窗里或其他标签页中做的修改。
- 只会拦下"真的要发送消息"的手势:空闲时段、
/开头的斜杠命令、Shift+Enter换行、发送按钮处于禁用状态(空草稿、附件仍在上传等)都不受影响;页面上输入框以外的其他文本框(以及其他插件挂在输入框浮层里的编辑器)里按Enter也不会被拦截。 - 拦截逻辑挂在宿主的
conversation.input.dock上,因此新建的空白会话在第一句话发送前就已经生效;计费药丸本身仍挂在conversation.composer.dock,宿主只在会话进入正常状态后才渲染它。
安装方法
前置:已安装 dsh 并启用 web profile(本插件在 dsh 0.2.x / Node v26 上验证)。
方式一:从 npm 安装(推荐)
dsh plugin --profile web add dsh-deepseek-billing-period
方式二:从 GitHub 安装
dsh plugin --profile web add github:zi7tian/dsh-deepseek-billing-period
dsh plugin add 会在该 profile 内执行安装,并把声明了 bundle 的包追加到 dsh.profile.bundles。完成后重启 dsh web,刷新页面即可在输入框下方看到计费时段药丸。
方式三:本地克隆后 link(便于改代码)
git clone https://github.com/zi7tian/dsh-deepseek-billing-period.git dsh plugin --profile web add link:/path/to/dsh-deepseek-billing-period
用 link: 安装时,直接编辑 checkout 里的 client.js 并在浏览器刷新页面即可生效,无需重装或重启 dsh web。
卸载
dsh plugin --profile web remove dsh-deepseek-billing-period
安装成功的标志
打开任意一个已进入正常状态的会话,输入框下方会出现:
● 空闲时段 · 半价 · 距高峰 12 小时 42 分
点击它即可展开上方那张规则说明卡片。
测试
node test/logic.test.mjs
测试不访问网络:2025 / 2026 年的真实日历以 fixture 形式内嵌,镜像竞速与回退、缓存、失败退避、占位年份、跨年日期合并等主要路径通过 mock fetch 覆盖;另有假 DOM 测试覆盖消息手势的编辑器 / composer card 解析,以及 apply() 同时注册 conversation.composer.dock 与 conversation.input.dock 两个 slot。当前共 162 个断言。
更新日志
1.3.0
- 节假日判定改为运行时从 holiday-cn 在线日历获取,每年自动更新,不再把放假安排硬编码进
client.js。 - 三镜像并发竞速 + 8 秒超时 + 本地缓存(7 天 / 占位 6 小时)+ 失败退避(60 秒),新增「刷新节假日数据」按钮。
- 规则卡片新增数据状态行(已收录并显示更新时间 / 正在获取 / 尚未公布 / 暂时无法访问),并保留未收录年份的显式回退提示。
1.3.1
- 修复:新建的空白会话(hero 输入框)中,高峰时段按
Enter或点发送按钮不会弹出确认框。拦截逻辑改挂在宿主conversation.input.dock(空白会话也会渲染),计费药丸仍留在conversation.composer.dock;手势识别改为从event.target反推编辑器与 composer card,不再依赖药丸所在节点。 - 面板上的「高峰时段发送前确认」开关改为每次发送时读取
localStorage偏好,切换后立即生效。 - 测试:新增假 DOM 的手势解析测试与
apply()双 slot 注册断言(130 → 149)。
1.3.2
- 修复:高峰时段在页面上任何文本框(输入框以外的编辑框、设置项等)按
Enter都会被误拦并弹出发送确认。现在只认输入框卡片里唯一的那个草稿编辑器。 - 修复:在弹窗里勾选"不再询问"后,规则卡片底部的开关仍显示为开启,需要点两次才能真正恢复确认;开关现在会跟随本页和其他标签页的偏好修改实时更新。
- 修复:弹窗复选框文案与效果相反(写的是"是否需要每次确认",勾选后却是不再询问),改为「以后高峰时段不再询问」。
- 修复:发送按钮处于禁用状态(空草稿、附件上传中等)时按
Enter不再弹出确认。 - 细节:弹窗内按
Esc不再继续传给宿主;卡片底部开关旁的文字也可点击切换。 - 测试:149 → 162 个断言。
许可证
[MIT](./LICENSE)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::5,最后更新 2026-11-03