@ebowwa/hetzner 0.1.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 (46) hide show
  1. package/actions.js +802 -0
  2. package/actions.ts +1053 -0
  3. package/auth.js +35 -0
  4. package/auth.ts +37 -0
  5. package/bootstrap/FIREWALL.md +326 -0
  6. package/bootstrap/KERNEL-HARDENING.md +258 -0
  7. package/bootstrap/SECURITY-INTEGRATION.md +281 -0
  8. package/bootstrap/TESTING.md +301 -0
  9. package/bootstrap/cloud-init.js +279 -0
  10. package/bootstrap/cloud-init.ts +394 -0
  11. package/bootstrap/firewall.js +279 -0
  12. package/bootstrap/firewall.ts +342 -0
  13. package/bootstrap/genesis.js +406 -0
  14. package/bootstrap/genesis.ts +518 -0
  15. package/bootstrap/index.js +35 -0
  16. package/bootstrap/index.ts +71 -0
  17. package/bootstrap/kernel-hardening.js +266 -0
  18. package/bootstrap/kernel-hardening.test.ts +230 -0
  19. package/bootstrap/kernel-hardening.ts +272 -0
  20. package/bootstrap/security-audit.js +118 -0
  21. package/bootstrap/security-audit.ts +124 -0
  22. package/bootstrap/ssh-hardening.js +182 -0
  23. package/bootstrap/ssh-hardening.ts +192 -0
  24. package/client.js +137 -0
  25. package/client.ts +177 -0
  26. package/config.js +5 -0
  27. package/config.ts +5 -0
  28. package/errors.js +270 -0
  29. package/errors.ts +371 -0
  30. package/index.js +28 -0
  31. package/index.ts +55 -0
  32. package/package.json +56 -0
  33. package/pricing.js +284 -0
  34. package/pricing.ts +422 -0
  35. package/schemas.js +660 -0
  36. package/schemas.ts +765 -0
  37. package/server-status.ts +81 -0
  38. package/servers.js +424 -0
  39. package/servers.ts +568 -0
  40. package/ssh-keys.js +90 -0
  41. package/ssh-keys.ts +122 -0
  42. package/ssh-setup.ts +218 -0
  43. package/types.js +96 -0
  44. package/types.ts +389 -0
  45. package/volumes.js +172 -0
  46. package/volumes.ts +229 -0
package/servers.ts ADDED
@@ -0,0 +1,568 @@
1
+ /**
2
+ * Hetzner server operations
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import type {
7
+ HetznerServer,
8
+ CreateServerOptions,
9
+ HetznerAction,
10
+ CreateServerResponse,
11
+ } from "./types.js";
12
+ import type { HetznerClient } from "./client.js";
13
+ import {
14
+ HetznerListServersResponseSchema,
15
+ HetznerGetServerResponseSchema,
16
+ HetznerCreateServerResponseSchema,
17
+ HetznerActionSchema,
18
+ } from "./schemas.js";
19
+
20
+ export class ServerOperations {
21
+ constructor(private client: HetznerClient) {}
22
+
23
+ /**
24
+ * List all servers
25
+ */
26
+ async list(): Promise<HetznerServer[]> {
27
+ const response = await this.client.request<{ servers: HetznerServer[] }>(
28
+ "/servers",
29
+ );
30
+
31
+ // Validate response with Zod
32
+ const validated = HetznerListServersResponseSchema.safeParse(response);
33
+ if (!validated.success) {
34
+ console.warn('Hetzner list servers validation warning:', validated.error.issues);
35
+ return response.servers; // Return unvalidated data for backward compatibility
36
+ }
37
+
38
+ return validated.data.servers;
39
+ }
40
+
41
+ /**
42
+ * Get a specific server by ID
43
+ */
44
+ async get(id: number): Promise<HetznerServer> {
45
+ const response = await this.client.request<{ server: HetznerServer }>(
46
+ `/servers/${id}`,
47
+ );
48
+
49
+ // Validate response with Zod
50
+ const validated = HetznerGetServerResponseSchema.safeParse(response);
51
+ if (!validated.success) {
52
+ console.warn('Hetzner get server validation warning:', validated.error.issues);
53
+ return response.server; // Return unvalidated data for backward compatibility
54
+ }
55
+
56
+ return validated.data.server;
57
+ }
58
+
59
+ /**
60
+ * Create a new server
61
+ *
62
+ * @param options - Server creation options
63
+ * @returns Create server response including server, action, and next_actions
64
+ */
65
+ async create(options: CreateServerOptions): Promise<CreateServerResponse> {
66
+ // Validate input with Zod
67
+ const createServerOptionsSchema = z.object({
68
+ name: z.string().min(1),
69
+ server_type: z.string().min(1).default("cpx11"),
70
+ image: z.string().min(1).default("ubuntu-24.04"),
71
+ location: z.string().min(1).optional(),
72
+ datacenter: z.string().min(1).optional(),
73
+ ssh_keys: z.array(z.union([z.string(), z.number()])).default([]),
74
+ volumes: z.array(z.number()).default([]),
75
+ labels: z.record(z.string(), z.any()).optional(),
76
+ start_after_create: z.boolean().default(true),
77
+ user_data: z.string().optional(),
78
+ });
79
+
80
+ const validatedOptions = createServerOptionsSchema.safeParse(options);
81
+ if (!validatedOptions.success) {
82
+ throw new Error(`Invalid server options: ${validatedOptions.error.issues.map(i => i.message).join(', ')}`);
83
+ }
84
+
85
+ // Ensure either location or datacenter, not both
86
+ if (validatedOptions.data.location && validatedOptions.data.datacenter) {
87
+ throw new Error('Cannot specify both location and datacenter');
88
+ }
89
+
90
+ const body = {
91
+ name: validatedOptions.data.name,
92
+ server_type: validatedOptions.data.server_type,
93
+ image: validatedOptions.data.image,
94
+ ...(validatedOptions.data.location && { location: validatedOptions.data.location }),
95
+ ...(validatedOptions.data.datacenter && { datacenter: { id: validatedOptions.data.datacenter } }),
96
+ ssh_keys: validatedOptions.data.ssh_keys,
97
+ volumes: validatedOptions.data.volumes,
98
+ ...(validatedOptions.data.labels && { labels: validatedOptions.data.labels }),
99
+ start_after_create: validatedOptions.data.start_after_create,
100
+ ...(validatedOptions.data.user_data && { user_data: validatedOptions.data.user_data }),
101
+ };
102
+
103
+ console.log('[Hetzner] Creating server with body:', JSON.stringify(body, null, 2));
104
+
105
+ const response = await this.client.request<CreateServerResponse>("/servers", {
106
+ method: "POST",
107
+ body: JSON.stringify(body),
108
+ });
109
+
110
+ // Validate response with Zod
111
+ const validatedResponse = HetznerCreateServerResponseSchema.safeParse(response);
112
+ if (!validatedResponse.success) {
113
+ console.warn('Hetzner create server validation warning:', validatedResponse.error.issues);
114
+ return response; // Return unvalidated data for backward compatibility
115
+ }
116
+
117
+ return validatedResponse.data as CreateServerResponse;
118
+ }
119
+
120
+ /**
121
+ * Create a new server and wait for it to be ready
122
+ *
123
+ * This convenience method creates a server and waits for the initial action to complete.
124
+ *
125
+ * @param options - Server creation options
126
+ * @param onProgress - Optional progress callback
127
+ * @returns Server once ready
128
+ */
129
+ async createAndWait(
130
+ options: CreateServerOptions,
131
+ onProgress?: (action: HetznerAction) => void
132
+ ): Promise<HetznerServer> {
133
+ const response = await this.create(options);
134
+
135
+ // Build wait options (only include onProgress if defined)
136
+ const waitOptions = onProgress !== undefined ? { onProgress } : {};
137
+
138
+ // Wait for the main create action to complete
139
+ if (response.action.status === 'running') {
140
+ await this.client.actions.waitFor(response.action.id, waitOptions);
141
+ }
142
+
143
+ // Wait for any next actions (e.g., start server)
144
+ if (response.next_actions.length > 0) {
145
+ for (const action of response.next_actions) {
146
+ await this.client.actions.waitFor(action.id, waitOptions);
147
+ }
148
+ }
149
+
150
+ // Return the server object
151
+ const server = await this.get(response.server.id);
152
+ return server;
153
+ }
154
+
155
+ /**
156
+ * Delete a server
157
+ *
158
+ * @param id - Server ID
159
+ * @returns Action for server deletion
160
+ */
161
+ async delete(id: number): Promise<HetznerAction> {
162
+ // Validate ID with Zod
163
+ const serverIdSchema = z.number().int().positive();
164
+ const validatedId = serverIdSchema.safeParse(id);
165
+ if (!validatedId.success) {
166
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
167
+ }
168
+
169
+ const response = await this.client.request<{ action: HetznerAction }>(
170
+ `/servers/${validatedId.data}`,
171
+ { method: "DELETE" }
172
+ );
173
+
174
+ const validated = HetznerActionSchema.safeParse(response.action);
175
+ if (!validated.success) {
176
+ console.warn('Hetzner delete server validation warning:', validated.error.issues);
177
+ return response.action;
178
+ }
179
+
180
+ return validated.data as HetznerAction;
181
+ }
182
+
183
+ /**
184
+ * Delete a server and wait for completion
185
+ *
186
+ * @param id - Server ID
187
+ * @param onProgress - Optional progress callback
188
+ */
189
+ async deleteAndWait(
190
+ id: number,
191
+ onProgress?: (action: HetznerAction) => void
192
+ ): Promise<void> {
193
+ const action = await this.delete(id);
194
+ await this.client.actions.waitFor(action.id, { onProgress });
195
+ }
196
+
197
+ /**
198
+ * Power on a server
199
+ *
200
+ * @param id - Server ID
201
+ * @returns Action for server power on
202
+ */
203
+ async powerOn(id: number): Promise<HetznerAction> {
204
+ // Validate ID with Zod
205
+ const serverIdSchema = z.number().int().positive();
206
+ const validatedId = serverIdSchema.safeParse(id);
207
+ if (!validatedId.success) {
208
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
209
+ }
210
+
211
+ const response = await this.client.request<{ action: HetznerAction }>(
212
+ `/servers/${validatedId.data}/actions/poweron`,
213
+ { method: "POST" }
214
+ );
215
+
216
+ const validated = HetznerActionSchema.safeParse(response.action);
217
+ if (!validated.success) {
218
+ console.warn('Hetzner power on validation warning:', validated.error.issues);
219
+ return response.action;
220
+ }
221
+
222
+ return validated.data as HetznerAction;
223
+ }
224
+
225
+ /**
226
+ * Power on a server and wait for completion
227
+ *
228
+ * @param id - Server ID
229
+ * @param onProgress - Optional progress callback
230
+ */
231
+ async powerOnAndWait(
232
+ id: number,
233
+ onProgress?: (action: HetznerAction) => void
234
+ ): Promise<HetznerAction> {
235
+ const action = await this.powerOn(id);
236
+ return await this.client.actions.waitFor(action.id, { onProgress });
237
+ }
238
+
239
+ /**
240
+ * Power off a server
241
+ *
242
+ * @param id - Server ID
243
+ * @returns Action for server power off
244
+ */
245
+ async powerOff(id: number): Promise<HetznerAction> {
246
+ // Validate ID with Zod
247
+ const serverIdSchema = z.number().int().positive();
248
+ const validatedId = serverIdSchema.safeParse(id);
249
+ if (!validatedId.success) {
250
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
251
+ }
252
+
253
+ const response = await this.client.request<{ action: HetznerAction }>(
254
+ `/servers/${validatedId.data}/actions/poweroff`,
255
+ { method: "POST" }
256
+ );
257
+
258
+ const validated = HetznerActionSchema.safeParse(response.action);
259
+ if (!validated.success) {
260
+ console.warn('Hetzner power off validation warning:', validated.error.issues);
261
+ return response.action;
262
+ }
263
+
264
+ return validated.data as HetznerAction;
265
+ }
266
+
267
+ /**
268
+ * Power off a server and wait for completion
269
+ *
270
+ * @param id - Server ID
271
+ * @param onProgress - Optional progress callback
272
+ */
273
+ async powerOffAndWait(
274
+ id: number,
275
+ onProgress?: (action: HetznerAction) => void
276
+ ): Promise<HetznerAction> {
277
+ const action = await this.powerOff(id);
278
+ return await this.client.actions.waitFor(action.id, { onProgress });
279
+ }
280
+
281
+ /**
282
+ * Reboot a server
283
+ *
284
+ * @param id - Server ID
285
+ * @returns Action for server reboot
286
+ */
287
+ async reboot(id: number): Promise<HetznerAction> {
288
+ // Validate ID with Zod
289
+ const serverIdSchema = z.number().int().positive();
290
+ const validatedId = serverIdSchema.safeParse(id);
291
+ if (!validatedId.success) {
292
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
293
+ }
294
+
295
+ const response = await this.client.request<{ action: HetznerAction }>(
296
+ `/servers/${validatedId.data}/actions/reboot`,
297
+ { method: "POST" }
298
+ );
299
+
300
+ const validated = HetznerActionSchema.safeParse(response.action);
301
+ if (!validated.success) {
302
+ console.warn('Hetzner reboot validation warning:', validated.error.issues);
303
+ return response.action;
304
+ }
305
+
306
+ return validated.data as HetznerAction;
307
+ }
308
+
309
+ /**
310
+ * Reboot a server and wait for completion
311
+ *
312
+ * @param id - Server ID
313
+ * @param onProgress - Optional progress callback
314
+ */
315
+ async rebootAndWait(
316
+ id: number,
317
+ onProgress?: (action: HetznerAction) => void
318
+ ): Promise<HetznerAction> {
319
+ const action = await this.reboot(id);
320
+ return await this.client.actions.waitFor(action.id, { onProgress });
321
+ }
322
+
323
+ /**
324
+ * Shutdown a server gracefully
325
+ *
326
+ * @param id - Server ID
327
+ * @returns Action for server shutdown
328
+ */
329
+ async shutdown(id: number): Promise<HetznerAction> {
330
+ // Validate ID with Zod
331
+ const serverIdSchema = z.number().int().positive();
332
+ const validatedId = serverIdSchema.safeParse(id);
333
+ if (!validatedId.success) {
334
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
335
+ }
336
+
337
+ const response = await this.client.request<{ action: HetznerAction }>(
338
+ `/servers/${validatedId.data}/actions/shutdown`,
339
+ { method: "POST" }
340
+ );
341
+
342
+ const validated = HetznerActionSchema.safeParse(response.action);
343
+ if (!validated.success) {
344
+ console.warn('Hetzner shutdown validation warning:', validated.error.issues);
345
+ return response.action;
346
+ }
347
+
348
+ return validated.data as HetznerAction;
349
+ }
350
+
351
+ /**
352
+ * Shutdown a server and wait for completion
353
+ *
354
+ * @param id - Server ID
355
+ * @param onProgress - Optional progress callback
356
+ */
357
+ async shutdownAndWait(
358
+ id: number,
359
+ onProgress?: (action: HetznerAction) => void
360
+ ): Promise<HetznerAction> {
361
+ const action = await this.shutdown(id);
362
+ return await this.client.actions.waitFor(action.id, { onProgress });
363
+ }
364
+
365
+ /**
366
+ * Reset a server
367
+ *
368
+ * @param id - Server ID
369
+ * @returns Action for server reset
370
+ */
371
+ async reset(id: number): Promise<HetznerAction> {
372
+ // Validate ID with Zod
373
+ const serverIdSchema = z.number().int().positive();
374
+ const validatedId = serverIdSchema.safeParse(id);
375
+ if (!validatedId.success) {
376
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
377
+ }
378
+
379
+ const response = await this.client.request<{ action: HetznerAction }>(
380
+ `/servers/${validatedId.data}/actions/reset`,
381
+ { method: "POST" }
382
+ );
383
+
384
+ const validated = HetznerActionSchema.safeParse(response.action);
385
+ if (!validated.success) {
386
+ console.warn('Hetzner reset validation warning:', validated.error.issues);
387
+ return response.action;
388
+ }
389
+
390
+ return validated.data as HetznerAction;
391
+ }
392
+
393
+ /**
394
+ * Reset a server and wait for completion
395
+ *
396
+ * @param id - Server ID
397
+ * @param onProgress - Optional progress callback
398
+ */
399
+ async resetAndWait(
400
+ id: number,
401
+ onProgress?: (action: HetznerAction) => void
402
+ ): Promise<HetznerAction> {
403
+ const action = await this.reset(id);
404
+ return await this.client.actions.waitFor(action.id, { onProgress });
405
+ }
406
+
407
+ /**
408
+ * Rebuild a server from an image
409
+ *
410
+ * @param id - Server ID
411
+ * @param image - Image ID or name
412
+ * @returns Action for server rebuild
413
+ */
414
+ async rebuild(id: number, image: string): Promise<HetznerAction> {
415
+ // Validate ID with Zod
416
+ const serverIdSchema = z.number().int().positive();
417
+ const validatedId = serverIdSchema.safeParse(id);
418
+ if (!validatedId.success) {
419
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
420
+ }
421
+
422
+ const response = await this.client.request<{ action: HetznerAction }>(
423
+ `/servers/${validatedId.data}/actions/rebuild`,
424
+ {
425
+ method: "POST",
426
+ body: JSON.stringify({ image }),
427
+ }
428
+ );
429
+
430
+ const validated = HetznerActionSchema.safeParse(response.action);
431
+ if (!validated.success) {
432
+ console.warn('Hetzner rebuild validation warning:', validated.error.issues);
433
+ return response.action;
434
+ }
435
+
436
+ return validated.data as HetznerAction;
437
+ }
438
+
439
+ /**
440
+ * Enable rescue mode for a server
441
+ *
442
+ * @param id - Server ID
443
+ * @param options - Rescue mode options
444
+ * @returns Action for enabling rescue mode
445
+ */
446
+ async enableRescue(
447
+ id: number,
448
+ options?: { type?: string; ssh_keys?: number[] }
449
+ ): Promise<HetznerAction> {
450
+ // Validate ID with Zod
451
+ const serverIdSchema = z.number().int().positive();
452
+ const validatedId = serverIdSchema.safeParse(id);
453
+ if (!validatedId.success) {
454
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
455
+ }
456
+
457
+ const response = await this.client.request<{ action: HetznerAction }>(
458
+ `/servers/${validatedId.data}/actions/enable_rescue`,
459
+ {
460
+ method: "POST",
461
+ body: JSON.stringify(options || {}),
462
+ }
463
+ );
464
+
465
+ const validated = HetznerActionSchema.safeParse(response.action);
466
+ if (!validated.success) {
467
+ console.warn('Hetzner enable rescue validation warning:', validated.error.issues);
468
+ return response.action;
469
+ }
470
+
471
+ return validated.data as HetznerAction;
472
+ }
473
+
474
+ /**
475
+ * Disable rescue mode for a server
476
+ *
477
+ * @param id - Server ID
478
+ * @returns Action for disabling rescue mode
479
+ */
480
+ async disableRescue(id: number): Promise<HetznerAction> {
481
+ // Validate ID with Zod
482
+ const serverIdSchema = z.number().int().positive();
483
+ const validatedId = serverIdSchema.safeParse(id);
484
+ if (!validatedId.success) {
485
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
486
+ }
487
+
488
+ const response = await this.client.request<{ action: HetznerAction }>(
489
+ `/servers/${validatedId.data}/actions/disable_rescue`,
490
+ { method: "POST" }
491
+ );
492
+
493
+ const validated = HetznerActionSchema.safeParse(response.action);
494
+ if (!validated.success) {
495
+ console.warn('Hetzner disable rescue validation warning:', validated.error.issues);
496
+ return response.action;
497
+ }
498
+
499
+ return validated.data as HetznerAction;
500
+ }
501
+
502
+ /**
503
+ * Change server type
504
+ *
505
+ * @param id - Server ID
506
+ * @param serverType - New server type
507
+ * @param upgradeDisk - Whether to upgrade disk (default: false)
508
+ * @returns Action for changing server type
509
+ */
510
+ async changeType(
511
+ id: number,
512
+ serverType: string,
513
+ upgradeDisk: boolean = false
514
+ ): Promise<HetznerAction> {
515
+ // Validate ID with Zod
516
+ const serverIdSchema = z.number().int().positive();
517
+ const validatedId = serverIdSchema.safeParse(id);
518
+ if (!validatedId.success) {
519
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
520
+ }
521
+
522
+ const response = await this.client.request<{ action: HetznerAction }>(
523
+ `/servers/${validatedId.data}/actions/change_type`,
524
+ {
525
+ method: "POST",
526
+ body: JSON.stringify({ server_type: serverType, upgrade_disk: upgradeDisk }),
527
+ }
528
+ );
529
+
530
+ const validated = HetznerActionSchema.safeParse(response.action);
531
+ if (!validated.success) {
532
+ console.warn('Hetzner change type validation warning:', validated.error.issues);
533
+ return response.action;
534
+ }
535
+
536
+ return validated.data as HetznerAction;
537
+ }
538
+
539
+ /**
540
+ * Get actions for a specific server
541
+ *
542
+ * @param id - Server ID
543
+ * @param options - Optional filters (status, sort, etc.)
544
+ * @returns Array of server actions
545
+ */
546
+ async getActions(
547
+ id: number,
548
+ options?: { status?: "running" | "success" | "error"; sort?: string }
549
+ ): Promise<HetznerAction[]> {
550
+ // Validate ID with Zod
551
+ const serverIdSchema = z.number().int().positive();
552
+ const validatedId = serverIdSchema.safeParse(id);
553
+ if (!validatedId.success) {
554
+ throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
555
+ }
556
+
557
+ const params = new URLSearchParams();
558
+ if (options?.status) params.append("status", options.status);
559
+ if (options?.sort) params.append("sort", options.sort);
560
+
561
+ const query = params.toString();
562
+ const response = await this.client.request<{ actions: HetznerAction[] }>(
563
+ `/servers/${validatedId.data}/actions${query ? `?${query}` : ""}`
564
+ );
565
+
566
+ return response.actions;
567
+ }
568
+ }
package/ssh-keys.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Hetzner SSH key operations
3
+ */
4
+ import { HetznerListSSHKeysResponseSchema, HetznerGetSSHKeyResponseSchema, HetznerCreateSSHKeyRequestSchema, HetznerCreateSSHKeyResponseSchema, } from "./schemas.js";
5
+ export class SSHKeyOperations {
6
+ client;
7
+ constructor(client) {
8
+ this.client = client;
9
+ }
10
+ /**
11
+ * List all SSH keys
12
+ */
13
+ async list() {
14
+ const response = await this.client.request("/ssh_keys");
15
+ // Validate response with Zod
16
+ const validated = HetznerListSSHKeysResponseSchema.safeParse(response);
17
+ if (!validated.success) {
18
+ console.warn('Hetzner list SSH keys validation warning:', validated.error.issues);
19
+ return response.ssh_keys; // Return unvalidated data for backward compatibility
20
+ }
21
+ return validated.data.ssh_keys;
22
+ }
23
+ /**
24
+ * Get a specific SSH key by ID or name
25
+ */
26
+ async get(idOrName) {
27
+ const endpoint = typeof idOrName === 'number'
28
+ ? `/ssh_keys/${idOrName}`
29
+ : `/ssh_keys?name=${encodeURIComponent(idOrName)}`;
30
+ const response = await this.client.request(endpoint);
31
+ // Validate response with Zod
32
+ const validated = HetznerGetSSHKeyResponseSchema.safeParse(response);
33
+ if (!validated.success) {
34
+ console.warn('Hetzner get SSH key validation warning:', validated.error.issues);
35
+ return response.ssh_key; // Return unvalidated data for backward compatibility
36
+ }
37
+ return validated.data.ssh_key;
38
+ }
39
+ /**
40
+ * Create a new SSH key
41
+ *
42
+ * @param options - SSH key creation options
43
+ * @returns Created SSH key
44
+ */
45
+ async create(options) {
46
+ // Validate input with Zod
47
+ const validatedOptions = HetznerCreateSSHKeyRequestSchema.safeParse(options);
48
+ if (!validatedOptions.success) {
49
+ throw new Error(`Invalid SSH key options: ${validatedOptions.error.issues.map(i => i.message).join(', ')}`);
50
+ }
51
+ const body = {
52
+ name: validatedOptions.data.name,
53
+ public_key: validatedOptions.data.public_key,
54
+ ...(validatedOptions.data.labels && { labels: validatedOptions.data.labels }),
55
+ };
56
+ const response = await this.client.request("/ssh_keys", {
57
+ method: "POST",
58
+ body: JSON.stringify(body),
59
+ });
60
+ // Validate response with Zod
61
+ const validated = HetznerCreateSSHKeyResponseSchema.safeParse(response);
62
+ if (!validated.success) {
63
+ console.warn('Hetzner create SSH key validation warning:', validated.error.issues);
64
+ return response.ssh_key; // Return unvalidated data for backward compatibility
65
+ }
66
+ return validated.data.ssh_key;
67
+ }
68
+ /**
69
+ * Delete an SSH key
70
+ *
71
+ * @param id - SSH key ID
72
+ */
73
+ async delete(id) {
74
+ await this.client.request(`/ssh_keys/${id}`, { method: "DELETE" });
75
+ }
76
+ /**
77
+ * Find an SSH key by name
78
+ * Returns undefined if not found
79
+ */
80
+ async findByName(name) {
81
+ try {
82
+ const keys = await this.list();
83
+ return keys.find(key => key.name === name);
84
+ }
85
+ catch {
86
+ return undefined;
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=ssh-keys.js.map