dsh-balance-widget 0.5.3 → 0.5.5
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.en.md +17 -2
- package/README.md +17 -2
- package/lib/index.js +58 -9
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-balance-widget
|
|
2
2
|
|
|
3
|
-
[中文](README.md) | **English**
|
|
3
|
+
[中文](https://github.com/LL-cmyk-so/dsh-balance-widget/blob/main/README.md) | **English**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-balance-widget)
|
|
6
6
|
[](https://github.com/LL-cmyk-so/dsh-balance-widget)
|
|
@@ -15,7 +15,7 @@ A balance & cost widget for the [DeepSeek Harness](https://github.com/deepseek-a
|
|
|
15
15
|
|
|
16
16
|
| Sidebar card | Five-tier cost popover |
|
|
17
17
|
| --- | --- |
|
|
18
|
-
|  |  |
|
|
18
|
+
|  |  |
|
|
19
19
|
|
|
20
20
|
## How it differs from similar plugins
|
|
21
21
|
|
|
@@ -153,6 +153,12 @@ Built-in DeepSeek official peak/off-peak pricing (CNY per 1M tokens), effective
|
|
|
153
153
|
|
|
154
154
|
The official page currently lists only `deepseek-flash` and `deepseek-v4-pro`; older names (`deepseek-v4-flash`, `deepseek-v4-flash-vision-exp`, `deepseek-chat`) still work and are billed at the Flash rate. The plugin re-parses the page at startup and every 12h, and falls back to the table above only when parsing fails. Costs are **estimates**; the provider's bill is authoritative.
|
|
155
155
|
|
|
156
|
+
### Search calls (estimated)
|
|
157
|
+
|
|
158
|
+
Every **query** of a `web_search` call is a separate auxiliary model call (`@deepseek-ai/dsh-web-search-deepseek`, `deepseek-v4-flash` by default, up to `maxUses` per request). The session log records only the pre-dispatch `web/deepseek-search-llm-request` event — **no usage field** — so the real token counts exist in no local file.
|
|
159
|
+
|
|
160
|
+
The plugin therefore counts requests × a calibrated constant: ~6k cache-miss input + ~2k output tokens per search, i.e. **¥0.014** off-peak and **¥0.028** at peak (calibrated 2026-09-22 against the balance delta: 14 searches measured **¥0.0133 each**). Searches are attributed to the session, workspace and day they belong to, but each amount is an estimate (±30% order).
|
|
161
|
+
|
|
156
162
|
## Security & permission boundaries
|
|
157
163
|
|
|
158
164
|
This section is for the DSH Store / plugin audit: dependencies, runtime permissions, external services, and failure bounds.
|
|
@@ -183,6 +189,15 @@ This section is for the DSH Store / plugin audit: dependencies, runtime permissi
|
|
|
183
189
|
|
|
184
190
|
## Changelog
|
|
185
191
|
|
|
192
|
+
### v0.5.5 — README assets now render on the npm package page
|
|
193
|
+
- 🐛 **Fixed**: the screenshots and the language switch used repository-relative paths (`docs/screenshot-corner.png`, `README.en.md`). The npm package page renders the README body only and does not resolve in-repo paths, so both screenshots and the language link were broken on npm. They are now absolute URLs: screenshots via `raw.githubusercontent.com`, the language switch via a GitHub blob link
|
|
194
|
+
- 📦 **Scope**: documentation only; no code change
|
|
195
|
+
|
|
196
|
+
### v0.5.4 — search calls are now counted (previously dropped entirely)
|
|
197
|
+
- 🐛 **Fixed**: the auxiliary model calls behind `web_search` contributed nothing to cost. DSH writes only a pre-dispatch `web/deepseek-search-llm-request` event per search (no usage), so any log-derived pricing undercounts systematically — measured 2026-09-22: all 1073 searches of the day were missing. Searches are now priced as requests × a calibrated constant and attributed to their session / workspace / day and turn
|
|
198
|
+
- 📏 **Calibration**: ~6k cache-miss input + ~2k output tokens per search (¥0.014 off-peak, ¥0.028 at peak). Measured 2026-09-22 from the balance delta: 14 searches in one window, balance delta ¥0.28, minus ¥0.094 of log-derivable conversation cost → **¥0.0133 per search**
|
|
199
|
+
- ⚠️ **Scope**: the search portion is an estimate (±30% order); conversation text is still priced exactly from the logs
|
|
200
|
+
|
|
186
201
|
### v0.5.3 — weekends no longer mispriced as peak
|
|
187
202
|
- 🐛 **Fixed**: the peak/off-peak check looked only at the hour and ignored the weekday, so weekends were priced as peak (2×) during 09:00–12:00 and 14:00–18:00, up to doubling "today's cost". The official rule is **Mon–Fri** 09:00–12:00 and 14:00–18:00 (everything else, weekends included, is off-peak); Saturday and Sunday are now excluded first, using the Beijing-time weekday
|
|
188
203
|
- 📄 **Docs**: the pricing section and the peak/off-peak tooltips now state the Mon–Fri restriction
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-balance-widget
|
|
2
2
|
|
|
3
|
-
**中文** | [English](README.en.md)
|
|
3
|
+
**中文** | [English](https://github.com/LL-cmyk-so/dsh-balance-widget/blob/main/README.en.md)
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-balance-widget)
|
|
6
6
|
[](https://github.com/LL-cmyk-so/dsh-balance-widget)
|
|
@@ -15,7 +15,7 @@ DeepSeek Harness (DSH) Web GUI 的余额与成本小部件:侧边栏底部常
|
|
|
15
15
|
|
|
16
16
|
| 侧边栏卡片(左下角常驻) | 点击弹出五层级成本 |
|
|
17
17
|
| --- | --- |
|
|
18
|
-
|  |  |
|
|
18
|
+
|  |  |
|
|
19
19
|
|
|
20
20
|
## 与同类插件的区别
|
|
21
21
|
|
|
@@ -150,6 +150,12 @@ DSH 的插件配置统一放在这个文件里:
|
|
|
150
150
|
|
|
151
151
|
官方页当前只列 `deepseek-flash` 与 `deepseek-v4-pro` 两列;`deepseek-v4-flash`、`deepseek-v4-flash-vision-exp`、`deepseek-chat` 等旧名仍可调用,按 Flash 价计费。插件在启动时与每 12h 从官方页重新解析,只有解析失败才回退上表。成本为**估算值**,实际以官方账单为准。
|
|
152
152
|
|
|
153
|
+
### 搜索调用(估算)
|
|
154
|
+
|
|
155
|
+
每次 `web_search` 的**每个查询**都会被 DSH 当作一次独立的辅助模型调用(`@deepseek-ai/dsh-web-search-deepseek`,默认 `deepseek-v4-flash`,每个请求最多 `maxUses` 次)。而会话日志只记录派发前的 `web/deepseek-search-llm-request` 事件——**不含 usage 字段**,本地任何文件都查不到这些调用的真实 token 数。
|
|
156
|
+
|
|
157
|
+
插件因此按「请求次数 × 标定常数」计入:每次搜索 ≈ 6k 缓存未命中输入 + 2k 输出 token,即谷时 **¥0.014**、峰时 **¥0.028**(2026-09-22 用余额差标定:14 次搜索实测 **¥0.0133/次**)。搜索会归到它所属的会话、工作区与「今日·全部」,但单次金额是估算值(±30% 量级)。
|
|
158
|
+
|
|
153
159
|
## 安全与权限边界
|
|
154
160
|
|
|
155
161
|
本节面向 DSH Store / 插件审计,列出依赖、运行时权限、外部服务与失败边界。
|
|
@@ -180,6 +186,15 @@ DSH 的插件配置统一放在这个文件里:
|
|
|
180
186
|
|
|
181
187
|
## 版本历史
|
|
182
188
|
|
|
189
|
+
### v0.5.5 — 修复 README 在 npm 包页面上的显示
|
|
190
|
+
- 🐛 **修复**:README 的截图与语言切换此前用相对路径(`docs/screenshot-corner.png`、`README.en.md`)。npm 包页面只渲染 README 正文、不解析仓库内的相对路径,所以在 npm 上两张截图和语言链接都是坏的。现改为绝对 URL:截图走 `raw.githubusercontent.com`,语言切换走 GitHub blob 链接
|
|
191
|
+
- 📦 **范围**:仅文档,代码无变化
|
|
192
|
+
|
|
193
|
+
### v0.5.4 — 计入搜索调用(此前全部漏算)
|
|
194
|
+
- 🐛 **修复**:`web_search` 的辅助模型调用完全不计入成本。DSH 对每次搜索只写一个派发前的 `web/deepseek-search-llm-request` 事件(无 usage),所以按会话日志计价的实现系统性漏算——实测 2026-09-22 一天 1073 次搜索全部没进账。现按「请求次数 × 标定常数」计价,并归入该搜索所属的会话 / 工作区 / 当日及当轮
|
|
195
|
+
- 📏 **标定**:单次搜索 ≈ 6k 缓存未命中输入 + 2k 输出 token(谷时 ¥0.014、峰时 ¥0.028)。2026-09-22 用余额差实测:同一窗口 14 次搜索、余额差 ¥0.28、扣掉日志可算的对话成本 ¥0.094,得 **¥0.0133/次**
|
|
196
|
+
- ⚠️ **口径**:搜索部分是估算值(±30% 量级),会话正文部分仍是按日志精算
|
|
197
|
+
|
|
183
198
|
### v0.5.3 — 修复周末被误判为峰时
|
|
184
199
|
- 🐛 **修复**:峰/谷判断只看小时、不看星期,导致周末 09:00–12:00、14:00–18:00 被误判为峰时并按 2× 计价,"今日花费"最多翻倍。官方规则为**周一至周五** 09:00–12:00、14:00–18:00(其余含周末全天为空闲时段),现按北京时间的星期先行排除周六/周日
|
|
185
200
|
- 📄 **文档**:定价说明与峰谷 tooltip 补上"周一至周五"限定
|
package/lib/index.js
CHANGED
|
@@ -298,6 +298,40 @@ function priceUsage(usage, modelId, timeMs) {
|
|
|
298
298
|
return (input * table.inputMiss + hit * table.inputHit + output * table.output) / 1e6;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Estimated token footprint of one auxiliary web-search turn.
|
|
303
|
+
*
|
|
304
|
+
* DeepSeek runs every `web_search` query as its own Messages call (see
|
|
305
|
+
* @deepseek-ai/dsh-web-search-deepseek) and DSH records only the
|
|
306
|
+
* pre-dispatch `web/deepseek-search-llm-request` event — never the response
|
|
307
|
+
* usage — so the real token counts are not recoverable from any local file.
|
|
308
|
+
* These constants were calibrated on 2026-09-22 against the account balance
|
|
309
|
+
* delta (14 searches over one window: ~¥0.0133 per search at off-peak Flash
|
|
310
|
+
* rates, i.e. ~6k cache-miss input + ~2k output per search).
|
|
311
|
+
*/
|
|
312
|
+
const SEARCH_TURN_INPUT_TOKENS = 6000;
|
|
313
|
+
const SEARCH_TURN_OUTPUT_TOKENS = 2000;
|
|
314
|
+
|
|
315
|
+
/** Price one auxiliary web-search turn (estimated; see constants above). */
|
|
316
|
+
function priceSearchTurn(modelId, timeMs) {
|
|
317
|
+
const table = isPeak(new Date(timeMs ?? Date.now())) ? pricingTable[pricingKey(modelId)].peak : pricingTable[pricingKey(modelId)].offPeak;
|
|
318
|
+
return (SEARCH_TURN_INPUT_TOKENS * table.inputMiss + SEARCH_TURN_OUTPUT_TOKENS * table.output) / 1e6;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Cost of one parsed sample — a real usage sample, or an estimated search turn. */
|
|
322
|
+
function sampleCost(sample) {
|
|
323
|
+
return sample.search === true ? priceSearchTurn(sample.model, sample.time) : priceUsage(sample.usage, sample.model, sample.time);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Token totals of one parsed sample (a search turn contributes its estimate). */
|
|
327
|
+
function sampleTokens(sample) {
|
|
328
|
+
if (sample.search === true) return { input: SEARCH_TURN_INPUT_TOKENS, output: SEARCH_TURN_OUTPUT_TOKENS };
|
|
329
|
+
return {
|
|
330
|
+
input: (sample.usage.inputTokens ?? sample.usage.uncachedInputTokens ?? 0) + (sample.usage.cacheReadTokens ?? 0),
|
|
331
|
+
output: sample.usage.outputTokens ?? 0
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
301
335
|
/**
|
|
302
336
|
* Parse the official pricing page into our pricingTable shape.
|
|
303
337
|
* The page is HTML; we look for per-model peak/off-peak "per 1M tokens" rates.
|
|
@@ -543,6 +577,7 @@ async function decompressSession(path) {
|
|
|
543
577
|
function parseSessionUsages(text) {
|
|
544
578
|
const samples = [];
|
|
545
579
|
let currentModel = void 0;
|
|
580
|
+
let currentTurn = 0;
|
|
546
581
|
let cwd = void 0;
|
|
547
582
|
let title = void 0;
|
|
548
583
|
for (const line of text.split("\n")) {
|
|
@@ -558,6 +593,8 @@ function parseSessionUsages(text) {
|
|
|
558
593
|
cwd = event.cwd;
|
|
559
594
|
} else if (type === "session/title") {
|
|
560
595
|
title = event.data?.title ?? title;
|
|
596
|
+
} else if (type === "turn/start") {
|
|
597
|
+
currentTurn = event.data?.turn ?? currentTurn;
|
|
561
598
|
} else if (type === "request/header") {
|
|
562
599
|
currentModel = event.data?.header?.config?.model ?? currentModel;
|
|
563
600
|
} else if (type === "assistant/message") {
|
|
@@ -570,6 +607,15 @@ function parseSessionUsages(text) {
|
|
|
570
607
|
usage
|
|
571
608
|
});
|
|
572
609
|
}
|
|
610
|
+
} else if (type === "web/deepseek-search-llm-request") {
|
|
611
|
+
// One auxiliary search turn: no usage is recorded anywhere, so it is
|
|
612
|
+
// priced from the calibrated estimate below.
|
|
613
|
+
samples.push({
|
|
614
|
+
turn: currentTurn,
|
|
615
|
+
time: event.time ?? Date.now(),
|
|
616
|
+
model: event.data?.body?.model ?? currentModel,
|
|
617
|
+
search: true
|
|
618
|
+
});
|
|
573
619
|
}
|
|
574
620
|
}
|
|
575
621
|
return { samples, cwd, title };
|
|
@@ -585,9 +631,10 @@ function lastTurnCost(samples) {
|
|
|
585
631
|
let output = 0;
|
|
586
632
|
for (const s of samples) {
|
|
587
633
|
if (s.turn !== lastTurn) continue;
|
|
588
|
-
cost +=
|
|
589
|
-
|
|
590
|
-
|
|
634
|
+
cost += sampleCost(s);
|
|
635
|
+
const t = sampleTokens(s);
|
|
636
|
+
input += t.input;
|
|
637
|
+
output += t.output;
|
|
591
638
|
}
|
|
592
639
|
return { cost, inputTokens: input, outputTokens: output };
|
|
593
640
|
}
|
|
@@ -602,9 +649,10 @@ function todaySessionCost(samples) {
|
|
|
602
649
|
let output = 0;
|
|
603
650
|
for (const s of samples) {
|
|
604
651
|
if (s.time < dayStart || s.time >= dayEnd) continue;
|
|
605
|
-
cost +=
|
|
606
|
-
|
|
607
|
-
|
|
652
|
+
cost += sampleCost(s);
|
|
653
|
+
const t = sampleTokens(s);
|
|
654
|
+
input += t.input;
|
|
655
|
+
output += t.output;
|
|
608
656
|
}
|
|
609
657
|
return { cost, inputTokens: input, outputTokens: output };
|
|
610
658
|
}
|
|
@@ -653,9 +701,10 @@ function todayCost(sessionList, cwdFilter) {
|
|
|
653
701
|
if (cwdFilter !== void 0 && session.cwd !== cwdFilter) continue;
|
|
654
702
|
for (const s of session.samples) {
|
|
655
703
|
if (s.time < dayStart || s.time >= dayEnd) continue;
|
|
656
|
-
cost +=
|
|
657
|
-
|
|
658
|
-
|
|
704
|
+
cost += sampleCost(s);
|
|
705
|
+
const t = sampleTokens(s);
|
|
706
|
+
input += t.input;
|
|
707
|
+
output += t.output;
|
|
659
708
|
}
|
|
660
709
|
}
|
|
661
710
|
return { cost, inputTokens: input, outputTokens: output };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-balance-widget",
|
|
3
3
|
"description": "Balance & cost widget for the dsh web GUI: a sidebar footer card shows the DeepSeek account balance and today's costs; clicking opens a popover with a five-tier cost breakdown (last prompt with session name / today-this-session / today-this-workspace / today-all-workspaces), peak/off-peak status tags, term explanations, and a one-click top-up link. Zero external dependencies — works on Node 24.",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"files": [
|