@vendian/cli 0.0.27 → 0.0.29
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/README.md +4 -1
- package/cli-wrapper.mjs +393 -83
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ vendian
|
|
|
8
8
|
vendian login
|
|
9
9
|
vendian init --output-dir ./agents
|
|
10
10
|
vendian create "My Agent" --output-dir ./agents
|
|
11
|
+
vendian cloud local run --collection-id YOUR_COLLECTION --path ./agents/my-agent --input-json '{}'
|
|
11
12
|
vendian cloud local serve --agents-dir ./agents
|
|
12
13
|
```
|
|
13
14
|
|
|
@@ -23,11 +24,13 @@ vendian create "My Agent" --output-dir ./agents
|
|
|
23
24
|
vendian validate ./agents/my-agent --runtime
|
|
24
25
|
vendian test ./agents/my-agent --dry-run --mock-credentials
|
|
25
26
|
vendian models
|
|
27
|
+
vendian cloud local run --collection-id YOUR_COLLECTION --path ./agents/my-agent --input-json '{}'
|
|
26
28
|
vendian cloud local serve --agents-dir ./agents
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
Run `vendian` with no arguments to open the interactive menu. It shows your
|
|
30
|
-
current endpoint connections and guides common local workflows
|
|
32
|
+
current endpoint connections and guides common local workflows, including
|
|
33
|
+
running one local agent immediately or starting the long-lived local daemon.
|
|
31
34
|
|
|
32
35
|
`vendian login` opens a browser sign-in, prepares the local runtime, and stores
|
|
33
36
|
the credentials needed by the CLI for the selected endpoint. `vendian setup` is
|
package/cli-wrapper.mjs
CHANGED
|
@@ -1107,7 +1107,7 @@ import path8 from "node:path";
|
|
|
1107
1107
|
import readlinePromises from "node:readline/promises";
|
|
1108
1108
|
|
|
1109
1109
|
// src/version.js
|
|
1110
|
-
var CLI_VERSION = true ? "0.0.
|
|
1110
|
+
var CLI_VERSION = true ? "0.0.29" : 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;
|
|
@@ -2399,6 +2399,87 @@ async function loadTerm() {
|
|
|
2399
2399
|
}
|
|
2400
2400
|
var HEADER_ROWS_BASE = 7;
|
|
2401
2401
|
var SERVE_CONTENT_ROW = 10;
|
|
2402
|
+
function clampListIndex(index, count) {
|
|
2403
|
+
if (!Number.isFinite(count) || count <= 0) return 0;
|
|
2404
|
+
if (!Number.isFinite(index)) return 0;
|
|
2405
|
+
return Math.max(0, Math.min(count - 1, index));
|
|
2406
|
+
}
|
|
2407
|
+
function centeredWindowStart(selectedIdx, visibleCount, totalCount) {
|
|
2408
|
+
if (visibleCount >= totalCount) return 0;
|
|
2409
|
+
const half = Math.floor(visibleCount / 2);
|
|
2410
|
+
const maxStart = Math.max(0, totalCount - visibleCount);
|
|
2411
|
+
return Math.max(0, Math.min(maxStart, selectedIdx - half));
|
|
2412
|
+
}
|
|
2413
|
+
function computeServeDashboardViewport({
|
|
2414
|
+
termHeight = 24,
|
|
2415
|
+
agentCount = 0,
|
|
2416
|
+
selectedIdx = 0,
|
|
2417
|
+
activityCount = 0,
|
|
2418
|
+
diagnosticsCount = 0
|
|
2419
|
+
} = {}) {
|
|
2420
|
+
const availableRows = Math.max(4, (termHeight || 24) - SERVE_CONTENT_ROW + 1);
|
|
2421
|
+
const clampedSelectedIdx = clampListIndex(selectedIdx, agentCount);
|
|
2422
|
+
const viewport = {
|
|
2423
|
+
selectedIdx: clampedSelectedIdx,
|
|
2424
|
+
showBlankAfterStatus: false,
|
|
2425
|
+
showAgentHeader: agentCount > 0,
|
|
2426
|
+
visibleAgentCount: 0,
|
|
2427
|
+
agentWindowStart: 0,
|
|
2428
|
+
showAgentHint: false,
|
|
2429
|
+
showAgentRange: false,
|
|
2430
|
+
visibleActivityCount: 0,
|
|
2431
|
+
showActivityHeader: false,
|
|
2432
|
+
visibleDiagnosticsCount: 0,
|
|
2433
|
+
showDiagnosticsHeader: false,
|
|
2434
|
+
showInlineErrors: (termHeight || 24) >= 24
|
|
2435
|
+
};
|
|
2436
|
+
let remaining = availableRows;
|
|
2437
|
+
remaining -= 1;
|
|
2438
|
+
remaining -= 1;
|
|
2439
|
+
if (agentCount > 0) {
|
|
2440
|
+
remaining -= 1;
|
|
2441
|
+
viewport.visibleAgentCount = 1;
|
|
2442
|
+
remaining -= 1;
|
|
2443
|
+
} else {
|
|
2444
|
+
remaining -= 1;
|
|
2445
|
+
}
|
|
2446
|
+
if (remaining > 0) {
|
|
2447
|
+
viewport.showBlankAfterStatus = true;
|
|
2448
|
+
remaining -= 1;
|
|
2449
|
+
}
|
|
2450
|
+
if (agentCount > 1 && remaining > 0) {
|
|
2451
|
+
const extraAgentRows = Math.min(agentCount - 1, remaining);
|
|
2452
|
+
viewport.visibleAgentCount += extraAgentRows;
|
|
2453
|
+
remaining -= extraAgentRows;
|
|
2454
|
+
}
|
|
2455
|
+
if (agentCount > 0) {
|
|
2456
|
+
viewport.agentWindowStart = centeredWindowStart(
|
|
2457
|
+
clampedSelectedIdx,
|
|
2458
|
+
viewport.visibleAgentCount,
|
|
2459
|
+
agentCount
|
|
2460
|
+
);
|
|
2461
|
+
viewport.showAgentHint = remaining > 0;
|
|
2462
|
+
if (viewport.showAgentHint) remaining -= 1;
|
|
2463
|
+
viewport.showAgentRange = viewport.visibleAgentCount > 0 && viewport.visibleAgentCount < agentCount;
|
|
2464
|
+
}
|
|
2465
|
+
if (activityCount > 0 && remaining > 1) {
|
|
2466
|
+
viewport.showActivityHeader = true;
|
|
2467
|
+
remaining -= 1;
|
|
2468
|
+
viewport.visibleActivityCount = Math.min(activityCount, remaining);
|
|
2469
|
+
remaining -= viewport.visibleActivityCount;
|
|
2470
|
+
}
|
|
2471
|
+
if (diagnosticsCount > 0 && remaining > 1) {
|
|
2472
|
+
viewport.showDiagnosticsHeader = true;
|
|
2473
|
+
remaining -= 1;
|
|
2474
|
+
viewport.visibleDiagnosticsCount = Math.min(diagnosticsCount, remaining);
|
|
2475
|
+
}
|
|
2476
|
+
return viewport;
|
|
2477
|
+
}
|
|
2478
|
+
function stopSignalForAttempt(attempt = 0) {
|
|
2479
|
+
if (!Number.isFinite(attempt) || attempt <= 0) return "SIGINT";
|
|
2480
|
+
if (attempt === 1) return "SIGTERM";
|
|
2481
|
+
return "SIGKILL";
|
|
2482
|
+
}
|
|
2402
2483
|
async function runTui({
|
|
2403
2484
|
env = process.env,
|
|
2404
2485
|
platform = process.platform,
|
|
@@ -2412,12 +2493,12 @@ async function runTui({
|
|
|
2412
2493
|
await refreshInteractiveManagedRuntime({ env, platform, output });
|
|
2413
2494
|
await loadTerm();
|
|
2414
2495
|
term.fullscreen(true);
|
|
2415
|
-
|
|
2496
|
+
setCursorVisible(false);
|
|
2416
2497
|
term.grabInput({ mouse: "motion" });
|
|
2417
2498
|
const cleanup = () => {
|
|
2418
2499
|
try {
|
|
2419
2500
|
term.grabInput(false);
|
|
2420
|
-
|
|
2501
|
+
setCursorVisible(true);
|
|
2421
2502
|
term.fullscreen(false);
|
|
2422
2503
|
} catch {
|
|
2423
2504
|
}
|
|
@@ -2451,6 +2532,9 @@ async function mainLoop({ env, platform }) {
|
|
|
2451
2532
|
while (true) {
|
|
2452
2533
|
let next;
|
|
2453
2534
|
switch (screen) {
|
|
2535
|
+
case "run":
|
|
2536
|
+
next = await showRun({ env, platform });
|
|
2537
|
+
break;
|
|
2454
2538
|
case "home":
|
|
2455
2539
|
next = await showHome({ env, platform });
|
|
2456
2540
|
break;
|
|
@@ -2532,6 +2616,7 @@ var HOME_ACTIONS = [
|
|
|
2532
2616
|
{ value: "connect", label: "Sign in", desc: "Connect to a different account or environment" },
|
|
2533
2617
|
{ value: "create", label: "Create Agent", desc: "Build a new agent from a template" },
|
|
2534
2618
|
{ value: "commands", label: "Commands", desc: "Show all available CLI commands" },
|
|
2619
|
+
{ value: "run", label: "Run once", desc: "Execute one local agent immediately" },
|
|
2535
2620
|
{ value: "exit", label: "Exit", desc: "" }
|
|
2536
2621
|
];
|
|
2537
2622
|
async function showHome({ env, platform }) {
|
|
@@ -2628,6 +2713,41 @@ async function showCreate({ env, platform }) {
|
|
|
2628
2713
|
);
|
|
2629
2714
|
return "home";
|
|
2630
2715
|
}
|
|
2716
|
+
async function showRun({ env, platform }) {
|
|
2717
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2718
|
+
const picked = await pickSingleAgentToRun({ env, platform, candidates });
|
|
2719
|
+
if (!picked) return "home";
|
|
2720
|
+
const workspace = await chooseCloudWorkspace({
|
|
2721
|
+
env,
|
|
2722
|
+
platform,
|
|
2723
|
+
loadingTitle: " Getting ready to run this agent\u2026",
|
|
2724
|
+
pickerTitle: " Which project should own this run?\n\n"
|
|
2725
|
+
});
|
|
2726
|
+
if (!workspace) return "home";
|
|
2727
|
+
term.clear();
|
|
2728
|
+
drawHeader({ env, platform });
|
|
2729
|
+
term("\n");
|
|
2730
|
+
term.bold(` Run ${picked.displayName}
|
|
2731
|
+
|
|
2732
|
+
`);
|
|
2733
|
+
term.gray(` Agent: ${picked.path}
|
|
2734
|
+
`);
|
|
2735
|
+
term.gray(` Project: ${workspace.label}
|
|
2736
|
+
|
|
2737
|
+
`);
|
|
2738
|
+
term.gray(" Input JSON: ");
|
|
2739
|
+
const inputJson = await term.inputField({ cancelable: true, style: term.cyan, default: "{}" }).promise;
|
|
2740
|
+
if (inputJson === null) return "home";
|
|
2741
|
+
await withOutputMode(
|
|
2742
|
+
`Running ${picked.displayName}`,
|
|
2743
|
+
() => forwardToPythonVendian(buildLocalRunArgs({
|
|
2744
|
+
agentPath: picked.path,
|
|
2745
|
+
collectionId: workspace.id,
|
|
2746
|
+
inputJson: inputJson || "{}"
|
|
2747
|
+
}), { env, platform })
|
|
2748
|
+
);
|
|
2749
|
+
return "home";
|
|
2750
|
+
}
|
|
2631
2751
|
var COMMAND_GROUPS = [
|
|
2632
2752
|
{ section: "Getting started", commands: [
|
|
2633
2753
|
{ cmd: "vendian login", desc: "Sign in and set up your computer" },
|
|
@@ -2640,6 +2760,7 @@ var COMMAND_GROUPS = [
|
|
|
2640
2760
|
{ cmd: "vendian models", desc: "List available AI models" }
|
|
2641
2761
|
] },
|
|
2642
2762
|
{ section: "Running agents", commands: [
|
|
2763
|
+
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one local agent once" },
|
|
2643
2764
|
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents (advanced mode)" },
|
|
2644
2765
|
{ cmd: "vendian login --backend staging", desc: "Sign in to staging environment" }
|
|
2645
2766
|
] },
|
|
@@ -2680,57 +2801,14 @@ async function showServe({ env, platform }) {
|
|
|
2680
2801
|
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2681
2802
|
if (!picked) return "home";
|
|
2682
2803
|
const { agentsDir } = picked;
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
wsResult = await listCloudWorkspaces({
|
|
2692
|
-
env,
|
|
2693
|
-
platform,
|
|
2694
|
-
onProgress: (msg) => {
|
|
2695
|
-
term.moveTo(1, HEADER_ROWS_BASE + 3);
|
|
2696
|
-
term.eraseLine();
|
|
2697
|
-
term.gray(` ${clip(msg, 60)}
|
|
2698
|
-
`);
|
|
2699
|
-
}
|
|
2700
|
-
});
|
|
2701
|
-
} catch (error) {
|
|
2702
|
-
term.red(`
|
|
2703
|
-
Couldn't connect to the server: ${errMsg(error)}
|
|
2704
|
-
`);
|
|
2705
|
-
term.gray(" Make sure you are signed in (choose Sign in from the main menu).\n");
|
|
2706
|
-
await pressEnterToContinue({ grabActive: false });
|
|
2707
|
-
return "home";
|
|
2708
|
-
}
|
|
2709
|
-
if (!wsResult.ok) {
|
|
2710
|
-
term.red(`
|
|
2711
|
-
${fig.cross} ${wsResult.error}
|
|
2712
|
-
`);
|
|
2713
|
-
await pressEnterToContinue({ grabActive: false });
|
|
2714
|
-
return "home";
|
|
2715
|
-
}
|
|
2716
|
-
let collectionId = wsResult.workspaces[0]?.id || "";
|
|
2717
|
-
if (wsResult.workspaces.length > 1) {
|
|
2718
|
-
term.clear();
|
|
2719
|
-
drawHeader({ env, platform });
|
|
2720
|
-
term("\n");
|
|
2721
|
-
term.bold(" Which project do you want to run?\n\n");
|
|
2722
|
-
const wsItems = [...wsResult.workspaces.map((w) => workspaceLabel(w)), "\u2190 Back"];
|
|
2723
|
-
const wsResp = await term.singleColumnMenu(wsItems, {
|
|
2724
|
-
cancelable: true,
|
|
2725
|
-
style: term.gray,
|
|
2726
|
-
selectedStyle: term.bgCyan.black.bold,
|
|
2727
|
-
leftPadding: " ",
|
|
2728
|
-
selectedLeftPadding: " \u203A "
|
|
2729
|
-
}).promise;
|
|
2730
|
-
if (wsResp.canceled || wsResp.selectedIndex === wsItems.length - 1) return "home";
|
|
2731
|
-
collectionId = wsResult.workspaces[wsResp.selectedIndex]?.id || "";
|
|
2732
|
-
}
|
|
2733
|
-
return await runServeDashboard({ env, platform, agentsDir, collectionId });
|
|
2804
|
+
const workspace = await chooseCloudWorkspace({
|
|
2805
|
+
env,
|
|
2806
|
+
platform,
|
|
2807
|
+
loadingTitle: " Getting ready to run your agents\u2026",
|
|
2808
|
+
pickerTitle: " Which project do you want to run?\n\n"
|
|
2809
|
+
});
|
|
2810
|
+
if (!workspace) return "home";
|
|
2811
|
+
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id });
|
|
2734
2812
|
}
|
|
2735
2813
|
async function pickAgentsToRun({ env, platform, candidates }) {
|
|
2736
2814
|
if (candidates.length === 0) {
|
|
@@ -2804,6 +2882,107 @@ async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
|
2804
2882
|
if (resp.selectedIndex === 0) return { agentsDir: candidate.path };
|
|
2805
2883
|
return { agentsDir: named[resp.selectedIndex - 1].path };
|
|
2806
2884
|
}
|
|
2885
|
+
async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
2886
|
+
if (candidates.length === 0) {
|
|
2887
|
+
term.clear();
|
|
2888
|
+
drawHeader({ env, platform });
|
|
2889
|
+
term("\n");
|
|
2890
|
+
term.bold(" I couldn't find any agents on your computer.\n\n");
|
|
2891
|
+
term.gray(" Enter the path to one agent folder:\n\n");
|
|
2892
|
+
term.gray(" Folder: ");
|
|
2893
|
+
const input = await term.inputField({ cancelable: true, style: term.cyan, default: "./agents/my-agent" }).promise;
|
|
2894
|
+
if (input === null) return null;
|
|
2895
|
+
const agentPath = input || "./agents/my-agent";
|
|
2896
|
+
return { path: agentPath, displayName: friendlyName(path8.basename(agentPath) || "Agent") };
|
|
2897
|
+
}
|
|
2898
|
+
const agents = candidates.flatMap((candidate) => {
|
|
2899
|
+
const folders = findAgentFolders(candidate.absolutePath);
|
|
2900
|
+
if (folders.length > 0) {
|
|
2901
|
+
return folders.map((folder) => ({
|
|
2902
|
+
path: folder.path,
|
|
2903
|
+
displayName: readManifestName(folder.absolutePath) || friendlyName(folder.name || path8.basename(folder.path))
|
|
2904
|
+
}));
|
|
2905
|
+
}
|
|
2906
|
+
return [{
|
|
2907
|
+
path: candidate.path,
|
|
2908
|
+
displayName: readManifestName(candidate.absolutePath) || friendlyName(path8.basename(candidate.absolutePath) || candidate.path)
|
|
2909
|
+
}];
|
|
2910
|
+
});
|
|
2911
|
+
if (agents.length === 1) return agents[0];
|
|
2912
|
+
term.clear();
|
|
2913
|
+
drawHeader({ env, platform });
|
|
2914
|
+
term("\n");
|
|
2915
|
+
term.bold(" Which agent do you want to run now?\n\n");
|
|
2916
|
+
const items = [...agents.map((agent) => `${agent.displayName.padEnd(32)} ${agent.path}`), "\u2190 Back"];
|
|
2917
|
+
const resp = await term.singleColumnMenu(items, {
|
|
2918
|
+
cancelable: true,
|
|
2919
|
+
style: term.gray,
|
|
2920
|
+
selectedStyle: term.bgCyan.black.bold,
|
|
2921
|
+
leftPadding: " ",
|
|
2922
|
+
selectedLeftPadding: " \u203A "
|
|
2923
|
+
}).promise;
|
|
2924
|
+
if (resp.canceled || resp.selectedIndex === items.length - 1) return null;
|
|
2925
|
+
return agents[resp.selectedIndex];
|
|
2926
|
+
}
|
|
2927
|
+
async function chooseCloudWorkspace({
|
|
2928
|
+
env,
|
|
2929
|
+
platform,
|
|
2930
|
+
loadingTitle,
|
|
2931
|
+
pickerTitle
|
|
2932
|
+
} = {}) {
|
|
2933
|
+
term.clear();
|
|
2934
|
+
drawHeader({ env, platform });
|
|
2935
|
+
term("\n");
|
|
2936
|
+
term.bold(`${loadingTitle}
|
|
2937
|
+
|
|
2938
|
+
`);
|
|
2939
|
+
let wsResult;
|
|
2940
|
+
try {
|
|
2941
|
+
wsResult = await listCloudWorkspaces({
|
|
2942
|
+
env,
|
|
2943
|
+
platform,
|
|
2944
|
+
onProgress: (msg) => {
|
|
2945
|
+
term.moveTo(1, HEADER_ROWS_BASE + 3);
|
|
2946
|
+
term.eraseLine();
|
|
2947
|
+
term.gray(` ${clip(msg, 60)}
|
|
2948
|
+
`);
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
term.red(`
|
|
2953
|
+
Couldn't connect to the server: ${errMsg(error)}
|
|
2954
|
+
`);
|
|
2955
|
+
term.gray(" Make sure you are signed in (choose Sign in from the main menu).\n");
|
|
2956
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2957
|
+
return null;
|
|
2958
|
+
}
|
|
2959
|
+
if (!wsResult.ok) {
|
|
2960
|
+
term.red(`
|
|
2961
|
+
${fig.cross} ${wsResult.error}
|
|
2962
|
+
`);
|
|
2963
|
+
await pressEnterToContinue({ grabActive: false });
|
|
2964
|
+
return null;
|
|
2965
|
+
}
|
|
2966
|
+
if (wsResult.workspaces.length <= 1) {
|
|
2967
|
+
const workspace2 = wsResult.workspaces[0];
|
|
2968
|
+
return workspace2 ? { id: workspace2.id || "", label: workspaceLabel(workspace2) } : null;
|
|
2969
|
+
}
|
|
2970
|
+
term.clear();
|
|
2971
|
+
drawHeader({ env, platform });
|
|
2972
|
+
term("\n");
|
|
2973
|
+
term.bold(pickerTitle);
|
|
2974
|
+
const wsItems = [...wsResult.workspaces.map((w) => workspaceLabel(w)), "\u2190 Back"];
|
|
2975
|
+
const wsResp = await term.singleColumnMenu(wsItems, {
|
|
2976
|
+
cancelable: true,
|
|
2977
|
+
style: term.gray,
|
|
2978
|
+
selectedStyle: term.bgCyan.black.bold,
|
|
2979
|
+
leftPadding: " ",
|
|
2980
|
+
selectedLeftPadding: " \u203A "
|
|
2981
|
+
}).promise;
|
|
2982
|
+
if (wsResp.canceled || wsResp.selectedIndex === wsItems.length - 1) return null;
|
|
2983
|
+
const workspace = wsResult.workspaces[wsResp.selectedIndex];
|
|
2984
|
+
return { id: workspace?.id || "", label: workspaceLabel(workspace) };
|
|
2985
|
+
}
|
|
2807
2986
|
async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
2808
2987
|
let state = initialServeState();
|
|
2809
2988
|
let child = null;
|
|
@@ -2814,6 +2993,10 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2814
2993
|
let dashboardClosed = false;
|
|
2815
2994
|
let exitPromptActive = false;
|
|
2816
2995
|
let selectedIdx = 0;
|
|
2996
|
+
let pressedAgentIdx = null;
|
|
2997
|
+
let stopRequested = false;
|
|
2998
|
+
let stopSignalAttempt = 0;
|
|
2999
|
+
let stopTimer = null;
|
|
2817
3000
|
const agentRowMap = /* @__PURE__ */ new Map();
|
|
2818
3001
|
function agentDisplayList() {
|
|
2819
3002
|
return state.agents.map((agent, i) => {
|
|
@@ -2841,12 +3024,26 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2841
3024
|
}
|
|
2842
3025
|
function redraw() {
|
|
2843
3026
|
if (dashboardClosed || overlayActive) return;
|
|
3027
|
+
const agents = agentDisplayList();
|
|
3028
|
+
const activity = recentActivityLines();
|
|
3029
|
+
const diagnostics = daemonDebugLines();
|
|
3030
|
+
const viewport = computeServeDashboardViewport({
|
|
3031
|
+
termHeight: term.height || 24,
|
|
3032
|
+
agentCount: agents.length,
|
|
3033
|
+
selectedIdx,
|
|
3034
|
+
activityCount: activity.length,
|
|
3035
|
+
diagnosticsCount: diagnostics.length
|
|
3036
|
+
});
|
|
3037
|
+
selectedIdx = viewport.selectedIdx;
|
|
2844
3038
|
drawHeader({ env, platform, serveState: state });
|
|
2845
3039
|
term.moveTo(1, SERVE_CONTENT_ROW);
|
|
2846
3040
|
term.eraseDisplayBelow();
|
|
2847
3041
|
agentRowMap.clear();
|
|
2848
3042
|
if (startupError) {
|
|
2849
3043
|
term.red(` ${fig.cross} ${clip(startupError, (term.width || 80) - 6)}
|
|
3044
|
+
`);
|
|
3045
|
+
} else if (stopRequested) {
|
|
3046
|
+
term.yellow(` ${fig.warning} ${clip(state.activity || "Stopping your agents\u2026", (term.width || 80) - 6)}
|
|
2850
3047
|
`);
|
|
2851
3048
|
} else if (state.stopped) {
|
|
2852
3049
|
term.yellow(` ${fig.warning} Your agents have stopped.
|
|
@@ -2860,12 +3057,15 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2860
3057
|
term.gray(`${clip(state.activity || "Starting up\u2026", 55)}
|
|
2861
3058
|
`);
|
|
2862
3059
|
}
|
|
2863
|
-
const agents = agentDisplayList();
|
|
2864
3060
|
if (agents.length > 0) {
|
|
2865
|
-
term("\n");
|
|
3061
|
+
if (viewport.showBlankAfterStatus) term("\n");
|
|
2866
3062
|
term.bold.gray(" Your agents:\n");
|
|
2867
|
-
let lineOffset = SERVE_CONTENT_ROW +
|
|
2868
|
-
|
|
3063
|
+
let lineOffset = SERVE_CONTENT_ROW + 2 + (viewport.showBlankAfterStatus ? 1 : 0);
|
|
3064
|
+
const visibleAgents = agents.slice(
|
|
3065
|
+
viewport.agentWindowStart,
|
|
3066
|
+
viewport.agentWindowStart + viewport.visibleAgentCount
|
|
3067
|
+
);
|
|
3068
|
+
for (const ag of visibleAgents) {
|
|
2869
3069
|
const isSelected = ag.num - 1 === selectedIdx;
|
|
2870
3070
|
agentRowMap.set(lineOffset, ag.num - 1);
|
|
2871
3071
|
const numStr = String(ag.num).padStart(2);
|
|
@@ -2916,26 +3116,32 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2916
3116
|
}
|
|
2917
3117
|
term("\n");
|
|
2918
3118
|
lineOffset++;
|
|
2919
|
-
if (ag.errMsg) {
|
|
3119
|
+
if (viewport.showInlineErrors && isSelected && ag.errMsg) {
|
|
2920
3120
|
term.red(` \u2514\u2500 ${clip(ag.errMsg, (term.width || 80) - 14)}
|
|
2921
3121
|
`);
|
|
2922
3122
|
lineOffset++;
|
|
2923
3123
|
}
|
|
2924
3124
|
}
|
|
2925
|
-
|
|
2926
|
-
|
|
3125
|
+
if (viewport.showAgentRange) {
|
|
3126
|
+
const first = viewport.agentWindowStart + 1;
|
|
3127
|
+
const last = viewport.agentWindowStart + visibleAgents.length;
|
|
3128
|
+
term.gray(` Showing agents ${first}\u2013${last} of ${agents.length}
|
|
2927
3129
|
`);
|
|
3130
|
+
}
|
|
3131
|
+
if (viewport.showAgentHint) {
|
|
3132
|
+
term.gray(` Click an agent or use \u2191\u2193 + Enter to see its activity log
|
|
3133
|
+
`);
|
|
3134
|
+
}
|
|
2928
3135
|
} else {
|
|
2929
|
-
term("\n");
|
|
3136
|
+
if (viewport.showBlankAfterStatus) term("\n");
|
|
2930
3137
|
term.gray(" Looking for your agents\u2026\n");
|
|
2931
3138
|
}
|
|
2932
|
-
|
|
2933
|
-
|
|
3139
|
+
if (viewport.showActivityHeader) {
|
|
3140
|
+
const visibleActivity = activity.slice(-viewport.visibleActivityCount);
|
|
2934
3141
|
const divW = Math.min((term.width || 80) - 4, 60);
|
|
2935
|
-
term("\n");
|
|
2936
3142
|
term.gray(` ${"\u2500".repeat(3)} Recent activity ${"\u2500".repeat(Math.max(0, divW - 18))}
|
|
2937
3143
|
`);
|
|
2938
|
-
for (const e of
|
|
3144
|
+
for (const e of visibleActivity) {
|
|
2939
3145
|
const t = formatLogTime(e.timestamp);
|
|
2940
3146
|
const name = clip(e._agentName || "Agent", 20);
|
|
2941
3147
|
const msg = clip(friendlyActivityLine(e), (term.width || 80) - 30);
|
|
@@ -2957,13 +3163,12 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2957
3163
|
}
|
|
2958
3164
|
}
|
|
2959
3165
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
3166
|
+
if (viewport.showDiagnosticsHeader) {
|
|
3167
|
+
const visibleDiagnostics = diagnostics.slice(-viewport.visibleDiagnosticsCount);
|
|
2962
3168
|
const divW = Math.min((term.width || 80) - 4, 72);
|
|
2963
|
-
term("\n");
|
|
2964
3169
|
term.gray(` ${"\u2500".repeat(3)} Daemon diagnostics ${"\u2500".repeat(Math.max(0, divW - 23))}
|
|
2965
3170
|
`);
|
|
2966
|
-
for (const entry of
|
|
3171
|
+
for (const entry of visibleDiagnostics) {
|
|
2967
3172
|
const t = formatLogTime(entry.timestamp);
|
|
2968
3173
|
const type = clip(entry.eventType || "event", 24).padEnd(24);
|
|
2969
3174
|
const msg = clip(entry.message || "", (term.width || 80) - 38);
|
|
@@ -2983,6 +3188,10 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
2983
3188
|
term("\n");
|
|
2984
3189
|
if (ctrlCArmed) {
|
|
2985
3190
|
term.yellow.bold(" Press Ctrl+C again to exit.\n");
|
|
3191
|
+
} else if (stopRequested) {
|
|
3192
|
+
term.yellow(" Stopping your agents\u2026");
|
|
3193
|
+
if (stopSignalAttempt > 0) term.gray(" (retrying shutdown)");
|
|
3194
|
+
term("\n");
|
|
2986
3195
|
} else {
|
|
2987
3196
|
term.gray(" ");
|
|
2988
3197
|
term.brightBlue.bold("S");
|
|
@@ -3039,9 +3248,11 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
3039
3248
|
}
|
|
3040
3249
|
return new Promise((resolve) => {
|
|
3041
3250
|
function openAgentLog(idx) {
|
|
3042
|
-
|
|
3251
|
+
const agents = agentDisplayList();
|
|
3252
|
+
const clampedIdx = clampListIndex(idx, agents.length);
|
|
3253
|
+
if (dashboardClosed || exitPromptActive || agents.length === 0 || idx !== clampedIdx) return;
|
|
3043
3254
|
overlayActive = true;
|
|
3044
|
-
const ag =
|
|
3255
|
+
const ag = agents[clampedIdx];
|
|
3045
3256
|
showAgentLog({ agent: ag, state, env, platform }).then(() => {
|
|
3046
3257
|
if (dashboardClosed) return;
|
|
3047
3258
|
overlayActive = false;
|
|
@@ -3056,14 +3267,62 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
3056
3267
|
ctrlCArmTimer = null;
|
|
3057
3268
|
}
|
|
3058
3269
|
}
|
|
3270
|
+
function clearStopTimer() {
|
|
3271
|
+
if (stopTimer) {
|
|
3272
|
+
clearTimeout(stopTimer);
|
|
3273
|
+
stopTimer = null;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
function childIsRunning() {
|
|
3277
|
+
return Boolean(child) && child.exitCode == null && child.signalCode == null;
|
|
3278
|
+
}
|
|
3279
|
+
function scheduleStopEscalation() {
|
|
3280
|
+
clearStopTimer();
|
|
3281
|
+
if (!childIsRunning()) return;
|
|
3282
|
+
stopTimer = setTimeout(() => {
|
|
3283
|
+
if (!stopRequested || dashboardClosed || !childIsRunning()) return;
|
|
3284
|
+
stopSignalAttempt += 1;
|
|
3285
|
+
const signal = stopSignalForAttempt(stopSignalAttempt);
|
|
3286
|
+
state = {
|
|
3287
|
+
...state,
|
|
3288
|
+
activity: signal === "SIGTERM" ? "Still stopping your agents\u2026" : "Force stopping your agents\u2026"
|
|
3289
|
+
};
|
|
3290
|
+
try {
|
|
3291
|
+
child.kill(signal);
|
|
3292
|
+
} catch {
|
|
3293
|
+
}
|
|
3294
|
+
if (!overlayActive) redraw();
|
|
3295
|
+
if (signal !== "SIGKILL") scheduleStopEscalation();
|
|
3296
|
+
}, 4e3);
|
|
3297
|
+
}
|
|
3298
|
+
function requestStop() {
|
|
3299
|
+
if (dashboardClosed) return;
|
|
3300
|
+
if (stopRequested) return;
|
|
3301
|
+
if (!childIsRunning()) {
|
|
3302
|
+
finish("home");
|
|
3303
|
+
return;
|
|
3304
|
+
}
|
|
3305
|
+
stopRequested = true;
|
|
3306
|
+
stopSignalAttempt = 0;
|
|
3307
|
+
state = { ...state, activity: "Stopping your agents\u2026" };
|
|
3308
|
+
clearCtrlCArm();
|
|
3309
|
+
try {
|
|
3310
|
+
child.kill(stopSignalForAttempt(stopSignalAttempt));
|
|
3311
|
+
} catch {
|
|
3312
|
+
}
|
|
3313
|
+
scheduleStopEscalation();
|
|
3314
|
+
if (!overlayActive) redraw();
|
|
3315
|
+
}
|
|
3059
3316
|
function detachDashboardHandlers() {
|
|
3060
3317
|
term.off("key", handleKey);
|
|
3061
3318
|
term.off("mouse", handleMouse);
|
|
3319
|
+
term.off("resize", handleResize);
|
|
3062
3320
|
}
|
|
3063
3321
|
function finish(next) {
|
|
3064
3322
|
if (dashboardClosed) return;
|
|
3065
3323
|
dashboardClosed = true;
|
|
3066
3324
|
clearCtrlCArm();
|
|
3325
|
+
clearStopTimer();
|
|
3067
3326
|
detachDashboardHandlers();
|
|
3068
3327
|
resolve(next);
|
|
3069
3328
|
}
|
|
@@ -3083,41 +3342,62 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
|
|
|
3083
3342
|
return;
|
|
3084
3343
|
}
|
|
3085
3344
|
if (overlayActive) return;
|
|
3086
|
-
|
|
3345
|
+
if (stopRequested) return;
|
|
3346
|
+
const agCount = agentDisplayList().length;
|
|
3087
3347
|
if (name >= "1" && name <= "9") {
|
|
3088
3348
|
openAgentLog(parseInt(name, 10) - 1);
|
|
3089
3349
|
return;
|
|
3090
3350
|
}
|
|
3091
3351
|
if (name === "UP" && agCount > 0) {
|
|
3092
|
-
selectedIdx = (selectedIdx - 1
|
|
3352
|
+
selectedIdx = clampListIndex(selectedIdx - 1, agCount);
|
|
3093
3353
|
redraw();
|
|
3094
3354
|
} else if (name === "DOWN" && agCount > 0) {
|
|
3095
|
-
selectedIdx = (selectedIdx + 1
|
|
3355
|
+
selectedIdx = clampListIndex(selectedIdx + 1, agCount);
|
|
3096
3356
|
redraw();
|
|
3097
3357
|
} else if (name === "ENTER") {
|
|
3098
3358
|
openAgentLog(selectedIdx);
|
|
3099
3359
|
} else if (name === "s" || name === "S") {
|
|
3100
|
-
|
|
3101
|
-
finish("home");
|
|
3360
|
+
requestStop();
|
|
3102
3361
|
}
|
|
3103
3362
|
}
|
|
3104
3363
|
function handleMouse(name, data) {
|
|
3105
|
-
if (overlayActive) return;
|
|
3364
|
+
if (overlayActive || stopRequested) return;
|
|
3106
3365
|
const idx = agentRowMap.get(data.y);
|
|
3107
|
-
if (idx === void 0) return;
|
|
3108
3366
|
if (name === "MOUSE_LEFT_BUTTON_PRESSED") {
|
|
3109
|
-
|
|
3110
|
-
|
|
3367
|
+
pressedAgentIdx = idx ?? null;
|
|
3368
|
+
if (idx !== void 0 && idx !== selectedIdx) {
|
|
3369
|
+
selectedIdx = idx;
|
|
3370
|
+
redraw();
|
|
3371
|
+
}
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
if (name === "MOUSE_LEFT_BUTTON_RELEASED") {
|
|
3375
|
+
const shouldOpen = idx !== void 0 && idx === pressedAgentIdx;
|
|
3376
|
+
pressedAgentIdx = null;
|
|
3377
|
+
if (shouldOpen) openAgentLog(idx);
|
|
3378
|
+
return;
|
|
3379
|
+
}
|
|
3380
|
+
if (idx === void 0) return;
|
|
3381
|
+
if (name !== "MOUSE_LEFT_BUTTON_RELEASED") {
|
|
3111
3382
|
if (idx !== selectedIdx) {
|
|
3112
3383
|
selectedIdx = idx;
|
|
3113
3384
|
redraw();
|
|
3114
3385
|
}
|
|
3115
3386
|
}
|
|
3116
3387
|
}
|
|
3388
|
+
function handleResize() {
|
|
3389
|
+
if (!overlayActive) redraw();
|
|
3390
|
+
}
|
|
3117
3391
|
term.on("key", handleKey);
|
|
3118
3392
|
term.on("mouse", handleMouse);
|
|
3393
|
+
term.on("resize", handleResize);
|
|
3119
3394
|
if (child) {
|
|
3120
3395
|
child.once("exit", () => {
|
|
3396
|
+
clearStopTimer();
|
|
3397
|
+
if (stopRequested) {
|
|
3398
|
+
finish("home");
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3121
3401
|
setTimeout(() => {
|
|
3122
3402
|
if (dashboardClosed || overlayActive) return;
|
|
3123
3403
|
exitPromptActive = true;
|
|
@@ -3350,7 +3630,7 @@ function attachServeChild(child, onUpdate, onExit, logStore = null) {
|
|
|
3350
3630
|
}
|
|
3351
3631
|
async function withOutputMode(label, fn) {
|
|
3352
3632
|
term.fullscreen(false);
|
|
3353
|
-
|
|
3633
|
+
setCursorVisible(true);
|
|
3354
3634
|
term.grabInput(false);
|
|
3355
3635
|
process.stdout.write(`
|
|
3356
3636
|
${label}
|
|
@@ -3366,7 +3646,7 @@ async function withOutputMode(label, fn) {
|
|
|
3366
3646
|
}
|
|
3367
3647
|
await pressEnterToContinue({ grabActive: false });
|
|
3368
3648
|
term.fullscreen(true);
|
|
3369
|
-
|
|
3649
|
+
setCursorVisible(false);
|
|
3370
3650
|
term.grabInput({ mouse: "motion" });
|
|
3371
3651
|
}
|
|
3372
3652
|
function waitForKey(keys = null) {
|
|
@@ -3380,6 +3660,19 @@ function waitForKey(keys = null) {
|
|
|
3380
3660
|
term.on("key", handler);
|
|
3381
3661
|
});
|
|
3382
3662
|
}
|
|
3663
|
+
function setCursorVisible(visible) {
|
|
3664
|
+
if (typeof term?.hideCursor === "function") {
|
|
3665
|
+
term.hideCursor(!visible);
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
if (visible && typeof term?.showCursor === "function") {
|
|
3669
|
+
term.showCursor();
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
if (!visible && typeof term?.hideCursor === "function") {
|
|
3673
|
+
term.hideCursor();
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3383
3676
|
async function pressEnterToContinue({ grabActive = true } = {}) {
|
|
3384
3677
|
if (grabActive) {
|
|
3385
3678
|
term("\n Press any key to continue\u2026");
|
|
@@ -3499,6 +3792,23 @@ function endpointErrorStatus(error) {
|
|
|
3499
3792
|
const message = error && typeof error.message === "string" ? error.message : String(error || "Connection failed");
|
|
3500
3793
|
return `${fig.cross} ${message}`;
|
|
3501
3794
|
}
|
|
3795
|
+
function buildLocalRunArgs({
|
|
3796
|
+
agentPath = ".",
|
|
3797
|
+
collectionId = "",
|
|
3798
|
+
inputJson = "{}"
|
|
3799
|
+
} = {}) {
|
|
3800
|
+
return [
|
|
3801
|
+
"cloud",
|
|
3802
|
+
"local",
|
|
3803
|
+
"run",
|
|
3804
|
+
"--collection-id",
|
|
3805
|
+
collectionId,
|
|
3806
|
+
"--path",
|
|
3807
|
+
agentPath || ".",
|
|
3808
|
+
"--input-json",
|
|
3809
|
+
inputJson || "{}"
|
|
3810
|
+
];
|
|
3811
|
+
}
|
|
3502
3812
|
function runtimeSummary({ env = process.env, platform = process.platform, now = Date.now() } = {}) {
|
|
3503
3813
|
const config = loadConfig(env, platform);
|
|
3504
3814
|
const venvPath = managedVenvPath(env, platform);
|