@dougen/dsh-deepseek-usage 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +55 -132
  2. package/lib/client.js +2 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,176 +1,99 @@
1
1
  # @dougen/dsh-deepseek-usage
2
2
 
3
- A sidebar plugin for **DeepSeek Harness Web UI** that shows your **DeepSeek account balance** (large display), the **current model's unit pricing**, and the **time left until the next peak/off-peak switch** — with a peak/off-peak billing indicator. UI language follows the DSH locale (Chinese UI shows Chinese, other languages show English).
3
+ <!-- 渲染器会给标题 id 加 `user-content-` 前缀(npm 页面、GitHub 都是),跳转链接要用带前缀的锚点 -->
4
+ [中文文档](#user-content-中文文档)
4
5
 
5
- DeepSeek **余额侧边栏插件**(DeepSeek Harness Web UI):在 DSH **网页界面**的侧边栏底部常驻显示你的 **DeepSeek 账号余额**(放大显示)、**当前模型档单价**,以及**距下次峰谷切换的倒计时**,并提供高峰/空闲计费时段指示灯。界面语言跟随 DSH 语言设置(中文界面显示中文,其他语言显示英文)。
6
+ A sidebar plugin for the **DeepSeek Harness (DSH) Web UI** that answers two questions without leaving the app: **how much balance is left**, and **what the model costs right now**.
6
7
 
7
8
  ![DeepSeek usage card / 用量卡片](https://cdn.jsdelivr.net/npm/@dougen/dsh-deepseek-usage/screenshot.png)
8
9
 
9
- ## Features / 功能
10
+ ## What it does
10
11
 
11
- - **Balance**: `¥ 110.00` large display (symbol follows account currency: `¥` CNY / `$` USD)
12
- - **Unit pricing**: `→|1.5 ⚡0.05 |→4.5` (input / input cache / output, bottom-right of the card; switches with the active model and peak/off-peak window — **updates instantly when you switch models**)
13
- - **Editable price table**: prices live in a JSON file (`<DSH_HOME>/dsh-deepseek-usage/pricing.json`), not in code. The plugin reads it at startup, so the card prices correctly **immediately after a restart**, and a successful official sync is written back into it. Hand-edit it for a gateway/manual pricing; changes apply without restarting (`autoUpdate: false` also turns the automatic sync off completely)
14
- - **Auto price sync**: prices are re-fetched from the official DeepSeek pricing page on first use each day and when you click the card; the result is merged back into the price table (official entries overwrite same-named ones, your own entries are kept). The page is parsed **structurally (HTML table cells)**, not by matching surrounding text, so official layout/rewording changes no longer break it; a plain-text strategy remains as a fallback
15
- - **Rename-proof model mapping**: when DeepSeek renames a model (e.g. `deepseek-v4-flash` → `deepseek-flash`) the old id is charged at the current model's price instead of silently showing an outdated price. (The card stays quiet about this — see *FAQ* below.)
16
- - **Peak indicator & countdown**: top-right dot — orange during peak hours (Mon–Fri 9:00–12:00, 14:00–18:00 Beijing Time), green otherwise. Beside the dot a countdown shows the time left until the next peak/off-peak switch, in English units (`2d 15h`, `5h 47m`, `23m`).
17
- - **Collapsed sidebar**: with the sidebar collapsed to the icon rail, this plugin shows the peak/off-peak dot alone (clicking it still refreshes); the full card returns when the sidebar expands.
18
- - **Auto refresh**: every 5 minutes; click the card to refresh instantly.
19
- - **i18n**: UI copy follows the DSH locale (Chinese / English)
20
- - Follows the app light/dark theme
12
+ - **Balance** in the sidebar footer: your DeepSeek account balance (the symbol follows the account currency: `¥` / `$`).
13
+ - **Unit prices of the active model**: input / input cache / output for the billing window in effect right now. They change when you switch models and when the peak/off-peak window flips.
14
+ - **Peak / off-peak dot with a countdown**: orange during peak hours (Mon–Fri 9:00–12:00 and 14:00–18:00 Beijing Time), green otherwise; next to it, the time left until the next switch (`2d 15h`, `5h 47m`, `23m`).
15
+ - **Collapsed sidebar** (56 px icon rail): only the dot stays, so the rail keeps its shape.
16
+ - Refreshes every 5 minutes, and immediately when you click the card or switch models. Follows the app light/dark theme and the DSH language (Chinese / English).
21
17
 
22
- - **余额**:`¥ 110.00` 大字显示(符号随账号币种自动切换:人民币 `¥` / 美元 `$`)
23
- - **档单价**:`→|1.5 ⚡0.05 |→4.5`(输入 / 输入缓存 / 输出,位于卡片右下角;随当前启用模型与峰谷时段自动切换,**切换模型时立即同步**)
24
- - **可编辑的价格表**:价格不再写在代码里,而是存在 JSON 文件 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`。插件启动时先读它,所以**重启后卡片立刻就是正确价格**;官方同步成功后会自动回写到这份表里。手改该文件即可用于中转/手动计价,改完**无需重启**(`autoUpdate: false` 还能彻底关掉自动同步)
25
- - **单价自动同步**:每日首次使用或点击卡片时,自动从官方价格页拉取最新档单价,并把结果**合并回写**到价格表(官方条目覆盖同名项,你自己加的条目保留)。页面解析采用**结构化表格解析**(直接读 HTML 表格单元格),不再依赖"正文里某段文字长什么样",官方改版/改文案不会失效;另保留纯文本兜底策略
26
- - **抗改名映射**:官方给模型改名(如 `deepseek-v4-flash` → `deepseek-flash`)时,旧名按现名价格计费,不会静默显示旧价(卡片不为此弹提示,见下方 FAQ)
27
- - **峰谷指示灯与倒计时**:卡片右上角圆点,高峰(周一至周五 9:00–12:00、14:00–18:00 北京时间)橙色,空闲绿色;圆点旁显示距下次峰谷切换的倒计时,统一英文单位(`2d 15h`、`5h 47m`、`23m`)
28
- - **侧边栏折叠**:折叠成图标栏时只显示峰谷指示灯(点击它同样可刷新),卡片在侧边栏展开时回来
29
- - **自动刷新**:每 5 分钟一次;点击卡片任意位置立即刷新
30
- - **多语言**:界面文案跟随 DSH 语言(中文 / English)
31
- - 跟随应用明暗主题
18
+ ## Install, uninstall, common errors
32
19
 
33
- ## Supported scope / 支持范围
20
+ ### Install
34
21
 
35
- > ⚠️ This plugin **only supports DeepSeek's official platform** balance display: balance comes from the official API `GET https://api.deepseek.com/user/balance` (using the `DEEPSEEK_API_KEY` configured in DSH). Pointing `DEEPSEEK_BASE_URL` at a third-party proxy/gateway may break or return unexpected results for the balance endpoint — **not guaranteed to be compatible**. Use an official DeepSeek platform account.
22
+ 1. Configure `DEEPSEEK_API_KEY` in DSH (model settings).
23
+ 2. `dsh plugin --profile web add @dougen/dsh-deepseek-usage`
24
+ 3. Restart `dsh web` and open the URL it prints — the card sits at the bottom of the sidebar, below the Cordis panel button.
36
25
 
37
- > ⚠️ 本插件**仅支持 DeepSeek 官方平台**的余额显示:余额来自 DeepSeek 官方 API `GET https://api.deepseek.com/user/balance`(使用 DSH 中已配置的 `DEEPSEEK_API_KEY`)。通过 `DEEPSEEK_BASE_URL` 指向第三方中转/代理时,余额接口可能不可用或返回异常,**不保证兼容**,请使用 DeepSeek 官方平台账号。
38
-
39
- ## Install / 安装
40
-
41
- Requires the DSH CLI. Install from npm:
42
-
43
- 需要 DSH CLI,从 npm 安装:
26
+ ### Uninstall
44
27
 
45
28
  ```sh
46
- dsh plugin --profile web add @dougen/dsh-deepseek-usage
29
+ dsh plugin --profile web remove @dougen/dsh-deepseek-usage
47
30
  ```
48
31
 
49
- Restart the web app (`dsh web`), open http://127.0.0.1:3080 , and the usage card appears at the bottom of the sidebar (below the Cordis panel button).
32
+ Then restart `dsh web`.
50
33
 
51
- 安装后重启网页应用:`dsh web`,打开 http://127.0.0.1:3080 ,侧边栏底部(Cordis 面板按钮下方)即出现用量卡片。
34
+ ### A blank card means the balance could not be read
52
35
 
53
- ## Data sources / 数据来源
36
+ The card then shows the reason instead of numbers:
54
37
 
55
- | Data | Source |
56
- |---|---|
57
- | Balance | Official DeepSeek API `GET /user/balance` (uses `DEEPSEEK_API_KEY` configured in DSH) |
58
- | Unit pricing | Local price table `<DSH_HOME>/dsh-deepseek-usage/pricing.json` (read at startup; editable). Refreshed from the official page ([api-docs.deepseek.com/zh-cn/quick_start/pricing](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/)) on first daily use or on card click, then written back. Parsed from the HTML **table cells** (text fallback available); the built-in snapshot in code is only the seed for the very first run |
59
- | Peak window | Built-in official rule: Mon–Fri 9:00–12:00, 14:00–18:00 Beijing Time (weekends are always off-peak). The card shows it as a coloured dot plus a countdown to the next switch |
38
+ - **`DEEPSEEK_API_KEY` is not configured** — set it in DSH settings and restart.
39
+ - **The balance request or the network failed** — click the card to retry.
40
+ - **`DEEPSEEK_BASE_URL` points at a third-party gateway** — the official balance endpoint `https://api.deepseek.com/user/balance` may be unavailable or answer differently; only DeepSeek's official platform is supported.
60
41
 
61
- | 数据 | 来源 |
62
- |---|---|
63
- | 余额 | DeepSeek 官方 API `GET /user/balance`(使用 DSH 中已配置的 `DEEPSEEK_API_KEY`) |
64
- | 档单价 | 本地价格表 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`(启动即读取,可手动编辑);每日首次使用或点击卡片时从官方价格页 [api-docs.deepseek.com/zh-cn/quick_start/pricing](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) 同步并**回写**该表。官方页解析**读取 HTML 表格单元格**(另有纯文本兜底策略);代码里的内置快照只在首次运行时用于生成价格表 |
65
- | 峰谷时段 | 内置官方口径:周一至周五 9:00–12:00、14:00–18:00(北京时间),周末恒为空闲;界面以「指示灯 + 距下次切换倒计时」呈现 |
42
+ ### Prices differ from the official page
66
43
 
67
- Make sure `DEEPSEEK_API_KEY` is configured in DSH (Model settings page) before use.
44
+ - Prices come from the local price table `<DSH_HOME>/dsh-deepseek-usage/pricing.json`. The plugin refreshes that file from the official pricing page on the first request of each day and when you click the card, then reads it back — a hand-edited table works after a restart, no code change needed.
45
+ - If the active model is not in the table, the card falls back to `deepseek-chat` pricing.
68
46
 
69
- 使用前请确保 DSH 已配置 `DEEPSEEK_API_KEY`(可在 DSH 设置中的模型页面配置)。
70
-
71
- ## Price table / 价格表
72
-
73
- `<DSH_HOME>/dsh-deepseek-usage/pricing.json` (default `~/.dsh/dsh-deepseek-usage/pricing.json`; override with `DEEPSEEK_USAGE_PRICE_FILE`). Created from the built-in snapshot on first run, then maintained by the plugin:
74
-
75
- ```jsonc
76
- {
77
- "autoUpdate": true, // false = manual mode: never fetch, use this table as-is
78
- "updatedAt": 1789090582880, // last write (ms or ISO string)
79
- "updatedFrom": "https://api-docs.deepseek.com/zh-cn/quick_start/pricing/",
80
- "strategy": "table", // table | flat | seed
81
- "aliases": { "deepseek-v4-flash": "deepseek-flash" }, // old id → current id
82
- "models": {
83
- // input / cacheRead / output are [peak, off-peak] in CNY per 1M tokens
84
- "deepseek-flash": { "input": [2, 1], "cacheRead": [0.04, 0.02], "cacheWrite": 0, "output": [8, 4] }
85
- }
86
- }
87
- ```
47
+ ## How it works
88
48
 
89
- - Full-line `//` comments are allowed; a bare `{"model-id": {...}}` map (no `models` wrapper) also works.
90
- - Every successful official sync **merges** into this file: official entries overwrite same-named ones, your own entries stay.
91
- - Edits are picked up without restarting the plugin (the file's mtime is checked per request).
92
- - If the file is corrupt, the plugin keeps the last good prices and **never overwrites** it — fix it and it reloads.
93
- (The card itself never shows this path — it only shows your balance, the unit prices, the peak dot and the countdown.)
49
+ - The **host half** (`lib/index.js`, `lib/pricing*.js`) registers `GET /api/deepseek-usage`. Per request it reads the API key from DSH credentials, asks the official balance API, resolves the active model's price from the price table (syncing it from the official pricing page at most once a day, or right away when you click the card), and answers with balance, tier prices, the peak flag and the next switch time.
50
+ - The **browser half** (`lib/client.js`) renders that answer into the `sidebar.footer.action` slot, polls every 5 minutes, and refreshes on click or on a model change. The peak state and the countdown come from the same answer, so the card itself holds no pricing logic.
94
51
 
95
- 价格表文件位于 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`(默认 `~/.dsh/dsh-deepseek-usage/pricing.json`,可用 `DEEPSEEK_USAGE_PRICE_FILE` 覆盖)。首次运行由内置快照生成,之后由插件维护:
52
+ ---
96
53
 
97
- - 允许**整行** `//` 注释;也接受省略 `models` 外壳的简写形式 `{"deepseek-flash": {...}}`。
98
- - 每次官方同步成功都会**合并回写**本文件:官方页列出的模型覆盖同名条目,你自己加的条目保留。
99
- - 手改后无需重启(每次请求会比对文件 mtime),改完点一下卡片即生效。
100
- - 文件损坏时不会覆盖你的内容,会继续用上一次的价格并写日志;修好后自动重载。
101
- (卡片上不会显示该路径——卡片只显示余额、档单价、峰谷圆点与切换倒计时。)
54
+ ## 中文文档
102
55
 
103
- ## FAQ / 常见问题
56
+ 一个 **DeepSeek Harness(DSH)网页界面**的侧边栏插件:不离开 DSH 就能知道**账号还剩多少钱**、**当前的模型现在什么价**。
104
57
 
105
- **Balance shows "—"? / 余额显示"—"?**
58
+ ### 这个插件做什么
106
59
 
107
- - `DEEPSEEK_API_KEY` is not configured — configure it in DSH settings and restart.
108
- - Network or API error — the card is blanked and shows only the error text; click it to retry.
60
+ - **余额**:侧边栏底部显示 DeepSeek 账号余额(符号随账号币种 `¥` / `$`)。
61
+ - **当前模型的档单价**:输入 / 输入缓存 / 输出,按此刻生效的计费时段显示;切换模型、跨过峰谷切换点时会跟着变。
62
+ - **峰谷指示灯 + 倒计时**:高峰(周一至周五 9:00–12:00、14:00–18:00 北京时间)橙色,其余时间绿色;指示灯旁给出距下次切换的剩余时间(`2d 15h`、`5h 47m`、`23m`)。
63
+ - **侧边栏折叠时**只剩这颗指示灯,图标栏保持原样。
64
+ - 每 5 分钟自动刷新;点击卡片或切换模型时立即刷新。跟随应用明暗主题与 DSH 语言(中文 / English)。
109
65
 
110
- - 未配置 `DEEPSEEK_API_KEY`:在 DSH 设置中配置后重启。
111
- - 网络或接口异常:卡片会置空并只显示错误文字,点击卡片可重试。
66
+ ### 安装、卸载与常见错误
112
67
 
113
- **Price differs from the official one? / 单价与官方价格不一致?**
68
+ 安装:
114
69
 
115
- - The unit price switches with the **active model** and the **peak/off-peak window**. Prices come from the local price table (last successful official sync, or your own edits); the plugin re-syncs the official page on first daily use or on card click and writes the result back into that table.
116
- - **The card deliberately shows no plugin internals**: no sync state, no pricing source, no price-table path. It shows your balance, the current unit prices, the peak dot and the countdown; **click it to refresh**.
117
- - Want to know where a price comes from? Open `<DSH_HOME>/dsh-deepseek-usage/pricing.json` (see *Price table* above) — official entries are merged there on every successful sync.
118
- - **After every `dsh` restart the card used to say "prices not synced (built-in table)"** — that was a race: sync state only lived in memory, so the first request after a restart was always answered from the built-in table. Since 0.7.0 the price table is persisted and read at startup, so restarts show real prices right away.
70
+ 1. 在 DSH 设置(模型页)里配置 `DEEPSEEK_API_KEY`。
71
+ 2. `dsh plugin --profile web add @dougen/dsh-deepseek-usage`
72
+ 3. 重启 `dsh web`,打开它打印的地址:卡片位于侧边栏底部、Cordis 面板按钮下方。
119
73
 
120
- - 单价按**当前启用模型**与**峰谷时段**自动切换;价格取自本地价格表(上次成功同步到的官方价,或你自己改的价)。插件在每日首次使用或点击卡片时重新同步官方页,并把结果回写到该表。
121
- - **卡片刻意不显示任何插件内部信息**:不显示同步状态、计价来源、价格表路径。卡片上只有余额、当前档单价、峰谷圆点与切换倒计时;**点击卡片即刷新**。
122
- - 想知道某个价从哪来?打开 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`(见上文「价格表」)——每次成功同步都会把官方价合并写进去。
123
- - **以前每次 `dsh` 重启后卡片都会提示"价格未同步(用内置表)"**:那是竞态——同步状态只存在内存里,重启后的第一次请求只能用内置表回答。0.7.0 起价格表落盘并在启动时读取,重启后立即显示真实价格。
124
-
125
- ## Development / 开发与自检
74
+ 卸载:
126
75
 
127
76
  ```sh
128
- npm test # 解析 + 定价 + 价格表 + 卡片布局 + 宿主路由(node --test)
129
- npm run check:pricing # 联网抓取官方页并打印解析结果;失败时退出码非 0
77
+ dsh plugin --profile web remove @dougen/dsh-deepseek-usage
130
78
  ```
131
79
 
132
- 模块划分:`lib/index.js` 宿主路由(同步节流、余额、状态透出)· `lib/pricing.js` 三层价格
133
- (remote/file/seed)· `lib/pricing-store.js` 价格表读写(可编辑的 pricing.json)·
134
- `lib/pricing-remote.js` 官方页抓取与解析 · `lib/client.js` 侧边栏卡片。
80
+ 然后重启 `dsh web`。
135
81
 
136
- 官方页再次改版时(自检失败 / 卡片提示"价格未同步"):
82
+ 卡片空白说明余额没读到,此时卡片会写明原因:
137
83
 
138
- ```sh
139
- # 1. 存档新页面(解析失败也会存下来)并看解析结果
140
- node lib/pricing-remote.js --check --save page.html
84
+ - **没有配置 `DEEPSEEK_API_KEY`**:在 DSH 设置里配置后重启。
85
+ - **余额接口或网络失败**:点击卡片重试。
86
+ - **`DEEPSEEK_BASE_URL` 指向第三方中转**:官方余额接口 `https://api.deepseek.com/user/balance` 可能不可用或返回异常,本插件只支持 DeepSeek 官方平台。
141
87
 
142
- # 2. 离线复现、迭代解析策略(结构化策略通常无需改动,只有官方改用非表格
143
- # 结构时才需要动 parseFlatPrices())
144
- node lib/pricing-remote.js --check --html page.html --json
88
+ 价格与官方不一致时:
145
89
 
146
- # 3. 把新页面存为 test/fixtures/official-pricing-zh.html,跑 npm test 防回归
147
- # (若解析出的模型集合变了,再同步更新 lib/pricing.js 里的内置快照——
148
- # 它只用于首次运行生成 pricing.json)
149
- ```
90
+ - 价格来自本地价格表 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`;插件在每日首次请求和点击卡片时从官方价格页刷新并回写该文件,手改这份表重启后即生效,不用改代码。
91
+ - 当前模型不在价格表里时,按 `deepseek-chat` 的价格显示。
150
92
 
151
- > 测试里的"重启"是给模块图打 `?graph=<tag>`(见 `test/host-route.test.mjs`),每个 tag
152
- > 都拿到全新模块实例;peer 依赖 `@deepseek-ai/dsh-credentials` 由 `test/stubs/` 里的
153
- > 替身接管,所以本仓库不需要 node_modules 也能跑宿主逻辑。
154
- >
155
- > DSH 沙箱内 `node --test test/` 可能因禁止子进程管道而报 `spawn EPERM`,此时用
156
- > `node --test --test-isolation=none "test/*.test.mjs"`(Node ≥ 22.8),或逐个跑
157
- > `node test/parse.test.mjs`、`node test/pricing.test.mjs`、`node test/pricing-store.test.mjs`、
158
- > `node test/client-layout.test.mjs`、`node test/host-route.test.mjs`。
159
- >
160
- > 两种偶发失败都属于环境限制,不是插件行为,逐文件运行可稳定复现通过:
161
- > - `host-route` 的"合并写回"用例依赖点击时的 `FORCED_GAP_MS`(30s)节流门;多个测试文件
162
- > 共用同一进程(`--test-isolation=none`)时那条"强制同步"可能被门挡掉。
163
- > - Windows 上原子写(写 `.tmp` 后 `rename`)可能被安全软件短暂占用而报
164
- > `EPERM ... rename`;重跑即过。
165
- >
166
- > 另:`test/host-route.test.mjs` 与 `test/pricing-store.test.mjs` 都只需要一个可写的临时目录
167
- > (`os.tmpdir()`),若该目录不可写(`mkdtemp ... EPERM`),把 `TEMP`/`TMP` 指到任意可写目录即可。
168
-
169
- ## Uninstall / 卸载
93
+ ### 简单的工作原理
170
94
 
171
- ```sh
172
- dsh plugin --profile web remove @dougen/dsh-deepseek-usage
173
- ```
95
+ - **宿主侧**(`lib/index.js`、`lib/pricing*.js`)注册 `GET /api/deepseek-usage`:每次请求读取 DSH 凭据里的 API Key、调用官方余额接口、从价格表解析当前模型档单价(每天最多同步一次官方价格页,点击卡片则立即同步),返回余额、档单价、峰谷状态与下次切换时刻。
96
+ - **浏览器侧**(`lib/client.js`)把这份响应渲染进 `sidebar.footer.action` 槽位,每 5 分钟轮询一次,点击卡片或模型变化时立即刷新;峰谷状态与倒计时同样来自这次响应,界面自身不做计价逻辑。
174
97
 
175
98
  ## License
176
99
 
package/lib/client.js CHANGED
@@ -317,17 +317,14 @@ window.__ModuleLoader__.load({
317
317
  };
318
318
 
319
319
  // 折叠态(图标栏):内容宽 36px(侧边栏 56px − 折叠态左右各 10px 内边距),
320
- // 只容得下峰谷指示灯。指示灯仍是"点击刷新"的落点。
320
+ // 只容得下峰谷指示灯,没有外框也没有底色(直接坐在侧边栏背景上)。
321
+ // 指示灯仍是"点击刷新"的落点。
321
322
  if (!wide) {
322
323
  return h("div", {
323
324
  style: {
324
325
  boxSizing: "border-box",
325
326
  flex: "none",
326
327
  padding: "4px 8px",
327
- borderRadius: 8,
328
- border: "1px solid " + C.border,
329
- background: C.bg,
330
- color: C.label,
331
328
  cursor: "pointer",
332
329
  display: "flex",
333
330
  flexDirection: "row",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougen/dsh-deepseek-usage",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "DeepSeek 余额侧边栏插件:余额(官方 API)、当前档单价与峰谷时段指示灯,支持中/英文界面。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",