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,311 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/named_base"
|
5
|
+
|
6
|
+
##
|
7
|
+
# Rails generator for creating A2A agent controllers
|
8
|
+
#
|
9
|
+
# This generator creates a new controller with A2A agent functionality,
|
10
|
+
# including skills, capabilities, and method definitions.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# rails generate a2a:agent Chat
|
14
|
+
# rails generate a2a:agent Weather --skills=forecast,alerts
|
15
|
+
# rails generate a2a:agent Assistant --with-authentication --namespace=api/v1
|
16
|
+
#
|
17
|
+
module A2A
|
18
|
+
module Rails
|
19
|
+
module Generators
|
20
|
+
class AgentGenerator < Rails::Generators::NamedBase
|
21
|
+
source_root File.expand_path("templates", __dir__)
|
22
|
+
|
23
|
+
class_option :skills, type: :array, default: [],
|
24
|
+
desc: "List of skills to generate (e.g., --skills=chat,search)"
|
25
|
+
|
26
|
+
class_option :with_authentication, type: :boolean, default: false,
|
27
|
+
desc: "Generate authentication-protected methods"
|
28
|
+
|
29
|
+
class_option :namespace, type: :string, default: nil,
|
30
|
+
desc: "Namespace for the controller (e.g., api/v1)"
|
31
|
+
|
32
|
+
class_option :skip_tests, type: :boolean, default: false,
|
33
|
+
desc: "Skip generating test files"
|
34
|
+
|
35
|
+
class_option :api_only, type: :boolean, default: false,
|
36
|
+
desc: "Generate API-only controller (inherits from ActionController::API)"
|
37
|
+
|
38
|
+
desc "Generate an A2A agent controller"
|
39
|
+
|
40
|
+
def create_controller
|
41
|
+
template "agent_controller.rb", controller_file_path
|
42
|
+
say "Created A2A agent controller: #{controller_class_name}", :green
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_tests
|
46
|
+
return if options[:skip_tests]
|
47
|
+
|
48
|
+
if rspec_available?
|
49
|
+
template "agent_controller_spec.rb", spec_file_path
|
50
|
+
say "Created RSpec test: #{spec_file_path}", :green
|
51
|
+
else
|
52
|
+
template "agent_controller_test.rb", test_file_path
|
53
|
+
say "Created test: #{test_file_path}", :green
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_documentation
|
58
|
+
template "agent_readme.md", documentation_file_path
|
59
|
+
say "Created documentation: #{documentation_file_path}", :green
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_routes
|
63
|
+
route_content = generate_route_content
|
64
|
+
|
65
|
+
return unless File.exist?("config/routes.rb")
|
66
|
+
|
67
|
+
inject_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
|
68
|
+
route_content
|
69
|
+
end
|
70
|
+
say "Added routes for #{controller_class_name}", :green
|
71
|
+
end
|
72
|
+
|
73
|
+
def show_post_generation_instructions
|
74
|
+
say "\n#{'=' * 60}", :green
|
75
|
+
say "A2A Agent '#{class_name}' generated successfully!", :green
|
76
|
+
say "=" * 60, :green
|
77
|
+
|
78
|
+
say "\nGenerated files:", :yellow
|
79
|
+
say " #{controller_file_path}"
|
80
|
+
say " #{documentation_file_path}"
|
81
|
+
say " #{test_file_path}" unless options[:skip_tests]
|
82
|
+
|
83
|
+
say "\nNext steps:", :yellow
|
84
|
+
say "1. Customize the agent skills and methods in #{controller_file_path}"
|
85
|
+
say "2. Implement the A2A method handlers"
|
86
|
+
say "3. Test your agent using the generated test file"
|
87
|
+
|
88
|
+
say "\nAgent endpoints:", :yellow
|
89
|
+
say " GET #{agent_card_path} (Agent Card)"
|
90
|
+
say " POST #{rpc_path} (JSON-RPC Methods)"
|
91
|
+
|
92
|
+
if skills.any?
|
93
|
+
say "\nGenerated skills:", :yellow
|
94
|
+
skills.each do |skill|
|
95
|
+
say " - #{skill.humanize}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
say "\nFor more information, visit: https://a2a-protocol.org/sdk/ruby/agents/", :blue
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def controller_file_path
|
105
|
+
if namespace.present?
|
106
|
+
"app/controllers/#{namespace}/#{file_name}_controller.rb"
|
107
|
+
else
|
108
|
+
"app/controllers/#{file_name}_controller.rb"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def spec_file_path
|
113
|
+
if namespace.present?
|
114
|
+
"spec/controllers/#{namespace}/#{file_name}_controller_spec.rb"
|
115
|
+
else
|
116
|
+
"spec/controllers/#{file_name}_controller_spec.rb"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_file_path
|
121
|
+
if namespace.present?
|
122
|
+
"test/controllers/#{namespace}/#{file_name}_controller_test.rb"
|
123
|
+
else
|
124
|
+
"test/controllers/#{file_name}_controller_test.rb"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def documentation_file_path
|
129
|
+
if namespace.present?
|
130
|
+
"docs/agents/#{namespace}/#{file_name}.md"
|
131
|
+
else
|
132
|
+
"docs/agents/#{file_name}.md"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def controller_class_name
|
137
|
+
if namespace.present?
|
138
|
+
"#{namespace.camelize}::#{class_name}Controller"
|
139
|
+
else
|
140
|
+
"#{class_name}Controller"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def controller_parent_class
|
145
|
+
if options[:api_only]
|
146
|
+
"ActionController::API"
|
147
|
+
else
|
148
|
+
"ApplicationController"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def namespace
|
153
|
+
options[:namespace]
|
154
|
+
end
|
155
|
+
|
156
|
+
def skills
|
157
|
+
@skills ||= options[:skills].map(&:strip).reject(&:empty?)
|
158
|
+
end
|
159
|
+
|
160
|
+
def with_authentication?
|
161
|
+
options[:with_authentication]
|
162
|
+
end
|
163
|
+
|
164
|
+
def generate_route_content
|
165
|
+
if namespace.present?
|
166
|
+
namespace_parts = namespace.split("/")
|
167
|
+
indent = " " * namespace_parts.length
|
168
|
+
|
169
|
+
route_lines = []
|
170
|
+
|
171
|
+
# Build nested namespace structure
|
172
|
+
namespace_parts.each_with_index do |ns, index|
|
173
|
+
route_lines << ((" " * index) + "namespace :#{ns} do")
|
174
|
+
end
|
175
|
+
|
176
|
+
# Add the actual route
|
177
|
+
route_lines << "#{indent} resources :#{file_name.pluralize}, only: [] do"
|
178
|
+
route_lines << "#{indent} collection do"
|
179
|
+
route_lines << "#{indent} get :agent_card"
|
180
|
+
route_lines << "#{indent} post :rpc"
|
181
|
+
route_lines << "#{indent} end"
|
182
|
+
route_lines << "#{indent} end"
|
183
|
+
|
184
|
+
# Close namespace blocks
|
185
|
+
namespace_parts.length.times do |index|
|
186
|
+
route_lines << "#{' ' * (namespace_parts.length - index - 1)}end"
|
187
|
+
end
|
188
|
+
|
189
|
+
"\n#{route_lines.join("\n")}\n"
|
190
|
+
else
|
191
|
+
<<~RUBY
|
192
|
+
|
193
|
+
# #{class_name} A2A Agent routes
|
194
|
+
resources :#{file_name.pluralize}, only: [] do
|
195
|
+
collection do
|
196
|
+
get :agent_card
|
197
|
+
post :rpc
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
RUBY
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def rspec_available?
|
206
|
+
File.exist?("spec/spec_helper.rb") || File.exist?("spec/rails_helper.rb")
|
207
|
+
end
|
208
|
+
|
209
|
+
def agent_card_path
|
210
|
+
if namespace.present?
|
211
|
+
"/#{namespace}/#{file_name.pluralize}/agent_card"
|
212
|
+
else
|
213
|
+
"/#{file_name.pluralize}/agent_card"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def rpc_path
|
218
|
+
if namespace.present?
|
219
|
+
"/#{namespace}/#{file_name.pluralize}/rpc"
|
220
|
+
else
|
221
|
+
"/#{file_name.pluralize}/rpc"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def authentication_methods
|
226
|
+
if with_authentication?
|
227
|
+
skills.map { |skill| "#{skill}_secure" }
|
228
|
+
else
|
229
|
+
[]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def generate_skill_methods
|
234
|
+
skills.map do |skill|
|
235
|
+
method_name = skill.underscore
|
236
|
+
|
237
|
+
if with_authentication? && authentication_methods.include?("#{skill}_secure")
|
238
|
+
<<~RUBY
|
239
|
+
# #{skill.humanize} skill method (authenticated)
|
240
|
+
a2a_method "#{method_name}" do |params|
|
241
|
+
# TODO: Implement #{skill.humanize.downcase} functionality
|
242
|
+
{
|
243
|
+
skill: "#{skill}",
|
244
|
+
message: "#{skill.humanize} functionality not yet implemented",
|
245
|
+
params: params,
|
246
|
+
user: current_user_info
|
247
|
+
}
|
248
|
+
end
|
249
|
+
RUBY
|
250
|
+
else
|
251
|
+
<<~RUBY
|
252
|
+
# #{skill.humanize} skill method
|
253
|
+
a2a_method "#{method_name}" do |params|
|
254
|
+
# TODO: Implement #{skill.humanize.downcase} functionality
|
255
|
+
{
|
256
|
+
skill: "#{skill}",
|
257
|
+
message: "#{skill.humanize} functionality not yet implemented",
|
258
|
+
params: params
|
259
|
+
}
|
260
|
+
end
|
261
|
+
RUBY
|
262
|
+
end
|
263
|
+
end.join("\n\n")
|
264
|
+
end
|
265
|
+
|
266
|
+
def generate_skill_definitions
|
267
|
+
skills.map do |skill|
|
268
|
+
<<~RUBY
|
269
|
+
a2a_skill "#{skill}" do |skill|
|
270
|
+
skill.description = "#{skill.humanize} functionality"
|
271
|
+
skill.tags = ["#{skill}", "generated"]
|
272
|
+
skill.examples = [
|
273
|
+
{
|
274
|
+
input: { action: "#{skill}" },
|
275
|
+
output: { result: "#{skill} completed" }
|
276
|
+
}
|
277
|
+
]
|
278
|
+
end
|
279
|
+
RUBY
|
280
|
+
end.join("\n\n")
|
281
|
+
end
|
282
|
+
|
283
|
+
def authentication_config
|
284
|
+
if with_authentication?
|
285
|
+
auth_methods = authentication_methods.map { |m| "\"#{m}\"" }.join(", ")
|
286
|
+
<<~RUBY
|
287
|
+
|
288
|
+
# Configure authentication for specific methods
|
289
|
+
a2a_authenticate :devise, methods: [#{auth_methods}]
|
290
|
+
RUBY
|
291
|
+
else
|
292
|
+
""
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def agent_description
|
297
|
+
if skills.any?
|
298
|
+
"A2A agent that provides #{skills.map(&:humanize).join(', ')} functionality"
|
299
|
+
else
|
300
|
+
"A2A agent for #{class_name.humanize.downcase} operations"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def agent_tags
|
305
|
+
base_tags = [class_name.underscore, "generated"]
|
306
|
+
(base_tags + skills).uniq
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/base"
|
5
|
+
|
6
|
+
##
|
7
|
+
# Rails generator for installing A2A integration
|
8
|
+
#
|
9
|
+
# This generator sets up the basic A2A configuration and files needed
|
10
|
+
# for Rails integration.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# rails generate a2a:install
|
14
|
+
# rails generate a2a:install --with-authentication
|
15
|
+
# rails generate a2a:install --storage=redis
|
16
|
+
#
|
17
|
+
module A2A
|
18
|
+
module Rails
|
19
|
+
module Generators
|
20
|
+
class InstallGenerator < Rails::Generators::Base
|
21
|
+
source_root File.expand_path("templates", __dir__)
|
22
|
+
|
23
|
+
class_option :with_authentication, type: :boolean, default: false,
|
24
|
+
desc: "Generate authentication configuration"
|
25
|
+
|
26
|
+
class_option :storage, type: :string, default: "memory",
|
27
|
+
desc: "Storage backend (memory, database, redis)"
|
28
|
+
|
29
|
+
class_option :mount_path, type: :string, default: "/a2a",
|
30
|
+
desc: "Mount path for A2A endpoints"
|
31
|
+
|
32
|
+
class_option :skip_routes, type: :boolean, default: false,
|
33
|
+
desc: "Skip adding A2A routes"
|
34
|
+
|
35
|
+
class_option :skip_initializer, type: :boolean, default: false,
|
36
|
+
desc: "Skip creating initializer file"
|
37
|
+
|
38
|
+
desc "Install A2A Rails integration"
|
39
|
+
|
40
|
+
def create_initializer
|
41
|
+
return if options[:skip_initializer]
|
42
|
+
|
43
|
+
template "initializer.rb", "config/initializers/a2a.rb"
|
44
|
+
say "Created A2A initializer", :green
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_routes
|
48
|
+
return if options[:skip_routes]
|
49
|
+
|
50
|
+
route_content = generate_route_content
|
51
|
+
|
52
|
+
if File.exist?("config/routes.rb")
|
53
|
+
inject_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
|
54
|
+
route_content
|
55
|
+
end
|
56
|
+
say "Added A2A routes", :green
|
57
|
+
else
|
58
|
+
say "Could not find config/routes.rb", :red
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_application_controller_example
|
63
|
+
return unless options[:with_authentication]
|
64
|
+
|
65
|
+
template "application_controller_with_auth.rb",
|
66
|
+
"app/controllers/concerns/a2a_authentication.rb"
|
67
|
+
say "Created A2A authentication concern", :green
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_example_agent
|
71
|
+
template "example_agent_controller.rb",
|
72
|
+
"app/controllers/example_agent_controller.rb"
|
73
|
+
say "Created example A2A agent controller", :green
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_storage_configuration
|
77
|
+
case options[:storage]
|
78
|
+
when "database"
|
79
|
+
create_database_storage
|
80
|
+
when "redis"
|
81
|
+
create_redis_storage
|
82
|
+
else
|
83
|
+
# Memory storage is default, no additional setup needed
|
84
|
+
say "Using in-memory storage (default)", :yellow
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def add_gem_dependencies
|
89
|
+
gem_content = []
|
90
|
+
|
91
|
+
case options[:storage]
|
92
|
+
when "database"
|
93
|
+
gem_content << "# A2A database storage dependencies"
|
94
|
+
gem_content << "gem 'activerecord', '>= 6.0'"
|
95
|
+
when "redis"
|
96
|
+
gem_content << "# A2A Redis storage dependencies"
|
97
|
+
gem_content << "gem 'redis', '~> 5.0'"
|
98
|
+
gem_content << "gem 'connection_pool', '~> 2.4'"
|
99
|
+
end
|
100
|
+
|
101
|
+
if options[:with_authentication]
|
102
|
+
gem_content << "# A2A authentication dependencies"
|
103
|
+
gem_content << "gem 'jwt', '~> 2.0'"
|
104
|
+
end
|
105
|
+
|
106
|
+
return unless gem_content.any?
|
107
|
+
|
108
|
+
append_to_file "Gemfile" do
|
109
|
+
"\n# A2A Ruby SDK dependencies\n#{gem_content.join("\n")}\n"
|
110
|
+
end
|
111
|
+
say "Added gem dependencies to Gemfile", :green
|
112
|
+
say "Run 'bundle install' to install new dependencies", :yellow
|
113
|
+
end
|
114
|
+
|
115
|
+
def show_post_install_instructions
|
116
|
+
say "\n#{'=' * 60}", :green
|
117
|
+
say "A2A Rails integration installed successfully!", :green
|
118
|
+
say "=" * 60, :green
|
119
|
+
|
120
|
+
say "\nNext steps:", :yellow
|
121
|
+
say "1. Run 'bundle install' if new gems were added"
|
122
|
+
say "2. Review and customize config/initializers/a2a.rb"
|
123
|
+
say "3. Check the example agent at app/controllers/example_agent_controller.rb"
|
124
|
+
|
125
|
+
if options[:storage] == "database"
|
126
|
+
say "4. Run 'rails generate a2a:migration' to create database tables"
|
127
|
+
say "5. Run 'rails db:migrate' to apply migrations"
|
128
|
+
end
|
129
|
+
|
130
|
+
say "\nA2A endpoints will be available at:", :yellow
|
131
|
+
say " #{options[:mount_path]}/rpc (JSON-RPC)"
|
132
|
+
say " #{options[:mount_path]}/agent-card (Agent Card)"
|
133
|
+
say " #{options[:mount_path]}/health (Health Check)"
|
134
|
+
|
135
|
+
say "\nFor more information, visit: https://a2a-protocol.org/sdk/ruby/", :blue
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def generate_route_content
|
141
|
+
mount_path = options[:mount_path]
|
142
|
+
|
143
|
+
<<~RUBY
|
144
|
+
|
145
|
+
# A2A Protocol endpoints
|
146
|
+
mount A2A::Rails::Engine => "#{mount_path}"
|
147
|
+
|
148
|
+
RUBY
|
149
|
+
end
|
150
|
+
|
151
|
+
def create_database_storage
|
152
|
+
template "migration.rb",
|
153
|
+
"db/migrate/#{migration_timestamp}_create_a2a_tables.rb"
|
154
|
+
say "Created A2A database migration", :green
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_redis_storage
|
158
|
+
template "redis_config.yml", "config/redis.yml"
|
159
|
+
say "Created Redis configuration", :green
|
160
|
+
end
|
161
|
+
|
162
|
+
def migration_timestamp
|
163
|
+
Time.now.strftime("%Y%m%d%H%M%S")
|
164
|
+
end
|
165
|
+
|
166
|
+
def authentication_strategy
|
167
|
+
if options[:with_authentication]
|
168
|
+
# Try to detect existing authentication gems
|
169
|
+
if gem_exists?("devise")
|
170
|
+
"devise"
|
171
|
+
elsif gem_exists?("jwt")
|
172
|
+
"jwt"
|
173
|
+
else
|
174
|
+
"api_key"
|
175
|
+
end
|
176
|
+
else
|
177
|
+
"none"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def gem_exists?(gem_name)
|
182
|
+
File.read("Gemfile").include?(gem_name) if File.exist?("Gemfile")
|
183
|
+
rescue StandardError
|
184
|
+
false
|
185
|
+
end
|
186
|
+
|
187
|
+
def storage_backend
|
188
|
+
options[:storage]
|
189
|
+
end
|
190
|
+
|
191
|
+
def mount_path
|
192
|
+
options[:mount_path]
|
193
|
+
end
|
194
|
+
|
195
|
+
def with_authentication?
|
196
|
+
options[:with_authentication]
|
197
|
+
end
|
198
|
+
|
199
|
+
def rails_version
|
200
|
+
::Rails.version
|
201
|
+
end
|
202
|
+
|
203
|
+
def a2a_version
|
204
|
+
A2A::VERSION
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|