@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.
- package/README.md +55 -132
- package/lib/client.js +2 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,176 +1,99 @@
|
|
|
1
1
|
# @dougen/dsh-deepseek-usage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- 渲染器会给标题 id 加 `user-content-` 前缀(npm 页面、GitHub 都是),跳转链接要用带前缀的锚点 -->
|
|
4
|
+
[中文文档](#user-content-中文文档)
|
|
4
5
|
|
|
5
|
-
|
|
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
|

|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## What it does
|
|
10
11
|
|
|
11
|
-
- **Balance
|
|
12
|
-
- **Unit
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
+
### Install
|
|
34
21
|
|
|
35
|
-
|
|
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
|
-
|
|
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
|
|
29
|
+
dsh plugin --profile web remove @dougen/dsh-deepseek-usage
|
|
47
30
|
```
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
Then restart `dsh web`.
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
### A blank card means the balance could not be read
|
|
52
35
|
|
|
53
|
-
|
|
36
|
+
The card then shows the reason instead of numbers:
|
|
54
37
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
90
|
-
-
|
|
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
|
-
|
|
52
|
+
---
|
|
96
53
|
|
|
97
|
-
|
|
98
|
-
- 每次官方同步成功都会**合并回写**本文件:官方页列出的模型覆盖同名条目,你自己加的条目保留。
|
|
99
|
-
- 手改后无需重启(每次请求会比对文件 mtime),改完点一下卡片即生效。
|
|
100
|
-
- 文件损坏时不会覆盖你的内容,会继续用上一次的价格并写日志;修好后自动重载。
|
|
101
|
-
(卡片上不会显示该路径——卡片只显示余额、档单价、峰谷圆点与切换倒计时。)
|
|
54
|
+
## 中文文档
|
|
102
55
|
|
|
103
|
-
|
|
56
|
+
一个 **DeepSeek Harness(DSH)网页界面**的侧边栏插件:不离开 DSH 就能知道**账号还剩多少钱**、**当前的模型现在什么价**。
|
|
104
57
|
|
|
105
|
-
|
|
58
|
+
### 这个插件做什么
|
|
106
59
|
|
|
107
|
-
-
|
|
108
|
-
-
|
|
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
|
-
|
|
111
|
-
- 网络或接口异常:卡片会置空并只显示错误文字,点击卡片可重试。
|
|
66
|
+
### 安装、卸载与常见错误
|
|
112
67
|
|
|
113
|
-
|
|
68
|
+
安装:
|
|
114
69
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
129
|
-
npm run check:pricing # 联网抓取官方页并打印解析结果;失败时退出码非 0
|
|
77
|
+
dsh plugin --profile web remove @dougen/dsh-deepseek-usage
|
|
130
78
|
```
|
|
131
79
|
|
|
132
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
84
|
+
- **没有配置 `DEEPSEEK_API_KEY`**:在 DSH 设置里配置后重启。
|
|
85
|
+
- **余额接口或网络失败**:点击卡片重试。
|
|
86
|
+
- **`DEEPSEEK_BASE_URL` 指向第三方中转**:官方余额接口 `https://api.deepseek.com/user/balance` 可能不可用或返回异常,本插件只支持 DeepSeek 官方平台。
|
|
141
87
|
|
|
142
|
-
|
|
143
|
-
# 结构时才需要动 parseFlatPrices())
|
|
144
|
-
node lib/pricing-remote.js --check --html page.html --json
|
|
88
|
+
价格与官方不一致时:
|
|
145
89
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# 它只用于首次运行生成 pricing.json)
|
|
149
|
-
```
|
|
90
|
+
- 价格来自本地价格表 `<DSH_HOME>/dsh-deepseek-usage/pricing.json`;插件在每日首次请求和点击卡片时从官方价格页刷新并回写该文件,手改这份表重启后即生效,不用改代码。
|
|
91
|
+
- 当前模型不在价格表里时,按 `deepseek-chat` 的价格显示。
|
|
150
92
|
|
|
151
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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",
|