diff --git a/README.md b/README.md index f87d6d2..be25819 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,38 @@ Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment with a proxy sidecar for controlled egress. Claude cannot access the host filesystem or network directly. +## Quick Start + +**1. Install Docker Desktop** + +Download and install [Docker Desktop](https://www.docker.com/products/docker-desktop/) for your platform. It's free and includes everything needed — no extra tools required. + +**2. Download this repo** + +Clone or download and unzip this repository somewhere on your machine. + +**3. Run setup** + +- **macOS / Linux:** Open a terminal, navigate to the folder, and run: + ```bash + ./setup.sh + ``` +- **Windows:** Double-click `launch.bat` — it will run setup automatically on first launch. + +Setup will ask how you want to authenticate (API key, subscription token, or browser login) and save your settings. + +**4. Start Claude** + +- **macOS / Linux:** Double-click `launch.sh`, or run it from a terminal: + ```bash + ./launch.sh + ``` + A folder picker will appear — select the project you want Claude to work on. + +- **Windows:** Double-click `launch.bat`. + +--- + ## Architecture ``` @@ -35,31 +67,17 @@ Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment ## Prerequisites -- Docker Engine 24+ -- Docker Compose v2 plugin (`docker compose version`) - -## Setup - -```bash -# 1. Clone / copy this repo -git clone docker-claude && cd docker-claude - -# 2. Configure credentials (see Authentication below) -cp .env.example .env -$EDITOR .env - -# 3. Make the control script executable -chmod +x claude.sh -``` +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) (includes Docker Engine and Compose) ## Authentication -Three options — pick one and set it in `.env`: +Three options — `./setup.sh` will guide you through picking one: ### Option 1 — API key ```bash ANTHROPIC_API_KEY=sk-ant-... ``` +Get a key at [console.anthropic.com](https://console.anthropic.com/settings/keys). ### Option 2 — OAuth token (subscription, headless-friendly) @@ -67,7 +85,7 @@ Run this **on your host** (not inside the container) to generate a 1-year token: ```bash claude setup-token ``` -Then set the printed token in `.env`: +Then paste the token into setup, or set it manually in `.env`: ```bash CLAUDE_CODE_OAUTH_TOKEN=... ``` @@ -75,26 +93,22 @@ CLAUDE_CODE_OAUTH_TOKEN=... ### Option 3 — Browser OAuth (interactive) Leave both keys unset. On first run, Claude Code will print a login URL. -Port 54545 must be reachable from your browser for the OAuth callback: - -```bash -sbx ports --publish 54545:54545/tcp -``` - -Then run `./claude.sh start` and follow the prompt. Credentials are stored in -`~/.claude` on the host and reused on every subsequent run. +Port 54545 must be reachable from your browser for the OAuth callback. ## Usage +### Normal use + ```bash -# Start proxy, pull latest images, launch Claude Code in the current directory -cd ~/myproject -./claude.sh start +./launch.sh # folder picker → starts Claude in the selected directory ``` -### Other commands +### CLI / power users ```bash +cd ~/myproject +./claude.sh start + ./claude.sh stop # Stop and remove all containers ./claude.sh update # Pull latest images from the registry ./claude.sh logs # Tail proxy logs @@ -104,8 +118,6 @@ cd ~/myproject ### Building locally -`build.sh` builds images from source using the local Dockerfiles: - ```bash ./build.sh # build with layer cache ./build.sh --no-cache # force full rebuild diff --git a/claude.sh b/claude.sh index a78dc25..7a7f3a0 100755 --- a/claude.sh +++ b/claude.sh @@ -16,24 +16,36 @@ error() { echo -e "${RED}[-]${NC} $*" >&2; } # ─── Helpers ────────────────────────────────────────────────────────────────── check_deps() { - command -v docker &>/dev/null \ - || { error "Docker is not installed. https://docs.docker.com/get-docker/"; exit 1; } - docker compose version &>/dev/null 2>&1 \ - || { error "Docker Compose v2 plugin is required."; exit 1; } + if ! command -v docker &>/dev/null; then + error "Docker is not installed." + error " → Download Docker Desktop (free): https://www.docker.com/products/docker-desktop/" + exit 1 + fi + if ! docker info &>/dev/null 2>&1; then + error "Docker is not running." + error " → Open Docker Desktop and wait for it to finish starting, then try again." + exit 1 + fi + if ! docker compose version &>/dev/null 2>&1; then + error "Docker Compose is not available." + error " → Download Docker Desktop (includes Compose): https://www.docker.com/products/docker-desktop/" + exit 1 + fi } load_env() { local env_file="$SCRIPT_DIR/.env" - if [[ -f "$env_file" ]]; then - # shellcheck disable=SC1090 - set -a; source "$env_file"; set +a + if [[ ! -f "$env_file" ]]; then + warn "Not set up yet. Run ./setup.sh first." + exit 1 fi + # shellcheck disable=SC1090 + set -a; source "$env_file"; set +a 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): log in when prompted; port 54545 must be reachable from your browser." + warn "No credentials found — Claude will ask you to log in via browser." + warn "A login URL will appear below. Open it to authenticate." + warn "(To skip this prompt in future, run ./setup.sh to configure credentials.)" + echo "" fi } diff --git a/hooks/pre-commit b/hooks/pre-commit index 9e6e3ae..cf9ee40 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -2,7 +2,7 @@ # Ensure control scripts stay executable. set -euo pipefail -SCRIPTS=(claude.sh build.sh hooks/pre-commit) +SCRIPTS=(claude.sh build.sh setup.sh launch.sh hooks/pre-commit) for f in "${SCRIPTS[@]}"; do if [[ -f "$f" && ! -x "$f" ]]; then diff --git a/launch.bat b/launch.bat new file mode 100644 index 0000000..21d3d13 --- /dev/null +++ b/launch.bat @@ -0,0 +1,55 @@ +@echo off +:: launch.bat — Pick a project folder and start Claude Code (Windows) +setlocal enabledelayedexpansion + +set "SCRIPT_DIR=%~dp0" +set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" + +:: ── Check for bash (Git Bash or WSL) ───────────────────────────────────────── +where bash >nul 2>&1 +if %errorlevel% neq 0 ( + echo Git Bash is required to run docker-claude on Windows. + echo. + echo Download it at: https://git-scm.com/download/win + echo Install with default options, then double-click this file again. + pause + exit /b 1 +) + +:: ── First-time setup ────────────────────────────────────────────────────────── +if not exist "%SCRIPT_DIR%\.env" ( + echo Looks like this is your first time. Running setup... + echo. + bash "%SCRIPT_DIR%/setup.sh" + if %errorlevel% neq 0 ( pause & exit /b 1 ) + echo. +) + +:: ── Folder picker via PowerShell ────────────────────────────────────────────── +set "PROJECT_FOLDER=" +for /f "usebackq tokens=*" %%i in (`powershell -NoProfile -Command ^ + "Add-Type -AssemblyName System.Windows.Forms; ^ + $d = New-Object System.Windows.Forms.FolderBrowserDialog; ^ + $d.Description = 'Select the project folder to work on'; ^ + $d.RootFolder = 'MyComputer'; ^ + $d.ShowNewFolderButton = $false; ^ + if ($d.ShowDialog() -eq 'OK') { Write-Output $d.SelectedPath } else { exit 1 }"`) do ( + set "PROJECT_FOLDER=%%i" +) + +if not defined PROJECT_FOLDER ( + echo No folder selected. Exiting. + pause + exit /b 1 +) + +:: ── Launch ──────────────────────────────────────────────────────────────────── +:: Convert Windows path to Unix path for bash +for /f "usebackq tokens=*" %%i in (`bash -c "cygpath -u '!PROJECT_FOLDER!'"`) do ( + set "UNIX_FOLDER=%%i" +) + +bash -c "cd '!UNIX_FOLDER!' && '!SCRIPT_DIR:/=\..\..\!/claude.sh' start" 2>nul || ^ +bash -c "cd '!UNIX_FOLDER!' && bash '$(cygpath -u '!SCRIPT_DIR!')/claude.sh' start" + +pause diff --git a/launch.sh b/launch.sh new file mode 100755 index 0000000..d987957 --- /dev/null +++ b/launch.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# launch.sh — Pick a project folder and start Claude Code +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# ─── First-time setup ───────────────────────────────────────────────────────── +if [[ ! -f "$SCRIPT_DIR/.env" ]]; then + echo "Looks like this is your first time. Running setup..." + echo "" + "$SCRIPT_DIR/setup.sh" || exit 1 + echo "" +fi + +# ─── Folder picker ──────────────────────────────────────────────────────────── +pick_folder() { + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS — native Finder dialog + osascript -e \ + 'tell application "Finder" to POSIX path of (choose folder with prompt "Select the project folder to work on:")' \ + 2>/dev/null | tr -d '\n' + elif command -v zenity &>/dev/null; then + # Linux — GNOME/GTK dialog + zenity --file-selection --directory \ + --title="Select your project folder" 2>/dev/null + elif command -v kdialog &>/dev/null; then + # Linux — KDE dialog + kdialog --getexistingdirectory "$HOME" \ + --title "Select your project folder" 2>/dev/null + else + echo "" + fi +} + +folder=$(pick_folder || true) + +# Fallback: text prompt (no GUI available, or user cancelled dialog) +if [[ -z "$folder" ]]; then + echo "Enter the path to your project folder" + echo "(Tip: you can drag the folder into this window, then press Enter)" + echo "" + read -rp "> " folder + # Clean up: strip surrounding quotes and trailing whitespace from drag-and-drop + folder="${folder%"${folder##*[![:space:]]}"}" + folder="${folder#\'}" ; folder="${folder%\'}" + folder="${folder#\"}" ; folder="${folder%\"}" + # Expand ~ to home directory + folder="${folder/#\~/$HOME}" +fi + +if [[ -z "$folder" ]]; then + echo "No folder selected. Exiting." + exit 1 +fi + +if [[ ! -d "$folder" ]]; then + echo "Folder not found: $folder" + exit 1 +fi + +cd "$folder" +exec "$SCRIPT_DIR/claude.sh" start diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..3f9d380 --- /dev/null +++ b/setup.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# setup.sh — First-time setup wizard for docker-claude +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ENV_FILE="$SCRIPT_DIR/.env" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m' +info() { echo -e "${GREEN}[+]${NC} $*"; } +warn() { echo -e "${YELLOW}[!]${NC} $*"; } +error() { echo -e "${RED}[✗]${NC} $*" >&2; } +step() { echo -e "\n${BOLD}$*${NC}"; } + +# ─── Check Docker ───────────────────────────────────────────────────────────── +check_docker() { + step "Checking Docker..." + + if ! command -v docker &>/dev/null; then + error "Docker is not installed." + echo " → Download Docker Desktop (free): https://www.docker.com/products/docker-desktop/" + echo " It includes everything you need — no extra tools required." + exit 1 + fi + + if ! docker info &>/dev/null 2>&1; then + error "Docker is installed but not running." + echo " → Open Docker Desktop and wait for the whale icon to stop animating," + echo " then run this setup again." + exit 1 + fi + + if ! docker compose version &>/dev/null 2>&1; then + error "Docker Compose is not available." + echo " → Download Docker Desktop (includes Compose): https://www.docker.com/products/docker-desktop/" + exit 1 + fi + + info "Docker is ready." +} + +# ─── Auth setup ─────────────────────────────────────────────────────────────── +setup_auth() { + step "Authentication" + echo " How would you like to sign in to Claude?" + echo "" + echo " 1) Anthropic API key (pay-per-use)" + echo " Get one at: https://console.anthropic.com/settings/keys" + echo "" + echo " 2) Claude subscription (Claude Pro or Max)" + echo " Generates a token from your existing subscription." + echo "" + echo " 3) Browser login (sign in when Claude first starts)" + echo "" + read -rp " Choice [1/2/3, default: 3]: " choice + choice="${choice:-3}" + + case "$choice" in + 1) + echo "" + read -rp " Paste your API key (sk-ant-...): " api_key + if [[ -z "$api_key" ]]; then + error "No API key entered. Run setup again when you have one." + exit 1 + fi + echo "ANTHROPIC_API_KEY=$api_key" > "$ENV_FILE" + ;; + 2) + echo "" + echo " You'll need to run 'claude setup-token' on your host to generate a token." + echo " If Claude Code is installed natively, run that command now and paste the result." + echo " Otherwise choose option 3 (browser login)." + echo "" + read -rp " Paste your OAuth token: " token + if [[ -z "$token" ]]; then + error "No token entered. Run setup again when you have one." + exit 1 + fi + echo "CLAUDE_CODE_OAUTH_TOKEN=$token" > "$ENV_FILE" + ;; + 3) + touch "$ENV_FILE" + warn "Browser login selected." + warn "When Claude starts for the first time, it will print a login URL." + warn "Open that URL in your browser to sign in." + ;; + *) + error "Invalid choice: $choice" + exit 1 + ;; + esac +} + +# ─── Main ───────────────────────────────────────────────────────────────────── +echo -e "\n${BOLD}docker-claude setup${NC}" +echo "────────────────────" + +if [[ -f "$ENV_FILE" ]]; then + warn ".env already exists (setup was already run)." + read -rp " Reconfigure authentication? [y/N]: " confirm + if [[ "${confirm,,}" != "y" ]]; then + info "Setup skipped. Run ./launch.sh to start Claude." + exit 0 + fi +fi + +check_docker +setup_auth + +echo "" +info "Setup complete!" +info "→ Run ./launch.sh to start Claude Code."