archive unused / old code
This commit is contained in:
@@ -3,22 +3,6 @@ name = "hero_supervisor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "supervisor"
|
||||
path = "cmd/supervisor.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hive-supervisor"
|
||||
path = "cmd/hive_supervisor.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hive-supervisor-tui"
|
||||
path = "cmd/hive_supervisor_tui.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hive-supervisor-tui-safe"
|
||||
path = "cmd/hive_supervisor_tui_safe.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
env_logger = "0.10"
|
||||
|
@@ -1,66 +0,0 @@
|
||||
# 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
|
@@ -1,365 +0,0 @@
|
||||
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
|
||||
}
|
@@ -1,236 +0,0 @@
|
||||
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