pi-mega-compact 0.14.1 → 0.15.1
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/registry.js +6 -0
- package/dist/extensions/dashboard-server/resolve-snapshot.js +20 -8
- package/dist/extensions/dashboard-server/routes-game.js +60 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +3 -1
- package/extensions/dashboard-client/dist/assets/AreaChart-rJdaOUlj.js +2 -0
- package/extensions/dashboard-client/dist/assets/AreaChart-rJdaOUlj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/BarChart-DOfc8me9.js +2 -0
- package/extensions/dashboard-client/dist/assets/BarChart-DOfc8me9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{CacheTab-VNFZSdRr.js → CacheTab-EhqkEglo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-VNFZSdRr.js.map → CacheTab-EhqkEglo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-Dr7RDBy2.js → EventsTab-BUACwWXf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-Dr7RDBy2.js.map → EventsTab-BUACwWXf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-B8VZ8gbb.js → HealthTab-CDEYVRBb.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-B8VZ8gbb.js.map → HealthTab-CDEYVRBb.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DMtrXRcn.js → MaintenanceTab-DtanIeCQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DMtrXRcn.js.map → MaintenanceTab-DtanIeCQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-PFrMdLOm.js → MemoryMapTab-BGEhL7OG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-PFrMdLOm.js.map → MemoryMapTab-BGEhL7OG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-Cktozn4s.js +2 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-Cktozn4s.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-08v6Ayuf.js +16 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-08v6Ayuf.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ReposTab-D6pEUtEJ.js → ReposTab-Bg72Ozdc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-D6pEUtEJ.js.map → ReposTab-Bg72Ozdc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-BcTK9Lg6.js +2 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CcFzDJ7W.js.map → SessionsTab-BcTK9Lg6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-YCt5NGB5.js → SetupTab-Bt-Ru1xp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-YCt5NGB5.js.map → SetupTab-Bt-Ru1xp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BsLNhOOj.js → TimeSavedCard-ic_LjJ79.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BsLNhOOj.js.map → TimeSavedCard-ic_LjJ79.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-C1jpVZrH.js → TurnsTab-D9AZTMgG.js} +4 -4
- package/extensions/dashboard-client/dist/assets/{TurnsTab-C1jpVZrH.js.map → TurnsTab-D9AZTMgG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/WikiTab-hRcJPi_m.js +2 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-RHoVt95g.js.map → WikiTab-hRcJPi_m.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-DSZI4aZ5.js → button-BdAtIbVu.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-DSZI4aZ5.js.map → button-BdAtIbVu.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-CsZzIyPW.js → card-BFYc_QrK.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-CsZzIyPW.js.map → card-BFYc_QrK.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/generateCategoricalChart-BwzyqytT.js +70 -0
- package/extensions/dashboard-client/dist/assets/generateCategoricalChart-BwzyqytT.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{index-B1_J_Y0b.js → index-pmtEr3sb.js} +8 -8
- package/extensions/dashboard-client/dist/assets/index-pmtEr3sb.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-za_gmEmQ.css +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-DOIA1Nxu.js → switch-DEyGlFwJ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-DOIA1Nxu.js.map → switch-DEyGlFwJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CyS6PHCO.js → toggle-pDMBQYQj.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CyS6PHCO.js.map → toggle-pDMBQYQj.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-CY7UIUzm.js → useSSE-BKaC4dao.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-CY7UIUzm.js.map → useSSE-BKaC4dao.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/package-lock.json +56 -39
- package/extensions/dashboard-client/package.json +3 -0
- package/extensions/dashboard-client/src/api/client.ts +11 -0
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +14 -8
- package/extensions/dashboard-client/src/components/WidgetDetailModal.tsx +69 -0
- package/extensions/dashboard-client/src/components/charts/PerfBarChart.tsx +89 -0
- package/extensions/dashboard-client/src/components/charts/PerfLineChart.tsx +108 -0
- package/extensions/dashboard-client/src/components/ui/SortableCard.tsx +75 -0
- package/extensions/dashboard-client/src/hooks/useCardPositions.ts +74 -0
- package/extensions/dashboard-client/src/styles/session-gauges.css +28 -15
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +310 -79
- package/extensions/dashboard-client/src/types/card.ts +40 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +10 -0
- package/extensions/dashboard-server/api-contracts/endpoints/types.ts +36 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +3 -0
- package/extensions/dashboard-server/api-contracts/index.ts +3 -0
- package/extensions/dashboard-server/resolve-snapshot.ts +22 -10
- package/extensions/dashboard-server/routes-game.ts +72 -0
- package/extensions/dashboard-server/routes.ts +1 -0
- package/extensions/dashboard-server/server.ts +2 -0
- package/package.json +1 -1
- package/extensions/dashboard-client/dist/assets/AreaChart-BUUketyS.js +0 -70
- package/extensions/dashboard-client/dist/assets/AreaChart-BUUketyS.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-Dd1kXuvg.js +0 -2
- package/extensions/dashboard-client/dist/assets/MetricsTab-Dd1kXuvg.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-DOpObVA9.js +0 -2
- package/extensions/dashboard-client/dist/assets/OverviewTab-DOpObVA9.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-CcFzDJ7W.js +0 -2
- package/extensions/dashboard-client/dist/assets/WikiTab-RHoVt95g.js +0 -2
- package/extensions/dashboard-client/dist/assets/index-B1_J_Y0b.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-C5TGTOd6.css +0 -1
|
@@ -348,3 +348,39 @@ export interface PerfResponse {
|
|
|
348
348
|
/** Diagnostic counters from the live snapshot, or null if unavailable. */
|
|
349
349
|
readonly diag: PerfDiag | null;
|
|
350
350
|
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Query parameters for GET /api/perf/samples. `kind` selects which perf metric
|
|
354
|
+
* to return raw samples for; `minutes` controls the rolling window size.
|
|
355
|
+
*/
|
|
356
|
+
export interface PerfSamplesQuery {
|
|
357
|
+
/** Perf metric kind (turn_latency_ms, tps, disk_write_ms, cache_hit_pct, ...). Required. */
|
|
358
|
+
readonly kind: string;
|
|
359
|
+
/** Rolling window size in minutes (default: 60). Optional. */
|
|
360
|
+
readonly minutes?: number;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** A single raw perf sample for time-series charting. */
|
|
364
|
+
export interface PerfSamplePoint {
|
|
365
|
+
/** Epoch ms timestamp of the sample. */
|
|
366
|
+
readonly ts: number;
|
|
367
|
+
/** Sample value for the requested kind. */
|
|
368
|
+
readonly value: number;
|
|
369
|
+
/** Optional stringified metadata associated with the sample. */
|
|
370
|
+
readonly meta?: string;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Response for GET /api/perf/samples. Raw perf samples for a single kind,
|
|
375
|
+
* ascending by ts, for chart drill-down in the Perf tab.
|
|
376
|
+
*/
|
|
377
|
+
export interface PerfSamplesResponse {
|
|
378
|
+
/** Array of raw samples ascending by ts. */
|
|
379
|
+
readonly samples: PerfSamplePoint[];
|
|
380
|
+
/** The requested perf kind. */
|
|
381
|
+
readonly kind: string;
|
|
382
|
+
/** The rolling window size in minutes used. */
|
|
383
|
+
readonly minutes: number;
|
|
384
|
+
/** Epoch ms timestamp when the response was generated. */
|
|
385
|
+
readonly updatedAt: number;
|
|
386
|
+
}
|
|
@@ -5,11 +5,17 @@
|
|
|
5
5
|
* active pi session may be running in a different repo. This module queries
|
|
6
6
|
* the repo registry to find the most recently active repo's dashboard.json,
|
|
7
7
|
* so the Overview always shows live data from the repo pi is actually running
|
|
8
|
-
* in
|
|
8
|
+
* in.
|
|
9
|
+
*
|
|
10
|
+
* The launch stateDir is a candidate like any other (NOT excluded): when
|
|
11
|
+
* `/dashboard` was run in the repo you are still coding in, the launch dir
|
|
12
|
+
* IS the most-recently-active repo and must win. Excluding it would serve a
|
|
13
|
+
* stale other-repo snapshot, surfacing a phantom 0% "this session" gauge
|
|
14
|
+
* plus a duplicate live-session gauge.
|
|
9
15
|
*
|
|
10
16
|
* Resolution order:
|
|
11
|
-
* 1. Most recently seen repo (by last_seen in repo_registry
|
|
12
|
-
* dashboard.json exists and has a non-null updatedAt
|
|
17
|
+
* 1. Most recently seen repo (by last_seen in repo_registry, launch dir
|
|
18
|
+
* included) whose dashboard.json exists and has a non-null updatedAt
|
|
13
19
|
* 2. The static launch stateDir's dashboard.json (original behavior)
|
|
14
20
|
*/
|
|
15
21
|
|
|
@@ -35,23 +41,30 @@ interface RepoRegistryRow {
|
|
|
35
41
|
|
|
36
42
|
/**
|
|
37
43
|
* Find the most recently active repo's stateDir by scanning the repo registry
|
|
38
|
-
* for
|
|
39
|
-
* if no suitable repo is found.
|
|
44
|
+
* for the repo with the highest last_seen whose dashboard.json exists and has
|
|
45
|
+
* a non-null updatedAt. Returns null if no suitable repo is found.
|
|
46
|
+
*
|
|
47
|
+
* The launch stateDir is NOT excluded: if it is itself the most-recently-active
|
|
48
|
+
* repo (the common case — `/dashboard` was run in the repo you are still
|
|
49
|
+
* coding in), its live dashboard.json must be served. Excluding it would cause
|
|
50
|
+
* a stale *other*-repo snapshot to win, producing a phantom 0% "this session"
|
|
51
|
+
* gauge + a duplicate live-session gauge.
|
|
40
52
|
*/
|
|
41
|
-
function mostRecentRepoStateDir(
|
|
53
|
+
function mostRecentRepoStateDir(_staticStateDir: string): string | null {
|
|
42
54
|
const indexPath = join(getIndexDir(), "index.sqlite");
|
|
43
55
|
if (!existsSync(indexPath)) return null;
|
|
44
56
|
let db;
|
|
45
57
|
try {
|
|
46
|
-
const { DatabaseSync } =
|
|
58
|
+
const { DatabaseSync } =
|
|
59
|
+
require("node:sqlite") as typeof import("node:sqlite");
|
|
47
60
|
db = new DatabaseSync(indexPath, { readOnly: true });
|
|
48
61
|
const rows = db
|
|
49
62
|
.prepare(
|
|
50
63
|
`SELECT state_dir, last_seen FROM repo_registry
|
|
51
|
-
WHERE state_dir IS NOT NULL AND
|
|
64
|
+
WHERE state_dir IS NOT NULL AND last_seen IS NOT NULL
|
|
52
65
|
ORDER BY last_seen DESC`,
|
|
53
66
|
)
|
|
54
|
-
.all(
|
|
67
|
+
.all() as unknown as RepoRegistryRow[];
|
|
55
68
|
for (const r of rows) {
|
|
56
69
|
const dashPath = join(r.state_dir, "dashboard.json");
|
|
57
70
|
if (!existsSync(dashPath)) continue;
|
|
@@ -95,4 +108,3 @@ export function resolveSnapshot(
|
|
|
95
108
|
stateDir: staticStateDir,
|
|
96
109
|
};
|
|
97
110
|
}
|
|
98
|
-
|
|
@@ -8,6 +8,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
8
8
|
|
|
9
9
|
import type { RouteContext } from "./routes-core.js";
|
|
10
10
|
import type { GameMetric } from "../../src/game/scoring.js";
|
|
11
|
+
import type { PerfKind } from "../../src/store/sqlite/perf-samples.js";
|
|
11
12
|
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// handleGameState — "/api/game-state" (GET + PUT)
|
|
@@ -351,6 +352,77 @@ export function handlePerf(
|
|
|
351
352
|
return true;
|
|
352
353
|
}
|
|
353
354
|
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
// handlePerfSamples — "/api/perf/samples"
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
export function handlePerfSamples(
|
|
360
|
+
req: IncomingMessage,
|
|
361
|
+
res: ServerResponse,
|
|
362
|
+
ctx: RouteContext,
|
|
363
|
+
): boolean {
|
|
364
|
+
if (!req.url?.startsWith("/api/perf/samples")) return false;
|
|
365
|
+
|
|
366
|
+
const { stateDir } = ctx;
|
|
367
|
+
|
|
368
|
+
// /api/perf/samples — dashboard Perf tab chart drill-down. GET returns raw
|
|
369
|
+
// perf_samples for a single kind (?kind=<k>&minutes=<n>) ascending by ts so
|
|
370
|
+
// the client can render a time-series for any perf metric (turn_latency_ms,
|
|
371
|
+
// tps, disk_write_ms, cache_hit_pct, db_recompute_ms, etc.). `kind` is
|
|
372
|
+
// required and validated against the PERF_KINDS allow-list (400 on unknown);
|
|
373
|
+
// `minutes` defaults to 60 and is capped at 1440 (24h). Reads perf_samples
|
|
374
|
+
// via the same require()'d sqlite helper as /api/perf — no new SQLite code.
|
|
375
|
+
// Non-GET -> 405. PREVENT-PI-004: loopback.
|
|
376
|
+
const psReq = createRequire(import.meta.url);
|
|
377
|
+
const { readPerfSamples, PERF_KINDS } = psReq(
|
|
378
|
+
"../../src/store/sqlite.js",
|
|
379
|
+
) as typeof import("../../src/store/sqlite.js");
|
|
380
|
+
if (req.method !== "GET") {
|
|
381
|
+
res.writeHead(405, { "Content-Type": "application/json" }); // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
382
|
+
res.end(JSON.stringify({ error: "method_not_allowed" }));
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
const url = new URL(req.url, "http://x"); // guardrails-allow PREVENT-PI-004: localhost dashboard URL base (loopback-only)
|
|
387
|
+
const kindParam = url.searchParams.get("kind") ?? "";
|
|
388
|
+
if (!(PERF_KINDS as readonly string[]).includes(kindParam)) {
|
|
389
|
+
res.writeHead(400, { "Content-Type": "application/json" }); // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
390
|
+
res.end(JSON.stringify({ error: "unknown_kind", kind: kindParam }));
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
let minutes = Number(url.searchParams.get("minutes") ?? "60");
|
|
394
|
+
if (!Number.isFinite(minutes) || minutes <= 0) minutes = 60;
|
|
395
|
+
minutes = Math.min(minutes, 1440); // cap at 24h
|
|
396
|
+
const sinceTs = Date.now() - minutes * 60_000;
|
|
397
|
+
const kind = kindParam as PerfKind; // validated against PERF_KINDS above
|
|
398
|
+
const rows = readPerfSamples(stateDir, sinceTs, kind); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
|
|
399
|
+
const samples = rows.map((r) => ({
|
|
400
|
+
ts: r.ts,
|
|
401
|
+
value: r.value,
|
|
402
|
+
...((r.meta != null && typeof r.meta === "object"
|
|
403
|
+
? { meta: JSON.stringify(r.meta) }
|
|
404
|
+
: r.meta != null
|
|
405
|
+
? { meta: String(r.meta) }
|
|
406
|
+
: {}) as { meta?: string }),
|
|
407
|
+
}));
|
|
408
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
409
|
+
res.end(
|
|
410
|
+
JSON.stringify({
|
|
411
|
+
samples,
|
|
412
|
+
kind: kindParam,
|
|
413
|
+
minutes,
|
|
414
|
+
updatedAt: Date.now(),
|
|
415
|
+
}),
|
|
416
|
+
);
|
|
417
|
+
} catch (e) {
|
|
418
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
419
|
+
res.end(
|
|
420
|
+
JSON.stringify({ error: "perf_samples_unavailable", detail: String(e) }),
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
354
426
|
// ---------------------------------------------------------------------------
|
|
355
427
|
// handleAchievements — "/api/achievements"
|
|
356
428
|
// ---------------------------------------------------------------------------
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
handleGameState,
|
|
34
34
|
handleGameScores,
|
|
35
35
|
handlePerf,
|
|
36
|
+
handlePerfSamples,
|
|
36
37
|
handleAchievements,
|
|
37
38
|
handleSessions,
|
|
38
39
|
handleTopics,
|
|
@@ -247,6 +248,7 @@ export async function launchDashboardServer(
|
|
|
247
248
|
if (handleEvents(req, res, ctx)) return;
|
|
248
249
|
if (handleGameState(req, res, ctx)) return;
|
|
249
250
|
if (handleGameScores(req, res, ctx)) return;
|
|
251
|
+
if (handlePerfSamples(req, res, ctx)) return;
|
|
250
252
|
if (handlePerf(req, res, ctx)) return;
|
|
251
253
|
if (handleAchievements(req, res, ctx)) return;
|
|
252
254
|
if (handleSessions(req, res, ctx)) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|