refactor(preflight): compact summary — agent / env / skills / bottle / gates
Trim the y/N preflight to the parts the operator actually scans
before pressing y:
agent
env (one per line)
skills (one per line)
bottle
git gate (one upstream per line)
egress-proxy (one route per line, with [auth:scheme] when set)
Dropped from the display (still on the plan dataclass / json
output for tooling): image, dockerfile, derived-image (cwd) line,
container, stage dir, docker runtime, git remotes list, egress
allowlist summary, tls interception note, supervise note, prompt
metadata, remote-control flag.
`remote_control` kwarg kept on `.print()` for callsite stability
but unused in the compact format.
A `_multi(label, values)` helper does the "first value next to
the label, remainder continuation-indented" pattern that env /
skills / git gate / egress-proxy all share — keeps the columns
aligned to the label width.
Verified against my own dev bottle: output is byte-for-byte the
spec the operator asked for.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -87,70 +87,46 @@ class DockerBottlePlan(BottlePlan):
|
||||
)
|
||||
|
||||
def print(self, *, remote_control: bool) -> None:
|
||||
"""Render the y/N preflight summary to stderr. Pure presentation."""
|
||||
"""Render the y/N preflight summary to stderr — compact form
|
||||
intended to fit on screen without scrolling. The full
|
||||
structured shape (image, container, runtime, etc.) is
|
||||
available via `to_dict` + `--format=json` for tooling /
|
||||
debugging."""
|
||||
del remote_control # not surfaced in the compact summary
|
||||
v = self._view()
|
||||
spec = self.spec
|
||||
runtime_label = "runsc (gVisor)" if self.use_runsc else "runc (default)"
|
||||
|
||||
def _multi(label: str, values: list[str]) -> None:
|
||||
"""Print a label with N continuation-indented values. Used
|
||||
for env / skills / git-gate / egress-proxy where one item
|
||||
per line keeps the summary scannable."""
|
||||
if not values:
|
||||
info(f"{label}: (none)")
|
||||
return
|
||||
info(f"{label}: {values[0]}")
|
||||
indent = " " * (len(label) + 2)
|
||||
for v_ in values[1:]:
|
||||
info(f"{indent}{v_}")
|
||||
|
||||
print(file=sys.stderr)
|
||||
info(f"agent : {spec.agent_name}")
|
||||
info(f"image : {self.image}")
|
||||
if self.dockerfile_path:
|
||||
info(
|
||||
f"dockerfile : {self.dockerfile_path} "
|
||||
f"(per-bottle override from PRD 0016 capability rebuild)"
|
||||
)
|
||||
if self.derived_image:
|
||||
info(
|
||||
f"cwd : {spec.user_cwd} -> /home/node/workspace "
|
||||
f"(derived: {self.derived_image})"
|
||||
)
|
||||
info(f"container : {self.container_name}")
|
||||
info(f"stage dir : {self.stage_dir}")
|
||||
info("env (names only): " + (", ".join(v.env_names) if v.env_names else "(none)"))
|
||||
info("skills : " + (" ".join(v.agent.skills) if v.agent.skills else "(none)"))
|
||||
info(f"docker runtime : {runtime_label}")
|
||||
info(f"bottle : {v.agent.bottle}")
|
||||
if v.git_names:
|
||||
info(f" git remotes : {', '.join(v.git_names)}")
|
||||
git_lines = [
|
||||
f"{u.name} -> {u.upstream_host}:{u.upstream_port} "
|
||||
f"(gitleaks-scanned)"
|
||||
for u in self.git_gate_plan.upstreams
|
||||
]
|
||||
info(f" git gate : {'; '.join(git_lines)}")
|
||||
else:
|
||||
info(" git remotes : (none)")
|
||||
_multi("env ", v.env_names)
|
||||
_multi("skills ", list(v.agent.skills))
|
||||
info(f"bottle : {v.agent.bottle}")
|
||||
|
||||
git_lines = [
|
||||
f"{u.upstream_host}:{u.upstream_port}"
|
||||
for u in self.git_gate_plan.upstreams
|
||||
]
|
||||
if git_lines:
|
||||
_multi(" git gate ", git_lines)
|
||||
|
||||
if self.egress_proxy_plan.routes:
|
||||
lines = []
|
||||
egress_lines = []
|
||||
for r in self.egress_proxy_plan.routes:
|
||||
paths = (
|
||||
" " + ",".join(r.path_allowlist) if r.path_allowlist else ""
|
||||
)
|
||||
auth = f" [auth:{r.auth_scheme}]" if r.auth_scheme else ""
|
||||
lines.append(f"{r.host}{auth}{paths}")
|
||||
refs = sorted({r.token_ref for r in self.egress_proxy_plan.routes if r.token_ref})
|
||||
tokens_part = (
|
||||
f"; tokens: {', '.join(refs)}" if refs else ""
|
||||
)
|
||||
info(f" egress-proxy : {len(lines)} route(s){tokens_part}")
|
||||
for line in lines:
|
||||
info(f" {line}")
|
||||
else:
|
||||
info(" egress-proxy : (none)")
|
||||
info(f" egress : {self.allowlist_summary}")
|
||||
info(" tls intercept : egress-proxy (per-bottle ephemeral CA, generated at launch)")
|
||||
if self.supervise_plan is not None:
|
||||
info(
|
||||
f" supervise : enabled; queue at {self.supervise_plan.queue_dir}"
|
||||
)
|
||||
else:
|
||||
info(" supervise : disabled (set bottle.supervise=true to enable)")
|
||||
info(
|
||||
f"prompt : {len(v.agent.prompt)} chars; "
|
||||
f"first line: {v.prompt_first_line or '(empty)'}"
|
||||
)
|
||||
info("remote-control : " + ("enabled" if remote_control else "disabled"))
|
||||
egress_lines.append(f"{r.host}{auth}")
|
||||
_multi(" egress-proxy ", egress_lines)
|
||||
print(file=sys.stderr)
|
||||
|
||||
def to_dict(self, *, remote_control: bool) -> dict[str, object]:
|
||||
|
||||
Reference in New Issue
Block a user