@cortexmemory/cli 0.27.1 → 0.27.3

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 (46) hide show
  1. package/dist/commands/convex.js +1 -1
  2. package/dist/commands/convex.js.map +1 -1
  3. package/dist/commands/deploy.d.ts +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +771 -144
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/dev.d.ts.map +1 -1
  8. package/dist/commands/dev.js +89 -26
  9. package/dist/commands/dev.js.map +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/utils/app-template-sync.d.ts +95 -0
  12. package/dist/utils/app-template-sync.d.ts.map +1 -0
  13. package/dist/utils/app-template-sync.js +425 -0
  14. package/dist/utils/app-template-sync.js.map +1 -0
  15. package/dist/utils/deployment-selector.d.ts +21 -0
  16. package/dist/utils/deployment-selector.d.ts.map +1 -1
  17. package/dist/utils/deployment-selector.js +32 -0
  18. package/dist/utils/deployment-selector.js.map +1 -1
  19. package/dist/utils/init/graph-setup.d.ts.map +1 -1
  20. package/dist/utils/init/graph-setup.js +13 -2
  21. package/dist/utils/init/graph-setup.js.map +1 -1
  22. package/package.json +1 -1
  23. package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +30 -0
  24. package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +83 -0
  25. package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +94 -0
  26. package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +59 -0
  27. package/templates/vercel-ai-quickstart/app/api/chat/route.ts +83 -2
  28. package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +179 -0
  29. package/templates/vercel-ai-quickstart/app/globals.css +161 -0
  30. package/templates/vercel-ai-quickstart/app/page.tsx +93 -8
  31. package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +139 -0
  32. package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +283 -0
  33. package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +323 -0
  34. package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +113 -16
  35. package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +202 -0
  36. package/templates/vercel-ai-quickstart/jest.config.js +45 -0
  37. package/templates/vercel-ai-quickstart/lib/cortex.ts +27 -0
  38. package/templates/vercel-ai-quickstart/lib/password.ts +120 -0
  39. package/templates/vercel-ai-quickstart/next.config.js +20 -0
  40. package/templates/vercel-ai-quickstart/package.json +7 -2
  41. package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +263 -0
  42. package/templates/vercel-ai-quickstart/tests/helpers/setup.ts +48 -0
  43. package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +455 -0
  44. package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +461 -0
  45. package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +228 -0
  46. package/templates/vercel-ai-quickstart/tsconfig.json +1 -1
@@ -112,3 +112,164 @@
112
112
  font-size: 0.875rem;
113
113
  line-height: 1.5;
114
114
  }
115
+
116
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
117
+ Auth Screen Animations
118
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
119
+
120
+ @keyframes fade-in-up {
121
+ from {
122
+ opacity: 0;
123
+ transform: translateY(20px);
124
+ }
125
+ to {
126
+ opacity: 1;
127
+ transform: translateY(0);
128
+ }
129
+ }
130
+
131
+ @keyframes logo-pulse {
132
+ 0%, 100% {
133
+ transform: scale(1);
134
+ box-shadow: 0 0 0 0 rgba(12, 140, 230, 0.4);
135
+ }
136
+ 50% {
137
+ transform: scale(1.02);
138
+ box-shadow: 0 0 40px 10px rgba(12, 140, 230, 0.2);
139
+ }
140
+ }
141
+
142
+ .auth-screen {
143
+ animation: fade-in-up 0.5s ease-out;
144
+ }
145
+
146
+ .auth-logo {
147
+ animation: logo-pulse 3s ease-in-out infinite;
148
+ }
149
+
150
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
151
+ Sidebar Animations
152
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
153
+
154
+ @keyframes sidebar-slide-in {
155
+ from {
156
+ opacity: 0;
157
+ transform: translateX(-20px);
158
+ }
159
+ to {
160
+ opacity: 1;
161
+ transform: translateX(0);
162
+ }
163
+ }
164
+
165
+ @keyframes conversation-item-in {
166
+ from {
167
+ opacity: 0;
168
+ transform: translateX(-10px);
169
+ }
170
+ to {
171
+ opacity: 1;
172
+ transform: translateX(0);
173
+ }
174
+ }
175
+
176
+ .sidebar-animate {
177
+ animation: sidebar-slide-in 0.3s ease-out;
178
+ }
179
+
180
+ .conversation-item {
181
+ animation: conversation-item-in 0.2s ease-out;
182
+ }
183
+
184
+ /* Staggered animation for conversation list */
185
+ .conversation-item:nth-child(1) { animation-delay: 0ms; }
186
+ .conversation-item:nth-child(2) { animation-delay: 30ms; }
187
+ .conversation-item:nth-child(3) { animation-delay: 60ms; }
188
+ .conversation-item:nth-child(4) { animation-delay: 90ms; }
189
+ .conversation-item:nth-child(5) { animation-delay: 120ms; }
190
+ .conversation-item:nth-child(n+6) { animation-delay: 150ms; }
191
+
192
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
193
+ Input Focus Animations
194
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
195
+
196
+ input:focus {
197
+ box-shadow: 0 0 0 2px rgba(12, 140, 230, 0.2);
198
+ }
199
+
200
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
201
+ Button Hover Effects
202
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
203
+
204
+ .btn-primary {
205
+ position: relative;
206
+ overflow: hidden;
207
+ }
208
+
209
+ .btn-primary::after {
210
+ content: "";
211
+ position: absolute;
212
+ top: 50%;
213
+ left: 50%;
214
+ width: 0;
215
+ height: 0;
216
+ background: rgba(255, 255, 255, 0.1);
217
+ border-radius: 50%;
218
+ transform: translate(-50%, -50%);
219
+ transition: width 0.4s ease, height 0.4s ease;
220
+ }
221
+
222
+ .btn-primary:hover::after {
223
+ width: 300px;
224
+ height: 300px;
225
+ }
226
+
227
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
228
+ Loading States
229
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
230
+
231
+ @keyframes shimmer {
232
+ 0% {
233
+ background-position: -200% 0;
234
+ }
235
+ 100% {
236
+ background-position: 200% 0;
237
+ }
238
+ }
239
+
240
+ .skeleton {
241
+ background: linear-gradient(
242
+ 90deg,
243
+ rgba(255, 255, 255, 0.05) 25%,
244
+ rgba(255, 255, 255, 0.1) 50%,
245
+ rgba(255, 255, 255, 0.05) 75%
246
+ );
247
+ background-size: 200% 100%;
248
+ animation: shimmer 1.5s infinite;
249
+ }
250
+
251
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
252
+ Responsive Sidebar
253
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
254
+
255
+ @media (max-width: 1024px) {
256
+ /* On smaller screens, the layer flow visualization is hidden */
257
+ .layer-flow-panel {
258
+ display: none;
259
+ }
260
+ }
261
+
262
+ @media (max-width: 768px) {
263
+ /* On mobile, sidebar becomes collapsible */
264
+ .sidebar-mobile-hidden {
265
+ transform: translateX(-100%);
266
+ transition: transform 0.3s ease;
267
+ }
268
+
269
+ .sidebar-mobile-visible {
270
+ transform: translateX(0);
271
+ position: fixed;
272
+ z-index: 50;
273
+ height: 100vh;
274
+ }
275
+ }
@@ -1,8 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import dynamic from "next/dynamic";
4
- import { useState } from "react";
4
+ import { useState, useCallback } from "react";
5
5
  import { useLayerTracking } from "@/lib/layer-tracking";
6
+ import { AuthProvider, useAuth } from "@/components/AuthProvider";
7
+ import { AdminSetup } from "@/components/AdminSetup";
8
+ import { LoginScreen } from "@/components/LoginScreen";
6
9
 
7
10
  // Dynamic imports to avoid SSR issues with framer-motion
8
11
  const ChatInterface = dynamic(
@@ -33,10 +36,22 @@ const HealthStatus = dynamic(
33
36
  })),
34
37
  { ssr: false },
35
38
  );
39
+ const ChatHistorySidebar = dynamic(
40
+ () =>
41
+ import("@/components/ChatHistorySidebar").then((m) => ({
42
+ default: m.ChatHistorySidebar,
43
+ })),
44
+ { ssr: false },
45
+ );
36
46
 
37
- export default function Home() {
47
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
48
+ // Main App Content (with auth)
49
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
50
+
51
+ function MainContent() {
52
+ const { isLoading, isAdminSetup, isAuthenticated, user } = useAuth();
38
53
  const [memorySpaceId, setMemorySpaceId] = useState("quickstart-demo");
39
- const [userId] = useState("demo-user");
54
+ const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);
40
55
  const {
41
56
  layers,
42
57
  isOrchestrating,
@@ -45,11 +60,59 @@ export default function Home() {
45
60
  resetLayers,
46
61
  } = useLayerTracking();
47
62
 
63
+ // Handle new chat
64
+ const handleNewChat = useCallback(() => {
65
+ setCurrentConversationId(null);
66
+ resetLayers();
67
+ }, [resetLayers]);
68
+
69
+ // Handle conversation selection
70
+ const handleSelectConversation = useCallback((conversationId: string) => {
71
+ setCurrentConversationId(conversationId);
72
+ resetLayers();
73
+ }, [resetLayers]);
74
+
75
+ // Handle conversation update (e.g., title change after first message)
76
+ const handleConversationUpdate = useCallback((conversationId: string) => {
77
+ // Update current conversation ID if it was null (new chat created)
78
+ if (!currentConversationId) {
79
+ setCurrentConversationId(conversationId);
80
+ }
81
+ }, [currentConversationId]);
82
+
83
+ // Loading state
84
+ if (isLoading) {
85
+ return (
86
+ <div className="min-h-screen flex items-center justify-center">
87
+ <div className="text-center">
88
+ <div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-cortex-500 to-cortex-700 flex items-center justify-center animate-pulse">
89
+ <span className="text-3xl">🧠</span>
90
+ </div>
91
+ <p className="text-gray-400">Loading Cortex...</p>
92
+ </div>
93
+ </div>
94
+ );
95
+ }
96
+
97
+ // First-run: Admin setup
98
+ if (isAdminSetup === false) {
99
+ return <AdminSetup />;
100
+ }
101
+
102
+ // Not authenticated: Login/Register
103
+ if (!isAuthenticated) {
104
+ return <LoginScreen />;
105
+ }
106
+
107
+ // Get userId from authenticated user
108
+ const userId = user?.id || "demo-user";
109
+
110
+ // Main authenticated interface
48
111
  return (
49
112
  <main className="min-h-screen flex flex-col">
50
113
  {/* Header */}
51
114
  <header className="border-b border-white/10 px-6 py-4">
52
- <div className="max-w-7xl mx-auto flex items-center justify-between">
115
+ <div className="flex items-center justify-between">
53
116
  <div className="flex items-center gap-3">
54
117
  <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-cortex-500 to-cortex-700 flex items-center justify-center">
55
118
  <span className="text-xl">🧠</span>
@@ -72,20 +135,30 @@ export default function Home() {
72
135
  </div>
73
136
  </header>
74
137
 
75
- {/* Main Content */}
138
+ {/* Main Content - Three Column Layout */}
76
139
  <div className="flex-1 flex overflow-hidden">
77
- {/* Chat Section */}
140
+ {/* Left: Chat History Sidebar */}
141
+ <ChatHistorySidebar
142
+ memorySpaceId={memorySpaceId}
143
+ currentConversationId={currentConversationId}
144
+ onSelectConversation={handleSelectConversation}
145
+ onNewChat={handleNewChat}
146
+ />
147
+
148
+ {/* Center: Chat Section */}
78
149
  <div className="flex-1 flex flex-col border-r border-white/10">
79
150
  <ChatInterface
80
151
  memorySpaceId={memorySpaceId}
81
152
  userId={userId}
153
+ conversationId={currentConversationId}
82
154
  onOrchestrationStart={startOrchestration}
83
155
  onLayerUpdate={updateLayer}
84
156
  onReset={resetLayers}
157
+ onConversationUpdate={handleConversationUpdate}
85
158
  />
86
159
  </div>
87
160
 
88
- {/* Layer Flow Visualization */}
161
+ {/* Right: Layer Flow Visualization */}
89
162
  <div className="w-[480px] flex flex-col bg-black/20">
90
163
  <div className="p-4 border-b border-white/10">
91
164
  <h2 className="font-semibold flex items-center gap-2">
@@ -110,7 +183,7 @@ export default function Home() {
110
183
 
111
184
  {/* Footer */}
112
185
  <footer className="border-t border-white/10 px-6 py-3">
113
- <div className="max-w-7xl mx-auto flex items-center justify-between text-sm text-gray-500">
186
+ <div className="flex items-center justify-between text-sm text-gray-500">
114
187
  <div className="flex items-center gap-4">
115
188
  <span>Cortex SDK v0.24.0</span>
116
189
  <span>•</span>
@@ -129,3 +202,15 @@ export default function Home() {
129
202
  </main>
130
203
  );
131
204
  }
205
+
206
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
207
+ // Page Component (wraps with AuthProvider)
208
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
209
+
210
+ export default function Home() {
211
+ return (
212
+ <AuthProvider>
213
+ <MainContent />
214
+ </AuthProvider>
215
+ );
216
+ }
@@ -0,0 +1,139 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { useAuth } from "./AuthProvider";
5
+
6
+ export function AdminSetup() {
7
+ const { setupAdmin, error, clearError } = useAuth();
8
+ const [password, setPassword] = useState("");
9
+ const [confirmPassword, setConfirmPassword] = useState("");
10
+ const [isLoading, setIsLoading] = useState(false);
11
+ const [localError, setLocalError] = useState<string | null>(null);
12
+
13
+ const handleSubmit = async (e: React.FormEvent) => {
14
+ e.preventDefault();
15
+ setLocalError(null);
16
+ clearError();
17
+
18
+ if (password.length < 4) {
19
+ setLocalError("Password must be at least 4 characters");
20
+ return;
21
+ }
22
+
23
+ if (password !== confirmPassword) {
24
+ setLocalError("Passwords do not match");
25
+ return;
26
+ }
27
+
28
+ setIsLoading(true);
29
+ await setupAdmin(password);
30
+ setIsLoading(false);
31
+ };
32
+
33
+ const displayError = localError || error;
34
+
35
+ return (
36
+ <div className="min-h-screen flex items-center justify-center p-4">
37
+ <div className="w-full max-w-md">
38
+ {/* Logo and Title */}
39
+ <div className="text-center mb-8">
40
+ <div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-cortex-500 to-cortex-700 flex items-center justify-center shadow-lg shadow-cortex-500/20">
41
+ <span className="text-4xl">🧠</span>
42
+ </div>
43
+ <h1 className="text-3xl font-bold mb-2">Welcome to Cortex</h1>
44
+ <p className="text-gray-400">Set up your admin password to get started</p>
45
+ </div>
46
+
47
+ {/* Setup Form */}
48
+ <div className="glass rounded-2xl p-8">
49
+ <form onSubmit={handleSubmit} className="space-y-6">
50
+ <div>
51
+ <label
52
+ htmlFor="password"
53
+ className="block text-sm font-medium text-gray-300 mb-2"
54
+ >
55
+ Admin Password
56
+ </label>
57
+ <input
58
+ id="password"
59
+ type="password"
60
+ value={password}
61
+ onChange={(e) => setPassword(e.target.value)}
62
+ placeholder="Enter a secure password"
63
+ className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
64
+ disabled={isLoading}
65
+ autoFocus
66
+ />
67
+ </div>
68
+
69
+ <div>
70
+ <label
71
+ htmlFor="confirmPassword"
72
+ className="block text-sm font-medium text-gray-300 mb-2"
73
+ >
74
+ Confirm Password
75
+ </label>
76
+ <input
77
+ id="confirmPassword"
78
+ type="password"
79
+ value={confirmPassword}
80
+ onChange={(e) => setConfirmPassword(e.target.value)}
81
+ placeholder="Confirm your password"
82
+ className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
83
+ disabled={isLoading}
84
+ />
85
+ </div>
86
+
87
+ {displayError && (
88
+ <div className="p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-sm">
89
+ {displayError}
90
+ </div>
91
+ )}
92
+
93
+ <button
94
+ type="submit"
95
+ disabled={isLoading || !password || !confirmPassword}
96
+ className="w-full py-3 bg-cortex-600 hover:bg-cortex-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl font-medium transition-colors flex items-center justify-center gap-2"
97
+ >
98
+ {isLoading ? (
99
+ <>
100
+ <svg
101
+ className="animate-spin h-5 w-5"
102
+ xmlns="http://www.w3.org/2000/svg"
103
+ fill="none"
104
+ viewBox="0 0 24 24"
105
+ >
106
+ <circle
107
+ className="opacity-25"
108
+ cx="12"
109
+ cy="12"
110
+ r="10"
111
+ stroke="currentColor"
112
+ strokeWidth="4"
113
+ />
114
+ <path
115
+ className="opacity-75"
116
+ fill="currentColor"
117
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
118
+ />
119
+ </svg>
120
+ Setting up...
121
+ </>
122
+ ) : (
123
+ "Set Admin Password"
124
+ )}
125
+ </button>
126
+ </form>
127
+
128
+ <div className="mt-6 pt-6 border-t border-white/10">
129
+ <p className="text-xs text-gray-500 text-center">
130
+ This password will be used to manage the quickstart demo.
131
+ <br />
132
+ You can create additional user accounts after setup.
133
+ </p>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ );
139
+ }