@vendian/cli 0.0.31 → 0.0.32
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 +558 -1241
- 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.32" : 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,152 @@ 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
|
-
output = process.stdout
|
|
2488
|
-
} = {}) {
|
|
2489
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2490
|
-
output.write(`${helpText()}
|
|
2491
|
-
`);
|
|
2492
|
-
return;
|
|
2493
|
-
}
|
|
2494
|
-
await refreshInteractiveManagedRuntime({ env, platform, output });
|
|
2495
|
-
await loadTerm();
|
|
2496
|
-
term.fullscreen(true);
|
|
2497
|
-
setCursorVisible(false);
|
|
2498
|
-
term.grabInput({ mouse: "motion" });
|
|
2499
|
-
const cleanup = () => {
|
|
2500
|
-
try {
|
|
2501
|
-
term.grabInput(false);
|
|
2502
|
-
setCursorVisible(true);
|
|
2503
|
-
term.fullscreen(false);
|
|
2504
|
-
} catch {
|
|
2505
|
-
}
|
|
2506
|
-
};
|
|
2507
|
-
process.once("exit", cleanup);
|
|
2508
|
-
try {
|
|
2509
|
-
await mainLoop({ env, platform });
|
|
2510
|
-
} finally {
|
|
2511
|
-
process.off("exit", cleanup);
|
|
2512
|
-
cleanup();
|
|
2513
|
-
}
|
|
2386
|
+
function setCursorVisible(visible) {
|
|
2387
|
+
if (!process.stdout.isTTY) return;
|
|
2388
|
+
process.stdout.write(visible ? "\x1B[?25h" : "\x1B[?25l");
|
|
2514
2389
|
}
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2390
|
+
function makeRl() {
|
|
2391
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
2392
|
+
}
|
|
2393
|
+
async function ask(question, defaultValue = "") {
|
|
2394
|
+
const rl = makeRl();
|
|
2395
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
2396
|
+
return new Promise((resolve) => {
|
|
2397
|
+
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
2398
|
+
rl.close();
|
|
2399
|
+
resolve(answer.trim() || defaultValue);
|
|
2400
|
+
});
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
async function pressEnterToContinue({ grabActive = true } = {}) {
|
|
2404
|
+
return new Promise((resolve) => {
|
|
2405
|
+
const rl = makeRl();
|
|
2406
|
+
process.stdout.write("\n Press Enter to continue\u2026 ");
|
|
2407
|
+
rl.once("line", () => {
|
|
2408
|
+
rl.close();
|
|
2409
|
+
resolve();
|
|
2410
|
+
});
|
|
2411
|
+
rl.once("close", resolve);
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
async function withOutputMode(label, fn) {
|
|
2415
|
+
setCursorVisible(true);
|
|
2416
|
+
print("");
|
|
2417
|
+
print(` ${label}`);
|
|
2418
|
+
print(` ${"\u2500".repeat(50)}`);
|
|
2419
|
+
print("");
|
|
2523
2420
|
try {
|
|
2524
|
-
await
|
|
2421
|
+
await fn();
|
|
2525
2422
|
} 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;
|
|
2423
|
+
print(`
|
|
2424
|
+
Something went wrong: ${errMsg(error)}`);
|
|
2559
2425
|
}
|
|
2426
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2560
2427
|
}
|
|
2561
|
-
function
|
|
2428
|
+
function printHeader({ env, platform } = {}) {
|
|
2562
2429
|
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");
|
|
2430
|
+
const acct = active.authenticated ? `${active.email || "signed in"} @ ${envLabel(active.apiUrl)}` : col.yellow("not signed in");
|
|
2431
|
+
const W = Math.min(process.stdout.columns || 80, 100);
|
|
2432
|
+
print(col.gray(hr(W)));
|
|
2433
|
+
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.gray(`v${CLI_VERSION}`)} ${col.gray("\u2502")} ${acct}`);
|
|
2434
|
+
print(col.gray(hr(W)));
|
|
2606
2435
|
}
|
|
2607
2436
|
function envLabel(apiUrl) {
|
|
2608
2437
|
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";
|
|
2438
|
+
if (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) return "local";
|
|
2439
|
+
if (apiUrl.includes("staging")) return "staging";
|
|
2440
|
+
if (apiUrl.includes(".dev.")) return "dev";
|
|
2613
2441
|
return clip(apiUrl.replace(/^https?:\/\//, ""), 30);
|
|
2614
2442
|
}
|
|
2615
2443
|
var HOME_ACTIONS = [
|
|
2616
|
-
{ value: "serve", label: "
|
|
2617
|
-
{ value: "connect", label: "
|
|
2444
|
+
{ value: "serve", label: "Serve my agents", desc: "Start agents and stream their logs" },
|
|
2445
|
+
{ value: "connect", label: "Login / Switch", desc: "Sign in or switch to a different environment" },
|
|
2618
2446
|
{ value: "create", label: "Create Agent", desc: "Build a new agent from a template" },
|
|
2619
|
-
{ value: "
|
|
2620
|
-
{ value: "
|
|
2447
|
+
{ value: "run", label: "Run once", desc: "Execute one agent immediately" },
|
|
2448
|
+
{ value: "commands", label: "Commands", desc: "Show all CLI commands" },
|
|
2621
2449
|
{ value: "exit", label: "Exit", desc: "" }
|
|
2622
2450
|
];
|
|
2623
2451
|
async function showHome({ env, platform }) {
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
);
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
if (resp.canceled) return "exit";
|
|
2639
|
-
return HOME_ACTIONS[resp.selectedIndex]?.value ?? "exit";
|
|
2452
|
+
print("");
|
|
2453
|
+
printHeader({ env, platform });
|
|
2454
|
+
print("");
|
|
2455
|
+
print(col.bold(" What would you like to do?"));
|
|
2456
|
+
print("");
|
|
2457
|
+
HOME_ACTIONS.forEach((action, i) => {
|
|
2458
|
+
const desc = action.desc ? col.gray(` \u2014 ${action.desc}`) : "";
|
|
2459
|
+
print(` ${col.blue(String(i + 1))} ${action.label.padEnd(22)}${desc}`);
|
|
2460
|
+
});
|
|
2461
|
+
print("");
|
|
2462
|
+
const ans = await ask("Choose a number");
|
|
2463
|
+
const n = parseInt(ans, 10);
|
|
2464
|
+
if (!Number.isFinite(n) || n < 1 || n > HOME_ACTIONS.length) return "home";
|
|
2465
|
+
return HOME_ACTIONS[n - 1].value;
|
|
2640
2466
|
}
|
|
2641
2467
|
var ENDPOINTS = [
|
|
2642
2468
|
{ key: "prod", label: "Production", subtitle: "Recommended for live use" },
|
|
2643
2469
|
{ key: "staging", label: "Staging", subtitle: "Pre-release testing" },
|
|
2644
2470
|
{ key: "dev", label: "Development", subtitle: "Developer preview" },
|
|
2645
|
-
{ key: "local", label: "Local (
|
|
2471
|
+
{ key: "local", label: "Local (localhost:3000)", subtitle: "Local backend" }
|
|
2646
2472
|
];
|
|
2647
2473
|
async function showConnect({ env, platform }) {
|
|
2648
2474
|
while (true) {
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2475
|
+
print("");
|
|
2476
|
+
printHeader({ env, platform });
|
|
2477
|
+
print("");
|
|
2478
|
+
print(col.bold(" Login / Switch environment:"));
|
|
2479
|
+
print("");
|
|
2653
2480
|
const rows = endpointRows({ env, platform });
|
|
2654
2481
|
const items = [
|
|
2655
2482
|
...ENDPOINTS.map((ep2) => {
|
|
2656
2483
|
const row = rows.find((r) => r.key === ep2.key);
|
|
2657
|
-
const badge = row?.active ? "
|
|
2484
|
+
const badge = row?.active ? col.green(" \u25CF active") : row?.status === "signed in" ? col.cyan(" \u25CB signed in") : col.gray(` \u2014 ${ep2.subtitle}`);
|
|
2658
2485
|
return `${ep2.label.padEnd(26)}${badge}`;
|
|
2659
2486
|
}),
|
|
2660
2487
|
"Custom server URL\u2026",
|
|
2661
|
-
"
|
|
2488
|
+
col.gray("Back")
|
|
2662
2489
|
];
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
if (
|
|
2671
|
-
if (resp.selectedIndex === items.length - 2) {
|
|
2490
|
+
items.forEach((item, i) => print(` ${col.blue(String(i + 1))} ${item}`));
|
|
2491
|
+
print("");
|
|
2492
|
+
const ans = await ask("Choose a number, or 0 to go back");
|
|
2493
|
+
const n = parseInt(ans, 10);
|
|
2494
|
+
if (n === 0 || isNaN(n)) return "home";
|
|
2495
|
+
if (n === items.length) return "home";
|
|
2496
|
+
if (n < 1 || n > items.length) continue;
|
|
2497
|
+
if (n === items.length - 1) {
|
|
2672
2498
|
await connectCustomUrl({ env, platform });
|
|
2673
2499
|
continue;
|
|
2674
2500
|
}
|
|
2675
|
-
const ep = ENDPOINTS[
|
|
2676
|
-
|
|
2501
|
+
const ep = ENDPOINTS[n - 1];
|
|
2502
|
+
print("");
|
|
2677
2503
|
const outcome = await connectEndpointInteractive({ backend: ep.key, label: ep.label, env, platform });
|
|
2678
2504
|
if (outcome.promptedInTui) {
|
|
2679
2505
|
await pressEnterToContinue();
|
|
@@ -2681,74 +2507,18 @@ async function showConnect({ env, platform }) {
|
|
|
2681
2507
|
}
|
|
2682
2508
|
}
|
|
2683
2509
|
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" });
|
|
2510
|
+
print("");
|
|
2511
|
+
print(col.bold(" Enter your server URL:"));
|
|
2512
|
+
print("");
|
|
2513
|
+
const url = await ask("URL", "https://");
|
|
2514
|
+
if (!url || url === "https://") return;
|
|
2515
|
+
print("");
|
|
2516
|
+
print(col.gray(" Connecting\u2026"));
|
|
2517
|
+
const outcome = await connectEndpointInteractive({ apiUrl: url, label: url, env, platform });
|
|
2694
2518
|
if (outcome.promptedInTui) {
|
|
2695
2519
|
await pressEnterToContinue();
|
|
2696
2520
|
}
|
|
2697
2521
|
}
|
|
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
2522
|
var COMMAND_GROUPS = [
|
|
2753
2523
|
{ section: "Getting started", commands: [
|
|
2754
2524
|
{ cmd: "vendian login", desc: "Sign in and set up your computer" },
|
|
@@ -2757,13 +2527,12 @@ var COMMAND_GROUPS = [
|
|
|
2757
2527
|
{ section: "Building agents", commands: [
|
|
2758
2528
|
{ cmd: 'vendian create "My Agent" --output-dir .', desc: "Create a new agent" },
|
|
2759
2529
|
{ 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
2530
|
{ cmd: "vendian models", desc: "List available AI models" }
|
|
2762
2531
|
] },
|
|
2763
2532
|
{ 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
|
|
2533
|
+
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one agent" },
|
|
2534
|
+
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents" },
|
|
2535
|
+
{ cmd: "vendian login --backend staging", desc: "Sign in to staging" }
|
|
2767
2536
|
] },
|
|
2768
2537
|
{ section: "Maintenance", commands: [
|
|
2769
2538
|
{ cmd: "vendian doctor", desc: "Check if everything is set up" },
|
|
@@ -2771,89 +2540,52 @@ var COMMAND_GROUPS = [
|
|
|
2771
2540
|
] }
|
|
2772
2541
|
];
|
|
2773
2542
|
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
|
-
`);
|
|
2543
|
+
print("");
|
|
2544
|
+
print(col.bold(` \u2191 VENDIAN v${CLI_VERSION} \u2014 All Commands`));
|
|
2545
|
+
print("");
|
|
2785
2546
|
for (const g of COMMAND_GROUPS) {
|
|
2786
|
-
|
|
2787
|
-
`);
|
|
2547
|
+
print(col.blue(col.bold(` ${g.section}`)));
|
|
2788
2548
|
for (const item of g.commands) {
|
|
2789
|
-
|
|
2790
|
-
term.cyan(item.cmd.padEnd(46));
|
|
2791
|
-
term.gray(`${item.desc}
|
|
2792
|
-
`);
|
|
2549
|
+
print(` ${col.cyan(item.cmd.padEnd(52))} ${col.gray(item.desc)}`);
|
|
2793
2550
|
}
|
|
2794
|
-
|
|
2551
|
+
print("");
|
|
2795
2552
|
}
|
|
2796
|
-
|
|
2797
|
-
await waitForKey();
|
|
2553
|
+
await pressEnterToContinue();
|
|
2798
2554
|
return "home";
|
|
2799
2555
|
}
|
|
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
2556
|
async function pickAgentsToRun({ env, platform, candidates }) {
|
|
2815
2557
|
if (candidates.length === 0) {
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
term.gray(" Enter the path to your agents folder:\n\n");
|
|
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" };
|
|
2558
|
+
print("");
|
|
2559
|
+
print(col.yellow(" Couldn't find any agents. Enter your agents folder path:"));
|
|
2560
|
+
const input = await ask("Folder", "./agents");
|
|
2561
|
+
return input ? { agentsDir: input } : null;
|
|
2825
2562
|
}
|
|
2826
2563
|
if (candidates.length === 1 && candidates[0].agentCount === 1) {
|
|
2827
2564
|
return { agentsDir: candidates[0].path };
|
|
2828
2565
|
}
|
|
2829
2566
|
if (candidates.length === 1) {
|
|
2830
|
-
return
|
|
2567
|
+
return pickAgentsFromFolder({ env, platform, candidate: candidates[0] });
|
|
2831
2568
|
}
|
|
2832
|
-
return
|
|
2569
|
+
return pickProject({ env, platform, candidates });
|
|
2833
2570
|
}
|
|
2834
2571
|
async function pickProject({ env, platform, candidates }) {
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
leftPadding: " ",
|
|
2851
|
-
selectedLeftPadding: " \u203A "
|
|
2852
|
-
}).promise;
|
|
2853
|
-
if (resp.canceled || resp.selectedIndex === items.length - 1) return null;
|
|
2854
|
-
const chosen = candidates[resp.selectedIndex];
|
|
2572
|
+
print("");
|
|
2573
|
+
printHeader({ env, platform });
|
|
2574
|
+
print("");
|
|
2575
|
+
print(col.bold(" Which project do you want to run?"));
|
|
2576
|
+
print("");
|
|
2577
|
+
candidates.forEach((candidate, i) => {
|
|
2578
|
+
const name = readManifestName(candidate.absolutePath) || friendlyName(path8.basename(candidate.absolutePath) || candidate.path);
|
|
2579
|
+
print(` ${col.blue(String(i + 1))} ${name.padEnd(32)} ${col.gray(`${candidate.agentCount} agent${candidate.agentCount === 1 ? "" : "s"}`)}`);
|
|
2580
|
+
});
|
|
2581
|
+
print(` ${col.gray("0 Back")}`);
|
|
2582
|
+
print("");
|
|
2583
|
+
const ans = await ask("Choose");
|
|
2584
|
+
const n = parseInt(ans, 10);
|
|
2585
|
+
if (n === 0 || isNaN(n) || n > candidates.length) return null;
|
|
2586
|
+
const chosen = candidates[n - 1];
|
|
2855
2587
|
if (chosen.agentCount <= 1) return { agentsDir: chosen.path };
|
|
2856
|
-
return
|
|
2588
|
+
return pickAgentsFromFolder({ env, platform, candidate: chosen });
|
|
2857
2589
|
}
|
|
2858
2590
|
async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
2859
2591
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
@@ -2862,39 +2594,28 @@ async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
|
2862
2594
|
...f,
|
|
2863
2595
|
displayName: readManifestName(f.absolutePath) || friendlyName(f.name || path8.basename(f.path))
|
|
2864
2596
|
}));
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
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 };
|
|
2597
|
+
print("");
|
|
2598
|
+
printHeader({ env, platform });
|
|
2599
|
+
print("");
|
|
2600
|
+
print(col.bold(" Which agents do you want to start?"));
|
|
2601
|
+
print("");
|
|
2602
|
+
print(` ${col.blue("1")} Run all ${folders.length} agents`);
|
|
2603
|
+
named.forEach((f, i) => print(` ${col.blue(String(i + 2))} ${f.displayName}`));
|
|
2604
|
+
print(` ${col.gray("0 Back")}`);
|
|
2605
|
+
print("");
|
|
2606
|
+
const ans = await ask("Choose");
|
|
2607
|
+
const n = parseInt(ans, 10);
|
|
2608
|
+
if (n === 0 || isNaN(n) || n > named.length + 1) return null;
|
|
2609
|
+
if (n === 1) return { agentsDir: candidate.path };
|
|
2610
|
+
return { agentsDir: named[n - 2].path };
|
|
2885
2611
|
}
|
|
2886
2612
|
async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
2887
2613
|
if (candidates.length === 0) {
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
term.gray(" Folder: ");
|
|
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") };
|
|
2614
|
+
print("");
|
|
2615
|
+
print(col.yellow(" Couldn't find any agents."));
|
|
2616
|
+
const input = await ask("Agent folder", "./agents/my-agent");
|
|
2617
|
+
if (!input) return null;
|
|
2618
|
+
return { path: input, displayName: friendlyName(path8.basename(input) || "Agent") };
|
|
2898
2619
|
}
|
|
2899
2620
|
const agents = candidates.flatMap((candidate) => {
|
|
2900
2621
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
@@ -2910,32 +2631,24 @@ async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
|
2910
2631
|
}];
|
|
2911
2632
|
});
|
|
2912
2633
|
if (agents.length === 1) return agents[0];
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
if (
|
|
2926
|
-
return agents[
|
|
2927
|
-
}
|
|
2928
|
-
async function chooseCloudWorkspace({
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
loadingTitle,
|
|
2932
|
-
pickerTitle
|
|
2933
|
-
} = {}) {
|
|
2934
|
-
term.clear();
|
|
2935
|
-
drawHeader({ env, platform });
|
|
2936
|
-
term("\n");
|
|
2937
|
-
term.bold(`${loadingTitle}
|
|
2938
|
-
|
|
2634
|
+
print("");
|
|
2635
|
+
printHeader({ env, platform });
|
|
2636
|
+
print("");
|
|
2637
|
+
print(col.bold(" Which agent do you want to run?"));
|
|
2638
|
+
print("");
|
|
2639
|
+
agents.forEach((agent, i) => {
|
|
2640
|
+
print(` ${col.blue(String(i + 1))} ${agent.displayName.padEnd(32)} ${col.gray(agent.path)}`);
|
|
2641
|
+
});
|
|
2642
|
+
print(` ${col.gray("0 Back")}`);
|
|
2643
|
+
print("");
|
|
2644
|
+
const ans = await ask("Choose");
|
|
2645
|
+
const n = parseInt(ans, 10);
|
|
2646
|
+
if (n === 0 || isNaN(n) || n > agents.length) return null;
|
|
2647
|
+
return agents[n - 1];
|
|
2648
|
+
}
|
|
2649
|
+
async function chooseCloudWorkspace({ env, platform, loadingTitle, pickerTitle } = {}) {
|
|
2650
|
+
print("");
|
|
2651
|
+
process.stdout.write(` ${col.gray(loadingTitle || "Loading workspaces\u2026")}
|
|
2939
2652
|
`);
|
|
2940
2653
|
let wsResult;
|
|
2941
2654
|
try {
|
|
@@ -2943,296 +2656,214 @@ async function chooseCloudWorkspace({
|
|
|
2943
2656
|
env,
|
|
2944
2657
|
platform,
|
|
2945
2658
|
onProgress: (msg) => {
|
|
2946
|
-
|
|
2947
|
-
term.eraseLine();
|
|
2948
|
-
term.gray(` ${clip(msg, 60)}
|
|
2949
|
-
`);
|
|
2659
|
+
process.stdout.write(`\r ${col.gray(clip(msg, 60))} `);
|
|
2950
2660
|
}
|
|
2951
2661
|
});
|
|
2662
|
+
process.stdout.write("\r" + " ".repeat(70) + "\r");
|
|
2952
2663
|
} catch (error) {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
`);
|
|
2956
|
-
|
|
2664
|
+
process.stdout.write("\r" + " ".repeat(70) + "\r");
|
|
2665
|
+
print(col.red(`
|
|
2666
|
+
Couldn't connect: ${errMsg(error)}`));
|
|
2667
|
+
print(col.gray(" Make sure you are signed in (choose Login / Switch from the menu)."));
|
|
2957
2668
|
await pressEnterToContinue({ grabActive: false });
|
|
2958
2669
|
return null;
|
|
2959
2670
|
}
|
|
2960
2671
|
if (!wsResult.ok) {
|
|
2961
|
-
|
|
2962
|
-
${fig.cross} ${wsResult.error}
|
|
2963
|
-
`);
|
|
2672
|
+
print(col.red(`
|
|
2673
|
+
${fig.cross} ${wsResult.error}`));
|
|
2964
2674
|
await pressEnterToContinue({ grabActive: false });
|
|
2965
2675
|
return null;
|
|
2966
2676
|
}
|
|
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
|
-
|
|
2677
|
+
if (wsResult.workspaces.length === 0) return null;
|
|
2678
|
+
if (wsResult.workspaces.length === 1) {
|
|
2679
|
+
const ws2 = wsResult.workspaces[0];
|
|
2680
|
+
return { id: ws2.id || "", label: workspaceLabel(ws2) };
|
|
2681
|
+
}
|
|
2682
|
+
print("");
|
|
2683
|
+
print(col.bold(` ${pickerTitle || "Choose a workspace:"}`));
|
|
2684
|
+
print("");
|
|
2685
|
+
wsResult.workspaces.forEach((ws2, i) => {
|
|
2686
|
+
print(` ${col.blue(String(i + 1))} ${workspaceLabel(ws2)}`);
|
|
2687
|
+
});
|
|
2688
|
+
print(` ${col.gray("0 Back")}`);
|
|
2689
|
+
print("");
|
|
2690
|
+
const ans = await ask("Choose");
|
|
2691
|
+
const n = parseInt(ans, 10);
|
|
2692
|
+
if (n === 0 || isNaN(n) || n > wsResult.workspaces.length) return null;
|
|
2693
|
+
const ws = wsResult.workspaces[n - 1];
|
|
2694
|
+
return { id: ws.id || "", label: workspaceLabel(ws) };
|
|
2695
|
+
}
|
|
2696
|
+
async function showCreate({ env, platform }) {
|
|
2697
|
+
print("");
|
|
2698
|
+
printHeader({ env, platform });
|
|
2699
|
+
print("");
|
|
2700
|
+
print(col.bold(" Create a new agent"));
|
|
2701
|
+
print("");
|
|
2702
|
+
const name = await ask("Agent name");
|
|
2703
|
+
if (!name?.trim()) return "home";
|
|
2704
|
+
const dir = await ask("Save it in folder", "./agents");
|
|
2705
|
+
await withOutputMode(
|
|
2706
|
+
`Creating agent: ${name.trim()}`,
|
|
2707
|
+
() => forwardToPythonVendian(["create", name.trim(), "--output-dir", dir || "./agents"], { env, platform })
|
|
2708
|
+
);
|
|
2709
|
+
return "home";
|
|
2710
|
+
}
|
|
2711
|
+
async function showRun({ env, platform }) {
|
|
2712
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2713
|
+
const picked = await pickSingleAgentToRun({ env, platform, candidates });
|
|
2714
|
+
if (!picked) return "home";
|
|
2715
|
+
const workspace = await chooseCloudWorkspace({
|
|
2716
|
+
env,
|
|
2717
|
+
platform,
|
|
2718
|
+
loadingTitle: "Getting ready to run this agent\u2026",
|
|
2719
|
+
pickerTitle: "Which project should own this run?"
|
|
2720
|
+
});
|
|
2721
|
+
if (!workspace) return "home";
|
|
2722
|
+
const inputJson = await ask("Input JSON", "{}");
|
|
2723
|
+
await withOutputMode(
|
|
2724
|
+
`Running ${picked.displayName}`,
|
|
2725
|
+
() => forwardToPythonVendian(buildLocalRunArgs({
|
|
2726
|
+
agentPath: picked.path,
|
|
2727
|
+
collectionId: workspace.id,
|
|
2728
|
+
inputJson: inputJson || "{}"
|
|
2729
|
+
}), { env, platform })
|
|
2730
|
+
);
|
|
2731
|
+
return "home";
|
|
2732
|
+
}
|
|
2733
|
+
async function showServe({ env, platform }) {
|
|
2734
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2735
|
+
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2736
|
+
if (!picked) return "home";
|
|
2737
|
+
const { agentsDir } = picked;
|
|
2738
|
+
const workspace = await chooseCloudWorkspace({
|
|
2739
|
+
env,
|
|
2740
|
+
platform,
|
|
2741
|
+
loadingTitle: "Getting ready to run your agents\u2026",
|
|
2742
|
+
pickerTitle: "Which project do you want to run?"
|
|
2743
|
+
});
|
|
2744
|
+
if (!workspace) return "home";
|
|
2745
|
+
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id, wsLabel: workspace.label });
|
|
2746
|
+
}
|
|
2747
|
+
async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLabel = "" }) {
|
|
2988
2748
|
let state = initialServeState();
|
|
2989
2749
|
let child = null;
|
|
2990
|
-
let startupError = "";
|
|
2991
|
-
let overlayActive = false;
|
|
2992
|
-
let overlayRedraw = null;
|
|
2993
2750
|
let ctrlCArmed = false;
|
|
2994
2751
|
let ctrlCArmTimer = null;
|
|
2995
|
-
let dashboardClosed = false;
|
|
2996
|
-
let exitPromptActive = false;
|
|
2997
|
-
let selectedIdx = 0;
|
|
2998
|
-
let pressedAgentIdx = null;
|
|
2999
2752
|
let stopRequested = false;
|
|
3000
2753
|
let stopSignalAttempt = 0;
|
|
3001
2754
|
let stopTimer = null;
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
function
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
}
|
|
3022
|
-
all.sort((a, b) => (a.timestamp || "").localeCompare(b.timestamp || ""));
|
|
3023
|
-
return all.slice(-maxLines);
|
|
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.
|
|
2755
|
+
let dashboardClosed = false;
|
|
2756
|
+
let logStore = null;
|
|
2757
|
+
const W = Math.min(process.stdout.columns || 80, 100);
|
|
2758
|
+
setCursorVisible(false);
|
|
2759
|
+
print("");
|
|
2760
|
+
print(col.gray("\u2550".repeat(W)));
|
|
2761
|
+
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.bold("Serving agents")}`);
|
|
2762
|
+
print(` ${col.gray("Dir:")} ${agentsDir}`);
|
|
2763
|
+
print(` ${col.gray("Workspace:")} ${wsLabel || collectionId || "\u2014"}`);
|
|
2764
|
+
print(col.gray(hr(W)));
|
|
2765
|
+
print(col.gray(" S stop gracefully | ^c ^c exit"));
|
|
2766
|
+
print(col.gray(hr(W)));
|
|
2767
|
+
print("");
|
|
2768
|
+
function ts() {
|
|
2769
|
+
return formatLogTime((/* @__PURE__ */ new Date()).toISOString());
|
|
2770
|
+
}
|
|
2771
|
+
function printRun(agentName, icon, message) {
|
|
2772
|
+
const name = clip(agentName || "agent", 22).padEnd(22);
|
|
2773
|
+
process.stdout.write(`${ts()} ${col.blue("RUN ")} ${name} ${icon} ${message}
|
|
3056
2774
|
`);
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
2775
|
+
}
|
|
2776
|
+
function printAgent(agentName, statusText) {
|
|
2777
|
+
const name = clip(agentName || "agent", 22).padEnd(22);
|
|
2778
|
+
process.stdout.write(`${ts()} ${col.cyan("AGENT ")} ${name} ${statusText}
|
|
3060
2779
|
`);
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
2780
|
+
}
|
|
2781
|
+
function printDaemon(eventType, message) {
|
|
2782
|
+
const evt = clip(String(eventType || "event"), 26).padEnd(26);
|
|
2783
|
+
process.stdout.write(`${ts()} ${col.gray("DAEMON")} ${evt} ${message}
|
|
3064
2784
|
`);
|
|
2785
|
+
}
|
|
2786
|
+
function onServeEvent(event) {
|
|
2787
|
+
if (!event) return;
|
|
2788
|
+
const type = String(event.type || "");
|
|
2789
|
+
const agentPath = String(event.relativePath || event.path || "");
|
|
2790
|
+
const agentName = agentPath ? friendlyName(path8.basename(agentPath)) || agentPath : "";
|
|
2791
|
+
if (type === "job_started") {
|
|
2792
|
+
printRun(agentName, col.blue("\u25B6"), `Started ${event.jobType || "job"} ${shortId2(event.runId || "")}`);
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
if (type === "job_completed") {
|
|
2796
|
+
const ok = event.success !== false;
|
|
2797
|
+
printRun(agentName, ok ? col.green("\u2714") : col.red("\u2716"), ok ? "Completed" : `Failed: ${fmtErr(event.error)}`);
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
if (type === "run_log") {
|
|
2801
|
+
const lvl = String(event.level || "info");
|
|
2802
|
+
const msg = String(event.message || event.eventType || "");
|
|
2803
|
+
if (!msg) return;
|
|
2804
|
+
const isCompletionEvent = event.eventType === "completion";
|
|
2805
|
+
const icon = lvl === "error" ? col.red("\u2716") : isCompletionEvent ? event.success !== false ? col.green("\u2714") : col.red("\u2716") : "\u2014";
|
|
2806
|
+
printRun(agentName || "?", icon, clip(msg, 80));
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
if (type === "error" && agentPath) {
|
|
2810
|
+
printRun(agentName, col.red("\u2716"), `Error: ${fmtErr(event.error) || String(event.code || "unknown")}`);
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
if (type === "agent_prepare_started") {
|
|
2814
|
+
printAgent(agentName, col.gray("Preparing\u2026"));
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
if (type === "agent_prepare_progress") {
|
|
2818
|
+
const msg = String(event.message || event.stage || "Preparing");
|
|
2819
|
+
printAgent(agentName, col.gray(clip(msg, 60)));
|
|
2820
|
+
return;
|
|
3065
2821
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
currentRow++;
|
|
2822
|
+
if (type === "agent_prepare_completed") {
|
|
2823
|
+
if (event.status === "error") {
|
|
2824
|
+
printAgent(agentName, col.red(`\u2716 Setup failed: ${fmtErr(event.error)}`));
|
|
2825
|
+
return;
|
|
3071
2826
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
const
|
|
3083
|
-
if (
|
|
3084
|
-
|
|
3085
|
-
|
|
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
|
-
}
|
|
2827
|
+
if (event.status === "disabled") {
|
|
2828
|
+
printAgent(agentName, col.yellow(`\u26A0 Disabled: ${event.disabledReason || "deps not met locally"}`));
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
printAgent(agentName, col.green("\u2714 Ready"));
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (type === "inventory_synced") {
|
|
2835
|
+
const agents = Array.isArray(event.agents) ? event.agents : [];
|
|
2836
|
+
for (const a of agents) {
|
|
2837
|
+
const name = friendlyName(path8.basename(a.relativePath || ".")) || a.manifestName || a.relativePath || "?";
|
|
2838
|
+
if (a.status === "error") {
|
|
2839
|
+
printAgent(name, col.red(`\u2716 Error: ${a.errorMessage || ""}`));
|
|
2840
|
+
continue;
|
|
3126
2841
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
2842
|
+
if (a.status === "disabled") {
|
|
2843
|
+
printAgent(name, col.yellow(`\u26A0 Disabled: ${a.disabledReason || ""}`));
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
if (a.status === "online") {
|
|
2847
|
+
printAgent(name, col.green("\u25CF Online"));
|
|
2848
|
+
continue;
|
|
3133
2849
|
}
|
|
3134
2850
|
}
|
|
3135
|
-
|
|
3136
|
-
const first = viewport.agentWindowStart + 1;
|
|
3137
|
-
const last = viewport.agentWindowStart + visibleAgents.length;
|
|
3138
|
-
term.gray(` Showing agents ${first}\u2013${last} of ${agents.length}
|
|
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++;
|
|
3151
|
-
}
|
|
3152
|
-
term.gray(" Looking for your agents\u2026\n");
|
|
3153
|
-
currentRow++;
|
|
2851
|
+
return;
|
|
3154
2852
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
const
|
|
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
|
-
}
|
|
3183
|
-
}
|
|
3184
|
-
if (viewport.showDiagnosticsHeader) {
|
|
3185
|
-
const visibleDiagnostics = diagnostics.slice(-viewport.visibleDiagnosticsCount);
|
|
3186
|
-
const divW = Math.min(W - 4, 72);
|
|
3187
|
-
term.gray(` ${"\u2500".repeat(3)} Daemon diagnostics ${"\u2500".repeat(Math.max(0, divW - 23))}
|
|
3188
|
-
`);
|
|
3189
|
-
currentRow++;
|
|
3190
|
-
for (const entry of visibleDiagnostics) {
|
|
3191
|
-
const t = formatLogTime(entry.timestamp);
|
|
3192
|
-
const type = clip(entry.eventType || "event", 22).padEnd(22);
|
|
3193
|
-
const msg = clip(entry.message || "", W - 36);
|
|
3194
|
-
term.gray(` ${t} `);
|
|
3195
|
-
if (entry.level === "error") term.red(`${type} ${msg}
|
|
3196
|
-
`);
|
|
3197
|
-
else if (entry.level === "warn") term.yellow(`${type} ${msg}
|
|
3198
|
-
`);
|
|
3199
|
-
else term.gray(`${type} ${msg}
|
|
3200
|
-
`);
|
|
3201
|
-
currentRow++;
|
|
3202
|
-
}
|
|
3203
|
-
}
|
|
3204
|
-
const footerRow = Math.max(currentRow + 1, H - 1);
|
|
3205
|
-
if (footerRow < H) term.moveTo(1, footerRow);
|
|
3206
|
-
else term("\n");
|
|
3207
|
-
if (ctrlCArmed) {
|
|
3208
|
-
term.yellow.bold(" Press Ctrl+C again to exit.\n");
|
|
3209
|
-
} else if (stopRequested) {
|
|
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");
|
|
2853
|
+
const dbgArr = serveDebugEntries([event], 1);
|
|
2854
|
+
if (dbgArr.length > 0) {
|
|
2855
|
+
const dbg = dbgArr[0];
|
|
2856
|
+
const msg = dbg.level === "error" ? col.red(dbg.message) : dbg.level === "warn" ? col.yellow(dbg.message) : col.gray(dbg.message);
|
|
2857
|
+
printDaemon(dbg.eventType, msg);
|
|
3219
2858
|
}
|
|
3220
2859
|
}
|
|
3221
|
-
term.clear();
|
|
3222
|
-
drawHeader({ env, platform, serveState: state });
|
|
3223
|
-
term("\n");
|
|
3224
|
-
term.cyan(` ${fig.dotEmpty} Getting your agents ready\u2026
|
|
3225
|
-
`);
|
|
3226
2860
|
try {
|
|
3227
|
-
|
|
3228
|
-
const logStore = createServeLogStore({ agentsDir, collectionId, env, platform });
|
|
2861
|
+
logStore = createServeLogStore({ agentsDir, collectionId, env, platform });
|
|
3229
2862
|
const historicalLogs = logStore.load();
|
|
3230
2863
|
if (historicalLogs.length > 0) {
|
|
3231
2864
|
const agentLogs = mergeAgentLogRecords(state.agentLogs, historicalLogs);
|
|
3232
2865
|
state = { ...state, agentLogs, agentRunState: { ...agentRunStateFromLogs(agentLogs, { includeRunning: false }), ...state.agentRunState } };
|
|
3233
2866
|
}
|
|
3234
|
-
state = { ...state, activity: "Preparing your agents\u2026" };
|
|
3235
|
-
redraw();
|
|
3236
2867
|
child = await spawnLocalServeEventStream({
|
|
3237
2868
|
agentsDir,
|
|
3238
2869
|
collectionId,
|
|
@@ -3240,553 +2871,219 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
3240
2871
|
platform,
|
|
3241
2872
|
onProgress: (msg) => {
|
|
3242
2873
|
if (dashboardClosed) return;
|
|
3243
|
-
|
|
3244
|
-
redraw();
|
|
2874
|
+
process.stdout.write(`\r ${col.gray(clip(msg || "Preparing\u2026", 70))} `);
|
|
3245
2875
|
}
|
|
3246
2876
|
});
|
|
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
|
-
);
|
|
2877
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
2878
|
+
printDaemon("serve_started", col.gray(`dir=${agentsDir}`));
|
|
3265
2879
|
} catch (error) {
|
|
3266
|
-
|
|
3267
|
-
|
|
2880
|
+
setCursorVisible(true);
|
|
2881
|
+
print(col.red(` ${fig.cross} Failed to start: ${errMsg(error)}`));
|
|
2882
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2883
|
+
return "home";
|
|
3268
2884
|
}
|
|
3269
2885
|
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
|
-
}
|
|
2886
|
+
let removeKeypress = () => {
|
|
2887
|
+
};
|
|
2888
|
+
function finish(next) {
|
|
2889
|
+
if (dashboardClosed) return;
|
|
2890
|
+
dashboardClosed = true;
|
|
2891
|
+
clearTimeout(ctrlCArmTimer);
|
|
2892
|
+
clearTimeout(stopTimer);
|
|
2893
|
+
removeKeypress();
|
|
2894
|
+
setCursorVisible(true);
|
|
2895
|
+
resolve(next);
|
|
3305
2896
|
}
|
|
3306
2897
|
function childIsRunning() {
|
|
3307
2898
|
return Boolean(child) && child.exitCode == null && child.signalCode == null;
|
|
3308
2899
|
}
|
|
3309
2900
|
function scheduleStopEscalation() {
|
|
3310
|
-
|
|
2901
|
+
clearTimeout(stopTimer);
|
|
3311
2902
|
if (!childIsRunning()) return;
|
|
3312
2903
|
stopTimer = setTimeout(() => {
|
|
3313
2904
|
if (!stopRequested || dashboardClosed || !childIsRunning()) return;
|
|
3314
2905
|
stopSignalAttempt += 1;
|
|
3315
2906
|
const signal = stopSignalForAttempt(stopSignalAttempt);
|
|
3316
|
-
state = {
|
|
3317
|
-
...state,
|
|
3318
|
-
activity: signal === "SIGTERM" ? "Still stopping your agents\u2026" : "Force stopping your agents\u2026"
|
|
3319
|
-
};
|
|
3320
2907
|
try {
|
|
3321
2908
|
child.kill(signal);
|
|
3322
2909
|
} catch {
|
|
3323
2910
|
}
|
|
3324
|
-
if (!overlayActive) redraw();
|
|
3325
2911
|
if (signal !== "SIGKILL") scheduleStopEscalation();
|
|
3326
2912
|
}, 4e3);
|
|
3327
2913
|
}
|
|
3328
2914
|
function requestStop() {
|
|
3329
|
-
if (dashboardClosed) return;
|
|
3330
|
-
if (stopRequested) return;
|
|
2915
|
+
if (dashboardClosed || stopRequested) return;
|
|
3331
2916
|
if (!childIsRunning()) {
|
|
3332
2917
|
finish("home");
|
|
3333
2918
|
return;
|
|
3334
2919
|
}
|
|
3335
2920
|
stopRequested = true;
|
|
3336
|
-
stopSignalAttempt = 0;
|
|
3337
2921
|
state = { ...state, activity: "Stopping your agents\u2026" };
|
|
3338
|
-
|
|
2922
|
+
print(col.yellow("\n Stopping your agents\u2026"));
|
|
2923
|
+
clearTimeout(ctrlCArmTimer);
|
|
2924
|
+
ctrlCArmed = false;
|
|
3339
2925
|
try {
|
|
3340
2926
|
child.kill(stopSignalForAttempt(stopSignalAttempt));
|
|
3341
2927
|
} catch {
|
|
3342
2928
|
}
|
|
3343
2929
|
scheduleStopEscalation();
|
|
3344
|
-
if (!overlayActive) redraw();
|
|
3345
|
-
}
|
|
3346
|
-
function detachDashboardHandlers() {
|
|
3347
|
-
term.off("key", handleKey);
|
|
3348
|
-
term.off("mouse", handleMouse);
|
|
3349
|
-
term.off("resize", handleResize);
|
|
3350
|
-
}
|
|
3351
|
-
function finish(next) {
|
|
3352
|
-
if (dashboardClosed) return;
|
|
3353
|
-
dashboardClosed = true;
|
|
3354
|
-
clearCtrlCArm();
|
|
3355
|
-
clearStopTimer();
|
|
3356
|
-
detachDashboardHandlers();
|
|
3357
|
-
resolve(next);
|
|
3358
2930
|
}
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
if (
|
|
3362
|
-
|
|
3363
|
-
|
|
2931
|
+
if (process.stdin.isTTY) {
|
|
2932
|
+
let onKeypress = function(str, key) {
|
|
2933
|
+
if (!key) return;
|
|
2934
|
+
if (key.ctrl && key.name === "c") {
|
|
2935
|
+
if (ctrlCArmed) {
|
|
2936
|
+
child?.kill("SIGINT");
|
|
2937
|
+
finish("exit");
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
ctrlCArmed = true;
|
|
2941
|
+
process.stdout.write("\n" + col.yellow(" Press Ctrl+C again to exit.") + "\n");
|
|
2942
|
+
ctrlCArmTimer = setTimeout(() => {
|
|
2943
|
+
ctrlCArmed = false;
|
|
2944
|
+
}, 1500);
|
|
3364
2945
|
return;
|
|
3365
2946
|
}
|
|
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
|
-
}
|
|
2947
|
+
if (stopRequested) return;
|
|
2948
|
+
if (key.name === "s" || key.name === "S") {
|
|
2949
|
+
requestStop();
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
readline.emitKeypressEvents(process.stdin);
|
|
2953
|
+
process.stdin.setRawMode(true);
|
|
2954
|
+
process.stdin.resume();
|
|
2955
|
+
process.stdin.on("keypress", onKeypress);
|
|
2956
|
+
removeKeypress = () => {
|
|
2957
|
+
process.stdin.off("keypress", onKeypress);
|
|
2958
|
+
try {
|
|
2959
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
2960
|
+
} catch {
|
|
2961
|
+
}
|
|
2962
|
+
};
|
|
3392
2963
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
2964
|
+
let stdoutBuffer = "";
|
|
2965
|
+
const stderrChunks = [];
|
|
2966
|
+
child.stdout.setEncoding("utf8");
|
|
2967
|
+
child.stdout.on("data", (chunk) => {
|
|
2968
|
+
stdoutBuffer += chunk;
|
|
2969
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
2970
|
+
stdoutBuffer = lines.pop() || "";
|
|
2971
|
+
for (const line of lines) {
|
|
2972
|
+
try {
|
|
2973
|
+
const event = parseServeEventLine(line);
|
|
2974
|
+
if (event) {
|
|
2975
|
+
try {
|
|
2976
|
+
logStore?.append(event);
|
|
2977
|
+
} catch {
|
|
2978
|
+
}
|
|
2979
|
+
state = applyServeEvent(state, event);
|
|
2980
|
+
onServeEvent(event);
|
|
2981
|
+
}
|
|
2982
|
+
} catch {
|
|
3401
2983
|
}
|
|
3402
|
-
return;
|
|
3403
2984
|
}
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
2985
|
+
});
|
|
2986
|
+
child.stderr.setEncoding("utf8");
|
|
2987
|
+
child.stderr.on("data", (chunk) => {
|
|
2988
|
+
const text = String(chunk).trim();
|
|
2989
|
+
if (!text) return;
|
|
2990
|
+
stderrChunks.push(text);
|
|
2991
|
+
printDaemon("stderr", col.yellow(clip(text, 120)));
|
|
2992
|
+
state = applyServeEvent(state, {
|
|
2993
|
+
type: "daemon_stderr",
|
|
2994
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2995
|
+
message: text
|
|
2996
|
+
});
|
|
2997
|
+
});
|
|
2998
|
+
child.on("error", (error) => {
|
|
2999
|
+
print(col.red(` Error: ${errMsg(error)}`));
|
|
3000
|
+
});
|
|
3001
|
+
child.once("exit", () => {
|
|
3002
|
+
if (stopRequested) {
|
|
3003
|
+
finish("home");
|
|
3408
3004
|
return;
|
|
3409
3005
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3006
|
+
const stderr = stderrChunks.join("\n");
|
|
3007
|
+
const message = serveProcessExitMessage({ stderr, code: child.exitCode, signal: child.signalCode });
|
|
3008
|
+
if (message) {
|
|
3009
|
+
print(col.yellow(`
|
|
3010
|
+
${message}`));
|
|
3011
|
+
} else {
|
|
3012
|
+
print(col.gray("\n Agents stopped."));
|
|
3416
3013
|
}
|
|
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);
|
|
3014
|
+
try {
|
|
3015
|
+
logStore?.compact();
|
|
3016
|
+
} catch {
|
|
3017
|
+
}
|
|
3018
|
+
removeKeypress();
|
|
3019
|
+
process.stdout.write("\n Press Enter to return to menu\u2026 ");
|
|
3020
|
+
const rl = makeRl();
|
|
3021
|
+
rl.once("line", () => {
|
|
3022
|
+
rl.close();
|
|
3023
|
+
finish("home");
|
|
3440
3024
|
});
|
|
3441
|
-
|
|
3025
|
+
rl.once("close", () => finish("home"));
|
|
3026
|
+
});
|
|
3442
3027
|
});
|
|
3443
3028
|
}
|
|
3444
|
-
async function
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
function currentLogs() {
|
|
3452
|
-
return agentLogEntries(liveState().agentLogs, agent.path);
|
|
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}`);
|
|
3029
|
+
async function mainLoop({ env, platform }) {
|
|
3030
|
+
let screen = "home";
|
|
3031
|
+
while (true) {
|
|
3032
|
+
let next;
|
|
3033
|
+
switch (screen) {
|
|
3034
|
+
case "home":
|
|
3035
|
+
next = await showHome({ env, platform });
|
|
3475
3036
|
break;
|
|
3476
|
-
case "
|
|
3477
|
-
|
|
3037
|
+
case "connect":
|
|
3038
|
+
next = await showConnect({ env, platform });
|
|
3478
3039
|
break;
|
|
3479
|
-
case "
|
|
3480
|
-
|
|
3040
|
+
case "create":
|
|
3041
|
+
next = await showCreate({ env, platform });
|
|
3481
3042
|
break;
|
|
3482
|
-
case "
|
|
3483
|
-
|
|
3043
|
+
case "run":
|
|
3044
|
+
next = await showRun({ env, platform });
|
|
3045
|
+
break;
|
|
3046
|
+
case "serve":
|
|
3047
|
+
next = await showServe({ env, platform });
|
|
3048
|
+
break;
|
|
3049
|
+
case "commands":
|
|
3050
|
+
next = await showCommands();
|
|
3484
3051
|
break;
|
|
3485
3052
|
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 {
|
|
3053
|
+
next = "exit";
|
|
3623
3054
|
}
|
|
3055
|
+
if (!next || next === "exit") break;
|
|
3056
|
+
screen = next;
|
|
3624
3057
|
}
|
|
3625
|
-
return null;
|
|
3626
|
-
}
|
|
3627
|
-
function attachServeChild(child, onUpdate, onExit, logStore = null) {
|
|
3628
|
-
let buffer = "";
|
|
3629
|
-
const stderrChunks = [];
|
|
3630
|
-
child.stdout.setEncoding("utf8");
|
|
3631
|
-
child.stdout.on("data", (chunk) => {
|
|
3632
|
-
buffer += chunk;
|
|
3633
|
-
const lines = buffer.split(/\r?\n/);
|
|
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
3058
|
}
|
|
3691
|
-
async function
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
`);
|
|
3700
|
-
try {
|
|
3701
|
-
await fn();
|
|
3702
|
-
} catch (error) {
|
|
3703
|
-
process.stdout.write(`
|
|
3704
|
-
Something went wrong: ${errMsg(error)}
|
|
3059
|
+
async function runTui({
|
|
3060
|
+
env = process.env,
|
|
3061
|
+
platform = process.platform,
|
|
3062
|
+
output = process.stdout
|
|
3063
|
+
} = {}) {
|
|
3064
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3065
|
+
output.write(`${helpText()}
|
|
3705
3066
|
`);
|
|
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
|
-
return;
|
|
3727
|
-
}
|
|
3728
|
-
if (visible && typeof term?.showCursor === "function") {
|
|
3729
|
-
term.showCursor();
|
|
3730
3067
|
return;
|
|
3731
3068
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
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);
|
|
3069
|
+
await refreshInteractiveManagedRuntime({ env, platform, output });
|
|
3070
|
+
await mainLoop({ env, platform });
|
|
3778
3071
|
}
|
|
3779
|
-
function
|
|
3780
|
-
|
|
3072
|
+
async function refreshInteractiveManagedRuntime({
|
|
3073
|
+
env = process.env,
|
|
3074
|
+
platform = process.platform,
|
|
3075
|
+
output = process.stderr,
|
|
3076
|
+
refreshPackageAccess = refreshPackageAccessFromCloudAuth,
|
|
3077
|
+
autoUpdate = maybeAutoUpdateManagedEnv
|
|
3078
|
+
} = {}) {
|
|
3079
|
+
if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") return false;
|
|
3781
3080
|
try {
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3081
|
+
await refreshPackageAccess({ env, platform });
|
|
3082
|
+
} catch (error) {
|
|
3083
|
+
output.write(`[vendian] Package access refresh failed; continuing. ${errMsg(error)}
|
|
3084
|
+
`);
|
|
3786
3085
|
}
|
|
3787
|
-
}
|
|
3788
|
-
function pad2(n) {
|
|
3789
|
-
return String(n).padStart(2, "0");
|
|
3086
|
+
return autoUpdate({ env, platform, force: true });
|
|
3790
3087
|
}
|
|
3791
3088
|
function endpointRows({ env = process.env, platform = process.platform } = {}) {
|
|
3792
3089
|
return ENDPOINTS.map((ep) => {
|
|
@@ -3831,15 +3128,12 @@ async function connectEndpointInteractive({
|
|
|
3831
3128
|
const status = cloudAuthStatus({ backend, apiUrl, env, platform });
|
|
3832
3129
|
const destination = label || envLabel(status.apiUrl);
|
|
3833
3130
|
if (status.authenticated) {
|
|
3834
|
-
|
|
3835
|
-
`);
|
|
3131
|
+
print(col.gray(` Connecting to ${destination}\u2026`));
|
|
3836
3132
|
try {
|
|
3837
3133
|
await switchOrLoginEndpoint({ backend, apiUrl, env, platform, setupFn, activateFn });
|
|
3838
|
-
|
|
3839
|
-
`);
|
|
3134
|
+
print(col.green(` ${fig.check} ${successLabel || `Signed in to ${destination} successfully`}`));
|
|
3840
3135
|
} catch (error) {
|
|
3841
|
-
|
|
3842
|
-
`);
|
|
3136
|
+
print(col.red(` ${endpointErrorStatus(error)}`));
|
|
3843
3137
|
}
|
|
3844
3138
|
return { promptedInTui: true };
|
|
3845
3139
|
}
|
|
@@ -3869,26 +3163,15 @@ function buildLocalRunArgs({
|
|
|
3869
3163
|
inputJson || "{}"
|
|
3870
3164
|
];
|
|
3871
3165
|
}
|
|
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" };
|
|
3166
|
+
function stopSignalForAttempt(attempt = 0) {
|
|
3167
|
+
if (!Number.isFinite(attempt) || attempt <= 0) return "SIGINT";
|
|
3168
|
+
if (attempt === 1) return "SIGTERM";
|
|
3169
|
+
return "SIGKILL";
|
|
3887
3170
|
}
|
|
3888
3171
|
function helpText() {
|
|
3889
3172
|
return [
|
|
3890
3173
|
"",
|
|
3891
|
-
`
|
|
3174
|
+
` \u2191 VENDIAN CLI v${CLI_VERSION}`,
|
|
3892
3175
|
"",
|
|
3893
3176
|
" Usage:",
|
|
3894
3177
|
" vendian Open the interactive shell",
|
|
@@ -3904,7 +3187,6 @@ function helpText() {
|
|
|
3904
3187
|
" vendian init --output-dir ./agents",
|
|
3905
3188
|
' vendian create "My Agent" --output-dir ./agents',
|
|
3906
3189
|
" vendian validate ./agents/my-agent --runtime",
|
|
3907
|
-
" vendian test ./agents/my-agent --dry-run --mock-credentials",
|
|
3908
3190
|
" vendian models",
|
|
3909
3191
|
" vendian cloud local serve --agents-dir ./agents",
|
|
3910
3192
|
" vendian doctor",
|
|
@@ -3914,6 +3196,21 @@ function helpText() {
|
|
|
3914
3196
|
""
|
|
3915
3197
|
].join("\n");
|
|
3916
3198
|
}
|
|
3199
|
+
function friendlyName(raw) {
|
|
3200
|
+
return String(raw || "").replace(/.*[\\/]/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim() || String(raw || "");
|
|
3201
|
+
}
|
|
3202
|
+
function readManifestName(absoluteFolderPath) {
|
|
3203
|
+
if (!absoluteFolderPath) return null;
|
|
3204
|
+
for (const fname of ["manifest.yaml", "manifest.yml"]) {
|
|
3205
|
+
try {
|
|
3206
|
+
const content = fs11.readFileSync(path8.join(absoluteFolderPath, fname), "utf8");
|
|
3207
|
+
const m = content.match(/^name\s*:\s*['"]?([^'"\n#]+?)['"]?\s*$/m);
|
|
3208
|
+
if (m?.[1]) return m[1].trim();
|
|
3209
|
+
} catch {
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
return null;
|
|
3213
|
+
}
|
|
3917
3214
|
function clip(value, length) {
|
|
3918
3215
|
const text = String(value || "");
|
|
3919
3216
|
return text.length <= length ? text : `${text.slice(0, Math.max(0, length - 1))}\u2026`;
|
|
@@ -3921,6 +3218,26 @@ function clip(value, length) {
|
|
|
3921
3218
|
function errMsg(error) {
|
|
3922
3219
|
return error && typeof error.message === "string" ? error.message : String(error);
|
|
3923
3220
|
}
|
|
3221
|
+
function fmtErr(error) {
|
|
3222
|
+
if (!error) return "";
|
|
3223
|
+
return typeof error === "object" ? error.message || "error" : String(error);
|
|
3224
|
+
}
|
|
3225
|
+
function shortId2(value) {
|
|
3226
|
+
const text = String(value || "");
|
|
3227
|
+
return text ? text.slice(0, 8) : "";
|
|
3228
|
+
}
|
|
3229
|
+
function formatLogTime(ts) {
|
|
3230
|
+
if (!ts) return " ";
|
|
3231
|
+
try {
|
|
3232
|
+
const d = new Date(ts);
|
|
3233
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
3234
|
+
} catch {
|
|
3235
|
+
return " ";
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
function pad2(n) {
|
|
3239
|
+
return String(n).padStart(2, "0");
|
|
3240
|
+
}
|
|
3924
3241
|
|
|
3925
3242
|
// src/main.js
|
|
3926
3243
|
function printHelp() {
|