use std::sync::Arc;
use chrono::{Duration, Utc};
use bamboo_subagent::discovery::Fabric;
use bamboo_subagent::executor::{ChildExecutor, EchoExecutor};
use bamboo_subagent::proto::AgentRecord;
use bamboo_subagent::provision::{ExecutorSpec, ProvisionSpec};
use bamboo_subagent::transport::WsServer;
fn build_executor(spec: &ProvisionSpec) -> Result<Arc<dyn ChildExecutor>, String> {
match &spec.executor {
ExecutorSpec::Echo => Ok(Arc::new(EchoExecutor)),
other => Err(format!(
"demo worker only supports the echo executor, got {other:?}; \
use `bamboo subagent-worker` for real engines"
)),
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let spec = ProvisionSpec::read_from_stdin()
.await
.expect("read ProvisionSpec from stdin");
let executor = match build_executor(&spec) {
Ok(e) => e,
Err(msg) => {
eprintln!("provision error: {msg}");
std::process::exit(2);
}
};
let server = WsServer::bind_loopback()
.await
.expect("bind loopback ws server");
let endpoint = server.ws_endpoint();
let fab = Fabric::at(&spec.fabric_dir);
let now = Utc::now();
let record = AgentRecord {
agent_id: spec.identity.child_id.clone(),
role: spec.identity.role.clone(),
labels: Vec::new(),
endpoint,
pid: std::process::id(),
version: env!("CARGO_PKG_VERSION").to_string(),
started_at: now,
lease_expires_at: now + Duration::seconds(60),
};
fab.publish(&record)
.await
.expect("publish discovery record");
let renew = if spec.reusable {
let fab = Fabric::at(&spec.fabric_dir);
let mut renew_record = record.clone();
Some(tokio::spawn(async move {
let mut tick = tokio::time::interval(std::time::Duration::from_secs(20));
tick.tick().await;
loop {
tick.tick().await;
renew_record.lease_expires_at = Utc::now() + Duration::seconds(60);
if fab.publish(&renew_record).await.is_err() {
break;
}
}
}))
} else {
None
};
if spec.reusable {
let idle = std::time::Duration::from_secs(spec.limits.idle_timeout_secs.unwrap_or(300));
let _ = server
.serve_reusable_with_idle_timeout(executor, idle)
.await;
} else {
let _ = server
.serve_one_with_accept_timeout(executor, std::time::Duration::from_secs(120))
.await;
}
if let Some(renew) = renew {
renew.abort();
}
let _ = fab.withdraw(&spec.identity.child_id).await;
}