From c8ab0c67a89205ad451b8f02a15e5d2d1243ca91 Mon Sep 17 00:00:00 2001 From: codex Date: Mon, 1 Jun 2026 22:32:03 +0000 Subject: [PATCH] refactor(agent): surface provider env defaults --- bot_bottle/agent_provider.py | 12 ++++++----- bot_bottle/backend/docker/prepare.py | 5 +++++ bot_bottle/backend/smolmachines/prepare.py | 23 +++++----------------- tests/unit/test_agent_provider.py | 4 +++- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/bot_bottle/agent_provider.py b/bot_bottle/agent_provider.py index 354f479..26b4dac 100644 --- a/bot_bottle/agent_provider.py +++ b/bot_bottle/agent_provider.py @@ -8,7 +8,7 @@ command, default image, and prompt/auth behavior. from __future__ import annotations import os -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import Literal @@ -71,6 +71,7 @@ class AgentProvisionPlan: image: str dockerfile: str guest_env: dict[str, str] + env_vars: dict[str, str] = field(default_factory=dict) dirs: tuple[AgentProvisionDir, ...] = () files: tuple[AgentProvisionFile, ...] = () pre_copy: tuple[AgentProvisionCommand, ...] = () @@ -124,17 +125,17 @@ def agent_provision_plan( ) -> AgentProvisionPlan: runtime = runtime_for(template) resolved_guest_env = dict(guest_env or {}) + env_vars: dict[str, str] = {} dirs: list[AgentProvisionDir] = [] files: list[AgentProvisionFile] = [] pre_copy: list[AgentProvisionCommand] = [] verify: list[AgentProvisionCommand] = [] if template == PROVIDER_CODEX: - resolved_guest_env.setdefault( - "CODEX_CA_CERTIFICATE", - "/etc/ssl/certs/ca-certificates.crt", - ) + env_vars["CODEX_CA_CERTIFICATE"] = "/etc/ssl/certs/ca-certificates.crt" auth_dir = resolved_guest_env.get("CODEX_HOME", f"{guest_home}/.codex") + if forward_host_credentials: + env_vars["CODEX_HOME"] = auth_dir dirs.append(AgentProvisionDir(auth_dir)) config_path = f"{auth_dir}/config.toml" config_file = state_dir / "codex-config.toml" @@ -177,6 +178,7 @@ def agent_provision_plan( prompt_mode=runtime.prompt_mode, image=runtime.image, dockerfile=dockerfile, + env_vars=env_vars, guest_env=resolved_guest_env, dirs=tuple(dirs), files=tuple(files), diff --git a/bot_bottle/backend/docker/prepare.py b/bot_bottle/backend/docker/prepare.py index 9c1452e..a15add7 100644 --- a/bot_bottle/backend/docker/prepare.py +++ b/bot_bottle/backend/docker/prepare.py @@ -12,6 +12,7 @@ from __future__ import annotations import os from datetime import datetime, timezone +from dataclasses import replace from pathlib import Path from ...agent_provider import agent_provision_plan, runtime_for @@ -231,6 +232,10 @@ def resolve_plan( forward_host_credentials=provider.forward_host_credentials, host_env=dict(os.environ), ) + guest_env = dict(agent_provision.guest_env) + for key, val in agent_provision.env_vars.items(): + guest_env.setdefault(key, val) + agent_provision = replace(agent_provision, guest_env=guest_env) return DockerBottlePlan( spec=spec, diff --git a/bot_bottle/backend/smolmachines/prepare.py b/bot_bottle/backend/smolmachines/prepare.py index c07442c..1952d6f 100644 --- a/bot_bottle/backend/smolmachines/prepare.py +++ b/bot_bottle/backend/smolmachines/prepare.py @@ -12,6 +12,7 @@ from __future__ import annotations import os from datetime import datetime, timezone +from dataclasses import replace from pathlib import Path from ...agent_provider import agent_provision_plan, runtime_for @@ -128,24 +129,6 @@ def resolve_plan( if provider.template == "claude" and has_provider_auth: guest_env.setdefault("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1") guest_env.setdefault("DISABLE_ERROR_REPORTING", "1") - if provider.template == "codex": - # Codex is a Rust/rustls client: unlike the Node agents it does - # NOT consult the system trust store or honor NODE_EXTRA_CA_CERTS. - # It reads CODEX_CA_CERTIFICATE (falling back to SSL_CERT_FILE) - # for custom roots, across HTTPS *and* the wss responses channel. - # Point it at the bundle update-ca-certificates rebuilt with the - # egress MITM CA so Codex trusts the proxy and egress can inject - # the host bearer — without this, codex bottles need - # pipelock tls_passthrough, which disables auth injection. - guest_env["CODEX_CA_CERTIFICATE"] = ( - "/etc/ssl/certs/ca-certificates.crt" - ) - if provider.template == "codex" and provider.forward_host_credentials: - # Smolvm exec process trees do not reliably inherit the image - # user's login environment. Pin CODEX_HOME to the same path - # provision_provider_auth writes so Codex never falls back to a - # root or unset home and shows the sign-in picker. - guest_env["CODEX_HOME"] = "/home/node/.codex" supervise_plan = None if bottle.supervise: @@ -189,6 +172,10 @@ def resolve_plan( forward_host_credentials=provider.forward_host_credentials, host_env=dict(os.environ), ) + merged_guest_env = dict(agent_provision.guest_env) + for key, val in agent_provision.env_vars.items(): + merged_guest_env.setdefault(key, val) + agent_provision = replace(agent_provision, guest_env=merged_guest_env) return SmolmachinesBottlePlan( spec=spec, diff --git a/tests/unit/test_agent_provider.py b/tests/unit/test_agent_provider.py index eedf1a6..72706e7 100644 --- a/tests/unit/test_agent_provider.py +++ b/tests/unit/test_agent_provider.py @@ -42,8 +42,9 @@ class TestAgentProviderRuntime(unittest.TestCase): self.assertEqual("/tmp/Dockerfile.codex", plan.dockerfile) self.assertEqual( "/etc/ssl/certs/ca-certificates.crt", - plan.guest_env["CODEX_CA_CERTIFICATE"], + plan.env_vars["CODEX_CA_CERTIFICATE"], ) + self.assertEqual({}, plan.guest_env) self.assertEqual(("/home/node/.codex",), tuple(d.guest_path for d in plan.dirs)) self.assertEqual( ("/home/node/.codex/config.toml",), @@ -70,6 +71,7 @@ class TestAgentProviderRuntime(unittest.TestCase): "/run/codex-home/auth.json", {f.guest_path for f in plan.files}, ) + self.assertEqual("/run/codex-home", plan.env_vars["CODEX_HOME"]) self.assertEqual(1, len(plan.pre_copy)) self.assertEqual(1, len(plan.verify)) self.assertIn("CODEX_HOME=/run/codex-home", plan.verify[0].argv)