Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ Thumbs.db
# Environment files
.env
.env.local
.env.*.local
.env.*.local

# User config (keep config.example as template)
/config
37 changes: 25 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# AgentBox - Simplified multi-language development environment for Claude
FROM debian:trixie

# Optional Java toolchain (default: on for compatibility)
ARG INCLUDE_JAVA=true

# Claude Code channel (stable or latest)
ARG CC_CHANNEL=stable

# Include OpenCode (default: on)
ARG INCLUDE_OPENCODE=true

# Prevent interactive prompts during installation
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=en_US.UTF-8
Expand Down Expand Up @@ -33,8 +42,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
# Python build dependencies
python3-dev python3-pip python3-venv \
libssl-dev libffi-dev \
# Java dependencies
default-jdk maven gradle \
# Java dependencies (conditional)
$(if [ "$INCLUDE_JAVA" = "true" ]; then echo "default-jdk maven gradle"; fi) \
# Search tools
ripgrep fd-find && \
# Setup locale
Expand Down Expand Up @@ -111,13 +120,15 @@ RUN bash -c "source $NVM_DIR/nvm.sh && \
yarn \
pnpm"

# Install SDKMAN for Java toolchain management
RUN curl -s "https://get.sdkman.io?rcupdate=false" | bash && \
echo 'source "$HOME/.sdkman/bin/sdkman-init.sh"' >> ~/.bashrc && \
echo 'source "$HOME/.sdkman/bin/sdkman-init.sh"' >> ~/.zshrc && \
bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && \
sdk install java 21.0.9-tem && \
sdk install gradle"
# Install SDKMAN for Java toolchain management (conditional)
RUN if [ "$INCLUDE_JAVA" = "true" ]; then \
curl -s "https://get.sdkman.io?rcupdate=false" | bash && \
echo 'source "$HOME/.sdkman/bin/sdkman-init.sh"' >> ~/.bashrc && \
echo 'source "$HOME/.sdkman/bin/sdkman-init.sh"' >> ~/.zshrc && \
bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && \
sdk install java 21.0.9-tem && \
sdk install gradle"; \
fi

# Setup Python tools
RUN /home/${USERNAME}/.local/bin/uv tool install black && \
Expand Down Expand Up @@ -201,11 +212,13 @@ USER ${USERNAME}
# Dockerfile hasn't changed. This ensures fresh installs on explicit rebuilds instead
# of relying on unpredictable auto-update timing.
ARG BUILD_TIMESTAMP=unknown
RUN curl -fsSL https://claude.ai/install.sh | bash -s stable && \
RUN curl -fsSL https://claude.ai/install.sh | bash -s ${CC_CHANNEL} && \
zsh -i -c 'which claude && claude --version'

RUN curl -fsSL https://opencode.ai/install | bash && \
zsh -i -c 'which opencode && opencode --version'
RUN if [ "$INCLUDE_OPENCODE" = "true" ]; then \
curl -fsSL https://opencode.ai/install | bash && \
zsh -i -c 'which opencode && opencode --version'; \
fi

# Entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Expand Down
48 changes: 45 additions & 3 deletions agentbox
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ build_image() {
--build-arg GROUP_ID="${group_id}" \
--build-arg USERNAME="agent" \
--build-arg BUILD_TIMESTAMP=${build_timestamp} \
--build-arg INCLUDE_JAVA="${include_java}" \
--build-arg INCLUDE_OPENCODE="${include_opencode}" \
--build-arg CC_CHANNEL="${cc_channel}" \
--label "agentbox.hash=${combined_hash}" \
--label "agentbox.version=1.0.0" \
--label "agentbox.built=${build_timestamp}" \
Expand Down Expand Up @@ -453,6 +456,9 @@ Options:
--rebuild Force rebuild of the image
--add-dir DIR Mount additional directory (can be used multiple times)
--ignore-dot-env Do not load .env files as environment variables (still mounted)
--no-java Exclude Java toolchain for smaller image (default: included)
--no-opencode Exclude OpenCode installation for smaller image (default: included)
--cc-latest Use Claude Code latest channel instead of stable (may be ahead of stable)

Commands:
shell [--admin] Start interactive shell instead of Claude CLI
Expand All @@ -463,18 +469,22 @@ Commands:
Use 'shell --admin' to get a shell with sudo privileges.
Other commands will be executed inside the container.

Configuration:
Config file: ~/.agentbox/config
CLI flags override config file settings.
See config.example for all available options.

Examples:
agentbox # Start Claude CLI for current project
agentbox --tool opencode # Start OpenCode instead of Claude
agentbox -p 3002 # Forward port 3002
agentbox -p 3002:8080 # Map host port 3002 to container port 8080
agentbox shell # Start interactive shell instead of Claude
agentbox shell --admin # Start admin shell with sudo access
agentbox --add-dir ~/1 --add-dir ~/2 # Add dirs with Claude CLI
agentbox --add-dir ~/1 --add-dir ~/2 # Add dirs with Claude CLI
agentbox python script.py # Run Python script in container
agentbox --rebuild # Force rebuild image
agentbox ssh-init # Set up SSH for AgentBox
AGENTBOX_TOOL=opencode agentbox # Use env var to select tool

Containers are ephemeral and automatically removed when you exit.
Package caches and shell history persist between sessions.
Expand Down Expand Up @@ -521,8 +531,20 @@ ssh_setup() {
log_info "AgentBox will use this directory for all SSH operations."
}

# Load config file if exists
load_config() {
local config_file="${HOME}/.agentbox/config"
if [[ -f "$config_file" ]]; then
# Source the config file, allowing it to override defaults
source "$config_file"
fi
}

# Main execution
main() {
# Load config file first (before CLI args, so CLI args can override)
load_config

# Parse arguments
local force_rebuild=false
local shell_mode=false
Expand All @@ -531,7 +553,10 @@ main() {
declare -a ports=()
local extra_dirs=()
local tool="${AGENTBOX_TOOL:-claude}"
local ignore_dot_env=false
local ignore_dot_env="${AGENTBOX_IGNORE_DOT_ENV:-false}"
local include_java="${AGENTBOX_INCLUDE_JAVA:-true}"
local include_opencode="${AGENTBOX_INCLUDE_OPENCODE:-true}"
local cc_channel="${AGENTBOX_CC_CHANNEL:-stable}"

while [[ $# -gt 0 ]]; do
case "$1" in
Expand Down Expand Up @@ -574,6 +599,18 @@ main() {
ignore_dot_env=true
shift
;;
--no-java)
include_java=false
shift
;;
--no-opencode)
include_opencode=false
shift
;;
--cc-latest)
cc_channel=latest
shift
;;
shell)
shell_mode=true
shift
Expand Down Expand Up @@ -603,6 +640,11 @@ main() {
exit 1
fi

# Auto-enable OpenCode if tool is set to opencode
if [[ "$tool" == "opencode" ]]; then
include_opencode=true
fi

# Check runtime
RUNTIME=$(check_runtime)
log_info "Using container runtime: $RUNTIME"
Expand Down
18 changes: 18 additions & 0 deletions config.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# AgentBox configuration
# Copy this file to ~/.agentbox/config to customize defaults

# Tool selection: claude (default) or opencode
# AGENTBOX_TOOL=claude

# Include Java toolchain (JDK, Maven, Gradle) - default: true
# AGENTBOX_INCLUDE_JAVA=false

# Include OpenCode - default: true
# AGENTBOX_INCLUDE_OPENCODE=false

# Claude Code channel: stable or latest - default: stable
# AGENTBOX_CC_CHANNEL=latest

# Ignore .env files - default: false
# AGENTBOX_IGNORE_DOT_ENV=true
2 changes: 1 addition & 1 deletion entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ if [ -t 0 ] && [ -t 1 ]; then
echo "📁 Project Directory: ${PROJECT_DIR:-unknown}"
echo "🐍 Python: $(python3 --version 2>&1 | cut -d' ' -f2) (uv available)"
echo "🟢 Node.js: $(node --version 2>/dev/null || echo 'not found')"
echo "☕ Java: $(java -version 2>&1 | head -1 | cut -d'"' -f2 || echo 'not found')"
echo "☕ Java: $(command -v java >/dev/null 2>&1 && java -version 2>&1 | head -1 | cut -d'"' -f2 || echo 'excluded (--no-java)')"
if [ "$TOOL" = "opencode" ]; then
echo "🤖 OpenCode: $(opencode --version 2>/dev/null || echo 'not found - check installation')"
else
Expand Down
Loading