diff --git a/bot_bottle/backend/smolmachines/backend.py b/bot_bottle/backend/smolmachines/backend.py index f24be61..a85905d 100644 --- a/bot_bottle/backend/smolmachines/backend.py +++ b/bot_bottle/backend/smolmachines/backend.py @@ -19,7 +19,7 @@ from ...env import ResolvedEnv from ...git_gate import GitGatePlan from ...supervise import SupervisePlan from ...manifest import Manifest -from .. import ActiveAgent, BottleBackend, BottleSpec +from .. import ActiveAgent, BottleBackend, BottleImages, BottleSpec from . import cleanup as _cleanup from . import enumerate as _enumerate from . import launch as _launch @@ -77,11 +77,14 @@ class SmolmachinesBottleBackend( stage_dir=stage_dir, ) + def _build_or_load_images(self, plan: SmolmachinesBottlePlan) -> BottleImages: + return _launch.build_or_load_images(plan) + @contextmanager - def launch( - self, plan: SmolmachinesBottlePlan + def _launch_impl( + self, plan: SmolmachinesBottlePlan, images: BottleImages ) -> Generator[SmolmachinesBottle, None, None]: - with _launch.launch(plan, provision=self.provision) as bottle: + with _launch.launch(plan, images, provision=self.provision) as bottle: yield bottle def supervise_mcp_url(self, plan: SmolmachinesBottlePlan) -> str: diff --git a/bot_bottle/backend/smolmachines/launch.py b/bot_bottle/backend/smolmachines/launch.py index 56e5929..ebff3e9 100644 --- a/bot_bottle/backend/smolmachines/launch.py +++ b/bot_bottle/backend/smolmachines/launch.py @@ -52,6 +52,7 @@ from ...bottle_state import ( git_gate_state_dir, read_committed_image, ) +from .. import BottleImages from . import loopback_alias as _loopback from . import sidecar_bundle as _bundle from . import smolvm as _smolvm @@ -79,13 +80,20 @@ _GIT_HTTP_PORT = 9420 _SUPERVISE_PORT = SUPERVISE_PORT +def build_or_load_images(plan: SmolmachinesBottlePlan) -> BottleImages: + """Build or resolve the agent smolmachine artifact and sidecar bundle image.""" + _bundle.ensure_bundle_image() + return BottleImages(agent=_agent_from_path(plan), sidecar=_bundle.SIDECAR_BUNDLE_IMAGE) + + @contextmanager def launch( plan: SmolmachinesBottlePlan, + images: BottleImages, *, provision: Callable[[SmolmachinesBottlePlan, "SmolmachinesBottle"], str | None], ) -> Generator[SmolmachinesBottle, None, None]: - """Build + run the bottle and yield a handle; tear everything + """Run the bottle from pre-built images and yield a handle; tear everything down on exit. Errors during bringup unwind any partial state via the ExitStack.""" stack = ExitStack() @@ -96,9 +104,7 @@ def launch( plan = _start_bundle(plan, network, proxy_host, stack) plan = _discover_urls(plan, proxy_host) - agent_from_path = _agent_from_path(plan) - - _launch_vm(plan, agent_from_path, proxy_host, stack) + _launch_vm(plan, Path(images.agent), proxy_host, stack) _init_vm(plan) bottle = SmolmachinesBottle( @@ -184,7 +190,6 @@ def _start_bundle( plan = _provision_git_gate_keys(plan) bundle_spec = _bundle_launch_spec(plan, network, proxy_host) token_env = _resolve_token_env(plan, dict(os.environ)) - _bundle.ensure_bundle_image(bundle_spec.image) _bundle.start_bundle(bundle_spec, env={**os.environ, **token_env}) stack.callback(_bundle.stop_bundle, plan.slug) return plan diff --git a/tests/unit/test_docker_launch_committed_image.py b/tests/unit/test_docker_launch_committed_image.py index bbdfb6b..ce2742a 100644 --- a/tests/unit/test_docker_launch_committed_image.py +++ b/tests/unit/test_docker_launch_committed_image.py @@ -12,7 +12,7 @@ from typing import Any from unittest import mock from bot_bottle.agent_provider import AgentProvisionPlan -from bot_bottle.backend import BottleImages, BottleSpec +from bot_bottle.backend import BottleSpec from bot_bottle.backend.docker import launch as launch_mod from bot_bottle.backend.docker.bottle_plan import DockerBottlePlan from bot_bottle.egress import EgressPlan diff --git a/tests/unit/test_macos_container_launch.py b/tests/unit/test_macos_container_launch.py index a2c5978..6c7d490 100644 --- a/tests/unit/test_macos_container_launch.py +++ b/tests/unit/test_macos_container_launch.py @@ -11,7 +11,7 @@ from typing import cast from unittest.mock import patch from bot_bottle.agent_provider import AgentProvisionPlan -from bot_bottle.backend import BottleImages, BottleSpec +from bot_bottle.backend import BottleSpec from bot_bottle.backend.macos_container import launch from bot_bottle.backend.macos_container.bottle_plan import MacosContainerBottlePlan from bot_bottle.egress import EgressPlan