mirror of
https://github.com/robcholz/vibebox.git
synced 2026-07-01 12:15:30 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1d484ee9d | |||
| 1201c311e0 | |||
| 8669deb078 |
Generated
+1
-1
@@ -1303,7 +1303,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vibebox"
|
name = "vibebox"
|
||||||
version = "0.2.5"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"block2",
|
"block2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vibebox"
|
name = "vibebox"
|
||||||
version = "0.2.5"
|
version = "0.3.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
authors = ["Finn Sheng"]
|
authors = ["Finn Sheng"]
|
||||||
description = "Ultrafast CLI on Apple Silicon macOS for fast, sandboxed development and LLM agents."
|
description = "Ultrafast CLI on Apple Silicon macOS for fast, sandboxed development and LLM agents."
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eEux
|
set -eEux
|
||||||
|
|
||||||
trap 'rc=$?; echo "[vibebox][error] provisioning failed at: ${BASH_COMMAND} (exit ${rc})"; 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 + DNS before apt-get to avoid early boot flakiness.
|
||||||
wait_for_network() {
|
wait_for_network() {
|
||||||
@@ -98,5 +98,5 @@ sleep 100 # sleep here so that we don't see the login screen flash up before the
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Done provisioning, power off the VM
|
# Done provisioning, power off the VM
|
||||||
echo "VIBEBOX_PROVISION_OK"
|
printf "%s%s\n" VIBEBOX_PROVISION_ OK
|
||||||
systemctl poweroff
|
systemctl poweroff
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
SSH_USER="__SSH_USER__"
|
SSH_USER="__SSH_USER__"
|
||||||
|
SUDO_PASSWORD="__SUDO_PASSWORD__"
|
||||||
PROJECT_NAME="__PROJECT_NAME__"
|
PROJECT_NAME="__PROJECT_NAME__"
|
||||||
PROJECT_GUEST_DIR="__PROJECT_GUEST_DIR__"
|
PROJECT_GUEST_DIR="__PROJECT_GUEST_DIR__"
|
||||||
KEY_PATH="__KEY_PATH__"
|
KEY_PATH="__KEY_PATH__"
|
||||||
@@ -61,6 +62,10 @@ if ! id -u "$SSH_USER" >/dev/null 2>&1; then
|
|||||||
usermod -aG sudo "$SSH_USER" || true
|
usermod -aG sudo "$SSH_USER" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SUDO_PASSWORD" ]; then
|
||||||
|
echo "${SSH_USER}:${SUDO_PASSWORD}" | chpasswd
|
||||||
|
fi
|
||||||
|
|
||||||
install -d -m 700 -o "$SSH_USER" -g "$SSH_USER" "/home/${SSH_USER}/.ssh"
|
install -d -m 700 -o "$SSH_USER" -g "$SSH_USER" "/home/${SSH_USER}/.ssh"
|
||||||
install -m 600 -o "$SSH_USER" -g "$SSH_USER" "$KEY_PATH" "/home/${SSH_USER}/.ssh/authorized_keys"
|
install -m 600 -o "$SSH_USER" -g "$SSH_USER" "$KEY_PATH" "/home/${SSH_USER}/.ssh/authorized_keys"
|
||||||
|
|
||||||
|
|||||||
@@ -209,8 +209,10 @@ where
|
|||||||
let guest_mise_cache = cache_dir.join(".guest-mise-cache");
|
let guest_mise_cache = cache_dir.join(".guest-mise-cache");
|
||||||
|
|
||||||
let instance_dir = project_root.join(INSTANCE_DIR_NAME);
|
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));
|
let status_file = StatusFile::new(instance_dir.join(STATUS_FILE_NAME));
|
||||||
status_file.update("preparing VM image...");
|
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 basename_compressed = DEBIAN_COMPRESSED_DISK_URL.rsplit('/').next().unwrap();
|
||||||
let base_compressed = cache_dir.join(basename_compressed);
|
let base_compressed = cache_dir.join(basename_compressed);
|
||||||
@@ -235,6 +237,7 @@ where
|
|||||||
&default_raw,
|
&default_raw,
|
||||||
std::slice::from_ref(&mise_directory_share),
|
std::slice::from_ref(&mise_directory_share),
|
||||||
Some(&status_file),
|
Some(&status_file),
|
||||||
|
Some(&provision_log),
|
||||||
)?;
|
)?;
|
||||||
let _ = ensure_instance_disk(
|
let _ = ensure_instance_disk(
|
||||||
&instance_raw,
|
&instance_raw,
|
||||||
@@ -583,6 +586,7 @@ fn ensure_default_image(
|
|||||||
default_raw: &Path,
|
default_raw: &Path,
|
||||||
directory_shares: &[DirectoryShare],
|
directory_shares: &[DirectoryShare],
|
||||||
status: Option<&StatusFile>,
|
status: Option<&StatusFile>,
|
||||||
|
provision_log: Option<&Path>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if default_raw.exists() {
|
if default_raw.exists() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -605,14 +609,31 @@ fn ensure_default_image(
|
|||||||
timeout: PROVISION_EXPECT_TIMEOUT,
|
timeout: PROVISION_EXPECT_TIMEOUT,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if let Err(err) = run_vm(
|
let provision_result = if let Some(log_path) = provision_log {
|
||||||
default_raw,
|
let log_path = log_path.to_path_buf();
|
||||||
&provision_actions,
|
run_vm_with_io(
|
||||||
directory_shares,
|
default_raw,
|
||||||
DEFAULT_CPU_COUNT,
|
&provision_actions,
|
||||||
DEFAULT_RAM_BYTES,
|
directory_shares,
|
||||||
None,
|
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);
|
let _ = fs::remove_file(default_raw);
|
||||||
return Err(err);
|
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)
|
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 {
|
impl IoContext {
|
||||||
pub fn shutdown(self) {
|
pub fn shutdown(self) {
|
||||||
let _ = self.input_tx.send(VmInput::Shutdown);
|
let _ = self.input_tx.send(VmInput::Shutdown);
|
||||||
|
|||||||
Reference in New Issue
Block a user