4runr-os 2.9.95 → 2.9.96
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
CHANGED
|
@@ -1506,11 +1506,13 @@ impl App {
|
|
|
1506
1506
|
} else {
|
|
1507
1507
|
// Open detail view
|
|
1508
1508
|
if let Some(run) = self.state.run_manager.selected_run() {
|
|
1509
|
-
|
|
1509
|
+
let run_id = run.id.clone();
|
|
1510
|
+
let run_name = run.name.clone();
|
|
1511
|
+
self.add_log(format!("[RUN] Viewing run: {}", run_name));
|
|
1510
1512
|
self.state.run_manager.toggle_detail_view();
|
|
1511
1513
|
if self.state.operation_mode == OperationMode::Connected {
|
|
1512
1514
|
if let Some(ws) = ws_client {
|
|
1513
|
-
let data = serde_json::json!({ "runId":
|
|
1515
|
+
let data = serde_json::json!({ "runId": run_id });
|
|
1514
1516
|
if let Ok(id) = ws.send_command("run.get", Some(data)) {
|
|
1515
1517
|
self.state.pending_run_get_id = Some(id);
|
|
1516
1518
|
}
|
|
@@ -1716,20 +1718,22 @@ impl App {
|
|
|
1716
1718
|
self.add_log("[AGENT] Connect to Gateway first to run on the server (key x).".to_string());
|
|
1717
1719
|
} else if self.state.agent_list.agents.is_empty() {
|
|
1718
1720
|
self.add_log("[AGENT] No agents in list.".to_string());
|
|
1719
|
-
} else if let Some(
|
|
1721
|
+
} else if let Some((agent_name, agent_model)) =
|
|
1722
|
+
self.get_selected_agent().map(|a| (a.name.clone(), a.model.clone()))
|
|
1723
|
+
{
|
|
1720
1724
|
if let Some(ws) = ws_client {
|
|
1721
|
-
let name = format!("TUI: {}",
|
|
1725
|
+
let name = format!("TUI: {}", agent_name);
|
|
1722
1726
|
let input = serde_json::json!({
|
|
1723
1727
|
"agent_id": "test",
|
|
1724
1728
|
"data": {
|
|
1725
|
-
"prompt": format!("Quick run for local agent profile '{}' (model {}).",
|
|
1729
|
+
"prompt": format!("Quick run for local agent profile '{}' (model {}).", agent_name, agent_model)
|
|
1726
1730
|
}
|
|
1727
1731
|
});
|
|
1728
1732
|
let data = serde_json::json!({ "name": name, "input": input });
|
|
1729
1733
|
match ws.send_command("run.quick", Some(data)) {
|
|
1730
1734
|
Ok(id) => {
|
|
1731
1735
|
self.state.pending_run_quick_id = Some(id);
|
|
1732
|
-
self.add_log(format!("[AGENT] Queued Gateway run for profile '{}' (x)",
|
|
1736
|
+
self.add_log(format!("[AGENT] Queued Gateway run for profile '{}' (x)", agent_name));
|
|
1733
1737
|
}
|
|
1734
1738
|
Err(e) => self.add_log(format!("[ERROR] run.quick: {}", e)),
|
|
1735
1739
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
//! File-based debug logger for Setup Portal.
|
|
2
|
+
//! When SETUP_PORTAL_DEBUG=1 or SETUP_PORTAL_DEBUG_FILE is set, all inputs and render state
|
|
3
|
+
//! are written to a file so you can capture the full trace when reproducing the navigation bug.
|
|
4
|
+
//!
|
|
5
|
+
//! Usage:
|
|
6
|
+
//! set SETUP_PORTAL_DEBUG_FILE=setup-portal-debug.log
|
|
7
|
+
//! mk3-tui (or 4r)
|
|
8
|
+
//! Then open Setup Portal (windowed), reproduce the bug, exit. Inspect setup-portal-debug.log.
|
|
9
|
+
|
|
10
|
+
use std::io::Write;
|
|
11
|
+
use std::sync::Mutex;
|
|
12
|
+
use std::sync::OnceLock;
|
|
13
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
14
|
+
|
|
15
|
+
fn enabled() -> bool {
|
|
16
|
+
std::env::var("SETUP_PORTAL_DEBUG").as_deref() == Ok("1")
|
|
17
|
+
|| std::env::var("SETUP_PORTAL_DEBUG_FILE").is_ok()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fn path() -> String {
|
|
21
|
+
std::env::var("SETUP_PORTAL_DEBUG_FILE")
|
|
22
|
+
.unwrap_or_else(|_| "setup-portal-debug.log".to_string())
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static FILE: OnceLock<Mutex<Option<std::io::BufWriter<std::fs::File>>>> = OnceLock::new();
|
|
26
|
+
|
|
27
|
+
fn ensure_open() -> bool {
|
|
28
|
+
if !enabled() {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
let mutex = FILE.get_or_init(|| Mutex::new(None));
|
|
32
|
+
let mut guard = mutex.lock().unwrap();
|
|
33
|
+
if guard.is_none() {
|
|
34
|
+
let path = path();
|
|
35
|
+
match std::fs::OpenOptions::new()
|
|
36
|
+
.create(true)
|
|
37
|
+
.append(true)
|
|
38
|
+
.open(&path)
|
|
39
|
+
{
|
|
40
|
+
Ok(file_handle) => {
|
|
41
|
+
// Resolve full path so user always knows where the log is
|
|
42
|
+
let full_path: String = std::path::Path::new(&path)
|
|
43
|
+
.canonicalize()
|
|
44
|
+
.map(|p| p.display().to_string())
|
|
45
|
+
.or_else(|_| std::env::current_dir().map(|cwd| cwd.join(&path).display().to_string()))
|
|
46
|
+
.unwrap_or_else(|_| path.clone());
|
|
47
|
+
let _ = writeln!(
|
|
48
|
+
std::io::stderr(),
|
|
49
|
+
"[SETUP_PORTAL_DEBUG] Log file: {}",
|
|
50
|
+
full_path
|
|
51
|
+
);
|
|
52
|
+
let mut w = std::io::BufWriter::new(file_handle);
|
|
53
|
+
let banner = format!("{} === Setup Portal debug log opened: {} ===\n", timestamp(), full_path);
|
|
54
|
+
let _ = w.write_all(banner.as_bytes());
|
|
55
|
+
let _ = w.flush();
|
|
56
|
+
*guard = Some(w);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
Err(e) => {
|
|
60
|
+
let _ = writeln!(
|
|
61
|
+
std::io::stderr(),
|
|
62
|
+
"[SETUP_PORTAL_DEBUG] Failed to open \"{}\": {}",
|
|
63
|
+
path, e
|
|
64
|
+
);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn log_raw(msg: &str) {
|
|
73
|
+
if !enabled() {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if !ensure_open() {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let mutex = FILE.get().unwrap();
|
|
80
|
+
let mut guard = mutex.lock().unwrap();
|
|
81
|
+
if let Some(ref mut w) = *guard {
|
|
82
|
+
let _ = w.write_all(msg.as_bytes());
|
|
83
|
+
let _ = w.write_all(b"\n");
|
|
84
|
+
let _ = w.flush();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn timestamp() -> String {
|
|
89
|
+
SystemTime::now()
|
|
90
|
+
.duration_since(UNIX_EPOCH)
|
|
91
|
+
.map(|d| format!("{}.{:03}", d.as_secs(), d.subsec_millis()))
|
|
92
|
+
.unwrap_or_else(|_| "?.???".to_string())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Log a line with timestamp. Call from main or setup_portal when debug file is enabled.
|
|
96
|
+
pub fn log(msg: &str) {
|
|
97
|
+
log_raw(&format!("{} {}", timestamp(), msg));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Log screen switch (enter/leave Setup Portal).
|
|
101
|
+
pub fn log_screen(from: &str, to: &str) {
|
|
102
|
+
log(&format!("SCREEN {} -> {}", from, to));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Log an input event (key, mouse, resize).
|
|
106
|
+
pub fn log_input(kind: &str, detail: &str) {
|
|
107
|
+
log(&format!("INPUT {} {}", kind, detail));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Log render state (dims, rects, selected option). Throttled by caller.
|
|
111
|
+
pub fn log_render(msg: &str) {
|
|
112
|
+
log(&format!("RENDER {}", msg));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Log flicker-debug: when we clear and when we finish draw (to correlate with visible flicker).
|
|
116
|
+
/// Writes to debug file when SETUP_PORTAL_DEBUG or SETUP_PORTAL_DEBUG_FILE is set.
|
|
117
|
+
pub fn log_flicker_debug(event: &str, detail: &str) {
|
|
118
|
+
let msg = format!("FLICKER {} {}", event, detail);
|
|
119
|
+
log(&msg);
|
|
120
|
+
// Also to stderr when file debug enabled, so "4r 2> setup-debug.log" captures it
|
|
121
|
+
if enabled() {
|
|
122
|
+
let _ = writeln!(std::io::stderr(), "[SETUP_PORTAL_DEBUG] {}", msg);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -126,16 +126,17 @@ fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
126
126
|
|
|
127
127
|
let portal = &state.connection_portal;
|
|
128
128
|
|
|
129
|
-
let (title, border_color) = if portal.connection_success {
|
|
129
|
+
let (title, border_color): (String, Color) = if portal.connection_success {
|
|
130
130
|
let u = portal.last_successful_url.as_deref().unwrap_or(portal.gateway_url.as_str());
|
|
131
|
-
let
|
|
132
|
-
|
|
131
|
+
let stripped = u.replace("http://", "").replace("https://", "");
|
|
132
|
+
let host = stripped.split('/').next().unwrap_or(u).to_string();
|
|
133
|
+
(format!(" Portal Connection - Connected to {} ", host), NEON_GREEN)
|
|
133
134
|
} else if portal.error.is_some() {
|
|
134
|
-
(" Portal Connection - Error ", ERROR_RED)
|
|
135
|
+
(" Portal Connection - Error ".to_string(), ERROR_RED)
|
|
135
136
|
} else if portal.connecting {
|
|
136
|
-
(" Portal Connection - Connecting... ", AMBER_WARN)
|
|
137
|
+
(" Portal Connection - Connecting... ".to_string(), AMBER_WARN)
|
|
137
138
|
} else {
|
|
138
|
-
(" Portal Connection ", BRAND_PURPLE)
|
|
139
|
+
(" Portal Connection ".to_string(), BRAND_PURPLE)
|
|
139
140
|
};
|
|
140
141
|
|
|
141
142
|
let block = Block::default()
|
|
@@ -182,7 +183,8 @@ fn render_input_fields(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
182
183
|
let portal = &state.connection_portal;
|
|
183
184
|
if portal.connection_success {
|
|
184
185
|
let url = state.gateway_url.as_deref().or(portal.last_successful_url.as_deref()).unwrap_or(portal.gateway_url.as_str());
|
|
185
|
-
let
|
|
186
|
+
let stripped = url.replace("http://", "").replace("https://", "");
|
|
187
|
+
let short = stripped.split('/').next().unwrap_or(url);
|
|
186
188
|
let block = Block::default().title(" URL ").borders(Borders::ALL).border_style(Style::default().fg(NEON_GREEN)).style(Style::default().bg(BG_PANEL));
|
|
187
189
|
let inner = block.inner(area);
|
|
188
190
|
f.render_widget(block, area);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.96",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.
|
|
5
|
+
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.96: npm bundle — copy-mk3 includes debug_log.rs (fixes global Rust build); mk3-tui borrow/type fixes (connection portal, run detail, run.quick). ⚠️ Pre-MVP / Development Phase",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|