Compare commits

...

2 Commits

Author SHA1 Message Date
openhands
2a001491c3 Fix: Add init-db.sh to create additional databases for Keycloak
Keycloak requires its own database. This script creates the keycloak
and litellm databases during PostgreSQL initialization.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-01-15 06:16:59 +00:00
openhands
11c11e633a Add local development setup for enterprise server
This adds Docker Compose configuration and documentation for running
OpenHands Enterprise locally with all required services:

- docker-compose.local.yml: Orchestrates PostgreSQL, Redis, Keycloak, and LiteLLM
- litellm-config.yaml: LiteLLM proxy configuration with model definitions
- .env.example: Example environment variables for local development
- LOCAL_DEV_SETUP.md: Comprehensive setup documentation
- setup-local.sh: Interactive setup helper script

Co-authored-by: openhands <openhands@all-hands.dev>
2026-01-15 06:06:18 +00:00
6 changed files with 909 additions and 0 deletions

118
enterprise/.env.example Normal file
View File

@@ -0,0 +1,118 @@
# ============================================
# OpenHands Enterprise Local Development
# Environment Variables Example
# ============================================
# Copy this to .env and fill in your values
# ============================================
# Database Configuration
# ============================================
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASS=postgres
DB_NAME=openhands
# ============================================
# Redis Configuration
# ============================================
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# ============================================
# Keycloak Configuration
# ============================================
# For local development with the docker-compose setup
KEYCLOAK_SERVER_URL=http://localhost:8080
KEYCLOAK_SERVER_URL_EXT=http://localhost:8080
KEYCLOAK_REALM_NAME=openhands
KEYCLOAK_CLIENT_ID=openhands-client
KEYCLOAK_CLIENT_SECRET=your-client-secret
KEYCLOAK_PROVIDER_NAME=github
KEYCLOAK_ADMIN_PASSWORD=admin
# ============================================
# LiteLLM Configuration
# ============================================
LITE_LLM_API_URL=http://localhost:4000
LITE_LLM_API_KEY=sk-local-dev-master-key
LITELLM_DEFAULT_MODEL=litellm_proxy/claude-sonnet-4-20250514
# ============================================
# OpenHands Configuration
# ============================================
OPENHANDS_CONFIG_CLS=server.config.SaaSServerConfig
OPENHANDS_GITHUB_SERVICE_CLS=integrations.github.github_service.SaaSGitHubService
OPENHANDS_GITLAB_SERVICE_CLS=integrations.gitlab.gitlab_service.SaaSGitLabService
OPENHANDS_BITBUCKET_SERVICE_CLS=integrations.bitbucket.bitbucket_service.SaaSBitBucketService
OPENHANDS_CONVERSATION_VALIDATOR_CLS=storage.saas_conversation_validator.SaasConversationValidator
# ============================================
# GitHub App Configuration (Optional)
# Required for GitHub OAuth and GitHub integration features
# Create a GitHub App at: https://github.com/settings/apps
# ============================================
# GITHUB_APP_CLIENT_ID=
# GITHUB_APP_CLIENT_SECRET=
# GITHUB_APP_WEBHOOK_SECRET=
# GITHUB_APP_PRIVATE_KEY=
# ============================================
# GitLab App Configuration (Optional)
# Required for GitLab OAuth and integration features
# ============================================
# GITLAB_APP_CLIENT_ID=
# GITLAB_APP_CLIENT_SECRET=
# ============================================
# Bitbucket App Configuration (Optional)
# ============================================
# BITBUCKET_APP_CLIENT_ID=
# BITBUCKET_APP_CLIENT_SECRET=
# ============================================
# Feature Flags
# ============================================
ENABLE_BILLING=false
HIDE_LLM_SETTINGS=false
ENABLE_JIRA=false
ENABLE_JIRA_DC=false
ENABLE_LINEAR=false
LOCAL_DEPLOYMENT=true
# ============================================
# Frontend Configuration
# ============================================
# Path to the frontend build directory
# FRONTEND_DIRECTORY=../frontend/build
# ============================================
# Logging
# ============================================
LOG_PLAIN_TEXT=1
# ============================================
# Runtime Configuration
# ============================================
SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:main-nikolaik
# ============================================
# PostHog (Analytics - can use dummy for local)
# ============================================
POSTHOG_CLIENT_KEY=test
# ============================================
# LLM API Keys (for LiteLLM proxy)
# Add these to litellm container environment
# ============================================
# OPENAI_API_KEY=
# ANTHROPIC_API_KEY=
# GOOGLE_API_KEY=
# ============================================
# CORS Configuration
# ============================================
WEB_HOST=localhost:3000
PERMITTED_CORS_ORIGINS=http://localhost:3000,http://localhost:3001

View File

@@ -0,0 +1,323 @@
# OpenHands Enterprise Local Development Setup
This guide provides instructions for setting up OpenHands Enterprise locally for development.
## Prerequisites
- **Docker** and **Docker Compose** (v2+)
- **Python 3.12+**
- **Poetry** (Python package manager)
- **Node.js 18+** and **npm** (for frontend)
- An LLM API key (OpenAI, Anthropic, or other supported provider)
## Quick Start
### 1. Start Infrastructure Services
Start all required services using Docker Compose:
```bash
cd enterprise
# Start PostgreSQL, Redis, Keycloak, and LiteLLM
docker-compose -f docker-compose.local.yml up -d
```
This will start:
- **PostgreSQL** on port `5432` (database for OpenHands and Keycloak)
- **Redis** on port `6379` (caching and pub/sub)
- **Keycloak** on port `8080` (authentication)
- **LiteLLM** on port `4000` (LLM proxy)
### 2. Configure Environment Variables
Copy the example environment file and configure it:
```bash
cp .env.example .env
```
Edit `.env` and set your LLM API keys. For LiteLLM to work, you need at least one of:
- `OPENAI_API_KEY` - for GPT models
- `ANTHROPIC_API_KEY` - for Claude models
Update the docker-compose to include your API keys:
```bash
# Add to litellm service environment in docker-compose.local.yml
# Or create a .env file for docker-compose
# Then restart litellm
docker-compose -f docker-compose.local.yml up -d litellm
```
### 3. Initialize the Database
Run Alembic migrations to set up the database schema:
```bash
cd enterprise
poetry install
poetry run alembic upgrade head
```
### 4. Configure Keycloak (Authentication)
#### Option A: Skip Keycloak (Simplified Setup)
For basic local development without authentication, you can skip Keycloak by not setting `GITHUB_APP_CLIENT_ID` and related variables. The app will still work but OAuth login won't be available.
#### Option B: Set Up Keycloak Realm
1. Access Keycloak Admin Console at `http://localhost:8080`
2. Login with `admin` / `admin`
3. Create a new realm called `openhands`
4. Create a client:
- Client ID: `openhands-client`
- Client authentication: ON
- Valid redirect URIs: `http://localhost:3000/*`, `http://localhost:3001/*`
5. Copy the client secret and update your `.env`:
```
KEYCLOAK_CLIENT_SECRET=<your-client-secret>
```
### 5. Build the Frontend
```bash
# From the repository root (not enterprise/)
cd ..
make build-frontend
# OR
cd frontend && npm install && npm run build
```
### 6. Start the Enterprise Backend
```bash
cd enterprise
# Option A: Using Make
OPENHANDS_PATH=../ make start-backend
# Option B: Using Poetry directly
FRONTEND_DIRECTORY=../frontend/build poetry run uvicorn saas_server:app --host 127.0.0.1 --port 3000 --reload
```
### 7. Access the Application
- **Frontend**: http://localhost:3000 (or 3001 if using dev server)
- **Backend API**: http://localhost:3000/api
- **Keycloak Admin**: http://localhost:8080
- **LiteLLM Proxy**: http://localhost:4000
## Individual Service Setup
If you prefer to run services individually:
### PostgreSQL
```bash
docker run -d \
--name openhands-postgres \
-p 5432:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=openhands \
postgres:15
```
### Redis
```bash
docker run -d \
--name openhands-redis \
-p 6379:6379 \
redis:7-alpine
```
### Keycloak
```bash
docker run -d \
--name openhands-keycloak \
-p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-e KC_HTTP_ENABLED=true \
-e KC_HOSTNAME_STRICT=false \
quay.io/keycloak/keycloak:26.1.1 start-dev
```
### LiteLLM
```bash
# Create litellm-config.yaml first (see file in this directory)
docker run -d \
--name openhands-litellm \
-p 4000:4000 \
-v $(pwd)/litellm-config.yaml:/app/config.yaml \
-e LITELLM_MASTER_KEY=sk-local-dev-master-key \
-e OPENAI_API_KEY=your-openai-key \
-e ANTHROPIC_API_KEY=your-anthropic-key \
ghcr.io/berriai/litellm:main-latest \
--config /app/config.yaml --port 4000 --host 0.0.0.0
```
## Configuration Details
### Environment Variables Reference
| Variable | Description | Default |
|----------|-------------|---------|
| `DB_HOST` | PostgreSQL host | `localhost` |
| `DB_PORT` | PostgreSQL port | `5432` |
| `DB_USER` | PostgreSQL user | `postgres` |
| `DB_PASS` | PostgreSQL password | `postgres` |
| `DB_NAME` | PostgreSQL database name | `openhands` |
| `REDIS_HOST` | Redis host | `localhost` |
| `REDIS_PORT` | Redis port | `6379` |
| `KEYCLOAK_SERVER_URL` | Keycloak internal URL | - |
| `KEYCLOAK_SERVER_URL_EXT` | Keycloak external URL | - |
| `KEYCLOAK_REALM_NAME` | Keycloak realm | - |
| `KEYCLOAK_CLIENT_ID` | Keycloak client ID | - |
| `KEYCLOAK_CLIENT_SECRET` | Keycloak client secret | - |
| `LITE_LLM_API_URL` | LiteLLM proxy URL | `http://localhost:4000` |
| `LITE_LLM_API_KEY` | LiteLLM master key | - |
| `LITELLM_DEFAULT_MODEL` | Default model to use | `litellm_proxy/claude-sonnet-4-20250514` |
| `OPENHANDS_CONFIG_CLS` | Server config class | `server.config.SaaSServerConfig` |
| `LOCAL_DEPLOYMENT` | Flag for local mode | `true` |
### LiteLLM Configuration
The `litellm-config.yaml` file defines which LLM models are available through the proxy. You can add or modify models as needed.
To test if LiteLLM is working:
```bash
curl http://localhost:4000/v1/models
```
### Keycloak Realm Setup for GitHub OAuth
To enable GitHub OAuth through Keycloak:
1. Create a GitHub OAuth App at https://github.com/settings/developers
2. In Keycloak:
- Go to Identity Providers > Add provider > GitHub
- Enter your GitHub Client ID and Secret
- Set redirect URI to match your Keycloak URL
## Troubleshooting
### Database Connection Issues
```bash
# Check if PostgreSQL is running
docker ps | grep postgres
# Check logs
docker logs openhands-postgres
# Test connection
psql -h localhost -U postgres -d openhands
```
### Redis Connection Issues
```bash
# Check if Redis is running
docker ps | grep redis
# Test connection
redis-cli -h localhost ping
```
### Keycloak Issues
```bash
# Check logs
docker logs openhands-keycloak
# Access admin console
open http://localhost:8080/admin
```
### LiteLLM Issues
```bash
# Check logs
docker logs openhands-litellm
# Check health
curl http://localhost:4000/health
# List available models
curl http://localhost:4000/v1/models
```
### Common Issues
1. **"Connection refused" errors**: Ensure all Docker containers are running and ports are not blocked.
2. **Authentication failures**: Check Keycloak configuration and client credentials.
3. **LLM errors**: Verify your API keys are correctly set in the LiteLLM environment.
4. **Database migration errors**: Ensure PostgreSQL is running before running migrations.
## Stopping Services
```bash
# Stop all services
docker-compose -f docker-compose.local.yml down
# Stop and remove volumes (clears all data)
docker-compose -f docker-compose.local.yml down -v
```
## Development Tips
1. **Hot reloading**: Use `--reload` flag with uvicorn for automatic server restarts.
2. **Frontend development**: Run the frontend dev server separately for faster iteration:
```bash
cd ../frontend && npm run dev
```
3. **Database changes**: After modifying models, create a new migration:
```bash
poetry run alembic revision --autogenerate -m "description"
poetry run alembic upgrade head
```
4. **Debugging**: Set `LOG_PLAIN_TEXT=1` for readable logs.
## Minimal Setup (Without Authentication)
For the simplest possible setup without OAuth:
```bash
# Start only PostgreSQL and Redis
docker run -d --name openhands-postgres -p 5432:5432 \
-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=openhands postgres:15
docker run -d --name openhands-redis -p 6379:6379 redis:7-alpine
# Create minimal .env
cat > .env << 'EOF'
DB_HOST=localhost
REDIS_HOST=localhost
OPENHANDS_CONFIG_CLS=server.config.SaaSServerConfig
LOCAL_DEPLOYMENT=true
POSTHOG_CLIENT_KEY=test
LOG_PLAIN_TEXT=1
EOF
# Run migrations and start server
poetry install
poetry run alembic upgrade head
FRONTEND_DIRECTORY=../frontend/build poetry run uvicorn saas_server:app --host 0.0.0.0 --port 3000 --reload
```
This provides a working enterprise server without Keycloak authentication or LiteLLM proxy. You can configure LLM directly through the UI or environment variables.

View File

@@ -0,0 +1,108 @@
# Docker Compose for running OpenHands Enterprise locally
# Usage: docker-compose -f docker-compose.local.yml up -d
services:
# PostgreSQL database
postgres:
image: postgres:15
container_name: openhands-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: openhands
# Create additional databases for keycloak and litellm
POSTGRES_MULTIPLE_DATABASES: keycloak,litellm
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
# Redis for caching and pub/sub
redis:
image: redis:7-alpine
container_name: openhands-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
# Keycloak for authentication
keycloak:
image: quay.io/keycloak/keycloak:26.1.1
container_name: openhands-keycloak
environment:
# Admin credentials
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
# Database configuration (using embedded H2 for simplicity)
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: postgres
KC_DB_PASSWORD: postgres
# Features
KC_FEATURES: token-exchange
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true
# HTTP settings (for local dev, disable HTTPS requirement)
KC_HTTP_ENABLED: true
KC_HTTP_PORT: 8080
KC_HOSTNAME_STRICT: false
KC_HOSTNAME_STRICT_HTTPS: false
KC_PROXY_HEADERS: xforwarded
ports:
- "8080:8080"
command: start-dev
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e 'GET /health/ready HTTP/1.1\r\nhost: localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
interval: 10s
timeout: 10s
retries: 10
start_period: 30s
# LiteLLM Proxy for LLM routing
litellm:
image: ghcr.io/berriai/litellm:main-latest
container_name: openhands-litellm
environment:
# Master key for admin access
LITELLM_MASTER_KEY: sk-local-dev-master-key
# Database for tracking spend/keys (using same postgres)
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/litellm
# General settings
LITELLM_LOG: DEBUG
ports:
- "4000:4000"
volumes:
- ./litellm-config.yaml:/app/config.yaml
command: --config /app/config.yaml --port 4000 --host 0.0.0.0 --detailed_debug
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 10s
timeout: 10s
retries: 5
start_period: 15s
volumes:
postgres_data:
redis_data:
networks:
default:
name: openhands-network

14
enterprise/init-db.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Initialize additional databases for OpenHands Enterprise
set -e
# Create keycloak database
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE DATABASE keycloak;
CREATE DATABASE litellm;
GRANT ALL PRIVILEGES ON DATABASE keycloak TO $POSTGRES_USER;
GRANT ALL PRIVILEGES ON DATABASE litellm TO $POSTGRES_USER;
EOSQL
echo "Additional databases created: keycloak, litellm"

View File

@@ -0,0 +1,76 @@
# LiteLLM Configuration for Local Development
# Documentation: https://docs.litellm.ai/docs/proxy/configs
general_settings:
master_key: sk-local-dev-master-key
database_url: postgresql://postgres:postgres@postgres:5432/litellm
model_list:
# OpenAI Models
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
- model_name: gpt-4o-mini
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY
- model_name: gpt-4-turbo
litellm_params:
model: openai/gpt-4-turbo
api_key: os.environ/OPENAI_API_KEY
# Anthropic Claude Models
- model_name: claude-3-5-sonnet-20241022
litellm_params:
model: anthropic/claude-3-5-sonnet-20241022
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: claude-3-7-sonnet-20250219
litellm_params:
model: anthropic/claude-3-7-sonnet-20250219
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: claude-sonnet-4-20250514
litellm_params:
model: anthropic/claude-sonnet-4-20250514
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: claude-opus-4-5-20251101
litellm_params:
model: anthropic/claude-opus-4-5-20251101
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: claude-3-opus-20240229
litellm_params:
model: anthropic/claude-3-opus-20240229
api_key: os.environ/ANTHROPIC_API_KEY
# Google Gemini Models (optional)
- model_name: gemini-1.5-pro
litellm_params:
model: gemini/gemini-1.5-pro
api_key: os.environ/GOOGLE_API_KEY
- model_name: gemini-2.0-flash
litellm_params:
model: gemini/gemini-2.0-flash
api_key: os.environ/GOOGLE_API_KEY
litellm_settings:
# Enable request/response logging
set_verbose: true
# Fallback behavior
drop_params: true
# Default max tokens if not specified
max_tokens: 4096
router_settings:
# Enable retries on failure
num_retries: 2
# Timeout for each request
timeout: 600
# Routing strategy
routing_strategy: simple-shuffle

270
enterprise/setup-local.sh Executable file
View File

@@ -0,0 +1,270 @@
#!/bin/bash
# OpenHands Enterprise Local Development Setup Script
# This script helps set up the local development environment
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENTERPRISE_DIR="$SCRIPT_DIR"
OPENHANDS_DIR="$(dirname "$ENTERPRISE_DIR")"
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ OpenHands Enterprise Local Development Setup ║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════╝${NC}"
echo ""
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to check if a Docker container is running
container_running() {
docker ps --format '{{.Names}}' | grep -q "^$1$"
}
# Function to wait for a service
wait_for_service() {
local host=$1
local port=$2
local name=$3
local max_attempts=30
local attempt=1
echo -ne " Waiting for $name to be ready..."
while ! nc -z "$host" "$port" 2>/dev/null; do
if [ $attempt -ge $max_attempts ]; then
echo -e " ${RED}FAILED${NC}"
echo -e " ${RED}$name did not become ready in time${NC}"
return 1
fi
sleep 2
attempt=$((attempt + 1))
echo -ne "."
done
echo -e " ${GREEN}READY${NC}"
}
# Check prerequisites
echo -e "${YELLOW}Checking prerequisites...${NC}"
if ! command_exists docker; then
echo -e " ${RED}✗ Docker is not installed${NC}"
echo " Please install Docker: https://docs.docker.com/get-docker/"
exit 1
fi
echo -e " ${GREEN}✓ Docker${NC}"
if ! command_exists docker-compose && ! docker compose version >/dev/null 2>&1; then
echo -e " ${RED}✗ Docker Compose is not installed${NC}"
exit 1
fi
echo -e " ${GREEN}✓ Docker Compose${NC}"
if ! command_exists poetry; then
echo -e " ${RED}✗ Poetry is not installed${NC}"
echo " Install with: curl -sSL https://install.python-poetry.org | python3 -"
exit 1
fi
echo -e " ${GREEN}✓ Poetry${NC}"
echo ""
# Parse arguments
MINIMAL=false
FULL=false
START_ONLY=false
STOP=false
while [[ $# -gt 0 ]]; do
case $1 in
--minimal)
MINIMAL=true
shift
;;
--full)
FULL=true
shift
;;
--start)
START_ONLY=true
shift
;;
--stop)
STOP=true
shift
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --minimal Start only PostgreSQL and Redis (no Keycloak/LiteLLM)"
echo " --full Start all services including Keycloak and LiteLLM"
echo " --start Only start services, skip installation"
echo " --stop Stop all services"
echo " --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Stop services
if [ "$STOP" = true ]; then
echo -e "${YELLOW}Stopping services...${NC}"
cd "$ENTERPRISE_DIR"
if [ -f docker-compose.local.yml ]; then
docker compose -f docker-compose.local.yml down 2>/dev/null || true
fi
docker rm -f openhands-postgres openhands-redis openhands-keycloak openhands-litellm 2>/dev/null || true
echo -e "${GREEN}Services stopped${NC}"
exit 0
fi
# Determine mode
if [ "$MINIMAL" = false ] && [ "$FULL" = false ]; then
echo -e "${YELLOW}Select setup mode:${NC}"
echo " 1) Minimal (PostgreSQL + Redis only)"
echo " 2) Full (PostgreSQL + Redis + Keycloak + LiteLLM)"
read -p "Enter choice [1]: " choice
choice=${choice:-1}
if [ "$choice" = "1" ]; then
MINIMAL=true
else
FULL=true
fi
fi
echo ""
# Start services
if [ "$MINIMAL" = true ]; then
echo -e "${YELLOW}Starting minimal services (PostgreSQL + Redis)...${NC}"
# PostgreSQL
if ! container_running openhands-postgres; then
echo -e " Starting PostgreSQL..."
docker run -d \
--name openhands-postgres \
-p 5432:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=openhands \
postgres:15 >/dev/null
else
echo -e " ${GREEN}PostgreSQL already running${NC}"
fi
# Redis
if ! container_running openhands-redis; then
echo -e " Starting Redis..."
docker run -d \
--name openhands-redis \
-p 6379:6379 \
redis:7-alpine >/dev/null
else
echo -e " ${GREEN}Redis already running${NC}"
fi
wait_for_service localhost 5432 "PostgreSQL"
wait_for_service localhost 6379 "Redis"
else
echo -e "${YELLOW}Starting full services (PostgreSQL + Redis + Keycloak + LiteLLM)...${NC}"
cd "$ENTERPRISE_DIR"
# Use docker compose
if docker compose version >/dev/null 2>&1; then
docker compose -f docker-compose.local.yml up -d
else
docker-compose -f docker-compose.local.yml up -d
fi
wait_for_service localhost 5432 "PostgreSQL"
wait_for_service localhost 6379 "Redis"
wait_for_service localhost 8080 "Keycloak"
wait_for_service localhost 4000 "LiteLLM"
fi
if [ "$START_ONLY" = true ]; then
echo ""
echo -e "${GREEN}Services started!${NC}"
exit 0
fi
echo ""
# Create .env file if it doesn't exist
echo -e "${YELLOW}Configuring environment...${NC}"
cd "$ENTERPRISE_DIR"
if [ ! -f .env ]; then
cp .env.example .env
echo -e " ${GREEN}Created .env file from template${NC}"
echo -e " ${YELLOW}Please edit .env to add your LLM API keys!${NC}"
else
echo -e " ${GREEN}.env file already exists${NC}"
fi
echo ""
# Install Python dependencies
echo -e "${YELLOW}Installing Python dependencies...${NC}"
cd "$ENTERPRISE_DIR"
poetry install
echo ""
# Run database migrations
echo -e "${YELLOW}Running database migrations...${NC}"
poetry run alembic upgrade head
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Setup Complete! ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "Services running:"
echo -e " • PostgreSQL: ${BLUE}localhost:5432${NC}"
echo -e " • Redis: ${BLUE}localhost:6379${NC}"
if [ "$FULL" = true ]; then
echo -e " • Keycloak: ${BLUE}http://localhost:8080${NC} (admin/admin)"
echo -e " • LiteLLM: ${BLUE}http://localhost:4000${NC}"
fi
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo ""
echo "1. Edit .env and add your LLM API keys (OPENAI_API_KEY or ANTHROPIC_API_KEY)"
echo ""
if [ "$FULL" = true ]; then
echo "2. Configure LiteLLM with your API keys:"
echo " docker exec -it openhands-litellm /bin/sh"
echo " # Then set environment variables"
echo ""
echo "3. (Optional) Configure Keycloak realm for OAuth"
echo ""
fi
echo "3. Build the frontend (from repo root):"
echo " cd .. && make build-frontend"
echo ""
echo "4. Start the backend server:"
echo " cd enterprise"
echo " OPENHANDS_PATH=../ make start-backend"
echo ""
echo "5. Access the application at: ${BLUE}http://localhost:3000${NC}"
echo ""
echo "To stop all services: $0 --stop"