@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.
- package/README.md +731 -0
- package/dist/admin-auth.d.ts +1 -0
- package/dist/admin-auth.js +52 -0
- package/dist/admin-auth.js.map +1 -0
- package/dist/app.d.ts +182 -0
- package/dist/app.js +218 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3495 -0
- package/dist/cli.js.map +1 -0
- package/dist/compute-ssh.d.ts +246 -0
- package/dist/compute-ssh.js +577 -0
- package/dist/compute-ssh.js.map +1 -0
- package/dist/config.d.ts +46 -0
- package/dist/config.js +183 -0
- package/dist/config.js.map +1 -0
- package/dist/credential-store.d.ts +4 -0
- package/dist/credential-store.js +180 -0
- package/dist/credential-store.js.map +1 -0
- package/dist/mascot-data.d.ts +1 -0
- package/dist/mascot-data.js +14 -0
- package/dist/mascot-data.js.map +1 -0
- package/dist/pay.d.ts +60 -0
- package/dist/pay.js +483 -0
- package/dist/pay.js.map +1 -0
- package/dist/sdk.d.ts +259 -0
- package/dist/sdk.js +944 -0
- package/dist/sdk.js.map +1 -0
- package/dist/social-queue.d.ts +125 -0
- package/dist/social-queue.js +340 -0
- package/dist/social-queue.js.map +1 -0
- package/dist/social-vault.d.ts +118 -0
- package/dist/social-vault.js +268 -0
- package/dist/social-vault.js.map +1 -0
- package/dist/social-worker.d.ts +43 -0
- package/dist/social-worker.js +155 -0
- package/dist/social-worker.js.map +1 -0
- package/dist/totp.d.ts +2 -0
- package/dist/totp.js +46 -0
- package/dist/totp.js.map +1 -0
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +441 -0
- package/dist/ui.js.map +1 -0
- package/dist/vault.d.ts +65 -0
- package/dist/vault.js +455 -0
- package/dist/vault.js.map +1 -0
- 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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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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;
|