English | 中文
A DeepSeek Harness (dsh) plugin that shows your DeepSeek API account balance and this session's spend in a line below the prompt (composer). The host half fetches the balance and resolves pricing; the browser half renders the readout into the conversation.composer.dock slot under the composer (same band as the built-in stats line).
dsh-balance/
├── package.json # dsh.bundle.patch → cordis.patch.yml; exports["."] → host half; dsh.client → browser half
├── cordis.patch.yml # bundle layer: mounts the plugin into the profile's loader
├── dsh/
│ ├── index.js # host half: GET /dsh-balance (balance + cache + optional proxy + pricing)
│ └── client.js # browser half: composer.dock slot entry (balance + session cost, polling + click-to-refresh)
└── smoke.mjs # smoke tests (fake ctx + stubbed fetch; --live hits the real API)
Zero dependencies — Node built-ins only.
- Host half: a
GET /dsh-balanceroute (registered when thewebServerservice appears; the headless profile is untouched). Resolves the API key (plugin configapiKey> theDEEPSEEK_API_KEYcredential viactx.credentials> environment variable), callsGET https://api.deepseek.com/user/balance, and caches the result forcacheMs(default 60s); a failed refresh falls back to the previous cached value flaggedstaleso the readout never blanks on a flaky network. The response also carries the currently effective pricing (peak/off-peak resolved automatically against Beijing time). - Browser half: registers into the
conversation.composer.dockslot (order: 10, after the built-in stats line). Shows余额 ¥48.62 · 本会话 ¥0.006— the session spend is computed live fromuseProjection('tokenUsage')(dsh's durable token-usage projection, updated in real time as a turn streams) and the pricing from the response:(uncached input × miss price + cache read × hit price + cache write × miss price + output × output price) / 1e6. Fetches on mount, polls on the response'srefreshMs(default 60s), refreshes when the tab becomes visible or the window regains focus, and refetches on click. Hovering shows the token breakdown (input/cache/output) and the peak/off-peak period. Failures degrade to a quiet placeholder (e.g. "未配置 API Key" / "no API key configured") — never a crash.
From GitHub (for everyone):
# Option 1: install as a dependency (recommended)
# 1) add to the dependencies of ~/.dsh/profiles/web/package.json:
# "dsh-balance": "github:m2lan/dsh-balance"
# 2) add "dsh-balance" to dsh.profile.bundles
# 3) install dependencies:
npx -y @deepseek-ai/dsh plugin --profile web install
# Option 2: add directly (requires pnpm)
npx -y @deepseek-ai/dsh plugin --profile web add github:m2lan/dsh-balanceLocal development install (this machine has no pnpm, so the manual three steps mirror gemini-eyes):
- Add
"dsh-balance": "file:/Users/M2/Documents/ai/harness/h1/dsh-balance"to thedependenciesof~/.dsh/profiles/web/package.json; - Append
"dsh-balance"todsh.profile.bundles; - Symlink
~/.dsh/profiles/node_modules/dsh-balance→ this directory.
Restart dsh web afterwards (a new bundle is not hot-loaded into a running instance).
With pnpm you can also use the standard command:
npx -y @deepseek-ai/dsh plugin --profile web add ./dsh-balanceKey resolution order: plugin config apiKey > the DEEPSEEK_API_KEY credential (written to ~/.dsh/.credentials.yaml by the web Models page) > environment variable. By default the credential's key is used — no extra configuration needed.
Proxy (optional): Node's global fetch ignores proxy environment variables. DeepSeek is usually reachable directly; if you must go through a proxy, the plugin ships a zero-dependency HTTP CONNECT tunnel, resolving the proxy from plugin config proxy > env vars such as HTTPS_PROXY. Add it to the dsh-balance entry in ~/.dsh/profiles/web/cordis.patch.yml:
- id: dsh-balance
config:
# apiKey: 'sk-...' # explicit key
# keyRef: 'DEEPSEEK_API_KEY' # credential/env ref to resolve (default)
# proxy: 'http://127.0.0.1:7897'
# cacheMs: 60000 # host-side cache duration (default 60000)
# refreshMs: 60000 # browser poll interval (default 60000)
# path: '/dsh-balance' # served route path (default /dsh-balance)
# --- session-cost pricing (CNY per million tokens, peak base) ---
# Defaults follow DeepSeek's official peak/off-peak pricing effective
# 2026-08-17 (https://api-docs.deepseek.com/quick_start/pricing/):
# peak hours are Beijing 09:00-12:00 and 14:00-18:00, off-peak = peak ×
# offPeakFactor. Override here when DeepSeek changes its prices — no code
# changes needed:
# pricing:
# deepseek-v4-flash: { inputHit: 0.10, inputMiss: 3.0, output: 9.0 } # peak
# deepseek-v4-pro: { inputHit: 0.30, inputMiss: 9.0, output: 27.0 }
# default: { inputHit: 0.10, inputMiss: 3.0, output: 9.0 } # fallback for unknown models
# offPeakFactor: 0.5 # off-peak price factor (default 0.5)
# peakHours: [[9,12],[14,18]] # peak windows in Beijing hours (default above)About the pricing: the session spend is an estimate (for display, not a bill). The token buckets come from dsh's durable usage projection (the same source the stats line reads); the price switches between peak and off-peak automatically according to current Beijing time; DeepSeek bills cache writes at the cache-miss rate, and the plugin does the same. DeepSeek changes its official prices from time to time — always defer to the official pricing page and override via pricing when needed. If you switch to a non-DeepSeek model, add a matching pricing entry (or change default).
After restart, a line 余额 ¥48.62 appears below the composer; once the session produces tokens it becomes 余额 ¥48.62 · 本会话 ¥0.006 (grows live; click to refresh, hover for the token breakdown and peak/off-peak period). You can also confirm the plugin is mounted in the composed config tree:
npx -y @deepseek-ai/dsh --profile web --dump-config | grep -A3 dsh-balanceSmoke tests (no network):
node smoke.mjs
node smoke.mjs --live # hits the real DeepSeek balance API (uses the key in ~/.dsh/.credentials.yaml)- The key never leaves the dsh process: the balance is fetched with an
Authorization: Bearerheader toapi.deepseek.com; the browser only receives the balance figure and the pricing, never the key. - The balance endpoint is read-only: only
GET /user/balanceis called — this plugin never spends anything. - The session spend is an estimate: token buckets come from dsh's token-meter projection (provider-reported usage with a heuristic fallback), priced with the official peak/off-peak rates that switch over time; reference only, not a billing record.
- Restart after code changes: changing
dsh/index.jsordsh/client.jsrequires a dsh web restart to take effect (the client bundle's rev changes with its content, so a page refresh picks up the new version). - Uninstall: remove the dependency and the bundles entry from
~/.dsh/profiles/web/package.json, delete the~/.dsh/profiles/node_modules/dsh-balancesymlink, revertcordis.patch.yml, and restart.