Spaces:
Runtime error
Runtime error
Upload sync_hf.py
Browse files- scripts/sync_hf.py +118 -0
scripts/sync_hf.py
CHANGED
|
@@ -85,6 +85,7 @@ HF_TOKEN = os.environ.get("HF_TOKEN")
|
|
| 85 |
OPENCLAW_HOME = Path.home() / ".openclaw"
|
| 86 |
APP_DIR = Path(os.environ.get("OPENCLAW_APP_DIR", "/app/openclaw"))
|
| 87 |
DATASET_PATH = ".openclaw"
|
|
|
|
| 88 |
|
| 89 |
# Z.AI API key (optional)
|
| 90 |
ZAI_API_KEY = os.environ.get("ZAI_API_KEY", "")
|
|
@@ -584,6 +585,7 @@ class OpenClawFullSync:
|
|
| 584 |
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 585 |
shutil.copy2(str(item), str(dest))
|
| 586 |
print("[SYNC] β Restore completed.")
|
|
|
|
| 587 |
else:
|
| 588 |
print("[SYNC] Downloaded snapshot but dir not found. Starting fresh.")
|
| 589 |
|
|
@@ -715,12 +717,128 @@ class OpenClawFullSync:
|
|
| 715 |
except Exception:
|
| 716 |
pass
|
| 717 |
|
|
|
|
|
|
|
| 718 |
except Exception as e:
|
| 719 |
print(f"[SYNC] β Upload failed: {str(e)[:500]}")
|
| 720 |
if "429" in str(e) or "rate limit" in str(e).lower():
|
| 721 |
self._cooldown_until = time.time() + 65 * 60
|
| 722 |
print("[SYNC] Hub rate limit hit β pausing uploads for 65 min")
|
| 723 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
# ββ Pre-gateway legacy cleanup (CRITICAL) βββββββββββββββββββββββββ
|
| 725 |
|
| 726 |
def _retire_workspace_markers(self):
|
|
|
|
| 85 |
OPENCLAW_HOME = Path.home() / ".openclaw"
|
| 86 |
APP_DIR = Path(os.environ.get("OPENCLAW_APP_DIR", "/app/openclaw"))
|
| 87 |
DATASET_PATH = ".openclaw"
|
| 88 |
+
SQLITE_SNAPSHOT_PATH = "sqlite-snapshots"
|
| 89 |
|
| 90 |
# Z.AI API key (optional)
|
| 91 |
ZAI_API_KEY = os.environ.get("ZAI_API_KEY", "")
|
|
|
|
| 585 |
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 586 |
shutil.copy2(str(item), str(dest))
|
| 587 |
print("[SYNC] β Restore completed.")
|
| 588 |
+
self._restore_sqlite_snapshots()
|
| 589 |
else:
|
| 590 |
print("[SYNC] Downloaded snapshot but dir not found. Starting fresh.")
|
| 591 |
|
|
|
|
| 717 |
except Exception:
|
| 718 |
pass
|
| 719 |
|
| 720 |
+
self._upload_sqlite_snapshots()
|
| 721 |
+
|
| 722 |
except Exception as e:
|
| 723 |
print(f"[SYNC] β Upload failed: {str(e)[:500]}")
|
| 724 |
if "429" in str(e) or "rate limit" in str(e).lower():
|
| 725 |
self._cooldown_until = time.time() + 65 * 60
|
| 726 |
print("[SYNC] Hub rate limit hit β pausing uploads for 65 min")
|
| 727 |
|
| 728 |
+
# ββ SQLite snapshots (live .sqlite files are never uploaded directly) ββ
|
| 729 |
+
|
| 730 |
+
def _iter_persistable_sqlites(self):
|
| 731 |
+
for root, dirs, files in os.walk(OPENCLAW_HOME):
|
| 732 |
+
for fn in files:
|
| 733 |
+
if not fn.endswith(".sqlite"):
|
| 734 |
+
continue
|
| 735 |
+
fp = Path(os.path.join(root, fn))
|
| 736 |
+
try:
|
| 737 |
+
rel = fp.relative_to(OPENCLAW_HOME)
|
| 738 |
+
except ValueError:
|
| 739 |
+
continue
|
| 740 |
+
if rel.as_posix().startswith("state/"):
|
| 741 |
+
continue
|
| 742 |
+
yield rel, fp
|
| 743 |
+
|
| 744 |
+
def _stage_sqlite_snapshots(self, staging_dir):
|
| 745 |
+
staged = []
|
| 746 |
+
for rel, fp in self._iter_persistable_sqlites():
|
| 747 |
+
try:
|
| 748 |
+
if fp.stat().st_size == 0:
|
| 749 |
+
continue
|
| 750 |
+
dest = Path(staging_dir) / rel
|
| 751 |
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 752 |
+
src = sqlite3.connect(str(fp))
|
| 753 |
+
dst = sqlite3.connect(str(dest))
|
| 754 |
+
try:
|
| 755 |
+
src.backup(dst)
|
| 756 |
+
finally:
|
| 757 |
+
dst.close()
|
| 758 |
+
src.close()
|
| 759 |
+
staged.append(rel)
|
| 760 |
+
except Exception as e:
|
| 761 |
+
print(f"[SYNC] sqlite snapshot skipped for {rel}: {str(e)[:120]}")
|
| 762 |
+
return staged
|
| 763 |
+
|
| 764 |
+
def _upload_sqlite_snapshots(self):
|
| 765 |
+
staging = tempfile.mkdtemp(prefix="oc-sqlite-snap-")
|
| 766 |
+
try:
|
| 767 |
+
staged = self._stage_sqlite_snapshots(staging)
|
| 768 |
+
if not staged:
|
| 769 |
+
print("[SYNC] No sqlite snapshots to upload")
|
| 770 |
+
return
|
| 771 |
+
print(f"[SYNC] βΆ Uploading {len(staged)} sqlite snapshot(s) β "
|
| 772 |
+
f"{SQLITE_SNAPSHOT_PATH}/ ...")
|
| 773 |
+
self.api.upload_folder(
|
| 774 |
+
folder_path=staging,
|
| 775 |
+
path_in_repo=SQLITE_SNAPSHOT_PATH,
|
| 776 |
+
repo_id=HF_REPO_ID,
|
| 777 |
+
repo_type="dataset",
|
| 778 |
+
token=HF_TOKEN,
|
| 779 |
+
commit_message=f"Sync sqlite snapshots β {datetime.now().isoformat()}",
|
| 780 |
+
)
|
| 781 |
+
print("[SYNC] β Sqlite snapshots uploaded")
|
| 782 |
+
except Exception as e:
|
| 783 |
+
print(f"[SYNC] β Sqlite snapshot upload failed: {str(e)[:300]}")
|
| 784 |
+
if "429" in str(e) or "rate limit" in str(e).lower():
|
| 785 |
+
self._cooldown_until = time.time() + 65 * 60
|
| 786 |
+
print("[SYNC] Hub rate limit hit β pausing uploads for 65 min")
|
| 787 |
+
finally:
|
| 788 |
+
shutil.rmtree(staging, ignore_errors=True)
|
| 789 |
+
|
| 790 |
+
def _restore_sqlite_snapshots(self):
|
| 791 |
+
try:
|
| 792 |
+
files = self.api.list_repo_files(repo_id=HF_REPO_ID, repo_type="dataset")
|
| 793 |
+
snap_files = [f for f in files if f.startswith(f"{SQLITE_SNAPSHOT_PATH}/")]
|
| 794 |
+
if not snap_files:
|
| 795 |
+
return
|
| 796 |
+
print(f"[SYNC] Found {len(snap_files)} sqlite snapshot(s) in dataset")
|
| 797 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 798 |
+
snapshot_download(
|
| 799 |
+
repo_id=HF_REPO_ID,
|
| 800 |
+
repo_type="dataset",
|
| 801 |
+
allow_patterns=f"{SQLITE_SNAPSHOT_PATH}/**",
|
| 802 |
+
local_dir=tmpdir,
|
| 803 |
+
token=HF_TOKEN,
|
| 804 |
+
)
|
| 805 |
+
snap_root = Path(tmpdir) / SQLITE_SNAPSHOT_PATH
|
| 806 |
+
if not snap_root.exists():
|
| 807 |
+
return
|
| 808 |
+
restored = 0
|
| 809 |
+
for item in snap_root.rglob("*"):
|
| 810 |
+
if not item.is_file():
|
| 811 |
+
continue
|
| 812 |
+
rel = item.relative_to(snap_root)
|
| 813 |
+
dest = OPENCLAW_HOME / rel
|
| 814 |
+
try:
|
| 815 |
+
conn = sqlite3.connect(str(item))
|
| 816 |
+
try:
|
| 817 |
+
ok = conn.execute("PRAGMA quick_check").fetchone()
|
| 818 |
+
finally:
|
| 819 |
+
conn.close()
|
| 820 |
+
except Exception as e:
|
| 821 |
+
print(f"[SYNC] Snapshot integrity check failed, skipped: "
|
| 822 |
+
f"{rel} ({str(e)[:80]})")
|
| 823 |
+
continue
|
| 824 |
+
if not ok or ok[0] != "ok":
|
| 825 |
+
print(f"[SYNC] Snapshot corrupt, skipped: {rel}")
|
| 826 |
+
continue
|
| 827 |
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 828 |
+
for suf in ("", "-wal", "-shm", "-journal"):
|
| 829 |
+
old = Path(str(dest) + suf)
|
| 830 |
+
if old.exists():
|
| 831 |
+
try:
|
| 832 |
+
old.unlink()
|
| 833 |
+
except OSError:
|
| 834 |
+
pass
|
| 835 |
+
shutil.copy2(str(item), str(dest))
|
| 836 |
+
restored += 1
|
| 837 |
+
if restored:
|
| 838 |
+
print(f"[SYNC] β Restored {restored} sqlite snapshot(s)")
|
| 839 |
+
except Exception as e:
|
| 840 |
+
print(f"[SYNC] sqlite snapshot restore failed: {str(e)[:300]}")
|
| 841 |
+
|
| 842 |
# ββ Pre-gateway legacy cleanup (CRITICAL) βββββββββββββββββββββββββ
|
| 843 |
|
| 844 |
def _retire_workspace_markers(self):
|