Skip to content
dsh.fish
Bundle

dsh-usage-blance

DSH Web 插件:在对话框下方显示 DeepSeek API 用量与余额(本月消费 / 本月日均 / 昨日消费 / 今日消费 / 账户余额),点击账单行打开控制面板配置 userToken。Billing strip below the chat dialog for the DeepSeek Harness web GUI.

Source
zhou-yihang
stars
1 stars
License
MIT
Updated
Updated 10 days ago

Readme

# dsh-usage-blance

A plugin for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) **web GUI** that monitors your **DeepSeek API usage and balance** in a billing strip pinned **directly below the chat dialog** (the `conversation.input.dock` slot, above the input box).

`中文说明见下文。`

## Features

- Shows five figures in order: **本月消费 · 本月日均 · 昨日消费 · 今日消费 · 账户余额**.
- **Click any billing row** to open the **control panel**: a `userToken` input with save / clear, the current token status, and step-by-step instructions for finding the token.
- **Glass control panel**: the billing strip is frosted glass (translucent + backdrop blur) by default; the control panel lets you toggle it and tune 透明度 (opacity) / 模糊 (blur) / 饱和度 (saturation) live — preferences persist in `localStorage`.
- **Strip position**: place the strip **above** (default, `conversation.input.dock`) or **below** the input box (`conversation.composer.dock`, inside the composer card) — one radio click, persisted in `localStorage`.
- **Two-column control panel**: userToken on the left, 主题 (position + glass) on the right; stacks vertically on narrow screens.
- **Balance alert**: set a warning threshold in the control panel; when the balance drops to ≤ the threshold, the 账户余额 value gets a red `!` and a **充值** button appears under the refresh timestamp, linking to the official top-up page (https://platform.deepseek.com/top_up).
- Balance comes from the official public endpoint `GET https://api.deepseek.com/user/balance` (authenticated with `DEEPSEEK_API_KEY`); usage comes from the platform dashboard endpoint `https://platform.deepseek.com/api/v0/usage/by_api_key/cost` with Beijing-time (UTC+8) windows — the same source the platform page's 今天/昨天/本月 filters use (authenticated with the platform `userToken` — the official API does not expose usage queries).
- Auto-refreshes every 60 s plus a manual refresh button; per-row error states (missing key, missing/expired token, network failure) with hover hints.
- Follows the app's light/dark theme (`--dsw-*` tokens).
- Secrets never leave your machine: the browser only talks to local routes registered by the host half.

## Screenshots

Billing strip above the input box (本月消费 · 本月日均 · 昨日消费 · 今日消费 · 账户余额):

![billing strip above the input box](docs/screenshot-billing-strip.png)

Billing strip below the input box (position switchable in the control panel):

![billing strip below the input box](docs/screenshot-billing-strip-below.png)

Control panel (opens when clicking any billing row): userToken input, save / clear, and the retrieval guide:

![userToken control panel](docs/screenshot-control-panel.png)

## Install

Requires the DSH CLI and [pnpm](https://pnpm.io/installation).

### From GitHub

```sh
# clone and install from the checkout:
git clone https://github.com/zhou-yihang/dsh-usage-blance.git
cd dsh-usage-blance
dsh plugin --profile web add .
```

The package declares `dsh.bundle`, so `dsh plugin` automatically adds it to the profile's bundle layers (no manual patch editing). Then:

1. Restart the web app: `dsh web` (bundle layers are read at boot).
2. Open http://127.0.0.1:3080 and refresh the page.
3. The billing strip appears below the chat dialog.

> Manual alternative: install the package into the profile's `node_modules` and add a loader entry to `~/.dsh/profiles/web/cordis.patch.yml`:
>
> ```yaml
> - insert:
>     - id: dsh-usage-blance
>       name: dsh-usage-blance
> ```

## Configuration

### userToken (usage figures)

The four usage figures need the platform session token. Click any billing row to open the control panel, then:

1. Sign in to [platform.deepseek.com](https://platform.deepseek.com).
2. Press `F12` to open DevTools → **Console**, paste and run:

   ```js
   JSON.parse(localStorage.getItem('userToken')).value
   ```

   Copy the printed string.

3. Or: **Application** → **Local Storage** → click `https://platform.deepseek.com` → find the `userToken` entry and copy its `value` field.
4. Paste it into the control panel input and click **保存**.

The token is validated immediately and saved to `$DSH_HOME/storages/dsh-usage-blance.json` (local machine only). When the token expires (e.g. after signing out), the usage rows show the error and you can renew it from the same panel.

### DEEPSEEK_API_KEY (balance)

The plugin reads the **same API key the harness already uses**: `DEEPSEEK_API_KEY`, resolved from the launching environment or `~/.dsh/.credentials.yaml` (set it on the **Settings → Models** page). No API key → the balance row shows the missing-key hint; the usage figures still work with a valid userToken.

## How it works

| Part | File | What it does |
|---|---|---|
| Host half | `lib/index.js` | Cordis plugin (`inject: webServer`) registering `GET /api/dsh-usage/overview` (balance + month usage in one call) and `GET/POST/DELETE /api/dsh-usage/token` (token status / save+validate / clear). Balance and usage are cached for 60 s. |
| Browser half | `lib/client.js` | `dsh.client` web bundle registering the billing strip into the `conversation.input.dock` slot; polls the overview every 60 s; clicking a row opens the control panel. |
| Composition | `cordis.patch.yml` | The `dsh.bundle` patch layer that inserts the loader entry. |
| Tests | `test/` | `npm test` runs the host parsing/state unit tests; `node test/smoke.mjs` boots the host half against mocked services and exercises the real route handlers; `node test/client.repro.mjs` renders the browser half under jsdom + react-dom and simulates a row click (panel opens, strip survives). |

### Local routes

| Route | Purpose |
|---|---|
| `GET /api/dsh-usage/overview` | `{ ok, at, tokenConfigured, balance, balanceError, usage, usageError }` |
| `GET /api/dsh-usage/token` | `{ ok, configured, masked }` (the full token is never returned) |
| `POST /api/dsh-usage/token` | Save (`{ "token": "..." }`) and validate; empty token clears |
| `DELETE /api/dsh-usage/token` | Clear the saved token |

## Data sources & privacy

- Balance: `GET https://api.deepseek.com/user/balance` — official public API, `Authorization: Bearer <DEEPSEEK_API_KEY>`.
- Usage: `GET https://platform.deepseek.com/api/v0/usage/by_api_key/cost?start=<sec>&end=<sec>&tz=<offset>` — a **private dashboard endpoint** (may change without notice), `Authorization: Bearer <userToken>`. This is the same source the platform usage page's 今天/昨天/本月 filters aggregate.
- Windows are anchored to **Beijing time (UTC+8)**: `start`/`end` are Beijing midnights in epoch seconds and `tz=28800` buckets the response by Beijing calendar days — the plain `/usage/cost?month=&year=` endpoint keys its day rows by UTC, which would shift each day's 00:00–08:00 usage into the previous row.
- Month figures come from one month window (daily buckets): 本月消费 = sum of all buckets; 本月日均 = 本月消费 ÷ 本月已过天数; 昨日/今日 = their Beijing-day buckets (missing data counts as zero).
- Neither the API key nor the userToken ever reaches the browser beyond the local routes above; the API key is read host-side per request and the userToken is stored in plain text under `$DSH_HOME/storages/` (protect that directory accordingly).
- UI preferences (strip position, glass settings, balance alert threshold) are persisted both in browser `localStorage` and in `$DSH_HOME/storages/dsh-usage-blance.json`, so they survive browser restarts and storage clears.

## Development

```sh
git clone https://github.com/zhou-yihang/dsh-usage-blance.git
cd dsh-usage-blance
npm test        # 单元测试(node --test test/host.test.mjs)
node test/smoke.mjs        # 宿主侧 mock 全链路
node test/client.repro.mjs # 浏览器侧 jsdom 点击复现
# install locally and test in the web GUI:
dsh plugin --profile web add .
```

After changing `lib/client.js`, restart `dsh web` so the boot-graph hash (`rev`) regenerates, then hard-refresh the page.

## License

MIT

---

# 中文说明

一个给 DeepSeek Harness(DSH)**网页界面**用的插件:在**对话框正下方**(`conversation.input.dock` 插槽,输入框上方)以账单条形式监控你的 **DeepSeek API 用量与余额**。

## 功能

- 依次显示五项指标:**本月消费 · 本月日均 · 昨日消费 · 今日消费 · 账户余额**。
- **点击任意账单行**弹出**控制面板**:`userToken` 输入框(保存/清除)、当前配置状态,以及分步骤的 userToken 获取教程。
- **玻璃质感调节**:账单条默认为毛玻璃(半透明 + 背景模糊);控制面板内可开关玻璃效果,并实时调节**透明度 / 模糊 / 饱和度**,偏好保存在 `localStorage` 中。
- **账单条位置**:可放在**输入框上方**(默认,`conversation.input.dock`)或**输入框下方**(`conversation.composer.dock`,输入卡片内部),单选切换、自动保存。
- **双栏控制面板**:左侧 userToken、右侧「主题」(位置 + 玻璃质感),窄屏自动上下堆叠。
- **余额预警**:在控制面板设置预警值;余额 ≤ 预警值时,账户余额数字后出现红色感叹号,并在「更新于」下方显示**充值**按钮,直达官方充值页(https://platform.deepseek.com/top_up)。
- 余额来自官方公开接口 `GET https://api.deepseek.com/user/balance`(用 `DEEPSEEK_API_KEY` 认证);用量来自平台控制台接口 `https://platform.deepseek.com/api/v0/usage/by_api_key/cost`,按北京时间(UTC+8)窗口查询——与平台用量页"今天/昨天/本月"同一数据源(用登录后拿到的平台 `userToken` 认证,官方 API 未开放用量查询)。
- 每 60 秒自动刷新,另有手动刷新按钮;每行都有独立错误态(未配置 Key、缺少/过期 token、网络失败),悬停可见原因。
- 自动跟随应用浅色/深色主题(`--dsw-*` 设计变量)。
- 密钥不出本机:浏览器只访问宿主侧注册的本地路由。

## 截图

输入框上方的账单条(本月消费 · 本月日均 · 昨日消费 · 今日消费 · 账户余额):

![输入框上方的账单条](docs/screenshot-billing-strip.png)

输入框下方的账单条(位置可在控制面板中切换):

![输入框下方的账单条](docs/screenshot-billing-strip-below.png)

控制面板(点击任意账单行打开):userToken 输入、保存/清除与获取教程:

![userToken 控制面板](docs/screenshot-control-panel.png)

## 安装

需要 DSH CLI 与 [pnpm](https://pnpm.io/installation)。

### 从 GitHub 安装

```sh
git clone https://github.com/zhou-yihang/dsh-usage-blance.git
cd dsh-usage-blance
dsh plugin --profile web add .
```

该包声明了 `dsh.bundle`,`dsh plugin` 会自动把它加进 profile 的 bundle 层(无需手动改配置)。之后:

1. 重启网页应用:`dsh web`(bundle 层在启动时读取)。
2. 打开 http://127.0.0.1:3080 并刷新页面。
3. 对话框下方即出现账单条。

> 手动方式:把包放进 profile 的 `node_modules`,并在 `~/.dsh/profiles/web/cordis.patch.yml` 中加一条:
>
> ```yaml
> - insert:
>     - id: dsh-usage-blance
>       name: dsh-usage-blance
> ```

## 配置

### userToken(四项用量)

四项用量需要平台会话令牌。点击任意账单行打开控制面板,然后:

1. 登录 [platform.deepseek.com](https://platform.deepseek.com)。
2. 按 `F12` 打开开发者工具 → **Console(控制台)**,粘贴执行:

   ```js
   JSON.parse(localStorage.getItem('userToken')).value
   ```

   复制输出的字符串。

3. 或者:**Application(应用)** → **Local Storage** → 点击 `https://platform.deepseek.com` → 找到 `userToken` 一项,复制其 `value` 字段。
4. 粘贴到控制面板输入框,点击**保存**。

保存时会立即验证 token,并存储到本机 `$DSH_HOME/storages/dsh-usage-blance.json`。token 过期(如退出登录)后,用量行会显示错误,在同一面板重新获取即可。

### DEEPSEEK_API_KEY(余额)

插件读取的正是 harness 自己在用的那个 Key:`DEEPSEEK_API_KEY`(从启动环境或 `~/.dsh/.credentials.yaml` 解析;在**设置 → 模型**页面填写)。未配置时余额行显示提示,四项用量在 userToken 有效时仍可正常显示。

## 工作原理

| 部分 | 文件 | 作用 |
|---|---|---|
| 宿主侧 | `lib/index.js` | Cordis 插件(`inject: webServer`),注册 `GET /api/dsh-usage/overview`(一次返回余额+本月用量)与 `GET/POST/DELETE /api/dsh-usage/token`(状态 / 保存并验证 / 清除)。余额与用量各缓存 60 秒。 |
| 浏览器侧 | `lib/client.js` | `dsh.client` 网页包,把账单条注册进 `conversation.input.dock` 插槽;每 60 秒轮询;点击账单行弹出控制面板。 |
| 组合层 | `cordis.patch.yml` | `dsh.bundle` 补丁层,插入加载项。 |
| 测试 | `test/` | `npm test` 跑宿主侧解析/状态单测;`node test/smoke.mjs` 用 mock 服务启动宿主侧并走通全部真实路由逻辑;`node test/client.repro.mjs` 在 jsdom + react-dom 中渲染浏览器侧并模拟点击(面板弹出、账单条不消失)。 |

### 本地路由

| 路由 | 用途 |
|---|---|
| `GET /api/dsh-usage/overview` | `{ ok, at, tokenConfigured, balance, balanceError, usage, usageError }` |
| `GET /api/dsh-usage/token` | `{ ok, configured, masked }`(绝不回传完整 token) |
| `POST /api/dsh-usage/token` | 保存(`{ "token": "..." }`)并验证;空 token 视为清除 |
| `DELETE /api/dsh-usage/token` | 清除已保存的 token |

## 数据来源与隐私

- 余额:`GET https://api.deepseek.com/user/balance`——官方公开 API,`Authorization: Bearer <DEEPSEEK_API_KEY>`。
- 用量:`GET https://platform.deepseek.com/api/v0/usage/by_api_key/cost?start=<sec>&end=<sec>&tz=<offset>`——**平台私有接口**(可能随时变更),`Authorization: Bearer <userToken>`。这正是平台用量页"今天/昨天/本月"筛选聚合的同一数据源。
- 时间窗口锚定**北京时间(UTC+8)**:`start`/`end` 为北京时间零点(epoch 秒)、`tz=28800` 按北京日历日分桶——旧的 `/usage/cost?month=&year=` 日行按 UTC 分桶,会把每天 00:00–08:00 的用量算进前一天。
- 月度指标由单次月窗口查询(按天 bucket)计算:本月消费 = 全部 bucket 之和;本月日均 = 本月消费 ÷ 本月已过天数;昨日/今日 = 对应北京日历日的 bucket(无数据记 0)。
- API Key 与 userToken 除上述本地路由外不会到达浏览器;API Key 由宿主侧每次请求时读取,userToken 以明文存于 `$DSH_HOME/storages/`(请自行保护好该目录)。
- 界面偏好(账单条位置、玻璃质感、余额预警值)同时保存在浏览器 localStorage 与 `$DSH_HOME/storages/dsh-usage-blance.json` 中,重启浏览器或清空站点数据也不会丢失。

## 开发

```sh
git clone https://github.com/zhou-yihang/dsh-usage-blance.git
cd dsh-usage-blance
npm test        # 单元测试(node --test test/host.test.mjs)
node test/smoke.mjs        # 宿主侧 mock 全链路
node test/client.repro.mjs # 浏览器侧 jsdom 点击复现
# 本地安装并在网页界面中测试:
dsh plugin --profile web add .
```

修改 `lib/client.js` 后需重启 `dsh web` 以重新生成引导哈希(`rev`),再强制刷新页面。

## 协议

MIT

Install

dsh plugin --profile web add github:zhou-yihang/dsh-usage-blance

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source