pi-mega-compact 0.8.7 → 0.8.10
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/core.js +7 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints.js +104 -0
- package/dist/extensions/dashboard-server/api-contracts/game.js +9 -0
- package/dist/extensions/dashboard-server/api-contracts/index.js +9 -0
- package/dist/extensions/dashboard-server/api-contracts/infrastructure.js +10 -0
- package/dist/extensions/dashboard-server/api-contracts/multi-repo.js +9 -0
- package/dist/extensions/dashboard-server/api-contracts/snapshot.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts.test.js +869 -0
- package/dist/extensions/dashboard-server/auth.js +18 -0
- package/dist/extensions/dashboard-server/helpers.js +37 -0
- package/dist/extensions/dashboard-server/html/all-repos-tab.js +26 -0
- package/dist/extensions/dashboard-server/html/body-open.js +23 -0
- package/dist/extensions/dashboard-server/html/current-repo-tab.js +130 -0
- package/dist/extensions/dashboard-server/html/head-open.js +16 -0
- package/dist/extensions/dashboard-server/html/high-score-tab.js +25 -0
- package/dist/extensions/dashboard-server/html/repo-detail-modal.js +26 -0
- package/dist/extensions/dashboard-server/html/script.js +259 -0
- package/dist/extensions/dashboard-server/html/styles.js +103 -0
- package/dist/extensions/dashboard-server/html/summary-tab.js +19 -0
- package/dist/extensions/dashboard-server/html-template.js +41 -0
- package/dist/extensions/dashboard-server/html.js +46 -1
- package/dist/extensions/dashboard-server/perf-server.test.js +80 -0
- package/dist/extensions/dashboard-server/server.js +269 -30
- package/dist/extensions/dashboard-server/tailscale.js +8 -0
- package/dist/extensions/dashboard-server/types.js +4 -0
- package/dist/extensions/mega-dashboard.js +9 -0
- package/dist/extensions/mega-events/perf-handler.js +71 -0
- package/dist/extensions/mega-events/register.js +2 -0
- package/dist/extensions/mega-events.js +1 -0
- package/dist/extensions/mega-runtime/state.js +59 -1
- package/dist/src/store/sqlite/connection.js +35 -0
- package/dist/src/store/sqlite/index-store.js +167 -0
- package/dist/src/store/sqlite/memory.js +54 -0
- package/dist/src/store/sqlite/minhash-lsh.js +47 -0
- package/dist/src/store/sqlite/perf-samples.js +81 -0
- package/dist/src/store/sqlite/perf-samples.test.js +54 -0
- package/dist/src/store/sqlite/schema.js +14 -0
- package/dist/src/store/sqlite/sessions.js +39 -0
- package/dist/src/store/sqlite/transaction.js +19 -0
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/vectorStore/add.js +260 -0
- package/dist/src/vectorStore/dedup.js +52 -0
- package/dist/src/vectorStore/index.js +10 -0
- package/dist/src/vectorStore/queries.js +83 -0
- package/dist/src/vectorStore/search.js +95 -0
- package/dist/src/vectorStore/session.js +19 -0
- package/dist/src/vectorStore/store.js +105 -0
- package/dist/src/vectorStore/types.js +6 -0
- package/dist/src/vectorStore/utils.js +23 -0
- package/extensions/dashboard-client/package-lock.json +1771 -0
- package/extensions/dashboard-client/package.json +27 -0
- package/extensions/dashboard-client/src/App.tsx +82 -0
- package/extensions/dashboard-client/src/api/client.ts +145 -0
- package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +44 -0
- package/extensions/dashboard-client/src/components/CompressionCard.tsx +61 -0
- package/extensions/dashboard-client/src/components/ContextGauge.tsx +59 -0
- package/extensions/dashboard-client/src/components/DataSafetyCard.tsx +49 -0
- package/extensions/dashboard-client/src/components/ErrorBoundary.tsx +53 -0
- package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +16 -0
- package/extensions/dashboard-client/src/components/EventStream.tsx +152 -0
- package/extensions/dashboard-client/src/components/LoadingSpinner.tsx +7 -0
- package/extensions/dashboard-client/src/components/MemoryStatusCard.tsx +24 -0
- package/extensions/dashboard-client/src/components/ModelBadge.tsx +41 -0
- package/extensions/dashboard-client/src/components/PerfChart.tsx +160 -0
- package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +126 -0
- package/extensions/dashboard-client/src/components/RepoTable.tsx +150 -0
- package/extensions/dashboard-client/src/components/SessionInfo.tsx +65 -0
- package/extensions/dashboard-client/src/components/SummaryTiles.tsx +52 -0
- package/extensions/dashboard-client/src/components/TabBar.tsx +34 -0
- package/extensions/dashboard-client/src/components/TriggerStatus.tsx +62 -0
- package/extensions/dashboard-client/src/hooks/useApi.ts +77 -0
- package/extensions/dashboard-client/src/hooks/useSSE.ts +87 -0
- package/extensions/dashboard-client/src/index.html +12 -0
- package/extensions/dashboard-client/src/main.tsx +23 -0
- package/extensions/dashboard-client/src/styles/base.css +121 -0
- package/extensions/dashboard-client/src/styles/overview-events.css +318 -0
- package/extensions/dashboard-client/src/styles/repos-metrics.css +307 -0
- package/extensions/dashboard-client/src/tabs/ConfigTab.tsx +16 -0
- package/extensions/dashboard-client/src/tabs/EventsTab.tsx +37 -0
- package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +49 -0
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +80 -0
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +59 -0
- package/extensions/dashboard-client/tsconfig.json +28 -0
- package/extensions/dashboard-client/vite.config.ts +38 -0
- package/extensions/dashboard-server/api-contracts/core.ts +302 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +476 -0
- package/extensions/dashboard-server/api-contracts/game.ts +145 -0
- package/extensions/dashboard-server/api-contracts/index.ts +159 -0
- package/extensions/dashboard-server/api-contracts/infrastructure.ts +465 -0
- package/extensions/dashboard-server/api-contracts/multi-repo.ts +328 -0
- package/extensions/dashboard-server/api-contracts/snapshot.ts +386 -0
- package/extensions/dashboard-server/api-contracts.test.ts +1050 -0
- package/extensions/dashboard-server/api-contracts.ts +96 -0
- package/extensions/dashboard-server/auth.ts +20 -0
- package/extensions/dashboard-server/html.ts +46 -1
- package/extensions/dashboard-server/perf-server.test.ts +101 -0
- package/extensions/dashboard-server/server.ts +862 -503
- package/extensions/dashboard-server/tailscale.ts +7 -0
- package/extensions/dashboard-server/types.ts +23 -114
- package/extensions/mega-dashboard.ts +17 -0
- package/extensions/mega-events/perf-handler.ts +113 -0
- package/extensions/mega-events/register.ts +2 -0
- package/extensions/mega-events.ts +1 -0
- package/extensions/mega-runtime/state.ts +57 -0
- package/package.json +2 -1
- package/src/store/sqlite/perf-samples.test.ts +65 -0
- package/src/store/sqlite/perf-samples.ts +125 -0
- package/src/store/sqlite/schema.ts +14 -0
- package/src/store/sqlite.ts +1 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Import from `./api-contracts/index.js` (barrel) instead.
|
|
3
|
+
* This file is retained for backward compatibility. All types are re-exported
|
|
4
|
+
* from domain modules under `api-contracts/`.
|
|
5
|
+
*
|
|
6
|
+
* See: api-contracts/core.ts, snapshot.ts, multi-repo.ts, game.ts, infrastructure.ts
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Re-export everything from the barrel
|
|
10
|
+
export type {
|
|
11
|
+
// Core
|
|
12
|
+
HttpMethod,
|
|
13
|
+
EndpointDef,
|
|
14
|
+
SseCompactStart,
|
|
15
|
+
SseCompactEnd,
|
|
16
|
+
SseCompactTrigger,
|
|
17
|
+
SseCompactSkip,
|
|
18
|
+
SseTierChanged,
|
|
19
|
+
SseModelChanged,
|
|
20
|
+
SsePressureLifted,
|
|
21
|
+
SseCheckpointPersisted,
|
|
22
|
+
SseRecallInject,
|
|
23
|
+
SseAnchorsUpdated,
|
|
24
|
+
SseConfigUpdated,
|
|
25
|
+
SseConfigPreset,
|
|
26
|
+
SseCrewPresenceChanged,
|
|
27
|
+
SseCrewTurnChanged,
|
|
28
|
+
SseCrewBanditChosen,
|
|
29
|
+
// Snapshot
|
|
30
|
+
SnapshotResponse,
|
|
31
|
+
TriggerResponse,
|
|
32
|
+
CompressionTotalsResponse,
|
|
33
|
+
CompactHistoryEntry,
|
|
34
|
+
CompactionRequest,
|
|
35
|
+
CompactionResponse,
|
|
36
|
+
// Multi-repo
|
|
37
|
+
RepoListItem,
|
|
38
|
+
RepoSnapshotEntry,
|
|
39
|
+
RepoSnapshotMap,
|
|
40
|
+
IndexesIndexRow,
|
|
41
|
+
IndexesSummaryResponse,
|
|
42
|
+
IndexesDiffEntry,
|
|
43
|
+
DiffRequest,
|
|
44
|
+
SnapshotLike,
|
|
45
|
+
DiffResponse,
|
|
46
|
+
UpdateRepoConfigRequest,
|
|
47
|
+
// Game
|
|
48
|
+
GameConfig,
|
|
49
|
+
GameStateResponse,
|
|
50
|
+
GameRitualStage,
|
|
51
|
+
SseGameRitualStart,
|
|
52
|
+
SseGameRitualStage,
|
|
53
|
+
SseGameRitualEnd,
|
|
54
|
+
SseGameModeChanged,
|
|
55
|
+
SseGameRender,
|
|
56
|
+
// Infrastructure
|
|
57
|
+
InfraHealthResponse,
|
|
58
|
+
InfraPerfSampleResponse,
|
|
59
|
+
InfraRateLimitStatus,
|
|
60
|
+
InfraRateLimitResponse,
|
|
61
|
+
ContextLevelState,
|
|
62
|
+
TierOverrideState,
|
|
63
|
+
FallbackState,
|
|
64
|
+
RepeatInjectionState,
|
|
65
|
+
SupersedeGatingState,
|
|
66
|
+
MinHashBandState,
|
|
67
|
+
// Composite
|
|
68
|
+
SseEvent,
|
|
69
|
+
// Endpoints registry (Sprint A1)
|
|
70
|
+
VersionResponse,
|
|
71
|
+
IndexSummary,
|
|
72
|
+
IndexFallbackResponse,
|
|
73
|
+
ReposQuery,
|
|
74
|
+
ReposResponse,
|
|
75
|
+
SummaryResponse,
|
|
76
|
+
DriftSeverity,
|
|
77
|
+
DriftSignal,
|
|
78
|
+
RepoDrift,
|
|
79
|
+
DriftReportResponse,
|
|
80
|
+
ServerEntry,
|
|
81
|
+
ServersResponse,
|
|
82
|
+
PerfPercentile,
|
|
83
|
+
PerfAverage,
|
|
84
|
+
PerfLatest,
|
|
85
|
+
PerfCacheHit,
|
|
86
|
+
PerfDiag,
|
|
87
|
+
PerfQuery,
|
|
88
|
+
PerfResponse,
|
|
89
|
+
GameScoreRow,
|
|
90
|
+
GameScoresQuery,
|
|
91
|
+
AchievementRow,
|
|
92
|
+
GameStatePatch,
|
|
93
|
+
SseEndpointDef,
|
|
94
|
+
} from './api-contracts/index.js';
|
|
95
|
+
|
|
96
|
+
export { ENDPOINTS } from './api-contracts/index.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
const CSRF_TOKENS = new Map<string, number>();
|
|
4
|
+
export const CSRF_MAX_AGE_MS = 60 * 60 * 1000; // 60 min rotation
|
|
5
|
+
|
|
6
|
+
export function generateCsrfToken(): string {
|
|
7
|
+
const token = randomUUID();
|
|
8
|
+
CSRF_TOKENS.set(token, Date.now());
|
|
9
|
+
return token;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isCsrfValid(token: string): boolean {
|
|
13
|
+
if (!CSRF_TOKENS.has(token)) return false;
|
|
14
|
+
const ts = CSRF_TOKENS.get(token) ?? 0;
|
|
15
|
+
if (Date.now() - ts > CSRF_MAX_AGE_MS) {
|
|
16
|
+
CSRF_TOKENS.delete(token);
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
@@ -189,6 +189,7 @@ export function dashboardHtml(tierName: string): string {
|
|
|
189
189
|
<button class="tab" data-tab="active">Active Repos</button>
|
|
190
190
|
<button class="tab" data-tab="summary">Summary</button>
|
|
191
191
|
<button class="tab" data-tab="game">Game Mode</button>
|
|
192
|
+
<button class="tab" data-tab="perf">Perf</button>
|
|
192
193
|
</nav>
|
|
193
194
|
|
|
194
195
|
<!-- Current repo (existing single-repo view) -->
|
|
@@ -443,6 +444,18 @@ export function dashboardHtml(tierName: string): string {
|
|
|
443
444
|
<div id="game-empty">No scores yet — run a session with game mode on.</div>
|
|
444
445
|
</div>
|
|
445
446
|
|
|
447
|
+
<!-- Perf (v0.8.8) — live local instrumentation -->
|
|
448
|
+
<div class="tab-panel" id="panel-perf">
|
|
449
|
+
<div class="grid">
|
|
450
|
+
<div class="card"><h2>Model latency</h2><div class="stat-grid"><span class="label">Turn p50</span><span class="value" id="pf-turn-p50">—</span><span class="label">Turn p95</span><span class="value" id="pf-turn-p95">—</span><span class="label">Provider p50</span><span class="value" id="pf-prov-p50">—</span><span class="label">Provider p95</span><span class="value" id="pf-prov-p95">—</span></div></div>
|
|
451
|
+
<div class="card"><h2>Throughput</h2><div class="stat-grid"><span class="label">TPS (avg)</span><span class="value" id="pf-tps">—</span><span class="label">Cache hit %</span><span class="value" id="pf-cache">—</span></div></div>
|
|
452
|
+
<div class="card"><h2>Process</h2><div class="stat-grid"><span class="label">RSS</span><span class="value" id="pf-rss">—</span><span class="label">Heap</span><span class="value" id="pf-heap">—</span><span class="label">CPU user/sys</span><span class="value" id="pf-cpu">—</span></div></div>
|
|
453
|
+
<div class="card"><h2>Snapshot cost</h2><div class="stat-grid"><span class="label">DB recompute p50</span><span class="value" id="pf-db-p50">—</span><span class="label">DB recompute p95</span><span class="value" id="pf-db-p95">—</span><span class="label">Disk write p50</span><span class="value" id="pf-disk">—</span></div></div>
|
|
454
|
+
<div class="card"><h2>TUI lag proxy</h2><div class="stat-grid"><span class="label">Live-trim fires</span><span class="value" id="pf-recompute">—</span><span class="label">Cache replays</span><span class="value" id="pf-replays">—</span><span class="label">Fast-gate skips</span><span class="value" id="pf-skips">—</span></div><div class="meter-sub">skip vs recompute vs replay cadence</div></div>
|
|
455
|
+
</div>
|
|
456
|
+
<div class="updated" id="perf-updated">waiting for data</div>
|
|
457
|
+
</div>
|
|
458
|
+
|
|
446
459
|
<script>
|
|
447
460
|
(function() {
|
|
448
461
|
var evBox = document.getElementById('events');
|
|
@@ -1000,9 +1013,40 @@ export function dashboardHtml(tierName: string): string {
|
|
|
1000
1013
|
}).catch(function() {});
|
|
1001
1014
|
}
|
|
1002
1015
|
|
|
1016
|
+
// --- Perf tab (v0.8.8) — live local instrumentation ----------------------
|
|
1017
|
+
var perfPollTimer = null;
|
|
1018
|
+
function pollPerf() {
|
|
1019
|
+
fetch('/api/perf?minutes=30').then(function(r) { return r.ok ? r.json() : null; }).then(function(d) { // guardrails-allow PREVENT-PI-004: browser-side fetch in dashboard HTML template (not Node runtime)
|
|
1020
|
+
if (!d) return;
|
|
1021
|
+
var el = document.getElementById('perf-updated');
|
|
1022
|
+
if (el) el.textContent = d.sampleCount + ' samples \u00b7 updated ' + (d.updatedAt || '');
|
|
1023
|
+
function setText(id, txt) { var e = document.getElementById(id); if (e) e.textContent = txt; }
|
|
1024
|
+
function fmtMs(v) { return v == null ? '\u2014' : (v >= 100 ? Math.round(v) + 'ms' : v.toFixed(1) + 'ms'); }
|
|
1025
|
+
function fmtNum(v, dec) { return v == null ? '\u2014' : (typeof v === 'number' ? v.toFixed(dec) : '\u2014'); }
|
|
1026
|
+
setText('pf-turn-p50', fmtMs(d.turn_latency_ms && d.turn_latency_ms.p50));
|
|
1027
|
+
setText('pf-turn-p95', fmtMs(d.turn_latency_ms && d.turn_latency_ms.p95));
|
|
1028
|
+
setText('pf-prov-p50', fmtMs(d.provider_latency_ms && d.provider_latency_ms.p50));
|
|
1029
|
+
setText('pf-prov-p95', fmtMs(d.provider_latency_ms && d.provider_latency_ms.p95));
|
|
1030
|
+
setText('pf-tps', fmtNum(d.tps && d.tps.avg, 1));
|
|
1031
|
+
setText('pf-cache', (d.cache_hit_pct && typeof d.cache_hit_pct.avg === 'number') ? fmtNum(d.cache_hit_pct.avg, 1) + '%' : '\u2014');
|
|
1032
|
+
setText('pf-rss', (d.rss_mb && typeof d.rss_mb.latest === 'number') ? fmtNum(d.rss_mb.latest, 1) + ' MB' : '\u2014');
|
|
1033
|
+
setText('pf-heap', (d.heap_mb && typeof d.heap_mb.latest === 'number') ? fmtNum(d.heap_mb.latest, 1) + ' MB' : '\u2014');
|
|
1034
|
+
setText('pf-cpu', (d.cpu_user_ms && d.cpu_sys_ms) ? (fmtNum(d.cpu_user_ms.latest,1) + ' / ' + fmtNum(d.cpu_sys_ms.latest,1) + ' ms') : '\u2014');
|
|
1035
|
+
setText('pf-db-p50', fmtMs(d.db_recompute_ms && d.db_recompute_ms.p50));
|
|
1036
|
+
setText('pf-db-p95', fmtMs(d.db_recompute_ms && d.db_recompute_ms.p95));
|
|
1037
|
+
setText('pf-disk', fmtMs(d.disk_write_ms && d.disk_write_ms.p50));
|
|
1038
|
+
var diag = d.diag || {};
|
|
1039
|
+
setText('pf-recompute', diag.liveTrimFires != null ? String(diag.liveTrimFires) : '\u2014');
|
|
1040
|
+
setText('pf-replays', diag.liveTrimReplays != null ? String(diag.liveTrimReplays) : '\u2014');
|
|
1041
|
+
setText('pf-skips', diag.ctxFastGate != null ? String(diag.ctxFastGate) : '\u2014');
|
|
1042
|
+
}).catch(function() {});
|
|
1043
|
+
}
|
|
1044
|
+
function startPerfPoll() { if (perfPollTimer) return; pollPerf(); perfPollTimer = setInterval(pollPerf, 2000); }
|
|
1045
|
+
function stopPerfPoll() { if (perfPollTimer) { clearInterval(perfPollTimer); perfPollTimer = null; } }
|
|
1046
|
+
|
|
1003
1047
|
// --- Tab switching ------------------------------------------------------
|
|
1004
1048
|
var tabs = document.querySelectorAll('.tab');
|
|
1005
|
-
var panels = { current: 'panel-current', all: 'panel-all', active: 'panel-active', summary: 'panel-summary', game: 'panel-game' };
|
|
1049
|
+
var panels = { current: 'panel-current', all: 'panel-all', active: 'panel-active', summary: 'panel-summary', game: 'panel-game', perf: 'panel-perf' };
|
|
1006
1050
|
for (var i = 0; i < tabs.length; i++) {
|
|
1007
1051
|
tabs[i].addEventListener('click', function() {
|
|
1008
1052
|
var name = this.getAttribute('data-tab');
|
|
@@ -1017,6 +1061,7 @@ export function dashboardHtml(tierName: string): string {
|
|
|
1017
1061
|
if (name === 'all' || name === 'summary') pollIndex();
|
|
1018
1062
|
if (name === 'active') pollServers();
|
|
1019
1063
|
if (name === 'game') { renderGameScores(); renderAchievements(); }
|
|
1064
|
+
if (name === 'perf') startPerfPoll(); else stopPerfPoll();
|
|
1020
1065
|
});
|
|
1021
1066
|
}
|
|
1022
1067
|
})();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* perf-server.test.ts — v0.8.8 /api/perf endpoint (GET aggregates + 405).
|
|
3
|
+
* Mirrors the server.test.ts spawn-and-fetch harness (self-contained so the
|
|
4
|
+
* dashboard HTTP-port lane stays isolated).
|
|
5
|
+
*/
|
|
6
|
+
import { test, describe } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { recordPerfSample } from "../../src/store/sqlite.js";
|
|
13
|
+
|
|
14
|
+
const SERVER_ENTRY = new URL("./server.js", import.meta.url).pathname;
|
|
15
|
+
|
|
16
|
+
function waitFor(cond: () => boolean | Promise<boolean>, timeoutMs = 6000): Promise<void> {
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const tick = async () => {
|
|
20
|
+
if (await cond()) return resolve();
|
|
21
|
+
if (Date.now() - start > timeoutMs) return reject(new Error("timeout"));
|
|
22
|
+
setTimeout(tick, 50);
|
|
23
|
+
};
|
|
24
|
+
tick();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function freshDir(prefix: string): string {
|
|
29
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function withServer<T>(
|
|
33
|
+
port: string,
|
|
34
|
+
dir: string,
|
|
35
|
+
fn: (port: number) => Promise<T>,
|
|
36
|
+
): Promise<T> {
|
|
37
|
+
process.env.MEGACOMPACT_DASHBOARD_PORT = port;
|
|
38
|
+
const child = spawn(process.execPath, [SERVER_ENTRY, dir], { stdio: "ignore" });
|
|
39
|
+
try {
|
|
40
|
+
await waitFor(async () => {
|
|
41
|
+
try {
|
|
42
|
+
const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
|
|
43
|
+
const res = await fetch(`http://localhost:${raw.port}/api/version`);
|
|
44
|
+
return res.ok;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
|
|
50
|
+
return await fn(raw.port);
|
|
51
|
+
} finally {
|
|
52
|
+
child.kill("SIGTERM");
|
|
53
|
+
delete process.env.MEGACOMPACT_DASHBOARD_PORT;
|
|
54
|
+
rmSync(dir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface PerfResp {
|
|
59
|
+
sampleCount: number;
|
|
60
|
+
turn_latency_ms: { p50: number; p95: number; n: number };
|
|
61
|
+
provider_latency_ms: { p50: number; p95: number; n: number };
|
|
62
|
+
tps: { avg: number; n: number };
|
|
63
|
+
cache_hit_pct: { avg: number; latest: number; n: number };
|
|
64
|
+
db_recompute_ms: { p50: number; p95: number; n: number };
|
|
65
|
+
disk_write_ms: { p50: number; p95: number; n: number };
|
|
66
|
+
rss_mb: { latest: number; n: number };
|
|
67
|
+
heap_mb: { latest: number; n: number };
|
|
68
|
+
cpu_user_ms: { latest: number; n: number };
|
|
69
|
+
cpu_sys_ms: { latest: number; n: number };
|
|
70
|
+
diag: { ctxFastGate: number; liveTrimFires: number; liveTrimReplays: number } | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("v0.8.8 /api/perf", () => {
|
|
74
|
+
test("GET returns aggregates over recorded perf_samples", async () => {
|
|
75
|
+
const dir = freshDir("dash-perf-agg-");
|
|
76
|
+
recordPerfSample(dir, "turn_latency_ms", 100, { turnIndex: 1 });
|
|
77
|
+
recordPerfSample(dir, "turn_latency_ms", 200);
|
|
78
|
+
recordPerfSample(dir, "tps", 50);
|
|
79
|
+
recordPerfSample(dir, "rss_mb", 256);
|
|
80
|
+
await withServer("19440", dir, async (port) => {
|
|
81
|
+
const res = await fetch(`http://localhost:${port}/api/perf?minutes=30`);
|
|
82
|
+
assert.equal(res.status, 200);
|
|
83
|
+
const d = (await res.json()) as PerfResp;
|
|
84
|
+
assert.equal(d.sampleCount, 4);
|
|
85
|
+
assert.equal(d.turn_latency_ms.n, 2);
|
|
86
|
+
assert.equal(d.turn_latency_ms.p50, 100); // nearest-rank p50 of [100,200]
|
|
87
|
+
assert.equal(d.turn_latency_ms.p95, 200); // nearest-rank p95 of [100,200]
|
|
88
|
+
assert.equal(d.tps.avg, 50);
|
|
89
|
+
assert.equal(d.rss_mb.latest, 256);
|
|
90
|
+
assert.equal(d.diag, null); // no runtime wrote dashboard.json in this dir
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("non-GET (POST) -> 405", async () => {
|
|
95
|
+
const dir = freshDir("dash-perf-meth-");
|
|
96
|
+
await withServer("19441", dir, async (port) => {
|
|
97
|
+
const res = await fetch(`http://localhost:${port}/api/perf`, { method: "POST" });
|
|
98
|
+
assert.equal(res.status, 405);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|