以DSH小鲸鱼挂件为例学会开发DSH插件:3个文件与23条路由的完整架构
【免费下载链接】DeepSeek-Balance-Whale-WidgetDeepSeek Harness(DSH)一只住在 DSH 界面右下角的小鲸鱼娘,帮你盯着DeepSeek账户余额。QQ弹弹,支持拖拽吸附、左吸附翻转、数字滚动动画,随界面自动启用,建议直接喊来你的dsh安装项目地址: https://gitcode.com/gh_mirrors/de/DeepSeek-Balance-Whale-Widget
本文以 DeepSeek-Balance-Whale-Widget 项目(DSH 小鲸鱼记账挂件插件dsh-whale-widget)为例,带你拆解一个 DeepSeek Harness(DSH)Web 插件的完整架构:它只靠 3 个核心 JS 文件 + 1 个挂载声明,就实现了余额监控、小鲸鱼记账、音效泡泡等全部功能,宿主侧共注册 23 条/dsh-whale/*路由。读完这篇 DSH 插件开发指南,你就能掌握「bundle 插件包 + webServer 路由 + 页面注入脚本」这套标准架构。
1. 先认识这个插件:3 个文件撑起全部功能
DSH 小鲸鱼挂件住在 DSH Web 界面右下角:气泡里显示 DeepSeek API 余额、今日已用、每轮对话消耗,支持拖拽吸附、Q 弹按压、音效与自定义泡泡。它的全部能力来自 3 个核心文件,各司其职:
| 文件 | 角色 | 一句话说明 |
|---|---|---|
| package.json | 插件身份证 | 声明包名dsh-whale-widget与dsh.bundle.patch,DSH 据此识别它为 bundle 插件 |
| lib/index.js | 宿主侧本体(约 3900 行) | 注册全部路由、监听会话事件、记账与余额拉取 |
| lib/accounting.mjs | 记账内核(仅 252 行) | 定点金额运算 + 余额观测/校正账本,独立成模块方便测试 |
| assets/whale-widget.js | 前端挂件本体(约 1.7 万行) | 原生 JS,运行在浏览器页面里,画鲸鱼、气泡、菜单 |
这套「宿主(Node 进程)+ 前端(浏览器)」双半区结构是 DSH 插件的通用形态:宿主负责取数、存盘、注册 HTTP 路由;前端只负责渲染与交互,两者通过路由通信。
2. 插件是如何被 DSH 加载的:2 个声明文件
包元数据:让 DSH 认出这是个插件
package.json 里最关键的是dsh.bundle.patch字段,它指向挂载声明文件;main指向宿主入口:
"main": "lib/index.js", "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }挂载声明:一行 YAML 把自己插进配置树
cordis.patch.yml 内容只有两行有效配置:
- insert: - id: dsh-whale-widget name: dsh-whale-widgetDSH 启动时会按 bundle 层栈叠加各插件的 patch,把这个插件插入 Web profile 的配置树。装进 DSH 只需一条命令:
dsh plugin --profile web add dsh-whale-widget⚠️ 新手常见坑:发布包里
name必须写插件包名(dsh-whale-widget),不要写成name: ./xxx.mjs?v=N那种相对路径——那是本机手动复制到 profile 时的热更写法,发布给他人会因路径不存在而破坏启动。完整原因见 whale-widget-prompt.md 的「关键技术结论」一节。
3. 宿主侧完整架构:23 条路由怎么组织
打开 lib/index.js,你会发现所有路由都不是零散register的,而是统一走一个包装入口 registerRoute:它给每条路由自动套上「浏览器信任栅栏」(拦截跨站请求、写操作仅限本机回环地址),再调用ctx.webServer.register。这是插件安全设计的第一原则——统一入口,默认拒绝。
全部 23 条/dsh-whale/*路由按职责可分为 5 组(源码位置见 lib/index.js):
数据类:挂件内容的"取数口"
| 路由 | 方法 | 职责 |
|---|---|---|
/dsh-whale/balance.json | GET | 余额 + 今日已用 + 峰谷状态,永远返回 200 JSON |
/dsh-whale/last-turn.json | GET | 最近一轮对话消耗(seq递增,前端据此判断"新轮次") |
/dsh-whale/wait.json | GET | 当前挂起的「提问/授权」事件,驱动提示音 |
/dsh-whale/usage-records.json | GET | 小鲸鱼记账的逐日/逐条明细 |
/dsh-whale/balance-adjustments.json | GET/POST | 余额校正(充值等非调用扣减) |
配置类:用户设置的持久化回路
| 路由 | 方法 | 职责 |
|---|---|---|
/dsh-whale/size.json | GET/PUT | 外观与开关(缩放、音量、吸附等) |
/dsh-whale/usage-settings.json | GET/PUT | 提醒类设置(预警、预算、每轮消耗提示内容) |
/dsh-whale/api-models.json | GET/POST/DELETE | 自定义 API 模型注册表(密钥走 DSH 凭据,不落配置) |
媒体类:图片、音效、前端代码
| 路由 | 职责 |
|---|---|
/dsh-whale/image.png | 鲸鱼本体图(读包内 assets/DSniang1.png) |
/dsh-whale/rua.gif | 随机台词/撒娇动图 |
/dsh-whale/widget.js | 下发前端挂件源码(宿主按 mtime 热读取,改前端不用重启) |
/dsh-whale/sound/press.mp3/sound/release.mp3 | 按压/松开音效,按?set=duck\|fx1切换 |
/dsh-whale/audio.json+audio-fragment.wav | 音效库索引与片段试听 |
/dsh-whale/role-image.png | 自定义角色图 |
自定义资产类:用户上传内容的管理
| 路由 | 职责 |
|---|---|
/dsh-whale/roles.json/role-pin.json/role-delete.json | 自定义角色列表、置顶、删除 |
/dsh-whale/bubble.json | 自定义泡泡(点击序列 + 模块库) |
/dsh-whale/bubble-imgs.json/bubble-img-upload.json/bubble-img.png | 泡泡图库的列表、上传、读取 |
架构要点:生命周期与可迁移路径
- disposer 收集模式:每个
registerRoute/tapIndex的返回物都 push 进disposers数组,统一挂到ctx.effect上清理,HMR 热重载不会留下重复路由(见 lib/index.js)。 - 可迁移路径:lib/index.js#L18-L22 用
fileURLToPath(import.meta.url)推出包根目录PACKAGE_ROOT,资源一律相对包内assets/取;用户数据(账本、角色、音频)写到$DSH_HOME(默认~/.dsh),因为node_modules可能在更新时被清理。 - 会话事件监听:宿主用
ctx.on('session/event', ...)捕获每轮assistant/message的真实 usage(input/cache/output/reasoning tokens),turn/end时按峰谷定价表结算写入lastTurn——前端每秒轮询last-turn.json,看到seq变大就弹出消耗泡泡。
4. 记账内核:252 行如何避免"浮点数陷阱"
lib/accounting.mjs 是独立模块,只导出纯函数:preciseMoney/addMoney/sumMoney/observeBalance等。核心设计有两个:
- 定点金额:金额先放大 1 亿倍(
SCALE = 100000000)转成整数运算,8 位小数记账、2 位小数显示,彻底绕开浮点误差(accounting.mjs#L4-L20)。 - 观测 ≠ 交易:余额下降记为消费(debit),余额上升(充值/赠金)单独记为 credit,不会冲掉已有消费;充值出现「待核对余额调整」提示,由用户在界面上显式校正。
把记账逻辑从 3900 行的宿主本体里拆出来,是新手最容易忽略、却最值钱的架构习惯:数据计算与 HTTP 处理分离,前者可独立验证,后者只做路由与校验。
5. 前端挂件:一个"寄生"在页面上的原生 JS 程序
assets/whale-widget.js 由宿主通过tapIndex注入<script defer src="/dsh-whale/widget.js">到 DSH 的 index.html(幂等注入,已存在则跳过)。它有几个值得学习的手法:
- 幂等守卫:首行
if (window.__dshWhaleWidget) return,防止热重载后重复初始化(whale-widget.js#L1-L4)。 - 环境自检:脚本会注入 DSH 的每个index 页面(含插件市场等 SPA 视图),所以开头先检测「当前是否主聊天界面」(识别 composer 输入区),不是就干脆不碰 DOM——避免干扰 React 渲染树。
- DOM 分层:
div.dshwv-root(定位/翻转)→div.dshwv-body(按压 Q 弹缩放)→ 鲸鱼图 + SVG 气泡,三层职责清晰,动画互不干扰。 - 浮层层级自检:项目还配了零依赖脚本 tools/z-layer-audit.mjs,
node tools/z-layer-audit.mjs即可校验所有 body 级浮层是否登记进 z-index 候选表,防止「两个窗口互相盖住且谁都不报错」的静默失效——小项目也值得有这种自检工具。
6. 本地开发工作流:热重载怎么用最顺
按 whale-widget-prompt.md 总结的踩坑结论,DSH 插件联调的最快姿势:
- 安装:在仓库根目录(
package.json所在层,不要多套子目录)执行dsh plugin --profile web add link:.,然后重启dsh web。 - 改前端(
assets/whale-widget.js):宿主按文件 mtime 热读取,浏览器Ctrl+F5 硬刷新即生效,不用重启。 - 改宿主(
lib/index.js/lib/accounting.mjs):必须重启dsh web。 - 验证路由存活:带会话在浏览器里访问
/dsh-whale/balance.json应返回 200 JSON(含totalBalance);裸curl返回 401/403 是信任栅栏在工作的预期行为,不是接口坏了(详见 README.md 的「验证」一节)。 - 卸载:
dsh plugin --profile web remove dsh-whale-widget。
7. 总结:从这个小挂件能学到的 DSH 插件架构清单
📌 把这份清单存下来,下一个插件直接套用:
- 标准 bundle 插件=
package.json(含dsh.bundle.patch)+cordis.patch.yml挂载声明 + ESM 宿主入口,实现"随界面自动启用"。 - 路由统一入口:用一个
registerRoute包装所有路由,安全栅栏、错误兜底一次配齐;balance.json这类高频接口永远返回 200 + JSON,绝不悬挂。 - disposer 生命周期:所有注册物收集进数组、挂
ctx.effect清理,热重载零残留。 - 数据与逻辑分层:记账内核独立成纯函数模块,定点数运算,观测与校正分离。
- 前端自守:幂等守卫 + 环境自检,注入脚本只在它该工作的页面工作。
- 可迁移路径:资源相对包根目录取,用户数据落
$DSH_HOME,插件升级/迁移不掉数据。
想深入完整规格(DOM 结构、视觉几何参数、12 条踩坑结论、全部路由表),直接读项目自带的「完整生成提示词」whale-widget-prompt.md——它本身就是面向二次开发的架构文档,也是"把 README 写成 AI 可复现规格"的一个优秀范例。
【免费下载链接】DeepSeek-Balance-Whale-WidgetDeepSeek Harness(DSH)一只住在 DSH 界面右下角的小鲸鱼娘,帮你盯着DeepSeek账户余额。QQ弹弹,支持拖拽吸附、左吸附翻转、数字滚动动画,随界面自动启用,建议直接喊来你的dsh安装项目地址: https://gitcode.com/gh_mirrors/de/DeepSeek-Balance-Whale-Widget
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考