@cortexmemory/cli 0.1.1 → 0.22.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/README.md +8 -0
- package/dist/commands/conversations.d.ts +1 -1
- package/dist/commands/conversations.d.ts.map +1 -1
- package/dist/commands/conversations.js +57 -27
- package/dist/commands/conversations.js.map +1 -1
- package/dist/commands/convex.d.ts +1 -1
- package/dist/commands/convex.d.ts.map +1 -1
- package/dist/commands/convex.js +237 -64
- package/dist/commands/convex.js.map +1 -1
- package/dist/commands/db.d.ts +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +59 -109
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts +8 -8
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +734 -513
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/facts.d.ts +1 -1
- package/dist/commands/facts.d.ts.map +1 -1
- package/dist/commands/facts.js +50 -21
- package/dist/commands/facts.js.map +1 -1
- package/dist/commands/init.d.ts +28 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +895 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/memory.d.ts +1 -1
- package/dist/commands/memory.d.ts.map +1 -1
- package/dist/commands/memory.js +64 -27
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/setup.d.ts +4 -5
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +428 -250
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/spaces.d.ts +1 -1
- package/dist/commands/spaces.d.ts.map +1 -1
- package/dist/commands/spaces.js +100 -43
- package/dist/commands/spaces.js.map +1 -1
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +314 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/users.d.ts +1 -1
- package/dist/commands/users.d.ts.map +1 -1
- package/dist/commands/users.js +80 -42
- package/dist/commands/users.js.map +1 -1
- package/dist/index.js +42 -14
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/__tests__/client.test.d.ts +5 -0
- package/dist/utils/__tests__/client.test.d.ts.map +1 -0
- package/dist/utils/__tests__/client.test.js +88 -0
- package/dist/utils/__tests__/client.test.js.map +1 -0
- package/dist/utils/__tests__/env-file.test.d.ts +7 -0
- package/dist/utils/__tests__/env-file.test.d.ts.map +1 -0
- package/dist/utils/__tests__/env-file.test.js +196 -0
- package/dist/utils/__tests__/env-file.test.js.map +1 -0
- package/dist/utils/__tests__/shell.test.d.ts +7 -0
- package/dist/utils/__tests__/shell.test.d.ts.map +1 -0
- package/dist/utils/__tests__/shell.test.js +89 -0
- package/dist/utils/__tests__/shell.test.js.map +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -39
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/deployment-selector.d.ts +50 -0
- package/dist/utils/deployment-selector.d.ts.map +1 -0
- package/dist/utils/deployment-selector.js +129 -0
- package/dist/utils/deployment-selector.js.map +1 -0
- package/dist/utils/init/convex-setup.d.ts +30 -0
- package/dist/utils/init/convex-setup.d.ts.map +1 -0
- package/dist/utils/init/convex-setup.js +225 -0
- package/dist/utils/init/convex-setup.js.map +1 -0
- package/dist/utils/init/env-generator.d.ts +32 -0
- package/dist/utils/init/env-generator.d.ts.map +1 -0
- package/dist/utils/init/env-generator.js +210 -0
- package/dist/utils/init/env-generator.js.map +1 -0
- package/dist/utils/init/file-operations.d.ts +22 -0
- package/dist/utils/init/file-operations.d.ts.map +1 -0
- package/dist/utils/init/file-operations.js +211 -0
- package/dist/utils/init/file-operations.js.map +1 -0
- package/dist/utils/init/graph-setup.d.ts +35 -0
- package/dist/utils/init/graph-setup.d.ts.map +1 -0
- package/dist/utils/init/graph-setup.js +413 -0
- package/dist/utils/init/graph-setup.js.map +1 -0
- package/dist/utils/init/index.d.ts +11 -0
- package/dist/utils/init/index.d.ts.map +1 -0
- package/dist/utils/init/index.js +11 -0
- package/dist/utils/init/index.js.map +1 -0
- package/dist/utils/init/types.d.ts +73 -0
- package/dist/utils/init/types.d.ts.map +1 -0
- package/dist/utils/init/types.js +5 -0
- package/dist/utils/init/types.js.map +1 -0
- package/dist/utils/shell.d.ts +60 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +188 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +25 -20
- package/templates/basic/README.md +105 -0
- package/templates/basic/dev-runner.mjs +215 -0
- package/templates/basic/package-lock.json +1263 -0
- package/templates/basic/package.json +22 -0
- package/templates/basic/src/index.ts +85 -0
- package/templates/basic/tsconfig.json +17 -0
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Command
|
|
3
|
+
*
|
|
4
|
+
* Interactive wizard for setting up a new Cortex Memory project.
|
|
5
|
+
* Replaces `npx create-cortex-memories` with a more integrated CLI experience.
|
|
6
|
+
*/
|
|
7
|
+
import prompts from "prompts";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import pc from "picocolors";
|
|
11
|
+
import ora from "ora";
|
|
12
|
+
import { spawn } from "child_process";
|
|
13
|
+
import { loadConfig, saveUserConfig } from "../utils/config.js";
|
|
14
|
+
import { isValidProjectName, isDirectoryEmpty, fetchLatestSDKMetadata, execCommand, commandExists, } from "../utils/shell.js";
|
|
15
|
+
import { setupNewConvex, setupExistingConvex, deployToConvex, } from "../utils/init/convex-setup.js";
|
|
16
|
+
import { getGraphConfig, setupGraphFiles, addGraphDependencies, createGraphExample, startGraphContainers, stopGraphContainers, } from "../utils/init/graph-setup.js";
|
|
17
|
+
import { copyTemplate, deployCortexBackend, createConvexJson, ensureGitignore, } from "../utils/init/file-operations.js";
|
|
18
|
+
import { createEnvFile, appendGraphEnvVars } from "../utils/init/env-generator.js";
|
|
19
|
+
/**
|
|
20
|
+
* Register start and stop commands (lifecycle management)
|
|
21
|
+
*/
|
|
22
|
+
export function registerLifecycleCommands(program, _config) {
|
|
23
|
+
// Quick start command
|
|
24
|
+
program
|
|
25
|
+
.command("start")
|
|
26
|
+
.description("Start development services (all enabled deployments)")
|
|
27
|
+
.option("-d, --deployment <name>", "Start a specific deployment only")
|
|
28
|
+
.option("-l, --local", "Use Convex local beta mode (starts a new local backend)", false)
|
|
29
|
+
.option("-f, --foreground", "Run in foreground (only works with single deployment)", false)
|
|
30
|
+
.option("--convex-only", "Only start Convex servers", false)
|
|
31
|
+
.option("--graph-only", "Only start graph databases", false)
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
const config = await loadConfig();
|
|
34
|
+
const deploymentsToStart = [];
|
|
35
|
+
if (options.deployment) {
|
|
36
|
+
// Start specific deployment
|
|
37
|
+
const deployment = config.deployments[options.deployment];
|
|
38
|
+
if (!deployment) {
|
|
39
|
+
console.error(pc.red(`\n Deployment "${options.deployment}" not found`));
|
|
40
|
+
const names = Object.keys(config.deployments);
|
|
41
|
+
if (names.length > 0) {
|
|
42
|
+
console.log(pc.dim(` Available: ${names.join(", ")}`));
|
|
43
|
+
}
|
|
44
|
+
console.log(pc.dim(" Run 'cortex config list' to see all deployments"));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const projectPath = deployment.projectPath || process.cwd();
|
|
48
|
+
if (deployment.projectPath && !fs.existsSync(projectPath)) {
|
|
49
|
+
console.error(pc.red(`\n Project path not found: ${projectPath}`));
|
|
50
|
+
console.log(pc.dim(" Run 'cortex config set-path <deployment> <path>' to update"));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
deploymentsToStart.push({
|
|
54
|
+
name: options.deployment,
|
|
55
|
+
url: deployment.url,
|
|
56
|
+
key: deployment.key,
|
|
57
|
+
projectPath,
|
|
58
|
+
// Only use --local flag if explicitly requested
|
|
59
|
+
// Self-hosted backends (localhost URLs) should NOT use --local
|
|
60
|
+
// as that starts a new local server instead of connecting to existing one
|
|
61
|
+
isLocal: options.local,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Start all enabled deployments
|
|
66
|
+
for (const [name, deployment] of Object.entries(config.deployments)) {
|
|
67
|
+
const isDefault = name === config.default;
|
|
68
|
+
const isEnabled = deployment.enabled === true || (deployment.enabled === undefined && isDefault);
|
|
69
|
+
if (!isEnabled)
|
|
70
|
+
continue;
|
|
71
|
+
if (!deployment.projectPath) {
|
|
72
|
+
console.log(pc.yellow(` Skipping "${name}" - no projectPath configured`));
|
|
73
|
+
console.log(pc.dim(` Run 'cortex config set-path ${name} <path>' to configure\n`));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!fs.existsSync(deployment.projectPath)) {
|
|
77
|
+
console.log(pc.yellow(` Skipping "${name}" - projectPath not found: ${deployment.projectPath}`));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
deploymentsToStart.push({
|
|
81
|
+
name,
|
|
82
|
+
url: deployment.url,
|
|
83
|
+
key: deployment.key,
|
|
84
|
+
projectPath: deployment.projectPath,
|
|
85
|
+
// Only use --local flag if explicitly requested
|
|
86
|
+
// Self-hosted backends (localhost URLs) should NOT use --local
|
|
87
|
+
isLocal: options.local,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (deploymentsToStart.length === 0) {
|
|
92
|
+
console.log(pc.yellow("\n No deployments to start"));
|
|
93
|
+
console.log(pc.dim(" Run 'cortex config enable <name>' to enable a deployment"));
|
|
94
|
+
console.log(pc.dim(" Or use 'cortex start -d <name>' to start a specific one"));
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
// Foreground mode only works with single deployment
|
|
98
|
+
if (options.foreground && deploymentsToStart.length > 1) {
|
|
99
|
+
console.error(pc.red("\n Foreground mode only works with a single deployment"));
|
|
100
|
+
console.log(pc.dim(" Use 'cortex start -d <name> -f' for foreground mode"));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
console.log(pc.cyan(`\n Starting ${deploymentsToStart.length} deployment(s)...\n`));
|
|
104
|
+
// Start each deployment
|
|
105
|
+
for (const dep of deploymentsToStart) {
|
|
106
|
+
console.log(pc.bold(` ${dep.name}`));
|
|
107
|
+
console.log(pc.dim(` Project: ${dep.projectPath}`));
|
|
108
|
+
console.log(pc.dim(` URL: ${dep.url}\n`));
|
|
109
|
+
// Start graph database if configured and not convex-only
|
|
110
|
+
if (!options.convexOnly) {
|
|
111
|
+
const dockerComposePath = path.join(dep.projectPath, "docker-compose.graph.yml");
|
|
112
|
+
if (fs.existsSync(dockerComposePath)) {
|
|
113
|
+
const composeContent = await fs.readFile(dockerComposePath, "utf-8");
|
|
114
|
+
const graphType = composeContent.includes("memgraph")
|
|
115
|
+
? "memgraph"
|
|
116
|
+
: "neo4j";
|
|
117
|
+
await startGraphContainers(dep.projectPath, graphType);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Start Convex if not graph-only
|
|
121
|
+
if (!options.graphOnly) {
|
|
122
|
+
const hasConvex = await commandExists("convex");
|
|
123
|
+
const env = { ...process.env };
|
|
124
|
+
env.CONVEX_URL = dep.url;
|
|
125
|
+
if (dep.key)
|
|
126
|
+
env.CONVEX_DEPLOY_KEY = dep.key;
|
|
127
|
+
// For cloud deployments with a deploy key, run `convex deploy` first
|
|
128
|
+
// This ensures functions are deployed to production before starting dev mode
|
|
129
|
+
if (dep.key && !dep.isLocal) {
|
|
130
|
+
const deploySpinner = ora("Deploying functions to production...").start();
|
|
131
|
+
try {
|
|
132
|
+
const deployCmd = hasConvex ? "convex" : "npx";
|
|
133
|
+
const deployArgs = hasConvex
|
|
134
|
+
? ["deploy", "--cmd", "echo deployed"]
|
|
135
|
+
: ["convex", "deploy", "--cmd", "echo deployed"];
|
|
136
|
+
await new Promise((resolve, reject) => {
|
|
137
|
+
const deployChild = spawn(deployCmd, deployArgs, {
|
|
138
|
+
cwd: dep.projectPath,
|
|
139
|
+
stdio: "pipe",
|
|
140
|
+
env,
|
|
141
|
+
});
|
|
142
|
+
let stderr = "";
|
|
143
|
+
deployChild.stderr?.on("data", (data) => { stderr += data.toString(); });
|
|
144
|
+
deployChild.on("close", (code) => {
|
|
145
|
+
if (code === 0) {
|
|
146
|
+
resolve();
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
reject(new Error(`Deploy failed with code ${code}: ${stderr}`));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
deployChild.on("error", reject);
|
|
153
|
+
});
|
|
154
|
+
deploySpinner.succeed("Functions deployed to production");
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
deploySpinner.fail("Failed to deploy functions");
|
|
158
|
+
console.error(pc.dim(` ${error}`));
|
|
159
|
+
console.log(pc.yellow(" Continuing with dev mode anyway..."));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (options.foreground) {
|
|
163
|
+
// Foreground mode - blocking (single deployment only)
|
|
164
|
+
console.log(pc.cyan(" Starting Convex development server...\n"));
|
|
165
|
+
console.log(pc.dim(" Press Ctrl+C to stop\n"));
|
|
166
|
+
const command = hasConvex ? "convex" : "npx";
|
|
167
|
+
const args = hasConvex ? ["dev"] : ["convex", "dev"];
|
|
168
|
+
if (dep.isLocal)
|
|
169
|
+
args.push("--local");
|
|
170
|
+
const child = spawn(command, args, {
|
|
171
|
+
cwd: dep.projectPath,
|
|
172
|
+
stdio: "inherit",
|
|
173
|
+
env,
|
|
174
|
+
});
|
|
175
|
+
await new Promise((resolve) => {
|
|
176
|
+
child.on("close", () => resolve());
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Background mode
|
|
181
|
+
await startConvexInBackground(dep.projectPath, dep.isLocal, dep.url, dep.key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Show summary
|
|
186
|
+
if (!options.foreground) {
|
|
187
|
+
console.log(pc.green("\n ✓ All deployments started\n"));
|
|
188
|
+
console.log(pc.dim(" Use 'cortex stop' to stop all services"));
|
|
189
|
+
console.log(pc.dim(" Use 'cortex config list' to see deployment status"));
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
// Stop command
|
|
193
|
+
program
|
|
194
|
+
.command("stop")
|
|
195
|
+
.description("Stop background services (Convex and graph database)")
|
|
196
|
+
.option("-d, --deployment <name>", "Stop specific deployment only")
|
|
197
|
+
.option("--convex-only", "Only stop Convex server", false)
|
|
198
|
+
.option("--graph-only", "Only stop graph database", false)
|
|
199
|
+
.action(async (options) => {
|
|
200
|
+
let stoppedSomething = false;
|
|
201
|
+
let stoppedCount = 0;
|
|
202
|
+
// Determine which deployments to stop
|
|
203
|
+
const deploymentsToStop = [];
|
|
204
|
+
if (options.deployment) {
|
|
205
|
+
// Stop specific deployment
|
|
206
|
+
const deployment = _config.deployments?.[options.deployment];
|
|
207
|
+
if (!deployment) {
|
|
208
|
+
console.error(pc.red(`\n Error: Deployment "${options.deployment}" not found`));
|
|
209
|
+
console.log(pc.dim(" Run 'cortex config list' to see available deployments\n"));
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
if (!deployment.projectPath) {
|
|
213
|
+
console.error(pc.red(`\n Error: Deployment "${options.deployment}" has no project path`));
|
|
214
|
+
console.log(pc.dim(" This deployment may be remote-only\n"));
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
deploymentsToStop.push({ name: options.deployment, projectPath: deployment.projectPath });
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
// Stop all deployments that have running processes
|
|
221
|
+
const deploymentEntries = Object.entries(_config.deployments || {});
|
|
222
|
+
if (deploymentEntries.length === 0) {
|
|
223
|
+
// Fallback to current directory
|
|
224
|
+
const cwd = process.cwd();
|
|
225
|
+
deploymentsToStop.push({ name: "current directory", projectPath: cwd });
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
for (const [name, deployment] of deploymentEntries) {
|
|
229
|
+
if (deployment.projectPath && fs.existsSync(deployment.projectPath)) {
|
|
230
|
+
// Check if anything is running for this deployment
|
|
231
|
+
const pidFile = path.join(deployment.projectPath, ".convex-dev.pid");
|
|
232
|
+
const dockerCompose = path.join(deployment.projectPath, "docker-compose.graph.yml");
|
|
233
|
+
const hasPidFile = fs.existsSync(pidFile);
|
|
234
|
+
const hasDockerCompose = fs.existsSync(dockerCompose);
|
|
235
|
+
if (hasPidFile || hasDockerCompose) {
|
|
236
|
+
deploymentsToStop.push({ name, projectPath: deployment.projectPath });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (deploymentsToStop.length === 0) {
|
|
243
|
+
console.log(pc.yellow("\n No deployments with running services found\n"));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
console.log(pc.cyan(`\n Stopping ${deploymentsToStop.length} deployment(s)...\n`));
|
|
247
|
+
for (const { name, projectPath } of deploymentsToStop) {
|
|
248
|
+
console.log(pc.bold(` ${name}`));
|
|
249
|
+
console.log(pc.dim(` ${projectPath}`));
|
|
250
|
+
let deploymentStopped = false;
|
|
251
|
+
// Stop Convex if not graph-only
|
|
252
|
+
if (!options.graphOnly) {
|
|
253
|
+
const pidFile = path.join(projectPath, ".convex-dev.pid");
|
|
254
|
+
try {
|
|
255
|
+
const pid = await fs.readFile(pidFile, "utf-8");
|
|
256
|
+
const pidNum = parseInt(pid.trim());
|
|
257
|
+
try {
|
|
258
|
+
process.kill(pidNum, "SIGTERM");
|
|
259
|
+
console.log(pc.green(` ✓ Convex stopped (PID: ${pidNum})`));
|
|
260
|
+
stoppedSomething = true;
|
|
261
|
+
deploymentStopped = true;
|
|
262
|
+
}
|
|
263
|
+
catch (e) {
|
|
264
|
+
const err = e;
|
|
265
|
+
if (err.code === "ESRCH") {
|
|
266
|
+
console.log(pc.yellow(" Convex was already stopped"));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
throw e;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Clean up pid file
|
|
273
|
+
await fs.remove(pidFile);
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
const err = e;
|
|
277
|
+
if (err.code !== "ENOENT") {
|
|
278
|
+
console.error(pc.red(" Error stopping Convex:"), e);
|
|
279
|
+
}
|
|
280
|
+
else if (!options.graphOnly) {
|
|
281
|
+
console.log(pc.dim(" No Convex process running"));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Stop graph containers if not convex-only
|
|
286
|
+
if (!options.convexOnly) {
|
|
287
|
+
const dockerComposePath = path.join(projectPath, "docker-compose.graph.yml");
|
|
288
|
+
if (fs.existsSync(dockerComposePath)) {
|
|
289
|
+
const stopped = await stopGraphContainers(projectPath);
|
|
290
|
+
if (stopped) {
|
|
291
|
+
console.log(pc.green(" ✓ Graph database stopped"));
|
|
292
|
+
stoppedSomething = true;
|
|
293
|
+
deploymentStopped = true;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
console.log(pc.dim(" No graph container running"));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (!options.convexOnly) {
|
|
300
|
+
console.log(pc.dim(" No graph database configured"));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (deploymentStopped) {
|
|
304
|
+
stoppedCount++;
|
|
305
|
+
}
|
|
306
|
+
console.log();
|
|
307
|
+
}
|
|
308
|
+
if (stoppedSomething) {
|
|
309
|
+
console.log(pc.green(` ✓ Stopped ${stoppedCount} deployment(s)\n`));
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
console.log(pc.yellow(" No services were running\n"));
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Register init command (project initialization)
|
|
318
|
+
*/
|
|
319
|
+
export function registerInitCommand(program, _config) {
|
|
320
|
+
// Init command
|
|
321
|
+
program
|
|
322
|
+
.command("init [directory]")
|
|
323
|
+
.description("Initialize a new Cortex Memory project")
|
|
324
|
+
.option("--local", "Quick setup with local Convex only", false)
|
|
325
|
+
.option("--cloud", "Quick setup with cloud Convex only", false)
|
|
326
|
+
.option("--skip-graph", "Skip graph database setup", false)
|
|
327
|
+
.option("-t, --template <name>", "Template to use (default: basic)", "basic")
|
|
328
|
+
.option("-y, --yes", "Skip confirmation prompts", false)
|
|
329
|
+
.option("--start", "Start Convex dev server after setup", false)
|
|
330
|
+
.action(async (targetDir, options) => {
|
|
331
|
+
try {
|
|
332
|
+
await runInitWizard(targetDir, options);
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
if (error instanceof Error) {
|
|
336
|
+
if (error.message === "Setup cancelled") {
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
console.error(pc.red("\n Error:"), error.message);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
console.error(pc.red("\n An unexpected error occurred:"), error);
|
|
343
|
+
}
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Run the interactive init wizard
|
|
350
|
+
*/
|
|
351
|
+
export async function runInitWizard(targetDir, options = {}) {
|
|
352
|
+
console.log(pc.bold(pc.cyan("\n Cortex Memory Project Setup\n")));
|
|
353
|
+
console.log(pc.dim(" Setting up AI agent with persistent memory\n"));
|
|
354
|
+
// Step 1: Project name and location
|
|
355
|
+
const projectInfo = await getProjectInfo(targetDir);
|
|
356
|
+
// Step 2: Installation type
|
|
357
|
+
const installationType = await getInstallationType(projectInfo.projectPath);
|
|
358
|
+
// Step 3: Convex setup
|
|
359
|
+
const convexConfig = await getConvexSetup(options);
|
|
360
|
+
// Step 4: Graph database (optional)
|
|
361
|
+
let graphConfig = null;
|
|
362
|
+
if (!options.skipGraph) {
|
|
363
|
+
graphConfig = await getGraphConfig();
|
|
364
|
+
}
|
|
365
|
+
// Step 5: CLI scripts option
|
|
366
|
+
const installCLI = await getCliInstallOption();
|
|
367
|
+
// Build wizard configuration
|
|
368
|
+
const config = {
|
|
369
|
+
projectName: projectInfo.projectName,
|
|
370
|
+
projectPath: projectInfo.projectPath,
|
|
371
|
+
installationType,
|
|
372
|
+
convexSetupType: convexConfig.type,
|
|
373
|
+
convexUrl: convexConfig.config.convexUrl,
|
|
374
|
+
deployKey: convexConfig.config.deployKey,
|
|
375
|
+
graphEnabled: graphConfig !== null,
|
|
376
|
+
graphType: graphConfig?.type || "skip",
|
|
377
|
+
graphUri: graphConfig?.uri,
|
|
378
|
+
graphUsername: graphConfig?.username,
|
|
379
|
+
graphPassword: graphConfig?.password,
|
|
380
|
+
installCLI,
|
|
381
|
+
};
|
|
382
|
+
// Show confirmation
|
|
383
|
+
if (!options.yes) {
|
|
384
|
+
await showConfirmation(config);
|
|
385
|
+
}
|
|
386
|
+
// Execute setup
|
|
387
|
+
await executeSetup(config);
|
|
388
|
+
// Ask to start Convex if not already specified
|
|
389
|
+
let shouldStart = options.start;
|
|
390
|
+
if (!shouldStart && !options.yes) {
|
|
391
|
+
const response = await prompts({
|
|
392
|
+
type: "confirm",
|
|
393
|
+
name: "startNow",
|
|
394
|
+
message: "Start Convex development server now?",
|
|
395
|
+
initial: true,
|
|
396
|
+
});
|
|
397
|
+
shouldStart = response.startNow;
|
|
398
|
+
}
|
|
399
|
+
// Start Convex if requested
|
|
400
|
+
if (shouldStart) {
|
|
401
|
+
const isLocal = config.convexSetupType === "local";
|
|
402
|
+
await startConvexInBackground(config.projectPath, isLocal);
|
|
403
|
+
// Show running status dashboard
|
|
404
|
+
console.log();
|
|
405
|
+
await showRunningStatus(config.projectPath, isLocal);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Get project name and location
|
|
410
|
+
*/
|
|
411
|
+
async function getProjectInfo(targetDir) {
|
|
412
|
+
if (targetDir) {
|
|
413
|
+
const projectPath = path.resolve(targetDir);
|
|
414
|
+
const projectName = targetDir === "."
|
|
415
|
+
? path.basename(process.cwd())
|
|
416
|
+
: path.basename(projectPath);
|
|
417
|
+
return { projectName, projectPath };
|
|
418
|
+
}
|
|
419
|
+
const response = await prompts({
|
|
420
|
+
type: "text",
|
|
421
|
+
name: "projectName",
|
|
422
|
+
message: "Project name:",
|
|
423
|
+
initial: "my-cortex-agent",
|
|
424
|
+
validate: (value) => {
|
|
425
|
+
if (!value)
|
|
426
|
+
return "Project name is required";
|
|
427
|
+
if (!isValidProjectName(value)) {
|
|
428
|
+
return "Project name must contain only lowercase letters, numbers, hyphens, and underscores";
|
|
429
|
+
}
|
|
430
|
+
return true;
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
if (!response.projectName) {
|
|
434
|
+
throw new Error("Project name is required");
|
|
435
|
+
}
|
|
436
|
+
const projectPath = path.resolve(response.projectName);
|
|
437
|
+
return {
|
|
438
|
+
projectName: response.projectName,
|
|
439
|
+
projectPath,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Get installation type
|
|
444
|
+
*/
|
|
445
|
+
async function getInstallationType(projectPath) {
|
|
446
|
+
const exists = fs.existsSync(projectPath);
|
|
447
|
+
const isEmpty = isDirectoryEmpty(projectPath);
|
|
448
|
+
if (exists && !isEmpty) {
|
|
449
|
+
const response = await prompts({
|
|
450
|
+
type: "confirm",
|
|
451
|
+
name: "addToExisting",
|
|
452
|
+
message: `Directory ${path.basename(projectPath)} already exists. Add Cortex to existing project?`,
|
|
453
|
+
initial: true,
|
|
454
|
+
});
|
|
455
|
+
if (!response.addToExisting) {
|
|
456
|
+
throw new Error("Setup cancelled");
|
|
457
|
+
}
|
|
458
|
+
return "existing";
|
|
459
|
+
}
|
|
460
|
+
return "new";
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get Convex setup configuration
|
|
464
|
+
*/
|
|
465
|
+
async function getConvexSetup(options) {
|
|
466
|
+
// Quick options
|
|
467
|
+
if (options.local) {
|
|
468
|
+
return {
|
|
469
|
+
type: "local",
|
|
470
|
+
config: { convexUrl: "http://127.0.0.1:3210" },
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (options.cloud) {
|
|
474
|
+
return {
|
|
475
|
+
type: "new",
|
|
476
|
+
config: { convexUrl: "" },
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
console.log(pc.cyan("\n Convex Database Setup"));
|
|
480
|
+
console.log(pc.dim(" Cortex uses Convex as its backend database\n"));
|
|
481
|
+
const response = await prompts({
|
|
482
|
+
type: "select",
|
|
483
|
+
name: "setupType",
|
|
484
|
+
message: "How would you like to set up Convex?",
|
|
485
|
+
choices: [
|
|
486
|
+
{
|
|
487
|
+
title: "Create new Convex project",
|
|
488
|
+
description: "Full features including vector search",
|
|
489
|
+
value: "new",
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
title: "Use existing Convex deployment",
|
|
493
|
+
description: "Connect to your existing deployment",
|
|
494
|
+
value: "existing",
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
initial: 0,
|
|
498
|
+
});
|
|
499
|
+
if (!response.setupType) {
|
|
500
|
+
throw new Error("Convex setup is required");
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
type: response.setupType,
|
|
504
|
+
config: {
|
|
505
|
+
convexUrl: "",
|
|
506
|
+
deployKey: undefined,
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Get CLI installation option
|
|
512
|
+
*/
|
|
513
|
+
async function getCliInstallOption() {
|
|
514
|
+
console.log(pc.cyan("\n CLI Scripts (Optional)"));
|
|
515
|
+
console.log(pc.dim(" Add npm scripts for common Cortex commands\n"));
|
|
516
|
+
const response = await prompts({
|
|
517
|
+
type: "confirm",
|
|
518
|
+
name: "installCLI",
|
|
519
|
+
message: "Add Cortex CLI scripts to package.json?",
|
|
520
|
+
initial: true,
|
|
521
|
+
});
|
|
522
|
+
return response.installCLI ?? false;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Show confirmation screen
|
|
526
|
+
*/
|
|
527
|
+
async function showConfirmation(config) {
|
|
528
|
+
console.log(pc.cyan("\n Configuration Summary"));
|
|
529
|
+
console.log(pc.dim(" " + "─".repeat(46)));
|
|
530
|
+
console.log(pc.bold(" Project:"), config.projectName);
|
|
531
|
+
console.log(pc.bold(" Location:"), config.projectPath);
|
|
532
|
+
console.log(pc.bold(" Type:"), config.installationType === "new" ? "New project" : "Add to existing");
|
|
533
|
+
console.log(pc.bold(" Convex:"), config.convexSetupType === "new" ? "New Convex project" : "Existing deployment");
|
|
534
|
+
console.log(pc.bold(" Graph DB:"), config.graphEnabled ? config.graphType : "Disabled");
|
|
535
|
+
console.log(pc.bold(" CLI Scripts:"), config.installCLI ? "Yes" : "No");
|
|
536
|
+
console.log(pc.dim(" " + "─".repeat(46)));
|
|
537
|
+
const response = await prompts({
|
|
538
|
+
type: "confirm",
|
|
539
|
+
name: "confirm",
|
|
540
|
+
message: "Proceed with setup?",
|
|
541
|
+
initial: true,
|
|
542
|
+
});
|
|
543
|
+
if (!response.confirm) {
|
|
544
|
+
console.log(pc.yellow("\n Setup cancelled"));
|
|
545
|
+
process.exit(0);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Execute the setup
|
|
550
|
+
*/
|
|
551
|
+
async function executeSetup(config) {
|
|
552
|
+
console.log(pc.cyan("\n Setting up Cortex...\n"));
|
|
553
|
+
try {
|
|
554
|
+
// Create project directory
|
|
555
|
+
await fs.ensureDir(config.projectPath);
|
|
556
|
+
// Fetch SDK metadata to get correct Convex version
|
|
557
|
+
const metadataSpinner = ora("Fetching SDK metadata...").start();
|
|
558
|
+
const sdkMetadata = await fetchLatestSDKMetadata();
|
|
559
|
+
metadataSpinner.succeed(`SDK v${sdkMetadata.sdkVersion} (Convex ${sdkMetadata.convexVersion})`);
|
|
560
|
+
// Copy template files (check if package.json exists)
|
|
561
|
+
const needsTemplate = !fs.existsSync(path.join(config.projectPath, "package.json"));
|
|
562
|
+
if (needsTemplate || config.installationType === "new") {
|
|
563
|
+
const spinner = ora("Creating project files...").start();
|
|
564
|
+
await copyTemplate("basic", config.projectPath, config.projectName, sdkMetadata.convexVersion);
|
|
565
|
+
spinner.succeed("Project files created");
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
console.log(pc.dim(" Using existing project files"));
|
|
569
|
+
}
|
|
570
|
+
// Create .gitignore first (before any generated files)
|
|
571
|
+
await ensureGitignore(config.projectPath);
|
|
572
|
+
// Install dependencies FIRST - required for convex dev to work
|
|
573
|
+
const installSpinner = ora("Installing dependencies...").start();
|
|
574
|
+
const result = await execCommand("npm", ["install"], {
|
|
575
|
+
cwd: config.projectPath,
|
|
576
|
+
quiet: true,
|
|
577
|
+
});
|
|
578
|
+
if (result.code !== 0) {
|
|
579
|
+
installSpinner.fail("Failed to install dependencies");
|
|
580
|
+
console.error(pc.red(result.stderr));
|
|
581
|
+
throw new Error("npm install failed");
|
|
582
|
+
}
|
|
583
|
+
installSpinner.succeed("Dependencies installed");
|
|
584
|
+
// Verify convex was installed (required for convex dev)
|
|
585
|
+
const convexCheck = fs.existsSync(path.join(config.projectPath, "node_modules", "convex"));
|
|
586
|
+
if (!convexCheck) {
|
|
587
|
+
console.warn(pc.yellow(" Warning: convex package not found in node_modules"));
|
|
588
|
+
console.log(pc.dim(" This may cause Convex setup to fail"));
|
|
589
|
+
}
|
|
590
|
+
// Copy Cortex backend functions FIRST (before convex dev)
|
|
591
|
+
// This way we deploy everything in ONE step
|
|
592
|
+
const backendSpinner = ora("Setting up Cortex backend functions...").start();
|
|
593
|
+
await deployCortexBackend(config.projectPath);
|
|
594
|
+
await createConvexJson(config.projectPath);
|
|
595
|
+
backendSpinner.succeed("Cortex backend files ready");
|
|
596
|
+
// Setup and deploy Convex in ONE step
|
|
597
|
+
let convexConfig;
|
|
598
|
+
if (config.convexSetupType === "new") {
|
|
599
|
+
// For new projects: prompt for project creation and deploy everything
|
|
600
|
+
convexConfig = await setupNewConvex(config.projectPath);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
// For existing projects: get URL/key, then deploy
|
|
604
|
+
convexConfig = await setupExistingConvex();
|
|
605
|
+
await deployToConvex(config.projectPath, convexConfig, false);
|
|
606
|
+
}
|
|
607
|
+
// Update config with actual Convex details
|
|
608
|
+
config.convexUrl = convexConfig.convexUrl;
|
|
609
|
+
config.deployKey = convexConfig.deployKey;
|
|
610
|
+
// Create .env.local (may already exist from convex dev, but ensure our values)
|
|
611
|
+
await createEnvFile(config.projectPath, config);
|
|
612
|
+
// Read actual Convex URL from .env.local (convex dev may have created/updated it)
|
|
613
|
+
const envLocalPath = path.join(config.projectPath, ".env.local");
|
|
614
|
+
if (fs.existsSync(envLocalPath)) {
|
|
615
|
+
try {
|
|
616
|
+
const envContent = await fs.readFile(envLocalPath, "utf-8");
|
|
617
|
+
const urlMatch = envContent.match(/CONVEX_URL=(.+)/);
|
|
618
|
+
const keyMatch = envContent.match(/CONVEX_DEPLOY_KEY=(.+)/);
|
|
619
|
+
if (urlMatch) {
|
|
620
|
+
config.convexUrl = urlMatch[1].trim();
|
|
621
|
+
}
|
|
622
|
+
if (keyMatch && !config.deployKey) {
|
|
623
|
+
config.deployKey = keyMatch[1].trim();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
// Ignore read errors
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Setup graph database if enabled
|
|
631
|
+
if (config.graphEnabled && config.graphType !== "skip") {
|
|
632
|
+
const graphSpinner = ora("Configuring graph database...").start();
|
|
633
|
+
await setupGraphFiles(config.projectPath, {
|
|
634
|
+
type: config.graphType,
|
|
635
|
+
uri: config.graphUri,
|
|
636
|
+
username: config.graphUsername,
|
|
637
|
+
password: config.graphPassword,
|
|
638
|
+
});
|
|
639
|
+
// Add graph env vars to .env.local (after Convex may have modified it)
|
|
640
|
+
await appendGraphEnvVars(config.projectPath, {
|
|
641
|
+
graphUri: config.graphUri,
|
|
642
|
+
graphUsername: config.graphUsername,
|
|
643
|
+
graphPassword: config.graphPassword,
|
|
644
|
+
});
|
|
645
|
+
await addGraphDependencies(config.projectPath);
|
|
646
|
+
await createGraphExample(config.projectPath);
|
|
647
|
+
graphSpinner.succeed("Graph database configured");
|
|
648
|
+
// Start graph containers if local deployment
|
|
649
|
+
const isLocalGraph = config.graphUri?.includes("localhost") ||
|
|
650
|
+
config.graphUri?.includes("127.0.0.1");
|
|
651
|
+
if (isLocalGraph) {
|
|
652
|
+
console.log();
|
|
653
|
+
await startGraphContainers(config.projectPath, config.graphType);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// Add CLI scripts if requested
|
|
657
|
+
if (config.installCLI) {
|
|
658
|
+
await addCLIScripts(config.projectPath);
|
|
659
|
+
}
|
|
660
|
+
// Save deployment to user config (~/.cortexrc)
|
|
661
|
+
await saveDeploymentToConfig(config);
|
|
662
|
+
// Success!
|
|
663
|
+
showSuccessMessage(config);
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
console.error(pc.red("\n Setup failed:"), error);
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Save deployment configuration to ~/.cortexrc
|
|
672
|
+
*/
|
|
673
|
+
async function saveDeploymentToConfig(config) {
|
|
674
|
+
// Skip if no URL was configured
|
|
675
|
+
if (!config.convexUrl) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
try {
|
|
679
|
+
const userConfig = await loadConfig();
|
|
680
|
+
// Always use project name as deployment name (sanitized)
|
|
681
|
+
// This keeps deployment names consistent regardless of URL type
|
|
682
|
+
const deploymentName = config.projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
683
|
+
// Add the deployment
|
|
684
|
+
userConfig.deployments[deploymentName] = {
|
|
685
|
+
url: config.convexUrl,
|
|
686
|
+
key: config.deployKey,
|
|
687
|
+
projectPath: config.projectPath,
|
|
688
|
+
};
|
|
689
|
+
// Set as default if no default exists
|
|
690
|
+
if (!userConfig.default) {
|
|
691
|
+
userConfig.default = deploymentName;
|
|
692
|
+
}
|
|
693
|
+
await saveUserConfig(userConfig);
|
|
694
|
+
console.log(pc.dim(` Saved deployment "${deploymentName}" to config`));
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
// Non-critical error - warn but don't fail
|
|
698
|
+
console.warn(pc.yellow(" Warning: Could not save deployment to config"));
|
|
699
|
+
console.log(pc.dim(" Run 'cortex config add-deployment' to add manually"));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Add CLI scripts to package.json
|
|
704
|
+
*/
|
|
705
|
+
async function addCLIScripts(projectPath) {
|
|
706
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
707
|
+
try {
|
|
708
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
709
|
+
// Add CLI scripts
|
|
710
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
711
|
+
packageJson.scripts["cortex"] = "cortex";
|
|
712
|
+
packageJson.scripts["cortex:setup"] = "cortex setup";
|
|
713
|
+
packageJson.scripts["cortex:stats"] = "cortex db stats";
|
|
714
|
+
packageJson.scripts["cortex:spaces"] = "cortex spaces list";
|
|
715
|
+
packageJson.scripts["cortex:status"] = "cortex status";
|
|
716
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
717
|
+
console.log(pc.dim(" Added CLI scripts to package.json"));
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
// Non-critical, skip silently
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Show success message
|
|
725
|
+
*/
|
|
726
|
+
function showSuccessMessage(config) {
|
|
727
|
+
console.log(pc.bold(pc.green("\n Cortex Memory successfully initialized!\n")));
|
|
728
|
+
console.log(pc.bold(" Project:"), config.projectName);
|
|
729
|
+
console.log(pc.bold(" Database:"), config.convexSetupType === "local"
|
|
730
|
+
? "Local Convex (development)"
|
|
731
|
+
: `Convex Cloud (${config.convexUrl})`);
|
|
732
|
+
if (config.graphEnabled && config.graphType !== "skip") {
|
|
733
|
+
console.log(pc.bold(" Graph:"), config.graphType, "(configured)");
|
|
734
|
+
}
|
|
735
|
+
console.log(pc.green("\n Setup complete!\n"));
|
|
736
|
+
console.log(pc.bold(" Next steps:\n"));
|
|
737
|
+
if (config.installationType === "new") {
|
|
738
|
+
console.log(pc.cyan(` cd ${config.projectName}`));
|
|
739
|
+
}
|
|
740
|
+
if (config.convexSetupType === "local") {
|
|
741
|
+
console.log(pc.cyan(" npm run dev") + pc.dim(" # Start Convex in watch mode"));
|
|
742
|
+
console.log(pc.dim(" (Then in another terminal)"));
|
|
743
|
+
console.log(pc.cyan(" npm start") + pc.dim(" # Run your AI agent"));
|
|
744
|
+
console.log(pc.dim("\n Dashboard: http://127.0.0.1:3210"));
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
console.log(pc.cyan(" npm start") + pc.dim(" # Run your AI agent"));
|
|
748
|
+
console.log(pc.dim(`\n Dashboard: ${config.convexUrl?.replace("/api", "")}`));
|
|
749
|
+
}
|
|
750
|
+
// CLI commands
|
|
751
|
+
console.log(pc.bold("\n CLI Commands:\n"));
|
|
752
|
+
console.log(pc.cyan(" cortex status") +
|
|
753
|
+
pc.dim(" # View setup status dashboard"));
|
|
754
|
+
console.log(pc.cyan(" cortex db stats") +
|
|
755
|
+
pc.dim(" # View database statistics"));
|
|
756
|
+
console.log(pc.cyan(" cortex spaces list") + pc.dim(" # List memory spaces"));
|
|
757
|
+
console.log(pc.cyan(" cortex --help") + pc.dim(" # See all CLI commands"));
|
|
758
|
+
console.log(pc.bold("\n Learn more:\n"));
|
|
759
|
+
console.log(pc.dim(" Documentation: https://github.com/SaintNick1214/Project-Cortex/tree/main/Documentation"));
|
|
760
|
+
console.log(pc.dim(" Examples: https://github.com/SaintNick1214/Project-Cortex/tree/main/Examples"));
|
|
761
|
+
console.log(pc.bold(pc.cyan("\n Happy building with Cortex!\n")));
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Start Convex development server in background
|
|
765
|
+
* Note: Deploy to production is handled by the caller before this function
|
|
766
|
+
*/
|
|
767
|
+
async function startConvexInBackground(projectPath, isLocal, deploymentUrl, deploymentKey) {
|
|
768
|
+
const hasConvex = await commandExists("convex");
|
|
769
|
+
const command = hasConvex ? "convex" : "npx";
|
|
770
|
+
// Set up environment with deployment-specific URL/key if provided
|
|
771
|
+
const env = { ...process.env };
|
|
772
|
+
if (deploymentUrl)
|
|
773
|
+
env.CONVEX_URL = deploymentUrl;
|
|
774
|
+
if (deploymentKey)
|
|
775
|
+
env.CONVEX_DEPLOY_KEY = deploymentKey;
|
|
776
|
+
// Start convex dev in background for watch mode
|
|
777
|
+
const devSpinner = ora("Starting Convex development server...").start();
|
|
778
|
+
const args = hasConvex ? ["dev"] : ["convex", "dev"];
|
|
779
|
+
if (isLocal) {
|
|
780
|
+
args.push("--local");
|
|
781
|
+
}
|
|
782
|
+
// Create log file for the background process
|
|
783
|
+
const logFile = path.join(projectPath, ".convex-dev.log");
|
|
784
|
+
// Use fs.openSync to get a file descriptor (required for detached process stdio)
|
|
785
|
+
const logFd = fs.openSync(logFile, "a");
|
|
786
|
+
// Spawn detached process
|
|
787
|
+
const child = spawn(command, args, {
|
|
788
|
+
cwd: projectPath,
|
|
789
|
+
detached: true,
|
|
790
|
+
stdio: ["ignore", logFd, logFd],
|
|
791
|
+
env,
|
|
792
|
+
});
|
|
793
|
+
// Close the file descriptor in parent process (child keeps it open)
|
|
794
|
+
fs.closeSync(logFd);
|
|
795
|
+
// Unref so parent can exit independently
|
|
796
|
+
child.unref();
|
|
797
|
+
// Save PID for later management
|
|
798
|
+
const pidFile = path.join(projectPath, ".convex-dev.pid");
|
|
799
|
+
await fs.writeFile(pidFile, String(child.pid));
|
|
800
|
+
// Wait a moment for startup
|
|
801
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
802
|
+
devSpinner.succeed("Convex development server started in background");
|
|
803
|
+
console.log(pc.dim(` PID: ${child.pid}`));
|
|
804
|
+
console.log(pc.dim(` Log: ${path.basename(logFile)}`));
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Show running services status dashboard
|
|
808
|
+
*/
|
|
809
|
+
async function showRunningStatus(projectPath, isLocal) {
|
|
810
|
+
const width = 56;
|
|
811
|
+
const line = "═".repeat(width);
|
|
812
|
+
const thinLine = "─".repeat(width);
|
|
813
|
+
console.log(pc.cyan("╔" + line + "╗"));
|
|
814
|
+
console.log(pc.cyan("║") +
|
|
815
|
+
pc.bold(" Cortex Development Environment").padEnd(width) +
|
|
816
|
+
pc.cyan("║"));
|
|
817
|
+
console.log(pc.cyan("╚" + line + "╝"));
|
|
818
|
+
console.log();
|
|
819
|
+
// Convex Status
|
|
820
|
+
console.log(pc.bold(pc.white(" Convex Backend")));
|
|
821
|
+
console.log(pc.dim(" " + thinLine));
|
|
822
|
+
const pidFile = path.join(projectPath, ".convex-dev.pid");
|
|
823
|
+
let convexRunning = false;
|
|
824
|
+
let convexPid = null;
|
|
825
|
+
try {
|
|
826
|
+
convexPid = await fs.readFile(pidFile, "utf-8");
|
|
827
|
+
// Check if process is running
|
|
828
|
+
try {
|
|
829
|
+
process.kill(parseInt(convexPid), 0);
|
|
830
|
+
convexRunning = true;
|
|
831
|
+
}
|
|
832
|
+
catch {
|
|
833
|
+
convexRunning = false;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
catch {
|
|
837
|
+
convexRunning = false;
|
|
838
|
+
}
|
|
839
|
+
if (convexRunning) {
|
|
840
|
+
console.log(` ${pc.green("●")} Running (PID: ${convexPid})`);
|
|
841
|
+
if (isLocal) {
|
|
842
|
+
console.log(pc.dim(" Dashboard: http://127.0.0.1:3210"));
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
console.log(pc.dim(` URL: ${process.env.CONVEX_URL || "configured"}`));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
console.log(` ${pc.yellow("○")} Not running`);
|
|
850
|
+
console.log(pc.dim(" Run: cortex start"));
|
|
851
|
+
}
|
|
852
|
+
console.log();
|
|
853
|
+
// Graph Database Status
|
|
854
|
+
console.log(pc.bold(pc.white(" Graph Database")));
|
|
855
|
+
console.log(pc.dim(" " + thinLine));
|
|
856
|
+
const hasGraphConfig = process.env.NEO4J_URI || fs.existsSync(path.join(projectPath, "docker-compose.graph.yml"));
|
|
857
|
+
if (hasGraphConfig) {
|
|
858
|
+
// Check if Docker container is running
|
|
859
|
+
try {
|
|
860
|
+
const result = await execCommand("docker", ["ps", "--filter", "name=cortex-neo4j", "--format", "{{.Status}}"], { quiet: true });
|
|
861
|
+
if (result.stdout.includes("Up")) {
|
|
862
|
+
console.log(` ${pc.green("●")} Neo4j running`);
|
|
863
|
+
console.log(pc.dim(" Browser: http://localhost:7474"));
|
|
864
|
+
console.log(pc.dim(" Bolt: bolt://localhost:7687"));
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
console.log(` ${pc.yellow("○")} Neo4j configured but not running`);
|
|
868
|
+
console.log(pc.dim(" Start: docker-compose -f docker-compose.graph.yml up -d"));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
catch {
|
|
872
|
+
console.log(` ${pc.yellow("○")} Neo4j configured but Docker not available`);
|
|
873
|
+
console.log(pc.dim(" Start Docker Desktop first"));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
console.log(` ${pc.dim("○")} Not configured`);
|
|
878
|
+
}
|
|
879
|
+
console.log();
|
|
880
|
+
// Quick Actions
|
|
881
|
+
console.log(pc.bold(pc.white(" Quick Actions")));
|
|
882
|
+
console.log(pc.dim(" " + thinLine));
|
|
883
|
+
console.log(pc.dim(" cortex status") + pc.dim(" # Full status dashboard"));
|
|
884
|
+
console.log(pc.dim(" cortex start -f") + pc.dim(" # Foreground mode (see logs)"));
|
|
885
|
+
console.log(pc.dim(" cortex stop") + pc.dim(" # Stop background services"));
|
|
886
|
+
console.log(pc.dim(" npm start") + pc.dim(" # Run your AI agent"));
|
|
887
|
+
console.log();
|
|
888
|
+
// Log file hint
|
|
889
|
+
const logFile = path.join(projectPath, ".convex-dev.log");
|
|
890
|
+
if (fs.existsSync(logFile)) {
|
|
891
|
+
console.log(pc.dim(` View logs: tail -f ${path.basename(logFile)}`));
|
|
892
|
+
}
|
|
893
|
+
console.log();
|
|
894
|
+
}
|
|
895
|
+
//# sourceMappingURL=init.js.map
|