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
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
// Legate web UI — framework JavaScript.
|
|
2
|
+
// Extracted from layout.slim. No server-side interpolation lives here;
|
|
3
|
+
// keep page-specific values in the views and call into these helpers.
|
|
4
|
+
|
|
5
|
+
// Add the CSRF token to every htmx request.
|
|
6
|
+
document.addEventListener('htmx:configRequest', function (e) {
|
|
7
|
+
var t = document.querySelector('meta[name="csrf-token"]');
|
|
8
|
+
if (t) e.detail.headers['X-CSRF-Token'] = t.content;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Mirror the same for raw fetch(): inject the token on same-origin
|
|
12
|
+
// mutating requests so individual call sites don't each have to remember
|
|
13
|
+
// it (and can't silently 403 when they forget).
|
|
14
|
+
(function () {
|
|
15
|
+
var nativeFetch = window.fetch;
|
|
16
|
+
if (!nativeFetch) return;
|
|
17
|
+
var SAFE = { GET: true, HEAD: true, OPTIONS: true };
|
|
18
|
+
window.fetch = function (resource, init) {
|
|
19
|
+
init = init || {};
|
|
20
|
+
var method = (init.method || (resource && resource.method) || 'GET').toUpperCase();
|
|
21
|
+
var url = (typeof resource === 'string') ? resource : ((resource && resource.url) || '');
|
|
22
|
+
var sameOrigin = (url.charAt(0) === '/' && url.charAt(1) !== '/') ||
|
|
23
|
+
url === window.location.origin || url.indexOf(window.location.origin + '/') === 0;
|
|
24
|
+
if (!SAFE[method] && sameOrigin) {
|
|
25
|
+
var meta = document.querySelector('meta[name="csrf-token"]');
|
|
26
|
+
if (meta) {
|
|
27
|
+
var headers = new Headers(init.headers || (typeof resource !== 'string' && resource.headers) || undefined);
|
|
28
|
+
if (!headers.has('X-CSRF-Token')) headers.set('X-CSRF-Token', meta.content);
|
|
29
|
+
init.headers = headers;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return nativeFetch.call(this, resource, init);
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
|
|
36
|
+
// Escape interpolated values before they go into an innerHTML sink.
|
|
37
|
+
window.escapeHtml = function (value) {
|
|
38
|
+
return String(value == null ? '' : value)
|
|
39
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
40
|
+
.replace(/"/g, '"').replace(/'/g, ''');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Global keyboard shortcut: Cmd/Ctrl+K to focus search
|
|
44
|
+
document.addEventListener('keydown', function(e) {
|
|
45
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
const searchInput = document.querySelector('#agent-search-input, .search-input, input[type="search"]');
|
|
48
|
+
if (searchInput) {
|
|
49
|
+
searchInput.focus();
|
|
50
|
+
searchInput.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Theme toggle function (global)
|
|
56
|
+
function toggleTheme() {
|
|
57
|
+
const current = document.documentElement.getAttribute('data-theme');
|
|
58
|
+
const next = current === 'dark' ? 'light' : 'dark';
|
|
59
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
60
|
+
localStorage.setItem('legate-theme', next);
|
|
61
|
+
// Update icon
|
|
62
|
+
const icon = document.querySelector('.theme-toggle i');
|
|
63
|
+
if (icon) {
|
|
64
|
+
icon.className = next === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
|
|
65
|
+
}
|
|
66
|
+
// Update highlight.js theme
|
|
67
|
+
updateHljsTheme(next);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Update highlight.js stylesheet based on theme
|
|
71
|
+
function updateHljsTheme(theme) {
|
|
72
|
+
const lightSheet = document.querySelector('link[data-hljs-theme="light"]');
|
|
73
|
+
const darkSheet = document.querySelector('link[data-hljs-theme="dark"]');
|
|
74
|
+
if (lightSheet && darkSheet) {
|
|
75
|
+
if (theme === 'dark') {
|
|
76
|
+
lightSheet.media = 'not all';
|
|
77
|
+
darkSheet.media = 'all';
|
|
78
|
+
} else {
|
|
79
|
+
lightSheet.media = 'all';
|
|
80
|
+
darkSheet.media = 'not all';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Update theme toggle icon and highlight.js theme on load
|
|
86
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
87
|
+
const theme = document.documentElement.getAttribute('data-theme');
|
|
88
|
+
const icon = document.querySelector('.theme-toggle i');
|
|
89
|
+
if (icon) {
|
|
90
|
+
icon.className = theme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
|
|
91
|
+
}
|
|
92
|
+
// Set highlight.js theme based on saved theme preference
|
|
93
|
+
if (typeof updateHljsTheme === 'function') {
|
|
94
|
+
updateHljsTheme(theme);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
99
|
+
// Navbar burger toggle
|
|
100
|
+
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
|
101
|
+
if ($navbarBurgers.length > 0) {
|
|
102
|
+
$navbarBurgers.forEach( el => {
|
|
103
|
+
el.addEventListener('click', () => {
|
|
104
|
+
const target = el.dataset.target;
|
|
105
|
+
const $target = document.getElementById(target);
|
|
106
|
+
el.classList.toggle('is-active');
|
|
107
|
+
$target.classList.toggle('is-active');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// CodeMirror global instances and observers
|
|
113
|
+
window.codeMirrorInstances = window.codeMirrorInstances || {};
|
|
114
|
+
window.codeMirrorObservers = window.codeMirrorObservers || {};
|
|
115
|
+
|
|
116
|
+
// Initialize CodeMirror editors
|
|
117
|
+
function initCodeMirrorEditor(editorElement, options = {}) {
|
|
118
|
+
if (editorElement && editorElement.id && !window.codeMirrorInstances[editorElement.id]) {
|
|
119
|
+
const elementId = editorElement.id;
|
|
120
|
+
try {
|
|
121
|
+
const defaultOptions = {
|
|
122
|
+
lineNumbers: true,
|
|
123
|
+
mode: { name: "javascript", json: true },
|
|
124
|
+
theme: "default",
|
|
125
|
+
lineWrapping: true,
|
|
126
|
+
gutters: ["CodeMirror-lint-markers"],
|
|
127
|
+
lint: false, // Default to false, enable for JSON
|
|
128
|
+
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }, "Ctrl-Space": "autocomplete"},
|
|
129
|
+
foldGutter: true,
|
|
130
|
+
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"]
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
if (options.mode === 'text/plain' || elementId.includes('instruction')) {
|
|
134
|
+
options.mode = 'text/plain';
|
|
135
|
+
options.lint = false; // No linting for plain text
|
|
136
|
+
} else if (options.mode === 'markdown' || elementId.includes('some-markdown-field')) { // Example for markdown
|
|
137
|
+
options.mode = 'markdown';
|
|
138
|
+
options.lint = false;
|
|
139
|
+
} else { // Default to JSON mode
|
|
140
|
+
options.mode = { name: "javascript", json: true };
|
|
141
|
+
options.lint = true; // Enable linting for JSON
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const cmOptions = Object.assign({}, defaultOptions, options);
|
|
145
|
+
const instance = CodeMirror.fromTextArea(editorElement, cmOptions);
|
|
146
|
+
|
|
147
|
+
instance.on('change', function(cm) {
|
|
148
|
+
cm.save(); // Update original textarea on change
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
window.codeMirrorInstances[elementId] = instance;
|
|
152
|
+
// Refresh after a slight delay to ensure layout is complete
|
|
153
|
+
setTimeout(() => instance.refresh(), 50);
|
|
154
|
+
|
|
155
|
+
// Observer to refresh CodeMirror when it becomes visible
|
|
156
|
+
const observer = new IntersectionObserver((entries) => {
|
|
157
|
+
entries.forEach(entry => {
|
|
158
|
+
if (entry.isIntersecting) {
|
|
159
|
+
if(window.codeMirrorInstances[elementId]) {
|
|
160
|
+
setTimeout(() => window.codeMirrorInstances[elementId].refresh(), 50);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}, { threshold: 0.1 });
|
|
165
|
+
observer.observe(instance.getWrapperElement());
|
|
166
|
+
window.codeMirrorObservers[elementId] = observer;
|
|
167
|
+
|
|
168
|
+
} catch (e) {
|
|
169
|
+
// Fallback if CodeMirror fails to initialize
|
|
170
|
+
editorElement.style.display = 'block';
|
|
171
|
+
console.error("CodeMirror initialization error for ID " + elementId + ":", e);
|
|
172
|
+
}
|
|
173
|
+
} else if (editorElement && editorElement.id && window.codeMirrorInstances[editorElement.id]) {
|
|
174
|
+
// If instance exists, just refresh it (e.g., after HTMX swap)
|
|
175
|
+
const elementId = editorElement.id;
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
if (window.codeMirrorInstances[elementId]) {
|
|
178
|
+
window.codeMirrorInstances[elementId].refresh()
|
|
179
|
+
}
|
|
180
|
+
}, 50);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Destroy CodeMirror instance
|
|
185
|
+
function destroyCodeMirrorInstance(elementId) {
|
|
186
|
+
if (window.codeMirrorObservers && window.codeMirrorObservers[elementId]) {
|
|
187
|
+
window.codeMirrorObservers[elementId].disconnect();
|
|
188
|
+
delete window.codeMirrorObservers[elementId];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (window.codeMirrorInstances && window.codeMirrorInstances[elementId]) {
|
|
192
|
+
const instance = window.codeMirrorInstances[elementId];
|
|
193
|
+
const wrapper = instance.getWrapperElement();
|
|
194
|
+
try {
|
|
195
|
+
instance.toTextArea(); // Restore original textarea
|
|
196
|
+
} catch (e) {
|
|
197
|
+
// console.error("Error converting CodeMirror to TextArea for ID " + elementId + ":", e);
|
|
198
|
+
}
|
|
199
|
+
delete window.codeMirrorInstances[elementId];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Initialize CodeMirror for relevant textareas on the page or within a target element
|
|
204
|
+
function initializeRelevantEditors(targetElement = document.body) {
|
|
205
|
+
const editorsToInit = [
|
|
206
|
+
{ id: 'mcp_servers_json', options: {} },
|
|
207
|
+
{ id: 'instruction', options: { mode: 'text/plain'} },
|
|
208
|
+
{ id: 'mcp-json-editor', options: {} },
|
|
209
|
+
{ id: 'mcp-json-display', options: { readOnly: true } },
|
|
210
|
+
{ id: 'task-json-editor', options: {} },
|
|
211
|
+
{ id: 'direct-task-json-input', options: {} },
|
|
212
|
+
{ id: 'edit-instruction-textarea', options: { mode: 'text/plain'} }
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
editorsToInit.forEach(editor => {
|
|
216
|
+
let element = null;
|
|
217
|
+
if (targetElement && targetElement !== document.body && typeof targetElement.querySelector === 'function') {
|
|
218
|
+
element = targetElement.querySelector(`#${editor.id}`);
|
|
219
|
+
}
|
|
220
|
+
if (!element) {
|
|
221
|
+
element = document.getElementById(editor.id);
|
|
222
|
+
}
|
|
223
|
+
if (element) {
|
|
224
|
+
initCodeMirrorEditor(element, editor.options);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Highlight.js initialization
|
|
230
|
+
function initializeHighlighting(targetElement = document.body) {
|
|
231
|
+
const selector = targetElement === document.body ? 'pre code:not(.language-mermaid):not(.mermaid)' : 'pre code:not(.language-mermaid):not(.mermaid)';
|
|
232
|
+
targetElement.querySelectorAll(selector).forEach(function(block) {
|
|
233
|
+
if (!block.classList.contains('hljs')) {
|
|
234
|
+
try {
|
|
235
|
+
hljs.highlightElement(block);
|
|
236
|
+
} catch (e) {
|
|
237
|
+
console.error("Highlight.js error:", e, "on block:", block);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Mermaid.js preparation
|
|
244
|
+
function prepareMermaidContainers(targetElement = document.body) {
|
|
245
|
+
const selector = targetElement === document.body ? 'code.language-mermaid' : 'code.language-mermaid';
|
|
246
|
+
targetElement.querySelectorAll(selector).forEach(block => {
|
|
247
|
+
const preElement = block.closest('pre');
|
|
248
|
+
if (!preElement || preElement.dataset.mermaidProcessed === 'true') {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const diagramCode = block.textContent || block.innerText;
|
|
252
|
+
if (!diagramCode || diagramCode.trim() === '') {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const mermaidContainer = document.createElement('div');
|
|
256
|
+
mermaidContainer.className = 'mermaid';
|
|
257
|
+
mermaidContainer.textContent = diagramCode;
|
|
258
|
+
if (preElement.parentNode) {
|
|
259
|
+
preElement.parentNode.replaceChild(mermaidContainer, preElement);
|
|
260
|
+
mermaidContainer.dataset.mermaidProcessed = 'true';
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Initial calls on DOMContentLoaded
|
|
266
|
+
initializeRelevantEditors(document.body);
|
|
267
|
+
initializeHighlighting(document.body);
|
|
268
|
+
prepareMermaidContainers(document.body);
|
|
269
|
+
try {
|
|
270
|
+
const initialMermaidNodes = document.querySelectorAll('.mermaid:not(#mermaid-modal-content .mermaid)');
|
|
271
|
+
if (initialMermaidNodes.length > 0) {
|
|
272
|
+
mermaid.run({ nodes: Array.from(initialMermaidNodes) });
|
|
273
|
+
}
|
|
274
|
+
} catch (e) {
|
|
275
|
+
console.error("Error running mermaid.run() on initial load (non-modal):", e);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Health Check Logic
|
|
279
|
+
if (!window.healthCheckInitialized) {
|
|
280
|
+
const healthCheckModal = document.getElementById('health-check-modal');
|
|
281
|
+
let isServerUnavailable = false;
|
|
282
|
+
let healthCheckIntervalId = null;
|
|
283
|
+
|
|
284
|
+
window.checkServerHealth = function() {
|
|
285
|
+
fetch('/healthz', { method: 'GET', cache: 'no-cache' })
|
|
286
|
+
.then(response => {
|
|
287
|
+
if (!response.ok) {
|
|
288
|
+
throw new Error(`Server status: ${response.status}`);
|
|
289
|
+
}
|
|
290
|
+
if (isServerUnavailable) {
|
|
291
|
+
// Server was down but is now back up
|
|
292
|
+
// Reload the page to get fresh agent statuses
|
|
293
|
+
// (Agent statuses are reset to 'stopped' on server restart)
|
|
294
|
+
if (healthCheckModal) healthCheckModal.classList.remove('is-active');
|
|
295
|
+
isServerUnavailable = false;
|
|
296
|
+
showToast("Server connection restored. Refreshing...", "is-success");
|
|
297
|
+
// Short delay to show the toast, then reload
|
|
298
|
+
setTimeout(() => {
|
|
299
|
+
window.location.reload();
|
|
300
|
+
}, 500);
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
.catch(error => {
|
|
304
|
+
if (!isServerUnavailable) {
|
|
305
|
+
if (healthCheckModal) healthCheckModal.classList.add('is-active');
|
|
306
|
+
isServerUnavailable = true;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (typeof window.checkServerHealth === 'function') {
|
|
311
|
+
window.checkServerHealth();
|
|
312
|
+
healthCheckIntervalId = setInterval(window.checkServerHealth, 5000);
|
|
313
|
+
}
|
|
314
|
+
window.healthCheckInitialized = true;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// --- Mermaid Modal Handling Setup ---
|
|
318
|
+
function setupMermaidModal() {
|
|
319
|
+
const modal = document.getElementById('mermaid-modal');
|
|
320
|
+
const modalContentArea = document.getElementById('mermaid-modal-content');
|
|
321
|
+
const closeButtons = modal.querySelectorAll('.modal-close-button, .modal-background');
|
|
322
|
+
|
|
323
|
+
if (!modal || !modalContentArea) {
|
|
324
|
+
console.error("Mermaid modal main elements not found for setup.");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function closeModal() {
|
|
329
|
+
modal.classList.remove('is-active');
|
|
330
|
+
document.documentElement.classList.remove('is-clipped');
|
|
331
|
+
modalContentArea.innerHTML = ''; // Clear content when modal closes
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
closeButtons.forEach(button => {
|
|
335
|
+
button.addEventListener('click', closeModal);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Listen for clicks on the body to catch .show-mermaid-flow-btn
|
|
339
|
+
document.body.addEventListener('click', function(event) {
|
|
340
|
+
const triggerButton = event.target.closest('.show-mermaid-flow-btn');
|
|
341
|
+
if (triggerButton) {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
const mermaidDefinition = triggerButton.dataset.mermaidDefinition;
|
|
344
|
+
|
|
345
|
+
if (mermaidDefinition && mermaidDefinition.trim() !== '') {
|
|
346
|
+
modalContentArea.innerHTML = ''; // Clear previous
|
|
347
|
+
const mermaidTargetPre = document.createElement('pre');
|
|
348
|
+
mermaidTargetPre.className = 'mermaid';
|
|
349
|
+
mermaidTargetPre.textContent = mermaidDefinition;
|
|
350
|
+
modalContentArea.appendChild(mermaidTargetPre);
|
|
351
|
+
|
|
352
|
+
modal.classList.add('is-active');
|
|
353
|
+
document.documentElement.classList.add('is-clipped');
|
|
354
|
+
|
|
355
|
+
// Delay rendering slightly to ensure modal is visible and DOM updated
|
|
356
|
+
setTimeout(() => {
|
|
357
|
+
try {
|
|
358
|
+
mermaid.run({ nodes: [mermaidTargetPre] });
|
|
359
|
+
} catch (e) {
|
|
360
|
+
console.error("Error rendering Mermaid in modal:", e);
|
|
361
|
+
mermaidTargetPre.textContent = `Error rendering diagram:\n${e.message}\n\n${mermaidDefinition}`;
|
|
362
|
+
}
|
|
363
|
+
}, 50);
|
|
364
|
+
} else {
|
|
365
|
+
console.warn("Mermaid definition data attribute was missing or empty on button:", triggerButton);
|
|
366
|
+
showToast("Could not load execution flow diagram.", "is-warning");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
setupMermaidModal();
|
|
372
|
+
// --- END Mermaid Modal Handling ---
|
|
373
|
+
}); // End DOMContentLoaded
|
|
374
|
+
|
|
375
|
+
// HTMX Event Listener for afterSwap
|
|
376
|
+
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
377
|
+
const targetElement = event.detail.target;
|
|
378
|
+
const newElement = event.detail.elt;
|
|
379
|
+
let elementToScan = newElement || targetElement;
|
|
380
|
+
|
|
381
|
+
if (elementToScan) {
|
|
382
|
+
if (typeof initializeRelevantEditors === 'function') initializeRelevantEditors(elementToScan);
|
|
383
|
+
if (typeof initializeHighlighting === 'function') initializeHighlighting(elementToScan);
|
|
384
|
+
if (typeof prepareMermaidContainers === 'function') prepareMermaidContainers(elementToScan);
|
|
385
|
+
try {
|
|
386
|
+
const mermaidNodesInSwap = elementToScan.querySelectorAll ? elementToScan.querySelectorAll('.mermaid:not(#mermaid-modal-content .mermaid)') : [];
|
|
387
|
+
if (mermaidNodesInSwap.length > 0) {
|
|
388
|
+
mermaid.run({ nodes: mermaidNodesInSwap });
|
|
389
|
+
}
|
|
390
|
+
} catch (e) {
|
|
391
|
+
console.error("Error running mermaid.run() after htmx swap on specific nodes:", e);
|
|
392
|
+
}
|
|
393
|
+
} else {
|
|
394
|
+
if (typeof initializeHighlighting === 'function') initializeHighlighting(document.body);
|
|
395
|
+
if (typeof prepareMermaidContainers === 'function') prepareMermaidContainers(document.body);
|
|
396
|
+
try {
|
|
397
|
+
const nonModalMermaidNodes = document.querySelectorAll('.mermaid:not(#mermaid-modal-content .mermaid)');
|
|
398
|
+
if (nonModalMermaidNodes.length > 0) {
|
|
399
|
+
mermaid.run({ nodes: Array.from(nonModalMermaidNodes) });
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
console.error("Error running mermaid.run() after htmx swap (fallback global):", e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const triggerHeader = event.detail.xhr.getResponseHeader('HX-Trigger-After-Swap');
|
|
407
|
+
if (triggerHeader) {
|
|
408
|
+
try {
|
|
409
|
+
if (triggerHeader.startsWith('{') && triggerHeader.endsWith('}')) {
|
|
410
|
+
const triggers = JSON.parse(triggerHeader);
|
|
411
|
+
if (triggers.showToast && typeof showToast === 'function') {
|
|
412
|
+
showToast(triggers.showToast.message, triggers.showToast.type);
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
triggerHeader.split(',').forEach(triggerName => {
|
|
416
|
+
triggerName = triggerName.trim();
|
|
417
|
+
if (typeof showToast === 'function') {
|
|
418
|
+
if (triggerName === 'showRestartToast') {
|
|
419
|
+
showToast("Agent automatically restarted to apply changes.", "is-info");
|
|
420
|
+
} else if (triggerName === 'showRestartErrorToast') {
|
|
421
|
+
showToast("Error: Failed to automatically restart agent. Please stop/start manually.", "is-danger");
|
|
422
|
+
} else if (triggerName === 'showSaveSuccessToast') {
|
|
423
|
+
showToast("Configuration saved successfully.", "is-success");
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
} catch (e) {
|
|
429
|
+
if (triggerHeader === 'showRestartToast' && typeof showToast === 'function') showToast("Agent automatically restarted.", "is-info");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// HTMX Event Listener for beforeSwap
|
|
435
|
+
document.body.addEventListener('htmx:beforeSwap', function(event) {
|
|
436
|
+
const swapTarget = event.detail.target;
|
|
437
|
+
if (swapTarget && swapTarget.id === 'agent-mcp-display-container') {
|
|
438
|
+
const editorId = 'mcp-json-editor';
|
|
439
|
+
if (typeof destroyCodeMirrorInstance === 'function') destroyCodeMirrorInstance(editorId);
|
|
440
|
+
}
|
|
441
|
+
if (swapTarget && swapTarget.id === 'agent-instruction-display-container') {
|
|
442
|
+
const editorId = 'edit-instruction-textarea';
|
|
443
|
+
if (typeof destroyCodeMirrorInstance === 'function') destroyCodeMirrorInstance(editorId);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// HTMX Event Listener for afterRequest
|
|
448
|
+
document.body.addEventListener('htmx:afterRequest', function(event) {
|
|
449
|
+
const config = event.detail.requestConfig;
|
|
450
|
+
const xhr = event.detail.xhr;
|
|
451
|
+
const elt = config.elt;
|
|
452
|
+
|
|
453
|
+
if (elt && elt.id === 'generate-example-task-btn') {
|
|
454
|
+
const editorId = 'direct-task-json-input';
|
|
455
|
+
const editorInstance = window.codeMirrorInstances[editorId];
|
|
456
|
+
if (!editorInstance) {
|
|
457
|
+
if (typeof showToast === 'function') showToast("Task editor is not ready. Please wait.", "is-warning");
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (event.detail.successful) {
|
|
461
|
+
try {
|
|
462
|
+
const responseJson = xhr.responseText;
|
|
463
|
+
editorInstance.setValue(responseJson);
|
|
464
|
+
if (typeof showToast === 'function') showToast("Example task generated.", "is-info", 2000);
|
|
465
|
+
} catch (e) {
|
|
466
|
+
if (typeof showToast === 'function') showToast("Failed to update task editor with example.", "is-danger");
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
if (typeof showToast === 'function') showToast(`Failed to generate example (Status: ${xhr.status})`, "is-danger");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const triggerHeader = xhr.getResponseHeader('HX-Trigger-After-Swap');
|
|
474
|
+
if (config.path && config.path.includes('/execute') && triggerHeader && triggerHeader.startsWith('showTaskError')) {
|
|
475
|
+
try {
|
|
476
|
+
const responseJson = xhr.responseText;
|
|
477
|
+
const parsedResponse = JSON.parse(responseJson);
|
|
478
|
+
const errorMessage = parsedResponse?.error || parsedResponse?.message || "An unknown execution error occurred.";
|
|
479
|
+
const errorHtml =
|
|
480
|
+
`<div class="notification is-danger is-light mt-3 is-small">
|
|
481
|
+
<button class="delete" type="button" onclick="this.parentElement.remove();"></button>
|
|
482
|
+
<strong>Execution Error:</strong><br>
|
|
483
|
+
<pre style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(errorMessage)}</pre>
|
|
484
|
+
</div>`;
|
|
485
|
+
const targetElement = document.getElementById('task-result');
|
|
486
|
+
if (targetElement) {
|
|
487
|
+
targetElement.innerHTML = errorHtml;
|
|
488
|
+
}
|
|
489
|
+
} catch (e) {
|
|
490
|
+
const targetElement = document.getElementById('task-result');
|
|
491
|
+
if (targetElement) {
|
|
492
|
+
targetElement.innerHTML = '<div class="notification is-danger is-light mt-3 is-small">Failed to parse error response from server.</div>';
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Function to show toast notifications
|
|
499
|
+
function showToast(message, type = 'is-info', duration = 4000) {
|
|
500
|
+
const toastContainer = document.getElementById('toast-container');
|
|
501
|
+
if (!toastContainer) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const toast = document.createElement('div');
|
|
505
|
+
toast.className = `notification ${type} is-light`;
|
|
506
|
+
toast.style.marginBottom = '0.5em';
|
|
507
|
+
toast.style.opacity = '0';
|
|
508
|
+
toast.style.transition = 'opacity 0.3s ease-in-out';
|
|
509
|
+
toast.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
|
|
510
|
+
const deleteButton = document.createElement('button');
|
|
511
|
+
deleteButton.className = 'delete';
|
|
512
|
+
deleteButton.type = 'button';
|
|
513
|
+
deleteButton.onclick = () => {
|
|
514
|
+
toast.style.opacity = '0';
|
|
515
|
+
setTimeout(() => toast.remove(), 300);
|
|
516
|
+
};
|
|
517
|
+
toast.appendChild(deleteButton);
|
|
518
|
+
toast.appendChild(document.createTextNode(message));
|
|
519
|
+
toastContainer.appendChild(toast);
|
|
520
|
+
setTimeout(() => toast.style.opacity = '1', 10);
|
|
521
|
+
const timeoutId = setTimeout(() => {
|
|
522
|
+
toast.style.opacity = '0';
|
|
523
|
+
setTimeout(() => toast.remove(), 300);
|
|
524
|
+
}, duration);
|
|
525
|
+
deleteButton.addEventListener('click', () => clearTimeout(timeoutId));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// General notification closer
|
|
529
|
+
document.body.addEventListener('click', function(event) {
|
|
530
|
+
if (event.target.matches('.notification > .delete')) {
|
|
531
|
+
if (!event.target.closest('#toast-container')) {
|
|
532
|
+
event.target.parentElement.remove();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// Dropdown adjustment logic
|
|
538
|
+
function adjustDropdownDirection(dropdownId) {
|
|
539
|
+
const dropdown = document.getElementById(dropdownId);
|
|
540
|
+
if (!dropdown) return;
|
|
541
|
+
const menu = dropdown.querySelector('.dropdown-menu');
|
|
542
|
+
if (!menu) return;
|
|
543
|
+
const trigger = dropdown.querySelector('.dropdown-trigger button');
|
|
544
|
+
if (!trigger) return;
|
|
545
|
+
const tableContainer = trigger.closest('.table-container');
|
|
546
|
+
if (!tableContainer) return;
|
|
547
|
+
|
|
548
|
+
requestAnimationFrame(() => {
|
|
549
|
+
const menuHeight = menu.offsetHeight;
|
|
550
|
+
if (menuHeight === 0) {
|
|
551
|
+
dropdown.classList.remove('is-up');
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
555
|
+
const containerRect = tableContainer.getBoundingClientRect();
|
|
556
|
+
const spaceBelow = containerRect.bottom - triggerRect.bottom;
|
|
557
|
+
if (spaceBelow < menuHeight) {
|
|
558
|
+
dropdown.classList.add('is-up');
|
|
559
|
+
} else {
|
|
560
|
+
dropdown.classList.remove('is-up');
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// ---------------------------------------------------------------------------
|
|
565
|
+
// Shared client helpers (window.Legate)
|
|
566
|
+
//
|
|
567
|
+
// Centralizes the fetch + JSON + error + result-rendering logic that the agent
|
|
568
|
+
// and auth views previously each copy-pasted. The CSRF token is added by the
|
|
569
|
+
// global fetch() wrapper above, so callers never deal with it directly.
|
|
570
|
+
// ---------------------------------------------------------------------------
|
|
571
|
+
window.Legate = window.Legate || {};
|
|
572
|
+
|
|
573
|
+
// Same escaper exposed at window.escapeHtml; namespaced alias for new callers.
|
|
574
|
+
Legate.escapeHtml = window.escapeHtml;
|
|
575
|
+
|
|
576
|
+
// Perform a JSON request. Resolves with the parsed body; rejects with an Error
|
|
577
|
+
// whose .message is the server-provided message when available (and .status /
|
|
578
|
+
// .data carry the response code and payload).
|
|
579
|
+
Legate.apiRequest = function (url, options) {
|
|
580
|
+
options = options || {};
|
|
581
|
+
var headers = new Headers(options.headers || {});
|
|
582
|
+
if (!headers.has('Accept')) headers.set('Accept', 'application/json');
|
|
583
|
+
options.headers = headers;
|
|
584
|
+
return fetch(url, options).then(function (response) {
|
|
585
|
+
return response.json().catch(function () { return {}; }).then(function (data) {
|
|
586
|
+
if (!response.ok) {
|
|
587
|
+
var err = new Error((data && (data.error || data.message)) || ('Request failed (' + response.status + ')'));
|
|
588
|
+
err.status = response.status;
|
|
589
|
+
err.data = data;
|
|
590
|
+
throw err;
|
|
591
|
+
}
|
|
592
|
+
return data;
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// Render a danger notification (escaped) into a container element.
|
|
598
|
+
Legate.renderError = function (el, message) {
|
|
599
|
+
if (!el) return;
|
|
600
|
+
el.innerHTML = '<div class="notification is-danger">' + Legate.escapeHtml(message) + '</div>';
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// Render a list of { status, <labelKey>, message } items as small Bulma
|
|
604
|
+
// notifications. Used by the auth credential/scheme/flow result panels.
|
|
605
|
+
Legate.renderTestItems = function (items, opts) {
|
|
606
|
+
opts = opts || {};
|
|
607
|
+
var labelKey = opts.labelKey || 'name';
|
|
608
|
+
var neutralClass = opts.neutralClass || 'is-warning';
|
|
609
|
+
return (items || []).map(function (item) {
|
|
610
|
+
var statusClass = item.status === 'passed' ? 'is-success'
|
|
611
|
+
: item.status === 'failed' ? 'is-danger' : neutralClass;
|
|
612
|
+
return '<div class="notification ' + statusClass + ' is-small">' +
|
|
613
|
+
'<strong>' + Legate.escapeHtml(item[labelKey]) + ':</strong> ' +
|
|
614
|
+
Legate.escapeHtml(item.message) + '</div>';
|
|
615
|
+
}).join('');
|
|
616
|
+
};
|