d75cc9325f
test / run tests/run_tests.py (pull_request) Successful in 16s
Introduce claude_bottle/bottles/ with a Bottle Protocol and a get_bottle_factory() that dispatches on CLAUDE_BOTTLE_PLATFORM (default "docker"). Move every Docker-specific subprocess.run call from cli/start.py, plus the orchestration of build, networks, the pipelock sidecar, container launch, and per-container provisioning (prompt, skills, ssh, .git), into create_docker_bottle. Drop bottles[].runtime from the manifest schema. Auto-detect whether gVisor is registered with the daemon and pass --runtime=runsc when it is; the preflight shows the resolved runtime so the choice is visible. Manifests still carrying 'runtime' get a clear error pointing at the auto-detect behavior, rather than silent ignore. Out of scope: cli/cleanup.py and cli/list.py still call docker directly. They enumerate active bottles across the host, which is a separate concern from "create a bottle" and is left for a follow-up that introduces a list_active/cleanup primitive on the factory.
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""Per-platform bottle factories.
|
|
|
|
A bottle is a running, isolated environment with claude inside. Each
|
|
platform exposes a factory (currently only Docker) that owns the
|
|
end-to-end lifecycle: image build, container/sidecar launch, file
|
|
provisioning, and teardown.
|
|
|
|
Selection is driven by the CLAUDE_BOTTLE_PLATFORM env var (default
|
|
"docker"). Per PRD 0003 the manifest does not carry a platform field;
|
|
the host environment picks.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from contextlib import AbstractContextManager
|
|
from typing import Callable, Protocol
|
|
|
|
from ..log import die
|
|
from .docker import create_docker_bottle
|
|
|
|
|
|
class Bottle(Protocol):
|
|
"""Handle to a running bottle. Yielded by a factory's context manager.
|
|
|
|
`exec_claude` runs `claude` inside the bottle and blocks until the
|
|
session ends. `cp_in` copies a host path into the bottle. `close`
|
|
is an idempotent alias for context-manager teardown.
|
|
"""
|
|
|
|
name: str
|
|
|
|
def exec_claude(self, argv: list[str], *, tty: bool = True) -> int: ...
|
|
def cp_in(self, host_path: str, container_path: str) -> None: ...
|
|
def close(self) -> None: ...
|
|
|
|
|
|
BottleFactory = Callable[..., AbstractContextManager[Bottle]]
|
|
|
|
|
|
_FACTORIES: dict[str, BottleFactory] = {
|
|
"docker": create_docker_bottle,
|
|
}
|
|
|
|
|
|
def get_bottle_factory() -> BottleFactory:
|
|
"""Resolve the bottle factory for the active platform. Dies with a
|
|
pointer at the known platforms if CLAUDE_BOTTLE_PLATFORM names an
|
|
unimplemented one."""
|
|
name = os.environ.get("CLAUDE_BOTTLE_PLATFORM", "docker")
|
|
if name not in _FACTORIES:
|
|
known = ", ".join(sorted(_FACTORIES))
|
|
die(f"unknown CLAUDE_BOTTLE_PLATFORM={name!r}; known platforms: {known}")
|
|
return _FACTORIES[name]
|