@goodandready/dsh-cost-meter 0.7.8 → 0.8.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.md CHANGED
@@ -108,14 +108,20 @@ dsh-cost-meter:
108
108
 
109
109
  ### Configuration Parameters
110
110
 
111
- | Parameter | Type | Default | Description |
112
- |:---|:---|:---|:---|
113
- | `currency` | `string` | `"$"` | Display currency symbol or code |
114
- | `usdRate` | `number` | `1.0` | Exchange rate multiplier from USD |
115
- | `displayTimeZone` | `string` | `"UTC"` | Timezone for window schedule formatting (e.g. `Europe/London`, `Asia/Shanghai`) |
116
- | `useOpenRouter` | `boolean` | `true` | Fetch OpenRouter public catalog for fallback models |
117
- | `prices` | `object` | `{}` | Custom override rates per 1M tokens `{ prompt, completion, cacheRead?, cacheWrite? }` |
118
- | `modelMap` | `object` | `{}` | Model alias mappings `{ "alias": "target-model" }` |
111
+ | Parameter | Scope / Location | Type | Default | Description |
112
+ |:---|:---|:---|:---|:---|
113
+ | `currency` | GUI / `settings.yaml` | `string` | `"$"` | Display currency symbol (e.g. `$`, `₽`, `€`) |
114
+ | `usdRate` | GUI / `settings.yaml` | `number` | `1.0` | Exchange rate multiplier: units of currency per 1 USD |
115
+ | `displayTimeZone` | GUI / `settings.yaml` | `string` | `"Europe/Moscow"` | IANA timezone for peak/off-peak windows formatting |
116
+ | `useOpenRouter` | GUI / `settings.yaml` | `boolean` | `true` | Automatically fetch rates & discount windows from OpenRouter |
117
+ | `refreshHours` | GUI / `settings.yaml` | `number` | `24` | Hours between periodic OpenRouter catalog updates |
118
+ | `deepseekPeakPrices` | `settings.yaml` only | `object` | `{}` | Override built-in DeepSeek peak rates by model id |
119
+ | `prices` | `settings.yaml` only | `object` | `{}` | Manual rates per 1M tokens `{ input, output, cacheHit?, cacheWrite? }` |
120
+ | `modelMap` | `settings.yaml` only | `object` | `{}` | Route override: `"provider/model"` -> OpenRouter model id |
121
+ | `manualPeakWindowsUtc` | `settings.yaml` only | `array` | `[]` | Manual peak windows HH:MM-HH:MM UTC used with manual prices |
122
+ | `manualOffPeakMultiplier` | `settings.yaml` only | `number` | `1.0` | Multiplier applied outside manual peak windows |
123
+
124
+ > **Note on GUI vs YAML**: Primary scalar parameters (`currency`, `usdRate`, `displayTimeZone`, `useOpenRouter`, `refreshHours`) are directly editable in the GUI Settings Card under **Settings → Plugins → Plugin Settings → Cost Meter**. Advanced structured rules (`prices`, `modelMap`, `deepseekPeakPrices`, `manualPeakWindowsUtc`, `manualOffPeakMultiplier`) are configured in `settings.yaml` due to their complex dictionary/array schema.
119
125
 
120
126
  ---
121
127
 
package/docs/README.ru.md CHANGED
@@ -108,14 +108,20 @@ dsh-cost-meter:
108
108
 
109
109
  ### Параметры конфигурации
110
110
 
111
- | Параметр | Тип | По умолчанию | Описание |
112
- |:---|:---|:---|:---|
113
- | `currency` | `string` | `"₽"` | Символ или код валюты для отображения |
114
- | `usdRate` | `number` | `90.0` | Множитель пересчёта долларовых цен в валюту отображения |
115
- | `displayTimeZone` | `string` | `"UTC"` | Таймзона для отображения окон в панели |
116
- | `useOpenRouter` | `boolean` | `true` | Загружать ли каталог цен OpenRouter для сторонних моделей |
117
- | `prices` | `object` | `{}` | Ручные ставки за 1M токенов `{ prompt, completion, cacheRead?, cacheWrite? }` |
118
- | `modelMap` | `object` | `{}` | Алиасы моделей `{ "alias": "target-model" }` |
111
+ | Параметр | Доступность | Тип | По умолчанию | Описание |
112
+ |:---|:---|:---|:---|:---|
113
+ | `currency` | GUI / `settings.yaml` | `string` | `"₽"` | Символ валюты в интерфейсе (например, `₽`, `$`, `€`) |
114
+ | `usdRate` | GUI / `settings.yaml` | `number` | `80.0` | Курс обмена: единиц валюты за 1 USD |
115
+ | `displayTimeZone` | GUI / `settings.yaml` | `string` | `"Europe/Moscow"` | IANA часовой пояс для отображения окон пика и не-пика |
116
+ | `useOpenRouter` | GUI / `settings.yaml` | `boolean` | `true` | Загружать ли каталог тарифов и скидочных окон OpenRouter |
117
+ | `refreshHours` | GUI / `settings.yaml` | `number` | `24` | Частота обновления каталога OpenRouter (в часах) |
118
+ | `deepseekPeakPrices` | Только `settings.yaml` | `object` | `{}` | Переопределение встроенных пиковых ставок DeepSeek по id модели |
119
+ | `prices` | Только `settings.yaml` | `object` | `{}` | Ручные ставки за 1M токенов `{ input, output, cacheHit?, cacheWrite? }` |
120
+ | `modelMap` | Только `settings.yaml` | `object` | `{}` | Сопоставление маршрута `"provider/model"` на id в OpenRouter |
121
+ | `manualPeakWindowsUtc` | Только `settings.yaml` | `array` | `[]` | Ручные окна пика HH:MM-HH:MM UTC для ручных ставок |
122
+ | `manualOffPeakMultiplier` | Только `settings.yaml` | `number` | `1.0` | Коэффициент тарифа вне ручных окон пика |
123
+
124
+ > **Интерфейс настроек (GUI) vs settings.yaml**: Основные параметры (`currency`, `usdRate`, `displayTimeZone`, `useOpenRouter`, `refreshHours`) настраиваются прямо в карточке интерфейса DSH (**Настройки → Плагины → Настройки плагинов → Cost Meter**). Расширенные структурированные правила сопоставления и ручных тарифов задаются через `settings.yaml` из-за сложной древовидной структуры.
119
125
 
120
126
  ---
121
127
 
@@ -22,7 +22,11 @@
22
22
  - Документация:
23
23
  - README.md, index.md, docs/testing/public-route.md, docs/testing/projection-lifecycle.md, docs/deployment/public-release.md.
24
24
 
25
- ## Visual Direction
25
+ ## Visual Direction (Unified with dsh-clinebot)
26
+ - Атмосфера: Строгий утилитарный нативный интерфейс DSH, высокая информационная плотность без визуального шума.
27
+ - Утверждённые референсы: Эталонный плагин `@goodandready/dsh-clinebot`, дизайн-система DeepSeek Harness (`--dsw-alias-*`), карточки секций `.dcm-section-card`, бейджи статуса `.dcm-badge`, адаптивная сетка параметров `.dcm-grid-2`, чипы в шапке сессии.
28
+ - Отказоустойчивость: Обязательный `ErrorBoundary` вокруг карточки настроек и поповера шапки (предотвращает исчезновение слотов ядра при исключениях рендеринга).
29
+ - Синхронизация зеркал: `refreshMirrorUntilVisible(ctx)` для надёжного доступа к зеркалу `settingsScope`.
26
30
  - Атмосфера: Строгий утилитарный нативный интерфейс DSH, высокая информационная плотность без визуального шума.
27
31
  - Утверждённые референсы: Дизайн-система DeepSeek Harness (`--dsw-alias-*`), чипы в шапке сессии (`dsh-key-rotation`, `dsh-context-lens`), стандарт карточек настроек DSH.
28
32
  - Не копировать: Избыточные градиенты, внешние модальные окна, тяжелые тени, кастомные несогласованные шрифты.
@@ -41,7 +45,7 @@
41
45
  - Сетка и отступы:
42
46
  - Чип: высота 22px, padding: 0 8px, gap: 6px, border-radius: 6px.
43
47
  - Панель: padding: 12px 14px, border-radius: 10px, ширина 320px.
44
- - Карточка настроек: нативный складной контейнер (`.dcm-settings-card`), отступы полей, бейдж статуса снимка (`ready`, `loading`, `unavailable`).
48
+ - Карточка настроек: нативный складной контейнер (`.dcm-card`) с шапкой, статусами-бейджами (`dcm-badge-ok`, `dcm-badge-warn`, `dcm-badge-bad`), 3 структурированными секциями (`Основные параметры`, `Каталог OpenRouter`, `Диагностика и статус`), защитой ввода и кнопкой сохранения (`dcm-btn-primary`).
45
49
 
46
50
  ## Components & States
47
51
  - Чип:
@@ -82,7 +86,7 @@
82
86
  - Не допускать утечек локальных путей и инфраструктурных данных в коде или документации.
83
87
 
84
88
  ## History & Decisions
85
- - 2026-09-05 (v0.7.8):
89
+ - 2026-09-05 (v0.7.9):
86
90
  - Hotfix: исправлен ReferenceError (t is not defined) в объекте словаря ru (lib/client.js) (#15).
87
91
  - Добавлен автоматический тест на чистое исполнение фабрики client loader (`test/public-route.test.mjs`).
88
92
  - 2026-09-05 (v0.7.4):
@@ -1,10 +1,13 @@
1
- # Findings & Architecture Decisions (v0.7.8)
1
+ # Findings: Issue #23
2
2
 
3
- ## 1. Browser Request Race Conditions
4
- In `CostMeter`, rapid model switches or toggling the popover calls `load(usedKeys)`. Since network latency varies, an older request might resolve after a newer one. By tracking an active request counter or cancellation token per effect invocation, we ensure only the most up-to-date state response mutates the component state.
3
+ ## Архитектурные паттерны dsh-clinebot
4
+ - Изолированный тег `<style id="dsh-cost-meter-unified-css" data-dsh-plugin="dsh-cost-meter">` с переменными `--dsw-alias-...`.
5
+ - `createErrorBoundary()` защищает слоты от падений при сбоях рендеринга.
6
+ - `refreshMirrorUntilVisible(ctx)` обеспечивает доступность зеркала настроек в `settingsScope`.
7
+ - Бейджи статуса (`cb-badge-ok`, `cb-badge-warn`, `cb-badge-bad`) позволяют пользователю моментально оценить состояние плагина.
5
8
 
6
- ## 2. IANA TimeZone Validation
7
- `Intl.DateTimeFormat(undefined, { timeZone })` throws a `RangeError` if the timezone string is invalid. We can safely leverage this in `CostMeterCard` to reject typos (e.g. `Europe/Moskow` instead of `Europe/Moscow`) on the client before writing to `settingsScope`.
8
-
9
- ## 3. Query Parameter Sanitization
10
- The endpoint `GET /dsh-cost-meter/state?models=...` accepts comma-separated model keys. Adding a length bound (e.g. <= 128 chars) and trimming ensures no malformed payloads cause unexpected overhead.
9
+ ## Найденные баги в dsh-cost-meter
10
+ 1. Строка 427 `lib/index.js`: `catalog = { index: { byId: {}, bySuffix: {} }, fetchedAt: 0, error: null }` не содержала `byLowerId`. При обращении к `matchModel` до первой загрузки каталога вызывался `index.byLowerId[lower]`, что приводило к `TypeError`.
11
+ 2. `slotOf(event.time)`: при `undefined` возвращал `NaN`, повреждая ключи слотов в проекции.
12
+ 3. `fetch(CATALOG_URL)`: без таймаута мог блокировать цикл обновлений при сетевых лагах.
13
+ 4. `splitRoute`: отбрасывал модели без слэша.
@@ -1,17 +1,25 @@
1
- # Implementation Progress (v0.7.8)
1
+ <!-- id: issue-23-progress -->
2
+ # Progress: Issue #23 — Стабилизация кода и редизайн UI по стандарту dsh-clinebot
2
3
 
3
- - [x] Create Gitea Issue #19
4
- - [x] Create isolated worktree `stability-v078`
5
- - [x] Bump version to 0.7.8 in `package.json`
6
- - [x] Add monotonic request sequence guard in `lib/client.js` `CostMeter`
7
- - [x] Add IANA time zone validation in `lib/client.js` `CostMeterCard`
8
- - [x] Sanitize query parameters in `lib/index.js`
9
- - [x] Add automated unit tests (`test/timezone-sanitization.test.mjs`, 11/11 passing)
10
- - [x] Update design documentation in `docs/design/DESIGN.md`
11
- - [ ] Commit changes with conventional commits (`git-antigravity`)
12
- - [ ] Create Pull Request and merge into `main`
13
- - [ ] Test package on MiniPC isolated test server (`192.168.1.123:3082`)
14
- - [ ] Smoke-test package on production candidate (`192.168.1.111:3080`)
15
- - [ ] Publish v0.7.8 to npm and GitHub Release
16
- - [ ] Install v0.7.8 from npm on MiniAI and verify live health
17
- - [ ] Close Gitea Issue #19 and clean up worktree
4
+ - [x] Создана Issue #23 в Gitea
5
+ - [x] Создан worktree `.worktrees/issue-23` на ветке `feat/stability-and-ui-clinebot`
6
+ - [x] Инициализированы файлы планирования (`task_plan.md`, `findings.md`, `progress.md`)
7
+ - [x] Применены серверные исправления (`lib/index.js`):
8
+ - [x] Инициализация `byLowerId` в начальном состоянии `catalog.index` (устранён крах холодного старта)
9
+ - [x] Защита `slotOf(timeMs)` от `NaN` при невалидных временных метках
10
+ - [x] Добавлен таймаут (15с) для внешних запросов к OpenRouter
11
+ - [x] Расширен `splitRoute` для поддержки моделей без слэша (`deepseek-chat`)
12
+ - [x] Экспорт `catalogModelsCount` в ответе `/dsh-cost-meter/state`
13
+ - [x] Применён визуальный редизайн по стандарту `dsh-clinebot` (`lib/client.js`):
14
+ - [x] Внедрён `createErrorBoundary()` для защиты слотов от падений
15
+ - [x] Внедрён `refreshMirrorUntilVisible(ctx)` для синхронизации настроек
16
+ - [x] Внедрена нативная CSS-система дизайн-токенов (`--dsw-alias-*`)
17
+ - [x] Шапка карточки снабжена бейджами статуса каталога, тарифа и таймзоны
18
+ - [x] Настройки структурированы по 3 секциям (`Основные параметры`, `Каталог OpenRouter`, `Диагностика`)
19
+ - [x] Модернизирован поповер шапки сессии
20
+ - [x] Обновлена документация (`DESIGN.md`, `README.md`, `docs/README.ru.md`)
21
+ - [x] Поднята версия до `0.7.10` в `package.json`
22
+ - [x] Написаны и успешно пройдены 16 тестов в `test/stability-and-visual.test.mjs` (`npm test` 16/16 pass)
23
+ - [ ] Оформление коммита, push ветки и создание PR #24
24
+ - [ ] Merge PR в `main` и сборка тарбола
25
+ - [ ] Тестирование на проде, публикация релиза v0.7.10 и закрытие Issue #23
@@ -1,24 +1,25 @@
1
- # Task Plan: Stability, Race Condition Prevention & TimeZone Validation (v0.7.8)
1
+ # Task Plan: Issue #23 — Стабилизация кода и редизайн UI карточки настроек по стандарту dsh-clinebot
2
2
 
3
- ## 1. Goal
4
- Strengthen `@goodandready/dsh-cost-meter` runtime resilience:
5
- - Prevent race condition response overwrites in browser `CostMeter` component.
6
- - Validate IANA TimeZone inputs in `CostMeterCard` settings before saving.
7
- - Sanitize `models` query parameter keys in `lib/index.js` route handler.
8
- - Maintain strict backward compatibility, zero regression in automated tests.
3
+ ## Цель
4
+ Устранить дефекты стабильности (холодный старт, event.time, таймаут OpenRouter, модели без слэша) и обновить UI плагина до единого стандарта dsh-clinebot (ErrorBoundary, дизайн-токены, бейджи, секции, адаптивная сетка, поповер).
9
5
 
10
- ## 2. Checklist
11
- - [ ] Bump version to `0.7.8` in `package.json`
12
- - [ ] Implement cancellation flag / abort protection in `lib/client.js` `CostMeter`
13
- - [ ] Implement `isValidTimeZone` helper and validation check in `CostMeterCard`
14
- - [ ] Implement key sanitization (length <= 128 chars, alphanumeric/dash/slash) in `lib/index.js`
15
- - [ ] Add unit tests for timezone check and query sanitization
16
- - [ ] Run test suite (`npm test`)
17
- - [ ] Update `docs/design/DESIGN.md`
18
- - [ ] Conventional Commit & Push
19
- - [ ] Gitea PR & Merge to `main`
20
- - [ ] Test on MiniPC test server (`192.168.1.123`)
21
- - [ ] Production Smoke Verification on MiniAI (`192.168.1.111`)
22
- - [ ] Publish Tag `v0.7.8`, GitHub Release & npm
23
- - [ ] Update production profile from npm
24
- - [ ] Close Gitea Issue #19 and clean up worktree
6
+ ## Задачи
7
+ - [ ] 1. Исправление серверной части (`lib/index.js`):
8
+ - [ ] Инициализация `byLowerId` в начальном состоянии каталога
9
+ - [ ] Валидация `timeMs` в `slotOf(timeMs)`
10
+ - [ ] Добавление таймаута (15с) для запросов к `openrouter.ai`
11
+ - [ ] Поддержка моделей без префикса в `splitRoute`
12
+ - [ ] Экспорт диагностических метаданных (`catalogModelsCount`, `fetchedAt`, `error`) в `/dsh-cost-meter/state`
13
+ - [ ] 2. Обновление клиентской части (`lib/client.js`):
14
+ - [ ] Внедрение `createErrorBoundary()` и обёртка карточки и поповера
15
+ - [ ] Внедрение `refreshMirrorUntilVisible(ctx)`
16
+ - [ ] Единая CSS-система дизайн-токенов (`.dcm-section-card`, `.dcm-badge`, `.dcm-grid-2`, `.dcm-btn`, `.dcm-alert-*`)
17
+ - [ ] Структурирование настроек по 3 логическим секциям с бейджами статуса в шапке
18
+ - [ ] Модернизация поповера шапки на классы дизайн-системы
19
+ - [ ] Локализация новых ключей (`ru`, `en`)
20
+ - [ ] 3. Обновление документации:
21
+ - [ ] Актуализация `docs/design/DESIGN.md`, `README.md`, `docs/README.ru.md`
22
+ - [ ] 4. Тестирование:
23
+ - [ ] Написание юнит-тестов `test/stability-and-visual.test.mjs`
24
+ - [ ] Прогон всех тестов (`npm test`)
25
+ - [ ] 5. Сборка, PR, релиз v0.7.10, верификация на MiniAI и закрытие Issue #23