buildwithnexus 0.3.8 → 0.4.1
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 +53 -58
- package/dist/nexus-release.tar.gz +0 -0
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -438,59 +438,59 @@ async function getPortBlocker(port) {
|
|
|
438
438
|
return null;
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
|
+
async function findFreePort(preferred, max = 20) {
|
|
442
|
+
for (let offset = 0; offset < max; offset++) {
|
|
443
|
+
if (await isPortFree(preferred + offset)) return preferred + offset;
|
|
444
|
+
}
|
|
445
|
+
throw new Error(`No free port found near ${preferred}`);
|
|
446
|
+
}
|
|
441
447
|
async function resolvePortConflicts(ports) {
|
|
442
448
|
const labels = { ssh: "SSH", http: "HTTP", https: "HTTPS" };
|
|
443
449
|
const resolved = { ...ports };
|
|
444
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
445
450
|
for (const [key, port] of Object.entries(ports)) {
|
|
446
|
-
if (await isPortFree(port)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
451
|
+
if (await isPortFree(port)) continue;
|
|
452
|
+
const blocker = await getPortBlocker(port);
|
|
453
|
+
const desc = blocker ? `${blocker.process} (PID ${blocker.pid})` : "unknown process";
|
|
454
|
+
const altPort = await findFreePort(port + 1).catch(() => null);
|
|
455
|
+
const choices = [];
|
|
456
|
+
if (blocker) {
|
|
457
|
+
choices.push({
|
|
458
|
+
name: `Kill ${desc} and use port ${port}`,
|
|
459
|
+
value: "kill"
|
|
460
|
+
});
|
|
457
461
|
}
|
|
458
462
|
if (altPort) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const desc = blocker ? `${blocker.process} (PID ${blocker.pid})` : "another process";
|
|
463
|
-
console.log(chalk5.yellow(` \u26A0 Port ${port} (${labels[key]}) in use by ${desc} \u2192 using ${altPort}`));
|
|
464
|
-
} else {
|
|
465
|
-
const blocker = await getPortBlocker(port);
|
|
466
|
-
const desc = blocker ? `${blocker.process} (PID ${blocker.pid})` : "unknown process";
|
|
467
|
-
console.log("");
|
|
468
|
-
console.log(chalk5.red(` \u2717 Port ${port} (${labels[key]}) is in use by ${desc} and no free port found nearby`));
|
|
469
|
-
const action = await select({
|
|
470
|
-
message: `No free ${labels[key]} port found. Kill ${desc} to free port ${port}?`,
|
|
471
|
-
choices: [
|
|
472
|
-
...blocker ? [{ name: `Kill ${desc} and use port ${port}`, value: "kill" }] : [],
|
|
473
|
-
{ name: "Abort init", value: "abort" }
|
|
474
|
-
]
|
|
463
|
+
choices.push({
|
|
464
|
+
name: `Use alternate port ${altPort} instead`,
|
|
465
|
+
value: "alt"
|
|
475
466
|
});
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
467
|
+
}
|
|
468
|
+
choices.push({ name: "Abort init", value: "abort" });
|
|
469
|
+
console.log("");
|
|
470
|
+
console.log(chalk5.yellow(` \u26A0 Port ${port} (${labels[key]}) is in use by ${desc}`));
|
|
471
|
+
const action = await select({
|
|
472
|
+
message: `How would you like to resolve the ${labels[key]} port conflict?`,
|
|
473
|
+
choices
|
|
474
|
+
});
|
|
475
|
+
if (action === "kill" && blocker) {
|
|
476
|
+
try {
|
|
477
|
+
process.kill(blocker.pid, "SIGTERM");
|
|
478
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
479
|
+
if (!await isPortFree(port)) {
|
|
480
|
+
process.kill(blocker.pid, "SIGKILL");
|
|
481
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
489
482
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
483
|
+
console.log(chalk5.green(` \u2713 Killed ${desc}, port ${port} is now free`));
|
|
484
|
+
} catch {
|
|
485
|
+
console.log(chalk5.red(` \u2717 Failed to kill PID ${blocker.pid}. Try: sudo kill ${blocker.pid}`));
|
|
486
|
+
process.exit(1);
|
|
493
487
|
}
|
|
488
|
+
} else if (action === "alt" && altPort) {
|
|
489
|
+
resolved[key] = altPort;
|
|
490
|
+
console.log(chalk5.green(` \u2713 Using port ${altPort} for ${labels[key]}`));
|
|
491
|
+
} else {
|
|
492
|
+
console.log(chalk5.dim(" Init aborted."));
|
|
493
|
+
process.exit(0);
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
return resolved;
|
|
@@ -671,7 +671,7 @@ function addSshConfig(port) {
|
|
|
671
671
|
fs4.writeFileSync(sshConfigPath, block, { mode: 384 });
|
|
672
672
|
}
|
|
673
673
|
}
|
|
674
|
-
async function waitForSsh(port, timeoutMs =
|
|
674
|
+
async function waitForSsh(port, timeoutMs = 3e5) {
|
|
675
675
|
const start = Date.now();
|
|
676
676
|
while (Date.now() - start < timeoutMs) {
|
|
677
677
|
try {
|
|
@@ -737,7 +737,6 @@ var init_ssh = __esm({
|
|
|
737
737
|
|
|
738
738
|
// src/cli.ts
|
|
739
739
|
import { Command as Command14 } from "commander";
|
|
740
|
-
import { createRequire } from "module";
|
|
741
740
|
|
|
742
741
|
// src/commands/init.ts
|
|
743
742
|
init_banner();
|
|
@@ -939,13 +938,13 @@ async function renderCloudInit(data, templateContent) {
|
|
|
939
938
|
}
|
|
940
939
|
const safeData = { ...data, sshPubKey: yamlEscape(trimmedPubKey), keys: safeKeys };
|
|
941
940
|
const rendered = ejs.render(templateContent, safeData);
|
|
942
|
-
const outputPath = path5.join(CONFIGS_DIR, "user-data");
|
|
941
|
+
const outputPath = path5.join(CONFIGS_DIR, "user-data.yaml");
|
|
943
942
|
fs5.writeFileSync(outputPath, rendered, { mode: 384 });
|
|
944
|
-
audit("cloudinit_rendered", "user-data written");
|
|
943
|
+
audit("cloudinit_rendered", "user-data.yaml written");
|
|
945
944
|
return outputPath;
|
|
946
945
|
}
|
|
947
946
|
async function createCloudInitIso(userDataPath) {
|
|
948
|
-
const metaDataPath = path5.join(CONFIGS_DIR, "meta-data");
|
|
947
|
+
const metaDataPath = path5.join(CONFIGS_DIR, "meta-data.yaml");
|
|
949
948
|
fs5.writeFileSync(metaDataPath, "instance-id: nexus-vm-1\nlocal-hostname: nexus-vm\n", { mode: 384 });
|
|
950
949
|
const isoPath = path5.join(IMAGES_DIR2, "init.iso");
|
|
951
950
|
const env = scrubEnv();
|
|
@@ -1006,7 +1005,7 @@ async function createCloudInitIso(userDataPath) {
|
|
|
1006
1005
|
fs5.unlinkSync(metaDataPath);
|
|
1007
1006
|
} catch {
|
|
1008
1007
|
}
|
|
1009
|
-
audit("cloudinit_plaintext_deleted", "user-data and meta-data removed");
|
|
1008
|
+
audit("cloudinit_plaintext_deleted", "user-data.yaml and meta-data.yaml removed");
|
|
1010
1009
|
}
|
|
1011
1010
|
}
|
|
1012
1011
|
|
|
@@ -1046,7 +1045,7 @@ async function checkHealth(port, vmRunning) {
|
|
|
1046
1045
|
}
|
|
1047
1046
|
return status;
|
|
1048
1047
|
}
|
|
1049
|
-
async function waitForServer(port, timeoutMs =
|
|
1048
|
+
async function waitForServer(port, timeoutMs = 9e5) {
|
|
1050
1049
|
const start = Date.now();
|
|
1051
1050
|
let lastLog = 0;
|
|
1052
1051
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -1070,7 +1069,7 @@ async function waitForServer(port, timeoutMs = 36e5) {
|
|
|
1070
1069
|
}
|
|
1071
1070
|
return false;
|
|
1072
1071
|
}
|
|
1073
|
-
async function waitForCloudInit(port, timeoutMs =
|
|
1072
|
+
async function waitForCloudInit(port, timeoutMs = 18e5) {
|
|
1074
1073
|
const start = Date.now();
|
|
1075
1074
|
let lastLog = 0;
|
|
1076
1075
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -1306,8 +1305,6 @@ var phases = [
|
|
|
1306
1305
|
name: "VM Provisioning",
|
|
1307
1306
|
run: async (ctx, spinner) => {
|
|
1308
1307
|
const { config, keys, tarballPath } = ctx;
|
|
1309
|
-
const pinPath = path6.join(process.env.HOME || "~", ".buildwithnexus", "ssh", "vm_host_key.pin");
|
|
1310
|
-
if (fs6.existsSync(pinPath)) fs6.unlinkSync(pinPath);
|
|
1311
1308
|
spinner.text = "Waiting for SSH...";
|
|
1312
1309
|
spinner.start();
|
|
1313
1310
|
const sshReady = await waitForSsh(config.sshPort);
|
|
@@ -1340,7 +1337,7 @@ var phases = [
|
|
|
1340
1337
|
spinner.start();
|
|
1341
1338
|
const cloudInitDone = await waitForCloudInit(config.sshPort);
|
|
1342
1339
|
if (!cloudInitDone) {
|
|
1343
|
-
fail(spinner, "Cloud-init timed out after
|
|
1340
|
+
fail(spinner, "Cloud-init timed out after 30 minutes");
|
|
1344
1341
|
log.warn("Check progress: buildwithnexus ssh \u2192 tail -f /var/log/cloud-init-output.log");
|
|
1345
1342
|
throw new Error("Cloud-init timed out");
|
|
1346
1343
|
}
|
|
@@ -2835,9 +2832,7 @@ var shellCommand2 = new Command13("shell").description("Launch the interactive N
|
|
|
2835
2832
|
});
|
|
2836
2833
|
|
|
2837
2834
|
// src/cli.ts
|
|
2838
|
-
var
|
|
2839
|
-
var { version } = require2("../package.json");
|
|
2840
|
-
var cli = new Command14().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version(version);
|
|
2835
|
+
var cli = new Command14().name("buildwithnexus").description("Auto-scaffold and launch a fully autonomous NEXUS runtime").version("0.3.1");
|
|
2841
2836
|
cli.addCommand(initCommand);
|
|
2842
2837
|
cli.addCommand(startCommand);
|
|
2843
2838
|
cli.addCommand(stopCommand);
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buildwithnexus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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"
|