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,413 @@
|
|
|
1
|
+
import { colorize, info, heading, formatStatus, formatBytes, formatDate, createTable, } from '../shared/formatter.js';
|
|
2
|
+
// Context
|
|
3
|
+
export function formatContextList(contexts) {
|
|
4
|
+
if (contexts.length === 0) {
|
|
5
|
+
return info('No contexts configured. Run: hetzner cloud context create');
|
|
6
|
+
}
|
|
7
|
+
const table = createTable(['Name', 'Active']);
|
|
8
|
+
for (const ctx of contexts) {
|
|
9
|
+
table.push([
|
|
10
|
+
ctx.name,
|
|
11
|
+
ctx.active ? colorize('*', 'green') : '',
|
|
12
|
+
]);
|
|
13
|
+
}
|
|
14
|
+
return table.toString();
|
|
15
|
+
}
|
|
16
|
+
// Datacenters
|
|
17
|
+
export function formatDatacenterList(datacenters) {
|
|
18
|
+
if (datacenters.length === 0)
|
|
19
|
+
return info('No datacenters found.');
|
|
20
|
+
const table = createTable(['ID', 'Name', 'Description', 'Location']);
|
|
21
|
+
for (const dc of datacenters) {
|
|
22
|
+
table.push([dc.id.toString(), dc.name, dc.description, dc.location.name]);
|
|
23
|
+
}
|
|
24
|
+
return table.toString();
|
|
25
|
+
}
|
|
26
|
+
export function formatDatacenterDetails(dc) {
|
|
27
|
+
const lines = [];
|
|
28
|
+
lines.push(heading(`Datacenter: ${dc.name}`));
|
|
29
|
+
lines.push('');
|
|
30
|
+
const table = createTable(['Property', 'Value']);
|
|
31
|
+
table.push(['ID', dc.id.toString()], ['Name', dc.name], ['Description', dc.description], ['Location', dc.location.description], ['City', dc.location.city], ['Country', dc.location.country], ['Network Zone', dc.location.network_zone]);
|
|
32
|
+
lines.push(table.toString());
|
|
33
|
+
return lines.join('\n');
|
|
34
|
+
}
|
|
35
|
+
// Locations
|
|
36
|
+
export function formatLocationList(locations) {
|
|
37
|
+
if (locations.length === 0)
|
|
38
|
+
return info('No locations found.');
|
|
39
|
+
const table = createTable(['ID', 'Name', 'Description', 'City', 'Country', 'Network Zone']);
|
|
40
|
+
for (const loc of locations) {
|
|
41
|
+
table.push([loc.id.toString(), loc.name, loc.description, loc.city, loc.country, loc.network_zone]);
|
|
42
|
+
}
|
|
43
|
+
return table.toString();
|
|
44
|
+
}
|
|
45
|
+
export function formatLocationDetails(loc) {
|
|
46
|
+
const lines = [];
|
|
47
|
+
lines.push(heading(`Location: ${loc.name}`));
|
|
48
|
+
lines.push('');
|
|
49
|
+
const table = createTable(['Property', 'Value']);
|
|
50
|
+
table.push(['ID', loc.id.toString()], ['Name', loc.name], ['Description', loc.description], ['City', loc.city], ['Country', loc.country], ['Latitude', loc.latitude.toString()], ['Longitude', loc.longitude.toString()], ['Network Zone', loc.network_zone]);
|
|
51
|
+
lines.push(table.toString());
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
}
|
|
54
|
+
// Server Types
|
|
55
|
+
export function formatServerTypeList(types) {
|
|
56
|
+
if (types.length === 0)
|
|
57
|
+
return info('No server types found.');
|
|
58
|
+
const table = createTable(['ID', 'Name', 'Cores', 'Memory', 'Disk', 'Storage', 'CPU', 'Arch']);
|
|
59
|
+
for (const st of types) {
|
|
60
|
+
table.push([
|
|
61
|
+
st.id.toString(),
|
|
62
|
+
st.name,
|
|
63
|
+
st.cores.toString(),
|
|
64
|
+
`${st.memory} GB`,
|
|
65
|
+
`${st.disk} GB`,
|
|
66
|
+
st.storage_type,
|
|
67
|
+
st.cpu_type,
|
|
68
|
+
st.architecture,
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
return table.toString();
|
|
72
|
+
}
|
|
73
|
+
export function formatServerTypeDetails(st) {
|
|
74
|
+
const lines = [];
|
|
75
|
+
lines.push(heading(`Server Type: ${st.name}`));
|
|
76
|
+
lines.push('');
|
|
77
|
+
const table = createTable(['Property', 'Value']);
|
|
78
|
+
table.push(['ID', st.id.toString()], ['Name', st.name], ['Description', st.description], ['Cores', st.cores.toString()], ['Memory', `${st.memory} GB`], ['Disk', `${st.disk} GB`], ['Storage Type', st.storage_type], ['CPU Type', st.cpu_type], ['Architecture', st.architecture], ['Deprecated', st.deprecated ? colorize('Yes', 'red') : 'No']);
|
|
79
|
+
lines.push(table.toString());
|
|
80
|
+
if (st.prices.length > 0) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(colorize('Pricing:', 'bold'));
|
|
83
|
+
const priceTable = createTable(['Location', 'Hourly', 'Monthly', 'Traffic']);
|
|
84
|
+
for (const p of st.prices) {
|
|
85
|
+
priceTable.push([p.location, `€${p.price_hourly.gross}`, `€${p.price_monthly.gross}`, formatBytes(p.included_traffic)]);
|
|
86
|
+
}
|
|
87
|
+
lines.push(priceTable.toString());
|
|
88
|
+
}
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
// Load Balancer Types
|
|
92
|
+
export function formatLoadBalancerTypeList(types) {
|
|
93
|
+
if (types.length === 0)
|
|
94
|
+
return info('No load balancer types found.');
|
|
95
|
+
const table = createTable(['ID', 'Name', 'Description', 'Max Conn', 'Max Services', 'Max Targets']);
|
|
96
|
+
for (const lbt of types) {
|
|
97
|
+
table.push([
|
|
98
|
+
lbt.id.toString(),
|
|
99
|
+
lbt.name,
|
|
100
|
+
lbt.description,
|
|
101
|
+
lbt.max_connections.toString(),
|
|
102
|
+
lbt.max_services.toString(),
|
|
103
|
+
lbt.max_targets.toString(),
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
return table.toString();
|
|
107
|
+
}
|
|
108
|
+
export function formatLoadBalancerTypeDetails(lbt) {
|
|
109
|
+
const lines = [];
|
|
110
|
+
lines.push(heading(`Load Balancer Type: ${lbt.name}`));
|
|
111
|
+
lines.push('');
|
|
112
|
+
const table = createTable(['Property', 'Value']);
|
|
113
|
+
table.push(['ID', lbt.id.toString()], ['Name', lbt.name], ['Description', lbt.description], ['Max Connections', lbt.max_connections.toString()], ['Max Services', lbt.max_services.toString()], ['Max Targets', lbt.max_targets.toString()], ['Max Certificates', lbt.max_assigned_certificates.toString()], ['Deprecated', lbt.deprecated ? colorize('Yes', 'red') : 'No']);
|
|
114
|
+
lines.push(table.toString());
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
// ISOs
|
|
118
|
+
export function formatIsoList(isos) {
|
|
119
|
+
if (isos.length === 0)
|
|
120
|
+
return info('No ISOs found.');
|
|
121
|
+
const table = createTable(['ID', 'Name', 'Type', 'Architecture']);
|
|
122
|
+
for (const iso of isos) {
|
|
123
|
+
table.push([iso.id.toString(), iso.name, iso.type, iso.architecture || '-']);
|
|
124
|
+
}
|
|
125
|
+
return table.toString();
|
|
126
|
+
}
|
|
127
|
+
export function formatIsoDetails(iso) {
|
|
128
|
+
const lines = [];
|
|
129
|
+
lines.push(heading(`ISO: ${iso.name}`));
|
|
130
|
+
lines.push('');
|
|
131
|
+
const table = createTable(['Property', 'Value']);
|
|
132
|
+
table.push(['ID', iso.id.toString()], ['Name', iso.name], ['Description', iso.description], ['Type', iso.type], ['Architecture', iso.architecture || '-']);
|
|
133
|
+
lines.push(table.toString());
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
// Cloud Servers
|
|
137
|
+
export function formatCloudServerList(servers) {
|
|
138
|
+
if (servers.length === 0)
|
|
139
|
+
return info('No servers found.');
|
|
140
|
+
const table = createTable(['ID', 'Name', 'Status', 'Type', 'DC', 'IPv4', 'Labels']);
|
|
141
|
+
for (const srv of servers) {
|
|
142
|
+
const labels = Object.entries(srv.labels).map(([k, v]) => `${k}=${v}`).join(', ') || '-';
|
|
143
|
+
table.push([
|
|
144
|
+
srv.id.toString(),
|
|
145
|
+
srv.name,
|
|
146
|
+
formatStatus(srv.status),
|
|
147
|
+
srv.server_type.name,
|
|
148
|
+
srv.datacenter.name,
|
|
149
|
+
srv.public_net.ipv4?.ip || '-',
|
|
150
|
+
labels.length > 30 ? labels.substring(0, 27) + '...' : labels,
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
return table.toString();
|
|
154
|
+
}
|
|
155
|
+
export function formatCloudServerDetails(srv) {
|
|
156
|
+
const lines = [];
|
|
157
|
+
lines.push(heading(`Server: ${srv.name}`));
|
|
158
|
+
lines.push('');
|
|
159
|
+
const table = createTable(['Property', 'Value']);
|
|
160
|
+
table.push(['ID', srv.id.toString()], ['Name', srv.name], ['Status', formatStatus(srv.status)], ['Server Type', `${srv.server_type.name} (${srv.server_type.cores} cores, ${srv.server_type.memory} GB RAM)`], ['Datacenter', `${srv.datacenter.name} (${srv.datacenter.location.city})`], ['Image', srv.image ? `${srv.image.description} (${srv.image.name || srv.image.id})` : '-'], ['IPv4', srv.public_net.ipv4?.ip || '-'], ['IPv6', srv.public_net.ipv6?.ip || '-'], ['Primary Disk', `${srv.primary_disk_size} GB`], ['Rescue', srv.rescue_enabled ? colorize('Enabled', 'yellow') : 'Disabled'], ['Locked', srv.locked ? colorize('Yes', 'red') : 'No'], ['Backup Window', srv.backup_window || 'Disabled'], ['Protection', srv.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(srv.created)]);
|
|
161
|
+
lines.push(table.toString());
|
|
162
|
+
if (Object.keys(srv.labels).length > 0) {
|
|
163
|
+
lines.push('');
|
|
164
|
+
lines.push(colorize('Labels:', 'bold'));
|
|
165
|
+
for (const [k, v] of Object.entries(srv.labels)) {
|
|
166
|
+
lines.push(` ${k} = ${v}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (srv.private_net.length > 0) {
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(colorize('Private Networks:', 'bold'));
|
|
172
|
+
const netTable = createTable(['Network', 'IP', 'Alias IPs']);
|
|
173
|
+
for (const pn of srv.private_net) {
|
|
174
|
+
netTable.push([pn.network.toString(), pn.ip, pn.alias_ips.join(', ') || '-']);
|
|
175
|
+
}
|
|
176
|
+
lines.push(netTable.toString());
|
|
177
|
+
}
|
|
178
|
+
if (srv.volumes.length > 0) {
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push(colorize('Volumes:', 'bold'));
|
|
181
|
+
lines.push(` ${srv.volumes.join(', ')}`);
|
|
182
|
+
}
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
// Networks
|
|
186
|
+
export function formatNetworkList(networks) {
|
|
187
|
+
if (networks.length === 0)
|
|
188
|
+
return info('No networks found.');
|
|
189
|
+
const table = createTable(['ID', 'Name', 'IP Range', 'Subnets', 'Servers']);
|
|
190
|
+
for (const net of networks) {
|
|
191
|
+
table.push([net.id.toString(), net.name, net.ip_range, net.subnets.length.toString(), net.servers.length.toString()]);
|
|
192
|
+
}
|
|
193
|
+
return table.toString();
|
|
194
|
+
}
|
|
195
|
+
export function formatNetworkDetails(net) {
|
|
196
|
+
const lines = [];
|
|
197
|
+
lines.push(heading(`Network: ${net.name}`));
|
|
198
|
+
lines.push('');
|
|
199
|
+
const table = createTable(['Property', 'Value']);
|
|
200
|
+
table.push(['ID', net.id.toString()], ['Name', net.name], ['IP Range', net.ip_range], ['Servers', net.servers.length.toString()], ['Protection', net.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(net.created)]);
|
|
201
|
+
lines.push(table.toString());
|
|
202
|
+
if (net.subnets.length > 0) {
|
|
203
|
+
lines.push('');
|
|
204
|
+
lines.push(colorize('Subnets:', 'bold'));
|
|
205
|
+
const subTable = createTable(['IP Range', 'Type', 'Network Zone', 'Gateway']);
|
|
206
|
+
for (const s of net.subnets) {
|
|
207
|
+
subTable.push([s.ip_range, s.type, s.network_zone, s.gateway]);
|
|
208
|
+
}
|
|
209
|
+
lines.push(subTable.toString());
|
|
210
|
+
}
|
|
211
|
+
if (net.routes.length > 0) {
|
|
212
|
+
lines.push('');
|
|
213
|
+
lines.push(colorize('Routes:', 'bold'));
|
|
214
|
+
const routeTable = createTable(['Destination', 'Gateway']);
|
|
215
|
+
for (const r of net.routes) {
|
|
216
|
+
routeTable.push([r.destination, r.gateway]);
|
|
217
|
+
}
|
|
218
|
+
lines.push(routeTable.toString());
|
|
219
|
+
}
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
// Cloud Firewalls
|
|
223
|
+
export function formatCloudFirewallList(firewalls) {
|
|
224
|
+
if (firewalls.length === 0)
|
|
225
|
+
return info('No firewalls found.');
|
|
226
|
+
const table = createTable(['ID', 'Name', 'Rules', 'Applied To', 'Created']);
|
|
227
|
+
for (const fw of firewalls) {
|
|
228
|
+
table.push([fw.id.toString(), fw.name, fw.rules.length.toString(), fw.applied_to.length.toString(), formatDate(fw.created)]);
|
|
229
|
+
}
|
|
230
|
+
return table.toString();
|
|
231
|
+
}
|
|
232
|
+
export function formatCloudFirewallDetails(fw) {
|
|
233
|
+
const lines = [];
|
|
234
|
+
lines.push(heading(`Firewall: ${fw.name}`));
|
|
235
|
+
lines.push('');
|
|
236
|
+
const table = createTable(['Property', 'Value']);
|
|
237
|
+
table.push(['ID', fw.id.toString()], ['Name', fw.name], ['Created', formatDate(fw.created)]);
|
|
238
|
+
lines.push(table.toString());
|
|
239
|
+
if (fw.rules.length > 0) {
|
|
240
|
+
lines.push('');
|
|
241
|
+
lines.push(colorize('Rules:', 'bold'));
|
|
242
|
+
const ruleTable = createTable(['Direction', 'Protocol', 'Port', 'Source IPs', 'Description']);
|
|
243
|
+
for (const r of fw.rules) {
|
|
244
|
+
ruleTable.push([r.direction, r.protocol, r.port || 'any', r.source_ips.join(', ').substring(0, 30) || '-', r.description || '-']);
|
|
245
|
+
}
|
|
246
|
+
lines.push(ruleTable.toString());
|
|
247
|
+
}
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
}
|
|
250
|
+
// Floating IPs
|
|
251
|
+
export function formatFloatingIpList(ips) {
|
|
252
|
+
if (ips.length === 0)
|
|
253
|
+
return info('No floating IPs found.');
|
|
254
|
+
const table = createTable(['ID', 'Name', 'IP', 'Type', 'Server', 'Location', 'Blocked']);
|
|
255
|
+
for (const ip of ips) {
|
|
256
|
+
table.push([ip.id.toString(), ip.name, ip.ip, ip.type, ip.server?.toString() || '-', ip.home_location.name, ip.blocked ? colorize('Yes', 'red') : 'No']);
|
|
257
|
+
}
|
|
258
|
+
return table.toString();
|
|
259
|
+
}
|
|
260
|
+
export function formatFloatingIpDetails(ip) {
|
|
261
|
+
const lines = [];
|
|
262
|
+
lines.push(heading(`Floating IP: ${ip.name}`));
|
|
263
|
+
lines.push('');
|
|
264
|
+
const table = createTable(['Property', 'Value']);
|
|
265
|
+
table.push(['ID', ip.id.toString()], ['Name', ip.name], ['Description', ip.description || '-'], ['IP', ip.ip], ['Type', ip.type], ['Server', ip.server?.toString() || 'Unassigned'], ['Home Location', ip.home_location.name], ['Blocked', ip.blocked ? colorize('Yes', 'red') : 'No'], ['Protection', ip.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(ip.created)]);
|
|
266
|
+
lines.push(table.toString());
|
|
267
|
+
return lines.join('\n');
|
|
268
|
+
}
|
|
269
|
+
// Primary IPs
|
|
270
|
+
export function formatPrimaryIpList(ips) {
|
|
271
|
+
if (ips.length === 0)
|
|
272
|
+
return info('No primary IPs found.');
|
|
273
|
+
const table = createTable(['ID', 'Name', 'IP', 'Type', 'Assignee', 'DC', 'Auto Delete']);
|
|
274
|
+
for (const ip of ips) {
|
|
275
|
+
table.push([ip.id.toString(), ip.name, ip.ip, ip.type, ip.assignee_id?.toString() || '-', ip.datacenter.name, ip.auto_delete ? 'Yes' : 'No']);
|
|
276
|
+
}
|
|
277
|
+
return table.toString();
|
|
278
|
+
}
|
|
279
|
+
export function formatPrimaryIpDetails(ip) {
|
|
280
|
+
const lines = [];
|
|
281
|
+
lines.push(heading(`Primary IP: ${ip.name}`));
|
|
282
|
+
lines.push('');
|
|
283
|
+
const table = createTable(['Property', 'Value']);
|
|
284
|
+
table.push(['ID', ip.id.toString()], ['Name', ip.name], ['IP', ip.ip], ['Type', ip.type], ['Assignee', ip.assignee_id?.toString() || 'Unassigned'], ['Datacenter', ip.datacenter.name], ['Auto Delete', ip.auto_delete ? 'Yes' : 'No'], ['Blocked', ip.blocked ? colorize('Yes', 'red') : 'No'], ['Protection', ip.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(ip.created)]);
|
|
285
|
+
lines.push(table.toString());
|
|
286
|
+
return lines.join('\n');
|
|
287
|
+
}
|
|
288
|
+
// Volumes
|
|
289
|
+
export function formatVolumeList(volumes) {
|
|
290
|
+
if (volumes.length === 0)
|
|
291
|
+
return info('No volumes found.');
|
|
292
|
+
const table = createTable(['ID', 'Name', 'Size', 'Server', 'Status', 'Location', 'Format']);
|
|
293
|
+
for (const vol of volumes) {
|
|
294
|
+
table.push([vol.id.toString(), vol.name, `${vol.size} GB`, vol.server?.toString() || '-', formatStatus(vol.status), vol.location.name, vol.format || '-']);
|
|
295
|
+
}
|
|
296
|
+
return table.toString();
|
|
297
|
+
}
|
|
298
|
+
export function formatVolumeDetails(vol) {
|
|
299
|
+
const lines = [];
|
|
300
|
+
lines.push(heading(`Volume: ${vol.name}`));
|
|
301
|
+
lines.push('');
|
|
302
|
+
const table = createTable(['Property', 'Value']);
|
|
303
|
+
table.push(['ID', vol.id.toString()], ['Name', vol.name], ['Size', `${vol.size} GB`], ['Server', vol.server?.toString() || 'Unattached'], ['Status', formatStatus(vol.status)], ['Location', vol.location.name], ['Linux Device', vol.linux_device || '-'], ['Format', vol.format || '-'], ['Protection', vol.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(vol.created)]);
|
|
304
|
+
lines.push(table.toString());
|
|
305
|
+
return lines.join('\n');
|
|
306
|
+
}
|
|
307
|
+
// Load Balancers
|
|
308
|
+
export function formatLoadBalancerList(lbs) {
|
|
309
|
+
if (lbs.length === 0)
|
|
310
|
+
return info('No load balancers found.');
|
|
311
|
+
const table = createTable(['ID', 'Name', 'IPv4', 'Type', 'Location', 'Targets', 'Services']);
|
|
312
|
+
for (const lb of lbs) {
|
|
313
|
+
table.push([lb.id.toString(), lb.name, lb.public_net.ipv4.ip, lb.load_balancer_type.name, lb.location.name, lb.targets.length.toString(), lb.services.length.toString()]);
|
|
314
|
+
}
|
|
315
|
+
return table.toString();
|
|
316
|
+
}
|
|
317
|
+
export function formatLoadBalancerDetails(lb) {
|
|
318
|
+
const lines = [];
|
|
319
|
+
lines.push(heading(`Load Balancer: ${lb.name}`));
|
|
320
|
+
lines.push('');
|
|
321
|
+
const table = createTable(['Property', 'Value']);
|
|
322
|
+
table.push(['ID', lb.id.toString()], ['Name', lb.name], ['Type', lb.load_balancer_type.name], ['Location', lb.location.name], ['IPv4', lb.public_net.ipv4.ip], ['IPv6', lb.public_net.ipv6.ip], ['Public Net', lb.public_net.enabled ? 'Enabled' : 'Disabled'], ['Algorithm', lb.algorithm.type], ['Protection', lb.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(lb.created)]);
|
|
323
|
+
lines.push(table.toString());
|
|
324
|
+
if (lb.services.length > 0) {
|
|
325
|
+
lines.push('');
|
|
326
|
+
lines.push(colorize('Services:', 'bold'));
|
|
327
|
+
const svcTable = createTable(['Protocol', 'Listen Port', 'Destination Port', 'Proxyprotocol']);
|
|
328
|
+
for (const svc of lb.services) {
|
|
329
|
+
svcTable.push([svc.protocol, svc.listen_port.toString(), svc.destination_port.toString(), svc.proxyprotocol ? 'Yes' : 'No']);
|
|
330
|
+
}
|
|
331
|
+
lines.push(svcTable.toString());
|
|
332
|
+
}
|
|
333
|
+
return lines.join('\n');
|
|
334
|
+
}
|
|
335
|
+
// Images
|
|
336
|
+
export function formatImageList(images) {
|
|
337
|
+
if (images.length === 0)
|
|
338
|
+
return info('No images found.');
|
|
339
|
+
const table = createTable(['ID', 'Type', 'Name', 'Description', 'OS', 'Arch', 'Disk', 'Status']);
|
|
340
|
+
for (const img of images) {
|
|
341
|
+
table.push([img.id.toString(), img.type, img.name || '-', img.description.substring(0, 30), img.os_flavor, img.architecture, `${img.disk_size} GB`, formatStatus(img.status)]);
|
|
342
|
+
}
|
|
343
|
+
return table.toString();
|
|
344
|
+
}
|
|
345
|
+
export function formatImageDetails(img) {
|
|
346
|
+
const lines = [];
|
|
347
|
+
lines.push(heading(`Image: ${img.description}`));
|
|
348
|
+
lines.push('');
|
|
349
|
+
const table = createTable(['Property', 'Value']);
|
|
350
|
+
table.push(['ID', img.id.toString()], ['Type', img.type], ['Name', img.name || '-'], ['Description', img.description], ['OS Flavor', img.os_flavor], ['OS Version', img.os_version || '-'], ['Architecture', img.architecture], ['Disk Size', `${img.disk_size} GB`], ['Image Size', img.image_size ? formatBytes(img.image_size * 1024 * 1024) : '-'], ['Status', formatStatus(img.status)], ['Rapid Deploy', img.rapid_deploy ? 'Yes' : 'No'], ['Protection', img.protection.delete ? colorize('Delete protected', 'yellow') : 'None'], ['Created', formatDate(img.created)]);
|
|
351
|
+
lines.push(table.toString());
|
|
352
|
+
return lines.join('\n');
|
|
353
|
+
}
|
|
354
|
+
// Cloud SSH Keys
|
|
355
|
+
export function formatCloudSshKeyList(keys) {
|
|
356
|
+
if (keys.length === 0)
|
|
357
|
+
return info('No SSH keys found.');
|
|
358
|
+
const table = createTable(['ID', 'Name', 'Fingerprint', 'Created']);
|
|
359
|
+
for (const key of keys) {
|
|
360
|
+
table.push([key.id.toString(), key.name, key.fingerprint, formatDate(key.created)]);
|
|
361
|
+
}
|
|
362
|
+
return table.toString();
|
|
363
|
+
}
|
|
364
|
+
export function formatCloudSshKeyDetails(key) {
|
|
365
|
+
const lines = [];
|
|
366
|
+
lines.push(heading(`SSH Key: ${key.name}`));
|
|
367
|
+
lines.push('');
|
|
368
|
+
const table = createTable(['Property', 'Value']);
|
|
369
|
+
table.push(['ID', key.id.toString()], ['Name', key.name], ['Fingerprint', key.fingerprint], ['Created', formatDate(key.created)]);
|
|
370
|
+
lines.push(table.toString());
|
|
371
|
+
lines.push('');
|
|
372
|
+
lines.push(colorize('Public Key:', 'bold'));
|
|
373
|
+
lines.push(key.public_key);
|
|
374
|
+
return lines.join('\n');
|
|
375
|
+
}
|
|
376
|
+
// Certificates
|
|
377
|
+
export function formatCertificateList(certs) {
|
|
378
|
+
if (certs.length === 0)
|
|
379
|
+
return info('No certificates found.');
|
|
380
|
+
const table = createTable(['ID', 'Name', 'Type', 'Domains', 'Valid Until', 'Created']);
|
|
381
|
+
for (const cert of certs) {
|
|
382
|
+
table.push([cert.id.toString(), cert.name, cert.type, cert.domain_names.join(', ').substring(0, 30), formatDate(cert.not_valid_after), formatDate(cert.created)]);
|
|
383
|
+
}
|
|
384
|
+
return table.toString();
|
|
385
|
+
}
|
|
386
|
+
export function formatCertificateDetails(cert) {
|
|
387
|
+
const lines = [];
|
|
388
|
+
lines.push(heading(`Certificate: ${cert.name}`));
|
|
389
|
+
lines.push('');
|
|
390
|
+
const table = createTable(['Property', 'Value']);
|
|
391
|
+
table.push(['ID', cert.id.toString()], ['Name', cert.name], ['Type', cert.type], ['Domains', cert.domain_names.join(', ')], ['Fingerprint', cert.fingerprint || '-'], ['Valid From', formatDate(cert.not_valid_before)], ['Valid Until', formatDate(cert.not_valid_after)], ['Created', formatDate(cert.created)]);
|
|
392
|
+
lines.push(table.toString());
|
|
393
|
+
return lines.join('\n');
|
|
394
|
+
}
|
|
395
|
+
// Placement Groups
|
|
396
|
+
export function formatPlacementGroupList(groups) {
|
|
397
|
+
if (groups.length === 0)
|
|
398
|
+
return info('No placement groups found.');
|
|
399
|
+
const table = createTable(['ID', 'Name', 'Type', 'Servers', 'Created']);
|
|
400
|
+
for (const pg of groups) {
|
|
401
|
+
table.push([pg.id.toString(), pg.name, pg.type, pg.servers.length.toString(), formatDate(pg.created)]);
|
|
402
|
+
}
|
|
403
|
+
return table.toString();
|
|
404
|
+
}
|
|
405
|
+
export function formatPlacementGroupDetails(pg) {
|
|
406
|
+
const lines = [];
|
|
407
|
+
lines.push(heading(`Placement Group: ${pg.name}`));
|
|
408
|
+
lines.push('');
|
|
409
|
+
const table = createTable(['Property', 'Value']);
|
|
410
|
+
table.push(['ID', pg.id.toString()], ['Name', pg.name], ['Type', pg.type], ['Servers', pg.servers.length > 0 ? pg.servers.join(', ') : '-'], ['Created', formatDate(pg.created)]);
|
|
411
|
+
lines.push(table.toString());
|
|
412
|
+
return lines.join('\n');
|
|
413
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HetznerCloudClient } from './client.js';
|
|
2
|
+
export interface CloudActionOptions {
|
|
3
|
+
token?: string;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
yes?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Wrap a cloud API action with token resolution and error handling.
|
|
9
|
+
*/
|
|
10
|
+
export declare function cloudAction<T extends unknown[]>(fn: (client: HetznerCloudClient, ...args: T) => Promise<void>): (...args: [...T, CloudActionOptions]) => Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Output data as JSON or formatted table based on options.
|
|
13
|
+
*/
|
|
14
|
+
export declare function cloudOutput<T>(data: T, formatter: (data: T) => string, options: CloudActionOptions): void;
|
|
15
|
+
/**
|
|
16
|
+
* Confirm destructive action unless --yes flag is set.
|
|
17
|
+
*/
|
|
18
|
+
export declare function cloudConfirm(message: string, options: CloudActionOptions, defaultValue?: boolean): Promise<boolean>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import * as fmt from '../shared/formatter.js';
|
|
3
|
+
import { HetznerCloudClient } from './client.js';
|
|
4
|
+
import { resolveToken } from './context.js';
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a cloud API action with token resolution and error handling.
|
|
7
|
+
*/
|
|
8
|
+
export function cloudAction(fn) {
|
|
9
|
+
return async (...args) => {
|
|
10
|
+
const options = args[args.length - 1];
|
|
11
|
+
try {
|
|
12
|
+
const token = await resolveToken(options.token);
|
|
13
|
+
const client = new HetznerCloudClient(token);
|
|
14
|
+
await fn(client, ...args.slice(0, -1));
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
if (error.message.includes('ExitPromptError') || error.name === 'ExitPromptError') {
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
console.error(fmt.error(error.message));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.error(fmt.error('An unknown error occurred'));
|
|
25
|
+
}
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Output data as JSON or formatted table based on options.
|
|
32
|
+
*/
|
|
33
|
+
export function cloudOutput(data, formatter, options) {
|
|
34
|
+
console.log(options.json ? fmt.formatJson(data) : formatter(data));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Confirm destructive action unless --yes flag is set.
|
|
38
|
+
*/
|
|
39
|
+
export async function cloudConfirm(message, options, defaultValue = false) {
|
|
40
|
+
if (options.yes)
|
|
41
|
+
return true;
|
|
42
|
+
const confirmed = await confirm({ message, default: defaultValue });
|
|
43
|
+
if (!confirmed) {
|
|
44
|
+
console.log('Aborted.');
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|