diff --git a/.gitignore b/.gitignore index d2f63d2..875a2e0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,7 @@ Thumbs.db # Environment files .env .env.local -.env.*.local \ No newline at end of file +.env.*.local + +# User config (keep config.example as template) +/config \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 402124e..cca1533 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -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 @@ -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 && \ @@ -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"] diff --git a/agentbox b/agentbox index 6e5dd5a..fa75ac4 100755 --- a/agentbox +++ b/agentbox @@ -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}" \ @@ -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 @@ -463,6 +469,11 @@ 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 @@ -470,11 +481,10 @@ Examples: 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. @@ -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 @@ -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 @@ -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 @@ -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" diff --git a/config.example b/config.example new file mode 100644 index 0000000..a9d1775 --- /dev/null +++ b/config.example @@ -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 diff --git a/entrypoint.sh b/entrypoint.sh index 191f1b5..0dac6b9 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -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