refactor: make AgentProvisionPlan the source of truth for instance_name, prompt_file, image, dockerfile, guest_home
lint / lint (push) Failing after 1m47s
test / unit (pull_request) Successful in 38s
test / integration (pull_request) Successful in 24s

Drop the parallel fields passed through prepare() → _resolve_plan and
read everything from agent_provision instead. The provider plugin now
declares its own guest_home (so the backend stops hardcoding
"/home/node") and the wrapper that builds the provision plan accepts
instance_name and prompt_file, which providers store on the plan.

DockerBottlePlan and SmolmachinesBottlePlan expose container_name /
machine_name, image / agent_image, dockerfile_path /
agent_dockerfile_path, and prompt_file as properties that delegate to
agent_provision so existing call sites keep working unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 19:23:19 +00:00
parent e2514b3885
commit 37a780acf6
18 changed files with 152 additions and 137 deletions
+16 -3
View File
@@ -104,6 +104,8 @@ class AgentProvisionPlan:
image: str
dockerfile: str
guest_home: str
instance_name: str
prompt_file: Path
guest_env: dict[str, str]
env_vars: dict[str, str] = field(default_factory=dict)
dirs: tuple[AgentProvisionDir, ...] = ()
@@ -128,6 +130,14 @@ class AgentProvider(ABC):
"""The static command / image / prompt-mode table for this
template."""
@property
def guest_home(self) -> str:
"""In-guest home directory for the agent user. Defaults to
`/home/node` to match the Debian-based bot-bottle-* images
(USER node). Override for plugins whose image runs as a
different user."""
return "/home/node"
@property
def dockerfile(self) -> Path:
"""Path to the provider's Dockerfile.
@@ -143,7 +153,8 @@ class AgentProvider(ABC):
*,
dockerfile: str,
state_dir: Path,
guest_home: str,
instance_name: str,
prompt_file: Path,
guest_env: dict[str, str] | None = None,
auth_token: str = "",
forward_host_credentials: bool = False,
@@ -333,7 +344,8 @@ def build_agent_provision_plan(
template: str,
dockerfile: str,
state_dir: Path,
guest_home: str,
instance_name: str,
prompt_file: Path,
guest_env: dict[str, str] | None = None,
auth_token: str = "",
forward_host_credentials: bool = False,
@@ -347,7 +359,8 @@ def build_agent_provision_plan(
return get_provider(template).provision_plan(
dockerfile=dockerfile,
state_dir=state_dir,
guest_home=guest_home,
instance_name=instance_name,
prompt_file=prompt_file,
guest_env=guest_env,
auth_token=auth_token,
forward_host_credentials=forward_host_credentials,
+5 -12
View File
@@ -292,7 +292,6 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
manifest_bottle = manifest.bottle_for(spec.agent_name)
manfiest_agent_provider = manifest_bottle.agent_provider
agent_provider = get_provider(manfiest_agent_provider.template)
agent_image = agent_provider.runtime.image
resolved_env = resolve_env(manifest, spec.agent_name)
slug = mint_slug(spec)
@@ -307,7 +306,6 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
)
else:
agent_dockerfile_path = str(agent_provider.dockerfile)
instance_name = f"bot-bottle-{slug}"
agent_dir, prompt_file = prepare_agent_state_dir(slug, spec)
@@ -315,7 +313,8 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
template=manfiest_agent_provider.template,
dockerfile=agent_dockerfile_path,
state_dir=agent_dir,
guest_home="/home/node", # FIXME: should be coming from the agent plan
instance_name=f"bot-bottle-{slug}",
prompt_file=prompt_file,
guest_env=self._build_guest_env(resolved_env),
forward_host_credentials=manfiest_agent_provider.forward_host_credentials,
auth_token=manfiest_agent_provider.auth_token,
@@ -333,10 +332,6 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
spec,
slug=slug,
resolved_env=resolved_env,
instance_name=instance_name, # FIXME: move to agent provision plan
agent_image=agent_image, # FIXME: move to agent provision plan
prompt_file=prompt_file, # FIXME: move to agent provision plan
agent_dockerfile_path=agent_dockerfile_path, # FIXME: move to agent provision plan
agent_provision_plan=agent_provision_plan,
egress_plan=egress_plan,
supervise_plan=supervise_plan,
@@ -408,18 +403,16 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
*,
slug: str,
resolved_env: ResolvedEnv,
instance_name: str,
agent_image: str,
prompt_file: Path,
agent_provision_plan: AgentProvisionPlan,
agent_dockerfile_path: str,
egress_plan: EgressPlan,
git_gate_plan: GitGatePlan,
supervise_plan: SupervisePlan | None,
stage_dir: Path) -> PlanT:
"""Backend-specific plan resolution: image/container names,
env-file, prompt-file, proxy plan, runtime detection. Called by
`prepare` after `_validate` succeeds."""
`prepare` after `_validate` succeeds. Instance name, image,
prompt file, Dockerfile path, and guest home all live on
`agent_provision_plan` — the source of truth."""
@abstractmethod
def launch(self, plan: PlanT) -> AbstractContextManager[Bottle]:
-8
View File
@@ -54,11 +54,7 @@ class DockerBottleBackend(BottleBackend["DockerBottlePlan", "DockerBottleCleanup
*,
slug: str,
resolved_env,
instance_name: str,
agent_image: str,
prompt_file: Path,
agent_provision_plan,
agent_dockerfile_path: str,
egress_plan,
git_gate_plan,
supervise_plan,
@@ -68,10 +64,6 @@ class DockerBottleBackend(BottleBackend["DockerBottlePlan", "DockerBottleCleanup
spec,
slug=slug,
resolved_env=resolved_env,
instance_name=instance_name,
agent_image=agent_image,
agent_dockerfile_path=agent_dockerfile_path,
prompt_file=prompt_file,
agent_provision_plan=agent_provision_plan,
egress_plan=egress_plan,
supervise_plan=supervise_plan,
+19 -8
View File
@@ -22,21 +22,32 @@ class DockerBottlePlan(BottlePlan):
`agent_provision` from BottlePlan."""
slug: str
container_name: str
image: str
# Absolute path to the Dockerfile that builds `image`. Empty means
# use the repo's default Dockerfile. Populated to a per-bottle
# state file (~/.bot-bottle/state/<slug>/Dockerfile) after a
# capability-block remediation (PRD 0016).
dockerfile_path: str
# name -> value for vars forwarded into the docker-run child process
# via subprocess env (so values never land on argv or in a file).
# repr=False keeps secret/interpolated/OAuth values out of any
# accidental log of the plan dataclass.
forwarded_env: dict[str, str] = field(repr=False)
prompt_file: Path
use_runsc: bool
@property
def container_name(self) -> str:
return self.agent_provision.instance_name
@property
def image(self) -> str:
return self.agent_provision.image
@property
def dockerfile_path(self) -> str:
"""Absolute path to the Dockerfile that builds `image`. Sourced
from the agent provision plan — the manifest may override per
bottle; otherwise the provider plugin's bundled Dockerfile."""
return self.agent_provision.dockerfile
@property
def prompt_file(self) -> Path:
return self.agent_provision.prompt_file
@property
def agent_command(self) -> str:
return self.agent_provision.command
@@ -34,10 +34,6 @@ def resolve_plan(
spec: BottleSpec,
slug: str,
resolved_env: ResolvedEnv,
instance_name: str,
agent_image: str,
agent_dockerfile_path: str,
prompt_file: Path,
agent_provision_plan: AgentProvisionPlan,
egress_plan: EgressPlan,
supervise_plan: SupervisePlan,
@@ -55,12 +51,7 @@ def resolve_plan(
spec=spec,
stage_dir=stage_dir,
slug=slug,
container_name=instance_name,
# container_name_pinned=container_name_pinned,
image=agent_image,
dockerfile_path=agent_dockerfile_path,
forwarded_env=dict(resolved_env.forwarded),
prompt_file=prompt_file,
git_gate_plan=git_gate_plan,
egress_plan=egress_plan,
supervise_plan=supervise_plan,
@@ -47,11 +47,7 @@ class SmolmachinesBottleBackend(
*,
slug: str,
resolved_env,
instance_name: str,
agent_image: str,
prompt_file: Path,
agent_provision_plan,
agent_dockerfile_path: str,
egress_plan,
git_gate_plan,
supervise_plan,
@@ -61,10 +57,6 @@ class SmolmachinesBottleBackend(
spec,
slug=slug,
resolved_env=resolved_env,
instance_name=instance_name,
agent_image=agent_image,
agent_dockerfile_path=agent_dockerfile_path,
prompt_file=prompt_file,
agent_provision_plan=agent_provision_plan,
egress_plan=egress_plan,
supervise_plan=supervise_plan,
+28 -26
View File
@@ -29,27 +29,6 @@ class SmolmachinesBottlePlan(BottlePlan):
bundle_subnet: str
bundle_gateway: str
bundle_ip: str
# smolvm machine name + agent image source. 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.
#
# Chunk 2d ships with a public placeholder image (alpine)
# since bot-bottle-claude:latest lives in the operator's local
# docker daemon and smolvm's crane backend can't read from
# there; chunk 4 resolves the agent-image-conversion gap
# (push to a registry first, or smolvm grows a docker-daemon
# transport).
machine_name: 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.
agent_image: 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.
@@ -57,11 +36,6 @@ class SmolmachinesBottlePlan(BottlePlan):
# `--smolfile` is mutually exclusive with `--from`, and
# `--from` is the path that avoids the registry-pull race).
guest_env: dict[str, str]
# 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.
prompt_file: Path
# Inner Plans for the sidecar bundle daemons. The same shape the
# docker backend uses — same `.prepare()` calls produced
# them — but our launch step doesn't populate the
@@ -82,6 +56,34 @@ class SmolmachinesBottlePlan(BottlePlan):
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
@@ -51,10 +51,6 @@ def resolve_plan(
spec: BottleSpec,
slug: str,
resolved_env: ResolvedEnv,
instance_name: str,
agent_image: str,
agent_dockerfile_path: str,
prompt_file: Path,
agent_provision_plan: AgentProvisionPlan,
egress_plan: EgressPlan,
supervise_plan: SupervisePlan,
@@ -79,10 +75,7 @@ def resolve_plan(
bundle_subnet=subnet,
bundle_gateway=gateway,
bundle_ip=bundle_ip,
machine_name=instance_name,
agent_image=agent_image,
guest_env=agent_provision_plan.guest_env,
prompt_file=prompt_file,
git_gate_plan=git_gate_plan,
egress_plan=egress_plan,
supervise_plan=supervise_plan,
+5 -1
View File
@@ -59,7 +59,8 @@ class ClaudeAgentProvider(AgentProvider):
*,
dockerfile: str,
state_dir: Path,
guest_home: str,
instance_name: str,
prompt_file: Path,
guest_env: dict[str, str] | None = None,
auth_token: str = "",
forward_host_credentials: bool = False,
@@ -70,6 +71,7 @@ class ClaudeAgentProvider(AgentProvider):
) -> AgentProvisionPlan:
del forward_host_credentials, host_env # Codex-only knobs
resolved_guest_env = dict(guest_env or {})
guest_home = self.guest_home
trusted_path = trusted_project_path or guest_home
env_vars: dict[str, str] = {
@@ -111,6 +113,8 @@ class ClaudeAgentProvider(AgentProvider):
image=_RUNTIME.image,
dockerfile=dockerfile,
guest_home=guest_home,
instance_name=instance_name,
prompt_file=prompt_file,
env_vars=env_vars,
guest_env=resolved_guest_env,
files=files,
+5 -1
View File
@@ -67,7 +67,8 @@ class CodexAgentProvider(AgentProvider):
*,
dockerfile: str,
state_dir: Path,
guest_home: str,
instance_name: str,
prompt_file: Path,
guest_env: dict[str, str] | None = None,
auth_token: str = "",
forward_host_credentials: bool = False,
@@ -78,6 +79,7 @@ class CodexAgentProvider(AgentProvider):
) -> AgentProvisionPlan:
del auth_token, label, color # Claude-only knobs
resolved_guest_env = dict(guest_env or {})
guest_home = self.guest_home
trusted_path = trusted_project_path or guest_home
env_vars: dict[str, str] = {
@@ -148,6 +150,8 @@ class CodexAgentProvider(AgentProvider):
image=_RUNTIME.image,
dockerfile=dockerfile,
guest_home=guest_home,
instance_name=instance_name,
prompt_file=prompt_file,
env_vars=env_vars,
guest_env=resolved_guest_env,
dirs=tuple(dirs),
+20 -10
View File
@@ -26,10 +26,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_codex_plan_declares_home_state(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="/tmp/Dockerfile.codex",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
)
config = Path(tmp, "codex-config.toml").read_text()
self.assertEqual("codex", plan.template)
@@ -51,10 +52,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_codex_trusts_requested_project_path(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
trusted_project_path="/home/node/workspace",
)
config = Path(tmp, "codex-config.toml").read_text()
@@ -69,10 +71,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
"tokens": {"access_token": _jwt(2000000000)},
}))
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
guest_env={"CODEX_HOME": "/run/codex-home"},
forward_host_credentials=True,
host_env={"CODEX_HOME": str(home)},
@@ -89,10 +92,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_claude_with_auth_token_injects_provider_route_and_placeholder(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
plan = build_agent_provision_plan(
guest_home="/home/node",
template="claude",
dockerfile="/tmp/Dockerfile.claude",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
auth_token="BOT_BOTTLE_CLAUDE_OAUTH_TOKEN",
)
claude_config = json.loads(Path(tmp, "claude.json").read_text())
@@ -111,10 +115,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_claude_trusts_requested_project_path(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
build_agent_provision_plan(
guest_home="/home/node",
template="claude",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
trusted_project_path="/home/node/workspace",
)
config = json.loads(Path(tmp, "claude.json").read_text())
@@ -130,10 +135,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
"tokens": {"access_token": _jwt(2000000000)},
}))
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
forward_host_credentials=True,
host_env={"CODEX_HOME": str(home)},
)
@@ -146,10 +152,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_codex_without_forward_host_credentials_has_passthrough_egress_routes(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
forward_host_credentials=False,
)
self.assertEqual(
@@ -163,10 +170,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_claude_without_auth_token_has_passthrough_egress_route(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
plan = build_agent_provision_plan(
guest_home="/home/node",
template="claude",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
)
self.assertEqual(1, len(plan.egress_routes))
route = plan.egress_routes[0]
@@ -186,10 +194,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
"tokens": {"access_token": access},
}))
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
forward_host_credentials=True,
host_env={"CODEX_HOME": str(home)},
)
@@ -201,10 +210,11 @@ class TestAgentProviderRuntime(unittest.TestCase):
def test_codex_without_forward_host_credentials_has_empty_provisioned_env(self):
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
plan = build_agent_provision_plan(
guest_home="/home/node",
template="codex",
dockerfile="",
state_dir=Path(tmp),
instance_name="bot-bottle-test",
prompt_file=Path(tmp) / "prompt.txt",
forward_host_credentials=False,
)
self.assertEqual({}, plan.provisioned_env)
+4 -4
View File
@@ -152,11 +152,7 @@ def _plan(
spec=spec,
stage_dir=STAGE,
slug=SLUG,
container_name=f"bot-bottle-{SLUG}",
image="bot-bottle-claude:latest",
dockerfile_path="",
forwarded_env={"CLAUDE_CODE_OAUTH_TOKEN": "x"},
prompt_file=STAGE / "prompt",
git_gate_plan=_git_gate_plan(upstreams),
egress_plan=_egress_plan(routes),
supervise_plan=_supervise_plan() if supervise else None,
@@ -168,6 +164,8 @@ def _plan(
image="bot-bottle-claude:latest",
dockerfile="",
guest_home="/home/node",
instance_name=f"bot-bottle-{SLUG}",
prompt_file=STAGE / "prompt",
guest_env={},
),
)
@@ -248,6 +246,8 @@ class TestAgentAlwaysPresent(unittest.TestCase):
image="bot-bottle-codex:latest",
dockerfile="",
guest_home="/home/node",
instance_name=f"bot-bottle-{SLUG}",
prompt_file=STAGE / "prompt",
guest_env={"CODEX_HOME": "/home/node/.codex"},
)
plan = type(plan)(**{**vars(plan), "agent_provision": provision}) # type: ignore
+17 -8
View File
@@ -79,11 +79,7 @@ def _plan(
spec=spec,
stage_dir=Path("/tmp/stage"),
slug="demo-abc12",
container_name="bot-bottle-demo-abc12",
image="bot-bottle-claude:latest",
dockerfile_path="",
forwarded_env={},
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
git_gate_plan=GitGatePlan(
slug="demo-abc12",
entrypoint_script=Path("/tmp/git-gate-entrypoint.sh"),
@@ -101,7 +97,11 @@ def _plan(
use_runsc=False,
agent_provision=agent_provision or AgentProvisionPlan(
template="claude", command="claude", prompt_mode="append_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="bot-bottle-claude:latest", dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
guest_env={},
),
)
@@ -205,7 +205,10 @@ class TestClaudeProvision(unittest.TestCase):
def test_copies_files_and_chowns(self):
provision = AgentProvisionPlan(
template="claude", command="claude", prompt_mode="append_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
files=(AgentProvisionFile(
Path("/tmp/claude.json"), "/home/node/.claude.json",
),),
@@ -228,7 +231,10 @@ class TestClaudeProvision(unittest.TestCase):
def test_dies_when_file_chown_fails(self):
provision = AgentProvisionPlan(
template="claude", command="claude", prompt_mode="append_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
files=(AgentProvisionFile(
Path("/tmp/claude.json"), "/home/node/.claude.json",
),),
@@ -244,7 +250,10 @@ class TestClaudeProvision(unittest.TestCase):
def test_runs_verify_commands(self):
provision = AgentProvisionPlan(
template="claude", command="claude", prompt_mode="append_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
verify=(AgentProvisionCommand(
("/usr/bin/true",), "verify failed",
),),
+17 -8
View File
@@ -80,11 +80,7 @@ def _plan(
spec=spec,
stage_dir=Path("/tmp/stage"),
slug="demo-abc12",
container_name="bot-bottle-demo-abc12",
image="bot-bottle-codex:latest",
dockerfile_path="",
forwarded_env={},
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
git_gate_plan=GitGatePlan(
slug="demo-abc12",
entrypoint_script=Path("/tmp/git-gate-entrypoint.sh"),
@@ -102,7 +98,11 @@ def _plan(
use_runsc=False,
agent_provision=agent_provision or AgentProvisionPlan(
template="codex", command="codex", prompt_mode="read_prompt_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="bot-bottle-codex:latest", dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
guest_env={},
),
)
@@ -171,7 +171,10 @@ class TestCodexProvision(unittest.TestCase):
provision = AgentProvisionPlan(
template="codex", command="codex",
prompt_mode="read_prompt_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
dirs=(AgentProvisionDir("/home/node/.codex"),),
files=(AgentProvisionFile(
Path("/tmp/codex-config.toml"),
@@ -195,7 +198,10 @@ class TestCodexProvision(unittest.TestCase):
provision = AgentProvisionPlan(
template="codex", command="codex",
prompt_mode="read_prompt_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
pre_copy=(AgentProvisionCommand(
("find", "/home/node/.codex", "-name", "*.sqlite", "-delete"),
"could not reset runtime db files",
@@ -217,7 +223,10 @@ class TestCodexProvision(unittest.TestCase):
provision = AgentProvisionPlan(
template="codex", command="codex",
prompt_mode="read_prompt_file",
image="", dockerfile="", guest_home="/home/node", guest_env={},
image="", dockerfile="", guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
dirs=(AgentProvisionDir("/home/node/.codex"),),
)
bottle = _make_bottle(exec_result=ExecResult(1, "", "mkdir: nope\n"))
+3 -5
View File
@@ -63,17 +63,15 @@ def _plan(tmp: str) -> DockerBottlePlan:
template="claude",
command="claude",
prompt_mode="append_file",
image="",
image="bot-bottle-claude:latest",
dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-test-teardown-abc",
prompt_file=stage / "prompt.txt",
guest_env={},
),
slug="test-teardown-00001",
container_name="bot-bottle-test-teardown-abc",
image="bot-bottle-claude:latest",
dockerfile_path="",
forwarded_env={},
prompt_file=stage / "prompt.txt",
use_runsc=False,
)
+2 -4
View File
@@ -64,11 +64,7 @@ def _plan(*, git_user: dict | None = None, # type: ignore
spec=spec,
stage_dir=stage_dir or Path("/tmp/stage"),
slug="demo-abc12",
container_name="bot-bottle-demo-abc12",
image="bot-bottle-claude:latest",
dockerfile_path="",
forwarded_env={},
prompt_file=Path("/tmp/prompt.txt"),
git_gate_plan=GitGatePlan(
slug="demo-abc12",
entrypoint_script=Path("/tmp/git-gate-entrypoint.sh"),
@@ -91,6 +87,8 @@ def _plan(*, git_user: dict | None = None, # type: ignore
image="bot-bottle-claude:latest",
dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/prompt.txt"),
guest_env={},
),
)
+6 -11
View File
@@ -79,14 +79,16 @@ def _egress_plan(tmp: str) -> EgressPlan:
)
def _agent_provision() -> AgentProvisionPlan:
def _agent_provision(tmp: str) -> AgentProvisionPlan:
return AgentProvisionPlan(
template="claude",
command="claude",
prompt_mode="append_file",
image="",
image="bot-bottle-claude:latest",
dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-test-00001",
prompt_file=Path(tmp) / "prompt.txt",
guest_env={"HTTPS_PROXY": "http://127.0.0.1:9999"},
)
@@ -99,13 +101,9 @@ def _docker_plan(spec: BottleSpec, tmp: str) -> DockerBottlePlan:
git_gate_plan=_git_gate_plan(tmp),
egress_plan=_egress_plan(tmp),
supervise_plan=None,
agent_provision=_agent_provision(),
agent_provision=_agent_provision(tmp),
slug="test-00001",
container_name="bot-bottle-test-00001",
image="bot-bottle-claude:latest",
dockerfile_path="",
forwarded_env={},
prompt_file=stage / "prompt.txt",
use_runsc=False,
)
@@ -118,15 +116,12 @@ def _smolmachines_plan(spec: BottleSpec, tmp: str) -> SmolmachinesBottlePlan:
git_gate_plan=_git_gate_plan(tmp),
egress_plan=_egress_plan(tmp),
supervise_plan=None,
agent_provision=_agent_provision(),
agent_provision=_agent_provision(tmp),
slug="test-00001",
bundle_subnet="10.99.0.0/24",
bundle_gateway="10.99.0.1",
bundle_ip="10.99.0.2",
machine_name="bot-bottle-test-00001",
agent_image="bot-bottle-claude:latest",
guest_env={"HTTPS_PROXY": "http://127.0.0.1:9999"},
prompt_file=stage / "prompt.txt",
)
+5 -4
View File
@@ -146,10 +146,7 @@ def _plan(
bundle_subnet="192.168.50.0/24",
bundle_gateway="192.168.50.1",
bundle_ip=bundle_ip,
machine_name="bot-bottle-demo-abc12",
agent_image="bot-bottle-claude:latest",
guest_env=dict(guest_env or {}),
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
git_gate_plan=GitGatePlan(
slug="demo-abc12",
entrypoint_script=Path("/tmp/git-gate-entrypoint.sh"),
@@ -186,9 +183,11 @@ def _agent_provision(
template=template,
command=template,
prompt_mode="append_file",
image="",
image="bot-bottle-claude:latest",
dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
guest_env=dict(guest_env or {}),
)
auth_dir = (guest_env or {}).get("CODEX_HOME", "/home/node/.codex")
@@ -227,6 +226,8 @@ def _agent_provision(
image="bot-bottle-codex:latest",
dockerfile="",
guest_home="/home/node",
instance_name="bot-bottle-demo-abc12",
prompt_file=Path("/tmp/state/demo-abc12/agent/prompt.txt"),
guest_env=dict(guest_env or {}),
dirs=(AgentProvisionDir(auth_dir),),
files=tuple(files),