@codemem/server 0.27.0 → 0.28.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 +91 -11
- package/dist/index.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +7 -7
- package/static/app.js +15200 -12717
- package/static/favicon.svg +4 -14
- package/static/index.html +848 -378
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, coordinatorArchiveGroupAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, 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, syncProjectAllowedByFilters, syncVisibilityAllowed, 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, coordinatorArchiveGroupAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, 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, syncProjectAllowedByFilters, syncVisibilityAllowed, 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";
|
|
@@ -188,6 +188,7 @@ var SECRET_CONFIG_KEYS = new Set([
|
|
|
188
188
|
"observer_headers",
|
|
189
189
|
"sync_coordinator_admin_secret"
|
|
190
190
|
]);
|
|
191
|
+
var REMOVED_CONFIG_KEYS = new Set(["observer_rich_openai_use_responses"]);
|
|
191
192
|
var ALLOWED_KEYS = [
|
|
192
193
|
"claude_command",
|
|
193
194
|
"observer_base_url",
|
|
@@ -198,7 +199,6 @@ var ALLOWED_KEYS = [
|
|
|
198
199
|
"observer_simple_temperature",
|
|
199
200
|
"observer_rich_model",
|
|
200
201
|
"observer_rich_temperature",
|
|
201
|
-
"observer_rich_openai_use_responses",
|
|
202
202
|
"observer_rich_reasoning_effort",
|
|
203
203
|
"observer_rich_reasoning_summary",
|
|
204
204
|
"observer_rich_max_output_tokens",
|
|
@@ -231,7 +231,6 @@ var DEFAULTS = {
|
|
|
231
231
|
observer_runtime: "api_http",
|
|
232
232
|
observer_auth_source: "auto",
|
|
233
233
|
observer_tier_routing_enabled: false,
|
|
234
|
-
observer_rich_openai_use_responses: false,
|
|
235
234
|
observer_auth_command: [],
|
|
236
235
|
observer_auth_timeout_ms: 1500,
|
|
237
236
|
observer_auth_cache_ttl_s: 300,
|
|
@@ -275,6 +274,11 @@ function readConfigFile(configPath) {
|
|
|
275
274
|
return {};
|
|
276
275
|
}
|
|
277
276
|
}
|
|
277
|
+
function withoutRemovedConfigKeys(configData) {
|
|
278
|
+
const next = { ...configData };
|
|
279
|
+
for (const key of REMOVED_CONFIG_KEYS) delete next[key];
|
|
280
|
+
return next;
|
|
281
|
+
}
|
|
278
282
|
function getEffectiveConfig(configData) {
|
|
279
283
|
const effective = {
|
|
280
284
|
...DEFAULTS,
|
|
@@ -406,7 +410,7 @@ function validateAndApplyUpdate(configData, key, value, providers) {
|
|
|
406
410
|
configData[key] = value;
|
|
407
411
|
return null;
|
|
408
412
|
}
|
|
409
|
-
if (key === "observer_tier_routing_enabled"
|
|
413
|
+
if (key === "observer_tier_routing_enabled") {
|
|
410
414
|
if (typeof value !== "boolean") return `${key} must be boolean`;
|
|
411
415
|
configData[key] = value;
|
|
412
416
|
return null;
|
|
@@ -446,7 +450,7 @@ function configRoutes(opts = {}) {
|
|
|
446
450
|
const app = new Hono();
|
|
447
451
|
app.get("/api/config", (c) => {
|
|
448
452
|
const configPath = getConfigPath();
|
|
449
|
-
const configData = readConfigFile(configPath);
|
|
453
|
+
const configData = withoutRemovedConfigKeys(readConfigFile(configPath));
|
|
450
454
|
const effective = getEffectiveConfig(configData);
|
|
451
455
|
return c.json({
|
|
452
456
|
path: configPath,
|
|
@@ -469,7 +473,7 @@ function configRoutes(opts = {}) {
|
|
|
469
473
|
if ("config" in payload && payload.config != null && (typeof payload.config !== "object" || Array.isArray(payload.config))) return c.json({ error: "config must be an object" }, 400);
|
|
470
474
|
const updates = "config" in payload && payload.config != null && typeof payload.config === "object" && !Array.isArray(payload.config) ? payload.config : payload;
|
|
471
475
|
const configPath = getCodememConfigPath();
|
|
472
|
-
const beforeConfig = readCodememConfigFile();
|
|
476
|
+
const beforeConfig = withoutRemovedConfigKeys(readCodememConfigFile());
|
|
473
477
|
const { allowedUpdates, error: protectedKeyError } = partitionViewerUpdates(updates, beforeConfig);
|
|
474
478
|
if (protectedKeyError) return c.json({ error: protectedKeyError }, 403);
|
|
475
479
|
const beforeEffective = getEffectiveConfig(beforeConfig);
|
|
@@ -1975,7 +1979,12 @@ function filterOpsForPeer(store, peerDeviceId, ops) {
|
|
|
1975
1979
|
* When showDiag is false, sensitive fields (fingerprint, last_error, addresses)
|
|
1976
1980
|
* are redacted.
|
|
1977
1981
|
*/
|
|
1978
|
-
function mapPeerRow(store, row, showDiag) {
|
|
1982
|
+
function mapPeerRow(store, row, showDiag, recentOpsByPeer) {
|
|
1983
|
+
const peerId = String(row.peer_device_id ?? "");
|
|
1984
|
+
const recentOps = recentOpsByPeer?.get(peerId) ?? {
|
|
1985
|
+
in: 0,
|
|
1986
|
+
out: 0
|
|
1987
|
+
};
|
|
1979
1988
|
return {
|
|
1980
1989
|
peer_device_id: row.peer_device_id,
|
|
1981
1990
|
name: row.name,
|
|
@@ -1989,9 +1998,33 @@ function mapPeerRow(store, row, showDiag) {
|
|
|
1989
1998
|
claimed_local_actor: Boolean(row.claimed_local_actor),
|
|
1990
1999
|
actor_id: row.actor_id ?? null,
|
|
1991
2000
|
actor_display_name: row.actor_display_name ?? null,
|
|
1992
|
-
project_scope: { ...currentProjectScope(row, readPeerProjectFilters(store, String(row.peer_device_id ?? ""))) }
|
|
2001
|
+
project_scope: { ...currentProjectScope(row, readPeerProjectFilters(store, String(row.peer_device_id ?? ""))) },
|
|
2002
|
+
recent_ops: {
|
|
2003
|
+
in: recentOps.in,
|
|
2004
|
+
out: recentOps.out
|
|
2005
|
+
}
|
|
1993
2006
|
};
|
|
1994
2007
|
}
|
|
2008
|
+
var SYNC_DIRECTION_WINDOW_SECONDS = 1440 * 60;
|
|
2009
|
+
function recentPeerOps(store) {
|
|
2010
|
+
const cutoff = (/* @__PURE__ */ new Date(Date.now() - SYNC_DIRECTION_WINDOW_SECONDS * 1e3)).toISOString();
|
|
2011
|
+
const rows = store.db.prepare(`SELECT peer_device_id,
|
|
2012
|
+
COALESCE(SUM(ops_in), 0) AS ops_in,
|
|
2013
|
+
COALESCE(SUM(ops_out), 0) AS ops_out
|
|
2014
|
+
FROM sync_attempts
|
|
2015
|
+
WHERE ok = 1 AND finished_at IS NOT NULL AND finished_at >= ?
|
|
2016
|
+
GROUP BY peer_device_id`).all(cutoff);
|
|
2017
|
+
const map = /* @__PURE__ */ new Map();
|
|
2018
|
+
for (const row of rows) {
|
|
2019
|
+
const id = String(row.peer_device_id ?? "").trim();
|
|
2020
|
+
if (!id) continue;
|
|
2021
|
+
map.set(id, {
|
|
2022
|
+
in: Number(row.ops_in ?? 0),
|
|
2023
|
+
out: Number(row.ops_out ?? 0)
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
return map;
|
|
2027
|
+
}
|
|
1995
2028
|
function isRecentIso(value, windowS = SYNC_STALE_AFTER_SECONDS) {
|
|
1996
2029
|
const raw = String(value ?? "").trim();
|
|
1997
2030
|
if (!raw) return false;
|
|
@@ -2361,8 +2394,10 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2361
2394
|
statusPayload.daemon_running = runtimeStatus.phase === "starting" || daemonRunning;
|
|
2362
2395
|
statusPayload.daemon_detail = runtimeStatus.detail ?? daemonDetail;
|
|
2363
2396
|
}
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2397
|
+
const peerRows = traceSync("peerRows", () => store.db.prepare(PEERS_QUERY).all());
|
|
2398
|
+
const recentOpsByPeer = traceSync("recentPeerOps", () => recentPeerOps(store));
|
|
2399
|
+
const peersItems = peerRows.map((row) => {
|
|
2400
|
+
const peer = mapPeerRow(store, row, showDiag, recentOpsByPeer);
|
|
2366
2401
|
peer.status = peerStatus(peer);
|
|
2367
2402
|
return peer;
|
|
2368
2403
|
});
|
|
@@ -2447,7 +2482,9 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2447
2482
|
const store = getStore();
|
|
2448
2483
|
{
|
|
2449
2484
|
const showDiag = queryBool(c.req.query("includeDiagnostics"));
|
|
2450
|
-
const
|
|
2485
|
+
const rows = store.db.prepare(PEERS_QUERY).all();
|
|
2486
|
+
const recentOpsByPeer = recentPeerOps(store);
|
|
2487
|
+
const peers = rows.map((row) => mapPeerRow(store, row, showDiag, recentOpsByPeer));
|
|
2451
2488
|
return c.json({
|
|
2452
2489
|
items: peers,
|
|
2453
2490
|
redacted: !showDiag
|
|
@@ -3381,6 +3418,49 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
3381
3418
|
}, 400);
|
|
3382
3419
|
}
|
|
3383
3420
|
});
|
|
3421
|
+
app.post("/api/coordinator/admin/devices/:device_id/enable", async (c) => {
|
|
3422
|
+
const config = readCoordinatorSyncConfig();
|
|
3423
|
+
const status = coordinatorAdminStatusPayload(config);
|
|
3424
|
+
if (status.readiness === "not_configured") return c.json({
|
|
3425
|
+
error: "coordinator_not_configured",
|
|
3426
|
+
status
|
|
3427
|
+
}, 400);
|
|
3428
|
+
if (!status.has_admin_secret) return c.json({
|
|
3429
|
+
error: "coordinator_admin_secret_missing",
|
|
3430
|
+
status
|
|
3431
|
+
}, 400);
|
|
3432
|
+
const deviceId = String(c.req.param("device_id") ?? "").trim();
|
|
3433
|
+
if (!deviceId) return c.json({
|
|
3434
|
+
error: "device_id required",
|
|
3435
|
+
status
|
|
3436
|
+
}, 400);
|
|
3437
|
+
const groupId = resolveCoordinatorAdminGroup(c.req.query("group_id"), status);
|
|
3438
|
+
if (!groupId) return c.json({
|
|
3439
|
+
error: "group_id required",
|
|
3440
|
+
status
|
|
3441
|
+
}, 400);
|
|
3442
|
+
try {
|
|
3443
|
+
if (!await coordinatorEnableDeviceAction({
|
|
3444
|
+
groupId,
|
|
3445
|
+
deviceId,
|
|
3446
|
+
remoteUrl: config.syncCoordinatorUrl || null,
|
|
3447
|
+
adminSecret: config.syncCoordinatorAdminSecret || null
|
|
3448
|
+
})) return c.json({
|
|
3449
|
+
error: "device_not_found",
|
|
3450
|
+
status
|
|
3451
|
+
}, 404);
|
|
3452
|
+
return c.json({
|
|
3453
|
+
ok: true,
|
|
3454
|
+
device_id: deviceId,
|
|
3455
|
+
status
|
|
3456
|
+
});
|
|
3457
|
+
} catch (error) {
|
|
3458
|
+
return c.json({
|
|
3459
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3460
|
+
status
|
|
3461
|
+
}, 400);
|
|
3462
|
+
}
|
|
3463
|
+
});
|
|
3384
3464
|
app.post("/api/coordinator/admin/devices/:device_id/remove", async (c) => {
|
|
3385
3465
|
const config = readCoordinatorSyncConfig();
|
|
3386
3466
|
const status = coordinatorAdminStatusPayload(config);
|