buildwithnexus 0.2.7 → 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.7 \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: "-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() });
@@ -593,31 +414,38 @@ async function resolvePortConflicts(ports) {
593
414
  async function launchVm(platform, diskPath, initIsoPath, ram, cpus, ports) {
594
415
  const machineArg = platform.os === "mac" ? "-machine virt,gic-version=3" : "-machine pc";
595
416
  const biosArgs = fs3.existsSync(platform.biosPath) ? ["-bios", platform.biosPath] : [];
596
- const args = [
597
- ...machineArg.split(" "),
598
- ...platform.qemuCpuFlag.split(" "),
599
- "-m",
600
- `${ram}G`,
601
- "-smp",
602
- `${cpus}`,
603
- "-drive",
604
- `file=${diskPath},if=virtio,cache=writethrough`,
605
- "-drive",
606
- `file=${initIsoPath},if=virtio,format=raw,cache=writethrough`,
607
- "-display",
608
- "none",
609
- "-serial",
610
- "none",
611
- "-net",
612
- "nic,model=virtio",
613
- "-net",
614
- `user,hostfwd=tcp::${ports.ssh}-:22,hostfwd=tcp::${ports.http}-:4200,hostfwd=tcp::${ports.https}-:443`,
615
- ...biosArgs,
616
- "-pidfile",
617
- PID_FILE,
618
- "-daemonize"
619
- ];
620
- await execa(platform.qemuBinary, args, { env: scrubEnv() });
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
+ }
443
+ try {
444
+ await execa(platform.qemuBinary, buildArgs(platform.qemuCpuFlag), { env: scrubEnv() });
445
+ } catch {
446
+ const fallbackCpu = platform.os === "mac" ? "-cpu max" : "-cpu qemu64";
447
+ await execa(platform.qemuBinary, buildArgs(fallbackCpu), { env: scrubEnv() });
448
+ }
621
449
  return ports;
622
450
  }
623
451
  function readValidPid() {
@@ -652,18 +480,36 @@ function stopVm() {
652
480
  function getVmPid() {
653
481
  return readValidPid();
654
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
+ });
655
495
 
656
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
+ });
657
508
  import fs4 from "fs";
658
509
  import path4 from "path";
659
510
  import crypto3 from "crypto";
660
511
  import { execa as execa2 } from "execa";
661
512
  import { NodeSSH } from "node-ssh";
662
- var SSH_DIR = path4.join(NEXUS_HOME2, "ssh");
663
- var SSH_KEY = path4.join(SSH_DIR, "id_nexus_vm");
664
- var SSH_PUB_KEY = path4.join(SSH_DIR, "id_nexus_vm.pub");
665
- var KNOWN_HOSTS = path4.join(SSH_DIR, "known_hosts_nexus_vm");
666
- var PINNED_HOST_KEY = path4.join(SSH_DIR, "vm_host_key.pin");
667
513
  function getHostVerifier() {
668
514
  if (!fs4.existsSync(PINNED_HOST_KEY)) {
669
515
  return (key) => {
@@ -681,6 +527,9 @@ function getHostVerifier() {
681
527
  return match;
682
528
  };
683
529
  }
530
+ function getKeyPath() {
531
+ return SSH_KEY;
532
+ }
684
533
  function getPubKey() {
685
534
  return fs4.readFileSync(SSH_PUB_KEY, "utf-8").trim();
686
535
  }
@@ -769,14 +618,256 @@ async function sshUploadFile(port, localPath, remotePath) {
769
618
  privateKeyPath: SSH_KEY,
770
619
  hostVerifier: getHostVerifier()
771
620
  });
772
- await ssh.putFile(localPath, remotePath);
773
- 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
+ };
774
823
  }
775
- async function openInteractiveSsh(port) {
776
- 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}`);
777
861
  }
778
862
 
863
+ // src/commands/init.ts
864
+ init_secrets();
865
+ init_qemu();
866
+ init_ssh();
867
+
779
868
  // src/core/cloudinit.ts
869
+ init_secrets();
870
+ init_dlp();
780
871
  import fs5 from "fs";
781
872
  import path5 from "path";
782
873
  import ejs from "ejs";
@@ -870,6 +961,7 @@ async function createCloudInitIso(userDataPath) {
870
961
  }
871
962
 
872
963
  // src/core/health.ts
964
+ init_ssh();
873
965
  async function checkHealth(port, vmRunning) {
874
966
  const status = {
875
967
  vmRunning,
@@ -930,6 +1022,8 @@ async function waitForCloudInit(port, timeoutMs = 9e5) {
930
1022
  }
931
1023
 
932
1024
  // src/core/tunnel.ts
1025
+ init_ssh();
1026
+ init_dlp();
933
1027
  var CLOUDFLARED_VERSION = "2024.12.2";
934
1028
  var CLOUDFLARED_SHA256 = {
935
1029
  amd64: "40ec9a0f5b58e3b04183aaf01c4ddd4dbc6af39b0f06be4b7ce8b1011d0a07ab",
@@ -977,6 +1071,8 @@ async function stopTunnel(sshPort) {
977
1071
  }
978
1072
 
979
1073
  // src/commands/init.ts
1074
+ init_dlp();
1075
+ init_ssh();
980
1076
  import fs6 from "fs";
981
1077
  import path6 from "path";
982
1078
  import os2 from "os";
@@ -1098,15 +1194,14 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1098
1194
  spinner.start();
1099
1195
  await sshUploadFile(config.sshPort, tarballPath, "/tmp/nexus-release.tar.gz");
1100
1196
  succeed(spinner, "Tarball uploaded");
1101
- spinner = createSpinner("Uploading API keys via SSH...");
1197
+ spinner = createSpinner("Staging API keys...");
1102
1198
  spinner.start();
1103
1199
  const keysContent = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
1104
1200
  const tmpKeysPath = path6.join(os2.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1105
1201
  fs6.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1106
1202
  try {
1107
- await sshExec(config.sshPort, "sudo -u nexus mkdir -p /home/nexus/.nexus");
1108
- await sshUploadFile(config.sshPort, tmpKeysPath, "/home/nexus/.nexus/.env.keys");
1109
- 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");
1110
1205
  } finally {
1111
1206
  try {
1112
1207
  fs6.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
@@ -1114,7 +1209,7 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1114
1209
  } catch {
1115
1210
  }
1116
1211
  }
1117
- succeed(spinner, "API keys delivered securely via SSH");
1212
+ succeed(spinner, "API keys staged");
1118
1213
  spinner = createSpinner("Cloud-init provisioning (extracting NEXUS, building Docker, installing deps)...");
1119
1214
  spinner.start();
1120
1215
  const cloudInitDone = await waitForCloudInit(config.sshPort);
@@ -1124,6 +1219,10 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1124
1219
  process.exit(1);
1125
1220
  }
1126
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");
1127
1226
  showPhase(8, TOTAL_PHASES, "NEXUS Server Startup");
1128
1227
  spinner = createSpinner("Waiting for NEXUS server...");
1129
1228
  spinner.start();
@@ -1166,6 +1265,10 @@ var initCommand = new Command("init").description("Scaffold and launch a new NEX
1166
1265
 
1167
1266
  // src/commands/start.ts
1168
1267
  import { Command as Command2 } from "commander";
1268
+ init_secrets();
1269
+ init_qemu();
1270
+ init_ssh();
1271
+ init_secrets();
1169
1272
  import path7 from "path";
1170
1273
  var startCommand = new Command2("start").description("Start the NEXUS runtime").action(async () => {
1171
1274
  const config = loadConfig();
@@ -1220,6 +1323,9 @@ var startCommand = new Command2("start").description("Start the NEXUS runtime").
1220
1323
 
1221
1324
  // src/commands/stop.ts
1222
1325
  import { Command as Command3 } from "commander";
1326
+ init_secrets();
1327
+ init_qemu();
1328
+ init_ssh();
1223
1329
  var stopCommand = new Command3("stop").description("Gracefully shut down the NEXUS runtime").action(async () => {
1224
1330
  const config = loadConfig();
1225
1331
  if (!config) {
@@ -1256,6 +1362,8 @@ var stopCommand = new Command3("stop").description("Gracefully shut down the NEX
1256
1362
  // src/commands/status.ts
1257
1363
  import { Command as Command4 } from "commander";
1258
1364
  import chalk6 from "chalk";
1365
+ init_secrets();
1366
+ init_qemu();
1259
1367
  var statusCommand = new Command4("status").description("Check NEXUS runtime health").option("--json", "Output as JSON").action(async (opts) => {
1260
1368
  const config = loadConfig();
1261
1369
  if (!config) {
@@ -1287,6 +1395,8 @@ var statusCommand = new Command4("status").description("Check NEXUS runtime heal
1287
1395
  import { Command as Command5 } from "commander";
1288
1396
  import chalk7 from "chalk";
1289
1397
  import fs7 from "fs";
1398
+ init_qemu();
1399
+ init_secrets();
1290
1400
  import path8 from "path";
1291
1401
  import { execa as execa4 } from "execa";
1292
1402
  var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime environment").action(async () => {
@@ -1354,6 +1464,10 @@ var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime e
1354
1464
 
1355
1465
  // src/commands/logs.ts
1356
1466
  import { Command as Command6 } from "commander";
1467
+ init_secrets();
1468
+ init_qemu();
1469
+ init_ssh();
1470
+ init_dlp();
1357
1471
  import { execa as execa5 } from "execa";
1358
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) => {
1359
1473
  const config = loadConfig();
@@ -1392,6 +1506,9 @@ import { Command as Command7 } from "commander";
1392
1506
  import path9 from "path";
1393
1507
  import fs8 from "fs";
1394
1508
  import { fileURLToPath as fileURLToPath2 } from "url";
1509
+ init_secrets();
1510
+ init_qemu();
1511
+ init_ssh();
1395
1512
  function getReleaseTarball2() {
1396
1513
  const dir = path9.dirname(fileURLToPath2(import.meta.url));
1397
1514
  const tarballPath = path9.join(dir, "nexus-release.tar.gz");
@@ -1450,6 +1567,8 @@ import { Command as Command8 } from "commander";
1450
1567
  import chalk8 from "chalk";
1451
1568
  import fs9 from "fs";
1452
1569
  import { input as input2 } from "@inquirer/prompts";
1570
+ init_secrets();
1571
+ init_qemu();
1453
1572
  import path10 from "path";
1454
1573
  var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and all data").option("--force", "Skip confirmation").action(async (opts) => {
1455
1574
  const config = loadConfig();
@@ -1504,6 +1623,8 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1504
1623
  import { Command as Command9 } from "commander";
1505
1624
  import { password as password2 } from "@inquirer/prompts";
1506
1625
  import chalk9 from "chalk";
1626
+ init_secrets();
1627
+ init_dlp();
1507
1628
  var keysCommand = new Command9("keys").description("Manage API keys");
1508
1629
  keysCommand.command("list").description("Show configured API keys (masked)").action(() => {
1509
1630
  const keys = loadKeys();
@@ -1560,6 +1681,9 @@ keysCommand.command("set <key>").description("Set or update an API key (e.g. ANT
1560
1681
 
1561
1682
  // src/commands/ssh.ts
1562
1683
  import { Command as Command10 } from "commander";
1684
+ init_secrets();
1685
+ init_qemu();
1686
+ init_ssh();
1563
1687
  var sshCommand = new Command10("ssh").description("Open an SSH session into the NEXUS VM").action(async () => {
1564
1688
  const config = loadConfig();
1565
1689
  if (!config) {
@@ -1578,6 +1702,10 @@ var sshCommand = new Command10("ssh").description("Open an SSH session into the
1578
1702
  import { Command as Command11 } from "commander";
1579
1703
  import chalk10 from "chalk";
1580
1704
  import { input as input3 } from "@inquirer/prompts";
1705
+ init_secrets();
1706
+ init_qemu();
1707
+ init_ssh();
1708
+ init_dlp();
1581
1709
  var COS_PREFIX = chalk10.bold.cyan(" Chief of Staff");
1582
1710
  var YOU_PREFIX = chalk10.bold.white(" You");
1583
1711
  var DIVIDER = chalk10.dim(" " + "\u2500".repeat(56));
@@ -1692,8 +1820,478 @@ var brainstormCommand = new Command11("brainstorm").description("Brainstorm an i
1692
1820
  }
1693
1821
  });
1694
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
+
1695
2293
  // src/cli.ts
1696
- var cli = new Command12().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.2.7");
2294
+ var cli = new Command13().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.2.8");
1697
2295
  cli.addCommand(initCommand);
1698
2296
  cli.addCommand(startCommand);
1699
2297
  cli.addCommand(stopCommand);
@@ -1705,23 +2303,34 @@ cli.addCommand(destroyCommand);
1705
2303
  cli.addCommand(keysCommand);
1706
2304
  cli.addCommand(sshCommand);
1707
2305
  cli.addCommand(brainstormCommand);
1708
- 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
+ }
1709
2318
  cli.help();
1710
2319
  });
1711
2320
 
1712
2321
  // src/core/update-notifier.ts
1713
- import fs10 from "fs";
1714
- import path11 from "path";
2322
+ import fs11 from "fs";
2323
+ import path12 from "path";
1715
2324
  import os3 from "os";
1716
2325
  import https from "https";
1717
- import chalk11 from "chalk";
2326
+ import chalk14 from "chalk";
1718
2327
  var PACKAGE_NAME = "buildwithnexus";
1719
2328
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
1720
- var STATE_DIR = path11.join(os3.homedir(), ".buildwithnexus");
1721
- 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");
1722
2331
  function readState() {
1723
2332
  try {
1724
- const raw = fs10.readFileSync(STATE_FILE, "utf-8");
2333
+ const raw = fs11.readFileSync(STATE_FILE, "utf-8");
1725
2334
  return JSON.parse(raw);
1726
2335
  } catch {
1727
2336
  return { lastCheck: 0, latestVersion: null };
@@ -1729,8 +2338,8 @@ function readState() {
1729
2338
  }
1730
2339
  function writeState(state) {
1731
2340
  try {
1732
- fs10.mkdirSync(STATE_DIR, { recursive: true, mode: 448 });
1733
- 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 });
1734
2343
  } catch {
1735
2344
  }
1736
2345
  }
@@ -1795,8 +2404,8 @@ async function checkForUpdates(currentVersion) {
1795
2404
  function printUpdateBanner(current, latest) {
1796
2405
  const msg = [
1797
2406
  "",
1798
- chalk11.yellow(` Update available: ${current} \u2192 ${latest}`),
1799
- chalk11.cyan(` Run: npm update -g buildwithnexus`),
2407
+ chalk14.yellow(` Update available: ${current} \u2192 ${latest}`),
2408
+ chalk14.cyan(` Run: npm update -g buildwithnexus`),
1800
2409
  ""
1801
2410
  ].join("\n");
1802
2411
  process.stderr.write(msg + "\n");