Powerman9x commited on
Commit
31d3e49
Β·
verified Β·
1 Parent(s): 1cfac89

Update scripts/sync_hf.py

Browse files
Files changed (1) hide show
  1. 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 cleanup ─────────────────────────────────────────────
451
- # Don't manage providers β€” let OpenClaw handle them natively from
452
- # its own env var support. We only clean up stale providers that
453
- # were restored from the dataset backup with hardcoded API keys
454
- # that no longer exist in the current environment.
455
- restored_providers = data.get("models", {}).get("providers", {})
456
- if restored_providers:
457
- stale = []
458
- for pid, pcfg in list(restored_providers.items()):
459
- api_key = pcfg.get("apiKey", "")
460
- # Skip placeholder-style keys (${VAR}) β€” OpenClaw resolves these
461
- if not api_key or str(api_key).startswith("${"):
462
- continue
463
- # Hardcoded key from backup β€” check if it's still in env
464
- # We can't know which env var it came from, so check if
465
- # the exact key value exists in any current env var
466
- if api_key not in os.environ.values():
467
- stale.append(pid)
468
- for pid in stale:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  del restored_providers[pid]
470
- print(f"[SYNC] Removed stale provider '{pid}' (API key no longer in environment)")
 
 
 
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(target=sync.background_sync_loop, args=(stop_event,), daemon=True)
 
 
 
 
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
+ ```