hetzner-cli 2.0.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 (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +907 -0
  3. package/dist/auction/client.d.ts +4 -0
  4. package/dist/auction/client.js +103 -0
  5. package/dist/auction/commands.d.ts +2 -0
  6. package/dist/auction/commands.js +138 -0
  7. package/dist/auction/formatter.d.ts +3 -0
  8. package/dist/auction/formatter.js +87 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +39 -0
  11. package/dist/client.d.ts +2 -0
  12. package/dist/client.js +4 -0
  13. package/dist/cloud/client.d.ts +511 -0
  14. package/dist/cloud/client.js +706 -0
  15. package/dist/cloud/commands/certificate.d.ts +2 -0
  16. package/dist/cloud/commands/certificate.js +77 -0
  17. package/dist/cloud/commands/context.d.ts +2 -0
  18. package/dist/cloud/commands/context.js +78 -0
  19. package/dist/cloud/commands/datacenter.d.ts +2 -0
  20. package/dist/cloud/commands/datacenter.js +20 -0
  21. package/dist/cloud/commands/firewall.d.ts +2 -0
  22. package/dist/cloud/commands/firewall.js +77 -0
  23. package/dist/cloud/commands/floating-ip.d.ts +2 -0
  24. package/dist/cloud/commands/floating-ip.js +83 -0
  25. package/dist/cloud/commands/image.d.ts +2 -0
  26. package/dist/cloud/commands/image.js +60 -0
  27. package/dist/cloud/commands/index.d.ts +2 -0
  28. package/dist/cloud/commands/index.js +41 -0
  29. package/dist/cloud/commands/iso.d.ts +2 -0
  30. package/dist/cloud/commands/iso.js +22 -0
  31. package/dist/cloud/commands/load-balancer-type.d.ts +2 -0
  32. package/dist/cloud/commands/load-balancer-type.js +20 -0
  33. package/dist/cloud/commands/load-balancer.d.ts +2 -0
  34. package/dist/cloud/commands/load-balancer.js +177 -0
  35. package/dist/cloud/commands/location.d.ts +2 -0
  36. package/dist/cloud/commands/location.js +20 -0
  37. package/dist/cloud/commands/network.d.ts +2 -0
  38. package/dist/cloud/commands/network.js +96 -0
  39. package/dist/cloud/commands/placement-group.d.ts +2 -0
  40. package/dist/cloud/commands/placement-group.js +53 -0
  41. package/dist/cloud/commands/primary-ip.d.ts +2 -0
  42. package/dist/cloud/commands/primary-ip.js +83 -0
  43. package/dist/cloud/commands/server-type.d.ts +2 -0
  44. package/dist/cloud/commands/server-type.js +20 -0
  45. package/dist/cloud/commands/server.d.ts +2 -0
  46. package/dist/cloud/commands/server.js +260 -0
  47. package/dist/cloud/commands/ssh-key.d.ts +2 -0
  48. package/dist/cloud/commands/ssh-key.js +63 -0
  49. package/dist/cloud/commands/volume.d.ts +2 -0
  50. package/dist/cloud/commands/volume.js +92 -0
  51. package/dist/cloud/context.d.ts +28 -0
  52. package/dist/cloud/context.js +172 -0
  53. package/dist/cloud/formatter.d.ts +37 -0
  54. package/dist/cloud/formatter.js +413 -0
  55. package/dist/cloud/helpers.d.ts +18 -0
  56. package/dist/cloud/helpers.js +48 -0
  57. package/dist/cloud/types.d.ts +398 -0
  58. package/dist/cloud/types.js +5 -0
  59. package/dist/config.d.ts +1 -0
  60. package/dist/config.js +2 -0
  61. package/dist/formatter.d.ts +3 -0
  62. package/dist/formatter.js +6 -0
  63. package/dist/index.d.ts +10 -0
  64. package/dist/index.js +17 -0
  65. package/dist/robot/client.d.ts +256 -0
  66. package/dist/robot/client.js +656 -0
  67. package/dist/robot/commands/auth.d.ts +2 -0
  68. package/dist/robot/commands/auth.js +54 -0
  69. package/dist/robot/commands/boot.d.ts +2 -0
  70. package/dist/robot/commands/boot.js +72 -0
  71. package/dist/robot/commands/cancel.d.ts +2 -0
  72. package/dist/robot/commands/cancel.js +36 -0
  73. package/dist/robot/commands/failover.d.ts +2 -0
  74. package/dist/robot/commands/failover.js +42 -0
  75. package/dist/robot/commands/firewall.d.ts +2 -0
  76. package/dist/robot/commands/firewall.js +66 -0
  77. package/dist/robot/commands/index.d.ts +2 -0
  78. package/dist/robot/commands/index.js +36 -0
  79. package/dist/robot/commands/interactive.d.ts +2 -0
  80. package/dist/robot/commands/interactive.js +134 -0
  81. package/dist/robot/commands/ip.d.ts +2 -0
  82. package/dist/robot/commands/ip.js +52 -0
  83. package/dist/robot/commands/key.d.ts +2 -0
  84. package/dist/robot/commands/key.js +64 -0
  85. package/dist/robot/commands/order.d.ts +2 -0
  86. package/dist/robot/commands/order.js +33 -0
  87. package/dist/robot/commands/rdns.d.ts +2 -0
  88. package/dist/robot/commands/rdns.js +41 -0
  89. package/dist/robot/commands/reset.d.ts +2 -0
  90. package/dist/robot/commands/reset.js +77 -0
  91. package/dist/robot/commands/server.d.ts +2 -0
  92. package/dist/robot/commands/server.js +29 -0
  93. package/dist/robot/commands/storagebox.d.ts +2 -0
  94. package/dist/robot/commands/storagebox.js +116 -0
  95. package/dist/robot/commands/subnet.d.ts +2 -0
  96. package/dist/robot/commands/subnet.js +21 -0
  97. package/dist/robot/commands/traffic.d.ts +2 -0
  98. package/dist/robot/commands/traffic.js +20 -0
  99. package/dist/robot/commands/vswitch.d.ts +2 -0
  100. package/dist/robot/commands/vswitch.js +64 -0
  101. package/dist/robot/commands/wol.d.ts +2 -0
  102. package/dist/robot/commands/wol.js +20 -0
  103. package/dist/robot/formatter.d.ts +58 -0
  104. package/dist/robot/formatter.js +500 -0
  105. package/dist/robot/types.d.ts +352 -0
  106. package/dist/robot/types.js +5 -0
  107. package/dist/shared/config.d.ts +86 -0
  108. package/dist/shared/config.js +273 -0
  109. package/dist/shared/formatter.d.ts +29 -0
  110. package/dist/shared/formatter.js +118 -0
  111. package/dist/shared/helpers.d.ts +17 -0
  112. package/dist/shared/helpers.js +72 -0
  113. package/dist/shared/reference.d.ts +2 -0
  114. package/dist/shared/reference.js +626 -0
  115. package/dist/types.d.ts +75 -0
  116. package/dist/types.js +1 -0
  117. package/package.json +112 -0
@@ -0,0 +1,54 @@
1
+ import { promptLogin, clearConfig, getCredentials, } from '../../shared/config.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import { asyncAction } from '../../shared/helpers.js';
4
+ export function registerAuthCommands(parent) {
5
+ const auth = parent.command('auth').description('Authentication management');
6
+ auth
7
+ .command('login')
8
+ .description('Interactively configure credentials')
9
+ .action(async () => {
10
+ try {
11
+ await promptLogin();
12
+ console.log('');
13
+ console.log(fmt.success('Authentication configured successfully.'));
14
+ }
15
+ catch (error) {
16
+ if (error instanceof Error && error.name === 'ExitPromptError') {
17
+ process.exit(0);
18
+ }
19
+ throw error;
20
+ }
21
+ });
22
+ auth
23
+ .command('logout')
24
+ .description('Clear saved credentials')
25
+ .action(async () => {
26
+ await clearConfig();
27
+ console.log(fmt.success('Credentials cleared.'));
28
+ });
29
+ auth
30
+ .command('status')
31
+ .description('Check authentication status')
32
+ .action(async () => {
33
+ const creds = await getCredentials();
34
+ if (creds) {
35
+ console.log(fmt.success(`Authenticated as: ${creds.user}`));
36
+ const sourceLabels = {
37
+ environment: 'environment variables',
38
+ keychain: 'keychain',
39
+ file: 'config file',
40
+ };
41
+ console.log(fmt.info('Stored in: ' + (sourceLabels[creds.source ?? ''] ?? 'unknown')));
42
+ }
43
+ else {
44
+ console.log(fmt.warning('Not authenticated. Run: hetzner auth login'));
45
+ }
46
+ });
47
+ auth
48
+ .command('test')
49
+ .description('Test API credentials')
50
+ .action(asyncAction(async (client) => {
51
+ const servers = await client.listServers();
52
+ console.log(fmt.success(`Authenticated successfully. Found ${servers.length} server(s).`));
53
+ }));
54
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerBootCommands(parent: Command): void;
@@ -0,0 +1,72 @@
1
+ import { asyncAction, output } from '../../shared/helpers.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import * as robotFmt from '../formatter.js';
4
+ export function registerBootCommands(parent) {
5
+ const boot = parent.command('boot').description('Boot configuration (rescue, linux, vnc, windows)');
6
+ boot
7
+ .command('status <server>')
8
+ .description('Show boot configuration status')
9
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
10
+ const { boot: bootConfig } = await client.getBootConfig(serverIdOrIp);
11
+ output(bootConfig, (b) => robotFmt.formatBootConfig(b, parseInt(serverIdOrIp) || 0), options);
12
+ }));
13
+ const rescue = boot.command('rescue').description('Rescue system management');
14
+ rescue
15
+ .command('activate <server>')
16
+ .description('Activate rescue system')
17
+ .option('-o, --os <os>', 'Operating system (linux, linuxold, vkvm)', 'linux')
18
+ .option('-a, --arch <arch>', 'Architecture (64 or 32)', '64')
19
+ .option('-k, --keys <fingerprints...>', 'SSH key fingerprints')
20
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
21
+ const { rescue: rsc } = await client.activateRescue(serverIdOrIp, options.os, parseInt(options.arch), options.keys);
22
+ console.log(robotFmt.formatRescueActivation(rsc));
23
+ }));
24
+ rescue
25
+ .command('deactivate <server>')
26
+ .description('Deactivate rescue system')
27
+ .action(asyncAction(async (client, serverIdOrIp) => {
28
+ await client.deactivateRescue(serverIdOrIp);
29
+ console.log(fmt.success('Rescue system deactivated.'));
30
+ }));
31
+ rescue
32
+ .command('last <server>')
33
+ .description('Show last rescue activation details')
34
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
35
+ const { rescue: rsc } = await client.getLastRescue(serverIdOrIp);
36
+ output(rsc, robotFmt.formatRescueActivation, options);
37
+ }));
38
+ const linux = boot.command('linux').description('Linux installation management');
39
+ linux
40
+ .command('activate <server>')
41
+ .description('Activate Linux installation')
42
+ .requiredOption('-d, --dist <dist>', 'Distribution (e.g., Debian-1210-bookworm-amd64-base)')
43
+ .option('-a, --arch <arch>', 'Architecture (64 or 32)', '64')
44
+ .option('-l, --lang <lang>', 'Language', 'en')
45
+ .option('-k, --keys <fingerprints...>', 'SSH key fingerprints')
46
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
47
+ const { linux: lnx } = await client.activateLinux(serverIdOrIp, options.dist, parseInt(options.arch), options.lang, options.keys);
48
+ console.log(robotFmt.formatLinuxActivation(lnx));
49
+ }));
50
+ linux
51
+ .command('deactivate <server>')
52
+ .description('Deactivate Linux installation')
53
+ .action(asyncAction(async (client, serverIdOrIp) => {
54
+ await client.deactivateLinux(serverIdOrIp);
55
+ console.log(fmt.success('Linux installation deactivated.'));
56
+ }));
57
+ linux
58
+ .command('options <server>')
59
+ .description('Show available Linux distributions')
60
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
61
+ const { linux: lnx } = await client.getLinux(serverIdOrIp);
62
+ output(lnx, (l) => {
63
+ const lines = [fmt.heading('Available Linux Distributions'), ''];
64
+ for (const dist of l.dist) {
65
+ lines.push(` ${dist}`);
66
+ }
67
+ lines.push('', fmt.info(`Languages: ${l.lang.join(', ')}`));
68
+ lines.push(fmt.info(`Architectures: ${l.arch.join(', ')}-bit`));
69
+ return lines.join('\n');
70
+ }, options);
71
+ }));
72
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCancelCommands(parent: Command): void;
@@ -0,0 +1,36 @@
1
+ import { asyncAction, output, confirmAction } from '../../shared/helpers.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import * as robotFmt from '../formatter.js';
4
+ export function registerCancelCommands(parent) {
5
+ const cancel = parent.command('cancel').description('Server cancellation');
6
+ cancel
7
+ .command('status <server>')
8
+ .description('Get cancellation status')
9
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
10
+ const { cancellation } = await client.getCancellation(serverIdOrIp);
11
+ output(cancellation, robotFmt.formatCancellation, options);
12
+ }));
13
+ cancel
14
+ .command('request <server>')
15
+ .description('Request server cancellation')
16
+ .option('--date <date>', 'Cancellation date (YYYY-MM-DD)')
17
+ .option('--reason <reasons...>', 'Cancellation reasons')
18
+ .option('-y, --yes', 'Skip confirmation')
19
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
20
+ if (!options.yes) {
21
+ console.log(fmt.warning('This will cancel your server!'));
22
+ }
23
+ if (!await confirmAction('Are you sure you want to cancel this server?', options))
24
+ return;
25
+ const { cancellation } = await client.cancelServer(serverIdOrIp, options.date, options.reason);
26
+ console.log(fmt.success('Cancellation requested.'));
27
+ console.log(robotFmt.formatCancellation(cancellation));
28
+ }));
29
+ cancel
30
+ .command('revoke <server>')
31
+ .description('Revoke server cancellation')
32
+ .action(asyncAction(async (client, serverIdOrIp) => {
33
+ await client.revokeCancellation(serverIdOrIp);
34
+ console.log(fmt.success('Cancellation revoked.'));
35
+ }));
36
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerFailoverCommands(parent: Command): void;
@@ -0,0 +1,42 @@
1
+ import { asyncAction, output, confirmAction } from '../../shared/helpers.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import * as robotFmt from '../formatter.js';
4
+ export function registerFailoverCommands(parent) {
5
+ const failover = parent.command('failover').description('Failover IP management');
6
+ failover
7
+ .command('list')
8
+ .alias('ls')
9
+ .description('List all failover IPs')
10
+ .action(asyncAction(async (client, options) => {
11
+ const failovers = await client.listFailovers();
12
+ output(failovers, robotFmt.formatFailoverList, options);
13
+ }));
14
+ failover
15
+ .command('get <ip>')
16
+ .alias('show')
17
+ .description('Get failover IP details')
18
+ .action(asyncAction(async (client, failoverIp, options) => {
19
+ const { failover: fo } = await client.getFailover(failoverIp);
20
+ output(fo, (f) => robotFmt.formatFailoverList([{ failover: f }]), options);
21
+ }));
22
+ failover
23
+ .command('switch <failover-ip> <target-server-ip>')
24
+ .description('Switch failover IP routing to another server')
25
+ .option('-y, --yes', 'Skip confirmation')
26
+ .action(asyncAction(async (client, failoverIp, targetServerIp, options) => {
27
+ if (!await confirmAction(`Route ${failoverIp} to ${targetServerIp}?`, options, true))
28
+ return;
29
+ const { failover: fo } = await client.switchFailover(failoverIp, targetServerIp);
30
+ console.log(robotFmt.formatFailoverSwitch(fo));
31
+ }));
32
+ failover
33
+ .command('delete <ip>')
34
+ .description('Delete failover IP routing')
35
+ .option('-y, --yes', 'Skip confirmation')
36
+ .action(asyncAction(async (client, failoverIp, options) => {
37
+ if (!await confirmAction(`Delete routing for ${failoverIp}?`, options))
38
+ return;
39
+ await client.deleteFailoverRouting(failoverIp);
40
+ console.log(fmt.success('Failover routing deleted.'));
41
+ }));
42
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerFirewallCommands(parent: Command): void;
@@ -0,0 +1,66 @@
1
+ import { asyncAction, output, confirmAction } from '../../shared/helpers.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import * as robotFmt from '../formatter.js';
4
+ export function registerFirewallCommands(parent) {
5
+ const firewall = parent.command('firewall').description('Firewall management');
6
+ firewall
7
+ .command('get <server>')
8
+ .alias('show')
9
+ .description('Get firewall configuration')
10
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
11
+ const { firewall: fw } = await client.getFirewall(serverIdOrIp);
12
+ output(fw, robotFmt.formatFirewall, options);
13
+ }));
14
+ firewall
15
+ .command('enable <server>')
16
+ .description('Enable firewall')
17
+ .action(asyncAction(async (client, serverIdOrIp) => {
18
+ await client.updateFirewall(serverIdOrIp, 'active');
19
+ console.log(fmt.success('Firewall enabled.'));
20
+ }));
21
+ firewall
22
+ .command('disable <server>')
23
+ .description('Disable firewall')
24
+ .action(asyncAction(async (client, serverIdOrIp) => {
25
+ await client.updateFirewall(serverIdOrIp, 'disabled');
26
+ console.log(fmt.success('Firewall disabled.'));
27
+ }));
28
+ firewall
29
+ .command('delete <server>')
30
+ .description('Delete all firewall rules')
31
+ .option('-y, --yes', 'Skip confirmation')
32
+ .action(asyncAction(async (client, serverIdOrIp, options) => {
33
+ if (!await confirmAction('Delete all firewall rules?', options))
34
+ return;
35
+ await client.deleteFirewall(serverIdOrIp);
36
+ console.log(fmt.success('Firewall rules deleted.'));
37
+ }));
38
+ const fwTemplate = firewall.command('template').description('Firewall template management');
39
+ fwTemplate
40
+ .command('list')
41
+ .alias('ls')
42
+ .description('List firewall templates')
43
+ .action(asyncAction(async (client, options) => {
44
+ const templates = await client.listFirewallTemplates();
45
+ output(templates, robotFmt.formatFirewallTemplateList, options);
46
+ }));
47
+ fwTemplate
48
+ .command('get <id>')
49
+ .alias('show')
50
+ .description('Get firewall template details')
51
+ .action(asyncAction(async (client, templateId, options) => {
52
+ const { firewall_template: tmpl } = await client.getFirewallTemplate(parseInt(templateId));
53
+ output(tmpl, (t) => robotFmt.formatFirewallTemplateList([{ firewall_template: t }]), options);
54
+ }));
55
+ fwTemplate
56
+ .command('delete <id>')
57
+ .alias('rm')
58
+ .description('Delete firewall template')
59
+ .option('-y, --yes', 'Skip confirmation')
60
+ .action(asyncAction(async (client, templateId, options) => {
61
+ if (!await confirmAction(`Delete firewall template ${templateId}?`, options))
62
+ return;
63
+ await client.deleteFirewallTemplate(parseInt(templateId));
64
+ console.log(fmt.success('Firewall template deleted.'));
65
+ }));
66
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerRobotCommands(parent: Command): void;
@@ -0,0 +1,36 @@
1
+ import { registerAuthCommands } from './auth.js';
2
+ import { registerServerCommands } from './server.js';
3
+ import { registerResetCommands } from './reset.js';
4
+ import { registerBootCommands } from './boot.js';
5
+ import { registerIpCommands } from './ip.js';
6
+ import { registerSubnetCommands } from './subnet.js';
7
+ import { registerFailoverCommands } from './failover.js';
8
+ import { registerRdnsCommands } from './rdns.js';
9
+ import { registerKeyCommands } from './key.js';
10
+ import { registerFirewallCommands } from './firewall.js';
11
+ import { registerVSwitchCommands } from './vswitch.js';
12
+ import { registerStorageBoxCommands } from './storagebox.js';
13
+ import { registerTrafficCommands } from './traffic.js';
14
+ import { registerWolCommands } from './wol.js';
15
+ import { registerCancelCommands } from './cancel.js';
16
+ import { registerOrderCommands } from './order.js';
17
+ import { registerInteractiveCommands } from './interactive.js';
18
+ export function registerRobotCommands(parent) {
19
+ registerAuthCommands(parent);
20
+ registerServerCommands(parent);
21
+ registerResetCommands(parent);
22
+ registerBootCommands(parent);
23
+ registerIpCommands(parent);
24
+ registerSubnetCommands(parent);
25
+ registerFailoverCommands(parent);
26
+ registerRdnsCommands(parent);
27
+ registerKeyCommands(parent);
28
+ registerFirewallCommands(parent);
29
+ registerVSwitchCommands(parent);
30
+ registerStorageBoxCommands(parent);
31
+ registerTrafficCommands(parent);
32
+ registerWolCommands(parent);
33
+ registerCancelCommands(parent);
34
+ registerOrderCommands(parent);
35
+ registerInteractiveCommands(parent);
36
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerInteractiveCommands(parent: Command): void;
@@ -0,0 +1,134 @@
1
+ import { select, confirm, checkbox } from '@inquirer/prompts';
2
+ import { asyncAction } from '../../shared/helpers.js';
3
+ import * as fmt from '../../shared/formatter.js';
4
+ import * as robotFmt from '../formatter.js';
5
+ export function registerInteractiveCommands(parent) {
6
+ parent
7
+ .command('interactive')
8
+ .alias('i')
9
+ .description('Interactive mode for common operations')
10
+ .action(asyncAction(async (client) => {
11
+ while (true) {
12
+ const action = await select({
13
+ message: 'What would you like to do?',
14
+ choices: [
15
+ { value: 'servers', name: 'List servers' },
16
+ { value: 'reset', name: 'Reset a server' },
17
+ { value: 'rescue', name: 'Activate rescue mode' },
18
+ { value: 'failover', name: 'Switch failover IP' },
19
+ { value: 'keys', name: 'Manage SSH keys' },
20
+ { value: 'exit', name: 'Exit' },
21
+ ],
22
+ pageSize: 10,
23
+ });
24
+ if (action === 'exit') {
25
+ console.log('Goodbye!');
26
+ break;
27
+ }
28
+ if (action === 'servers') {
29
+ const servers = await client.listServers();
30
+ console.log('');
31
+ console.log(robotFmt.formatServerList(servers));
32
+ console.log('');
33
+ }
34
+ if (action === 'reset') {
35
+ const servers = await client.listServers();
36
+ if (servers.length === 0) {
37
+ console.log(fmt.info('No servers found.'));
38
+ continue;
39
+ }
40
+ const selected = await checkbox({
41
+ message: 'Select servers to reset:',
42
+ choices: servers.map(({ server }) => ({
43
+ value: server.server_ip,
44
+ name: `${server.server_number} - ${server.server_ip} (${server.server_name || 'unnamed'})`,
45
+ })),
46
+ });
47
+ if (selected.length === 0) {
48
+ console.log('No servers selected.');
49
+ continue;
50
+ }
51
+ const resetType = (await select({
52
+ message: 'Select reset type:',
53
+ choices: [
54
+ { value: 'sw', name: 'Software reset (ACPI)' },
55
+ { value: 'hw', name: 'Hardware reset (forced)' },
56
+ { value: 'power', name: 'Power cycle' },
57
+ ],
58
+ }));
59
+ const confirmed = await confirm({
60
+ message: `Reset ${selected.length} server(s) with ${resetType}?`,
61
+ default: false,
62
+ });
63
+ if (confirmed) {
64
+ for (const srv of selected) {
65
+ try {
66
+ const { reset: rst } = await client.resetServer(srv, resetType);
67
+ console.log(robotFmt.formatResetResult(rst, resetType));
68
+ }
69
+ catch (error) {
70
+ console.log(fmt.error(`Failed to reset ${srv}: ${error instanceof Error ? error.message : 'Unknown'}`));
71
+ }
72
+ }
73
+ }
74
+ }
75
+ if (action === 'rescue') {
76
+ const servers = await client.listServers();
77
+ if (servers.length === 0) {
78
+ console.log(fmt.info('No servers found.'));
79
+ continue;
80
+ }
81
+ const serverIp = await select({
82
+ message: 'Select server:',
83
+ choices: servers.map(({ server }) => ({
84
+ value: server.server_ip,
85
+ name: `${server.server_number} - ${server.server_ip} (${server.server_name || 'unnamed'})`,
86
+ })),
87
+ });
88
+ const os = await select({
89
+ message: 'Select rescue OS:',
90
+ choices: [
91
+ { value: 'linux', name: 'Linux (64-bit)' },
92
+ { value: 'linuxold', name: 'Linux (32-bit)' },
93
+ { value: 'vkvm', name: 'vKVM' },
94
+ ],
95
+ });
96
+ const { rescue } = await client.activateRescue(serverIp, os, 64);
97
+ console.log('');
98
+ console.log(robotFmt.formatRescueActivation(rescue));
99
+ }
100
+ if (action === 'failover') {
101
+ const failovers = await client.listFailovers();
102
+ if (failovers.length === 0) {
103
+ console.log(fmt.info('No failover IPs found.'));
104
+ continue;
105
+ }
106
+ const failoverIp = await select({
107
+ message: 'Select failover IP:',
108
+ choices: failovers.map(({ failover }) => ({
109
+ value: failover.ip,
110
+ name: `${failover.ip} -> ${failover.active_server_ip}`,
111
+ })),
112
+ });
113
+ const servers = await client.listServers();
114
+ const targetIp = await select({
115
+ message: 'Route to server:',
116
+ choices: servers.map(({ server }) => ({
117
+ value: server.server_ip,
118
+ name: `${server.server_number} - ${server.server_ip}`,
119
+ })),
120
+ });
121
+ const { failover: fo } = await client.switchFailover(failoverIp, targetIp);
122
+ console.log('');
123
+ console.log(robotFmt.formatFailoverSwitch(fo));
124
+ }
125
+ if (action === 'keys') {
126
+ const keys = await client.listSshKeys();
127
+ console.log('');
128
+ console.log(robotFmt.formatSshKeyList(keys));
129
+ console.log('');
130
+ }
131
+ console.log('');
132
+ }
133
+ }));
134
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerIpCommands(parent: Command): void;
@@ -0,0 +1,52 @@
1
+ import { asyncAction, output } from '../../shared/helpers.js';
2
+ import * as fmt from '../../shared/formatter.js';
3
+ import * as robotFmt from '../formatter.js';
4
+ export function registerIpCommands(parent) {
5
+ const ip = parent.command('ip').alias('ips').description('IP address management');
6
+ ip.command('list')
7
+ .alias('ls')
8
+ .description('List all IPs')
9
+ .action(asyncAction(async (client, options) => {
10
+ const ips = await client.listIps();
11
+ output(ips, robotFmt.formatIpList, options);
12
+ }));
13
+ ip.command('get <ip>')
14
+ .alias('show')
15
+ .description('Get IP details')
16
+ .action(asyncAction(async (client, ipAddr, options) => {
17
+ const { ip: ipData } = await client.getIp(ipAddr);
18
+ output(ipData, robotFmt.formatIpDetails, options);
19
+ }));
20
+ ip.command('update <ip>')
21
+ .description('Update IP traffic warning settings')
22
+ .option('--warnings <enabled>', 'Enable/disable traffic warnings (true/false)')
23
+ .option('--hourly <mb>', 'Hourly traffic limit in MB')
24
+ .option('--daily <mb>', 'Daily traffic limit in MB')
25
+ .option('--monthly <gb>', 'Monthly traffic limit in GB')
26
+ .action(asyncAction(async (client, ipAddr, options) => {
27
+ await client.updateIp(ipAddr, options.warnings ? options.warnings === 'true' : undefined, options.hourly ? parseInt(options.hourly) : undefined, options.daily ? parseInt(options.daily) : undefined, options.monthly ? parseInt(options.monthly) : undefined);
28
+ console.log(fmt.success(`IP ${ipAddr} updated.`));
29
+ }));
30
+ const mac = ip.command('mac').description('MAC address management');
31
+ mac
32
+ .command('get <ip>')
33
+ .description('Get separate MAC address for IP')
34
+ .action(asyncAction(async (client, ipAddr, options) => {
35
+ const { mac: macData } = await client.getIpMac(ipAddr);
36
+ output(macData, (m) => `IP: ${m.ip}\nMAC: ${m.mac}`, options);
37
+ }));
38
+ mac
39
+ .command('generate <ip>')
40
+ .description('Generate separate MAC address for IP')
41
+ .action(asyncAction(async (client, ipAddr) => {
42
+ const { mac: macData } = await client.generateIpMac(ipAddr);
43
+ console.log(fmt.success(`Generated MAC: ${macData.mac}`));
44
+ }));
45
+ mac
46
+ .command('delete <ip>')
47
+ .description('Delete separate MAC address')
48
+ .action(asyncAction(async (client, ipAddr) => {
49
+ await client.deleteIpMac(ipAddr);
50
+ console.log(fmt.success('MAC address deleted.'));
51
+ }));
52
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerKeyCommands(parent: Command): void;
@@ -0,0 +1,64 @@
1
+ import { input } from '@inquirer/prompts';
2
+ import { readFileSync } from 'node:fs';
3
+ import { asyncAction, output, confirmAction } from '../../shared/helpers.js';
4
+ import * as fmt from '../../shared/formatter.js';
5
+ import * as robotFmt from '../formatter.js';
6
+ export function registerKeyCommands(parent) {
7
+ const key = parent.command('key').alias('keys').description('SSH key management');
8
+ key
9
+ .command('list')
10
+ .alias('ls')
11
+ .description('List all SSH keys')
12
+ .action(asyncAction(async (client, options) => {
13
+ const keys = await client.listSshKeys();
14
+ output(keys, robotFmt.formatSshKeyList, options);
15
+ }));
16
+ key
17
+ .command('get <fingerprint>')
18
+ .alias('show')
19
+ .description('Get SSH key details')
20
+ .action(asyncAction(async (client, fingerprint, options) => {
21
+ const { key: keyData } = await client.getSshKey(fingerprint);
22
+ output(keyData, robotFmt.formatSshKeyDetails, options);
23
+ }));
24
+ key
25
+ .command('add <name>')
26
+ .description('Add a new SSH key')
27
+ .option('-f, --file <path>', 'Path to public key file')
28
+ .option('-d, --data <key>', 'Public key data')
29
+ .action(asyncAction(async (client, name, options) => {
30
+ let keyData;
31
+ if (options.file) {
32
+ keyData = readFileSync(options.file, 'utf-8').trim();
33
+ }
34
+ else if (options.data) {
35
+ keyData = options.data;
36
+ }
37
+ else {
38
+ keyData = await input({
39
+ message: 'Paste public key:',
40
+ validate: (v) => v.length > 0 || 'Key data is required',
41
+ });
42
+ }
43
+ const { key: newKey } = await client.createSshKey(name, keyData);
44
+ console.log(fmt.success(`SSH key added: ${newKey.fingerprint}`));
45
+ }));
46
+ key
47
+ .command('rename <fingerprint> <name>')
48
+ .description('Rename an SSH key')
49
+ .action(asyncAction(async (client, fingerprint, name) => {
50
+ await client.updateSshKey(fingerprint, name);
51
+ console.log(fmt.success(`SSH key renamed to: ${name}`));
52
+ }));
53
+ key
54
+ .command('delete <fingerprint>')
55
+ .alias('rm')
56
+ .description('Delete an SSH key')
57
+ .option('-y, --yes', 'Skip confirmation')
58
+ .action(asyncAction(async (client, fingerprint, options) => {
59
+ if (!await confirmAction(`Delete SSH key ${fingerprint}?`, options))
60
+ return;
61
+ await client.deleteSshKey(fingerprint);
62
+ console.log(fmt.success('SSH key deleted.'));
63
+ }));
64
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerOrderCommands(parent: Command): void;
@@ -0,0 +1,33 @@
1
+ import { asyncAction, output } from '../../shared/helpers.js';
2
+ import * as robotFmt from '../formatter.js';
3
+ export function registerOrderCommands(parent) {
4
+ const order = parent.command('order').description('Server ordering');
5
+ order
6
+ .command('products')
7
+ .description('List available server products')
8
+ .action(asyncAction(async (client, options) => {
9
+ const products = await client.listServerProducts();
10
+ output(products, robotFmt.formatServerProductList, options);
11
+ }));
12
+ order
13
+ .command('market')
14
+ .description('List server market (auction) products')
15
+ .action(asyncAction(async (client, options) => {
16
+ const products = await client.listServerMarketProducts();
17
+ output(products, robotFmt.formatServerMarketProductList, options);
18
+ }));
19
+ order
20
+ .command('transactions')
21
+ .description('List order transactions')
22
+ .action(asyncAction(async (client, options) => {
23
+ const transactions = await client.listServerTransactions();
24
+ output(transactions, robotFmt.formatTransactionList, options);
25
+ }));
26
+ order
27
+ .command('transaction <id>')
28
+ .description('Get order transaction details')
29
+ .action(asyncAction(async (client, transactionId, options) => {
30
+ const { transaction } = await client.getServerTransaction(transactionId);
31
+ output(transaction, (t) => robotFmt.formatTransactionList([{ transaction: t }]), options);
32
+ }));
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerRdnsCommands(parent: Command): void;