From c2cdb7777d1f19ca2a2876632bc8f70957c4f35f Mon Sep 17 00:00:00 2001 From: didericis Date: Mon, 11 May 2026 01:26:38 -0400 Subject: [PATCH] refactor(pipelock): prepare_proxy returns a ProxyPlan Add a frozen ProxyPlan dataclass to pipelock.py (currently one field: yaml_path; kept as a class so future proxy-level state has a home). - prepare_proxy(spec, stage_dir) now returns pipelock.ProxyPlan instead of a raw Path. - DockerBottlePlan replaces pipelock_yaml_path + pipelock_yaml_filename with a single proxy: ProxyPlan field. - launch reads plan.proxy.yaml_path.parent / .name when calling pipelock_start. Eventually pipelock_start should just take a Path but that's a separate change. --- claude_bottle/backend/docker/backend.py | 18 +++++++++--------- claude_bottle/backend/docker/bottle_plan.py | 4 ++-- claude_bottle/pipelock.py | 10 ++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/claude_bottle/backend/docker/backend.py b/claude_bottle/backend/docker/backend.py index a1f5da2..68ed110 100644 --- a/claude_bottle/backend/docker/backend.py +++ b/claude_bottle/backend/docker/backend.py @@ -101,7 +101,7 @@ class DockerBottleBackend(BottleBackend): prompt_file.write_text("") prompt_file.chmod(0o600) - pipelock_yaml = self.prepare_proxy(spec, stage_dir) + proxy_plan = self.prepare_proxy(spec, stage_dir) env_resolve(manifest, spec.agent_name, env_file, args_file) prompt_file.write_text(agent.prompt) @@ -120,20 +120,20 @@ class DockerBottleBackend(BottleBackend): env_file=env_file, args_file=args_file, prompt_file=prompt_file, - pipelock_yaml_path=pipelock_yaml, - pipelock_yaml_filename=pipelock_yaml.name, + proxy_plan=proxy_plan, allowlist_summary=allowlist_summary, use_runsc=use_runsc, ) - def prepare_proxy(self, spec: BottleSpec, stage_dir: Path) -> Path: + def prepare_proxy(self, spec: BottleSpec, stage_dir: Path) -> pipelock.ProxyPlan: """Decide where the pipelock yaml lives in `stage_dir`, delegate - to PipelockProxy to write it, and return the resolved path. - Stage-only: no Docker resources created yet.""" + to PipelockProxy to write it, and return the resolved ProxyPlan + for the launch step to consume. Stage-only: no Docker resources + created yet.""" yaml_path = stage_dir / "pipelock.yaml" bottle_name = spec.manifest.agents[spec.agent_name].bottle self._proxy.prepare(spec.manifest, bottle_name, yaml_path) - return yaml_path + return pipelock.ProxyPlan(yaml_path=yaml_path) @contextmanager def launch(self, plan: BottlePlan) -> Iterator[DockerBottle]: @@ -186,8 +186,8 @@ class DockerBottleBackend(BottleBackend): plan.slug, state["internal_network"], state["egress_network"], - plan.stage_dir, - plan.pipelock_yaml_filename, + plan.proxy_plan.yaml_path.parent, + plan.proxy_plan.yaml_path.name, ) container = self._run_agent_container(plan, state["internal_network"]) diff --git a/claude_bottle/backend/docker/bottle_plan.py b/claude_bottle/backend/docker/bottle_plan.py index 187987e..acca735 100644 --- a/claude_bottle/backend/docker/bottle_plan.py +++ b/claude_bottle/backend/docker/bottle_plan.py @@ -12,6 +12,7 @@ from dataclasses import dataclass from pathlib import Path from ...log import info +from ...pipelock import ProxyPlan from .. import BottlePlan @@ -30,8 +31,7 @@ class DockerBottlePlan(BottlePlan): env_file: Path args_file: Path prompt_file: Path - pipelock_yaml_path: Path - pipelock_yaml_filename: str + proxy_plan: ProxyPlan allowlist_summary: str use_runsc: bool diff --git a/claude_bottle/pipelock.py b/claude_bottle/pipelock.py index 10afcce..c3b3e10 100644 --- a/claude_bottle/pipelock.py +++ b/claude_bottle/pipelock.py @@ -15,6 +15,7 @@ from __future__ import annotations import os import re import subprocess +from dataclasses import dataclass from pathlib import Path from .log import die, info, warn @@ -123,6 +124,15 @@ def pipelock_allowlist_summary(manifest: Manifest, bottle_name: str) -> str: # --- Proxy class ----------------------------------------------------------- +@dataclass(frozen=True) +class ProxyPlan: + """Output of a proxy's prepare step; consumed by launch when the + proxy needs to be brought up. Currently single-field (the on-host + yaml path); kept as a dataclass so future proxy state has a home.""" + + yaml_path: Path + + class PipelockProxy: """The pipelock egress proxy. Encapsulates the YAML-config generation (and is the natural home for any future proxy-level