@remnic/server 9.3.765 → 9.3.767
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.d.ts +29 -1
- package/dist/index.js +190 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ interface ServerConfig {
|
|
|
25
25
|
adminConsoleEnabled?: boolean;
|
|
26
26
|
adminConsolePublicDir?: string;
|
|
27
27
|
adminConsolePrefillToken?: boolean;
|
|
28
|
+
readinessOverride?: boolean;
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
interface ParsedServerConfig {
|
|
@@ -36,6 +37,7 @@ interface ParsedServerConfig {
|
|
|
36
37
|
adminConsoleEnabled: boolean;
|
|
37
38
|
adminConsolePublicDir?: string;
|
|
38
39
|
adminConsolePrefillToken: boolean;
|
|
40
|
+
readinessOverride: boolean;
|
|
39
41
|
}
|
|
40
42
|
declare function parseServerConfig(raw: Partial<ServerConfig["server"]>, options?: {
|
|
41
43
|
portSource?: string;
|
|
@@ -43,6 +45,32 @@ declare function parseServerConfig(raw: Partial<ServerConfig["server"]>, options
|
|
|
43
45
|
declare function loadConfigFile(configPath: string): ServerConfig;
|
|
44
46
|
declare function mergeRemnicConfigForServer(fileRemnic: Record<string, unknown>, envRemnic: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
45
47
|
declare function createAdminControls(configPath: string, config: PluginConfig, serverConfig: ParsedServerConfig): RemnicAdminControls;
|
|
48
|
+
interface StartupReadinessState {
|
|
49
|
+
ready: boolean;
|
|
50
|
+
warmupAttempts: number;
|
|
51
|
+
lastError?: string | null;
|
|
52
|
+
}
|
|
53
|
+
type StartupReadinessOutcome = "warmed" | "cancelled" | "overridden" | "search-disabled";
|
|
54
|
+
declare function runStartupSearchWarmup(options: {
|
|
55
|
+
signal: AbortSignal;
|
|
56
|
+
isAvailable: () => boolean;
|
|
57
|
+
search: (onDegradation: (code: string) => void) => Promise<unknown>;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
declare function completeStartupReadiness(options: {
|
|
60
|
+
deferredReady: Promise<void>;
|
|
61
|
+
warmup: (signal: AbortSignal) => Promise<unknown>;
|
|
62
|
+
prepareWarmup?: (signal: AbortSignal) => Promise<boolean>;
|
|
63
|
+
state: StartupReadinessState;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
retryIntervalMs?: number;
|
|
66
|
+
override?: boolean;
|
|
67
|
+
skipWarmup?: () => boolean;
|
|
68
|
+
openGate: () => void;
|
|
69
|
+
shutdownSignal?: AbortSignal;
|
|
70
|
+
warn?: (message: string) => void;
|
|
71
|
+
info?: (message: string) => void;
|
|
72
|
+
error?: (message: string) => void;
|
|
73
|
+
}): Promise<StartupReadinessOutcome>;
|
|
46
74
|
interface ServerResult {
|
|
47
75
|
config: PluginConfig;
|
|
48
76
|
service: EngramAccessService;
|
|
@@ -64,4 +92,4 @@ declare function startServer(options?: {
|
|
|
64
92
|
}): Promise<ServerResult>;
|
|
65
93
|
declare function cliMain(argv?: string[]): Promise<void>;
|
|
66
94
|
|
|
67
|
-
export { type ParsedServerConfig, type ServerConfig, type ServerResult, cliMain, createAdminControls, loadConfigFile, mergeRemnicConfigForServer, parseServerConfig, startServer };
|
|
95
|
+
export { type ParsedServerConfig, type ServerConfig, type ServerResult, type StartupReadinessOutcome, type StartupReadinessState, cliMain, completeStartupReadiness, createAdminControls, loadConfigFile, mergeRemnicConfigForServer, parseServerConfig, runStartupSearchWarmup, startServer };
|
package/dist/index.js
CHANGED
|
@@ -56,7 +56,8 @@ function parseServerConfig(raw, options) {
|
|
|
56
56
|
maxBodyBytes: parseOptionalPositiveInteger(raw.maxBodyBytes, "server.maxBodyBytes"),
|
|
57
57
|
adminConsoleEnabled: parseOptionalBoolean(raw.adminConsoleEnabled, "server.adminConsoleEnabled") ?? false,
|
|
58
58
|
adminConsolePublicDir: parseOptionalString(raw.adminConsolePublicDir, "server.adminConsolePublicDir"),
|
|
59
|
-
adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, "server.adminConsolePrefillToken") ?? false
|
|
59
|
+
adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, "server.adminConsolePrefillToken") ?? false,
|
|
60
|
+
readinessOverride: parseOptionalBoolean(raw.readinessOverride, "server.readinessOverride") ?? false
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
function resolveUserPath(value) {
|
|
@@ -133,12 +134,14 @@ function envOverrides() {
|
|
|
133
134
|
const adminConsoleEnabled = readCompatEnv("REMNIC_ADMIN_CONSOLE_ENABLED", "ENGRAM_ADMIN_CONSOLE_ENABLED");
|
|
134
135
|
const adminConsolePublicDir = readCompatEnv("REMNIC_ADMIN_CONSOLE_PUBLIC_DIR", "ENGRAM_ADMIN_CONSOLE_PUBLIC_DIR");
|
|
135
136
|
const adminConsolePrefillToken = readCompatEnv("REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN", "ENGRAM_ADMIN_CONSOLE_PREFILL_TOKEN");
|
|
137
|
+
const readinessOverride = process.env.REMNIC_READY_OVERRIDE;
|
|
136
138
|
if (port) overrides.port = port;
|
|
137
139
|
if (host) overrides.host = host;
|
|
138
140
|
if (authToken) overrides.authToken = authToken;
|
|
139
141
|
if (adminConsoleEnabled) overrides.adminConsoleEnabled = adminConsoleEnabled;
|
|
140
142
|
if (adminConsolePublicDir) overrides.adminConsolePublicDir = adminConsolePublicDir;
|
|
141
143
|
if (adminConsolePrefillToken) overrides.adminConsolePrefillToken = adminConsolePrefillToken;
|
|
144
|
+
if (readinessOverride !== void 0) overrides.readinessOverride = readinessOverride;
|
|
142
145
|
if (process.env.OPENAI_API_KEY) remnic.openaiApiKey = process.env.OPENAI_API_KEY;
|
|
143
146
|
const memoryDir = readCompatEnv("REMNIC_MEMORY_DIR", "ENGRAM_MEMORY_DIR");
|
|
144
147
|
if (memoryDir) remnic.memoryDir = memoryDir;
|
|
@@ -529,14 +532,141 @@ function createAdminControls(configPath, config, serverConfig) {
|
|
|
529
532
|
}
|
|
530
533
|
function abortableDelay(ms, signal) {
|
|
531
534
|
if (signal.aborted) return Promise.resolve();
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
535
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
536
|
+
const timer = setTimeout(resolve, ms);
|
|
537
|
+
const onAbort = () => {
|
|
538
|
+
clearTimeout(timer);
|
|
539
|
+
resolve();
|
|
540
|
+
};
|
|
541
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
542
|
+
return promise.finally(() => signal.removeEventListener("abort", onAbort));
|
|
543
|
+
}
|
|
544
|
+
var STARTUP_WARMUP_TIMEOUT_MS = 2e4;
|
|
545
|
+
var STARTUP_WARMUP_RETRY_INTERVAL_MS = 3e4;
|
|
546
|
+
var StartupWarmupDegradationError = class extends Error {
|
|
547
|
+
constructor(code) {
|
|
548
|
+
super(`startup search degraded: ${code}`);
|
|
549
|
+
this.name = "StartupWarmupDegradationError";
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
var StartupSyncPendingError = class extends Error {
|
|
553
|
+
constructor() {
|
|
554
|
+
super("startup search sync is not complete");
|
|
555
|
+
this.name = "StartupSyncPendingError";
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
async function runStartupSearchWarmup(options) {
|
|
559
|
+
let degradationCode;
|
|
560
|
+
await options.search((code) => {
|
|
561
|
+
degradationCode = code;
|
|
539
562
|
});
|
|
563
|
+
if (options.signal.aborted) return;
|
|
564
|
+
if (degradationCode) throw new StartupWarmupDegradationError(degradationCode);
|
|
565
|
+
if (!options.isAvailable()) {
|
|
566
|
+
throw new StartupWarmupDegradationError("backend_unavailable");
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async function completeStartupReadiness(options) {
|
|
570
|
+
const timeoutMs = options.timeoutMs ?? STARTUP_WARMUP_TIMEOUT_MS;
|
|
571
|
+
const retryIntervalMs = options.retryIntervalMs ?? STARTUP_WARMUP_RETRY_INTERVAL_MS;
|
|
572
|
+
const warn = options.warn ?? ((message) => log.warn(message));
|
|
573
|
+
const info = options.info ?? ((message) => log.info(message));
|
|
574
|
+
const error = options.error ?? ((message) => log.error(message));
|
|
575
|
+
options.state.ready = false;
|
|
576
|
+
options.state.lastError = null;
|
|
577
|
+
if (options.override) {
|
|
578
|
+
options.openGate();
|
|
579
|
+
options.state.ready = true;
|
|
580
|
+
error(
|
|
581
|
+
"CRITICAL: emergency readiness override enabled; exposing a cold search backend to traffic"
|
|
582
|
+
);
|
|
583
|
+
return "overridden";
|
|
584
|
+
}
|
|
585
|
+
if (options.skipWarmup?.()) {
|
|
586
|
+
options.openGate();
|
|
587
|
+
options.state.ready = true;
|
|
588
|
+
info("Standalone init gate opened without search warm-up (search intentionally disabled)");
|
|
589
|
+
return "search-disabled";
|
|
590
|
+
}
|
|
591
|
+
let removeDeferredShutdownListener = () => void 0;
|
|
592
|
+
const deferredShutdown = new Promise((resolve) => {
|
|
593
|
+
if (options.shutdownSignal?.aborted) {
|
|
594
|
+
resolve("shutdown");
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const onDeferredShutdown = () => resolve("shutdown");
|
|
598
|
+
options.shutdownSignal?.addEventListener("abort", onDeferredShutdown, { once: true });
|
|
599
|
+
removeDeferredShutdownListener = () => options.shutdownSignal?.removeEventListener("abort", onDeferredShutdown);
|
|
600
|
+
});
|
|
601
|
+
try {
|
|
602
|
+
const deferredOutcome = await Promise.race([
|
|
603
|
+
options.deferredReady.then(() => "ready"),
|
|
604
|
+
deferredShutdown
|
|
605
|
+
]);
|
|
606
|
+
if (deferredOutcome === "shutdown") return "cancelled";
|
|
607
|
+
} catch (err) {
|
|
608
|
+
if (options.shutdownSignal?.aborted) return "cancelled";
|
|
609
|
+
options.state.lastError = err instanceof Error ? err.name : typeof err;
|
|
610
|
+
warn(`Standalone deferred initialization failed; warm-up retries will continue: ${err}`);
|
|
611
|
+
} finally {
|
|
612
|
+
removeDeferredShutdownListener();
|
|
613
|
+
}
|
|
614
|
+
if (options.shutdownSignal?.aborted) return "cancelled";
|
|
615
|
+
const lifecycleAbort = new AbortController();
|
|
616
|
+
const onShutdown = () => lifecycleAbort.abort(options.shutdownSignal?.reason);
|
|
617
|
+
options.shutdownSignal?.addEventListener("abort", onShutdown, { once: true });
|
|
618
|
+
try {
|
|
619
|
+
while (!lifecycleAbort.signal.aborted) {
|
|
620
|
+
if (options.skipWarmup?.()) {
|
|
621
|
+
options.openGate();
|
|
622
|
+
options.state.ready = true;
|
|
623
|
+
info("Standalone init gate opened without search warm-up (search intentionally disabled)");
|
|
624
|
+
return "search-disabled";
|
|
625
|
+
}
|
|
626
|
+
options.state.warmupAttempts += 1;
|
|
627
|
+
const warmupAbort = new AbortController();
|
|
628
|
+
const onLifecycleAbort = () => warmupAbort.abort(lifecycleAbort.signal.reason);
|
|
629
|
+
lifecycleAbort.signal.addEventListener("abort", onLifecycleAbort, { once: true });
|
|
630
|
+
const timeout = Promise.withResolvers();
|
|
631
|
+
let timedOut = false;
|
|
632
|
+
const timer = setTimeout(() => {
|
|
633
|
+
timedOut = true;
|
|
634
|
+
warmupAbort.abort();
|
|
635
|
+
timeout.reject(new Error(`startup warm-up timed out after ${timeoutMs}ms`));
|
|
636
|
+
}, timeoutMs);
|
|
637
|
+
timer.unref();
|
|
638
|
+
try {
|
|
639
|
+
const attempt = async () => {
|
|
640
|
+
if (options.prepareWarmup && !await options.prepareWarmup(warmupAbort.signal)) {
|
|
641
|
+
throw new StartupSyncPendingError();
|
|
642
|
+
}
|
|
643
|
+
return options.warmup(warmupAbort.signal);
|
|
644
|
+
};
|
|
645
|
+
await Promise.race([attempt(), timeout.promise]);
|
|
646
|
+
if (lifecycleAbort.signal.aborted) return "cancelled";
|
|
647
|
+
options.state.lastError = null;
|
|
648
|
+
options.openGate();
|
|
649
|
+
options.state.ready = true;
|
|
650
|
+
info(
|
|
651
|
+
`Standalone init gate opened after search warm-up attempt ${options.state.warmupAttempts}`
|
|
652
|
+
);
|
|
653
|
+
return "warmed";
|
|
654
|
+
} catch (err) {
|
|
655
|
+
if (lifecycleAbort.signal.aborted) return "cancelled";
|
|
656
|
+
options.state.lastError = timedOut ? "TimeoutError" : err instanceof Error ? err.name : typeof err;
|
|
657
|
+
warn(
|
|
658
|
+
timedOut ? `Standalone startup warm-up attempt ${options.state.warmupAttempts} timed out after ${timeoutMs}ms; retrying in ${retryIntervalMs}ms` : `Standalone startup warm-up attempt ${options.state.warmupAttempts} failed (${options.state.lastError}); retrying in ${retryIntervalMs}ms`
|
|
659
|
+
);
|
|
660
|
+
} finally {
|
|
661
|
+
clearTimeout(timer);
|
|
662
|
+
lifecycleAbort.signal.removeEventListener("abort", onLifecycleAbort);
|
|
663
|
+
}
|
|
664
|
+
await abortableDelay(retryIntervalMs, lifecycleAbort.signal);
|
|
665
|
+
}
|
|
666
|
+
return "cancelled";
|
|
667
|
+
} finally {
|
|
668
|
+
options.shutdownSignal?.removeEventListener("abort", onShutdown);
|
|
669
|
+
}
|
|
540
670
|
}
|
|
541
671
|
async function cleanupFailedStartup(orchestrator, httpServer) {
|
|
542
672
|
try {
|
|
@@ -572,6 +702,7 @@ async function startServer(options) {
|
|
|
572
702
|
const orchestrator = new Orchestrator(config);
|
|
573
703
|
await orchestrator.initialize();
|
|
574
704
|
const service = new EngramAccessService(orchestrator);
|
|
705
|
+
const readiness = { ready: false, warmupAttempts: 0, lastError: null };
|
|
575
706
|
const authToken = parsedServerConfig.authToken ?? readCompatEnv("REMNIC_AUTH_TOKEN", "ENGRAM_AUTH_TOKEN") ?? "";
|
|
576
707
|
if (!authToken && getAllValidTokens().length === 0) {
|
|
577
708
|
log.warn("No auth token set \u2014 server will reject all requests. Set REMNIC_AUTH_TOKEN, server.authToken in config, or generate tokens with 'remnic token generate'.");
|
|
@@ -582,6 +713,7 @@ async function startServer(options) {
|
|
|
582
713
|
port: parsedServerConfig.port,
|
|
583
714
|
authToken: authToken || void 0,
|
|
584
715
|
authTokensGetter: () => getAllValidTokensCached(),
|
|
716
|
+
readiness: () => readiness,
|
|
585
717
|
principal: parsedServerConfig.principal,
|
|
586
718
|
maxBodyBytes: parsedServerConfig.maxBodyBytes,
|
|
587
719
|
adminConsoleEnabled: parsedServerConfig.adminConsoleEnabled,
|
|
@@ -601,17 +733,63 @@ async function startServer(options) {
|
|
|
601
733
|
throw err;
|
|
602
734
|
}
|
|
603
735
|
const startupSyncAbort = new AbortController();
|
|
736
|
+
const readinessAbort = new AbortController();
|
|
737
|
+
let startupSyncInFlight;
|
|
738
|
+
const ensureStartupSync = async (signal) => {
|
|
739
|
+
if (orchestrator.deferredSyncSucceeded) return true;
|
|
740
|
+
if (startupSyncInFlight) return startupSyncInFlight;
|
|
741
|
+
const attempt = orchestrator.startupSearchSync(signal).then((synced) => {
|
|
742
|
+
if (synced) orchestrator.deferredSyncSucceeded = true;
|
|
743
|
+
return synced;
|
|
744
|
+
});
|
|
745
|
+
startupSyncInFlight = attempt;
|
|
746
|
+
try {
|
|
747
|
+
return await attempt;
|
|
748
|
+
} finally {
|
|
749
|
+
if (startupSyncInFlight === attempt) startupSyncInFlight = void 0;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
const readinessTask = completeStartupReadiness({
|
|
753
|
+
deferredReady: orchestrator.deferredReady,
|
|
754
|
+
warmup: (signal) => runStartupSearchWarmup({
|
|
755
|
+
signal,
|
|
756
|
+
isAvailable: () => orchestrator.qmd.isAvailable(),
|
|
757
|
+
search: (onDegradation) => orchestrator.qmd.search(
|
|
758
|
+
"remnic startup readiness",
|
|
759
|
+
config.defaultNamespace,
|
|
760
|
+
1,
|
|
761
|
+
void 0,
|
|
762
|
+
{
|
|
763
|
+
signal,
|
|
764
|
+
onDegradation: (degradation) => onDegradation(degradation.code)
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
}),
|
|
768
|
+
prepareWarmup: ensureStartupSync,
|
|
769
|
+
state: readiness,
|
|
770
|
+
override: parsedServerConfig.readinessOverride,
|
|
771
|
+
skipWarmup: () => orchestrator.qmd.debugStatus() === "backend=noop",
|
|
772
|
+
openGate: () => {
|
|
773
|
+
readiness.ready = true;
|
|
774
|
+
},
|
|
775
|
+
shutdownSignal: readinessAbort.signal
|
|
776
|
+
});
|
|
604
777
|
const originalStop = httpServer.stop.bind(httpServer);
|
|
605
778
|
let stopPromise;
|
|
606
779
|
const stop = async () => {
|
|
607
780
|
if (stopPromise) return stopPromise;
|
|
608
781
|
stopPromise = (async () => {
|
|
609
782
|
startupSyncAbort.abort();
|
|
783
|
+
readinessAbort.abort();
|
|
610
784
|
orchestrator.abortDeferredInit();
|
|
611
785
|
try {
|
|
612
786
|
await originalStop();
|
|
613
787
|
} finally {
|
|
614
|
-
|
|
788
|
+
try {
|
|
789
|
+
await readinessTask;
|
|
790
|
+
} finally {
|
|
791
|
+
await orchestrator.destroy();
|
|
792
|
+
}
|
|
615
793
|
}
|
|
616
794
|
})();
|
|
617
795
|
return stopPromise;
|
|
@@ -643,7 +821,7 @@ async function startServer(options) {
|
|
|
643
821
|
log.debug("QMD startup-sync retry: cancelled by shutdown");
|
|
644
822
|
return;
|
|
645
823
|
}
|
|
646
|
-
const synced = await
|
|
824
|
+
const synced = await ensureStartupSync(startupSyncAbort.signal);
|
|
647
825
|
if (!synced) {
|
|
648
826
|
if (orchestrator.qmd.debugStatus() === "backend=noop") {
|
|
649
827
|
log.debug("QMD startup-sync retry: search intentionally disabled; stopping retries");
|
|
@@ -751,10 +929,12 @@ if (process.argv[1] && /(?:remnic-server|engram-server)[\\/](?:dist|src)[\\/]ind
|
|
|
751
929
|
}
|
|
752
930
|
export {
|
|
753
931
|
cliMain,
|
|
932
|
+
completeStartupReadiness,
|
|
754
933
|
createAdminControls,
|
|
755
934
|
loadConfigFile,
|
|
756
935
|
mergeRemnicConfigForServer,
|
|
757
936
|
parseServerConfig,
|
|
937
|
+
runStartupSearchWarmup,
|
|
758
938
|
startServer
|
|
759
939
|
};
|
|
760
940
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @remnic/server\n *\n * Standalone Remnic memory server.\n *\n * Loads config from `remnic.config.json` (or env vars), creates an Orchestrator,\n * and starts the HTTP access server with MCP endpoint — no OpenClaw required.\n *\n * Usage:\n * npx --package @remnic/server remnic-server\n * npx --package @remnic/server remnic-server --config ./my-remnic.json\n * npx --package @remnic/server remnic-server --port 4320\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseConfig, isOpenaiApiKeyDisabled, resolveRemnicConfigRecord, Orchestrator, EngramAccessService, EngramAccessHttpServer, initLogger, log, getAllValidTokens, getAllValidTokensCached, expandTildePath, type PluginConfig, type RemnicAdminControls, type RemnicAdminDashboardStatus, type RemnicAdminModelOption, type RemnicAdminConfigPatch } from \"@remnic/core\";\n\n// ── Config loading ──────────────────────────────────────────────────────────\n\nexport interface ServerConfig {\n remnic: Record<string, unknown>;\n server: {\n host?: string;\n port?: unknown;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n adminConsoleEnabled?: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken?: boolean;\n };\n}\n\nfunction readCompatEnv(primary: string, legacy: string): string | undefined {\n return process.env[primary] ?? process.env[legacy];\n}\n\nfunction parseServerPort(value: unknown, source: string): number {\n const port = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof port !== \"number\" ||\n !Number.isInteger(port) ||\n port < 1 ||\n port > 65535\n ) {\n throw new Error(`Invalid ${source}: expected an integer port from 1 to 65535`);\n }\n return port;\n}\n\nfunction parseOptionalString(value: unknown, source: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected a string`);\n }\n return value;\n}\n\nfunction parseOptionalNonEmptyString(value: unknown, source: string): string | undefined {\n const parsed = parseOptionalString(value, source);\n if (parsed === undefined) return undefined;\n if (parsed.trim() === \"\") {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return parsed;\n}\n\nfunction parseOptionalPositiveInteger(value: unknown, source: string): number | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof parsed !== \"number\" ||\n !Number.isInteger(parsed) ||\n parsed < 1\n ) {\n throw new Error(`Invalid ${source}: expected a positive integer`);\n }\n return parsed;\n}\n\nfunction parseOptionalBoolean(value: unknown, source: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean`);\n}\n\nexport interface ParsedServerConfig {\n host: string;\n port: number;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n adminConsoleEnabled: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken: boolean;\n}\n\nexport function parseServerConfig(\n raw: Partial<ServerConfig[\"server\"]>,\n options?: { portSource?: string },\n): ParsedServerConfig {\n return {\n host: parseOptionalNonEmptyString(raw.host, \"server.host\") ?? \"127.0.0.1\",\n port: raw.port === undefined\n ? 4318\n : parseServerPort(raw.port, options?.portSource ?? \"server.port\"),\n authToken: parseOptionalString(raw.authToken, \"server.authToken\"),\n principal: parseOptionalString(raw.principal, \"server.principal\"),\n maxBodyBytes: parseOptionalPositiveInteger(raw.maxBodyBytes, \"server.maxBodyBytes\"),\n adminConsoleEnabled: parseOptionalBoolean(raw.adminConsoleEnabled, \"server.adminConsoleEnabled\") ?? false,\n adminConsolePublicDir: parseOptionalString(raw.adminConsolePublicDir, \"server.adminConsolePublicDir\"),\n adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, \"server.adminConsolePrefillToken\") ?? false,\n };\n}\n\ninterface ResolvedConfigPath {\n path: string;\n explicit: boolean;\n source: string;\n}\n\nfunction resolveUserPath(value: string): string {\n return path.resolve(expandTildePath(value));\n}\n\nfunction resolveConfigPath(cliPath?: string): ResolvedConfigPath {\n if (cliPath) {\n return { path: resolveUserPath(cliPath), explicit: true, source: \"--config\" };\n }\n\n const envPath = readCompatEnv(\"REMNIC_CONFIG_PATH\", \"ENGRAM_CONFIG_PATH\");\n if (envPath) {\n return { path: resolveUserPath(envPath), explicit: true, source: \"REMNIC_CONFIG_PATH/ENGRAM_CONFIG_PATH\" };\n }\n\n const homeDir = process.env.HOME ?? \"~\";\n const candidates = [\n path.join(process.cwd(), \"remnic.config.json\"),\n path.join(process.cwd(), \"engram.config.json\"),\n path.join(homeDir, \".config\", \"remnic\", \"config.json\"),\n path.join(homeDir, \".config\", \"engram\", \"config.json\"),\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return { path: candidate, explicit: false, source: \"auto-discovery\" };\n }\n }\n\n return { path: path.join(homeDir, \".config\", \"remnic\", \"config.json\"), explicit: false, source: \"auto-discovery\" };\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requirePlainConfigBlock(\n raw: Record<string, unknown>,\n key: \"remnic\" | \"engram\" | \"server\",\n configPath: string,\n): Record<string, unknown> | undefined {\n const value = raw[key];\n if (value === undefined) return undefined;\n if (!isPlainRecord(value)) {\n throw new Error(`Invalid config file ${configPath}: ${key} must be a JSON object`);\n }\n return value;\n}\n\nexport function loadConfigFile(configPath: string): ServerConfig {\n const raw = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(raw)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n requirePlainConfigBlock(raw, \"remnic\", configPath);\n requirePlainConfigBlock(raw, \"engram\", configPath);\n const server = requirePlainConfigBlock(raw, \"server\", configPath);\n return {\n remnic: resolveRemnicConfigRecord(raw),\n server: server ?? {},\n };\n}\n\nfunction loadResolvedConfig(resolved: ResolvedConfigPath): ServerConfig {\n if (!fs.existsSync(resolved.path)) {\n if (resolved.explicit) {\n throw new Error(`Config file from ${resolved.source} not found: ${resolved.path}`);\n }\n return { remnic: {}, server: {} };\n }\n\n const stat = fs.statSync(resolved.path);\n if (!stat.isFile()) {\n if (!resolved.explicit) {\n return { remnic: {}, server: {} };\n }\n throw new Error(`Config file from ${resolved.source} is not a regular file: ${resolved.path}`);\n }\n\n return loadConfigFile(resolved.path);\n}\n\nfunction envOverrides(): Partial<ServerConfig[\"server\"]> & { remnic?: Record<string, unknown> } {\n const overrides: Record<string, unknown> = {};\n const remnic: Record<string, unknown> = {};\n\n const port = readCompatEnv(\"REMNIC_PORT\", \"ENGRAM_PORT\");\n const host = readCompatEnv(\"REMNIC_HOST\", \"ENGRAM_HOST\");\n const authToken = readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\");\n const adminConsoleEnabled = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_ENABLED\", \"ENGRAM_ADMIN_CONSOLE_ENABLED\");\n const adminConsolePublicDir = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PUBLIC_DIR\", \"ENGRAM_ADMIN_CONSOLE_PUBLIC_DIR\");\n const adminConsolePrefillToken = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\", \"ENGRAM_ADMIN_CONSOLE_PREFILL_TOKEN\");\n if (port) overrides.port = port;\n if (host) overrides.host = host;\n if (authToken) overrides.authToken = authToken;\n if (adminConsoleEnabled) overrides.adminConsoleEnabled = adminConsoleEnabled;\n if (adminConsolePublicDir) overrides.adminConsolePublicDir = adminConsolePublicDir;\n if (adminConsolePrefillToken) overrides.adminConsolePrefillToken = adminConsolePrefillToken;\n\n if (process.env.OPENAI_API_KEY) remnic.openaiApiKey = process.env.OPENAI_API_KEY;\n const memoryDir = readCompatEnv(\"REMNIC_MEMORY_DIR\", \"ENGRAM_MEMORY_DIR\");\n if (memoryDir) remnic.memoryDir = memoryDir;\n\n return { ...overrides, ...(Object.keys(remnic).length > 0 ? { remnic } : {}) };\n}\n\nexport function mergeRemnicConfigForServer(\n fileRemnic: Record<string, unknown>,\n envRemnic: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const effectiveEnvRemnic = { ...(envRemnic ?? {}) };\n if (isOpenaiApiKeyDisabled(fileRemnic.openaiApiKey)) {\n // A local/gateway-only deployment can explicitly disable the direct\n // OpenAI client. Preserve that opt-out even when the process has a\n // global OPENAI_API_KEY for unrelated tools.\n delete effectiveEnvRemnic.openaiApiKey;\n }\n return { ...fileRemnic, ...effectiveEnvRemnic };\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst WRITABLE_BOOLEAN_CONFIG_KEYS = new Set([\n \"citationsAutoDetect\",\n \"citationsEnabled\",\n \"embeddingFallbackEnabled\",\n \"enrichmentAutoOnCreate\",\n \"enrichmentEnabled\",\n \"feedbackEnabled\",\n \"hostEmbeddingProviderEnabled\",\n \"localLlmDisableThinking\",\n \"localLlmEnabled\",\n \"localLlmFallback\",\n \"localLlmFastEnabled\",\n \"memoryExtensionsEnabled\",\n \"namespacesEnabled\",\n \"qmdEnabled\",\n \"queryExpansionEnabled\",\n \"recallPlannerEnabled\",\n \"recallPlannerLlmEnabled\",\n \"recallPlannerTelemetryEnabled\",\n \"rerankEnabled\",\n]);\n\nconst WRITABLE_STRING_CONFIG_KEYS = new Set([\n \"embeddingFallbackModel\",\n \"embeddingFallbackProvider\",\n \"fastGatewayAgentId\",\n \"gatewayAgentId\",\n \"localLlmFastModel\",\n \"localLlmModel\",\n \"localLlmUrl\",\n \"model\",\n \"modelSource\",\n \"openaiBaseUrl\",\n \"qmdEmbedModel\",\n \"qmdGenerateModel\",\n \"qmdRerankModel\",\n \"recallPlannerModel\",\n]);\n\nfunction isWritableConfigKey(key: string): boolean {\n return WRITABLE_BOOLEAN_CONFIG_KEYS.has(key) || WRITABLE_STRING_CONFIG_KEYS.has(key);\n}\n\nfunction hasEnv(name: string): boolean {\n const value = process.env[name];\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction fileExists(candidate: string): boolean {\n try {\n return fs.existsSync(expandTildePath(candidate));\n } catch {\n return false;\n }\n}\n\nfunction canWriteConfigPath(configPath: string): boolean {\n try {\n if (fs.existsSync(configPath)) {\n fs.accessSync(configPath, fs.constants.W_OK);\n return true;\n }\n fs.accessSync(path.dirname(configPath), fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction writeConfigFileAtomically(configPath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const tmpPath = path.join(\n path.dirname(configPath),\n `.${path.basename(configPath)}.tmp-${process.pid}-${Date.now()}`,\n );\n let completed = false;\n try {\n fs.writeFileSync(tmpPath, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n mode: 0o600,\n });\n fs.renameSync(tmpPath, configPath);\n try {\n fs.chmodSync(configPath, 0o600);\n } catch {\n // Best effort for platforms/filesystems that do not support chmod.\n }\n completed = true;\n } finally {\n if (!completed) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best effort cleanup for failed writes.\n }\n }\n }\n}\n\nfunction readJsonRecordIfPresent(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(parsed)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n return parsed;\n}\n\nfunction resolveEditableRemnicBlock(root: Record<string, unknown>): Record<string, unknown> {\n if (isPlainRecord(root.remnic)) return root.remnic;\n if (isPlainRecord(root.engram)) return root.engram;\n return root;\n}\n\nfunction normalizePatchValue(key: string, value: unknown): string | boolean | null {\n if (!isWritableConfigKey(key)) {\n throw new Error(`Unsupported admin config key: ${key}`);\n }\n if (value === null) return null;\n if (WRITABLE_BOOLEAN_CONFIG_KEYS.has(key)) {\n if (typeof value === \"boolean\") return value;\n throw new Error(`Invalid ${key}: expected boolean or null`);\n }\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${key}: expected string or null`);\n }\n const normalized = value.trim();\n if (key === \"modelSource\" && normalized !== \"plugin\" && normalized !== \"gateway\") {\n throw new Error(\"Invalid modelSource: expected plugin or gateway\");\n }\n if (\n key === \"embeddingFallbackProvider\" &&\n normalized !== \"auto\" &&\n normalized !== \"openai\" &&\n normalized !== \"local\"\n ) {\n throw new Error(\"Invalid embeddingFallbackProvider: expected auto, openai, or local\");\n }\n return normalized;\n}\n\nfunction publicConfigValues(config: PluginConfig, serverConfig: ParsedServerConfig): Record<string, string | number | boolean | null> {\n return {\n adminConsoleEnabled: serverConfig.adminConsoleEnabled,\n memoryDir: config.memoryDir,\n model: config.model,\n modelSource: config.modelSource,\n gatewayAgentId: config.gatewayAgentId || null,\n fastGatewayAgentId: config.fastGatewayAgentId || null,\n localLlmEnabled: config.localLlmEnabled,\n localLlmUrl: config.localLlmUrl || null,\n localLlmModel: config.localLlmModel || null,\n localLlmFastEnabled: config.localLlmFastEnabled,\n localLlmFastModel: config.localLlmFastModel || null,\n localLlmFallback: config.localLlmFallback,\n localLlmDisableThinking: config.localLlmDisableThinking,\n qmdEnabled: config.qmdEnabled,\n qmdEmbedModel: config.qmdEmbedModel || null,\n qmdRerankModel: config.qmdRerankModel || null,\n qmdGenerateModel: config.qmdGenerateModel || null,\n embeddingFallbackEnabled: config.embeddingFallbackEnabled,\n embeddingFallbackProvider: config.embeddingFallbackProvider,\n embeddingFallbackModel: config.embeddingFallbackModel || null,\n hostEmbeddingProviderEnabled: config.hostEmbeddingProviderEnabled,\n namespacesEnabled: config.namespacesEnabled,\n recallPlannerEnabled: config.recallPlannerEnabled,\n recallPlannerLlmEnabled: config.recallPlannerLlmEnabled,\n recallPlannerModel: config.recallPlannerModel || null,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n queryExpansionEnabled: config.queryExpansionEnabled,\n rerankEnabled: config.rerankEnabled,\n feedbackEnabled: config.feedbackEnabled,\n memoryExtensionsEnabled: config.memoryExtensionsEnabled,\n enrichmentEnabled: config.enrichmentEnabled,\n enrichmentAutoOnCreate: config.enrichmentAutoOnCreate,\n };\n}\n\nfunction configuredModels(config: PluginConfig): RemnicAdminModelOption[] {\n const models = new Map<string, RemnicAdminModelOption>();\n const add = (id: string | undefined, provider: string, label: string, enabled: boolean, isDefault = false, source = \"config\") => {\n const normalized = id?.trim();\n if (!normalized) return;\n const existing = models.get(`${provider}:${normalized}`);\n models.set(`${provider}:${normalized}`, {\n id: normalized,\n provider,\n label,\n detected: existing?.detected ?? true,\n enabled: existing?.enabled || enabled,\n default: existing?.default || isDefault,\n source,\n });\n };\n\n add(config.model, \"openai\", config.model, !isOpenaiApiKeyDisabled(config.openaiApiKey), config.modelSource === \"plugin\");\n add(config.gatewayAgentId, \"gateway\", config.gatewayAgentId, config.modelSource === \"gateway\", config.modelSource === \"gateway\");\n add(config.fastGatewayAgentId, \"gateway\", config.fastGatewayAgentId, config.modelSource === \"gateway\");\n add(config.localLlmModel, \"local\", config.localLlmModel, config.localLlmEnabled, config.localLlmEnabled);\n add(config.localLlmFastModel, \"local\", `${config.localLlmFastModel} (fast)`, config.localLlmFastEnabled);\n add(config.embeddingFallbackModel, config.embeddingFallbackProvider, `${config.embeddingFallbackModel} (embedding fallback)`, config.embeddingFallbackEnabled);\n add(config.qmdEmbedModel, \"qmd\", `${config.qmdEmbedModel} (embed)`, config.qmdEnabled);\n add(config.qmdRerankModel, \"qmd\", `${config.qmdRerankModel} (rerank)`, config.qmdEnabled);\n add(config.qmdGenerateModel, \"qmd\", `${config.qmdGenerateModel} (generate)`, config.qmdEnabled);\n add(config.recallPlannerModel, \"planner\", `${config.recallPlannerModel} (planner)`, config.recallPlannerLlmEnabled);\n\n return [...models.values()].sort((a, b) => `${a.provider}:${a.id}`.localeCompare(`${b.provider}:${b.id}`));\n}\n\nfunction isLikelyOllamaEndpoint(endpoint: string): boolean {\n return /(?:ollama|11434)/i.test(endpoint);\n}\n\nasync function detectOllamaModels(baseUrl: string | undefined): Promise<RemnicAdminModelOption[]> {\n const configuredEndpoint = baseUrl?.trim();\n const envEndpoint = process.env.OLLAMA_HOST?.trim();\n const endpoint =\n configuredEndpoint && isLikelyOllamaEndpoint(configuredEndpoint)\n ? configuredEndpoint\n : envEndpoint;\n if (!endpoint) return [];\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 900);\n try {\n const url = new URL(\"/api/tags\", endpoint.endsWith(\"/\") ? endpoint : `${endpoint}/`);\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) return [];\n const payload = await response.json() as { models?: Array<{ name?: unknown; model?: unknown }> };\n return (payload.models ?? [])\n .map((model) => typeof model.name === \"string\" ? model.name : typeof model.model === \"string\" ? model.model : \"\")\n .filter((name) => name.length > 0)\n .map((name) => ({\n id: name,\n label: name,\n provider: \"ollama\",\n detected: true,\n enabled: true,\n source: \"ollama\",\n endpoint,\n }));\n } catch {\n return [];\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction dashboardHarnesses(config: PluginConfig) {\n const openclawDetected = hasEnv(\"OPENCLAW_HOME\") || hasEnv(\"OPENCLAW_WORKSPACE\") || fileExists(\"~/.openclaw\");\n const codexDetected = hasEnv(\"CODEX_HOME\") || fileExists(\"~/.codex/auth.json\") || fileExists(\"~/.codex\");\n return [\n {\n id: \"remnic-http\",\n label: \"Remnic HTTP API\",\n detected: true,\n enabled: true,\n source: \"server\",\n detail: \"MCP and REST access server\",\n },\n {\n id: \"openclaw\",\n label: \"OpenClaw\",\n detected: openclawDetected,\n enabled: config.modelSource === \"gateway\" || config.hostEmbeddingProviderEnabled,\n source: openclawDetected ? \"host\" : \"not detected\",\n detail: \"Gateway and host adapters\",\n },\n {\n id: \"codex\",\n label: \"Codex\",\n detected: codexDetected,\n enabled: config.citationsEnabled || config.citationsAutoDetect,\n source: codexDetected ? \"host\" : \"not detected\",\n detail: \"Citation-aware adapter\",\n },\n {\n id: \"qmd\",\n label: \"QMD Search\",\n detected: Boolean(config.qmdPath) || config.qmdEnabled,\n enabled: config.qmdEnabled,\n source: config.qmdPath ? \"qmdPath\" : \"config\",\n detail: config.qmdSearchStrategy,\n },\n ];\n}\n\nfunction dashboardProviders(config: PluginConfig) {\n const localLlmUrl = config.localLlmUrl || \"\";\n const gatewayIds = [config.gatewayAgentId, config.fastGatewayAgentId].filter(Boolean).join(\" \");\n const openaiBaseUrl = process.env.OPENAI_BASE_URL || \"\";\n const sageRouterDetected =\n hasEnv(\"SAGE_ROUTER_URL\") ||\n hasEnv(\"SAGE_ROUTER_HOST\") ||\n /sage[-_ ]?router/i.test(`${gatewayIds} ${openaiBaseUrl}`);\n const ollamaDetected = hasEnv(\"OLLAMA_HOST\") || /(?:ollama|11434)/i.test(localLlmUrl);\n const localDetected = Boolean(localLlmUrl.trim());\n\n return [\n {\n id: \"openai\",\n label: \"OpenAI\",\n detected: !isOpenaiApiKeyDisabled(config.openaiApiKey),\n enabled: config.modelSource === \"plugin\" && !isOpenaiApiKeyDisabled(config.openaiApiKey),\n source: !isOpenaiApiKeyDisabled(config.openaiApiKey) ? \"config/env\" : \"disabled\",\n detail: config.model,\n },\n {\n id: \"sage-router\",\n label: \"Sage Router\",\n detected: sageRouterDetected,\n enabled: config.modelSource === \"gateway\" && sageRouterDetected,\n source: sageRouterDetected ? \"gateway/env\" : \"not detected\",\n detail: config.gatewayAgentId || config.fastGatewayAgentId || openaiBaseUrl || \"OpenAI-compatible provider router\",\n },\n {\n id: \"ollama\",\n label: \"Ollama\",\n detected: ollamaDetected,\n enabled: config.localLlmEnabled && ollamaDetected,\n source: ollamaDetected ? \"localLlmUrl/OLLAMA_HOST\" : \"not detected\",\n detail: \"Local or cloud-compatible Ollama endpoint\",\n },\n {\n id: \"local-openai-compatible\",\n label: \"Local OpenAI-compatible\",\n detected: localDetected && !ollamaDetected,\n enabled: config.localLlmEnabled && localDetected && !ollamaDetected,\n source: localDetected ? \"localLlmUrl\" : \"not detected\",\n detail: localLlmUrl || \"Local provider endpoint\",\n },\n ];\n}\n\nfunction dashboardFeatures(config: PluginConfig) {\n return [\n [\"localLlmEnabled\", \"Local LLM\", config.localLlmEnabled],\n [\"localLlmFastEnabled\", \"Fast Local Tier\", config.localLlmFastEnabled],\n [\"qmdEnabled\", \"QMD Search\", config.qmdEnabled],\n [\"embeddingFallbackEnabled\", \"Embedding Fallback\", config.embeddingFallbackEnabled],\n [\"hostEmbeddingProviderEnabled\", \"Host Embeddings\", config.hostEmbeddingProviderEnabled],\n [\"namespacesEnabled\", \"Namespaces\", config.namespacesEnabled],\n [\"recallPlannerEnabled\", \"Recall Planner\", config.recallPlannerEnabled],\n [\"recallPlannerLlmEnabled\", \"Planner LLM\", config.recallPlannerLlmEnabled],\n [\"citationsEnabled\", \"Citations\", config.citationsEnabled],\n [\"citationsAutoDetect\", \"Citation Auto-detect\", config.citationsAutoDetect],\n [\"queryExpansionEnabled\", \"Query Expansion\", config.queryExpansionEnabled],\n [\"rerankEnabled\", \"Rerank\", config.rerankEnabled],\n [\"feedbackEnabled\", \"Feedback\", config.feedbackEnabled],\n [\"memoryExtensionsEnabled\", \"Memory Extensions\", config.memoryExtensionsEnabled],\n [\"enrichmentEnabled\", \"Entity Enrichment\", config.enrichmentEnabled],\n ].map(([key, label, enabled]) => ({\n key: String(key),\n label: String(label),\n enabled: enabled === true,\n writable: WRITABLE_BOOLEAN_CONFIG_KEYS.has(String(key)),\n restartRequired: true,\n }));\n}\n\nexport function createAdminControls(\n configPath: string,\n config: PluginConfig,\n serverConfig: ParsedServerConfig,\n): RemnicAdminControls {\n let restartRequired = false;\n let displayConfig = config;\n const status = async (): Promise<RemnicAdminDashboardStatus> => {\n const models = configuredModels(displayConfig);\n const ollamaModels = await detectOllamaModels(displayConfig.localLlmUrl);\n const modelKeys = new Set(models.map((model) => `${model.provider}:${model.id}`));\n for (const model of ollamaModels) {\n if (!modelKeys.has(`${model.provider}:${model.id}`)) models.push(model);\n }\n return {\n config: {\n path: configPath,\n exists: fs.existsSync(configPath),\n writable: canWriteConfigPath(configPath),\n restartRequired,\n values: publicConfigValues(displayConfig, serverConfig),\n },\n harnesses: dashboardHarnesses(displayConfig),\n providers: dashboardProviders(displayConfig),\n models,\n features: dashboardFeatures(displayConfig),\n };\n };\n\n return {\n status,\n update: async (patch: RemnicAdminConfigPatch): Promise<RemnicAdminDashboardStatus> => {\n if (!isPlainRecord(patch)) {\n throw new Error(\"Admin config patch must be an object\");\n }\n const normalizedEntries = Object.entries(patch).map(([key, value]) => [key, normalizePatchValue(key, value)] as const);\n if (normalizedEntries.length === 0) return status();\n\n const raw = readJsonRecordIfPresent(configPath);\n const target = resolveEditableRemnicBlock(raw);\n for (const [key, value] of normalizedEntries) {\n if (value === null) {\n delete target[key];\n if (target !== raw) delete raw[key];\n } else {\n target[key] = value;\n }\n }\n\n const nextDisplayConfig = parseConfig(resolveRemnicConfigRecord(raw));\n\n writeConfigFileAtomically(configPath, raw);\n displayConfig = nextDisplayConfig;\n restartRequired = true;\n return status();\n },\n };\n}\n\n/**\n * Like `setTimeout` wrapped in a Promise, but respects an `AbortSignal`.\n * Resolves immediately (without throwing) when the signal fires so the\n * caller can check `signal.aborted` and exit cleanly.\n */\nfunction abortableDelay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nasync function cleanupFailedStartup(\n orchestrator: Orchestrator,\n httpServer: EngramAccessHttpServer,\n): Promise<void> {\n try {\n await httpServer.stop();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not stop server: ${err}`);\n }\n\n try {\n await orchestrator.destroy();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not destroy orchestrator: ${err}`);\n }\n}\n\n// ── Server startup ──────────────────────────────────────────────────────────\n\nexport interface ServerResult {\n config: PluginConfig;\n service: EngramAccessService;\n httpServer: EngramAccessHttpServer;\n host: string;\n port: number;\n /** Stop HTTP, cancel startup work, abort deferred init, and destroy the orchestrator. */\n stop: () => Promise<void>;\n /** Cancel any pending startup-sync retry timers. Called automatically on shutdown. */\n cancelStartupSync: () => void;\n /** Abort deferred orchestrator initialization (QMD sync, warmup, cache). */\n abortDeferredInit: () => void;\n}\n\nexport async function startServer(options?: {\n configPath?: string;\n host?: string;\n port?: number;\n authToken?: string;\n}): Promise<ServerResult> {\n initLogger();\n\n const resolvedConfigPath = resolveConfigPath(options?.configPath);\n const fileConfig = loadResolvedConfig(resolvedConfigPath);\n\n const env = envOverrides();\n const { remnic: envRemnic, ...envServer } = env;\n\n // Merge: file < env < cli flags\n const remnicConfig = mergeRemnicConfigForServer(fileConfig.remnic, envRemnic);\n const cliServerConfig: Partial<ServerConfig[\"server\"]> = {};\n if (options?.host !== undefined) cliServerConfig.host = options.host;\n if (options?.port !== undefined) cliServerConfig.port = parseServerPort(options.port, \"options.port\");\n if (options?.authToken !== undefined) cliServerConfig.authToken = options.authToken;\n\n const serverConfig = {\n ...fileConfig.server,\n ...envServer,\n ...cliServerConfig,\n };\n const portSource = cliServerConfig.port !== undefined\n ? \"options.port\"\n : envServer.port !== undefined\n ? \"REMNIC_PORT/ENGRAM_PORT\"\n : \"server.port\";\n const parsedServerConfig = parseServerConfig(serverConfig, { portSource });\n\n const config = parseConfig(remnicConfig);\n const orchestrator = new Orchestrator(config);\n await orchestrator.initialize();\n\n // Start the HTTP server immediately so health checks, MCP handshakes,\n // and liveness probes can connect while deferred init is still running.\n const service = new EngramAccessService(orchestrator);\n\n const authToken = parsedServerConfig.authToken ?? readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\") ?? \"\";\n\n // Connector tokens are loaded dynamically per request via authTokensGetter\n // so that token generate/revoke takes effect without server restart\n if (!authToken && getAllValidTokens().length === 0) {\n log.warn(\"No auth token set — server will reject all requests. Set REMNIC_AUTH_TOKEN, server.authToken in config, or generate tokens with 'remnic token generate'.\");\n }\n\n const httpServer = new EngramAccessHttpServer({\n service,\n host: parsedServerConfig.host,\n port: parsedServerConfig.port,\n authToken: authToken || undefined,\n authTokensGetter: () => getAllValidTokensCached(),\n principal: parsedServerConfig.principal,\n maxBodyBytes: parsedServerConfig.maxBodyBytes,\n adminConsoleEnabled: parsedServerConfig.adminConsoleEnabled,\n adminConsolePublicDir: parsedServerConfig.adminConsolePublicDir\n ? path.resolve(expandTildePath(parsedServerConfig.adminConsolePublicDir))\n : undefined,\n adminConsolePrefillToken: parsedServerConfig.adminConsolePrefillToken,\n adminControls: parsedServerConfig.adminConsoleEnabled\n ? createAdminControls(resolvedConfigPath.path, config, parsedServerConfig)\n : undefined,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n emitLegacyTools: config.emitLegacyTools,\n });\n\n let host: string;\n let port: number;\n try {\n ({ host, port } = await httpServer.start());\n } catch (err) {\n await cleanupFailedStartup(orchestrator, httpServer);\n throw err;\n }\n\n // Fire-and-forget: wait for deferred init (QMD probe, collection setup,\n // warmup) then check QMD availability and retry if needed. This does NOT\n // block the server listener — connections are accepted immediately above.\n // An AbortController allows the shutdown handler to cancel pending retries.\n const startupSyncAbort = new AbortController();\n\n // Wrap httpServer.stop() so that existing callers also get full lifecycle\n // cleanup: retry timers, deferred init, HTTP listener, and orchestrator.\n const originalStop = httpServer.stop.bind(httpServer);\n let stopPromise: Promise<void> | undefined;\n const stop = async (): Promise<void> => {\n if (stopPromise) return stopPromise;\n stopPromise = (async () => {\n startupSyncAbort.abort();\n orchestrator.abortDeferredInit();\n try {\n await originalStop();\n } finally {\n await orchestrator.destroy();\n }\n })();\n return stopPromise;\n };\n httpServer.stop = stop;\n\n orchestrator.deferredReady.then(() => {\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync: cancelled before deferred init completed\");\n return;\n }\n\n // Skip retries when search is explicitly disabled via config or when the\n // orchestrator already resolved to a noop backend (e.g. missing collection\n // detected during deferredInitialize). Both cases mean no sync should ever\n // run; scheduling retries would create misleading operational noise and\n // unnecessary background work on every server start.\n if (!config.qmdEnabled || orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync: search disabled or noop backend, skipping retries\");\n return;\n }\n\n // Retry when either: (a) QMD is not available yet (cold-start race), or\n // (b) QMD is available but the deferred init sync step failed silently\n // (e.g., update errors swallowed by backend, throttle skip, transient\n // network failure). Without (b), the daemon permanently serves stale\n // recall after a failed sync despite healthy QMD probe.\n const needsRetry = !orchestrator.qmd.isAvailable() || !orchestrator.deferredSyncSucceeded;\n if (!needsRetry) {\n log.debug(\"QMD startup-sync: deferred init completed successfully, no retries needed\");\n return;\n }\n\n const RETRY_DELAYS_MS = [5_000, 15_000, 30_000, 60_000, 120_000];\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled before retry task started\");\n return;\n }\n (async () => {\n for (const delay of RETRY_DELAYS_MS) {\n await abortableDelay(delay, startupSyncAbort.signal);\n\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled by shutdown\");\n return;\n }\n\n const synced = await orchestrator.startupSearchSync(startupSyncAbort.signal);\n if (!synced) {\n if (orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync retry: search intentionally disabled; stopping retries\");\n return;\n }\n log.debug(`QMD startup-sync retry: not available yet (next retry in ${RETRY_DELAYS_MS[RETRY_DELAYS_MS.indexOf(delay) + 1] ?? \"n/a\"}ms)`);\n continue;\n }\n\n return; // sync succeeded, stop retrying\n }\n\n log.warn(\"QMD startup-sync retry: exhausted all retries; search index may be stale\");\n })().catch((err: unknown) => {\n log.warn(`QMD startup-sync retry: unexpected error: ${err}`);\n });\n }).catch((err: unknown) => {\n log.warn(`Deferred init error: ${err}`);\n });\n\n return { config, service, httpServer, host, port, stop, cancelStartupSync: () => startupSyncAbort.abort(), abortDeferredInit: () => orchestrator.abortDeferredInit() };\n}\n\n// ── CLI entry point ──────────────────────────────────────────────────────────\n\nconst BOOLEAN_CLI_OPTIONS = new Set([\"help\"]);\nconst VALUE_CLI_OPTIONS = new Set([\"config\", \"host\", \"port\", \"auth-token\"]);\n\nfunction parseCliArgs(argv: string[]): Record<string, string | undefined> {\n const args: Record<string, string | undefined> = {};\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (token === \"-h\") {\n args.help = \"true\";\n continue;\n }\n\n if (token.startsWith(\"--\")) {\n const [key, inlineValue] = token.slice(2).split(/=(.*)/s, 2);\n if (!key) {\n throw new Error(`Invalid option ${token}`);\n }\n\n if (BOOLEAN_CLI_OPTIONS.has(key)) {\n if (inlineValue !== undefined) {\n throw new Error(`Option --${key} does not accept a value`);\n }\n args[key] = \"true\";\n continue;\n }\n\n if (!VALUE_CLI_OPTIONS.has(key)) {\n throw new Error(`Unknown option --${key}`);\n }\n\n const value = inlineValue ?? argv[i + 1];\n if (\n value === undefined ||\n (inlineValue === undefined && value.startsWith(\"--\")) ||\n value.trim() === \"\"\n ) {\n throw new Error(`Missing value for --${key}`);\n }\n\n args[key] = value;\n if (inlineValue === undefined) i++;\n }\n }\n return args;\n}\n\nexport async function cliMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n const args = parseCliArgs(argv);\n\n if (args.help) {\n console.log(`\nremnic-server — Standalone Remnic memory server\n\nUsage:\n remnic-server [options]\n\nOptions:\n --config <path> Path to config file (default: remnic.config.json)\n --host <addr> Bind address (default: 127.0.0.1)\n --port <number> Port number (default: 4318)\n --auth-token <tok> Bearer token for auth (or set REMNIC_AUTH_TOKEN)\n --help Show this help\n\nEnvironment:\n REMNIC_CONFIG_PATH Config file path (ENGRAM_CONFIG_PATH also supported)\n REMNIC_PORT Server port (ENGRAM_PORT also supported)\n REMNIC_HOST Bind address (ENGRAM_HOST also supported)\n REMNIC_AUTH_TOKEN Auth bearer token (ENGRAM_AUTH_TOKEN also supported)\n REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\n Prefill admin UI with REMNIC_AUTH_TOKEN when true\n REMNIC_MEMORY_DIR Override memory directory (ENGRAM_MEMORY_DIR also supported)\n OPENAI_API_KEY OpenAI API key for extraction; ignored when config sets openaiApiKey=false\n`);\n process.exit(0);\n }\n\n const result = await startServer({\n configPath: args.config,\n host: args.host,\n port: args.port === undefined ? undefined : parseServerPort(args.port, \"--port\"),\n authToken: args[\"auth-token\"],\n });\n\n console.log(`Remnic server listening on http://${result.host}:${result.port}`);\n\n // Graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\nReceived ${signal}, shutting down...`);\n await result.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n}\n\n// Auto-run when executed directly\n// Matches direct execution of `node .../remnic-server/dist/index.js` or\n// `node .../remnic-server/src/index.ts`. Package command names are handled by\n// the bin wrappers in ../bin so importing this module cannot start twice.\nif (\n process.argv[1] &&\n /(?:remnic-server|engram-server)[\\\\/](?:dist|src)[\\\\/]index\\.[jt]s$/.test(process.argv[1])\n) {\n cliMain().catch((err) => {\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @remnic/server\n *\n * Standalone Remnic memory server.\n *\n * Loads config from `remnic.config.json` (or env vars), creates an Orchestrator,\n * and starts the HTTP access server with MCP endpoint — no OpenClaw required.\n *\n * Usage:\n * npx --package @remnic/server remnic-server\n * npx --package @remnic/server remnic-server --config ./my-remnic.json\n * npx --package @remnic/server remnic-server --port 4320\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseConfig, isOpenaiApiKeyDisabled, resolveRemnicConfigRecord, Orchestrator, EngramAccessService, EngramAccessHttpServer, initLogger, log, getAllValidTokens, getAllValidTokensCached, expandTildePath, type PluginConfig, type RemnicAdminControls, type RemnicAdminDashboardStatus, type RemnicAdminModelOption, type RemnicAdminConfigPatch } from \"@remnic/core\";\n\n// ── Config loading ──────────────────────────────────────────────────────────\n\nexport interface ServerConfig {\n remnic: Record<string, unknown>;\n server: {\n host?: string;\n port?: unknown;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n adminConsoleEnabled?: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken?: boolean;\n readinessOverride?: boolean;\n };\n}\n\nfunction readCompatEnv(primary: string, legacy: string): string | undefined {\n return process.env[primary] ?? process.env[legacy];\n}\n\nfunction parseServerPort(value: unknown, source: string): number {\n const port = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof port !== \"number\" ||\n !Number.isInteger(port) ||\n port < 1 ||\n port > 65535\n ) {\n throw new Error(`Invalid ${source}: expected an integer port from 1 to 65535`);\n }\n return port;\n}\n\nfunction parseOptionalString(value: unknown, source: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected a string`);\n }\n return value;\n}\n\nfunction parseOptionalNonEmptyString(value: unknown, source: string): string | undefined {\n const parsed = parseOptionalString(value, source);\n if (parsed === undefined) return undefined;\n if (parsed.trim() === \"\") {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return parsed;\n}\n\nfunction parseOptionalPositiveInteger(value: unknown, source: string): number | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof parsed !== \"number\" ||\n !Number.isInteger(parsed) ||\n parsed < 1\n ) {\n throw new Error(`Invalid ${source}: expected a positive integer`);\n }\n return parsed;\n}\n\nfunction parseOptionalBoolean(value: unknown, source: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean`);\n}\n\nexport interface ParsedServerConfig {\n host: string;\n port: number;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n adminConsoleEnabled: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken: boolean;\n readinessOverride: boolean;\n}\n\nexport function parseServerConfig(\n raw: Partial<ServerConfig[\"server\"]>,\n options?: { portSource?: string },\n): ParsedServerConfig {\n return {\n host: parseOptionalNonEmptyString(raw.host, \"server.host\") ?? \"127.0.0.1\",\n port: raw.port === undefined\n ? 4318\n : parseServerPort(raw.port, options?.portSource ?? \"server.port\"),\n authToken: parseOptionalString(raw.authToken, \"server.authToken\"),\n principal: parseOptionalString(raw.principal, \"server.principal\"),\n maxBodyBytes: parseOptionalPositiveInteger(raw.maxBodyBytes, \"server.maxBodyBytes\"),\n adminConsoleEnabled: parseOptionalBoolean(raw.adminConsoleEnabled, \"server.adminConsoleEnabled\") ?? false,\n adminConsolePublicDir: parseOptionalString(raw.adminConsolePublicDir, \"server.adminConsolePublicDir\"),\n adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, \"server.adminConsolePrefillToken\") ?? false,\n readinessOverride: parseOptionalBoolean(raw.readinessOverride, \"server.readinessOverride\") ?? false,\n };\n}\n\ninterface ResolvedConfigPath {\n path: string;\n explicit: boolean;\n source: string;\n}\n\nfunction resolveUserPath(value: string): string {\n return path.resolve(expandTildePath(value));\n}\n\nfunction resolveConfigPath(cliPath?: string): ResolvedConfigPath {\n if (cliPath) {\n return { path: resolveUserPath(cliPath), explicit: true, source: \"--config\" };\n }\n\n const envPath = readCompatEnv(\"REMNIC_CONFIG_PATH\", \"ENGRAM_CONFIG_PATH\");\n if (envPath) {\n return { path: resolveUserPath(envPath), explicit: true, source: \"REMNIC_CONFIG_PATH/ENGRAM_CONFIG_PATH\" };\n }\n\n const homeDir = process.env.HOME ?? \"~\";\n const candidates = [\n path.join(process.cwd(), \"remnic.config.json\"),\n path.join(process.cwd(), \"engram.config.json\"),\n path.join(homeDir, \".config\", \"remnic\", \"config.json\"),\n path.join(homeDir, \".config\", \"engram\", \"config.json\"),\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return { path: candidate, explicit: false, source: \"auto-discovery\" };\n }\n }\n\n return { path: path.join(homeDir, \".config\", \"remnic\", \"config.json\"), explicit: false, source: \"auto-discovery\" };\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requirePlainConfigBlock(\n raw: Record<string, unknown>,\n key: \"remnic\" | \"engram\" | \"server\",\n configPath: string,\n): Record<string, unknown> | undefined {\n const value = raw[key];\n if (value === undefined) return undefined;\n if (!isPlainRecord(value)) {\n throw new Error(`Invalid config file ${configPath}: ${key} must be a JSON object`);\n }\n return value;\n}\n\nexport function loadConfigFile(configPath: string): ServerConfig {\n const raw = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(raw)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n requirePlainConfigBlock(raw, \"remnic\", configPath);\n requirePlainConfigBlock(raw, \"engram\", configPath);\n const server = requirePlainConfigBlock(raw, \"server\", configPath);\n return {\n remnic: resolveRemnicConfigRecord(raw),\n server: server ?? {},\n };\n}\n\nfunction loadResolvedConfig(resolved: ResolvedConfigPath): ServerConfig {\n if (!fs.existsSync(resolved.path)) {\n if (resolved.explicit) {\n throw new Error(`Config file from ${resolved.source} not found: ${resolved.path}`);\n }\n return { remnic: {}, server: {} };\n }\n\n const stat = fs.statSync(resolved.path);\n if (!stat.isFile()) {\n if (!resolved.explicit) {\n return { remnic: {}, server: {} };\n }\n throw new Error(`Config file from ${resolved.source} is not a regular file: ${resolved.path}`);\n }\n\n return loadConfigFile(resolved.path);\n}\n\nfunction envOverrides(): Partial<ServerConfig[\"server\"]> & { remnic?: Record<string, unknown> } {\n const overrides: Record<string, unknown> = {};\n const remnic: Record<string, unknown> = {};\n\n const port = readCompatEnv(\"REMNIC_PORT\", \"ENGRAM_PORT\");\n const host = readCompatEnv(\"REMNIC_HOST\", \"ENGRAM_HOST\");\n const authToken = readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\");\n const adminConsoleEnabled = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_ENABLED\", \"ENGRAM_ADMIN_CONSOLE_ENABLED\");\n const adminConsolePublicDir = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PUBLIC_DIR\", \"ENGRAM_ADMIN_CONSOLE_PUBLIC_DIR\");\n const adminConsolePrefillToken = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\", \"ENGRAM_ADMIN_CONSOLE_PREFILL_TOKEN\");\n const readinessOverride = process.env.REMNIC_READY_OVERRIDE;\n if (port) overrides.port = port;\n if (host) overrides.host = host;\n if (authToken) overrides.authToken = authToken;\n if (adminConsoleEnabled) overrides.adminConsoleEnabled = adminConsoleEnabled;\n if (adminConsolePublicDir) overrides.adminConsolePublicDir = adminConsolePublicDir;\n if (adminConsolePrefillToken) overrides.adminConsolePrefillToken = adminConsolePrefillToken;\n if (readinessOverride !== undefined) overrides.readinessOverride = readinessOverride;\n\n if (process.env.OPENAI_API_KEY) remnic.openaiApiKey = process.env.OPENAI_API_KEY;\n const memoryDir = readCompatEnv(\"REMNIC_MEMORY_DIR\", \"ENGRAM_MEMORY_DIR\");\n if (memoryDir) remnic.memoryDir = memoryDir;\n\n return { ...overrides, ...(Object.keys(remnic).length > 0 ? { remnic } : {}) };\n}\n\nexport function mergeRemnicConfigForServer(\n fileRemnic: Record<string, unknown>,\n envRemnic: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const effectiveEnvRemnic = { ...(envRemnic ?? {}) };\n if (isOpenaiApiKeyDisabled(fileRemnic.openaiApiKey)) {\n // A local/gateway-only deployment can explicitly disable the direct\n // OpenAI client. Preserve that opt-out even when the process has a\n // global OPENAI_API_KEY for unrelated tools.\n delete effectiveEnvRemnic.openaiApiKey;\n }\n return { ...fileRemnic, ...effectiveEnvRemnic };\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst WRITABLE_BOOLEAN_CONFIG_KEYS = new Set([\n \"citationsAutoDetect\",\n \"citationsEnabled\",\n \"embeddingFallbackEnabled\",\n \"enrichmentAutoOnCreate\",\n \"enrichmentEnabled\",\n \"feedbackEnabled\",\n \"hostEmbeddingProviderEnabled\",\n \"localLlmDisableThinking\",\n \"localLlmEnabled\",\n \"localLlmFallback\",\n \"localLlmFastEnabled\",\n \"memoryExtensionsEnabled\",\n \"namespacesEnabled\",\n \"qmdEnabled\",\n \"queryExpansionEnabled\",\n \"recallPlannerEnabled\",\n \"recallPlannerLlmEnabled\",\n \"recallPlannerTelemetryEnabled\",\n \"rerankEnabled\",\n]);\n\nconst WRITABLE_STRING_CONFIG_KEYS = new Set([\n \"embeddingFallbackModel\",\n \"embeddingFallbackProvider\",\n \"fastGatewayAgentId\",\n \"gatewayAgentId\",\n \"localLlmFastModel\",\n \"localLlmModel\",\n \"localLlmUrl\",\n \"model\",\n \"modelSource\",\n \"openaiBaseUrl\",\n \"qmdEmbedModel\",\n \"qmdGenerateModel\",\n \"qmdRerankModel\",\n \"recallPlannerModel\",\n]);\n\nfunction isWritableConfigKey(key: string): boolean {\n return WRITABLE_BOOLEAN_CONFIG_KEYS.has(key) || WRITABLE_STRING_CONFIG_KEYS.has(key);\n}\n\nfunction hasEnv(name: string): boolean {\n const value = process.env[name];\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction fileExists(candidate: string): boolean {\n try {\n return fs.existsSync(expandTildePath(candidate));\n } catch {\n return false;\n }\n}\n\nfunction canWriteConfigPath(configPath: string): boolean {\n try {\n if (fs.existsSync(configPath)) {\n fs.accessSync(configPath, fs.constants.W_OK);\n return true;\n }\n fs.accessSync(path.dirname(configPath), fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction writeConfigFileAtomically(configPath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const tmpPath = path.join(\n path.dirname(configPath),\n `.${path.basename(configPath)}.tmp-${process.pid}-${Date.now()}`,\n );\n let completed = false;\n try {\n fs.writeFileSync(tmpPath, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n mode: 0o600,\n });\n fs.renameSync(tmpPath, configPath);\n try {\n fs.chmodSync(configPath, 0o600);\n } catch {\n // Best effort for platforms/filesystems that do not support chmod.\n }\n completed = true;\n } finally {\n if (!completed) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best effort cleanup for failed writes.\n }\n }\n }\n}\n\nfunction readJsonRecordIfPresent(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(parsed)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n return parsed;\n}\n\nfunction resolveEditableRemnicBlock(root: Record<string, unknown>): Record<string, unknown> {\n if (isPlainRecord(root.remnic)) return root.remnic;\n if (isPlainRecord(root.engram)) return root.engram;\n return root;\n}\n\nfunction normalizePatchValue(key: string, value: unknown): string | boolean | null {\n if (!isWritableConfigKey(key)) {\n throw new Error(`Unsupported admin config key: ${key}`);\n }\n if (value === null) return null;\n if (WRITABLE_BOOLEAN_CONFIG_KEYS.has(key)) {\n if (typeof value === \"boolean\") return value;\n throw new Error(`Invalid ${key}: expected boolean or null`);\n }\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${key}: expected string or null`);\n }\n const normalized = value.trim();\n if (key === \"modelSource\" && normalized !== \"plugin\" && normalized !== \"gateway\") {\n throw new Error(\"Invalid modelSource: expected plugin or gateway\");\n }\n if (\n key === \"embeddingFallbackProvider\" &&\n normalized !== \"auto\" &&\n normalized !== \"openai\" &&\n normalized !== \"local\"\n ) {\n throw new Error(\"Invalid embeddingFallbackProvider: expected auto, openai, or local\");\n }\n return normalized;\n}\n\nfunction publicConfigValues(config: PluginConfig, serverConfig: ParsedServerConfig): Record<string, string | number | boolean | null> {\n return {\n adminConsoleEnabled: serverConfig.adminConsoleEnabled,\n memoryDir: config.memoryDir,\n model: config.model,\n modelSource: config.modelSource,\n gatewayAgentId: config.gatewayAgentId || null,\n fastGatewayAgentId: config.fastGatewayAgentId || null,\n localLlmEnabled: config.localLlmEnabled,\n localLlmUrl: config.localLlmUrl || null,\n localLlmModel: config.localLlmModel || null,\n localLlmFastEnabled: config.localLlmFastEnabled,\n localLlmFastModel: config.localLlmFastModel || null,\n localLlmFallback: config.localLlmFallback,\n localLlmDisableThinking: config.localLlmDisableThinking,\n qmdEnabled: config.qmdEnabled,\n qmdEmbedModel: config.qmdEmbedModel || null,\n qmdRerankModel: config.qmdRerankModel || null,\n qmdGenerateModel: config.qmdGenerateModel || null,\n embeddingFallbackEnabled: config.embeddingFallbackEnabled,\n embeddingFallbackProvider: config.embeddingFallbackProvider,\n embeddingFallbackModel: config.embeddingFallbackModel || null,\n hostEmbeddingProviderEnabled: config.hostEmbeddingProviderEnabled,\n namespacesEnabled: config.namespacesEnabled,\n recallPlannerEnabled: config.recallPlannerEnabled,\n recallPlannerLlmEnabled: config.recallPlannerLlmEnabled,\n recallPlannerModel: config.recallPlannerModel || null,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n queryExpansionEnabled: config.queryExpansionEnabled,\n rerankEnabled: config.rerankEnabled,\n feedbackEnabled: config.feedbackEnabled,\n memoryExtensionsEnabled: config.memoryExtensionsEnabled,\n enrichmentEnabled: config.enrichmentEnabled,\n enrichmentAutoOnCreate: config.enrichmentAutoOnCreate,\n };\n}\n\nfunction configuredModels(config: PluginConfig): RemnicAdminModelOption[] {\n const models = new Map<string, RemnicAdminModelOption>();\n const add = (id: string | undefined, provider: string, label: string, enabled: boolean, isDefault = false, source = \"config\") => {\n const normalized = id?.trim();\n if (!normalized) return;\n const existing = models.get(`${provider}:${normalized}`);\n models.set(`${provider}:${normalized}`, {\n id: normalized,\n provider,\n label,\n detected: existing?.detected ?? true,\n enabled: existing?.enabled || enabled,\n default: existing?.default || isDefault,\n source,\n });\n };\n\n add(config.model, \"openai\", config.model, !isOpenaiApiKeyDisabled(config.openaiApiKey), config.modelSource === \"plugin\");\n add(config.gatewayAgentId, \"gateway\", config.gatewayAgentId, config.modelSource === \"gateway\", config.modelSource === \"gateway\");\n add(config.fastGatewayAgentId, \"gateway\", config.fastGatewayAgentId, config.modelSource === \"gateway\");\n add(config.localLlmModel, \"local\", config.localLlmModel, config.localLlmEnabled, config.localLlmEnabled);\n add(config.localLlmFastModel, \"local\", `${config.localLlmFastModel} (fast)`, config.localLlmFastEnabled);\n add(config.embeddingFallbackModel, config.embeddingFallbackProvider, `${config.embeddingFallbackModel} (embedding fallback)`, config.embeddingFallbackEnabled);\n add(config.qmdEmbedModel, \"qmd\", `${config.qmdEmbedModel} (embed)`, config.qmdEnabled);\n add(config.qmdRerankModel, \"qmd\", `${config.qmdRerankModel} (rerank)`, config.qmdEnabled);\n add(config.qmdGenerateModel, \"qmd\", `${config.qmdGenerateModel} (generate)`, config.qmdEnabled);\n add(config.recallPlannerModel, \"planner\", `${config.recallPlannerModel} (planner)`, config.recallPlannerLlmEnabled);\n\n return [...models.values()].sort((a, b) => `${a.provider}:${a.id}`.localeCompare(`${b.provider}:${b.id}`));\n}\n\nfunction isLikelyOllamaEndpoint(endpoint: string): boolean {\n return /(?:ollama|11434)/i.test(endpoint);\n}\n\nasync function detectOllamaModels(baseUrl: string | undefined): Promise<RemnicAdminModelOption[]> {\n const configuredEndpoint = baseUrl?.trim();\n const envEndpoint = process.env.OLLAMA_HOST?.trim();\n const endpoint =\n configuredEndpoint && isLikelyOllamaEndpoint(configuredEndpoint)\n ? configuredEndpoint\n : envEndpoint;\n if (!endpoint) return [];\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 900);\n try {\n const url = new URL(\"/api/tags\", endpoint.endsWith(\"/\") ? endpoint : `${endpoint}/`);\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) return [];\n const payload = await response.json() as { models?: Array<{ name?: unknown; model?: unknown }> };\n return (payload.models ?? [])\n .map((model) => typeof model.name === \"string\" ? model.name : typeof model.model === \"string\" ? model.model : \"\")\n .filter((name) => name.length > 0)\n .map((name) => ({\n id: name,\n label: name,\n provider: \"ollama\",\n detected: true,\n enabled: true,\n source: \"ollama\",\n endpoint,\n }));\n } catch {\n return [];\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction dashboardHarnesses(config: PluginConfig) {\n const openclawDetected = hasEnv(\"OPENCLAW_HOME\") || hasEnv(\"OPENCLAW_WORKSPACE\") || fileExists(\"~/.openclaw\");\n const codexDetected = hasEnv(\"CODEX_HOME\") || fileExists(\"~/.codex/auth.json\") || fileExists(\"~/.codex\");\n return [\n {\n id: \"remnic-http\",\n label: \"Remnic HTTP API\",\n detected: true,\n enabled: true,\n source: \"server\",\n detail: \"MCP and REST access server\",\n },\n {\n id: \"openclaw\",\n label: \"OpenClaw\",\n detected: openclawDetected,\n enabled: config.modelSource === \"gateway\" || config.hostEmbeddingProviderEnabled,\n source: openclawDetected ? \"host\" : \"not detected\",\n detail: \"Gateway and host adapters\",\n },\n {\n id: \"codex\",\n label: \"Codex\",\n detected: codexDetected,\n enabled: config.citationsEnabled || config.citationsAutoDetect,\n source: codexDetected ? \"host\" : \"not detected\",\n detail: \"Citation-aware adapter\",\n },\n {\n id: \"qmd\",\n label: \"QMD Search\",\n detected: Boolean(config.qmdPath) || config.qmdEnabled,\n enabled: config.qmdEnabled,\n source: config.qmdPath ? \"qmdPath\" : \"config\",\n detail: config.qmdSearchStrategy,\n },\n ];\n}\n\nfunction dashboardProviders(config: PluginConfig) {\n const localLlmUrl = config.localLlmUrl || \"\";\n const gatewayIds = [config.gatewayAgentId, config.fastGatewayAgentId].filter(Boolean).join(\" \");\n const openaiBaseUrl = process.env.OPENAI_BASE_URL || \"\";\n const sageRouterDetected =\n hasEnv(\"SAGE_ROUTER_URL\") ||\n hasEnv(\"SAGE_ROUTER_HOST\") ||\n /sage[-_ ]?router/i.test(`${gatewayIds} ${openaiBaseUrl}`);\n const ollamaDetected = hasEnv(\"OLLAMA_HOST\") || /(?:ollama|11434)/i.test(localLlmUrl);\n const localDetected = Boolean(localLlmUrl.trim());\n\n return [\n {\n id: \"openai\",\n label: \"OpenAI\",\n detected: !isOpenaiApiKeyDisabled(config.openaiApiKey),\n enabled: config.modelSource === \"plugin\" && !isOpenaiApiKeyDisabled(config.openaiApiKey),\n source: !isOpenaiApiKeyDisabled(config.openaiApiKey) ? \"config/env\" : \"disabled\",\n detail: config.model,\n },\n {\n id: \"sage-router\",\n label: \"Sage Router\",\n detected: sageRouterDetected,\n enabled: config.modelSource === \"gateway\" && sageRouterDetected,\n source: sageRouterDetected ? \"gateway/env\" : \"not detected\",\n detail: config.gatewayAgentId || config.fastGatewayAgentId || openaiBaseUrl || \"OpenAI-compatible provider router\",\n },\n {\n id: \"ollama\",\n label: \"Ollama\",\n detected: ollamaDetected,\n enabled: config.localLlmEnabled && ollamaDetected,\n source: ollamaDetected ? \"localLlmUrl/OLLAMA_HOST\" : \"not detected\",\n detail: \"Local or cloud-compatible Ollama endpoint\",\n },\n {\n id: \"local-openai-compatible\",\n label: \"Local OpenAI-compatible\",\n detected: localDetected && !ollamaDetected,\n enabled: config.localLlmEnabled && localDetected && !ollamaDetected,\n source: localDetected ? \"localLlmUrl\" : \"not detected\",\n detail: localLlmUrl || \"Local provider endpoint\",\n },\n ];\n}\n\nfunction dashboardFeatures(config: PluginConfig) {\n return [\n [\"localLlmEnabled\", \"Local LLM\", config.localLlmEnabled],\n [\"localLlmFastEnabled\", \"Fast Local Tier\", config.localLlmFastEnabled],\n [\"qmdEnabled\", \"QMD Search\", config.qmdEnabled],\n [\"embeddingFallbackEnabled\", \"Embedding Fallback\", config.embeddingFallbackEnabled],\n [\"hostEmbeddingProviderEnabled\", \"Host Embeddings\", config.hostEmbeddingProviderEnabled],\n [\"namespacesEnabled\", \"Namespaces\", config.namespacesEnabled],\n [\"recallPlannerEnabled\", \"Recall Planner\", config.recallPlannerEnabled],\n [\"recallPlannerLlmEnabled\", \"Planner LLM\", config.recallPlannerLlmEnabled],\n [\"citationsEnabled\", \"Citations\", config.citationsEnabled],\n [\"citationsAutoDetect\", \"Citation Auto-detect\", config.citationsAutoDetect],\n [\"queryExpansionEnabled\", \"Query Expansion\", config.queryExpansionEnabled],\n [\"rerankEnabled\", \"Rerank\", config.rerankEnabled],\n [\"feedbackEnabled\", \"Feedback\", config.feedbackEnabled],\n [\"memoryExtensionsEnabled\", \"Memory Extensions\", config.memoryExtensionsEnabled],\n [\"enrichmentEnabled\", \"Entity Enrichment\", config.enrichmentEnabled],\n ].map(([key, label, enabled]) => ({\n key: String(key),\n label: String(label),\n enabled: enabled === true,\n writable: WRITABLE_BOOLEAN_CONFIG_KEYS.has(String(key)),\n restartRequired: true,\n }));\n}\n\nexport function createAdminControls(\n configPath: string,\n config: PluginConfig,\n serverConfig: ParsedServerConfig,\n): RemnicAdminControls {\n let restartRequired = false;\n let displayConfig = config;\n const status = async (): Promise<RemnicAdminDashboardStatus> => {\n const models = configuredModels(displayConfig);\n const ollamaModels = await detectOllamaModels(displayConfig.localLlmUrl);\n const modelKeys = new Set(models.map((model) => `${model.provider}:${model.id}`));\n for (const model of ollamaModels) {\n if (!modelKeys.has(`${model.provider}:${model.id}`)) models.push(model);\n }\n return {\n config: {\n path: configPath,\n exists: fs.existsSync(configPath),\n writable: canWriteConfigPath(configPath),\n restartRequired,\n values: publicConfigValues(displayConfig, serverConfig),\n },\n harnesses: dashboardHarnesses(displayConfig),\n providers: dashboardProviders(displayConfig),\n models,\n features: dashboardFeatures(displayConfig),\n };\n };\n\n return {\n status,\n update: async (patch: RemnicAdminConfigPatch): Promise<RemnicAdminDashboardStatus> => {\n if (!isPlainRecord(patch)) {\n throw new Error(\"Admin config patch must be an object\");\n }\n const normalizedEntries = Object.entries(patch).map(([key, value]) => [key, normalizePatchValue(key, value)] as const);\n if (normalizedEntries.length === 0) return status();\n\n const raw = readJsonRecordIfPresent(configPath);\n const target = resolveEditableRemnicBlock(raw);\n for (const [key, value] of normalizedEntries) {\n if (value === null) {\n delete target[key];\n if (target !== raw) delete raw[key];\n } else {\n target[key] = value;\n }\n }\n\n const nextDisplayConfig = parseConfig(resolveRemnicConfigRecord(raw));\n\n writeConfigFileAtomically(configPath, raw);\n displayConfig = nextDisplayConfig;\n restartRequired = true;\n return status();\n },\n };\n}\n\ninterface PromiseResolvers<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\ntype PromiseConstructorWithResolvers = PromiseConstructor & {\n withResolvers<T>(): PromiseResolvers<T>;\n};\n\n/**\n * Like `setTimeout` wrapped in a Promise, but respects an `AbortSignal`.\n * Resolves immediately (without throwing) when the signal fires so the\n * caller can check `signal.aborted` and exit cleanly.\n */\nfunction abortableDelay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n const { promise, resolve } = (Promise as PromiseConstructorWithResolvers).withResolvers<void>();\n const timer = setTimeout(resolve, ms);\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return promise.finally(() => signal.removeEventListener(\"abort\", onAbort));\n}\n\nconst STARTUP_WARMUP_TIMEOUT_MS = 20_000;\nconst STARTUP_WARMUP_RETRY_INTERVAL_MS = 30_000;\n\nexport interface StartupReadinessState {\n ready: boolean;\n warmupAttempts: number;\n lastError?: string | null;\n}\n\nexport type StartupReadinessOutcome = \"warmed\" | \"cancelled\" | \"overridden\" | \"search-disabled\";\n\nclass StartupWarmupDegradationError extends Error {\n constructor(code: string) {\n super(`startup search degraded: ${code}`);\n this.name = \"StartupWarmupDegradationError\";\n }\n}\n\nclass StartupSyncPendingError extends Error {\n constructor() {\n super(\"startup search sync is not complete\");\n this.name = \"StartupSyncPendingError\";\n }\n}\n\nexport async function runStartupSearchWarmup(options: {\n signal: AbortSignal;\n isAvailable: () => boolean;\n search: (onDegradation: (code: string) => void) => Promise<unknown>;\n}): Promise<void> {\n let degradationCode: string | undefined;\n await options.search((code) => {\n degradationCode = code;\n });\n if (options.signal.aborted) return;\n if (degradationCode) throw new StartupWarmupDegradationError(degradationCode);\n if (!options.isAvailable()) {\n throw new StartupWarmupDegradationError(\"backend_unavailable\");\n }\n}\n\nexport async function completeStartupReadiness(options: {\n deferredReady: Promise<void>;\n warmup: (signal: AbortSignal) => Promise<unknown>;\n prepareWarmup?: (signal: AbortSignal) => Promise<boolean>;\n state: StartupReadinessState;\n timeoutMs?: number;\n retryIntervalMs?: number;\n override?: boolean;\n skipWarmup?: () => boolean;\n openGate: () => void;\n shutdownSignal?: AbortSignal;\n warn?: (message: string) => void;\n info?: (message: string) => void;\n error?: (message: string) => void;\n}): Promise<StartupReadinessOutcome> {\n const timeoutMs = options.timeoutMs ?? STARTUP_WARMUP_TIMEOUT_MS;\n const retryIntervalMs = options.retryIntervalMs ?? STARTUP_WARMUP_RETRY_INTERVAL_MS;\n const warn = options.warn ?? ((message: string) => log.warn(message));\n const info = options.info ?? ((message: string) => log.info(message));\n const error = options.error ?? ((message: string) => log.error(message));\n\n options.state.ready = false;\n options.state.lastError = null;\n if (options.override) {\n options.openGate();\n options.state.ready = true;\n error(\n \"CRITICAL: emergency readiness override enabled; exposing a cold search backend to traffic\",\n );\n return \"overridden\";\n }\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n\n\n let removeDeferredShutdownListener: () => void = () => undefined;\n const deferredShutdown = new Promise<\"shutdown\">((resolve) => {\n if (options.shutdownSignal?.aborted) {\n resolve(\"shutdown\");\n return;\n }\n const onDeferredShutdown = () => resolve(\"shutdown\");\n options.shutdownSignal?.addEventListener(\"abort\", onDeferredShutdown, { once: true });\n removeDeferredShutdownListener = () =>\n options.shutdownSignal?.removeEventListener(\"abort\", onDeferredShutdown);\n });\n try {\n const deferredOutcome = await Promise.race([\n options.deferredReady.then(() => \"ready\" as const),\n deferredShutdown,\n ]);\n if (deferredOutcome === \"shutdown\") return \"cancelled\";\n } catch (err) {\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n options.state.lastError = err instanceof Error ? err.name : typeof err;\n warn(`Standalone deferred initialization failed; warm-up retries will continue: ${err}`);\n } finally {\n removeDeferredShutdownListener();\n }\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n\n const lifecycleAbort = new AbortController();\n const onShutdown = () => lifecycleAbort.abort(options.shutdownSignal?.reason);\n options.shutdownSignal?.addEventListener(\"abort\", onShutdown, { once: true });\n\n try {\n while (!lifecycleAbort.signal.aborted) {\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n options.state.warmupAttempts += 1;\n const warmupAbort = new AbortController();\n const onLifecycleAbort = () => warmupAbort.abort(lifecycleAbort.signal.reason);\n lifecycleAbort.signal.addEventListener(\"abort\", onLifecycleAbort, { once: true });\n const timeout = (Promise as PromiseConstructorWithResolvers).withResolvers<never>();\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n warmupAbort.abort();\n timeout.reject(new Error(`startup warm-up timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n timer.unref();\n\n try {\n const attempt = async () => {\n if (options.prepareWarmup && !await options.prepareWarmup(warmupAbort.signal)) {\n throw new StartupSyncPendingError();\n }\n return options.warmup(warmupAbort.signal);\n };\n await Promise.race([attempt(), timeout.promise]);\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = null;\n options.openGate();\n options.state.ready = true;\n info(\n `Standalone init gate opened after search warm-up attempt ${options.state.warmupAttempts}`,\n );\n return \"warmed\";\n } catch (err) {\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = timedOut\n ? \"TimeoutError\"\n : err instanceof Error\n ? err.name\n : typeof err;\n warn(\n timedOut\n ? `Standalone startup warm-up attempt ${options.state.warmupAttempts} timed out after ${timeoutMs}ms; retrying in ${retryIntervalMs}ms`\n : `Standalone startup warm-up attempt ${options.state.warmupAttempts} failed (${options.state.lastError}); retrying in ${retryIntervalMs}ms`,\n );\n } finally {\n clearTimeout(timer);\n lifecycleAbort.signal.removeEventListener(\"abort\", onLifecycleAbort);\n }\n\n await abortableDelay(retryIntervalMs, lifecycleAbort.signal);\n }\n return \"cancelled\";\n } finally {\n options.shutdownSignal?.removeEventListener(\"abort\", onShutdown);\n }\n}\n\nasync function cleanupFailedStartup(\n orchestrator: Orchestrator,\n httpServer: EngramAccessHttpServer,\n): Promise<void> {\n try {\n await httpServer.stop();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not stop server: ${err}`);\n }\n\n try {\n await orchestrator.destroy();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not destroy orchestrator: ${err}`);\n }\n}\n\n// ── Server startup ──────────────────────────────────────────────────────────\n\nexport interface ServerResult {\n config: PluginConfig;\n service: EngramAccessService;\n httpServer: EngramAccessHttpServer;\n host: string;\n port: number;\n /** Stop HTTP, cancel startup work, abort deferred init, and destroy the orchestrator. */\n stop: () => Promise<void>;\n /** Cancel any pending startup-sync retry timers. Called automatically on shutdown. */\n cancelStartupSync: () => void;\n /** Abort deferred orchestrator initialization (QMD sync, warmup, cache). */\n abortDeferredInit: () => void;\n}\n\nexport async function startServer(options?: {\n configPath?: string;\n host?: string;\n port?: number;\n authToken?: string;\n}): Promise<ServerResult> {\n initLogger();\n\n const resolvedConfigPath = resolveConfigPath(options?.configPath);\n const fileConfig = loadResolvedConfig(resolvedConfigPath);\n\n const env = envOverrides();\n const { remnic: envRemnic, ...envServer } = env;\n\n // Merge: file < env < cli flags\n const remnicConfig = mergeRemnicConfigForServer(fileConfig.remnic, envRemnic);\n const cliServerConfig: Partial<ServerConfig[\"server\"]> = {};\n if (options?.host !== undefined) cliServerConfig.host = options.host;\n if (options?.port !== undefined) cliServerConfig.port = parseServerPort(options.port, \"options.port\");\n if (options?.authToken !== undefined) cliServerConfig.authToken = options.authToken;\n\n const serverConfig = {\n ...fileConfig.server,\n ...envServer,\n ...cliServerConfig,\n };\n const portSource = cliServerConfig.port !== undefined\n ? \"options.port\"\n : envServer.port !== undefined\n ? \"REMNIC_PORT/ENGRAM_PORT\"\n : \"server.port\";\n const parsedServerConfig = parseServerConfig(serverConfig, { portSource });\n\n const config = parseConfig(remnicConfig);\n const orchestrator = new Orchestrator(config);\n await orchestrator.initialize();\n\n // Start the HTTP server immediately so health checks, MCP handshakes,\n // and liveness probes can connect while deferred init is still running.\n const service = new EngramAccessService(orchestrator);\n const readiness: StartupReadinessState = { ready: false, warmupAttempts: 0, lastError: null };\n\n const authToken = parsedServerConfig.authToken ?? readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\") ?? \"\";\n\n // Connector tokens are loaded dynamically per request via authTokensGetter\n // so that token generate/revoke takes effect without server restart\n if (!authToken && getAllValidTokens().length === 0) {\n log.warn(\"No auth token set — server will reject all requests. Set REMNIC_AUTH_TOKEN, server.authToken in config, or generate tokens with 'remnic token generate'.\");\n }\n\n const httpServer = new EngramAccessHttpServer({\n service,\n host: parsedServerConfig.host,\n port: parsedServerConfig.port,\n authToken: authToken || undefined,\n authTokensGetter: () => getAllValidTokensCached(),\n readiness: () => readiness,\n principal: parsedServerConfig.principal,\n maxBodyBytes: parsedServerConfig.maxBodyBytes,\n adminConsoleEnabled: parsedServerConfig.adminConsoleEnabled,\n adminConsolePublicDir: parsedServerConfig.adminConsolePublicDir\n ? path.resolve(expandTildePath(parsedServerConfig.adminConsolePublicDir))\n : undefined,\n adminConsolePrefillToken: parsedServerConfig.adminConsolePrefillToken,\n adminControls: parsedServerConfig.adminConsoleEnabled\n ? createAdminControls(resolvedConfigPath.path, config, parsedServerConfig)\n : undefined,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n emitLegacyTools: config.emitLegacyTools,\n });\n\n let host: string;\n let port: number;\n try {\n ({ host, port } = await httpServer.start());\n } catch (err) {\n await cleanupFailedStartup(orchestrator, httpServer);\n throw err;\n }\n\n // Fire-and-forget: wait for deferred init (QMD probe, collection setup,\n // warmup) then check QMD availability and retry if needed. This does NOT\n // block the server listener — connections are accepted immediately above.\n // An AbortController allows the shutdown handler to cancel pending retries.\n const startupSyncAbort = new AbortController();\n const readinessAbort = new AbortController();\n let startupSyncInFlight: Promise<boolean> | undefined;\n const ensureStartupSync = async (signal: AbortSignal): Promise<boolean> => {\n if (orchestrator.deferredSyncSucceeded) return true;\n if (startupSyncInFlight) return startupSyncInFlight;\n const attempt = orchestrator.startupSearchSync(signal).then((synced) => {\n if (synced) orchestrator.deferredSyncSucceeded = true;\n return synced;\n });\n startupSyncInFlight = attempt;\n try {\n return await attempt;\n } finally {\n if (startupSyncInFlight === attempt) startupSyncInFlight = undefined;\n }\n };\n const readinessTask = completeStartupReadiness({\n deferredReady: orchestrator.deferredReady,\n warmup: (signal) =>\n runStartupSearchWarmup({\n signal,\n isAvailable: () => orchestrator.qmd.isAvailable(),\n search: (onDegradation) =>\n orchestrator.qmd.search(\n \"remnic startup readiness\",\n config.defaultNamespace,\n 1,\n undefined,\n {\n signal,\n onDegradation: (degradation) => onDegradation(degradation.code),\n },\n ),\n }),\n prepareWarmup: ensureStartupSync,\n state: readiness,\n override: parsedServerConfig.readinessOverride,\n skipWarmup: () => orchestrator.qmd.debugStatus() === \"backend=noop\",\n openGate: () => {\n readiness.ready = true;\n },\n shutdownSignal: readinessAbort.signal,\n });\n // Wrap httpServer.stop() so that existing callers also get full lifecycle\n // cleanup: retry timers, deferred init, HTTP listener, and orchestrator.\n const originalStop = httpServer.stop.bind(httpServer);\n let stopPromise: Promise<void> | undefined;\n const stop = async (): Promise<void> => {\n if (stopPromise) return stopPromise;\n stopPromise = (async () => {\n startupSyncAbort.abort();\n readinessAbort.abort();\n orchestrator.abortDeferredInit();\n try {\n await originalStop();\n } finally {\n try {\n await readinessTask;\n } finally {\n await orchestrator.destroy();\n }\n }\n })();\n return stopPromise;\n };\n httpServer.stop = stop;\n\n orchestrator.deferredReady.then(() => {\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync: cancelled before deferred init completed\");\n return;\n }\n\n // Skip retries when search is explicitly disabled via config or when the\n // orchestrator already resolved to a noop backend (e.g. missing collection\n // detected during deferredInitialize). Both cases mean no sync should ever\n // run; scheduling retries would create misleading operational noise and\n // unnecessary background work on every server start.\n if (!config.qmdEnabled || orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync: search disabled or noop backend, skipping retries\");\n return;\n }\n\n // Retry when either: (a) QMD is not available yet (cold-start race), or\n // (b) QMD is available but the deferred init sync step failed silently\n // (e.g., update errors swallowed by backend, throttle skip, transient\n // network failure). Without (b), the daemon permanently serves stale\n // recall after a failed sync despite healthy QMD probe.\n const needsRetry = !orchestrator.qmd.isAvailable() || !orchestrator.deferredSyncSucceeded;\n if (!needsRetry) {\n log.debug(\"QMD startup-sync: deferred init completed successfully, no retries needed\");\n return;\n }\n\n const RETRY_DELAYS_MS = [5_000, 15_000, 30_000, 60_000, 120_000];\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled before retry task started\");\n return;\n }\n (async () => {\n for (const delay of RETRY_DELAYS_MS) {\n await abortableDelay(delay, startupSyncAbort.signal);\n\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled by shutdown\");\n return;\n }\n\n const synced = await ensureStartupSync(startupSyncAbort.signal);\n if (!synced) {\n if (orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync retry: search intentionally disabled; stopping retries\");\n return;\n }\n log.debug(`QMD startup-sync retry: not available yet (next retry in ${RETRY_DELAYS_MS[RETRY_DELAYS_MS.indexOf(delay) + 1] ?? \"n/a\"}ms)`);\n continue;\n }\n\n return; // sync succeeded, stop retrying\n }\n\n log.warn(\"QMD startup-sync retry: exhausted all retries; search index may be stale\");\n })().catch((err: unknown) => {\n log.warn(`QMD startup-sync retry: unexpected error: ${err}`);\n });\n }).catch((err: unknown) => {\n log.warn(`Deferred init error: ${err}`);\n });\n\n return { config, service, httpServer, host, port, stop, cancelStartupSync: () => startupSyncAbort.abort(), abortDeferredInit: () => orchestrator.abortDeferredInit() };\n}\n\n// ── CLI entry point ──────────────────────────────────────────────────────────\n\nconst BOOLEAN_CLI_OPTIONS = new Set([\"help\"]);\nconst VALUE_CLI_OPTIONS = new Set([\"config\", \"host\", \"port\", \"auth-token\"]);\n\nfunction parseCliArgs(argv: string[]): Record<string, string | undefined> {\n const args: Record<string, string | undefined> = {};\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (token === \"-h\") {\n args.help = \"true\";\n continue;\n }\n\n if (token.startsWith(\"--\")) {\n const [key, inlineValue] = token.slice(2).split(/=(.*)/s, 2);\n if (!key) {\n throw new Error(`Invalid option ${token}`);\n }\n\n if (BOOLEAN_CLI_OPTIONS.has(key)) {\n if (inlineValue !== undefined) {\n throw new Error(`Option --${key} does not accept a value`);\n }\n args[key] = \"true\";\n continue;\n }\n\n if (!VALUE_CLI_OPTIONS.has(key)) {\n throw new Error(`Unknown option --${key}`);\n }\n\n const value = inlineValue ?? argv[i + 1];\n if (\n value === undefined ||\n (inlineValue === undefined && value.startsWith(\"--\")) ||\n value.trim() === \"\"\n ) {\n throw new Error(`Missing value for --${key}`);\n }\n\n args[key] = value;\n if (inlineValue === undefined) i++;\n }\n }\n return args;\n}\n\nexport async function cliMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n const args = parseCliArgs(argv);\n\n if (args.help) {\n console.log(`\nremnic-server — Standalone Remnic memory server\n\nUsage:\n remnic-server [options]\n\nOptions:\n --config <path> Path to config file (default: remnic.config.json)\n --host <addr> Bind address (default: 127.0.0.1)\n --port <number> Port number (default: 4318)\n --auth-token <tok> Bearer token for auth (or set REMNIC_AUTH_TOKEN)\n --help Show this help\n\nEnvironment:\n REMNIC_CONFIG_PATH Config file path (ENGRAM_CONFIG_PATH also supported)\n REMNIC_PORT Server port (ENGRAM_PORT also supported)\n REMNIC_HOST Bind address (ENGRAM_HOST also supported)\n REMNIC_AUTH_TOKEN Auth bearer token (ENGRAM_AUTH_TOKEN also supported)\n REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\n Prefill admin UI with REMNIC_AUTH_TOKEN when true\n REMNIC_MEMORY_DIR Override memory directory (ENGRAM_MEMORY_DIR also supported)\n OPENAI_API_KEY OpenAI API key for extraction; ignored when config sets openaiApiKey=false\n`);\n process.exit(0);\n }\n\n const result = await startServer({\n configPath: args.config,\n host: args.host,\n port: args.port === undefined ? undefined : parseServerPort(args.port, \"--port\"),\n authToken: args[\"auth-token\"],\n });\n\n console.log(`Remnic server listening on http://${result.host}:${result.port}`);\n\n // Graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\nReceived ${signal}, shutting down...`);\n await result.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n}\n\n// Auto-run when executed directly\n// Matches direct execution of `node .../remnic-server/dist/index.js` or\n// `node .../remnic-server/src/index.ts`. Package command names are handled by\n// the bin wrappers in ../bin so importing this module cannot start twice.\nif (\n process.argv[1] &&\n /(?:remnic-server|engram-server)[\\\\/](?:dist|src)[\\\\/]index\\.[jt]s$/.test(process.argv[1])\n) {\n cliMain().catch((err) => {\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/server",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.767",
|
|
4
4
|
"description": "Standalone Remnic memory server — HTTP + MCP without OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
"provenance": true
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@remnic/core": "^9.3.
|
|
27
|
+
"@remnic/core": "^9.3.767"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"tsup": "^8.5.1",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
|
-
"@remnic/core": "^9.3.
|
|
32
|
+
"@remnic/core": "^9.3.767"
|
|
33
33
|
},
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|