#!/usr/bin/env bash # email-channel supervisor. # # Streamable HTTP MCP server on port 18810 (Phase C dual-listener: # also runs the Microsoft 365 polling loop in the same bun process). # Bun + restart-on-crash. Locked with flock so multiple invocations are safe. set -euo pipefail LOCK_FILE="/tmp/email-channel.lock" LOG_FILE="/tmp/email-channel.log" HEALTH_FILE="/tmp/email-channel-health.json" ENV_FILE="$HOME/.config/email-channel.env" RESTART_DELAY=3 STABILITY_THRESHOLD=60 SERVER_DIR="$HOME/repos/exult-agent/tools/email-channel" cleanup() { rm -f "$LOCK_FILE" 2>/dev/null || true } trap cleanup EXIT # Single-instance guard if [ -f "$LOCK_FILE" ]; then other=$(cat "$LOCK_FILE" 2>/dev/null || true) if [ -n "$other" ] && kill -0 "$other" 2>/dev/null; then echo "[$(date)] another instance running (pid $other). Exiting." >&2 exit 0 fi rm -f "$LOCK_FILE" fi echo $$ > "$LOCK_FILE" log_msg() { echo "[$(date '+%Y-%m-%dT%H:%M:%S%z')] $1" | tee -a "$LOG_FILE"; } BUN_BIN="$(command -v bun || echo "$HOME/.bun/bin/bun")" [ -x "$BUN_BIN" ] || { log_msg "ERROR: bun binary not found"; exit 1; } [ -d "$SERVER_DIR" ] || { log_msg "ERROR: server dir missing: $SERVER_DIR"; exit 1; } # Load env (MS365 creds, poll interval, MCP_BEARER_TOKEN) if [ -f "$ENV_FILE" ]; then set -a # shellcheck disable=SC1090 source "$ENV_FILE" set +a else log_msg "ERROR: env file missing: $ENV_FILE" exit 1 fi # Required env sanity for k in MS365_CLIENT_ID MS365_CLIENT_SECRET MS365_TENANT_ID MS365_MAILBOX MCP_BEARER_TOKEN; do if [ -z "${!k:-}" ]; then log_msg "ERROR: $k not set in $ENV_FILE" exit 1 fi done : "${EMAIL_POLL_INTERVAL:=20}" export MS365_CLIENT_ID MS365_CLIENT_SECRET MS365_TENANT_ID MS365_MAILBOX \ EMAIL_POLL_INTERVAL MCP_BEARER_TOKEN consecutive_failures=0 last_failure_ts=0 restarts_today=0 backoff() { if [ "$consecutive_failures" -ge 5 ]; then echo 60 elif [ "$consecutive_failures" -ge 3 ]; then echo 15 else echo "$RESTART_DELAY"; fi } while true; do start_ts=$(date +%s) log_msg "starting bun server (port=18810 failures=$consecutive_failures)" "$BUN_BIN" "$SERVER_DIR/server.ts" >>"$LOG_FILE" 2>&1 || true end_ts=$(date +%s) dur=$((end_ts - start_ts)) if [ "$dur" -gt "$STABILITY_THRESHOLD" ]; then consecutive_failures=0 log_msg "session ran ${dur}s (stable)" else if [ "$last_failure_ts" -gt 0 ] && [ $((end_ts - last_failure_ts)) -gt 300 ]; then consecutive_failures=1 else consecutive_failures=$((consecutive_failures + 1)) fi last_failure_ts="$end_ts" log_msg "session ran ${dur}s (consecutive failure #$consecutive_failures)" fi restarts_today=$((restarts_today + 1)) cat >"$HEALTH_FILE" <