Add standalone executable packaging with esbuild + pkg
- Add npm scripts for building Windows/Linux/macOS executables - Replace uuid package with crypto.randomUUID() for ESM compatibility - Use esbuild to pre-bundle code before pkg (fixes MCP SDK subpath exports) - Update CLI name from 'memory' to 'cortex' - Update USAGE.md with build instructions and standalone setup - Add bundle/ and build/ to .gitignore
This commit is contained in:
@@ -16,8 +16,8 @@ import { closeDb } from '../core/db';
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('memory')
|
||||
.description('Cortex — AI project memory & knowledge graph')
|
||||
.name('cortex')
|
||||
.description('Cortex — AI project memory & knowledge graph\n\nStore, link, and search project knowledge as a graph of typed nodes.')
|
||||
.version('1.0.0');
|
||||
|
||||
program.addCommand(addCommand);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { randomUUID as uuid } from 'crypto';
|
||||
import { getDb } from './db';
|
||||
import { Node, Edge, AddNodeInput, UpdateNodeInput, ListOptions, QueryOptions, SearchResult, EdgeType } from '../types';
|
||||
import { hybridSearch, deserializeEmbedding } from './search/index';
|
||||
|
||||
@@ -367,6 +367,24 @@ server.tool(
|
||||
}
|
||||
);
|
||||
|
||||
// --- memory_summary ---
|
||||
import { getCachedSummary, generateSummary } from '../core/summary';
|
||||
|
||||
server.tool(
|
||||
'memory_summary',
|
||||
'Get a pre-computed hierarchical summary of the memory graph. Use this instead of memory_list to reduce context usage.',
|
||||
{
|
||||
refresh: z.boolean().optional().describe('Force regenerate summary (default: use cached)'),
|
||||
},
|
||||
async ({ refresh }) => {
|
||||
let summary = refresh ? null : getCachedSummary();
|
||||
if (!summary) {
|
||||
summary = await generateSummary();
|
||||
}
|
||||
return { content: [{ type: 'text' as const, text: serialize(summary) }] };
|
||||
}
|
||||
);
|
||||
|
||||
// --- memory_prompt ---
|
||||
import { interpretAndExecute } from '../core/prompt/interpreter';
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { getDb } from '../core/db';
|
||||
import { deserializeEmbedding } from '../core/search/index';
|
||||
import { cosineSimilarity } from '../core/search/vector';
|
||||
import { isGenAvailable, generate } from '../core/search/ollamaGen';
|
||||
import { generateSummary } from '../core/summary';
|
||||
|
||||
let dirty = false;
|
||||
|
||||
@@ -194,7 +195,7 @@ Existing tags: ${tagVocab.join(', ')}`;
|
||||
|
||||
if (orphans.length > 0 && nonOrphans.length > 0) {
|
||||
const insertEdge = db.prepare('INSERT INTO edges (id, from_id, to_id, type, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?)');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { randomUUID: uuidv4 } = require('crypto');
|
||||
|
||||
if (aiAvailable) {
|
||||
for (const orphan of orphans) {
|
||||
@@ -265,7 +266,7 @@ Content: ${node.content.slice(0, 2000)}`;
|
||||
|
||||
// Auto-split: content > 2000 chars
|
||||
const hugeNodes = nodes.filter(n => n.content.length > 2000 && !staleIds.has(n.id));
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { randomUUID: uuidv4Split } = require('crypto');
|
||||
const insertNode = db.prepare(`INSERT INTO nodes (id, kind, title, content, status, tags, metadata, embedding, created_at, updated_at, last_accessed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
||||
const insertEdge2 = db.prepare('INSERT INTO edges (id, from_id, to_id, type, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?)');
|
||||
|
||||
@@ -289,10 +290,10 @@ Content: ${node.content.slice(0, 3000)}`;
|
||||
const titleMatch = section.match(/Title:\s*(.+)/);
|
||||
const contentMatch = section.match(/Content:\s*([\s\S]+)/);
|
||||
if (titleMatch && contentMatch) {
|
||||
const childId = uuidv4();
|
||||
const childId = uuidv4Split();
|
||||
insertNode.run(childId, node.kind, titleMatch[1].trim(), contentMatch[1].trim(),
|
||||
null, JSON.stringify(node.tags), JSON.stringify({}), null, now, now, now);
|
||||
insertEdge2.run(uuidv4(), node.id, childId, 'contains', '{}', now);
|
||||
insertEdge2.run(uuidv4Split(), node.id, childId, 'contains', '{}', now);
|
||||
childIds.push(childId);
|
||||
}
|
||||
}
|
||||
@@ -334,6 +335,14 @@ Content: ${node.content.slice(0, 300)}`;
|
||||
const pruneResult = db.prepare('DELETE FROM nodes WHERE is_stale = 1 AND updated_at < ?').run(thirtyDaysAgo);
|
||||
pruned = pruneResult.changes;
|
||||
|
||||
// Regenerate summary cache
|
||||
try {
|
||||
await generateSummary();
|
||||
console.log('[Heartbeat] Summary cache regenerated');
|
||||
} catch (err) {
|
||||
console.error('[Heartbeat] Failed to regenerate summary:', err);
|
||||
}
|
||||
|
||||
const report: HeartbeatReport = {
|
||||
ranAt: now, deduped, autoTagged, autoOrganized, pruned,
|
||||
summarized, merged, split: splitCount, archived,
|
||||
|
||||
Reference in New Issue
Block a user