@cortexmemory/cli 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/completion.d.ts +12 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +71 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +145 -40
- package/dist/commands/deploy.js.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/scripts/completions/cortex.bash +379 -0
- package/scripts/completions/cortex.fish +285 -0
- package/scripts/completions/cortex.zsh +751 -0
- package/scripts/postinstall.js +196 -0
- package/scripts/preuninstall.js +121 -0
- package/templates/basic/src/chat.ts +24 -3
- package/templates/basic/src/cortex.ts +41 -5
- package/templates/basic/src/index.ts +17 -1
- package/templates/basic/src/server.ts +18 -2
- package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +2 -1
- package/templates/vercel-ai-quickstart/next.config.js +13 -4
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cortex CLI Postinstall Script
|
|
5
|
+
*
|
|
6
|
+
* Automatically installs shell completion for the cortex CLI.
|
|
7
|
+
* Supports zsh, bash, and fish shells.
|
|
8
|
+
*
|
|
9
|
+
* This script runs after `npm install -g @cortexmemory/cli`
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, copyFileSync } from 'fs';
|
|
13
|
+
import { join, dirname } from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { homedir } from 'os';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const home = homedir();
|
|
19
|
+
|
|
20
|
+
// Marker comments for identifying our additions
|
|
21
|
+
const MARKER_START = '# >>> cortex completion >>>';
|
|
22
|
+
const MARKER_END = '# <<< cortex completion <<<';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the completion scripts directory (relative to this script)
|
|
26
|
+
*/
|
|
27
|
+
function getCompletionsDir() {
|
|
28
|
+
return join(__dirname, 'completions');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the target directory for completion scripts (~/.cortex/completions/)
|
|
33
|
+
*/
|
|
34
|
+
function getTargetDir() {
|
|
35
|
+
return join(home, '.cortex', 'completions');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Ensure directory exists
|
|
40
|
+
*/
|
|
41
|
+
function ensureDir(dir) {
|
|
42
|
+
if (!existsSync(dir)) {
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if a file already contains our completion setup
|
|
49
|
+
*/
|
|
50
|
+
function hasCompletionSetup(filePath) {
|
|
51
|
+
if (!existsSync(filePath)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
55
|
+
return content.includes(MARKER_START);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Detect the user's shell
|
|
60
|
+
*/
|
|
61
|
+
function detectShell() {
|
|
62
|
+
const shell = process.env.SHELL || '';
|
|
63
|
+
if (shell.includes('zsh')) return 'zsh';
|
|
64
|
+
if (shell.includes('bash')) return 'bash';
|
|
65
|
+
if (shell.includes('fish')) return 'fish';
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the shell RC file path
|
|
71
|
+
*/
|
|
72
|
+
function getShellRcPath(shell) {
|
|
73
|
+
switch (shell) {
|
|
74
|
+
case 'zsh':
|
|
75
|
+
return join(home, '.zshrc');
|
|
76
|
+
case 'bash': {
|
|
77
|
+
// Check for .bashrc first, then .bash_profile
|
|
78
|
+
const bashrc = join(home, '.bashrc');
|
|
79
|
+
const bashProfile = join(home, '.bash_profile');
|
|
80
|
+
if (existsSync(bashrc)) return bashrc;
|
|
81
|
+
if (existsSync(bashProfile)) return bashProfile;
|
|
82
|
+
return bashrc; // Default to .bashrc
|
|
83
|
+
}
|
|
84
|
+
case 'fish':
|
|
85
|
+
return join(home, '.config', 'fish', 'config.fish');
|
|
86
|
+
default:
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get the source line for shell RC file
|
|
93
|
+
*/
|
|
94
|
+
function getSourceLine(shell, targetPath) {
|
|
95
|
+
switch (shell) {
|
|
96
|
+
case 'zsh':
|
|
97
|
+
return `[[ -f "${targetPath}" ]] && source "${targetPath}"`;
|
|
98
|
+
case 'bash':
|
|
99
|
+
return `[[ -f "${targetPath}" ]] && source "${targetPath}"`;
|
|
100
|
+
case 'fish':
|
|
101
|
+
return `test -f "${targetPath}"; and source "${targetPath}"`;
|
|
102
|
+
default:
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Install completion for a specific shell
|
|
109
|
+
*/
|
|
110
|
+
function installCompletion(shell) {
|
|
111
|
+
const completionsDir = getCompletionsDir();
|
|
112
|
+
const targetDir = getTargetDir();
|
|
113
|
+
|
|
114
|
+
// Source completion script
|
|
115
|
+
const sourceFile = join(completionsDir, `cortex.${shell}`);
|
|
116
|
+
if (!existsSync(sourceFile)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Ensure target directory exists
|
|
121
|
+
ensureDir(targetDir);
|
|
122
|
+
|
|
123
|
+
// Copy completion script to target
|
|
124
|
+
const targetFile = join(targetDir, `cortex.${shell}`);
|
|
125
|
+
copyFileSync(sourceFile, targetFile);
|
|
126
|
+
|
|
127
|
+
// Get shell RC file
|
|
128
|
+
const rcPath = getShellRcPath(shell);
|
|
129
|
+
if (!rcPath) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check if already installed
|
|
134
|
+
if (hasCompletionSetup(rcPath)) {
|
|
135
|
+
return true; // Already installed
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Ensure RC file's parent directory exists (for fish)
|
|
139
|
+
ensureDir(dirname(rcPath));
|
|
140
|
+
|
|
141
|
+
// Create RC file if it doesn't exist
|
|
142
|
+
if (!existsSync(rcPath)) {
|
|
143
|
+
writeFileSync(rcPath, '', 'utf-8');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add source line to RC file
|
|
147
|
+
const sourceLine = getSourceLine(shell, targetFile);
|
|
148
|
+
if (!sourceLine) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const completionBlock = `
|
|
153
|
+
${MARKER_START}
|
|
154
|
+
${sourceLine}
|
|
155
|
+
${MARKER_END}
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
appendFileSync(rcPath, completionBlock, 'utf-8');
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Main installation function
|
|
164
|
+
*/
|
|
165
|
+
function main() {
|
|
166
|
+
// Skip if running in CI or non-interactive environment
|
|
167
|
+
if (process.env.CI || process.env.CORTEX_SKIP_COMPLETION) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Detect shell
|
|
172
|
+
const shell = detectShell();
|
|
173
|
+
if (!shell) {
|
|
174
|
+
// Unknown shell, skip silently
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
// Install completion for detected shell
|
|
180
|
+
installCompletion(shell);
|
|
181
|
+
|
|
182
|
+
// Also try to install for other common shells if their RC files exist
|
|
183
|
+
const otherShells = ['zsh', 'bash', 'fish'].filter(s => s !== shell);
|
|
184
|
+
for (const otherShell of otherShells) {
|
|
185
|
+
const rcPath = getShellRcPath(otherShell);
|
|
186
|
+
if (rcPath && existsSync(rcPath)) {
|
|
187
|
+
installCompletion(otherShell);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
// Silently fail - completion is not critical
|
|
192
|
+
// User can manually run `cortex completion <shell>` if needed
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
main();
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cortex CLI Preuninstall Script
|
|
5
|
+
*
|
|
6
|
+
* Removes shell completion configuration when the CLI is uninstalled.
|
|
7
|
+
* Cleans up ~/.cortex/completions/ and removes source lines from shell RC files.
|
|
8
|
+
*
|
|
9
|
+
* This script runs before `npm uninstall -g @cortexmemory/cli`
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
|
|
16
|
+
const home = homedir();
|
|
17
|
+
|
|
18
|
+
// Marker comments for identifying our additions
|
|
19
|
+
const MARKER_START = '# >>> cortex completion >>>';
|
|
20
|
+
const MARKER_END = '# <<< cortex completion <<<';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the target directory for completion scripts (~/.cortex/completions/)
|
|
24
|
+
*/
|
|
25
|
+
function getTargetDir() {
|
|
26
|
+
return join(home, '.cortex', 'completions');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get shell RC file paths to clean up
|
|
31
|
+
*/
|
|
32
|
+
function getShellRcPaths() {
|
|
33
|
+
return [
|
|
34
|
+
join(home, '.zshrc'),
|
|
35
|
+
join(home, '.bashrc'),
|
|
36
|
+
join(home, '.bash_profile'),
|
|
37
|
+
join(home, '.config', 'fish', 'config.fish'),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Remove completion block from a shell RC file
|
|
43
|
+
*/
|
|
44
|
+
function removeCompletionFromRc(rcPath) {
|
|
45
|
+
if (!existsSync(rcPath)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const content = readFileSync(rcPath, 'utf-8');
|
|
50
|
+
|
|
51
|
+
// Check if our completion setup exists
|
|
52
|
+
if (!content.includes(MARKER_START)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Remove the completion block (including markers and content between them)
|
|
57
|
+
const regex = new RegExp(
|
|
58
|
+
`\\n?${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}\\n?`,
|
|
59
|
+
'g'
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const newContent = content.replace(regex, '\n');
|
|
63
|
+
|
|
64
|
+
// Write cleaned content back
|
|
65
|
+
writeFileSync(rcPath, newContent, 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Escape special regex characters
|
|
70
|
+
*/
|
|
71
|
+
function escapeRegex(string) {
|
|
72
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Remove completion scripts directory
|
|
77
|
+
*/
|
|
78
|
+
function removeCompletionsDir() {
|
|
79
|
+
const targetDir = getTargetDir();
|
|
80
|
+
if (existsSync(targetDir)) {
|
|
81
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Also remove parent .cortex directory if empty
|
|
85
|
+
const cortexDir = join(home, '.cortex');
|
|
86
|
+
if (existsSync(cortexDir)) {
|
|
87
|
+
try {
|
|
88
|
+
const files = readdirSync(cortexDir);
|
|
89
|
+
if (files.length === 0) {
|
|
90
|
+
rmSync(cortexDir, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
// Directory not empty or other error, leave it
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Main uninstall function
|
|
100
|
+
*/
|
|
101
|
+
function main() {
|
|
102
|
+
// Skip if running in CI
|
|
103
|
+
if (process.env.CI || process.env.CORTEX_SKIP_COMPLETION) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Remove completion setup from all shell RC files
|
|
109
|
+
const rcPaths = getShellRcPaths();
|
|
110
|
+
for (const rcPath of rcPaths) {
|
|
111
|
+
removeCompletionFromRc(rcPath);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Remove completion scripts directory
|
|
115
|
+
removeCompletionsDir();
|
|
116
|
+
} catch {
|
|
117
|
+
// Silently fail - cleanup is not critical
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main();
|
|
@@ -45,6 +45,20 @@ export interface Fact {
|
|
|
45
45
|
subject?: string;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Transform SDK FactRecord to local Fact interface
|
|
50
|
+
* SDK uses 'fact' field, local interface uses 'content'
|
|
51
|
+
*/
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
function transformFacts(sdkFacts: any[]): Fact[] {
|
|
54
|
+
return sdkFacts.map((f) => ({
|
|
55
|
+
content: f.fact || f.content || "", // SDK uses 'fact', fallback to 'content'
|
|
56
|
+
factType: f.factType,
|
|
57
|
+
confidence: f.confidence,
|
|
58
|
+
subject: f.subject,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
|
|
48
62
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
49
63
|
// Conversation State
|
|
50
64
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -122,7 +136,9 @@ export async function chat(
|
|
|
122
136
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
137
|
const result = recallResult as any;
|
|
124
138
|
memories = (result.sources?.vector?.items || result.memories || []) as Memory[];
|
|
125
|
-
|
|
139
|
+
// Transform facts: SDK FactRecord uses 'fact' field, local Fact uses 'content'
|
|
140
|
+
const rawFacts = result.sources?.facts?.items || result.facts || [];
|
|
141
|
+
facts = transformFacts(rawFacts);
|
|
126
142
|
|
|
127
143
|
stopSpinner(true, `Found ${memories.length} memories, ${facts.length} facts`);
|
|
128
144
|
|
|
@@ -207,7 +223,9 @@ export async function recallMemories(query: string): Promise<void> {
|
|
|
207
223
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
224
|
const result = recallResult as any;
|
|
209
225
|
const memories = (result.sources?.vector?.items || result.memories || []) as Memory[];
|
|
210
|
-
|
|
226
|
+
// Transform facts: SDK FactRecord uses 'fact' field, local Fact uses 'content'
|
|
227
|
+
const rawFacts = result.sources?.facts?.items || result.facts || [];
|
|
228
|
+
const facts = transformFacts(rawFacts);
|
|
211
229
|
|
|
212
230
|
stopSpinner(true, `Found ${memories.length} memories, ${facts.length} facts`);
|
|
213
231
|
printRecallResults(memories, facts);
|
|
@@ -231,7 +249,10 @@ export async function listFacts(): Promise<void> {
|
|
|
231
249
|
limit: 20,
|
|
232
250
|
});
|
|
233
251
|
|
|
234
|
-
|
|
252
|
+
// Transform facts: SDK returns FactRecord[] directly with 'fact' field
|
|
253
|
+
// Local Fact interface uses 'content'
|
|
254
|
+
const rawFacts = Array.isArray(result) ? result : [];
|
|
255
|
+
const facts = transformFacts(rawFacts);
|
|
235
256
|
|
|
236
257
|
stopSpinner(true, `Found ${facts.length} facts`);
|
|
237
258
|
printRecallResults([], facts);
|
|
@@ -38,12 +38,28 @@ export const CONFIG = {
|
|
|
38
38
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
39
39
|
|
|
40
40
|
let cortexClient: Cortex | null = null;
|
|
41
|
+
let initPromise: Promise<Cortex> | null = null;
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
+
* Initialize the Cortex SDK client (async)
|
|
45
|
+
*
|
|
46
|
+
* Uses Cortex.create() for automatic graph configuration from env vars.
|
|
47
|
+
* When CORTEX_GRAPH_SYNC=true and NEO4J_URI (or MEMGRAPH_URI) is set,
|
|
48
|
+
* the graph adapter is automatically created and connected.
|
|
49
|
+
*
|
|
50
|
+
* v0.29.0+: Graph sync is automatic when graphAdapter is configured.
|
|
51
|
+
* No need to pass syncToGraph option to remember() calls.
|
|
44
52
|
*/
|
|
45
|
-
export function
|
|
46
|
-
if (
|
|
53
|
+
export async function initCortex(): Promise<Cortex> {
|
|
54
|
+
if (cortexClient) {
|
|
55
|
+
return cortexClient;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (initPromise) {
|
|
59
|
+
return initPromise;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
initPromise = (async () => {
|
|
47
63
|
const convexUrl = process.env.CONVEX_URL;
|
|
48
64
|
if (!convexUrl) {
|
|
49
65
|
throw new Error(
|
|
@@ -65,9 +81,29 @@ export function getCortex(): Cortex {
|
|
|
65
81
|
};
|
|
66
82
|
}
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
// Use Cortex.create() for async initialization with auto graph configuration
|
|
85
|
+
// This automatically:
|
|
86
|
+
// - Detects CORTEX_GRAPH_SYNC=true
|
|
87
|
+
// - Reads NEO4J_URI/MEMGRAPH_URI and credentials from env
|
|
88
|
+
// - Creates and connects the CypherGraphAdapter
|
|
89
|
+
cortexClient = await Cortex.create(config);
|
|
90
|
+
return cortexClient;
|
|
91
|
+
})();
|
|
92
|
+
|
|
93
|
+
return initPromise;
|
|
94
|
+
}
|
|
70
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Get the Cortex SDK client (must call initCortex first)
|
|
98
|
+
*
|
|
99
|
+
* @throws Error if client hasn't been initialized
|
|
100
|
+
*/
|
|
101
|
+
export function getCortex(): Cortex {
|
|
102
|
+
if (!cortexClient) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
"Cortex client not initialized. Call initCortex() first.",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
71
107
|
return cortexClient;
|
|
72
108
|
}
|
|
73
109
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import * as readline from "readline";
|
|
22
|
-
import { closeCortex } from "./cortex.js";
|
|
22
|
+
import { closeCortex, initCortex, CONFIG } from "./cortex.js";
|
|
23
23
|
import {
|
|
24
24
|
chat,
|
|
25
25
|
recallMemories,
|
|
@@ -189,6 +189,22 @@ async function main(): Promise<void> {
|
|
|
189
189
|
// Print welcome
|
|
190
190
|
printWelcome("cli");
|
|
191
191
|
|
|
192
|
+
// Initialize Cortex client (async for graph support)
|
|
193
|
+
// v0.29.0+: Uses Cortex.create() for automatic graph configuration
|
|
194
|
+
try {
|
|
195
|
+
await initCortex();
|
|
196
|
+
// Check if graph is actually configured (flag + URI)
|
|
197
|
+
const hasGraphUri = !!(process.env.NEO4J_URI || process.env.MEMGRAPH_URI);
|
|
198
|
+
if (CONFIG.enableGraphMemory && hasGraphUri) {
|
|
199
|
+
printSuccess("Graph memory connected (auto-sync active)");
|
|
200
|
+
} else if (CONFIG.enableGraphMemory && !hasGraphUri) {
|
|
201
|
+
printInfo("Graph sync enabled but no database URI configured (NEO4J_URI or MEMGRAPH_URI)");
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
printError("Failed to initialize Cortex", error instanceof Error ? error : undefined);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
192
208
|
// Initialize conversation
|
|
193
209
|
const convId = getConversationId();
|
|
194
210
|
printSuccess(`Conversation: ${convId}`);
|
|
@@ -19,14 +19,14 @@ import { serve } from "@hono/node-server";
|
|
|
19
19
|
import { Hono } from "hono";
|
|
20
20
|
import { cors } from "hono/cors";
|
|
21
21
|
import { logger } from "hono/logger";
|
|
22
|
-
import { closeCortex, CONFIG } from "./cortex.js";
|
|
22
|
+
import { closeCortex, initCortex, CONFIG } from "./cortex.js";
|
|
23
23
|
import {
|
|
24
24
|
chat,
|
|
25
25
|
recallMemories,
|
|
26
26
|
listFacts,
|
|
27
27
|
generateConversationId,
|
|
28
28
|
} from "./chat.js";
|
|
29
|
-
import { printWelcome, printInfo, printError } from "./display.js";
|
|
29
|
+
import { printWelcome, printInfo, printError, printSuccess } from "./display.js";
|
|
30
30
|
|
|
31
31
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
32
32
|
// Server Setup
|
|
@@ -237,6 +237,22 @@ async function main(): Promise<void> {
|
|
|
237
237
|
// Print welcome
|
|
238
238
|
printWelcome("server");
|
|
239
239
|
|
|
240
|
+
// Initialize Cortex client (async for graph support)
|
|
241
|
+
// v0.29.0+: Uses Cortex.create() for automatic graph configuration
|
|
242
|
+
try {
|
|
243
|
+
await initCortex();
|
|
244
|
+
// Check if graph is actually configured (flag + URI)
|
|
245
|
+
const hasGraphUri = !!(process.env.NEO4J_URI || process.env.MEMGRAPH_URI);
|
|
246
|
+
if (CONFIG.enableGraphMemory && hasGraphUri) {
|
|
247
|
+
printSuccess("Graph memory connected (auto-sync active)");
|
|
248
|
+
} else if (CONFIG.enableGraphMemory && !hasGraphUri) {
|
|
249
|
+
printInfo("Graph sync enabled but no database URI configured (NEO4J_URI or MEMGRAPH_URI)");
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
printError("Failed to initialize Cortex", error instanceof Error ? error : undefined);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
240
256
|
// Start server
|
|
241
257
|
console.log(`🚀 Server starting on http://localhost:${PORT}`);
|
|
242
258
|
console.log("");
|
|
@@ -97,13 +97,14 @@ export const memoryAgent = new ToolLoopAgent({
|
|
|
97
97
|
// │ - Facts (extracted knowledge) │
|
|
98
98
|
// │ - Graph relationships (if configured) │
|
|
99
99
|
// └─────────────────────────────────────────────────────────────────┘
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
101
|
prepareCall: createMemoryPrepareCall({
|
|
101
102
|
convexUrl: process.env.CONVEX_URL!,
|
|
102
103
|
maxMemories: 20, // Max items to inject from recall
|
|
103
104
|
includeFacts: true, // Include Layer 3 facts
|
|
104
105
|
includeVector: true, // Include Layer 2 vector memories
|
|
105
106
|
includeGraph: true, // Expand through graph relationships
|
|
106
|
-
}),
|
|
107
|
+
}) as any, // Type assertion needed due to AI SDK v6 type evolution
|
|
107
108
|
|
|
108
109
|
// Default to 5 steps (sufficient for most chat interactions)
|
|
109
110
|
stopWhen: stepCountIs(5),
|
|
@@ -2,8 +2,8 @@ const path = require("path");
|
|
|
2
2
|
|
|
3
3
|
/** @type {import('next').NextConfig} */
|
|
4
4
|
const nextConfig = {
|
|
5
|
-
transpilePackages: ["@cortexmemory/
|
|
6
|
-
serverExternalPackages: ["convex"],
|
|
5
|
+
transpilePackages: ["@cortexmemory/vercel-ai-provider"],
|
|
6
|
+
serverExternalPackages: ["convex", "neo4j-driver", "@cortexmemory/sdk"],
|
|
7
7
|
// Disable image optimization to avoid sharp dependency (LGPL licensed)
|
|
8
8
|
// This quickstart doesn't use image optimization features
|
|
9
9
|
images: {
|
|
@@ -18,7 +18,7 @@ const nextConfig = {
|
|
|
18
18
|
// Webpack configuration for module resolution when SDK is file-linked
|
|
19
19
|
// This is needed because the SDK uses dynamic imports that don't resolve
|
|
20
20
|
// correctly from a linked package's location during local development
|
|
21
|
-
webpack: (config) => {
|
|
21
|
+
webpack: (config, { isServer }) => {
|
|
22
22
|
config.resolve.alias = {
|
|
23
23
|
...config.resolve.alias,
|
|
24
24
|
"@anthropic-ai/sdk": path.resolve(
|
|
@@ -26,8 +26,17 @@ const nextConfig = {
|
|
|
26
26
|
"node_modules/@anthropic-ai/sdk",
|
|
27
27
|
),
|
|
28
28
|
openai: path.resolve(__dirname, "node_modules/openai"),
|
|
29
|
-
"neo4j-driver": path.resolve(__dirname, "node_modules/neo4j-driver"),
|
|
30
29
|
};
|
|
30
|
+
|
|
31
|
+
// Mark neo4j-driver and its rxjs dependency as external for server builds
|
|
32
|
+
// neo4j-driver uses rxjs for reactive sessions which doesn't bundle well
|
|
33
|
+
if (isServer) {
|
|
34
|
+
config.externals = config.externals || [];
|
|
35
|
+
if (Array.isArray(config.externals)) {
|
|
36
|
+
config.externals.push("neo4j-driver", /^rxjs/, /^rxjs\//);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
return config;
|
|
32
41
|
},
|
|
33
42
|
};
|