@cortexmemory/cli 0.27.4 → 0.28.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.
Files changed (66) hide show
  1. package/dist/commands/db.d.ts.map +1 -1
  2. package/dist/commands/db.js +18 -6
  3. package/dist/commands/db.js.map +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +74 -34
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/dev.js +3 -2
  8. package/dist/commands/dev.js.map +1 -1
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +12 -0
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/types.d.ts +1 -1
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/utils/app-template-sync.d.ts.map +1 -1
  15. package/dist/utils/app-template-sync.js +3 -1
  16. package/dist/utils/app-template-sync.js.map +1 -1
  17. package/dist/utils/init/quickstart-setup.d.ts.map +1 -1
  18. package/dist/utils/init/quickstart-setup.js.map +1 -1
  19. package/package.json +4 -4
  20. package/templates/basic/.env.local.example +23 -0
  21. package/templates/basic/README.md +181 -56
  22. package/templates/basic/package-lock.json +2180 -406
  23. package/templates/basic/package.json +23 -5
  24. package/templates/basic/src/__tests__/chat.test.ts +340 -0
  25. package/templates/basic/src/__tests__/cortex.test.ts +260 -0
  26. package/templates/basic/src/__tests__/display.test.ts +455 -0
  27. package/templates/basic/src/__tests__/e2e/fact-extraction.test.ts +498 -0
  28. package/templates/basic/src/__tests__/e2e/memory-flow.test.ts +355 -0
  29. package/templates/basic/src/__tests__/e2e/server-e2e.test.ts +414 -0
  30. package/templates/basic/src/__tests__/helpers/test-utils.ts +345 -0
  31. package/templates/basic/src/__tests__/integration/chat-flow.test.ts +422 -0
  32. package/templates/basic/src/__tests__/integration/server.test.ts +441 -0
  33. package/templates/basic/src/__tests__/llm.test.ts +344 -0
  34. package/templates/basic/src/chat.ts +300 -0
  35. package/templates/basic/src/cortex.ts +203 -0
  36. package/templates/basic/src/display.ts +425 -0
  37. package/templates/basic/src/index.ts +194 -64
  38. package/templates/basic/src/llm.ts +214 -0
  39. package/templates/basic/src/server.ts +280 -0
  40. package/templates/basic/vitest.config.ts +33 -0
  41. package/templates/basic/vitest.e2e.config.ts +28 -0
  42. package/templates/basic/vitest.integration.config.ts +25 -0
  43. package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +1 -1
  44. package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +6 -9
  45. package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +14 -18
  46. package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +4 -7
  47. package/templates/vercel-ai-quickstart/app/api/chat/route.ts +28 -11
  48. package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +19 -13
  49. package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +16 -16
  50. package/templates/vercel-ai-quickstart/app/globals.css +24 -9
  51. package/templates/vercel-ai-quickstart/app/page.tsx +25 -13
  52. package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +3 -1
  53. package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +6 -6
  54. package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +19 -8
  55. package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +41 -14
  56. package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +10 -5
  57. package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +3 -3
  58. package/templates/vercel-ai-quickstart/lib/password.ts +5 -5
  59. package/templates/vercel-ai-quickstart/next.config.js +10 -2
  60. package/templates/vercel-ai-quickstart/package.json +18 -11
  61. package/templates/vercel-ai-quickstart/test-api.mjs +131 -100
  62. package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +73 -44
  63. package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +40 -40
  64. package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +8 -8
  65. package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +12 -8
  66. package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +4 -1
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Integration Tests: HTTP Server
3
+ *
4
+ * Tests HTTP endpoints with mocked internals.
5
+ * Uses Hono's test client for request simulation.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
9
+ import { Hono } from "hono";
10
+ import { generateTestId } from "../helpers/test-utils.js";
11
+
12
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13
+ // Types
14
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
15
+
16
+ interface MockMemory {
17
+ memoryId: string;
18
+ content: string;
19
+ importance: number;
20
+ }
21
+
22
+ interface MockFact {
23
+ factId: string;
24
+ fact: string;
25
+ factType: string;
26
+ confidence: number;
27
+ }
28
+
29
+ interface MockConversation {
30
+ conversationId: string;
31
+ messages: Array<{ role: string; content: string }>;
32
+ }
33
+
34
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
35
+ // Test State
36
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
37
+
38
+ // Mutable state for each test
39
+ let memories: MockMemory[] = [];
40
+ let facts: MockFact[] = [];
41
+ let conversations: Map<string, MockConversation> = new Map();
42
+ let chatResult = {
43
+ response: "Test response",
44
+ conversationId: "conv-test-123",
45
+ memoriesRecalled: 0,
46
+ factsRecalled: 0,
47
+ };
48
+
49
+ // Reset state before each test
50
+ function resetState() {
51
+ memories = [];
52
+ facts = [];
53
+ conversations = new Map();
54
+ chatResult = {
55
+ response: "Test response",
56
+ conversationId: "conv-test-123",
57
+ memoriesRecalled: 0,
58
+ factsRecalled: 0,
59
+ };
60
+ }
61
+
62
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
63
+ // Create Test App (mirrors server.ts routes)
64
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
65
+
66
+ function createTestApp() {
67
+ const app = new Hono();
68
+
69
+ const CONFIG = {
70
+ memorySpaceId: "test-space",
71
+ agentId: "test-agent",
72
+ enableFactExtraction: true,
73
+ enableGraphMemory: false,
74
+ };
75
+
76
+ // Health check
77
+ app.get("/health", (c) => {
78
+ return c.json({
79
+ status: "ok",
80
+ memorySpaceId: CONFIG.memorySpaceId,
81
+ agentId: CONFIG.agentId,
82
+ features: {
83
+ factExtraction: CONFIG.enableFactExtraction,
84
+ graphSync: CONFIG.enableGraphMemory,
85
+ llm: false,
86
+ },
87
+ });
88
+ });
89
+
90
+ // Chat endpoint
91
+ app.post("/chat", async (c) => {
92
+ try {
93
+ const body = await c.req.json();
94
+ const { message, conversationId } = body;
95
+
96
+ if (!message || typeof message !== "string") {
97
+ return c.json({ error: "message is required" }, 400);
98
+ }
99
+
100
+ const convId = conversationId || generateTestId("conv");
101
+
102
+ return c.json({
103
+ response: chatResult.response,
104
+ conversationId: convId,
105
+ memoriesRecalled: chatResult.memoriesRecalled,
106
+ factsRecalled: chatResult.factsRecalled,
107
+ });
108
+ } catch (error) {
109
+ const message = error instanceof Error ? error.message : "Unknown error";
110
+ return c.json({ error: message }, 500);
111
+ }
112
+ });
113
+
114
+ // Recall endpoint
115
+ app.get("/recall", async (c) => {
116
+ try {
117
+ const query = c.req.query("query");
118
+
119
+ if (!query) {
120
+ return c.json({ error: "query parameter is required" }, 400);
121
+ }
122
+
123
+ return c.json({
124
+ memories: memories,
125
+ facts: facts,
126
+ query,
127
+ });
128
+ } catch (error) {
129
+ const message = error instanceof Error ? error.message : "Unknown error";
130
+ return c.json({ error: message }, 500);
131
+ }
132
+ });
133
+
134
+ // Facts endpoint
135
+ app.get("/facts", async (c) => {
136
+ try {
137
+ return c.json({ facts: facts, count: facts.length });
138
+ } catch (error) {
139
+ const message = error instanceof Error ? error.message : "Unknown error";
140
+ return c.json({ error: message }, 500);
141
+ }
142
+ });
143
+
144
+ // History endpoint
145
+ app.get("/history/:conversationId", async (c) => {
146
+ try {
147
+ const conversationId = c.req.param("conversationId");
148
+ const conversation = conversations.get(conversationId);
149
+
150
+ if (!conversation) {
151
+ return c.json({ error: "Conversation not found" }, 404);
152
+ }
153
+
154
+ return c.json({
155
+ conversationId,
156
+ messages: conversation.messages,
157
+ messageCount: conversation.messages.length,
158
+ });
159
+ } catch (error) {
160
+ const message = error instanceof Error ? error.message : "Unknown error";
161
+ return c.json({ error: message }, 500);
162
+ }
163
+ });
164
+
165
+ // Root endpoint
166
+ app.get("/", (c) => {
167
+ return c.json({
168
+ name: "Cortex Memory - Basic Demo API",
169
+ version: "1.0.0",
170
+ });
171
+ });
172
+
173
+ return app;
174
+ }
175
+
176
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
177
+ // Tests
178
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
179
+
180
+ describe("HTTP Server Integration", () => {
181
+ let app: Hono;
182
+
183
+ beforeEach(() => {
184
+ resetState();
185
+ app = createTestApp();
186
+ });
187
+
188
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
189
+ // Health Check
190
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
191
+
192
+ describe("GET /health", () => {
193
+ it("should return status ok", async () => {
194
+ const res = await app.request("/health");
195
+ const data = await res.json();
196
+
197
+ expect(res.status).toBe(200);
198
+ expect(data.status).toBe("ok");
199
+ });
200
+
201
+ it("should return config information", async () => {
202
+ const res = await app.request("/health");
203
+ const data = await res.json();
204
+
205
+ expect(data.memorySpaceId).toBe("test-space");
206
+ expect(data.agentId).toBe("test-agent");
207
+ expect(data.features).toBeDefined();
208
+ expect(data.features.factExtraction).toBe(true);
209
+ });
210
+ });
211
+
212
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
213
+ // Chat Endpoint
214
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
215
+
216
+ describe("POST /chat", () => {
217
+ it("should process valid chat message", async () => {
218
+ const res = await app.request("/chat", {
219
+ method: "POST",
220
+ headers: { "Content-Type": "application/json" },
221
+ body: JSON.stringify({ message: "Hello, world!" }),
222
+ });
223
+ const data = await res.json();
224
+
225
+ expect(res.status).toBe(200);
226
+ expect(data.response).toBeDefined();
227
+ expect(data.conversationId).toBeDefined();
228
+ expect(data.memoriesRecalled).toBeDefined();
229
+ expect(data.factsRecalled).toBeDefined();
230
+ });
231
+
232
+ it("should use provided conversation ID", async () => {
233
+ const customConvId = "conv-custom-456";
234
+
235
+ const res = await app.request("/chat", {
236
+ method: "POST",
237
+ headers: { "Content-Type": "application/json" },
238
+ body: JSON.stringify({
239
+ message: "Hello",
240
+ conversationId: customConvId,
241
+ }),
242
+ });
243
+ const data = await res.json();
244
+
245
+ expect(res.status).toBe(200);
246
+ expect(data.conversationId).toBe(customConvId);
247
+ });
248
+
249
+ it("should return 400 if message is missing", async () => {
250
+ const res = await app.request("/chat", {
251
+ method: "POST",
252
+ headers: { "Content-Type": "application/json" },
253
+ body: JSON.stringify({}),
254
+ });
255
+ const data = await res.json();
256
+
257
+ expect(res.status).toBe(400);
258
+ expect(data.error).toBe("message is required");
259
+ });
260
+
261
+ it("should return 400 if message is not a string", async () => {
262
+ const res = await app.request("/chat", {
263
+ method: "POST",
264
+ headers: { "Content-Type": "application/json" },
265
+ body: JSON.stringify({ message: 123 }),
266
+ });
267
+ const data = await res.json();
268
+
269
+ expect(res.status).toBe(400);
270
+ expect(data.error).toBe("message is required");
271
+ });
272
+
273
+ it("should include memory and fact counts", async () => {
274
+ chatResult.memoriesRecalled = 3;
275
+ chatResult.factsRecalled = 2;
276
+
277
+ const res = await app.request("/chat", {
278
+ method: "POST",
279
+ headers: { "Content-Type": "application/json" },
280
+ body: JSON.stringify({ message: "What do you know?" }),
281
+ });
282
+ const data = await res.json();
283
+
284
+ expect(res.status).toBe(200);
285
+ expect(data.memoriesRecalled).toBe(3);
286
+ expect(data.factsRecalled).toBe(2);
287
+ });
288
+ });
289
+
290
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
291
+ // Recall Endpoint
292
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
293
+
294
+ describe("GET /recall", () => {
295
+ it("should return memories and facts for query", async () => {
296
+ // Add test data
297
+ memories.push({
298
+ memoryId: "mem-1",
299
+ content: "Test memory",
300
+ importance: 80,
301
+ });
302
+ facts.push({
303
+ factId: "fact-1",
304
+ fact: "Test fact",
305
+ factType: "knowledge",
306
+ confidence: 90,
307
+ });
308
+
309
+ const res = await app.request("/recall?query=test");
310
+ const data = await res.json();
311
+
312
+ expect(res.status).toBe(200);
313
+ expect(data.memories).toBeDefined();
314
+ expect(data.memories.length).toBe(1);
315
+ expect(data.facts).toBeDefined();
316
+ expect(data.facts.length).toBe(1);
317
+ expect(data.query).toBe("test");
318
+ });
319
+
320
+ it("should return 400 if query is missing", async () => {
321
+ const res = await app.request("/recall");
322
+ const data = await res.json();
323
+
324
+ expect(res.status).toBe(400);
325
+ expect(data.error).toBe("query parameter is required");
326
+ });
327
+
328
+ it("should return empty arrays when no memories found", async () => {
329
+ const res = await app.request("/recall?query=nonexistent");
330
+ const data = await res.json();
331
+
332
+ expect(res.status).toBe(200);
333
+ expect(data.memories).toEqual([]);
334
+ expect(data.facts).toEqual([]);
335
+ });
336
+ });
337
+
338
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
339
+ // Facts Endpoint
340
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
341
+
342
+ describe("GET /facts", () => {
343
+ it("should return list of facts", async () => {
344
+ facts.push({
345
+ factId: "fact-1",
346
+ fact: "User likes coffee",
347
+ factType: "preference",
348
+ confidence: 85,
349
+ });
350
+ facts.push({
351
+ factId: "fact-2",
352
+ fact: "User is a developer",
353
+ factType: "occupation",
354
+ confidence: 90,
355
+ });
356
+
357
+ const res = await app.request("/facts");
358
+ const data = await res.json();
359
+
360
+ expect(res.status).toBe(200);
361
+ expect(data.facts).toBeDefined();
362
+ expect(data.facts.length).toBe(2);
363
+ expect(data.count).toBe(2);
364
+ });
365
+
366
+ it("should return empty array when no facts exist", async () => {
367
+ const res = await app.request("/facts");
368
+ const data = await res.json();
369
+
370
+ expect(res.status).toBe(200);
371
+ expect(data.facts).toEqual([]);
372
+ expect(data.count).toBe(0);
373
+ });
374
+ });
375
+
376
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
377
+ // History Endpoint
378
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
379
+
380
+ describe("GET /history/:conversationId", () => {
381
+ it("should return conversation history", async () => {
382
+ // Add conversation
383
+ conversations.set("conv-123", {
384
+ conversationId: "conv-123",
385
+ messages: [
386
+ { role: "user", content: "Hello" },
387
+ { role: "assistant", content: "Hi there!" },
388
+ ],
389
+ });
390
+
391
+ const res = await app.request("/history/conv-123");
392
+ const data = await res.json();
393
+
394
+ expect(res.status).toBe(200);
395
+ expect(data.conversationId).toBe("conv-123");
396
+ expect(data.messages).toBeDefined();
397
+ expect(data.messageCount).toBe(2);
398
+ });
399
+
400
+ it("should return 404 for non-existent conversation", async () => {
401
+ const res = await app.request("/history/nonexistent-conv");
402
+ const data = await res.json();
403
+
404
+ expect(res.status).toBe(404);
405
+ expect(data.error).toBe("Conversation not found");
406
+ });
407
+
408
+ it("should return message count", async () => {
409
+ conversations.set("conv-456", {
410
+ conversationId: "conv-456",
411
+ messages: [
412
+ { role: "user", content: "First" },
413
+ { role: "assistant", content: "First reply" },
414
+ { role: "user", content: "Second" },
415
+ { role: "assistant", content: "Second reply" },
416
+ ],
417
+ });
418
+
419
+ const res = await app.request("/history/conv-456");
420
+ const data = await res.json();
421
+
422
+ expect(res.status).toBe(200);
423
+ expect(data.messageCount).toBe(4);
424
+ });
425
+ });
426
+
427
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
428
+ // Root Endpoint
429
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
430
+
431
+ describe("GET /", () => {
432
+ it("should return API info", async () => {
433
+ const res = await app.request("/");
434
+ const data = await res.json();
435
+
436
+ expect(res.status).toBe(200);
437
+ expect(data.name).toContain("Cortex Memory");
438
+ expect(data.version).toBeDefined();
439
+ });
440
+ });
441
+ });