From a86a247180bd401d356e73d2d0608ca9dcf2a41e Mon Sep 17 00:00:00 2001 From: Sameh Abouelsaad Date: Thu, 8 May 2025 14:02:58 +0300 Subject: [PATCH] Simplified the CLI to directly accept a Rhai script path as its primary argument, remove all commands --- Cargo.toml | 2 +- src/cli/commands.rs | 772 ------------------------------------------- src/cli/config.rs | 6 +- src/cli/mod.rs | 244 +------------- src/cli/shell.rs | 284 ---------------- src/main.rs | 38 +-- src/scripting/api.rs | 6 +- 7 files changed, 18 insertions(+), 1334 deletions(-) delete mode 100644 src/cli/commands.rs delete mode 100644 src/cli/shell.rs diff --git a/Cargo.toml b/Cargo.toml index 71fd3ad..afca626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT" crate-type = ["cdylib", "rlib"] [[bin]] -name = "crypto-cli" +name = "hero-vault" path = "src/main.rs" [dependencies] diff --git a/src/cli/commands.rs b/src/cli/commands.rs deleted file mode 100644 index 70ae639..0000000 --- a/src/cli/commands.rs +++ /dev/null @@ -1,772 +0,0 @@ -use colored::Colorize; -use std::fs; -use std::io::{self, Read, Write}; -use std::path::Path; - -use crate::cli::error::{CliError, Result}; -use crate::cli::{CryptoCommands, EthereumCommands, KeyCommands}; -use webassembly::core::keypair; -use webassembly::core::symmetric; -use webassembly::core::ethereum; -use webassembly::core::error::CryptoError; - -// Load a key space from disk -fn load_key_space(name: &str, password: &str) -> Result<()> { - // Load config to get key spaces directory - let config = crate::cli::config::Config::default(); - - // Check if directory exists - if !config.key_spaces_dir.exists() { - return Err(CliError::ConfigError("Key spaces directory does not exist".to_string())); - } - - // Get the key space file path - let space_path = config.key_spaces_dir.join(format!("{}.json", name)); - - // Check if file exists - if !space_path.exists() { - return Err(CliError::ConfigError(format!("Key space file not found: {}", space_path.display()))); - } - - // Read the file - let serialized = fs::read_to_string(&space_path)?; - - // Deserialize the encrypted space - let encrypted_space = match symmetric::deserialize_encrypted_space(&serialized) { - Ok(space) => space, - Err(e) => { - println!("Error deserializing key space: {}", e); - return Err(CliError::CryptoError(format!("Failed to deserialize key space: {}", e))); - } - }; - - // Decrypt the space - let space = match symmetric::decrypt_key_space(&encrypted_space, password) { - Ok(space) => space, - Err(e) => { - println!("Error decrypting key space: {}", e); - return Err(CliError::CryptoError(format!("Failed to decrypt key space: {}", e))); - } - }; - - // Set as current space - keypair::set_current_space(space)?; - - Ok(()) -} - -// Execute key management commands -pub fn execute_key_command(command: &KeyCommands) -> Result<()> { - match command { - KeyCommands::Load { name, password } => { - println!("Loading key space: {}", name); - - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", name); - rpassword::read_password()? - } - }; - - // Load the key space - load_key_space(name, &pwd)?; - println!("{}", "Key space loaded successfully".green()); - Ok(()) - }, - KeyCommands::CreateSpace { name, password } => { - println!("Creating key space: {}", name); - - // Create the key space - keypair::create_space(name)?; - - // Get the current space - let space = keypair::get_current_space()?; - - // Encrypt the key space with the provided password - let encrypted_space = symmetric::encrypt_key_space(&space, &password)?; - - // Load config to get key spaces directory - let config = crate::cli::config::Config::default(); - config.ensure_key_spaces_dir()?; - - // Store the encrypted space to disk - let space_path = config.key_spaces_dir.join(format!("{}.json", name)); - let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?; - fs::write(&space_path, serialized)?; - - println!("Key space encrypted with password and saved to {}", space_path.display()); - println!("{}", "Key space created successfully".green()); - Ok(()) - }, - KeyCommands::ListSpaces => { - println!("Listing key spaces:"); - - // Load config to get key spaces directory - let config = crate::cli::config::Config::default(); - - // Check if directory exists - if !config.key_spaces_dir.exists() { - println!("No key spaces found (directory does not exist)"); - return Ok(()); - } - - // List all space files - let mut spaces_found = false; - for entry in fs::read_dir(&config.key_spaces_dir)? { - let entry = entry?; - let path = entry.path(); - - if path.is_file() && path.extension().map_or(false, |ext| ext == "json") { - if let Some(name) = path.file_stem() { - println!("- {}", name.to_string_lossy()); - spaces_found = true; - } - } - } - - if !spaces_found { - println!("No key spaces found in {}", config.key_spaces_dir.display()); - } - - // Show the current space if it exists - match keypair::get_current_space() { - Ok(space) => { - println!("\nCurrent active space: {}", space.name.green()); - }, - Err(_) => { - println!("\nNo active key space"); - } - } - - Ok(()) - }, - KeyCommands::CreateKeypair { name, space, password } => { - println!("Creating keypair: {}", name); - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - // If space is provided, try to load it - if let Some(space_name) = space { - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", space_name); - rpassword::read_password()? - } - }; - - // Load the key space - load_key_space(&space_name, &pwd)?; - println!("Loaded key space: {}", space_name); - } else { - // Try to load the default space - println!("No active key space. Enter the name of the key space to use:"); - let mut space_name = String::new(); - io::stdin().read_line(&mut space_name)?; - space_name = space_name.trim().to_string(); - - println!("Enter password for key space {}:", space_name); - let pwd = rpassword::read_password()?; - - // Load the key space - load_key_space(&space_name, &pwd)?; - println!("Loaded key space: {}", space_name); - } - } - - // Create the keypair in the current space - keypair::create_keypair(name)?; - - // Save the key space to disk - let space = keypair::get_current_space()?; - - // Get the space name - let space_name = space.name.clone(); - - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", space_name); - rpassword::read_password()? - } - }; - - // Encrypt the key space - let encrypted_space = symmetric::encrypt_key_space(&space, &pwd)?; - - // Load config to get key spaces directory - let config = crate::cli::config::Config::default(); - config.ensure_key_spaces_dir()?; - - // Store the encrypted space to disk - let space_path = config.key_spaces_dir.join(format!("{}.json", space_name)); - let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?; - fs::write(&space_path, serialized)?; - - println!("Key space saved to {}", space_path.display()); - - println!("{}", "Keypair created successfully".green()); - Ok(()) - }, - KeyCommands::ListKeypairs { space, password } => { - println!("Listing keypairs:"); - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - // If space is provided, try to load it - if let Some(space_name) = space { - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", space_name); - rpassword::read_password()? - } - }; - - // Load the key space - match load_key_space(&space_name, &pwd) { - Ok(_) => println!("Loaded key space: {}", space_name), - Err(e) => { - println!("Error loading key space: {}", e); - return Err(e); - } - } - } else { - // Try to load the default space - println!("No active key space. Enter the name of the key space to use:"); - let mut space_name = String::new(); - io::stdin().read_line(&mut space_name)?; - space_name = space_name.trim().to_string(); - - println!("Enter password for key space {}:", space_name); - let pwd = rpassword::read_password()?; - - // Load the key space - match load_key_space(&space_name, &pwd) { - Ok(_) => println!("Loaded key space: {}", space_name), - Err(e) => { - println!("Error loading key space: {}", e); - return Err(e); - } - } - } - } - - // Get the current space - let space = match keypair::get_current_space() { - Ok(s) => s, - Err(e) => { - println!("Error getting current key space: {}", e); - return Err(CliError::CryptoError(format!("Failed to get current key space: {}", e))); - } - }; - - // List keypairs directly from the space - let keypairs = space.list_keypairs(); - if keypairs.is_empty() { - println!("No keypairs found"); - } else { - for (i, name) in keypairs.iter().enumerate() { - println!("{}. {}", i + 1, name); - } - } - - Ok(()) - }, - KeyCommands::Export { name, output, space, password } => { - println!("Exporting keypair: {}", name); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Select the keypair - keypair::select_keypair(name)?; - - // Get the keypair - let kp = keypair::get_selected_keypair()?; - - // Serialize the keypair - let serialized = serde_json::to_string_pretty(&kp) - .map_err(|e| CliError::CryptoError(format!("Failed to serialize keypair: {}", e)))?; - - // Output the serialized keypair - match output { - Some(path) => { - fs::write(path, &serialized)?; - println!("{}", format!("Keypair exported to {}", path).green()); - }, - None => { - println!("{}", serialized); - } - } - - Ok(()) - }, - KeyCommands::Import { name, input, space: space_name, password } => { - println!("Importing keypair: {}", name); - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - // If space is provided, try to load it - if let Some(space_name) = space_name { - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", space_name); - rpassword::read_password()? - } - }; - - // Load the key space - load_key_space(&space_name, &pwd)?; - println!("Loaded key space: {}", space_name); - } else { - // Try to load the default space - println!("No active key space. Enter the name of the key space to use:"); - let mut space_name = String::new(); - io::stdin().read_line(&mut space_name)?; - space_name = space_name.trim().to_string(); - - println!("Enter password for key space {}:", space_name); - let pwd = rpassword::read_password()?; - - // Load the key space - load_key_space(&space_name, &pwd)?; - println!("Loaded key space: {}", space_name); - } - } - - // Get input data - let import_data = match input { - Some(path) => fs::read_to_string(path)?, - None => { - println!("Enter keypair data (end with Ctrl+D on Unix or Ctrl+Z on Windows):"); - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - buffer - } - }; - - // Deserialize the keypair - let kp: keypair::KeyPair = serde_json::from_str(&import_data) - .map_err(|e| CliError::CryptoError(format!("Failed to deserialize keypair: {}", e)))?; - - // Get the current space - let mut space = keypair::get_current_space()?; - - // Add the keypair to the space - space.keypairs.insert(name.to_string(), kp); - - // Update the space - keypair::set_current_space(space)?; - - // Auto-save the key space - let space = keypair::get_current_space()?; - let space_name = space.name.clone(); - - // Get password - let pwd = match password { - Some(p) => p.clone(), - None => { - println!("Enter password for key space {}:", space_name); - rpassword::read_password()? - } - }; - - // Encrypt the key space - let encrypted_space = symmetric::encrypt_key_space(&space, &pwd)?; - - // Load config to get key spaces directory - let config = crate::cli::config::Config::default(); - config.ensure_key_spaces_dir()?; - - // Store the encrypted space to disk - let space_path = config.key_spaces_dir.join(format!("{}.json", space_name)); - let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?; - fs::write(&space_path, serialized)?; - - println!("Key space saved to {}", space_path.display()); - println!("{}", "Keypair imported successfully".green()); - Ok(()) - }, - } -} - -// Execute cryptographic operation commands -pub fn execute_crypto_command(command: &CryptoCommands) -> Result<()> { - match command { - CryptoCommands::Sign { message, input, keypair, output, space, password } => { - println!("Signing with keypair: {}", keypair); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Select the keypair - keypair::select_keypair(keypair)?; - - // Get message to sign - let msg = match (message, input) { - (Some(m), _) => m.clone(), - (_, Some(path)) => fs::read_to_string(path)?, - _ => { - println!("Enter message to sign (end with Ctrl+D on Unix or Ctrl+Z on Windows):"); - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - buffer - } - }; - - // Sign the message - let signature_bytes = keypair::keypair_sign(msg.as_bytes())?; - - // Encode the signature as base64 - let signature = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &signature_bytes); - - // Output signature - match output { - Some(path) => { - fs::write(path, &signature)?; - println!("{}", format!("Signature written to {}", path).green()); - }, - None => { - println!("Signature: {}", signature); - } - } - - Ok(()) - }, - CryptoCommands::Verify { message, input, signature, keypair, pubkey, space, password } => { - println!("Verifying signature"); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space and a keypair is specified - if let Some(kp) = keypair { - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - } - - // Get message to verify - let msg = match (message, input) { - (Some(m), _) => m.clone(), - (_, Some(path)) => fs::read_to_string(path)?, - _ => { - println!("Enter message to verify (end with Ctrl+D on Unix or Ctrl+Z on Windows):"); - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - buffer - } - }; - - // Decode the signature from base64 - let signature_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, signature) { - Ok(bytes) => bytes, - Err(e) => { - return Err(CliError::CryptoError(format!("Invalid signature format: {}", e))); - } - }; - - // Verify the signature - let is_valid = if let Some(kp) = keypair { - // Select the keypair and verify - keypair::select_keypair(kp)?; - keypair::keypair_verify(msg.as_bytes(), &signature_bytes)? - } else if let Some(pk) = pubkey { - // Decode the public key from base64 - let pubkey_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, pk) { - Ok(bytes) => bytes, - Err(e) => { - return Err(CliError::CryptoError(format!("Invalid public key format: {}", e))); - } - }; - - // Verify with the public key - keypair::verify_with_public_key(&pubkey_bytes, msg.as_bytes(), &signature_bytes)? - } else { - // Use the currently selected keypair - keypair::keypair_verify(msg.as_bytes(), &signature_bytes)? - }; - - if is_valid { - println!("{}", "Signature is valid".green()); - } else { - println!("{}", "Signature is invalid".red()); - } - - Ok(()) - }, - CryptoCommands::Encrypt { data, input, recipient, output, space, password } => { - println!("Encrypting for recipient: {}", recipient); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Get data to encrypt - let plaintext = match (data, input) { - (Some(d), _) => d.clone(), - (_, Some(path)) => fs::read_to_string(path)?, - _ => { - println!("Enter data to encrypt (end with Ctrl+D on Unix or Ctrl+Z on Windows):"); - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - buffer - } - }; - - // Get the recipient's public key - // For now, we'll assume the recipient is a keypair name - keypair::select_keypair(&recipient)?; - let recipient_pubkey = keypair::keypair_pub_key()?; - - // Encrypt the data - let ciphertext_bytes = keypair::encrypt_asymmetric(&recipient_pubkey, plaintext.as_bytes())?; - - // Encode the ciphertext as base64 - let ciphertext = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &ciphertext_bytes); - - // Output ciphertext - match output { - Some(path) => { - fs::write(path, &ciphertext)?; - println!("{}", format!("Encrypted data written to {}", path).green()); - }, - None => { - println!("Encrypted data: {}", ciphertext); - } - } - - Ok(()) - }, - CryptoCommands::Decrypt { data, input, keypair, output, space, password } => { - println!("Decrypting with keypair: {}", keypair); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Select the keypair - keypair::select_keypair(keypair)?; - - // Get data to decrypt - let ciphertext = match (data, input) { - (Some(d), _) => d.clone(), - (_, Some(path)) => fs::read_to_string(path)?, - _ => { - println!("Enter data to decrypt (end with Ctrl+D on Unix or Ctrl+Z on Windows):"); - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - buffer - } - }; - - // Decode the ciphertext from base64 - let ciphertext_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &ciphertext) { - Ok(bytes) => bytes, - Err(e) => { - return Err(CliError::CryptoError(format!("Invalid ciphertext format: {}", e))); - } - }; - - // Decrypt the data - let plaintext_bytes = keypair::decrypt_asymmetric(&ciphertext_bytes)?; - - // Convert the plaintext to a string - let plaintext = match String::from_utf8(plaintext_bytes) { - Ok(text) => text, - Err(e) => { - return Err(CliError::CryptoError(format!("Invalid UTF-8 in decrypted data: {}", e))); - } - }; - - // Output plaintext - match output { - Some(path) => { - fs::write(path, &plaintext)?; - println!("{}", format!("Decrypted data written to {}", path).green()); - }, - None => { - println!("Decrypted data: {}", plaintext); - } - } - - Ok(()) - }, - } -} - -// Execute Ethereum wallet commands -pub fn execute_ethereum_command(command: &EthereumCommands) -> Result<()> { - match command { - EthereumCommands::Create { keypair, space, password } => { - println!("Creating Ethereum wallet from keypair: {}", keypair); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Select the keypair - keypair::select_keypair(keypair)?; - - // Create the Ethereum wallet - let wallet = ethereum::create_ethereum_wallet()?; - - println!("{}", "Ethereum wallet created successfully".green()); - println!("Address: {}", wallet.address_string()); - Ok(()) - }, - EthereumCommands::Address { keypair, space, password } => { - println!("Getting Ethereum address for keypair: {}", keypair); - - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - // Select the keypair - keypair::select_keypair(keypair)?; - - // Get the Ethereum address - match ethereum::get_current_ethereum_wallet() { - Ok(wallet) => { - println!("Ethereum address: {}", wallet.address_string()); - Ok(()) - }, - Err(e) => { - // If no wallet exists, create one - if let CryptoError::NoKeypairSelected = e { - println!("No Ethereum wallet found for this keypair. Creating one..."); - let wallet = ethereum::create_ethereum_wallet()?; - println!("Ethereum address: {}", wallet.address_string()); - Ok(()) - } else { - Err(e.into()) - } - } - } - }, - EthereumCommands::Balance { address, network, space, password } => { - // If space and password are provided, load the key space - if let (Some(s), Some(p)) = (space, password) { - load_key_space(s, p)?; - println!("Loaded key space: {}", s); - } - - // Check if we have an active space - if let Err(_) = keypair::get_current_space() { - return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string())); - } - - let addr = match address { - Some(a) => a.clone(), - None => { - // Try to get the address from the current wallet - match ethereum::get_current_ethereum_wallet() { - Ok(wallet) => wallet.address_string(), - Err(_) => { - println!("Enter Ethereum address:"); - let mut buffer = String::new(); - io::stdin().read_line(&mut buffer)?; - buffer.trim().to_string() - } - } - } - }; - - println!("Getting balance for address {} on network {}", addr, network); - - // Create provider based on network - let provider = match network.to_lowercase().as_str() { - "gnosis" => ethereum::create_gnosis_provider()?, - _ => { - return Err(CliError::CryptoError(format!("Unsupported network: {}", network))); - } - }; - - // Parse the address - let eth_address = addr.parse::() - .map_err(|_| CliError::CryptoError("Invalid Ethereum address".to_string()))?; - - // Get the balance - let rt = tokio::runtime::Runtime::new() - .map_err(|e| CliError::IoError(format!("Failed to create runtime: {}", e)))?; - - let balance = rt.block_on(ethereum::get_balance(&provider, eth_address))?; - - // Format the balance - let formatted_balance = ethereum::format_eth_balance(balance); - - println!("Balance: {}", formatted_balance); - Ok(()) - }, - } -} - -// Helper function to read file contents -fn read_file>(path: P) -> io::Result> { - let mut file = fs::File::open(path)?; - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer)?; - Ok(buffer) -} - -// Helper function to write file contents -fn write_file>(path: P, data: &[u8]) -> io::Result<()> { - let mut file = fs::File::create(path)?; - file.write_all(data)?; - Ok(()) -} diff --git a/src/cli/config.rs b/src/cli/config.rs index e7bacef..9222e5c 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -14,7 +14,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces"); + let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces"); Config { default_key_space: None, @@ -30,7 +30,7 @@ impl Config { Some(p) => PathBuf::from(p.as_ref()), None => { let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - home_dir.join(".crypto-cli").join("config.json") + home_dir.join(".hero-vault").join("config.json") } }; @@ -50,7 +50,7 @@ impl Config { Some(p) => PathBuf::from(p.as_ref()), None => { let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - let config_dir = home_dir.join(".crypto-cli"); + let config_dir = home_dir.join(".hero-vault"); if !config_dir.exists() { fs::create_dir_all(&config_dir) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f8bbc22..095fbb8 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,16 +1,14 @@ -pub mod commands; pub mod config; pub mod error; -pub mod shell; -use clap::{Parser, Subcommand}; +use clap::Parser; #[derive(Parser)] -#[command(name = "crypto-cli")] +#[command(name = "hero-vault")] #[command(about = "Cryptographic operations CLI with Rhai scripting support", long_about = None)] pub struct Cli { - #[command(subcommand)] - pub command: Commands, + /// Path to Rhai script file to execute + pub script_path: String, #[arg(short, long, help = "Enable verbose output")] pub verbose: bool, @@ -18,237 +16,3 @@ pub struct Cli { #[arg(short, long, help = "Config file path")] pub config: Option, } - -#[derive(Subcommand)] -pub enum Commands { - /// Key management commands - Key { - #[command(subcommand)] - command: KeyCommands, - }, - - /// Encryption/decryption commands - Crypto { - #[command(subcommand)] - command: CryptoCommands, - }, - - /// Ethereum wallet commands - Ethereum { - #[command(subcommand)] - command: EthereumCommands, - }, - - /// Execute Rhai script - Script { - #[arg(help = "Path to Rhai script file")] - path: Option, - - #[arg(short, long, help = "Execute script from string")] - inline: Option, - }, - - /// Interactive shell - Shell, -} - -// Define subcommands for each category -#[derive(Subcommand)] -pub enum KeyCommands { - /// Create a new key space - CreateSpace { - /// Name of the key space - name: String, - #[arg(long)] - /// Password to encrypt the key space (required) - password: String - }, - /// List available key spaces - ListSpaces, - /// Load a key space - Load { - /// Name of the key space to load - name: String, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Create a new keypair in the current key space - CreateKeypair { - /// Name of the keypair - name: String, - #[arg(long)] - space: Option, - #[arg(long)] - password: Option - }, - /// List keypairs in the current key space - ListKeypairs { - #[arg(long)] - /// Key space to list keypairs from - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Export a keypair - Export { - /// Name of the keypair to export - name: String, - /// Output file path (prints to stdout if not specified) - output: Option, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Import a keypair - Import { - /// Name to give the imported keypair - name: String, - /// Input file path (reads from stdin if not specified) - input: Option, - #[arg(long)] - /// Key space to import the keypair into - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, -} - -#[derive(Subcommand)] -pub enum CryptoCommands { - /// Sign a message with a keypair - Sign { - #[arg(long)] - /// Message to sign (as a string) - message: Option, - #[arg(long)] - /// Input file containing the message to sign - input: Option, - #[arg(long)] - /// Name of the keypair to use for signing - keypair: String, - #[arg(long)] - /// Output file for the signature (prints to stdout if not specified) - output: Option, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Verify a signature - Verify { - #[arg(long)] - /// Message to verify (as a string) - message: Option, - #[arg(long)] - /// Input file containing the message to verify - input: Option, - #[arg(long)] - /// Signature to verify (base64 encoded) - signature: String, - #[arg(long)] - /// Name of the keypair to use for verification - keypair: Option, - #[arg(long)] - /// Public key to use for verification (base64 encoded) - pubkey: Option, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Encrypt data for a recipient - Encrypt { - #[arg(long)] - /// Data to encrypt (as a string) - data: Option, - #[arg(long)] - /// Input file containing the data to encrypt - input: Option, - #[arg(long)] - /// Name of the recipient keypair - recipient: String, - #[arg(long)] - /// Output file for the encrypted data (prints to stdout if not specified) - output: Option, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Decrypt data with a keypair - Decrypt { - #[arg(long)] - /// Data to decrypt (as a string, base64 encoded) - data: Option, - #[arg(long)] - /// Input file containing the data to decrypt - input: Option, - #[arg(long)] - /// Name of the keypair to use for decryption - keypair: String, - #[arg(long)] - /// Output file for the decrypted data (prints to stdout if not specified) - output: Option, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, -} - -#[derive(Subcommand)] -pub enum EthereumCommands { - /// Create an Ethereum wallet from a keypair - Create { - #[arg(long)] - /// Name of the keypair to use - keypair: String, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Get the Ethereum address for a keypair - Address { - #[arg(long)] - /// Name of the keypair - keypair: String, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, - /// Get the balance of an Ethereum address - Balance { - #[arg(long)] - /// Ethereum address (uses the current wallet if not specified) - address: Option, - #[arg(long)] - /// Network to use (e.g., "gnosis") - network: String, - #[arg(long)] - /// Key space containing the keypair - space: Option, - #[arg(long)] - /// Password to decrypt the key space - password: Option - }, -} diff --git a/src/cli/shell.rs b/src/cli/shell.rs deleted file mode 100644 index dca7fd2..0000000 --- a/src/cli/shell.rs +++ /dev/null @@ -1,284 +0,0 @@ -use colored::Colorize; -use rustyline::error::ReadlineError; -use rustyline::DefaultEditor; -use std::process; - -use crate::cli::commands::{execute_crypto_command, execute_ethereum_command, execute_key_command}; -use crate::cli::error::{CliError, Result}; -use crate::cli::{CryptoCommands, EthereumCommands, KeyCommands}; - -pub fn run_interactive_shell() -> Result<()> { - println!("{}", "Crypto CLI Interactive Shell".green().bold()); - println!("Type 'help' for a list of commands, 'exit' to quit"); - - let mut rl = DefaultEditor::new().map_err(|e| CliError::IoError(e.to_string()))?; - - if rl.load_history("history.txt").is_err() { - println!("No previous history."); - } - - loop { - let readline = rl.readline("crypto> "); - match readline { - Ok(line) => { - rl.add_history_entry(line.as_str()); - - let line = line.trim(); - if line.is_empty() { - continue; - } - - match line { - "exit" | "quit" => { - println!("Goodbye!"); - break; - }, - "help" => { - print_help(); - }, - _ => { - if let Err(e) = process_command(line) { - println!("{}: {}", "Error".red().bold(), e); - } - } - } - }, - Err(ReadlineError::Interrupted) => { - println!("CTRL-C"); - break; - }, - Err(ReadlineError::Eof) => { - println!("CTRL-D"); - break; - }, - Err(err) => { - println!("Error: {:?}", err); - break; - } - } - } - - rl.save_history("history.txt").map_err(|e| CliError::IoError(e.to_string()))?; - - Ok(()) -} - -fn print_help() { - println!("{}", "Available Commands:".green().bold()); - println!(" {}", "Key Management:".yellow()); - println!(" key create-space [password]"); - println!(" key list-spaces"); - println!(" key load [password]"); - println!(" key create-keypair "); - println!(" key list-keypairs"); - println!(" key export [output-file]"); - println!(" key import [input-file]"); - - println!(" {}", "Cryptographic Operations:".yellow()); - println!(" crypto sign [output-file]"); - println!(" crypto verify [keypair]"); - println!(" crypto encrypt [output-file]"); - println!(" crypto decrypt [output-file]"); - - println!(" {}", "Ethereum Operations:".yellow()); - println!(" eth create "); - println!(" eth address "); - println!(" eth balance
"); - - println!(" {}", "General:".yellow()); - println!(" help - Show this help message"); - println!(" exit - Exit the shell"); -} - -fn process_command(cmd: &str) -> Result<()> { - let parts: Vec<&str> = cmd.split_whitespace().collect(); - - if parts.is_empty() { - return Ok(()); - } - - match parts[0] { - "key" => { - if parts.len() < 2 { - println!("Missing key subcommand. Try 'help' for a list of commands."); - return Ok(()); - } - - match parts[1] { - "create-space" => { - if parts.len() < 3 { - println!("Missing space name. Usage: key create-space [password]"); - return Ok(()); - } - - let name = parts[2].to_string(); - let password = if parts.len() > 3 { parts[3].to_string() } else { String::new() }; - - execute_key_command(&KeyCommands::CreateSpace { name, password }) - }, - "list-spaces" => { - execute_key_command(&KeyCommands::ListSpaces) - }, - "load" => { - if parts.len() < 3 { - println!("Missing space name. Usage: key load [password]"); - return Ok(()); - } - - let name = parts[2].to_string(); - let password = if parts.len() > 3 { Some(parts[3].to_string()) } else { None }; - - execute_key_command(&KeyCommands::Load { name, password }) - }, - "create-keypair" => { - if parts.len() < 3 { - println!("Missing keypair name. Usage: key create-keypair "); - return Ok(()); - } - - let name = parts[2].to_string(); - - execute_key_command(&KeyCommands::CreateKeypair { name, space: None, password: None }) - }, - "list-keypairs" => { - execute_key_command(&KeyCommands::ListKeypairs { space: None, password: None }) - }, - "export" => { - if parts.len() < 3 { - println!("Missing keypair name. Usage: key export [output-file]"); - return Ok(()); - } - - let name = parts[2].to_string(); - let output = if parts.len() > 3 { Some(parts[3].to_string()) } else { None }; - - execute_key_command(&KeyCommands::Export { name, output, space: None, password: None }) - }, - "import" => { - if parts.len() < 3 { - println!("Missing keypair name. Usage: key import [input-file]"); - return Ok(()); - } - - let name = parts[2].to_string(); - let input = if parts.len() > 3 { Some(parts[3].to_string()) } else { None }; - - execute_key_command(&KeyCommands::Import { name, input, space: None, password: None }) - }, - _ => { - println!("Unknown key subcommand: {}. Try 'help' for a list of commands.", parts[1]); - Ok(()) - } - } - }, - "crypto" => { - if parts.len() < 2 { - println!("Missing crypto subcommand. Try 'help' for a list of commands."); - return Ok(()); - } - - match parts[1] { - "sign" => { - if parts.len() < 4 { - println!("Missing arguments. Usage: crypto sign [output-file]"); - return Ok(()); - } - - let keypair = parts[2].to_string(); - let message = Some(parts[3].to_string()); - let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None }; - - execute_crypto_command(&CryptoCommands::Sign { message, input: None, keypair, output, space: None, password: None }) - }, - "verify" => { - if parts.len() < 4 { - println!("Missing arguments. Usage: crypto verify [keypair]"); - return Ok(()); - } - - let signature = parts[2].to_string(); - let message = Some(parts[3].to_string()); - let keypair = if parts.len() > 4 { Some(parts[4].to_string()) } else { None }; - - execute_crypto_command(&CryptoCommands::Verify { message, input: None, signature, keypair, pubkey: None, space: None, password: None }) - }, - "encrypt" => { - if parts.len() < 4 { - println!("Missing arguments. Usage: crypto encrypt [output-file]"); - return Ok(()); - } - - let recipient = parts[2].to_string(); - let data = Some(parts[3].to_string()); - let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None }; - - execute_crypto_command(&CryptoCommands::Encrypt { data, input: None, recipient, output, space: None, password: None }) - }, - "decrypt" => { - if parts.len() < 4 { - println!("Missing arguments. Usage: crypto decrypt [output-file]"); - return Ok(()); - } - - let keypair = parts[2].to_string(); - let data = Some(parts[3].to_string()); - let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None }; - - execute_crypto_command(&CryptoCommands::Decrypt { data, input: None, keypair, output, space: None, password: None }) - }, - _ => { - println!("Unknown crypto subcommand: {}. Try 'help' for a list of commands.", parts[1]); - Ok(()) - } - } - }, - "eth" => { - if parts.len() < 2 { - println!("Missing eth subcommand. Try 'help' for a list of commands."); - return Ok(()); - } - - match parts[1] { - "create" => { - if parts.len() < 3 { - println!("Missing keypair name. Usage: eth create "); - return Ok(()); - } - - let keypair = parts[2].to_string(); - - execute_ethereum_command(&EthereumCommands::Create { keypair, space: None, password: None }) - }, - "address" => { - if parts.len() < 3 { - println!("Missing keypair name. Usage: eth address "); - return Ok(()); - } - - let keypair = parts[2].to_string(); - - execute_ethereum_command(&EthereumCommands::Address { keypair, space: None, password: None }) - }, - "balance" => { - if parts.len() < 4 { - println!("Missing arguments. Usage: eth balance
"); - return Ok(()); - } - - let address = Some(parts[2].to_string()); - let network = parts[3].to_string(); - - execute_ethereum_command(&EthereumCommands::Balance { address, network, space: None, password: None }) - }, - _ => { - println!("Unknown eth subcommand: {}. Try 'help' for a list of commands.", parts[1]); - Ok(()) - } - } - }, - _ => { - println!("Unknown command: {}. Try 'help' for a list of commands.", parts[0]); - Ok(()) - } - } -} diff --git a/src/main.rs b/src/main.rs index 3768b44..0edaba9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use clap::Parser; -use colored::Colorize; use env_logger::Builder; use log::{info, LevelFilter}; @@ -9,7 +8,7 @@ extern crate webassembly; mod cli; mod scripting; -use cli::{Cli, Commands}; +use cli::Cli; fn main() -> Result<(), Box> { // Parse command line arguments @@ -24,35 +23,12 @@ fn main() -> Result<(), Box> { } builder.init(); - // Execute the appropriate command - match &cli.command { - Commands::Key { command } => { - cli::commands::execute_key_command(command)?; - }, - Commands::Crypto { command } => { - cli::commands::execute_crypto_command(command)?; - }, - Commands::Ethereum { command } => { - cli::commands::execute_ethereum_command(command)?; - }, - Commands::Script { path, inline } => { - let mut engine = scripting::ScriptEngine::new(); - - if let Some(script_path) = path { - info!("Executing script from file: {}", script_path); - engine.eval_file(script_path)?; - } else if let Some(script) = inline { - info!("Executing inline script"); - engine.eval(script)?; - } else { - println!("Error: No script provided"); - return Ok(()); - } - }, - Commands::Shell => { - cli::shell::run_interactive_shell()?; - }, - } + // Initialize script engine + let mut engine = scripting::ScriptEngine::new(); + + // Execute the script + info!("Executing script from file: {}", cli.script_path); + engine.eval_file(&cli.script_path)?; Ok(()) } diff --git a/src/scripting/api.rs b/src/scripting/api.rs index d5190d3..d59e4ab 100644 --- a/src/scripting/api.rs +++ b/src/scripting/api.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; fn load_key_space(name: &str, password: &str) -> bool { // Get the key spaces directory from config let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces"); + let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces"); // Check if directory exists if !key_spaces_dir.exists() { @@ -93,7 +93,7 @@ fn create_key_space(name: &str, password: &str) -> bool { // Get the key spaces directory let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces"); + let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces"); // Create directory if it doesn't exist if !key_spaces_dir.exists() { @@ -156,7 +156,7 @@ fn auto_save_key_space(password: &str) -> bool { // Get the key spaces directory let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces"); + let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces"); // Create directory if it doesn't exist if !key_spaces_dir.exists() {