#!/usr/bin/env bash # On-board reload-watcher. The ZeroClaw dashboard lets a team edit config from # their browser (enable their Telegram bot, set allowed_users, …) — but a config # write only sets `pending_reload`; the channel listeners don't re-spawn until # `POST /admin/reload`, which is LOOPBACK-ONLY. A LAN browser can't call it. # # This watcher closes that gap: it runs on the board, notices when config.toml # changes, and fires the loopback reload — so a team's dashboard edit takes # effect on its own within a few seconds, no shell required. It's the thing that # makes browser self-serve (Telegram, and the lockdown flip) actually work. # # ./zeroclaw-reload-watcher.sh # loops; run via setsid or systemd # # Env: # ZC_CONFIG config file to watch (default /home/arduino/.zeroclaw/config.toml) # GATEWAY_PORT loopback gateway port (default 8080) # POLL_SECS poll interval (default 3) # SETTLE_SECS debounce after a change before reloading (default 2) set -uo pipefail CONFIG="${ZC_CONFIG:-/home/arduino/.zeroclaw/config.toml}" PORT="${GATEWAY_PORT:-8080}" POLL="${POLL_SECS:-3}" SETTLE="${SETTLE_SECS:-2}" BASE="http://127.0.0.1:${PORT}" mtime() { stat -c %Y "$CONFIG" 2>/dev/null || echo 0; } reload() { # loopback → passes the gateway's require_localhost gate curl -sf --max-time 5 -X POST "${BASE}/admin/reload" >/dev/null 2>&1 } echo "[reload-watcher] watching $CONFIG → ${BASE}/admin/reload (poll ${POLL}s)" last="$(mtime)" while :; do sleep "$POLL" cur="$(mtime)" [ "$cur" = "$last" ] && continue # config changed — let a burst of per-field writes settle, then reload once sleep "$SETTLE" cur="$(mtime)" if reload; then echo "[reload-watcher] config changed → reloaded daemon ($(date -u +%H:%M:%SZ))" else echo "[reload-watcher] config changed → reload failed (daemon down?); will retry" fi last="$cur" done