dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH Web GUI 会话页头部余额芯片:经宿主侧 Remote 用你的 API Key 查询 DeepSeek 账户余额,点击跳转充值,悬停可见赠送/充值拆分。API Key 不进浏览器。
English | 中文
A DeepSeek balance chip for the DSH Web GUI: shows your account balance in the conversation header, opens the recharge page on click, and reveals the granted / topped-up split on hover.
The API key and the network request live entirely on the host side — the key never reaches browser code.
| Capability | Notes |
|---|---|
| Balance display | A chip in the conversation header showing the total, matching the visual density of the built-in "Session log" button |
| Click to recharge | Clicking the chip opens platform.deepseek.com/top_up; the Electron shell routes it to your system browser |
| Manual refresh | The ↻ button inside the chip (it stops propagation, so it cannot trigger the recharge jump) |
| Auto refresh | On mount, every 60 seconds, and whenever the window regains focus |
| Hover detail | Account balance plus the granted / topped-up split (that line is omitted when the grant is 0) and the recharge hint |
| Narrow screens | Phones drop the word "余额" and keep only the amount |
| Unavailable warning | When is_available is false it says so, instead of quietly showing ¥0.00 |
| Readable failures | Distinct messages for a missing key, an invalid key, insufficient balance, rate limiting, timeout, no network, and a malformed response |
dsh plugin --profile web add dsh-header-balance
Restart DSH and the chip appears in the conversation header.
dsh plugin add reads the dsh.bundle.patch field in this package's package.json and inserts the plugin into the profile tree for you. No manual edit of cordis.patch.yml is needed. The entry it writes is shown below, for troubleshooting only:
- insert:
- id: dsh-header-balance
name: 'dsh-header-balance'
Installing through npm directly also works (for a hand-rolled profile, or offline distribution), but then you do add that entry yourself:
npm install dsh-header-balance
The plugin resolves the same credential reference as the official llm-deepseek adapter (default DEEPSEEK_API_KEY), so if your model works, the balance lookup works — no separate configuration.
Balance is not a pure front-end widget; it needs the host:
Browser (lib/client.js) Host (lib/index.js)
connection.rpc.call('/api', reads settings['llm-deepseek']
'accountBalance/balance', {} ) → resolves the API key via credentials
↓ GET api.deepseek.com/user/balance
receives normalized numbers only ←── parses the wire's snake_case strings
Three hard constraints, each documented with citations in ARCHITECTURE.md:
ctx.remote.accountBalance. The remote.<ns> namespace services are mounted only for the 15 official strict contributions; declaring one would park the plugin forever and take host startup down with it. Use the raw carrier connection.rpc.call.inject must stay empty. It is a startup gate: a declared service that never arrives keeps the plugin pending and the whole app fails to boot. settings / credentials are read softly via ctx.get() with fallbacks.Function.prototype.toString(), so no destructuring, defaults, rest parameters, or duplicate names.Wire endpoint: accountBalance/balance, payload { args: {} }.
node scripts/link-host-deps.mjs # symlink host-side peer deps into this repo (dev only)
npm run build:bundle # regenerate the bundle's inlined logic from lib/balance.js
npm run check # syntax preflight + verify the inlined region matches the source
npm test # 36 tests
npm run verify # all of the above
The display logic inside lib/client.js is not hand-written: scripts/inline-balance.mjs inlines it from lib/balance.js at build time. DSH's bundle resolver only knows platform seed words and registered package ids, so requiring the package's own subpath always fails — and the logic cannot be hand-written into the bundle either, since then it could not be unit-tested in Node.
test/balance.test.js (12) — display formatting and copy. Pure functions, no dependencies.test/host-service.test.js (19) — the host half. It really imports @deepseek-ai/dsh-typert-protocol and asserts that remoteMethods() reads the Remote marker back, so the SRC discovery path is genuinely exercised rather than stubbed. Covers the happy path, seven failure classes, credential precedence and fallback, and "the API key never appears in the return value".test/client-contract.test.js (5) — the bundle contract. Actually executes lib/client.js in node:vm, with a require that only admits platform seed words — stricter than the real host.The host-side tests need scripts/link-host-deps.mjs; without an installed DSH they skip automatically and say so in the report, leaving the pure-logic tests unaffected.
conversation.session.header.utilities, kind: list).settings and credentials services; if either is absent it falls back to process environment variables, and reports "not configured" when neither yields a key.MIT