Pilot
Pilot Rust SDK
Async Rust client for the Cuitty Pilot API — manage playbooks, runs, and providers with reqwest and serde.
Pilot Rust SDK
cuitty-pilot-sdk is an async Rust client for the Pilot API. It uses reqwest for HTTP, serde for JSON serialization, and thiserror for typed errors.
Install
Add to your Cargo.toml:
[dependencies]
cuitty-pilot-sdk = { path = "../packages/sdk-rust" }
# or when published:
# cuitty-pilot-sdk = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
Quick start
use cuitty_pilot_sdk::{PilotClient, StartRunRequest, PilotError};
#[tokio::main]
async fn main() -> Result<(), PilotError> {
let client = PilotClient::new(
"http://localhost:4320",
Some("your-auth-token".into()),
);
// List all playbooks
let playbooks = client.list_playbooks(None).await?;
// Start a run
let resp = client.start_run(&StartRunRequest {
playbook_id: "cloudflare.dns.add-a-record".into(),
mode: Some("autonomous".into()),
inputs: Some(serde_json::json!({
"zone": "example.com",
"record_name": "app",
"ip_address": "1.2.3.4"
})),
..Default::default()
}).await?;
println!("Run started: {} ({})", resp.id, resp.status);
// Get run details
let run = client.get_run(&resp.id).await?;
println!("Run status: {}", run.status);
Ok(())
}
PilotClient
The main entry point. All methods are async and return Result<T, PilotError>.
// Without auth
let client = PilotClient::new("http://localhost:4320", None);
// With bearer token
let client = PilotClient::new(
"http://localhost:4320",
Some("ey...".into()),
);
| Method | Returns | Description |
|---|---|---|
health() | HealthStatus | Check server health |
list_playbooks(target) | Vec<PlaybookSummary> | List playbooks, optionally filtered |
get_playbook(id) | Playbook | Get playbook by ID (latest version) |
create_playbook(req) | CreatePlaybookResponse | Create a new playbook |
update_playbook(id, req) | UpdatePlaybookResponse | Update (version-bump) a playbook |
delete_playbook(id) | DeletePlaybookResponse | Delete all versions |
start_run(req) | StartRunResponse | Start a new run |
list_runs(status, playbook_id, limit) | Vec<Run> | List runs with optional filters |
get_run(id) | Run | Get run details including steps |
abort_run(id) | AbortRunResponse | Abort a running or queued run |
approve_run(id, req) | ApproveResponse | Submit approval decision for a step |
list_providers() | Vec<Provider> | List all provider profiles |
Playbooks
// List all
let playbooks = client.list_playbooks(None).await?;
// Filter by target
let cf = client.list_playbooks(Some("cloudflare")).await?;
// Get one
let pb = client.get_playbook("cloudflare.dns.add-a-record").await?;
// Create
let resp = client.create_playbook(&CreatePlaybookRequest {
target: "cloudflare".into(),
title: "Add CNAME record".into(),
yaml: playbook_yaml,
authored_by: Some("human".into()),
}).await?;
// Update
client.update_playbook("cloudflare.dns.add-cname", &UpdatePlaybookRequest {
yaml: updated_yaml,
target: None,
title: None,
authored_by: None,
}).await?;
// Delete
client.delete_playbook("cloudflare.dns.add-cname").await?;
Runs
// Start a run
let resp = client.start_run(&StartRunRequest {
playbook_id: "cloudflare.dns.add-a-record".into(),
mode: Some("interactive".into()),
inputs: Some(serde_json::json!({
"zone": "example.com",
"record_name": "api",
"ip_address": "1.2.3.4"
})),
provider_profile_id: Some("cf-prod".into()),
..Default::default()
}).await?;
// Get run with steps
let run = client.get_run(&resp.id).await?;
if let Some(steps) = &run.steps {
for step in steps {
println!("{}: {} ({}ms)", step.step_id, step.status,
step.duration_ms.unwrap_or(0));
}
}
// List recent running runs
let running = client.list_runs(Some("running"), None, Some(10)).await?;
// Abort
client.abort_run(&resp.id).await?;
// Approve a step
client.approve_run(&resp.id, &ApproveRequest {
step_id: "save".into(),
decision: Some("approve".into()),
}).await?;
Types
Core structs
| Struct | Key fields |
|---|---|
Playbook | id, version, target, title, document_yaml, authored_by, created_at |
PlaybookSummary | id, version, target, title, authored_by |
Run | id, project_id, playbook_id, mode, status, inputs_json, outputs_json, steps: Option<Vec<RunStep>> |
RunStep | id, run_id, step_id, action, status, ai_used, duration_ms, error_message |
Provider | id, project_id, provider, label, base_url, last_authenticated, expires_at |
HealthStatus | status, database, driver_pool, provider_sessions, last_checked |
Request structs
| Struct | Fields |
|---|---|
StartRunRequest | playbook_id, version?, mode?, inputs? (serde_json::Value), provider_profile_id?, project_id? |
CreatePlaybookRequest | target, title, yaml, authored_by? |
UpdatePlaybookRequest | yaml, target?, title?, authored_by? |
ApproveRequest | step_id, decision? |
All structs derive Debug, Clone, Serialize, and Deserialize.
Error handling
The SDK uses a single PilotError enum with three variants:
use cuitty_pilot_sdk::PilotError;
match client.start_run(&req).await {
Ok(resp) => println!("Started: {}", resp.id),
Err(PilotError::Api { status, message }) => {
eprintln!("API error {}: {}", status, message);
}
Err(PilotError::Connection(err)) => {
eprintln!("Network error: {}", err);
}
Err(PilotError::Other(msg)) => {
eprintln!("Unexpected: {}", msg);
}
}
| Variant | Fields | When |
|---|---|---|
Api | status: u16, message: String | Non-2xx HTTP response |
Connection | wraps reqwest::Error | Network or timeout failure |
Other | String | Any other error |
See also
- JavaScript SDK — TypeScript client with event streaming
- Playbook reference — YAML schema for playbook documents
- Quickstart — record and replay your first workflow