mirror of
https://github.com/robcholz/vibebox.git
synced 2026-07-01 12:15:30 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1201c311e0 | |||
| 8669deb078 | |||
| 4d1529905e | |||
| a568295bd3 | |||
| b5cd1f2064 |
Generated
+1
-1
@@ -1303,7 +1303,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vibebox"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"block2",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vibebox"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
authors = ["Finn Sheng"]
|
||||
description = "Ultrafast CLI on Apple Silicon macOS for fast, sandboxed development and LLM agents."
|
||||
|
||||
+8
-2
@@ -120,9 +120,15 @@ fn main() -> Result<()> {
|
||||
tracing::debug!(auto_shutdown_ms, "auto shutdown config");
|
||||
let manager_conn =
|
||||
vm_manager::ensure_manager(&raw_args, auto_shutdown_ms, config_override.as_deref())
|
||||
.map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
|
||||
.map_err(|err| {
|
||||
tracing::error!(error = %err, "failed to ensure vm manager");
|
||||
color_eyre::eyre::eyre!(err.to_string())
|
||||
})?;
|
||||
|
||||
instance::run_with_ssh(manager_conn).map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
|
||||
instance::run_with_ssh(manager_conn).map_err(|err| {
|
||||
tracing::error!(error = %err, "failed to ensure vm manager");
|
||||
color_eyre::eyre::eyre!(err.to_string())
|
||||
})?;
|
||||
|
||||
tracing::info!("See you again — keep vibecoding (no SEVs, only vibes) 😈");
|
||||
|
||||
|
||||
@@ -266,6 +266,11 @@ fn wait_for_vm_ipv4(
|
||||
Ok(status) => {
|
||||
status_missing = false;
|
||||
let status = status.trim().to_string();
|
||||
if status.starts_with("error:") {
|
||||
let _ = fs::remove_file(&status_path);
|
||||
let message = status.trim_start_matches("error:").trim().to_string();
|
||||
return Err(message.into());
|
||||
}
|
||||
if !status.is_empty() && last_status.as_deref() != Some(status.as_str()) {
|
||||
tracing::info!("[background]: {}", status);
|
||||
last_status = Some(status);
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -eEux
|
||||
|
||||
trap 'echo "[vibebox][error] provisioning failed"; echo "VIBEBOX_PROVISION_FAILED"; systemctl poweroff || true; exit 1' ERR
|
||||
trap 'rc=$?; echo "[vibebox][error] provisioning failed at: ${BASH_COMMAND} (exit ${rc})"; printf "%s%s\n" VIBEBOX_PROVISION_ FAILED; systemctl poweroff || true; exit 1' ERR
|
||||
|
||||
# Wait for network + DNS before apt-get to avoid early boot flakiness.
|
||||
wait_for_network() {
|
||||
echo "[vibebox] waiting for network/DNS readiness"
|
||||
local deadline=$((SECONDS + 60))
|
||||
local deadline=$((SECONDS + 180))
|
||||
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||
local has_route=0
|
||||
if ip -4 route show default >/dev/null 2>&1; then
|
||||
@@ -21,7 +21,7 @@ wait_for_network() {
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "[vibebox][warn] network/DNS still not ready after 60s; continuing" >&2
|
||||
echo "[vibebox][warn] network/DNS still not ready after 180s; continuing" >&2
|
||||
echo "[vibebox][warn] /etc/resolv.conf:" >&2
|
||||
cat /etc/resolv.conf >&2 || true
|
||||
ip -br addr >&2 || true
|
||||
@@ -44,9 +44,9 @@ apt_update_with_retries() {
|
||||
}
|
||||
|
||||
# Don't wait too long for slow mirrors.
|
||||
echo 'Acquire::http::Timeout "2";' | tee /etc/apt/apt.conf.d/99timeout
|
||||
echo 'Acquire::https::Timeout "2";' | tee -a /etc/apt/apt.conf.d/99timeout
|
||||
echo 'Acquire::Retries "2";' | tee -a /etc/apt/apt.conf.d/99timeout
|
||||
echo 'Acquire::http::Timeout "10";' | tee /etc/apt/apt.conf.d/99timeout
|
||||
echo 'Acquire::https::Timeout "10";' | tee -a /etc/apt/apt.conf.d/99timeout
|
||||
echo 'Acquire::Retries "5";' | tee -a /etc/apt/apt.conf.d/99timeout
|
||||
|
||||
wait_for_network
|
||||
apt_update_with_retries
|
||||
@@ -98,5 +98,5 @@ sleep 100 # sleep here so that we don't see the login screen flash up before the
|
||||
EOF
|
||||
|
||||
# Done provisioning, power off the VM
|
||||
echo "VIBEBOX_PROVISION_OK"
|
||||
printf "%s%s\n" VIBEBOX_PROVISION_ OK
|
||||
systemctl poweroff
|
||||
|
||||
@@ -209,8 +209,10 @@ where
|
||||
let guest_mise_cache = cache_dir.join(".guest-mise-cache");
|
||||
|
||||
let instance_dir = project_root.join(INSTANCE_DIR_NAME);
|
||||
fs::create_dir_all(&instance_dir)?;
|
||||
let status_file = StatusFile::new(instance_dir.join(STATUS_FILE_NAME));
|
||||
status_file.update("preparing VM image...");
|
||||
let provision_log = instance_dir.join("provision.log");
|
||||
|
||||
let basename_compressed = DEBIAN_COMPRESSED_DISK_URL.rsplit('/').next().unwrap();
|
||||
let base_compressed = cache_dir.join(basename_compressed);
|
||||
@@ -235,6 +237,7 @@ where
|
||||
&default_raw,
|
||||
std::slice::from_ref(&mise_directory_share),
|
||||
Some(&status_file),
|
||||
Some(&provision_log),
|
||||
)?;
|
||||
let _ = ensure_instance_disk(
|
||||
&instance_raw,
|
||||
@@ -583,6 +586,7 @@ fn ensure_default_image(
|
||||
default_raw: &Path,
|
||||
directory_shares: &[DirectoryShare],
|
||||
status: Option<&StatusFile>,
|
||||
provision_log: Option<&Path>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if default_raw.exists() {
|
||||
return Ok(());
|
||||
@@ -605,14 +609,31 @@ fn ensure_default_image(
|
||||
timeout: PROVISION_EXPECT_TIMEOUT,
|
||||
},
|
||||
];
|
||||
if let Err(err) = run_vm(
|
||||
default_raw,
|
||||
&provision_actions,
|
||||
directory_shares,
|
||||
DEFAULT_CPU_COUNT,
|
||||
DEFAULT_RAM_BYTES,
|
||||
None,
|
||||
) {
|
||||
let provision_result = if let Some(log_path) = provision_log {
|
||||
let log_path = log_path.to_path_buf();
|
||||
run_vm_with_io(
|
||||
default_raw,
|
||||
&provision_actions,
|
||||
directory_shares,
|
||||
DEFAULT_CPU_COUNT,
|
||||
DEFAULT_RAM_BYTES,
|
||||
None,
|
||||
move |output_monitor, vm_output_fd, vm_input_fd| {
|
||||
spawn_vm_io_with_log(output_monitor, vm_output_fd, vm_input_fd, log_path)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
run_vm(
|
||||
default_raw,
|
||||
&provision_actions,
|
||||
directory_shares,
|
||||
DEFAULT_CPU_COUNT,
|
||||
DEFAULT_RAM_BYTES,
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
if let Err(err) = provision_result {
|
||||
let _ = fs::remove_file(default_raw);
|
||||
return Err(err);
|
||||
}
|
||||
@@ -915,6 +936,36 @@ pub fn spawn_vm_io(
|
||||
spawn_vm_io_with_line_handler(output_monitor, vm_output_fd, vm_input_fd, |_| false)
|
||||
}
|
||||
|
||||
fn spawn_vm_io_with_log(
|
||||
output_monitor: Arc<OutputMonitor>,
|
||||
vm_output_fd: OwnedFd,
|
||||
vm_input_fd: OwnedFd,
|
||||
log_path: PathBuf,
|
||||
) -> IoContext {
|
||||
let log_file = fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&log_path)
|
||||
.ok()
|
||||
.map(|file| Arc::new(Mutex::new(file)));
|
||||
|
||||
spawn_vm_io_with_hooks(
|
||||
output_monitor,
|
||||
vm_output_fd,
|
||||
vm_input_fd,
|
||||
IoControl::new(),
|
||||
|_| false,
|
||||
move |bytes| {
|
||||
if let Some(log) = &log_file
|
||||
&& let Ok(mut file) = log.lock()
|
||||
{
|
||||
let _ = file.write_all(bytes);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
impl IoContext {
|
||||
pub fn shutdown(self) {
|
||||
let _ = self.input_tx.send(VmInput::Shutdown);
|
||||
|
||||
@@ -17,6 +17,7 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
config::CONFIG_PATH_ENV,
|
||||
instance::STATUS_FILE_NAME,
|
||||
instance::VM_ROOT_LOG_NAME,
|
||||
instance::{
|
||||
DEFAULT_SSH_USER, InstanceConfig, build_ssh_login_actions, ensure_instance_dir,
|
||||
@@ -765,6 +766,10 @@ fn run_manager_with(
|
||||
);
|
||||
tracing::info!("vm manager vm run completed");
|
||||
let vm_err = vm_result.err().map(|e| e.to_string());
|
||||
if let Some(err) = &vm_err {
|
||||
let status_path = instance_dir.join(STATUS_FILE_NAME);
|
||||
let _ = fs::write(&status_path, format!("error: {err}"));
|
||||
}
|
||||
let _ = event_tx.send(ManagerEvent::VmExited(vm_err.clone()));
|
||||
let event_loop_result: Result<(), String> = event_loop_handle
|
||||
.join()
|
||||
|
||||
Reference in New Issue
Block a user