类原生计费插件
在 DeepSeek Harness 终端运行:
dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billingEnglish | 中文
A DeepSeek Harness plugin that shows your DeepSeek account balance, this session's (this conversation's) billed spend, and today's total spend across all sessions directly in the web session header; each completed turn also shows its turn cost as a static amount at the end of the message actions row, and the detail panel ends with a today session-spend ranking.
The balance is the real
GET /user/balancefigure; the session, turn, and today spends price each message's billed tokens at the official peak/off-peak rates and are estimates, not billing promises.
剩余额度:¥X) and this conversation's billed spend (本轮对话花费:¥X).本会话花费) with today's all-session spend beside it (今日共花费), one priced row per model (缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z), plus a manual refresh action and a spend disclaimer. The panel ends with a today session-spend ranking: sessions sorted by today's spend, highest first (names come from the log's Chinese titles and follow renames automatically; at most the top 10 rows, with a "…N more sessions" hint).¥X at the end of the actions row, after the clock: non-interactive (no icon, no "cost" word, no card), its typography replicates the clock text (13px secondary tier, tertiary tone, nowrap), and it is always visible (not hover-revealed like the clock text — the row's own hover reveal shows both together); turns without DeepSeek usage (zero cost) or failed loads stay hidden.billingTodaySpend) and pushes it to the browser, so this session's spend updates live with no Remote call; the Remote read remains the fallback when the projection registry is absent. Today's spend is recomputed on turn settle (one shared scan serves both the aggregate and the ranking), and each turn's cost comes from one batch fetch per session instead of one call per rendered message./user/balance snapshot for 15 seconds (manual refresh forces a fresh one) and caps each request at 5 seconds; the browser keeps the last settled value so a session switch renders the amount immediately and revalidates in the background. There is still no polling.A real session: the session-header badge, the detail panel (remaining amount, this session's spend next to today's all-session spend, per-model breakdown, and the today session-spend ranking), plus the turn-cost amount at the end of the message actions row:
Close-up of the detail panel — the API 剩余金额 figure, 本会话花费 next to 今日共花费, the per-model breakdown (缓存命中 · 未命中输入 · 输出), and the today session-spend ranking:
Close-up of the turn-cost amount — the static ¥ amount at the end of the actions row, after the clock:
| Package | Side | Role |
|---|---|---|
packages/llm-billing — @rayadesu/dsh-llm-billing |
Host | Owns the /user/balance transport and the peak/off-peak pricing table. Exposes the billing Remote (getBalance(force?), getSessionSpend, getTodaySpend, getTodaySessionsSpend, getTurnSpend, getSessionTurnSpends) and registers the client-visible billingTodaySpend projection unit. |
packages/ui-billing — @rayadesu/dsh-client-ui-billing |
Browser | Mounts the billing Remote itself and contributes the session-header badge and detail panel, plus the static turn-cost amount at the end of the message actions strip. |
dsh) — the plugin runs inside a dsh profile.The three packages are published to npm under the @rayadesu scope. Install the
bundle plus the two plugin packages in one command — the bundle declares the two
plugin packages as peer dependencies, which pnpm does not auto-install into the
profile, so they must be named explicitly.
The dsh command you use depends on how dsh is installed:
Global install — use the global dsh from anywhere:
dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
Source-built dsh (a deepseek-harness checkout) — the CLI only resolves from
the source directory, so run it through pnpm there (pnpm dsh is the
harness-local binary, equivalent to the global dsh):
cd deepseek-harness
pnpm dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
A dsh profile installs plugins through pnpm, and pnpm 11's supply-chain
release-age gate does not pick up packages younger than 24 hours by default —
a freshly published version is therefore not resolved immediately. To get the
latest version right after a publish:
Disable the age gate in the profile's pnpm config:
# ~/.dsh/profiles/web/pnpm-workspace.yaml
minimumReleaseAge: 0
Or, within the 24-hour window, install with an explicitly pinned version (an
explicit pin bypasses the age gate; replace 0.3.0 with the version you want;
from a source checkout, use pnpm dsh … as above):
dsh plugin --profile web add @rayadesu/dsh-billing@0.3.0 @rayadesu/dsh-llm-billing@0.3.0 @rayadesu/dsh-client-ui-billing@0.3.0
Manual rows (only when you do not want the bundle):
# ~/.dsh/profiles/web/cordis.patch.yml
- insert:
- id: llm-billing
name: '@rayadesu/dsh-llm-billing'
- id: ui-billing
name: '@rayadesu/dsh-client-ui-billing'
Global dsh is assumed; a source-built dsh uses pnpm dsh from the
deepseek-harness checkout instead — the subcommands are identical.
dsh plugin --profile web list # list the web profile's installed plugins
dsh plugin --profile web add @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
dsh plugin --profile web remove @rayadesu/dsh-billing @rayadesu/dsh-llm-billing @rayadesu/dsh-client-ui-billing
dsh plugin --profile web update # update plugins to the latest allowed versions
dsh plugin --profile web update --latest # ignore declared ranges; upgrade every plugin to its newest published version
update respects the version ranges in the profile's package.json, so it
stays within the semver range each plugin declares. Adding --latest (a pnpmupdate flag) instead ignores those ranges and upgrades every plugin to its
newest published version — the way to pick up a fresh release immediately once
it is resolvable. From a source-built dsh checkout you run it aspnpm dsh … in the deepseek-harness directory, exactly as with the other
commands.
The two plugin packages declare the DeepSeek Harness packages they build on
(@deepseek-ai/cordis, @deepseek-ai/dsh-credentials, @deepseek-ai/dsh-session,
and the client runtime packages) as peerDependencies at ^0.1.2-alpha.5. A dsh
profile does not auto-install peers, so these are provided by the dsh
installation itself through the profiles/node_modules fallback rather than
fetched from the registry — no extra packages to install, and no registry token
needed on the installing machine.
The plugin builds against the 0.1.2-alpha.5 published line and keeps both DSH
runtime families readable: the live Session log surface
(Session.events + header.seedLength at/before 0.1.1-rc.2,snapshotEvents() + inheritedEventCount since 0.1.2-alpha.4), and the
persistence service surface (inspect/listSnapshots at/before 0.1.1-rc.2,open+SessionHandle/list on the 0.1.2-alpha.5 handle-based seam — the
checkout master that ships the refactor). The projection unit's init is
declared with the newer metadata parameters and stays callable as the older
zero-arg shape.
The browser-half tests exercise the published client bundles through the
module-loader shim; since the 0.1.2-alpha.5 client stack split the runtime out
of dsh-client-runtime (deleted) into dsh-client-store,dsh-client-ui-session, dsh-client-ui-chat, and the renderer-ownedSlotRegistry, the test harness re-checks registered bundle exports after a
fallback require and pins react copies with a resolve alias. The assistant-actions
slot row moved from ui-conversation to ui-chat, so the plugin's client half
pulls the ui-chat type merge too.
Either fill it in on the web "Models" page (writes DEEPSEEK_API_KEY into ~/.dsh/.credentials.yaml), or export it:
export DEEPSEEK_API_KEY=sk-...
dsh web
This repository is a standalone pnpm workspace: the plugin packages resolve the@deepseek-ai/* peer packages from npm, so building does not need a full
DeepSeek Harness checkout.
Requirements: Node ^22.19 || >=24 and pnpm.
pnpm install # installs workspace and npm dev dependencies
pnpm run build # host face (tsc + tsdown + typert artifacts), then client face
pnpm run typecheck # both compile faces
pnpm run test # vitest unit/browser tests
pnpm run verify # pre-publish gate (also runs via prepublishOnly)
The host pass regenerates lib/typert.host.js and lib/typert.remote-client.*
from the package source, keyed by each package.json name; the client pass
rebuilds lib/client.js. lib/ is git-ignored build output — do not hand-edit
it. If a typert manifest ever names a package other than its own
(TYPERT.package !== package.json name), the verify gate fails before publish.
The typert generator recognizes Remote/TypertRemoteService only from a
workspace-registered protocol package, so packages/typert-protocol vendors
the published @deepseek-ai/dsh-typert-protocol@0.1.2-alpha.5 declarations; when
the dsh dependency line moves, refresh it from the installed package.
Publishing (the bundle and both plugins share one version; prepublishOnly
runs the verify gate automatically). Use npm publish from inside each
package directory — pnpm publish fails (token resolution) and a folder
argument like npm publish packages/llm-billing is parsed as a GitHub
shorthand, which triggers a bogus git ls-remote instead of a publish. The
registry requires a token that bypasses 2FA (an npm login session token gets
E403).
Configure the token once, so it never appears in a command — put one line
in ~/.npmrc referencing an environment variable, which npm expands at
publish time:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
Then set the variable and npm publish plainly — the token is in no argument
and stays out of shell history:
export NPM_TOKEN=<your npm token>
cd packages/llm-billing && npm publish
cd packages/ui-billing && npm publish
npm publish # @rayadesu/dsh-billing bundle (repo root)
(Alternatively write the real token directly into ~/.npmrc, e.g.npm config set //registry.npmjs.org/:_authToken <TOKEN>; the commands then
carry no token either. Either way, never commit the token.)
Both packages ship sane defaults; everything below is optional.
llm-billing)| Field | Default | Meaning |
|---|---|---|
apiKeyEnv |
DEEPSEEK_API_KEY |
Credential-reference (environment-variable) name resolved per call. |
baseURL |
$DEEPSEEK_BASE_URL then https://api.deepseek.com |
Endpoint base; /user/balance is appended. |
models |
V4.1 Flash (deepseek-flash) + V4 Flash + V4 Pro + V4 Flash Vision Exp + MiMo-V2.5 series |
Advisory display rows, in presentation order; they mirror DSH's llm-deepseek catalog. |
billing.peakHours |
09:00–12:00, 14:00–18:00 (Beijing, weekdays) | Peak-hour windows, applied weekdays (Mon–Fri) only; weekends and all other hours are off-peak. |
billing.models |
Published V4 + MiMo rates | Per-model price rows (cacheHitInput, cacheMissInput, output, in CNY per 1M tokens) with an optional inclusive effectiveFrom; several rows sharing a model are its rate revisions. |
assistant/message event reports three billed token buckets: cache-hit input, cache-miss input (uncached input + cache writes), and output (including reasoning). A failed or retried assistant/attempt reports its usage only in its embedded stream; that sample is priced too (with the model of the latest request/header), a later sample for the same (turn, step) replaces the earlier one, and llm/retry-started makes the retried attempt add — the same accounting DSH's own turn-usage disclosure uses.缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z), then summed per model. Peak windows apply weekdays (Monday–Friday) only; weekends are always off-peak.turn/start..turn/end range with the same rules (located by the closing message's session id + message id), folded in one pass for the whole session and served as a messageId → cost map.session/title event (the auto-generated Chinese title or a user rename).llm-deepseek catalog — V4.1 Flash deepseek-flash, V4 Flash, V4 Pro, V4 Flash Vision Exp — plus the retired deepseek-v4.1-flash-expires-on-0910 preview id and the MiMo-V2.5 series; every flash-series route shares the same pair). Each sample takes the rate revision in effect at its own timestamp: the base schedule is the DeepSeek pricing effective August 17; the flash series (V4.1 Flash, V4 Flash, V4 Flash Vision Exp, and the retired id) was re-priced effective September 10, 12:00 Beijing time to off-peak 0.02 / 1.0 / 4.0 CNY per 1M tokens with peak at twice those prices — samples from before that instant, the V4.1 Flash route's own earlier usage included, keep the superseded rates; the V4 Pro route is announced to switch to V4.1 Flash and its rates on September 14, 12:00 Beijing time; the MiMo-V2.5 series is untouched. The weekend-off-peak rule (weekends billed at off-peak prices all day) follows the adjustment effective August 23.billing.models row.登录后即可为该插件评分和评价。
还没有人评价这个插件,来抢个沙发吧!