0efc07ba67
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.
45 lines
1.7 KiB
Python
45 lines
1.7 KiB
Python
"""Copy the agent prompt into a running smolmachines bottle.
|
|
|
|
The prompt file is always copied (so the in-guest path always
|
|
exists) but `--append-system-prompt-file` only fires when the
|
|
agent actually has a prompt — the return value signals which
|
|
case, mirroring the docker backend's contract.
|
|
|
|
cp_in lands files as root inside the VM; the claude
|
|
process runs as `node`, so we chown + chmod the prompt after the
|
|
copy. Same flow as the docker backend's provision_prompt."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from ... import Bottle
|
|
from ..bottle_plan import SmolmachinesBottlePlan
|
|
|
|
|
|
# `node` is the agent user from the repo Dockerfile.
|
|
# BOT_BOTTLE_GUEST_HOME mirrors the docker backend's
|
|
# BOT_BOTTLE_CONTAINER_HOME knob.
|
|
_DEFAULT_GUEST_HOME = "/home/node"
|
|
|
|
|
|
def provision_prompt(plan: SmolmachinesBottlePlan, bottle: Bottle) -> str | None:
|
|
"""Copy the prompt file into the running smolvm guest, fix
|
|
ownership/mode. Returns the in-guest path if the agent has a
|
|
non-empty prompt (drives --append-system-prompt-file), else
|
|
None. The file is copied either way so the path always
|
|
exists — mirrors the docker backend's behavior."""
|
|
guest_home = os.environ.get("BOT_BOTTLE_GUEST_HOME", _DEFAULT_GUEST_HOME)
|
|
in_guest_prompt_path = f"{guest_home}/.bot-bottle-prompt.txt"
|
|
|
|
bottle.cp_in(str(plan.prompt_file), in_guest_prompt_path)
|
|
# cp_in lands as root, source's 0o600 mode is preserved —
|
|
# node can't read its own prompt without these two.
|
|
bottle.exec(
|
|
f"chown node:node {in_guest_prompt_path} && chmod 600 {in_guest_prompt_path}",
|
|
user="root",
|
|
)
|
|
|
|
agent = plan.spec.manifest.agents[plan.spec.agent_name]
|
|
return in_guest_prompt_path if agent.prompt else None
|