feat(zulip-health): monitor script + infra report — cron-ready implementations of prose contracts
This commit is contained in:
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
# /root/scripts/zulip-monitor.sh — Zulip Mesh Health Monitor
|
||||
# Implements zulip-health.prose.md v2
|
||||
# Runs every 15 min via cron. Alerts via Telegram.
|
||||
set -euo pipefail
|
||||
|
||||
ZULIP_SITE="https://chat.sysloggh.net"
|
||||
ZULIP_EMAIL="abiba-bot@chat.sysloggh.net"
|
||||
ZULIP_KEY="cKTDMZAPW08dk3zl05sStzO7HRztzyn8"
|
||||
OWNER_ZULIP_ID="9"
|
||||
|
||||
# Email config
|
||||
GMAIL_USER="jtabiri@gmail.com"
|
||||
GMAIL_PASS="rgbuomwcydxwbszd"
|
||||
EMAIL_TO="jerome@sysloggh.com"
|
||||
|
||||
notify() {
|
||||
local severity="$1" msg="$2"
|
||||
echo "[$severity] $msg"
|
||||
|
||||
# Zulip DM to owner
|
||||
local content="${severity} Zulip Monitor: ${msg}"
|
||||
local form="type=private&to=%5B${OWNER_ZULIP_ID}%5D&content=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''${content}'''))")"
|
||||
curl -sf -X POST "${ZULIP_SITE}/api/v1/messages" \
|
||||
-u "${ZULIP_EMAIL}:${ZULIP_KEY}" \
|
||||
-d "${form}" > /dev/null 2>&1 || true
|
||||
|
||||
# Email alert
|
||||
local subject="${severity} Zulip Monitor Alert"
|
||||
python3 -c "
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
m = MIMEText('''${msg}''')
|
||||
m['From'] = 'abiba@sysloggh.com'
|
||||
m['To'] = '${EMAIL_TO}'
|
||||
m['Subject'] = '${subject}'
|
||||
s = smtplib.SMTP('smtp.gmail.com', 587)
|
||||
s.starttls()
|
||||
s.login('${GMAIL_USER}', '${GMAIL_PASS}')
|
||||
s.sendmail('abiba@sysloggh.com', ['${EMAIL_TO}'], m.as_string())
|
||||
s.quit()
|
||||
" 2>/dev/null || true
|
||||
}
|
||||
|
||||
TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||||
ISSUES=0
|
||||
LOG="/root/zulip-health-monitor.log"
|
||||
|
||||
echo "=== Zulip Health Check — $TIMESTAMP ===" >> "$LOG"
|
||||
|
||||
# ── Global: Zulip Server ──
|
||||
SERVER_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 \
|
||||
https://chat.sysloggh.net/api/v1/server_settings \
|
||||
-u 'abiba-bot@chat.sysloggh.net:cKTDMZAPW08dk3zl05sStzO7HRztzyn8' 2>/dev/null || echo "000")
|
||||
if [ "$SERVER_CODE" != "200" ]; then
|
||||
notify "🔴" "Zulip server returned HTTP $SERVER_CODE"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo " Server: ✅ HTTP 200" >> "$LOG"
|
||||
fi
|
||||
|
||||
# ── Platform A: pi (Abiba) ──
|
||||
PI_HEALTH=$(curl -sf --connect-timeout 5 http://localhost:9200/health 2>/dev/null || echo "{}")
|
||||
PI_CONNECTED=$(echo "$PI_HEALTH" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('connected',False))" 2>/dev/null)
|
||||
PI_ERROR=$(echo "$PI_HEALTH" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('last_error') or '')" 2>/dev/null)
|
||||
PI_RETRIES=$(echo "$PI_HEALTH" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('retry_count',0))" 2>/dev/null)
|
||||
|
||||
if [ "$PI_CONNECTED" != "True" ]; then
|
||||
notify "🔴" "Abiba pi extension DISCONNECTED — restarting"
|
||||
pm2 restart abiba-zulip 2>/dev/null || true
|
||||
ISSUES=$((ISSUES + 1))
|
||||
echo " Abiba: ❌ Disconnected — restarted" >> "$LOG"
|
||||
elif [ -n "$PI_ERROR" ]; then
|
||||
notify "🟡" "Abiba pi extension error: ${PI_ERROR:0:100}"
|
||||
echo " Abiba: 🟡 Error: ${PI_ERROR:0:100}" >> "$LOG"
|
||||
elif [ "$PI_RETRIES" -ge 3 ]; then
|
||||
notify "🟡" "Abiba pi extension: $PI_RETRIES retries — restarting"
|
||||
pm2 restart abiba-zulip 2>/dev/null || true
|
||||
echo " Abiba: 🟡 $PI_RETRIES retries — restarted" >> "$LOG"
|
||||
else
|
||||
echo " Abiba: ✅ Connected (processed=$(echo "$PI_HEALTH" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('messages_processed',0))" 2>/dev/null))" >> "$LOG"
|
||||
fi
|
||||
|
||||
# ── Platform B: Hermes (Tanko) ──
|
||||
TANKO_STATE=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 jerome@192.168.68.122 \
|
||||
"cat ~/.hermes/gateway_state.json 2>/dev/null" 2>/dev/null || echo "{}")
|
||||
TANKO_ZULIP=$(echo "$TANKO_STATE" | python3 -c "
|
||||
import sys,json
|
||||
d=json.load(sys.stdin)
|
||||
p=d.get('platforms',{}).get('zulip',{})
|
||||
print(p.get('state','unknown'))
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ "$TANKO_ZULIP" != "connected" ]; then
|
||||
notify "🔴" "Tanko (Hermes) Zulip state: $TANKO_ZULIP — needs restart"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
echo " Tanko: ❌ state=$TANKO_ZULIP" >> "$LOG"
|
||||
else
|
||||
echo " Tanko: ✅ Zulip connected" >> "$LOG"
|
||||
fi
|
||||
|
||||
# ── Platform B: Hermes (Mumuni) ──
|
||||
MUMUNI_STATE=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@192.168.68.123 \
|
||||
"cat ~/.hermes/gateway_state.json 2>/dev/null" 2>/dev/null || echo "{}")
|
||||
MUMUNI_ZULIP=$(echo "$MUMUNI_STATE" | python3 -c "
|
||||
import sys,json
|
||||
d=json.load(sys.stdin)
|
||||
p=d.get('platforms',{}).get('zulip',{})
|
||||
print(p.get('state','unknown'))
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ "$MUMUNI_ZULIP" != "connected" ]; then
|
||||
notify "🔴" "Mumuni (Hermes) Zulip state: $MUMUNI_ZULIP"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
echo " Mumuni: ❌ state=$MUMUNI_ZULIP" >> "$LOG"
|
||||
else
|
||||
echo " Mumuni: ✅ Zulip connected" >> "$LOG"
|
||||
fi
|
||||
|
||||
# ── Platform C: Agent Zero (kagentz) ──
|
||||
AZ_A2A=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@192.168.68.14 \
|
||||
"docker exec agent-zero curl -s --connect-timeout 5 http://127.0.0.1:8001/.well-known/agent.json 2>/dev/null" 2>/dev/null || echo "")
|
||||
AZ_ALIVE=$(echo "$AZ_A2A" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('name',''))" 2>/dev/null)
|
||||
|
||||
if [ "$AZ_ALIVE" != "kagentz" ]; then
|
||||
notify "🔴" "kagentz A2A server DOWN — restarting"
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@192.168.68.14 \
|
||||
"docker exec agent-zero bash -c 'pkill -9 -f a2a_agent; sleep 1; cd /a0 && /opt/venv-a0/bin/python3 -u /a0/usr/a2a_agent.py > /tmp/a2a.log 2>&1 &'" 2>/dev/null || true
|
||||
ISSUES=$((ISSUES + 1))
|
||||
echo " kagentz: ❌ A2A down — restarted" >> "$LOG"
|
||||
else
|
||||
echo " kagentz: ✅ A2A alive" >> "$LOG"
|
||||
|
||||
# Check adapter process
|
||||
AZ_ADAPTER=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@192.168.68.14 \
|
||||
"docker exec agent-zero ps aux 2>/dev/null | grep adapter | grep -v grep | wc -l" 2>/dev/null || echo "0")
|
||||
if [ "$AZ_ADAPTER" -lt 1 ]; then
|
||||
notify "🔴" "kagentz Zulip adapter DOWN — restarting"
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@192.168.68.14 \
|
||||
"docker exec agent-zero bash -c 'cd /a0/usr/kagentz-zulip && ZULIP_SITE=https://chat.sysloggh.net ZULIP_EMAIL=kagentz-bot@chat.sysloggh.net ZULIP_API_KEY=E9q9PXJTxftPYBkb5pBDWupDO7KK21ty ZULIP_AGENT_NAME=kagentz A2A_URL=http://localhost:8001/a2a A2A_TOKEN=8zNgdOEXzYxjQvTl /opt/venv-a0/bin/python3 -u adapter.py > /tmp/zulip-adapter.log 2>&1 &'" 2>/dev/null || true
|
||||
ISSUES=$((ISSUES + 1))
|
||||
echo " kagentz: ❌ Adapter down — restarted" >> "$LOG"
|
||||
else
|
||||
echo " kagentz: ✅ Adapter running" >> "$LOG"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Summary ──
|
||||
if [ "$ISSUES" -eq 0 ]; then
|
||||
echo " Result: ✅ All healthy" >> "$LOG"
|
||||
else
|
||||
echo " Result: 🔴 $ISSUES issue(s) found" >> "$LOG"
|
||||
notify "🔴" "$ISSUES issue(s) found — check /root/zulip-health-monitor.log"
|
||||
fi
|
||||
|
||||
echo "" >> "$LOG"
|
||||
Reference in New Issue
Block a user