Spaces:
Running
Running
File size: 20,016 Bytes
3a639b9 5e5fe8d 37a39cf 3a639b9 37a39cf 18855f0 5e5fe8d 115986a 9f8c8ee 5e5fe8d 115986a 18855f0 115986a 291e2f7 4f04cfe 805b3d2 4f04cfe 805b3d2 291e2f7 115986a a4c161c 14a487c a4c161c 14a487c a4c161c 6d0ee8a 42fc0fd 6d0ee8a 2972dae 6d0ee8a 42fc0fd 6d0ee8a fe55787 6d0ee8a 42fc0fd 6d0ee8a fe55787 291e2f7 fe55787 3f28b6e fe55787 4f04cfe 3a639b9 fe55787 4f04cfe d4c8996 4f04cfe fe55787 4f04cfe fe55787 4f04cfe 20a21f0 6fef1ae 4f04cfe fe55787 4f04cfe 20a21f0 4f04cfe 20a21f0 3a639b9 4f04cfe 3a639b9 fe55787 3a639b9 fe55787 20a21f0 4f04cfe 20a21f0 4f04cfe 20a21f0 3a639b9 4f04cfe 805b3d2 4f04cfe 20a21f0 fe55787 ef38f88 fe55787 ef38f88 f730dac 800c52a 115986a 6137b1d 115986a 6137b1d 115986a fcb03e9 115986a bb6b376 115986a 77abf38 115986a 77abf38 115986a 77abf38 115986a 77abf38 115986a ac8eef8 3956c72 4f04cfe 3956c72 ac8eef8 fe55787 8e49251 fe55787 3a639b9 fe55787 8e49251 fe55787 9097a56 55104ce 3956c72 4aad19d 6d0ee8a 4aad19d fe55787 ee68a9d fe55787 001e508 fe55787 001e508 fe55787 001e508 fe55787 043245f fe55787 2972dae 291e2f7 185afee 291e2f7 115986a 4f04cfe 2972dae 115986a f730dac 115986a ef38f88 115986a 2972dae 115986a bb6b376 115986a 2972dae 8e49251 46c1b7f fdfbafc 8e49251 46c1b7f e499dd1 24a746e e499dd1 46c1b7f e499dd1 8e49251 4f04cfe 020b203 115986a | 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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | #!/usr/bin/env bash
set -Eeuo pipefail
START_TIME=$(date +%s)
FASTAPI_PID=""
NGINX_PID=""
OMNIROUTE_PID=""
OMNIROUTE_FAIL_COUNT=0
OMNIROUTE_BACKOFF_S=2
get_elapsed() {
if [ -n "$START_TIME" ]; then
python3 -c "import time; print(f'{time.time() - $START_TIME:.2f}s')" 2>/dev/null || echo "0.0s"
else
echo "0.0s"
fi
}
log_info() {
echo "$(date -u '+%Y-%m-%d %H:%M:%S') [INFO] [$1] $2"
}
log_warn() {
echo "$(date -u '+%Y-%m-%d %H:%M:%S') [WARN] [$1] $2"
}
log_error() {
echo "$(date -u '+%Y-%m-%d %H:%M:%S') [ERROR] [$1] $2"
}
log_info "SYSTEM" "============================================"
log_info "SYSTEM" "=== OpenCode Space Container Starting ==="
log_info "SYSTEM" "============================================"
# Step 1: Configure Git & Environment Secrets Validation
git config --global --add safe.directory '*' 2>/dev/null || true
# Step 2: Ensure persistent /data directory structure exists
log_info "INIT" "Setting up /data persistent volume directories..."
mkdir -p /data/share/opencode /data/config/opencode /data/cache/opencode /data/state/opencode 2>/dev/null || true
mkdir -p /root/.cache /data/cache 2>/dev/null || true
# Ensure persistent volume directories exist (Never wipe user data on restarts)
mkdir -p /data/omniroute/backups /data/omniroute/oauth /data/omniroute/credentials /data/omniroute/call_logs /data/omniroute/runtime 2>/dev/null || true
# Clean only legacy non-OmniRoute directories if lingering
rm -rf /data/jellyfin /data/hermes /root/.hermes /data/share 2>/dev/null || true
chmod -R 777 /root/.cache /data/cache /data/omniroute 2>/dev/null || true
# Playwright Browser Metadata Fix for gemini-web / browser providers
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
export PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright
mkdir -p /root/.cache/ms-playwright /omniroute/.cache/ms-playwright 2>/dev/null || true
python3 -c "
import os, json
browsers_json = {
'browsers': [
{'name': 'chromium', 'revision': '1112', 'installByDefault': True},
{'name': 'firefox', 'revision': '1448', 'installByDefault': True},
{'name': 'webkit', 'revision': '2007', 'installByDefault': True},
{'name': 'ffmpeg', 'revision': '1009', 'installByDefault': True}
]
}
targets = [
'/root/.cache/ms-playwright',
'/omniroute/.cache/ms-playwright',
'/omniroute/node_modules/playwright-core',
'/omniroute/node_modules/playwright/node_modules/playwright-core',
'/usr/local/lib/node_modules/playwright-core',
'/usr/local/lib/node_modules/playwright/node_modules/playwright-core'
]
if os.path.exists('/omniroute'):
for root, dirs, files in os.walk('/omniroute'):
if os.path.basename(root) == 'playwright-core':
targets.append(root)
for target in set(targets):
try:
os.makedirs(target, exist_ok=True)
b_path = os.path.join(target, 'browsers.json')
if not os.path.exists(b_path) or os.path.getsize(b_path) == 0:
with open(b_path, 'w', encoding='utf-8') as f:
json.dump(browsers_json, f, indent=2)
print(f'[PLAYWRIGHT] Created browsers.json at {b_path}')
except Exception:
pass
" 2>/dev/null || true
# ββ STEP 1: Master Secret Validation & Persistent Encryption Keys βββββββββββββ
mkdir -p /data/omniroute 2>/dev/null || true
if [ -n "$STORAGE_ENCRYPTION_KEY" ]; then
echo "$STORAGE_ENCRYPTION_KEY" > /data/omniroute/.storage_encryption_key 2>/dev/null || true
elif [ -f "/data/omniroute/.storage_encryption_key" ] && [ -s "/data/omniroute/.storage_encryption_key" ]; then
export STORAGE_ENCRYPTION_KEY=$(cat /data/omniroute/.storage_encryption_key | tr -d '\r\n')
echo "[PERSISTENCE] Loaded persistent STORAGE_ENCRYPTION_KEY from /data/omniroute/.storage_encryption_key"
else
export STORAGE_ENCRYPTION_KEY=$(python3 -c "import secrets; print(secrets.token_hex(16))" 2>/dev/null || echo "omniroute_default_storage_key_32bytes")
echo "$STORAGE_ENCRYPTION_KEY" > /data/omniroute/.storage_encryption_key 2>/dev/null || true
echo "[PERSISTENCE] Generated and saved persistent STORAGE_ENCRYPTION_KEY to /data/omniroute"
fi
if [ -n "$JWT_SECRET" ]; then
echo "$JWT_SECRET" > /data/omniroute/.jwt_secret 2>/dev/null || true
elif [ -f "/data/omniroute/.jwt_secret" ] && [ -s "/data/omniroute/.jwt_secret" ]; then
export JWT_SECRET=$(cat /data/omniroute/.jwt_secret | tr -d '\r\n')
echo "[PERSISTENCE] Loaded persistent JWT_SECRET from /data/omniroute/.jwt_secret"
else
export JWT_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(16))" 2>/dev/null || echo "omniroute_default_jwt_secret_key_32bytes")
echo "$JWT_SECRET" > /data/omniroute/.jwt_secret 2>/dev/null || true
echo "[PERSISTENCE] Generated and saved persistent JWT_SECRET to /data/omniroute"
fi
if [ -n "$API_KEY_SECRET" ]; then
echo "$API_KEY_SECRET" > /data/omniroute/.api_key_secret 2>/dev/null || true
elif [ -f "/data/omniroute/.api_key_secret" ] && [ -s "/data/omniroute/.api_key_secret" ]; then
export API_KEY_SECRET=$(cat /data/omniroute/.api_key_secret | tr -d '\r\n')
echo "[PERSISTENCE] Loaded persistent API_KEY_SECRET from /data/omniroute/.api_key_secret"
else
export API_KEY_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(16))" 2>/dev/null || echo "omniroute_default_api_key_secret_32bytes")
echo "$API_KEY_SECRET" > /data/omniroute/.api_key_secret 2>/dev/null || true
echo "[PERSISTENCE] Generated and saved persistent API_KEY_SECRET to /data/omniroute"
fi
export INITIAL_PASSWORD="${INITIAL_PASSWORD:-Jishnu2005}"
echo "$INITIAL_PASSWORD" > /data/omniroute/.initial_password 2>/dev/null || true
echo "[PERSISTENCE] Configured OmniRoute administrator password to persistent storage."
export ENCRYPTION_SECRET="${STORAGE_ENCRYPTION_KEY}"
export OMNIROUTE_SECRET_KEY="${STORAGE_ENCRYPTION_KEY}"
export OMNIROUTE_STORAGE_KEY="${STORAGE_ENCRYPTION_KEY}"
export OMNIROUTE_STORAGE_SECRET="${STORAGE_ENCRYPTION_KEY}"
export OMNIROUTE_WS_BRIDGE_SECRET="${OMNIROUTE_WS_BRIDGE_SECRET:-$(echo "ws_bridge_${JWT_SECRET}" | sha256sum | cut -c1-48)}"
# Purge any legacy secret env files from storage bucket to maintain strict secret separation
rm -f /data/.env /data/secrets.env /data/secrets /data/config/.env /data/omniroute/.env /data/omniroute/secrets.env /data/omniroute/secrets /data/omniroute/.secrets /data/omniroute/server.env 2>/dev/null || true
echo "[BOOT] Secrets validated: $(get_elapsed)"
# ββ STEP 2: Canonical Direct Persistent Storage & Preflight Gate ββββββββββββββ
export DATA_DIR="/data/omniroute"
export OMNIROUTE_DATA_DIR="/data/omniroute"
export OMNIROUTE_RUNTIME_DIR="/root/.omniroute"
export OMNIROUTE_RUNTIME_DB="/root/.omniroute/storage.sqlite"
mkdir -p "$OMNIROUTE_RUNTIME_DIR" "$DATA_DIR/.incoming"
preflight_persistence_gate() {
log_info "PREFLIGHT" "Validating persistent storage invariants for OmniRoute..."
if [ ! -d "/data" ]; then
log_error "PREFLIGHT" "CRITICAL: /data directory does not exist! Persistent bucket not mounted."
return 1
fi
if [ ! -w "/data" ]; then
log_error "PREFLIGHT" "CRITICAL: /data is not writable!"
return 1
fi
mkdir -p "$DATA_DIR/backups" "$DATA_DIR/oauth" "$DATA_DIR/credentials" "$DATA_DIR/call_logs" "$DATA_DIR/runtime" 2>/dev/null || true
if [ ! -w "$DATA_DIR" ]; then
log_error "PREFLIGHT" "CRITICAL: DATA_DIR ($DATA_DIR) is not writable!"
return 1
fi
# Touch probe to verify read-write access
_PROBE="$DATA_DIR/.probe_$$"
if ! touch "$_PROBE" 2>/dev/null; then
log_error "PREFLIGHT" "CRITICAL: Write probe failed in $DATA_DIR!"
return 1
fi
rm -f "$_PROBE" 2>/dev/null || true
# Cloud Vault Restore: Synchronize clean, vacuumed authoritative database
log_info "PREFLIGHT" "Synchronizing 100% verified vacuumed SQLite database from cloud vault..."
rm -f "$DATA_DIR/storage.sqlite"* "$DATA_DIR/backups/last-known-good.sqlite"* "$OMNIROUTE_RUNTIME_DIR/storage.sqlite"* 2>/dev/null || true
if [ -f "/vault_sync.py" ]; then
python3 /vault_sync.py restore --force || true
fi
log_info "PREFLIGHT" "Persistence preflight passed successfully. Single source of truth: $DATA_DIR"
return 0
}
preflight_persistence_gate || log_error "PREFLIGHT" "Warning: persistence preflight reported issues, continuing with best effort."
# Runtime SQLite is authoritative; /data contains only verified snapshots.
if ! python3 /persistence_manager.py recover 2>&1 | tee /data/cache/persistence-recovery.log; then
log_error "PERSISTENCE" "Verified runtime database recovery failed; refusing to start OmniRoute."
exit 1
fi
# SQLite-Aware Backup Routine (Scheduled & On Shutdown)
backup_omniroute_db() {
if [ -f "$OMNIROUTE_RUNTIME_DB" ]; then
if python3 /persistence_manager.py snapshot >> /data/cache/persistence.log 2>&1; then
log_info "BACKUP" "Verified SQLite snapshot promoted."
if [ -f "/vault_sync.py" ]; then
python3 /vault_sync.py backup >/dev/null 2>&1 &
fi
else
log_warn "BACKUP" "Verified snapshot failed; previous persistent snapshot preserved."
fi
fi
}
shutdown_gracefully() {
log_info "SHUTDOWN" "Process termination signal received. Flushing persistence state..."
if [ -n "$OMNIROUTE_PID" ] && kill -0 $OMNIROUTE_PID 2>/dev/null; then
kill -INT $OMNIROUTE_PID 2>/dev/null || true
for _i in $(seq 1 10); do
if ! kill -0 $OMNIROUTE_PID 2>/dev/null; then
break
fi
sleep 1
done
fi
if [ -f "$OMNIROUTE_RUNTIME_DB" ]; then
backup_omniroute_db >/dev/null 2>&1 || true
if [ -f "/vault_sync.py" ]; then
python3 /vault_sync.py backup >/dev/null 2>&1 || true
fi
fi
log_info "SHUTDOWN" "Persistence checkpoint complete. Container exiting."
exit 0
}
trap shutdown_gracefully EXIT INT TERM
export HF_HUB_DISABLE_PROGRESS_BARS=1
export TQDM_DISABLE=1
# ββ STEP 3: Start FastAPI Gateway Immediately ββββββββββββββββββββββββββββββββ
echo "[BOOT] FastAPI starting: $(get_elapsed)"
cd /
python3 -m uvicorn proxy:app --host 127.0.0.1 --port 8000 --workers 2 --no-access-log --log-level warning &
FASTAPI_PID=$!
# Step 4: Wait for FastAPI /health/live to return HTTP 200
FASTAPI_READY=0
for i in $(seq 1 30); do
if curl --max-time 2 -sf -i http://127.0.0.1:8000/health/live | grep -q "200 OK"; then
FASTAPI_READY=1
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo "[BOOT] FastAPI live: ${ELAPSED}s"
break
fi
sleep 0.2
done
if [ "$FASTAPI_READY" -ne 1 ]; then
echo "[WARN] FastAPI did not report ready within timeout, proceeding with startup..."
fi
# Step 5: Start Nginx Edge Server on Public Port 4096
echo "[BOOT] Nginx starting..."
rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default 2>/dev/null || true
nginx -t -c /nginx.conf
nginx -g 'daemon off;' -c /nginx.conf &
NGINX_PID=$!
# Step 6: Wait for Nginx /health/live on Port 4096 to return HTTP 200
NGINX_READY=0
for i in $(seq 1 30); do
if curl --max-time 2 -sf -i http://127.0.0.1:4096/health/live | grep -q "200 OK"; then
NGINX_READY=1
break
fi
sleep 0.2
done
# Step 7: Signal Public Gateway Readiness immediately to Hugging Face
echo "[BOOT] Public gateway live"
echo "[BOOT] Background services starting asynchronously..."
# Step 8: Start Redis Server & OmniRoute AI Gateway in Background
if command -v redis-server >/dev/null 2>&1; then
echo "[INIT] Starting Redis server on port 6379..."
redis-server --daemonize yes 2>/dev/null || true
if command -v redis-cli >/dev/null 2>&1; then
echo "[INIT] Redis server active on port 6379."
fi
fi
echo "[INIT] Starting OmniRoute AI Gateway..."
export PORT=20128
export HOSTNAME="127.0.0.1"
export DATA_DIR="$OMNIROUTE_RUNTIME_DIR"
export OMNIROUTE_DATA_DIR="$OMNIROUTE_RUNTIME_DIR"
export REDIS_URL="redis://127.0.0.1:6379"
export NEXT_PUBLIC_BASE_URL="https://jishnupg-opencode-cli.hf.space"
export AUTH_COOKIE_SECURE="true"
export ALLOW_REMOTE_OAUTH="true"
export ALLOW_BROWSER_OAUTH="true"
export OAUTH_CALLBACK_URL="https://jishnupg-opencode-cli.hf.space/callback"
export NEXT_PUBLIC_OAUTH_CALLBACK_URL="https://jishnupg-opencode-cli.hf.space/callback"
export REDIRECT_URI="https://jishnupg-opencode-cli.hf.space/callback"
export ANTIGRAVITY_OAUTH_REDIRECT_URI="https://jishnupg-opencode-cli.hf.space/callback"
export ANTIGRAVITY_REDIRECT_URI="https://jishnupg-opencode-cli.hf.space/callback"
export ADMIN_PASSWORD="$INITIAL_PASSWORD"
export OMNIROUTE_INITIAL_PASSWORD="$INITIAL_PASSWORD"
export OMNIROUTE_PASSWORD="$INITIAL_PASSWORD"
export PASSWORD="$INITIAL_PASSWORD"
export RESET_PASSWORD="$INITIAL_PASSWORD"
export OMNIROUTE_RESET_PASSWORD="$INITIAL_PASSWORD"
export CLI_COMPAT_ANTIGRAVITY=1
export CLI_COMPAT_GITHUB=1
export CLI_COMPAT_KIMI_CODING=1
export CLI_COMPAT_CLAUDE=1
export CLI_COMPAT_CODEX=1
export CLI_COMPAT_CURSOR=1
export CLI_COMPAT_QWEN=1
export OMNIROUTE_AUTO_FREE_FALLBACK_TO_FULL_POOL=true
export OMNIROUTE_ALLOW_UNAUTHENTICATED=true
export REQUIRE_API_KEY=false
export OMNIROUTE_REQUIRE_API_KEY=false
export OMNIROUTE_AUTH_REQUIRED=false
export RATE_LIMIT_ENABLED=false
export ENABLE_RATE_LIMIT=false
export ENABLE_RATE_LIMITING=false
export OMNIROUTE_RATE_LIMIT_ENABLED=false
export AUTH_RATE_LIMIT=false
export LOGIN_RATE_LIMIT=false
export DISABLE_RATE_LIMIT=true
export DISABLE_RATE_LIMITING=true
export AUTH_RATE_LIMIT_MAX=100000
export AUTH_RATE_LIMIT_WINDOW_MS=1000
export RATE_LIMIT_MAX=100000
export RATE_LIMIT_WINDOW_MS=1000
export TRUST_PROXY=true
# ββ Master Provider API Keys & Secrets Auto-Discovery βββββββββββββββββββββ
export OPENAI_API_KEY="${OPENAI_API_KEY:-}"
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-${CLAUDE_API_KEY:-}}"
export GEMINI_API_KEY="${GEMINI_API_KEY:-${GOOGLE_API_KEY:-${GOOGLE_GENAI_API_KEY:-}}}"
export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-}"
export GROQ_API_KEY="${GROQ_API_KEY:-}"
export NVIDIA_API_KEY="${NVIDIA_API_KEY:-${NVIDIA_NIM_API_KEY:-}}"
export HUGGINGFACE_API_KEY="${HUGGINGFACE_API_KEY:-${HF_TOKEN:-${HUGGING_FACE_HUB_TOKEN:-}}}"
export HF_TOKEN="${HF_TOKEN:-$HUGGINGFACE_API_KEY}"
export OPENROUTER_API_KEY="${OPENROUTER_API_KEY:-}"
export MISTRAL_API_KEY="${MISTRAL_API_KEY:-}"
export COHERE_API_KEY="${COHERE_API_KEY:-}"
export MOONSHOT_API_KEY="${MOONSHOT_API_KEY:-${KIMI_API_KEY:-}}"
export TOGETHER_API_KEY="${TOGETHER_API_KEY:-}"
export FIREWORKS_API_KEY="${FIREWORKS_API_KEY:-}"
export CEREBRAS_API_KEY="${CEREBRAS_API_KEY:-}"
export PERPLEXITY_API_KEY="${PERPLEXITY_API_KEY:-}"
export XAI_API_KEY="${XAI_API_KEY:-}"
export GITHUB_TOKEN="${GITHUB_TOKEN:-${GH_TOKEN:-}}"
export ANTIGRAVITY_API_KEY="${ANTIGRAVITY_API_KEY:-${ANTIGRAVITY_TOKEN:-}}"
# ββ Restore Persistent Credential Vault into Environment ββββββββββββββββββ
if [ -f "/data/omniroute/credentials_vault.json" ]; then
echo "[VAULT] Restoring persistent provider secrets from /data/omniroute/credentials_vault.json..."
python3 -c "
import json
try:
with open('/data/omniroute/credentials_vault.json', 'r', encoding='utf-8') as f:
v = json.load(f)
for k, val in v.items():
if k and val:
print(f'export {k}=\"{val}\"')
except Exception:
pass
" > /tmp/vault_env.sh 2>/dev/null || true
if [ -f "/tmp/vault_env.sh" ]; then
. /tmp/vault_env.sh
rm -f /tmp/vault_env.sh
fi
fi
if [ -d "/omniroute" ]; then
cd /omniroute
if [ -f "/fix_omniroute.py" ]; then
python3 /fix_omniroute.py /omniroute 2>&1 || true
elif [ -f "fix_omniroute.py" ]; then
python3 fix_omniroute.py ./ 2>&1 || true
fi
mkdir -p /data/omniroute 2>/dev/null || true
if [ -f "server.js" ]; then
echo "[INIT] OmniRoute entry: node server.js (PORT=20128)"
node server.js > /data/omniroute/omniroute.log 2>&1 &
else
echo "[INIT] OmniRoute entry: npm run start (PORT=20128)"
npm run start > /data/omniroute/omniroute.log 2>&1 &
fi
OMNIROUTE_PID=$!
(
for i in $(seq 1 90); do
if curl -fsS "http://127.0.0.1:20128/api/monitoring/health" >/dev/null 2>&1; then
echo "[HEALTH] OmniRoute ready after ${i}s"
echo "[PROCESS] OmniRoute: PID ${OMNIROUTE_PID}"
break
fi
sleep 1
done
) &
fi
# Step 9: Start Database & Disk Health Doctor Daemon
if [ -f "/health_doctor.py" ]; then
echo "[HEALTH] Starting Database & Disk Health Doctor daemon..."
python3 /health_doctor.py > /data/cache/health_doctor.log 2>&1 &
fi
# Step 10: Keep PID 1 Alive and Monitor Child Processes
echo "[BOOT] All services dispatched. Process Supervisor active."
BACKUP_TIMER=0
while true; do
if [ -n "$FASTAPI_PID" ] && ! kill -0 $FASTAPI_PID 2>/dev/null; then
echo "[CRITICAL] FastAPI Gateway process died! Restarting..."
python3 -m uvicorn proxy:app --host 127.0.0.1 --port 8000 --workers 2 --no-access-log --log-level warning > /data/cache/fastapi_gateway.log 2>&1 &
FASTAPI_PID=$!
fi
if [ -n "$NGINX_PID" ] && ! kill -0 $NGINX_PID 2>/dev/null; then
echo "[CRITICAL] Nginx process died! Restarting..."
nginx -g 'daemon off;' -c /nginx.conf &
NGINX_PID=$!
fi
if [ -n "$OMNIROUTE_PID" ] && ! kill -0 $OMNIROUTE_PID 2>/dev/null; then
echo "[CRITICAL] OmniRoute process died! (backoff: ${OMNIROUTE_BACKOFF_S:-2}s) β last crash log:"
tail -n 20 /data/omniroute/omniroute.log 2>/dev/null || true
sleep "${OMNIROUTE_BACKOFF_S:-2}"
(cd /omniroute && node server.js) > /data/omniroute/omniroute.log 2>&1 &
OMNIROUTE_PID=$!
# Exponential backoff: 2 β 4 β 8 β 16 β 32 β 60 (cap)
OMNIROUTE_BACKOFF_S=$(( ${OMNIROUTE_BACKOFF_S:-2} * 2 ))
if [ "$OMNIROUTE_BACKOFF_S" -gt 60 ]; then OMNIROUTE_BACKOFF_S=60; fi
OMNIROUTE_FAIL_COUNT=0
elif [ -n "$OMNIROUTE_PID" ]; then
_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "http://127.0.0.1:20128/api/monitoring/health" 2>/dev/null || echo "000")
if [ "$_STATUS" = "500" ] || [ "$_STATUS" = "000" ]; then
OMNIROUTE_FAIL_COUNT=$((OMNIROUTE_FAIL_COUNT + 1))
if [ "$OMNIROUTE_FAIL_COUNT" -ge 12 ]; then
echo "[SUPERVISOR] OmniRoute unhealthy (HTTP $_STATUS) for 120s. Last crash/server log:"
tail -n 25 /data/omniroute/omniroute.log 2>/dev/null || true
echo "[SUPERVISOR] Performing clean self-healing restart..."
kill -TERM $OMNIROUTE_PID 2>/dev/null || true
sleep 2
kill -9 $OMNIROUTE_PID 2>/dev/null || true
(cd /omniroute && node server.js) > /data/omniroute/omniroute.log 2>&1 &
OMNIROUTE_PID=$!
OMNIROUTE_FAIL_COUNT=0
fi
else
OMNIROUTE_FAIL_COUNT=0
OMNIROUTE_BACKOFF_S=2
fi
fi
# Periodic clean SQLite backup routine every 3600 seconds (1 hour)
BACKUP_TIMER=$((BACKUP_TIMER + 10))
if [ "$BACKUP_TIMER" -ge 3600 ]; then
backup_omniroute_db >/dev/null 2>&1 || true
BACKUP_TIMER=0
fi
sleep 10
done
|