#!/usr/bin/env bash # claude.sh — Manage the isolated Claude Code Docker environment # Usage: ./claude.sh [args] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml" PROJECT="claude-secure" # ─── Colours ────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' info() { echo -e "${GREEN}[+]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } error() { echo -e "${RED}[-]${NC} $*" >&2; } # ─── Dependency check ───────────────────────────────────────────────────────── check_deps() { if ! command -v docker &>/dev/null; then error "Docker is not installed. https://docs.docker.com/get-docker/" exit 1 fi if ! docker compose version &>/dev/null 2>&1; then error "Docker Compose v2 plugin is required." exit 1 fi } # ─── Environment loading ────────────────────────────────────────────────────── load_env() { local env_file="$SCRIPT_DIR/.env" if [[ -f "$env_file" ]]; then # shellcheck disable=SC1090 set -a; source "$env_file"; set +a fi if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]]; then warn "No ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN found." warn "Claude Code will prompt you to authenticate on first run." warn " Option 1 (API key): set ANTHROPIC_API_KEY in .env" warn " Option 2 (token): run 'claude setup-token' and set CLAUDE_CODE_OAUTH_TOKEN in .env" warn " Option 3 (browser): run './claude.sh run' and follow the login prompt;" warn " port 54545 must be reachable from your browser." fi } # ─── Workspace volume resolution ────────────────────────────────────────────── # Mounts the current working directory as /workspace inside the container. workspace_flag() { echo "--volume $(pwd):/workspace:z" } # ─── Compose wrapper ────────────────────────────────────────────────────────── dc() { docker compose -f "$COMPOSE_FILE" -p "$PROJECT" "$@"; } # ─── Commands ───────────────────────────────────────────────────────────────── cmd_start() { check_deps load_env info "Building images..." dc build info "Starting proxy sidecar..." dc up -d proxy info "Waiting for proxy health check..." dc up -d proxy # no-op if already healthy; compose waits via depends_on info "Launching Claude Code..." # shellcheck disable=SC2046 dc run --rm --service-ports $(workspace_flag) claude "$@" } cmd_stop() { check_deps info "Stopping all containers..." dc down info "Done." } cmd_run() { check_deps load_env info "Ensuring proxy is running..." dc up -d proxy info "Launching Claude Code..." # shellcheck disable=SC2046 dc run --rm --service-ports $(workspace_flag) claude "$@" } cmd_update() { check_deps info "Rebuilding images (no cache)..." dc build --no-cache info "Update complete. Run './claude.sh start' to launch." } cmd_logs() { check_deps local svc="${1:-proxy}" dc logs -f "$svc" } cmd_status() { check_deps dc ps } cmd_shell() { check_deps load_env warn "Opening debug shell inside Claude container (non-Claude entrypoint)." # shellcheck disable=SC2046 dc run --rm --service-ports --entrypoint /bin/bash $(workspace_flag) claude } cmd_web() { check_deps load_env if [[ -z "${WEBUI_PASSWORD:-}" ]]; then error "WEBUI_PASSWORD is not set. Add it to .env before starting the web interface." exit 1 fi info "Building images..." dc build info "Starting proxy and web interface..." dc up -d webui local port=7681 info "Web interface is up → http://0.0.0.0:${port}" info "Credentials: ${WEBUI_USER:-claude} / [WEBUI_PASSWORD]" warn "To reach it from outside this host, publish the port:" warn " sbx ports --publish ${port}:${port}/tcp" } cmd_web_stop() { check_deps info "Stopping web interface..." dc stop webui dc rm -f webui } cmd_help() { cat < [args] Commands: start [args] Build images, start proxy, launch Claude Code (CLI) run [args] Start proxy if needed, launch Claude Code (CLI) web Build images, start proxy + web interface (browser terminal) web-stop Stop the web interface (keeps proxy running) stop Stop and remove all containers update Rebuild images without cache logs [svc] Tail logs (default: proxy) status Show container status shell Open a bash shell in the Claude container (debug) help Show this message Environment variables (set in .env or shell): ANTHROPIC_API_KEY Required for all modes. WEBUI_USER Web interface username (default: claude). WEBUI_PASSWORD Required for web mode. Basic auth password. Examples: cd ~/myproject && ./claude.sh start ./claude.sh web ./claude.sh logs proxy ./claude.sh logs webui ./claude.sh shell EOF } # ─── Dispatch ───────────────────────────────────────────────────────────────── case "${1:-help}" in start) shift; cmd_start "$@" ;; stop) cmd_stop ;; run) shift; cmd_run "$@" ;; web) cmd_web ;; web-stop) cmd_web_stop ;; update) cmd_update ;; logs) shift; cmd_logs "${1:-}" ;; status) cmd_status ;; shell) cmd_shell ;; help|-h|--help) cmd_help ;; *) error "Unknown command: ${1}" cmd_help exit 1 ;; esac