hehe1111
dsh-my-cost
DeepSeek Harness 费用与余额插件
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-my-cost
DeepSeek Harness 费用与余额插件:定时拉取官方定价、缓存感知的会话/消息级费用估算(峰谷定价兼容),以及账户余额查询。
- 价格轮询:每小时抓取 DeepSeek 官方定价页(
api-docs.deepseek.com/zh-cn/quick_start/pricing/),解析后写入$DSH_HOME/prices/official-prices.json;失败时保留旧价格并记录错误。 - 峰谷定价兼容:官方宣布 2026-08-17 起采用峰谷定价。插件解析出峰谷两档价格与生效日期,但 生效日之前一律按当前(平价)价格计费——峰谷机制已就位,目前处于休眠状态;生效日后自动按北京时间的峰谷时段计价(高峰 9:00–12:00、14:00–18:00)。可用
peakPricing: on/off强制开启/关闭。 - 缓存计费:输入(缓存未命中)、输出、缓存命中、缓存写入四类 token 各自按官方单价计价。
- 余额查询:通过
GET https://api.deepseek.com/user/balance查询账户余额,凭证复用 DSH 凭证服务里的DEEPSEEK_API_KEY(web「设置 → 模型」页写入的那个),API key 只在 host 端使用,不会下发到浏览器。 - 展示:会话头部估算费用、每条消息的估算费用、输入框底部信息栏的会话总花费(与 turns/steps/token 统计同一
conversation.composer.dock,悬停显示分模型明细)、设置页「费用与余额」面板(余额卡片 + 跨会话费用汇总 + 当前/峰谷价格表)。
安装
# 1. 安装到 web profile(已发布到 npm,直接用包名;本地开发可改用 link: 指向本地目录)
dsh plugin --profile web add dsh-my-cost
# 2. 在 ~/.dsh/profiles/web/cordis.patch.yml 追加:
# - insert:
# - id: my-cost
# name: 'dsh-my-cost'
# config:
# currency: CNY # 显示币种
# pollIntervalMs: 3600000 # 价格轮询周期(默认 1 小时)
# balanceIntervalMs: 3600000 # 余额刷新周期
# peakPricing: auto # auto | on | off
# 3. 重启 dsh web,浏览器硬刷新
说明:也可以从 GitHub 源码安装:
dsh plugin --profile web add github:hehe1111/dsh-my-cost。lib/为构建产物(不入 git),git 安装会触发prepare构建脚本;pnpm ≥10 默认拦截 git 依赖的 prepare,需按 dsh 提示把dsh-my-cost加入 profile 的pnpm-workspace.yamlallowBuilds(key 含冒号,写入时加引号)后重装。推荐直接用 npm 包安装(发布时已构建)。
开发与构建
lib/ 是纯构建产物(.gitignore 忽略、不入库):src/(host 端,Node)与 client/
(浏览器端,React 19 + JSX)经 Vite 8(编程式构建脚本)产出 lib/index.js 与
lib/client.js。prepare 脚本会在 npm install/npm publish 时自动构建:
npm install # 安装 devDependencies(vite ^8、vitest ^4、@deepseek-ai/schemastery;react/react-dom 运行时由 dsh 的 seed 词表提供,不声明)
npm run build # node scripts/build.mjs → 构建 host + client 两个目标到 lib/
npm test # vitest:单测(定价核心/投影/格式化/组件渲染)+ 集成(apply 接线、/my-cost/status 路由)+ 构建产物契约
- host 产物
lib/index.js(ESM):src/源码,zod/@deepseek-ai/schemastery/node:*保持 external(由 profile pnpm 闭包注入)。 - client 产物
lib/client.js(CJS):符合 DSH 官方 client 契约——window.__ModuleLoader__.load({ id, factory })包装,react/react/jsx-runtime/react-dom/react-dom/client为平台 seed 词、不打进包(低余额 toast 用createRoot渲染在插槽之外的独立容器)。 - 样式:client 全部使用 JS 内联样式对象(
client/styles/,渲染为style={{...}})。 为什么必须这样、官方怎么写、限制在哪,见下方「样式编写」小节。 - 改
client/或src/源码 → 重新npm run build:client 改动重装 + 刷新页面即可; host(src/)改动需重启 web(ESM 缓存)。
样式编写(为什么全用内联)
限制(硬约束,实测确认)
- client 通道没有 CSS 资源路由。dsh 的 client-modules 只服务
/plugins/<id>/client.js(+.map),浏览器加载不到任何独立.css文件。Vite lib 模式遇到 CSS 导入必然提取成 独立.css(cssCodeSplit: false也不内联)——所以 Less/CSS Modules/Tailwind/UnoCSS 的 默认产物在这里会静默丢失样式。 - 宿主 CSS 可覆盖插件 class。官方坑清单(make-dsh-plugin skill 的
references/gotchas.md第 4 条):宿主全局 CSS 可能覆盖插件注入的<style>规则或命中同名 class;关键样式必须 用 JS 内联 style 属性(内联优先级最高,宿主无法覆盖),不要依赖 CSS class 注入。
原因
- 单文件通道 + 无 CSS 路由 → 样式要么进 JS bundle,要么不存在;
- class 体系(含工具类框架)样式可被宿主更高优先级规则覆盖;内联
style属性无法被覆盖; - Tailwind/UnoCSS 的 preflight(全局 reset)注入会冲掉宿主 UI,必须关闭。
官方写法
- 官方 client 包把 CSS 作为字符串内联进 bundle,运行时注入
<style>标签,例如const css$9 = ".lXshSW_root{...}"+document.createElement("style")+data-plugin-css去重标记;client-modules 的claimStyles()跟踪这些标签,插件卸载时自动清理。 - 对"关键样式",官方建议直接 JS 内联(见限制 2)。
本插件写法
- 全部使用 JS 内联样式对象:
client/styles/*.js定义样式对象,组件以style={{...}}渲染;不注入任何<style>标签、零 CSS 工具链、零额外依赖。 - 曾引入 UnoCSS(构建时扫描生成 CSS 字符串内联进 bundle + 运行时注入
<style>), 因"class 可被宿主覆盖 + 额外工具链 + preflight 风险"最终回归纯内联。 - 动态样式(如随状态切换的颜色)用
{ ...baseStyle, color: xxx }在组件内合并。
结论:除非未来出现官方的 CSS 资源通道,否则新样式一律写成
client/styles/里的样式对象 并用style={{...}}内联,不要引入 CSS 文件、class 体系或工具类框架。
配置
| 字段 | 默认 | 说明 |
|---|---|---|
currency | CNY | 显示币种 |
pricesDir | $DSH_HOME/prices | 价格文件目录 |
pricesUrl | 官方定价页 | 轮询源 |
balanceUrl | https://api.deepseek.com/user/balance | 余额 API |
apiKeyRef | DEEPSEEK_API_KEY | 凭证引用(走 ctx.credentials) |
pollIntervalMs | 3600000 | 价格轮询周期(最小 60s) |
balanceIntervalMs | 3600000 | 余额自动刷新周期 |
balanceCacheMs | 300000 | 余额缓存时长(?refreshBalance=1 可强制) |
peakPricing | auto | auto:生效日之后自动启用峰谷;on/off 强制 |
价格覆盖:$DSH_HOME/prices/local-prices.json(可选)可覆盖官方价,格式:
{ "prices": { "deepseek-v4-flash": { "inputPerM": 1.5, "outputPerM": 3, "cacheReadPerM": 0.03 } } }
HTTP 接口
GET /my-cost/status[?refreshBalance=1]:返回当前合并价格、峰谷时段/生效状态、以及(缓存的)账户余额。仅绑定在本机 dsh web 服务上。
代码结构
dsh-my-cost/
├── package.json # 包元数据:入口/exports、dsh.client 声明、构建脚本、peerDependencies
├── README.md # 本文档
├── LICENSE # MIT 许可
├── .gitignore # 忽略 node_modules/、.temp/ 等
│
├── scripts/
│ └── build.mjs # Vite 构建脚本:src/ → lib/index.js(Node ESM),client/ → lib/client.js
│
├── src/ # 【host 端】Node 侧源码(纯 ESM,无中间产物)
│ ├── index.js # host 插件入口:注册 myCost 会话费用统计、每小时价格轮询、
│ │ # GET /my-cost/status 路由、余额查询(凭证走 ctx.credentials,key 不下发)
│ └── pricing-core.js # 定价纯函数核心:官方定价页解析、峰谷计价、costOf 费用计算、默认价表
│
├── client/ # 【浏览器端】React 19 + JSX 源码
│ ├── index.jsx # 浏览器插件入口:apply(ctx) 注册字典、3 个插槽、低余额轮询
│ ├── toast.jsx # 低余额 toast:React 组件 + createRoot 挂载(渲染在插槽之外)
│ ├── status.js # useStatus 钩子(拉 /my-cost/status)+ 低余额轮询逻辑
│ ├── format.js # 金额/token/时间/价格格式化纯函数
│ ├── locales.js # myCost 命名空间的中英文案
│ ├── components/ # 三个插槽组件
│ │ ├── MessageCost.jsx # 每条消息的估算费用 chip(conversation.chat.assistant-actions)
│ │ ├── SessionCostLine.jsx # 输入框底部信息栏:会话总花费 + 跨会话总计 + 余额(composer.dock)
│ │ └── MyCostSection.jsx # 设置页「费用与余额」面板(settings.section)
│ └── styles/ # 内联样式对象(无 .css 文件,见「样式编写」小节)
│ ├── section.js # MyCostSection 的样式对象
│ ├── dock.js # SessionCostLine 的样式对象
│ ├── message.js # MessageCost 的样式对象
│ └── toast.js # 低余额 toast 的样式对象
│
└── lib/ # 【构建产物】Vite 输出(.gitignore,不入 git;npm 发布时由 prepare 构建)
├── index.js # host bundle(Node ESM;zod/官方包/node:* 为 external)
└── client.js # 浏览器 bundle(CJS + window.__ModuleLoader__.load 包装)
补充说明("会话投影"是什么):dsh 有个叫 session projection(会话投影) 的机制——框架把 每个会话的 token 用量事件依次回放给插件的统计逻辑,插件累计后产出费用数据,界面再用
useProjection("myCost")读取。插件本身不用存数据,投影值会随会话持久化。
架构图(Archify)
交互式架构图(由 Archify 生成):
- 在线预览(GitHub Pages):https://hehe1111.github.io/dsh-my-cost/architecture.html
- 本地文件:docs/architecture.html;源规格:docs/architecture.json
flowchart LR
subgraph ext["外网"]
pp["DeepSeek 定价页<br/>api-docs.deepseek.com"]
ba["DeepSeek 余额 API<br/>api.deepseek.com/user/balance"]
end
subgraph host["Host(Node)"]
runtime["DSH 框架运行时<br/>webServer · credentials · timer · 投影重放"]
plugin["myCost Host 插件<br/>src/index.js"]
core["定价核心<br/>src/pricing-core.js"]
prices["$DSH_HOME/prices<br/>official/local JSON"]
sessions["DSH 会话存储<br/>事件日志 + 投影值"]
end
subgraph browser["Browser(Web)"]
shell["dsh Web Shell<br/>seed 词表 · __ModuleLoader__"]
client["myCost Client 插件<br/>client/ · 3 插槽 + toast"]
end
plugin -->|"每小时抓取"| pp
plugin -->|"GET /user/balance · Bearer key"| ba
plugin -->|"解析 / 计价"| core
plugin -->|"写 JSON"| prices
plugin -.->|"注册投影"| runtime
runtime -->|"回放用量事件"| plugin
runtime -->|"持久化"| sessions
client -->|"通道 A · GET /my-cost/status(价格 + 余额)"| plugin
client -->|"通道 B · useProjection(费用)"| shell
client -.->|"__ModuleLoader__.load 注册"| shell
核心设计是两条互不重叠的数据通道:
- 通道 A(HTTP):
GET /my-cost/status—— 价格表、峰谷状态、账户余额。API key 只在 host 侧解析(ctx.credentials),绝不下发浏览器。 - 通道 B(会话投影):
useProjection("myCost")/projectionValues.myCost—— 费用数据。 框架回放每次请求的 token 用量事件,插件纯函数累计计价,结果随会话持久化。
在线打开 https://hehe1111.github.io/dsh-my-cost/architecture.html(或本地
docs/architecture.html)可交互查看:聚焦节点、追踪路径、切换主题/预设。
重新生成:安装 Archify 的 DSH 集成(dsh plugin --profile web add @tt-a1i/archify-dsh@0.1.0)
后用 archify skill 按 docs/architecture.json 规格 validate + deliver。
说明
- 费用是怎么算出来的(为什么插件能算花费):依赖上面的会话投影机制,相当于给每个会话装了 一个"自动记账器"——框架负责把账本事件送过来(每次请求的输入/输出/缓存命中/缓存写入 token 用量),插件只负责"怎么算"(按当前官方单价实时计价,且按事件发生时刻计费,支持未来的 峰谷时段),记账结果跟着会话自动保存、界面随时读取。插件不需要自己监听事件、不需要自己 存数据——这就是它能在 host 端无侵入地算出每会话/每消息花费的原因。
- 所有费用均为估算,以官方账单为准。
- 价格轮询失败不影响运行:继续使用上次成功拉取的价格(或内置默认价)。
- 源码在
src/(host 端:index.js+pricing-core.js)与client/(浏览器端,React 19);lib/为 Vite 构建产物。
License
MIT