From db3a4981336e667a306c117537d3bb3a3ef4d2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wo=C5=9B?= Date: Fri, 24 Apr 2026 17:14:09 +0900 Subject: [PATCH] feat(cli): wire microsandbox into the terminal backend wizard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the Microsandbox choice to `hermes setup terminal` on Linux and macOS (Apple Silicon). Gates the option by host — hosts without KVM/libkrun support don't see it in the menu. When selected: - Verifies `msb` is on PATH or at MSB_PATH; prints install hint otherwise. - On Linux, checks /dev/kvm is readable/writable and prints the kvm-group fix if not. - Prompts for the OCI image (same default as the other container backends). - Reuses _prompt_container_resources for CPU/memory/disk. Depends on the backend added in the preceding commit. --- hermes_cli/setup.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index ebc7de940..d04b9fe5f 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -1174,6 +1174,8 @@ def setup_terminal_backend(config: dict): current_backend = config.get("terminal", {}).get("backend", "local") is_linux = _platform.system() == "Linux" + is_macos_arm = _platform.system() == "Darwin" and _platform.machine() == "arm64" + microsandbox_supported = is_linux or is_macos_arm # Build backend choices with descriptions terminal_choices = [ @@ -1193,6 +1195,14 @@ def setup_terminal_backend(config: dict): backend_to_idx["singularity"] = next_idx next_idx += 1 + if microsandbox_supported: + terminal_choices.append( + "Microsandbox - libkrun microVM (strong isolation, local)" + ) + idx_to_backend[next_idx] = "microsandbox" + backend_to_idx["microsandbox"] = next_idx + next_idx += 1 + # Add keep current option keep_current_idx = next_idx terminal_choices.append(f"Keep current ({current_backend})") @@ -1441,6 +1451,40 @@ def setup_terminal_backend(config: dict): _prompt_container_resources(config) + elif selected_backend == "microsandbox": + print_success("Terminal backend: Microsandbox") + print_info("Runs commands in a libkrun microVM with its own kernel.") + print_info("Stronger isolation than containers; no cloud dependency.") + + # Check if msb is available + msb_bin = os.environ.get("MSB_PATH") or shutil.which("msb") + if not msb_bin or not os.access(msb_bin, os.X_OK): + print_warning("msb not found on PATH or at MSB_PATH!") + print_info("Install: curl -fsSL https://install.microsandbox.dev | sh") + else: + print_info(f"msb found: {msb_bin}") + + # KVM check on Linux + if _platform.system() == "Linux": + if not os.access("/dev/kvm", os.R_OK | os.W_OK): + print_warning("/dev/kvm is not readable/writable by this user!") + print_info( + "Add your user to the 'kvm' group (Linux), then log out/in: " + "sudo usermod -aG kvm $USER" + ) + else: + print_info("KVM access: ok") + + # Microsandbox image (OCI) + current_image = config.get("terminal", {}).get( + "microsandbox_image", "nikolaik/python-nodejs:python3.11-nodejs20" + ) + image = prompt(" Microsandbox image (OCI)", current_image) + config["terminal"]["microsandbox_image"] = image + save_env_value("TERMINAL_MICROSANDBOX_IMAGE", image) + + _prompt_container_resources(config) + elif selected_backend == "ssh": print_success("Terminal backend: SSH") print_info("Run commands on a remote machine via SSH.")