bjpd6/dsh-client-ui-usage-hud ↗★ 0

dsh-client-ui-usage-hud

A DeepSeek Harness sidebar HUD: account balance, gifted balance, cumulative spend and token usage, styled after the Abyssal Maid Atelier skin. 适合需要实时监控API消费额度,且喜欢深海女仆皮肤风格的用户。

패키지
dsh-client-ui-usage-hud
호환성
미검증
버전
0.1.0
라이선스
MIT
최근 업데이트
2026. 10. 4.

설치

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:bjpd6/dsh-client-ui-usage-hud

dsh-client-ui-usage-hud

A sidebar HUD for DeepSeek Harness (DSH) that shows your account balance, gifted balance, cumulative spend and token usage — always visible in the left sidebar foot.

Styled after the Abyssal Maid Atelier skin (@smalltailqwq/dsh-client-ui-skin-maid-atelier, 深海女仆工坊): navy, gold and porcelain, Georgia serif, lace-edged glass panel. It reads that skin's own published CSS variables and falls back to the same values when the skin is not active, so it looks right either way.

                            ┌──────────────────────────────────────┐
   footer trigger  →  🐋 ¥16.8287            │ 深海账户 · Token 用量        刷新 │
                       14,096万 tok          │ 账户余额              ¥16.8287 │
                                             │ 赠送余额               ¥0.0000 │
                                             │ Token 总用量    14,096万 17 会话 │
                                             │ 最近一次用量        39.3万 tok T4 │
                                             │   未命中 173 · 命中 354,688 · 输出 285 │
                                             │ 累计消费               ¥9.3329 │
                                             │ 金额来自 DeepSeek 平台账户接口   │
                                             │                  查看官方用量 → │
                                             └──────────────────────────────────────┘

Collapsed, the trigger shrinks to a 34px round whale button.

What it shows, and where each number comes from

RowValueSource
Account balancenormal_wallets per currencyplatform GET /api/v0/users/get_user_summary
Gifted balancebonus_wallets per currencysame
Cumulative spendtotal_costs per currencysame
Total tokenssum of all four token buckets + session countsession projection cache, tokenUsage.totals
Last requestuncached input / cache read / outputsame, tokenUsage.last

Every money figure is read from the platform. Nothing is estimated, and no price table is applied.

Why this plugin has to talk to the platform itself

DSH's account service does request GET /api/v0/users/get_user_summary, but its parser projects only two of the fields:

balance: {
  path: '/api/v0/users/get_user_summary',
  parse: (value) => {
    const parsed = summary.safeParse(value);
    return {
      status: 'ready',
      value: parsed.data.normal_wallets,
      bonusWallets: parsed.data.bonus_wallets,   // total_costs is dropped right here
    };
  },
},

The platform's own docs say the same thing: "Host projects only ... normal_wallets / bonus_wallets currency/balance strings; response tokens and other fields are discarded." That is why the shipped UI never shows cumulative spend — the data is there, DSH throws it away.

So this plugin's host half obtains a platform credential and reads the same endpoint for itself, parsing total_costs. Real response:

{"code":0,"data":{"biz_code":0,"biz_data":{
  "normal_wallets":[{"currency":"CNY","balance":"16.8287497600000000"}],
  "bonus_wallets":[{"currency":"CNY","balance":"0"}],
  "total_costs":[{"currency":"CNY","amount":"9.3328969200000000"}]}}}

Amounts are decimal strings and are kept verbatim until formatting, so precision is never lost to binary floating point.

The credential API that actually works here

This is the one sharp edge worth knowing if you write something similar.

deepseekAccount.resolveToken(url) looks like the supported way to get a request credential, and DSH's docs describe it as "the only sanctioned way a host consumer obtains a request credential". But its implementation is:

async resolveToken(url) {
  const destination = new URL(url);
  if (destination.origin !== this.inferenceOrigin || /* ... */) return void 0;
  // ...
}

inferenceOrigin is https://api.deepseek.com — the inference origin. The account endpoints live on https://platform.deepseek.com. Passing the platform origin therefore returns undefined with no error, which reads exactly like "signed out".

The correct accessor for the platform origin is getPlatformSession(), which returns { origin, token, userId, requestHeaders } and "exports the stored grant only when its issuer matches platformOrigin". It also hands back the provider-owned client headers the platform expects. This plugin uses that.

Data flow

client.js ──GET /api/dsh/usage-hud──► lib/index.js
                                        ├─ deepseekAccount.getPlatformSession()
                                        │    └─ GET platform.deepseek.com
                                        │         /api/v0/users/get_user_summary
                                        │         → balance / gifted / cumulative spend
                                        └─ /storages/session_projcache/sessions/*.json
                                             record.rows.tokenUsage.val
                                             → total tokens / last request

Token totals come from the persisted projection cache, not from live sessions, so cold and archived sessions count too. The route also wraps the same response envelope the account service does (code / data.biz_code / data.biz_data) and honours the same 401 and 40003 invalidation paths, calling rejectToken when the grant is dead.

Nothing secret is exposed to the browser: the token lives only inside the host half, and the route rejects cross-site callers.

Requirements

  • DSH with the deepseek-account platform plugin enabled and an official account signed in. In API-key mode there is no platform grant, and the money rows say so instead of inventing a zero.
  • No third-party runtime dependencies. No build step — lib/*.js is hand-written, directly executable JavaScript.
  • Works in light and dark themes.

Install

A DSH bundle is a package whose package.json declares dsh.bundle.patch; DSH composes its patch layer into the profile. Install it into a profile directory.

Option A — plugin manager (uses pnpm and git)

dsh plugin --profile desktop add 

DSH enables a newly installed bundle by default. Restart DSH once.

Option B — manual (no pnpm, no git required)

  1. Copy this directory into the profile's node_modules:

    /profiles/
    

/node_modules/dsh-client-ui-usage-hud/


2. Append the package name to the profile's `package.json`:

```json
{
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "@deepseek-ai/dsh-web-app",
        "dsh-client-ui-usage-hud"
      ]
    }
  }
}
  1. Restart DSH. The loader applies this bundle's cordis.patch.yml, which inserts the row ui-usage-hud, and serves lib/client.js to the browser.

  2. Refresh the page.

Making the host half hot-reload

By default DSH watch roots are empty, so editing lib/index.js needs a restart. To iterate without one, add this plugin's directory to the hmr row in the profile's cordis.patch.yml:

- id: hmr
  name: "@deepseek-ai/dsh-hmr"
  config:
    root:
      - "/profiles/
/node_modules/dsh-client-ui-usage-hud"

lib/client.js is polled by dsh-client-hmr already: a page refresh picks up changes.

Layout

package.json          # dsh.bundle.patch + dsh.client (platform: web)
cordis.patch.yml      # insert: ui-usage-hud
lib/index.js          # host half: the GET /api/dsh/usage-hud route
lib/client.js         # browser half: the sidebar widget
tests/client-test.mjs # renders the widget with a minimal React hooks shim
tests/host-e2e.mjs    # drives the real route handler and calls the platform

The browser half is a browser artifact in DSH's lazy-CJS shape — it registers itself with window.__ModuleLoader__.load({ id, factory }) and exports apply and inject. It uses only the shared platform runtime (require("react")), mutates nothing outside its own [data-usage-hud] scope, and removes its stylesheet on disposal.

Verifying

There is no build step, so the tests run the shipped files directly: