diff --git a/claude-agents-ui-Dockerfile b/claude-agents-ui-Dockerfile index c2b53b1..9002f79 100644 --- a/claude-agents-ui-Dockerfile +++ b/claude-agents-ui-Dockerfile @@ -1,55 +1,64 @@ +# ============================================================================ +# Claude Code Agents UI - Dockerfile +# Combines Ngxba/claude-code-agents-ui frontend with Claude Code CLI runtime +# ============================================================================ + +# --- Build Stage --- FROM node:20-alpine AS builder WORKDIR /app # Install build dependencies -RUN apk add --no-cache git bun +RUN apk add --no-cache git # Clone the Claude Code Agents UI repository -RUN git clone --depth 1 https://github.com/Ngxba/claude-code-agents-ui.git . || true +RUN git clone --depth 1 https://github.com/Ngxba/claude-code-agents-ui.git . -# Install dependencies using bun or npm +# Install dependencies (prefer bun if lockfile present, fallback to npm) RUN if [ -f bun.lockb ]; then \ - bun install --frozen-lockfile; \ - else \ - npm install; \ - fi + npm install -g bun && bun install --frozen-lockfile; \ + else \ + npm install; \ + fi # Build the Nuxt application -RUN npm run build || bun run build +RUN npm run build 2>/dev/null || npx nuxt build -# Production stage +# --- Production Stage --- FROM node:20-alpine WORKDIR /app -# Install runtime dependencies including Claude Code and utilities +# Install runtime dependencies RUN apk add --no-cache \ - python3 py3-pip \ - git git-lfs \ - curl wget \ - bash zsh \ - vim nano \ + python3 \ + py3-pip \ + git \ + curl \ + wget \ + bash \ + vim \ + nano \ build-base \ openssh-client \ ca-certificates \ - tini + tini \ + jq # Install Claude Code CLI RUN npm install -g @anthropic-ai/claude-code # Install Python tools for agent execution -RUN pip3 install --no-cache-dir \ +RUN pip3 install --no-cache-dir --break-system-packages \ requests \ python-dotenv \ pydantic \ fastapi \ uvicorn -# Copy built application from builder +# Copy built Nuxt application from builder COPY --from=builder /app/.output /app/.output COPY --from=builder /app/package.json /app/package.json -COPY --from=builder /app/node_modules /app/node_modules # Create non-root user for security RUN addgroup -g 1000 claude && \ @@ -57,36 +66,76 @@ RUN addgroup -g 1000 claude && \ # Create necessary directories RUN mkdir -p /workspace /root/.claude && \ - chown -R claude:claude /workspace /root/.claude + chown -R claude:claude /workspace && \ + chown -R root:root /root/.claude -# Set permissions +# Set app ownership RUN chown -R claude:claude /app -# Create entrypoint script that starts both services -RUN cat > /entrypoint.sh << 'EOF' +# ---------------------------------------------------------------------------- +# Entrypoint: bootstraps Claude auth then starts the UI +# ---------------------------------------------------------------------------- +# Auth strategy (in priority order): +# 1. If ANTHROPIC_API_KEY is set → write a minimal credentials file so the +# Claude Code CLI recognises the key without needing an interactive login. +# 2. If a credentials file was volume-mounted at /root/.claude/.credentials.json +# already (e.g. from a docker volume) → use it as-is. +# 3. Fall through to the UI which will surface an auth error to the user. +# ---------------------------------------------------------------------------- +RUN cat > /entrypoint.sh << 'ENTRYPOINT_EOF' #!/bin/sh set -e -# Start Claude Code in the background if specified -if [ "$RUN_CLAUDE_CODE" = "true" ]; then - echo "Starting Claude Code daemon..." - claude serve & - CLAUDE_PID=$! +CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-/root/.claude}" +CREDS_FILE="${CLAUDE_DIR}/.credentials.json" + +echo "[entrypoint] Claude config dir: ${CLAUDE_DIR}" +mkdir -p "${CLAUDE_DIR}" + +# --- Bootstrap auth from ANTHROPIC_API_KEY --- +if [ -n "${ANTHROPIC_API_KEY}" ]; then + if [ ! -f "${CREDS_FILE}" ]; then + echo "[entrypoint] Writing credentials from ANTHROPIC_API_KEY..." + # Claude Code CLI accepts a credentials file with an apiKey field as + # an alternative to OAuth. This is the non-interactive equivalent of + # running `claude auth login` with an API key. + cat > "${CREDS_FILE}" << CREDS_EOF +{ + "claudeAiOauth": null, + "apiKey": "${ANTHROPIC_API_KEY}" +} +CREDS_EOF + chmod 600 "${CREDS_FILE}" + echo "[entrypoint] Credentials written successfully." + else + echo "[entrypoint] Credentials file already exists, skipping write." + fi +else + echo "[entrypoint] WARNING: ANTHROPIC_API_KEY not set." + echo "[entrypoint] Set it in your .env file or via 'docker run -e ANTHROPIC_API_KEY=...'." + echo "[entrypoint] The UI will load but Claude Code execution will fail without auth." fi -# Start the Nuxt application -echo "Starting Claude Code Agents UI..." +# --- Verify Claude CLI can see the key --- +if claude auth status 2>/dev/null | grep -q "Logged in\|api key"; then + echo "[entrypoint] Claude CLI auth: OK" +else + echo "[entrypoint] Claude CLI auth: not authenticated (will use ANTHROPIC_API_KEY at runtime)" +fi + +# --- Start the Nuxt application --- +echo "[entrypoint] Starting Claude Code Agents UI on port 3000..." exec node /app/.output/server/index.mjs -EOF +ENTRYPOINT_EOF RUN chmod +x /entrypoint.sh # Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD curl -f http://localhost:3000/api/health || exit 1 +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -f http://localhost:3000 || exit 1 EXPOSE 3000 -# Use tini to handle signals properly +# Use tini for proper signal handling ENTRYPOINT ["/sbin/tini", "--"] CMD ["/entrypoint.sh"]