236 lines
7.3 KiB
Rust
236 lines
7.3 KiB
Rust
use clap::Parser;
|
|
use hero_supervisor::{Supervisor, SupervisorBuilder, ScriptType};
|
|
use log::{error, info};
|
|
use colored::Colorize;
|
|
use std::io::{self, Write};
|
|
use std::time::Duration;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(author, version, about = "Rhai Client - Script execution client", long_about = None)]
|
|
struct Args {
|
|
/// Caller ID (your identity)
|
|
#[arg(short = 'c', long = "caller-id", help = "Caller ID (your identity)")]
|
|
caller_id: String,
|
|
|
|
/// Context ID (execution context)
|
|
#[arg(short = 'k', long = "context-id", help = "Context ID (execution context)")]
|
|
context_id: String,
|
|
|
|
/// Script type to execute (osis, sal, v, python)
|
|
#[arg(short = 'T', long = "script-type", default_value = "osis", help = "Script type: osis, sal, v, or python")]
|
|
script_type: String,
|
|
|
|
/// Redis URL
|
|
#[arg(short, long, default_value = "redis://localhost:6379", help = "Redis connection URL")]
|
|
redis_url: String,
|
|
|
|
/// Rhai script to execute
|
|
#[arg(short, long, help = "Rhai script to execute")]
|
|
script: Option<String>,
|
|
|
|
/// Path to Rhai script file
|
|
#[arg(short, long, help = "Path to Rhai script file")]
|
|
file: Option<String>,
|
|
|
|
/// Timeout for script execution (in seconds)
|
|
#[arg(short, long, default_value = "30", help = "Timeout for script execution in seconds")]
|
|
timeout: u64,
|
|
|
|
/// Increase verbosity (can be used multiple times)
|
|
#[arg(short, long, action = clap::ArgAction::Count, help = "Increase verbosity (-v for debug, -vv for trace)")]
|
|
verbose: u8,
|
|
|
|
/// Disable timestamps in log output
|
|
#[arg(long, help = "Remove timestamps from log output")]
|
|
no_timestamp: bool,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let args = Args::parse();
|
|
|
|
// Configure logging based on verbosity level
|
|
let log_config = match args.verbose {
|
|
0 => "warn,hero_supervisor=warn",
|
|
1 => "info,hero_supervisor=info",
|
|
2 => "debug,hero_supervisor=debug",
|
|
_ => "trace,hero_supervisor=trace",
|
|
};
|
|
|
|
std::env::set_var("RUST_LOG", log_config);
|
|
|
|
// Configure env_logger with or without timestamps
|
|
if args.no_timestamp {
|
|
env_logger::Builder::from_default_env()
|
|
.format_timestamp(None)
|
|
.init();
|
|
} else {
|
|
env_logger::init();
|
|
}
|
|
|
|
|
|
|
|
// Validate script type
|
|
match args.script_type.to_lowercase().as_str() {
|
|
"osis" | "sal" | "v" | "python" => {
|
|
// Valid script types - no worker validation needed since we use hardcoded queues
|
|
}
|
|
_ => {
|
|
error!("❌ Invalid script type: {}. Valid types: osis, sal, v, python", args.script_type);
|
|
return Err(format!("Invalid script type: {}", args.script_type).into());
|
|
}
|
|
}
|
|
|
|
if args.verbose > 0 {
|
|
info!("🔗 Starting Hero Supervisor");
|
|
info!("📋 Configuration:");
|
|
info!(" Caller ID: {}", args.caller_id);
|
|
info!(" Context ID: {}", args.context_id);
|
|
info!(" Script Type: {}", args.script_type);
|
|
info!(" Redis URL: {}", args.redis_url);
|
|
info!(" Timeout: {}s", args.timeout);
|
|
info!(" Using hardcoded worker queues for script type: {}", args.script_type);
|
|
info!("");
|
|
}
|
|
|
|
// Create the supervisor client
|
|
let client = SupervisorBuilder::new()
|
|
.redis_url(&args.redis_url)
|
|
.build()?;
|
|
|
|
if args.verbose > 0 {
|
|
info!("✅ Connected to Redis at {}", args.redis_url);
|
|
}
|
|
|
|
// Determine execution mode
|
|
if let Some(script_content) = args.script {
|
|
// Execute inline script
|
|
if args.verbose > 0 {
|
|
info!("📜 Executing inline script");
|
|
}
|
|
execute_script(&client, script_content, &args.script_type, args.timeout).await?;
|
|
} else if let Some(file_path) = args.file {
|
|
// Execute script from file
|
|
if args.verbose > 0 {
|
|
info!("📁 Loading script from file: {}", file_path);
|
|
}
|
|
let script_content = std::fs::read_to_string(&file_path)
|
|
.map_err(|e| format!("Failed to read script file '{}': {}", file_path, e))?;
|
|
execute_script(&client, script_content, &args.script_type, args.timeout).await?;
|
|
} else {
|
|
// Interactive mode
|
|
info!("🎮 Entering interactive mode");
|
|
info!("Type Rhai scripts and press Enter to execute. Type 'exit' or 'quit' to close.");
|
|
run_interactive_mode(&client, &args.script_type, args.timeout, args.verbose).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn execute_script(
|
|
client: &Supervisor,
|
|
script: String,
|
|
script_type_str: &str,
|
|
timeout_secs: u64,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
info!("⚡ Executing script: {:.50}...", script);
|
|
|
|
// Parse script type
|
|
let script_type = match script_type_str.to_lowercase().as_str() {
|
|
"osis" => ScriptType::OSIS,
|
|
"sal" => ScriptType::SAL,
|
|
"v" => ScriptType::V,
|
|
"python" => ScriptType::Python,
|
|
_ => {
|
|
error!("❌ Invalid script type: {}. Valid types: osis, sal, v, python", script_type_str);
|
|
return Err(format!("Invalid script type: {}", script_type_str).into());
|
|
}
|
|
};
|
|
|
|
let timeout = Duration::from_secs(timeout_secs);
|
|
|
|
match client
|
|
.new_job()
|
|
.script_type(script_type)
|
|
.script(&script)
|
|
.timeout(timeout)
|
|
.await_response()
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
info!("✅ Script execution completed");
|
|
println!("{}", "Result:".green().bold());
|
|
println!("{}", result);
|
|
}
|
|
Err(e) => {
|
|
error!("❌ Script execution failed: {}", e);
|
|
return Err(Box::new(e));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn run_interactive_mode(
|
|
client: &Supervisor,
|
|
script_type_str: &str,
|
|
timeout_secs: u64,
|
|
verbose: u8,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
// Parse script type
|
|
let script_type = match script_type_str.to_lowercase().as_str() {
|
|
"osis" => ScriptType::OSIS,
|
|
"sal" => ScriptType::SAL,
|
|
"v" => ScriptType::V,
|
|
"python" => ScriptType::Python,
|
|
_ => {
|
|
error!("❌ Invalid script type: {}. Valid types: osis, sal, v, python", script_type_str);
|
|
return Err(format!("Invalid script type: {}", script_type_str).into());
|
|
}
|
|
};
|
|
|
|
let timeout = Duration::from_secs(timeout_secs);
|
|
|
|
loop {
|
|
print!("rhai> ");
|
|
io::stdout().flush()?;
|
|
|
|
let mut input = String::new();
|
|
io::stdin().read_line(&mut input)?;
|
|
|
|
let input = input.trim();
|
|
|
|
if input.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
if input == "exit" || input == "quit" {
|
|
info!("👋 Goodbye!");
|
|
break;
|
|
}
|
|
|
|
if verbose > 0 {
|
|
info!("⚡ Executing: {}", input);
|
|
}
|
|
|
|
match client
|
|
.new_job()
|
|
.script_type(script_type.clone())
|
|
.script(input)
|
|
.timeout(timeout)
|
|
.await_response()
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
println!("{}", result.green());
|
|
}
|
|
Err(e) => {
|
|
println!("{}", format!("error: {}", e).red());
|
|
}
|
|
}
|
|
|
|
println!(); // Add blank line for readability
|
|
}
|
|
|
|
Ok(())
|
|
} |