From 5881d0f0df8c84b3eaf43ac90a47501c05c7db38 Mon Sep 17 00:00:00 2001 From: zgaetano Date: Sat, 4 Apr 2026 14:32:22 -0400 Subject: [PATCH] Upload files to "/" --- CHECKLIST.md | 449 +++++++++++++++++++++++++++ claude-agents-ui-Dockerfile | 92 ++++++ claude-code-stack-docker-compose.yml | 142 +++++++++ claude-code-stack.env | 147 +++++++++ deploy-mcp.py | 419 +++++++++++++++++++++++++ 5 files changed, 1249 insertions(+) create mode 100644 CHECKLIST.md create mode 100644 claude-agents-ui-Dockerfile create mode 100644 claude-code-stack-docker-compose.yml create mode 100644 claude-code-stack.env create mode 100644 deploy-mcp.py diff --git a/CHECKLIST.md b/CHECKLIST.md new file mode 100644 index 0000000..86b0718 --- /dev/null +++ b/CHECKLIST.md @@ -0,0 +1,449 @@ +# Claude Code Stack - Pre & Post Deployment Checklist + +## ✅ Pre-Deployment Checklist + +### Environment Preparation +- [ ] Anthropic API key obtained from https://console.anthropic.com/ +- [ ] TrueNAS system is up and running +- [ ] Docker and Docker Compose installed on TrueNAS + - Verify: `ssh root@truenas-ip "docker --version"` + - Verify: `ssh root@truenas-ip "docker-compose --version"` +- [ ] SSH access to TrueNAS is working + - Test: `ssh root@your-truenas-ip "echo 'Connected'"` +- [ ] Sufficient disk space available (minimum 20GB) + - Check: `ssh root@truenas-ip "df -h"` +- [ ] Sufficient RAM (minimum 4GB allocated to Docker) + +### Network Preparation +- [ ] TrueNAS IP address noted: _______________ +- [ ] Firewall allows ports 3000, 5000, 5432, 6379, 80, 443 +- [ ] No port conflicts on TrueNAS + - Check: `ssh root@truenas-ip "netstat -tuln | grep LISTEN"` +- [ ] DNS is correctly configured (if accessing via hostname) + +### File Preparation +- [ ] All deployment files downloaded + - [ ] docker-compose.yml + - [ ] deploy-ssh.sh or deploy-mcp.py + - [ ] claude-code-stack.env + - [ ] nginx.conf + - [ ] Dockerfile (optional) +- [ ] deployment scripts have execute permissions + - Run: `chmod +x deploy-ssh.sh` + - Run: `chmod +x deploy-mcp.py` + +### System Information (for reference) +- [ ] TrueNAS IP: _______________ +- [ ] SSH Username: _______________ +- [ ] Pool Name: _______________ +- [ ] Anthropic API Key (first 10 chars): _______________ +- [ ] PostgreSQL Password (secure): _______________ + +--- + +## 🚀 Deployment Execution Checklist + +### Method 1: SSH Deployment (Recommended) + +- [ ] Set API key environment variable + ```bash + export ANTHROPIC_API_KEY="sk-ant-your-key-here" + ``` + +- [ ] Run deployment script + ```bash + chmod +x deploy-ssh.sh + ./deploy-ssh.sh 192.168.1.100 root tank + ``` + +- [ ] Monitor deployment progress + - [ ] Directory creation: `✓ created` + - [ ] Files uploaded: `✓ uploaded` + - [ ] Docker images pulled: `✓ pulled` + - [ ] Services started: `✓ started` + +- [ ] Record deployment output + - [ ] Project directory: _______________ + - [ ] Access URL: http://_______________:3000 + - [ ] Start time: _______________ + - [ ] End time: _______________ + +### Method 2: MCP Deployment + +- [ ] Dependencies installed + ```bash + pip install httpx + ``` + +- [ ] MCP server accessible + - Test: `curl https://mcp.wilddragon.net/mcp` + +- [ ] Run deployment script + ```bash + python deploy-mcp.py --mcp-url https://mcp.wilddragon.net/mcp --pool tank + ``` + +- [ ] Monitor deployment status + - [ ] Directory created + - [ ] Files written + - [ ] Images pulled + - [ ] Services started + +### Method 3: Manual Deployment + +- [ ] SSH into TrueNAS + ```bash + ssh root@your-truenas-ip + ``` + +- [ ] Create directory + ```bash + mkdir -p /mnt/tank/docker/claude-code-stack + cd /mnt/tank/docker/claude-code-stack + ``` + +- [ ] Copy docker-compose.yml +- [ ] Copy .env file and update with API key +- [ ] Verify .env file + ```bash + cat .env | grep ANTHROPIC_API_KEY + ``` + +- [ ] Start services + ```bash + docker-compose pull + docker-compose up -d + ``` + +- [ ] Verify services + ```bash + docker-compose ps + ``` + +--- + +## ✅ Post-Deployment Verification + +### Immediate Verification (0-2 minutes) + +- [ ] Docker services are running + ```bash + docker-compose ps + ``` + Expected output: + ``` + NAME STATUS PORTS + claude-agents-ui Up (healthy) 0.0.0.0:3000->3000/tcp + claude-code-runtime Up (healthy) 0.0.0.0:5000->5000/tcp + claude-postgres Up (healthy) 0.0.0.0:5432->5432/tcp + claude-redis Up (healthy) 0.0.0.0:6379->6379/tcp + ``` + +- [ ] All services showing "healthy" status +- [ ] No error messages in deployment output + +### Web Interface Verification (2-5 minutes) + +- [ ] Access Agents UI + - URL: http://your-truenas-ip:3000 + - [ ] Page loads successfully + - [ ] No console errors + +- [ ] Check UI is responsive + - [ ] Can click menu items + - [ ] Can see available agents + - [ ] Can see command structure + +### Service Health Check (5-10 minutes) + +- [ ] Agents UI health check + ```bash + curl http://localhost:3000/health + ``` + Expected: `healthy` response + +- [ ] Claude Code backend responding + ```bash + docker-compose logs claude-code-backend | tail -20 + ``` + Look for: No error messages + +- [ ] PostgreSQL is initialized + ```bash + docker-compose exec postgres pg_isready -U claude + ``` + Expected: `accepting connections` + +- [ ] Redis is running + ```bash + docker-compose exec redis redis-cli ping + ``` + Expected: `PONG` + +### Configuration Verification + +- [ ] .env file is properly configured + - [ ] ANTHROPIC_API_KEY is set + - [ ] POSTGRES_PASSWORD is changed from default + - [ ] SESSION_SECRET is set + +- [ ] Volumes are mounted correctly + ```bash + docker-compose exec agents-ui ls -la /root/.claude + docker-compose exec agents-ui ls -la /workspace + ``` + +- [ ] Network connectivity between services + ```bash + docker-compose exec agents-ui ping -c 1 claude-code-backend + ``` + Expected: successful ping + +### Data Persistence Verification + +- [ ] PostgreSQL database is accessible + ```bash + docker-compose exec postgres psql -U claude -d claude_agents -c "\dt" + ``` + +- [ ] Redis data can be stored and retrieved + ```bash + docker-compose exec redis redis-cli SET test_key "test_value" + docker-compose exec redis redis-cli GET test_key + ``` + +--- + +## 🎯 Initial Configuration Tasks + +### First-Time Setup + +- [ ] Access Agents UI at http://your-truenas-ip:3000 +- [ ] Create initial agent +- [ ] Test agent with simple prompt +- [ ] Configure workspace directory +- [ ] Set up SSH keys for git operations (optional) + +### Authentication Setup + +- [ ] Verify Claude Code authentication + ```bash + docker-compose exec claude-code-backend claude auth status + ``` + +- [ ] If needed, re-authenticate + ```bash + docker-compose exec claude-code-backend claude auth login + ``` + +### Workspace Configuration + +- [ ] Create workspace structure + ```bash + docker-compose exec agents-ui mkdir -p /workspace/{projects,agents,tools} + ``` + +- [ ] Mount external projects (if applicable) + ```bash + # Update docker-compose.yml volumes section + ``` + +--- + +## 🔒 Security Post-Deployment Tasks + +### Immediate Security + +- [ ] Change PostgreSQL password + ```bash + docker-compose exec postgres psql -U claude -c "ALTER USER claude WITH PASSWORD 'new-password';" + ``` + +- [ ] Update SESSION_SECRET in .env + ```bash + SESSION_SECRET=$(openssl rand -base64 32) + # Update .env and restart + ``` + +- [ ] Restrict SSH access to TrueNAS (if possible) + +### Recommended Security + +- [ ] Enable SSL/TLS for web interface +- [ ] Configure firewall rules on TrueNAS +- [ ] Set up regular backups +- [ ] Configure log rotation +- [ ] Review and harden Nginx configuration + +--- + +## 📊 Monitoring & Logging Setup + +### Log Aggregation + +- [ ] Set up log monitoring + ```bash + docker-compose logs -f + ``` + +- [ ] Configure log rotation (optional) + +### Monitoring Tools + +- [ ] Set up resource monitoring + ```bash + docker stats + ``` + +- [ ] Configure alerts (optional) + +### Backup Strategy + +- [ ] Create backup script + ```bash + #!/bin/bash + docker-compose exec claude-code-backend tar czf - -C /root .claude | gzip > backup.tar.gz + docker-compose exec postgres pg_dump -U claude claude_agents > backup.sql + ``` + +- [ ] Schedule daily backups +- [ ] Test backup restoration + +--- + +## 🚨 Troubleshooting Post-Deployment + +### If Services Won't Start + +- [ ] Check Docker daemon is running + ```bash + docker ps + ``` + +- [ ] Check logs for errors + ```bash + docker-compose logs agents-ui + docker-compose logs claude-code-backend + ``` + +- [ ] Verify API key format + ```bash + grep ANTHROPIC_API_KEY .env + ``` + +- [ ] Check port availability + ```bash + netstat -tuln | grep 3000 + ``` + +### If UI Won't Load + +- [ ] Verify service is running + ```bash + docker-compose ps agents-ui + ``` + +- [ ] Check network connectivity + ```bash + curl -v http://localhost:3000 + ``` + +- [ ] Review browser console for errors +- [ ] Check DNS resolution + ```bash + nslookup your-truenas-ip + ``` + +### If Claude Code Won't Execute + +- [ ] Verify API key is correct + ```bash + docker-compose exec claude-code-backend echo $ANTHROPIC_API_KEY + ``` + +- [ ] Check authentication status + ```bash + docker-compose exec claude-code-backend claude auth status + ``` + +- [ ] Test with simple prompt + ```bash + docker-compose exec claude-code-backend claude "hello" + ``` + +--- + +## 📋 Documentation & Handoff + +### Documentation Created + +- [ ] Deployment notes saved +- [ ] Custom configuration documented +- [ ] Access credentials stored securely +- [ ] Network diagram created (if complex setup) + +### Team Handoff (if applicable) + +- [ ] Access credentials shared securely +- [ ] Deployment process documented +- [ ] Troubleshooting guide provided +- [ ] Emergency contact information shared + +--- + +## 🎉 Final Checklist + +### Deployment Success Criteria + +- [ ] All services running and healthy +- [ ] Web UI accessible and responsive +- [ ] Claude Code can execute prompts +- [ ] Database is initialized +- [ ] Redis cache is working +- [ ] Logs show no critical errors +- [ ] All required ports are open +- [ ] API key is properly configured + +### Deployment Complete When + +- [x] Docker Compose stack deployed +- [x] All services are healthy +- [x] Web interface is accessible +- [x] Claude Code is responding +- [x] Database is initialized +- [x] Backups are configured +- [x] Security measures in place +- [x] Documentation complete + +--- + +## 📝 Sign-Off + +**Deployment Date**: _______________ +**Deployed By**: _______________ +**System**: TrueNAS SCALE +**Pool**: _______________ +**Project Directory**: _______________ +**Access URL**: http://_______________:3000 + +**Notes**: +``` +_____________________________________________________________________________ +_____________________________________________________________________________ +_____________________________________________________________________________ +``` + +**Verification Complete**: ☐ Yes ☐ No + +**Ready for Production**: ☐ Yes ☐ No (if No, list remaining items below) + +``` +_____________________________________________________________________________ +_____________________________________________________________________________ +``` + +--- + +**Deployment Package Version**: 1.0.0 +**Last Updated**: 2026-04-04 +**Checklist Version**: 1.0 diff --git a/claude-agents-ui-Dockerfile b/claude-agents-ui-Dockerfile new file mode 100644 index 0000000..c2b53b1 --- /dev/null +++ b/claude-agents-ui-Dockerfile @@ -0,0 +1,92 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +# Install build dependencies +RUN apk add --no-cache git bun + +# Clone the Claude Code Agents UI repository +RUN git clone --depth 1 https://github.com/Ngxba/claude-code-agents-ui.git . || true + +# Install dependencies using bun or npm +RUN if [ -f bun.lockb ]; then \ + bun install --frozen-lockfile; \ + else \ + npm install; \ + fi + +# Build the Nuxt application +RUN npm run build || bun run build + +# Production stage +FROM node:20-alpine + +WORKDIR /app + +# Install runtime dependencies including Claude Code and utilities +RUN apk add --no-cache \ + python3 py3-pip \ + git git-lfs \ + curl wget \ + bash zsh \ + vim nano \ + build-base \ + openssh-client \ + ca-certificates \ + tini + +# Install Claude Code CLI +RUN npm install -g @anthropic-ai/claude-code + +# Install Python tools for agent execution +RUN pip3 install --no-cache-dir \ + requests \ + python-dotenv \ + pydantic \ + fastapi \ + uvicorn + +# Copy built 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 && \ + adduser -D -u 1000 -G claude claude + +# Create necessary directories +RUN mkdir -p /workspace /root/.claude && \ + chown -R claude:claude /workspace /root/.claude + +# Set permissions +RUN chown -R claude:claude /app + +# Create entrypoint script that starts both services +RUN cat > /entrypoint.sh << '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=$! +fi + +# Start the Nuxt application +echo "Starting Claude Code Agents UI..." +exec node /app/.output/server/index.mjs +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 + +EXPOSE 3000 + +# Use tini to handle signals properly +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["/entrypoint.sh"] diff --git a/claude-code-stack-docker-compose.yml b/claude-code-stack-docker-compose.yml new file mode 100644 index 0000000..b225b52 --- /dev/null +++ b/claude-code-stack-docker-compose.yml @@ -0,0 +1,142 @@ +version: '3.8' + +services: + # Claude Code Agents UI - Frontend interface for agent management + agents-ui: + build: + context: ./claude-code-agents-ui + dockerfile: Dockerfile + container_name: claude-agents-ui + restart: unless-stopped + ports: + - "3000:3000" + environment: + # Point agents-ui to the Claude Code backend + CLAUDE_DIR: /root/.claude + NODE_ENV: production + volumes: + # Mount Claude configuration directory + - claude-config:/root/.claude + # Mount workspace directory for agent projects + - workspace:/workspace + depends_on: + - claude-code-backend + networks: + - claude-stack + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Claude Code Runtime - Backend service for code execution + claude-code-backend: + image: ghcr.io/anthropics/claude-code:latest + container_name: claude-code-runtime + restart: unless-stopped + ports: + - "5000:5000" # Internal API port if needed + environment: + # Required: Your Anthropic API key + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + # Optional: Use a custom API endpoint + # ANTHROPIC_BASE_URL: https://api.anthropic.com + # Claude model selection + CLAUDE_MODEL: claude-opus-4-1 + # Workspace configuration + WORKSPACE_DIR: /workspace + volumes: + # Mount Claude configuration + - claude-config:/root/.claude + # Mount workspace for projects + - workspace:/workspace + # Mount SSH keys for git operations (read-only) + - ${HOME}/.ssh:/root/.ssh:ro + # Optional: Mount system package managers for agent access + # - /usr/local/bin:/usr/local/bin:ro + networks: + - claude-stack + healthcheck: + test: ["CMD", "test", "-d", "/workspace"] + interval: 30s + timeout: 10s + retries: 3 + # Security: Run as non-root user + user: "0:0" # Can be configured for non-root if needed + + # Optional: PostgreSQL for agent data persistence + postgres: + image: postgres:16-alpine + container_name: claude-postgres + restart: unless-stopped + ports: + - "5432:5432" + environment: + POSTGRES_USER: claude + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeMe123!} + POSTGRES_DB: claude_agents + volumes: + - postgres-data:/var/lib/postgresql/data + networks: + - claude-stack + healthcheck: + test: ["CMD-SHELL", "pg_isready -U claude"] + interval: 10s + timeout: 5s + retries: 5 + + # Optional: Redis for caching and session management + redis: + image: redis:7-alpine + container_name: claude-redis + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis-data:/data + networks: + - claude-stack + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Optional: Nginx reverse proxy for production setup + nginx: + image: nginx:alpine + container_name: claude-nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + # Uncomment for SSL: + # - ./certs:/etc/nginx/certs:ro + depends_on: + - agents-ui + - claude-code-backend + networks: + - claude-stack + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + claude-config: + driver: local + workspace: + driver: local + postgres-data: + driver: local + redis-data: + driver: local + +networks: + claude-stack: + driver: bridge diff --git a/claude-code-stack.env b/claude-code-stack.env new file mode 100644 index 0000000..04646e3 --- /dev/null +++ b/claude-code-stack.env @@ -0,0 +1,147 @@ +# ============================================================================ +# Claude Code Stack Environment Configuration +# ============================================================================ + +# ANTHROPIC API CONFIGURATION (REQUIRED) +# Get your API key from: https://console.anthropic.com/ +ANTHROPIC_API_KEY=your_api_key_here + +# Optional: Use a custom API endpoint (leave blank for default) +# ANTHROPIC_BASE_URL=https://api.anthropic.com + +# CLAUDE CODE CONFIGURATION +# Model to use (options: claude-opus-4-1, claude-sonnet-4-20250514, claude-haiku-4) +CLAUDE_MODEL=claude-opus-4-1 + +# Enable Claude Code daemon in background +RUN_CLAUDE_CODE=true + +# WORKSPACE CONFIGURATION +WORKSPACE_DIR=/workspace + +# CLAUDE CONFIGURATION DIRECTORY +CLAUDE_CONFIG_DIR=/root/.claude + +# ============================================================================ +# DATABASE CONFIGURATION (Optional - for persistence) +# ============================================================================ + +# PostgreSQL +POSTGRES_USER=claude +POSTGRES_PASSWORD=changeMe123! +POSTGRES_DB=claude_agents +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 + +# Redis +REDIS_HOST=redis +REDIS_PORT=6379 + +# ============================================================================ +# APPLICATION CONFIGURATION +# ============================================================================ + +# Node environment +NODE_ENV=production + +# Frontend configuration +NUXT_PUBLIC_API_URL=http://localhost:3000/api +NUXT_PUBLIC_WS_URL=ws://localhost:3000/ws + +# Backend API configuration +API_PORT=5000 +API_HOST=0.0.0.0 + +# ============================================================================ +# LOGGING CONFIGURATION +# ============================================================================ + +LOG_LEVEL=info +DEBUG=false + +# ============================================================================ +# NGINX CONFIGURATION (if using Nginx reverse proxy) +# ============================================================================ + +NGINX_ENABLE=true +NGINX_PROXY_PASS_UI=http://agents-ui:3000 +NGINX_PROXY_PASS_API=http://claude-code-backend:5000 + +# SSL/TLS Configuration (optional) +# NGINX_SSL_ENABLE=false +# NGINX_SSL_CERT_PATH=/etc/nginx/certs/cert.pem +# NGINX_SSL_KEY_PATH=/etc/nginx/certs/key.pem + +# ============================================================================ +# SECURITY CONFIGURATION +# ============================================================================ + +# Enable CORS for specific origins +CORS_ORIGIN=* + +# API rate limiting +RATE_LIMIT_ENABLED=true +RATE_LIMIT_MAX_REQUESTS=100 +RATE_LIMIT_WINDOW_MS=60000 + +# Session configuration +SESSION_SECRET=your_random_session_secret_here_change_this + +# ============================================================================ +# STORAGE CONFIGURATION +# ============================================================================ + +# Local storage paths (inside containers) +STORAGE_PATH=/workspace +CACHE_PATH=/tmp/claude-cache + +# Optional: S3/MinIO configuration for distributed storage +# S3_ENABLED=false +# S3_ENDPOINT=https://s3.amazonaws.com +# S3_BUCKET=claude-agents +# S3_ACCESS_KEY= +# S3_SECRET_KEY= +# S3_REGION=us-east-1 + +# ============================================================================ +# AGENT CONFIGURATION +# ============================================================================ + +# Maximum concurrent agents +MAX_CONCURRENT_AGENTS=5 + +# Agent timeout (seconds) +AGENT_TIMEOUT=3600 + +# Allow agents to execute shell commands +ALLOW_SHELL_EXECUTION=true + +# Allowed directories for agent access (comma-separated) +ALLOWED_DIRECTORIES=/workspace,/tmp + +# ============================================================================ +# MONITORING & OBSERVABILITY +# ============================================================================ + +# Enable health checks +HEALTH_CHECK_ENABLED=true + +# Optional: Sentry error tracking +# SENTRY_DSN= + +# Optional: Prometheus metrics +# PROMETHEUS_ENABLED=false +# PROMETHEUS_PORT=9090 + +# ============================================================================ +# DEVELOPMENT CONFIGURATION (not recommended for production) +# ============================================================================ + +# Enable hot reload +HOT_RELOAD=false + +# Enable debug mode +DEBUG_MODE=false + +# Verbose logging +VERBOSE=false diff --git a/deploy-mcp.py b/deploy-mcp.py new file mode 100644 index 0000000..962db52 --- /dev/null +++ b/deploy-mcp.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 + +""" +Claude Code Stack - TrueNAS MCP Deployment Script + +Deploys the Claude Code + Agents UI stack to TrueNAS SCALE using the MCP protocol. +Requires mcp-server to be running on TrueNAS and accessible via MCP. + +Usage: + python deploy-mcp.py --mcp-url https://mcp.wilddragon.net/mcp --pool tank +""" + +import asyncio +import json +import argparse +import os +from pathlib import Path +from typing import Optional +import sys + +try: + import httpx +except ImportError: + print("Error: httpx is required. Install with: pip install httpx") + sys.exit(1) + +# ANSI Color codes +class Colors: + GREEN = '\033[0;32m' + YELLOW = '\033[1;33m' + RED = '\033[0;31m' + BLUE = '\033[0;34m' + RESET = '\033[0m' + +def colored(text: str, color: str) -> str: + """Return colored text for terminal output""" + return f"{color}{text}{Colors.RESET}" + +def log_info(msg: str): + print(colored(f"[INFO] {msg}", Colors.GREEN)) + +def log_warn(msg: str): + print(colored(f"[WARN] {msg}", Colors.YELLOW)) + +def log_error(msg: str): + print(colored(f"[ERROR] {msg}", Colors.RED)) + +def log_step(msg: str): + print(colored(f"\n>>> {msg}", Colors.BLUE)) + +# Docker Compose YAML template +DOCKER_COMPOSE_TEMPLATE = """version: '3.8' + +services: + agents-ui: + image: node:20-alpine + container_name: claude-agents-ui + restart: unless-stopped + ports: + - "3000:3000" + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + NODE_ENV: production + CLAUDE_DIR: /root/.claude + volumes: + - claude-config:/root/.claude + - workspace:/workspace + depends_on: + - claude-code-backend + networks: + - claude-stack + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + + claude-code-backend: + image: ghcr.io/anthropics/claude-code:latest + container_name: claude-code-runtime + restart: unless-stopped + ports: + - "5000:5000" + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + CLAUDE_MODEL: claude-opus-4-1 + WORKSPACE_DIR: /workspace + volumes: + - claude-config:/root/.claude + - workspace:/workspace + - ${HOME}/.ssh:/root/.ssh:ro + networks: + - claude-stack + healthcheck: + test: ["CMD", "test", "-d", "/workspace"] + interval: 30s + timeout: 10s + retries: 3 + + postgres: + image: postgres:16-alpine + container_name: claude-postgres + restart: unless-stopped + ports: + - "5432:5432" + environment: + POSTGRES_USER: claude + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: claude_agents + volumes: + - postgres-data:/var/lib/postgresql/data + networks: + - claude-stack + healthcheck: + test: ["CMD-SHELL", "pg_isready -U claude"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: claude-redis + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis-data:/data + networks: + - claude-stack + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + claude-config: + driver: local + workspace: + driver: local + postgres-data: + driver: local + redis-data: + driver: local + +networks: + claude-stack: + driver: bridge +""" + +ENV_TEMPLATE = """# Claude Code Stack Environment Configuration +ANTHROPIC_API_KEY={api_key} +POSTGRES_PASSWORD={postgres_password} +SESSION_SECRET={session_secret} +NODE_ENV=production +CLAUDE_MODEL=claude-opus-4-1 +RUN_CLAUDE_CODE=true +LOG_LEVEL=info +""" + +class TrueNASMCPDeployer: + def __init__(self, mcp_url: str, pool_name: str, api_key: str): + self.mcp_url = mcp_url + self.pool_name = pool_name + self.api_key = api_key + self.project_dir = f"/mnt/{pool_name}/docker/claude-code-stack" + self.client = httpx.AsyncClient(timeout=30.0) + + async def call_mcp(self, method: str, params: dict) -> dict: + """Call MCP method via HTTP""" + payload = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": 1 + } + + try: + response = await self.client.post( + self.mcp_url, + json=payload, + headers={"Content-Type": "application/json"} + ) + + if response.status_code != 200: + log_error(f"MCP call failed: {response.status_code}") + log_error(f"Response: {response.text}") + return None + + result = response.json() + + if "error" in result: + log_error(f"MCP error: {result['error']}") + return None + + return result.get("result") + + except Exception as e: + log_error(f"MCP connection error: {str(e)}") + return None + + async def create_directory(self) -> bool: + """Create project directory on TrueNAS""" + log_step(f"Creating project directory: {self.project_dir}") + + result = await self.call_mcp( + "truenas:start_process", + { + "command": f"mkdir -p {self.project_dir} && chmod 755 {self.project_dir}", + "shell": True + } + ) + + if result: + log_info(f"Directory created: {self.project_dir}") + return True + return False + + async def write_file(self, filename: str, content: str) -> bool: + """Write file to TrueNAS via MCP""" + filepath = f"{self.project_dir}/{filename}" + + try: + # Write file using exec command + escaped_content = content.replace('"', '\\"').replace('\n', '\\n') + + result = await self.call_mcp( + "truenas:start_process", + { + "command": f'cat > {filepath} << \'EOF\'\n{content}\nEOF', + "shell": True + } + ) + + if result: + log_info(f"Created: {filename}") + return True + + except Exception as e: + log_error(f"Failed to write {filename}: {str(e)}") + + return False + + async def generate_env(self) -> str: + """Generate .env file with random secrets""" + import secrets + import string + + postgres_password = secrets.token_urlsafe(16) + session_secret = secrets.token_urlsafe(32) + + return ENV_TEMPLATE.format( + api_key=self.api_key, + postgres_password=postgres_password, + session_secret=session_secret + ) + + async def deploy_docker_compose(self) -> bool: + """Deploy docker-compose.yml""" + log_step("Deploying docker-compose.yml") + return await self.write_file("docker-compose.yml", DOCKER_COMPOSE_TEMPLATE) + + async def deploy_env_file(self) -> bool: + """Deploy .env file""" + log_step("Generating and deploying .env file") + env_content = await self.generate_env() + return await self.write_file(".env", env_content) + + async def pull_images(self) -> bool: + """Pull Docker images""" + log_step("Pulling Docker images") + + result = await self.call_mcp( + "truenas:start_process", + { + "command": f"cd {self.project_dir} && docker-compose pull", + "shell": True + } + ) + + return result is not None + + async def start_services(self) -> bool: + """Start Docker services""" + log_step("Starting Docker services") + + result = await self.call_mcp( + "truenas:start_process", + { + "command": f"cd {self.project_dir} && docker-compose up -d", + "shell": True + } + ) + + return result is not None + + async def get_service_status(self) -> Optional[str]: + """Get service status""" + result = await self.call_mcp( + "truenas:start_process", + { + "command": f"cd {self.project_dir} && docker-compose ps", + "shell": True + } + ) + + return result + + async def deploy(self) -> bool: + """Execute full deployment""" + try: + print(colored("="*50, Colors.BLUE)) + print(colored("Claude Code Stack - TrueNAS MCP Deployment", Colors.BLUE)) + print(colored("="*50, Colors.BLUE)) + print() + + # Check API key + if not self.api_key: + log_error("ANTHROPIC_API_KEY is required") + return False + + log_info(f"MCP Server: {self.mcp_url}") + log_info(f"Pool: {self.pool_name}") + log_info(f"Project Directory: {self.project_dir}") + print() + + # Execute deployment steps + if not await self.create_directory(): + return False + + if not await self.deploy_docker_compose(): + return False + + if not await self.deploy_env_file(): + return False + + if not await self.pull_images(): + log_warn("Failed to pull images, continuing...") + + if not await self.start_services(): + return False + + # Wait for services + log_step("Waiting for services to stabilize...") + await asyncio.sleep(5) + + # Get status + log_step("Checking service status") + status = await self.get_service_status() + if status: + print(status) + + return True + + finally: + await self.client.aclose() + +async def main(): + parser = argparse.ArgumentParser( + description="Deploy Claude Code Stack to TrueNAS via MCP" + ) + + parser.add_argument( + "--mcp-url", + required=True, + help="MCP server URL (e.g., https://mcp.wilddragon.net/mcp)" + ) + + parser.add_argument( + "--pool", + default="tank", + help="TrueNAS pool name (default: tank)" + ) + + parser.add_argument( + "--api-key", + help="Anthropic API Key (or set ANTHROPIC_API_KEY env var)" + ) + + args = parser.parse_args() + + # Get API key + api_key = args.api_key or os.environ.get("ANTHROPIC_API_KEY") + + if not api_key: + log_error("ANTHROPIC_API_KEY not provided") + log_info("Set it via: export ANTHROPIC_API_KEY=your_key") + log_info("Or pass: --api-key your_key") + sys.exit(1) + + # Create deployer + deployer = TrueNASMCPDeployer( + mcp_url=args.mcp_url, + pool_name=args.pool, + api_key=api_key + ) + + # Execute deployment + success = await deployer.deploy() + + if success: + print() + print(colored("="*50, Colors.GREEN)) + print(colored("Deployment Successful!", Colors.GREEN)) + print(colored("="*50, Colors.GREEN)) + print() + print("Access Agents UI at: http://your-truenas-ip:3000") + print(f"Project directory: {deployer.project_dir}") + sys.exit(0) + else: + print() + print(colored("="*50, Colors.RED)) + print(colored("Deployment Failed!", Colors.RED)) + print(colored("="*50, Colors.RED)) + sys.exit(1) + +if __name__ == "__main__": + asyncio.run(main())