@vendian/cli 0.0.31 → 0.0.33
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/cli-wrapper.mjs +647 -1240
- package/package.json +2 -4
package/cli-wrapper.mjs
CHANGED
|
@@ -1104,10 +1104,10 @@ function reportProgress(onProgress, message) {
|
|
|
1104
1104
|
// src/tui.js
|
|
1105
1105
|
import fs11 from "node:fs";
|
|
1106
1106
|
import path8 from "node:path";
|
|
1107
|
-
import
|
|
1107
|
+
import readline from "node:readline";
|
|
1108
1108
|
|
|
1109
1109
|
// src/version.js
|
|
1110
|
-
var CLI_VERSION = true ? "0.0.
|
|
1110
|
+
var CLI_VERSION = true ? "0.0.33" : process.env.npm_package_version || "0.0.0-dev";
|
|
1111
1111
|
|
|
1112
1112
|
// src/npm-update.js
|
|
1113
1113
|
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
@@ -1383,10 +1383,6 @@ function serveProcessExitMessage({ stderr = "", code = 0, signal = "" } = {}) {
|
|
|
1383
1383
|
}
|
|
1384
1384
|
return "";
|
|
1385
1385
|
}
|
|
1386
|
-
function serveProcessExitStderrTail(stderr = "", limit = 1200) {
|
|
1387
|
-
const text = String(stderr || "").trim();
|
|
1388
|
-
return text.length > limit ? text.slice(-limit) : text;
|
|
1389
|
-
}
|
|
1390
1386
|
function applyServeEvent(state, event) {
|
|
1391
1387
|
if (!event || typeof event.type !== "string") {
|
|
1392
1388
|
return state;
|
|
@@ -1678,9 +1674,6 @@ function appendAgentLog(agentLogs, event) {
|
|
|
1678
1674
|
[key]: [...existing, normalized.entry].slice(-5e3)
|
|
1679
1675
|
};
|
|
1680
1676
|
}
|
|
1681
|
-
function agentLogEntries(agentLogs, relativePath) {
|
|
1682
|
-
return (agentLogs || {})[relativePath] || [];
|
|
1683
|
-
}
|
|
1684
1677
|
function mergeAgentLogRecords(agentLogs, records = []) {
|
|
1685
1678
|
let next = agentLogs || {};
|
|
1686
1679
|
for (const record of records) {
|
|
@@ -1872,36 +1865,6 @@ function serveEventAgentLogEntry(event) {
|
|
|
1872
1865
|
}
|
|
1873
1866
|
return null;
|
|
1874
1867
|
}
|
|
1875
|
-
function agentRuntimeStatus(agent, agentRunState = {}) {
|
|
1876
|
-
const path9 = stringValue(agent?.relativePath || ".");
|
|
1877
|
-
const run2 = (agentRunState || {})[path9];
|
|
1878
|
-
const inventoryStatus = stringValue(agent?.status);
|
|
1879
|
-
if (run2?.status === "running") {
|
|
1880
|
-
return { status: "running", label: "running", run: run2 };
|
|
1881
|
-
}
|
|
1882
|
-
if (run2?.status === "preparing") {
|
|
1883
|
-
return { status: "preparing", label: "preparing", run: run2 };
|
|
1884
|
-
}
|
|
1885
|
-
if (run2?.status === "error" || inventoryStatus === "error") {
|
|
1886
|
-
return { status: "error", label: "error", run: run2 };
|
|
1887
|
-
}
|
|
1888
|
-
if (run2?.status === "disabled" || inventoryStatus === "disabled") {
|
|
1889
|
-
return {
|
|
1890
|
-
status: "disabled",
|
|
1891
|
-
label: "disabled",
|
|
1892
|
-
run: run2,
|
|
1893
|
-
disabledReason: run2?.disabledReason || agent?.disabledReason || "System dependency not met locally",
|
|
1894
|
-
resolutionHints: run2?.resolutionHints || agent?.resolutionHints || []
|
|
1895
|
-
};
|
|
1896
|
-
}
|
|
1897
|
-
if (inventoryStatus === "online") {
|
|
1898
|
-
return { status: run2?.status === "completed" ? "completed" : "ready", label: "ready", run: run2 };
|
|
1899
|
-
}
|
|
1900
|
-
if (run2?.status === "completed") {
|
|
1901
|
-
return { status: "completed", label: "ready", run: run2 };
|
|
1902
|
-
}
|
|
1903
|
-
return { status: inventoryStatus || "unknown", label: inventoryStatus || "unknown", run: run2 };
|
|
1904
|
-
}
|
|
1905
1868
|
function appendError(errors, scope, message) {
|
|
1906
1869
|
return [...errors, { scope, message: stringValue(message) }].slice(-20);
|
|
1907
1870
|
}
|
|
@@ -2391,289 +2354,224 @@ var ascii = {
|
|
|
2391
2354
|
var fig = supportsUnicode ? unicode : ascii;
|
|
2392
2355
|
|
|
2393
2356
|
// src/tui.js
|
|
2394
|
-
var
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
diagnosticsCount = 0
|
|
2420
|
-
} = {}) {
|
|
2421
|
-
const availableRows = Math.max(4, (termHeight || 24) - SERVE_CONTENT_ROW + 1);
|
|
2422
|
-
const clampedSelectedIdx = clampListIndex(selectedIdx, agentCount);
|
|
2423
|
-
const viewport = {
|
|
2424
|
-
selectedIdx: clampedSelectedIdx,
|
|
2425
|
-
showBlankAfterStatus: false,
|
|
2426
|
-
showAgentHeader: agentCount > 0,
|
|
2427
|
-
visibleAgentCount: 0,
|
|
2428
|
-
agentWindowStart: 0,
|
|
2429
|
-
showAgentHint: false,
|
|
2430
|
-
showAgentRange: false,
|
|
2431
|
-
visibleActivityCount: 0,
|
|
2432
|
-
showActivityHeader: false,
|
|
2433
|
-
visibleDiagnosticsCount: 0,
|
|
2434
|
-
showDiagnosticsHeader: false,
|
|
2435
|
-
showInlineErrors: (termHeight || 24) >= 24
|
|
2436
|
-
};
|
|
2437
|
-
let remaining = availableRows;
|
|
2438
|
-
remaining -= 1;
|
|
2439
|
-
remaining -= 1;
|
|
2440
|
-
if (agentCount > 0) {
|
|
2441
|
-
remaining -= 1;
|
|
2442
|
-
viewport.visibleAgentCount = 1;
|
|
2443
|
-
remaining -= 1;
|
|
2444
|
-
} else {
|
|
2445
|
-
remaining -= 1;
|
|
2446
|
-
}
|
|
2447
|
-
if (remaining > 0) {
|
|
2448
|
-
viewport.showBlankAfterStatus = true;
|
|
2449
|
-
remaining -= 1;
|
|
2450
|
-
}
|
|
2451
|
-
if (agentCount > 1 && remaining > 0) {
|
|
2452
|
-
const extraAgentRows = Math.min(agentCount - 1, remaining);
|
|
2453
|
-
viewport.visibleAgentCount += extraAgentRows;
|
|
2454
|
-
remaining -= extraAgentRows;
|
|
2455
|
-
}
|
|
2456
|
-
if (agentCount > 0) {
|
|
2457
|
-
viewport.agentWindowStart = centeredWindowStart(
|
|
2458
|
-
clampedSelectedIdx,
|
|
2459
|
-
viewport.visibleAgentCount,
|
|
2460
|
-
agentCount
|
|
2461
|
-
);
|
|
2462
|
-
viewport.showAgentHint = remaining > 0;
|
|
2463
|
-
if (viewport.showAgentHint) remaining -= 1;
|
|
2464
|
-
viewport.showAgentRange = viewport.visibleAgentCount > 0 && viewport.visibleAgentCount < agentCount;
|
|
2465
|
-
}
|
|
2466
|
-
if (activityCount > 0 && remaining > 1) {
|
|
2467
|
-
viewport.showActivityHeader = true;
|
|
2468
|
-
remaining -= 1;
|
|
2469
|
-
viewport.visibleActivityCount = Math.min(activityCount, remaining);
|
|
2470
|
-
remaining -= viewport.visibleActivityCount;
|
|
2471
|
-
}
|
|
2472
|
-
if (diagnosticsCount > 0 && remaining > 1) {
|
|
2473
|
-
viewport.showDiagnosticsHeader = true;
|
|
2474
|
-
remaining -= 1;
|
|
2475
|
-
viewport.visibleDiagnosticsCount = Math.min(diagnosticsCount, remaining);
|
|
2476
|
-
}
|
|
2477
|
-
return viewport;
|
|
2357
|
+
var ANSI = {
|
|
2358
|
+
reset: "\x1B[0m",
|
|
2359
|
+
bold: "\x1B[1m",
|
|
2360
|
+
gray: "\x1B[90m",
|
|
2361
|
+
cyan: "\x1B[36m",
|
|
2362
|
+
green: "\x1B[32m",
|
|
2363
|
+
yellow: "\x1B[33m",
|
|
2364
|
+
red: "\x1B[31m",
|
|
2365
|
+
blue: "\x1B[94m"
|
|
2366
|
+
};
|
|
2367
|
+
function colorize(code, text) {
|
|
2368
|
+
return process.stdout.isTTY ? `${code}${String(text)}${ANSI.reset}` : String(text);
|
|
2369
|
+
}
|
|
2370
|
+
var col = {
|
|
2371
|
+
bold: (t) => colorize(ANSI.bold, t),
|
|
2372
|
+
gray: (t) => colorize(ANSI.gray, t),
|
|
2373
|
+
cyan: (t) => colorize(ANSI.cyan, t),
|
|
2374
|
+
green: (t) => colorize(ANSI.green, t),
|
|
2375
|
+
yellow: (t) => colorize(ANSI.yellow, t),
|
|
2376
|
+
red: (t) => colorize(ANSI.red, t),
|
|
2377
|
+
blue: (t) => colorize(ANSI.blue, t)
|
|
2378
|
+
};
|
|
2379
|
+
function print(line = "") {
|
|
2380
|
+
process.stdout.write(`${line}
|
|
2381
|
+
`);
|
|
2478
2382
|
}
|
|
2479
|
-
function
|
|
2480
|
-
|
|
2481
|
-
if (attempt === 1) return "SIGTERM";
|
|
2482
|
-
return "SIGKILL";
|
|
2383
|
+
function hr(w) {
|
|
2384
|
+
return "\u2500".repeat(Math.min(w || 60, process.stdout.columns || 80));
|
|
2483
2385
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2386
|
+
function setCursorVisible(visible) {
|
|
2387
|
+
if (!process.stdout.isTTY) return;
|
|
2388
|
+
process.stdout.write(visible ? "\x1B[?25h" : "\x1B[?25l");
|
|
2389
|
+
}
|
|
2390
|
+
function clearScreen() {
|
|
2391
|
+
if (process.stdout.isTTY) process.stdout.write("\x1B[2J\x1B[H");
|
|
2392
|
+
}
|
|
2393
|
+
function makeRl() {
|
|
2394
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
2395
|
+
}
|
|
2396
|
+
async function ask(question, defaultValue = "") {
|
|
2397
|
+
const rl = makeRl();
|
|
2398
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
2399
|
+
return new Promise((resolve) => {
|
|
2400
|
+
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
2401
|
+
rl.close();
|
|
2402
|
+
resolve(answer.trim() || defaultValue);
|
|
2403
|
+
});
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
async function pressEnterToContinue({ grabActive = true } = {}) {
|
|
2407
|
+
return new Promise((resolve) => {
|
|
2408
|
+
const rl = makeRl();
|
|
2409
|
+
process.stdout.write("\n Press Enter to continue... ");
|
|
2410
|
+
rl.once("line", () => {
|
|
2411
|
+
rl.close();
|
|
2412
|
+
resolve();
|
|
2413
|
+
});
|
|
2414
|
+
rl.once("close", resolve);
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
async function pickMenu(items, { allowBack = true } = {}) {
|
|
2489
2418
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2419
|
+
return pickMenuNumeric(items, allowBack);
|
|
2420
|
+
}
|
|
2421
|
+
const backIdx = allowBack ? items.length : -1;
|
|
2422
|
+
const total = items.length + (allowBack ? 1 : 0);
|
|
2423
|
+
let sel = 0;
|
|
2424
|
+
function renderLine(i) {
|
|
2425
|
+
const isBack = allowBack && i === items.length;
|
|
2426
|
+
const label = isBack ? col.gray("Back") : items[i];
|
|
2427
|
+
const pfx = i === sel ? col.cyan("\u25BA") : " ";
|
|
2428
|
+
const num = col.gray(isBack ? "0" : String(i + 1));
|
|
2429
|
+
return ` ${pfx} ${num} ${label}\x1B[K`;
|
|
2493
2430
|
}
|
|
2494
|
-
await refreshInteractiveManagedRuntime({ env, platform, output });
|
|
2495
|
-
await loadTerm();
|
|
2496
|
-
term.fullscreen(true);
|
|
2497
2431
|
setCursorVisible(false);
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2432
|
+
for (let i = 0; i < total; i++) {
|
|
2433
|
+
process.stdout.write(renderLine(i) + "\n");
|
|
2434
|
+
}
|
|
2435
|
+
return new Promise((resolve) => {
|
|
2436
|
+
readline.emitKeypressEvents(process.stdin);
|
|
2437
|
+
process.stdin.setRawMode(true);
|
|
2438
|
+
process.stdin.resume();
|
|
2439
|
+
function redraw() {
|
|
2440
|
+
process.stdout.write(`\x1B[${total}A`);
|
|
2441
|
+
for (let i = 0; i < total; i++) {
|
|
2442
|
+
process.stdout.write(renderLine(i) + "\n");
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
function done(result) {
|
|
2446
|
+
process.stdin.off("keypress", onKey);
|
|
2447
|
+
try {
|
|
2448
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
2449
|
+
} catch {
|
|
2450
|
+
}
|
|
2502
2451
|
setCursorVisible(true);
|
|
2503
|
-
|
|
2504
|
-
} catch {
|
|
2452
|
+
resolve(result);
|
|
2505
2453
|
}
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2454
|
+
function onKey(str, key) {
|
|
2455
|
+
if (!key) return;
|
|
2456
|
+
if (key.name === "up") {
|
|
2457
|
+
sel = (sel - 1 + total) % total;
|
|
2458
|
+
redraw();
|
|
2459
|
+
} else if (key.name === "down") {
|
|
2460
|
+
sel = (sel + 1) % total;
|
|
2461
|
+
redraw();
|
|
2462
|
+
} else if (key.name === "return") {
|
|
2463
|
+
done(sel === backIdx ? -1 : sel);
|
|
2464
|
+
} else if (key.name === "escape") {
|
|
2465
|
+
done(-1);
|
|
2466
|
+
} else if (key.ctrl && key.name === "c") {
|
|
2467
|
+
done(null);
|
|
2468
|
+
} else if (/^\d$/.test(str || "")) {
|
|
2469
|
+
const n = parseInt(str, 10);
|
|
2470
|
+
if (n === 0 && allowBack) {
|
|
2471
|
+
done(-1);
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2474
|
+
if (n >= 1 && n <= items.length) {
|
|
2475
|
+
done(n - 1);
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
process.stdin.on("keypress", onKey);
|
|
2481
|
+
});
|
|
2514
2482
|
}
|
|
2515
|
-
async function
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
if (
|
|
2483
|
+
async function pickMenuNumeric(items, allowBack) {
|
|
2484
|
+
items.forEach((item, i) => print(` ${col.blue(String(i + 1))} ${item}`));
|
|
2485
|
+
if (allowBack) print(` ${col.gray("0 Back")}`);
|
|
2486
|
+
print("");
|
|
2487
|
+
const ans = await ask("Choose");
|
|
2488
|
+
const n = parseInt(ans, 10);
|
|
2489
|
+
if (n === 0 && allowBack) return -1;
|
|
2490
|
+
if (n >= 1 && n <= items.length) return n - 1;
|
|
2491
|
+
return -1;
|
|
2492
|
+
}
|
|
2493
|
+
async function withOutputMode(label, fn) {
|
|
2494
|
+
setCursorVisible(true);
|
|
2495
|
+
print("");
|
|
2496
|
+
print(` ${label}`);
|
|
2497
|
+
print(` ${"\u2500".repeat(50)}`);
|
|
2498
|
+
print("");
|
|
2523
2499
|
try {
|
|
2524
|
-
await
|
|
2500
|
+
await fn();
|
|
2525
2501
|
} catch (error) {
|
|
2526
|
-
|
|
2527
|
-
`);
|
|
2528
|
-
}
|
|
2529
|
-
return autoUpdate({ env, platform, force: true });
|
|
2530
|
-
}
|
|
2531
|
-
async function mainLoop({ env, platform }) {
|
|
2532
|
-
let screen = "home";
|
|
2533
|
-
while (true) {
|
|
2534
|
-
let next;
|
|
2535
|
-
switch (screen) {
|
|
2536
|
-
case "run":
|
|
2537
|
-
next = await showRun({ env, platform });
|
|
2538
|
-
break;
|
|
2539
|
-
case "home":
|
|
2540
|
-
next = await showHome({ env, platform });
|
|
2541
|
-
break;
|
|
2542
|
-
case "connect":
|
|
2543
|
-
next = await showConnect({ env, platform });
|
|
2544
|
-
break;
|
|
2545
|
-
case "create":
|
|
2546
|
-
next = await showCreate({ env, platform });
|
|
2547
|
-
break;
|
|
2548
|
-
case "serve":
|
|
2549
|
-
next = await showServe({ env, platform });
|
|
2550
|
-
break;
|
|
2551
|
-
case "commands":
|
|
2552
|
-
next = await showCommands();
|
|
2553
|
-
break;
|
|
2554
|
-
default:
|
|
2555
|
-
next = "exit";
|
|
2556
|
-
}
|
|
2557
|
-
if (!next || next === "exit") break;
|
|
2558
|
-
screen = next;
|
|
2502
|
+
print(`
|
|
2503
|
+
Something went wrong: ${errMsg(error)}`);
|
|
2559
2504
|
}
|
|
2505
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2560
2506
|
}
|
|
2561
|
-
function
|
|
2507
|
+
function printHeader({ env, platform } = {}) {
|
|
2562
2508
|
const active = activeCloudAuthStatus({ env, platform });
|
|
2563
|
-
const
|
|
2564
|
-
const
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
const title = ` ${fig.arrowUp} VENDIAN`;
|
|
2569
|
-
const gap = Math.max(1, inner - title.length - ver.length - 2);
|
|
2570
|
-
term.moveTo(1, 1);
|
|
2571
|
-
term.gray(`\u256D${"\u2500".repeat(inner)}\u256E
|
|
2572
|
-
`);
|
|
2573
|
-
term.gray("\u2502");
|
|
2574
|
-
term.cyan.bold(title);
|
|
2575
|
-
term(" ".repeat(gap));
|
|
2576
|
-
term.gray(`${ver} \u2502
|
|
2577
|
-
`);
|
|
2578
|
-
term.gray(`\u251C${"\u2500".repeat(inner)}\u2524
|
|
2579
|
-
`);
|
|
2580
|
-
const accountVal = active.authenticated ? `${active.email || "Signed in"} \u2014 ${envLabel(active.apiUrl)}` : "Not signed in \u2014 choose Sign in from the menu";
|
|
2581
|
-
drawHdrRow(inner, active.authenticated ? "ok" : "warn", "Account", accountVal);
|
|
2582
|
-
const systemOk = runtime.installed && pkg.configured;
|
|
2583
|
-
const systemVal = systemOk ? "Ready to run agents" : !runtime.installed ? "Not set up yet \u2014 choose Sign in from the menu" : "Package access missing \u2014 choose Sign in from the menu";
|
|
2584
|
-
drawHdrRow(inner, systemOk ? "ok" : "warn", "System", systemVal);
|
|
2585
|
-
if (serveState !== null) {
|
|
2586
|
-
const st = serveState.stopped ? "err" : serveState.connected ? "ok" : "warn";
|
|
2587
|
-
const val = serveState.stopped ? "Stopped" : serveState.connected ? `Running \u2014 ${clip(serveState.daemonId || "", 16)}` : clip(serveState.activity || "Starting up\u2026", 52);
|
|
2588
|
-
drawHdrRow(inner, st, "Agents", val);
|
|
2589
|
-
}
|
|
2590
|
-
term.gray(`\u2570${"\u2500".repeat(inner)}\u256F
|
|
2591
|
-
`);
|
|
2592
|
-
}
|
|
2593
|
-
function drawHdrRow(inner, status, label, value) {
|
|
2594
|
-
const maxVal = Math.max(0, inner - 18);
|
|
2595
|
-
const clipped = clip(value, maxVal);
|
|
2596
|
-
const pad = Math.max(0, inner - 15 - clipped.length - 3);
|
|
2597
|
-
term.gray("\u2502 ");
|
|
2598
|
-
if (status === "ok") term.green("\u25CF");
|
|
2599
|
-
else if (status === "warn") term.yellow("\u25CF");
|
|
2600
|
-
else term.red("\u25CF");
|
|
2601
|
-
term(" ");
|
|
2602
|
-
term.bold(label.padEnd(10));
|
|
2603
|
-
term(clipped);
|
|
2604
|
-
term(" ".repeat(pad));
|
|
2605
|
-
term.gray(" \u2502\n");
|
|
2509
|
+
const acct = active.authenticated ? `${active.email || "signed in"} @ ${envLabel(active.apiUrl)}` : col.yellow("not signed in");
|
|
2510
|
+
const W = Math.min(process.stdout.columns || 80, 100);
|
|
2511
|
+
print(col.gray(hr(W)));
|
|
2512
|
+
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.gray(`v${CLI_VERSION}`)} ${col.gray("\u2502")} ${acct}`);
|
|
2513
|
+
print(col.gray(hr(W)));
|
|
2606
2514
|
}
|
|
2607
2515
|
function envLabel(apiUrl) {
|
|
2608
2516
|
if (!apiUrl) return "";
|
|
2609
|
-
if (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) return "
|
|
2610
|
-
if (apiUrl.includes("staging")) return "
|
|
2611
|
-
if (apiUrl.includes(".dev.")) return "
|
|
2612
|
-
if (apiUrl.includes("vendian.ai") && !apiUrl.includes(".")) return "Production";
|
|
2517
|
+
if (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) return "local";
|
|
2518
|
+
if (apiUrl.includes("staging")) return "staging";
|
|
2519
|
+
if (apiUrl.includes(".dev.")) return "dev";
|
|
2613
2520
|
return clip(apiUrl.replace(/^https?:\/\//, ""), 30);
|
|
2614
2521
|
}
|
|
2615
2522
|
var HOME_ACTIONS = [
|
|
2616
|
-
{ value: "serve", label: "
|
|
2617
|
-
{ value: "connect", label: "
|
|
2523
|
+
{ value: "serve", label: "Serve my agents", desc: "Start agents and stream their logs" },
|
|
2524
|
+
{ value: "connect", label: "Login / Switch", desc: "Sign in or switch to a different environment" },
|
|
2618
2525
|
{ value: "create", label: "Create Agent", desc: "Build a new agent from a template" },
|
|
2619
|
-
{ value: "
|
|
2620
|
-
{ value: "
|
|
2526
|
+
{ value: "run", label: "Run once", desc: "Execute one agent immediately" },
|
|
2527
|
+
{ value: "commands", label: "Commands", desc: "Show all CLI commands" },
|
|
2621
2528
|
{ value: "exit", label: "Exit", desc: "" }
|
|
2622
2529
|
];
|
|
2623
2530
|
async function showHome({ env, platform }) {
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
)
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
}).promise;
|
|
2638
|
-
if (resp.canceled) return "exit";
|
|
2639
|
-
return HOME_ACTIONS[resp.selectedIndex]?.value ?? "exit";
|
|
2531
|
+
clearScreen();
|
|
2532
|
+
print("");
|
|
2533
|
+
printHeader({ env, platform });
|
|
2534
|
+
print("");
|
|
2535
|
+
print(col.bold(" What would you like to do?"));
|
|
2536
|
+
print("");
|
|
2537
|
+
const items = HOME_ACTIONS.map((a) => {
|
|
2538
|
+
const desc = a.desc ? col.gray(` - ${a.desc}`) : "";
|
|
2539
|
+
return `${a.label.padEnd(22)}${desc}`;
|
|
2540
|
+
});
|
|
2541
|
+
const idx = await pickMenu(items, { allowBack: false });
|
|
2542
|
+
if (idx === null || idx < 0) return null;
|
|
2543
|
+
return HOME_ACTIONS[idx].value;
|
|
2640
2544
|
}
|
|
2641
2545
|
var ENDPOINTS = [
|
|
2642
2546
|
{ key: "prod", label: "Production", subtitle: "Recommended for live use" },
|
|
2643
2547
|
{ key: "staging", label: "Staging", subtitle: "Pre-release testing" },
|
|
2644
2548
|
{ key: "dev", label: "Development", subtitle: "Developer preview" },
|
|
2645
|
-
{ key: "local", label: "Local (
|
|
2549
|
+
{ key: "local", label: "Local (localhost:3000)", subtitle: "Local backend" }
|
|
2646
2550
|
];
|
|
2647
2551
|
async function showConnect({ env, platform }) {
|
|
2648
2552
|
while (true) {
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2553
|
+
clearScreen();
|
|
2554
|
+
print("");
|
|
2555
|
+
printHeader({ env, platform });
|
|
2556
|
+
print("");
|
|
2557
|
+
print(col.bold(" Login / Switch environment:"));
|
|
2558
|
+
print("");
|
|
2653
2559
|
const rows = endpointRows({ env, platform });
|
|
2654
|
-
const
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
cancelable: true,
|
|
2665
|
-
style: term.gray,
|
|
2666
|
-
selectedStyle: term.bgCyan.black.bold,
|
|
2667
|
-
leftPadding: " ",
|
|
2668
|
-
selectedLeftPadding: " \u203A "
|
|
2669
|
-
}).promise;
|
|
2670
|
-
if (resp.canceled || resp.selectedIndex === items.length - 1) return "home";
|
|
2671
|
-
if (resp.selectedIndex === items.length - 2) {
|
|
2560
|
+
const epItems = ENDPOINTS.map((ep2) => {
|
|
2561
|
+
const row = rows.find((r) => r.key === ep2.key);
|
|
2562
|
+
const badge = row?.active ? col.green(" \u25CF active") : row?.status === "signed in" ? col.cyan(" \u25CB signed in") : col.gray(` - ${ep2.subtitle}`);
|
|
2563
|
+
return `${ep2.label.padEnd(26)}${badge}`;
|
|
2564
|
+
});
|
|
2565
|
+
const allItems = [...epItems, "Custom server URL..."];
|
|
2566
|
+
const idx = await pickMenu(allItems, { allowBack: true });
|
|
2567
|
+
if (idx === null) return null;
|
|
2568
|
+
if (idx === -1) return "home";
|
|
2569
|
+
if (idx === allItems.length - 1) {
|
|
2672
2570
|
await connectCustomUrl({ env, platform });
|
|
2673
2571
|
continue;
|
|
2674
2572
|
}
|
|
2675
|
-
const ep = ENDPOINTS[
|
|
2676
|
-
|
|
2573
|
+
const ep = ENDPOINTS[idx];
|
|
2574
|
+
print("");
|
|
2677
2575
|
const outcome = await connectEndpointInteractive({ backend: ep.key, label: ep.label, env, platform });
|
|
2678
2576
|
if (outcome.promptedInTui) {
|
|
2679
2577
|
await pressEnterToContinue();
|
|
@@ -2681,74 +2579,18 @@ async function showConnect({ env, platform }) {
|
|
|
2681
2579
|
}
|
|
2682
2580
|
}
|
|
2683
2581
|
async function connectCustomUrl({ env, platform }) {
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
term.gray(" Connecting\u2026\n");
|
|
2693
|
-
const outcome = await connectEndpointInteractive({ apiUrl: url, label: url, env, platform, successLabel: "Connected successfully" });
|
|
2582
|
+
print("");
|
|
2583
|
+
print(col.bold(" Enter your server URL:"));
|
|
2584
|
+
print("");
|
|
2585
|
+
const url = await ask("URL", "https://");
|
|
2586
|
+
if (!url || url === "https://") return;
|
|
2587
|
+
print("");
|
|
2588
|
+
print(col.gray(" Connecting..."));
|
|
2589
|
+
const outcome = await connectEndpointInteractive({ apiUrl: url, label: url, env, platform });
|
|
2694
2590
|
if (outcome.promptedInTui) {
|
|
2695
2591
|
await pressEnterToContinue();
|
|
2696
2592
|
}
|
|
2697
2593
|
}
|
|
2698
|
-
async function showCreate({ env, platform }) {
|
|
2699
|
-
term.clear();
|
|
2700
|
-
drawHeader({ env, platform });
|
|
2701
|
-
term("\n");
|
|
2702
|
-
term.bold(" Create a new agent\n\n");
|
|
2703
|
-
term.gray(" Agent name: ");
|
|
2704
|
-
const name = await term.inputField({ cancelable: true, style: term.cyan }).promise;
|
|
2705
|
-
if (!name?.trim()) return "home";
|
|
2706
|
-
term("\n");
|
|
2707
|
-
term.gray(" Save it in folder: ");
|
|
2708
|
-
const dir = await term.inputField({ cancelable: true, style: term.cyan, default: "./agents" }).promise;
|
|
2709
|
-
if (dir === null) return "home";
|
|
2710
|
-
term("\n");
|
|
2711
|
-
await withOutputMode(
|
|
2712
|
-
`Creating agent: ${name.trim()}`,
|
|
2713
|
-
() => forwardToPythonVendian(["create", name.trim(), "--output-dir", dir || "./agents"], { env, platform })
|
|
2714
|
-
);
|
|
2715
|
-
return "home";
|
|
2716
|
-
}
|
|
2717
|
-
async function showRun({ env, platform }) {
|
|
2718
|
-
const candidates = findAgentDirectoryCandidates();
|
|
2719
|
-
const picked = await pickSingleAgentToRun({ env, platform, candidates });
|
|
2720
|
-
if (!picked) return "home";
|
|
2721
|
-
const workspace = await chooseCloudWorkspace({
|
|
2722
|
-
env,
|
|
2723
|
-
platform,
|
|
2724
|
-
loadingTitle: " Getting ready to run this agent\u2026",
|
|
2725
|
-
pickerTitle: " Which project should own this run?\n\n"
|
|
2726
|
-
});
|
|
2727
|
-
if (!workspace) return "home";
|
|
2728
|
-
term.clear();
|
|
2729
|
-
drawHeader({ env, platform });
|
|
2730
|
-
term("\n");
|
|
2731
|
-
term.bold(` Run ${picked.displayName}
|
|
2732
|
-
|
|
2733
|
-
`);
|
|
2734
|
-
term.gray(` Agent: ${picked.path}
|
|
2735
|
-
`);
|
|
2736
|
-
term.gray(` Project: ${workspace.label}
|
|
2737
|
-
|
|
2738
|
-
`);
|
|
2739
|
-
term.gray(" Input JSON: ");
|
|
2740
|
-
const inputJson = await term.inputField({ cancelable: true, style: term.cyan, default: "{}" }).promise;
|
|
2741
|
-
if (inputJson === null) return "home";
|
|
2742
|
-
await withOutputMode(
|
|
2743
|
-
`Running ${picked.displayName}`,
|
|
2744
|
-
() => forwardToPythonVendian(buildLocalRunArgs({
|
|
2745
|
-
agentPath: picked.path,
|
|
2746
|
-
collectionId: workspace.id,
|
|
2747
|
-
inputJson: inputJson || "{}"
|
|
2748
|
-
}), { env, platform })
|
|
2749
|
-
);
|
|
2750
|
-
return "home";
|
|
2751
|
-
}
|
|
2752
2594
|
var COMMAND_GROUPS = [
|
|
2753
2595
|
{ section: "Getting started", commands: [
|
|
2754
2596
|
{ cmd: "vendian login", desc: "Sign in and set up your computer" },
|
|
@@ -2757,13 +2599,12 @@ var COMMAND_GROUPS = [
|
|
|
2757
2599
|
{ section: "Building agents", commands: [
|
|
2758
2600
|
{ cmd: 'vendian create "My Agent" --output-dir .', desc: "Create a new agent" },
|
|
2759
2601
|
{ cmd: "vendian validate ./agents/my-agent", desc: "Check an agent for errors" },
|
|
2760
|
-
{ cmd: "vendian test ./agents/my-agent --dry-run", desc: "Test an agent without running it" },
|
|
2761
2602
|
{ cmd: "vendian models", desc: "List available AI models" }
|
|
2762
2603
|
] },
|
|
2763
2604
|
{ section: "Running agents", commands: [
|
|
2764
|
-
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one
|
|
2765
|
-
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents
|
|
2766
|
-
{ cmd: "vendian login --backend staging", desc: "Sign in to staging
|
|
2605
|
+
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one agent" },
|
|
2606
|
+
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents" },
|
|
2607
|
+
{ cmd: "vendian login --backend staging", desc: "Sign in to staging" }
|
|
2767
2608
|
] },
|
|
2768
2609
|
{ section: "Maintenance", commands: [
|
|
2769
2610
|
{ cmd: "vendian doctor", desc: "Check if everything is set up" },
|
|
@@ -2771,89 +2612,51 @@ var COMMAND_GROUPS = [
|
|
|
2771
2612
|
] }
|
|
2772
2613
|
];
|
|
2773
2614
|
async function showCommands() {
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
term.gray("\u2502");
|
|
2779
|
-
term.cyan.bold(` ${fig.arrowUp} VENDIAN All Commands`);
|
|
2780
|
-
term(" ".repeat(Math.max(0, w - 26)));
|
|
2781
|
-
term.gray("\u2502\n");
|
|
2782
|
-
term.gray(`\u2570${"\u2500".repeat(w - 2)}\u256F
|
|
2783
|
-
|
|
2784
|
-
`);
|
|
2615
|
+
clearScreen();
|
|
2616
|
+
print("");
|
|
2617
|
+
print(col.bold(` \u2191 VENDIAN v${CLI_VERSION} - All Commands`));
|
|
2618
|
+
print("");
|
|
2785
2619
|
for (const g of COMMAND_GROUPS) {
|
|
2786
|
-
|
|
2787
|
-
`);
|
|
2620
|
+
print(col.blue(col.bold(` ${g.section}`)));
|
|
2788
2621
|
for (const item of g.commands) {
|
|
2789
|
-
|
|
2790
|
-
term.cyan(item.cmd.padEnd(46));
|
|
2791
|
-
term.gray(`${item.desc}
|
|
2792
|
-
`);
|
|
2622
|
+
print(` ${col.cyan(item.cmd.padEnd(52))} ${col.gray(item.desc)}`);
|
|
2793
2623
|
}
|
|
2794
|
-
|
|
2624
|
+
print("");
|
|
2795
2625
|
}
|
|
2796
|
-
|
|
2797
|
-
await waitForKey();
|
|
2626
|
+
await pressEnterToContinue();
|
|
2798
2627
|
return "home";
|
|
2799
2628
|
}
|
|
2800
|
-
async function showServe({ env, platform }) {
|
|
2801
|
-
const candidates = findAgentDirectoryCandidates();
|
|
2802
|
-
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2803
|
-
if (!picked) return "home";
|
|
2804
|
-
const { agentsDir } = picked;
|
|
2805
|
-
const workspace = await chooseCloudWorkspace({
|
|
2806
|
-
env,
|
|
2807
|
-
platform,
|
|
2808
|
-
loadingTitle: " Getting ready to run your agents\u2026",
|
|
2809
|
-
pickerTitle: " Which project do you want to run?\n\n"
|
|
2810
|
-
});
|
|
2811
|
-
if (!workspace) return "home";
|
|
2812
|
-
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id });
|
|
2813
|
-
}
|
|
2814
2629
|
async function pickAgentsToRun({ env, platform, candidates }) {
|
|
2815
2630
|
if (candidates.length === 0) {
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
term.gray(" Folder: ");
|
|
2822
|
-
const input = await term.inputField({ cancelable: true, style: term.cyan, default: "./agents" }).promise;
|
|
2823
|
-
if (input === null) return null;
|
|
2824
|
-
return { agentsDir: input || "./agents" };
|
|
2631
|
+
clearScreen();
|
|
2632
|
+
print("");
|
|
2633
|
+
print(col.yellow(" Couldn't find any agents. Enter your agents folder path:"));
|
|
2634
|
+
const input = await ask("Folder", "./agents");
|
|
2635
|
+
return input ? { agentsDir: input } : null;
|
|
2825
2636
|
}
|
|
2826
2637
|
if (candidates.length === 1 && candidates[0].agentCount === 1) {
|
|
2827
2638
|
return { agentsDir: candidates[0].path };
|
|
2828
2639
|
}
|
|
2829
2640
|
if (candidates.length === 1) {
|
|
2830
|
-
return
|
|
2641
|
+
return pickAgentsFromFolder({ env, platform, candidate: candidates[0] });
|
|
2831
2642
|
}
|
|
2832
|
-
return
|
|
2643
|
+
return pickProject({ env, platform, candidates });
|
|
2833
2644
|
}
|
|
2834
2645
|
async function pickProject({ env, platform, candidates }) {
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
const
|
|
2847
|
-
cancelable: true,
|
|
2848
|
-
style: term.gray,
|
|
2849
|
-
selectedStyle: term.bgCyan.black.bold,
|
|
2850
|
-
leftPadding: " ",
|
|
2851
|
-
selectedLeftPadding: " \u203A "
|
|
2852
|
-
}).promise;
|
|
2853
|
-
if (resp.canceled || resp.selectedIndex === items.length - 1) return null;
|
|
2854
|
-
const chosen = candidates[resp.selectedIndex];
|
|
2646
|
+
clearScreen();
|
|
2647
|
+
printHeader({ env, platform });
|
|
2648
|
+
print("");
|
|
2649
|
+
print(col.bold(" Which project do you want to run?"));
|
|
2650
|
+
print("");
|
|
2651
|
+
const items = candidates.map((candidate) => {
|
|
2652
|
+
const name = readManifestName(candidate.absolutePath) || friendlyName(path8.basename(candidate.absolutePath) || candidate.path);
|
|
2653
|
+
return `${name.padEnd(32)} ${col.gray(`${candidate.agentCount} agent${candidate.agentCount === 1 ? "" : "s"}`)}`;
|
|
2654
|
+
});
|
|
2655
|
+
const idx = await pickMenu(items, { allowBack: true });
|
|
2656
|
+
if (idx === null || idx === -1) return null;
|
|
2657
|
+
const chosen = candidates[idx];
|
|
2855
2658
|
if (chosen.agentCount <= 1) return { agentsDir: chosen.path };
|
|
2856
|
-
return
|
|
2659
|
+
return pickAgentsFromFolder({ env, platform, candidate: chosen });
|
|
2857
2660
|
}
|
|
2858
2661
|
async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
2859
2662
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
@@ -2862,39 +2665,28 @@ async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
|
2862
2665
|
...f,
|
|
2863
2666
|
displayName: readManifestName(f.absolutePath) || friendlyName(f.name || path8.basename(f.path))
|
|
2864
2667
|
}));
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2668
|
+
clearScreen();
|
|
2669
|
+
printHeader({ env, platform });
|
|
2670
|
+
print("");
|
|
2671
|
+
print(col.bold(" Which agents do you want to start?"));
|
|
2672
|
+
print("");
|
|
2870
2673
|
const items = [
|
|
2871
|
-
`Run all ${folders.length} agents
|
|
2872
|
-
...named.map((f) => f.displayName)
|
|
2873
|
-
"\u2190 Back"
|
|
2674
|
+
col.bold(`Run all ${folders.length} agents`),
|
|
2675
|
+
...named.map((f) => f.displayName)
|
|
2874
2676
|
];
|
|
2875
|
-
const
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
leftPadding: " ",
|
|
2880
|
-
selectedLeftPadding: " \u203A "
|
|
2881
|
-
}).promise;
|
|
2882
|
-
if (resp.canceled || resp.selectedIndex === items.length - 1) return null;
|
|
2883
|
-
if (resp.selectedIndex === 0) return { agentsDir: candidate.path };
|
|
2884
|
-
return { agentsDir: named[resp.selectedIndex - 1].path };
|
|
2677
|
+
const idx = await pickMenu(items, { allowBack: true });
|
|
2678
|
+
if (idx === null || idx === -1) return null;
|
|
2679
|
+
if (idx === 0) return { agentsDir: candidate.path };
|
|
2680
|
+
return { agentsDir: named[idx - 1].path };
|
|
2885
2681
|
}
|
|
2886
2682
|
async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
2887
2683
|
if (candidates.length === 0) {
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
const input = await term.inputField({ cancelable: true, style: term.cyan, default: "./agents/my-agent" }).promise;
|
|
2895
|
-
if (input === null) return null;
|
|
2896
|
-
const agentPath = input || "./agents/my-agent";
|
|
2897
|
-
return { path: agentPath, displayName: friendlyName(path8.basename(agentPath) || "Agent") };
|
|
2684
|
+
clearScreen();
|
|
2685
|
+
print("");
|
|
2686
|
+
print(col.yellow(" Couldn't find any agents."));
|
|
2687
|
+
const input = await ask("Agent folder", "./agents/my-agent");
|
|
2688
|
+
if (!input) return null;
|
|
2689
|
+
return { path: input, displayName: friendlyName(path8.basename(input) || "Agent") };
|
|
2898
2690
|
}
|
|
2899
2691
|
const agents = candidates.flatMap((candidate) => {
|
|
2900
2692
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
@@ -2910,32 +2702,19 @@ async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
|
2910
2702
|
}];
|
|
2911
2703
|
});
|
|
2912
2704
|
if (agents.length === 1) return agents[0];
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
const
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
return agents[resp.selectedIndex];
|
|
2927
|
-
}
|
|
2928
|
-
async function chooseCloudWorkspace({
|
|
2929
|
-
env,
|
|
2930
|
-
platform,
|
|
2931
|
-
loadingTitle,
|
|
2932
|
-
pickerTitle
|
|
2933
|
-
} = {}) {
|
|
2934
|
-
term.clear();
|
|
2935
|
-
drawHeader({ env, platform });
|
|
2936
|
-
term("\n");
|
|
2937
|
-
term.bold(`${loadingTitle}
|
|
2938
|
-
|
|
2705
|
+
clearScreen();
|
|
2706
|
+
printHeader({ env, platform });
|
|
2707
|
+
print("");
|
|
2708
|
+
print(col.bold(" Which agent do you want to run?"));
|
|
2709
|
+
print("");
|
|
2710
|
+
const items = agents.map((a) => `${a.displayName.padEnd(32)} ${col.gray(a.path)}`);
|
|
2711
|
+
const idx = await pickMenu(items, { allowBack: true });
|
|
2712
|
+
if (idx === null || idx === -1) return null;
|
|
2713
|
+
return agents[idx];
|
|
2714
|
+
}
|
|
2715
|
+
async function chooseCloudWorkspace({ env, platform, loadingTitle, pickerTitle } = {}) {
|
|
2716
|
+
print("");
|
|
2717
|
+
process.stdout.write(` ${col.gray(loadingTitle || "Loading workspaces...")}
|
|
2939
2718
|
`);
|
|
2940
2719
|
let wsResult;
|
|
2941
2720
|
try {
|
|
@@ -2943,296 +2722,235 @@ async function chooseCloudWorkspace({
|
|
|
2943
2722
|
env,
|
|
2944
2723
|
platform,
|
|
2945
2724
|
onProgress: (msg) => {
|
|
2946
|
-
|
|
2947
|
-
term.eraseLine();
|
|
2948
|
-
term.gray(` ${clip(msg, 60)}
|
|
2949
|
-
`);
|
|
2725
|
+
process.stdout.write(`\r ${col.gray(clip(msg, 60))} `);
|
|
2950
2726
|
}
|
|
2951
2727
|
});
|
|
2728
|
+
process.stdout.write("\r" + " ".repeat(70) + "\r");
|
|
2952
2729
|
} catch (error) {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
`);
|
|
2956
|
-
|
|
2730
|
+
process.stdout.write("\r" + " ".repeat(70) + "\r");
|
|
2731
|
+
print(col.red(`
|
|
2732
|
+
Couldn't connect: ${errMsg(error)}`));
|
|
2733
|
+
print(col.gray(" Make sure you are signed in (choose Login / Switch from the menu)."));
|
|
2957
2734
|
await pressEnterToContinue({ grabActive: false });
|
|
2958
2735
|
return null;
|
|
2959
2736
|
}
|
|
2960
2737
|
if (!wsResult.ok) {
|
|
2961
|
-
|
|
2962
|
-
${fig.cross} ${wsResult.error}
|
|
2963
|
-
`);
|
|
2738
|
+
print(col.red(`
|
|
2739
|
+
${fig.cross} ${wsResult.error}`));
|
|
2964
2740
|
await pressEnterToContinue({ grabActive: false });
|
|
2965
2741
|
return null;
|
|
2966
2742
|
}
|
|
2967
|
-
if (wsResult.workspaces.length
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
}
|
|
2987
|
-
|
|
2743
|
+
if (wsResult.workspaces.length === 0) return null;
|
|
2744
|
+
if (wsResult.workspaces.length === 1) {
|
|
2745
|
+
const ws2 = wsResult.workspaces[0];
|
|
2746
|
+
return { id: ws2.id || "", label: workspaceLabel(ws2) };
|
|
2747
|
+
}
|
|
2748
|
+
clearScreen();
|
|
2749
|
+
print("");
|
|
2750
|
+
printHeader({ env, platform });
|
|
2751
|
+
print("");
|
|
2752
|
+
print(col.bold(` ${pickerTitle || "Choose a workspace:"}`));
|
|
2753
|
+
print("");
|
|
2754
|
+
const items = wsResult.workspaces.map((ws2) => workspaceLabel(ws2));
|
|
2755
|
+
const idx = await pickMenu(items, { allowBack: true });
|
|
2756
|
+
if (idx === null || idx === -1) return null;
|
|
2757
|
+
const ws = wsResult.workspaces[idx];
|
|
2758
|
+
return { id: ws.id || "", label: workspaceLabel(ws) };
|
|
2759
|
+
}
|
|
2760
|
+
async function showCreate({ env, platform }) {
|
|
2761
|
+
clearScreen();
|
|
2762
|
+
printHeader({ env, platform });
|
|
2763
|
+
print("");
|
|
2764
|
+
print(col.bold(" Create a new agent"));
|
|
2765
|
+
print("");
|
|
2766
|
+
const name = await ask("Agent name");
|
|
2767
|
+
if (!name?.trim()) return "home";
|
|
2768
|
+
const dir = await ask("Save it in folder", "./agents");
|
|
2769
|
+
await withOutputMode(
|
|
2770
|
+
`Creating agent: ${name.trim()}`,
|
|
2771
|
+
() => forwardToPythonVendian(["create", name.trim(), "--output-dir", dir || "./agents"], { env, platform })
|
|
2772
|
+
);
|
|
2773
|
+
return "home";
|
|
2774
|
+
}
|
|
2775
|
+
async function showRun({ env, platform }) {
|
|
2776
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2777
|
+
const picked = await pickSingleAgentToRun({ env, platform, candidates });
|
|
2778
|
+
if (!picked) return "home";
|
|
2779
|
+
const workspace = await chooseCloudWorkspace({
|
|
2780
|
+
env,
|
|
2781
|
+
platform,
|
|
2782
|
+
loadingTitle: "Getting ready to run this agent...",
|
|
2783
|
+
pickerTitle: "Which project should own this run?"
|
|
2784
|
+
});
|
|
2785
|
+
if (!workspace) return "home";
|
|
2786
|
+
const inputJson = await ask("Input JSON", "{}");
|
|
2787
|
+
await withOutputMode(
|
|
2788
|
+
`Running ${picked.displayName}`,
|
|
2789
|
+
() => forwardToPythonVendian(buildLocalRunArgs({
|
|
2790
|
+
agentPath: picked.path,
|
|
2791
|
+
collectionId: workspace.id,
|
|
2792
|
+
inputJson: inputJson || "{}"
|
|
2793
|
+
}), { env, platform })
|
|
2794
|
+
);
|
|
2795
|
+
return "home";
|
|
2796
|
+
}
|
|
2797
|
+
async function showServe({ env, platform }) {
|
|
2798
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2799
|
+
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2800
|
+
if (!picked) return "home";
|
|
2801
|
+
const { agentsDir } = picked;
|
|
2802
|
+
const workspace = await chooseCloudWorkspace({
|
|
2803
|
+
env,
|
|
2804
|
+
platform,
|
|
2805
|
+
loadingTitle: "Getting ready to run your agents...",
|
|
2806
|
+
pickerTitle: "Which project do you want to run?"
|
|
2807
|
+
});
|
|
2808
|
+
if (!workspace) return "home";
|
|
2809
|
+
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id, wsLabel: workspace.label });
|
|
2810
|
+
}
|
|
2811
|
+
async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLabel = "" }) {
|
|
2988
2812
|
let state = initialServeState();
|
|
2989
2813
|
let child = null;
|
|
2990
|
-
let startupError = "";
|
|
2991
|
-
let overlayActive = false;
|
|
2992
|
-
let overlayRedraw = null;
|
|
2993
2814
|
let ctrlCArmed = false;
|
|
2994
2815
|
let ctrlCArmTimer = null;
|
|
2995
|
-
let dashboardClosed = false;
|
|
2996
|
-
let exitPromptActive = false;
|
|
2997
|
-
let selectedIdx = 0;
|
|
2998
|
-
let pressedAgentIdx = null;
|
|
2999
2816
|
let stopRequested = false;
|
|
3000
2817
|
let stopSignalAttempt = 0;
|
|
3001
2818
|
let stopTimer = null;
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
}
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
}
|
|
3025
|
-
function daemonDebugLines(maxLines = 8) {
|
|
3026
|
-
return serveDebugEntries(state.logs, maxLines);
|
|
3027
|
-
}
|
|
3028
|
-
function redraw() {
|
|
3029
|
-
if (dashboardClosed || overlayActive) return;
|
|
3030
|
-
const W = term.width || 80;
|
|
3031
|
-
const H = term.height || 24;
|
|
3032
|
-
const agents = agentDisplayList();
|
|
3033
|
-
const activity = recentActivityLines();
|
|
3034
|
-
const diagnostics = daemonDebugLines();
|
|
3035
|
-
const viewport = computeServeDashboardViewport({
|
|
3036
|
-
termHeight: H,
|
|
3037
|
-
agentCount: agents.length,
|
|
3038
|
-
selectedIdx,
|
|
3039
|
-
activityCount: activity.length,
|
|
3040
|
-
diagnosticsCount: diagnostics.length
|
|
3041
|
-
});
|
|
3042
|
-
selectedIdx = viewport.selectedIdx;
|
|
3043
|
-
term.clear();
|
|
3044
|
-
agentRowMap.clear();
|
|
3045
|
-
drawHeader({ env, platform, serveState: state });
|
|
3046
|
-
term("\n");
|
|
3047
|
-
let currentRow = HEADER_ROWS_SERVE + 2;
|
|
3048
|
-
if (startupError) {
|
|
3049
|
-
term.red(` ${fig.cross} ${clip(startupError, W - 6)}
|
|
3050
|
-
`);
|
|
3051
|
-
} else if (stopRequested) {
|
|
3052
|
-
term.yellow(` ${fig.warning} ${clip(state.activity || "Stopping your agents\u2026", W - 6)}
|
|
3053
|
-
`);
|
|
3054
|
-
} else if (state.stopped) {
|
|
3055
|
-
term.yellow(` ${fig.warning} Your agents have stopped.
|
|
2819
|
+
let dashboardClosed = false;
|
|
2820
|
+
let logStore = null;
|
|
2821
|
+
let lastAgentOnlinePrint = 0;
|
|
2822
|
+
let agentsSeenOnline = false;
|
|
2823
|
+
let initialSetupDone = false;
|
|
2824
|
+
const W = Math.min(process.stdout.columns || 80, 100);
|
|
2825
|
+
clearScreen();
|
|
2826
|
+
setCursorVisible(false);
|
|
2827
|
+
print("");
|
|
2828
|
+
print(col.gray("\u2550".repeat(W)));
|
|
2829
|
+
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.bold("Serving agents")}`);
|
|
2830
|
+
print(` ${col.gray("Dir:")} ${agentsDir}`);
|
|
2831
|
+
print(` ${col.gray("Workspace:")} ${wsLabel || collectionId || "\u2014"}`);
|
|
2832
|
+
print(col.gray(hr(W)));
|
|
2833
|
+
print(col.gray(" S stop gracefully | ^c ^c exit"));
|
|
2834
|
+
print(col.gray(hr(W)));
|
|
2835
|
+
print("");
|
|
2836
|
+
function ts() {
|
|
2837
|
+
return formatLogTime((/* @__PURE__ */ new Date()).toISOString());
|
|
2838
|
+
}
|
|
2839
|
+
function printRun(agentName, icon, message) {
|
|
2840
|
+
const name = clip(agentName || "agent", 22).padEnd(22);
|
|
2841
|
+
process.stdout.write(`${ts()} ${col.blue("RUN ")} ${name} ${icon} ${message}
|
|
3056
2842
|
`);
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
2843
|
+
}
|
|
2844
|
+
function printAgent(agentName, statusText) {
|
|
2845
|
+
const name = clip(agentName || "agent", 22).padEnd(22);
|
|
2846
|
+
process.stdout.write(`${ts()} ${col.cyan("AGENT ")} ${name} ${statusText}
|
|
3060
2847
|
`);
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
2848
|
+
}
|
|
2849
|
+
function printDaemon(eventType, message) {
|
|
2850
|
+
const evt = clip(String(eventType || "event"), 26).padEnd(26);
|
|
2851
|
+
process.stdout.write(`${ts()} ${col.gray("DAEMON")} ${evt} ${message}
|
|
3064
2852
|
`);
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
if (
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
);
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
const
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
} else {
|
|
3086
|
-
term(` ${numStr} `);
|
|
3087
|
-
switch (ag.status.color) {
|
|
3088
|
-
case "green":
|
|
3089
|
-
term.green(ag.status.icon);
|
|
3090
|
-
break;
|
|
3091
|
-
case "brightBlue":
|
|
3092
|
-
term.brightBlue(ag.status.icon);
|
|
3093
|
-
break;
|
|
3094
|
-
case "cyan":
|
|
3095
|
-
term.cyan(ag.status.icon);
|
|
3096
|
-
break;
|
|
3097
|
-
case "yellow":
|
|
3098
|
-
term.yellow(ag.status.icon);
|
|
3099
|
-
break;
|
|
3100
|
-
case "red":
|
|
3101
|
-
term.red(ag.status.icon);
|
|
3102
|
-
break;
|
|
3103
|
-
default:
|
|
3104
|
-
term.gray(ag.status.icon);
|
|
3105
|
-
}
|
|
3106
|
-
term(` ${nameStr} `);
|
|
3107
|
-
switch (ag.status.color) {
|
|
3108
|
-
case "green":
|
|
3109
|
-
term.green(ag.status.text);
|
|
3110
|
-
break;
|
|
3111
|
-
case "brightBlue":
|
|
3112
|
-
term.brightBlue(ag.status.text);
|
|
3113
|
-
break;
|
|
3114
|
-
case "cyan":
|
|
3115
|
-
term.cyan(ag.status.text);
|
|
3116
|
-
break;
|
|
3117
|
-
case "yellow":
|
|
3118
|
-
term.yellow(ag.status.text);
|
|
3119
|
-
break;
|
|
3120
|
-
case "red":
|
|
3121
|
-
term.red(ag.status.text);
|
|
3122
|
-
break;
|
|
3123
|
-
default:
|
|
3124
|
-
term.gray(ag.status.text);
|
|
3125
|
-
}
|
|
2853
|
+
}
|
|
2854
|
+
function onServeEvent(event) {
|
|
2855
|
+
if (!event) return;
|
|
2856
|
+
const type = String(event.type || "");
|
|
2857
|
+
const agentPath = String(event.relativePath || event.path || "");
|
|
2858
|
+
const agentName = agentPath ? friendlyName(path8.basename(agentPath)) || agentPath : ".";
|
|
2859
|
+
if (type === "daemon_wake_received") return;
|
|
2860
|
+
if (type === "lease_claim_no_job") return;
|
|
2861
|
+
if (type === "dispatch_job_received" || type === "dispatch_job_acked") return;
|
|
2862
|
+
if (type === "inventory_synced") {
|
|
2863
|
+
const agents = Array.isArray(event.agents) ? event.agents : [];
|
|
2864
|
+
const hasProblems = agents.some((a) => a.status === "error" || a.status === "disabled");
|
|
2865
|
+
const allOnline = agents.length > 0 && agents.every((a) => a.status === "online");
|
|
2866
|
+
const now = Date.now();
|
|
2867
|
+
if (!hasProblems && allOnline && agentsSeenOnline && now - lastAgentOnlinePrint < 12e4) return;
|
|
2868
|
+
for (const a of agents) {
|
|
2869
|
+
const name = friendlyName(path8.basename(a.relativePath || ".")) || a.manifestName || a.relativePath || "?";
|
|
2870
|
+
if (a.status === "error") {
|
|
2871
|
+
printAgent(name, col.red(`\u2716 Error: ${a.errorMessage || ""}`));
|
|
2872
|
+
continue;
|
|
3126
2873
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
2874
|
+
if (a.status === "disabled") {
|
|
2875
|
+
printAgent(name, col.yellow(`\u26A0 Disabled: ${a.disabledReason || ""}`));
|
|
2876
|
+
continue;
|
|
2877
|
+
}
|
|
2878
|
+
if (a.status === "online") {
|
|
2879
|
+
printAgent(name, col.green("\u25CF Online"));
|
|
3133
2880
|
}
|
|
3134
2881
|
}
|
|
3135
|
-
if (
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
`);
|
|
3140
|
-
currentRow++;
|
|
3141
|
-
}
|
|
3142
|
-
if (viewport.showAgentHint) {
|
|
3143
|
-
term.gray(` Click an agent or use \u2191\u2193 + Enter to see its activity log
|
|
3144
|
-
`);
|
|
3145
|
-
currentRow++;
|
|
3146
|
-
}
|
|
3147
|
-
} else {
|
|
3148
|
-
if (viewport.showBlankAfterStatus) {
|
|
3149
|
-
term("\n");
|
|
3150
|
-
currentRow++;
|
|
2882
|
+
if (allOnline) {
|
|
2883
|
+
lastAgentOnlinePrint = now;
|
|
2884
|
+
agentsSeenOnline = true;
|
|
2885
|
+
initialSetupDone = true;
|
|
3151
2886
|
}
|
|
3152
|
-
|
|
3153
|
-
currentRow++;
|
|
2887
|
+
return;
|
|
3154
2888
|
}
|
|
3155
|
-
if (
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
`);
|
|
3160
|
-
currentRow++;
|
|
3161
|
-
for (const e of visibleActivity) {
|
|
3162
|
-
const t = formatLogTime(e.timestamp);
|
|
3163
|
-
const name = clip(e._agentName || "Agent", 20);
|
|
3164
|
-
const msg = clip(friendlyActivityLine(e), W - 32);
|
|
3165
|
-
if (e.level === "error" || e.eventType === "completion" && e.success === false) {
|
|
3166
|
-
term.gray(` ${t} `);
|
|
3167
|
-
term.red(`${name} \u2014 ${msg}
|
|
3168
|
-
`);
|
|
3169
|
-
} else if (e.eventType === "job_completed" && e.success !== false) {
|
|
3170
|
-
term.gray(` ${t} `);
|
|
3171
|
-
term.green(`${name} \u2014 ${msg}
|
|
3172
|
-
`);
|
|
3173
|
-
} else if (e.eventType === "job_started") {
|
|
3174
|
-
term.gray(` ${t} `);
|
|
3175
|
-
term.brightBlue(`${name} \u2014 ${msg}
|
|
3176
|
-
`);
|
|
3177
|
-
} else {
|
|
3178
|
-
term.gray(` ${t} ${name} \u2014 ${msg}
|
|
3179
|
-
`);
|
|
3180
|
-
}
|
|
3181
|
-
currentRow++;
|
|
3182
|
-
}
|
|
2889
|
+
if (type === "job_started") {
|
|
2890
|
+
print("");
|
|
2891
|
+
printRun(agentName, col.blue("\u25B6"), col.bold(`Started run ${shortId2(event.runId || "")}`));
|
|
2892
|
+
return;
|
|
3183
2893
|
}
|
|
3184
|
-
if (
|
|
3185
|
-
const
|
|
3186
|
-
const
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
2894
|
+
if (type === "job_completed") {
|
|
2895
|
+
const ok = event.success !== false;
|
|
2896
|
+
const errText = ok ? "" : fmtErr(event.error) || "check serve logs for details";
|
|
2897
|
+
printRun(agentName, ok ? col.green("\u2714") : col.red("\u2716"), ok ? col.green("Completed") : col.red(`Failed: ${errText}`));
|
|
2898
|
+
print("");
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
if (type === "run_log") {
|
|
2902
|
+
const lvl = String(event.level || "info");
|
|
2903
|
+
const evtType = String(event.eventType || "");
|
|
2904
|
+
const msg = String(
|
|
2905
|
+
event.message || (event.data?.label ? event.data.label : null) || (event.stepId ? `${evtType === "step_event" ? "Step" : evtType}: ${event.stepId}` : null) || evtType || ""
|
|
2906
|
+
);
|
|
2907
|
+
if (!msg || msg === "heartbeat") return;
|
|
2908
|
+
if (evtType === "start" && msg === "Run started") return;
|
|
2909
|
+
const isCompletionEvent = evtType === "completion";
|
|
2910
|
+
const icon = lvl === "error" ? col.red("\u2716") : isCompletionEvent ? event.success !== false ? col.green("\u2714") : col.red("\u2716") : col.cyan("\u2502");
|
|
2911
|
+
printRun(agentName, icon, clip(msg, 90));
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
if (type === "error" && agentPath) {
|
|
2915
|
+
printRun(agentName, col.red("\u2716"), `Error: ${fmtErr(event.error) || String(event.code || "unknown")}`);
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
if (type === "agent_prepare_started") {
|
|
2919
|
+
printAgent(agentName, col.gray("Preparing..."));
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
if (type === "agent_prepare_progress") {
|
|
2923
|
+
const msg = String(event.message || event.stage || "Preparing");
|
|
2924
|
+
printAgent(agentName, col.gray(clip(msg, 60)));
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
if (type === "agent_prepare_completed") {
|
|
2928
|
+
if (event.status === "error") {
|
|
2929
|
+
printAgent(agentName, col.red(`\u2716 Setup failed: ${fmtErr(event.error)}`));
|
|
2930
|
+
return;
|
|
2931
|
+
}
|
|
2932
|
+
if (event.status === "disabled") {
|
|
2933
|
+
printAgent(agentName, col.yellow(`\u26A0 Disabled: ${event.disabledReason || "deps not met locally"}`));
|
|
2934
|
+
return;
|
|
3202
2935
|
}
|
|
2936
|
+
printAgent(agentName, col.green("\u2714 Ready"));
|
|
2937
|
+
return;
|
|
3203
2938
|
}
|
|
3204
|
-
const
|
|
3205
|
-
if (
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
term.yellow(" Stopping your agents\u2026");
|
|
3211
|
-
if (stopSignalAttempt > 0) term.gray(" (retrying shutdown)");
|
|
3212
|
-
term("\n");
|
|
3213
|
-
} else {
|
|
3214
|
-
term.gray(" ");
|
|
3215
|
-
term.brightBlue.bold("S");
|
|
3216
|
-
term.gray(" Stop ");
|
|
3217
|
-
term.brightBlue.bold("^c ^c");
|
|
3218
|
-
term.gray(" Exit\n");
|
|
2939
|
+
const dbgArr = serveDebugEntries([event], 1);
|
|
2940
|
+
if (dbgArr.length > 0) {
|
|
2941
|
+
const dbg = dbgArr[0];
|
|
2942
|
+
if (initialSetupDone && dbg.level === "info") return;
|
|
2943
|
+
const msg = dbg.level === "error" ? col.red(dbg.message) : dbg.level === "warn" ? col.yellow(dbg.message) : col.gray(dbg.message);
|
|
2944
|
+
printDaemon(dbg.eventType, msg);
|
|
3219
2945
|
}
|
|
3220
2946
|
}
|
|
3221
|
-
term.clear();
|
|
3222
|
-
drawHeader({ env, platform, serveState: state });
|
|
3223
|
-
term("\n");
|
|
3224
|
-
term.cyan(` ${fig.dotEmpty} Getting your agents ready\u2026
|
|
3225
|
-
`);
|
|
3226
2947
|
try {
|
|
3227
|
-
|
|
3228
|
-
const logStore = createServeLogStore({ agentsDir, collectionId, env, platform });
|
|
2948
|
+
logStore = createServeLogStore({ agentsDir, collectionId, env, platform });
|
|
3229
2949
|
const historicalLogs = logStore.load();
|
|
3230
2950
|
if (historicalLogs.length > 0) {
|
|
3231
2951
|
const agentLogs = mergeAgentLogRecords(state.agentLogs, historicalLogs);
|
|
3232
2952
|
state = { ...state, agentLogs, agentRunState: { ...agentRunStateFromLogs(agentLogs, { includeRunning: false }), ...state.agentRunState } };
|
|
3233
2953
|
}
|
|
3234
|
-
state = { ...state, activity: "Preparing your agents\u2026" };
|
|
3235
|
-
redraw();
|
|
3236
2954
|
child = await spawnLocalServeEventStream({
|
|
3237
2955
|
agentsDir,
|
|
3238
2956
|
collectionId,
|
|
@@ -3240,553 +2958,222 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
3240
2958
|
platform,
|
|
3241
2959
|
onProgress: (msg) => {
|
|
3242
2960
|
if (dashboardClosed) return;
|
|
3243
|
-
|
|
3244
|
-
redraw();
|
|
2961
|
+
process.stdout.write(`\r ${col.gray(clip(msg || "Preparing...", 70))} `);
|
|
3245
2962
|
}
|
|
3246
2963
|
});
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
attachServeChild(
|
|
3250
|
-
child,
|
|
3251
|
-
(updater) => {
|
|
3252
|
-
if (dashboardClosed) return;
|
|
3253
|
-
state = updater(state);
|
|
3254
|
-
if (overlayActive && typeof overlayRedraw === "function") overlayRedraw();
|
|
3255
|
-
else redraw();
|
|
3256
|
-
},
|
|
3257
|
-
({ message } = {}) => {
|
|
3258
|
-
if (dashboardClosed) return;
|
|
3259
|
-
if (!state.stopped) state = { ...state, stopped: true, activity: message || "Stopped" };
|
|
3260
|
-
if (overlayActive && typeof overlayRedraw === "function") overlayRedraw();
|
|
3261
|
-
else redraw();
|
|
3262
|
-
},
|
|
3263
|
-
logStore
|
|
3264
|
-
);
|
|
2964
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
2965
|
+
printDaemon("serve_started", col.gray(`dir=${agentsDir}`));
|
|
3265
2966
|
} catch (error) {
|
|
3266
|
-
|
|
3267
|
-
|
|
2967
|
+
setCursorVisible(true);
|
|
2968
|
+
print(col.red(` ${fig.cross} Failed to start: ${errMsg(error)}`));
|
|
2969
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2970
|
+
return "home";
|
|
3268
2971
|
}
|
|
3269
2972
|
return new Promise((resolve) => {
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
if (dashboardClosed
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
onAttachLiveUpdates: (redrawFn) => {
|
|
3281
|
-
overlayRedraw = redrawFn;
|
|
3282
|
-
},
|
|
3283
|
-
env,
|
|
3284
|
-
platform
|
|
3285
|
-
}).then(() => {
|
|
3286
|
-
if (dashboardClosed) return;
|
|
3287
|
-
overlayActive = false;
|
|
3288
|
-
overlayRedraw = null;
|
|
3289
|
-
term.clear();
|
|
3290
|
-
redraw();
|
|
3291
|
-
});
|
|
3292
|
-
}
|
|
3293
|
-
function clearCtrlCArm() {
|
|
3294
|
-
ctrlCArmed = false;
|
|
3295
|
-
if (ctrlCArmTimer) {
|
|
3296
|
-
clearTimeout(ctrlCArmTimer);
|
|
3297
|
-
ctrlCArmTimer = null;
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
|
-
function clearStopTimer() {
|
|
3301
|
-
if (stopTimer) {
|
|
3302
|
-
clearTimeout(stopTimer);
|
|
3303
|
-
stopTimer = null;
|
|
3304
|
-
}
|
|
2973
|
+
let removeKeypress = () => {
|
|
2974
|
+
};
|
|
2975
|
+
function finish(next) {
|
|
2976
|
+
if (dashboardClosed) return;
|
|
2977
|
+
dashboardClosed = true;
|
|
2978
|
+
clearTimeout(ctrlCArmTimer);
|
|
2979
|
+
clearTimeout(stopTimer);
|
|
2980
|
+
removeKeypress();
|
|
2981
|
+
setCursorVisible(true);
|
|
2982
|
+
resolve(next);
|
|
3305
2983
|
}
|
|
3306
2984
|
function childIsRunning() {
|
|
3307
2985
|
return Boolean(child) && child.exitCode == null && child.signalCode == null;
|
|
3308
2986
|
}
|
|
3309
2987
|
function scheduleStopEscalation() {
|
|
3310
|
-
|
|
2988
|
+
clearTimeout(stopTimer);
|
|
3311
2989
|
if (!childIsRunning()) return;
|
|
3312
2990
|
stopTimer = setTimeout(() => {
|
|
3313
2991
|
if (!stopRequested || dashboardClosed || !childIsRunning()) return;
|
|
3314
2992
|
stopSignalAttempt += 1;
|
|
3315
2993
|
const signal = stopSignalForAttempt(stopSignalAttempt);
|
|
3316
|
-
state = {
|
|
3317
|
-
...state,
|
|
3318
|
-
activity: signal === "SIGTERM" ? "Still stopping your agents\u2026" : "Force stopping your agents\u2026"
|
|
3319
|
-
};
|
|
3320
2994
|
try {
|
|
3321
2995
|
child.kill(signal);
|
|
3322
2996
|
} catch {
|
|
3323
2997
|
}
|
|
3324
|
-
if (!overlayActive) redraw();
|
|
3325
2998
|
if (signal !== "SIGKILL") scheduleStopEscalation();
|
|
3326
2999
|
}, 4e3);
|
|
3327
3000
|
}
|
|
3328
3001
|
function requestStop() {
|
|
3329
|
-
if (dashboardClosed) return;
|
|
3330
|
-
if (stopRequested) return;
|
|
3002
|
+
if (dashboardClosed || stopRequested) return;
|
|
3331
3003
|
if (!childIsRunning()) {
|
|
3332
3004
|
finish("home");
|
|
3333
3005
|
return;
|
|
3334
3006
|
}
|
|
3335
3007
|
stopRequested = true;
|
|
3336
|
-
stopSignalAttempt = 0;
|
|
3337
3008
|
state = { ...state, activity: "Stopping your agents\u2026" };
|
|
3338
|
-
|
|
3009
|
+
print(col.yellow("\n Stopping your agents\u2026"));
|
|
3010
|
+
clearTimeout(ctrlCArmTimer);
|
|
3011
|
+
ctrlCArmed = false;
|
|
3339
3012
|
try {
|
|
3340
3013
|
child.kill(stopSignalForAttempt(stopSignalAttempt));
|
|
3341
3014
|
} catch {
|
|
3342
3015
|
}
|
|
3343
3016
|
scheduleStopEscalation();
|
|
3344
|
-
if (!overlayActive) redraw();
|
|
3345
3017
|
}
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
if (name === "CTRL_C") {
|
|
3361
|
-
if (ctrlCArmed) {
|
|
3362
|
-
child?.kill("SIGINT");
|
|
3363
|
-
finish("exit");
|
|
3018
|
+
if (process.stdin.isTTY) {
|
|
3019
|
+
let onKeypress = function(str, key) {
|
|
3020
|
+
if (!key) return;
|
|
3021
|
+
if (key.ctrl && key.name === "c") {
|
|
3022
|
+
if (ctrlCArmed) {
|
|
3023
|
+
child?.kill("SIGINT");
|
|
3024
|
+
finish("exit");
|
|
3025
|
+
return;
|
|
3026
|
+
}
|
|
3027
|
+
ctrlCArmed = true;
|
|
3028
|
+
process.stdout.write("\n" + col.yellow(" Press Ctrl+C again to exit.") + "\n");
|
|
3029
|
+
ctrlCArmTimer = setTimeout(() => {
|
|
3030
|
+
ctrlCArmed = false;
|
|
3031
|
+
}, 1500);
|
|
3364
3032
|
return;
|
|
3365
3033
|
}
|
|
3366
|
-
|
|
3367
|
-
if (
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
selectedIdx = clampListIndex(selectedIdx - 1, agCount);
|
|
3383
|
-
redraw();
|
|
3384
|
-
} else if (name === "DOWN" && agCount > 0) {
|
|
3385
|
-
selectedIdx = clampListIndex(selectedIdx + 1, agCount);
|
|
3386
|
-
redraw();
|
|
3387
|
-
} else if (name === "ENTER") {
|
|
3388
|
-
openAgentLog(selectedIdx);
|
|
3389
|
-
} else if (name === "s" || name === "S") {
|
|
3390
|
-
requestStop();
|
|
3391
|
-
}
|
|
3034
|
+
if (stopRequested) return;
|
|
3035
|
+
if (key.name === "s" || key.name === "S") {
|
|
3036
|
+
requestStop();
|
|
3037
|
+
}
|
|
3038
|
+
};
|
|
3039
|
+
readline.emitKeypressEvents(process.stdin);
|
|
3040
|
+
process.stdin.setRawMode(true);
|
|
3041
|
+
process.stdin.resume();
|
|
3042
|
+
process.stdin.on("keypress", onKeypress);
|
|
3043
|
+
removeKeypress = () => {
|
|
3044
|
+
process.stdin.off("keypress", onKeypress);
|
|
3045
|
+
try {
|
|
3046
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
3047
|
+
} catch {
|
|
3048
|
+
}
|
|
3049
|
+
};
|
|
3392
3050
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3051
|
+
let stdoutBuffer = "";
|
|
3052
|
+
const stderrChunks = [];
|
|
3053
|
+
child.stdout.setEncoding("utf8");
|
|
3054
|
+
child.stdout.on("data", (chunk) => {
|
|
3055
|
+
stdoutBuffer += chunk;
|
|
3056
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
3057
|
+
stdoutBuffer = lines.pop() || "";
|
|
3058
|
+
for (const line of lines) {
|
|
3059
|
+
try {
|
|
3060
|
+
const event = parseServeEventLine(line);
|
|
3061
|
+
if (event) {
|
|
3062
|
+
try {
|
|
3063
|
+
logStore?.append(event);
|
|
3064
|
+
} catch {
|
|
3065
|
+
}
|
|
3066
|
+
state = applyServeEvent(state, event);
|
|
3067
|
+
onServeEvent(event);
|
|
3068
|
+
}
|
|
3069
|
+
} catch {
|
|
3401
3070
|
}
|
|
3402
|
-
return;
|
|
3403
3071
|
}
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3072
|
+
});
|
|
3073
|
+
child.stderr.setEncoding("utf8");
|
|
3074
|
+
child.stderr.on("data", (chunk) => {
|
|
3075
|
+
const text = String(chunk).trim();
|
|
3076
|
+
if (!text) return;
|
|
3077
|
+
stderrChunks.push(text);
|
|
3078
|
+
const isNoise = /^\[local\]\s+(running|uploading|preparing)/i.test(text) || /^(Resolved|Audited|Using Python)\b/i.test(text);
|
|
3079
|
+
if (!isNoise) {
|
|
3080
|
+
printDaemon("stderr", col.yellow(clip(text, 120)));
|
|
3081
|
+
}
|
|
3082
|
+
state = applyServeEvent(state, {
|
|
3083
|
+
type: "daemon_stderr",
|
|
3084
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3085
|
+
message: text
|
|
3086
|
+
});
|
|
3087
|
+
});
|
|
3088
|
+
child.on("error", (error) => {
|
|
3089
|
+
print(col.red(` Error: ${errMsg(error)}`));
|
|
3090
|
+
});
|
|
3091
|
+
child.once("exit", () => {
|
|
3092
|
+
if (stopRequested) {
|
|
3093
|
+
finish("home");
|
|
3408
3094
|
return;
|
|
3409
3095
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3096
|
+
const stderr = stderrChunks.join("\n");
|
|
3097
|
+
const message = serveProcessExitMessage({ stderr, code: child.exitCode, signal: child.signalCode });
|
|
3098
|
+
if (message) {
|
|
3099
|
+
print(col.yellow(`
|
|
3100
|
+
${message}`));
|
|
3101
|
+
} else {
|
|
3102
|
+
print(col.gray("\n Agents stopped."));
|
|
3416
3103
|
}
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
if (stopRequested) {
|
|
3428
|
-
finish("home");
|
|
3429
|
-
return;
|
|
3430
|
-
}
|
|
3431
|
-
setTimeout(() => {
|
|
3432
|
-
if (dashboardClosed || overlayActive) return;
|
|
3433
|
-
exitPromptActive = true;
|
|
3434
|
-
detachDashboardHandlers();
|
|
3435
|
-
redraw();
|
|
3436
|
-
term("\n");
|
|
3437
|
-
term.gray(" Your agents have stopped. Press any key to go back\u2026");
|
|
3438
|
-
term.once("key", () => finish("home"));
|
|
3439
|
-
}, 600);
|
|
3104
|
+
try {
|
|
3105
|
+
logStore?.compact();
|
|
3106
|
+
} catch {
|
|
3107
|
+
}
|
|
3108
|
+
removeKeypress();
|
|
3109
|
+
process.stdout.write("\n Press Enter to return to menu... ");
|
|
3110
|
+
const rl = makeRl();
|
|
3111
|
+
rl.once("line", () => {
|
|
3112
|
+
rl.close();
|
|
3113
|
+
finish("home");
|
|
3440
3114
|
});
|
|
3441
|
-
|
|
3115
|
+
rl.once("close", () => finish("home"));
|
|
3116
|
+
});
|
|
3442
3117
|
});
|
|
3443
3118
|
}
|
|
3444
|
-
async function
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
function draw() {
|
|
3455
|
-
if (done) return;
|
|
3456
|
-
const curState = liveState();
|
|
3457
|
-
const logs = currentLogs();
|
|
3458
|
-
const agentObj = curState.agents.find((a) => (a.relativePath || ".") === agent.path);
|
|
3459
|
-
const runtime = agentObj ? agentRuntimeStatus(agentObj, curState.agentRunState) : { status: "unknown", label: "offline" };
|
|
3460
|
-
const status = friendlyStatus(runtime.status);
|
|
3461
|
-
const W = term.width || 80;
|
|
3462
|
-
if (autoScroll || scrollOffset > Math.max(0, logs.length - visibleCount)) {
|
|
3463
|
-
scrollOffset = Math.max(0, logs.length - visibleCount);
|
|
3464
|
-
autoScroll = logs.length > 0;
|
|
3465
|
-
}
|
|
3466
|
-
term.clear();
|
|
3467
|
-
drawHeader({ env, platform, serveState: curState });
|
|
3468
|
-
term("\n");
|
|
3469
|
-
term.bold(` ${agent.name}
|
|
3470
|
-
`);
|
|
3471
|
-
term.gray(" ");
|
|
3472
|
-
switch (status.color) {
|
|
3473
|
-
case "green":
|
|
3474
|
-
term.green(`${status.icon} ${status.text}`);
|
|
3119
|
+
async function mainLoop({ env, platform }) {
|
|
3120
|
+
let screen = "home";
|
|
3121
|
+
while (true) {
|
|
3122
|
+
let next;
|
|
3123
|
+
switch (screen) {
|
|
3124
|
+
case "home":
|
|
3125
|
+
next = await showHome({ env, platform });
|
|
3126
|
+
break;
|
|
3127
|
+
case "connect":
|
|
3128
|
+
next = await showConnect({ env, platform });
|
|
3475
3129
|
break;
|
|
3476
|
-
case "
|
|
3477
|
-
|
|
3130
|
+
case "create":
|
|
3131
|
+
next = await showCreate({ env, platform });
|
|
3478
3132
|
break;
|
|
3479
|
-
case "
|
|
3480
|
-
|
|
3133
|
+
case "run":
|
|
3134
|
+
next = await showRun({ env, platform });
|
|
3481
3135
|
break;
|
|
3482
|
-
case "
|
|
3483
|
-
|
|
3136
|
+
case "serve":
|
|
3137
|
+
next = await showServe({ env, platform });
|
|
3138
|
+
break;
|
|
3139
|
+
case "commands":
|
|
3140
|
+
next = await showCommands();
|
|
3484
3141
|
break;
|
|
3485
3142
|
default:
|
|
3486
|
-
|
|
3487
|
-
}
|
|
3488
|
-
term("\n\n");
|
|
3489
|
-
if (logs.length === 0) {
|
|
3490
|
-
term.gray(" No activity yet \u2014 logs appear here as the agent runs.\n");
|
|
3491
|
-
} else {
|
|
3492
|
-
const eff = Math.min(scrollOffset, Math.max(0, logs.length - visibleCount));
|
|
3493
|
-
const visible = logs.slice(eff, eff + visibleCount);
|
|
3494
|
-
const msgW = Math.max(30, W - 16);
|
|
3495
|
-
for (const entry of visible) {
|
|
3496
|
-
const t = formatLogTime(entry.timestamp);
|
|
3497
|
-
const text = clip(formatAgentLogEntry(entry), msgW);
|
|
3498
|
-
term.gray(` ${t} `);
|
|
3499
|
-
if (entry.level === "error" || entry.eventType === "completion" && entry.success === false) {
|
|
3500
|
-
term.red(`\u2716 ${text}
|
|
3501
|
-
`);
|
|
3502
|
-
} else if (entry.eventType === "job_completed" && entry.success !== false) {
|
|
3503
|
-
term.green(`\u2714 ${text}
|
|
3504
|
-
`);
|
|
3505
|
-
} else if (entry.eventType === "job_started") {
|
|
3506
|
-
term.brightBlue(`\u25B6 ${text}
|
|
3507
|
-
`);
|
|
3508
|
-
} else if (entry.eventType === "progress") {
|
|
3509
|
-
term.cyan(`\u2192 ${text}
|
|
3510
|
-
`);
|
|
3511
|
-
} else {
|
|
3512
|
-
term.gray(`\u2014 ${text}
|
|
3513
|
-
`);
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
|
-
if (logs.length > visibleCount) {
|
|
3517
|
-
const eff2 = Math.min(eff, Math.max(0, logs.length - visibleCount));
|
|
3518
|
-
term.gray(`
|
|
3519
|
-
${eff2 + 1}\u2013${eff2 + visible.length} of ${logs.length}`);
|
|
3520
|
-
if (autoScroll) term.cyan(" \u2193 live");
|
|
3521
|
-
term("\n");
|
|
3522
|
-
}
|
|
3523
|
-
}
|
|
3524
|
-
term.moveTo(1, Math.max(H - 1, 6));
|
|
3525
|
-
term.gray(" ");
|
|
3526
|
-
term.brightBlue.bold("\u2191\u2193");
|
|
3527
|
-
term.gray("/PgUp/PgDn scroll ");
|
|
3528
|
-
if (!autoScroll) {
|
|
3529
|
-
term.brightBlue.bold("End");
|
|
3530
|
-
term.gray(" resume live ");
|
|
3531
|
-
}
|
|
3532
|
-
term.brightBlue.bold("Esc");
|
|
3533
|
-
term.gray(" back\n");
|
|
3534
|
-
}
|
|
3535
|
-
if (typeof onAttachLiveUpdates === "function") {
|
|
3536
|
-
onAttachLiveUpdates(() => draw());
|
|
3537
|
-
}
|
|
3538
|
-
draw();
|
|
3539
|
-
await new Promise((resolve) => {
|
|
3540
|
-
function handler(name) {
|
|
3541
|
-
if (done) return;
|
|
3542
|
-
const logs = currentLogs();
|
|
3543
|
-
const maxOff = Math.max(0, logs.length - visibleCount);
|
|
3544
|
-
if (name === "ESCAPE") {
|
|
3545
|
-
done = true;
|
|
3546
|
-
term.off("key", handler);
|
|
3547
|
-
resolve();
|
|
3548
|
-
} else if (name === "UP") {
|
|
3549
|
-
autoScroll = false;
|
|
3550
|
-
scrollOffset = Math.max(0, scrollOffset - 1);
|
|
3551
|
-
draw();
|
|
3552
|
-
} else if (name === "DOWN") {
|
|
3553
|
-
scrollOffset = Math.min(maxOff, scrollOffset + 1);
|
|
3554
|
-
if (scrollOffset >= maxOff) autoScroll = true;
|
|
3555
|
-
draw();
|
|
3556
|
-
} else if (name === "PAGE_UP") {
|
|
3557
|
-
autoScroll = false;
|
|
3558
|
-
scrollOffset = Math.max(0, scrollOffset - Math.max(1, visibleCount - 1));
|
|
3559
|
-
draw();
|
|
3560
|
-
} else if (name === "PAGE_DOWN") {
|
|
3561
|
-
scrollOffset = Math.min(maxOff, scrollOffset + Math.max(1, visibleCount - 1));
|
|
3562
|
-
if (scrollOffset >= maxOff) autoScroll = true;
|
|
3563
|
-
draw();
|
|
3564
|
-
} else if (name === "END" || name === "g") {
|
|
3565
|
-
autoScroll = true;
|
|
3566
|
-
scrollOffset = maxOff;
|
|
3567
|
-
draw();
|
|
3568
|
-
}
|
|
3569
|
-
}
|
|
3570
|
-
term.on("key", handler);
|
|
3571
|
-
});
|
|
3572
|
-
}
|
|
3573
|
-
function friendlyStatus(status) {
|
|
3574
|
-
switch (status) {
|
|
3575
|
-
case "running":
|
|
3576
|
-
return { icon: "\u25B6", text: "Running a job right now", color: "brightBlue" };
|
|
3577
|
-
case "preparing":
|
|
3578
|
-
return { icon: "\u25CB", text: "Getting ready\u2026", color: "cyan" };
|
|
3579
|
-
case "ready":
|
|
3580
|
-
case "completed":
|
|
3581
|
-
return { icon: "\u2714", text: "Ready and waiting", color: "green" };
|
|
3582
|
-
case "disabled":
|
|
3583
|
-
return { icon: "\u26A0", text: "Needs setup to run locally", color: "yellow" };
|
|
3584
|
-
case "error":
|
|
3585
|
-
return { icon: "\u2716", text: "Something went wrong", color: "red" };
|
|
3586
|
-
default:
|
|
3587
|
-
return { icon: "\u25CB", text: "Starting up\u2026", color: "gray" };
|
|
3588
|
-
}
|
|
3589
|
-
}
|
|
3590
|
-
function friendlyActivityLine(entry) {
|
|
3591
|
-
switch (entry.eventType) {
|
|
3592
|
-
case "job_started":
|
|
3593
|
-
return `Started a new job`;
|
|
3594
|
-
case "job_completed":
|
|
3595
|
-
return entry.success === false ? `Job failed \u2014 ${fmtErr(entry.error) || entry.message || "unknown error"}` : `Job completed successfully`;
|
|
3596
|
-
case "error":
|
|
3597
|
-
return `Error \u2014 ${entry.message || fmtErr(entry.error) || "unknown"}`;
|
|
3598
|
-
case "log":
|
|
3599
|
-
return entry.message || "Activity";
|
|
3600
|
-
case "completion":
|
|
3601
|
-
return entry.success ? "Finished successfully" : `Failed \u2014 ${fmtErr(entry.error) || "error"}`;
|
|
3602
|
-
case "step_event":
|
|
3603
|
-
return `Step ${entry.stepId || ""}${entry.message ? ": " + entry.message : ""}`;
|
|
3604
|
-
case "progress": {
|
|
3605
|
-
const p = entry.current != null && entry.total != null ? ` (${entry.current}/${entry.total})` : "";
|
|
3606
|
-
return `${entry.message || "Working"}${p}`;
|
|
3607
|
-
}
|
|
3608
|
-
default:
|
|
3609
|
-
return entry.message || entry.eventType || "Activity";
|
|
3610
|
-
}
|
|
3611
|
-
}
|
|
3612
|
-
function friendlyName(raw) {
|
|
3613
|
-
return String(raw || "").replace(/.*[\\/]/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim() || raw;
|
|
3614
|
-
}
|
|
3615
|
-
function readManifestName(absoluteFolderPath) {
|
|
3616
|
-
if (!absoluteFolderPath) return null;
|
|
3617
|
-
for (const fname of ["manifest.yaml", "manifest.yml"]) {
|
|
3618
|
-
try {
|
|
3619
|
-
const content = fs11.readFileSync(path8.join(absoluteFolderPath, fname), "utf8");
|
|
3620
|
-
const m = content.match(/^name\s*:\s*['"]?([^'"\n#]+?)['"]?\s*$/m);
|
|
3621
|
-
if (m?.[1]) return m[1].trim();
|
|
3622
|
-
} catch {
|
|
3143
|
+
next = "exit";
|
|
3623
3144
|
}
|
|
3145
|
+
if (!next || next === "exit") break;
|
|
3146
|
+
screen = next;
|
|
3624
3147
|
}
|
|
3625
|
-
return null;
|
|
3626
3148
|
}
|
|
3627
|
-
function
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
buffer = lines.pop() || "";
|
|
3635
|
-
const events = [];
|
|
3636
|
-
for (const line of lines) {
|
|
3637
|
-
try {
|
|
3638
|
-
const event = parseServeEventLine(line);
|
|
3639
|
-
if (event) {
|
|
3640
|
-
try {
|
|
3641
|
-
logStore?.append(event);
|
|
3642
|
-
} catch {
|
|
3643
|
-
}
|
|
3644
|
-
events.push(event);
|
|
3645
|
-
}
|
|
3646
|
-
} catch {
|
|
3647
|
-
}
|
|
3648
|
-
}
|
|
3649
|
-
if (events.length > 0) onUpdate((cur) => events.reduce((s, e) => applyServeEvent(s, e), cur));
|
|
3650
|
-
});
|
|
3651
|
-
child.stderr.setEncoding("utf8");
|
|
3652
|
-
child.stderr.on("data", (chunk) => {
|
|
3653
|
-
const text = String(chunk).trim();
|
|
3654
|
-
if (text) {
|
|
3655
|
-
stderrChunks.push(text);
|
|
3656
|
-
onUpdate((cur) => applyServeEvent(cur, {
|
|
3657
|
-
type: "daemon_stderr",
|
|
3658
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3659
|
-
message: text
|
|
3660
|
-
}));
|
|
3661
|
-
}
|
|
3662
|
-
});
|
|
3663
|
-
child.on("error", (error) => {
|
|
3664
|
-
onUpdate((cur) => ({ ...cur, errors: [...cur.errors, { scope: "daemon", message: errMsg(error) }].slice(-20) }));
|
|
3665
|
-
});
|
|
3666
|
-
child.on("exit", (code, signal) => {
|
|
3667
|
-
const stderr = stderrChunks.join("\n");
|
|
3668
|
-
const stderrTail = serveProcessExitStderrTail(stderr);
|
|
3669
|
-
const message = serveProcessExitMessage({ stderr, code, signal });
|
|
3670
|
-
if (message) {
|
|
3671
|
-
onUpdate((cur) => {
|
|
3672
|
-
const event = { type: "process_exit", timestamp: (/* @__PURE__ */ new Date()).toISOString(), code, signal, message, stderrTail, relativePath: cur.currentJob?.relativePath || "", runId: cur.currentJob?.runId || "" };
|
|
3673
|
-
try {
|
|
3674
|
-
logStore?.append(event);
|
|
3675
|
-
} catch {
|
|
3676
|
-
}
|
|
3677
|
-
try {
|
|
3678
|
-
logStore?.compact();
|
|
3679
|
-
} catch {
|
|
3680
|
-
}
|
|
3681
|
-
return applyServeEvent(cur, event);
|
|
3682
|
-
});
|
|
3683
|
-
}
|
|
3684
|
-
try {
|
|
3685
|
-
logStore?.compact();
|
|
3686
|
-
} catch {
|
|
3687
|
-
}
|
|
3688
|
-
onExit({ code, signal, message });
|
|
3689
|
-
});
|
|
3690
|
-
}
|
|
3691
|
-
async function withOutputMode(label, fn) {
|
|
3692
|
-
term.fullscreen(false);
|
|
3693
|
-
setCursorVisible(true);
|
|
3694
|
-
term.grabInput(false);
|
|
3695
|
-
process.stdout.write(`
|
|
3696
|
-
${label}
|
|
3697
|
-
${"\u2500".repeat(50)}
|
|
3698
|
-
|
|
3699
|
-
`);
|
|
3700
|
-
try {
|
|
3701
|
-
await fn();
|
|
3702
|
-
} catch (error) {
|
|
3703
|
-
process.stdout.write(`
|
|
3704
|
-
Something went wrong: ${errMsg(error)}
|
|
3149
|
+
async function runTui({
|
|
3150
|
+
env = process.env,
|
|
3151
|
+
platform = process.platform,
|
|
3152
|
+
output = process.stdout
|
|
3153
|
+
} = {}) {
|
|
3154
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3155
|
+
output.write(`${helpText()}
|
|
3705
3156
|
`);
|
|
3706
|
-
}
|
|
3707
|
-
await pressEnterToContinue({ grabActive: false });
|
|
3708
|
-
term.fullscreen(true);
|
|
3709
|
-
setCursorVisible(false);
|
|
3710
|
-
term.grabInput({ mouse: "motion" });
|
|
3711
|
-
}
|
|
3712
|
-
function waitForKey(keys = null) {
|
|
3713
|
-
return new Promise((resolve) => {
|
|
3714
|
-
function handler(name) {
|
|
3715
|
-
if (!keys || keys.includes(name)) {
|
|
3716
|
-
term.off("key", handler);
|
|
3717
|
-
resolve(name);
|
|
3718
|
-
}
|
|
3719
|
-
}
|
|
3720
|
-
term.on("key", handler);
|
|
3721
|
-
});
|
|
3722
|
-
}
|
|
3723
|
-
function setCursorVisible(visible) {
|
|
3724
|
-
if (typeof term?.hideCursor === "function") {
|
|
3725
|
-
term.hideCursor(!visible);
|
|
3726
3157
|
return;
|
|
3727
3158
|
}
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
return;
|
|
3731
|
-
}
|
|
3732
|
-
if (!visible && typeof term?.hideCursor === "function") {
|
|
3733
|
-
term.hideCursor();
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
async function pressEnterToContinue({ grabActive = true } = {}) {
|
|
3737
|
-
if (grabActive) {
|
|
3738
|
-
term("\n Press any key to continue\u2026");
|
|
3739
|
-
await waitForKey();
|
|
3740
|
-
return;
|
|
3741
|
-
}
|
|
3742
|
-
process.stdout.write("\n Press Enter to continue\u2026 ");
|
|
3743
|
-
return new Promise((resolve) => {
|
|
3744
|
-
const rl = readlinePromises.createInterface({ input: process.stdin });
|
|
3745
|
-
rl.once("line", () => {
|
|
3746
|
-
rl.close();
|
|
3747
|
-
resolve();
|
|
3748
|
-
});
|
|
3749
|
-
rl.once("close", resolve);
|
|
3750
|
-
});
|
|
3751
|
-
}
|
|
3752
|
-
function formatAgentLogEntry(entry) {
|
|
3753
|
-
if (!entry) return "";
|
|
3754
|
-
switch (entry.eventType) {
|
|
3755
|
-
case "job_started":
|
|
3756
|
-
return entry.message || `Started ${entry.jobType || "job"}`;
|
|
3757
|
-
case "job_completed":
|
|
3758
|
-
return entry.success === false ? `Failed: ${fmtErr(entry.error) || entry.message || "error"}` : entry.message || "Completed";
|
|
3759
|
-
case "error":
|
|
3760
|
-
return `Error: ${entry.message || fmtErr(entry.error) || "unknown"}`;
|
|
3761
|
-
case "log":
|
|
3762
|
-
return entry.message || "";
|
|
3763
|
-
case "progress": {
|
|
3764
|
-
const p = entry.current != null && entry.total != null ? `${entry.current}/${entry.total} ` : "";
|
|
3765
|
-
return `${p}${entry.message || "progress"}`;
|
|
3766
|
-
}
|
|
3767
|
-
case "completion":
|
|
3768
|
-
return entry.success ? "Completed" : `Failed: ${fmtErr(entry.error) || "error"}`;
|
|
3769
|
-
case "step_event":
|
|
3770
|
-
return `Step ${entry.stepId || ""}${entry.message ? ": " + entry.message : ""}`;
|
|
3771
|
-
default:
|
|
3772
|
-
return entry.message || entry.eventType || "event";
|
|
3773
|
-
}
|
|
3774
|
-
}
|
|
3775
|
-
function fmtErr(error) {
|
|
3776
|
-
if (!error) return "";
|
|
3777
|
-
return typeof error === "object" ? error.message || "error" : String(error);
|
|
3159
|
+
await refreshInteractiveManagedRuntime({ env, platform, output });
|
|
3160
|
+
await mainLoop({ env, platform });
|
|
3778
3161
|
}
|
|
3779
|
-
function
|
|
3780
|
-
|
|
3162
|
+
async function refreshInteractiveManagedRuntime({
|
|
3163
|
+
env = process.env,
|
|
3164
|
+
platform = process.platform,
|
|
3165
|
+
output = process.stderr,
|
|
3166
|
+
refreshPackageAccess = refreshPackageAccessFromCloudAuth,
|
|
3167
|
+
autoUpdate = maybeAutoUpdateManagedEnv
|
|
3168
|
+
} = {}) {
|
|
3169
|
+
if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") return false;
|
|
3781
3170
|
try {
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3171
|
+
await refreshPackageAccess({ env, platform });
|
|
3172
|
+
} catch (error) {
|
|
3173
|
+
output.write(`[vendian] Package access refresh failed; continuing. ${errMsg(error)}
|
|
3174
|
+
`);
|
|
3786
3175
|
}
|
|
3787
|
-
}
|
|
3788
|
-
function pad2(n) {
|
|
3789
|
-
return String(n).padStart(2, "0");
|
|
3176
|
+
return autoUpdate({ env, platform, force: true });
|
|
3790
3177
|
}
|
|
3791
3178
|
function endpointRows({ env = process.env, platform = process.platform } = {}) {
|
|
3792
3179
|
return ENDPOINTS.map((ep) => {
|
|
@@ -3831,15 +3218,12 @@ async function connectEndpointInteractive({
|
|
|
3831
3218
|
const status = cloudAuthStatus({ backend, apiUrl, env, platform });
|
|
3832
3219
|
const destination = label || envLabel(status.apiUrl);
|
|
3833
3220
|
if (status.authenticated) {
|
|
3834
|
-
|
|
3835
|
-
`);
|
|
3221
|
+
print(col.gray(` Connecting to ${destination}...`));
|
|
3836
3222
|
try {
|
|
3837
3223
|
await switchOrLoginEndpoint({ backend, apiUrl, env, platform, setupFn, activateFn });
|
|
3838
|
-
|
|
3839
|
-
`);
|
|
3224
|
+
print(col.green(` ${fig.check} ${successLabel || `Signed in to ${destination} successfully`}`));
|
|
3840
3225
|
} catch (error) {
|
|
3841
|
-
|
|
3842
|
-
`);
|
|
3226
|
+
print(col.red(` ${endpointErrorStatus(error)}`));
|
|
3843
3227
|
}
|
|
3844
3228
|
return { promptedInTui: true };
|
|
3845
3229
|
}
|
|
@@ -3869,26 +3253,15 @@ function buildLocalRunArgs({
|
|
|
3869
3253
|
inputJson || "{}"
|
|
3870
3254
|
];
|
|
3871
3255
|
}
|
|
3872
|
-
function
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
|
|
3877
|
-
const stale = !Number.isFinite(lastUpdate) || now - lastUpdate > 24 * 60 * 60 * 1e3;
|
|
3878
|
-
return {
|
|
3879
|
-
installed,
|
|
3880
|
-
detail: installed ? venvPath : "Run vendian login to prepare it",
|
|
3881
|
-
updateHint: installed && stale ? "Run vendian update to refresh packages" : ""
|
|
3882
|
-
};
|
|
3883
|
-
}
|
|
3884
|
-
function packageAccessSummary({ env = process.env, platform = process.platform } = {}) {
|
|
3885
|
-
const registry = registryConfig(loadConfig(env, platform), env, platform);
|
|
3886
|
-
return { configured: Boolean(registry.token), source: registry.tokenSource || "local config" };
|
|
3256
|
+
function stopSignalForAttempt(attempt = 0) {
|
|
3257
|
+
if (!Number.isFinite(attempt) || attempt <= 0) return "SIGINT";
|
|
3258
|
+
if (attempt === 1) return "SIGTERM";
|
|
3259
|
+
return "SIGKILL";
|
|
3887
3260
|
}
|
|
3888
3261
|
function helpText() {
|
|
3889
3262
|
return [
|
|
3890
3263
|
"",
|
|
3891
|
-
`
|
|
3264
|
+
` \u2191 VENDIAN CLI v${CLI_VERSION}`,
|
|
3892
3265
|
"",
|
|
3893
3266
|
" Usage:",
|
|
3894
3267
|
" vendian Open the interactive shell",
|
|
@@ -3904,7 +3277,6 @@ function helpText() {
|
|
|
3904
3277
|
" vendian init --output-dir ./agents",
|
|
3905
3278
|
' vendian create "My Agent" --output-dir ./agents',
|
|
3906
3279
|
" vendian validate ./agents/my-agent --runtime",
|
|
3907
|
-
" vendian test ./agents/my-agent --dry-run --mock-credentials",
|
|
3908
3280
|
" vendian models",
|
|
3909
3281
|
" vendian cloud local serve --agents-dir ./agents",
|
|
3910
3282
|
" vendian doctor",
|
|
@@ -3914,13 +3286,48 @@ function helpText() {
|
|
|
3914
3286
|
""
|
|
3915
3287
|
].join("\n");
|
|
3916
3288
|
}
|
|
3289
|
+
function friendlyName(raw) {
|
|
3290
|
+
return String(raw || "").replace(/.*[\\/]/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim() || String(raw || "");
|
|
3291
|
+
}
|
|
3292
|
+
function readManifestName(absoluteFolderPath) {
|
|
3293
|
+
if (!absoluteFolderPath) return null;
|
|
3294
|
+
for (const fname of ["manifest.yaml", "manifest.yml"]) {
|
|
3295
|
+
try {
|
|
3296
|
+
const content = fs11.readFileSync(path8.join(absoluteFolderPath, fname), "utf8");
|
|
3297
|
+
const m = content.match(/^name\s*:\s*['"]?([^'"\n#]+?)['"]?\s*$/m);
|
|
3298
|
+
if (m?.[1]) return m[1].trim();
|
|
3299
|
+
} catch {
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
return null;
|
|
3303
|
+
}
|
|
3917
3304
|
function clip(value, length) {
|
|
3918
3305
|
const text = String(value || "");
|
|
3919
|
-
return text.length <= length ? text : `${text.slice(0, Math.max(0, length - 1))}
|
|
3306
|
+
return text.length <= length ? text : `${text.slice(0, Math.max(0, length - 1))}...`;
|
|
3920
3307
|
}
|
|
3921
3308
|
function errMsg(error) {
|
|
3922
3309
|
return error && typeof error.message === "string" ? error.message : String(error);
|
|
3923
3310
|
}
|
|
3311
|
+
function fmtErr(error) {
|
|
3312
|
+
if (!error) return "";
|
|
3313
|
+
return typeof error === "object" ? error.message || "error" : String(error);
|
|
3314
|
+
}
|
|
3315
|
+
function shortId2(value) {
|
|
3316
|
+
const text = String(value || "");
|
|
3317
|
+
return text ? text.slice(0, 8) : "";
|
|
3318
|
+
}
|
|
3319
|
+
function formatLogTime(ts) {
|
|
3320
|
+
if (!ts) return " ";
|
|
3321
|
+
try {
|
|
3322
|
+
const d = new Date(ts);
|
|
3323
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
3324
|
+
} catch {
|
|
3325
|
+
return " ";
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
function pad2(n) {
|
|
3329
|
+
return String(n).padStart(2, "0");
|
|
3330
|
+
}
|
|
3924
3331
|
|
|
3925
3332
|
// src/main.js
|
|
3926
3333
|
function printHelp() {
|