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

@@ -26,6 +26,7 @@ indexmap = "2.0"
jsonrpsee = { version = "0.24", features = ["http-client", "macros"] }
tokio = { version = "1.0", features = ["full"] }
hero-supervisor = { path = "../.." }
runner_rust = { git = "https://git.ourworld.tf/herocode/runner_rust.git", branch = "main" }
env_logger = "0.11"
# WASM-specific dependencies

View File

@@ -115,40 +115,16 @@ impl From<wasm_bindgen::JsValue> for ClientError {
/// Result type for client operations
pub type ClientResult<T> = Result<T, ClientError>;
/// Types of runners supported by the supervisor
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RunnerType {
/// SAL Runner for system abstraction layer operations
SALRunner,
/// OSIS Runner for operating system interface operations
OSISRunner,
/// V Runner for virtualization operations
VRunner,
/// Python Runner for Python-based actors
PyRunner,
}
/// Process manager type for WASM compatibility
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ProcessManagerType {
Simple,
Tmux(String),
}
/// Configuration for an actor runner
/// Configuration for a runner
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunnerConfig {
/// Unique identifier for the actor
pub actor_id: String,
/// Type of runner
pub runner_type: RunnerType,
/// Path to the actor binary
pub binary_path: PathBuf,
/// Database path for the actor
pub db_path: String,
/// Redis URL for job queue
pub redis_url: String,
/// Name of the runner
pub name: String,
/// Command to run the runner (full command line)
pub command: String,
/// Optional environment variables
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<std::collections::HashMap<String, String>>,
}
@@ -171,6 +147,21 @@ pub struct JobStatusResponse {
pub completed_at: Option<String>,
}
/// Response from job.run (blocking execution)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobRunResponse {
pub job_id: String,
pub status: String,
pub result: Option<String>,
}
/// Response from job.start (non-blocking execution)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobStartResponse {
pub job_id: String,
pub status: String,
}
// Re-export Job types from runner_rust crate (native only)
#[cfg(not(target_arch = "wasm32"))]
pub use runner_rust::{Job, JobStatus, JobError, JobBuilder, Client, ClientBuilder};
@@ -219,7 +210,7 @@ pub type ProcessStatus = ProcessStatusWrapper;
/// Re-export types from supervisor crate for native builds
#[cfg(not(target_arch = "wasm32"))]
pub use hero_supervisor::{ProcessManagerType, RunnerStatus};
pub use hero_supervisor::RunnerStatus;
#[cfg(not(target_arch = "wasm32"))]
pub use hero_supervisor::runner::LogInfo;
@@ -262,17 +253,17 @@ impl SupervisorClient {
}
/// Register a new runner to the supervisor with secret authentication
/// The runner name is also used as the queue name
pub async fn register_runner(
&self,
secret: &str,
name: &str,
queue: &str,
) -> ClientResult<()> {
let _: () = self
.client
.request(
"register_runner",
rpc_params![secret, name, queue],
rpc_params![secret, name],
)
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
@@ -305,40 +296,47 @@ impl SupervisorClient {
Ok(jobs)
}
/// Run a job on the appropriate runner and return the result
/// Run a job on the appropriate runner and wait for the result (blocking)
/// This method queues the job and waits for completion before returning
pub async fn job_run(
&self,
secret: &str,
job: Job,
) -> ClientResult<JobResult> {
let params = serde_json::json!({
timeout: Option<u64>,
) -> ClientResult<JobRunResponse> {
let mut params = serde_json::json!({
"secret": secret,
"job": job
});
let result: JobResult = self
if let Some(t) = timeout {
params["timeout"] = serde_json::json!(t);
}
let result: JobRunResponse = self
.client
.request("job.run", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(result)
}
/// Start a previously created job by queuing it to its assigned runner
/// Start a job without waiting for the result (non-blocking)
/// This method queues the job and returns immediately with the job_id
pub async fn job_start(
&self,
secret: &str,
job_id: &str,
) -> ClientResult<()> {
job: Job,
) -> ClientResult<JobStartResponse> {
let params = serde_json::json!({
"secret": secret,
"job_id": job_id
"job": job
});
let _: () = self
let result: JobStartResponse = self
.client
.request("job.start", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
Ok(result)
}
/// Get the current status of a job
@@ -360,10 +358,14 @@ impl SupervisorClient {
}
/// Remove a runner from the supervisor
pub async fn remove_runner(&self, actor_id: &str) -> ClientResult<()> {
pub async fn remove_runner(&self, secret: &str, actor_id: &str) -> ClientResult<()> {
let params = serde_json::json!({
"secret": secret,
"actor_id": actor_id
});
let _: () = self
.client
.request("remove_runner", rpc_params![actor_id])
.request("remove_runner", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
}
@@ -378,37 +380,54 @@ impl SupervisorClient {
}
/// Start a specific runner
pub async fn start_runner(&self, actor_id: &str) -> ClientResult<()> {
pub async fn start_runner(&self, secret: &str, actor_id: &str) -> ClientResult<()> {
let params = serde_json::json!({
"secret": secret,
"actor_id": actor_id
});
let _: () = self
.client
.request("start_runner", rpc_params![actor_id])
.request("start_runner", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
}
/// Stop a specific runner
pub async fn stop_runner(&self, actor_id: &str, force: bool) -> ClientResult<()> {
pub async fn stop_runner(&self, secret: &str, actor_id: &str, force: bool) -> ClientResult<()> {
let params = serde_json::json!({
"secret": secret,
"actor_id": actor_id,
"force": force
});
let _: () = self
.client
.request("stop_runner", rpc_params![actor_id, force])
.request("stop_runner", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
}
/// Add a runner to the supervisor
pub async fn add_runner(&self, config: RunnerConfig, process_manager: ProcessManagerType) -> ClientResult<()> {
pub async fn add_runner(&self, secret: &str, config: RunnerConfig) -> ClientResult<()> {
let params = serde_json::json!({
"secret": secret,
"config": config
});
let _: () = self
.client
.request("add_runner", rpc_params![config, process_manager])
.request("add_runner", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(())
}
/// Get status of a specific runner
pub async fn get_runner_status(&self, actor_id: &str) -> ClientResult<RunnerStatus> {
pub async fn get_runner_status(&self, secret: &str, actor_id: &str) -> ClientResult<RunnerStatus> {
let params = serde_json::json!({
"secret": secret,
"actor_id": actor_id
});
let status: RunnerStatus = self
.client
.request("get_runner_status", rpc_params![actor_id])
.request("get_runner_status", rpc_params![params])
.await.map_err(|e| ClientError::JsonRpc(e))?;
Ok(status)
}