- Add _load_user_plugin: loads AgentProvider subclass from
~/.bot-bottle/contrib/<name>/agent_provider.py; get_provider()
checks there first before falling back to built-ins
- Add Dockerfile cascade to docker prepare: per-bottle override →
manifest dockerfile → user plugin Dockerfile → provider default
- Move provision_ca and provision_git from backend-specific
provision/ modules to AgentProvider ABC as overridable defaults;
delete docker/provision/ca.py, docker/provision/git.py,
smolmachines/provision/ca.py, smolmachines/provision/git.py
- Add git_gate_insteadof_host/scheme properties to BottlePlan base;
SmolmachinesBottlePlan overrides them to return agent_git_gate_host
and "http" so provision_git works correctly on both backends
- Move SIGKILL retry from smolmachines provision/ca.py into
SmolmachinesBottle.exec via _exec_raw helper — all exec calls
on smolmachines now transparently retry once on exit 137
- Relax manifest_agent template validation to allow user-defined
template names; keep auth_token/forward_host_credentials guards
for built-in-only features
- Update tests: rewrite test_docker_provision_git_user and
test_smolmachines_provision to call provider methods directly;
add TestSmolmachinesBottleExec for SIGKILL retry coverage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Strip pipelock from all unit and integration test fixtures:
proxy_plan fields removed from DockerBottlePlan/SmolmachinesBottlePlan
constructors; pipelock-specific test classes deleted or renamed
- Update test_sidecar_init: remove test_pipelock_loses_egress_tokens,
rename "pipelock" daemon fixtures to "git-gate" throughout
- Remove test_pipelock_binary_present_and_versioned from integration test
- Remove test_pipelock_answers_on_bundle_ip from smolmachines launch test
- Update _SANDBOX_BLOCK_MARKERS: remove "pipelock" marker (egress blocks)
- Dockerfile.sidecars: remove pipelock build stage and COPY; update layout
comments and port table
- egress_entrypoint.sh: update comments now that egress is sole proxy
- Clean up pipelock references in comments/docstrings across backend,
network, manifest, supervise, git_gate, yaml_subset, agent_provider,
sidecar_bundle, sidecar_init, egress_addon_core modules
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per PR review feedback (review #132): guest_home shouldn't be
buried inside workspace_plan / read from a hardcoded literal in
each provision module. It's a cross-cutting bottle property — the
backend's prepare step knows it, and every downstream consumer
(contrib providers, git provisioning, gitconfig path) should
read it from one place.
- Adds guest_home: str to BottlePlan base dataclass.
- Both backends' prepare steps populate plan.guest_home.
- contrib/{claude,codex}/agent_provider.py read plan.guest_home
(was plan.workspace_plan.guest_home).
- bot_bottle/backend/docker/provision/git.py reads plan.guest_home
for the gitconfig destination (was hardcoded "/home/node").
- bot_bottle/backend/smolmachines/provision/git.py drops the
_GUEST_HOME / _guest_home() helpers and reads plan.guest_home.
- Tests that construct BottlePlan subclasses directly pass
guest_home="/home/node" explicitly.
- tests/unit/test_provision_apply.py covers the new shared
apply helpers (apply_skills / apply_prompt / apply_provision)
that replace the per-backend modules deleted in the prior
commit.
- tests/unit/test_contrib_supervise_mcp.py covers both providers'
provision_supervise_mcp behavior — confirms the codex bottle
now runs `codex mcp add` symmetrically with claude.
- tests/unit/test_smolmachines_provision.py drops the four test
classes whose subjects moved (TestProvisionPrompt /
TestProvisionProviderAuth / TestProvisionSkills /
TestProvisionSupervise); the backend-side CA / git / workspace
classes stay.
- tests/unit/test_docker_provision_provider_auth.py removed; its
coverage now lives in tests/unit/test_provision_apply.py
(apply_provision is backend-agnostic, one test file suffices).
Drops the BOT_BOTTLE_CONTAINER_HOME, BOT_BOTTLE_GUEST_HOME,
BOT_BOTTLE_CONTAINER_SKILLS_DIR, and BOT_BOTTLE_GUEST_SKILLS_DIR
env knobs the deleted provision modules used to read. /home/node
is hardcoded everywhere the knobs lived; the values were
effectively constants today and removing them keeps the PRD-0050
surface area honest.
Flips PRD 0050 Status: Draft → Active. Closes#177 on merge.
BottleBackend.provision now resolves the provider plugin from the
plan and dispatches prompt / skills / declarative-apply /
supervise-mcp through it. The four hooks the docker + smolmachines
backends used to override (provision_skills, provision_prompt,
provision_provider_auth, provision_supervise) are gone — the
duplicated 50-line implementations under
backend/{docker,smolmachines}/provision/{skills,prompt,
provider_auth,supervise}.py are deleted.
Each backend gains a small supervise_mcp_url(plan) override so the
provider plugin can run `claude mcp add` / `codex mcp add`
against the right URL: docker returns
http://{SUPERVISE_HOSTNAME}:{SUPERVISE_PORT}/ on the compose
network alias; smolmachines returns plan.agent_supervise_url which
launch.py already pins to a host-loopback port.
Removes tests/unit/test_provision_supervise.py — the URL it
asserted on now lives on the backend, with no equivalent
standalone surface to test against (it's covered by the broader
plan / launch integration tests).
Closes#178.
The backend provision functions now receive a Bottle handle with
exec / cp_in methods instead of a raw target string. Provisioner
modules use bottle.exec and bottle.cp_in in place of inlined
subprocess.run(["docker", "exec"/"cp", ...]) and direct
_smolvm.machine_cp / machine_exec calls. This decouples the
provisioners from backend-specific runtime primitives so future
refactors (e.g. the supervise rework) can swap the bottle's exec
implementation without touching every provisioner.
Each launch.py constructs the Bottle handle before calling
provision so it can be passed in; provision_prompt's return value
is wired back onto the bottle's prompt path attribute after the
fact.
The two Debian-family CA-layout constants lived in
docker/provision/ca.py, which forced the smolmachines backend to
import them cross-backend (smolmachines -> docker). Move them into
the shared backend/util.py next to select_ca_cert; docker, compose,
and smolmachines now all import from there. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both backends' provision_ca duplicated _select_ca_cert and the
SHA-256 fingerprint computation verbatim. Lift them into the shared
backend/util.py as select_ca_cert + log_ca_fingerprint; docker and
smolmachines now call the shared helpers. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>