Compare commits
3 Commits
58169e2ce9
...
873d75f852
| Author | SHA1 | Date | |
|---|---|---|---|
| 873d75f852 | |||
| 1bd676de06 | |||
| 0bf1532557 |
@@ -23,7 +23,7 @@ class DockerBottle(Bottle):
|
||||
):
|
||||
self.name = container
|
||||
self._teardown = teardown
|
||||
self._prompt_path = prompt_path_in_container
|
||||
self.prompt_path = prompt_path_in_container
|
||||
self._agent_prompt_mode = agent_prompt_mode
|
||||
self.agent_command = agent_command
|
||||
self.agent_provider_template = (
|
||||
@@ -36,7 +36,7 @@ class DockerBottle(Bottle):
|
||||
) -> list[str]:
|
||||
full_argv = list(argv)
|
||||
full_argv.extend(
|
||||
prompt_args(self._agent_prompt_mode, self._prompt_path, argv=full_argv)
|
||||
prompt_args(self._agent_prompt_mode, self.prompt_path, argv=full_argv)
|
||||
)
|
||||
cmd = ["docker", "exec"]
|
||||
if tty:
|
||||
|
||||
@@ -80,7 +80,7 @@ _REPO_DIR = str(Path(__file__).resolve().parent.parent.parent.parent)
|
||||
def launch(
|
||||
plan: DockerBottlePlan,
|
||||
*,
|
||||
provision: Callable[[DockerBottlePlan, str], str | None],
|
||||
provision: Callable[[DockerBottlePlan, "DockerBottle"], str | None],
|
||||
) -> Generator[DockerBottle, None, None]:
|
||||
"""Build, launch, and provision a Docker bottle via compose.
|
||||
Teardown on exit."""
|
||||
@@ -218,7 +218,7 @@ def launch(
|
||||
agent_command=plan.agent_command,
|
||||
agent_prompt_mode=plan.agent_prompt_mode,
|
||||
)
|
||||
bottle._prompt_path = provision(plan, bottle)
|
||||
bottle.prompt_path = provision(plan, bottle)
|
||||
|
||||
# Step 9: yield. exec_agent continues to use `docker exec -it`
|
||||
# — the agent runs `sleep infinity` per the renderer's
|
||||
|
||||
@@ -72,7 +72,7 @@ class SmolmachinesBottle(Bottle):
|
||||
# In-VM path to the agent's prompt file. None when the
|
||||
# agent declared no prompt (file still exists; we just
|
||||
# don't pass --append-system-prompt-file).
|
||||
self._prompt_path = prompt_path
|
||||
self.prompt_path = prompt_path
|
||||
# Env vars the agent process needs (HTTPS_PROXY,
|
||||
# CLAUDE_CODE_OAUTH_TOKEN, manifest-declared bottle env, …).
|
||||
# Forwarded on every `smolvm machine exec` via `-e K=V`
|
||||
@@ -93,7 +93,7 @@ class SmolmachinesBottle(Bottle):
|
||||
agent_tail = ["env", *_env_assignments_for("node", self._guest_env),
|
||||
self.agent_command]
|
||||
provider_prompt_args = prompt_args(
|
||||
self._agent_prompt_mode, self._prompt_path, argv=argv,
|
||||
self._agent_prompt_mode, self.prompt_path, argv=argv,
|
||||
)
|
||||
if self._agent_prompt_mode == "read_prompt_file":
|
||||
agent_tail += argv
|
||||
|
||||
@@ -89,7 +89,7 @@ _SUPERVISE_PORT = SUPERVISE_PORT
|
||||
def launch(
|
||||
plan: SmolmachinesBottlePlan,
|
||||
*,
|
||||
provision: Callable[[SmolmachinesBottlePlan, str], str | None],
|
||||
provision: Callable[[SmolmachinesBottlePlan, "SmolmachinesBottle"], str | None],
|
||||
) -> Generator[SmolmachinesBottle, None, None]:
|
||||
"""Build + run the bottle and yield a handle; tear everything
|
||||
down on exit. Errors during bringup unwind any partial state
|
||||
@@ -120,7 +120,7 @@ def launch(
|
||||
agent_command=plan.agent_command,
|
||||
agent_prompt_mode=plan.agent_prompt_mode,
|
||||
)
|
||||
bottle._prompt_path = provision(plan, bottle)
|
||||
bottle.prompt_path = provision(plan, bottle)
|
||||
|
||||
yield bottle
|
||||
finally:
|
||||
|
||||
@@ -42,7 +42,7 @@ import time
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterator
|
||||
from typing import Generator, Iterator
|
||||
|
||||
from ...log import die
|
||||
|
||||
@@ -98,7 +98,7 @@ class RegistryHandle:
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ephemeral_registry() -> Iterator[RegistryHandle]:
|
||||
def ephemeral_registry() -> Generator[RegistryHandle, None, None]:
|
||||
"""Bring up a per-session docker network + a `registry:2.8.3`
|
||||
container on it (published on a random host port), yield a
|
||||
`RegistryHandle`, force-remove both on exit.
|
||||
|
||||
@@ -123,7 +123,7 @@ def main(argv: list[str]) -> int:
|
||||
machine = argv[0]
|
||||
inner = argv[2:]
|
||||
|
||||
def sync(*_args) -> None:
|
||||
def sync(*_args: int) -> None:
|
||||
size = _read_winsize()
|
||||
if size is None:
|
||||
return
|
||||
|
||||
@@ -216,14 +216,14 @@ def egress_token_env_map(
|
||||
return out
|
||||
|
||||
|
||||
def _route_to_yaml_fields(r: Route) -> dict:
|
||||
def _route_to_yaml_fields(r: Route) -> dict[str, object]:
|
||||
"""Return the addon-visible fields for one route.
|
||||
|
||||
Single authoritative mapping between EgressRoute (host-side) and
|
||||
egress_addon_core.Route (sidecar-side). When a field is added to
|
||||
the addon's Route that must appear in the YAML, add it here and
|
||||
in egress_addon_core._parse_one together."""
|
||||
fields: dict = {"host": r.host}
|
||||
fields: dict[str, object] = {"host": r.host}
|
||||
if r.auth_scheme and r.token_env:
|
||||
fields["auth_scheme"] = r.auth_scheme
|
||||
fields["token_env"] = r.token_env
|
||||
@@ -252,7 +252,7 @@ def egress_render_routes(
|
||||
lines.append(f' token_env: "{f["token_env"]}"')
|
||||
if "path_allowlist" in f:
|
||||
lines.append(" path_allowlist:")
|
||||
for p in f["path_allowlist"]:
|
||||
for p in f["path_allowlist"]: # type: ignore
|
||||
lines.append(f' - "{p}"')
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
@@ -13,8 +13,15 @@ DEFAULT_WORKSPACE_MODE = "755"
|
||||
|
||||
|
||||
class WorkspaceSpec(Protocol):
|
||||
copy_cwd: bool
|
||||
user_cwd: str
|
||||
@property
|
||||
def copy_cwd(self) -> bool:
|
||||
"""Whether to copy the current working directory."""
|
||||
...
|
||||
|
||||
@property
|
||||
def user_cwd(self) -> str:
|
||||
"""The user's current working directory."""
|
||||
...
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -58,6 +58,7 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
|
||||
class YamlSubsetError(ValueError):
|
||||
@@ -283,7 +284,7 @@ def _split_flow(body: str, lineno: int, kind: str) -> list[str]:
|
||||
depth_c = 0
|
||||
in_single = False
|
||||
in_double = False
|
||||
cur = []
|
||||
cur: list[str] = []
|
||||
for ch in body:
|
||||
if ch == "'" and not in_double:
|
||||
in_single = not in_single
|
||||
@@ -330,6 +331,7 @@ def _split_key_value(content: str, lineno: int) -> tuple[str, str]:
|
||||
if i + 1 >= len(content) or content[i + 1] in (" ", "\t"):
|
||||
return content[:i].strip(), content[i + 1:].lstrip()
|
||||
die(f"yaml-subset: line {lineno} missing `: ` separator: {content!r}")
|
||||
return "", "" # unreachable, but needed for type checker
|
||||
|
||||
|
||||
def _parse_block(
|
||||
@@ -536,7 +538,7 @@ def parse_yaml_subset(text: str) -> dict[str, object]:
|
||||
)
|
||||
if not isinstance(value, dict):
|
||||
die("yaml-subset: top-level value must be a mapping")
|
||||
return value
|
||||
return cast(dict[str, object], value)
|
||||
|
||||
|
||||
def parse_frontmatter(text: str) -> tuple[dict[str, object], str]:
|
||||
|
||||
Reference in New Issue
Block a user