#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" CONFIG_PATH="${AUTH_WATCHDOG_CONFIG:-$ROOT_DIR/config/copilot-auth-watchdog.config.json}" if ! command -v jq >/dev/null 2>&1; then echo "[copilot-auth-watchdog] jq is required" >&2 exit 1 fi expand_tilde() { local p="$1" if [[ "$p" == "~" ]]; then echo "$HOME" elif [[ "$p" == "~/"* ]]; then echo "$HOME/${p#~/}" else echo "$p" fi } now_ms() { python3 - <<'PY' import time print(int(time.time()*1000)) PY } send_notice() { local text="$1" local last_channel="$2" local last_to="$3" if [[ -z "$last_channel" || -z "$last_to" ]]; then echo "[copilot-auth-watchdog] notice skipped (missing channel/target): $text" >&2 return 0 fi local target="$last_to" if [[ "$target" == *:* ]]; then target="${target#*:}" fi openclaw message send \ --channel "$last_channel" \ --target "$target" \ --message "$text" \ >/dev/null 2>&1 || true } if [[ ! -f "$CONFIG_PATH" ]]; then echo "[copilot-auth-watchdog] missing config: $CONFIG_PATH" >&2 exit 1 fi enabled="$(jq -r '.enabled // true' "$CONFIG_PATH")" if [[ "$enabled" != "true" ]]; then exit 0 fi session_key="$(jq -r '.sessionKey // "agent:main:main"' "$CONFIG_PATH")" alert_interval_min="$(jq -r '.minAlertIntervalMinutes // 30' "$CONFIG_PATH")" state_file_raw="$(jq -r '.stateFile // "~/.openclaw/copilot-auth-watchdog-state.json"' "$CONFIG_PATH")" state_file="$(expand_tilde "$state_file_raw")" check_model_status="$(jq -r '.checkOpenClawModelStatus // true' "$CONFIG_PATH")" state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}" sessions_file="$state_dir/agents/main/sessions/sessions.json" last_channel="" last_to="" if [[ -r "$sessions_file" ]]; then session_json="$(jq -c --arg key "$session_key" '.[$key] // {}' "$sessions_file" 2>/dev/null || true)" if [[ -n "$session_json" && "$session_json" != "{}" ]]; then last_channel="$(jq -r '.lastChannel // empty' <<<"$session_json")" last_to="$(jq -r '.lastTo // empty' <<<"$session_json")" fi fi declare -a issues=() if ! command -v copilot >/dev/null 2>&1; then issues+=("copilot CLI not installed; run setup_openclaw_copilot.sh") else set +e copilot_out="$(copilot auth status 2>&1)" copilot_rc=$? set -e if [[ $copilot_rc -ne 0 ]]; then issues+=("copilot auth status failed; run: copilot auth login") elif echo "$copilot_out" | grep -Eiq 'not authenticated|not logged|login required|expired|unauthorized|invalid'; then issues+=("copilot auth not healthy; run: copilot auth login") fi fi if [[ "$check_model_status" == "true" ]]; then if ! command -v openclaw >/dev/null 2>&1; then issues+=("openclaw CLI not installed") else if ! openclaw models status --check >/dev/null 2>&1; then issues+=("openclaw model auth check failed; run: openclaw models status --json and copilot auth login") fi fi fi if [[ ${#issues[@]} -eq 0 ]]; then exit 0 fi mkdir -p "$(dirname "$state_file")" if [[ ! -f "$state_file" ]]; then printf '{}\n' > "$state_file" fi state_json="$(cat "$state_file")" last_alert_at="$(jq -r --arg key "$session_key" '.[$key].lastAlertAt // 0' <<<"$state_json")" if [[ ! "$last_alert_at" =~ ^[0-9]+$ ]]; then last_alert_at=0 fi now="$(now_ms)" interval_ms=$((alert_interval_min * 60 * 1000)) if (( now - last_alert_at < interval_ms )); then exit 0 fi summary="Copilot auth watchdog alert: ${issues[*]}" echo "[copilot-auth-watchdog] $summary" send_notice "$summary" "$last_channel" "$last_to" state_json="$(jq --arg key "$session_key" --argjson now "$now" '.[$key].lastAlertAt=$now' <<<"$state_json")" printf '%s\n' "$state_json" > "$state_file"