legate 0.1.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.
Files changed (317) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +345 -0
  4. data/bin/legate +13 -0
  5. data/examples/00_quickstart.rb +51 -0
  6. data/examples/01_simple_agent.rb +105 -0
  7. data/examples/02_multi_tool_agent.rb +140 -0
  8. data/examples/03_custom_tool.rb +93 -0
  9. data/examples/04_agent_instructions.rb +84 -0
  10. data/examples/05_state_and_sessions.rb +91 -0
  11. data/examples/06_callbacks.rb +186 -0
  12. data/examples/07_async_jobs.rb +112 -0
  13. data/examples/08_loop_agent.rb +197 -0
  14. data/examples/09_sequential_workflow.rb +40 -0
  15. data/examples/10_parallel_workflow.rb +34 -0
  16. data/examples/11_agent_delegation.rb +24 -0
  17. data/examples/12_http_client_tool.rb +156 -0
  18. data/examples/13_authentication.rb +220 -0
  19. data/examples/14_mcp_client.rb +154 -0
  20. data/examples/15_mcp_server.rb +79 -0
  21. data/examples/16_webhooks.rb +91 -0
  22. data/examples/README_sequential_agents.md +164 -0
  23. data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
  24. data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
  25. data/examples/advanced/auth/excon_middleware.rb +317 -0
  26. data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
  27. data/examples/advanced/auth/fiber_auth_example.rb +281 -0
  28. data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
  29. data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
  30. data/examples/advanced/auth/oauth2_auth.rb +419 -0
  31. data/examples/advanced/auth/oidc_auth.rb +514 -0
  32. data/examples/advanced/auth/openweather_api.rb +251 -0
  33. data/examples/advanced/auth/openweather_tool.rb +153 -0
  34. data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
  35. data/examples/advanced/auth/service_account.rb +135 -0
  36. data/examples/advanced/auth/test_with_httpbin.rb +202 -0
  37. data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
  38. data/examples/advanced/callback_monitoring.rb +679 -0
  39. data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
  40. data/examples/advanced/mas/loop_workflow.rb +28 -0
  41. data/examples/advanced/mas/mock_planner.rb +77 -0
  42. data/examples/advanced/mas/proper_delegation_example.rb +276 -0
  43. data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
  44. data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
  45. data/examples/advanced/mcp/mcp_server_async.rb +76 -0
  46. data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
  47. data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
  48. data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
  49. data/examples/advanced/random_calculator.rb +104 -0
  50. data/examples/advanced/sleep_agent.rb +153 -0
  51. data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
  52. data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
  53. data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
  54. data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
  55. data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
  56. data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
  57. data/examples/tools/oauth2_example.rb +136 -0
  58. data/examples/tools/sleepy_tool.rb +42 -0
  59. data/lib/legate/activity_log.rb +71 -0
  60. data/lib/legate/agent.rb +959 -0
  61. data/lib/legate/agent_code_generator.rb +185 -0
  62. data/lib/legate/agent_definition.rb +812 -0
  63. data/lib/legate/agentic/decision.rb +49 -0
  64. data/lib/legate/agentic/loop.rb +134 -0
  65. data/lib/legate/agentic.rb +5 -0
  66. data/lib/legate/agents/loop_agent.rb +248 -0
  67. data/lib/legate/agents/parallel_agent.rb +163 -0
  68. data/lib/legate/agents/sequential_agent.rb +190 -0
  69. data/lib/legate/agents.rb +14 -0
  70. data/lib/legate/auth/config.rb +148 -0
  71. data/lib/legate/auth/coordinator.rb +218 -0
  72. data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
  73. data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
  74. data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
  75. data/lib/legate/auth/credential.rb +157 -0
  76. data/lib/legate/auth/encryption.rb +108 -0
  77. data/lib/legate/auth/error.rb +94 -0
  78. data/lib/legate/auth/exchanged_credential.rb +180 -0
  79. data/lib/legate/auth/excon_middleware.rb +285 -0
  80. data/lib/legate/auth/http_client_utils.rb +364 -0
  81. data/lib/legate/auth/manager.rb +531 -0
  82. data/lib/legate/auth/manager_store.rb +394 -0
  83. data/lib/legate/auth/middleware_factory.rb +290 -0
  84. data/lib/legate/auth/runner.rb +279 -0
  85. data/lib/legate/auth/scheme.rb +125 -0
  86. data/lib/legate/auth/schemes/api_key.rb +212 -0
  87. data/lib/legate/auth/schemes/google_service_account.rb +108 -0
  88. data/lib/legate/auth/schemes/http_bearer.rb +98 -0
  89. data/lib/legate/auth/schemes/oauth2.rb +396 -0
  90. data/lib/legate/auth/schemes/openid_connect.rb +346 -0
  91. data/lib/legate/auth/schemes/service_account.rb +388 -0
  92. data/lib/legate/auth/schemes.rb +40 -0
  93. data/lib/legate/auth/token_manager.rb +362 -0
  94. data/lib/legate/auth/token_store.rb +86 -0
  95. data/lib/legate/auth/tool_context_extension.rb +97 -0
  96. data/lib/legate/auth/tool_integration.rb +188 -0
  97. data/lib/legate/auth/url_guard.rb +81 -0
  98. data/lib/legate/auth.rb +453 -0
  99. data/lib/legate/callbacks/callback_context.rb +71 -0
  100. data/lib/legate/cli/agent_commands.rb +950 -0
  101. data/lib/legate/cli/auth_commands.rb +520 -0
  102. data/lib/legate/cli/base_command.rb +24 -0
  103. data/lib/legate/cli/deployment_commands.rb +934 -0
  104. data/lib/legate/cli/output_helper.rb +108 -0
  105. data/lib/legate/cli/session_commands.rb +138 -0
  106. data/lib/legate/cli/skaffold_commands.rb +223 -0
  107. data/lib/legate/cli/tool_commands.rb +261 -0
  108. data/lib/legate/cli/web_commands.rb +182 -0
  109. data/lib/legate/cli.rb +40 -0
  110. data/lib/legate/configuration/webhooks.rb +113 -0
  111. data/lib/legate/configuration.rb +39 -0
  112. data/lib/legate/definition_store.rb +23 -0
  113. data/lib/legate/errors.rb +118 -0
  114. data/lib/legate/event.rb +161 -0
  115. data/lib/legate/gemini_ai_beta_patch.rb +39 -0
  116. data/lib/legate/generators/agent_generator.rb +412 -0
  117. data/lib/legate/generators/code_validator.rb +48 -0
  118. data/lib/legate/generators/legate/install_generator.rb +35 -0
  119. data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
  120. data/lib/legate/generators/legate/templates/initializer.rb +18 -0
  121. data/lib/legate/generators/runtime_tool_loader.rb +76 -0
  122. data/lib/legate/generators/tool_generator.rb +408 -0
  123. data/lib/legate/generators.rb +11 -0
  124. data/lib/legate/global_definition_registry.rb +506 -0
  125. data/lib/legate/global_tool_manager.rb +135 -0
  126. data/lib/legate/llm/adapter.rb +69 -0
  127. data/lib/legate/llm/gemini.rb +172 -0
  128. data/lib/legate/llm/ollama.rb +80 -0
  129. data/lib/legate/llm.rb +34 -0
  130. data/lib/legate/mcp/client.rb +320 -0
  131. data/lib/legate/mcp/connection/sse.rb +292 -0
  132. data/lib/legate/mcp/connection/stdio.rb +273 -0
  133. data/lib/legate/mcp/connection_manager.rb +103 -0
  134. data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
  135. data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
  136. data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
  137. data/lib/legate/mcp/tool_wrapper.rb +138 -0
  138. data/lib/legate/mcp/util/schema_converter.rb +134 -0
  139. data/lib/legate/mcp.rb +23 -0
  140. data/lib/legate/plan_executor.rb +375 -0
  141. data/lib/legate/planner.rb +839 -0
  142. data/lib/legate/rails/railtie.rb +43 -0
  143. data/lib/legate/rails.rb +9 -0
  144. data/lib/legate/redaction.rb +32 -0
  145. data/lib/legate/session.rb +299 -0
  146. data/lib/legate/session_service/active_record.rb +300 -0
  147. data/lib/legate/session_service/base.rb +68 -0
  148. data/lib/legate/session_service/event_broadcast.rb +74 -0
  149. data/lib/legate/session_service/in_memory.rb +188 -0
  150. data/lib/legate/tool/metadata_dsl.rb +122 -0
  151. data/lib/legate/tool.rb +276 -0
  152. data/lib/legate/tool_code_generator.rb +103 -0
  153. data/lib/legate/tool_context.rb +350 -0
  154. data/lib/legate/tool_loader.rb +39 -0
  155. data/lib/legate/tool_registry.rb +73 -0
  156. data/lib/legate/tool_result.rb +61 -0
  157. data/lib/legate/tools/agent_tool.rb +187 -0
  158. data/lib/legate/tools/base/http_client.rb +319 -0
  159. data/lib/legate/tools/base/safe_url.rb +56 -0
  160. data/lib/legate/tools/base_async_job_tool.rb +91 -0
  161. data/lib/legate/tools/calculator.rb +89 -0
  162. data/lib/legate/tools/cat_facts.rb +81 -0
  163. data/lib/legate/tools/check_job_status_tool.rb +48 -0
  164. data/lib/legate/tools/current_time_tool.rb +64 -0
  165. data/lib/legate/tools/echo.rb +43 -0
  166. data/lib/legate/tools/http_request_tool.rb +105 -0
  167. data/lib/legate/tools/random_number_tool.rb +64 -0
  168. data/lib/legate/tools/read_webpage_tool.rb +92 -0
  169. data/lib/legate/tools/sleepy_tool.rb +74 -0
  170. data/lib/legate/tools/webhook_tool.rb +146 -0
  171. data/lib/legate/version.rb +5 -0
  172. data/lib/legate/web/app.rb +984 -0
  173. data/lib/legate/web/public/css/main.css +4980 -0
  174. data/lib/legate/web/public/images/favicon-256.png +0 -0
  175. data/lib/legate/web/public/images/favicon-32.png +0 -0
  176. data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
  177. data/lib/legate/web/public/images/legate-logo-light.png +0 -0
  178. data/lib/legate/web/public/js/legate.js +616 -0
  179. data/lib/legate/web/public/styles/main.scss +4402 -0
  180. data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
  181. data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
  182. data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
  183. data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
  184. data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
  185. data/lib/legate/web/routes/api_routes.rb +56 -0
  186. data/lib/legate/web/routes/authentication_routes.rb +1541 -0
  187. data/lib/legate/web/routes/core_routes.rb +111 -0
  188. data/lib/legate/web/routes/documentation_routes.rb +220 -0
  189. data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
  190. data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
  191. data/lib/legate/web/sass_compiler.rb +73 -0
  192. data/lib/legate/web/views/_active_session_info.slim +25 -0
  193. data/lib/legate/web/views/_activity_list.slim +55 -0
  194. data/lib/legate/web/views/_agent_card.slim +56 -0
  195. data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
  196. data/lib/legate/web/views/_agent_status_controls.slim +71 -0
  197. data/lib/legate/web/views/_agent_tool_table.slim +74 -0
  198. data/lib/legate/web/views/_chat_message.slim +95 -0
  199. data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
  200. data/lib/legate/web/views/_display_agent_description.slim +11 -0
  201. data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
  202. data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
  203. data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
  204. data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
  205. data/lib/legate/web/views/_display_agent_model.slim +17 -0
  206. data/lib/legate/web/views/_display_agent_name.slim +42 -0
  207. data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
  208. data/lib/legate/web/views/_display_agent_type.slim +65 -0
  209. data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
  210. data/lib/legate/web/views/_edit_agent_description.slim +16 -0
  211. data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
  212. data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
  213. data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
  214. data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
  215. data/lib/legate/web/views/_edit_agent_model.slim +23 -0
  216. data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
  217. data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
  218. data/lib/legate/web/views/_edit_agent_type.slim +67 -0
  219. data/lib/legate/web/views/_session_error.slim +4 -0
  220. data/lib/legate/web/views/_skeleton.slim +69 -0
  221. data/lib/legate/web/views/_tool_card.slim +9 -0
  222. data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
  223. data/lib/legate/web/views/agent.slim +436 -0
  224. data/lib/legate/web/views/agent_auth.slim +562 -0
  225. data/lib/legate/web/views/agents.slim +369 -0
  226. data/lib/legate/web/views/auth.slim +112 -0
  227. data/lib/legate/web/views/auth_credential_detail.slim +327 -0
  228. data/lib/legate/web/views/auth_credentials.slim +261 -0
  229. data/lib/legate/web/views/auth_debug.slim +94 -0
  230. data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
  231. data/lib/legate/web/views/auth_mapping_new.slim +123 -0
  232. data/lib/legate/web/views/auth_mappings.slim +120 -0
  233. data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
  234. data/lib/legate/web/views/auth_schemes.slim +259 -0
  235. data/lib/legate/web/views/auth_test.slim +418 -0
  236. data/lib/legate/web/views/chat.slim +192 -0
  237. data/lib/legate/web/views/docs_index.slim +105 -0
  238. data/lib/legate/web/views/docs_show.slim +105 -0
  239. data/lib/legate/web/views/error_404.slim +5 -0
  240. data/lib/legate/web/views/index.slim +148 -0
  241. data/lib/legate/web/views/layout.slim +144 -0
  242. data/lib/legate/web/views/tool_detail.slim +87 -0
  243. data/lib/legate/web/views/tools.slim +50 -0
  244. data/lib/legate/web/webhook_listener.rb +367 -0
  245. data/lib/legate/web.rb +9 -0
  246. data/lib/legate.rb +220 -0
  247. data/public/docs/advanced/callbacks.md +828 -0
  248. data/public/docs/advanced/mcp_schema_conversion.md +59 -0
  249. data/public/docs/authentication/api_reference/config.md +210 -0
  250. data/public/docs/authentication/api_reference/credential.md +246 -0
  251. data/public/docs/authentication/api_reference/encryption.md +218 -0
  252. data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
  253. data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
  254. data/public/docs/authentication/api_reference/index.md +30 -0
  255. data/public/docs/authentication/api_reference/scheme.md +250 -0
  256. data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
  257. data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
  258. data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
  259. data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
  260. data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
  261. data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
  262. data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
  263. data/public/docs/authentication/api_reference/token_manager.md +221 -0
  264. data/public/docs/authentication/api_reference/token_store.md +146 -0
  265. data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
  266. data/public/docs/authentication/guides/api_key.md +190 -0
  267. data/public/docs/authentication/guides/bearer.md +172 -0
  268. data/public/docs/authentication/guides/configuration.md +255 -0
  269. data/public/docs/authentication/guides/custom_flow.md +523 -0
  270. data/public/docs/authentication/guides/index.md +24 -0
  271. data/public/docs/authentication/guides/migration.md +435 -0
  272. data/public/docs/authentication/guides/oauth2.md +252 -0
  273. data/public/docs/authentication/guides/oidc.md +241 -0
  274. data/public/docs/authentication/guides/overview.md +155 -0
  275. data/public/docs/authentication/guides/secure_storage.md +301 -0
  276. data/public/docs/authentication/guides/service_account.md +228 -0
  277. data/public/docs/authentication/guides/token_lifecycle.md +295 -0
  278. data/public/docs/authentication/guides/web_ui_integration.md +504 -0
  279. data/public/docs/authentication/index.md +58 -0
  280. data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
  281. data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
  282. data/public/docs/authentication/troubleshooting/index.md +11 -0
  283. data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
  284. data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
  285. data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
  286. data/public/docs/cli/legate_cli_usage.md +363 -0
  287. data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
  288. data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
  289. data/public/docs/core_concepts/legate_configuration.md +116 -0
  290. data/public/docs/core_concepts/legate_definition_store.md +102 -0
  291. data/public/docs/core_concepts/legate_planner.md +94 -0
  292. data/public/docs/core_concepts/legate_session_service.md +104 -0
  293. data/public/docs/error_handling/legate_error_handling.md +122 -0
  294. data/public/docs/examples.md +199 -0
  295. data/public/docs/getting_started.md +111 -0
  296. data/public/docs/guides/agentic_agents.md +137 -0
  297. data/public/docs/guides/ai_code_generators.md +437 -0
  298. data/public/docs/guides/auto_loading.md +326 -0
  299. data/public/docs/guides/configuring_agent_webhooks.md +219 -0
  300. data/public/docs/guides/http_client_usage.md +264 -0
  301. data/public/docs/guides/llm_providers.md +137 -0
  302. data/public/docs/guides/mcp_client_integration.md +232 -0
  303. data/public/docs/guides/mcp_server_exposure.md +206 -0
  304. data/public/docs/guides/rails_integration.md +128 -0
  305. data/public/docs/guides/sending_outbound_webhooks.md +227 -0
  306. data/public/docs/guides/streaming.md +112 -0
  307. data/public/docs/guides/webhooks.md +288 -0
  308. data/public/docs/introduction.md +51 -0
  309. data/public/docs/multi_agent_systems/advanced_features.md +57 -0
  310. data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
  311. data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
  312. data/public/docs/multi_agent_systems/state_management.md +47 -0
  313. data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
  314. data/public/docs/tools/legate_built_in_tools.md +332 -0
  315. data/public/docs/tools/legate_tools_and_registry.md +263 -0
  316. data/public/docs/web_ui/legate_web_ui.md +137 -0
  317. metadata +823 -0
@@ -0,0 +1,530 @@
1
+ # File: lib/legate/web/routes/agent_authentication_routes.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Legate
5
+ module Web
6
+ module AgentAuthenticationRoutes
7
+ def self.registered(app)
8
+ # Add helper methods to the app
9
+ app.helpers do
10
+ # Helper method to get agent authentication status
11
+ def get_agent_auth_status(agent_name)
12
+ definition_store = instance_variable_get(:@definition_store)
13
+ return { status: :error, message: 'Definition store unavailable' } unless definition_store
14
+
15
+ begin
16
+ agent_definition = definition_store.get_definition(agent_name)
17
+ return { status: :error, message: 'Agent not found' } unless agent_definition
18
+
19
+ auth_manager = Legate::Auth::Manager.instance
20
+ scheme_assignments = agent_definition[:auth_scheme_assignments] || {}
21
+ credential_assignments = agent_definition[:auth_credential_assignments] || {}
22
+ url_mappings = agent_definition[:auth_url_mappings] || []
23
+
24
+ # Check if agent has any authentication configured
25
+ has_auth = !scheme_assignments.empty? || !credential_assignments.empty? || !url_mappings.empty?
26
+
27
+ unless has_auth
28
+ return {
29
+ status: :warning,
30
+ message: 'No authentication configured - using global defaults',
31
+ details: {
32
+ scheme_assignments: 0,
33
+ credential_assignments: 0,
34
+ url_mappings: 0,
35
+ global_mappings: auth_manager.instance_variable_get(:@url_mappings)&.size || 0
36
+ }
37
+ }
38
+ end
39
+
40
+ # Validate assigned schemes and credentials exist
41
+ issues = []
42
+ scheme_assignments.each do |service, scheme_name|
43
+ issues << "Scheme '#{scheme_name}' not found for service '#{service}'" unless auth_manager.get_scheme(scheme_name.to_sym)
44
+ end
45
+
46
+ credential_assignments.each do |service, credential_name|
47
+ issues << "Credential '#{credential_name}' not found for service '#{service}'" unless auth_manager.get_credential(credential_name.to_sym)
48
+ end
49
+
50
+ if issues.any?
51
+ return {
52
+ status: :error,
53
+ message: 'Authentication configuration issues found',
54
+ details: { issues: issues }
55
+ }
56
+ end
57
+
58
+ {
59
+ status: :success,
60
+ message: 'Authentication properly configured',
61
+ details: {
62
+ scheme_assignments: scheme_assignments.size,
63
+ credential_assignments: credential_assignments.size,
64
+ url_mappings: url_mappings.size
65
+ }
66
+ }
67
+ rescue StandardError => e
68
+ { status: :error, message: "Error checking authentication status: #{e.message}" }
69
+ end
70
+ end
71
+
72
+ # Helper method to get available authentication options for an agent
73
+ def get_agent_auth_options
74
+ auth_manager = Legate::Auth::Manager.instance
75
+ schemes = auth_manager.instance_variable_get(:@schemes) || {}
76
+ credentials = auth_manager.instance_variable_get(:@credentials) || {}
77
+
78
+ {
79
+ schemes: schemes.map { |name, scheme|
80
+ {
81
+ name: name,
82
+ type: scheme.scheme_type,
83
+ description: get_scheme_description(scheme)
84
+ }
85
+ },
86
+ credentials: credentials.map { |name, credential|
87
+ {
88
+ name: name,
89
+ type: credential.auth_type,
90
+ description: get_credential_description(credential)
91
+ }
92
+ }
93
+ }
94
+ end
95
+
96
+ # Helper method to test agent authentication in context
97
+ def test_agent_authentication(agent_name, test_options = {})
98
+ definition_store = instance_variable_get(:@definition_store)
99
+ return { success: false, error: 'Definition store unavailable' } unless definition_store
100
+
101
+ begin
102
+ agent_definition = definition_store.get_definition(agent_name)
103
+ return { success: false, error: 'Agent not found' } unless agent_definition
104
+
105
+ auth_manager = Legate::Auth::Manager.instance
106
+ scheme_assignments = agent_definition[:auth_scheme_assignments] || {}
107
+ credential_assignments = agent_definition[:auth_credential_assignments] || {}
108
+ url_mappings = agent_definition[:auth_url_mappings] || []
109
+
110
+ test_results = {
111
+ success: true,
112
+ tests: [],
113
+ agent_name: agent_name,
114
+ has_agent_auth: !scheme_assignments.empty? || !credential_assignments.empty? || !url_mappings.empty?
115
+ }
116
+
117
+ # Test 1: Configuration validation
118
+ if test_results[:has_agent_auth]
119
+ config_issues = []
120
+
121
+ scheme_assignments.each do |service, scheme_name|
122
+ scheme = auth_manager.get_scheme(scheme_name.to_sym)
123
+ config_issues << "Missing scheme '#{scheme_name}'" unless scheme
124
+ end
125
+
126
+ credential_assignments.each do |service, credential_name|
127
+ credential = auth_manager.get_credential(credential_name.to_sym)
128
+ config_issues << "Missing credential '#{credential_name}'" unless credential
129
+ end
130
+
131
+ if config_issues.empty?
132
+ test_results[:tests] << {
133
+ name: 'Agent Authentication Configuration',
134
+ status: 'passed',
135
+ message: 'All assigned schemes and credentials are available'
136
+ }
137
+ else
138
+ test_results[:success] = false
139
+ test_results[:tests] << {
140
+ name: 'Agent Authentication Configuration',
141
+ status: 'failed',
142
+ message: "Configuration issues: #{config_issues.join(', ')}"
143
+ }
144
+ end
145
+ else
146
+ test_results[:tests] << {
147
+ name: 'Agent Authentication Configuration',
148
+ status: 'warning',
149
+ message: 'No agent-specific authentication configured - using global defaults'
150
+ }
151
+ end
152
+
153
+ # Test 2: URL mapping resolution (if test URL provided)
154
+ if test_options[:test_url]
155
+ url = test_options[:test_url]
156
+ resolved_auth = resolve_agent_authentication(agent_name, url)
157
+
158
+ test_results[:tests] << if resolved_auth[:scheme] && resolved_auth[:credential]
159
+ {
160
+ name: 'URL Authentication Resolution',
161
+ status: 'passed',
162
+ message: "URL '#{url}' resolves to scheme '#{resolved_auth[:scheme]}' with credential '#{resolved_auth[:credential]}'"
163
+ }
164
+ else
165
+ {
166
+ name: 'URL Authentication Resolution',
167
+ status: 'warning',
168
+ message: "No authentication resolved for URL '#{url}'"
169
+ }
170
+ end
171
+ end
172
+
173
+ test_results
174
+ rescue StandardError => e
175
+ { success: false, error: "Error testing agent authentication: #{e.message}" }
176
+ end
177
+ end
178
+
179
+ # Helper method to resolve authentication for an agent and URL
180
+ def resolve_agent_authentication(agent_name, url)
181
+ definition_store = instance_variable_get(:@definition_store)
182
+ return { scheme: nil, credential: nil, source: :error } unless definition_store
183
+
184
+ begin
185
+ agent_definition = definition_store.get_definition(agent_name)
186
+ return { scheme: nil, credential: nil, source: :error } unless agent_definition
187
+
188
+ auth_manager = Legate::Auth::Manager.instance
189
+
190
+ # First check agent-specific URL mappings
191
+ agent_url_mappings = agent_definition[:auth_url_mappings] || []
192
+ agent_url_mappings.each do |mapping|
193
+ pattern = mapping['pattern'] || mapping[:pattern]
194
+ next unless pattern
195
+
196
+ matched = if pattern.is_a?(Regexp)
197
+ !!(url =~ pattern)
198
+ elsif pattern.include?('*')
199
+ regex = Regexp.new('^' + Regexp.escape(pattern).gsub('\\*', '.*') + '$')
200
+ !!(url =~ regex)
201
+ else
202
+ url == pattern
203
+ end
204
+
205
+ next unless matched
206
+
207
+ scheme_name = mapping['scheme_name'] || mapping[:scheme_name]
208
+ credential_name = mapping['credential_name'] || mapping[:credential_name]
209
+ return {
210
+ scheme: scheme_name,
211
+ credential: credential_name,
212
+ source: :agent_mapping
213
+ }
214
+ end
215
+
216
+ # Then check global URL mappings
217
+ global_mappings = auth_manager.instance_variable_get(:@url_mappings) || []
218
+ global_mappings.each do |mapping|
219
+ pattern = mapping[:pattern]
220
+ next unless pattern
221
+
222
+ matched = if pattern.is_a?(Regexp)
223
+ !!(url =~ pattern)
224
+ elsif pattern.to_s.include?('*')
225
+ regex = Regexp.new('^' + Regexp.escape(pattern.to_s).gsub('\\*', '.*') + '$')
226
+ !!(url =~ regex)
227
+ else
228
+ url == pattern.to_s
229
+ end
230
+
231
+ if matched
232
+ return {
233
+ scheme: mapping[:scheme_name],
234
+ credential: mapping[:credential_name],
235
+ source: :global_mapping
236
+ }
237
+ end
238
+ end
239
+
240
+ { scheme: nil, credential: nil, source: :none }
241
+ rescue StandardError => e
242
+ { scheme: nil, credential: nil, source: :error }
243
+ end
244
+ end
245
+ end
246
+
247
+ # GET /agents/:name/auth - Agent-specific authentication configuration
248
+ app.get '/agents/:name/auth' do |name|
249
+ logger.info("GET /agents/#{name}/auth route handler entered (from AgentAuthenticationRoutes)")
250
+ content_type :html
251
+
252
+ definition_store = instance_variable_get(:@definition_store)
253
+ halt 503, 'Definition Store unavailable.' unless definition_store
254
+
255
+ agent_definition = definition_store.get_definition(name)
256
+ halt 404, 'Agent not found.' unless agent_definition
257
+
258
+ auth_status = get_agent_auth_status(name)
259
+ auth_options = get_agent_auth_options
260
+
261
+ agent_auth_data = {
262
+ name: name,
263
+ description: agent_definition[:description],
264
+ auth_status: auth_status,
265
+ scheme_assignments: agent_definition[:auth_scheme_assignments] || {},
266
+ credential_assignments: agent_definition[:auth_credential_assignments] || {},
267
+ url_mappings: agent_definition[:auth_url_mappings] || [],
268
+ available_schemes: auth_options[:schemes],
269
+ available_credentials: auth_options[:credentials]
270
+ }
271
+
272
+ instance_variable_set(:@agent_auth_data, agent_auth_data)
273
+ slim :agent_auth
274
+ rescue StandardError => e
275
+ logger.error("Error in /agents/#{name}/auth route (from AgentAuthenticationRoutes): #{e.class} - #{e.message}")
276
+ halt 500, "Error loading agent authentication configuration: #{e.message}"
277
+ end
278
+
279
+ # POST /agents/:name/auth/assign - Assign authentication to agent
280
+ app.post '/agents/:name/auth/assign' do |name|
281
+ logger.info("POST /agents/#{name}/auth/assign route handler entered (from AgentAuthenticationRoutes)")
282
+ content_type :json
283
+
284
+ definition_store = instance_variable_get(:@definition_store)
285
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
286
+
287
+ agent_definition = definition_store.get_definition(name)
288
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
289
+
290
+ assignment_type = params[:assignment_type]
291
+ service = params[:service]
292
+ auth_name = params[:auth_name]
293
+
294
+ halt 400, { error: 'Assignment type is required' }.to_json unless assignment_type
295
+ halt 400, { error: 'Service is required' }.to_json unless service
296
+ halt 400, { error: 'Authentication name is required' }.to_json unless auth_name
297
+
298
+ begin
299
+ case assignment_type
300
+ when 'scheme'
301
+ # Verify scheme exists
302
+ auth_manager = Legate::Auth::Manager.instance
303
+ scheme = auth_manager.get_scheme(auth_name.to_sym)
304
+ halt 400, { error: "Scheme '#{auth_name}' not found" }.to_json unless scheme
305
+
306
+ # Update agent's scheme assignments
307
+ scheme_assignments = agent_definition[:auth_scheme_assignments] || {}
308
+ scheme_assignments[service] = auth_name
309
+
310
+ definition_store.update_definition(name, { auth_scheme_assignments: scheme_assignments })
311
+
312
+ when 'credential'
313
+ # Verify credential exists
314
+ auth_manager = Legate::Auth::Manager.instance
315
+ credential = auth_manager.get_credential(auth_name.to_sym)
316
+ halt 400, { error: "Credential '#{auth_name}' not found" }.to_json unless credential
317
+
318
+ # Update agent's credential assignments
319
+ credential_assignments = agent_definition[:auth_credential_assignments] || {}
320
+ credential_assignments[service] = auth_name
321
+
322
+ definition_store.update_definition(name, { auth_credential_assignments: credential_assignments })
323
+
324
+ else
325
+ halt 400, { error: "Invalid assignment type: #{assignment_type}" }.to_json
326
+ end
327
+
328
+ logger.info("Successfully assigned #{assignment_type} '#{auth_name}' to service '#{service}' for agent '#{name}'")
329
+ { success: true, message: "#{assignment_type.capitalize} '#{auth_name}' assigned to service '#{service}'" }.to_json
330
+ rescue StandardError => e
331
+ logger.error("Error assigning authentication to agent '#{name}': #{e.class} - #{e.message}")
332
+ halt 500, { error: "Failed to assign authentication: #{e.message}" }.to_json
333
+ end
334
+ end
335
+
336
+ # DELETE /agents/:name/auth/remove - Remove authentication from agent
337
+ app.delete '/agents/:name/auth/remove' do |name|
338
+ logger.info("DELETE /agents/#{name}/auth/remove route handler entered (from AgentAuthenticationRoutes)")
339
+ content_type :json
340
+
341
+ definition_store = instance_variable_get(:@definition_store)
342
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
343
+
344
+ agent_definition = definition_store.get_definition(name)
345
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
346
+
347
+ assignment_type = params[:assignment_type]
348
+ service = params[:service]
349
+
350
+ halt 400, { error: 'Assignment type is required' }.to_json unless assignment_type
351
+ halt 400, { error: 'Service is required' }.to_json unless service
352
+
353
+ begin
354
+ case assignment_type
355
+ when 'scheme'
356
+ scheme_assignments = agent_definition[:auth_scheme_assignments] || {}
357
+ removed_scheme = scheme_assignments.delete(service)
358
+
359
+ if removed_scheme
360
+ definition_store.update_definition(name, { auth_scheme_assignments: scheme_assignments })
361
+ logger.info("Successfully removed scheme assignment for service '#{service}' from agent '#{name}'")
362
+ { success: true, message: "Scheme assignment for service '#{service}' removed" }.to_json
363
+ else
364
+ { success: false, message: "No scheme assignment found for service '#{service}'" }.to_json
365
+ end
366
+
367
+ when 'credential'
368
+ credential_assignments = agent_definition[:auth_credential_assignments] || {}
369
+ removed_credential = credential_assignments.delete(service)
370
+
371
+ if removed_credential
372
+ definition_store.update_definition(name, { auth_credential_assignments: credential_assignments })
373
+ logger.info("Successfully removed credential assignment for service '#{service}' from agent '#{name}'")
374
+ { success: true, message: "Credential assignment for service '#{service}' removed" }.to_json
375
+ else
376
+ { success: false, message: "No credential assignment found for service '#{service}'" }.to_json
377
+ end
378
+
379
+ else
380
+ halt 400, { error: "Invalid assignment type: #{assignment_type}" }.to_json
381
+ end
382
+ rescue StandardError => e
383
+ logger.error("Error removing authentication from agent '#{name}': #{e.class} - #{e.message}")
384
+ halt 500, { error: "Failed to remove authentication: #{e.message}" }.to_json
385
+ end
386
+ end
387
+
388
+ # POST /agents/:name/auth/test - Test authentication in agent context
389
+ app.post '/agents/:name/auth/test' do |name|
390
+ logger.info("POST /agents/#{name}/auth/test route handler entered (from AgentAuthenticationRoutes)")
391
+ content_type :json
392
+
393
+ definition_store = instance_variable_get(:@definition_store)
394
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
395
+
396
+ agent_definition = definition_store.get_definition(name)
397
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
398
+
399
+ begin
400
+ test_options = {}
401
+ test_options[:test_url] = params[:test_url] if params[:test_url] && !params[:test_url].empty?
402
+
403
+ test_results = test_agent_authentication(name, test_options)
404
+
405
+ logger.info("Agent authentication test completed for '#{name}': #{test_results[:success] ? 'PASSED' : 'FAILED'}")
406
+ test_results.to_json
407
+ rescue StandardError => e
408
+ logger.error("Error testing agent authentication for '#{name}': #{e.class} - #{e.message}")
409
+ halt 500, { error: "Failed to test agent authentication: #{e.message}" }.to_json
410
+ end
411
+ end
412
+
413
+ # GET /agents/:name/auth/status - Get authentication status for agent
414
+ app.get '/agents/:name/auth/status' do |name|
415
+ logger.info("GET /agents/#{name}/auth/status route handler entered (from AgentAuthenticationRoutes)")
416
+ content_type :json
417
+
418
+ definition_store = instance_variable_get(:@definition_store)
419
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
420
+
421
+ agent_definition = definition_store.get_definition(name)
422
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
423
+
424
+ begin
425
+ auth_status = get_agent_auth_status(name)
426
+ auth_status.to_json
427
+ rescue StandardError => e
428
+ logger.error("Error getting agent authentication status for '#{name}': #{e.class} - #{e.message}")
429
+ halt 500, { error: "Failed to get authentication status: #{e.message}" }.to_json
430
+ end
431
+ end
432
+
433
+ # POST /agents/:name/auth/url-mapping - Add URL mapping for agent
434
+ app.post '/agents/:name/auth/url-mapping' do |name|
435
+ logger.info("POST /agents/#{name}/auth/url-mapping route handler entered (from AgentAuthenticationRoutes)")
436
+ content_type :json
437
+
438
+ definition_store = instance_variable_get(:@definition_store)
439
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
440
+
441
+ agent_definition = definition_store.get_definition(name)
442
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
443
+
444
+ pattern = params[:pattern]
445
+ pattern_type = params[:pattern_type]
446
+ scheme_name = params[:scheme_name]
447
+ credential_name = params[:credential_name]
448
+ priority = params[:priority]&.to_i || 1
449
+
450
+ halt 400, { error: 'Pattern is required' }.to_json unless pattern && !pattern.empty?
451
+ halt 400, { error: 'Scheme name is required' }.to_json unless scheme_name && !scheme_name.empty?
452
+ halt 400, { error: 'Credential name is required' }.to_json unless credential_name && !credential_name.empty?
453
+
454
+ begin
455
+ # Validate pattern
456
+ if pattern_type == 'regex'
457
+ begin
458
+ Regexp.new(pattern)
459
+ rescue RegexpError
460
+ halt 400, { error: 'Invalid regex pattern' }.to_json
461
+ end
462
+ end
463
+
464
+ # Verify scheme and credential exist
465
+ auth_manager = Legate::Auth::Manager.instance
466
+ scheme = auth_manager.get_scheme(scheme_name.to_sym)
467
+ credential = auth_manager.get_credential(credential_name.to_sym)
468
+
469
+ halt 400, { error: "Scheme '#{scheme_name}' not found" }.to_json unless scheme
470
+ halt 400, { error: "Credential '#{credential_name}' not found" }.to_json unless credential
471
+
472
+ # Check compatibility
473
+ compatible_types = get_compatible_credential_types(scheme.scheme_type)
474
+ halt 400, { error: 'Scheme and credential are not compatible' }.to_json unless compatible_types.include?(credential.auth_type.to_s) || compatible_types.include?(credential.auth_type)
475
+
476
+ # Add URL mapping to agent
477
+ url_mappings = agent_definition[:auth_url_mappings] || []
478
+ new_mapping = {
479
+ pattern: pattern_type == 'regex' ? pattern : pattern,
480
+ pattern_type: pattern_type,
481
+ scheme_name: scheme_name,
482
+ credential_name: credential_name,
483
+ priority: priority,
484
+ active: true
485
+ }
486
+
487
+ url_mappings << new_mapping
488
+ definition_store.update_definition(name, { auth_url_mappings: url_mappings })
489
+
490
+ logger.info("Successfully added URL mapping for agent '#{name}': #{pattern} -> #{scheme_name}/#{credential_name}")
491
+ { success: true, message: 'URL mapping added successfully', mapping_id: url_mappings.size - 1 }.to_json
492
+ rescue StandardError => e
493
+ logger.error("Error adding URL mapping for agent '#{name}': #{e.class} - #{e.message}")
494
+ halt 500, { error: "Failed to add URL mapping: #{e.message}" }.to_json
495
+ end
496
+ end
497
+
498
+ # DELETE /agents/:name/auth/url-mapping/:id - Remove URL mapping from agent
499
+ app.delete '/agents/:name/auth/url-mapping/:id' do |name, mapping_id|
500
+ logger.info("DELETE /agents/#{name}/auth/url-mapping/#{mapping_id} route handler entered (from AgentAuthenticationRoutes)")
501
+ content_type :json
502
+
503
+ definition_store = instance_variable_get(:@definition_store)
504
+ halt 503, { error: 'Definition Store unavailable' }.to_json unless definition_store
505
+
506
+ agent_definition = definition_store.get_definition(name)
507
+ halt 404, { error: 'Agent not found' }.to_json unless agent_definition
508
+
509
+ begin
510
+ url_mappings = agent_definition[:auth_url_mappings] || []
511
+ id = mapping_id.to_i
512
+
513
+ if id >= 0 && id < url_mappings.size
514
+ removed_mapping = url_mappings.delete_at(id)
515
+ definition_store.update_definition(name, { auth_url_mappings: url_mappings })
516
+
517
+ logger.info("Successfully removed URL mapping #{id} from agent '#{name}'")
518
+ { success: true, message: 'URL mapping removed successfully' }.to_json
519
+ else
520
+ { success: false, message: 'URL mapping not found' }.to_json
521
+ end
522
+ rescue StandardError => e
523
+ logger.error("Error removing URL mapping from agent '#{name}': #{e.class} - #{e.message}")
524
+ halt 500, { error: "Failed to remove URL mapping: #{e.message}" }.to_json
525
+ end
526
+ end
527
+ end
528
+ end
529
+ end
530
+ end