container: Forward env vars by prefix and source shell config from container-home

Replace hardcoded env var lists with prefix-based forwarding (OPENCODE_*,
ANTHROPIC_*, CLAUDE_*) so new env vars are picked up automatically.

Run tools through bash -l inside the container so that .bashrc, .bash_profile,
and .profile from container-home are sourced. Seed container-home with default
shell config from /etc/skel on first run if the files don't already exist.
This commit is contained in:
Jeena 2026-03-31 04:26:20 +00:00
parent d6f179ec57
commit a3db357b6f
2 changed files with 51 additions and 26 deletions

View file

@ -35,9 +35,16 @@ The container home directory at `$XDG_DATA_HOME/agent-container/container-home/`
## Environment Variables
Host environment variables matching the following prefixes are automatically forwarded into the container:
- `OPENCODE_*` -- passed through to OpenCode
- `ANTHROPIC_*`, `CLAUDE_*` -- passed through to Claude Code
This means inline overrides work as expected: `OPENCODE_CONFIG=foo opencode`
For persistent environment variables, add them to `container-home/.bashrc` (or `.bash_profile` / `.profile`). Tools run through `bash -l` inside the container, so standard shell config files are sourced automatically. Default shell config files from `/etc/skel` are seeded into the container home on first run.
- `XDG_DATA_HOME`: Override default data directory (default: `~/.local/share`)
- `ANTHROPIC_API_KEY`: Your Anthropic API key (passed through to Claude Code)
- `ANTHROPIC_BASE_URL`: Override the API base URL (optional, passed through to Claude Code)
## Usage

View file

@ -3,6 +3,7 @@
import hashlib
import logging
import os
import shlex
import signal
import subprocess
import sys
@ -69,23 +70,13 @@ class AgentContainer:
self._run_tool(
"opencode",
args,
env_vars=[
"OPENCODE_SERVER_PASSWORD",
"OPENCODE_SERVER_USERNAME",
"OPENCODE_CONFIG",
"OPENCODE_CONFIG_DIR",
"OPENCODE_CONFIG_CONTENT",
"OPENCODE_TUI_CONFIG",
],
env_prefixes=["OPENCODE_"],
)
elif command == "claude":
self._run_tool(
"claude",
args,
env_vars=[
"ANTHROPIC_API_KEY",
"ANTHROPIC_BASE_URL",
],
env_prefixes=["ANTHROPIC_", "CLAUDE_"],
extra_env={"DISABLE_AUTOUPDATER": "1"},
)
elif command == "update":
@ -105,7 +96,7 @@ class AgentContainer:
self,
tool: str,
args: list[str],
env_vars: list[str],
env_prefixes: list[str],
extra_env: dict[str, str] | None = None,
) -> None:
self.container_home_path.mkdir(parents=True, exist_ok=True)
@ -143,27 +134,53 @@ class AgentContainer:
if not self.start_container():
raise RuntimeError("Container failed to start after recreation")
# Seed container home with default shell config from /etc/skel
# (only copies files that don't already exist)
self._seed_home()
try:
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
self._exec_tool(tool, args, env_vars, extra_env or {})
self._exec_tool(tool, args, env_prefixes, extra_env or {})
finally:
signal.signal(signal.SIGTSTP, signal.SIG_DFL)
self.stop_container()
def _seed_home(self) -> None:
"""Copy default shell config from /etc/skel into the container
home directory, skipping files that already exist."""
subprocess.run(
[
"docker",
"exec",
self.container_name,
"bash",
"-c",
'for f in /etc/skel/.*; do '
'[ -f "$f" ] && [ ! -e "$HOME/$(basename "$f")" ] && '
'cp "$f" "$HOME/"; done',
],
)
def _exec_tool(
self, tool: str, args: list[str], env_vars: list[str], extra_env: dict[str, str]
self,
tool: str,
args: list[str],
env_prefixes: list[str],
extra_env: dict[str, str],
) -> None:
env_args: list[str] = []
# Pass through host environment variables if set
for var in env_vars:
val = os.environ.get(var)
if val:
env_args += ["-e", f"{var}={val}"]
# Pass through host environment variables matching any prefix
for key, val in os.environ.items():
if any(key.startswith(prefix) for prefix in env_prefixes):
env_args += ["-e", f"{key}={val}"]
# Set additional environment variables
for var, val in extra_env.items():
env_args += ["-e", f"{var}={val}"]
for key, val in extra_env.items():
env_args += ["-e", f"{key}={val}"]
# Build the shell command with proper quoting
cmd_str = " ".join(shlex.quote(a) for a in [tool, *args])
result = subprocess.run(
[
@ -174,8 +191,9 @@ class AgentContainer:
"-w",
str(self.project_path),
self.container_name,
tool,
*args,
"bash",
"-lc",
f"exec {cmd_str}",
],
)
if result.returncode != 0: