diff --git a/src/core/all.rs b/src/core/all.rs index c41a5f9ec..17870da07 100644 --- a/src/core/all.rs +++ b/src/core/all.rs @@ -20,6 +20,16 @@ pub type ControllerFuture = Pin> + /// Handlers take a map of parameters and return a [`ControllerFuture`]. pub type ControllerHandler = fn(Map) -> ControllerFuture; +/// A function pointer type for domain-specific CLI handlers. +pub type CliHandler = fn(&[String]) -> anyhow::Result<()>; + +/// A registered standalone CLI adapter for a domain. +#[derive(Clone)] +pub struct RegisteredCliAdapter { + pub namespace: &'static str, + pub handler: CliHandler, +} + /// A registered controller combining its schema and handler function. #[derive(Clone)] pub struct RegisteredController { @@ -39,6 +49,9 @@ impl RegisteredController { /// The global static registry of all controllers, initialized once on first access. static REGISTRY: OnceLock> = OnceLock::new(); +/// The global static registry of standalone CLI adapters. +static CLI_ADAPTERS: OnceLock> = OnceLock::new(); + /// Returns a reference to the global controller registry. /// /// This function initializes the registry if it hasn't been already, @@ -56,6 +69,16 @@ fn registry() -> &'static [RegisteredController] { .as_slice() } +/// Returns a reference to the global CLI adapter registry. +fn cli_adapters() -> &'static [RegisteredCliAdapter] { + CLI_ADAPTERS.get_or_init(|| { + vec![RegisteredCliAdapter { + namespace: "voice", + handler: crate::openhuman::voice::cli::run_standalone_subcommand, + }] + }) +} + /// Aggregates all controller implementations from across the codebase. /// /// This function is responsible for collecting every domain-specific controller @@ -306,6 +329,14 @@ pub fn namespace_description(namespace: &str) -> Option<&'static str> { } } +/// Returns the CLI handler for a given namespace, if one is registered. +pub fn cli_handler_for_namespace(namespace: &str) -> Option { + cli_adapters() + .iter() + .find(|a| a.namespace == namespace) + .map(|a| a.handler) +} + /// Looks up an RPC method name based on namespace and function. pub fn rpc_method_from_parts(namespace: &str, function: &str) -> Option { registry() diff --git a/src/core/cli.rs b/src/core/cli.rs index 5e2c16220..6f602191b 100644 --- a/src/core/cli.rs +++ b/src/core/cli.rs @@ -63,7 +63,6 @@ pub fn run_from_cli_args(args: &[String]) -> Result<()> { "screen-intelligence" => { crate::openhuman::screen_intelligence::cli::run_screen_intelligence_command(&args[1..]) } - "voice" | "dictate" => crate::openhuman::voice::cli::run_standalone_subcommand(&args[1..]), "text-input" => crate::openhuman::text_input::cli::run_text_input_command(&args[1..]), "tree-summarizer" => { crate::openhuman::tree_summarizer::cli::run_tree_summarizer_command(&args[1..]) @@ -361,6 +360,10 @@ fn run_namespace_command( } if args.is_empty() || is_help(&args[0]) { + // If there's a domain-specific CLI handler for this namespace, use it as the default. + if let Some(cli_handler) = all::cli_handler_for_namespace(namespace) { + return cli_handler(args); + } print_namespace_help(namespace, schemas); return Ok(()); }