fix: restore runtime workspace provisioning
This commit is contained in:
@@ -32,6 +32,7 @@ manifest does not carry a backend field; the host picks.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from contextlib import AbstractContextManager
|
||||
@@ -47,7 +48,7 @@ from ..manifest import ManifestGitEntry, Manifest
|
||||
from ..supervise import SupervisePlan
|
||||
from ..util import expand_tilde
|
||||
from ..env import resolve_env, ResolvedEnv
|
||||
# from ..workspace import WorkspacePlan
|
||||
from ..workspace import WorkspacePlan, workspace_plan
|
||||
from .print_util import print_multi, visible_agent_env_names
|
||||
from .util import host_skill_dir
|
||||
|
||||
@@ -101,7 +102,10 @@ class BottlePlan(ABC):
|
||||
egress_plan: EgressPlan
|
||||
supervise_plan: SupervisePlan | None
|
||||
agent_provision: AgentProvisionPlan
|
||||
# workspace_plan: WorkspacePlan
|
||||
|
||||
@property
|
||||
def workspace_plan(self) -> WorkspacePlan:
|
||||
return workspace_plan(self.spec, guest_home=self.guest_home)
|
||||
|
||||
def print(self, *, remote_control: bool) -> None:
|
||||
"""Render the y/N preflight summary to stderr."""
|
||||
@@ -293,6 +297,7 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
manifest_agent_provider = manifest_bottle.agent_provider
|
||||
agent_provider = get_provider(manifest_agent_provider.template)
|
||||
resolved_env = resolve_env(manifest, spec.agent_name)
|
||||
workspace = workspace_plan(spec, guest_home=agent_provider.guest_home)
|
||||
|
||||
slug = mint_slug(spec)
|
||||
write_launch_metadata(slug, spec, compose_project="", backend=self.name)
|
||||
@@ -319,7 +324,7 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
forward_host_credentials=manifest_agent_provider.forward_host_credentials,
|
||||
auth_token=manifest_agent_provider.auth_token,
|
||||
host_env=dict(os.environ),
|
||||
# trusted_project_path=workspace_plan.workdir,
|
||||
trusted_project_path=workspace.workdir,
|
||||
label=spec.label,
|
||||
color=spec.color,
|
||||
)
|
||||
@@ -448,7 +453,7 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
prompt_path = provider.provision_prompt(plan, bottle)
|
||||
provider.provision(plan, bottle)
|
||||
provider.provision_skills(plan, bottle)
|
||||
# self.provision_workspace(plan, bottle)
|
||||
self.provision_workspace(plan, bottle)
|
||||
provider.provision_git(bottle, plan)
|
||||
provider.provision_supervise_mcp(
|
||||
plan, bottle, self.supervise_mcp_url(plan),
|
||||
@@ -456,9 +461,30 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
return prompt_path
|
||||
|
||||
def provision_workspace(self, plan: PlanT, bottle: "Bottle") -> None:
|
||||
"""Copy the operator workspace into the running bottle when
|
||||
the backend cannot bake it into the agent image. Default is
|
||||
no-op for backends like Docker that handle this before launch."""
|
||||
"""Copy the operator workspace into the running bottle.
|
||||
|
||||
This is the only supported workspace-provisioning path: Docker
|
||||
does not build a derived image containing the current
|
||||
workspace."""
|
||||
workspace = plan.workspace_plan
|
||||
if not (workspace.enabled and workspace.copy_contents):
|
||||
return
|
||||
|
||||
guest_parent = workspace.guest_path.rsplit("/", 1)[0] or "/"
|
||||
guest_path = shlex.quote(workspace.guest_path)
|
||||
guest_parent = shlex.quote(guest_parent)
|
||||
owner = shlex.quote(workspace.owner)
|
||||
mode = shlex.quote(workspace.mode)
|
||||
info(f"copying {workspace.host_path} -> {bottle.name}:{workspace.guest_path}")
|
||||
bottle.exec(
|
||||
f"rm -rf {guest_path} && mkdir -p {guest_parent}",
|
||||
user="root",
|
||||
)
|
||||
bottle.cp_in(str(workspace.host_path), workspace.guest_path)
|
||||
bottle.exec(
|
||||
f"chown -R {owner} {guest_path} && chmod {mode} {guest_path}",
|
||||
user="root",
|
||||
)
|
||||
|
||||
def supervise_mcp_url(self, plan: PlanT) -> str:
|
||||
"""Return the agent-side URL of the per-bottle supervise
|
||||
|
||||
@@ -4,8 +4,8 @@ PRD 0018 chunk 3: each instance is one `docker compose` project.
|
||||
|
||||
The flow is:
|
||||
|
||||
1. Build the agent's base + derived image (compose builds the
|
||||
sidecar images via the `build:` directive on first up).
|
||||
1. Build the agent image from the provider Dockerfile (compose
|
||||
builds the sidecar images via the `build:` directive on first up).
|
||||
2. Mint the per-bottle egress CA (chunk 2 writes it under
|
||||
state/<slug>/egress/).
|
||||
3. Populate the inner plans with launch-time fields so the
|
||||
@@ -15,8 +15,8 @@ The flow is:
|
||||
7. `docker compose up -d` (token + OAuth values flow into the
|
||||
compose subprocess env so `environment: [NAME]` bare-name
|
||||
entries inherit without rendering values into the file).
|
||||
8. Provision (CA install, prompt copy, skills, git, supervise
|
||||
config) — unchanged, uses `docker exec`.
|
||||
8. Provision (CA install, prompt copy, skills, workspace, git,
|
||||
supervise config) — unchanged, uses `docker exec` / `docker cp`.
|
||||
9. Yield a DockerBottle handle. `exec_agent` runs claude via
|
||||
`docker exec -it` exactly like the pre-compose world.
|
||||
|
||||
|
||||
@@ -56,5 +56,4 @@ def resolve_plan(
|
||||
supervise_plan=supervise_plan,
|
||||
use_runsc=use_runsc,
|
||||
agent_provision=agent_provision_plan,
|
||||
# workspace_plan=workspace_plan,
|
||||
)
|
||||
|
||||
@@ -27,7 +27,6 @@ from . import smolvm as _smolvm
|
||||
from .bottle import SmolmachinesBottle
|
||||
from .bottle_cleanup_plan import SmolmachinesBottleCleanupPlan
|
||||
from .bottle_plan import SmolmachinesBottlePlan
|
||||
# from .provision import workspace as _workspace
|
||||
|
||||
|
||||
class SmolmachinesBottleBackend(
|
||||
@@ -82,11 +81,6 @@ class SmolmachinesBottleBackend(
|
||||
with _launch.launch(plan, provision=self.provision) as bottle:
|
||||
yield bottle
|
||||
|
||||
# def provision_workspace(
|
||||
# self, plan: SmolmachinesBottlePlan, bottle: Bottle
|
||||
# ) -> None:
|
||||
# _workspace.provision_workspace(plan, bottle)
|
||||
|
||||
def supervise_mcp_url(self, plan: SmolmachinesBottlePlan) -> str:
|
||||
"""The smolmachines guest reaches the supervise sidecar via a
|
||||
host-published random port the launch step pinned earlier
|
||||
|
||||
@@ -6,9 +6,7 @@ the `AgentProvider` plugin under `bot_bottle/contrib/`. CA and git
|
||||
provisioning also moved to the AgentProvider ABC (with Debian/node
|
||||
defaults); user plugins override them for non-standard images.
|
||||
|
||||
The module left in this subpackage handles the remaining backend-
|
||||
specific step:
|
||||
|
||||
- workspace.py — copy the operator workspace into the guest
|
||||
(currently commented out — workspace planning is disabled)
|
||||
No modules remain in this subpackage. Workspace copying now runs
|
||||
through `BottleBackend.provision_workspace` against the running
|
||||
bottle for every backend.
|
||||
"""
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
"""Copy the operator workspace into a smolmachines guest.
|
||||
|
||||
DISABLED — workspace planning is currently commented out at the
|
||||
BottlePlan level. This module is kept as a placeholder for when
|
||||
workspace support is re-enabled.
|
||||
"""
|
||||
|
||||
# from __future__ import annotations
|
||||
#
|
||||
# import shlex
|
||||
#
|
||||
# from ....log import info
|
||||
# from ... import Bottle
|
||||
# from ..bottle_plan import SmolmachinesBottlePlan
|
||||
#
|
||||
#
|
||||
# def provision_workspace(plan: SmolmachinesBottlePlan, bottle: Bottle) -> None:
|
||||
# """Copy host cwd contents to the planned guest workspace."""
|
||||
# workspace = plan.workspace_plan
|
||||
# if not (workspace.enabled and workspace.copy_contents):
|
||||
# return
|
||||
#
|
||||
# guest_parent = workspace.guest_path.rsplit("/", 1)[0] or "/"
|
||||
# guest_path_q = shlex.quote(workspace.guest_path)
|
||||
# guest_parent_q = shlex.quote(guest_parent)
|
||||
# owner_q = shlex.quote(workspace.owner)
|
||||
# mode_q = shlex.quote(workspace.mode)
|
||||
# info(f"copying {workspace.host_path} -> {bottle.name}:{workspace.guest_path}")
|
||||
# bottle.exec(
|
||||
# f"rm -rf {guest_path_q} && mkdir -p {guest_parent_q}",
|
||||
# user="root",
|
||||
# )
|
||||
# bottle.cp_in(str(workspace.host_path), workspace.guest_path)
|
||||
# bottle.exec(
|
||||
# f"chown -R {owner_q} {guest_path_q} && chmod {mode_q} {guest_path_q}",
|
||||
# user="root",
|
||||
# )
|
||||
@@ -18,8 +18,6 @@ from ...agent_provider import AgentProvisionPlan
|
||||
from ...egress import EgressPlan
|
||||
from ...supervise import SupervisePlan
|
||||
from ...git_gate import GitGatePlan
|
||||
|
||||
# from ...workspace import workspace_plan as resolve_workspace_plan
|
||||
from .bottle_plan import SmolmachinesBottlePlan
|
||||
from .util import smolmachines_bundle_subnet, smolmachines_preflight
|
||||
|
||||
@@ -79,5 +77,4 @@ def resolve_plan(
|
||||
egress_plan=egress_plan,
|
||||
supervise_plan=supervise_plan,
|
||||
agent_provision=agent_provision_plan,
|
||||
# workspace_plan=workspace_plan,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user