@kenz1117/dsh-ui-usage-billing 1.2.9 → 1.4.0

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 CHANGED
@@ -30,74 +30,82 @@
30
30
 
31
31
  ![Demo](screenshots/demo.gif)
32
32
 
33
- ## ✨ Highlights
33
+ ## ✨ Why this plugin
34
34
 
35
- - **Real usage, no fabricated samples** the server aggregates from persisted session logs and estimates against live multi-provider official prices; it shows an empty snapshot until real data arrives.
36
- - **Everything on one screen** — a sidebar trigger card plus a full dashboard (Overview / Trends / Providers / Stats / Rates / Settings) across six tabs: month / today / projection / heatmap / trend.
37
- - **Subscriptions · balance · quota · reconcile** — plan quota, multi-provider balance, relay-station quota, declared endpoints and balance-delta reconciliation form a cross-verifiable billing loop.
38
- - **Peak/off-peak pricing + switch alerts** — weekday peak split and weekend all-day off-peak, **priced per official change boundary** (base price before 08-17, weekend peak hours 08-17~08-23, weekend all-day off-peak from 08-23), with a popover / system notification before a tier switch, configurable lead time.
39
- - **Offline & self-contained** — no chart library, no external CDN, pure design tokens; lightweight and ready to use.
40
- - **Multi-language + dual currency** — Chinese / English, ¥/$ toggle that only affects this plugin.
35
+ Most billing plugins stop at "token count × unit price". dsh-ui-usage-billing turns billing into a **reconcilable ledger pipeline** real usage, live prices, and peak/off-peak awareness that follows the model.
41
36
 
42
- ## ⚖️ How it differs from similar plugins
37
+ ### Real usage you can reconcile
38
+ Usage is aggregated live from persisted session logs — never fabricated (an empty snapshot shows until real data arrives); daily official-balance deltas are cross-checked against the local ledger, and deviations beyond the threshold prompt a review — a bill that survives scrutiny.
43
39
 
44
- Peer plugins (cost-meter, usage-stats, dsh-bill, …) each have their strengths; this plugin's positioning:
40
+ ### Live prices, history never rewritten
41
+ A live models.dev catalog + a built-in catalog of 77 models across 24 vendors + user-defined prices in the settings panel (bindable per relay origin) mean new models never wait for a release; DeepSeek time-of-day prices are **segmented by official change boundaries** (base price before 08-17, weekend peak hours 08-17~08-23, weekend all-day off-peak from 08-23) and later price changes never rewrite old bills.
45
42
 
46
- - **Live pricing sources** — a real-time models.dev catalog + a built-in catalog of 77 models across 24 vendors + user-defined prices in the settings panel (bindable per relay origin); new models don't wait for a hand-edited price table.
47
- - **History is never recomputed** every call is priced at its own moment, segmented by official change boundaries (base price before 08-17 → peak/off-peak v1 weekend all-day off-peak from 08-23); pre-install history is backfilled from existing session logs, and later price changes never rewrite old bills.
48
- - **Not just "how much" but "on what"** — input split by cache hit/miss (including reasoning), official vs third-party buckets, drill-down by workspace/session/relay site, per-turn cost-spike attribution; plus a performance panel few peers offer (per-model TTFT mean/P50/P90 and generation speed).
49
- - **Subscription & balance loop** — 7 official provider balances + Coding Plan quotas + relay-station balances and rolling quota windows + self-declared endpoints + cross-reconciliation between official balance deltas and the local ledger.
50
- - **Uncatalogued models stay visible** they are explicitly marked "uncatalogued" and counted in the Hero hint; a custom price takes effect immediately instead of silently billing 0 or charging the wrong model's rate.
51
- - **The model can check for itself** — an optional `usage_stats` tool lets the model answer "what did I spend today" or "which site used the most".
43
+ ### Peak/off-peak aware, alerts follow the model
44
+ The billing channel is detected from the current session's model: DeepSeek metered uses time-of-day prices (weekday 9-12 / 14-18 peak ×2, weekend all-day off-peak), Zhipu Coding Plan uses credit windows (weekday 14-18 peak at full rate, off-peak at **50% of base credits**) the channel layer is extensible for more providers; a popover / system notification fires automatically before each switch, and only sessions whose current model actually involves peak/off-peak get alerted or show the tier section — no watching the clock.
45
+
46
+ ### Subscriptions, balances and quotas on one screen
47
+ 7 official provider balances (DeepSeek / Kimi / Zhipu GLM / Tencent Cloud TokenHub / …), Coding Plan quotas, relay-station rolling quota windows, self-declared endpoints, plus balance-delta reconciliation what the plan deducted and what the balance deducted, verifiable side by side.
48
+
49
+ ### Details few peers offer
50
+
51
+ - Not just "how much" but "on what": input split by cache hit/miss (including reasoning), official vs third-party buckets, drill-down by workspace/session/relay site, per-turn cost-spike attribution.
52
+ - A performance panel: per-model TTFT mean/P50/P90 and generation speed.
53
+ - Uncatalogued models are explicitly marked and never silently billed 0; one alias entry prices an out-of-catalog model.
54
+ - An optional `usage_stats` tool lets the model answer "what did I spend today" or "which site used the most".
55
+ - A pure UI surface: no tools registered, no system-prompt injection, no model-visible log events.
56
+ - Chinese / English and ¥ / ≈$ toggles; no chart library, no external CDN, offline & self-contained.
52
57
 
53
58
  ## 📊 Dashboard
54
59
 
55
- - **Sidebar entry**: a dashboard-style trigger card above the Settings button — month cost as the headline number (monospace) with a 7-day sparkline mini-trend, second line "Today / This week"; collapses to an icon button; hover reveals a quick-look card.
56
- - **Billing dashboard (tabbed)**: Overview / Trends / Providers / Stats / Rates / Settings — hero figures + YoY/DoD + month projection + KPI×4 + heatmap; 7/30-day trend (switches between Cost / Tokens); provider (channel) billing & subscriptions; export / cost breakdown / workspaces / session detail; model rate table; budget & peak alerts. Restrained tones, `--dsw-*` tokens, dark/light adaptive.
60
+ - **Sidebar trigger card**: persistent above the Settings button — month cost as the headline number with a 7-day sparkline mini-trend, second line "Today / This week"; collapses to an icon button; hover reveals a quick-look card.
61
+ - **Six-tab dashboard**: Overview / Trends / Detail / Stats / Rates / Settings — hero figures + comparisons + month projection + KPIs + usage heatmap, 7/30-day trends switching cost / tokens, plus the model rate table, budget and peak/off-peak alerts; restrained tones, dark/light adaptive.
57
62
 
58
63
  ![Overview: month cost hero, budget progress, KPIs and usage heatmap](screenshots/1.png)
59
- - **Live cost bar**: below the composer, persistent "This turn ¥x · Session ¥y" plus the peak/off-peak tier & switch countdown and subscription low-quota chips (≤20% appear, ≤10% red); the whole capsule can be hidden via the settings-tab "Live cost capsule" toggle (display-only preference persisted locally; stats and alerts are unaffected).
60
- - **Peak/off-peak switch alert**: a popover before a tier switch plus an optional system notification (lead time / position / mode / preview configurable), distinguishing "About to enter peak ×2 can wait" / "About to enter off-peak, price halves".
61
- - **Plugin info card**: a persistent "About" card in the Settings tab — name, description, author (jump to GitHub), source repo, npm, MIT license, version (read server-side from the package's `package.json`, single source of truth, correct on publish).
64
+ - **Live cost bar**: persistent "this turn / session" cost below the composer; 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).
65
+ - **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%).
66
+ - **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.
62
67
 
63
68
  ## 💰 Billing engine
64
69
 
65
- - **Provider-first grouping**: the billing section groups usage by the llm entry the calls actually went through (channel) — Tencent Cloud TokenHub / Token Plan / DeepSeek official / direct:<route> / unknown routes; the model brand remains as the row logo + sub-line. Official-channel judgement follows the channel origin (`api.deepseek.com`) instead of the route name, so gateway routes named `deepseek-*` no longer count as official traffic. `routeAliases` relocates renamed/deleted historical routes; `modelKeyAliases` binds uncatalogued model ids to catalog keys (date-stamped `deepseek-v4-flash-202605`, org-prefixed `deepseek/deepseek-v4-flash` and the TokenHub short id `hy3` are recognized out of the box).
66
- - **Live-priced rate table**: models.dev fetched pricing + live-model alignment — the models actually configured are all included; peak/off-peak split (weekdays 9-12 / 14-18 peak ×2, weekends off-peak all day) plus a live USD→CNY rate, refreshed every 6 hours.
70
+ - **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).
71
+ - **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).
67
72
 
68
73
  ![Rates: model rate table (peak/off-peak split and live rate)](screenshots/5.png)
69
- - **Official vs third-party buckets**: the detail cost column is split by official DeepSeek direct / third-party relay ("official x / third y" when mixed); the Stats tab has an official/third-party summary card. Official judgement is channel-origin based (`api.deepseek.com`); web-search assist calls count as official.
70
- - **Monthly budget + tier alerts**: a budget bar (on/off / amount / progress, ≥80% amber, over red pulse); notifies once per tier crossing 50/80/100%.
71
- - **Cost-spike attribution**: per-turn cost bars (last 40 turns, amount at bar top, peak/off-peak background bands, >2× spike flagged with attribution).
74
+ - **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.
75
+ - **Official vs third-party buckets**: the detail cost column splits official direct / third-party relay ("official x / third y" when mixed); the Stats tab has a summary card; web-search assist calls count as official.
76
+ - **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.
77
+ - **Cost-spike attribution**: last-40-turn cost bars, amount at bar top, peak/off-peak background bands, >2× spikes flagged with attribution.
72
78
 
73
79
  ## 🔌 Subscriptions & balance
74
80
 
75
- - **Subscription quota**: detects subscription providers in `llm-pi-ai` (Kimi / Z.ai / OpenCode Go / MiniMax / OpenRouter / Xiaomi / Volcano…); those with a quota API show remaining % and reset time live, exhausted in red, no API shown as "not wired"; subscription-channel model cost is 0. **MiniMax note**: use the `minimax-token-plan-cn` provider id for `https://api.minimaxi.com`; the international route keeps `minimax` / `minimax-token-plan` against `https://www.minimaxi.com`. Override `baseUrl` per provider for proxies or staging.
76
- - **Multi-provider balance**: DeepSeek / Kimi / StepFun / SiliconFlow / xAI / Zhipu GLM (Z.ai CN region) built-in official balances, and the balance column estimates "≈N days" from the 7-day daily burn.
77
- - **Custom provider balance**: configure any HTTP endpoint for balance (`extract` supports constant / dot-path / add-subtract / divide, header `{{ENV}}` via the credentials seam).
78
- - **Declared endpoints + balance reconcile**: **declared endpoints** (`declaredEndpoints`) let you self-declare balance/quota interfaces for vendors absent from the built-in table — you write only dot-paths ("where the number is"), no expressions; the request URL is built from the matched same-origin provider's `origin`, and safety bounds (single-slash absolute path, GET only, reject cross-origin redirects, response-size/timeout caps, credentials only from the matched provider's own `apiKeyEnv`) are enforced by `src/declarative.ts`; a wrong path is shown in the UI as `declared` with a `reason`. **Balance reconcile** (`reconcilePath`) cross-checks the official (DeepSeek-direct only) balance change against the local ledger's official-channel cost for the day, and flags a drift above the threshold (0.3 CNY and >15%) so you can double-check the price table or recent bills; top-ups / grants / currency changes reset the baseline instead of alerting, and a flat balance (subscription spend) stays silent.
79
- - **Relay-site attribution & quota**: usage is grouped by a provider's `baseURL` origin — multiple keys on the same relay station merge into one row, named by its domain. Routes with a `baseURL` are auto-detected as New API (`/api/status`) or Sub2API (`/v1/usage`) to read their **balance and rolling quota windows**, labeled "no quota" when unreadable, <20% remaining in red; station recognition is cached for 5 minutes (multiple keys on one station fuse-break independently), and the `relay-quotas` endpoint attaches `diagnostics` for "why is my relay not showing". Project attribution prefers the workspace title for naming. **Unpriced models** (out-of-catalog / no price) count as 0 cost, with a "N models not priced" hint under the hero.
81
+ - **Subscription quota**: auto-detects subscription providers (Kimi / Z.ai / OpenCode Go / MiniMax / OpenRouter / Xiaomi / Volcano…); those with a quota API show remaining % and reset time live, exhausted in red, otherwise "not wired"; subscription-channel model cost is 0, and plan tiers are recognized by the built-in knowledge base (e.g. OpenCode Go $10/mo + $30 weekly). **MiniMax note**: use `minimax-token-plan-cn` for the CN domain (`api.minimaxi.com`), `minimax` / `minimax-token-plan` international; override `baseUrl` per provider if needed.
82
+ - **Multi-provider balance**: built-in official balances for DeepSeek / Kimi / StepFun / SiliconFlow / xAI / Zhipu GLM, with an "≈N days" estimate from the 7-day daily burn; **Tencent Cloud TokenHub Token Plan** goes through the cloud-API control plane (TC3-signed, `src/tc3.ts`) — set the credential to a `<SecretId>:<SecretKey>` key pair (not the inference key) and name the route `tencent-tokenhub` / `tokenhub` / `tencent` / `tencentcloud`; a percentage window is produced only when both remaining and total quotas parse (never guessed).
83
+ - **Custom provider balance**: configure any HTTP endpoint (`extract` supports constant / dot-path / arithmetic, header `{{ENV}}` via the credentials seam).
84
+ - **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.
85
+ - **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.
80
86
 
81
87
  ![Providers: provider billing & subscriptions (balance, plan quota, model usage)](screenshots/3.png)
82
88
 
89
+ > For the full adapter matrix per channel (detection / endpoints / credentials / troubleshooting), see [docs/adapters.md](docs/adapters.md).
90
+
83
91
  ## 📈 Usage visualizations
84
92
 
85
- - **Session detail + cost spikes + heatmap**: sessions sorted by cost (title / project / calls / cost / last active); per-turn cost bars (peak/off-peak background bands, >2× spike flagged with attribution); month / year calendar heatmap (5-color scale, hover detail; the year view is ~52 weeks, GitHub-style), with active-day and streak counts on top.
86
- - **Performance metrics**: per-model first-token latency (TTFT) mean / P50 / P90, generation speed (tokens/s), total-latency mean; per-hour × per-model comparison curve — metric tabs (TTFT / tok/s), clickable model chips (top-5 by samples lit by default, one-click select-all), hover snapping to the nearest hour with a crosshair and per-model tooltip, broken lines for missing-sample hours (never fabricated); view preferences persist locally.
87
- - **Token insights**: a dedicated "Tokens" tab — the daily token chart switches between two views: "Structure" stacks "input (cache miss) / input (cache hit) / output" (including reasoning), while "By model" stacks each day by model (same per-model colors as the trends tab; the toggle hides itself when the snapshot carries no per-day-per-model detail); the hover tooltip shows the day's exact breakdown (per-bucket in structure view, per-model "hit / miss / output" in model view, thousand-separated); clicking a legend swatch or a model-table row focuses that model (other segments dim, y-axis unchanged, click again to release); per-model totals and share, structural KPIs (cache-hit rate / reasoning share / input-output ratio / peak day); per-day token CSV and JSON export (JSON includes the per-day-per-model detail).
93
+ - **Session detail + heatmap**: sessions sorted by cost (title / project / calls / cost / last active); 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.
94
+ - **Performance metrics**: 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.
95
+ - **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); structural KPIs (cache-hit rate / reasoning share / input-output ratio / peak day); per-day CSV and JSON export (JSON includes per-day-per-model detail).
88
96
 
89
97
  ![Trends: daily cost trend, per-turn costs and peak/off-peak share](screenshots/2.png)
90
- - **Export + offline self-contained**: the Stats tab exports daily / per-session / per-site CSV and full JSON; cost breakdown / workspaces / session-detail sections are drillable (click a project row to expand its sessions); no chart library, no external CDN, pure design tokens.
98
+ - **Export + drill-down**: daily / per-session / per-site CSV and full JSON from the Stats tab; cost breakdown / workspaces / session detail drillable (click a project row to expand its sessions); no chart library, no external CDN, pure design tokens.
91
99
 
92
100
  ![Stats: export, cost breakdown, workspaces and session detail](screenshots/4.png)
93
101
 
94
102
  ## 🛡️ Robustness & privacy
95
103
 
96
- - **Real usage aggregation**: the server aggregates from session logs on demand (incremental cache recomputes only written sessions), with per-session corruption tolerance and snapshot fallback; an optional `usage_stats` tool lets the model query today / month / current session / cumulative spend, plus `bySite` (relay-attributed) and `relay` (relay-only) summaries (toggle it in Settings; takes effect after a reload).
97
- - **Model health + uncatalogued annotation**: provider connection dots (green / red / grey); a model id not in the catalog is marked "uncatalogued" priced at the fallback, with provider inferred (e.g. `mi-mimo-2.5` → Xiaomi); estimated-price models are marked "estimated".
104
+ - **Real usage aggregation**: incremental server-side aggregation (only written sessions recompute), per-session corruption tolerance, snapshot fallback; the optional `usage_stats` tool queries today / month / current session / cumulative spend, plus `bySite` (relay-attributed) and `relay` (relay-only) summaries.
105
+ - **Model health + uncatalogued annotation**: provider connection dots (green / red / grey); uncatalogued models are marked and priced at the fallback, with the provider inferred (e.g. `mi-mimo-2.5` → Xiaomi); estimated-price models are labeled "estimated".
98
106
  - **Multi-language + dual currency**: the ¥/$ switch is bilingual (USD→English, CNY→Chinese, this plugin only); the rate table converts to the selected currency.
99
- - **Security hardening**: every HTTP endpoint enforces loopback-only access via a dual check of the peer socket address and an exact Host-header match (literal loopback IP, not a prefix), rejecting `127.0.0.1.evil.com`-style DNS-rebinding names; the `/api/billing/usage-tool` write path additionally validates a loopback Origin and `application/json` Content-Type with a body-size cap to block cross-site rewrites; balance / subscription / relay queries and pricing fetches carry bounded retries with per-upstream circuit breaking (auth failures are config issues and do not trip the breaker).
100
- - **Export injection guard**: daily / session / site CSV cells starting with `=` / `+` / `-` / `@` are prefixed with a single quote, and commas / quotes / newlines / carriage returns are fully escaped, so they cannot be executed as formulas in Excel / WPS.
107
+ - **Security hardening**: every HTTP endpoint enforces loopback-only access via a dual check of the peer socket address and an exact Host-header match, rejecting `127.0.0.1.evil.com`-style DNS-rebinding names; write paths additionally validate a loopback Origin and Content-Type with a body-size cap against cross-site rewrites; balance / subscription / pricing fetches carry bounded retries with per-upstream circuit breaking (auth failures are config issues and do not trip the breaker).
108
+ - **Export injection guard**: CSV cells starting with `=` / `+` / `-` / `@` get a leading single quote and full escaping, so they cannot execute as formulas in Excel / WPS.
101
109
  - **Privacy baseline**: a pure UI surface — registers no tools, injects no system prompt, writes no model-visible events; it only aggregates from existing session logs, whose content is owned by other packages.
102
110
 
103
111
  ## 🚀 Quick start
@@ -146,10 +154,6 @@ Browser Server (Node)
146
154
  - **Server** (`src/index.ts`): injects `webServer`, `sessionPersistence` and `credentials`, and registers `GET /api/billing/usage-stats`, `/api/billing/pricing`, `/api/billing/balance`, `/api/billing/subscriptions`, `/api/billing/relay-quotas`. The aggregator caches folded results per session: each LLM call is attributed to the model of its preceding `request/header`, tokens split into cache-hit / cache-miss buckets, dates bucketed by the local timezone; a log file with unchanged mtime+size reuses its cached fold, only written sessions are re-folded, and the whole document has a 5s TTL to coalesce heavy polling. Every successfully folded session is also atomically written to an independent durable usage ledger, so permanently deleting a session no longer removes its historical cost or tokens. Aggregation logic lives in `src/aggregate.ts`.
147
155
  - **Browser** (`src/client/`): requests the endpoints above to render the dashboard and probes each provider connection via `llm.models`. Until real data arrives it shows an all-zero empty snapshot, never fabricated samples.
148
156
 
149
- ## 🧩 Theme collaboration
150
-
151
- This plugin **depends on no theme package** and runs standalone. The dashboard modal declares a `billing.dashboard.decor` decoration slot (head / hero / trend / models / footer anchors) and registers a real-time cost summary as the `ctx.billingMetrics` service: theme plugins (e.g. acid-zine) inject their own decoration visuals (MacDots, tape, torn notes…) and subscription-cost data into their sticker layer. Plugin and theme load/unload independently — with no theme the default visuals apply; without billing the theme still runs.
152
-
153
157
  ## 💡 Billing details
154
158
 
155
159
  The rate table (`src/client/pricing.ts`) stores each model in its **native currency**: domestic providers enter CNY directly, overseas providers enter USD. Cost is computed and displayed in CNY uniformly — USD models convert via the **live rate**, domestic models never pass through a rate. At startup the server fetches the live rate and model prices (`src/pricing-fetch.ts`): USD→CNY prefers the Tencent Finance quote (keyless, reachable in China), falling back to open.er-api then the built-in default; it then refreshes every 6 hours, and the rate-table modal shows a "today's rate" marker plus live / built-in badge. The **display currency follows the user**: switching ¥ / $ converts each per-1M-token unit price via `convertUnitPrice` at the live rate (falling back to the native currency when the rate is unavailable).
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  <p align="center">把每一分模型开销,看得清清楚楚。</p>
6
6
 
7
7
  <p align="center">
8
+ <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>
8
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>
9
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>
10
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>
@@ -29,76 +30,83 @@
29
30
 
30
31
  ![演示](screenshots/demo.gif)
31
32
 
32
- ## ✨ 核心亮点
33
+ ## ✨ 为什么选它
33
34
 
34
- - **真实用量,不伪造样本** 服务端从持久化会话日志实时聚合,按实时多厂商官方价估算;数据到达前显示空快照,绝不展示假数据。
35
- - **一屏看懂一切** — 侧边栏触发卡 + 全屏仪表盘(概览 / Token / 用量 / 趋势 / 费率 / 设置)六区,本月/今日/预计/热力图/趋势全在。
36
- - **订阅 · 余额 · 额度 · 对账** — 订阅套餐额度、多厂商余额、中转站额度、声明端点、余额差对账,形成可交叉验证的计费闭环。
37
- - **峰谷计价 + 切换提醒** — 工作日峰谷分时、周末全天低谷,**按官方变更节点分段计价**(8-17 前基础价、8-17~8-23 周末计峰、8-23 起周末全谷),切档前弹窗/系统通知,提前量可配。
38
- - **离线自包含** — 无图表库、无外部 CDN、纯设计令牌;依赖极轻,随装随用。
39
- - **多语种 + 双币种** — 中文/English、¥/≈$ 切换,只对本插件生效。
35
+ 市面计费插件大多停在「token × 单价」。dsh-ui-usage-billing 把计费做成一条**可对账的账本链路**——用量是真的、价格是活的、峰谷跟着模型走。
40
36
 
41
- ## ⚖️ 与同类计费插件的差异
37
+ ### 账是真的,还能对账
38
+ 用量从持久化会话日志实时聚合,绝不伪造样本(数据到达前显示空快照);官方余额当日变动与本地账本交叉对账,偏差超阈值主动提示核对——账单经得起质疑。
42
39
 
43
- 同类插件(cost-meter、usage-stats、dsh-bill 等)各有所长,本插件的定位差异如下:
40
+ ### 价格是活的,历史不重算
41
+ models.dev 实时目录 + 内置 24 厂商 77 款模型 + 设置面板自定义单价(可按中转站绑定同模型不同价),新模型无需等发版;DeepSeek 分时价按官方变更节点**分段计价**(8-17 前基础价、8-17~8-23 周末计峰、8-23 起周末全谷),价格调整永不回写旧账。
44
42
 
45
- - **价格来源是活的** — models.dev 实时目录 + 内置 24 厂商 77 款模型目录 + 设置面板自定义单价(可按中转站来源绑定同模型不同价),新模型无需等待手填价表。
46
- - **历史永不重算** — 费用按事件发生时刻的价格计价,并按官方变更节点分段(8-17 前基础价 峰谷 v1 8-23 起周末全谷);安装前的历史用量从既有会话日志自动回填,价格调整不回写旧账。
47
- - **不止「花了多少」,还答「花在哪」** — 输入按缓存命中/未命中分桶(含 reasoning)、官方/三方分桶、按工作区/会话/中转站下钻、每轮成本突增归因;还有同类少有的性能面板(各模型 TTFT 均值/P50/P90 与生成速度)。
48
- - **订阅与余额闭环** — 7 家官方余额 + Coding Plan 额度 + 中转站余额与滚动额度窗口 + 自声明端点 + 官方余额变动与本地账本的交叉对账。
49
- - **目录外模型不静默** 未收录模型显著标注「未收录」并在 Hero 区提示数量,自定义单价填入即生效,不会被静默计 0 或错按其他模型价格。
50
- - **模型可自查** — 可选的 `usage_stats` 工具让模型直接回答「今天花了多少」「哪个站点用得最多」。
43
+ ### 峰谷全感知,提醒跟着模型走
44
+ 计费通道按当前会话使用的模型自动识别:DeepSeek 按量走分时价(工作日 9-12 / 14-18 高峰 ×2、周末全天低谷),智谱 Coding Plan 走积分峰谷(工作日 14-18 高峰全额、非高峰**积分 5 折**),通道层可扩展、更多厂商逐步接入;切档前弹窗 / 系统通知自动提醒,且只有当前模型真正涉及峰谷才会提醒、费用条才显示档位——不用你盯时间表。
45
+
46
+ ### 订阅、余额、额度一屏闭环
47
+ DeepSeek / Kimi / 智谱 GLM / 腾讯云 TokenHub 等 7 家官方余额、Coding Plan 额度、中转站滚动额度窗口、自声明端点,再加余额差对账——订阅扣的和余额扣的同屏可查、交叉验证。
48
+
49
+ ### 还有这些同类少见的细节
50
+
51
+ - 不止「花了多少」还答「花在哪」:输入按缓存命中 / 未命中分桶(含 reasoning)、官方 / 三方分桶、按工作区 / 会话 / 中转站下钻、每轮费用突增归因。
52
+ - 性能面板:各模型首字延时(TTFT)均值 / P50 / P90 与生成速度,同类少有。
53
+ - 未收录模型显著标注「未收录」、绝不静默计 0;目录外模型配一条别名即完成识别与计价。
54
+ - 可选的 `usage_stats` 工具让模型直接回答「今天花了多少」「哪个站点用得最多」。
55
+ - 纯 UI surface:不注册工具、不注入系统提示、不写模型可见事件。
56
+ - 中文 / English、¥ / ≈$ 双语双币切换;无图表库、无外部 CDN、离线自包含。
51
57
 
52
58
  ## 📊 仪表盘
53
59
 
54
- - **侧边栏入口**:设置按钮上方的仪表盘式触发卡——本月费用主数字(等宽字体)+ 近 7 天 sparkline 迷你趋势,副行「今日 / 本周」;折叠栏自动切为图标钮;悬停浮现速览卡。
55
- - **计费仪表盘(分区 Tab)**:概览 / 趋势 / 明细 / 统计 / 费率 / 设置 六区——Hero 大数字 + 本年/今日环比 + 本月预计 + KPI×4 + 热力图;趋势图 7/30 天(可切费用 / Token);提供商计费与订阅(按调用通道分组);导出 / 费用构成 / 工作区 / 会话明细;模型单价表;预算与峰谷提醒。克制冷调、`--dsw-*` 令牌、深浅主题自适应。
60
+ - **侧边栏触发卡**:设置按钮上方常驻本月费用主数字 + 近 7 天 sparkline 迷你趋势,副行「今日 / 本周」;折叠栏自动切为图标钮,悬停浮现速览卡。
61
+ - **六区仪表盘**:概览 / 趋势 / 明细 / 统计 / 费率 / 设置——Hero 大数字 + 环比 + 本月预计 + KPI + 用量热力图,趋势 7/30 天可切费用 / Token,模型单价表、预算与峰谷提醒都在;克制冷调、深浅主题自适应。
56
62
 
57
63
  ![概览:本月费用 Hero、预算进度、KPI 与用量热力图](screenshots/1.png)
58
- - **即时代费用条**:输入框下方常驻「本轮 ¥x · 会话 ¥y」+ 峰谷档位与切换倒计时 + 订阅额度预警 chips(≤20% 浮现、≤10% 红);可在设置 Tab 以「平价消耗胶囊」开关整条隐藏(显示偏好本地持久化,统计与提醒不受影响)。
59
- - **峰/谷切换提醒**:切档前弹窗 + 可选系统通知(提前量 / 位置 / 模式 / 预览可配),区分「即将进峰时 ×2 可稍等」/「即将进平价 价格减半」。
60
- - **插件信息卡**:设置 Tab 常驻「关于」卡——插件名、描述、作者(可跳 GitHub)、源码仓库、npm、许可证 MIT、版本号(服务端读自包 `package.json`,单一来源,发布自动正确)。
64
+ - **即时代费用条**:输入框下方常驻「本轮 / 会话」费用;峰谷档位与切换倒计时只在当前模型涉及峰谷时出现;订阅额度预警 chips 剩余 20% 浮现、≤10% 标红;可在设置 Tab 整条隐藏(偏好本地持久化,统计与提醒不受影响)。
65
+ - **峰 / 谷切换提醒**:切档前弹窗 + 可选系统通知,提前量 / 位置 / 模式 / 预览均可配;文案按计费通道区分(DeepSeek 价格减半、智谱积分 5 折)。
66
+ - **插件信息卡**:设置 Tab 常驻「关于」卡——版本号服务端读自包 `package.json`(单一来源,发布自动正确),作者 / 仓库 / npm / 许可证一键可达。
61
67
 
62
68
  ## 💰 计费引擎
63
69
 
64
- - **提供商优先分组**:计费区块按调用实际发生的 llm 入口(通道)分组——腾讯云 TokenHub / 腾讯云 Token Plan / DeepSeek 官方 / 直连·路由名 / 未知路由,模型品牌只是行内徽标 + 副标;官方判定按通道 origin(`api.deepseek.com`)而非路由名,名为 `deepseek-*` 的网关路由不再被误算官方流量。`routeAliases` 可把改名/删除的历史路由归位,`modelKeyAliases` 可把目录外模型 id 绑定到计费键(日期后缀 `deepseek-v4-flash-202605`、组织前缀 `deepseek/deepseek-v4-flash`、TokenHub 短 id `hy3` 已内置识别)。
65
- - **实时定价费率表**:models.dev 抓价 + 探活模型对标——系统实际配置模型全纳入;峰谷分时(工作日 9-12 / 14-18 高峰 ×2,周末全天低谷;历史费用按官方变更节点分段计价,见下方「计费细节」)+ 实时汇率(USD→CNY),每 6 小时刷新。
66
- - **自定义单价**:设置面板为未收录或变价模型填入实付价(未命中 / 缓存命中 / 输出,可选 USD 与低谷价三栏),总览与日趋势按用户价重估显示;支持按中转站来源绑定同模型不同价(origin 规范化宽松匹配),目录外模型填价即生效。
70
+ - **提供商优先分组**:费用按调用实际发生的 llm 入口(通道)分组——腾讯云 TokenHub / Token Plan / DeepSeek 官方 / 直连·路由名 / 未知路由,模型品牌只是行内徽标 + 副标;官方判定按通道 origin(`api.deepseek.com`)而非路由名,`deepseek-*` 网关路由不再被误算官方。`routeAliases` 归位改名 / 删除的历史路由,`modelKeyAliases` 把目录外模型 id 绑定到计费键(日期后缀、组织前缀、TokenHub 短 id `hy3` 已内置识别)。
71
+ - **实时费率表**:models.dev 抓价 + 探活模型对标,系统实际配置的模型全纳入;峰谷分时(工作日 9-12 / 14-18 高峰 ×2、周末全天低谷,历史费用按官方变更节点分段,见下方「计费细节」)+ 实时汇率(USD→CNY),每 6 小时自动刷新;费率条显示「上次同步」时间并可**一键立即同步**(无需重启宿主)。
72
+ - **自定义单价**:设置面板为未收录或变价模型填实付价(未命中 / 缓存命中 / 输出,可选 USD 与低谷价),总览与日趋势按用户价重估;支持按中转站来源绑定同模型不同价,目录外模型填价即生效。
67
73
 
68
74
  ![费率:模型单价表(峰谷分时与实时汇率)](screenshots/5.png)
69
- - **官方 vs 三方分桶**:明细费用列按官方 DeepSeek 直连 / 第三方中转分解(混合时「官 x / 三 y」),统计 Tab 有「官方/三方」汇总卡;官方判定以通道 origin 为准(`api.deepseek.com`),联网搜索辅助请求计入官方。
70
- - **月度预算 + 分档提醒**:预算条(开关 / 金额 / 进度,≥80% 琥珀、超支红脉);跨 50 / 80 / 100% 各提醒一次;余额折算 CNY 低于阈值每天提醒一次。
71
- - **成本突增归因**:每轮费用柱状图(最近 40 轮、金额贴柱顶、峰谷背景分带、超 2 倍红标归因)。
75
+ - **官方 vs 三方分桶**:明细费用列按官方直连 / 第三方中转分解(混合时「官 x / 三 y」),统计 Tab 有汇总卡;联网搜索辅助请求计入官方。
76
+ - **月度预算 + 分档提醒**:预算条 80% 琥珀、超支红脉;跨 50 / 80 / 100% 各提醒一次;余额折算 CNY 低于阈值每天提醒一次。
77
+ - **成本突增归因**:最近 40 轮费用柱状图,金额贴柱顶、峰谷背景分带、超 2 倍红标归因。
72
78
 
73
79
  ## 🔌 订阅与余额
74
80
 
75
- - **订阅套餐额度**:识别 `llm-pi-ai` 里的订阅类 provider(Kimi / Z.ai / OpenCode Go / MiniMax / OpenRouter / 小米 / 火山…),有额度 API 的实时显示剩余%与重置时间、用尽标红,无 API 标「未接入」;订阅通道模型费用记 0。档位月费与周期额度口径由内置知识库自动识别(如 OpenCode Go $10/月 + 周 $30 额度)。**MiniMax 用户注意**:国内开发者环境请用 `minimax-token-plan-cn`(自动对接 `https://api.minimaxi.com`);国际保留 `minimax` / `minimax-token-plan`(默认 `https://www.minimaxi.com`);需要自配中转或 staging 时可在该 provider 设置里覆盖 `baseUrl`。
76
- - **多厂商余额**:DeepSeek / Kimi / 阶跃星辰 / 硅基流动 / xAI / 智谱 GLM(Z.ai 国内域)内置官方余额,余额列按近 7 天日均折算「约可撑 N 天」;**腾讯云 TokenHub Token Plan** 余量已接入(云 API 管控面,TC3 签名)——凭据值填 `<SecretId>:<SecretKey>`(云 API 密钥对,非 TokenHub 推理 key),推理路由命名为 `tencent-tokenhub` / `tokenhub` / `tencent` / `tencentcloud` 任一即可命中;订阅卡的 **腾讯云 Token Plan** 额度适配器走同一条 TC3 管控面链路(`src/tc3.ts`,`DescribeTokenPlanList → DescribeTokenPlan`)——把 `tencent-token-plan` 路由的 `apiKeyEnv` 凭据设为云 API 密钥对即可实时显示余量/重置,剩余与总额度都可解析时产出百分比窗口(绝不猜总额度),订阅通道模型行同时显示「订阅包含 ≈目录价预估」。
77
- - **自定义 Provider 余额**:配置任意 HTTP 端点查余额(`extract` 支持常量 / 点路径 / add-subtract / divide,请求头 `{{ENV}}` 经凭据 seam)。
78
- - **声明端点 + 余额对账**:**声明端点**(`declaredEndpoints`)为内置表没有的供应商自声明余额/额度接口——只写「数字在哪里」的点路径、无表达式;请求由匹配到同源 provider origin 构造,安全边界(单斜杠绝对路径、仅 GET、拒跨源重定向、响应体/超时上限、凭据只取匹配 provider 自有 `apiKeyEnv`)由 `src/declarative.ts` 强制执行,取错路径在界面标注 `declared` 与 reason。**余额差对账**(`reconcilePath`)用官方(仅 DeepSeek 官方方向)余额当日变动与本地账本当日的官方渠道费用交叉校验,偏差超阈值(0.3 元且 >15%)时提示核对价格表或近期账单;充值 / 授信 / 币种变化重置基准而非告警、余额未减少(走订阅扣费)静默。
79
- - **中转站归组与额度**:按 provider 的 `baseURL` 归一化 origin 归组——同一中转站的多把 key 合并成一行,站名即域名;对配了 `baseURL` 的路由自动识别 New API 系(`/api/status`)与 Sub2API(`/v1/usage`)的**余额与滚动额度窗口**,读不出标「未读出额度」,剩余 <20% 标红;识别结果有 5 分钟指纹缓存(同站多把 key 独立熔断),`relay-quotas` 端点附 `diagnostics` 供「我的中转站为什么不显示」自查。项目归属优先用工作区标题命名。**未计价的模型**(目录外/无价)费用按 0 计,Hero 下会提示「N 个模型未收录计价」。
81
+ - **订阅套餐额度**:自动识别订阅类 provider(Kimi / Z.ai / OpenCode Go / MiniMax / OpenRouter / Claude / CommandCode / 小米 / 火山…),有额度 API 的实时显示剩余 % 与重置时间、用尽标红,无 API 标「未接入」;订阅通道模型费用记 0,档位月费与周期额度由内置知识库识别(如 OpenCode Go $10/月 + 周 $30 额度)。**MiniMax 注意**:国内用 `minimax-token-plan-cn`(自动对接 `api.minimaxi.com`),国际用 `minimax` / `minimax-token-plan`;可在该 provider 设置覆盖 `baseUrl`。**Claude 订阅**:本机登录 Claude Code 后自动发现(读 `~/.claude/.credentials.json` OAuth token),显示 5 小时 / 周窗口用量;llm-pi-ai 里按量 `anthropic` 路由不会被误识别,费用照常按 token 计。**CommandCode**:在 llm-pi-ai 给 `commandcode` 路由配 `apiKeyEnv`(`user_` 前缀 key),显示 5 小时 / 周窗口与月度 Credits。
82
+ - **多厂商余额**:DeepSeek / Kimi / 阶跃星辰 / 硅基流动 / xAI / 智谱 GLM 内置官方余额,按近 7 天日均折算「约可撑 N 天」;**腾讯云 TokenHub Token Plan** 余量与订阅额度走云 API 管控面(TC3 签名,`src/tc3.ts`)——凭据填 `<SecretId>:<SecretKey>` 密钥对(非推理 key),路由命名 `tencent-tokenhub` / `tokenhub` / `tencent` / `tencentcloud` 任一即可命中,剩余与总额度都可解析时才产出百分比窗口(绝不猜总额度)。
83
+ - **自定义 Provider 余额**:配置任意 HTTP 端点查余额(`extract` 支持常量 / 点路径 / 四则运算,请求头 `{{ENV}}` 经凭据 seam)。
84
+ - **声明端点 + 余额对账**:内置表没有的供应商用 `declaredEndpoints` 自声明余额接口——只写「数字在哪里」的点路径、无表达式;安全边界(单斜杠绝对路径、仅 GET、拒跨源重定向、响应体 / 超时上限、凭据只取本 provider)由 `src/declarative.ts` 强制执行,取错路径在界面标 `declared` 与 reason。**余额差对账**(`reconcilePath`)用官方余额当日变动与本地账本交叉校验,偏差超阈值(0.3 元且 >15%)提示核对;充值 / 授信 / 币种变化重置基准而非告警,余额未减少(走订阅扣费)静默。
85
+ - **中转站归组与额度**:按 `baseURL` 归一化 origin 归组,同站多把 key 合并一行、站名即域名;自动识别 New API 系(`/api/status`)与 Sub2API(`/v1/usage`)的余额与滚动额度窗口,读不出标「未读出额度」、剩余 <20% 标红;识别结果 5 分钟指纹缓存(同站多 key 独立熔断),`relay-quotas` 端点附 `diagnostics` 供「为什么不显示」自查;项目归属优先用工作区标题命名。
80
86
 
81
87
  ![明细:厂商计费与订阅(余额、套餐额度、模型用量)](screenshots/3.png)
82
88
 
89
+ > 全部渠道的适配矩阵(识别方式 / 端点 / 凭据要求 / 排查顺序)见 [docs/adapters.md](docs/adapters.md)。
90
+
83
91
  ## 📈 用量可视化
84
92
 
85
- - **会话明细 + 成本突增 + 热力图**:按会话费用倒序(标题 / 项目 / 调用 / 费用 / 最后活跃);每轮费用柱状图(峰谷背景分带、超 2 倍红标归因);月 / 年日历热力图(5 档色阶、悬停明细;年视图近 52 周、GitHub 风格),头部显示活跃天数 / 连续使用天数。
86
- - **性能指标**:每个模型首字延时(TTFT)均值 / P50 / P90、生成速度(tokens/s)、总延迟均值;按小时×模型对比曲线——指标 tab 切换(首字延时 / 生成速度),模型 chip 点击开/关曲线(默认点亮样本数前 5,全选一键),悬停吸附最近小时显示十字线与逐模型数值,缺失样本小时断线不造假;视图偏好本地持久化。
87
- - **Token 统计洞察**:独立「Token」分区——每日 token 堆叠双视角切换:「按结构」按「输入(缓存未命中)/ 输入(缓存命中)/ 输出」三桶分色(含 reasoning 思考),「按模型」每天按模型堆叠(分色与趋势页一致,旧快照缺按日×模型明细时自动隐藏切换);悬停柱状图显示当日精确明细(结构视角给三桶逐项,模型视角给逐模型「命中/未命中/输出」,千分位不缩写);点击图例色块或模型 Token 表行可聚焦单个模型(其余段弱化、y 轴不变,再点解除),模型 token 总量与占比,结构 KPI(缓存命中率 / 思考占比 / 输入输出比 / 峰值日);按日 token CSV 与 JSON 导出(JSON 含按日×模型明细)。
93
+ - **会话明细 + 热力图**:按会话费用倒序(标题 / 项目 / 调用 / 费用 / 最后活跃);月 / 半年 / 年三档日历热力图(5 档色阶、悬停明细,年视图近 52 GitHub 风格,**半年视图 26 周大格**——一张图看完近半年强度,截图即用),**费用 / Token 双口径**切换,头部显示区间合计、活跃天数 / 连续使用天数。
94
+ - **性能指标**:每模型 TTFT 均值 / P50 / P90、生成速度(tokens/s)、总延迟均值;按小时 × 模型对比曲线——指标 tab 切换、模型 chip 点击开关曲线(默认点亮样本数前 5)、悬停吸附最近小时显示十字线与逐模型数值,缺失样本小时断线不造假;视图偏好本地持久化。
95
+ - **Token 统计洞察**:每日 token 堆叠双视角——「按结构」(输入未命中 / 命中 / 输出三桶,含 reasoning)与「按模型」(旧快照缺明细时自动隐藏切换);悬停显示当日精确明细(千分位不缩写),点击图例色块或 Token 表行聚焦单模型(再点解除);结构 KPI(缓存命中率 / 思考占比 / 输入输出比 / 峰值日);按日 CSV 与 JSON 导出(JSON 含按日 × 模型明细)。
88
96
 
89
97
  ![趋势:每日费用趋势、每轮费用与峰谷时段占比](screenshots/2.png)
90
- - **数据导出 + 离线自包含**:统计 Tab 导出按日 / 按会话 / 按站点 CSV 与全量 JSON;费用构成 / 工作区 / 会话明细分区可下钻(点项目行展开该项目的会话);无图表库、无外部 CDN、纯设计令牌。
98
+ - **数据导出 + 下钻**:统计 Tab 导出按日 / 按会话 / 按站点 CSV 与全量 JSON;费用构成 / 工作区 / 会话明细可下钻(点项目行展开该项目的会话);无图表库、无外部 CDN、纯设计令牌。
91
99
 
92
100
  ![统计:导出、费用构成、工作区与会话明细](screenshots/4.png)
93
101
 
94
102
  ## 🛡️ 健壮性与隐私
95
103
 
96
- - **真实用量聚合**:服务端从会话日志实时聚合(增量缓存只重算写过的会话),单会话损坏容错、快照落盘回退;`usage_stats` 工具让模型自查今天 / 本月 / 当前会话 / 累计费用,还可查 `bySite`(按站点归组)与 `relay`(只看中转站)的汇总。
97
- - **模型健康 + 未收录标注**:厂商接入状态圆点(绿 / 红 / 灰);模型 id 不在目录时标「未收录」按兜底价估算、厂商自动推断(如 `mi-mimo-2.5` → 小米);估算价模型标注「估算价」。
104
+ - **真实用量聚合**:服务端增量聚合(只重算写过的会话),单会话损坏容错、快照落盘回退;`usage_stats` 工具让模型自查今天 / 本月 / 当前会话 / 累计费用,还可查 `bySite`(按站点归组)与 `relay`(只看中转站)汇总。
105
+ - **模型健康 + 未收录标注**:厂商接入状态圆点(绿 / 红 / 灰);未收录模型显著标注、按兜底价估算、厂商自动推断(如 `mi-mimo-2.5` → 小米);估算价模型标注「估算价」。
98
106
  - **多语种 + 双币种**:¥ / $ 切换随币种双语(USD→英文、CNY→中文,仅本插件生效);费率表按所选币种换算。
99
- - **安全加固**:全部 HTTP 端点强制回环访问——peer socket 地址 + Host 头精确匹配(回环 IP 字面量,而非前缀)双重校验,拒绝 `127.0.0.1.evil.com` 形式的 DNS rebinding 域名;`/api/billing/usage-tool` 的写操作额外校验 Origin 回环与 Content-Type 为 `application/json` 并限制 body 上限,杜绝跨站改写;余额 / 订阅 / 中转站查询与定价拉取均带有限重试与按上游维度熔断(鉴权失败属配置问题、不计入熔断)。
100
- - **数据导出防注入**:按日 / 按会话 / 按站点 CSV 对以 `=` / `+` / `-` / `@` 开头的单元格前置单引号,并完整转义逗号 / 引号 / 换行 / 回车,防止在 Excel / WPS 中被当作公式执行。
101
- - **隐私底线**:纯 UI surface,不注册工具、不注入系统提示、不向会话日志写入模型可见事件;仅从既有会话日志聚合,日志内容由其他包负责。
107
+ - **安全加固**:全部 HTTP 端点强制回环访问——peer socket 地址 + Host 头精确匹配双重校验,拒绝 `127.0.0.1.evil.com` 形式的 DNS rebinding;写操作额外校验 Origin 回环与 Content-Type 并限制 body 上限,杜绝跨站改写;余额 / 订阅 / 定价拉取带有限重试与按上游维度熔断(鉴权失败属配置问题、不计入熔断)。
108
+ - **导出防注入**:CSV `=` / `+` / `-` / `@` 开头单元格前置单引号并完整转义,防止在 Excel / WPS 中被当作公式执行。
109
+ - **隐私底线**:纯 UI surface,不注册工具、不注入系统提示、不向会话日志写模型可见事件;仅从既有会话日志聚合,日志内容由其他包负责。
102
110
 
103
111
  ## 🚀 快速开始
104
112
 
@@ -146,10 +154,6 @@
146
154
  - **服务端**(`src/index.ts`):注入 `webServer`、`sessionPersistence` 与 `credentials`,注册 `GET /api/billing/usage-stats`、`/api/billing/pricing`、`/api/billing/balance`、`/api/billing/subscriptions`、`/api/billing/relay-quotas`。聚合器按会话缓存折叠结果:一次 LLM 调用归属到其前置 `request/header` 记录的模型,token 拆分到缓存命中 / 未命中桶,日期按本机时区归天;日志文件 mtime+size 不变则直接复用缓存,只有写过的会话重新折叠,整份文档另有 5 秒 TTL 合并密集轮询。每个成功折叠的会话同时原子写入独立的持久用量账本,永久删除会话后历史费用与 token 仍保留。
147
155
  - **浏览器端**(`src/client/`):请求上述接口渲染仪表盘,通过 `llm.models` 探测各厂商连接状态。真实数据到达前显示全零空快照,不展示伪造样本。
148
156
 
149
- ## 🧩 主题协作
150
-
151
- 本插件**不依赖任何主题包**,可独立安装运行。仪表盘弹窗声明 `billing.dashboard.decor` 装饰孔位(head / hero / trend / models / footer 锚点),并将实时费用摘要注册为 `ctx.billingMetrics` 服务:主题插件(如 acid-zine)主动注入 MacDots / 胶带 / 撕角便签等装饰视觉、订阅费用数据渲染自己的贴纸层。插件与主题各自独立装卸——主题不存在时走默认视觉,billing 不存在时主题照常运行。
152
-
153
157
  ## 💡 计费细节
154
158
 
155
159
  单价表(`src/client/pricing.ts`)采用**原生币种**存储:国内厂商直接录入人民币价格,国外厂商录入美元价格。费用统一以人民币计算与展示——美元模型按**实时汇率**折算,国内模型全程不经过汇率换算。启动时服务端拉取实时汇率与模型价(`src/pricing-fetch.ts`):USD→CNY 优先腾讯财经行情(免 key、国内可达),失败依次降级 open.er-api 与内置默认值;之后每 6 小时后台刷新,单价表弹窗标注「今日汇率」与实时 / 内置徽标。金额与费率表的**展示币种跟随用户所选**:切 ¥ / $ 时把每条每百万 token 单价经 `convertUnitPrice` 按实时汇率换算到目标币种再显示(汇率缺失时回退原生币种)。
@@ -264,6 +268,7 @@ npm publish --access public
264
268
  - **会话明细不可跳转**:点击会话行不会打开对应会话(跨插件导航需要宿主会话选择通道);会话数封顶 100 行、面板只显示前 20 行。
265
269
  - **费用为目录价估算**:讯飞 / 商汤 / 小米等未公布按量单价的模型使用估算价(特性表脚注 ¹),正式定价以厂商账单为准。
266
270
  - **账本从首次成功聚合开始生效**:升级前已经永久删除且不在旧快照中的会话无法恢复;手动删除 `.dsh-usage-ledger.json` 及其 `.bak` 会清空独立保留的历史。账本只保留本插件已经成功观测过的调用。
271
+ - **历史回放预热**:插件加载 3 秒后台自动全量折叠宿主全部历史会话日志(账本幂等,重复折叠不重复计数),首次打开面板即可看到完整历史统计,无需等一遍首次折叠。历史会话按「首次折叠时的模型单价」计价;厂商改价前的历史消耗不会按新价重算(价格目录无历史时点价,属估算口径)。
267
272
 
268
273
  ## ❤️ Contributors
269
274