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.
- package/LICENSE +21 -0
- package/README.md +907 -0
- package/dist/auction/client.d.ts +4 -0
- package/dist/auction/client.js +103 -0
- package/dist/auction/commands.d.ts +2 -0
- package/dist/auction/commands.js +138 -0
- package/dist/auction/formatter.d.ts +3 -0
- package/dist/auction/formatter.js +87 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +39 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +4 -0
- package/dist/cloud/client.d.ts +511 -0
- package/dist/cloud/client.js +706 -0
- package/dist/cloud/commands/certificate.d.ts +2 -0
- package/dist/cloud/commands/certificate.js +77 -0
- package/dist/cloud/commands/context.d.ts +2 -0
- package/dist/cloud/commands/context.js +78 -0
- package/dist/cloud/commands/datacenter.d.ts +2 -0
- package/dist/cloud/commands/datacenter.js +20 -0
- package/dist/cloud/commands/firewall.d.ts +2 -0
- package/dist/cloud/commands/firewall.js +77 -0
- package/dist/cloud/commands/floating-ip.d.ts +2 -0
- package/dist/cloud/commands/floating-ip.js +83 -0
- package/dist/cloud/commands/image.d.ts +2 -0
- package/dist/cloud/commands/image.js +60 -0
- package/dist/cloud/commands/index.d.ts +2 -0
- package/dist/cloud/commands/index.js +41 -0
- package/dist/cloud/commands/iso.d.ts +2 -0
- package/dist/cloud/commands/iso.js +22 -0
- package/dist/cloud/commands/load-balancer-type.d.ts +2 -0
- package/dist/cloud/commands/load-balancer-type.js +20 -0
- package/dist/cloud/commands/load-balancer.d.ts +2 -0
- package/dist/cloud/commands/load-balancer.js +177 -0
- package/dist/cloud/commands/location.d.ts +2 -0
- package/dist/cloud/commands/location.js +20 -0
- package/dist/cloud/commands/network.d.ts +2 -0
- package/dist/cloud/commands/network.js +96 -0
- package/dist/cloud/commands/placement-group.d.ts +2 -0
- package/dist/cloud/commands/placement-group.js +53 -0
- package/dist/cloud/commands/primary-ip.d.ts +2 -0
- package/dist/cloud/commands/primary-ip.js +83 -0
- package/dist/cloud/commands/server-type.d.ts +2 -0
- package/dist/cloud/commands/server-type.js +20 -0
- package/dist/cloud/commands/server.d.ts +2 -0
- package/dist/cloud/commands/server.js +260 -0
- package/dist/cloud/commands/ssh-key.d.ts +2 -0
- package/dist/cloud/commands/ssh-key.js +63 -0
- package/dist/cloud/commands/volume.d.ts +2 -0
- package/dist/cloud/commands/volume.js +92 -0
- package/dist/cloud/context.d.ts +28 -0
- package/dist/cloud/context.js +172 -0
- package/dist/cloud/formatter.d.ts +37 -0
- package/dist/cloud/formatter.js +413 -0
- package/dist/cloud/helpers.d.ts +18 -0
- package/dist/cloud/helpers.js +48 -0
- package/dist/cloud/types.d.ts +398 -0
- package/dist/cloud/types.js +5 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -0
- package/dist/formatter.d.ts +3 -0
- package/dist/formatter.js +6 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +17 -0
- package/dist/robot/client.d.ts +256 -0
- package/dist/robot/client.js +656 -0
- package/dist/robot/commands/auth.d.ts +2 -0
- package/dist/robot/commands/auth.js +54 -0
- package/dist/robot/commands/boot.d.ts +2 -0
- package/dist/robot/commands/boot.js +72 -0
- package/dist/robot/commands/cancel.d.ts +2 -0
- package/dist/robot/commands/cancel.js +36 -0
- package/dist/robot/commands/failover.d.ts +2 -0
- package/dist/robot/commands/failover.js +42 -0
- package/dist/robot/commands/firewall.d.ts +2 -0
- package/dist/robot/commands/firewall.js +66 -0
- package/dist/robot/commands/index.d.ts +2 -0
- package/dist/robot/commands/index.js +36 -0
- package/dist/robot/commands/interactive.d.ts +2 -0
- package/dist/robot/commands/interactive.js +134 -0
- package/dist/robot/commands/ip.d.ts +2 -0
- package/dist/robot/commands/ip.js +52 -0
- package/dist/robot/commands/key.d.ts +2 -0
- package/dist/robot/commands/key.js +64 -0
- package/dist/robot/commands/order.d.ts +2 -0
- package/dist/robot/commands/order.js +33 -0
- package/dist/robot/commands/rdns.d.ts +2 -0
- package/dist/robot/commands/rdns.js +41 -0
- package/dist/robot/commands/reset.d.ts +2 -0
- package/dist/robot/commands/reset.js +77 -0
- package/dist/robot/commands/server.d.ts +2 -0
- package/dist/robot/commands/server.js +29 -0
- package/dist/robot/commands/storagebox.d.ts +2 -0
- package/dist/robot/commands/storagebox.js +116 -0
- package/dist/robot/commands/subnet.d.ts +2 -0
- package/dist/robot/commands/subnet.js +21 -0
- package/dist/robot/commands/traffic.d.ts +2 -0
- package/dist/robot/commands/traffic.js +20 -0
- package/dist/robot/commands/vswitch.d.ts +2 -0
- package/dist/robot/commands/vswitch.js +64 -0
- package/dist/robot/commands/wol.d.ts +2 -0
- package/dist/robot/commands/wol.js +20 -0
- package/dist/robot/formatter.d.ts +58 -0
- package/dist/robot/formatter.js +500 -0
- package/dist/robot/types.d.ts +352 -0
- package/dist/robot/types.js +5 -0
- package/dist/shared/config.d.ts +86 -0
- package/dist/shared/config.js +273 -0
- package/dist/shared/formatter.d.ts +29 -0
- package/dist/shared/formatter.js +118 -0
- package/dist/shared/helpers.d.ts +17 -0
- package/dist/shared/helpers.js +72 -0
- package/dist/shared/reference.d.ts +2 -0
- package/dist/shared/reference.js +626 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +1 -0
- 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,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,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,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,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,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,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,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,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,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
|
+
}
|