@cortexmemory/cli 0.26.0 → 0.27.1
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/db.d.ts.map +1 -1
- package/dist/commands/db.js +2 -18
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +153 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +373 -70
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +102 -46
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +94 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +20 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/init/convex-setup.d.ts +58 -6
- package/dist/utils/init/convex-setup.d.ts.map +1 -1
- package/dist/utils/init/convex-setup.js +261 -57
- package/dist/utils/init/convex-setup.js.map +1 -1
- package/dist/utils/init/env-generator.d.ts.map +1 -1
- package/dist/utils/init/env-generator.js +12 -2
- package/dist/utils/init/env-generator.js.map +1 -1
- package/dist/utils/init/graph-setup.d.ts.map +1 -1
- package/dist/utils/init/graph-setup.js +12 -0
- package/dist/utils/init/graph-setup.js.map +1 -1
- package/dist/utils/init/quickstart-setup.d.ts +87 -0
- package/dist/utils/init/quickstart-setup.d.ts.map +1 -0
- package/dist/utils/init/quickstart-setup.js +462 -0
- package/dist/utils/init/quickstart-setup.js.map +1 -0
- package/dist/utils/init/types.d.ts +4 -0
- package/dist/utils/init/types.d.ts.map +1 -1
- package/dist/utils/schema-sync.d.ts.map +1 -1
- package/dist/utils/schema-sync.js +27 -21
- package/dist/utils/schema-sync.js.map +1 -1
- package/package.json +3 -2
- package/templates/vercel-ai-quickstart/.env.local.example +45 -0
- package/templates/vercel-ai-quickstart/README.md +280 -0
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +196 -0
- package/templates/vercel-ai-quickstart/app/api/facts/route.ts +39 -0
- package/templates/vercel-ai-quickstart/app/api/health/route.ts +99 -0
- package/templates/vercel-ai-quickstart/app/api/memories/route.ts +37 -0
- package/templates/vercel-ai-quickstart/app/globals.css +114 -0
- package/templates/vercel-ai-quickstart/app/layout.tsx +19 -0
- package/templates/vercel-ai-quickstart/app/page.tsx +131 -0
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +237 -0
- package/templates/vercel-ai-quickstart/components/ConvexClientProvider.tsx +21 -0
- package/templates/vercel-ai-quickstart/components/DataPreview.tsx +57 -0
- package/templates/vercel-ai-quickstart/components/HealthStatus.tsx +214 -0
- package/templates/vercel-ai-quickstart/components/LayerCard.tsx +263 -0
- package/templates/vercel-ai-quickstart/components/LayerFlowDiagram.tsx +195 -0
- package/templates/vercel-ai-quickstart/components/MemorySpaceSwitcher.tsx +93 -0
- package/templates/vercel-ai-quickstart/convex/conversations.ts +67 -0
- package/templates/vercel-ai-quickstart/convex/facts.ts +131 -0
- package/templates/vercel-ai-quickstart/convex/health.ts +15 -0
- package/templates/vercel-ai-quickstart/convex/memories.ts +104 -0
- package/templates/vercel-ai-quickstart/convex/schema.ts +20 -0
- package/templates/vercel-ai-quickstart/convex/users.ts +105 -0
- package/templates/vercel-ai-quickstart/lib/animations.ts +146 -0
- package/templates/vercel-ai-quickstart/lib/layer-tracking.ts +214 -0
- package/templates/vercel-ai-quickstart/next.config.js +7 -0
- package/templates/vercel-ai-quickstart/package.json +41 -0
- package/templates/vercel-ai-quickstart/postcss.config.js +5 -0
- package/templates/vercel-ai-quickstart/tailwind.config.js +37 -0
- package/templates/vercel-ai-quickstart/tsconfig.json +33 -0
package/dist/commands/init.js
CHANGED
|
@@ -12,7 +12,8 @@ import ora from "ora";
|
|
|
12
12
|
import { spawn } from "child_process";
|
|
13
13
|
import { loadConfig, saveUserConfig } from "../utils/config.js";
|
|
14
14
|
import { isValidProjectName, isDirectoryEmpty, fetchLatestSDKMetadata, execCommand, commandExists, } from "../utils/shell.js";
|
|
15
|
-
import { setupNewConvex, setupExistingConvex, deployToConvex, } from "../utils/init/convex-setup.js";
|
|
15
|
+
import { setupNewConvex, setupExistingConvex, deployToConvex, ensureConvexAuth, sanitizeProjectName, } from "../utils/init/convex-setup.js";
|
|
16
|
+
import { installVercelAIQuickstart, startApp, stopApp, findAppPidFiles, isAppRunning, findProcessByPort, stopProcessByPort, } from "../utils/init/quickstart-setup.js";
|
|
16
17
|
import { getGraphConfig, setupGraphFiles, addGraphDependencies, createGraphExample, startGraphContainers, stopGraphContainers, } from "../utils/init/graph-setup.js";
|
|
17
18
|
import { copyTemplate, deployCortexBackend, createConvexJson, ensureGitignore, } from "../utils/init/file-operations.js";
|
|
18
19
|
import { createEnvFile, appendGraphEnvVars, } from "../utils/init/env-generator.js";
|
|
@@ -101,12 +102,24 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
101
102
|
console.log(pc.dim(" Use 'cortex start -d <name> -f' for foreground mode"));
|
|
102
103
|
process.exit(1);
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
+
// Collect enabled apps for summary
|
|
106
|
+
const apps = Object.entries(config.apps || {});
|
|
107
|
+
const enabledApps = apps.filter(([, app]) => app.enabled);
|
|
108
|
+
// Show summary header
|
|
109
|
+
console.log();
|
|
110
|
+
console.log(pc.bold(" ═══════════════════════════════════════════════════════════"));
|
|
111
|
+
console.log(pc.bold(" Cortex Start"));
|
|
112
|
+
console.log(pc.bold(" ═══════════════════════════════════════════════════════════"));
|
|
113
|
+
console.log();
|
|
114
|
+
// Deployments section
|
|
115
|
+
console.log(pc.bold(pc.cyan(` Deployments (${deploymentsToStart.length})`)));
|
|
116
|
+
console.log(pc.dim(" ───────────────────────────────────────────────────────────"));
|
|
105
117
|
// Start each deployment
|
|
106
118
|
for (const dep of deploymentsToStart) {
|
|
107
|
-
console.log(pc.
|
|
108
|
-
console.log(pc.dim(`
|
|
109
|
-
console.log(pc.dim(`
|
|
119
|
+
console.log(` ${pc.green("●")} ${pc.cyan(dep.name)}`);
|
|
120
|
+
console.log(pc.dim(` Project: ${dep.projectPath}`));
|
|
121
|
+
console.log(pc.dim(` URL: ${dep.url}`));
|
|
122
|
+
console.log();
|
|
110
123
|
// Start graph database if configured and not convex-only
|
|
111
124
|
if (!options.convexOnly) {
|
|
112
125
|
const dockerComposePath = path.join(dep.projectPath, "docker-compose.graph.yml");
|
|
@@ -185,28 +198,86 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
185
198
|
}
|
|
186
199
|
}
|
|
187
200
|
}
|
|
201
|
+
// Start enabled apps (unless convex-only or graph-only)
|
|
202
|
+
if (!options.convexOnly && !options.graphOnly && !options.foreground) {
|
|
203
|
+
if (enabledApps.length > 0) {
|
|
204
|
+
console.log(pc.bold(pc.cyan(` Apps (${enabledApps.length})`)));
|
|
205
|
+
console.log(pc.dim(" ───────────────────────────────────────────────────────────"));
|
|
206
|
+
for (const [name, app] of enabledApps) {
|
|
207
|
+
console.log(` ${pc.green("●")} ${pc.cyan(name)}`);
|
|
208
|
+
console.log(pc.dim(` Type: ${app.type}`));
|
|
209
|
+
console.log(pc.dim(` Path: ${path.join(app.projectPath, app.path)}`));
|
|
210
|
+
console.log(pc.dim(` Port: ${app.port || 3000}`));
|
|
211
|
+
console.log();
|
|
212
|
+
await startApp(name, app);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
188
216
|
// Show summary
|
|
189
217
|
if (!options.foreground) {
|
|
190
|
-
|
|
191
|
-
console.log(pc.
|
|
192
|
-
console.log(pc.
|
|
218
|
+
const totalStarted = deploymentsToStart.length + (enabledApps.length || 0);
|
|
219
|
+
console.log(pc.bold(" ═══════════════════════════════════════════════════════════"));
|
|
220
|
+
console.log(pc.green(` ✓ Started ${totalStarted} service(s)`));
|
|
221
|
+
console.log(pc.bold(" ═══════════════════════════════════════════════════════════"));
|
|
222
|
+
console.log();
|
|
223
|
+
console.log(pc.dim(" Use 'cortex stop' to stop all services"));
|
|
224
|
+
console.log(pc.dim(" Use 'cortex config list' to see deployment status"));
|
|
225
|
+
console.log();
|
|
193
226
|
}
|
|
194
227
|
});
|
|
195
228
|
// Stop command
|
|
196
229
|
program
|
|
197
230
|
.command("stop")
|
|
198
|
-
.description("Stop background services (Convex
|
|
231
|
+
.description("Stop background services (Convex, graph database, and apps)")
|
|
199
232
|
.option("-d, --deployment <name>", "Stop specific deployment only")
|
|
233
|
+
.option("-a, --app <name>", "Stop specific app only")
|
|
200
234
|
.option("--convex-only", "Only stop Convex server", false)
|
|
201
235
|
.option("--graph-only", "Only stop graph database", false)
|
|
236
|
+
.option("--apps-only", "Only stop template apps", false)
|
|
202
237
|
.action(async (options) => {
|
|
238
|
+
// Load fresh config to get current state
|
|
239
|
+
const config = await loadConfig();
|
|
203
240
|
let stoppedSomething = false;
|
|
204
241
|
let stoppedCount = 0;
|
|
205
|
-
|
|
242
|
+
let stoppedAppsCount = 0;
|
|
243
|
+
// If stopping specific app only
|
|
244
|
+
if (options.app) {
|
|
245
|
+
const appConfig = config.apps?.[options.app];
|
|
246
|
+
if (!appConfig) {
|
|
247
|
+
// Try to find app in current directory
|
|
248
|
+
const cwd = process.cwd();
|
|
249
|
+
const appPidFiles = await findAppPidFiles(cwd);
|
|
250
|
+
const matchingApp = appPidFiles.find((a) => a.name === options.app);
|
|
251
|
+
if (!matchingApp) {
|
|
252
|
+
console.error(pc.red(`\n Error: App "${options.app}" not found`));
|
|
253
|
+
console.log(pc.dim(" Run 'cortex config list' to see available apps\n"));
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
// Stop from current directory
|
|
257
|
+
const stopped = await stopApp(options.app, cwd);
|
|
258
|
+
if (stopped) {
|
|
259
|
+
console.log(pc.green(`\n ✓ App "${options.app}" stopped\n`));
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
console.log(pc.yellow(`\n App "${options.app}" was not running\n`));
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// Stop specific configured app
|
|
267
|
+
const stopped = await stopApp(options.app, appConfig.projectPath);
|
|
268
|
+
if (stopped) {
|
|
269
|
+
console.log(pc.green(`\n ✓ App "${options.app}" stopped\n`));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
console.log(pc.yellow(`\n App "${options.app}" was not running\n`));
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// Determine which deployments/project paths to stop
|
|
206
277
|
const deploymentsToStop = [];
|
|
207
278
|
if (options.deployment) {
|
|
208
279
|
// Stop specific deployment
|
|
209
|
-
const deployment =
|
|
280
|
+
const deployment = config.deployments?.[options.deployment];
|
|
210
281
|
if (!deployment) {
|
|
211
282
|
console.error(pc.red(`\n Error: Deployment "${options.deployment}" not found`));
|
|
212
283
|
console.log(pc.dim(" Run 'cortex config list' to see available deployments\n"));
|
|
@@ -224,36 +295,52 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
224
295
|
}
|
|
225
296
|
else {
|
|
226
297
|
// Stop all deployments that have running processes
|
|
227
|
-
const deploymentEntries = Object.entries(
|
|
228
|
-
|
|
298
|
+
const deploymentEntries = Object.entries(config.deployments || {});
|
|
299
|
+
// Collect project paths from both deployments and apps
|
|
300
|
+
const projectPaths = new Map(); // path -> name
|
|
301
|
+
if (deploymentEntries.length === 0 && !config.apps) {
|
|
229
302
|
// Fallback to current directory
|
|
230
303
|
const cwd = process.cwd();
|
|
231
|
-
|
|
232
|
-
name: "current directory",
|
|
233
|
-
projectPath: cwd,
|
|
234
|
-
});
|
|
304
|
+
projectPaths.set(cwd, "current directory");
|
|
235
305
|
}
|
|
236
306
|
else {
|
|
307
|
+
// Add deployment project paths
|
|
237
308
|
for (const [name, deployment] of deploymentEntries) {
|
|
238
309
|
if (deployment.projectPath &&
|
|
239
310
|
fs.existsSync(deployment.projectPath)) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
311
|
+
projectPaths.set(deployment.projectPath, name);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Add app project paths (may overlap with deployments)
|
|
315
|
+
if (config.apps) {
|
|
316
|
+
for (const [appName, app] of Object.entries(config.apps)) {
|
|
317
|
+
if (app.projectPath && fs.existsSync(app.projectPath)) {
|
|
318
|
+
// Use app name if no deployment for this path
|
|
319
|
+
if (!projectPaths.has(app.projectPath)) {
|
|
320
|
+
projectPaths.set(app.projectPath, `app:${appName}`);
|
|
321
|
+
}
|
|
250
322
|
}
|
|
251
323
|
}
|
|
252
324
|
}
|
|
253
325
|
}
|
|
326
|
+
// Check each project path for running services
|
|
327
|
+
for (const [projectPath, name] of projectPaths) {
|
|
328
|
+
// Check if anything is running for this deployment
|
|
329
|
+
const pidFile = path.join(projectPath, ".convex-dev.pid");
|
|
330
|
+
const dockerCompose = path.join(projectPath, "docker-compose.graph.yml");
|
|
331
|
+
const appPidFiles = await findAppPidFiles(projectPath);
|
|
332
|
+
const hasPidFile = fs.existsSync(pidFile);
|
|
333
|
+
const hasDockerCompose = fs.existsSync(dockerCompose);
|
|
334
|
+
const hasApps = appPidFiles.length > 0;
|
|
335
|
+
if (hasPidFile || hasDockerCompose || hasApps) {
|
|
336
|
+
deploymentsToStop.push({ name, projectPath });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
254
339
|
}
|
|
255
340
|
if (deploymentsToStop.length === 0) {
|
|
256
|
-
console.log(pc.yellow("\n No deployments with running services found
|
|
341
|
+
console.log(pc.yellow("\n No deployments with running services found"));
|
|
342
|
+
console.log(pc.dim(" Note: Services started without PID files cannot be auto-detected.\n" +
|
|
343
|
+
" Use 'ps aux | grep convex' or 'ps aux | grep next' to find running processes.\n"));
|
|
257
344
|
return;
|
|
258
345
|
}
|
|
259
346
|
console.log(pc.cyan(`\n Stopping ${deploymentsToStop.length} deployment(s)...\n`));
|
|
@@ -261,8 +348,9 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
261
348
|
console.log(pc.bold(` ${name}`));
|
|
262
349
|
console.log(pc.dim(` ${projectPath}`));
|
|
263
350
|
let deploymentStopped = false;
|
|
264
|
-
|
|
265
|
-
if
|
|
351
|
+
let convexStopped = false;
|
|
352
|
+
// Stop Convex if not graph-only and not apps-only
|
|
353
|
+
if (!options.graphOnly && !options.appsOnly) {
|
|
266
354
|
const pidFile = path.join(projectPath, ".convex-dev.pid");
|
|
267
355
|
try {
|
|
268
356
|
const pid = await fs.readFile(pidFile, "utf-8");
|
|
@@ -272,11 +360,13 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
272
360
|
console.log(pc.green(` ✓ Convex stopped (PID: ${pidNum})`));
|
|
273
361
|
stoppedSomething = true;
|
|
274
362
|
deploymentStopped = true;
|
|
363
|
+
convexStopped = true;
|
|
275
364
|
}
|
|
276
365
|
catch (e) {
|
|
277
366
|
const err = e;
|
|
278
367
|
if (err.code === "ESRCH") {
|
|
279
368
|
console.log(pc.yellow(" Convex was already stopped"));
|
|
369
|
+
convexStopped = true; // Consider it handled
|
|
280
370
|
}
|
|
281
371
|
else {
|
|
282
372
|
throw e;
|
|
@@ -290,13 +380,37 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
290
380
|
if (err.code !== "ENOENT") {
|
|
291
381
|
console.error(pc.red(" Error stopping Convex:"), e);
|
|
292
382
|
}
|
|
293
|
-
|
|
294
|
-
|
|
383
|
+
// No PID file - will try port-based detection below
|
|
384
|
+
}
|
|
385
|
+
// Fallback: try to stop by port if no PID file and deployment is local
|
|
386
|
+
if (!convexStopped) {
|
|
387
|
+
// Check if this is a local deployment (port 3210)
|
|
388
|
+
const deploymentConfig = Object.values(config.deployments || {}).find((d) => d.projectPath === projectPath);
|
|
389
|
+
const isLocal = deploymentConfig?.url?.includes("127.0.0.1:3210") ||
|
|
390
|
+
deploymentConfig?.url?.includes("localhost:3210");
|
|
391
|
+
if (isLocal) {
|
|
392
|
+
const convexPid = await findProcessByPort(3210);
|
|
393
|
+
if (convexPid) {
|
|
394
|
+
console.log(pc.yellow(` Found Convex on port 3210 (no PID file)`));
|
|
395
|
+
const stopped = await stopProcessByPort(3210);
|
|
396
|
+
if (stopped) {
|
|
397
|
+
console.log(pc.green(` ✓ Convex stopped (port 3210, PID: ${convexPid})`));
|
|
398
|
+
stoppedSomething = true;
|
|
399
|
+
deploymentStopped = true;
|
|
400
|
+
convexStopped = true;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
console.log(pc.dim(" No Convex process running"));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else if (!options.graphOnly && !options.appsOnly) {
|
|
408
|
+
console.log(pc.dim(" No Convex process running (PID file not found)"));
|
|
295
409
|
}
|
|
296
410
|
}
|
|
297
411
|
}
|
|
298
|
-
// Stop graph containers if not convex-only
|
|
299
|
-
if (!options.convexOnly) {
|
|
412
|
+
// Stop graph containers if not convex-only and not apps-only
|
|
413
|
+
if (!options.convexOnly && !options.appsOnly) {
|
|
300
414
|
const dockerComposePath = path.join(projectPath, "docker-compose.graph.yml");
|
|
301
415
|
if (fs.existsSync(dockerComposePath)) {
|
|
302
416
|
const stopped = await stopGraphContainers(projectPath);
|
|
@@ -309,17 +423,76 @@ export function registerLifecycleCommands(program, _config) {
|
|
|
309
423
|
console.log(pc.dim(" No graph container running"));
|
|
310
424
|
}
|
|
311
425
|
}
|
|
312
|
-
else if (!options.convexOnly) {
|
|
426
|
+
else if (!options.convexOnly && !options.appsOnly) {
|
|
313
427
|
console.log(pc.dim(" No graph database configured"));
|
|
314
428
|
}
|
|
315
429
|
}
|
|
430
|
+
// Stop apps if not convex-only and not graph-only
|
|
431
|
+
if (!options.convexOnly && !options.graphOnly) {
|
|
432
|
+
const appPidFiles = await findAppPidFiles(projectPath);
|
|
433
|
+
const stoppedAppNames = new Set();
|
|
434
|
+
// First, try to stop apps with PID files
|
|
435
|
+
if (appPidFiles.length > 0) {
|
|
436
|
+
for (const { name: appName } of appPidFiles) {
|
|
437
|
+
const appStatus = await isAppRunning(appName, projectPath);
|
|
438
|
+
if (appStatus.running) {
|
|
439
|
+
const stopped = await stopApp(appName, projectPath);
|
|
440
|
+
if (stopped) {
|
|
441
|
+
console.log(pc.green(` ✓ App "${appName}" stopped (PID: ${appStatus.pid})`));
|
|
442
|
+
stoppedSomething = true;
|
|
443
|
+
stoppedAppsCount++;
|
|
444
|
+
deploymentStopped = true;
|
|
445
|
+
stoppedAppNames.add(appName);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
// Clean up stale PID file (already handled by isAppRunning)
|
|
450
|
+
console.log(pc.dim(` App "${appName}" was not running`));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// Also check configured apps that might not have PID files
|
|
455
|
+
// (started before PID tracking was added)
|
|
456
|
+
if (config.apps) {
|
|
457
|
+
for (const [appName, appConfig] of Object.entries(config.apps)) {
|
|
458
|
+
// Skip if already stopped via PID file or not matching this project
|
|
459
|
+
if (stoppedAppNames.has(appName))
|
|
460
|
+
continue;
|
|
461
|
+
if (appConfig.projectPath !== projectPath)
|
|
462
|
+
continue;
|
|
463
|
+
const port = appConfig.port || 3000;
|
|
464
|
+
const pid = await findProcessByPort(port);
|
|
465
|
+
if (pid) {
|
|
466
|
+
console.log(pc.yellow(` Found app "${appName}" on port ${port} (no PID file)`));
|
|
467
|
+
const stopped = await stopProcessByPort(port);
|
|
468
|
+
if (stopped) {
|
|
469
|
+
console.log(pc.green(` ✓ App "${appName}" stopped (port ${port}, PID: ${pid})`));
|
|
470
|
+
stoppedSomething = true;
|
|
471
|
+
stoppedAppsCount++;
|
|
472
|
+
deploymentStopped = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (appPidFiles.length === 0 && !config.apps && options.appsOnly) {
|
|
478
|
+
console.log(pc.dim(" No apps configured"));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
316
481
|
if (deploymentStopped) {
|
|
317
482
|
stoppedCount++;
|
|
318
483
|
}
|
|
319
484
|
console.log();
|
|
320
485
|
}
|
|
486
|
+
// Summary
|
|
321
487
|
if (stoppedSomething) {
|
|
322
|
-
|
|
488
|
+
const parts = [];
|
|
489
|
+
if (stoppedCount > 0 && !options.appsOnly) {
|
|
490
|
+
parts.push(`${stoppedCount} deployment(s)`);
|
|
491
|
+
}
|
|
492
|
+
if (stoppedAppsCount > 0) {
|
|
493
|
+
parts.push(`${stoppedAppsCount} app(s)`);
|
|
494
|
+
}
|
|
495
|
+
console.log(pc.green(` ✓ Stopped ${parts.join(" and ")}\n`));
|
|
323
496
|
}
|
|
324
497
|
else {
|
|
325
498
|
console.log(pc.yellow(" No services were running\n"));
|
|
@@ -368,8 +541,8 @@ export async function runInitWizard(targetDir, options = {}) {
|
|
|
368
541
|
const projectInfo = await getProjectInfo(targetDir);
|
|
369
542
|
// Step 2: Installation type
|
|
370
543
|
const installationType = await getInstallationType(projectInfo.projectPath);
|
|
371
|
-
// Step 3: Convex setup
|
|
372
|
-
const convexConfig = await getConvexSetup(options);
|
|
544
|
+
// Step 3: Convex setup (pass project name for non-interactive setup)
|
|
545
|
+
const convexConfig = await getConvexSetup(options, projectInfo.projectName);
|
|
373
546
|
// Step 4: Graph database (optional)
|
|
374
547
|
let graphConfig = null;
|
|
375
548
|
if (!options.skipGraph) {
|
|
@@ -387,6 +560,8 @@ export async function runInitWizard(targetDir, options = {}) {
|
|
|
387
560
|
convexSetupType: convexConfig.type,
|
|
388
561
|
convexUrl: convexConfig.config.convexUrl,
|
|
389
562
|
deployKey: convexConfig.config.deployKey,
|
|
563
|
+
teamSlug: convexConfig.config.teamSlug,
|
|
564
|
+
sanitizedProjectName: convexConfig.config.sanitizedProjectName,
|
|
390
565
|
graphEnabled: graphConfig !== null,
|
|
391
566
|
graphType: graphConfig?.type || "skip",
|
|
392
567
|
graphUri: graphConfig?.uri,
|
|
@@ -399,26 +574,64 @@ export async function runInitWizard(targetDir, options = {}) {
|
|
|
399
574
|
if (!options.yes) {
|
|
400
575
|
await showConfirmation(config);
|
|
401
576
|
}
|
|
402
|
-
// Execute setup
|
|
403
|
-
await executeSetup(config);
|
|
404
|
-
// Ask
|
|
577
|
+
// Execute setup (returns SDK metadata for post-setup steps)
|
|
578
|
+
const sdkMetadata = await executeSetup(config);
|
|
579
|
+
// Ask about Vercel AI quickstart (optional demo app)
|
|
580
|
+
let installedQuickstart = false;
|
|
581
|
+
let quickstartAppConfig = null;
|
|
582
|
+
if (!options.yes) {
|
|
583
|
+
const { installQuickstart } = await prompts({
|
|
584
|
+
type: "confirm",
|
|
585
|
+
name: "installQuickstart",
|
|
586
|
+
message: "Install Vercel AI quickstart demo?",
|
|
587
|
+
initial: false,
|
|
588
|
+
});
|
|
589
|
+
if (installQuickstart) {
|
|
590
|
+
// Need the actual Convex URL from the config that was just set up
|
|
591
|
+
const convexUrl = config.convexUrl || "";
|
|
592
|
+
// Pass graph config if it was enabled during init
|
|
593
|
+
const graphConfig = config.graphEnabled
|
|
594
|
+
? {
|
|
595
|
+
enabled: true,
|
|
596
|
+
uri: config.graphUri,
|
|
597
|
+
username: config.graphUsername,
|
|
598
|
+
password: config.graphPassword,
|
|
599
|
+
}
|
|
600
|
+
: undefined;
|
|
601
|
+
quickstartAppConfig = await installVercelAIQuickstart(config.projectPath, sdkMetadata.sdkVersion, convexUrl, config.openaiApiKey, graphConfig);
|
|
602
|
+
// Save app to CLI config
|
|
603
|
+
const cliConfig = await loadConfig();
|
|
604
|
+
cliConfig.apps = cliConfig.apps || {};
|
|
605
|
+
cliConfig.apps["quickstart"] = quickstartAppConfig;
|
|
606
|
+
await saveUserConfig(cliConfig);
|
|
607
|
+
installedQuickstart = true;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Context-aware start prompt
|
|
405
611
|
let shouldStart = options.start;
|
|
406
612
|
if (!shouldStart && !options.yes) {
|
|
613
|
+
const message = installedQuickstart
|
|
614
|
+
? "Start Convex backend and quickstart app now?"
|
|
615
|
+
: "Start Convex development server now?";
|
|
407
616
|
const response = await prompts({
|
|
408
617
|
type: "confirm",
|
|
409
618
|
name: "startNow",
|
|
410
|
-
message
|
|
619
|
+
message,
|
|
411
620
|
initial: true,
|
|
412
621
|
});
|
|
413
622
|
shouldStart = response.startNow;
|
|
414
623
|
}
|
|
415
|
-
// Start
|
|
624
|
+
// Start services if requested
|
|
416
625
|
if (shouldStart) {
|
|
417
626
|
const isLocal = config.convexSetupType === "local";
|
|
418
627
|
await startConvexInBackground(config.projectPath, isLocal);
|
|
628
|
+
// Start quickstart app if installed
|
|
629
|
+
if (installedQuickstart && quickstartAppConfig) {
|
|
630
|
+
await startApp("quickstart", quickstartAppConfig);
|
|
631
|
+
}
|
|
419
632
|
// Show running status dashboard
|
|
420
633
|
console.log();
|
|
421
|
-
await showRunningStatus(config.projectPath, isLocal);
|
|
634
|
+
await showRunningStatus(config.projectPath, isLocal, installedQuickstart);
|
|
422
635
|
}
|
|
423
636
|
}
|
|
424
637
|
/**
|
|
@@ -477,49 +690,89 @@ async function getInstallationType(projectPath) {
|
|
|
477
690
|
}
|
|
478
691
|
/**
|
|
479
692
|
* Get Convex setup configuration
|
|
693
|
+
*
|
|
694
|
+
* Handles auth, gets team slug, and sanitizes project name for non-interactive setup.
|
|
480
695
|
*/
|
|
481
|
-
async function getConvexSetup(options) {
|
|
482
|
-
|
|
696
|
+
async function getConvexSetup(options, projectName) {
|
|
697
|
+
console.log(pc.cyan("\n Convex Database Setup"));
|
|
698
|
+
console.log(pc.dim(" Cortex uses Convex as its backend database\n"));
|
|
699
|
+
// Ensure user is logged in and get team slug
|
|
700
|
+
const authStatus = await ensureConvexAuth();
|
|
701
|
+
const teamSlug = authStatus.teamSlug;
|
|
702
|
+
const sanitizedName = sanitizeProjectName(projectName);
|
|
703
|
+
// Quick options via CLI flags
|
|
483
704
|
if (options.local) {
|
|
484
705
|
return {
|
|
485
706
|
type: "local",
|
|
486
|
-
config: {
|
|
707
|
+
config: {
|
|
708
|
+
convexUrl: "http://127.0.0.1:3210",
|
|
709
|
+
teamSlug,
|
|
710
|
+
sanitizedProjectName: sanitizedName,
|
|
711
|
+
},
|
|
487
712
|
};
|
|
488
713
|
}
|
|
489
714
|
if (options.cloud) {
|
|
490
715
|
return {
|
|
491
716
|
type: "new",
|
|
492
|
-
config: {
|
|
717
|
+
config: {
|
|
718
|
+
convexUrl: "",
|
|
719
|
+
teamSlug,
|
|
720
|
+
sanitizedProjectName: sanitizedName,
|
|
721
|
+
},
|
|
493
722
|
};
|
|
494
723
|
}
|
|
495
|
-
|
|
496
|
-
|
|
724
|
+
// Build menu choices with project name context
|
|
725
|
+
const choices = [
|
|
726
|
+
{
|
|
727
|
+
title: "Local development (fast, recommended)",
|
|
728
|
+
description: `Creates "${sanitizedName}" with local backend`,
|
|
729
|
+
value: "local",
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
title: "Create new cloud project",
|
|
733
|
+
description: teamSlug
|
|
734
|
+
? `Creates "${sanitizedName}" in team "${teamSlug}"`
|
|
735
|
+
: `Creates "${sanitizedName}" on Convex cloud`,
|
|
736
|
+
value: "new",
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
title: "Use existing Convex project",
|
|
740
|
+
description: "Connect to your existing deployment",
|
|
741
|
+
value: "existing",
|
|
742
|
+
},
|
|
743
|
+
];
|
|
497
744
|
const response = await prompts({
|
|
498
745
|
type: "select",
|
|
499
746
|
name: "setupType",
|
|
500
747
|
message: "How would you like to set up Convex?",
|
|
501
|
-
choices
|
|
502
|
-
{
|
|
503
|
-
title: "Create new Convex project",
|
|
504
|
-
description: "Full features including vector search",
|
|
505
|
-
value: "new",
|
|
506
|
-
},
|
|
507
|
-
{
|
|
508
|
-
title: "Use existing Convex deployment",
|
|
509
|
-
description: "Connect to your existing deployment",
|
|
510
|
-
value: "existing",
|
|
511
|
-
},
|
|
512
|
-
],
|
|
748
|
+
choices,
|
|
513
749
|
initial: 0,
|
|
514
750
|
});
|
|
515
751
|
if (!response.setupType) {
|
|
516
752
|
throw new Error("Convex setup is required");
|
|
517
753
|
}
|
|
754
|
+
// For existing projects, ask for the project name
|
|
755
|
+
let existingProjectName = sanitizedName;
|
|
756
|
+
if (response.setupType === "existing") {
|
|
757
|
+
const existingResponse = await prompts({
|
|
758
|
+
type: "text",
|
|
759
|
+
name: "projectName",
|
|
760
|
+
message: "Enter the name of your existing Convex project:",
|
|
761
|
+
initial: sanitizedName,
|
|
762
|
+
validate: (value) => (value ? true : "Project name is required"),
|
|
763
|
+
});
|
|
764
|
+
if (!existingResponse.projectName) {
|
|
765
|
+
throw new Error("Project name is required");
|
|
766
|
+
}
|
|
767
|
+
existingProjectName = existingResponse.projectName;
|
|
768
|
+
}
|
|
518
769
|
return {
|
|
519
770
|
type: response.setupType,
|
|
520
771
|
config: {
|
|
521
772
|
convexUrl: "",
|
|
522
773
|
deployKey: undefined,
|
|
774
|
+
teamSlug,
|
|
775
|
+
sanitizedProjectName: response.setupType === "existing" ? existingProjectName : sanitizedName,
|
|
523
776
|
},
|
|
524
777
|
};
|
|
525
778
|
}
|
|
@@ -601,6 +854,7 @@ async function showConfirmation(config) {
|
|
|
601
854
|
}
|
|
602
855
|
/**
|
|
603
856
|
* Execute the setup
|
|
857
|
+
* Returns SDK metadata for use in post-setup steps (like quickstart installation)
|
|
604
858
|
*/
|
|
605
859
|
async function executeSetup(config) {
|
|
606
860
|
console.log(pc.cyan("\n Setting up Cortex...\n"));
|
|
@@ -648,15 +902,46 @@ async function executeSetup(config) {
|
|
|
648
902
|
await createConvexJson(config.projectPath);
|
|
649
903
|
backendSpinner.succeed("Cortex backend files ready");
|
|
650
904
|
// Setup and deploy Convex in ONE step
|
|
905
|
+
// Pass teamSlug and projectName for non-interactive setup
|
|
906
|
+
// IMPORTANT: Always pass useLocalBackend when local mode is selected,
|
|
907
|
+
// even if teamSlug/projectName are missing (falls back to interactive)
|
|
651
908
|
let convexConfig;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
909
|
+
const isLocalMode = config.convexSetupType === "local";
|
|
910
|
+
const convexOptions = config.teamSlug && config.sanitizedProjectName
|
|
911
|
+
? {
|
|
912
|
+
teamSlug: config.teamSlug,
|
|
913
|
+
projectName: config.sanitizedProjectName,
|
|
914
|
+
useLocalBackend: isLocalMode,
|
|
915
|
+
}
|
|
916
|
+
: isLocalMode
|
|
917
|
+
? { useLocalBackend: true }
|
|
918
|
+
: undefined;
|
|
919
|
+
if (config.convexSetupType === "new" ||
|
|
920
|
+
config.convexSetupType === "local") {
|
|
921
|
+
// For new projects (cloud or local): create project and deploy
|
|
922
|
+
convexConfig = await setupNewConvex(config.projectPath, convexOptions);
|
|
655
923
|
}
|
|
656
924
|
else {
|
|
657
|
-
// For existing projects:
|
|
658
|
-
|
|
659
|
-
|
|
925
|
+
// For existing projects: connect and deploy
|
|
926
|
+
try {
|
|
927
|
+
convexConfig = await setupExistingConvex(config.projectPath, convexOptions
|
|
928
|
+
? {
|
|
929
|
+
teamSlug: convexOptions.teamSlug,
|
|
930
|
+
projectName: convexOptions.projectName,
|
|
931
|
+
}
|
|
932
|
+
: undefined);
|
|
933
|
+
// Only deploy if we used interactive mode (no options)
|
|
934
|
+
if (!convexOptions) {
|
|
935
|
+
await deployToConvex(config.projectPath, convexConfig, false);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
// The Convex CLI already printed the actual error (e.g., "Project not found",
|
|
940
|
+
// "Authentication failed", "Network error", etc.) to the console.
|
|
941
|
+
// Just provide a helpful hint without assuming the cause.
|
|
942
|
+
console.log(pc.dim("\n If the project wasn't found, check for typos or select 'Create new' instead."));
|
|
943
|
+
throw error;
|
|
944
|
+
}
|
|
660
945
|
}
|
|
661
946
|
// Update config with actual Convex details
|
|
662
947
|
config.convexUrl = convexConfig.convexUrl;
|
|
@@ -715,6 +1000,11 @@ async function executeSetup(config) {
|
|
|
715
1000
|
await saveDeploymentToConfig(config);
|
|
716
1001
|
// Success!
|
|
717
1002
|
showSuccessMessage(config);
|
|
1003
|
+
// Return SDK metadata for use in post-setup steps
|
|
1004
|
+
return {
|
|
1005
|
+
sdkVersion: sdkMetadata.sdkVersion,
|
|
1006
|
+
convexVersion: sdkMetadata.convexVersion,
|
|
1007
|
+
};
|
|
718
1008
|
}
|
|
719
1009
|
catch (error) {
|
|
720
1010
|
console.error(pc.red("\n Setup failed:"), error);
|
|
@@ -736,11 +1026,12 @@ async function saveDeploymentToConfig(config) {
|
|
|
736
1026
|
const deploymentName = config.projectName
|
|
737
1027
|
.toLowerCase()
|
|
738
1028
|
.replace(/[^a-z0-9-]/g, "-");
|
|
739
|
-
// Add the deployment
|
|
1029
|
+
// Add the deployment (enabled by default when created by init)
|
|
740
1030
|
userConfig.deployments[deploymentName] = {
|
|
741
1031
|
url: config.convexUrl,
|
|
742
1032
|
key: config.deployKey,
|
|
743
1033
|
projectPath: config.projectPath,
|
|
1034
|
+
enabled: true,
|
|
744
1035
|
};
|
|
745
1036
|
// Set as default if no default exists
|
|
746
1037
|
if (!userConfig.default) {
|
|
@@ -861,7 +1152,7 @@ async function startConvexInBackground(projectPath, isLocal, deploymentUrl, depl
|
|
|
861
1152
|
/**
|
|
862
1153
|
* Show running services status dashboard
|
|
863
1154
|
*/
|
|
864
|
-
async function showRunningStatus(projectPath, isLocal) {
|
|
1155
|
+
async function showRunningStatus(projectPath, isLocal, hasQuickstart = false) {
|
|
865
1156
|
const width = 56;
|
|
866
1157
|
const line = "═".repeat(width);
|
|
867
1158
|
const thinLine = "─".repeat(width);
|
|
@@ -933,6 +1224,15 @@ async function showRunningStatus(projectPath, isLocal) {
|
|
|
933
1224
|
console.log(` ${pc.dim("○")} Not configured`);
|
|
934
1225
|
}
|
|
935
1226
|
console.log();
|
|
1227
|
+
// Quickstart App Status (if installed)
|
|
1228
|
+
if (hasQuickstart) {
|
|
1229
|
+
console.log(pc.bold(pc.white(" Vercel AI Quickstart")));
|
|
1230
|
+
console.log(pc.dim(" " + thinLine));
|
|
1231
|
+
console.log(` ${pc.green("●")} Running`);
|
|
1232
|
+
console.log(pc.dim(" URL: http://localhost:3000"));
|
|
1233
|
+
console.log(pc.dim(" Interactive chat demo with memory visualization"));
|
|
1234
|
+
console.log();
|
|
1235
|
+
}
|
|
936
1236
|
// Quick Actions
|
|
937
1237
|
console.log(pc.bold(pc.white(" Quick Actions")));
|
|
938
1238
|
console.log(pc.dim(" " + thinLine));
|
|
@@ -941,6 +1241,9 @@ async function showRunningStatus(projectPath, isLocal) {
|
|
|
941
1241
|
pc.dim(" # Foreground mode (see logs)"));
|
|
942
1242
|
console.log(pc.dim(" cortex stop") +
|
|
943
1243
|
pc.dim(" # Stop background services"));
|
|
1244
|
+
if (hasQuickstart) {
|
|
1245
|
+
console.log(pc.dim(" npm run quickstart") + pc.dim(" # Start quickstart demo"));
|
|
1246
|
+
}
|
|
944
1247
|
console.log(pc.dim(" npm start") + pc.dim(" # Run your AI agent"));
|
|
945
1248
|
console.log();
|
|
946
1249
|
// Log file hint
|