@kenz1117/dsh-ui-usage-billing 1.7.2 → 1.7.4
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 +28 -16
- package/README.md +28 -16
- package/lib/index.js +194 -123
- package/lib/types/aggregate.d.ts +29 -0
- package/lib/types/config.d.ts +8 -0
- package/package.json +35 -34
package/README.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<a href="https://www.npmjs.com/package/@kenz1117/dsh-ui-usage-billing"><img alt="npm version" src="https://img.shields.io/npm/v/@kenz1117/dsh-ui-usage-billing?logo=npm"></a>
|
|
10
10
|
<a href="https://www.npmjs.com/package/@kenz1117/dsh-ui-usage-billing"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@kenz1117/dsh-ui-usage-billing?logo=npm"></a>
|
|
11
11
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/kenz1117/dsh-ui-usage-billing/actions/workflows/ci.yml/badge.svg"></a>
|
|
12
|
-
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/COMPATIBILITY.md"><img alt="DSH compat" src="https://img.shields.io/badge/DSH-0.1.2_%7E_0.2.1--rc.
|
|
12
|
+
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/COMPATIBILITY.md"><img alt="DSH compat" src="https://img.shields.io/badge/DSH-0.1.2_%7E_0.2.1--rc.3_%7C_0.1.0--rc.8%2B-blue"></a>
|
|
13
13
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/LICENSE"><img alt="License MIT" src="https://img.shields.io/github/license/kenz1117/dsh-ui-usage-billing"></a>
|
|
14
14
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/pulls"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen"></a>
|
|
15
15
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/kenz1117/dsh-ui-usage-billing?logo=github"></a>
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
<div align="center">
|
|
28
|
-
<img src="screenshots/
|
|
28
|
+
<img src="screenshots/overview.png" alt="dsh-ui-usage-billing — billing dashboard overview" width="80%">
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
### Demo GIF
|
|
32
32
|
|
|
33
|
+
The eight dashboard tabs in turn (Overview / Billing / Usage / Trends / Context / Agents / Rates / Settings):
|
|
34
|
+
|
|
33
35
|

|
|
34
36
|
|
|
35
37
|
## ✨ Why this plugin
|
|
@@ -63,24 +65,31 @@ The billing channel is detected from the current session's model: DeepSeek meter
|
|
|
63
65
|
## 📊 Dashboard
|
|
64
66
|
|
|
65
67
|
- **Sidebar trigger card**: persistent above the Settings button — a single headline figure (today / this week / this month, selectable in settings, optional day-over-day badge) with a 7-day sparkline mini-trend and a today-vs-average gauge needle; collapses to an icon button; hover reveals a quick-look card (updated-at + metric grid + top spenders / quota alerts).
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
66
71
|
- **Eight-tab dashboard**: Overview / Billing / Usage / Trends / Context / Agents / Rates / Settings — hero figures + comparisons + month projection + KPIs (hover for the metric definition) + usage heatmap, trends switchable between cost and tokens over 7/30 days, model price table, budgets (global + per model) and peak/off-peak alerts all live here; the settings page supports full-text search (zh/en keywords, empty state when nothing matches); tabs carry names only — no status badges.
|
|
67
72
|
- **Context section**: an occupancy bar plus next-request / last-request occupancy, window capacity and the composition split (system / tools / messages). The figures come from the host's official context projections; a session without one hides the whole section rather than showing a fabricated 0.
|
|
68
73
|
- **Agents section**: the agent network rebuilt from delegation relations — a parent conversation → sub-agent tree where each row carries its call count, own cost and **chain total** (itself + all descendants), which directly answers "which delegation was the most expensive".
|
|
69
74
|
|
|
70
|
-

|
|
76
|
+

|
|
77
|
+

|
|
71
78
|
- **Live cost bar**: persistent "this turn / session" cost with three positions (composer toolbar row / above / below, toolbar by default); the peak/off-peak tier & countdown section appears only when the current model involves peak/off-peak pricing; subscription low-quota chips appear at ≤20% remaining, red at ≤10%; hideable via the settings-tab toggle (display-only preference persisted locally; stats and alerts unaffected).
|
|
72
79
|
- **Peak/off-peak switch alert**: a popover plus an optional system notification before a switch; lead time / position / mode / preview configurable; copy differs by billing channel (DeepSeek price halves, Zhipu credits at 50%).
|
|
73
80
|
- **Session completion notification**: an optional system notification when a session transitions running → completed (off by default, enable it in the panel); multi-window / multi-instance dedup through the claim endpoint, so each session notifies once.
|
|
74
81
|
- **Plugin info card**: a persistent "About" card in the Settings tab — version read server-side from the package's `package.json` (single source of truth, correct on publish), author / repo / npm / license one click away.
|
|
75
82
|
|
|
83
|
+

|
|
84
|
+
|
|
76
85
|
## 💰 Billing engine
|
|
77
86
|
|
|
78
87
|
- **Provider-first grouping**: usage is grouped by the llm entry the calls actually went through (channel) — Tencent Cloud TokenHub / Token Plan / DeepSeek official / direct:<route> / unknown routes; the model brand stays as a row logo + sub-line. Official judgement follows the channel origin (`api.deepseek.com`) instead of the route name, so gateway routes named `deepseek-*` no longer count as official. `routeAliases` relocates renamed/deleted historical routes; `modelKeyAliases` binds uncatalogued model ids to catalog keys (date suffixes, org prefixes and the TokenHub short id `hy3` are recognized out of the box).
|
|
79
88
|
- **Live rate table**: models.dev fetched pricing + live-model alignment — all configured models included; peak/off-peak split (weekdays 9-12 / 14-18 peak ×2, weekends off-peak all day; history priced per official change boundaries, see "Billing details") + a live USD→CNY rate, auto-refreshed every 6 hours; the rate strip shows the last sync time with a **one-click "Sync now"** button (no host restart needed).
|
|
80
89
|
|
|
81
|
-
](screenshots/pricing.png)
|
|
82
91
|
- **Custom unit prices**: set real paid prices (miss / cache-hit / output, optional USD and off-peak columns) for uncatalogued or repriced models; bindable per relay origin; an out-of-catalog model is priced as soon as you fill it in.
|
|
83
|
-
- **Official vs third-party buckets**: the
|
|
92
|
+
- **Official vs third-party buckets**: the model cost column in the Billing tab splits official direct / third-party relay ("official x / third y" when mixed); web-search assist calls count as official.
|
|
84
93
|
- **Monthly budget + tier alerts**: budget bar ≥80% amber, over-budget red pulse; one alert per 50/80/100% crossing; a balance below the CNY threshold alerts once a day. **Per-model budgets**: set a CNY cap per top-spending model of the month (independent of the global budget), with one alert per model per 80% / 100% crossing.
|
|
85
94
|
- **Cost-spike attribution**: last-40-turn cost bars, amount at bar top, peak/off-peak background bands, >2× spikes flagged with attribution.
|
|
86
95
|
|
|
@@ -92,20 +101,23 @@ The billing channel is detected from the current session's model: DeepSeek meter
|
|
|
92
101
|
- **Declared endpoints + balance reconcile**: `declaredEndpoints` self-declares balance/quota interfaces for vendors absent from the built-in table — dot-paths only ("where the number is"), no expressions; safety bounds (single-slash absolute path, GET only, no cross-origin redirects, response-size/timeout caps, credentials from the matched provider only) are enforced by `src/declarative.ts`; a wrong path is marked `declared` with a reason. **Balance reconcile** (`reconcilePath`) cross-checks the official balance change against the local ledger, flagging drift above the threshold (0.3 CNY and >15%); top-ups / grants / currency changes reset the baseline instead of alerting, and a flat balance (subscription spend) stays silent.
|
|
93
102
|
- **Relay-site attribution & quota**: usage is grouped by `baseURL` origin — multiple keys on one relay merge into a row named by its domain; New API (`/api/status`) and Sub2API (`/v1/usage`) are auto-detected for balance and rolling quota windows, labeled "no quota" when unreadable, <20% remaining in red; recognition caches for 5 minutes (per-key fuse-breaking), and the `relay-quotas` endpoint attaches `diagnostics` for "why is my relay not showing"; project attribution prefers the workspace title.
|
|
94
103
|
|
|
95
|
-

|
|
96
105
|
|
|
97
106
|
> For the full adapter matrix per channel (detection / endpoints / credentials / troubleshooting), see [docs/adapters.md](docs/adapters.md).
|
|
98
107
|
|
|
99
108
|
## 📈 Usage visualizations
|
|
100
109
|
|
|
101
|
-
- **Session detail
|
|
102
|
-
- **
|
|
103
|
-
- **Token insights**: the daily token chart switches between two views — "Structure" (cache-miss / cache-hit / output, including reasoning) and "By model" (the toggle hides itself when snapshots lack per-day-per-model detail); hover shows the day's exact breakdown (thousand-separated); clicking a legend swatch or a model-table row focuses that model (click again to release);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- **
|
|
110
|
+
- **Session detail (Billing tab)**: sessions sorted by cost (title / project / calls / actual cost / last active); "view all N sessions" opens a full-screen viewer (substring search by title / project / id, capped at 100 rows); **clicking a session title opens that conversation in the host** (hosts without a session-selection channel show a readable notice instead of silently doing nothing).
|
|
111
|
+
- **Usage heatmap (Overview tab)**: month / half-year / year calendar heatmap (5-color scale, hover detail; the year view is ~52 weeks, GitHub-style, and the **half-year view uses 26 weeks of large cells** — half a year of intensity in one screenshot-ready chart) with a **cost / tokens metric switch**; total, active-day and streak counts on top.
|
|
112
|
+
- **Token insights (Usage tab)**: the daily token chart switches between two views — "Structure" (cache-miss / cache-hit / output, including reasoning) and "By model" (the toggle hides itself when snapshots lack per-day-per-model detail); hover shows the day's exact breakdown (thousand-separated); clicking a legend swatch or a model-table row focuses that model (click again to release); the model table breaks down input / output / reasoning / cache-hit rate / share / calls.
|
|
113
|
+
- **Tool-call ranking (Usage tab)**: call counts per tool (tokens cannot be attributed per tool, so only counts and shares), showing which tools dominate an agent loop.
|
|
114
|
+
- **Cost composition (Usage tab)**: output cost is measured, input cost is apportioned by user / tool message text length (the log has no per-role token measurement, so the UI labels it as an estimate).
|
|
115
|
+
- **Performance metrics (Usage tab)**: per-model TTFT mean / P50 / P90, generation speed (tokens/s), total-latency mean; per-hour × per-model comparison curves — metric tabs, clickable model chips (top-5 by samples lit by default), hover snapping to the nearest hour with a crosshair and per-model values, broken lines for missing-sample hours (never fabricated); view preferences persist locally.
|
|
116
|
+
- **Trends (Trends tab)**: daily cost & call trend (7 / 30 days, cost / tokens metric switch), last-40-turn cost bars (amount at bar top, >2× spikes flagged with attribution), peak/off-peak time share and the amount saved in off-peak hours.
|
|
107
117
|
|
|
108
|
-

|
|
119
|
+

|
|
120
|
+
- **Export + drill-down**: daily / per-session / per-site CSV and full JSON from the Billing tab, daily-token CSV from the Usage tab; session detail is drillable (click a project row to expand its sessions); no chart library, no external CDN, pure design tokens.
|
|
109
121
|
|
|
110
122
|
## 🛡️ Robustness & privacy
|
|
111
123
|
|
|
@@ -121,7 +133,7 @@ The billing channel is detected from the current session's model: DeepSeek meter
|
|
|
121
133
|
|
|
122
134
|
Check your host generation first (`dsh --version`), then pick the matching install command — **a mismatched line is rejected by the DSH Store via the `engines.dsh` declaration** (declared since v1.0.41). Note that `dsh plugin add` requires an explicit `--profile` (otherwise it fails with `required option '--profile <name>' not specified`), and prefer **pinning an exact version** over `@latest` (pnpm's `minimumReleaseAge` cooldown skips freshly published versions and may fall back to the other line):
|
|
123
135
|
|
|
124
|
-
- **DSH 0.1.2 ~ 0.2.
|
|
136
|
+
- **DSH 0.1.2 ~ 0.2.1 era** (0.1.2-alpha.1 and later; the host's npm `latest` is currently 0.2.1-rc.2 and the preview line has reached 0.2.1-rc.3; `npm ls -g @deepseek-ai/dsh` shows 0.1.2-* ~ 0.2.1-*):
|
|
125
137
|
|
|
126
138
|
```sh
|
|
127
139
|
dsh plugin --profile web add npm:@kenz1117/dsh-ui-usage-billing@latest
|
|
@@ -274,7 +286,7 @@ The host discovers the browser side automatically via the `dsh.client` declarati
|
|
|
274
286
|
|
|
275
287
|
- **Permission level: high**: reads durable session logs (files), calls official multi-vendor / subscription / balance / pricing APIs (network), reads `apiKeyEnv` via the credentials seam (credentials), writes the ledger and stats snapshot under the harness home (`DSH_HOME` / `~/.dsh`) (persistent state); **no** command execution / shell.
|
|
276
288
|
- **Update channel: `user-reviewed`**: with file / network / credential capabilities, DSH STORE requires local manual confirmation on every install; review the repo, pinned commit, lifecycle scripts, and impact scope before installing.
|
|
277
|
-
- **Compatibility**: the preview line (npm `latest`/`alpha`, 1.
|
|
289
|
+
- **Compatibility**: the preview line (npm `latest`/`alpha`, 1.7.x — kept above the stable line to kill the version inversion) targets DSH `0.1.2-alpha.1` ~ `0.2.1-rc.3` (the host's `latest` currently at 0.2.1-rc.2, preview at 0.2.1-rc.3; the full 0.2.1 series is declared per version); the stable line (npm `stable`, 1.1.x, **frozen**, final v1.1.17) targets legacy hosts `0.1.0-rc.8` ~ `0.1.1-rc.2`. Per-version declarations live in `package.json` under `dsh.compatibility`; the two-line mapping and monitoring mechanism are documented in [COMPATIBILITY.md](COMPATIBILITY.md). Node.js `^22.19.0 || >=24.0.0`.
|
|
278
290
|
- **Lifecycle**: no `preinstall` / `install` / `postinstall` / `prepare` (ready on install).
|
|
279
291
|
- **No impersonation**: adds only its own entry id `ui-usage-billing`; `@deepseek-ai/dsh-*` packages are `peerDependencies` only (no reinstall / replace / shadowing of official components); the package uses the third-party namespace `@kenz1117/*`.
|
|
280
292
|
- **Build artifacts**: runtime files `lib/*` and `cordis.patch.yml` are committed at the pinned commit and declared in `files`.
|
|
@@ -297,7 +309,7 @@ Boundaries: the tool reads only this plugin's own aggregates — never session b
|
|
|
297
309
|
- **Balance queries cover DeepSeek / Moonshot (Kimi) / StepFun / SiliconFlow / xAI / Zhipu GLM (Z.ai CN region) / Tencent Cloud TokenHub (token-plan quota)**: the first six use a standard Bearer API key; Tencent Cloud uses the cloud-API key pair (`<SecretId>:<SecretKey>`, TC3-signed management API). Other providers expose no public balance API or need non-Bearer auth (Xiaomi MiMo via console Cookie, SenseTime via AccessKey signing, MiniMax/Doubao via quota or AK/SK), so they currently show "not configured"; the extension point is `src/balance.ts` (add a querier per provider balance API).
|
|
298
310
|
- **Relay quota depends on upstream private schemas**: New API / Sub2API interface fields are not public, so an unreadable station is labeled "no quota" rather than fabricating an amount; if a station's response fields differ, extend the parsers in `src/relay.ts`. An "unknown route" means that route exists neither in the current provider config nor in the host llm registry (deleted config, logs older than the install, …); historical call data is not lost — re-adding the same-named route restores attribution automatically. Cost in the unknown-route bucket is always 0 (issue #82): the spending party cannot be verified — same posture as uncatalogued models and subscription channels — while tokens are still counted, and the panel shows "—" for the group cost with an explanatory note. Channels registered by another plugin through `ctx.llm.registerAdapter` (e.g. CommandCode) are **not** unknown routes: the plugin discovers them from the host llm registry and bills them in the `direct:<provider>` bucket at the model's official catalog price (issue #91); the registry is read through `ctx.get('llm')` (no `inject` requirement), and an unreadable or empty registry warns once (panel numbers and the unknown policy are unchanged) so this class of failure is never silent again.
|
|
299
311
|
- **Overspend notifications rely on the browser Notification API**: when permission is denied or the platform lacks support, only the in-UI red-pulse fallback remains — no host-level notification channel; notifications are capped at once per day.
|
|
300
|
-
- **Session
|
|
312
|
+
- **Session-row navigation depends on a host channel**: clicking a session title in the panel opens that conversation in the host (both generations of the host's session-selection channel are probed); on very old hosts, or when the session was deleted / is not in the current session list, the UI shows a readable notice instead of silently doing nothing. The panel shows the top 20 by default and the "view all" full-screen viewer is capped at 100 rows.
|
|
301
313
|
- **Cost is a catalog estimate**: models without published per-token pricing (iFlytek, SenseTime, Xiaomi) use estimates (feature-list footnote ¹); official billing is authoritative.
|
|
302
314
|
- **The ledger starts at its first successful aggregation**: sessions permanently deleted before the upgrade and absent from the old snapshot cannot be recovered. Manually deleting `.dsh-usage-ledger.json` and its `.bak` clears independently retained history. Only calls successfully observed by this plugin are retained.
|
|
303
315
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/kenz1117/dsh-ui-usage-billing?logo=github"></a>
|
|
10
10
|
<a href="https://www.npmjs.com/package/@kenz1117/dsh-ui-usage-billing"><img alt="npm version" src="https://img.shields.io/npm/v/@kenz1117/dsh-ui-usage-billing?logo=npm"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/@kenz1117/dsh-ui-usage-billing"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@kenz1117/dsh-ui-usage-billing?logo=npm"></a>
|
|
12
|
-
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/COMPATIBILITY.md"><img alt="DSH 兼容" src="https://img.shields.io/badge/DSH-0.1.2_%7E_0.2.1--rc.
|
|
12
|
+
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/COMPATIBILITY.md"><img alt="DSH 兼容" src="https://img.shields.io/badge/DSH-0.1.2_%7E_0.2.1--rc.3_%7C_0.1.0--rc.8%2B-blue"></a>
|
|
13
13
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/blob/main/LICENSE"><img alt="License MIT" src="https://img.shields.io/github/license/kenz1117/dsh-ui-usage-billing"></a>
|
|
14
14
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing/pulls"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen"></a>
|
|
15
15
|
<a href="https://github.com/kenz1117/dsh-ui-usage-billing"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/kenz1117/dsh-ui-usage-billing?logo=github"></a>
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
<div align="center">
|
|
28
|
-
<img src="screenshots/
|
|
28
|
+
<img src="screenshots/overview.png" alt="dsh-ui-usage-billing — 计费仪表盘总览" width="80%">
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
### 演示动图
|
|
32
32
|
|
|
33
|
+
新版仪表盘八个分区逐个切换(概览 / 账单 / 用量 / 趋势 / 上下文 / 智能体 / 费率 / 设置):
|
|
34
|
+
|
|
33
35
|

|
|
34
36
|
|
|
35
37
|
## ✨ 为什么选它
|
|
@@ -64,24 +66,31 @@ DeepSeek / Kimi / 智谱 GLM / 腾讯云 TokenHub 等 7 家官方余额、Coding
|
|
|
64
66
|
## 📊 仪表盘
|
|
65
67
|
|
|
66
68
|
- **侧边栏触发卡**:设置按钮上方常驻单值主数字(今日 / 本周 / 本月统计范围在设置里选,可选涨跌徽章),配近 7 天 sparkline 迷你趋势与当日水位仪表指针;折叠栏自动切为图标钮,悬停浮现速览卡(更新时间 + 指标网格 + 主力消耗 / 额度提醒)。
|
|
69
|
+
|
|
70
|
+

|
|
71
|
+
|
|
67
72
|
- **八区仪表盘**:概览 / 账单 / 用量 / 趋势 / 上下文 / 智能体 / 费率 / 设置——Hero 大数字 + 环比 + 本月预计 + KPI(悬停显示口径说明)+ 用量热力图,趋势 7/30 天可切费用 / Token,模型单价表、预算(全局 + 按模型)与峰谷提醒都在;设置页支持全文搜索(中英关键词过滤分区,全不命中显示空态);克制冷调、深浅主题自适应。Tab 只保留名称,不再挂状态徽标(信息都在各自分区里)。
|
|
68
73
|
- **上下文分区**:占用率进度条 + 下一次 / 最近一次请求占用 + 窗口上限 + 构成拆解(系统 / 工具 / 消息)。数据取自宿主官方 context 投影;**没有投影的会话整块不显示**,不拿 0 冒充。
|
|
69
74
|
- **智能体分区**:按委派关系还原的智能体网络——父对话 → 子智能体的层级树,每行给调用数、自身费用与**链合计**(自身 + 全部后代),直接回答「哪次委派最贵」。
|
|
70
75
|
|
|
71
|
-

|
|
77
|
+

|
|
78
|
+

|
|
72
79
|
- **即时代费用条**:常驻「本轮 / 会话」费用,位置三档可选(输入框内部工具行 / 上方 / 下方,默认工具行);峰谷档位与切换倒计时只在当前模型涉及峰谷时出现;订阅额度预警 chips 剩余 ≤20% 浮现、≤10% 标红;可在设置 Tab 整条隐藏(偏好本地持久化,统计与提醒不受影响)。
|
|
73
80
|
- **峰 / 谷切换提醒**:切档前弹窗 + 可选系统通知,提前量 / 位置 / 模式 / 预览均可配;文案按计费通道区分(DeepSeek 价格减半、智谱积分 5 折)。
|
|
74
81
|
- **会话完成提醒**:会话 running → completed 时弹系统通知(默认关闭,面板设置开启);多窗口 / 多实例按认领端点去重,同一会话只提醒一次。
|
|
75
82
|
- **插件信息卡**:设置 Tab 常驻「关于」卡——版本号服务端读自包 `package.json`(单一来源,发布自动正确),作者 / 仓库 / npm / 许可证一键可达。
|
|
76
83
|
|
|
84
|
+

|
|
85
|
+
|
|
77
86
|
## 💰 计费引擎
|
|
78
87
|
|
|
79
88
|
- **提供商优先分组**:费用按调用实际发生的 llm 入口(通道)分组——腾讯云 TokenHub / Token Plan / DeepSeek 官方 / 直连·路由名 / 未知路由,模型品牌只是行内徽标 + 副标;官方判定按通道 origin(`api.deepseek.com`)而非路由名,`deepseek-*` 网关路由不再被误算官方。`routeAliases` 归位改名 / 删除的历史路由,`modelKeyAliases` 把目录外模型 id 绑定到计费键(日期后缀、组织前缀、TokenHub 短 id `hy3` 已内置识别)。
|
|
80
89
|
- **实时费率表**:models.dev 抓价 + 探活模型对标,系统实际配置的模型全纳入;峰谷分时(工作日 9-12 / 14-18 高峰 ×2、周末全天低谷,历史费用按官方变更节点分段,见下方「计费细节」)+ 实时汇率(USD→CNY),每 6 小时自动刷新;费率条显示「上次同步」时间并可**一键立即同步**(无需重启宿主)。
|
|
81
90
|
- **自定义单价**:设置面板为未收录或变价模型填实付价(未命中 / 缓存命中 / 输出,可选 USD 与低谷价),总览与日趋势按用户价重估;支持按中转站来源绑定同模型不同价,目录外模型填价即生效。
|
|
82
91
|
|
|
83
|
-
](screenshots/pricing.png)
|
|
93
|
+
- **官方 vs 三方分桶**:账单 Tab 的模型明细费用列按官方直连 / 第三方中转分解(混合时「官 x / 三 y」);联网搜索辅助请求计入官方。
|
|
85
94
|
- **月度预算 + 分档提醒**:预算条 ≥80% 琥珀、超支红脉;跨 50 / 80 / 100% 各提醒一次;余额折算 CNY 低于阈值每天提醒一次。**按模型预算**:为本月消耗 Top 模型逐个设人民币上限(与全局预算相互独立),单模型跨 80% / 100% 各提醒一次。
|
|
86
95
|
- **成本突增归因**:最近 40 轮费用柱状图,金额贴柱顶、峰谷背景分带、超 2 倍红标归因。
|
|
87
96
|
|
|
@@ -93,20 +102,23 @@ DeepSeek / Kimi / 智谱 GLM / 腾讯云 TokenHub 等 7 家官方余额、Coding
|
|
|
93
102
|
- **声明端点 + 余额对账**:内置表没有的供应商用 `declaredEndpoints` 自声明余额接口——只写「数字在哪里」的点路径、无表达式;安全边界(单斜杠绝对路径、仅 GET、拒跨源重定向、响应体 / 超时上限、凭据只取本 provider)由 `src/declarative.ts` 强制执行,取错路径在界面标 `declared` 与 reason。**余额差对账**(`reconcilePath`)用官方余额当日变动与本地账本交叉校验,偏差超阈值(0.3 元且 >15%)提示核对;充值 / 授信 / 币种变化重置基准而非告警,余额未减少(走订阅扣费)静默。
|
|
94
103
|
- **中转站归组与额度**:按 `baseURL` 归一化 origin 归组,同站多把 key 合并一行、站名即域名;自动识别 New API 系(`/api/status`)与 Sub2API(`/v1/usage`)的余额与滚动额度窗口,读不出标「未读出额度」、剩余 <20% 标红;识别结果 5 分钟指纹缓存(同站多 key 独立熔断),`relay-quotas` 端点附 `diagnostics` 供「为什么不显示」自查;项目归属优先用工作区标题命名。
|
|
95
104
|
|
|
96
|
-

|
|
97
106
|
|
|
98
107
|
> 全部渠道的适配矩阵(识别方式 / 端点 / 凭据要求 / 排查顺序)见 [docs/adapters.md](docs/adapters.md)。
|
|
99
108
|
|
|
100
109
|
## 📈 用量可视化
|
|
101
110
|
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
- **Token
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
111
|
+
- **会话明细(账单 Tab)**:按会话费用倒序(标题 / 项目 / 调用 / 实际费用 / 最后活跃),「查看全部 N 个会话」打开全屏浮层(按标题 / 项目 / id 子串搜索,封顶 100 行);**点会话标题直接打开宿主对话**(宿主不支持时给出可读提示,不静默无反应)。
|
|
112
|
+
- **用量热力图(概览 Tab)**:月 / 半年 / 年三档日历热力图(5 档色阶、悬停明细,年视图近 52 周 GitHub 风格,**半年视图 26 周大格**——一张图看完近半年强度,截图即用),**费用 / Token 双口径**切换,头部显示区间合计、活跃天数 / 连续使用天数。
|
|
113
|
+
- **Token 统计洞察(用量 Tab)**:每日 token 堆叠双视角——「按结构」(输入未命中 / 命中 / 输出三桶,含 reasoning)与「按模型」(旧快照缺明细时自动隐藏切换);悬停显示当日精确明细(千分位不缩写),点击图例色块或 Token 表行聚焦单模型(再点解除);模型 Token 表给出输入 / 输出 / 思考 / 缓存命中率 / 占比 / 调用数。
|
|
114
|
+
- **工具调用排行(用量 Tab)**:按工具计次的调用结构(token 无法按工具归因,只给次数与占比),看清 agent 循环里哪些工具在刷量。
|
|
115
|
+
- **费用构成(用量 Tab)**:输出成本按实测计价、输入成本按 user / tool 消息文本长度占比摊分(日志无角色级 token 实测,界面标注估算口径)。
|
|
116
|
+
- **性能指标(用量 Tab)**:每模型 TTFT 均值 / P50 / P90、生成速度(tokens/s)、总延迟均值;按小时 × 模型对比曲线——指标 tab 切换、模型 chip 点击开关曲线(默认点亮样本数前 5)、悬停吸附最近小时显示十字线与逐模型数值,缺失样本小时断线不造假;视图偏好本地持久化。
|
|
117
|
+
- **趋势(趋势 Tab)**:每日费用与调用趋势(7 / 30 天可切,费用 / Token 双口径)、最近 40 轮费用柱状图(金额贴柱顶、超 2 倍红标归因)、峰谷时段占比与低谷省下的金额。
|
|
108
118
|
|
|
109
|
-

|
|
120
|
+

|
|
121
|
+
- **数据导出 + 下钻**:账单 Tab 导出按日 / 按会话 / 按站点 CSV 与全量 JSON,用量 Tab 导出按日 Token CSV;会话明细可下钻(点项目行展开该项目的会话);无图表库、无外部 CDN、纯设计令牌。
|
|
110
122
|
|
|
111
123
|
## 🛡️ 健壮性与隐私
|
|
112
124
|
|
|
@@ -122,7 +134,7 @@ DeepSeek / Kimi / 智谱 GLM / 腾讯云 TokenHub 等 7 家官方余额、Coding
|
|
|
122
134
|
|
|
123
135
|
先确认宿主代际(`dsh --version`),再按代际选安装命令——**装错线会在市场侧被 `engines.dsh` 声明拦截**(v1.0.41 起声明生效)。注意 `dsh plugin add` 需要显式 `--profile`(缺省会报 `required option '--profile <name>' not specified`),且建议**钉具体版本号**而非 `@latest`(pnpm 的 `minimumReleaseAge` 冷静期会让刚发布的版本被跳过、回退到旧线):
|
|
124
136
|
|
|
125
|
-
- **DSH 0.1.2 ~ 0.2.
|
|
137
|
+
- **DSH 0.1.2 ~ 0.2.1 系**(0.1.2-alpha.1 起,宿主 `latest` 现为 0.2.1-rc.2、预览线已到 0.2.1-rc.3;`npm ls -g @deepseek-ai/dsh` 显示 0.1.2-* ~ 0.2.1-*):
|
|
126
138
|
|
|
127
139
|
```sh
|
|
128
140
|
dsh plugin --profile web add npm:@kenz1117/dsh-ui-usage-billing@latest
|
|
@@ -283,7 +295,7 @@ npm dist-tag add @kenz1117/dsh-ui-usage-billing@<版本> alpha
|
|
|
283
295
|
|
|
284
296
|
- **权限等级:high**:读取持久会话日志(文件)、访问多厂商官方 / 订阅 / 余额 / 定价 API(网络)、经凭据 seam 读取 `apiKeyEnv`(凭据)、写入 harness home(`DSH_HOME` / `~/.dsh`)下的账本与快照(持久状态);**不含**命令执行 / Shell。
|
|
285
297
|
- **更新通道:`user-reviewed`**:本插件具备文件 / 网络 / 凭据能力,DSH STORE 采用每次安装需本机人工确认的通道;安装前请复核仓库、固定 Commit、生命周期脚本与影响范围。
|
|
286
|
-
- **兼容范围**:预览线(npm `latest`/`alpha`,1.
|
|
298
|
+
- **兼容范围**:预览线(npm `latest`/`alpha`,1.7.x,自 v1.2.0 起恒高于稳定线以消除版本号倒挂)适配 DSH `0.1.2-alpha.1` ~ `0.2.1-rc.3`(宿主 `latest` 现指向 0.2.1-rc.2、预览线已到 0.2.1-rc.3;0.2.1 全系列逐版本声明);稳定线(npm `stable`,1.1.x,**已冻结**,终版 v1.1.17)适配旧宿主 `0.1.0-rc.8` ~ `0.1.1-rc.2`。逐版本声明见 `package.json` 的 `dsh.compatibility`,双线对照与监控机制见 [COMPATIBILITY.md](COMPATIBILITY.md)。Node.js `^22.19.0 || >=24.0.0`。
|
|
287
299
|
- **生命周期**:无 `preinstall` / `install` / `postinstall` / `prepare`(安装即用)。
|
|
288
300
|
- **不冒用官方**:仅新增自有 entry id `ui-usage-billing`;对 `@deepseek-ai/dsh-*` 仅为 `peerDependencies` 依赖(不重复安装 / 不替换 / 不遮蔽官方组件),包名使用第三方命名空间 `@kenz1117/*`。
|
|
289
301
|
- **打包产物**:运行文件 `lib/*` 与 `cordis.patch.yml` 已随固定 Commit 提交并声明在 `files`。
|
|
@@ -306,7 +318,7 @@ npm dist-tag add @kenz1117/dsh-ui-usage-billing@<版本> alpha
|
|
|
306
318
|
- **余额查询已接入 DeepSeek / 月之暗面(Kimi)/ 阶跃星辰(StepFun)/ 硅基流动 / xAI / 智谱 GLM(Z.ai 国内域)/ 腾讯云 TokenHub(Token Plan 余量)**:前六家用标准 Bearer API key,腾讯云用云 API 密钥对(TC3 签名)。其余厂商因无公开余额接口或需非 Bearer 鉴权(小米 MiMo 走控制台 Cookie、商汤走 AccessKey 签名、MiniMax/字节豆包走额度制或 AK/SK),暂显示「未配置」;扩展点在 `src/balance.ts`(按厂商余额 API 增加查询器)。
|
|
307
319
|
- **中转站额度依赖上游私有 schema**:New API / Sub2API 的接口字段未公开,读不出时标「未读出额度」而非臆造金额;若某中转站响应字段不同,需按 `src/relay.ts` 的解析器扩展。未知路由表示该路由在当前 provider 配置与宿主 llm 注册表里都不存在(配置已删、日志比安装更早等),历史调用数据未丢,重新配置同名路由即可自动归位。未知路由桶的费用一律记 0(issue #82):支出主体无法核实,与未收录模型、订阅通道同姿态——token 照常统计,面板组头费用显示「—」并附说明。由独立插件经 `ctx.llm.registerAdapter` 注册的通道(如 CommandCode)不属于未知路由:插件从宿主 llm 注册表发现它们,按 `direct:<provider>` 桶以模型官方目录价计费(issue #91);注册表读取走 `ctx.get('llm')`(免 `inject` 声明),读不到/读到空集合时告警一次(面板数字不变,unknown 口径不变),避免这类失效无声无息。
|
|
308
320
|
- **超支通知依赖浏览器 Notification**:权限被拒绝或平台不支持时只有界面红色脉冲兜底,没有宿主级通知通道;通知上限为每天一次。
|
|
309
|
-
-
|
|
321
|
+
- **会话行跳转依赖宿主通道**:面板里的会话标题点一下即可打开宿主对话(双代会话选择通道自动探测);宿主过旧、或会话已被删除 / 不在当前会话列表时,界面给出可读提示而不是静默无反应。面板默认只显示前 20 行,「查看全部」全屏浮层封顶 100 行。
|
|
310
322
|
- **费用为目录价估算**:讯飞 / 商汤 / 小米等未公布按量单价的模型使用估算价(特性表脚注 ¹),正式定价以厂商账单为准。
|
|
311
323
|
- **账本从首次成功聚合开始生效**:升级前已经永久删除且不在旧快照中的会话无法恢复;手动删除 `.dsh-usage-ledger.json` 及其 `.bak` 会清空独立保留的历史。账本只保留本插件已经成功观测过的调用。
|
|
312
324
|
- **历史回放预热**:插件加载 3 秒后台自动全量折叠宿主全部历史会话日志(账本幂等,重复折叠不重复计数),首次打开面板即可看到完整历史统计,无需等一遍首次折叠。历史会话按「首次折叠时的模型单价」计价;厂商改价前的历史消耗不会按新价重算(价格目录无历史时点价,属估算口径)。
|
package/lib/index.js
CHANGED
|
@@ -1694,6 +1694,18 @@ const BUILTIN_MODEL_CATALOG = [
|
|
|
1694
1694
|
output: 10
|
|
1695
1695
|
}
|
|
1696
1696
|
},
|
|
1697
|
+
{
|
|
1698
|
+
key: "gpt-6.1-terra",
|
|
1699
|
+
name: "GPT-6.1 Terra",
|
|
1700
|
+
provider: "OpenAI",
|
|
1701
|
+
colorVar: "ds-green",
|
|
1702
|
+
price: {
|
|
1703
|
+
currency: "USD",
|
|
1704
|
+
input: 1,
|
|
1705
|
+
cacheHit: .05,
|
|
1706
|
+
output: 5
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1697
1709
|
{
|
|
1698
1710
|
key: "gpt-5",
|
|
1699
1711
|
name: "GPT-5",
|
|
@@ -5188,7 +5200,8 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5188
5200
|
const foldFingerprintOf = (registered, routes) => configFingerprint(subscriptionMatcher, officialProviderIds, routeAliases, searchEstimate, options.modelKeyAliases, registered, routeOriginsOf(routes));
|
|
5189
5201
|
let foldFingerprint = foldFingerprintOf(/* @__PURE__ */ new Set(), {});
|
|
5190
5202
|
const FOLD_CHUNK_EVENTS = 8e3;
|
|
5191
|
-
const
|
|
5203
|
+
const FOLD_SLICE_MS = 8;
|
|
5204
|
+
const foldAllowedAt = Date.now() + Math.max(0, options.deferFoldMs ?? 0);
|
|
5192
5205
|
/** 惰性失效(价目表时序):账本/缓存行若以「当时不可计价」的模型落盘,
|
|
5193
5206
|
* 而价目表现在能计价了,该行必须重折——否则显示层按实时价算出的
|
|
5194
5207
|
* 「目录价预估」与服务端 cost=0 并存,出现「明细比总额高」的双口径
|
|
@@ -5226,6 +5239,10 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5226
5239
|
const document = stored;
|
|
5227
5240
|
if (runLedgerMigrations(document)) ledgerNeedsSave = true;
|
|
5228
5241
|
ledgerAppliedMigrations = document.appliedMigrations;
|
|
5242
|
+
for (const refusal of document.refused ?? []) if (typeof refusal?.id === "string" && typeof refusal?.stamp === "string") unreadable.set(refusal.id, {
|
|
5243
|
+
stamp: refusal.stamp,
|
|
5244
|
+
format: refusal.format === true
|
|
5245
|
+
});
|
|
5229
5246
|
}
|
|
5230
5247
|
for (const entry of ledgerSessionsOf(stored)) ledger.set(entry.id, entry);
|
|
5231
5248
|
} catch (error) {
|
|
@@ -5260,6 +5277,11 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5260
5277
|
version: 1,
|
|
5261
5278
|
updatedAt: Date.now(),
|
|
5262
5279
|
sessions: [...ledger.values()],
|
|
5280
|
+
...unreadable.size === 0 ? {} : { refused: [...unreadable].slice(0, 500).map(([id, mark]) => ({
|
|
5281
|
+
id,
|
|
5282
|
+
stamp: mark.stamp,
|
|
5283
|
+
format: mark.format
|
|
5284
|
+
})) },
|
|
5263
5285
|
...ledgerAppliedMigrations === void 0 ? {} : { appliedMigrations: ledgerAppliedMigrations }
|
|
5264
5286
|
});
|
|
5265
5287
|
ledgerNeedsSave = false;
|
|
@@ -5268,20 +5290,19 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5268
5290
|
console.warn("[usage-billing] failed to persist durable usage ledger:", error);
|
|
5269
5291
|
}
|
|
5270
5292
|
};
|
|
5271
|
-
|
|
5293
|
+
/**
|
|
5294
|
+
* 一轮聚合。mode='full' 走完整流程(list + 逐会话失效判定 + 折叠/复用);
|
|
5295
|
+
* mode='ledger-only' 只吃账本存档出轻文档(冷phase,issue #105):不 list、
|
|
5296
|
+
* 不读任何日志,doc 带 staleSnapshot=true 且**不进 TTL 缓存**(lastDoc 不动,
|
|
5297
|
+
* 开门后的下一轮即完整折叠)——轻文档的每行折叠反序列化有 WeakMap 记忆,
|
|
5298
|
+
* 重复构建只付 O(行数) 的汇总合并,冷phase里每个请求都可负担。
|
|
5299
|
+
*/
|
|
5300
|
+
const aggregateOnce = async (mode) => {
|
|
5272
5301
|
const now = Date.now();
|
|
5273
5302
|
await ensureLedgerLoaded();
|
|
5274
5303
|
const registeredProviders = options.resolveRegisteredProviders?.() ?? /* @__PURE__ */ new Set();
|
|
5275
5304
|
const roundRoutes = routesOf(registeredProviders);
|
|
5276
5305
|
foldFingerprint = foldFingerprintOf(registeredProviders, roundRoutes);
|
|
5277
|
-
let metas;
|
|
5278
|
-
try {
|
|
5279
|
-
metas = await persistence.list();
|
|
5280
|
-
} catch (error) {
|
|
5281
|
-
if (options.ledger === void 0 || ledger.size === 0) throw error;
|
|
5282
|
-
console.warn("[usage-billing] session list unavailable; serving durable usage ledger:", error);
|
|
5283
|
-
metas = [];
|
|
5284
|
-
}
|
|
5285
5306
|
const seen = /* @__PURE__ */ new Set();
|
|
5286
5307
|
const included = /* @__PURE__ */ new Set();
|
|
5287
5308
|
const folds = [];
|
|
@@ -5291,132 +5312,164 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5291
5312
|
let formatUnreadable = 0;
|
|
5292
5313
|
let noLedgerUnreadable = 0;
|
|
5293
5314
|
let staleLedgerSessions = 0;
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
setImmediate(resolve);
|
|
5297
|
-
});
|
|
5298
|
-
const id = String(meta.id);
|
|
5299
|
-
seen.add(id);
|
|
5300
|
-
const stamp = await stampOf(meta);
|
|
5301
|
-
const mark = unreadable.get(id);
|
|
5302
|
-
if (stamp !== null && mark !== void 0 && mark.stamp === stamp) {
|
|
5303
|
-
carriedUnreadable += 1;
|
|
5304
|
-
if (mark.format) carriedFormatUnreadable += 1;
|
|
5305
|
-
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
5306
|
-
continue;
|
|
5307
|
-
}
|
|
5315
|
+
if (mode === "full") {
|
|
5316
|
+
let metas;
|
|
5308
5317
|
try {
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5318
|
+
metas = await persistence.list();
|
|
5319
|
+
} catch (error) {
|
|
5320
|
+
if (options.ledger === void 0 || ledger.size === 0) throw error;
|
|
5321
|
+
console.warn("[usage-billing] session list unavailable; serving durable usage ledger:", error);
|
|
5322
|
+
metas = [];
|
|
5323
|
+
}
|
|
5324
|
+
let sliceAt = Date.now();
|
|
5325
|
+
for (const meta of metas) {
|
|
5326
|
+
if (Date.now() - sliceAt >= FOLD_SLICE_MS) {
|
|
5327
|
+
await new Promise((resolve) => {
|
|
5328
|
+
setImmediate(resolve);
|
|
5319
5329
|
});
|
|
5320
|
-
|
|
5321
|
-
continue;
|
|
5330
|
+
sliceAt = Date.now();
|
|
5322
5331
|
}
|
|
5323
|
-
const
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
fold: hit.fold
|
|
5332
|
-
});
|
|
5333
|
-
included.add(id);
|
|
5332
|
+
const id = String(meta.id);
|
|
5333
|
+
seen.add(id);
|
|
5334
|
+
const stamp = await stampOf(meta);
|
|
5335
|
+
const mark = unreadable.get(id);
|
|
5336
|
+
if (stamp !== null && mark !== void 0 && mark.stamp === stamp) {
|
|
5337
|
+
carriedUnreadable += 1;
|
|
5338
|
+
if (mark.format) carriedFormatUnreadable += 1;
|
|
5339
|
+
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
5334
5340
|
continue;
|
|
5335
5341
|
}
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
const
|
|
5339
|
-
const
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
+
try {
|
|
5343
|
+
const cwd = meta.cwd;
|
|
5344
|
+
const subOf = subParentOf(meta);
|
|
5345
|
+
const ledgerRow = ledger.get(id);
|
|
5346
|
+
if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 23 && ledgerRow.fingerprint === foldFingerprint && !foldNeedsReprice(ledgerRow.fold)) {
|
|
5347
|
+
const fold = deserializedFoldOf(ledgerRow.fold);
|
|
5348
|
+
folds.push({
|
|
5349
|
+
id,
|
|
5350
|
+
...cwd === void 0 ? {} : { cwd },
|
|
5351
|
+
...subOf === void 0 ? {} : { subOf },
|
|
5352
|
+
fold
|
|
5353
|
+
});
|
|
5354
|
+
included.add(id);
|
|
5355
|
+
continue;
|
|
5356
|
+
}
|
|
5357
|
+
const hit = cache.get(id);
|
|
5358
|
+
if (hit !== void 0 && stamp !== null && hit.stamp === stamp && hit.fingerprint === foldFingerprint && ![...hit.fold.unpricedModels].some((key) => isPriced(key))) {
|
|
5342
5359
|
cache.delete(id);
|
|
5360
|
+
cache.set(id, hit);
|
|
5361
|
+
folds.push({
|
|
5362
|
+
id,
|
|
5363
|
+
...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
|
|
5364
|
+
...subOf === void 0 ? {} : { subOf },
|
|
5365
|
+
fold: hit.fold
|
|
5366
|
+
});
|
|
5367
|
+
included.add(id);
|
|
5343
5368
|
continue;
|
|
5344
5369
|
}
|
|
5345
|
-
const
|
|
5346
|
-
if (
|
|
5370
|
+
const previous = cache.get(id);
|
|
5371
|
+
if (previous?.machine !== void 0 && previous.lastSeq !== void 0 && previous.fingerprint === foldFingerprint && ![...previous.fold.unpricedModels].some((key) => isPriced(key))) {
|
|
5372
|
+
const from = previous.lastSeq + 1;
|
|
5373
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, sessionLogOffsetOf(from));
|
|
5374
|
+
const after = await stampOf(meta);
|
|
5375
|
+
if (stamp !== null && after !== stamp) {
|
|
5376
|
+
cache.delete(id);
|
|
5377
|
+
continue;
|
|
5378
|
+
}
|
|
5379
|
+
const first = events[0]?.seq;
|
|
5380
|
+
if (events.length === 0 || first !== from) {
|
|
5381
|
+
cache.delete(id);
|
|
5382
|
+
continue;
|
|
5383
|
+
}
|
|
5384
|
+
foldInto(previous.fold, previous.machine, events, subscriptionMatcher, officialProviderIds, roundRoutes, searchEstimate, inheritedEventCount, routeAliases);
|
|
5385
|
+
refreshTurns(previous.fold, previous.machine);
|
|
5386
|
+
previous.lastSeq = events[events.length - 1]?.seq ?? previous.lastSeq;
|
|
5387
|
+
previous.stamp = stamp;
|
|
5347
5388
|
cache.delete(id);
|
|
5389
|
+
cache.set(id, previous);
|
|
5390
|
+
folds.push({
|
|
5391
|
+
id,
|
|
5392
|
+
...cwd === void 0 ? {} : { cwd },
|
|
5393
|
+
...subOf === void 0 ? {} : { subOf },
|
|
5394
|
+
fold: previous.fold
|
|
5395
|
+
});
|
|
5396
|
+
included.add(id);
|
|
5397
|
+
recordLedger(id, cwd, stamp, previous.fold, subOf);
|
|
5348
5398
|
continue;
|
|
5349
5399
|
}
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5400
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, sessionLogOffsetOf(0));
|
|
5401
|
+
const after = await stampOf(meta);
|
|
5402
|
+
if (stamp !== null && after !== stamp) continue;
|
|
5403
|
+
const fold = freshFold();
|
|
5404
|
+
const machine = freshMachine();
|
|
5405
|
+
for (let start = 0; start < events.length; start += FOLD_CHUNK_EVENTS) {
|
|
5406
|
+
foldInto(fold, machine, events.slice(start, start + FOLD_CHUNK_EVENTS), subscriptionMatcher, officialProviderIds, roundRoutes, searchEstimate, inheritedEventCount, routeAliases);
|
|
5407
|
+
if (start + FOLD_CHUNK_EVENTS < events.length && Date.now() - sliceAt >= FOLD_SLICE_MS) {
|
|
5408
|
+
await new Promise((resolve) => {
|
|
5409
|
+
setImmediate(resolve);
|
|
5410
|
+
});
|
|
5411
|
+
sliceAt = Date.now();
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
refreshTurns(fold, machine);
|
|
5415
|
+
const last = events[events.length - 1];
|
|
5416
|
+
cache.set(id, {
|
|
5417
|
+
stamp,
|
|
5418
|
+
fold,
|
|
5419
|
+
machine,
|
|
5420
|
+
...last !== void 0 && typeof last.seq === "number" ? { lastSeq: last.seq } : {},
|
|
5421
|
+
fingerprint: foldFingerprint
|
|
5422
|
+
});
|
|
5356
5423
|
folds.push({
|
|
5357
5424
|
id,
|
|
5358
5425
|
...cwd === void 0 ? {} : { cwd },
|
|
5359
5426
|
...subOf === void 0 ? {} : { subOf },
|
|
5360
|
-
fold
|
|
5427
|
+
fold
|
|
5361
5428
|
});
|
|
5362
5429
|
included.add(id);
|
|
5363
|
-
recordLedger(id, cwd, stamp,
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
}
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
fold,
|
|
5382
|
-
machine,
|
|
5383
|
-
...last !== void 0 && typeof last.seq === "number" ? { lastSeq: last.seq } : {},
|
|
5384
|
-
fingerprint: foldFingerprint
|
|
5385
|
-
});
|
|
5386
|
-
folds.push({
|
|
5387
|
-
id,
|
|
5388
|
-
...cwd === void 0 ? {} : { cwd },
|
|
5389
|
-
...subOf === void 0 ? {} : { subOf },
|
|
5390
|
-
fold
|
|
5391
|
-
});
|
|
5392
|
-
included.add(id);
|
|
5393
|
-
recordLedger(id, cwd, stamp, fold, subOf);
|
|
5394
|
-
} catch (error) {
|
|
5395
|
-
skipped.push(id);
|
|
5396
|
-
const format = isFormatUnsupportedError(error);
|
|
5397
|
-
if (format) formatUnreadable += 1;
|
|
5398
|
-
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
5399
|
-
if (stamp !== null) unreadable.set(id, {
|
|
5400
|
-
stamp,
|
|
5401
|
-
format
|
|
5402
|
-
});
|
|
5403
|
-
if (options.resolveSessionTitle !== void 0) {
|
|
5404
|
-
const hint = options.resolveSessionTitle(meta);
|
|
5405
|
-
if (hint !== void 0) titleHints.set(id, hint);
|
|
5430
|
+
recordLedger(id, cwd, stamp, fold, subOf);
|
|
5431
|
+
} catch (error) {
|
|
5432
|
+
skipped.push(id);
|
|
5433
|
+
const format = isFormatUnsupportedError(error);
|
|
5434
|
+
if (format) formatUnreadable += 1;
|
|
5435
|
+
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
5436
|
+
if (stamp !== null) {
|
|
5437
|
+
unreadable.set(id, {
|
|
5438
|
+
stamp,
|
|
5439
|
+
format
|
|
5440
|
+
});
|
|
5441
|
+
ledgerNeedsSave = true;
|
|
5442
|
+
}
|
|
5443
|
+
if (options.resolveSessionTitle !== void 0) {
|
|
5444
|
+
const hint = options.resolveSessionTitle(meta);
|
|
5445
|
+
if (hint !== void 0) titleHints.set(id, hint);
|
|
5446
|
+
}
|
|
5447
|
+
console.warn("[usage-billing] skip unreadable session", id, error);
|
|
5406
5448
|
}
|
|
5407
|
-
|
|
5449
|
+
if (included.has(id)) titleHints.delete(id);
|
|
5408
5450
|
}
|
|
5409
|
-
if (included.has(id)) titleHints.delete(id);
|
|
5410
5451
|
}
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5452
|
+
if (mode === "full") {
|
|
5453
|
+
for (const key of [...cache.keys()]) if (!seen.has(key)) cache.delete(key);
|
|
5454
|
+
for (const key of [...unreadable.keys()]) if (!seen.has(key)) {
|
|
5455
|
+
unreadable.delete(key);
|
|
5456
|
+
ledgerNeedsSave = true;
|
|
5457
|
+
}
|
|
5458
|
+
while (cache.size > maxCacheSessions) {
|
|
5459
|
+
const oldest = cache.keys().next();
|
|
5460
|
+
if (oldest.done === true) break;
|
|
5461
|
+
cache.delete(oldest.value);
|
|
5462
|
+
}
|
|
5417
5463
|
}
|
|
5418
5464
|
if (options.ledger !== void 0) {
|
|
5465
|
+
let fallbackSliceAt = Date.now();
|
|
5419
5466
|
for (const entry of ledger.values()) {
|
|
5467
|
+
if (Date.now() - fallbackSliceAt >= FOLD_SLICE_MS) {
|
|
5468
|
+
await new Promise((resolve) => {
|
|
5469
|
+
setImmediate(resolve);
|
|
5470
|
+
});
|
|
5471
|
+
fallbackSliceAt = Date.now();
|
|
5472
|
+
}
|
|
5420
5473
|
if (included.has(entry.id)) continue;
|
|
5421
5474
|
try {
|
|
5422
5475
|
const stale = (entry.foldVersion ?? 1) < 23 || entry.fingerprint !== foldFingerprint;
|
|
@@ -5594,7 +5647,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5594
5647
|
};
|
|
5595
5648
|
const unreadableTotal = skipped.length + carriedUnreadable;
|
|
5596
5649
|
const formatUnreadableTotal = formatUnreadable + carriedFormatUnreadable;
|
|
5597
|
-
|
|
5650
|
+
const document = {
|
|
5598
5651
|
version: 4,
|
|
5599
5652
|
updatedAt: now,
|
|
5600
5653
|
source: "session-logs",
|
|
@@ -5628,15 +5681,21 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
5628
5681
|
assistant: roles.outputCost,
|
|
5629
5682
|
tool: roles.inputCost * (1 - userShare)
|
|
5630
5683
|
};
|
|
5631
|
-
})()
|
|
5684
|
+
})(),
|
|
5685
|
+
...mode === "ledger-only" ? { staleSnapshot: true } : {}
|
|
5632
5686
|
};
|
|
5633
|
-
|
|
5634
|
-
|
|
5687
|
+
if (mode === "full") {
|
|
5688
|
+
lastDoc = document;
|
|
5689
|
+
lastAt = now;
|
|
5690
|
+
}
|
|
5691
|
+
return document;
|
|
5635
5692
|
};
|
|
5636
5693
|
return {
|
|
5637
5694
|
aggregate() {
|
|
5638
|
-
|
|
5639
|
-
|
|
5695
|
+
const now = Date.now();
|
|
5696
|
+
if (lastDoc !== void 0 && now - lastAt < 5e3) return Promise.resolve(lastDoc);
|
|
5697
|
+
if (now < foldAllowedAt) return inflight ?? aggregateOnce("ledger-only");
|
|
5698
|
+
inflight ??= aggregateOnce("full").finally(() => {
|
|
5640
5699
|
inflight = void 0;
|
|
5641
5700
|
});
|
|
5642
5701
|
return inflight;
|
|
@@ -9335,9 +9394,19 @@ const UsageBillingSettingsSchema = z.object({ [ENABLE_USAGE_STATS_TOOL_FIELD]: z
|
|
|
9335
9394
|
/** 实时定价的后台刷新间隔(毫秒):汇率/模型价低频变化,6 小时一次足够。 */
|
|
9336
9395
|
/** 实时定价的后台刷新间隔(毫秒):汇率/模型价低频变化,6 小时一次足够。 */
|
|
9337
9396
|
const PRICING_REFRESH_INTERVAL_MS = 216e5;
|
|
9338
|
-
/**
|
|
9339
|
-
*
|
|
9340
|
-
|
|
9397
|
+
/** 启动后延迟全量折叠的默认时长(毫秒,issue #105):冷phase内 /api/billing/*
|
|
9398
|
+
* 只出账本轻文档(不读日志),到点后预热定时器照常完整折叠——UI 一加载的
|
|
9399
|
+
* 轮询不再把全量折叠拉进启动关键路径。可用 config.replayDelayMs 覆盖;0 = 旧
|
|
9400
|
+
* 行为(随到随折)。20 秒的取值:小账本用户只损失十几秒新鲜度,重账本用户的
|
|
9401
|
+
* 启动高峰(插件加载/索引/首批会话写入)已经过去;配合聚合器的墙钟分片让出
|
|
9402
|
+
* (FOLD_SLICE_MS),折叠真跑起来时单片阻塞也被压在 ~8ms。
|
|
9403
|
+
*/
|
|
9404
|
+
const DEFAULT_REPLAY_DELAY_MS = 2e4;
|
|
9405
|
+
/** 手写收窄(schemastery 不能当形状校验器,S-7):非有限非负数字回落默认值。 */
|
|
9406
|
+
function readReplayDelayMs(value) {
|
|
9407
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
|
|
9408
|
+
return DEFAULT_REPLAY_DELAY_MS;
|
|
9409
|
+
}
|
|
9341
9410
|
/** 订阅套餐额度缓存时长(毫秒):上游配额 API 低频变化,5 分钟足够。 */
|
|
9342
9411
|
const SUBSCRIPTION_CACHE_MS = 3e5;
|
|
9343
9412
|
const BALANCE_CACHE_MS = 3e5;
|
|
@@ -9821,6 +9890,7 @@ function apply(ctx, config = {}) {
|
|
|
9821
9890
|
swallowed("minimax-plan-classify-failed", error);
|
|
9822
9891
|
}
|
|
9823
9892
|
};
|
|
9893
|
+
const replayDelayMs = readReplayDelayMs(config.replayDelayMs);
|
|
9824
9894
|
const aggregator = createUsageAggregator(adaptSessionPersistence(ctx.sessionPersistence), {
|
|
9825
9895
|
subscriptionMatcher: subscriptionRoutes,
|
|
9826
9896
|
...config.routeAliases === void 0 ? {} : { routeAliases: config.routeAliases },
|
|
@@ -9830,6 +9900,7 @@ function apply(ctx, config = {}) {
|
|
|
9830
9900
|
...workspaceTitleResolver === void 0 ? {} : { resolveWorkspaceTitle: workspaceTitleResolver },
|
|
9831
9901
|
...sessionTitleResolver === void 0 ? {} : { resolveSessionTitle: sessionTitleResolver },
|
|
9832
9902
|
...config.searchCallEstimateCny === void 0 ? {} : { searchCallEstimateCny: config.searchCallEstimateCny },
|
|
9903
|
+
deferFoldMs: replayDelayMs,
|
|
9833
9904
|
ledger: ledgerStore
|
|
9834
9905
|
});
|
|
9835
9906
|
refreshMiniMaxPlanRoutes();
|
|
@@ -9843,7 +9914,7 @@ function apply(ctx, config = {}) {
|
|
|
9843
9914
|
}).catch((error) => {
|
|
9844
9915
|
console.warn("[usage-billing] historical replay warmup failed; will fold on first dashboard request:", error);
|
|
9845
9916
|
});
|
|
9846
|
-
},
|
|
9917
|
+
}, replayDelayMs);
|
|
9847
9918
|
ctx.effect(() => () => {
|
|
9848
9919
|
clearTimeout(warmupTimer);
|
|
9849
9920
|
}, "usage-billing: historical replay warmup timer");
|
package/lib/types/aggregate.d.ts
CHANGED
|
@@ -179,6 +179,13 @@ export interface AggregateOptions {
|
|
|
179
179
|
/** 联网搜索请求(`web/deepseek-search-llm-request`,无用量事件)的单次费用
|
|
180
180
|
* 估算(人民币元);默认 {@link DEFAULT_SEARCH_CALL_ESTIMATE_CNY},设 0 关闭。 */
|
|
181
181
|
searchCallEstimateCny?: number;
|
|
182
|
+
/**
|
|
183
|
+
* 启动冷phase时长(毫秒,自聚合器创建起算,issue #105):期间 aggregate()
|
|
184
|
+
* 只出账本轻文档——不 list 会话、不读任何日志,文档带 `staleSnapshot: true`,
|
|
185
|
+
* UI 一加载的轮询不再把「全量折叠」拉进启动关键路径。到点后首个请求(或预热
|
|
186
|
+
* 定时器)照常完整折叠。0 / 缺省 = 不设闸(旧行为:随到随折)。
|
|
187
|
+
*/
|
|
188
|
+
deferFoldMs?: number;
|
|
182
189
|
}
|
|
183
190
|
/** 每会话折叠缓存默认上限:超过则按 LRU 淘汰(P1-6 峰值内存治理)。 */
|
|
184
191
|
export declare const DEFAULT_MAX_CACHE_SESSIONS = 400;
|
|
@@ -352,6 +359,12 @@ export interface UsageStatsDocument {
|
|
|
352
359
|
unreadableFormatSessions?: number;
|
|
353
360
|
/** 未统计中无任何账本存档、完全未计入的子集(区别于有存档按最近可用点计入的);无存档缺失时省略。 */
|
|
354
361
|
unreadableNoLedgerSessions?: number;
|
|
362
|
+
/**
|
|
363
|
+
* 冷phase轻文档标记(issue #105):本档全部出自账本存档、未读任何会话日志
|
|
364
|
+
* (启动后 replayDelayMs 内的请求)。客户端复用既有「数据滞后」横幅呈现
|
|
365
|
+
* (issue #103 加的 staleSnapshot 通道,此前无生产方),零新增客户端体积。
|
|
366
|
+
*/
|
|
367
|
+
staleSnapshot?: boolean;
|
|
355
368
|
}
|
|
356
369
|
/** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
|
|
357
370
|
export interface RoleCost {
|
|
@@ -637,6 +650,19 @@ export interface UsageLedgerDocument {
|
|
|
637
650
|
sessions: UsageLedgerSession[];
|
|
638
651
|
/** 已应用的一次性配置迁移 id 列表(随文档落盘;缺省 = 尚未跑过任何迁移)。 */
|
|
639
652
|
appliedMigrations?: string[];
|
|
653
|
+
/**
|
|
654
|
+
* 拒读会话负缓存(issue #105):读失败(含格式拒读)会话的失败时 stamp。落盘
|
|
655
|
+
* 后重启不再重读——宿主对读不动的日志要解压解析到拒绝点才抛错,单条数百 ms、
|
|
656
|
+
* 老用户上百条时是每次启动的固定大头(负缓存此前只在内存,重启即失)。stamp
|
|
657
|
+
* 变化(日志被改写/追加)后照常失效重试;会话消失由 seen 清理兜底。
|
|
658
|
+
*/
|
|
659
|
+
refused?: UsageLedgerRefusal[];
|
|
660
|
+
}
|
|
661
|
+
/** 账本里的一条拒读记录:会话 id + 失败时的失效键 + 是否格式拒读(计数细分用)。 */
|
|
662
|
+
export interface UsageLedgerRefusal {
|
|
663
|
+
id: string;
|
|
664
|
+
stamp: string;
|
|
665
|
+
format: boolean;
|
|
640
666
|
}
|
|
641
667
|
/**
|
|
642
668
|
* 多实例账本合并(自审计延期项:save 路径 last-writer-wins 丢写):同一
|
|
@@ -666,6 +692,9 @@ export declare function mergeUsageLedgerDocuments(document: UsageLedgerDocument,
|
|
|
666
692
|
* 持久账本行据此区分新旧算法:日志已删/不可读而只能沿用旧行时,UI 标注置信度提示。
|
|
667
693
|
*/
|
|
668
694
|
export declare const FOLD_VERSION = 23;
|
|
695
|
+
/** 账本里拒读记录的条数上限:防异常环境(大量损坏日志)把账本撑爆;
|
|
696
|
+
* 超限按 Map 插入序保留前 N 条(最早发现的先留,stamp 失效的自然被清理让位)。 */
|
|
697
|
+
export declare const LEDGER_REFUSED_CAP = 500;
|
|
669
698
|
/**
|
|
670
699
|
* 一次性账本迁移:id 唯一,apply 在加载边界对原始文档执行,已应用过的跳过。
|
|
671
700
|
* 未来账本/schema 字段变更(重命名、拆桶、语义调整)时,在此追加一条迁移并
|
package/lib/types/config.d.ts
CHANGED
|
@@ -96,5 +96,13 @@ export interface UsageBillingConfig {
|
|
|
96
96
|
* 如 `{ "hy4-preview": "hunyuan" }`)。聚合折叠与客户端渲染共用同一映射。
|
|
97
97
|
*/
|
|
98
98
|
modelKeyAliases?: Record<string, string>;
|
|
99
|
+
/**
|
|
100
|
+
* 启动后延迟全量折叠的时长(毫秒,issue #105):聚合器创建起 `replayDelayMs`
|
|
101
|
+
* 之内,`/api/billing/*` 只出账本轻文档(不读任何会话日志,面板以「数据滞后」
|
|
102
|
+
* 横幅提示,复用 issue #103 的 staleSnapshot 通道),到点后预热定时器照常完整
|
|
103
|
+
* 折叠。默认 20 000(20 秒);重账本用户(数千会话 / 数十 MB 日志)可调大到
|
|
104
|
+
* 180 000;0 = 旧行为(随到随折,首屏轮询即触发全量折叠)。
|
|
105
|
+
*/
|
|
106
|
+
replayDelayMs?: number;
|
|
99
107
|
}
|
|
100
108
|
//# sourceMappingURL=config.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenz1117/dsh-ui-usage-billing",
|
|
3
3
|
"description": "Usage billing dashboard for DeepSeek Harness: sidebar cost metrics plus a full dashboard modal, priced from a current multi-provider catalog with real usage aggregated from session logs.",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"deepseek",
|
|
@@ -82,7 +82,8 @@
|
|
|
82
82
|
"0.2.1-alpha.5": "compatible",
|
|
83
83
|
"0.2.1-rc.1": "compatible",
|
|
84
84
|
"0.2.1-rc.2": "compatible",
|
|
85
|
-
"0.2.1-rc.3": "compatible"
|
|
85
|
+
"0.2.1-rc.3": "compatible",
|
|
86
|
+
"0.2.1-rc.4": "compatible"
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
},
|
|
@@ -134,39 +135,39 @@
|
|
|
134
135
|
"@deepseek-ai/cordis": "*",
|
|
135
136
|
"@deepseek-ai/cordis-plugin-include": "^1.0.7",
|
|
136
137
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.5",
|
|
137
|
-
"@deepseek-ai/dsh-agent": "0.2.1-rc.
|
|
138
|
-
"@deepseek-ai/dsh-api-gateway": "0.2.1-rc.
|
|
139
|
-
"@deepseek-ai/dsh-api-remotes": "0.2.1-rc.
|
|
140
|
-
"@deepseek-ai/dsh-api-session-controller": "0.2.1-rc.
|
|
141
|
-
"@deepseek-ai/dsh-atomic-write": "0.2.1-rc.
|
|
142
|
-
"@deepseek-ai/dsh-client-locale": "0.2.1-rc.
|
|
143
|
-
"@deepseek-ai/dsh-client-store": "0.2.1-rc.
|
|
144
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-rc.
|
|
145
|
-
"@deepseek-ai/dsh-client-ui-commands": "0.2.1-rc.
|
|
146
|
-
"@deepseek-ai/dsh-client-ui-workspace": "0.2.1-rc.
|
|
147
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.
|
|
148
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-rc.
|
|
149
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-rc.
|
|
150
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.
|
|
151
|
-
"@deepseek-ai/dsh-credentials": "0.2.1-rc.
|
|
152
|
-
"@deepseek-ai/dsh-home-paths": "0.2.1-rc.
|
|
153
|
-
"@deepseek-ai/dsh-host-webserver": "0.2.1-rc.
|
|
138
|
+
"@deepseek-ai/dsh-agent": "0.2.1-rc.4",
|
|
139
|
+
"@deepseek-ai/dsh-api-gateway": "0.2.1-rc.4",
|
|
140
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.1-rc.4",
|
|
141
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-rc.4",
|
|
142
|
+
"@deepseek-ai/dsh-atomic-write": "0.2.1-rc.4",
|
|
143
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-rc.4",
|
|
144
|
+
"@deepseek-ai/dsh-client-store": "0.2.1-rc.4",
|
|
145
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-rc.4",
|
|
146
|
+
"@deepseek-ai/dsh-client-ui-commands": "0.2.1-rc.4",
|
|
147
|
+
"@deepseek-ai/dsh-client-ui-workspace": "0.2.1-rc.4",
|
|
148
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.4",
|
|
149
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-rc.4",
|
|
150
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-rc.4",
|
|
151
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.4",
|
|
152
|
+
"@deepseek-ai/dsh-credentials": "0.2.1-rc.4",
|
|
153
|
+
"@deepseek-ai/dsh-home-paths": "0.2.1-rc.4",
|
|
154
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.1-rc.4",
|
|
154
155
|
"@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
|
|
155
|
-
"@deepseek-ai/dsh-llm": "0.2.1-rc.
|
|
156
|
-
"@deepseek-ai/dsh-ptc-runtime": "0.2.1-rc.
|
|
157
|
-
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.
|
|
158
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.
|
|
159
|
-
"@deepseek-ai/dsh-scope": "0.2.1-rc.
|
|
160
|
-
"@deepseek-ai/dsh-session": "0.2.1-rc.
|
|
161
|
-
"@deepseek-ai/dsh-session-persistence": "0.2.1-rc.
|
|
162
|
-
"@deepseek-ai/dsh-settings": "0.2.1-rc.
|
|
163
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.
|
|
164
|
-
"@deepseek-ai/dsh-token-meter": "0.2.1-rc.
|
|
165
|
-
"@deepseek-ai/dsh-tools": "0.2.1-rc.
|
|
166
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.
|
|
167
|
-
"@deepseek-ai/dsh-user-approval": "0.2.1-rc.
|
|
168
|
-
"@deepseek-ai/dsh-util-code-language": "0.2.1-rc.
|
|
169
|
-
"@deepseek-ai/dsh-util-workspace-path": "0.2.1-rc.
|
|
156
|
+
"@deepseek-ai/dsh-llm": "0.2.1-rc.4",
|
|
157
|
+
"@deepseek-ai/dsh-ptc-runtime": "0.2.1-rc.4",
|
|
158
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.4",
|
|
159
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.4",
|
|
160
|
+
"@deepseek-ai/dsh-scope": "0.2.1-rc.4",
|
|
161
|
+
"@deepseek-ai/dsh-session": "0.2.1-rc.4",
|
|
162
|
+
"@deepseek-ai/dsh-session-persistence": "0.2.1-rc.4",
|
|
163
|
+
"@deepseek-ai/dsh-settings": "0.2.1-rc.4",
|
|
164
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.4",
|
|
165
|
+
"@deepseek-ai/dsh-token-meter": "0.2.1-rc.4",
|
|
166
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.4",
|
|
167
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.4",
|
|
168
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-rc.4",
|
|
169
|
+
"@deepseek-ai/dsh-util-code-language": "0.2.1-rc.4",
|
|
170
|
+
"@deepseek-ai/dsh-util-workspace-path": "0.2.1-rc.4",
|
|
170
171
|
"@deepseek-ai/schemastery": "^3.18.4",
|
|
171
172
|
"@playwright/test": "^1.64.1",
|
|
172
173
|
"@shikijs/langs": "^4.3.1",
|