dsh-quota-panel
Provider quota/balance widget for the dsh web surface: collapsed glanceable capsule expanding into a Harness-native card with a settings panel (provider visibility / refresh interval / warn thresholds), fed by a loopback Connection RPC channel whose host half proxies each provider with credentials that never reach the browser.
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:wenzetan/dsh-quota-panel说明文档
阅读完整 README ↗Configuration
Out of the box: nothing. Install, restart, and any provider whose key resolves appears automatically. The table below is only for tuning.
All keys are optional — the structure and defaults live in the exported
Config schema, so profile patches may omit every defaulted field.
| Key | Meaning | Default |
|---|---|---|
auto | probe the built-in catalog; providers with a resolvable key join the panel | true |
hide | row ids to drop (catalog and explicit rows alike) | [] |
proxies | named proxy definitions {: "http://host:port"}, HTTP(S) only | {} |
catalog | partial overrides for auto-discovered rows {: {...}} | {} |
refreshMs | auto-refresh interval | 60000 |
providers | explicit rows; a same-id entry replaces the catalog row wholesale | [] |
Each catalog override may set: label / endpoint / format /
proxy / refs (credential references to probe, UPPER_SNAKE) / currency
(balance rows: symbol like $ or US$) / balanceTiers / warnPercent /
errorPercent / windowLabels.
Explicit providers fields:
| Field | Meaning | Default |
|---|---|---|
id | row id (RPC rows align by id), ^[a-z0-9-]+$ | required |
label | provider name shown on the card | required |
credential | credential reference ($DSH_HOME/.credentials.yaml or environment) | required |
endpoint | quota JSON endpoint; base URL for openai-billing | required |
format | row adapter (see table below) | deepseek-balance |
proxy | a proxy name defined in proxies; absent = direct | — |
currency | (balance rows) currency symbol, overrides the format default | format default |
balanceTiers | (balance rows) {critical, warn, healthy} | {10, 20, 50} |
lowBalance | legacy alias for balanceTiers.warn | — |
windowLabels | (usage-kind formats) labels for the usage windows | {滚, 周, 月} |
warnPercent / errorPercent | (usage rows) thresholds | 70 / 90 |
Built-in provider catalog (auto discovery)
| Provider | Credential refs probed | Endpoint | Row kind |
|---|---|---|---|
| DeepSeek | DEEPSEEK_API_KEY | api.deepseek.com/user/balance | ¥ balance |
| OpenRouter | OPENROUTER_API_KEY | openrouter.ai/api/v1/credits | $ balance (purchased − used) |
| SiliconFlow (global) | SILICONFLOW_API_KEY | api.siliconflow.com/v1/user/info | $ balance |
| SiliconFlow (CN) | SILICONFLOW_CN_API_KEY | api.siliconflow.cn/v1/user/info | ¥ balance |
| Moonshot / Kimi | MOONSHOT_API_KEY | api.moonshot.cn/v1/users/me/balance | ¥ balance |
| MiniMax Coding (global) | MINIMAX_API_KEY | www.minimax.io/v1/token_plan/remains | 5h prompt usage % |
| MiniMax Coding (CN) | MINIMAX_CN_API_KEY | api.minimaxi.com/v1/token_plan/remains | 5h prompt usage % |
| StepFun | STEP_API_KEY / STEPFUN_API_KEY | api.stepfun.com/v1/accounts | ¥ balance (hover: cash/voucher) |
| xAI | XAI_API_KEY | api.x.ai/v1/billing/credits | $ balance |
| Zhipu GLM | ZHIPU_API_KEY / GLM_API_KEY | open.bigmodel.cn/api/monitor/usage/quota/limit | text row (quota remaining/total; no public balance API) |
| 智谱 GLM Coding | ZAI_CODING_CN_API_KEY | open.bigmodel.cn/api/monitor/usage/quota/limit | coding-plan windows (5h tokens / weekly / searches) |
| Z.AI GLM Coding | ZAI_API_KEY | api.z.ai/api/monitor/usage/quota/limit | coding-plan windows (5h tokens / weekly / searches) |
| Kimi Coding | KIMI_API_KEY | api.kimi.com/coding/v1/usages | usage % (5h rate limit + weekly request pool) |
| OpenCode Go | OPENCODE_GO_API_KEY | opencode.ai/zen/go/v1/usage | three-window usage % |
An additional openai-billing format adapts one-api / new-api style
aggregators: set endpoint to the aggregator base URL and the host half
requests {base}/v1/dashboard/billing/subscription
(hard_limit_usd) plus {base}/v1/dashboard/billing/usage
(total_usage); remaining = limit − used ($). Aggregator domains differ
per deployment, so this format is explicit-config only.
Dual-site provider ids (custom id → site mapping)
Some providers run separate international and China sites with different
endpoints, credential references and currencies. The catalog models each
site as its own provider id, so configuring the matching key is all it
takes — and an explicit providers: entry reusing one of these ids replaces
the catalog row wholesale (same fields, your endpoint/label/currency):
| provider id | Site | Endpoint | Credential ref | Currency |
|---|---|---|---|---|
siliconflow | SiliconFlow global | api.siliconflow.com/v1/user/info | SILICONFLOW_API_KEY | $ |
siliconflow-cn | SiliconFlow China | api.siliconflow.cn/v1/user/info | SILICONFLOW_CN_API_KEY | ¥ |
minimax | MiniMax Coding global | www.minimax.io/v1/token_plan/remains | MINIMAX_API_KEY | — (usage %) |
minimax-cn | MiniMax Coding China | api.minimaxi.com/v1/token_plan/remains | MINIMAX_CN_API_KEY | — (usage %) |
zai | Z.AI GLM Coding global | api.z.ai/api/monitor/usage/quota/limit | ZAI_API_KEY | — (usage %) |
zai-coding-cn | 智谱 GLM Coding China | open.bigmodel.cn/api/monitor/usage/quota/limit | ZAI_CODING_CN_API_KEY | — (usage %) |
Both sites of one provider can be on the panel at the same time (configure
both keys); hide: ["siliconflow"] drops either row individually.
The currency symbol for balance-kind rows comes from the format by default
(siliconflow-balance renders ¥) and can be overridden per row: catalog
rows carry currency (the global SiliconFlow row sets $), a catalog:
override may set it, and explicit providers: entries accept a currency
field (e.g. "US$").
Built-in formats
| format | Row kind | Upstream response shape |
|---|---|---|
deepseek-balance | ¥ balance | { balance_infos: [{ currency, total_balance, granted_balance, topped_up_balance }] } |
openrouter-credits | $ balance | { data: { total_credits, total_usage } } |
siliconflow-balance | balance (¥ by default, per-row currency override) | { data: { balance, chargeBalance, totalUsage } } |
moonshot-balance | ¥ balance | { data: { total_balance } } |
minimax-remains | usage % | { base_resp, model_remains: [{ model_name, current_interval_total_count, current_interval_usage_count, current_interval_remaining_percent, end_time, current_weekly_total_count, current_weekly_usage_count, weekly_end_time }] } — coding model row (MiniMax-M*) preferred; counts are remaining-side (used = total − count); weekly window only when current_weekly_total_count > 0 |
stepfun-accounts | ¥ balance | { balance, total_cash_balance, total_voucher_balance } |
xai-credits | $ balance | { total: { val } } (cents → dollars) |
openai-billing | $ balance | aggregator dashboard/billing endpoints |
zhipu-quota | text | { code: 200, data: { limits: [{ remaining, number }] } } (limits without remaining fall back to percentage) |
opencode-usage | usage % | `{ usage: { rolling |
zai-coding-quota | usage % | { code: 200, data: { limits: [{ type: TOKENS_LIMIT | TIME_LIMIT, unit, number, percentage, currentValue, usage, nextResetTime }] } } — semantic mapping (glm-plan-usage2, issue #2): TOKENS_LIMIT unit=3 → 5h window, unit=6 → weekly, TIME_LIMIT → MCP monthly lane; unknown units fall back to nextResetTime ordering; every window prefers the percentage field |
kimi-coding-usage | usage % | { usage: { limit, used, remaining, resetTime }, limits: [{ window: { duration, timeUnit }, detail: { limit, used, remaining, resetTime } }] } — 5h = the duration=300 window, weekly = duration=10080 (fallback: top-level usage); used = limit − remaining |
Proxy (providers that cannot be reached directly)
Configure per-provider proxies in the frontend settings panel (⚙ →
代理): fill an HTTP(S) proxy URL (e.g. http://127.0.0.1:7890,
user:pass allowed), saved to browser localStorage, effective immediately —
leave it empty to fall back to the profile config or a direct connection.
Requests still run host-side: the browser sends each row's proxy URL in the
fetch-all payload, the host validates it (http/https only, socks
rejected) and fetches through it — keys never reach the browser, but the
proxy itself can observe them (see Known issues & risks
under Security).
The profile proxies map + row-level proxy /
catalog..proxy remain available as default proxies (used when
the frontend field is empty). Precedence:
frontend settings > profile config > direct.