Spaces:
Running
Running
Update scripts/sync_hf.py
Browse files- scripts/sync_hf.py +120 -21
scripts/sync_hf.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
OpenClaw HF Spaces Persistence β Full Directory Sync
|
|
@@ -447,27 +448,48 @@ class OpenClawFullSync:
|
|
| 447 |
data.setdefault("agents", {}).setdefault("defaults", {}).setdefault("model", {})
|
| 448 |
data.setdefault("session", {})["scope"] = "global"
|
| 449 |
|
| 450 |
-
# ββ Provider
|
| 451 |
-
#
|
| 452 |
-
#
|
| 453 |
-
#
|
| 454 |
-
#
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
del restored_providers[pid]
|
| 470 |
-
print(
|
|
|
|
|
|
|
|
|
|
| 471 |
|
| 472 |
if OPENCLAW_DEFAULT_MODEL:
|
| 473 |
data["agents"]["defaults"]["model"]["primary"] = OPENCLAW_DEFAULT_MODEL
|
|
@@ -618,6 +640,63 @@ class OpenClawFullSync:
|
|
| 618 |
print(f"[SYNC] ββ Periodic sync triggered at {datetime.now().isoformat()} ββ")
|
| 619 |
self.save_to_repo()
|
| 620 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 621 |
# ββ Application runner βββββββββββββββββββββββββββββββββββββββββββββ
|
| 622 |
|
| 623 |
def run_openclaw(self):
|
|
@@ -738,9 +817,28 @@ def main():
|
|
| 738 |
sync.load_from_repo()
|
| 739 |
print(f"[TIMER] load_from_repo (restore): {time.time() - t0:.1f}s")
|
| 740 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
# 2. Background sync
|
| 742 |
stop_event = threading.Event()
|
| 743 |
-
t = threading.Thread(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 744 |
t.start()
|
| 745 |
|
| 746 |
# 3. Start application
|
|
@@ -839,3 +937,4 @@ def main():
|
|
| 839 |
|
| 840 |
if __name__ == "__main__":
|
| 841 |
main()
|
|
|
|
|
|
| 1 |
+
```
|
| 2 |
#!/usr/bin/env python3
|
| 3 |
"""
|
| 4 |
OpenClaw HF Spaces Persistence β Full Directory Sync
|
|
|
|
| 448 |
data.setdefault("agents", {}).setdefault("defaults", {}).setdefault("model", {})
|
| 449 |
data.setdefault("session", {})["scope"] = "global"
|
| 450 |
|
| 451 |
+
# ββ Provider API-key resolution ββββββββββββββββββββββββββββββββββ
|
| 452 |
+
# The persisted config may contain template placeholders such as
|
| 453 |
+
# __OPENROUTER_API_KEY__ or __NVIDIA_API_KEY__. Resolve those from
|
| 454 |
+
# the current HF Space environment instead of treating them as
|
| 455 |
+
# stale hardcoded credentials.
|
| 456 |
+
models_cfg = data.setdefault("models", {})
|
| 457 |
+
restored_providers = models_cfg.setdefault("providers", {})
|
| 458 |
+
|
| 459 |
+
provider_env_map = {
|
| 460 |
+
"openrouter": "OPENROUTER_API_KEY",
|
| 461 |
+
"nvidia": "NVIDIA_API_KEY",
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
for pid, env_name in provider_env_map.items():
|
| 465 |
+
if pid not in restored_providers:
|
| 466 |
+
continue
|
| 467 |
+
|
| 468 |
+
env_key = os.environ.get(env_name, "").strip()
|
| 469 |
+
current_key = str(
|
| 470 |
+
restored_providers[pid].get("apiKey", "")
|
| 471 |
+
).strip()
|
| 472 |
+
|
| 473 |
+
# Replace template placeholders with the actual environment key.
|
| 474 |
+
if env_key and (
|
| 475 |
+
not current_key
|
| 476 |
+
or current_key.startswith("${")
|
| 477 |
+
or current_key.startswith("__")
|
| 478 |
+
):
|
| 479 |
+
restored_providers[pid]["apiKey"] = env_key
|
| 480 |
+
print(f"[SYNC] β Resolved {pid} API key from {env_name}")
|
| 481 |
+
|
| 482 |
+
# Remove only an unresolved placeholder/empty credential.
|
| 483 |
+
elif not env_key and (
|
| 484 |
+
not current_key
|
| 485 |
+
or current_key.startswith("${")
|
| 486 |
+
or current_key.startswith("__")
|
| 487 |
+
):
|
| 488 |
del restored_providers[pid]
|
| 489 |
+
print(
|
| 490 |
+
f"[SYNC] Removed provider '{pid}' "
|
| 491 |
+
f"(no API key available in {env_name})"
|
| 492 |
+
)
|
| 493 |
|
| 494 |
if OPENCLAW_DEFAULT_MODEL:
|
| 495 |
data["agents"]["defaults"]["model"]["primary"] = OPENCLAW_DEFAULT_MODEL
|
|
|
|
| 640 |
print(f"[SYNC] ββ Periodic sync triggered at {datetime.now().isoformat()} ββ")
|
| 641 |
self.save_to_repo()
|
| 642 |
|
| 643 |
+
# ββ OpenClaw migration / repair βββββββββββββββββββββββββββββββββββ
|
| 644 |
+
|
| 645 |
+
def run_doctor_fix(self):
|
| 646 |
+
"""Run OpenClaw doctor --fix before starting the gateway.
|
| 647 |
+
|
| 648 |
+
OpenClaw may refuse to start when its SQLite state database needs a
|
| 649 |
+
schema migration. Running doctor outside the gateway process lets
|
| 650 |
+
OpenClaw perform the required migration before startup.
|
| 651 |
+
"""
|
| 652 |
+
entry_js = Path(APP_DIR) / "dist" / "entry.js"
|
| 653 |
+
openclaw_mjs = Path(APP_DIR) / "openclaw.mjs"
|
| 654 |
+
|
| 655 |
+
if entry_js.exists():
|
| 656 |
+
doctor_cmd = ["node", "dist/entry.js", "doctor", "--fix"]
|
| 657 |
+
elif openclaw_mjs.exists():
|
| 658 |
+
doctor_cmd = ["node", "openclaw.mjs", "doctor", "--fix"]
|
| 659 |
+
else:
|
| 660 |
+
print(f"[SYNC] ERROR: No OpenClaw entry point found in {APP_DIR}")
|
| 661 |
+
return False
|
| 662 |
+
|
| 663 |
+
print(f"[SYNC] Running OpenClaw migration: {' '.join(doctor_cmd)}")
|
| 664 |
+
print(f"[SYNC] Working directory: {APP_DIR}")
|
| 665 |
+
|
| 666 |
+
try:
|
| 667 |
+
result = subprocess.run(
|
| 668 |
+
doctor_cmd,
|
| 669 |
+
cwd=str(APP_DIR),
|
| 670 |
+
env=os.environ.copy(),
|
| 671 |
+
text=True,
|
| 672 |
+
capture_output=True,
|
| 673 |
+
timeout=300,
|
| 674 |
+
)
|
| 675 |
+
|
| 676 |
+
if result.stdout:
|
| 677 |
+
print(result.stdout, end="" if result.stdout.endswith("\n") else "\n")
|
| 678 |
+
if result.stderr:
|
| 679 |
+
print(result.stderr, end="" if result.stderr.endswith("\n") else "\n")
|
| 680 |
+
|
| 681 |
+
if result.returncode != 0:
|
| 682 |
+
print(
|
| 683 |
+
f"[SYNC] β openclaw doctor --fix failed "
|
| 684 |
+
f"with exit code {result.returncode}"
|
| 685 |
+
)
|
| 686 |
+
return False
|
| 687 |
+
|
| 688 |
+
print("[SYNC] β OpenClaw doctor --fix completed")
|
| 689 |
+
return True
|
| 690 |
+
|
| 691 |
+
except subprocess.TimeoutExpired:
|
| 692 |
+
print("[SYNC] β openclaw doctor --fix timed out after 300 seconds")
|
| 693 |
+
return False
|
| 694 |
+
|
| 695 |
+
except Exception as e:
|
| 696 |
+
print(f"[SYNC] β Failed to run doctor --fix: {e}")
|
| 697 |
+
traceback.print_exc()
|
| 698 |
+
return False
|
| 699 |
+
|
| 700 |
# ββ Application runner βββββββββββββββββββββββββββββββββββββββββββββ
|
| 701 |
|
| 702 |
def run_openclaw(self):
|
|
|
|
| 817 |
sync.load_from_repo()
|
| 818 |
print(f"[TIMER] load_from_repo (restore): {time.time() - t0:.1f}s")
|
| 819 |
|
| 820 |
+
# 1.5 Run OpenClaw database/config migrations BEFORE background sync
|
| 821 |
+
# and BEFORE starting the gateway. This prevents the gateway from
|
| 822 |
+
# exiting with code 78 when a state DB schema migration is required.
|
| 823 |
+
t0 = time.time()
|
| 824 |
+
doctor_ok = sync.run_doctor_fix()
|
| 825 |
+
print(f"[TIMER] doctor --fix: {time.time() - t0:.1f}s")
|
| 826 |
+
|
| 827 |
+
if not doctor_ok:
|
| 828 |
+
print("[SYNC] ERROR: OpenClaw doctor --fix failed. Refusing to start gateway.")
|
| 829 |
+
sys.exit(78)
|
| 830 |
+
|
| 831 |
+
# Doctor can migrate/rewrite OpenClaw config. Re-apply the HuggingClaw
|
| 832 |
+
# Space-specific settings and provider environment keys afterward.
|
| 833 |
+
sync._patch_config()
|
| 834 |
+
|
| 835 |
# 2. Background sync
|
| 836 |
stop_event = threading.Event()
|
| 837 |
+
t = threading.Thread(
|
| 838 |
+
target=sync.background_sync_loop,
|
| 839 |
+
args=(stop_event,),
|
| 840 |
+
daemon=True
|
| 841 |
+
)
|
| 842 |
t.start()
|
| 843 |
|
| 844 |
# 3. Start application
|
|
|
|
| 937 |
|
| 938 |
if __name__ == "__main__":
|
| 939 |
main()
|
| 940 |
+
```
|