4runr-os 2.10.42 → 2.10.43
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.
|
@@ -4782,9 +4782,9 @@
|
|
|
4782
4782
|
}
|
|
4783
4783
|
},
|
|
4784
4784
|
"node_modules/electron-to-chromium": {
|
|
4785
|
-
"version": "1.5.
|
|
4786
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
|
4787
|
-
"integrity": "sha512-
|
|
4785
|
+
"version": "1.5.354",
|
|
4786
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.354.tgz",
|
|
4787
|
+
"integrity": "sha512-JaBHwWcfIdmSAfWM5l3uwjGd431j8YEMikZ+K/2nXVuBqJKyZ0f+2h4n4JY5AyNiZmnY9qQr2RU3v9DxDmHMNg==",
|
|
4788
4788
|
"dev": true,
|
|
4789
4789
|
"license": "ISC"
|
|
4790
4790
|
},
|
package/mk3-tui/src/app.rs
CHANGED
|
@@ -922,6 +922,20 @@ impl App {
|
|
|
922
922
|
Ok(display)
|
|
923
923
|
}
|
|
924
924
|
|
|
925
|
+
pub fn scroll_portal_monitoring_by(&mut self, delta: isize) {
|
|
926
|
+
use crate::ui::portal_monitoring::portal_monitoring_scroll_max;
|
|
927
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
928
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
929
|
+
let max_scroll = portal_monitoring_scroll_max(&self.state, sw, inner_h);
|
|
930
|
+
let current = self.state.portal_monitoring.scroll_offset;
|
|
931
|
+
self.state.portal_monitoring.scroll_offset = if delta < 0 {
|
|
932
|
+
current.saturating_sub(delta.unsigned_abs())
|
|
933
|
+
} else {
|
|
934
|
+
current.saturating_add(delta as usize).min(max_scroll)
|
|
935
|
+
};
|
|
936
|
+
self.request_immediate_render("portal_obs_scroll");
|
|
937
|
+
}
|
|
938
|
+
|
|
925
939
|
/// CLI ↔ TUI WebSocket lost: drop Gateway link UI and leave Portal Monitoring / Connection Portal so the session does not appear still "connected".
|
|
926
940
|
pub fn on_cli_backend_disconnect(&mut self) {
|
|
927
941
|
use crate::screens::Screen;
|
|
@@ -2693,7 +2707,9 @@ impl App {
|
|
|
2693
2707
|
key: KeyEvent,
|
|
2694
2708
|
ws_client: Option<&WebSocketClient>,
|
|
2695
2709
|
) -> anyhow::Result<bool> {
|
|
2696
|
-
use crate::ui::portal_monitoring::
|
|
2710
|
+
use crate::ui::portal_monitoring::{
|
|
2711
|
+
portal_monitoring_scroll_max, portal_monitoring_section_offset,
|
|
2712
|
+
};
|
|
2697
2713
|
use crossterm::event::KeyModifiers;
|
|
2698
2714
|
if key.modifiers.contains(KeyModifiers::CONTROL) {
|
|
2699
2715
|
match key.code {
|
|
@@ -2774,12 +2790,24 @@ impl App {
|
|
|
2774
2790
|
return Ok(false);
|
|
2775
2791
|
}
|
|
2776
2792
|
if key.code == KeyCode::Char('l') || key.code == KeyCode::Char('L') {
|
|
2793
|
+
self.state
|
|
2794
|
+
.advanced_monitoring
|
|
2795
|
+
.monitoring_state
|
|
2796
|
+
.select_section(MonitoringSection::Logs);
|
|
2797
|
+
self.state
|
|
2798
|
+
.advanced_monitoring
|
|
2799
|
+
.monitoring_state
|
|
2800
|
+
.expand_section(MonitoringSection::Logs);
|
|
2777
2801
|
if let Some(ws) = ws_client {
|
|
2778
2802
|
self.begin_monitoring_logs_request(ws);
|
|
2779
|
-
self.state
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
.
|
|
2803
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
2804
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
2805
|
+
self.state.portal_monitoring.scroll_offset = portal_monitoring_section_offset(
|
|
2806
|
+
&self.state,
|
|
2807
|
+
sw,
|
|
2808
|
+
inner_h,
|
|
2809
|
+
MonitoringSection::Logs,
|
|
2810
|
+
);
|
|
2783
2811
|
self.request_immediate_render("portal_monitoring_logs");
|
|
2784
2812
|
} else {
|
|
2785
2813
|
self.state.portal_monitoring.error =
|
|
@@ -2790,7 +2818,23 @@ impl App {
|
|
|
2790
2818
|
}
|
|
2791
2819
|
|
|
2792
2820
|
if key.code == KeyCode::Char('h') || key.code == KeyCode::Char('H') {
|
|
2821
|
+
self.state
|
|
2822
|
+
.advanced_monitoring
|
|
2823
|
+
.monitoring_state
|
|
2824
|
+
.select_section(MonitoringSection::Metrics);
|
|
2825
|
+
self.state
|
|
2826
|
+
.advanced_monitoring
|
|
2827
|
+
.monitoring_state
|
|
2828
|
+
.expand_section(MonitoringSection::Metrics);
|
|
2793
2829
|
self.state.portal_monitoring.trend_view = !self.state.portal_monitoring.trend_view;
|
|
2830
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
2831
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
2832
|
+
self.state.portal_monitoring.scroll_offset = portal_monitoring_section_offset(
|
|
2833
|
+
&self.state,
|
|
2834
|
+
sw,
|
|
2835
|
+
inner_h,
|
|
2836
|
+
MonitoringSection::Metrics,
|
|
2837
|
+
);
|
|
2794
2838
|
let status = if self.state.portal_monitoring.trend_view {
|
|
2795
2839
|
"enabled"
|
|
2796
2840
|
} else {
|
|
@@ -2817,103 +2861,94 @@ impl App {
|
|
|
2817
2861
|
|
|
2818
2862
|
// Phase 3: extended dependency view (GET /api/monitoring/dependencies/detail)
|
|
2819
2863
|
if key.code == KeyCode::Char('t') || key.code == KeyCode::Char('T') {
|
|
2820
|
-
|
|
2821
|
-
let idx = self
|
|
2822
|
-
.state
|
|
2864
|
+
self.state
|
|
2823
2865
|
.advanced_monitoring
|
|
2824
2866
|
.monitoring_state
|
|
2825
|
-
.
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
.
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
}
|
|
2846
|
-
return Ok(false);
|
|
2867
|
+
.select_section(MonitoringSection::Dependencies);
|
|
2868
|
+
self.state
|
|
2869
|
+
.advanced_monitoring
|
|
2870
|
+
.monitoring_state
|
|
2871
|
+
.expand_section(MonitoringSection::Dependencies);
|
|
2872
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
2873
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
2874
|
+
self.state.portal_monitoring.scroll_offset = portal_monitoring_section_offset(
|
|
2875
|
+
&self.state,
|
|
2876
|
+
sw,
|
|
2877
|
+
inner_h,
|
|
2878
|
+
MonitoringSection::Dependencies,
|
|
2879
|
+
);
|
|
2880
|
+
if let Some(ws) = ws_client {
|
|
2881
|
+
self.begin_dependencies_detail_drill(ws);
|
|
2882
|
+
self.request_immediate_render("portal_deps_drill");
|
|
2883
|
+
} else {
|
|
2884
|
+
self.state.portal_monitoring.error =
|
|
2885
|
+
Some("WebSocket to CLI is not connected.".to_string());
|
|
2886
|
+
self.request_immediate_render("portal_obs_no_ws");
|
|
2847
2887
|
}
|
|
2888
|
+
return Ok(false);
|
|
2848
2889
|
}
|
|
2849
2890
|
|
|
2850
2891
|
// Phase 4: diagnostics for the local CLI/TUI bridge + Gateway reachability.
|
|
2851
2892
|
if key.code == KeyCode::Char('s') || key.code == KeyCode::Char('S') {
|
|
2852
|
-
|
|
2853
|
-
let idx = self
|
|
2854
|
-
.state
|
|
2893
|
+
self.state
|
|
2855
2894
|
.advanced_monitoring
|
|
2856
2895
|
.monitoring_state
|
|
2857
|
-
.
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
.
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
}
|
|
2878
|
-
return Ok(false);
|
|
2896
|
+
.select_section(MonitoringSection::System);
|
|
2897
|
+
self.state
|
|
2898
|
+
.advanced_monitoring
|
|
2899
|
+
.monitoring_state
|
|
2900
|
+
.expand_section(MonitoringSection::System);
|
|
2901
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
2902
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
2903
|
+
self.state.portal_monitoring.scroll_offset = portal_monitoring_section_offset(
|
|
2904
|
+
&self.state,
|
|
2905
|
+
sw,
|
|
2906
|
+
inner_h,
|
|
2907
|
+
MonitoringSection::System,
|
|
2908
|
+
);
|
|
2909
|
+
if let Some(ws) = ws_client {
|
|
2910
|
+
self.begin_system_diagnostics_request(ws);
|
|
2911
|
+
self.request_immediate_render("portal_system_diagnostics");
|
|
2912
|
+
} else {
|
|
2913
|
+
self.state.portal_monitoring.error =
|
|
2914
|
+
Some("WebSocket to CLI is not connected.".to_string());
|
|
2915
|
+
self.request_immediate_render("portal_obs_no_ws");
|
|
2879
2916
|
}
|
|
2917
|
+
return Ok(false);
|
|
2880
2918
|
}
|
|
2881
2919
|
|
|
2882
2920
|
if key.code == KeyCode::Right {
|
|
2883
|
-
|
|
2884
|
-
let idx = self
|
|
2885
|
-
.state
|
|
2921
|
+
self.state
|
|
2886
2922
|
.advanced_monitoring
|
|
2887
2923
|
.monitoring_state
|
|
2888
|
-
.
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
.
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
}
|
|
2915
|
-
return Ok(false);
|
|
2924
|
+
.select_section(MonitoringSection::Metrics);
|
|
2925
|
+
self.state
|
|
2926
|
+
.advanced_monitoring
|
|
2927
|
+
.monitoring_state
|
|
2928
|
+
.expand_section(MonitoringSection::Metrics);
|
|
2929
|
+
let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
|
|
2930
|
+
let sw = self.state.portal_monitoring.summary_clip_width.max(24);
|
|
2931
|
+
self.state.portal_monitoring.scroll_offset = portal_monitoring_section_offset(
|
|
2932
|
+
&self.state,
|
|
2933
|
+
sw,
|
|
2934
|
+
inner_h,
|
|
2935
|
+
MonitoringSection::Metrics,
|
|
2936
|
+
);
|
|
2937
|
+
if let Some(ws) = ws_client {
|
|
2938
|
+
let panel = self
|
|
2939
|
+
.state
|
|
2940
|
+
.portal_monitoring
|
|
2941
|
+
.metrics_drill
|
|
2942
|
+
.map(|p| p.next())
|
|
2943
|
+
.unwrap_or(MetricsDrillPanel::Http);
|
|
2944
|
+
self.begin_monitoring_drill_request(ws, panel);
|
|
2945
|
+
self.request_immediate_render("portal_metrics_drill");
|
|
2946
|
+
} else {
|
|
2947
|
+
self.state.portal_monitoring.error =
|
|
2948
|
+
Some("WebSocket to CLI is not connected.".to_string());
|
|
2949
|
+
self.request_immediate_render("portal_obs_no_ws");
|
|
2916
2950
|
}
|
|
2951
|
+
return Ok(false);
|
|
2917
2952
|
}
|
|
2918
2953
|
|
|
2919
2954
|
if key.code == KeyCode::Left {
|
|
@@ -2963,20 +2998,46 @@ impl App {
|
|
|
2963
2998
|
|
|
2964
2999
|
match key.code {
|
|
2965
3000
|
KeyCode::Up => {
|
|
2966
|
-
self
|
|
3001
|
+
if self
|
|
3002
|
+
.state
|
|
2967
3003
|
.advanced_monitoring
|
|
2968
3004
|
.monitoring_state
|
|
2969
|
-
.
|
|
2970
|
-
|
|
2971
|
-
|
|
3005
|
+
.selected_section
|
|
3006
|
+
== Some(MonitoringSection::Logs)
|
|
3007
|
+
&& self.state.portal_monitoring.scroll_offset > 0
|
|
3008
|
+
{
|
|
3009
|
+
self.state.portal_monitoring.scroll_offset =
|
|
3010
|
+
self.state.portal_monitoring.scroll_offset.saturating_sub(1);
|
|
3011
|
+
self.request_immediate_render("portal_obs_scroll");
|
|
3012
|
+
} else {
|
|
3013
|
+
self.state
|
|
3014
|
+
.advanced_monitoring
|
|
3015
|
+
.monitoring_state
|
|
3016
|
+
.select_previous_wrapped();
|
|
3017
|
+
self.state.portal_monitoring.scroll_offset = 0;
|
|
3018
|
+
self.request_immediate_render("portal_monitoring_select");
|
|
3019
|
+
}
|
|
2972
3020
|
}
|
|
2973
3021
|
KeyCode::Down => {
|
|
2974
|
-
self
|
|
3022
|
+
if self
|
|
3023
|
+
.state
|
|
2975
3024
|
.advanced_monitoring
|
|
2976
3025
|
.monitoring_state
|
|
2977
|
-
.
|
|
2978
|
-
|
|
2979
|
-
|
|
3026
|
+
.selected_section
|
|
3027
|
+
== Some(MonitoringSection::Logs)
|
|
3028
|
+
&& self.state.portal_monitoring.scroll_offset < max_scroll
|
|
3029
|
+
{
|
|
3030
|
+
self.state.portal_monitoring.scroll_offset =
|
|
3031
|
+
(self.state.portal_monitoring.scroll_offset + 1).min(max_scroll);
|
|
3032
|
+
self.request_immediate_render("portal_obs_scroll");
|
|
3033
|
+
} else {
|
|
3034
|
+
self.state
|
|
3035
|
+
.advanced_monitoring
|
|
3036
|
+
.monitoring_state
|
|
3037
|
+
.select_next_wrapped();
|
|
3038
|
+
self.state.portal_monitoring.scroll_offset = 0;
|
|
3039
|
+
self.request_immediate_render("portal_monitoring_select");
|
|
3040
|
+
}
|
|
2980
3041
|
}
|
|
2981
3042
|
KeyCode::PageUp => {
|
|
2982
3043
|
let step = inner_h.saturating_sub(3).max(1);
|
package/mk3-tui/src/main.rs
CHANGED
|
@@ -1548,6 +1548,8 @@ fn main() -> Result<()> {
|
|
|
1548
1548
|
// Handle ESC on both Press and Release so we never miss it (e.g. when stuck on "Detecting...")
|
|
1549
1549
|
if crossterm::event::poll(Duration::from_millis(1))? {
|
|
1550
1550
|
let on_setup = app.state.navigation.current_screen() == &screens::Screen::SetupPortal;
|
|
1551
|
+
let on_portal_monitoring =
|
|
1552
|
+
app.state.navigation.current_screen() == &screens::Screen::PortalMonitoring;
|
|
1551
1553
|
let on_standalone_input = matches!(
|
|
1552
1554
|
app.state.navigation.current_screen(),
|
|
1553
1555
|
&screens::Screen::ConnectionPortal
|
|
@@ -1571,8 +1573,14 @@ fn main() -> Result<()> {
|
|
|
1571
1573
|
if on_standalone_input {
|
|
1572
1574
|
debug_log::log_input("Mouse", &format!("{:?}", mouse));
|
|
1573
1575
|
}
|
|
1574
|
-
// Handle mouse scroll events for
|
|
1575
|
-
if
|
|
1576
|
+
// Handle mouse scroll events for standalone portals
|
|
1577
|
+
if on_portal_monitoring {
|
|
1578
|
+
match mouse.kind {
|
|
1579
|
+
MouseEventKind::ScrollUp => app.scroll_portal_monitoring_by(-3),
|
|
1580
|
+
MouseEventKind::ScrollDown => app.scroll_portal_monitoring_by(3),
|
|
1581
|
+
_ => {}
|
|
1582
|
+
}
|
|
1583
|
+
} else if on_setup {
|
|
1576
1584
|
if !app.state.setup_portal.detecting {
|
|
1577
1585
|
match mouse.kind {
|
|
1578
1586
|
MouseEventKind::ScrollUp => {
|
|
@@ -294,6 +294,17 @@ impl MonitoringState {
|
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
/// Move keyboard focus directly to a specific section.
|
|
298
|
+
pub fn select_section(&mut self, section: MonitoringSection) {
|
|
299
|
+
if let Some(index) = MonitoringSection::all()
|
|
300
|
+
.iter()
|
|
301
|
+
.position(|candidate| *candidate == section)
|
|
302
|
+
{
|
|
303
|
+
self.selected_index = index;
|
|
304
|
+
self.selected_section = Some(section);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
297
308
|
/// Collapse a specific section
|
|
298
309
|
pub fn collapse_section(&mut self, section: MonitoringSection) {
|
|
299
310
|
if let Some(state) = self.sections.get_mut(§ion) {
|
|
@@ -468,6 +468,27 @@ pub fn portal_monitoring_scroll_max(
|
|
|
468
468
|
total.saturating_sub(content_h.min(total.max(1)))
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
/// Best-effort scroll offset that brings a section header into view.
|
|
472
|
+
pub fn portal_monitoring_section_offset(
|
|
473
|
+
state: &AppState,
|
|
474
|
+
summary_width: usize,
|
|
475
|
+
body_inner_h: usize,
|
|
476
|
+
section: MonitoringSection,
|
|
477
|
+
) -> usize {
|
|
478
|
+
let body_lines = build_body_lines(state, summary_width);
|
|
479
|
+
let target = section.display_name();
|
|
480
|
+
let raw_offset = body_lines
|
|
481
|
+
.iter()
|
|
482
|
+
.position(|line| {
|
|
483
|
+
line.spans
|
|
484
|
+
.iter()
|
|
485
|
+
.any(|span| span.content.as_ref().contains(target))
|
|
486
|
+
})
|
|
487
|
+
.unwrap_or(0);
|
|
488
|
+
let max_scroll = portal_monitoring_scroll_max(state, summary_width, body_inner_h);
|
|
489
|
+
raw_offset.min(max_scroll)
|
|
490
|
+
}
|
|
491
|
+
|
|
471
492
|
/// Build scrollable section lines. **Phase 1:** collapsed summaries and ●/⚠/✖ colors are derived only from
|
|
472
493
|
/// `parse_snapshot` of `content_lines`; `MonitoringState.sections` controls **expand** + **selection** only
|
|
473
494
|
/// (not `SectionState.status` / `summary`).
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.43",
|
|
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.43: Makes Portal Monitoring footer shortcuts jump to their sections and fixes log scrolling. v2.10.42: Prevents stale global MK3 binaries from masking packaged TUI fixes.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|