pi-mega-compact 0.11.1 → 0.11.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/api-contracts/endpoints.js +19 -0
- package/dist/extensions/dashboard-server/api-contracts/maintenance.js +7 -0
- package/dist/extensions/dashboard-server/api-contracts.test.js +1 -1
- package/dist/extensions/dashboard-server/routes-maintenance.js +293 -0
- package/dist/extensions/dashboard-server/routes.js +1 -0
- package/dist/extensions/dashboard-server/server.js +3 -1
- package/dist/src/store/sqlite/maintenance.js +20 -1
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DZp_xQOW.js → AchievementTiles-Cml6YC8w.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DZp_xQOW.js.map → AchievementTiles-Cml6YC8w.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-BzFHpAHM.js → AchievementsTab-B8NqDt7I.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-BzFHpAHM.js.map → AchievementsTab-B8NqDt7I.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-DEQbTq2T.js → CacheTab-BCLzVL4S.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-DEQbTq2T.js.map → CacheTab-BCLzVL4S.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DQ4FG33J.js → ConfigTab-CBYvp2mV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DQ4FG33J.js.map → ConfigTab-CBYvp2mV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-D5HoEVYo.js → EventsTab-DlTJqiDm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-D5HoEVYo.js.map → EventsTab-DlTJqiDm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-Jzsc4yeY.js → GameTab-DkGTqKJW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-Jzsc4yeY.js.map → GameTab-DkGTqKJW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Cw43zmUx.js +2 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Cw43zmUx.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-D_XnsEYX.js → MetricsTab-BFo7pKM-.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-D_XnsEYX.js.map → MetricsTab-BFo7pKM-.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BMREvAth.js → OverviewTab-CClCUd3-.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BMREvAth.js.map → OverviewTab-CClCUd3-.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DuyhOktG.js → ReposTab-_3R4RSLZ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DuyhOktG.js.map → ReposTab-_3R4RSLZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-mz_vz8i2.js → SessionsTab-CCYd-kSz.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-mz_vz8i2.js.map → SessionsTab-CCYd-kSz.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B8AIjNTY.js → TimeSavedCard-0TqsrY6y.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B8AIjNTY.js.map → TimeSavedCard-0TqsrY6y.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TopicsTab-DKuct4oD.js → TopicsTab-DRLJmVv0.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-DKuct4oD.js.map → TopicsTab-DRLJmVv0.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-11MGLxY8.js → TurnsTab-CLNM0T5R.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-11MGLxY8.js.map → TurnsTab-CLNM0T5R.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-CqLN7STl.js → index-BcoSz-L4.js} +10 -10
- package/extensions/dashboard-client/dist/assets/{index-CqLN7STl.js.map → index-BcoSz-L4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-CXrQSbVY.js → useSSE-Cezlih5V.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-CXrQSbVY.js.map → useSSE-Cezlih5V.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/App.tsx +5 -1
- package/extensions/dashboard-client/src/api/client.ts +23 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +293 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +30 -0
- package/extensions/dashboard-server/api-contracts/index.ts +12 -0
- package/extensions/dashboard-server/api-contracts/maintenance.ts +84 -0
- package/extensions/dashboard-server/api-contracts.test.ts +1 -1
- package/extensions/dashboard-server/api-contracts.ts +9 -0
- package/extensions/dashboard-server/routes-maintenance.ts +345 -0
- package/extensions/dashboard-server/routes.ts +1 -0
- package/extensions/dashboard-server/server.ts +2 -0
- package/package.json +1 -1
- package/src/store/sqlite/maintenance.ts +20 -1
|
@@ -169,4 +169,23 @@ export const ENDPOINTS = {
|
|
|
169
169
|
path: "/api/topics/:topicId/memories",
|
|
170
170
|
description: "Member memories assigned to a wiki topic (drill-down).",
|
|
171
171
|
},
|
|
172
|
+
// ─── S49B Maintenance Tab ─────────────────────────────────────────
|
|
173
|
+
/** GET /api/maintenance — DB stats (table row counts + storage). */
|
|
174
|
+
maintenanceStats: {
|
|
175
|
+
method: "GET",
|
|
176
|
+
path: "/api/maintenance",
|
|
177
|
+
description: "SQLite table row counts, storage stats, and DB file sizes.",
|
|
178
|
+
},
|
|
179
|
+
/** GET /api/maintenance/schema-health — Schema version + integrity audit. */
|
|
180
|
+
schemaHealth: {
|
|
181
|
+
method: "GET",
|
|
182
|
+
path: "/api/maintenance/schema-health",
|
|
183
|
+
description: "SCHEMA_VERSION, PRAGMA integrity_check, FK check, and per-column audit.",
|
|
184
|
+
},
|
|
185
|
+
/** POST /api/maintenance/action — Trigger a maintenance action. */
|
|
186
|
+
maintenanceAction: {
|
|
187
|
+
method: "POST",
|
|
188
|
+
path: "/api/maintenance/action",
|
|
189
|
+
description: "Run vacuum, checkpoint, reindex, fts5-rebuild, reconcile-dedup, prune, or integrity-check.",
|
|
190
|
+
},
|
|
172
191
|
};
|
|
@@ -76,7 +76,7 @@ void _c_achievements;
|
|
|
76
76
|
// Verify the ENDPOINTS registry has exactly 15 entries with correct paths.
|
|
77
77
|
// (Sprint 39 added /api/sessions + /api/sessions/timeseries.)
|
|
78
78
|
const ENDPOINT_KEYS = Object.keys(ENDPOINTS);
|
|
79
|
-
const EXPECTED_ENDPOINT_COUNT = 23
|
|
79
|
+
const EXPECTED_ENDPOINT_COUNT = 26; // 23 base + 3 S49B maintenance
|
|
80
80
|
/** All `/api/*` paths served by server.ts (extracted from the route handlers). */
|
|
81
81
|
const SERVER_TS_PATHS = [
|
|
82
82
|
"/api/snapshot",
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-maintenance.ts — S49B Maintenance tab API routes.
|
|
3
|
+
*
|
|
4
|
+
* Handlers for:
|
|
5
|
+
* GET /api/maintenance — DB stats (table row counts + storage)
|
|
6
|
+
* GET /api/maintenance/schema-health — SCHEMA_VERSION + integrity audit
|
|
7
|
+
* POST /api/maintenance/action — Trigger a maintenance action
|
|
8
|
+
*
|
|
9
|
+
* All pi-agnostic; lazy-requires maintenance primitives to avoid a top-level
|
|
10
|
+
* import of the store from the dashboard server process. PREVENT-PI-004: local
|
|
11
|
+
* SQLite only; zero network calls.
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Lazy imports (via createRequire)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const lazyMaintenance = (() => {
|
|
18
|
+
let mod = null;
|
|
19
|
+
return () => {
|
|
20
|
+
if (!mod)
|
|
21
|
+
mod = createRequire(import.meta.url)("../../src/store/sqlite/maintenance.js");
|
|
22
|
+
return mod;
|
|
23
|
+
};
|
|
24
|
+
})();
|
|
25
|
+
const lazyStoreUtils = (() => {
|
|
26
|
+
let mod = null;
|
|
27
|
+
return () => {
|
|
28
|
+
if (!mod)
|
|
29
|
+
mod = createRequire(import.meta.url)("../../src/store/sqlite/utils.js");
|
|
30
|
+
return mod;
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// GET /api/maintenance and sub-paths
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
export function handleMaintenance(req, res, ctx) {
|
|
37
|
+
const url = req.url ?? "";
|
|
38
|
+
if (!url.startsWith("/api/maintenance"))
|
|
39
|
+
return false;
|
|
40
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
41
|
+
// ── GET /api/maintenance/schema-health ────────────────────────────
|
|
42
|
+
if (method === "GET" && url === "/api/maintenance/schema-health") {
|
|
43
|
+
sendSchemaHealth(res, ctx);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
// ── POST /api/maintenance/action ──────────────────────────────────
|
|
47
|
+
if (method === "POST" && url === "/api/maintenance/action") {
|
|
48
|
+
handleMaintenanceAction(req, res, ctx);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// ── GET /api/maintenance ──────────────────────────────────────────
|
|
52
|
+
if (method === "GET" && url === "/api/maintenance") {
|
|
53
|
+
sendDbStats(res, ctx);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// ── Unknown sub-path ──────────────────────────────────────────────
|
|
57
|
+
sendJson(res, 404, { error: "not found" });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// GET /api/maintenance — DB stats
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
function sendDbStats(res, ctx) {
|
|
64
|
+
try {
|
|
65
|
+
const { getDbStats } = lazyMaintenance();
|
|
66
|
+
const raw = getDbStats(ctx.stateDir);
|
|
67
|
+
const tables = Object.entries(raw.tableCounts).map(([table, rowCount]) => ({
|
|
68
|
+
table,
|
|
69
|
+
rowCount,
|
|
70
|
+
}));
|
|
71
|
+
sendJson(res, 200, {
|
|
72
|
+
type: "db-stats",
|
|
73
|
+
tables,
|
|
74
|
+
storage: {
|
|
75
|
+
files: {
|
|
76
|
+
dbBytes: raw.dbBytes,
|
|
77
|
+
walBytes: raw.walBytes,
|
|
78
|
+
shmBytes: raw.shmBytes,
|
|
79
|
+
},
|
|
80
|
+
pageSize: raw.pageSize,
|
|
81
|
+
pageCount: raw.pageCount,
|
|
82
|
+
freelistPages: raw.freelistPages,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
sendJson(res, 500, { error: err.message });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// GET /api/maintenance/schema-health — schema version + integrity audit
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
function sendSchemaHealth(res, ctx) {
|
|
94
|
+
try {
|
|
95
|
+
const { integrityCheck } = lazyMaintenance();
|
|
96
|
+
const integrity = integrityCheck(ctx.stateDir);
|
|
97
|
+
const { openStore } = lazyStoreUtils();
|
|
98
|
+
const db = openStore(ctx.stateDir);
|
|
99
|
+
// SCHEMA_VERSION from meta table
|
|
100
|
+
const metaRow = db
|
|
101
|
+
.prepare("SELECT value FROM meta WHERE key = ?")
|
|
102
|
+
.get("schema_version");
|
|
103
|
+
const schemaVersion = metaRow ? Number(metaRow.value) : 0;
|
|
104
|
+
// PRAGMA foreign_key_check
|
|
105
|
+
const fkRows = db
|
|
106
|
+
.prepare("PRAGMA foreign_key_check")
|
|
107
|
+
.all();
|
|
108
|
+
const fkCheck = fkRows.map((r) => JSON.stringify(r));
|
|
109
|
+
// Per-column audit: every ensureColumn migration column declared in
|
|
110
|
+
// schema.ts, checked via PRAGMA table_info (read-only).
|
|
111
|
+
const expectedColumns = [
|
|
112
|
+
{ table: "context_chunks", column: "original_token_estimate", decl: "INTEGER" },
|
|
113
|
+
{ table: "raw_transcript", column: "content_ref", decl: "TEXT" },
|
|
114
|
+
{ table: "memories", column: "category", decl: "TEXT" },
|
|
115
|
+
{ table: "memories", column: "target", decl: "TEXT" },
|
|
116
|
+
{ table: "memories", column: "last_referenced", decl: "INTEGER" },
|
|
117
|
+
{ table: "memories", column: "source_turn", decl: "INTEGER" },
|
|
118
|
+
{ table: "raptor_nodes", column: "built_at", decl: "INTEGER" },
|
|
119
|
+
{ table: "raw_transcript", column: "turn_index", decl: "INTEGER" },
|
|
120
|
+
{ table: "session_state", column: "conversation_id", decl: "TEXT" },
|
|
121
|
+
{ table: "session_state", column: "last_turn_id", decl: "INTEGER" },
|
|
122
|
+
];
|
|
123
|
+
const columns = expectedColumns.map((ec) => {
|
|
124
|
+
const cols = db
|
|
125
|
+
.prepare(`PRAGMA table_info(${ec.table})`)
|
|
126
|
+
.all();
|
|
127
|
+
const present = cols.some((c) => c.name === ec.column);
|
|
128
|
+
return {
|
|
129
|
+
table: ec.table,
|
|
130
|
+
column: ec.column,
|
|
131
|
+
present,
|
|
132
|
+
expectedDecl: ec.decl,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
const healthy = integrity.length === 1 &&
|
|
136
|
+
integrity[0] === "ok" &&
|
|
137
|
+
columns.every((c) => c.present);
|
|
138
|
+
sendJson(res, 200, {
|
|
139
|
+
type: "schema-health",
|
|
140
|
+
schemaVersion,
|
|
141
|
+
integrity,
|
|
142
|
+
fkCheck,
|
|
143
|
+
columns,
|
|
144
|
+
healthy,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
sendJson(res, 500, { error: err.message });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// POST /api/maintenance/action — Trigger a maintenance action
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
function handleMaintenanceAction(req, res, ctx) {
|
|
155
|
+
let body = "";
|
|
156
|
+
req.setEncoding("utf8");
|
|
157
|
+
req.on("data", (chunk) => {
|
|
158
|
+
body += chunk;
|
|
159
|
+
if (body.length > 10_000)
|
|
160
|
+
req.destroy();
|
|
161
|
+
});
|
|
162
|
+
req.on("end", () => {
|
|
163
|
+
let payload;
|
|
164
|
+
try {
|
|
165
|
+
payload = JSON.parse(body);
|
|
166
|
+
if (!payload || !payload.action) {
|
|
167
|
+
sendJson(res, 400, { error: "missing 'action' field" });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
sendJson(res, 400, { error: "invalid JSON" });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const m = lazyMaintenance();
|
|
177
|
+
const u = lazyStoreUtils();
|
|
178
|
+
// All destructive actions: temp backup first
|
|
179
|
+
if (payload.action !== "integrity-check") {
|
|
180
|
+
// Non-blocking — backup failure is non-fatal
|
|
181
|
+
m.backupDb(ctx.stateDir);
|
|
182
|
+
}
|
|
183
|
+
switch (payload.action) {
|
|
184
|
+
case "vacuum": {
|
|
185
|
+
const r = m.vacuumDb(ctx.stateDir);
|
|
186
|
+
sendJson(res, 200, {
|
|
187
|
+
operation: "vacuum",
|
|
188
|
+
success: true,
|
|
189
|
+
affected: r.affected,
|
|
190
|
+
reclaimedBytes: r.reclaimedBytes,
|
|
191
|
+
summary: r.summary,
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
case "checkpoint": {
|
|
196
|
+
const r = m.checkpointWal(ctx.stateDir);
|
|
197
|
+
sendJson(res, 200, {
|
|
198
|
+
operation: "checkpoint",
|
|
199
|
+
success: true,
|
|
200
|
+
affected: r.affected,
|
|
201
|
+
reclaimedBytes: r.reclaimedBytes,
|
|
202
|
+
summary: r.summary,
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
case "reindex": {
|
|
207
|
+
const db = u.openStore(ctx.stateDir);
|
|
208
|
+
db.exec("REINDEX");
|
|
209
|
+
sendJson(res, 200, {
|
|
210
|
+
operation: "reindex",
|
|
211
|
+
success: true,
|
|
212
|
+
affected: 0,
|
|
213
|
+
reclaimedBytes: 0,
|
|
214
|
+
summary: "REINDEX completed",
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
case "fts5-rebuild": {
|
|
219
|
+
const db = u.openStore(ctx.stateDir);
|
|
220
|
+
db.exec("INSERT INTO context_chunks_trgm(context_chunks_trgm) VALUES('rebuild')");
|
|
221
|
+
sendJson(res, 200, {
|
|
222
|
+
operation: "fts5-rebuild",
|
|
223
|
+
success: true,
|
|
224
|
+
affected: 0,
|
|
225
|
+
reclaimedBytes: 0,
|
|
226
|
+
summary: "FTS5 rebuild triggered",
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
case "reconcile-dedup": {
|
|
231
|
+
const r = m.reconcileDedupMirror(ctx.stateDir);
|
|
232
|
+
const lines = [];
|
|
233
|
+
if (r.fixedRefCount)
|
|
234
|
+
lines.push(`${r.fixedRefCount} ref_counts corrected`);
|
|
235
|
+
if (r.orphansDeleted)
|
|
236
|
+
lines.push(`${r.orphansDeleted} orphans deleted`);
|
|
237
|
+
if (r.refsBackfilled)
|
|
238
|
+
lines.push(`${r.refsBackfilled} refs backfilled`);
|
|
239
|
+
sendJson(res, 200, {
|
|
240
|
+
operation: "reconcile-dedup",
|
|
241
|
+
success: true,
|
|
242
|
+
affected: r.fixedRefCount + r.orphansDeleted + r.refsBackfilled,
|
|
243
|
+
reclaimedBytes: 0,
|
|
244
|
+
summary: lines.length ? lines.join(", ") : "nothing to fix",
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
case "prune": {
|
|
249
|
+
const days = Number(payload.daysOld) || 30;
|
|
250
|
+
const r = m.pruneOldRows(ctx.stateDir, days);
|
|
251
|
+
sendJson(res, 200, {
|
|
252
|
+
operation: "prune",
|
|
253
|
+
success: true,
|
|
254
|
+
affected: r.affected,
|
|
255
|
+
reclaimedBytes: r.reclaimedBytes,
|
|
256
|
+
summary: r.summary,
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
case "integrity-check": {
|
|
261
|
+
const lines = m.integrityCheck(ctx.stateDir);
|
|
262
|
+
sendJson(res, 200, {
|
|
263
|
+
operation: "integrity-check",
|
|
264
|
+
success: lines.length === 1 && lines[0] === "ok",
|
|
265
|
+
affected: 0,
|
|
266
|
+
reclaimedBytes: 0,
|
|
267
|
+
summary: lines.join("; "),
|
|
268
|
+
detail: lines,
|
|
269
|
+
});
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
default:
|
|
273
|
+
sendJson(res, 400, {
|
|
274
|
+
error: `unknown action: ${payload.action}`,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
sendJson(res, 500, { error: err.message });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// JSON helper
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
function sendJson(res, status, data) {
|
|
287
|
+
const body = JSON.stringify(data);
|
|
288
|
+
res.writeHead(status, {
|
|
289
|
+
"Content-Type": "application/json",
|
|
290
|
+
"Content-Length": Buffer.byteLength(body),
|
|
291
|
+
});
|
|
292
|
+
res.end(body);
|
|
293
|
+
}
|
|
@@ -10,3 +10,4 @@ export { handleGameState, handleGameScores, handlePerf, handleAchievements, } fr
|
|
|
10
10
|
export { handleEvents, handleSessions } from "./routes-sessions.js";
|
|
11
11
|
export { handleTopics } from "./routes-topics.js";
|
|
12
12
|
export { handleTurns } from "./routes-turns.js";
|
|
13
|
+
export { handleMaintenance } from "./routes-maintenance.js";
|
|
@@ -12,7 +12,7 @@ import { join, dirname } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
14
|
import { log, setLogPath, setDashboardServerVersion } from "./state.js";
|
|
15
|
-
import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleStatic, } from "./routes.js";
|
|
15
|
+
import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleStatic, } from "./routes.js";
|
|
16
16
|
export async function launchDashboardServer(stateDir) {
|
|
17
17
|
// Our own package version — exposed at /api/version so the launcher can
|
|
18
18
|
// detect a stale server (started by an older build) and replace it on
|
|
@@ -191,6 +191,8 @@ export async function launchDashboardServer(stateDir) {
|
|
|
191
191
|
return;
|
|
192
192
|
if (handleTurns(req, res, ctx))
|
|
193
193
|
return;
|
|
194
|
+
if (handleMaintenance(req, res, ctx))
|
|
195
|
+
return;
|
|
194
196
|
handleStatic(req, res, ctx);
|
|
195
197
|
});
|
|
196
198
|
// Bind base + range are env-configurable so tests can use a private,
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* All pi-agnostic, all parameterized (PREVENT-002), all local (PREVENT-PI-004).
|
|
5
5
|
* Exposed via the /mega-db-* slash commands in extensions/mega-db-cmds.ts.
|
|
6
6
|
*/
|
|
7
|
-
import { statSync } from "node:fs";
|
|
7
|
+
import { statSync, mkdirSync, existsSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
9
10
|
import { getStateDir } from "../../store.js";
|
|
10
11
|
import { openStore, withTx } from "./utils.js";
|
|
11
12
|
const DB_TABLE_NAMES = [
|
|
@@ -70,6 +71,24 @@ export function getDbStats(stateDir = getStateDir()) {
|
|
|
70
71
|
walFrames,
|
|
71
72
|
};
|
|
72
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Lightweight temp backup of the main sqlite.db via VACUUM INTO.
|
|
76
|
+
*
|
|
77
|
+
* Creates a one-off backup in the OS tmpdir. Returns the backup path.
|
|
78
|
+
* VACUUM INTO produces a clean, defragmented copy — also serves as a
|
|
79
|
+
* pre-op safeguard for any destructive maintenance action.
|
|
80
|
+
*/
|
|
81
|
+
export function backupDb(stateDir = getStateDir()) {
|
|
82
|
+
const backupDir = join(tmpdir(), "pi-mega-compact-backups");
|
|
83
|
+
if (!existsSync(backupDir))
|
|
84
|
+
mkdirSync(backupDir, { recursive: true });
|
|
85
|
+
const ts = Date.now();
|
|
86
|
+
const backupPath = join(backupDir, `sqlite-${ts}.db`);
|
|
87
|
+
const db = openStore(stateDir);
|
|
88
|
+
// guardrails-disable-next-line PREVENT-002: path built from tmpdir + timestamp, not user input
|
|
89
|
+
db.exec(`VACUUM INTO '${backupPath.replace(/'/g, "''")}'`);
|
|
90
|
+
return backupPath;
|
|
91
|
+
}
|
|
73
92
|
/**
|
|
74
93
|
* Prune raw_transcript + checkpoint_epochs rows older than `daysOld`.
|
|
75
94
|
* Uses `message_timestamp` (raw_transcript) and `created_at` (epochs), both
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as o,j as n}from"./index-
|
|
2
|
-
//# sourceMappingURL=AchievementTiles-
|
|
1
|
+
import{r as o,j as n}from"./index-BcoSz-L4.js";function x(s){try{return new Date(s*1e3).toLocaleString()}catch{return"—"}}function f({achievements:s}){const i=o.useRef(0),[r,a]=o.useState(null),l=s.reduce((e,t)=>Math.max(e,t.unlocked_at??0),0),c=i.current;o.useEffect(()=>{l>i.current&&(i.current=l)},[l]),o.useEffect(()=>{if(c>0&&l>c){const e=s.filter(t=>t.unlocked_at!=null&&(t.unlocked_at??0)>c);if(e.length>0){const t=e.map(d=>`${d.icon??""} ${d.title}`.trim()).join(", ")+" unlocked!";a(t);const h=setTimeout(()=>a(null),4e3);return()=>clearTimeout(h)}}},[s,l,c]);const u=s.filter(e=>!(e.hidden===1&&e.unlocked_at==null));return n.jsxs("div",{className:"ach-section",children:[r!=null&&n.jsx("div",{className:"ach-toast show",children:r}),n.jsxs("div",{className:"ach-tiles",children:[u.length===0&&n.jsx("span",{className:"repo-none",children:"no achievements yet"}),u.map(e=>{if(e.unlocked_at!=null){const t=c>0&&(e.unlocked_at??0)>c;return n.jsxs("div",{className:`ach-tile unlocked${t?" just-unlocked":""}`,children:[e.icon??""," ",e.title,n.jsxs("span",{className:"ach-detail",children:["unlocked ",x(e.unlocked_at)]})]},e.id)}return n.jsxs("div",{className:"ach-tile locked",children:["??? ",e.title]},e.id)})]})]})}export{f as A};
|
|
2
|
+
//# sourceMappingURL=AchievementTiles-Cml6YC8w.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AchievementTiles-
|
|
1
|
+
{"version":3,"file":"AchievementTiles-Cml6YC8w.js","sources":["../../src/components/AchievementTiles.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/AchievementTiles.tsx — shared achievement grid.\n *\n * Renders a grid of achievement tiles from fetchAchievements() data.\n * Tile states: unlocked (icon+title+date), visible-locked (??? teaser),\n * hidden+locked (not rendered). Just-unlocked tiles get a pulse animation.\n * Toast notification fires for newly-unlocked achievements.\n *\n * Used by both GameTab (sub-section) and AchievementsTab (standalone).\n */\n\nimport type React from \"react\";\nimport { useRef, useState, useEffect } from \"react\";\nimport type { AchievementRow } from \"@contracts\";\n\nexport interface AchievementTilesProps {\n\t/** Achievement rows from /api/achievements. */\n\tachievements: AchievementRow[];\n}\n\n/** Format unlock timestamp (seconds → locale string). */\nfunction fmtUnlockDate(ts: number): string {\n\ttry {\n\t\treturn new Date(ts * 1000).toLocaleString();\n\t} catch {\n\t\treturn \"\\u2014\";\n\t}\n}\n\nexport function AchievementTiles({\n\tachievements,\n}: AchievementTilesProps): React.ReactElement {\n\tconst lastMaxTsRef = useRef(0);\n\tconst [toastText, setToastText] = useState<string | null>(null);\n\n\tconst maxTs = achievements.reduce(\n\t\t(mx, a) => Math.max(mx, a.unlocked_at ?? 0),\n\t\t0,\n\t);\n\tconst prevMaxTs = lastMaxTsRef.current;\n\n\t/* Update ref after render so next poll sees previous baseline. */\n\tuseEffect(() => {\n\t\tif (maxTs > lastMaxTsRef.current) {\n\t\t\tlastMaxTsRef.current = maxTs;\n\t\t}\n\t}, [maxTs]);\n\n\t/* Toast for newly-unlocked achievements. */\n\tuseEffect(() => {\n\t\tif (prevMaxTs > 0 && maxTs > prevMaxTs) {\n\t\t\tconst newly = achievements.filter(\n\t\t\t\t(a) => a.unlocked_at != null && (a.unlocked_at ?? 0) > prevMaxTs,\n\t\t\t);\n\t\t\tif (newly.length > 0) {\n\t\t\t\tconst text =\n\t\t\t\t\tnewly\n\t\t\t\t\t\t.map((a) => `${a.icon ?? \"\"} ${a.title}`.trim())\n\t\t\t\t\t\t.join(\", \") + \" unlocked!\";\n\t\t\t\tsetToastText(text);\n\t\t\t\tconst t = setTimeout(() => setToastText(null), 4000);\n\t\t\t\treturn (): void => clearTimeout(t);\n\t\t\t}\n\t\t}\n\t}, [achievements, maxTs, prevMaxTs]);\n\n\t/* Visible tiles: skip hidden+locked (hidden === 1 && unlocked_at == null). */\n\tconst tiles = achievements.filter(\n\t\t(a) => !(a.hidden === 1 && a.unlocked_at == null),\n\t);\n\n\treturn (\n\t\t<div className=\"ach-section\">\n\t\t\t{toastText != null && (\n\t\t\t\t<div className=\"ach-toast show\">{toastText}</div>\n\t\t\t)}\n\t\t\t<div className=\"ach-tiles\">\n\t\t\t\t{tiles.length === 0 && (\n\t\t\t\t\t<span className=\"repo-none\">no achievements yet</span>\n\t\t\t\t)}\n\t\t\t\t{tiles.map((a) => {\n\t\t\t\t\tif (a.unlocked_at != null) {\n\t\t\t\t\t\tconst isNew = prevMaxTs > 0 && (a.unlocked_at ?? 0) > prevMaxTs;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={a.id}\n\t\t\t\t\t\t\t\tclassName={`ach-tile unlocked${isNew ? \" just-unlocked\" : \"\"}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{a.icon ?? \"\"} {a.title}\n\t\t\t\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\t\t\t\tunlocked {fmtUnlockDate(a.unlocked_at)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div key={a.id} className=\"ach-tile locked\">\n\t\t\t\t\t\t\t??? {a.title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["fmtUnlockDate","ts","AchievementTiles","achievements","lastMaxTsRef","useRef","toastText","setToastText","useState","maxTs","mx","a","prevMaxTs","useEffect","newly","text","t","tiles","jsxs","jsx","isNew"],"mappings":"+CAqBA,SAASA,EAAcC,EAAoB,CAC1C,GAAI,CACH,OAAO,IAAI,KAAKA,EAAK,GAAI,EAAE,eAAA,CAC5B,MAAQ,CACP,MAAO,GACR,CACD,CAEO,SAASC,EAAiB,CAChC,aAAAC,CACD,EAA8C,CAC7C,MAAMC,EAAeC,EAAAA,OAAO,CAAC,EACvB,CAACC,EAAWC,CAAY,EAAIC,EAAAA,SAAwB,IAAI,EAExDC,EAAQN,EAAa,OAC1B,CAACO,EAAIC,IAAM,KAAK,IAAID,EAAIC,EAAE,aAAe,CAAC,EAC1C,CAAA,EAEKC,EAAYR,EAAa,QAG/BS,EAAAA,UAAU,IAAM,CACXJ,EAAQL,EAAa,UACxBA,EAAa,QAAUK,EAEzB,EAAG,CAACA,CAAK,CAAC,EAGVI,EAAAA,UAAU,IAAM,CACf,GAAID,EAAY,GAAKH,EAAQG,EAAW,CACvC,MAAME,EAAQX,EAAa,OACzBQ,GAAMA,EAAE,aAAe,OAASA,EAAE,aAAe,GAAKC,CAAA,EAExD,GAAIE,EAAM,OAAS,EAAG,CACrB,MAAMC,EACLD,EACE,IAAKH,GAAM,GAAGA,EAAE,MAAQ,EAAE,IAAIA,EAAE,KAAK,GAAG,KAAA,CAAM,EAC9C,KAAK,IAAI,EAAI,aAChBJ,EAAaQ,CAAI,EACjB,MAAMC,EAAI,WAAW,IAAMT,EAAa,IAAI,EAAG,GAAI,EACnD,MAAO,IAAY,aAAaS,CAAC,CAClC,CACD,CACD,EAAG,CAACb,EAAcM,EAAOG,CAAS,CAAC,EAGnC,MAAMK,EAAQd,EAAa,OACzBQ,GAAM,EAAEA,EAAE,SAAW,GAAKA,EAAE,aAAe,KAAA,EAG7C,OACCO,EAAAA,KAAC,MAAA,CAAI,UAAU,cACb,SAAA,CAAAZ,GAAa,MACba,EAAAA,IAAC,MAAA,CAAI,UAAU,iBAAkB,SAAAb,EAAU,EAE5CY,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAD,EAAM,SAAW,GACjBE,MAAC,OAAA,CAAK,UAAU,YAAY,SAAA,sBAAmB,EAE/CF,EAAM,IAAKN,GAAM,CACjB,GAAIA,EAAE,aAAe,KAAM,CAC1B,MAAMS,EAAQR,EAAY,IAAMD,EAAE,aAAe,GAAKC,EACtD,OACCM,EAAAA,KAAC,MAAA,CAEA,UAAW,oBAAoBE,EAAQ,iBAAmB,EAAE,GAE3D,SAAA,CAAAT,EAAE,MAAQ,GAAG,IAAEA,EAAE,MAClBO,EAAAA,KAAC,OAAA,CAAK,UAAU,aAAa,SAAA,CAAA,YAClBlB,EAAcW,EAAE,WAAW,CAAA,CAAA,CACtC,CAAA,CAAA,EANKA,EAAE,EAAA,CASV,CACA,OACCO,EAAAA,KAAC,MAAA,CAAe,UAAU,kBAAkB,SAAA,CAAA,OACtCP,EAAE,KAAA,CAAA,EADEA,EAAE,EAEZ,CAEF,CAAC,CAAA,CAAA,CACF,CAAA,EACD,CAEF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as t,h as m,j as s}from"./index-
|
|
2
|
-
//# sourceMappingURL=AchievementsTab-
|
|
1
|
+
import{r as t,h as m,j as s}from"./index-BcoSz-L4.js";import{A as v}from"./AchievementTiles-Cml6YC8w.js";function x(){const[r,o]=t.useState([]),[l,h]=t.useState(!0),[a,n]=t.useState(null),c=t.useCallback(async()=>{try{const e=await m();o(e),n(null)}catch(e){n(e instanceof Error?e:new Error(String(e)))}finally{h(!1)}},[]);return t.useEffect(()=>{let e=!0;const i=async()=>{e&&await c()};i();const u=setInterval(()=>void i(),15e3);return()=>{e=!1,clearInterval(u)}},[c]),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-B8NqDt7I.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AchievementsTab-
|
|
1
|
+
{"version":3,"file":"AchievementsTab-B8NqDt7I.js","sources":["../../src/tabs/AchievementsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/AchievementsTab.tsx — Achievements tab (NEW).\n *\n * Renders the shared AchievementTiles component standalone, plus a toast area.\n * Polls /api/achievements every 15s.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { AchievementRow } from \"@contracts\";\nimport { fetchAchievements } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\nexport default function AchievementsTab(): React.ReactElement {\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst doFetch = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst rows = await fetchAchievements();\n\t\t\tsetAchievements(rows);\n\t\t\tsetError(null);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst run = async (): Promise<void> => {\n\t\t\tif (active) await doFetch();\n\t\t};\n\t\tvoid run();\n\t\tconst timer = setInterval(() => void run(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [doFetch]);\n\n\tif (loading && achievements.length === 0)\n\t\treturn <div className=\"tab-stub\">Loading achievements…</div>;\n\tif (error && achievements.length === 0)\n\t\treturn <div className=\"tab-stub\">Error: {error.message}</div>;\n\n\treturn (\n\t\t<div className=\"achievements-tab\">\n\t\t\t<h2>Achievements</h2>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["AchievementsTab","achievements","setAchievements","useState","loading","setLoading","error","setError","doFetch","useCallback","rows","fetchAchievements","useEffect","active","run","timer","jsx","jsxs","AchievementTiles"],"mappings":"yGAaA,SAAwBA,GAAsC,CAC7D,KAAM,CAACC,EAAcC,CAAe,EAAIC,EAAAA,SAA2B,CAAA,CAAE,EAC/D,CAACC,EAASC,CAAU,EAAIF,EAAAA,SAAS,EAAI,EACrC,CAACG,EAAOC,CAAQ,EAAIJ,EAAAA,SAAuB,IAAI,EAE/CK,EAAUC,EAAAA,YAAY,SAA2B,CACtD,GAAI,CACH,MAAMC,EAAO,MAAMC,EAAA,EACnBT,EAAgBQ,CAAI,EACpBH,EAAS,IAAI,CACd,OAAS,EAAG,CACXA,EAAS,aAAa,MAAQ,EAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,CACvD,QAAA,CACCF,EAAW,EAAK,CACjB,CACD,EAAG,CAAA,CAAE,EAeL,OAbAO,EAAAA,UAAU,IAAM,CACf,IAAIC,EAAS,GACb,MAAMC,EAAM,SAA2B,CAClCD,SAAcL,EAAA,CACnB,EACKM,EAAA,EACL,MAAMC,EAAQ,YAAY,IAAM,KAAKD,EAAA,EAAO,IAAK,EACjD,MAAO,IAAM,CACZD,EAAS,GACT,cAAcE,CAAK,CACpB,CACD,EAAG,CAACP,CAAO,CAAC,EAERJ,GAAWH,EAAa,SAAW,EAC/Be,EAAAA,IAAC,MAAA,CAAI,UAAU,WAAW,SAAA,wBAAqB,EACnDV,GAASL,EAAa,SAAW,EAC7BgB,EAAAA,KAAC,MAAA,CAAI,UAAU,WAAW,SAAA,CAAA,UAAQX,EAAM,OAAA,EAAQ,EAGvDW,EAAAA,KAAC,MAAA,CAAI,UAAU,mBACd,SAAA,CAAAD,EAAAA,IAAC,MAAG,SAAA,cAAA,CAAY,EAChBA,MAACE,GAAiB,aAAAjB,CAAA,CAA4B,CAAA,EAC/C,CAEF"}
|
package/extensions/dashboard-client/dist/assets/{CacheTab-DEQbTq2T.js → CacheTab-BCLzVL4S.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{u as n,r,c as d,j as s}from"./index-
|
|
2
|
-
//# sourceMappingURL=CacheTab-
|
|
1
|
+
import{u as n,r,c as d,j as s}from"./index-BcoSz-L4.js";import{C as l,T as h}from"./TimeSavedCard-0TqsrY6y.js";function p(){const{data:a,loading:i,error:c}=n(r.useCallback(()=>d(),[]),{pollInterval:5e3});if(i&&!a)return s.jsx("div",{className:"tab-stub",children:"Loading snapshot…"});if(c&&!a)return s.jsxs("div",{className:"tab-stub",children:["Error: ",c.message]});if(!a)return s.jsx("div",{className:"tab-stub",children:"No snapshot data."});const{cacheHits:e,compacts:o,timeSaved:t}=a;return s.jsx("div",{className:"cache-tab",children:s.jsxs("div",{className:"card-grid overview-card-grid",children:[s.jsx(l,{cacheHitsSession:e.session,cacheHitsTotal:e.total,tokensSavedSession:e.sessionTokensSaved,tokensSavedTotal:e.totalTokensSaved,compactionsSession:o.session,compactionsTotal:o.total}),s.jsx(h,{compactSessionSec:t.compact.sessionSec,compactTotalSec:t.compact.totalSec,cacheHitSessionSec:t.cacheHit.sessionSec,cacheHitTotalSec:t.cacheHit.totalSec})]})})}export{p as default};
|
|
2
|
+
//# sourceMappingURL=CacheTab-BCLzVL4S.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheTab-
|
|
1
|
+
{"version":3,"file":"CacheTab-BCLzVL4S.js","sources":["../../src/tabs/CacheTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/CacheTab.tsx — Cache tab (NEW).\n *\n * Renders CacheHitsCard + TimeSavedCard from /api/snapshot\n * via the useApi hook with 5s polling.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type { SnapshotResponse } from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchSnapshot } from \"../api/client\";\nimport { CacheHitsCard } from \"../components/CacheHitsCard\";\nimport { TimeSavedCard } from \"../components/TimeSavedCard\";\n\nexport default function CacheTab(): React.ReactElement {\n\tconst { data: snapshot, loading, error } = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\tif (loading && !snapshot)\n\t\treturn <div className=\"tab-stub\">Loading snapshot…</div>;\n\tif (error && !snapshot)\n\t\treturn <div className=\"tab-stub\">Error: {error.message}</div>;\n\tif (!snapshot) return <div className=\"tab-stub\">No snapshot data.</div>;\n\n\tconst { cacheHits, compacts, timeSaved } = snapshot;\n\n\treturn (\n\t\t<div className=\"cache-tab\">\n\t\t\t<div className=\"card-grid overview-card-grid\">\n\t\t\t\t<CacheHitsCard\n\t\t\t\t\tcacheHitsSession={cacheHits.session}\n\t\t\t\t\tcacheHitsTotal={cacheHits.total}\n\t\t\t\t\ttokensSavedSession={cacheHits.sessionTokensSaved}\n\t\t\t\t\ttokensSavedTotal={cacheHits.totalTokensSaved}\n\t\t\t\t\tcompactionsSession={compacts.session}\n\t\t\t\t\tcompactionsTotal={compacts.total}\n\t\t\t\t/>\n\t\t\t\t<TimeSavedCard\n\t\t\t\t\tcompactSessionSec={timeSaved.compact.sessionSec}\n\t\t\t\t\tcompactTotalSec={timeSaved.compact.totalSec}\n\t\t\t\t\tcacheHitSessionSec={timeSaved.cacheHit.sessionSec}\n\t\t\t\t\tcacheHitTotalSec={timeSaved.cacheHit.totalSec}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["CacheTab","snapshot","loading","error","useApi","useCallback","fetchSnapshot","jsx","jsxs","cacheHits","compacts","timeSaved","CacheHitsCard","TimeSavedCard"],"mappings":"+GAeA,SAAwBA,GAA+B,CACtD,KAAM,CAAE,KAAMC,EAAU,QAAAC,EAAS,MAAAC,GAAUC,EAC1CC,cAAY,IAAMC,EAAA,EAAiB,EAAE,EACrC,CAAE,aAAc,GAAA,CAAK,EAGtB,GAAIJ,GAAW,CAACD,EACf,OAAOM,EAAAA,IAAC,MAAA,CAAI,UAAU,WAAW,SAAA,oBAAiB,EACnD,GAAIJ,GAAS,CAACF,EACb,OAAOO,EAAAA,KAAC,MAAA,CAAI,UAAU,WAAW,SAAA,CAAA,UAAQL,EAAM,OAAA,EAAQ,EACxD,GAAI,CAACF,EAAU,aAAQ,MAAA,CAAI,UAAU,WAAW,SAAA,oBAAiB,EAEjE,KAAM,CAAE,UAAAQ,EAAW,SAAAC,EAAU,UAAAC,CAAA,EAAcV,EAE3C,aACE,MAAA,CAAI,UAAU,YACd,SAAAO,EAAAA,KAAC,MAAA,CAAI,UAAU,+BACd,SAAA,CAAAD,EAAAA,IAACK,EAAA,CACA,iBAAkBH,EAAU,QAC5B,eAAgBA,EAAU,MAC1B,mBAAoBA,EAAU,mBAC9B,iBAAkBA,EAAU,iBAC5B,mBAAoBC,EAAS,QAC7B,iBAAkBA,EAAS,KAAA,CAAA,EAE5BH,EAAAA,IAACM,EAAA,CACA,kBAAmBF,EAAU,QAAQ,WACrC,gBAAiBA,EAAU,QAAQ,SACnC,mBAAoBA,EAAU,SAAS,WACvC,iBAAkBA,EAAU,SAAS,QAAA,CAAA,CACtC,CAAA,CACD,CAAA,CACD,CAEF"}
|
package/extensions/dashboard-client/dist/assets/{ConfigTab-DQ4FG33J.js → ConfigTab-CBYvp2mV.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as i,p as N,j as e,c as w,d as y}from"./index-
|
|
2
|
-
//# sourceMappingURL=ConfigTab-
|
|
1
|
+
import{r as i,p as N,j as e,c as w,d as y}from"./index-BcoSz-L4.js";const b=[{id:"transparent",label:"Transparent (accents only)",css:{bg:null,fg:"#c9d1d9",accent:"#3fb950",mega:"#f0883e"},ansi:{bg:null,fg:"39",accent:"32",mega:"33"}},{id:"retro",label:"Retro 8-bit green",css:{bg:"#003300",fg:"#33ff33",accent:"#00ff41",mega:"#39ff14"},ansi:{bg:"40",fg:"92",accent:"92",mega:"1;92"}},{id:"orange-bold",label:"Orange bold",css:{bg:"#1a0f00",fg:"#ff8c00",accent:"#ff6b00",mega:"#ff4500"},ansi:{bg:"48;5;52",fg:"38;5;208",accent:"38;5;208",mega:"1;38;5;202"}},{id:"cyan-neon",label:"Cyan neon",css:{bg:"#001a1a",fg:"#00ffff",accent:"#22d3ee",mega:"#7df9ff"},ansi:{bg:"48;5;23",fg:"38;5;51",accent:"38;5;51",mega:"1;38;5;51"}},{id:"amber-mono",label:"Amber phosphor",css:{bg:"#1a1200",fg:"#ffb000",accent:"#ff8c00",mega:"#ff5500"},ansi:{bg:"48;5;58",fg:"38;5;214",accent:"38;5;214",mega:"1;38;5;208"}},{id:"grayscale",label:"Grayscale minimal",css:{bg:"#161616",fg:"#b0b0b0",accent:"#8b949e",mega:"#e6edf3"},ansi:{bg:"48;5;235",fg:"38;5;249",accent:"38;5;249",mega:"1;38;5;255"}}],g=b.map(t=>t.id),c={gameMode:"Turn game mode on/off (themes the widget + dashboard)",theme:"Visual theme (applies instantly)",tuiMode:"TUI widget display density",tier:"Live pressure band — climbs low→mega as context fills the window.",preset:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.",pressure:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window).",threshold:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size.",fastGate:"Fast-gate arming floor — the live trim arms once context passes this % of the window."};function T(t,d,l){let o=t.toLocaleString();return d!=null&&l>0&&(o+=` (${Math.round(d*100)}% of ${l.toLocaleString()})`),o}function S(t){return`${Math.round((t||0)*100)}%`}function E(){const[t,d]=i.useState(null),[l,o]=i.useState(null),[m,u]=i.useState(!1);i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await w();s&&d(r)}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]),i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await y();s&&(o(r),r.theme&&(document.documentElement.dataset.theme=r.theme))}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]);const h=i.useCallback(async s=>{u(!0);try{const a=await N(s);o(a),a.theme&&(document.documentElement.dataset.theme=a.theme)}catch{}u(!1)},[]),x=l?.game_mode_on??!1,p=l?.theme??"transparent",v=l?.tui_display_mode??"full",n=t?.config,j=t?.context.contextWindow??0;return e.jsxs("div",{className:"config-tab",children:[e.jsxs("section",{className:"config-settings",children:[e.jsx("h3",{children:"Settings"}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.gameMode,children:[e.jsx("input",{type:"checkbox",checked:x,onChange:s=>h({game_mode_on:s.currentTarget.checked}),disabled:m}),"Game Mode"]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.theme,children:["Theme",e.jsx("select",{value:p,onChange:s=>h({theme:s.currentTarget.value}),disabled:m,children:b.map(s=>e.jsx("option",{value:s.id,children:s.label},s.id))}),e.jsx("button",{type:"button",disabled:m,className:"theme-cycle-btn",title:"Cycle to the next theme (P3)",onClick:()=>{const s=g.indexOf(p),a=g[(s<0?0:s+1)%g.length];a&&h({theme:a})},children:"Next theme →"})]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.tuiMode,children:["TUI Mode",e.jsxs("select",{value:v,onChange:s=>h({tui_display_mode:s.currentTarget.value}),disabled:m,children:[e.jsx("option",{value:"full",children:"Full"}),e.jsx("option",{value:"minimal",children:"Minimal"})]})," ",e.jsx("span",{className:"tui-mode-hint",children:"(affects the in-app TUI widget; not shown here)"})]})})]}),e.jsxs("section",{className:"config-display",children:[e.jsx("h3",{children:"Configuration"}),e.jsxs("div",{className:"config-grid",children:[e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.tier,children:"Tier"}),e.jsx("span",{className:"config-value",children:t?`${t.tier} (live)`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.preset,children:"Preset"}),e.jsx("span",{className:"config-value",children:t?.presetTier??"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.pressure,children:"Pressure"}),e.jsx("span",{className:"config-value",children:t?S(t.pressure):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.threshold,children:"Threshold"}),e.jsx("span",{className:"config-value",children:n?T(n.thresholdTokens,n.tierPct,j):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.fastGate,children:"Fast Gate"}),e.jsx("span",{className:"config-value",children:n?`${n.fastGatePct}%`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Auto"}),e.jsx("span",{className:"config-value",children:n?n.auto?"enabled":"disabled":"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Anchor"}),e.jsx("span",{className:"config-value",children:n?.anchorUserMessages??"—"})]})]})]})]})}export{E as default};
|
|
2
|
+
//# sourceMappingURL=ConfigTab-CBYvp2mV.js.map
|