dsh-deepseek-balance
vincibeans/dsh-deepseek-balance
DeepSeek account balance monitor for DSH: polls GET /user/balance every 5s and shows the balance in the web UI (session header pill + settings card).
安装
dsh plugin --profile web add github:vincibeans/dsh-deepseek-balanceREADME
dsh-deepseek-balance
A DSH (DeepSeek Harness) plugin that polls the account balance of your DeepSeek API key every 5 seconds and shows it in the web UI.
- a compact pill in the session header — total balance per currency, with a heartbeat dot (green = live data, amber = last poll failed and it is retrying, red = low balance or no balance available)
- a settings card (Settings → Plugins → Configurable) with the balance details (granted / topped up / availability / last update) and the plugin configuration
中文 | English
Compatibility
Targets DSH v0.1.2-rc.1 and v0.1.3-alpha.2 (the latter verified against the alpha.2 checkout at 72fde8ccca). Both were verified end-to-end with scripts/compat-probe.mjs (11/11 checks: boot graph, bundle form, status endpoint, auth fence, pill render, card fold flow, clean console).
The contract surface this plugin consumes — everything else is local code or the platform seed table:
| seam | use |
|---|---|
dsh.bundle.patch row + dsh.client declaration | host load + browser bundle discovery |
/plugins/??<pkg>/client.js module-table closure (window.__ModuleLoader__.load) | browser half delivery |
ctx.slots.inject/register | conversation.session.header.actions (pill), settings.plugin.item (card) |
ctx.connection.fetch.register | host status endpoint on the shared /api carrier, which applies the deployment's Host/Origin fence and browser authentication |
browser fetch('/api/deepseek-balance') | same-origin status read; no DSH client service involved |
ctx.settings.installSection / ctx.settingsScope.bind | configuration persistence |
ctx.locale.register | zh/en copy |
ctx.credentials.resolve (optional) | DEEPSEEK_API_KEY; falls back to the process environment |
platform seed rows react, react/jsx-runtime, @deepseek-ai/dsh-client-ui-primitives | browser bundle externals |
Deliberately not used: connection.rpc.handle. Its route registration resolves the owner context through a cordis shadow and throws cannot get property "webServer" without inject for out-of-repo plugins on 0.1.3+ (no in-repo production code uses it). The exact Fetch route reaches the same carrier with the same auth and no such coupling.
Not supported: 0.1.1-rc.2 and earlier — that generation runs a different client-runtime contract.
Verify any DSH build yourself against a running instance (from a source checkout; the probe is not shipped in the npm tarball):
node scripts/compat-probe.mjs http://127.0.0.1:3080 <boot-token> --browser
Notable packaging fact: the 0.1.2-rc.1/0.1.3-alpha.* package line is not published to npm yet, so the plugin carries no runtime @deepseek-ai/* dependency — the host half bundles @deepseek-ai/schemastery (validation only) and everything else resolves from the DSH install; @deepseek-ai/* types are hand-written shims, never imports.
Install
The plugin does not store an API key: it resolves the same DEEPSEEK_API_KEY credential reference the model adapter uses (through DSH's credentials service, then the environment), so a key stored or rotated on the web Models page reaches the next poll. The key never leaves the host process — the browser only receives the balance snapshot.
From source (current)
npm install && npm run build # build host + browser bundles
dsh plugin --profile <name> add ./dsh-deepseek-balance
dsh --profile <name>
Or as a git dependency once the repo has the prepare build (pnpm runs it after a git install; allow it if pnpm asks):
dsh plugin --profile <name> add github:VinciBeans/dsh-deepseek-balance
From npm (after the first release)
dsh plugin --profile <name> add @wenqi_bian/dsh-deepseek-balance
The npm package ships prebuilt lib/ bundles, so no build step is needed on install.
Quickstart
npm install
npm run build # lib/index.js (host) + lib/client.js (browser)
dsh plugin --profile <name> add .
dsh --profile <name> --dump-config # a "== @wenqi_bian/dsh-deepseek-balance" layer appears
# restart dsh web, open a conversation — the balance pill sits in the session header
What you see
The pill shows CNY 100.00 next to the session title. Hover for the breakdown (granted / topped up / availability / last update / last error); click to refresh immediately.
| state | meaning |
|---|---|
| green dot | last poll succeeded (ticked on each poll) |
| amber dot | last poll failed; it keeps retrying with the backoff below |
| red pill | balance below the threshold, or is_available is false |
No key | no API key resolved |
Configuration
Settings card
Settings → Plugins → Configurable → DeepSeek Balance:
| field | default | meaning |
|---|---|---|
| Poll interval (ms) | 5000 | query cadence; minimum 1000 |
| Low-balance threshold | 10 | per currency; below it the pill turns red |
| API key environment variable | DEEPSEEK_API_KEY | credential reference name |
| API base URL | https://api.deepseek.com | origin for GET /user/balance |
cordis.patch.yml
The same values as the entry row's config (the base layer; the settings card overrides them):
- insert:
- id: deepseek-balance
name: '@wenqi_bian/dsh-deepseek-balance'
config:
intervalMs: 5000
lowBalanceThreshold: 10
apiKeyEnv: DEEPSEEK_API_KEY
baseUrl: https://api.deepseek.com
Failure behavior
Consecutive failures delay the next poll: failure 1 → max(2×interval, 10s), failure 2 → max(3×interval, 20s), failure 3 → max(4×interval, 30s), no further escalation. Any success resets the counter. The pill keeps the last successful value (amber dot) and the tooltip explains the reason; without a first success it shows a placeholder. A 401 is reported as an invalid key. While the browser tab is hidden the timer pauses; showing it again polls immediately.
Development
npm install
npm run build # esbuild: lib/index.js (host) + lib/client.js (browser)
npm run typecheck
npm test # node --test tests/
Load into a DSH source checkout without publishing — the entry row may point at the source file directly:
# dev.cordis.yml
- insert:
- id: deepseek-balance
name: 'file:///E:/path/to/dsh-deepseek-balance/src/index.ts'
On Windows the entry name must be a file:// URL (as above); on POSIX an absolute path works.
pnpm dsh web --patch ./dev.cordis.yml # from the DSH checkout
The host half runs from TSX; build the browser half (npm run build) after client edits and refresh the page (or run the DSH client bundle watcher for no-refresh updates). Verify the composed tree with pnpm dsh --profile <name> --dump-config.
How it works
- Host half (
src/index.ts): registers thedeepseek-balancesettings section, then a private RPC channel/deepseek-balance(connection.rpc.handle— registered through a deferred inject because the connection service activates only after the web server binds). Eachstatuscall resolves the key from the credential seam and fetchesGET {baseUrl}/user/balance; failures come back as typed codes (no-key,unauthorized,network,api,invalid-response). - Browser half (
src/client/): one reference-counted poller drives both surfaces overconnection.rpc.call('/deepseek-balance', 'status'); the pill (BalancePill.tsx) and the settings card (BalanceCard.tsx) render with DSW design tokens, and the copy follows the UI language (ctx.locale, zh/en, en fallback).
Design decisions are recorded in docs/adr; the domain glossary is in CONTEXT.md.
License
MIT — see LICENSE.