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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +345 -0
- data/bin/legate +13 -0
- data/examples/00_quickstart.rb +51 -0
- data/examples/01_simple_agent.rb +105 -0
- data/examples/02_multi_tool_agent.rb +140 -0
- data/examples/03_custom_tool.rb +93 -0
- data/examples/04_agent_instructions.rb +84 -0
- data/examples/05_state_and_sessions.rb +91 -0
- data/examples/06_callbacks.rb +186 -0
- data/examples/07_async_jobs.rb +112 -0
- data/examples/08_loop_agent.rb +197 -0
- data/examples/09_sequential_workflow.rb +40 -0
- data/examples/10_parallel_workflow.rb +34 -0
- data/examples/11_agent_delegation.rb +24 -0
- data/examples/12_http_client_tool.rb +156 -0
- data/examples/13_authentication.rb +220 -0
- data/examples/14_mcp_client.rb +154 -0
- data/examples/15_mcp_server.rb +79 -0
- data/examples/16_webhooks.rb +91 -0
- data/examples/README_sequential_agents.md +164 -0
- data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
- data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
- data/examples/advanced/auth/excon_middleware.rb +317 -0
- data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
- data/examples/advanced/auth/fiber_auth_example.rb +281 -0
- data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
- data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
- data/examples/advanced/auth/oauth2_auth.rb +419 -0
- data/examples/advanced/auth/oidc_auth.rb +514 -0
- data/examples/advanced/auth/openweather_api.rb +251 -0
- data/examples/advanced/auth/openweather_tool.rb +153 -0
- data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
- data/examples/advanced/auth/service_account.rb +135 -0
- data/examples/advanced/auth/test_with_httpbin.rb +202 -0
- data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
- data/examples/advanced/callback_monitoring.rb +679 -0
- data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
- data/examples/advanced/mas/loop_workflow.rb +28 -0
- data/examples/advanced/mas/mock_planner.rb +77 -0
- data/examples/advanced/mas/proper_delegation_example.rb +276 -0
- data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
- data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
- data/examples/advanced/mcp/mcp_server_async.rb +76 -0
- data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
- data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
- data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
- data/examples/advanced/random_calculator.rb +104 -0
- data/examples/advanced/sleep_agent.rb +153 -0
- data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
- data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
- data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
- data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
- data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
- data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
- data/examples/tools/oauth2_example.rb +136 -0
- data/examples/tools/sleepy_tool.rb +42 -0
- data/lib/legate/activity_log.rb +71 -0
- data/lib/legate/agent.rb +959 -0
- data/lib/legate/agent_code_generator.rb +185 -0
- data/lib/legate/agent_definition.rb +812 -0
- data/lib/legate/agentic/decision.rb +49 -0
- data/lib/legate/agentic/loop.rb +134 -0
- data/lib/legate/agentic.rb +5 -0
- data/lib/legate/agents/loop_agent.rb +248 -0
- data/lib/legate/agents/parallel_agent.rb +163 -0
- data/lib/legate/agents/sequential_agent.rb +190 -0
- data/lib/legate/agents.rb +14 -0
- data/lib/legate/auth/config.rb +148 -0
- data/lib/legate/auth/coordinator.rb +218 -0
- data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
- data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
- data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
- data/lib/legate/auth/credential.rb +157 -0
- data/lib/legate/auth/encryption.rb +108 -0
- data/lib/legate/auth/error.rb +94 -0
- data/lib/legate/auth/exchanged_credential.rb +180 -0
- data/lib/legate/auth/excon_middleware.rb +285 -0
- data/lib/legate/auth/http_client_utils.rb +364 -0
- data/lib/legate/auth/manager.rb +531 -0
- data/lib/legate/auth/manager_store.rb +394 -0
- data/lib/legate/auth/middleware_factory.rb +290 -0
- data/lib/legate/auth/runner.rb +279 -0
- data/lib/legate/auth/scheme.rb +125 -0
- data/lib/legate/auth/schemes/api_key.rb +212 -0
- data/lib/legate/auth/schemes/google_service_account.rb +108 -0
- data/lib/legate/auth/schemes/http_bearer.rb +98 -0
- data/lib/legate/auth/schemes/oauth2.rb +396 -0
- data/lib/legate/auth/schemes/openid_connect.rb +346 -0
- data/lib/legate/auth/schemes/service_account.rb +388 -0
- data/lib/legate/auth/schemes.rb +40 -0
- data/lib/legate/auth/token_manager.rb +362 -0
- data/lib/legate/auth/token_store.rb +86 -0
- data/lib/legate/auth/tool_context_extension.rb +97 -0
- data/lib/legate/auth/tool_integration.rb +188 -0
- data/lib/legate/auth/url_guard.rb +81 -0
- data/lib/legate/auth.rb +453 -0
- data/lib/legate/callbacks/callback_context.rb +71 -0
- data/lib/legate/cli/agent_commands.rb +950 -0
- data/lib/legate/cli/auth_commands.rb +520 -0
- data/lib/legate/cli/base_command.rb +24 -0
- data/lib/legate/cli/deployment_commands.rb +934 -0
- data/lib/legate/cli/output_helper.rb +108 -0
- data/lib/legate/cli/session_commands.rb +138 -0
- data/lib/legate/cli/skaffold_commands.rb +223 -0
- data/lib/legate/cli/tool_commands.rb +261 -0
- data/lib/legate/cli/web_commands.rb +182 -0
- data/lib/legate/cli.rb +40 -0
- data/lib/legate/configuration/webhooks.rb +113 -0
- data/lib/legate/configuration.rb +39 -0
- data/lib/legate/definition_store.rb +23 -0
- data/lib/legate/errors.rb +118 -0
- data/lib/legate/event.rb +161 -0
- data/lib/legate/gemini_ai_beta_patch.rb +39 -0
- data/lib/legate/generators/agent_generator.rb +412 -0
- data/lib/legate/generators/code_validator.rb +48 -0
- data/lib/legate/generators/legate/install_generator.rb +35 -0
- data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
- data/lib/legate/generators/legate/templates/initializer.rb +18 -0
- data/lib/legate/generators/runtime_tool_loader.rb +76 -0
- data/lib/legate/generators/tool_generator.rb +408 -0
- data/lib/legate/generators.rb +11 -0
- data/lib/legate/global_definition_registry.rb +506 -0
- data/lib/legate/global_tool_manager.rb +135 -0
- data/lib/legate/llm/adapter.rb +69 -0
- data/lib/legate/llm/gemini.rb +172 -0
- data/lib/legate/llm/ollama.rb +80 -0
- data/lib/legate/llm.rb +34 -0
- data/lib/legate/mcp/client.rb +320 -0
- data/lib/legate/mcp/connection/sse.rb +292 -0
- data/lib/legate/mcp/connection/stdio.rb +273 -0
- data/lib/legate/mcp/connection_manager.rb +103 -0
- data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
- data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
- data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
- data/lib/legate/mcp/tool_wrapper.rb +138 -0
- data/lib/legate/mcp/util/schema_converter.rb +134 -0
- data/lib/legate/mcp.rb +23 -0
- data/lib/legate/plan_executor.rb +375 -0
- data/lib/legate/planner.rb +839 -0
- data/lib/legate/rails/railtie.rb +43 -0
- data/lib/legate/rails.rb +9 -0
- data/lib/legate/redaction.rb +32 -0
- data/lib/legate/session.rb +299 -0
- data/lib/legate/session_service/active_record.rb +300 -0
- data/lib/legate/session_service/base.rb +68 -0
- data/lib/legate/session_service/event_broadcast.rb +74 -0
- data/lib/legate/session_service/in_memory.rb +188 -0
- data/lib/legate/tool/metadata_dsl.rb +122 -0
- data/lib/legate/tool.rb +276 -0
- data/lib/legate/tool_code_generator.rb +103 -0
- data/lib/legate/tool_context.rb +350 -0
- data/lib/legate/tool_loader.rb +39 -0
- data/lib/legate/tool_registry.rb +73 -0
- data/lib/legate/tool_result.rb +61 -0
- data/lib/legate/tools/agent_tool.rb +187 -0
- data/lib/legate/tools/base/http_client.rb +319 -0
- data/lib/legate/tools/base/safe_url.rb +56 -0
- data/lib/legate/tools/base_async_job_tool.rb +91 -0
- data/lib/legate/tools/calculator.rb +89 -0
- data/lib/legate/tools/cat_facts.rb +81 -0
- data/lib/legate/tools/check_job_status_tool.rb +48 -0
- data/lib/legate/tools/current_time_tool.rb +64 -0
- data/lib/legate/tools/echo.rb +43 -0
- data/lib/legate/tools/http_request_tool.rb +105 -0
- data/lib/legate/tools/random_number_tool.rb +64 -0
- data/lib/legate/tools/read_webpage_tool.rb +92 -0
- data/lib/legate/tools/sleepy_tool.rb +74 -0
- data/lib/legate/tools/webhook_tool.rb +146 -0
- data/lib/legate/version.rb +5 -0
- data/lib/legate/web/app.rb +984 -0
- data/lib/legate/web/public/css/main.css +4980 -0
- data/lib/legate/web/public/images/favicon-256.png +0 -0
- data/lib/legate/web/public/images/favicon-32.png +0 -0
- data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
- data/lib/legate/web/public/images/legate-logo-light.png +0 -0
- data/lib/legate/web/public/js/legate.js +616 -0
- data/lib/legate/web/public/styles/main.scss +4402 -0
- data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
- data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
- data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
- data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
- data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
- data/lib/legate/web/routes/api_routes.rb +56 -0
- data/lib/legate/web/routes/authentication_routes.rb +1541 -0
- data/lib/legate/web/routes/core_routes.rb +111 -0
- data/lib/legate/web/routes/documentation_routes.rb +220 -0
- data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
- data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
- data/lib/legate/web/sass_compiler.rb +73 -0
- data/lib/legate/web/views/_active_session_info.slim +25 -0
- data/lib/legate/web/views/_activity_list.slim +55 -0
- data/lib/legate/web/views/_agent_card.slim +56 -0
- data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
- data/lib/legate/web/views/_agent_status_controls.slim +71 -0
- data/lib/legate/web/views/_agent_tool_table.slim +74 -0
- data/lib/legate/web/views/_chat_message.slim +95 -0
- data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
- data/lib/legate/web/views/_display_agent_description.slim +11 -0
- data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
- data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
- data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
- data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
- data/lib/legate/web/views/_display_agent_model.slim +17 -0
- data/lib/legate/web/views/_display_agent_name.slim +42 -0
- data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
- data/lib/legate/web/views/_display_agent_type.slim +65 -0
- data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
- data/lib/legate/web/views/_edit_agent_description.slim +16 -0
- data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
- data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
- data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
- data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
- data/lib/legate/web/views/_edit_agent_model.slim +23 -0
- data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
- data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
- data/lib/legate/web/views/_edit_agent_type.slim +67 -0
- data/lib/legate/web/views/_session_error.slim +4 -0
- data/lib/legate/web/views/_skeleton.slim +69 -0
- data/lib/legate/web/views/_tool_card.slim +9 -0
- data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
- data/lib/legate/web/views/agent.slim +436 -0
- data/lib/legate/web/views/agent_auth.slim +562 -0
- data/lib/legate/web/views/agents.slim +369 -0
- data/lib/legate/web/views/auth.slim +112 -0
- data/lib/legate/web/views/auth_credential_detail.slim +327 -0
- data/lib/legate/web/views/auth_credentials.slim +261 -0
- data/lib/legate/web/views/auth_debug.slim +94 -0
- data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
- data/lib/legate/web/views/auth_mapping_new.slim +123 -0
- data/lib/legate/web/views/auth_mappings.slim +120 -0
- data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
- data/lib/legate/web/views/auth_schemes.slim +259 -0
- data/lib/legate/web/views/auth_test.slim +418 -0
- data/lib/legate/web/views/chat.slim +192 -0
- data/lib/legate/web/views/docs_index.slim +105 -0
- data/lib/legate/web/views/docs_show.slim +105 -0
- data/lib/legate/web/views/error_404.slim +5 -0
- data/lib/legate/web/views/index.slim +148 -0
- data/lib/legate/web/views/layout.slim +144 -0
- data/lib/legate/web/views/tool_detail.slim +87 -0
- data/lib/legate/web/views/tools.slim +50 -0
- data/lib/legate/web/webhook_listener.rb +367 -0
- data/lib/legate/web.rb +9 -0
- data/lib/legate.rb +220 -0
- data/public/docs/advanced/callbacks.md +828 -0
- data/public/docs/advanced/mcp_schema_conversion.md +59 -0
- data/public/docs/authentication/api_reference/config.md +210 -0
- data/public/docs/authentication/api_reference/credential.md +246 -0
- data/public/docs/authentication/api_reference/encryption.md +218 -0
- data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
- data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
- data/public/docs/authentication/api_reference/index.md +30 -0
- data/public/docs/authentication/api_reference/scheme.md +250 -0
- data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
- data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
- data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
- data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
- data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
- data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
- data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
- data/public/docs/authentication/api_reference/token_manager.md +221 -0
- data/public/docs/authentication/api_reference/token_store.md +146 -0
- data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
- data/public/docs/authentication/guides/api_key.md +190 -0
- data/public/docs/authentication/guides/bearer.md +172 -0
- data/public/docs/authentication/guides/configuration.md +255 -0
- data/public/docs/authentication/guides/custom_flow.md +523 -0
- data/public/docs/authentication/guides/index.md +24 -0
- data/public/docs/authentication/guides/migration.md +435 -0
- data/public/docs/authentication/guides/oauth2.md +252 -0
- data/public/docs/authentication/guides/oidc.md +241 -0
- data/public/docs/authentication/guides/overview.md +155 -0
- data/public/docs/authentication/guides/secure_storage.md +301 -0
- data/public/docs/authentication/guides/service_account.md +228 -0
- data/public/docs/authentication/guides/token_lifecycle.md +295 -0
- data/public/docs/authentication/guides/web_ui_integration.md +504 -0
- data/public/docs/authentication/index.md +58 -0
- data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
- data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
- data/public/docs/authentication/troubleshooting/index.md +11 -0
- data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
- data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
- data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
- data/public/docs/cli/legate_cli_usage.md +363 -0
- data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
- data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
- data/public/docs/core_concepts/legate_configuration.md +116 -0
- data/public/docs/core_concepts/legate_definition_store.md +102 -0
- data/public/docs/core_concepts/legate_planner.md +94 -0
- data/public/docs/core_concepts/legate_session_service.md +104 -0
- data/public/docs/error_handling/legate_error_handling.md +122 -0
- data/public/docs/examples.md +199 -0
- data/public/docs/getting_started.md +111 -0
- data/public/docs/guides/agentic_agents.md +137 -0
- data/public/docs/guides/ai_code_generators.md +437 -0
- data/public/docs/guides/auto_loading.md +326 -0
- data/public/docs/guides/configuring_agent_webhooks.md +219 -0
- data/public/docs/guides/http_client_usage.md +264 -0
- data/public/docs/guides/llm_providers.md +137 -0
- data/public/docs/guides/mcp_client_integration.md +232 -0
- data/public/docs/guides/mcp_server_exposure.md +206 -0
- data/public/docs/guides/rails_integration.md +128 -0
- data/public/docs/guides/sending_outbound_webhooks.md +227 -0
- data/public/docs/guides/streaming.md +112 -0
- data/public/docs/guides/webhooks.md +288 -0
- data/public/docs/introduction.md +51 -0
- data/public/docs/multi_agent_systems/advanced_features.md +57 -0
- data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
- data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
- data/public/docs/multi_agent_systems/state_management.md +47 -0
- data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
- data/public/docs/tools/legate_built_in_tools.md +332 -0
- data/public/docs/tools/legate_tools_and_registry.md +263 -0
- data/public/docs/web_ui/legate_web_ui.md +137 -0
- metadata +823 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using HTTP Bearer authentication with Legate
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use HTTP Bearer authentication to make authenticated requests
|
|
7
|
+
# to an API that requires a bearer token in the Authorization header.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/13_authentication.rb [token]
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'legate'
|
|
14
|
+
|
|
15
|
+
# Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
|
|
16
|
+
# The library never reads .env on its own; an application must opt in.
|
|
17
|
+
Legate.load_environment
|
|
18
|
+
require 'legate/auth'
|
|
19
|
+
require 'legate/auth/schemes/http_bearer'
|
|
20
|
+
require 'json'
|
|
21
|
+
|
|
22
|
+
# Check if a bearer token was provided
|
|
23
|
+
bearer_token = ARGV[0] || ENV['BEARER_TOKEN']
|
|
24
|
+
unless bearer_token
|
|
25
|
+
puts 'Error: Please provide a bearer token as an argument'
|
|
26
|
+
puts 'Usage: ruby examples/13_authentication.rb [token]'
|
|
27
|
+
puts 'Alternatively, set the BEARER_TOKEN environment variable'
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
puts "Using Bearer Token: #{bearer_token[0..5]}..." + ('*' * 10)
|
|
32
|
+
|
|
33
|
+
# Create the Auth::Credential with the bearer token
|
|
34
|
+
credential = Legate::Auth::Credential.new(
|
|
35
|
+
auth_type: :http_bearer,
|
|
36
|
+
bearer_token: bearer_token
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Create a session service to manage authentication state
|
|
40
|
+
session_service = Legate::SessionService::InMemory.new
|
|
41
|
+
|
|
42
|
+
# Create a token store (optional but recommended for consistency)
|
|
43
|
+
token_store = Legate::Auth::TokenStore.new(session_service: session_service)
|
|
44
|
+
|
|
45
|
+
puts "\n=== Example 1: Basic HTTP Bearer Authentication ==="
|
|
46
|
+
# Create an HTTP Bearer scheme
|
|
47
|
+
bearer_scheme = Legate::Auth::Schemes::HTTPBearer.new
|
|
48
|
+
|
|
49
|
+
# For simple non-interactive schemes like HTTP Bearer, we can use the token directly
|
|
50
|
+
exchanged_token = bearer_scheme.exchange_token(credential)
|
|
51
|
+
puts 'Generated token for HTTP Bearer authentication'
|
|
52
|
+
|
|
53
|
+
# Example of making a request with the bearer token
|
|
54
|
+
require 'excon'
|
|
55
|
+
conn = Excon.new('https://httpbin.org')
|
|
56
|
+
|
|
57
|
+
# Prepare request with authentication
|
|
58
|
+
request = {
|
|
59
|
+
path: '/headers',
|
|
60
|
+
headers: {
|
|
61
|
+
'Accept' => 'application/json'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Apply authentication
|
|
66
|
+
request = bearer_scheme.apply_to_request(request, exchanged_token)
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
response = conn.get(request)
|
|
70
|
+
|
|
71
|
+
if response.status == 200
|
|
72
|
+
puts "Request succeeded with status: #{response.status}"
|
|
73
|
+
puts 'Headers received by server:'
|
|
74
|
+
JSON.parse(response.body)['headers'].each do |name, value|
|
|
75
|
+
puts " #{name}: #{value}"
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
puts "Request failed with status: #{response.status}"
|
|
79
|
+
puts "Error: #{response.body}"
|
|
80
|
+
end
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
puts "Request failed: #{e.message}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
puts "\n=== Example 2: HTTP Bearer with Expiry Simulation ==="
|
|
86
|
+
# In real-world scenarios, bearer tokens often expire
|
|
87
|
+
# This example simulates a token with expiry information
|
|
88
|
+
|
|
89
|
+
# Create a token with expiry information
|
|
90
|
+
expiring_token = Legate::Auth::ExchangedCredential.new(
|
|
91
|
+
auth_type: :http_bearer,
|
|
92
|
+
access_token: bearer_token,
|
|
93
|
+
bearer_token: bearer_token,
|
|
94
|
+
expires_in: 3600, # Token expires in 1 hour
|
|
95
|
+
token_type: 'Bearer'
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
puts "Token expires in: #{expiring_token[:expires_in]} seconds"
|
|
99
|
+
|
|
100
|
+
# Example of checking token expiry
|
|
101
|
+
if expiring_token.expired?
|
|
102
|
+
puts 'Token has expired and needs to be refreshed'
|
|
103
|
+
else
|
|
104
|
+
remaining = expiring_token.expires_at - Time.now
|
|
105
|
+
puts "Token is still valid for #{remaining.to_i} seconds"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
puts "\n=== Example 3: Using with the Legate Excon Middleware ==="
|
|
109
|
+
|
|
110
|
+
# Create the Excon middleware for HTTP Bearer authentication
|
|
111
|
+
middleware = Legate::Auth.create_middleware(
|
|
112
|
+
scheme: bearer_scheme,
|
|
113
|
+
credential: credential
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Create an Excon connection with the middleware
|
|
117
|
+
connection = Legate::Auth.create_connection(
|
|
118
|
+
'https://httpbin.org',
|
|
119
|
+
scheme: bearer_scheme,
|
|
120
|
+
credential: credential
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
begin
|
|
124
|
+
response = connection.get(
|
|
125
|
+
path: '/headers',
|
|
126
|
+
headers: { 'Accept' => 'application/json' }
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if response.status == 200
|
|
130
|
+
puts "Excon request succeeded with status: #{response.status}"
|
|
131
|
+
headers = JSON.parse(response.body)['headers']
|
|
132
|
+
puts 'Headers received by server:'
|
|
133
|
+
headers.each do |name, value|
|
|
134
|
+
puts " #{name}: #{value}"
|
|
135
|
+
end
|
|
136
|
+
else
|
|
137
|
+
puts "Excon request failed with status: #{response.status}"
|
|
138
|
+
puts "Error: #{response.body}"
|
|
139
|
+
end
|
|
140
|
+
rescue StandardError => e
|
|
141
|
+
puts "Excon request failed: #{e.message}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
puts "\n=== Example 4: Using with the Legate Tooling System ==="
|
|
145
|
+
# Example of a tool using bearer authentication
|
|
146
|
+
class BearerAuthExampleTool < Legate::Tool
|
|
147
|
+
include Legate::Tools::Base::HttpClient
|
|
148
|
+
|
|
149
|
+
tool_description 'Example tool demonstrating HTTP Bearer authentication'
|
|
150
|
+
|
|
151
|
+
parameter :endpoint,
|
|
152
|
+
type: :string,
|
|
153
|
+
description: 'API endpoint to call',
|
|
154
|
+
required: true
|
|
155
|
+
|
|
156
|
+
def initialize(options = {})
|
|
157
|
+
super()
|
|
158
|
+
@auth_scheme = options[:auth_scheme]
|
|
159
|
+
@auth_credential = options[:auth_credential]
|
|
160
|
+
setup_http_client(
|
|
161
|
+
base_url: 'https://httpbin.org',
|
|
162
|
+
options: {
|
|
163
|
+
connect_timeout: 3,
|
|
164
|
+
read_timeout: 3,
|
|
165
|
+
write_timeout: 3
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def perform_execution(params, _context)
|
|
173
|
+
# Prepare request with authentication
|
|
174
|
+
request = {
|
|
175
|
+
method: :get,
|
|
176
|
+
path: "/#{params[:endpoint]}",
|
|
177
|
+
headers: {
|
|
178
|
+
'Accept' => 'application/json'
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Apply authentication if configured
|
|
183
|
+
request = @auth_scheme.apply_to_request(request, @auth_credential) if @auth_scheme && @auth_credential
|
|
184
|
+
|
|
185
|
+
# Make the request
|
|
186
|
+
response = http_get(
|
|
187
|
+
request[:path],
|
|
188
|
+
headers: request[:headers]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Parse and return the response
|
|
192
|
+
data = JSON.parse(response.body)
|
|
193
|
+
{
|
|
194
|
+
status: :success,
|
|
195
|
+
endpoint: params[:endpoint],
|
|
196
|
+
headers: data['headers'],
|
|
197
|
+
data: data
|
|
198
|
+
}
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Create and use the example tool
|
|
203
|
+
example_tool = BearerAuthExampleTool.new(
|
|
204
|
+
auth_scheme: bearer_scheme,
|
|
205
|
+
auth_credential: credential
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
puts "\nTesting bearer auth tool with endpoint: headers"
|
|
209
|
+
begin
|
|
210
|
+
result = example_tool.execute(endpoint: 'headers')
|
|
211
|
+
puts 'Tool execution succeeded!'
|
|
212
|
+
puts 'Response headers:'
|
|
213
|
+
result[:headers].each do |name, value|
|
|
214
|
+
puts " #{name}: #{value}"
|
|
215
|
+
end
|
|
216
|
+
rescue StandardError => e
|
|
217
|
+
puts "Tool execution failed: #{e.message}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
puts "\nHTTP Bearer authentication example completed successfully!"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Using an Legate Agent as an MCP Client
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to configure an Legate::Agent to connect to an
|
|
7
|
+
# external MCP server (like the @modelcontextprotocol/server-filesystem) and use its tools.
|
|
8
|
+
#
|
|
9
|
+
# Key Concepts:
|
|
10
|
+
# - MCP Server Configuration: Defining how to connect to the external server (e.g., via stdio).
|
|
11
|
+
# - Selected Tool Names: Explicitly telling the agent which tools from the MCP server it is allowed to use.
|
|
12
|
+
# The names provided in `selected_tool_names` MUST exactly match the tool names exposed by the specific
|
|
13
|
+
# MCP server you are connecting to (check server logs or capabilities if unsure).
|
|
14
|
+
# - Runtime Start/Stop: Managing the connection lifecycle to the MCP server.
|
|
15
|
+
# - Using External Tools: Making requests that the agent's planner can map to the available MCP tools.
|
|
16
|
+
#
|
|
17
|
+
# Requires:
|
|
18
|
+
# - legate gem with MCP support installed
|
|
19
|
+
# - An external MCP server running. For testing, you can use:
|
|
20
|
+
# `npx @modelcontextprotocol/server-filesystem --stdio path/to/a/directory`
|
|
21
|
+
# (Replace `path/to/a/directory` with a real directory the server can access)
|
|
22
|
+
#
|
|
23
|
+
# To Run:
|
|
24
|
+
# 1. Start the external MCP server in one terminal (e.g., the filesystem server command above).
|
|
25
|
+
# 2. Ensure the directory specified in `mcp_server_config[:args]` exists and is accessible.
|
|
26
|
+
# 3. Run this script in another terminal: `bundle exec ruby examples/14_mcp_client.rb`
|
|
27
|
+
# 4. Observe the logs: The agent should initialize, connect to the MCP server, and list available tools
|
|
28
|
+
# (including the ones explicitly selected via `selected_tool_names`, like `:read_file` in this example).
|
|
29
|
+
# 5. The script will then attempt to run a task using the `read_file` tool from the MCP server.
|
|
30
|
+
|
|
31
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
32
|
+
require 'legate'
|
|
33
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
34
|
+
|
|
35
|
+
require 'legate/mcp' # Ensure MCP modules are loaded
|
|
36
|
+
|
|
37
|
+
# Configure Legate logger
|
|
38
|
+
ENV['LEGATE_LOG_LEVEL'] = 'DEBUG'
|
|
39
|
+
dirname = File.expand_path(File.dirname(File.dirname(__FILE__)))
|
|
40
|
+
|
|
41
|
+
# --- 1. Define a Native Legate Tool (Optional) ---
|
|
42
|
+
class NativeEchoTool < Legate::Tool
|
|
43
|
+
define_metadata(
|
|
44
|
+
name: :native_echo,
|
|
45
|
+
description: 'Echoes back the provided message (native Legate tool).',
|
|
46
|
+
parameters: {
|
|
47
|
+
message: { type: :string, required: true, description: 'Message to echo' }
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
def perform_execution(params, _context)
|
|
51
|
+
Legate.logger.info("[NativeEchoTool] Echoing: #{params[:message]}")
|
|
52
|
+
{ status: :success, result: "Native echo: #{params[:message]}" }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Register the native tool globally so it can be found by its name.
|
|
57
|
+
Legate::GlobalToolManager.register_tool(NativeEchoTool)
|
|
58
|
+
|
|
59
|
+
# --- 2. Configure the External MCP Server Connection ---
|
|
60
|
+
# NOTE: Adjust the command and args based on how you run *your* external server.
|
|
61
|
+
# This example assumes the filesystem server.
|
|
62
|
+
# Make sure the directory path exists and is accessible!
|
|
63
|
+
mcp_server_config = {
|
|
64
|
+
type: 'stdio',
|
|
65
|
+
command: 'npx', # Command to start the server
|
|
66
|
+
args: [ # Arguments for the command
|
|
67
|
+
'--',
|
|
68
|
+
'@modelcontextprotocol/server-filesystem',
|
|
69
|
+
# '--stdio',
|
|
70
|
+
dirname # <<< IMPORTANT: Change this to a real, accessible directory!
|
|
71
|
+
# Create this directory before running: mkdir /tmp/mcp_fs_test_dir
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
# Check if args include a placeholder path and warn if so
|
|
75
|
+
if mcp_server_config[:args].any? { |arg| arg.include?('path/to/') || arg.include?('tmp/mcp_fs_test_dir') }
|
|
76
|
+
Legate.logger.warn('MCP server config in example still uses a placeholder directory.')
|
|
77
|
+
Legate.logger.warn('Please edit examples/14_mcp_client.rb and set a real directory path in `mcp_server_config[:args]`.')
|
|
78
|
+
# Optionally exit if you want to enforce configuration
|
|
79
|
+
# exit(1)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# --- 3. Initialize the Legate Agent ---
|
|
83
|
+
Legate.logger.info('Initializing agent...')
|
|
84
|
+
|
|
85
|
+
mcp_client_agent_definition = Legate::AgentDefinition.new.define do |a|
|
|
86
|
+
a.name :mcp_client_agent
|
|
87
|
+
a.description 'An agent using native and external MCP tools.'
|
|
88
|
+
a.instruction 'You can echo messages natively and use filesystem tools from an MCP server, such as reading files.'
|
|
89
|
+
a.use_tool :native_echo # Native tool defined above
|
|
90
|
+
a.use_tool :read_file # Tool expected from the MCP server (e.g., from server-filesystem)
|
|
91
|
+
# Add MCP server configuration
|
|
92
|
+
a.mcp_servers mcp_server_config
|
|
93
|
+
# model_name: 'gemini-pro-1.5' # Optional: Specify model via a.model_name 'gemini-pro-1.5'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
my_agent = Legate::Agent.new(definition: mcp_client_agent_definition)
|
|
97
|
+
|
|
98
|
+
# --- 4. Start the Agent Runtime ---
|
|
99
|
+
# This connects to the MCP server and registers its tools.
|
|
100
|
+
Legate.logger.info('Starting agent runtime...')
|
|
101
|
+
my_agent.start
|
|
102
|
+
Legate.logger.info("Agent started. Available tool names: #{my_agent.tool_registry.tools.keys}")
|
|
103
|
+
Legate.logger.info("Full metadata: #{my_agent.available_tools_metadata.inspect}")
|
|
104
|
+
|
|
105
|
+
# --- 5. Run a Task (Optional Example) ---
|
|
106
|
+
# Uncomment this section to try running a task.
|
|
107
|
+
# Requires setting up a session service and ensuring the external server
|
|
108
|
+
# provides the tool mentioned in the user_input (e.g., `filesystem/readFile`).
|
|
109
|
+
|
|
110
|
+
puts "\n--- Running Task Example ---"
|
|
111
|
+
begin
|
|
112
|
+
session_service = Legate::SessionService::InMemory.new
|
|
113
|
+
session = session_service.create_session(app_name: 'mcp_client_test', user_id: 'test_user')
|
|
114
|
+
puts "Created session: #{session.id}"
|
|
115
|
+
|
|
116
|
+
# Create a dummy file for the filesystem tool to read
|
|
117
|
+
# Ensure the directory matches the one in mcp_server_config[:args]
|
|
118
|
+
dummy_file_path = "#{dirname}/hello.txt"
|
|
119
|
+
begin
|
|
120
|
+
File.write(dummy_file_path, 'Hello from Legate client example!')
|
|
121
|
+
puts "Created dummy file: #{dummy_file_path}"
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
puts "Warning: Could not create dummy file '#{dummy_file_path}': #{e.message}"
|
|
124
|
+
puts 'Filesystem tool example might fail.'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Input asking to use a tool likely provided by the filesystem server
|
|
128
|
+
# user_input = "What files are in the #{dirname}? What are the contents of hello.txt in #{dirname}?"
|
|
129
|
+
user_input = "Read the content of the file named 'hello.txt' in #{dirname} using the filesystem tool." # <<< MODIFIED: More specific input
|
|
130
|
+
puts "User Input: #{user_input}"
|
|
131
|
+
|
|
132
|
+
final_event = my_agent.run_task(
|
|
133
|
+
session_id: session.id,
|
|
134
|
+
user_input: user_input,
|
|
135
|
+
session_service: session_service
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
puts 'Final Agent Event:'
|
|
139
|
+
require 'pp' # Pretty print
|
|
140
|
+
pp final_event
|
|
141
|
+
rescue StandardError => e
|
|
142
|
+
puts "Error running task: #{e.message}"
|
|
143
|
+
puts e.backtrace
|
|
144
|
+
end
|
|
145
|
+
puts "--------------------------\n"
|
|
146
|
+
|
|
147
|
+
# Delete the dummy file after the task is complete
|
|
148
|
+
File.delete(dummy_file_path) if File.exist?(dummy_file_path)
|
|
149
|
+
|
|
150
|
+
#--- 6. Stop the Agent Runtime ---
|
|
151
|
+
# This disconnects from the MCP server.
|
|
152
|
+
Legate.logger.info('Stopping agent runtime...')
|
|
153
|
+
my_agent.stop
|
|
154
|
+
Legate.logger.info('Agent stopped.')
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# File: examples/15_mcp_server.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# --- Example: Exposing an Legate::Tool via MCP using fast-mcp ---
|
|
5
|
+
#
|
|
6
|
+
# This script demonstrates how to wrap an existing Legate::Tool
|
|
7
|
+
# (in this case, the built-in Calculator tool) and expose it
|
|
8
|
+
# on an MCP server run over STDIO using the fast-mcp gem.
|
|
9
|
+
#
|
|
10
|
+
# Prerequisites:
|
|
11
|
+
# - Run `bundle install` in the legate directory.
|
|
12
|
+
# - Ensure the `fast-mcp` gem is available (e.g., via Gemfile path).
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# 1. Run this script from the legate root directory:
|
|
16
|
+
# `bundle exec ruby examples/15_mcp_server.rb`
|
|
17
|
+
# 2. The script will start and wait for MCP JSON-RPC messages on STDIN.
|
|
18
|
+
# 3. Use an MCP client (like `mcp-client`, `mcp-inspector`, or another script
|
|
19
|
+
# using Legate::Mcp::Client) connected to this script's STDIO to interact.
|
|
20
|
+
#
|
|
21
|
+
# Example Interaction (using a generic client):
|
|
22
|
+
# -> {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}
|
|
23
|
+
# <- {"jsonrpc":"2.0","id":1,"result":{"capabilities":{},"serverInfo":{"name":"Legate Tool Server","version":"1.0"}}}
|
|
24
|
+
# -> {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
|
|
25
|
+
# <- {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"calculator","description":"Performs basic arithmetic operations.","inputSchema":{"type":"object","properties":{"a":{"type":"number","description":"First operand"},"b":{"type":"number","description":"Second operand"},"op":{"type":"string","description":"Operator (+, -, *, /)"}},"required":["a","b","op"]}}]}}
|
|
26
|
+
# -> {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"calculator","arguments":{"a":10,"b":5,"op":"*"}}}
|
|
27
|
+
# <- {"jsonrpc":"2.0","id":3,"result":50.0}
|
|
28
|
+
#
|
|
29
|
+
# -------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
32
|
+
require 'legate'
|
|
33
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
34
|
+
|
|
35
|
+
require 'legate/mcp' # Load Legate MCP modules
|
|
36
|
+
require 'legate/mcp/server/legate_tool_adapter' # Load the Tool adapter
|
|
37
|
+
require 'legate/tools/calculator' # Load the specific Legate tool we want to expose
|
|
38
|
+
require 'fast_mcp' # Load the fast-mcp library
|
|
39
|
+
|
|
40
|
+
Legate.configure do |config|
|
|
41
|
+
# Configure Legate logger level if desired (e.g., :debug for more verbose MCP logs)
|
|
42
|
+
# config.log_level = :debug
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# --- 1. Wrap the Legate::Tool ---
|
|
46
|
+
# Use the LegateToolAdapter to create a fast-mcp compatible class
|
|
47
|
+
begin
|
|
48
|
+
AdaptedCalculator = Legate::Mcp::Server::LegateToolAdapter.wrap(Legate::Tools::Calculator)
|
|
49
|
+
Legate.logger.info('Successfully wrapped Legate::Tools::Calculator for MCP.')
|
|
50
|
+
rescue StandardError => e
|
|
51
|
+
Legate.logger.fatal("Failed to wrap Legate Tool: #{e.message}")
|
|
52
|
+
exit(1)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# --- 2. Create fast-mcp Server Instance ---
|
|
56
|
+
# FastMcp::Server uses STDIO transport by default when calling #start
|
|
57
|
+
mcp_server = FastMcp::Server.new(
|
|
58
|
+
name: 'Legate Tool Server',
|
|
59
|
+
version: Legate::VERSION # Use Legate version
|
|
60
|
+
)
|
|
61
|
+
Legate.logger.info('Initialized FastMcp::Server.')
|
|
62
|
+
|
|
63
|
+
# --- 3. Register the Wrapped Tool ---
|
|
64
|
+
mcp_server.register_tool(AdaptedCalculator)
|
|
65
|
+
Legate.logger.info("Registered adapted '#{AdaptedCalculator.tool_name}' tool with fast-mcp server.")
|
|
66
|
+
|
|
67
|
+
# --- 4. Start the Server ---
|
|
68
|
+
Legate.logger.info('Starting MCP server on STDIO. Waiting for requests...')
|
|
69
|
+
puts '--- Legate MCP Tool Server (STDIO) Ready --- ' # Signal readiness besides logs
|
|
70
|
+
begin
|
|
71
|
+
mcp_server.start # This method typically blocks, listening on STDIN
|
|
72
|
+
rescue Interrupt
|
|
73
|
+
Legate.logger.info('Received interrupt, shutting down server.')
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
Legate.logger.fatal("MCP server crashed: #{e.class} - #{e.message}")
|
|
76
|
+
Legate.logger.fatal(e.backtrace.join("\n"))
|
|
77
|
+
ensure
|
|
78
|
+
Legate.logger.info('MCP server stopped.')
|
|
79
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Sending Webhooks with HMAC Signing
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use the built-in WebhookTool to send
|
|
7
|
+
# an HMAC-signed webhook payload to an external URL.
|
|
8
|
+
#
|
|
9
|
+
# Setup:
|
|
10
|
+
# 1. Go to https://webhook.site and copy your unique URL
|
|
11
|
+
# 2. Run: WEBHOOK_URL=https://webhook.site/your-uuid WEBHOOK_SECRET=my-secret \
|
|
12
|
+
# bundle exec ruby examples/16_webhooks.rb
|
|
13
|
+
# 3. Check webhook.site to see the received payload and signature header
|
|
14
|
+
#
|
|
15
|
+
# Run with: bundle exec ruby examples/16_webhooks.rb
|
|
16
|
+
|
|
17
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
18
|
+
|
|
19
|
+
require 'legate'
|
|
20
|
+
|
|
21
|
+
# Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
|
|
22
|
+
# The library never reads .env on its own; an application must opt in.
|
|
23
|
+
Legate.load_environment
|
|
24
|
+
require 'legate/tools/webhook_tool'
|
|
25
|
+
require 'openssl'
|
|
26
|
+
require 'json'
|
|
27
|
+
|
|
28
|
+
puts '--- Webhook Example ---'
|
|
29
|
+
|
|
30
|
+
# 1. Configuration
|
|
31
|
+
webhook_url = ENV['WEBHOOK_URL']
|
|
32
|
+
secret_key = ENV.fetch('WEBHOOK_SECRET', 'example-secret-key')
|
|
33
|
+
|
|
34
|
+
unless webhook_url
|
|
35
|
+
puts "\nUsage: WEBHOOK_URL=https://webhook.site/your-uuid bundle exec ruby examples/16_webhooks.rb"
|
|
36
|
+
puts "\nGet a free URL at https://webhook.site — then check the site to see your payload arrive."
|
|
37
|
+
puts "You can also set WEBHOOK_SECRET (defaults to 'example-secret-key')."
|
|
38
|
+
exit(1)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
puts "Target URL: #{webhook_url}"
|
|
42
|
+
|
|
43
|
+
# 2. Build the payload
|
|
44
|
+
payload_data = {
|
|
45
|
+
message: 'Hello from Legate!',
|
|
46
|
+
timestamp: Time.now.iso8601,
|
|
47
|
+
source: 'legate-webhook-example'
|
|
48
|
+
}
|
|
49
|
+
payload_json = payload_data.to_json
|
|
50
|
+
|
|
51
|
+
# 3. Calculate HMAC signature (SHA-256)
|
|
52
|
+
# The receiver can verify the payload integrity using the same secret
|
|
53
|
+
calculated_signature = OpenSSL::HMAC.hexdigest('sha256', secret_key, payload_json)
|
|
54
|
+
signature_header_value = "sha256=#{calculated_signature}"
|
|
55
|
+
|
|
56
|
+
puts "Payload: #{payload_json}"
|
|
57
|
+
puts "HMAC Signature: #{signature_header_value}"
|
|
58
|
+
|
|
59
|
+
# 4. Build headers
|
|
60
|
+
custom_headers = {
|
|
61
|
+
'Content-Type' => 'application/json',
|
|
62
|
+
'X-Hub-Signature-256' => signature_header_value
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# 5. Send the webhook using the built-in WebhookTool
|
|
66
|
+
webhook_tool = Legate::Tools::WebhookTool.new
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
puts "\nSending webhook..."
|
|
70
|
+
result = webhook_tool.execute(
|
|
71
|
+
url: webhook_url,
|
|
72
|
+
payload: payload_json,
|
|
73
|
+
headers: custom_headers
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if result[:status] == :success
|
|
77
|
+
response_status = result.dig(:result, :response_status)
|
|
78
|
+
response_body = result.dig(:result, :response_body)
|
|
79
|
+
puts 'Webhook sent successfully!'
|
|
80
|
+
puts " Response status: #{response_status}"
|
|
81
|
+
puts " Response body: #{response_body}"
|
|
82
|
+
else
|
|
83
|
+
puts "Webhook failed: #{result[:error_message]}"
|
|
84
|
+
end
|
|
85
|
+
rescue Legate::ToolError => e
|
|
86
|
+
puts "Tool error: #{e.message}"
|
|
87
|
+
rescue StandardError => e
|
|
88
|
+
puts "Unexpected error: #{e.message}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
puts "\n--- Example Complete ---"
|