DeepSeek Harness 插件

LinHyu-2006/dsh-balance-hud

Star 数 ★ 1 分类 用量与计费 收录于 2026-10-22

在右下角浮层显示 DeepSeek 账户余额与本会话花费,支持预算告警、峰时提醒,以及可选开启的官方价格同步。

安装

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:LinHyu-2006/dsh-balance-hud

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

English | 简体中文

dsh-balance-hud

DSH(DeepSeek Harness) Web GUI 右下角的常驻 HUD:一眼看到 DeepSeek 账户余额和本会话花费。

English · 设计说明 · Design notes · LICENSE

HUD 的两种状态:折叠角标与展开明细面板

关于这些图片:它们不是运行中 DSH 窗口的真实截图,而是按插件自身的布局与配色 渲染出来的图(用的是真实样式取值与真实数字)—— 制作时没有可用的实时界面截图工具。 请把它们当作如实的布局参考,而不是"已装好并正在运行"的证据。 欢迎用真实截图替换。

# Windows —— 双击 tools\install.cmd,或在命令行里:
tools\install.cmd

# macOS / Linux:
sh tools/install.sh

两个启动器都会替你找 Node,包括 DSH 自带的那份运行时(它不在 PATH 上)。 如果你的机器完全没装 Node,它会明确告诉你怎么办,而不是丢一句 node: command not found。 换个 profile 用 --profile <目录>,只看不改用 --dry-run,卸掉用 --uninstall。

想自己跑也行 —— 下面这条假定 Node 已经在 PATH 上:

node tools/install.mjs    # 安装到 ~/.dsh/profiles/desktop,然后重启 DSH
node tools/smoke.mjs      # 离线冒烟套件(83 条断言),不需要装 DSH

装完必须重启 DSH。 装插件会改 profile 的 bundle 列表,那个列表不热加载 —— 只刷新页面不会生效。

  • 账户余额 —— 充值余额 + 赠金余额,走的是「设置 → 账号与余额」同一个服务 (ctx.deepseekAccount.getBalance())。
  • 两条花费口径 —— 按官方价格表算的 token 预估,以及由真实余额减少量累计出的实际值。
  • 付充值无感 —— 充值只抬高余额,不会打断花费计数。
  • 按会话独立 —— 基线与计数按会话 id 分槽存储;刷新页面或切换会话都不会覆盖别的会话。
  • 不碰凭据,默认不外联 —— 插件从头到尾不接触你的 API key;只有你开启价格同步后才会联网 (见 价格与同步),在此之前不产生任何外部请求。
  • 前后各半、一条中继 —— 浏览器半边负责渲染,Host 半边开只读回环路由(余额、赠金信号、 价格同步、模型列表),浏览器本身只跟回环地址说话。
  • 可配置、有预算 —— 「设置 → 通用」里有一行偏好,可改模型、轮询间隔、预算与提醒; 花费接近预算时卡片会亮起彩色描边,面板还会倒计时到下一次峰谷切换。

界面

                                                        ┌──────────────────────────────┐
                                                        │  账户余额            ¥43.42  │
                                                        │  本会话预估           ¥0.59  │
                                                        └──────────────────────────────┘

把鼠标移到卡片上会展开明细面板:余额分项(充值 / 赠金 / 合计)、预估花费的三个计价分项、 实际值、token 用量,以及并行会话时的分摊行(本会话占比、其它会话、跟踪会话、 账户级合计)。展开面板用 4 位小数,小卡片用 2 位。

字段 含义
账户余额 账户合计(充值 + 赠金),2 位小数
本会话预估 本会话的预估花费;卡片 2 位、面板 4 位;会话还没有 token 用量时显示 —
实际扣费 本会话累计的真实花费(单会话时;并行时改叫 本会话分摊)

金额小于可显示的最小单位时,卡片显示 <¥0.01、面板显示 <¥0.0001 —— 花了钱就绝不会被抹成 0。

交互

  • 悬停卡片展开明细面板。
  • 卡片与面板是两个独立元素,中间留了 8 px 的视觉间隔。悬停判定挂在卡片和面板自己身上 (各自有 enter / leave),不挂在外层定位容器上;并用一个 140 ms 的待关闭定时器, 让指针落进那段间隔时面板的 mouseenter 能取消卡片的 mouseleave。
  • 从卡片左边或面板左边滑出即收起。这是刻意的:容器在四周预留了空白, 把收起判定挂在容器盒子上会让面板停在"其实已经离开"的空白处不消失。
  • 点击卡片折叠成角标(2 格:标签 + 余额),再点恢复。
  • 键盘:卡片是 role="button" + tabIndex=0,Enter / 空格 切换折叠;面板是 role="dialog"。
  • 面板里有 刷新 按钮(点击立刻显示忙碌文案,完成后给一个 更新于 HH:MM:SS 时间戳), 以及手动兜底的 我刚充值了 按钮(处理中显示查询状态)。
  • HUD 里刻意没有任何 title 属性。 原生浏览器 tooltip 会在悬停约一秒后弹出黑色方块盖住面板, 所以摘要写在 aria-label 里。冒烟测试把这条当回归守卫断言。

设置

插件往 DSH 的 settings.general.item 槽位注册一行偏好 —— 余额与花费 HUD。DSH 对这个槽位的 定义是「General 区里的一行偏好 —— 单个设置专用的增量座位,不需要独占一整页」(要整页请用 settings.section;DSH 自己的 Language、link-opening、performance-usage 几行也注册在这里)。 位置:设置 → 通用 → 余额与花费 HUD。它不会占用整个设置页。

设置项 取值 默认 作用
计价模型 deepseek-flash、deepseek-v4-pro deepseek-flash 预估与分摊权重所用的价格表
自动同步价格 不自动(只手动) / 每 6 小时 / 每 1 天 / 每 3 天 / 每 7 天 不自动(只手动) 官方价格表的自动同步节奏;不选就永远不会自动跑(见 价格与同步)
前台刷新间隔 30 / 60 / 120 / 300 秒 60 秒 页面可见时的余额轮询节奏
后台(页面隐藏) 30 / 60 / 120 / 300 秒 300 秒 标签页隐藏时的轮询节奏
峰前提醒 5 / 10 / 15 / 30 分钟 10 分钟 峰谷切换提醒提前多久出现
本会话预算(元) 元,0 = 不设 0 本会话预估花费的预算
每日预算(元) 元,0 = 不设 0 北京自然日确认花费的预算
显示账户级合计 开 / 关 开 并行会话时面板里的 账户级合计 行
峰时提醒 开 / 关 开 面板里的峰谷提醒行
预算超限发系统通知 开 / 关 关 首次达到某个预算档位时的可选系统通知

持久化。 这一行写的是 localStorage 的 dsh-balance-hud:settings:v1,与记账键 (dsh-balance-hud:tracker:v4)刻意分开,两者不可能互相干扰。

两个注册点,一个容器。 设置行(settings.general.item)与浮层(shell.overlay)是 两个独立的槽位注册,没有共同的 React 祖先,因此无法共享 React state。它们改用一个小的订阅 容器 —— createSettingsStore → get / set / subscribe:set 合并一份部分补丁、落盘、广播, 所以在任一处改动,另一处立刻同步 —— 不需要刷新,也不需要共享父组件。useSettings 是架在同一个 容器上的 React 绑定。

配置永不被信任。 normalizeSettings 逐字段对白名单校验:未知模型、不在选项内的间隔、 负数预算、非布尔开关,一律回落默认值,而不是传进界面;localStorage 里的 JSON 损坏时同样回落 默认、不抛错。配置是用户可写、且随版本变化的,所以每次读取都重新校验。

预算

在设置行里可以设本会话预算和/或每日预算(单位元;0 表示不设)。

evaluateBudget(spent, budget) 返回 { level, ratio, remaining },level 为 none | warn | critical | over:

使用率 档位
< 75 % none
≥ 75 % warn
≥ 90 % critical
≥ 100 % over

remaining 夹在 0 以上,"超限"不会报出负数余额。worstBudgetLevel 再取"本会话"与"每日"两条预算 里更严重的一条,所以「今天已经用爆、但本会话还很便宜」同样会告警。

视觉。 预算达到或超过阈值时,卡片左侧出现 3 px 彩色描边 —— warn / critical 用主题的 警告色、over 用错误色。它刻意不是网格里多出来的一格:卡片网格必须保持 2 列 × 2 行, 冒烟测试用断言钉住了这一点。展开面板会多一行预算,显示「已用 / 预算」以及剩余金额或 已超限。

可选系统通知。 打开 预算超限发系统通知(默认关)后,每个档位在本次挂载内最多只发一次, 所以不会变成骚扰。它走浏览器 Notification API,只在权限已授权时才发;没有授权时静默降级为 只有描边变色,不影响其它功能。

每日预算用按天台账。 localStorage 键 dsh-balance-hud:daily:v1,内容是 { date, total }, 其中 date 是北京自然日(beijingDateKey)—— 官方计费与峰谷窗口都按北京时间,日界跟着同一个 口径才不会错位。台账只累加确认花费(某次余额读数确认的那部分),不用递推显示值,因此同一笔钱 不会被重复计入。读到新的一天时台账归零。

峰时提醒

峰时 = 北京时间周一至周五 09:00–12:00 与 14:00–18:00;峰时价恰好是闲时价的 2 倍。 minutesUntilTierChange(now) 返回 { minutes, to },to 为 'peak'(即将进入贵的那档) 或 'off'(即将回到便宜的那档)。

当"下一次切换"落在配置的提前量以内时,面板显示一行提醒:

  • 约 N 分钟后进入峰时,价格翻倍
  • 约 N 分钟后回到闲时价

一个 60 秒的滴答同时驱动倒计时与跨天重置,所以数字是活的。午休、跨夜、跨周末都算得对 —— 例如周五 18:30 → 周一 09:00 是 870 分钟 —— 冒烟测试断言了这些用例。

两条花费口径

口径 算法 性质
预估 会话 tokenUsage 投影 × 官方价格表 精确可算,但只覆盖模型 token
实际 观测到的余额减少量累计求和,按会话存在 localStorage 平台真实扣费,但有结算延迟

预估把用量拆成四类 token —— 未命中输入、缓存命中、缓存写、输出 —— 各自按峰谷两档价计价。 缓存写按未命中输入价计,与平台扣费口径一致。实际值是一个计数器,不是做减法: 每一次观测到的余额下降都加进当前会话的计数器,所以充值无法干扰它。

充值识别

原则:外部变动改变余额,但绝不能改变花费计数。

判据用的是 token 估算上界,而且它双向都成立:平台扣掉的钱不可能超过这段时间 消耗的 token 所值的钱;反过来,余额减少的量如果超过这个上界,多出来的部分也不可能来自消费。

余额升幅 > 全部会话 token 估算之和 + 0.02  →  外部加钱:充值 / 赠金到账 / 账单修正
余额降幅 > 全部会话 token 估算之和 + 0.02  →  外部减钱:赠金过期回收 / 退款 / 账单修正
其余                                      →  普通消费,照常累计

这样既不会因为"充值和消费同时发生"漏判,也不会因为正常消费波动误判。

为什么必须处理"减钱"那个方向:赠金会过期

赠金有有效期。到期未用完的部分会被平台回收,表现为余额凭空下降、却没有任何消费。 早期版本把任何余额下降都算成消费(delta < 0 → spend),于是 5 元赠金过期会被记成 5 元"本会话花费";而计数器只增不减,这个错误会一直留在记录里直到 30 天后被淘汰。

现在两个方向走同一条路径:只移动账户级字段(baseline / lastBalance), spent 原封不动,并在 topups[] 里记下是哪一类变动。

多会话时,applyExternalChange 会把所有会话的账户级字段一起移动,但刻意不动 lastRead (会话私有的"上次真正观测到的余额")。于是每个会话都会自愈:还没观测到这次变动的会话, 下次读数时仍能看到完整落差,从而自己判出这是外部变动,而不会把它摊到自己的消费上。

三层,按权威度排序

  1. token 上界判据 —— 纯客户端、始终生效,是权威判据(两个方向都是它说了算)。
  2. 官方赠金印证 —— GET /api/balance-hud/bonuses 读 ctx.deepseekAccount.getUnnotifiedBonuses()。它是只读且刻意不回执的 (不调 ackBonusNotified),因此不会消费掉 DSH 自己的赠金通知;数据保持可读, 客户端按 orderId 去重。即使这条路由不通,变动仍会被识别 —— 这一层只是印证。 注意:它只能印证"发放",官方没有"过期"的推送,所以减钱那个方向完全靠第 1 层。 顺带把官方给的 expiresAt 透出来,提示里会带上"最早到期"日期。
  3. 手动兜底 —— 我刚充值了 按钮走同一条重锚路径,处理期间显示"查询中…"。

面板会显示约 12 秒的提示,两类都有:

  • 加钱:已识别充值 ¥50.00,花费继续累计(官方赠金 ¥5.00 印证),最早到期 2025-12-31。
  • 减钱:检测到 ¥5.00 非消费性扣减(赠金过期回收等),未计入花费。

并行会话与分摊

账户余额是账户级的:它不携带"这笔钱是哪个会话花的",所以单个会话的真实花费 在本地不可观测。插件用比例分摊:

本会话分摊 = 本会话累计花费 × (本会话 token 成本 / 全部会话 token 成本)

所有会话的 token 成本都用同一套四类计价 + 峰谷价,权重才可比。只有一个会话时权重恒为 1, 数值就退化成精确值。

这是估算。 两个会话的 token 消耗与平台结算并不同步,比例会漂移;有新会话加入分母时, 数字会下跳一次。面板里有 本会话占比、其它会话、跟踪会话 几行,能看到当时的权重是多少。

持久化

记录在 localStorage 的 dsh-balance-hud:tracker:v4 下,按会话 id 分槽:

{ "sessions": { "<会话id>": { "baseline": 43.42, "lastBalance": 43.12, "spent": 0.3, "ts": 0 } } }
情况 行为
刷新页面 / 重新挂载 从 localStorage 读回,计数器继续累计
切换会话 各会话有自己的记录,互不覆盖、互不清零
识别到充值 只移动基线,spent 不动
记录超过 30 天 写回时淘汰
超过 30 个会话 保留最近 30 个
localStorage 不可用 降级为仅内存,HUD 照常工作

页面存活期间,内存里的 store 还带着一个 topups[] 历史和一个 lastObservedBalance; 但当前写盘只持久化 sessions。老数据会自动迁移 —— v3 是 { sessions: { … } }、 v2 是扁平的 { [会话id]: { … } } 字典、v1 是单条 { sessionId, … } —— 迁完会删掉旧键。

余额默认每 60 秒拉一次(页面隐藏时 5 分钟),页面回到前台立刻补读, 失败按 1s → 2s → 5s → 10s → 20s 退避重试。未登录不算错误,也不会走快速重试。两个间隔都可以在 设置行里改(见 设置)。

除了记账键,还有三个互相独立的键:dsh-balance-hud:settings:v1(设置行,见 设置)、dsh-balance-hud:daily:v1(北京自然日台账,见 预算)和 dsh-balance-hud:prices:v1(同步来的价格表与它的 HTTP 校验器,见 价格与同步)。 清理或写坏其中一个,都不会影响其它几个。

架构

浏览器(客户端半边 lib/client.js)              Host(Host 半边 lib/index.js)
  注册 shell.overlay 槽位                        inject: [deepseekAccount, webServer]
  每 60 秒轮询一次                               │
  fetch ──────────────────────────────────────► GET /api/balance-hud/balance   (只读)
                                                GET /api/balance-hud/bonuses   (只读)
                                                GET /api/balance-hud/pricing   (价格同步,需主动开启)
                                                GET /api/balance-hud/models    (模型列表,仅回环)
                                                └─► ctx.deepseekAccount.getBalance()
                                                    ctx.deepseekAccount.getUnnotifiedBonuses()
                                                     └─► 官方 account platform
                                                    fetch(PRICING_DOC_URL)   ← 仅主动开启同步时
                                                     └─► 官方价格文档页

为什么需要这条中继。 浏览器半边无法直接调 ctx.remote.account.getBalance: 不声明就访问会抛 cannot get property "remote.account" without inject; 而在客户端半边声明 inject = ["remote"] 又无法满足 —— fiber 永久挂起,插件根本不激活。 两条都在真实 @deepseek-ai/cordis 4.0.4 上复现过。所以由 Host 半边读余额, 再经仅回环、只读的路由交给浏览器。所有路由都只接受回环来源 (127.0.0.1、::1、IPv4 映射回环形式),其它一律 403;只支持 GET, 不改任何状态,不返回凭据,并带 cache-control: no-store。其中一条在用户主动开启后 才会产生对外请求 —— 价格同步。

返回 含义 HUD 显示
{ kind: 'ready', currency, rechargeTotal, bonusTotal, total } 已登录且有钱包 ¥xx.xx
{ kind: 'unavailable', reason } 未登录 / 凭据在查询期间变更 未登录
{ kind: 'error', reason } 查询失败、路由不可达、响应不合法 读取失败(原因在面板里)

价格口径

单位 元 / 百万 tokens,取自官方 模型 & 价格;闲时价恰好是峰时价的一半。

模型 缓存命中输入 缓存未命中输入 输出
deepseek-flash 闲 0.02 / 峰 0.04 闲 1 / 峰 2 闲 4 / 峰 8
deepseek-v4-pro 闲 0.15 / 峰 0.30 闲 4.5 / 峰 9 闲 13.5 / 峰 27
  • 峰时 = 北京时间周一至周五 09:00–12:00 与 14:00–18:00。其余时段(含周末与法定节假日) 按闲时计;只取本地时间的「星期 + 小时」换算,不引入时区库。
  • 缓存写按未命中输入价计费。
  • 价格表里两个模型都有;模型是一个设置项(默认 deepseek-flash),预估与分摊权重都跟着它走。 未知模型回落到 flash 价格(而不是归零),面板会说明这一点。
  • 官方调价后,可以同步价格表(见 价格与同步),或者改 PRICES 再刷新页面。
  • 上表是内置快照,日期为 2026-09-29(BUILTIN_PRICES_DATE);同步成功后就改用官方的新表。

价格与同步

预估所用的价格表内置在插件里,是一份有日期的快照(内置表日期 2026-09-29);除非你主动 要求,插件一直用它。官方随时可能调价,所以插件也可以同步官方价格表 —— 来源是 官方价格页,这也是它唯一会访问的外部主机。

两种触发方式,都必须由你主动选择:

  • 设置行里的 同步价格 按钮(英文名 Sync prices):点一次,立刻同步一次;
  • 自动同步价格 下拉:选项是 不自动(只手动) / 每 6 小时 / 每 1 天 / 每 3 天 / 每 7 天 (即 off / 6 小时 / 24 小时 / 3 天 / 7 天)。不选间隔就不会自动跑,出厂状态是「不自动」 (syncHours: 0)—— 装上插件后不碰这些控件,插件一个外部请求都不会发。 开启自动同步后,默认节奏是 24 小时(DEFAULT_SYNC_HOURS),理由是:官方调价会先在 changelog 里预告,价格页随后才改,所以这是「几个月一次」的事件,不是每天的事。价格页是 CDN 资源, 会回 ETag 与 Last-Modified,同步时带上 If-None-Match,页面没变就回 304 Not Modified 且没有响应体 —— 每次轮询只有几百字节。24 小时把「过期窗口」压到一天, 代价几乎为零。

抓取由 Host 半边执行,不是浏览器。 浏览器从不直接访问价格页,它调两条只读回环路由: GET /api/balance-hud/pricing(同步价格)与 GET /api/balance-hud/models(DSH 已注册的模型)。 只有前者可能产生对外请求;后者完全不需要网络 —— 它读 DSH 自己的 llm 服务 (listProviders() / listModels()),设置行用它填充「计价模型」下拉,拿不到 llm 时回落内置选项。

算错价,比用旧价更糟。 页面由一个纯函数解析,它的每个判断都与页面编码无关 (抓取链路可能把 UTF-8 字节按 Latin-1 解开,中文标签会变成乱码),并且做语义校验: 官方规则是「闲时价 = 峰时价的一半」,所以峰时价必须严格大于闲时价。任何一项校验不过, 整次同步整体作废,插件继续用上一份好表 —— 内置表或上次同步成功的表。 半解析出来的表永远不会被用来算钱。

存在哪里、行里显示什么。 同步结果缓存在 localStorage 的 dsh-balance-hud:prices:v1, 一起存的还有 ETag、Last-Modified 与抓取时间戳;收到 304 时只刷新检查时间,不动价格表。 设置行会显示当前用的是哪一份、是哪天的 —— 当前价格:内置表(2026-09-29), 同步成功后变成 当前价格:已同步(YYYY-MM-DD)。

安装

这是一个本地 profile 插件,不发布到 npm。

node tools/install.mjs                     # 安装到 ~/.dsh/profiles/desktop
node tools/install.mjs --dry-run           # 预览,不写任何文件
node tools/install.mjs --uninstall         # 卸载
node tools/install.mjs --profile <dir>     # 指定别的 profile

手工安装等价于:

  1. 把目录放到 ~/.dsh/profiles/<profile>/plugins/dsh-balance-hud (客户端 bundle 是从 profile 提供的,所以必须真的在 profile 目录里)。
  2. 在 profile 的 dependencies 加 "dsh-balance-hud": "file:./plugins/dsh-balance-hud", 并在 dsh.profile.bundles 里加 "dsh-balance-hud"。
  3. 把它链接进 profile 的 node_modules(Windows 用 junction,其它平台用符号链接)。
  4. 重启 DSH。

注意:profile 的 package.json 不能带 UTF-8 BOM。带 BOM 时插件管理器会以 Unexpected token '\ufeff' 失败。tools/install.mjs 会把清单重写成不带 BOM 的版本, 并保留一份 package.json.bak-balance-hud 备份。

改客户端半边(lib/client.js)只需刷新页面。改 Host 半边(lib/index.js, 例如赠金路由或价格抓取)需要重启 DSH,因为 profile bundles 不热加载。

开发

node tools/smoke.mjs                      # 离线冒烟套件,不需要装 DSH
node tools/verify-install.mjs --package   # 打包自检,不需要装 DSH
node tools/verify-install.mjs             # 校验真实安装

tools/smoke.mjs 会在假的 React 运行时、假 Host 响应、假 localStorage 和假 document 上求值真实的 lib/client.js,然后驱动组件走完整个 hook 周期。每项检查打印一行 ✓ (共 83 条),首个失败即非零退出;覆盖模块/槽位注册、卡片布局、悬停与折叠交互、 余额协议与全部路由失败模式、价格算术、显示精度、充值/过期两类外部变动判据、 持久化与迁移链,以及可见性驱动的补读。A1–A3 三个新功能各有守卫:两个槽位注册 (浮层 和 设置行)、设置默认值、脏配置的逐字段收敛、配置落盘往返、容器广播 + 部分合并 + 取消订阅、配置损坏回落、四档预算与剩余夹 0、两条预算取最严重、每日台账(累加 / 忽略 0 与负数 / 跨天归零 / 损坏回落),以及峰谷倒计时在峰时、闲时、跨午休与跨周末的正确性。价格同步也各有一条 守卫:解析器对真实页面的固定用例、以及对「UTF-8 被误按 Latin-1 解码」的乱码副本给出同一张表; 页面结构损坏或档位反常时整条放弃;resolvePrices 的来源可分辨;缓存落盘往返保留条件请求头; 以及默认零外联 —— 非法间隔收敛为关闭、24 小时 是默认档。

CI(.github/workflows/ci.yml)在 ubuntu / windows / macos 上用 Node 20 跑这两个离线脚本 —— node tools/smoke.mjs 与 node tools/verify-install.mjs --package。两个脚本都完全离线: 不依赖 DSH 安装、不联网、不需要构建,所以能在干净容器里跑,也顺带证明行尾 (.gitattributes 锁 LF)、路径派生与 import.meta 用法都不依赖 Windows。

它不覆盖 Host 半边(lib/index.js 从未被加载,因此回环过滤、对外价格抓取、 If-None-Match/304 路径与模型列表路由都没有离线测试)、真实浏览器、真实 Cordis 注入和真实平台。 lib/client.js 是手写的 CJS 风格 bundle,格式就是官方的 window.__ModuleLoader__.load({ id, factory }),所以没有构建步骤。

文件 作用
package.json 声明 dsh.bundle.patch(profile 条目树)与 dsh.client(platform: web,导出 ./client)
cordis.patch.yml 把插件作为一行插入 profile 条目树
lib/index.js Host 半边:只读回环路由(余额、赠金信号、可选的价格同步、模型列表)
lib/client.js 浏览器半边:shell.overlay HUD、settings.general.item 设置行、价格表解析与全部记账逻辑
tools/ 安装脚本、安装校验、离线冒烟套件
.github/workflows/ci.yml 离线冒烟 + 打包自检,跑 ubuntu / windows / macos
docs/DESIGN.zh-CN.md 贡献者设计说明 —— 记账模型的完整推导

隐私与安全

  • 默认:零外部请求。 装上插件后不碰价格同步的控件,插件新增的网络流量就只有到 /api/balance-hud/… 的回环 HTTP;它用内置价格表,不接触任何第三方。
  • 唯一的例外是可选的价格同步。 只有你点了 同步价格,或主动选了自动同步间隔之后, 插件才会按你选的节奏去抓 https://api-docs.deepseek.com/zh-cn/quick_start/pricing —— 官方的公开价格文档页。 这是它涉及的唯一外部主机,而且抓取由 Host 半边执行,不由浏览器发起(见 架构)。
  • 永远不会发送的东西: 凭据、API key、账号 id、会话数据、cookie,以及任何能识别你的查询参数。 它只是对一个公开文档页发一个普通 GET;同步既不读取、也不上传你的余额。
  • 本插件从不接触官方 API key。 涉及账号数据时,Host 半边只调 getBalance 和 getUnnotifiedBonuses,从不调 resolveToken 或 getPlatformSession,凭据始终留在 @deepseek-ai/dsh-deepseek-account-platform 内部。
  • 仓库里、localStorage 里、HUD 上都没有密钥、令牌或账号 id。同步价格缓存 (dsh-balance-hud:prices:v1)里只有价格、HTTP 校验器和时间戳,没有账号数据。
  • 余额、赠金与模型列表三条路由都是仅回环、只读:对非回环调用者一律 403,只返回余额数字或 已注册的模型名。插件对平台的调用走的是 DSH 本来就用的账号服务。
  • 赠金路由既只读又不回执,因此不可能吞掉 DSH 自己的赠金通知。

已知边界

  • 预估只覆盖模型 token。 任何非 token 计费项(插件、检索等)都会让它偏低。
  • 价格表是有日期的快照。 如果你一直不同步,官方调价后预估会继续用内置的 2026-09-29 表, 直到你同步、或改 PRICES 再刷新。解析器读的是当前页面结构,文档改版可能让解析失败 —— 那时同步整体作废、继续用上一份好表:宁可用旧价,也不会用错价。
  • 分摊是估算;凡是拿不到用量投影的会话在分母里权重为 0,会让占比失真。
  • 赠金"部分过期"可能被算成消费。 判据是"降幅是否超过 token 上界":如果某笔赠金 只有一部分过期,且这笔降幅恰好落在上界以内,就会被当成消费。要彻底解决需要按钱包分项的 余额明细(哪个钱包、多少、何时过期),而 getBalance() 返回的 AccountWallet 只有 { currency, balance },不含过期时间。所以这是"把误差压到不超过 token 估值", 不是消除。
  • 拿不到 token 数据时判据会变紧。 上界退化为 0 附近,此时较大的真实消费有可能被 误判为外部减钱。实际运行中 token 用量每 5 秒读一次,这种情况罕见。
  • 平台结算有延迟。 刚跑完任务时实际值可能还停在原处,而预估已经动了; 两者会在几十秒内收敛。
  • 第一个基线略晚于会话开始,更早的花费由 token 预估覆盖。
  • 账户级合计只是参照,不是某个会话的数字。
  • 未登录是正常状态:卡片会说明,登录后下一次轮询自动恢复。

许可

MIT,见 LICENSE。

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。