4runr-os 2.10.43 → 2.10.44

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.
@@ -311,7 +311,7 @@ impl Default for PortalMonitoringState {
311
311
  viewport_lines: 18,
312
312
  last_refresh: None,
313
313
  auto_refresh_interval: Duration::from_secs(5),
314
- auto_refresh_enabled: true,
314
+ auto_refresh_enabled: false,
315
315
  last_http_for_delta: None,
316
316
  last_instant_for_delta: None,
317
317
  section_overrides: HashMap::new(),
@@ -936,6 +936,17 @@ impl App {
936
936
  self.request_immediate_render("portal_obs_scroll");
937
937
  }
938
938
 
939
+ fn cancel_portal_monitoring_requests(&mut self) {
940
+ self.state.pending_gateway_observability_id = None;
941
+ self.state.pending_monitoring_refresh_id = None;
942
+ self.state.pending_monitoring_logs_id = None;
943
+ self.state.pending_monitoring_drill_id = None;
944
+ self.state.portal_monitoring.loading = false;
945
+ self.state.portal_monitoring.section_refresh_loading = None;
946
+ self.state.portal_monitoring.logs_fetch_loading = false;
947
+ self.state.portal_monitoring.metrics_drill_loading = false;
948
+ }
949
+
939
950
  /// CLI ↔ TUI WebSocket lost: drop Gateway link UI and leave Portal Monitoring / Connection Portal so the session does not appear still "connected".
940
951
  pub fn on_cli_backend_disconnect(&mut self) {
941
952
  use crate::screens::Screen;
@@ -2750,37 +2761,13 @@ impl App {
2750
2761
  self.request_immediate_render("portal_help_open");
2751
2762
  return Ok(false);
2752
2763
  }
2753
- // R is context-sensitive: Overview full `gateway.observability` snapshot; any other
2754
- // section → `monitoring.refresh` for that section only (cheaper; matches expanded detail).
2764
+ // R is deliberately global: refresh the full snapshot without changing the current view.
2755
2765
  if key.code == KeyCode::Char('r') || key.code == KeyCode::Char('R') {
2756
2766
  if let Some(ws) = ws_client {
2757
- let sections = MonitoringSection::all();
2758
- let idx = self
2759
- .state
2760
- .advanced_monitoring
2761
- .monitoring_state
2762
- .selected_index;
2763
- let current = sections.get(idx).copied();
2764
2767
  self.state.portal_observability_last_poll = None;
2765
- self.state.pending_gateway_observability_id = None;
2766
- self.state.pending_monitoring_refresh_id = None;
2767
- self.state.pending_monitoring_logs_id = None;
2768
- self.state.pending_monitoring_drill_id = None;
2769
- self.state.portal_monitoring.loading = false;
2770
- self.state.portal_monitoring.section_refresh_loading = None;
2771
- self.state.portal_monitoring.logs_fetch_loading = false;
2772
- self.state.portal_monitoring.metrics_drill = None;
2773
- self.state.portal_monitoring.metrics_drill_lines.clear();
2774
- self.state.portal_monitoring.metrics_drill_loading = false;
2768
+ self.cancel_portal_monitoring_requests();
2775
2769
  self.state.portal_monitoring.last_refresh = Some(std::time::Instant::now());
2776
- match current {
2777
- Some(MonitoringSection::Overview) | None => {
2778
- self.begin_portal_observability_request(ws);
2779
- }
2780
- Some(sec) => {
2781
- self.begin_monitoring_refresh_request(ws, sec);
2782
- }
2783
- }
2770
+ self.begin_portal_observability_request(ws);
2784
2771
  self.request_immediate_render("portal_obs_refresh");
2785
2772
  } else {
2786
2773
  self.state.portal_monitoring.error =
@@ -2799,6 +2786,7 @@ impl App {
2799
2786
  .monitoring_state
2800
2787
  .expand_section(MonitoringSection::Logs);
2801
2788
  if let Some(ws) = ws_client {
2789
+ self.cancel_portal_monitoring_requests();
2802
2790
  self.begin_monitoring_logs_request(ws);
2803
2791
  let inner_h = self.state.portal_monitoring.viewport_lines.max(5);
2804
2792
  let sw = self.state.portal_monitoring.summary_clip_width.max(24);
@@ -2878,6 +2866,7 @@ impl App {
2878
2866
  MonitoringSection::Dependencies,
2879
2867
  );
2880
2868
  if let Some(ws) = ws_client {
2869
+ self.cancel_portal_monitoring_requests();
2881
2870
  self.begin_dependencies_detail_drill(ws);
2882
2871
  self.request_immediate_render("portal_deps_drill");
2883
2872
  } else {
@@ -2907,6 +2896,7 @@ impl App {
2907
2896
  MonitoringSection::System,
2908
2897
  );
2909
2898
  if let Some(ws) = ws_client {
2899
+ self.cancel_portal_monitoring_requests();
2910
2900
  self.begin_system_diagnostics_request(ws);
2911
2901
  self.request_immediate_render("portal_system_diagnostics");
2912
2902
  } else {
@@ -2935,6 +2925,7 @@ impl App {
2935
2925
  MonitoringSection::Metrics,
2936
2926
  );
2937
2927
  if let Some(ws) = ws_client {
2928
+ self.cancel_portal_monitoring_requests();
2938
2929
  let panel = self
2939
2930
  .state
2940
2931
  .portal_monitoring
@@ -890,19 +890,46 @@ fn main() -> Result<()> {
890
890
  .to_string(),
891
891
  );
892
892
  }
893
+ let preserve_scroll =
894
+ app.state.portal_monitoring.scroll_offset;
895
+ let preserve_metrics_drill =
896
+ app.state.portal_monitoring.metrics_drill;
897
+ let preserve_metrics_drill_lines = app
898
+ .state
899
+ .portal_monitoring
900
+ .metrics_drill_lines
901
+ .clone();
902
+ let preserve_logs = app
903
+ .state
904
+ .portal_monitoring
905
+ .section_overrides
906
+ .get(&crate::monitoring::MonitoringSection::Logs)
907
+ .cloned();
893
908
  app.state.portal_monitoring.last_updated =
894
909
  Some(snapshot);
895
910
  app.state.portal_monitoring.content_lines = lines;
896
911
  app.state.portal_monitoring.section_overrides.clear();
912
+ if let Some(logs) = preserve_logs {
913
+ app.state
914
+ .portal_monitoring
915
+ .section_overrides
916
+ .insert(
917
+ crate::monitoring::MonitoringSection::Logs,
918
+ logs,
919
+ );
920
+ }
897
921
  app.state.portal_monitoring.section_refresh_loading =
898
922
  None;
899
923
  app.state.portal_monitoring.logs_fetch_loading = false;
900
- app.state.portal_monitoring.metrics_drill = None;
901
- app.state.portal_monitoring.metrics_drill_lines.clear();
924
+ app.state.portal_monitoring.metrics_drill =
925
+ preserve_metrics_drill;
926
+ app.state.portal_monitoring.metrics_drill_lines =
927
+ preserve_metrics_drill_lines;
902
928
  app.state.portal_monitoring.metrics_drill_loading =
903
929
  false;
904
930
  app.state.portal_monitoring.error = None;
905
- app.state.portal_monitoring.scroll_offset = 0;
931
+ app.state.portal_monitoring.scroll_offset =
932
+ preserve_scroll;
906
933
  } else {
907
934
  app.state.portal_monitoring.error = Some(
908
935
  "Unexpected observability response.".to_string(),
@@ -878,7 +878,7 @@ pub fn render(f: &mut Frame, state: &mut AppState) {
878
878
  ]);
879
879
 
880
880
  let keys_hint = Line::from(vec![Span::styled(
881
- "R sect/full · H trends · E export · Metrics + →/← drill · Dep ▼ + T detail · Sys ▼ + S diagnostics · L logs · A auto",
881
+ "R full refresh · L logs · wheel/Pg scroll · H metrics trends · → metrics drill · T dependencies · S system · A live",
882
882
  Style::default().fg(TEXT_DIM),
883
883
  )]);
884
884
 
@@ -983,7 +983,7 @@ pub fn render(f: &mut Frame, state: &mut AppState) {
983
983
  Span::styled("expand ", Style::default().fg(TEXT_DIM)),
984
984
  Span::styled("│ ", Style::default().fg(SEPARATOR_COLOR)),
985
985
  Span::styled("R ", Style::default().fg(CYBER_CYAN).bold()),
986
- Span::styled("full/sect ", Style::default().fg(TEXT_DIM)),
986
+ Span::styled("refresh ", Style::default().fg(TEXT_DIM)),
987
987
  Span::styled("│ ", Style::default().fg(SEPARATOR_COLOR)),
988
988
  Span::styled("L ", Style::default().fg(CYBER_CYAN).bold()),
989
989
  Span::styled("logs ", Style::default().fg(TEXT_DIM)),
@@ -1032,16 +1032,19 @@ pub fn render(f: &mut Frame, state: &mut AppState) {
1032
1032
  )]),
1033
1033
  Line::from(""),
1034
1034
  Line::from("Navigation"),
1035
- Line::from(" ↑/↓ select section Enter/Space expand/collapse PgUp/PgDn scroll"),
1035
+ Line::from(
1036
+ " ↑/↓ select section Enter/Space expand/collapse PgUp/PgDn/mouse scroll",
1037
+ ),
1038
+ Line::from(" When Logs is selected, ↑/↓ scroll log content first."),
1036
1039
  Line::from(" ESC close help / return to Main"),
1037
1040
  Line::from(""),
1038
1041
  Line::from("Actions"),
1039
- Line::from(" R refresh current row (Overview = full snapshot)"),
1040
- Line::from(
1041
- " L fetch Gateway logs H toggle trends E export JSON to current directory",
1042
- ),
1043
- Line::from(" Metrics expanded: cycle HTTP/Runs/Queue/SSE drill, leave drill"),
1044
- Line::from(" Dependencies expanded: T detail System expanded: S diagnostics"),
1042
+ Line::from(" R full snapshot refresh without closing your current section"),
1043
+ Line::from(" L jump to Logs + fetch Gateway logs"),
1044
+ Line::from(" H jump to Metrics + toggle trends Metrics drill ← leave drill"),
1045
+ Line::from(" T jump to Dependencies detail S jump to System diagnostics"),
1046
+ Line::from(" A toggle live auto-refresh (off by default for stable reading)"),
1047
+ Line::from(" E export diagnostics JSON to the current working directory"),
1045
1048
  Line::from(""),
1046
1049
  Line::from("Recovery"),
1047
1050
  Line::from(
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "4runr-os",
3
- "version": "2.10.43",
3
+ "version": "2.10.44",
4
4
  "type": "module",
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.",
5
+ "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.10.44: Stabilizes Portal Monitoring reading mode, global refresh, logs, and shortcut behavior. v2.10.43: Makes Portal Monitoring footer shortcuts jump to their sections and fixes log scrolling.",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "4runr": "dist/index.js",