#!/usr/bin/env bash set -euo pipefail DATA_ROOT="${DATA_ROOT:-/data/root}" SYNC_INTERVAL="${SYNC_INTERVAL:-300}" CONFIG_PATH=".openclaw/openclaw.json" EXCLUDES=( "--exclude=.cache" "--exclude=.npm/_cacache" "--exclude=.npm/_update-notifier-last-checked*" "--exclude=.pnpm-store" "--exclude=node_modules" "--exclude=.next" "--exclude=dist" "--exclude=build" "--exclude=coverage" "--exclude=tmp" ) data_available() { [ -d /data ] || mkdir -p /data 2>/dev/null || return 1 mkdir -p "$DATA_ROOT" 2>/dev/null || return 1 } restore_root() { if data_available; then echo "Restoring persistent data from $DATA_ROOT to /root..." rsync -rlptD --no-specials --no-devices --update "${EXCLUDES[@]}" "$DATA_ROOT/" /root/ || true else echo "Persistent /data is not available; skipping restore." fi } persist_root() { if data_available; then echo "Syncing /root to $DATA_ROOT..." rsync -rlptD --no-specials --no-devices --update --delete "${EXCLUDES[@]}" /root/ "$DATA_ROOT/" || true else echo "Persistent /data is not available; skipping sync." fi } sync_config_newer() { local root_config="/root/$CONFIG_PATH" local data_config="$DATA_ROOT/$CONFIG_PATH" if [ -f "$data_config" ] && { [ ! -f "$root_config" ] || [ "$data_config" -nt "$root_config" ]; }; then mkdir -p "$(dirname "$root_config")" cp -p "$data_config" "$root_config" || true echo "Pulled newer OpenClaw config from $data_config to $root_config." elif [ -f "$root_config" ] && { [ ! -f "$data_config" ] || [ "$root_config" -nt "$data_config" ]; }; then mkdir -p "$(dirname "$data_config")" cp -p "$root_config" "$data_config" || true echo "Persisted newer OpenClaw config from $root_config to $data_config." fi } reconcile_root_data() { if data_available; then restore_root sync_config_newer persist_root else echo "Persistent /data is not available; skipping two-way sync." fi } case "${1:-loop}" in restore) restore_root ;; persist) persist_root ;; reconcile) reconcile_root_data ;; loop) while true; do reconcile_root_data sleep "$SYNC_INTERVAL" done ;; *) echo "Usage: $0 {restore|persist|loop}" exit 2 ;; esac