@cruxgarden/cli 0.0.5 → 0.0.7

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/bin/crux.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  logsNursery,
13
13
  cleanNursery,
14
14
  purgeNursery,
15
- pullNursery,
15
+ updateNursery,
16
16
  resetNursery,
17
17
  connectNurseryDb,
18
18
  connectNurseryRedis,
@@ -80,9 +80,9 @@ nursery
80
80
  .action(purgeNursery);
81
81
 
82
82
  nursery
83
- .command("pull")
84
- .description("Pull the latest API image from ghcr.io")
85
- .action(pullNursery);
83
+ .command("update")
84
+ .description("Download the latest API image from ghcr.io")
85
+ .action(updateNursery);
86
86
 
87
87
  nursery
88
88
  .command("reset")
package/lib/commands.js CHANGED
@@ -16,23 +16,40 @@ export function showBanner() {
16
16
  // Check if banner was already shown in this shell session
17
17
  if (process.env.CRUX_BANNER_SHOWN) return;
18
18
 
19
- // Custom ASCII art - replace this with your own!
20
- const banner = `
21
- ██████╗██████╗ ██╗ ██╗██╗ ██╗
22
- ██╔════╝██╔══██╗██║ ██║╚██╗██╔╝
23
- ██║ ██████╔╝██║ ██║ ╚███╔╝
24
- ██║ ██╔══██╗██║ ██║ ██╔██╗
25
- ╚██████╗██║ ██║╚██████╔╝██╔╝ ██╗
26
- ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
27
-
28
- ██████╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ██╗
29
- ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██╔════╝████╗ ██║
30
- ██║ ███╗███████║██████╔╝██║ ██║█████╗ ██╔██╗ ██║
31
- ██║ ██║██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╗██║
32
- ╚██████╔╝██║ ██║██║ ██║██████╔╝███████╗██║ ╚████║
33
- ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝`;
34
-
35
- console.log(chalk.hex(SUCCESS_GREEN)(banner));
19
+ const bannerLines = [
20
+ " ██████╗██████╗ ██╗ ██╗██╗ ██╗",
21
+ "██╔════╝██╔══██╗██║ ██║╚██╗██╔╝",
22
+ "██║ ██████╔╝██║ ██║ ╚███╔╝",
23
+ "██║ ██╔══██╗██║ ██║ ██╔██╗",
24
+ "╚██████╗██║ ██║╚██████╔╝██╔╝ ██╗",
25
+ " ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝",
26
+ "",
27
+ " ██████╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ██╗",
28
+ "██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██╔════╝████╗ ██║",
29
+ "██║ ███╗███████║██████╔╝██║ ██║█████╗ ██╔██╗ ██║",
30
+ "██║ ██║██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╗██║",
31
+ "╚██████╔╝██║ ██║██║ ██║██████╔╝███████╗██║ ╚████║",
32
+ " ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝"
33
+ ];
34
+
35
+ // Randomly pick a color from the CLI color palette
36
+ const colors = [
37
+ chalk.hex(SUCCESS_GREEN), // Green
38
+ chalk.cyan, // Cyan
39
+ chalk.yellow, // Yellow
40
+ chalk.magenta, // Magenta
41
+ chalk.blue, // Blue
42
+ chalk.red, // Red
43
+ ];
44
+
45
+ const randomColor = colors[Math.floor(Math.random() * colors.length)];
46
+
47
+ // Print each line with the same random color
48
+ console.log();
49
+ bannerLines.forEach((line) => {
50
+ console.log(randomColor(line));
51
+ });
52
+
36
53
  console.log(chalk.gray(" Nursery Environment - Demo & Trial\n"));
37
54
 
38
55
  // Set environment variable so it persists for this shell session
@@ -89,7 +106,8 @@ function runCommandAsync(command, options = {}) {
89
106
  );
90
107
  reject(error);
91
108
  } else {
92
- resolve(stdout);
109
+ // Return both stdout and stderr since Docker commands often write to stderr
110
+ resolve({ stdout, stderr, combined: stdout + stderr });
93
111
  }
94
112
  });
95
113
 
@@ -117,11 +135,36 @@ function askConfirmation(question) {
117
135
  });
118
136
  }
119
137
 
138
+ function checkDockerAvailable() {
139
+ try {
140
+ execSync("docker info", { stdio: "pipe" });
141
+ return true;
142
+ } catch (error) {
143
+ return false;
144
+ }
145
+ }
146
+
147
+ function ensureDockerRunning() {
148
+ if (!checkDockerAvailable()) {
149
+ console.error(
150
+ chalk.red(
151
+ "\n✗ Docker is not running or not installed.\n",
152
+ ),
153
+ );
154
+ console.log(chalk.gray("Please ensure Docker is installed and running:"));
155
+ console.log(chalk.gray(" • Start Docker Desktop, or"));
156
+ console.log(chalk.gray(" • Start the Docker daemon\n"));
157
+ console.log(chalk.gray("Then try your command again.\n"));
158
+ process.exit(1);
159
+ }
160
+ }
161
+
120
162
  // ============================================================================
121
163
  // Nursery Environment Commands
122
164
  // ============================================================================
123
165
 
124
166
  export async function startNursery(options) {
167
+ ensureDockerRunning();
125
168
  const spinner = ora("Starting Crux Garden Nursery environment...").start();
126
169
 
127
170
  try {
@@ -198,6 +241,7 @@ export async function startNursery(options) {
198
241
  }
199
242
 
200
243
  export async function stopNursery() {
244
+ ensureDockerRunning();
201
245
  const spinner = ora("Stopping Crux Garden Nursery environment...").start();
202
246
  await runCommandAsync("docker-compose -f docker-compose.nursery.yml down", {
203
247
  silent: true,
@@ -212,6 +256,7 @@ export async function stopNursery() {
212
256
  }
213
257
 
214
258
  export async function logsNursery(options) {
259
+ ensureDockerRunning();
215
260
  if (options.follow) {
216
261
  console.log(
217
262
  chalk.gray("Following nursery logs (press Ctrl+C to exit)...\n"),
@@ -236,6 +281,7 @@ export async function logsNursery(options) {
236
281
  }
237
282
 
238
283
  export async function cleanNursery() {
284
+ ensureDockerRunning();
239
285
  console.log(
240
286
  chalk.yellow(
241
287
  "\n⚠️ This will delete all nursery containers and volumes (including data)!\n",
@@ -273,6 +319,7 @@ export async function cleanNursery() {
273
319
  }
274
320
 
275
321
  export async function purgeNursery() {
322
+ ensureDockerRunning();
276
323
  console.log(
277
324
  chalk.yellow(
278
325
  "\n⚠️ This will delete ALL nursery resources (containers, volumes, AND images)!\n",
@@ -315,25 +362,63 @@ export async function purgeNursery() {
315
362
  console.log();
316
363
  }
317
364
 
318
- export async function pullNursery() {
365
+ export async function updateNursery() {
366
+ ensureDockerRunning();
319
367
  const spinner = ora(
320
- "Pulling latest Crux Garden API image from ghcr.io...",
368
+ "Checking for latest Crux Garden API image from ghcr.io...",
321
369
  ).start();
322
370
 
323
- await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
324
- silent: true,
325
- });
371
+ try {
372
+ // Get the current local image digest before pulling
373
+ const beforeResult = await runCommandAsync(
374
+ "docker images --digests --format '{{.Digest}}' ghcr.io/cruxgarden/api:latest",
375
+ { silent: true, ignoreError: true }
376
+ );
377
+ const digestBefore = beforeResult?.combined?.trim() || beforeResult?.stdout?.trim() || "";
326
378
 
327
- spinner.succeed("Latest nursery image pulled!");
328
- console.log(
329
- chalk.gray("Run"),
330
- chalk.cyan("crux nursery restart"),
331
- chalk.gray("to use the new image."),
332
- );
379
+ // Pull latest images
380
+ spinner.text = "Pulling latest images...";
381
+ await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
382
+ silent: true,
383
+ });
384
+
385
+ // Get the image digest after pulling
386
+ const afterResult = await runCommandAsync(
387
+ "docker images --digests --format '{{.Digest}}' ghcr.io/cruxgarden/api:latest",
388
+ { silent: true, ignoreError: true }
389
+ );
390
+ const digestAfter = afterResult?.combined?.trim() || afterResult?.stdout?.trim() || "";
391
+
392
+ // Compare digests to see if image was updated
393
+ if (digestBefore && digestAfter && digestBefore === digestAfter) {
394
+ spinner.succeed("Nursery image is already up-to-date!");
395
+ console.log(
396
+ chalk.gray("\nYou're running the latest version."),
397
+ );
398
+ } else if (digestAfter) {
399
+ spinner.succeed("Latest nursery image downloaded!");
400
+ console.log(
401
+ chalk.gray("\nRun"),
402
+ chalk.cyan("crux nursery restart"),
403
+ chalk.gray("to use the new image."),
404
+ );
405
+ } else {
406
+ spinner.succeed("Images pulled successfully!");
407
+ console.log(
408
+ chalk.gray("\nRun"),
409
+ chalk.cyan("crux nursery restart"),
410
+ chalk.gray("to use the latest images."),
411
+ );
412
+ }
413
+ } catch (error) {
414
+ spinner.fail("Failed to update images");
415
+ throw error;
416
+ }
333
417
  console.log();
334
418
  }
335
419
 
336
420
  export async function resetNursery() {
421
+ ensureDockerRunning();
337
422
  console.log(
338
423
  chalk.yellow(
339
424
  "\n⚠️ This will delete all nursery data and start fresh with the latest image!\n",
@@ -406,12 +491,14 @@ export async function restartNursery() {
406
491
  }
407
492
 
408
493
  export async function statusNursery() {
494
+ ensureDockerRunning();
409
495
  console.log(chalk.bold("\nCrux Garden Nursery Environment Status:\n"));
410
496
  runCommand("docker-compose -f docker-compose.nursery.yml ps");
411
497
  console.log();
412
498
  }
413
499
 
414
500
  export async function connectNurseryDb() {
501
+ ensureDockerRunning();
415
502
  console.log(chalk.gray("Connecting to Nursery PostgreSQL...\n"));
416
503
  runCommand(
417
504
  "docker exec -it cruxgarden-nursery-postgres psql -U cruxgarden -d cruxgarden",
@@ -419,16 +506,19 @@ export async function connectNurseryDb() {
419
506
  }
420
507
 
421
508
  export async function connectNurseryRedis() {
509
+ ensureDockerRunning();
422
510
  console.log(chalk.gray("Connecting to Nursery Redis...\n"));
423
511
  runCommand("docker exec -it cruxgarden-nursery-redis redis-cli");
424
512
  }
425
513
 
426
514
  export async function connectNurseryApi() {
515
+ ensureDockerRunning();
427
516
  console.log(chalk.gray("Opening shell in Nursery API container...\n"));
428
517
  runCommand("docker exec -it cruxgarden-nursery-api sh");
429
518
  }
430
519
 
431
520
  export async function stopNurseryDb() {
521
+ ensureDockerRunning();
432
522
  const spinner = ora("Stopping nursery database services...").start();
433
523
  await runCommandAsync(
434
524
  "docker-compose -f docker-compose.nursery.yml stop postgres redis",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxgarden/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Crux Garden CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,10 @@
19
19
  "docker:nursery:db:stop": "cd docker && docker-compose -f docker-compose.nursery.yml stop postgres redis",
20
20
  "docker:nursery:redis:connect": "docker exec -it cruxgarden-nursery-redis redis-cli",
21
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"
22
+ "docker:nursery:api:connect": "docker exec -it cruxgarden-nursery-api sh",
23
+ "publish:patch": "npm version patch && npm publish && git push && git push --tags",
24
+ "publish:minor": "npm version minor && npm publish && git push && git push --tags",
25
+ "publish:major": "npm version major && npm publish && git push && git push --tags"
23
26
  },
24
27
  "engines": {
25
28
  "node": ">=18.0.0"