From b34241a2a0d3679bf834f25f6c42c4bba3c8f072 Mon Sep 17 00:00:00 2001 From: Juan Diaz Date: Mon, 20 Jul 2026 21:34:53 +0000 Subject: [PATCH 1/5] Update SmolVM to 0.0.28 --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bb1848a..b5cc67c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.12" license = "Apache-2.0" authors = [{ name = "sbx contributors" }] dependencies = [ - "smolvm==0.0.26", + "smolvm==0.0.28", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index 0fa2b48..ad0d830 100644 --- a/uv.lock +++ b/uv.lock @@ -839,13 +839,13 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.1.1" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.1.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.21" }, - { name = "smolvm", specifier = "==0.0.26" }, + { name = "smolvm", specifier = "==0.0.28" }, ] provides-extras = ["dev"] [[package]] name = "smolvm" -version = "0.0.26" +version = "0.0.28" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -857,9 +857,9 @@ dependencies = [ { name = "smolvm-core" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/ec/f1490b80821c3fee9a95bba4edbff068008ea407687fbd3b80f47b1270dd/smolvm-0.0.26.tar.gz", hash = "sha256:07a3e9b41d8935c5e9b2967057dcb53fc21e2adb78ad9ce11c1c949dbe29300d", size = 748170, upload-time = "2026-07-09T09:06:24.023Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/06/9ba5693ce90463b60ba471467500304240a2cb6664b47083ff7fb1889dfa/smolvm-0.0.28.tar.gz", hash = "sha256:e17311a11eb9ddcbb9588053f7f92c7646b6bd31dbe079cbbd75a5f7041f9a9a", size = 792713, upload-time = "2026-07-20T14:41:20.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/27/57965b42a93dd3b3da6e059c670788d1f04a15e57b11509df595479fdacb/smolvm-0.0.26-py3-none-any.whl", hash = "sha256:80bd27fd19611532569f361c73c46bb0cde61ee7482629ec175bd90b49a7e6f4", size = 377072, upload-time = "2026-07-09T09:06:22.479Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6f/692f9f989c75445a0cfa9055122f9d6162f1e5695881d5d4e87525beb4bf/smolvm-0.0.28-py3-none-any.whl", hash = "sha256:28ecf7b2dae73558005745a91115bb2ca890e6c0cb6057a9d9d271c00db4655a", size = 431270, upload-time = "2026-07-20T14:41:19.631Z" }, ] [[package]] From f7991e531e0a4edaba4c2310b3c9c18942747afe Mon Sep 17 00:00:00 2001 From: Juan Diaz Date: Mon, 20 Jul 2026 21:35:54 +0000 Subject: [PATCH 2/5] Suppress SmolVM version notices by default --- src/sbx/cli.py | 3 +++ src/sbx/runtime.py | 15 +++++++++++++++ tests/test_cli.py | 37 +++++++++++++++++++++++++++++++++++++ tests/test_runtime.py | 24 ++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 tests/test_runtime.py diff --git a/src/sbx/cli.py b/src/sbx/cli.py index 0857e83..4b89864 100644 --- a/src/sbx/cli.py +++ b/src/sbx/cli.py @@ -29,6 +29,7 @@ ) from sbx.image import build_debian from sbx.runtime import ConfigError as RuntimeConfigError +from sbx.runtime import smolvm_env AGENTS = ("pi", "claude", "codex") MIB = 1024 * 1024 @@ -118,12 +119,14 @@ def _smolvm_argv(args: Sequence[str]) -> list[str]: def _run_smolvm(args: Sequence[str], **kwargs: Any) -> int: + kwargs["env"] = smolvm_env(kwargs.get("env")) return _run(_smolvm_argv(args), **kwargs) def _run_smolvm_capture( args: Sequence[str], **kwargs: Any ) -> subprocess.CompletedProcess[str] | None: + kwargs["env"] = smolvm_env(kwargs.get("env")) return _run_capture(_smolvm_argv(args), **kwargs) diff --git a/src/sbx/runtime.py b/src/sbx/runtime.py index 07b6796..e6c70c7 100644 --- a/src/sbx/runtime.py +++ b/src/sbx/runtime.py @@ -10,6 +10,8 @@ from typing import Any DEBUG = False +SMOLVM_DISABLE_VERSION_CHECK_ENV = "SMOLVM_DISABLE_VERSION_CHECK" +SBX_SMOLVM_VERSION_NOTICES_ENV = "SBX_SMOLVM_VERSION_NOTICES" class ConfigError(ValueError): @@ -92,13 +94,26 @@ def smolvm_argv(args: Sequence[str]) -> list[str]: ] +def env_boolean(value: str | None) -> bool: + return (value or "").strip().lower() in {"1", "true", "yes", "on"} + + +def smolvm_env(env: Mapping[str, str] | None = None) -> dict[str, str]: + result = dict(os.environ if env is None else env) + if not env_boolean(result.get(SBX_SMOLVM_VERSION_NOTICES_ENV)): + result[SMOLVM_DISABLE_VERSION_CHECK_ENV] = "1" + return result + + def run_smolvm(args: Sequence[str], **kwargs: Any) -> int: + kwargs["env"] = smolvm_env(kwargs.get("env")) return run(smolvm_argv(args), **kwargs) def run_smolvm_capture( args: Sequence[str], **kwargs: Any ) -> subprocess.CompletedProcess[str] | None: + kwargs["env"] = smolvm_env(kwargs.get("env")) return run_capture(smolvm_argv(args), **kwargs) diff --git a/tests/test_cli.py b/tests/test_cli.py index 1b7abd1..180bd47 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -44,6 +44,43 @@ def test_smolvm_runner_does_not_need_console_script_on_path() -> None: ] +def test_smolvm_runner_suppresses_upstream_version_notice(monkeypatch: pytest.MonkeyPatch) -> None: + captured: dict[str, object] = {} + + def fake_run(argv: list[str], **kwargs: object) -> int: + captured.update(kwargs) + return 0 + + monkeypatch.delenv("SBX_SMOLVM_VERSION_NOTICES", raising=False) + monkeypatch.setattr(cli, "_run", fake_run) + + assert cli._run_smolvm(["doctor"]) == 0 + + env = captured["env"] + assert isinstance(env, dict) + assert env["SMOLVM_DISABLE_VERSION_CHECK"] == "1" + + +def test_smolvm_runner_allows_upstream_version_notice_when_requested( + monkeypatch: pytest.MonkeyPatch, +) -> None: + captured: dict[str, object] = {} + + def fake_run(argv: list[str], **kwargs: object) -> int: + captured.update(kwargs) + return 0 + + monkeypatch.setenv("SBX_SMOLVM_VERSION_NOTICES", "true") + monkeypatch.delenv("SMOLVM_DISABLE_VERSION_CHECK", raising=False) + monkeypatch.setattr(cli, "_run", fake_run) + + assert cli._run_smolvm(["doctor"]) == 0 + + env = captured["env"] + assert isinstance(env, dict) + assert "SMOLVM_DISABLE_VERSION_CHECK" not in env + + def test_doctor_checks_qemu_by_default( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, diff --git a/tests/test_runtime.py b/tests/test_runtime.py new file mode 100644 index 0000000..f594da7 --- /dev/null +++ b/tests/test_runtime.py @@ -0,0 +1,24 @@ +from sbx.runtime import env_boolean, smolvm_env + + +def test_env_boolean_accepts_common_truthy_values() -> None: + for value in ("1", "true", "TRUE", " yes ", "on"): + assert env_boolean(value) is True + + +def test_env_boolean_rejects_falsey_values() -> None: + for value in (None, "", "0", "false", "no", "off"): + assert env_boolean(value) is False + + +def test_smolvm_env_preserves_custom_env_and_disables_notice_by_default() -> None: + env = smolvm_env({"HOME": "/tmp/home"}) + + assert env["HOME"] == "/tmp/home" + assert env["SMOLVM_DISABLE_VERSION_CHECK"] == "1" + + +def test_smolvm_env_opt_in_leaves_version_check_enabled() -> None: + env = smolvm_env({"SBX_SMOLVM_VERSION_NOTICES": "yes"}) + + assert "SMOLVM_DISABLE_VERSION_CHECK" not in env From 296c30827ed3c7bc50af8a622a8d237014aa9dfc Mon Sep 17 00:00:00 2001 From: Juan Diaz Date: Mon, 20 Jul 2026 21:37:30 +0000 Subject: [PATCH 3/5] Sync guest clock from host after boot --- src/sbx/cli.py | 38 ++++++++++++++++++++++++++++++++++++++ tests/test_cli.py | 8 +++++++- tests/test_cli_extra.py | 26 ++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/sbx/cli.py b/src/sbx/cli.py index 4b89864..933f1df 100644 --- a/src/sbx/cli.py +++ b/src/sbx/cli.py @@ -13,6 +13,7 @@ import tomllib from collections.abc import Iterator, Mapping, Sequence from contextlib import contextmanager, suppress +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -802,6 +803,42 @@ def _install_git_config(vm_id: str, user: str | None, git_config_text: str | Non raise ConfigError(f"failed to install git config: {stderr}") +def _host_timezone() -> str: + zoneinfo = Path("/usr/share/zoneinfo") + try: + target = Path("/etc/localtime").resolve() + return target.relative_to(zoneinfo).as_posix() + except (OSError, ValueError): + pass + try: + timezone = Path("/etc/timezone").read_text(encoding="utf-8").strip() + except OSError: + return "UTC" + return timezone or "UTC" + + +def _sync_guest_clock(vm_id: str) -> None: + timestamp = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") + timezone = _host_timezone() + script = f""" +set -eu +zone={shlex.quote(timezone)} +if [ -f "/usr/share/zoneinfo/$zone" ]; then + ln -sf "/usr/share/zoneinfo/$zone" /etc/localtime + printf '%s\n' "$zone" > /etc/timezone +fi +date -u -s {shlex.quote(timestamp)} +""" + cmd = _ssh_command(vm_id) + cmd.append("bash -lc " + shlex.quote(script)) + completed = _run_capture(cmd) + if completed is None: + raise ConfigError("failed to sync VM clock: ssh command not found") + if completed.returncode != 0: + stderr = completed.stderr.strip() or completed.stdout.strip() + raise ConfigError(f"failed to sync VM clock: {stderr}") + + def _prepare_run_user(vm_id: str, user: str) -> None: quoted_user = shlex.quote(user) home = f"/home/{quoted_user}" @@ -992,6 +1029,7 @@ def _post_start_actions( cwd: str | None = None, git_config_text: str | None = None, ) -> int: + _sync_guest_clock(vm_name) if auth_port: port_rc = network.expose_auth_port(vm_name, auth_host_port, auth_guest_port) if port_rc != 0: diff --git a/tests/test_cli.py b/tests/test_cli.py index 180bd47..75a4a11 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -747,7 +747,11 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "30", "--no-attach", ] - assert captured["env"] == {"HOME": str(captured["temp_home"]), "SBX_TEST": "credential-free"} + assert captured["env"] == { + "HOME": str(captured["temp_home"]), + "SBX_TEST": "credential-free", + "SMOLVM_DISABLE_VERSION_CHECK": "1", + } def test_env_vars_are_not_forwarded_by_default_with_host_credentials( @@ -933,6 +937,7 @@ def test_reusing_existing_vm_writes_config_only_when_requested( smolvm.chmod(0o755) monkeypatch.setenv("PATH", f"{bin_dir}{os.pathsep}{os.environ.get('PATH', '')}") monkeypatch.setattr(cli, "_smolvm_argv", lambda args: ["smolvm", *args]) + monkeypatch.setattr(cli, "_sync_guest_clock", lambda vm_id: None) assert cli.main(["run", "vm1", "--no-attach", "--no-auth-port"]) == 0 assert not (tmp_path / ".sbx.toml").exists() @@ -1106,6 +1111,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None monkeypatch.setattr(cli, "_run_capture", fake_run_capture) monkeypatch.setattr(cli, "_run", fake_run) + monkeypatch.setattr(cli, "_sync_guest_clock", lambda vm_id: None) monkeypatch.setattr(cli.network, "expose_auth_port", lambda vm_id, host_port, guest_port: 0) monkeypatch.setattr(cli, "_attach_as_root", lambda vm_id, launch_command, cwd=None: 0) diff --git a/tests/test_cli_extra.py b/tests/test_cli_extra.py index c242e60..676201a 100644 --- a/tests/test_cli_extra.py +++ b/tests/test_cli_extra.py @@ -652,6 +652,25 @@ def test_attach_commands(monkeypatch: pytest.MonkeyPatch) -> None: assert "sudo -iu agent" in commands[1][-1] +def test_sync_guest_clock_sets_host_time_and_timezone(monkeypatch: pytest.MonkeyPatch) -> None: + captured: list[str] = [] + + def fake_run_capture(argv: list[str]) -> subprocess.CompletedProcess[str]: + captured.extend(argv) + return subprocess.CompletedProcess(argv, 0, stdout="", stderr="") + + monkeypatch.setattr(cli, "_ssh_command", lambda vm_id: ["ssh", vm_id]) + monkeypatch.setattr(cli, "_host_timezone", lambda: "America/Chicago") + monkeypatch.setattr(cli, "_run_capture", fake_run_capture) + + cli._sync_guest_clock("vm1") + + assert captured[:2] == ["ssh", "vm1"] + assert "America/Chicago" in captured[2] + assert "ln -sf" in captured[2] + assert "date -u -s" in captured[2] + + def test_post_start_actions_paths(monkeypatch: pytest.MonkeyPatch) -> None: calls: list[tuple[str, object]] = [] monkeypatch.setattr( @@ -664,6 +683,7 @@ def test_post_start_actions_paths(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( cli, "_stop_vm_if_last_session", lambda *args, **kwargs: calls.append(("stop", kwargs)) ) + monkeypatch.setattr(cli, "_sync_guest_clock", lambda *args: calls.append(("clock", args))) monkeypatch.setattr(cli, "_prepare_run_user", lambda *args: calls.append(("prepare", args))) monkeypatch.setattr(cli, "_install_git_config", lambda *args: calls.append(("git", args))) monkeypatch.setattr( @@ -686,7 +706,7 @@ def test_post_start_actions_paths(monkeypatch: pytest.MonkeyPatch) -> None: ) == 0 ) - assert calls == [("port", ("vm1", 1, 2))] + assert calls == [("clock", ("vm1",)), ("port", ("vm1", 1, 2))] calls.clear() assert ( @@ -704,6 +724,7 @@ def test_post_start_actions_paths(monkeypatch: pytest.MonkeyPatch) -> None: == 0 ) assert [name for name, _ in calls] == [ + "clock", "register", "prepare", "git", @@ -1161,6 +1182,7 @@ def test_read_toml_oserror_and_create_alias( def test_post_start_actions_auth_port_failure_skips_attach(monkeypatch: pytest.MonkeyPatch) -> None: calls: list[str] = [] + monkeypatch.setattr(cli, "_sync_guest_clock", lambda vm_id: calls.append("clock")) monkeypatch.setattr(cli.network, "expose_auth_port", lambda *args: calls.append("port") or 9) monkeypatch.setattr(cli, "_attach_as_root", lambda *args, **kwargs: calls.append("attach") or 0) @@ -1177,7 +1199,7 @@ def test_post_start_actions_auth_port_failure_skips_attach(monkeypatch: pytest.M ) == 9 ) - assert calls == ["port"] + assert calls == ["clock", "port"] def test_close_auth_port_escalates_to_sigkill(monkeypatch: pytest.MonkeyPatch) -> None: From 73397df90216e01a7fbcb37d538239a27ec59b54 Mon Sep 17 00:00:00 2001 From: Juan Diaz Date: Mon, 20 Jul 2026 21:37:45 +0000 Subject: [PATCH 4/5] Allow test doubles to accept runner env kwargs --- tests/test_cli_extra.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/test_cli_extra.py b/tests/test_cli_extra.py index 676201a..f1caddd 100644 --- a/tests/test_cli_extra.py +++ b/tests/test_cli_extra.py @@ -626,7 +626,7 @@ def fake_ok(argv: list[str], *, env: dict[str, str] | None = None): assert "127.0.1.1" in captured["argv"][-1] assert ".ssh .pi .codex .claude .claude.json" in captured["argv"][-1] - monkeypatch.setattr(cli, "_run_capture", lambda argv: None) + monkeypatch.setattr(cli, "_run_capture", lambda argv, **kwargs: None) with pytest.raises(cli.ConfigError, match="ssh command not found"): cli._prepare_run_user("vm1", "agent") @@ -831,14 +831,16 @@ def fake_run(argv: list[str]) -> int: def test_delete_vm_error_paths( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: - monkeypatch.setattr(cli, "_run_capture", lambda argv: None) + monkeypatch.setattr(cli, "_run_capture", lambda argv, **kwargs: None) assert cli._delete_vm("vm1") == 127 not_found = json.dumps({"data": {"failed": [{"error": "VM 'vm1' not found"}]}}) monkeypatch.setattr( cli, "_run_capture", - lambda argv: subprocess.CompletedProcess(argv, 1, stdout=not_found, stderr="warn\n"), + lambda argv, **kwargs: subprocess.CompletedProcess( + argv, 1, stdout=not_found, stderr="warn\n" + ), ) assert cli._delete_vm("vm1") == 0 captured = capsys.readouterr() @@ -848,7 +850,7 @@ def test_delete_vm_error_paths( monkeypatch.setattr( cli, "_run_capture", - lambda argv: subprocess.CompletedProcess(argv, 3, stdout="not-json", stderr=""), + lambda argv, **kwargs: subprocess.CompletedProcess(argv, 3, stdout="not-json", stderr=""), ) assert cli._delete_vm("vm1") == 3 assert "not-json" in capsys.readouterr().out @@ -860,7 +862,9 @@ def test_network_status_variants( monkeypatch.setattr( cli.network, "run_smolvm_capture", - lambda argv: subprocess.CompletedProcess(argv, 1, stdout="bad out\n", stderr="bad err\n"), + lambda argv, **kwargs: subprocess.CompletedProcess( + argv, 1, stdout="bad out\n", stderr="bad err\n" + ), ) assert cli.network.cmd_status(type("Args", (), {"name": "vm1", "host_port": 1455})()) == 1 captured = capsys.readouterr() @@ -883,7 +887,7 @@ def test_network_status_variants( monkeypatch.setattr( cli.network, "run_smolvm_capture", - lambda argv: subprocess.CompletedProcess(argv, 0, stdout=payload, stderr=""), + lambda argv, **kwargs: subprocess.CompletedProcess(argv, 0, stdout=payload, stderr=""), ) monkeypatch.setattr( cli.network, @@ -1117,7 +1121,7 @@ def test_network_status_inactive( monkeypatch.setattr( cli.network, "run_smolvm_capture", - lambda argv: subprocess.CompletedProcess(argv, 0, stdout=payload, stderr=""), + lambda argv, **kwargs: subprocess.CompletedProcess(argv, 0, stdout=payload, stderr=""), ) monkeypatch.setattr(cli.network, "_tracked_auth_tunnel", lambda name: None) monkeypatch.setattr(cli.network, "_localhost_port_is_listening", lambda port: False) @@ -1233,7 +1237,9 @@ def test_delete_vm_success_path( monkeypatch.setattr( cli, "_run_capture", - lambda argv: subprocess.CompletedProcess(argv, 0, stdout='{"data": {}}', stderr=""), + lambda argv, **kwargs: subprocess.CompletedProcess( + argv, 0, stdout='{"data": {}}', stderr="" + ), ) assert cli._delete_vm("vm1") == 0 @@ -1421,7 +1427,7 @@ def test_local_image_qcow2_disk_size_does_not_request_filesystem_growth( def test_network_status_run_capture_missing(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr(cli.network, "run_smolvm_capture", lambda argv: None) + monkeypatch.setattr(cli.network, "run_smolvm_capture", lambda argv, **kwargs: None) assert cli.network.cmd_status(type("Args", (), {"name": "vm1", "host_port": 1455})()) == 127 @@ -1540,7 +1546,7 @@ def test_start_existing_vm_timeout_hint_when_vm_is_running( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], ) -> None: - monkeypatch.setattr(cli, "_run", lambda argv: 1) + monkeypatch.setattr(cli, "_run", lambda argv, **kwargs: 1) monkeypatch.setattr(cli, "_get_existing_vm_status", lambda vm_id: "running") assert cli._start_existing_vm_if_needed("vm1", "stopped", 60) == 1 From 08529ce6c2e7fde12a6761e43f1e4895e8fce1fd Mon Sep 17 00:00:00 2001 From: Juan Diaz Date: Mon, 20 Jul 2026 21:37:49 +0000 Subject: [PATCH 5/5] Increase default boot timeout --- src/sbx/cli.py | 2 +- src/sbx/constants.py | 2 +- tests/test_cli.py | 28 ++++++++++++++-------------- tests/test_cli_extra.py | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sbx/cli.py b/src/sbx/cli.py index 933f1df..03197c0 100644 --- a/src/sbx/cli.py +++ b/src/sbx/cli.py @@ -2019,7 +2019,7 @@ def _add_start_options(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--boot-timeout", type=float, - help="Seconds to wait for VM boot/SSH readiness (default: [sbx].boot_timeout or 30).", + help="Seconds to wait for VM boot/SSH readiness (default: [sbx].boot_timeout or 60).", ) parser.add_argument("--install-timeout", type=float) write_config = parser.add_mutually_exclusive_group() diff --git a/src/sbx/constants.py b/src/sbx/constants.py index 61a09e6..df632f7 100644 --- a/src/sbx/constants.py +++ b/src/sbx/constants.py @@ -2,7 +2,7 @@ from pathlib import Path DEFAULT_BACKEND = "qemu" -DEFAULT_BOOT_TIMEOUT = 30.0 +DEFAULT_BOOT_TIMEOUT = 60.0 SBX_STATE_DIR = Path(os.environ.get("XDG_STATE_HOME", Path.home() / ".local" / "state")) / "sbx" TUNNELS_FILE = SBX_STATE_DIR / "tunnels.json" SESSIONS_FILE = SBX_STATE_DIR / "sessions.json" diff --git a/tests/test_cli.py b/tests/test_cli.py index 75a4a11..caaac0f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -430,7 +430,7 @@ def test_run_uses_local_toml_defaults( assert rc == 0 assert capfd.readouterr().out == ( - "codex start --name demo --backend qemu --boot-timeout 30 --mount .:/workspace --attach\n" + "codex start --name demo --backend qemu --boot-timeout 60 --mount .:/workspace --attach\n" ) @@ -595,7 +595,7 @@ def fake_attach(vm_id: str, user: str, launch_command: str, cwd: str | None = No "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", "--json", ] @@ -744,7 +744,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", ] assert captured["env"] == { @@ -832,7 +832,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", ] assert captured["env"] is not None @@ -1023,7 +1023,7 @@ def fake_attach(vm_id: str, launch_command: str, cwd: str | None = None) -> int: "--backend", "qemu", "--boot-timeout", - "30", + "60", "--mount", f"{project}:{project}", "--writable-mounts", @@ -1076,7 +1076,7 @@ def fake_attach(vm_id: str, launch_command: str, cwd: str | None = None) -> int: "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", "--json", ] @@ -1120,7 +1120,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None assert rc == 0 assert calls == [ ["smolvm", "sandbox", "info", "vm1", "--json"], - ["smolvm", "sandbox", "start", "vm1", "--boot-timeout", "30"], + ["smolvm", "sandbox", "start", "vm1", "--boot-timeout", "60"], ["smolvm", "sandbox", "stop", "vm1"], ] @@ -1228,7 +1228,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", ] @@ -1272,7 +1272,7 @@ def fake_run_capture( "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", "--json", ] @@ -1317,7 +1317,7 @@ def fake_run_capture( "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", "--json", ] @@ -1348,7 +1348,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", ] @@ -1635,7 +1635,7 @@ def fake_run(argv: list[str], *, check: bool = False, env: dict[str, str] | None "--backend", "qemu", "--boot-timeout", - "30", + "60", "--no-attach", ], ] @@ -1674,7 +1674,7 @@ def test_cli_project_path_overrides_config( assert rc == 0 assert capfd.readouterr().out == ( - "pi start --backend qemu --boot-timeout 30 " + "pi start --backend qemu --boot-timeout 60 " f"--mount {cli_project}:{cli_project} " "--writable-mounts --no-attach\n" ) @@ -1711,7 +1711,7 @@ def test_cli_flags_override_config( assert rc == 0 assert capfd.readouterr().out == ( - "claude start --name from-cli --backend qemu --boot-timeout 30 --attach\n" + "claude start --name from-cli --backend qemu --boot-timeout 60 --attach\n" ) diff --git a/tests/test_cli_extra.py b/tests/test_cli_extra.py index f1caddd..8e71ea6 100644 --- a/tests/test_cli_extra.py +++ b/tests/test_cli_extra.py @@ -303,8 +303,8 @@ def test_start_local_image_happy_path( assert calls == ["sync", "attach"] assert fake_smolvm_sdk["started"] is True assert fake_smolvm_sdk["waited"] is True - assert fake_smolvm_sdk["start_kwargs"] == {"boot_timeout": 30.0} - assert fake_smolvm_sdk["wait_kwargs"] == {"timeout": 30.0} + assert fake_smolvm_sdk["start_kwargs"] == {"boot_timeout": 60.0} + assert fake_smolvm_sdk["wait_kwargs"] == {"timeout": 60.0} vm_config = fake_smolvm_sdk["vm_config"] assert isinstance(vm_config, dict) assert vm_config["vm_id"] == "vm-from-cli"