mirror of
https://github.com/robcholz/vibebox.git
synced 2026-04-29 04:35:57 +02:00
Merge pull request #1 from robcholz/vibebox-cmd
feat: setup vibebox commands
This commit is contained in:
@@ -68,7 +68,6 @@ In host cli:
|
||||
|
||||
In vibebox:
|
||||
|
||||
- use `:new` to prompt user to delete and create a session.
|
||||
- use `:exit` to exit vibebox.
|
||||
|
||||
### (Shows all the mounts/network/visibility)
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@
|
||||
4. [x] use vm.lock to ensure process concurrency safety.
|
||||
5. [x] wire up SessionManager.
|
||||
6. [x] VM should be separated by a per-session VM daemon process (only accepts if to shut down vm and itself).
|
||||
7. [ ] setup vibebox commands
|
||||
7. [x] setup vibebox commands
|
||||
8. [ ] setup cli commands.
|
||||
9. [ ] fix ui overlap.
|
||||
10. [ ] intensive integration test.
|
||||
|
||||
@@ -9,9 +9,9 @@ use color_eyre::Result;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use vibebox::tui::{AppState, VmInfo};
|
||||
use vibebox::{SessionManager, config, instance, tui, vm, vm_manager};
|
||||
use vibebox::{SessionManager, commands, config, instance, tui, vm, vm_manager};
|
||||
|
||||
const DEFAULT_AUTO_SHUTDOWN_MS: u64 = 3000;
|
||||
const DEFAULT_AUTO_SHUTDOWN_MS: u64 = 30000;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
init_tracing();
|
||||
@@ -65,7 +65,8 @@ fn main() -> Result<()> {
|
||||
} else {
|
||||
tracing::warn!("failed to initialize session manager");
|
||||
}
|
||||
let app = Arc::new(Mutex::new(AppState::new(cwd.clone(), vm_info)));
|
||||
let commands = commands::build_commands();
|
||||
let app = Arc::new(Mutex::new(AppState::new(cwd.clone(), vm_info, commands)));
|
||||
|
||||
{
|
||||
let mut locked = app.lock().expect("app state poisoned");
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::tui::{AppState, VibeboxCommands};
|
||||
use crate::vm::IoControl;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum CommandKind {
|
||||
Help,
|
||||
Exit,
|
||||
}
|
||||
|
||||
struct CommandSpec {
|
||||
name: &'static str,
|
||||
description: &'static str,
|
||||
kind: CommandKind,
|
||||
shell_alias: Option<&'static str>,
|
||||
}
|
||||
|
||||
const COMMAND_SPECS: &[CommandSpec] = &[
|
||||
CommandSpec {
|
||||
name: ":help",
|
||||
description: "Show Vibebox commands.",
|
||||
kind: CommandKind::Help,
|
||||
shell_alias: Some("vibebox_help"),
|
||||
},
|
||||
CommandSpec {
|
||||
name: ":exit",
|
||||
description: "Exit Vibebox.",
|
||||
kind: CommandKind::Exit,
|
||||
shell_alias: Some("exit"),
|
||||
},
|
||||
];
|
||||
|
||||
pub struct CommandHandlers {
|
||||
handlers: HashMap<String, Box<dyn Fn() + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl CommandHandlers {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
handlers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn register<F>(&mut self, name: &str, handler: F)
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
{
|
||||
self.handlers.insert(name.to_string(), Box::new(handler));
|
||||
}
|
||||
|
||||
pub fn handle(&self, line: &str) -> bool {
|
||||
if let Some(handler) = self.handlers.get(line) {
|
||||
handler();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_commands() -> VibeboxCommands {
|
||||
let mut commands = VibeboxCommands::new_empty();
|
||||
for spec in COMMAND_SPECS {
|
||||
commands.add_command(spec.name, spec.description);
|
||||
}
|
||||
commands
|
||||
}
|
||||
|
||||
pub fn render_shell_script() -> String {
|
||||
let mut lines = Vec::new();
|
||||
lines.push("vibebox_help() {".to_string());
|
||||
lines.push(" cat <<'VIBEBOX_HELP'".to_string());
|
||||
lines.push("Vibebox Commands".to_string());
|
||||
for spec in COMMAND_SPECS {
|
||||
lines.push(format!("{} {}", spec.name, spec.description));
|
||||
}
|
||||
lines.push("VIBEBOX_HELP".to_string());
|
||||
lines.push("}".to_string());
|
||||
for spec in COMMAND_SPECS {
|
||||
if let Some(alias) = spec.shell_alias {
|
||||
lines.push(format!("alias {}='{}'", spec.name, alias));
|
||||
}
|
||||
}
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
pub fn build_handlers(app: Arc<Mutex<AppState>>, io_control: Arc<IoControl>) -> CommandHandlers {
|
||||
let mut handlers = CommandHandlers::new();
|
||||
for spec in COMMAND_SPECS {
|
||||
match spec.kind {
|
||||
CommandKind::Help => {
|
||||
let app = app.clone();
|
||||
handlers.register(spec.name, move || {
|
||||
if let Ok(mut locked) = app.lock() {
|
||||
let _ = crate::tui::render_commands_component(&mut locked);
|
||||
}
|
||||
});
|
||||
}
|
||||
CommandKind::Exit => {
|
||||
let io_control = io_control.clone();
|
||||
handlers.register(spec.name, move || {
|
||||
io_control.request_terminal_restore();
|
||||
std::process::exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
handlers
|
||||
}
|
||||
+6
-14
@@ -19,8 +19,9 @@ use time::{OffsetDateTime, format_description::well_known::Rfc3339};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
commands,
|
||||
session_manager::{INSTANCE_DIR_NAME, INSTANCE_TOML_FILENAME},
|
||||
tui::{self, AppState},
|
||||
tui::AppState,
|
||||
vm::{self, LoginAction, VmInput},
|
||||
};
|
||||
|
||||
@@ -344,7 +345,8 @@ pub(crate) fn build_ssh_login_actions(
|
||||
.replace("__SSH_USER__", &ssh_user)
|
||||
.replace("__SUDO_PASSWORD__", &sudo_password)
|
||||
.replace("__PROJECT_NAME__", project_name)
|
||||
.replace("__KEY_PATH__", &key_path);
|
||||
.replace("__KEY_PATH__", &key_path)
|
||||
.replace("__VIBEBOX_SHELL_SCRIPT__", &commands::render_shell_script());
|
||||
let setup = vm::script_command_from_content("ssh_setup", &setup_script)
|
||||
.expect("ssh setup script contained invalid marker");
|
||||
|
||||
@@ -385,18 +387,8 @@ fn spawn_ssh_io(
|
||||
|
||||
let mut line_buf = String::new();
|
||||
|
||||
let on_line = {
|
||||
let app = app.clone();
|
||||
move |line: &str| {
|
||||
if line == ":help" {
|
||||
if let Ok(mut locked) = app.lock() {
|
||||
let _ = tui::render_commands_component(&mut locked);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
let handlers = commands::build_handlers(app.clone(), io_control.clone());
|
||||
let on_line = move |line: &str| handlers.handle(line);
|
||||
|
||||
let on_output = move |bytes: &[u8]| {
|
||||
if ssh_connected_for_output.load(Ordering::SeqCst) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod commands;
|
||||
pub mod instance;
|
||||
pub mod session_manager;
|
||||
pub mod tui;
|
||||
|
||||
+15
@@ -77,6 +77,21 @@ if [ ! -e "${USER_HOME}/.claude" ]; then
|
||||
fi
|
||||
chown -h "${SSH_USER}:${SSH_USER}" "${USER_HOME}/.codex" "${USER_HOME}/.claude" 2>/dev/null || true
|
||||
|
||||
# Vibebox shell commands
|
||||
install -d -m 755 /etc/profile.d
|
||||
cat > /etc/profile.d/vibebox.sh <<'VIBEBOX_SHELL_EOF'
|
||||
__VIBEBOX_SHELL_SCRIPT__
|
||||
VIBEBOX_SHELL_EOF
|
||||
chmod 644 /etc/profile.d/vibebox.sh
|
||||
|
||||
if ! grep -q "vibebox-aliases" "${USER_HOME}/.bashrc" 2>/dev/null; then
|
||||
{
|
||||
echo ""
|
||||
echo "# vibebox-aliases"
|
||||
echo ". /etc/profile.d/vibebox.sh"
|
||||
} >> "${USER_HOME}/.bashrc"
|
||||
fi
|
||||
|
||||
# Install Mise
|
||||
MISE_BIN="${USER_HOME}/.local/bin/mise"
|
||||
if [ ! -x "$MISE_BIN" ] && ! command -v mise >/dev/null 2>&1; then
|
||||
|
||||
+5
-5
@@ -50,11 +50,7 @@ pub struct AppState {
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(cwd: PathBuf, vm_info: VmInfo) -> Self {
|
||||
let mut commands = VibeboxCommands::default();
|
||||
commands.add_command(":new", "Create a new session.");
|
||||
commands.add_command(":exit", "Exit Vibebox.");
|
||||
|
||||
pub fn new(cwd: PathBuf, vm_info: VmInfo, commands: VibeboxCommands) -> Self {
|
||||
Self {
|
||||
cwd,
|
||||
vm_info,
|
||||
@@ -69,6 +65,10 @@ pub struct VibeboxCommands {
|
||||
}
|
||||
|
||||
impl VibeboxCommands {
|
||||
pub fn new_empty() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add_command(&mut self, name: impl Into<String>, description: impl Into<String>) {
|
||||
self.items.push(VibeboxCommand {
|
||||
name: name.into(),
|
||||
|
||||
Reference in New Issue
Block a user