niliemi/dsh-billing ↗★ 0

yoka-dsh-billing

DSH plugin: prices tokens from the official model price table, shows the per-session and global running cost beside the composer's context meter, mirrors your DeepSeek account balance in the sidebar, blocks a turn past your own global ceiling (clamped to that balance) or a per-session ceiling (each session's ceiling is independent), and lets you reset the billing baseline (per session or globally, the two are fully independent) so this period goes back to zero while history only rolls into the lifetime total, which every session row shows on its own second line. 适合需要严格控制 DeepSeek 消费、设置单会话或全局预算上限的用户。

Package
yoka-dsh-billing
Compatibility
Unverified
Version
0.2.8
License
MIT
Last updated
Sep 30, 2026

Install

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:niliemi/dsh-billing

yoka-dsh-billing · DSH 计费插件

在 DSH Desktop 里按 token 计费:聊天输入框下方、token 环左边常驻显示本会话与全局两条费用,左侧边栏底部显示 DeepSeek 账号余额;每条费用右边有一个 ↺ 可重置计费起点(本次归零、历史只进累计),达到上限时宿主在该轮开始前阻断 LLM 请求。

● ¥0.0834 / ¥50.00 ↺      ● ¥12.3600 / ¥1000.00 ↺
  本会话本次计费 / 上限         全局本次计费 / 你设的全局上限
  • 左侧边栏底部(设置按钮旁):余额 ¥1007.20(充值 + 赠金,只作参考)。
  • 点角标打开面板;点 ↺ 重置计费起点;超额时弹确认框。
  • 圆点分三档:**绿 &refresh=1| 角标/面板所需的全部状态;带session 时附带该会话的用量与上限,refresh=1强制刷新余额 | | POST |/config | 写入汇率、守卫、**全局上限({limit},0 = 不设)**、模型单价覆盖({price:{key,value}}/{price:{key,reset:true}})、**某个会话自己的上限({sessionId, sessionLimitFor},传 0 清除该会话的上限)**;上限被余额夹取时响应带 limitClamped: true。0.2.1 的 {sessionLimit}(共用默认上限)已废弃、被忽略 | | POST | /override|{mode:"once"|"session"|"off"|"reset", sessionId?}放行 / 清除放行 | | POST |/reset|{scope:"session"|"global", sessionId?} **重置计费起点**(默认软重置 = 基线前移:本次归零、历史只进累计、token 一条不丢;scope:"global" 只动全局起点,各会话自己的起点不受影响,反之亦然);{hard:true}才是旧的**清空历史**(累计一起归零,不可恢复) | | GET |/pricing?q=| 价目表检索(精确命中优先) | | GET |/diag | 诊断:clientModules` 是否已把浏览器半边收进启动图,以及实时余额、自设全局上限与单会话上限表、本次/累计金额与各会话基线 |

守卫:仅接受同源 / 带 dsh-auth- cookie / 回环地址的请求,响应 cache-control: no-store。

权限、依赖与失败边界

面向 DSH STORE 自动审查的声明(package.json 里的 engines 与 dsh.compatibility 是机器可读的那一份,这里是同一件事的说明):

能力用在哪边界
文件系统(files)宿主半边读写插件自己的账本 ~/.dsh/billing/ledger.json(config storeFile 可指到别处)不读工作区、不读其它插件的数据;写盘是「临时文件 + 改名」的原子替换
网络(network)浏览器半边用同源 fetch 调本插件自己的宿主路由 /plugin-billing/…宿主半边不发起任何网络请求;不访问任何第三方域
子进程(commands)运行时零子进程;只有 tests/ 下的开发脚本(自测、HMR/HTTP 探针、发布)会用到 shell 与 git这些脚本不在 files 里,不随包发布
凭据(credentials)运行时零凭据;余额走宿主 deepseekAccount 服务,插件从不接触账号 tokentests/submit-remote.mjs 读本机 ~/.dsh/github-token.txt,仅供作者发布用
原生模块 / 生命周期脚本 / 运行时依赖都没有:纯 ESM,peerDependencies 只有 react(宿主已提供)装包不执行任何 install / build 脚本
  • 外部服务:唯一可选的对外依赖是宿主服务 deepseekAccount(读账号余额)。其余全部本地:计费靠内置价目表 lib/pricing.json 与账本里的 token 记录。
  • 兼容性:Node >=20(实测 Node 24.14.0 —— DSH Desktop 0.2.0-rc.2 自带的 Electron 44 运行时,也是两份自测所用的 Node);DSH >=0.2.0-rc.1;逐版本记录里 0.2.0-rc.2 = compatible,依据是本插件在该版本上长期实跑(宿主路由、双角标、余额、重置都在用);浏览器半边声明 platform: web,profile web。一次性 profile 的安装 / 启动 / 卸载尚未自动化验证,因此不声明 dshOperations(保持 unknown)。
  • 失败边界(都不影响会话本身):账本读不到或 JSON 损坏 → 从空账本开始,不抛错;写盘失败 → 记一条 billing: 账本写入失败 warn,进程内继续算;余额服务不可用(未登录 / 接口失败 / 服务缺失)→ 余额显示 — 且不夹取上限,计费与阻断照旧;价目表缺失或模型未知 → 用保守估算价 [1, 4, 0.1, 0];路由对非本机 / 非同源请求返回拒绝,只影响该请求。
  • DSH STORE 自动审查的现状:按商城自己的扫描规则复扫本仓库,运行文件里只剩两条信号——lib/index.js 的 files(账本写 JSON)与 lib/client.js 的 network(浏览器半边调自己的宿主路由)。开发脚本已放在 tests/,而商城把测试目录排除在「运行能力证据」之外,所以 commands / credentials 两条不再计入。剩下这两条是这类插件的本性,条目因此仍可能停在人工复核(商城的说明也写明:高能力项目可能保持 user-reviewed,声明本身不保证自动批准)。

安装

宿主半边跑在 DSH 进程里;浏览器半边是手写的 window.__ModuleLoader__ 包装(lib/client.js,无需构建),由 dsh-client-modules 以 combo URL 提供——注意不是 /plugins/yoka-dsh-billing/client.js(该形状返回 404),真实 URL 形如 /plugins/??yoka-dsh-billing/client.js&rev=,由启动图注入,页面上会自行加载。

安装 = 让桌面 profile 能解析到本包,并在 dsh.profile.bundles 里登记。已执行的命令:

# 在 DSH 会话内用 plugin_manager 工具,等价于 dsh plugin --profile desktop add link:
# target: link:C:\Users\walex lin\Desktop\dsh workflow\插件\dsh-billing

它会把 "yoka-dsh-billing": "link:…/dsh-billing" 写进 ~/.dsh/profiles/desktop/package.json 的 dependencies,并把 yoka-dsh-billing 追加进 dsh.profile.bundles,随后热应用(application: applied)。插件自带的 cordis.patch.yml:

- insert:
    - id: yoka-dsh-billing
      name: 'yoka-dsh-billing'
      config: { rate: 7.2, limit: 0, guard: true }

开发

node tests/build-pricing.mjs "
/dist/providers/data"   # 重新生成 lib/pricing.json
node tests/self-test.mjs          # 宿主:折叠 / 计价 / 余额 / 自设上限与夹取 / 守卫 / 路由 / 持久化 / 旧账本迁移 / 重置计费起点(全局与单会话互不影响)(153 项)
node tests/self-test-client.mjs   # 浏览器:模块外壳 / 槽位 / 双角标与 ↺、侧栏余额、面板文案与「单会话上限」独立段(每行两排:本次 / 累计)/ 两个 ↺ 互不牵连(107 项)
node tests/probe-hmr.mjs          # 核对「运行中的宿主」是否已发布本地这版 client.js(无需刷新/重启)

已知边界

  • 订阅制账号并不按 token 单价扣费,这里的金额是等效价值参考,不是账单。
  • 余额来自宿主 deepseekAccount 服务(可选依赖:拿不到就只影响余额显示与「上限夹取」,不影响计费、自设上限与单会话上限)。
  • 面板里的模型键是 provider/model;直接改单价只影响该键的用量。
  • 角标挂在 conversation.composer.dock 槽位,只在会话输入态(composer)渲染;欢迎页不显示。侧栏余额挂在 sidebar.footer.action,与设置按钮同一行,收起态(56px)只留一个圆点。
  • 浏览器半边的改动由模块图 HMR 自动跟上:dsh-client-hmr 每 500ms stat 一次各条目的 client.js,元数据一变就 rebuilt(id) 并经 /plugins/events 广播,页面会把该插件重挂载(不用刷新页面,但被重载插件的 React 状态会丢,所以打开着的面板会自己关掉)。node tests/probe-hmr.mjs 可核对当前发行的 revision。宿主半边(lib/index.js)不热重载源码,改完需要重启 DSH 才会生效——已安装的那份实例跑的是安装时的代码。
  • 面板与确认框的底色是完全不透明的:官方「菜单面」色(--dsw-specific-menu,浅 #f8f9faf0 / 深 #303136f0,94% 不透明)叠在 --dsw-alias-bg-layer-1(纯色层)上,外加 rgba(0,0,0,.34) 遮罩;面板经 react-dom 的 createPortal 挂到 document.body,避免侧栏祖先的 transform 困住 fixed 定位。