hetzner-robot-cli 1.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.
@@ -0,0 +1,617 @@
1
+ import Table from 'cli-table3';
2
+ export const colors = {
3
+ reset: '\x1b[0m',
4
+ bold: '\x1b[1m',
5
+ dim: '\x1b[2m',
6
+ red: '\x1b[31m',
7
+ green: '\x1b[32m',
8
+ yellow: '\x1b[33m',
9
+ blue: '\x1b[34m',
10
+ magenta: '\x1b[35m',
11
+ cyan: '\x1b[36m',
12
+ white: '\x1b[37m',
13
+ gray: '\x1b[90m',
14
+ bgRed: '\x1b[41m',
15
+ bgGreen: '\x1b[42m',
16
+ bgYellow: '\x1b[43m',
17
+ };
18
+ export function colorize(text, color) {
19
+ return `${colors[color]}${text}${colors.reset}`;
20
+ }
21
+ export function success(message) {
22
+ return `${colors.green}✓${colors.reset} ${message}`;
23
+ }
24
+ export function error(message) {
25
+ return `${colors.red}✗${colors.reset} ${message}`;
26
+ }
27
+ export function warning(message) {
28
+ return `${colors.yellow}⚠${colors.reset} ${message}`;
29
+ }
30
+ export function info(message) {
31
+ return `${colors.blue}ℹ${colors.reset} ${message}`;
32
+ }
33
+ export function heading(text) {
34
+ return `\n${colors.bold}${colors.cyan}${text}${colors.reset}\n${'─'.repeat(text.length)}`;
35
+ }
36
+ export function formatStatus(status) {
37
+ switch (status.toLowerCase()) {
38
+ case 'ready':
39
+ case 'active':
40
+ case 'enabled':
41
+ case 'success':
42
+ return colorize(status, 'green');
43
+ case 'installing':
44
+ case 'in process':
45
+ case 'running':
46
+ return colorize(status, 'yellow');
47
+ case 'maintenance':
48
+ case 'disabled':
49
+ case 'failed':
50
+ case 'cancelled':
51
+ return colorize(status, 'red');
52
+ default:
53
+ return status;
54
+ }
55
+ }
56
+ export function formatBytes(bytes) {
57
+ if (bytes === 0)
58
+ return '0 B';
59
+ const k = 1024;
60
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
61
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
62
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
63
+ }
64
+ export function formatDate(dateStr) {
65
+ if (!dateStr)
66
+ return '-';
67
+ const date = new Date(dateStr);
68
+ return date.toLocaleDateString('en-US', {
69
+ year: 'numeric',
70
+ month: 'short',
71
+ day: 'numeric',
72
+ });
73
+ }
74
+ export function formatDateTime(dateStr) {
75
+ if (!dateStr)
76
+ return '-';
77
+ const date = new Date(dateStr);
78
+ return date.toLocaleString('en-US', {
79
+ year: 'numeric',
80
+ month: 'short',
81
+ day: 'numeric',
82
+ hour: '2-digit',
83
+ minute: '2-digit',
84
+ });
85
+ }
86
+ function createTable(head, colWidths) {
87
+ const options = {
88
+ head: head.map((h) => colorize(h, 'cyan')),
89
+ chars: {
90
+ top: '─',
91
+ 'top-mid': '┬',
92
+ 'top-left': '┌',
93
+ 'top-right': '┐',
94
+ bottom: '─',
95
+ 'bottom-mid': '┴',
96
+ 'bottom-left': '└',
97
+ 'bottom-right': '┘',
98
+ left: '│',
99
+ 'left-mid': '├',
100
+ mid: '─',
101
+ 'mid-mid': '┼',
102
+ right: '│',
103
+ 'right-mid': '┤',
104
+ middle: '│',
105
+ },
106
+ style: {
107
+ 'padding-left': 1,
108
+ 'padding-right': 1,
109
+ },
110
+ };
111
+ if (colWidths) {
112
+ options.colWidths = colWidths;
113
+ }
114
+ return new Table(options);
115
+ }
116
+ export function formatServerList(servers) {
117
+ if (servers.length === 0) {
118
+ return info('No servers found.');
119
+ }
120
+ const table = createTable(['#', 'IP', 'Name', 'Product', 'DC', 'Status', 'Paid Until']);
121
+ for (const { server } of servers) {
122
+ table.push([
123
+ server.server_number.toString(),
124
+ server.server_ip,
125
+ server.server_name || '-',
126
+ server.product,
127
+ server.dc,
128
+ formatStatus(server.status),
129
+ server.cancelled ? colorize('Cancelled', 'red') : formatDate(server.paid_until),
130
+ ]);
131
+ }
132
+ return table.toString();
133
+ }
134
+ export function formatServerDetails(server) {
135
+ const lines = [];
136
+ lines.push(heading(`Server ${server.server_number}`));
137
+ lines.push('');
138
+ const table = createTable(['Property', 'Value']);
139
+ table.push(['Server Number', server.server_number.toString()], ['IPv4', server.server_ip], ['IPv6 Net', server.server_ipv6_net || '-'], ['Name', server.server_name || '-'], ['Product', server.product], ['Datacenter', server.dc], ['Traffic', server.traffic], ['Status', formatStatus(server.status)], ['Paid Until', formatDate(server.paid_until)], ['Cancelled', server.cancelled ? colorize('Yes', 'red') : 'No']);
140
+ lines.push(table.toString());
141
+ // Features
142
+ lines.push('');
143
+ lines.push(colorize('Features:', 'bold'));
144
+ const features = [
145
+ { name: 'Reset', enabled: server.reset },
146
+ { name: 'Rescue', enabled: server.rescue },
147
+ { name: 'VNC', enabled: server.vnc },
148
+ { name: 'Windows', enabled: server.windows },
149
+ { name: 'Plesk', enabled: server.plesk },
150
+ { name: 'cPanel', enabled: server.cpanel },
151
+ { name: 'WoL', enabled: server.wol },
152
+ { name: 'Hot Swap', enabled: server.hot_swap },
153
+ ];
154
+ const enabledFeatures = features.filter((f) => f.enabled).map((f) => f.name);
155
+ lines.push(enabledFeatures.length > 0 ? ` ${enabledFeatures.join(', ')}` : ' None');
156
+ // IPs
157
+ if (server.ip && server.ip.length > 0) {
158
+ lines.push('');
159
+ lines.push(colorize('Additional IPs:', 'bold'));
160
+ for (const ip of server.ip) {
161
+ lines.push(` ${ip}`);
162
+ }
163
+ }
164
+ // Subnets
165
+ if (server.subnet && server.subnet.length > 0) {
166
+ lines.push('');
167
+ lines.push(colorize('Subnets:', 'bold'));
168
+ for (const subnet of server.subnet) {
169
+ lines.push(` ${subnet.ip}/${subnet.mask}`);
170
+ }
171
+ }
172
+ return lines.join('\n');
173
+ }
174
+ export function formatResetOptions(reset) {
175
+ const lines = [];
176
+ lines.push(heading(`Reset Options for Server ${reset.server_number}`));
177
+ lines.push('');
178
+ const table = createTable(['Property', 'Value']);
179
+ table.push(['Server IP', reset.server_ip], ['Server Number', reset.server_number.toString()], ['Operating Status', formatStatus(reset.operating_status)], ['Available Reset Types', reset.type.join(', ')]);
180
+ lines.push(table.toString());
181
+ lines.push('');
182
+ lines.push(colorize('Reset Types:', 'bold'));
183
+ lines.push(' sw - Software reset (ACPI)');
184
+ lines.push(' hw - Hardware reset (forced)');
185
+ lines.push(' man - Manual reset (technician)');
186
+ lines.push(' power - Power cycle');
187
+ lines.push(' power_long - Long power cycle (10+ seconds)');
188
+ return lines.join('\n');
189
+ }
190
+ export function formatResetResult(reset, type) {
191
+ return success(`Server ${reset.server_number} reset initiated (type: ${type})`);
192
+ }
193
+ export function formatBootConfig(config, serverNumber) {
194
+ const lines = [];
195
+ lines.push(heading(`Boot Configuration for Server ${serverNumber}`));
196
+ lines.push('');
197
+ // Rescue
198
+ if (config.rescue) {
199
+ lines.push(colorize('Rescue System:', 'bold'));
200
+ lines.push(` Active: ${config.rescue.active ? colorize('Yes', 'green') : 'No'}`);
201
+ if (config.rescue.active && config.rescue.password) {
202
+ lines.push(` Password: ${config.rescue.password}`);
203
+ }
204
+ lines.push(` Available OS: ${config.rescue.os.join(', ')}`);
205
+ lines.push(` Architectures: ${config.rescue.arch.join(', ')}-bit`);
206
+ lines.push('');
207
+ }
208
+ // Linux
209
+ if (config.linux) {
210
+ lines.push(colorize('Linux Install:', 'bold'));
211
+ lines.push(` Active: ${config.linux.active ? colorize('Yes', 'green') : 'No'}`);
212
+ if (config.linux.active && config.linux.password) {
213
+ lines.push(` Password: ${config.linux.password}`);
214
+ }
215
+ lines.push(` Distributions: ${config.linux.dist.slice(0, 5).join(', ')}${config.linux.dist.length > 5 ? '...' : ''}`);
216
+ lines.push('');
217
+ }
218
+ // VNC
219
+ if (config.vnc) {
220
+ lines.push(colorize('VNC Install:', 'bold'));
221
+ lines.push(` Active: ${config.vnc.active ? colorize('Yes', 'green') : 'No'}`);
222
+ if (config.vnc.active && config.vnc.password) {
223
+ lines.push(` Password: ${config.vnc.password}`);
224
+ }
225
+ lines.push('');
226
+ }
227
+ // Windows
228
+ if (config.windows) {
229
+ lines.push(colorize('Windows Install:', 'bold'));
230
+ lines.push(` Active: ${config.windows.active ? colorize('Yes', 'green') : 'No'}`);
231
+ if (config.windows.active && config.windows.password) {
232
+ lines.push(` Password: ${config.windows.password}`);
233
+ }
234
+ lines.push('');
235
+ }
236
+ return lines.join('\n');
237
+ }
238
+ export function formatRescueActivation(rescue) {
239
+ const lines = [];
240
+ lines.push(success('Rescue system activated'));
241
+ lines.push('');
242
+ lines.push(colorize('Connection Details:', 'bold'));
243
+ lines.push(` Server: ${rescue.server_ip}`);
244
+ if (rescue.password) {
245
+ lines.push(` Password: ${colorize(rescue.password, 'yellow')}`);
246
+ }
247
+ lines.push('');
248
+ lines.push(info('Reboot the server to enter rescue mode.'));
249
+ return lines.join('\n');
250
+ }
251
+ export function formatLinuxActivation(linux) {
252
+ const lines = [];
253
+ lines.push(success('Linux installation activated'));
254
+ lines.push('');
255
+ lines.push(colorize('Installation Details:', 'bold'));
256
+ lines.push(` Server: ${linux.server_ip}`);
257
+ if (linux.password) {
258
+ lines.push(` Password: ${colorize(linux.password, 'yellow')}`);
259
+ }
260
+ lines.push('');
261
+ lines.push(info('Reboot the server to start installation.'));
262
+ return lines.join('\n');
263
+ }
264
+ export function formatIpList(ips) {
265
+ if (ips.length === 0) {
266
+ return info('No IPs found.');
267
+ }
268
+ const table = createTable(['IP', 'Server IP', 'Server #', 'Locked', 'MAC', 'Traffic Warnings']);
269
+ for (const { ip } of ips) {
270
+ table.push([
271
+ ip.ip,
272
+ ip.server_ip,
273
+ ip.server_number.toString(),
274
+ ip.locked ? colorize('Yes', 'red') : 'No',
275
+ ip.separate_mac || '-',
276
+ ip.traffic_warnings ? 'Yes' : 'No',
277
+ ]);
278
+ }
279
+ return table.toString();
280
+ }
281
+ export function formatIpDetails(ip) {
282
+ const table = createTable(['Property', 'Value']);
283
+ table.push(['IP Address', ip.ip], ['Server IP', ip.server_ip], ['Server Number', ip.server_number.toString()], ['Locked', ip.locked ? colorize('Yes', 'red') : 'No'], ['Separate MAC', ip.separate_mac || '-'], ['Traffic Warnings', ip.traffic_warnings ? 'Enabled' : 'Disabled'], ['Traffic Hourly', `${ip.traffic_hourly} MB`], ['Traffic Daily', `${ip.traffic_daily} MB`], ['Traffic Monthly', `${ip.traffic_monthly} GB`]);
284
+ return table.toString();
285
+ }
286
+ export function formatSubnetList(subnets) {
287
+ if (subnets.length === 0) {
288
+ return info('No subnets found.');
289
+ }
290
+ const table = createTable(['Subnet', 'Gateway', 'Server IP', 'Failover', 'Locked']);
291
+ for (const { subnet } of subnets) {
292
+ table.push([
293
+ `${subnet.ip}/${subnet.mask}`,
294
+ subnet.gateway,
295
+ subnet.server_ip,
296
+ subnet.failover ? 'Yes' : 'No',
297
+ subnet.locked ? colorize('Yes', 'red') : 'No',
298
+ ]);
299
+ }
300
+ return table.toString();
301
+ }
302
+ export function formatFailoverList(failovers) {
303
+ if (failovers.length === 0) {
304
+ return info('No failover IPs found.');
305
+ }
306
+ const table = createTable(['Failover IP', 'Netmask', 'Server IP', 'Active Server']);
307
+ for (const { failover } of failovers) {
308
+ table.push([
309
+ failover.ip,
310
+ failover.netmask,
311
+ failover.server_ip,
312
+ failover.active_server_ip,
313
+ ]);
314
+ }
315
+ return table.toString();
316
+ }
317
+ export function formatFailoverSwitch(failover) {
318
+ return success(`Failover ${failover.ip} routed to ${failover.active_server_ip}`);
319
+ }
320
+ export function formatRdnsList(entries) {
321
+ if (entries.length === 0) {
322
+ return info('No reverse DNS entries found.');
323
+ }
324
+ const table = createTable(['IP', 'PTR Record']);
325
+ for (const { rdns } of entries) {
326
+ table.push([rdns.ip, rdns.ptr]);
327
+ }
328
+ return table.toString();
329
+ }
330
+ export function formatSshKeyList(keys) {
331
+ if (keys.length === 0) {
332
+ return info('No SSH keys found.');
333
+ }
334
+ const table = createTable(['Name', 'Fingerprint', 'Type', 'Size']);
335
+ for (const { key } of keys) {
336
+ table.push([
337
+ key.name,
338
+ key.fingerprint,
339
+ key.type.toUpperCase(),
340
+ `${key.size} bits`,
341
+ ]);
342
+ }
343
+ return table.toString();
344
+ }
345
+ export function formatSshKeyDetails(key) {
346
+ const lines = [];
347
+ lines.push(heading(`SSH Key: ${key.name}`));
348
+ lines.push('');
349
+ const table = createTable(['Property', 'Value']);
350
+ table.push(['Name', key.name], ['Fingerprint', key.fingerprint], ['Type', key.type.toUpperCase()], ['Size', `${key.size} bits`]);
351
+ lines.push(table.toString());
352
+ lines.push('');
353
+ lines.push(colorize('Public Key:', 'bold'));
354
+ lines.push(key.data);
355
+ return lines.join('\n');
356
+ }
357
+ export function formatFirewall(firewall) {
358
+ const lines = [];
359
+ lines.push(heading(`Firewall for Server ${firewall.server_number}`));
360
+ lines.push('');
361
+ const statusTable = createTable(['Property', 'Value']);
362
+ statusTable.push(['Server IP', firewall.server_ip], ['Status', formatStatus(firewall.status)], ['IPv6 Filtering', firewall.filter_ipv6 ? 'Enabled' : 'Disabled'], ['Whitelist HOS', firewall.whitelist_hos ? 'Enabled' : 'Disabled'], ['Port', firewall.port]);
363
+ lines.push(statusTable.toString());
364
+ // Input rules
365
+ if (firewall.rules.input && firewall.rules.input.length > 0) {
366
+ lines.push('');
367
+ lines.push(colorize('Input Rules:', 'bold'));
368
+ const inputTable = createTable(['Name', 'Action', 'Protocol', 'Src IP', 'Dst Port']);
369
+ for (const rule of firewall.rules.input) {
370
+ inputTable.push([
371
+ rule.name || '-',
372
+ rule.action === 'accept' ? colorize(rule.action, 'green') : colorize(rule.action, 'red'),
373
+ rule.protocol || 'any',
374
+ rule.src_ip || 'any',
375
+ rule.dst_port || 'any',
376
+ ]);
377
+ }
378
+ lines.push(inputTable.toString());
379
+ }
380
+ return lines.join('\n');
381
+ }
382
+ export function formatFirewallTemplateList(templates) {
383
+ if (templates.length === 0) {
384
+ return info('No firewall templates found.');
385
+ }
386
+ const table = createTable(['ID', 'Name', 'Default', 'IPv6', 'Rules']);
387
+ for (const { firewall_template: tmpl } of templates) {
388
+ table.push([
389
+ tmpl.id.toString(),
390
+ tmpl.name,
391
+ tmpl.is_default ? colorize('Yes', 'green') : 'No',
392
+ tmpl.filter_ipv6 ? 'Yes' : 'No',
393
+ tmpl.rules.input.length.toString(),
394
+ ]);
395
+ }
396
+ return table.toString();
397
+ }
398
+ export function formatVSwitchList(vswitches) {
399
+ if (vswitches.length === 0) {
400
+ return info('No vSwitches found.');
401
+ }
402
+ const table = createTable(['ID', 'Name', 'VLAN', 'Servers', 'Subnets', 'Cancelled']);
403
+ for (const { vswitch } of vswitches) {
404
+ table.push([
405
+ vswitch.id.toString(),
406
+ vswitch.name,
407
+ vswitch.vlan.toString(),
408
+ vswitch.server.length.toString(),
409
+ vswitch.subnet.length.toString(),
410
+ vswitch.cancelled ? colorize('Yes', 'red') : 'No',
411
+ ]);
412
+ }
413
+ return table.toString();
414
+ }
415
+ export function formatVSwitchDetails(vswitch) {
416
+ const lines = [];
417
+ lines.push(heading(`vSwitch ${vswitch.id}: ${vswitch.name}`));
418
+ lines.push('');
419
+ const table = createTable(['Property', 'Value']);
420
+ table.push(['ID', vswitch.id.toString()], ['Name', vswitch.name], ['VLAN', vswitch.vlan.toString()], ['Cancelled', vswitch.cancelled ? colorize('Yes', 'red') : 'No']);
421
+ lines.push(table.toString());
422
+ // Servers
423
+ if (vswitch.server.length > 0) {
424
+ lines.push('');
425
+ lines.push(colorize('Connected Servers:', 'bold'));
426
+ const serverTable = createTable(['Server IP', 'Server #', 'Status']);
427
+ for (const srv of vswitch.server) {
428
+ serverTable.push([
429
+ srv.server_ip,
430
+ srv.server_number.toString(),
431
+ formatStatus(srv.status),
432
+ ]);
433
+ }
434
+ lines.push(serverTable.toString());
435
+ }
436
+ // Subnets
437
+ if (vswitch.subnet.length > 0) {
438
+ lines.push('');
439
+ lines.push(colorize('Subnets:', 'bold'));
440
+ const subnetTable = createTable(['Subnet', 'Gateway']);
441
+ for (const subnet of vswitch.subnet) {
442
+ subnetTable.push([`${subnet.ip}/${subnet.mask}`, subnet.gateway]);
443
+ }
444
+ lines.push(subnetTable.toString());
445
+ }
446
+ return lines.join('\n');
447
+ }
448
+ export function formatStorageBoxList(boxes) {
449
+ if (boxes.length === 0) {
450
+ return info('No storage boxes found.');
451
+ }
452
+ const table = createTable(['ID', 'Name', 'Product', 'Location', 'Usage', 'Status']);
453
+ for (const { storagebox } of boxes) {
454
+ const usagePercent = Math.round((storagebox.disk_usage / storagebox.disk_quota) * 100);
455
+ const usageBar = `${formatBytes(storagebox.disk_usage)} / ${formatBytes(storagebox.disk_quota)} (${usagePercent}%)`;
456
+ let status;
457
+ if (storagebox.cancelled) {
458
+ status = colorize('Cancelled', 'red');
459
+ }
460
+ else if (storagebox.locked) {
461
+ status = colorize('Locked', 'yellow');
462
+ }
463
+ else {
464
+ status = colorize('Active', 'green');
465
+ }
466
+ table.push([
467
+ storagebox.id.toString(),
468
+ storagebox.name || storagebox.login,
469
+ storagebox.product,
470
+ storagebox.location,
471
+ usageBar,
472
+ status,
473
+ ]);
474
+ }
475
+ return table.toString();
476
+ }
477
+ export function formatStorageBoxDetails(box) {
478
+ const lines = [];
479
+ lines.push(heading(`Storage Box ${box.id}: ${box.name || box.login}`));
480
+ lines.push('');
481
+ const table = createTable(['Property', 'Value']);
482
+ const usagePercent = Math.round((box.disk_usage / box.disk_quota) * 100);
483
+ table.push(['ID', box.id.toString()], ['Login', box.login], ['Name', box.name || '-'], ['Product', box.product], ['Location', box.location], ['Server', box.server], ['Host System', box.host_system], ['Paid Until', formatDate(box.paid_until)], ['Disk Quota', formatBytes(box.disk_quota)], ['Disk Usage', `${formatBytes(box.disk_usage)} (${usagePercent}%)`], ['Data Usage', formatBytes(box.disk_usage_data)], ['Snapshot Usage', formatBytes(box.disk_usage_snapshots)], ['Cancelled', box.cancelled ? colorize('Yes', 'red') : 'No'], ['Locked', box.locked ? colorize('Yes', 'yellow') : 'No']);
484
+ lines.push(table.toString());
485
+ // Features
486
+ lines.push('');
487
+ lines.push(colorize('Features:', 'bold'));
488
+ const features = [
489
+ { name: 'WebDAV', enabled: box.webdav },
490
+ { name: 'Samba/CIFS', enabled: box.samba },
491
+ { name: 'SSH/SFTP', enabled: box.ssh },
492
+ { name: 'External Access', enabled: box.external_reachability },
493
+ { name: 'ZFS', enabled: box.zfs },
494
+ ];
495
+ for (const feature of features) {
496
+ lines.push(` ${feature.name}: ${feature.enabled ? colorize('Enabled', 'green') : colorize('Disabled', 'gray')}`);
497
+ }
498
+ return lines.join('\n');
499
+ }
500
+ export function formatStorageBoxSnapshots(snapshots) {
501
+ if (snapshots.length === 0) {
502
+ return info('No snapshots found.');
503
+ }
504
+ const table = createTable(['Name', 'Timestamp', 'Size']);
505
+ for (const { snapshot } of snapshots) {
506
+ table.push([
507
+ snapshot.name,
508
+ formatDateTime(snapshot.timestamp),
509
+ snapshot.size_formatted || formatBytes(snapshot.size),
510
+ ]);
511
+ }
512
+ return table.toString();
513
+ }
514
+ export function formatStorageBoxSubaccounts(subaccounts) {
515
+ if (subaccounts.length === 0) {
516
+ return info('No subaccounts found.');
517
+ }
518
+ const table = createTable(['Username', 'Home Directory', 'SSH', 'Samba', 'WebDAV', 'Read-only']);
519
+ for (const { subaccount } of subaccounts) {
520
+ table.push([
521
+ subaccount.username,
522
+ subaccount.homedirectory,
523
+ subaccount.ssh ? 'Yes' : 'No',
524
+ subaccount.samba ? 'Yes' : 'No',
525
+ subaccount.webdav ? 'Yes' : 'No',
526
+ subaccount.readonly ? colorize('Yes', 'yellow') : 'No',
527
+ ]);
528
+ }
529
+ return table.toString();
530
+ }
531
+ export function formatTraffic(traffic) {
532
+ const lines = [];
533
+ lines.push(heading(`Traffic for ${traffic.ip}`));
534
+ lines.push(`Period: ${formatDate(traffic.from)} - ${formatDate(traffic.to)}`);
535
+ lines.push('');
536
+ const table = createTable(['Date', 'In', 'Out', 'Total']);
537
+ for (const data of traffic.data) {
538
+ table.push([
539
+ data.date || '-',
540
+ formatBytes(data.in),
541
+ formatBytes(data.out),
542
+ formatBytes(data.sum),
543
+ ]);
544
+ }
545
+ lines.push(table.toString());
546
+ return lines.join('\n');
547
+ }
548
+ export function formatWolResult(wol) {
549
+ return success(`Wake-on-LAN packet sent to server ${wol.server_number} (${wol.server_ip})`);
550
+ }
551
+ export function formatServerProductList(products) {
552
+ if (products.length === 0) {
553
+ return info('No products found.');
554
+ }
555
+ const table = createTable(['ID', 'Name', 'Traffic', 'Locations']);
556
+ for (const { product } of products) {
557
+ table.push([
558
+ product.id,
559
+ product.name,
560
+ product.traffic,
561
+ product.location.join(', '),
562
+ ]);
563
+ }
564
+ return table.toString();
565
+ }
566
+ export function formatServerMarketProductList(products) {
567
+ if (products.length === 0) {
568
+ return info('No market products available.');
569
+ }
570
+ const table = createTable(['ID', 'Name', 'CPU', 'RAM', 'HDD', 'DC', 'Price']);
571
+ for (const { product } of products) {
572
+ table.push([
573
+ product.id.toString(),
574
+ product.name,
575
+ product.cpu,
576
+ `${product.memory_size} GB`,
577
+ product.hdd_text,
578
+ product.datacenter,
579
+ `€${product.price}/mo`,
580
+ ]);
581
+ }
582
+ return table.toString();
583
+ }
584
+ export function formatTransactionList(transactions) {
585
+ if (transactions.length === 0) {
586
+ return info('No transactions found.');
587
+ }
588
+ const table = createTable(['ID', 'Date', 'Product', 'Status', 'Server']);
589
+ for (const { transaction } of transactions) {
590
+ table.push([
591
+ transaction.id,
592
+ formatDate(transaction.date),
593
+ transaction.product.name,
594
+ formatStatus(transaction.status),
595
+ transaction.server_ip || '-',
596
+ ]);
597
+ }
598
+ return table.toString();
599
+ }
600
+ export function formatCancellation(cancellation) {
601
+ const lines = [];
602
+ lines.push(heading(`Cancellation for Server ${cancellation.server_number}`));
603
+ lines.push('');
604
+ const table = createTable(['Property', 'Value']);
605
+ table.push(['Server Number', cancellation.server_number.toString()], ['Server IP', cancellation.server_ip], ['Server Name', cancellation.server_name || '-'], ['Earliest Cancellation', formatDate(cancellation.earliest_cancellation_date)], ['Cancelled', cancellation.cancelled ? colorize('Yes', 'red') : 'No']);
606
+ if (cancellation.cancelled && cancellation.cancellation_date) {
607
+ table.push(['Cancellation Date', formatDate(cancellation.cancellation_date)]);
608
+ if (cancellation.cancellation_reason) {
609
+ table.push(['Reason', cancellation.cancellation_reason.join(', ')]);
610
+ }
611
+ }
612
+ lines.push(table.toString());
613
+ return lines.join('\n');
614
+ }
615
+ export function formatJson(data) {
616
+ return JSON.stringify(data, null, 2);
617
+ }
@@ -0,0 +1,4 @@
1
+ export { HetznerRobotClient } from './client.js';
2
+ export type { Server, ServerSubnet, ServerDetails, Cancellation, ResetType, Reset, BootConfig, RescueConfig, LinuxConfig, VncConfig, WindowsConfig, PleskConfig, CpanelConfig, IP, Mac, Subnet, Failover, Rdns, SshKey, Firewall, FirewallRule, FirewallTemplate, VSwitch, VSwitchServer, VSwitchSubnet, VSwitchCloudNetwork, StorageBox, StorageBoxSnapshot, StorageBoxSnapshotPlan, StorageBoxSubaccount, Traffic, TrafficData, Wol, ServerProduct, ProductPrice, ServerMarketProduct, ServerTransaction, ServerTransactionProduct, ApiResponse, ApiError, } from './types.js';
3
+ export { loadConfig, saveConfig, clearConfig, getCredentials, hasCredentials, promptLogin, requireCredentials, type Config, } from './config.js';
4
+ export { colors, colorize, success, error, warning, info, heading, formatStatus, formatBytes, formatDate, formatDateTime, formatJson, } from './formatter.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // ============================================================================
2
+ // Hetzner Robot API - Main Library Export
3
+ // ============================================================================
4
+ // Client
5
+ export { HetznerRobotClient } from './client.js';
6
+ // Configuration utilities (for CLI integration)
7
+ export { loadConfig, saveConfig, clearConfig, getCredentials, hasCredentials, promptLogin, requireCredentials, } from './config.js';
8
+ // Formatter utilities (for custom output)
9
+ export { colors, colorize, success, error, warning, info, heading, formatStatus, formatBytes, formatDate, formatDateTime, formatJson, } from './formatter.js';