"""SmolmachinesBottlePlan — concrete BottlePlan for the smolmachines backend (PRD 0023). Slug + legacy bundle network coordinates + smolvm machine name + agent `.smolmachine` artifact + per-bottle guest env.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path from ...agent_provider import PromptMode from .. import BottlePlan @dataclass(frozen=True) class SmolmachinesBottlePlan(BottlePlan): """Resolved fields the launch step needs to bring up the bottle. Inherits `spec`, `stage_dir`, `git_gate_plan`, `egress_plan`, `supervise_plan`, and `agent_provision` from BottlePlan.""" slug: str # Legacy per-bottle bundle network coordinates. These remain on # the plan while BundleLaunchSpec still carries the original shape, # but the smolmachines launch path exposes the sidecar VM through # host-loopback forwarders instead of a Docker bridge IP. bundle_subnet: str bundle_gateway: str bundle_ip: str # In-guest env vars (HTTPS_PROXY etc) — IP-literal URLs since # the guest has no DNS resolver inside the TSI allowlist. # Passed to `smolvm machine create` as `-e K=V` flags. # Smolfile-rendering is gone (smolvm 0.8.0's # `--smolfile` is mutually exclusive with `--from`, and # `--from` is the path that avoids the registry-pull race). guest_env: dict[str, str] # Agent-side endpoints. Empty at prepare time; launch populates # these after sidecar VM bringup via `dataclasses.replace`. # Format: a `host:port` for git-gate (insteadOf URL prefix) + # full URLs for proxy / supervise. agent_proxy_url: str = "" agent_git_gate_host: str = "" agent_supervise_url: str = "" @property def machine_name(self) -> str: """smolvm machine name. `machine_create` boots from a packed `.smolmachine` artifact (pre-baked at prepare time via `smolvm pack create`); using `--from` instead of `--image` avoids the registry-pull race we hit when machine_start tried to fetch on-demand and the libkrun agent's network attempt got refused by macOS.""" return self.agent_provision.instance_name @property def agent_image(self) -> str: """Agent image ref (docker tag). `launch` runs the build → save → registry push → smolvm pack pipeline against this and feeds the resulting `.smolmachine` artifact to `machine_create --from`. The pipeline runs at launch time (not prepare time) so the docker build output doesn't garble the dashboard's preflight modal.""" return self.agent_provision.image @property def prompt_file(self) -> Path: """Path to the agent's prompt file on the host. Always written (mode 0o600) so the in-VM path always exists; the file is empty when the agent has no prompt — claude-code reads it via --append-system-prompt-file only when non-empty.""" return self.agent_provision.prompt_file @property def git_gate_insteadof_host(self) -> str: return self.agent_git_gate_host @property def git_gate_insteadof_scheme(self) -> str: return "http" @property def agent_command(self) -> str: return self.agent_provision.command @property def agent_prompt_mode(self) -> PromptMode: return self.agent_provision.prompt_mode @property def agent_provider_template(self) -> str: return self.agent_provision.template @property def agent_dockerfile_path(self) -> str: return self.agent_provision.dockerfile