@inceptionstack/roundhouse 0.3.2 → 0.3.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli/setup.ts +70 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inceptionstack/roundhouse",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "description": "Multi-platform chat gateway that routes messages through a configured AI agent",
6
6
  "license": "MIT",
package/src/cli/setup.ts CHANGED
@@ -319,8 +319,7 @@ async function stepInstallPackages(opts: SetupOptions): Promise<void> {
319
319
  // Roundhouse
320
320
  const rhInstalled = whichSync("roundhouse");
321
321
  if (rhInstalled && !opts.force) {
322
- const ver = execSafe("roundhouse", ["--version"], { silent: true }) || "installed";
323
- ok(`@inceptionstack/roundhouse (${ver}, already installed)`);
322
+ ok(`@inceptionstack/roundhouse (already installed)`);
324
323
  } else {
325
324
  log(" Installing @inceptionstack/roundhouse...");
326
325
  execOrFail("npm", ["install", "-g", "@inceptionstack/roundhouse"], "roundhouse install");
@@ -330,14 +329,37 @@ async function stepInstallPackages(opts: SetupOptions): Promise<void> {
330
329
  // Pi agent
331
330
  const piInstalled = whichSync("pi");
332
331
  if (piInstalled && !opts.force) {
333
- const ver = execSafe("pi", ["--version"], { silent: true }) || "installed";
334
- ok(`@mariozechner/pi-coding-agent (${ver}, already installed)`);
332
+ ok(`@mariozechner/pi-coding-agent (already installed)`);
335
333
  } else {
336
334
  log(" Installing @mariozechner/pi-coding-agent...");
337
335
  execOrFail("npm", ["install", "-g", "@mariozechner/pi-coding-agent"], "pi install");
338
336
  ok("@mariozechner/pi-coding-agent");
339
337
  }
340
338
 
339
+ // psst-cli (requires bun runtime)
340
+ if (opts.psst) {
341
+ // Install bun if not present (psst-cli shebang is #!/usr/bin/env bun)
342
+ if (!whichSync("bun")) {
343
+ log(" Installing bun runtime (required by psst)...");
344
+ try {
345
+ execFileSync("bash", ["-c", "curl -fsSL https://bun.sh/install | bash"], {
346
+ encoding: "utf8", stdio: "pipe", timeout: 120_000,
347
+ env: { ...process.env, HOME: homedir() },
348
+ });
349
+ // bun installs to ~/.bun/bin/bun
350
+ const bunPath = resolve(homedir(), ".bun", "bin");
351
+ process.env.PATH = `${bunPath}:${process.env.PATH}`;
352
+ ok("bun runtime");
353
+ } catch (err: any) {
354
+ warn(`bun install failed: ${err.message}`);
355
+ warn("psst requires bun — install manually: curl -fsSL https://bun.sh/install | bash");
356
+ opts.psst = false;
357
+ }
358
+ } else {
359
+ ok("bun runtime (already installed)");
360
+ }
361
+ }
362
+
341
363
  // psst-cli
342
364
  if (opts.psst) {
343
365
  const psstInstalled = whichSync("psst");
@@ -345,8 +367,19 @@ async function stepInstallPackages(opts: SetupOptions): Promise<void> {
345
367
  ok(`psst-cli (already installed)`);
346
368
  } else {
347
369
  log(" Installing psst-cli...");
348
- execOrFail("npm", ["install", "-g", "psst-cli"], "psst-cli install");
349
- ok("psst-cli");
370
+ try {
371
+ execFileSync("npm", ["install", "-g", "psst-cli"], {
372
+ encoding: "utf8", stdio: "pipe", timeout: 120_000,
373
+ });
374
+ } catch {
375
+ // npm may exit non-zero due to postinstall warnings — check if binary exists
376
+ }
377
+ if (whichSync("psst")) {
378
+ ok("psst-cli");
379
+ } else {
380
+ warn("psst-cli install failed");
381
+ opts.psst = false;
382
+ }
350
383
  }
351
384
 
352
385
  // Initialize psst vault
@@ -355,8 +388,27 @@ async function stepInstallPackages(opts: SetupOptions): Promise<void> {
355
388
  ok("psst vault exists");
356
389
  } else {
357
390
  log(" Initializing psst vault...");
358
- execOrFail("psst", ["init"], "psst init");
359
- ok("psst vault initialized");
391
+ // On headless servers, no keychain is available — use PSST_PASSWORD
392
+ const psstEnv = { ...process.env };
393
+ if (!psstEnv.PSST_PASSWORD) {
394
+ // Generate a random password and store it for future use
395
+ const psstPw = randomBytes(32).toString("base64");
396
+ const pwFile = resolve(ROUNDHOUSE_DIR, ".psst-password");
397
+ await atomicWriteText(pwFile, psstPw + "\n", 0o600);
398
+ psstEnv.PSST_PASSWORD = psstPw;
399
+ // Also set for subsequent psst calls in this process
400
+ process.env.PSST_PASSWORD = psstPw;
401
+ }
402
+ try {
403
+ execFileSync("psst", ["init"], {
404
+ encoding: "utf8", stdio: "pipe", timeout: 30_000,
405
+ env: psstEnv,
406
+ });
407
+ ok("psst vault initialized");
408
+ } catch (err: any) {
409
+ warn(`psst vault init failed: ${err.stderr?.trim() || err.message}`);
410
+ opts.psst = false;
411
+ }
360
412
  }
361
413
 
362
414
  // Install pi-psst extension
@@ -516,6 +568,15 @@ async function stepConfigure(
516
568
  envLines.push(`ALLOWED_USERS=${envQuote(opts.users.join(","))}`);
517
569
  }
518
570
 
571
+ // If psst uses a generated password (headless), include it in env for systemd
572
+ if (opts.psst) {
573
+ const pwFile = resolve(ROUNDHOUSE_DIR, ".psst-password");
574
+ if (await fileExists(pwFile)) {
575
+ const pw = (await readFile(pwFile, "utf8")).trim();
576
+ envLines.push(`PSST_PASSWORD=${envQuote(pw)}`);
577
+ }
578
+ }
579
+
519
580
  if (opts.provider === "amazon-bedrock") {
520
581
  // Preserve existing AWS config
521
582
  let existingEnv: Record<string, string> = {};
@@ -880,6 +941,7 @@ function printDryRun(opts: SetupOptions): void {
880
941
  log(`Would install: npm install -g @inceptionstack/roundhouse`);
881
942
  log(`Would install: npm install -g @mariozechner/pi-coding-agent`);
882
943
  if (opts.psst) {
944
+ log(`Would install: bun runtime (if not present)`);
883
945
  log(`Would install: npm install -g psst-cli`);
884
946
  log(`Would initialize psst vault`);
885
947
  log(`Would install: pi-psst extension`);