DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

LZ-jun /

LZ-jun/dsh-balance-usage

Verified

This plugin has no description yet.

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@b6f708e8

dsh-balance-usage

DeepSeek Harness 插件:在侧边栏左下角用绿色显示 DeepSeek 账户余额与当前峰谷时段 (空闲 / 忙碌),点击展开账户余额、峰谷切换倒计时、忙碌时段时间表与近期用量统计; 并在输入框下方与每一轮的用量旁边,按官方高峰 / 空闲价估算每个会话与每一轮的花费。

只依赖 DSH 官方账号服务与本地会话日志,不接入任何第三方服务。

English overview

A DeepSeek Harness (DSH) plugin that adds cost awareness to the Web UI. It shows the DeepSeek account balance and the current peak / off-peak phase in the sidebar, and estimates per-session and per-turn cost using the official peak and off-peak rates. The balance comes from DSH's own account service and the cost is estimated from local session logs — it talks to no third-party service.

  • Sidebar footer (bottom-left) — the account balance in green when healthy, plus a coloured dot and a label stating whether it is currently off-peak or peak (peak = twice the off-peak price). Clicking opens a panel with the balance, the current pricing window with a live countdown to the next switch, the peak-hours timetable, and recent usage (today / last 7 days / last N days, a 14-day chart, and a per-model table).
  • Composer dock — an inline 本会话消耗 ≈¥x pill next to DSH's own session usage line. Clicking expands an anchored panel upwards (a position: fixed portal, so the row never grows).
  • Per-turn actions row — an inline 本轮花费 ≈¥x trigger rendered in the same row as DSH's built-in 本轮用量 trigger, using flex order to sit to its right.

Pricing follows the official rules: peak is Mon–Fri 09:00–12:00 and 14:00–18:00 Beijing time; weekends and Chinese public holidays are off-peak all day. Un-cached input, cache reads, cache writes and output are priced separately, using the token fields DSH records per request.

Everything is local: the client talks to the host half over one read-only loopback route (/dsh-balance-usage/summary and /dsh-balance-usage/session). Credentials are never touched by the plugin — the balance comes from DSH's account service, or from a DEEPSEEK_API_KEY only when the official account is not signed in.

npm test runs test/verify.mjs, a dependency-free offline end-to-end verifier (176 assertions) that drives the real host route handler with fake services and renders the client half through a minimal React compatibility layer.

The rest of this document is in Chinese.


功能

1. 左下角余额条目(绿色)

  • 侧边栏展开时:钱包图标 + 余额 + 金额胶囊 + 时段圆点 + 时段文字。
  • 侧边栏收起(56px 轨道)时:紧凑显示 ¥12.34,圆点颜色依旧表示峰谷。
  • 金额按余额状态着色:
    • 充足 → 绿色(--dsw-alias-state-success-primary)
    • 偏低 → 琥珀色(低于 lowBalanceThreshold,默认 10)
    • 为 0 → 红色
  • 时段圆点:绿色 = 空闲时段,琥珀色 = 忙碌时段。
  • 悬停提示会同时给出余额、当前时段与「距切换还有多久」。

2. 点击展开的浮层面板

区块 内容
账户余额 总额(大字、按状态着色,点击直接跳官方账号页)、充值 / 赠送拆分、余额来源;「官方账号」与「充值」分别打开官方用量页 / 充值页
当前计价时段 空闲 / 忙碌胶囊、当前北京时间、距下一次切换的实时倒计时与切换时刻
忙碌时段(高峰计价) 明确列出 周一至周五 09:00–12:00、14:00–18:00(北京时间),并说明高峰价 = 空闲价 × 2、周末与法定节假日全天按空闲计价
近期用量 今日 / 近 7 天 / 近 N 天的 Token、估算费用、请求次数;近 14 天每日 Token 柱状图;按模型拆分表
  • 竖条悬停看详情:鼠标移到每日柱状图上会弹出明细浮层 —— 日期、合计 Token、输入 / 输出、 缓存命中、请求次数、高峰 / 空闲拆分,以及当天的花费。
  • Token 一律用 K / M 表示(不换算成 B),大小量级之间可以直接比较。
  • 面板 560px 宽,高度贴合可用窗口(最多占满视口高度减去 32px),内容超出时面板内部滚动。
  • 面板通过 react-dom 的 portal 渲染到 document.body,用 position: fixed 贴住左下角条目向上展开, 因此不受侧边栏 overflow 与过渡动画的裁剪影响。
  • 点击面板外部或按 Esc 关闭;点「刷新」、右键余额条目、或窗口重新获得焦点都会立即刷新。
  • 自动刷新间隔可调:面板底部有下拉框(关闭 / 15 秒 / 1 分钟 / 2 分钟 / 5 分钟 / 15 分钟), 改动立即生效并存进 localStorage(键名 dsh-balance-usage.prefs)。默认值来自 Host 的 config.refreshSeconds(出厂 60 秒)。
  • 外观可切换:面板底部的「外观」分三档 —— 默认(跟随应用主题)/ 深色 / 浅色, 选择也存进 dsh-balance-usage.prefs,两个浮层(左下角面板、底部与每轮的花费浮层)一起生效。 深色 / 浅色不是另画一套配色,而是在浮层根元素上覆盖官方主题 token (--dsw-alias-bg-overlay、--dsw-alias-label-*、--dsw-alias-border-* 等), 浮层里所有既有规则整体跟随,不会出现「不深不浅」的半吊子配色。
  • 不会让你白等一个刷新周期:刚打开 Harness 时 Host 路由可能刚注册、当前会话也可能还没有用量, 这时每 3 秒重试一次直到拿到数据(拿到后交回常规刷新间隔);页面重新可见或窗口重新获得焦点时 也会立刻刷新。Host 侧也不再缓存失败结果,避免客户端在 TTL 内一直拿不到数据。

3. 对话下方:本会话花费

注册进 conversation.composer.dock(输入框下方那一栏):

🎛 本会话消耗  ≈ ¥5.67        ← 胶囊是 inline-flex,和官方那行用量统计并排同一行

点一下向上弹出明细浮层:

┌───────────────────────────────┐
│ 🎛 本会话金额         ≈ ¥5.67 │
│ ───────────────────────────── │
│ 高峰花费             ≈ ¥1.20 │
│ 空闲花费             ≈ ¥4.47 │
│ 请求次数              128 次 │
│ 缓存命中率              99.4% │
│ 模型           deepseek-flash │
│                               │
│ 本地按官方价表估算,非官方账单。│
└───────────────────────────────┘

关键是展开不会改变那一栏的高度(早期版本就是在这一点上做错了):

  • 胶囊外层 display:inline-flex,与官方那行用量统计并排,不另起一行;
  • 展开的浮层是 portal 到 document.body 的 position: fixed 元素,用官方基元 primitives.useAnchoredPosition({ side: "top", gap: 6, margin: 8 }) 锚在胶囊上沿并向上展开 —— 浮层完全脱离文档流,展开 / 收起时那一栏高度一个像素都不变;
  • 关闭走官方基元 primitives.useDismissOnOutsidePointer,另加 Esc;
  • 两个官方基元缺失时都有降级实现(自己按 bottom 定位 + 自己监听外部点击),功能不受影响;
  • 官方界面已经显示 Token 用量,所以这里只谈钱:胶囊显示金额,浮层显示花费拆分与请求数, 不再重复 未缓存输入 / 缓存读取 / 输出 那些数字;
  • 没有可统计用量时整条不渲染(不留占位、不留空行)。

数据来自 Host 的 /dsh-balance-usage/session?sessionId=…(按会话统计整个对话,不按时间窗口截断, 带 15 秒缓存),随自动刷新间隔一起更新。

4. 每一轮:本轮花费

挂在 conversation.chat.assistant-actions —— 也就是官方「本轮用量」所在的那一行。 DSH 内部把该槽位的内容当作 extraActions 传给 MessageIconActions,而官方的 「本轮用量」正是同一个组件的 usageAction:

<MessageIconActions
   extraActions={renderSlot("conversation.chat.assistant-actions", { messageId })}
   usageAction={<TurnUsagePanel usage={data.tokenUsage} />}   // 官方「本轮用量 11.14M tok」
/>

extraActions 在 usageAction 之前渲染,而 .actions 是 flex 容器, 所以本插件用 order: 1 把自己排到所有默认 order: 0 的兄弟之后,落到行尾:

… ⧉  ⑂  |  本轮用量 11.14M tok  12:34  |  🎛 本轮花费 ≈¥0.46
                                                  ↑ 本插件(order:1 排到最后)
  • 触发器尺寸对齐官方 TurnUsagePanel 的触发器:inline-flex、高 calc(28px + var(--dsh-content-font-delta,0px))、border-radius:var(--dsw-radius-sm), hover / 展开时用 --dsw-alias-interactive-bg-hover —— 和官方那个触发器是同一套控件;
  • 图标用官方的 IconGaugeOutlineRegular(仪表盘 = 计量 / 消耗)。 官方「本轮用量」用的是 IconDatabaseOutlineRegular,所以花费这边刻意不复用它;
  • 点击用和官方完全相同的交互向上弹出明细浮层(portal 到 body + position: fixed + useAnchoredPosition({ side: "top" })):标题「本轮金额」+ 高峰花费 / 空闲花费 / 请求次数 / 模型;
  • 该槽位只提供 messageId,所以 Host 会把每一轮包含哪些 assistant 消息 id 一起返回, 客户端按 messageId → 轮次 → 花费 反查;
  • 没有该轮数据时返回 null(该 slot 的契约明确允许「无内容的条目返回 null」), 不会留空位、不会多出空白。

5. 空闲 / 忙碌时段的官方口径

依据 DeepSeek 官方定价文档:

Off-peak rates are half of the peak rates. Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC, Monday through Friday, excluding Chinese public holidays. All other hours are off-peak, including weekends and Chinese public holidays in full.

—— https://api-docs.deepseek.com/quick_start/pricing

换算为北京时间(UTC+8)即:

时间
忙碌(高峰计价) 周一至周五 09:00–12:00、14:00–18:00
空闲(低谷计价) 工作日其余时间、周六周日全天、中国法定节假日全天

内置了 2026 年法定节假日表(依据国务院办公厅 2025-11-04 通知), 可用 holidays 配置项整体覆盖。

峰谷归属按请求发起时刻(step/start 事件时间)判定,取不到时回退到消息完成时刻。 官方未公开跨时段请求按发起还是按完成计费,这是合理近似;实测请求耗时在秒级,边界影响极小。

6. 价格表(空闲时段价,元 / 百万 tokens)

模型 输入(缓存未命中) 输入(缓存命中) 输出
deepseek-flash 1 0.02 4
deepseek-v4-pro 4.5 0.15 13.5
deepseek-chat(旧) 2 0.5 3
deepseek-reasoner(旧) 4 1 16

高峰时段按 ×2 计。模型名做了归一化:含 pro → v4-pro,含 flash → flash, 因此 deepseek-flash 与旧名 deepseek-v4-flash 走同一套价。

计费口径:DSH 的 TokenUsage.inputTokens 是未缓存输入,不含缓存命中的部分 —— 实测 inputTokens + cacheReadTokens + cacheWriteTokens + outputTokens === totalTokens, 正好和官方价表的「INPUT (CACHE MISS)」/「INPUT (CACHE HIT)」两栏一一对应。 所以未缓存输入按 input 价、缓存读取按 cacheRead 价分别计价(早期版本曾把缓存读取从输入里 再减一次,导致未缓存输入被算成 0、费用偏低,已修正)。


安装

本插件是一个标准的 DSH bundle 包(package.json 里带 dsh.bundle.patch 与 dsh.client), 安装后由 profile 的 bundle 栈自动挂载,无需手改 cordis.patch.yml。

已验证的安装方式(本地 link)

cd $env:DSH_PROFILE_DIR      # 通常是 C:\Users\<你>\.dsh\profiles\desktop
pnpm add "link:C:\Users\<你>\Documents\deepseek-harness\default-workspace\dsh-balance-usage"

然后把 dsh-balance-usage 加进该 profile package.json 的 dsh.profile.bundles 数组:

{
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "@deepseek-ai/dsh-web-app",
        "dsh-balance-usage"
      ]
    }
  }
}

link: 会建立指向源码目录的符号链接,改完源码刷新页面即可看到客户端改动 (服务端改动需要重启 DSH Desktop)。


配置

所有字段都是可选的,写在注册项的 config 块里(cordis.patch.yml):

- id: balance-usage
  name: dsh-balance-usage
  config:
    utcOffsetHours: 8
    peakWindows:
      - { start: 9, end: 12 }
      - { start: 14, end: 18 }
    peakWeekdays: [1, 2, 3, 4, 5]     # 0=周日 … 6=周六
    peakMultiplier: 2
    holidays: ["2026-10-01"]          # 覆盖内置节假日表
    scanDays: 30                      # 用量回溯天数
    maxSessions: 150                  # 单次统计最多扫描的会话数
    cacheTtlMs: 120000                # 服务端统计缓存
    refreshSeconds: 60                # 客户端自动刷新间隔(秒),0 = 不自动刷新
    apiKey: ""                        # 未登录官方账号时的回退 API Key
    lowBalanceThreshold: 10           # 低于该值金额转琥珀色
    currency: ""                      # 强制币种,如 CNY / USD
    price:                            # 逐项覆盖价格(空闲时段价)
      deepseek-flash: { input: 1, cacheRead: 0.02, cacheWrite: 0, output: 4 }

refreshSeconds 是 Host 侧配置,改动后需要重启 DSH Desktop; 客户端下拉框里的选择只覆盖它,可随时改、无需重启。

apiKey 留空时依次尝试环境变量 DEEPSEEK_API_KEY。已登录 DSH 官方账号时不会用到它。


权限与数据

  • 余额优先通过 DSH 内置账号服务读取,插件不接触、不保存任何登录凭据。
  • 仅在未登录官方账号且配置了 API Key 时,才会向 api.deepseek.com/user/balance 发一次余额查询。
  • 用量统计完全基于本机会话日志(ctx.sessionQuery 读取 assistant/message 的 token usage)计算, 不向任何外部服务发送会话内容。
  • 客户端与服务端只通过一个本机只读路由 /dsh-balance-usage/summary 通信。

已知限制

  • 显示的费用是本地估算,不是 DeepSeek 官方账单;官方调价后需更新价表。
  • 用量统计默认只扫描最新的 150 个会话(maxSessions)、最近 30 天(scanDays), 面板上会显示「已扫描 N 个会话」。
  • 本地 token 数与官方计费口径可能不同(系统提示词、工具定义的内部算法未公开)。
  • 模型靠事件里的 request/header 推断,同名不同版本无法区分。
  • 法定节假日表内置到 2026 年,之后需通过 holidays 配置补充。

可以直接用的官方 UI 基元

客户端 bundle 里 require("@deepseek-ai/dsh-client-ui-primitives") 就能拿到官方 UI 库 (由 harness 在运行时提供,不需要写进 dsh.client.inject)。下面是官方自己正在使用的那批导出:

类别 可用项
图标 约 200 个 Icon*,每个都有 OutlineRegular(线框)/ Medium / Artwork 三档,例如 IconGaugeOutlineRegular、IconDatabaseOutlineRegular、IconClockOutlineRegular、IconDataOutlineRegular、IconSparkleRegular、IconRefreshOutlineRegular、IconWarningOutlineRegular、IconInfoOutlineRegular、IconCopyOutlineRegular、IconChevronDownOutlineRegular、IconRightUpOutlineRegular、IconLinkOutlineRegular
基础控件 Button、Tooltip、Modal、Menu / MenuItemButton / MenuSurface / MenuGroup、HoverCard、Toast、Tag、Pill、StateDot、Switch、Checkbox、Input、ShortcutKeys、SegmentedTabs / SegmentedControl、DisclosureRow、TextShimmer
内容展示 MarkdownText、CodeBlock、JsonTree / JsonBlock、DiffBlock、ReadBlock、SearchBlock、WebBlock、TerminalBlock、PathLabel、FileTypeIcon、ImageLightbox
设置页 SettingsForm / SettingsFormModel / SettingsValueField / SettingsSecretField、settingsNumberField、settingsTextField
Hook useAnchoredPosition、useAnchoredMaxHeight、useDismissOnOutsidePointer、useModalLayer、observeComposition、pointerModality
工具函数 writeClipboard、relativeTime、focusWithoutRing、closeTopModal、isBehindModal、classifyFileType、fileSizeText、languageForPath

本插件用到的是 IconGaugeOutlineRegular(花费)、useAnchoredPosition(side: "top" 向上锚定)、 useDismissOnOutsidePointer(点击外部关闭)。三者缺失时都有降级实现,功能不受影响。

开发与验证

test/verify.mjs 是离线端到端验证脚本,不需要 DSH、不需要网络、没有任何依赖: 用假的 ctx / sessionQuery / deepseekAccount 驱动 Host 半体并直接调用它注册的 HTTP 路由处理器,再用一层极简 React 兼容层渲染客户端半体(含 ref 提交、依赖数组、document 事件与 portal 模拟)。

176 项断言,覆盖:

  • 余额拆分(充值 / 赠送)、峰谷判定、中国法定节假日、周末;
  • 计费口径:未缓存输入 / 缓存读取 / 缓存写入 / 输出分别计价,高峰 ×2;
  • 逐日、逐模型、逐轮(turn)聚合,以及逐轮之和 == 会话总额;
  • 左下角条目、面板、底部花费胶囊、每轮花费的渲染输出;
  • 浮层必须是 portal 到 body 的 position: fixed 元素且 side: "top"(展开不撑大布局);
  • 点击开关、点击外部关闭、Esc、以及各种缺参数时的降级。
npm test          # 等价于 node test/verify.mjs

改动何时生效

改动的文件 生效方式
lib/client.js 直接改即生效(link: 安装下 DSH 的 client HMR 会发布新版本);刷新页面即可看到
lib/index.js、package.json、cordis.patch.yml 属于 Host 侧,需重启 DSH Desktop

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit b6f708e8ac43

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout