dsh-cost-meter 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-cost-meter contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,316 @@
1
+ # dsh-cost-meter
2
+
3
+ <div align="center">
4
+
5
+ **Session cost tracking plugin for the DeepSeek Harness web GUI (bilingual UI)**
6
+
7
+ Per-conversation cost · daily totals · OpenCode Go subscription quota display · budget with usage percentage · official account balance · custom provider balance · balance progress bar · history · peak/off-peak pricing hours display (peak hours UTC 01:00–04:00, 06:00–10:00) · one-click price sync from the official docs · Codex-style token usage heat grid · multi-vendor model pricing (built-in 90+ model price catalog with auto-matching) · mainstream Coding Plan quota queries & display (Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow)
8
+
9
+ [![version](https://img.shields.io/badge/version-1.5.5-4176E6)](https://github.com/Han-1413141/dsh-cost-meter)
10
+ [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
11
+ [![dsh](https://img.shields.io/badge/DeepSeek%20Harness-dsh--plugin-4176E6)](https://github.com/deepseek-ai/deepseek-harness)
12
+ [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
13
+ [![WhaleHarness audit](https://whaleharness.com/badge/Han-1413141/dsh-cost-meter/badge.svg)](https://whaleharness.com/audit-report.md)
14
+
15
+ English | [中文](README.md)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ![Promo art](docs/promo.png)
22
+
23
+ ## Feature overview
24
+
25
+ | Feature | Location | Description |
26
+ |---|---|---|
27
+ | Per-conversation cost | Below the composer / session title bar | Live accumulated cost + input/cache/output tokens; position configurable |
28
+ | Official balance | Sidebar top / Settings page (configurable) | Total / granted / topped-up balance, auto-refresh + manual refresh; optional three-segment progress bar (blue/orange/gray) |
29
+ | Custom provider balance | Sidebar / Settings page (configurable) | Configurable HTTP balance lookup (e.g. LiteLLM); bilingual labels, currency, extract rules; collapsible panel alongside Coding Plan quotas |
30
+ | OpenCode Go quota | Sidebar / Settings / bottom-right dock (configurable) | Rolling-5h / weekly / monthly usage percent and reset times, each window toggleable independently, budget used % can show alongside; key auto-discovered (DSH credential store OPENCODE_GO_API_KEY / env / opencode login) or entered manually |
31
+ | Coding plan quotas | Settings page | Multi-vendor coding-plan quota queries (Anthropic Claude Pro/Max, Z.ai / Zhipu GLM Coding Plan, MiniMax Token Plan, Kimi/Moonshot balance, OpenRouter credits, SiliconFlow balance); per-vendor enable switch and key, credentials only sent to official endpoints; neutral hints when no credentials/subscription |
32
+ | Today's cost | Sidebar bottom (above the settings button) | “Today ¥x”, hover for call count and token details |
33
+ | Budget box | Sidebar bottom (between the balance row and the settings button) | Rounded-square frame: budget, used %, progress bar, today's cost & share of budget, used/limit; ≥80% warning, ≥100% over-budget |
34
+ | Summary cards | Settings page | Today / this month / cumulative cost and call counts |
35
+ | Token usage stats | Settings page (Cost section) | All-time token totals (input/cache/output/calls) + a Codex-style 26-week daily usage heat grid that fills the settings width; hover a cell for that day's detail |
36
+ | Today's sessions | Settings page | Per-session call count, input/cache/output tokens and cost |
37
+ | History | Settings page | Per-day totals; retention days configurable (default 180) |
38
+ | Budget settings | Settings page, top | Limit, period (today / month / cumulative / custom date range), used % |
39
+ | Price table | Settings page | Per-model off-peak / peak prices (input/output shorthand supported; cache prices derived automatically); fully editable |
40
+ | Peak/off-peak hours display | Settings / budget / today | Shows UTC peak hours 01:00–04:00 and 06:00–10:00 with the current tier; expanded view shows a peak/off-peak period strip (current period + countdown), collapsed (rail) view shows a vertical peak/off-peak progress bar; independently toggleable |
41
+ | Official price sync | Settings page | Fetches and parses the official pricing page, applies with one click |
42
+ | UI language | Settings → Display settings | Simplified Chinese / English / Follow browser (auto); switches instantly and auto-saves |
43
+ | AI price sync | [prompt](docs/AI-PRICE-SYNC-PROMPT.en.md) | DeepSeek official sync; other providers use the verified official price catalog and manual configuration |
44
+ | Model & Plan adaptation guide | [adaptation doc](docs/model-and-plan-adaptation.en.md) | Adaptation matrix for per-model billing and the 6 Coding Plan vendors, the auto-matching mechanism and price sources ([中文](docs/model-and-plan-adaptation.md)) |
45
+ | Multi-provider billing | Settings / ledger | OpenAI, Anthropic, Google Gemini, Mistral and other providers with input/output, cache and reasoning-token pricing isolated by provider + model |
46
+ | Model-name auto-matching | Settings / ledger | Unknown model ids are matched against the price table: case/spaces/hyphens/dots and bracket annotations (e.g. (go)) are ignored — a normalized-equal or containing name hits (e.g. `gpt5.6 luna(go)`); router providers (opencode/zen etc.) search across all vendors; can be restricted to exact match, and unmatched models can be pinned to a specific entry in Settings |
47
+ | Extended price catalog | Settings → Extended price catalog | Built-in reference catalog grouped by vendor and model family (expandable; vendors collapsed by default); mount entries into billing with one click — mounted third-party models live inside the catalog and stay editable; a per-model “Show directly in Cost settings” toggle chooses which models (DeepSeek included) appear directly in the price table |
48
+
49
+ ## Bilingual UI
50
+
51
+ The plugin UI (session badge, sidebar balance row & budget box, and the entire Settings page) supports **Simplified Chinese** and **English**:
52
+
53
+ - Language options: **Simplified Chinese** / **English** / **Follow browser (auto)**;
54
+ - Default is “Follow browser”: the browser language is auto-detected (`zh*` → Chinese, otherwise English), and the detected value is written back into the config so server-side messages (balance query, price sync, etc.) match the UI language;
55
+ - Switch it under **Settings → Cost → Display settings → Language** — the whole plugin UI updates instantly and auto-saves; the section label in the Settings sidebar switches too (费用 / Cost);
56
+ - Server-generated notices (balance refresh, official price sync, config validation errors, …) are also output in the current language.
57
+
58
+ ## Screenshots & walkthrough
59
+
60
+ > All screenshots were captured on a live DeepSeek Harness instance. They show the Chinese UI by default; the plugin UI itself is bilingual (Simplified Chinese / English) — switch to English under Settings → Cost → Display settings → Language.
61
+
62
+ ### Main page
63
+
64
+ **Sidebar bottom** (top to bottom: official balance → quota/budget box → settings button):
65
+
66
+ ![Sidebar footer](docs/screenshot-sidebar-footer.png)
67
+
68
+ - The balance row shows the official open-platform total balance; hovering reveals the granted/topped-up split; with “Balance progress bar” enabled, both official and custom balances use the same three-segment box (blue = remaining, orange = today, gray = spent);
69
+ - With no budget enabled, that spot shows the “Today ¥x” badge.
70
+
71
+ **Balance progress bar & custom provider settings**:
72
+
73
+ | Sidebar progress bar + display settings | Custom provider balance panel |
74
+ |---|---|
75
+ | ![Balance progress bar](docs/screenshot-balance-progress-bar-zh.png) | ![Custom provider settings](docs/screenshot-custom-balance-settings-zh.png) |
76
+
77
+ - Display settings → global “Balance progress bar” toggle; optional “Budget cap” overrides API `max_budget`;
78
+ - Settings → Cost → “Custom provider balance”: expand to edit URL / headers (JSON) / extract (JSON), bilingual names, and currency.
79
+
80
+ **Quota / budget box — three states** (OpenCode Go quota and the budget each toggle independently in the same rounded style; with both on they **merge into one card** — Go on top, budget below, thin divider, each keeps its own warning colors; the “box details” toggle collapses secondary rows to just label + used % + progress bar):
81
+
82
+ | Go quota only | Budget only | Merged |
83
+ |---|---|---|
84
+ | ![Go quota only](docs/screenshot-go-box.png) | ![Budget only](docs/screenshot-budget-box.png) | ![Merged card](docs/screenshot-sidebar-footer-v2.png) |
85
+
86
+ - The budget box shows “budget · used % · progress bar · today's cost & share of budget · used/limit”; ≥80% warning, ≥100% over-budget; rail mode narrows to a percentage tile;
87
+ - The peak/off-peak hours display shows UTC peak hours 01:00–04:00 and 06:00–10:00 with the current tier; the budget box and today's cost area show a compact one-line period strip — a thin orange/blue track with a marker line on the current period, and text on the right showing the current period plus the countdown to the next switch (refreshed every 30 seconds); no prices are shown; it can be disabled independently in Settings, and the “Peak period strip style” option switches between the Compact and Classic looks; collapsed rail mode shows the same design vertically with a short horizontal label (“Peak / Off-peak”) below — the countdown and full text appear on hover;
88
+
89
+ **Peak/off-peak period strip & collapsed vertical progress bar**:
90
+
91
+ | Settings peak panel (notice toggle / style switch / preview) | Settings bottom-right (dock) display & box details |
92
+ |---|---|
93
+ | ![Peak/off-peak pricing & notice panel](docs/peak-panel-settings-en.png) | ![Dock display & box details settings](docs/dock-display-settings-en.png) |
94
+
95
+ Real captures from an actual DSH sidebar of the period strip and collapsed vertical bar (current looks), grouped by UI type (shown during peak hours):
96
+
97
+ **Expanded** — the budget box / today's cost area shows a one-line period strip:
98
+
99
+ | Compact | Classic |
100
+ |---|---|
101
+ | ![Expanded · Compact](docs/peak-strip-expanded-compact-en.png) | ![Expanded · Classic](docs/peak-strip-expanded-classic-en.png) |
102
+
103
+ - Compact: thin orange/blue track with a marker line on the current period and a short caption, e.g. “Peak · Off-peak in 1h 40m”;
104
+ - Classic: same track and marker line with the full caption “Peak · Off-peak in HH:MM:SS” countdown (refreshed every 30 seconds); no prices are shown.
105
+
106
+ **Collapsed (rail)** — a vertical period bar stacked at the sidebar bottom, centered with the percentage squares:
107
+
108
+ | Compact | Classic |
109
+ |---|---|
110
+ | ![Collapsed · Compact](docs/peak-strip-rail-compact-en.png) | ![Collapsed · Classic](docs/peak-strip-rail-classic-en.png) |
111
+
112
+ - Compact: only the short horizontal label (“Peak / Off-peak”) below the vertical bar;
113
+ - Classic: the full caption stacked vertically below the bar, including the countdown to the next switch; in both styles the full text is also available on hover.
114
+
115
+ - The display follows the `peakNotice` / `peakEnabled` / `peakEffectiveAt` / `peakWindows` gates and uses the configured UTC peak windows;
116
+ - Settings → Cost → Peak/off-peak pricing includes an independent “Prominent notice during peak hours” toggle; turning it off hides both the expanded strip and the collapsed vertical bar;
117
+ - The first image above is the Settings peak panel (notice toggle, style switch and live preview); see the grouped captures for the strip and collapsed vertical bar; the dock toggles and box-details switches are shown in the second image.
118
+
119
+ - The Go box shows the main window's used % and progress bar (default rolling 5h; switchable to weekly/monthly in Display settings), with the other two windows and reset times in a row below:
120
+
121
+ ![Rail mode](docs/screenshot-sidebar-rail-v2.png)
122
+
123
+ **Bottom-right (dock) quota / budget chips** (enabled in Display settings; four independent toggles: 5h / weekly / monthly quota + budget used %):
124
+
125
+ | Corner chips in action | Display settings (where the toggles live) |
126
+ |---|---|
127
+ | ![Corner chips](docs/screenshot-display-corner-v2.png) | ![Dock display settings](docs/dock-display-settings-en.png) |
128
+
129
+ **Per-conversation cost** (two positions, switchable in Settings):
130
+
131
+ | Below the composer | Session title bar |
132
+ |---|---|
133
+ | ![Session dock](docs/screenshot-session-dock.png) | ![Session header](docs/screenshot-session-header.png) |
134
+
135
+ > Left: this session ¥5.5939 · input 321K · cache 119M · output 235K; right: title-bar badge “cost ¥6.1606” (real session captures)
136
+
137
+ ![Session page](docs/screenshot-session.png)
138
+
139
+ ### Settings → Cost
140
+
141
+ **Overview** (OpenCode Go quota → budget → balance → summary cards → today's sessions → history → display settings → price table → data & sync):
142
+
143
+ ![Settings page](docs/screenshot-settings.png)
144
+
145
+ **OpenCode Go quota panel** (very top of the Settings page: three progress bars, main window highlighted, manual refresh; a neutral hint when there is no subscription, one-click disable):
146
+
147
+ ![Go quota panel](docs/screenshot-settings-top-v2.png)
148
+
149
+ **Budget panel** (including custom date ranges):
150
+
151
+ ![Budget](docs/screenshot-budget-panel.png)
152
+
153
+ **Balance panel** (total/granted/topped-up + manual refresh):
154
+
155
+ ![Balance](docs/screenshot-balance-panel.png)
156
+
157
+ **Display settings** (Go main window & key, corner chips, box details, …):
158
+
159
+ ![Display settings](docs/screenshot-display-settings-v2.png)
160
+
161
+ **Summary cards**:
162
+
163
+ ![Cards](docs/screenshot-cards.png)
164
+
165
+ **Token usage stats** (all-time totals + a Codex-style 26-week heat grid filling the settings width; translucent glass cells for unused days):
166
+
167
+ ![Token usage stats](docs/screenshot-usage-grid.png)
168
+
169
+ **Today's sessions / history** (input, cache and output tokens in separate columns):
170
+
171
+ ![Today's sessions](docs/screenshot-table-1.png) ![History](docs/screenshot-table-2.png)
172
+
173
+ **Price table** (off-peak / peak tiers, with input/output shorthand support, USD / 1M tokens):
174
+
175
+ ![Price table](docs/screenshot-price-card.png)
176
+
177
+ **Data & sync** (instant auto-save of settings + official price sync + clear history):
178
+
179
+ ![Sync](docs/screenshot-sync.png)
180
+
181
+ ## Installation
182
+
183
+ > Requirements: Node.js ≥ 20 + DeepSeek Harness (a version with the `dsh plugin` command; `npm install -g @deepseek-ai/dsh`).
184
+
185
+ ### One-click install (recommended)
186
+
187
+ **PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.5.5`** — review the script before running):
188
+
189
+ ```powershell
190
+ irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.5/install.ps1 | iex
191
+ ```
192
+
193
+ **Or a plain command line** (the machine must already have pnpm and git; also pinned to the tag):
194
+
195
+ ```sh
196
+ dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.5
197
+ ```
198
+
199
+ Without git, use the GitHub tag archive:
200
+
201
+ ```sh
202
+ dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.5.tar.gz
203
+ ```
204
+
205
+ After installing, **restart** `dsh web` (plugin rows, the Typert manifest and the client bundle are all scanned at startup):
206
+
207
+ ```sh
208
+ dsh web
209
+ ```
210
+
211
+ ### Update / Uninstall
212
+
213
+ ```sh
214
+ # update: re-run the new release's install.ps1 (the pinned tag inside it moves with the release)
215
+ dsh plugin --profile web remove dsh-cost-meter # uninstall
216
+ ```
217
+
218
+ ### Local development
219
+
220
+ ```sh
221
+ git clone https://github.com/Han-1413141/dsh-cost-meter.git
222
+ cd <parent directory of the clone>
223
+ dsh plugin --profile web add link:./dsh-cost-meter # symlink; edit lib/client.js, refresh the page, done
224
+ ```
225
+
226
+ ## Billing rules
227
+
228
+ ![Billing rules & peak/off-peak pricing](docs/diagram-pricing.en.svg)
229
+
230
+ - Price units match the official docs: **USD / 1M tokens**;
231
+ - cost = cache-missed input × cache-miss + output × output + (cache read + cache write) × cache-hit (cache writes follow the legacy official rule and are billed at the hit price);
232
+ - **Pure two-tier peak/off-peak pricing** (the official scheme since 2026-08): peak hours (01:00–04:00, 06:00–10:00 UTC) bill at the peak price and all other hours at the off-peak price (off-peak = half of peak). The base tier equals the off-peak tier, and billing falls back to off-peak when peak/off-peak is disabled; the Settings page shows the live tier (peak / off-peak); the budget/today's cost area shows a peak/off-peak period strip (current/next period with countdown), and the collapsed rail shows a vertical peak/off-peak progress bar;
233
+ - **Historical billing correctness**: calls before 2026-08-16 16:00 UTC (the peak-era boundary) are billed at the base prices of that time, and later calls at the two-tier scheme;
234
+ - The ledger always stores amounts in **USD**; currency and FX rate only affect display (default 1 USD = 7.2 CNY, configurable);
235
+ - The session badge is **billed exactly** at the moment each call is made (host-exported per-call cost), just like daily/monthly/cumulative totals and the budget;
236
+ - Billing sources are the `usage` block of every model call (including sub-agents, compression, title generation and other auxiliary calls), matching the billable view;
237
+ - Budget and over-budget warnings **only warn — they never block calls**.
238
+
239
+ ## Data storage
240
+
241
+ - Ledger: `$DSH_HOME/storages/cost-meter/ledger.json` (atomic write + 2-second debounce; retained per `historyDays`, up to 200 per-session entries per day);
242
+ - Every settings change is **saved instantly and automatically** (600 ms debounce) — no manual save needed;
243
+ - Delete the ledger file to reset everything, or use “Clear all history” in Settings.
244
+
245
+ ## Architecture
246
+
247
+ ![Architecture & data flow](docs/diagram-architecture.en.svg)
248
+
249
+ ```
250
+ dsh-cost-meter
251
+ ├── cordis.patch.yml # bundle patch: inserts the cost-meter row into the web profile
252
+ ├── install.ps1 # one-click install/update script (irm … | iex)
253
+ ├── .github/workflows/ # CI: install-smoke for the one-click install path
254
+ ├── package.json # dsh.bundle patch declaration + dsh.client browser declaration
255
+ └── lib/
256
+ ├── index.js # host plugin: llm/stream billing wrapper, costUsage session
257
+ │ # projection, costMeter service (hand-written typertRemote
258
+ │ # binding), balance lookup
259
+ ├── pricing.js # official price table, official page HTML parsing, peak/off-peak math
260
+ ├── store.js # ledger persistence & config management ($DSH_HOME/storages/cost-meter)
261
+ ├── typert.host.js # ./typert export: Typert manifest (auto-registered by typert-loader)
262
+ └── client.js # ./client export: browser single-file bundle (badges/box/settings)
263
+ ```
264
+
265
+ Data channels:
266
+
267
+ - **Per-conversation cost**: the host registers the `costUsage` session projection (pure token buckets, split per model); the browser reads it via `useProjection('costUsage')` and prices it with the current price table;
268
+ - **Global ledger / budget / balance / config**: `costMeter/getState | updateConfig | fetchPrices | refreshBalance | resetHistory` over the Typert gateway RPC (`remote.costMeter.*`);
269
+ - **Balance**: calls the official `GET {baseURL}/user/balance`, reusing the same API key as model requests (credential service / env var), with an in-process cache expiring per `refreshMinutes`.
270
+
271
+ The plugin never imports cordis/dsh Service/Context runtime classes (only Node builtins, zod, and pure functions from dsh-home-paths and dsh-credentials), so it shares one runtime instance with the host with no duplicated dependency risk.
272
+
273
+ ## How official price sync works
274
+
275
+ `fetchPrices` fetches the official pricing page (Docusaurus server-side pre-rendered) and parses:
276
+
277
+ 1. the base price table (transposed layout: first row MODEL + model ids, price labels followed by the prices);
278
+ 2. the peak/off-peak price table (two rows per model: OFF-PEAK / PEAK);
279
+ 3. the effective time (“take effect at …”) and the peak-hour windows (“Peak hours are …”).
280
+
281
+ The parsed result is written into the price table and persisted; if the page structure changes, sync reports an error and keeps the previous prices, with manual editing as a fallback.
282
+
283
+ ## AI price sync
284
+
285
+ [docs/AI-PRICE-SYNC-PROMPT.en.md](docs/AI-PRICE-SYNC-PROMPT.en.md) (English) and [docs/AI-PRICE-SYNC-PROMPT.md](docs/AI-PRICE-SYNC-PROMPT.md) (中文) provide prompts you can copy straight into any AI:
286
+ the AI reads the official pricing on its own → outputs per-model, time-of-day (base/off-peak/peak + effective time) price JSON → you review and apply it (Settings page / RPC / file — pick one). Handy when the official prices change.
287
+
288
+ ## Development & verification
289
+
290
+ ```sh
291
+ corepack pnpm install # dependencies
292
+ node --check lib/index.js && node --check lib/pricing.js \
293
+ && node --check lib/store.js && node --check lib/typert.host.js \
294
+ && node --check lib/client.js # syntax checks
295
+ node test/verify.mjs # pure-module verification (parsing/billing/ledger/config)
296
+ node test/mock-balance.mjs # (optional) local balance API mock: 3101
297
+ dsh --profile web --dump-config # composition-tree check
298
+ dsh --profile web --port 3099 # real startup (watch logs and the UI)
299
+ ```
300
+
301
+ ## Known limitations
302
+
303
+ - Official-page parsing depends on the current page structure; after a redesign, “Sync prices from official docs” fails — edit the price table manually as a fallback;
304
+ - The session badge is estimated at the current price tier; exact figures come from the ledger;
305
+ - Price sync overwrites the same-named models listed on the official page; custom model entries are unaffected;
306
+ - Balance lookup needs network access to api.deepseek.com and a valid API key; **the API key is only ever sent to the official domain** (if baseURL points at a non-official host, balance queries refuse to run — model requests are unaffected);
307
+ - The OpenCode Go quota endpoint is the official opencode.ai endpoint (community-documented); if its response shape changes, the Settings page shows an error and the display can be turned off in Display settings;
308
+ - A restart of `dsh web` is required after installing/updating the plugin.
309
+
310
+ ## Update history
311
+
312
+ A per-version overview and the community-issue resolution log live in [docs/UPDATE-HISTORY.md](docs/UPDATE-HISTORY.md) (中文); the itemized changelog is [CHANGELOG.md](CHANGELOG.md).
313
+
314
+ ## License
315
+
316
+ [MIT](LICENSE) © 2026 dsh-cost-meter contributors