pi-mega-compact 0.11.3 → 0.11.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/dist/extensions/dashboard-server/api-contracts/endpoints.js +19 -0
- package/dist/extensions/dashboard-server/api-contracts/memory.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/setup.js +10 -0
- package/dist/extensions/dashboard-server/index-reader.js +6 -2
- package/dist/extensions/dashboard-server/routes-cache.js +115 -55
- package/dist/extensions/dashboard-server/routes-memory.js +51 -0
- package/dist/extensions/dashboard-server/routes-repo.js +2 -2
- package/dist/extensions/dashboard-server/routes-setup.js +162 -0
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +7 -1
- package/dist/extensions/mega-commands.js +189 -1
- package/dist/extensions/mega-config.js +3 -0
- package/dist/extensions/mega-events/agent-handlers.js +41 -6
- package/dist/extensions/mega-events/compact-handlers.js +1 -0
- package/dist/extensions/mega-events/context-handler.js +37 -92
- package/dist/extensions/mega-events/context-hwm.js +51 -0
- package/dist/extensions/mega-events/error-classifier.js +118 -128
- package/dist/extensions/mega-events/mirror-append.js +107 -0
- package/dist/extensions/mega-events/outage-advisor.js +59 -0
- package/dist/extensions/mega-events/perf-handler.js +51 -1
- package/dist/extensions/mega-events/recall-tail.js +59 -0
- package/dist/extensions/mega-events/session-handlers.js +15 -8
- package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
- package/dist/extensions/mega-runtime/runtime.js +7 -0
- package/dist/src/embedder.js +41 -2
- package/dist/src/memoryStats.js +75 -0
- package/dist/src/prefix-break.js +43 -0
- package/dist/src/pricing.js +1 -1
- package/dist/src/store/sqlite/perf-samples.js +144 -16
- package/dist/src/store/turns/memory-store.js +8 -0
- package/dist/src/store/turns/sqlite-store.js +14 -3
- package/dist/src/store/turns/types.js +18 -1
- package/dist/src/store/vectorIndex.js +26 -5
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-BoL7qBOJ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-BoL7qBOJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/AchievementsTab-BFDE3HYc.js +2 -0
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-BFDE3HYc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-DDFE-ckA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-DDFE-ckA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-B6UxZ7PM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-B6UxZ7PM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js → GameTab-DehUX-KE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-DehUX-KE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-0GWRt4wn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-0GWRt4wn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-C3rwMVsN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-C3rwMVsN.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-39O9_x5j.js +2 -0
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-39O9_x5j.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-DXDTCi3r.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-DXDTCi3r.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BAw972BV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BAw972BV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-Cf5mGIdl.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-Cf5mGIdl.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js +2 -0
- package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{index-BX0bcgLu.js → index-4gQ7auad.js} +10 -10
- package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +2 -0
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js → useSSE-DNgMuyP6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-DNgMuyP6.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/App.tsx +4 -0
- package/extensions/dashboard-client/src/api/client.ts +14 -0
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
- package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +56 -0
- package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +38 -57
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +1 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +355 -0
- package/extensions/dashboard-client/tsconfig.json +3 -2
- package/extensions/dashboard-client/vite.config.ts +2 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +36 -3
- package/extensions/dashboard-server/api-contracts/index.ts +7 -0
- package/extensions/dashboard-server/api-contracts/memory.ts +27 -0
- package/extensions/dashboard-server/api-contracts/multi-repo.ts +231 -231
- package/extensions/dashboard-server/api-contracts/provider-cache.ts +37 -0
- package/extensions/dashboard-server/api-contracts/setup.ts +58 -0
- package/extensions/dashboard-server/index-reader.ts +138 -121
- package/extensions/dashboard-server/routes-cache.ts +161 -85
- package/extensions/dashboard-server/routes-memory.ts +63 -0
- package/extensions/dashboard-server/routes-repo.ts +36 -36
- package/extensions/dashboard-server/routes-setup.ts +181 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +6 -0
- package/extensions/mega-commands.ts +211 -1
- package/extensions/mega-config.ts +18 -0
- package/extensions/mega-events/agent-handlers.ts +44 -5
- package/extensions/mega-events/compact-handlers.ts +1 -0
- package/extensions/mega-events/context-handler.ts +45 -110
- package/extensions/mega-events/context-hwm.ts +85 -0
- package/extensions/mega-events/error-classifier.ts +156 -124
- package/extensions/mega-events/mirror-append.ts +155 -0
- package/extensions/mega-events/outage-advisor.ts +79 -0
- package/extensions/mega-events/perf-handler.ts +79 -5
- package/extensions/mega-events/recall-tail.ts +88 -0
- package/extensions/mega-events/session-handlers.ts +14 -7
- package/extensions/mega-runtime/helpers.ts +14 -0
- package/extensions/mega-runtime/reset-runtime.ts +6 -0
- package/extensions/mega-runtime/runtime.ts +7 -0
- package/package.json +5 -2
- package/src/embedder.ts +42 -2
- package/src/memoryStats.ts +107 -0
- package/src/prefix-break.ts +83 -0
- package/src/pricing.ts +2 -2
- package/src/store/sqlite/perf-samples.ts +215 -22
- package/src/store/turns/memory-store.ts +10 -1
- package/src/store/turns/sqlite-store.ts +27 -15
- package/src/store/turns/types.ts +21 -0
- package/src/store/vectorIndex.ts +33 -4
- package/dist/extensions/conflict-scan.test.js +0 -115
- package/dist/extensions/dashboard-server/api-contracts.test.js +0 -884
- package/dist/extensions/dashboard-server/html.test.js +0 -52
- package/dist/extensions/dashboard-server/perf-server.test.js +0 -80
- package/dist/extensions/dashboard-server/routes-cache.test.js +0 -248
- package/dist/extensions/dashboard-server/server.test.js +0 -120
- package/dist/extensions/dashboard-server-s32.test.js +0 -181
- package/dist/extensions/dashboard-server.test.js +0 -339
- package/dist/extensions/mega-cache-replay.test.js +0 -400
- package/dist/extensions/mega-compact-s38.test.js +0 -820
- package/dist/extensions/mega-compact.test.js +0 -1112
- package/dist/extensions/mega-events.test.js +0 -47
- package/dist/extensions/mega-game-cmds.test.js +0 -163
- package/dist/extensions/mega-runtime/snapshot.test.js +0 -127
- package/dist/extensions/mega-runtime/state.test.js +0 -266
- package/dist/extensions/mega-runtime/widget.test.js +0 -343
- package/dist/extensions/mega-shutdown-widget.test.js +0 -121
- package/dist/extensions/mega-teamrun.test.js +0 -156
- package/dist/extensions/mega-trim.test.js +0 -58
- package/dist/extensions/mega-turn-store.test.js +0 -68
- package/dist/src/boundary.test.js +0 -170
- package/dist/src/compact.test.js +0 -78
- package/dist/src/config/themes.test.js +0 -94
- package/dist/src/dedup/dedup.test.js +0 -41
- package/dist/src/dedup/l1.test.js +0 -50
- package/dist/src/dedup/raptor/buildHistory.test.js +0 -292
- package/dist/src/dedup/raptor/multilevel-serve.test.js +0 -229
- package/dist/src/dedup/raptor/multilevel.test.js +0 -238
- package/dist/src/dedup/raptor/promote.test.js +0 -69
- package/dist/src/dedup/raptor/raptor.test.js +0 -248
- package/dist/src/dedup/raptor/retrieval.test.js +0 -95
- package/dist/src/dedup/raptor/serve-gate.test.js +0 -298
- package/dist/src/dedup/sprint12.test.js +0 -219
- package/dist/src/dedup-engine.test.js +0 -472
- package/dist/src/driftDetection.test.js +0 -87
- package/dist/src/e2e.test.js +0 -698
- package/dist/src/engine.test.js +0 -111
- package/dist/src/extractive.test.js +0 -147
- package/dist/src/fork.test.js +0 -81
- package/dist/src/game/scoring.test.js +0 -98
- package/dist/src/httpEmbedder.test.js +0 -277
- package/dist/src/importance.test.js +0 -372
- package/dist/src/intent.test.js +0 -78
- package/dist/src/log.test.js +0 -42
- package/dist/src/mechanical-fix.test.js +0 -65
- package/dist/src/memory.test.js +0 -70
- package/dist/src/memoryConsolidate.test.js +0 -38
- package/dist/src/memoryOps.test.js +0 -129
- package/dist/src/memoryRecall.test.js +0 -171
- package/dist/src/memoryRoundtrip.test.js +0 -137
- package/dist/src/metrics/turns.test.js +0 -152
- package/dist/src/mirror/mirror.test.js +0 -185
- package/dist/src/raptor-inject-summaries.test.js +0 -162
- package/dist/src/ratio.bench.test.js +0 -897
- package/dist/src/recall.integration.test.js +0 -77
- package/dist/src/recall.test.js +0 -379
- package/dist/src/sprint14.test.js +0 -219
- package/dist/src/sprint4x-rag-verification.test.js +0 -93
- package/dist/src/store/compression.test.js +0 -139
- package/dist/src/store/memoryIndex.test.js +0 -51
- package/dist/src/store/migrate.test.js +0 -139
- package/dist/src/store/pgOpenGuard.test.js +0 -74
- package/dist/src/store/phase01.test.js +0 -81
- package/dist/src/store/phase04.test.js +0 -54
- package/dist/src/store/sprint10.test.js +0 -186
- package/dist/src/store/sqlite/game-achievements.test.js +0 -67
- package/dist/src/store/sqlite/game-scores.test.js +0 -106
- package/dist/src/store/sqlite/game-state.test.js +0 -76
- package/dist/src/store/sqlite/global-index.test.js +0 -136
- package/dist/src/store/sqlite/mechanical-fix.test.js +0 -146
- package/dist/src/store/sqlite/perf-samples.test.js +0 -146
- package/dist/src/store/sqlite/raptor.test.js +0 -106
- package/dist/src/store/sqlite/turns.test.js +0 -183
- package/dist/src/store/sqlite.cachehit.test.js +0 -55
- package/dist/src/store/sqlite.dbmaint.test.js +0 -158
- package/dist/src/store/sqlite.dbmirror.test.js +0 -184
- package/dist/src/store/turns/contract-compliance.test.js +0 -428
- package/dist/src/store/turns/memory-store.test.js +0 -9
- package/dist/src/store/turns/migrations.test.js +0 -138
- package/dist/src/store/turns/sqlite-store.test.js +0 -144
- package/dist/src/store/vectorIndex.test.js +0 -123
- package/dist/src/store.test.js +0 -161
- package/dist/src/supersede.test.js +0 -36
- package/dist/src/topics/cluster.test.js +0 -150
- package/dist/src/topics/store.test.js +0 -117
- package/dist/src/types.test.js +0 -9
- package/dist/src/vector-search-cache.test.js +0 -157
- package/dist/src/vectorStore.test.js +0 -479
- package/dist/src/wiki.test.js +0 -171
- package/extensions/conflict-scan.test.ts +0 -129
- package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-D4kGvsiQ.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +0 -2
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js.map +0 -1
- package/extensions/dashboard-server/api-contracts.test.ts +0 -1065
- package/extensions/dashboard-server/html.test.ts +0 -62
- package/extensions/dashboard-server/perf-server.test.ts +0 -101
- package/extensions/dashboard-server/routes-cache.test.ts +0 -302
- package/extensions/dashboard-server/server.test.ts +0 -131
- package/extensions/dashboard-server-s32.test.ts +0 -195
- package/extensions/dashboard-server.test.ts +0 -382
- package/extensions/mega-cache-replay.test.ts +0 -526
- package/extensions/mega-compact-s38.test.ts +0 -872
- package/extensions/mega-compact.test.ts +0 -1509
- package/extensions/mega-events.test.ts +0 -55
- package/extensions/mega-game-cmds.test.ts +0 -197
- package/extensions/mega-runtime/snapshot.test.ts +0 -139
- package/extensions/mega-runtime/state.test.ts +0 -262
- package/extensions/mega-runtime/widget.test.ts +0 -468
- package/extensions/mega-shutdown-widget.test.ts +0 -141
- package/extensions/mega-teamrun.test.ts +0 -176
- package/extensions/mega-trim.test.ts +0 -64
- package/extensions/mega-turn-store.test.ts +0 -108
- package/src/boundary.test.ts +0 -187
- package/src/compact.test.ts +0 -99
- package/src/config/themes.test.ts +0 -116
- package/src/dedup/dedup.test.ts +0 -46
- package/src/dedup/l1.test.ts +0 -57
- package/src/dedup/raptor/buildHistory.test.ts +0 -353
- package/src/dedup/raptor/multilevel-serve.test.ts +0 -273
- package/src/dedup/raptor/multilevel.test.ts +0 -325
- package/src/dedup/raptor/promote.test.ts +0 -82
- package/src/dedup/raptor/raptor.test.ts +0 -297
- package/src/dedup/raptor/retrieval.test.ts +0 -118
- package/src/dedup/raptor/serve-gate.test.ts +0 -348
- package/src/dedup/sprint12.test.ts +0 -242
- package/src/dedup-engine.test.ts +0 -670
- package/src/driftDetection.test.ts +0 -100
- package/src/e2e.test.ts +0 -843
- package/src/engine.test.ts +0 -123
- package/src/extractive.test.ts +0 -174
- package/src/fork.test.ts +0 -94
- package/src/game/scoring.test.ts +0 -103
- package/src/httpEmbedder.test.ts +0 -286
- package/src/importance.test.ts +0 -538
- package/src/intent.test.ts +0 -87
- package/src/log.test.ts +0 -47
- package/src/mechanical-fix.test.ts +0 -70
- package/src/memory.test.ts +0 -92
- package/src/memoryConsolidate.test.ts +0 -47
- package/src/memoryOps.test.ts +0 -148
- package/src/memoryRecall.test.ts +0 -194
- package/src/memoryRoundtrip.test.ts +0 -155
- package/src/metrics/turns.test.ts +0 -165
- package/src/mirror/mirror.test.ts +0 -240
- package/src/raptor-inject-summaries.test.ts +0 -228
- package/src/ratio.bench.test.ts +0 -1316
- package/src/recall.integration.test.ts +0 -96
- package/src/recall.test.ts +0 -467
- package/src/sprint14.test.ts +0 -245
- package/src/sprint4x-rag-verification.test.ts +0 -119
- package/src/store/compression.test.ts +0 -168
- package/src/store/memoryIndex.test.ts +0 -61
- package/src/store/migrate.test.ts +0 -158
- package/src/store/pgOpenGuard.test.ts +0 -89
- package/src/store/phase01.test.ts +0 -88
- package/src/store/phase04.test.ts +0 -58
- package/src/store/sprint10.test.ts +0 -214
- package/src/store/sqlite/game-achievements.test.ts +0 -80
- package/src/store/sqlite/game-scores.test.ts +0 -132
- package/src/store/sqlite/game-state.test.ts +0 -89
- package/src/store/sqlite/global-index.test.ts +0 -222
- package/src/store/sqlite/mechanical-fix.test.ts +0 -162
- package/src/store/sqlite/perf-samples.test.ts +0 -169
- package/src/store/sqlite/raptor.test.ts +0 -139
- package/src/store/sqlite/turns.test.ts +0 -218
- package/src/store/sqlite.cachehit.test.ts +0 -68
- package/src/store/sqlite.dbmaint.test.ts +0 -193
- package/src/store/sqlite.dbmirror.test.ts +0 -229
- package/src/store/turns/contract-compliance.test.ts +0 -562
- package/src/store/turns/memory-store.test.ts +0 -15
- package/src/store/turns/migrations.test.ts +0 -161
- package/src/store/turns/sqlite-store.test.ts +0 -161
- package/src/store/vectorIndex.test.ts +0 -140
- package/src/store.test.ts +0 -191
- package/src/supersede.test.ts +0 -42
- package/src/topics/cluster.test.ts +0 -176
- package/src/topics/store.test.ts +0 -132
- package/src/types.test.ts +0 -10
- package/src/vector-search-cache.test.ts +0 -190
- package/src/vectorStore.test.ts +0 -533
- package/src/wiki.test.ts +0 -217
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* conflict-scan.test.ts — unit tests for the extension-conflict scanner.
|
|
3
|
-
*
|
|
4
|
-
* Fixture trees are written under a temp dir and scanned via
|
|
5
|
-
* MEGACOMPACT_EXT_SCAN_DIR (which makes collectScanRoots() return that
|
|
6
|
-
* single root). This covers the S24 follow-up fix:
|
|
7
|
-
*
|
|
8
|
-
* 1. node_modules-style code extensions (package.json + pi.extensions) are
|
|
9
|
-
* still detected by source-marker grep (regression).
|
|
10
|
-
* 2. USER-LEVEL extensions installed outside npm (e.g. pi-hermes-memory)
|
|
11
|
-
* now get scanned too — previously only `node_modules` was walked, so a
|
|
12
|
-
* data-only memory store (MEMORY.md + sessions.db, no package.json)
|
|
13
|
-
* was never flagged (the 5000-char file-buffer error slipped through).
|
|
14
|
-
* 3. The data-only memory-store signature is detected even with no source.
|
|
15
|
-
* 4. pi-mega-compact (selfName) is always skipped.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { test, after } from "node:test";
|
|
19
|
-
import assert from "node:assert/strict";
|
|
20
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
21
|
-
import { tmpdir } from "node:os";
|
|
22
|
-
import { join } from "node:path";
|
|
23
|
-
import { detectConflicts, collectScanRoots } from "./conflict-scan.js";
|
|
24
|
-
|
|
25
|
-
const base = mkdtempSync(join(tmpdir(), "mc-scan-"));
|
|
26
|
-
let n = 0;
|
|
27
|
-
|
|
28
|
-
/** Make a fixture root containing one or more fake extensions, return its path. */
|
|
29
|
-
function fixture(build: (root: string) => void): string {
|
|
30
|
-
const root = join(base, `case-${n++}`);
|
|
31
|
-
mkdirSync(root, { recursive: true });
|
|
32
|
-
build(root);
|
|
33
|
-
return root;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
after(() => {
|
|
37
|
-
rmSync(base, { recursive: true, force: true });
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("scans a user-level, data-only memory store (no package.json)", () => {
|
|
41
|
-
const root = fixture((r) => {
|
|
42
|
-
const ext = join(r, "pi-hermes-memory");
|
|
43
|
-
mkdirSync(ext, { recursive: true });
|
|
44
|
-
// No package.json, no source — just pi's memory-store signature.
|
|
45
|
-
writeFileSync(join(ext, "MEMORY.md"), "# memory\n");
|
|
46
|
-
writeFileSync(join(ext, "sessions.db"), "");
|
|
47
|
-
});
|
|
48
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
49
|
-
try {
|
|
50
|
-
const { conflicts } = detectConflicts();
|
|
51
|
-
assert.ok(conflicts.length >= 1, "expected a memory conflict");
|
|
52
|
-
const hit = conflicts.find((c) => c.kind === "memory");
|
|
53
|
-
assert.ok(hit, "expected a memory-kind conflict");
|
|
54
|
-
assert.equal(hit!.severity, "high");
|
|
55
|
-
assert.ok(
|
|
56
|
-
hit!.evidence.includes("MEMORY.md") ||
|
|
57
|
-
hit!.evidence.includes("sessions.db"),
|
|
58
|
-
"evidence should name the on-disk memory signature",
|
|
59
|
-
);
|
|
60
|
-
} finally {
|
|
61
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("still detects a code extension by source marker (regression)", () => {
|
|
66
|
-
const root = fixture((r) => {
|
|
67
|
-
// A code extension is a DIRECT child of the scan root (mirrors the
|
|
68
|
-
// node_modules layout: packages live one level under the root).
|
|
69
|
-
const ext = join(r, "some-memory-ext");
|
|
70
|
-
mkdirSync(ext, { recursive: true });
|
|
71
|
-
writeFileSync(
|
|
72
|
-
join(ext, "package.json"),
|
|
73
|
-
JSON.stringify({ name: "some-memory-ext", pi: { extensions: ["x.ts"] } }),
|
|
74
|
-
);
|
|
75
|
-
writeFileSync(join(ext, "index.ts"), "export const MEMORY_TOOL = true;");
|
|
76
|
-
});
|
|
77
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
78
|
-
try {
|
|
79
|
-
const { conflicts } = detectConflicts();
|
|
80
|
-
const hit = conflicts.find((c) => c.package === "some-memory-ext");
|
|
81
|
-
assert.ok(hit, "expected some-memory-ext to be flagged");
|
|
82
|
-
assert.equal(hit!.kind, "memory");
|
|
83
|
-
} finally {
|
|
84
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("skips pi-mega-compact (selfName) and non-extension dirs", () => {
|
|
89
|
-
const root = fixture((r) => {
|
|
90
|
-
// selfName dir with a memory signature — must be ignored.
|
|
91
|
-
const me = join(r, "node_modules", "pi-mega-compact");
|
|
92
|
-
mkdirSync(me, { recursive: true });
|
|
93
|
-
writeFileSync(join(me, "sessions.db"), "");
|
|
94
|
-
// unrelated dir with no pi.extensions and no memory signature.
|
|
95
|
-
mkdirSync(join(r, "node_modules", "totally-fine"), { recursive: true });
|
|
96
|
-
});
|
|
97
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
98
|
-
try {
|
|
99
|
-
const { scanned, conflicts } = detectConflicts();
|
|
100
|
-
assert.equal(conflicts.length, 0, "no conflicts expected");
|
|
101
|
-
assert.ok(
|
|
102
|
-
!scanned.some((s) => s.includes("pi-mega-compact")),
|
|
103
|
-
"selfName should not appear in scanned",
|
|
104
|
-
);
|
|
105
|
-
} finally {
|
|
106
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("collectScanRoots honors MEGACOMPACT_EXT_SCAN_DIR override", () => {
|
|
111
|
-
const root = fixture(() => {});
|
|
112
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
113
|
-
try {
|
|
114
|
-
const roots = collectScanRoots();
|
|
115
|
-
assert.deepEqual(roots, [root], "override replaces the whole root list");
|
|
116
|
-
} finally {
|
|
117
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("collectScanRoots falls back to node_modules + user dir when no override", () => {
|
|
122
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
123
|
-
delete process.env.MEGACOMPACT_EXT_USER_DIR;
|
|
124
|
-
// No override set and this test file lives under extensions/, so node_modules
|
|
125
|
-
// resolution walks up from here; the user dir (~/.pi/agent) may or may
|
|
126
|
-
// not exist in CI. We only assert the call returns a non-throwing array.
|
|
127
|
-
const roots = collectScanRoots();
|
|
128
|
-
assert.ok(Array.isArray(roots), "collectScanRoots must return an array");
|
|
129
|
-
});
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as t,i as m,j as s}from"./index-BX0bcgLu.js";import{A as v}from"./AchievementTiles-vEo2MtE2.js";function x(){const[r,o]=t.useState([]),[l,u]=t.useState(!0),[a,n]=t.useState(null),i=t.useCallback(async()=>{try{const e=await m();o(e),n(null)}catch(e){n(e instanceof Error?e:new Error(String(e)))}finally{u(!1)}},[]);return t.useEffect(()=>{let e=!0;const c=async()=>{e&&await i()};c();const h=setInterval(()=>void c(),15e3);return()=>{e=!1,clearInterval(h)}},[i]),l&&r.length===0?s.jsx("div",{className:"tab-stub",children:"Loading achievements…"}):a&&r.length===0?s.jsxs("div",{className:"tab-stub",children:["Error: ",a.message]}):s.jsxs("div",{className:"achievements-tab",children:[s.jsx("h2",{children:"Achievements"}),s.jsx(v,{achievements:r})]})}export{x as default};
|
|
2
|
-
//# sourceMappingURL=AchievementsTab-DjxfYrDc.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,u as h,r as v,g as S,c as x}from"./index-BX0bcgLu.js";import{b as r,c as i,d as C,e as b,C as j,T}from"./TimeSavedCard-UqljSyP-.js";function f(a){return a>=80?"ov-stat-value-green":a>=50?"ov-stat-value-yellow":"ov-stat-value-red"}function g(a){return a==null?"":a>=0?"ov-stat-value-green":"ov-stat-value-red"}function t({label:a,value:o,className:s,title:c}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:c,children:a}),e.jsx("span",{className:`ov-stat-value${s?` ${s}`:""}`,children:o})]})}function p(a){return e.jsxs("div",{className:"card provider-cache-card",children:[e.jsx(t,{label:"Cache Hit Rate",value:`${a.hitPct.toFixed(1)}%`,className:f(a.hitPct)}),e.jsx(t,{label:"Turns Recorded",value:String(a.turnCount)}),e.jsx(t,{label:"Cache Read Tokens",value:r(a.totalCacheRead)}),e.jsx(t,{label:"Cache Write Tokens",value:r(a.totalCacheWrite)}),e.jsx(t,{label:"Input Tokens",value:r(a.totalInput)}),e.jsx(t,{label:"$ Saved (reads)",value:i(a.cacheReadSaved)}),e.jsx(t,{label:"Write Investment",value:i(a.cacheWriteCost)}),e.jsx(t,{label:"Net Saved",value:i(a.netSaved),className:g(a.netSaved)}),e.jsx(t,{label:"Model",value:a.modelLabel??"—"}),e.jsx(t,{label:"Tracked Since",value:C(a.firstTurnAt)}),e.jsx(t,{label:"Last Updated",value:b(a.latestTurnAt)})]})}function k(){const{data:a,loading:o,error:s}=h(v.useCallback(()=>S(),[]),{pollInterval:5e3}),{data:c,loading:m,error:d}=h(v.useCallback(()=>x(),[]),{pollInterval:5e3});return e.jsxs("div",{className:"cache-tab",children:[e.jsx("h2",{children:"Provider Prompt Cache"}),o?e.jsx("p",{className:"tab-stub",children:"Loading…"}):s?e.jsxs("p",{className:"tab-stub",children:["Provider cache unavailable: ",s.message]}):a?e.jsx(p,{hitPct:a.cache.avgHitPct,turnCount:a.cache.turnCount,totalCacheRead:a.cache.totalCacheRead,totalCacheWrite:a.cache.totalCacheWrite,totalInput:a.cache.totalInput,cacheReadSaved:a.savings?.cacheReadSaved??null,cacheWriteCost:a.savings?.cacheWriteCost??null,netSaved:a.savings?.netSaved??null,modelLabel:a.savings?.model??null,firstTurnAt:a.cache.firstTurnAt,latestTurnAt:a.cache.latestTurnAt}):null,e.jsx("h2",{children:"Mega-Compact Dedup Cache"}),m?e.jsx("p",{className:"tab-stub",children:"Loading…"}):d?e.jsxs("p",{className:"tab-stub",children:["Snapshot unavailable: ",d.message]}):c&&(()=>{const{cacheHits:l,compacts:u,timeSaved:n}=c;return e.jsxs("div",{className:"card-grid overview-card-grid",children:[e.jsx(j,{cacheHitsSession:l.session,cacheHitsTotal:l.total,tokensSavedSession:l.sessionTokensSaved,tokensSavedTotal:l.totalTokensSaved,compactionsSession:u.session,compactionsTotal:u.total}),e.jsx(T,{compactSessionSec:n.compact.sessionSec,compactTotalSec:n.compact.totalSec,cacheHitSessionSec:n.cacheHit.sessionSec,cacheHitTotalSec:n.cacheHit.totalSec})]})})()]})}export{k as default};
|
|
2
|
-
//# sourceMappingURL=CacheTab-Dnpv94wq.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CacheTab-Dnpv94wq.js","sources":["../../src/components/ProviderCacheCard.tsx","../../src/tabs/CacheTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/ProviderCacheCard.tsx — Provider prompt cache stats card.\n *\n * Displays lifetime provider prompt cache hit-rate aggregates + estimated\n * dollar savings from the /api/provider-cache endpoint.\n * Reuses the `ov-stat-row` / `StatRow` pattern from CacheHitsCard.tsx.\n *\n * B.2 spec: Cache Hit Rate with color classes, humanized token counts,\n * priced $ rows with green/red net, model name, locale date + relative time.\n */\n\nimport type React from \"react\";\nimport {\n\tfmtTokens,\n\tfmtDollars,\n\tfmtDate,\n\tfmtRelativeTime,\n} from \"../utils/format\";\n\n// ---------------------------------------------------------------------------\n// Flattened props — destructured from ProviderCacheResponse in CacheTab.\n// ---------------------------------------------------------------------------\n\nexport interface ProviderCacheCardProps {\n\t/** Cache hit rate percentage (0–100). */\n\thitPct: number;\n\t/** Number of turns with cache_hit_pct samples. */\n\tturnCount: number;\n\t/** Total cache-read tokens across all sampled turns. */\n\ttotalCacheRead: number;\n\t/** Total cache-write tokens across all sampled turns. */\n\ttotalCacheWrite: number;\n\t/** Total input tokens across all sampled turns. */\n\ttotalInput: number;\n\t/** $ saved from cache reads (null when unpriced). */\n\tcacheReadSaved: number | null;\n\t/** $ spent on cache writes (null when unpriced). */\n\tcacheWriteCost: number | null;\n\t/** netSaved = cacheReadSaved - cacheWriteCost (null when unpriced). */\n\tnetSaved: number | null;\n\t/** Model display name (modelName || modelId, or null). */\n\tmodelLabel: string | null;\n\t/** ISO timestamp of first recorded turn. */\n\tfirstTurnAt: string | null;\n\t/** ISO timestamp of latest recorded turn. */\n\tlatestTurnAt: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Hit-rate color: green ≥80, yellow ≥50, red <50. */\nfunction hitPctClass(pct: number): string {\n\tif (pct >= 80) return \"ov-stat-value-green\";\n\tif (pct >= 50) return \"ov-stat-value-yellow\";\n\treturn \"ov-stat-value-red\";\n}\n\n/** Net-saved color: green positive, red negative. */\nfunction netClass(v: number | null): string {\n\tif (v == null) return \"\";\n\treturn v >= 0 ? \"ov-stat-value-green\" : \"ov-stat-value-red\";\n}\n\nfunction StatRow({\n\tlabel,\n\tvalue,\n\tclassName,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\tclassName?: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"ov-stat-row\">\n\t\t\t<span className=\"ov-stat-label\" title={title}>\n\t\t\t\t{label}\n\t\t\t</span>\n\t\t\t<span className={`ov-stat-value${className ? ` ${className}` : \"\"}`}>\n\t\t\t\t{value}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\nexport function ProviderCacheCard(\n\tprops: ProviderCacheCardProps,\n): React.ReactElement {\n\treturn (\n\t\t<div className=\"card provider-cache-card\">\n\t\t\t{/* --- Hit Rate + Turns --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hit Rate\"\n\t\t\t\tvalue={`${props.hitPct.toFixed(1)}%`}\n\t\t\t\tclassName={hitPctClass(props.hitPct)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Turns Recorded\" value={String(props.turnCount)} />\n\n\t\t\t{/* --- Token counts --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Read Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheRead)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Write Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheWrite)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Input Tokens\" value={fmtTokens(props.totalInput)} />\n\n\t\t\t{/* --- Dollar rows (null when unpriced) --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"$ Saved (reads)\"\n\t\t\t\tvalue={fmtDollars(props.cacheReadSaved)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Write Investment\"\n\t\t\t\tvalue={fmtDollars(props.cacheWriteCost)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Net Saved\"\n\t\t\t\tvalue={fmtDollars(props.netSaved)}\n\t\t\t\tclassName={netClass(props.netSaved)}\n\t\t\t/>\n\n\t\t\t{/* --- Model + timestamps --- */}\n\t\t\t<StatRow label=\"Model\" value={props.modelLabel ?? \"\\u2014\"} />\n\t\t\t<StatRow label=\"Tracked Since\" value={fmtDate(props.firstTurnAt)} />\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Last Updated\"\n\t\t\t\tvalue={fmtRelativeTime(props.latestTurnAt)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/CacheTab.tsx — Cache tab (B.3).\n *\n * Two sections, each with its own header:\n * 1. Provider Prompt Cache → ProviderCacheCard (/api/provider-cache)\n * 2. Mega-Compact Dedup Cache → CacheHitsCard + TimeSavedCard (/api/snapshot)\n *\n * Both fetch at 5s polling (same interval). Loading / error shown per-section\n * only when its own fetch fails; the other section keeps rendering.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type { SnapshotResponse, ProviderCacheResponse } from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchSnapshot, fetchProviderCache } from \"../api/client\";\nimport { CacheHitsCard } from \"../components/CacheHitsCard\";\nimport { TimeSavedCard } from \"../components/TimeSavedCard\";\nimport { ProviderCacheCard } from \"../components/ProviderCacheCard\";\n\nexport default function CacheTab(): React.ReactElement {\n\t/* --- Provider prompt cache (5s poll) --- */\n\tconst {\n\t\tdata: providerCache,\n\t\tloading: pcLoading,\n\t\terror: pcError,\n\t} = useApi<ProviderCacheResponse>(\n\t\tuseCallback(() => fetchProviderCache(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\t/* --- Dedup cache snapshot (5s poll) --- */\n\tconst {\n\t\tdata: snapshot,\n\t\tloading: snapLoading,\n\t\terror: snapError,\n\t} = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\treturn (\n\t\t<div className=\"cache-tab\">\n\t\t\t{/* ==============================================================\n\t\t\t Section 1 — Provider Prompt Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2>Provider Prompt Cache</h2>\n\t\t\t{pcLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading…</p>\n\t\t\t) : pcError ? (\n\t\t\t\t<p className=\"tab-stub\">\n\t\t\t\t\tProvider cache unavailable: {pcError.message}\n\t\t\t\t</p>\n\t\t\t) : providerCache ? (\n\t\t\t\t<ProviderCacheCard\n\t\t\t\t\thitPct={providerCache.cache.avgHitPct}\n\t\t\t\t\tturnCount={providerCache.cache.turnCount}\n\t\t\t\t\ttotalCacheRead={providerCache.cache.totalCacheRead}\n\t\t\t\t\ttotalCacheWrite={providerCache.cache.totalCacheWrite}\n\t\t\t\t\ttotalInput={providerCache.cache.totalInput}\n\t\t\t\t\tcacheReadSaved={providerCache.savings?.cacheReadSaved ?? null}\n\t\t\t\t\tcacheWriteCost={providerCache.savings?.cacheWriteCost ?? null}\n\t\t\t\t\tnetSaved={providerCache.savings?.netSaved ?? null}\n\t\t\t\t\tmodelLabel={providerCache.savings?.model ?? null}\n\t\t\t\t\tfirstTurnAt={providerCache.cache.firstTurnAt}\n\t\t\t\t\tlatestTurnAt={providerCache.cache.latestTurnAt}\n\t\t\t\t/>\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 2 — Mega-Compact Dedup Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2>Mega-Compact Dedup Cache</h2>\n\t\t\t{snapLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading…</p>\n\t\t\t) : snapError ? (\n\t\t\t\t<p className=\"tab-stub\">Snapshot unavailable: {snapError.message}</p>\n\t\t\t) : (\n\t\t\t\tsnapshot &&\n\t\t\t\t(() => {\n\t\t\t\t\tconst { cacheHits, compacts, timeSaved } = snapshot;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div className=\"card-grid overview-card-grid\">\n\t\t\t\t\t\t\t<CacheHitsCard\n\t\t\t\t\t\t\t\tcacheHitsSession={cacheHits.session}\n\t\t\t\t\t\t\t\tcacheHitsTotal={cacheHits.total}\n\t\t\t\t\t\t\t\ttokensSavedSession={cacheHits.sessionTokensSaved}\n\t\t\t\t\t\t\t\ttokensSavedTotal={cacheHits.totalTokensSaved}\n\t\t\t\t\t\t\t\tcompactionsSession={compacts.session}\n\t\t\t\t\t\t\t\tcompactionsTotal={compacts.total}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<TimeSavedCard\n\t\t\t\t\t\t\t\tcompactSessionSec={timeSaved.compact.sessionSec}\n\t\t\t\t\t\t\t\tcompactTotalSec={timeSaved.compact.totalSec}\n\t\t\t\t\t\t\t\tcacheHitSessionSec={timeSaved.cacheHit.sessionSec}\n\t\t\t\t\t\t\t\tcacheHitTotalSec={timeSaved.cacheHit.totalSec}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})()\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["hitPctClass","pct","netClass","v","StatRow","label","value","className","title","jsxs","jsx","ProviderCacheCard","props","fmtTokens","fmtDollars","fmtDate","fmtRelativeTime","CacheTab","providerCache","pcLoading","pcError","useApi","useCallback","fetchProviderCache","snapshot","snapLoading","snapError","fetchSnapshot","cacheHits","compacts","timeSaved","CacheHitsCard","TimeSavedCard"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,u as H,r as C,f as I}from"./index-BX0bcgLu.js";import{u as A}from"./useSSE-BBYt5UPB.js";import{f as g,a as E,C as _,T as G}from"./TimeSavedCard-UqljSyP-.js";const $=["#58a6ff","#3fb950","#d29922","#f85149","#bc8cff","#ff7b72","#79c0ff","#56d4dd","#e3b341","#a5d6ff","#7ee787","#ffa657"];function B(s){let t=5381;for(let a=0;a<s.length;a++)t=(t<<5)+t+s.charCodeAt(a)>>>0;return t}function U(s){return s?$[B(s)%$.length]:$[0]}function K(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function z(s){const t=new Map;for(const o of s){const i=o.repoRoot??"__null__",u=o.tokens??0,m=t.get(i);if(m)m.tokens+=u;else{const c=K(o.repoRoot);t.set(i,{repoRoot:o.repoRoot,basename:c,tokens:u,color:U(c)})}}const a=[...t.values()],r=a.reduce((o,i)=>o+i.tokens,0),l=s.reduce((o,i)=>Math.max(o,i.ctxWindow),0);return{repos:a,totalTokens:r,maxWindow:l,sessionCount:s.length}}function R(s){return s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function V({sessions:s,loading:t,error:a}){if(a)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]})]});if(t&&!s)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"})]});const r=s?.sessions??[],{repos:l,totalTokens:o,maxWindow:i,sessionCount:u}=z(r);if(l.length===0)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"No active sessions."})]});const m=i>0?Math.round(o/i*100):0;return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"repo-stack-bar",role:"group","aria-label":"Per-repo context usage",children:l.map(c=>{const n=o>0?c.tokens/o*100:0,h=o>0?n:0,d=`${c.basename}: ${R(c.tokens)} tokens (${n.toFixed(1)}% of total)`;return e.jsx("div",{className:"repo-stack-segment",style:{width:`${Math.max(h,.5)}%`,backgroundColor:c.color},title:d,"aria-label":d},c.repoRoot??"__null__")})}),e.jsxs("p",{className:"repo-stack-total",children:[R(o)," tokens across ",l.length," ",l.length===1?"repo":"repos"," /"," ",u," ",u===1?"session":"sessions"," ",e.jsxs("span",{className:"pct",children:["— ",m,"% of combined max window"]})]}),e.jsx("div",{className:"repo-stack-legend",children:l.map(c=>{const n=o>0?c.tokens/o*100:0;return e.jsxs("div",{className:"repo-stack-legend-item",children:[e.jsx("span",{className:"repo-stack-legend-swatch",style:{backgroundColor:c.color}}),e.jsx("span",{className:"repo-stack-legend-name",children:c.basename}),e.jsxs("span",{children:[R(c.tokens)," (",n.toFixed(0),"%)"]})]},c.repoRoot??"__null__")})})]})}function q(s){return s>=90?"gauge-red":s>=70?"gauge-yellow":"gauge-green"}function X(s){return s==null?"?":s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function J(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function Q(s){const t=Math.max(0,Math.round(s/1e3));if(t<60)return`${t}s ago`;const a=Math.floor(t/60);return a<60?`${a}m ago`:`${Math.floor(a/60)}h ${a%60}m ago`}function W({data:s}){const t=s.percent??0,a=Math.max(t,1),r=q(t),l=X(s.tokens),o=s.contextWindow.toLocaleString(),i=`${l} / ${o} tokens`;return e.jsxs("div",{className:`session-gauge ${s.isSelf?"session-gauge-self":""}`,children:[e.jsxs("div",{className:"session-gauge-head",children:[e.jsx("span",{className:"session-gauge-label",children:s.label}),e.jsxs("span",{className:"session-gauge-pct",children:[t,"%"]})]}),e.jsx("div",{className:`gauge-bar ${r}`,role:"meter","aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,"aria-label":`${s.label}: ${t}%`,children:e.jsx("div",{className:"gauge-fill",style:{width:`${a}%`}})}),e.jsx("p",{className:"gauge-label session-gauge-sub",children:i}),s.sublabel&&e.jsx("p",{className:"session-gauge-meta",children:s.sublabel})]})}function Y({sessions:s,loading:t,error:a,launcherSessionId:r,launcher:l}){const o=Date.now(),i=[{key:"self",label:"this session",sublabel:"",tokens:l.tokens,percent:l.percent,contextWindow:l.contextWindow,isSelf:!0}],u=(s?.sessions??[]).filter(n=>n.sessionId!==r);u.sort((n,h)=>h.lastSeen-n.lastSeen);for(const n of u)i.push({key:n.sessionId,label:J(n.repoRoot),sublabel:`pid ${n.pid} · ${Q(o-n.lastSeen)}${n.model?` · ${n.model}`:""}`,tokens:n.tokens,percent:n.percent,contextWindow:n.ctxWindow||0,isSelf:!1});const m=a&&!s,c=t&&!s;return e.jsxs("div",{className:"card session-gauges-card",children:[e.jsx("h3",{children:"Context per Session (live)"}),m?e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]}):c?e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"}):i.length<=1&&u.length===0?e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))}):e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))})]})}function M({state:s,label:t}){const a=s==="on"?"bullet-on":s==="na"?"bullet-na":"bullet-off";return e.jsxs("div",{className:`bullet ${a}`,children:[e.jsx("span",{className:"bullet-dot","aria-hidden":"true"}),e.jsx("span",{className:"bullet-label",children:t})]})}function Z({armed:s,ready:t}){const a=s?"on":"off",r=t?"on":s?"off":"na",l=t?"THRESHOLD EXCEEDED — compacting next event":s?"past fast gate — monitoring token count":"idle — below fast gate";return e.jsxs("div",{className:"card trigger-status",children:[e.jsx("h3",{children:"Trigger Status"}),e.jsxs("div",{className:"bullet-row",children:[e.jsx(M,{state:a,label:"Armed"}),e.jsx(M,{state:r,label:"Ready"})]}),e.jsx("p",{className:"trigger-state-text",children:l})]})}function ee(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function p({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function se(s){const t=s.compressionPct,a=ee(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card vector-store-card",children:[e.jsx("h3",{children:"Vector Store (session)"}),e.jsx(p,{label:"Checkpoints",value:s.checkpointCount.toLocaleString(),title:"A saved summary of a chunk of your conversation that was compacted to free up space."}),e.jsx(p,{label:"Original (dropped)",value:s.tokensIn.toLocaleString(),title:"Total size of the original conversation text dropped into compaction this session, including redundant regions skipped by dedup. This is the 'in'."}),e.jsx(p,{label:"Kept (summaries)",value:s.tokensOut.toLocaleString(),title:"Compact summaries we are currently holding as 'memory' for this session (the 'out'). Smaller is better."}),e.jsx(p,{label:"Freed",value:s.tokensFreed.toLocaleString(),title:"Conversation space freed = dropped − kept (the 'saved')."}),e.jsx(p,{label:"Injected",value:s.injectedCount,title:"How many times old context was automatically brought back into the conversation because it was relevant to what you were doing."}),e.jsx(p,{label:"Recall Relevance",value:g(s.dedupHitRate),title:"Of the times we recalled old context, how often it was actually on-topic."}),e.jsx(p,{label:"Storage Dedup",value:g(s.storageDedupRate),title:"How often new content matched something we already had, so we skipped storing a duplicate copy. Higher = less wasted space."}),e.jsx(p,{label:"Collapsed",value:s.dedupCollapsed,title:"How many duplicate chunks we collapsed into one instead of storing separately."}),e.jsx(p,{label:"Last ID",value:s.lastCheckpointId??"—",title:"The ID of the most recent saved checkpoint."}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function te(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function x({label:s,value:t}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function ae(s){const t=s.compressionPct,a=te(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card repo-sessions-card",children:[e.jsx("h3",{children:"Repo (all sessions)"}),e.jsx(x,{label:"Checkpoints",value:s.checkpointCount.toLocaleString()}),e.jsx(x,{label:"Original",value:s.tokensIn.toLocaleString()}),e.jsx(x,{label:"Kept",value:s.tokensOut.toLocaleString()}),e.jsx(x,{label:"Freed",value:s.tokensFreed.toLocaleString()}),e.jsx(x,{label:"Sessions",value:s.sessionCount.toLocaleString()}),e.jsx(x,{label:"Collapsed",value:s.dedupCollapsed}),e.jsx(x,{label:"Storage Dedup",value:g(s.storageDedupRate)}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function ne(s){return e.jsxs("div",{className:"card data-safety-card",children:[e.jsx("h3",{children:"🛡 Data Safety"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Regions Retained"}),e.jsx("span",{className:"ov-stat-value",children:s.regionsRetained.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Compressed-Original"}),e.jsx("span",{className:"ov-stat-value",children:E(s.compressedOriginalBytes)})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Dedup Duplicates"}),e.jsx("span",{className:"ov-stat-value",children:s.duplicatesCollapsed.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Permanently Deleted"}),e.jsx("span",{className:"ov-stat-value ov-value-ok",children:E(s.bytesPermanentlyDeleted)})]}),e.jsx("p",{className:"safe-note",children:'Every compacted region is kept verbatim (compressed). "Drop" = removed from the live window only. We never delete your data.'})]})}function v({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function oe(s){const t=`${Math.round((s.pressure??0)*100)}%`;let a=s.thresholdTokens.toLocaleString();return s.tierPct!=null&&s.contextWindow>0&&(a+=` (${Math.round(s.tierPct*100)}% of ${s.contextWindow.toLocaleString()})`),e.jsxs("div",{className:"card config-summary-card",children:[e.jsx("h3",{children:"Configuration"}),e.jsx(v,{label:"Tier",value:`${s.tier} (live)`,title:"Live pressure band — climbs low→mega as context fills the window."}),e.jsx(v,{label:"Preset",value:s.presetTier,title:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold."}),e.jsx(v,{label:"Pressure",value:t,title:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window)."}),e.jsx(v,{label:"Threshold",value:a,title:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size."}),e.jsx(v,{label:"Fast Gate",value:`${s.fastGatePct}%`,title:"Fast-gate arming floor — the live trim arms once context passes this % of the window."}),e.jsx(v,{label:"Auto",value:s.auto?"enabled":"disabled"}),e.jsx(v,{label:"Anchor",value:String(s.anchorUserMessages)})]})}function T({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function le(s){const t=s.repoTokensFreed||0;let a=0,r="0";if(s.inputRate&&t>0){a=t*s.inputRate;const i=s.contextWindow||0;r=i>0?(t/i).toFixed(1):"0"}const l=`≈ $${a.toFixed(4)} saved`,o=`${r} context-windows extended`;return e.jsxs("div",{className:"card model-cost-card",children:[e.jsx("h3",{children:"💰 Model & Cost Savings"}),e.jsx("div",{className:"ov-cost-usd",children:l}),e.jsx("div",{className:"ov-cost-sub",children:o}),e.jsx(T,{label:"Model",value:s.name,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(T,{label:"Provider",value:s.provider,title:"The provider serving the model."}),e.jsx(T,{label:"Input Rate",value:`$${s.inputRate.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(T,{label:"Output Rate",value:`$${s.outputRate.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function re(s){const{activeAgents:t,currentTurn:a}=s,r=t>0?`▶ ${t} running`:"idle";return e.jsxs("div",{className:"card crew-card",children:[e.jsx("h3",{children:"Crew / Agents"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Active Agents"}),e.jsx("span",{className:"ov-stat-value",children:t})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Current Turn"}),e.jsx("span",{className:"ov-stat-value",children:a})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Status"}),e.jsx("span",{className:"ov-stat-value",children:r})]})]})}const ie=[e.jsxs("li",{children:[e.jsx("b",{children:"Original (dropped)"}),' — everything compacted away (including duplicates caught by dedup). The "in."']},"1"),e.jsxs("li",{children:[e.jsx("b",{children:"Kept (summaries)"}),' — compact summaries still held as "memory" (the "out").']},"2"),e.jsxs("li",{children:[e.jsx("b",{children:"Freed"})," = dropped − kept — tokens saved so far (higher = better)."]},"3"),e.jsxs("li",{children:[e.jsx("b",{children:"Compression %"})," — Freed ÷ Dropped — the headline efficiency number. Higher = more space reclaimed."]},"4"),e.jsxs("li",{children:[e.jsx("b",{children:"Storage dedup %"})," — how often new content matched something already saved, so no duplicate copy was written."]},"5"),e.jsxs("li",{children:[e.jsx("b",{children:"Data safety"})," — every compacted region is kept verbatim (compressed). Nothing is permanently deleted; you can restore any of it."]},"6")];function ce(){return e.jsx("div",{className:"card legend-card",children:e.jsxs("details",{children:[e.jsx("summary",{children:"What these numbers mean"}),e.jsx("ol",{className:"legend-list",children:ie}),e.jsx("p",{className:"legend-note",children:"Hover any label above for a quick explanation."})]})})}function de(s){return s.type==="session_sample"}function ue(s){if(!s)return"never";try{return new Date(s).toLocaleString()}catch{return s}}function xe({snapshot:s,loading:t,error:a}){const{data:r,error:l,loading:o,refetch:i}=H(C.useCallback(()=>I(),[]),{pollInterval:2e3}),{events:u}=A(),m=C.useMemo(()=>u.filter(de),[u]),c=C.useMemo(()=>m.length===0?null:m[m.length-1].ts,[m]);if(C.useEffect(()=>{c!=null&&i()},[c,i]),t&&!s)return e.jsx("div",{className:"tab-stub",children:"Loading snapshot…"});if(a&&!s)return e.jsxs("div",{className:"tab-stub",children:["Error: ",a.message]});if(!s)return e.jsx("div",{className:"tab-stub",children:"No snapshot data."});const n=s,{context:h,compression:d,trigger:f,session:P,store:b,tier:D,updatedAt:O}=n,k=n.config,L=n.crew,w=n.integrity,N=n.cacheHits,F=n.compacts,S=n.timeSaved,y=n.repo,j=n.model;return e.jsxs("div",{className:"overview-tab",children:[e.jsxs("div",{className:"overview-header",children:[e.jsx("span",{className:"tier-pill",children:D}),e.jsxs("span",{className:"updated",children:["updated ",ue(O)]})]}),e.jsxs("div",{className:"card-grid overview-card-grid",children:[e.jsx(Y,{sessions:r,loading:o,error:l,launcherSessionId:P.id,launcher:{tokens:h.tokens,percent:h.percent,contextWindow:h.contextWindow}}),e.jsx(V,{sessions:r,loading:o,error:l}),e.jsx(Z,{armed:f.armed,ready:f.ready,currentTokens:f.currentTokens,thresholdTokens:f.thresholdTokens,fastGatePct:f.fastGatePct}),e.jsx(se,{checkpointCount:b.checkpointCount,tokensIn:d.session.tokensIn,tokensOut:d.session.tokensOut,tokensFreed:d.session.tokensFreed,injectedCount:b.injectedCount,dedupHitRate:b.dedupHitRate,storageDedupRate:b.storageDedupRate,dedupCollapsed:b.dedupCollapsed,lastCheckpointId:P.lastCheckpointId,compressionPct:d.session.compressionPct,dedupPct:d.session.dedupPct}),e.jsx(ae,{checkpointCount:y.checkpointCount,tokensIn:d.repo.tokensIn,tokensOut:d.repo.tokensOut,tokensFreed:d.repo.tokensFreed,sessionCount:y.sessionCount,dedupCollapsed:y.dedupCollapsed,storageDedupRate:y.storageDedupRate,compressionPct:d.repo.compressionPct,dedupPct:d.repo.dedupPct}),e.jsx(ne,{regionsRetained:w.regionsRetained,compressedOriginalBytes:w.compressedOriginalBytes,duplicatesCollapsed:w.duplicatesCollapsed,bytesPermanentlyDeleted:w.bytesPermanentlyDeleted}),e.jsx(oe,{tier:D,presetTier:n.presetTier,pressure:n.pressure,thresholdTokens:k.thresholdTokens,tierPct:k.tierPct,contextWindow:h.contextWindow,fastGatePct:k.fastGatePct,auto:k.auto,anchorUserMessages:k.anchorUserMessages}),j&&e.jsx(le,{name:j.name,provider:j.providerName||j.provider,inputRate:j.inputRate,outputRate:j.outputRate,repoTokensFreed:d.repo.tokensFreed,contextWindow:h.contextWindow}),e.jsx(re,{activeAgents:L.activeAgents,currentTurn:L.currentTurn}),e.jsx(_,{cacheHitsSession:N.session,cacheHitsTotal:N.total,tokensSavedSession:N.sessionTokensSaved,tokensSavedTotal:N.totalTokensSaved,compactionsSession:F.session,compactionsTotal:F.total}),e.jsx(G,{compactSessionSec:S.compact.sessionSec,compactTotalSec:S.compact.totalSec,cacheHitSessionSec:S.cacheHit.sessionSec,cacheHitTotalSec:S.cacheHit.totalSec}),e.jsx(ce,{})]})]})}export{xe as default};
|
|
2
|
-
//# sourceMappingURL=OverviewTab-D4kGvsiQ.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,r as o,u as y,a as L,b as M}from"./index-BX0bcgLu.js";function O(s){return s>=1048576?`${(s/1048576).toFixed(1)} MiB`:s>=1024?`${(s/1024).toFixed(1)} KiB`:`${s} B`}function F(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function A({totalRepos:s,totalCheckpoints:a,totalTokensSaved:t,compressedOriginalBytes:c}){return e.jsxs("div",{className:"summary-tiles",children:[e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Repositories"}),e.jsx("span",{className:"tile-value",children:s.toLocaleString()})]}),e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Total Checkpoints"}),e.jsx("span",{className:"tile-value",children:a.toLocaleString()})]}),e.jsxs("div",{className:"tile tile-highlight",children:[e.jsx("span",{className:"tile-label",children:"Total Tokens Saved"}),e.jsx("span",{className:"tile-value",children:F(t)})]}),e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Compressed-Original"}),e.jsx("span",{className:"tile-value",children:O(c)})]})]})}const R=[{key:"displayName",label:"Repo"},{key:"modelName",label:"Model"},{key:"checkpointCount",label:"Checkpoints"},{key:"tokensSaved",label:"Tokens Saved"},{key:"compressedOriginalBytes",label:"Retained"},{key:"lastCompactedAt",label:"Last Compacted"}];function B(s){return s>=1048576?`${(s/1048576).toFixed(1)} MiB`:s>=1024?`${(s/1024).toFixed(1)} KiB`:`${s} B`}function I(s){if(s==null)return"—";try{return new Date(s).toLocaleString()}catch{return String(s)}}function W({repos:s,onSelect:a}){const[t,c]=o.useState(""),[n,d]=o.useState("lastCompactedAt"),[i,x]=o.useState("desc"),v=o.useMemo(()=>{const l=t.trim().toLowerCase(),p=l?s.filter(N=>N.displayName.toLowerCase().includes(l)):s.slice();return p.sort((N,T)=>{const m=N[n],h=T[n],k=m==null,f=h==null;if(k&&f)return 0;if(k)return i==="desc"?1:-1;if(f)return i==="desc"?-1:1;const S=typeof m=="number"&&typeof h=="number"?m-h:String(m).localeCompare(String(h));return i==="desc"?-S:S}),p},[s,t,n,i]),w=l=>{n===l?x(p=>p==="asc"?"desc":"asc"):(d(l),x(l==="lastCompactedAt"||l==="tokensSaved"||l==="compressedOriginalBytes"?"desc":"asc"))};return e.jsxs("div",{className:"repo-table-wrap",children:[e.jsx("input",{type:"search",className:"repo-search",placeholder:"Search repos…",value:t,onChange:l=>c(l.target.value),"aria-label":"Search repos"}),e.jsxs("table",{className:"repo-table",children:[e.jsx("thead",{children:e.jsx("tr",{children:R.map(l=>e.jsx("th",{children:e.jsxs("button",{type:"button",className:`sort-header ${n===l.key?`active-${i}`:""}`,onClick:()=>w(l.key),children:[l.label,n===l.key&&e.jsx("span",{className:"sort-arrow",children:i==="asc"?" ▲":" ▼"})]})},l.key))})}),e.jsxs("tbody",{children:[v.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:R.length,className:"repo-empty",children:s.length===0?"No repositories registered yet.":"No repos match your search."})}),v.map(l=>e.jsxs("tr",{onClick:()=>a(l),children:[e.jsx("td",{className:"repo-name",title:l.repoRoot,children:l.displayName}),e.jsx("td",{className:"repo-model",children:l.modelName??"—"}),e.jsx("td",{className:"num",children:l.checkpointCount.toLocaleString()}),e.jsx("td",{className:"num",children:l.tokensSaved.toLocaleString()}),e.jsx("td",{className:"num",children:B(l.compressedOriginalBytes)}),e.jsx("td",{className:"num",children:I(l.lastCompactedAt)})]},l.stateDir))]})]})]})}function u(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function D(s){return s>=1048576?`${(s/1048576).toFixed(1)} MiB`:s>=1024?`${(s/1024).toFixed(1)} KiB`:`${s} B`}function C(s){if(s==null)return"never";try{return new Date(s).toLocaleString()}catch{return String(s)}}function E({repo:s,onClose:a}){return o.useEffect(()=>{const t=c=>{c.key==="Escape"&&a()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[a]),e.jsx("div",{className:"modal-backdrop",onClick:a,role:"dialog","aria-modal":"true",children:e.jsxs("div",{className:"modal-card",onClick:t=>t.stopPropagation(),children:[e.jsxs("header",{className:"modal-header",children:[e.jsx("h2",{children:s.displayName}),e.jsx("button",{type:"button",className:"modal-close",onClick:a,"aria-label":"Close",children:"×"})]}),e.jsx("p",{className:"modal-path",children:s.repoRoot}),e.jsxs("div",{className:"modal-grid",children:[e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Checkpoints"}),e.jsx("span",{className:"modal-value",children:s.checkpointCount.toLocaleString()})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Sessions"}),e.jsx("span",{className:"modal-value",children:s.sessions.toLocaleString()})]}),e.jsxs("div",{className:"modal-stat modal-highlight",children:[e.jsx("span",{className:"modal-label",children:"Tokens saved"}),e.jsx("span",{className:"modal-value",children:u(s.tokensSaved)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Tokens kept"}),e.jsx("span",{className:"modal-value",children:u(s.tokensKept)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Tokens dropped"}),e.jsx("span",{className:"modal-value",children:u(s.tokensDropped)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Dedup rate"}),e.jsx("span",{className:"modal-value",children:"—"})]})]}),e.jsx("h3",{children:"Model"}),e.jsxs("dl",{className:"modal-dl",children:[e.jsx("dt",{children:"Provider"}),e.jsx("dd",{children:s.providerName??"—"}),e.jsx("dt",{children:"Model"}),e.jsx("dd",{children:s.modelName??"—"}),e.jsx("dt",{children:"Input rate"}),e.jsx("dd",{children:s.inputRate!==null?`${s.inputRate} tok/s`:"—"}),e.jsx("dt",{children:"Output rate"}),e.jsx("dd",{children:s.outputRate!==null?`${s.outputRate} tok/s`:"—"}),e.jsx("dt",{children:"Context window"}),e.jsx("dd",{children:s.contextWindow!==null?u(s.contextWindow):"—"}),e.jsx("dt",{children:"Max tokens"}),e.jsx("dd",{children:s.maxTokens!==null?u(s.maxTokens):"—"}),e.jsx("dt",{children:"Reasoning"}),e.jsx("dd",{children:s.reasoning===null?"—":s.reasoning?"on":"off"})]}),e.jsx("h3",{children:"Activity"}),e.jsxs("dl",{className:"modal-dl",children:[e.jsx("dt",{children:"Last compacted"}),e.jsx("dd",{children:C(s.lastCompactedAt)}),e.jsx("dt",{children:"Last seen"}),e.jsx("dd",{children:C(s.lastSeen)}),e.jsx("dt",{children:"Compressed-Original"}),e.jsx("dd",{children:D(s.compressedOriginalBytes)})]})]})})}function j(s){const a=s??0;return a>=3600?`${(a/3600).toFixed(1)}h`:a>=60?`${Math.round(a/60)}m`:a>=1?`${a.toFixed(1)}s`:`${Math.round(a*1e3)}ms`}const g=["Repo","Model","Tier","Context %","State","Compactions (s/t)","Cache Hits (s/t)","Cache Hit %","Est. Saved","Compact s/t (s)","CacheHit s/t (s)"];function H({servers:s}){return e.jsxs("div",{className:"active-repos-section",children:[e.jsx("p",{className:"legend-note",children:"Repos seen within the last 30 minutes, with their per-repo cache-hit, compaction, and time-saved (est.) totals pulled live from each repo's dashboard.json."}),e.jsx("div",{className:"table-scroll",children:e.jsxs("table",{className:"active-repos-table",children:[e.jsx("thead",{children:e.jsx("tr",{children:g.map((a,t)=>e.jsx("th",{className:t>=3&&t!==4?"num":void 0,children:a},a))})}),e.jsxs("tbody",{children:[s.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:g.length,className:"repo-empty",children:"No active repositories."})}),s.map(a=>{const t=a.cacheHits??{session:0,total:0},c=a.compacts??{session:0,total:0},n=a.timeSaved??{compact:{sessionSec:0,totalSec:0},cacheHit:{sessionSec:0,totalSec:0}};return e.jsxs("tr",{children:[e.jsx("td",{title:a.repoRoot,children:a.displayName||a.repoRoot}),e.jsx("td",{children:a.model??"—"}),e.jsx("td",{children:a.tier??"—"}),e.jsx("td",{className:"num",children:a.contextPct!=null?`${Math.round(a.contextPct*100)}%`:"—"}),e.jsx("td",{children:a.state??"—"}),e.jsxs("td",{className:"num",children:[c.session," / ",c.total]}),e.jsxs("td",{className:"num",children:[t.session," / ",t.total]}),e.jsx("td",{className:"num",children:a.providerCache!=null?`${a.providerCache.avgHitPct.toFixed(1)}%`:"—"}),e.jsx("td",{className:"num",children:a.providerCache?.estimatedSaved!=null?`$${a.providerCache.estimatedSaved.toFixed(2)}`:"—"}),e.jsxs("td",{className:"num",children:[j(n.compact.sessionSec)," /"," ",j(n.compact.totalSec)]}),e.jsxs("td",{className:"num",children:[j(n.cacheHit.sessionSec)," /"," ",j(n.cacheHit.totalSec)]})]},a.repoRoot)})]})]})})]})}function b(s){if(!s.length)return"—";const a=Math.min(...s),t=Math.max(...s);return a===t?a.toLocaleString():`${a.toLocaleString()}–${t.toLocaleString()}`}function $(s){if(!s.length)return"—";const a=Math.min(...s),t=Math.max(...s),c=n=>`$${n.toFixed(6)}`;return a===t?c(a):`${c(a)}–${c(t)}`}function K(s){const a={};for(const t of s){const c=t.modelName&&t.modelName.trim()||"(unknown)";a[c]||(a[c]={model:c,provider:t.providerName??t.provider??"—",checkpoints:0,tokensSaved:0,tokensIn:0,tokensOut:0,sessions:0,usd:0,inRates:[],outRates:[],ctxWindows:[],maxTokens:[],reasoning:null,lastAt:0,cacheRead:0,cacheWrite:0});const n=a[c];n.checkpoints+=t.checkpointCount||0,n.tokensSaved+=t.tokensSaved||0,n.tokensIn+=t.tokensDropped||0,n.tokensOut+=t.tokensKept||0,n.sessions+=t.sessions||0,t.inputRate&&(n.usd+=(t.tokensSaved||0)*t.inputRate,n.inRates.push(t.inputRate)),t.outputRate&&n.outRates.push(t.outputRate),t.contextWindow&&n.ctxWindows.push(t.contextWindow),t.maxTokens&&n.maxTokens.push(t.maxTokens),t.reasoning!=null&&(n.reasoning=t.reasoning),t.providerCacheRead&&(n.cacheRead+=t.providerCacheRead),t.providerCacheWrite&&(n.cacheWrite+=t.providerCacheWrite),t.lastCompactedAt&&t.lastCompactedAt>n.lastAt&&(n.lastAt=t.lastCompactedAt)}return Object.values(a).sort((t,c)=>c.tokensSaved-t.tokensSaved)}const r={tokensIn:"Tokens dropped from context by compaction (the input reclaimed)",tokensOut:"Tokens kept as compacted summaries still in context (the output retained)",ctxWindow:"Model context window (max input tokens the model accepts)",maxOut:"Model max output tokens per turn",reas:"Reasoning-capable model",sessions:"Distinct sessions with at least one checkpoint",inRate:"USD per input token",outRate:"USD per output token"};function P({repos:s}){const a=o.useMemo(()=>K(s),[s]);return e.jsxs("div",{className:"savings-by-model",children:[e.jsx("p",{className:"legend-note",children:"How much context & cost mega-compact has reclaimed, grouped by the model you were running. Compression ratio reflects workload/content, not model quality."}),e.jsx("div",{className:"table-scroll",children:e.jsxs("table",{className:"savings-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Model"}),e.jsx("th",{children:"Provider"}),e.jsx("th",{className:"num",title:r.tokensIn,children:"Tokens In"}),e.jsx("th",{className:"num",title:r.tokensOut,children:"Tokens Out"}),e.jsx("th",{className:"num",children:"Freed"}),e.jsx("th",{className:"num",title:r.ctxWindow,children:"Ctx Window"}),e.jsx("th",{className:"num",title:r.maxOut,children:"Max Out"}),e.jsx("th",{className:"num",title:r.reas,children:"Reas."}),e.jsx("th",{className:"num",title:r.sessions,children:"Sessions"}),e.jsx("th",{className:"num",children:"Checkpoints"}),e.jsx("th",{className:"num",title:r.inRate,children:"In $/tok"}),e.jsx("th",{className:"num",title:r.outRate,children:"Out $/tok"}),e.jsx("th",{className:"num",children:"$ Saved"}),e.jsx("th",{className:"num",title:"Provider prompt-cache hit rate (reads / reads+writes+tokens_in)",children:"Cache Hit %"}),e.jsx("th",{className:"num",title:"Estimated provider prompt-cache savings (read tokens * 0.9 * inputRate − write tokens * 0.25 * inputRate)",children:"Cache $ Saved"}),e.jsx("th",{className:"num",children:"Last Used"})]})}),e.jsxs("tbody",{children:[a.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:16,className:"repo-empty",children:"No repositories registered yet."})}),a.map(t=>{const c=(t.tokensIn||0)-(t.tokensOut||0),n=t.usd>0?`$${t.usd.toFixed(4)}`:"—",d=t.lastAt?new Date(t.lastAt).toLocaleString():"—",i=t.reasoning==null?"—":t.reasoning?"yes":"no";return e.jsxs("tr",{children:[e.jsx("td",{className:"repo-model",children:t.model}),e.jsx("td",{children:t.provider}),e.jsx("td",{className:"num",children:t.tokensIn.toLocaleString()}),e.jsx("td",{className:"num",children:t.tokensOut.toLocaleString()}),e.jsx("td",{className:"num",children:c.toLocaleString()}),e.jsx("td",{className:"num",children:b(t.ctxWindows)}),e.jsx("td",{className:"num",children:b(t.maxTokens)}),e.jsx("td",{className:"num",children:i}),e.jsx("td",{className:"num",children:t.sessions.toLocaleString()}),e.jsx("td",{className:"num",children:t.checkpoints.toLocaleString()}),e.jsx("td",{className:"num",children:$(t.inRates)}),e.jsx("td",{className:"num",children:$(t.outRates)}),e.jsx("td",{className:"num",children:n}),e.jsx("td",{className:"num",children:t.cacheRead>0?`${(t.cacheRead*100/(t.cacheRead+t.cacheWrite+t.tokensIn||1)).toFixed(1)}%`:"—"}),e.jsx("td",{className:"num",children:t.cacheRead>0?`$${(t.cacheRead*.9*(t.inRates[0]||0)-t.cacheWrite*.25*(t.inRates[0]||0)).toFixed(4)}`:"—"}),e.jsx("td",{className:"num",children:d})]},t.model)})]})]})}),e.jsx("p",{className:"legend-note",children:"Tokens In = Σ original region tokens dropped by compaction. Tokens Out = Σ compacted summary tokens still retained in context. Freed = Tokens In − Tokens Out (net context reclaimed). Ctx Window / Max Out / Reas. come from the latest captured model snapshot for each repo."})]})}async function U(){return await M()}function _(){const[s,a]=o.useState(null),{data:t,error:c}=y(o.useCallback(()=>U(),[]),{pollInterval:1e4}),{data:n}=y(o.useCallback(()=>L(),[]),{pollInterval:1e4});if(c&&!t)return e.jsxs("div",{className:"tab-stub",children:["Error loading repos: ",c.message]});if(!t)return e.jsx("div",{className:"tab-stub",children:"Loading repos…"});const d=t.summary,i=t.repos,x=n?.servers??[];return e.jsxs("div",{className:"repos-tab",children:[e.jsx(A,{totalRepos:d?.totalRepos??0,totalCheckpoints:d?.totalCheckpoints??0,totalTokensSaved:d?.totalTokensSaved??0,compressedOriginalBytes:d?.totalCompressedOriginalBytes??0}),e.jsx("h2",{className:"section-header",children:"All Repositories"}),e.jsx(W,{repos:i,onSelect:a}),e.jsx("h2",{className:"section-header",children:"Active Repos — Live Cache Hits & Compactions"}),e.jsx(H,{servers:x}),e.jsx("h2",{className:"section-header",children:"Savings by Model"}),e.jsx(P,{repos:i}),s&&e.jsx(E,{repo:s,onClose:()=>a(null)})]})}export{_ as default};
|
|
2
|
-
//# sourceMappingURL=ReposTab-C0Wnrf2u.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReposTab-C0Wnrf2u.js","sources":["../../src/components/SummaryTiles.tsx","../../src/components/RepoTable.tsx","../../src/components/RepoDetailModal.tsx","../../src/components/ActiveReposTable.tsx","../../src/components/SavingsByModelTable.tsx","../../src/tabs/ReposTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/SummaryTiles.tsx — aggregate header tiles.\n *\n * 4 tiles: Repositories, Total Checkpoints, Total Tokens Saved,\n * Compressed-Original. Matches old dashboard #panel-summary tiles.\n */\n\nimport type React from \"react\";\n\nexport interface SummaryTilesProps {\n\t/** Total repos in the registry. */\n\ttotalRepos: number;\n\t/** Total checkpoints across all repos. */\n\ttotalCheckpoints: number;\n\t/** Total tokens saved across all repos. */\n\ttotalTokensSaved: number;\n\t/** Total compressed-original bytes across all repos. */\n\tcompressedOriginalBytes: number;\n}\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\n/** Format large numbers with K/M suffix. */\nfunction fmt(n: number): string {\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n\treturn String(n);\n}\n\nexport function SummaryTiles({\n\ttotalRepos,\n\ttotalCheckpoints,\n\ttotalTokensSaved,\n\tcompressedOriginalBytes,\n}: SummaryTilesProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"summary-tiles\">\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Repositories</span>\n\t\t\t\t<span className=\"tile-value\">{totalRepos.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Total Checkpoints</span>\n\t\t\t\t<span className=\"tile-value\">{totalCheckpoints.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile tile-highlight\">\n\t\t\t\t<span className=\"tile-label\">Total Tokens Saved</span>\n\t\t\t\t<span className=\"tile-value\">{fmt(totalTokensSaved)}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Compressed-Original</span>\n\t\t\t\t<span className=\"tile-value\">{fmtBytesTop(compressedOriginalBytes)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoTable.tsx — sortable, searchable repo table.\n *\n * 6 columns: Repo, Model, Checkpoints, Tokens Saved, Retained, Last Compacted.\n * Client-side sort (click header to toggle asc/desc). Search by displayName\n * (case-insensitive substring). Default sort: Last Compacted DESC.\n * Row click opens RepoDetailModal.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoTableProps {\n\trepos: IndexesIndexRow[];\n\t/** Called when a row is clicked — opens the detail modal. */\n\tonSelect: (repo: IndexesIndexRow) => void;\n}\n\ntype SortKey =\n\t| \"displayName\"\n\t| \"modelName\"\n\t| \"checkpointCount\"\n\t| \"tokensSaved\"\n\t| \"compressedOriginalBytes\"\n\t| \"lastCompactedAt\";\n\ntype SortDir = \"asc\" | \"desc\";\n\nconst COLUMNS: Array<{ key: SortKey; label: string }> = [\n\t{ key: \"displayName\", label: \"Repo\" },\n\t{ key: \"modelName\", label: \"Model\" },\n\t{ key: \"checkpointCount\", label: \"Checkpoints\" },\n\t{ key: \"tokensSaved\", label: \"Tokens Saved\" },\n\t{ key: \"compressedOriginalBytes\", label: \"Retained\" },\n\t{ key: \"lastCompactedAt\", label: \"Last Compacted\" },\n];\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"\\u2014\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoTable({\n\trepos,\n\tonSelect,\n}: RepoTableProps): React.ReactElement {\n\tconst [search, setSearch] = useState(\"\");\n\tconst [sortKey, setSortKey] = useState<SortKey>(\"lastCompactedAt\");\n\tconst [sortDir, setSortDir] = useState<SortDir>(\"desc\");\n\n\tconst filtered = useMemo(() => {\n\t\tconst q = search.trim().toLowerCase();\n\t\tconst list = q\n\t\t\t? repos.filter((r) => r.displayName.toLowerCase().includes(q))\n\t\t\t: repos.slice();\n\t\tlist.sort((a, b) => {\n\t\t\tconst av = a[sortKey];\n\t\t\tconst bv = b[sortKey];\n\t\t\t// nulls last on desc, first on asc (consistent UX)\n\t\t\tconst aNull = av === null || av === undefined;\n\t\t\tconst bNull = bv === null || bv === undefined;\n\t\t\tif (aNull && bNull) return 0;\n\t\t\tif (aNull) return sortDir === \"desc\" ? 1 : -1;\n\t\t\tif (bNull) return sortDir === \"desc\" ? -1 : 1;\n\t\t\tconst cmp =\n\t\t\t\ttypeof av === \"number\" && typeof bv === \"number\"\n\t\t\t\t\t? av - bv\n\t\t\t\t\t: String(av).localeCompare(String(bv));\n\t\t\treturn sortDir === \"desc\" ? -cmp : cmp;\n\t\t});\n\t\treturn list;\n\t}, [repos, search, sortKey, sortDir]);\n\n\tconst handleSort = (key: SortKey): void => {\n\t\tif (sortKey === key) {\n\t\t\tsetSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\n\t\t} else {\n\t\t\tsetSortKey(key);\n\t\t\tsetSortDir(\n\t\t\t\tkey === \"lastCompactedAt\" ||\n\t\t\t\tkey === \"tokensSaved\" ||\n\t\t\t\tkey === \"compressedOriginalBytes\"\n\t\t\t\t\t? \"desc\"\n\t\t\t\t\t: \"asc\",\n\t\t\t);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"repo-table-wrap\">\n\t\t\t<input\n\t\t\t\ttype=\"search\"\n\t\t\t\tclassName=\"repo-search\"\n\t\t\t\tplaceholder=\"Search repos…\"\n\t\t\t\tvalue={search}\n\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\taria-label=\"Search repos\"\n\t\t\t/>\n\t\t\t<table className=\"repo-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t{COLUMNS.map((col) => (\n\t\t\t\t\t\t\t<th key={col.key}>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName={`sort-header ${sortKey === col.key ? `active-${sortDir}` : \"\"}`}\n\t\t\t\t\t\t\t\t\tonClick={() => handleSort(col.key)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{col.label}\n\t\t\t\t\t\t\t\t\t{sortKey === col.key && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"sort-arrow\">\n\t\t\t\t\t\t\t\t\t\t\t{sortDir === \"asc\" ? \" ▲\" : \" ▼\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td colSpan={COLUMNS.length} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t{repos.length === 0\n\t\t\t\t\t\t\t\t\t? \"No repositories registered yet.\"\n\t\t\t\t\t\t\t\t\t: \"No repos match your search.\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{filtered.map((repo) => (\n\t\t\t\t\t\t<tr key={repo.stateDir} onClick={() => onSelect(repo)}>\n\t\t\t\t\t\t\t<td className=\"repo-name\" title={repo.repoRoot}>\n\t\t\t\t\t\t\t\t{repo.displayName}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"repo-model\">{repo.modelName ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.checkpointCount.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.tokensSaved.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytesTop(repo.compressedOriginalBytes)}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{formatTs(repo.lastCompactedAt)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoDetailModal.tsx — repo drill-down.\n *\n * Full detail from an /api/index row: token breakdown (kept/dropped), model\n * info, context window, dedup rate, reasoning flag. Click backdrop to close.\n */\n\nimport type React from \"react\";\nimport { useEffect } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoDetailModalProps {\n\trepo: IndexesIndexRow;\n\tonClose: () => void;\n}\n\nfunction fmt(n: number): string {\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n\treturn String(n);\n}\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"never\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoDetailModal({\n\trepo,\n\tonClose,\n}: RepoDetailModalProps): React.ReactElement {\n\t// Close on Escape key.\n\tuseEffect(() => {\n\t\tconst handler = (e: KeyboardEvent): void => {\n\t\t\tif (e.key === \"Escape\") onClose();\n\t\t};\n\t\twindow.addEventListener(\"keydown\", handler);\n\t\treturn () => window.removeEventListener(\"keydown\", handler);\n\t}, [onClose]);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"modal-backdrop\"\n\t\t\tonClick={onClose}\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t>\n\t\t\t<div className=\"modal-card\" onClick={(e) => e.stopPropagation()}>\n\t\t\t\t<header className=\"modal-header\">\n\t\t\t\t\t<h2>{repo.displayName}</h2>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"modal-close\"\n\t\t\t\t\t\tonClick={onClose}\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t>\n\t\t\t\t\t\t×\n\t\t\t\t\t</button>\n\t\t\t\t</header>\n\t\t\t\t<p className=\"modal-path\">{repo.repoRoot}</p>\n\t\t\t\t<div className=\"modal-grid\">\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Checkpoints</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.checkpointCount.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Sessions</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.sessions.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat modal-highlight\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens saved</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensSaved)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens kept</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensKept)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens dropped</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensDropped)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Dedup rate</span>\n\t\t\t\t\t\t<span className=\"modal-value\">—</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<h3>Model</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Provider</dt>\n\t\t\t\t\t<dd>{repo.providerName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Model</dt>\n\t\t\t\t\t<dd>{repo.modelName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Input rate</dt>\n\t\t\t\t\t<dd>{repo.inputRate !== null ? `${repo.inputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Output rate</dt>\n\t\t\t\t\t<dd>{repo.outputRate !== null ? `${repo.outputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Context window</dt>\n\t\t\t\t\t<dd>{repo.contextWindow !== null ? fmt(repo.contextWindow) : \"—\"}</dd>\n\t\t\t\t\t<dt>Max tokens</dt>\n\t\t\t\t\t<dd>{repo.maxTokens !== null ? fmt(repo.maxTokens) : \"—\"}</dd>\n\t\t\t\t\t<dt>Reasoning</dt>\n\t\t\t\t\t<dd>\n\t\t\t\t\t\t{repo.reasoning === null ? \"—\" : repo.reasoning ? \"on\" : \"off\"}\n\t\t\t\t\t</dd>\n\t\t\t\t</dl>\n\t\t\t\t<h3>Activity</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Last compacted</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastCompactedAt)}</dd>\n\t\t\t\t\t<dt>Last seen</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastSeen)}</dd>\n\t\t\t\t\t<dt>Compressed-Original</dt>\n\t\t\t\t\t<dd>{fmtBytesTop(repo.compressedOriginalBytes)}</dd>\n\t\t\t\t</dl>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ActiveReposTable.tsx — Active repos table.\n *\n * 9 columns: Repo, Model, Tier, Context %, State, Compactions s/t,\n * Cache Hits s/t, Compact s/t, CacheHit s/t.\n * Data from fetchServers() via useApi with 10s polling.\n */\n\nimport type React from \"react\";\nimport type { ServerEntry } from \"@contracts\";\n\nexport interface ActiveReposTableProps {\n\tservers: ServerEntry[];\n}\n\n/** Format seconds → human-readable duration (matches html.ts fmtSec). */\nfunction fmtSec(s: number | undefined): string {\n\tconst v = s ?? 0;\n\tif (v >= 3600) return `${(v / 3600).toFixed(1)}h`;\n\tif (v >= 60) return `${Math.round(v / 60)}m`;\n\tif (v >= 1) return `${v.toFixed(1)}s`;\n\treturn `${Math.round(v * 1000)}ms`;\n}\n\nconst HEADERS = [\n\t\"Repo\",\n\t\"Model\",\n\t\"Tier\",\n\t\"Context %\",\n\t\"State\",\n\t\"Compactions (s/t)\",\n\t\"Cache Hits (s/t)\",\n\t\"Cache Hit %\",\n\t\"Est. Saved\",\n\t\"Compact s/t (s)\",\n\t\"CacheHit s/t (s)\",\n] as const;\n\nexport function ActiveReposTable({\n\tservers,\n}: ActiveReposTableProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"active-repos-section\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tRepos seen within the last 30 minutes, with their per-repo\n\t\t\t\tcache-hit, compaction, and time-saved (est.) totals pulled live\n\t\t\t\tfrom each repo's dashboard.json.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"active-repos-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t{HEADERS.map((h, i) => (\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tkey={h}\n\t\t\t\t\t\t\t\t\tclassName={i >= 3 && i !== 4 ? \"num\" : undefined}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{h}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{servers.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={HEADERS.length} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo active repositories.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{servers.map((r) => {\n\t\t\t\t\t\t\tconst ch = r.cacheHits ?? {\n\t\t\t\t\t\t\t\tsession: 0,\n\t\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t\t\tsessionTokensSaved: 0,\n\t\t\t\t\t\t\t\ttotalTokensSaved: 0,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tconst cp = r.compacts ?? { session: 0, total: 0 };\n\t\t\t\t\t\t\tconst ts = r.timeSaved ?? {\n\t\t\t\t\t\t\t\tcompact: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t\tcacheHit: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t<td title={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t\t{r.displayName || r.repoRoot}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.model ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td>{r.tier ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{r.contextPct != null\n\t\t\t\t\t\t\t\t\t\t\t? `${Math.round(r.contextPct * 100)}%`\n\t\t\t\t\t\t\t\t\t\t\t: \"\\u2014\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.state ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{cp.session} / {cp.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{ch.session} / {ch.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{r.providerCache != null\n\t\t\t\t\t\t\t\t\t\t\t? `${r.providerCache.avgHitPct.toFixed(1)}%`\n\t\t\t\t\t\t\t\t\t\t\t: \"—\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{r.providerCache?.estimatedSaved != null\n\t\t\t\t\t\t\t\t\t\t\t? `$${r.providerCache.estimatedSaved.toFixed(2)}`\n\t\t\t\t\t\t\t\t\t\t\t: \"—\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/SavingsByModelTable.tsx — Savings by Model.\n *\n * 14 columns aggregated client-side from /api/index repos by model.\n * Replicates html.ts renderByModel logic: group by modelName, accumulate\n * tokens/cost/sessions, collapse numeric ranges when repos disagree.\n */\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface SavingsByModelTableProps {\n\trepos: IndexesIndexRow[];\n}\n\ninterface ModelGroup {\n\tmodel: string;\n\tprovider: string;\n\tcheckpoints: number;\n\ttokensSaved: number;\n\ttokensIn: number;\n\ttokensOut: number;\n\tsessions: number;\n\tusd: number;\n\tinRates: number[];\n\toutRates: number[];\n\tctxWindows: number[];\n\tmaxTokens: number[];\n\treasoning: boolean | null;\n\tlastAt: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n}\n\n/** Collapse numeric samples: \"—\" | single | \"lo–hi\" (matches html.ts). */\nfunction collapseNum(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\treturn lo === hi\n\t\t? lo.toLocaleString()\n\t\t: `${lo.toLocaleString()}\\u2013${hi.toLocaleString()}`;\n}\n\n/** Collapse rate samples: \"—\" | \"$rate\" | \"$lo–$hi\" (matches html.ts). */\nfunction collapseRate(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\tconst fmt = (v: number): string => `$${v.toFixed(6)}`;\n\treturn lo === hi ? fmt(lo) : `${fmt(lo)}\\u2013${fmt(hi)}`;\n}\n\n/** Group repos by model, accumulate totals, sort by tokensSaved desc. */\nfunction aggregate(repos: IndexesIndexRow[]): ModelGroup[] {\n\tconst map: Record<string, ModelGroup> = {};\n\tfor (const r of repos) {\n\t\tconst key = (r.modelName && r.modelName.trim()) || \"(unknown)\";\n\t\tif (!map[key]) {\n\t\t\tmap[key] = {\n\t\t\t\tmodel: key,\n\t\t\t\tprovider: r.providerName ?? r.provider ?? \"\\u2014\",\n\t\t\t\tcheckpoints: 0,\n\t\t\t\ttokensSaved: 0,\n\t\t\t\ttokensIn: 0,\n\t\t\t\ttokensOut: 0,\n\t\t\t\tsessions: 0,\n\t\t\t\tusd: 0,\n\t\t\t\tinRates: [],\n\t\t\t\toutRates: [],\n\t\t\t\tctxWindows: [],\n\t\t\t\tmaxTokens: [],\n\t\t\t\treasoning: null,\n\t\t\t\tlastAt: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t};\n\t\t}\n\t\tconst g = map[key];\n\t\tg.checkpoints += r.checkpointCount || 0;\n\t\tg.tokensSaved += r.tokensSaved || 0;\n\t\tg.tokensIn += r.tokensDropped || 0;\n\t\tg.tokensOut += r.tokensKept || 0;\n\t\tg.sessions += r.sessions || 0;\n\t\tif (r.inputRate) {\n\t\t\tg.usd += (r.tokensSaved || 0) * r.inputRate;\n\t\t\tg.inRates.push(r.inputRate);\n\t\t}\n\t\tif (r.outputRate) g.outRates.push(r.outputRate);\n\t\tif (r.contextWindow) g.ctxWindows.push(r.contextWindow);\n\t\tif (r.maxTokens) g.maxTokens.push(r.maxTokens);\n\t\tif (r.reasoning != null) g.reasoning = r.reasoning;\n\t\tif (r.providerCacheRead) g.cacheRead += r.providerCacheRead;\n\t\tif (r.providerCacheWrite) g.cacheWrite += r.providerCacheWrite;\n\t\tif (r.lastCompactedAt && r.lastCompactedAt > g.lastAt)\n\t\t\tg.lastAt = r.lastCompactedAt;\n\t}\n\treturn Object.values(map).sort((a, b) => b.tokensSaved - a.tokensSaved);\n}\n\n/** Tooltip text copied verbatim from html.ts title attributes. */\nconst TOOLTIPS = {\n\ttokensIn: \"Tokens dropped from context by compaction (the input reclaimed)\",\n\ttokensOut:\n\t\t\"Tokens kept as compacted summaries still in context (the output retained)\",\n\tctxWindow: \"Model context window (max input tokens the model accepts)\",\n\tmaxOut: \"Model max output tokens per turn\",\n\treas: \"Reasoning-capable model\",\n\tsessions: \"Distinct sessions with at least one checkpoint\",\n\tinRate: \"USD per input token\",\n\toutRate: \"USD per output token\",\n} as const;\n\nexport function SavingsByModelTable({\n\trepos,\n}: SavingsByModelTableProps): React.ReactElement {\n\tconst groups = useMemo(() => aggregate(repos), [repos]);\n\n\treturn (\n\t\t<div className=\"savings-by-model\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tHow much context & cost mega-compact has reclaimed, grouped by\n\t\t\t\tthe model you were running. Compression ratio reflects\n\t\t\t\tworkload/content, not model quality.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"savings-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Model</th>\n\t\t\t\t\t\t\t<th>Provider</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensIn}>\n\t\t\t\t\t\t\t\tTokens In\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensOut}>\n\t\t\t\t\t\t\t\tTokens Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Freed</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.ctxWindow}>\n\t\t\t\t\t\t\t\tCtx Window\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.maxOut}>\n\t\t\t\t\t\t\t\tMax Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.reas}>\n\t\t\t\t\t\t\t\tReas.\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.sessions}>\n\t\t\t\t\t\t\t\tSessions\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Checkpoints</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.inRate}>\n\t\t\t\t\t\t\t\tIn $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.outRate}>\n\t\t\t\t\t\t\t\tOut $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">$ Saved</th>\n\t\t\t\t\t\t\t<th className=\"num\" title=\"Provider prompt-cache hit rate (reads / reads+writes+tokens_in)\">\n\t\t\t\t\t\t\t\tCache Hit %\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title=\"Estimated provider prompt-cache savings (read tokens * 0.9 * inputRate − write tokens * 0.25 * inputRate)\">\n\t\t\t\t\t\t\t\tCache $ Saved\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Last Used</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{groups.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={16} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo repositories registered yet.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{groups.map((g) => {\n\t\t\t\t\t\t\tconst freed = (g.tokensIn || 0) - (g.tokensOut || 0);\n\t\t\t\t\t\t\tconst usd =\n\t\t\t\t\t\t\t\tg.usd > 0 ? `$${g.usd.toFixed(4)}` : \"\\u2014\";\n\t\t\t\t\t\t\tconst when = g.lastAt\n\t\t\t\t\t\t\t\t? new Date(g.lastAt).toLocaleString()\n\t\t\t\t\t\t\t\t: \"\\u2014\";\n\t\t\t\t\t\t\tconst reas =\n\t\t\t\t\t\t\t\tg.reasoning == null\n\t\t\t\t\t\t\t\t\t? \"\\u2014\"\n\t\t\t\t\t\t\t\t\t: g.reasoning\n\t\t\t\t\t\t\t\t\t\t? \"yes\"\n\t\t\t\t\t\t\t\t\t\t: \"no\";\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={g.model}>\n\t\t\t\t\t\t\t\t\t<td className=\"repo-model\">{g.model}</td>\n\t\t\t\t\t\t\t\t\t<td>{g.provider}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensIn.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensOut.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{freed.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.ctxWindows)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.maxTokens)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{reas}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.sessions.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.checkpoints.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.inRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.outRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{usd}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t{g.cacheRead > 0\n\t\t\t\t\t\t\t\t\t\t? `${(\n\t\t\t\t\t\t\t\t\t\t\t\t(g.cacheRead * 100) /\n\t\t\t\t\t\t\t\t\t\t\t\t(g.cacheRead + g.cacheWrite + g.tokensIn || 1)\n\t\t\t\t\t\t\t\t\t\t\t).toFixed(1)}%`\n\t\t\t\t\t\t\t\t\t\t: \"—\"}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t{g.cacheRead > 0\n\t\t\t\t\t\t\t\t\t\t? `$${(\n\t\t\t\t\t\t\t\t\t\t\t\tg.cacheRead * 0.9 * (g.inRates[0] || 0) -\n\t\t\t\t\t\t\t\t\t\t\t\tg.cacheWrite * 0.25 * (g.inRates[0] || 0)\n\t\t\t\t\t\t\t\t\t\t\t).toFixed(4)}`\n\t\t\t\t\t\t\t\t\t\t: \"—\"}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{when}</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tTokens In = Σ original region tokens dropped by compaction. Tokens\n\t\t\t\tOut = Σ compacted summary tokens still retained in context. Freed =\n\t\t\t\tTokens In − Tokens Out (net context reclaimed). Ctx Window / Max Out\n\t\t\t\t/ Reas. come from the latest captured model snapshot for each repo.\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/ReposTab.tsx — Repos tab (C2 fleshed out).\n *\n * Renders: SummaryTiles (4 tiles), All Repositories table (RepoTable),\n * Active Repos table (ActiveReposTable), Savings by Model table\n * (SavingsByModelTable), and per-repo detail modal (RepoDetailModal).\n *\n * Data sources: fetchIndex() for summary + all repos + savings-by-model;\n * fetchServers() for active repos. Both polled every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchIndex, fetchServers } from \"../api/client\";\nimport type { IndexesIndexRow, ServersResponse } from \"@contracts\";\nimport { SummaryTiles } from \"../components/SummaryTiles\";\nimport { RepoTable } from \"../components/RepoTable\";\nimport { RepoDetailModal } from \"../components/RepoDetailModal\";\nimport { ActiveReposTable } from \"../components/ActiveReposTable\";\nimport { SavingsByModelTable } from \"../components/SavingsByModelTable\";\n\n/**\n * Actual runtime shape of /api/index. The contract's IndexesSummaryResponse\n * puts summary fields at the top level, but the real server nests them under\n * `summary` (matching IndexFallbackResponse shape with non-null summary).\n */\ninterface DashboardIndexSummary {\n\ttotalRepos: number;\n\ttotalCheckpoints: number;\n\ttotalTokensSaved: number;\n\ttotalCompressedOriginalBytes: number;\n}\n\ninterface DashboardIndexResponse {\n\tupdatedAt: string | null;\n\tsummary: DashboardIndexSummary | null;\n\trepos: IndexesIndexRow[];\n}\n\n/** Fetch /api/index and cast to the actual runtime shape. */\nasync function fetchIndexTyped(): Promise<DashboardIndexResponse> {\n\treturn (await fetchIndex()) as unknown as DashboardIndexResponse;\n}\n\nexport default function ReposTab(): React.ReactElement {\n\tconst [selected, setSelected] = useState<IndexesIndexRow | null>(null);\n\n\tconst { data: indexData, error: indexErr } =\n\t\tuseApi<DashboardIndexResponse>(\n\t\t\tuseCallback(() => fetchIndexTyped(), []),\n\t\t\t{ pollInterval: 10_000 },\n\t\t);\n\n\tconst { data: serversData } = useApi<ServersResponse>(\n\t\tuseCallback(() => fetchServers(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (indexErr && !indexData) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading repos: {indexErr.message}</div>\n\t\t);\n\t}\n\tif (!indexData) {\n\t\treturn <div className=\"tab-stub\">Loading repos…</div>;\n\t}\n\n\tconst summary = indexData.summary;\n\tconst repos = indexData.repos;\n\tconst servers = serversData?.servers ?? [];\n\n\treturn (\n\t\t<div className=\"repos-tab\">\n\t\t\t<SummaryTiles\n\t\t\t\ttotalRepos={summary?.totalRepos ?? 0}\n\t\t\t\ttotalCheckpoints={summary?.totalCheckpoints ?? 0}\n\t\t\t\ttotalTokensSaved={summary?.totalTokensSaved ?? 0}\n\t\t\t\tcompressedOriginalBytes={summary?.totalCompressedOriginalBytes ?? 0}\n\t\t\t/>\n\t\t\t<h2 className=\"section-header\">All Repositories</h2>\n\t\t\t<RepoTable repos={repos} onSelect={setSelected} />\n\t\t\t<h2 className=\"section-header\">\n\t\t\t\tActive Repos — Live Cache Hits & Compactions\n\t\t\t</h2>\n\t\t\t<ActiveReposTable servers={servers} />\n\t\t\t<h2 className=\"section-header\">Savings by Model</h2>\n\t\t\t<SavingsByModelTable repos={repos} />\n\t\t\t{selected && (\n\t\t\t\t<RepoDetailModal repo={selected} onClose={() => setSelected(null)} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["fmtBytesTop","b","fmt","n","SummaryTiles","totalRepos","totalCheckpoints","totalTokensSaved","compressedOriginalBytes","jsxs","jsx","COLUMNS","formatTs","ts","RepoTable","repos","onSelect","search","setSearch","useState","sortKey","setSortKey","sortDir","setSortDir","filtered","useMemo","q","list","r","a","av","bv","aNull","bNull","cmp","handleSort","key","d","e","col","repo","RepoDetailModal","onClose","useEffect","handler","fmtSec","v","HEADERS","ActiveReposTable","servers","h","i","ch","cp","collapseNum","samples","lo","hi","collapseRate","aggregate","map","g","TOOLTIPS","SavingsByModelTable","groups","freed","usd","when","reas","fetchIndexTyped","fetchIndex","ReposTab","selected","setSelected","indexData","indexErr","useApi","useCallback","serversData","fetchServers","summary"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as a}from"./index-BX0bcgLu.js";function f(t){const e=t??0;return e>=1048576?`${(e/1048576).toFixed(1)} MiB`:e>=1024?`${(e/1024).toFixed(1)} KiB`:`${e} B`}function o(t){const e=t??0;return e>=3600?`${(e/3600).toFixed(1)}h`:e>=60?`${Math.round(e/60)}m`:e>=1?`${e.toFixed(1)}s`:`${Math.round(e*1e3)}ms`}function d(t){const n=(t??0)*100;return n>=10?`${Math.round(n)}%`:`${n.toFixed(1)}%`}function m(t){if(!t)return"—";try{return new Date(t).toLocaleString()}catch{return"—"}}function h(t){return t==null?"—":t>=1e6?`${(t/1e6).toFixed(1)}M`:t>=1e3?`${Math.round(t/1e3)}K`:t.toLocaleString()}function x(t){return t==null?"—":`$${t.toFixed(4)}`}function S(t){if(!t)return"—";let e;try{if(e=new Date(t),isNaN(e.getTime()))return"—"}catch{return"—"}const n=Date.now()-e.getTime();if(n<0)return"just now";const c=Math.floor(n/1e3);if(c<60)return"just now";const r=Math.floor(c/60);if(r<60)return`${r}m ago`;const l=Math.floor(r/60);return l<24?`${l}h ago`:e.toLocaleDateString()}function s({label:t,value:e}){return a.jsxs("div",{className:"ov-stat-row",children:[a.jsx("span",{className:"ov-stat-label",children:t}),a.jsx("span",{className:"ov-stat-value",children:e})]})}function j(t){return a.jsxs("div",{className:"card cache-hits-card",children:[a.jsx("h3",{children:"💾 Cache Hits & Compactions"}),a.jsx(s,{label:"Cache Hits (session)",value:t.cacheHitsSession.toLocaleString()}),a.jsx(s,{label:"Cache Hits (total)",value:t.cacheHitsTotal.toLocaleString()}),a.jsx(s,{label:"Tokens Saved (session)",value:t.tokensSavedSession.toLocaleString()}),a.jsx(s,{label:"Tokens Saved (total)",value:t.tokensSavedTotal.toLocaleString()}),a.jsx(s,{label:"Compactions (session)",value:t.compactionsSession.toLocaleString()}),a.jsx(s,{label:"Compactions (total)",value:t.compactionsTotal.toLocaleString()})]})}function i({label:t,value:e}){return a.jsxs("div",{className:"ov-stat-row",children:[a.jsx("span",{className:"ov-stat-label",children:t}),a.jsx("span",{className:"ov-stat-value",children:e})]})}function v(t){return a.jsxs("div",{className:"card time-saved-card",children:[a.jsx("h3",{children:"⏱ Time Saved (est.)"}),a.jsx(i,{label:"Compact (session)",value:o(t.compactSessionSec)}),a.jsx(i,{label:"Compact (total)",value:o(t.compactTotalSec)}),a.jsx(i,{label:"Cache Hit (session)",value:o(t.cacheHitSessionSec)}),a.jsx(i,{label:"Cache Hit (total)",value:o(t.cacheHitTotalSec)})]})}export{j as C,v as T,f as a,h as b,x as c,m as d,S as e,d as f};
|
|
2
|
-
//# sourceMappingURL=TimeSavedCard-UqljSyP-.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimeSavedCard-UqljSyP-.js","sources":["../../src/utils/format.ts","../../src/components/CacheHitsCard.tsx","../../src/components/TimeSavedCard.tsx"],"sourcesContent":["/**\n * dashboard-client/src/utils/format.ts — shared formatting helpers.\n *\n * Mirrors the format functions from the old static dashboard (html.ts)\n * so the React rewrite shows identical values.\n */\n\n/** Format bytes as KiB/MiB/B (matches html.ts fmtBytes). */\nexport function fmtBytes(b: number | null | undefined): string {\n\tconst n = b ?? 0;\n\tif (n >= 1_048_576) return `${(n / 1_048_576).toFixed(1)} MiB`;\n\tif (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;\n\treturn `${n} B`;\n}\n\n/** Format seconds as h/m/s/ms (matches html.ts fmtSec). */\nexport function fmtSec(s: number | null | undefined): string {\n\tconst n = s ?? 0;\n\tif (n >= 3600) return `${(n / 3600).toFixed(1)}h`;\n\tif (n >= 60) return `${Math.round(n / 60)}m`;\n\tif (n >= 1) return `${n.toFixed(1)}s`;\n\treturn `${Math.round(n * 1000)}ms`;\n}\n\n/**\n * Display a 0–1 fraction as a percentage string.\n * Uses one decimal place below 10%, rounded above (matches html.ts pct logic).\n */\nexport function fmtPctFromFraction(v: number | null | undefined): string {\n\tconst n = v ?? 0;\n\tconst pct = n * 100;\n\tif (pct >= 10) return `${Math.round(pct)}%`;\n\treturn `${pct.toFixed(1)}%`;\n}\n\n/** Round a 0–1 fraction to a one-decimal percentage (for trophy values). */\nexport function fmtPct(v: number | null | undefined): string {\n\tconst n = v ?? 0;\n\treturn `${Math.round(n * 10) / 10}%`;\n}\n\n/** Format milliseconds (matches html.ts fmtMs). */\nexport function fmtMs(v: number | null | undefined): string {\n\tif (v == null) return \"\\u2014\";\n\treturn v >= 100 ? `${Math.round(v)}ms` : `${v.toFixed(1)}ms`;\n}\n\n/** Format a number with fixed decimals or em-dash for null (matches fmtNum). */\nexport function fmtNum(v: number | null | undefined, dec: number): string {\n\tif (v == null || typeof v !== \"number\") return \"\\u2014\";\n\treturn v.toFixed(dec);\n}\n\n/** Collapse an array of numbers into a range string or single value. */\nexport function collapseNum(samples: number[]): string {\n\tif (samples.length === 0) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\tif (lo === hi) return lo.toLocaleString();\n\treturn `${lo.toLocaleString()}\\u2013${hi.toLocaleString()}`;\n}\n\n/** Collapse an array of rates into a range string or single value. */\nexport function collapseRate(samples: number[]): string {\n\tif (samples.length === 0) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\tif (lo === hi) return `$${lo.toFixed(6)}`;\n\treturn `$${lo.toFixed(6)}\\u2013$${hi.toFixed(6)}`;\n}\n\n/** Format a date string or null as locale string or em-dash. */\nexport function fmtDate(ts: string | number | null | undefined): string {\n\tif (!ts) return \"\\u2014\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn \"\\u2014\";\n\t}\n}\n\n/** Format tokens as human-readable (1.2M / 340K / 890 / —). */\nexport function fmtTokens(v: number | null | undefined): string {\n\tif (v == null) return \"\\u2014\";\n\tif (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;\n\tif (v >= 1_000) return `${Math.round(v / 1000)}K`;\n\treturn v.toLocaleString();\n}\n\n/** Format USD savings (always 4 decimals, null → em-dash). */\nexport function fmtDollars(v: number | null | undefined): string {\n\tif (v == null) return \"\\u2014\";\n\treturn `$${v.toFixed(4)}`;\n}\n\n/**\n * Format an ISO timestamp as relative time (\"2m ago\", \"3h ago\", \"just now\").\n * Falls back to locale date for times >24h or unparseable input.\n */\nexport function fmtRelativeTime(\n\tts: string | number | null | undefined,\n): string {\n\tif (!ts) return \"\\u2014\";\n\tlet date: Date;\n\ttry {\n\t\tdate = new Date(ts);\n\t\tif (isNaN(date.getTime())) return \"\\u2014\";\n\t} catch {\n\t\treturn \"\\u2014\";\n\t}\n\tconst diffMs = Date.now() - date.getTime();\n\tif (diffMs < 0) return \"just now\";\n\tconst sec = Math.floor(diffMs / 1000);\n\tif (sec < 60) return \"just now\";\n\tconst min = Math.floor(sec / 60);\n\tif (min < 60) return `${min}m ago`;\n\tconst hrs = Math.floor(min / 60);\n\tif (hrs < 24) return `${hrs}h ago`;\n\treturn date.toLocaleDateString();\n}\n","/**\n * dashboard-client/src/components/CacheHitsCard.tsx — Cache Hits & Compactions card.\n *\n * 6 fields: Cache Hits (session), Cache Hits (total),\n * Tokens Saved (session), Tokens Saved (total),\n * Compactions (session), Compactions (total).\n */\n\nimport type React from \"react\";\n\nexport interface CacheHitsCardProps {\n\t/** Cache hits in the current session. */\n\tcacheHitsSession: number;\n\t/** Total cache hits across all sessions. */\n\tcacheHitsTotal: number;\n\t/** Tokens saved by cache hits this session. */\n\ttokensSavedSession: number;\n\t/** Total tokens saved by cache hits. */\n\ttokensSavedTotal: number;\n\t/** Compactions performed this session. */\n\tcompactionsSession: number;\n\t/** Total compactions performed. */\n\tcompactionsTotal: number;\n}\n\nfunction StatRow({\n\tlabel,\n\tvalue,\n}: {\n\tlabel: string;\n\tvalue: string | number;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"ov-stat-row\">\n\t\t\t<span className=\"ov-stat-label\">{label}</span>\n\t\t\t<span className=\"ov-stat-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\nexport function CacheHitsCard(\n\tprops: CacheHitsCardProps,\n): React.ReactElement {\n\treturn (\n\t\t<div className=\"card cache-hits-card\">\n\t\t\t<h3>💾 Cache Hits & Compactions</h3>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hits (session)\"\n\t\t\t\tvalue={props.cacheHitsSession.toLocaleString()}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hits (total)\"\n\t\t\t\tvalue={props.cacheHitsTotal.toLocaleString()}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Tokens Saved (session)\"\n\t\t\t\tvalue={props.tokensSavedSession.toLocaleString()}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Tokens Saved (total)\"\n\t\t\t\tvalue={props.tokensSavedTotal.toLocaleString()}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Compactions (session)\"\n\t\t\t\tvalue={props.compactionsSession.toLocaleString()}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Compactions (total)\"\n\t\t\t\tvalue={props.compactionsTotal.toLocaleString()}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/TimeSavedCard.tsx — Time Saved (est.) card.\n *\n * 4 fields: Compact (session), Compact (total),\n * Cache Hit (session), Cache Hit (total).\n */\n\nimport type React from \"react\";\nimport { fmtSec } from \"../utils/format\";\n\nexport interface TimeSavedCardProps {\n\t/** Seconds saved by compaction this session. */\n\tcompactSessionSec: number;\n\t/** Total seconds saved by compaction. */\n\tcompactTotalSec: number;\n\t/** Seconds saved by cache hits this session. */\n\tcacheHitSessionSec: number;\n\t/** Total seconds saved by cache hits. */\n\tcacheHitTotalSec: number;\n}\n\nfunction StatRow({\n\tlabel,\n\tvalue,\n}: {\n\tlabel: string;\n\tvalue: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"ov-stat-row\">\n\t\t\t<span className=\"ov-stat-label\">{label}</span>\n\t\t\t<span className=\"ov-stat-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\nexport function TimeSavedCard(\n\tprops: TimeSavedCardProps,\n): React.ReactElement {\n\treturn (\n\t\t<div className=\"card time-saved-card\">\n\t\t\t<h3>⏱ Time Saved (est.)</h3>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Compact (session)\"\n\t\t\t\tvalue={fmtSec(props.compactSessionSec)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Compact (total)\"\n\t\t\t\tvalue={fmtSec(props.compactTotalSec)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hit (session)\"\n\t\t\t\tvalue={fmtSec(props.cacheHitSessionSec)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hit (total)\"\n\t\t\t\tvalue={fmtSec(props.cacheHitTotalSec)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"names":["fmtBytes","b","n","fmtSec","s","fmtPctFromFraction","v","pct","fmtDate","ts","fmtTokens","fmtDollars","fmtRelativeTime","date","diffMs","sec","min","hrs","StatRow","label","value","jsxs","jsx","CacheHitsCard","props","TimeSavedCard"],"mappings":"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"}
|