feat: add non-technical user onboarding
- setup.sh: interactive wizard for Docker check and auth configuration - launch.sh: folder-picker launcher (macOS native dialog, zenity/kdialog on Linux, text fallback) - launch.bat: Windows launcher using PowerShell folder browser + Git Bash - claude.sh: friendlier error messages with actionable links; prompt setup.sh if .env missing - hooks/pre-commit: add setup.sh and launch.sh to executable enforcement - README: add Quick Start section aimed at non-technical users Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
51e7ab2b08
commit
f68ed674d0
6 changed files with 297 additions and 45 deletions
76
README.md
76
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.
|
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
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -35,31 +67,17 @@ Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Docker Engine 24+
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) (includes Docker Engine and Compose)
|
||||||
- Docker Compose v2 plugin (`docker compose version`)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Clone / copy this repo
|
|
||||||
git clone <repo> 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
|
|
||||||
```
|
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
Three options — pick one and set it in `.env`:
|
Three options — `./setup.sh` will guide you through picking one:
|
||||||
|
|
||||||
### Option 1 — API key
|
### Option 1 — API key
|
||||||
```bash
|
```bash
|
||||||
ANTHROPIC_API_KEY=sk-ant-...
|
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)
|
### 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
|
```bash
|
||||||
claude setup-token
|
claude setup-token
|
||||||
```
|
```
|
||||||
Then set the printed token in `.env`:
|
Then paste the token into setup, or set it manually in `.env`:
|
||||||
```bash
|
```bash
|
||||||
CLAUDE_CODE_OAUTH_TOKEN=...
|
CLAUDE_CODE_OAUTH_TOKEN=...
|
||||||
```
|
```
|
||||||
|
|
@ -75,26 +93,22 @@ CLAUDE_CODE_OAUTH_TOKEN=...
|
||||||
### Option 3 — Browser OAuth (interactive)
|
### Option 3 — Browser OAuth (interactive)
|
||||||
|
|
||||||
Leave both keys unset. On first run, Claude Code will print a login URL.
|
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:
|
Port 54545 must be reachable from your browser for the OAuth callback.
|
||||||
|
|
||||||
```bash
|
|
||||||
sbx ports <sandbox-name> --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.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Normal use
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start proxy, pull latest images, launch Claude Code in the current directory
|
./launch.sh # folder picker → starts Claude in the selected directory
|
||||||
cd ~/myproject
|
|
||||||
./claude.sh start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Other commands
|
### CLI / power users
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd ~/myproject
|
||||||
|
./claude.sh start
|
||||||
|
|
||||||
./claude.sh stop # Stop and remove all containers
|
./claude.sh stop # Stop and remove all containers
|
||||||
./claude.sh update # Pull latest images from the registry
|
./claude.sh update # Pull latest images from the registry
|
||||||
./claude.sh logs # Tail proxy logs
|
./claude.sh logs # Tail proxy logs
|
||||||
|
|
@ -104,8 +118,6 @@ cd ~/myproject
|
||||||
|
|
||||||
### Building locally
|
### Building locally
|
||||||
|
|
||||||
`build.sh` builds images from source using the local Dockerfiles:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build.sh # build with layer cache
|
./build.sh # build with layer cache
|
||||||
./build.sh --no-cache # force full rebuild
|
./build.sh --no-cache # force full rebuild
|
||||||
|
|
|
||||||
34
claude.sh
34
claude.sh
|
|
@ -16,24 +16,36 @@ error() { echo -e "${RED}[-]${NC} $*" >&2; }
|
||||||
|
|
||||||
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
check_deps() {
|
check_deps() {
|
||||||
command -v docker &>/dev/null \
|
if ! command -v docker &>/dev/null; then
|
||||||
|| { error "Docker is not installed. https://docs.docker.com/get-docker/"; exit 1; }
|
error "Docker is not installed."
|
||||||
docker compose version &>/dev/null 2>&1 \
|
error " → Download Docker Desktop (free): https://www.docker.com/products/docker-desktop/"
|
||||||
|| { error "Docker Compose v2 plugin is required."; exit 1; }
|
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() {
|
load_env() {
|
||||||
local env_file="$SCRIPT_DIR/.env"
|
local env_file="$SCRIPT_DIR/.env"
|
||||||
if [[ -f "$env_file" ]]; then
|
if [[ ! -f "$env_file" ]]; then
|
||||||
|
warn "Not set up yet. Run ./setup.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
set -a; source "$env_file"; set +a
|
set -a; source "$env_file"; set +a
|
||||||
fi
|
|
||||||
if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]]; then
|
if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]]; then
|
||||||
warn "No ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN found."
|
warn "No credentials found — Claude will ask you to log in via browser."
|
||||||
warn "Claude Code will prompt you to authenticate on first run."
|
warn "A login URL will appear below. Open it to authenticate."
|
||||||
warn " Option 1 (API key): set ANTHROPIC_API_KEY in .env"
|
warn "(To skip this prompt in future, run ./setup.sh to configure credentials.)"
|
||||||
warn " Option 2 (token): run 'claude setup-token' and set CLAUDE_CODE_OAUTH_TOKEN in .env"
|
echo ""
|
||||||
warn " Option 3 (browser): log in when prompted; port 54545 must be reachable from your browser."
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Ensure control scripts stay executable.
|
# Ensure control scripts stay executable.
|
||||||
set -euo pipefail
|
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
|
for f in "${SCRIPTS[@]}"; do
|
||||||
if [[ -f "$f" && ! -x "$f" ]]; then
|
if [[ -f "$f" && ! -x "$f" ]]; then
|
||||||
|
|
|
||||||
55
launch.bat
Normal file
55
launch.bat
Normal file
|
|
@ -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
|
||||||
62
launch.sh
Executable file
62
launch.sh
Executable file
|
|
@ -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
|
||||||
111
setup.sh
Executable file
111
setup.sh
Executable file
|
|
@ -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."
|
||||||
Loading…
Add table
Add a link
Reference in a new issue