From 0e730402bd1b7e2597f42826435dd1042dac860e Mon Sep 17 00:00:00 2001 From: "Hermes (debby)" Date: Fri, 19 Jun 2026 11:31:18 +0200 Subject: [PATCH] feat(cli): add register-agent and list-agents commands - lib.rs: register_agent() / list_agents() client methods - bin/colibri.rs: Command::RegisterAgent, Command::ListAgents - Capabilities passed via --capability or --capabilities CSV --- crates/colibri-client/src/bin/colibri.rs | 48 ++++++++++++++++++++++++ crates/colibri-client/src/lib.rs | 16 ++++++++ 2 files changed, 64 insertions(+) diff --git a/crates/colibri-client/src/bin/colibri.rs b/crates/colibri-client/src/bin/colibri.rs index 71b84bc..345404a 100644 --- a/crates/colibri-client/src/bin/colibri.rs +++ b/crates/colibri-client/src/bin/colibri.rs @@ -48,6 +48,11 @@ enum Command { description: Option, category: Option, }, + RegisterAgent { + name: String, + capabilities: Vec, + }, + ListAgents, } fn default_socket_path() -> PathBuf { @@ -81,6 +86,8 @@ Examples: colibri list-tasks --status queued colibri register-skill freebsd-check --description "Live USB startup check" --category freebsd colibri list-skills + colibri register-agent NAME [--capability CAP]... [--capabilities CSV] + colibri list-agents "# } @@ -166,6 +173,7 @@ where }) } "list-skills" => expect_arity(&args, 1).map(|()| Command::ListSkills), + "list-agents" => expect_arity(&args, 1).map(|()| Command::ListAgents), "register-skill" => { if args.len() < 2 { Err("register-skill requires NAME\n\n".to_string() + usage()) @@ -178,6 +186,17 @@ where }) } } + "register-agent" => { + if args.len() < 2 { + Err("register-agent requires NAME\n\n".to_string() + usage()) + } else { + let capabilities = parse_capabilities(&args[2..])?; + Ok(Command::RegisterAgent { + name: args[1].clone(), + capabilities, + }) + } + } other => Err(format!("unknown command: {other}\n\n{}", usage())), }?; @@ -328,6 +347,31 @@ fn parse_spawn_options(args: &[String]) -> Result<(Option, Option Result, String> { + let mut caps = Vec::new(); + let mut i = 0; + while i < args.len() { + match args[i].as_str() { + "--capability" => { + let Some(value) = args.get(i + 1) else { + return Err("--capability requires CAP\n\n".to_string() + usage()); + }; + caps.push(value.clone()); + i += 2; + } + "--capabilities" => { + let Some(value) = args.get(i + 1) else { + return Err("--capabilities requires CSV\n\n".to_string() + usage()); + }; + caps.extend(value.split(',').map(str::trim).filter(|c| !c.is_empty()).map(ToString::to_string)); + i += 2; + } + other => return Err(format!("unknown register-agent option: {other}\n\n{}", usage())), + } + } + Ok(caps) +} + fn parse_skill_options(args: &[String]) -> Result<(Option, Option), String> { let mut description = None; let mut category = None; @@ -395,6 +439,10 @@ async fn run(options: Options) -> Result<(), ClientError> { description, category, } => print_json(&client.register_skill(name, description, category).await?), + Command::RegisterAgent { name, capabilities } => { + print_json(&client.register_agent(name, capabilities).await?) + } + Command::ListAgents => print_json(&client.list_agents().await?), } } diff --git a/crates/colibri-client/src/lib.rs b/crates/colibri-client/src/lib.rs index 5c7d597..aca5176 100644 --- a/crates/colibri-client/src/lib.rs +++ b/crates/colibri-client/src/lib.rs @@ -181,6 +181,22 @@ impl DaemonClient { self.request(&ColibriCommand::ListSkills).await } + pub async fn register_agent( + &self, + name: impl Into, + capabilities: Vec, + ) -> Result { + self.request(&ColibriCommand::RegisterAgent { + name: name.into(), + capabilities: Some(serde_json::to_value(capabilities).unwrap_or_default()), + }) + .await + } + + pub async fn list_agents(&self) -> Result { + self.request(&ColibriCommand::ListAgents).await + } + pub async fn register_skill( &self, name: impl Into, -- 2.45.3