///! Comprehensive OSIRIS + OpenRPC + Admin UI Example ///! ///! This example demonstrates: ///! 1. Starting a Hero Supervisor with OpenRPC server ///! 2. Building and serving the Admin UI (Yew WASM) ///! 3. Starting an OSIRIS runner ///! 4. Registering the runner with the supervisor ///! 5. Dispatching multiple OSIRIS jobs via OpenRPC ///! 6. Monitoring job execution via CLI and Web UI ///! 7. Graceful shutdown ///! ///! Services: ///! - Supervisor OpenRPC API: http://127.0.0.1:3030 ///! - Admin UI: http://127.0.0.1:8080 ///! ///! Usage: ///! ```bash ///! cargo run --example osiris_openrpc ///! ``` ///! ///! Requirements: ///! - Redis running on localhost:6379 ///! - Trunk installed (cargo install trunk) use hero_supervisor_openrpc_client::{SupervisorClient, JobBuilder}; use std::time::Duration; use escargot::CargoBuild; use std::process::{Stdio, Command}; use tokio::time::sleep; #[tokio::main] async fn main() -> Result<(), Box> { println!("πŸš€ OSIRIS + OpenRPC Comprehensive Example"); println!("=========================================\n"); // ======================================================================== // STEP 1: Build and start supervisor with OpenRPC // ======================================================================== println!("Step 1: Building and starting supervisor"); println!("─────────────────────────────────────────────────────────────\n"); let supervisor_binary = CargoBuild::new() .bin("supervisor") .current_release() .manifest_path("../supervisor/Cargo.toml") .run()?; println!("βœ… Supervisor binary built"); let mut supervisor = supervisor_binary.command() .arg("--redis-url") .arg("redis://localhost:6379") .arg("--port") .arg("3030") .arg("--admin-secret") .arg("admin_secret") .arg("--user-secret") .arg("user_secret") .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .spawn()?; println!("βœ… Supervisor started on port 3030"); println!("⏳ Waiting for supervisor to initialize..."); sleep(Duration::from_secs(5)).await; // Check if supervisor is still running match supervisor.try_wait()? { Some(status) => { return Err(format!("Supervisor exited early with status: {}", status).into()); } None => { println!("βœ… Supervisor is running"); } } // ======================================================================== // STEP 2: Build and serve Admin UI // ======================================================================== println!("\nStep 2: Building and serving Admin UI"); println!("─────────────────────────────────────────────────────────────\n"); let mut admin_ui = Command::new("trunk") .arg("serve") .arg("--port") .arg("8080") .arg("--address") .arg("127.0.0.1") .current_dir("clients/admin-ui") .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn()?; println!("βœ… Admin UI building..."); println!("🌐 Admin UI will be available at: http://127.0.0.1:8080"); sleep(Duration::from_secs(3)).await; // ======================================================================== // STEP 3: Build OSIRIS runner // ======================================================================== println!("\nStep 3: Building OSIRIS runner"); println!("─────────────────────────────────────────────────────────────\n"); let runner_binary = CargoBuild::new() .bin("runner_osiris") .current_release() .manifest_path("../runner_rust/Cargo.toml") .run()?; println!("βœ… OSIRIS runner binary built"); // ======================================================================== // STEP 4: Connect OpenRPC client // ======================================================================== println!("\nStep 4: Connecting OpenRPC client"); println!("─────────────────────────────────────────────────────────────\n"); let client = SupervisorClient::new("http://127.0.0.1:3030")?; println!("βœ… Connected to supervisor\n"); // ======================================================================== // STEP 5: Register and start OSIRIS runner // ======================================================================== println!("Step 5: Registering OSIRIS runner"); println!("─────────────────────────────────────────────────────────────\n"); let runner_path = runner_binary.path().to_string_lossy(); let db_path = "/tmp/osiris_openrpc.db"; // Register the runner with the supervisor // Note: The current OpenRPC server uses register_runner, not add_runner client.register_runner("admin_secret", "osiris_runner").await?; println!("βœ… Runner registered: osiris_runner"); client.start_runner("admin_secret", "osiris_runner").await?; println!("βœ… Runner started\n"); sleep(Duration::from_secs(2)).await; // ======================================================================== // STEP 6: Load job scripts // ======================================================================== println!("Step 6: Loading job scripts"); println!("─────────────────────────────────────────────────────────────\n"); let note_script = std::fs::read_to_string("examples/osiris_openrpc/note.rhai")?; let event_script = std::fs::read_to_string("examples/osiris_openrpc/event.rhai")?; let query_script = std::fs::read_to_string("examples/osiris_openrpc/query.rhai")?; let access_denied_script = std::fs::read_to_string("examples/osiris_openrpc/access_denied.rhai")?; println!("βœ… Loaded 4 job scripts\n"); // ======================================================================== // STEP 7: Dispatch jobs via OpenRPC // ======================================================================== println!("Step 7: Dispatching jobs"); println!("─────────────────────────────────────────────────────────────\n"); // Job 1: Create Note println!("πŸ“ Job 1: Creating Note..."); let job1 = JobBuilder::new() .caller_id("openrpc_client") .context_id("osiris_demo") .payload(¬e_script) .runner("osiris_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .build()?; let job1_result = client.run_job("user_secret", job1).await; match job1_result { Ok(result) => println!("βœ… {:?}\n", result), Err(e) => println!("❌ Job failed: {}\n", e), } sleep(Duration::from_secs(1)).await; // Job 2: Create Event println!("πŸ“… Job 2: Creating Event..."); let job2 = JobBuilder::new() .caller_id("openrpc_client") .context_id("osiris_demo") .payload(&event_script) .runner("osiris_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .build()?; let job2_result = client.run_job("user_secret", job2).await; match job2_result { Ok(result) => println!("βœ… {:?}\n", result), Err(e) => println!("❌ Job failed: {}\n", e), } sleep(Duration::from_secs(1)).await; // Job 3: Query Data println!("πŸ” Job 3: Querying Data..."); let job3 = JobBuilder::new() .caller_id("openrpc_client") .context_id("osiris_demo") .payload(&query_script) .runner("osiris_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; let job3_result = client.run_job("user_secret", job3).await; match job3_result { Ok(result) => println!("βœ… {:?}\n", result), Err(e) => println!("❌ Job failed: {}\n", e), } sleep(Duration::from_secs(1)).await; // Job 4: Access Control Test (should fail) println!("πŸ”’ Job 4: Testing Access Control (expected to fail)..."); let job4 = JobBuilder::new() .caller_id("openrpc_client") .context_id("osiris_demo") .payload(&access_denied_script) .runner("osiris_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; let job4_result = client.run_job("user_secret", job4).await; match job4_result { Ok(result) => println!("❌ Unexpected success: {:?}\n", result), Err(e) => println!("βœ… Access denied as expected: {}\n", e), } // ======================================================================== // STEP 8: Check runner status // ======================================================================== println!("\nStep 8: Checking runner status"); println!("─────────────────────────────────────────────────────────────\n"); let status = client.get_runner_status("admin_secret", "osiris_runner").await?; println!("Runner status: {:?}\n", status); // ======================================================================== // STEP 9: Keep services running for manual testing // ======================================================================== println!("\nStep 9: Services Running"); println!("─────────────────────────────────────────────────────────────\n"); println!("🌐 Admin UI: http://127.0.0.1:8080"); println!("πŸ“‘ OpenRPC API: http://127.0.0.1:3030"); println!("\n⏸️ Press Ctrl+C to stop all services...\n"); // Wait for Ctrl+C tokio::signal::ctrl_c().await?; // ======================================================================== // STEP 10: Cleanup // ======================================================================== println!("\n\nStep 10: Cleanup"); println!("─────────────────────────────────────────────────────────────\n"); client.stop_runner("admin_secret", "osiris_runner", false).await?; println!("βœ… Runner stopped"); client.remove_runner("admin_secret", "osiris_runner").await?; println!("βœ… Runner removed"); admin_ui.kill()?; println!("βœ… Admin UI stopped"); supervisor.kill()?; println!("βœ… Supervisor stopped"); println!("\n✨ Example completed successfully!"); Ok(()) }