@ebowwa/hetzner 0.3.0 → 0.3.2

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.
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Onboarding Types
3
+ *
4
+ * Type definitions for server onboarding operations.
5
+ */
6
+ /**
7
+ * Onboarding configuration for a server
8
+ */
9
+ export interface OnboardingConfig {
10
+ /** Server IP or hostname */
11
+ host: string;
12
+ /** SSH user (default: root) */
13
+ user?: string;
14
+ /** SSH port (default: 22) */
15
+ port?: number;
16
+ /** Doppler configuration */
17
+ doppler?: {
18
+ /** Doppler service token */
19
+ token: string;
20
+ /** Doppler project (default: seed) */
21
+ project?: string;
22
+ /** Doppler config (default: prd) */
23
+ config?: string;
24
+ };
25
+ /** Tailscale configuration */
26
+ tailscale?: {
27
+ /** Tailscale auth key */
28
+ authKey: string;
29
+ /** Tailscale hostname (optional) */
30
+ hostname?: string;
31
+ /** Tailscale tags (optional) */
32
+ tags?: string[];
33
+ };
34
+ /** Git configuration */
35
+ git?: {
36
+ /** Git username */
37
+ username?: string;
38
+ /** Git email */
39
+ email?: string;
40
+ /** GitHub token for gh cli */
41
+ githubToken?: string;
42
+ };
43
+ /** Claude Code configuration */
44
+ claude?: {
45
+ /** Skip installation if already installed (default: true) */
46
+ skipIfInstalled?: boolean;
47
+ };
48
+ }
49
+ /**
50
+ * Onboarding status for a single service
51
+ */
52
+ export interface ServiceStatus {
53
+ /** Service name */
54
+ service: "doppler" | "tailscale" | "git" | "claude";
55
+ /** Whether service is configured */
56
+ configured: boolean;
57
+ /** Status message */
58
+ message: string;
59
+ /** Configuration details (sanitized) */
60
+ details?: Record<string, unknown>;
61
+ }
62
+ /**
63
+ * Overall onboarding status
64
+ */
65
+ export interface OnboardingStatus {
66
+ /** Server being checked */
67
+ host: string;
68
+ /** Status of each service */
69
+ services: {
70
+ doppler: ServiceStatus;
71
+ tailscale: ServiceStatus;
72
+ git: ServiceStatus;
73
+ claude: ServiceStatus;
74
+ };
75
+ /** Overall onboarding complete */
76
+ complete: boolean;
77
+ /** Timestamp */
78
+ checkedAt: string;
79
+ }
80
+ /**
81
+ * Result of an onboarding operation
82
+ */
83
+ export interface OnboardingResult {
84
+ /** Server that was onboarded */
85
+ host: string;
86
+ /** Services that were configured */
87
+ configured: ("doppler" | "tailscale" | "git" | "claude")[];
88
+ /** Services that failed */
89
+ failed: Array<{
90
+ service: "doppler" | "tailscale" | "git" | "claude";
91
+ error: string;
92
+ }>;
93
+ /** Overall success */
94
+ success: boolean;
95
+ /** Timestamp */
96
+ completedAt: string;
97
+ }
98
+ /**
99
+ * Batch onboarding result
100
+ */
101
+ export interface BatchOnboardingResult {
102
+ /** Results for each server */
103
+ results: OnboardingResult[];
104
+ /** Summary statistics */
105
+ summary: {
106
+ total: number;
107
+ succeeded: number;
108
+ failed: number;
109
+ partial: number;
110
+ };
111
+ }
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Hetzner pricing operations - fetch server types, locations, and calculate costs
3
+ */
4
+ import { z } from "zod";
5
+ import type { HetznerServerType, HetznerLocation, HetznerDatacenter } from "./types.js";
6
+ import type { HetznerClient } from "./client.js";
7
+ import type { Environment } from "@ebowwa/codespaces-types/compile";
8
+ /**
9
+ * Price information for a server type (in EUR)
10
+ */
11
+ export interface ServerTypePrice {
12
+ /** Server type name (e.g., "cpx11") */
13
+ serverType: string;
14
+ /** Monthly price in EUR */
15
+ priceMonthly: number;
16
+ /** Hourly price in EUR */
17
+ priceHourly: number;
18
+ /** Whether the server type is deprecated */
19
+ deprecated: boolean;
20
+ }
21
+ /**
22
+ * Cost breakdown for a single environment
23
+ */
24
+ export interface EnvironmentCost {
25
+ /** Environment ID */
26
+ environmentId: string;
27
+ /** Environment name */
28
+ environmentName: string;
29
+ /** Server type name */
30
+ serverType: string;
31
+ /** Whether the environment is currently running */
32
+ isRunning: boolean;
33
+ /** Monthly cost in EUR */
34
+ costMonthly: number;
35
+ /** Hourly cost in EUR */
36
+ costHourly: number;
37
+ /** Price info (undefined if server type not found) */
38
+ priceInfo?: ServerTypePrice;
39
+ }
40
+ /**
41
+ * Total cost calculation result
42
+ */
43
+ export interface CostCalculationResult {
44
+ /** Total monthly cost for all running environments (EUR) */
45
+ totalMonthly: number;
46
+ /** Total hourly cost for all running environments (EUR) */
47
+ totalHourly: number;
48
+ /** Number of environments included in calculation */
49
+ runningEnvironmentCount: number;
50
+ /** Number of environments excluded (not running) */
51
+ stoppedEnvironmentCount: number;
52
+ /** Number of environments with unknown server types */
53
+ unknownServerTypeCount: number;
54
+ /** Detailed breakdown by environment */
55
+ breakdown: EnvironmentCost[];
56
+ /** Map of server type names to their prices (for reference) */
57
+ priceMap: Map<string, ServerTypePrice>;
58
+ }
59
+ /**
60
+ * Schema for server type price entry
61
+ */
62
+ export declare const ServerTypePriceSchema: z.ZodObject<{
63
+ serverType: z.ZodString;
64
+ priceMonthly: z.ZodNumber;
65
+ priceHourly: z.ZodNumber;
66
+ deprecated: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
67
+ }, "strip", z.ZodTypeAny, {
68
+ deprecated?: boolean;
69
+ serverType?: string;
70
+ priceMonthly?: number;
71
+ priceHourly?: number;
72
+ }, {
73
+ deprecated?: boolean;
74
+ serverType?: string;
75
+ priceMonthly?: number;
76
+ priceHourly?: number;
77
+ }>;
78
+ /**
79
+ * Schema for environment cost entry
80
+ */
81
+ export declare const EnvironmentCostSchema: z.ZodObject<{
82
+ environmentId: z.ZodString;
83
+ environmentName: z.ZodString;
84
+ serverType: z.ZodString;
85
+ isRunning: z.ZodBoolean;
86
+ costMonthly: z.ZodNumber;
87
+ costHourly: z.ZodNumber;
88
+ priceInfo: z.ZodOptional<z.ZodObject<{
89
+ serverType: z.ZodString;
90
+ priceMonthly: z.ZodNumber;
91
+ priceHourly: z.ZodNumber;
92
+ deprecated: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
93
+ }, "strip", z.ZodTypeAny, {
94
+ deprecated?: boolean;
95
+ serverType?: string;
96
+ priceMonthly?: number;
97
+ priceHourly?: number;
98
+ }, {
99
+ deprecated?: boolean;
100
+ serverType?: string;
101
+ priceMonthly?: number;
102
+ priceHourly?: number;
103
+ }>>;
104
+ }, "strip", z.ZodTypeAny, {
105
+ serverType?: string;
106
+ environmentId?: string;
107
+ environmentName?: string;
108
+ isRunning?: boolean;
109
+ costMonthly?: number;
110
+ costHourly?: number;
111
+ priceInfo?: {
112
+ deprecated?: boolean;
113
+ serverType?: string;
114
+ priceMonthly?: number;
115
+ priceHourly?: number;
116
+ };
117
+ }, {
118
+ serverType?: string;
119
+ environmentId?: string;
120
+ environmentName?: string;
121
+ isRunning?: boolean;
122
+ costMonthly?: number;
123
+ costHourly?: number;
124
+ priceInfo?: {
125
+ deprecated?: boolean;
126
+ serverType?: string;
127
+ priceMonthly?: number;
128
+ priceHourly?: number;
129
+ };
130
+ }>;
131
+ /**
132
+ * Schema for cost calculation result
133
+ */
134
+ export declare const CostCalculationResultSchema: z.ZodObject<{
135
+ totalMonthly: z.ZodNumber;
136
+ totalHourly: z.ZodNumber;
137
+ runningEnvironmentCount: z.ZodNumber;
138
+ stoppedEnvironmentCount: z.ZodNumber;
139
+ unknownServerTypeCount: z.ZodNumber;
140
+ breakdown: z.ZodArray<z.ZodObject<{
141
+ environmentId: z.ZodString;
142
+ environmentName: z.ZodString;
143
+ serverType: z.ZodString;
144
+ isRunning: z.ZodBoolean;
145
+ costMonthly: z.ZodNumber;
146
+ costHourly: z.ZodNumber;
147
+ priceInfo: z.ZodOptional<z.ZodObject<{
148
+ serverType: z.ZodString;
149
+ priceMonthly: z.ZodNumber;
150
+ priceHourly: z.ZodNumber;
151
+ deprecated: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
152
+ }, "strip", z.ZodTypeAny, {
153
+ deprecated?: boolean;
154
+ serverType?: string;
155
+ priceMonthly?: number;
156
+ priceHourly?: number;
157
+ }, {
158
+ deprecated?: boolean;
159
+ serverType?: string;
160
+ priceMonthly?: number;
161
+ priceHourly?: number;
162
+ }>>;
163
+ }, "strip", z.ZodTypeAny, {
164
+ serverType?: string;
165
+ environmentId?: string;
166
+ environmentName?: string;
167
+ isRunning?: boolean;
168
+ costMonthly?: number;
169
+ costHourly?: number;
170
+ priceInfo?: {
171
+ deprecated?: boolean;
172
+ serverType?: string;
173
+ priceMonthly?: number;
174
+ priceHourly?: number;
175
+ };
176
+ }, {
177
+ serverType?: string;
178
+ environmentId?: string;
179
+ environmentName?: string;
180
+ isRunning?: boolean;
181
+ costMonthly?: number;
182
+ costHourly?: number;
183
+ priceInfo?: {
184
+ deprecated?: boolean;
185
+ serverType?: string;
186
+ priceMonthly?: number;
187
+ priceHourly?: number;
188
+ };
189
+ }>, "many">;
190
+ priceMap: z.ZodType<Map<string, ServerTypePrice>, z.ZodTypeDef, Map<string, ServerTypePrice>>;
191
+ }, "strip", z.ZodTypeAny, {
192
+ totalMonthly?: number;
193
+ totalHourly?: number;
194
+ runningEnvironmentCount?: number;
195
+ stoppedEnvironmentCount?: number;
196
+ unknownServerTypeCount?: number;
197
+ breakdown?: {
198
+ serverType?: string;
199
+ environmentId?: string;
200
+ environmentName?: string;
201
+ isRunning?: boolean;
202
+ costMonthly?: number;
203
+ costHourly?: number;
204
+ priceInfo?: {
205
+ deprecated?: boolean;
206
+ serverType?: string;
207
+ priceMonthly?: number;
208
+ priceHourly?: number;
209
+ };
210
+ }[];
211
+ priceMap?: Map<string, ServerTypePrice>;
212
+ }, {
213
+ totalMonthly?: number;
214
+ totalHourly?: number;
215
+ runningEnvironmentCount?: number;
216
+ stoppedEnvironmentCount?: number;
217
+ unknownServerTypeCount?: number;
218
+ breakdown?: {
219
+ serverType?: string;
220
+ environmentId?: string;
221
+ environmentName?: string;
222
+ isRunning?: boolean;
223
+ costMonthly?: number;
224
+ costHourly?: number;
225
+ priceInfo?: {
226
+ deprecated?: boolean;
227
+ serverType?: string;
228
+ priceMonthly?: number;
229
+ priceHourly?: number;
230
+ };
231
+ }[];
232
+ priceMap?: Map<string, ServerTypePrice>;
233
+ }>;
234
+ /**
235
+ * Parse Hetzner price string to number (EUR)
236
+ * Hetzner returns prices as strings in gross format (e.g., "5.3400" for 5.34 EUR)
237
+ *
238
+ * @param priceString - Price string from Hetzner API (gross field)
239
+ * @returns Price in EUR as number
240
+ */
241
+ export declare function parseHetznerPrice(priceString: string): number;
242
+ /**
243
+ * Extract pricing information from a Hetzner server type
244
+ * Uses the first price entry (location-agnostic pricing)
245
+ *
246
+ * @param serverType - Hetzner server type object
247
+ * @returns Server type price info or undefined if pricing unavailable
248
+ */
249
+ export declare function extractServerTypePrice(serverType: HetznerServerType): ServerTypePrice | undefined;
250
+ /**
251
+ * Build a price lookup map from server types
252
+ *
253
+ * @param serverTypes - Array of Hetzner server types
254
+ * @returns Map of server type name to price info
255
+ */
256
+ export declare function buildPriceMap(serverTypes: HetznerServerType[]): Map<string, ServerTypePrice>;
257
+ /**
258
+ * Get the monthly price for a server type from the price map
259
+ * Returns fallback price for unknown/deprecated types
260
+ *
261
+ * @param serverTypeName - Name of the server type
262
+ * @param priceMap - Price lookup map
263
+ * @param fallbackPrice - Fallback price for unknown types (default: 5.0 EUR)
264
+ * @returns Monthly price in EUR
265
+ */
266
+ export declare function getServerTypeMonthlyPrice(serverTypeName: string, priceMap: Map<string, ServerTypePrice>, fallbackPrice?: number): number;
267
+ /**
268
+ * Calculate hourly price from monthly price
269
+ * Uses standard 730 hours per month
270
+ *
271
+ * @param monthlyPrice - Monthly price in EUR
272
+ * @returns Hourly price in EUR
273
+ */
274
+ export declare function calculateHourlyFromMonthly(monthlyPrice: number): number;
275
+ /**
276
+ * Calculate total costs for a list of environments
277
+ *
278
+ * This function:
279
+ * - Only includes environments with "running" status
280
+ * - Handles missing/deprecated server types with fallback pricing
281
+ * - Returns detailed breakdown and aggregated totals
282
+ *
283
+ * @param environments - Array of environment objects
284
+ * @param serverTypes - Array of Hetzner server types with pricing
285
+ * @param options - Optional configuration
286
+ * @returns Cost calculation result with totals and breakdown
287
+ */
288
+ export declare function calculateCosts(environments: Environment[], serverTypes: HetznerServerType[], options?: {
289
+ /** Custom fallback price for unknown server types (EUR/month) */
290
+ fallbackPrice?: number;
291
+ /** Whether to include stopped environments in breakdown (default: false) */
292
+ includeStopped?: boolean;
293
+ }): CostCalculationResult;
294
+ export declare class PricingOperations {
295
+ private client;
296
+ constructor(client: HetznerClient);
297
+ /**
298
+ * List all server types
299
+ */
300
+ listServerTypes(): Promise<HetznerServerType[]>;
301
+ /**
302
+ * Get a specific server type by name
303
+ */
304
+ getServerType(name: string): Promise<HetznerServerType | undefined>;
305
+ /**
306
+ * List all locations
307
+ */
308
+ listLocations(): Promise<HetznerLocation[]>;
309
+ /**
310
+ * Get a specific location by name
311
+ */
312
+ getLocation(name: string): Promise<HetznerLocation | undefined>;
313
+ /**
314
+ * List all datacenters
315
+ */
316
+ listDatacenters(): Promise<HetznerDatacenter[]>;
317
+ /**
318
+ * Calculate costs for environments using current Hetzner pricing
319
+ *
320
+ * Convenience method that fetches server types and calculates costs in one call
321
+ *
322
+ * @param environments - Array of environment objects
323
+ * @param options - Optional configuration for cost calculation
324
+ * @returns Cost calculation result
325
+ */
326
+ calculateEnvironmentCosts(environments: Environment[], options?: {
327
+ fallbackPrice?: number;
328
+ includeStopped?: boolean;
329
+ }): Promise<CostCalculationResult>;
330
+ }