durable_huggingface_hub 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +29 -0
- data/.rubocop.yml +108 -0
- data/CHANGELOG.md +127 -0
- data/README.md +547 -0
- data/Rakefile +106 -0
- data/devenv.lock +171 -0
- data/devenv.nix +15 -0
- data/devenv.yaml +8 -0
- data/huggingface_hub.gemspec +63 -0
- data/lib/durable_huggingface_hub/authentication.rb +245 -0
- data/lib/durable_huggingface_hub/cache.rb +508 -0
- data/lib/durable_huggingface_hub/configuration.rb +191 -0
- data/lib/durable_huggingface_hub/constants.rb +145 -0
- data/lib/durable_huggingface_hub/errors.rb +412 -0
- data/lib/durable_huggingface_hub/file_download.rb +831 -0
- data/lib/durable_huggingface_hub/hf_api.rb +1278 -0
- data/lib/durable_huggingface_hub/repo_card.rb +430 -0
- data/lib/durable_huggingface_hub/types/cache_info.rb +298 -0
- data/lib/durable_huggingface_hub/types/commit_info.rb +149 -0
- data/lib/durable_huggingface_hub/types/dataset_info.rb +158 -0
- data/lib/durable_huggingface_hub/types/model_info.rb +154 -0
- data/lib/durable_huggingface_hub/types/space_info.rb +158 -0
- data/lib/durable_huggingface_hub/types/user.rb +179 -0
- data/lib/durable_huggingface_hub/types.rb +205 -0
- data/lib/durable_huggingface_hub/utils/auth.rb +174 -0
- data/lib/durable_huggingface_hub/utils/headers.rb +220 -0
- data/lib/durable_huggingface_hub/utils/http.rb +329 -0
- data/lib/durable_huggingface_hub/utils/paths.rb +230 -0
- data/lib/durable_huggingface_hub/utils/progress.rb +217 -0
- data/lib/durable_huggingface_hub/utils/retry.rb +165 -0
- data/lib/durable_huggingface_hub/utils/validators.rb +236 -0
- data/lib/durable_huggingface_hub/version.rb +8 -0
- data/lib/huggingface_hub.rb +205 -0
- metadata +334 -0
data/devenv.lock
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"devenv": {
|
|
4
|
+
"locked": {
|
|
5
|
+
"dir": "src/modules",
|
|
6
|
+
"lastModified": 1761091275,
|
|
7
|
+
"owner": "cachix",
|
|
8
|
+
"repo": "devenv",
|
|
9
|
+
"rev": "a795c32dc826b51d12706f27fb344f966bb2b084",
|
|
10
|
+
"type": "github"
|
|
11
|
+
},
|
|
12
|
+
"original": {
|
|
13
|
+
"dir": "src/modules",
|
|
14
|
+
"owner": "cachix",
|
|
15
|
+
"repo": "devenv",
|
|
16
|
+
"type": "github"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"flake-compat": {
|
|
20
|
+
"flake": false,
|
|
21
|
+
"locked": {
|
|
22
|
+
"lastModified": 1747046372,
|
|
23
|
+
"owner": "edolstra",
|
|
24
|
+
"repo": "flake-compat",
|
|
25
|
+
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
|
26
|
+
"type": "github"
|
|
27
|
+
},
|
|
28
|
+
"original": {
|
|
29
|
+
"owner": "edolstra",
|
|
30
|
+
"repo": "flake-compat",
|
|
31
|
+
"type": "github"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"flake-compat_2": {
|
|
35
|
+
"flake": false,
|
|
36
|
+
"locked": {
|
|
37
|
+
"lastModified": 1747046372,
|
|
38
|
+
"owner": "edolstra",
|
|
39
|
+
"repo": "flake-compat",
|
|
40
|
+
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
|
41
|
+
"type": "github"
|
|
42
|
+
},
|
|
43
|
+
"original": {
|
|
44
|
+
"owner": "edolstra",
|
|
45
|
+
"repo": "flake-compat",
|
|
46
|
+
"type": "github"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"flake-utils": {
|
|
50
|
+
"inputs": {
|
|
51
|
+
"systems": "systems"
|
|
52
|
+
},
|
|
53
|
+
"locked": {
|
|
54
|
+
"lastModified": 1731533236,
|
|
55
|
+
"owner": "numtide",
|
|
56
|
+
"repo": "flake-utils",
|
|
57
|
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
58
|
+
"type": "github"
|
|
59
|
+
},
|
|
60
|
+
"original": {
|
|
61
|
+
"owner": "numtide",
|
|
62
|
+
"repo": "flake-utils",
|
|
63
|
+
"type": "github"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"git-hooks": {
|
|
67
|
+
"inputs": {
|
|
68
|
+
"flake-compat": "flake-compat",
|
|
69
|
+
"gitignore": "gitignore",
|
|
70
|
+
"nixpkgs": [
|
|
71
|
+
"nixpkgs"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"locked": {
|
|
75
|
+
"lastModified": 1760663237,
|
|
76
|
+
"owner": "cachix",
|
|
77
|
+
"repo": "git-hooks.nix",
|
|
78
|
+
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
|
|
79
|
+
"type": "github"
|
|
80
|
+
},
|
|
81
|
+
"original": {
|
|
82
|
+
"owner": "cachix",
|
|
83
|
+
"repo": "git-hooks.nix",
|
|
84
|
+
"type": "github"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"gitignore": {
|
|
88
|
+
"inputs": {
|
|
89
|
+
"nixpkgs": [
|
|
90
|
+
"git-hooks",
|
|
91
|
+
"nixpkgs"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"locked": {
|
|
95
|
+
"lastModified": 1709087332,
|
|
96
|
+
"owner": "hercules-ci",
|
|
97
|
+
"repo": "gitignore.nix",
|
|
98
|
+
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
|
99
|
+
"type": "github"
|
|
100
|
+
},
|
|
101
|
+
"original": {
|
|
102
|
+
"owner": "hercules-ci",
|
|
103
|
+
"repo": "gitignore.nix",
|
|
104
|
+
"type": "github"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"nixpkgs": {
|
|
108
|
+
"locked": {
|
|
109
|
+
"lastModified": 1758532697,
|
|
110
|
+
"owner": "cachix",
|
|
111
|
+
"repo": "devenv-nixpkgs",
|
|
112
|
+
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
|
|
113
|
+
"type": "github"
|
|
114
|
+
},
|
|
115
|
+
"original": {
|
|
116
|
+
"owner": "cachix",
|
|
117
|
+
"ref": "rolling",
|
|
118
|
+
"repo": "devenv-nixpkgs",
|
|
119
|
+
"type": "github"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"nixpkgs-ruby": {
|
|
123
|
+
"inputs": {
|
|
124
|
+
"flake-compat": "flake-compat_2",
|
|
125
|
+
"flake-utils": "flake-utils",
|
|
126
|
+
"nixpkgs": [
|
|
127
|
+
"nixpkgs"
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"locked": {
|
|
131
|
+
"lastModified": 1759902829,
|
|
132
|
+
"owner": "bobvanderlinden",
|
|
133
|
+
"repo": "nixpkgs-ruby",
|
|
134
|
+
"rev": "5fba6c022a63f1e76dee4da71edddad8959f088a",
|
|
135
|
+
"type": "github"
|
|
136
|
+
},
|
|
137
|
+
"original": {
|
|
138
|
+
"owner": "bobvanderlinden",
|
|
139
|
+
"repo": "nixpkgs-ruby",
|
|
140
|
+
"type": "github"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"root": {
|
|
144
|
+
"inputs": {
|
|
145
|
+
"devenv": "devenv",
|
|
146
|
+
"git-hooks": "git-hooks",
|
|
147
|
+
"nixpkgs": "nixpkgs",
|
|
148
|
+
"nixpkgs-ruby": "nixpkgs-ruby",
|
|
149
|
+
"pre-commit-hooks": [
|
|
150
|
+
"git-hooks"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"systems": {
|
|
155
|
+
"locked": {
|
|
156
|
+
"lastModified": 1681028828,
|
|
157
|
+
"owner": "nix-systems",
|
|
158
|
+
"repo": "default",
|
|
159
|
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
160
|
+
"type": "github"
|
|
161
|
+
},
|
|
162
|
+
"original": {
|
|
163
|
+
"owner": "nix-systems",
|
|
164
|
+
"repo": "default",
|
|
165
|
+
"type": "github"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"root": "root",
|
|
170
|
+
"version": 7
|
|
171
|
+
}
|
data/devenv.nix
ADDED
data/devenv.yaml
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/durable_huggingface_hub/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "durable_huggingface_hub"
|
|
7
|
+
spec.version = DurableHuggingfaceHub::VERSION.to_s
|
|
8
|
+
spec.authors = ["David Berube"]
|
|
9
|
+
spec.email = ["commercial@durableprogramming.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Pure Ruby client for HuggingFace Hub"
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
A complete, production-ready Ruby implementation of the HuggingFace Hub client library.
|
|
14
|
+
Download models, datasets, and manage repositories with zero Python dependencies.
|
|
15
|
+
Features smart caching, authentication, progress tracking, and comprehensive error handling.
|
|
16
|
+
DESC
|
|
17
|
+
spec.homepage = "https://github.com/durableprogramming/huggingface-hub-ruby"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
20
|
+
|
|
21
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/durableprogramming/huggingface-hub-ruby"
|
|
24
|
+
spec.metadata["changelog_uri"] = "https://github.com/durableprogramming/huggingface-hub-ruby/blob/master/CHANGELOG.md"
|
|
25
|
+
spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/huggingface_hub"
|
|
26
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/durableprogramming/huggingface-hub-ruby/issues"
|
|
27
|
+
|
|
28
|
+
# Specify which files should be added to the gem when it is released.
|
|
29
|
+
spec.files = Dir.chdir(__dir__) do
|
|
30
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
31
|
+
(File.expand_path(f) == __FILE__) ||
|
|
32
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile reference/ philosophy/ durableprogramming-coding-standards/])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
spec.bindir = "exe"
|
|
36
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
37
|
+
spec.require_paths = ["lib"]
|
|
38
|
+
|
|
39
|
+
# Runtime dependencies
|
|
40
|
+
spec.add_dependency "faraday", "~> 2.0"
|
|
41
|
+
spec.add_dependency "faraday-multipart", "~> 1.0"
|
|
42
|
+
spec.add_dependency "faraday-retry", "~> 2.0"
|
|
43
|
+
spec.add_dependency "dry-struct", "~> 1.6"
|
|
44
|
+
spec.add_dependency "dry-types", "~> 1.7"
|
|
45
|
+
spec.add_dependency "ruby-progressbar", "~> 1.13"
|
|
46
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
|
47
|
+
|
|
48
|
+
# Development dependencies
|
|
49
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
50
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
51
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
|
52
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.6"
|
|
53
|
+
spec.add_development_dependency "webmock", "~> 3.18"
|
|
54
|
+
spec.add_development_dependency "vcr", "~> 6.1"
|
|
55
|
+
spec.add_development_dependency "rubocop", "~> 1.50"
|
|
56
|
+
spec.add_development_dependency "rubocop-minitest", "~> 0.31"
|
|
57
|
+
spec.add_development_dependency "rubocop-rake", "~> 0.6"
|
|
58
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
|
59
|
+
spec.add_development_dependency "simplecov", "~> 0.22"
|
|
60
|
+
|
|
61
|
+
# For more information and examples about making a new gem, check out our
|
|
62
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
63
|
+
end
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "io/console"
|
|
4
|
+
|
|
5
|
+
module DurableHuggingfaceHub
|
|
6
|
+
# Authentication methods for HuggingFace Hub.
|
|
7
|
+
#
|
|
8
|
+
# This module provides methods for logging in and out of HuggingFace Hub,
|
|
9
|
+
# storing authentication tokens securely, and verifying credentials.
|
|
10
|
+
module Authentication
|
|
11
|
+
# Logs in to HuggingFace Hub and stores the authentication token.
|
|
12
|
+
#
|
|
13
|
+
# If a token is provided, it will be validated and stored. If no token
|
|
14
|
+
# is provided and the process is running interactively, the user will
|
|
15
|
+
# be prompted to enter a token.
|
|
16
|
+
#
|
|
17
|
+
# @param token [String, nil] Authentication token
|
|
18
|
+
# @param add_to_git_credential [Boolean] Whether to add token to git credential helper
|
|
19
|
+
# @return [String] The stored token (masked)
|
|
20
|
+
# @raise [DurableHuggingfaceHubError] If token is invalid or login fails
|
|
21
|
+
#
|
|
22
|
+
# @example With explicit token
|
|
23
|
+
# DurableHuggingfaceHub.login(token: "hf_...")
|
|
24
|
+
#
|
|
25
|
+
# @example Interactive login
|
|
26
|
+
# DurableHuggingfaceHub.login # Prompts for token
|
|
27
|
+
#
|
|
28
|
+
# @example Login with git credential storage
|
|
29
|
+
# DurableHuggingfaceHub.login(token: "hf_...", add_to_git_credential: true)
|
|
30
|
+
def self.login(token: nil, add_to_git_credential: false)
|
|
31
|
+
# Get token from parameter or prompt
|
|
32
|
+
token = obtain_token(token)
|
|
33
|
+
|
|
34
|
+
# Validate token format
|
|
35
|
+
unless Utils::Auth.valid_token_format?(token)
|
|
36
|
+
raise ValidationError.new("token", "Invalid token format. Token should start with 'hf_'")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Verify token works by calling whoami
|
|
40
|
+
begin
|
|
41
|
+
user_info = whoami(token: token)
|
|
42
|
+
rescue HfHubHTTPError => e
|
|
43
|
+
raise DurableHuggingfaceHubError.new("Login failed: #{e.message}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Store token
|
|
47
|
+
Utils::Auth.write_token_to_file(token)
|
|
48
|
+
|
|
49
|
+
# Store in git credential helper if requested
|
|
50
|
+
if add_to_git_credential
|
|
51
|
+
store_git_credential(token)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Update configuration
|
|
55
|
+
Configuration.instance.token = token
|
|
56
|
+
|
|
57
|
+
# Return masked token and user info
|
|
58
|
+
masked = Utils::Auth.mask_token(token)
|
|
59
|
+
username = user_info.name || "unknown"
|
|
60
|
+
|
|
61
|
+
puts "Login successful!"
|
|
62
|
+
puts "Token: #{masked}"
|
|
63
|
+
puts "User: #{username}"
|
|
64
|
+
|
|
65
|
+
masked
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Logs out of HuggingFace Hub by removing the stored token.
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] True if token was removed, false if no token existed
|
|
71
|
+
#
|
|
72
|
+
# @example
|
|
73
|
+
# DurableHuggingfaceHub.logout
|
|
74
|
+
def self.logout
|
|
75
|
+
result = Utils::Auth.delete_token_file
|
|
76
|
+
|
|
77
|
+
# Remove from git credential helper if present
|
|
78
|
+
remove_git_credential
|
|
79
|
+
|
|
80
|
+
# Clear from configuration
|
|
81
|
+
Configuration.instance.token = nil
|
|
82
|
+
|
|
83
|
+
if result
|
|
84
|
+
puts "Logged out successfully. Token removed."
|
|
85
|
+
else
|
|
86
|
+
puts "No token found to remove."
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
result
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns information about the currently authenticated user.
|
|
93
|
+
#
|
|
94
|
+
# Makes a request to the /api/whoami endpoint to retrieve user information.
|
|
95
|
+
#
|
|
96
|
+
# @param token [String, nil] Authentication token (uses stored token if not provided)
|
|
97
|
+
# @return [Hash] User information hash
|
|
98
|
+
# @raise [LocalTokenNotFoundError] If no token is available
|
|
99
|
+
# @raise [HfHubHTTPError] If the API request fails
|
|
100
|
+
#
|
|
101
|
+
# @example
|
|
102
|
+
# user_info = DurableHuggingfaceHub.whoami
|
|
103
|
+
# puts user_info["name"]
|
|
104
|
+
# puts user_info["fullname"]
|
|
105
|
+
def self.whoami(token: nil)
|
|
106
|
+
token = Utils::Auth.get_token!(token: token)
|
|
107
|
+
|
|
108
|
+
client = Utils::HttpClient.new(token: token)
|
|
109
|
+
response = client.get("/api/whoami")
|
|
110
|
+
|
|
111
|
+
Types::User.from_hash(response.body)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Checks if a user is currently logged in (has a valid token).
|
|
115
|
+
#
|
|
116
|
+
# @return [Boolean] True if a token is available
|
|
117
|
+
#
|
|
118
|
+
# @example
|
|
119
|
+
# if DurableHuggingfaceHub.logged_in?
|
|
120
|
+
# puts "Already logged in"
|
|
121
|
+
# end
|
|
122
|
+
def self.logged_in?
|
|
123
|
+
token = Utils::Auth.get_token
|
|
124
|
+
!token.nil? && !token.empty?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# Checks if git is available on the system.
|
|
130
|
+
#
|
|
131
|
+
# @return [Boolean] True if git is available
|
|
132
|
+
def self.git_available?
|
|
133
|
+
system("which git > /dev/null 2>&1")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Stores the token in the git credential helper.
|
|
137
|
+
#
|
|
138
|
+
# @param token [String] Authentication token
|
|
139
|
+
# @return [Boolean] True if stored successfully
|
|
140
|
+
def self.store_git_credential(token)
|
|
141
|
+
# Check if git is available
|
|
142
|
+
return false unless git_available?
|
|
143
|
+
|
|
144
|
+
credential_data = <<~CREDENTIAL
|
|
145
|
+
protocol=https
|
|
146
|
+
host=huggingface.co
|
|
147
|
+
username=hf_#{token[3..]} # Extract token part after 'hf_'
|
|
148
|
+
password=#{token}
|
|
149
|
+
CREDENTIAL
|
|
150
|
+
|
|
151
|
+
# Use git credential approve to store the credential
|
|
152
|
+
begin
|
|
153
|
+
IO.popen("git credential approve", "w+") do |io|
|
|
154
|
+
io.write(credential_data)
|
|
155
|
+
io.close_write
|
|
156
|
+
io.read # Consume any output
|
|
157
|
+
end
|
|
158
|
+
$?.success?
|
|
159
|
+
rescue Errno::ENOENT, IOError
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Removes the token from the git credential helper.
|
|
165
|
+
#
|
|
166
|
+
# @return [Boolean] True if removed successfully
|
|
167
|
+
def self.remove_git_credential
|
|
168
|
+
# Check if git is available
|
|
169
|
+
return false unless git_available?
|
|
170
|
+
|
|
171
|
+
# First check if we have a stored token to know what to remove
|
|
172
|
+
token = Utils::Auth.get_token
|
|
173
|
+
return false unless token
|
|
174
|
+
|
|
175
|
+
credential_data = <<~CREDENTIAL
|
|
176
|
+
protocol=https
|
|
177
|
+
host=huggingface.co
|
|
178
|
+
username=hf_#{token[3..]} # Extract token part after 'hf_'
|
|
179
|
+
CREDENTIAL
|
|
180
|
+
|
|
181
|
+
# Use git credential reject to remove the credential
|
|
182
|
+
begin
|
|
183
|
+
IO.popen("git credential reject", "w+") do |io|
|
|
184
|
+
io.write(credential_data)
|
|
185
|
+
io.close_write
|
|
186
|
+
io.read # Consume any output
|
|
187
|
+
end
|
|
188
|
+
$?.success?
|
|
189
|
+
rescue Errno::ENOENT, IOError
|
|
190
|
+
false
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Obtains a token either from parameter or by prompting the user.
|
|
195
|
+
#
|
|
196
|
+
# @param token [String, nil] Provided token
|
|
197
|
+
# @return [String] Token
|
|
198
|
+
# @raise [DurableHuggingfaceHubError] If unable to obtain token
|
|
199
|
+
def self.obtain_token(token)
|
|
200
|
+
return token if token && !token.empty?
|
|
201
|
+
|
|
202
|
+
# Check if running interactively
|
|
203
|
+
unless $stdin.respond_to?(:tty?) && $stdin.tty?
|
|
204
|
+
raise DurableHuggingfaceHubError.new(
|
|
205
|
+
"No token provided and not running interactively. " \
|
|
206
|
+
"Please provide a token using the 'token' parameter."
|
|
207
|
+
)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Prompt for token
|
|
211
|
+
prompt_for_token
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Prompts the user to enter their authentication token.
|
|
215
|
+
#
|
|
216
|
+
# The token input is hidden for security.
|
|
217
|
+
#
|
|
218
|
+
# @return [String] Entered token
|
|
219
|
+
# @raise [DurableHuggingfaceHubError] If token entry is cancelled
|
|
220
|
+
def self.prompt_for_token
|
|
221
|
+
puts "\nTo login, you need a User Access Token from https://huggingface.co/settings/tokens"
|
|
222
|
+
puts "You can create a new token or use an existing one."
|
|
223
|
+
puts "\nPaste your token (input will be hidden):"
|
|
224
|
+
|
|
225
|
+
# Read token without echoing to terminal
|
|
226
|
+
begin
|
|
227
|
+
if $stdin.respond_to?(:noecho) && $stdin.isatty
|
|
228
|
+
token = $stdin.noecho(&:gets)&.chomp
|
|
229
|
+
else
|
|
230
|
+
# For testing or when noecho is not available
|
|
231
|
+
token = $stdin.gets&.chomp
|
|
232
|
+
end
|
|
233
|
+
rescue Interrupt
|
|
234
|
+
puts "\nLogin cancelled"
|
|
235
|
+
raise DurableHuggingfaceHubError.new("Login cancelled by user")
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if token.nil? || token.empty?
|
|
239
|
+
raise DurableHuggingfaceHubError.new("Token entry cancelled or empty")
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
token
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|