4runr-os 2.4.0 → 2.4.2
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/mk3-tui/src/app.rs +111 -47
- package/mk3-tui/src/main.rs +42 -7
- package/mk3-tui/src/screens/mod.rs +3 -1
- package/mk3-tui/src/storage/cache.rs +213 -213
- package/mk3-tui/src/storage/mod.rs +6 -6
- package/mk3-tui/src/ui/agent_builder.rs +921 -921
- package/mk3-tui/src/ui/agent_list.rs +247 -0
- package/mk3-tui/src/ui/help.rs +353 -0
- package/mk3-tui/src/ui/layout.rs +3 -3
- package/mk3-tui/src/ui/mod.rs +2 -0
- package/mk3-tui/src/ui/run_manager.rs +676 -676
- package/mk3-tui/src/ui/settings.rs +362 -362
- package/mk3-tui/src/websocket.rs +303 -303
- package/package.json +2 -1
package/mk3-tui/src/app.rs
CHANGED
|
@@ -29,6 +29,35 @@ pub enum AppMode {
|
|
|
29
29
|
Main,
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
#[derive(Debug, Clone)]
|
|
33
|
+
pub struct AgentListState {
|
|
34
|
+
pub agents: Vec<AgentInfo>,
|
|
35
|
+
pub selected_index: usize,
|
|
36
|
+
pub detail_view: Option<usize>, // None = list view, Some(index) = detail popup
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Default for AgentListState {
|
|
40
|
+
fn default() -> Self {
|
|
41
|
+
Self {
|
|
42
|
+
agents: Vec::new(),
|
|
43
|
+
selected_index: 0,
|
|
44
|
+
detail_view: None,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#[derive(Debug, Clone)]
|
|
50
|
+
pub struct AgentInfo {
|
|
51
|
+
pub name: String,
|
|
52
|
+
pub description: Option<String>,
|
|
53
|
+
pub model: String,
|
|
54
|
+
pub provider: String,
|
|
55
|
+
pub system_prompt: Option<String>,
|
|
56
|
+
pub temperature: Option<f32>,
|
|
57
|
+
pub max_tokens: Option<u32>,
|
|
58
|
+
pub tools: Vec<String>,
|
|
59
|
+
}
|
|
60
|
+
|
|
32
61
|
#[derive(Debug, Clone)]
|
|
33
62
|
pub struct AppState {
|
|
34
63
|
// Navigation state (NEW - replaces simple mode)
|
|
@@ -96,6 +125,7 @@ pub struct AppState {
|
|
|
96
125
|
pub agent_builder: AgentBuilderState,
|
|
97
126
|
pub run_manager: RunManagerState,
|
|
98
127
|
pub settings: SettingsState,
|
|
128
|
+
pub agent_list: AgentListState,
|
|
99
129
|
|
|
100
130
|
// Local cache
|
|
101
131
|
pub cache: Option<Cache>,
|
|
@@ -153,6 +183,7 @@ impl Default for AppState {
|
|
|
153
183
|
agent_builder: AgentBuilderState::default(),
|
|
154
184
|
run_manager: RunManagerState::default(),
|
|
155
185
|
settings: SettingsState::default(),
|
|
186
|
+
agent_list: AgentListState::default(),
|
|
156
187
|
cache: Cache::new().ok(),
|
|
157
188
|
cache_loaded: false,
|
|
158
189
|
}
|
|
@@ -339,6 +370,11 @@ impl App {
|
|
|
339
370
|
return self.handle_settings_input(key);
|
|
340
371
|
}
|
|
341
372
|
|
|
373
|
+
// === AGENT LIST INPUT HANDLING ===
|
|
374
|
+
if self.state.navigation.current_screen() == &Screen::AgentList {
|
|
375
|
+
return self.handle_agent_list_input(key);
|
|
376
|
+
}
|
|
377
|
+
|
|
342
378
|
// === MAIN INPUT HANDLING ===
|
|
343
379
|
match key.code {
|
|
344
380
|
// Typing - ALL characters go to command input (with debounce)
|
|
@@ -362,48 +398,9 @@ impl App {
|
|
|
362
398
|
"quit" | "exit" => return Ok(true),
|
|
363
399
|
"clear" => self.state.logs.clear(),
|
|
364
400
|
"help" => {
|
|
365
|
-
|
|
366
|
-
self.
|
|
367
|
-
self.
|
|
368
|
-
self.state.logs.push_back("".into());
|
|
369
|
-
|
|
370
|
-
// Local Commands
|
|
371
|
-
self.state.logs.push_back("[HELP] Local Commands:".into());
|
|
372
|
-
self.state.logs.push_back(" quit, exit - Exit application".into());
|
|
373
|
-
self.state.logs.push_back(" clear - Clear logs".into());
|
|
374
|
-
self.state.logs.push_back(" help - Show this help".into());
|
|
375
|
-
self.state.logs.push_back(" :perf - Show performance stats".into());
|
|
376
|
-
self.state.logs.push_back("".into());
|
|
377
|
-
|
|
378
|
-
// Navigation Commands
|
|
379
|
-
self.state.logs.push_back("[HELP] Navigation Commands:".into());
|
|
380
|
-
self.state.logs.push_back(" build - Open Agent Builder (6-step wizard)".into());
|
|
381
|
-
self.state.logs.push_back(" runs - Open Run Manager (list, filter, sort)".into());
|
|
382
|
-
self.state.logs.push_back(" config, settings - Open Settings (mode, AI provider)".into());
|
|
383
|
-
self.state.logs.push_back(" ESC - Close overlay/popup or clear input".into());
|
|
384
|
-
self.state.logs.push_back("".into());
|
|
385
|
-
|
|
386
|
-
// WebSocket Commands - Label BEFORE commands with clear separator
|
|
387
|
-
self.state.logs.push_back("[HELP] ─────────────────────────────────────────".into());
|
|
388
|
-
self.state.logs.push_back("[HELP] WebSocket Commands (requires connection):".into());
|
|
389
|
-
self.state.logs.push_back("[HELP] ─────────────────────────────────────────".into());
|
|
390
|
-
self.state.logs.push_back(" agent.list - List all agents".into());
|
|
391
|
-
self.state.logs.push_back(" agent.get - Get agent details (data: {name})".into());
|
|
392
|
-
self.state.logs.push_back(" agent.create - Create agent (use Agent Builder)".into());
|
|
393
|
-
self.state.logs.push_back(" agent.delete - Delete agent (data: {name})".into());
|
|
394
|
-
self.state.logs.push_back(" system.status - Get system status".into());
|
|
395
|
-
self.state.logs.push_back(" run.list - List runs (requires gateway)".into());
|
|
396
|
-
self.state.logs.push_back(" tool.list - List available tools".into());
|
|
397
|
-
self.state.logs.push_back("".into());
|
|
398
|
-
|
|
399
|
-
// Screen Controls
|
|
400
|
-
self.state.logs.push_back("[HELP] Screen Controls:".into());
|
|
401
|
-
self.state.logs.push_back(" Agent Builder: Enter=Next, Backspace=Prev, ESC=Cancel".into());
|
|
402
|
-
self.state.logs.push_back(" Run Manager: ↑/↓=Navigate, F=Filter, S=Sort, R=Refresh".into());
|
|
403
|
-
self.state.logs.push_back(" Settings: ↑/↓=Navigate, Space=Toggle, Enter=Save".into());
|
|
404
|
-
self.state.logs.push_back("".into());
|
|
405
|
-
self.state.logs.push_back("[HELP] Press F12 to toggle performance overlay".into());
|
|
406
|
-
self.state.logs.push_back("[HELP] ═══════════════════════════════════════".into());
|
|
401
|
+
// Open help popup instead of logging to operations log
|
|
402
|
+
self.push_popup(Screen::Help);
|
|
403
|
+
self.request_render("help_command");
|
|
407
404
|
}
|
|
408
405
|
":perf" => {
|
|
409
406
|
// Perf self-check command
|
|
@@ -591,6 +588,10 @@ impl App {
|
|
|
591
588
|
use crate::ui::settings;
|
|
592
589
|
settings::render(f, &self.state);
|
|
593
590
|
}
|
|
591
|
+
Screen::AgentList => {
|
|
592
|
+
use crate::ui::agent_list;
|
|
593
|
+
agent_list::render(f, &self.state);
|
|
594
|
+
}
|
|
594
595
|
Screen::Confirmation { message, action } => {
|
|
595
596
|
// TODO: Implement confirmation popup
|
|
596
597
|
// For now, just render the base screen
|
|
@@ -606,10 +607,13 @@ impl App {
|
|
|
606
607
|
let _ = message; // Suppress warning
|
|
607
608
|
}
|
|
608
609
|
Screen::Help => {
|
|
609
|
-
//
|
|
610
|
-
// For now, just render the base screen
|
|
610
|
+
// Render base screen first (dimmed)
|
|
611
611
|
use crate::ui::layout;
|
|
612
612
|
layout::render(f, &self.state);
|
|
613
|
+
|
|
614
|
+
// Render help popup overlay
|
|
615
|
+
use crate::ui::help;
|
|
616
|
+
help::render_help(f, f.size(), &self.state);
|
|
613
617
|
}
|
|
614
618
|
}
|
|
615
619
|
}
|
|
@@ -1034,20 +1038,17 @@ impl App {
|
|
|
1034
1038
|
// Filter
|
|
1035
1039
|
KeyCode::Char('f') | KeyCode::Char('F') => {
|
|
1036
1040
|
self.state.run_manager.next_filter();
|
|
1037
|
-
self.add_log(format!("[RUN] Filter: {}", self.state.run_manager.filter.as_str()));
|
|
1038
1041
|
self.request_render("run_manager_filter");
|
|
1039
1042
|
}
|
|
1040
1043
|
|
|
1041
1044
|
// Sort
|
|
1042
1045
|
KeyCode::Char('s') | KeyCode::Char('S') => {
|
|
1043
1046
|
self.state.run_manager.next_sort();
|
|
1044
|
-
self.add_log(format!("[RUN] Sort: {}", self.state.run_manager.sort.as_str()));
|
|
1045
1047
|
self.request_render("run_manager_sort");
|
|
1046
1048
|
}
|
|
1047
1049
|
|
|
1048
1050
|
// Refresh
|
|
1049
1051
|
KeyCode::Char('r') | KeyCode::Char('R') => {
|
|
1050
|
-
self.add_log("[RUN] Refreshing run list...".to_string());
|
|
1051
1052
|
// TODO: Send run.list command via WebSocket
|
|
1052
1053
|
self.request_render("run_manager_refresh");
|
|
1053
1054
|
}
|
|
@@ -1111,6 +1112,69 @@ impl App {
|
|
|
1111
1112
|
Ok(false)
|
|
1112
1113
|
}
|
|
1113
1114
|
|
|
1115
|
+
// ============================================================
|
|
1116
|
+
// AGENT LIST INPUT HANDLING (Step 4)
|
|
1117
|
+
// ============================================================
|
|
1118
|
+
|
|
1119
|
+
/// Handle input when Agent List screen is active
|
|
1120
|
+
fn handle_agent_list_input(&mut self, key: KeyEvent) -> anyhow::Result<bool> {
|
|
1121
|
+
use crossterm::event::KeyModifiers;
|
|
1122
|
+
|
|
1123
|
+
// Ctrl+C/Q to exit
|
|
1124
|
+
if key.modifiers.contains(KeyModifiers::CONTROL) {
|
|
1125
|
+
match key.code {
|
|
1126
|
+
KeyCode::Char('c') | KeyCode::Char('q') => return Ok(true),
|
|
1127
|
+
_ => return Ok(false),
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
match key.code {
|
|
1132
|
+
// Navigation
|
|
1133
|
+
KeyCode::Up => {
|
|
1134
|
+
if self.state.agent_list.selected_index > 0 {
|
|
1135
|
+
self.state.agent_list.selected_index -= 1;
|
|
1136
|
+
}
|
|
1137
|
+
self.request_render("agent_list_up");
|
|
1138
|
+
}
|
|
1139
|
+
KeyCode::Down => {
|
|
1140
|
+
let max = self.state.agent_list.agents.len().saturating_sub(1);
|
|
1141
|
+
if self.state.agent_list.selected_index < max {
|
|
1142
|
+
self.state.agent_list.selected_index += 1;
|
|
1143
|
+
}
|
|
1144
|
+
self.request_render("agent_list_down");
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// View details / Close detail view
|
|
1148
|
+
KeyCode::Enter => {
|
|
1149
|
+
if self.state.agent_list.detail_view.is_some() {
|
|
1150
|
+
// Close detail view
|
|
1151
|
+
self.state.agent_list.detail_view = None;
|
|
1152
|
+
} else {
|
|
1153
|
+
// Open detail view for selected agent
|
|
1154
|
+
self.state.agent_list.detail_view = Some(self.state.agent_list.selected_index);
|
|
1155
|
+
}
|
|
1156
|
+
self.request_render("agent_list_toggle_detail");
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Close
|
|
1160
|
+
KeyCode::Esc => {
|
|
1161
|
+
if self.state.agent_list.detail_view.is_some() {
|
|
1162
|
+
// Close detail popup first
|
|
1163
|
+
self.state.agent_list.detail_view = None;
|
|
1164
|
+
self.request_render("agent_list_close_detail");
|
|
1165
|
+
} else {
|
|
1166
|
+
// Close agent list
|
|
1167
|
+
self.pop_overlay();
|
|
1168
|
+
self.request_render("agent_list_close");
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
_ => {}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
Ok(false)
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1114
1178
|
// ============================================================
|
|
1115
1179
|
// SETTINGS INPUT HANDLING (Step 4.8)
|
|
1116
1180
|
// ============================================================
|
package/mk3-tui/src/main.rs
CHANGED
|
@@ -151,12 +151,35 @@ fn main() -> Result<()> {
|
|
|
151
151
|
})
|
|
152
152
|
.collect();
|
|
153
153
|
|
|
154
|
+
// Parse agents into AgentInfo structs for Agent List viewer
|
|
155
|
+
use crate::app::AgentInfo;
|
|
156
|
+
let agents: Vec<AgentInfo> = agents_array.iter()
|
|
157
|
+
.filter_map(|agent| {
|
|
158
|
+
let obj = agent.as_object()?;
|
|
159
|
+
Some(AgentInfo {
|
|
160
|
+
name: obj.get("name")?.as_str()?.to_string(),
|
|
161
|
+
description: obj.get("description").and_then(|d| d.as_str()).map(|s| s.to_string()),
|
|
162
|
+
model: obj.get("model").and_then(|m| m.as_str()).unwrap_or("unknown").to_string(),
|
|
163
|
+
provider: obj.get("provider").and_then(|p| p.as_str()).unwrap_or("unknown").to_string(),
|
|
164
|
+
system_prompt: obj.get("systemPrompt").and_then(|sp| sp.as_str()).map(|s| s.to_string()),
|
|
165
|
+
temperature: obj.get("temperature").and_then(|t| t.as_f64()).map(|f| f as f32),
|
|
166
|
+
max_tokens: obj.get("maxTokens").and_then(|mt| mt.as_u64()).map(|u| u as u32),
|
|
167
|
+
tools: obj.get("tools")
|
|
168
|
+
.and_then(|t| t.as_array())
|
|
169
|
+
.map(|arr| arr.iter()
|
|
170
|
+
.filter_map(|v| v.as_str().map(|s| s.to_string()))
|
|
171
|
+
.collect())
|
|
172
|
+
.unwrap_or_default(),
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
.collect();
|
|
176
|
+
|
|
154
177
|
// Update cache with agent data
|
|
155
178
|
if let Some(cache) = &mut app.state.cache {
|
|
156
179
|
use crate::storage::cache::AgentData;
|
|
157
180
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
158
181
|
|
|
159
|
-
let
|
|
182
|
+
let cache_agents: Vec<AgentData> = agents_array.iter()
|
|
160
183
|
.filter_map(|agent| {
|
|
161
184
|
let obj = agent.as_object()?;
|
|
162
185
|
Some(AgentData {
|
|
@@ -169,14 +192,26 @@ fn main() -> Result<()> {
|
|
|
169
192
|
})
|
|
170
193
|
.collect();
|
|
171
194
|
|
|
172
|
-
let _ = cache.update_agents(
|
|
195
|
+
let _ = cache.update_agents(cache_agents);
|
|
173
196
|
}
|
|
174
197
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
198
|
+
// Update agent list state
|
|
199
|
+
app.state.agent_list.agents = agents;
|
|
200
|
+
app.state.agent_list.selected_index = 0;
|
|
201
|
+
app.state.agent_list.detail_view = None;
|
|
202
|
+
|
|
203
|
+
// Open AgentList overlay
|
|
204
|
+
app.push_overlay(Screen::AgentList);
|
|
205
|
+
app.request_render("agent_list_opened");
|
|
206
|
+
|
|
207
|
+
// Only log if agents were actually loaded
|
|
208
|
+
if agents_array.len() > 0 {
|
|
209
|
+
app.add_log(format!(
|
|
210
|
+
"✓ [{}] Loaded {} agents",
|
|
211
|
+
short_id,
|
|
212
|
+
agents_array.len()
|
|
213
|
+
));
|
|
214
|
+
}
|
|
180
215
|
}
|
|
181
216
|
} else {
|
|
182
217
|
app.add_log(format!("✓ [{}] Success", short_id));
|
|
@@ -19,6 +19,7 @@ pub enum Screen {
|
|
|
19
19
|
AgentBuilder,
|
|
20
20
|
RunManager,
|
|
21
21
|
Settings,
|
|
22
|
+
AgentList,
|
|
22
23
|
|
|
23
24
|
// Popup screens (small overlays)
|
|
24
25
|
Confirmation { message: String, action: String },
|
|
@@ -31,7 +32,7 @@ impl Screen {
|
|
|
31
32
|
pub fn is_overlay(&self) -> bool {
|
|
32
33
|
matches!(
|
|
33
34
|
self,
|
|
34
|
-
Screen::AgentBuilder | Screen::RunManager | Screen::Settings
|
|
35
|
+
Screen::AgentBuilder | Screen::RunManager | Screen::Settings | Screen::AgentList
|
|
35
36
|
)
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -56,6 +57,7 @@ impl Screen {
|
|
|
56
57
|
Screen::AgentBuilder => "Agent Builder",
|
|
57
58
|
Screen::RunManager => "Run Manager",
|
|
58
59
|
Screen::Settings => "Settings",
|
|
60
|
+
Screen::AgentList => "Agent List",
|
|
59
61
|
Screen::Confirmation { .. } => "Confirmation",
|
|
60
62
|
Screen::Alert { .. } => "Alert",
|
|
61
63
|
Screen::Help => "Help",
|