buildwithnexus 0.3.0 → 0.3.2

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/dist/bin.js CHANGED
@@ -9,6 +9,84 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/ui/banner.ts
13
+ var banner_exports = {};
14
+ __export(banner_exports, {
15
+ showBanner: () => showBanner,
16
+ showCompletion: () => showCompletion,
17
+ showPhase: () => showPhase,
18
+ showSecurityPosture: () => showSecurityPosture
19
+ });
20
+ import chalk from "chalk";
21
+ function showBanner() {
22
+ console.log(BANNER);
23
+ console.log(chalk.dim(" v0.3.1 \xB7 buildwithnexus.dev\n"));
24
+ }
25
+ function showPhase(phase, total, description) {
26
+ const progress = chalk.cyan(`[${phase}/${total}]`);
27
+ console.log(`
28
+ ${progress} ${chalk.bold(description)}`);
29
+ }
30
+ function showSecurityPosture() {
31
+ const lines = [
32
+ "",
33
+ chalk.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
34
+ chalk.bold(" \u2551 ") + chalk.bold.green("Security Posture") + chalk.bold(" \u2551"),
35
+ chalk.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
36
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" Triple-nested isolation: Host \u2192 VM \u2192 Docker \u2192 KVM".padEnd(54)) + chalk.bold("\u2551"),
37
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" Network hardened: UFW deny-all, allow 22/80/443/4200".padEnd(54)) + chalk.bold("\u2551"),
38
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" All databases encrypted at rest (AES-256-CBC)".padEnd(54)) + chalk.bold("\u2551"),
39
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" API keys never embedded in VM \u2014 delivered via SCP".padEnd(54)) + chalk.bold("\u2551"),
40
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" SSH-only communication (no exposed network ports)".padEnd(54)) + chalk.bold("\u2551"),
41
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" DLP: secret detection, shell escaping, output redaction".padEnd(54)) + chalk.bold("\u2551"),
42
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" HMAC integrity verification on all key files".padEnd(54)) + chalk.bold("\u2551"),
43
+ chalk.bold(" \u2551 ") + chalk.green("\u2713") + chalk.white(" Docker: --read-only, no-new-privileges, cap-drop=ALL".padEnd(54)) + chalk.bold("\u2551"),
44
+ chalk.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
45
+ chalk.bold(" \u2551 ") + chalk.dim("Full details: https://buildwithnexus.dev/security".padEnd(55)) + chalk.bold("\u2551"),
46
+ chalk.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
47
+ ""
48
+ ];
49
+ console.log(lines.join("\n"));
50
+ }
51
+ function showCompletion(urls) {
52
+ const lines = [
53
+ "",
54
+ chalk.green(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
55
+ chalk.green(" \u2551 ") + chalk.bold.green("NEXUS Runtime is Live!") + chalk.green(" \u2551"),
56
+ chalk.green(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
57
+ chalk.green(" \u2551 ") + chalk.white(`Connect: ${urls.ssh}`.padEnd(55)) + chalk.green("\u2551")
58
+ ];
59
+ if (urls.remote) {
60
+ lines.push(chalk.green(" \u2551 ") + chalk.white(`Remote: ${urls.remote}`.padEnd(55)) + chalk.green("\u2551"));
61
+ }
62
+ lines.push(
63
+ chalk.green(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
64
+ chalk.green(" \u2551 ") + chalk.dim("Quick Start:".padEnd(55)) + chalk.green("\u2551"),
65
+ chalk.green(" \u2551 ") + chalk.white(" buildwithnexus - Interactive shell".padEnd(55)) + chalk.green("\u2551"),
66
+ chalk.green(" \u2551 ") + chalk.white(" buildwithnexus brainstorm - Brainstorm ideas".padEnd(55)) + chalk.green("\u2551"),
67
+ chalk.green(" \u2551 ") + chalk.white(" buildwithnexus status - Check health".padEnd(55)) + chalk.green("\u2551"),
68
+ chalk.green(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
69
+ chalk.green(" \u2551 ") + chalk.dim("All commands:".padEnd(55)) + chalk.green("\u2551"),
70
+ chalk.green(" \u2551 ") + chalk.white(" buildwithnexus stop/start/update/logs/ssh/destroy".padEnd(55)) + chalk.green("\u2551"),
71
+ chalk.green(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
72
+ ""
73
+ );
74
+ console.log(lines.join("\n"));
75
+ }
76
+ var BANNER;
77
+ var init_banner = __esm({
78
+ "src/ui/banner.ts"() {
79
+ "use strict";
80
+ BANNER = `
81
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
82
+ \u2551 ${chalk.bold.cyan("B U I L D W I T H N E X U S")} \u2551
83
+ \u2551 \u2551
84
+ \u2551 Autonomous AI Runtime \xB7 Nested Isolation \u2551
85
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
86
+ `;
87
+ }
88
+ });
89
+
12
90
  // src/core/dlp.ts
13
91
  import crypto from "crypto";
14
92
  import fs from "fs";
@@ -285,7 +363,7 @@ __export(qemu_exports, {
285
363
  import fs3 from "fs";
286
364
  import net from "net";
287
365
  import path3 from "path";
288
- import { execa } from "execa";
366
+ import { execa, execaSync } from "execa";
289
367
  import { select } from "@inquirer/prompts";
290
368
  import chalk5 from "chalk";
291
369
  async function isQemuInstalled(platform) {
@@ -300,10 +378,16 @@ async function installQemu(platform) {
300
378
  if (platform.os === "mac") {
301
379
  await execa("brew", ["install", "qemu", "cdrtools"], { stdio: "inherit", env: scrubEnv() });
302
380
  } else if (platform.os === "linux") {
381
+ let hasApt = false;
303
382
  try {
383
+ await execa("which", ["apt-get"], { env: scrubEnv() });
384
+ hasApt = true;
385
+ } catch {
386
+ }
387
+ if (hasApt) {
304
388
  await execa("sudo", ["apt-get", "update"], { stdio: "inherit", env: scrubEnv() });
305
389
  await execa("sudo", ["apt-get", "install", "-y", "qemu-system", "qemu-utils", "genisoimage"], { stdio: "inherit", env: scrubEnv() });
306
- } catch {
390
+ } else {
307
391
  await execa("sudo", ["yum", "install", "-y", "qemu-system-arm", "qemu-system-x86", "qemu-img", "genisoimage"], { stdio: "inherit", env: scrubEnv() });
308
392
  }
309
393
  } else {
@@ -314,7 +398,7 @@ async function downloadImage(platform) {
314
398
  const imagePath = path3.join(IMAGES_DIR, platform.ubuntuImage);
315
399
  if (fs3.existsSync(imagePath)) return imagePath;
316
400
  const url = `${UBUNTU_BASE_URL}/${platform.ubuntuImage}`;
317
- await execa("curl", ["-L", "-o", imagePath, "--progress-bar", url], { stdio: "inherit", env: scrubEnv() });
401
+ await execa("curl", ["-L", "-C", "-", "-o", imagePath, "--progress-bar", url], { stdio: "inherit", env: scrubEnv() });
318
402
  return imagePath;
319
403
  }
320
404
  async function createDisk(basePath, sizeGb) {
@@ -412,38 +496,36 @@ async function resolvePortConflicts(ports) {
412
496
  return resolved;
413
497
  }
414
498
  async function launchVm(platform, diskPath, initIsoPath, ram, cpus, ports) {
415
- const machineArg = platform.os === "mac" ? "-machine virt,gic-version=3" : "-machine pc";
499
+ const machineArgs = platform.os === "mac" ? ["-machine", "virt,gic-version=3"] : ["-machine", "pc"];
416
500
  const biosArgs = fs3.existsSync(platform.biosPath) ? ["-bios", platform.biosPath] : [];
417
- function buildArgs(cpuFlag) {
418
- return [
419
- ...machineArg.split(" "),
420
- ...cpuFlag.split(" "),
421
- "-m",
422
- `${ram}G`,
423
- "-smp",
424
- `${cpus}`,
425
- "-drive",
426
- `file=${diskPath},if=virtio,cache=writethrough`,
427
- "-drive",
428
- `file=${initIsoPath},if=virtio,format=raw,cache=writethrough`,
429
- "-display",
430
- "none",
431
- "-serial",
432
- "none",
433
- "-net",
434
- "nic,model=virtio",
435
- "-net",
436
- `user,hostfwd=tcp::${ports.ssh}-:22,hostfwd=tcp::${ports.http}-:4200,hostfwd=tcp::${ports.https}-:443`,
437
- ...biosArgs,
438
- "-pidfile",
439
- PID_FILE,
440
- "-daemonize"
441
- ];
442
- }
501
+ const buildArgs = (cpuArgs) => [
502
+ ...machineArgs,
503
+ ...cpuArgs,
504
+ "-m",
505
+ `${ram}G`,
506
+ "-smp",
507
+ `${cpus}`,
508
+ "-drive",
509
+ `file=${diskPath},if=virtio,cache=writethrough`,
510
+ "-drive",
511
+ `file=${initIsoPath},if=virtio,format=raw,cache=writethrough`,
512
+ "-display",
513
+ "none",
514
+ "-serial",
515
+ "none",
516
+ "-net",
517
+ "nic,model=virtio",
518
+ "-net",
519
+ `user,hostfwd=tcp::${ports.ssh}-:22,hostfwd=tcp::${ports.http}-:4200,hostfwd=tcp::${ports.https}-:443`,
520
+ ...biosArgs,
521
+ "-pidfile",
522
+ PID_FILE,
523
+ "-daemonize"
524
+ ];
443
525
  try {
444
- await execa(platform.qemuBinary, buildArgs(platform.qemuCpuFlag), { env: scrubEnv() });
526
+ await execa(platform.qemuBinary, buildArgs(platform.qemuCpuFlag.split(" ")), { env: scrubEnv() });
445
527
  } catch {
446
- const fallbackCpu = platform.os === "mac" ? "-cpu max" : "-cpu qemu64";
528
+ const fallbackCpu = platform.os === "mac" ? ["-cpu", "max"] : ["-cpu", "qemu64"];
447
529
  await execa(platform.qemuBinary, buildArgs(fallbackCpu), { env: scrubEnv() });
448
530
  }
449
531
  return ports;
@@ -455,6 +537,14 @@ function readValidPid() {
455
537
  if (!Number.isInteger(pid) || pid <= 1 || pid > 4194304) return null;
456
538
  return pid;
457
539
  }
540
+ function isQemuPid(pid) {
541
+ try {
542
+ const { stdout } = execaSync("ps", ["-p", String(pid), "-o", "comm="], { env: scrubEnv() });
543
+ return stdout.trim().toLowerCase().includes("qemu");
544
+ } catch {
545
+ return false;
546
+ }
547
+ }
458
548
  function isVmRunning() {
459
549
  const pid = readValidPid();
460
550
  if (!pid) return false;
@@ -468,6 +558,13 @@ function isVmRunning() {
468
558
  function stopVm() {
469
559
  const pid = readValidPid();
470
560
  if (!pid) return;
561
+ if (!isQemuPid(pid)) {
562
+ try {
563
+ fs3.unlinkSync(PID_FILE);
564
+ } catch {
565
+ }
566
+ return;
567
+ }
471
568
  try {
472
569
  process.kill(pid, "SIGTERM");
473
570
  } catch {
@@ -639,56 +736,12 @@ var init_ssh = __esm({
639
736
  });
640
737
 
641
738
  // src/cli.ts
642
- import { Command as Command13 } from "commander";
739
+ import { Command as Command14 } from "commander";
643
740
 
644
741
  // src/commands/init.ts
742
+ init_banner();
645
743
  import { Command } from "commander";
646
-
647
- // src/ui/banner.ts
648
- import chalk from "chalk";
649
- var BANNER = `
650
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
651
- \u2551 ${chalk.bold.cyan("B U I L D W I T H N E X U S")} \u2551
652
- \u2551 \u2551
653
- \u2551 Autonomous AI Runtime \xB7 Nested Isolation \u2551
654
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
655
- `;
656
- function showBanner() {
657
- console.log(BANNER);
658
- console.log(chalk.dim(" v0.2.8 \xB7 buildwithnexus.dev\n"));
659
- }
660
- function showPhase(phase, total, description) {
661
- const progress = chalk.cyan(`[${phase}/${total}]`);
662
- console.log(`
663
- ${progress} ${chalk.bold(description)}`);
664
- }
665
- function showCompletion(urls) {
666
- const lines = [
667
- "",
668
- chalk.green(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),
669
- chalk.green(" \u2551 ") + chalk.bold.green("NEXUS Runtime is Live!") + chalk.green(" \u2551"),
670
- chalk.green(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
671
- chalk.green(" \u2551 ") + chalk.white(`Connect: ${urls.ssh}`.padEnd(55)) + chalk.green("\u2551")
672
- ];
673
- if (urls.remote) {
674
- lines.push(chalk.green(" \u2551 ") + chalk.white(`Remote: ${urls.remote}`.padEnd(55)) + chalk.green("\u2551"));
675
- }
676
- lines.push(
677
- chalk.green(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),
678
- chalk.green(" \u2551 ") + chalk.dim("Commands:".padEnd(55)) + chalk.green("\u2551"),
679
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus ssh - Open CLI".padEnd(55)) + chalk.green("\u2551"),
680
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus status - Check health".padEnd(55)) + chalk.green("\u2551"),
681
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus logs - View logs".padEnd(55)) + chalk.green("\u2551"),
682
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus stop - Shutdown".padEnd(55)) + chalk.green("\u2551"),
683
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus start - Restart".padEnd(55)) + chalk.green("\u2551"),
684
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus update - Update release".padEnd(55)) + chalk.green("\u2551"),
685
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus brainstorm - Brainstorm ideas".padEnd(55)) + chalk.green("\u2551"),
686
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus destroy - Remove all".padEnd(55)) + chalk.green("\u2551"),
687
- chalk.green(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),
688
- ""
689
- );
690
- console.log(lines.join("\n"));
691
- }
744
+ import chalk6 from "chalk";
692
745
 
693
746
  // src/ui/spinner.ts
694
747
  import ora from "ora";
@@ -895,69 +948,65 @@ async function createCloudInitIso(userDataPath) {
895
948
  fs5.writeFileSync(metaDataPath, "instance-id: nexus-vm-1\nlocal-hostname: nexus-vm\n", { mode: 384 });
896
949
  const isoPath = path5.join(IMAGES_DIR2, "init.iso");
897
950
  const env = scrubEnv();
898
- let created = false;
899
951
  try {
900
- await execa3("mkisofs", [
901
- "-output",
902
- isoPath,
903
- "-volid",
904
- "cidata",
905
- "-joliet",
906
- "-rock",
907
- userDataPath,
908
- metaDataPath
909
- ], { env });
910
- created = true;
911
- } catch {
912
- }
913
- if (!created) {
952
+ let created = false;
953
+ for (const tool of ["mkisofs", "genisoimage"]) {
954
+ if (created) break;
955
+ try {
956
+ await execa3(tool, [
957
+ "-output",
958
+ isoPath,
959
+ "-volid",
960
+ "cidata",
961
+ "-joliet",
962
+ "-rock",
963
+ userDataPath,
964
+ metaDataPath
965
+ ], { env });
966
+ created = true;
967
+ } catch {
968
+ }
969
+ }
970
+ if (!created) {
971
+ try {
972
+ const stagingDir = path5.join(CONFIGS_DIR, "cidata-staging");
973
+ fs5.mkdirSync(stagingDir, { recursive: true, mode: 448 });
974
+ fs5.copyFileSync(userDataPath, path5.join(stagingDir, "user-data"));
975
+ fs5.copyFileSync(metaDataPath, path5.join(stagingDir, "meta-data"));
976
+ await execa3("hdiutil", [
977
+ "makehybrid",
978
+ "-o",
979
+ isoPath,
980
+ "-joliet",
981
+ "-iso",
982
+ "-default-volume-name",
983
+ "cidata",
984
+ stagingDir
985
+ ], { env });
986
+ fs5.rmSync(stagingDir, { recursive: true, force: true });
987
+ created = true;
988
+ } catch {
989
+ }
990
+ }
991
+ if (!created) {
992
+ throw new Error(
993
+ "Cannot create cloud-init ISO: none of mkisofs, genisoimage, or hdiutil are available. On macOS, install cdrtools: brew install cdrtools. On Linux: sudo apt install genisoimage"
994
+ );
995
+ }
996
+ fs5.chmodSync(isoPath, 384);
997
+ audit("cloudinit_iso_created", "init.iso created");
998
+ return isoPath;
999
+ } finally {
914
1000
  try {
915
- await execa3("genisoimage", [
916
- "-output",
917
- isoPath,
918
- "-volid",
919
- "cidata",
920
- "-joliet",
921
- "-rock",
922
- userDataPath,
923
- metaDataPath
924
- ], { env });
925
- created = true;
1001
+ fs5.unlinkSync(userDataPath);
926
1002
  } catch {
927
1003
  }
928
- }
929
- if (!created) {
930
1004
  try {
931
- const stagingDir = path5.join(CONFIGS_DIR, "cidata-staging");
932
- fs5.mkdirSync(stagingDir, { recursive: true, mode: 448 });
933
- fs5.copyFileSync(userDataPath, path5.join(stagingDir, "user-data"));
934
- fs5.copyFileSync(metaDataPath, path5.join(stagingDir, "meta-data"));
935
- await execa3("hdiutil", [
936
- "makehybrid",
937
- "-o",
938
- isoPath,
939
- "-joliet",
940
- "-iso",
941
- "-default-volume-name",
942
- "cidata",
943
- stagingDir
944
- ], { env });
945
- fs5.rmSync(stagingDir, { recursive: true, force: true });
946
- created = true;
1005
+ fs5.unlinkSync(metaDataPath);
947
1006
  } catch {
948
1007
  }
1008
+ audit("cloudinit_plaintext_deleted", "user-data.yaml and meta-data.yaml removed");
949
1009
  }
950
- if (!created) {
951
- throw new Error(
952
- "Cannot create cloud-init ISO: none of mkisofs, genisoimage, or hdiutil are available. On macOS, install cdrtools: brew install cdrtools. On Linux: sudo apt install genisoimage"
953
- );
954
- }
955
- fs5.chmodSync(isoPath, 384);
956
- fs5.unlinkSync(userDataPath);
957
- fs5.unlinkSync(metaDataPath);
958
- audit("cloudinit_plaintext_deleted", "user-data.yaml and meta-data.yaml removed");
959
- audit("cloudinit_iso_created", "init.iso created");
960
- return isoPath;
961
1010
  }
962
1011
 
963
1012
  // src/core/health.ts
@@ -996,27 +1045,51 @@ async function checkHealth(port, vmRunning) {
996
1045
  }
997
1046
  return status;
998
1047
  }
999
- async function waitForServer(port, timeoutMs = 6e5) {
1048
+ async function waitForServer(port, timeoutMs = 9e5) {
1000
1049
  const start = Date.now();
1050
+ let lastLog = 0;
1001
1051
  while (Date.now() - start < timeoutMs) {
1002
1052
  try {
1003
1053
  const { stdout, code } = await sshExec(port, "curl -sf http://localhost:4200/health");
1004
1054
  if (code === 0 && stdout.includes("ok")) return true;
1005
1055
  } catch {
1006
1056
  }
1007
- await new Promise((r) => setTimeout(r, 1e4));
1057
+ const elapsed = Date.now() - start;
1058
+ if (elapsed - lastLog >= 3e4) {
1059
+ lastLog = elapsed;
1060
+ try {
1061
+ const { stdout } = await sshExec(port, "systemctl is-active nexus 2>/dev/null || echo 'starting...'");
1062
+ process.stderr.write(`
1063
+ [server ${Math.round(elapsed / 1e3)}s] ${stdout.trim().slice(0, 120)}
1064
+ `);
1065
+ } catch {
1066
+ }
1067
+ }
1068
+ await new Promise((r) => setTimeout(r, 5e3));
1008
1069
  }
1009
1070
  return false;
1010
1071
  }
1011
- async function waitForCloudInit(port, timeoutMs = 9e5) {
1072
+ async function waitForCloudInit(port, timeoutMs = 18e5) {
1012
1073
  const start = Date.now();
1074
+ let lastLog = 0;
1013
1075
  while (Date.now() - start < timeoutMs) {
1014
1076
  try {
1015
1077
  const { code } = await sshExec(port, "test -f /var/lib/cloud/instance/boot-finished");
1016
1078
  if (code === 0) return true;
1017
1079
  } catch {
1018
1080
  }
1019
- await new Promise((r) => setTimeout(r, 1e4));
1081
+ const elapsed = Date.now() - start;
1082
+ if (elapsed - lastLog >= 6e4) {
1083
+ lastLog = elapsed;
1084
+ try {
1085
+ const { stdout } = await sshExec(port, "tail -1 /var/log/cloud-init-output.log 2>/dev/null || echo 'waiting...'");
1086
+ process.stderr.write(`
1087
+ [cloud-init ${Math.round(elapsed / 1e3)}s] ${stdout.trim().slice(0, 120)}
1088
+ `);
1089
+ } catch {
1090
+ }
1091
+ }
1092
+ await new Promise((r) => setTimeout(r, 2e4));
1020
1093
  }
1021
1094
  return false;
1022
1095
  }
@@ -1043,17 +1116,19 @@ async function installCloudflared(sshPort, arch) {
1043
1116
  }
1044
1117
  async function startTunnel(sshPort) {
1045
1118
  await sshExec(sshPort, [
1046
- "nohup cloudflared tunnel --url http://localhost:4200",
1047
- "> /tmp/tunnel.log 2>&1 &",
1119
+ "install -m 600 /dev/null /home/nexus/.nexus/tunnel.log",
1120
+ "&& nohup cloudflared tunnel --no-autoupdate --url http://localhost:4200",
1121
+ "> /home/nexus/.nexus/tunnel.log 2>&1 &",
1048
1122
  "disown"
1049
1123
  ].join(" "));
1050
1124
  const start = Date.now();
1051
- while (Date.now() - start < 3e4) {
1125
+ while (Date.now() - start < 6e4) {
1052
1126
  try {
1053
- const { stdout } = await sshExec(sshPort, "grep -o 'https://[^ ]*\\.trycloudflare\\.com' /tmp/tunnel.log 2>/dev/null | head -1");
1127
+ const { stdout } = await sshExec(sshPort, "grep -oE 'https://[a-z0-9-]+\\.trycloudflare\\.com' /home/nexus/.nexus/tunnel.log 2>/dev/null | head -1");
1054
1128
  if (stdout.includes("https://")) {
1055
1129
  const url = stdout.trim();
1056
1130
  if (!/^https:\/\/[a-z0-9-]+\.trycloudflare\.com$/.test(url)) {
1131
+ audit("tunnel_url_rejected", `Invalid URL format: ${url.slice(0, 80)}`);
1057
1132
  return null;
1058
1133
  }
1059
1134
  await sshExec(sshPort, shellCommand`printf '%s\n' ${url} > /home/nexus/.nexus/tunnel-url.txt && chmod 600 /home/nexus/.nexus/tunnel-url.txt`);
@@ -1086,176 +1161,281 @@ function getReleaseTarball() {
1086
1161
  if (fs6.existsSync(rootPath)) return rootPath;
1087
1162
  throw new Error("nexus-release.tar.gz not found. Run: npm run bundle");
1088
1163
  }
1089
- var TOTAL_PHASES = 10;
1090
1164
  function getCloudInitTemplate() {
1091
1165
  const dir = path6.dirname(fileURLToPath(import.meta.url));
1092
1166
  const templatePath = path6.join(dir, "templates", "cloud-init.yaml.ejs");
1093
- if (fs6.existsSync(templatePath)) {
1094
- return fs6.readFileSync(templatePath, "utf-8");
1095
- }
1167
+ if (fs6.existsSync(templatePath)) return fs6.readFileSync(templatePath, "utf-8");
1096
1168
  const srcPath = path6.resolve(dir, "..", "src", "templates", "cloud-init.yaml.ejs");
1097
1169
  return fs6.readFileSync(srcPath, "utf-8");
1098
1170
  }
1099
- var initCommand = new Command("init").description("Scaffold and launch a new NEXUS runtime").action(async () => {
1100
- try {
1101
- showBanner();
1102
- showPhase(1, TOTAL_PHASES, "Configuration");
1103
- const platform = detectPlatform();
1104
- log.detail("Platform", `${platform.os} ${platform.arch}`);
1105
- log.detail("QEMU", platform.qemuBinary);
1106
- const userConfig = await promptInitConfig();
1107
- ensureHome();
1108
- const masterSecret = generateMasterSecret();
1109
- const config = {
1110
- vmRam: userConfig.vmRam,
1111
- vmCpus: userConfig.vmCpus,
1112
- vmDisk: userConfig.vmDisk,
1113
- enableTunnel: userConfig.enableTunnel,
1114
- sshPort: 2222,
1115
- httpPort: 4200,
1116
- httpsPort: 8443,
1117
- masterSecret
1118
- };
1119
- const keys = {
1120
- ANTHROPIC_API_KEY: userConfig.anthropicKey,
1121
- OPENAI_API_KEY: userConfig.openaiKey || void 0,
1122
- GOOGLE_API_KEY: userConfig.googleKey || void 0,
1123
- NEXUS_MASTER_SECRET: masterSecret
1124
- };
1125
- const violations = validateAllKeys(keys);
1126
- if (violations.length > 0) {
1127
- for (const v of violations) log.error(v);
1128
- process.exit(1);
1171
+ async function withSpinner(spinner, label, fn) {
1172
+ spinner.text = label;
1173
+ spinner.start();
1174
+ const result = await fn();
1175
+ succeed(spinner, label);
1176
+ return result;
1177
+ }
1178
+ var phases = [
1179
+ // Phase 1 — Configuration
1180
+ {
1181
+ name: "Configuration",
1182
+ run: async (ctx) => {
1183
+ showBanner();
1184
+ const platform = detectPlatform();
1185
+ log.detail("Platform", `${platform.os} ${platform.arch}`);
1186
+ log.detail("QEMU", platform.qemuBinary);
1187
+ const userConfig = await promptInitConfig();
1188
+ ensureHome();
1189
+ const masterSecret = generateMasterSecret();
1190
+ const config = {
1191
+ vmRam: userConfig.vmRam,
1192
+ vmCpus: userConfig.vmCpus,
1193
+ vmDisk: userConfig.vmDisk,
1194
+ enableTunnel: userConfig.enableTunnel,
1195
+ sshPort: 2222,
1196
+ httpPort: 4200,
1197
+ httpsPort: 8443,
1198
+ masterSecret
1199
+ };
1200
+ const keys = {
1201
+ ANTHROPIC_API_KEY: userConfig.anthropicKey,
1202
+ OPENAI_API_KEY: userConfig.openaiKey || void 0,
1203
+ GOOGLE_API_KEY: userConfig.googleKey || void 0,
1204
+ NEXUS_MASTER_SECRET: masterSecret
1205
+ };
1206
+ const violations = validateAllKeys(keys);
1207
+ if (violations.length > 0) {
1208
+ for (const v of violations) log.error(v);
1209
+ process.exit(1);
1210
+ }
1211
+ saveConfig(config);
1212
+ saveKeys(keys);
1213
+ log.success("Configuration saved");
1214
+ ctx.platform = platform;
1215
+ ctx.config = config;
1216
+ ctx.keys = keys;
1129
1217
  }
1130
- saveConfig(config);
1131
- saveKeys(keys);
1132
- log.success("Configuration saved");
1133
- showPhase(2, TOTAL_PHASES, "QEMU Installation");
1134
- let spinner = createSpinner("Checking QEMU...");
1135
- spinner.start();
1136
- const hasQemu = await isQemuInstalled(platform);
1137
- if (hasQemu) {
1138
- succeed(spinner, "QEMU already installed");
1139
- } else {
1140
- spinner.text = "Installing QEMU...";
1141
- await installQemu(platform);
1142
- succeed(spinner, "QEMU installed");
1218
+ },
1219
+ // Phase 2 — QEMU
1220
+ {
1221
+ name: "QEMU Installation",
1222
+ run: async (ctx, spinner) => {
1223
+ const { platform } = ctx;
1224
+ await withSpinner(spinner, "Checking QEMU...", async () => {
1225
+ const hasQemu = await isQemuInstalled(platform);
1226
+ if (!hasQemu) {
1227
+ spinner.text = "Installing QEMU...";
1228
+ await installQemu(platform);
1229
+ }
1230
+ });
1231
+ succeed(spinner, hasQemuLabel(await isQemuInstalled(ctx.platform)));
1143
1232
  }
1144
- showPhase(3, TOTAL_PHASES, "SSH Key Setup");
1145
- spinner = createSpinner("Generating SSH key...");
1146
- spinner.start();
1147
- await generateSshKey();
1148
- addSshConfig(config.sshPort);
1149
- succeed(spinner, "SSH key ready (ed25519)");
1150
- showPhase(4, TOTAL_PHASES, "VM Image Download");
1151
- spinner = createSpinner(`Downloading Ubuntu 24.04 (${platform.ubuntuImage})...`);
1152
- spinner.start();
1153
- const imagePath = await downloadImage(platform);
1154
- succeed(spinner, "Ubuntu image ready");
1155
- showPhase(5, TOTAL_PHASES, "Cloud-Init Generation");
1156
- spinner = createSpinner("Locating release tarball...");
1157
- spinner.start();
1158
- const tarballPath = getReleaseTarball();
1159
- succeed(spinner, `Release tarball found (${path6.basename(tarballPath)})`);
1160
- spinner = createSpinner("Rendering cloud-init...");
1161
- spinner.start();
1162
- const pubKey = getPubKey();
1163
- const template = getCloudInitTemplate();
1164
- const userDataPath = await renderCloudInit({ sshPubKey: pubKey, keys, config }, template);
1165
- const isoPath = await createCloudInitIso(userDataPath);
1166
- succeed(spinner, "Cloud-init ISO created");
1167
- showPhase(6, TOTAL_PHASES, "VM Launch");
1168
- spinner = createSpinner("Checking port availability...");
1169
- spinner.start();
1170
- const requestedPorts = { ssh: config.sshPort, http: config.httpPort, https: config.httpsPort };
1171
- spinner.stop();
1172
- spinner.clear();
1173
- const resolvedPorts = await resolvePortConflicts(requestedPorts);
1174
- spinner = createSpinner("Creating disk and launching VM...");
1175
- spinner.start();
1176
- const diskPath = await createDisk(imagePath, config.vmDisk);
1177
- await launchVm(platform, diskPath, isoPath, config.vmRam, config.vmCpus, resolvedPorts);
1178
- config.sshPort = resolvedPorts.ssh;
1179
- config.httpPort = resolvedPorts.http;
1180
- config.httpsPort = resolvedPorts.https;
1181
- saveConfig(config);
1182
- const portNote = resolvedPorts.ssh !== 2222 || resolvedPorts.http !== 4200 || resolvedPorts.https !== 8443 ? ` (ports: SSH=${resolvedPorts.ssh}, HTTP=${resolvedPorts.http}, HTTPS=${resolvedPorts.https})` : "";
1183
- succeed(spinner, `VM launched (daemonized)${portNote}`);
1184
- showPhase(7, TOTAL_PHASES, "VM Provisioning");
1185
- spinner = createSpinner("Waiting for SSH...");
1186
- spinner.start();
1187
- const sshReady = await waitForSsh(config.sshPort);
1188
- if (!sshReady) {
1189
- fail(spinner, "SSH connection timed out");
1190
- process.exit(1);
1233
+ },
1234
+ // Phase 3 SSH Keys
1235
+ {
1236
+ name: "SSH Key Setup",
1237
+ run: async (ctx, spinner) => {
1238
+ const { config } = ctx;
1239
+ await withSpinner(spinner, "Generating SSH key...", async () => {
1240
+ await generateSshKey();
1241
+ addSshConfig(config.sshPort);
1242
+ });
1191
1243
  }
1192
- succeed(spinner, "SSH connected");
1193
- spinner = createSpinner("Uploading NEXUS release tarball...");
1194
- spinner.start();
1195
- await sshUploadFile(config.sshPort, tarballPath, "/tmp/nexus-release.tar.gz");
1196
- succeed(spinner, "Tarball uploaded");
1197
- spinner = createSpinner("Staging API keys...");
1198
- spinner.start();
1199
- const keysContent = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
1200
- const tmpKeysPath = path6.join(os2.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1201
- fs6.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1202
- try {
1203
- await sshUploadFile(config.sshPort, tmpKeysPath, "/tmp/.nexus-env-keys");
1204
- await sshExec(config.sshPort, "chmod 600 /tmp/.nexus-env-keys");
1205
- } finally {
1206
- try {
1207
- fs6.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
1208
- fs6.unlinkSync(tmpKeysPath);
1209
- } catch {
1210
- }
1244
+ },
1245
+ // Phase 4 Ubuntu Image
1246
+ {
1247
+ name: "VM Image Download",
1248
+ run: async (ctx, spinner) => {
1249
+ const { platform } = ctx;
1250
+ const imagePath = await withSpinner(
1251
+ spinner,
1252
+ `Downloading Ubuntu 24.04 (${platform.ubuntuImage})...`,
1253
+ () => downloadImage(platform)
1254
+ );
1255
+ ctx.imagePath = imagePath;
1211
1256
  }
1212
- succeed(spinner, "API keys staged");
1213
- spinner = createSpinner("Cloud-init provisioning (extracting NEXUS, building Docker, installing deps)...");
1214
- spinner.start();
1215
- const cloudInitDone = await waitForCloudInit(config.sshPort);
1216
- if (!cloudInitDone) {
1217
- fail(spinner, "Cloud-init timed out");
1218
- log.warn("You can check progress with: buildwithnexus ssh then: tail -f /var/log/cloud-init-output.log");
1219
- process.exit(1);
1257
+ },
1258
+ // Phase 5 — Cloud-Init
1259
+ {
1260
+ name: "Cloud-Init Generation",
1261
+ run: async (ctx, spinner) => {
1262
+ const { keys, config } = ctx;
1263
+ const tarballPath = await withSpinner(spinner, "Locating release tarball...", async () => {
1264
+ return getReleaseTarball();
1265
+ });
1266
+ ctx.tarballPath = tarballPath;
1267
+ const isoPath = await withSpinner(spinner, "Rendering cloud-init...", async () => {
1268
+ const pubKey = getPubKey();
1269
+ const template = getCloudInitTemplate();
1270
+ const userDataPath = await renderCloudInit({ sshPubKey: pubKey, keys, config }, template);
1271
+ return createCloudInitIso(userDataPath);
1272
+ });
1273
+ ctx.isoPath = isoPath;
1220
1274
  }
1221
- succeed(spinner, "VM fully provisioned");
1222
- spinner = createSpinner("Delivering API keys...");
1223
- spinner.start();
1224
- await sshExec(config.sshPort, "mkdir -p /home/nexus/.nexus && mv /tmp/.nexus-env-keys /home/nexus/.nexus/.env.keys && chown -R nexus:nexus /home/nexus/.nexus && chmod 600 /home/nexus/.nexus/.env.keys");
1225
- succeed(spinner, "API keys delivered securely");
1226
- showPhase(8, TOTAL_PHASES, "NEXUS Server Startup");
1227
- spinner = createSpinner("Waiting for NEXUS server...");
1228
- spinner.start();
1229
- const serverReady = await waitForServer(config.sshPort);
1230
- if (!serverReady) {
1231
- fail(spinner, "Server failed to start");
1232
- log.warn("Check logs: buildwithnexus logs");
1233
- process.exit(1);
1275
+ },
1276
+ // Phase 6 VM Boot
1277
+ {
1278
+ name: "VM Launch",
1279
+ run: async (ctx, spinner) => {
1280
+ const { platform, imagePath, isoPath, config } = ctx;
1281
+ spinner.text = "Checking port availability...";
1282
+ spinner.start();
1283
+ spinner.stop();
1284
+ spinner.clear();
1285
+ const requestedPorts = { ssh: config.sshPort, http: config.httpPort, https: config.httpsPort };
1286
+ const resolvedPorts = await resolvePortConflicts(requestedPorts);
1287
+ const diskPath = await withSpinner(spinner, "Creating disk and launching VM...", async () => {
1288
+ const disk = await createDisk(imagePath, config.vmDisk);
1289
+ await launchVm(platform, disk, isoPath, config.vmRam, config.vmCpus, resolvedPorts);
1290
+ return disk;
1291
+ });
1292
+ config.sshPort = resolvedPorts.ssh;
1293
+ config.httpPort = resolvedPorts.http;
1294
+ config.httpsPort = resolvedPorts.https;
1295
+ saveConfig(config);
1296
+ const portNote = resolvedPorts.ssh !== 2222 || resolvedPorts.http !== 4200 || resolvedPorts.https !== 8443 ? ` (ports: SSH=${resolvedPorts.ssh}, HTTP=${resolvedPorts.http}, HTTPS=${resolvedPorts.https})` : "";
1297
+ succeed(spinner, `VM launched (daemonized)${portNote}`);
1298
+ ctx.diskPath = diskPath;
1299
+ ctx.resolvedPorts = resolvedPorts;
1300
+ ctx.vmLaunched = true;
1234
1301
  }
1235
- succeed(spinner, "NEXUS server healthy on port 4200");
1236
- let tunnelUrl;
1237
- if (config.enableTunnel) {
1238
- showPhase(9, TOTAL_PHASES, "Cloudflare Tunnel");
1239
- spinner = createSpinner("Installing cloudflared...");
1302
+ },
1303
+ // Phase 7 — VM Provisioning
1304
+ {
1305
+ name: "VM Provisioning",
1306
+ run: async (ctx, spinner) => {
1307
+ const { config, keys, tarballPath } = ctx;
1308
+ spinner.text = "Waiting for SSH...";
1309
+ spinner.start();
1310
+ const sshReady = await waitForSsh(config.sshPort);
1311
+ if (!sshReady) {
1312
+ fail(spinner, "SSH connection timed out");
1313
+ throw new Error("SSH connection timed out");
1314
+ }
1315
+ succeed(spinner, "SSH connected");
1316
+ await withSpinner(
1317
+ spinner,
1318
+ "Uploading NEXUS release tarball...",
1319
+ () => sshUploadFile(config.sshPort, tarballPath, "/tmp/nexus-release.tar.gz")
1320
+ );
1321
+ await withSpinner(spinner, "Staging API keys...", async () => {
1322
+ const keysContent = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
1323
+ const tmpKeysPath = path6.join(os2.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1324
+ fs6.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1325
+ try {
1326
+ await sshUploadFile(config.sshPort, tmpKeysPath, "/tmp/.nexus-env-keys");
1327
+ await sshExec(config.sshPort, "chmod 600 /tmp/.nexus-env-keys");
1328
+ } finally {
1329
+ try {
1330
+ fs6.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
1331
+ fs6.unlinkSync(tmpKeysPath);
1332
+ } catch {
1333
+ }
1334
+ }
1335
+ });
1336
+ spinner.text = "Cloud-init provisioning \u2014 this takes 10-20 min (extracting NEXUS, building Docker, installing deps)...";
1337
+ spinner.start();
1338
+ const cloudInitDone = await waitForCloudInit(config.sshPort);
1339
+ if (!cloudInitDone) {
1340
+ fail(spinner, "Cloud-init timed out after 30 minutes");
1341
+ log.warn("Check progress: buildwithnexus ssh \u2192 tail -f /var/log/cloud-init-output.log");
1342
+ throw new Error("Cloud-init timed out");
1343
+ }
1344
+ succeed(spinner, "VM fully provisioned");
1345
+ await withSpinner(
1346
+ spinner,
1347
+ "Delivering API keys...",
1348
+ () => sshExec(
1349
+ config.sshPort,
1350
+ "mkdir -p /home/nexus/.nexus && mv /tmp/.nexus-env-keys /home/nexus/.nexus/.env.keys && chown -R nexus:nexus /home/nexus/.nexus && chmod 600 /home/nexus/.nexus/.env.keys"
1351
+ )
1352
+ );
1353
+ }
1354
+ },
1355
+ // Phase 8 — Server Health
1356
+ {
1357
+ name: "NEXUS Server Startup",
1358
+ run: async (ctx, spinner) => {
1359
+ const { config } = ctx;
1360
+ spinner.text = "Waiting for NEXUS server...";
1240
1361
  spinner.start();
1241
- await installCloudflared(config.sshPort, platform.arch);
1362
+ const serverReady = await waitForServer(config.sshPort);
1363
+ if (!serverReady) {
1364
+ fail(spinner, "Server failed to start");
1365
+ log.warn("Check logs: buildwithnexus logs");
1366
+ throw new Error("NEXUS server failed to start");
1367
+ }
1368
+ succeed(spinner, "NEXUS server healthy on port 4200");
1369
+ }
1370
+ },
1371
+ // Phase 9 — Cloudflare Tunnel
1372
+ {
1373
+ name: "Cloudflare Tunnel",
1374
+ run: async (ctx, spinner) => {
1375
+ const { config, platform } = ctx;
1376
+ if (!config.enableTunnel) {
1377
+ log.dim("Skipped (not enabled)");
1378
+ return;
1379
+ }
1380
+ await withSpinner(
1381
+ spinner,
1382
+ "Installing cloudflared...",
1383
+ () => installCloudflared(config.sshPort, platform.arch)
1384
+ );
1242
1385
  spinner.text = "Starting tunnel...";
1386
+ spinner.start();
1243
1387
  const url = await startTunnel(config.sshPort);
1244
1388
  if (url) {
1245
- tunnelUrl = url;
1389
+ ctx.tunnelUrl = url;
1246
1390
  succeed(spinner, `Tunnel active: ${url}`);
1247
1391
  } else {
1248
1392
  fail(spinner, "Tunnel failed to start (server still accessible locally)");
1249
1393
  }
1250
- } else {
1251
- showPhase(9, TOTAL_PHASES, "Cloudflare Tunnel");
1252
- log.dim("Skipped (not enabled)");
1253
1394
  }
1254
- showPhase(10, TOTAL_PHASES, "Complete");
1255
- showCompletion({
1256
- remote: tunnelUrl,
1257
- ssh: "buildwithnexus ssh"
1258
- });
1395
+ },
1396
+ // Phase 10 — Complete
1397
+ {
1398
+ name: "Complete",
1399
+ run: async (ctx) => {
1400
+ showCompletion({ remote: ctx.tunnelUrl, ssh: "buildwithnexus ssh" });
1401
+ }
1402
+ }
1403
+ ];
1404
+ function hasQemuLabel(installed) {
1405
+ return installed ? "QEMU ready" : "QEMU installed";
1406
+ }
1407
+ var TOTAL_PHASES = phases.length;
1408
+ async function runInit() {
1409
+ const ctx = { vmLaunched: false, tunnelUrl: void 0 };
1410
+ const spinner = createSpinner("");
1411
+ for (let i = 0; i < phases.length; i++) {
1412
+ const phase = phases[i];
1413
+ showPhase(i + 1, TOTAL_PHASES, phase.name);
1414
+ if (phase.skip?.(ctx)) {
1415
+ log.dim("Skipped");
1416
+ continue;
1417
+ }
1418
+ try {
1419
+ await phase.run(ctx, spinner);
1420
+ } catch (err) {
1421
+ try {
1422
+ spinner.stop();
1423
+ } catch {
1424
+ }
1425
+ if (ctx.vmLaunched) {
1426
+ process.stderr.write(chalk6.dim("\n Stopping VM due to init failure...\n"));
1427
+ try {
1428
+ stopVm();
1429
+ } catch {
1430
+ }
1431
+ }
1432
+ throw err;
1433
+ }
1434
+ }
1435
+ }
1436
+ var initCommand = new Command("init").description("Scaffold and launch a new NEXUS runtime").action(async () => {
1437
+ try {
1438
+ await runInit();
1259
1439
  } catch (err) {
1260
1440
  const safeErr = redactError(err);
1261
1441
  log.error(`Init failed: ${safeErr.message}`);
@@ -1361,7 +1541,7 @@ var stopCommand = new Command3("stop").description("Gracefully shut down the NEX
1361
1541
 
1362
1542
  // src/commands/status.ts
1363
1543
  import { Command as Command4 } from "commander";
1364
- import chalk6 from "chalk";
1544
+ import chalk7 from "chalk";
1365
1545
  init_secrets();
1366
1546
  init_qemu();
1367
1547
  var statusCommand = new Command4("status").description("Check NEXUS runtime health").option("--json", "Output as JSON").action(async (opts) => {
@@ -1376,15 +1556,15 @@ var statusCommand = new Command4("status").description("Check NEXUS runtime heal
1376
1556
  console.log(JSON.stringify({ ...health, pid: getVmPid(), ports: { ssh: config.sshPort, http: config.httpPort, https: config.httpsPort } }, null, 2));
1377
1557
  return;
1378
1558
  }
1379
- const check = (ok) => ok ? chalk6.green("\u25CF") : chalk6.red("\u25CB");
1559
+ const check = (ok) => ok ? chalk7.green("\u25CF") : chalk7.red("\u25CB");
1380
1560
  console.log("");
1381
- console.log(chalk6.bold(" NEXUS Runtime Status"));
1561
+ console.log(chalk7.bold(" NEXUS Runtime Status"));
1382
1562
  console.log("");
1383
- console.log(` ${check(health.vmRunning)} VM ${health.vmRunning ? chalk6.green("running") + chalk6.dim(` (PID ${getVmPid()})`) : chalk6.red("stopped")}`);
1384
- console.log(` ${check(health.sshReady)} SSH ${health.sshReady ? chalk6.green("connected") + chalk6.dim(` (port ${config.sshPort})`) : chalk6.red("unreachable")}`);
1385
- console.log(` ${check(health.dockerReady)} Docker ${health.dockerReady ? chalk6.green("ready") : chalk6.red("not ready")}`);
1386
- console.log(` ${check(health.serverHealthy)} Server ${health.serverHealthy ? chalk6.green("healthy") + chalk6.dim(` (port ${config.httpPort})`) : chalk6.red("unhealthy")}`);
1387
- console.log(` ${check(!!health.tunnelUrl)} Tunnel ${health.tunnelUrl ? chalk6.green(health.tunnelUrl) : chalk6.dim("not active")}`);
1563
+ console.log(` ${check(health.vmRunning)} VM ${health.vmRunning ? chalk7.green("running") + chalk7.dim(` (PID ${getVmPid()})`) : chalk7.red("stopped")}`);
1564
+ console.log(` ${check(health.sshReady)} SSH ${health.sshReady ? chalk7.green("connected") + chalk7.dim(` (port ${config.sshPort})`) : chalk7.red("unreachable")}`);
1565
+ console.log(` ${check(health.dockerReady)} Docker ${health.dockerReady ? chalk7.green("ready") : chalk7.red("not ready")}`);
1566
+ console.log(` ${check(health.serverHealthy)} Server ${health.serverHealthy ? chalk7.green("healthy") + chalk7.dim(` (port ${config.httpPort})`) : chalk7.red("unhealthy")}`);
1567
+ console.log(` ${check(!!health.tunnelUrl)} Tunnel ${health.tunnelUrl ? chalk7.green(health.tunnelUrl) : chalk7.dim("not active")}`);
1388
1568
  console.log("");
1389
1569
  if (health.serverHealthy) {
1390
1570
  log.success(`NEXUS CLI ready \u2014 connect via: buildwithnexus ssh`);
@@ -1393,7 +1573,7 @@ var statusCommand = new Command4("status").description("Check NEXUS runtime heal
1393
1573
 
1394
1574
  // src/commands/doctor.ts
1395
1575
  import { Command as Command5 } from "commander";
1396
- import chalk7 from "chalk";
1576
+ import chalk8 from "chalk";
1397
1577
  import fs7 from "fs";
1398
1578
  init_qemu();
1399
1579
  init_secrets();
@@ -1401,12 +1581,12 @@ import path8 from "path";
1401
1581
  import { execa as execa4 } from "execa";
1402
1582
  var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime environment").action(async () => {
1403
1583
  const platform = detectPlatform();
1404
- const check = (ok) => ok ? chalk7.green("\u2713") : chalk7.red("\u2717");
1584
+ const check = (ok) => ok ? chalk8.green("\u2713") : chalk8.red("\u2717");
1405
1585
  console.log("");
1406
- console.log(chalk7.bold(" NEXUS Doctor"));
1586
+ console.log(chalk8.bold(" NEXUS Doctor"));
1407
1587
  console.log("");
1408
1588
  const nodeOk = Number(process.versions.node.split(".")[0]) >= 18;
1409
- console.log(` ${check(nodeOk)} Node.js ${process.versions.node} ${nodeOk ? "" : chalk7.red("(need >= 18)")}`);
1589
+ console.log(` ${check(nodeOk)} Node.js ${process.versions.node} ${nodeOk ? "" : chalk8.red("(need >= 18)")}`);
1410
1590
  console.log(` ${check(true)} Platform: ${platform.os} ${platform.arch}`);
1411
1591
  const qemuOk = await isQemuInstalled(platform);
1412
1592
  if (qemuOk) {
@@ -1446,14 +1626,14 @@ var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime e
1446
1626
  });
1447
1627
  server.listen(port);
1448
1628
  });
1449
- console.log(` ${check(available)} Port ${port} (${name}) ${available ? "available" : chalk7.red("in use")}`);
1629
+ console.log(` ${check(available)} Port ${port} (${name}) ${available ? "available" : chalk8.red("in use")}`);
1450
1630
  } catch {
1451
1631
  console.log(` ${check(false)} Port ${port} (${name}) \u2014 check failed`);
1452
1632
  }
1453
1633
  }
1454
1634
  }
1455
1635
  const biosOk = fs7.existsSync(platform.biosPath);
1456
- console.log(` ${check(biosOk)} UEFI firmware ${biosOk ? "" : chalk7.dim(platform.biosPath)}`);
1636
+ console.log(` ${check(biosOk)} UEFI firmware ${biosOk ? "" : chalk8.dim(platform.biosPath)}`);
1457
1637
  console.log("");
1458
1638
  if (qemuOk && isoTool && biosOk) {
1459
1639
  log.success("Environment ready for NEXUS");
@@ -1564,7 +1744,7 @@ var updateCommand = new Command7("update").description("Update NEXUS to the late
1564
1744
 
1565
1745
  // src/commands/destroy.ts
1566
1746
  import { Command as Command8 } from "commander";
1567
- import chalk8 from "chalk";
1747
+ import chalk9 from "chalk";
1568
1748
  import fs9 from "fs";
1569
1749
  import { input as input2 } from "@inquirer/prompts";
1570
1750
  init_secrets();
@@ -1574,11 +1754,11 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1574
1754
  const config = loadConfig();
1575
1755
  if (!opts.force) {
1576
1756
  console.log("");
1577
- console.log(chalk8.red.bold(" This will permanently delete:"));
1578
- console.log(chalk8.red(" - NEXUS VM and all data inside it"));
1579
- console.log(chalk8.red(" - VM disk images"));
1580
- console.log(chalk8.red(" - SSH keys"));
1581
- console.log(chalk8.red(" - Configuration and API keys"));
1757
+ console.log(chalk9.red.bold(" This will permanently delete:"));
1758
+ console.log(chalk9.red(" - NEXUS VM and all data inside it"));
1759
+ console.log(chalk9.red(" - VM disk images"));
1760
+ console.log(chalk9.red(" - SSH keys"));
1761
+ console.log(chalk9.red(" - Configuration and API keys"));
1582
1762
  console.log("");
1583
1763
  const confirm2 = await input2({
1584
1764
  message: 'Type "destroy" to confirm:'
@@ -1622,7 +1802,7 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1622
1802
  // src/commands/keys.ts
1623
1803
  import { Command as Command9 } from "commander";
1624
1804
  import { password as password2 } from "@inquirer/prompts";
1625
- import chalk9 from "chalk";
1805
+ import chalk10 from "chalk";
1626
1806
  init_secrets();
1627
1807
  init_dlp();
1628
1808
  var keysCommand = new Command9("keys").description("Manage API keys");
@@ -1632,10 +1812,10 @@ keysCommand.command("list").description("Show configured API keys (masked)").act
1632
1812
  log.error("No keys configured. Run: buildwithnexus init");
1633
1813
  process.exit(1);
1634
1814
  }
1635
- console.log(chalk9.bold("\n Configured Keys\n"));
1815
+ console.log(chalk10.bold("\n Configured Keys\n"));
1636
1816
  for (const [name, value] of Object.entries(keys)) {
1637
1817
  if (value) {
1638
- console.log(` ${chalk9.cyan(name.padEnd(24))} ${maskKey(value)}`);
1818
+ console.log(` ${chalk10.cyan(name.padEnd(24))} ${maskKey(value)}`);
1639
1819
  }
1640
1820
  }
1641
1821
  console.log("");
@@ -1700,18 +1880,18 @@ var sshCommand = new Command10("ssh").description("Open an SSH session into the
1700
1880
 
1701
1881
  // src/commands/brainstorm.ts
1702
1882
  import { Command as Command11 } from "commander";
1703
- import chalk10 from "chalk";
1883
+ import chalk11 from "chalk";
1704
1884
  import { input as input3 } from "@inquirer/prompts";
1705
1885
  init_secrets();
1706
1886
  init_qemu();
1707
1887
  init_ssh();
1708
1888
  init_dlp();
1709
- var COS_PREFIX = chalk10.bold.cyan(" Chief of Staff");
1710
- var YOU_PREFIX = chalk10.bold.white(" You");
1711
- var DIVIDER = chalk10.dim(" " + "\u2500".repeat(56));
1889
+ var COS_PREFIX = chalk11.bold.cyan(" Chief of Staff");
1890
+ var YOU_PREFIX = chalk11.bold.white(" You");
1891
+ var DIVIDER = chalk11.dim(" " + "\u2500".repeat(56));
1712
1892
  function formatResponse(text) {
1713
1893
  const lines = text.split("\n");
1714
- return lines.map((line) => chalk10.white(" " + line)).join("\n");
1894
+ return lines.map((line) => chalk11.white(" " + line)).join("\n");
1715
1895
  }
1716
1896
  async function sendMessage(sshPort, message, source) {
1717
1897
  const payload = JSON.stringify({ message, source });
@@ -1754,13 +1934,13 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1754
1934
  }
1755
1935
  succeed(spinner, "Connected to NEXUS");
1756
1936
  console.log("");
1757
- console.log(chalk10.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1758
- console.log(chalk10.bold(" \u2551 ") + chalk10.bold.cyan("NEXUS Brainstorm Session") + chalk10.bold(" \u2551"));
1759
- console.log(chalk10.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1760
- console.log(chalk10.bold(" \u2551 ") + chalk10.dim("The Chief of Staff will discuss your idea with the".padEnd(55)) + chalk10.bold("\u2551"));
1761
- console.log(chalk10.bold(" \u2551 ") + chalk10.dim("NEXUS team and share their recommendations.".padEnd(55)) + chalk10.bold("\u2551"));
1762
- console.log(chalk10.bold(" \u2551 ") + chalk10.dim("Type 'exit' or 'quit' to end the session.".padEnd(55)) + chalk10.bold("\u2551"));
1763
- console.log(chalk10.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1937
+ console.log(chalk11.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1938
+ console.log(chalk11.bold(" \u2551 ") + chalk11.bold.cyan("NEXUS Brainstorm Session") + chalk11.bold(" \u2551"));
1939
+ console.log(chalk11.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1940
+ console.log(chalk11.bold(" \u2551 ") + chalk11.dim("The Chief of Staff will discuss your idea with the".padEnd(55)) + chalk11.bold("\u2551"));
1941
+ console.log(chalk11.bold(" \u2551 ") + chalk11.dim("NEXUS team and share their recommendations.".padEnd(55)) + chalk11.bold("\u2551"));
1942
+ console.log(chalk11.bold(" \u2551 ") + chalk11.dim("Type 'exit' or 'quit' to end the session.".padEnd(55)) + chalk11.bold("\u2551"));
1943
+ console.log(chalk11.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1764
1944
  console.log("");
1765
1945
  let idea = ideaWords.length > 0 ? ideaWords.join(" ") : "";
1766
1946
  if (!idea) {
@@ -1777,7 +1957,7 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1777
1957
  while (true) {
1778
1958
  turn++;
1779
1959
  if (turn === 1) {
1780
- console.log(`${YOU_PREFIX}: ${chalk10.white(idea)}`);
1960
+ console.log(`${YOU_PREFIX}: ${chalk11.white(idea)}`);
1781
1961
  }
1782
1962
  console.log(DIVIDER);
1783
1963
  const thinking = createSpinner(
@@ -1795,17 +1975,17 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1795
1975
  console.log(formatResponse(redact(response)));
1796
1976
  console.log(DIVIDER);
1797
1977
  const followUp = await input3({
1798
- message: chalk10.bold("You:")
1978
+ message: chalk11.bold("You:")
1799
1979
  });
1800
1980
  const trimmed = followUp.trim().toLowerCase();
1801
1981
  if (!trimmed || trimmed === "exit" || trimmed === "quit" || trimmed === "q") {
1802
1982
  console.log("");
1803
1983
  log.success("Brainstorm session ended");
1804
- console.log(chalk10.dim(" Run again anytime: buildwithnexus brainstorm"));
1984
+ console.log(chalk11.dim(" Run again anytime: buildwithnexus brainstorm"));
1805
1985
  console.log("");
1806
1986
  return;
1807
1987
  }
1808
- console.log(`${YOU_PREFIX}: ${chalk10.white(followUp)}`);
1988
+ console.log(`${YOU_PREFIX}: ${chalk11.white(followUp)}`);
1809
1989
  currentMessage = `[BRAINSTORM FOLLOW-UP] The CEO responds: ${followUp}`;
1810
1990
  }
1811
1991
  } catch (err) {
@@ -1820,9 +2000,205 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1820
2000
  }
1821
2001
  });
1822
2002
 
1823
- // src/commands/shell.ts
2003
+ // src/commands/ninety-nine.ts
1824
2004
  import { Command as Command12 } from "commander";
1825
- import chalk13 from "chalk";
2005
+ import chalk12 from "chalk";
2006
+ import { input as input4 } from "@inquirer/prompts";
2007
+ init_secrets();
2008
+ init_qemu();
2009
+ init_ssh();
2010
+ init_dlp();
2011
+ var AGENT_PREFIX = chalk12.bold.green(" 99 \u276F");
2012
+ var YOU_PREFIX2 = chalk12.bold.white(" You");
2013
+ var DIVIDER2 = chalk12.dim(" " + "\u2500".repeat(56));
2014
+ function formatAgentActivity(text) {
2015
+ const lines = text.split("\n");
2016
+ return lines.map((line) => chalk12.white(" " + line)).join("\n");
2017
+ }
2018
+ function parsePrefixes(instruction) {
2019
+ const files = [];
2020
+ const rules = [];
2021
+ const tokens = instruction.split(/\s+/);
2022
+ const remaining = [];
2023
+ for (const token of tokens) {
2024
+ if (token.startsWith("@") && token.length > 1) {
2025
+ files.push(token.slice(1));
2026
+ } else if (token.startsWith("#") && token.length > 1) {
2027
+ rules.push(token.slice(1));
2028
+ } else {
2029
+ remaining.push(token);
2030
+ }
2031
+ }
2032
+ return { cleaned: remaining.join(" "), files, rules };
2033
+ }
2034
+ async function sendToNexus(sshPort, instruction, files, rules, cwd) {
2035
+ const message = `[99] ${instruction}`;
2036
+ const payload = JSON.stringify({
2037
+ message,
2038
+ source: "99",
2039
+ context: { files, rules, cwd }
2040
+ });
2041
+ const escaped = shellEscape(payload);
2042
+ const { stdout, code } = await sshExec(
2043
+ sshPort,
2044
+ `curl -sf -X POST http://localhost:4200/message -H 'Content-Type: application/json' -d ${escaped}`
2045
+ );
2046
+ if (code !== 0) {
2047
+ throw new Error("NEXUS engine returned a non-zero exit code");
2048
+ }
2049
+ try {
2050
+ const parsed = JSON.parse(stdout);
2051
+ return parsed.response ?? parsed.message ?? stdout;
2052
+ } catch {
2053
+ return stdout;
2054
+ }
2055
+ }
2056
+ var ninetyNineCommand = new Command12("99").description("AI pair-programming session backed by the full NEXUS agent engine").argument("[instruction...]", "What to build, edit, or debug").option("--edit <file>", "AI-assisted edit for a specific file").option("--search <query>", "Contextual codebase search").option("--debug", "Debug current issue with NEXUS agent assistance").action(async (instructionWords, opts) => {
2057
+ try {
2058
+ const config = loadConfig();
2059
+ if (!config) {
2060
+ log.error("No NEXUS configuration found. Run: buildwithnexus init");
2061
+ process.exit(1);
2062
+ }
2063
+ if (!isVmRunning()) {
2064
+ log.error("VM is not running. Start it with: buildwithnexus start");
2065
+ process.exit(1);
2066
+ }
2067
+ const spinner = createSpinner("Connecting to NEXUS...");
2068
+ spinner.start();
2069
+ const { stdout: healthCheck, code: healthCode } = await sshExec(
2070
+ config.sshPort,
2071
+ "curl -sf http://localhost:4200/health"
2072
+ );
2073
+ if (healthCode !== 0 || !healthCheck.includes("ok")) {
2074
+ fail(spinner, "NEXUS server is not healthy");
2075
+ log.warn("Check status: buildwithnexus status");
2076
+ process.exit(1);
2077
+ }
2078
+ succeed(spinner, "Connected to NEXUS");
2079
+ console.log("");
2080
+ console.log(chalk12.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2081
+ console.log(chalk12.bold(" \u2551 ") + chalk12.bold.green("/99 Pair Programming") + chalk12.dim(" \u2014 powered by NEXUS") + chalk12.bold(" \u2551"));
2082
+ console.log(chalk12.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2083
+ console.log(chalk12.bold(" \u2551 ") + chalk12.dim("Describe what you want changed. NEXUS engineers".padEnd(55)) + chalk12.bold("\u2551"));
2084
+ console.log(chalk12.bold(" \u2551 ") + chalk12.dim("analyze and modify your code in real time.".padEnd(55)) + chalk12.bold("\u2551"));
2085
+ console.log(chalk12.bold(" \u2551 ") + chalk12.dim("Use @file to attach context, #rule to load rules.".padEnd(55)) + chalk12.bold("\u2551"));
2086
+ console.log(chalk12.bold(" \u2551 ") + chalk12.dim("Type 'exit' or 'quit' to end the session.".padEnd(55)) + chalk12.bold("\u2551"));
2087
+ console.log(chalk12.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2088
+ console.log("");
2089
+ const cwd = process.cwd();
2090
+ if (opts.edit) {
2091
+ const instruction = instructionWords.join(" ") || await input4({
2092
+ message: `What change should be made to ${opts.edit}?`
2093
+ });
2094
+ const { cleaned, files, rules } = parsePrefixes(instruction);
2095
+ const fullInstruction = `Edit file ${opts.edit}: ${cleaned}`;
2096
+ console.log(`${YOU_PREFIX2}: ${chalk12.white(fullInstruction)}`);
2097
+ console.log(DIVIDER2);
2098
+ const thinking = createSpinner("NEXUS engineers analyzing the file...");
2099
+ thinking.start();
2100
+ const response = await sendToNexus(config.sshPort, fullInstruction, [opts.edit, ...files], rules, cwd);
2101
+ thinking.stop();
2102
+ thinking.clear();
2103
+ console.log(`${AGENT_PREFIX}`);
2104
+ console.log(formatAgentActivity(redact(response)));
2105
+ console.log(DIVIDER2);
2106
+ return;
2107
+ }
2108
+ if (opts.search) {
2109
+ const fullInstruction = `Search the codebase for: ${opts.search}`;
2110
+ console.log(`${YOU_PREFIX2}: ${chalk12.white(fullInstruction)}`);
2111
+ console.log(DIVIDER2);
2112
+ const thinking = createSpinner("Searching with NEXUS context...");
2113
+ thinking.start();
2114
+ const response = await sendToNexus(config.sshPort, fullInstruction, [], [], cwd);
2115
+ thinking.stop();
2116
+ thinking.clear();
2117
+ console.log(`${AGENT_PREFIX}`);
2118
+ console.log(formatAgentActivity(redact(response)));
2119
+ console.log(DIVIDER2);
2120
+ return;
2121
+ }
2122
+ if (opts.debug) {
2123
+ const fullInstruction = "Debug the current issue \u2014 analyze recent errors, identify the root cause, and propose a fix";
2124
+ console.log(`${YOU_PREFIX2}: ${chalk12.white(fullInstruction)}`);
2125
+ console.log(DIVIDER2);
2126
+ const thinking = createSpinner("NEXUS debugger agent analyzing...");
2127
+ thinking.start();
2128
+ const response = await sendToNexus(config.sshPort, fullInstruction, [], [], cwd);
2129
+ thinking.stop();
2130
+ thinking.clear();
2131
+ console.log(`${AGENT_PREFIX}`);
2132
+ console.log(formatAgentActivity(redact(response)));
2133
+ console.log(DIVIDER2);
2134
+ return;
2135
+ }
2136
+ let initialInstruction = instructionWords.length > 0 ? instructionWords.join(" ") : "";
2137
+ if (!initialInstruction) {
2138
+ initialInstruction = await input4({
2139
+ message: chalk12.green("99 \u276F")
2140
+ });
2141
+ if (!initialInstruction.trim()) {
2142
+ log.warn("No instruction provided");
2143
+ return;
2144
+ }
2145
+ }
2146
+ let turn = 0;
2147
+ let currentInstruction = initialInstruction;
2148
+ while (true) {
2149
+ turn++;
2150
+ const { cleaned, files, rules } = parsePrefixes(currentInstruction);
2151
+ const display = currentInstruction;
2152
+ const nexusInstruction = cleaned || currentInstruction;
2153
+ if (turn === 1) {
2154
+ console.log(`${YOU_PREFIX2}: ${chalk12.white(display)}`);
2155
+ }
2156
+ if (files.length > 0) {
2157
+ console.log(chalk12.dim(` Attaching: ${files.join(", ")}`));
2158
+ }
2159
+ if (rules.length > 0) {
2160
+ console.log(chalk12.dim(` Rules: ${rules.join(", ")}`));
2161
+ }
2162
+ console.log(DIVIDER2);
2163
+ const thinking = createSpinner(
2164
+ turn === 1 ? "NEXUS agents analyzing and implementing..." : "NEXUS agents processing..."
2165
+ );
2166
+ thinking.start();
2167
+ const response = await sendToNexus(config.sshPort, nexusInstruction, files, rules, cwd);
2168
+ thinking.stop();
2169
+ thinking.clear();
2170
+ console.log(`${AGENT_PREFIX}`);
2171
+ console.log(formatAgentActivity(redact(response)));
2172
+ console.log(DIVIDER2);
2173
+ const followUp = await input4({
2174
+ message: chalk12.green("99 \u276F")
2175
+ });
2176
+ const trimmed = followUp.trim().toLowerCase();
2177
+ if (!trimmed || trimmed === "exit" || trimmed === "quit" || trimmed === "q") {
2178
+ console.log("");
2179
+ log.success("/99 session ended");
2180
+ console.log(chalk12.dim(" Run again: buildwithnexus 99"));
2181
+ console.log("");
2182
+ return;
2183
+ }
2184
+ console.log(`${YOU_PREFIX2}: ${chalk12.white(followUp)}`);
2185
+ currentInstruction = followUp;
2186
+ }
2187
+ } catch (err) {
2188
+ if (err.code === "ERR_USE_AFTER_CLOSE") {
2189
+ console.log("");
2190
+ log.success("/99 session ended");
2191
+ return;
2192
+ }
2193
+ const safeErr = redactError(err);
2194
+ log.error(`/99 failed: ${safeErr.message}`);
2195
+ process.exit(1);
2196
+ }
2197
+ });
2198
+
2199
+ // src/commands/shell.ts
2200
+ import { Command as Command13 } from "commander";
2201
+ import chalk15 from "chalk";
1826
2202
  init_secrets();
1827
2203
  init_qemu();
1828
2204
  init_ssh();
@@ -1833,7 +2209,7 @@ init_secrets();
1833
2209
  import readline from "readline";
1834
2210
  import fs10 from "fs";
1835
2211
  import path11 from "path";
1836
- import chalk11 from "chalk";
2212
+ import chalk13 from "chalk";
1837
2213
  var HISTORY_FILE = path11.join(NEXUS_HOME2, "shell_history");
1838
2214
  var MAX_HISTORY = 1e3;
1839
2215
  var Repl = class {
@@ -1868,7 +2244,7 @@ var Repl = class {
1868
2244
  this.rl = readline.createInterface({
1869
2245
  input: process.stdin,
1870
2246
  output: process.stdout,
1871
- prompt: chalk11.bold.cyan("nexus") + chalk11.dim(" \u276F "),
2247
+ prompt: chalk13.bold.cyan("nexus") + chalk13.dim(" \u276F "),
1872
2248
  history: this.history,
1873
2249
  historySize: MAX_HISTORY,
1874
2250
  terminal: true
@@ -1898,25 +2274,25 @@ var Repl = class {
1898
2274
  try {
1899
2275
  await cmd.handler();
1900
2276
  } catch (err) {
1901
- console.log(chalk11.red(` \u2717 Command failed: ${err.message}`));
2277
+ console.log(chalk13.red(` \u2717 Command failed: ${err.message}`));
1902
2278
  }
1903
2279
  this.rl?.prompt();
1904
2280
  return;
1905
2281
  }
1906
- console.log(chalk11.yellow(` Unknown command: /${cmdName}. Type /help for available commands.`));
2282
+ console.log(chalk13.yellow(` Unknown command: /${cmdName}. Type /help for available commands.`));
1907
2283
  this.rl?.prompt();
1908
2284
  return;
1909
2285
  }
1910
2286
  try {
1911
2287
  await this.onMessage(trimmed);
1912
2288
  } catch (err) {
1913
- console.log(chalk11.red(` \u2717 ${err.message}`));
2289
+ console.log(chalk13.red(` \u2717 ${err.message}`));
1914
2290
  }
1915
2291
  this.rl?.prompt();
1916
2292
  });
1917
2293
  this.rl.on("close", () => {
1918
2294
  this.saveHistory();
1919
- console.log(chalk11.dim("\n Session ended."));
2295
+ console.log(chalk13.dim("\n Session ended."));
1920
2296
  process.exit(0);
1921
2297
  });
1922
2298
  this.rl.on("SIGINT", () => {
@@ -1925,15 +2301,15 @@ var Repl = class {
1925
2301
  }
1926
2302
  showHelp() {
1927
2303
  console.log("");
1928
- console.log(chalk11.bold(" Available Commands:"));
1929
- console.log(chalk11.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2304
+ console.log(chalk13.bold(" Available Commands:"));
2305
+ console.log(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1930
2306
  for (const [name, cmd] of this.commands) {
1931
- console.log(` ${chalk11.cyan("/" + name.padEnd(14))} ${chalk11.dim(cmd.description)}`);
2307
+ console.log(` ${chalk13.cyan("/" + name.padEnd(14))} ${chalk13.dim(cmd.description)}`);
1932
2308
  }
1933
- console.log(` ${chalk11.cyan("/help".padEnd(15))} ${chalk11.dim("Show this help message")}`);
1934
- console.log(` ${chalk11.cyan("/exit".padEnd(15))} ${chalk11.dim("Exit the shell")}`);
1935
- console.log(chalk11.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1936
- console.log(chalk11.dim(" Type anything else to chat with NEXUS"));
2309
+ console.log(` ${chalk13.cyan("/help".padEnd(15))} ${chalk13.dim("Show this help message")}`);
2310
+ console.log(` ${chalk13.cyan("/exit".padEnd(15))} ${chalk13.dim("Exit the shell")}`);
2311
+ console.log(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2312
+ console.log(chalk13.dim(" Type anything else to chat with NEXUS"));
1937
2313
  console.log("");
1938
2314
  }
1939
2315
  write(text) {
@@ -1959,17 +2335,17 @@ var Repl = class {
1959
2335
  init_ssh();
1960
2336
  init_secrets();
1961
2337
  init_dlp();
1962
- import chalk12 from "chalk";
2338
+ import chalk14 from "chalk";
1963
2339
  var ROLE_COLORS = {
1964
- "Chief of Staff": chalk12.bold.cyan,
1965
- "VP Engineering": chalk12.bold.blue,
1966
- "VP Product": chalk12.bold.magenta,
1967
- "Senior Engineer": chalk12.bold.green,
1968
- "Engineer": chalk12.green,
1969
- "QA Lead": chalk12.bold.yellow,
1970
- "Security Engineer": chalk12.bold.red,
1971
- "DevOps Engineer": chalk12.bold.hex("#FF8C00"),
1972
- "default": chalk12.bold.white
2340
+ "Chief of Staff": chalk14.bold.cyan,
2341
+ "VP Engineering": chalk14.bold.blue,
2342
+ "VP Product": chalk14.bold.magenta,
2343
+ "Senior Engineer": chalk14.bold.green,
2344
+ "Engineer": chalk14.green,
2345
+ "QA Lead": chalk14.bold.yellow,
2346
+ "Security Engineer": chalk14.bold.red,
2347
+ "DevOps Engineer": chalk14.bold.hex("#FF8C00"),
2348
+ "default": chalk14.bold.white
1973
2349
  };
1974
2350
  function getColor(role) {
1975
2351
  if (!role) return ROLE_COLORS["default"];
@@ -1980,15 +2356,15 @@ function formatEvent(event) {
1980
2356
  const prefix = event.role ? color(` [${event.role}]`) : "";
1981
2357
  switch (event.type) {
1982
2358
  case "agent_thinking":
1983
- return `${prefix} ${chalk12.dim("thinking...")}`;
2359
+ return `${prefix} ${chalk14.dim("thinking...")}`;
1984
2360
  case "agent_response":
1985
2361
  return `${prefix} ${redact(event.content ?? "")}`;
1986
2362
  case "task_delegated":
1987
- return `${prefix} ${chalk12.dim("\u2192")} delegated to ${chalk12.bold(event.target ?? "agent")}`;
2363
+ return `${prefix} ${chalk14.dim("\u2192")} delegated to ${chalk14.bold(event.target ?? "agent")}`;
1988
2364
  case "agent_complete":
1989
- return `${prefix} ${chalk12.green("\u2713")} ${chalk12.dim("complete")}`;
2365
+ return `${prefix} ${chalk14.green("\u2713")} ${chalk14.dim("complete")}`;
1990
2366
  case "error":
1991
- return ` ${chalk12.red("\u2717")} ${redact(event.content ?? "Unknown error")}`;
2367
+ return ` ${chalk14.red("\u2717")} ${redact(event.content ?? "Unknown error")}`;
1992
2368
  case "heartbeat":
1993
2369
  return null;
1994
2370
  default:
@@ -2071,19 +2447,19 @@ async function sendMessage2(sshPort, message) {
2071
2447
  }
2072
2448
  }
2073
2449
  function showShellBanner(health) {
2074
- const check = chalk13.green("\u2713");
2075
- const cross = chalk13.red("\u2717");
2450
+ const check = chalk15.green("\u2713");
2451
+ const cross = chalk15.red("\u2717");
2076
2452
  console.log("");
2077
- console.log(chalk13.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2078
- console.log(chalk13.bold(" \u2551 ") + chalk13.bold.cyan("NEXUS Interactive Shell") + chalk13.bold(" \u2551"));
2079
- console.log(chalk13.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2080
- console.log(chalk13.bold(" \u2551 ") + `${health.vmRunning ? check : cross} VM ${health.sshReady ? check : cross} SSH ${health.dockerReady ? check : cross} Docker ${health.serverHealthy ? check : cross} Engine`.padEnd(55) + chalk13.bold("\u2551"));
2453
+ console.log(chalk15.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2454
+ console.log(chalk15.bold(" \u2551 ") + chalk15.bold.cyan("NEXUS Interactive Shell") + chalk15.bold(" \u2551"));
2455
+ console.log(chalk15.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2456
+ console.log(chalk15.bold(" \u2551 ") + `${health.vmRunning ? check : cross} VM ${health.sshReady ? check : cross} SSH ${health.dockerReady ? check : cross} Docker ${health.serverHealthy ? check : cross} Engine`.padEnd(55) + chalk15.bold("\u2551"));
2081
2457
  if (health.tunnelUrl) {
2082
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim(`Tunnel: ${health.tunnelUrl}`.padEnd(55)) + chalk13.bold("\u2551"));
2458
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim(`Tunnel: ${health.tunnelUrl}`.padEnd(55)) + chalk15.bold("\u2551"));
2083
2459
  }
2084
- console.log(chalk13.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2085
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim("Type naturally to chat \xB7 /help for commands".padEnd(55)) + chalk13.bold("\u2551"));
2086
- console.log(chalk13.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2460
+ console.log(chalk15.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2461
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim("Type naturally to chat \xB7 /help for commands".padEnd(55)) + chalk15.bold("\u2551"));
2462
+ console.log(chalk15.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2087
2463
  console.log("");
2088
2464
  }
2089
2465
  async function getAgentList(sshPort) {
@@ -2093,13 +2469,13 @@ async function getAgentList(sshPort) {
2093
2469
  const agents = JSON.parse(stdout);
2094
2470
  if (!Array.isArray(agents)) return stdout;
2095
2471
  const lines = [""];
2096
- lines.push(chalk13.bold(" Registered Agents:"));
2097
- lines.push(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2472
+ lines.push(chalk15.bold(" Registered Agents:"));
2473
+ lines.push(chalk15.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2098
2474
  for (const agent of agents) {
2099
2475
  const name = agent.name ?? agent.id ?? "unknown";
2100
2476
  const role = agent.role ?? "";
2101
- const status = agent.status === "active" ? chalk13.green("\u25CF") : chalk13.dim("\u25CB");
2102
- lines.push(` ${status} ${chalk13.bold(name.padEnd(24))} ${chalk13.dim(role)}`);
2477
+ const status = agent.status === "active" ? chalk15.green("\u25CF") : chalk15.dim("\u25CB");
2478
+ lines.push(` ${status} ${chalk15.bold(name.padEnd(24))} ${chalk15.dim(role)}`);
2103
2479
  }
2104
2480
  lines.push("");
2105
2481
  return lines.join("\n");
@@ -2111,11 +2487,11 @@ async function getStatus(sshPort) {
2111
2487
  try {
2112
2488
  const vmRunning = isVmRunning();
2113
2489
  const health = await checkHealth(sshPort, vmRunning);
2114
- const check = chalk13.green("\u2713");
2115
- const cross = chalk13.red("\u2717");
2490
+ const check = chalk15.green("\u2713");
2491
+ const cross = chalk15.red("\u2717");
2116
2492
  const lines = [""];
2117
- lines.push(chalk13.bold(" System Status:"));
2118
- lines.push(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2493
+ lines.push(chalk15.bold(" System Status:"));
2494
+ lines.push(chalk15.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2119
2495
  lines.push(` ${health.vmRunning ? check : cross} Virtual Machine`);
2120
2496
  lines.push(` ${health.sshReady ? check : cross} SSH Connection`);
2121
2497
  lines.push(` ${health.dockerReady ? check : cross} Docker Engine`);
@@ -2137,17 +2513,17 @@ async function getCost(sshPort) {
2137
2513
  if (code !== 0) return " Could not retrieve cost data";
2138
2514
  const data = JSON.parse(stdout);
2139
2515
  const lines = [""];
2140
- lines.push(chalk13.bold(" Token Costs:"));
2141
- lines.push(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2516
+ lines.push(chalk15.bold(" Token Costs:"));
2517
+ lines.push(chalk15.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2142
2518
  if (data.total !== void 0) {
2143
- lines.push(` Total: ${chalk13.bold.green("$" + Number(data.total).toFixed(4))}`);
2519
+ lines.push(` Total: ${chalk15.bold.green("$" + Number(data.total).toFixed(4))}`);
2144
2520
  }
2145
2521
  if (data.today !== void 0) {
2146
- lines.push(` Today: ${chalk13.bold("$" + Number(data.today).toFixed(4))}`);
2522
+ lines.push(` Today: ${chalk15.bold("$" + Number(data.today).toFixed(4))}`);
2147
2523
  }
2148
2524
  if (data.by_agent && typeof data.by_agent === "object") {
2149
2525
  lines.push("");
2150
- lines.push(chalk13.dim(" By Agent:"));
2526
+ lines.push(chalk15.dim(" By Agent:"));
2151
2527
  for (const [agent, cost] of Object.entries(data.by_agent)) {
2152
2528
  lines.push(` ${agent.padEnd(20)} $${Number(cost).toFixed(4)}`);
2153
2529
  }
@@ -2158,7 +2534,7 @@ async function getCost(sshPort) {
2158
2534
  return " Could not retrieve cost data";
2159
2535
  }
2160
2536
  }
2161
- var shellCommand2 = new Command12("shell").description("Launch the interactive NEXUS shell").action(async () => {
2537
+ var shellCommand2 = new Command13("shell").description("Launch the interactive NEXUS shell").action(async () => {
2162
2538
  try {
2163
2539
  const config = loadConfig();
2164
2540
  if (!config) {
@@ -2192,10 +2568,10 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2192
2568
  thinkingSpinner.stop();
2193
2569
  thinkingSpinner.clear();
2194
2570
  console.log("");
2195
- console.log(chalk13.bold.cyan(" Chief of Staff:"));
2571
+ console.log(chalk15.bold.cyan(" Chief of Staff:"));
2196
2572
  const lines = redact(response).split("\n");
2197
2573
  for (const line of lines) {
2198
- console.log(chalk13.white(" " + line));
2574
+ console.log(chalk15.white(" " + line));
2199
2575
  }
2200
2576
  console.log("");
2201
2577
  } catch (err) {
@@ -2209,17 +2585,17 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2209
2585
  description: "Brainstorm with the full NEXUS org (led by Chief of Staff)",
2210
2586
  handler: async () => {
2211
2587
  console.log("");
2212
- console.log(chalk13.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2213
- console.log(chalk13.bold(" \u2551 ") + chalk13.bold.cyan("NEXUS Brainstorm Session") + chalk13.bold(" \u2551"));
2214
- console.log(chalk13.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2215
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim("The Chief of Staff will facilitate a discussion with".padEnd(55)) + chalk13.bold("\u2551"));
2216
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim("the full NEXUS org to refine your idea. When ready,".padEnd(55)) + chalk13.bold("\u2551"));
2217
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim("NEXUS will draft an execution plan for your review.".padEnd(55)) + chalk13.bold("\u2551"));
2218
- console.log(chalk13.bold(" \u2551 ") + chalk13.dim("Type 'exit' to end brainstorm. Type 'plan' to hand off.".padEnd(55)) + chalk13.bold("\u2551"));
2219
- console.log(chalk13.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2588
+ console.log(chalk15.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2589
+ console.log(chalk15.bold(" \u2551 ") + chalk15.bold.cyan("NEXUS Brainstorm Session") + chalk15.bold(" \u2551"));
2590
+ console.log(chalk15.bold(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2591
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim("The Chief of Staff will facilitate a discussion with".padEnd(55)) + chalk15.bold("\u2551"));
2592
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim("the full NEXUS org to refine your idea. When ready,".padEnd(55)) + chalk15.bold("\u2551"));
2593
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim("NEXUS will draft an execution plan for your review.".padEnd(55)) + chalk15.bold("\u2551"));
2594
+ console.log(chalk15.bold(" \u2551 ") + chalk15.dim("Type 'exit' to end brainstorm. Type 'plan' to hand off.".padEnd(55)) + chalk15.bold("\u2551"));
2595
+ console.log(chalk15.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2220
2596
  console.log("");
2221
- const { input: input4 } = await import("@inquirer/prompts");
2222
- const idea = await input4({ message: "What would you like to brainstorm?" });
2597
+ const { input: input5 } = await import("@inquirer/prompts");
2598
+ const idea = await input5({ message: "What would you like to brainstorm?" });
2223
2599
  if (!idea.trim()) return;
2224
2600
  let currentMessage = `[BRAINSTORM] The CEO wants to brainstorm the following idea. As Chief of Staff, facilitate a discussion with the entire NEXUS organization \u2014 involve VPs, engineers, QA, security, and any relevant specialists. Gather diverse perspectives, identify risks and opportunities, and help refine the idea. Do NOT execute \u2014 only discuss, analyze, and recommend. Idea: ${idea}`;
2225
2601
  while (true) {
@@ -2229,12 +2605,12 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2229
2605
  brainstormSpinner.stop();
2230
2606
  brainstormSpinner.clear();
2231
2607
  console.log("");
2232
- console.log(chalk13.bold.cyan(" Chief of Staff:"));
2608
+ console.log(chalk15.bold.cyan(" Chief of Staff:"));
2233
2609
  for (const line of redact(response).split("\n")) {
2234
- console.log(chalk13.white(" " + line));
2610
+ console.log(chalk15.white(" " + line));
2235
2611
  }
2236
2612
  console.log("");
2237
- const followUp = await input4({ message: chalk13.bold("You:") });
2613
+ const followUp = await input5({ message: chalk15.bold("You:") });
2238
2614
  const trimmed = followUp.trim().toLowerCase();
2239
2615
  if (!trimmed || trimmed === "exit" || trimmed === "quit") {
2240
2616
  console.log("");
@@ -2248,9 +2624,9 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2248
2624
  planSpinner.stop();
2249
2625
  planSpinner.clear();
2250
2626
  console.log("");
2251
- console.log(chalk13.bold.green(" Execution Plan:"));
2627
+ console.log(chalk15.bold.green(" Execution Plan:"));
2252
2628
  for (const line of redact(planResponse).split("\n")) {
2253
- console.log(chalk13.white(" " + line));
2629
+ console.log(chalk15.white(" " + line));
2254
2630
  }
2255
2631
  console.log("");
2256
2632
  log.success("Plan drafted. Use the shell to refine or approve.");
@@ -2290,17 +2666,17 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2290
2666
  handler: async () => {
2291
2667
  const { stdout } = await sshExec(config.sshPort, "tail -30 /home/nexus/.nexus/logs/server.log 2>/dev/null");
2292
2668
  console.log("");
2293
- console.log(chalk13.bold(" Recent Logs:"));
2294
- console.log(chalk13.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2669
+ console.log(chalk15.bold(" Recent Logs:"));
2670
+ console.log(chalk15.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2295
2671
  for (const line of redact(stdout).split("\n")) {
2296
- console.log(chalk13.dim(" " + line));
2672
+ console.log(chalk15.dim(" " + line));
2297
2673
  }
2298
2674
  console.log("");
2299
2675
  }
2300
2676
  });
2301
2677
  repl.registerCommand({
2302
2678
  name: "ssh",
2303
- description: "Open interactive SSH session",
2679
+ description: "Drop into the VM for debugging/inspection",
2304
2680
  handler: async () => {
2305
2681
  const { openInteractiveSsh: openInteractiveSsh2 } = await Promise.resolve().then(() => (init_ssh(), ssh_exports));
2306
2682
  eventStream.stop();
@@ -2308,6 +2684,136 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2308
2684
  eventStream.start();
2309
2685
  }
2310
2686
  });
2687
+ repl.registerCommand({
2688
+ name: "org-chart",
2689
+ description: "Display the NEXUS organizational hierarchy",
2690
+ handler: async () => {
2691
+ console.log("");
2692
+ console.log(chalk15.bold(" NEXUS Organizational Hierarchy"));
2693
+ console.log(chalk15.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2694
+ console.log(` ${chalk15.bold.white("You")} ${chalk15.dim("(CEO)")}`);
2695
+ console.log(` \u2514\u2500\u2500 ${chalk15.bold.cyan("Chief of Staff")} ${chalk15.dim("\u2014 orchestrates all work, your direct interface")}`);
2696
+ console.log(` \u251C\u2500\u2500 ${chalk15.bold.blue("VP Engineering")} ${chalk15.dim("\u2014 owns technical execution")}`);
2697
+ console.log(` \u2502 \u251C\u2500\u2500 ${chalk15.green("Senior Engineer")} ${chalk15.dim("\xD7 8 \u2014 implementation, refactoring")}`);
2698
+ console.log(` \u2502 \u251C\u2500\u2500 ${chalk15.green("Engineer")} ${chalk15.dim("\xD7 12 \u2014 feature work, bug fixes")}`);
2699
+ console.log(` \u2502 \u2514\u2500\u2500 ${chalk15.hex("#FF8C00")("DevOps Engineer")} ${chalk15.dim("\xD7 4 \u2014 CI/CD, Docker, infra")}`);
2700
+ console.log(` \u251C\u2500\u2500 ${chalk15.bold.magenta("VP Product")} ${chalk15.dim("\u2014 owns roadmap and priorities")}`);
2701
+ console.log(` \u2502 \u251C\u2500\u2500 ${chalk15.magenta("Product Manager")} ${chalk15.dim("\xD7 3 \u2014 specs, requirements")}`);
2702
+ console.log(` \u2502 \u2514\u2500\u2500 ${chalk15.magenta("Designer")} ${chalk15.dim("\xD7 2 \u2014 UI/UX, prototyping")}`);
2703
+ console.log(` \u251C\u2500\u2500 ${chalk15.bold.yellow("QA Lead")} ${chalk15.dim("\u2014 owns quality assurance")}`);
2704
+ console.log(` \u2502 \u2514\u2500\u2500 ${chalk15.yellow("QA Engineer")} ${chalk15.dim("\xD7 6 \u2014 testing, coverage, validation")}`);
2705
+ console.log(` \u251C\u2500\u2500 ${chalk15.bold.red("Security Engineer")} ${chalk15.dim("\xD7 4 \u2014 auth, scanning, compliance")}`);
2706
+ console.log(` \u2514\u2500\u2500 ${chalk15.bold.white("Knowledge Manager")} ${chalk15.dim("\u2014 RAG, documentation, learning")}`);
2707
+ console.log("");
2708
+ console.log(chalk15.dim(" 56 agents total \xB7 Self-learning ML pipeline"));
2709
+ console.log(chalk15.dim(" Full details: https://buildwithnexus.dev/overview"));
2710
+ console.log("");
2711
+ }
2712
+ });
2713
+ repl.registerCommand({
2714
+ name: "security",
2715
+ description: "Show the security posture of this runtime",
2716
+ handler: async () => {
2717
+ const { showSecurityPosture: showSecurityPosture2 } = await Promise.resolve().then(() => (init_banner(), banner_exports));
2718
+ showSecurityPosture2();
2719
+ }
2720
+ });
2721
+ repl.registerCommand({
2722
+ name: "tutorial",
2723
+ description: "Guided walkthrough of NEXUS capabilities",
2724
+ handler: async () => {
2725
+ const { input: input5 } = await import("@inquirer/prompts");
2726
+ const steps = [
2727
+ {
2728
+ title: "Welcome to NEXUS",
2729
+ content: [
2730
+ "NEXUS is a 56-agent autonomous engineering organization.",
2731
+ "You are the CEO. The Chief of Staff leads your team.",
2732
+ "",
2733
+ "When you type a request, the Chief of Staff:",
2734
+ " 1. Analyzes what needs to be done",
2735
+ " 2. Delegates to the right VPs and specialists",
2736
+ " 3. Agents collaborate, review each other's work",
2737
+ " 4. Results stream back to you in real time"
2738
+ ]
2739
+ },
2740
+ {
2741
+ title: "The Org Chart",
2742
+ content: [
2743
+ "You \u2192 Chief of Staff \u2192 VPs \u2192 Engineers",
2744
+ "",
2745
+ " VP Engineering \u2014 24 engineers, DevOps",
2746
+ " VP Product \u2014 PMs, designers",
2747
+ " QA Lead \u2014 6 QA engineers",
2748
+ " Security \u2014 4 security engineers",
2749
+ " Knowledge \u2014 RAG, documentation",
2750
+ "",
2751
+ "Run /org-chart to see the full hierarchy."
2752
+ ]
2753
+ },
2754
+ {
2755
+ title: "Try It: Natural Language",
2756
+ content: [
2757
+ "Just type what you want built. Examples:",
2758
+ "",
2759
+ ' "Build a REST API with JWT authentication"',
2760
+ ' "Fix the memory leak in the worker pool"',
2761
+ ' "Refactor the database layer to use connection pooling"',
2762
+ "",
2763
+ "NEXUS assigns the right agents automatically.",
2764
+ "You'll see their thinking and delegation in real time."
2765
+ ]
2766
+ },
2767
+ {
2768
+ title: "Brainstorming",
2769
+ content: [
2770
+ "Use /brainstorm to explore ideas before committing.",
2771
+ "",
2772
+ "The Chief of Staff facilitates an org-wide discussion:",
2773
+ " - Engineers assess technical feasibility",
2774
+ " - Security flags risks",
2775
+ " - Product suggests user impact",
2776
+ " - QA identifies testing needs",
2777
+ "",
2778
+ "Type 'plan' when ready to hand off for execution."
2779
+ ]
2780
+ },
2781
+ {
2782
+ title: "Monitoring & Control",
2783
+ content: [
2784
+ " /status \u2014 System health (VM, SSH, Docker, Engine)",
2785
+ " /agents \u2014 List all 56 registered agents",
2786
+ " /cost \u2014 Token usage and spend tracking",
2787
+ " /logs \u2014 Server logs for debugging",
2788
+ " /ssh \u2014 Drop into the VM directly",
2789
+ " /security \u2014 View the security posture",
2790
+ "",
2791
+ "You're in control. NEXUS executes."
2792
+ ]
2793
+ }
2794
+ ];
2795
+ for (let i = 0; i < steps.length; i++) {
2796
+ const step = steps[i];
2797
+ console.log("");
2798
+ console.log(chalk15.bold(` \u2500\u2500 ${chalk15.cyan(`Step ${i + 1}/${steps.length}`)} \u2500\u2500 ${step.title} \u2500\u2500`));
2799
+ console.log("");
2800
+ for (const line of step.content) {
2801
+ console.log(chalk15.white(" " + line));
2802
+ }
2803
+ console.log("");
2804
+ if (i < steps.length - 1) {
2805
+ const next = await input5({ message: chalk15.dim("Press Enter to continue (or 'skip' to exit)") });
2806
+ if (next.trim().toLowerCase() === "skip") {
2807
+ log.success("Tutorial ended. Type /help to see all commands.");
2808
+ return;
2809
+ }
2810
+ }
2811
+ }
2812
+ console.log("");
2813
+ log.success("Tutorial complete! Start typing to talk to NEXUS.");
2814
+ console.log("");
2815
+ }
2816
+ });
2311
2817
  process.on("SIGINT", () => {
2312
2818
  eventStream.stop();
2313
2819
  repl.stop();
@@ -2326,7 +2832,7 @@ var shellCommand2 = new Command12("shell").description("Launch the interactive N
2326
2832
  });
2327
2833
 
2328
2834
  // src/cli.ts
2329
- var cli = new Command13().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.3.0");
2835
+ var cli = new Command14().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.3.1");
2330
2836
  cli.addCommand(initCommand);
2331
2837
  cli.addCommand(startCommand);
2332
2838
  cli.addCommand(stopCommand);
@@ -2338,13 +2844,14 @@ cli.addCommand(destroyCommand);
2338
2844
  cli.addCommand(keysCommand);
2339
2845
  cli.addCommand(sshCommand);
2340
2846
  cli.addCommand(brainstormCommand);
2847
+ cli.addCommand(ninetyNineCommand);
2341
2848
  cli.addCommand(shellCommand2);
2342
2849
  cli.action(async () => {
2343
2850
  try {
2344
2851
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_secrets(), secrets_exports));
2345
- const { isVmRunning: isVmRunning3 } = await Promise.resolve().then(() => (init_qemu(), qemu_exports));
2852
+ const { isVmRunning: isVmRunning2 } = await Promise.resolve().then(() => (init_qemu(), qemu_exports));
2346
2853
  const config = loadConfig2();
2347
- if (config && isVmRunning3()) {
2854
+ if (config && isVmRunning2()) {
2348
2855
  await shellCommand2.parseAsync([], { from: "user" });
2349
2856
  return;
2350
2857
  }
@@ -2358,7 +2865,7 @@ import fs11 from "fs";
2358
2865
  import path12 from "path";
2359
2866
  import os3 from "os";
2360
2867
  import https from "https";
2361
- import chalk14 from "chalk";
2868
+ import chalk16 from "chalk";
2362
2869
  var PACKAGE_NAME = "buildwithnexus";
2363
2870
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
2364
2871
  var STATE_DIR = path12.join(os3.homedir(), ".buildwithnexus");
@@ -2439,8 +2946,8 @@ async function checkForUpdates(currentVersion) {
2439
2946
  function printUpdateBanner(current, latest) {
2440
2947
  const msg = [
2441
2948
  "",
2442
- chalk14.yellow(` Update available: ${current} \u2192 ${latest}`),
2443
- chalk14.cyan(` Run: npm update -g buildwithnexus`),
2949
+ chalk16.yellow(` Update available: ${current} \u2192 ${latest}`),
2950
+ chalk16.cyan(` Run: npm update -g buildwithnexus`),
2444
2951
  ""
2445
2952
  ].join("\n");
2446
2953
  process.stderr.write(msg + "\n");