4runr-os 2.10.39 → 2.10.41
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/apps/gateway/dist/apps/gateway/src/index.js +14 -4
- package/apps/gateway/dist/apps/gateway/src/index.js.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts +18 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js +117 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts +2 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js +54 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts +15 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js +164 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js.map +1 -0
- package/apps/gateway/package-lock.json +204 -353
- package/apps/gateway/src/index.ts +27 -8
- package/apps/gateway/src/metrics/monitoring-detail.ts +162 -0
- package/apps/gateway/src/middleware/log-capture.ts +70 -0
- package/apps/gateway/src/routes/monitoring.ts +298 -0
- package/dist/gateway-client.d.ts +2 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +22 -0
- package/dist/gateway-client.js.map +1 -1
- package/dist/tui-handlers.js +498 -0
- package/dist/tui-handlers.js.map +1 -1
- package/mk3-tui/src/app/render_scheduler.rs +111 -112
- package/mk3-tui/src/app.rs +1078 -295
- package/mk3-tui/src/debug_log.rs +131 -124
- package/mk3-tui/src/io/mod.rs +63 -66
- package/mk3-tui/src/io/protocol.rs +14 -15
- package/mk3-tui/src/io/stdio.rs +31 -32
- package/mk3-tui/src/io/ws.rs +25 -32
- package/mk3-tui/src/main.rs +774 -212
- package/mk3-tui/src/monitoring/mod.rs +428 -0
- package/mk3-tui/src/screens/mod.rs +53 -39
- package/mk3-tui/src/storage/cache.rs +221 -224
- package/mk3-tui/src/storage/mod.rs +5 -6
- package/mk3-tui/src/ui/agent_builder.rs +1148 -922
- package/mk3-tui/src/ui/agent_list.rs +344 -295
- package/mk3-tui/src/ui/boot.rs +145 -148
- package/mk3-tui/src/ui/connection_portal.rs +121 -98
- package/mk3-tui/src/ui/help.rs +340 -284
- package/mk3-tui/src/ui/layout.rs +966 -803
- package/mk3-tui/src/ui/mod.rs +1 -1
- package/mk3-tui/src/ui/portal_monitoring.rs +1027 -147
- package/mk3-tui/src/ui/run_manager.rs +784 -764
- package/mk3-tui/src/ui/safe_viewport.rs +236 -235
- package/mk3-tui/src/ui/settings.rs +414 -362
- package/mk3-tui/src/ui/setup_portal.rs +158 -101
- package/mk3-tui/src/websocket.rs +315 -308
- package/package.json +2 -2
package/mk3-tui/src/websocket.rs
CHANGED
|
@@ -1,308 +1,315 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocket Client for MK3 TUI
|
|
3
|
-
* Connects to Node.js backend WebSocket server
|
|
4
|
-
* Handles bidirectional JSON message communication
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
use
|
|
8
|
-
use
|
|
9
|
-
use
|
|
10
|
-
use tokio::sync::mpsc;
|
|
11
|
-
use tokio_tungstenite::{connect_async, tungstenite::Message as WsMessage};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#[
|
|
16
|
-
#[serde(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
pub
|
|
29
|
-
pub
|
|
30
|
-
pub
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
pub
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
pub
|
|
43
|
-
pub
|
|
44
|
-
pub
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
pub
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
pub
|
|
59
|
-
pub
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
pub
|
|
65
|
-
pub
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
let (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
let
|
|
97
|
-
let
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
let _ = client_tx.send(WsClientMessage::
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Client for MK3 TUI
|
|
3
|
+
* Connects to Node.js backend WebSocket server
|
|
4
|
+
* Handles bidirectional JSON message communication
|
|
5
|
+
*/
|
|
6
|
+
use anyhow::{Context, Result};
|
|
7
|
+
use futures_util::{SinkExt, StreamExt};
|
|
8
|
+
use serde::{Deserialize, Serialize};
|
|
9
|
+
use std::sync::{Arc, Mutex};
|
|
10
|
+
use tokio::sync::mpsc;
|
|
11
|
+
use tokio_tungstenite::{connect_async, tungstenite::Message as WsMessage};
|
|
12
|
+
|
|
13
|
+
// Message types from STEP-2-COMPLETE-SPECIFICATION.md
|
|
14
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
15
|
+
#[serde(tag = "type")]
|
|
16
|
+
#[serde(rename_all = "lowercase")]
|
|
17
|
+
pub enum Message {
|
|
18
|
+
#[serde(rename = "command")]
|
|
19
|
+
Command(CommandMessage),
|
|
20
|
+
#[serde(rename = "response")]
|
|
21
|
+
Response(ResponseMessage),
|
|
22
|
+
#[serde(rename = "event")]
|
|
23
|
+
Event(EventMessage),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
27
|
+
pub struct CommandMessage {
|
|
28
|
+
pub id: String,
|
|
29
|
+
pub timestamp: String,
|
|
30
|
+
pub payload: CommandPayload,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
34
|
+
pub struct CommandPayload {
|
|
35
|
+
pub command: String,
|
|
36
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
37
|
+
pub data: Option<serde_json::Value>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
41
|
+
pub struct ResponseMessage {
|
|
42
|
+
pub id: String,
|
|
43
|
+
pub timestamp: String,
|
|
44
|
+
pub payload: ResponsePayload,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
48
|
+
pub struct ResponsePayload {
|
|
49
|
+
pub success: bool,
|
|
50
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
51
|
+
pub data: Option<serde_json::Value>,
|
|
52
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
53
|
+
pub error: Option<String>,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
57
|
+
pub struct EventMessage {
|
|
58
|
+
pub timestamp: String,
|
|
59
|
+
pub payload: EventPayload,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
63
|
+
pub struct EventPayload {
|
|
64
|
+
pub event: String,
|
|
65
|
+
pub data: serde_json::Value,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pub enum WsClientMessage {
|
|
69
|
+
Connected,
|
|
70
|
+
Disconnected,
|
|
71
|
+
Response(ResponseMessage),
|
|
72
|
+
Event(EventMessage),
|
|
73
|
+
Error(String),
|
|
74
|
+
ConnectionLost,
|
|
75
|
+
#[allow(dead_code)]
|
|
76
|
+
Reconnecting,
|
|
77
|
+
#[allow(dead_code)]
|
|
78
|
+
ReconnectFailed(String),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub struct WebSocketClient {
|
|
82
|
+
#[allow(dead_code)]
|
|
83
|
+
url: String,
|
|
84
|
+
tx: mpsc::UnboundedSender<WsMessage>,
|
|
85
|
+
rx: Arc<Mutex<mpsc::UnboundedReceiver<WsClientMessage>>>,
|
|
86
|
+
connected: Arc<Mutex<bool>>,
|
|
87
|
+
pending_commands: Arc<Mutex<Vec<(String, String, Option<serde_json::Value>)>>>, // (id, command, data)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl WebSocketClient {
|
|
91
|
+
pub async fn connect(url: &str) -> Result<Self> {
|
|
92
|
+
let (msg_tx, msg_rx) = mpsc::unbounded_channel::<WsMessage>();
|
|
93
|
+
let (client_tx, client_rx) = mpsc::unbounded_channel::<WsClientMessage>();
|
|
94
|
+
|
|
95
|
+
let url_clone = url.to_string();
|
|
96
|
+
let connected = Arc::new(Mutex::new(false));
|
|
97
|
+
let connected_clone = connected.clone();
|
|
98
|
+
|
|
99
|
+
// Spawn WebSocket connection task
|
|
100
|
+
tokio::spawn(async move {
|
|
101
|
+
if let Err(e) =
|
|
102
|
+
Self::run_connection(&url_clone, client_tx.clone(), msg_rx, connected_clone).await
|
|
103
|
+
{
|
|
104
|
+
let _ = client_tx.send(WsClientMessage::Error(e.to_string()));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
Ok(Self {
|
|
109
|
+
url: url.to_string(),
|
|
110
|
+
tx: msg_tx,
|
|
111
|
+
rx: Arc::new(Mutex::new(client_rx)),
|
|
112
|
+
connected,
|
|
113
|
+
pending_commands: Arc::new(Mutex::new(Vec::new())),
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async fn run_connection(
|
|
118
|
+
url: &str,
|
|
119
|
+
client_tx: mpsc::UnboundedSender<WsClientMessage>,
|
|
120
|
+
mut client_rx: mpsc::UnboundedReceiver<WsMessage>,
|
|
121
|
+
connected: Arc<Mutex<bool>>,
|
|
122
|
+
) -> Result<()> {
|
|
123
|
+
// Connect to WebSocket server
|
|
124
|
+
let (ws_stream, _) = connect_async(url)
|
|
125
|
+
.await
|
|
126
|
+
.context("Failed to connect to WebSocket server")?;
|
|
127
|
+
|
|
128
|
+
*connected.lock().unwrap() = true;
|
|
129
|
+
let _ = client_tx.send(WsClientMessage::Connected);
|
|
130
|
+
|
|
131
|
+
let (mut write, mut read) = ws_stream.split();
|
|
132
|
+
|
|
133
|
+
// Spawn task to handle outgoing messages
|
|
134
|
+
tokio::spawn(async move {
|
|
135
|
+
while let Some(msg) = client_rx.recv().await {
|
|
136
|
+
if let Err(e) = write.send(msg).await {
|
|
137
|
+
eprintln!("[WebSocket] Error sending message: {}", e);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Handle incoming messages
|
|
144
|
+
while let Some(msg) = read.next().await {
|
|
145
|
+
match msg {
|
|
146
|
+
Ok(WsMessage::Text(text)) => {
|
|
147
|
+
if let Err(e) = Self::handle_message(&text, &client_tx) {
|
|
148
|
+
eprintln!("[WebSocket] Error handling message: {}", e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
Ok(WsMessage::Close(_)) => {
|
|
152
|
+
*connected.lock().unwrap() = false;
|
|
153
|
+
let _ = client_tx.send(WsClientMessage::Disconnected);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
Err(e) => {
|
|
157
|
+
*connected.lock().unwrap() = false;
|
|
158
|
+
let _ = client_tx.send(WsClientMessage::ConnectionLost);
|
|
159
|
+
let _ =
|
|
160
|
+
client_tx.send(WsClientMessage::Error(format!("Connection error: {}", e)));
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
_ => {}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Ok(())
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn handle_message(
|
|
171
|
+
text: &str,
|
|
172
|
+
client_tx: &mpsc::UnboundedSender<WsClientMessage>,
|
|
173
|
+
) -> Result<()> {
|
|
174
|
+
let message: Message = serde_json::from_str(text).context("Failed to parse message")?;
|
|
175
|
+
|
|
176
|
+
match message {
|
|
177
|
+
Message::Response(resp) => {
|
|
178
|
+
let _ = client_tx.send(WsClientMessage::Response(resp));
|
|
179
|
+
}
|
|
180
|
+
Message::Event(event) => {
|
|
181
|
+
let _ = client_tx.send(WsClientMessage::Event(event));
|
|
182
|
+
}
|
|
183
|
+
Message::Command(_) => {
|
|
184
|
+
// Client should not receive commands
|
|
185
|
+
eprintln!("[WebSocket] Unexpected command message from server");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
Ok(())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
pub fn send_command(&self, command: &str, data: Option<serde_json::Value>) -> Result<String> {
|
|
193
|
+
let id = format!("req-{}", uuid::Uuid::new_v4());
|
|
194
|
+
let timestamp = chrono::Utc::now().to_rfc3339();
|
|
195
|
+
|
|
196
|
+
let cmd = Message::Command(CommandMessage {
|
|
197
|
+
id: id.clone(),
|
|
198
|
+
timestamp,
|
|
199
|
+
payload: CommandPayload {
|
|
200
|
+
command: command.to_string(),
|
|
201
|
+
data: data.clone(),
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
let json = serde_json::to_string(&cmd)?;
|
|
206
|
+
|
|
207
|
+
// Track pending command for retry
|
|
208
|
+
if self.is_connected() {
|
|
209
|
+
self.pending_commands
|
|
210
|
+
.lock()
|
|
211
|
+
.unwrap()
|
|
212
|
+
.push((id.clone(), command.to_string(), data));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
self.tx.send(WsMessage::Text(json))?;
|
|
216
|
+
|
|
217
|
+
Ok(id)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Retry pending commands after reconnection
|
|
221
|
+
#[allow(dead_code)]
|
|
222
|
+
pub fn retry_pending_commands(&self) -> Result<Vec<String>> {
|
|
223
|
+
let mut pending = self.pending_commands.lock().unwrap();
|
|
224
|
+
let mut retried_ids = Vec::new();
|
|
225
|
+
|
|
226
|
+
for (old_id, command, data) in pending.drain(..) {
|
|
227
|
+
// Generate new ID for retry
|
|
228
|
+
let new_id = format!("retry-{}", uuid::Uuid::new_v4());
|
|
229
|
+
let timestamp = chrono::Utc::now().to_rfc3339();
|
|
230
|
+
|
|
231
|
+
let cmd = Message::Command(CommandMessage {
|
|
232
|
+
id: new_id.clone(),
|
|
233
|
+
timestamp,
|
|
234
|
+
payload: CommandPayload { command, data },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
let json = serde_json::to_string(&cmd)?;
|
|
238
|
+
self.tx.send(WsMessage::Text(json))?;
|
|
239
|
+
|
|
240
|
+
retried_ids.push(format!(
|
|
241
|
+
"{} -> {}",
|
|
242
|
+
&old_id[old_id.len().saturating_sub(8)..],
|
|
243
|
+
&new_id[new_id.len().saturating_sub(8)..]
|
|
244
|
+
));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Ok(retried_ids)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// Clear pending commands (e.g., on explicit disconnect)
|
|
251
|
+
#[allow(dead_code)]
|
|
252
|
+
pub fn clear_pending_commands(&self) {
|
|
253
|
+
self.pending_commands.lock().unwrap().clear();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
pub fn try_recv(&self) -> Option<WsClientMessage> {
|
|
257
|
+
self.rx.lock().unwrap().try_recv().ok()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
pub fn is_connected(&self) -> bool {
|
|
261
|
+
*self.connected.lock().unwrap()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Helper for creating UUID
|
|
266
|
+
mod uuid {
|
|
267
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
268
|
+
|
|
269
|
+
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
270
|
+
|
|
271
|
+
pub struct Uuid;
|
|
272
|
+
|
|
273
|
+
impl Uuid {
|
|
274
|
+
pub fn new_v4() -> String {
|
|
275
|
+
let count = COUNTER.fetch_add(1, Ordering::SeqCst);
|
|
276
|
+
let timestamp = std::time::SystemTime::now()
|
|
277
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
278
|
+
.unwrap()
|
|
279
|
+
.as_secs();
|
|
280
|
+
format!("{:016x}{:016x}", timestamp, count)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Helper for timestamps
|
|
286
|
+
mod chrono {
|
|
287
|
+
pub struct Utc;
|
|
288
|
+
|
|
289
|
+
impl Utc {
|
|
290
|
+
pub fn now() -> DateTime {
|
|
291
|
+
DateTime
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
pub struct DateTime;
|
|
296
|
+
|
|
297
|
+
impl DateTime {
|
|
298
|
+
pub fn to_rfc3339(&self) -> String {
|
|
299
|
+
let now = std::time::SystemTime::now()
|
|
300
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
301
|
+
.unwrap();
|
|
302
|
+
|
|
303
|
+
// Simple ISO 8601 format
|
|
304
|
+
format!(
|
|
305
|
+
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
|
|
306
|
+
1970,
|
|
307
|
+
1,
|
|
308
|
+
1, // Simplified - real impl would calculate actual date
|
|
309
|
+
(now.as_secs() / 3600) % 24,
|
|
310
|
+
(now.as_secs() / 60) % 60,
|
|
311
|
+
now.as_secs() % 60
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.41",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.10.
|
|
5
|
+
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.10.41: Stabilizes Portal Monitoring refresh/log shortcuts and log scrolling. v2.10.40: Portal Monitoring redesign with sections, drill-down, history, system diagnostics, and help overlay.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|