Files
memind/scripts/check-memory-v2-config-gaps.mjs
2026-07-03 09:46:03 +08:00

196 lines
5.7 KiB
JavaScript

#!/usr/bin/env node
const BACKEND_SPECS = [
{
name: 'pgvector',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_VECTOR_ENABLED', 'MEMORY_PGVECTOR_DATABASE_URL', 'MEMORY_PGVECTOR_EMBEDDING_MODULE'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'pgvector',
MEMORY_VECTOR_ENABLED: '1',
MEMORY_PGVECTOR_DATABASE_URL: 'postgresql://john@127.0.0.1:5432/memind_memory',
MEMORY_PGVECTOR_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
},
},
{
name: 'qdrant',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_QDRANT_ENABLED', 'MEMORY_QDRANT_URL', 'MEMORY_QDRANT_EMBEDDING_MODULE'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'qdrant',
MEMORY_QDRANT_ENABLED: '1',
MEMORY_QDRANT_URL: 'http://127.0.0.1:6333',
MEMORY_QDRANT_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
},
},
{
name: 'weaviate',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_WEAVIATE_ENABLED', 'MEMORY_WEAVIATE_URL', 'MEMORY_WEAVIATE_EMBEDDING_MODULE'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'weaviate',
MEMORY_WEAVIATE_ENABLED: '1',
MEMORY_WEAVIATE_URL: 'http://127.0.0.1:8080',
MEMORY_WEAVIATE_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
},
},
{
name: 'mem0',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_MEM0_ENABLED', 'MEMORY_MEM0_API_KEY'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'mem0',
MEMORY_MEM0_ENABLED: '1',
MEMORY_MEM0_API_KEY: 'replace-me',
},
},
{
name: 'letta',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_LETTA_ENABLED', 'MEMORY_LETTA_API_KEY', 'MEMORY_LETTA_AGENT_ID'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'letta',
MEMORY_LETTA_ENABLED: '1',
MEMORY_LETTA_API_KEY: 'replace-me',
MEMORY_LETTA_AGENT_ID: 'agent_1',
},
},
{
name: 'neo4j',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_NEO4J_ENABLED', 'MEMORY_NEO4J_HTTP_URL', 'MEMORY_NEO4J_USER', 'MEMORY_NEO4J_PASSWORD'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'neo4j',
MEMORY_NEO4J_ENABLED: '1',
MEMORY_NEO4J_HTTP_URL: 'http://127.0.0.1:7474',
MEMORY_NEO4J_USER: 'neo4j',
MEMORY_NEO4J_PASSWORD: 'replace-me',
},
},
{
name: 'redis-streams',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_REDIS_STREAMS_ENABLED', 'MEMORY_REDIS_STREAMS_URL'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'redis-streams',
MEMORY_REDIS_STREAMS_ENABLED: '1',
MEMORY_REDIS_STREAMS_URL: 'redis://127.0.0.1:6379',
},
},
{
name: 'langgraph',
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_LANGGRAPH_ENABLED', 'MEMORY_LANGGRAPH_URL'],
recommended: {
MEMORY_ENABLED: '1',
MEMORY_BACKEND: 'langgraph',
MEMORY_LANGGRAPH_ENABLED: '1',
MEMORY_LANGGRAPH_URL: 'http://127.0.0.1:2024',
},
},
];
function usage() {
return `Usage:
node scripts/check-memory-v2-config-gaps.mjs [options]
Options:
--backend <name> Limit output to one backend.
--format <type> json | shell. Default: json.
-h, --help Show this help.
`;
}
export function parseMemoryV2ConfigGapArgs(argv = []) {
const options = {
backend: null,
format: 'json',
help: false,
};
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === '--backend') {
options.backend = argv[++i] ?? '';
} else if (arg === '--format') {
options.format = argv[++i] ?? '';
} else if (arg === '-h' || arg === '--help') {
options.help = true;
} else {
throw new Error(`Unknown argument: ${arg}`);
}
}
if (options.backend && !BACKEND_SPECS.some((item) => item.name === options.backend)) {
throw new Error(`--backend must be one of: ${BACKEND_SPECS.map((item) => item.name).join(', ')}`);
}
if (!['json', 'shell'].includes(options.format)) {
throw new Error('--format must be json or shell');
}
return options;
}
function inspectBackend(spec, env = process.env) {
const present = {};
const missing = [];
for (const key of spec.env) {
const value = String(env[key] ?? '').trim();
if (value) present[key] = value;
else missing.push(key);
}
return {
backend: spec.name,
ready: missing.length === 0,
missing,
presentKeys: Object.keys(present),
recommended: spec.recommended,
};
}
function renderShell(reports) {
const chunks = [];
for (const report of reports) {
chunks.push(`# ${report.backend}`);
for (const [key, value] of Object.entries(report.recommended)) {
chunks.push(`export ${key}='${String(value).replaceAll("'", "'\\''")}'`);
}
chunks.push('');
}
return chunks.join('\n').trimEnd();
}
export async function runMemoryV2ConfigGapCli({
argv = process.argv.slice(2),
env = process.env,
stdout = process.stdout,
} = {}) {
const options = parseMemoryV2ConfigGapArgs(argv);
if (options.help) {
stdout.write(usage());
return 0;
}
const reports = BACKEND_SPECS
.filter((spec) => !options.backend || spec.name === options.backend)
.map((spec) => inspectBackend(spec, env));
if (options.format === 'shell') {
stdout.write(`${renderShell(reports)}\n`);
return reports.every((item) => item.ready) ? 0 : 1;
}
const output = {
ok: reports.every((item) => item.ready),
checkedAt: new Date().toISOString(),
reports,
};
stdout.write(`${JSON.stringify(output, null, 2)}\n`);
return output.ok ? 0 : 1;
}
if (import.meta.url === `file://${process.argv[1]}`) {
runMemoryV2ConfigGapCli().then((code) => {
process.exitCode = code;
}).catch((err) => {
console.error(err instanceof Error ? err.message : err);
process.exitCode = 1;
});
}