on main help sign in
refactor: remove old cli.
8c816c3robcholz
docs/tasks.md+1−1
@@ -44,7 +44,7 @@
447. [x] setup vibebox commands
447. [x] setup vibebox commands
458. [ ] setup cli commands.
458. [ ] setup cli commands.
46 1. [x] Organize all the params.
46 1. [x] Organize all the params.
47− 2. [ ] Remove old cli.
47+ 2. [x] Remove old cli.
48 3. [ ] add an actual config file.
48 3. [ ] add an actual config file.
49 4. [ ] set up the cli.
49 4. [ ] set up the cli.
509. [ ] fix ui overlap, and consistency issue.
509. [ ] fix ui overlap, and consistency issue.
src/bin/vibebox-cli.rs+17−14
@@ -21,7 +21,13 @@ fn main() -> Result<()> {
21
21
22 if env::var("VIBEBOX_VM_MANAGER").as_deref() == Ok("1") {
22 if env::var("VIBEBOX_VM_MANAGER").as_deref() == Ok("1") {
23 tracing::info!("starting vm manager mode");
23 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+ };
25 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
31 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
26 .ok()
32 .ok()
27 .and_then(|value| value.parse::<u64>().ok())
33 .and_then(|value| value.parse::<u64>().ok())
@@ -34,23 +40,20 @@ fn main() -> Result<()> {
34 return Ok(());
40 return Ok(());
35 }
41 }
36
42
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−
48 vm::ensure_signed();
43 vm::ensure_signed();
49
44
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+
50 let vm_info = VmInfo {
53 let vm_info = VmInfo {
51 version: env!("CARGO_PKG_VERSION").to_string(),
54 version: env!("CARGO_PKG_VERSION").to_string(),
52− max_memory_mb: args.ram_mb(),
55+ max_memory_mb: vm_args.ram_bytes / (1024 * 1024),
53− cpu_cores: args.cpu_count(),
56+ cpu_cores: vm_args.cpu_count,
54 };
57 };
55 let cwd = env::current_dir().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
58 let cwd = env::current_dir().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
56 tracing::info!(cwd = %cwd.display(), "starting vibebox cli");
59 tracing::info!(cwd = %cwd.display(), "starting vibebox cli");
src/bin/vibebox-supervisor.rs+7−1
@@ -19,7 +19,13 @@ fn main() -> Result<()> {
19 let instance_dir = instance::ensure_instance_dir(&cwd)
19 let instance_dir = instance::ensure_instance_dir(&cwd)
20 .map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
20 .map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
21 let _ = instance::touch_last_active(&instance_dir);
21 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+ };
23 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
29 let auto_shutdown_ms = env::var("VIBEBOX_AUTO_SHUTDOWN_MS")
24 .ok()
30 .ok()
25 .and_then(|value| value.parse::<u64>().ok())
31 .and_then(|value| value.parse::<u64>().ok())
src/instance.rs−2
@@ -26,7 +26,6 @@ use crate::{
26};
26};
27
27
28const SSH_KEY_NAME: &str = "ssh_key";
28const SSH_KEY_NAME: &str = "ssh_key";
29−#[allow(dead_code)]
30pub(crate) const SERIAL_LOG_NAME: &str = "serial.log";
29pub(crate) const SERIAL_LOG_NAME: &str = "serial.log";
31const DEFAULT_SSH_USER: &str = "vibecoder";
30const DEFAULT_SSH_USER: &str = "vibecoder";
32const SSH_CONNECT_RETRIES: usize = 30;
31const SSH_CONNECT_RETRIES: usize = 30;
@@ -353,7 +352,6 @@ pub(crate) fn build_ssh_login_actions(
353 vec![LoginAction::Send(setup)]
352 vec![LoginAction::Send(setup)]
354}
353}
355
354
356−#[allow(dead_code)]
357fn spawn_ssh_io(
355fn spawn_ssh_io(
358 app: Arc<Mutex<AppState>>,
356 app: Arc<Mutex<AppState>>,
359 config: Arc<Mutex<InstanceConfig>>,
357 config: Arc<Mutex<InstanceConfig>>,
src/vm.rs+18−176
@@ -1,8 +1,6 @@
1use crate::session_manager::{GLOBAL_CACHE_DIR_NAME, INSTANCE_DIR_NAME};
1use crate::session_manager::{GLOBAL_CACHE_DIR_NAME, INSTANCE_DIR_NAME};
2use std::{
2use std::{
3− env,
3+ env, fs,
4− ffi::OsString,
5− fs,
6 io::{self, Write},
4 io::{self, Write},
7 os::{
5 os::{
8 fd::RawFd,
6 fd::RawFd,
@@ -25,7 +23,6 @@ use std::{
25
23
26use block2::RcBlock;
24use block2::RcBlock;
27use dispatch2::DispatchQueue;
25use dispatch2::DispatchQueue;
28−use lexopt::prelude::*;
29use objc2::{AnyThread, rc::Retained, runtime::ProtocolObject};
26use objc2::{AnyThread, rc::Retained, runtime::ProtocolObject};
30use objc2_foundation::*;
27use objc2_foundation::*;
31use objc2_virtualization::*;
28use objc2_virtualization::*;
@@ -40,7 +37,6 @@ const DEFAULT_CPU_COUNT: usize = 2;
40const DEFAULT_RAM_MB: u64 = 2048;
37const DEFAULT_RAM_MB: u64 = 2048;
41const DEFAULT_RAM_BYTES: u64 = DEFAULT_RAM_MB * BYTES_PER_MB;
38const DEFAULT_RAM_BYTES: u64 = DEFAULT_RAM_MB * BYTES_PER_MB;
42const START_TIMEOUT: Duration = Duration::from_secs(60);
39const START_TIMEOUT: Duration = Duration::from_secs(60);
43−const DEFAULT_EXPECT_TIMEOUT: Duration = Duration::from_secs(30);
44const LOGIN_EXPECT_TIMEOUT: Duration = Duration::from_secs(120);
40const LOGIN_EXPECT_TIMEOUT: Duration = Duration::from_secs(120);
45const PROVISION_SCRIPT: &str = include_str!("provision.sh");
41const PROVISION_SCRIPT: &str = include_str!("provision.sh");
46const PROVISION_SCRIPT_NAME: &str = "provision.sh";
42const PROVISION_SCRIPT_NAME: &str = "provision.sh";
@@ -121,7 +117,14 @@ impl DirectoryShare {
121 }
117 }
122}
118}
123
119
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+
124−pub fn run_with_args<F>(args: CliArgs, io_handler: F) -> Result<(), Box<dyn std::error::Error>>
127+pub fn run_with_args<F>(args: VmArg, io_handler: F) -> Result<(), Box<dyn std::error::Error>>
125where
128where
126 F: FnOnce(Arc<OutputMonitor>, OwnedFd, OwnedFd) -> IoContext,
129 F: FnOnce(Arc<OutputMonitor>, OwnedFd, OwnedFd) -> IoContext,
127{
130{
@@ -129,7 +132,7 @@ where
129}
132}
130
133
131pub(crate) fn run_with_args_and_extras<F>(
134pub(crate) fn run_with_args_and_extras<F>(
132− args: CliArgs,
135+ args: VmArg,
133 io_handler: F,
136 io_handler: F,
134 extra_login_actions: Vec<LoginAction>,
137 extra_login_actions: Vec<LoginAction>,
135 extra_directory_shares: Vec<DirectoryShare>,
138 extra_directory_shares: Vec<DirectoryShare>,
@@ -172,22 +175,14 @@ where
172 let mise_directory_share =
175 let mise_directory_share =
173 DirectoryShare::new(guest_mise_cache, "/root/.local/share/mise".into(), false)?;
176 DirectoryShare::new(guest_mise_cache, "/root/.local/share/mise".into(), false)?;
174
177
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(
178+ ensure_default_image(
182− &base_raw,
179+ &base_raw,
183− &base_compressed,
180+ &base_compressed,
184− &default_raw,
181+ &default_raw,
185− std::slice::from_ref(&mise_directory_share),
182+ std::slice::from_ref(&mise_directory_share),
186− )?;
183+ )?;
187− ensure_instance_disk(&instance_raw, &default_raw)?;
184+ ensure_instance_disk(&instance_raw, &default_raw)?;
188−
189− instance_raw
185+ let disk_path = instance_raw;
190− };
191
186
192 let mut login_actions = Vec::new();
187 let mut login_actions = Vec::new();
193 let mut directory_shares = Vec::new();
188 let mut directory_shares = Vec::new();
@@ -236,9 +231,6 @@ where
236
231
237 login_actions.extend(extra_login_actions);
232 login_actions.extend(extra_login_actions);
238
233
239− // Any user-provided login actions must come after our system ones
240− login_actions.extend(args.login_actions);
241−
242 run_vm_with_io(
234 run_vm_with_io(
243 &disk_path,
235 &disk_path,
244 &login_actions,
236 &login_actions,
@@ -249,156 +241,6 @@ where
249 )
241 )
250}
242}
251
243
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−
402fn script_command_from_path(
244fn script_command_from_path(
403 path: &Path,
245 path: &Path,
404 index: usize,
246 index: usize,
src/vm_manager.rs+4−4
@@ -91,7 +91,7 @@ pub fn ensure_manager(
91}
91}
92
92
93pub fn run_manager(
93pub fn run_manager(
94− args: vm::CliArgs,
94+ args: vm::VmArg,
95 auto_shutdown_ms: u64,
95 auto_shutdown_ms: u64,
96) -> Result<(), Box<dyn std::error::Error>> {
96) -> Result<(), Box<dyn std::error::Error>> {
97 let project_root = env::current_dir()?;
97 let project_root = env::current_dir()?;
@@ -392,7 +392,7 @@ struct ManagerOptions {
392trait VmExecutor {
392trait VmExecutor {
393 fn run_vm(
393 fn run_vm(
394 &self,
394 &self,
395− args: vm::CliArgs,
395+ args: vm::VmArg,
396 extra_login_actions: Vec<LoginAction>,
396 extra_login_actions: Vec<LoginAction>,
397 extra_shares: Vec<DirectoryShare>,
397 extra_shares: Vec<DirectoryShare>,
398 config: Arc<Mutex<InstanceConfig>>,
398 config: Arc<Mutex<InstanceConfig>>,
@@ -406,7 +406,7 @@ struct RealVmExecutor;
406impl VmExecutor for RealVmExecutor {
406impl VmExecutor for RealVmExecutor {
407 fn run_vm(
407 fn run_vm(
408 &self,
408 &self,
409− args: vm::CliArgs,
409+ args: vm::VmArg,
410 extra_login_actions: Vec<LoginAction>,
410 extra_login_actions: Vec<LoginAction>,
411 extra_shares: Vec<DirectoryShare>,
411 extra_shares: Vec<DirectoryShare>,
412 config: Arc<Mutex<InstanceConfig>>,
412 config: Arc<Mutex<InstanceConfig>>,
@@ -435,7 +435,7 @@ impl VmExecutor for RealVmExecutor {
435
435
436fn run_manager_with(
436fn run_manager_with(
437 project_root: &Path,
437 project_root: &Path,
438− args: vm::CliArgs,
438+ args: vm::VmArg,
439 auto_shutdown_ms: u64,
439 auto_shutdown_ms: u64,
440 executor: &dyn VmExecutor,
440 executor: &dyn VmExecutor,
441 options: ManagerOptions,
441 options: ManagerOptions,