@@ -45,6 +45,13 @@ enum Commands {
4545 /// Task description for the agent
4646 task : String ,
4747 } ,
48+ /// Show LuminaGuard system status (VM pool, active sessions, etc.)
49+ Status ,
50+ /// Manage LuminaGuard configuration
51+ Config {
52+ #[ command( subcommand) ]
53+ command : Option < ConfigCommands > ,
54+ } ,
4855 /// Start interactive chat mode
4956 Chat ,
5057 /// Spawn a new JIT Micro-VM
@@ -104,6 +111,17 @@ enum VmCommands {
104111 Pool ,
105112}
106113
114+ /// Configuration management subcommands
115+ #[ derive( Subcommand , Debug ) ]
116+ enum ConfigCommands {
117+ /// Show current configuration
118+ Show ,
119+ /// Validate configuration
120+ Validate ,
121+ /// Reset configuration to defaults
122+ Reset ,
123+ }
124+
107125#[ tokio:: main]
108126async fn main ( ) -> Result < ( ) > {
109127 // Parse command-line arguments
@@ -132,6 +150,14 @@ async fn main() -> Result<()> {
132150 info ! ( "Running agent task: {}" , task) ;
133151 run_agent ( task) . await ?;
134152 }
153+ Some ( Commands :: Status ) => {
154+ info ! ( "Showing system status..." ) ;
155+ show_status ( ) . await ?;
156+ }
157+ Some ( Commands :: Config { command } ) => {
158+ info ! ( "Managing configuration..." ) ;
159+ handle_config_command ( command) . await ?;
160+ }
135161 Some ( Commands :: Chat ) => {
136162 info ! ( "Starting interactive chat mode..." ) ;
137163 chat_mode ( ) . await ?;
@@ -220,6 +246,126 @@ async fn run_agent(task: String) -> Result<()> {
220246 }
221247}
222248
249+ /// Show system status (VM pool, active sessions, etc.)
250+ async fn show_status ( ) -> Result < ( ) > {
251+ println ! ( "\n ==========================================" ) ;
252+ println ! ( "📊 LuminaGuard System Status" ) ;
253+ println ! ( "==========================================" ) ;
254+
255+ // Show VM pool statistics
256+ println ! ( "\n 🖥️ VM Pool:" ) ;
257+ match vm:: pool_stats ( ) . await {
258+ Ok ( stats) => {
259+ println ! ( " Pool size: {}/{}" , stats. current_size, stats. max_size) ;
260+ println ! ( " Active VMs: {}" , stats. active_vms) ;
261+ println ! ( " Queued tasks: {}" , stats. queued_tasks) ;
262+ }
263+ Err ( e) => {
264+ println ! ( " Pool stats unavailable: {}" , e) ;
265+ }
266+ }
267+
268+ // Show version info
269+ println ! ( "\n 📦 Version:" ) ;
270+ println ! ( " Orchestrator: 0.1.0" ) ;
271+ println ! ( " Built from Rust" ) ;
272+
273+ // Show environment info
274+ println ! ( "\n 🔧 Environment:" ) ;
275+ println ! ( " Firecracker: {}" , {
276+ std:: process:: Command :: new( "which" )
277+ . arg( "firecracker" )
278+ . output( )
279+ . map( |o| {
280+ if o. status. success( ) {
281+ "Available"
282+ } else {
283+ "Not found"
284+ }
285+ } )
286+ . unwrap_or( "Unknown" )
287+ } ) ;
288+
289+ println ! ( "\n ==========================================\n " ) ;
290+
291+ Ok ( ( ) )
292+ }
293+
294+ /// Handle configuration management commands
295+ async fn handle_config_command ( command : Option < ConfigCommands > ) -> Result < ( ) > {
296+ let cmd = command. unwrap_or ( ConfigCommands :: Show ) ;
297+
298+ match cmd {
299+ ConfigCommands :: Show => {
300+ println ! ( "\n ==========================================" ) ;
301+ println ! ( "⚙️ LuminaGuard Configuration" ) ;
302+ println ! ( "==========================================\n " ) ;
303+
304+ println ! ( "VM Configuration:" ) ;
305+ let config = vm:: config:: VmConfig :: default ( ) ;
306+ println ! ( " vCPU Count: {}" , config. vcpu_count) ;
307+ println ! ( " Memory: {} MB" , config. memory_mb) ;
308+ println ! ( " Kernel Path: {}" , config. kernel_path) ;
309+ println ! ( " RootFS Path: {}" , config. effective_rootfs_path( ) ) ;
310+ println ! (
311+ " Networking: {}" ,
312+ if config. enable_networking {
313+ "Enabled"
314+ } else {
315+ "Disabled"
316+ }
317+ ) ;
318+ println ! (
319+ " RootFS Hardening: {}" ,
320+ if config. has_rootfs_hardening( ) {
321+ "Enabled"
322+ } else {
323+ "Disabled"
324+ }
325+ ) ;
326+
327+ println ! ( "\n Environment Variables:" ) ;
328+ for ( key, description) in [
329+ ( "LUMINAGUARD_POOL_SIZE" , "Snapshot pool size (default: 5)" ) ,
330+ (
331+ "LUMINAGUARD_SNAPSHOT_REFRESH_SECS" ,
332+ "Snapshot refresh interval (default: 3600)" ,
333+ ) ,
334+ (
335+ "LUMINAGUARD_SNAPSHOT_PATH" ,
336+ "Snapshot storage path (default: /var/lib/luminaguard/snapshots)" ,
337+ ) ,
338+ ] {
339+ let value = std:: env:: var ( key) . unwrap_or_else ( |_| "(not set)" . to_string ( ) ) ;
340+ println ! ( " {}: {} - {}" , key, value, description) ;
341+ }
342+
343+ println ! ( "\n ==========================================\n " ) ;
344+ }
345+ ConfigCommands :: Validate => {
346+ println ! ( "\n 🔍 Validating configuration..." ) ;
347+
348+ let config = vm:: config:: VmConfig :: default ( ) ;
349+ match config. validate ( ) {
350+ Ok ( _) => {
351+ println ! ( "✅ Configuration is valid!" ) ;
352+ }
353+ Err ( e) => {
354+ println ! ( "❌ Configuration validation failed: {}" , e) ;
355+ return Err ( e) ;
356+ }
357+ }
358+ }
359+ ConfigCommands :: Reset => {
360+ println ! ( "\n 🔄 Configuration reset to defaults." ) ;
361+ println ! ( "Note: Configuration is primarily managed via environment variables." ) ;
362+ println ! ( "To reset, unset environment variables and restart." ) ;
363+ }
364+ }
365+
366+ Ok ( ( ) )
367+ }
368+
223369/// Interactive chat mode - allows multiple exchanges with the agent
224370async fn chat_mode ( ) -> Result < ( ) > {
225371 use std:: io:: { self , Write } ;
@@ -722,11 +868,138 @@ mod tests {
722868 use std:: path:: Path ;
723869
724870 #[ test]
725- fn test_args_parsing ( ) {
871+ fn test_args_parsing_run ( ) {
726872 let args = Args :: parse_from ( [ "luminaguard" , "run" , "test task" ] ) ;
727873 assert ! ( matches!( args. command, Some ( Commands :: Run { .. } ) ) ) ;
728874 }
729875
876+ #[ test]
877+ fn test_args_parsing_status ( ) {
878+ let args = Args :: parse_from ( [ "luminaguard" , "status" ] ) ;
879+ assert ! ( matches!( args. command, Some ( Commands :: Status ) ) ) ;
880+ }
881+
882+ #[ test]
883+ fn test_args_parsing_config ( ) {
884+ let args = Args :: parse_from ( [ "luminaguard" , "config" , "show" ] ) ;
885+ assert ! ( matches!(
886+ args. command,
887+ Some ( Commands :: Config {
888+ command: Some ( ConfigCommands :: Show )
889+ } )
890+ ) ) ;
891+ }
892+
893+ #[ test]
894+ fn test_args_parsing_config_default ( ) {
895+ let args = Args :: parse_from ( [ "luminaguard" , "config" ] ) ;
896+ assert ! ( matches!(
897+ args. command,
898+ Some ( Commands :: Config { command: None } )
899+ ) ) ;
900+ }
901+
902+ #[ test]
903+ fn test_args_parsing_config_validate ( ) {
904+ let args = Args :: parse_from ( [ "luminaguard" , "config" , "validate" ] ) ;
905+ assert ! ( matches!(
906+ args. command,
907+ Some ( Commands :: Config {
908+ command: Some ( ConfigCommands :: Validate )
909+ } )
910+ ) ) ;
911+ }
912+
913+ #[ test]
914+ fn test_args_parsing_config_reset ( ) {
915+ let args = Args :: parse_from ( [ "luminaguard" , "config" , "reset" ] ) ;
916+ assert ! ( matches!(
917+ args. command,
918+ Some ( Commands :: Config {
919+ command: Some ( ConfigCommands :: Reset )
920+ } )
921+ ) ) ;
922+ }
923+
924+ #[ test]
925+ fn test_args_parsing_verbose ( ) {
926+ let args = Args :: parse_from ( [ "luminaguard" , "--verbose" , "status" ] ) ;
927+ assert ! ( args. verbose) ;
928+ assert ! ( matches!( args. command, Some ( Commands :: Status ) ) ) ;
929+ }
930+
931+ #[ test]
932+ fn test_args_parsing_chat ( ) {
933+ let args = Args :: parse_from ( [ "luminaguard" , "chat" ] ) ;
934+ assert ! ( matches!( args. command, Some ( Commands :: Chat ) ) ) ;
935+ }
936+
937+ #[ test]
938+ fn test_args_parsing_spawn_vm ( ) {
939+ let args = Args :: parse_from ( [ "luminaguard" , "spawn-vm" ] ) ;
940+ assert ! ( matches!( args. command, Some ( Commands :: SpawnVm ) ) ) ;
941+ }
942+
943+ #[ test]
944+ fn test_args_parsing_daemon ( ) {
945+ let args = Args :: parse_from ( [ "luminaguard" , "daemon" , "--metrics-port" , "8080" ] ) ;
946+ assert ! ( matches!( args. command, Some ( Commands :: Daemon { .. } ) ) ) ;
947+ if let Some ( Commands :: Daemon { metrics_port } ) = args. command {
948+ assert_eq ! ( metrics_port, 8080 ) ;
949+ }
950+ }
951+
952+ #[ tokio:: test]
953+ async fn test_show_status ( ) {
954+ // This test should always succeed (doesn't require actual VM)
955+ let result = show_status ( ) . await ;
956+ assert ! (
957+ result. is_ok( ) ,
958+ "show_status should succeed: {:?}" ,
959+ result. err( )
960+ ) ;
961+ }
962+
963+ #[ tokio:: test]
964+ async fn test_config_show ( ) {
965+ let result = handle_config_command ( Some ( ConfigCommands :: Show ) ) . await ;
966+ assert ! (
967+ result. is_ok( ) ,
968+ "config show should succeed: {:?}" ,
969+ result. err( )
970+ ) ;
971+ }
972+
973+ #[ tokio:: test]
974+ async fn test_config_validate ( ) {
975+ let result = handle_config_command ( Some ( ConfigCommands :: Validate ) ) . await ;
976+ assert ! (
977+ result. is_ok( ) ,
978+ "config validate should succeed: {:?}" ,
979+ result. err( )
980+ ) ;
981+ }
982+
983+ #[ tokio:: test]
984+ async fn test_config_reset ( ) {
985+ let result = handle_config_command ( Some ( ConfigCommands :: Reset ) ) . await ;
986+ assert ! (
987+ result. is_ok( ) ,
988+ "config reset should succeed: {:?}" ,
989+ result. err( )
990+ ) ;
991+ }
992+
993+ #[ tokio:: test]
994+ async fn test_config_default ( ) {
995+ let result = handle_config_command ( None ) . await ;
996+ assert ! (
997+ result. is_ok( ) ,
998+ "config with no subcommand should succeed: {:?}" ,
999+ result. err( )
1000+ ) ;
1001+ }
1002+
7301003 #[ tokio:: test]
7311004 async fn test_spawn_vm_integration ( ) {
7321005 // Skip if firecracker or resources are missing
0 commit comments