buildwithnexus 0.7.2 → 0.7.4
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 +342 -233
- package/dist/deep-agents-bin.js +15 -9
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -428,6 +428,112 @@ var init_platform = __esm({
|
|
|
428
428
|
}
|
|
429
429
|
});
|
|
430
430
|
|
|
431
|
+
// src/core/utils.ts
|
|
432
|
+
function backoffMs(attempt) {
|
|
433
|
+
return Math.min(3e3 * Math.pow(2, attempt), 3e4);
|
|
434
|
+
}
|
|
435
|
+
var init_utils = __esm({
|
|
436
|
+
"src/core/utils.ts"() {
|
|
437
|
+
"use strict";
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// src/core/health.ts
|
|
442
|
+
var health_exports = {};
|
|
443
|
+
__export(health_exports, {
|
|
444
|
+
checkHealth: () => checkHealth,
|
|
445
|
+
waitForServer: () => waitForServer
|
|
446
|
+
});
|
|
447
|
+
async function checkHealth(vmRunning) {
|
|
448
|
+
const status = {
|
|
449
|
+
vmRunning,
|
|
450
|
+
sshReady: false,
|
|
451
|
+
dockerReady: false,
|
|
452
|
+
serverHealthy: false,
|
|
453
|
+
tunnelUrl: null,
|
|
454
|
+
dockerVersion: null,
|
|
455
|
+
serverVersion: null,
|
|
456
|
+
diskUsagePercent: null,
|
|
457
|
+
uptimeSeconds: null,
|
|
458
|
+
lastChecked: (/* @__PURE__ */ new Date()).toISOString()
|
|
459
|
+
};
|
|
460
|
+
if (!vmRunning) return status;
|
|
461
|
+
status.sshReady = true;
|
|
462
|
+
try {
|
|
463
|
+
const { stdout, code } = await dockerExec("docker version --format '{{.Server.Version}}'");
|
|
464
|
+
status.dockerReady = code === 0 && stdout.trim().length > 0;
|
|
465
|
+
if (status.dockerReady) status.dockerVersion = stdout.trim();
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const { stdout, code } = await dockerExec("curl -sf http://localhost:4200/health");
|
|
470
|
+
status.serverHealthy = code === 0 && stdout.includes("ok");
|
|
471
|
+
if (status.serverHealthy) {
|
|
472
|
+
try {
|
|
473
|
+
const parsed = JSON.parse(stdout);
|
|
474
|
+
if (typeof parsed.version === "string") status.serverVersion = parsed.version;
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const { stdout } = await dockerExec("df / --output=pcent | tail -1 | tr -dc '0-9'");
|
|
482
|
+
const pct = parseInt(stdout.trim(), 10);
|
|
483
|
+
if (!isNaN(pct)) status.diskUsagePercent = pct;
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
try {
|
|
487
|
+
const { stdout } = await dockerExec("awk '{print int($1)}' /proc/uptime 2>/dev/null");
|
|
488
|
+
const up = parseInt(stdout.trim(), 10);
|
|
489
|
+
if (!isNaN(up)) status.uptimeSeconds = up;
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
492
|
+
try {
|
|
493
|
+
const { stdout } = await dockerExec("cat /home/nexus/.nexus/tunnel-url.txt 2>/dev/null");
|
|
494
|
+
if (stdout.includes("https://")) {
|
|
495
|
+
status.tunnelUrl = stdout.trim();
|
|
496
|
+
}
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
499
|
+
return status;
|
|
500
|
+
}
|
|
501
|
+
async function waitForServer(timeoutMs = 9e5) {
|
|
502
|
+
const start = Date.now();
|
|
503
|
+
let lastLog = 0;
|
|
504
|
+
let attempt = 0;
|
|
505
|
+
while (Date.now() - start < timeoutMs) {
|
|
506
|
+
try {
|
|
507
|
+
const { stdout, code } = await dockerExec("curl -sf http://localhost:4200/health");
|
|
508
|
+
if (code === 0 && stdout.includes("ok")) return true;
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
const elapsed = Date.now() - start;
|
|
512
|
+
if (elapsed - lastLog >= 3e4) {
|
|
513
|
+
lastLog = elapsed;
|
|
514
|
+
try {
|
|
515
|
+
const { stdout } = await dockerExec("systemctl is-active nexus 2>/dev/null || echo 'starting...'");
|
|
516
|
+
process.stderr.write(`
|
|
517
|
+
[server ${Math.round(elapsed / 1e3)}s] ${stdout.trim().slice(0, 120)}
|
|
518
|
+
`);
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const delay = backoffMs(attempt++);
|
|
523
|
+
const remaining = timeoutMs - (Date.now() - start);
|
|
524
|
+
if (remaining <= 0) break;
|
|
525
|
+
await new Promise((r) => setTimeout(r, Math.min(delay, remaining)));
|
|
526
|
+
}
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
var init_health = __esm({
|
|
530
|
+
"src/core/health.ts"() {
|
|
531
|
+
"use strict";
|
|
532
|
+
init_docker();
|
|
533
|
+
init_utils();
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
431
537
|
// src/core/docker.ts
|
|
432
538
|
var docker_exports = {};
|
|
433
539
|
__export(docker_exports, {
|
|
@@ -436,6 +542,7 @@ __export(docker_exports, {
|
|
|
436
542
|
installDocker: () => installDocker,
|
|
437
543
|
isDockerInstalled: () => isDockerInstalled,
|
|
438
544
|
isNexusRunning: () => isNexusRunning,
|
|
545
|
+
launchNexus: () => launchNexus,
|
|
439
546
|
pullImage: () => pullImage,
|
|
440
547
|
startBackend: () => startBackend,
|
|
441
548
|
startNexus: () => startNexus,
|
|
@@ -811,7 +918,7 @@ async function startBackend() {
|
|
|
811
918
|
const path11 = await import("path");
|
|
812
919
|
const nexusDir = path11.join(os5.homedir(), "Projects", "nexus");
|
|
813
920
|
log.step(`Starting Nexus backend from ${nexusDir}...`);
|
|
814
|
-
const child = spawn2("
|
|
921
|
+
const child = spawn2("python3", ["-m", "src.deep_agents_server"], {
|
|
815
922
|
cwd: nexusDir,
|
|
816
923
|
detached: true,
|
|
817
924
|
stdio: "ignore",
|
|
@@ -820,6 +927,16 @@ async function startBackend() {
|
|
|
820
927
|
child.unref();
|
|
821
928
|
log.success("Nexus backend process started");
|
|
822
929
|
}
|
|
930
|
+
async function launchNexus(keys, config, opts) {
|
|
931
|
+
const { healthTimeoutMs = 6e4, stopExisting = true } = opts ?? {};
|
|
932
|
+
if (stopExisting && await isNexusRunning()) {
|
|
933
|
+
await stopNexus();
|
|
934
|
+
}
|
|
935
|
+
await startNexus(keys, config);
|
|
936
|
+
if (healthTimeoutMs <= 0) return true;
|
|
937
|
+
const { waitForServer: waitForServer2 } = await Promise.resolve().then(() => (init_health(), health_exports));
|
|
938
|
+
return waitForServer2(healthTimeoutMs);
|
|
939
|
+
}
|
|
823
940
|
async function isNexusRunning() {
|
|
824
941
|
try {
|
|
825
942
|
const { stdout } = await execa("docker", [
|
|
@@ -1228,6 +1345,77 @@ function validateBackendUrl(url) {
|
|
|
1228
1345
|
};
|
|
1229
1346
|
}
|
|
1230
1347
|
|
|
1348
|
+
// src/core/api.ts
|
|
1349
|
+
function buildRunPayload(task, agentRole, agentGoal, keys) {
|
|
1350
|
+
const k = keys ?? loadApiKeys();
|
|
1351
|
+
return {
|
|
1352
|
+
task,
|
|
1353
|
+
agent_role: agentRole,
|
|
1354
|
+
agent_goal: agentGoal,
|
|
1355
|
+
api_key: k.anthropic || "",
|
|
1356
|
+
openai_api_key: k.openai || "",
|
|
1357
|
+
google_api_key: k.google || ""
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
async function httpPost(httpPort, path11, body, timeoutMs = 6e4) {
|
|
1361
|
+
const res = await fetch(`http://localhost:${httpPort}${path11}`, {
|
|
1362
|
+
method: "POST",
|
|
1363
|
+
headers: { "Content-Type": "application/json" },
|
|
1364
|
+
body: JSON.stringify(body),
|
|
1365
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1366
|
+
});
|
|
1367
|
+
if (!res.ok) throw new Error(`Server returned ${res.status}`);
|
|
1368
|
+
const text = await res.text();
|
|
1369
|
+
try {
|
|
1370
|
+
const parsed = JSON.parse(text);
|
|
1371
|
+
return parsed.response ?? parsed.message ?? text;
|
|
1372
|
+
} catch {
|
|
1373
|
+
return text;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
async function httpGet(httpPort, path11, timeoutMs = 1e4) {
|
|
1377
|
+
try {
|
|
1378
|
+
const res = await fetch(`http://localhost:${httpPort}${path11}`, {
|
|
1379
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1380
|
+
});
|
|
1381
|
+
const text = await res.text();
|
|
1382
|
+
return { ok: res.ok, text };
|
|
1383
|
+
} catch {
|
|
1384
|
+
return { ok: false, text: "" };
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async function checkServerHealth(target, timeoutMs = 1e4) {
|
|
1388
|
+
const url = typeof target === "number" ? `http://localhost:${target}/health` : `${target}/health`;
|
|
1389
|
+
try {
|
|
1390
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
1391
|
+
return res.ok;
|
|
1392
|
+
} catch {
|
|
1393
|
+
return false;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// src/core/sse-parser.ts
|
|
1398
|
+
async function* parseSSEStream(reader) {
|
|
1399
|
+
const decoder = new TextDecoder();
|
|
1400
|
+
let buffer = "";
|
|
1401
|
+
while (true) {
|
|
1402
|
+
const { done, value } = await reader.read();
|
|
1403
|
+
if (done) break;
|
|
1404
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1405
|
+
const lines = buffer.split("\n");
|
|
1406
|
+
buffer = lines.pop() || "";
|
|
1407
|
+
for (const line of lines) {
|
|
1408
|
+
if (!line.startsWith("data: ")) continue;
|
|
1409
|
+
try {
|
|
1410
|
+
const parsed = JSON.parse(line.slice(6));
|
|
1411
|
+
yield parsed;
|
|
1412
|
+
} catch (e) {
|
|
1413
|
+
if (process.env.LOG_LEVEL === "debug") console.error("SSE parse error:", e);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1231
1419
|
// src/cli/run-command.ts
|
|
1232
1420
|
async function runCommand(task, options) {
|
|
1233
1421
|
const backendUrl = process.env.BACKEND_URL || "http://localhost:4200";
|
|
@@ -1240,30 +1428,17 @@ ${urlCheck.error}`);
|
|
|
1240
1428
|
tui.displayHeader(task, options.agent);
|
|
1241
1429
|
tui.displayConnecting();
|
|
1242
1430
|
try {
|
|
1243
|
-
|
|
1244
|
-
try {
|
|
1245
|
-
const healthResponse = await fetch(`${backendUrl}/health`);
|
|
1246
|
-
healthOk = healthResponse.ok;
|
|
1247
|
-
} catch {
|
|
1248
|
-
}
|
|
1249
|
-
if (!healthOk) {
|
|
1431
|
+
if (!await checkServerHealth(backendUrl)) {
|
|
1250
1432
|
console.error(
|
|
1251
1433
|
"Backend not responding. Start it with:\n buildwithnexus server"
|
|
1252
1434
|
);
|
|
1253
1435
|
process.exit(1);
|
|
1254
1436
|
}
|
|
1255
|
-
const
|
|
1437
|
+
const payload = buildRunPayload(task, options.agent, options.goal || "");
|
|
1256
1438
|
const response = await fetch(`${backendUrl}/api/run`, {
|
|
1257
1439
|
method: "POST",
|
|
1258
1440
|
headers: { "Content-Type": "application/json" },
|
|
1259
|
-
body: JSON.stringify(
|
|
1260
|
-
task,
|
|
1261
|
-
agent_role: options.agent,
|
|
1262
|
-
agent_goal: options.goal || "",
|
|
1263
|
-
api_key: keys.anthropic || "",
|
|
1264
|
-
openai_api_key: keys.openai || "",
|
|
1265
|
-
google_api_key: keys.google || ""
|
|
1266
|
-
})
|
|
1441
|
+
body: JSON.stringify(payload)
|
|
1267
1442
|
});
|
|
1268
1443
|
if (!response.ok) {
|
|
1269
1444
|
console.error("Backend error");
|
|
@@ -1415,32 +1590,13 @@ function classifyIntent(task) {
|
|
|
1415
1590
|
|
|
1416
1591
|
// src/cli/interactive.ts
|
|
1417
1592
|
init_secrets();
|
|
1593
|
+
init_docker();
|
|
1418
1594
|
|
|
1419
|
-
// src/core/
|
|
1420
|
-
|
|
1421
|
-
const decoder = new TextDecoder();
|
|
1422
|
-
let buffer = "";
|
|
1423
|
-
while (true) {
|
|
1424
|
-
const { done, value } = await reader.read();
|
|
1425
|
-
if (done) break;
|
|
1426
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1427
|
-
const lines = buffer.split("\n");
|
|
1428
|
-
buffer = lines.pop() || "";
|
|
1429
|
-
for (const line of lines) {
|
|
1430
|
-
if (!line.startsWith("data: ")) continue;
|
|
1431
|
-
try {
|
|
1432
|
-
const parsed = JSON.parse(line.slice(6));
|
|
1433
|
-
yield parsed;
|
|
1434
|
-
} catch (e) {
|
|
1435
|
-
if (process.env.LOG_LEVEL === "debug") console.error("SSE parse error:", e);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1595
|
+
// src/core/version.ts
|
|
1596
|
+
var resolvedVersion = true ? "0.7.4" : pkg.version;
|
|
1440
1597
|
|
|
1441
1598
|
// src/cli/interactive.ts
|
|
1442
|
-
|
|
1443
|
-
var appVersion = true ? "0.7.2" : pkg.version;
|
|
1599
|
+
var appVersion = resolvedVersion;
|
|
1444
1600
|
async function interactiveMode() {
|
|
1445
1601
|
const backendUrl = process.env.BACKEND_URL || "http://localhost:4200";
|
|
1446
1602
|
const urlCheck = validateBackendUrl(backendUrl);
|
|
@@ -1479,26 +1635,11 @@ ${urlCheck.error}`));
|
|
|
1479
1635
|
async function waitForBackend() {
|
|
1480
1636
|
for (let i = 0; i < 5; i++) {
|
|
1481
1637
|
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
1482
|
-
|
|
1483
|
-
const retryResponse = await fetch(`${backendUrl}/health`, { signal: AbortSignal.timeout(1e4) });
|
|
1484
|
-
if (retryResponse.ok) return true;
|
|
1485
|
-
} catch {
|
|
1486
|
-
}
|
|
1638
|
+
if (await checkServerHealth(backendUrl)) return true;
|
|
1487
1639
|
}
|
|
1488
1640
|
return false;
|
|
1489
1641
|
}
|
|
1490
|
-
|
|
1491
|
-
const response = await fetch(`${backendUrl}/health`, { signal: AbortSignal.timeout(1e4) });
|
|
1492
|
-
if (!response.ok) {
|
|
1493
|
-
console.log(chalk3.yellow("\u26A0\uFE0F Backend not responding, starting..."));
|
|
1494
|
-
await startBackend();
|
|
1495
|
-
const ready = await waitForBackend();
|
|
1496
|
-
if (!ready) {
|
|
1497
|
-
console.error(chalk3.red("\u274C Backend failed to start. Run: buildwithnexus server"));
|
|
1498
|
-
process.exit(1);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
} catch {
|
|
1642
|
+
if (!await checkServerHealth(backendUrl)) {
|
|
1502
1643
|
console.log(chalk3.yellow("\u26A0\uFE0F Backend not accessible, attempting to start..."));
|
|
1503
1644
|
await startBackend();
|
|
1504
1645
|
const ready = await waitForBackend();
|
|
@@ -1619,25 +1760,45 @@ async function planModeLoop(task, backendUrl, ask) {
|
|
|
1619
1760
|
console.log("");
|
|
1620
1761
|
console.log(chalk3.yellow("\u23F3 Fetching plan from backend..."));
|
|
1621
1762
|
let steps = [];
|
|
1622
|
-
const keys = loadApiKeys();
|
|
1623
1763
|
try {
|
|
1624
1764
|
const response = await fetch(`${backendUrl}/api/run`, {
|
|
1625
1765
|
method: "POST",
|
|
1626
1766
|
headers: { "Content-Type": "application/json" },
|
|
1627
|
-
body: JSON.stringify(
|
|
1767
|
+
body: JSON.stringify(buildRunPayload(task, "engineer", "")),
|
|
1628
1768
|
signal: AbortSignal.timeout(12e4)
|
|
1629
1769
|
});
|
|
1630
1770
|
if (!response.ok) {
|
|
1631
1771
|
console.error(chalk3.red("Backend error \u2014 cannot fetch plan."));
|
|
1632
1772
|
return "cancel";
|
|
1633
1773
|
}
|
|
1634
|
-
const
|
|
1774
|
+
const planText = await response.text();
|
|
1775
|
+
let planParsed;
|
|
1776
|
+
try {
|
|
1777
|
+
planParsed = JSON.parse(planText);
|
|
1778
|
+
} catch {
|
|
1779
|
+
console.error(chalk3.red(`Backend returned invalid JSON: ${planText.slice(0, 200)}`));
|
|
1780
|
+
return "cancel";
|
|
1781
|
+
}
|
|
1782
|
+
const { run_id: planRunId } = planParsed;
|
|
1783
|
+
if (!planRunId || typeof planRunId !== "string") {
|
|
1784
|
+
console.error(chalk3.red("Backend did not return a valid run ID"));
|
|
1785
|
+
return "cancel";
|
|
1786
|
+
}
|
|
1787
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(planRunId)) {
|
|
1788
|
+
console.error(chalk3.red("Backend returned run ID with invalid characters"));
|
|
1789
|
+
return "cancel";
|
|
1790
|
+
}
|
|
1791
|
+
const run_id = planRunId;
|
|
1635
1792
|
tui.displayConnected(run_id);
|
|
1636
1793
|
const streamResponse = await fetch(`${backendUrl}/api/stream/${run_id}`, { signal: AbortSignal.timeout(12e4) });
|
|
1794
|
+
if (!streamResponse.ok) {
|
|
1795
|
+
console.error(chalk3.red(`Stream endpoint returned ${streamResponse.status}`));
|
|
1796
|
+
return "cancel";
|
|
1797
|
+
}
|
|
1637
1798
|
const reader = streamResponse.body?.getReader();
|
|
1638
1799
|
if (!reader) throw new Error("No response body");
|
|
1639
1800
|
let planReceived = false;
|
|
1640
|
-
for await (const parsed of
|
|
1801
|
+
for await (const parsed of parseSSEStream(reader)) {
|
|
1641
1802
|
if (parsed.type === "plan") {
|
|
1642
1803
|
steps = parsed.data["steps"] || [];
|
|
1643
1804
|
planReceived = true;
|
|
@@ -1710,26 +1871,46 @@ async function editPlanSteps(steps, ask) {
|
|
|
1710
1871
|
async function buildModeLoop(task, backendUrl, ask) {
|
|
1711
1872
|
console.log(chalk3.bold("Task:"), chalk3.white(task));
|
|
1712
1873
|
tui.displayConnecting();
|
|
1713
|
-
const keys = loadApiKeys();
|
|
1714
1874
|
try {
|
|
1715
1875
|
const response = await fetch(`${backendUrl}/api/run`, {
|
|
1716
1876
|
method: "POST",
|
|
1717
1877
|
headers: { "Content-Type": "application/json" },
|
|
1718
|
-
body: JSON.stringify(
|
|
1878
|
+
body: JSON.stringify(buildRunPayload(task, "engineer", "")),
|
|
1719
1879
|
signal: AbortSignal.timeout(12e4)
|
|
1720
1880
|
});
|
|
1721
1881
|
if (!response.ok) {
|
|
1722
1882
|
console.error(chalk3.red("Backend error"));
|
|
1723
1883
|
return "done";
|
|
1724
1884
|
}
|
|
1725
|
-
const
|
|
1885
|
+
const buildText = await response.text();
|
|
1886
|
+
let buildParsed;
|
|
1887
|
+
try {
|
|
1888
|
+
buildParsed = JSON.parse(buildText);
|
|
1889
|
+
} catch {
|
|
1890
|
+
console.error(chalk3.red(`Backend returned invalid JSON: ${buildText.slice(0, 200)}`));
|
|
1891
|
+
return "done";
|
|
1892
|
+
}
|
|
1893
|
+
const { run_id: buildRunId } = buildParsed;
|
|
1894
|
+
if (!buildRunId || typeof buildRunId !== "string") {
|
|
1895
|
+
console.error(chalk3.red("Backend did not return a valid run ID"));
|
|
1896
|
+
return "done";
|
|
1897
|
+
}
|
|
1898
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(buildRunId)) {
|
|
1899
|
+
console.error(chalk3.red("Backend returned run ID with invalid characters"));
|
|
1900
|
+
return "done";
|
|
1901
|
+
}
|
|
1902
|
+
const run_id = buildRunId;
|
|
1726
1903
|
tui.displayConnected(run_id);
|
|
1727
1904
|
console.log(chalk3.bold.green("\u2699\uFE0F Executing..."));
|
|
1728
1905
|
tui.displayStreamStart();
|
|
1729
1906
|
const streamResponse = await fetch(`${backendUrl}/api/stream/${run_id}`, { signal: AbortSignal.timeout(12e4) });
|
|
1907
|
+
if (!streamResponse.ok) {
|
|
1908
|
+
console.error(chalk3.red(`Stream endpoint returned ${streamResponse.status}`));
|
|
1909
|
+
return "done";
|
|
1910
|
+
}
|
|
1730
1911
|
const reader = streamResponse.body?.getReader();
|
|
1731
1912
|
if (!reader) throw new Error("No response body");
|
|
1732
|
-
for await (const parsed of
|
|
1913
|
+
for await (const parsed of parseSSEStream(reader)) {
|
|
1733
1914
|
const type = parsed.type;
|
|
1734
1915
|
if (type === "execution_complete") {
|
|
1735
1916
|
const summary = parsed.data["summary"] || "";
|
|
@@ -1768,58 +1949,77 @@ async function brainstormModeLoop(task, backendUrl, ask) {
|
|
|
1768
1949
|
while (true) {
|
|
1769
1950
|
console.log(chalk3.bold.blue("\u{1F4A1} Thinking..."));
|
|
1770
1951
|
try {
|
|
1771
|
-
const keys = loadApiKeys();
|
|
1772
1952
|
const response = await fetch(`${backendUrl}/api/run`, {
|
|
1773
1953
|
method: "POST",
|
|
1774
1954
|
headers: { "Content-Type": "application/json" },
|
|
1775
|
-
body: JSON.stringify(
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
openai_api_key: keys.openai || "",
|
|
1781
|
-
google_api_key: keys.google || ""
|
|
1782
|
-
}),
|
|
1955
|
+
body: JSON.stringify(buildRunPayload(
|
|
1956
|
+
currentQuestion,
|
|
1957
|
+
"brainstorm",
|
|
1958
|
+
"Generate ideas, considerations, and suggestions. Be concise and helpful."
|
|
1959
|
+
)),
|
|
1783
1960
|
signal: AbortSignal.timeout(12e4)
|
|
1784
1961
|
});
|
|
1785
1962
|
if (response.ok) {
|
|
1786
|
-
const
|
|
1963
|
+
const brainstormText = await response.text();
|
|
1964
|
+
let brainstormParsed;
|
|
1965
|
+
try {
|
|
1966
|
+
brainstormParsed = JSON.parse(brainstormText);
|
|
1967
|
+
} catch {
|
|
1968
|
+
console.error(chalk3.red(`Backend returned invalid JSON: ${brainstormText.slice(0, 200)}`));
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
const { run_id: brainstormRunId } = brainstormParsed;
|
|
1972
|
+
if (!brainstormRunId || typeof brainstormRunId !== "string") {
|
|
1973
|
+
console.error(chalk3.red("Backend did not return a valid run ID"));
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(brainstormRunId)) {
|
|
1977
|
+
console.error(chalk3.red("Backend returned run ID with invalid characters"));
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
const run_id = brainstormRunId;
|
|
1787
1981
|
const streamResponse = await fetch(`${backendUrl}/api/stream/${run_id}`, { signal: AbortSignal.timeout(12e4) });
|
|
1982
|
+
if (!streamResponse.ok) {
|
|
1983
|
+
console.error(chalk3.red(`Stream endpoint returned ${streamResponse.status}`));
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1788
1986
|
const reader = streamResponse.body?.getReader();
|
|
1789
|
-
if (reader) {
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
tui.displayBrainstormResponse(responseText.trim());
|
|
1819
|
-
} else {
|
|
1820
|
-
console.log(chalk3.gray("(No response received from agent)"));
|
|
1987
|
+
if (!reader) {
|
|
1988
|
+
console.error(chalk3.red("No response body from agent"));
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
let responseText = "";
|
|
1992
|
+
for await (const parsed of parseSSEStream(reader)) {
|
|
1993
|
+
const type = parsed.type;
|
|
1994
|
+
const data = parsed.data;
|
|
1995
|
+
if (type === "done" || type === "execution_complete" || type === "final_result") {
|
|
1996
|
+
const summary = data["summary"] || data["result"] || "";
|
|
1997
|
+
if (summary) responseText = summary;
|
|
1998
|
+
break;
|
|
1999
|
+
} else if (type === "error") {
|
|
2000
|
+
const errorMsg = data["error"] || data["content"] || "Unknown error";
|
|
2001
|
+
responseText += errorMsg + "\n";
|
|
2002
|
+
break;
|
|
2003
|
+
} else if (type === "thought" || type === "observation") {
|
|
2004
|
+
const content = data["content"] || "";
|
|
2005
|
+
if (content) responseText += content + "\n";
|
|
2006
|
+
} else if (type === "agent_response" || type === "agent_result") {
|
|
2007
|
+
const content = data["content"] || data["result"] || "";
|
|
2008
|
+
if (content) responseText += content + "\n";
|
|
2009
|
+
} else if (type === "action") {
|
|
2010
|
+
const content = data["content"] || "";
|
|
2011
|
+
if (content) responseText += content + "\n";
|
|
2012
|
+
} else if (type === "agent_working" || type === "started") {
|
|
2013
|
+
} else if (type !== "plan") {
|
|
2014
|
+
const content = data["content"] || data["response"] || "";
|
|
2015
|
+
if (content) responseText += content + "\n";
|
|
1821
2016
|
}
|
|
1822
2017
|
}
|
|
2018
|
+
if (responseText.trim()) {
|
|
2019
|
+
tui.displayBrainstormResponse(responseText.trim());
|
|
2020
|
+
} else {
|
|
2021
|
+
console.log(chalk3.gray("(No response received from agent)"));
|
|
2022
|
+
}
|
|
1823
2023
|
} else {
|
|
1824
2024
|
console.log(chalk3.red("Could not reach backend for brainstorm response."));
|
|
1825
2025
|
}
|
|
@@ -2021,26 +2221,6 @@ async function withSpinner(spinner, label, fn) {
|
|
|
2021
2221
|
succeed(spinner, label);
|
|
2022
2222
|
return result;
|
|
2023
2223
|
}
|
|
2024
|
-
async function waitForHealthy(port, timeoutMs = 12e4) {
|
|
2025
|
-
const start = Date.now();
|
|
2026
|
-
let attempt = 0;
|
|
2027
|
-
const backoffMs = (n) => Math.min(2e3 * Math.pow(2, n), 1e4);
|
|
2028
|
-
while (Date.now() - start < timeoutMs) {
|
|
2029
|
-
try {
|
|
2030
|
-
const res = await fetch(`http://localhost:${port}/health`);
|
|
2031
|
-
if (res.ok) {
|
|
2032
|
-
const body = await res.text();
|
|
2033
|
-
if (body.includes("ok")) return true;
|
|
2034
|
-
}
|
|
2035
|
-
} catch {
|
|
2036
|
-
}
|
|
2037
|
-
const delay = backoffMs(attempt++);
|
|
2038
|
-
const remaining = timeoutMs - (Date.now() - start);
|
|
2039
|
-
if (remaining <= 0) break;
|
|
2040
|
-
await new Promise((r) => setTimeout(r, Math.min(delay, remaining)));
|
|
2041
|
-
}
|
|
2042
|
-
return false;
|
|
2043
|
-
}
|
|
2044
2224
|
var phases = [
|
|
2045
2225
|
// Phase 1 — Configuration (~30s)
|
|
2046
2226
|
{
|
|
@@ -2120,19 +2300,16 @@ var phases = [
|
|
|
2120
2300
|
name: "Launch",
|
|
2121
2301
|
run: async (ctx, spinner) => {
|
|
2122
2302
|
const { config, keys } = ctx;
|
|
2123
|
-
const alreadyRunning = await isNexusRunning();
|
|
2124
|
-
if (alreadyRunning) {
|
|
2125
|
-
await withSpinner(spinner, "Stopping existing NEXUS container...", () => stopNexus());
|
|
2126
|
-
}
|
|
2127
2303
|
await withSpinner(
|
|
2128
2304
|
spinner,
|
|
2129
2305
|
"Starting NEXUS container...",
|
|
2130
|
-
() =>
|
|
2306
|
+
() => launchNexus(
|
|
2131
2307
|
{
|
|
2132
2308
|
anthropic: keys.ANTHROPIC_API_KEY,
|
|
2133
2309
|
openai: keys.OPENAI_API_KEY || ""
|
|
2134
2310
|
},
|
|
2135
|
-
{ port: config.httpPort }
|
|
2311
|
+
{ port: config.httpPort },
|
|
2312
|
+
{ healthTimeoutMs: 0, stopExisting: true }
|
|
2136
2313
|
)
|
|
2137
2314
|
);
|
|
2138
2315
|
ctx.containerStarted = true;
|
|
@@ -2145,7 +2322,8 @@ var phases = [
|
|
|
2145
2322
|
const { config } = ctx;
|
|
2146
2323
|
spinner.text = `Waiting for NEXUS server on port ${config.httpPort}...`;
|
|
2147
2324
|
spinner.start();
|
|
2148
|
-
const
|
|
2325
|
+
const { waitForServer: waitForServer2 } = await Promise.resolve().then(() => (init_health(), health_exports));
|
|
2326
|
+
const healthy = await waitForServer2(12e4);
|
|
2149
2327
|
if (!healthy) {
|
|
2150
2328
|
fail(spinner, "Server failed to start within 120s");
|
|
2151
2329
|
log.warn("Check logs: docker logs nexus");
|
|
@@ -2167,11 +2345,11 @@ var phases = [
|
|
|
2167
2345
|
await withSpinner(
|
|
2168
2346
|
spinner,
|
|
2169
2347
|
"Installing cloudflared...",
|
|
2170
|
-
() => installCloudflared(
|
|
2348
|
+
() => installCloudflared(platform.arch)
|
|
2171
2349
|
);
|
|
2172
2350
|
spinner.text = "Starting tunnel...";
|
|
2173
2351
|
spinner.start();
|
|
2174
|
-
const url = await startTunnel(
|
|
2352
|
+
const url = await startTunnel();
|
|
2175
2353
|
if (url) {
|
|
2176
2354
|
ctx.tunnelUrl = url;
|
|
2177
2355
|
succeed(spinner, `Tunnel active: ${url}`);
|
|
@@ -2231,40 +2409,6 @@ import { Command as Command3 } from "commander";
|
|
|
2231
2409
|
init_logger();
|
|
2232
2410
|
init_secrets();
|
|
2233
2411
|
init_docker();
|
|
2234
|
-
|
|
2235
|
-
// src/core/health.ts
|
|
2236
|
-
init_docker();
|
|
2237
|
-
async function waitForServer(timeoutMs = 9e5) {
|
|
2238
|
-
const start = Date.now();
|
|
2239
|
-
let lastLog = 0;
|
|
2240
|
-
let attempt = 0;
|
|
2241
|
-
const backoffMs = (n) => Math.min(3e3 * Math.pow(2, n), 3e4);
|
|
2242
|
-
while (Date.now() - start < timeoutMs) {
|
|
2243
|
-
try {
|
|
2244
|
-
const { stdout, code } = await dockerExec("curl -sf http://localhost:4200/health");
|
|
2245
|
-
if (code === 0 && stdout.includes("ok")) return true;
|
|
2246
|
-
} catch {
|
|
2247
|
-
}
|
|
2248
|
-
const elapsed = Date.now() - start;
|
|
2249
|
-
if (elapsed - lastLog >= 3e4) {
|
|
2250
|
-
lastLog = elapsed;
|
|
2251
|
-
try {
|
|
2252
|
-
const { stdout } = await dockerExec("systemctl is-active nexus 2>/dev/null || echo 'starting...'");
|
|
2253
|
-
process.stderr.write(`
|
|
2254
|
-
[server ${Math.round(elapsed / 1e3)}s] ${stdout.trim().slice(0, 120)}
|
|
2255
|
-
`);
|
|
2256
|
-
} catch {
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
const delay = backoffMs(attempt++);
|
|
2260
|
-
const remaining = timeoutMs - (Date.now() - start);
|
|
2261
|
-
if (remaining <= 0) break;
|
|
2262
|
-
await new Promise((r) => setTimeout(r, Math.min(delay, remaining)));
|
|
2263
|
-
}
|
|
2264
|
-
return false;
|
|
2265
|
-
}
|
|
2266
|
-
|
|
2267
|
-
// src/commands/start.ts
|
|
2268
2412
|
var startCommand = new Command3("start").description("Start the NEXUS runtime").action(async () => {
|
|
2269
2413
|
const config = loadConfig();
|
|
2270
2414
|
if (!config) {
|
|
@@ -2275,25 +2419,25 @@ var startCommand = new Command3("start").description("Start the NEXUS runtime").
|
|
|
2275
2419
|
log.success("NEXUS is already running");
|
|
2276
2420
|
return;
|
|
2277
2421
|
}
|
|
2278
|
-
let spinner = createSpinner("
|
|
2422
|
+
let spinner = createSpinner("Checking NEXUS image...");
|
|
2279
2423
|
spinner.start();
|
|
2280
|
-
await
|
|
2424
|
+
const localExists = await imageExistsLocally("buildwithnexus/nexus", "latest");
|
|
2425
|
+
if (!localExists) {
|
|
2426
|
+
spinner.text = "Pulling NEXUS image...";
|
|
2427
|
+
await pullImage("buildwithnexus/nexus", "latest");
|
|
2428
|
+
}
|
|
2281
2429
|
succeed(spinner, "Image ready");
|
|
2282
|
-
spinner = createSpinner("Starting NEXUS container...");
|
|
2283
|
-
spinner.start();
|
|
2284
2430
|
const keys = loadKeys();
|
|
2285
2431
|
if (!keys) {
|
|
2286
|
-
|
|
2432
|
+
log.error("No API keys found. Run: buildwithnexus init");
|
|
2287
2433
|
process.exit(1);
|
|
2288
2434
|
}
|
|
2289
|
-
|
|
2435
|
+
spinner = createSpinner("Starting NEXUS container...");
|
|
2436
|
+
spinner.start();
|
|
2437
|
+
const ok = await launchNexus(
|
|
2290
2438
|
{ anthropic: keys.ANTHROPIC_API_KEY, openai: keys.OPENAI_API_KEY || "" },
|
|
2291
2439
|
{ port: config.httpPort }
|
|
2292
2440
|
);
|
|
2293
|
-
succeed(spinner, "Container started");
|
|
2294
|
-
spinner = createSpinner("Waiting for NEXUS server...");
|
|
2295
|
-
spinner.start();
|
|
2296
|
-
const ok = await waitForServer(6e4);
|
|
2297
2441
|
if (ok) {
|
|
2298
2442
|
succeed(spinner, "NEXUS server running");
|
|
2299
2443
|
} else {
|
|
@@ -2314,13 +2458,11 @@ var startCommand = new Command3("start").description("Start the NEXUS runtime").
|
|
|
2314
2458
|
|
|
2315
2459
|
// src/commands/stop.ts
|
|
2316
2460
|
import { Command as Command4 } from "commander";
|
|
2461
|
+
import { execa as execa3 } from "execa";
|
|
2317
2462
|
init_logger();
|
|
2318
|
-
import { execFile } from "child_process";
|
|
2319
|
-
import { promisify } from "util";
|
|
2320
|
-
var execFileAsync = promisify(execFile);
|
|
2321
2463
|
async function containerExists() {
|
|
2322
2464
|
try {
|
|
2323
|
-
const { stdout } = await
|
|
2465
|
+
const { stdout } = await execa3("docker", [
|
|
2324
2466
|
"ps",
|
|
2325
2467
|
"-a",
|
|
2326
2468
|
"--filter",
|
|
@@ -2335,7 +2477,7 @@ async function containerExists() {
|
|
|
2335
2477
|
}
|
|
2336
2478
|
async function isContainerRunning() {
|
|
2337
2479
|
try {
|
|
2338
|
-
const { stdout } = await
|
|
2480
|
+
const { stdout } = await execa3("docker", [
|
|
2339
2481
|
"ps",
|
|
2340
2482
|
"--filter",
|
|
2341
2483
|
"name=^nexus$",
|
|
@@ -2359,10 +2501,10 @@ var stopCommand = new Command4("stop").description("Gracefully shut down the NEX
|
|
|
2359
2501
|
try {
|
|
2360
2502
|
if (await isContainerRunning()) {
|
|
2361
2503
|
spinner.text = "Stopping container...";
|
|
2362
|
-
await
|
|
2504
|
+
await execa3("docker", ["stop", "nexus"]);
|
|
2363
2505
|
}
|
|
2364
2506
|
spinner.text = "Removing container...";
|
|
2365
|
-
await
|
|
2507
|
+
await execa3("docker", ["rm", "nexus"]);
|
|
2366
2508
|
succeed(spinner, "NEXUS container stopped and removed");
|
|
2367
2509
|
} catch (err) {
|
|
2368
2510
|
fail(spinner, "Failed to stop NEXUS container");
|
|
@@ -2506,11 +2648,11 @@ var doctorCommand = new Command6("doctor").description("Diagnose NEXUS runtime e
|
|
|
2506
2648
|
// src/commands/logs.ts
|
|
2507
2649
|
init_logger();
|
|
2508
2650
|
import { Command as Command7 } from "commander";
|
|
2509
|
-
import { execa as
|
|
2651
|
+
import { execa as execa4 } from "execa";
|
|
2510
2652
|
var logsCommand = new Command7("logs").description("View NEXUS server logs").action(async () => {
|
|
2511
2653
|
let containerExists2 = false;
|
|
2512
2654
|
try {
|
|
2513
|
-
const { stdout } = await
|
|
2655
|
+
const { stdout } = await execa4("docker", [
|
|
2514
2656
|
"ps",
|
|
2515
2657
|
"-a",
|
|
2516
2658
|
"--filter",
|
|
@@ -2527,7 +2669,7 @@ var logsCommand = new Command7("logs").description("View NEXUS server logs").act
|
|
|
2527
2669
|
log.error("NEXUS container not found. Start with: buildwithnexus start");
|
|
2528
2670
|
process.exit(1);
|
|
2529
2671
|
}
|
|
2530
|
-
const proc =
|
|
2672
|
+
const proc = execa4("docker", ["logs", "-f", "nexus"], {
|
|
2531
2673
|
stdout: "pipe",
|
|
2532
2674
|
stderr: "pipe"
|
|
2533
2675
|
});
|
|
@@ -2544,7 +2686,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
2544
2686
|
init_logger();
|
|
2545
2687
|
init_secrets();
|
|
2546
2688
|
init_docker();
|
|
2547
|
-
import { execa as
|
|
2689
|
+
import { execa as execa5 } from "execa";
|
|
2548
2690
|
function getReleaseTarball() {
|
|
2549
2691
|
const dir = path6.dirname(fileURLToPath2(import.meta.url));
|
|
2550
2692
|
const tarballPath = path6.join(dir, "nexus-release.tar.gz");
|
|
@@ -2566,7 +2708,7 @@ var updateCommand = new Command8("update").description("Update NEXUS to the late
|
|
|
2566
2708
|
let spinner = createSpinner("Uploading release tarball...");
|
|
2567
2709
|
spinner.start();
|
|
2568
2710
|
const tarballPath = getReleaseTarball();
|
|
2569
|
-
await
|
|
2711
|
+
await execa5("docker", ["cp", tarballPath, "nexus:/tmp/nexus-release.tar.gz"]);
|
|
2570
2712
|
succeed(spinner, "Tarball uploaded");
|
|
2571
2713
|
spinner = createSpinner("Stopping NEXUS server...");
|
|
2572
2714
|
spinner.start();
|
|
@@ -2720,7 +2862,7 @@ keysCommand.command("set <key>").description("Set or update an API key (e.g. ANT
|
|
|
2720
2862
|
init_logger();
|
|
2721
2863
|
init_docker();
|
|
2722
2864
|
import { Command as Command11 } from "commander";
|
|
2723
|
-
import { execa as
|
|
2865
|
+
import { execa as execa6 } from "execa";
|
|
2724
2866
|
var sshCommand = new Command11("ssh").description("Open an interactive shell inside the NEXUS container").action(async () => {
|
|
2725
2867
|
const running = await isNexusRunning();
|
|
2726
2868
|
if (!running) {
|
|
@@ -2728,7 +2870,7 @@ var sshCommand = new Command11("ssh").description("Open an interactive shell ins
|
|
|
2728
2870
|
process.exit(1);
|
|
2729
2871
|
}
|
|
2730
2872
|
log.dim("Opening shell in NEXUS container...");
|
|
2731
|
-
await
|
|
2873
|
+
await execa6("docker", ["exec", "-it", "nexus", "/bin/bash"], { stdio: "inherit" });
|
|
2732
2874
|
});
|
|
2733
2875
|
|
|
2734
2876
|
// src/commands/brainstorm.ts
|
|
@@ -2776,13 +2918,7 @@ var brainstormCommand = new Command12("brainstorm").description("Brainstorm an i
|
|
|
2776
2918
|
}
|
|
2777
2919
|
const spinner = createSpinner("Connecting to NEXUS...");
|
|
2778
2920
|
spinner.start();
|
|
2779
|
-
|
|
2780
|
-
try {
|
|
2781
|
-
const healthRes = await fetch(`http://localhost:${config.httpPort}/health`);
|
|
2782
|
-
const healthText = await healthRes.text();
|
|
2783
|
-
healthOk = healthRes.ok && healthText.includes("ok");
|
|
2784
|
-
} catch {
|
|
2785
|
-
}
|
|
2921
|
+
const healthOk = await checkServerHealth(config.httpPort);
|
|
2786
2922
|
if (!healthOk) {
|
|
2787
2923
|
fail(spinner, "NEXUS server is not healthy");
|
|
2788
2924
|
log.warn("Check status: buildwithnexus status");
|
|
@@ -3303,33 +3439,6 @@ var EventStream = class {
|
|
|
3303
3439
|
};
|
|
3304
3440
|
|
|
3305
3441
|
// src/commands/shell.ts
|
|
3306
|
-
async function httpPost(httpPort, path11, body) {
|
|
3307
|
-
const res = await fetch(`http://localhost:${httpPort}${path11}`, {
|
|
3308
|
-
method: "POST",
|
|
3309
|
-
headers: { "Content-Type": "application/json" },
|
|
3310
|
-
body: JSON.stringify(body),
|
|
3311
|
-
signal: AbortSignal.timeout(6e4)
|
|
3312
|
-
});
|
|
3313
|
-
if (!res.ok) throw new Error(`Server returned ${res.status}`);
|
|
3314
|
-
const text = await res.text();
|
|
3315
|
-
try {
|
|
3316
|
-
const parsed = JSON.parse(text);
|
|
3317
|
-
return parsed.response ?? parsed.message ?? text;
|
|
3318
|
-
} catch {
|
|
3319
|
-
return text;
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
async function httpGet(httpPort, path11) {
|
|
3323
|
-
try {
|
|
3324
|
-
const res = await fetch(`http://localhost:${httpPort}${path11}`, {
|
|
3325
|
-
signal: AbortSignal.timeout(1e4)
|
|
3326
|
-
});
|
|
3327
|
-
const text = await res.text();
|
|
3328
|
-
return { ok: res.ok, text };
|
|
3329
|
-
} catch {
|
|
3330
|
-
return { ok: false, text: "" };
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
3442
|
async function sendMessage2(httpPort, message) {
|
|
3334
3443
|
return httpPost(httpPort, "/message", { message, source: "shell" });
|
|
3335
3444
|
}
|
|
@@ -3563,12 +3672,12 @@ var shellCommand2 = new Command14("shell").description("Launch the interactive N
|
|
|
3563
3672
|
name: "logs",
|
|
3564
3673
|
description: "Show recent container logs",
|
|
3565
3674
|
handler: async () => {
|
|
3566
|
-
const { execa:
|
|
3675
|
+
const { execa: execa7 } = await import("execa");
|
|
3567
3676
|
console.log("");
|
|
3568
3677
|
console.log(chalk16.bold(" Recent Logs:"));
|
|
3569
3678
|
console.log(chalk16.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"));
|
|
3570
3679
|
try {
|
|
3571
|
-
const { stdout } = await
|
|
3680
|
+
const { stdout } = await execa7("docker", ["logs", "--tail", "30", "nexus"]);
|
|
3572
3681
|
for (const line of redact(stdout).split("\n")) {
|
|
3573
3682
|
console.log(chalk16.dim(" " + line));
|
|
3574
3683
|
}
|
|
@@ -3582,10 +3691,10 @@ var shellCommand2 = new Command14("shell").description("Launch the interactive N
|
|
|
3582
3691
|
name: "exec",
|
|
3583
3692
|
description: "Drop into the container shell for debugging/inspection",
|
|
3584
3693
|
handler: async () => {
|
|
3585
|
-
const { execa:
|
|
3694
|
+
const { execa: execa7 } = await import("execa");
|
|
3586
3695
|
eventStream.stop();
|
|
3587
3696
|
try {
|
|
3588
|
-
await
|
|
3697
|
+
await execa7("docker", ["exec", "-it", "nexus", "/bin/sh"], { stdio: "inherit" });
|
|
3589
3698
|
} catch {
|
|
3590
3699
|
}
|
|
3591
3700
|
eventStream.start();
|
|
@@ -3845,7 +3954,7 @@ import os4 from "os";
|
|
|
3845
3954
|
import path10 from "path";
|
|
3846
3955
|
var homeEnvPath = path10.join(os4.homedir(), ".env.local");
|
|
3847
3956
|
dotenv2.config({ path: homeEnvPath });
|
|
3848
|
-
var version =
|
|
3957
|
+
var version = resolvedVersion;
|
|
3849
3958
|
checkForUpdates(version);
|
|
3850
3959
|
program.name("buildwithnexus").description("Nexus - AI-Powered Task Execution").version(version);
|
|
3851
3960
|
program.command("da-init").description("Initialize Nexus (set up API keys and .env.local)").action(deepAgentsInitCommand);
|
package/dist/deep-agents-bin.js
CHANGED
|
@@ -231,6 +231,19 @@ function loadApiKeys() {
|
|
|
231
231
|
return result;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
// src/core/api.ts
|
|
235
|
+
function buildRunPayload(task, agentRole, agentGoal, keys) {
|
|
236
|
+
const k = keys ?? loadApiKeys();
|
|
237
|
+
return {
|
|
238
|
+
task,
|
|
239
|
+
agent_role: agentRole,
|
|
240
|
+
agent_goal: agentGoal,
|
|
241
|
+
api_key: k.anthropic || "",
|
|
242
|
+
openai_api_key: k.openai || "",
|
|
243
|
+
google_api_key: k.google || ""
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
234
247
|
// src/core/models.ts
|
|
235
248
|
var MODELS = {
|
|
236
249
|
OPUS: "claude-opus-4-6",
|
|
@@ -256,18 +269,11 @@ Starting Nexus Workflow
|
|
|
256
269
|
console.log(` Backend: ${backendUrl}
|
|
257
270
|
`);
|
|
258
271
|
try {
|
|
259
|
-
const
|
|
272
|
+
const payload = buildRunPayload(task, options.agent, options.goal || "");
|
|
260
273
|
const response = await fetch(`${backendUrl}/api/run`, {
|
|
261
274
|
method: "POST",
|
|
262
275
|
headers: { "Content-Type": "application/json" },
|
|
263
|
-
body: JSON.stringify(
|
|
264
|
-
task,
|
|
265
|
-
agent_role: options.agent,
|
|
266
|
-
agent_goal: options.goal || "",
|
|
267
|
-
api_key: keys.anthropic || "",
|
|
268
|
-
openai_api_key: keys.openai || "",
|
|
269
|
-
google_api_key: keys.google || ""
|
|
270
|
-
})
|
|
276
|
+
body: JSON.stringify(payload)
|
|
271
277
|
});
|
|
272
278
|
const { run_id } = await response.json();
|
|
273
279
|
console.log(`Run ID: ${run_id}
|