fix(codex): provision dummy user auth state
This commit is contained in:
@@ -286,6 +286,7 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
intercepted without per-tool reconfiguration."""
|
||||
self.provision_ca(plan, target)
|
||||
prompt_path = self.provision_prompt(plan, target)
|
||||
self.provision_provider_auth(plan, target)
|
||||
self.provision_skills(plan, target)
|
||||
self.provision_git(plan, target)
|
||||
self.provision_supervise(plan, target)
|
||||
@@ -300,6 +301,11 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
backend overrides to docker-cp the cert in and run
|
||||
`update-ca-certificates`."""
|
||||
|
||||
def provision_provider_auth(self, plan: PlanT, target: str) -> None:
|
||||
"""Install non-secret provider auth marker files into the agent
|
||||
home when a provider needs them to select the right auth mode.
|
||||
The default is no-op."""
|
||||
|
||||
@abstractmethod
|
||||
def provision_prompt(self, plan: PlanT, target: str) -> str | None:
|
||||
"""Copy the prompt file into the running bottle. Returns the
|
||||
|
||||
@@ -29,6 +29,7 @@ from .bottle_plan import DockerBottlePlan
|
||||
from .provision import ca as _ca
|
||||
from .provision import git as _git
|
||||
from .provision import prompt as _prompt
|
||||
from .provision import provider_auth as _provider_auth
|
||||
from .provision import skills as _skills
|
||||
from .provision import supervise as _supervise_prov
|
||||
|
||||
@@ -62,6 +63,9 @@ class DockerBottleBackend(BottleBackend["DockerBottlePlan", "DockerBottleCleanup
|
||||
def provision_prompt(self, plan: DockerBottlePlan, target: str) -> str | None:
|
||||
return _prompt.provision_prompt(plan, target)
|
||||
|
||||
def provision_provider_auth(self, plan: DockerBottlePlan, target: str) -> None:
|
||||
_provider_auth.provision_provider_auth(plan, target)
|
||||
|
||||
def provision_skills(self, plan: DockerBottlePlan, target: str) -> None:
|
||||
_skills.provision_skills(plan, target)
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ class DockerBottlePlan(BottlePlan):
|
||||
agent_command: str = "claude"
|
||||
agent_prompt_mode: PromptMode = "append_file"
|
||||
agent_provider_template: str = "claude"
|
||||
codex_auth_file: Path | None = None
|
||||
|
||||
def print(self, *, remote_control: bool) -> None:
|
||||
"""Render the y/N preflight summary to stderr — compact form
|
||||
|
||||
@@ -15,6 +15,7 @@ from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from ...agent_provider import runtime_for
|
||||
from ...codex_auth import write_codex_dummy_auth_file
|
||||
from ...egress import Egress
|
||||
from ...env import ResolvedEnv, resolve_env
|
||||
from ...git_gate import GitGate
|
||||
@@ -155,6 +156,7 @@ def resolve_plan(
|
||||
agent_dir.mkdir(parents=True, exist_ok=True)
|
||||
env_file = agent_dir / "agent.env"
|
||||
prompt_file = agent_dir / "prompt.txt"
|
||||
codex_auth_file = agent_dir / "codex-auth.json"
|
||||
prompt_file.write_text("")
|
||||
prompt_file.chmod(0o600)
|
||||
|
||||
@@ -219,6 +221,8 @@ def resolve_plan(
|
||||
# error reporting) that egress can't gate by auth.
|
||||
forwarded_env.setdefault("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1")
|
||||
forwarded_env.setdefault("DISABLE_ERROR_REPORTING", "1")
|
||||
if provider.forward_host_credentials:
|
||||
write_codex_dummy_auth_file(codex_auth_file, dict(os.environ))
|
||||
_write_env_file(resolved, env_file)
|
||||
prompt_file.write_text(agent.prompt)
|
||||
|
||||
@@ -245,6 +249,7 @@ def resolve_plan(
|
||||
agent_command=provider_runtime.command,
|
||||
agent_prompt_mode=provider_runtime.prompt_mode,
|
||||
agent_provider_template=provider.template,
|
||||
codex_auth_file=codex_auth_file if provider.forward_host_credentials else None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Provision non-secret provider auth markers into a Docker bottle."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from ..bottle_plan import DockerBottlePlan
|
||||
|
||||
|
||||
def provision_provider_auth(plan: DockerBottlePlan, target: str) -> None:
|
||||
"""Copy a dummy Codex auth marker when host credentials are
|
||||
forwarded through egress.
|
||||
|
||||
The file contains no real access or refresh token values; it only
|
||||
nudges Codex into the same user/device auth branch as the host.
|
||||
"""
|
||||
if not plan.codex_auth_file:
|
||||
return
|
||||
container_home = os.environ.get("BOT_BOTTLE_CONTAINER_HOME", "/home/node")
|
||||
auth_dir = f"{container_home}/.codex"
|
||||
auth_path = f"{auth_dir}/auth.json"
|
||||
|
||||
subprocess.run(
|
||||
["docker", "exec", "-u", "0", target, "mkdir", "-p", auth_dir],
|
||||
stdout=subprocess.DEVNULL,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["docker", "cp", str(plan.codex_auth_file), f"{target}:{auth_path}"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["docker", "exec", "-u", "0", target, "chown", "node:node", auth_path],
|
||||
stdout=subprocess.DEVNULL,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["docker", "exec", "-u", "0", target, "chmod", "600", auth_path],
|
||||
stdout=subprocess.DEVNULL,
|
||||
check=True,
|
||||
)
|
||||
@@ -19,6 +19,7 @@ from .bottle_plan import SmolmachinesBottlePlan
|
||||
from .provision import ca as _ca
|
||||
from .provision import git as _git
|
||||
from .provision import prompt as _prompt
|
||||
from .provision import provider_auth as _provider_auth
|
||||
from .provision import skills as _skills
|
||||
from .provision import supervise as _supervise
|
||||
|
||||
@@ -61,6 +62,11 @@ class SmolmachinesBottleBackend(
|
||||
) -> str | None:
|
||||
return _prompt.provision_prompt(plan, target)
|
||||
|
||||
def provision_provider_auth(
|
||||
self, plan: SmolmachinesBottlePlan, target: str
|
||||
) -> None:
|
||||
_provider_auth.provision_provider_auth(plan, target)
|
||||
|
||||
def provision_skills(
|
||||
self, plan: SmolmachinesBottlePlan, target: str
|
||||
) -> None:
|
||||
|
||||
@@ -97,6 +97,7 @@ class SmolmachinesBottlePlan(BottlePlan):
|
||||
agent_prompt_mode: PromptMode = "append_file"
|
||||
agent_provider_template: str = "claude"
|
||||
agent_dockerfile_path: str = ""
|
||||
codex_auth_file: Path | None = None
|
||||
|
||||
def print(self, *, remote_control: bool) -> None:
|
||||
"""Compact y/N preflight. Same shape as the Docker
|
||||
|
||||
@@ -16,6 +16,7 @@ from pathlib import Path
|
||||
|
||||
from ...agent_provider import runtime_for
|
||||
from ...backend import BottleSpec
|
||||
from ...codex_auth import write_codex_dummy_auth_file
|
||||
from ...backend.docker.bottle_state import (
|
||||
BottleMetadata,
|
||||
agent_state_dir,
|
||||
@@ -144,9 +145,12 @@ def resolve_plan(
|
||||
agent_dir = agent_state_dir(slug)
|
||||
agent_dir.mkdir(parents=True, exist_ok=True)
|
||||
prompt_file = agent_dir / "prompt.txt"
|
||||
codex_auth_file = agent_dir / "codex-auth.json"
|
||||
agent = manifest.agents[spec.agent_name]
|
||||
prompt_file.write_text(agent.prompt or "")
|
||||
prompt_file.chmod(0o600)
|
||||
if provider.forward_host_credentials:
|
||||
write_codex_dummy_auth_file(codex_auth_file, dict(os.environ))
|
||||
|
||||
machine_name = f"bot-bottle-{slug}"
|
||||
# Stash the agent image ref — `launch.launch` runs the
|
||||
@@ -182,6 +186,7 @@ def resolve_plan(
|
||||
agent_prompt_mode=provider_runtime.prompt_mode,
|
||||
agent_provider_template=provider.template,
|
||||
agent_dockerfile_path=agent_dockerfile_path,
|
||||
codex_auth_file=codex_auth_file if provider.forward_host_credentials else None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Provision non-secret provider auth markers into a smolmachines bottle."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from .. import smolvm as _smolvm
|
||||
from ..bottle_plan import SmolmachinesBottlePlan
|
||||
|
||||
|
||||
_DEFAULT_GUEST_HOME = "/home/node"
|
||||
|
||||
|
||||
def provision_provider_auth(plan: SmolmachinesBottlePlan, target: str) -> None:
|
||||
"""Copy a dummy Codex auth marker when host credentials are
|
||||
forwarded through egress.
|
||||
|
||||
The real host access token remains in the egress bundle env; this
|
||||
file only selects Codex's user/device auth code path.
|
||||
"""
|
||||
if not plan.codex_auth_file:
|
||||
return
|
||||
guest_home = os.environ.get("BOT_BOTTLE_GUEST_HOME", _DEFAULT_GUEST_HOME)
|
||||
auth_dir = f"{guest_home}/.codex"
|
||||
auth_path = f"{auth_dir}/auth.json"
|
||||
|
||||
_smolvm.machine_exec(target, ["mkdir", "-p", auth_dir])
|
||||
_smolvm.machine_cp(str(plan.codex_auth_file), f"{target}:{auth_path}")
|
||||
_smolvm.machine_exec(target, ["chown", "node:node", auth_path])
|
||||
_smolvm.machine_exec(target, ["chmod", "600", auth_path])
|
||||
Reference in New Issue
Block a user