Skill: yu200512/idle-shutdown (version 1.0.0)

--- name: gateway-idle-shutdown description: Monitors OpenClaw Gateway user session idleness and automatically shuts down the Gateway after a configured idle period. Use when: (1) you need to detect idle state when no messages are exchanged between bot and user, (2) you want to set an idle timeout to shut down the Gateway and save resources, (3) you need to install/configure an idle monitoring systemd service, (4) you are managing the Gateway idle lifecycle. Trigger words: idle, shutdown, sleep, suspend, gateway monitor. --- # Gateway Idle Shutdown Monitors session activity between users and the bot, and automatically stops the OpenClaw Gateway when all sessions have been idle continuously beyond the configured threshold. ## How It Works - Polls session transcript files (`~/.openclaw/agents/main/sessions/`) every 10 seconds for their last modification time (mtime) - Preferentially matches QQBot/channel/direct related session files - When the latest mtime is older than the threshold (default 120 seconds), executes `openclaw gateway stop` - Logs are written to `~/.openclaw/logs/idle-shutdown.log` ## Benefits - **Resource Savings** — Automatically stops the Gateway when no one is using it, freeing CPU and memory that would otherwise be wasted during idle periods. - **Cost Reduction** — On metered or cloud-hosted environments, shutting down idle services reduces compute costs by avoiding charges for unused uptime. - **Energy Efficiency** — Reduces power consumption by ensuring services only run when actually needed, lowering your carbon footprint. - **Zero Manual Intervention** — Once configured, the watcher runs autonomously with systemd; you never need to remember to shut down the Gateway manually. - **Seamless Lifecycle Integration** — The systemd watcher is bound to the Gateway service (BindsTo + PartOf), so it starts and stops in lockstep — no orphaned watcher processes and no stale state. - **Configurable & Forgiving** — Idle threshold, polling interval, session directory, and binary path are all controlled via environment variables; the watcher retries on failure instead of exiting abruptly. - **Lightweight Footprint** — A simple bash script with `sleep`-based polling uses negligible system resources, making it suitable for low-power devices and headless servers alike. ## Installation ```bash # 1. Copy the script cp scripts/idle-shutdown.sh ~/.openclaw/workspace/scripts/ chmod +x ~/.openclaw/workspace/scripts/idle-shutdown.sh # 2. Create the systemd service file mkdir -p ~/.config/systemd/user cat > ~/.config/systemd/user/openclaw-idle-watch.service << 'EOF' [Unit] Description=OpenClaw idle gateway shutdown watcher After=openclaw-gateway.service BindsTo=openclaw-gateway.service PartOf=openclaw-gateway.service [Service] Type=simple ExecStart=%h/.openclaw/workspace/scripts/idle-shutdown.sh Restart=on-failure RestartSec=10s Environment=HOME=%h # Adjust this PATH if your openclaw binary lives elsewhere Environment=PATH=%h/.local/bin:%h/bin:/usr/local/bin:/usr/bin:/bin Environment=OPENCLAW_BIN=openclaw [Install] WantedBy=default.target EOF # 3. Enable and start the service systemctl --user daemon-reload systemctl --user enable openclaw-idle-watch.service systemctl --user start openclaw-idle-watch.service ``` ## Configuration Customize behavior via environment variables: | Variable | Default | Description | |----------|---------|-------------| | `IDLE_SECONDS` | 120 | Idle threshold (seconds) | | `POLL_SECONDS` | 10 | Polling interval (seconds) | | `SESSIONS_DIR` | `~/.openclaw/agents/main/sessions` | Session directory | | `LOG_FILE` | `~/.openclaw/logs/idle-shutdown.log` | Log file path | | `OPENCLAW_BIN` | `openclaw` | openclaw binary path | ## Uninstallation ```bash systemctl --user stop openclaw-idle-watch.service systemctl --user disable openclaw-idle-watch.service rm ~/.config/systemd/user/openclaw-idle-watch.service rm ~/.openclaw/workspace/scripts/idle-shutdown.sh ``` ## Service Lifecycle - The watcher is bound to `openclaw-gateway.service` (BindsTo + PartOf) - Gateway starts → Watcher starts automatically - Gateway stops → Watcher stops automatically - Watcher triggers on idle → Gateway is stopped → Watcher stops along with Gateway

path: scripts/idle-shutdown.sh
content: #!/usr/bin/env bash
set -uo pipefail

# OpenClaw idle gateway shutdown watcher.
# Polls QQBot/user-bot session transcript files and stops the gateway after all
# active sessions have been idle continuously for IDLE_SECONDS.

IDLE_SECONDS="${IDLE_SECONDS:-120}"
POLL_SECONDS="${POLL_SECONDS:-10}"
SESSIONS_DIR="${SESSIONS_DIR:-$HOME/.openclaw/agents/main/sessions}"
LOG_FILE="${LOG_FILE:-$HOME/.openclaw/logs/idle-shutdown.log}"
OPENCLAW_BIN="${OPENCLAW_BIN:-openclaw}"

mkdir -p "$(dirname "$LOG_FILE")"

log() {
  printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S %z')" "$*" >> "$LOG_FILE"
}

gateway_is_running() {
  local output rc
  output="$($OPENCLAW_BIN gateway status 2>&1)"
  rc=$?

  # openclaw gateway status should normally return success. If it does not,
  # treat obvious stopped/unavailable output as stopped and anything else as
  # not safely stoppable this round.
  if printf '%s\n' "$output" | grep -Eiq '(^|[^a-z])(stopped|not running|inactive|dead|failed|offline)([^a-z]|$)'; then
    return 1
  fi

  if [ "$rc" -ne 0 ]; then
    log "gateway status failed (rc=$rc): ${output//$'\n'/ }"
    return 1
  fi

  return 0
}

session_files() {
  [ -d "$SESSIONS_DIR" ] || return 0

  # Prefer QQBot/channel session transcript files when names expose that.
  # If none match, fall back to all regular session files so the watcher still
  # works across OpenClaw transcript naming changes.
  local matches
  matches="$(find "$SESSIONS_DIR" -type f \
    \( -iname '*qqbot*' -o -iname '*channel*' -o -iname '*direct*' \) \
    -print 2>/dev/null | sort || true)"

  if [ -n "$matches" ]; then
    printf '%s\n' "$matches"
  else
    find "$SESSIONS_DIR" -type f -print 2>/dev/null | sort || true
  fi
}

newest_session_mtime() {
  local newest=0 file mtime
  while IFS= read -r file; do
    [ -n "$file" ] || continue
    [ -f "$file" ] || continue
    mtime="$(stat -c '%Y' "$file" 2>/dev/null || echo 0)"
    if [ "${mtime:-0}" -gt "$newest" ]; then
      newest="$mtime"
    fi
  done < <(session_files)
  printf '%s\n' "$newest"
}

log "idle shutdown watcher started (idle=${IDLE_SECONDS}s poll=${POLL_SECONDS}s sessions=$SESSIONS_DIR)"

while true; do
  if ! gateway_is_running; then
    log "gateway already stopped or status unavailable; watcher exiting"
    exit 0
  fi

  newest_mtime="$(newest_session_mtime)"
  now="$(date +%s)"

  if [ "${newest_mtime:-0}" -le 0 ]; then
    log "no session transcript files found under $SESSIONS_DIR; waiting"
    sleep "$POLL_SECONDS"
    continue
  fi

  idle_for=$(( now - newest_mtime ))

  if [ "$idle_for" -ge "$IDLE_SECONDS" ]; then
    log "all detected sessions idle for ${idle_for}s (threshold ${IDLE_SECONDS}s); stopping gateway"
    if "$OPENCLAW_BIN" gateway stop >> "$LOG_FILE" 2>&1; then
      log "gateway stop completed"
      exit 0
    else
      rc=$?
      log "gateway stop failed (rc=$rc); retrying after ${POLL_SECONDS}s"
    fi
  fi

  sleep "$POLL_SECONDS"
done


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)