elestio 1.0.2 → 1.1.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/src/cli.js CHANGED
@@ -1,829 +1,32 @@
1
- import { parseArgs, log, colors, showHelp } from './utils.js';
2
1
  import { createRequire } from 'module';
2
+ import { parseArgs, log } from './utils.js';
3
+ import { registry } from './registry.js';
4
+ import { dispatch, renderMainHelp } from './router.js';
3
5
 
4
6
  const require = createRequire(import.meta.url);
5
7
  const pkg = require('../package.json');
6
8
 
7
- // ── Main help ──
8
-
9
- function printHelp() {
10
- console.log(`
11
- ${colors.bold}Elestio CLI${colors.reset} v${pkg.version}
12
- Deploy and manage services on the Elestio DevOps platform.
13
-
14
- ${colors.bold}Usage:${colors.reset} elestio <command> [action] [options]
15
-
16
- ${colors.bold}Auth & Config${colors.reset}
17
- login Configure credentials (--email, --token)
18
- whoami Show current user
19
- config Show/update configuration
20
- auth test Test authentication
21
-
22
- ${colors.bold}Catalog${colors.reset}
23
- templates Browse software templates
24
- sizes List server sizes and pricing
25
- categories List template categories
26
-
27
- ${colors.bold}Projects${colors.reset}
28
- projects Manage projects (list, create, edit, delete, members)
29
-
30
- ${colors.bold}Services${colors.reset}
31
- services List services in a project
32
- service Show service details
33
- deploy Deploy a new service
34
- delete-service Delete a service
35
- move-service Move a service to another project
36
- wait <vmID> Wait for a deployment to complete
37
-
38
- ${colors.bold}Server Actions${colors.reset}
39
- reboot <vmID> Reboot a VM
40
- reset <vmID> Hard reset a VM
41
- shutdown <vmID> Shutdown a VM
42
- poweroff <vmID> Force power off
43
- poweron <vmID> Power on a VM
44
- restart-stack <vmID> Restart Docker stack
45
- lock <vmID> Enable termination protection
46
- unlock <vmID> Disable termination protection
47
- resize <vmID> Resize a VM
48
-
49
- ${colors.bold}Networking & Security${colors.reset}
50
- firewall Manage firewall rules
51
- ssl Manage SSL/custom domains
52
- ssh-keys Manage SSH keys
53
-
54
- ${colors.bold}Updates${colors.reset}
55
- updates Manage auto-updates (system & app)
56
-
57
- ${colors.bold}Monitoring${colors.reset}
58
- alerts Manage alert rules
59
-
60
- ${colors.bold}Backups & Snapshots${colors.reset}
61
- backups Manage local/remote backups
62
- snapshots Manage VM snapshots
63
- s3-backup Manage S3 external backups
64
-
65
- ${colors.bold}Access${colors.reset}
66
- credentials <vmID> Get app credentials
67
- ssh <vmID> Get SSH/web terminal access
68
- vscode <vmID> Get VSCode web access
69
- files <vmID> Get file explorer access
70
-
71
- ${colors.bold}Volumes${colors.reset}
72
- volumes Manage block storage volumes
73
-
74
- ${colors.bold}CI/CD${colors.reset}
75
- cicd Manage CI/CD targets and pipelines
76
-
77
- ${colors.bold}Billing${colors.reset}
78
- billing View billing summary and details
79
-
80
- ${colors.bold}Global Options${colors.reset}
81
- --json Output in JSON format
82
- --project <id> Specify project ID
83
- --help, -h Show help
84
- --version, -v Show version
85
- `);
86
- }
87
-
88
- // ── Command router ──
89
-
90
9
  export async function run(argv) {
91
10
  const args = parseArgs(argv);
92
- const command = args._[0];
93
- const action = args._[1];
94
- const json = !!args.json;
95
-
96
- try {
97
- if (args.version || args.v) {
98
- console.log(pkg.version);
99
- return;
100
- }
101
-
102
- if (!command || args.help || args.h) {
103
- printHelp();
104
- return;
105
- }
106
-
107
- switch (command) {
108
-
109
- // ── Auth & Config ──
110
-
111
- case 'login': {
112
- const { configure } = await import('./commands/auth.js');
113
- await configure(args.email || args._[1], args.token || args._[2], json);
114
- break;
115
- }
116
-
117
- case 'whoami': {
118
- const { whoami } = await import('./commands/auth.js');
119
- whoami(json);
120
- break;
121
- }
122
-
123
- case 'config': {
124
- const auth = await import('./commands/auth.js');
125
- if (args.email && args.token) {
126
- await auth.configure(args.email, args.token, json);
127
- } else if (args['set-default-project'] || args['default-project']) {
128
- auth.setDefaultProject(args['set-default-project'] || args['default-project']);
129
- } else if (args.provider || args.datacenter || args['server-type'] || args.support) {
130
- auth.setDefaults(args.provider, args.datacenter, args['server-type'], args.support);
131
- } else if (action === 'test') {
132
- await auth.testAuth(json);
133
- } else {
134
- auth.showConfig(json);
135
- }
136
- break;
137
- }
138
-
139
- // ── Catalog ──
140
-
141
- case 'templates': {
142
- const { listTemplates, searchTemplates, findTemplate } = await import('./commands/templates.js');
143
- if (action === 'search') {
144
- const results = await searchTemplates(args._[2], args.category);
145
- if (json) {
146
- const { outputJson } = await import('./utils.js');
147
- outputJson(results.map(t => ({ id: t.id, title: t.title, category: t.category })));
148
- } else {
149
- console.log(`\n${colors.bold}Search Results (${results.length})${colors.reset}\n`);
150
- results.forEach(t => console.log(` ${t.id}: ${t.title} [${t.category}]`));
151
- console.log('');
152
- }
153
- } else if (action === 'info') {
154
- const template = await findTemplate(args._[2]);
155
- if (!template) throw new Error(`Template "${args._[2]}" not found`);
156
- if (json) {
157
- const { outputJson } = await import('./utils.js');
158
- outputJson(template);
159
- } else {
160
- console.log(`\n${colors.bold}${template.title}${colors.reset}`);
161
- console.log(` ID: ${template.id}`);
162
- console.log(` Category: ${template.category}`);
163
- console.log(` Version: ${template.version || template.dockerhub_default_tag || 'latest'}`);
164
- if (template.description) console.log(` Desc: ${template.description}`);
165
- console.log('');
166
- }
167
- } else {
168
- await listTemplates(args.category, json);
169
- }
170
- break;
171
- }
172
-
173
- case 'sizes': {
174
- const { listSizes } = await import('./commands/templates.js');
175
- await listSizes(args.provider, args.country, json);
176
- break;
177
- }
178
-
179
- case 'categories': {
180
- const { listCategories } = await import('./commands/templates.js');
181
- await listCategories(json);
182
- break;
183
- }
184
-
185
- // ── Projects ──
186
-
187
- case 'projects': {
188
- const projects = await import('./commands/projects.js');
189
- switch (action) {
190
- case 'create':
191
- await projects.createProject(args._[2] || args.name, args.description, args.emails);
192
- break;
193
- case 'edit':
194
- await projects.editProject(args._[2] || args.id, args.name, args.description, args.emails);
195
- break;
196
- case 'delete':
197
- await projects.deleteProject(args._[2] || args.id, !!args.force);
198
- break;
199
- case 'members':
200
- await projects.listMembers(args._[2] || args.project || args.id, json);
201
- break;
202
- case 'add-member':
203
- await projects.addMember(args._[2] || args.project, args._[3] || args.email, args.role);
204
- break;
205
- case 'remove-member':
206
- await projects.removeMember(args._[2] || args.project, args._[3] || args.member);
207
- break;
208
- default:
209
- if (action && action !== 'list') {
210
- showHelp('projects', {
211
- 'list': 'List all projects',
212
- 'create <name>': 'Create a project',
213
- 'edit <id>': 'Edit a project (--name, --description)',
214
- 'delete <id>': 'Delete a project (--force)',
215
- 'members <projectId>': 'List project members',
216
- 'add-member <projectId> <email>': 'Add a member (--role)',
217
- 'remove-member <projectId> <memberId>': 'Remove a member'
218
- });
219
- } else {
220
- await projects.listProjects(json);
221
- }
222
- }
223
- break;
224
- }
225
-
226
- // ── Services ──
227
-
228
- case 'services': {
229
- const { listServices } = await import('./commands/services.js');
230
- await listServices(args.project, json);
231
- break;
232
- }
233
-
234
- case 'service': {
235
- const { showService } = await import('./commands/services.js');
236
- if (!action) throw new Error('Usage: elestio service <vmID>');
237
- await showService(action, args.project, json);
238
- break;
239
- }
240
-
241
- case 'deploy': {
242
- const { deployService } = await import('./commands/services.js');
243
- if (!action) throw new Error('Usage: elestio deploy <template> [options]');
244
- await deployService(action, {
245
- name: args.name, size: args.size, region: args.region,
246
- provider: args.provider, project: args.project,
247
- support: args.support, email: args.email,
248
- version: args.version, dryRun: !!args['dry-run'],
249
- wait: args.wait !== 'false', timeout: args.timeout ? parseInt(args.timeout) : undefined,
250
- json, pipelineName: args['pipeline-name']
251
- });
252
- break;
253
- }
254
-
255
- case 'delete-service': {
256
- const { deleteService } = await import('./commands/services.js');
257
- if (!action) throw new Error('Usage: elestio delete-service <vmID> --force');
258
- await deleteService(action, { force: !!args.force, project: args.project, withBackups: !!args['with-backups'] });
259
- break;
260
- }
261
-
262
- case 'move-service': {
263
- const { moveService } = await import('./commands/services.js');
264
- const vmID = args._[1];
265
- const targetProject = args._[2] || args.target;
266
- if (!vmID || !targetProject) throw new Error('Usage: elestio move-service <vmID> <targetProjectId>');
267
- await moveService(vmID, targetProject, args.project);
268
- break;
269
- }
270
-
271
- case 'wait': {
272
- const { waitForDeployment } = await import('./commands/services.js');
273
- if (!action) throw new Error('Usage: elestio wait <vmID> [--project <id>]');
274
- await waitForDeployment(action, args.project, args.timeout ? parseInt(args.timeout) : undefined);
275
- break;
276
- }
277
-
278
- // ── Auth ──
279
-
280
- case 'auth': {
281
- const auth = await import('./commands/auth.js');
282
- if (action === 'test' || !action) {
283
- await auth.testAuth(json);
284
- } else {
285
- showHelp('auth', { 'test': 'Test authentication' });
286
- }
287
- break;
288
- }
289
11
 
290
- // ── Server Actions ──
291
-
292
- case 'reboot': {
293
- const { reboot } = await import('./commands/actions.js');
294
- if (!action) throw new Error('Usage: elestio reboot <vmID>');
295
- await reboot(action);
296
- break;
297
- }
298
-
299
- case 'reset': {
300
- const { reset } = await import('./commands/actions.js');
301
- if (!action) throw new Error('Usage: elestio reset <vmID>');
302
- await reset(action);
303
- break;
304
- }
305
-
306
- case 'shutdown': {
307
- const { shutdown } = await import('./commands/actions.js');
308
- if (!action) throw new Error('Usage: elestio shutdown <vmID>');
309
- await shutdown(action, { project: args.project });
310
- break;
311
- }
312
-
313
- case 'poweroff': {
314
- const { poweroff } = await import('./commands/actions.js');
315
- if (!action) throw new Error('Usage: elestio poweroff <vmID>');
316
- await poweroff(action);
317
- break;
318
- }
319
-
320
- case 'poweron': {
321
- const { poweron } = await import('./commands/actions.js');
322
- if (!action) throw new Error('Usage: elestio poweron <vmID>');
323
- await poweron(action);
324
- break;
325
- }
326
-
327
- case 'restart-stack': {
328
- const { restartStack } = await import('./commands/actions.js');
329
- if (!action) throw new Error('Usage: elestio restart-stack <vmID>');
330
- await restartStack(action);
331
- break;
332
- }
333
-
334
- case 'lock': {
335
- const { lock } = await import('./commands/actions.js');
336
- if (!action) throw new Error('Usage: elestio lock <vmID>');
337
- await lock(action);
338
- break;
339
- }
340
-
341
- case 'unlock': {
342
- const { unlock } = await import('./commands/actions.js');
343
- if (!action) throw new Error('Usage: elestio unlock <vmID>');
344
- await unlock(action);
345
- break;
346
- }
347
-
348
- case 'resize': {
349
- const { resizeServer } = await import('./commands/actions.js');
350
- if (!action) throw new Error('Usage: elestio resize <vmID> --size <type>');
351
- await resizeServer(action, args.size || args._[2], { project: args.project, provider: args.provider, region: args.region, cpuRamOnly: args['cpu-ram-only'] !== 'false' });
352
- break;
353
- }
354
-
355
- case 'change-version': {
356
- const { changeVersion } = await import('./commands/actions.js');
357
- if (!action) throw new Error('Usage: elestio change-version <vmID> <version>');
358
- await changeVersion(action, args._[2] || args.version);
359
- break;
360
- }
361
-
362
- // ── Firewall ──
363
-
364
- case 'firewall': {
365
- const actions = await import('./commands/actions.js');
366
- const vmID = args._[2];
367
- switch (action) {
368
- case 'get':
369
- if (!vmID) throw new Error('Usage: elestio firewall get <vmID>');
370
- await actions.getFirewallRules(vmID, json);
371
- break;
372
- case 'enable':
373
- if (!vmID) throw new Error('Usage: elestio firewall enable <vmID> --rules \'[...]\'');
374
- await actions.enableFirewall(vmID, JSON.parse(args.rules || '[]'));
375
- break;
376
- case 'update':
377
- if (!vmID) throw new Error('Usage: elestio firewall update <vmID> --rules \'[...]\'');
378
- await actions.updateFirewall(vmID, JSON.parse(args.rules || '[]'));
379
- break;
380
- case 'disable':
381
- if (!vmID) throw new Error('Usage: elestio firewall disable <vmID>');
382
- await actions.disableFirewall(vmID);
383
- break;
384
- default:
385
- showHelp('firewall', {
386
- 'get <vmID>': 'Show firewall rules',
387
- 'enable <vmID> --rules \'[...]\'': 'Enable firewall with rules',
388
- 'update <vmID> --rules \'[...]\'': 'Update firewall rules',
389
- 'disable <vmID>': 'Disable firewall'
390
- });
391
- }
392
- break;
393
- }
394
-
395
- // ── SSL / Custom Domains ──
396
-
397
- case 'ssl': {
398
- const actions = await import('./commands/actions.js');
399
- switch (action) {
400
- case 'list':
401
- await actions.listSslDomains(args._[2], json);
402
- break;
403
- case 'add':
404
- await actions.addSslDomain(args._[2], args._[3] || args.domain);
405
- break;
406
- case 'remove':
407
- await actions.removeSslDomain(args._[2], args._[3] || args.domain);
408
- break;
409
- default:
410
- showHelp('ssl', {
411
- 'list <vmID>': 'List custom domains',
412
- 'add <vmID> <domain>': 'Add a custom domain with auto-SSL',
413
- 'remove <vmID> <domain>': 'Remove a custom domain'
414
- });
415
- }
416
- break;
417
- }
418
-
419
- // ── SSH Keys ──
420
-
421
- case 'ssh-keys': {
422
- const actions = await import('./commands/actions.js');
423
- switch (action) {
424
- case 'list':
425
- await actions.listSshKeys(args._[2], json);
426
- break;
427
- case 'add':
428
- await actions.addSshKey(args._[2], args.name || args._[3], args.key || args._[4]);
429
- break;
430
- case 'remove':
431
- await actions.removeSshKey(args._[2], args.name || args._[3]);
432
- break;
433
- default:
434
- showHelp('ssh-keys', {
435
- 'list <vmID>': 'List SSH keys',
436
- 'add <vmID> --name <name> --key <key>': 'Add an SSH key',
437
- 'remove <vmID> --name <name>': 'Remove an SSH key'
438
- });
439
- }
440
- break;
441
- }
442
-
443
- // ── Updates ──
444
-
445
- case 'updates': {
446
- const actions = await import('./commands/actions.js');
447
- switch (action) {
448
- case 'system-enable':
449
- await actions.enableSystemAutoUpdate(args._[2], { day: args.day, hour: args.hour, minute: args.minute, securityOnly: args['security-only'] !== 'false' });
450
- break;
451
- case 'system-disable':
452
- await actions.disableSystemAutoUpdate(args._[2]);
453
- break;
454
- case 'system-now':
455
- await actions.runSystemUpdate(args._[2]);
456
- break;
457
- case 'app-enable':
458
- await actions.enableAppAutoUpdate(args._[2], { day: args.day, hour: args.hour, minute: args.minute });
459
- break;
460
- case 'app-disable':
461
- await actions.disableAppAutoUpdate(args._[2]);
462
- break;
463
- case 'app-now':
464
- await actions.runAppUpdate(args._[2]);
465
- break;
466
- default:
467
- showHelp('updates', {
468
- 'system-enable <vmID>': 'Enable OS auto-updates (--day, --hour, --minute)',
469
- 'system-disable <vmID>': 'Disable OS auto-updates',
470
- 'system-now <vmID>': 'Run OS update now',
471
- 'app-enable <vmID>': 'Enable app auto-updates (--day, --hour, --minute)',
472
- 'app-disable <vmID>': 'Disable app auto-updates',
473
- 'app-now <vmID>': 'Run app update now'
474
- });
475
- }
476
- break;
477
- }
478
-
479
- // ── Alerts ──
480
-
481
- case 'alerts': {
482
- const actions = await import('./commands/actions.js');
483
- switch (action) {
484
- case 'get':
485
- await actions.getAlerts(args._[2], json);
486
- break;
487
- case 'enable':
488
- case 'update':
489
- await actions.enableAlerts(args._[2], args.rules ? JSON.parse(args.rules) : undefined, args.cycle);
490
- break;
491
- case 'disable':
492
- await actions.disableAlerts(args._[2]);
493
- break;
494
- default:
495
- showHelp('alerts', {
496
- 'get <vmID>': 'Show alert rules',
497
- 'enable <vmID> --rules \'...\'': 'Enable/update alerts',
498
- 'disable <vmID>': 'Disable alerts'
499
- });
500
- }
501
- break;
502
- }
503
-
504
- // ── Backups ──
505
-
506
- case 'backups': {
507
- const backups = await import('./commands/backups.js');
508
- switch (action) {
509
- // Local
510
- case 'local-list':
511
- await backups.listLocalBackups(args._[2], json);
512
- break;
513
- case 'local-take':
514
- await backups.takeLocalBackup(args._[2]);
515
- break;
516
- case 'local-restore':
517
- await backups.restoreLocalBackup(args._[2], args._[3] || args.path);
518
- break;
519
- case 'local-delete':
520
- await backups.deleteLocalBackup(args._[2], args._[3] || args.path);
521
- break;
522
- // Remote
523
- case 'remote-list':
524
- await backups.listRemoteBackups(args._[2], json);
525
- break;
526
- case 'remote-take':
527
- await backups.takeRemoteBackup(args._[2]);
528
- break;
529
- case 'remote-restore':
530
- await backups.restoreRemoteBackup(args._[2], args._[3] || args.snapshot);
531
- break;
532
- case 'auto-enable':
533
- await backups.setupAutoBackups(args._[2], args.path, args.hour);
534
- break;
535
- case 'auto-disable':
536
- await backups.disableAutoBackups(args._[2]);
537
- break;
538
- default:
539
- showHelp('backups', {
540
- 'local-list <vmID>': 'List local backups',
541
- 'local-take <vmID>': 'Take a local backup',
542
- 'local-restore <vmID> <path>': 'Restore a local backup',
543
- 'local-delete <vmID> <path>': 'Delete a local backup',
544
- 'remote-list <vmID>': 'List remote backups',
545
- 'remote-take <vmID>': 'Take a remote backup',
546
- 'remote-restore <vmID> <snapshot>': 'Restore a remote backup',
547
- 'auto-enable <vmID>': 'Setup auto backups (--path, --hour)',
548
- 'auto-disable <vmID>': 'Disable auto backups'
549
- });
550
- }
551
- break;
552
- }
553
-
554
- // ── Snapshots ──
555
-
556
- case 'snapshots': {
557
- const backups = await import('./commands/backups.js');
558
- switch (action) {
559
- case 'list':
560
- await backups.listSnapshots(args._[2], json);
561
- break;
562
- case 'take':
563
- await backups.takeSnapshot(args._[2]);
564
- break;
565
- case 'restore':
566
- await backups.restoreSnapshot(args._[2], args._[3] || args.order);
567
- break;
568
- case 'delete':
569
- await backups.deleteSnapshot(args._[2], args._[3] || args.id);
570
- break;
571
- case 'auto-enable':
572
- await backups.enableAutoSnapshots(args._[2]);
573
- break;
574
- case 'auto-disable':
575
- await backups.disableAutoSnapshots(args._[2]);
576
- break;
577
- default:
578
- showHelp('snapshots', {
579
- 'list <vmID>': 'List snapshots',
580
- 'take <vmID>': 'Take a snapshot',
581
- 'restore <vmID> <orderID>': 'Restore a snapshot (0 = most recent)',
582
- 'delete <vmID> <snapshotID>': 'Delete a snapshot',
583
- 'auto-enable <vmID>': 'Enable auto snapshots',
584
- 'auto-disable <vmID>': 'Disable auto snapshots'
585
- });
586
- }
587
- break;
588
- }
589
-
590
- // ── S3 External Backups ──
591
-
592
- case 's3-backup': {
593
- const backups = await import('./commands/backups.js');
594
- switch (action) {
595
- case 'verify':
596
- await backups.verifyS3Config(args._[2], { apiKey: args.key, secretKey: args.secret, bucketName: args.bucket, endPoint: args.endpoint, prefix: args.prefix, providerType: args.type });
597
- break;
598
- case 'enable':
599
- await backups.enableS3Backup(args._[2], { apiKey: args.key, secretKey: args.secret, bucketName: args.bucket, endPoint: args.endpoint, prefix: args.prefix, providerType: args.type });
600
- break;
601
- case 'disable':
602
- await backups.disableS3Backup(args._[2]);
603
- break;
604
- case 'take':
605
- await backups.takeS3Backup(args._[2]);
606
- break;
607
- case 'list':
608
- await backups.listS3Backups(args._[2], json);
609
- break;
610
- case 'restore':
611
- await backups.restoreS3Backup(args._[2], args._[3] || args.key);
612
- break;
613
- case 'delete':
614
- await backups.deleteS3Backup(args._[2], args._[3] || args.key);
615
- break;
616
- default:
617
- showHelp('s3-backup', {
618
- 'verify <vmID>': 'Verify S3 config (--key, --secret, --bucket, --endpoint)',
619
- 'enable <vmID>': 'Enable S3 backup (--key, --secret, --bucket, --endpoint)',
620
- 'disable <vmID>': 'Disable S3 backup',
621
- 'take <vmID>': 'Take S3 backup now',
622
- 'list <vmID>': 'List S3 backups',
623
- 'restore <vmID> <key>': 'Restore S3 backup',
624
- 'delete <vmID> <key>': 'Delete S3 backup'
625
- });
626
- }
627
- break;
628
- }
629
-
630
- // ── Access ──
631
-
632
- case 'credentials': {
633
- const { getCredentials } = await import('./commands/access.js');
634
- if (!action) throw new Error('Usage: elestio credentials <vmID>');
635
- await getCredentials(action, args.project, json);
636
- break;
637
- }
638
-
639
- case 'ssh': {
640
- const access = await import('./commands/access.js');
641
- if (!action) throw new Error('Usage: elestio ssh <vmID>');
642
- if (args.direct) {
643
- await access.getSSHDirect(action, json);
644
- } else {
645
- await access.getSSH(action, args.project, json);
646
- }
647
- break;
648
- }
649
-
650
- case 'vscode': {
651
- const { getVSCode } = await import('./commands/access.js');
652
- if (!action) throw new Error('Usage: elestio vscode <vmID>');
653
- await getVSCode(action, args.project, json);
654
- break;
655
- }
656
-
657
- case 'files': {
658
- const { getFileExplorer } = await import('./commands/access.js');
659
- if (!action) throw new Error('Usage: elestio files <vmID>');
660
- await getFileExplorer(action, args.project, json);
661
- break;
662
- }
663
-
664
- // ── Volumes ──
665
-
666
- case 'volumes': {
667
- const volumes = await import('./commands/volumes.js');
668
- switch (action) {
669
- case 'create':
670
- await volumes.createVolume({ name: args.name || args._[2], size: args.size ? parseInt(args.size) : undefined, provider: args.provider, datacenter: args.datacenter, projectId: args.project, serverId: args.server, storageType: args.type });
671
- break;
672
- case 'service-list':
673
- await volumes.getServiceVolumes(args._[2], json);
674
- break;
675
- case 'service-create':
676
- await volumes.createServiceVolume(args._[2], { name: args.name || args._[3], size: args.size ? parseInt(args.size) : undefined, storageType: args.type });
677
- break;
678
- case 'resize':
679
- await volumes.resizeVolume(args._[2], args._[3] || args.volume, parseInt(args.size || args._[4]));
680
- break;
681
- case 'detach':
682
- await volumes.detachVolume(args._[2], args._[3] || args.volume, { keepVolume: args.keep !== 'false' });
683
- break;
684
- case 'delete':
685
- await volumes.deleteServiceVolume(args._[2], args._[3] || args.volume);
686
- break;
687
- case 'protect':
688
- await volumes.setVolumeProtection(args._[2], args._[3] || args.volume, args.disable !== true);
689
- break;
690
- default:
691
- if (action && action !== 'list') {
692
- showHelp('volumes', {
693
- 'list': 'List volumes in project (--project)',
694
- 'create --name <n> --size <gb>': 'Create a standalone volume',
695
- 'service-list <vmID>': 'List volumes attached to a service',
696
- 'service-create <vmID> --name <n>': 'Create and attach a volume',
697
- 'resize <vmID> <volumeID> --size <gb>': 'Resize a volume',
698
- 'detach <vmID> <volumeID>': 'Detach a volume (--keep false to delete)',
699
- 'delete <vmID> <volumeID>': 'Delete a volume',
700
- 'protect <vmID> <volumeID>': 'Toggle volume protection (--disable)'
701
- });
702
- } else {
703
- await volumes.listVolumes(args.project, json);
704
- }
705
- }
706
- break;
707
- }
708
-
709
- // ── CI/CD ──
710
-
711
- case 'cicd': {
712
- const cicd = await import('./commands/cicd.js');
713
- switch (action) {
714
- case 'targets':
715
- await cicd.getCicdServices(args.project, json);
716
- break;
717
- case 'pipelines':
718
- await cicd.getServicePipelines(args._[2], args.project, json);
719
- break;
720
- case 'pipeline-info':
721
- await cicd.getPipelineDetails(args._[2], args._[3] || args.pipeline, args.project, json);
722
- break;
723
- case 'pipeline-restart':
724
- await cicd.restartPipeline(args._[2], args._[3] || args.pipeline, args.project);
725
- break;
726
- case 'pipeline-stop':
727
- await cicd.stopPipeline(args._[2], args._[3] || args.pipeline, args.project);
728
- break;
729
- case 'pipeline-delete':
730
- await cicd.deletePipeline(args._[2], args._[3] || args.pipeline, args.project, !!args.force);
731
- break;
732
- case 'pipeline-resync':
733
- await cicd.resyncPipeline(args._[2], args._[3] || args.pipeline, args.project);
734
- break;
735
- case 'pipeline-logs':
736
- await cicd.getPipelineLogs(args._[2], args._[3] || args.pipeline, args.project);
737
- break;
738
- case 'pipeline-history':
739
- await cicd.getPipelineHistory(args._[2], args._[3] || args.pipeline, args.project, json);
740
- break;
741
- case 'pipeline-log':
742
- await cicd.viewPipelineLog(args._[2], args.pipeline, args.file, args.project);
743
- break;
744
- case 'domains':
745
- await cicd.listPipelineDomains(args._[2], args._[3] || args.pipeline, args.project, json);
746
- break;
747
- case 'domain-add':
748
- await cicd.addPipelineDomain(args._[2], args.pipeline, args.domain || args._[3], args.project);
749
- break;
750
- case 'domain-remove':
751
- await cicd.removePipelineDomain(args._[2], args.pipeline, args.domain || args._[3], args.project);
752
- break;
753
- case 'create': {
754
- if (args.auto) {
755
- await cicd.autoCreatePipeline({
756
- project: args.project, target: args.target, name: args.name,
757
- mode: args.mode, repo: args.repo, branch: args.branch,
758
- authId: args['auth-id'], image: args.image, imageTag: args['image-tag'],
759
- buildDir: args['build-dir'], rootDir: args['root-dir'],
760
- framework: args.framework, buildCmd: args['build-cmd'],
761
- runCmd: args['run-cmd'], installCmd: args['install-cmd'],
762
- nodeVersion: args['node-version'], variables: args.variables,
763
- compose: args.compose
764
- });
765
- } else {
766
- await cicd.createPipeline(args._[2] || args.file);
767
- }
768
- break;
769
- }
770
- case 'template': {
771
- const template = cicd.generatePipelineTemplate(args._[2] || args.mode || 'docker');
772
- console.log(template);
773
- break;
774
- }
775
- case 'registries':
776
- await cicd.getDockerRegistries(args.project, json);
777
- break;
778
- case 'registry-add':
779
- await cicd.addDockerRegistry(args.project, args.name || args._[2], args.username || args._[3], args.password, args.url);
780
- break;
781
- default:
782
- showHelp('cicd', {
783
- 'targets': 'List CI/CD targets',
784
- 'pipelines <vmID>': 'List pipelines on a target',
785
- 'pipeline-info <vmID> <pipelineID>': 'Show pipeline details',
786
- 'pipeline-restart <vmID> <pipelineID>': 'Restart a pipeline',
787
- 'pipeline-stop <vmID> <pipelineID>': 'Stop a pipeline',
788
- 'pipeline-delete <vmID> <pipelineID>': 'Delete a pipeline (--force)',
789
- 'pipeline-resync <vmID> <pipelineID>': 'Re-sync a pipeline',
790
- 'pipeline-logs <vmID> <pipelineID>': 'View pipeline logs',
791
- 'pipeline-history <vmID> <pipelineID>': 'View build history',
792
- 'create --auto --target <vmID> --name <n>': 'Auto-create pipeline (--mode, --repo)',
793
- 'create <config.json>': 'Create pipeline from config file',
794
- 'template [mode]': 'Generate pipeline config template',
795
- 'domains <vmID> <pipelineID>': 'List pipeline domains',
796
- 'domain-add <vmID> --pipeline <id> --domain <d>': 'Add a domain',
797
- 'domain-remove <vmID> --pipeline <id> --domain <d>': 'Remove a domain',
798
- 'registries': 'List Docker registries',
799
- 'registry-add --name <n> --username <u> --password <p> --url <url>': 'Add Docker registry'
800
- });
801
- }
802
- break;
803
- }
804
-
805
- // ── Billing ──
806
-
807
- case 'billing': {
808
- const billing = await import('./commands/billing.js');
809
- if (action === 'project') {
810
- await billing.getProjectBilling(args._[2] || args.project, json);
811
- } else {
812
- await billing.getBillingSummary(json);
813
- }
814
- break;
815
- }
12
+ if (args.version || args.v) {
13
+ console.log(pkg.version);
14
+ return;
15
+ }
816
16
 
817
- // ── Unknown ──
17
+ if (!args._[0] || ((args.help || args.h) && !args._[0])) {
18
+ console.log(renderMainHelp(registry, pkg.version));
19
+ return;
20
+ }
818
21
 
819
- default:
820
- log('error', `Unknown command: ${command}`);
821
- log('info', 'Run "elestio --help" for usage');
822
- process.exitCode = 1;
823
- }
22
+ try {
23
+ const code = await dispatch(registry, argv, args, { json: !!args.json });
24
+ if (code !== 0) process.exitCode = code;
824
25
  } catch (err) {
825
26
  log('error', err.message);
826
27
  if (args.debug) console.error(err);
827
28
  process.exitCode = 1;
828
29
  }
829
30
  }
31
+
32
+ export { registry };