buildwithnexus 0.2.8 → 0.2.9

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
@@ -1,116 +1,18 @@
1
1
  #!/usr/bin/env node
2
- // src/cli.ts
3
- import { Command as Command12 } from "commander";
4
-
5
- // src/commands/init.ts
6
- import { Command } from "commander";
7
-
8
- // src/ui/banner.ts
9
- import chalk from "chalk";
10
- var BANNER = `
11
- \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
12
- \u2551 ${chalk.bold.cyan("B U I L D W I T H N E X U S")} \u2551
13
- \u2551 \u2551
14
- \u2551 Autonomous AI Runtime \xB7 Nested Isolation \u2551
15
- \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
16
- `;
17
- function showBanner() {
18
- console.log(BANNER);
19
- console.log(chalk.dim(" v0.2.8 \xB7 buildwithnexus.dev\n"));
20
- }
21
- function showPhase(phase, total, description) {
22
- const progress = chalk.cyan(`[${phase}/${total}]`);
23
- console.log(`
24
- ${progress} ${chalk.bold(description)}`);
25
- }
26
- function showCompletion(urls) {
27
- const lines = [
28
- "",
29
- 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"),
30
- chalk.green(" \u2551 ") + chalk.bold.green("NEXUS Runtime is Live!") + chalk.green(" \u2551"),
31
- 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"),
32
- chalk.green(" \u2551 ") + chalk.white(`Connect: ${urls.ssh}`.padEnd(55)) + chalk.green("\u2551")
33
- ];
34
- if (urls.remote) {
35
- lines.push(chalk.green(" \u2551 ") + chalk.white(`Remote: ${urls.remote}`.padEnd(55)) + chalk.green("\u2551"));
36
- }
37
- lines.push(
38
- 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"),
39
- chalk.green(" \u2551 ") + chalk.dim("Commands:".padEnd(55)) + chalk.green("\u2551"),
40
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus ssh - Open CLI".padEnd(55)) + chalk.green("\u2551"),
41
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus status - Check health".padEnd(55)) + chalk.green("\u2551"),
42
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus logs - View logs".padEnd(55)) + chalk.green("\u2551"),
43
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus stop - Shutdown".padEnd(55)) + chalk.green("\u2551"),
44
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus start - Restart".padEnd(55)) + chalk.green("\u2551"),
45
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus update - Update release".padEnd(55)) + chalk.green("\u2551"),
46
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus brainstorm - Brainstorm ideas".padEnd(55)) + chalk.green("\u2551"),
47
- chalk.green(" \u2551 ") + chalk.white(" buildwithnexus destroy - Remove all".padEnd(55)) + chalk.green("\u2551"),
48
- 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"),
49
- ""
50
- );
51
- console.log(lines.join("\n"));
52
- }
53
-
54
- // src/ui/spinner.ts
55
- import ora from "ora";
56
- import chalk2 from "chalk";
57
- function createSpinner(text) {
58
- return ora({ text, color: "cyan", spinner: "dots" });
59
- }
60
- function succeed(spinner, text) {
61
- spinner.succeed(chalk2.green(text));
62
- }
63
- function fail(spinner, text) {
64
- spinner.fail(chalk2.red(text));
65
- }
66
-
67
- // src/ui/logger.ts
68
- import chalk3 from "chalk";
69
- var log = {
70
- step(msg) {
71
- console.log(chalk3.cyan(" \u2192 ") + msg);
72
- },
73
- success(msg) {
74
- console.log(chalk3.green(" \u2713 ") + msg);
75
- },
76
- error(msg) {
77
- console.error(chalk3.red(" \u2717 ") + msg);
78
- },
79
- warn(msg) {
80
- console.log(chalk3.yellow(" \u26A0 ") + msg);
81
- },
82
- dim(msg) {
83
- console.log(chalk3.dim(" " + msg));
84
- },
85
- detail(label, value) {
86
- console.log(chalk3.dim(" " + label + ": ") + value);
87
- }
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
88
10
  };
89
-
90
- // src/ui/prompts.ts
91
- import { input, confirm, password } from "@inquirer/prompts";
92
- import chalk4 from "chalk";
93
11
 
94
12
  // src/core/dlp.ts
95
13
  import crypto from "crypto";
96
14
  import fs from "fs";
97
15
  import path from "path";
98
- var NEXUS_HOME = path.join(process.env.HOME || "~", ".buildwithnexus");
99
- var SECRET_PATTERNS = [
100
- /sk-ant-api03-[A-Za-z0-9_-]{20,}/g,
101
- // Anthropic API key
102
- /sk-[A-Za-z0-9]{20,}/g,
103
- // OpenAI API key
104
- /AIza[A-Za-z0-9_-]{35}/g
105
- // Google AI API key
106
- ];
107
- var FORBIDDEN_KEY_CHARS = /[\n\r\t'"\\`${}();&|<>!#%^]/;
108
- var KEY_VALIDATORS = {
109
- ANTHROPIC_API_KEY: /^sk-ant-[A-Za-z0-9_-]{20,}$/,
110
- OPENAI_API_KEY: /^sk-[A-Za-z0-9_-]{20,}$/,
111
- GOOGLE_API_KEY: /^AIza[A-Za-z0-9_-]{35,}$/,
112
- NEXUS_MASTER_SECRET: /^[A-Za-z0-9_-]{20,64}$/
113
- };
114
16
  function yamlEscape(value) {
115
17
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\0/g, "");
116
18
  }
@@ -144,12 +46,6 @@ function redactError(err) {
144
46
  }
145
47
  return new Error(redact(String(err)));
146
48
  }
147
- var DlpViolation = class extends Error {
148
- constructor(message) {
149
- super(message);
150
- this.name = "DlpViolation";
151
- }
152
- };
153
49
  function validateKeyValue(keyName, value) {
154
50
  if (FORBIDDEN_KEY_CHARS.test(value)) {
155
51
  throw new DlpViolation(
@@ -180,7 +76,6 @@ function validateAllKeys(keys) {
180
76
  }
181
77
  return violations;
182
78
  }
183
- var HMAC_PATH = path.join(NEXUS_HOME, ".keys.hmac");
184
79
  function computeFileHmac(filePath, secret) {
185
80
  const content = fs.readFileSync(filePath);
186
81
  return crypto.createHmac("sha256", secret).update(content).digest("hex");
@@ -205,8 +100,6 @@ function verifyKeysFile(keysPath, masterSecret) {
205
100
  return false;
206
101
  }
207
102
  }
208
- var AUDIT_PATH = path.join(NEXUS_HOME, "audit.log");
209
- var MAX_AUDIT_SIZE = 10 * 1024 * 1024;
210
103
  function audit(event, detail = "") {
211
104
  try {
212
105
  const dir = path.dirname(AUDIT_PATH);
@@ -233,20 +126,6 @@ function audit(event, detail = "") {
233
126
  } catch {
234
127
  }
235
128
  }
236
- var SCRUB_KEYS = [
237
- "ANTHROPIC_API_KEY",
238
- "OPENAI_API_KEY",
239
- "GOOGLE_API_KEY",
240
- "NEXUS_MASTER_SECRET",
241
- "NEXUS_SECRET",
242
- "AWS_SECRET_ACCESS_KEY",
243
- "AWS_SESSION_TOKEN",
244
- "GITHUB_TOKEN",
245
- "GH_TOKEN",
246
- "NPM_TOKEN",
247
- "DOCKER_PASSWORD",
248
- "CI_JOB_TOKEN"
249
- ];
250
129
  function scrubEnv() {
251
130
  const clean = { ...process.env };
252
131
  for (const key of SCRUB_KEYS) {
@@ -265,145 +144,69 @@ function scrubEnv() {
265
144
  }
266
145
  return clean;
267
146
  }
268
-
269
- // src/ui/prompts.ts
270
- async function promptInitConfig() {
271
- console.log(chalk4.bold("\n API Keys\n"));
272
- const anthropicKey = await password({
273
- message: "Anthropic API key (required):",
274
- mask: "*",
275
- validate: (val) => {
276
- if (!val) return "API key is required";
277
- if (!val.startsWith("sk-ant-")) return "Must start with sk-ant-";
278
- try {
279
- validateKeyValue("ANTHROPIC_API_KEY", val);
280
- } catch (err) {
281
- if (err instanceof DlpViolation) return err.message;
282
- }
283
- return true;
284
- }
285
- });
286
- const openaiKey = await password({
287
- message: "OpenAI API key (optional, press Enter to skip):",
288
- mask: "*",
289
- validate: (val) => {
290
- if (!val) return true;
291
- try {
292
- validateKeyValue("OPENAI_API_KEY", val);
293
- } catch (err) {
294
- if (err instanceof DlpViolation) return err.message;
295
- }
296
- return true;
297
- }
298
- });
299
- const googleKey = await password({
300
- message: "Google AI API key (optional, press Enter to skip):",
301
- mask: "*",
302
- validate: (val) => {
303
- if (!val) return true;
304
- try {
305
- validateKeyValue("GOOGLE_API_KEY", val);
306
- } catch (err) {
307
- if (err instanceof DlpViolation) return err.message;
308
- }
309
- return true;
310
- }
311
- });
312
- console.log(chalk4.bold("\n VM Resources\n"));
313
- const vmRam = Number(
314
- await input({
315
- message: "VM RAM in GB:",
316
- default: "4",
317
- validate: (v) => {
318
- const n = Number(v);
319
- if (!Number.isInteger(n) || n < 2 || n > 256) return "Must be a whole number between 2 and 256";
320
- return true;
321
- }
322
- })
323
- );
324
- const vmCpus = Number(
325
- await input({
326
- message: "VM CPUs:",
327
- default: "2",
328
- validate: (v) => {
329
- const n = Number(v);
330
- if (!Number.isInteger(n) || n < 1 || n > 64) return "Must be a whole number between 1 and 64";
331
- return true;
332
- }
333
- })
334
- );
335
- const vmDisk = Number(
336
- await input({
337
- message: "VM Disk in GB:",
338
- default: "20",
339
- validate: (v) => {
340
- const n = Number(v);
341
- if (!Number.isInteger(n) || n < 10 || n > 2048) return "Must be a whole number between 10 and 2048";
342
- return true;
343
- }
344
- })
345
- );
346
- console.log(chalk4.bold("\n Configuration\n"));
347
- const enableTunnel = await confirm({
348
- message: "Enable Cloudflare tunnel for remote access?",
349
- default: true
350
- });
351
- return {
352
- anthropicKey,
353
- openaiKey,
354
- googleKey,
355
- vmRam,
356
- vmCpus,
357
- vmDisk,
358
- enableTunnel
359
- };
360
- }
361
-
362
- // src/core/platform.ts
363
- import os from "os";
364
- function detectPlatform() {
365
- const platform = os.platform();
366
- const arch = os.arch();
367
- if (platform === "darwin") {
368
- return {
369
- os: "mac",
370
- arch: arch === "arm64" ? "arm64" : "x64",
371
- qemuBinary: "qemu-system-aarch64",
372
- qemuCpuFlag: "-accel hvf -cpu host",
373
- ubuntuImage: "jammy-server-cloudimg-arm64.img",
374
- biosPath: "/opt/homebrew/share/qemu/edk2-aarch64-code.fd"
375
- };
376
- }
377
- if (platform === "linux") {
378
- return {
379
- os: "linux",
380
- arch: arch === "arm64" ? "arm64" : "x64",
381
- qemuBinary: arch === "arm64" ? "qemu-system-aarch64" : "qemu-system-x86_64",
382
- qemuCpuFlag: "-cpu host -enable-kvm",
383
- ubuntuImage: arch === "arm64" ? "jammy-server-cloudimg-arm64.img" : "jammy-server-cloudimg-amd64.img",
384
- biosPath: arch === "arm64" ? "/usr/share/qemu-efi-aarch64/QEMU_EFI.fd" : "/usr/share/OVMF/OVMF_CODE.fd"
147
+ var NEXUS_HOME, SECRET_PATTERNS, FORBIDDEN_KEY_CHARS, KEY_VALIDATORS, DlpViolation, HMAC_PATH, AUDIT_PATH, MAX_AUDIT_SIZE, SCRUB_KEYS;
148
+ var init_dlp = __esm({
149
+ "src/core/dlp.ts"() {
150
+ "use strict";
151
+ NEXUS_HOME = path.join(process.env.HOME || "~", ".buildwithnexus");
152
+ SECRET_PATTERNS = [
153
+ /sk-ant-api03-[A-Za-z0-9_-]{20,}/g,
154
+ // Anthropic API key
155
+ /sk-[A-Za-z0-9]{20,}/g,
156
+ // OpenAI API key
157
+ /AIza[A-Za-z0-9_-]{35}/g
158
+ // Google AI API key
159
+ ];
160
+ FORBIDDEN_KEY_CHARS = /[\n\r\t'"\\`${}();&|<>!#%^]/;
161
+ KEY_VALIDATORS = {
162
+ ANTHROPIC_API_KEY: /^sk-ant-[A-Za-z0-9_-]{20,}$/,
163
+ OPENAI_API_KEY: /^sk-[A-Za-z0-9_-]{20,}$/,
164
+ GOOGLE_API_KEY: /^AIza[A-Za-z0-9_-]{35,}$/,
165
+ NEXUS_MASTER_SECRET: /^[A-Za-z0-9_-]{20,64}$/
385
166
  };
386
- }
387
- if (platform === "win32") {
388
- return {
389
- os: "windows",
390
- arch: "x64",
391
- qemuBinary: "qemu-system-x86_64",
392
- qemuCpuFlag: "-cpu qemu64",
393
- ubuntuImage: "jammy-server-cloudimg-amd64.img",
394
- biosPath: "C:\\Program Files\\qemu\\share\\edk2-x86_64-code.fd"
167
+ DlpViolation = class extends Error {
168
+ constructor(message) {
169
+ super(message);
170
+ this.name = "DlpViolation";
171
+ }
395
172
  };
173
+ HMAC_PATH = path.join(NEXUS_HOME, ".keys.hmac");
174
+ AUDIT_PATH = path.join(NEXUS_HOME, "audit.log");
175
+ MAX_AUDIT_SIZE = 10 * 1024 * 1024;
176
+ SCRUB_KEYS = [
177
+ "ANTHROPIC_API_KEY",
178
+ "OPENAI_API_KEY",
179
+ "GOOGLE_API_KEY",
180
+ "NEXUS_MASTER_SECRET",
181
+ "NEXUS_SECRET",
182
+ "AWS_SECRET_ACCESS_KEY",
183
+ "AWS_SESSION_TOKEN",
184
+ "GITHUB_TOKEN",
185
+ "GH_TOKEN",
186
+ "NPM_TOKEN",
187
+ "DOCKER_PASSWORD",
188
+ "CI_JOB_TOKEN"
189
+ ];
396
190
  }
397
- throw new Error(`Unsupported platform: ${platform} ${arch}`);
398
- }
191
+ });
399
192
 
400
193
  // src/core/secrets.ts
194
+ var secrets_exports = {};
195
+ __export(secrets_exports, {
196
+ CONFIG_PATH: () => CONFIG_PATH,
197
+ KEYS_PATH: () => KEYS_PATH,
198
+ NEXUS_HOME: () => NEXUS_HOME2,
199
+ ensureHome: () => ensureHome,
200
+ generateMasterSecret: () => generateMasterSecret,
201
+ loadConfig: () => loadConfig,
202
+ loadKeys: () => loadKeys,
203
+ maskKey: () => maskKey,
204
+ saveConfig: () => saveConfig,
205
+ saveKeys: () => saveKeys
206
+ });
401
207
  import fs2 from "fs";
402
208
  import path2 from "path";
403
209
  import crypto2 from "crypto";
404
- var NEXUS_HOME2 = path2.join(process.env.HOME || "~", ".buildwithnexus");
405
- var CONFIG_PATH = path2.join(NEXUS_HOME2, "config.json");
406
- var KEYS_PATH = path2.join(NEXUS_HOME2, ".env.keys");
407
210
  function ensureHome() {
408
211
  fs2.mkdirSync(NEXUS_HOME2, { recursive: true, mode: 448 });
409
212
  fs2.mkdirSync(path2.join(NEXUS_HOME2, "vm", "images"), { recursive: true, mode: 448 });
@@ -455,18 +258,36 @@ function maskKey(key) {
455
258
  const reveal = Math.min(4, Math.floor(key.length * 0.1));
456
259
  return key.slice(0, reveal) + "..." + key.slice(-reveal);
457
260
  }
261
+ var NEXUS_HOME2, CONFIG_PATH, KEYS_PATH;
262
+ var init_secrets = __esm({
263
+ "src/core/secrets.ts"() {
264
+ "use strict";
265
+ init_dlp();
266
+ NEXUS_HOME2 = path2.join(process.env.HOME || "~", ".buildwithnexus");
267
+ CONFIG_PATH = path2.join(NEXUS_HOME2, "config.json");
268
+ KEYS_PATH = path2.join(NEXUS_HOME2, ".env.keys");
269
+ }
270
+ });
458
271
 
459
272
  // src/core/qemu.ts
273
+ var qemu_exports = {};
274
+ __export(qemu_exports, {
275
+ createDisk: () => createDisk,
276
+ downloadImage: () => downloadImage,
277
+ getVmPid: () => getVmPid,
278
+ installQemu: () => installQemu,
279
+ isQemuInstalled: () => isQemuInstalled,
280
+ isVmRunning: () => isVmRunning,
281
+ launchVm: () => launchVm,
282
+ resolvePortConflicts: () => resolvePortConflicts,
283
+ stopVm: () => stopVm
284
+ });
460
285
  import fs3 from "fs";
461
286
  import net from "net";
462
287
  import path3 from "path";
463
288
  import { execa } from "execa";
464
289
  import { select } from "@inquirer/prompts";
465
290
  import chalk5 from "chalk";
466
- var VM_DIR = path3.join(NEXUS_HOME2, "vm");
467
- var IMAGES_DIR = path3.join(VM_DIR, "images");
468
- var PID_FILE = path3.join(VM_DIR, "qemu.pid");
469
- var UBUNTU_BASE_URL = "https://cloud-images.ubuntu.com/jammy/current";
470
291
  async function isQemuInstalled(platform) {
471
292
  try {
472
293
  await execa(platform.qemuBinary, ["--version"], { env: scrubEnv() });
@@ -659,18 +480,36 @@ function stopVm() {
659
480
  function getVmPid() {
660
481
  return readValidPid();
661
482
  }
483
+ var VM_DIR, IMAGES_DIR, PID_FILE, UBUNTU_BASE_URL;
484
+ var init_qemu = __esm({
485
+ "src/core/qemu.ts"() {
486
+ "use strict";
487
+ init_secrets();
488
+ init_dlp();
489
+ VM_DIR = path3.join(NEXUS_HOME2, "vm");
490
+ IMAGES_DIR = path3.join(VM_DIR, "images");
491
+ PID_FILE = path3.join(VM_DIR, "qemu.pid");
492
+ UBUNTU_BASE_URL = "https://cloud-images.ubuntu.com/jammy/current";
493
+ }
494
+ });
662
495
 
663
496
  // src/core/ssh.ts
497
+ var ssh_exports = {};
498
+ __export(ssh_exports, {
499
+ addSshConfig: () => addSshConfig,
500
+ generateSshKey: () => generateSshKey,
501
+ getKeyPath: () => getKeyPath,
502
+ getPubKey: () => getPubKey,
503
+ openInteractiveSsh: () => openInteractiveSsh,
504
+ sshExec: () => sshExec,
505
+ sshUploadFile: () => sshUploadFile,
506
+ waitForSsh: () => waitForSsh
507
+ });
664
508
  import fs4 from "fs";
665
509
  import path4 from "path";
666
510
  import crypto3 from "crypto";
667
511
  import { execa as execa2 } from "execa";
668
512
  import { NodeSSH } from "node-ssh";
669
- var SSH_DIR = path4.join(NEXUS_HOME2, "ssh");
670
- var SSH_KEY = path4.join(SSH_DIR, "id_nexus_vm");
671
- var SSH_PUB_KEY = path4.join(SSH_DIR, "id_nexus_vm.pub");
672
- var KNOWN_HOSTS = path4.join(SSH_DIR, "known_hosts_nexus_vm");
673
- var PINNED_HOST_KEY = path4.join(SSH_DIR, "vm_host_key.pin");
674
513
  function getHostVerifier() {
675
514
  if (!fs4.existsSync(PINNED_HOST_KEY)) {
676
515
  return (key) => {
@@ -688,6 +527,9 @@ function getHostVerifier() {
688
527
  return match;
689
528
  };
690
529
  }
530
+ function getKeyPath() {
531
+ return SSH_KEY;
532
+ }
691
533
  function getPubKey() {
692
534
  return fs4.readFileSync(SSH_PUB_KEY, "utf-8").trim();
693
535
  }
@@ -776,14 +618,256 @@ async function sshUploadFile(port, localPath, remotePath) {
776
618
  privateKeyPath: SSH_KEY,
777
619
  hostVerifier: getHostVerifier()
778
620
  });
779
- await ssh.putFile(localPath, remotePath);
780
- ssh.dispose();
621
+ await ssh.putFile(localPath, remotePath);
622
+ ssh.dispose();
623
+ }
624
+ async function openInteractiveSsh(port) {
625
+ await execa2("ssh", ["nexus-vm"], { stdio: "inherit", env: scrubEnv() });
626
+ }
627
+ var SSH_DIR, SSH_KEY, SSH_PUB_KEY, KNOWN_HOSTS, PINNED_HOST_KEY;
628
+ var init_ssh = __esm({
629
+ "src/core/ssh.ts"() {
630
+ "use strict";
631
+ init_secrets();
632
+ init_dlp();
633
+ SSH_DIR = path4.join(NEXUS_HOME2, "ssh");
634
+ SSH_KEY = path4.join(SSH_DIR, "id_nexus_vm");
635
+ SSH_PUB_KEY = path4.join(SSH_DIR, "id_nexus_vm.pub");
636
+ KNOWN_HOSTS = path4.join(SSH_DIR, "known_hosts_nexus_vm");
637
+ PINNED_HOST_KEY = path4.join(SSH_DIR, "vm_host_key.pin");
638
+ }
639
+ });
640
+
641
+ // src/cli.ts
642
+ import { Command as Command13 } from "commander";
643
+
644
+ // src/commands/init.ts
645
+ 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
+ }
692
+
693
+ // src/ui/spinner.ts
694
+ import ora from "ora";
695
+ import chalk2 from "chalk";
696
+ function createSpinner(text) {
697
+ return ora({ text, color: "cyan", spinner: "dots" });
698
+ }
699
+ function succeed(spinner, text) {
700
+ spinner.succeed(chalk2.green(text));
701
+ }
702
+ function fail(spinner, text) {
703
+ spinner.fail(chalk2.red(text));
704
+ }
705
+
706
+ // src/ui/logger.ts
707
+ import chalk3 from "chalk";
708
+ var log = {
709
+ step(msg) {
710
+ console.log(chalk3.cyan(" \u2192 ") + msg);
711
+ },
712
+ success(msg) {
713
+ console.log(chalk3.green(" \u2713 ") + msg);
714
+ },
715
+ error(msg) {
716
+ console.error(chalk3.red(" \u2717 ") + msg);
717
+ },
718
+ warn(msg) {
719
+ console.log(chalk3.yellow(" \u26A0 ") + msg);
720
+ },
721
+ dim(msg) {
722
+ console.log(chalk3.dim(" " + msg));
723
+ },
724
+ detail(label, value) {
725
+ console.log(chalk3.dim(" " + label + ": ") + value);
726
+ }
727
+ };
728
+
729
+ // src/ui/prompts.ts
730
+ init_dlp();
731
+ import { input, confirm, password } from "@inquirer/prompts";
732
+ import chalk4 from "chalk";
733
+ async function promptInitConfig() {
734
+ console.log(chalk4.bold("\n API Keys\n"));
735
+ const anthropicKey = await password({
736
+ message: "Anthropic API key (required):",
737
+ mask: "*",
738
+ validate: (val) => {
739
+ if (!val) return "API key is required";
740
+ if (!val.startsWith("sk-ant-")) return "Must start with sk-ant-";
741
+ try {
742
+ validateKeyValue("ANTHROPIC_API_KEY", val);
743
+ } catch (err) {
744
+ if (err instanceof DlpViolation) return err.message;
745
+ }
746
+ return true;
747
+ }
748
+ });
749
+ const openaiKey = await password({
750
+ message: "OpenAI API key (optional, press Enter to skip):",
751
+ mask: "*",
752
+ validate: (val) => {
753
+ if (!val) return true;
754
+ try {
755
+ validateKeyValue("OPENAI_API_KEY", val);
756
+ } catch (err) {
757
+ if (err instanceof DlpViolation) return err.message;
758
+ }
759
+ return true;
760
+ }
761
+ });
762
+ const googleKey = await password({
763
+ message: "Google AI API key (optional, press Enter to skip):",
764
+ mask: "*",
765
+ validate: (val) => {
766
+ if (!val) return true;
767
+ try {
768
+ validateKeyValue("GOOGLE_API_KEY", val);
769
+ } catch (err) {
770
+ if (err instanceof DlpViolation) return err.message;
771
+ }
772
+ return true;
773
+ }
774
+ });
775
+ console.log(chalk4.bold("\n VM Resources\n"));
776
+ const vmRam = Number(
777
+ await input({
778
+ message: "VM RAM in GB:",
779
+ default: "4",
780
+ validate: (v) => {
781
+ const n = Number(v);
782
+ if (!Number.isInteger(n) || n < 2 || n > 256) return "Must be a whole number between 2 and 256";
783
+ return true;
784
+ }
785
+ })
786
+ );
787
+ const vmCpus = Number(
788
+ await input({
789
+ message: "VM CPUs:",
790
+ default: "2",
791
+ validate: (v) => {
792
+ const n = Number(v);
793
+ if (!Number.isInteger(n) || n < 1 || n > 64) return "Must be a whole number between 1 and 64";
794
+ return true;
795
+ }
796
+ })
797
+ );
798
+ const vmDisk = Number(
799
+ await input({
800
+ message: "VM Disk in GB:",
801
+ default: "20",
802
+ validate: (v) => {
803
+ const n = Number(v);
804
+ if (!Number.isInteger(n) || n < 10 || n > 2048) return "Must be a whole number between 10 and 2048";
805
+ return true;
806
+ }
807
+ })
808
+ );
809
+ console.log(chalk4.bold("\n Configuration\n"));
810
+ const enableTunnel = await confirm({
811
+ message: "Enable Cloudflare tunnel for remote access?",
812
+ default: true
813
+ });
814
+ return {
815
+ anthropicKey,
816
+ openaiKey,
817
+ googleKey,
818
+ vmRam,
819
+ vmCpus,
820
+ vmDisk,
821
+ enableTunnel
822
+ };
781
823
  }
782
- async function openInteractiveSsh(port) {
783
- await execa2("ssh", ["nexus-vm"], { stdio: "inherit", env: scrubEnv() });
824
+
825
+ // src/core/platform.ts
826
+ import os from "os";
827
+ function detectPlatform() {
828
+ const platform = os.platform();
829
+ const arch = os.arch();
830
+ if (platform === "darwin") {
831
+ return {
832
+ os: "mac",
833
+ arch: arch === "arm64" ? "arm64" : "x64",
834
+ qemuBinary: "qemu-system-aarch64",
835
+ qemuCpuFlag: "-accel hvf -cpu host",
836
+ ubuntuImage: "jammy-server-cloudimg-arm64.img",
837
+ biosPath: "/opt/homebrew/share/qemu/edk2-aarch64-code.fd"
838
+ };
839
+ }
840
+ if (platform === "linux") {
841
+ return {
842
+ os: "linux",
843
+ arch: arch === "arm64" ? "arm64" : "x64",
844
+ qemuBinary: arch === "arm64" ? "qemu-system-aarch64" : "qemu-system-x86_64",
845
+ qemuCpuFlag: "-cpu host -enable-kvm",
846
+ ubuntuImage: arch === "arm64" ? "jammy-server-cloudimg-arm64.img" : "jammy-server-cloudimg-amd64.img",
847
+ biosPath: arch === "arm64" ? "/usr/share/qemu-efi-aarch64/QEMU_EFI.fd" : "/usr/share/OVMF/OVMF_CODE.fd"
848
+ };
849
+ }
850
+ if (platform === "win32") {
851
+ return {
852
+ os: "windows",
853
+ arch: "x64",
854
+ qemuBinary: "qemu-system-x86_64",
855
+ qemuCpuFlag: "-cpu qemu64",
856
+ ubuntuImage: "jammy-server-cloudimg-amd64.img",
857
+ biosPath: "C:\\Program Files\\qemu\\share\\edk2-x86_64-code.fd"
858
+ };
859
+ }
860
+ throw new Error(`Unsupported platform: ${platform} ${arch}`);
784
861
  }
785
862
 
863
+ // src/commands/init.ts
864
+ init_secrets();
865
+ init_qemu();
866
+ init_ssh();
867
+
786
868
  // src/core/cloudinit.ts
869
+ init_secrets();
870
+ init_dlp();
787
871
  import fs5 from "fs";
788
872
  import path5 from "path";
789
873
  import ejs from "ejs";
@@ -877,6 +961,7 @@ async function createCloudInitIso(userDataPath) {
877
961
  }
878
962
 
879
963
  // src/core/health.ts
964
+ init_ssh();
880
965
  async function checkHealth(port, vmRunning) {
881
966
  const status = {
882
967
  vmRunning,
@@ -937,6 +1022,8 @@ async function waitForCloudInit(port, timeoutMs = 9e5) {
937
1022
  }
938
1023
 
939
1024
  // src/core/tunnel.ts
1025
+ init_ssh();
1026
+ init_dlp();
940
1027
  var CLOUDFLARED_VERSION = "2024.12.2";
941
1028
  var CLOUDFLARED_SHA256 = {
942
1029
  amd64: "40ec9a0f5b58e3b04183aaf01c4ddd4dbc6af39b0f06be4b7ce8b1011d0a07ab",
@@ -984,6 +1071,8 @@ async function stopTunnel(sshPort) {
984
1071
  }
985
1072
 
986
1073
  // src/commands/init.ts
1074
+ init_dlp();
1075
+ init_ssh();
987
1076
  import fs6 from "fs";
988
1077
  import path6 from "path";
989
1078
  import os2 from "os";
@@ -1105,15 +1194,14 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1105
1194
  spinner.start();
1106
1195
  await sshUploadFile(config.sshPort, tarballPath, "/tmp/nexus-release.tar.gz");
1107
1196
  succeed(spinner, "Tarball uploaded");
1108
- spinner = createSpinner("Uploading API keys via SSH...");
1197
+ spinner = createSpinner("Staging API keys...");
1109
1198
  spinner.start();
1110
1199
  const keysContent = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
1111
1200
  const tmpKeysPath = path6.join(os2.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1112
1201
  fs6.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1113
1202
  try {
1114
- await sshExec(config.sshPort, "sudo -u nexus mkdir -p /home/nexus/.nexus");
1115
- await sshUploadFile(config.sshPort, tmpKeysPath, "/home/nexus/.nexus/.env.keys");
1116
- await sshExec(config.sshPort, "chown nexus:nexus /home/nexus/.nexus/.env.keys && chmod 600 /home/nexus/.nexus/.env.keys");
1203
+ await sshUploadFile(config.sshPort, tmpKeysPath, "/tmp/.nexus-env-keys");
1204
+ await sshExec(config.sshPort, "chmod 600 /tmp/.nexus-env-keys");
1117
1205
  } finally {
1118
1206
  try {
1119
1207
  fs6.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
@@ -1121,7 +1209,7 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1121
1209
  } catch {
1122
1210
  }
1123
1211
  }
1124
- succeed(spinner, "API keys delivered securely via SSH");
1212
+ succeed(spinner, "API keys staged");
1125
1213
  spinner = createSpinner("Cloud-init provisioning (extracting NEXUS, building Docker, installing deps)...");
1126
1214
  spinner.start();
1127
1215
  const cloudInitDone = await waitForCloudInit(config.sshPort);
@@ -1131,6 +1219,10 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1131
1219
  process.exit(1);
1132
1220
  }
1133
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");
1134
1226
  showPhase(8, TOTAL_PHASES, "NEXUS Server Startup");
1135
1227
  spinner = createSpinner("Waiting for NEXUS server...");
1136
1228
  spinner.start();
@@ -1173,6 +1265,10 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1173
1265
 
1174
1266
  // src/commands/start.ts
1175
1267
  import { Command as Command2 } from "commander";
1268
+ init_secrets();
1269
+ init_qemu();
1270
+ init_ssh();
1271
+ init_secrets();
1176
1272
  import path7 from "path";
1177
1273
  var startCommand = new Command2("start").description("Start the NEXUS runtime").action(async () => {
1178
1274
  const config = loadConfig();
@@ -1227,6 +1323,9 @@ var startCommand = new Command2("start").description("Start the NEXUS runtime").
1227
1323
 
1228
1324
  // src/commands/stop.ts
1229
1325
  import { Command as Command3 } from "commander";
1326
+ init_secrets();
1327
+ init_qemu();
1328
+ init_ssh();
1230
1329
  var stopCommand = new Command3("stop").description("Gracefully shut down the NEXUS runtime").action(async () => {
1231
1330
  const config = loadConfig();
1232
1331
  if (!config) {
@@ -1263,6 +1362,8 @@ var stopCommand = new Command3("stop").description("Gracefully shut down the NEX
1263
1362
  // src/commands/status.ts
1264
1363
  import { Command as Command4 } from "commander";
1265
1364
  import chalk6 from "chalk";
1365
+ init_secrets();
1366
+ init_qemu();
1266
1367
  var statusCommand = new Command4("status").description("Check NEXUS runtime health").option("--json", "Output as JSON").action(async (opts) => {
1267
1368
  const config = loadConfig();
1268
1369
  if (!config) {
@@ -1294,6 +1395,8 @@ var statusCommand = new Command4("status").description("Check NEXUS runtime heal
1294
1395
  import { Command as Command5 } from "commander";
1295
1396
  import chalk7 from "chalk";
1296
1397
  import fs7 from "fs";
1398
+ init_qemu();
1399
+ init_secrets();
1297
1400
  import path8 from "path";
1298
1401
  import { execa as execa4 } from "execa";
1299
1402
  var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime environment").action(async () => {
@@ -1361,6 +1464,10 @@ var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime e
1361
1464
 
1362
1465
  // src/commands/logs.ts
1363
1466
  import { Command as Command6 } from "commander";
1467
+ init_secrets();
1468
+ init_qemu();
1469
+ init_ssh();
1470
+ init_dlp();
1364
1471
  import { execa as execa5 } from "execa";
1365
1472
  var logsCommand = new Command6("logs").description("View NEXUS server logs").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Number of lines to show", "50").action(async (opts) => {
1366
1473
  const config = loadConfig();
@@ -1399,6 +1506,9 @@ import { Command as Command7 } from "commander";
1399
1506
  import path9 from "path";
1400
1507
  import fs8 from "fs";
1401
1508
  import { fileURLToPath as fileURLToPath2 } from "url";
1509
+ init_secrets();
1510
+ init_qemu();
1511
+ init_ssh();
1402
1512
  function getReleaseTarball2() {
1403
1513
  const dir = path9.dirname(fileURLToPath2(import.meta.url));
1404
1514
  const tarballPath = path9.join(dir, "nexus-release.tar.gz");
@@ -1457,6 +1567,8 @@ import { Command as Command8 } from "commander";
1457
1567
  import chalk8 from "chalk";
1458
1568
  import fs9 from "fs";
1459
1569
  import { input as input2 } from "@inquirer/prompts";
1570
+ init_secrets();
1571
+ init_qemu();
1460
1572
  import path10 from "path";
1461
1573
  var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and all data").option("--force", "Skip confirmation").action(async (opts) => {
1462
1574
  const config = loadConfig();
@@ -1511,6 +1623,8 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1511
1623
  import { Command as Command9 } from "commander";
1512
1624
  import { password as password2 } from "@inquirer/prompts";
1513
1625
  import chalk9 from "chalk";
1626
+ init_secrets();
1627
+ init_dlp();
1514
1628
  var keysCommand = new Command9("keys").description("Manage API keys");
1515
1629
  keysCommand.command("list").description("Show configured API keys (masked)").action(() => {
1516
1630
  const keys = loadKeys();
@@ -1567,6 +1681,9 @@ keysCommand.command("set <key>").description("Set or update an API key (e.g. ANT
1567
1681
 
1568
1682
  // src/commands/ssh.ts
1569
1683
  import { Command as Command10 } from "commander";
1684
+ init_secrets();
1685
+ init_qemu();
1686
+ init_ssh();
1570
1687
  var sshCommand = new Command10("ssh").description("Open an SSH session into the NEXUS VM").action(async () => {
1571
1688
  const config = loadConfig();
1572
1689
  if (!config) {
@@ -1585,6 +1702,10 @@ var sshCommand = new Command10("ssh").description("Open an SSH session into the
1585
1702
  import { Command as Command11 } from "commander";
1586
1703
  import chalk10 from "chalk";
1587
1704
  import { input as input3 } from "@inquirer/prompts";
1705
+ init_secrets();
1706
+ init_qemu();
1707
+ init_ssh();
1708
+ init_dlp();
1588
1709
  var COS_PREFIX = chalk10.bold.cyan(" Chief of Staff");
1589
1710
  var YOU_PREFIX = chalk10.bold.white(" You");
1590
1711
  var DIVIDER = chalk10.dim(" " + "\u2500".repeat(56));
@@ -1699,8 +1820,478 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1699
1820
  }
1700
1821
  });
1701
1822
 
1823
+ // src/commands/shell.ts
1824
+ import { Command as Command12 } from "commander";
1825
+ import chalk13 from "chalk";
1826
+ init_secrets();
1827
+ init_qemu();
1828
+ init_ssh();
1829
+ init_dlp();
1830
+
1831
+ // src/ui/repl.ts
1832
+ init_secrets();
1833
+ import readline from "readline";
1834
+ import fs10 from "fs";
1835
+ import path11 from "path";
1836
+ import chalk11 from "chalk";
1837
+ var HISTORY_FILE = path11.join(NEXUS_HOME2, "shell_history");
1838
+ var MAX_HISTORY = 1e3;
1839
+ var Repl = class {
1840
+ rl = null;
1841
+ commands = /* @__PURE__ */ new Map();
1842
+ onMessage;
1843
+ history = [];
1844
+ constructor(onMessage) {
1845
+ this.onMessage = onMessage;
1846
+ this.loadHistory();
1847
+ }
1848
+ registerCommand(cmd) {
1849
+ this.commands.set(cmd.name, cmd);
1850
+ }
1851
+ loadHistory() {
1852
+ try {
1853
+ if (fs10.existsSync(HISTORY_FILE)) {
1854
+ this.history = fs10.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter(Boolean).slice(-MAX_HISTORY);
1855
+ }
1856
+ } catch {
1857
+ }
1858
+ }
1859
+ saveHistory() {
1860
+ try {
1861
+ const dir = path11.dirname(HISTORY_FILE);
1862
+ fs10.mkdirSync(dir, { recursive: true });
1863
+ fs10.writeFileSync(HISTORY_FILE, this.history.slice(-MAX_HISTORY).join("\n") + "\n", { mode: 384 });
1864
+ } catch {
1865
+ }
1866
+ }
1867
+ async start() {
1868
+ this.rl = readline.createInterface({
1869
+ input: process.stdin,
1870
+ output: process.stdout,
1871
+ prompt: chalk11.bold.cyan("nexus") + chalk11.dim(" \u276F "),
1872
+ history: this.history,
1873
+ historySize: MAX_HISTORY,
1874
+ terminal: true
1875
+ });
1876
+ this.rl.prompt();
1877
+ this.rl.on("line", async (line) => {
1878
+ const trimmed = line.trim();
1879
+ if (!trimmed) {
1880
+ this.rl?.prompt();
1881
+ return;
1882
+ }
1883
+ this.history.push(trimmed);
1884
+ if (trimmed.startsWith("/")) {
1885
+ const parts = trimmed.slice(1).split(/\s+/);
1886
+ const cmdName = parts[0].toLowerCase();
1887
+ if (cmdName === "help") {
1888
+ this.showHelp();
1889
+ this.rl?.prompt();
1890
+ return;
1891
+ }
1892
+ if (cmdName === "exit" || cmdName === "quit") {
1893
+ this.stop();
1894
+ return;
1895
+ }
1896
+ const cmd = this.commands.get(cmdName);
1897
+ if (cmd) {
1898
+ try {
1899
+ await cmd.handler();
1900
+ } catch (err) {
1901
+ console.log(chalk11.red(` \u2717 Command failed: ${err.message}`));
1902
+ }
1903
+ this.rl?.prompt();
1904
+ return;
1905
+ }
1906
+ console.log(chalk11.yellow(` Unknown command: /${cmdName}. Type /help for available commands.`));
1907
+ this.rl?.prompt();
1908
+ return;
1909
+ }
1910
+ try {
1911
+ await this.onMessage(trimmed);
1912
+ } catch (err) {
1913
+ console.log(chalk11.red(` \u2717 ${err.message}`));
1914
+ }
1915
+ this.rl?.prompt();
1916
+ });
1917
+ this.rl.on("close", () => {
1918
+ this.saveHistory();
1919
+ console.log(chalk11.dim("\n Session ended."));
1920
+ process.exit(0);
1921
+ });
1922
+ this.rl.on("SIGINT", () => {
1923
+ this.stop();
1924
+ });
1925
+ }
1926
+ showHelp() {
1927
+ 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"));
1930
+ for (const [name, cmd] of this.commands) {
1931
+ console.log(` ${chalk11.cyan("/" + name.padEnd(14))} ${chalk11.dim(cmd.description)}`);
1932
+ }
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"));
1937
+ console.log("");
1938
+ }
1939
+ write(text) {
1940
+ if (this.rl) {
1941
+ readline.clearLine(process.stdout, 0);
1942
+ readline.cursorTo(process.stdout, 0);
1943
+ console.log(text);
1944
+ this.rl.prompt(true);
1945
+ } else {
1946
+ console.log(text);
1947
+ }
1948
+ }
1949
+ stop() {
1950
+ this.saveHistory();
1951
+ if (this.rl) {
1952
+ this.rl.close();
1953
+ this.rl = null;
1954
+ }
1955
+ }
1956
+ };
1957
+
1958
+ // src/core/event-stream.ts
1959
+ init_ssh();
1960
+ init_secrets();
1961
+ init_dlp();
1962
+ import chalk12 from "chalk";
1963
+ 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
1973
+ };
1974
+ function getColor(role) {
1975
+ if (!role) return ROLE_COLORS["default"];
1976
+ return ROLE_COLORS[role] || ROLE_COLORS["default"];
1977
+ }
1978
+ function formatEvent(event) {
1979
+ const color = getColor(event.role);
1980
+ const prefix = event.role ? color(` [${event.role}]`) : "";
1981
+ switch (event.type) {
1982
+ case "agent_thinking":
1983
+ return `${prefix} ${chalk12.dim("thinking...")}`;
1984
+ case "agent_response":
1985
+ return `${prefix} ${redact(event.content ?? "")}`;
1986
+ case "task_delegated":
1987
+ return `${prefix} ${chalk12.dim("\u2192")} delegated to ${chalk12.bold(event.target ?? "agent")}`;
1988
+ case "agent_complete":
1989
+ return `${prefix} ${chalk12.green("\u2713")} ${chalk12.dim("complete")}`;
1990
+ case "error":
1991
+ return ` ${chalk12.red("\u2717")} ${redact(event.content ?? "Unknown error")}`;
1992
+ case "heartbeat":
1993
+ return null;
1994
+ default:
1995
+ return null;
1996
+ }
1997
+ }
1998
+ function parseSSEData(raw) {
1999
+ const events = [];
2000
+ const blocks = raw.split("\n\n");
2001
+ for (const block of blocks) {
2002
+ if (!block.trim()) continue;
2003
+ let data = "";
2004
+ for (const line of block.split("\n")) {
2005
+ if (line.startsWith("data: ")) {
2006
+ data += line.slice(6);
2007
+ }
2008
+ }
2009
+ if (data) {
2010
+ try {
2011
+ events.push(JSON.parse(data));
2012
+ } catch {
2013
+ }
2014
+ }
2015
+ }
2016
+ return events;
2017
+ }
2018
+ var EventStream = class {
2019
+ active = false;
2020
+ lastId = "0";
2021
+ onEvent;
2022
+ pollInterval = null;
2023
+ constructor(onEvent) {
2024
+ this.onEvent = onEvent;
2025
+ }
2026
+ async start() {
2027
+ this.active = true;
2028
+ const config = loadConfig();
2029
+ if (!config) return;
2030
+ this.pollInterval = setInterval(async () => {
2031
+ if (!this.active) return;
2032
+ try {
2033
+ const { stdout, code } = await sshExec(
2034
+ config.sshPort,
2035
+ `curl -sf -H 'Last-Event-ID: ${this.lastId}' http://localhost:4200/events?timeout=1 2>/dev/null || true`
2036
+ );
2037
+ if (code === 0 && stdout.trim()) {
2038
+ const events = parseSSEData(stdout);
2039
+ for (const event of events) {
2040
+ if (event.id) this.lastId = event.id;
2041
+ this.onEvent(event);
2042
+ }
2043
+ }
2044
+ } catch {
2045
+ }
2046
+ }, 2e3);
2047
+ }
2048
+ stop() {
2049
+ this.active = false;
2050
+ if (this.pollInterval) {
2051
+ clearInterval(this.pollInterval);
2052
+ this.pollInterval = null;
2053
+ }
2054
+ }
2055
+ };
2056
+
2057
+ // src/commands/shell.ts
2058
+ async function sendMessage2(sshPort, message) {
2059
+ const payload = JSON.stringify({ message, source: "shell" });
2060
+ const escaped = shellEscape(payload);
2061
+ const { stdout, code } = await sshExec(
2062
+ sshPort,
2063
+ `curl -sf -X POST http://localhost:4200/message -H 'Content-Type: application/json' -d ${escaped}`
2064
+ );
2065
+ if (code !== 0) throw new Error("Server returned a non-zero exit code");
2066
+ try {
2067
+ const parsed = JSON.parse(stdout);
2068
+ return parsed.response ?? parsed.message ?? stdout;
2069
+ } catch {
2070
+ return stdout;
2071
+ }
2072
+ }
2073
+ function showShellBanner(health) {
2074
+ const check = chalk13.green("\u2713");
2075
+ const cross = chalk13.red("\u2717");
2076
+ 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"));
2081
+ if (health.tunnelUrl) {
2082
+ console.log(chalk13.bold(" \u2551 ") + chalk13.dim(`Tunnel: ${health.tunnelUrl}`.padEnd(55)) + chalk13.bold("\u2551"));
2083
+ }
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"));
2087
+ console.log("");
2088
+ }
2089
+ async function getAgentList(sshPort) {
2090
+ try {
2091
+ const { stdout, code } = await sshExec(sshPort, "curl -sf http://localhost:4200/agents");
2092
+ if (code !== 0) return "Could not retrieve agent list";
2093
+ const agents = JSON.parse(stdout);
2094
+ if (!Array.isArray(agents)) return stdout;
2095
+ 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"));
2098
+ for (const agent of agents) {
2099
+ const name = agent.name ?? agent.id ?? "unknown";
2100
+ 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)}`);
2103
+ }
2104
+ lines.push("");
2105
+ return lines.join("\n");
2106
+ } catch {
2107
+ return " Could not retrieve agent list";
2108
+ }
2109
+ }
2110
+ async function getStatus(sshPort) {
2111
+ try {
2112
+ const vmRunning = isVmRunning();
2113
+ const health = await checkHealth(sshPort, vmRunning);
2114
+ const check = chalk13.green("\u2713");
2115
+ const cross = chalk13.red("\u2717");
2116
+ 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"));
2119
+ lines.push(` ${health.vmRunning ? check : cross} Virtual Machine`);
2120
+ lines.push(` ${health.sshReady ? check : cross} SSH Connection`);
2121
+ lines.push(` ${health.dockerReady ? check : cross} Docker Engine`);
2122
+ lines.push(` ${health.serverHealthy ? check : cross} NEXUS Engine`);
2123
+ if (health.tunnelUrl) {
2124
+ lines.push(` ${check} Tunnel: ${health.tunnelUrl}`);
2125
+ } else {
2126
+ lines.push(` ${cross} Tunnel: not active`);
2127
+ }
2128
+ lines.push("");
2129
+ return lines.join("\n");
2130
+ } catch {
2131
+ return " Could not check status";
2132
+ }
2133
+ }
2134
+ async function getCost(sshPort) {
2135
+ try {
2136
+ const { stdout, code } = await sshExec(sshPort, "curl -sf http://localhost:4200/cost");
2137
+ if (code !== 0) return " Could not retrieve cost data";
2138
+ const data = JSON.parse(stdout);
2139
+ 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"));
2142
+ if (data.total !== void 0) {
2143
+ lines.push(` Total: ${chalk13.bold.green("$" + Number(data.total).toFixed(4))}`);
2144
+ }
2145
+ if (data.today !== void 0) {
2146
+ lines.push(` Today: ${chalk13.bold("$" + Number(data.today).toFixed(4))}`);
2147
+ }
2148
+ if (data.by_agent && typeof data.by_agent === "object") {
2149
+ lines.push("");
2150
+ lines.push(chalk13.dim(" By Agent:"));
2151
+ for (const [agent, cost] of Object.entries(data.by_agent)) {
2152
+ lines.push(` ${agent.padEnd(20)} $${Number(cost).toFixed(4)}`);
2153
+ }
2154
+ }
2155
+ lines.push("");
2156
+ return lines.join("\n");
2157
+ } catch {
2158
+ return " Could not retrieve cost data";
2159
+ }
2160
+ }
2161
+ var shellCommand2 = new Command12("shell").description("Launch the interactive NEXUS shell").action(async () => {
2162
+ try {
2163
+ const config = loadConfig();
2164
+ if (!config) {
2165
+ log.error("No NEXUS configuration found. Run: buildwithnexus init");
2166
+ process.exit(1);
2167
+ }
2168
+ if (!isVmRunning()) {
2169
+ log.error("VM is not running. Start it with: buildwithnexus start");
2170
+ process.exit(1);
2171
+ }
2172
+ const spinner = createSpinner("Connecting to NEXUS...");
2173
+ spinner.start();
2174
+ const health = await checkHealth(config.sshPort, true);
2175
+ if (!health.serverHealthy) {
2176
+ fail(spinner, "NEXUS engine is not responding");
2177
+ log.warn("Check status: buildwithnexus status");
2178
+ process.exit(1);
2179
+ }
2180
+ succeed(spinner, "Connected to NEXUS engine");
2181
+ showShellBanner(health);
2182
+ const eventStream = new EventStream((event) => {
2183
+ const formatted = formatEvent(event);
2184
+ if (formatted) repl.write(formatted);
2185
+ });
2186
+ eventStream.start();
2187
+ const repl = new Repl(async (text) => {
2188
+ const thinkingSpinner = createSpinner("Processing...");
2189
+ thinkingSpinner.start();
2190
+ try {
2191
+ const response = await sendMessage2(config.sshPort, text);
2192
+ thinkingSpinner.stop();
2193
+ thinkingSpinner.clear();
2194
+ console.log("");
2195
+ console.log(chalk13.bold.cyan(" Chief of Staff:"));
2196
+ const lines = redact(response).split("\n");
2197
+ for (const line of lines) {
2198
+ console.log(chalk13.white(" " + line));
2199
+ }
2200
+ console.log("");
2201
+ } catch (err) {
2202
+ thinkingSpinner.stop();
2203
+ thinkingSpinner.clear();
2204
+ throw err;
2205
+ }
2206
+ });
2207
+ repl.registerCommand({
2208
+ name: "brainstorm",
2209
+ description: "Enter brainstorm mode",
2210
+ handler: async () => {
2211
+ console.log(chalk13.dim(" Entering brainstorm mode... (type 'exit' to return)"));
2212
+ const { input: input4 } = await import("@inquirer/prompts");
2213
+ const idea = await input4({ message: "What would you like to brainstorm?" });
2214
+ if (!idea.trim()) return;
2215
+ const brainstormSpinner = createSpinner("Chief of Staff is consulting the team...");
2216
+ brainstormSpinner.start();
2217
+ const response = await sendMessage2(config.sshPort, `[BRAINSTORM] The CEO wants to brainstorm: ${idea}`);
2218
+ brainstormSpinner.stop();
2219
+ brainstormSpinner.clear();
2220
+ console.log("");
2221
+ console.log(chalk13.bold.cyan(" Chief of Staff:"));
2222
+ for (const line of redact(response).split("\n")) {
2223
+ console.log(chalk13.white(" " + line));
2224
+ }
2225
+ console.log("");
2226
+ }
2227
+ });
2228
+ repl.registerCommand({
2229
+ name: "status",
2230
+ description: "Show system health status",
2231
+ handler: async () => {
2232
+ const result = await getStatus(config.sshPort);
2233
+ console.log(result);
2234
+ }
2235
+ });
2236
+ repl.registerCommand({
2237
+ name: "agents",
2238
+ description: "List registered agents",
2239
+ handler: async () => {
2240
+ const result = await getAgentList(config.sshPort);
2241
+ console.log(result);
2242
+ }
2243
+ });
2244
+ repl.registerCommand({
2245
+ name: "cost",
2246
+ description: "Show token usage and costs",
2247
+ handler: async () => {
2248
+ const result = await getCost(config.sshPort);
2249
+ console.log(result);
2250
+ }
2251
+ });
2252
+ repl.registerCommand({
2253
+ name: "logs",
2254
+ description: "Show recent server logs",
2255
+ handler: async () => {
2256
+ const { stdout } = await sshExec(config.sshPort, "tail -30 /home/nexus/.nexus/logs/server.log 2>/dev/null");
2257
+ console.log("");
2258
+ console.log(chalk13.bold(" Recent Logs:"));
2259
+ 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"));
2260
+ for (const line of redact(stdout).split("\n")) {
2261
+ console.log(chalk13.dim(" " + line));
2262
+ }
2263
+ console.log("");
2264
+ }
2265
+ });
2266
+ repl.registerCommand({
2267
+ name: "ssh",
2268
+ description: "Open interactive SSH session",
2269
+ handler: async () => {
2270
+ const { openInteractiveSsh: openInteractiveSsh2 } = await Promise.resolve().then(() => (init_ssh(), ssh_exports));
2271
+ eventStream.stop();
2272
+ await openInteractiveSsh2(config.sshPort);
2273
+ eventStream.start();
2274
+ }
2275
+ });
2276
+ process.on("SIGINT", () => {
2277
+ eventStream.stop();
2278
+ repl.stop();
2279
+ });
2280
+ await repl.start();
2281
+ } catch (err) {
2282
+ if (err.code === "ERR_USE_AFTER_CLOSE") {
2283
+ console.log("");
2284
+ log.success("Shell session ended");
2285
+ return;
2286
+ }
2287
+ const safeErr = redactError(err);
2288
+ log.error(`Shell failed: ${safeErr.message}`);
2289
+ process.exit(1);
2290
+ }
2291
+ });
2292
+
1702
2293
  // src/cli.ts
1703
- var cli = new Command12().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.2.8");
2294
+ var cli = new Command13().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.2.8");
1704
2295
  cli.addCommand(initCommand);
1705
2296
  cli.addCommand(startCommand);
1706
2297
  cli.addCommand(stopCommand);
@@ -1712,23 +2303,34 @@ cli.addCommand(destroyCommand);
1712
2303
  cli.addCommand(keysCommand);
1713
2304
  cli.addCommand(sshCommand);
1714
2305
  cli.addCommand(brainstormCommand);
1715
- cli.action(() => {
2306
+ cli.addCommand(shellCommand2);
2307
+ cli.action(async () => {
2308
+ try {
2309
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_secrets(), secrets_exports));
2310
+ const { isVmRunning: isVmRunning3 } = await Promise.resolve().then(() => (init_qemu(), qemu_exports));
2311
+ const config = loadConfig2();
2312
+ if (config && isVmRunning3()) {
2313
+ await shellCommand2.parseAsync([], { from: "user" });
2314
+ return;
2315
+ }
2316
+ } catch {
2317
+ }
1716
2318
  cli.help();
1717
2319
  });
1718
2320
 
1719
2321
  // src/core/update-notifier.ts
1720
- import fs10 from "fs";
1721
- import path11 from "path";
2322
+ import fs11 from "fs";
2323
+ import path12 from "path";
1722
2324
  import os3 from "os";
1723
2325
  import https from "https";
1724
- import chalk11 from "chalk";
2326
+ import chalk14 from "chalk";
1725
2327
  var PACKAGE_NAME = "buildwithnexus";
1726
2328
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
1727
- var STATE_DIR = path11.join(os3.homedir(), ".buildwithnexus");
1728
- var STATE_FILE = path11.join(STATE_DIR, ".update-check.json");
2329
+ var STATE_DIR = path12.join(os3.homedir(), ".buildwithnexus");
2330
+ var STATE_FILE = path12.join(STATE_DIR, ".update-check.json");
1729
2331
  function readState() {
1730
2332
  try {
1731
- const raw = fs10.readFileSync(STATE_FILE, "utf-8");
2333
+ const raw = fs11.readFileSync(STATE_FILE, "utf-8");
1732
2334
  return JSON.parse(raw);
1733
2335
  } catch {
1734
2336
  return { lastCheck: 0, latestVersion: null };
@@ -1736,8 +2338,8 @@ function readState() {
1736
2338
  }
1737
2339
  function writeState(state) {
1738
2340
  try {
1739
- fs10.mkdirSync(STATE_DIR, { recursive: true, mode: 448 });
1740
- fs10.writeFileSync(STATE_FILE, JSON.stringify(state), { mode: 384 });
2341
+ fs11.mkdirSync(STATE_DIR, { recursive: true, mode: 448 });
2342
+ fs11.writeFileSync(STATE_FILE, JSON.stringify(state), { mode: 384 });
1741
2343
  } catch {
1742
2344
  }
1743
2345
  }
@@ -1802,8 +2404,8 @@ async function checkForUpdates(currentVersion) {
1802
2404
  function printUpdateBanner(current, latest) {
1803
2405
  const msg = [
1804
2406
  "",
1805
- chalk11.yellow(` Update available: ${current} \u2192 ${latest}`),
1806
- chalk11.cyan(` Run: npm update -g buildwithnexus`),
2407
+ chalk14.yellow(` Update available: ${current} \u2192 ${latest}`),
2408
+ chalk14.cyan(` Run: npm update -g buildwithnexus`),
1807
2409
  ""
1808
2410
  ].join("\n");
1809
2411
  process.stderr.write(msg + "\n");