diff --git a/CLAUDE.md b/CLAUDE.md index 482ca76..14517bf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,16 +28,18 @@ All keyboard navigation and service selection features successfully implemented: - ✅ **Smart Panel Switching**: Only cycles through panels with data (backup panel conditional) - ✅ **Scroll Support**: All panels support content scrolling with proper overflow indicators -**Current Status - October 23, 2025:** +**Current Status - October 24, 2025:** - All keyboard navigation features working correctly ✅ - Service selection cursor implemented with focus-aware highlighting ✅ - Panel scrolling fixed for System, Services, and Backup panels ✅ - Build display working: "Build: 25.05.20251004.3bcc93c" ✅ +- Configuration hash display implemented: "Config: d16f0d0" ✅ **Layout Achieved:** ``` NixOS: -Build: 25.05.20251004.3bcc93c # Target (currently shows "unknown") +Build: 25.05.20251004.3bcc93c +Config: d16f0d0 # Shows actual nixosbox config hash Active users: cm, simon CPU: ● Load: 0.02 0.31 0.86 • 3000MHz @@ -50,12 +52,10 @@ Storage: └─ ● 18% 167.4GB/928.2GB ``` -**Current Status - October 23, 2025:** -- System panel layout fully implemented with blue tree symbols ✅ -- Backup panel layout restructured per specification ✅ -- Tree symbols now use consistent blue theming across all panels ✅ -- Overflow handling restored for all widgets ("... and X more") ✅ -- Agent hash display working correctly ✅ +**System panel layout fully implemented with blue tree symbols ✅** +**Tree symbols now use consistent blue theming across all panels ✅** +**Overflow handling restored for all widgets ("... and X more") ✅** +**Agent hash display working correctly ✅** ### Current Keyboard Navigation Implementation @@ -77,28 +77,24 @@ Storage: - **Focus-Aware Selection**: Selection highlighting only visible when Services panel focused - **Dynamic Statusbar**: Context-aware shortcuts based on focused panel -### Current Priority: Visual Feedback Implementation +### Remote Command Execution - WORKING ✅ -**Remote Command Execution - PARTIALLY WORKING** ⚠️ - -Core infrastructure implemented but issues remain: +**All Issues Resolved (as of 2025-10-24):** - ✅ **ZMQ Command Protocol**: Extended with ServiceControl and SystemRebuild variants - ✅ **Agent Handlers**: systemctl and nixos-rebuild execution with maintenance mode -- ✅ **Dashboard Integration**: Existing keyboard shortcuts now execute commands -- ⚠️ **Service Control**: systemctl commands work but Space key toggle needs fixing -- ❌ **System Rebuild**: nixos-rebuild still failing with permission denied - -**Current Issues (as of 2025-10-23):** -1. **Service Toggle Bug**: Space key always sends "Start" instead of toggling start/stop -2. **nixos-rebuild Permissions**: Still getting permission denied despite sudo config updates -3. **No Command Completion**: Visual feedback shows ⏳ but never completes (missing response protocol) +- ✅ **Dashboard Integration**: Keyboard shortcuts execute commands +- ✅ **Service Control**: Fixed toggle logic - replaced with separate 's' (start) and 'S' (stop) +- ✅ **System Rebuild**: Fixed permission issues and sandboxing problems +- ✅ **Git Clone Approach**: Implemented for nixos-rebuild to avoid directory permissions +- ✅ **Visual Feedback**: Directional arrows for service status (↑ starting, ↓ stopping, ↻ restarting) **Keyboard Controls Status:** - **Services Panel**: - R (restart) ✅ Working - - Space (start/stop) ❌ Always starts, doesn't toggle -- **System Panel**: R (nixos-rebuild) ❌ Permission denied -- **Backup Panel**: B (trigger backup) ❓ Not tested + - s (start) ✅ Working + - S (stop) ✅ Working +- **System Panel**: R (nixos-rebuild) ✅ Working with --option sandbox false +- **Backup Panel**: B (trigger backup) ❓ Not implemented **Visual Feedback Implementation - IN PROGRESS:** @@ -126,26 +122,21 @@ Latest backup: → Latest backup: **Next Session Priority Tasks:** -**Critical Fixes Needed:** -1. **Fix Service Toggle Logic**: Debug why Space key service status detection isn't working - - Check systemd metric format (`systemd_{service}_status`) - - Test service status parsing logic - - Ensure toggle detects active vs inactive correctly - -2. **Resolve nixos-rebuild Permissions**: - - Verify sudo rules are actually applied after NixOS rebuild - - Test `sudo -u cm-agent sudo nixos-rebuild --help` manually - - May need different approach for nixos-rebuild access - -3. **Implement Command Response Protocol**: +**Remaining Features:** +1. **Command Response Protocol**: - Agent sends command completion/failure back to dashboard via ZMQ - Dashboard updates UI status from ⏳ to ● when commands complete - Clear success/failure status after timeout -**Secondary Tasks:** +2. **Backup Panel Features**: + - Implement backup trigger functionality (B key) + - Complete visual feedback for backup operations + - Add backup progress indicators + +**Enhancement Tasks:** - Add confirmation dialogs for destructive actions (stop/restart/rebuild) -- Complete visual feedback for System and Backup panels -- Test backup trigger functionality +- Implement command history/logging +- Add keyboard shortcuts help overlay **Future Enhanced Navigation:** - Add Page Up/Down for faster scrolling through long service lists @@ -155,7 +146,7 @@ Latest backup: → Latest backup: **Future Advanced Features:** - Service dependency visualization - Historical service status tracking -- Backup trigger functionality with progress indication +- Real-time log viewing integration ## Core Architecture Principles - CRITICAL diff --git a/dashboard/src/app.rs b/dashboard/src/app.rs index f1e5223..0c4c918 100644 --- a/dashboard/src/app.rs +++ b/dashboard/src/app.rs @@ -238,9 +238,25 @@ impl Dashboard { // Update TUI with new hosts and metrics (only if not headless) if let Some(ref mut tui_app) = self.tui_app { - let connected_hosts = self + let mut connected_hosts = self .metric_store .get_connected_hosts(Duration::from_secs(30)); + + // Add hosts that are rebuilding but may be temporarily disconnected + // Use extended timeout (5 minutes) for rebuilding hosts + let rebuilding_hosts = self + .metric_store + .get_connected_hosts(Duration::from_secs(300)); + + for host in rebuilding_hosts { + if !connected_hosts.contains(&host) { + // Check if this host is rebuilding in the UI + if tui_app.is_host_rebuilding(&host) { + connected_hosts.push(host); + } + } + } + tui_app.update_hosts(connected_hosts); tui_app.update_metrics(&self.metric_store); } diff --git a/dashboard/src/ui/mod.rs b/dashboard/src/ui/mod.rs index 20c86a1..d56c038 100644 --- a/dashboard/src/ui/mod.rs +++ b/dashboard/src/ui/mod.rs @@ -226,6 +226,16 @@ impl TuiApp { pub fn update_hosts(&mut self, hosts: Vec) { // Sort hosts alphabetically let mut sorted_hosts = hosts.clone(); + + // Keep hosts that are undergoing SystemRebuild even if they're offline + for (hostname, host_widgets) in &self.host_widgets { + if let Some(CommandStatus::InProgress { command_type: CommandType::SystemRebuild, .. }) = &host_widgets.command_status { + if !sorted_hosts.contains(hostname) { + sorted_hosts.push(hostname.clone()); + } + } + } + sorted_hosts.sort(); self.available_hosts = sorted_hosts; @@ -375,6 +385,18 @@ impl TuiApp { info!("Switched to host: {}", self.current_host.as_ref().unwrap()); } + /// Check if a host is currently rebuilding + pub fn is_host_rebuilding(&self, hostname: &str) -> bool { + if let Some(host_widgets) = self.host_widgets.get(hostname) { + matches!( + &host_widgets.command_status, + Some(CommandStatus::InProgress { command_type: CommandType::SystemRebuild, .. }) + ) + } else { + false + } + } + /// Switch to next panel (Shift+Tab) - only cycles through visible panels pub fn next_panel(&mut self) { let visible_panels = self.get_visible_panels(); @@ -660,10 +682,21 @@ impl TuiApp { spans.push(Span::styled(" ", Typography::title())); } - // Calculate overall host status from metrics - let host_status = self.calculate_host_status(host, metric_store); - let status_icon = StatusIcons::get_icon(host_status); - let status_color = Theme::status_color(host_status); + // Check if this host has a SystemRebuild command in progress + let (status_icon, status_color) = if let Some(host_widgets) = self.host_widgets.get(host) { + if let Some(CommandStatus::InProgress { command_type: CommandType::SystemRebuild, .. }) = &host_widgets.command_status { + // Show blue circular arrow during rebuild + ("↻", Theme::highlight()) + } else { + // Normal status icon based on metrics + let host_status = self.calculate_host_status(host, metric_store); + (StatusIcons::get_icon(host_status), Theme::status_color(host_status)) + } + } else { + // No host widgets yet, use normal status + let host_status = self.calculate_host_status(host, metric_store); + (StatusIcons::get_icon(host_status), Theme::status_color(host_status)) + }; // Add status icon spans.push(Span::styled(