DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Sidebar Balance — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
S

@local/dsh-sidebar-balance

Sidebar Balance

在侧边栏底部显示 DeepSeek 和 Moonshot AI CN 的余额

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:WXJ-71/dsh-sidebar-balance#7fc50a933a6632be259696eaab23527c6075bdbe
README兼容性版本

兼容性与来源证明

Sidebar Balance 以 @local/dsh-sidebar-balance 发布,当前版本为 1.1.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/8

版本

1.1.1stable
2026/10/8
1.0.1stable
2026/10/4
1.0.0stable
2026/10/2

相关插件

正在加载相关插件…

最新版
1.1.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/8
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 models-usage 分类下经过校验的插件。

Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义Usage@linxin666/dsh-usage用于 dsh Web GUI 的使用统计插件:检测各提供商的余额和编程套餐配额,并实时记录令牌使用情况;侧边栏 Settings 行上方显示紧凑的价格概览卡片Client Ui Model Capabilities@linxin666/dsh-client-ui-model-capabilities为自定义 pi-ai 提供商声明每个模型的能力(图像输入和推理力度),可直接在 Models 设置卡片中编辑;写入官方 llm-pi-ai 设置命名空间,不更改 DSH 源代码。Plugin@opencode2dsh/dsh-plugin供 DeepSeek Harness (DSH) 使用的免费 OpenCode Zen 模型:原生 dsh-llm 适配器插件,无需 API 密钥。

README

dsh-sidebar-balance

给 DeepSeek Harness 的侧边栏底部加一条余额:DeepSeek 账号余额、Moonshot AI CN 余额、计费时段提示。 包名 @local/dsh-sidebar-balance,插件页显示为 侧边栏余额 / Sidebar Balance。

插件图标

  • 余额常在眼前 — 侧栏「设置」上方一条 余额 ¥12.34,点开是账户面板:充值余额与赠金余额、刷新、用量/充值入口,以及浏览器内的 DeepSeek 账号登录。
  • Moonshot 余额同栏可见 — 面板里另有一节 Moonshot AI CN:可用余额、现金余额、赠金余额,随每分钟轮询一起刷新;DeepSeek 无余额时小芯片自动回落显示 Moonshot 可用余额。余额由 Host 半边经凭据缝读取 MOONSHOTAI_CN_API_KEY(回落 MOONSHOT_API_KEY)后访问 api.moonshot.cn,密钥不出本机。
  • 计费时段一眼可辨 — 钱包图标红色 = 计费高峰,绿色 = 空闲优惠(UTC 01:00–04:00 与 06:00–10:00,周一至周五,中国法定节假日除外;节假日按空闲计)。

安装

在 Harness 网页的插件页 → 安装里粘贴这个地址:

https://github.com/WXJ-71/dsh-sidebar-balance

命令行等价写法:

dsh plugin --profile web add https://github.com/WXJ-71/dsh-sidebar-balance

装完在插件列表里启用 @local/dsh-sidebar-balance,刷新页面即可。完整步骤、依赖要求、 卸载与排查见 INSTALL.md。

适用环境

项目说明
适用系统 / 界面DeepSeek Harness 的 Web 界面(dsh web / dsh --profile web):手机浏览器、安卓 DSH 应用内嵌页面、桌面浏览器都适用。清单里以 dsh.client.platform: "web" 声明,不依赖任何桌面端组件。
Harness 版本^0.2.0-rc.2 —— 0.2.0(含 -rc 预发布)起、0.3.0 之前。已写进 package.json 的 peerDependencies:版本不符时插件页直接拒绝安装并提示版本不兼容,不会出现「装上了却不工作」。核验方式见 INSTALL.md。
已验证版本0.2.0-rc.2
依赖只用 Harness 自带能力:sidebar.footer.action 槽位、账户 Remote,以及随 dsh 一起发布的 @deepseek-ai/schemastery(仅用于声明 Config,不必额外安装)
账号需要 DeepSeek 开放平台账号:余额由 Harness 的 Host 读取,登录在浏览器里完成(授权回调落在本机回环地址)
网络插件的浏览器半边不直连任何平台;只有 Host 读余额(DeepSeek 经账户 Remote、Moonshot 经 api.moonshot.cn)、以及你点「登录」时才访问外网

许可

MIT。


以下为技术说明(English)。

@local/dsh-sidebar-balance

Display name in the plugin page: 侧边栏余额 / Sidebar Balance (from locale/zh.json and locale/en.json; without them the card falls back to the raw package name).

Shows the DeepSeek account balance at the foot of the Harness sidebar, above the Settings row, and signs the Harness into a DeepSeek Platform account from the browser. It reads the built-in account Remote (account.getState, account.getBalance, account.startSignIn, account.cancelSignIn), so it never talks to the Platform itself and needs no configuration.

Why this plugin exists

The shipped account UI (@deepseek-ai/dsh-client-ui-settings-account) registers only in the Desktop renderer — its apply() starts with if (!("dshDesktop" in globalThis)) return. In a browser profile there is therefore no account page and no way to sign in, even though the Host half (dsh-deepseek-account-platform) supports a loginSource: 'web' PKCE flow whose callback lands on the loopback Web server. This plugin adds the missing entry point.

Where it renders

One entry in the sidebar.footer.action slot (id: account-balance):

  • signed in — wallet glyph (see WalletIcon: a 1.25:1 billfold with a snap clasp, drawn from the owner's two reference icons), 余额/Balance, and the topped-up amount (¥12.34, or <0.01 for a positive sub-cent balance); clicking opens a panel with the topped-up and granted wallets, a refresh action, and the account's Usage / Top-up links.

  • signed out — wallet glyph with 未登录/Not signed in; the panel offers 登录/Sign in.

  • signing in — the state reads 等待登录完成…/Waiting for sign-in… in the brand color while an attempt is in flight, with 打开授权页 and 取消 actions.

  • read failed — the state turns into the error color and the panel carries the Remote error text; polling keeps retrying.

  • collapsed sidebar — a 36px circular icon-only button, like the Cordis panel beside it.

  • billing indicator — the wallet glyph itself is tinted by the window that is open right now: red for peak, green for off-peak, in both themes and in the icon-only rail chip as well. The chip's tooltip names the window (· 计费高峰时段 / · 空闲优惠时段).

  • Moonshot AI CN — the panel carries a second section with the Moonshot open-platform wallet: available balance plus the cash/voucher breakdown, refreshed on the same 60-second poll, and the chip falls back to it when there is no DeepSeek wallet. The page never sees the key — the Host half reads MOONSHOTAI_CN_API_KEY (falling back to MOONSHOT_API_KEY) through the credential seam and calls api.moonshot.cn itself.

State refreshes on mount, every 60 seconds, and whenever the tab becomes visible. While a sign-in attempt is pending the cadence is 2 seconds, so the chip flips to the balance by itself once the browser finishes.

The billing indicator

DeepSeek bills the standard rate in two UTC windows, Monday to Friday, and the discount rate in every other hour — weekends and Chinese public holidays in full (pricing):

RateWhen (UTC)
peak (wallet red)01:00–04:00 and 06:00–10:00, Mon–Fri, except CN holidays
off-peak (wallet green)every other hour, all weekend, and every CN holiday

peakAt(date) in client.js is a pure function of the instant, so the tint is right whenever the page re-renders, and useOffPeak only exists to re-render at the edges: one timer re-arms for the next 00:00/01:00/04:00/06:00/10:00 UTC boundary, and a visibilitychange listener re-reads the clock because phones freeze timers while the tab is backgrounded. The window is evaluated in UTC, so the device's own timezone never changes the tint.

Holidays come from the State Council's yearly arrangement, listed per year in CN_HOLIDAYS as MM-DD (2026 is 国办发明电〔2025〕7号). The table is the one part that goes stale: a year that is missing falls back to the weekday rule, which claims peak on a holiday rather than promising a discount that is not there. Add the next year's dates from https://www.gov.cn/zhengce/zhengceku/ when they are published each November. Shifting a weekend to a workday does not matter: DeepSeek bills weekends off-peak in full, 调休 or not.

prefers-reduced-motion has nothing to switch off here: the tint is static, so the window reads the same either way.

The tint is the theme's own state token, painted through currentColor: --dsw-alias-state-error-primary for peak and --dsw-alias-state-success-primary for off-peak. Every stroke of the glyph carries it, so the wallet reads as one colour rather than a tinted detail. Reusing the tokens keeps it legible in light and dark without a colour of its own, and the icon-only rail chip takes the same tint — there it is the only signal the chip has.

Two rules set it, and they sit after .dsh-sidebar-balance_icon in the sheet so they win on the element they share with it. The wallet's blank left panel, which used to hold the billing dots, stays blank.

Moonshot balance

The panel's Moonshot AI CN section rides the same poll as the DeepSeek read. The page never sees the key: the Host half registers one same-origin route, /dsh-sidebar-balance/moonshot.json, which resolves MOONSHOTAI_CN_API_KEY (then MOONSHOT_API_KEY) through ctx.credentials.resolve per read — the credential seam's rule, so a rotated key reaches the next poll without a restart — and calls GET {baseUrl}/users/me/balance. The route answers the available balance plus the cash/voucher breakdown, cached for 55 seconds so a burst of polls shares one upstream read; ?refresh=1 bypasses the cache (the panel's refresh button).

Every request passes through Connection's requestRejection, the same browser-session trust fence the built-in JSON routes use, so only a page this Harness would serve can read it. The four statuses the page renders are ready (amounts), no-credential (a hint naming the missing ref), failed (the upstream or network error text, trimmed to 200 chars), and disabled (the Config turned the section off).

Surviving a flaky resolver

Measured on a machine whose DNS intermittently stalls: a cold dns.lookup took 7.3 s while the next one answered in 10 ms, and undici fails the request at its own ~10 s connect timeout — before this plugin's fetchTimeoutMs can matter. Two behaviours follow from that, and both are deliberately asymmetric with the happy path:

  • A transport failure gets exactly one retry, 500 ms later, and only when the cause is one a second attempt can fix (UND_ERR_CONNECT_TIMEOUT, ETIMEDOUT, ECONNRESET, EAI_AGAIN, …). A warm resolver answers that retry instantly, so the read succeeds where a single attempt would have reported an outage. Deterministic causes (ENOTFOUND, ECONNREFUSED), HTTP errors, and this plugin's own timeout abort are not retried — a second round trip would only delay an honest failure.
  • Failures are cached for 5 seconds, not cacheTtlMs. Replaying a blip for the rest of the read interval is exactly when someone is staring at the panel wondering why it still says unavailable; with the short failure TTL the next poll shows the recovery instead.

The error text also carries the syscall-level cause (fetch failed / UND_ERR_CONNECT_TIMEOUT), because undici reports every transport failure as the bare string fetch failed and hides the real one in cause.cause — without it an outage is indistinguishable from a bad address.

With no DeepSeek wallet the chip falls back to the Moonshot available balance. That fallback keeps the wallet glyph neutral: the red/green tint is DeepSeek's billing window and is dropped — from the icon and from the tooltip — whenever the chip is speaking for Moonshot. The section carries its own 更新于 stamp, because the two providers refresh on independent reads and one shared stamp would claim a freshness the Moonshot number may not have.

Failures and keyboard

Every failure carries a code, and the panel renders the code's localized text rather than raw provider English. The codes are the Host route's (timeout, payload, http, network, unknown), this half's own (host-route, wallet), and the account Remote's documented ones (expired, storage, protocol). A code with no dictionary entry still prints whatever text arrived — an unmapped message is worth more to a reader than a generic replacement — and where a localized line and a provider message both exist, the provider's words follow a · separator and stay in the element's title, so a report stays diagnosable.

The panel follows the host's modal-layer conventions (its useModalLayer, whose focusable selector this plugin copies rather than redefining):

  • opening moves focus into the panel once it has been measured — the panel renders hidden until its position is known, and a hidden element cannot take focus;
  • Tab wraps inside the panel instead of walking the page behind it;
  • Escape closes only while this panel owns focus, so a host modal opened on top keeps its own Escape;
  • closing hands focus back to whatever opened the panel, unless the close came from clicking something else — that click owns focus then.

It deliberately declares aria-modal="true" without a scrim: interaction and keyboard ownership match a modal, but the popover still dismisses on a click anywhere else and never dims the app.

Config

Both halves are driven by one row config, written in your profile's cordis.patch.yml. Defaults suit the common case, so the row needs no config at all:

- id: sidebar-balance
  name: '@local/dsh-sidebar-balance'
  config:
    keyRefs: ['MOONSHOTAI_CN_API_KEY', 'MOONSHOT_API_KEY']
    baseUrl: https://api.moonshot.cn/v1
    cacheTtlMs: 55000
    fetchTimeoutMs: 20000
    pollIntervalMs: 60000
    showMoonshot: true
FieldDefaultEffect
keyRefsboth Moonshot refsCredential references tried in order; the first configured one wins
baseUrlhttps://api.moonshot.cn/v1API root; the wallet endpoint is ${baseUrl}/users/me/balance
cacheTtlMs55000How long one upstream read is served to every poll
fetchTimeoutMs20000Hard ceiling on one upstream read
pollIntervalMs60000Cadence the page polls both providers at
showMoonshottrueOff hides the section; the route then answers disabled without touching credentials or the network

The page cannot read a Host plugin's Config, so the route echoes the two client-relevant fields (pollIntervalMs, showMoonshot) on every answer and the panel adopts them — which is why changing the cadence or hiding the section takes effect on the next poll instead of needing a reload.

The sign-in flow

  1. The panel's 登录/Sign in action calls account.startSignIn(client, window.location.origin, 'web').
  2. The Host runs the PKCE attempt, registers the browser callback route /oauth/callback on its Web server, and returns the attempt with an authorizeUrl; the plugin opens it in a new tab.
  3. Platform approval redirects the tab to http://127.0.0.1:<port>/oauth/callback?code=…&state=…, which the Host exchanges and stores as the deepseek-account-platform/default grant.
  4. The next 2-second poll sees credential-stored, reads the wallet, and the chip shows the amount.

window.open runs in the same turn as the click, so it is not treated as a popup; if the browser blocks it anyway the panel says so and offers the 打开授权页 button instead. Cancelling calls account.cancelSignIn(attempt.id); if the panel is closed mid-attempt the attempt stays alive on the Host and the next poll adopts its id again.

Files

  • package.json — bundle manifest: dsh.bundle.patch, the display icon, the locale/*.json and package.json exports the metadata reader resolves, plus the Client declaration (platform: web, immediately, and the load-order inject).
  • cordis.patch.yml — inserts the single Host row for this package.
  • locale/en.json, locale/zh.json — the meta.title / meta.description shown on the plugin card and component rows.
  • icon.png — the plugin-card artwork: a 241×256 RGBA PNG (90.9 KiB) cut down from a 1155×1229 source that already carries a transparent background. Only the plugin list, bundle list, and inventory rows read it; the sidebar chip keeps its inline wallet glyph in client.js.
  • index.js — Host half: one authenticated same-origin route, /dsh-sidebar-balance/moonshot.json, resolving the Moonshot credential through ctx.credentials and reading the wallet from api.moonshot.cn with a short cache, so the key never leaves the Host. It also declares the plugin's Config.
  • client.js — the browser half: the sidebar.footer.action registration, the chip and account panel, the Moonshot section, the sign-in flow, inline styles, the inline WalletIcon / RefreshIcon glyphs, the peakAt / useOffPeak billing-window helpers, and the sidebar.balance dictionaries (en/zh).

Maintenance

clientMetadata() in client.js sends x-client-version to the Platform. It pins the Harness version this bundle was authored against (0.2.0-rc.2); bump it after upgrading the Harness.

package.json declares peerDependencies: { "@deepseek-ai/dsh": "^0.2.0-rc.2" }, which is what the manager's compatibility preflight reads: outside that range the plugin is refused at install time instead of failing silently later. The matching peerDependenciesMeta entry marks it optional so a package manager never tries to fetch a whole dsh runtime for it.

Do not judge that range by default semver semantics. dsh-app-boot evaluates it as semver.satisfies(runtime, range, { includePrerelease: true }), and includePrerelease is what lets one caret range cover the whole 0.2.x line including its prereleases:

runtime^0.2.0-rc.2 with includePrereleasewithout
0.2.0-rc.2coveredcovered
0.2.1-alpha.1coverednot covered
0.2.9coveredcovered
0.3.0-rc.1not coverednot covered

So the range already spans 0.2.0-rc.2 through the end of the 0.2 line; the next real decision point is 0.3.0, not the next 0.2.x prerelease. The test-sidebar-balance-host.mjs peer-range block re-checks this table against the installed runtime so the reasoning above cannot silently rot.

A link: install into a profile keeps this directory live: editing client.js is picked up by Client HMR when the web dev watcher is running, and by a page refresh otherwise. Changes to cordis.patch.yml rows need a bundle install (or a Harness restart) to take effect. See INSTALL.md for the install itself.

The card icon is the exception, and it is not the case the note below covers: readPluginMeta resolves ./package.json once and then re-reads that file with readFileSync on every call, inlining the icon bytes as a data: URL. So replacing icon.png — or pointing icon at another file that already sits inside the manifest directory — lands on the next plugin-list fetch, which the page performs on mount. Adding the icon field, a new exports entry, or locale/*.json for the first time is different: Node caches the package's resolved exports map for the life of the process, so the running Harness keeps reading the previous map — and therefore keeps showing the package-name and default-artwork fallbacks — until dsh web is restarted. Verify new text or a new icon address without waiting by calling readPluginMeta in a fresh process:

node --input-type=module -e "import { readPluginMeta } from '@deepseek-ai/dsh-app-boot'; \
  console.log(JSON.stringify(readPluginMeta('@local/dsh-sidebar-balance', 'file://' + process.env.DSH_PROFILE_DIR + '/'), null, 2))"

icon.png was cut from a 1155×1229 RGBA source with a transparent background: fit to a 256px box with Lanczos, landing at 241×256 and about 89 KiB. The manifest cap is 256 KiB, so a 512px render is refused and a 384px one only just fits. Nothing else in this package depends on that source.

Diagnosing a failed sign-in

The panel prints the Remote error verbatim. Useful mappings:

  • account start failed / protocol — startSignIn rejected the callback origin. The Host accepts only http://localhost|127.0.0.1|[::1]:<port> with no path, and the authorize URL must be <platformOrigin>/dsh/authorize.
  • expired — the attempt outlived attemptTimeoutMs (10 minutes by default) or the Browser was closed before consent.
  • network — the Host could not reach https://platform.deepseek.com.
  • storage — a stored grant exists but does not parse; signOut (or deleting the deepseek-account-platform/default record in $DSH_HOME/.credentials.yaml) clears it.