@codemem/server 0.25.3 → 0.26.0
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 +78 -7
- package/dist/index.js.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +2 -2
- package/static/app.js +11696 -10021
- package/static/index.html +212 -11
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, listMaintenanceJobs, 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, getSemanticIndexDiagnostics, 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";
|
|
@@ -553,6 +553,39 @@ function queryBool(value) {
|
|
|
553
553
|
}
|
|
554
554
|
//#endregion
|
|
555
555
|
//#region src/routes/memory.ts
|
|
556
|
+
function cleanOwnershipValue(value) {
|
|
557
|
+
if (typeof value !== "string") return null;
|
|
558
|
+
const trimmed = value.trim();
|
|
559
|
+
return trimmed ? trimmed : null;
|
|
560
|
+
}
|
|
561
|
+
function buildOwnershipContext(store) {
|
|
562
|
+
const claimedPeerIds = new Set(store.sameActorPeerIds());
|
|
563
|
+
return {
|
|
564
|
+
actorId: store.actorId,
|
|
565
|
+
claimedPeerIds,
|
|
566
|
+
deviceId: store.deviceId,
|
|
567
|
+
legacyActorIds: new Set(Array.from(claimedPeerIds, (peerId) => `legacy-sync:${peerId}`))
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function memoryOwnedBySelf(row, ownership, metadata = fromJson(row.metadata_json ?? null)) {
|
|
571
|
+
const meta = metadata;
|
|
572
|
+
const actorId = cleanOwnershipValue(row.actor_id) ?? cleanOwnershipValue(meta.actor_id);
|
|
573
|
+
if (actorId === ownership.actorId) return true;
|
|
574
|
+
const deviceId = cleanOwnershipValue(row.origin_device_id) ?? cleanOwnershipValue(meta.origin_device_id);
|
|
575
|
+
if (deviceId === ownership.deviceId) return true;
|
|
576
|
+
if (deviceId && ownership.claimedPeerIds.has(deviceId)) return true;
|
|
577
|
+
if (actorId && ownership.legacyActorIds.has(actorId)) return true;
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
function serializeMemoryRow(ownership, row) {
|
|
581
|
+
const metadata = fromJson(row.metadata_json ?? null);
|
|
582
|
+
return {
|
|
583
|
+
...row,
|
|
584
|
+
kind: canonicalMemoryKind(row.kind ?? null, row.metadata_json),
|
|
585
|
+
metadata_json: metadata,
|
|
586
|
+
owned_by_self: memoryOwnedBySelf(row, ownership, metadata)
|
|
587
|
+
};
|
|
588
|
+
}
|
|
556
589
|
/**
|
|
557
590
|
* Attach session project/cwd fields to memory items.
|
|
558
591
|
*/
|
|
@@ -615,14 +648,12 @@ function queryMemoryPage(store, options) {
|
|
|
615
648
|
});
|
|
616
649
|
const where = ["memory_items.active = 1", ...filterResult.clauses].join(" AND ");
|
|
617
650
|
const from = filterResult.joinSessions ? "memory_items JOIN sessions ON sessions.id = memory_items.session_id" : "memory_items";
|
|
618
|
-
|
|
651
|
+
const rows = store.db.prepare(`SELECT memory_items.* FROM ${from}
|
|
619
652
|
WHERE ${where}
|
|
620
653
|
ORDER BY memory_items.created_at DESC
|
|
621
|
-
LIMIT ? OFFSET ?`).all(...filterResult.params, options.limit + 1, options.offset)
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
metadata_json: fromJson(row.metadata_json ?? null)
|
|
625
|
-
}));
|
|
654
|
+
LIMIT ? OFFSET ?`).all(...filterResult.params, options.limit + 1, options.offset);
|
|
655
|
+
const ownership = buildOwnershipContext(store);
|
|
656
|
+
return rows.map((row) => serializeMemoryRow(ownership, row));
|
|
626
657
|
}
|
|
627
658
|
function isSummaryLikeMemory$1(item) {
|
|
628
659
|
return isSummaryLikeMemory({
|
|
@@ -870,6 +901,31 @@ function memoryRoutes(getStore) {
|
|
|
870
901
|
return c.json({ error: msg }, 400);
|
|
871
902
|
}
|
|
872
903
|
});
|
|
904
|
+
app.post("/api/memories/forget", async (c) => {
|
|
905
|
+
const store = getStore();
|
|
906
|
+
let body;
|
|
907
|
+
try {
|
|
908
|
+
body = await c.req.json();
|
|
909
|
+
} catch {
|
|
910
|
+
return c.json({ error: "invalid JSON" }, 400);
|
|
911
|
+
}
|
|
912
|
+
const memoryId = parseStrictInteger(typeof body.memory_id === "string" ? body.memory_id : String(body.memory_id ?? ""));
|
|
913
|
+
if (memoryId == null || memoryId <= 0) return c.json({ error: "memory_id must be int" }, 400);
|
|
914
|
+
const row = drizzle(store.db, { schema }).select().from(schema.memoryItems).where(eq(schema.memoryItems.id, memoryId)).get();
|
|
915
|
+
if (!row) return c.json({ error: "memory not found" }, 404);
|
|
916
|
+
if (!memoryOwnedBySelf(row, buildOwnershipContext(store))) return c.json({ error: "memory not owned by this device" }, 403);
|
|
917
|
+
if (Number(row.active ?? 1) === 0 || row.deleted_at != null) return c.json({ status: "ok" });
|
|
918
|
+
try {
|
|
919
|
+
store.forget(memoryId);
|
|
920
|
+
return c.json({ status: "ok" });
|
|
921
|
+
} catch (err) {
|
|
922
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
923
|
+
if (msg.includes("not found")) return c.json({ error: msg }, 404);
|
|
924
|
+
if (msg.includes("not owned")) return c.json({ error: msg }, 403);
|
|
925
|
+
if (msg.includes("sync_rebootstrap_in_progress")) return c.json({ error: msg }, 409);
|
|
926
|
+
return c.json({ error: msg }, 400);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
873
929
|
return app;
|
|
874
930
|
}
|
|
875
931
|
//#endregion
|
|
@@ -1387,6 +1443,19 @@ function summarizeMaintenanceJobs(jobs, showDiagnostics) {
|
|
|
1387
1443
|
} : {}
|
|
1388
1444
|
}));
|
|
1389
1445
|
}
|
|
1446
|
+
function redactSemanticIndexDiagnostics(diagnostics, showDiagnostics) {
|
|
1447
|
+
if (showDiagnostics || !diagnostics.maintenance_job) return diagnostics;
|
|
1448
|
+
const summary = diagnostics.state === "pending" ? "Semantic-index catch-up is pending" : diagnostics.state === "failed" ? "Semantic-index catch-up failed" : diagnostics.state === "degraded" ? "Semantic index is running in degraded mode" : diagnostics.summary;
|
|
1449
|
+
return {
|
|
1450
|
+
...diagnostics,
|
|
1451
|
+
summary,
|
|
1452
|
+
maintenance_job: {
|
|
1453
|
+
...diagnostics.maintenance_job,
|
|
1454
|
+
message: null,
|
|
1455
|
+
error: null
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1390
1459
|
function syncKeysDir() {
|
|
1391
1460
|
return process.env.CODEMEM_KEYS_DIR?.trim() || void 0;
|
|
1392
1461
|
}
|
|
@@ -2222,6 +2291,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2222
2291
|
retentionState = void 0;
|
|
2223
2292
|
}
|
|
2224
2293
|
const lastSyncRow = d.select({ last_sync_at: max(schema.syncPeers.last_sync_at) }).from(schema.syncPeers).get();
|
|
2294
|
+
const semanticIndex = redactSemanticIndexDiagnostics(getSemanticIndexDiagnostics(store.db), showDiag);
|
|
2225
2295
|
const lastError = daemonState?.last_error;
|
|
2226
2296
|
const lastErrorAt = daemonState?.last_error_at;
|
|
2227
2297
|
const lastOkAt = daemonState?.last_ok_at;
|
|
@@ -2248,6 +2318,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2248
2318
|
last_error: retentionState?.last_error ?? null,
|
|
2249
2319
|
last_error_at: retentionState?.last_error_at ?? null
|
|
2250
2320
|
},
|
|
2321
|
+
semantic_index: semanticIndex,
|
|
2251
2322
|
peer_count: Number(peerCountRow?.total ?? 0),
|
|
2252
2323
|
last_sync_at: lastSyncRow?.last_sync_at ?? null,
|
|
2253
2324
|
daemon_state: daemonStateValue,
|