File size: 4,356 Bytes
09eb9d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/bin/bash

# Centralized Logging Configuration (Bug #18)
export LOG_LEVEL=${LOG_LEVEL:-"info"}
export LOG_FILE=${LOG_FILE:-"/app/verdant_claw.log"}

# Mask sensitive env vars in logs (Bug #13)
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 (Bug #12)
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 (Bug #9)
# Supports multi-key rotation: "nvidia_2" β†’ NVIDIA_API_KEY_2
if [ -n "$API_KEYS_JSON" ]; then
  log "Parsing API keys from JSON..."
  
  # Primary keys
  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')

  # Rotation keys (nvidia_2, openai_2, etc.)
  for provider in nvidia openai anthropic google groq; do
    for i in $(seq 2 5); do
      val=$(echo $API_KEYS_JSON | jq -r ".${provider}_${i} // empty")
      if [ -n "$val" ] && [ "$val" != "null" ]; then
        envname=$(echo "${provider}_API_KEY_${i}" | tr '[:lower:]' '[:upper:]')
        export "$envname=$val"
        log "βœ“ Loaded rotation key: $envname"
      fi
    done
  done

  log "βœ“ API keys loaded from JSON"
else
  log "⚠️ WARNING: API_KEYS_JSON not set!"
fi

# Function to start service with retry (Bug #15)
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 (Bug #4)
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; do
  check_port $port || exit 1
done

# πŸš€ Start Services
# Locate memgraph binary (common paths in memgraph-mage)
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_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 alternate port (7861)
log "Starting 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..."

# Launch Marimo as the primary UI (port 7860 for HF Spaces healthcheck)
cd /app
nohup marimo run /app/app.py --host 0.0.0.0 --port 7860 --headless > /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

# Start Pi Agent in background with EOF persistence fix
log "Starting Pi Agent in background..."
cd /app/mom-infra
start_with_retry "Pi Agent" "bash -c 'tail -f /dev/null | npm run pi -- --mode rpc --extensions /app/agent-pi/pi-extensions --skills /app/agent-pi/skills --prompts /app/agent-pi/commands'" || log "Pi Agent finished or failed."

log "All services active!"
log "  - Memgraph: port 7687"
log "  - ZeroClaw: gateway"
log "  - OmniRoute: port 20128"
log "  - Graph Viewer: port 7861"
log "  - Marimo UI: port 7860 (primary)"
log "  - Pi Agent: Background RPC"

# Keep container alive by waiting for all background processes
log "Holding container open. Access the UI at https://your-space.hf.space"
wait