Lee Smet bc30c9cc89 Add supervisor API
Signed-off-by: Lee Smet <lee.smet@hotmail.com>
2025-08-22 23:19:05 +02:00
2025-08-21 17:03:21 +02:00
2025-08-22 23:19:05 +02:00
2025-08-20 12:33:09 +02:00
2025-08-22 23:19:05 +02:00
2025-08-22 23:19:05 +02:00
2025-08-22 23:19:05 +02:00

herocoordinator

Supervisor JSON-RPC client

This crate now includes a typed client to communicate with an external "supervisor" component via JSON-RPC 2.0 over HTTP and WebSocket, generated from the OpenAPI spec in specs/supervisor.yaml.

Highlights

  • Transports: HTTP and WebSocket (jsonrpsee).
  • Session: optional bearer token support (Authorization header).
  • Methods implemented: fetch_nonce, authenticate, whoami, play, create_job, start_job, run_job, get_job_status, get_job_output, get_job_logs, list_jobs, stop_job, delete_job, clear_all_jobs.
  • Types mirror the spec exactly (enum casing etc.).

Enable features

jsonrpsee client features are enabled in Cargo.toml:

  • server, macros, client, http-client, ws-client.

Usage

Add to your crate imports:

use herocoordinator::supervisor::{
    SupervisorClient,
    ScriptType,
    JobParams,
};

Create an HTTP client (default http://127.0.0.1:9944/)

# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = SupervisorClient::new_http("http://127.0.0.1:9944/").await?;

// Optional: obtain a token (out-of-band) and set it
// client.set_bearer_token("your-token").await?;

let pk = "abcdef1234deadbeef";
let nonce = client.fetch_nonce(pk).await?;
let ok = client.authenticate(pk, "signature-here", &nonce).await?;
assert!(ok, "authentication should succeed");

// whoami
let who = client.whoami().await?;
println!("whoami: {who}");

// play
let res = client.play(r#"echo "hello""#).await?;
println!("play.output: {}", res.output);

// create a job
let job_id = client
    .create_job(JobParams {
        script: r#"print("hi")"#.into(),
        script_type: ScriptType::Python,
        caller_id: "cli".into(),
        context_id: "demo".into(),
        timeout: Some(30),
        prerequisites: Some(vec![]),
    })
    .await?;
println!("created job: {job_id}");

// start a job
let started = client.start_job(&job_id).await?;
println!("job started: {}", started.success);

// get status / output / logs
let status = client.get_job_status(&job_id).await?;
println!("job status: {:?}", status);

let output = client.get_job_output(&job_id).await?;
println!("job output: {output}");

let logs = client.get_job_logs(&job_id).await?;
println!("job logs: {:?}", logs.logs);

// list / stop / delete / clear
let jobs = client.list_jobs().await?;
println!("jobs: {:?}", jobs);

// stop and delete are noop if job is already finished (server-defined behavior)
let _ = client.stop_job(&job_id).await?;
let _ = client.delete_job(&job_id).await?;

// clear all jobs (use with care)
let _ = client.clear_all_jobs().await?;
# Ok(())
# }

Create a WebSocket client (default ws://127.0.0.1:9944/)

# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SupervisorClient::new_ws("ws://127.0.0.1:9944/").await?;
// Use the same methods as the HTTP client
let who = client.whoami().await?;
println!("whoami: {who}");
# Ok(())
# }

Notes on authenticate and tokens

  • The OpenAPI spec defines authenticate returning a boolean. If your deployment provides a token via a header or another method, capture it externally and set it on the client using:
    • client.set_bearer_token("...").await?
    • To remove: client.clear_bearer_token().await?

Types

  • Enums and DTOs mirror the OpenAPI casing:
    • ScriptType: "OSIS" | "SAL" | "V" | "Python"
    • JobStatus: "Dispatched" | "WaitingForPrerequisites" | "Started" | "Error" | "Finished"
  • JobParams include: script, script_type, caller_id, context_id, timeout?, prerequisites?.

Testing

  • Unit tests verify enum casing and request param shapes. No live server needed. Run: cargo test.

Files

  • src/supervisor/mod.rs
  • src/supervisor/types.rs
  • src/supervisor/error.rs
  • src/supervisor/client.rs
Description
No description provided
Readme 508 KiB
Languages
Rust 89.8%
Python 6.2%
V 4%