| #!/usr/bin/env bash |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| if [[ -t 0 ]]; then |
| stty sane 2>/dev/null || true |
| 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 |
| |
| 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 |
| |
| 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 |
| |
| printf '%s' "" |
| return 1 |
| fi |
| fi |
| done |
| printf '%s' "$value" |
| } |
|
|
| |
| |
| |
| |
| |
| 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 |
| |
| if [[ "$default_value" == "y" ]]; then |
| printf 'yes' |
| else |
| printf 'no' |
| fi |
| fi |
| } |
|
|
| |
|
|
| |
| |
| _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" |
| } |
|
|
| |
| |
| 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 '' |
| } |
|
|
| |
| login_with_hf_token() { |
| local token="$1" |
| hf auth login --token "$token" >/dev/null 2>&1 || die "hf auth login with token failed." |
| } |
|
|
| |
|
|
| |
| |
| |
| get_hf_username() { |
| local whoami_output |
| local username |
|
|
| |
| whoami_output="$(hf auth whoami 2>&1 || true)" |
| username="$(_parse_whoami_username "$whoami_output")" |
| if [[ -n "$username" ]]; then |
| printf '%s' "$username" |
| return 0 |
| fi |
|
|
| |
| 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 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| require_hf_token() { |
| local token="" |
|
|
| |
| if prompt_hf_account_switch; then |
| token="$(read_current_hf_token)" |
| [[ -n "$token" ]] && { printf '%s' "$token"; return 0; } |
| fi |
|
|
| |
| token="$(read_current_hf_token)" |
| [[ -n "$token" ]] && { printf '%s' "$token"; return 0; } |
|
|
| |
| 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)")" || { |
| |
| 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" |
| } |
|
|