on main help sign in
refactor: remove old cli.
commit8c816c38b7acb250f11c5665eafb0f1426e5abbf authorrobcholz <84130577+robcholz@users.noreply.github.com> dateSat 7 Feb 2026, 15:10 (8 months ago) parent0d7c4a7
6 files changed+47−198
docs/tasks.md+1−1 src/bin/vibebox-cli.rs+17−14 src/bin/vibebox-supervisor.rs+7−1 src/instance.rs−2 src/vm.rs+18−176 src/vm_manager.rs+4−4
docs/tasks.md+1−1
@@ -44,7 +44,7 @@
44447. [x] setup vibebox commands
45458. [ ] setup cli commands.
4646 1. [x] Organize all the params.
47− 2. [ ] Remove old cli.
47+ 2. [x] Remove old cli.
4848 3. [ ] add an actual config file.
4949 4. [ ] set up the cli.
50509. [ ] fix ui overlap, and consistency issue.
src/bin/vibebox-cli.rs+17−14
@@ -21,7 +21,13 @@ fn main() -> Result<()> {
2121
2222 if env::var("VIBEBOX_VM_MANAGER").as_deref() == Ok("1") {
2323 tracing::info!("starting vm manager mode");
24− let args = vm::parse_cli().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
24+ // TODO: wire CLI args into VmArg once we reintroduce CLI parsing.
25+ let args = vm::VmArg {
26+ cpu_count: 2,
27+ ram_bytes: 2048 * 1024 * 1024,
28+ no_default_mounts: false,
29+ mounts: Vec::new(),
30+ };
2531 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
2632 .ok()
2733 .and_then(|value| value.parse::<u64>().ok())
@@ -34,23 +40,20 @@ fn main() -> Result<()> {
3440 return Ok(());
3541 }
3642
37− let args = vm::parse_cli().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
38− tracing::debug!("parsed cli args");
39− if args.version() {
40− vm::print_version();
41− return Ok(());
42− }
43− if args.help() {
44− vm::print_help();
45− return Ok(());
46− }
47−
4843 vm::ensure_signed();
4944
45+ // TODO: wire CLI args into VmArg once we reintroduce CLI parsing.
46+ let vm_args = vm::VmArg {
47+ cpu_count: 2,
48+ ram_bytes: 2048 * 1024 * 1024,
49+ no_default_mounts: false,
50+ mounts: Vec::new(),
51+ };
52+
5053 let vm_info = VmInfo {
5154 version: env!("CARGO_PKG_VERSION").to_string(),
52− max_memory_mb: args.ram_mb(),
53− cpu_cores: args.cpu_count(),
55+ max_memory_mb: vm_args.ram_bytes / (1024 * 1024),
56+ cpu_cores: vm_args.cpu_count,
5457 };
5558 let cwd = env::current_dir().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
5659 tracing::info!(cwd = %cwd.display(), "starting vibebox cli");
src/bin/vibebox-supervisor.rs+7−1
@@ -19,7 +19,13 @@ fn main() -> Result<()> {
1919 let instance_dir = instance::ensure_instance_dir(&cwd)
2020 .map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
2121 let _ = instance::touch_last_active(&instance_dir);
22− let args = vm::parse_cli().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
22+ // TODO: wire CLI args into VmArg once we reintroduce CLI parsing.
23+ let args = vm::VmArg {
24+ cpu_count: 2,
25+ ram_bytes: 2048 * 1024 * 1024,
26+ no_default_mounts: false,
27+ mounts: Vec::new(),
28+ };
2329 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
2430 .ok()
2531 .and_then(|value| value.parse::<u64>().ok())
src/instance.rs−2
@@ -26,7 +26,6 @@ use crate::{
2626};
2727
2828const SSH_KEY_NAME: &str = "ssh_key";
29−#[allow(dead_code)]
3029pub(crate) const SERIAL_LOG_NAME: &str = "serial.log";
3130const DEFAULT_SSH_USER: &str = "vibecoder";
3231const SSH_CONNECT_RETRIES: usize = 30;
@@ -353,7 +352,6 @@ pub(crate) fn build_ssh_login_actions(
353352 vec![LoginAction::Send(setup)]
354353}
355354
356−#[allow(dead_code)]
357355fn spawn_ssh_io(
358356 app: Arc<Mutex<AppState>>,
359357 config: Arc<Mutex<InstanceConfig>>,
src/vm.rs+18−176
@@ -1,8 +1,6 @@
11use crate::session_manager::{GLOBAL_CACHE_DIR_NAME, INSTANCE_DIR_NAME};
22use std::{
3− env,
4− ffi::OsString,
5− fs,
3+ env, fs,
64 io::{self, Write},
75 os::{
86 fd::RawFd,
@@ -25,7 +23,6 @@ use std::{
2523
2624use block2::RcBlock;
2725use dispatch2::DispatchQueue;
28−use lexopt::prelude::*;
2926use objc2::{AnyThread, rc::Retained, runtime::ProtocolObject};
3027use objc2_foundation::*;
3128use objc2_virtualization::*;
@@ -40,7 +37,6 @@ const DEFAULT_CPU_COUNT: usize = 2;
4037const DEFAULT_RAM_MB: u64 = 2048;
4138const DEFAULT_RAM_BYTES: u64 = DEFAULT_RAM_MB * BYTES_PER_MB;
4239const START_TIMEOUT: Duration = Duration::from_secs(60);
43−const DEFAULT_EXPECT_TIMEOUT: Duration = Duration::from_secs(30);
4440const LOGIN_EXPECT_TIMEOUT: Duration = Duration::from_secs(120);
4541const PROVISION_SCRIPT: &str = include_str!("provision.sh");
4642const PROVISION_SCRIPT_NAME: &str = "provision.sh";
@@ -121,7 +117,14 @@ impl DirectoryShare {
121117 }
122118}
123119
124−pub fn run_with_args<F>(args: CliArgs, io_handler: F) -> Result<(), Box<dyn std::error::Error>>
120+pub struct VmArg {
121+ pub cpu_count: usize,
122+ pub ram_bytes: u64,
123+ pub no_default_mounts: bool,
124+ pub mounts: Vec<String>,
125+}
126+
127+pub fn run_with_args<F>(args: VmArg, io_handler: F) -> Result<(), Box<dyn std::error::Error>>
125128where
126129 F: FnOnce(Arc<OutputMonitor>, OwnedFd, OwnedFd) -> IoContext,
127130{
@@ -129,7 +132,7 @@ where
129132}
130133
131134pub(crate) fn run_with_args_and_extras<F>(
132− args: CliArgs,
135+ args: VmArg,
133136 io_handler: F,
134137 extra_login_actions: Vec<LoginAction>,
135138 extra_directory_shares: Vec<DirectoryShare>,
@@ -172,22 +175,14 @@ where
172175 let mise_directory_share =
173176 DirectoryShare::new(guest_mise_cache, "/root/.local/share/mise".into(), false)?;
174177
175− let disk_path = if let Some(path) = args.disk {
176− if !path.exists() {
177− return Err(format!("Disk image does not exist: {}", path.display()).into());
178− }
179− path
180− } else {
181− ensure_default_image(
182− &base_raw,
183− &base_compressed,
184− &default_raw,
185− std::slice::from_ref(&mise_directory_share),
186− )?;
187− ensure_instance_disk(&instance_raw, &default_raw)?;
188−
189− instance_raw
190− };
178+ ensure_default_image(
179+ &base_raw,
180+ &base_compressed,
181+ &default_raw,
182+ std::slice::from_ref(&mise_directory_share),
183+ )?;
184+ ensure_instance_disk(&instance_raw, &default_raw)?;
185+ let disk_path = instance_raw;
191186
192187 let mut login_actions = Vec::new();
193188 let mut directory_shares = Vec::new();
@@ -236,9 +231,6 @@ where
236231
237232 login_actions.extend(extra_login_actions);
238233
239− // Any user-provided login actions must come after our system ones
240− login_actions.extend(args.login_actions);
241−
242234 run_vm_with_io(
243235 &disk_path,
244236 &login_actions,
@@ -249,156 +241,6 @@ where
249241 )
250242}
251243
252−pub fn print_help() {
253− println!(
254− "Vibe is a quick way to spin up a Linux virtual machine on Mac to sandbox LLM agents.
255−
256−vibe [OPTIONS] [disk-image.raw]
257−
258−Options
259−
260− --help Print this help message.
261− --version Print the version (commit SHA).
262− --no-default-mounts Disable all default mounts.
263− --mount host-path:guest-path[:read-only | :read-write] Mount `host-path` inside VM at `guest-path`.
264− Defaults to read-write.
265− Errors if host-path does not exist.
266− --cpus <count> Number of virtual CPUs (default {DEFAULT_CPU_COUNT}).
267− --ram <megabytes> RAM size in megabytes (default {DEFAULT_RAM_MB}).
268− --script <path/to/script.sh> Run script in VM.
269− --send <some-command> Type `some-command` followed by newline into the VM.
270− --expect <string> [timeout-seconds] Wait for `string` to appear in console output before executing next `--script` or `--send`.
271− If `string` does not appear within timeout (default 30 seconds), shutdown VM with error.
272−"
273− );
274−}
275−
276−pub fn print_version() {
277− println!("Vibe");
278− println!("https://github.com/lynaghk/vibe/");
279− println!("Git SHA: {}", env!("GIT_SHA"));
280−}
281−
282−pub struct CliArgs {
283− disk: Option<PathBuf>,
284− version: bool,
285− help: bool,
286− no_default_mounts: bool,
287− mounts: Vec<String>,
288− login_actions: Vec<LoginAction>,
289− cpu_count: usize,
290− ram_bytes: u64,
291−}
292−
293−impl CliArgs {
294− pub fn version(&self) -> bool {
295− self.version
296− }
297−
298− pub fn help(&self) -> bool {
299− self.help
300− }
301−
302− pub fn cpu_count(&self) -> usize {
303− self.cpu_count
304− }
305−
306− pub fn ram_bytes(&self) -> u64 {
307− self.ram_bytes
308− }
309−
310− pub fn ram_mb(&self) -> u64 {
311− self.ram_bytes / BYTES_PER_MB
312− }
313−}
314−
315−pub fn parse_cli() -> Result<CliArgs, Box<dyn std::error::Error>> {
316− parse_cli_from(env::args_os())
317−}
318−
319−pub fn parse_cli_from<I>(args: I) -> Result<CliArgs, Box<dyn std::error::Error>>
320−where
321− I: IntoIterator<Item = OsString>,
322−{
323− fn os_to_string(value: OsString, flag: &str) -> Result<String, Box<dyn std::error::Error>> {
324− value
325− .into_string()
326− .map_err(|_| format!("{flag} expects valid UTF-8").into())
327− }
328−
329− let mut parser = lexopt::Parser::from_iter(args);
330− let mut disk = None;
331− let mut version = false;
332− let mut help = false;
333− let mut no_default_mounts = false;
334− let mut mounts = Vec::new();
335− let mut login_actions = Vec::new();
336− let mut script_index = 0;
337− let mut cpu_count = DEFAULT_CPU_COUNT;
338− let mut ram_bytes = DEFAULT_RAM_BYTES;
339−
340− while let Some(arg) = parser.next()? {
341− match arg {
342− Long("version") => version = true,
343− Long("help") | Short('h') => help = true,
344− Long("no-default-mounts") => no_default_mounts = true,
345− Long("cpus") => {
346− let value = os_to_string(parser.value()?, "--cpus")?.parse()?;
347− if value == 0 {
348− return Err("--cpus must be >= 1".into());
349− }
350− cpu_count = value;
351− }
352− Long("ram") => {
353− let value: u64 = os_to_string(parser.value()?, "--ram")?.parse()?;
354− if value == 0 {
355− return Err("--ram must be >= 1".into());
356− }
357− ram_bytes = value * BYTES_PER_MB;
358− }
359− Long("mount") => {
360− mounts.push(os_to_string(parser.value()?, "--mount")?);
361− }
362− Long("script") => {
363− login_actions.push(Script {
364− path: os_to_string(parser.value()?, "--script")?.into(),
365− index: script_index,
366− });
367− script_index += 1;
368− }
369− Long("send") => {
370− login_actions.push(Send(os_to_string(parser.value()?, "--send")?));
371− }
372− Long("expect") => {
373− let text = os_to_string(parser.value()?, "--expect")?;
374− let timeout = match parser.optional_value() {
375− Some(value) => Duration::from_secs(os_to_string(value, "--expect")?.parse()?),
376− None => DEFAULT_EXPECT_TIMEOUT,
377− };
378− login_actions.push(Expect { text, timeout });
379− }
380− Value(value) => {
381− if disk.is_some() {
382− return Err("Only one disk path may be provided".into());
383− }
384− disk = Some(PathBuf::from(value));
385− }
386− _ => return Err(arg.unexpected().into()),
387− }
388− }
389−
390− Ok(CliArgs {
391− disk,
392− version,
393− help,
394− no_default_mounts,
395− mounts,
396− login_actions,
397− cpu_count,
398− ram_bytes,
399− })
400−}
401−
402244fn script_command_from_path(
403245 path: &Path,
404246 index: usize,
src/vm_manager.rs+4−4
@@ -91,7 +91,7 @@ pub fn ensure_manager(
9191}
9292
9393pub fn run_manager(
94− args: vm::CliArgs,
94+ args: vm::VmArg,
9595 auto_shutdown_ms: u64,
9696) -> Result<(), Box<dyn std::error::Error>> {
9797 let project_root = env::current_dir()?;
@@ -392,7 +392,7 @@ struct ManagerOptions {
392392trait VmExecutor {
393393 fn run_vm(
394394 &self,
395− args: vm::CliArgs,
395+ args: vm::VmArg,
396396 extra_login_actions: Vec<LoginAction>,
397397 extra_shares: Vec<DirectoryShare>,
398398 config: Arc<Mutex<InstanceConfig>>,
@@ -406,7 +406,7 @@ struct RealVmExecutor;
406406impl VmExecutor for RealVmExecutor {
407407 fn run_vm(
408408 &self,
409− args: vm::CliArgs,
409+ args: vm::VmArg,
410410 extra_login_actions: Vec<LoginAction>,
411411 extra_shares: Vec<DirectoryShare>,
412412 config: Arc<Mutex<InstanceConfig>>,
@@ -435,7 +435,7 @@ impl VmExecutor for RealVmExecutor {
435435
436436fn run_manager_with(
437437 project_root: &Path,
438− args: vm::CliArgs,
438+ args: vm::VmArg,
439439 auto_shutdown_ms: u64,
440440 executor: &dyn VmExecutor,
441441 options: ManagerOptions,