mvp-factory-setups/CLAUDE.md

15 KiB

🚀 AI Dev Factory - Session Continuation Guide

Last Updated: 2024-11-28
Current Phase: Phase 2 - OpenHands Integration (API Mode)
Time to Completion: ~3-4 hours
Next Approach: OpenHands Server API Mode


📊 CURRENT STATUS

What's Working:

  • Gitea: https://git.oky.sh (HTTPS, PostgreSQL backend)
  • n8n: https://n8n.oky.sh (HTTPS, workflow automation)
  • Caddy: Auto SSL with Let's Encrypt
  • SSH: n8n → localhost credentials configured
  • OpenHands CLI: /home/bam/.local/bin/openhands (v1.3.0)

⚠️ Current Blocker:

  • OpenHands headless mode (Docker) hangs on runtime startup
  • Solution: Switch to OpenHands Server API mode

🎯 Goal:

Create automated workflow: Gitea push → n8n → OpenHands API → Build/Test


🔧 SYSTEM CONFIGURATION

VM Details:

Hostname: ai-dev-node
IP: 10.10.10.11
User: bam
CPU: 8 vCPU
RAM: 24GB (optimized from 40GB)
OS: Ubuntu 22.04

Services Running:

docker compose ps
# Expected output:
# - caddy (ports 80, 443)
# - gitea (port 3333 internal, 2229 SSH)
# - n8n (port 5678 internal)
# - postgres (port 5432 internal)

Important Directories:

/home/bam/services-stack/     # Docker services (Gitea, n8n, Caddy)
/home/bam/.local/bin/         # OpenHands CLI
/home/bam/.openhands/         # OpenHands config & sessions
  ├── agent_settings.json     # Agent configuration
  ├── cache/                  # Model cache
  ├── conversations/          # Chat history
  ├── sessions/               # Active sessions data
  └── settings.json           # LLM & server settings
/home/bam/workspace/          # Default workspace for builds
/home/bam/.ssh/n8n_key        # SSH key for n8n automation

API Keys Location:

/home/bam/openhands/.env
# Contains:
# MINIMAX_API_KEY=xxx       (Primary LLM)
# DEEPSEEK_API_KEY=xxx      (Backup LLM)
# OPENAI_API_KEY=xxx        (Optional 2nd backup)

🎯 NEXT STEPS: OpenHands Server API Setup

Step 1: Start OpenHands in Server Mode (15 min)

Goal: Run OpenHands as persistent HTTP API service

OpenHands serve uses port 3000 by default (cannot be changed in current version)

# Test server startup
/home/bam/.local/bin/openhands serve

# Expected: Server starts, listens on http://localhost:3000

Create systemd service for persistence:

sudo nano /etc/systemd/system/openhands.service

# Paste:
[Unit]
Description=OpenHands Server
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=bam
WorkingDirectory=/home/bam
Environment="PATH=/home/bam/.local/bin:/usr/local/bin:/usr/bin:/bin"
EnvironmentFile=/home/bam/openhands/.env
ExecStart=/home/bam/.local/bin/openhands serve
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

# Save and enable:
sudo systemctl daemon-reload
sudo systemctl enable openhands.service
sudo systemctl start openhands.service
sudo systemctl status openhands.service

Verify:

# Check if running
curl http://localhost:3000/

# Check logs
sudo journalctl -u openhands.service -f

Step 1.5: Configure Backup LLM Models (10 min)

Goal: Setup DeepSeek V2 as fallback when MiniMax fails

OpenHands can use multiple LLM providers with automatic fallback.

Check current config:

cat /home/bam/.openhands/settings.json

Update settings to include backup models:

nano /home/bam/.openhands/settings.json

Add LLM configuration:

{
  "LLM_MODEL": "openai/MiniMax-M2",
  "LLM_API_KEY": "${MINIMAX_API_KEY}",
  "LLM_BASE_URL": "https://api.minimax.io/v1",
  
  "LLM_FALLBACK_MODELS": [
    {
      "model": "deepseek/deepseek-coder-v2",
      "api_key": "${DEEPSEEK_API_KEY}",
      "base_url": "https://api.deepseek.com/v1"
    },
    {
      "model": "gpt-4o",
      "api_key": "${OPENAI_API_KEY}",
      "base_url": "https://api.openai.com/v1"
    }
  ],
  
  "LLM_TIMEOUT": 60,
  "LLM_RETRY_COUNT": 3
}

Verify API keys are loaded:

cat /home/bam/openhands/.env
# Should contain:
# MINIMAX_API_KEY=xxx
# DEEPSEEK_API_KEY=xxx
# OPENAI_API_KEY=xxx (optional backup)

Note: Exact config format may vary. Check OpenHands documentation or existing settings.json structure. The systemd service will load these env vars automatically.

Restart OpenHands to apply:

sudo systemctl restart openhands.service
sudo systemctl status openhands.service

Step 2: Discover API Endpoints (15 min)

Goal: Find available API endpoints for triggering tasks

Check OpenHands documentation:

# Look for API docs in help
/home/bam/.local/bin/openhands serve --help

# Or check if web UI exposes API docs:
# Visit: http://10.10.10.11:3000/docs
# Or: http://10.10.10.11:3000/api/docs

Expected endpoints (typical OpenHands API):

POST /api/sessions          # Create new session
POST /api/sessions/{id}/messages  # Send task message
GET  /api/sessions/{id}     # Get session status
GET  /api/sessions/{id}/events    # Get execution events

Test manually:

# Create session
curl -X POST http://localhost:3000/api/sessions \
  -H "Content-Type: application/json" \
  -d '{
    "workspace": "/home/bam/workspace",
    "model": "openai/MiniMax-M2",
    "api_key": "your_minimax_key"
  }'

# Response should include session_id

If API endpoints unclear:

  • Check OpenHands GitHub docs
  • Inspect network requests in web UI (browser DevTools)
  • Look for OpenAPI/Swagger spec

Step 3: Create n8n → OpenHands API Workflow (45 min)

Goal: Build n8n workflow that calls OpenHands API

Workflow Design:

[1] Manual Trigger (for testing)
     ↓
[2] HTTP Request - Create Session
     → POST /api/sessions
     → Save session_id
     ↓
[3] HTTP Request - Send Task
     → POST /api/sessions/{session_id}/messages
     → Body: { "task": "Create file test.txt" }
     ↓
[4] Wait 5 seconds
     ↓
[5] HTTP Request - Get Status (loop until done)
     → GET /api/sessions/{session_id}
     → Check if "status": "completed"
     ↓
[6] If Node - Check Success
     ├─ TRUE → [7] Get Results
     └─ FALSE → [8] Error Handler

n8n Configuration:

Node 1: Manual Trigger

  • Just add it, no config

Node 2: HTTP Request - Create Session

Method: POST
URL: http://127.0.0.1:3000/api/sessions
Headers:
  Content-Type: application/json
Body:
{
  "workspace": "/home/bam/workspace",
  "model": "openai/MiniMax-M2",
  "api_key": "{{$env.MINIMAX_API_KEY}}"
}

Options:
  Response Format: JSON

Node 3: HTTP Request - Send Task

Method: POST
URL: http://127.0.0.1:3000/api/sessions/{{ $json.session_id }}/messages
Body:
{
  "task": "Create a file named test.txt with content: Hello from n8n API!"
}

Node 4: Wait Node

Time: 5 seconds

Node 5: HTTP Request - Get Status

Method: GET
URL: http://127.0.0.1:3000/api/sessions/{{ $node["HTTP Request"].json.session_id }}

# May need to loop this until status is "completed"
# Use n8n Loop node if available

Node 6: SSH - Verify File Created

Credentials: ai-dev-localhost
Command: cat /home/bam/workspace/test.txt

Test Workflow:

  1. Execute manually
  2. Check each node output
  3. Verify file created in /home/bam/workspace/

Step 4: Gitea Webhook Integration (30 min)

Goal: Trigger n8n workflow on git push

In Gitea (https://git.oky.sh):

  1. Create test repository: test-project
  2. Go to Settings → Webhooks → Add Webhook → Gitea
  3. Configure:
    Target URL: https://n8n.oky.sh/webhook/gitea-push
    HTTP Method: POST
    Content Type: application/json
    Secret: [generate random string]
    Trigger On: Push events
    Active: ✓
    
  4. Test webhook

In n8n:

  1. Replace Manual Trigger with Webhook Trigger

  2. Configure:

    Webhook URLs: 
      Production: https://n8n.oky.sh/webhook/gitea-push
    
    HTTP Method: POST
    
    Authentication: Header Auth
      Name: X-Gitea-Signature
      Value: [your secret from Gitea]
    
    Response:
      Mode: Last Node
      Code: 200
    
  3. Extract data:

    Repository: {{ $json.repository.full_name }}
    Commit: {{ $json.commits[0].message }}
    Branch: {{ $json.ref }}
    
  4. Pass to OpenHands:

    Task: "Build and test project {{ $json.repository.full_name }} on commit {{ $json.after }}"
    Workspace: "/home/bam/workspace/{{ $json.repository.name }}"
    

Test:

# In test-project repo
echo "Test" > README.md
git add .
git commit -m "Test webhook"
git push origin main

# Check n8n workflow executes
# Check OpenHands builds project

Step 5: Build Workflow with Retry Logic (1-2 hours)

Goal: Production-ready workflow with error handling

Enhanced Workflow:

[1] Webhook Trigger (Gitea push)
     ↓
[2] Extract Repo Info
     ↓
[3] Clone/Update Repository (SSH)
     → git clone or git pull
     ↓
[4] Create OpenHands Session
     ↓
[5] Send Build Task
     → Task: "Run npm install, npm test, npm build"
     ↓
[6] Poll Status (Loop)
     → Check every 10s
     → Timeout: 5 minutes
     ↓
[7] If Node - Build Success?
     ├─ YES → [8] Success Notification
     │         └─ Update commit status ✅
     │
     └─ NO → [9] Get Error Details
               ↓
          [10] Send Feedback to OpenHands
               → "Build failed with: {error}, please fix"
               ↓
          [11] Retry Counter (max 3)
               ├─ < 3 → Back to [5]
               └─ ≥ 3 → [12] Final Failure Notification

Key Components:

Clone Repository Node (SSH):

cd /home/bam/workspace
if [ -d "{{ $json.repo_name }}" ]; then
  cd {{ $json.repo_name }} && git pull
else
  git clone {{ $json.clone_url }}
fi

Build Task (to OpenHands):

{
  "task": "Navigate to /home/bam/workspace/{{ $json.repo_name }} and run: npm install && npm test && npm build. Report any errors.",
  "workspace": "/home/bam/workspace/{{ $json.repo_name }}",
  "max_iterations": 20
}

Retry Logic (n8n Function Node):

// Get retry count from workflow static data
const retries = $workflow.staticData.retry_count || 0;

if (retries >= 3) {
  return { 
    action: 'fail',
    message: 'Max retries exceeded'
  };
}

// Increment retry
$workflow.staticData.retry_count = retries + 1;

return {
  action: 'retry',
  attempt: retries + 1,
  feedback: $json.error_message
};

Success Notification (HTTP to Gitea API):

POST https://git.oky.sh/api/v1/repos/{{ $json.repo }}/statuses/{{ $json.commit_sha }}
Authorization: token YOUR_GITEA_TOKEN
Body:
{
  "state": "success",
  "description": "Build passed",
  "context": "openhands/build"
}

🧪 TESTING CHECKLIST

Integration Tests:

  • OpenHands server starts and responds
  • API creates sessions successfully
  • API accepts and executes tasks
  • Tasks complete and return results
  • n8n can call all API endpoints
  • Webhook receives Gitea events
  • Repository clones correctly
  • Build task executes
  • Success path works end-to-end
  • Failure triggers retry
  • Max retries stops infinite loop
  • Notifications sent correctly

Error Scenarios:

  • Invalid API request
  • OpenHands timeout
  • Build failure (syntax error)
  • Network issues
  • Git clone failure
  • Webhook auth failure

📋 TROUBLESHOOTING

OpenHands Server Won't Start:

# Check if port in use
sudo netstat -tulpn | grep 3000

# Check OpenHands logs
sudo journalctl -u openhands.service -n 50

# Try manual start to see errors
/home/bam/.local/bin/openhands serve

API Endpoints Not Found:

# Check OpenHands version
/home/bam/.local/bin/openhands --version

# May need to update
pipx upgrade openhands-ai

# Or reinstall
pipx uninstall openhands-ai
pipx install openhands-ai

n8n Cannot Reach OpenHands:

# Test from n8n container
docker exec -it n8n curl http://host.docker.internal:3000

# If fails, check if n8n has host.docker.internal access
# May need to add to docker-compose:
extra_hosts:
  - "host.docker.internal:host-gateway"

Webhook Not Triggering:

# Check n8n webhook URL is accessible
curl https://n8n.oky.sh/webhook/gitea-push

# Check Gitea webhook logs:
# Gitea UI → Repository → Settings → Webhooks → Recent Deliveries

# Test with manual webhook trigger in Gitea UI

🎯 SUCCESS CRITERIA

Phase 2 Complete When:

  1. OpenHands server running persistently
  2. n8n can create sessions via API
  3. Simple test task executes successfully
  4. Gitea webhook triggers n8n workflow
  5. End-to-end: Push → Build → Success/Fail

Phase 3 Complete When:

  1. Retry logic working (max 3 attempts)
  2. Error feedback to OpenHands
  3. Commit status updates in Gitea
  4. Tested with real project build

📚 REFERENCE COMMANDS

Quick Status Check:

# All services status
cd /home/bam/services-stack && docker compose ps

# OpenHands server
sudo systemctl status openhands.service

# Check OpenHands API
curl http://localhost:3000/

# n8n workflows
curl -u admin:password https://n8n.oky.sh/api/v1/workflows

Restart Services:

# Restart all Docker services
cd /home/bam/services-stack
docker compose restart

# Restart OpenHands only
sudo systemctl restart openhands.service

# Restart n8n only
docker compose restart n8n

View Logs:

# OpenHands logs
sudo journalctl -u openhands.service -f

# n8n logs
docker logs -f n8n

# Caddy logs
docker logs -f caddy

# Gitea logs
docker logs -f gitea

🔐 CREDENTIALS REFERENCE

n8n Login:

Gitea Login:

API Keys:

  • MiniMax: /home/bam/openhands/.env
  • SSH Key: /home/bam/.ssh/n8n_key

⏱️ ESTIMATED TIME BREAKDOWN

  • Step 1: OpenHands Server Setup: 15 min
  • Step 1.5: Backup LLM Configuration: 10 min
  • Step 2: API Discovery: 15 min
  • Step 3: n8n API Workflow: 45 min
  • Step 4: Gitea Webhook: 30 min
  • Step 5: Retry Logic: 1-2 hours
  • Testing & Debugging: 30 min

Total: 3-4 hours


🚀 READY TO START?

Pre-flight Checklist:

First Command:

/home/bam/.local/bin/openhands serve

If starts successfully → proceed to Step 1 systemd service!
If error → investigate OpenHands CLI installation


📝 NOTES

  • Port 3000: OpenHands serve uses port 3000 (not configurable in CLI v1.3.0)
  • API Mode vs Headless: API is more reliable for automation
  • Persistence: systemd service ensures OpenHands survives reboots
  • Security: OpenHands API on localhost only (127.0.0.1)
  • Scaling: Can add more OpenHands instances later
  • LLM Fallback: MiniMax M2 → DeepSeek V2 → OpenAI GPT-4o (if configured)

Good luck! 🎯