codemem 0.28.0 → 0.29.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 +116 -11
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SESSION_CONTEXT_BACKFILL_JOB, SessionContextBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, ensureSchemaBootstrapped, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingSessionContextBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
2
|
+
import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, ensureSchemaBootstrapped, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, listMaintenanceJobs, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
5
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { styleText } from "node:util";
|
|
7
|
-
import {
|
|
7
|
+
import { randomInt } from "node:crypto";
|
|
8
8
|
import { homedir, networkInterfaces } from "node:os";
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
import * as p from "@clack/prompts";
|
|
@@ -536,6 +536,15 @@ var MAX_FILES_MODIFIED = 64;
|
|
|
536
536
|
var MAX_WORKING_SET_PATHS = 8;
|
|
537
537
|
var MAX_QUERY_CHARS = 500;
|
|
538
538
|
var MAX_QUERY_FILE_BASENAMES = 5;
|
|
539
|
+
var SESSION_FILE_LABEL_CHARS = 24;
|
|
540
|
+
function stableSessionSuffix(sessionId) {
|
|
541
|
+
let hash = 14695981039346656037n;
|
|
542
|
+
for (const byte of Buffer.from(sessionId, "utf8")) {
|
|
543
|
+
hash ^= BigInt(byte);
|
|
544
|
+
hash = BigInt.asUintN(64, hash * 1099511628211n);
|
|
545
|
+
}
|
|
546
|
+
return hash.toString(16).padStart(16, "0");
|
|
547
|
+
}
|
|
539
548
|
function defaultSessionState() {
|
|
540
549
|
return {
|
|
541
550
|
first_prompt: "",
|
|
@@ -553,9 +562,13 @@ function contextDir() {
|
|
|
553
562
|
const override = process.env.CODEMEM_CLAUDE_HOOK_CONTEXT_DIR;
|
|
554
563
|
return expandHome(override?.trim() ? override : "~/.codemem/claude-hook-context");
|
|
555
564
|
}
|
|
565
|
+
function sessionFileStem(sessionId) {
|
|
566
|
+
const trimmed = sessionId.trim();
|
|
567
|
+
if (!trimmed) return "session-state";
|
|
568
|
+
return `${trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, SESSION_FILE_LABEL_CHARS) || "session"}-${stableSessionSuffix(trimmed)}`;
|
|
569
|
+
}
|
|
556
570
|
function statePathForSession(sessionId) {
|
|
557
|
-
|
|
558
|
-
return join(contextDir(), `${digest}.json`);
|
|
571
|
+
return join(contextDir(), `${sessionFileStem(sessionId)}.json`);
|
|
559
572
|
}
|
|
560
573
|
/**
|
|
561
574
|
* Normalize a prompt-shaped payload field: drop non-strings, trim
|
|
@@ -2562,6 +2575,78 @@ cmd$1.action((inputFile, opts) => {
|
|
|
2562
2575
|
});
|
|
2563
2576
|
var importMemoriesCommand = cmd$1;
|
|
2564
2577
|
//#endregion
|
|
2578
|
+
//#region src/commands/maintenance.ts
|
|
2579
|
+
var maintenanceCmd = new Command("maintenance").configureHelp(helpStyle).description("Inspect background maintenance / backfill jobs");
|
|
2580
|
+
var statusCmd$1 = new Command("status").configureHelp(helpStyle).description("Print current status of all maintenance jobs");
|
|
2581
|
+
addDbOption(statusCmd$1);
|
|
2582
|
+
addJsonOption(statusCmd$1);
|
|
2583
|
+
statusCmd$1.action((opts) => {
|
|
2584
|
+
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
2585
|
+
try {
|
|
2586
|
+
const jobs = listMaintenanceJobs(store.db);
|
|
2587
|
+
if (opts.json) {
|
|
2588
|
+
console.log(JSON.stringify({ jobs }, null, 2));
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
printJobsTable(jobs);
|
|
2592
|
+
} finally {
|
|
2593
|
+
store.close();
|
|
2594
|
+
}
|
|
2595
|
+
});
|
|
2596
|
+
maintenanceCmd.addCommand(statusCmd$1);
|
|
2597
|
+
var maintenanceCommand = maintenanceCmd;
|
|
2598
|
+
function printJobsTable(jobs) {
|
|
2599
|
+
p.intro("codemem maintenance");
|
|
2600
|
+
if (jobs.length === 0) {
|
|
2601
|
+
p.log.info("No maintenance jobs recorded in this database.");
|
|
2602
|
+
p.outro("done");
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
const statusRank = {
|
|
2606
|
+
running: 0,
|
|
2607
|
+
pending: 1,
|
|
2608
|
+
failed: 2,
|
|
2609
|
+
cancelled: 3,
|
|
2610
|
+
completed: 4
|
|
2611
|
+
};
|
|
2612
|
+
const sorted = [...jobs].sort((a, b) => {
|
|
2613
|
+
const rankDiff = (statusRank[a.status] ?? 9) - (statusRank[b.status] ?? 9);
|
|
2614
|
+
if (rankDiff !== 0) return rankDiff;
|
|
2615
|
+
return b.updated_at.localeCompare(a.updated_at);
|
|
2616
|
+
});
|
|
2617
|
+
for (const job of sorted) {
|
|
2618
|
+
const progress = formatProgress(job);
|
|
2619
|
+
const when = job.finished_at ? `finished ${shortAge(job.finished_at)} ago` : job.started_at ? `started ${shortAge(job.started_at)} ago` : `updated ${shortAge(job.updated_at)} ago`;
|
|
2620
|
+
const header = `${job.kind} · ${job.status}${progress ? ` · ${progress}` : ""} · ${when}`;
|
|
2621
|
+
const body = [
|
|
2622
|
+
job.title,
|
|
2623
|
+
job.message,
|
|
2624
|
+
job.error ? `error: ${job.error}` : null
|
|
2625
|
+
].filter(Boolean).join("\n");
|
|
2626
|
+
(job.status === "failed" ? p.log.error : job.status === "completed" ? p.log.success : p.log.step)(`${header}\n${body}`);
|
|
2627
|
+
}
|
|
2628
|
+
p.outro("done");
|
|
2629
|
+
}
|
|
2630
|
+
function formatProgress(job) {
|
|
2631
|
+
const { current, total, unit } = job.progress;
|
|
2632
|
+
if (!current && !total) return null;
|
|
2633
|
+
const unitLabel = unit || "items";
|
|
2634
|
+
if (total == null || total <= 0) return `${current.toLocaleString()} ${unitLabel}`;
|
|
2635
|
+
const pct = Math.round(100 * current / total);
|
|
2636
|
+
return `${current.toLocaleString()}/${total.toLocaleString()} ${unitLabel} (${pct}%)`;
|
|
2637
|
+
}
|
|
2638
|
+
function shortAge(iso) {
|
|
2639
|
+
const parsed = Date.parse(iso);
|
|
2640
|
+
if (!Number.isFinite(parsed)) return iso;
|
|
2641
|
+
const seconds = Math.max(0, Math.round((Date.now() - parsed) / 1e3));
|
|
2642
|
+
if (seconds < 60) return `${seconds}s`;
|
|
2643
|
+
const minutes = Math.round(seconds / 60);
|
|
2644
|
+
if (minutes < 60) return `${minutes}m`;
|
|
2645
|
+
const hours = Math.round(minutes / 60);
|
|
2646
|
+
if (hours < 48) return `${hours}h`;
|
|
2647
|
+
return `${Math.round(hours / 24)}d`;
|
|
2648
|
+
}
|
|
2649
|
+
//#endregion
|
|
2565
2650
|
//#region src/commands/mcp.ts
|
|
2566
2651
|
var mcpCmd = new Command("mcp").configureHelp(helpStyle).description("Start the MCP stdio server");
|
|
2567
2652
|
addDbOption(mcpCmd);
|
|
@@ -3868,7 +3953,10 @@ async function startForegroundViewer(invocation) {
|
|
|
3868
3953
|
dbPath,
|
|
3869
3954
|
signal: retentionAbort.signal
|
|
3870
3955
|
});
|
|
3871
|
-
const vectorMigrationRunner = new VectorModelMigrationRunner({
|
|
3956
|
+
const vectorMigrationRunner = new VectorModelMigrationRunner({
|
|
3957
|
+
dbPath,
|
|
3958
|
+
signal: backfillAbort.signal
|
|
3959
|
+
});
|
|
3872
3960
|
const backfillCoordinator = createSequentialBackfillCoordinator(store, [
|
|
3873
3961
|
{
|
|
3874
3962
|
name: "Dedup-key",
|
|
@@ -3896,6 +3984,16 @@ async function startForegroundViewer(invocation) {
|
|
|
3896
3984
|
dbPath,
|
|
3897
3985
|
signal: backfillAbort.signal
|
|
3898
3986
|
})
|
|
3987
|
+
},
|
|
3988
|
+
{
|
|
3989
|
+
name: "Session-summary dedup",
|
|
3990
|
+
kind: SUMMARY_DEDUP_BACKFILL_JOB,
|
|
3991
|
+
isPending: hasPendingSummaryDedupBackfill,
|
|
3992
|
+
createRunner: () => new SummaryDedupBackfillRunner({
|
|
3993
|
+
dbPath,
|
|
3994
|
+
deviceId: store.deviceId,
|
|
3995
|
+
signal: backfillAbort.signal
|
|
3996
|
+
})
|
|
3899
3997
|
}
|
|
3900
3998
|
], backfillAbort.signal);
|
|
3901
3999
|
const syncRuntimeStatus = {
|
|
@@ -4100,10 +4198,12 @@ serveCmd.addOption(new Option("--stop", "stop background viewer").hideHelp());
|
|
|
4100
4198
|
serveCmd.addOption(new Option("--restart", "restart background viewer").hideHelp());
|
|
4101
4199
|
var serveCommand = serveCmd.action(async (action, opts) => {
|
|
4102
4200
|
try {
|
|
4103
|
-
if (
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4201
|
+
if (action === void 0) {
|
|
4202
|
+
if (opts.stop) emitDeprecationWarning("--stop", "codemem serve stop");
|
|
4203
|
+
if (opts.restart) emitDeprecationWarning("--restart", "codemem serve restart");
|
|
4204
|
+
if (opts.background) emitDeprecationWarning("--background", "codemem serve start");
|
|
4205
|
+
if (opts.foreground) emitDeprecationWarning("--foreground", "codemem serve start --foreground");
|
|
4206
|
+
}
|
|
4107
4207
|
const normalizedAction = action === void 0 ? void 0 : action === "start" || action === "stop" || action === "restart" ? action : null;
|
|
4108
4208
|
if (normalizedAction === null) {
|
|
4109
4209
|
p.log.error(`Unknown serve action: ${action}`);
|
|
@@ -4884,11 +4984,15 @@ doctorCmd.action(async (opts) => {
|
|
|
4884
4984
|
if (reach !== "ok") issues.push(`peer ${peer.peer_device_id} unreachable`);
|
|
4885
4985
|
if (!pinned || !hasKey) issues.push(`peer ${peer.peer_device_id} not pinned`);
|
|
4886
4986
|
}
|
|
4987
|
+
const mdnsActive = mdnsEnabled();
|
|
4988
|
+
const mdnsSource = process.env.CODEMEM_SYNC_MDNS ? "env" : config.sync_mdns ? "config" : null;
|
|
4989
|
+
const mdnsLabel = mdnsActive ? mdnsSource === "env" ? "enabled (env)" : "enabled (config)" : "disabled";
|
|
4887
4990
|
if (opts.json) {
|
|
4888
4991
|
console.log(JSON.stringify({
|
|
4889
4992
|
enabled: config.sync_enabled === true,
|
|
4890
4993
|
listen: `${syncHost}:${syncPort}`,
|
|
4891
|
-
mdns:
|
|
4994
|
+
mdns: mdnsLabel,
|
|
4995
|
+
mdns_source: mdnsSource,
|
|
4892
4996
|
daemon: reachable ? "running" : "not running",
|
|
4893
4997
|
identity: device?.device_id ?? null,
|
|
4894
4998
|
daemon_error: daemonState?.last_error ?? null,
|
|
@@ -4901,7 +5005,7 @@ doctorCmd.action(async (opts) => {
|
|
|
4901
5005
|
console.log("Sync doctor");
|
|
4902
5006
|
console.log(`- Enabled: ${config.sync_enabled === true}`);
|
|
4903
5007
|
console.log(`- Listen: ${syncHost}:${syncPort}`);
|
|
4904
|
-
console.log(`- mDNS: ${
|
|
5008
|
+
console.log(`- mDNS: ${mdnsLabel}`);
|
|
4905
5009
|
console.log(`- Daemon: ${reachable ? "running" : "not running"}`);
|
|
4906
5010
|
if (!device) console.log("- Identity: missing (run `codemem sync enable`)");
|
|
4907
5011
|
else console.log(`- Identity: ${device.device_id}`);
|
|
@@ -5394,6 +5498,7 @@ program.addCommand(dbCommand);
|
|
|
5394
5498
|
program.addCommand(exportMemoriesCommand);
|
|
5395
5499
|
program.addCommand(importMemoriesCommand);
|
|
5396
5500
|
program.addCommand(statsCommand);
|
|
5501
|
+
program.addCommand(maintenanceCommand);
|
|
5397
5502
|
program.addCommand(embedCommand);
|
|
5398
5503
|
program.addCommand(recentCommand);
|
|
5399
5504
|
program.addCommand(searchCommand);
|