archive unused / old code
This commit is contained in:
		
							
								
								
									
										66
									
								
								_archive/core/supervisor/cmd/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								_archive/core/supervisor/cmd/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
# Supervisor CLI
 | 
			
		||||
 | 
			
		||||
A command-line interface for the Hero Supervisor.
 | 
			
		||||
 | 
			
		||||
## Binary: `hive-supervisor`
 | 
			
		||||
 | 
			
		||||
### Installation
 | 
			
		||||
 | 
			
		||||
Build the binary:
 | 
			
		||||
```bash
 | 
			
		||||
cargo build --bin hive-supervisor --release
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Usage
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Basic usage
 | 
			
		||||
hive-supervisor --config <CONFIG_PATH>
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Where config is toml file with the following structure:
 | 
			
		||||
```toml
 | 
			
		||||
[global]
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
 | 
			
		||||
[osis_worker]
 | 
			
		||||
binary_path = "/path/to/osis_worker"
 | 
			
		||||
env_vars = { "VAR1" = "value1", "VAR2" = "value2" }
 | 
			
		||||
 | 
			
		||||
[sal_worker]
 | 
			
		||||
binary_path = "/path/to/sal_worker"
 | 
			
		||||
env_vars = { "VAR1" = "value1", "VAR2" = "value2" }
 | 
			
		||||
 | 
			
		||||
[v_worker]
 | 
			
		||||
binary_path = "/path/to/v_worker"
 | 
			
		||||
env_vars = { "VAR1" = "value1", "VAR2" = "value2" }
 | 
			
		||||
 | 
			
		||||
[python_worker]
 | 
			
		||||
binary_path = "/path/to/python_worker"
 | 
			
		||||
env_vars = { "VAR1" = "value1", "VAR2" = "value2" }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Lets have verbosity settings etc.
 | 
			
		||||
CLI Offers a few commands:
 | 
			
		||||
 | 
			
		||||
workers:
 | 
			
		||||
    start
 | 
			
		||||
    stop
 | 
			
		||||
    restart
 | 
			
		||||
    status
 | 
			
		||||
    logs
 | 
			
		||||
    list
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
    create
 | 
			
		||||
    start
 | 
			
		||||
    stop
 | 
			
		||||
    restart
 | 
			
		||||
    status
 | 
			
		||||
    logs
 | 
			
		||||
    list
 | 
			
		||||
    
 | 
			
		||||
repl: you can enter interactive mode to run scripts, however predefine caller_id, context_id and worker type so supervisor dispathces jobs accordingly
 | 
			
		||||
							
								
								
									
										365
									
								
								_archive/core/supervisor/cmd/hive_supervisor_tui_safe.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								_archive/core/supervisor/cmd/hive_supervisor_tui_safe.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use crossterm::{
 | 
			
		||||
    event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
 | 
			
		||||
    execute,
 | 
			
		||||
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 | 
			
		||||
};
 | 
			
		||||
use hero_supervisor::{Supervisor, SupervisorBuilder};
 | 
			
		||||
use zinit_client::ZinitClient;
 | 
			
		||||
use log::{error, info};
 | 
			
		||||
use ratatui::{
 | 
			
		||||
    backend::CrosstermBackend,
 | 
			
		||||
    layout::{Constraint, Direction, Layout, Rect},
 | 
			
		||||
    style::{Color, Modifier, Style},
 | 
			
		||||
    text::Line,
 | 
			
		||||
    widgets::{
 | 
			
		||||
        Block, Borders, List, ListItem, Paragraph, Tabs, Wrap,
 | 
			
		||||
    },
 | 
			
		||||
    Frame, Terminal,
 | 
			
		||||
};
 | 
			
		||||
use std::{
 | 
			
		||||
    io,
 | 
			
		||||
    path::PathBuf,
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
    time::{Duration, Instant},
 | 
			
		||||
};
 | 
			
		||||
use tokio::time::sleep;
 | 
			
		||||
use toml;
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
 | 
			
		||||
#[derive(Parser)]
 | 
			
		||||
#[command(name = "hive-supervisor-tui")]
 | 
			
		||||
#[command(about = "Hero Supervisor Terminal User Interface")]
 | 
			
		||||
struct Args {
 | 
			
		||||
    #[arg(short, long, help = "Configuration file path")]
 | 
			
		||||
    config: PathBuf,
 | 
			
		||||
    
 | 
			
		||||
    #[arg(short, long, help = "Enable verbose logging")]
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
struct Config {
 | 
			
		||||
    global: GlobalConfig,
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    workers: std::collections::HashMap<String, WorkerConfigToml>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
struct GlobalConfig {
 | 
			
		||||
    redis_url: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
struct WorkerConfigToml {
 | 
			
		||||
    binary_path: String,
 | 
			
		||||
    env_vars: Option<std::collections::HashMap<String, String>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
enum TabId {
 | 
			
		||||
    Dashboard,
 | 
			
		||||
    Workers,
 | 
			
		||||
    Jobs,
 | 
			
		||||
    Logs,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TabId {
 | 
			
		||||
    fn all() -> Vec<TabId> {
 | 
			
		||||
        vec![TabId::Dashboard, TabId::Workers, TabId::Jobs, TabId::Logs]
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn title(&self) -> &str {
 | 
			
		||||
        match self {
 | 
			
		||||
            TabId::Dashboard => "Dashboard",
 | 
			
		||||
            TabId::Workers => "Workers",
 | 
			
		||||
            TabId::Jobs => "Jobs", 
 | 
			
		||||
            TabId::Logs => "Logs",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct App {
 | 
			
		||||
    supervisor: Arc<Supervisor>,
 | 
			
		||||
    current_tab: TabId,
 | 
			
		||||
    should_quit: bool,
 | 
			
		||||
    logs: Vec<String>,
 | 
			
		||||
    last_update: Instant,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl App {
 | 
			
		||||
    fn new(supervisor: Arc<Supervisor>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            supervisor,
 | 
			
		||||
            current_tab: TabId::Dashboard,
 | 
			
		||||
            should_quit: false,
 | 
			
		||||
            logs: vec!["TUI started successfully".to_string()],
 | 
			
		||||
            last_update: Instant::now(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn next_tab(&mut self) {
 | 
			
		||||
        let tabs = TabId::all();
 | 
			
		||||
        let current_index = tabs.iter().position(|t| *t == self.current_tab).unwrap_or(0);
 | 
			
		||||
        let next_index = (current_index + 1) % tabs.len();
 | 
			
		||||
        self.current_tab = tabs[next_index].clone();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn prev_tab(&mut self) {
 | 
			
		||||
        let tabs = TabId::all();
 | 
			
		||||
        let current_index = tabs.iter().position(|t| *t == self.current_tab).unwrap_or(0);
 | 
			
		||||
        let prev_index = if current_index == 0 { tabs.len() - 1 } else { current_index - 1 };
 | 
			
		||||
        self.current_tab = tabs[prev_index].clone();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn add_log(&mut self, message: String) {
 | 
			
		||||
        self.logs.push(format!("[{}] {}", 
 | 
			
		||||
            chrono::Utc::now().format("%H:%M:%S"), 
 | 
			
		||||
            message
 | 
			
		||||
        ));
 | 
			
		||||
        if self.logs.len() > 100 {
 | 
			
		||||
            self.logs.remove(0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn handle_key(&mut self, key: KeyCode) -> bool {
 | 
			
		||||
        match key {
 | 
			
		||||
            KeyCode::Char('q') => {
 | 
			
		||||
                self.should_quit = true;
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
            KeyCode::Tab => {
 | 
			
		||||
                self.next_tab();
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
            KeyCode::BackTab => {
 | 
			
		||||
                self.prev_tab();
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
            _ => false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render_ui(f: &mut Frame, app: &mut App) {
 | 
			
		||||
    let chunks = Layout::default()
 | 
			
		||||
        .direction(Direction::Vertical)
 | 
			
		||||
        .constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
 | 
			
		||||
        .split(f.area());
 | 
			
		||||
    
 | 
			
		||||
    // Render tabs
 | 
			
		||||
    let tabs_list = TabId::all();
 | 
			
		||||
    let tab_titles: Vec<Line> = tabs_list
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|t| Line::from(t.title()))
 | 
			
		||||
        .collect();
 | 
			
		||||
    
 | 
			
		||||
    let selected_tab = TabId::all().iter().position(|t| *t == app.current_tab).unwrap_or(0);
 | 
			
		||||
    let tabs = Tabs::new(tab_titles)
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("Hero Supervisor TUI"))
 | 
			
		||||
        .select(selected_tab)
 | 
			
		||||
        .style(Style::default().fg(Color::Cyan))
 | 
			
		||||
        .highlight_style(Style::default().add_modifier(Modifier::BOLD).bg(Color::Black));
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(tabs, chunks[0]);
 | 
			
		||||
    
 | 
			
		||||
    // Render content based on selected tab
 | 
			
		||||
    match app.current_tab {
 | 
			
		||||
        TabId::Dashboard => render_dashboard(f, chunks[1], app),
 | 
			
		||||
        TabId::Workers => render_workers(f, chunks[1], app),
 | 
			
		||||
        TabId::Jobs => render_jobs(f, chunks[1], app),
 | 
			
		||||
        TabId::Logs => render_logs(f, chunks[1], app),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render_dashboard(f: &mut Frame, area: Rect, app: &App) {
 | 
			
		||||
    let chunks = Layout::default()
 | 
			
		||||
        .direction(Direction::Vertical)
 | 
			
		||||
        .constraints([Constraint::Length(7), Constraint::Min(0)].as_ref())
 | 
			
		||||
        .split(area);
 | 
			
		||||
    
 | 
			
		||||
    // Status overview - supervisor is already running if we get here
 | 
			
		||||
    let status_text = "Status: ✓ Running\nWorkers: Started successfully\nJobs: Ready for processing\n\nPress 'q' to quit, Tab to navigate";
 | 
			
		||||
    
 | 
			
		||||
    let status_paragraph = Paragraph::new(status_text)
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("System Status"))
 | 
			
		||||
        .wrap(Wrap { trim: true });
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(status_paragraph, chunks[0]);
 | 
			
		||||
    
 | 
			
		||||
    // Recent logs
 | 
			
		||||
    let log_items: Vec<ListItem> = app.logs
 | 
			
		||||
        .iter()
 | 
			
		||||
        .rev()
 | 
			
		||||
        .take(10)
 | 
			
		||||
        .map(|log| ListItem::new(log.as_str()))
 | 
			
		||||
        .collect();
 | 
			
		||||
    
 | 
			
		||||
    let logs_list = List::new(log_items)
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("Recent Activity"));
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(logs_list, chunks[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render_workers(f: &mut Frame, area: Rect, _app: &App) {
 | 
			
		||||
    let paragraph = Paragraph::new("Workers tab - Status checking not implemented yet to avoid system issues")
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("Workers"))
 | 
			
		||||
        .wrap(Wrap { trim: true });
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(paragraph, area);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render_jobs(f: &mut Frame, area: Rect, _app: &App) {
 | 
			
		||||
    let paragraph = Paragraph::new("Jobs tab - Job monitoring not implemented yet to avoid system issues")
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("Jobs"))
 | 
			
		||||
        .wrap(Wrap { trim: true });
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(paragraph, area);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn render_logs(f: &mut Frame, area: Rect, app: &App) {
 | 
			
		||||
    let items: Vec<ListItem> = app.logs
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|log| ListItem::new(log.as_str()))
 | 
			
		||||
        .collect();
 | 
			
		||||
    
 | 
			
		||||
    let logs_list = List::new(items)
 | 
			
		||||
        .block(Block::default().borders(Borders::ALL).title("System Logs"));
 | 
			
		||||
    
 | 
			
		||||
    f.render_widget(logs_list, area);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn run_app(
 | 
			
		||||
    terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
 | 
			
		||||
    app: &mut App,
 | 
			
		||||
) -> Result<()> {
 | 
			
		||||
    loop {
 | 
			
		||||
        terminal.draw(|f| render_ui(f, app))?;
 | 
			
		||||
 | 
			
		||||
        // Simple, safe event handling
 | 
			
		||||
        if event::poll(Duration::from_millis(100))? {
 | 
			
		||||
            if let Event::Key(key) = event::read()? {
 | 
			
		||||
                if key.kind == KeyEventKind::Press {
 | 
			
		||||
                    if app.handle_key(key.code) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if app.should_quit {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Small delay to prevent excessive CPU usage
 | 
			
		||||
        sleep(Duration::from_millis(50)).await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<()> {
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
    
 | 
			
		||||
    // Initialize logging
 | 
			
		||||
    if args.verbose {
 | 
			
		||||
        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
 | 
			
		||||
    } else {
 | 
			
		||||
        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    info!("Hero Supervisor TUI - Fail-fast initialization");
 | 
			
		||||
    
 | 
			
		||||
    // Step 1: Load and parse configuration
 | 
			
		||||
    info!("Step 1/4: Loading configuration from {:?}", args.config);
 | 
			
		||||
    let config_content = std::fs::read_to_string(&args.config)
 | 
			
		||||
        .map_err(|e| anyhow::anyhow!("Failed to read config file: {}", e))?;
 | 
			
		||||
    let config: Config = toml::from_str(&config_content)
 | 
			
		||||
        .map_err(|e| anyhow::anyhow!("Failed to parse config file: {}", e))?;
 | 
			
		||||
    info!("✓ Configuration loaded successfully");
 | 
			
		||||
    
 | 
			
		||||
    // Step 2: Check if Zinit is running
 | 
			
		||||
    info!("Step 2/4: Checking if Zinit is running...");
 | 
			
		||||
    let zinit_client = ZinitClient::new("/tmp/zinit.sock");
 | 
			
		||||
    match zinit_client.status("_test_connectivity").await {
 | 
			
		||||
        Ok(_) => {
 | 
			
		||||
            info!("✓ Zinit is running and accessible");
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            let error_msg = e.to_string();
 | 
			
		||||
            if error_msg.contains("Connection refused") || error_msg.contains("No such file") {
 | 
			
		||||
                eprintln!("Error: Zinit process manager is not running.");
 | 
			
		||||
                eprintln!("Please start Zinit before running the supervisor TUI.");
 | 
			
		||||
                eprintln!("Expected Zinit socket at: /tmp/zinit.sock");
 | 
			
		||||
                std::process::exit(1);
 | 
			
		||||
            } else {
 | 
			
		||||
                info!("✓ Zinit is running (service not found is expected)");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Step 3: Build supervisor
 | 
			
		||||
    info!("Step 3/4: Building supervisor...");
 | 
			
		||||
    let mut builder = SupervisorBuilder::new()
 | 
			
		||||
        .redis_url(&config.global.redis_url);
 | 
			
		||||
    
 | 
			
		||||
    for (worker_name, worker_config) in &config.workers {
 | 
			
		||||
        match worker_name.as_str() {
 | 
			
		||||
            "osis_worker" => builder = builder.osis_worker(&worker_config.binary_path),
 | 
			
		||||
            "sal_worker" => builder = builder.sal_worker(&worker_config.binary_path),
 | 
			
		||||
            "v_worker" => builder = builder.v_worker(&worker_config.binary_path),
 | 
			
		||||
            "python_worker" => builder = builder.python_worker(&worker_config.binary_path),
 | 
			
		||||
            _ => log::warn!("Unknown worker type: {}", worker_name),
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(env_vars) = &worker_config.env_vars {
 | 
			
		||||
            for (key, value) in env_vars {
 | 
			
		||||
                builder = builder.worker_env_var(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    let supervisor = Arc::new(builder.build()
 | 
			
		||||
        .map_err(|e| anyhow::anyhow!("Failed to build supervisor: {}", e))?);
 | 
			
		||||
    info!("✓ Supervisor built successfully");
 | 
			
		||||
    
 | 
			
		||||
    // Step 4: Start supervisor and workers
 | 
			
		||||
    info!("Step 4/4: Starting supervisor and workers...");
 | 
			
		||||
    supervisor.start_workers().await
 | 
			
		||||
        .map_err(|e| anyhow::anyhow!("Failed to start workers: {}", e))?;
 | 
			
		||||
    info!("✓ All workers started successfully");
 | 
			
		||||
    
 | 
			
		||||
    // All initialization successful - now start TUI
 | 
			
		||||
    info!("Initialization complete - starting TUI...");
 | 
			
		||||
    let mut app = App::new(Arc::clone(&supervisor));
 | 
			
		||||
    
 | 
			
		||||
    // Setup terminal
 | 
			
		||||
    enable_raw_mode()?;
 | 
			
		||||
    let mut stdout = io::stdout();
 | 
			
		||||
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
 | 
			
		||||
    let backend = CrosstermBackend::new(stdout);
 | 
			
		||||
    let mut terminal = Terminal::new(backend)?;
 | 
			
		||||
    
 | 
			
		||||
    // Run the app
 | 
			
		||||
    let result = run_app(&mut terminal, &mut app).await;
 | 
			
		||||
    
 | 
			
		||||
    // Cleanup
 | 
			
		||||
    disable_raw_mode()?;
 | 
			
		||||
    execute!(
 | 
			
		||||
        terminal.backend_mut(),
 | 
			
		||||
        LeaveAlternateScreen,
 | 
			
		||||
        DisableMouseCapture
 | 
			
		||||
    )?;
 | 
			
		||||
    terminal.show_cursor()?;
 | 
			
		||||
    
 | 
			
		||||
    // Cleanup supervisor
 | 
			
		||||
    if let Err(e) = supervisor.cleanup_and_shutdown().await {
 | 
			
		||||
        error!("Error during cleanup: {}", e);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    info!("Hero Supervisor TUI shutdown complete");
 | 
			
		||||
    
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										236
									
								
								_archive/core/supervisor/cmd/supervisor.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								_archive/core/supervisor/cmd/supervisor.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
			
		||||
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(())
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user