quicklify 0.4.2 → 0.6.0
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 +97 -11
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +63 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/destroy.d.ts.map +1 -1
- package/dist/commands/destroy.js +7 -42
- package/dist/commands/destroy.js.map +1 -1
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +92 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/health.d.ts +9 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +56 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/logs.d.ts +8 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +53 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/monitor.d.ts +13 -0
- package/dist/commands/monitor.d.ts.map +1 -0
- package/dist/commands/monitor.js +94 -0
- package/dist/commands/monitor.js.map +1 -0
- package/dist/commands/restart.d.ts +2 -0
- package/dist/commands/restart.d.ts.map +1 -0
- package/dist/commands/restart.js +58 -0
- package/dist/commands/restart.js.map +1 -0
- package/dist/commands/ssh.d.ts +4 -0
- package/dist/commands/ssh.d.ts.map +1 -0
- package/dist/commands/ssh.js +33 -0
- package/dist/commands/ssh.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +6 -43
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +63 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/base.d.ts +1 -0
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/digitalocean.d.ts +1 -0
- package/dist/providers/digitalocean.d.ts.map +1 -1
- package/dist/providers/digitalocean.js +16 -0
- package/dist/providers/digitalocean.js.map +1 -1
- package/dist/providers/hetzner.d.ts +1 -0
- package/dist/providers/hetzner.d.ts.map +1 -1
- package/dist/providers/hetzner.js +16 -0
- package/dist/providers/hetzner.js.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/defaults.d.ts +10 -0
- package/dist/utils/defaults.d.ts.map +1 -0
- package/dist/utils/defaults.js +47 -0
- package/dist/utils/defaults.js.map +1 -0
- package/dist/utils/serverSelect.d.ts +5 -0
- package/dist/utils/serverSelect.d.ts.map +1 -0
- package/dist/utils/serverSelect.js +49 -0
- package/dist/utils/serverSelect.js.map +1 -0
- package/dist/utils/ssh.d.ts +9 -0
- package/dist/utils/ssh.d.ts.map +1 -0
- package/dist/utils/ssh.js +44 -0
- package/dist/utils/ssh.js.map +1 -0
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -45,8 +45,15 @@ npx quicklify init
|
|
|
45
45
|
- ✨ **Dynamic Server Types** - Only shows compatible types for selected location
|
|
46
46
|
- 🔥 **Auto Firewall** - Ports 8000, 22, 80, 443 configured automatically
|
|
47
47
|
- 🚀 **Zero SSH Required** - Opens directly in browser after deployment
|
|
48
|
-
- 📋 **Server Management** - List, status check,
|
|
48
|
+
- 📋 **Server Management** - List, status check, destroy, restart commands
|
|
49
|
+
- 🔧 **Default Config** - Set defaults to skip repetitive prompts
|
|
50
|
+
- 🔑 **SSH Access** - Connect to servers or run remote commands
|
|
51
|
+
- 🔄 **Coolify Update** - Update Coolify with one command
|
|
49
52
|
- 🏥 **Health Check Polling** - Detects when Coolify is ready (no more blind waiting)
|
|
53
|
+
- 📊 **Server Monitoring** - CPU/RAM/Disk usage and Docker container status
|
|
54
|
+
- 📜 **Log Viewer** - View Coolify, Docker, or system logs with follow mode
|
|
55
|
+
- 🩺 **Environment Doctor** - Diagnose local setup issues
|
|
56
|
+
- 🫀 **Bulk Health Check** - Check all servers at once
|
|
50
57
|
- 🤖 **Non-Interactive Mode** - CI/CD friendly with `--provider --token --region --size --name` flags
|
|
51
58
|
|
|
52
59
|
## 📦 Installation
|
|
@@ -159,13 +166,27 @@ For production use, we recommend setting up a domain instead of using the IP add
|
|
|
159
166
|
|
|
160
167
|
## 📋 Recent Updates
|
|
161
168
|
|
|
169
|
+
### v0.6.0 (2026-02-20)
|
|
170
|
+
- **New commands:** `quicklify logs`, `quicklify monitor`, `quicklify health`, `quicklify doctor`
|
|
171
|
+
- **Log viewer:** View Coolify/Docker/system logs with `--follow` real-time streaming
|
|
172
|
+
- **Server monitoring:** CPU/RAM/Disk usage and Docker container list
|
|
173
|
+
- **Bulk health check:** Check all registered servers at once with response times
|
|
174
|
+
- **Environment doctor:** Diagnose Node.js, SSH, config issues locally
|
|
175
|
+
- Zero new dependencies, 354 tests with 97%+ statement coverage
|
|
176
|
+
|
|
177
|
+
### v0.5.0 (2026-02-20)
|
|
178
|
+
- **New commands:** `quicklify config`, `quicklify ssh`, `quicklify update`, `quicklify restart`
|
|
179
|
+
- **Default config:** Set defaults for provider, region, size with `quicklify config set`
|
|
180
|
+
- **SSH access:** Connect to servers with `quicklify ssh` or run commands with `--command`
|
|
181
|
+
- **Coolify updates:** Update Coolify via SSH with `quicklify update`
|
|
182
|
+
- **Server restart:** Reboot via provider API with `quicklify restart`
|
|
183
|
+
- 311 tests with 97%+ statement coverage
|
|
184
|
+
|
|
162
185
|
### v0.4.0 (2026-02-20)
|
|
163
186
|
- **New commands:** `quicklify list`, `quicklify status [query]`, `quicklify destroy [query]`
|
|
164
187
|
- **Non-interactive mode:** `quicklify init --provider --token --region --size --name` for CI/CD
|
|
165
188
|
- **Health check polling:** Detects when Coolify is ready instead of blind waiting
|
|
166
|
-
-
|
|
167
|
-
- **`destroyServer()`** added to provider interface (Hetzner + DigitalOcean)
|
|
168
|
-
- 233 tests with 97%+ statement coverage
|
|
189
|
+
- 246 tests with 97%+ statement coverage
|
|
169
190
|
|
|
170
191
|
### v0.3.1 (2026-02-19)
|
|
171
192
|
- Hetzner pricing now shows net prices (excl. VAT), matching website display
|
|
@@ -221,14 +242,29 @@ For production use, we recommend setting up a domain instead of using the IP add
|
|
|
221
242
|
- [x] `destroyServer()` on provider interface
|
|
222
243
|
- [x] Double confirmation safety for destroy
|
|
223
244
|
|
|
245
|
+
### v0.5.0 (Completed)
|
|
246
|
+
|
|
247
|
+
- [x] Default configuration management (`quicklify config`)
|
|
248
|
+
- [x] SSH access to servers (`quicklify ssh`)
|
|
249
|
+
- [x] Coolify update via SSH (`quicklify update`)
|
|
250
|
+
- [x] Server restart via provider API (`quicklify restart`)
|
|
251
|
+
- [x] Shared server selection and token utilities (DRY refactor)
|
|
252
|
+
|
|
253
|
+
### v0.6.0 (Completed)
|
|
254
|
+
|
|
255
|
+
- [x] Server monitoring - CPU/RAM/Disk usage (`quicklify monitor`)
|
|
256
|
+
- [x] Log viewer - Coolify/Docker/system logs (`quicklify logs`)
|
|
257
|
+
- [x] Bulk health check for all servers (`quicklify health`)
|
|
258
|
+
- [x] Environment diagnostics (`quicklify doctor`)
|
|
259
|
+
- [x] SSH streaming for real-time log following
|
|
260
|
+
|
|
224
261
|
### Future
|
|
225
262
|
- [ ] Vultr support
|
|
226
|
-
- [ ] Linode support
|
|
227
|
-
- [ ] Domain configuration helper
|
|
228
|
-
- [ ]
|
|
229
|
-
- [ ]
|
|
230
|
-
- [ ]
|
|
231
|
-
- [ ] GitHub Actions integration
|
|
263
|
+
- [ ] Linode / AWS Lightsail support
|
|
264
|
+
- [ ] Domain + SSL configuration helper
|
|
265
|
+
- [ ] Backup/restore commands
|
|
266
|
+
- [ ] Interactive TUI dashboard
|
|
267
|
+
- [ ] Firewall management
|
|
232
268
|
|
|
233
269
|
## 🛠️ Tech Stack
|
|
234
270
|
|
|
@@ -264,6 +300,45 @@ quicklify status my-server
|
|
|
264
300
|
quicklify destroy 123.45.67.89
|
|
265
301
|
quicklify destroy my-server
|
|
266
302
|
|
|
303
|
+
# Manage default configuration
|
|
304
|
+
quicklify config set provider hetzner
|
|
305
|
+
quicklify config set region nbg1
|
|
306
|
+
quicklify config get provider
|
|
307
|
+
quicklify config list
|
|
308
|
+
quicklify config reset
|
|
309
|
+
|
|
310
|
+
# SSH into a server
|
|
311
|
+
quicklify ssh my-server
|
|
312
|
+
quicklify ssh 123.45.67.89 -c "docker ps"
|
|
313
|
+
|
|
314
|
+
# Update Coolify on a server
|
|
315
|
+
quicklify update my-server
|
|
316
|
+
|
|
317
|
+
# Restart a server
|
|
318
|
+
quicklify restart my-server
|
|
319
|
+
|
|
320
|
+
# View Coolify logs (last 50 lines)
|
|
321
|
+
quicklify logs my-server
|
|
322
|
+
|
|
323
|
+
# Follow Coolify logs in real-time
|
|
324
|
+
quicklify logs my-server --follow
|
|
325
|
+
|
|
326
|
+
# View Docker or system logs
|
|
327
|
+
quicklify logs my-server --service docker --lines 100
|
|
328
|
+
quicklify logs my-server --service system
|
|
329
|
+
|
|
330
|
+
# Show CPU/RAM/Disk usage
|
|
331
|
+
quicklify monitor my-server
|
|
332
|
+
|
|
333
|
+
# Show usage with Docker containers
|
|
334
|
+
quicklify monitor my-server --containers
|
|
335
|
+
|
|
336
|
+
# Check health of all servers
|
|
337
|
+
quicklify health
|
|
338
|
+
|
|
339
|
+
# Run environment diagnostics
|
|
340
|
+
quicklify doctor
|
|
341
|
+
|
|
267
342
|
# Show version
|
|
268
343
|
quicklify --version
|
|
269
344
|
|
|
@@ -341,14 +416,25 @@ tests/
|
|
|
341
416
|
│ ├── cloudInit.test.ts
|
|
342
417
|
│ ├── config.test.ts # Config CRUD operations
|
|
343
418
|
│ ├── config-edge.test.ts # Config edge cases (corruption, empty files)
|
|
419
|
+
│ ├── config-command.test.ts # Config command subcommands
|
|
420
|
+
│ ├── defaults.test.ts # Default config CRUD
|
|
344
421
|
│ ├── destroy.test.ts # Destroy command unit tests
|
|
422
|
+
│ ├── doctor.test.ts # Doctor command tests
|
|
423
|
+
│ ├── health-command.test.ts # Health command tests
|
|
345
424
|
│ ├── healthCheck.test.ts # Health check polling tests
|
|
346
425
|
│ ├── healthCheck-edge.test.ts # Health check edge cases (302, 401, 500)
|
|
347
426
|
│ ├── list.test.ts # List command unit tests
|
|
348
427
|
│ ├── logger.test.ts
|
|
428
|
+
│ ├── logs.test.ts # Logs command tests
|
|
429
|
+
│ ├── monitor.test.ts # Monitor command tests
|
|
349
430
|
│ ├── prompts.test.ts
|
|
350
431
|
│ ├── providerFactory.test.ts # Provider factory tests
|
|
432
|
+
│ ├── restart.test.ts # Restart command tests
|
|
433
|
+
│ ├── serverSelect.test.ts # Server selection utility tests
|
|
434
|
+
│ ├── ssh-command.test.ts # SSH command tests
|
|
435
|
+
│ ├── ssh-utils.test.ts # SSH helper tests
|
|
351
436
|
│ ├── status.test.ts # Status command unit tests
|
|
437
|
+
│ ├── update.test.ts # Update command tests
|
|
352
438
|
│ └── validators.test.ts
|
|
353
439
|
├── integration/ # Integration tests (provider API calls)
|
|
354
440
|
│ ├── hetzner.test.ts # Including destroyServer tests
|
|
@@ -369,7 +455,7 @@ Tests run automatically on every push/PR via GitHub Actions across:
|
|
|
369
455
|
|
|
370
456
|
### Coverage
|
|
371
457
|
|
|
372
|
-
Current coverage: **97%+ statements/lines**, **
|
|
458
|
+
Current coverage: **97%+ statements/lines**, **87%+ branches**, **96%+ functions**. 354 tests across 29 test suites.
|
|
373
459
|
|
|
374
460
|
## 🔧 Troubleshooting
|
|
375
461
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDvF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getDefaults, setDefault, getDefault, resetDefaults, VALID_KEYS } from "../utils/defaults.js";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
export async function configCommand(subcommand, args) {
|
|
4
|
+
switch (subcommand) {
|
|
5
|
+
case "set": {
|
|
6
|
+
if (!args || args.length < 2) {
|
|
7
|
+
logger.error("Usage: quicklify config set <key> <value>");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const [key, value] = args;
|
|
11
|
+
try {
|
|
12
|
+
setDefault(key, value);
|
|
13
|
+
logger.success(`Set ${key} = ${value}`);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
17
|
+
}
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
case "get": {
|
|
21
|
+
if (!args || args.length < 1) {
|
|
22
|
+
logger.error("Usage: quicklify config get <key>");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const val = getDefault(args[0]);
|
|
26
|
+
if (val !== undefined) {
|
|
27
|
+
logger.info(`${args[0]} = ${val}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
logger.info(`${args[0]} is not set`);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case "list": {
|
|
35
|
+
const config = getDefaults();
|
|
36
|
+
const entries = Object.entries(config).filter(([, v]) => v !== undefined);
|
|
37
|
+
if (entries.length === 0) {
|
|
38
|
+
logger.info("No default config set. Use: quicklify config set <key> <value>");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
logger.title("Default Configuration");
|
|
42
|
+
for (const [k, v] of entries) {
|
|
43
|
+
logger.info(`${k.padEnd(12)} ${v}`);
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "reset": {
|
|
48
|
+
resetDefaults();
|
|
49
|
+
logger.success("Default configuration reset");
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
logger.error("Usage: quicklify config <set|get|list|reset>");
|
|
54
|
+
logger.info(` set <key> <value> Set a default value`);
|
|
55
|
+
logger.info(` get <key> Get a default value`);
|
|
56
|
+
logger.info(` list Show all defaults`);
|
|
57
|
+
logger.info(` reset Clear all defaults`);
|
|
58
|
+
logger.info(``);
|
|
59
|
+
logger.info(`Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AAMA,wBAAsB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DlE"}
|
package/dist/commands/destroy.js
CHANGED
|
@@ -1,40 +1,12 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
|
-
import {
|
|
2
|
+
import { removeServer } from "../utils/config.js";
|
|
3
|
+
import { resolveServer, promptApiToken } from "../utils/serverSelect.js";
|
|
3
4
|
import { createProviderWithToken } from "../utils/providerFactory.js";
|
|
4
5
|
import { logger, createSpinner } from "../utils/logger.js";
|
|
5
|
-
async function selectServer() {
|
|
6
|
-
const servers = getServers();
|
|
7
|
-
if (servers.length === 0) {
|
|
8
|
-
logger.info("No servers found.");
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|
|
11
|
-
const { serverId } = await inquirer.prompt([
|
|
12
|
-
{
|
|
13
|
-
type: "list",
|
|
14
|
-
name: "serverId",
|
|
15
|
-
message: "Select a server to destroy:",
|
|
16
|
-
choices: servers.map((s) => ({
|
|
17
|
-
name: `${s.name} (${s.ip}) - ${s.provider}`,
|
|
18
|
-
value: s.id,
|
|
19
|
-
})),
|
|
20
|
-
},
|
|
21
|
-
]);
|
|
22
|
-
return servers.find((s) => s.id === serverId);
|
|
23
|
-
}
|
|
24
6
|
export async function destroyCommand(query) {
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
if (!server) {
|
|
29
|
-
logger.error(`Server not found: ${query}`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
server = await selectServer();
|
|
35
|
-
if (!server)
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
7
|
+
const server = await resolveServer(query, "Select a server to destroy:");
|
|
8
|
+
if (!server)
|
|
9
|
+
return;
|
|
38
10
|
// First confirmation
|
|
39
11
|
const { confirm } = await inquirer.prompt([
|
|
40
12
|
{
|
|
@@ -61,18 +33,11 @@ export async function destroyCommand(query) {
|
|
|
61
33
|
return;
|
|
62
34
|
}
|
|
63
35
|
// Ask for API token
|
|
64
|
-
const
|
|
65
|
-
{
|
|
66
|
-
type: "password",
|
|
67
|
-
name: "apiToken",
|
|
68
|
-
message: `Enter your ${server.provider} API token:`,
|
|
69
|
-
validate: (input) => (input.trim().length > 0 ? true : "API token is required"),
|
|
70
|
-
},
|
|
71
|
-
]);
|
|
36
|
+
const apiToken = await promptApiToken(server.provider);
|
|
72
37
|
const spinner = createSpinner("Destroying server...");
|
|
73
38
|
spinner.start();
|
|
74
39
|
try {
|
|
75
|
-
const provider = createProviderWithToken(server.provider, apiToken
|
|
40
|
+
const provider = createProviderWithToken(server.provider, apiToken);
|
|
76
41
|
await provider.destroyServer(server.id);
|
|
77
42
|
removeServer(server.id);
|
|
78
43
|
spinner.succeed(`Server "${server.name}" destroyed`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destroy.js","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"destroy.js","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAc;IACjD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;IACzE,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,qBAAqB;IACrB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACxC;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,qCAAqC,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,EAAE,IAAI;YAC5E,OAAO,EAAE,KAAK;SACf;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,wCAAwC;IACxC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QAC5C;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,yBAAyB,MAAM,CAAC,IAAI,eAAe;SAC7D;KACF,CAAC,CAAC;IAEH,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC/D,OAAO;IACT,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,aAAa,CAAC,sBAAsB,CAAC,CAAC;IACtD,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACpE,MAAM,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,CAAC,OAAO,CAAC,WAAW,MAAM,CAAC,IAAI,aAAa,CAAC,CAAC;QACrD,MAAM,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,UAAU,GACd,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE7F,IAAI,UAAU,EAAE,CAAC;YACf,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,QAAQ,mCAAmC,CAAC,CAAC;YACxF,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CheckResult {
|
|
2
|
+
name: string;
|
|
3
|
+
status: "pass" | "fail" | "warn";
|
|
4
|
+
detail: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function runDoctorChecks(version?: string): CheckResult[];
|
|
7
|
+
export declare function doctorCommand(options?: {
|
|
8
|
+
checkTokens?: boolean;
|
|
9
|
+
}, version?: string): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAsDD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAS/D;AAED,wBAAsB,aAAa,CACjC,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { existsSync, accessSync, constants } from "fs";
|
|
3
|
+
import { getServers } from "../utils/config.js";
|
|
4
|
+
import { checkSshAvailable } from "../utils/ssh.js";
|
|
5
|
+
import { logger } from "../utils/logger.js";
|
|
6
|
+
import { CONFIG_DIR } from "../utils/config.js";
|
|
7
|
+
function checkNodeVersion() {
|
|
8
|
+
const version = process.version;
|
|
9
|
+
const major = parseInt(version.slice(1).split(".")[0], 10);
|
|
10
|
+
if (major >= 20) {
|
|
11
|
+
return { name: "Node.js", status: "pass", detail: version };
|
|
12
|
+
}
|
|
13
|
+
return { name: "Node.js", status: "fail", detail: `${version} (requires >= 20)` };
|
|
14
|
+
}
|
|
15
|
+
function checkNpmVersion() {
|
|
16
|
+
try {
|
|
17
|
+
const version = execSync("npm --version", { stdio: "pipe" }).toString().trim();
|
|
18
|
+
return { name: "npm", status: "pass", detail: `v${version}` };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { name: "npm", status: "fail", detail: "not found" };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function checkSsh() {
|
|
25
|
+
if (checkSshAvailable()) {
|
|
26
|
+
return { name: "SSH Client", status: "pass", detail: "available" };
|
|
27
|
+
}
|
|
28
|
+
return { name: "SSH Client", status: "warn", detail: "not found (needed for ssh/logs/monitor/update)" };
|
|
29
|
+
}
|
|
30
|
+
function checkQuicklifyVersion(version) {
|
|
31
|
+
if (version) {
|
|
32
|
+
return { name: "quicklify", status: "pass", detail: `v${version}` };
|
|
33
|
+
}
|
|
34
|
+
return { name: "quicklify", status: "warn", detail: "version unknown" };
|
|
35
|
+
}
|
|
36
|
+
function checkConfigDir() {
|
|
37
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
38
|
+
return { name: "Config Dir", status: "warn", detail: `${CONFIG_DIR} (not created yet)` };
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
accessSync(CONFIG_DIR, constants.R_OK | constants.W_OK);
|
|
42
|
+
return { name: "Config Dir", status: "pass", detail: CONFIG_DIR };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { name: "Config Dir", status: "fail", detail: `${CONFIG_DIR} (not writable)` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function checkRegisteredServers() {
|
|
49
|
+
const servers = getServers();
|
|
50
|
+
if (servers.length === 0) {
|
|
51
|
+
return { name: "Servers", status: "warn", detail: "0 registered (run quicklify init)" };
|
|
52
|
+
}
|
|
53
|
+
return { name: "Servers", status: "pass", detail: `${servers.length} registered` };
|
|
54
|
+
}
|
|
55
|
+
export function runDoctorChecks(version) {
|
|
56
|
+
return [
|
|
57
|
+
checkNodeVersion(),
|
|
58
|
+
checkNpmVersion(),
|
|
59
|
+
checkSsh(),
|
|
60
|
+
checkQuicklifyVersion(version),
|
|
61
|
+
checkConfigDir(),
|
|
62
|
+
checkRegisteredServers(),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
export async function doctorCommand(options, version) {
|
|
66
|
+
logger.title("Quicklify Doctor");
|
|
67
|
+
const results = runDoctorChecks(version);
|
|
68
|
+
for (const result of results) {
|
|
69
|
+
const colorFn = result.status === "pass"
|
|
70
|
+
? logger.success
|
|
71
|
+
: result.status === "warn"
|
|
72
|
+
? logger.warning
|
|
73
|
+
: logger.error;
|
|
74
|
+
colorFn(`${result.name}: ${result.detail}`);
|
|
75
|
+
}
|
|
76
|
+
const failures = results.filter((r) => r.status === "fail");
|
|
77
|
+
const warnings = results.filter((r) => r.status === "warn");
|
|
78
|
+
console.log();
|
|
79
|
+
if (failures.length > 0) {
|
|
80
|
+
logger.error(`${failures.length} check(s) failed. Please fix the issues above.`);
|
|
81
|
+
}
|
|
82
|
+
else if (warnings.length > 0) {
|
|
83
|
+
logger.warning(`All checks passed with ${warnings.length} warning(s).`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
logger.success("All checks passed! Your environment is ready.");
|
|
87
|
+
}
|
|
88
|
+
if (options?.checkTokens) {
|
|
89
|
+
logger.info("Token validation is not yet implemented.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ServerRecord } from "../types/index.js";
|
|
2
|
+
export interface HealthResult {
|
|
3
|
+
server: ServerRecord;
|
|
4
|
+
status: "healthy" | "unhealthy" | "unreachable";
|
|
5
|
+
responseTime: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function checkServerHealth(server: ServerRecord): Promise<HealthResult>;
|
|
8
|
+
export declare function healthCommand(): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/commands/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAcnF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA+CnD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { getServers } from "../utils/config.js";
|
|
3
|
+
import { logger, createSpinner } from "../utils/logger.js";
|
|
4
|
+
export async function checkServerHealth(server) {
|
|
5
|
+
const start = Date.now();
|
|
6
|
+
try {
|
|
7
|
+
const response = await axios.get(`http://${server.ip}:8000`, {
|
|
8
|
+
timeout: 5000,
|
|
9
|
+
validateStatus: () => true,
|
|
10
|
+
});
|
|
11
|
+
const responseTime = Date.now() - start;
|
|
12
|
+
const status = response.status < 500 ? "healthy" : "unhealthy";
|
|
13
|
+
return { server, status, responseTime };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
const responseTime = Date.now() - start;
|
|
17
|
+
return { server, status: "unreachable", responseTime };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function healthCommand() {
|
|
21
|
+
const servers = getServers();
|
|
22
|
+
if (servers.length === 0) {
|
|
23
|
+
logger.info("No servers found. Deploy one with: quicklify init");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const spinner = createSpinner(`Checking health of ${servers.length} server(s)...`);
|
|
27
|
+
spinner.start();
|
|
28
|
+
const results = await Promise.all(servers.map(checkServerHealth));
|
|
29
|
+
spinner.succeed("Health check complete");
|
|
30
|
+
console.log();
|
|
31
|
+
// Table header
|
|
32
|
+
const header = `${"Name".padEnd(20)} ${"IP".padEnd(16)} ${"Status".padEnd(14)} ${"Response".padEnd(10)}`;
|
|
33
|
+
console.log(header);
|
|
34
|
+
console.log("─".repeat(header.length));
|
|
35
|
+
// Table rows
|
|
36
|
+
for (const result of results) {
|
|
37
|
+
const statusStr = result.status === "healthy"
|
|
38
|
+
? "✔ healthy"
|
|
39
|
+
: result.status === "unhealthy"
|
|
40
|
+
? "⚠ unhealthy"
|
|
41
|
+
: "✖ unreachable";
|
|
42
|
+
const timeStr = result.status === "unreachable" ? "timeout" : `${result.responseTime}ms`;
|
|
43
|
+
console.log(`${result.server.name.padEnd(20)} ${result.server.ip.padEnd(16)} ${statusStr.padEnd(14)} ${timeStr.padEnd(10)}`);
|
|
44
|
+
}
|
|
45
|
+
console.log();
|
|
46
|
+
const healthy = results.filter((r) => r.status === "healthy").length;
|
|
47
|
+
const unhealthy = results.filter((r) => r.status === "unhealthy").length;
|
|
48
|
+
const unreachable = results.filter((r) => r.status === "unreachable").length;
|
|
49
|
+
if (unreachable > 0 || unhealthy > 0) {
|
|
50
|
+
logger.warning(`${healthy} healthy, ${unhealthy} unhealthy, ${unreachable} unreachable`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
logger.success(`All ${healthy} server(s) are healthy`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/commands/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type LogService = "coolify" | "docker" | "system";
|
|
2
|
+
export declare function buildLogCommand(service: LogService, lines: number, follow: boolean): string;
|
|
3
|
+
export declare function logsCommand(query?: string, options?: {
|
|
4
|
+
lines?: string;
|
|
5
|
+
follow?: boolean;
|
|
6
|
+
service?: string;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=logs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzD,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAS3F;AAED,wBAAsB,WAAW,CAC/B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { resolveServer } from "../utils/serverSelect.js";
|
|
2
|
+
import { checkSshAvailable, sshExec, sshStream } from "../utils/ssh.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
export function buildLogCommand(service, lines, follow) {
|
|
5
|
+
switch (service) {
|
|
6
|
+
case "coolify":
|
|
7
|
+
return `docker logs coolify --tail ${lines}${follow ? " --follow" : ""}`;
|
|
8
|
+
case "docker":
|
|
9
|
+
return `journalctl -u docker --no-pager -n ${lines}${follow ? " -f" : ""}`;
|
|
10
|
+
case "system":
|
|
11
|
+
return `journalctl --no-pager -n ${lines}${follow ? " -f" : ""}`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function logsCommand(query, options) {
|
|
15
|
+
if (!checkSshAvailable()) {
|
|
16
|
+
logger.error("SSH client not found. Please install OpenSSH.");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const server = await resolveServer(query, "Select a server to view logs:");
|
|
20
|
+
if (!server)
|
|
21
|
+
return;
|
|
22
|
+
const lines = parseInt(options?.lines || "50", 10);
|
|
23
|
+
if (isNaN(lines) || lines <= 0) {
|
|
24
|
+
logger.error("Invalid --lines value. Must be a positive number.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const service = options?.service || "coolify";
|
|
28
|
+
const validServices = ["coolify", "docker", "system"];
|
|
29
|
+
if (!validServices.includes(service)) {
|
|
30
|
+
logger.error(`Invalid service: ${service}. Choose from: ${validServices.join(", ")}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const follow = options?.follow || false;
|
|
34
|
+
const command = buildLogCommand(service, lines, follow);
|
|
35
|
+
logger.info(`Fetching ${service} logs from ${server.name} (${server.ip})...`);
|
|
36
|
+
if (follow) {
|
|
37
|
+
const exitCode = await sshStream(server.ip, command);
|
|
38
|
+
if (exitCode !== 0) {
|
|
39
|
+
logger.error(`Log stream ended with code ${exitCode}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const result = await sshExec(server.ip, command);
|
|
44
|
+
if (result.stdout)
|
|
45
|
+
console.log(result.stdout);
|
|
46
|
+
if (result.stderr)
|
|
47
|
+
console.error(result.stderr);
|
|
48
|
+
if (result.code !== 0) {
|
|
49
|
+
logger.error(`Failed to fetch logs (exit code ${result.code})`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SystemMetrics {
|
|
2
|
+
cpu: string;
|
|
3
|
+
ramUsed: string;
|
|
4
|
+
ramTotal: string;
|
|
5
|
+
diskUsed: string;
|
|
6
|
+
diskTotal: string;
|
|
7
|
+
diskPercent: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseMetrics(stdout: string): SystemMetrics;
|
|
10
|
+
export declare function monitorCommand(query?: string, options?: {
|
|
11
|
+
containers?: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/commands/monitor.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAiD1D;AAED,wBAAsB,cAAc,CAClC,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAiDf"}
|