//! Simple End-to-End Example //! //! A minimal example showing supervisor + runner + client workflow. //! //! Prerequisites: //! - Redis running on localhost:6379 //! //! Usage: //! ```bash //! # Terminal 1: Start Redis //! redis-server //! //! # Terminal 2: Run this example //! RUST_LOG=info cargo run --example simple_e2e //! ``` use anyhow::Result; use log::info; use std::time::Duration; use tokio::time::sleep; use hero_supervisor_openrpc_client::{SupervisorClient, JobBuilder}; #[tokio::main] async fn main() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); println!("\n╔════════════════════════════════════════╗"); println!("║ Simple End-to-End Demo ║"); println!("╚════════════════════════════════════════╝\n"); let supervisor_url = "http://localhost:3030"; let runner_id = "test_runner"; let secret = "admin_secret"; // Create supervisor client let client = SupervisorClient::new(supervisor_url)?; println!("📝 Prerequisites:"); println!(" 1. Redis running on localhost:6379"); println!(" 2. Supervisor running on {}", supervisor_url); println!(" 3. Runner '{}' registered and running\n", runner_id); println!("💡 To start the supervisor:"); println!(" cargo run --bin hero-supervisor -- --redis-url redis://localhost:6379\n"); println!("💡 To start a runner:"); println!(" cd /Users/timurgordon/code/git.ourworld.tf/herocode/runner_rust"); println!(" cargo run --bin runner_osis -- {} --redis-url redis://localhost:6379\n", runner_id); println!("⏳ Waiting 3 seconds for you to start the prerequisites...\n"); sleep(Duration::from_secs(3)).await; // Register runner println!("📋 Step 1: Registering Runner"); println!("─────────────────────────────────────────"); let queue = format!("hero:q:work:type:osis:group:default:inst:{}", runner_id); match client.register_runner(secret, runner_id, &queue).await { Ok(_) => { println!("✅ Runner registered successfully"); } Err(e) => { println!("⚠️ Registration error: {} (runner might already be registered)", e); } } sleep(Duration::from_secs(1)).await; // Run a simple job println!("\n📋 Step 2: Running a Simple Job (Blocking)"); println!("─────────────────────────────────────────"); let job = JobBuilder::new() .caller_id("simple_demo") .context_id("demo_context") .payload(r#" let message = "Hello from the runner!"; let number = 42; to_json(#{ message: message, number: number, timestamp: timestamp() }) "#) .runner(runner_id) .executor("rhai") .timeout(30) .build()?; let job_id = job.id.clone(); info!("Sending job with ID: {}", job_id); match client.job_run(secret, job, Some(30)).await { Ok(response) => { println!("✅ Job completed!"); if let Some(result) = response.result { println!(" Result: {}", result); } } Err(e) => { println!("❌ Job failed: {}", e); return Ok(()); } } // Run another job (calculation) println!("\n📋 Step 3: Running a Calculation Job"); println!("─────────────────────────────────────────"); let calc_job = JobBuilder::new() .caller_id("simple_demo") .context_id("demo_context") .payload(r#" let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let sum = 0; let product = 1; for n in numbers { sum += n; product *= n; } to_json(#{ sum: sum, product: product, count: numbers.len(), average: sum / numbers.len() }) "#) .runner(runner_id) .executor("rhai") .timeout(30) .build()?; let calc_job_id = calc_job.id.clone(); info!("Sending calculation job with ID: {}", calc_job_id); match client.job_run(secret, calc_job, Some(30)).await { Ok(response) => { println!("✅ Calculation completed!"); if let Some(result) = response.result { println!(" Result: {}", result); } } Err(e) => { println!("❌ Calculation failed: {}", e); } } // Start a non-blocking job println!("\n📋 Step 4: Starting a Non-Blocking Job"); println!("─────────────────────────────────────────"); let async_job = JobBuilder::new() .caller_id("simple_demo") .context_id("demo_context") .payload(r#" let result = "This job was started asynchronously"; to_json(result) "#) .runner(runner_id) .executor("rhai") .timeout(30) .build()?; let async_job_id = async_job.id.clone(); info!("Starting async job with ID: {}", async_job_id); match client.job_start(secret, async_job).await { Ok(response) => { println!("✅ Job started!"); println!(" Job ID: {} (running in background)", response.job_id); println!(" Status: {}", response.status); } Err(e) => { println!("❌ Failed to start job: {}", e); } } // Summary println!("\n╔════════════════════════════════════════╗"); println!("║ Demo Summary ║"); println!("╚════════════════════════════════════════╝"); println!("✅ Runner registered: {}", runner_id); println!("✅ Blocking jobs completed: 2"); println!("✅ Non-blocking jobs started: 1"); println!("\n🎉 Demo completed successfully!\n"); println!("📚 What happened:"); println!(" 1. Registered a runner with the supervisor"); println!(" 2. Sent jobs with Rhai scripts to execute"); println!(" 3. Supervisor queued jobs to the runner"); println!(" 4. Runner executed the scripts and returned results"); println!(" 5. Client received results (for blocking jobs)\n"); println!("🔍 Key Concepts:"); println!(" • job.run = Execute and wait for result (blocking)"); println!(" • job.start = Start and return immediately (non-blocking)"); println!(" • Jobs contain Rhai scripts that run on the runner"); println!(" • Supervisor coordinates job distribution via Redis\n"); Ok(()) }