dsh-deepseek-balance-badge
DSH 插件:在 Web GUI 侧边栏底部常驻一个 DeepSeek 账户余额徽标,点击展开明细浮层,内含手动刷新按钮,并按固定周期自动刷新;刷新周期与宿主缓存窗口可在 设置 → 插件 → 插件配置 里改。

侧边栏底部 [◈ ¥354.64] ← 展开态
[◈] ← 收起态(56px 轨道)
点击后浮层:
DeepSeek 余额 账户可用
总余额 ¥354.64
赠送余额 0.00
充值余额 354.64
─────────────────────────────────
更新于 2 分钟前 [刷新]
组成
| 半边 | 位置 | 职责 |
|---|---|---|
| 宿主 | src/host/index.js |
从 ctx.credentials 解析 API Key,请求 api.deepseek.com/user/balance,在 GET /balance/api/summary 上返回归一化 JSON;经 ctx.settings 注册本插件的设置 namespace |
| 客户端 | src/client/client.js |
注册 sidebar.footer.action 席位(徽标与浮层)与 settings.plugin.item 卡片(轮询周期 / 缓存窗口),轮询宿主路由 |
API Key 永不进入浏览器:浏览器只访问同源宿主路由,密钥留在宿主进程。这也是必需的——api.deepseek.com 不返回 CORS 头,浏览器直连本来就会失败。
安装
从 npm 安装(推荐,预构建、无需本地构建授权):
dsh plugin --profile web add dsh-deepseek-balance-badge
或直接从 GitHub 安装源码:
dsh plugin --profile web add github:<your-github-user>/dsh-deepseek-balance-badge
装完刷新页面即可(客户端半走 HMR);宿主半在首次安装后需要重启一次应用。
本地开发安装
开发时把工作区以 file: 依赖装进 desktop profile:
# 1. profile 清单:dependencies 与 dsh.profile.bundles 两处都要写
# C:\Users\User\.dsh\profiles\desktop\package.json
# "dependencies": { "dsh-deepseek-balance-badge": "file:D:/Projects/DeepSeekHarness插件/dsh-deepseek-balance" }
# "dsh.profile.bundles": [ ..., "dsh-deepseek-balance-badge" ]
# 2. 安装
pnpm install --dir "C:\Users\User\.dsh\profiles\desktop" --no-frozen-lockfile
# 3. 校验解析(不重启就能发现清单错误)
node scripts/verify-install.mjs
# 4. 完全退出并重启 DSH Desktop —— 宿主半不会热加载
改了代码之后
file: 依赖是拷贝而非软链,且 pnpm 按版本号缓存。改了源码后必须强制重装,否则运行的是旧副本:
node scripts/pack.mjs # 源码 → client/client.js(客户端半的唯一构建步骤)
node scripts/resync.mjs # 删除已安装副本 + 重装 + 校验
客户端 bundle 改动后,删除副本重装即会触发 dsh-client-hmr 的 rev 变更,浏览器端会自行重载;宿主半改动始终需要重启应用。
卸载
从 profile 的 dependencies 与 dsh.profile.bundles 删除 dsh-deepseek-balance-badge,执行一次 pnpm install,重启应用即可。
配置
配置有两层,用户层在上:
| 层 | 位置 | 谁能改 |
|---|---|---|
| 用户层 | ~/.dsh/settings.yaml 的 dsh-deepseek-balance: 分节 |
设置 → 插件 → 插件配置 里的卡片,或直接编辑该文件(改完即生效) |
| 组合层(base) | profile 的 cordis.patch.yml 里本插件条目的 config: |
部署者 |
没有用户覆盖时,值来自组合层,缺失字段回落到 schema 默认值;卡片里留空某个字段就是删除用户覆盖,该字段重新继承组合层。两层都写了同一个键时,用户层生效——所以改了 patch 里的值却不生效时,先去卡片里看看是不是覆盖过。
# profile 的 cordis.patch.yml(组合层)
- id: dsh-deepseek-balance
config:
pollSeconds: 300 # 自动刷新周期(秒),钳制到 [30, 86400],默认 300
apiKeyRef: DEEPSEEK_API_KEY
baseUrl: https://api.deepseek.com
cacheTtlSeconds: 10 # 宿主结果缓存窗口,手动刷新绕过它
卡片只暴露 pollSeconds 与 cacheTtlSeconds 两个数值;apiKeyRef、baseUrl 属于部署级配置,仍走组合层。改完立即生效:宿主监听 settings 提交后重新解析,客户端下一次轮询就采用新的周期,不需要重启应用。部署没有 settings 提供方(或存储的分节被 schema 拒绝)时,插件回落到组合层继续工作,卡片显示为「未就绪 / 只读」。
行为细节
- 徽标左对齐:
sidebar.footer.action席位是align-items: center的纵向 flex,插件自带一小段样式表:外层容器display: flex; width: 100%先把整行占住,触发器再width: 100%+justify-content: flex-start,并用margin-inline: -2px吃掉页脚 house rule 的 4px bleed,于是与市场/Cordis 启动器和下方设置行共用同一条左边线。触发器宽度刻意用100%而不是 house rule 的calc(100% + 4px):桌面外壳会自行加宽并重新居中这个席位,左对齐不该取决于百分比在一个被重新居中的盒子里怎么解算。水平内边距取设置行自己的0 10px 0 8px,因为 Button 原语统一的0 10px会让图标比设置齿轮右偏 4px。收起态轨道仍还原成居中的 36px 圆形图标按钮。 - 打开即取:面板挂载时立刻拉取一次;此后按
pollSeconds轮询。 - 设置卡片:在 设置 → 插件 → 插件配置 里编辑
pollSeconds与cacheTtlSeconds。卡片自身不画外壳——表头、未保存标记、保存/放弃按钮与只读提示都由设置外壳渲染,插件只提供两个输入框与写入逻辑。写入走设置 seam 的 revision 栅:文档在别处被改过就拒绝本次写入并重新载入,而不是覆盖别人的修改;空输入框表示删除该字段的用户覆盖。 - 手动刷新:
?force=1跳过宿主缓存;请求期间按钮禁用并旋转,且不会被同刻的在途轮询"顶包"。 - 后台省电:页面不可见时跳过轮询;回到前台且数据已过期则立即补拉。
- 单飞:宿主的并发轮询共享一次上游调用,避免刷新风暴。
- 失败不清空:上游出错时保留上次成功数据,浮层内联显示错误码与原因。
- 多币种:逐行展示,徽标显示
balance_currency对应币种。
错误码
| 错误码 | 含义 |
|---|---|
credential-missing |
未找到 DEEPSEEK_API_KEY |
credential-rejected |
上游 401/403,密钥无效或被吊销 |
credential-error |
凭据服务读取失败 |
rate-limited |
上游 429 |
upstream-timeout |
上游请求超时(10s) |
upstream-unreachable |
无法连接上游 |
upstream-error |
上游返回其它错误状态 |
upstream-malformed |
上游响应无法解析,或没有可用余额条目 |
internal-error |
宿主内部错误 |
开发
node --test "tests/*.test.mjs" # 44 个用例:宿主路由/错误映射/缓存/单飞/设置分节 + 客户端 bundle 契约、样式表、设置卡片与真实 React 渲染
node scripts/pack.mjs # 生成 client/client.js
node scripts/pack.mjs --check # 校验产物与源码一致
node scripts/publish-check.mjs # 发布前置检查:manifest、dsh.bundle、repository、tarball 内容
node scripts/settings-check.mjs # 用真实的 dsh-settings-file provider 验证「卡片写入 → 落盘 → 即时生效」
node scripts/live-check.mjs # 真实凭据 + 真实上游(需已配置 Key 且联网)
node scripts/verify-install.mjs # 已安装副本能否被 Loader 正确解析
node scripts/resync.mjs # 强制把当前源码同步进 profile
node_modules/@deepseek-ai/* 与 node_modules/react 是指向 DSH 安装的 junction,仅供本地跑测试;它们已从 package.json 的 files 白名单排除,不会进入安装副本。运行时这些包由 DSH 安装锚点解析(与所有 @deepseek-ai/* 消费者相同)。
发布与收录
本包只通过 awesome-dsh-plugin 精选列表分发:dsh-market、DSH Desktop 内置社区市场、dshget、1024store、dshfind 等商店的目录都源自该列表,且内置市场只允许安装列表内的来源。想让插件上架,只需要向那个仓库提一个 PR。
# 0. 把 package.json 里的 author / repository / homepage / bugs 换成你自己的 GitHub 账号
# 仓库名默认是 dsh-deepseek-balance-badge;若你换了仓库名,一并替换:
# (Get-Content package.json -Raw).Replace('REPLACE_WITH_YOUR_GITHUB_USERNAME','<owner>') |
# Set-Content package.json -Encoding utf8 -NoNewline
node scripts/publish-check.mjs # 占位符没换掉会直接 FAIL
# 1. 建仓并推送(仓库名需与 package.json 的 repository 一致)
git init; git add -A; git commit -m "feat: DeepSeek balance badge for the DSH sidebar"
git remote add origin https://github.com/<owner>/dsh-deepseek-balance-badge.git
git push -u origin main
# 再给仓库加上 `dsh-plugin` topic,并确认仓库创建满 1 天(列表 CI 会校验)
# 2. 发布到 npm(可选,但预构建安装体验更好;package 的 repository 字段负责与列表条目关联)
npm publish
# 3. 向列表投稿:只加一个文件,不要改 README
# data/plugins/<owner>__dsh-deepseek-balance-badge.yml
# url: https://github.com/<owner>/dsh-deepseek-balance-badge
# name: <owner>/dsh-deepseek-balance-badge
# category: usage
# description:
# en: DeepSeek account balance in the DSH sidebar footer, ...
# zh: 在 DSH 侧边栏底部显示 DeepSeek 账户余额,...
商店截图由仓库自带的 screenshots.json 声明(相对路径,1–8 张),这样以后换图只需推自己的仓库。
已知限制
- 客户端 bundle 手写、无打包器:浏览器 seed 表只有
react、react/jsx-runtime、react-dom、@deepseek-ai/cordis、dsh-client-store、dsh-client-ui-slots、dsh-client-ui-primitives、dsh-client-ui-dockkit。因此源码只用React.createElement(不用 JSX),也不 importdsh-client-ui-settings等非 seed 包——设置卡片是通过ctx.get('settingsScope')这个运行时服务拿到的,不产生模块依赖。测试中的require白名单会挡住违规。 - UI 测试不自建 DOM:
react-dom不是本包依赖,渲染用例用真实react加手写 dispatcher 展开组件树,覆盖组件逻辑但不覆盖真实布局与样式。 - 新增宿主插件必须重启应用:DSH 不热加载宿主半。
- 客户端 seed 包不作为 peerDependency 声明:
@deepseek-ai/dsh-client-ui-primitives与dsh-client-ui-settings由浏览器 seed 表提供(dsh.client.inject里已声明),不是 Node 解析的依赖。声明成 peer 反而会在 harness 换预发布版本时给用户制造 ERESOLVE,而那些版本本来就是 seed 提供的。宿主半边真正 import 的@deepseek-ai/schemastery是唯一的 peerDependency。
No comments yet. Be the first to write one.