Files
openclaw-control-plane/sync/sync-agent.sh
wangzhendong b5acead1e1 Add polling sync communication layer
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-15 11:11:18 +08:00

107 lines
1.9 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
INTERVAL_SECONDS="${INTERVAL_SECONDS:-30}"
ONCE="${1:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
STATE_DIR="$REPO_ROOT/.sync-state"
ERROR_FILE="$STATE_DIR/sync-error-vps.md"
ALLOWED_PATHS=(
"AGENT_BOARD.md"
"README.md"
".cursor/rules"
"handoff"
"evidence"
"rollback"
"tasks"
"sync"
".gitignore"
)
ensure_state_dir() {
mkdir -p "$STATE_DIR"
}
write_sync_error() {
ensure_state_dir
local message="$1"
local timestamp
timestamp="$(date '+%Y-%m-%d %H:%M:%S %z')"
cat > "$ERROR_FILE" <<EOF
# VPS Sync Error
- Time: $timestamp
- Repository: $REPO_ROOT
- Message: $message
Manual action required. Do not continue automatic sync until the repository is clean.
EOF
}
has_allowed_changes() {
local line path allowed
while IFS= read -r line; do
[[ -z "$line" ]] && continue
path="${line:3}"
for allowed in "${ALLOWED_PATHS[@]}"; do
if [[ "$path" == "$allowed" || "$path" == "$allowed/"* ]]; then
return 0
fi
done
done < <(git status --porcelain)
return 1
}
has_conflicts() {
[[ -n "$(git diff --name-only --diff-filter=U)" ]]
}
sync_once() {
cd "$REPO_ROOT"
ensure_state_dir
git pull --rebase --autostash
if has_conflicts; then
return 20
fi
if ! has_allowed_changes; then
return 0
fi
local path
for path in "${ALLOWED_PATHS[@]}"; do
if [[ -e "$path" ]]; then
git add -- "$path"
fi
done
if [[ -z "$(git diff --cached --name-only)" ]]; then
return 0
fi
git commit -m "Sync agent state from VPS at $(date '+%Y-%m-%d %H:%M:%S')"
git push
}
while true; do
if sync_once; then
rm -f "$ERROR_FILE"
else
exit_code="$?"
write_sync_error "sync failed with exit code $exit_code"
echo "sync failed with exit code $exit_code" >&2
exit "$exit_code"
fi
if [[ "$ONCE" == "--once" ]]; then
break
fi
sleep "$INTERVAL_SECONDS"
done