DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

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

@dsh/dsh-balance

Balance

DeepSeek Harness 插件:识别当前路由背后的提供商,并查询其 API 余额或订阅配额。不使用子进程,不具备执行权限,仅使用官方端点。

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

npx -y @deepseek-ai/dsh plugin --profile web add github:zhujiuyi/dsh-balance-meter#bb6edb838cdff6d01d93a969f1e408559d60446f
README兼容性版本

兼容性与来源证明

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

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

版本

0.1.0stable
2026/10/14

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/15
查看源码 ↗
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 源代码。Cost Meterdsh-cost-meterDeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 170+ 模型 ID 价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(13 家,含 OpenDesign 工作区查询、含小米 MiMo Token Plan 控制台 Cookie 查询、SCNet Token Plan 本地 Credits 计量与火山方舟 Volcano Ark Coding Plan AK/SK 签名查询)、自定义 Provider

README

中文 | English

dsh-balance-meter

A native DeepSeek Harness (DSH) plugin: read the provider behind your current model route, then query its API balance or subscription quota.

A readout sits in the session header, with a popover listing every route. On the model side a balance_check tool lets the agent answer "how much credit is left?" on demand.

This is the DSH counterpart of ccgui-plugin-balance-meter. Because DSH exposes natively what that host lacked, this version requests no exec capability, spawns no subprocess, and probes no relay stations:

CC GUI versionThis plugin
Credentialsparses each engine's config file, needs exec:curlctx.credentials, no shell
Networkhost egress required a declared domain, so exec:curlplain fetch in the host process
Route identitysniffs gateway hostnamesctx.llm.listProviders() + profile config

Install

The npm package is @dsh/dsh-balance. Install it from the release tarball:

dsh plugin --profile desktop add https://github.com/zhujiuyi/dsh-balance-meter/releases/latest/download/dsh-dsh-balance-0.1.0.tgz

Or straight from the repository:

dsh plugin --profile desktop add github:zhujiuyi/dsh-balance-meter

Do not run dsh plugin add dsh-balance-meter. That unscoped name belongs to an unrelated package on npm, and the command would install that one instead.

Restart DSH. A @dsh/dsh-balance card appears on the Plugins page and the readout appears on the composer status row.

After editing the source, run the same command again to overwrite it (same package name) — no uninstall first.

Uninstall:

dsh plugin --profile desktop remove @dsh/dsh-balance

What it shows

The status row under the composer — the same row as the built-in 1 turn 6 steps · 165 tok/s pills, to their left. A balance route shows money (¥12.34); a subscription shows remaining quota (每月 剩 47%). Click for a popover with:

  • the vendor, billing mode, credential variable, endpoint, and when it was last read
  • for subscriptions, every quota window (rolling / weekly / monthly) with a remaining-share bar and its reset time
  • a Refresh now button that bypasses the failure cooldown

It reports only the provider the current session actually uses. The route comes from that session's model selection (current.provider in modelDirectories), not from every adapter registered in the build — listProviders() reports built-ins like deepseek-official and deepseek-account that the user never configured, and listing those is noise.

Readings can be stale. When an automatic query fails but a previous one succeeded, the readout keeps the older figure, labels it stale, and the popover states the failure. A stale number is never presented as current.

Catalogued providers

Official domains only; no request ever goes anywhere else.

ProviderEndpointBilling
DeepSeekGET /user/balanceAPI balance
OpenRouterGET /api/v1/keyAPI balance
Moonshot / KimiGET /v1/users/me/balanceAPI balance
SiliconFlowGET /v1/user/infoAPI balance
智谱 GLM / Z.AIGET /api/monitor/usage/quota/limitSubscription windows
Kimi CodingGET /coding/v1/usagesSubscription windows
MiniMaxGET /v1/token_plan/remainsSubscription
OpenCode GoGET /zen/go/v1/usageSubscription windows

The credential variable is resolved per route: an adapter-declared apiKeyEnv first, then the matching llm-* row in the profile's cordis.patch.yml, then the catalog default (e.g. DEEPSEEK_API_KEY).

The four ways a query can miss

The core promise is never to guess a number. Every non-success lands in exactly one of these, each saying what to do next:

StatusMeaning
no-credentialthe credential variable exists but is unset — names the variable to configure
unknown-providerthis plugin has not catalogued the route and the preset chain below missed too — which is not the same as the vendor having no endpoint
no-public-endpointcatalogued, and that vendor definitively has no public endpoint
parse-failedHTTP 200, but the response shape is unrecognised — echoes a raw response fragment

Preset endpoint probing

A route outside the built-in catalogue is not given up on. A fixed sequence of conventional relay-panel endpoints is tried, and the first that yields a readable figure wins:

#EndpointWhere it comes from
1GET /api/usage/token/New-API ≥ v0.9.0 — remaining quota in one call
2GET /v1/dashboard/billing/subscriptionOpenAI-style billing, in cents
3GET /v1/usageSub2API family
4GET /api/v1/me/quotaclaude-code-hub, which uses an X-Api-Key header
5GET /dashboard/billing/credit_grantsCloseAI family
6GET /api/v1/credits—
7GET /api/user/selfNew-API variants

Presets 1 and 7 first make one unauthenticated GET /api/status read to pick up the panel's own quota_per_unit — a New-API operator can change the accounting unit, and hardcoding 500000 would be off by orders of magnitude.

An endpoint you set outranks all of them: the value written by balance_set_endpoint or routes[].endpoint is tried first, because that is the thing you actually know. If none of the presets fit, setting one is the fix.

A 401 or 403 stops the chain — every remaining path would use the same credential.

Scope of the claim: the mechanism (order, short-circuit, calibration, fallback) is tested and every extraction path has a fixture, but the preset shapes themselves are transcribed from the reference project's panel catalogue and could not be re-verified per vendor. A preset that mismatches therefore reports parse-failed and echoes its response, rather than returning a wrong number.

Id aliases in the catalogue

Adapters name their own routes deepseek-official, deepseek-account and the like. Those resolve to the vendor's entry once the suffix is stripped, instead of being called uncatalogued — the vendor is plainly known. A genuinely unrelated id (say deepseek-proxy) is still reported as uncatalogued.

Echoing the body on parse-failed is deliberate: the eight response shapes were compiled from vendor documentation, and the author holds no keys for these vendors, so none could be verified against a live API during development. The echo is how you see what actually came back.

Echoed bodies are redacted (sk-…, Bearer …, { "api_key": … } and similar become ***) and truncated to 800 characters.

Permissions and privacy

  • No exec capability, no subprocess. No shell, no curl, no process spawning.
  • Requests go only to the gateway of the route you configured in DSH. No third-party domain, no telemetry, no reporting back. A catalogued route is queried at its vendor's official endpoint; an uncatalogued one only ever at its own base URL (see Preset endpoint probing).
  • A key only ever goes to its own gateway. Read through ctx.credentials.resolve(), placed in a request header, never written to disk, logs, or the browser snapshot. The snapshot carries the credential variable name and no value — there is a test asserting this.
  • No other DSH configuration is touched. balance_set_endpoint edits only this plugin's own row config.

Configuration

Plugins page → the @dsh/dsh-balance card → the form edits the primary route, the auto-refresh switch, and the minimum interval.

Route overrides (custom endpoints, different credential variables) go through the tool, or by editing the balance-meter row in the profile's cordis.patch.yml:

- id: balance-meter
  name: "@dsh/dsh-balance"
  config:
    primary: deepseek
    routes:
      - id: my-provider
        baseURL: https://api.example.com/v1
        apiKeyEnv: MY_PROVIDER_API_KEY
        endpoint: '{origin}/v1/balance'

Endpoint templates accept {origin} (the route's scheme and host) and {base} (the route's base URL). Prefer {origin}: it stays correct no matter which path segment the configured base URL carries.

Tools

balance_check — { route?, refresh? }, returning structured JSON: route, vendor, billing mode, status, figure or quota windows, endpoint, query time. With no route it queries the route the header shows.

balance_set_endpoint — { route, endpoint }, persists a custom endpoint into this profile's cordis.patch.yml, applied immediately. An empty endpoint clears the override.

Writing routes sends the plugin row through the Loader's normal lifecycle (effectively a reload), so the in-memory cache resets and the readout re-queries on its next poll. That is a schemastery structural limit rather than a choice: array elements are always validated as "blocked", so a volatile field can never appear inside an array — and since route ids are user-chosen, no schema shape would make route overrides volatile (and volatile is what buys an in-place update without a reload).

How it works

DSH host process                               browser
┌────────────────────────────────────┐       ┌────────────────────┐
│ @dsh/dsh-balance (host half)      │       │ @dsh/dsh-balance  │
│                                    │       │ (browser half)     │
│ ctx.llm.listProviders()  ─┐        │       │                    │
│ llm-* rows via             ├→ routes│       │  header readout    │
│   configEditor            ─┤        │       │    ↓ fetch         │
│ built-in catalog          ─┘        │       │  /api/balance-     │
│                        ↓           │       │   meter/snapshot   │
│ ctx.credentials.resolve()          │       │    ↑               │
│                        ↓           │       │    │               │
│ fetch(vendor) → parse → cache      │──────→│    │               │
│                        ↓           │ JSON  │    │               │
│ ctx.tools.register ×2              │       │  config form       │
└────────────────────────────────────┘       └────────────────────┘

Refresh policy. The host holds no timer, and nothing guesses when a turn ended.

  • After every turn, the host listens for the session log's turn/end event and refreshes the current route. That event name is not guessed — it was read out of a real session.v4.jsonl.zstd ({"type":"turn/end","data":{"turn":1,"reason":{…}}}).
  • The browser half reads a snapshot every 10s, and that is a local cache read that does not touch the vendor. It queries exactly once in one case: when the route has never been read — on plugin load, or right after you switch models. Otherwise a new figure arrives from turn/end or your own refresh.
  • Vendor requests are throttled by minIntervalSec (default 30s) and the failure cooldown, so a burst of turns cannot become a burst of vendor requests. A hidden page does not poll.
  • A failed query never blanks the readout. The host keeps the last successful figure and marks the snapshot stale, so the readout keeps showing it labelled stale with the reason in the detail panel. Only a route that has never succeeded reads as Unavailable.

Caching. The endpoint that worked is cached per route; after every candidate fails the route enters a 10-minute cooldown; concurrent queries for one route coalesce into a single in-flight request.

Known differences from the reference implementation

  • Different surface. The reference sits in the status bar; this one sits on the status row under the composer, which in DSH's slot system is conversation.composer.dock — the row the built-in session-stats pills use.
  • Which window the readout shows: shortest span first. 5-hour → weekly → monthly, taking the first that reports a figure. That order is the order in which each window can block you, and a subscription is managed against its nearest cycle. It is deliberately not the window with the least remaining, which would sit on the monthly figure permanently. Only when no window's span is recognisable does it fall back to least-remaining.
  • An uncatalogued route is stated, not papered over. When the preset chain misses too, the readout says Unavailable, explains that this plugin has not catalogued it, and carries the last raw response rather than inventing a figure.

Which response shapes are verified

The eight parsers were written from vendor documentation, and the author holds no keys for these vendors — only OpenCode Go has been checked against a live response, which is what fixed it:

{"usage":{"rolling":{"status":"ok","percent":0,"resetsAt":"…"},
          "weekly":{"status":"ok","percent":8,"resetsAt":"…"},
          "monthly":{"status":"ok","percent":53,"resetsAt":"…"}}}

percent here is a used share, not a remaining one. That is inferable rather than assumed: a live account read rolling: 0 while actively completing turns, which cannot mean "0% remaining". The remaining figure is derived from it, and the raw value is kept in the window note (原始 percent=53) so a change of convention at the vendor would be visible immediately.

If any of the other seven disagrees with its documentation, it reports parse-failed and echoes the response — send that echo and it can be corrected.

Development

npm run verify   # static contract check + the full test suite
npm test         # 209 tests, no DSH required
npm run check    # static contract check only

Tests come in three layers, all runnable without DSH:

  • Pure logic — catalog matching and id aliases, the eight response parsers, the seven-preset probe chain, cache/throttle/cooldown/coalescing, route discovery — with an injected clock and fetch, so results are deterministic.
  • Host-half integration — stubs stand in for the @deepseek-ai/* packages and the real apply() runs, covering tool registration, credential resolution, the snapshot route, and config writes.
  • Static contract — package.json, cordis.patch.yml and the browser half checked for self-consistency: every bare import declared, the bundle patch naming this package, the module id equal to the package name, and files shipping both entry points.
  • Schema guard (test/config-schema.test.mjs) — validates Config's volatile paths against a stub that mirrors the real rule. Mirroring it rather than waving it through matters: a structurally invalid Config fails no behavioural test, it just stops the plugin activating in a user's install. routes[].id escaped that way once.

The design spec is at docs/superpowers/specs/2026-10-14-dsh-balance-meter-design.md (Chinese).

License

MIT