@codemem/server 0.24.0 → 0.25.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/index.js +72 -2
- package/dist/index.js.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/stats.d.ts.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +3 -3
- package/static/app.js +168 -6
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { CODEMEM_CONFIG_ENV_OVERRIDES, DEFAULT_TIME_WINDOW_S, MemoryStore, VERSION, VERSION as VERSION$1, applyReplicationOps, buildBaseUrl, buildFilterClausesWithContext, buildRawEventEnvelopeFromHook, canonicalMemoryKind, cleanupNonces, coordinatorCreateInviteAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorStatusSnapshot, createCoordinatorReciprocalApproval, ensureDeviceIdentity, extractReplicationOps, fingerprintPublicKey, fromJson, getCodememConfigPath, getCodememEnvOverrides, getSyncResetState, isSummaryLikeMemory, listCoordinatorJoinRequests, listObserverProviderOptions, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, parseStrictInteger, probeAvailableCredentials, readCodememConfigFile, readCoordinatorSyncConfig, recordNonce, requestJson, resolveDbPath, runSyncPass, schema, stripJsonComments, stripPrivateObj, stripTrailingCommas, verifySignature, writeCodememConfigFile } from "@codemem/core";
|
|
3
|
+
import { CODEMEM_CONFIG_ENV_OVERRIDES, DEFAULT_TIME_WINDOW_S, MemoryStore, VERSION, VERSION as VERSION$1, applyReplicationOps, buildBaseUrl, buildFilterClausesWithContext, buildRawEventEnvelopeFromHook, canonicalMemoryKind, cleanupNonces, coordinatorCreateInviteAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorStatusSnapshot, createCoordinatorReciprocalApproval, ensureDeviceIdentity, extractReplicationOps, fingerprintPublicKey, fromJson, getCodememConfigPath, getCodememEnvOverrides, getSyncResetState, isSummaryLikeMemory, listCoordinatorJoinRequests, listMaintenanceJobs, listObserverProviderOptions, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, parseStrictInteger, probeAvailableCredentials, readCodememConfigFile, readCoordinatorSyncConfig, recordNonce, requestJson, resolveDbPath, runSyncPass, schema, stripJsonComments, stripPrivateObj, stripTrailingCommas, verifySignature, writeCodememConfigFile } from "@codemem/core";
|
|
4
4
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { createMiddleware } from "hono/factory";
|
|
@@ -799,6 +799,30 @@ function memoryRoutes(getStore) {
|
|
|
799
799
|
return c.json(pack);
|
|
800
800
|
}
|
|
801
801
|
});
|
|
802
|
+
app.post("/api/pack/trace", async (c) => {
|
|
803
|
+
const store = getStore();
|
|
804
|
+
const parsed = await c.req.json().catch(() => Symbol.for("invalid-json"));
|
|
805
|
+
if (parsed === Symbol.for("invalid-json")) return c.json({ error: "invalid json body" }, 400);
|
|
806
|
+
const body = parsed;
|
|
807
|
+
const context = typeof body?.context === "string" ? body.context.trim() : "";
|
|
808
|
+
if (!context) return c.json({ error: "context required" }, 400);
|
|
809
|
+
const limit = body?.limit == null ? 10 : typeof body.limit === "number" && Number.isInteger(body.limit) && body.limit >= 1 ? body.limit : null;
|
|
810
|
+
if (limit == null) return c.json({ error: "limit must be a positive int" }, 400);
|
|
811
|
+
let tokenBudget = null;
|
|
812
|
+
if (body?.token_budget != null) {
|
|
813
|
+
if (typeof body.token_budget !== "number" || !Number.isInteger(body.token_budget) || body.token_budget < 0) return c.json({ error: "token_budget must be int" }, 400);
|
|
814
|
+
tokenBudget = body.token_budget;
|
|
815
|
+
}
|
|
816
|
+
const project = typeof body?.project === "string" && body.project.trim() ? body.project : null;
|
|
817
|
+
if (body?.working_set_files != null && !Array.isArray(body.working_set_files)) return c.json({ error: "working_set_files must be an array of strings" }, 400);
|
|
818
|
+
if (Array.isArray(body?.working_set_files) && body.working_set_files.some((value) => typeof value !== "string")) return c.json({ error: "working_set_files must be an array of strings" }, 400);
|
|
819
|
+
const workingSetFiles = Array.isArray(body?.working_set_files) ? body.working_set_files : [];
|
|
820
|
+
const filters = {};
|
|
821
|
+
if (project) filters.project = project;
|
|
822
|
+
if (workingSetFiles.length > 0) filters.working_set_paths = workingSetFiles;
|
|
823
|
+
const trace = await store.buildMemoryPackTraceAsync(context, limit, tokenBudget, filters);
|
|
824
|
+
return c.json(trace);
|
|
825
|
+
});
|
|
802
826
|
app.get("/api/memory", (c) => {
|
|
803
827
|
const store = getStore();
|
|
804
828
|
{
|
|
@@ -1214,6 +1238,17 @@ function rawEventsRoutes(getStore, sweeper) {
|
|
|
1214
1238
|
*
|
|
1215
1239
|
* Ports Python's viewer_routes/stats.py.
|
|
1216
1240
|
*/
|
|
1241
|
+
function maintenanceJobSortKey(job) {
|
|
1242
|
+
return [job.started_at ?? job.updated_at, job.kind];
|
|
1243
|
+
}
|
|
1244
|
+
function sortActiveMaintenanceJobs$1(jobs) {
|
|
1245
|
+
return [...jobs].sort((a, b) => {
|
|
1246
|
+
const [aTime, aKind] = maintenanceJobSortKey(a);
|
|
1247
|
+
const [bTime, bKind] = maintenanceJobSortKey(b);
|
|
1248
|
+
if (aTime !== bTime) return aTime.localeCompare(bTime);
|
|
1249
|
+
return aKind.localeCompare(bKind);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1217
1252
|
/**
|
|
1218
1253
|
* Create stats routes. The store factory is called per-request to get a
|
|
1219
1254
|
* fresh connection (matching the Python viewer pattern).
|
|
@@ -1230,9 +1265,18 @@ function statsRoutes(getStore) {
|
|
|
1230
1265
|
};
|
|
1231
1266
|
app.get("/api/stats", (c) => {
|
|
1232
1267
|
const store = getStore();
|
|
1268
|
+
const activeJobs = sortActiveMaintenanceJobs$1(listMaintenanceJobs(store.db).filter((job) => job.status === "pending" || job.status === "running" || job.status === "failed")).map((job) => ({
|
|
1269
|
+
kind: job.kind,
|
|
1270
|
+
title: job.title,
|
|
1271
|
+
status: job.status,
|
|
1272
|
+
message: job.message,
|
|
1273
|
+
progress: job.progress,
|
|
1274
|
+
error: job.error
|
|
1275
|
+
}));
|
|
1233
1276
|
return c.json({
|
|
1234
1277
|
...store.stats(),
|
|
1235
|
-
viewer_pid: process.pid
|
|
1278
|
+
viewer_pid: process.pid,
|
|
1279
|
+
maintenance_jobs: activeJobs
|
|
1236
1280
|
});
|
|
1237
1281
|
});
|
|
1238
1282
|
app.get("/api/runtime", (c) => {
|
|
@@ -1318,6 +1362,31 @@ var SYNC_STALE_AFTER_SECONDS = 600;
|
|
|
1318
1362
|
var SYNC_PROTOCOL_VERSION = "2";
|
|
1319
1363
|
var LEGACY_SYNC_ACTOR_DISPLAY_NAME = "Legacy synced peer";
|
|
1320
1364
|
var LEGACY_SHARED_WORKSPACE_ID = "shared:legacy";
|
|
1365
|
+
function sortActiveMaintenanceJobs(jobs) {
|
|
1366
|
+
return [...jobs].sort((a, b) => {
|
|
1367
|
+
const aTime = a.started_at ?? a.updated_at;
|
|
1368
|
+
const bTime = b.started_at ?? b.updated_at;
|
|
1369
|
+
if (aTime !== bTime) return aTime.localeCompare(bTime);
|
|
1370
|
+
return a.kind.localeCompare(b.kind);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function summarizeMaintenanceJobs(jobs, showDiagnostics) {
|
|
1374
|
+
return sortActiveMaintenanceJobs(jobs.filter((job) => [
|
|
1375
|
+
"pending",
|
|
1376
|
+
"running",
|
|
1377
|
+
"failed"
|
|
1378
|
+
].includes(String(job.status ?? "")))).map((job) => ({
|
|
1379
|
+
kind: job.kind,
|
|
1380
|
+
title: job.title,
|
|
1381
|
+
status: job.status,
|
|
1382
|
+
progress: job.progress,
|
|
1383
|
+
...showDiagnostics ? {
|
|
1384
|
+
message: job.message,
|
|
1385
|
+
error: job.error,
|
|
1386
|
+
metadata: job.metadata
|
|
1387
|
+
} : {}
|
|
1388
|
+
}));
|
|
1389
|
+
}
|
|
1321
1390
|
function syncKeysDir() {
|
|
1322
1391
|
return process.env.CODEMEM_KEYS_DIR?.trim() || void 0;
|
|
1323
1392
|
}
|
|
@@ -2238,6 +2307,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2238
2307
|
const latestAttemptError = String(attemptsItems[0]?.error || "").trim();
|
|
2239
2308
|
const statusBlock = {
|
|
2240
2309
|
...statusPayload,
|
|
2310
|
+
background_maintenance: summarizeMaintenanceJobs(listMaintenanceJobs(store.db), showDiag),
|
|
2241
2311
|
peers: peersMap,
|
|
2242
2312
|
pending: 0,
|
|
2243
2313
|
sync: {},
|