@ebowwa/hetzner 0.2.2 → 0.3.1

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 (61) hide show
  1. package/dist/bootstrap/index.js +1126 -0
  2. package/dist/bootstrap/index.js.map +15 -0
  3. package/dist/index.js +3540 -0
  4. package/dist/index.js.map +31 -0
  5. package/dist/onboarding/index.js +460 -0
  6. package/dist/onboarding/index.js.map +14 -0
  7. package/package.json +53 -16
  8. package/actions.js +0 -1084
  9. package/actions.ts +0 -1053
  10. package/auth.js +0 -39
  11. package/auth.ts +0 -37
  12. package/bootstrap/FIREWALL.md +0 -326
  13. package/bootstrap/KERNEL-HARDENING.md +0 -258
  14. package/bootstrap/SECURITY-INTEGRATION.md +0 -281
  15. package/bootstrap/TESTING.md +0 -301
  16. package/bootstrap/cloud-init.js +0 -323
  17. package/bootstrap/cloud-init.ts +0 -394
  18. package/bootstrap/firewall.js +0 -292
  19. package/bootstrap/firewall.ts +0 -342
  20. package/bootstrap/genesis.js +0 -424
  21. package/bootstrap/genesis.ts +0 -518
  22. package/bootstrap/index.js +0 -59
  23. package/bootstrap/index.ts +0 -71
  24. package/bootstrap/kernel-hardening.js +0 -270
  25. package/bootstrap/kernel-hardening.test.js +0 -182
  26. package/bootstrap/kernel-hardening.test.ts +0 -230
  27. package/bootstrap/kernel-hardening.ts +0 -272
  28. package/bootstrap/security-audit.js +0 -122
  29. package/bootstrap/security-audit.ts +0 -124
  30. package/bootstrap/ssh-hardening.js +0 -186
  31. package/bootstrap/ssh-hardening.ts +0 -192
  32. package/client.js +0 -234
  33. package/client.ts +0 -177
  34. package/config.js +0 -7
  35. package/config.ts +0 -5
  36. package/errors.js +0 -345
  37. package/errors.ts +0 -371
  38. package/index.js +0 -73
  39. package/index.ts +0 -59
  40. package/onboarding/doppler.ts +0 -116
  41. package/onboarding/git.ts +0 -133
  42. package/onboarding/index.ts +0 -18
  43. package/onboarding/onboarding.ts +0 -193
  44. package/onboarding/tailscale.ts +0 -159
  45. package/onboarding/types.ts +0 -115
  46. package/pricing.js +0 -387
  47. package/pricing.ts +0 -422
  48. package/schemas.js +0 -667
  49. package/schemas.ts +0 -765
  50. package/server-status.js +0 -122
  51. package/server-status.ts +0 -81
  52. package/servers.js +0 -667
  53. package/servers.ts +0 -568
  54. package/ssh-keys.js +0 -180
  55. package/ssh-keys.ts +0 -122
  56. package/ssh-setup.js +0 -253
  57. package/ssh-setup.ts +0 -218
  58. package/types.js +0 -99
  59. package/types.ts +0 -389
  60. package/volumes.js +0 -295
  61. package/volumes.ts +0 -229
package/servers.ts DELETED
@@ -1,568 +0,0 @@
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
- }