#!/usr/bin/env bash set -euo pipefail # Watchdog B MVP tri-state checker for OpenClaw main runtime. # Output (stdout): exactly one token: running | stalled | idle # # Heuristic (MVP): # - If openclaw.pid exists and process is alive => running unless logs are stale. # - If process alive but log file hasn't changed for STALL_AFTER_SECONDS => stalled. # - Otherwise => idle. # # Future extension point: # - Replace/augment log-freshness with real main-agent session/ledger signals. PID_FILE_DEFAULT="${OPENCLAW_PID_FILE:-/home/chchang/.openclaw/workspace/host-runtime/openclaw.pid}" LOG_FILE_DEFAULT="${OPENCLAW_LOG_FILE:-/home/chchang/.openclaw/workspace/logs/openclaw.log}" STALL_AFTER_SECONDS="${STALL_AFTER_SECONDS:-1200}" # 20 minutes default NOW_EPOCH="$(date +%s)" pid_file="$PID_FILE_DEFAULT" log_file="$LOG_FILE_DEFAULT" get_mtime_epoch() { # GNU stat: %Y; BSD stat: -f %m local path="$1" if stat -c %Y "$path" >/dev/null 2>&1; then stat -c %Y "$path" else stat -f %m "$path" fi } proc_alive() { local pid="$1" [[ -n "$pid" ]] || return 1 [[ "$pid" =~ ^[0-9]+$ ]] || return 1 kill -0 "$pid" >/dev/null 2>&1 } # No pid file => idle if [[ ! -f "$pid_file" ]]; then echo "idle" exit 0 fi pid="$(tr -d ' \t\n\r' < "$pid_file" || true)" # PID file exists but process not alive => idle if ! proc_alive "$pid"; then echo "idle" exit 0 fi # Process alive. If no log file, assume running (can't assess stall) if [[ ! -f "$log_file" ]]; then echo "running" exit 0 fi log_mtime="$(get_mtime_epoch "$log_file")" age=$(( NOW_EPOCH - log_mtime )) if (( age > STALL_AFTER_SECONDS )); then echo "stalled" else echo "running" fi