| use bex_core::{Engine, EngineConfig}; |
| use bex_types::Manifest; |
| use clap::{Parser, Subcommand}; |
| use std::io::Read; |
| use std::path::PathBuf; |
|
|
| #[derive(Parser)] |
| #[command(name = "bex", about = "BEX Plugin Engine CLI")] |
| struct Cli { |
| #[arg(long, default_value = "./bex-data")] |
| data_dir: PathBuf, |
| #[command(subcommand)] |
| command: Commands, |
| } |
|
|
| #[derive(Subcommand)] |
| enum Commands { |
| Install { path: PathBuf }, |
| Uninstall { id: String }, |
| List, |
| Inspect { path: PathBuf }, |
| Pack { manifest: PathBuf, wasm: PathBuf, output: PathBuf }, |
| Home { plugin_id: String }, |
| Search { plugin_id: String, query: String }, |
| Info { plugin_id: String, id: String }, |
| |
| |
| Servers { plugin_id: String, id: String }, |
| Stream { plugin_id: String, server_json: String }, |
| Enable { id: String }, |
| Disable { id: String }, |
| |
| PluginInfo { id: String }, |
| |
| SetKey { plugin_id: String, key: String, value: String }, |
| |
| GetKey { plugin_id: String, key: String }, |
| |
| DeleteKey { plugin_id: String, key: String }, |
| |
| ListKeys { plugin_id: String }, |
| Stats, |
| } |
|
|
| fn main() -> anyhow::Result<()> { |
| |
| tracing_subscriber::fmt::init(); |
|
|
| let cli = Cli::parse(); |
| let config = EngineConfig { |
| data_dir: cli.data_dir.clone(), |
| ..Default::default() |
| }; |
| let engine = Engine::new(config)?; |
|
|
| match cli.command { |
| Commands::Install { path } => { |
| let info = engine.install_plugin(&path)?; |
| println!("Installed: {} ({}) v{}", info.name, info.id, info.version); |
| let caps = bex_types::Capabilities::from_bits(info.capabilities).unwrap_or(bex_types::Capabilities::empty()); |
| println!("Capabilities: {:?}", caps); |
| } |
| Commands::Uninstall { id } => { |
| engine.uninstall_plugin(&id)?; |
| println!("Uninstalled: {}", id); |
| } |
| Commands::List => { |
| let plugins = engine.list_plugins(); |
| if plugins.is_empty() { |
| println!("No plugins installed."); |
| return Ok(()); |
| } |
| println!("{:<40} {:<20} {:<10} {}", "ID", "NAME", "VERSION", "ENABLED"); |
| for p in plugins { |
| println!("{:<40} {:<20} {:<10} {}", p.id, p.name, p.version, p.enabled); |
| } |
| } |
| Commands::Inspect { path } => { |
| let data = std::fs::read(&path)?; |
| let manifest = bex_pkg::read_manifest(&data)?; |
| println!("ID: {}", manifest.id); |
| println!("Name: {}", manifest.name); |
| println!("Version: {}", manifest.version); |
| println!("ABI: {}", manifest.abi); |
| println!("Capabilities: {:?}", manifest.capabilities()); |
| } |
| Commands::Pack { manifest, wasm, output } => { |
| let yaml_str = std::fs::read_to_string(&manifest)?; |
| let m: Manifest = serde_yaml::from_str(&yaml_str)?; |
| let wasm_bytes = std::fs::read(&wasm)?; |
| let packed = bex_pkg::pack(&m, &wasm_bytes)?; |
| std::fs::write(&output, packed)?; |
| println!( |
| "Packed to {} ({} bytes)", |
| output.display(), |
| std::fs::metadata(&output)?.len() |
| ); |
| } |
| Commands::Home { plugin_id } => { |
| let result = engine.call_get_home_json(&plugin_id)?; |
| println_pretty(&result); |
| } |
| Commands::Search { plugin_id, query } => { |
| let result = engine.call_search_json(&plugin_id, &query)?; |
| println_pretty(&result); |
| } |
| Commands::Info { plugin_id, id } => { |
| let result = engine.call_get_info_json(&plugin_id, &id)?; |
| println_pretty(&result); |
| } |
| Commands::Servers { plugin_id, id } => { |
| |
| |
| let result = engine.call_get_servers_json(&plugin_id, &id)?; |
| println_pretty(&result); |
| } |
| Commands::Stream { plugin_id, server_json } => { |
| let server_json = if server_json == "-" { |
| let mut input = String::new(); |
| std::io::stdin().read_to_string(&mut input)?; |
| input |
| } else { |
| server_json |
| }; |
| let result = engine.call_resolve_stream_json(&plugin_id, &server_json)?; |
| println_pretty(&result); |
| } |
| Commands::Enable { id } => { |
| engine.enable_plugin(&id)?; |
| println!("Enabled: {}", id); |
| } |
| Commands::Disable { id } => { |
| engine.disable_plugin(&id)?; |
| println!("Disabled: {}", id); |
| } |
| Commands::PluginInfo { id } => { |
| match engine.get_plugin_info(&id) { |
| Some(info) => { |
| println!("ID: {}", info.id); |
| println!("Name: {}", info.name); |
| println!("Version: {}", info.version); |
| println!("Enabled: {}", info.enabled); |
| let caps = bex_types::Capabilities::from_bits(info.capabilities) |
| .unwrap_or(bex_types::Capabilities::empty()); |
| println!("Capabilities: {:?}", caps); |
| } |
| None => println!("Plugin not found: {}", id), |
| } |
| } |
| Commands::SetKey { plugin_id, key, value } => { |
| engine.secret_set(&plugin_id, &key, &value)?; |
| println!("Key '{}' set for plugin '{}'", key, plugin_id); |
| } |
| Commands::GetKey { plugin_id, key } => { |
| match engine.secret_get(&plugin_id, &key)? { |
| Some(val) => println!("{}", val), |
| None => println!("Key '{}' not found for plugin '{}'", key, plugin_id), |
| } |
| } |
| Commands::DeleteKey { plugin_id, key } => { |
| let existed = engine.secret_remove(&plugin_id, &key)?; |
| if existed { |
| println!("Key '{}' deleted from plugin '{}'", key, plugin_id); |
| } else { |
| println!("Key '{}' not found for plugin '{}'", key, plugin_id); |
| } |
| } |
| Commands::ListKeys { plugin_id } => { |
| let keys = engine.secret_keys(&plugin_id)?; |
| if keys.is_empty() { |
| println!("No keys found for plugin '{}'", plugin_id); |
| } else { |
| println!("Keys for plugin '{}':", plugin_id); |
| for k in keys { |
| println!(" {}", k); |
| } |
| } |
| } |
| Commands::Stats => { |
| let stats = engine.stats(); |
| println!("Uptime: {}ms", stats.uptime_ms); |
| println!("Total plugins: {}", stats.total_plugins); |
| println!("Enabled plugins: {}", stats.enabled_plugins); |
| } |
| } |
| Ok(()) |
| } |
|
|
| fn println_pretty(json: &str) { |
| match serde_json::from_str::<serde_json::Value>(json) { |
| Ok(val) => println!("{}", serde_json::to_string_pretty(&val).unwrap()), |
| Err(_) => println!("{}", json), |
| } |
| } |
|
|