@cruxgarden/cli 0.0.1 → 0.0.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.
Binary file
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- 22.13.1
1
+ 22.19.0
package/README.md CHANGED
@@ -1,6 +1,13 @@
1
- # Crux Garden CLI
1
+ <div align="center">
2
+ <img src=".github/banner.jpg" alt="Crux Garden - Where Ideas Grow" width="100%">
3
+ <p>
4
+ <a href="https://github.com/CruxGarden/cli/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
5
+ <a href="https://github.com/CruxGarden/cli/issues"><img src="https://img.shields.io/github/issues/CruxGarden/cli" alt="Issues"></a>
6
+ <a href="https://github.com/CruxGarden/cli/stargazers"><img src="https://img.shields.io/github/stars/CruxGarden/cli" alt="Stars"></a>
7
+ </p>
8
+ </div>
2
9
 
3
- CLI tool to manage the Crux Garden Nursery environment with Docker.
10
+ The Crux Garden CLI tool helps manage the Crux Garden Nursery environment with Docker.
4
11
 
5
12
  The **Nursery** is a production-like demo environment with sample data, perfect for trials, demos, and showcasing features.
6
13
 
@@ -55,7 +62,7 @@ Start the Nursery environment:
55
62
  crux nursery start
56
63
  ```
57
64
 
58
- The API will be available at `http://localhost:3001` with demo data loaded.
65
+ The API will be available at `http://localhost:3000` with demo data loaded.
59
66
 
60
67
  View logs:
61
68
 
@@ -144,6 +151,20 @@ Stop and remove all Nursery containers and volumes. **Warning: This deletes all
144
151
  crux nursery clean
145
152
  ```
146
153
 
154
+ ### `crux nursery purge`
155
+
156
+ Stop and remove ALL Nursery resources including containers, volumes, AND images. **Warning: This deletes everything including downloaded images! You'll need to re-download images on next start.**
157
+
158
+ ```bash
159
+ crux nursery purge
160
+ ```
161
+
162
+ This is more aggressive than `clean` - it removes Docker images too, which means:
163
+
164
+ - Frees up more disk space
165
+ - Requires re-downloading images (~100-500MB) on next start
166
+ - Useful when you want to completely remove all traces of the Nursery
167
+
147
168
  ### `crux nursery db start`
148
169
 
149
170
  Start only Nursery database services (PostgreSQL and Redis).
@@ -201,9 +222,9 @@ The Nursery is a production-like demo environment that:
201
222
 
202
223
  **Services:**
203
224
 
204
- - **API**: `http://localhost:3001` - Crux Garden API (published image with demo data)
205
- - **PostgreSQL**: `localhost:5433` - Database
206
- - **Redis**: `localhost:6380` - Cache
225
+ - **API**: `http://localhost:3000` - Crux Garden API (published image with demo data)
226
+ - **PostgreSQL**: `localhost:5432` - Database
227
+ - **Redis**: `localhost:6379` - Cache
207
228
 
208
229
  ## Environment Variables
209
230
 
@@ -222,7 +243,7 @@ FROM_EMAIL_ADDRESS=demo@example.com
222
243
  # Optional overrides
223
244
  CORS_ORIGIN=*
224
245
  LOG_LEVEL=info
225
- PORT=3001
246
+ PORT=3000
226
247
  ```
227
248
 
228
249
  ## Common Workflows
@@ -235,7 +256,7 @@ Use the Nursery environment for demos, trials, or showcasing features:
235
256
  # First time setup - pulls image and starts with demo data
236
257
  crux nursery start
237
258
 
238
- # View the demo at http://localhost:3001
259
+ # View the demo at http://localhost:3000
239
260
 
240
261
  # Stop (keeps data for next demo)
241
262
  crux nursery stop
@@ -287,9 +308,9 @@ If you get an error about ports being in use, stop any existing services:
287
308
 
288
309
  ```bash
289
310
  # Check what's using the ports
290
- lsof -i :3001 # Nursery API
291
- lsof -i :5433 # Nursery PostgreSQL
292
- lsof -i :6380 # Nursery Redis
311
+ lsof -i :3000 # Nursery API
312
+ lsof -i :5432 # Nursery PostgreSQL
313
+ lsof -i :6379 # Nursery Redis
293
314
 
294
315
  # Stop the Nursery
295
316
  crux nursery stop
package/bin/crux.js CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from "commander";
3
+ import { readFileSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
3
6
  import {
4
7
  showBanner,
5
8
  startNursery,
@@ -8,6 +11,7 @@ import {
8
11
  statusNursery,
9
12
  logsNursery,
10
13
  cleanNursery,
14
+ purgeNursery,
11
15
  pullNursery,
12
16
  resetNursery,
13
17
  connectNurseryDb,
@@ -16,10 +20,16 @@ import {
16
20
  stopNurseryDb,
17
21
  } from "../lib/commands.js";
18
22
 
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const packageJson = JSON.parse(
26
+ readFileSync(join(__dirname, "..", "package.json"), "utf-8"),
27
+ );
28
+
19
29
  program
20
30
  .name("crux")
21
- .description("Crux Garden CLI - Nursery Environment Manager")
22
- .version("0.1.0");
31
+ .description("Crux Garden CLI")
32
+ .version(packageJson.version);
23
33
 
24
34
  // Nursery environment commands
25
35
  const nursery = program
@@ -28,7 +38,7 @@ const nursery = program
28
38
 
29
39
  nursery
30
40
  .command("start")
31
- .description("Start the Nursery environment (postgres, redis, api)")
41
+ .description("Start the Nursery environment (api, postgres, redis)")
32
42
  .option("--db-only", "Start only database services (postgres, redis)")
33
43
  .option("--no-banner", "Hide the startup banner")
34
44
  .action((options) => {
@@ -62,6 +72,13 @@ nursery
62
72
  .description("Stop and remove all Nursery containers and volumes")
63
73
  .action(cleanNursery);
64
74
 
75
+ nursery
76
+ .command("purge")
77
+ .description(
78
+ "Stop and remove ALL Nursery resources (containers, volumes, AND images)",
79
+ )
80
+ .action(purgeNursery);
81
+
65
82
  nursery
66
83
  .command("pull")
67
84
  .description("Pull the latest API image from ghcr.io")
@@ -4,17 +4,19 @@
4
4
  # Perfect for: demos, trials, showcasing features, onboarding
5
5
  # Usage: npm run docker:nursery
6
6
 
7
+ name: cruxgarden-nursery
8
+
7
9
  services:
8
10
  # PostgreSQL Database
9
11
  postgres:
10
12
  image: postgres:16-alpine
11
- container_name: cruxgarden-postgres-nursery
13
+ container_name: cruxgarden-nursery-postgres
12
14
  environment:
13
15
  POSTGRES_DB: cruxgarden
14
16
  POSTGRES_USER: cruxgarden
15
17
  POSTGRES_PASSWORD: cruxgarden_nursery_password
16
18
  ports:
17
- - "5433:5432"
19
+ - "5432:5432"
18
20
  volumes:
19
21
  - postgres_data_nursery:/var/lib/postgresql/data
20
22
  healthcheck:
@@ -26,10 +28,10 @@ services:
26
28
  # Redis Cache
27
29
  redis:
28
30
  image: redis:7-alpine
29
- container_name: cruxgarden-redis-nursery
31
+ container_name: cruxgarden-nursery-redis
30
32
  command: redis-server --appendonly yes
31
33
  ports:
32
- - "6380:6379"
34
+ - "6379:6379"
33
35
  volumes:
34
36
  - redis_data_nursery:/data
35
37
  healthcheck:
@@ -41,7 +43,7 @@ services:
41
43
  # Database Migrations (runs once before API starts)
42
44
  migrations:
43
45
  image: ghcr.io/cruxgarden/api:latest
44
- container_name: cruxgarden-migrations-nursery
46
+ container_name: cruxgarden-nursery-migrations
45
47
  depends_on:
46
48
  postgres:
47
49
  condition: service_healthy
@@ -54,7 +56,7 @@ services:
54
56
  # Crux Garden API - Nursery Configuration
55
57
  api:
56
58
  image: ghcr.io/cruxgarden/api:latest
57
- container_name: cruxgarden-api-nursery
59
+ container_name: cruxgarden-nursery-api
58
60
  restart: unless-stopped
59
61
  depends_on:
60
62
  migrations:
@@ -62,7 +64,7 @@ services:
62
64
  redis:
63
65
  condition: service_healthy
64
66
  ports:
65
- - "${PORT:-3001}:3000"
67
+ - "${PORT:-3000}:3000"
66
68
  environment:
67
69
  # Application
68
70
  NODE_ENV: production
package/lib/commands.js CHANGED
@@ -56,6 +56,53 @@ function runCommand(command, options = {}) {
56
56
  }
57
57
  }
58
58
 
59
+ function runCommandAsync(command, options = {}) {
60
+ return new Promise((resolve, reject) => {
61
+ const [cmd, ...args] = command.split(/\s+/);
62
+ const child = spawn(cmd, args, {
63
+ cwd: dockerDir,
64
+ stdio: options.silent ? "pipe" : "inherit",
65
+ shell: true,
66
+ ...options,
67
+ });
68
+
69
+ let stdout = "";
70
+ let stderr = "";
71
+
72
+ if (options.silent) {
73
+ if (child.stdout) {
74
+ child.stdout.on("data", (data) => {
75
+ stdout += data.toString();
76
+ });
77
+ }
78
+ if (child.stderr) {
79
+ child.stderr.on("data", (data) => {
80
+ stderr += data.toString();
81
+ });
82
+ }
83
+ }
84
+
85
+ child.on("close", (code) => {
86
+ if (code !== 0 && !options.ignoreError) {
87
+ const error = new Error(
88
+ stderr || `Command failed with exit code ${code}`,
89
+ );
90
+ reject(error);
91
+ } else {
92
+ resolve(stdout);
93
+ }
94
+ });
95
+
96
+ child.on("error", (error) => {
97
+ if (!options.ignoreError) {
98
+ reject(error);
99
+ } else {
100
+ resolve(null);
101
+ }
102
+ });
103
+ });
104
+ }
105
+
59
106
  function askConfirmation(question) {
60
107
  return new Promise((resolve) => {
61
108
  const rl = readline.createInterface({
@@ -80,23 +127,42 @@ export async function startNursery(options) {
80
127
  try {
81
128
  if (options.dbOnly) {
82
129
  spinner.text = "Starting nursery database services (postgres, redis)...";
83
- runCommand(
84
- "docker-compose -f docker-compose.nursery.yml up -d postgres redis",
130
+ await runCommandAsync(
131
+ "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans postgres redis",
85
132
  { silent: true },
86
133
  );
87
134
  spinner.succeed("Nursery database services started!");
88
- console.log(chalk.hex(SUCCESS_GREEN)("\n✓ PostgreSQL running on:"), "localhost:5433");
89
- console.log(chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"), "localhost:6380");
135
+ console.log(
136
+ chalk.hex(SUCCESS_GREEN)("\nPostgreSQL running on:"),
137
+ "localhost:5432",
138
+ );
139
+ console.log(
140
+ chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
141
+ "localhost:6379",
142
+ );
90
143
  } else {
91
144
  spinner.text =
92
- "Starting nursery services (postgres, redis, migrations, api)...";
93
- runCommand("docker-compose -f docker-compose.nursery.yml up -d", {
94
- silent: true,
95
- });
145
+ "Starting nursery services (api, postgres, redis)...";
146
+ await runCommandAsync(
147
+ "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans && docker rm cruxgarden-nursery-migrations 2>/dev/null || true",
148
+ {
149
+ silent: true,
150
+ },
151
+ );
152
+
96
153
  spinner.succeed("Crux Garden Nursery environment started!");
97
- console.log(chalk.hex(SUCCESS_GREEN)("\n✓ API running on:"), "http://localhost:3001");
98
- console.log(chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"), "localhost:5433");
99
- console.log(chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"), "localhost:6380");
154
+ console.log(
155
+ chalk.hex(SUCCESS_GREEN)("\nAPI running on:"),
156
+ "http://localhost:3000",
157
+ );
158
+ console.log(
159
+ chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"),
160
+ "localhost:5432",
161
+ );
162
+ console.log(
163
+ chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
164
+ "localhost:6379",
165
+ );
100
166
  console.log(
101
167
  chalk.yellow(
102
168
  "\nℹ Nursery includes demo data for trials and showcases",
@@ -109,6 +175,11 @@ export async function startNursery(options) {
109
175
  chalk.cyan("crux nursery logs"),
110
176
  chalk.gray("to view logs"),
111
177
  );
178
+ console.log(
179
+ chalk.gray("Run"),
180
+ chalk.cyan("crux nursery api connect"),
181
+ chalk.gray("to shell into the API container"),
182
+ );
112
183
  console.log(
113
184
  chalk.gray("Run"),
114
185
  chalk.cyan("crux nursery db connect"),
@@ -119,11 +190,6 @@ export async function startNursery(options) {
119
190
  chalk.cyan("crux nursery redis connect"),
120
191
  chalk.gray("to connect to Redis"),
121
192
  );
122
- console.log(
123
- chalk.gray("Run"),
124
- chalk.cyan("crux nursery api connect"),
125
- chalk.gray("to shell into the API container"),
126
- );
127
193
  } catch (error) {
128
194
  spinner.fail("Failed to start nursery services");
129
195
  throw error;
@@ -132,7 +198,7 @@ export async function startNursery(options) {
132
198
 
133
199
  export async function stopNursery() {
134
200
  const spinner = ora("Stopping Crux Garden Nursery environment...").start();
135
- runCommand("docker-compose -f docker-compose.nursery.yml down", {
201
+ await runCommandAsync("docker-compose -f docker-compose.nursery.yml down", {
136
202
  silent: true,
137
203
  });
138
204
  spinner.succeed("Crux Garden Nursery environment stopped!");
@@ -186,10 +252,13 @@ export async function cleanNursery() {
186
252
  "Cleaning up nursery (removing containers and volumes)...",
187
253
  ).start();
188
254
 
189
- runCommand("docker-compose -f docker-compose.nursery.yml down -v", {
190
- silent: true,
191
- ignoreError: true,
192
- });
255
+ await runCommandAsync(
256
+ "docker-compose -f docker-compose.nursery.yml down -v",
257
+ {
258
+ silent: true,
259
+ ignoreError: true,
260
+ },
261
+ );
193
262
 
194
263
  spinner.succeed("Nursery cleanup complete!");
195
264
  console.log(
@@ -199,12 +268,54 @@ export async function cleanNursery() {
199
268
  );
200
269
  }
201
270
 
271
+ export async function purgeNursery() {
272
+ console.log(
273
+ chalk.yellow(
274
+ "\n⚠️ This will delete ALL nursery resources (containers, volumes, AND images)!\n",
275
+ ),
276
+ );
277
+ console.log(
278
+ chalk.yellow(
279
+ "You will need to re-download images on next start (may take several minutes).\n",
280
+ ),
281
+ );
282
+
283
+ const confirmed = await askConfirmation(
284
+ chalk.red("Are you sure you want to continue? (y/N): "),
285
+ );
286
+
287
+ if (!confirmed) {
288
+ console.log(chalk.gray("\nPurge cancelled."));
289
+ return;
290
+ }
291
+
292
+ const spinner = ora(
293
+ "Purging nursery (removing containers, volumes, and images)...",
294
+ ).start();
295
+
296
+ await runCommandAsync(
297
+ "docker-compose -f docker-compose.nursery.yml down -v --rmi all",
298
+ {
299
+ silent: true,
300
+ ignoreError: true,
301
+ },
302
+ );
303
+
304
+ spinner.succeed("Nursery purge complete!");
305
+ console.log(chalk.gray("\nAll nursery resources have been removed."));
306
+ console.log(
307
+ chalk.gray("Run"),
308
+ chalk.cyan("crux nursery start"),
309
+ chalk.gray("to download images and start fresh."),
310
+ );
311
+ }
312
+
202
313
  export async function pullNursery() {
203
314
  const spinner = ora(
204
315
  "Pulling latest Crux Garden API image from ghcr.io...",
205
316
  ).start();
206
317
 
207
- runCommand("docker-compose -f docker-compose.nursery.yml pull", {
318
+ await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
208
319
  silent: true,
209
320
  });
210
321
 
@@ -237,27 +348,42 @@ export async function resetNursery() {
237
348
  try {
238
349
  // Stop and remove volumes
239
350
  spinner.text = "Stopping and removing containers/volumes...";
240
- runCommand("docker-compose -f docker-compose.nursery.yml down -v", {
241
- silent: true,
242
- ignoreError: true,
243
- });
351
+ await runCommandAsync(
352
+ "docker-compose -f docker-compose.nursery.yml down -v",
353
+ {
354
+ silent: true,
355
+ ignoreError: true,
356
+ },
357
+ );
244
358
 
245
359
  // Pull latest image
246
360
  spinner.text = "Pulling latest image...";
247
- runCommand("docker-compose -f docker-compose.nursery.yml pull", {
361
+ await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
248
362
  silent: true,
249
363
  });
250
364
 
251
365
  // Start fresh
252
366
  spinner.text = "Starting fresh nursery environment...";
253
- runCommand("docker-compose -f docker-compose.nursery.yml up -d", {
254
- silent: true,
255
- });
367
+ await runCommandAsync(
368
+ "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans && docker rm cruxgarden-nursery-migrations 2>/dev/null || true",
369
+ {
370
+ silent: true,
371
+ },
372
+ );
256
373
 
257
374
  spinner.succeed("Nursery environment reset complete!");
258
- console.log(chalk.hex(SUCCESS_GREEN)("\n✓ API running on:"), "http://localhost:3001");
259
- console.log(chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"), "localhost:5433");
260
- console.log(chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"), "localhost:6380");
375
+ console.log(
376
+ chalk.hex(SUCCESS_GREEN)("\nAPI running on:"),
377
+ "http://localhost:3000",
378
+ );
379
+ console.log(
380
+ chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"),
381
+ "localhost:5432",
382
+ );
383
+ console.log(
384
+ chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
385
+ "localhost:6379",
386
+ );
261
387
  console.log(
262
388
  chalk.yellow("\nℹ Fresh nursery with latest demo data loaded"),
263
389
  );
@@ -280,23 +406,23 @@ export async function statusNursery() {
280
406
  export async function connectNurseryDb() {
281
407
  console.log(chalk.gray("Connecting to Nursery PostgreSQL...\n"));
282
408
  runCommand(
283
- "docker exec -it cruxgarden-postgres-nursery psql -U cruxgarden -d cruxgarden",
409
+ "docker exec -it cruxgarden-nursery-postgres psql -U cruxgarden -d cruxgarden",
284
410
  );
285
411
  }
286
412
 
287
413
  export async function connectNurseryRedis() {
288
414
  console.log(chalk.gray("Connecting to Nursery Redis...\n"));
289
- runCommand("docker exec -it cruxgarden-redis-nursery redis-cli");
415
+ runCommand("docker exec -it cruxgarden-nursery-redis redis-cli");
290
416
  }
291
417
 
292
418
  export async function connectNurseryApi() {
293
419
  console.log(chalk.gray("Opening shell in Nursery API container...\n"));
294
- runCommand("docker exec -it cruxgarden-api-nursery sh");
420
+ runCommand("docker exec -it cruxgarden-nursery-api sh");
295
421
  }
296
422
 
297
423
  export async function stopNurseryDb() {
298
424
  const spinner = ora("Stopping nursery database services...").start();
299
- runCommand(
425
+ await runCommandAsync(
300
426
  "docker-compose -f docker-compose.nursery.yml stop postgres redis",
301
427
  { silent: true },
302
428
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxgarden/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Crux Garden CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,9 +17,9 @@
17
17
  "docker:nursery:reset": "cd docker && docker-compose -f docker-compose.nursery.yml down -v && docker-compose -f docker-compose.nursery.yml pull && docker-compose -f docker-compose.nursery.yml up -d",
18
18
  "docker:nursery:db": "cd docker && docker-compose -f docker-compose.nursery.yml up -d postgres redis",
19
19
  "docker:nursery:db:stop": "cd docker && docker-compose -f docker-compose.nursery.yml stop postgres redis",
20
- "docker:nursery:redis:connect": "docker exec -it cruxgarden-redis-nursery redis-cli",
21
- "docker:nursery:db:connect": "docker exec -it cruxgarden-postgres-nursery psql -U cruxgarden -d cruxgarden",
22
- "docker:nursery:api:connect": "docker exec -it cruxgarden-api-nursery sh"
20
+ "docker:nursery:redis:connect": "docker exec -it cruxgarden-nursery-redis redis-cli",
21
+ "docker:nursery:db:connect": "docker exec -it cruxgarden-nursery-postgres psql -U cruxgarden -d cruxgarden",
22
+ "docker:nursery:api:connect": "docker exec -it cruxgarden-nursery-api sh"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=18.0.0"