#!/data/data/com.termux/files/usr/bin/bash # Start all Android Agent Runtime services # # MUST be run from within the Termux terminal app on the phone (not via adb run-as). # Root is required for the notification monitor. # # Usage (in Termux terminal): # bash ~/.hermes/event_bus/start_all.sh # Termux home is always here regardless of how the script is invoked TERMUX_HOME="/data/data/com.termux/files/home" export HOME="$TERMUX_HOME" BASE="$HOME/.hermes/event_bus" VENV="$HOME/.hermes/hermes-agent/venv/bin" LOGS="$HOME/.hermes/logs" mkdir -p "$LOGS" # ── root check ───────────────────────────────────────────────────────────────── if ! su -c 'echo ok' > /dev/null 2>&1; then echo "[!] ERROR: cannot get root via su." echo " Run this script from inside the Termux terminal app on the phone." echo " (adb run-as does not have su access)" exit 1 fi # ── helpers ──────────────────────────────────────────────────────────────────── is_running() { local pid_file="$1" local pid [ -f "$pid_file" ] || return 1 pid=$(cat "$pid_file" 2>/dev/null) [ -n "$pid" ] && [ -d "/proc/$pid" ] } start_service() { local name="$1" local pid_file="$2" local log_file="$3" shift 3 if is_running "$pid_file"; then echo "[✓] $name already running (pid $(cat "$pid_file"))" return 0 fi # setsid creates a new session so the process survives shell exit setsid nohup "$@" >> "$log_file" 2>&1 & local pid=$! echo $pid > "$pid_file" sleep 1 if is_running "$pid_file"; then echo "[+] $name started (pid $pid)" else echo "[!] $name failed to start — check $log_file" return 1 fi } # ── event server ─────────────────────────────────────────────────────────────── start_service "EventServer" \ "$BASE/event_server.pid" \ "$LOGS/event_server.log" \ env EVENT_BUS_DB="$BASE/events.db" \ "$VENV/uvicorn" event_server:app \ --app-dir "$BASE" \ --host 127.0.0.1 --port 8765 \ --log-level warning # Wait for EventServer to be ready for i in $(seq 1 10); do "$VENV/python" -c " import urllib.request try: urllib.request.urlopen('http://127.0.0.1:8765/health', timeout=1) exit(0) except: exit(1) " 2>/dev/null && break sleep 1 done # ── APK AgentRuntimeService ──────────────────────────────────────────────────── # The APK provides real-time NotificationListenerService + AccessibilityService # events via EventServerRelay. Start it via root am if not already running. APK_PKG="ai.lemon.phoneuse.debug" APK_SVC="${APK_PKG}/ai.lemon.phoneuse.service.AgentRuntimeService" apk_svc_running() { su -c "dumpsys activity services $APK_PKG 2>/dev/null" | grep -q "AgentRuntimeService" } if apk_svc_running; then echo "[✓] APK AgentRuntimeService already running" else su -c "am start-foreground-service ${APK_SVC}" > /dev/null 2>&1 sleep 2 if apk_svc_running; then echo "[+] APK AgentRuntimeService started" else echo "[~] APK AgentRuntimeService not started (install APK first, or start manually)" fi fi # ── status ───────────────────────────────────────────────────────────────────── echo "" echo "── Status ────────────────────────────────────" "$VENV/python" - << 'PYEOF' import urllib.request, json try: r = urllib.request.urlopen('http://127.0.0.1:8765/stats', timeout=3) s = json.loads(r.read().decode()) print(f" EventServer: OK total_events={s['total_events']} last_id={s['last_event_id']}") except Exception as e: print(f" EventServer: ERROR ({e})") PYEOF echo " APK EventServerRelay: real-time notifications + UI events via Kotlin APK" echo " MCP server: android_mcp.py (spawned on demand by Hermes)" echo "──────────────────────────────────────────────" echo "" echo "Logs: $LOGS/" echo "Stop: bash $BASE/stop_all.sh"