@cortexmemory/cli 0.27.1 → 0.27.4
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/convex.js +1 -1
- package/dist/commands/convex.js.map +1 -1
- package/dist/commands/deploy.d.ts +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +839 -141
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +89 -26
- package/dist/commands/dev.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/app-template-sync.d.ts +95 -0
- package/dist/utils/app-template-sync.d.ts.map +1 -0
- package/dist/utils/app-template-sync.js +445 -0
- package/dist/utils/app-template-sync.js.map +1 -0
- package/dist/utils/deployment-selector.d.ts +21 -0
- package/dist/utils/deployment-selector.d.ts.map +1 -1
- package/dist/utils/deployment-selector.js +32 -0
- package/dist/utils/deployment-selector.js.map +1 -1
- package/dist/utils/init/graph-setup.d.ts.map +1 -1
- package/dist/utils/init/graph-setup.js +13 -2
- package/dist/utils/init/graph-setup.js.map +1 -1
- package/package.json +1 -1
- package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +30 -0
- package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +128 -0
- package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +94 -0
- package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +59 -0
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +139 -3
- package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +333 -0
- package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +179 -0
- package/templates/vercel-ai-quickstart/app/globals.css +161 -0
- package/templates/vercel-ai-quickstart/app/page.tsx +110 -11
- package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +139 -0
- package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +283 -0
- package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +323 -0
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +117 -17
- package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +202 -0
- package/templates/vercel-ai-quickstart/jest.config.js +52 -0
- package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +165 -0
- package/templates/vercel-ai-quickstart/lib/cortex.ts +27 -0
- package/templates/vercel-ai-quickstart/lib/password.ts +120 -0
- package/templates/vercel-ai-quickstart/lib/versions.ts +60 -0
- package/templates/vercel-ai-quickstart/next.config.js +20 -0
- package/templates/vercel-ai-quickstart/package.json +11 -2
- package/templates/vercel-ai-quickstart/test-api.mjs +272 -0
- package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +454 -0
- package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +263 -0
- package/templates/vercel-ai-quickstart/tests/helpers/setup.ts +48 -0
- package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +455 -0
- package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +461 -0
- package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +228 -0
- package/templates/vercel-ai-quickstart/tsconfig.json +1 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Cortex SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of the Cortex SDK for testing.
|
|
5
|
+
* Simulates the SDK behavior without requiring a real Convex backend.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// In-memory stores for test state
|
|
9
|
+
let mutableStore: Map<string, Map<string, unknown>> = new Map();
|
|
10
|
+
let usersStore: Map<string, { userId: string; data: Record<string, unknown> }> =
|
|
11
|
+
new Map();
|
|
12
|
+
interface StoredConversation {
|
|
13
|
+
conversationId: string;
|
|
14
|
+
memorySpaceId: string;
|
|
15
|
+
type: string;
|
|
16
|
+
participants: { userId?: string; agentId?: string };
|
|
17
|
+
metadata: Record<string, unknown>;
|
|
18
|
+
createdAt: number;
|
|
19
|
+
updatedAt: number;
|
|
20
|
+
messageCount: number;
|
|
21
|
+
messages?: Array<{
|
|
22
|
+
id: string;
|
|
23
|
+
role: string;
|
|
24
|
+
content: string;
|
|
25
|
+
timestamp: number;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let conversationsStore: Map<string, StoredConversation> = new Map();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reset all mock stores (call in beforeEach)
|
|
33
|
+
*/
|
|
34
|
+
export function resetMockStores(): void {
|
|
35
|
+
mutableStore = new Map();
|
|
36
|
+
usersStore = new Map();
|
|
37
|
+
conversationsStore = new Map();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a mock Cortex SDK instance
|
|
42
|
+
*/
|
|
43
|
+
export function createMockCortex() {
|
|
44
|
+
return {
|
|
45
|
+
mutable: {
|
|
46
|
+
get: jest.fn(
|
|
47
|
+
async (namespace: string, key: string): Promise<unknown | null> => {
|
|
48
|
+
const ns = mutableStore.get(namespace);
|
|
49
|
+
if (!ns) return null;
|
|
50
|
+
return ns.get(key) ?? null;
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
set: jest.fn(
|
|
54
|
+
async (
|
|
55
|
+
namespace: string,
|
|
56
|
+
key: string,
|
|
57
|
+
value: unknown
|
|
58
|
+
): Promise<void> => {
|
|
59
|
+
if (!mutableStore.has(namespace)) {
|
|
60
|
+
mutableStore.set(namespace, new Map());
|
|
61
|
+
}
|
|
62
|
+
mutableStore.get(namespace)!.set(key, value);
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
delete: jest.fn(
|
|
66
|
+
async (namespace: string, key: string): Promise<void> => {
|
|
67
|
+
const ns = mutableStore.get(namespace);
|
|
68
|
+
if (ns) ns.delete(key);
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
users: {
|
|
73
|
+
get: jest.fn(
|
|
74
|
+
async (
|
|
75
|
+
userId: string
|
|
76
|
+
): Promise<{
|
|
77
|
+
userId: string;
|
|
78
|
+
data: Record<string, unknown>;
|
|
79
|
+
} | null> => {
|
|
80
|
+
return usersStore.get(userId) ?? null;
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
update: jest.fn(
|
|
84
|
+
async (
|
|
85
|
+
userId: string,
|
|
86
|
+
data: Record<string, unknown>
|
|
87
|
+
): Promise<{ userId: string; data: Record<string, unknown> }> => {
|
|
88
|
+
const existing = usersStore.get(userId);
|
|
89
|
+
const user = {
|
|
90
|
+
userId,
|
|
91
|
+
data: { ...(existing?.data || {}), ...data },
|
|
92
|
+
};
|
|
93
|
+
usersStore.set(userId, user);
|
|
94
|
+
return user;
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
delete: jest.fn(async (userId: string): Promise<void> => {
|
|
98
|
+
usersStore.delete(userId);
|
|
99
|
+
}),
|
|
100
|
+
list: jest.fn(async () => {
|
|
101
|
+
return {
|
|
102
|
+
users: Array.from(usersStore.values()),
|
|
103
|
+
hasMore: false,
|
|
104
|
+
};
|
|
105
|
+
}),
|
|
106
|
+
},
|
|
107
|
+
conversations: {
|
|
108
|
+
list: jest.fn(
|
|
109
|
+
async (params: { memorySpaceId?: string; userId?: string }) => {
|
|
110
|
+
const conversations = Array.from(conversationsStore.values()).filter(
|
|
111
|
+
(conv) => {
|
|
112
|
+
if (
|
|
113
|
+
params.memorySpaceId &&
|
|
114
|
+
conv.memorySpaceId !== params.memorySpaceId
|
|
115
|
+
) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (
|
|
119
|
+
params.userId &&
|
|
120
|
+
conv.participants.userId !== params.userId
|
|
121
|
+
) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
return { conversations, hasMore: false };
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
create: jest.fn(
|
|
131
|
+
async (params: {
|
|
132
|
+
memorySpaceId: string;
|
|
133
|
+
conversationId: string;
|
|
134
|
+
type: string;
|
|
135
|
+
participants: { userId?: string; agentId?: string };
|
|
136
|
+
metadata?: Record<string, unknown>;
|
|
137
|
+
}) => {
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
const conversation = {
|
|
140
|
+
conversationId: params.conversationId,
|
|
141
|
+
memorySpaceId: params.memorySpaceId,
|
|
142
|
+
type: params.type,
|
|
143
|
+
participants: params.participants,
|
|
144
|
+
metadata: params.metadata || {},
|
|
145
|
+
createdAt: now,
|
|
146
|
+
updatedAt: now,
|
|
147
|
+
messageCount: 0,
|
|
148
|
+
};
|
|
149
|
+
conversationsStore.set(params.conversationId, conversation);
|
|
150
|
+
return conversation;
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
get: jest.fn(async (conversationId: string, options?: { includeMessages?: boolean; messageLimit?: number }) => {
|
|
154
|
+
const conv = conversationsStore.get(conversationId);
|
|
155
|
+
if (!conv) return null;
|
|
156
|
+
|
|
157
|
+
// If includeMessages is requested, return with messages
|
|
158
|
+
if (options?.includeMessages) {
|
|
159
|
+
return {
|
|
160
|
+
...conv,
|
|
161
|
+
messages: conv.messages || [],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Otherwise return without messages
|
|
166
|
+
const { messages: _messages, ...convWithoutMessages } = conv;
|
|
167
|
+
return convWithoutMessages;
|
|
168
|
+
}),
|
|
169
|
+
delete: jest.fn(async (conversationId: string): Promise<void> => {
|
|
170
|
+
conversationsStore.delete(conversationId);
|
|
171
|
+
}),
|
|
172
|
+
update: jest.fn(
|
|
173
|
+
async (
|
|
174
|
+
conversationId: string,
|
|
175
|
+
updates: { metadata?: Record<string, unknown> }
|
|
176
|
+
) => {
|
|
177
|
+
const conv = conversationsStore.get(conversationId);
|
|
178
|
+
if (!conv) throw new Error("Conversation not found");
|
|
179
|
+
const updated = {
|
|
180
|
+
...conv,
|
|
181
|
+
metadata: { ...conv.metadata, ...updates.metadata },
|
|
182
|
+
updatedAt: Date.now(),
|
|
183
|
+
};
|
|
184
|
+
conversationsStore.set(conversationId, updated);
|
|
185
|
+
return updated;
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
},
|
|
189
|
+
memory: {
|
|
190
|
+
search: jest.fn().mockResolvedValue([]),
|
|
191
|
+
remember: jest.fn().mockResolvedValue({
|
|
192
|
+
conversation: { conversationId: "conv-1", messageIds: [] },
|
|
193
|
+
memories: [],
|
|
194
|
+
facts: [],
|
|
195
|
+
}),
|
|
196
|
+
recall: jest.fn().mockResolvedValue({
|
|
197
|
+
context: "",
|
|
198
|
+
totalResults: 0,
|
|
199
|
+
queryTimeMs: 10,
|
|
200
|
+
sources: { vector: { count: 0, items: [] } },
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
203
|
+
close: jest.fn(),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Type for the mock Cortex instance
|
|
209
|
+
*/
|
|
210
|
+
export type MockCortex = ReturnType<typeof createMockCortex>;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Helper to seed test data into stores
|
|
214
|
+
*/
|
|
215
|
+
export const seedTestData = {
|
|
216
|
+
user: (
|
|
217
|
+
userId: string,
|
|
218
|
+
data: Record<string, unknown> = {}
|
|
219
|
+
): { userId: string; data: Record<string, unknown> } => {
|
|
220
|
+
const user = { userId, data };
|
|
221
|
+
usersStore.set(userId, user);
|
|
222
|
+
return user;
|
|
223
|
+
},
|
|
224
|
+
mutable: (namespace: string, key: string, value: unknown): void => {
|
|
225
|
+
if (!mutableStore.has(namespace)) {
|
|
226
|
+
mutableStore.set(namespace, new Map());
|
|
227
|
+
}
|
|
228
|
+
mutableStore.get(namespace)!.set(key, value);
|
|
229
|
+
},
|
|
230
|
+
conversation: (
|
|
231
|
+
conversationId: string,
|
|
232
|
+
params: {
|
|
233
|
+
memorySpaceId?: string;
|
|
234
|
+
userId?: string;
|
|
235
|
+
title?: string;
|
|
236
|
+
messages?: Array<{ role: string; content: string }>;
|
|
237
|
+
} = {}
|
|
238
|
+
) => {
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
const messages = params.messages?.map((msg, i) => ({
|
|
241
|
+
id: `msg-${i}`,
|
|
242
|
+
role: msg.role,
|
|
243
|
+
content: msg.content,
|
|
244
|
+
timestamp: now + i,
|
|
245
|
+
}));
|
|
246
|
+
const conversation: StoredConversation = {
|
|
247
|
+
conversationId,
|
|
248
|
+
memorySpaceId: params.memorySpaceId || "quickstart-demo",
|
|
249
|
+
type: "user-agent",
|
|
250
|
+
participants: {
|
|
251
|
+
userId: params.userId || "test-user",
|
|
252
|
+
agentId: "quickstart-assistant",
|
|
253
|
+
},
|
|
254
|
+
metadata: { title: params.title || "Test Chat" },
|
|
255
|
+
createdAt: now,
|
|
256
|
+
updatedAt: now,
|
|
257
|
+
messageCount: messages?.length || 0,
|
|
258
|
+
messages,
|
|
259
|
+
};
|
|
260
|
+
conversationsStore.set(conversationId, conversation);
|
|
261
|
+
return conversation;
|
|
262
|
+
},
|
|
263
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest Test Setup
|
|
3
|
+
*
|
|
4
|
+
* Configures global test environment for quickstart tests.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { TextEncoder, TextDecoder } from "util";
|
|
8
|
+
|
|
9
|
+
// Polyfill TextEncoder/TextDecoder for Node.js environment
|
|
10
|
+
global.TextEncoder = TextEncoder;
|
|
11
|
+
global.TextDecoder = TextDecoder as typeof global.TextDecoder;
|
|
12
|
+
|
|
13
|
+
// Polyfill crypto for Node.js environment (required for password utilities)
|
|
14
|
+
if (typeof global.crypto === "undefined") {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
+
const { webcrypto } = require("crypto");
|
|
17
|
+
global.crypto = webcrypto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Polyfill btoa/atob for Node.js environment
|
|
21
|
+
if (typeof global.btoa === "undefined") {
|
|
22
|
+
global.btoa = (str: string) => Buffer.from(str, "binary").toString("base64");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof global.atob === "undefined") {
|
|
26
|
+
global.atob = (str: string) => Buffer.from(str, "base64").toString("binary");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Set test environment variables
|
|
30
|
+
process.env.NODE_ENV = "test";
|
|
31
|
+
process.env.CONVEX_URL = "https://test.convex.cloud";
|
|
32
|
+
|
|
33
|
+
// Suppress console output during tests (optional - comment out for debugging)
|
|
34
|
+
// global.console = {
|
|
35
|
+
// ...console,
|
|
36
|
+
// log: jest.fn(),
|
|
37
|
+
// debug: jest.fn(),
|
|
38
|
+
// info: jest.fn(),
|
|
39
|
+
// warn: jest.fn(),
|
|
40
|
+
// };
|
|
41
|
+
|
|
42
|
+
// Increase test timeout for async operations
|
|
43
|
+
jest.setTimeout(10000);
|
|
44
|
+
|
|
45
|
+
// Reset mocks between tests
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
jest.clearAllMocks();
|
|
48
|
+
});
|