Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/runbox-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ dialoguer = "0.11"
chrono = { version = "0.4", features = ["serde"] }
log = "0.4"

# TUI dependencies
ratatui = "0.28"
crossterm = "0.28"
tokio = { version = "1", features = ["full", "sync", "time"] }
futures = "0.3"
libc = "0.2"

[features]
# Enable tests that require tmux to be installed
tmux-tests = []
Expand Down
69 changes: 69 additions & 0 deletions crates/runbox-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::{bail, Context, Result};
mod tui;
use chrono::Utc;
use clap::{Parser, Subcommand, ValueEnum};
use dialoguer::{theme::ColorfulTheme, Input};
Expand Down Expand Up @@ -696,6 +697,40 @@ CONTENTS:
- Examples")]
/// Display the full tutorial in the terminal
Tutorial,
/// Interactive process monitor with real-time updates
#[command(after_help = "\
EXAMPLES:
# Launch the interactive monitor
runbox monitor

# Specify tick rate for refresh (default: 2 seconds)
runbox monitor --tick-rate 1

FEATURES:
- Real-time process list with auto-refresh
- Keyboard navigation (j/k or arrows)
- Press Enter to view logs for selected process
- Press 's' to stop a running process
- Press 'a' to attach to tmux/zellij session
- Press 'q' to quit

KEYBINDINGS:
↑/k Move selection up
↓/j Move selection down
Enter View logs for selected process
s Stop selected process (if running)
a Attach to tmux/zellij session
r Refresh process list
q Quit

RELATED COMMANDS:
runbox ps Static process list (non-interactive)
runbox logs View logs for a specific run")]
Monitor {
/// Tick rate in seconds for auto-refresh (default: 2)
#[arg(long, default_value = "2")]
tick_rate: u64,
},
}
#[derive(Subcommand)]
enum DaemonCommands {
Expand Down Expand Up @@ -1084,12 +1119,46 @@ fn main() -> Result<()> {
DaemonCommands::Ping => cmd_daemon_ping(),
},
Commands::Tutorial => cmd_tutorial(),
Commands::Monitor { tick_rate } => cmd_monitor(&storage, tick_rate),
}
}
fn cmd_tutorial() -> Result<()> {
println!("{}", TUTORIAL);
Ok(())
}

// === Monitor Command ===
fn cmd_monitor(_storage: &Storage, tick_rate: u64) -> Result<()> {
use std::time::Duration;
use crate::tui::{run_app, app::execute_post_exit_action};

let tick = Duration::from_secs(tick_rate);

// Create a new storage instance for the TUI
let tui_storage = if let Ok(home) = std::env::var("RUNBOX_HOME") {
Storage::with_base_dir(PathBuf::from(home))?
} else {
Storage::new()?
};

// Run the TUI
match run_app(tui_storage, tick) {
Ok(Some(action)) => {
// Execute post-exit action (e.g., attach to tmux)
execute_post_exit_action(action)?;
}
Ok(None) => {
// Normal exit
}
Err(e) => {
// TUI failed, show error
eprintln!("TUI error: {}", e);
return Err(e);
}
}

Ok(())
}
// === Daemon Commands ===
fn cmd_daemon_start() -> Result<()> {
use std::process::Command as StdCommand;
Expand Down
Loading