@cruxgarden/cli 0.0.8 → 0.0.10

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 CHANGED
@@ -57,7 +57,7 @@ Start the Nursery environment:
57
57
  crux nursery start
58
58
  ```
59
59
 
60
- The API will be available at `http://localhost:3000` with demo data loaded.
60
+ The App will be available at `http://localhost:8080` and the API at `http://localhost:3000` with demo data loaded.
61
61
 
62
62
  View logs:
63
63
 
@@ -77,15 +77,26 @@ All commands are scoped under `crux nursery`:
77
77
 
78
78
  ### `crux nursery start`
79
79
 
80
- Start the Nursery environment (PostgreSQL, Redis, Migrations, and API with demo data).
80
+ Start the Nursery environment (App, API, PostgreSQL, Redis with demo data).
81
81
 
82
82
  ```bash
83
83
  crux nursery start
84
+
85
+ # With inline environment variables
86
+ crux nursery start API_PORT=3001 APP_PORT=8081
87
+
88
+ # With options and environment variables
89
+ crux nursery start --api-only JWT_SECRET=my-secret
84
90
  ```
85
91
 
86
92
  **Options:**
87
93
 
88
94
  - `--db-only` - Start only database services (PostgreSQL and Redis)
95
+ - `--api-only` - Start only API services (API, PostgreSQL, Redis) without the App
96
+
97
+ **Environment Variables:**
98
+
99
+ You can pass environment variables directly on the command line in `KEY=VALUE` format after the command and options. These override values from your `.env` file.
89
100
 
90
101
  ### `crux nursery stop`
91
102
 
@@ -103,6 +114,11 @@ Restart the Nursery environment.
103
114
  crux nursery restart
104
115
  ```
105
116
 
117
+ **Options:**
118
+
119
+ - `--db-only` - Restart only database services (PostgreSQL and Redis)
120
+ - `--api-only` - Restart only API services (API, PostgreSQL, Redis) without the App
121
+
106
122
  ### `crux nursery status`
107
123
 
108
124
  Show the status of all Nursery services.
@@ -198,6 +214,14 @@ Connect to Nursery Redis with `redis-cli`.
198
214
  crux nursery redis connect
199
215
  ```
200
216
 
217
+ ### `crux nursery api start`
218
+
219
+ Start only Nursery API services (API, PostgreSQL, Redis) without the App.
220
+
221
+ ```bash
222
+ crux nursery api start
223
+ ```
224
+
201
225
  ### `crux nursery api connect`
202
226
 
203
227
  Open a shell in the Nursery API container.
@@ -217,31 +241,81 @@ The Nursery is a production-like demo environment that:
217
241
 
218
242
  **Services:**
219
243
 
244
+ - **App**: `http://localhost:8080` - Crux Garden Web Application
220
245
  - **API**: `http://localhost:3000` - Crux Garden API (published image with demo data)
221
246
  - **PostgreSQL**: `localhost:5432` - Database
222
247
  - **Redis**: `localhost:6379` - Cache
223
248
 
224
249
  ## Environment Variables
225
250
 
226
- The Nursery environment has defaults for all environment variables, so a `.env` file is optional. However, you can override any variable by creating a `.env` file in your working directory:
251
+ The Nursery environment has defaults for all environment variables, so configuration is optional. You can override variables in two ways:
252
+
253
+ ### 1. Using a `.env` file
254
+
255
+ Create a `.env` file in your working directory:
227
256
 
228
257
  ```bash
229
- # JWT (has dev default, but you should use a different one)
258
+ # Port Mappings
259
+ API_PORT=3000 # External API port
260
+ APP_PORT=8080 # External App port
261
+ POSTGRES_PORT=5432 # External PostgreSQL port
262
+ REDIS_PORT=6379 # External Redis port
263
+
264
+ # PostgreSQL Configuration
265
+ POSTGRES_DB=cruxgarden
266
+ POSTGRES_USER=cruxgarden
267
+ POSTGRES_PASSWORD=cruxgarden_nursery_password
268
+
269
+ # Database & Cache URLs (override to use external services)
270
+ DATABASE_URL=postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden
271
+ REDIS_URL=redis://redis:6379
272
+
273
+ # Security (has dev default, but you should use a different one)
230
274
  JWT_SECRET=your-super-secret-jwt-key-min-32-chars
231
275
 
232
- # AWS (defaults to "dummy" values)
276
+ # AWS Configuration (defaults to "dummy" values)
233
277
  AWS_ACCESS_KEY_ID=your-key
234
278
  AWS_SECRET_ACCESS_KEY=your-secret
235
279
  AWS_REGION=us-east-1
236
280
  AWS_SES_FROM_EMAIL=demo@example.com
237
281
  AWS_S3_ATTACHMENTS_BUCKET=crux-garden-attachments
238
282
 
239
- # Optional overrides
283
+ # Optional Configuration
284
+ NODE_ENV=production
240
285
  CORS_ORIGIN=*
241
286
  LOG_LEVEL=info
242
- PORT=3000
287
+ HOSTNAME=0.0.0.0
288
+ DB_POOL_MIN=2
289
+ DB_POOL_MAX=10
290
+ DB_POOL_IDLE_TIMEOUT=30000
291
+ DB_POOL_ACQUIRE_TIMEOUT=60000
292
+ RATE_LIMIT_TTL=60000
293
+ RATE_LIMIT_MAX=100
243
294
  ```
244
295
 
296
+ ### 2. Using inline environment variables
297
+
298
+ Pass environment variables directly on the command line:
299
+
300
+ ```bash
301
+ # Override ports
302
+ crux nursery start API_PORT=3001 APP_PORT=8081
303
+
304
+ # Override database connection
305
+ crux nursery start DATABASE_URL=postgresql://user:pass@external-host:5432/db
306
+
307
+ # Multiple variables
308
+ crux nursery start API_PORT=3001 JWT_SECRET=my-secret AWS_REGION=us-west-2
309
+
310
+ # Works with all start commands
311
+ crux nursery api start API_PORT=4000
312
+ crux nursery db start POSTGRES_PORT=5433
313
+ crux nursery restart API_PORT=3001
314
+ crux nursery reset JWT_SECRET=new-secret
315
+ ```
316
+
317
+ **Note:** Inline environment variables override values from your `.env` file. You can override `DATABASE_URL` and `REDIS_URL` to connect the API to external database/cache services instead of the bundled ones.
318
+
245
319
  ## Common Workflows
246
320
 
247
321
  ### Demo/Trial Setup
@@ -249,10 +323,11 @@ PORT=3000
249
323
  Use the Nursery environment for demos, trials, or showcasing features:
250
324
 
251
325
  ```bash
252
- # First time setup - pulls image and starts with demo data
326
+ # First time setup - pulls images and starts with demo data
253
327
  crux nursery start
254
328
 
255
- # View the demo at http://localhost:3000
329
+ # View the app at http://localhost:8080
330
+ # Or access the API directly at http://localhost:3000
256
331
 
257
332
  # Stop (keeps data for next demo)
258
333
  crux nursery stop
@@ -304,6 +379,7 @@ If you get an error about ports being in use, stop any existing services:
304
379
 
305
380
  ```bash
306
381
  # Check what's using the ports
382
+ lsof -i :8080 # Nursery App
307
383
  lsof -i :3000 # Nursery API
308
384
  lsof -i :5432 # Nursery PostgreSQL
309
385
  lsof -i :6379 # Nursery Redis
package/bin/crux.js CHANGED
@@ -37,13 +37,14 @@ const nursery = program
37
37
  .description("Manage the Nursery environment (demo/trial environment)");
38
38
 
39
39
  nursery
40
- .command("start")
41
- .description("Start the Nursery environment (api, postgres, redis)")
40
+ .command("start [env...]")
41
+ .description("Start the Nursery environment (app, api, postgres, redis)")
42
42
  .option("--db-only", "Start only database services (postgres, redis)")
43
+ .option("--api-only", "Start only API services (api, postgres, redis) without app")
43
44
  .option("--no-banner", "Hide the startup banner")
44
- .action((options) => {
45
+ .action((envVars, options) => {
45
46
  if (!options.noBanner) showBanner();
46
- startNursery(options);
47
+ startNursery(options, envVars);
47
48
  });
48
49
 
49
50
  nursery
@@ -52,9 +53,11 @@ nursery
52
53
  .action(stopNursery);
53
54
 
54
55
  nursery
55
- .command("restart")
56
+ .command("restart [env...]")
56
57
  .description("Restart the Nursery environment")
57
- .action(restartNursery);
58
+ .option("--db-only", "Restart only database services (postgres, redis)")
59
+ .option("--api-only", "Restart only API services (api, postgres, redis) without app")
60
+ .action((envVars, options) => restartNursery(options, envVars));
58
61
 
59
62
  nursery
60
63
  .command("status")
@@ -85,7 +88,7 @@ nursery
85
88
  .action(updateNursery);
86
89
 
87
90
  nursery
88
- .command("reset")
91
+ .command("reset [env...]")
89
92
  .description("Complete fresh reset (stop, clean, pull latest image, restart)")
90
93
  .action(resetNursery);
91
94
 
@@ -95,9 +98,9 @@ const nurseryDb = nursery
95
98
  .description("Manage Nursery database services");
96
99
 
97
100
  nurseryDb
98
- .command("start")
101
+ .command("start [env...]")
99
102
  .description("Start only Nursery database services (postgres, redis)")
100
- .action(() => startNursery({ dbOnly: true }));
103
+ .action((envVars) => startNursery({ dbOnly: true }, envVars));
101
104
 
102
105
  nurseryDb
103
106
  .command("stop")
@@ -122,6 +125,11 @@ nurseryRedis
122
125
  // Nursery API commands
123
126
  const nurseryApi = nursery.command("api").description("Manage Nursery API");
124
127
 
128
+ nurseryApi
129
+ .command("start [env...]")
130
+ .description("Start only Nursery API services (api, postgres, redis) without app")
131
+ .action((envVars) => startNursery({ apiOnly: true }, envVars));
132
+
125
133
  nurseryApi
126
134
  .command("connect")
127
135
  .description("Open a shell in the Nursery API container")
@@ -12,15 +12,15 @@ services:
12
12
  image: postgres:16-alpine
13
13
  container_name: cruxgarden-nursery-postgres
14
14
  environment:
15
- POSTGRES_DB: cruxgarden
16
- POSTGRES_USER: cruxgarden
17
- POSTGRES_PASSWORD: cruxgarden_nursery_password
15
+ POSTGRES_DB: ${POSTGRES_DB:-cruxgarden}
16
+ POSTGRES_USER: ${POSTGRES_USER:-cruxgarden}
17
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cruxgarden_nursery_password}
18
18
  ports:
19
- - "5432:5432"
19
+ - "${POSTGRES_PORT:-5432}:5432"
20
20
  volumes:
21
21
  - postgres_data_nursery:/var/lib/postgresql/data
22
22
  healthcheck:
23
- test: ["CMD-SHELL", "pg_isready -U cruxgarden"]
23
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-cruxgarden}"]
24
24
  interval: 5s
25
25
  timeout: 5s
26
26
  retries: 5
@@ -31,7 +31,7 @@ services:
31
31
  container_name: cruxgarden-nursery-redis
32
32
  command: redis-server --appendonly yes
33
33
  ports:
34
- - "6379:6379"
34
+ - "${REDIS_PORT:-6379}:6379"
35
35
  volumes:
36
36
  - redis_data_nursery:/data
37
37
  healthcheck:
@@ -48,8 +48,8 @@ services:
48
48
  postgres:
49
49
  condition: service_healthy
50
50
  environment:
51
- NODE_ENV: production
52
- DATABASE_URL: postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden
51
+ NODE_ENV: ${NODE_ENV:-production}
52
+ DATABASE_URL: ${DATABASE_URL:-postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden}
53
53
  command: ["npm", "run", "migrate:nursery"]
54
54
  restart: "no"
55
55
 
@@ -64,16 +64,16 @@ services:
64
64
  redis:
65
65
  condition: service_healthy
66
66
  ports:
67
- - "${PORT:-3000}:3000"
67
+ - "${API_PORT:-3000}:3000"
68
68
  environment:
69
69
  # Application
70
- NODE_ENV: production
70
+ NODE_ENV: ${NODE_ENV:-production}
71
71
  PORT: 3000
72
- HOSTNAME: 0.0.0.0
72
+ HOSTNAME: ${HOSTNAME:-0.0.0.0}
73
73
 
74
- # Bundled Services
75
- DATABASE_URL: postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden
76
- REDIS_URL: redis://redis:6379
74
+ # Bundled Services (can override to use external services)
75
+ DATABASE_URL: ${DATABASE_URL:-postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden}
76
+ REDIS_URL: ${REDIS_URL:-redis://redis:6379}
77
77
 
78
78
  # Security
79
79
  JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-min-32-chars-for-development-only}
@@ -107,6 +107,17 @@ services:
107
107
  retries: 3
108
108
  start_period: 40s
109
109
 
110
+ # Crux Garden App - Web Application
111
+ app:
112
+ image: ghcr.io/cruxgarden/app:latest
113
+ container_name: cruxgarden-nursery-app
114
+ restart: unless-stopped
115
+ depends_on:
116
+ api:
117
+ condition: service_healthy
118
+ ports:
119
+ - "${APP_PORT:-8080}:80"
120
+
110
121
  volumes:
111
122
  postgres_data_nursery:
112
123
  redis_data_nursery:
package/lib/commands.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { execSync, spawn } from "child_process";
2
2
  import { fileURLToPath } from "url";
3
3
  import { dirname, join } from "path";
4
+ import { readFileSync, existsSync } from "fs";
4
5
  import readline from "readline";
5
6
  import chalk from "chalk";
6
7
  import ora from "ora";
@@ -9,6 +10,12 @@ const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = dirname(__filename);
10
11
  const dockerDir = join(__dirname, "..", "docker");
11
12
 
13
+ // Read version from package.json
14
+ const packageJson = JSON.parse(
15
+ readFileSync(join(__dirname, "..", "package.json"), "utf-8")
16
+ );
17
+ const VERSION = packageJson.version;
18
+
12
19
  // Colors
13
20
  const SUCCESS_GREEN = "#9BD39B";
14
21
 
@@ -50,7 +57,7 @@ export function showBanner() {
50
57
  console.log(randomColor(line));
51
58
  });
52
59
 
53
- console.log(chalk.gray(" Nursery Environment - Demo & Trial\n"));
60
+ console.log(chalk.gray(` Nursery Environment - Demo & Trial (v${VERSION})\n`));
54
61
 
55
62
  // Set environment variable so it persists for this shell session
56
63
  process.env.CRUX_BANNER_SHOWN = "1";
@@ -159,35 +166,110 @@ function ensureDockerRunning() {
159
166
  }
160
167
  }
161
168
 
169
+ function getEnvFileFlag() {
170
+ const envFilePath = join(process.cwd(), ".env");
171
+ if (existsSync(envFilePath)) {
172
+ return `--env-file "${envFilePath}"`;
173
+ }
174
+ return "";
175
+ }
176
+
177
+ function parseAndSetEnvVars(envArgs = []) {
178
+ if (!envArgs || envArgs.length === 0) return;
179
+
180
+ for (const arg of envArgs) {
181
+ const match = arg.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
182
+ if (match) {
183
+ const [, key, value] = match;
184
+ process.env[key] = value;
185
+ console.log(chalk.gray(` Setting ${key}=${value}`));
186
+ } else {
187
+ console.log(chalk.yellow(` Warning: Ignoring invalid env var format: ${arg}`));
188
+ }
189
+ }
190
+ }
191
+
162
192
  // ============================================================================
163
193
  // Nursery Environment Commands
164
194
  // ============================================================================
165
195
 
166
- export async function startNursery(options) {
196
+ export async function startNursery(options, envVars) {
167
197
  ensureDockerRunning();
198
+
199
+ // Parse and set environment variables from command line
200
+ if (envVars && envVars.length > 0) {
201
+ console.log(chalk.cyan("\nApplying environment variables:"));
202
+ parseAndSetEnvVars(envVars);
203
+ console.log();
204
+ }
205
+
168
206
  const spinner = ora("Starting Crux Garden Nursery environment...").start();
207
+ const envFileFlag = getEnvFileFlag();
208
+
209
+ // Get actual port values from environment
210
+ const appPort = process.env.APP_PORT || "8080";
211
+ const apiPort = process.env.API_PORT || "3000";
212
+ const postgresPort = process.env.POSTGRES_PORT || "5432";
213
+ const redisPort = process.env.REDIS_PORT || "6379";
169
214
 
170
215
  try {
171
216
  if (options.dbOnly) {
172
217
  spinner.text = "Starting nursery database services (postgres, redis)...";
173
218
  await runCommandAsync(
174
- "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans postgres redis",
219
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml up -d --remove-orphans postgres redis`,
175
220
  { silent: true },
176
221
  );
177
222
  spinner.succeed("Nursery database services started!");
178
223
  console.log(
179
224
  chalk.hex(SUCCESS_GREEN)("\n✓ PostgreSQL running on:"),
180
- "localhost:5432",
225
+ `localhost:${postgresPort}`,
181
226
  );
182
227
  console.log(
183
228
  chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
184
- "localhost:6379",
229
+ `localhost:${redisPort}`,
230
+ );
231
+ } else if (options.apiOnly) {
232
+ spinner.text =
233
+ "Starting nursery API services (api, postgres, redis)...";
234
+ await runCommandAsync(
235
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml up -d --remove-orphans postgres redis migrations api`,
236
+ {
237
+ silent: true,
238
+ },
239
+ );
240
+
241
+ // Clean up the migrations container if it exists (cross-platform)
242
+ await runCommandAsync(
243
+ "docker rm cruxgarden-nursery-migrations",
244
+ {
245
+ silent: true,
246
+ ignoreError: true,
247
+ },
248
+ );
249
+
250
+ spinner.succeed("Crux Garden Nursery API services started!");
251
+ console.log(
252
+ chalk.hex(SUCCESS_GREEN)("\n✓ API running on:"),
253
+ `http://localhost:${apiPort}`,
254
+ );
255
+ console.log(
256
+ chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"),
257
+ `localhost:${postgresPort}`,
258
+ );
259
+ console.log(
260
+ chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
261
+ `localhost:${redisPort}`,
262
+ );
263
+ console.log(
264
+ chalk.yellow(
265
+ "\nℹ Nursery includes demo data for trials and showcases",
266
+ ),
185
267
  );
186
268
  } else {
187
269
  spinner.text =
188
- "Starting nursery services (api, postgres, redis)...";
270
+ "Starting nursery services (app, api, postgres, redis)...";
189
271
  await runCommandAsync(
190
- "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans",
272
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml up -d --remove-orphans`,
191
273
  {
192
274
  silent: true,
193
275
  },
@@ -204,16 +286,20 @@ export async function startNursery(options) {
204
286
 
205
287
  spinner.succeed("Crux Garden Nursery environment started!");
206
288
  console.log(
207
- chalk.hex(SUCCESS_GREEN)("\n✓ API running on:"),
208
- "http://localhost:3000",
289
+ chalk.hex(SUCCESS_GREEN)("\n✓ App running on:"),
290
+ `http://localhost:${appPort}`,
291
+ );
292
+ console.log(
293
+ chalk.hex(SUCCESS_GREEN)("✓ API running on:"),
294
+ `http://localhost:${apiPort}`,
209
295
  );
210
296
  console.log(
211
297
  chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"),
212
- "localhost:5432",
298
+ `localhost:${postgresPort}`,
213
299
  );
214
300
  console.log(
215
301
  chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
216
- "localhost:6379",
302
+ `localhost:${redisPort}`,
217
303
  );
218
304
  console.log(
219
305
  chalk.yellow(
@@ -252,7 +338,8 @@ export async function startNursery(options) {
252
338
  export async function stopNursery() {
253
339
  ensureDockerRunning();
254
340
  const spinner = ora("Stopping Crux Garden Nursery environment...").start();
255
- await runCommandAsync("docker-compose -f docker-compose.nursery.yml down", {
341
+ const envFileFlag = getEnvFileFlag();
342
+ await runCommandAsync(`docker-compose ${envFileFlag} -f docker-compose.nursery.yml down`, {
256
343
  silent: true,
257
344
  });
258
345
  spinner.succeed("Crux Garden Nursery environment stopped!");
@@ -266,13 +353,16 @@ export async function stopNursery() {
266
353
 
267
354
  export async function logsNursery(options) {
268
355
  ensureDockerRunning();
356
+ const envFileFlag = getEnvFileFlag();
357
+ const envFileArgs = envFileFlag ? envFileFlag.split(" ") : [];
358
+
269
359
  if (options.follow) {
270
360
  console.log(
271
361
  chalk.gray("Following nursery logs (press Ctrl+C to exit)...\n"),
272
362
  );
273
363
  const child = spawn(
274
364
  "docker-compose",
275
- ["-f", "docker-compose.nursery.yml", "logs", "-f"],
365
+ [...envFileArgs, "-f", "docker-compose.nursery.yml", "logs", "-f"],
276
366
  {
277
367
  cwd: dockerDir,
278
368
  stdio: "inherit",
@@ -284,7 +374,7 @@ export async function logsNursery(options) {
284
374
  process.exit(0);
285
375
  });
286
376
  } else {
287
- runCommand("docker-compose -f docker-compose.nursery.yml logs --tail=100");
377
+ runCommand(`docker-compose ${envFileFlag} -f docker-compose.nursery.yml logs --tail=100`);
288
378
  console.log();
289
379
  }
290
380
  }
@@ -309,9 +399,10 @@ export async function cleanNursery() {
309
399
  const spinner = ora(
310
400
  "Cleaning up nursery (removing containers and volumes)...",
311
401
  ).start();
402
+ const envFileFlag = getEnvFileFlag();
312
403
 
313
404
  await runCommandAsync(
314
- "docker-compose -f docker-compose.nursery.yml down -v",
405
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml down -v`,
315
406
  {
316
407
  silent: true,
317
408
  ignoreError: true,
@@ -352,9 +443,10 @@ export async function purgeNursery() {
352
443
  const spinner = ora(
353
444
  "Purging nursery (removing containers, volumes, and images)...",
354
445
  ).start();
446
+ const envFileFlag = getEnvFileFlag();
355
447
 
356
448
  await runCommandAsync(
357
- "docker-compose -f docker-compose.nursery.yml down -v --rmi all",
449
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml down -v --rmi all`,
358
450
  {
359
451
  silent: true,
360
452
  ignoreError: true,
@@ -376,6 +468,7 @@ export async function updateNursery() {
376
468
  const spinner = ora(
377
469
  "Checking for latest Crux Garden API image from ghcr.io...",
378
470
  ).start();
471
+ const envFileFlag = getEnvFileFlag();
379
472
 
380
473
  try {
381
474
  // Get the current local image digest before pulling
@@ -387,7 +480,7 @@ export async function updateNursery() {
387
480
 
388
481
  // Pull latest images
389
482
  spinner.text = "Pulling latest images...";
390
- await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
483
+ await runCommandAsync(`docker-compose ${envFileFlag} -f docker-compose.nursery.yml pull`, {
391
484
  silent: true,
392
485
  });
393
486
 
@@ -426,7 +519,7 @@ export async function updateNursery() {
426
519
  console.log();
427
520
  }
428
521
 
429
- export async function resetNursery() {
522
+ export async function resetNursery(envVars) {
430
523
  ensureDockerRunning();
431
524
  console.log(
432
525
  chalk.yellow(
@@ -443,13 +536,27 @@ export async function resetNursery() {
443
536
  return;
444
537
  }
445
538
 
539
+ // Parse and set environment variables from command line
540
+ if (envVars && envVars.length > 0) {
541
+ console.log(chalk.cyan("\nApplying environment variables:"));
542
+ parseAndSetEnvVars(envVars);
543
+ console.log();
544
+ }
545
+
446
546
  const spinner = ora("Resetting nursery environment...").start();
547
+ const envFileFlag = getEnvFileFlag();
548
+
549
+ // Get actual port values from environment
550
+ const appPort = process.env.APP_PORT || "8080";
551
+ const apiPort = process.env.API_PORT || "3000";
552
+ const postgresPort = process.env.POSTGRES_PORT || "5432";
553
+ const redisPort = process.env.REDIS_PORT || "6379";
447
554
 
448
555
  try {
449
556
  // Stop and remove volumes
450
557
  spinner.text = "Stopping and removing containers/volumes...";
451
558
  await runCommandAsync(
452
- "docker-compose -f docker-compose.nursery.yml down -v",
559
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml down -v`,
453
560
  {
454
561
  silent: true,
455
562
  ignoreError: true,
@@ -458,14 +565,14 @@ export async function resetNursery() {
458
565
 
459
566
  // Pull latest image
460
567
  spinner.text = "Pulling latest image...";
461
- await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
568
+ await runCommandAsync(`docker-compose ${envFileFlag} -f docker-compose.nursery.yml pull`, {
462
569
  silent: true,
463
570
  });
464
571
 
465
572
  // Start fresh
466
573
  spinner.text = "Starting fresh nursery environment...";
467
574
  await runCommandAsync(
468
- "docker-compose -f docker-compose.nursery.yml up -d --remove-orphans",
575
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml up -d --remove-orphans`,
469
576
  {
470
577
  silent: true,
471
578
  },
@@ -482,16 +589,20 @@ export async function resetNursery() {
482
589
 
483
590
  spinner.succeed("Nursery environment reset complete!");
484
591
  console.log(
485
- chalk.hex(SUCCESS_GREEN)("\n✓ API running on:"),
486
- "http://localhost:3000",
592
+ chalk.hex(SUCCESS_GREEN)("\n✓ App running on:"),
593
+ `http://localhost:${appPort}`,
594
+ );
595
+ console.log(
596
+ chalk.hex(SUCCESS_GREEN)("✓ API running on:"),
597
+ `http://localhost:${apiPort}`,
487
598
  );
488
599
  console.log(
489
600
  chalk.hex(SUCCESS_GREEN)("✓ PostgreSQL running on:"),
490
- "localhost:5432",
601
+ `localhost:${postgresPort}`,
491
602
  );
492
603
  console.log(
493
604
  chalk.hex(SUCCESS_GREEN)("✓ Redis running on:"),
494
- "localhost:6379",
605
+ `localhost:${redisPort}`,
495
606
  );
496
607
  console.log(
497
608
  chalk.yellow("\nℹ Fresh nursery with latest demo data loaded"),
@@ -503,15 +614,16 @@ export async function resetNursery() {
503
614
  }
504
615
  }
505
616
 
506
- export async function restartNursery() {
617
+ export async function restartNursery(options = {}, envVars) {
507
618
  await stopNursery();
508
- await startNursery({});
619
+ await startNursery(options, envVars);
509
620
  }
510
621
 
511
622
  export async function statusNursery() {
512
623
  ensureDockerRunning();
624
+ const envFileFlag = getEnvFileFlag();
513
625
  console.log(chalk.bold("\nCrux Garden Nursery Environment Status:\n"));
514
- runCommand("docker-compose -f docker-compose.nursery.yml ps");
626
+ runCommand(`docker-compose ${envFileFlag} -f docker-compose.nursery.yml ps`);
515
627
  console.log();
516
628
  }
517
629
 
@@ -538,8 +650,9 @@ export async function connectNurseryApi() {
538
650
  export async function stopNurseryDb() {
539
651
  ensureDockerRunning();
540
652
  const spinner = ora("Stopping nursery database services...").start();
653
+ const envFileFlag = getEnvFileFlag();
541
654
  await runCommandAsync(
542
- "docker-compose -f docker-compose.nursery.yml stop postgres redis",
655
+ `docker-compose ${envFileFlag} -f docker-compose.nursery.yml stop postgres redis`,
543
656
  { silent: true },
544
657
  );
545
658
  spinner.succeed("Nursery database services stopped!");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxgarden/cli",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Crux Garden CLI",
5
5
  "type": "module",
6
6
  "bin": {