@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/client.js DELETED
@@ -1,234 +0,0 @@
1
- "use strict";
2
- /**
3
- * Hetzner Cloud API client
4
- * For server-side use only (requires API token)
5
- *
6
- * TODO: RE-REVIEW https://docs.hetzner.cloud/reference/cloud#authentication
7
- * - https://tailscale.com/kb/1150/cloud-hetzner
8
- */
9
- var __assign = (this && this.__assign) || function () {
10
- __assign = Object.assign || function(t) {
11
- for (var s, i = 1, n = arguments.length; i < n; i++) {
12
- s = arguments[i];
13
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
14
- t[p] = s[p];
15
- }
16
- return t;
17
- };
18
- return __assign.apply(this, arguments);
19
- };
20
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
21
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
- return new (P || (P = Promise))(function (resolve, reject) {
23
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
- step((generator = generator.apply(thisArg, _arguments || [])).next());
27
- });
28
- };
29
- var __generator = (this && this.__generator) || function (thisArg, body) {
30
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
31
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
32
- function verb(n) { return function (v) { return step([n, v]); }; }
33
- function step(op) {
34
- if (f) throw new TypeError("Generator is already executing.");
35
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
36
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
37
- if (y = 0, t) op = [op[0] & 2, t.value];
38
- switch (op[0]) {
39
- case 0: case 1: t = op; break;
40
- case 4: _.label++; return { value: op[1], done: false };
41
- case 5: _.label++; y = op[1]; op = [0]; continue;
42
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
43
- default:
44
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
45
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
46
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
47
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
48
- if (t[2]) _.ops.pop();
49
- _.trys.pop(); continue;
50
- }
51
- op = body.call(thisArg, _);
52
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
53
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
54
- }
55
- };
56
- Object.defineProperty(exports, "__esModule", { value: true });
57
- exports.HetznerClient = void 0;
58
- // Explicitly import fetch for Bun compatibility
59
- var bun_1 = require("bun");
60
- var config_js_1 = require("./config.js");
61
- // Use Bun's fetch if available, otherwise try global fetch
62
- var fetch = bun_1.fetch || globalThis.fetch;
63
- var auth_js_1 = require("./auth.js");
64
- var servers_js_1 = require("./servers.js");
65
- var actions_js_1 = require("./actions.js");
66
- var pricing_js_1 = require("./pricing.js");
67
- var ssh_keys_js_1 = require("./ssh-keys.js");
68
- var volumes_js_1 = require("./volumes.js");
69
- var errors_js_1 = require("./errors.js");
70
- var actions_js_2 = require("./actions.js");
71
- var HetznerClient = /** @class */ (function () {
72
- function HetznerClient(apiToken) {
73
- this.servers = new servers_js_1.ServerOperations(this);
74
- this.actions = new actions_js_1.ActionOperations(this);
75
- this.pricing = new pricing_js_1.PricingOperations(this);
76
- this.ssh_keys = new ssh_keys_js_1.SSHKeyOperations(this);
77
- this.volumes = new volumes_js_1.VolumeOperations(this);
78
- this.apiToken = (0, auth_js_1.resolveApiToken)(apiToken);
79
- // If no token from env or explicit, try CLI config
80
- if (!this.apiToken) {
81
- this.apiToken = (0, auth_js_1.getTokenFromCLI)();
82
- }
83
- }
84
- Object.defineProperty(HetznerClient.prototype, "isAuthenticated", {
85
- get: function () {
86
- return (0, auth_js_1.isAuthenticated)(this.apiToken);
87
- },
88
- enumerable: false,
89
- configurable: true
90
- });
91
- /**
92
- * Make a request to the Hetzner Cloud API
93
- *
94
- * @param endpoint - API endpoint (e.g., "/servers")
95
- * @param options - RequestInit options
96
- * @returns Parsed JSON response
97
- * @throws {HetznerAPIError} On API errors
98
- */
99
- HetznerClient.prototype.request = function (endpoint_1) {
100
- return __awaiter(this, arguments, void 0, function (endpoint, options) {
101
- var response, rateLimit, body, data;
102
- if (options === void 0) { options = {}; }
103
- return __generator(this, function (_a) {
104
- switch (_a.label) {
105
- case 0: return [4 /*yield*/, fetch("".concat(config_js_1.HETZNER_API_BASE).concat(endpoint), __assign(__assign({}, options), { headers: __assign({ Authorization: "Bearer ".concat(this.apiToken), "Content-Type": "application/json" }, options.headers) }))];
106
- case 1:
107
- response = _a.sent();
108
- rateLimit = (0, actions_js_2.parseRateLimitHeaders)(response.headers);
109
- this.handleRateLimit(rateLimit);
110
- if (!!response.ok) return [3 /*break*/, 3];
111
- return [4 /*yield*/, response.json().catch(function () { return ({}); })];
112
- case 2:
113
- body = _a.sent();
114
- throw (0, errors_js_1.createHetznerError)(response.status, body);
115
- case 3: return [4 /*yield*/, response.json()];
116
- case 4:
117
- data = _a.sent();
118
- return [2 /*return*/, data];
119
- }
120
- });
121
- });
122
- };
123
- /**
124
- * Validate Hetzner API response with Zod schema
125
- *
126
- * @param schema - Zod schema to validate against
127
- * @param data - Data to validate
128
- * @returns Validated data
129
- */
130
- HetznerClient.prototype.validateResponse = function (schema, data) {
131
- var result = schema.safeParse(data);
132
- if (result.success) {
133
- return result.data;
134
- }
135
- // Log validation errors but don't throw to maintain backward compatibility
136
- console.warn("Hetzner API response validation warning:", result.error.issues);
137
- return data;
138
- };
139
- /**
140
- * Handle rate limit information from response headers
141
- *
142
- * @param rateLimit - Rate limit info from response
143
- */
144
- HetznerClient.prototype.handleRateLimit = function (rateLimit) {
145
- if (!rateLimit)
146
- return;
147
- // Warn if rate limit is low
148
- if (rateLimit.remaining < 100) {
149
- console.warn("[Hetzner API] Rate limit low: ".concat(rateLimit.remaining, "/").concat(rateLimit.limit, " remaining. Resets at ").concat(new Date(rateLimit.reset * 1000).toISOString()));
150
- }
151
- };
152
- /**
153
- * Get current rate limit information
154
- *
155
- * Makes a lightweight request to check rate limit status
156
- *
157
- * @returns Rate limit info or null if not available
158
- */
159
- HetznerClient.prototype.getRateLimit = function () {
160
- return __awaiter(this, void 0, void 0, function () {
161
- var response, _a;
162
- return __generator(this, function (_b) {
163
- switch (_b.label) {
164
- case 0:
165
- _b.trys.push([0, 2, , 3]);
166
- return [4 /*yield*/, fetch("".concat(config_js_1.HETZNER_API_BASE, "/servers"), {
167
- headers: {
168
- Authorization: "Bearer ".concat(this.apiToken),
169
- },
170
- })];
171
- case 1:
172
- response = _b.sent();
173
- return [2 /*return*/, (0, actions_js_2.parseRateLimitHeaders)(response.headers)];
174
- case 2:
175
- _a = _b.sent();
176
- return [2 /*return*/, null];
177
- case 3: return [2 /*return*/];
178
- }
179
- });
180
- });
181
- };
182
- // Backward-compatible convenience methods (delegates to servers operations)
183
- HetznerClient.prototype.listServers = function () {
184
- return __awaiter(this, void 0, void 0, function () {
185
- return __generator(this, function (_a) {
186
- return [2 /*return*/, this.servers.list()];
187
- });
188
- });
189
- };
190
- HetznerClient.prototype.getServer = function (id) {
191
- return __awaiter(this, void 0, void 0, function () {
192
- return __generator(this, function (_a) {
193
- return [2 /*return*/, this.servers.get(id)];
194
- });
195
- });
196
- };
197
- HetznerClient.prototype.createServer = function (options) {
198
- return __awaiter(this, void 0, void 0, function () {
199
- return __generator(this, function (_a) {
200
- return [2 /*return*/, this.servers.create(options)];
201
- });
202
- });
203
- };
204
- HetznerClient.prototype.deleteServer = function (id) {
205
- return __awaiter(this, void 0, void 0, function () {
206
- return __generator(this, function (_a) {
207
- return [2 /*return*/, this.servers.delete(id)];
208
- });
209
- });
210
- };
211
- HetznerClient.prototype.powerOn = function (id) {
212
- return __awaiter(this, void 0, void 0, function () {
213
- return __generator(this, function (_a) {
214
- return [2 /*return*/, this.servers.powerOn(id)];
215
- });
216
- });
217
- };
218
- HetznerClient.prototype.powerOff = function (id) {
219
- return __awaiter(this, void 0, void 0, function () {
220
- return __generator(this, function (_a) {
221
- return [2 /*return*/, this.servers.powerOff(id)];
222
- });
223
- });
224
- };
225
- HetznerClient.prototype.reboot = function (id) {
226
- return __awaiter(this, void 0, void 0, function () {
227
- return __generator(this, function (_a) {
228
- return [2 /*return*/, this.servers.reboot(id)];
229
- });
230
- });
231
- };
232
- return HetznerClient;
233
- }());
234
- exports.HetznerClient = HetznerClient;
package/client.ts DELETED
@@ -1,177 +0,0 @@
1
- /**
2
- * Hetzner Cloud API client
3
- * For server-side use only (requires API token)
4
- *
5
- * TODO: RE-REVIEW https://docs.hetzner.cloud/reference/cloud#authentication
6
- * - https://tailscale.com/kb/1150/cloud-hetzner
7
- */
8
-
9
- // Explicitly import fetch for Bun compatibility
10
- import { fetch as bunFetch } from "bun";
11
- import { z } from "zod";
12
- import { HETZNER_API_BASE } from "./config.js";
13
-
14
- // Use Bun's fetch if available, otherwise try global fetch
15
- const fetch = bunFetch || (globalThis as any).fetch;
16
- import { resolveApiToken, getTokenFromCLI, isAuthenticated } from "./auth.js";
17
- import { ServerOperations } from "./servers.js";
18
- import { ActionOperations } from "./actions.js";
19
- import { PricingOperations } from "./pricing.js";
20
- import { SSHKeyOperations } from "./ssh-keys.js";
21
- import { VolumeOperations } from "./volumes.js";
22
- import {
23
- HetznerListServersResponseSchema,
24
- HetznerGetServerResponseSchema,
25
- HetznerCreateServerResponseSchema,
26
- } from "./schemas.js";
27
- import {
28
- createHetznerError,
29
- isRateLimitError,
30
- } from "./errors.js";
31
- import {
32
- parseRateLimitHeaders,
33
- waitForRateLimitReset,
34
- } from "./actions.js";
35
- import type { RateLimitInfo } from "./types.js";
36
-
37
- export class HetznerClient {
38
- private apiToken: string;
39
-
40
- constructor(apiToken?: string) {
41
- this.apiToken = resolveApiToken(apiToken);
42
-
43
- // If no token from env or explicit, try CLI config
44
- if (!this.apiToken) {
45
- this.apiToken = getTokenFromCLI();
46
- }
47
- }
48
-
49
- get isAuthenticated(): boolean {
50
- return isAuthenticated(this.apiToken);
51
- }
52
-
53
- /**
54
- * Make a request to the Hetzner Cloud API
55
- *
56
- * @param endpoint - API endpoint (e.g., "/servers")
57
- * @param options - RequestInit options
58
- * @returns Parsed JSON response
59
- * @throws {HetznerAPIError} On API errors
60
- */
61
- async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
62
- const response = await fetch(`${HETZNER_API_BASE}${endpoint}`, {
63
- ...options,
64
- headers: {
65
- Authorization: `Bearer ${this.apiToken}`,
66
- "Content-Type": "application/json",
67
- ...options.headers,
68
- },
69
- });
70
-
71
- // Parse rate limit headers
72
- const rateLimit = parseRateLimitHeaders(response.headers);
73
- this.handleRateLimit(rateLimit);
74
-
75
- if (!response.ok) {
76
- const body = await response.json().catch(() => ({}));
77
- throw createHetznerError(response.status, body);
78
- }
79
-
80
- const data = await response.json();
81
- return data as T;
82
- }
83
-
84
- /**
85
- * Validate Hetzner API response with Zod schema
86
- *
87
- * @param schema - Zod schema to validate against
88
- * @param data - Data to validate
89
- * @returns Validated data
90
- */
91
- private validateResponse<T>(schema: z.ZodType<T>, data: unknown): T {
92
- const result = schema.safeParse(data);
93
- if (result.success) {
94
- return result.data;
95
- }
96
- // Log validation errors but don't throw to maintain backward compatibility
97
- console.warn(
98
- "Hetzner API response validation warning:",
99
- result.error.issues
100
- );
101
- return data as T;
102
- }
103
-
104
- /**
105
- * Handle rate limit information from response headers
106
- *
107
- * @param rateLimit - Rate limit info from response
108
- */
109
- private handleRateLimit(rateLimit: RateLimitInfo | null): void {
110
- if (!rateLimit) return;
111
-
112
- // Warn if rate limit is low
113
- if (rateLimit.remaining < 100) {
114
- console.warn(
115
- `[Hetzner API] Rate limit low: ${rateLimit.remaining}/${rateLimit.limit} remaining. Resets at ${new Date(rateLimit.reset * 1000).toISOString()}`
116
- );
117
- }
118
- }
119
-
120
- /**
121
- * Get current rate limit information
122
- *
123
- * Makes a lightweight request to check rate limit status
124
- *
125
- * @returns Rate limit info or null if not available
126
- */
127
- async getRateLimit(): Promise<RateLimitInfo | null> {
128
- try {
129
- const response = await fetch(`${HETZNER_API_BASE}/servers`, {
130
- headers: {
131
- Authorization: `Bearer ${this.apiToken}`,
132
- },
133
- });
134
- return parseRateLimitHeaders(response.headers);
135
- } catch {
136
- return null;
137
- }
138
- }
139
-
140
- readonly servers = new ServerOperations(this);
141
- readonly actions = new ActionOperations(this);
142
- readonly pricing = new PricingOperations(this);
143
- readonly ssh_keys = new SSHKeyOperations(this);
144
- readonly volumes = new VolumeOperations(this);
145
-
146
- // Backward-compatible convenience methods (delegates to servers operations)
147
- async listServers() {
148
- return this.servers.list();
149
- }
150
-
151
- async getServer(id: number) {
152
- return this.servers.get(id);
153
- }
154
-
155
- async createServer(options: import("./types.js").CreateServerOptions) {
156
- return this.servers.create(options);
157
- }
158
-
159
- async deleteServer(id: number) {
160
- return this.servers.delete(id);
161
- }
162
-
163
- async powerOn(id: number) {
164
- return this.servers.powerOn(id);
165
- }
166
-
167
- async powerOff(id: number) {
168
- return this.servers.powerOff(id);
169
- }
170
-
171
- async reboot(id: number) {
172
- return this.servers.reboot(id);
173
- }
174
- }
175
-
176
- // Re-export types for convenience
177
- export type * from "./types.js";
package/config.js DELETED
@@ -1,7 +0,0 @@
1
- "use strict";
2
- /**
3
- * Hetzner Cloud API configuration
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.HETZNER_API_BASE = void 0;
7
- exports.HETZNER_API_BASE = "https://api.hetzner.cloud/v1";
package/config.ts DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * Hetzner Cloud API configuration
3
- */
4
-
5
- export const HETZNER_API_BASE = "https://api.hetzner.cloud/v1";