vibelet 1.0.10 → 1.0.11
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/bin/vibelet.mjs +86 -6
- package/dist/index.cjs +47 -45
- package/package.json +1 -19
package/bin/vibelet.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn, spawnSync } from 'node:child_process';
|
|
4
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, openSync, writeSync } from 'node:fs';
|
|
4
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, openSync, writeSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { basename, delimiter, dirname, join, resolve } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import QRCode from 'qrcode';
|
|
9
9
|
import { extractQuickTunnelUrl } from './cloudflared-quick-tunnel.mjs';
|
|
@@ -218,6 +218,75 @@ function isProcessAlive(pid) {
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// ─── Service PATH helpers ──────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
// Transient shims like fnm_multishells/<pid>/bin disappear or get GC'd after the
|
|
224
|
+
// shell that created them exits. Long-lived services (launchd, systemd) that
|
|
225
|
+
// inherit these paths end up spawning wrapper scripts whose `exec node` fails
|
|
226
|
+
// with "node: not found". Stabilize to the underlying stable dir/file.
|
|
227
|
+
function isTransientNodePath(p) {
|
|
228
|
+
return typeof p === 'string' && p.includes('fnm_multishells');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function stabilizeServiceFile(p) {
|
|
232
|
+
if (!p || !isTransientNodePath(p)) return p;
|
|
233
|
+
try {
|
|
234
|
+
const stable = join(realpathSync(dirname(p)), basename(p));
|
|
235
|
+
if (existsSync(stable) && !isTransientNodePath(stable)) return stable;
|
|
236
|
+
} catch { /* broken symlink */ }
|
|
237
|
+
return p;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function stabilizeServiceDir(dir) {
|
|
241
|
+
if (!dir || !isTransientNodePath(dir)) return dir;
|
|
242
|
+
try {
|
|
243
|
+
const real = realpathSync(dir);
|
|
244
|
+
if (real && !isTransientNodePath(real)) return real;
|
|
245
|
+
} catch { /* broken symlink */ }
|
|
246
|
+
return dir;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Build a PATH the daemon can rely on under launchd/systemd, where no login
|
|
250
|
+
// shell ran and no rc files loaded. Puts the node bin dir first so wrapper
|
|
251
|
+
// scripts spawned by the daemon (codex, claude) can resolve `node` via PATH,
|
|
252
|
+
// then falls back to common system and user-tool locations.
|
|
253
|
+
function buildServicePath(nodeBinDir) {
|
|
254
|
+
const home = homedir();
|
|
255
|
+
const pnpmHome = process.env.PNPM_HOME
|
|
256
|
+
|| (process.platform === 'win32' ? join(home, 'pnpm') : join(home, 'Library', 'pnpm'));
|
|
257
|
+
const baseline = process.platform === 'win32'
|
|
258
|
+
? [
|
|
259
|
+
nodeBinDir,
|
|
260
|
+
join(home, '.npm-global', 'bin'),
|
|
261
|
+
join(home, '.local', 'bin'),
|
|
262
|
+
join(home, '.claude', 'bin'),
|
|
263
|
+
pnpmHome,
|
|
264
|
+
]
|
|
265
|
+
: [
|
|
266
|
+
nodeBinDir,
|
|
267
|
+
'/usr/local/bin',
|
|
268
|
+
'/opt/homebrew/bin',
|
|
269
|
+
'/usr/bin',
|
|
270
|
+
'/bin',
|
|
271
|
+
'/usr/sbin',
|
|
272
|
+
'/sbin',
|
|
273
|
+
join(home, '.npm-global', 'bin'),
|
|
274
|
+
join(home, '.local', 'bin'),
|
|
275
|
+
join(home, '.claude', 'bin'),
|
|
276
|
+
pnpmHome,
|
|
277
|
+
];
|
|
278
|
+
const current = (process.env.PATH || '')
|
|
279
|
+
.split(delimiter)
|
|
280
|
+
.filter(Boolean)
|
|
281
|
+
.map(stabilizeServiceDir);
|
|
282
|
+
const out = [];
|
|
283
|
+
const seen = new Set();
|
|
284
|
+
for (const p of [...baseline, ...current]) {
|
|
285
|
+
if (p && !seen.has(p)) { out.push(p); seen.add(p); }
|
|
286
|
+
}
|
|
287
|
+
return out.join(delimiter);
|
|
288
|
+
}
|
|
289
|
+
|
|
221
290
|
// ─── Platform service backends ──────────────────────────────────────────────────
|
|
222
291
|
|
|
223
292
|
function createDarwinBackend() {
|
|
@@ -232,12 +301,20 @@ function createDarwinBackend() {
|
|
|
232
301
|
}
|
|
233
302
|
|
|
234
303
|
function plistContents() {
|
|
304
|
+
// Prefer a stable node path — fnm_multishells shims disappear when the
|
|
305
|
+
// installing shell exits, leaving launchd unable to respawn the daemon.
|
|
306
|
+
const nodeBin = stabilizeServiceFile(process.execPath);
|
|
235
307
|
const programArgs = [
|
|
236
|
-
|
|
308
|
+
nodeBin,
|
|
237
309
|
runtimeDaemonEntryPath,
|
|
238
310
|
].map((value) => ` <string>${value}</string>`).join('\n');
|
|
239
311
|
|
|
240
|
-
|
|
312
|
+
// launchd starts the daemon with a bare PATH; inject a rich one so child
|
|
313
|
+
// CLIs like codex (a sh wrapper that does `exec node`) can find node.
|
|
314
|
+
const envVars = {
|
|
315
|
+
PATH: buildServicePath(dirname(nodeBin)),
|
|
316
|
+
VIBE_PORT: String(port),
|
|
317
|
+
};
|
|
241
318
|
if (process.env.VIBELET_RELAY_URL) envVars.VIBELET_RELAY_URL = process.env.VIBELET_RELAY_URL;
|
|
242
319
|
if (process.env.VIBELET_CANONICAL_HOST) envVars.VIBELET_CANONICAL_HOST = process.env.VIBELET_CANONICAL_HOST;
|
|
243
320
|
if (process.env.VIBELET_FALLBACK_HOSTS) envVars.VIBELET_FALLBACK_HOSTS = process.env.VIBELET_FALLBACK_HOSTS;
|
|
@@ -347,17 +424,20 @@ function createLinuxBackend() {
|
|
|
347
424
|
}
|
|
348
425
|
|
|
349
426
|
function unitContents() {
|
|
427
|
+
const nodeBin = stabilizeServiceFile(process.execPath);
|
|
428
|
+
const servicePath = buildServicePath(dirname(nodeBin));
|
|
350
429
|
return `[Unit]
|
|
351
430
|
Description=Vibelet Daemon
|
|
352
431
|
After=network.target
|
|
353
432
|
|
|
354
433
|
[Service]
|
|
355
|
-
ExecStart=${
|
|
434
|
+
ExecStart=${nodeBin} ${runtimeDaemonEntryPath}
|
|
356
435
|
WorkingDirectory=${runtimeCurrentDir}
|
|
357
436
|
Restart=always
|
|
358
437
|
RestartSec=3
|
|
359
438
|
StandardOutput=append:${stdoutLogPath}
|
|
360
439
|
StandardError=append:${stderrLogPath}
|
|
440
|
+
Environment=PATH=${servicePath}
|
|
361
441
|
Environment=VIBE_PORT=${port}${process.env.VIBELET_RELAY_URL ? `\nEnvironment=VIBELET_RELAY_URL=${process.env.VIBELET_RELAY_URL}` : ''}${process.env.VIBELET_CANONICAL_HOST ? `\nEnvironment=VIBELET_CANONICAL_HOST=${process.env.VIBELET_CANONICAL_HOST}` : ''}${process.env.VIBELET_FALLBACK_HOSTS ? `\nEnvironment=VIBELET_FALLBACK_HOSTS=${process.env.VIBELET_FALLBACK_HOSTS}` : ''}
|
|
362
442
|
|
|
363
443
|
[Install]
|
|
@@ -556,7 +636,7 @@ async function waitForDaemonExit(timeoutMs) {
|
|
|
556
636
|
return false;
|
|
557
637
|
}
|
|
558
638
|
|
|
559
|
-
async function waitForHealth(timeoutMs =
|
|
639
|
+
async function waitForHealth(timeoutMs = 30_000) {
|
|
560
640
|
const health = await probeHealth(timeoutMs);
|
|
561
641
|
if (health) {
|
|
562
642
|
return health;
|