dsh-provider-billing
English | 中文
A DeepSeek Harness plugin (maintained outside the official repository) that shows the account balance behind each configured provider route's stored API key — one balance line inside each provider row of the Models settings page, with a refresh control per route. The key never leaves the host: the browser sends only the route id, and the host resolves the endpoint and credential from the configuration plane before asking the provider's OpenAI-compatible /user/balance endpoint.
Host requirement: the plugin renders inside the Models page's settings.models.row contribution hole, which only newer harness builds declare. Older harness builds are not supported: the plugin reports a clear load failure instead of degrading silently. Do not install it on an unsupported harness.
Install
Any of the three equivalent routes:
1. npm
dsh plugin --profile web add dsh-provider-billing
Published on npm as dsh-provider-billing — the simplest route: the tarball ships the built lib/ and cordis.patch.yml, so no build step runs on install.
2. GitHub
dsh plugin --profile web add github:ZeroingIn/dsh-provider-billing
A GitHub install fetches sources: pnpm runs the package's prepare script (tsdown, fully self-contained — no sibling checkout, no npm resolution of @deepseek-ai/*) during install. pnpm ≥ 10 refuses to run a git dependency's build script until it is allowlisted — copy the exact package key pnpm prints into the profile's pnpm-workspace.yaml:
allowBuilds:
dsh-provider-billing: true
Allowlisting means permission to execute this package's code on your machine at install time. For a locked build, append a commit sha: github:ZeroingIn/dsh-provider-billing#<sha>.
3. tarball
pnpm pack # produces dsh-provider-billing-0.1.1.tgz
dsh plugin --profile web add ./dsh-provider-billing-0.1.1.tgz
Local development
dsh plugin --profile web add link:/absolute/path/to/dsh-provider-billing
link: points the profile at this directory: edit → pnpm build → restart dsh web for host-half changes, or just refresh the browser for client-only changes (the client bundle is served live at /plugins/dsh-provider-billing/client.js). Switch to npm/GitHub distribution once stable.
Config
- id: provider-billing
name: dsh-provider-billing
config:
providers: [deepseek-official]
providers names the configurable-provider routes this deployment may check; a route absent from the directory never appears. The UI is fixed to the Models-row form (settings.models.row) — there is no other form and no compatibility fallback.
How it works
- Host half (
src/index.ts): one plugin row registers a loopback-pinned generic RPC channel (/provider-billing, endpointslistandquery) throughctx.connection.rpc.handle(..., { authority: 'loopback' }), with the loopback fence enforced by the Connection layer — the same fence that pins the privileged/apimethods.queryresolves the route's stored key and endpoint (profile →<ROUTE>_API_KEYderivation → adapter default, each through the credential seam then the trusted environment, first hit wins) and fetches/user/balance. - Browser half (
src/client/): registers into the Models page'ssettings.models.rowhole and renders the balance card (bilingual copy,余额/Balance). Without the hole it prints an explicit load-failure error.
Development
pnpm install # toolchain only — @deepseek-ai/* peers come from the installing harness
pnpm typecheck # needs tsconfig.local.json (below)
pnpm test # vitest: channel/query + the row card
pnpm build # tsdown + tsc declarations → lib/{index,invariant,client}.js + lib/types
pnpm pack # tarball
Local harness checkout: the published @deepseek-ai/* rc packages on npm have a broken dependency tree, so this package declares them as peerDependencies (satisfied by the installing harness) and resolves their types and test runtime from a local harness checkout. Create tsconfig.local.json (gitignored; pnpm typecheck, pnpm test and pnpm build read it) by extending tsconfig.json with declaration emit for lib/types plus a paths block pointing every @deepseek-ai/* specifier at the checkout's built types:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "lib/types",
"paths": {
"@deepseek-ai/cordis": ["<HARNESS_CHECKOUT>/vendor/cordis/lib/types"],
"@deepseek-ai/dsh-llm": ["<HARNESS_CHECKOUT>/packages/llm/llm/lib/types"],
"@deepseek-ai/dsh-settings": ["<HARNESS_CHECKOUT>/packages/settings/settings/lib/types"],
"@deepseek-ai/dsh-credentials": ["<HARNESS_CHECKOUT>/packages/credentials/credentials/lib/types"],
"@deepseek-ai/dsh-launch-environment": ["<HARNESS_CHECKOUT>/packages/util/launch-environment/lib/types"],
"@deepseek-ai/dsh-host-apiproxy": ["<HARNESS_CHECKOUT>/packages/host/apiproxy/lib/types"],
"@deepseek-ai/dsh-host-apiproxy/api": ["<HARNESS_CHECKOUT>/packages/host/apiproxy/lib/types/api"],
"@deepseek-ai/dsh-invariants": ["<HARNESS_CHECKOUT>/packages/runtime-diagnostics/invariants/lib/types"],
"@deepseek-ai/dsh-client-connection": ["<HARNESS_CHECKOUT>/packages/client/connection/lib/types"],
"@deepseek-ai/dsh-client-connection/client": ["<HARNESS_CHECKOUT>/packages/client/connection/lib/types/client"],
"@deepseek-ai/dsh-client-locale/client": ["<HARNESS_CHECKOUT>/packages/client/locale/lib/types/client"],
"@deepseek-ai/dsh-client-runtime/client": ["<HARNESS_CHECKOUT>/packages/client/runtime/lib/types/client"],
"@deepseek-ai/dsh-client-test-runtime": ["<HARNESS_CHECKOUT>/packages/test-support/client-runtime/lib/types"],
"@deepseek-ai/dsh-client-ui-settings/client": ["<HARNESS_CHECKOUT>/packages/client/ui-settings/lib/types/client"],
"@deepseek-ai/dsh-client-ui-settings-models/client": ["<HARNESS_CHECKOUT>/packages/client/ui-settings-models/lib/types/client"],
"@deepseek-ai/dsh-client-ui-slots": ["<HARNESS_CHECKOUT>/packages/client/ui-slots/lib/types"],
"@deepseek-ai/dsh-host-webserver": ["<HARNESS_CHECKOUT>/packages/host/webserver/lib/types"]
}
},
"include": ["src"]
}
The tests derive their runtime aliases from the same file by rebasing /lib/types onto /src.
Known limitations
- Only the
/user/balanceconvention is served (the DeepSeek official shape); a provider with a different balance API reports its own error through the card. - Endpoint errors ride the closed wire taxonomy's
internalbranch (with descriptive messages). - npm tarballs do not include
lib/typesunlesspnpm buildran on the publishing machine first (runtime is unaffected). - The
rowUI requires thesettings.models.rowhole; unsupported harness builds fail loud.
No comments yet. Be the first to write one.