diff --git a/README.md b/README.md index 9f1272d..6de21d9 100644 --- a/README.md +++ b/README.md @@ -90,13 +90,27 @@ When a customer requests a human: - Freshdesk web portal - WhatsApp (collected details → adapter) -## Quick Start +## Getting Started + +**Prerequisites:** Docker Engine and Docker Compose must be installed and running. + +```bash +# 1. Verify Docker is active (required) +docker info >/dev/null 2>&1 || echo "Docker daemon is not running" +docker compose version +``` ```bash -# 1. Clone +# 2. Clone the repository git clone https://github.com/OneByJorah/CommandDesk.git cd CommandDesk +# 3. Start the platform +docker compose up -d --build + +# 4. Verify services +docker compose ps +``` # 2. Run setup ./scripts/setup.sh diff --git a/deploy_log.txt b/deploy_log.txt new file mode 100644 index 0000000..683cc86 --- /dev/null +++ b/deploy_log.txt @@ -0,0 +1,6 @@ +[CommandDesk deploy attempt] +1. docker compose config -> FAILED (extra top-level keys in docker-compose.yml) +2. Applied fix: moved tools-ui, whatsapp-webhook, health-monitor under services. +3. docker compose config -> OK +4. docker compose build -> FAILED: "Cannot connect to the Docker daemon at unix:///var/run/docker.sock" +Root cause: Docker daemon is not running on this host (system policy: Docker stopped/masked). diff --git a/docker-compose.yml b/docker-compose.yml index ebf3107..5890bb5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,69 +1,65 @@ -version: "3.9" - +version: '3.9' services: - # ═══════════════════════════════════════════════════ - # LLM Inference (llama.cpp with Qwen2.5-7B) - # ═══════════════════════════════════════════════════ llama: image: ghcr.io/ggerganov/llama.cpp:server container_name: helpdesk-llama ports: - - "127.0.0.1:8081:8081" + - 127.0.0.1:8081:8081 volumes: - - ./models:/models:ro + - ./models:/models:ro environment: - - LLAMA_ARG_MODEL=/models/qwen2.5-7b-instruct-q4_k_m.gguf - - LLAMA_ARG_PORT=8081 - - LLAMA_ARG_CTX_SIZE=65536 - - LLAMA_ARG_N_BATCH=512 - - LLAMA_ARG_THREADS=6 - - LLAMA_ARG_HOST=0.0.0.0 - - LLAMA_ARG_N_GL=0 + - LLAMA_ARG_MODEL=/models/qwen2.5-7b-instruct-q4_k_m.gguf + - LLAMA_ARG_PORT=8081 + - LLAMA_ARG_CTX_SIZE=65536 + - LLAMA_ARG_N_BATCH=512 + - LLAMA_ARG_THREADS=6 + - LLAMA_ARG_HOST=0.0.0.0 + - LLAMA_ARG_N_GL=0 deploy: resources: limits: memory: 7G restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:8081/health"] + test: + - CMD + - curl + - -f + - http://127.0.0.1:8081/health interval: 30s timeout: 10s retries: 5 start_period: 60s networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # Helpdesk Agent (Hermes - restricted, no ticket creation) - # ═══════════════════════════════════════════════════ + - helpdesk-net helpdesk-agent: build: context: . dockerfile: Dockerfile container_name: helpdesk-agent ports: - - "127.0.0.1:8080:8080" + - 127.0.0.1:8080:8080 volumes: - - ./config:/app/config:ro - - ./ticket_platforms:/app/ticket_platforms:ro - - ./scripts:/app/scripts:ro - - ./knowledge-base:/app/knowledge-base:ro - - hermes-data:/app/data + - ./config:/app/config:ro + - ./ticket_platforms:/app/ticket_platforms:ro + - ./scripts:/app/scripts:ro + - ./knowledge-base:/app/knowledge-base:ro + - hermes-data:/app/data environment: - - HERMES_CONFIG=/app/config/hermes-config.yaml - - LLM_API_BASE=http://llama:8081/v1 - - LLM_MODEL=qwen2.5-7b-instruct - - CHROMA_URL=http://chroma:8000 - - SEARX_URL=http://searxng:8080 - - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk - - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/0 - - RATE_LIMIT_PER_SESSION=${RATE_LIMIT_PER_SESSION:-50} - - RATE_LIMIT_WINDOW=${RATE_LIMIT_WINDOW:-3600} - - MAX_MESSAGE_LENGTH=${MAX_MESSAGE_LENGTH:-4000} - - MAX_SESSION_DURATION=${MAX_SESSION_DURATION:-7200} - - AGENT_MODE=helpdesk - - ALLOW_CREATE_TICKET=false - - TZ=UTC + - HERMES_CONFIG=/app/config/hermes-config.yaml + - LLM_API_BASE=http://llama:8081/v1 + - LLM_MODEL=qwen2.5-7b-instruct + - CHROMA_URL=http://chroma:8000 + - SEARX_URL=http://searxng:8080 + - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk + - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/0 + - RATE_LIMIT_PER_SESSION=${RATE_LIMIT_PER_SESSION:-50} + - RATE_LIMIT_WINDOW=${RATE_LIMIT_WINDOW:-3600} + - MAX_MESSAGE_LENGTH=${MAX_MESSAGE_LENGTH:-4000} + - MAX_SESSION_DURATION=${MAX_SESSION_DURATION:-7200} + - AGENT_MODE=helpdesk + - ALLOW_CREATE_TICKET=false + - TZ=UTC depends_on: llama: condition: service_healthy @@ -79,35 +75,31 @@ services: memory: 2G restart: unless-stopped networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # Admin Agent (Hermes - full access, plug-in to main Hermes) - # ═══════════════════════════════════════════════════ + - helpdesk-net admin-agent: build: context: . dockerfile: Dockerfile container_name: helpdesk-admin-agent ports: - - "127.0.0.1:8082:8082" + - 127.0.0.1:8082:8082 volumes: - - ./config:/app/config:ro - - ./ticket_platforms:/app/ticket_platforms:ro - - ./scripts:/app/scripts:ro - - ./knowledge-base:/app/knowledge-base:ro - - hermes-admin-data:/app/data + - ./config:/app/config:ro + - ./ticket_platforms:/app/ticket_platforms:ro + - ./scripts:/app/scripts:ro + - ./knowledge-base:/app/knowledge-base:ro + - hermes-admin-data:/app/data environment: - - HERMES_CONFIG=/app/config/admin-agent-config.yaml - - LLM_API_BASE=http://llama:8081/v1 - - LLM_MODEL=qwen2.5-7b-instruct - - CHROMA_URL=http://chroma:8000 - - SEARX_URL=http://searxng:8080 - - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk - - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/1 - - AGENT_MODE=admin - - ALLOW_CREATE_TICKET=true - - TZ=UTC + - HERMES_CONFIG=/app/config/admin-agent-config.yaml + - LLM_API_BASE=http://llama:8081/v1 + - LLM_MODEL=qwen2.5-7b-instruct + - CHROMA_URL=http://chroma:8000 + - SEARX_URL=http://searxng:8080 + - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk + - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/1 + - AGENT_MODE=admin + - ALLOW_CREATE_TICKET=true + - TZ=UTC depends_on: llama: condition: service_healthy @@ -123,206 +115,194 @@ services: memory: 2G restart: unless-stopped networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # ChromaDB (Knowledge Base Vector Store) - # ═══════════════════════════════════════════════════ + - helpdesk-net chroma: image: chromadb/chroma:0.5.23 container_name: helpdesk-chroma ports: - - "127.0.0.1:8000:8000" + - 127.0.0.1:8000:8000 volumes: - - chroma-data:/chroma/chroma + - chroma-data:/chroma/chroma environment: - - CHROMA_SERVER_HOST=0.0.0.0 - - CHROMA_SERVER_PORT=8000 - - PERSIST_DIRECTORY=/chroma/chroma - - ALLOW_RESET=true - - CHROMA_AUTH_TOKEN=${CHROMA_AUTH_TOKEN:-chromadb_token_change_me} + - CHROMA_SERVER_HOST=0.0.0.0 + - CHROMA_SERVER_PORT=8000 + - PERSIST_DIRECTORY=/chroma/chroma + - ALLOW_RESET=true + - CHROMA_AUTH_TOKEN=${CHROMA_AUTH_TOKEN:-chromadb_token_change_me} deploy: resources: limits: memory: 2G restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-sf", "http://127.0.0.1:8000/api/v1/heartbeat"] + test: + - CMD + - curl + - -sf + - http://127.0.0.1:8000/api/v1/heartbeat interval: 30s timeout: 10s retries: 3 networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # SearXNG (Self-hosted Web Search) - # ═══════════════════════════════════════════════════ + - helpdesk-net searxng: image: searxng/searxng:latest container_name: helpdesk-searxng ports: - - "127.0.0.1:8888:8080" + - 127.0.0.1:8888:8080 volumes: - - searxng-data:/etc/searxng - - ./config/searxng-settings.yml:/etc/searxng/settings.yml:ro + - searxng-data:/etc/searxng + - ./config/searxng-settings.yml:/etc/searxng/settings.yml:ro environment: - - SEARXNG_BASE_URL=http://localhost:8888/ - - SEARXNG_BIND_ADDRESS=0.0.0.0 + - SEARXNG_BASE_URL=http://localhost:8888/ + - SEARXNG_BIND_ADDRESS=0.0.0.0 deploy: resources: limits: memory: 1G restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-sf", "http://127.0.0.1:8888/search?q=test&format=json"] + test: + - CMD + - curl + - -sf + - http://127.0.0.1:8888/search?q=test&format=json interval: 60s timeout: 10s retries: 3 networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # n8n (Workflow Automation) - # ═══════════════════════════════════════════════════ + - helpdesk-net n8n: image: n8nio/n8n:latest container_name: helpdesk-n8n ports: - - "127.0.0.1:5678:5678" + - 127.0.0.1:5678:5678 volumes: - - n8n-data:/home/node/.n8n - - ./workflows:/home/node/.n8n/workflows:ro + - n8n-data:/home/node/.n8n + - ./workflows:/home/node/.n8n/workflows:ro environment: - - N8N_HOST=localhost - - N8N_PORT=5678 - - N8N_PROTOCOL=http - - WEBHOOK_URL=http://localhost:5678/ - - GENERIC_TIMEZONE=UTC - - N8N_CUSTOM_ENDPOINTS=webhook - - N8N_USER_MANAGEMENT_JWT_SECRET=${JWT_SECRET:-jwt_secret_change_me_please} - - N8N_DEFAULT_BINARY_DATA_MODE=filesystem + - N8N_HOST=localhost + - N8N_PORT=5678 + - N8N_PROTOCOL=http + - WEBHOOK_URL=http://localhost:5678/ + - GENERIC_TIMEZONE=UTC + - N8N_CUSTOM_ENDPOINTS=webhook + - N8N_USER_MANAGEMENT_JWT_SECRET=${JWT_SECRET:-jwt_secret_change_me_please} + - N8N_DEFAULT_BINARY_DATA_MODE=filesystem deploy: resources: limits: memory: 1G restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-sf", "http://127.0.0.1:5678/healthz"] + test: + - CMD + - curl + - -sf + - http://127.0.0.1:5678/healthz interval: 30s timeout: 10s retries: 3 networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # PostgreSQL (Ticket Database + Agent State) - # ═══════════════════════════════════════════════════ + - helpdesk-net postgres: image: postgres:16-alpine container_name: helpdesk-postgres ports: - - "127.0.0.1:5432:5432" + - 127.0.0.1:5432:5432 volumes: - - postgres-data:/var/lib/postgresql/data - - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro + - postgres-data:/var/lib/postgresql/data + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro environment: - - POSTGRES_DB=helpdesk - - POSTGRES_USER=helpdesk - - POSTGRES_PASSWORD=${DB_PASSWORD:-helpdesk_pass} - - POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C + - POSTGRES_DB=helpdesk + - POSTGRES_USER=helpdesk + - POSTGRES_PASSWORD=${DB_PASSWORD:-helpdesk_pass} + - POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C deploy: resources: limits: memory: 1G restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "pg_isready -U helpdesk"] + test: + - CMD-SHELL + - pg_isready -U helpdesk interval: 10s timeout: 5s retries: 5 networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # Redis (Caching + Session Store + Rate Limiting) - # ═══════════════════════════════════════════════════ + - helpdesk-net redis: image: redis:7-alpine container_name: helpdesk-redis ports: - - "127.0.0.1:6379:6379" + - 127.0.0.1:6379:6379 volumes: - - redis-data:/data - command: > - redis-server - --requirepass ${REDIS_PASSWORD:-redis_pass} - --maxmemory 256mb - --maxmemory-policy allkeys-lru - --appendonly yes - --appendfsync everysec + - redis-data:/data + command: 'redis-server --requirepass ${REDIS_PASSWORD:-redis_pass} --maxmemory + 256mb --maxmemory-policy allkeys-lru --appendonly yes --appendfsync everysec + + ' deploy: resources: limits: memory: 300M restart: unless-stopped healthcheck: - test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-redis_pass}", "ping"] + test: + - CMD + - redis-cli + - -a + - ${REDIS_PASSWORD:-redis_pass} + - ping interval: 10s timeout: 5s retries: 3 networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # Nginx (Reverse Proxy + Security Headers + Rate Limiting) - # ═══════════════════════════════════════════════════ + - helpdesk-net nginx: image: nginx:alpine container_name: helpdesk-nginx ports: - - "80:80" - - "443:443" + - 80:80 + - 443:443 volumes: - - ./config/nginx.conf:/etc/nginx/nginx.conf:ro - - ./admin:/app/admin:ro - - ./certs:/etc/nginx/certs:ro - - nginx-cache:/var/cache/nginx + - ./config/nginx.conf:/etc/nginx/nginx.conf:ro + - ./admin:/app/admin:ro + - ./certs:/etc/nginx/certs:ro + - nginx-cache:/var/cache/nginx depends_on: - - helpdesk-agent - - admin-agent - - n8n - - searxng + - helpdesk-agent + - admin-agent + - n8n + - searxng deploy: resources: limits: memory: 200M restart: unless-stopped networks: - - helpdesk-net - - # ═══════════════════════════════════════════════════ - # Email Fetcher (IMAP → Ticket via helpdesk-agent) - # ═══════════════════════════════════════════════════ + - helpdesk-net email-fetcher: build: context: . dockerfile: Dockerfile.email container_name: helpdesk-email-fetcher volumes: - - ./config:/app/config:ro - - ./ticket_platforms:/app/ticket_platforms:ro - - email-queue:/app/queue + - ./config:/app/config:ro + - ./ticket_platforms:/app/ticket_platforms:ro + - email-queue:/app/queue environment: - - IMAP_HOST=${IMAP_HOST:-} - - IMAP_PORT=${IMAP_PORT:-993} - - IMAP_USER=${IMAP_USER:-} - - IMAP_PASSWORD=${IMAP_PASSWORD:-} - - IMAP_FOLDER=INBOX - - POLL_INTERVAL=${POLL_INTERVAL:-60} - - TICKET_PLATFORM=${TICKET_PLATFORM:-osticket} - - HELPDESK_AGENT_URL=http://helpdesk-agent:8080 - - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk - - TZ=UTC + - IMAP_HOST=${IMAP_HOST:-} + - IMAP_PORT=${IMAP_PORT:-993} + - IMAP_USER=${IMAP_USER:-} + - IMAP_PASSWORD=${IMAP_PASSWORD:-} + - IMAP_FOLDER=INBOX + - POLL_INTERVAL=${POLL_INTERVAL:-60} + - TICKET_PLATFORM=${TICKET_PLATFORM:-osticket} + - HELPDESK_AGENT_URL=http://helpdesk-agent:8080 + - POSTGRES_URL=postgresql://helpdesk:${DB_PASSWORD:-helpdesk_pass}@postgres:5432/helpdesk + - TZ=UTC depends_on: helpdesk-agent: condition: service_started @@ -334,23 +314,36 @@ services: memory: 500M restart: unless-stopped networks: - - helpdesk-net - -# ═══════════════════════════════════════════════════ -# WhatsApp Webhook Receiver -# ═══════════════════════════════════════════════════ -whatsapp-webhook: - build: - context: . - dockerfile: Dockerfile.whatsapp - container_name: helpdesk-whatsapp - ports: - - "127.0.0.1:9090:9090" - - "0.0.0.0:8383:8383" - volumes: + - helpdesk-net + tools-ui: + build: + context: tools-ui + dockerfile: Dockerfile + container_name: helpdesk-tools-ui + ports: + - 127.0.0.1:8484:8484 + depends_on: + - helpdesk-agent + - admin-agent + deploy: + resources: + limits: + memory: 200M + restart: unless-stopped + networks: + - helpdesk-net + whatsapp-webhook: + build: + context: . + dockerfile: Dockerfile.whatsapp + container_name: helpdesk-whatsapp + ports: + - 127.0.0.1:9090:9090 + - 0.0.0.0:8383:8383 + volumes: - ./config:/app/config:ro - ./scripts:/app/scripts:ro - environment: + environment: - WHATSAPP_TOKEN=${WHATSAPP_TOKEN} - WHATSAPP_PHONE_NUMBER_ID=${WHATSAPP_PHONE_NUMBER_ID} - WHATSAPP_WEBHOOK_SECRET=${WHATSAPP_WEBHOOK_SECRET:-change_me} @@ -358,77 +351,44 @@ whatsapp-webhook: - HELPDESK_AGENT_URL=http://helpdesk-agent:8080 - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/0 - WHATSAPP_RATE_LIMIT_PER_MINUTE=${WHATSAPP_RATE_LIMIT_PER_MINUTE:-10} - depends_on: - helpdesk-agent: - condition: service_started - redis: - condition: service_healthy - deploy: - resources: - limits: - memory: 500M - restart: unless-stopped - networks: + depends_on: + helpdesk-agent: + condition: service_started + redis: + condition: service_healthy + deploy: + resources: + limits: + memory: 500M + restart: unless-stopped + networks: - helpdesk-net - -# ═══════════════════════════════════════════════════ -# Health Monitor -# ═══════════════════════════════════════════════════ -health-monitor: - build: - context: . - dockerfile: Dockerfile - container_name: helpdesk-health - volumes: + health-monitor: + build: + context: . + dockerfile: Dockerfile + container_name: helpdesk-health + volumes: - ./scripts:/app/scripts:ro - ./config:/app/config:ro - environment: + environment: - REDIS_URL=redis://:${REDIS_PASSWORD:-redis_pass}@redis:6379/0 - depends_on: - redis: - condition: service_healthy - deploy: - resources: - limits: - memory: 200M - restart: unless-stopped - networks: - - helpdesk-net - -# ═══════════════════════════════════════════════════ -# Tools UI (Widget + Admin Panel) -# ═══════════════════════════════════════════════════ -tools-ui: - build: - context: tools-ui - dockerfile: Dockerfile - container_name: helpdesk-tools-ui - ports: - - "127.0.0.1:8484:8484" - depends_on: - - helpdesk-agent - - admin-agent - deploy: - resources: - limits: - memory: 200M - restart: unless-stopped - networks: + depends_on: + redis: + condition: service_healthy + deploy: + resources: + limits: + memory: 200M + restart: unless-stopped + networks: - helpdesk-net - -# ═══════════════════════════════════════════════════ -# Networks -# ═══════════════════════════════════════════════════ networks: helpdesk-net: driver: bridge ipam: config: - - subnet: 172.20.0.0/16 - -# ═══════════════════════════════════════════════════ -# Volumes -# ═══════════════════════════════════════════════════ + - subnet: 172.20.0.0/16 volumes: hermes-data: driver: local diff --git a/review_findings.json b/review_findings.json new file mode 100644 index 0000000..b390193 --- /dev/null +++ b/review_findings.json @@ -0,0 +1,18 @@ +[ + { + "id": "CMD-001", + "file": "docker-compose.yml", + "line_range": "1-10", + "issue": "Compose file contains extra top-level keys (tools-ui, whatsapp-webhook, health-monitor) that are not allowed by the schema, causing `docker compose config` to fail.", + "severity": "high", + "suggested_fix": "Move the three extra service blocks under the root services key so docker compose config validates." + }, + { + "id": "CMD-002", + "file": "README.md", + "line_range": "1-120", + "issue": "README does not mention the local requirement that Docker must be running before `docker compose build && up` will work. This is a missing environment precondition.", + "severity": "medium", + "suggested_fix": "Add a Docker check / explicit precondition note in the README Getting Started section." + } +] \ No newline at end of file diff --git a/stack_manifest.json b/stack_manifest.json new file mode 100644 index 0000000..6016a89 --- /dev/null +++ b/stack_manifest.json @@ -0,0 +1,16 @@ +{ + "runtime": "Docker Compose", + "build_method": "docker compose build && docker compose up -d", + "entry_point": "multi-service orchestrator", + "dependencies": ["docker", "docker compose", "llama.cpp", "chromadb", "postgres", "redis", "searxng", "n8n"], + "ports": [8080, 8081, 8082, 8000, 5432, 5678, 6379, 8484, 8888, 9090, 8383], + "deploy_steps": [ + "docker compose config", + "docker compose build", + "docker compose up -d" + ], + "has_docker": true, + "has_compose": true, + "has_npm": false, + "notes": "Compose file fixed: moved extra top-level services into services block. Host Docker daemon is not running; deploy needs active Docker engine." +} \ No newline at end of file