@palmyr/cli 1.0.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 (47) hide show
  1. package/README.md +731 -0
  2. package/dist/admin-auth.d.ts +1 -0
  3. package/dist/admin-auth.js +52 -0
  4. package/dist/admin-auth.js.map +1 -0
  5. package/dist/app.d.ts +182 -0
  6. package/dist/app.js +218 -0
  7. package/dist/app.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +3495 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/compute-ssh.d.ts +246 -0
  12. package/dist/compute-ssh.js +577 -0
  13. package/dist/compute-ssh.js.map +1 -0
  14. package/dist/config.d.ts +46 -0
  15. package/dist/config.js +183 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/credential-store.d.ts +4 -0
  18. package/dist/credential-store.js +180 -0
  19. package/dist/credential-store.js.map +1 -0
  20. package/dist/mascot-data.d.ts +1 -0
  21. package/dist/mascot-data.js +14 -0
  22. package/dist/mascot-data.js.map +1 -0
  23. package/dist/pay.d.ts +60 -0
  24. package/dist/pay.js +483 -0
  25. package/dist/pay.js.map +1 -0
  26. package/dist/sdk.d.ts +259 -0
  27. package/dist/sdk.js +944 -0
  28. package/dist/sdk.js.map +1 -0
  29. package/dist/social-queue.d.ts +125 -0
  30. package/dist/social-queue.js +340 -0
  31. package/dist/social-queue.js.map +1 -0
  32. package/dist/social-vault.d.ts +118 -0
  33. package/dist/social-vault.js +268 -0
  34. package/dist/social-vault.js.map +1 -0
  35. package/dist/social-worker.d.ts +43 -0
  36. package/dist/social-worker.js +155 -0
  37. package/dist/social-worker.js.map +1 -0
  38. package/dist/totp.d.ts +2 -0
  39. package/dist/totp.js +46 -0
  40. package/dist/totp.js.map +1 -0
  41. package/dist/ui.d.ts +77 -0
  42. package/dist/ui.js +441 -0
  43. package/dist/ui.js.map +1 -0
  44. package/dist/vault.d.ts +65 -0
  45. package/dist/vault.js +455 -0
  46. package/dist/vault.js.map +1 -0
  47. package/package.json +75 -0
@@ -0,0 +1,577 @@
1
+ /**
2
+ * SSH-key helpers for the `compute` command group.
3
+ *
4
+ * Three responsibilities:
5
+ * 1. Local keypair generation (`generateKeypair`) — shells out to `ssh-keygen`
6
+ * so we don't have to ship our own OpenSSH-format encoder. Saved under
7
+ * `~/.palmyr/ssh/<server-id>/id_ed25519{,.pub}` with chmod 600.
8
+ * 2. Local server cache (`saveDeployedServer` / `findCachedServer`) — lets
9
+ * `palmyr compute ssh <name>` resolve a friendly name → IP without a
10
+ * paid `GET /compute/servers` round-trip.
11
+ * 3. Wait-for-running (`waitForRunning`) — polls the server's status with
12
+ * backoff until Hetzner reports `running` or the deadline trips.
13
+ */
14
+ import { spawnSync } from 'child_process';
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'fs';
16
+ import { homedir } from 'os';
17
+ import { join, dirname } from 'path';
18
+ import { createConnection } from 'net';
19
+ const PALMYR_DIR = join(homedir(), '.palmyr');
20
+ const SSH_DIR = join(PALMYR_DIR, 'ssh');
21
+ const SERVER_CACHE = join(PALMYR_DIR, 'data', 'servers.json');
22
+ function ensureDir(p) {
23
+ if (!existsSync(p))
24
+ mkdirSync(p, { recursive: true });
25
+ }
26
+ /**
27
+ * Confirm `ssh-keygen` is on PATH before we promise a keypair to the caller.
28
+ * On Windows it's bundled with Git for Windows / OpenSSH client; on macOS and
29
+ * most Linux distros it's installed by default. We prefer this over rolling
30
+ * our own ed25519 OpenSSH-format encoder because the file format
31
+ * (`-----BEGIN OPENSSH PRIVATE KEY-----`, base64-wrapped, encrypted by KDF)
32
+ * is finicky to produce correctly without bugs.
33
+ */
34
+ export function hasSshKeygen() {
35
+ try {
36
+ const r = spawnSync('ssh-keygen', ['-V'], { stdio: 'pipe' });
37
+ // ssh-keygen prints help to stderr and exits non-zero on -V, but the
38
+ // command is found if spawn didn't ENOENT.
39
+ return r.error == null;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
45
+ /**
46
+ * Generate a fresh ed25519 keypair in `~/.palmyr/ssh/<serverId>/`. The label
47
+ * (typically the Hetzner server id, but any safe slug works) namespaces the
48
+ * key so multiple deploys don't trample each other. Caller is responsible for
49
+ * passing the resulting `publicKey` string to the deploy endpoint.
50
+ *
51
+ * Throws if `ssh-keygen` isn't on PATH or if the key already exists at the
52
+ * destination — refusing to overwrite is intentional, the user should delete
53
+ * the old key explicitly if they want a clean slate.
54
+ */
55
+ export function generateKeypair(label) {
56
+ if (!hasSshKeygen()) {
57
+ throw new Error("ssh-keygen not on PATH. Install OpenSSH client (Windows: 'Add Optional Feature → OpenSSH Client', macOS: bundled, Linux: 'apt install openssh-client') or pass --pubkey-file to deploy with an existing key.");
58
+ }
59
+ if (!/^[A-Za-z0-9._-]{1,64}$/.test(label)) {
60
+ throw new Error('keypair label must be 1–64 chars of [A-Za-z0-9._-]');
61
+ }
62
+ const dir = join(SSH_DIR, label);
63
+ ensureDir(dir);
64
+ const privateKeyPath = join(dir, 'id_ed25519');
65
+ const publicKeyPath = privateKeyPath + '.pub';
66
+ if (existsSync(privateKeyPath)) {
67
+ throw new Error(`Key already exists at ${privateKeyPath}. Delete it first or pass --pubkey-file ${publicKeyPath} to reuse it.`);
68
+ }
69
+ const comment = `palmyr-${label}`;
70
+ // -N "" is empty passphrase. -q suppresses ssh-keygen's banner.
71
+ // We pass the path with no shell metachars (label already validated).
72
+ const r = spawnSync('ssh-keygen', ['-t', 'ed25519', '-N', '', '-q', '-f', privateKeyPath, '-C', comment], { stdio: 'pipe' });
73
+ if (r.status !== 0) {
74
+ const errText = (r.stderr?.toString() || r.stdout?.toString() || 'ssh-keygen failed').trim();
75
+ throw new Error(`ssh-keygen failed: ${errText}`);
76
+ }
77
+ // ssh-keygen sets 600 on Unix already, but Windows NTFS doesn't honor that.
78
+ // chmod is a no-op on Windows but harmless.
79
+ try {
80
+ chmodSync(privateKeyPath, 0o600);
81
+ }
82
+ catch { }
83
+ const publicKey = readFileSync(publicKeyPath, 'utf8').trim();
84
+ return { privateKeyPath, publicKeyPath, publicKey, comment };
85
+ }
86
+ function readCache() {
87
+ if (!existsSync(SERVER_CACHE))
88
+ return [];
89
+ try {
90
+ const data = JSON.parse(readFileSync(SERVER_CACHE, 'utf8'));
91
+ return Array.isArray(data) ? data : [];
92
+ }
93
+ catch {
94
+ return [];
95
+ }
96
+ }
97
+ function writeCache(entries) {
98
+ ensureDir(dirname(SERVER_CACHE));
99
+ writeFileSync(SERVER_CACHE, JSON.stringify(entries, null, 2));
100
+ }
101
+ export function saveDeployedServer(s) {
102
+ const all = readCache().filter(e => e.id !== s.id && e.name !== s.name);
103
+ all.push(s);
104
+ writeCache(all);
105
+ }
106
+ export function removeCachedServer(idOrName) {
107
+ const filtered = readCache().filter(e => e.id !== idOrName && e.name !== idOrName);
108
+ writeCache(filtered);
109
+ }
110
+ /**
111
+ * Resolve a user-supplied identifier (name or id) against the local cache.
112
+ * Returns the first matching entry, or null if nothing matches. Caller can
113
+ * fall back to a server-side `GET /compute/servers` lookup on null.
114
+ */
115
+ export function findCachedServer(idOrName) {
116
+ const norm = idOrName.trim();
117
+ if (!norm)
118
+ return null;
119
+ const all = readCache();
120
+ // Exact id match wins over name match — ids are globally unique, names aren't.
121
+ return all.find(e => e.id === norm) || all.find(e => e.name === norm) || null;
122
+ }
123
+ export function listCachedServers() {
124
+ return readCache();
125
+ }
126
+ /**
127
+ * Spawn `ssh root@<ip>` with stdio inherited so the user's interactive
128
+ * session takes over the parent terminal. The optional `keyPath` is appended
129
+ * as `-i <path>` only if the file actually exists — saves a confusing "no
130
+ * such identity" error when the cached path is stale.
131
+ *
132
+ * Returns the ssh exit code (or non-zero on spawn failure). Caller is
133
+ * expected to `process.exit(returnCode)` so shell pipelines see the right
134
+ * status. Non-TTY callers shouldn't reach here — they should print the
135
+ * command instead via `buildSshCommand`.
136
+ */
137
+ export function spawnInteractiveSsh(ip, keyPath) {
138
+ const args = [];
139
+ if (keyPath && existsSync(keyPath))
140
+ args.push('-i', keyPath);
141
+ args.push('-o', 'StrictHostKeyChecking=accept-new', '-o', 'UserKnownHostsFile=' + join(PALMYR_DIR, 'ssh', 'known_hosts'), `root@${ip}`);
142
+ ensureDir(join(PALMYR_DIR, 'ssh'));
143
+ const r = spawnSync('ssh', args, { stdio: 'inherit' });
144
+ if (r.error) {
145
+ process.stderr.write(`ssh spawn failed: ${r.error.message}\n`);
146
+ return 127;
147
+ }
148
+ return r.status ?? 1;
149
+ }
150
+ /**
151
+ * Build the equivalent shell command without running it. Used in agent mode
152
+ * so the caller (an automated runner) can shell out to ssh themselves.
153
+ */
154
+ export function buildSshCommand(ip, keyPath) {
155
+ const parts = ['ssh'];
156
+ if (keyPath && existsSync(keyPath))
157
+ parts.push('-i', shellQuote(keyPath));
158
+ parts.push(`root@${ip}`);
159
+ return parts.join(' ');
160
+ }
161
+ function shellQuote(s) {
162
+ // Simple POSIX quoting — wrap in single quotes, escape any embedded ones.
163
+ // Good enough for path-like inputs we control; not for arbitrary user data.
164
+ if (/^[A-Za-z0-9._/:\\-]+$/.test(s))
165
+ return s;
166
+ return "'" + s.replace(/'/g, "'\\''") + "'";
167
+ }
168
+ /**
169
+ * Poll `getStatus()` until it returns `running` or the deadline trips. Backs
170
+ * off from 1s → 5s so we don't hammer the API while a fresh box is doing its
171
+ * first-boot work (Hetzner servers usually go `initializing` → `running` in
172
+ * 30–60s). Returns the final status object or throws on timeout.
173
+ */
174
+ export async function waitForRunning(getStatus, opts = {}) {
175
+ const deadline = Date.now() + (opts.timeoutMs ?? 120_000);
176
+ let delay = 1_000;
177
+ while (Date.now() < deadline) {
178
+ const s = await getStatus().catch(() => null);
179
+ if (s && s.status === 'running' && s.ipv4)
180
+ return s;
181
+ await new Promise(r => setTimeout(r, delay));
182
+ delay = Math.min(delay * 1.4, 5_000);
183
+ }
184
+ throw new Error(`Server did not reach 'running' state within ${opts.timeoutMs ?? 120_000}ms`);
185
+ }
186
+ /**
187
+ * TCP-probe `host:port` once with a hard timeout. Resolves true on connect,
188
+ * false on RST/refused/timeout. Used as the second readiness gate after
189
+ * Hetzner reports `running` — Hetzner's status flips before the OS finishes
190
+ * booting and sshd binds, so we need an independent signal that port 22 is
191
+ * actually listening.
192
+ */
193
+ export function tcpProbe(host, port, timeoutMs = 3000) {
194
+ return new Promise(resolve => {
195
+ const sock = createConnection({ host, port });
196
+ let done = false;
197
+ const finish = (ok) => {
198
+ if (done)
199
+ return;
200
+ done = true;
201
+ try {
202
+ sock.destroy();
203
+ }
204
+ catch { }
205
+ resolve(ok);
206
+ };
207
+ sock.setTimeout(timeoutMs);
208
+ sock.once('connect', () => finish(true));
209
+ sock.once('timeout', () => finish(false));
210
+ sock.once('error', () => finish(false));
211
+ });
212
+ }
213
+ /**
214
+ * Probe an SSH endpoint with the user's key, asking sshd to run `true` and
215
+ * exit. Confirms three things at once: sshd is up, the key is in
216
+ * authorized_keys, and the user can actually open a session. We pass
217
+ * `BatchMode=yes` so ssh refuses to fall back to password prompts (which
218
+ * would hang in a non-interactive runner).
219
+ *
220
+ * Returns true on exit code 0; false on anything else (connection refused,
221
+ * permission denied, host key mismatch, timeout). The caller can decide
222
+ * whether to retry — most "fresh box" failures resolve in 10–30s as
223
+ * cloud-init finishes touching authorized_keys.
224
+ */
225
+ export function sshProbe(ip, keyPath, timeoutSec = 5) {
226
+ if (!existsSync(keyPath))
227
+ return false;
228
+ // -q + -T: quiet, no PTY (issue #85 — see sshRun).
229
+ //
230
+ // UserKnownHostsFile=/dev/null + StrictHostKeyChecking=no: deploy/wait
231
+ // probes are NOT security-critical. We're verifying "does this newly-
232
+ // provisioned VPS accept our key" — not authenticating a long-lived
233
+ // host. Hetzner reuses public IPs across short-lived test boxes, and a
234
+ // persistent known_hosts file accumulates stale entries that trigger
235
+ // host-key-mismatch failures (user report 2026-05-05: same IP reused →
236
+ // SSH gate hung for the full --wait-timeout). Discarding host keys
237
+ // matches the platform-side `sshCmd` and removes the entire class of
238
+ // failure. The interactive `compute ssh` command — which is security-
239
+ // relevant — keeps host-key pinning via spawnInteractiveSsh.
240
+ const r = spawnSync('ssh', [
241
+ '-i', keyPath,
242
+ '-q',
243
+ '-T',
244
+ '-o', 'BatchMode=yes',
245
+ '-o', `ConnectTimeout=${timeoutSec}`,
246
+ '-o', 'StrictHostKeyChecking=no',
247
+ '-o', 'UserKnownHostsFile=/dev/null',
248
+ `root@${ip}`,
249
+ 'true',
250
+ ], { stdio: 'pipe', timeout: (timeoutSec + 2) * 1000 });
251
+ return r.status === 0;
252
+ }
253
+ /**
254
+ * Run a single command on the server via SSH using the platform/user key
255
+ * supplied to gate 3 + 4. Returns {stdout, status} so callers can branch on
256
+ * exit code rather than just trim/parse stdout.
257
+ *
258
+ * Used by the install-status probe, the cloud-init status probe, and the
259
+ * diagnostic-fetcher that runs on readiness failure. None of those need
260
+ * structured stderr handling, so we redirect 2>&1 to keep a single buffer.
261
+ */
262
+ function sshRun(ip, keyPath, command, timeoutSec = 10) {
263
+ if (!existsSync(keyPath))
264
+ return { stdout: '', status: 127 };
265
+ // -q + -T: quiet, no PTY (issue #85).
266
+ // UserKnownHostsFile=/dev/null + StrictHostKeyChecking=no: not security-
267
+ // critical (we're polling /etc/palmyr/install-status.json on a server
268
+ // we just deployed). See sshProbe for full rationale.
269
+ const r = spawnSync('ssh', [
270
+ '-i', keyPath,
271
+ '-q',
272
+ '-T',
273
+ '-o', 'BatchMode=yes',
274
+ '-o', `ConnectTimeout=${Math.min(15, timeoutSec)}`,
275
+ '-o', 'StrictHostKeyChecking=no',
276
+ '-o', 'UserKnownHostsFile=/dev/null',
277
+ `root@${ip}`,
278
+ command,
279
+ ], { stdio: 'pipe', timeout: (timeoutSec + 2) * 1000, encoding: 'utf-8', maxBuffer: 1024 * 1024 });
280
+ return {
281
+ stdout: (r.stdout ?? '').toString(),
282
+ status: r.status === null ? 124 : r.status,
283
+ };
284
+ }
285
+ /**
286
+ * Read /etc/palmyr/install-status.json from the server via SSH. Returns the
287
+ * parsed object on success, null if the file doesn't exist yet (cloud-init
288
+ * still running) or can't be parsed. Used as gate 4 of the readiness chain
289
+ * when a deploy requested install recipes.
290
+ */
291
+ export function readInstallStatus(ip, keyPath, timeoutSec = 8) {
292
+ // 2>/dev/null suppresses the "no such file" error so the missing-file
293
+ // case is just an empty stdout, not a parse failure with stderr noise.
294
+ const r = sshRun(ip, keyPath, 'cat /etc/palmyr/install-status.json 2>/dev/null', timeoutSec);
295
+ if (r.status !== 0)
296
+ return null;
297
+ const out = r.stdout.trim();
298
+ if (!out)
299
+ return null;
300
+ try {
301
+ const parsed = JSON.parse(out);
302
+ if (parsed && typeof parsed === 'object' && parsed.status === 'ok' && Array.isArray(parsed.installs)) {
303
+ return parsed;
304
+ }
305
+ }
306
+ catch { }
307
+ return null;
308
+ }
309
+ /**
310
+ * Probe `cloud-init status` so the readiness chain can fail fast when the
311
+ * user_data script has clearly aborted. Returns the parsed status string
312
+ * (`done`, `running`, `error`, `disabled`, etc.) or null when the binary
313
+ * isn't reachable yet.
314
+ *
315
+ * Polled in gate 4 — if it returns 'error' we break out immediately and
316
+ * surface diagnostics, instead of waiting the full 600s install timeout
317
+ * for a marker file that's never going to be written.
318
+ */
319
+ export function readCloudInitStatus(ip, keyPath, timeoutSec = 8) {
320
+ const r = sshRun(ip, keyPath, 'cloud-init status 2>/dev/null', timeoutSec);
321
+ if (r.status !== 0)
322
+ return null;
323
+ // cloud-init status output: "status: done" / "status: error" / etc.
324
+ const m = r.stdout.match(/status:\s*(\S+)/);
325
+ return m ? m[1] : null;
326
+ }
327
+ /**
328
+ * On readiness failure, SSH in (best effort) and grab a small bundle of
329
+ * diagnostic data so the JSON response includes a clear cause without the
330
+ * caller needing to ssh in by hand:
331
+ *
332
+ * - cloud-init status (--long if available)
333
+ * - tail of /var/log/cloud-init-output.log
334
+ * - tail of /var/log/palmyr/cloud-init.log (the wrapper's own tee)
335
+ * - tail of any /var/log/palmyr/<recipe>-install.log files
336
+ *
337
+ * Tails are capped to keep response bodies sane. If SSH is unreachable
338
+ * (gate 2 or 3 tripped), returns a short note instead of throwing.
339
+ */
340
+ export function fetchDeployDiagnostics(ip, keyPath) {
341
+ const result = {
342
+ cloudInitStatus: null,
343
+ cloudInitLogTail: null,
344
+ palmyrLogTail: null,
345
+ recipeLogs: [],
346
+ };
347
+ if (!existsSync(keyPath))
348
+ return result;
349
+ // cloud-init status --long has more detail than plain status (errors[],
350
+ // boot_status_code, etc.) but is also Ubuntu-version-dependent. Fall back
351
+ // to plain status if --long isn't supported.
352
+ const status = sshRun(ip, keyPath, 'cloud-init status --long 2>/dev/null || cloud-init status 2>/dev/null', 10);
353
+ if (status.status === 0 && status.stdout.trim()) {
354
+ result.cloudInitStatus = status.stdout.trim().split('\n').slice(0, 40).join('\n');
355
+ }
356
+ const ciLog = sshRun(ip, keyPath, 'tail -120 /var/log/cloud-init-output.log 2>/dev/null', 15);
357
+ if (ciLog.status === 0 && ciLog.stdout.trim()) {
358
+ result.cloudInitLogTail = ciLog.stdout;
359
+ }
360
+ const palmyrLog = sshRun(ip, keyPath, 'tail -80 /var/log/palmyr/cloud-init.log 2>/dev/null', 15);
361
+ if (palmyrLog.status === 0 && palmyrLog.stdout.trim()) {
362
+ result.palmyrLogTail = palmyrLog.stdout;
363
+ }
364
+ // List per-recipe install logs and tail the last one each. We use ls + a
365
+ // capped find so we don't accidentally pull megabytes from a runaway log.
366
+ const list = sshRun(ip, keyPath, 'ls /var/log/palmyr/*-install.log 2>/dev/null', 10);
367
+ if (list.status === 0) {
368
+ const files = list.stdout.split('\n').map(s => s.trim()).filter(Boolean).slice(0, 8);
369
+ for (const file of files) {
370
+ // Path is from `ls`, not user input, so safe to splice.
371
+ const r = sshRun(ip, keyPath, `tail -80 ${file}`, 15);
372
+ if (r.status === 0) {
373
+ const m = file.match(/\/([^/]+)-install\.log$/);
374
+ result.recipeLogs.push({ name: m ? m[1] : file, tail: r.stdout });
375
+ }
376
+ }
377
+ }
378
+ return result;
379
+ }
380
+ /**
381
+ * Four-gate readiness chain used by `compute deploy --wait` and `compute
382
+ * wait <id>`:
383
+ *
384
+ * 1. Hetzner status flips to `running`.
385
+ * 2. TCP port 22 accepts connections.
386
+ * 3. (Optional) `ssh -i <key> root@<ip> 'true'` exits 0.
387
+ * 4. (Optional) /etc/palmyr/install-status.json exists with `status: ok`
388
+ * and contains every recipe in `expectedInstalls`. Skipped when no
389
+ * installs were requested at deploy time, OR when we don't have a
390
+ * private key path to SSH in with.
391
+ *
392
+ * Gate 3 is skipped when `keyPath` is undefined or the file doesn't exist —
393
+ * e.g. user deployed with `--ssh-key <hetzner-id>` and we don't have the
394
+ * private key locally. They get gates 1+2 and a documented "ssh: skipped"
395
+ * marker so they know the credential check wasn't done.
396
+ *
397
+ * The function shares one wall-clock budget across all four gates so a slow
398
+ * Hetzner provisioning step doesn't steal time from the install poll. Default
399
+ * 240s is enough for the SSH gates with no install; bump via `timeoutMs` (the
400
+ * CLI defaults to 600s when an install is requested, since Hermes pulls
401
+ * Python 3.11 + several hundred MB of pip packages).
402
+ */
403
+ export async function waitForReady(opts) {
404
+ const startedAt = Date.now();
405
+ const deadline = startedAt + (opts.timeoutMs ?? 240_000);
406
+ const checks = {
407
+ hetznerStatus: 'fail',
408
+ port22: 'skipped',
409
+ ssh: 'skipped',
410
+ };
411
+ let ip = null;
412
+ let status = null;
413
+ // Gate 1: Hetzner status → running
414
+ opts.onProgress?.({ stage: 'status', message: 'Waiting for Hetzner status=running…' });
415
+ let delay = 1_000;
416
+ while (Date.now() < deadline) {
417
+ const s = await opts.getStatus().catch(() => null);
418
+ if (s) {
419
+ status = s.status;
420
+ ip = s.ipv4;
421
+ if (s.status === 'running' && s.ipv4) {
422
+ checks.hetznerStatus = 'pass';
423
+ break;
424
+ }
425
+ }
426
+ await new Promise(r => setTimeout(r, delay));
427
+ delay = Math.min(delay * 1.4, 5_000);
428
+ }
429
+ if (checks.hetznerStatus !== 'pass') {
430
+ checks.hetznerStatus = 'timeout';
431
+ return {
432
+ ready: false,
433
+ ip,
434
+ status,
435
+ checks,
436
+ elapsedMs: Date.now() - startedAt,
437
+ reason: `Hetzner status did not reach 'running' (last seen: ${status ?? 'unknown'})`,
438
+ };
439
+ }
440
+ // Gate 2: TCP port 22
441
+ opts.onProgress?.({ stage: 'port22', message: 'Probing port 22…' });
442
+ checks.port22 = 'fail';
443
+ let port22Open = false;
444
+ delay = 1_000;
445
+ while (Date.now() < deadline) {
446
+ if (await tcpProbe(ip, 22, 3_000)) {
447
+ checks.port22 = 'pass';
448
+ port22Open = true;
449
+ break;
450
+ }
451
+ await new Promise(r => setTimeout(r, delay));
452
+ delay = Math.min(delay * 1.4, 5_000);
453
+ }
454
+ if (!port22Open) {
455
+ checks.port22 = 'timeout';
456
+ return {
457
+ ready: false,
458
+ ip,
459
+ status,
460
+ checks,
461
+ elapsedMs: Date.now() - startedAt,
462
+ reason: `Server is running but port 22 did not open within the timeout`,
463
+ };
464
+ }
465
+ // Gate 3: SSH credential probe (optional)
466
+ const skipReasons = {};
467
+ if (opts.keyPath && existsSync(opts.keyPath)) {
468
+ opts.onProgress?.({ stage: 'ssh', message: `Verifying SSH login with ${opts.keyPath}…` });
469
+ checks.ssh = 'fail';
470
+ let sshOk = false;
471
+ delay = 2_000;
472
+ while (Date.now() < deadline) {
473
+ if (sshProbe(ip, opts.keyPath, 5)) {
474
+ checks.ssh = 'pass';
475
+ sshOk = true;
476
+ break;
477
+ }
478
+ await new Promise(r => setTimeout(r, delay));
479
+ delay = Math.min(delay * 1.4, 5_000);
480
+ }
481
+ if (!sshOk) {
482
+ checks.ssh = 'timeout';
483
+ return {
484
+ ready: false,
485
+ ip,
486
+ status,
487
+ checks,
488
+ elapsedMs: Date.now() - startedAt,
489
+ reason: `Port 22 is open but ssh -i ${opts.keyPath} root@${ip} did not authenticate. Check that the public half is in authorized_keys (cloud-init may still be running).`,
490
+ };
491
+ }
492
+ }
493
+ else {
494
+ checks.ssh = 'skipped';
495
+ skipReasons.ssh = opts.keyPath
496
+ ? `Private key not found at ${opts.keyPath}.`
497
+ : 'No local SSH key path supplied. Pass --key <path> to enable the SSH credential probe, or run from the host where the key was generated.';
498
+ }
499
+ // Gate 4: Install marker (optional). Cloud-init writes
500
+ // /etc/palmyr/install-status.json once every requested recipe finishes.
501
+ // We poll it via SSH every 5–8s, AND probe `cloud-init status` so we can
502
+ // fail fast when scripts_user has aborted (no point polling for a marker
503
+ // file that's never going to land).
504
+ let installStatus;
505
+ if (opts.expectedInstalls && opts.expectedInstalls.length > 0) {
506
+ if (opts.keyPath && existsSync(opts.keyPath)) {
507
+ checks.installs = 'fail';
508
+ opts.onProgress?.({ stage: 'installs', message: `Waiting for installs to finish: ${opts.expectedInstalls.join(', ')}…` });
509
+ let installOk = false;
510
+ let cloudInitErrored = false;
511
+ delay = 5_000;
512
+ let pollIter = 0;
513
+ while (Date.now() < deadline) {
514
+ const s = readInstallStatus(ip, opts.keyPath, 8);
515
+ if (s) {
516
+ // Confirm every requested recipe shows up in the marker. The server
517
+ // writes them in install order, so set-equality is sufficient.
518
+ const have = new Set(s.installs);
519
+ const missing = opts.expectedInstalls.filter(r => !have.has(r));
520
+ if (missing.length === 0) {
521
+ checks.installs = 'pass';
522
+ installStatus = s;
523
+ installOk = true;
524
+ break;
525
+ }
526
+ }
527
+ // Every 3rd iteration, probe cloud-init status. Don't probe every
528
+ // tick — it's an extra SSH roundtrip and cloud-init's status file
529
+ // doesn't update that often anyway. 3 polls × ~6s = ~18s cadence.
530
+ pollIter++;
531
+ if (pollIter % 3 === 0) {
532
+ const ciStatus = readCloudInitStatus(ip, opts.keyPath, 8);
533
+ if (ciStatus === 'error') {
534
+ cloudInitErrored = true;
535
+ break;
536
+ }
537
+ }
538
+ await new Promise(r => setTimeout(r, delay));
539
+ delay = Math.min(delay * 1.2, 8_000);
540
+ }
541
+ if (!installOk) {
542
+ checks.installs = cloudInitErrored ? 'fail' : 'timeout';
543
+ const diagnostics = fetchDeployDiagnostics(ip, opts.keyPath);
544
+ return {
545
+ ready: false,
546
+ ip,
547
+ status,
548
+ checks,
549
+ elapsedMs: Date.now() - startedAt,
550
+ reason: cloudInitErrored
551
+ ? `cloud-init aborted (status: error). The user_data script failed before writing the install marker. See diagnostics.cloudInitLogTail for the failure.`
552
+ : `Install did not complete within ${Math.round((opts.timeoutMs ?? 240_000) / 1000)}s. Recipes requested: ${opts.expectedInstalls.join(', ')}. See diagnostics for cloud-init + recipe logs.`,
553
+ installStatus,
554
+ diagnostics,
555
+ };
556
+ }
557
+ }
558
+ else {
559
+ // Install was requested but we have no local key to SSH in with — can't
560
+ // poll the marker. Mark skipped so the caller knows the gate didn't run.
561
+ checks.installs = 'skipped';
562
+ skipReasons.installs = opts.keyPath
563
+ ? `Private key not found at ${opts.keyPath}; can't read /etc/palmyr/install-status.json.`
564
+ : 'No local SSH key path supplied; can\'t read /etc/palmyr/install-status.json. Pass --key <path>, or check the marker manually: palmyr compute exec <id> -- cat /etc/palmyr/install-status.json';
565
+ }
566
+ }
567
+ return {
568
+ ready: true,
569
+ ip,
570
+ status,
571
+ checks,
572
+ ...(Object.keys(skipReasons).length > 0 ? { skipReasons } : {}),
573
+ elapsedMs: Date.now() - startedAt,
574
+ installStatus,
575
+ };
576
+ }
577
+ //# sourceMappingURL=compute-ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-ssh.js","sourceRoot":"","sources":["../compute-ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Palmyr local config + data management
3
+ * Everything lives in ~/.palmyr/
4
+ */
5
+ export declare function ensureDirs(): void;
6
+ export interface WalletConfig {
7
+ keyfile?: string;
8
+ vaultWalletId?: string;
9
+ }
10
+ export interface PalmyrConfig {
11
+ api: string;
12
+ wallets: {
13
+ solana?: WalletConfig;
14
+ base?: WalletConfig;
15
+ };
16
+ defaultChain: 'solana' | 'base';
17
+ setupDone?: boolean;
18
+ vaultEnabled?: boolean;
19
+ apiKey?: string;
20
+ defaultPayWalletId?: string;
21
+ defaultPayChain?: 'solana' | 'base';
22
+ chain?: string;
23
+ keyfile?: string;
24
+ }
25
+ export declare function loadConfig(): PalmyrConfig;
26
+ export declare function saveConfig(config: PalmyrConfig): void;
27
+ export declare function addWalletToConfig(chain: 'solana' | 'base', keyfile: string): void;
28
+ export declare function getKeyfile(chain?: 'solana' | 'base'): string | null;
29
+ export declare function loadKeypair(chain?: 'solana' | 'base'): Uint8Array | null;
30
+ export declare function getConfiguredChains(): string[];
31
+ export declare function getData(file: string): any;
32
+ export declare function setData(file: string, data: any): void;
33
+ export declare function getPhones(): any[];
34
+ export declare function addPhone(phone: any): void;
35
+ export declare function getInboxes(): any[];
36
+ export declare function addInbox(inbox: any): void;
37
+ export declare function getServers(): any[];
38
+ export declare function addServer(server: any): void;
39
+ export declare function getDomains(): any[];
40
+ export declare function addDomain(domain: any): void;
41
+ export declare function getAccounts(): any[];
42
+ export declare function addAccount(account: any): void;
43
+ export declare function saveDraft(id: string, draft: any): void;
44
+ export declare function getDraft(id: string): any;
45
+ export declare function addNote(note: string): void;
46
+ export declare function log(message: string): void;