pi-mega-compact 0.15.0 → 0.15.2
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/resolve-snapshot.js +20 -8
- package/dist/src/store/sqlite/global-sessions.js +40 -7
- package/extensions/dashboard-client/dist/assets/{AreaChart-rJdaOUlj.js → AreaChart-D0hDLKC1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-rJdaOUlj.js.map → AreaChart-D0hDLKC1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-DOfc8me9.js → BarChart-DSbTWZTQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-DOfc8me9.js.map → BarChart-DSbTWZTQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-EhqkEglo.js → CacheTab-Bgt2Wuar.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-EhqkEglo.js.map → CacheTab-Bgt2Wuar.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-BUACwWXf.js → EventsTab-CBb7O-6M.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-BUACwWXf.js.map → EventsTab-CBb7O-6M.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-CDEYVRBb.js → HealthTab-BFMHwnlg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-CDEYVRBb.js.map → HealthTab-BFMHwnlg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DtanIeCQ.js → MaintenanceTab-DxIQyeh-.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DtanIeCQ.js.map → MaintenanceTab-DxIQyeh-.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BGEhL7OG.js → MemoryMapTab-C576-nmM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BGEhL7OG.js.map → MemoryMapTab-C576-nmM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cktozn4s.js → MetricsTab-vk8Nqg7k.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cktozn4s.js.map → MetricsTab-vk8Nqg7k.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-BVaddBS4.js +16 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-BVaddBS4.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ReposTab-Bg72Ozdc.js → ReposTab-CLCd-Gfd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-Bg72Ozdc.js.map → ReposTab-CLCd-Gfd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BcTK9Lg6.js → SessionsTab-CV_qDKZB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BcTK9Lg6.js.map → SessionsTab-CV_qDKZB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-Bt-Ru1xp.js → SetupTab-BuIwk7LZ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-Bt-Ru1xp.js.map → SetupTab-BuIwk7LZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-ic_LjJ79.js → TimeSavedCard-BS5-w4mP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-ic_LjJ79.js.map → TimeSavedCard-BS5-w4mP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-D9AZTMgG.js → TurnsTab-DhgjZmEQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-D9AZTMgG.js.map → TurnsTab-DhgjZmEQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-hRcJPi_m.js → WikiTab-CmOAaPnM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-hRcJPi_m.js.map → WikiTab-CmOAaPnM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-BdAtIbVu.js → button-DyV48WDX.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-BdAtIbVu.js.map → button-DyV48WDX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-BFYc_QrK.js → card-CfCQ36pG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-BFYc_QrK.js.map → card-CfCQ36pG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BwzyqytT.js → generateCategoricalChart-CGlLggNu.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BwzyqytT.js.map → generateCategoricalChart-CGlLggNu.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-pmtEr3sb.js → index-tPP7N38D.js} +28 -28
- package/extensions/dashboard-client/dist/assets/index-tPP7N38D.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-DEyGlFwJ.js → switch-7461qYip.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-DEyGlFwJ.js.map → switch-7461qYip.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-pDMBQYQj.js → toggle-CHyUS1MH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-pDMBQYQj.js.map → toggle-CHyUS1MH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BKaC4dao.js → useSSE-CPIU9hax.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BKaC4dao.js.map → useSSE-CPIU9hax.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/package-lock.json +0 -39
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +43 -19
- package/extensions/dashboard-client/src/hooks/useApi.ts +38 -5
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +32 -16
- package/extensions/dashboard-server/resolve-snapshot.ts +22 -10
- package/package.json +1 -1
- package/src/store/sqlite/global-sessions.ts +205 -175
- package/extensions/dashboard-client/dist/assets/OverviewTab-08v6Ayuf.js +0 -16
- package/extensions/dashboard-client/dist/assets/OverviewTab-08v6Ayuf.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-pmtEr3sb.js.map +0 -1
|
@@ -19,7 +19,12 @@
|
|
|
19
19
|
|
|
20
20
|
import type React from "react";
|
|
21
21
|
import type { ActiveSession, SessionsResponse } from "@contracts";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
Card,
|
|
24
|
+
CardContent,
|
|
25
|
+
CardHeader,
|
|
26
|
+
CardTitle,
|
|
27
|
+
} from "../components/ui/card";
|
|
23
28
|
|
|
24
29
|
export interface SessionContextGaugesProps {
|
|
25
30
|
/** Active sessions from /api/sessions (may be null while loading). */
|
|
@@ -107,7 +112,11 @@ function SessionGauge({ data }: { data: GaugeData }): React.ReactElement {
|
|
|
107
112
|
<div className="gauge-fill" style={{ width: `${fillWidth}%` }} />
|
|
108
113
|
</div>
|
|
109
114
|
<p className="gauge-label session-gauge-sub">{sublabel}</p>
|
|
110
|
-
{data.sublabel &&
|
|
115
|
+
{data.sublabel && (
|
|
116
|
+
<p className="session-gauge-meta truncate" title={data.sublabel}>
|
|
117
|
+
{data.sublabel}
|
|
118
|
+
</p>
|
|
119
|
+
)}
|
|
111
120
|
</div>
|
|
112
121
|
);
|
|
113
122
|
}
|
|
@@ -135,11 +144,26 @@ export function SessionContextGauges({
|
|
|
135
144
|
];
|
|
136
145
|
|
|
137
146
|
// Other active sessions (exclude the launcher by sessionId to avoid dup).
|
|
138
|
-
|
|
147
|
+
// Also dedup BY PID: the heartbeat schema PK is (pid, session_id), so a
|
|
148
|
+
// process that restarted into a new session_id leaves a stale
|
|
149
|
+
// (pid, old_session_id) row. That stale row has a different sessionId so
|
|
150
|
+
// the launcher filter above doesn't catch it, and it surfaces as a
|
|
151
|
+
// phantom 0% box (null tokens). Keep only the most-recently-seen row per
|
|
152
|
+
// pid (rows are sorted by lastSeen DESC below before the loop).
|
|
153
|
+
const othersRaw: ActiveSession[] = (sessions?.sessions ?? []).filter(
|
|
139
154
|
(s) => s.sessionId !== launcherSessionId,
|
|
140
155
|
);
|
|
141
|
-
|
|
142
|
-
|
|
156
|
+
othersRaw.sort((a, b) => b.lastSeen - a.lastSeen);
|
|
157
|
+
const seenPid = new Set<number>();
|
|
158
|
+
const others: ActiveSession[] = othersRaw.filter((s) => {
|
|
159
|
+
// Skip stale rows with no token sample — they have nothing to gauge
|
|
160
|
+
// and are always either a fresh session (briefly) or a dead lingering
|
|
161
|
+
// row. Either way a 0% / "?" gauge is noise.
|
|
162
|
+
if (s.tokens == null && s.percent == null) return false;
|
|
163
|
+
if (seenPid.has(s.pid)) return false;
|
|
164
|
+
seenPid.add(s.pid);
|
|
165
|
+
return true;
|
|
166
|
+
});
|
|
143
167
|
for (const s of others) {
|
|
144
168
|
gauges.push({
|
|
145
169
|
key: s.sessionId,
|
|
@@ -163,21 +187,21 @@ export function SessionContextGauges({
|
|
|
163
187
|
<CardTitle>Context per Session (live)</CardTitle>
|
|
164
188
|
</CardHeader>
|
|
165
189
|
<CardContent>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
</div>
|
|
170
|
-
) : showLoading ? (
|
|
171
|
-
<div className="text-sm text-muted-foreground">Loading sessions…</div>
|
|
172
|
-
) : (
|
|
173
|
-
<div className="session-gauges-scroll">
|
|
174
|
-
<div className="session-gauges-grid">
|
|
175
|
-
{gauges.map((g) => (
|
|
176
|
-
<SessionGauge key={g.key} data={g} />
|
|
177
|
-
))}
|
|
190
|
+
{hasError ? (
|
|
191
|
+
<div className="text-sm text-muted-foreground">
|
|
192
|
+
Error loading sessions: {error!.message}
|
|
178
193
|
</div>
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
) : showLoading ? (
|
|
195
|
+
<div className="text-sm text-muted-foreground">Loading sessions…</div>
|
|
196
|
+
) : (
|
|
197
|
+
<div className="session-gauges-scroll">
|
|
198
|
+
<div className="session-gauges-grid">
|
|
199
|
+
{gauges.map((g) => (
|
|
200
|
+
<SessionGauge key={g.key} data={g} />
|
|
201
|
+
))}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
181
205
|
</CardContent>
|
|
182
206
|
</Card>
|
|
183
207
|
);
|
|
@@ -24,15 +24,45 @@ export interface UseApiOptions {
|
|
|
24
24
|
retryBaseMs?: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// SPRINT-D1
|
|
28
|
-
//
|
|
27
|
+
// SPRINT-D1: retry with exponential backoff on fetch failure, so a
|
|
28
|
+
// transient network blip (dashboard server still starting, ECONNRESET, a
|
|
29
|
+
// single malformed response) auto-recovers instead of surfacing a hard
|
|
30
|
+
// error screen that forces a manual page refresh. Crosses any fetch this
|
|
31
|
+
// hook backs (snapshot, sessions, ...).
|
|
32
|
+
function sleep(ms: number): Promise<void> {
|
|
33
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Fetch with bounded exponential backoff retry. The retry schedule is
|
|
37
|
+
* {0, retryBaseMs, retryBaseMs*2, retryBaseMs*4, ...} capped at retryBaseMs*8,
|
|
38
|
+
* so maxRetries=3 yields ~0ms + 500ms + 1000ms + 2000ms before giving up. */
|
|
39
|
+
async function fetchWithRetry<T>(
|
|
40
|
+
fetchFn: () => Promise<T>,
|
|
41
|
+
maxRetries: number,
|
|
42
|
+
retryBaseMs: number,
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
let lastErr: unknown;
|
|
45
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
46
|
+
try {
|
|
47
|
+
return await fetchFn();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
lastErr = err;
|
|
50
|
+
if (attempt === maxRetries) break;
|
|
51
|
+
// Exponential backoff capped at 8x base to avoid long stalls.
|
|
52
|
+
const delay = Math.min(retryBaseMs * 2 ** attempt, retryBaseMs * 8);
|
|
53
|
+
await sleep(delay);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
57
|
+
}
|
|
58
|
+
|
|
29
59
|
// SPRINT-T1-REMAINING: add Authorization header when auth token present.
|
|
30
60
|
|
|
31
61
|
export function useApi<T>(
|
|
32
62
|
fetchFn: () => Promise<T>,
|
|
33
63
|
options: UseApiOptions = {},
|
|
34
64
|
): UseApiResult<T> {
|
|
35
|
-
const { pollInterval = 0 } = options;
|
|
65
|
+
const { pollInterval = 0, maxRetries = 3, retryBaseMs = 500 } = options;
|
|
36
66
|
const [data, setData] = useState<T | null>(null);
|
|
37
67
|
const [error, setError] = useState<Error | null>(null);
|
|
38
68
|
const [loading, setLoading] = useState(true);
|
|
@@ -43,7 +73,10 @@ export function useApi<T>(
|
|
|
43
73
|
setLoading(true);
|
|
44
74
|
setError(null);
|
|
45
75
|
try {
|
|
46
|
-
|
|
76
|
+
// Retry transient failures with exponential backoff so a single
|
|
77
|
+
// network blip on page load auto-recovers instead of showing a
|
|
78
|
+
// hard error that requires a manual refresh.
|
|
79
|
+
const result = await fetchWithRetry(fetchFn, maxRetries, retryBaseMs);
|
|
47
80
|
if (mountedRef.current) {
|
|
48
81
|
setData(result);
|
|
49
82
|
setLastFetchedAt(Date.now());
|
|
@@ -57,7 +90,7 @@ export function useApi<T>(
|
|
|
57
90
|
setLoading(false);
|
|
58
91
|
}
|
|
59
92
|
}
|
|
60
|
-
}, [fetchFn]);
|
|
93
|
+
}, [fetchFn, maxRetries, retryBaseMs]);
|
|
61
94
|
|
|
62
95
|
useEffect(() => {
|
|
63
96
|
mountedRef.current = true;
|
|
@@ -124,7 +124,9 @@ export default function OverviewTab({
|
|
|
124
124
|
// which widget's modal is open (null = none)
|
|
125
125
|
const [openWidget, setOpenWidget] = useState<string | null>(null);
|
|
126
126
|
// perf samples fetched for the opened perf-backed widget
|
|
127
|
-
const [perfData, setPerfData] = useState<
|
|
127
|
+
const [perfData, setPerfData] = useState<
|
|
128
|
+
Array<{ ts: number; value: number }>
|
|
129
|
+
>([]);
|
|
128
130
|
const [perfLoading, setPerfLoading] = useState(false);
|
|
129
131
|
const [perfError, setPerfError] = useState<Error | null>(null);
|
|
130
132
|
|
|
@@ -148,9 +150,7 @@ export default function OverviewTab({
|
|
|
148
150
|
})
|
|
149
151
|
.catch((err) => {
|
|
150
152
|
if (!cancelled) {
|
|
151
|
-
setPerfError(
|
|
152
|
-
err instanceof Error ? err : new Error(String(err)),
|
|
153
|
-
);
|
|
153
|
+
setPerfError(err instanceof Error ? err : new Error(String(err)));
|
|
154
154
|
}
|
|
155
155
|
})
|
|
156
156
|
.finally(() => {
|
|
@@ -177,7 +177,15 @@ export default function OverviewTab({
|
|
|
177
177
|
if (loading && !snapshot)
|
|
178
178
|
return <div className="tab-stub">Loading snapshot…</div>;
|
|
179
179
|
if (error && !snapshot)
|
|
180
|
-
return
|
|
180
|
+
return (
|
|
181
|
+
<div className="tab-stub">
|
|
182
|
+
<p>Error loading snapshot: {error.message}</p>
|
|
183
|
+
<p className="text-xs text-muted-foreground mt-2">
|
|
184
|
+
Auto-retried with backoff and will re-poll every 5s — no action
|
|
185
|
+
needed.
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
181
189
|
if (!snapshot) return <div className="tab-stub">No snapshot data.</div>;
|
|
182
190
|
|
|
183
191
|
// Cast to RuntimeSnapshot for tierPct (runtime-only field).
|
|
@@ -389,15 +397,15 @@ export default function OverviewTab({
|
|
|
389
397
|
onClose={() => setOpenWidget(null)}
|
|
390
398
|
>
|
|
391
399
|
{perfLoading ? (
|
|
392
|
-
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
400
|
+
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
401
|
+
Loading…
|
|
402
|
+
</p>
|
|
393
403
|
) : perfError ? (
|
|
394
|
-
<p className="py-8 text-center text-sm text-red-400">
|
|
404
|
+
<p className="py-8 text-center text-sm text-red-400">
|
|
405
|
+
{perfError.message}
|
|
406
|
+
</p>
|
|
395
407
|
) : (
|
|
396
|
-
<PerfLineChart
|
|
397
|
-
data={perfData}
|
|
398
|
-
label="Cache hit %"
|
|
399
|
-
color="#58a6ff"
|
|
400
|
-
/>
|
|
408
|
+
<PerfLineChart data={perfData} label="Cache hit %" color="#58a6ff" />
|
|
401
409
|
)}
|
|
402
410
|
</WidgetDetailModal>
|
|
403
411
|
|
|
@@ -407,9 +415,13 @@ export default function OverviewTab({
|
|
|
407
415
|
onClose={() => setOpenWidget(null)}
|
|
408
416
|
>
|
|
409
417
|
{perfLoading ? (
|
|
410
|
-
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
418
|
+
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
419
|
+
Loading…
|
|
420
|
+
</p>
|
|
411
421
|
) : perfError ? (
|
|
412
|
-
<p className="py-8 text-center text-sm text-red-400">
|
|
422
|
+
<p className="py-8 text-center text-sm text-red-400">
|
|
423
|
+
{perfError.message}
|
|
424
|
+
</p>
|
|
413
425
|
) : (
|
|
414
426
|
<PerfLineChart
|
|
415
427
|
data={perfData}
|
|
@@ -425,9 +437,13 @@ export default function OverviewTab({
|
|
|
425
437
|
onClose={() => setOpenWidget(null)}
|
|
426
438
|
>
|
|
427
439
|
{perfLoading ? (
|
|
428
|
-
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
440
|
+
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
441
|
+
Loading…
|
|
442
|
+
</p>
|
|
429
443
|
) : perfError ? (
|
|
430
|
-
<p className="py-8 text-center text-sm text-red-400">
|
|
444
|
+
<p className="py-8 text-center text-sm text-red-400">
|
|
445
|
+
{perfError.message}
|
|
446
|
+
</p>
|
|
431
447
|
) : (
|
|
432
448
|
<PerfLineChart
|
|
433
449
|
data={perfData}
|
|
@@ -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
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
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",
|