Spaces:
Sleeping
Sleeping
| # Centralized Logging Configuration | |
| export LOG_LEVEL=${LOG_LEVEL:-"info"} | |
| export LOG_FILE=${LOG_FILE:-"/app/verdant_claw.log"} | |
| # Mask sensitive env vars in logs | |
| log() { | |
| local msg=$(echo "$1" | sed -E 's/(nvapi|sk|gsk)[a-zA-Z0-9]+/\1*****/g') | |
| echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] [$LOG_LEVEL] $msg" | tee -a $LOG_FILE | |
| } | |
| # Trap SIGTERM for graceful shutdown | |
| cleanup() { | |
| log "Shutting down services gracefully..." | |
| kill $(jobs -p) 2>/dev/null | |
| sleep 2 | |
| log "Shutdown complete" | |
| exit 0 | |
| } | |
| trap cleanup SIGTERM SIGINT | |
| # Parse API_KEYS_JSON secret | |
| if [ -n "$API_KEYS_JSON" ]; then | |
| log "Parsing API keys from JSON..." | |
| export NVIDIA_API_KEY=$(echo $API_KEYS_JSON | jq -r '.nvidia // empty') | |
| export OPENAI_API_KEY=$(echo $API_KEYS_JSON | jq -r '.openai // empty') | |
| export ANTHROPIC_API_KEY=$(echo $API_KEYS_JSON | jq -r '.anthropic // empty') | |
| export GOOGLE_API_KEY=$(echo $API_KEYS_JSON | jq -r '.google // empty') | |
| export GROQ_API_KEY=$(echo $API_KEYS_JSON | jq -r '.groq // empty') | |
| log "β API keys loaded from JSON" | |
| else | |
| log "β οΈ WARNING: API_KEYS_JSON not set!" | |
| fi | |
| # Function to start service with retry | |
| start_with_retry() { | |
| local name=$1 | |
| local cmd=$2 | |
| local max_attempts=3 | |
| for i in $(seq 1 $max_attempts); do | |
| log "Starting $name (attempt $i/$max_attempts)..." | |
| eval "$cmd" & | |
| local pid=$! | |
| sleep 5 | |
| if ps -p $pid > /dev/null; then | |
| log "β $name started (PID: $pid)" | |
| return 0 | |
| fi | |
| log "β οΈ $name failed to start, retrying..." | |
| done | |
| log "β ERROR: $name failed after $max_attempts attempts" | |
| return 1 | |
| } | |
| # Check if ports are available | |
| check_port() { | |
| local port=$1 | |
| if netstat -tlnp 2>/dev/null | grep -q ":$port"; then | |
| log "β ERROR: Port $port already in use!" | |
| return 1 | |
| fi | |
| return 0 | |
| } | |
| log "Verifying infrastructure ports..." | |
| for port in 7860 7861 7687 20128 7862; do | |
| check_port $port || exit 1 | |
| done | |
| # π Start Services | |
| MEMGRAPH_BIN=$(command -v memgraph || echo "/usr/bin/memgraph") | |
| if [ ! -f "$MEMGRAPH_BIN" ]; then | |
| MEMGRAPH_BIN="/usr/lib/memgraph/memgraph" | |
| fi | |
| start_with_retry "Memgraph" "$MEMGRAPH_BIN --bolt-port 7687" || exit 1 | |
| # Start ZeroClaw on port 42617 (NOT 7860 - Marimo needs that) | |
| # Use environment variable to set port | |
| export ZEROCLAW_GATEWAY_PORT=42617 | |
| start_with_retry "ZeroClaw" "zeroclaw gateway" || exit 1 | |
| start_with_retry "OmniRoute" "node /app/scripts/omniroute.js --config /app/config/omniroute-masterfile.json --port 20128" || exit 1 | |
| # Start Graph Viewer on port 7861 | |
| GRAPH_VIEWER_PORT=7861 start_with_retry "Graph Viewer" "node /app/scripts/graph-viewer.js" || exit 1 | |
| log "All infrastructure ready! Launching Marimo UI on port 7860..." | |
| # Setup Ollama config (proxy to ZeroClaw) | |
| mkdir -p /home/node/.ollama | |
| cp /app/ollama-config.json /home/node/.ollama/config.json 2>/dev/null || true | |
| log "β Ollama configured to proxy to ZeroClaw" | |
| # Start Ollama serve (in background) | |
| log "Starting Ollama..." | |
| nohup ollama serve > /tmp/ollama.log 2>&1 & | |
| OLLAMA_PID=$! | |
| sleep 3 | |
| if ps -p $OLLAMA_PID > /dev/null; then | |
| log "β Ollama started (PID: $OLLAMA_PID)" | |
| # Pull a default model | |
| log "Pulling default model (this may take a minute)..." | |
| ollama pull llama3.2 || log "β οΈ Could not pull model, will use ZeroClaw models" | |
| else | |
| log "β οΈ Ollama failed to start" | |
| fi | |
| # Setup Marimo config (pre-configured with Ollama as AI provider) | |
| mkdir -p /home/node/.config/marimo | |
| cat > /home/node/.config/marimo/marimo.toml << 'TOML' | |
| [ai] | |
| enabled = true | |
| [ai.providers.ollama] | |
| name = "Ollama" | |
| base_url = "http://localhost:11434/v1" | |
| api_key = "ollama-local-key" | |
| [[ai.models]] | |
| provider = "ollama" | |
| model = "zeroclaw/default" | |
| TOML | |
| log "β Marimo AI config created with Ollama provider" | |
| # Initialize Pi (accepts config, creates necessary files) | |
| log "Initializing Pi agent..." | |
| echo "y" | pi --mode rpc --extensions /app/agent-pi/pi-extensions --skills /app/agent-pi/skills --prompts /app/agent-pi/commands </dev/null > /tmp/pi-init.log 2>&1 || true | |
| sleep 2 | |
| # Start pi-acp for ACP support (Marimo AI integration) through stdio-to-ws bridge | |
| # Name it "opencode" so Marimo recognizes it! | |
| log "Starting pi-acp ACP server via stdio-to-ws bridge (as 'opencode')..." | |
| nohup npx stdio-to-ws "pi-acp --terminal-login" --port 3017 --name "opencode" > /tmp/pi-acp.log 2>&1 & | |
| PI_ACP_PID=$! | |
| sleep 3 | |
| if ps -p $PI_ACP_PID > /dev/null; then | |
| log "β pi-acp started on ws://localhost:3017 as 'opencode' (PID: $PI_ACP_PID)" | |
| else | |
| log "β οΈ pi-acp failed to start, check /tmp/pi-acp.log" | |
| cat /tmp/pi-acp.log | |
| fi | |
| # Launch Marimo in EDIT MODE (not app mode) | |
| cd /app | |
| nohup marimo edit app.py --host 0.0.0.0 --port 7860 --no-token > /tmp/marimo.log 2>&1 & | |
| MARIMO_PID=$! | |
| sleep 3 | |
| if ps -p $MARIMO_PID > /dev/null; then | |
| log "β Marimo started (PID: $MARIMO_PID)" | |
| log " Marimo logs: /tmp/marimo.log" | |
| else | |
| log "β ERROR: Marimo failed to start!" | |
| cat /tmp/marimo.log | |
| exit 1 | |
| fi | |
| log "All services active!" | |
| log " - Memgraph: port 7687" | |
| log " - ZeroClaw: port 7862" | |
| log " - OmniRoute: port 20128" | |
| log " - Graph Viewer: port 7861" | |
| log " - Marimo UI: port 7860 (primary)" | |
| # Keep container alive | |
| log "Holding container open. Access the UI at https://your-space.hf.space" | |
| wait | |