#!/usr/bin/env node import fs from 'node:fs'; import path from 'node:path'; import { createMemoryV2Runtime } from '../memory-v2-runtime.mjs'; import { evaluateMemoryV2Health } from '../memory-v2-health.mjs'; function loadEnvFile(filePath, env = process.env) { if (!fs.existsSync(filePath)) return; for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const idx = trimmed.indexOf('='); if (idx < 0) continue; const key = trimmed.slice(0, idx).trim(); const value = trimmed.slice(idx + 1).trim(); if (!env[key]) env[key] = value; } } export function parseMemoryV2HealthArgs(argv = []) { const options = { requireEnabled: false, expectedBackend: null, envFile: process.env.MEMIND_ENV_FILE || path.join(process.cwd(), '.env'), }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; if (arg === '--require-enabled') { options.requireEnabled = true; } else if (arg === '--expect-backend') { options.expectedBackend = argv[++i] ?? ''; } else if (arg === '--no-env-file') { options.envFile = ''; } else if (arg === '--env-file') { options.envFile = argv[++i] ?? ''; } else if (arg === '-h' || arg === '--help') { options.help = true; } else { throw new Error(`Unknown argument: ${arg}`); } } return options; } function usage() { return `Usage: node scripts/check-memory-v2-health.mjs [options] Options: --require-enabled Fail unless Memory V2 is enabled. --expect-backend Fail unless MEMORY_BACKEND equals this value. --env-file Env file to load. Defaults to MEMIND_ENV_FILE or .env. --no-env-file Do not load an env file. `; } function createSyntheticLegacyMemoryService() { return { async listMemories() { return [{ label: 'health', text: 'synthetic memory-v2 health check' }]; }, async saveAndAnalyze() { return { saved: 1, analyzed: 1, memories: 1 }; }, async analyzeUser() { return { analyzed: 1, memories: 1 }; }, }; } export async function runMemoryV2HealthCli({ argv = process.argv.slice(2), env = process.env, stdout = process.stdout, stderr = process.stderr, importPg, importModule, } = {}) { const options = parseMemoryV2HealthArgs(argv); if (options.help) { stdout.write(usage()); return 0; } if (options.envFile) loadEnvFile(options.envFile, env); const memory = await createMemoryV2Runtime({ legacyMemoryService: createSyntheticLegacyMemoryService(), env, logger: { warn(message) { stderr.write(`${message}\n`); }, }, ...(importPg ? { importPg } : {}), ...(importModule ? { importModule } : {}), }); const status = memory.getStatus(); const writeResult = await memory.write({ userId: 'memory-v2-health-user', sessionId: 'memory-v2-health-session', messages: [], }); const compactResult = await memory.compact({ userId: 'memory-v2-health-user', sessionId: 'memory-v2-health-session', }); await memory.close?.(); const report = evaluateMemoryV2Health({ status, writeResult, compactResult, expectedBackend: options.expectedBackend || null, requireEnabled: options.requireEnabled, }); stdout.write(`${JSON.stringify(report, null, 2)}\n`); return report.ok ? 0 : 1; } if (import.meta.url === `file://${process.argv[1]}`) { runMemoryV2HealthCli().then((code) => { process.exitCode = code; }).catch((err) => { console.error(err instanceof Error ? err.message : err); process.exitCode = 1; }); }