container: Improve usability of web mode, error handling, and CLI commands

Use --network host so that web interfaces (e.g. opencode web) are
accessible from the host. Suppress Python tracebacks when the tool
exits with a non-zero code. Ignore SIGTSTP to prevent Ctrl+Z from
leaving orphaned containers. Rename force-cleanup to purge.
This commit is contained in:
Jeena 2026-03-28 22:07:58 +00:00
parent 61017da6ba
commit 2d3d5caacb
2 changed files with 15 additions and 9 deletions

View file

@ -63,12 +63,12 @@ agent-container update
This removes all existing containers and rebuilds the image from scratch. Containers are recreated automatically on the next run. The persistent home directory is not affected.
### Force Cleanup
### Purge
To remove all containers, the image, and the persistent home directory:
```sh
agent-container force-cleanup
agent-container purge
```
## Sharing host config files via hard links

View file

@ -3,6 +3,7 @@
import hashlib
import logging
import os
import signal
import subprocess
import sys
import time
@ -16,7 +17,7 @@ Usage:
agent-container opencode [args...] Run OpenCode in the container
agent-container claude [args...] Run Claude Code in the container
agent-container update Rebuild image with latest versions
agent-container force-cleanup Remove all containers, image, and data
agent-container purge Remove all containers, image, and data
"""
@ -78,8 +79,8 @@ class AgentContainer:
)
elif command == "update":
self.update()
elif command == "force-cleanup":
self.force_cleanup()
elif command == "purge":
self.purge()
else:
logger.error(f"Unknown command: {command}")
print(USAGE, file=sys.stderr)
@ -132,8 +133,10 @@ class AgentContainer:
raise RuntimeError("Container failed to start after recreation")
try:
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
self._exec_tool(tool, args, env_vars, extra_env or {})
finally:
signal.signal(signal.SIGTSTP, signal.SIG_DFL)
self.stop_container()
def _exec_tool(
@ -151,7 +154,7 @@ class AgentContainer:
for var, val in extra_env.items():
env_args += ["-e", f"{var}={val}"]
subprocess.run(
result = subprocess.run(
[
"docker",
"exec",
@ -163,8 +166,9 @@ class AgentContainer:
tool,
*args,
],
check=True,
)
if result.returncode != 0:
sys.exit(result.returncode)
# =========================
# Image handling
@ -213,6 +217,8 @@ class AgentContainer:
"create",
"--name",
self.container_name,
"--network",
"host",
"--user",
f"{self.host_uid}:{self.host_gid}",
"--volume",
@ -263,8 +269,8 @@ class AgentContainer:
self.build_image(no_cache=True)
logger.info("Update complete. Containers will be recreated on next run.")
def force_cleanup(self) -> None:
logger.info("Running force cleanup...")
def purge(self) -> None:
logger.info("Purging all containers, image, and data...")
self._remove_all_containers()
if self.image_exists():