diff --git a/.gitignore b/.gitignore index 1de5659..a7f3110 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -target \ No newline at end of file +target +logs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c8a06c6..618fd0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ version = 4 name = "actor_osis" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "baobab_actor", "chrono", @@ -192,11 +193,13 @@ dependencies = [ name = "baobab_actor" version = "0.1.0" dependencies = [ + "anyhow", "anyhow", "async-trait", "chrono", "clap", "crossterm", + "crossterm", "env_logger", "hero_job", "hero_supervisor", @@ -205,6 +208,7 @@ dependencies = [ "heromodels_core", "log", "ratatui", + "ratatui", "redis", "rhai", "serde", @@ -340,8 +344,10 @@ dependencies = [ [[package]] name = "clap" version = "4.5.43" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -350,8 +356,10 @@ dependencies = [ [[package]] name = "clap_builder" version = "4.5.43" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -390,7 +398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -938,8 +946,10 @@ dependencies = [ [[package]] name = "h2" version = "0.4.12" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1189,6 +1199,7 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.12", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -2460,6 +2471,7 @@ dependencies = [ name = "reth-ipc" version = "1.6.0" source = "git+https://github.com/paradigmxyz/reth#59e4a5556fa54f1c210e45412b6a91f2351bea19" +source = "git+https://github.com/paradigmxyz/reth#59e4a5556fa54f1c210e45412b6a91f2351bea19" dependencies = [ "bytes", "futures", @@ -3821,7 +3833,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 41228d9..c0a4741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,11 @@ path = "src/lib.rs" [[bin]] name = "actor_osis" -path = "cmd/actor_osis.rs" +path = "cmd/actor.rs" + +[[bin]] +name = "actor_osis_tui" +path = "cmd/terminal_ui.rs" [[example]] name = "engine" @@ -22,6 +26,7 @@ path = "examples/actor.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0" redis = { version = "0.25.0", features = ["tokio-comp"] } rhai = { version = "1.21.0", features = ["std", "sync", "decimal", "internals"] } serde = { version = "1.0", features = ["derive"] } diff --git a/cmd/actor_osis.rs b/cmd/actor.rs similarity index 100% rename from cmd/actor_osis.rs rename to cmd/actor.rs diff --git a/cmd/terminal_ui.rs b/cmd/terminal_ui.rs new file mode 100644 index 0000000..1a3e55a --- /dev/null +++ b/cmd/terminal_ui.rs @@ -0,0 +1,148 @@ +//! Simplified main function for Baobab Actor TUI +//! +//! This binary provides a clean entry point for the actor monitoring and job dispatch interface. + +use anyhow::{Result, Context}; +use baobab_actor::terminal_ui::{App, setup_and_run_tui}; +use clap::Parser; +use log::{info, warn, error}; +use std::path::PathBuf; +use std::process::{Child, Command}; +use tokio::signal; + +#[derive(Parser)] +#[command(name = "baobab-actor-tui")] +#[command(about = "Terminal UI for Baobab Actor - Monitor and dispatch jobs to a single actor")] +struct Args { + /// Redis URL for job queue + #[arg(short, long, default_value = "redis://localhost:6379")] + redis_url: String, + + /// Enable verbose logging + #[arg(short, long)] + verbose: bool, +} + +/// Initialize logging based on verbosity level +fn init_logging(verbose: bool) { + if verbose { + env_logger::Builder::from_default_env() + .filter_level(log::LevelFilter::Debug) + .init(); + } else { + env_logger::Builder::from_default_env() + .filter_level(log::LevelFilter::Info) + .init(); + } +} + +/// Create and configure the TUI application +fn create_app(args: &Args) -> Result { + let actor_id = "osis".to_string(); + + // Get the crate root directory + let crate_root = std::env::var("CARGO_MANIFEST_DIR") + .unwrap_or_else(|_| ".".to_string()); + let crate_root = PathBuf::from(crate_root); + + let actor_path = crate_root.join("target/debug/actor_osis"); + let example_dir = Some(crate_root.join("examples/scripts")); + + App::new( + actor_id, + actor_path, + args.redis_url.clone(), + example_dir, + ) +} + +/// Spawn the actor binary as a background process +fn spawn_actor_process(_args: &Args) -> Result { + // Get the crate root directory + let crate_root = std::env::var("CARGO_MANIFEST_DIR") + .unwrap_or_else(|_| ".".to_string()); + let actor_path = PathBuf::from(crate_root).join("target/debug/actor_osis"); + info!("๐ŸŽฌ Spawning actor process: {}", actor_path.display()); + + let mut cmd = Command::new(&actor_path); + + // Redirect stdout and stderr to null to prevent logs from interfering with TUI + cmd.stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()); + + // Spawn the process + let child = cmd + .spawn() + .with_context(|| format!("Failed to spawn actor process: {}", actor_path.display()))?; + + info!("โœ… Actor process spawned with PID: {}", child.id()); + Ok(child) +} + +/// Cleanup function to terminate actor process +fn cleanup_actor_process(mut actor_process: Child) { + info!("๐Ÿงน Cleaning up actor process..."); + + match actor_process.try_wait() { + Ok(Some(status)) => { + info!("Actor process already exited with status: {}", status); + } + Ok(None) => { + info!("Terminating actor process..."); + if let Err(e) = actor_process.kill() { + error!("Failed to kill actor process: {}", e); + } else { + match actor_process.wait() { + Ok(status) => info!("Actor process terminated with status: {}", status), + Err(e) => error!("Failed to wait for actor process: {}", e), + } + } + } + Err(e) => { + error!("Failed to check actor process status: {}", e); + } + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let args = Args::parse(); + + // Initialize logging + init_logging(args.verbose); + + let crate_root = std::env::var("CARGO_MANIFEST_DIR") + .unwrap_or_else(|_| ".".to_string()); + + info!("๐Ÿš€ Starting Baobab Actor TUI..."); + info!("Actor ID: osis"); + info!("Actor Path: {}/target/debug/actor_osis", crate_root); + info!("Redis URL: {}", args.redis_url); + info!("Example Directory: {}/examples/scripts", crate_root); + + // Spawn the actor process first + let actor_process = spawn_actor_process(&args)?; + + // Give the actor a moment to start up + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + + // Create app and run TUI + let app = create_app(&args)?; + + // Set up signal handling for graceful shutdown + let result = tokio::select! { + tui_result = setup_and_run_tui(app) => { + info!("TUI exited"); + tui_result + } + _ = signal::ctrl_c() => { + info!("Received Ctrl+C, shutting down..."); + Ok(()) + } + }; + + // Clean up the actor process + cleanup_actor_process(actor_process); + + result +} diff --git a/examples/scripts/heroledger.rhai b/examples/scripts/heroledger.rhai new file mode 100644 index 0000000..3d4c72f --- /dev/null +++ b/examples/scripts/heroledger.rhai @@ -0,0 +1,53 @@ +// heroledger.rhai - Demonstration of HeroLedger models in Rhai + +print("=== HeroLedger Models Demo ==="); + +// Create a new user +print("\n--- Creating User ---"); +let new_user = new_user() + .name("Alice Johnson") + .email("alice@herocode.com") + .pubkey("0x1234567890abcdef") + .status("Active") + .save_user(); + +print("Created user: " + new_user.get_name()); +print("User ID: " + new_user.get_id()); +print("User email: " + new_user.get_email()); +print("User pubkey: " + new_user.get_pubkey()); + +// Create a new group +print("\n--- Creating Group ---"); +let new_group = new_group() + .name("HeroCode Developers") + .description("A group for HeroCode development team members") + .visibility("Public") + .save_group(); + +print("Created group: " + new_group.get_name()); +print("Group ID: " + new_group.get_id()); +print("Group description: " + new_group.get_description()); + +// Create a new account +print("\n--- Creating Account ---"); +let new_account = new_account() + .name("Alice's Main Account") + .description("Primary account for Alice Johnson") + .currency("USD") + .save_account(); + +print("Created account: " + new_account.get_name()); +print("Account ID: " + new_account.get_id()); +print("Account currency: " + new_account.get_currency()); + +// Create a new DNS zone +print("\n--- Creating DNS Zone ---"); +let new_dns_zone = new_dns_zone() + .name("herocode.com") + .description("Main domain for HeroCode") + .save_dns_zone(); + +print("Created DNS zone: " + new_dns_zone.get_name()); +print("DNS zone ID: " + new_dns_zone.get_id()); + +print("\n=== Demo Complete ==="); diff --git a/src/engine.rs b/src/engine.rs index 1f895a4..602b178 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,42 +1,4 @@ -//! # Rhailib Domain-Specific Language (DSL) Engine -//! -//! This module provides a comprehensive Domain-Specific Language implementation for the Rhai -//! scripting engine, exposing business domain models and operations through a fluent, -//! chainable API. -//! -//! ## Overview -//! -//! The DSL is organized into business domain modules, each providing Rhai-compatible -//! functions for creating, manipulating, and persisting domain entities. All operations -//! include proper authorization checks and type safety. -//! -//! ## Available Domains -//! -//! - **Business Operations** (`biz`): Companies, products, sales, shareholders -//! - **Financial Models** (`finance`): Accounts, assets, marketplace operations -//! - **Content Management** (`library`): Collections, images, PDFs, books, slideshows -//! - **Workflow Management** (`flow`): Flows, steps, signature requirements -//! - **Community Management** (`circle`): Circles, themes, membership -//! - **Contact Management** (`contact`): Contact information and relationships -//! - **Access Control** (`access`): Security and permissions -//! - **Time Management** (`calendar`): Calendar and scheduling -//! - **Core Utilities** (`core`): Comments and fundamental operations -//! - **Generic Objects** (`object`): Generic object manipulation -//! -//! ## Usage Example -//! -//! ```rust -//! use rhai::Engine; -//! use crate::engine::register_dsl_modules; -//! -//! let mut engine = Engine::new(); -//! register_dsl_modules(&mut engine); -//! -//! // Now the engine can execute scripts like: -//! // let company = new_company().name("Acme Corp").email("contact@acme.com"); -//! // let saved = save_company(company); -//! ``` - +use heromodels::models::heroledger::rhai::register_heroledger_rhai_modules; use rhai::Engine; use rhailib_dsl; use std::sync::{Arc, OnceLock}; @@ -161,6 +123,8 @@ pub fn register_dsl_modules(engine: &mut Engine) { // Register basic object functionality directly register_object_functions(engine); + heromodels::heroledger::rhai::register_heroledger_rhai_modules(&mut engine); + println!("Rhailib Domain Specific Language modules registered successfully."); }