buildwithnexus 0.5.13 → 0.5.15

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
@@ -23,12 +23,12 @@ import { dirname, join } from "path";
23
23
  import { fileURLToPath } from "url";
24
24
  function getVersion() {
25
25
  try {
26
- const __dirname2 = dirname(fileURLToPath(import.meta.url));
27
- const packagePath = join(__dirname2, "..", "..", "package.json");
28
- const packageJson2 = JSON.parse(readFileSync(packagePath, "utf-8"));
29
- return packageJson2.version;
26
+ const __dirname = dirname(fileURLToPath(import.meta.url));
27
+ const packagePath = join(__dirname, "..", "..", "package.json");
28
+ const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
29
+ return packageJson.version;
30
30
  } catch (e) {
31
- return "0.5.10";
31
+ return "0.5.15";
32
32
  }
33
33
  }
34
34
  function showBanner() {
@@ -296,26 +296,26 @@ __export(secrets_exports, {
296
296
  saveConfig: () => saveConfig,
297
297
  saveKeys: () => saveKeys
298
298
  });
299
- import fs3 from "fs";
300
- import path3 from "path";
299
+ import fs2 from "fs";
300
+ import path2 from "path";
301
301
  import crypto2 from "crypto";
302
302
  function ensureHome() {
303
- fs3.mkdirSync(NEXUS_HOME2, { recursive: true, mode: 448 });
304
- fs3.mkdirSync(path3.join(NEXUS_HOME2, "vm", "images"), { recursive: true, mode: 448 });
305
- fs3.mkdirSync(path3.join(NEXUS_HOME2, "vm", "configs"), { recursive: true, mode: 448 });
306
- fs3.mkdirSync(path3.join(NEXUS_HOME2, "vm", "logs"), { recursive: true, mode: 448 });
307
- fs3.mkdirSync(path3.join(NEXUS_HOME2, "ssh"), { recursive: true, mode: 448 });
303
+ fs2.mkdirSync(NEXUS_HOME2, { recursive: true, mode: 448 });
304
+ fs2.mkdirSync(path2.join(NEXUS_HOME2, "vm", "images"), { recursive: true, mode: 448 });
305
+ fs2.mkdirSync(path2.join(NEXUS_HOME2, "vm", "configs"), { recursive: true, mode: 448 });
306
+ fs2.mkdirSync(path2.join(NEXUS_HOME2, "vm", "logs"), { recursive: true, mode: 448 });
307
+ fs2.mkdirSync(path2.join(NEXUS_HOME2, "ssh"), { recursive: true, mode: 448 });
308
308
  }
309
309
  function generateMasterSecret() {
310
310
  return crypto2.randomBytes(32).toString("base64url");
311
311
  }
312
312
  function saveConfig(config) {
313
313
  const { masterSecret: _secret, ...safeConfig } = config;
314
- fs3.writeFileSync(CONFIG_PATH, JSON.stringify(safeConfig, null, 2), { mode: 384 });
314
+ fs2.writeFileSync(CONFIG_PATH, JSON.stringify(safeConfig, null, 2), { mode: 384 });
315
315
  }
316
316
  function loadConfig() {
317
- if (!fs3.existsSync(CONFIG_PATH)) return null;
318
- return JSON.parse(fs3.readFileSync(CONFIG_PATH, "utf-8"));
317
+ if (!fs2.existsSync(CONFIG_PATH)) return null;
318
+ return JSON.parse(fs2.readFileSync(CONFIG_PATH, "utf-8"));
319
319
  }
320
320
  function saveKeys(keys) {
321
321
  const violations = validateAllKeys(keys);
@@ -323,13 +323,13 @@ function saveKeys(keys) {
323
323
  throw new DlpViolation(`Key validation failed: ${violations.join("; ")}`);
324
324
  }
325
325
  const lines = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`);
326
- fs3.writeFileSync(KEYS_PATH, lines.join("\n") + "\n", { mode: 384 });
326
+ fs2.writeFileSync(KEYS_PATH, lines.join("\n") + "\n", { mode: 384 });
327
327
  sealKeysFile(KEYS_PATH, keys.NEXUS_MASTER_SECRET);
328
328
  audit("keys_saved", `${Object.keys(keys).filter((k) => keys[k]).length} keys saved`);
329
329
  }
330
330
  function loadKeys() {
331
- if (!fs3.existsSync(KEYS_PATH)) return null;
332
- const content = fs3.readFileSync(KEYS_PATH, "utf-8");
331
+ if (!fs2.existsSync(KEYS_PATH)) return null;
332
+ const content = fs2.readFileSync(KEYS_PATH, "utf-8");
333
333
  const keys = {};
334
334
  for (const line of content.split("\n")) {
335
335
  const eq = line.indexOf("=");
@@ -355,9 +355,9 @@ var init_secrets = __esm({
355
355
  "src/core/secrets.ts"() {
356
356
  "use strict";
357
357
  init_dlp();
358
- NEXUS_HOME2 = process.env.NEXUS_HOME || path3.join(process.env.HOME || "~", ".buildwithnexus");
359
- CONFIG_PATH = process.env.NEXUS_CONFIG_PATH || path3.join(NEXUS_HOME2, "config.json");
360
- KEYS_PATH = process.env.NEXUS_KEYS_PATH || path3.join(NEXUS_HOME2, ".env.keys");
358
+ NEXUS_HOME2 = process.env.NEXUS_HOME || path2.join(process.env.HOME || "~", ".buildwithnexus");
359
+ CONFIG_PATH = process.env.NEXUS_CONFIG_PATH || path2.join(NEXUS_HOME2, "config.json");
360
+ KEYS_PATH = process.env.NEXUS_KEYS_PATH || path2.join(NEXUS_HOME2, ".env.keys");
361
361
  }
362
362
  });
363
363
 
@@ -374,9 +374,9 @@ __export(qemu_exports, {
374
374
  resolvePortConflicts: () => resolvePortConflicts,
375
375
  stopVm: () => stopVm
376
376
  });
377
- import fs4 from "fs";
377
+ import fs3 from "fs";
378
378
  import net from "net";
379
- import path4 from "path";
379
+ import path3 from "path";
380
380
  import { execa, execaSync } from "execa";
381
381
  import { select } from "@inquirer/prompts";
382
382
  import chalk5 from "chalk";
@@ -409,15 +409,15 @@ async function installQemu(platform) {
409
409
  }
410
410
  }
411
411
  async function downloadImage(platform) {
412
- const imagePath = path4.join(IMAGES_DIR, platform.ubuntuImage);
413
- if (fs4.existsSync(imagePath)) return imagePath;
412
+ const imagePath = path3.join(IMAGES_DIR, platform.ubuntuImage);
413
+ if (fs3.existsSync(imagePath)) return imagePath;
414
414
  const url = `${UBUNTU_BASE_URL}/${platform.ubuntuImage}`;
415
415
  await execa("curl", ["-L", "-C", "-", "-o", imagePath, "--progress-bar", url], { stdio: "inherit", env: scrubEnv() });
416
416
  return imagePath;
417
417
  }
418
418
  async function createDisk(basePath, sizeGb) {
419
- const diskPath = path4.join(IMAGES_DIR, "nexus-vm-disk.qcow2");
420
- if (fs4.existsSync(diskPath)) return diskPath;
419
+ const diskPath = path3.join(IMAGES_DIR, "nexus-vm-disk.qcow2");
420
+ if (fs3.existsSync(diskPath)) return diskPath;
421
421
  await execa("qemu-img", ["create", "-f", "qcow2", "-b", basePath, "-F", "qcow2", diskPath, `${sizeGb}G`], { env: scrubEnv() });
422
422
  return diskPath;
423
423
  }
@@ -511,36 +511,31 @@ async function resolvePortConflicts(ports) {
511
511
  }
512
512
  async function launchVm(platform, diskPath, initIsoPath, ram, cpus, ports) {
513
513
  const machineArgs = platform.os === "mac" ? ["-machine", "virt,gic-version=3"] : ["-machine", "pc"];
514
- const biosArgs = fs4.existsSync(platform.biosPath) ? ["-bios", platform.biosPath] : [];
515
- const buildArgs = (cpuArgs) => {
516
- const logsDir = path4.join(NEXUS_HOME2, "vm", "logs");
517
- fs4.mkdirSync(logsDir, { recursive: true });
518
- const serialLogPath = path4.join(logsDir, "serial.log");
519
- return [
520
- ...machineArgs,
521
- ...cpuArgs,
522
- "-m",
523
- `${ram}G`,
524
- "-smp",
525
- `${cpus}`,
526
- "-drive",
527
- `file=${diskPath},if=virtio,cache=writethrough`,
528
- "-drive",
529
- `file=${initIsoPath},if=ide,media=cdrom`,
530
- "-display",
531
- "none",
532
- "-serial",
533
- `file:${serialLogPath}`,
534
- "-net",
535
- "nic,model=virtio",
536
- "-net",
537
- `user,hostfwd=tcp::${ports.ssh}-:22,hostfwd=tcp::${ports.http}-:4200,hostfwd=tcp::${ports.https}-:443`,
538
- ...biosArgs,
539
- "-pidfile",
540
- PID_FILE,
541
- "-daemonize"
542
- ];
543
- };
514
+ const biosArgs = fs3.existsSync(platform.biosPath) ? ["-bios", platform.biosPath] : [];
515
+ const buildArgs = (cpuArgs) => [
516
+ ...machineArgs,
517
+ ...cpuArgs,
518
+ "-m",
519
+ `${ram}G`,
520
+ "-smp",
521
+ `${cpus}`,
522
+ "-drive",
523
+ `file=${diskPath},if=virtio,cache=writethrough`,
524
+ "-drive",
525
+ `file=${initIsoPath},if=virtio,format=raw,cache=writethrough`,
526
+ "-display",
527
+ "none",
528
+ "-serial",
529
+ "none",
530
+ "-net",
531
+ "nic,model=virtio",
532
+ "-net",
533
+ `user,hostfwd=tcp::${ports.ssh}-:22,hostfwd=tcp::${ports.http}-:4200,hostfwd=tcp::${ports.https}-:443`,
534
+ ...biosArgs,
535
+ "-pidfile",
536
+ PID_FILE,
537
+ "-daemonize"
538
+ ];
544
539
  try {
545
540
  await execa(platform.qemuBinary, buildArgs(platform.qemuCpuFlag.split(" ")), { env: scrubEnv() });
546
541
  } catch {
@@ -550,8 +545,8 @@ async function launchVm(platform, diskPath, initIsoPath, ram, cpus, ports) {
550
545
  return ports;
551
546
  }
552
547
  function readValidPid() {
553
- if (!fs4.existsSync(PID_FILE)) return null;
554
- const raw = fs4.readFileSync(PID_FILE, "utf-8").trim();
548
+ if (!fs3.existsSync(PID_FILE)) return null;
549
+ const raw = fs3.readFileSync(PID_FILE, "utf-8").trim();
555
550
  const pid = parseInt(raw, 10);
556
551
  if (!Number.isInteger(pid) || pid <= 1 || pid > 4194304) return null;
557
552
  return pid;
@@ -579,7 +574,7 @@ function stopVm() {
579
574
  if (!pid) return;
580
575
  if (!isQemuPid(pid)) {
581
576
  try {
582
- fs4.unlinkSync(PID_FILE);
577
+ fs3.unlinkSync(PID_FILE);
583
578
  } catch {
584
579
  }
585
580
  return;
@@ -589,7 +584,7 @@ function stopVm() {
589
584
  } catch {
590
585
  }
591
586
  try {
592
- fs4.unlinkSync(PID_FILE);
587
+ fs3.unlinkSync(PID_FILE);
593
588
  } catch {
594
589
  }
595
590
  }
@@ -602,9 +597,9 @@ var init_qemu = __esm({
602
597
  "use strict";
603
598
  init_secrets();
604
599
  init_dlp();
605
- VM_DIR = path4.join(NEXUS_HOME2, "vm");
606
- IMAGES_DIR = path4.join(VM_DIR, "images");
607
- PID_FILE = path4.join(VM_DIR, "qemu.pid");
600
+ VM_DIR = path3.join(NEXUS_HOME2, "vm");
601
+ IMAGES_DIR = path3.join(VM_DIR, "images");
602
+ PID_FILE = path3.join(VM_DIR, "qemu.pid");
608
603
  UBUNTU_BASE_URL = "https://cloud-images.ubuntu.com/jammy/current";
609
604
  }
610
605
  });
@@ -621,21 +616,21 @@ __export(ssh_exports, {
621
616
  sshUploadFile: () => sshUploadFile,
622
617
  waitForSsh: () => waitForSsh
623
618
  });
624
- import fs5 from "fs";
625
- import path5 from "path";
619
+ import fs4 from "fs";
620
+ import path4 from "path";
626
621
  import crypto3 from "crypto";
627
622
  import { execa as execa2 } from "execa";
628
623
  import { NodeSSH } from "node-ssh";
629
624
  function getHostVerifier() {
630
- if (!fs5.existsSync(PINNED_HOST_KEY)) {
625
+ if (!fs4.existsSync(PINNED_HOST_KEY)) {
631
626
  return (key) => {
632
627
  const fp = crypto3.createHash("sha256").update(key).digest("base64");
633
- fs5.writeFileSync(PINNED_HOST_KEY, fp, { mode: 384 });
628
+ fs4.writeFileSync(PINNED_HOST_KEY, fp, { mode: 384 });
634
629
  audit("ssh_exec", `host key pinned: SHA256:${fp}`);
635
630
  return true;
636
631
  };
637
632
  }
638
- const pinned = fs5.readFileSync(PINNED_HOST_KEY, "utf-8").trim();
633
+ const pinned = fs4.readFileSync(PINNED_HOST_KEY, "utf-8").trim();
639
634
  return (key) => {
640
635
  const fp = crypto3.createHash("sha256").update(key).digest("base64");
641
636
  const match = fp === pinned;
@@ -647,11 +642,11 @@ function getKeyPath() {
647
642
  return SSH_KEY;
648
643
  }
649
644
  function getPubKey() {
650
- return fs5.readFileSync(SSH_PUB_KEY, "utf-8").trim();
645
+ return fs4.readFileSync(SSH_PUB_KEY, "utf-8").trim();
651
646
  }
652
647
  async function generateSshKey() {
653
- if (fs5.existsSync(SSH_KEY)) return;
654
- fs5.mkdirSync(SSH_DIR, { recursive: true });
648
+ if (fs4.existsSync(SSH_KEY)) return;
649
+ fs4.mkdirSync(SSH_DIR, { recursive: true });
655
650
  await execa2("ssh-keygen", [
656
651
  "-t",
657
652
  "ed25519",
@@ -663,13 +658,13 @@ async function generateSshKey() {
663
658
  "buildwithnexus@localhost",
664
659
  "-q"
665
660
  ], { env: scrubEnv() });
666
- fs5.chmodSync(SSH_KEY, 384);
667
- fs5.chmodSync(SSH_PUB_KEY, 420);
661
+ fs4.chmodSync(SSH_KEY, 384);
662
+ fs4.chmodSync(SSH_PUB_KEY, 420);
668
663
  }
669
664
  function addSshConfig(port) {
670
- const sshConfigPath = path5.join(process.env.HOME || "~", ".ssh", "config");
671
- const sshDir = path5.dirname(sshConfigPath);
672
- fs5.mkdirSync(sshDir, { recursive: true });
665
+ const sshConfigPath = path4.join(process.env.HOME || "~", ".ssh", "config");
666
+ const sshDir = path4.dirname(sshConfigPath);
667
+ fs4.mkdirSync(sshDir, { recursive: true });
673
668
  const block = [
674
669
  "",
675
670
  "Host nexus-vm",
@@ -682,21 +677,19 @@ function addSshConfig(port) {
682
677
  " ServerAliveInterval 60",
683
678
  ""
684
679
  ].join("\n");
685
- if (fs5.existsSync(sshConfigPath)) {
686
- const existing = fs5.readFileSync(sshConfigPath, "utf-8");
680
+ if (fs4.existsSync(sshConfigPath)) {
681
+ const existing = fs4.readFileSync(sshConfigPath, "utf-8");
687
682
  if (existing.includes("Host nexus-vm")) return;
688
- fs5.appendFileSync(sshConfigPath, block);
683
+ fs4.appendFileSync(sshConfigPath, block);
689
684
  } else {
690
- fs5.writeFileSync(sshConfigPath, block, { mode: 384 });
685
+ fs4.writeFileSync(sshConfigPath, block, { mode: 384 });
691
686
  }
692
687
  }
693
- async function waitForSsh(port, timeoutMs = 6e5) {
688
+ async function waitForSsh(port, timeoutMs = 9e5) {
694
689
  const start = Date.now();
695
- let attempt = 0;
696
- const backoffMs = (n) => Math.min(3e3 * Math.pow(2, n), 3e4);
697
690
  while (Date.now() - start < timeoutMs) {
698
- const ssh = new NodeSSH();
699
691
  try {
692
+ const ssh = new NodeSSH();
700
693
  await ssh.connect({
701
694
  host: "localhost",
702
695
  port,
@@ -708,14 +701,7 @@ async function waitForSsh(port, timeoutMs = 6e5) {
708
701
  ssh.dispose();
709
702
  return true;
710
703
  } catch {
711
- try {
712
- ssh.dispose();
713
- } catch {
714
- }
715
- const delay = backoffMs(attempt++);
716
- const remaining = timeoutMs - (Date.now() - start);
717
- if (remaining <= 0) break;
718
- await new Promise((r) => setTimeout(r, Math.min(delay, remaining)));
704
+ await new Promise((r) => setTimeout(r, 1e4));
719
705
  }
720
706
  }
721
707
  return false;
@@ -728,6 +714,7 @@ async function sshExec(port, command) {
728
714
  port,
729
715
  username: "nexus",
730
716
  privateKeyPath: SSH_KEY,
717
+ readyTimeout: 3e4,
731
718
  hostVerifier: getHostVerifier()
732
719
  });
733
720
  const result = await ssh.execCommand(command);
@@ -755,19 +742,16 @@ var init_ssh = __esm({
755
742
  "use strict";
756
743
  init_secrets();
757
744
  init_dlp();
758
- SSH_DIR = path5.join(NEXUS_HOME2, "ssh");
759
- SSH_KEY = path5.join(SSH_DIR, "id_nexus_vm");
760
- SSH_PUB_KEY = path5.join(SSH_DIR, "id_nexus_vm.pub");
761
- KNOWN_HOSTS = path5.join(SSH_DIR, "known_hosts_nexus_vm");
762
- PINNED_HOST_KEY = path5.join(SSH_DIR, "vm_host_key.pin");
745
+ SSH_DIR = path4.join(NEXUS_HOME2, "ssh");
746
+ SSH_KEY = path4.join(SSH_DIR, "id_nexus_vm");
747
+ SSH_PUB_KEY = path4.join(SSH_DIR, "id_nexus_vm.pub");
748
+ KNOWN_HOSTS = path4.join(SSH_DIR, "known_hosts_nexus_vm");
749
+ PINNED_HOST_KEY = path4.join(SSH_DIR, "vm_host_key.pin");
763
750
  }
764
751
  });
765
752
 
766
753
  // src/cli.ts
767
754
  import { Command as Command14 } from "commander";
768
- import { readFileSync as readFileSync2 } from "fs";
769
- import { dirname as dirname2, join as join2 } from "path";
770
- import { fileURLToPath as fileURLToPath4 } from "url";
771
755
 
772
756
  // src/commands/init.ts
773
757
  init_banner();
@@ -821,39 +805,6 @@ var log = {
821
805
  init_dlp();
822
806
  import { input, confirm, password } from "@inquirer/prompts";
823
807
  import chalk4 from "chalk";
824
- import fs2 from "fs";
825
- import path2 from "path";
826
- import os from "os";
827
- function loadInitConfigFromDisk() {
828
- const homeDir = os.homedir();
829
- const configPath = path2.join(homeDir, ".buildwithnexus", "config.json");
830
- const keysPath = path2.join(homeDir, ".buildwithnexus", ".env.keys");
831
- if (!fs2.existsSync(configPath)) return null;
832
- try {
833
- const configData = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
834
- const keysData = {};
835
- if (fs2.existsSync(keysPath)) {
836
- const keysContent = fs2.readFileSync(keysPath, "utf-8");
837
- keysContent.split("\n").forEach((line) => {
838
- const [key, ...valueParts] = line.split("=");
839
- if (key && valueParts.length > 0) {
840
- keysData[key] = valueParts.join("=");
841
- }
842
- });
843
- }
844
- return {
845
- anthropicKey: keysData["ANTHROPIC_API_KEY"] || "",
846
- openaiKey: keysData["OPENAI_API_KEY"] || "",
847
- googleKey: keysData["GOOGLE_API_KEY"] || "",
848
- vmRam: configData.vmRam || 4,
849
- vmCpus: configData.vmCpus || 2,
850
- vmDisk: configData.vmDisk || 10,
851
- enableTunnel: configData.enableTunnel !== false
852
- };
853
- } catch {
854
- return null;
855
- }
856
- }
857
808
  async function promptInitConfig() {
858
809
  console.log(chalk4.bold("\n API Keys\n"));
859
810
  const anthropicKey = await password({
@@ -947,10 +898,10 @@ async function promptInitConfig() {
947
898
  }
948
899
 
949
900
  // src/core/platform.ts
950
- import os2 from "os";
901
+ import os from "os";
951
902
  function detectPlatform() {
952
- const platform = os2.platform();
953
- const arch = os2.arch();
903
+ const platform = os.platform();
904
+ const arch = os.arch();
954
905
  if (platform === "darwin") {
955
906
  return {
956
907
  os: "mac",
@@ -992,12 +943,12 @@ init_ssh();
992
943
  // src/core/cloudinit.ts
993
944
  init_secrets();
994
945
  init_dlp();
995
- import fs6 from "fs";
996
- import path6 from "path";
946
+ import fs5 from "fs";
947
+ import path5 from "path";
997
948
  import ejs from "ejs";
998
949
  import { execa as execa3 } from "execa";
999
- var CONFIGS_DIR = path6.join(NEXUS_HOME2, "vm", "configs");
1000
- var IMAGES_DIR2 = path6.join(NEXUS_HOME2, "vm", "images");
950
+ var CONFIGS_DIR = path5.join(NEXUS_HOME2, "vm", "configs");
951
+ var IMAGES_DIR2 = path5.join(NEXUS_HOME2, "vm", "images");
1001
952
  async function renderCloudInit(data, templateContent) {
1002
953
  const trimmedPubKey = data.sshPubKey.trim();
1003
954
  if (!/^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp\d+) [A-Za-z0-9+/=]+ ?\S*$/.test(trimmedPubKey)) {
@@ -1009,22 +960,17 @@ async function renderCloudInit(data, templateContent) {
1009
960
  }
1010
961
  const safeData = { ...data, sshPubKey: yamlEscape(trimmedPubKey), keys: safeKeys };
1011
962
  const rendered = ejs.render(templateContent, safeData);
1012
- const outputPath = path6.join(CONFIGS_DIR, "user-data.yaml");
1013
- fs6.writeFileSync(outputPath, rendered, { mode: 384 });
963
+ const outputPath = path5.join(CONFIGS_DIR, "user-data.yaml");
964
+ fs5.writeFileSync(outputPath, rendered, { mode: 384 });
1014
965
  audit("cloudinit_rendered", "user-data.yaml written");
1015
966
  return outputPath;
1016
967
  }
1017
968
  async function createCloudInitIso(userDataPath) {
1018
- const metaDataPath = path6.join(CONFIGS_DIR, "meta-data.yaml");
1019
- fs6.writeFileSync(metaDataPath, "instance-id: nexus-vm-1\nlocal-hostname: nexus-vm\n", { mode: 384 });
1020
- const isoPath = path6.join(IMAGES_DIR2, "init.iso");
969
+ const metaDataPath = path5.join(CONFIGS_DIR, "meta-data.yaml");
970
+ fs5.writeFileSync(metaDataPath, "instance-id: nexus-vm-1\nlocal-hostname: nexus-vm\n", { mode: 384 });
971
+ const isoPath = path5.join(IMAGES_DIR2, "init.iso");
1021
972
  const env = scrubEnv();
1022
973
  try {
1023
- const stagingDir = path6.join(CONFIGS_DIR, "cidata-staging");
1024
- fs6.rmSync(stagingDir, { recursive: true, force: true });
1025
- fs6.mkdirSync(stagingDir, { recursive: true, mode: 448 });
1026
- fs6.copyFileSync(userDataPath, path6.join(stagingDir, "user-data"));
1027
- fs6.copyFileSync(metaDataPath, path6.join(stagingDir, "meta-data"));
1028
974
  let created = false;
1029
975
  for (const tool of ["mkisofs", "genisoimage"]) {
1030
976
  if (created) break;
@@ -1036,7 +982,8 @@ async function createCloudInitIso(userDataPath) {
1036
982
  "cidata",
1037
983
  "-joliet",
1038
984
  "-rock",
1039
- stagingDir
985
+ userDataPath,
986
+ metaDataPath
1040
987
  ], { env });
1041
988
  created = true;
1042
989
  } catch {
@@ -1044,10 +991,10 @@ async function createCloudInitIso(userDataPath) {
1044
991
  }
1045
992
  if (!created) {
1046
993
  try {
1047
- const stagingDir2 = path6.join(CONFIGS_DIR, "cidata-staging");
1048
- fs6.mkdirSync(stagingDir2, { recursive: true, mode: 448 });
1049
- fs6.copyFileSync(userDataPath, path6.join(stagingDir2, "user-data"));
1050
- fs6.copyFileSync(metaDataPath, path6.join(stagingDir2, "meta-data"));
994
+ const stagingDir = path5.join(CONFIGS_DIR, "cidata-staging");
995
+ fs5.mkdirSync(stagingDir, { recursive: true, mode: 448 });
996
+ fs5.copyFileSync(userDataPath, path5.join(stagingDir, "user-data"));
997
+ fs5.copyFileSync(metaDataPath, path5.join(stagingDir, "meta-data"));
1051
998
  await execa3("hdiutil", [
1052
999
  "makehybrid",
1053
1000
  "-o",
@@ -1056,9 +1003,9 @@ async function createCloudInitIso(userDataPath) {
1056
1003
  "-iso",
1057
1004
  "-default-volume-name",
1058
1005
  "cidata",
1059
- stagingDir2
1006
+ stagingDir
1060
1007
  ], { env });
1061
- fs6.rmSync(stagingDir2, { recursive: true, force: true });
1008
+ fs5.rmSync(stagingDir, { recursive: true, force: true });
1062
1009
  created = true;
1063
1010
  } catch {
1064
1011
  }
@@ -1068,16 +1015,16 @@ async function createCloudInitIso(userDataPath) {
1068
1015
  "Cannot create cloud-init ISO: none of mkisofs, genisoimage, or hdiutil are available. On macOS, install cdrtools: brew install cdrtools. On Linux: sudo apt install genisoimage"
1069
1016
  );
1070
1017
  }
1071
- fs6.chmodSync(isoPath, 384);
1018
+ fs5.chmodSync(isoPath, 384);
1072
1019
  audit("cloudinit_iso_created", "init.iso created");
1073
1020
  return isoPath;
1074
1021
  } finally {
1075
1022
  try {
1076
- fs6.unlinkSync(userDataPath);
1023
+ fs5.unlinkSync(userDataPath);
1077
1024
  } catch {
1078
1025
  }
1079
1026
  try {
1080
- fs6.unlinkSync(metaDataPath);
1027
+ fs5.unlinkSync(metaDataPath);
1081
1028
  } catch {
1082
1029
  }
1083
1030
  audit("cloudinit_plaintext_deleted", "user-data.yaml and meta-data.yaml removed");
@@ -1225,14 +1172,12 @@ async function installCloudflared(sshPort, arch) {
1225
1172
  ].join(" && "));
1226
1173
  }
1227
1174
  async function startTunnel(sshPort) {
1228
- await sshExec(sshPort, [
1229
- "install -m 600 /dev/null /home/nexus/.nexus/tunnel.log",
1230
- "&& nohup cloudflared tunnel --no-autoupdate --url http://localhost:4200",
1231
- "> /home/nexus/.nexus/tunnel.log 2>&1 &",
1232
- "disown"
1233
- ].join(" "));
1175
+ await sshExec(
1176
+ sshPort,
1177
+ "install -m 600 /dev/null /home/nexus/.nexus/tunnel.log && bash -c 'nohup cloudflared tunnel --no-autoupdate --url http://localhost:4200 > /home/nexus/.nexus/tunnel.log 2>&1 &'"
1178
+ );
1234
1179
  const start = Date.now();
1235
- while (Date.now() - start < 6e4) {
1180
+ while (Date.now() - start < 12e4) {
1236
1181
  try {
1237
1182
  const { stdout } = await sshExec(sshPort, "grep -oE 'https://[a-z0-9-]+\\.trycloudflare\\.com' /home/nexus/.nexus/tunnel.log 2>/dev/null | head -1");
1238
1183
  if (stdout.includes("https://")) {
@@ -1258,25 +1203,32 @@ async function stopTunnel(sshPort) {
1258
1203
  // src/commands/init.ts
1259
1204
  init_dlp();
1260
1205
  init_ssh();
1261
- import fs7 from "fs";
1262
- import path7 from "path";
1263
- import os3 from "os";
1206
+ import fs6 from "fs";
1207
+ import path6 from "path";
1208
+ import os2 from "os";
1264
1209
  import crypto4 from "crypto";
1265
1210
  import { fileURLToPath as fileURLToPath2 } from "url";
1266
1211
  function getReleaseTarball() {
1267
- const dir = path7.dirname(fileURLToPath2(import.meta.url));
1268
- const tarballPath = path7.join(dir, "nexus-release.tar.gz");
1269
- if (fs7.existsSync(tarballPath)) return tarballPath;
1270
- const rootPath = path7.resolve(dir, "..", "dist", "nexus-release.tar.gz");
1271
- if (fs7.existsSync(rootPath)) return rootPath;
1272
- throw new Error("nexus-release.tar.gz not found. Run: npm run bundle");
1212
+ const dir = path6.dirname(fileURLToPath2(import.meta.url));
1213
+ const possiblePaths = [
1214
+ // Current directory (dev)
1215
+ path6.join(dir, "nexus-release.tar.gz"),
1216
+ // dist folder (when built)
1217
+ path6.resolve(dir, "..", "dist", "nexus-release.tar.gz"),
1218
+ // node_modules location (when installed from npm)
1219
+ path6.resolve(dir, "..", "nexus-release.tar.gz")
1220
+ ];
1221
+ for (const tarballPath of possiblePaths) {
1222
+ if (fs6.existsSync(tarballPath)) return tarballPath;
1223
+ }
1224
+ throw new Error("nexus-release.tar.gz not found. Run: npm install buildwithnexus@latest");
1273
1225
  }
1274
1226
  function getCloudInitTemplate() {
1275
- const dir = path7.dirname(fileURLToPath2(import.meta.url));
1276
- const templatePath = path7.join(dir, "templates", "cloud-init.yaml.ejs");
1277
- if (fs7.existsSync(templatePath)) return fs7.readFileSync(templatePath, "utf-8");
1278
- const srcPath = path7.resolve(dir, "..", "src", "templates", "cloud-init.yaml.ejs");
1279
- return fs7.readFileSync(srcPath, "utf-8");
1227
+ const dir = path6.dirname(fileURLToPath2(import.meta.url));
1228
+ const templatePath = path6.join(dir, "templates", "cloud-init.yaml.ejs");
1229
+ if (fs6.existsSync(templatePath)) return fs6.readFileSync(templatePath, "utf-8");
1230
+ const srcPath = path6.resolve(dir, "..", "src", "templates", "cloud-init.yaml.ejs");
1231
+ return fs6.readFileSync(srcPath, "utf-8");
1280
1232
  }
1281
1233
  async function withSpinner(spinner, label, fn) {
1282
1234
  spinner.text = label;
@@ -1294,11 +1246,7 @@ var phases = [
1294
1246
  const platform = detectPlatform();
1295
1247
  log.detail("Platform", `${platform.os} ${platform.arch}`);
1296
1248
  log.detail("QEMU", platform.qemuBinary);
1297
- const isNonInteractive = process.env.BUILDWITHNEXUS_NONINTERACTIVE === "1" || process.env.CI === "true";
1298
- let userConfig = isNonInteractive ? loadInitConfigFromDisk() : null;
1299
- if (!userConfig) {
1300
- userConfig = await promptInitConfig();
1301
- }
1249
+ const userConfig = await promptInitConfig();
1302
1250
  ensureHome();
1303
1251
  const masterSecret = generateMasterSecret();
1304
1252
  const config = {
@@ -1352,6 +1300,11 @@ var phases = [
1352
1300
  const { config } = ctx;
1353
1301
  await withSpinner(spinner, "Generating SSH key...", async () => {
1354
1302
  await generateSshKey();
1303
+ const pinFile = path6.join(path6.dirname(getKeyPath()), "vm_host_key.pin");
1304
+ try {
1305
+ fs6.unlinkSync(pinFile);
1306
+ } catch {
1307
+ }
1355
1308
  addSshConfig(config.sshPort);
1356
1309
  });
1357
1310
  }
@@ -1434,15 +1387,15 @@ var phases = [
1434
1387
  );
1435
1388
  await withSpinner(spinner, "Staging API keys...", async () => {
1436
1389
  const keysContent = Object.entries(keys).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
1437
- const tmpKeysPath = path7.join(os3.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1438
- fs7.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1390
+ const tmpKeysPath = path6.join(os2.tmpdir(), `.nexus-keys-${crypto4.randomBytes(8).toString("hex")}`);
1391
+ fs6.writeFileSync(tmpKeysPath, keysContent, { mode: 384 });
1439
1392
  try {
1440
1393
  await sshUploadFile(config.sshPort, tmpKeysPath, "/tmp/.nexus-env-keys");
1441
1394
  await sshExec(config.sshPort, "chmod 600 /tmp/.nexus-env-keys");
1442
1395
  } finally {
1443
1396
  try {
1444
- fs7.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
1445
- fs7.unlinkSync(tmpKeysPath);
1397
+ fs6.writeFileSync(tmpKeysPath, "0".repeat(keysContent.length));
1398
+ fs6.unlinkSync(tmpKeysPath);
1446
1399
  } catch {
1447
1400
  }
1448
1401
  }
@@ -1563,7 +1516,7 @@ init_secrets();
1563
1516
  init_qemu();
1564
1517
  init_ssh();
1565
1518
  init_secrets();
1566
- import path8 from "path";
1519
+ import path7 from "path";
1567
1520
  var startCommand = new Command2("start").description("Start the NEXUS runtime").action(async () => {
1568
1521
  const config = loadConfig();
1569
1522
  if (!config) {
@@ -1575,8 +1528,8 @@ var startCommand = new Command2("start").description("Start the NEXUS runtime").
1575
1528
  return;
1576
1529
  }
1577
1530
  const platform = detectPlatform();
1578
- const diskPath = path8.join(NEXUS_HOME2, "vm", "images", "nexus-vm-disk.qcow2");
1579
- const isoPath = path8.join(NEXUS_HOME2, "vm", "images", "init.iso");
1531
+ const diskPath = path7.join(NEXUS_HOME2, "vm", "images", "nexus-vm-disk.qcow2");
1532
+ const isoPath = path7.join(NEXUS_HOME2, "vm", "images", "init.iso");
1580
1533
  let spinner = createSpinner("Starting VM...");
1581
1534
  spinner.start();
1582
1535
  await launchVm(platform, diskPath, isoPath, config.vmRam, config.vmCpus, {
@@ -1692,10 +1645,10 @@ var statusCommand = new Command4("status").description("Check NEXUS runtime heal
1692
1645
  // src/commands/doctor.ts
1693
1646
  import { Command as Command5 } from "commander";
1694
1647
  import chalk8 from "chalk";
1695
- import fs8 from "fs";
1648
+ import fs7 from "fs";
1696
1649
  init_qemu();
1697
1650
  init_secrets();
1698
- import path9 from "path";
1651
+ import path8 from "path";
1699
1652
  import { execa as execa4 } from "execa";
1700
1653
  var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime environment").action(async () => {
1701
1654
  const platform = detectPlatform();
@@ -1725,11 +1678,11 @@ var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime e
1725
1678
  } catch {
1726
1679
  }
1727
1680
  console.log(` ${check(isoTool)} ISO tool (mkisofs/genisoimage)`);
1728
- const keyExists = fs8.existsSync(path9.join(NEXUS_HOME2, "ssh", "id_nexus_vm"));
1681
+ const keyExists = fs7.existsSync(path8.join(NEXUS_HOME2, "ssh", "id_nexus_vm"));
1729
1682
  console.log(` ${check(keyExists)} SSH key`);
1730
1683
  const config = loadConfig();
1731
1684
  console.log(` ${check(!!config)} Configuration`);
1732
- const diskExists = fs8.existsSync(path9.join(NEXUS_HOME2, "vm", "images", "nexus-vm-disk.qcow2"));
1685
+ const diskExists = fs7.existsSync(path8.join(NEXUS_HOME2, "vm", "images", "nexus-vm-disk.qcow2"));
1733
1686
  console.log(` ${check(diskExists)} VM disk image`);
1734
1687
  if (config) {
1735
1688
  for (const [name, port] of [["SSH", config.sshPort], ["HTTP", config.httpPort], ["HTTPS", config.httpsPort]]) {
@@ -1750,7 +1703,7 @@ var doctorCommand = new Command5("doctor").description("Diagnose NEXUS runtime e
1750
1703
  }
1751
1704
  }
1752
1705
  }
1753
- const biosOk = fs8.existsSync(platform.biosPath);
1706
+ const biosOk = fs7.existsSync(platform.biosPath);
1754
1707
  console.log(` ${check(biosOk)} UEFI firmware ${biosOk ? "" : chalk8.dim(platform.biosPath)}`);
1755
1708
  console.log("");
1756
1709
  if (qemuOk && isoTool && biosOk) {
@@ -1801,18 +1754,18 @@ var logsCommand = new Command6("logs").description("View NEXUS server logs").opt
1801
1754
 
1802
1755
  // src/commands/update.ts
1803
1756
  import { Command as Command7 } from "commander";
1804
- import path10 from "path";
1805
- import fs9 from "fs";
1757
+ import path9 from "path";
1758
+ import fs8 from "fs";
1806
1759
  import { fileURLToPath as fileURLToPath3 } from "url";
1807
1760
  init_secrets();
1808
1761
  init_qemu();
1809
1762
  init_ssh();
1810
1763
  function getReleaseTarball2() {
1811
- const dir = path10.dirname(fileURLToPath3(import.meta.url));
1812
- const tarballPath = path10.join(dir, "nexus-release.tar.gz");
1813
- if (fs9.existsSync(tarballPath)) return tarballPath;
1814
- const rootPath = path10.resolve(dir, "..", "dist", "nexus-release.tar.gz");
1815
- if (fs9.existsSync(rootPath)) return rootPath;
1764
+ const dir = path9.dirname(fileURLToPath3(import.meta.url));
1765
+ const tarballPath = path9.join(dir, "nexus-release.tar.gz");
1766
+ if (fs8.existsSync(tarballPath)) return tarballPath;
1767
+ const rootPath = path9.resolve(dir, "..", "dist", "nexus-release.tar.gz");
1768
+ if (fs8.existsSync(rootPath)) return rootPath;
1816
1769
  throw new Error("nexus-release.tar.gz not found. Reinstall buildwithnexus to get the latest release.");
1817
1770
  }
1818
1771
  var updateCommand = new Command7("update").description("Update NEXUS to the latest bundled release and restart").action(async () => {
@@ -1863,11 +1816,11 @@ var updateCommand = new Command7("update").description("Update NEXUS to the late
1863
1816
  // src/commands/destroy.ts
1864
1817
  import { Command as Command8 } from "commander";
1865
1818
  import chalk9 from "chalk";
1866
- import fs10 from "fs";
1819
+ import fs9 from "fs";
1867
1820
  import { input as input2 } from "@inquirer/prompts";
1868
1821
  init_secrets();
1869
1822
  init_qemu();
1870
- import path11 from "path";
1823
+ import path10 from "path";
1871
1824
  var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and all data").option("--force", "Skip confirmation").action(async (opts) => {
1872
1825
  const config = loadConfig();
1873
1826
  if (!opts.force) {
@@ -1895,9 +1848,9 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1895
1848
  }
1896
1849
  stopVm();
1897
1850
  }
1898
- const sshConfigPath = path11.join(process.env.HOME || "~", ".ssh", "config");
1899
- if (fs10.existsSync(sshConfigPath)) {
1900
- const content = fs10.readFileSync(sshConfigPath, "utf-8");
1851
+ const sshConfigPath = path10.join(process.env.HOME || "~", ".ssh", "config");
1852
+ if (fs9.existsSync(sshConfigPath)) {
1853
+ const content = fs9.readFileSync(sshConfigPath, "utf-8");
1901
1854
  const lines = content.split("\n");
1902
1855
  const filtered = [];
1903
1856
  let skip = false;
@@ -1910,9 +1863,9 @@ var destroyCommand = new Command8("destroy").description("Remove NEXUS VM and al
1910
1863
  skip = false;
1911
1864
  filtered.push(line);
1912
1865
  }
1913
- fs10.writeFileSync(sshConfigPath, filtered.join("\n"));
1866
+ fs9.writeFileSync(sshConfigPath, filtered.join("\n"));
1914
1867
  }
1915
- fs10.rmSync(NEXUS_HOME2, { recursive: true, force: true });
1868
+ fs9.rmSync(NEXUS_HOME2, { recursive: true, force: true });
1916
1869
  succeed(spinner, "NEXUS runtime destroyed");
1917
1870
  log.dim("Run 'buildwithnexus init' to set up again");
1918
1871
  });
@@ -2325,10 +2278,10 @@ init_dlp();
2325
2278
  // src/ui/repl.ts
2326
2279
  init_secrets();
2327
2280
  import readline from "readline";
2328
- import fs11 from "fs";
2329
- import path12 from "path";
2281
+ import fs10 from "fs";
2282
+ import path11 from "path";
2330
2283
  import chalk13 from "chalk";
2331
- var HISTORY_FILE = path12.join(NEXUS_HOME2, "shell_history");
2284
+ var HISTORY_FILE = path11.join(NEXUS_HOME2, "shell_history");
2332
2285
  var MAX_HISTORY = 1e3;
2333
2286
  var Repl = class {
2334
2287
  rl = null;
@@ -2344,17 +2297,17 @@ var Repl = class {
2344
2297
  }
2345
2298
  loadHistory() {
2346
2299
  try {
2347
- if (fs11.existsSync(HISTORY_FILE)) {
2348
- this.history = fs11.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter(Boolean).slice(-MAX_HISTORY);
2300
+ if (fs10.existsSync(HISTORY_FILE)) {
2301
+ this.history = fs10.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter(Boolean).slice(-MAX_HISTORY);
2349
2302
  }
2350
2303
  } catch {
2351
2304
  }
2352
2305
  }
2353
2306
  saveHistory() {
2354
2307
  try {
2355
- const dir = path12.dirname(HISTORY_FILE);
2356
- fs11.mkdirSync(dir, { recursive: true });
2357
- fs11.writeFileSync(HISTORY_FILE, this.history.slice(-MAX_HISTORY).join("\n") + "\n", { mode: 384 });
2308
+ const dir = path11.dirname(HISTORY_FILE);
2309
+ fs10.mkdirSync(dir, { recursive: true });
2310
+ fs10.writeFileSync(HISTORY_FILE, this.history.slice(-MAX_HISTORY).join("\n") + "\n", { mode: 384 });
2358
2311
  } catch {
2359
2312
  }
2360
2313
  }
@@ -2969,9 +2922,7 @@ var shellCommand2 = new Command13("shell").description("Launch the interactive N
2969
2922
  });
2970
2923
 
2971
2924
  // src/cli.ts
2972
- var __dirname = dirname2(fileURLToPath4(import.meta.url));
2973
- var packageJson = JSON.parse(readFileSync2(join2(__dirname, "..", "package.json"), "utf-8"));
2974
- var cli = new Command14().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version(packageJson.version);
2925
+ var cli = new Command14().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.3.1");
2975
2926
  cli.addCommand(initCommand);
2976
2927
  cli.addCommand(startCommand);
2977
2928
  cli.addCommand(stopCommand);
@@ -3000,18 +2951,18 @@ cli.action(async () => {
3000
2951
  });
3001
2952
 
3002
2953
  // src/core/update-notifier.ts
3003
- import fs12 from "fs";
3004
- import path13 from "path";
3005
- import os4 from "os";
2954
+ import fs11 from "fs";
2955
+ import path12 from "path";
2956
+ import os3 from "os";
3006
2957
  import https from "https";
3007
2958
  import chalk16 from "chalk";
3008
2959
  var PACKAGE_NAME = "buildwithnexus";
3009
2960
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
3010
- var STATE_DIR = path13.join(os4.homedir(), ".buildwithnexus");
3011
- var STATE_FILE = path13.join(STATE_DIR, ".update-check.json");
2961
+ var STATE_DIR = path12.join(os3.homedir(), ".buildwithnexus");
2962
+ var STATE_FILE = path12.join(STATE_DIR, ".update-check.json");
3012
2963
  function readState() {
3013
2964
  try {
3014
- const raw = fs12.readFileSync(STATE_FILE, "utf-8");
2965
+ const raw = fs11.readFileSync(STATE_FILE, "utf-8");
3015
2966
  return JSON.parse(raw);
3016
2967
  } catch {
3017
2968
  return { lastCheck: 0, latestVersion: null };
@@ -3019,8 +2970,8 @@ function readState() {
3019
2970
  }
3020
2971
  function writeState(state) {
3021
2972
  try {
3022
- fs12.mkdirSync(STATE_DIR, { recursive: true, mode: 448 });
3023
- fs12.writeFileSync(STATE_FILE, JSON.stringify(state), { mode: 384 });
2973
+ fs11.mkdirSync(STATE_DIR, { recursive: true, mode: 448 });
2974
+ fs11.writeFileSync(STATE_FILE, JSON.stringify(state), { mode: 384 });
3024
2975
  } catch {
3025
2976
  }
3026
2977
  }
Binary file
@@ -10,7 +10,7 @@ users:
10
10
  runcmd:
11
11
  # Wait for network + install packages (QEMU SLIRP DNS is slow at boot)
12
12
  - |
13
- PACKAGES="openssh-server docker.io docker-compose software-properties-common git curl wget jq tmux auditd ufw libvirt-daemon libvirt-daemon-system libvirt-clients"
13
+ PACKAGES="docker.io docker-compose software-properties-common git curl wget jq tmux auditd ufw libvirt-daemon libvirt-daemon-system libvirt-clients"
14
14
  for attempt in $(seq 1 10); do
15
15
  apt-get update && \
16
16
  DEBIAN_FRONTEND=noninteractive apt-get install -y --fix-missing $PACKAGES && \
@@ -36,13 +36,10 @@ runcmd:
36
36
  - systemctl enable auditd
37
37
  - systemctl start auditd
38
38
 
39
- # SSH hardening + ensure service is running
39
+ # SSH hardening
40
40
  - sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
41
41
  - sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
42
- - systemctl enable ssh
43
42
  - systemctl restart ssh
44
- - sleep 2
45
- - systemctl is-active ssh || systemctl start ssh
46
43
 
47
44
  # Docker setup
48
45
  - usermod -aG docker nexus
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildwithnexus",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
4
4
  "description": "Launch an autonomous AI runtime with triple-nested VM isolation in one command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "test": "vitest run",
20
20
  "test:watch": "vitest",
21
21
  "test:coverage": "vitest run --coverage",
22
- "prepublishOnly": "npm run build && npm run bundle"
22
+ "prepublishOnly": "npm run build && NEXUS_SRC=/Users/garretteaglin/Projects/nexus npm run bundle"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=18"