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.
Files changed (66) hide show
  1. package/README.md +97 -11
  2. package/dist/commands/config.d.ts +2 -0
  3. package/dist/commands/config.d.ts.map +1 -0
  4. package/dist/commands/config.js +63 -0
  5. package/dist/commands/config.js.map +1 -0
  6. package/dist/commands/destroy.d.ts.map +1 -1
  7. package/dist/commands/destroy.js +7 -42
  8. package/dist/commands/destroy.js.map +1 -1
  9. package/dist/commands/doctor.d.ts +10 -0
  10. package/dist/commands/doctor.d.ts.map +1 -0
  11. package/dist/commands/doctor.js +92 -0
  12. package/dist/commands/doctor.js.map +1 -0
  13. package/dist/commands/health.d.ts +9 -0
  14. package/dist/commands/health.d.ts.map +1 -0
  15. package/dist/commands/health.js +56 -0
  16. package/dist/commands/health.js.map +1 -0
  17. package/dist/commands/logs.d.ts +8 -0
  18. package/dist/commands/logs.d.ts.map +1 -0
  19. package/dist/commands/logs.js +53 -0
  20. package/dist/commands/logs.js.map +1 -0
  21. package/dist/commands/monitor.d.ts +13 -0
  22. package/dist/commands/monitor.d.ts.map +1 -0
  23. package/dist/commands/monitor.js +94 -0
  24. package/dist/commands/monitor.js.map +1 -0
  25. package/dist/commands/restart.d.ts +2 -0
  26. package/dist/commands/restart.d.ts.map +1 -0
  27. package/dist/commands/restart.js +58 -0
  28. package/dist/commands/restart.js.map +1 -0
  29. package/dist/commands/ssh.d.ts +4 -0
  30. package/dist/commands/ssh.d.ts.map +1 -0
  31. package/dist/commands/ssh.js +33 -0
  32. package/dist/commands/ssh.js.map +1 -0
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +6 -43
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/update.d.ts +2 -0
  37. package/dist/commands/update.d.ts.map +1 -0
  38. package/dist/commands/update.js +63 -0
  39. package/dist/commands/update.js.map +1 -0
  40. package/dist/index.js +43 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers/base.d.ts +1 -0
  43. package/dist/providers/base.d.ts.map +1 -1
  44. package/dist/providers/digitalocean.d.ts +1 -0
  45. package/dist/providers/digitalocean.d.ts.map +1 -1
  46. package/dist/providers/digitalocean.js +16 -0
  47. package/dist/providers/digitalocean.js.map +1 -1
  48. package/dist/providers/hetzner.d.ts +1 -0
  49. package/dist/providers/hetzner.d.ts.map +1 -1
  50. package/dist/providers/hetzner.js +16 -0
  51. package/dist/providers/hetzner.js.map +1 -1
  52. package/dist/types/index.d.ts +6 -0
  53. package/dist/types/index.d.ts.map +1 -1
  54. package/dist/utils/defaults.d.ts +10 -0
  55. package/dist/utils/defaults.d.ts.map +1 -0
  56. package/dist/utils/defaults.js +47 -0
  57. package/dist/utils/defaults.js.map +1 -0
  58. package/dist/utils/serverSelect.d.ts +5 -0
  59. package/dist/utils/serverSelect.d.ts.map +1 -0
  60. package/dist/utils/serverSelect.js +49 -0
  61. package/dist/utils/serverSelect.js.map +1 -0
  62. package/dist/utils/ssh.d.ts +9 -0
  63. package/dist/utils/ssh.d.ts.map +1 -0
  64. package/dist/utils/ssh.js +44 -0
  65. package/dist/utils/ssh.js.map +1 -0
  66. 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, and destroy commands
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
- - **Server persistence:** Deploys saved to `~/.quicklify/servers.json` for list/status/destroy
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
- - [ ] SSL certificate automation
229
- - [ ] Backup configuration
230
- - [ ] Web dashboard
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**, **89%+ branches**, **96%+ functions**. 233 tests across 18 test suites.
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,2 @@
1
+ export declare function configCommand(subcommand?: string, args?: string[]): Promise<void>;
2
+ //# sourceMappingURL=config.d.ts.map
@@ -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":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtG,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAmB,EAAE,IAAe;IACtE,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC1D,OAAO;YACT,CAAC;YACD,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC;gBACH,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACvB,MAAM,CAAC,OAAO,CAAC,OAAO,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACvE,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAChC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;YACvC,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;YAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;gBAC9E,OAAO;YACT,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACtC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACtC,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,MAAM,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;YAC9C,MAAM;QACR,CAAC;QACD;YACE,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;YACxD,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;YACxD,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YACvD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,eAAe,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpD,MAAM;IACV,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AA6BA,wBAAsB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ElE"}
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"}
@@ -1,40 +1,12 @@
1
1
  import inquirer from "inquirer";
2
- import { getServers, findServer, removeServer } from "../utils/config.js";
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
- let server;
26
- if (query) {
27
- server = findServer(query);
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 { apiToken } = await inquirer.prompt([
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.trim());
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,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG3D,KAAK,UAAU,YAAY;IACzB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACzC;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,6BAA6B;YACtC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3B,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE;gBAC3C,KAAK,EAAE,CAAC,CAAC,EAAE;aACZ,CAAC,CAAC;SACJ;KACF,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAc;IACjD,IAAI,MAAgC,CAAC;IAErC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM;YAAE,OAAO;IACtB,CAAC;IAED,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,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACzC;YACE,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,cAAc,MAAM,CAAC,QAAQ,aAAa;YACnD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC;SACxF;KACF,CAAC,CAAC;IAEH,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,IAAI,EAAE,CAAC,CAAC;QAC3E,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"}
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"}