feat: simplify OpenRPC API and reorganize examples

- Simplified RunnerConfig to just name, command, and optional env
- Removed RunnerType and ProcessManagerType enums
- Removed db_path, redis_url, binary_path from config
- Made runner name also serve as queue name (no separate queue param)
- Added secret-based authentication to all runner management methods
- Created comprehensive osiris_openrpc example
- Archived old examples to _archive/
- Updated client API to match simplified supervisor interface
This commit is contained in:
Timur Gordon
2025-10-27 14:20:40 +01:00
parent ef56ed0290
commit 98b2718d58
33 changed files with 3018 additions and 788 deletions

View File

@@ -5,9 +5,12 @@
//! then pass it to SupervisorApp for runtime management.
use crate::Supervisor;
#[cfg(feature = "mycelium")]
use crate::mycelium::MyceliumIntegration;
use log::{info, error, debug};
#[cfg(feature = "mycelium")]
use std::sync::Arc;
#[cfg(feature = "mycelium")]
use tokio::sync::Mutex;
/// Main supervisor application
@@ -54,37 +57,45 @@ impl SupervisorApp {
/// Start the Mycelium integration
async fn start_mycelium_integration(&self) -> Result<(), Box<dyn std::error::Error>> {
// Skip Mycelium if URL is empty
if self.mycelium_url.is_empty() {
info!("Mycelium integration disabled (no URL provided)");
return Ok(());
#[cfg(feature = "mycelium")]
{
// Skip Mycelium if URL is empty
if self.mycelium_url.is_empty() {
info!("Mycelium integration disabled (no URL provided)");
return Ok(());
}
info!("Starting Mycelium integration...");
let supervisor_for_mycelium = Arc::new(Mutex::new(self.supervisor.clone()));
let mycelium_url = self.mycelium_url.clone();
let topic = self.topic.clone();
let mycelium_integration = MyceliumIntegration::new(
supervisor_for_mycelium,
mycelium_url,
topic,
);
// Start the Mycelium integration in a background task
let integration_handle = tokio::spawn(async move {
if let Err(e) = mycelium_integration.start().await {
error!("Mycelium integration error: {}", e);
}
});
// Give the integration a moment to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
info!("Mycelium integration started successfully");
// Store the handle for potential cleanup
std::mem::forget(integration_handle); // For now, let it run in background
}
info!("Starting Mycelium integration...");
let supervisor_for_mycelium = Arc::new(Mutex::new(self.supervisor.clone()));
let mycelium_url = self.mycelium_url.clone();
let topic = self.topic.clone();
let mycelium_integration = MyceliumIntegration::new(
supervisor_for_mycelium,
mycelium_url,
topic,
);
// Start the Mycelium integration in a background task
let integration_handle = tokio::spawn(async move {
if let Err(e) = mycelium_integration.start().await {
error!("Mycelium integration error: {}", e);
}
});
// Give the integration a moment to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
info!("Mycelium integration started successfully");
// Store the handle for potential cleanup
std::mem::forget(integration_handle); // For now, let it run in background
#[cfg(not(feature = "mycelium"))]
{
info!("Mycelium integration not enabled (compile with --features mycelium)");
}
Ok(())
}

View File

@@ -7,6 +7,8 @@ pub mod job;
pub mod supervisor;
pub mod app;
pub mod openrpc;
#[cfg(feature = "mycelium")]
pub mod mycelium;
// Re-export main types for convenience
@@ -15,4 +17,6 @@ pub use runner::{Runner, RunnerConfig, RunnerResult, RunnerStatus};
pub use supervisor::{Supervisor, SupervisorBuilder, ProcessManagerType};
pub use runner_rust::{Job, JobBuilder, JobStatus, JobError, Client, ClientBuilder};
pub use app::SupervisorApp;
#[cfg(feature = "mycelium")]
pub use mycelium::{MyceliumIntegration, MyceliumServer};

View File

@@ -303,20 +303,88 @@ impl MyceliumIntegration {
}
"job.run" => {
// Run job and wait for result (blocking)
if let Some(param_obj) = params.as_array().and_then(|arr| arr.get(0)) {
let _secret = param_obj.get("secret")
.and_then(|v| v.as_str())
.ok_or("missing secret")?;
let _job = param_obj.get("job")
let job_value = param_obj.get("job")
.ok_or("missing job")?;
// TODO: Implement actual job execution
Ok(json!("job_queued"))
let timeout = param_obj.get("timeout")
.and_then(|v| v.as_u64())
.unwrap_or(60);
// Deserialize the job
let job: runner_rust::job::Job = serde_json::from_value(job_value.clone())
.map_err(|e| format!("invalid job format: {}", e))?;
let job_id = job.id.clone();
let runner_name = job.runner.clone();
// Verify signatures
job.verify_signatures()
.map_err(|e| format!("signature verification failed: {}", e))?;
info!("Job {} signature verification passed for signatories: {:?}",
job_id, job.signatories());
// Queue and wait for result
let mut supervisor_guard = self.supervisor.lock().await;
let result = supervisor_guard.queue_and_wait(&runner_name, job, timeout)
.await
.map_err(|e| format!("job execution failed: {}", e))?;
Ok(json!({
"job_id": job_id,
"status": "completed",
"result": result
}))
} else {
Err("invalid job.run params".into())
}
}
"job.start" => {
// Start job without waiting (non-blocking)
if let Some(param_obj) = params.as_array().and_then(|arr| arr.get(0)) {
let _secret = param_obj.get("secret")
.and_then(|v| v.as_str())
.ok_or("missing secret")?;
let job_value = param_obj.get("job")
.ok_or("missing job")?;
// Deserialize the job
let job: runner_rust::job::Job = serde_json::from_value(job_value.clone())
.map_err(|e| format!("invalid job format: {}", e))?;
let job_id = job.id.clone();
let runner_name = job.runner.clone();
// Verify signatures
job.verify_signatures()
.map_err(|e| format!("signature verification failed: {}", e))?;
info!("Job {} signature verification passed for signatories: {:?}",
job_id, job.signatories());
// Queue the job without waiting
let mut supervisor_guard = self.supervisor.lock().await;
supervisor_guard.queue_job_to_runner(&runner_name, job)
.await
.map_err(|e| format!("failed to queue job: {}", e))?;
Ok(json!({
"job_id": job_id,
"status": "queued"
}))
} else {
Err("invalid job.start params".into())
}
}
"job.status" => {
if let Some(_job_id) = params.as_array().and_then(|arr| arr.get(0)).and_then(|v| v.as_str()) {
// TODO: Implement actual job status lookup
@@ -339,7 +407,7 @@ impl MyceliumIntegration {
let methods = vec![
"list_runners", "register_runner", "start_runner", "stop_runner",
"get_runner_status", "get_all_runner_status", "start_all", "stop_all",
"job.run", "job.status", "job.result", "rpc.discover"
"job.run", "job.start", "job.status", "job.result", "rpc.discover"
];
Ok(json!(methods))
}

View File

@@ -330,7 +330,6 @@ pub trait SupervisorRpc {
#[method(name = "queue_job_to_runner")]
async fn queue_job_to_runner(&self, params: QueueJobParams) -> RpcResult<()>;
/// Get a job by job ID
#[method(name = "get_job")]
async fn get_job(&self, job_id: String) -> RpcResult<Job>;