@ebowwa/hetzner 0.2.2 → 0.3.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 (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
@@ -0,0 +1,460 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/onboarding/doppler.ts
21
+ async function onboardDoppler(host, config, sshOptions = {}) {
22
+ const { execSSH } = await import("@ebowwa/terminal/client");
23
+ const { token, project = "seed", config: dopplerConfig = "prd" } = config;
24
+ try {
25
+ const configureCmd = [
26
+ `doppler configure set token ${token}`,
27
+ `doppler configure set project ${project}`,
28
+ `doppler configure set config ${dopplerConfig}`
29
+ ].join(" && ");
30
+ await execSSH(configureCmd, {
31
+ host,
32
+ user: "root",
33
+ ...sshOptions
34
+ });
35
+ const verifyCmd = `doppler secrets get --config ${dopplerConfig} --project ${project} --json 2>/dev/null | head -5`;
36
+ await execSSH(verifyCmd, {
37
+ host,
38
+ user: "root",
39
+ timeout: 1e4,
40
+ ...sshOptions
41
+ });
42
+ return {
43
+ success: true,
44
+ message: `Doppler configured for project ${project}/${dopplerConfig}`
45
+ };
46
+ } catch (error) {
47
+ return {
48
+ success: false,
49
+ message: `Doppler configuration failed: ${error instanceof Error ? error.message : String(error)}`
50
+ };
51
+ }
52
+ }
53
+ async function checkDopplerStatus(host, sshOptions = {}) {
54
+ const { execSSH } = await import("@ebowwa/terminal/client");
55
+ try {
56
+ const checkCmd = `
57
+ doppler configure get project 2>/dev/null || echo "NOT_CONFIGURED"
58
+ doppler configure get config 2>/dev/null || echo "NOT_CONFIGURED"
59
+ `;
60
+ const result = await execSSH(checkCmd, {
61
+ host,
62
+ user: "root",
63
+ timeout: 5000,
64
+ ...sshOptions
65
+ });
66
+ const lines = result.trim().split(`
67
+ `);
68
+ const project = lines[0]?.trim() || "";
69
+ const config = lines[1]?.trim() || "";
70
+ const configured = project !== "NOT_CONFIGURED" && config !== "NOT_CONFIGURED";
71
+ return {
72
+ configured,
73
+ project: configured ? project : undefined,
74
+ config: configured ? config : undefined,
75
+ message: configured ? `Doppler configured: ${project}/${config}` : "Doppler not configured"
76
+ };
77
+ } catch (error) {
78
+ return {
79
+ configured: false,
80
+ message: `Could not check Doppler status: ${error instanceof Error ? error.message : String(error)}`
81
+ };
82
+ }
83
+ }
84
+ // src/onboarding/tailscale.ts
85
+ async function onboardTailscale(host, config, sshOptions = {}) {
86
+ const { execSSH } = await import("@ebowwa/terminal/client");
87
+ const { authKey, hostname, tags = [] } = config;
88
+ try {
89
+ const installCmd = `
90
+ if ! command -v tailscale &>/dev/null; then
91
+ curl -fsSL https://tailscale.com/install.sh | sh
92
+ fi
93
+ `;
94
+ await execSSH(installCmd, {
95
+ host,
96
+ user: "root",
97
+ timeout: 60000,
98
+ ...sshOptions
99
+ });
100
+ const upArgs = ["up", "--authkey", authKey];
101
+ if (hostname) {
102
+ upArgs.push("--hostname", hostname);
103
+ }
104
+ if (tags.length > 0) {
105
+ upArgs.push("--tags", tags.join(","));
106
+ }
107
+ const upCmd = `tailscale ${upArgs.join(" ")}`;
108
+ await execSSH(upCmd, {
109
+ host,
110
+ user: "root",
111
+ timeout: 30000,
112
+ ...sshOptions
113
+ });
114
+ const ipCmd = "tailscale ip -4 2>/dev/null || tailscale ip 2>/dev/null || echo 'PENDING'";
115
+ const ipResult = await execSSH(ipCmd, {
116
+ host,
117
+ user: "root",
118
+ timeout: 5000,
119
+ ...sshOptions
120
+ });
121
+ const tailscaleIp = ipResult.trim();
122
+ return {
123
+ success: true,
124
+ message: hostname ? `Tailscale joined as ${hostname}` : "Tailscale joined",
125
+ tailscaleIp: tailscaleIp !== "PENDING" ? tailscaleIp : undefined
126
+ };
127
+ } catch (error) {
128
+ return {
129
+ success: false,
130
+ message: `Tailscale onboarding failed: ${error instanceof Error ? error.message : String(error)}`
131
+ };
132
+ }
133
+ }
134
+ async function checkTailscaleStatus(host, sshOptions = {}) {
135
+ const { execSSH } = await import("@ebowwa/terminal/client");
136
+ try {
137
+ const checkCmd = `
138
+ command -v tailscale &>/dev/null || echo "NOT_INSTALLED"
139
+ tailscale status --json 2>/dev/null || echo "NOT_RUNNING"
140
+ `;
141
+ const result = await execSSH(checkCmd, {
142
+ host,
143
+ user: "root",
144
+ timeout: 1e4,
145
+ ...sshOptions
146
+ });
147
+ const lines = result.trim().split(`
148
+ `);
149
+ if (lines[0]?.includes("NOT_INSTALLED")) {
150
+ return {
151
+ configured: false,
152
+ message: "Tailscale not installed"
153
+ };
154
+ }
155
+ const statusJson = lines[1] || "";
156
+ if (statusJson.includes("NOT_RUNNING")) {
157
+ return {
158
+ configured: false,
159
+ message: "Tailscale installed but not running"
160
+ };
161
+ }
162
+ const status = JSON.parse(statusJson);
163
+ const tailscaleIp = status.TailnetIPs?.[0] || status.IPv4;
164
+ const hostname = status.HostName?.HostName || status.HostName;
165
+ return {
166
+ configured: true,
167
+ tailscaleIp,
168
+ hostname,
169
+ message: `Tailscale connected as ${hostname || "unknown"} (${tailscaleIp || "no IP"})`
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ configured: false,
174
+ message: `Could not check Tailscale status: ${error instanceof Error ? error.message : String(error)}`
175
+ };
176
+ }
177
+ }
178
+ // src/onboarding/git.ts
179
+ async function onboardGit(host, config, sshOptions = {}) {
180
+ const { execSSH } = await import("@ebowwa/terminal/client");
181
+ const { username = "root", email, githubToken } = config;
182
+ try {
183
+ const commands = [];
184
+ commands.push(`git config --global user.name "${username}"`);
185
+ if (email) {
186
+ commands.push(`git config --global user.email "${email}"`);
187
+ }
188
+ commands.push(`
189
+ git config --global credential.helper store
190
+ mkdir -p ~/.config/gh
191
+ `);
192
+ if (githubToken) {
193
+ commands.push(`
194
+ echo "github.com:" ${username}:${githubToken} > ~/.git-credentials
195
+ chmod 600 ~/.git-credentials
196
+ echo "GITHUB_TOKEN=${githubToken}" > ~/.config/gh/hosts.yml
197
+ chmod 600 ~/.config/gh/hosts.yml
198
+ `);
199
+ }
200
+ const cmd = commands.join(" && ");
201
+ await execSSH(cmd, {
202
+ host,
203
+ user: "root",
204
+ timeout: 1e4,
205
+ ...sshOptions
206
+ });
207
+ return {
208
+ success: true,
209
+ message: `Git configured for ${username}${email ? ` (${email})` : ""}`
210
+ };
211
+ } catch (error) {
212
+ return {
213
+ success: false,
214
+ message: `Git configuration failed: ${error instanceof Error ? error.message : String(error)}`
215
+ };
216
+ }
217
+ }
218
+ async function checkGitStatus(host, sshOptions = {}) {
219
+ const { execSSH } = await import("@ebowwa/terminal/client");
220
+ try {
221
+ const checkCmd = `
222
+ git config --global user.name 2>/dev/null || echo "NOT_SET"
223
+ git config --global user.email 2>/dev/null || echo "NOT_SET"
224
+ test -f ~/.git-credentials && echo "HAS_CREDS" || echo "NO_CREDS"
225
+ test -f ~/.config/gh/hosts.yml && echo "HAS_GH_TOKEN" || echo "NO_GH_TOKEN"
226
+ `;
227
+ const result = await execSSH(checkCmd, {
228
+ host,
229
+ user: "root",
230
+ timeout: 5000,
231
+ ...sshOptions
232
+ });
233
+ const lines = result.trim().split(`
234
+ `);
235
+ const username = lines[0]?.trim();
236
+ const email = lines[1]?.trim();
237
+ const hasCreds = lines[2]?.includes("HAS_CREDS");
238
+ const hasGhToken = lines[3]?.includes("HAS_GH_TOKEN");
239
+ const configured = username !== "NOT_SET";
240
+ return {
241
+ configured,
242
+ username: username !== "NOT_SET" ? username : undefined,
243
+ email: email !== "NOT_SET" ? email : undefined,
244
+ hasToken: hasCreds || hasGhToken,
245
+ message: configured ? `Git configured: ${username}${email ? ` <${email}>` : ""}` : "Git not configured"
246
+ };
247
+ } catch (error) {
248
+ return {
249
+ configured: false,
250
+ hasToken: false,
251
+ message: `Could not check Git status: ${error instanceof Error ? error.message : String(error)}`
252
+ };
253
+ }
254
+ }
255
+ // src/onboarding/claude.ts
256
+ async function onboardClaude(host, config = {}, sshOptions = {}) {
257
+ const { execSSH } = await import("@ebowwa/terminal/client");
258
+ try {
259
+ if (config.skipIfInstalled !== false) {
260
+ const checkResult = await execSSH('export PATH="$HOME/.local/bin:$PATH" && claude --version 2>/dev/null || echo "NOT_INSTALLED"', {
261
+ host,
262
+ user: "root",
263
+ timeout: 5000,
264
+ ...sshOptions
265
+ });
266
+ if (!checkResult.includes("NOT_INSTALLED")) {
267
+ const version2 = checkResult.trim();
268
+ return {
269
+ success: true,
270
+ message: `Claude Code already installed (${version2})`,
271
+ version: version2
272
+ };
273
+ }
274
+ }
275
+ const installCmd = `
276
+ curl -fsSL https://claude.ai/install.sh | bash && echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
277
+ `;
278
+ await execSSH(installCmd, {
279
+ host,
280
+ user: "root",
281
+ timeout: 60000,
282
+ ...sshOptions
283
+ });
284
+ const verifyResult = await execSSH('export PATH="$HOME/.local/bin:$PATH" && claude --version', {
285
+ host,
286
+ user: "root",
287
+ timeout: 5000,
288
+ ...sshOptions
289
+ });
290
+ const version = verifyResult.trim();
291
+ return {
292
+ success: true,
293
+ message: `Claude Code installed (${version})`,
294
+ version
295
+ };
296
+ } catch (error) {
297
+ return {
298
+ success: false,
299
+ message: `Claude Code installation failed: ${error instanceof Error ? error.message : String(error)}`
300
+ };
301
+ }
302
+ }
303
+ async function checkClaudeStatus(host, sshOptions = {}) {
304
+ const { execSSH } = await import("@ebowwa/terminal/client");
305
+ try {
306
+ const checkCmd = `
307
+ export PATH="$HOME/.local/bin:$PATH" && claude --version 2>/dev/null || echo "NOT_INSTALLED"
308
+ `;
309
+ const result = await execSSH(checkCmd, {
310
+ host,
311
+ user: "root",
312
+ timeout: 5000,
313
+ ...sshOptions
314
+ });
315
+ if (result.includes("NOT_INSTALLED")) {
316
+ return {
317
+ configured: false,
318
+ message: "Claude Code not installed"
319
+ };
320
+ }
321
+ const version = result.trim();
322
+ const pathCheck = await execSSH('grep -q ".local/bin" ~/.bashrc 2>/dev/null && echo "IN_PATH" || echo "NOT_IN_PATH"', {
323
+ host,
324
+ user: "root",
325
+ timeout: 5000,
326
+ ...sshOptions
327
+ });
328
+ const inPath = pathCheck.includes("IN_PATH");
329
+ return {
330
+ configured: true,
331
+ version,
332
+ path: inPath ? "~/.local/bin (in PATH)" : "~/.local/bin (not in PATH)",
333
+ message: `Claude Code ${version} installed${inPath ? "" : " (add to PATH)"}`
334
+ };
335
+ } catch (error) {
336
+ return {
337
+ configured: false,
338
+ message: `Could not check Claude status: ${error instanceof Error ? error.message : String(error)}`
339
+ };
340
+ }
341
+ }
342
+ // src/onboarding/onboarding.ts
343
+ async function onboardServer(config, sshOptions = {}) {
344
+ const { host, user = "root", doppler, tailscale, git, claude } = config;
345
+ const configured = [];
346
+ const failed = [];
347
+ const baseSshOptions = {
348
+ user,
349
+ ...sshOptions
350
+ };
351
+ if (doppler) {
352
+ const result = await onboardDoppler(host, doppler, baseSshOptions);
353
+ if (result.success) {
354
+ configured.push("doppler");
355
+ } else {
356
+ failed.push({ service: "doppler", error: result.message });
357
+ }
358
+ }
359
+ if (tailscale) {
360
+ const result = await onboardTailscale(host, tailscale, baseSshOptions);
361
+ if (result.success) {
362
+ configured.push("tailscale");
363
+ } else {
364
+ failed.push({ service: "tailscale", error: result.message });
365
+ }
366
+ }
367
+ if (git) {
368
+ const result = await onboardGit(host, git, baseSshOptions);
369
+ if (result.success) {
370
+ configured.push("git");
371
+ } else {
372
+ failed.push({ service: "git", error: result.message });
373
+ }
374
+ }
375
+ {
376
+ const result = await onboardClaude(host, claude || {}, baseSshOptions);
377
+ if (result.success) {
378
+ configured.push("claude");
379
+ } else {
380
+ failed.push({ service: "claude", error: result.message });
381
+ }
382
+ }
383
+ return {
384
+ host,
385
+ configured,
386
+ failed,
387
+ success: failed.length === 0,
388
+ completedAt: new Date().toISOString()
389
+ };
390
+ }
391
+ async function checkOnboardingStatus(host, sshOptions = {}) {
392
+ const user = "root";
393
+ const [dopplerStatus, tailscaleStatus, gitStatus, claudeStatus] = await Promise.all([
394
+ checkDopplerStatus(host, { user, ...sshOptions }),
395
+ checkTailscaleStatus(host, { user, ...sshOptions }),
396
+ checkGitStatus(host, { user, ...sshOptions }),
397
+ checkClaudeStatus(host, { user, ...sshOptions })
398
+ ]);
399
+ const services = {
400
+ doppler: {
401
+ service: "doppler",
402
+ configured: dopplerStatus.configured,
403
+ message: dopplerStatus.message,
404
+ details: dopplerStatus.project ? { project: dopplerStatus.project, config: dopplerStatus.config } : undefined
405
+ },
406
+ tailscale: {
407
+ service: "tailscale",
408
+ configured: tailscaleStatus.configured,
409
+ message: tailscaleStatus.message,
410
+ details: tailscaleStatus.tailscaleIp ? { tailscaleIp: tailscaleStatus.tailscaleIp, hostname: tailscaleStatus.hostname } : undefined
411
+ },
412
+ git: {
413
+ service: "git",
414
+ configured: gitStatus.configured,
415
+ message: gitStatus.message,
416
+ details: gitStatus.username ? { username: gitStatus.username, email: gitStatus.email } : undefined
417
+ },
418
+ claude: {
419
+ service: "claude",
420
+ configured: claudeStatus.configured,
421
+ message: claudeStatus.message,
422
+ details: claudeStatus.version ? { version: claudeStatus.version, path: claudeStatus.path } : undefined
423
+ }
424
+ };
425
+ const complete = services.doppler.configured && services.tailscale.configured && services.git.configured && services.claude.configured;
426
+ return {
427
+ host,
428
+ services,
429
+ complete,
430
+ checkedAt: new Date().toISOString()
431
+ };
432
+ }
433
+ async function onboardBatch(hosts, sharedConfig, sshOptions = {}) {
434
+ const results = await Promise.all(hosts.map((host) => onboardServer({ ...sharedConfig, host }, sshOptions)));
435
+ const succeeded = results.filter((r) => r.success).length;
436
+ const failed = results.filter((r) => !r.success).length;
437
+ const partial = results.filter((r) => !r.success && r.configured.length > 0).length;
438
+ return {
439
+ results,
440
+ summary: {
441
+ total: results.length,
442
+ succeeded,
443
+ failed,
444
+ partial
445
+ }
446
+ };
447
+ }
448
+ export {
449
+ onboardTailscale,
450
+ onboardServer,
451
+ onboardGit,
452
+ onboardDoppler,
453
+ onboardClaude,
454
+ onboardBatch,
455
+ checkTailscaleStatus,
456
+ checkOnboardingStatus,
457
+ checkGitStatus,
458
+ checkDopplerStatus,
459
+ checkClaudeStatus
460
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/onboarding/doppler.ts", "../../src/onboarding/tailscale.ts", "../../src/onboarding/git.ts", "../../src/onboarding/claude.ts", "../../src/onboarding/onboarding.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Doppler Onboarding\n *\n * Configure Doppler CLI service token on remote servers.\n * This enables doppler run to inject secrets into processes.\n */\n\nimport type { SSHOptions } from \"@ebowwa/terminal/types\";\n\nexport interface DopplerConfig {\n token: string;\n project?: string;\n config?: string;\n}\n\n/**\n * Configure Doppler on a remote server\n *\n * @param host - Server IP or hostname\n * @param config - Doppler configuration\n * @param sshOptions - SSH options\n * @returns Success status\n */\nexport async function onboardDoppler(\n host: string,\n config: DopplerConfig,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ success: boolean; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n const { token, project = \"seed\", config: dopplerConfig = \"prd\" } = config;\n\n try {\n // Step 1: Configure Doppler service token\n const configureCmd = [\n `doppler configure set token ${token}`,\n `doppler configure set project ${project}`,\n `doppler configure set config ${dopplerConfig}`,\n ].join(\" && \");\n\n await execSSH(configureCmd, {\n host,\n user: \"root\",\n ...sshOptions\n });\n\n // Step 2: Verify configuration by fetching a secret\n const verifyCmd = `doppler secrets get --config ${dopplerConfig} --project ${project} --json 2>/dev/null | head -5`;\n\n await execSSH(verifyCmd, {\n host,\n user: \"root\",\n timeout: 10000,\n ...sshOptions\n });\n\n return {\n success: true,\n message: `Doppler configured for project ${project}/${dopplerConfig}`\n };\n } catch (error) {\n return {\n success: false,\n message: `Doppler configuration failed: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n\n/**\n * Check Doppler status on a remote server\n *\n * @param host - Server IP or hostname\n * @param sshOptions - SSH options\n * @returns Doppler status\n */\nexport async function checkDopplerStatus(\n host: string,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ configured: boolean; project?: string; config?: string; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n try {\n // Check if doppler is configured\n const checkCmd = `\n doppler configure get project 2>/dev/null || echo \"NOT_CONFIGURED\"\n doppler configure get config 2>/dev/null || echo \"NOT_CONFIGURED\"\n `;\n\n const result = await execSSH(checkCmd, {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n });\n\n const lines = result.trim().split(\"\\n\");\n const project = lines[0]?.trim() || \"\";\n const config = lines[1]?.trim() || \"\";\n\n const configured = project !== \"NOT_CONFIGURED\" && config !== \"NOT_CONFIGURED\";\n\n return {\n configured,\n project: configured ? project : undefined,\n config: configured ? config : undefined,\n message: configured\n ? `Doppler configured: ${project}/${config}`\n : \"Doppler not configured\"\n };\n } catch (error) {\n return {\n configured: false,\n message: `Could not check Doppler status: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n",
6
+ "/**\n * Tailscale Onboarding\n *\n * Join Tailscale network on remote servers.\n * Enables secure VPN access to servers.\n */\n\nimport type { SSHOptions } from \"@ebowwa/terminal/types\";\n\nexport interface TailscaleConfig {\n authKey: string;\n hostname?: string;\n tags?: string[];\n}\n\n/**\n * Join Tailscale network on a remote server\n *\n * @param host - Server IP or hostname\n * @param config - Tailscale configuration\n * @param sshOptions - SSH options\n * @returns Success status with Tailscale IP\n */\nexport async function onboardTailscale(\n host: string,\n config: TailscaleConfig,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ success: boolean; message: string; tailscaleIp?: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n const { authKey, hostname, tags = [] } = config;\n\n try {\n // Step 1: Install Tailscale if not present\n const installCmd = `\n if ! command -v tailscale &>/dev/null; then\n curl -fsSL https://tailscale.com/install.sh | sh\n fi\n `;\n\n await execSSH(installCmd, {\n host,\n user: \"root\",\n timeout: 60000, // 1 minute for install\n ...sshOptions\n });\n\n // Step 2: Build tailscale up command\n const upArgs = [\"up\", \"--authkey\", authKey];\n\n if (hostname) {\n upArgs.push(\"--hostname\", hostname);\n }\n\n if (tags.length > 0) {\n upArgs.push(\"--tags\", tags.join(\",\"));\n }\n\n const upCmd = `tailscale ${upArgs.join(\" \")}`;\n\n // Step 3: Run tailscale up\n await execSSH(upCmd, {\n host,\n user: \"root\",\n timeout: 30000,\n ...sshOptions\n });\n\n // Step 4: Get Tailscale IP\n const ipCmd = \"tailscale ip -4 2>/dev/null || tailscale ip 2>/dev/null || echo 'PENDING'\";\n\n const ipResult = await execSSH(ipCmd, {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n });\n\n const tailscaleIp = ipResult.trim();\n\n return {\n success: true,\n message: hostname\n ? `Tailscale joined as ${hostname}`\n : \"Tailscale joined\",\n tailscaleIp: tailscaleIp !== \"PENDING\" ? tailscaleIp : undefined\n };\n } catch (error) {\n return {\n success: false,\n message: `Tailscale onboarding failed: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n\n/**\n * Check Tailscale status on a remote server\n *\n * @param host - Server IP or hostname\n * @param sshOptions - SSH options\n * @returns Tailscale status\n */\nexport async function checkTailscaleStatus(\n host: string,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ configured: boolean; tailscaleIp?: string; hostname?: string; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n try {\n // Check if tailscale is installed and running\n const checkCmd = `\n command -v tailscale &>/dev/null || echo \"NOT_INSTALLED\"\n tailscale status --json 2>/dev/null || echo \"NOT_RUNNING\"\n `;\n\n const result = await execSSH(checkCmd, {\n host,\n user: \"root\",\n timeout: 10000,\n ...sshOptions\n });\n\n const lines = result.trim().split(\"\\n\");\n\n if (lines[0]?.includes(\"NOT_INSTALLED\")) {\n return {\n configured: false,\n message: \"Tailscale not installed\"\n };\n }\n\n const statusJson = lines[1] || \"\";\n\n if (statusJson.includes(\"NOT_RUNNING\")) {\n return {\n configured: false,\n message: \"Tailscale installed but not running\"\n };\n }\n\n // Parse status JSON\n const status = JSON.parse(statusJson);\n\n const tailscaleIp = status.TailnetIPs?.[0] || status.IPv4;\n const hostname = status.HostName?.HostName || status.HostName;\n\n return {\n configured: true,\n tailscaleIp,\n hostname,\n message: `Tailscale connected as ${hostname || \"unknown\"} (${tailscaleIp || \"no IP\"})`\n };\n } catch (error) {\n return {\n configured: false,\n message: `Could not check Tailscale status: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n",
7
+ "/**\n * Git Onboarding\n *\n * Configure Git credentials on remote servers.\n * Sets up GitHub token for gh cli and git credential helper.\n */\n\nimport type { SSHOptions } from \"@ebowwa/terminal/types\";\n\nexport interface GitConfig {\n username?: string;\n email?: string;\n githubToken?: string;\n}\n\n/**\n * Configure Git on a remote server\n *\n * @param host - Server IP or hostname\n * @param config - Git configuration\n * @param sshOptions - SSH options\n * @returns Success status\n */\nexport async function onboardGit(\n host: string,\n config: GitConfig,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ success: boolean; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n const { username = \"root\", email, githubToken } = config;\n\n try {\n const commands: string[] = [];\n\n // Configure git user\n commands.push(`git config --global user.name \"${username}\"`);\n\n if (email) {\n commands.push(`git config --global user.email \"${email}\"`);\n }\n\n // Configure credential helper\n commands.push(`\n git config --global credential.helper store\n mkdir -p ~/.config/gh\n `);\n\n // Configure GitHub CLI token if provided\n if (githubToken) {\n commands.push(`\n echo \"github.com:\" ${username}:${githubToken} > ~/.git-credentials\n chmod 600 ~/.git-credentials\n echo \"GITHUB_TOKEN=${githubToken}\" > ~/.config/gh/hosts.yml\n chmod 600 ~/.config/gh/hosts.yml\n `);\n }\n\n const cmd = commands.join(\" && \");\n\n await execSSH(cmd, {\n host,\n user: \"root\",\n timeout: 10000,\n ...sshOptions\n });\n\n return {\n success: true,\n message: `Git configured for ${username}${email ? ` (${email})` : \"\"}`\n };\n } catch (error) {\n return {\n success: false,\n message: `Git configuration failed: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n\n/**\n * Check Git status on a remote server\n *\n * @param host - Server IP or hostname\n * @param sshOptions - SSH options\n * @returns Git status\n */\nexport async function checkGitStatus(\n host: string,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ configured: boolean; username?: string; email?: string; hasToken: boolean; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n try {\n const checkCmd = `\n git config --global user.name 2>/dev/null || echo \"NOT_SET\"\n git config --global user.email 2>/dev/null || echo \"NOT_SET\"\n test -f ~/.git-credentials && echo \"HAS_CREDS\" || echo \"NO_CREDS\"\n test -f ~/.config/gh/hosts.yml && echo \"HAS_GH_TOKEN\" || echo \"NO_GH_TOKEN\"\n `;\n\n const result = await execSSH(checkCmd, {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n });\n\n const lines = result.trim().split(\"\\n\");\n\n const username = lines[0]?.trim();\n const email = lines[1]?.trim();\n const hasCreds = lines[2]?.includes(\"HAS_CREDS\");\n const hasGhToken = lines[3]?.includes(\"HAS_GH_TOKEN\");\n\n const configured = username !== \"NOT_SET\";\n\n return {\n configured,\n username: username !== \"NOT_SET\" ? username : undefined,\n email: email !== \"NOT_SET\" ? email : undefined,\n hasToken: hasCreds || hasGhToken,\n message: configured\n ? `Git configured: ${username}${email ? ` <${email}>` : \"\"}`\n : \"Git not configured\"\n };\n } catch (error) {\n return {\n configured: false,\n hasToken: false,\n message: `Could not check Git status: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n",
8
+ "/**\n * Claude Code Onboarding\n *\n * Install and configure Claude Code on remote servers.\n * Uses official installer: curl -fsSL https://claude.ai/install.sh | bash\n */\n\nimport type { SSHOptions } from \"@ebowwa/terminal/types\";\n\nexport interface ClaudeConfig {\n /** Skip installation if already installed */\n skipIfInstalled?: boolean;\n}\n\n/**\n * Install Claude Code on a remote server\n *\n * @param host - Server IP or hostname\n * @param config - Claude configuration\n * @param sshOptions - SSH options\n * @returns Success status\n */\nexport async function onboardClaude(\n host: string,\n config: ClaudeConfig = {},\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ success: boolean; message: string; version?: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n try {\n // Check if already installed\n if (config.skipIfInstalled !== false) {\n const checkResult = await execSSH(\n 'export PATH=\"$HOME/.local/bin:$PATH\" && claude --version 2>/dev/null || echo \"NOT_INSTALLED\"',\n {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n }\n );\n\n if (!checkResult.includes(\"NOT_INSTALLED\")) {\n const version = checkResult.trim();\n return {\n success: true,\n message: `Claude Code already installed (${version})`,\n version\n };\n }\n }\n\n // Install Claude Code using official installer\n const installCmd = `\n curl -fsSL https://claude.ai/install.sh | bash && \\\n echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc\n `;\n\n await execSSH(installCmd, {\n host,\n user: \"root\",\n timeout: 60000, // 60 seconds for download\n ...sshOptions\n });\n\n // Verify installation\n const verifyResult = await execSSH(\n 'export PATH=\"$HOME/.local/bin:$PATH\" && claude --version',\n {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n }\n );\n\n const version = verifyResult.trim();\n\n return {\n success: true,\n message: `Claude Code installed (${version})`,\n version\n };\n } catch (error) {\n return {\n success: false,\n message: `Claude Code installation failed: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n\n/**\n * Check Claude Code status on a remote server\n *\n * @param host - Server IP or hostname\n * @param sshOptions - SSH options\n * @returns Claude status\n */\nexport async function checkClaudeStatus(\n host: string,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<{ configured: boolean; version?: string; path?: string; message: string }> {\n const { execSSH } = await import(\"@ebowwa/terminal/client\");\n\n try {\n const checkCmd = `\n export PATH=\"$HOME/.local/bin:$PATH\" && \\\n claude --version 2>/dev/null || echo \"NOT_INSTALLED\"\n `;\n\n const result = await execSSH(checkCmd, {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n });\n\n if (result.includes(\"NOT_INSTALLED\")) {\n return {\n configured: false,\n message: \"Claude Code not installed\"\n };\n }\n\n const version = result.trim();\n\n // Check if PATH is configured in .bashrc\n const pathCheck = await execSSH(\n 'grep -q \".local/bin\" ~/.bashrc 2>/dev/null && echo \"IN_PATH\" || echo \"NOT_IN_PATH\"',\n {\n host,\n user: \"root\",\n timeout: 5000,\n ...sshOptions\n }\n );\n\n const inPath = pathCheck.includes(\"IN_PATH\");\n\n return {\n configured: true,\n version,\n path: inPath ? \"~/.local/bin (in PATH)\" : \"~/.local/bin (not in PATH)\",\n message: `Claude Code ${version} installed${inPath ? \"\" : \" (add to PATH)\"}`\n };\n } catch (error) {\n return {\n configured: false,\n message: `Could not check Claude status: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n}\n",
9
+ "/**\n * Server Onboarding\n *\n * Main onboarding orchestration.\n * Coordinates Doppler, Tailscale, and Git onboarding.\n */\n\nimport type { SSHOptions } from \"@ebowwa/terminal/types\";\nimport type {\n OnboardingConfig,\n OnboardingStatus,\n OnboardingResult,\n BatchOnboardingResult,\n} from \"./types\";\nimport { checkDopplerStatus, onboardDoppler } from \"./doppler\";\nimport { checkTailscaleStatus, onboardTailscale } from \"./tailscale\";\nimport { checkGitStatus, onboardGit } from \"./git\";\nimport { checkClaudeStatus, onboardClaude } from \"./claude\";\n\n/**\n * Onboard a single server with all configured services\n *\n * @param config - Onboarding configuration\n * @param sshOptions - Additional SSH options\n * @returns Onboarding result\n */\nexport async function onboardServer(\n config: OnboardingConfig,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<OnboardingResult> {\n const { host, user = \"root\", doppler, tailscale, git, claude } = config;\n\n const configured: (\"doppler\" | \"tailscale\" | \"git\" | \"claude\")[] = [];\n const failed: Array<{\n service: \"doppler\" | \"tailscale\" | \"git\" | \"claude\";\n error: string;\n }> = [];\n\n const baseSshOptions: Partial<SSHOptions> = {\n user,\n ...sshOptions\n };\n\n // Onboard Doppler\n if (doppler) {\n const result = await onboardDoppler(host, doppler, baseSshOptions);\n if (result.success) {\n configured.push(\"doppler\");\n } else {\n failed.push({ service: \"doppler\", error: result.message });\n }\n }\n\n // Onboard Tailscale\n if (tailscale) {\n const result = await onboardTailscale(host, tailscale, baseSshOptions);\n if (result.success) {\n configured.push(\"tailscale\");\n } else {\n failed.push({ service: \"tailscale\", error: result.message });\n }\n }\n\n // Onboard Git\n if (git) {\n const result = await onboardGit(host, git, baseSshOptions);\n if (result.success) {\n configured.push(\"git\");\n } else {\n failed.push({ service: \"git\", error: result.message });\n }\n }\n\n // Onboard Claude Code (always install by default - core platform tool)\n {\n const result = await onboardClaude(host, claude || {}, baseSshOptions);\n if (result.success) {\n configured.push(\"claude\");\n } else {\n failed.push({ service: \"claude\", error: result.message });\n }\n }\n\n return {\n host,\n configured,\n failed,\n success: failed.length === 0,\n completedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Check onboarding status of a server\n *\n * @param host - Server IP or hostname\n * @param sshOptions - SSH options\n * @returns Onboarding status\n */\nexport async function checkOnboardingStatus(\n host: string,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<OnboardingStatus> {\n const user = \"root\";\n\n const [dopplerStatus, tailscaleStatus, gitStatus, claudeStatus] = await Promise.all([\n checkDopplerStatus(host, { user, ...sshOptions }),\n checkTailscaleStatus(host, { user, ...sshOptions }),\n checkGitStatus(host, { user, ...sshOptions }),\n checkClaudeStatus(host, { user, ...sshOptions }),\n ]);\n\n const services = {\n doppler: {\n service: \"doppler\",\n configured: dopplerStatus.configured,\n message: dopplerStatus.message,\n details: dopplerStatus.project\n ? { project: dopplerStatus.project, config: dopplerStatus.config }\n : undefined,\n } as const,\n tailscale: {\n service: \"tailscale\",\n configured: tailscaleStatus.configured,\n message: tailscaleStatus.message,\n details: tailscaleStatus.tailscaleIp\n ? { tailscaleIp: tailscaleStatus.tailscaleIp, hostname: tailscaleStatus.hostname }\n : undefined,\n } as const,\n git: {\n service: \"git\",\n configured: gitStatus.configured,\n message: gitStatus.message,\n details: gitStatus.username\n ? { username: gitStatus.username, email: gitStatus.email }\n : undefined,\n } as const,\n claude: {\n service: \"claude\",\n configured: claudeStatus.configured,\n message: claudeStatus.message,\n details: claudeStatus.version\n ? { version: claudeStatus.version, path: claudeStatus.path }\n : undefined,\n } as const,\n };\n\n const complete = services.doppler.configured && services.tailscale.configured && services.git.configured && services.claude.configured;\n\n return {\n host,\n services,\n complete,\n checkedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Onboard multiple servers in parallel\n *\n * @param hosts - List of server IPs/hostnames\n * @param sharedConfig - Shared onboarding config for all servers\n * @param sshOptions - SSH options\n * @returns Batch onboarding result\n */\nexport async function onboardBatch(\n hosts: string[],\n sharedConfig: Omit<OnboardingConfig, \"host\">,\n sshOptions: Partial<SSHOptions> = {}\n): Promise<BatchOnboardingResult> {\n // Onboard all servers in parallel\n const results = await Promise.all(\n hosts.map((host) =>\n onboardServer({ ...sharedConfig, host }, sshOptions)\n )\n );\n\n // Calculate summary\n const succeeded = results.filter((r) => r.success).length;\n const failed = results.filter((r) => !r.success).length;\n const partial = results.filter((r) => !r.success && r.configured.length > 0).length;\n\n return {\n results,\n summary: {\n total: results.length,\n succeeded,\n failed,\n partial,\n },\n };\n}\n\n/**\n * Quick onboarding check for multiple servers\n *\n * @param hosts - List of server IPs/hostnames\n * @param sshOptions - SSH options\n * @returns Map of host to onboarding status\n */\nexport async function checkBatchStatus(\n hosts: string[],\n sshOptions: Partial<SSHOptions> = {}\n): Promise<Map<string, OnboardingStatus>> {\n const statuses = await Promise.all(\n hosts.map(async (host) => {\n const status = await checkOnboardingStatus(host, sshOptions);\n return { host, status };\n })\n );\n\n return new Map(statuses.map(({ host, status }) => [host, status]));\n}\n"
10
+ ],
11
+ "mappings": "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",
12
+ "debugId": "948A1D2510F927B064756E2164756E21",
13
+ "names": []
14
+ }
package/package.json CHANGED
@@ -1,36 +1,64 @@
1
1
  {
2
2
  "name": "@ebowwa/hetzner",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Hetzner Cloud API client - servers, volumes, SSH keys, actions, pricing",
5
5
  "type": "module",
6
- "main": "./index.js",
7
- "types": "./index.ts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./index.ts",
11
- "default": "./index.ts"
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
12
  },
13
13
  "./client": {
14
- "types": "./client.ts",
15
- "default": "./client.ts"
14
+ "types": "./dist/client.d.ts",
15
+ "default": "./dist/client.js"
16
16
  },
17
17
  "./types": {
18
- "types": "./types.ts",
19
- "default": "./types.ts"
18
+ "types": "./dist/types.d.ts",
19
+ "default": "./dist/types.js"
20
20
  },
21
21
  "./schemas": {
22
- "types": "./schemas.ts",
23
- "default": "./schemas.ts"
22
+ "types": "./dist/schemas.d.ts",
23
+ "default": "./dist/schemas.js"
24
24
  },
25
25
  "./bootstrap": {
26
- "types": "./bootstrap/index.ts",
27
- "default": "./bootstrap/index.ts"
26
+ "types": "./dist/bootstrap/index.d.ts",
27
+ "default": "./dist/bootstrap/index.js"
28
28
  },
29
29
  "./onboarding": {
30
- "types": "./onboarding/index.ts",
31
- "default": "./onboarding/index.ts"
30
+ "types": "./dist/onboarding/index.d.ts",
31
+ "default": "./dist/onboarding/index.js"
32
+ },
33
+ "./servers": {
34
+ "types": "./dist/servers.d.ts",
35
+ "default": "./dist/servers.js"
36
+ },
37
+ "./volumes": {
38
+ "types": "./dist/volumes.d.ts",
39
+ "default": "./dist/volumes.js"
40
+ },
41
+ "./actions": {
42
+ "types": "./dist/actions.d.ts",
43
+ "default": "./dist/actions.js"
44
+ },
45
+ "./ssh-keys": {
46
+ "types": "./dist/ssh-keys.d.ts",
47
+ "default": "./dist/ssh-keys.js"
48
+ },
49
+ "./pricing": {
50
+ "types": "./dist/pricing.d.ts",
51
+ "default": "./dist/pricing.js"
52
+ },
53
+ "./errors": {
54
+ "types": "./dist/errors.d.ts",
55
+ "default": "./dist/errors.js"
32
56
  }
33
57
  },
58
+ "scripts": {
59
+ "build": "bun build src/index.ts --outdir dist --target node --external '@ebowwa/*' --external 'zod' && bun build src/bootstrap/index.ts --outdir dist/bootstrap --target node --external '@ebowwa/*' --external 'zod' && bun build src/onboarding/index.ts --outdir dist/onboarding --target node --external '@ebowwa/*' --external 'zod'",
60
+ "prepublishOnly": "bun run build"
61
+ },
34
62
  "keywords": [
35
63
  "hetzner",
36
64
  "cloud",
@@ -54,8 +82,17 @@
54
82
  "engines": {
55
83
  "node": ">=18.0.0"
56
84
  },
85
+ "files": [
86
+ "dist",
87
+ "README.md"
88
+ ],
57
89
  "dependencies": {
90
+ "@ebowwa/codespaces-types": "^1.4.0",
58
91
  "@ebowwa/terminal": "^0.3.0",
59
- "zod": "^4.3.6"
92
+ "zod": "^3.24.1"
93
+ },
94
+ "devDependencies": {
95
+ "@types/bun": "latest",
96
+ "typescript": "^5.9.3"
60
97
  }
61
98
  }