@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 +1 -0
- package/LICENSE +21 -0
- package/README.md +391 -0
- package/bin/crux.js +113 -0
- package/docker/docker-compose.nursery.yml +109 -0
- package/lib/commands.js +304 -0
- package/package.json +55 -0
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:
|
package/lib/commands.js
ADDED
|
@@ -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
|
+
}
|