@ebowwa/hetzner-mcp 1.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 (227) hide show
  1. package/bun.lock +250 -0
  2. package/dist/index.d.ts +16 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +1928 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/hetzner/actions.d.ts +356 -0
  7. package/dist/lib/hetzner/actions.d.ts.map +1 -0
  8. package/dist/lib/hetzner/actions.js +804 -0
  9. package/dist/lib/hetzner/actions.js.map +1 -0
  10. package/dist/lib/hetzner/auth.d.ts +7 -0
  11. package/dist/lib/hetzner/auth.d.ts.map +1 -0
  12. package/dist/lib/hetzner/auth.js +35 -0
  13. package/dist/lib/hetzner/auth.js.map +1 -0
  14. package/dist/lib/hetzner/bootstrap/cloud-init.d.ts +79 -0
  15. package/dist/lib/hetzner/bootstrap/cloud-init.d.ts.map +1 -0
  16. package/dist/lib/hetzner/bootstrap/cloud-init.js +279 -0
  17. package/dist/lib/hetzner/bootstrap/cloud-init.js.map +1 -0
  18. package/dist/lib/hetzner/bootstrap/firewall.d.ts +119 -0
  19. package/dist/lib/hetzner/bootstrap/firewall.d.ts.map +1 -0
  20. package/dist/lib/hetzner/bootstrap/firewall.js +279 -0
  21. package/dist/lib/hetzner/bootstrap/firewall.js.map +1 -0
  22. package/dist/lib/hetzner/bootstrap/genesis.d.ts +83 -0
  23. package/dist/lib/hetzner/bootstrap/genesis.d.ts.map +1 -0
  24. package/dist/lib/hetzner/bootstrap/genesis.js +406 -0
  25. package/dist/lib/hetzner/bootstrap/genesis.js.map +1 -0
  26. package/dist/lib/hetzner/bootstrap/index.d.ts +30 -0
  27. package/dist/lib/hetzner/bootstrap/index.d.ts.map +1 -0
  28. package/dist/lib/hetzner/bootstrap/index.js +35 -0
  29. package/dist/lib/hetzner/bootstrap/index.js.map +1 -0
  30. package/dist/lib/hetzner/bootstrap/kernel-hardening.d.ts +70 -0
  31. package/dist/lib/hetzner/bootstrap/kernel-hardening.d.ts.map +1 -0
  32. package/dist/lib/hetzner/bootstrap/kernel-hardening.js +266 -0
  33. package/dist/lib/hetzner/bootstrap/kernel-hardening.js.map +1 -0
  34. package/dist/lib/hetzner/bootstrap/kernel-hardening.test.d.ts +7 -0
  35. package/dist/lib/hetzner/bootstrap/kernel-hardening.test.d.ts.map +1 -0
  36. package/dist/lib/hetzner/bootstrap/kernel-hardening.test.js +181 -0
  37. package/dist/lib/hetzner/bootstrap/kernel-hardening.test.js.map +1 -0
  38. package/dist/lib/hetzner/bootstrap/security-audit.d.ts +46 -0
  39. package/dist/lib/hetzner/bootstrap/security-audit.d.ts.map +1 -0
  40. package/dist/lib/hetzner/bootstrap/security-audit.js +118 -0
  41. package/dist/lib/hetzner/bootstrap/security-audit.js.map +1 -0
  42. package/dist/lib/hetzner/bootstrap/ssh-hardening.d.ts +68 -0
  43. package/dist/lib/hetzner/bootstrap/ssh-hardening.d.ts.map +1 -0
  44. package/dist/lib/hetzner/bootstrap/ssh-hardening.js +182 -0
  45. package/dist/lib/hetzner/bootstrap/ssh-hardening.js.map +1 -0
  46. package/dist/lib/hetzner/client.d.ts +63 -0
  47. package/dist/lib/hetzner/client.d.ts.map +1 -0
  48. package/dist/lib/hetzner/client.js +137 -0
  49. package/dist/lib/hetzner/client.js.map +1 -0
  50. package/dist/lib/hetzner/config.d.ts +5 -0
  51. package/dist/lib/hetzner/config.d.ts.map +1 -0
  52. package/dist/lib/hetzner/config.js +5 -0
  53. package/dist/lib/hetzner/config.js.map +1 -0
  54. package/dist/lib/hetzner/errors.d.ts +171 -0
  55. package/dist/lib/hetzner/errors.d.ts.map +1 -0
  56. package/dist/lib/hetzner/errors.js +270 -0
  57. package/dist/lib/hetzner/errors.js.map +1 -0
  58. package/dist/lib/hetzner/index.d.ts +21 -0
  59. package/dist/lib/hetzner/index.d.ts.map +1 -0
  60. package/dist/lib/hetzner/index.js +29 -0
  61. package/dist/lib/hetzner/index.js.map +1 -0
  62. package/dist/lib/hetzner/pricing.d.ts +207 -0
  63. package/dist/lib/hetzner/pricing.d.ts.map +1 -0
  64. package/dist/lib/hetzner/pricing.js +284 -0
  65. package/dist/lib/hetzner/pricing.js.map +1 -0
  66. package/dist/lib/hetzner/schemas.d.ts +1644 -0
  67. package/dist/lib/hetzner/schemas.d.ts.map +1 -0
  68. package/dist/lib/hetzner/schemas.js +660 -0
  69. package/dist/lib/hetzner/schemas.js.map +1 -0
  70. package/dist/lib/hetzner/server-status.d.ts +26 -0
  71. package/dist/lib/hetzner/server-status.d.ts.map +1 -0
  72. package/dist/lib/hetzner/server-status.js +64 -0
  73. package/dist/lib/hetzner/server-status.js.map +1 -0
  74. package/dist/lib/hetzner/servers.d.ts +165 -0
  75. package/dist/lib/hetzner/servers.d.ts.map +1 -0
  76. package/dist/lib/hetzner/servers.js +424 -0
  77. package/dist/lib/hetzner/servers.js.map +1 -0
  78. package/dist/lib/hetzner/ssh-keys.d.ts +36 -0
  79. package/dist/lib/hetzner/ssh-keys.d.ts.map +1 -0
  80. package/dist/lib/hetzner/ssh-keys.js +90 -0
  81. package/dist/lib/hetzner/ssh-keys.js.map +1 -0
  82. package/dist/lib/hetzner/ssh-setup.d.ts +48 -0
  83. package/dist/lib/hetzner/ssh-setup.d.ts.map +1 -0
  84. package/dist/lib/hetzner/ssh-setup.js +167 -0
  85. package/dist/lib/hetzner/ssh-setup.js.map +1 -0
  86. package/dist/lib/hetzner/types.d.ts +330 -0
  87. package/dist/lib/hetzner/types.d.ts.map +1 -0
  88. package/dist/lib/hetzner/types.js +96 -0
  89. package/dist/lib/hetzner/types.js.map +1 -0
  90. package/dist/lib/hetzner/volumes.d.ts +106 -0
  91. package/dist/lib/hetzner/volumes.d.ts.map +1 -0
  92. package/dist/lib/hetzner/volumes.js +172 -0
  93. package/dist/lib/hetzner/volumes.js.map +1 -0
  94. package/dist/lib/resources.d.ts +70 -0
  95. package/dist/lib/resources.d.ts.map +1 -0
  96. package/dist/lib/resources.js +115 -0
  97. package/dist/lib/resources.js.map +1 -0
  98. package/dist/lib/ssh/flags.d.ts +349 -0
  99. package/dist/lib/ssh/flags.d.ts.map +1 -0
  100. package/dist/lib/ssh/flags.js +322 -0
  101. package/dist/lib/ssh/flags.js.map +1 -0
  102. package/dist/lib/ssh/index.d.ts +5 -0
  103. package/dist/lib/ssh/index.d.ts.map +1 -0
  104. package/dist/lib/ssh/index.js +5 -0
  105. package/dist/lib/ssh/index.js.map +1 -0
  106. package/dist/lib/terminal/api.d.ts +8 -0
  107. package/dist/lib/terminal/api.d.ts.map +1 -0
  108. package/dist/lib/terminal/api.js +594 -0
  109. package/dist/lib/terminal/api.js.map +1 -0
  110. package/dist/lib/terminal/client.d.ts +15 -0
  111. package/dist/lib/terminal/client.d.ts.map +1 -0
  112. package/dist/lib/terminal/client.js +45 -0
  113. package/dist/lib/terminal/client.js.map +1 -0
  114. package/dist/lib/terminal/config.d.ts +86 -0
  115. package/dist/lib/terminal/config.d.ts.map +1 -0
  116. package/dist/lib/terminal/config.js +375 -0
  117. package/dist/lib/terminal/config.js.map +1 -0
  118. package/dist/lib/terminal/error.d.ts +8 -0
  119. package/dist/lib/terminal/error.d.ts.map +1 -0
  120. package/dist/lib/terminal/error.js +12 -0
  121. package/dist/lib/terminal/error.js.map +1 -0
  122. package/dist/lib/terminal/exec.d.ts +47 -0
  123. package/dist/lib/terminal/exec.d.ts.map +1 -0
  124. package/dist/lib/terminal/exec.js +107 -0
  125. package/dist/lib/terminal/exec.js.map +1 -0
  126. package/dist/lib/terminal/files.d.ts +124 -0
  127. package/dist/lib/terminal/files.d.ts.map +1 -0
  128. package/dist/lib/terminal/files.js +436 -0
  129. package/dist/lib/terminal/files.js.map +1 -0
  130. package/dist/lib/terminal/fingerprint.d.ts +67 -0
  131. package/dist/lib/terminal/fingerprint.d.ts.map +1 -0
  132. package/dist/lib/terminal/fingerprint.js +214 -0
  133. package/dist/lib/terminal/fingerprint.js.map +1 -0
  134. package/dist/lib/terminal/index.d.ts +14 -0
  135. package/dist/lib/terminal/index.d.ts.map +1 -0
  136. package/dist/lib/terminal/index.js +21 -0
  137. package/dist/lib/terminal/index.js.map +1 -0
  138. package/dist/lib/terminal/manager.d.ts +103 -0
  139. package/dist/lib/terminal/manager.d.ts.map +1 -0
  140. package/dist/lib/terminal/manager.js +237 -0
  141. package/dist/lib/terminal/manager.js.map +1 -0
  142. package/dist/lib/terminal/network-error-detector.d.ts +19 -0
  143. package/dist/lib/terminal/network-error-detector.d.ts.map +1 -0
  144. package/dist/lib/terminal/network-error-detector.js +98 -0
  145. package/dist/lib/terminal/network-error-detector.js.map +1 -0
  146. package/dist/lib/terminal/pool.d.ts +143 -0
  147. package/dist/lib/terminal/pool.d.ts.map +1 -0
  148. package/dist/lib/terminal/pool.js +554 -0
  149. package/dist/lib/terminal/pool.js.map +1 -0
  150. package/dist/lib/terminal/pty.d.ts +59 -0
  151. package/dist/lib/terminal/pty.d.ts.map +1 -0
  152. package/dist/lib/terminal/pty.js +224 -0
  153. package/dist/lib/terminal/pty.js.map +1 -0
  154. package/dist/lib/terminal/resources.d.ts +63 -0
  155. package/dist/lib/terminal/resources.d.ts.map +1 -0
  156. package/dist/lib/terminal/resources.js +62 -0
  157. package/dist/lib/terminal/resources.js.map +1 -0
  158. package/dist/lib/terminal/scp.d.ts +30 -0
  159. package/dist/lib/terminal/scp.d.ts.map +1 -0
  160. package/dist/lib/terminal/scp.js +74 -0
  161. package/dist/lib/terminal/scp.js.map +1 -0
  162. package/dist/lib/terminal/sessions.d.ts +101 -0
  163. package/dist/lib/terminal/sessions.d.ts.map +1 -0
  164. package/dist/lib/terminal/sessions.js +718 -0
  165. package/dist/lib/terminal/sessions.js.map +1 -0
  166. package/dist/lib/terminal/tmux-exec.d.ts +50 -0
  167. package/dist/lib/terminal/tmux-exec.d.ts.map +1 -0
  168. package/dist/lib/terminal/tmux-exec.js +79 -0
  169. package/dist/lib/terminal/tmux-exec.js.map +1 -0
  170. package/dist/lib/terminal/tmux-local.d.ts +273 -0
  171. package/dist/lib/terminal/tmux-local.d.ts.map +1 -0
  172. package/dist/lib/terminal/tmux-local.js +629 -0
  173. package/dist/lib/terminal/tmux-local.js.map +1 -0
  174. package/dist/lib/terminal/tmux-manager.d.ts +328 -0
  175. package/dist/lib/terminal/tmux-manager.d.ts.map +1 -0
  176. package/dist/lib/terminal/tmux-manager.js +667 -0
  177. package/dist/lib/terminal/tmux-manager.js.map +1 -0
  178. package/dist/lib/terminal/tmux.d.ts +213 -0
  179. package/dist/lib/terminal/tmux.d.ts.map +1 -0
  180. package/dist/lib/terminal/tmux.js +528 -0
  181. package/dist/lib/terminal/tmux.js.map +1 -0
  182. package/dist/lib/terminal/types.d.ts +18 -0
  183. package/dist/lib/terminal/types.d.ts.map +1 -0
  184. package/dist/lib/terminal/types.js +5 -0
  185. package/dist/lib/terminal/types.js.map +1 -0
  186. package/lmdb.db +0 -0
  187. package/lmdb.db-lock +0 -0
  188. package/package.json +48 -0
  189. package/src/index.js +2034 -0
  190. package/src/index.ts +2295 -0
  191. package/src/lib/hetzner/actions.ts +1056 -0
  192. package/src/lib/hetzner/auth.ts +37 -0
  193. package/src/lib/hetzner/bootstrap/cloud-init.ts +394 -0
  194. package/src/lib/hetzner/bootstrap/firewall.ts +342 -0
  195. package/src/lib/hetzner/bootstrap/genesis.ts +518 -0
  196. package/src/lib/hetzner/bootstrap/index.ts +71 -0
  197. package/src/lib/hetzner/bootstrap/kernel-hardening.test.ts +230 -0
  198. package/src/lib/hetzner/bootstrap/kernel-hardening.ts +272 -0
  199. package/src/lib/hetzner/bootstrap/security-audit.ts +124 -0
  200. package/src/lib/hetzner/bootstrap/ssh-hardening.ts +192 -0
  201. package/src/lib/hetzner/client.ts +177 -0
  202. package/src/lib/hetzner/config.ts +5 -0
  203. package/src/lib/hetzner/errors.ts +371 -0
  204. package/src/lib/hetzner/index.ts +56 -0
  205. package/src/lib/hetzner/pricing.ts +422 -0
  206. package/src/lib/hetzner/schemas.ts +765 -0
  207. package/src/lib/hetzner/server-status.ts +81 -0
  208. package/src/lib/hetzner/servers.ts +568 -0
  209. package/src/lib/hetzner/ssh-keys.ts +122 -0
  210. package/src/lib/hetzner/ssh-setup.ts +218 -0
  211. package/src/lib/hetzner/types.ts +419 -0
  212. package/src/lib/hetzner/volumes.ts +229 -0
  213. package/src/lib/resources.ts +156 -0
  214. package/src/lib/ssh/flags.ts +578 -0
  215. package/src/lib/ssh/index.ts +5 -0
  216. package/src/lib/terminal/client.ts +55 -0
  217. package/src/lib/terminal/config.ts +489 -0
  218. package/src/lib/terminal/error.ts +13 -0
  219. package/src/lib/terminal/exec.ts +128 -0
  220. package/src/lib/terminal/files.ts +636 -0
  221. package/src/lib/terminal/index.ts +71 -0
  222. package/src/lib/terminal/pool.ts +662 -0
  223. package/src/lib/terminal/scp.ts +109 -0
  224. package/src/lib/terminal/tmux-exec.ts +96 -0
  225. package/src/lib/terminal/tmux.ts +711 -0
  226. package/src/lib/terminal/types.ts +19 -0
  227. package/tsconfig.json +20 -0
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Hetzner Cloud API error types and utilities
3
+ */
4
+
5
+ import type { RateLimitInfo, ActionError } from "./types.js";
6
+
7
+ // Re-export ActionError for convenience
8
+ export type { ActionError };
9
+
10
+ // ============================================================================
11
+ // Error Codes
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Hetzner API error codes
16
+ * @see https://docs.hetzner.cloud/#errors
17
+ */
18
+ export enum HetznerErrorCode {
19
+ // Authentication errors
20
+ Unauthorized = "unauthorized",
21
+ InvalidInput = "invalid_input",
22
+ JSONError = "json_error",
23
+ Forbidden = "forbidden",
24
+
25
+ // Resource errors
26
+ NotFound = "not_found",
27
+ ResourceLocked = "locked",
28
+ ResourceLimitExceeded = "resource_limit_exceeded",
29
+ UniquenessError = "uniqueness_error",
30
+
31
+ // Rate limiting
32
+ RateLimitExceeded = "rate_limit_exceeded",
33
+
34
+ // Conflict errors
35
+ Conflict = "conflict",
36
+ ServiceError = "service_error",
37
+
38
+ // Server-specific errors
39
+ ServerNotStopped = "server_not_stopped",
40
+ ServerAlreadyStopped = "server_already_stopped",
41
+ InvalidServerType = "invalid_server_type",
42
+
43
+ // IP/network errors
44
+ IpNotOwned = "ip_not_owned",
45
+ IpAlreadyAssigned = "ip_already_assigned",
46
+
47
+ // Volume errors
48
+ VolumeAlreadyAttached = "volume_already_attached",
49
+ VolumeSizeNotMultiple = "volume_size_not_multiple",
50
+
51
+ // Firewall errors
52
+ FirewallInUse = "firewall_in_use",
53
+
54
+ // Certificate errors
55
+ CertificateValidationFailed = "certificate_validation_failed",
56
+ CertificatePending = "certificate_pending",
57
+ }
58
+
59
+ // ============================================================================
60
+ // Error Classes
61
+ // ============================================================================
62
+
63
+ /**
64
+ * Base Hetzner API error
65
+ */
66
+ export class HetznerAPIError extends Error {
67
+ constructor(
68
+ message: string,
69
+ public code?: string,
70
+ public details?: unknown
71
+ ) {
72
+ super(message);
73
+ this.name = "HetznerAPIError";
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Authentication error (401)
79
+ */
80
+ export class HetznerUnauthorizedError extends HetznerAPIError {
81
+ constructor(message: string = "Unauthorized: Invalid API token") {
82
+ super(message, HetznerErrorCode.Unauthorized);
83
+ this.name = "HetznerUnauthorizedError";
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Forbidden error (403)
89
+ */
90
+ export class HetznerForbiddenError extends HetznerAPIError {
91
+ constructor(message: string = "Forbidden: Insufficient permissions") {
92
+ super(message, HetznerErrorCode.Forbidden);
93
+ this.name = "HetznerForbiddenError";
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Resource not found error (404)
99
+ */
100
+ export class HetznerNotFoundError extends HetznerAPIError {
101
+ constructor(resource: string, id: number | string) {
102
+ super(
103
+ `${resource} with ID ${id} not found`,
104
+ HetznerErrorCode.NotFound,
105
+ { resource, id }
106
+ );
107
+ this.name = "HetznerNotFoundError";
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Rate limit exceeded error (429)
113
+ */
114
+ export class HetznerRateLimitError extends HetznerAPIError {
115
+ constructor(
116
+ message: string = "Rate limit exceeded",
117
+ public rateLimitInfo?: RateLimitInfo
118
+ ) {
119
+ super(message, HetznerErrorCode.RateLimitExceeded, rateLimitInfo);
120
+ this.name = "HetznerRateLimitError";
121
+ }
122
+
123
+ /**
124
+ * Get the number of milliseconds until the rate limit resets
125
+ */
126
+ get resetInMs(): number {
127
+ if (!this.rateLimitInfo) return 60000; // Default to 1 minute
128
+ return Math.max(0, this.rateLimitInfo.reset * 1000 - Date.now());
129
+ }
130
+
131
+ /**
132
+ * Get a human-readable reset time
133
+ */
134
+ get resetTime(): string {
135
+ if (!this.rateLimitInfo) return "unknown";
136
+ return new Date(this.rateLimitInfo.reset * 1000).toISOString();
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Resource locked error
142
+ */
143
+ export class HetznerResourceLockedError extends HetznerAPIError {
144
+ constructor(
145
+ resource: string,
146
+ id: number | string,
147
+ public actionInProgress?: string
148
+ ) {
149
+ super(
150
+ `${resource} ${id} is locked${actionInProgress ? ` by ${actionInProgress}` : ""}`,
151
+ HetznerErrorCode.ResourceLocked,
152
+ { resource, id, actionInProgress }
153
+ );
154
+ this.name = "HetznerResourceLockedError";
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Resource limit exceeded error
160
+ */
161
+ export class HetznerResourceLimitError extends HetznerAPIError {
162
+ constructor(resource: string, limit: number) {
163
+ super(
164
+ `Resource limit exceeded: ${resource} (limit: ${limit})`,
165
+ HetznerErrorCode.ResourceLimitExceeded,
166
+ { resource, limit }
167
+ );
168
+ this.name = "HetznerResourceLimitError";
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Invalid input error
174
+ */
175
+ export class HetznerInvalidInputError extends HetznerAPIError {
176
+ constructor(
177
+ message: string,
178
+ public fields?: Record<string, string>
179
+ ) {
180
+ super(message, HetznerErrorCode.InvalidInput, { fields });
181
+ this.name = "HetznerInvalidInputError";
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Conflict error
187
+ */
188
+ export class HetznerConflictError extends HetznerAPIError {
189
+ constructor(message: string, details?: unknown) {
190
+ super(message, HetznerErrorCode.Conflict, details);
191
+ this.name = "HetznerConflictError";
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Service error (5xx)
197
+ */
198
+ export class HetznerServiceError extends HetznerAPIError {
199
+ constructor(message: string, public statusCode?: number) {
200
+ super(message, HetznerErrorCode.ServiceError, { statusCode });
201
+ this.name = "HetznerServiceError";
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Action failed error
207
+ */
208
+ export class HetznerActionError extends HetznerAPIError {
209
+ constructor(
210
+ public actionError: ActionError,
211
+ public actionId: number
212
+ ) {
213
+ super(
214
+ `Action ${actionId} failed: ${actionError.code} - ${actionError.message}`,
215
+ actionError.code,
216
+ { actionError, actionId }
217
+ );
218
+ this.name = "HetznerActionError";
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Timeout error for action polling
224
+ */
225
+ export class HetznerTimeoutError extends HetznerAPIError {
226
+ constructor(
227
+ actionId: number,
228
+ timeout: number,
229
+ public lastProgress: number
230
+ ) {
231
+ super(
232
+ `Action ${actionId} timed out after ${timeout}ms (last progress: ${lastProgress}%)`,
233
+ "timeout",
234
+ { actionId, timeout, lastProgress }
235
+ );
236
+ this.name = "HetznerTimeoutError";
237
+ }
238
+ }
239
+
240
+ // ============================================================================
241
+ // Error Factory
242
+ // ============================================================================
243
+
244
+ /**
245
+ * Parse Hetzner API error response and create appropriate error
246
+ */
247
+ export function createHetznerError(
248
+ statusCode: number,
249
+ body: {
250
+ error?: {
251
+ code: string;
252
+ message: string;
253
+ details?: unknown;
254
+ };
255
+ }
256
+ ): HetznerAPIError {
257
+ const error = body.error;
258
+
259
+ if (!error) {
260
+ return new HetznerServiceError(
261
+ `HTTP ${statusCode}: ${JSON.stringify(body)}`,
262
+ statusCode
263
+ );
264
+ }
265
+
266
+ switch (statusCode) {
267
+ case 401:
268
+ return new HetznerUnauthorizedError(error.message);
269
+ case 403:
270
+ return new HetznerForbiddenError(error.message);
271
+ case 404:
272
+ return new HetznerNotFoundError("resource", "unknown");
273
+ case 429:
274
+ return new HetznerRateLimitError(error.message);
275
+ case 400:
276
+ if (error.code === HetznerErrorCode.ResourceLocked) {
277
+ return new HetznerResourceLockedError(
278
+ "resource",
279
+ "unknown",
280
+ error.message
281
+ );
282
+ }
283
+ if (error.code === HetznerErrorCode.InvalidInput) {
284
+ return new HetznerInvalidInputError(error.message);
285
+ }
286
+ return new HetznerInvalidInputError(error.message);
287
+ case 409:
288
+ return new HetznerConflictError(error.message, error.details);
289
+ default:
290
+ if (statusCode >= 500) {
291
+ return new HetznerServiceError(error.message, statusCode);
292
+ }
293
+ return new HetznerAPIError(error.message, error.code, error.details);
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Check if an error is retryable
299
+ */
300
+ export function isRetryableError(error: unknown): boolean {
301
+ if (error instanceof HetznerRateLimitError) return true;
302
+ if (error instanceof HetznerResourceLockedError) return true;
303
+ if (error instanceof HetznerServiceError) return true;
304
+ if (error instanceof HetznerConflictError) return true;
305
+ return false;
306
+ }
307
+
308
+ /**
309
+ * Check if an error is a rate limit error
310
+ */
311
+ export function isRateLimitError(error: unknown): error is HetznerRateLimitError {
312
+ return error instanceof HetznerRateLimitError;
313
+ }
314
+
315
+ /**
316
+ * Check if an error is a resource locked error
317
+ */
318
+ export function isResourceLockedError(
319
+ error: unknown
320
+ ): error is HetznerResourceLockedError {
321
+ return error instanceof HetznerResourceLockedError;
322
+ }
323
+
324
+ /**
325
+ * Calculate retry delay with exponential backoff
326
+ */
327
+ export function calculateRetryDelay(
328
+ attempt: number,
329
+ baseDelay: number = 1000,
330
+ maxDelay: number = 60000
331
+ ): number {
332
+ const delay = baseDelay * Math.pow(2, attempt);
333
+ // Add jitter (±25%)
334
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
335
+ return Math.min(maxDelay, delay + jitter);
336
+ }
337
+
338
+ // ============================================================================
339
+ // Error Handler Types
340
+ // ============================================================================
341
+
342
+ /**
343
+ * Error handler function type
344
+ */
345
+ export type ErrorHandler = (error: HetznerAPIError) => void | Promise<void>;
346
+
347
+ /**
348
+ * Error handler options
349
+ */
350
+ export interface ErrorHandlerContext {
351
+ /** Maximum number of retry attempts */
352
+ maxRetries?: number;
353
+ /** Base delay for exponential backoff in milliseconds */
354
+ baseDelay?: number;
355
+ /** Maximum delay between retries in milliseconds */
356
+ maxDelay?: number;
357
+ /** Optional error handler callback */
358
+ onError?: ErrorHandler;
359
+ /** Whether to log errors */
360
+ logErrors?: boolean;
361
+ }
362
+
363
+ /**
364
+ * Default error handler that logs to console
365
+ */
366
+ export function defaultErrorHandler(error: HetznerAPIError): void {
367
+ console.error(`[Hetzner API Error] ${error.name}: ${error.message}`);
368
+ if (error.details) {
369
+ console.error("Details:", error.details);
370
+ }
371
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Hetzner Cloud API client
3
+ * For server-side use only (requires API token)
4
+ *
5
+ * TODO:
6
+ * - Certificate actions: https://docs.hetzner.cloud/reference/cloud#certificate-actions
7
+ * - DNS operations
8
+ */
9
+
10
+ // Core exports
11
+ export { HetznerClient } from "./client.js";
12
+ export { ServerOperations } from "./servers.js";
13
+ export { VolumeOperations } from "./volumes.js";
14
+ export { ActionOperations } from "./actions.js";
15
+ export { SSHKeyOperations } from "./ssh-keys.js";
16
+
17
+ // Auth
18
+ export { getTokenFromCLI, isAuthenticated, resolveApiToken } from "./auth.js";
19
+
20
+ // Config
21
+ export { HETZNER_API_BASE } from "./config.js";
22
+
23
+ // Types
24
+ export * from "./types.js";
25
+
26
+ // Schemas
27
+ export * from "./schemas.js";
28
+
29
+ // Errors
30
+ export * from "./errors.js";
31
+
32
+ // Action utilities
33
+ export {
34
+ waitForAction,
35
+ waitForMultipleActions,
36
+ waitForMultipleActionsWithLimit,
37
+ batchCheckActions,
38
+ getActionTimeout,
39
+ isActionRunning,
40
+ isActionSuccess,
41
+ isActionError,
42
+ formatActionProgress,
43
+ getActionDescription,
44
+ getPollInterval,
45
+ getAdaptivePollInterval,
46
+ waitForActionAdaptive,
47
+ parseRateLimitHeaders,
48
+ isRateLimitLow,
49
+ formatRateLimitStatus,
50
+ waitForRateLimitReset,
51
+ createProgressLogger,
52
+ ACTION_TIMEOUTS,
53
+ } from "./actions.js";
54
+
55
+ // Bootstrap security modules
56
+ export * from "./bootstrap/index.js";