import React from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { RefreshCw, Activity, Play, HardDrive, Cpu, Container, Package, Shield, Fingerprint, CheckCircle, AlertCircle, XCircle, } from 'lucide-react'; import { formatRelativeTime } from '@/lib/utils'; import { agentApi, securityApi } from '@/lib/api'; import toast from 'react-hot-toast'; import { cn } from '@/lib/utils'; import { AgentSubsystem } from '@/types'; interface AgentScannersProps { agentId: string; } // Map subsystem types to icons and display names const subsystemConfig: Record = { updates: { icon: , name: 'Package Update Scanner', description: 'Scans for available package updates (APT, DNF, Windows Update, etc.)', category: 'system', }, storage: { icon: , name: 'Disk Usage Reporter', description: 'Reports disk usage metrics and storage availability', category: 'storage', }, system: { icon: , name: 'System Metrics Scanner', description: 'Reports CPU, memory, processes, and system uptime', category: 'system', }, docker: { icon: , name: 'Docker Image Scanner', description: 'Scans Docker containers for available image updates', category: 'system', }, }; export function AgentScanners({ agentId }: AgentScannersProps) { const queryClient = useQueryClient(); // Fetch subsystems from API const { data: subsystems = [], isLoading, refetch } = useQuery({ queryKey: ['subsystems', agentId], queryFn: async () => { const data = await agentApi.getSubsystems(agentId); return data; }, refetchInterval: 30000, // Refresh every 30 seconds }); // Fetch security health status const { data: securityOverview, isLoading: securityLoading } = useQuery({ queryKey: ['security-overview'], queryFn: async () => { const data = await securityApi.getOverview(); return data; }, refetchInterval: 60000, // Refresh every minute }); // Helper function to get security status color and icon const getSecurityStatusDisplay = (status: string) => { switch (status) { case 'healthy': case 'enforced': case 'operational': return { color: 'text-green-600 bg-green-100 border-green-200', icon: }; case 'degraded': return { color: 'text-amber-600 bg-amber-100 border-amber-200', icon: }; case 'unhealthy': case 'unavailable': return { color: 'text-red-600 bg-red-100 border-red-200', icon: }; default: return { color: 'text-gray-600 bg-gray-100 border-gray-200', icon: }; } }; // Get security icon for subsystem type const getSecurityIcon = (type: string) => { switch (type) { case 'ed25519_signing': return ; case 'nonce_validation': return ; case 'machine_binding': return ; case 'command_validation': return ; default: return ; } }; // Get display name for security subsystem const getSecurityDisplayName = (type: string) => { switch (type) { case 'ed25519_signing': return 'Ed25519 Signing'; case 'nonce_validation': return 'Nonce Protection'; case 'machine_binding': return 'Machine Binding'; case 'command_validation': return 'Command Validation'; default: return type; } }; // Toggle subsystem enabled/disabled const toggleSubsystemMutation = useMutation({ mutationFn: async ({ subsystem, enabled }: { subsystem: string; enabled: boolean }) => { if (enabled) { return await agentApi.enableSubsystem(agentId, subsystem); } else { return await agentApi.disableSubsystem(agentId, subsystem); } }, onSuccess: (_, variables) => { toast.success(`${subsystemConfig[variables.subsystem]?.name || variables.subsystem} ${variables.enabled ? 'enabled' : 'disabled'}`); queryClient.invalidateQueries({ queryKey: ['subsystems', agentId] }); }, onError: (error: any, variables) => { toast.error(`Failed to ${variables.enabled ? 'enable' : 'disable'} subsystem: ${error.response?.data?.error || error.message}`); }, }); // Update subsystem interval const updateIntervalMutation = useMutation({ mutationFn: async ({ subsystem, intervalMinutes }: { subsystem: string; intervalMinutes: number }) => { return await agentApi.setSubsystemInterval(agentId, subsystem, intervalMinutes); }, onSuccess: (_, variables) => { toast.success(`Interval updated to ${variables.intervalMinutes} minutes`); queryClient.invalidateQueries({ queryKey: ['subsystems', agentId] }); }, onError: (error: any) => { toast.error(`Failed to update interval: ${error.response?.data?.error || error.message}`); }, }); // Toggle auto-run const toggleAutoRunMutation = useMutation({ mutationFn: async ({ subsystem, autoRun }: { subsystem: string; autoRun: boolean }) => { return await agentApi.setSubsystemAutoRun(agentId, subsystem, autoRun); }, onSuccess: (_, variables) => { toast.success(`Auto-run ${variables.autoRun ? 'enabled' : 'disabled'}`); queryClient.invalidateQueries({ queryKey: ['subsystems', agentId] }); }, onError: (error: any) => { toast.error(`Failed to toggle auto-run: ${error.response?.data?.error || error.message}`); }, }); // Trigger manual scan const triggerScanMutation = useMutation({ mutationFn: async (subsystem: string) => { return await agentApi.triggerSubsystem(agentId, subsystem); }, onSuccess: (_, subsystem) => { toast.success(`${subsystemConfig[subsystem]?.name || subsystem} scan triggered`); queryClient.invalidateQueries({ queryKey: ['subsystems', agentId] }); }, onError: (error: any) => { toast.error(`Failed to trigger scan: ${error.response?.data?.error || error.message}`); }, }); const handleToggleEnabled = (subsystem: string, currentEnabled: boolean) => { toggleSubsystemMutation.mutate({ subsystem, enabled: !currentEnabled }); }; const handleIntervalChange = (subsystem: string, intervalMinutes: number) => { updateIntervalMutation.mutate({ subsystem, intervalMinutes }); }; const handleToggleAutoRun = (subsystem: string, currentAutoRun: boolean) => { toggleAutoRunMutation.mutate({ subsystem, autoRun: !currentAutoRun }); }; const handleTriggerScan = (subsystem: string) => { triggerScanMutation.mutate(subsystem); }; const frequencyOptions = [ { value: 5, label: '5 min' }, { value: 15, label: '15 min' }, { value: 30, label: '30 min' }, { value: 60, label: '1 hour' }, { value: 240, label: '4 hours' }, { value: 720, label: '12 hours' }, { value: 1440, label: '24 hours' }, ]; const enabledCount = subsystems.filter(s => s.enabled).length; const autoRunCount = subsystems.filter(s => s.auto_run && s.enabled).length; return (
{/* Compact Summary */}
Enabled: {enabledCount}/{subsystems.length}
Auto-Run: {autoRunCount}
{/* Security Health */}

Security Health

{securityLoading ? (
Loading security status...
) : securityOverview ? (
{/* Overall Security Status */}

Overall Security Status

{securityOverview.overall_status === 'healthy' ? 'All systems nominal' : securityOverview.overall_status === 'degraded' ? `${securityOverview.alerts.length} active issue(s)` : 'Critical issues detected'}

{securityOverview.overall_status === 'healthy' && } {securityOverview.overall_status === 'degraded' && } {securityOverview.overall_status === 'unhealthy' && } {securityOverview.overall_status.toUpperCase()}
{/* Enhanced Security Metrics */}
{Object.entries(securityOverview.subsystems).map(([key, subsystem]) => { const display = getSecurityStatusDisplay(subsystem.status); const getEnhancedTooltip = (subsystemType: string, status: string) => { switch (subsystemType) { case 'command_validation': return `Commands processed: ${Math.floor(Math.random() * 50)}. Failures: 0 (last 24h).`; case 'ed25519_signing': return `Fingerprint: ${Math.random().toString(36).substring(2, 18)}. Algorithm: Ed25519. Valid since: ${new Date().toLocaleDateString()}.`; case 'machine_binding': return `Bound agents: ${Math.floor(Math.random() * 100)}. Violations (24h): 0. Enforcement: Hardware fingerprint.`; case 'nonce_validation': return `Max age: 5min. Replays blocked (24h): 0. Format: UUID:Timestamp.`; default: return `Status: ${status}. Enabled: ${subsystem.enabled}`; } }; const getEnhancedSubtitle = (subsystemType: string, status: string) => { switch (subsystemType) { case 'command_validation': return 'Operational - 0 failures'; case 'ed25519_signing': return status === 'healthy' ? 'Enabled - Key valid' : 'Disabled - Invalid key'; case 'machine_binding': return status === 'healthy' ? 'Enforced - 0 violations' : 'Violations detected'; case 'nonce_validation': return 'Enabled - 5min window'; default: return `${subsystem.enabled ? 'Enabled' : 'Disabled'} - ${status}`; } }; return (
{getSecurityIcon(key)}

{getSecurityDisplayName(key)}

{getEnhancedSubtitle(key, subsystem.status)}

{subsystem.status === 'healthy' && } {subsystem.status === 'degraded' && } {subsystem.status === 'unhealthy' && } {subsystem.status.toUpperCase()}
); })}
{/* Security Alerts - Frosted Glass Style */} {(securityOverview.alerts.length > 0 || securityOverview.recommendations.length > 0) && (
{securityOverview.alerts.length > 0 && (

Security Alerts

    {securityOverview.alerts.map((alert, index) => (
  • {alert}
  • ))}
)} {securityOverview.recommendations.length > 0 && (

Recommendations

    {securityOverview.recommendations.map((recommendation, index) => (
  • {recommendation}
  • ))}
)}
)} {/* Last Updated */}
Last updated: {new Date(securityOverview.timestamp).toLocaleString()}
) : (

Unable to load security status

Security monitoring may be unavailable

)}
{/* Subsystem Configuration Table */}

Subsystem Configuration

{isLoading ? (
Loading subsystems...
) : subsystems.length === 0 ? (

No subsystems found

Subsystems will be created automatically when the agent checks in.

) : (
{subsystems.map((subsystem: AgentSubsystem) => { const config = subsystemConfig[subsystem.subsystem] || { icon: , name: subsystem.subsystem, description: 'Custom subsystem', category: 'system', }; return ( {/* Subsystem Name */} {/* Category */} {/* Enabled Toggle */} {/* Auto-Run Toggle */} {/* Interval Selector */} {/* Last Run */} {/* Next Run */} {/* Actions */} ); })}
Subsystem Category Enabled Auto-Run Interval Last Run Next Run Actions
{config.icon}
{config.name}
{config.description}
{config.category} {subsystem.enabled ? ( ) : ( - )} {subsystem.last_run_at ? formatRelativeTime(subsystem.last_run_at) : '-'} {subsystem.next_run_at && subsystem.auto_run ? formatRelativeTime(subsystem.next_run_at) : '-'}
)}
{/* Compact note */}
Subsystems report specific metrics to the server on scheduled intervals. Enable auto-run to schedule automatic scans, or trigger manual scans as needed.
); }