Back to home

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-costlib/ 为构建产物(不入 git),git 安装会触发 prepare 构建脚本;pnpm ≥10 默认拦截 git 依赖的 prepare,需按 dsh 提示把 dsh-my-cost 加入 profile 的 pnpm-workspace.yaml allowBuilds(key 含冒号,写入时加引号)后重装。推荐直接用 npm 包安装(发布时已构建)。

开发与构建

lib/纯构建产物.gitignore 忽略、不入库):src/(host 端,Node)与 client/ (浏览器端,React 19 + JSX)经 Vite 8(编程式构建脚本)产出 lib/index.jslib/client.jsprepare 脚本会在 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 缓存)。

样式编写(为什么全用内联)

限制(硬约束,实测确认)

  1. client 通道没有 CSS 资源路由。dsh 的 client-modules 只服务 /plugins/<id>/client.js (+ .map),浏览器加载不到任何独立 .css 文件。Vite lib 模式遇到 CSS 导入必然提取成 独立 .csscssCodeSplit: false 也不内联)——所以 Less/CSS Modules/Tailwind/UnoCSS 的 默认产物在这里会静默丢失样式
  2. 宿主 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 体系或工具类框架。

配置

字段默认说明
currencyCNY显示币种
pricesDir$DSH_HOME/prices价格文件目录
pricesUrl官方定价页轮询源
balanceUrlhttps://api.deepseek.com/user/balance余额 API
apiKeyRefDEEPSEEK_API_KEY凭证引用(走 ctx.credentials
pollIntervalMs3600000价格轮询周期(最小 60s)
balanceIntervalMs3600000余额自动刷新周期
balanceCacheMs300000余额缓存时长(?refreshBalance=1 可强制)
peakPricingautoauto:生效日之后自动启用峰谷;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 生成):

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