| #!/usr/bin/env bash |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| set -euo pipefail |
|
|
| SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" |
| REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)" |
|
|
| |
| if command -v uv &>/dev/null && [[ -f "$REPO_ROOT/pyproject.toml" ]]; then |
| PYTHON="uv run python3" |
| else |
| PYTHON="python3" |
| fi |
|
|
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' |
| CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' |
|
|
| info() { printf "${GREEN}%s${NC}\n" "$*"; } |
| warn() { printf "${YELLOW}%s${NC}\n" "$*" >&2; } |
| error() { printf "${RED}%s${NC}\n" "$*" >&2; } |
| title() { printf "\n${CYAN}${BOLD}%s${NC}\n" "$*"; } |
|
|
| |
| |
| |
| cmd_bootstrap() { |
| info "▶ 启动交互式部署引导..." |
| exec "$SCRIPT_DIR/bootstrap-hf.sh" |
| } |
|
|
| cmd_rebuild() { |
| if [ $# -gt 0 ]; then |
| exec "$SCRIPT_DIR/rebuild-space.sh" "$@" |
| fi |
| read -r -p "Space ID (如 GGSheng/page): " repo |
| read -r -p "HF_TOKEN (留空从文件读取): " token |
| exec "$SCRIPT_DIR/rebuild-space.sh" "${repo}" ${token:+"$token"} |
| } |
|
|
| cmd_cleanup() { |
| if [ $# -gt 1 ]; then |
| exec "$SCRIPT_DIR/delete-backups.sh" "$@" |
| fi |
| read -r -p "Dataset ID (如 GGSheng/page-backup): " repo |
| read -r -p "删除此日期及更早的备份 (YYYYMMDD): " date |
| read -r -p "HF_TOKEN (留空从文件读取): " token |
| exec "$SCRIPT_DIR/delete-backups.sh" "${repo}" "${date}" ${token:+"$token"} |
| } |
|
|
| cmd_find_backup() { |
| if [ $# -gt 0 ]; then |
| exec $PYTHON "$SCRIPT_DIR/find-largest-backup.py" "$@" |
| fi |
| read -r -p "Dataset ID (如 GGSheng/page-backup): " repo |
| shift_args=() |
| read -r -p "详细模式? (y/n, 默认 n): " verbose |
| [[ "$verbose" == "y" ]] && shift_args+=("--verbose") |
| read -r -p "找到最佳备份后清理旧备份? (y/n, 默认 n): " del |
| [[ "$del" == "y" ]] && shift_args+=("--delete-before") |
| exec $PYTHON "$SCRIPT_DIR/find-largest-backup.py" "$repo" "${shift_args[@]}" |
| } |
|
|
| cmd_delete_hf() { |
| if [ $# -gt 0 ]; then |
| exec $PYTHON "$SCRIPT_DIR/delete-hf.py" "$@" |
| fi |
| echo "删除类型:" |
| echo " 1) 删除整个仓库 (Space/Dataset/Model)" |
| echo " 2) 按通配符批量删除文件" |
| echo " 3) 删除 Space 持久存储" |
| read -r -p "请选择 [1-3]: " t |
| case "$t" in |
| 1) read -r -p "Repo ID: " rid; read -r -p "类型 (space/dataset/model): " rtype |
| exec $PYTHON "$SCRIPT_DIR/delete-hf.py" repo "$rid" --type "$rtype" ;; |
| 2) read -r -p "Repo ID: " rid; read -r -p "通配符模式: " pat |
| exec $PYTHON "$SCRIPT_DIR/delete-hf.py" files "$rid" --pattern "$pat" ;; |
| 3) read -r -p "Space ID: " sid |
| exec $PYTHON "$SCRIPT_DIR/delete-hf.py" storage "$sid" ;; |
| esac |
| } |
|
|
| cmd_restart_space() { |
| local repo="$1" |
| if [ -z "$repo" ]; then |
| read -r -p "Space ID (如 GGSheng/page): " repo |
| fi |
| info "▶ 重启 Space: $repo" |
| if hf spaces restart "$repo" 2>&1; then |
| info "✓ 重启请求已发送" |
| else |
| error "✗ 重启失败" |
| return 1 |
| fi |
| } |
|
|
| cmd_pause_space() { |
| local repo="$1" |
| if [ -z "$repo" ]; then |
| read -r -p "Space ID (如 GGSheng/page): " repo |
| fi |
| warn "▶ 暂停 Space: $repo" |
| warn "警告: Space 暂停后无法通过网络访问,需手动重启恢复" |
| read -r -p "确认暂停? (y/n): " confirm |
| [[ "$confirm" != "y" ]] && info "已取消" && return 0 |
| if hf spaces pause "$repo" 2>&1; then |
| info "✓ Space 已暂停" |
| else |
| error "✗ 暂停失败" |
| return 1 |
| fi |
| } |
|
|
| cmd_factory_rebuild() { |
| local repo="$1" |
| if [ -z "$repo" ]; then |
| read -r -p "Space ID (如 GGSheng/page): " repo |
| fi |
| warn "▶ Factory 重建 Space: $repo" |
| warn "警告: 这将删除 Space 后重新创建!所有持久存储数据将被清除!" |
| read -r -p "确认重建? (输入 Space 名称确认): " confirm |
| [[ "$confirm" != "${repo#*/}" ]] && error "名称不匹配,已取消" && return 1 |
|
|
| info "1/3 删除 Space..." |
| $PYTHON -c " |
| from huggingface_hub import HfApi |
| import os |
| api = HfApi() |
| try: |
| api.delete_repo('${repo}', repo_type='space') |
| print('✓ 已删除') |
| except Exception as e: |
| print(f'删除失败(可能不存在): {e}') |
| " |
| sleep 3 |
| info "2/3 重新创建 Space..." |
| hf repos create "$repo" --repo-type space --space-sdk docker --private |
| info "3/3 上传代码..." |
| exec "$SCRIPT_DIR/rebuild-space.sh" "$repo" |
| } |
|
|
| cmd_storage() { |
| if [ $# -gt 0 ]; then |
| exec "$SCRIPT_DIR/hf-storage.sh" "$@" |
| fi |
| echo "存储操作:" |
| echo " 1) 上传文件" |
| echo " 2) 上传目录" |
| echo " 3) 下载文件" |
| echo " 4) 列出文件" |
| echo " 5) 同步目录" |
| read -r -p "请选择 [1-5]: " op |
| case "$op" in |
| 1) read -r -p "文件路径: " f; read -r -p "目标路径 (留空同上): " d |
| exec "$SCRIPT_DIR/hf-storage.sh" upload "$f" ${d:+"$d"} ;; |
| 2) read -r -p "目录路径: " d; read -r -p "前缀 (留空无前缀): " p |
| exec "$SCRIPT_DIR/hf-storage.sh" upload-dir "$d" ${p:+"$p"} ;; |
| 3) read -r -p "远程文件: " f; read -r -p "本地路径 (留空当前目录): " l |
| exec "$SCRIPT_DIR/hf-storage.sh" download "$f" ${l:+"$l"} ;; |
| 4) exec "$SCRIPT_DIR/hf-storage.sh" list ;; |
| 5) read -r -p "本地目录: " l; read -r -p "远程前缀 (留空无): " r |
| exec "$SCRIPT_DIR/hf-storage.sh" sync "$l" ${r:+"$r"} ;; |
| esac |
| } |
|
|
| show_menu() { |
| clear 2>/dev/null || true |
| title "╔══════════════════════════════════════╗" |
| title "║ OpenClaw 工具箱 ║" |
| title "║ 本地开发工具集 ║" |
| title "╚══════════════════════════════════════╝" |
| echo "" |
| info " 1) 🚀 部署到 HF Space" |
| echo " 交互式全流程部署 OpenClaw 到 Hugging Face Space" |
| echo "" |
| info " 2) 📤 推送代码到 Space" |
| echo " 将本地最新代码强制推送到 Space 并触发重建" |
| echo "" |
| info " 3) 🗑️ 清理备份" |
| echo " 删除 Dataset 中指定日期及更早的所有备份文件" |
| echo "" |
| info " 4) 🔍 查找最佳备份" |
| echo " 综合评分找出最佳备份,可选清理旧备份" |
| echo "" |
| info " 5) ❌ 删除 HF 资源" |
| echo " 删除仓库、批量删除文件、或删除 Space 持久存储" |
| echo "" |
| info " 6) 📦 存储管理" |
| echo " HuggingFace 存储工具:上传/下载/同步文件" |
| echo "" |
| info " ── Space 运维 ──" |
| echo "" |
| info " 7) 🔄 重启 Space" |
| echo " 发送重启请求到 HuggingFace Space" |
| echo "" |
| info " 8) ⏸️ 暂停 Space" |
| echo " 暂停 Space,暂停后无法网络访问" |
| echo "" |
| info " 9) 🏭 Factory 重建" |
| echo " 删除 Space 后重新创建并上传代码(清除所有数据和持久存储)" |
| echo "" |
| info " 0) ❎ 退出" |
| echo "" |
| } |
|
|
| |
| |
| |
| interactive_mode() { |
| while true; do |
| show_menu |
| read -r -p "请选择 [0-9]: " choice |
| echo "" |
| case "$choice" in |
| 1) cmd_bootstrap ;; |
| 2) cmd_rebuild ;; |
| 3) cmd_cleanup ;; |
| 4) cmd_find_backup ;; |
| 5) cmd_delete_hf ;; |
| 6) cmd_storage ;; |
| 7) cmd_restart_space ;; |
| 8) cmd_pause_space ;; |
| 9) cmd_factory_rebuild ;; |
| 0) info "再见!"; exit 0 ;; |
| *) warn "无效选择,请重新输入"; sleep 1 ;; |
| esac |
| done |
| } |
|
|
| show_usage() { |
| title "OpenClaw 工具箱 — 本地开发工具集" |
| echo "" |
| echo "用法: $0 [命令] [参数...]" |
| echo "" |
| echo "不带参数时启动交互式菜单。" |
| echo "" |
| info "命令模式:" |
| echo " $0 bootstrap 交互式部署到 HF Space" |
| echo " $0 rebuild-space <ID> [TOKEN] 推送代码到 Space" |
| echo " $0 restart-space <ID> 重启 Space" |
| echo " $0 pause-space <ID> 暂停 Space" |
| echo " $0 factory-rebuild <ID> Factory 重建 Space(删除重建)" |
| echo " $0 cleanup <ID> <日期> [TOKEN] 清理旧备份" |
| echo " $0 find-backup <ID> [选项] 查找最佳备份" |
| echo " $0 rm-hf <子命令> [参数] 删除 HF 资源" |
| echo " $0 storage <子命令> [参数] 存储管理" |
| echo "" |
| info "示例:" |
| echo " $0 # 交互菜单" |
| echo " $0 rebuild-space GGSheng/page # 推送代码" |
| echo " $0 restart-space GGSheng/page # 重启 Space" |
| echo " $0 cleanup GGSheng/page-backup 20260427 # 清理备份" |
| } |
|
|
| |
| |
| |
| main() { |
| if [ $# -eq 0 ]; then |
| interactive_mode |
| exit 0 |
| fi |
|
|
| local cmd="$1" |
| shift |
|
|
| case "$cmd" in |
| -h|--help|help) show_usage ;; |
| bootstrap) cmd_bootstrap "$@" ;; |
| rebuild-space|rebuild) cmd_rebuild "$@" ;; |
| restart-space) cmd_restart_space "$@" ;; |
| pause-space) cmd_pause_space "$@" ;; |
| factory-rebuild) cmd_factory_rebuild "$@" ;; |
| cleanup) cmd_cleanup "$@" ;; |
| find-backup|find) cmd_find_backup "$@" ;; |
| rm-hf|delete-hf) cmd_delete_hf "$@" ;; |
| storage) cmd_storage "$@" ;; |
| *) |
| error "未知命令: $cmd" |
| echo "使用 '$0 --help' 查看可用命令" |
| exit 1 |
| ;; |
| esac |
| } |
|
|
| main "$@" |
|
|