Skill: liuyue8135/yuebao-self-backup (version 1.0.1)

--- name: self-backup description: > Backup and restore OpenClaw agent configuration, skills, memory, and workspace files. Use when asked to "backup", "backup yourself", "create a restore point", "archive my agent", "export configuration", "save current state", or "setup auto-backup". Also handles restore: "restore from backup", "recover agent", "reinstall from backup". --- # Self Backup & Restore Backup and restore your OpenClaw agent's complete state, including: configuration, skills, memory, and workspace files. ## What Gets Backed Up | Category | Path | Priority | |----------|------|----------| | OpenClaw config | `~/.openclaw/openclaw.json` | ⭐⭐⭐ Required | | API credentials | `~/.openclaw/.env` | ⭐⭐⭐ Required | | Skills | `~/.openclaw/workspace/skills/` | ⭐⭐⭐ Required | | Memory files | `~/.openclaw/workspace/memory/` | ⭐⭐⭐ Required | | Identity files | `MEMORY.md` `SOUL.md` `IDENTITY.md` `USER.md` | ⭐⭐⭐ Required | | Config files | `AGENTS.md` `TOOLS.md` `HEARTBEAT.md` | ⭐⭐ Important | | Cron jobs | `~/.openclaw/cron/jobs.json` | ⭐⭐ Important | | Credentials | `~/.openclaw/credentials/` | ⭐⭐ Important | ## What's NOT Backed Up - `~/.openclaw/logs/` — logs, not needed - `~/.openclaw/media/` — media cache, too large - `~/.openclaw/workspace/.venv-*/` — Python envs, can be rebuilt - `~/.openclaw/completions/` — history cache --- ## Run Backup ```bash python3 ~/.openclaw/workspace/skills/self-backup/scripts/backup.py ``` Output: `~/backups/openclaw-backup-YYYY-MM-DD_HHMM.tar.gz` ## Run Restore ```bash python3 ~/.openclaw/workspace/skills/self-backup/scripts/restore.py ~/backups/openclaw-backup-YYYY-MM-DD_HHMM.tar.gz ``` ## List Backups ```bash python3 ~/.openclaw/workspace/skills/self-backup/scripts/backup.py --list ``` ## Setup Weekly Auto-Backup (via OpenClaw cron) ```bash openclaw cron add \ --name "weekly-self-backup" \ --cron "0 2 * * 0" \ --tz "Asia/Shanghai" \ --message "Run backup: python3 ~/.openclaw/workspace/skills/self-backup/scripts/backup.py and report result" \ --session isolated \ --announce \ --channel telegram ``` --- ## After Restore: Rebuild Steps 1. **Reinstall OpenClaw** (fresh machine only) ```bash npm install -g openclaw ``` 2. **Restore backup** ```bash python3 restore.py openclaw-backup-YYYY-MM-DD.tar.gz ``` 3. **Rebuild Python environment** (not included in backup) ```bash cd ~/.openclaw/workspace python3 -m venv .venv-stock source .venv-stock/bin/activate pip install yfinance pandas numpy pandas-ta ta ddgs tavily-python requests beautifulsoup4 ``` 4. **Re-login ClawHub** ```bash clawhub auth login --token <YOUR_TOKEN> --no-browser ``` 5. **Verify** ```bash openclaw status && openclaw doctor ```

path: scripts/backup.py
content: #!/usr/bin/env python3
"""
yue宝 自备份脚本
备份 OpenClaw 配置、技能、记忆到本地压缩包
"""

import os
import sys
import tarfile
import json
import shutil
from datetime import datetime
from pathlib import Path

HOME = Path.home()
OPENCLAW_DIR = HOME / ".openclaw"
WORKSPACE_DIR = OPENCLAW_DIR / "workspace"
BACKUP_DIR = HOME / "backups"

# 要备份的路径（相对于HOME）
BACKUP_PATHS = [
    # 核心配置
    ".openclaw/openclaw.json",
    ".openclaw/.env",
    ".openclaw/cron/jobs.json",
    # 凭证（加密存储，备份后需妥善保管）
    ".openclaw/credentials",
    ".openclaw/identity",
    # 工作区核心文件
    ".openclaw/workspace/MEMORY.md",
    ".openclaw/workspace/SOUL.md",
    ".openclaw/workspace/IDENTITY.md",
    ".openclaw/workspace/USER.md",
    ".openclaw/workspace/AGENTS.md",
    ".openclaw/workspace/TOOLS.md",
    ".openclaw/workspace/HEARTBEAT.md",
    ".openclaw/workspace/BOOTSTRAP.md",
    # 技能
    ".openclaw/workspace/skills",
    # 记忆
    ".openclaw/workspace/memory",
    # 投资文档
    ".openclaw/workspace/investment_plan_FINAL_v6.md",
    ".openclaw/workspace/investment_plan_FINAL_v5.md",
]

# 排除的路径（太大或可重建）
EXCLUDE_PATTERNS = [
    ".venv-stock",
    "__pycache__",
    "*.pyc",
    ".git",
    "node_modules",
]

def should_exclude(path_str):
    for pattern in EXCLUDE_PATTERNS:
        if pattern.startswith("*."):
            ext = pattern[1:]
            if path_str.endswith(ext):
                return True
        elif pattern in path_str:
            return True
    return False

def create_backup():
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    date_str = datetime.now().strftime("%Y-%m-%d_%H%M")
    backup_name = f"openclaw-backup-{date_str}.tar.gz"
    backup_path = BACKUP_DIR / backup_name

    print(f"🗄️  开始备份 yue宝...")
    print(f"📦  备份文件: {backup_path}")
    print()

    included = []
    skipped = []

    with tarfile.open(backup_path, "w:gz") as tar:
        for rel_path in BACKUP_PATHS:
            full_path = HOME / rel_path
            if not full_path.exists():
                skipped.append(f"  ⚠️  不存在: {rel_path}")
                continue
            if should_exclude(str(full_path)):
                skipped.append(f"  ⏭️  已排除: {rel_path}")
                continue

            tar.add(full_path, arcname=rel_path,
                    filter=lambda ti: None if should_exclude(ti.name) else ti)
            included.append(f"  ✅  {rel_path}")

    # 写入备份清单
    manifest = {
        "backup_date": datetime.now().isoformat(),
        "backup_file": backup_name,
        "openclaw_version": "2026.3.13",
        "included_paths": [p.strip("  ✅  ") for p in included],
        "restore_instructions": "python3 restore.py <backup_file>",
        "python_env_rebuild": "pip install yfinance pandas numpy pandas-ta ta ddgs tavily-python requests beautifulsoup4",
    }
    manifest_path = BACKUP_DIR / f"manifest-{date_str}.json"
    with open(manifest_path, "w") as f:
        json.dump(manifest, f, indent=2, ensure_ascii=False)

    # 更新 latest 软链接
    latest_link = BACKUP_DIR / "openclaw-backup-latest.tar.gz"
    if latest_link.exists() or latest_link.is_symlink():
        latest_link.unlink()
    latest_link.symlink_to(backup_path)

    # 输出结果
    print("已备份:")
    for line in included:
        print(line)
    if skipped:
        print("\n跳过:")
        for line in skipped:
            print(line)

    size_mb = backup_path.stat().st_size / 1024 / 1024
    print(f"\n✅  备份完成！")
    print(f"   文件: {backup_path}")
    print(f"   大小: {size_mb:.1f} MB")
    print(f"   清单: {manifest_path}")

    # 保留最近6个备份，超出自动删除最早的
    backups = sorted(BACKUP_DIR.glob("openclaw-backup-*.tar.gz"),
                     key=lambda p: p.stat().st_mtime, reverse=True)
    backups = [b for b in backups if not b.name == "openclaw-backup-latest.tar.gz"]
    if len(backups) > 6:
        for old in backups[6:]:
            print(f"   🗑️   删除旧备份: {old.name}")
            old.unlink()

    return backup_path

def list_backups():
    if not BACKUP_DIR.exists():
        print("暂无备份")
        return
    backups = sorted(BACKUP_DIR.glob("openclaw-backup-*.tar.gz"),
                     key=lambda p: p.stat().st_mtime, reverse=True)
    backups = [b for b in backups if not b.name == "openclaw-backup-latest.tar.gz"]
    if not backups:
        print("暂无备份")
        return
    print(f"📦  找到 {len(backups)} 个备份:")
    for b in backups:
        size_mb = b.stat().st_size / 1024 / 1024
        mtime = datetime.fromtimestamp(b.stat().st_mtime).strftime("%Y-%m-%d %H:%M")
        print(f"  {mtime}  {b.name}  ({size_mb:.1f} MB)")

if __name__ == "__main__":
    if "--list" in sys.argv or "-l" in sys.argv:
        list_backups()
    else:
        create_backup()


path: scripts/restore.py
content: #!/usr/bin/env python3
"""
yue宝 恢复脚本
从备份包恢复 OpenClaw 配置、技能、记忆
"""

import os
import sys
import tarfile
import shutil
import json
from datetime import datetime
from pathlib import Path

HOME = Path.home()

def restore_backup(backup_file):
    backup_path = Path(backup_file)
    if not backup_path.exists():
        print(f"❌  备份文件不存在: {backup_file}")
        sys.exit(1)

    print(f"🔄  开始恢复 yue宝...")
    print(f"📦  备份文件: {backup_path}")
    print()

    # 先备份当前状态（如果存在）
    openclaw_dir = HOME / ".openclaw"
    if openclaw_dir.exists():
        pre_restore_backup = HOME / f"backups/pre-restore-{datetime.now().strftime('%Y%m%d_%H%M')}.tar.gz"
        pre_restore_backup.parent.mkdir(parents=True, exist_ok=True)
        print(f"⚠️  先备份当前状态到: {pre_restore_backup}")
        with tarfile.open(pre_restore_backup, "w:gz") as tar:
            tar.add(openclaw_dir, arcname=".openclaw",
                    filter=lambda ti: None if ".venv-stock" in ti.name or "__pycache__" in ti.name else ti)
        print(f"✅  当前状态已备份")
        print()

    # 解压备份
    print("解压备份文件...")
    with tarfile.open(backup_path, "r:gz") as tar:
        members = tar.getmembers()
        for member in members:
            target = HOME / member.name
            target.parent.mkdir(parents=True, exist_ok=True)
            print(f"  恢复: {member.name}")
        tar.extractall(HOME)

    print()
    print("✅  文件恢复完成！")
    print()
    print("📋  后续步骤:")
    print("   1. 重建 Python 环境（如果是新机器）:")
    print("      cd ~/.openclaw/workspace")
    print("      python3 -m venv .venv-stock")
    print("      source .venv-stock/bin/activate")
    print("      pip install yfinance pandas numpy pandas-ta ta ddgs tavily-python requests beautifulsoup4")
    print()
    print("   2. 重新登录 ClawHub（如果需要）:")
    print("      clawhub auth login --token <YOUR_TOKEN> --no-browser")
    print()
    print("   3. 启动 OpenClaw:")
    print("      openclaw start")
    print()
    print("   4. 验证状态:")
    print("      openclaw status")
    print("      openclaw doctor")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python3 restore.py <备份文件路径>")
        print("示例: python3 restore.py ~/backups/openclaw-backup-2026-03-19.tar.gz")
        sys.exit(1)
    restore_backup(sys.argv[1])


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)