Download scripts/_hf_user.sh from SZFSPC/apple: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/SZFSPC/apple/resolve/main/scripts/_hf_user.sh
- Command line
-
hf download hf://spaces/SZFSPC/apple/scripts/_hf_user.sh
-
curl -L -o _hf_user.sh https://huggingface.co/spaces/SZFSPC/apple/resolve/main/scripts/_hf_user.sh
10.6 kB
| # ============================================================ | |
| # HF User helper - 提供 HF 账户切换和 token 获取的共享函数 | |
| # | |
| # 提供以下公共 API(参照 bootstrap-hf.sh 的实现): | |
| # - get_hf_username 获取当前 HF 用户名 | |
| # - read_current_hf_token 从 env / cache 文件读取 token | |
| # - login_with_hf_token 用 token 登录 HF | |
| # - prompt_hf_account_switch 检测/询问/切换 HF 账户 | |
| # - require_hf_token 必要时 prompt 后获取 token | |
| # - prompt_yes_no y/n 询问(非 TTY 不阻塞) | |
| # - prompt_line / prompt_required | |
| # - prompt_secret_optional / prompt_secret_required | |
| # - trim / info / warn / error / die | |
| # | |
| # 用法: | |
| # source "$SCRIPT_DIR/_hf_user.sh" | |
| # HF_TOKEN="$(require_hf_token)" || exit 1 | |
| # export HF_TOKEN | |
| # | |
| # 设计: | |
| # - 所有 prompt 函数在 stdin 不是 TTY 时使用默认值 / 返回错误, | |
| # 避免 c63f250 修复的"stdin 非 TTY 进入死循环"问题 | |
| # - login 成功后调用 hf auth whoami 验证,切换时输出 switched 信息 | |
| # - 不实现 RESTORE_HF_LOGIN_* 恢复机制(单命令工具不需要) | |
| # ============================================================ | |
| # ---- Fix terminal for interactive input ---- | |
| if [[ -t 0 ]]; then | |
| stty sane 2>/dev/null || true | |
| fi | |
| # ---- Ensure hf is in PATH (handles SSH_ASKPASS restricted env) ---- | |
| _HF_EXTRA_PATH="/root/.local/bin" | |
| if [[ ":$PATH:" != *":$_HF_EXTRA_PATH:"* ]]; then | |
| export PATH="$_HF_EXTRA_PATH:$PATH" | |
| fi | |
| # ---- 通用辅助函数 ---- | |
| trim() { | |
| printf '%s' "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | |
| } | |
| info() { | |
| printf '%s\n' "$*" | |
| } | |
| warn() { | |
| printf 'warning: %s\n' "$*" >&2 | |
| } | |
| error() { | |
| printf 'error: %s\n' "$*" >&2 | |
| } | |
| die() { | |
| error "$1" | |
| exit 1 | |
| } | |
| # ---- 输入提示 ---- | |
| prompt_line() { | |
| local prompt="$1" | |
| local default_value="${2:-}" | |
| local value="" | |
| if [[ -t 0 ]]; then | |
| if [[ -n "$default_value" ]]; then | |
| read -r -p "$prompt [$default_value]: " value || true | |
| else | |
| read -r -p "$prompt: " value || true | |
| fi | |
| value="$(printf '%s' "$value" | tr -d '\r\n')" | |
| if [[ -z "$value" ]]; then | |
| value="$default_value" | |
| fi | |
| else | |
| value="$default_value" | |
| fi | |
| printf '%s' "$value" | |
| } | |
| prompt_required() { | |
| local prompt="$1" | |
| local default_value="${2:-}" | |
| local value="" | |
| while [[ -z "$value" ]]; do | |
| value="$(prompt_line "$prompt" "$default_value")" | |
| if [[ -z "$value" ]]; then | |
| if [[ -t 0 ]]; then | |
| printf 'This value is required.\n' >&2 | |
| else | |
| # 非 TTY 模式无法补全,直接返回空(调用方应 die) | |
| printf '%s' "" | |
| return 1 | |
| fi | |
| fi | |
| done | |
| printf '%s' "$value" | |
| } | |
| prompt_secret_optional() { | |
| 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 | |
| $'\177'|$'\b') | |
| if [[ -n "$value" ]]; then | |
| value="${value%?}" | |
| printf '\b \b' >&2 | |
| fi | |
| ;; | |
| *) | |
| value+="$char" | |
| printf '*' >&2 | |
| ;; | |
| esac | |
| done | |
| printf '\n' >&2 | |
| else | |
| # 非 TTY:直接读一行(无回显),不阻塞 | |
| read -r -p "$prompt: " value || true | |
| fi | |
| printf '%s' "$(trim "${value:-}")" | |
| } | |
| prompt_secret_required() { | |
| local prompt="$1" | |
| local value="" | |
| while [[ -z "$value" ]]; do | |
| value="$(prompt_secret_optional "$prompt")" | |
| value="$(trim "$value")" | |
| if [[ -z "$value" ]]; then | |
| if [[ -t 0 ]]; then | |
| printf 'This value is required.\n' >&2 | |
| else | |
| # 非 TTY: 不阻塞,返回空 | |
| printf '%s' "" | |
| return 1 | |
| fi | |
| fi | |
| done | |
| printf '%s' "$value" | |
| } | |
| # y/n 询问 | |
| # $1: prompt 文本 | |
| # $2: 默认值 "y" 或 "n"(默认 n) | |
| # 输出: "yes" 或 "no" | |
| # 非 TTY 时直接使用默认值,不阻塞 | |
| prompt_yes_no() { | |
| local prompt="$1" | |
| local default_value="${2:-n}" | |
| local answer="" | |
| local hint="[y/N]" | |
| if [[ "$default_value" == "y" ]]; then | |
| hint="[Y/n]" | |
| fi | |
| if [[ -t 0 ]]; then | |
| while true; do | |
| read -r -p "$prompt $hint: " answer || true | |
| answer="$(printf '%s' "$answer" | tr -d '\r\n' | tr '[:upper:]' '[:lower:]')" | |
| if [[ -z "$answer" ]]; then | |
| answer="$default_value" | |
| fi | |
| case "$answer" in | |
| y|yes) | |
| printf 'yes' | |
| return 0 | |
| ;; | |
| n|no) | |
| printf 'no' | |
| return 0 | |
| ;; | |
| *) | |
| printf 'Please answer y or n.\n' >&2 | |
| ;; | |
| esac | |
| done | |
| else | |
| # 非 TTY:使用默认值,不阻塞 | |
| if [[ "$default_value" == "y" ]]; then | |
| printf 'yes' | |
| else | |
| printf 'no' | |
| fi | |
| fi | |
| } | |
| # ---- Token 处理 ---- | |
| # 解析 whoami 输出,提取 username | |
| # 接受两种格式: "user: <name>" 或 "Logged in as <name>" | |
| _parse_whoami_username() { | |
| local output="$1" | |
| local username | |
| username="$(printf '%s\n' "$output" | sed -nE 's/^[[:space:]]*user:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n 1)" | |
| if [[ -z "$username" ]]; then | |
| username="$(printf '%s\n' "$output" | sed -nE 's/.*[Ll]ogged in as[[:space:]]+([^[:space:]]+).*/\1/p' | head -n 1)" | |
| fi | |
| printf '%s' "$username" | |
| } | |
| # 读取当前可用的 token | |
| # 优先级: HUGGINGFACE_HUB_TOKEN > HF_TOKEN > HF_TOKEN_FILE > ~/.cache/huggingface/token | |
| read_current_hf_token() { | |
| if [[ -n "${HUGGINGFACE_HUB_TOKEN:-}" ]]; then | |
| printf '%s' "${HUGGINGFACE_HUB_TOKEN}" | tr -d '\r\n' | |
| return 0 | |
| fi | |
| if [[ -n "${HF_TOKEN:-}" ]]; then | |
| printf '%s' "${HF_TOKEN}" | tr -d '\r\n' | |
| return 0 | |
| fi | |
| local token_file="${HF_TOKEN_FILE:-$HOME/.cache/huggingface/token}" | |
| if [[ -f "$token_file" ]]; then | |
| head -n 1 "$token_file" | tr -d '\r\n' | |
| return 0 | |
| fi | |
| printf '' | |
| } | |
| # 用 token 登录 HF | |
| login_with_hf_token() { | |
| local token="$1" | |
| hf auth login --token "$token" >/dev/null 2>&1 || die "hf auth login with token failed." | |
| } | |
| # ---- 核心 API ---- | |
| # 获取当前 HF 用户名 | |
| # 返回: HF username 或 空字符串 | |
| # 实现与之前 _hf_user.sh:17-61 一致 | |
| get_hf_username() { | |
| local whoami_output | |
| local username | |
| # 方法1: 通过 hf auth whoami | |
| whoami_output="$(hf auth whoami 2>&1 || true)" | |
| username="$(_parse_whoami_username "$whoami_output")" | |
| if [[ -n "$username" ]]; then | |
| printf '%s' "$username" | |
| return 0 | |
| fi | |
| # 方法2: 通过 Python huggingface_hub(fallback) | |
| local hf_token_file="${HF_TOKEN_FILE:-$HOME/.cache/huggingface/token}" | |
| local token_from_cache="" | |
| if [[ -f "$hf_token_file" ]]; then | |
| token_from_cache="$(head -n 1 "$hf_token_file" | tr -d '\r\n')" | |
| fi | |
| if [[ -n "$token_from_cache" ]]; then | |
| username="$( | |
| HF_API_TOKEN="$token_from_cache" python3 - <<'PY' 2>/dev/null || true | |
| from huggingface_hub import HfApi | |
| import os | |
| token = (os.environ.get("HF_API_TOKEN") or "").strip() or None | |
| api = HfApi(token=token) | |
| data = api.whoami(token=token) | |
| name = data.get("name", "") if isinstance(data, dict) else "" | |
| print((name or "").strip()) | |
| PY | |
| )" | |
| username="$(printf '%s' "$username" | tr -d '\r\n')" | |
| if [[ -n "$username" ]]; then | |
| printf '%s' "$username" | |
| return 0 | |
| fi | |
| fi | |
| return 1 | |
| } | |
| # HF 账户切换提示 | |
| # 已登录时: | |
| # - TTY: 询问 "Use this user? <name> [Y/n]",如拒绝则 prompt 新 token 并切换 | |
| # - 非 TTY: 静默使用当前用户 | |
| # 未登录时: | |
| # - return 1 | |
| prompt_hf_account_switch() { | |
| local whoami_output | |
| local current_username | |
| whoami_output="$(hf auth whoami 2>&1 || true)" | |
| current_username="$(_parse_whoami_username "$whoami_output")" | |
| if [[ -z "$current_username" ]]; then | |
| return 1 | |
| fi | |
| if [[ -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 | |
| # 切换账户 | |
| while true; do | |
| local new_token | |
| new_token="$(prompt_secret_required "HF_TOKEN to switch to a different account")" || { | |
| warn "Empty token, please try again or press Ctrl+C to cancel" | |
| continue | |
| } | |
| if [[ -z "$new_token" ]]; then | |
| warn "Empty token, please try again or press Ctrl+C to cancel" | |
| continue | |
| fi | |
| if ! hf auth login --token "$new_token" >/dev/null 2>&1; then | |
| warn "HF login failed, please try again" | |
| continue | |
| fi | |
| local verify_output | |
| verify_output="$(hf auth whoami 2>&1 || true)" | |
| local new_username | |
| new_username="$(_parse_whoami_username "$verify_output")" | |
| if [[ -n "$new_username" && "$new_username" != "$current_username" ]]; then | |
| info "HF account switched: $current_username -> $new_username" | |
| elif [[ -n "$new_username" ]]; then | |
| info "HF login successful as: $new_username" | |
| else | |
| info "HF login successful" | |
| fi | |
| return 0 | |
| done | |
| fi | |
| return 0 | |
| } | |
| # 获取 HF token,必要时提示用户登录 | |
| # 优先级: | |
| # 1. prompt_hf_account_switch(已登录则用当前;TTY 下可切换) | |
| # 2. env (HUGGINGFACE_HUB_TOKEN / HF_TOKEN) | |
| # 3. ~/.cache/huggingface/token | |
| # 4. TTY 下交互式 prompt | |
| # 5. die | |
| # 成功时通过 stdout 输出 token | |
| require_hf_token() { | |
| local token="" | |
| # 1. 尝试切换(如果已登录) | |
| if prompt_hf_account_switch; then | |
| token="$(read_current_hf_token)" | |
| [[ -n "$token" ]] && { printf '%s' "$token"; return 0; } | |
| fi | |
| # 2. 回退到环境变量 / cache 文件 | |
| token="$(read_current_hf_token)" | |
| [[ -n "$token" ]] && { printf '%s' "$token"; return 0; } | |
| # 3. 最后手段: 交互式 prompt | |
| if [[ -t 0 ]]; then | |
| info "HF CLI is not logged in and HF_TOKEN is not set." | |
| while true; do | |
| local new_token | |
| new_token="$(prompt_secret_required "HF_TOKEN (required for HF login)")" || { | |
| # 非 TTY 或 Ctrl+C | |
| break | |
| } | |
| if [[ -z "$new_token" ]]; then | |
| warn "Empty token, please try again or press Ctrl+C to cancel" | |
| continue | |
| fi | |
| if ! hf auth login --token "$new_token" >/dev/null 2>&1; then | |
| warn "HF login failed, please try again" | |
| continue | |
| fi | |
| local verify_output | |
| verify_output="$(hf auth whoami 2>&1 || true)" | |
| local new_username | |
| new_username="$(_parse_whoami_username "$verify_output")" | |
| if [[ -n "$new_username" ]]; then | |
| info "HF login successful as: $new_username" | |
| else | |
| info "HF login successful" | |
| fi | |
| printf '%s' "$new_token" | |
| return 0 | |
| done | |
| fi | |
| die "HF_TOKEN is required. Set HF_TOKEN env var or run: hf auth login" | |
| } | |