| #!/usr/bin/env bash |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| if [[ -t 0 ]]; then |
| stty sane 2>/dev/null || true |
| fi |
|
|
| update_readme_title() { |
| local new_title="$1" |
| local readme_file="$REPO_ROOT/README.md" |
| local temp_file |
|
|
| if [[ ! -f "$readme_file" ]]; then |
| return 0 |
| fi |
|
|
| temp_file="$(mktemp)" |
| if sed "s/^title:.*/title: $new_title/" "$readme_file" > "$temp_file"; then |
| mv "$temp_file" "$readme_file" |
| else |
| rm -f "$temp_file" |
| fi |
| } |
|
|
| restore_readme_title() { |
| local original_title="$1" |
| local readme_file="$REPO_ROOT/README.md" |
| local temp_file |
|
|
| if [[ ! -f "$readme_file" ]]; then |
| return 0 |
| fi |
|
|
| temp_file="$(mktemp)" |
| if sed "s/^title:.*/title: $original_title/" "$readme_file" > "$temp_file"; then |
| mv "$temp_file" "$readme_file" |
| else |
| rm -f "$temp_file" |
| fi |
| } |
|
|
| prompt_yes_no() { |
| local prompt="$1" |
| local default="${2:-}" |
| local choice="" |
| local answer="" |
|
|
| while [[ -z "$answer" ]]; do |
| if [[ -t 0 ]]; then |
| if [[ -n "$default" ]]; then |
| read -r -p "$prompt (Y/n) [$default]: " choice || true |
| else |
| read -r -p "$prompt (y/N) [$default]: " choice || true |
| fi |
| else |
| choice="$default" |
| fi |
| choice="$(printf '%s' "$choice" | tr -d '\r\n' | tr '[:upper:]' '[:lower:]')" |
| if [[ -z "$choice" ]]; then |
| choice="$default" |
| fi |
| case "$choice" in |
| y|yes) answer="yes" ;; |
| n|no) answer="no" ;; |
| *) printf 'Please answer y or n.\n' >&2 ;; |
| esac |
| done |
| printf '%s' "$answer" |
| } |
|
|
| prompt_secret() { |
| local prompt="$1" |
| local value="" |
| local char="" |
|
|
| if [[ -t 0 && -t 1 ]]; then |
| printf '%s: ' "$prompt" >&2 |
| while IFS= read -r -s -n 1 char; do |
| if [[ -z "$char" ]]; then |
| break |
| fi |
| case "$char" in |
| $'\e') break ;; |
| '') break ;; |
| *) value+="$char"; printf '*' >&2 ;; |
| esac |
| done |
| printf '\n' >&2 |
| fi |
| printf '%s' "$value" |
| } |
|
|
| prompt_hf_account_switch() { |
| local current_username="" |
| local hf_whoami_output |
|
|
| hf_whoami_output="$(hf auth whoami 2>&1 || true)" |
| current_username="$(printf '%s\n' "$hf_whoami_output" | sed -nE 's/^[[:space:]]*user:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n 1)" |
| if [[ -z "$current_username" ]]; then |
| current_username="$(printf '%s\n' "$hf_whoami_output" | sed -nE 's/.*[Ll]ogged in as[[:space:]]+([^[:space:]]+).*/\1/p' | head -n 1)" |
| fi |
|
|
| if [[ -n "$current_username" ]] && [[ -t 0 ]]; then |
| local use_current |
| use_current="$(prompt_yes_no "HF CLI is logged in as '$current_username'. Use this user?" "y")" |
| if [[ "$use_current" == "yes" ]]; then |
| return 0 |
| fi |
| fi |
|
|
| while true; do |
| if [[ -t 0 ]]; then |
| local token |
| token="$(prompt_secret "Enter HF_TOKEN to switch account")" |
| if [[ -z "$token" ]]; then |
| printf '[WARN] Empty token, please try again or press Ctrl+C to cancel\n' >&2 |
| continue |
| fi |
| local login_output |
| login_output="$(hf auth login --token "$token" 2>&1)" |
| if [[ $? -eq 0 ]]; then |
| local verify_output |
| verify_output="$(hf auth whoami 2>&1 || true)" |
| local new_username |
| new_username="$(printf '%s\n' "$verify_output" | sed -nE 's/^[[:space:]]*user:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n 1)" |
| if [[ -z "$new_username" ]]; then |
| new_username="$(printf '%s\n' "$verify_output" | sed -nE 's/.*[Ll]ogged in as[[:space:]]+([^[:space:]]+).*/\1/p' | head -n 1)" |
| fi |
| if [[ -n "$current_username" ]] && [[ -n "$new_username" ]] && [[ "$current_username" != "$new_username" ]]; then |
| printf '[INFO] HF account switched: %s -> %s\n' "$current_username" "$new_username" |
| elif [[ -n "$new_username" ]]; then |
| printf '[INFO] HF login successful as: %s\n' "$new_username" |
| else |
| printf '[INFO] HF login successful\n' |
| fi |
| return 0 |
| else |
| printf '[ERROR] HF login failed: %s\n' "$login_output" >&2 |
| continue |
| fi |
| else |
| return 1 |
| fi |
| done |
| } |
|
|
| SPACE_REPO_ID="${1:-${SPACE_REPO_ID:-}}" |
| HF_TOKEN="${2:-${HF_TOKEN:-}}" |
|
|
| if [[ -t 0 ]]; then |
| prompt_hf_account_switch || true |
| fi |
|
|
| if [[ -z "$SPACE_REPO_ID" ]]; then |
| echo "Usage: $0 <SPACE_REPO_ID> [HF_TOKEN]" |
| echo "" |
| echo "Examples:" |
| echo " $0 <user>/<space> hf_xxxxx" |
| echo " SPACE_REPO_ID=<user>/<space> HF_TOKEN=hf_xxxxx $0" |
| echo " $0 <user>/<space>" |
| echo "" |
| echo "Environment variables:" |
| echo " SPACE_REPO_ID - Space repo ID (例如 <user>/<space>)" |
| echo " HF_TOKEN - Hugging Face API Token" |
| echo " HF_TOKEN_FILE - Token file path (default: ~/.cache/huggingface/token)" |
| exit 1 |
| fi |
|
|
| if [[ -z "$HF_TOKEN" ]]; then |
| HF_TOKEN_FILE="${HF_TOKEN_FILE:-$HOME/.cache/huggingface/token}" |
| if [[ -f "$HF_TOKEN_FILE" ]]; then |
| HF_TOKEN="$(cat "$HF_TOKEN_FILE")" |
| fi |
| fi |
|
|
| if [[ -z "$HF_TOKEN" ]]; then |
| echo "Error: HF_TOKEN is required. Provide as 2nd arg, set HF_TOKEN env var, or ensure ~/.cache/huggingface/token exists." |
| exit 1 |
| fi |
|
|
| SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" |
| REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)" |
|
|
| SPACE_NAME="${SPACE_REPO_ID##*/}" |
|
|
| echo "============================================" |
| echo "OpenClaw HF Space Rebuild Script" |
| echo "============================================" |
| echo "Space: $SPACE_REPO_ID" |
| echo "Repo: $REPO_ROOT" |
| echo "" |
|
|
| cd "$REPO_ROOT" |
|
|
| echo "[1/5] Logging into Hugging Face..." |
| echo "$HF_TOKEN" | hf auth login --token "$HF_TOKEN" |
|
|
| echo "" |
| echo "[2/5] Saving original README title..." |
| original_readme_title="$(sed -n 's/^title: *//p' "$REPO_ROOT/README.md" | head -n 1)" |
| update_readme_title "$SPACE_NAME" |
|
|
| restore_readme_on_exit() { |
| restore_readme_title "${original_readme_title:-HF Space}" |
| } |
| trap restore_readme_on_exit EXIT |
|
|
| echo "" |
| echo "[3/5] Ensuring Space exists..." |
| hf repos create "$SPACE_REPO_ID" --repo-type space --space-sdk docker --exist-ok |
|
|
| echo "" |
| echo "[4/5] Uploading files to Space..." |
| upload_tmp=$(mktemp) |
| hf upload "$SPACE_REPO_ID" . --repo-type space \ |
| --exclude '.git/**' \ |
| --exclude '.git' \ |
| --exclude '.github/**' \ |
| --exclude 'logs/**' \ |
| --exclude '*.log' \ |
| --exclude 'docs/**' \ |
| --exclude 'tests/**' \ |
| --exclude 'LICENSE' \ |
| --exclude '.dockerignore' \ |
| --exclude '.python-version' \ |
| --commit-message "fix: improve SSH service stability and backup.py error handling" 2>&1 | tee "$upload_tmp" |
| upload_exit_code=${PIPESTATUS[0]} |
|
|
| if [ $upload_exit_code -ne 0 ]; then |
| echo "Error: Upload failed:" |
| cat "$upload_tmp" |
| rm -f "$upload_tmp" |
| exit 1 |
| fi |
| rm -f "$upload_tmp" |
|
|
| echo "" |
| echo "[5/5] Requesting Space restart..." |
| restart_tmp=$(mktemp) |
| hf spaces restart "$SPACE_REPO_ID" 2>&1 | tee "$restart_tmp" |
| restart_exit_code=${PIPESTATUS[0]} |
|
|
| if [ $restart_exit_code -ne 0 ]; then |
| echo "Warning: Restart request failed:" |
| cat "$restart_tmp" |
| fi |
| rm -f "$restart_tmp" |
|
|
| echo "" |
| echo "============================================" |
| echo "Done! Space updated: https://huggingface.co/spaces/$SPACE_REPO_ID" |
| echo "============================================" |
|
|