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
|
@@ -1,213 +1,213 @@
|
|
|
1
|
-
/// Cache implementation for local data storage
|
|
2
|
-
/// Stores agents, runs, and system status in JSON format
|
|
3
|
-
|
|
4
|
-
use serde::{Deserialize, Serialize};
|
|
5
|
-
use std::fs;
|
|
6
|
-
use std::path::PathBuf;
|
|
7
|
-
use std::time::{SystemTime, UNIX_EPOCH};
|
|
8
|
-
|
|
9
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
10
|
-
pub struct AgentData {
|
|
11
|
-
pub name: String,
|
|
12
|
-
pub description: Option<String>,
|
|
13
|
-
pub model: String,
|
|
14
|
-
pub provider: String,
|
|
15
|
-
pub created_at: u64,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
19
|
-
pub struct RunData {
|
|
20
|
-
pub id: String,
|
|
21
|
-
pub name: String,
|
|
22
|
-
pub agent: String,
|
|
23
|
-
pub status: String,
|
|
24
|
-
pub started_at: u64,
|
|
25
|
-
pub duration: Option<u64>,
|
|
26
|
-
pub tokens_used: Option<u64>,
|
|
27
|
-
pub cost: Option<f64>,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
31
|
-
pub struct SystemStatusData {
|
|
32
|
-
pub connected: bool,
|
|
33
|
-
pub gateway_url: Option<String>,
|
|
34
|
-
pub posture: String,
|
|
35
|
-
pub last_updated: u64,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
39
|
-
pub struct CacheData {
|
|
40
|
-
pub agents: Vec<AgentData>,
|
|
41
|
-
pub runs: Vec<RunData>,
|
|
42
|
-
pub system_status: Option<SystemStatusData>,
|
|
43
|
-
pub last_updated: u64,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
#[derive(Debug, Clone)]
|
|
47
|
-
pub struct Cache {
|
|
48
|
-
cache_path: PathBuf,
|
|
49
|
-
data: CacheData,
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
impl Cache {
|
|
53
|
-
/// Create a new cache instance
|
|
54
|
-
pub fn new() -> Result<Self, std::io::Error> {
|
|
55
|
-
let cache_dir = Self::get_cache_dir()?;
|
|
56
|
-
fs::create_dir_all(&cache_dir)?;
|
|
57
|
-
|
|
58
|
-
let cache_path = cache_dir.join("4runr_cache.json");
|
|
59
|
-
let data = Self::load_from_file(&cache_path)?;
|
|
60
|
-
|
|
61
|
-
Ok(Self { cache_path, data })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// Get the cache directory path
|
|
65
|
-
fn get_cache_dir() -> Result<PathBuf, std::io::Error> {
|
|
66
|
-
#[cfg(target_os = "windows")]
|
|
67
|
-
{
|
|
68
|
-
let appdata = std::env::var("APPDATA")
|
|
69
|
-
.unwrap_or_else(|_| String::from("C:\\Users\\Default\\AppData\\Roaming"));
|
|
70
|
-
Ok(PathBuf::from(appdata).join("4runr"))
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
#[cfg(not(target_os = "windows"))]
|
|
74
|
-
{
|
|
75
|
-
let home = std::env::var("HOME")
|
|
76
|
-
.unwrap_or_else(|_| String::from("/tmp"));
|
|
77
|
-
Ok(PathBuf::from(home).join(".4runr"))
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// Load cache from file
|
|
82
|
-
fn load_from_file(path: &PathBuf) -> Result<CacheData, std::io::Error> {
|
|
83
|
-
if !path.exists() {
|
|
84
|
-
return Ok(CacheData::default());
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let contents = fs::read_to_string(path)?;
|
|
88
|
-
let data: CacheData = serde_json::from_str(&contents)
|
|
89
|
-
.unwrap_or_default();
|
|
90
|
-
|
|
91
|
-
Ok(data)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/// Save cache to file
|
|
95
|
-
fn save_to_file(&self) -> Result<(), std::io::Error> {
|
|
96
|
-
let json = serde_json::to_string_pretty(&self.data)?;
|
|
97
|
-
fs::write(&self.cache_path, json)?;
|
|
98
|
-
Ok(())
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/// Get current timestamp
|
|
102
|
-
fn now() -> u64 {
|
|
103
|
-
SystemTime::now()
|
|
104
|
-
.duration_since(UNIX_EPOCH)
|
|
105
|
-
.unwrap()
|
|
106
|
-
.as_secs()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// === Agent Operations ===
|
|
110
|
-
|
|
111
|
-
/// Get all cached agents
|
|
112
|
-
pub fn get_agents(&self) -> Vec<AgentData> {
|
|
113
|
-
self.data.agents.clone()
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/// Update agents cache
|
|
117
|
-
pub fn update_agents(&mut self, agents: Vec<AgentData>) -> Result<(), std::io::Error> {
|
|
118
|
-
self.data.agents = agents;
|
|
119
|
-
self.data.last_updated = Self::now();
|
|
120
|
-
self.save_to_file()
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/// Add a single agent
|
|
124
|
-
pub fn add_agent(&mut self, agent: AgentData) -> Result<(), std::io::Error> {
|
|
125
|
-
self.data.agents.push(agent);
|
|
126
|
-
self.data.last_updated = Self::now();
|
|
127
|
-
self.save_to_file()
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/// Remove an agent by name
|
|
131
|
-
pub fn remove_agent(&mut self, name: &str) -> Result<(), std::io::Error> {
|
|
132
|
-
self.data.agents.retain(|a| a.name != name);
|
|
133
|
-
self.data.last_updated = Self::now();
|
|
134
|
-
self.save_to_file()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// === Run Operations ===
|
|
138
|
-
|
|
139
|
-
/// Get all cached runs
|
|
140
|
-
pub fn get_runs(&self) -> Vec<RunData> {
|
|
141
|
-
self.data.runs.clone()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/// Update runs cache
|
|
145
|
-
pub fn update_runs(&mut self, runs: Vec<RunData>) -> Result<(), std::io::Error> {
|
|
146
|
-
self.data.runs = runs;
|
|
147
|
-
self.data.last_updated = Self::now();
|
|
148
|
-
self.save_to_file()
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/// Add a single run
|
|
152
|
-
pub fn add_run(&mut self, run: RunData) -> Result<(), std::io::Error> {
|
|
153
|
-
self.data.runs.push(run);
|
|
154
|
-
self.data.last_updated = Self::now();
|
|
155
|
-
self.save_to_file()
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/// Update a run by ID
|
|
159
|
-
pub fn update_run(&mut self, id: &str, run: RunData) -> Result<(), std::io::Error> {
|
|
160
|
-
if let Some(existing) = self.data.runs.iter_mut().find(|r| r.id == id) {
|
|
161
|
-
*existing = run;
|
|
162
|
-
self.data.last_updated = Self::now();
|
|
163
|
-
self.save_to_file()?;
|
|
164
|
-
}
|
|
165
|
-
Ok(())
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// === System Status Operations ===
|
|
169
|
-
|
|
170
|
-
/// Get cached system status
|
|
171
|
-
pub fn get_system_status(&self) -> Option<SystemStatusData> {
|
|
172
|
-
self.data.system_status.clone()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/// Update system status cache
|
|
176
|
-
pub fn update_system_status(&mut self, status: SystemStatusData) -> Result<(), std::io::Error> {
|
|
177
|
-
self.data.system_status = Some(status);
|
|
178
|
-
self.data.last_updated = Self::now();
|
|
179
|
-
self.save_to_file()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// === Cache Management ===
|
|
183
|
-
|
|
184
|
-
/// Get cache age in seconds
|
|
185
|
-
pub fn get_age(&self) -> u64 {
|
|
186
|
-
Self::now().saturating_sub(self.data.last_updated)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/// Check if cache is stale (older than threshold)
|
|
190
|
-
pub fn is_stale(&self, threshold_secs: u64) -> bool {
|
|
191
|
-
self.get_age() > threshold_secs
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/// Clear all cached data
|
|
195
|
-
pub fn clear(&mut self) -> Result<(), std::io::Error> {
|
|
196
|
-
self.data = CacheData::default();
|
|
197
|
-
self.save_to_file()
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/// Get cache data reference
|
|
201
|
-
pub fn data(&self) -> &CacheData {
|
|
202
|
-
&self.data
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
impl Default for Cache {
|
|
207
|
-
fn default() -> Self {
|
|
208
|
-
Self::new().unwrap_or_else(|_| Self {
|
|
209
|
-
cache_path: PathBuf::from("4runr_cache.json"),
|
|
210
|
-
data: CacheData::default(),
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
|
-
}
|
|
1
|
+
/// Cache implementation for local data storage
|
|
2
|
+
/// Stores agents, runs, and system status in JSON format
|
|
3
|
+
|
|
4
|
+
use serde::{Deserialize, Serialize};
|
|
5
|
+
use std::fs;
|
|
6
|
+
use std::path::PathBuf;
|
|
7
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
8
|
+
|
|
9
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
10
|
+
pub struct AgentData {
|
|
11
|
+
pub name: String,
|
|
12
|
+
pub description: Option<String>,
|
|
13
|
+
pub model: String,
|
|
14
|
+
pub provider: String,
|
|
15
|
+
pub created_at: u64,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
19
|
+
pub struct RunData {
|
|
20
|
+
pub id: String,
|
|
21
|
+
pub name: String,
|
|
22
|
+
pub agent: String,
|
|
23
|
+
pub status: String,
|
|
24
|
+
pub started_at: u64,
|
|
25
|
+
pub duration: Option<u64>,
|
|
26
|
+
pub tokens_used: Option<u64>,
|
|
27
|
+
pub cost: Option<f64>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
31
|
+
pub struct SystemStatusData {
|
|
32
|
+
pub connected: bool,
|
|
33
|
+
pub gateway_url: Option<String>,
|
|
34
|
+
pub posture: String,
|
|
35
|
+
pub last_updated: u64,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
39
|
+
pub struct CacheData {
|
|
40
|
+
pub agents: Vec<AgentData>,
|
|
41
|
+
pub runs: Vec<RunData>,
|
|
42
|
+
pub system_status: Option<SystemStatusData>,
|
|
43
|
+
pub last_updated: u64,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[derive(Debug, Clone)]
|
|
47
|
+
pub struct Cache {
|
|
48
|
+
cache_path: PathBuf,
|
|
49
|
+
data: CacheData,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl Cache {
|
|
53
|
+
/// Create a new cache instance
|
|
54
|
+
pub fn new() -> Result<Self, std::io::Error> {
|
|
55
|
+
let cache_dir = Self::get_cache_dir()?;
|
|
56
|
+
fs::create_dir_all(&cache_dir)?;
|
|
57
|
+
|
|
58
|
+
let cache_path = cache_dir.join("4runr_cache.json");
|
|
59
|
+
let data = Self::load_from_file(&cache_path)?;
|
|
60
|
+
|
|
61
|
+
Ok(Self { cache_path, data })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Get the cache directory path
|
|
65
|
+
fn get_cache_dir() -> Result<PathBuf, std::io::Error> {
|
|
66
|
+
#[cfg(target_os = "windows")]
|
|
67
|
+
{
|
|
68
|
+
let appdata = std::env::var("APPDATA")
|
|
69
|
+
.unwrap_or_else(|_| String::from("C:\\Users\\Default\\AppData\\Roaming"));
|
|
70
|
+
Ok(PathBuf::from(appdata).join("4runr"))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#[cfg(not(target_os = "windows"))]
|
|
74
|
+
{
|
|
75
|
+
let home = std::env::var("HOME")
|
|
76
|
+
.unwrap_or_else(|_| String::from("/tmp"));
|
|
77
|
+
Ok(PathBuf::from(home).join(".4runr"))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Load cache from file
|
|
82
|
+
fn load_from_file(path: &PathBuf) -> Result<CacheData, std::io::Error> {
|
|
83
|
+
if !path.exists() {
|
|
84
|
+
return Ok(CacheData::default());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let contents = fs::read_to_string(path)?;
|
|
88
|
+
let data: CacheData = serde_json::from_str(&contents)
|
|
89
|
+
.unwrap_or_default();
|
|
90
|
+
|
|
91
|
+
Ok(data)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Save cache to file
|
|
95
|
+
fn save_to_file(&self) -> Result<(), std::io::Error> {
|
|
96
|
+
let json = serde_json::to_string_pretty(&self.data)?;
|
|
97
|
+
fs::write(&self.cache_path, json)?;
|
|
98
|
+
Ok(())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Get current timestamp
|
|
102
|
+
fn now() -> u64 {
|
|
103
|
+
SystemTime::now()
|
|
104
|
+
.duration_since(UNIX_EPOCH)
|
|
105
|
+
.unwrap()
|
|
106
|
+
.as_secs()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// === Agent Operations ===
|
|
110
|
+
|
|
111
|
+
/// Get all cached agents
|
|
112
|
+
pub fn get_agents(&self) -> Vec<AgentData> {
|
|
113
|
+
self.data.agents.clone()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Update agents cache
|
|
117
|
+
pub fn update_agents(&mut self, agents: Vec<AgentData>) -> Result<(), std::io::Error> {
|
|
118
|
+
self.data.agents = agents;
|
|
119
|
+
self.data.last_updated = Self::now();
|
|
120
|
+
self.save_to_file()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Add a single agent
|
|
124
|
+
pub fn add_agent(&mut self, agent: AgentData) -> Result<(), std::io::Error> {
|
|
125
|
+
self.data.agents.push(agent);
|
|
126
|
+
self.data.last_updated = Self::now();
|
|
127
|
+
self.save_to_file()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Remove an agent by name
|
|
131
|
+
pub fn remove_agent(&mut self, name: &str) -> Result<(), std::io::Error> {
|
|
132
|
+
self.data.agents.retain(|a| a.name != name);
|
|
133
|
+
self.data.last_updated = Self::now();
|
|
134
|
+
self.save_to_file()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// === Run Operations ===
|
|
138
|
+
|
|
139
|
+
/// Get all cached runs
|
|
140
|
+
pub fn get_runs(&self) -> Vec<RunData> {
|
|
141
|
+
self.data.runs.clone()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Update runs cache
|
|
145
|
+
pub fn update_runs(&mut self, runs: Vec<RunData>) -> Result<(), std::io::Error> {
|
|
146
|
+
self.data.runs = runs;
|
|
147
|
+
self.data.last_updated = Self::now();
|
|
148
|
+
self.save_to_file()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Add a single run
|
|
152
|
+
pub fn add_run(&mut self, run: RunData) -> Result<(), std::io::Error> {
|
|
153
|
+
self.data.runs.push(run);
|
|
154
|
+
self.data.last_updated = Self::now();
|
|
155
|
+
self.save_to_file()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Update a run by ID
|
|
159
|
+
pub fn update_run(&mut self, id: &str, run: RunData) -> Result<(), std::io::Error> {
|
|
160
|
+
if let Some(existing) = self.data.runs.iter_mut().find(|r| r.id == id) {
|
|
161
|
+
*existing = run;
|
|
162
|
+
self.data.last_updated = Self::now();
|
|
163
|
+
self.save_to_file()?;
|
|
164
|
+
}
|
|
165
|
+
Ok(())
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// === System Status Operations ===
|
|
169
|
+
|
|
170
|
+
/// Get cached system status
|
|
171
|
+
pub fn get_system_status(&self) -> Option<SystemStatusData> {
|
|
172
|
+
self.data.system_status.clone()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// Update system status cache
|
|
176
|
+
pub fn update_system_status(&mut self, status: SystemStatusData) -> Result<(), std::io::Error> {
|
|
177
|
+
self.data.system_status = Some(status);
|
|
178
|
+
self.data.last_updated = Self::now();
|
|
179
|
+
self.save_to_file()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// === Cache Management ===
|
|
183
|
+
|
|
184
|
+
/// Get cache age in seconds
|
|
185
|
+
pub fn get_age(&self) -> u64 {
|
|
186
|
+
Self::now().saturating_sub(self.data.last_updated)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Check if cache is stale (older than threshold)
|
|
190
|
+
pub fn is_stale(&self, threshold_secs: u64) -> bool {
|
|
191
|
+
self.get_age() > threshold_secs
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// Clear all cached data
|
|
195
|
+
pub fn clear(&mut self) -> Result<(), std::io::Error> {
|
|
196
|
+
self.data = CacheData::default();
|
|
197
|
+
self.save_to_file()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Get cache data reference
|
|
201
|
+
pub fn data(&self) -> &CacheData {
|
|
202
|
+
&self.data
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
impl Default for Cache {
|
|
207
|
+
fn default() -> Self {
|
|
208
|
+
Self::new().unwrap_or_else(|_| Self {
|
|
209
|
+
cache_path: PathBuf::from("4runr_cache.json"),
|
|
210
|
+
data: CacheData::default(),
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/// Local data storage and caching module
|
|
2
|
-
/// Provides persistent storage for agents, runs, and system status
|
|
3
|
-
|
|
4
|
-
pub mod cache;
|
|
5
|
-
|
|
6
|
-
pub use cache::{Cache, CacheData};
|
|
1
|
+
/// Local data storage and caching module
|
|
2
|
+
/// Provides persistent storage for agents, runs, and system status
|
|
3
|
+
|
|
4
|
+
pub mod cache;
|
|
5
|
+
|
|
6
|
+
pub use cache::{Cache, CacheData};
|