Spaces:
Running
Running
Merge pull request #14 from anurag162008/codex/investigate-changes-in-openclaw.json-backup
Browse filesWatch and settle openclaw.json before sync; preserve config patching; rotator logs to stderr; env/docs updates
- .env.example +3 -0
- README.md +1 -0
- openclaw-sync.py +44 -4
.env.example
CHANGED
|
@@ -288,6 +288,9 @@ SYNC_INTERVAL=180
|
|
| 288 |
# Check openclaw.json for settings changes this often (seconds). Default: 1.
|
| 289 |
OPENCLAW_CONFIG_WATCH_INTERVAL=1
|
| 290 |
|
|
|
|
|
|
|
|
|
|
| 291 |
# Webhooks: Standard POST notifications for lifecycle events
|
| 292 |
# WEBHOOK_URL=https://your-webhook-endpoint.com/log
|
| 293 |
|
|
|
|
| 288 |
# Check openclaw.json for settings changes this often (seconds). Default: 1.
|
| 289 |
OPENCLAW_CONFIG_WATCH_INTERVAL=1
|
| 290 |
|
| 291 |
+
# Wait for openclaw.json to stay valid and unchanged before syncing. Default: 3.
|
| 292 |
+
OPENCLAW_CONFIG_SETTLE_SECONDS=3
|
| 293 |
+
|
| 294 |
# Webhooks: Standard POST notifications for lifecycle events
|
| 295 |
# WEBHOOK_URL=https://your-webhook-endpoint.com/log
|
| 296 |
|
README.md
CHANGED
|
@@ -164,6 +164,7 @@ HuggingClaw automatically syncs your workspace (chats, settings, sessions) to a
|
|
| 164 |
| `HF_TOKEN` | — | HF token with **Write** access |
|
| 165 |
| `SYNC_INTERVAL` | `180` | Full backup frequency in seconds |
|
| 166 |
| `OPENCLAW_CONFIG_WATCH_INTERVAL` | `1` | How often to check `openclaw.json` for immediate settings sync |
|
|
|
|
| 167 |
|
| 168 |
## 📦 Ephemeral Package Re-install *(Optional)*
|
| 169 |
|
|
|
|
| 164 |
| `HF_TOKEN` | — | HF token with **Write** access |
|
| 165 |
| `SYNC_INTERVAL` | `180` | Full backup frequency in seconds |
|
| 166 |
| `OPENCLAW_CONFIG_WATCH_INTERVAL` | `1` | How often to check `openclaw.json` for immediate settings sync |
|
| 167 |
+
| `OPENCLAW_CONFIG_SETTLE_SECONDS` | `3` | How long `openclaw.json` must stay valid and unchanged before syncing |
|
| 168 |
|
| 169 |
## 📦 Ephemeral Package Re-install *(Optional)*
|
| 170 |
|
openclaw-sync.py
CHANGED
|
@@ -43,6 +43,10 @@ CONFIG_WATCH_INTERVAL = max(
|
|
| 43 |
0.5,
|
| 44 |
float(os.environ.get("OPENCLAW_CONFIG_WATCH_INTERVAL", "1")),
|
| 45 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
HF_TOKEN = os.environ.get("HF_TOKEN", "").strip()
|
| 47 |
HF_USERNAME = os.environ.get("HF_USERNAME", "").strip()
|
| 48 |
SPACE_AUTHOR_NAME = os.environ.get("SPACE_AUTHOR_NAME", "").strip()
|
|
@@ -440,13 +444,46 @@ def handle_signal(_sig, _frame) -> None:
|
|
| 440 |
STOP_EVENT.set()
|
| 441 |
|
| 442 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
def wait_for_sync_trigger(config_marker: tuple[int, int, int]) -> tuple[str, tuple[int, int, int]]:
|
| 444 |
deadline = time.monotonic() + max(0, INTERVAL)
|
| 445 |
|
| 446 |
while not STOP_EVENT.is_set():
|
| 447 |
current_config_marker = file_marker(OPENCLAW_CONFIG_FILE)
|
| 448 |
if current_config_marker != config_marker:
|
| 449 |
-
return (
|
| 450 |
|
| 451 |
remaining = deadline - time.monotonic()
|
| 452 |
if remaining <= 0:
|
|
@@ -511,7 +548,10 @@ def loop() -> int:
|
|
| 511 |
config_marker = file_marker(OPENCLAW_CONFIG_FILE)
|
| 512 |
|
| 513 |
if config_marker != sync_started_config_marker:
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
| 515 |
continue
|
| 516 |
except Exception as exc:
|
| 517 |
write_status("error", f"Sync failed: {exc}")
|
|
@@ -521,8 +561,8 @@ def loop() -> int:
|
|
| 521 |
trigger, config_marker = wait_for_sync_trigger(config_marker)
|
| 522 |
if trigger == "stopped":
|
| 523 |
break
|
| 524 |
-
if trigger == "
|
| 525 |
-
print("OpenClaw config changed; syncing immediately.")
|
| 526 |
|
| 527 |
return 0
|
| 528 |
|
|
|
|
| 43 |
0.5,
|
| 44 |
float(os.environ.get("OPENCLAW_CONFIG_WATCH_INTERVAL", "1")),
|
| 45 |
)
|
| 46 |
+
CONFIG_SETTLE_SECONDS = max(
|
| 47 |
+
0.0,
|
| 48 |
+
float(os.environ.get("OPENCLAW_CONFIG_SETTLE_SECONDS", "3")),
|
| 49 |
+
)
|
| 50 |
HF_TOKEN = os.environ.get("HF_TOKEN", "").strip()
|
| 51 |
HF_USERNAME = os.environ.get("HF_USERNAME", "").strip()
|
| 52 |
SPACE_AUTHOR_NAME = os.environ.get("SPACE_AUTHOR_NAME", "").strip()
|
|
|
|
| 444 |
STOP_EVENT.set()
|
| 445 |
|
| 446 |
|
| 447 |
+
def is_valid_json_file(path: Path) -> bool:
|
| 448 |
+
if not path.exists():
|
| 449 |
+
return True
|
| 450 |
+
|
| 451 |
+
try:
|
| 452 |
+
json.loads(path.read_text(encoding="utf-8"))
|
| 453 |
+
return True
|
| 454 |
+
except Exception:
|
| 455 |
+
return False
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def wait_for_config_settle(config_marker: tuple[int, int, int]) -> tuple[str, tuple[int, int, int]]:
|
| 459 |
+
stable_since = time.monotonic()
|
| 460 |
+
current_marker = config_marker
|
| 461 |
+
|
| 462 |
+
while not STOP_EVENT.is_set():
|
| 463 |
+
latest_marker = file_marker(OPENCLAW_CONFIG_FILE)
|
| 464 |
+
if latest_marker != current_marker:
|
| 465 |
+
current_marker = latest_marker
|
| 466 |
+
stable_since = time.monotonic()
|
| 467 |
+
|
| 468 |
+
if (
|
| 469 |
+
time.monotonic() - stable_since >= CONFIG_SETTLE_SECONDS
|
| 470 |
+
and is_valid_json_file(OPENCLAW_CONFIG_FILE)
|
| 471 |
+
):
|
| 472 |
+
return ("settled", current_marker)
|
| 473 |
+
|
| 474 |
+
if STOP_EVENT.wait(CONFIG_WATCH_INTERVAL):
|
| 475 |
+
return ("stopped", current_marker)
|
| 476 |
+
|
| 477 |
+
return ("stopped", current_marker)
|
| 478 |
+
|
| 479 |
+
|
| 480 |
def wait_for_sync_trigger(config_marker: tuple[int, int, int]) -> tuple[str, tuple[int, int, int]]:
|
| 481 |
deadline = time.monotonic() + max(0, INTERVAL)
|
| 482 |
|
| 483 |
while not STOP_EVENT.is_set():
|
| 484 |
current_config_marker = file_marker(OPENCLAW_CONFIG_FILE)
|
| 485 |
if current_config_marker != config_marker:
|
| 486 |
+
return wait_for_config_settle(current_config_marker)
|
| 487 |
|
| 488 |
remaining = deadline - time.monotonic()
|
| 489 |
if remaining <= 0:
|
|
|
|
| 548 |
config_marker = file_marker(OPENCLAW_CONFIG_FILE)
|
| 549 |
|
| 550 |
if config_marker != sync_started_config_marker:
|
| 551 |
+
trigger, config_marker = wait_for_config_settle(config_marker)
|
| 552 |
+
if trigger == "stopped":
|
| 553 |
+
break
|
| 554 |
+
print("OpenClaw config changed during sync; syncing again after it settled.")
|
| 555 |
continue
|
| 556 |
except Exception as exc:
|
| 557 |
write_status("error", f"Sync failed: {exc}")
|
|
|
|
| 561 |
trigger, config_marker = wait_for_sync_trigger(config_marker)
|
| 562 |
if trigger == "stopped":
|
| 563 |
break
|
| 564 |
+
if trigger == "settled":
|
| 565 |
+
print("OpenClaw config changed and settled; syncing immediately.")
|
| 566 |
|
| 567 |
return 0
|
| 568 |
|