dsh-deepseek-cost-watch
Manifest validDeepSeek off-peak alert, account balance and live Session cost estimate for the DeepSeek Harness Web UI
dsh-deepseek-cost-watch
A DeepSeek Harness profile bundle that puts the DeepSeek off-peak schedule, the remaining account balance, and a live cost estimate into the Web UI.
It replaces two throw-away dynamic plugins with one permanent, installable package: no bundler, no build step, no dependencies, two small JavaScript files.
What it does
1. Off-peak badge (frame-wide overlay)
A floating card in the bottom-right corner of the app:
┌─────────────────────────────────────┐
│ ● DeepSeek off-peak – │
│ [−50%] Discount active │
│ Ends in 1h 42m 07s │
│ Balance $8.28 │
│ ─────────────────────────────────── │
│ Peak UTC: Mon–Fri 01:00–04:00 · │
│ 06:00–10:00 │
│ Local time (UTC+02:00): │
│ 03:00–06:00 · 08:00–12:00 │
│ Outside those windows every model │
│ costs half. │
└─────────────────────────────────────┘
- Live state — green (
−50%) during off-peak, amber (PEAK) during peak hours, refreshed once a second. - Countdown to the next peak/off-peak flip.
- Balance — the credit left on the DeepSeek account, straight from the official
GET /user/balanceendpoint. Hover for the granted / topped-up split and the read time; click the row to refresh it on the spot. - Schedule in both zones — the fixed UTC windows plus the same windows shifted into your
browser's timezone, including the
(+1d)marker when a window crosses midnight locally. - Collapsible — press
–to shrink it into aPEAK · 18m 04s/OFF-PEAK −50%pill; click the pill to expand it again.
2. Cost estimate (under the composer)
Beside the shipped token counter, above the composer:
- Collapsed: a coloured dot plus the estimated spend for the open Session —
● ≈ $0.1414. Green means the request you are about to make is billed off-peak, amber means peak. - Expanded (click): the full breakdown, in normal page flow, so nothing overlaps the composer or the badge:
Estimated total $0.1414
Balance $8.28
Peak (01-04, 06-10 UTC, Mon-Fri) $0.1414 · 5.95M tok
Off-peak $0.00 · 0 tok
Uncached input 114.46k tok
Cache read (hit) 5.77M tok
Output 60.35k tok
Model DeepSeek-V4.1-Flash · $0.1414
Right now peak · changes at 10:00 UTC
Estimate from the official DeepSeek rates (2026-09-15): cache hit /
cache miss / output, with off-peak at half the peak rate. Not a billed amount.
The estimate is history-aware: every billed request in the Session log is priced with the card that was in force when that request happened, so a Session that spans a tier change is split correctly between peak and off-peak instead of being priced entirely at today's rate.
3. Balance
The same figure the DeepSeek platform shows on its top-up page, without leaving the app:
- Read by the Host, never by the browser: the API key stays in the Host process and is sent
only to
api.deepseek.com. - Resolved exactly like the model route does — the stored credential (
DEEPSEEK_API_KEY, the ref the Models page writes) and, failing that, the launching environment. - Refreshed once on plugin activation, every 5 minutes, and on demand when you click the row. The Host caches an answer for 60 seconds, so several browser tabs never multiply the calls.
- Degrades instead of breaking: no credential renders
no API key, an unreachable API rendersunavailable, and both explain themselves in the tooltip. Low credit (is_available: false) turns the amount amber.
Requirements
- DeepSeek Harness with the
webprofile (a version that shipsdsh pluginanddsh.bundleprofile bundles —0.1.5-rcor later). pnpmonPATH(dsh pluginforwards to it).gitonPATHwhen installing from GitHub.
Install
From GitHub:
dsh plugin --profile web add github:Webificio/dsh-deepseek-cost-watch
From a local checkout (after replacing owner/repo with your own fork, the same command works
against your fork):
dsh plugin --profile web add /absolute/path/to/dsh-deepseek-cost-watch
Either command installs the package into the web profile and appends it to
dsh.profile.bundles automatically (the package declares dsh.bundle.patch, so dsh plugin
recognises it as a profile layer).
Then restart the profile — a bundle is composed at profile boot, so stop the running
dsh web and start it again:
dsh web
Reload the browser page. You should see the off-peak card in the bottom-right corner, and the cost line appear in the composer dock as soon as the open Session has billed at least one request.
Verify the install
cat "${DSH_HOME:-$HOME/.dsh}/profiles/web/package.json"
dsh-deepseek-cost-watch must appear in dsh.profile.bundles. If it does not, the plugin was
installed as a plain dependency; re-run the add command and read pnpm's output.
Uninstall
dsh plugin --profile web remove dsh-deepseek-cost-watch
Restart the profile afterwards. Both occupants and both Host routes disappear with the package.
How it works
One profile row, two halves of the same package:
| Half | Entry | What it contributes |
| --- | --- | --- |
| Host | lib/host.js (main) | Two read-only Fetch routes on the Connection carrier: GET /api/deepseek-cost-watch/report?sessionId=… (folds the live Session log into a cost estimate) and GET /api/deepseek-cost-watch/balance (reads the account credit with the stored key). |
| Browser | lib/client.js (exports["./client"]) | A Cordis client plugin claiming two slots: shell.overlay (the badge with the balance row) and conversation.composer.dock (the cost line). |
browser (client.js) host (host.js)
──────────────────── ──────────────
shell.overlay ─┐
off-peak badge │ in-process: local peak/off-peak math (no round trip)
balance row │
conversation. ─┤
composer.dock │
cost line ─────────┤ fetch ─────────▶ GET /api/deepseek-cost-watch/report
│ ctx.sessions.get(id).snapshotEvents()
│ → foldUsage() → JSON
└ fetch ─────────▶ GET /api/deepseek-cost-watch/balance
ctx.credentials.resolve('DEEPSEEK_API_KEY')
→ api.deepseek.com/user/balance → JSON
The fold lives on the Host on purpose: only the Session log records when each request was
billed, while the browser's tokenUsage projection carries aggregate token totals with no
timestamps. The balance is read on the Host for a different reason: the API key must never reach
the browser. The overlay's live clock needs no Host round trip at all, so it recomputes locally
every second from the published schedule.
Everything the plugin registers — both Fetch routes, the stylesheet, the balance poll, and both Slot occupants — is owned by the plugin's fiber, so stopping, updating, or removing the package withdraws all of it.
Files
dsh-deepseek-cost-watch/
├── package.json # name, exports, dsh.bundle.patch, dsh.client.platform
├── cordis.patch.yml # the one profile row this bundle inserts
├── lib/host.js # Host half: rates, schedule, session-log fold, balance, both routes
├── lib/client.js # Browser half: hand-written module-loader bundle
├── test/smoke.mjs # dependency-free checks for both halves
├── README.md
└── LICENSE
Balance, credentials and endpoints
| Setting | Default | Where |
| --- | --- | --- |
| Credential ref | DEEPSEEK_API_KEY | stored credential (Models page) or the launching environment |
| API base URL | https://api.deepseek.com | DEEPSEEK_BASE_URL, the same variable the model adapter reads |
| Balance endpoint | GET /user/balance | — |
| Host cache | 60 s | BALANCE_TTL_MS in lib/host.js |
| Browser poll | 5 min, plus click-to-refresh | BALANCE_REFRESH_MS in lib/client.js |
The key is used for exactly one request: Authorization: Bearer <key> against the configured base
URL. It is never logged, never returned by the route, and never sent to the browser.
Prices, and how to update them
The rate tables live at the top of lib/host.js as USD per 1,000,000 tokens,
written [off-peak, peak] per bucket (cache hit, cache miss, output):
export const RATE_TABLES = [
{
id: 'deepseek-flash',
name: 'DeepSeek-V4.1-Flash',
models: ['deepseek-flash', 'deepseek-v4-flash'],
cacheHit: [0.003, 0.006],
cacheMiss: [0.15, 0.3],
output: [0.6, 1.2],
},
]
RATE_AS_OFandRATE_SOURCEare printed in the expanded panel, so a stale table is visible in the UI rather than silent.- Update the numbers when DeepSeek publishes new pricing, then restart the profile.
- Only
deepseek-officialroutes are priced. Anything else is counted and reported as unpriced rather than guessed at. - The schedule (
PEAK_WINDOWS) is UTC: Monday–Friday 01:00–04:00 and 06:00–10:00, with off-peak at half price at every other hour. - The number shown is an estimate, not an invoice: it uses the list price of the configured model, while credits, discounts, or a provider-side price change are invisible to it.
Development
There is no build step. Edit lib/host.js or lib/client.js and restart the profile.
npm test # node test/smoke.mjs — no dependencies
The smoke test prices a synthetic Session log across both rate cards, exercises the report route
(200 / 400 / 404), drives the balance route against a stubbed DeepSeek API (success, cache,
?refresh, HTTP error, unreachable, missing credential), then loads the browser bundle with a
stub module loader and asserts that it registers the module id, claims both Slot occupants,
injects the stylesheet, polls the balance, and renders the badge with the remaining credit.
lib/client.js is a hand-written bundle in the Harness's client module-loader format
(window.__ModuleLoader__.load({ id, factory })), which is why the package needs no bundler.
Two details are load-bearing:
- the registered module
idmust equal the package name inpackage.json, and - the bundle must stay self-contained —
Reactcomes from the runtime's module table (require('react')), and nothing else is imported.
Limitations
- The cost line appears only for Sessions the Host still holds in memory, i.e. the conversation you have open. Cold or archived Sessions are not re-read from disk.
- The estimate covers billed assistant requests in the Session log; subagent Sessions are priced in their own conversation.
- The browser half targets the Web profile (
dsh.client.platform: "web"); TUI and headless profiles load the Host routes only. - The balance needs outbound network access to the DeepSeek API from the Host process; behind a
proxy, set
DEEPSEEK_BASE_URLto the reachable origin. When it cannot be read, the row says so and the rest of the plugin keeps working. - The balance is the account's credit, not a per-Session figure, and it is only as fresh as its last read (5 minutes, or one click).
License
MIT — see LICENSE.
Comments
Loading…
Similar plugins
by Way2LOose4
Real-time session cost preview for DeepSeek Harness: live cost readout, today usage chart, price_estimate/session_cost tools, prices kept in sync with the DeepSeek official site (peak/off-peak aware)
★ 3
MIT
JavaScript
Aug 14, 2026
dsh plugin --profile web add dsh-agent-pricingby LucienLL
DeepSeek Harness plugin: peak/off-peak price watch, live account balance with tiered low-balance alerts, and a top-up button for the main web UI
★ 0
MIT
JavaScript
Aug 26, 2026
dsh plugin --profile web add dsh-peak-price-panelby Ghost011118
DeepSeek account balance and session cost readout for the DeepSeek Harness Web GUI
★ 18
↓ 152/wk
BSD-3-Clause
JavaScript
Aug 20, 2026
dsh plugin --profile web add dsh-balance-meterby davidgereb
Cost overview, peak/off-peak hours indicator, balance on dashboard, off-peak scheduling for DeepSeek Harness.
★ 0
MIT
JavaScript
Aug 21, 2026
dsh plugin --profile web add dsh-plugin-cost-lensby TwotwoPiggy
dsh余额插件. A DeepSeek Harness plugin for real-time token tracking and highly accurate session cost estimation, featuring dynamic peak/off-peak pricing support.
★ 12
JavaScript
Sep 9, 2026
dsh plugin --profile web add dsh-balanceby Saikel-Orado-Liu
Cost tracking plugin for the DeepSeek Harness Web GUI — snapshot-anchored per-turn pricing, account balance, and live cost estimates.
★ 3
↓ 418/wk
MIT
TypeScript
Sep 10, 2026
dsh plugin --profile web add @gamegeek-saikel/dsh-cost-meter