@goodandready/dsh-cost-meter 0.7.6 → 0.7.7
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/docs/design/DESIGN.md +1 -1
- package/docs/plans/findings.md +16 -0
- package/docs/plans/progress.md +15 -0
- package/docs/plans/task_plan.md +25 -0
- package/lib/client.js +16 -21
- package/package.json +1 -1
package/docs/design/DESIGN.md
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
- Не допускать утечек локальных путей и инфраструктурных данных в коде или документации.
|
|
83
83
|
|
|
84
84
|
## History & Decisions
|
|
85
|
-
- 2026-09-05 (v0.7.
|
|
85
|
+
- 2026-09-05 (v0.7.7):
|
|
86
86
|
- Hotfix: исправлен ReferenceError (t is not defined) в объекте словаря ru (lib/client.js) (#15).
|
|
87
87
|
- Добавлен автоматический тест на чистое исполнение фабрики client loader (`test/public-route.test.mjs`).
|
|
88
88
|
- 2026-09-05 (v0.7.4):
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Technical Findings & Architecture Decisions (v0.7.7)
|
|
2
|
+
|
|
3
|
+
## 1. Zod Usage Rationale
|
|
4
|
+
- `zod` is used in `lib/index.js` for `usageByModelProjection`:
|
|
5
|
+
DeepSeek Harness core projection machinery requires `.parse(view)` method on projection schemas for runtime boundary contract validation. Schemastery does not provide `.parse()`, so `zod` is strictly required here and is not overengineering.
|
|
6
|
+
|
|
7
|
+
## 2. Browser Reactivity & Settings
|
|
8
|
+
- Reactivity in browser UI (`lib/client.js`):
|
|
9
|
+
Using `React.useSyncExternalStore` connected to `ctx.settingsScope` allows instant updates of `currency` and `usdRate` in the header chip and popover without needing a page refresh or roundtrip state reload.
|
|
10
|
+
|
|
11
|
+
## 3. Slot Hygiene
|
|
12
|
+
- Cleaned up slot injection in `lib/client.js`:
|
|
13
|
+
Removed `setTimeout(..., 500)` fallback registering `settings.section`. The standard `settings.plugin.item` slot cleanly renders the settings card inside the Harness Settings view.
|
|
14
|
+
|
|
15
|
+
## 4. Style Isolation & Deduplication
|
|
16
|
+
- Added `data-dsh-plugin="dsh-cost-meter"` attribute to injected `<style>` tags to adhere to DSH UI design guidelines and prevent duplicate stylesheets.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Implementation Progress (v0.7.7)
|
|
2
|
+
|
|
3
|
+
- [x] Create Gitea Issue #17
|
|
4
|
+
- [x] Create isolated worktree `refactor/stability-and-quality`
|
|
5
|
+
- [x] Bump version to 0.7.7 in `package.json`
|
|
6
|
+
- [x] Refactor `lib/client.js` for reactive settings, clean slot registration, and style tagging
|
|
7
|
+
- [x] Verify syntax and run test suite (`npm test`, 9 passed)
|
|
8
|
+
- [ ] Document design contract in `docs/design/DESIGN.md`
|
|
9
|
+
- [ ] Commit changes with conventional commits (`git-antigravity`)
|
|
10
|
+
- [ ] Create Pull Request and merge into `main`
|
|
11
|
+
- [ ] Test package on MiniPC isolated test server (`192.168.1.123:3082`)
|
|
12
|
+
- [ ] Smoke-test package on production candidate (`192.168.1.111:3080`)
|
|
13
|
+
- [ ] Publish v0.7.7 to npm and GitHub Release
|
|
14
|
+
- [ ] Install v0.7.7 from npm on MiniAI and verify live health
|
|
15
|
+
- [ ] Close Gitea Issue #17 and clean up worktree
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Task Plan: Refactor dsh-cost-meter for stability, simplification & performance
|
|
2
|
+
|
|
3
|
+
- **Issue**: Gitea #17
|
|
4
|
+
- **Worktree**: `.worktrees/refactor-stability`
|
|
5
|
+
- **Branch**: `refactor/stability-and-quality`
|
|
6
|
+
|
|
7
|
+
## Phases
|
|
8
|
+
|
|
9
|
+
- [ ] **Phase 1: Architecture & Overengineering Cleanup**
|
|
10
|
+
- Remove `zod` dependency from `lib/index.js`, standardize on `@deepseek-ai/schemastery`.
|
|
11
|
+
- Remove dynamic fragile `require('@deepseek-ai/dsh-client-ui-primitives')` from `lib/client.js`, standardize on clean native inline SVG chevron.
|
|
12
|
+
- Remove race-prone `setTimeout(..., 500)` fallback slot registration in `lib/client.js`.
|
|
13
|
+
- [ ] **Phase 2: Fault Tolerance & Safe Caching**
|
|
14
|
+
- Wrap catalog filesystem operations (`mkdirSync`, `writeFileSync`, `readFileSync`) in safe `try/catch`.
|
|
15
|
+
- Add safe error catching in `lib/client.js` for `/dsh-cost-meter/state` fetch to prevent console error spam on DSH reload.
|
|
16
|
+
- [ ] **Phase 3: Performance & Settings Reactivity**
|
|
17
|
+
- Implement in-memory catalog index Map in `lib/index.js` for O(1) model rate lookups.
|
|
18
|
+
- Subscribe to `settingsScope` updates in `lib/client.js` to dynamically refresh currency and USD rate without reload.
|
|
19
|
+
- [ ] **Phase 4: Verification, Tests & Documentation**
|
|
20
|
+
- Run regression test suite `npm test` and add tests for schema validation and safe catalog failure.
|
|
21
|
+
- Update `docs/design/DESIGN.md` and release notes for v0.7.7.
|
|
22
|
+
- [ ] **Phase 5: Release Gate & Production Rollout**
|
|
23
|
+
- Test on MiniPC test server (`192.168.1.123:3082`).
|
|
24
|
+
- Merge PR, tag v0.7.7, publish to npm & GitHub release.
|
|
25
|
+
- Deploy to production web profile on MiniAI (`192.168.1.111:3080`).
|
package/lib/client.js
CHANGED
|
@@ -131,6 +131,7 @@ window.__ModuleLoader__.load({
|
|
|
131
131
|
if (document.getElementById('dsh-cost-meter-card-styles')) return
|
|
132
132
|
const el = document.createElement('style')
|
|
133
133
|
el.id = 'dsh-cost-meter-card-styles'
|
|
134
|
+
el.dataset.dshPlugin = 'dsh-cost-meter'
|
|
134
135
|
el.textContent = cardCss
|
|
135
136
|
document.head.appendChild(el)
|
|
136
137
|
}
|
|
@@ -471,6 +472,14 @@ window.__ModuleLoader__.load({
|
|
|
471
472
|
const [open, setOpen] = React.useState(false)
|
|
472
473
|
const [now, setNow] = React.useState(() => new Date())
|
|
473
474
|
const box = React.useRef(null)
|
|
475
|
+
const settingsScope = ctx.settingsScope || (ctx.services && ctx.services.settingsScope)
|
|
476
|
+
const settingsSnap = React.useSyncExternalStore
|
|
477
|
+
? React.useSyncExternalStore(
|
|
478
|
+
(cb) => (settingsScope && typeof settingsScope.subscribe === 'function' ? settingsScope.subscribe(cb) : () => {}),
|
|
479
|
+
() => (settingsScope && typeof settingsScope.get === 'function' ? settingsScope.get(NS) : null),
|
|
480
|
+
() => null,
|
|
481
|
+
)
|
|
482
|
+
: null
|
|
474
483
|
|
|
475
484
|
// Every model this session actually billed, so each part is priced on its
|
|
476
485
|
// own tariff instead of the current model's. Usage arrives split into
|
|
@@ -507,8 +516,9 @@ window.__ModuleLoader__.load({
|
|
|
507
516
|
|
|
508
517
|
if (!state) return null
|
|
509
518
|
|
|
510
|
-
const
|
|
511
|
-
const
|
|
519
|
+
const stored = (settingsSnap && settingsSnap.value) || {}
|
|
520
|
+
const currency = stored.currency !== undefined ? String(stored.currency) : (state.currency || '$')
|
|
521
|
+
const fx = stored.usdRate !== undefined && !isNaN(Number(stored.usdRate)) ? Number(stored.usdRate) : (Number(state.usdRate) || 1)
|
|
512
522
|
const schedule = state.schedule
|
|
513
523
|
const hasUsage = usage !== undefined && usage !== null
|
|
514
524
|
const minute = now.getUTCHours() * 60 + now.getUTCMinutes()
|
|
@@ -734,27 +744,12 @@ window.__ModuleLoader__.load({
|
|
|
734
744
|
),
|
|
735
745
|
)
|
|
736
746
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
placed = true
|
|
740
|
-
return ctx.slots.register(
|
|
747
|
+
ctx.slots.inject('settings.plugin.item', () =>
|
|
748
|
+
ctx.slots.register(
|
|
741
749
|
{ name: 'settings.plugin.item', key: NS, locale: NS, order: 20, inject: () => ({ ctx }) },
|
|
742
750
|
(p) => React.createElement(CostMeterCard, { ...p, ctx }),
|
|
743
|
-
)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
// Fallback to settings.section if settings.plugin.item is not declared
|
|
747
|
-
setTimeout(() => {
|
|
748
|
-
if (placed) return
|
|
749
|
-
try {
|
|
750
|
-
ctx.slots.inject('settings.section', () =>
|
|
751
|
-
ctx.slots.register(
|
|
752
|
-
{ name: 'settings.section', id: '@goodandready/dsh-cost-meter', order: 25, locale: NS, label: () => makeT(useActiveLocale(ctx) === 'ru' ? ru : en, en)('title'), inject: () => ({ ctx }) },
|
|
753
|
-
(p) => React.createElement(CostMeterCard, { ...p, ctx }),
|
|
754
|
-
),
|
|
755
|
-
)
|
|
756
|
-
} catch (_) {}
|
|
757
|
-
}, 500)
|
|
751
|
+
),
|
|
752
|
+
)
|
|
758
753
|
}
|
|
759
754
|
|
|
760
755
|
module.exports = { apply, inject: ['slots', 'locale', 'settingsScope'] }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-cost-meter",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "DeepSeek Harness plugin: a session cost chip in the conversation header - live token usage by bucket, peak/off-peak tariff, and a countdown to the next tariff switch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|