@rollupdate/hctl 0.0.0-stage → 3.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1066 -2
  3. package/dist/auction/cache.d.ts +6 -0
  4. package/dist/auction/cache.js +55 -0
  5. package/dist/auction/client.d.ts +24 -0
  6. package/dist/auction/client.js +236 -0
  7. package/dist/auction/commands.d.ts +2 -0
  8. package/dist/auction/commands.js +440 -0
  9. package/dist/auction/diff-formatter.d.ts +3 -0
  10. package/dist/auction/diff-formatter.js +61 -0
  11. package/dist/auction/diff.d.ts +11 -0
  12. package/dist/auction/diff.js +28 -0
  13. package/dist/auction/formatter.d.ts +21 -0
  14. package/dist/auction/formatter.js +182 -0
  15. package/dist/auction/store.d.ts +24 -0
  16. package/dist/auction/store.js +177 -0
  17. package/dist/auction/types.d.ts +74 -0
  18. package/dist/auction/types.js +5 -0
  19. package/dist/cli.d.ts +2 -0
  20. package/dist/cli.js +74 -0
  21. package/dist/client.d.ts +2 -0
  22. package/dist/client.js +4 -0
  23. package/dist/cloud/browser-bootstrap.d.ts +16 -0
  24. package/dist/cloud/browser-bootstrap.js +438 -0
  25. package/dist/cloud/client.d.ts +520 -0
  26. package/dist/cloud/client.js +755 -0
  27. package/dist/cloud/commands/certificate.d.ts +2 -0
  28. package/dist/cloud/commands/certificate.js +108 -0
  29. package/dist/cloud/commands/context.d.ts +2 -0
  30. package/dist/cloud/commands/context.js +133 -0
  31. package/dist/cloud/commands/datacenter.d.ts +2 -0
  32. package/dist/cloud/commands/datacenter.js +24 -0
  33. package/dist/cloud/commands/firewall.d.ts +2 -0
  34. package/dist/cloud/commands/firewall.js +142 -0
  35. package/dist/cloud/commands/floating-ip.d.ts +2 -0
  36. package/dist/cloud/commands/floating-ip.js +138 -0
  37. package/dist/cloud/commands/image.d.ts +2 -0
  38. package/dist/cloud/commands/image.js +98 -0
  39. package/dist/cloud/commands/index.d.ts +2 -0
  40. package/dist/cloud/commands/index.js +47 -0
  41. package/dist/cloud/commands/inventory.d.ts +2 -0
  42. package/dist/cloud/commands/inventory.js +13 -0
  43. package/dist/cloud/commands/iso.d.ts +2 -0
  44. package/dist/cloud/commands/iso.js +27 -0
  45. package/dist/cloud/commands/load-balancer-type.d.ts +2 -0
  46. package/dist/cloud/commands/load-balancer-type.js +24 -0
  47. package/dist/cloud/commands/load-balancer.d.ts +2 -0
  48. package/dist/cloud/commands/load-balancer.js +269 -0
  49. package/dist/cloud/commands/location.d.ts +2 -0
  50. package/dist/cloud/commands/location.js +24 -0
  51. package/dist/cloud/commands/network.d.ts +2 -0
  52. package/dist/cloud/commands/network.js +157 -0
  53. package/dist/cloud/commands/placement-group.d.ts +2 -0
  54. package/dist/cloud/commands/placement-group.js +79 -0
  55. package/dist/cloud/commands/pricing.d.ts +2 -0
  56. package/dist/cloud/commands/pricing.js +15 -0
  57. package/dist/cloud/commands/primary-ip.d.ts +2 -0
  58. package/dist/cloud/commands/primary-ip.js +139 -0
  59. package/dist/cloud/commands/server-type.d.ts +2 -0
  60. package/dist/cloud/commands/server-type.js +24 -0
  61. package/dist/cloud/commands/server.d.ts +2 -0
  62. package/dist/cloud/commands/server.js +346 -0
  63. package/dist/cloud/commands/ssh-key.d.ts +2 -0
  64. package/dist/cloud/commands/ssh-key.js +94 -0
  65. package/dist/cloud/commands/volume.d.ts +2 -0
  66. package/dist/cloud/commands/volume.js +138 -0
  67. package/dist/cloud/context.d.ts +34 -0
  68. package/dist/cloud/context.js +215 -0
  69. package/dist/cloud/formatter.d.ts +40 -0
  70. package/dist/cloud/formatter.js +824 -0
  71. package/dist/cloud/helpers.d.ts +27 -0
  72. package/dist/cloud/helpers.js +67 -0
  73. package/dist/cloud/inventory.d.ts +100 -0
  74. package/dist/cloud/inventory.js +229 -0
  75. package/dist/cloud/pricing.d.ts +27 -0
  76. package/dist/cloud/pricing.js +103 -0
  77. package/dist/cloud/token-instructions.d.ts +2 -0
  78. package/dist/cloud/token-instructions.js +21 -0
  79. package/dist/cloud/types.d.ts +437 -0
  80. package/dist/cloud/types.js +5 -0
  81. package/dist/config.d.ts +1 -0
  82. package/dist/config.js +2 -0
  83. package/dist/formatter.d.ts +3 -0
  84. package/dist/formatter.js +6 -0
  85. package/dist/index.d.ts +12 -0
  86. package/dist/index.js +21 -0
  87. package/dist/robot/client.d.ts +246 -0
  88. package/dist/robot/client.js +719 -0
  89. package/dist/robot/commands/auth.d.ts +2 -0
  90. package/dist/robot/commands/auth.js +54 -0
  91. package/dist/robot/commands/boot.d.ts +2 -0
  92. package/dist/robot/commands/boot.js +76 -0
  93. package/dist/robot/commands/cancel.d.ts +2 -0
  94. package/dist/robot/commands/cancel.js +37 -0
  95. package/dist/robot/commands/failover.d.ts +2 -0
  96. package/dist/robot/commands/failover.js +46 -0
  97. package/dist/robot/commands/firewall.d.ts +2 -0
  98. package/dist/robot/commands/firewall.js +72 -0
  99. package/dist/robot/commands/index.d.ts +2 -0
  100. package/dist/robot/commands/index.js +36 -0
  101. package/dist/robot/commands/interactive.d.ts +2 -0
  102. package/dist/robot/commands/interactive.js +152 -0
  103. package/dist/robot/commands/ip.d.ts +2 -0
  104. package/dist/robot/commands/ip.js +55 -0
  105. package/dist/robot/commands/key.d.ts +2 -0
  106. package/dist/robot/commands/key.js +68 -0
  107. package/dist/robot/commands/order.d.ts +2 -0
  108. package/dist/robot/commands/order.js +33 -0
  109. package/dist/robot/commands/rdns.d.ts +2 -0
  110. package/dist/robot/commands/rdns.js +41 -0
  111. package/dist/robot/commands/reset.d.ts +2 -0
  112. package/dist/robot/commands/reset.js +77 -0
  113. package/dist/robot/commands/server.d.ts +2 -0
  114. package/dist/robot/commands/server.js +32 -0
  115. package/dist/robot/commands/storagebox.d.ts +2 -0
  116. package/dist/robot/commands/storagebox.js +126 -0
  117. package/dist/robot/commands/subnet.d.ts +2 -0
  118. package/dist/robot/commands/subnet.js +24 -0
  119. package/dist/robot/commands/traffic.d.ts +2 -0
  120. package/dist/robot/commands/traffic.js +23 -0
  121. package/dist/robot/commands/vswitch.d.ts +2 -0
  122. package/dist/robot/commands/vswitch.js +65 -0
  123. package/dist/robot/commands/wol.d.ts +2 -0
  124. package/dist/robot/commands/wol.js +20 -0
  125. package/dist/robot/formatter.d.ts +56 -0
  126. package/dist/robot/formatter.js +546 -0
  127. package/dist/robot/types.d.ts +353 -0
  128. package/dist/robot/types.js +5 -0
  129. package/dist/shared/config.d.ts +86 -0
  130. package/dist/shared/config.js +289 -0
  131. package/dist/shared/formatter.d.ts +29 -0
  132. package/dist/shared/formatter.js +121 -0
  133. package/dist/shared/helpers.d.ts +39 -0
  134. package/dist/shared/helpers.js +86 -0
  135. package/dist/shared/init.d.ts +2 -0
  136. package/dist/shared/init.js +107 -0
  137. package/dist/shared/reference.d.ts +5 -0
  138. package/dist/shared/reference.js +688 -0
  139. package/dist/shared/schemas.d.ts +1 -0
  140. package/dist/shared/schemas.js +1022 -0
  141. package/dist/types.d.ts +2 -0
  142. package/dist/types.js +2 -0
  143. package/package.json +131 -4
@@ -0,0 +1,346 @@
1
+ import { colorize, info, success } from "../../shared/formatter.js";
2
+ import { confirmAction, output } from "../../shared/helpers.js";
3
+ import { formatCloudServerDetails, formatCloudServerList, } from "../formatter.js";
4
+ import { cloudAction, parseLabels, resolveIdOrName, } from "../helpers.js";
5
+ function resolveServerId(client, idOrName) {
6
+ return resolveIdOrName(idOrName, "server", (name) => client.listServers({ name }));
7
+ }
8
+ async function handleServerList(client, options) {
9
+ const servers = await client.listServers({
10
+ label_selector: options.labelSelector,
11
+ name: options.name,
12
+ sort: options.sort,
13
+ status: options.status,
14
+ });
15
+ output(servers, formatCloudServerList, options);
16
+ }
17
+ async function handleServerDescribe(client, idOrName, options) {
18
+ const id = await resolveServerId(client, idOrName);
19
+ const srv = await client.getServer(id);
20
+ output(srv, formatCloudServerDetails, options);
21
+ }
22
+ async function handleServerCreate(client, options) {
23
+ const result = await client.createServer({
24
+ name: options.name,
25
+ server_type: options.type,
26
+ image: options.image,
27
+ location: options.location,
28
+ ssh_keys: options.sshKey,
29
+ user_data: options.userData,
30
+ labels: options.labels ? parseLabels(options.labels) : undefined,
31
+ firewalls: options.firewall?.map((id) => ({
32
+ firewall: Number.parseInt(id, 10),
33
+ })),
34
+ placement_group: options.placementGroup
35
+ ? Number.parseInt(options.placementGroup, 10)
36
+ : undefined,
37
+ start_after_create: options.startAfterCreate,
38
+ });
39
+ console.log(success(`Server '${result.server.name}' created (ID: ${result.server.id})`));
40
+ if (result.root_password) {
41
+ console.log(`Root password: ${colorize(result.root_password, "yellow")}`);
42
+ }
43
+ console.log(info(`IPv4: ${result.server.public_net.ipv4?.ip || "pending"}`));
44
+ }
45
+ async function handleServerDelete(client, idOrName, options) {
46
+ const id = await resolveServerId(client, idOrName);
47
+ const srv = await client.getServer(id);
48
+ if (!(await confirmAction(`Delete server '${srv.name}' (ID: ${id})?`, options))) {
49
+ return;
50
+ }
51
+ await client.deleteServer(id);
52
+ console.log(success(`Server '${srv.name}' (ID: ${id}) deleted.`));
53
+ }
54
+ async function handleServerUpdate(client, idOrName, options) {
55
+ const id = await resolveServerId(client, idOrName);
56
+ const { server: srv } = await client.updateServer(id, {
57
+ name: options.name,
58
+ });
59
+ console.log(success(`Server '${srv.name}' updated.`));
60
+ }
61
+ async function handleServerPoweron(client, idOrName) {
62
+ const id = await resolveServerId(client, idOrName);
63
+ await client.powerOnServer(id);
64
+ console.log(success(`Server ${id} powered on.`));
65
+ }
66
+ async function handleServerPoweroff(client, idOrName) {
67
+ const id = await resolveServerId(client, idOrName);
68
+ await client.powerOffServer(id);
69
+ console.log(success(`Server ${id} powered off.`));
70
+ }
71
+ async function handleServerReboot(client, idOrName) {
72
+ const id = await resolveServerId(client, idOrName);
73
+ await client.rebootServer(id);
74
+ console.log(success(`Server ${id} rebooted.`));
75
+ }
76
+ async function handleServerReset(client, idOrName) {
77
+ const id = await resolveServerId(client, idOrName);
78
+ await client.resetServer(id);
79
+ console.log(success(`Server ${id} reset.`));
80
+ }
81
+ async function handleServerShutdown(client, idOrName) {
82
+ const id = await resolveServerId(client, idOrName);
83
+ await client.shutdownServer(id);
84
+ console.log(success(`Server ${id} shutdown initiated.`));
85
+ }
86
+ async function handleServerRebuild(client, idOrName, options) {
87
+ const id = await resolveServerId(client, idOrName);
88
+ const result = await client.rebuildServer(id, options.image);
89
+ console.log(success(`Server ${id} rebuilding.`));
90
+ if (result.root_password) {
91
+ console.log(`Root password: ${colorize(result.root_password, "yellow")}`);
92
+ }
93
+ }
94
+ async function handleServerChangeType(client, idOrName, options) {
95
+ const id = await resolveServerId(client, idOrName);
96
+ await client.changeServerType(id, options.type, !!options.upgradeDisk);
97
+ console.log(success(`Server ${id} type change initiated.`));
98
+ }
99
+ async function handleServerEnableRescue(client, idOrName, options) {
100
+ const id = await resolveServerId(client, idOrName);
101
+ const result = await client.enableServerRescue(id, options.type, options.sshKey?.map(Number));
102
+ console.log(success("Rescue mode enabled."));
103
+ console.log(`Root password: ${colorize(result.root_password, "yellow")}`);
104
+ }
105
+ async function handleServerDisableRescue(client, idOrName) {
106
+ const id = await resolveServerId(client, idOrName);
107
+ await client.disableServerRescue(id);
108
+ console.log(success("Rescue mode disabled."));
109
+ }
110
+ async function handleServerEnableBackup(client, idOrName) {
111
+ const id = await resolveServerId(client, idOrName);
112
+ await client.enableServerBackup(id);
113
+ console.log(success(`Backups enabled for server ${id}.`));
114
+ }
115
+ async function handleServerDisableBackup(client, idOrName) {
116
+ const id = await resolveServerId(client, idOrName);
117
+ await client.disableServerBackup(id);
118
+ console.log(success(`Backups disabled for server ${id}.`));
119
+ }
120
+ async function handleServerCreateImage(client, idOrName, options) {
121
+ const id = await resolveServerId(client, idOrName);
122
+ const result = await client.createServerImage(id, {
123
+ description: options.description,
124
+ type: options.type,
125
+ });
126
+ console.log(success(`Image created (ID: ${result.image.id})`));
127
+ }
128
+ async function handleServerAttachIso(client, idOrName, options) {
129
+ const id = await resolveServerId(client, idOrName);
130
+ await client.attachIsoToServer(id, options.iso);
131
+ console.log(success(`ISO attached to server ${id}.`));
132
+ }
133
+ async function handleServerDetachIso(client, idOrName) {
134
+ const id = await resolveServerId(client, idOrName);
135
+ await client.detachIsoFromServer(id);
136
+ console.log(success(`ISO detached from server ${id}.`));
137
+ }
138
+ async function handleServerResetPassword(client, idOrName) {
139
+ const id = await resolveServerId(client, idOrName);
140
+ const result = await client.resetServerPassword(id);
141
+ console.log(success("Root password reset."));
142
+ console.log(`New password: ${colorize(result.root_password, "yellow")}`);
143
+ }
144
+ async function handleServerSetRdns(client, idOrName, options) {
145
+ const id = await resolveServerId(client, idOrName);
146
+ await client.setServerRdns(id, options.ip, options.dnsPtr);
147
+ console.log(success(`rDNS set: ${options.ip} -> ${options.dnsPtr}`));
148
+ }
149
+ async function handleServerEnableProtection(client, idOrName, options) {
150
+ const id = await resolveServerId(client, idOrName);
151
+ await client.enableServerProtection(id, {
152
+ delete: options.delete,
153
+ rebuild: options.rebuild,
154
+ });
155
+ console.log(success(`Protection enabled for server ${id}.`));
156
+ }
157
+ async function handleServerDisableProtection(client, idOrName) {
158
+ const id = await resolveServerId(client, idOrName);
159
+ await client.enableServerProtection(id, {
160
+ delete: false,
161
+ rebuild: false,
162
+ });
163
+ console.log(success(`Protection disabled for server ${id}.`));
164
+ }
165
+ async function handleServerRequestConsole(client, idOrName) {
166
+ const id = await resolveServerId(client, idOrName);
167
+ const result = await client.requestServerConsole(id);
168
+ console.log(success("Console ready."));
169
+ console.log(`WebSocket URL: ${result.wss_url}`);
170
+ console.log(`Password: ${colorize(result.password, "yellow")}`);
171
+ }
172
+ async function handleServerAttachToNetwork(client, idOrName, options) {
173
+ const id = await resolveServerId(client, idOrName);
174
+ await client.attachServerToNetwork(id, Number.parseInt(options.network, 10), options.ip);
175
+ console.log(success(`Server ${id} attached to network ${options.network}.`));
176
+ }
177
+ async function handleServerDetachFromNetwork(client, idOrName, options) {
178
+ const id = await resolveServerId(client, idOrName);
179
+ await client.detachServerFromNetwork(id, Number.parseInt(options.network, 10));
180
+ console.log(success(`Server ${id} detached from network ${options.network}.`));
181
+ }
182
+ async function handleServerAddLabel(client, idOrName, label) {
183
+ const id = await resolveServerId(client, idOrName);
184
+ const srv = await client.getServer(id);
185
+ const [key, value] = label.split("=");
186
+ const labels = { ...srv.labels, [key]: value || "" };
187
+ await client.updateServer(id, { labels });
188
+ console.log(success(`Label '${key}' added to server ${id}.`));
189
+ }
190
+ async function handleServerRemoveLabel(client, idOrName, key) {
191
+ const id = await resolveServerId(client, idOrName);
192
+ const srv = await client.getServer(id);
193
+ const labels = Object.fromEntries(Object.entries(srv.labels).filter(([k]) => k !== key));
194
+ await client.updateServer(id, { labels });
195
+ console.log(success(`Label '${key}' removed from server ${id}.`));
196
+ }
197
+ export function registerCloudServerCommands(parent) {
198
+ const server = parent
199
+ .command("server")
200
+ .description("Cloud server management");
201
+ server
202
+ .command("list")
203
+ .alias("ls")
204
+ .description("List all servers")
205
+ .option("-l, --label-selector <selector>", "Label selector")
206
+ .option("-n, --name <name>", "Filter by name")
207
+ .option("-s, --sort <field>", "Sort by field")
208
+ .option("--status <status>", "Filter by status")
209
+ .action(cloudAction(handleServerList));
210
+ server
211
+ .command("describe <id-or-name>")
212
+ .description("Show server details")
213
+ .action(cloudAction(handleServerDescribe));
214
+ server
215
+ .command("create")
216
+ .description("Create a new server")
217
+ .requiredOption("--name <name>", "Server name")
218
+ .requiredOption("--type <type>", "Server type")
219
+ .requiredOption("--image <image>", "Image to use")
220
+ .option("--location <location>", "Location")
221
+ .option("--ssh-key <keys...>", "SSH key IDs or names")
222
+ .option("--user-data <data>", "Cloud-init user data")
223
+ .option("--labels <labels>", "Labels as key=value pairs (comma-separated)")
224
+ .option("--firewall <ids...>", "Firewall IDs to apply")
225
+ .option("--placement-group <id>", "Placement group ID")
226
+ .option("--start-after-create", "Start server after creation", true)
227
+ .option("--no-start-after-create", "Do not start server after creation")
228
+ .action(cloudAction(handleServerCreate));
229
+ server
230
+ .command("delete <id-or-name>")
231
+ .description("Delete a server")
232
+ .option("-y, --yes", "Skip confirmation")
233
+ .action(cloudAction(handleServerDelete));
234
+ server
235
+ .command("update <id-or-name>")
236
+ .description("Update server")
237
+ .option("--name <name>", "New name")
238
+ .action(cloudAction(handleServerUpdate));
239
+ server
240
+ .command("poweron <id-or-name>")
241
+ .description("Power on a server")
242
+ .action(cloudAction(handleServerPoweron));
243
+ server
244
+ .command("poweroff <id-or-name>")
245
+ .description("Power off a server (hard)")
246
+ .action(cloudAction(handleServerPoweroff));
247
+ server
248
+ .command("reboot <id-or-name>")
249
+ .description("Soft reboot a server")
250
+ .action(cloudAction(handleServerReboot));
251
+ server
252
+ .command("reset <id-or-name>")
253
+ .description("Hard reset a server")
254
+ .action(cloudAction(handleServerReset));
255
+ server
256
+ .command("shutdown <id-or-name>")
257
+ .description("Gracefully shutdown a server (ACPI)")
258
+ .action(cloudAction(handleServerShutdown));
259
+ server
260
+ .command("rebuild <id-or-name>")
261
+ .description("Rebuild a server with a new image")
262
+ .requiredOption("--image <image>", "Image to rebuild with")
263
+ .action(cloudAction(handleServerRebuild));
264
+ server
265
+ .command("change-type <id-or-name>")
266
+ .description("Change server type (resize)")
267
+ .requiredOption("--type <type>", "New server type")
268
+ .option("--upgrade-disk", "Upgrade disk size (irreversible)", false)
269
+ .action(cloudAction(handleServerChangeType));
270
+ server
271
+ .command("enable-rescue <id-or-name>")
272
+ .description("Enable rescue mode")
273
+ .option("--type <type>", "Rescue system type", "linux64")
274
+ .option("--ssh-key <keys...>", "SSH key IDs")
275
+ .action(cloudAction(handleServerEnableRescue));
276
+ server
277
+ .command("disable-rescue <id-or-name>")
278
+ .description("Disable rescue mode")
279
+ .action(cloudAction(handleServerDisableRescue));
280
+ server
281
+ .command("enable-backup <id-or-name>")
282
+ .description("Enable automatic backups")
283
+ .action(cloudAction(handleServerEnableBackup));
284
+ server
285
+ .command("disable-backup <id-or-name>")
286
+ .description("Disable automatic backups")
287
+ .action(cloudAction(handleServerDisableBackup));
288
+ server
289
+ .command("create-image <id-or-name>")
290
+ .description("Create an image (snapshot) from server")
291
+ .option("--description <desc>", "Image description")
292
+ .option("--type <type>", "Image type (snapshot or backup)", "snapshot")
293
+ .action(cloudAction(handleServerCreateImage));
294
+ server
295
+ .command("attach-iso <id-or-name>")
296
+ .description("Attach an ISO to a server")
297
+ .requiredOption("--iso <iso>", "ISO name or ID")
298
+ .action(cloudAction(handleServerAttachIso));
299
+ server
300
+ .command("detach-iso <id-or-name>")
301
+ .description("Detach ISO from a server")
302
+ .action(cloudAction(handleServerDetachIso));
303
+ server
304
+ .command("reset-password <id-or-name>")
305
+ .description("Reset server root password")
306
+ .action(cloudAction(handleServerResetPassword));
307
+ server
308
+ .command("set-rdns <id-or-name>")
309
+ .description("Set reverse DNS for server")
310
+ .requiredOption("--ip <ip>", "IP address")
311
+ .requiredOption("--dns-ptr <ptr>", "DNS pointer")
312
+ .action(cloudAction(handleServerSetRdns));
313
+ server
314
+ .command("enable-protection <id-or-name>")
315
+ .description("Enable server protection")
316
+ .option("--delete", "Enable delete protection", true)
317
+ .option("--rebuild", "Enable rebuild protection", false)
318
+ .action(cloudAction(handleServerEnableProtection));
319
+ server
320
+ .command("disable-protection <id-or-name>")
321
+ .description("Disable server protection")
322
+ .action(cloudAction(handleServerDisableProtection));
323
+ server
324
+ .command("request-console <id-or-name>")
325
+ .description("Request a WebSocket VNC console")
326
+ .action(cloudAction(handleServerRequestConsole));
327
+ server
328
+ .command("attach-to-network <id-or-name>")
329
+ .description("Attach server to a network")
330
+ .requiredOption("--network <network>", "Network ID")
331
+ .option("--ip <ip>", "IP address in network")
332
+ .action(cloudAction(handleServerAttachToNetwork));
333
+ server
334
+ .command("detach-from-network <id-or-name>")
335
+ .description("Detach server from a network")
336
+ .requiredOption("--network <network>", "Network ID")
337
+ .action(cloudAction(handleServerDetachFromNetwork));
338
+ server
339
+ .command("add-label <id-or-name> <label>")
340
+ .description("Add a label (key=value)")
341
+ .action(cloudAction(handleServerAddLabel));
342
+ server
343
+ .command("remove-label <id-or-name> <key>")
344
+ .description("Remove a label by key")
345
+ .action(cloudAction(handleServerRemoveLabel));
346
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerCloudSshKeyCommands(parent: Command): void;
@@ -0,0 +1,94 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { error, success } from "../../shared/formatter.js";
3
+ import { confirmAction, output } from "../../shared/helpers.js";
4
+ import { formatCloudSshKeyDetails, formatCloudSshKeyList, } from "../formatter.js";
5
+ import { cloudAction, resolveIdOrName, } from "../helpers.js";
6
+ export function registerCloudSshKeyCommands(parent) {
7
+ const sshKey = parent.command("ssh-key").description("SSH key management");
8
+ sshKey
9
+ .command("list")
10
+ .alias("ls")
11
+ .description("List all SSH keys")
12
+ .option("-l, --label-selector <selector>", "Label selector")
13
+ .option("-s, --sort <field>", "Sort by field")
14
+ .action(cloudAction(async (client, options) => {
15
+ const keys = await client.listSshKeys({
16
+ label_selector: options.labelSelector,
17
+ sort: options.sort,
18
+ });
19
+ output(keys, formatCloudSshKeyList, options);
20
+ }));
21
+ sshKey
22
+ .command("describe <id-or-name>")
23
+ .description("Show SSH key details")
24
+ .action(cloudAction(async (client, idOrName, options) => {
25
+ const id = await resolveIdOrName(idOrName, "SSH key", (name) => client.listSshKeys({ name }));
26
+ const key = await client.getSshKey(id);
27
+ output(key, formatCloudSshKeyDetails, options);
28
+ }));
29
+ sshKey
30
+ .command("create")
31
+ .description("Create an SSH key")
32
+ .requiredOption("--name <name>", "Key name")
33
+ .option("--public-key <key>", "Public key string")
34
+ .option("--public-key-from-file <path>", "Read public key from file")
35
+ .action(cloudAction(async (client, options) => {
36
+ let pubKey = options.publicKey;
37
+ if (options.publicKeyFromFile) {
38
+ pubKey = readFileSync(options.publicKeyFromFile, "utf-8").trim();
39
+ }
40
+ if (!pubKey) {
41
+ console.error(error("Provide --public-key or --public-key-from-file"));
42
+ process.exit(1);
43
+ }
44
+ const { ssh_key: key } = await client.createSshKey({
45
+ name: options.name,
46
+ public_key: pubKey,
47
+ });
48
+ console.log(success(`SSH key '${key.name}' created (ID: ${key.id})`));
49
+ }));
50
+ sshKey
51
+ .command("delete <id-or-name>")
52
+ .description("Delete an SSH key")
53
+ .option("-y, --yes", "Skip confirmation")
54
+ .action(cloudAction(async (client, idOrName, options) => {
55
+ const id = await resolveIdOrName(idOrName, "SSH key", (name) => client.listSshKeys({ name }));
56
+ const key = await client.getSshKey(id);
57
+ if (!(await confirmAction(`Delete SSH key '${key.name}' (ID: ${id})?`, options))) {
58
+ return;
59
+ }
60
+ await client.deleteSshKey(id);
61
+ console.log(success(`SSH key '${key.name}' (ID: ${id}) deleted.`));
62
+ }));
63
+ sshKey
64
+ .command("update <id-or-name>")
65
+ .description("Update SSH key")
66
+ .option("--name <name>", "New name")
67
+ .action(cloudAction(async (client, idOrName, options) => {
68
+ const id = await resolveIdOrName(idOrName, "SSH key", (name) => client.listSshKeys({ name }));
69
+ await client.updateSshKey(id, { name: options.name });
70
+ console.log(success(`SSH key ${id} updated.`));
71
+ }));
72
+ sshKey
73
+ .command("add-label <id-or-name> <label>")
74
+ .description("Add a label (key=value)")
75
+ .action(cloudAction(async (client, idOrName, label) => {
76
+ const id = await resolveIdOrName(idOrName, "SSH key", (name) => client.listSshKeys({ name }));
77
+ const key = await client.getSshKey(id);
78
+ const [k, v] = label.split("=");
79
+ await client.updateSshKey(id, {
80
+ labels: { ...key.labels, [k]: v || "" },
81
+ });
82
+ console.log(success(`Label '${k}' added.`));
83
+ }));
84
+ sshKey
85
+ .command("remove-label <id-or-name> <key>")
86
+ .description("Remove a label")
87
+ .action(cloudAction(async (client, idOrName, key) => {
88
+ const id = await resolveIdOrName(idOrName, "SSH key", (name) => client.listSshKeys({ name }));
89
+ const sshKeyData = await client.getSshKey(id);
90
+ const labels = Object.fromEntries(Object.entries(sshKeyData.labels).filter(([k]) => k !== key));
91
+ await client.updateSshKey(id, { labels });
92
+ console.log(success(`Label '${key}' removed.`));
93
+ }));
94
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerVolumeCommands(parent: Command): void;
@@ -0,0 +1,138 @@
1
+ import { success } from "../../shared/formatter.js";
2
+ import { confirmAction, output } from "../../shared/helpers.js";
3
+ import { formatVolumeDetails, formatVolumeList } from "../formatter.js";
4
+ import { cloudAction, resolveIdOrName, } from "../helpers.js";
5
+ export function registerVolumeCommands(parent) {
6
+ const volume = parent.command("volume").description("Volume management");
7
+ volume
8
+ .command("list")
9
+ .alias("ls")
10
+ .description("List all volumes")
11
+ .option("-l, --label-selector <selector>", "Label selector")
12
+ .option("-s, --sort <field>", "Sort by field")
13
+ .option("--status <status>", "Filter by status")
14
+ .action(cloudAction(async (client, options) => {
15
+ const volumes = await client.listVolumes({
16
+ label_selector: options.labelSelector,
17
+ sort: options.sort,
18
+ status: options.status,
19
+ });
20
+ output(volumes, formatVolumeList, options);
21
+ }));
22
+ volume
23
+ .command("describe <id-or-name>")
24
+ .description("Show volume details")
25
+ .action(cloudAction(async (client, idOrName, options) => {
26
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
27
+ const vol = await client.getVolume(id);
28
+ output(vol, formatVolumeDetails, options);
29
+ }));
30
+ volume
31
+ .command("create")
32
+ .description("Create a volume")
33
+ .requiredOption("--name <name>", "Volume name")
34
+ .requiredOption("--size <size>", "Size in GB")
35
+ .option("--location <loc>", "Location")
36
+ .option("--server <id>", "Server to attach to")
37
+ .option("--format <format>", "Filesystem format (ext4, xfs)")
38
+ .option("--automount", "Auto-mount on server")
39
+ .action(cloudAction(async (client, options) => {
40
+ const { volume: vol } = await client.createVolume({
41
+ name: options.name,
42
+ size: Number.parseInt(options.size, 10),
43
+ location: options.location,
44
+ server: options.server
45
+ ? Number.parseInt(options.server, 10)
46
+ : undefined,
47
+ format: options.format,
48
+ automount: options.automount,
49
+ });
50
+ console.log(success(`Volume '${vol.name}' created (ID: ${vol.id}, ${vol.size} GB)`));
51
+ }));
52
+ volume
53
+ .command("delete <id-or-name>")
54
+ .description("Delete a volume")
55
+ .option("-y, --yes", "Skip confirmation")
56
+ .action(cloudAction(async (client, idOrName, options) => {
57
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
58
+ const vol = await client.getVolume(id);
59
+ if (!(await confirmAction(`Delete volume '${vol.name}' (ID: ${id})?`, options))) {
60
+ return;
61
+ }
62
+ await client.deleteVolume(id);
63
+ console.log(success(`Volume '${vol.name}' (ID: ${id}) deleted.`));
64
+ }));
65
+ volume
66
+ .command("update <id-or-name>")
67
+ .description("Update volume")
68
+ .option("--name <name>", "New name")
69
+ .action(cloudAction(async (client, idOrName, options) => {
70
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
71
+ await client.updateVolume(id, { name: options.name });
72
+ console.log(success(`Volume ${id} updated.`));
73
+ }));
74
+ volume
75
+ .command("attach <id-or-name> <server>")
76
+ .description("Attach volume to server")
77
+ .option("--automount", "Auto-mount on server")
78
+ .action(cloudAction(async (client, idOrName, server, options) => {
79
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
80
+ await client.attachVolume(id, Number.parseInt(server, 10), options.automount);
81
+ console.log(success(`Volume ${id} attached to server ${server}.`));
82
+ }));
83
+ volume
84
+ .command("detach <id-or-name>")
85
+ .description("Detach volume from server")
86
+ .action(cloudAction(async (client, idOrName) => {
87
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
88
+ await client.detachVolume(id);
89
+ console.log(success(`Volume ${id} detached.`));
90
+ }));
91
+ volume
92
+ .command("resize <id-or-name>")
93
+ .description("Resize a volume")
94
+ .requiredOption("--size <size>", "New size in GB")
95
+ .action(cloudAction(async (client, idOrName, options) => {
96
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
97
+ await client.resizeVolume(id, Number.parseInt(options.size, 10));
98
+ console.log(success(`Volume ${id} resized to ${options.size} GB.`));
99
+ }));
100
+ volume
101
+ .command("enable-protection <id-or-name>")
102
+ .description("Enable delete protection")
103
+ .action(cloudAction(async (client, idOrName) => {
104
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
105
+ await client.changeVolumeProtection(id, true);
106
+ console.log(success(`Protection enabled for volume ${id}.`));
107
+ }));
108
+ volume
109
+ .command("disable-protection <id-or-name>")
110
+ .description("Disable delete protection")
111
+ .action(cloudAction(async (client, idOrName) => {
112
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
113
+ await client.changeVolumeProtection(id, false);
114
+ console.log(success(`Protection disabled for volume ${id}.`));
115
+ }));
116
+ volume
117
+ .command("add-label <id-or-name> <label>")
118
+ .description("Add a label (key=value)")
119
+ .action(cloudAction(async (client, idOrName, label) => {
120
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
121
+ const vol = await client.getVolume(id);
122
+ const [key, value] = label.split("=");
123
+ await client.updateVolume(id, {
124
+ labels: { ...vol.labels, [key]: value || "" },
125
+ });
126
+ console.log(success(`Label '${key}' added.`));
127
+ }));
128
+ volume
129
+ .command("remove-label <id-or-name> <key>")
130
+ .description("Remove a label")
131
+ .action(cloudAction(async (client, idOrName, key) => {
132
+ const id = await resolveIdOrName(idOrName, "volume", (name) => client.listVolumes({ name }));
133
+ const vol = await client.getVolume(id);
134
+ const labels = Object.fromEntries(Object.entries(vol.labels).filter(([k]) => k !== key));
135
+ await client.updateVolume(id, { labels });
136
+ console.log(success(`Label '${key}' removed.`));
137
+ }));
138
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Create a new cloud context.
3
+ */
4
+ export declare function createContext(name: string, token: string, options?: {
5
+ verify?: boolean;
6
+ }): Promise<void>;
7
+ /**
8
+ * Set the active context.
9
+ */
10
+ export declare function useContext(name: string): void;
11
+ /**
12
+ * Delete a context.
13
+ */
14
+ export declare function deleteContext(name: string): Promise<void>;
15
+ /**
16
+ * List all contexts.
17
+ */
18
+ export declare function listContexts(): {
19
+ name: string;
20
+ active: boolean;
21
+ }[];
22
+ /**
23
+ * Get the active context name.
24
+ */
25
+ export declare function getActiveContext(): string | null;
26
+ /**
27
+ * Resolve the cloud API token.
28
+ * Priority: --token flag > HETZNER_CLOUD_TOKEN env > active context
29
+ */
30
+ export declare function resolveToken(flagToken?: string): Promise<string>;
31
+ /**
32
+ * Validate that a token authenticates against the Hetzner Cloud API.
33
+ */
34
+ export declare function validateCloudToken(token: string): Promise<void>;