a2a-ruby 1.0.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/.rspec +3 -0
- data/.rubocop.yml +137 -0
- data/.simplecov +46 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +33 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +165 -0
- data/Gemfile +43 -0
- data/Guardfile +34 -0
- data/LICENSE.txt +21 -0
- data/PUBLISHING_CHECKLIST.md +214 -0
- data/README.md +171 -0
- data/Rakefile +165 -0
- data/docs/agent_execution.md +309 -0
- data/docs/api_reference.md +792 -0
- data/docs/configuration.md +780 -0
- data/docs/events.md +475 -0
- data/docs/getting_started.md +668 -0
- data/docs/integration.md +262 -0
- data/docs/server_apps.md +621 -0
- data/docs/troubleshooting.md +765 -0
- data/lib/a2a/client/api_methods.rb +263 -0
- data/lib/a2a/client/auth/api_key.rb +161 -0
- data/lib/a2a/client/auth/interceptor.rb +288 -0
- data/lib/a2a/client/auth/jwt.rb +189 -0
- data/lib/a2a/client/auth/oauth2.rb +146 -0
- data/lib/a2a/client/auth.rb +137 -0
- data/lib/a2a/client/base.rb +316 -0
- data/lib/a2a/client/config.rb +210 -0
- data/lib/a2a/client/connection_pool.rb +233 -0
- data/lib/a2a/client/http_client.rb +524 -0
- data/lib/a2a/client/json_rpc_handler.rb +136 -0
- data/lib/a2a/client/middleware/circuit_breaker_interceptor.rb +245 -0
- data/lib/a2a/client/middleware/logging_interceptor.rb +371 -0
- data/lib/a2a/client/middleware/rate_limit_interceptor.rb +142 -0
- data/lib/a2a/client/middleware/retry_interceptor.rb +161 -0
- data/lib/a2a/client/middleware.rb +116 -0
- data/lib/a2a/client/performance_tracker.rb +60 -0
- data/lib/a2a/configuration/defaults.rb +34 -0
- data/lib/a2a/configuration/environment_loader.rb +76 -0
- data/lib/a2a/configuration/file_loader.rb +115 -0
- data/lib/a2a/configuration/inheritance.rb +101 -0
- data/lib/a2a/configuration/validator.rb +180 -0
- data/lib/a2a/configuration.rb +201 -0
- data/lib/a2a/errors.rb +291 -0
- data/lib/a2a/modules.rb +50 -0
- data/lib/a2a/monitoring/alerting.rb +490 -0
- data/lib/a2a/monitoring/distributed_tracing.rb +398 -0
- data/lib/a2a/monitoring/health_endpoints.rb +204 -0
- data/lib/a2a/monitoring/metrics_collector.rb +438 -0
- data/lib/a2a/monitoring.rb +463 -0
- data/lib/a2a/plugin.rb +358 -0
- data/lib/a2a/plugin_manager.rb +159 -0
- data/lib/a2a/plugins/example_auth.rb +81 -0
- data/lib/a2a/plugins/example_middleware.rb +118 -0
- data/lib/a2a/plugins/example_transport.rb +76 -0
- data/lib/a2a/protocol/agent_card.rb +8 -0
- data/lib/a2a/protocol/agent_card_server.rb +584 -0
- data/lib/a2a/protocol/capability.rb +496 -0
- data/lib/a2a/protocol/json_rpc.rb +254 -0
- data/lib/a2a/protocol/message.rb +8 -0
- data/lib/a2a/protocol/task.rb +8 -0
- data/lib/a2a/rails/a2a_controller.rb +258 -0
- data/lib/a2a/rails/controller_helpers.rb +499 -0
- data/lib/a2a/rails/engine.rb +167 -0
- data/lib/a2a/rails/generators/agent_generator.rb +311 -0
- data/lib/a2a/rails/generators/install_generator.rb +209 -0
- data/lib/a2a/rails/generators/migration_generator.rb +232 -0
- data/lib/a2a/rails/generators/templates/add_a2a_indexes.rb +57 -0
- data/lib/a2a/rails/generators/templates/agent_controller.rb +122 -0
- data/lib/a2a/rails/generators/templates/agent_controller_spec.rb +160 -0
- data/lib/a2a/rails/generators/templates/agent_readme.md +200 -0
- data/lib/a2a/rails/generators/templates/create_a2a_push_notification_configs.rb +68 -0
- data/lib/a2a/rails/generators/templates/create_a2a_tasks.rb +83 -0
- data/lib/a2a/rails/generators/templates/example_agent_controller.rb +228 -0
- data/lib/a2a/rails/generators/templates/initializer.rb +108 -0
- data/lib/a2a/rails/generators/templates/push_notification_config_model.rb +228 -0
- data/lib/a2a/rails/generators/templates/task_model.rb +200 -0
- data/lib/a2a/rails/tasks/a2a.rake +228 -0
- data/lib/a2a/server/a2a_methods.rb +520 -0
- data/lib/a2a/server/agent.rb +537 -0
- data/lib/a2a/server/agent_execution/agent_executor.rb +279 -0
- data/lib/a2a/server/agent_execution/request_context.rb +219 -0
- data/lib/a2a/server/apps/rack_app.rb +311 -0
- data/lib/a2a/server/apps/sinatra_app.rb +261 -0
- data/lib/a2a/server/default_request_handler.rb +350 -0
- data/lib/a2a/server/events/event_consumer.rb +116 -0
- data/lib/a2a/server/events/event_queue.rb +226 -0
- data/lib/a2a/server/example_agent.rb +248 -0
- data/lib/a2a/server/handler.rb +281 -0
- data/lib/a2a/server/middleware/authentication_middleware.rb +212 -0
- data/lib/a2a/server/middleware/cors_middleware.rb +171 -0
- data/lib/a2a/server/middleware/logging_middleware.rb +362 -0
- data/lib/a2a/server/middleware/rate_limit_middleware.rb +382 -0
- data/lib/a2a/server/middleware.rb +213 -0
- data/lib/a2a/server/push_notification_manager.rb +327 -0
- data/lib/a2a/server/request_handler.rb +136 -0
- data/lib/a2a/server/storage/base.rb +141 -0
- data/lib/a2a/server/storage/database.rb +266 -0
- data/lib/a2a/server/storage/memory.rb +274 -0
- data/lib/a2a/server/storage/redis.rb +320 -0
- data/lib/a2a/server/storage.rb +38 -0
- data/lib/a2a/server/task_manager.rb +534 -0
- data/lib/a2a/transport/grpc.rb +481 -0
- data/lib/a2a/transport/http.rb +415 -0
- data/lib/a2a/transport/sse.rb +499 -0
- data/lib/a2a/types/agent_card.rb +540 -0
- data/lib/a2a/types/artifact.rb +99 -0
- data/lib/a2a/types/base_model.rb +223 -0
- data/lib/a2a/types/events.rb +117 -0
- data/lib/a2a/types/message.rb +106 -0
- data/lib/a2a/types/part.rb +288 -0
- data/lib/a2a/types/push_notification.rb +139 -0
- data/lib/a2a/types/security.rb +167 -0
- data/lib/a2a/types/task.rb +154 -0
- data/lib/a2a/types.rb +88 -0
- data/lib/a2a/utils/helpers.rb +245 -0
- data/lib/a2a/utils/message_buffer.rb +278 -0
- data/lib/a2a/utils/performance.rb +247 -0
- data/lib/a2a/utils/rails_detection.rb +97 -0
- data/lib/a2a/utils/structured_logger.rb +306 -0
- data/lib/a2a/utils/time_helpers.rb +167 -0
- data/lib/a2a/utils/validation.rb +8 -0
- data/lib/a2a/version.rb +6 -0
- data/lib/a2a-rails.rb +58 -0
- data/lib/a2a.rb +198 -0
- metadata +437 -0
@@ -0,0 +1,263 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module A2A
|
4
|
+
module Client
|
5
|
+
##
|
6
|
+
# API methods for A2A HTTP client
|
7
|
+
#
|
8
|
+
module ApiMethods
|
9
|
+
##
|
10
|
+
# Send a message to the agent
|
11
|
+
#
|
12
|
+
# @param message [Message, Hash] The message to send
|
13
|
+
# @param context [Hash, nil] Optional context information
|
14
|
+
# @return [Enumerator, Message] Stream of responses or single response
|
15
|
+
def send_message(message, context: nil)
|
16
|
+
message = ensure_message(message)
|
17
|
+
|
18
|
+
if @config.streaming?
|
19
|
+
send_streaming_message(message, context)
|
20
|
+
else
|
21
|
+
send_sync_message(message, context)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Get a task by ID
|
27
|
+
#
|
28
|
+
# @param task_id [String] The task ID
|
29
|
+
# @param context [Hash, nil] Optional context information
|
30
|
+
# @param history_length [Integer, nil] Maximum number of history messages to include
|
31
|
+
# @return [Task] The task
|
32
|
+
def get_task(task_id, context: nil, history_length: nil)
|
33
|
+
params = { id: task_id }
|
34
|
+
params[:historyLength] = history_length if history_length
|
35
|
+
|
36
|
+
request = build_json_rpc_request("tasks/get", params)
|
37
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
38
|
+
send_json_rpc_request(req)
|
39
|
+
end
|
40
|
+
|
41
|
+
ensure_task(response["result"])
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Cancel a task
|
46
|
+
#
|
47
|
+
# @param task_id [String] The task ID to cancel
|
48
|
+
# @param context [Hash, nil] Optional context information
|
49
|
+
# @return [Task] The updated task
|
50
|
+
def cancel_task(task_id, context: nil)
|
51
|
+
request = build_json_rpc_request("tasks/cancel", { id: task_id })
|
52
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
53
|
+
send_json_rpc_request(req)
|
54
|
+
end
|
55
|
+
|
56
|
+
ensure_task(response["result"])
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Get the agent card
|
61
|
+
#
|
62
|
+
# @param context [Hash, nil] Optional context information
|
63
|
+
# @param authenticated [Boolean] Whether to get authenticated extended card
|
64
|
+
# @return [AgentCard] The agent card
|
65
|
+
def get_card(context: nil, authenticated: false)
|
66
|
+
if authenticated
|
67
|
+
request = build_json_rpc_request("agent/getAuthenticatedExtendedCard", {})
|
68
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
69
|
+
send_json_rpc_request(req)
|
70
|
+
end
|
71
|
+
ensure_agent_card(response["result"])
|
72
|
+
else
|
73
|
+
# Use HTTP GET for basic agent card
|
74
|
+
response = execute_with_middleware({}, context || {}) do |_req, _ctx|
|
75
|
+
@connection.get("/agent-card") do |request|
|
76
|
+
request.headers.merge!(@config.all_headers)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
raise A2A::Errors::HTTPError, "HTTP #{response.status}: #{response.body}" unless response.success?
|
81
|
+
|
82
|
+
ensure_agent_card(JSON.parse(response.body))
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Resubscribe to a task
|
89
|
+
#
|
90
|
+
# @param task_id [String] The task ID to resubscribe to
|
91
|
+
# @param context [Hash, nil] Optional context information
|
92
|
+
# @return [Task] The task
|
93
|
+
def resubscribe(task_id, context: nil)
|
94
|
+
request = build_json_rpc_request("tasks/resubscribe", { id: task_id })
|
95
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
96
|
+
send_json_rpc_request(req)
|
97
|
+
end
|
98
|
+
|
99
|
+
ensure_task(response["result"])
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Set a task callback (deprecated - use set_task_push_notification_config)
|
104
|
+
#
|
105
|
+
# @param task_id [String] The task ID
|
106
|
+
# @param push_notification_config [Hash] The push notification configuration
|
107
|
+
# @param context [Hash, nil] Optional context information
|
108
|
+
# @return [Hash] The response
|
109
|
+
def set_task_callback(task_id, push_notification_config, context: nil)
|
110
|
+
params = {
|
111
|
+
taskId: task_id,
|
112
|
+
pushNotificationConfig: push_notification_config
|
113
|
+
}
|
114
|
+
|
115
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/set", params)
|
116
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
117
|
+
send_json_rpc_request(req)
|
118
|
+
end
|
119
|
+
|
120
|
+
response["result"]
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Get a task callback (deprecated - use get_task_push_notification_config)
|
125
|
+
#
|
126
|
+
# @param task_id [String] The task ID
|
127
|
+
# @param push_notification_config_id [String] The config ID
|
128
|
+
# @param context [Hash, nil] Optional context information
|
129
|
+
# @return [Hash] The configuration
|
130
|
+
def get_task_callback(task_id, push_notification_config_id, context: nil)
|
131
|
+
params = {
|
132
|
+
taskId: task_id,
|
133
|
+
pushNotificationConfigId: push_notification_config_id
|
134
|
+
}
|
135
|
+
|
136
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/get", params)
|
137
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
138
|
+
send_json_rpc_request(req)
|
139
|
+
end
|
140
|
+
|
141
|
+
response["result"]
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# List task callbacks (deprecated - use list_task_push_notification_configs)
|
146
|
+
#
|
147
|
+
# @param task_id [String] The task ID
|
148
|
+
# @param context [Hash, nil] Optional context information
|
149
|
+
# @return [Array] List of configurations
|
150
|
+
def list_task_callbacks(task_id, context: nil)
|
151
|
+
params = { taskId: task_id }
|
152
|
+
|
153
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/list", params)
|
154
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
155
|
+
send_json_rpc_request(req)
|
156
|
+
end
|
157
|
+
|
158
|
+
response["result"]
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Delete a task callback (deprecated - use delete_task_push_notification_config)
|
163
|
+
#
|
164
|
+
# @param task_id [String] The task ID
|
165
|
+
# @param push_notification_config_id [String] The config ID
|
166
|
+
# @param context [Hash, nil] Optional context information
|
167
|
+
# @return [Boolean] Success status
|
168
|
+
def delete_task_callback(task_id, push_notification_config_id, context: nil)
|
169
|
+
params = {
|
170
|
+
taskId: task_id,
|
171
|
+
pushNotificationConfigId: push_notification_config_id
|
172
|
+
}
|
173
|
+
|
174
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/delete", params)
|
175
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
176
|
+
send_json_rpc_request(req)
|
177
|
+
end
|
178
|
+
|
179
|
+
response["result"]
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Set a push notification config for a task
|
184
|
+
#
|
185
|
+
# @param task_id [String] The task ID
|
186
|
+
# @param config [Hash] The push notification configuration
|
187
|
+
# @param context [Hash, nil] Optional context information
|
188
|
+
# @return [Hash] The response
|
189
|
+
def set_task_push_notification_config(task_id, config, context: nil)
|
190
|
+
params = {
|
191
|
+
taskId: task_id,
|
192
|
+
pushNotificationConfig: config
|
193
|
+
}
|
194
|
+
|
195
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/set", params)
|
196
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
197
|
+
send_json_rpc_request(req)
|
198
|
+
end
|
199
|
+
|
200
|
+
response["result"]
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Get a push notification config for a task
|
205
|
+
#
|
206
|
+
# @param task_id [String] The task ID
|
207
|
+
# @param config_id [String] The config ID
|
208
|
+
# @param context [Hash, nil] Optional context information
|
209
|
+
# @return [Hash] The configuration
|
210
|
+
def get_task_push_notification_config(task_id, config_id, context: nil)
|
211
|
+
params = {
|
212
|
+
taskId: task_id,
|
213
|
+
pushNotificationConfigId: config_id
|
214
|
+
}
|
215
|
+
|
216
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/get", params)
|
217
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
218
|
+
send_json_rpc_request(req)
|
219
|
+
end
|
220
|
+
|
221
|
+
response["result"]
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# List push notification configs for a task
|
226
|
+
#
|
227
|
+
# @param task_id [String] The task ID
|
228
|
+
# @param context [Hash, nil] Optional context information
|
229
|
+
# @return [Array] List of configurations
|
230
|
+
def list_task_push_notification_configs(task_id, context: nil)
|
231
|
+
params = { taskId: task_id }
|
232
|
+
|
233
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/list", params)
|
234
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
235
|
+
send_json_rpc_request(req)
|
236
|
+
end
|
237
|
+
|
238
|
+
response["result"]
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Delete a push notification config for a task
|
243
|
+
#
|
244
|
+
# @param task_id [String] The task ID
|
245
|
+
# @param config_id [String] The config ID
|
246
|
+
# @param context [Hash, nil] Optional context information
|
247
|
+
# @return [Boolean] Success status
|
248
|
+
def delete_task_push_notification_config(task_id, config_id, context: nil)
|
249
|
+
params = {
|
250
|
+
taskId: task_id,
|
251
|
+
pushNotificationConfigId: config_id
|
252
|
+
}
|
253
|
+
|
254
|
+
request = build_json_rpc_request("tasks/pushNotificationConfig/delete", params)
|
255
|
+
response = execute_with_middleware(request, context || {}) do |req, _ctx|
|
256
|
+
send_json_rpc_request(req)
|
257
|
+
end
|
258
|
+
|
259
|
+
response["result"]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# API Key authentication strategy
|
5
|
+
#
|
6
|
+
# Supports API key authentication via headers, query parameters,
|
7
|
+
# or custom locations as specified by the agent's security scheme.
|
8
|
+
#
|
9
|
+
module A2A
|
10
|
+
module Client
|
11
|
+
module Auth
|
12
|
+
class ApiKey
|
13
|
+
attr_reader :key, :value, :location, :name
|
14
|
+
|
15
|
+
# Valid locations for API key
|
16
|
+
VALID_LOCATIONS = %w[header query cookie].freeze
|
17
|
+
|
18
|
+
##
|
19
|
+
# Initialize API key authentication
|
20
|
+
#
|
21
|
+
# @param key [String] The API key value
|
22
|
+
# @param name [String] The parameter/header name for the API key (default: 'X-API-Key')
|
23
|
+
# @param location [String] Where to place the API key: 'header', 'query', or 'cookie' (default: 'header')
|
24
|
+
def initialize(key:, name: "X-API-Key", location: "header")
|
25
|
+
@key = key
|
26
|
+
@name = name
|
27
|
+
@location = location.downcase
|
28
|
+
@value = key # Alias for consistency
|
29
|
+
|
30
|
+
validate_configuration!
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Apply authentication to a Faraday request
|
35
|
+
#
|
36
|
+
# @param request [Faraday::Request] The request to authenticate
|
37
|
+
def apply_to_request(request)
|
38
|
+
case @location
|
39
|
+
when "header"
|
40
|
+
request.headers[@name] = @key
|
41
|
+
when "query"
|
42
|
+
# Add to query parameters
|
43
|
+
request.params[@name] = @key
|
44
|
+
when "cookie"
|
45
|
+
# Add to cookie header
|
46
|
+
existing_cookies = request.headers["Cookie"]
|
47
|
+
cookie_value = "#{@name}=#{@key}"
|
48
|
+
|
49
|
+
request.headers["Cookie"] = if existing_cookies
|
50
|
+
"#{existing_cookies}; #{cookie_value}"
|
51
|
+
else
|
52
|
+
cookie_value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Get the authentication header (for header-based API keys)
|
59
|
+
#
|
60
|
+
# @return [Hash] Header name and value
|
61
|
+
def authentication_header
|
62
|
+
return {} unless @location == "header"
|
63
|
+
|
64
|
+
{ @name => @key }
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Get the authentication query parameter (for query-based API keys)
|
69
|
+
#
|
70
|
+
# @return [Hash] Parameter name and value
|
71
|
+
def authentication_params
|
72
|
+
return {} unless @location == "query"
|
73
|
+
|
74
|
+
{ @name => @key }
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Check if the API key is valid (basic validation)
|
79
|
+
#
|
80
|
+
# @return [Boolean] True if key appears valid
|
81
|
+
def valid?
|
82
|
+
@key.present? && @key.is_a?(String)
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Mask the API key for logging (shows only first and last 4 characters)
|
87
|
+
#
|
88
|
+
# @return [String] Masked API key
|
89
|
+
def masked_key
|
90
|
+
return "[empty]" if @key.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?)
|
91
|
+
return @key if @key.length <= 8
|
92
|
+
|
93
|
+
"#{@key[0..3]}#{'*' * (@key.length - 8)}#{@key[-4..]}"
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Create API key authentication from security scheme
|
98
|
+
#
|
99
|
+
# @param scheme [Hash] Security scheme definition
|
100
|
+
# @param key_value [String] The API key value
|
101
|
+
# @return [ApiKey] Configured API key authentication
|
102
|
+
def self.from_security_scheme(scheme, key_value)
|
103
|
+
location = scheme["in"] || "header"
|
104
|
+
name = scheme["name"] || "X-API-Key"
|
105
|
+
|
106
|
+
new(key: key_value, name: name, location: location)
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Convert to hash representation
|
111
|
+
#
|
112
|
+
# @return [Hash] Configuration as hash
|
113
|
+
def to_h
|
114
|
+
{
|
115
|
+
type: "api_key",
|
116
|
+
key: masked_key,
|
117
|
+
name: @name,
|
118
|
+
location: @location
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# String representation (with masked key)
|
124
|
+
#
|
125
|
+
# @return [String] String representation
|
126
|
+
def to_s
|
127
|
+
"ApiKey(name=#{@name}, location=#{@location}, key=#{masked_key})"
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Inspect representation (with masked key)
|
132
|
+
#
|
133
|
+
# @return [String] Inspect representation
|
134
|
+
def inspect
|
135
|
+
"#<A2A::Client::Auth::ApiKey:0x#{object_id.to_s(16)} #{self}>"
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
##
|
141
|
+
# Validate the authentication configuration
|
142
|
+
def validate_configuration!
|
143
|
+
if @key.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?)
|
144
|
+
raise ArgumentError,
|
145
|
+
"API key cannot be nil or empty"
|
146
|
+
end
|
147
|
+
raise ArgumentError, "API key must be a string" unless @key.is_a?(String)
|
148
|
+
|
149
|
+
if @name.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?)
|
150
|
+
raise ArgumentError,
|
151
|
+
"Name cannot be nil or empty"
|
152
|
+
end
|
153
|
+
|
154
|
+
return if VALID_LOCATIONS.include?(@location)
|
155
|
+
|
156
|
+
raise ArgumentError, "Invalid location '#{@location}'. Must be one of: #{VALID_LOCATIONS.join(', ')}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|