@cruxgarden/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 22.13.1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Crux Garden
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,391 @@
1
+ # Crux Garden CLI
2
+
3
+ CLI tool to manage the Crux Garden Nursery environment with Docker.
4
+
5
+ The **Nursery** is a production-like demo environment with sample data, perfect for trials, demos, and showcasing features.
6
+
7
+ ## Prerequisites
8
+
9
+ - [Docker](https://docs.docker.com/get-docker/) installed and running
10
+ - [Node.js](https://nodejs.org/) 18 or higher
11
+
12
+ ## Installation
13
+
14
+ ### Install from npm
15
+
16
+ Install globally with npm:
17
+
18
+ ```bash
19
+ npm install -g @cruxgarden/cli
20
+ ```
21
+
22
+ Or use with npx (no installation required):
23
+
24
+ ```bash
25
+ npx @cruxgarden/cli nursery start
26
+ ```
27
+
28
+ ### Local Development
29
+
30
+ To develop or test the CLI locally:
31
+
32
+ ```bash
33
+ # Clone the repository
34
+ git clone https://github.com/CruxGarden/cli.git
35
+ cd cli
36
+
37
+ # Install dependencies
38
+ npm install
39
+
40
+ # Link the CLI globally for testing
41
+ npm link
42
+
43
+ # Now you can use the `crux` command
44
+ crux --help
45
+
46
+ # When done, unlink
47
+ npm unlink -g @cruxgarden/cli
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ Start the Nursery environment:
53
+
54
+ ```bash
55
+ crux nursery start
56
+ ```
57
+
58
+ The API will be available at `http://localhost:3001` with demo data loaded.
59
+
60
+ View logs:
61
+
62
+ ```bash
63
+ crux nursery logs
64
+ ```
65
+
66
+ Stop the environment:
67
+
68
+ ```bash
69
+ crux nursery stop
70
+ ```
71
+
72
+ ## Commands
73
+
74
+ All commands are scoped under `crux nursery`:
75
+
76
+ ### `crux nursery start`
77
+
78
+ Start the Nursery environment (PostgreSQL, Redis, Migrations, and API with demo data).
79
+
80
+ ```bash
81
+ crux nursery start
82
+ ```
83
+
84
+ **Options:**
85
+
86
+ - `--db-only` - Start only database services (PostgreSQL and Redis)
87
+
88
+ ### `crux nursery stop`
89
+
90
+ Stop the Nursery environment. Data is preserved.
91
+
92
+ ```bash
93
+ crux nursery stop
94
+ ```
95
+
96
+ ### `crux nursery restart`
97
+
98
+ Restart the Nursery environment.
99
+
100
+ ```bash
101
+ crux nursery restart
102
+ ```
103
+
104
+ ### `crux nursery status`
105
+
106
+ Show the status of all Nursery services.
107
+
108
+ ```bash
109
+ crux nursery status
110
+ ```
111
+
112
+ ### `crux nursery logs`
113
+
114
+ View logs from all Nursery services.
115
+
116
+ ```bash
117
+ crux nursery logs
118
+
119
+ # Follow logs (like tail -f)
120
+ crux nursery logs -f
121
+ ```
122
+
123
+ ### `crux nursery pull`
124
+
125
+ Pull the latest API image from GitHub Container Registry.
126
+
127
+ ```bash
128
+ crux nursery pull
129
+ ```
130
+
131
+ ### `crux nursery reset`
132
+
133
+ Complete fresh reset: stops everything, deletes all data and volumes, pulls the latest image, and starts fresh. **Warning: This deletes all data!**
134
+
135
+ ```bash
136
+ crux nursery reset
137
+ ```
138
+
139
+ ### `crux nursery clean`
140
+
141
+ Stop and remove all Nursery containers and volumes. **Warning: This deletes all data!**
142
+
143
+ ```bash
144
+ crux nursery clean
145
+ ```
146
+
147
+ ### `crux nursery db start`
148
+
149
+ Start only Nursery database services (PostgreSQL and Redis).
150
+
151
+ ```bash
152
+ crux nursery db start
153
+ ```
154
+
155
+ ### `crux nursery db stop`
156
+
157
+ Stop Nursery database services.
158
+
159
+ ```bash
160
+ crux nursery db stop
161
+ ```
162
+
163
+ ### `crux nursery db connect`
164
+
165
+ Connect to the Nursery PostgreSQL database with `psql`.
166
+
167
+ ```bash
168
+ crux nursery db connect
169
+ ```
170
+
171
+ Useful psql commands:
172
+
173
+ - `\dt` - List all tables
174
+ - `\d table_name` - Describe a table
175
+ - `\q` - Quit
176
+
177
+ ### `crux nursery redis connect`
178
+
179
+ Connect to Nursery Redis with `redis-cli`.
180
+
181
+ ```bash
182
+ crux nursery redis connect
183
+ ```
184
+
185
+ ### `crux nursery api connect`
186
+
187
+ Open a shell in the Nursery API container.
188
+
189
+ ```bash
190
+ crux nursery api connect
191
+ ```
192
+
193
+ ## What is the Nursery?
194
+
195
+ The Nursery is a production-like demo environment that:
196
+
197
+ - **Uses the published Docker image** from `ghcr.io/cruxgarden/api:latest`
198
+ - **Includes demo data** - Sample cruxes, paths, and relationships for showcasing
199
+ - **Runs standalone** - Bundled PostgreSQL and Redis, no external dependencies
200
+ - **Perfect for demos** - Show features to stakeholders, QA testing, trials
201
+
202
+ **Services:**
203
+
204
+ - **API**: `http://localhost:3001` - Crux Garden API (published image with demo data)
205
+ - **PostgreSQL**: `localhost:5433` - Database
206
+ - **Redis**: `localhost:6380` - Cache
207
+
208
+ ## Environment Variables
209
+
210
+ 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:
211
+
212
+ ```bash
213
+ # JWT (has dev default, but you should use a different one)
214
+ JWT_SECRET=your-super-secret-jwt-key-min-32-chars
215
+
216
+ # AWS (defaults to "dummy" values)
217
+ AWS_ACCESS_KEY_ID=your-key
218
+ AWS_SECRET_ACCESS_KEY=your-secret
219
+ AWS_REGION=us-east-1
220
+ FROM_EMAIL_ADDRESS=demo@example.com
221
+
222
+ # Optional overrides
223
+ CORS_ORIGIN=*
224
+ LOG_LEVEL=info
225
+ PORT=3001
226
+ ```
227
+
228
+ ## Common Workflows
229
+
230
+ ### Demo/Trial Setup
231
+
232
+ Use the Nursery environment for demos, trials, or showcasing features:
233
+
234
+ ```bash
235
+ # First time setup - pulls image and starts with demo data
236
+ crux nursery start
237
+
238
+ # View the demo at http://localhost:3001
239
+
240
+ # Stop (keeps data for next demo)
241
+ crux nursery stop
242
+
243
+ # Restart for another demo
244
+ crux nursery start
245
+
246
+ # Get latest updates and fresh data
247
+ crux nursery reset
248
+ ```
249
+
250
+ ### Testing Latest Changes
251
+
252
+ Pull the latest published image and test:
253
+
254
+ ```bash
255
+ # Pull latest image
256
+ crux nursery pull
257
+
258
+ # Restart with latest image
259
+ crux nursery restart
260
+
261
+ # Or do a complete fresh reset
262
+ crux nursery reset
263
+ ```
264
+
265
+ ### Database Exploration
266
+
267
+ Connect to the database to explore the demo data:
268
+
269
+ ```bash
270
+ # Start the environment
271
+ crux nursery start
272
+
273
+ # Connect to PostgreSQL
274
+ crux nursery db connect
275
+
276
+ # In psql:
277
+ # \dt - list tables
278
+ # SELECT * FROM cruxes; - view demo cruxes
279
+ # \q - quit
280
+ ```
281
+
282
+ ## Troubleshooting
283
+
284
+ ### Port already in use
285
+
286
+ If you get an error about ports being in use, stop any existing services:
287
+
288
+ ```bash
289
+ # Check what's using the ports
290
+ lsof -i :3001 # Nursery API
291
+ lsof -i :5433 # Nursery PostgreSQL
292
+ lsof -i :6380 # Nursery Redis
293
+
294
+ # Stop the Nursery
295
+ crux nursery stop
296
+
297
+ # Or clean everything
298
+ crux nursery clean
299
+ ```
300
+
301
+ ### Containers won't start
302
+
303
+ Try cleaning and restarting:
304
+
305
+ ```bash
306
+ crux nursery clean
307
+ crux nursery start
308
+ ```
309
+
310
+ ### Database connection issues
311
+
312
+ Make sure the database is healthy:
313
+
314
+ ```bash
315
+ crux nursery status
316
+ ```
317
+
318
+ You should see `Up (healthy)` for postgres.
319
+
320
+ ### Nursery image is outdated
321
+
322
+ Pull the latest image and restart:
323
+
324
+ ```bash
325
+ crux nursery pull
326
+ crux nursery restart
327
+
328
+ # Or do a complete fresh reset
329
+ crux nursery reset
330
+ ```
331
+
332
+ ### Docker daemon not running
333
+
334
+ Make sure Docker Desktop (or Docker daemon) is running:
335
+
336
+ ```bash
337
+ docker ps
338
+ ```
339
+
340
+ If you get an error, start Docker Desktop.
341
+
342
+ ## Development Scripts
343
+
344
+ The CLI also exposes npm scripts that you can use during development:
345
+
346
+ ```bash
347
+ # Run tests
348
+ npm test
349
+
350
+ # Format code with Prettier
351
+ npm run format
352
+
353
+ # Run nursery commands directly (for testing)
354
+ npm run docker:nursery
355
+ npm run docker:nursery:logs
356
+ npm run docker:nursery:down
357
+ npm run docker:nursery:clean
358
+ npm run docker:nursery:reset
359
+ npm run docker:nursery:pull
360
+ npm run docker:nursery:db
361
+ npm run docker:nursery:db:stop
362
+ npm run docker:nursery:db:connect
363
+ npm run docker:nursery:redis:connect
364
+ npm run docker:nursery:api:connect
365
+ ```
366
+
367
+ ## Future Features
368
+
369
+ This CLI will eventually support:
370
+
371
+ - **Cloud mode** - Login and interact with the official Crux Garden API at `api.crux.garden`
372
+ - **Data operations** - Export, import, and sync data between environments
373
+ - **Multi-instance management** - Switch between local and cloud instances
374
+
375
+ ## API Development
376
+
377
+ If you're developing the Crux Garden API itself, use the npm scripts in the [API repository](https://github.com/CruxGarden/api) instead of this CLI. This CLI is specifically for running the published Nursery environment.
378
+
379
+ ## Contributing
380
+
381
+ Contributions are welcome! Please feel free to submit a Pull Request.
382
+
383
+ ## License
384
+
385
+ MIT
386
+
387
+ ## Links
388
+
389
+ - [API Repository](https://github.com/CruxGarden/api)
390
+ - [Documentation](https://github.com/CruxGarden/api#readme)
391
+ - [Issues](https://github.com/CruxGarden/cli/issues)
package/bin/crux.js ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ import { program } from "commander";
3
+ import {
4
+ showBanner,
5
+ startNursery,
6
+ stopNursery,
7
+ restartNursery,
8
+ statusNursery,
9
+ logsNursery,
10
+ cleanNursery,
11
+ pullNursery,
12
+ resetNursery,
13
+ connectNurseryDb,
14
+ connectNurseryRedis,
15
+ connectNurseryApi,
16
+ stopNurseryDb,
17
+ } from "../lib/commands.js";
18
+
19
+ program
20
+ .name("crux")
21
+ .description("Crux Garden CLI - Nursery Environment Manager")
22
+ .version("0.1.0");
23
+
24
+ // Nursery environment commands
25
+ const nursery = program
26
+ .command("nursery")
27
+ .description("Manage the Nursery environment (demo/trial environment)");
28
+
29
+ nursery
30
+ .command("start")
31
+ .description("Start the Nursery environment (postgres, redis, api)")
32
+ .option("--db-only", "Start only database services (postgres, redis)")
33
+ .option("--no-banner", "Hide the startup banner")
34
+ .action((options) => {
35
+ if (!options.noBanner) showBanner();
36
+ startNursery(options);
37
+ });
38
+
39
+ nursery
40
+ .command("stop")
41
+ .description("Stop the Nursery environment")
42
+ .action(stopNursery);
43
+
44
+ nursery
45
+ .command("restart")
46
+ .description("Restart the Nursery environment")
47
+ .action(restartNursery);
48
+
49
+ nursery
50
+ .command("status")
51
+ .description("Show status of Nursery services")
52
+ .action(statusNursery);
53
+
54
+ nursery
55
+ .command("logs")
56
+ .description("Show logs from Nursery services")
57
+ .option("-f, --follow", "Follow log output")
58
+ .action(logsNursery);
59
+
60
+ nursery
61
+ .command("clean")
62
+ .description("Stop and remove all Nursery containers and volumes")
63
+ .action(cleanNursery);
64
+
65
+ nursery
66
+ .command("pull")
67
+ .description("Pull the latest API image from ghcr.io")
68
+ .action(pullNursery);
69
+
70
+ nursery
71
+ .command("reset")
72
+ .description("Complete fresh reset (stop, clean, pull latest image, restart)")
73
+ .action(resetNursery);
74
+
75
+ // Nursery database commands
76
+ const nurseryDb = nursery
77
+ .command("db")
78
+ .description("Manage Nursery database services");
79
+
80
+ nurseryDb
81
+ .command("start")
82
+ .description("Start only Nursery database services (postgres, redis)")
83
+ .action(() => startNursery({ dbOnly: true }));
84
+
85
+ nurseryDb
86
+ .command("stop")
87
+ .description("Stop Nursery database services")
88
+ .action(stopNurseryDb);
89
+
90
+ nurseryDb
91
+ .command("connect")
92
+ .description("Connect to the Nursery PostgreSQL database")
93
+ .action(connectNurseryDb);
94
+
95
+ // Nursery Redis commands
96
+ const nurseryRedis = nursery
97
+ .command("redis")
98
+ .description("Manage Nursery Redis");
99
+
100
+ nurseryRedis
101
+ .command("connect")
102
+ .description("Connect to Nursery Redis")
103
+ .action(connectNurseryRedis);
104
+
105
+ // Nursery API commands
106
+ const nurseryApi = nursery.command("api").description("Manage Nursery API");
107
+
108
+ nurseryApi
109
+ .command("connect")
110
+ .description("Open a shell in the Nursery API container")
111
+ .action(connectNurseryApi);
112
+
113
+ program.parse();
@@ -0,0 +1,109 @@
1
+ # Nursery Environment - Standalone Configuration
2
+ # Production-like demo environment with sample data
3
+ # Uses published Docker image with bundled PostgreSQL and Redis
4
+ # Perfect for: demos, trials, showcasing features, onboarding
5
+ # Usage: npm run docker:nursery
6
+
7
+ services:
8
+ # PostgreSQL Database
9
+ postgres:
10
+ image: postgres:16-alpine
11
+ container_name: cruxgarden-postgres-nursery
12
+ environment:
13
+ POSTGRES_DB: cruxgarden
14
+ POSTGRES_USER: cruxgarden
15
+ POSTGRES_PASSWORD: cruxgarden_nursery_password
16
+ ports:
17
+ - "5433:5432"
18
+ volumes:
19
+ - postgres_data_nursery:/var/lib/postgresql/data
20
+ healthcheck:
21
+ test: ["CMD-SHELL", "pg_isready -U cruxgarden"]
22
+ interval: 5s
23
+ timeout: 5s
24
+ retries: 5
25
+
26
+ # Redis Cache
27
+ redis:
28
+ image: redis:7-alpine
29
+ container_name: cruxgarden-redis-nursery
30
+ command: redis-server --appendonly yes
31
+ ports:
32
+ - "6380:6379"
33
+ volumes:
34
+ - redis_data_nursery:/data
35
+ healthcheck:
36
+ test: ["CMD", "redis-cli", "ping"]
37
+ interval: 5s
38
+ timeout: 5s
39
+ retries: 5
40
+
41
+ # Database Migrations (runs once before API starts)
42
+ migrations:
43
+ image: ghcr.io/cruxgarden/api:latest
44
+ container_name: cruxgarden-migrations-nursery
45
+ depends_on:
46
+ postgres:
47
+ condition: service_healthy
48
+ environment:
49
+ NODE_ENV: production
50
+ DATABASE_URL: postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden
51
+ command: ["npm", "run", "migrate:nursery"]
52
+ restart: "no"
53
+
54
+ # Crux Garden API - Nursery Configuration
55
+ api:
56
+ image: ghcr.io/cruxgarden/api:latest
57
+ container_name: cruxgarden-api-nursery
58
+ restart: unless-stopped
59
+ depends_on:
60
+ migrations:
61
+ condition: service_completed_successfully
62
+ redis:
63
+ condition: service_healthy
64
+ ports:
65
+ - "${PORT:-3001}:3000"
66
+ environment:
67
+ # Application
68
+ NODE_ENV: production
69
+ PORT: 3000
70
+ HOSTNAME: 0.0.0.0
71
+
72
+ # Bundled Services
73
+ DATABASE_URL: postgresql://cruxgarden:cruxgarden_nursery_password@postgres:5432/cruxgarden
74
+ REDIS_URL: redis://redis:6379
75
+
76
+ # Security
77
+ JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-min-32-chars-for-development-only}
78
+
79
+ # AWS SES (optional for nursery)
80
+ AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-dummy}
81
+ AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-dummy}
82
+ AWS_REGION: ${AWS_REGION:-us-east-1}
83
+ FROM_EMAIL_ADDRESS: ${FROM_EMAIL_ADDRESS:-noreply@example.com}
84
+
85
+ # Optional Configuration
86
+ CORS_ORIGIN: ${CORS_ORIGIN:-*}
87
+ LOG_LEVEL: ${LOG_LEVEL:-info}
88
+ DB_POOL_MIN: ${DB_POOL_MIN:-2}
89
+ DB_POOL_MAX: ${DB_POOL_MAX:-10}
90
+ DB_POOL_IDLE_TIMEOUT: ${DB_POOL_IDLE_TIMEOUT:-30000}
91
+ DB_POOL_ACQUIRE_TIMEOUT: ${DB_POOL_ACQUIRE_TIMEOUT:-60000}
92
+ RATE_LIMIT_TTL: ${RATE_LIMIT_TTL:-60000}
93
+ RATE_LIMIT_MAX: ${RATE_LIMIT_MAX:-100}
94
+ healthcheck:
95
+ test:
96
+ [
97
+ "CMD",
98
+ "node",
99
+ "-e",
100
+ "require('http').get('http://localhost:3000/', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})",
101
+ ]
102
+ interval: 30s
103
+ timeout: 10s
104
+ retries: 3
105
+ start_period: 40s
106
+
107
+ volumes:
108
+ postgres_data_nursery:
109
+ redis_data_nursery:
@@ -0,0 +1,304 @@
1
+ import { execSync, spawn } from "child_process";
2
+ import { fileURLToPath } from "url";
3
+ import { dirname, join } from "path";
4
+ import readline from "readline";
5
+ import chalk from "chalk";
6
+ import ora from "ora";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const dockerDir = join(__dirname, "..", "docker");
11
+
12
+ // Colors
13
+ const SUCCESS_GREEN = "#9BD39B";
14
+
15
+ export function showBanner() {
16
+ // Check if banner was already shown in this shell session
17
+ if (process.env.CRUX_BANNER_SHOWN) return;
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));
36
+ console.log(chalk.gray(" Nursery Environment - Demo & Trial\n"));
37
+
38
+ // Set environment variable so it persists for this shell session
39
+ process.env.CRUX_BANNER_SHOWN = "1";
40
+ }
41
+
42
+ function runCommand(command, options = {}) {
43
+ try {
44
+ return execSync(command, {
45
+ cwd: dockerDir,
46
+ stdio: options.silent ? "pipe" : "inherit",
47
+ encoding: "utf-8",
48
+ ...options,
49
+ });
50
+ } catch (error) {
51
+ if (!options.ignoreError) {
52
+ console.error(chalk.red(`Error: ${error.message}`));
53
+ process.exit(1);
54
+ }
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function askConfirmation(question) {
60
+ return new Promise((resolve) => {
61
+ const rl = readline.createInterface({
62
+ input: process.stdin,
63
+ output: process.stdout,
64
+ });
65
+
66
+ rl.question(question, (answer) => {
67
+ rl.close();
68
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
69
+ });
70
+ });
71
+ }
72
+
73
+ // ============================================================================
74
+ // Nursery Environment Commands
75
+ // ============================================================================
76
+
77
+ export async function startNursery(options) {
78
+ const spinner = ora("Starting Crux Garden Nursery environment...").start();
79
+
80
+ try {
81
+ if (options.dbOnly) {
82
+ spinner.text = "Starting nursery database services (postgres, redis)...";
83
+ runCommand(
84
+ "docker-compose -f docker-compose.nursery.yml up -d postgres redis",
85
+ { silent: true },
86
+ );
87
+ 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");
90
+ } else {
91
+ 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
+ });
96
+ 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");
100
+ console.log(
101
+ chalk.yellow(
102
+ "\nℹ Nursery includes demo data for trials and showcases",
103
+ ),
104
+ );
105
+ }
106
+
107
+ console.log(
108
+ chalk.gray("\nRun"),
109
+ chalk.cyan("crux nursery logs"),
110
+ chalk.gray("to view logs"),
111
+ );
112
+ console.log(
113
+ chalk.gray("Run"),
114
+ chalk.cyan("crux nursery db connect"),
115
+ chalk.gray("to connect to PostgreSQL"),
116
+ );
117
+ console.log(
118
+ chalk.gray("Run"),
119
+ chalk.cyan("crux nursery redis connect"),
120
+ chalk.gray("to connect to Redis"),
121
+ );
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
+ } catch (error) {
128
+ spinner.fail("Failed to start nursery services");
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ export async function stopNursery() {
134
+ const spinner = ora("Stopping Crux Garden Nursery environment...").start();
135
+ runCommand("docker-compose -f docker-compose.nursery.yml down", {
136
+ silent: true,
137
+ });
138
+ spinner.succeed("Crux Garden Nursery environment stopped!");
139
+ console.log(
140
+ chalk.gray("\nData preserved. Run"),
141
+ chalk.cyan("crux nursery start"),
142
+ chalk.gray("to restart with existing data."),
143
+ );
144
+ }
145
+
146
+ export async function logsNursery(options) {
147
+ if (options.follow) {
148
+ console.log(
149
+ chalk.gray("Following nursery logs (press Ctrl+C to exit)...\n"),
150
+ );
151
+ const child = spawn(
152
+ "docker-compose",
153
+ ["-f", "docker-compose.nursery.yml", "logs", "-f"],
154
+ {
155
+ cwd: dockerDir,
156
+ stdio: "inherit",
157
+ },
158
+ );
159
+
160
+ process.on("SIGINT", () => {
161
+ child.kill("SIGINT");
162
+ process.exit(0);
163
+ });
164
+ } else {
165
+ runCommand("docker-compose -f docker-compose.nursery.yml logs --tail=100");
166
+ }
167
+ }
168
+
169
+ export async function cleanNursery() {
170
+ console.log(
171
+ chalk.yellow(
172
+ "\n⚠️ This will delete all nursery containers and volumes (including data)!\n",
173
+ ),
174
+ );
175
+
176
+ const confirmed = await askConfirmation(
177
+ chalk.red("Are you sure you want to continue? (y/N): "),
178
+ );
179
+
180
+ if (!confirmed) {
181
+ console.log(chalk.gray("\nClean cancelled."));
182
+ return;
183
+ }
184
+
185
+ const spinner = ora(
186
+ "Cleaning up nursery (removing containers and volumes)...",
187
+ ).start();
188
+
189
+ runCommand("docker-compose -f docker-compose.nursery.yml down -v", {
190
+ silent: true,
191
+ ignoreError: true,
192
+ });
193
+
194
+ spinner.succeed("Nursery cleanup complete!");
195
+ console.log(
196
+ chalk.gray("Run"),
197
+ chalk.cyan("crux nursery start"),
198
+ chalk.gray("to start fresh."),
199
+ );
200
+ }
201
+
202
+ export async function pullNursery() {
203
+ const spinner = ora(
204
+ "Pulling latest Crux Garden API image from ghcr.io...",
205
+ ).start();
206
+
207
+ runCommand("docker-compose -f docker-compose.nursery.yml pull", {
208
+ silent: true,
209
+ });
210
+
211
+ spinner.succeed("Latest nursery image pulled!");
212
+ console.log(
213
+ chalk.gray("Run"),
214
+ chalk.cyan("crux nursery restart"),
215
+ chalk.gray("to use the new image."),
216
+ );
217
+ }
218
+
219
+ export async function resetNursery() {
220
+ console.log(
221
+ chalk.yellow(
222
+ "\n⚠️ This will delete all nursery data and start fresh with the latest image!\n",
223
+ ),
224
+ );
225
+
226
+ const confirmed = await askConfirmation(
227
+ chalk.red("Are you sure you want to continue? (y/N): "),
228
+ );
229
+
230
+ if (!confirmed) {
231
+ console.log(chalk.gray("\nReset cancelled."));
232
+ return;
233
+ }
234
+
235
+ const spinner = ora("Resetting nursery environment...").start();
236
+
237
+ try {
238
+ // Stop and remove volumes
239
+ 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
+ });
244
+
245
+ // Pull latest image
246
+ spinner.text = "Pulling latest image...";
247
+ runCommand("docker-compose -f docker-compose.nursery.yml pull", {
248
+ silent: true,
249
+ });
250
+
251
+ // Start fresh
252
+ spinner.text = "Starting fresh nursery environment...";
253
+ runCommand("docker-compose -f docker-compose.nursery.yml up -d", {
254
+ silent: true,
255
+ });
256
+
257
+ 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");
261
+ console.log(
262
+ chalk.yellow("\nℹ Fresh nursery with latest demo data loaded"),
263
+ );
264
+ } catch (error) {
265
+ spinner.fail("Failed to reset nursery");
266
+ throw error;
267
+ }
268
+ }
269
+
270
+ export async function restartNursery() {
271
+ await stopNursery();
272
+ await startNursery({});
273
+ }
274
+
275
+ export async function statusNursery() {
276
+ console.log(chalk.bold("\nCrux Garden Nursery Environment Status:\n"));
277
+ runCommand("docker-compose -f docker-compose.nursery.yml ps");
278
+ }
279
+
280
+ export async function connectNurseryDb() {
281
+ console.log(chalk.gray("Connecting to Nursery PostgreSQL...\n"));
282
+ runCommand(
283
+ "docker exec -it cruxgarden-postgres-nursery psql -U cruxgarden -d cruxgarden",
284
+ );
285
+ }
286
+
287
+ export async function connectNurseryRedis() {
288
+ console.log(chalk.gray("Connecting to Nursery Redis...\n"));
289
+ runCommand("docker exec -it cruxgarden-redis-nursery redis-cli");
290
+ }
291
+
292
+ export async function connectNurseryApi() {
293
+ console.log(chalk.gray("Opening shell in Nursery API container...\n"));
294
+ runCommand("docker exec -it cruxgarden-api-nursery sh");
295
+ }
296
+
297
+ export async function stopNurseryDb() {
298
+ const spinner = ora("Stopping nursery database services...").start();
299
+ runCommand(
300
+ "docker-compose -f docker-compose.nursery.yml stop postgres redis",
301
+ { silent: true },
302
+ );
303
+ spinner.succeed("Nursery database services stopped!");
304
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@cruxgarden/cli",
3
+ "version": "0.0.1",
4
+ "description": "Crux Garden CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "crux": "./bin/crux.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
11
+ "format": "prettier --write .",
12
+ "docker:nursery": "cd docker && docker-compose -f docker-compose.nursery.yml up -d",
13
+ "docker:nursery:logs": "cd docker && docker-compose -f docker-compose.nursery.yml logs -f",
14
+ "docker:nursery:down": "cd docker && docker-compose -f docker-compose.nursery.yml down",
15
+ "docker:nursery:clean": "cd docker && docker-compose -f docker-compose.nursery.yml down -v",
16
+ "docker:nursery:pull": "cd docker && docker-compose -f docker-compose.nursery.yml pull",
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
+ "docker:nursery:db": "cd docker && docker-compose -f docker-compose.nursery.yml up -d postgres redis",
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"
23
+ },
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "dependencies": {
28
+ "chalk": "^5.3.0",
29
+ "commander": "^11.1.0",
30
+ "figlet": "^1.9.3",
31
+ "ora": "^7.0.1"
32
+ },
33
+ "devDependencies": {
34
+ "jest": "^29.7.0",
35
+ "prettier": "^3.5.0"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/CruxGarden/cli.git"
40
+ },
41
+ "keywords": [
42
+ "crux",
43
+ "cruxgarden",
44
+ "api",
45
+ "cli",
46
+ "docker",
47
+ "knowledge-management"
48
+ ],
49
+ "author": "The Keeper",
50
+ "license": "MIT",
51
+ "bugs": {
52
+ "url": "https://github.com/CruxGarden/cli/issues"
53
+ },
54
+ "homepage": "https://github.com/CruxGarden/cli#readme"
55
+ }