@stkxp/cli 0.1.2
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.
- package/README.md +273 -0
- package/bin/stkxp.mjs +198 -0
- package/package.json +97 -0
- package/src/bootstrap.mjs +37 -0
- package/src/bootstrap.test.mjs +73 -0
- package/src/deploy.mjs +145 -0
- package/src/deploy.test.mjs +131 -0
- package/src/export.mjs +30 -0
- package/src/export.test.mjs +56 -0
- package/src/secret-input.mjs +64 -0
- package/src/secret-input.test.mjs +58 -0
- package/src/serve.mjs +83 -0
- package/src/serve.test.mjs +93 -0
- package/vendor/dist-server/asyncapi/generator.js +1 -0
- package/vendor/dist-server/asyncapi/messages.js +1 -0
- package/vendor/dist-server/asyncapi/viewer.html +43 -0
- package/vendor/dist-server/core/app-config/branding.js +1 -0
- package/vendor/dist-server/core/app-config/mantine-theme.js +1 -0
- package/vendor/dist-server/core/app-config/mermaid-theme.js +2 -0
- package/vendor/dist-server/core/app-config/prompt-optimization.js +1 -0
- package/vendor/dist-server/core/app-config/settings.js +1 -0
- package/vendor/dist-server/core/config.js +1 -0
- package/vendor/dist-server/core/graph/a2a-agent-executor.js +2 -0
- package/vendor/dist-server/core/graph/app.js +120 -0
- package/vendor/dist-server/core/graph/delegated-agent-adapter.js +1 -0
- package/vendor/dist-server/core/graph/graph-builder.js +3 -0
- package/vendor/dist-server/core/graph/kibana-agent-executor.js +1 -0
- package/vendor/dist-server/core/graph/nodes/context/assistants-context.js +5 -0
- package/vendor/dist-server/core/graph/nodes/context/compare-context.js +10 -0
- package/vendor/dist-server/core/graph/nodes/context/enrich-context.js +8 -0
- package/vendor/dist-server/core/graph/nodes/context/inventory-context.js +4 -0
- package/vendor/dist-server/core/graph/nodes/context/namespace-context.js +2 -0
- package/vendor/dist-server/core/graph/nodes/context/node-types-context.js +4 -0
- package/vendor/dist-server/core/graph/nodes/context/relevant-assistants-context.js +5 -0
- package/vendor/dist-server/core/graph/nodes/context/relevant-skills-context.js +5 -0
- package/vendor/dist-server/core/graph/nodes/context/skills-context.js +8 -0
- package/vendor/dist-server/core/graph/nodes/context/state-setter.js +1 -0
- package/vendor/dist-server/core/graph/nodes/control/check-reset.js +1 -0
- package/vendor/dist-server/core/graph/nodes/control/topic-detection.js +1 -0
- package/vendor/dist-server/core/graph/nodes/governance/human-approval.js +16 -0
- package/vendor/dist-server/core/graph/nodes/index.js +1 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/suggestion-generator.js +17 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-decider.js +1 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-error.js +4 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-standalone-runner.js +1 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-synthesis.js +29 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-memory.js +2 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-runner.js +7 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/team-assistant-utils.js +1 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-finder.js +13 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-invoker.js +3 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-parallel.js +1 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-pipeline.js +6 -0
- package/vendor/dist-server/core/graph/nodes/orchestration/team-router.js +26 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/index.js +6 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/resource-bridge.js +2 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/sandbox.js +6 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/tool-bridge.js +1 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/local-extractor.js +5 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/response-generator.js +47 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/static-code-executor/index.js +1 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/system.js +54 -0
- package/vendor/dist-server/core/graph/nodes/reasoning/tool-executor.js +9 -0
- package/vendor/dist-server/core/graph/nodes/tools/raw-output.js +4 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-cleaner.js +7 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-execution/execute-tool-calls.js +3 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-execution/invoke.js +1 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-execution/namespace-guard.js +1 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-execution/result-limits.js +3 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool-invoker.js +1 -0
- package/vendor/dist-server/core/graph/nodes/tools/tool.js +4 -0
- package/vendor/dist-server/core/graph/schemas.js +1 -0
- package/vendor/dist-server/core/graph/topic-variants.js +1 -0
- package/vendor/dist-server/core/graph/types.js +1 -0
- package/vendor/dist-server/core/graph/utils/content-utils.js +17 -0
- package/vendor/dist-server/core/graph/utils/edge-jsonata-condition.js +1 -0
- package/vendor/dist-server/core/graph/utils/engine-vars.js +1 -0
- package/vendor/dist-server/core/graph/utils/logging-utils.js +4 -0
- package/vendor/dist-server/core/graph/utils/message-utils.js +5 -0
- package/vendor/dist-server/core/graph/utils/node-type-defaults-cache.js +1 -0
- package/vendor/dist-server/core/graph/utils/sandbox-catalog.js +1 -0
- package/vendor/dist-server/core/graph/utils/schema-utils.js +1 -0
- package/vendor/dist-server/core/graph/utils/team-context-policy.js +5 -0
- package/vendor/dist-server/core/graph/utils/team-node-resolvers.js +1 -0
- package/vendor/dist-server/core/graph/utils/tool-result.js +4 -0
- package/vendor/dist-server/core/graph/utils/tool-wrapper.js +14 -0
- package/vendor/dist-server/core/llm/init-indices.js +1 -0
- package/vendor/dist-server/core/llm/models/index.js +1 -0
- package/vendor/dist-server/core/llm/models/model.model.js +1 -0
- package/vendor/dist-server/core/llm/models/provider.model.js +1 -0
- package/vendor/dist-server/core/llm/models/routing-rule.model.js +1 -0
- package/vendor/dist-server/core/llm/providers.js +1 -0
- package/vendor/dist-server/core/mcp/client.js +1 -0
- package/vendor/dist-server/core/runtime/active-runs-registry.js +1 -0
- package/vendor/dist-server/core/runtime/system-prompt-cache.js +1 -0
- package/vendor/dist-server/core/runtime/trace-bus.js +1 -0
- package/vendor/dist-server/core/schema/cluster_health_report.js +1 -0
- package/vendor/dist-server/core/schema/cluster_info.js +1 -0
- package/vendor/dist-server/core/schema/cluster_license.js +5 -0
- package/vendor/dist-server/core/schema/cluster_stats.js +12 -0
- package/vendor/dist-server/core/schema/dashboard.js +1 -0
- package/vendor/dist-server/core/schema/indices_settings.js +1 -0
- package/vendor/dist-server/core/schema/indices_shards.js +1 -0
- package/vendor/dist-server/core/schema/indices_stats.js +1 -0
- package/vendor/dist-server/core/schema/nodes_info.js +2 -0
- package/vendor/dist-server/core/schema/nodes_stats.js +11 -0
- package/vendor/dist-server/core/schema/pricing.js +1 -0
- package/vendor/dist-server/core/schema/ui.js +1 -0
- package/vendor/dist-server/core/schema/zod_client.js +83 -0
- package/vendor/dist-server/core/services/a2a-client.js +2 -0
- package/vendor/dist-server/core/services/alerts-service.js +1 -0
- package/vendor/dist-server/core/services/api-keys-service.js +1 -0
- package/vendor/dist-server/core/services/assistant-resolver.js +1 -0
- package/vendor/dist-server/core/services/assistants-index-service.js +1 -0
- package/vendor/dist-server/core/services/async-conversations-service.js +22 -0
- package/vendor/dist-server/core/services/asyncapi-dispatch.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-drivers/driver-manager.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-drivers/driver-types.js +0 -0
- package/vendor/dist-server/core/services/asyncapi-drivers/http-driver.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-drivers/nats-driver.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-drivers/websocket-driver.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-events-service.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-reply-service.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-sink.js +1 -0
- package/vendor/dist-server/core/services/asyncapi-tools-service.js +1 -0
- package/vendor/dist-server/core/services/block-repair-service.js +13 -0
- package/vendor/dist-server/core/services/chart-renderer.js +1 -0
- package/vendor/dist-server/core/services/chat-llms-service.js +1 -0
- package/vendor/dist-server/core/services/chat-runs-service.js +1 -0
- package/vendor/dist-server/core/services/chat-tools-service.js +1 -0
- package/vendor/dist-server/core/services/chat-traces-service.js +1 -0
- package/vendor/dist-server/core/services/chats-service.js +5 -0
- package/vendor/dist-server/core/services/comparison-service.js +107 -0
- package/vendor/dist-server/core/services/default-model.service.js +1 -0
- package/vendor/dist-server/core/services/elastic-stack-sync-service.js +2 -0
- package/vendor/dist-server/core/services/elasticsearch-wrapper.js +1 -0
- package/vendor/dist-server/core/services/embed-rate-limiter.js +1 -0
- package/vendor/dist-server/core/services/error-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/es-field-resolver.js +1 -0
- package/vendor/dist-server/core/services/gateway-platform-types.js +1 -0
- package/vendor/dist-server/core/services/gliner-tagging-service.js +1 -0
- package/vendor/dist-server/core/services/golden-questions-scoring.js +1 -0
- package/vendor/dist-server/core/services/golden-questions-service.js +1 -0
- package/vendor/dist-server/core/services/graph-drilldown-service.js +1 -0
- package/vendor/dist-server/core/services/graph-registry-service.js +1 -0
- package/vendor/dist-server/core/services/hitl-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/ingestion-jobs-service.js +1 -0
- package/vendor/dist-server/core/services/ingestion-service.js +5 -0
- package/vendor/dist-server/core/services/kibana-client-factory.js +1 -0
- package/vendor/dist-server/core/services/kibana-client.js +2 -0
- package/vendor/dist-server/core/services/kibana-dashboard-extractor.js +1 -0
- package/vendor/dist-server/core/services/kibana-service.js +1 -0
- package/vendor/dist-server/core/services/llm-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/llm-client-resolver.js +1 -0
- package/vendor/dist-server/core/services/llm-models-service.js +1 -0
- package/vendor/dist-server/core/services/llm-pricing-pure.js +1 -0
- package/vendor/dist-server/core/services/llm-pricing-service.js +1 -0
- package/vendor/dist-server/core/services/llm-providers-service.js +1 -0
- package/vendor/dist-server/core/services/llm-routing-service.js +1 -0
- package/vendor/dist-server/core/services/llm-services.js +1 -0
- package/vendor/dist-server/core/services/llm-sync-service.js +1 -0
- package/vendor/dist-server/core/services/manifest-loader.js +1 -0
- package/vendor/dist-server/core/services/mcp-servers-service.js +1 -0
- package/vendor/dist-server/core/services/mcp-sync-service.js +1 -0
- package/vendor/dist-server/core/services/mcp-tools-service.js +1 -0
- package/vendor/dist-server/core/services/memories-service.js +2 -0
- package/vendor/dist-server/core/services/memory-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/monitoring-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/monitoring-service.js +1 -0
- package/vendor/dist-server/core/services/multi-kibana-service.js +1 -0
- package/vendor/dist-server/core/services/node-latency-service.js +1 -0
- package/vendor/dist-server/core/services/package-policy-service.js +2 -0
- package/vendor/dist-server/core/services/pdf-service.js +572 -0
- package/vendor/dist-server/core/services/plan-service.js +1 -0
- package/vendor/dist-server/core/services/platform-direction.js +1 -0
- package/vendor/dist-server/core/services/platform-secrets.js +1 -0
- package/vendor/dist-server/core/services/platforms-service.js +1 -0
- package/vendor/dist-server/core/services/policy-factory.js +1 -0
- package/vendor/dist-server/core/services/prompt-cache-service.js +1 -0
- package/vendor/dist-server/core/services/provider-rate-limits.js +1 -0
- package/vendor/dist-server/core/services/quota-service.js +1 -0
- package/vendor/dist-server/core/services/report-assets-service.js +1 -0
- package/vendor/dist-server/core/services/run-analytics-collector.js +1 -0
- package/vendor/dist-server/core/services/run-stream-sink.js +1 -0
- package/vendor/dist-server/core/services/send_mail.js +460 -0
- package/vendor/dist-server/core/services/share-service.js +1 -0
- package/vendor/dist-server/core/services/slack-signature.js +1 -0
- package/vendor/dist-server/core/services/sources-service.js +6 -0
- package/vendor/dist-server/core/services/team-mcp-result.js +4 -0
- package/vendor/dist-server/core/services/team-mcp-run-tokens.js +1 -0
- package/vendor/dist-server/core/services/team-mcp-runner.js +1 -0
- package/vendor/dist-server/core/services/team-mcp-server.js +1 -0
- package/vendor/dist-server/core/services/team-mcp-tools.js +1 -0
- package/vendor/dist-server/core/services/team-mcp-ui.js +551 -0
- package/vendor/dist-server/core/services/team-run-progress.js +1 -0
- package/vendor/dist-server/core/services/team-runner-headless.js +7 -0
- package/vendor/dist-server/core/services/team-search-service.js +1 -0
- package/vendor/dist-server/core/services/teams-service.js +1 -0
- package/vendor/dist-server/core/services/token-projection-service.js +1 -0
- package/vendor/dist-server/core/services/tool-analytics-service.js +1 -0
- package/vendor/dist-server/core/services/tool-history-service.js +1 -0
- package/vendor/dist-server/core/services/tool-metrics-service.js +1 -0
- package/vendor/dist-server/core/services/tool-scoring.js +1 -0
- package/vendor/dist-server/core/services/toolbox-import-mappers.js +1 -0
- package/vendor/dist-server/core/services/toolbox-import-service.js +1 -0
- package/vendor/dist-server/core/services/trigger-service.js +1 -0
- package/vendor/dist-server/core/services/version-snapshot-service.js +1 -0
- package/vendor/dist-server/core/services/webhook-calls-service.js +1 -0
- package/vendor/dist-server/core/tools/compare-chat.js +46 -0
- package/vendor/dist-server/core/tools/enrich-chat.js +50 -0
- package/vendor/dist-server/core/tools/variable-encoding.js +1 -0
- package/vendor/dist-server/core/types/settings.js +1 -0
- package/vendor/dist-server/core/utils/ab-evaluator.js +5 -0
- package/vendor/dist-server/core/utils/condition-evaluator.js +1 -0
- package/vendor/dist-server/core/utils/http-client.js +1 -0
- package/vendor/dist-server/core/utils/json-schema-to-form.js +1 -0
- package/vendor/dist-server/core/utils/logger.js +3 -0
- package/vendor/dist-server/core/utils/owner-scope.js +1 -0
- package/vendor/dist-server/core/utils/ownership.js +1 -0
- package/vendor/dist-server/core/utils/parallel-tool-executor.js +1 -0
- package/vendor/dist-server/core/utils/performance-tracker.js +3 -0
- package/vendor/dist-server/core/utils/prompt-optimizer.js +40 -0
- package/vendor/dist-server/core/utils/response-cache.js +1 -0
- package/vendor/dist-server/core/utils/schema-validator.js +1 -0
- package/vendor/dist-server/core/utils/streaming-optimizer.js +2 -0
- package/vendor/dist-server/core/utils/string-helpers.js +1 -0
- package/vendor/dist-server/core/utils/test-responses.js +9 -0
- package/vendor/dist-server/core/utils/text-utils.js +1 -0
- package/vendor/dist-server/core/utils/tool-form-trigger.js +1 -0
- package/vendor/dist-server/entrypoints/cli-bootstrap-run.js +1 -0
- package/vendor/dist-server/entrypoints/cli-deploy-run.js +1 -0
- package/vendor/dist-server/entrypoints/cli-deploy.js +1 -0
- package/vendor/dist-server/entrypoints/team-runner.js +1 -0
- package/vendor/dist-server/generated/toolbox-catalog.js +1 -0
- package/vendor/dist-server/index.js +2 -0
- package/vendor/dist-server/middleware/integration-logos.js +1 -0
- package/vendor/dist-server/middleware/plan-guard.js +1 -0
- package/vendor/dist-server/openapi/generator.js +1 -0
- package/vendor/dist-server/openapi/html-tool-spec.js +1 -0
- package/vendor/dist-server/routes/a2a-routes.js +1 -0
- package/vendor/dist-server/routes/a2a-server-routes.js +5 -0
- package/vendor/dist-server/routes/admin.js +1 -0
- package/vendor/dist-server/routes/assistants-routes.js +1 -0
- package/vendor/dist-server/routes/asyncapi-routes.js +1 -0
- package/vendor/dist-server/routes/auth.js +1 -0
- package/vendor/dist-server/routes/billing-routes.js +1 -0
- package/vendor/dist-server/routes/chat-llms.js +1 -0
- package/vendor/dist-server/routes/chat-tools.js +1 -0
- package/vendor/dist-server/routes/chat-traces.js +1 -0
- package/vendor/dist-server/routes/chats.js +1 -0
- package/vendor/dist-server/routes/clusters.js +1 -0
- package/vendor/dist-server/routes/compare.js +43 -0
- package/vendor/dist-server/routes/connectors-routes.js +1 -0
- package/vendor/dist-server/routes/consumptions-routes.js +1 -0
- package/vendor/dist-server/routes/data-admin-routes.js +1 -0
- package/vendor/dist-server/routes/data-transfer-routes.js +1 -0
- package/vendor/dist-server/routes/elastic-tool-execution-routes.js +1 -0
- package/vendor/dist-server/routes/geo-proxy-routes.js +1 -0
- package/vendor/dist-server/routes/golden-questions-routes.js +1 -0
- package/vendor/dist-server/routes/graph-registry.js +1 -0
- package/vendor/dist-server/routes/graph-templates-routes.js +1 -0
- package/vendor/dist-server/routes/helpdesk-routes.js +35 -0
- package/vendor/dist-server/routes/html-routes.js +1 -0
- package/vendor/dist-server/routes/index.js +1 -0
- package/vendor/dist-server/routes/indices.js +1 -0
- package/vendor/dist-server/routes/integrations-routes.js +1 -0
- package/vendor/dist-server/routes/langgraph.js +7 -0
- package/vendor/dist-server/routes/live-resources-routes.js +1 -0
- package/vendor/dist-server/routes/llm-analytics.js +1 -0
- package/vendor/dist-server/routes/llm-control-plane.js +1 -0
- package/vendor/dist-server/routes/llm.js +1 -0
- package/vendor/dist-server/routes/mcp-gateway-routes.js +1 -0
- package/vendor/dist-server/routes/mcp-query-routes.js +1 -0
- package/vendor/dist-server/routes/mcp-servers-routes.js +1 -0
- package/vendor/dist-server/routes/mcp-team-routes.js +1 -0
- package/vendor/dist-server/routes/mcp-tools-routes.js +1 -0
- package/vendor/dist-server/routes/me-routes.js +63 -0
- package/vendor/dist-server/routes/memories-routes.js +1 -0
- package/vendor/dist-server/routes/monitoring-analytics.js +12 -0
- package/vendor/dist-server/routes/monitoring.js +1 -0
- package/vendor/dist-server/routes/node-types-routes.js +171 -0
- package/vendor/dist-server/routes/nodes.js +1 -0
- package/vendor/dist-server/routes/packages.js +1 -0
- package/vendor/dist-server/routes/pdf.js +3 -0
- package/vendor/dist-server/routes/plan-routes.js +1 -0
- package/vendor/dist-server/routes/platform-requests-routes.js +1 -0
- package/vendor/dist-server/routes/platforms-routes.js +1 -0
- package/vendor/dist-server/routes/prompts-routes.js +1 -0
- package/vendor/dist-server/routes/proxy-logos.js +1 -0
- package/vendor/dist-server/routes/quality-routes.js +1 -0
- package/vendor/dist-server/routes/resources-ingest-routes.js +1 -0
- package/vendor/dist-server/routes/resources-routes.js +1 -0
- package/vendor/dist-server/routes/schema-routes.js +1 -0
- package/vendor/dist-server/routes/settings-original.js +1 -0
- package/vendor/dist-server/routes/settings.js +1 -0
- package/vendor/dist-server/routes/share-routes.js +1 -0
- package/vendor/dist-server/routes/sources-catalog-routes.js +1 -0
- package/vendor/dist-server/routes/sources-routes.js +1 -0
- package/vendor/dist-server/routes/team-optimizer-routes.js +1 -0
- package/vendor/dist-server/routes/team-schedule-routes.js +1 -0
- package/vendor/dist-server/routes/teams-routes.js +1 -0
- package/vendor/dist-server/routes/tool-analytics.js +1 -0
- package/vendor/dist-server/routes/tool-history-routes.js +1 -0
- package/vendor/dist-server/routes/tool-metrics-routes.js +1 -0
- package/vendor/dist-server/routes/toolbox-import-routes.js +1 -0
- package/vendor/dist-server/routes/tools-routes.js +5 -0
- package/vendor/dist-server/routes/transcribe-routes.js +1 -0
- package/vendor/dist-server/routes/triggers.js +1 -0
- package/vendor/dist-server/routes/versions-routes.js +1 -0
- package/vendor/dist-server/routes/webhooks-routes.js +1 -0
- package/vendor/dist-server/scripts/add-label-to-tools.js +4 -0
- package/vendor/dist-server/scripts/migrate-assistants-to-mcp.js +10 -0
- package/vendor/dist-server/scripts/migrate-chat-runs.js +8 -0
- package/vendor/dist-server/scripts/migrate-mcp-protocol.js +8 -0
- package/vendor/dist-server/scripts/migrate-mcp-to-platforms.js +15 -0
- package/vendor/dist-server/services/alert-evaluator.js +3 -0
- package/vendor/dist-server/services/auth.js +1 -0
- package/vendor/dist-server/services/billing-service.js +1 -0
- package/vendor/dist-server/services/chat-title-generator.js +12 -0
- package/vendor/dist-server/services/clone/clone-executor.js +1 -0
- package/vendor/dist-server/services/clone/closure-resolver.js +3 -0
- package/vendor/dist-server/services/clone/entity-graph.js +3 -0
- package/vendor/dist-server/services/clone/team-bundle-bootstrap.js +1 -0
- package/vendor/dist-server/services/clone/team-bundle-crypto.js +1 -0
- package/vendor/dist-server/services/clone/team-bundle-encrypted.js +1 -0
- package/vendor/dist-server/services/clone/team-bundle.js +1 -0
- package/vendor/dist-server/services/cluster-service.js +1 -0
- package/vendor/dist-server/services/connection-adapters.js +1 -0
- package/vendor/dist-server/services/connectors-service.js +10 -0
- package/vendor/dist-server/services/cost-forecast-service.js +1 -0
- package/vendor/dist-server/services/eui-ssr-renderer.js +4 -0
- package/vendor/dist-server/services/graph-canvas/full-structure-builder.js +1 -0
- package/vendor/dist-server/services/graph-templates-service.js +13 -0
- package/vendor/dist-server/services/guest-service.js +1 -0
- package/vendor/dist-server/services/html-service.js +1 -0
- package/vendor/dist-server/services/langgraph-service.js +2 -0
- package/vendor/dist-server/services/leaflet-render-service.js +80 -0
- package/vendor/dist-server/services/live-resources-service.js +16 -0
- package/vendor/dist-server/services/mcp-app-tester-package.js +1 -0
- package/vendor/dist-server/services/mcp-gateway/executors/openapi-executor.js +1 -0
- package/vendor/dist-server/services/mcp-gateway/gateway-grant-service.js +2 -0
- package/vendor/dist-server/services/team-optimizer-service.js +1 -0
- package/vendor/dist-server/services/trace-bus-sink.js +1 -0
- package/vendor/dist-server/services/user-provisioning-service.js +3 -0
- package/vendor/dist-server/templates/mcp-app-tester/src/mcp-http.js +3 -0
- package/vendor/dist-server/templates/mcp-app-tester/src/server.js +1 -0
- package/vendor/dist-server/utils.js +1 -0
- package/vendor/dist-server/ws/assistant-executor.js +7 -0
- package/vendor/dist-server/ws/classify-streamed-json-block.js +1 -0
- package/vendor/dist-server/ws/collect-response-generator-node-ids.js +1 -0
- package/vendor/dist-server/ws/extractors/blockkit-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/echarts-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/eui-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/form-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/index.js +1 -0
- package/vendor/dist-server/ws/extractors/leaflet-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/mantine-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/markdown-extractor.js +7 -0
- package/vendor/dist-server/ws/extractors/mermaid-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/recharts-extractor.js +1 -0
- package/vendor/dist-server/ws/extractors/remotion-extractor.js +1 -0
- package/vendor/dist-server/ws/handler.js +66 -0
- package/vendor/dist-server/ws/parsers/anthropic-parser.js +7 -0
- package/vendor/dist-server/ws/parsers/base-parser.js +3 -0
- package/vendor/dist-server/ws/parsers/gemini-parser.js +8 -0
- package/vendor/dist-server/ws/parsers/index.js +1 -0
- package/vendor/dist-server/ws/parsers/parse-json-blocks.js +1 -0
- package/vendor/dist-server/ws/types.js +0 -0
- package/vendor/dist-server/ws/utils.js +1 -0
- package/vendor/shared/engine-vars.ts +51 -0
- package/vendor/shared/llm-providers-config.ts +69 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as i}from"zod";import{createMcpClient as G}from"../core/mcp/client";import{config as J}from"../core/config";import{Client as W}from"@elastic/elasticsearch";import{applyDslVariables as Z,applyUrlVariables as X}from"./tools-routes";import{getPlatformById as Y,fetchClientCredentialsToken as ee}from"../core/services/platforms-service";import{normalizeCustomHeaders as te}from"../core/utils/string-helpers";const H=new W({node:process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",auth:{username:process.env.ELASTICSEARCH_USER||"elastic",password:process.env.ELASTICSEARCH_PASSWORD||"diagnostics"},tls:{rejectUnauthorized:!1},requestTimeout:6e4});async function oe(b,c){const E=J.mcpUrl||process.env.MCP_URL;let R;try{const{toolName:l}=b.params,{packageName:u,mcpServerId:S,toolEndpoint:p,toolMethod:j,toolBodyFormat:z,toolCustomHeaders:B,toolAuthConfig:x,toolBodyTemplate:M,toolVariables:I,toolPlatformId:U,userToken:se,executeQuery:ae,...d}=b.body,k=b.auth?.username||"";if(!l){c.status(400).json({success:!1,error:"Tool name is required"});return}if(console.log(`[MCP Query] tool: ${l}, packageName: ${u||"none"}, mcpServerId: ${S||"none"}, externalEndpoint: ${p||"none"}`),p&&/^https?:\/\//i.test(p)){const o=j==="GET"||j==="POST"||j==="PUT"?j:"GET",s={};if(x){const e=x;if((e.type==="apiKey"||e.type==="api_key")&&(e.apiKey||e.key)){const t=e.headerName||"Authorization",n=e.headerName?e.apiKey||e.key:`ApiKey ${e.apiKey||e.key}`;s[t]=n}else if(e.type==="bearer"&&e.token)s.Authorization=`Bearer ${e.token}`;else if(e.type==="basic"&&e.username){const t=Buffer.from(`${e.username}:${e.password||""}`).toString("base64");s.Authorization=`Basic ${t}`}else if(e.type==="platform_es"){if(!U){c.status(400).json({success:!1,error:"Tool uses authConfig.type='platform_es' but has no platformId set"});return}try{const t=await Y(U,k,!0,!k),n=t?.config,$=n?.endpoints?.elasticsearch?.username,g=n?.endpoints?.elasticsearch?.password;if(!t||t.type!=="ElasticStack"||!$||!g){c.status(400).json({success:!1,error:`platform_es: platform "${U}" not found or has no Elasticsearch credentials`});return}s.Authorization=`Basic ${Buffer.from(`${$}:${g}`).toString("base64")}`}catch(t){c.status(500).json({success:!1,error:`platform_es lookup failed: ${t.message}`});return}}else if(e.type==="oauth2_cc"&&e.tokenUrl&&e.clientId&&e.clientSecret)try{const t=await ee({tokenUrl:e.tokenUrl,clientId:e.clientId,clientSecret:e.clientSecret,scope:e.scope,audience:e.audience,authStyle:e.authStyle}),n=(e.tokenHeaderName||"").trim()||"Authorization";s[n]=n.toLowerCase()==="authorization"?`Bearer ${t}`:t}catch(t){c.status(502).json({success:!1,error:`oauth2_cc token fetch failed: ${t.message}`});return}console.log(`[MCP Query] Auth type: ${e.type}, headers built: ${Object.keys(s).join(", ")||"none"}`)}const T=new Set(["platform","namespace","cluster"]),a=Object.fromEntries(Object.entries(d).filter(([e])=>!T.has(e)));let f=p;const _=/\{([^}]+)\}|:([A-Za-z_][A-Za-z0-9_]*)/g;f=f.replace(_,(e,t,n)=>{const $=t||n,g=a[$];return g==null||g===""?e:(delete a[$],encodeURIComponent(String(g)))});const V=te(B);let v=f;const y={Accept:"application/json",...V,...s};let w={method:o,headers:y};const A={__username:k,__now:new Date().toISOString()};for(const[e,t]of Object.entries(d))e.startsWith("__")&&typeof t=="string"&&(A[e]=t);const Q=Array.isArray(I)?I:[],N=typeof p=="string"&&p.includes("{{");if(N)try{v=X(p,Q,a,A)}catch(e){c.status(400).json({success:!1,error:`URL render failed: ${e.message}`});return}if(o==="GET"){const e=new URLSearchParams(Object.entries(a).filter(([,t])=>t!=null&&t!=="").map(([t,n])=>[t,String(n)])).toString();e&&!N&&(v=`${f}${f.includes("?")?"&":"?"}${e}`)}else if(typeof M=="string"&&M.trim()){let e;try{e=Z(M,Q,a,A)}catch(t){c.status(400).json({success:!1,error:`Body render failed: ${t.message}`});return}try{const t=JSON.parse(e);Object.keys(y).some(n=>n.toLowerCase()==="content-type")||(y["Content-Type"]="application/json"),w.body=JSON.stringify(t)}catch(t){c.status(400).json({success:!1,error:`Rendered body is not valid JSON: ${t.message}`,rendered:e});return}}else(z==="form"?"form":"json")==="form"?(Object.keys(y).some(t=>t.toLowerCase()==="content-type")||(y["Content-Type"]="application/x-www-form-urlencoded"),w.body=new URLSearchParams(Object.entries(a).filter(([,t])=>t!=null&&t!=="").map(([t,n])=>[t,String(n)])).toString()):(Object.keys(y).some(t=>t.toLowerCase()==="content-type")||(y["Content-Type"]="application/json"),w.body=JSON.stringify(a));console.log(`[MCP Query] Direct HTTP ${o} ${v}`);const h=await fetch(v,w);if(!h.ok){const e=await h.text();throw new Error(`HTTP ${h.status}: ${e||h.statusText}`)}const F=h.headers.get("content-type")?.includes("application/json")?await h.json():{data:await h.text()};c.json({success:!0,query:F,toolName:l,parameters:a});return}let C,r=null;if(S||u)try{if(S){const o=await H.get({index:".stkxp_platforms",id:S});o.found&&(r=o._source,console.log(`[MCP Query] Found server by ID "${S}": ${r.name}`))}else{const o=await H.search({index:".stkxp_platforms",body:{query:{term:{name:u}},size:1}});o.hits.hits.length>0?(r=o.hits.hits[0]._source,console.log(`[MCP Query] Found server by name "${u}"`)):console.log(`[MCP Query] No server found for name "${u}" \u2014 falling back to legacy URL`)}if(r){const o=r.config?.url||r.url;if(!o)console.log(`[MCP Query] Server "${r.name}" has no URL (local/managed) \u2014 using legacy URL`),r=null;else{let s=o;if(r.type==="Toolbox"){const a=s.replace(/\/$/,"");s=/\/mcp(\/sse)?$/.test(a)?a.replace(/\/mcp\/sse$/,"/mcp"):`${a}/mcp`}const T=r.config?.queryParams||r.queryParams||{};if(Object.keys(T).length>0){const a=new URL(s);Object.entries(T).forEach(([f,_])=>a.searchParams.set(f,_)),s=a.toString()}C=s,console.log(`[MCP Query] Resolved remote server URL: ${C}`)}}}catch(o){console.error("[MCP Query] ES lookup failed:",o.message)}if(!r){if(!E){c.status(503).json({success:!1,error:"MCP server not configured. Please configure MCP servers via Settings UI."});return}C=u?E.replace(/\/mcp\/?$/,"")+`/mcp/${u}`:E}const m={},P=r?r.config||r:null;if(!!P&&(P.serverType==="remote"||P.serverType==="gateway")){const o=P.headers||{},s=Object.keys(o).some(T=>T.toLowerCase()==="authorization");P.authKey&&!s&&(m.Authorization=`ApiKey ${P.authKey}`),Object.assign(m,o)}else{const o=b.cookies?.token||b.headers.authorization?.substring(7);o&&(m.Authorization=`Bearer ${o}`),k&&(m["X-Username"]=k)}const O=b.headers["x-platform-id"]||d.platform;if(O&&(m["x-platform-id"]=O),d.namespace&&(m["x-namespace"]=String(d.namespace)),!C){c.status(503).json({success:!1,error:"Could not resolve MCP server URL for this tool"});return}const L=C.includes("/claude-code"),q=L?18e4:6e4,D={...d};R=await G(C,{headers:m,timeout:L?18e4:3e4});const K=await R.client.callTool({name:l,arguments:D},void 0,{timeout:q});console.log("[MCP Query] Tool call successful"),c.json({success:!0,query:K,toolName:l,parameters:d})}catch(l){console.error("[MCP Query] Error generating query:",l),c.status(500).json({success:!1,error:l.message||"Failed to generate query",details:l.toString()})}finally{if(R)try{await R.close()}catch(l){console.error("[MCP Query] Error closing MCP client:",l)}}}const re=i.object({packageName:i.string().optional().describe("MCP package name to dispatch to"),mcpServerId:i.string().optional().describe("Id of the MCP server (.stkxp_mcp_servers) to dispatch to"),toolEndpoint:i.string().optional().describe("Direct HTTP endpoint (matches ^https?://) \u2014 short-circuits MCP for OpenAPI-typed tools"),toolMethod:i.enum(["GET","POST","PUT"]).optional().describe("HTTP method for toolEndpoint"),toolBodyFormat:i.string().optional().describe("Body format for the external call"),toolCustomHeaders:i.record(i.any()).optional().describe("Extra headers for the external call"),toolAuthConfig:i.record(i.any()).optional().describe("Auth config for the external call"),toolBodyTemplate:i.string().optional().describe("Mustache body template (platform_es / templated tools)"),toolVariables:i.array(i.any()).optional().describe("Declared template variables"),toolPlatformId:i.string().optional().describe("Linked platform id (for authConfig.type='platform_es')")}).passthrough(),be=[{method:"post",path:"/api/mcp/query/:toolName",validate:{body:re},handler:oe,openapi:{summary:"Invoke an MCP tool (or OpenAPI endpoint) by name",description:"Executes a tool against the resolved MCP server (`mcpServerId` or `packageName`) \u2014 falls back to the legacy MCP_URL when neither is supplied. When the request body contains `toolEndpoint` matching `^https?://`, this short-circuits the MCP transport and calls the HTTP endpoint directly (used for OpenAPI-typed tools). The arbitrary remaining keys in the body are passed as the tool's input arguments \u2014 internal-only keys (`platform`, `namespace`, `cluster`) are filtered out before forwarding.",tags:["mcp"]}}];export{be as routes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as a}from"zod";import{Client as k}from"@elastic/elasticsearch";import{syncRemoteToolsForPlatform as K,getMcpPlatformImpact as B}from"../core/services/mcp-sync-service";import{createMcpClient as W}from"../core/mcp/client";const R=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",j=process.env.ELASTICSEARCH_USER||"elastic",T=process.env.ELASTICSEARCH_PASSWORD||"",g=".stkxp_platforms",E=".stkxp_tools",D=".stkxp_assistants",N={term:{type:"MCPServer"}};function G(t,s){return Buffer.from(JSON.stringify({page:t,size:s})).toString("base64")}function J(t){try{const s=JSON.parse(Buffer.from(t,"base64").toString("utf8"));if(typeof s?.page=="number"&&typeof s?.size=="number")return s}catch{}}const h=new k({node:R,auth:{username:j,password:T},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),z=new k({node:R,auth:{username:j,password:T},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),q=new k({node:R,auth:{username:j,password:T},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3});async function A(t,s,e,o){const r=e.config??{};if(r.serverType!=="remote"&&r.serverType!=="gateway")return{synced:0};const n={...e,id:t,type:"MCPServer",config:r};return K(t,n,o)}async function X(t,s){return B(t,s)}const F=a.object({name:a.string().min(1),url:a.string().url(),authKey:a.string().optional(),type:a.enum(["local","remote"]).default("remote"),protocol:a.enum(["http","sse"]).optional(),packageName:a.string().optional(),namespace:a.string().optional(),description:a.string().optional(),enabled:a.boolean().default(!0),headers:a.record(a.string(),a.string()).optional(),queryParams:a.record(a.string(),a.string()).optional()}).refine(t=>t.type==="local"?t.packageName&&t.packageName.length>0:!0,{message:"packageName est obligatoire pour les serveurs de type 'local' (FK vers Package)",path:["packageName"]}),$=a.object({name:a.string().min(1).optional(),url:a.string().url().optional(),authKey:a.string().optional(),type:a.enum(["local","remote"]).optional(),protocol:a.enum(["http","sse"]).optional(),packageName:a.string().optional(),namespace:a.string().optional(),description:a.string().optional(),enabled:a.boolean().optional(),owner:a.string().optional(),headers:a.record(a.string(),a.string()).optional(),queryParams:a.record(a.string(),a.string()).optional()}),Z=a.object({search:a.string().optional().describe("Free-text search across name, description, and URL"),enabled:a.enum(["true","false"]).optional().describe("Filter by enabled status"),type:a.string().optional().describe("Filter by server type (local/remote/gateway/kibana)"),packageNames:a.string().optional().describe("Comma-separated packageName values to filter by"),size:a.string().optional().describe("Page size, 1-1000 (default 100). Alias: limit"),limit:a.string().optional().describe("Alias for size"),from:a.string().optional().describe("Zero-based result offset (alternative to page)"),page:a.string().optional().describe("1-based page number (alternative to from)"),cursor:a.string().optional().describe("Opaque continuation cursor returned as nextCursor by a previous call. Takes priority over from/page/size when present \u2014 treat as an opaque token, do not construct it manually.")}),Q=a.object({ids:a.array(a.string()).optional().describe("Specific server ids to check. Omit to check all enabled MCP servers the caller owns."),timeoutMs:a.number().int().optional().describe("Per-server connect timeout in ms (default 5000, clamped to [1000, 15000])"),concurrency:a.number().int().optional().describe("Max servers checked in parallel (default 10, clamped to [1, 20])")});async function V(){try{await h.indices.exists({index:g})||(await h.indices.create({index:g,body:{mappings:{properties:{name:{type:"keyword"},url:{type:"keyword"},authKey:{type:"keyword"},protocol:{type:"keyword"},type:{type:"keyword"},packageName:{type:"keyword"},description:{type:"text"},enabled:{type:"boolean"},owner:{type:"keyword"},headers:{type:"object",dynamic:!0},queryParams:{type:"object",dynamic:!0},createdAt:{type:"date"},updatedAt:{type:"date"},createdBy:{type:"keyword"},updatedBy:{type:"keyword"}}}}}),console.log(`Index ${g} created successfully`))}catch(t){console.error("Error ensuring MCP servers index:",t)}}V();function I(t){const s=t?.username||"system";return t?.roles?.includes("superuser")??!1?[s,"stkxp"]:[s]}function Y(t,s){return t==="stkxp"&&s!=="stkxp"}function H(t){const s=t.headers||{},e=Object.keys(s).some(c=>c.toLowerCase()==="authorization"),o={...t.authKey&&!e?{authorization:`ApiKey ${t.authKey}`}:{},...s};let r=t.url??"";const n=t.queryParams||{};if(Object.keys(n).length>0){const c=new URL(r);Object.entries(n).forEach(([i,u])=>c.searchParams.set(i,u)),r=c.toString()}return{url:r,headers:o,transportType:t.protocol||"http"}}async function ee(t,s,e){const o=new Array(t.length);let r=0;async function n(){for(;r<t.length;){const i=r++;o[i]=await e(t[i],i)}}const c=t.length===0?0:Math.min(Math.max(s,1),t.length);return await Promise.all(Array.from({length:c},n)),o}async function se(t,s){const e=Date.now();let o;try{const{url:r,headers:n,transportType:c}=H(t);return o=await W(r,{headers:n,transportType:c,timeout:s}),{healthy:!0,latencyMs:Date.now()-e}}catch(r){return{healthy:!1,latencyMs:Date.now()-e,error:r?.message||String(r)}}finally{o&&await o.close().catch(()=>{})}}function _(t,s,e){const o=s?.config??{};return{id:t,name:s.name,description:s.description,enabled:s.enabled,owner:s.owner,url:o.url,authKey:void 0,authKeySet:!!o.authKey,packageName:o.packageName,namespace:o.namespace,protocol:o.protocol,type:o.serverType,headers:o.headers??{},queryParams:o.queryParams??{},createdAt:s.created,updatedAt:s.updated,readOnly:Y(s.owner,e)}}function L(t,s,e,o=new Date().toISOString()){const r=e?.config??{},n=t.url??r.url??"";return{...e??{},type:"MCPServer",managedType:e?.managedType??"self-hosted",name:t.name??e?.name,description:t.description??e?.description??"",enabled:t.enabled??e?.enabled??!0,owner:e?.owner??s,created:e?.created??o,updated:o,config:{url:n,protocol:t.protocol??r.protocol??(n.includes("/sse")?"sse":"http"),serverType:t.type??r.serverType??"remote",authKey:t.authKey||r.authKey,packageName:t.packageName??r.packageName,namespace:t.namespace??r.namespace,headers:t.headers??r.headers??{},queryParams:t.queryParams??r.queryParams??{}}}}async function te(t,s){try{console.log("[MCP Servers] listServers called");const{search:e,enabled:o,limit:r,size:n,from:c,page:i,type:u,packageNames:m,cursor:b}=t.query,l=t.auth,S=l?.username||"stkxp",M=I(l);let p=parseInt(n||r||"100"),v=0;if(b!==void 0){const C=J(b);if(!C){s.status(400).json({success:!1,error:"Invalid cursor"});return}p=C.size,v=(C.page-1)*p}else c!==void 0?v=parseInt(c):i!==void 0&&(v=(parseInt(i)-1)*p);if(p<1||p>1e3){s.status(400).json({success:!1,error:"Invalid size parameter (must be between 1 and 1000)"});return}if(v<0){s.status(400).json({success:!1,error:"Invalid from/page parameter (must be >= 0)"});return}const d={bool:{must:[{terms:{owner:M}},N]}};if(e&&d.bool.must.push({multi_match:{query:e,fields:["name^2","description","config.url"]}}),o!==void 0&&d.bool.must.push({term:{enabled:o==="true"}}),u&&d.bool.must.push({term:{"config.serverType":u}}),m){const C=m.split(",").map(U=>U.trim()).filter(Boolean);C.length>0&&d.bool.must.push({terms:{"config.packageName":C}})}const y=await h.search({index:g,body:{query:d,from:v,size:p,sort:[{created:{order:"desc",unmapped_type:"date"}}]}});console.log(JSON.stringify({query:d,from:v,size:p,sort:[{created:{order:"desc",unmapped_type:"date"}}]}));const x=y.hits.hits.map(C=>_(C._id,C._source,S)),P=typeof y.hits.total=="object"?y.hits.total.value:y.hits.total,f=Math.floor(v/p)+1,w=Math.ceil(P/p),O=f<w;console.log(`[MCP Servers] Found ${x.length} servers (${P} total, page ${f}/${w})`),s.json({success:!0,total:P,servers:x,nextCursor:O?G(f+1,p):void 0,pagination:{page:f,size:p,from:v,totalPages:w,hasNextPage:O,hasPreviousPage:f>1}})}catch(e){console.error("Error fetching MCP servers:",e),s.status(500).json({success:!1,error:"Failed to fetch MCP servers",message:e.message})}}async function re(t,s){try{const{id:e}=t.params,o=t.auth,r=o?.username||"system",n=I(o),c=await h.get({index:g,id:e}),i=c._source;if(!n.includes(i?.owner)){s.status(403).json({success:!1,error:"Access denied"});return}s.json({success:!0,server:_(c._id,i,r)})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}console.error("Error fetching MCP server:",e),s.status(500).json({success:!1,error:"Failed to fetch MCP server",message:e.message})}}async function oe(t,s){try{console.log("[MCP Servers] createServer called with body:",t.body);const e=F.parse(t.body),o=t.auth?.username||"system";if((await h.search({index:g,body:{query:{bool:{must:[{term:{name:e.name}},N]}}}})).hits.hits.length>0){s.status(409).json({success:!1,error:"MCP server with this name already exists"});return}const n=new Date().toISOString(),c=L(e,o,void 0,n),u=(await h.index({index:g,body:c,refresh:!0}))._id;if(c.config.serverType==="remote"){try{const{synced:m}=await A(u,c.name,c,o);s.status(201).json({success:!0,server:_(u,c,o),toolsSynced:m})}catch(m){try{await h.delete({index:g,id:u,refresh:!0})}catch{}console.warn(`[MCP Sync] Sync failed for new server "${c.name}", rolled back:`,m.message),s.status(400).json({success:!1,error:`Server saved but tool sync failed: ${m.message}`})}return}s.status(201).json({success:!0,server:_(u,c,o)})}catch(e){if(e instanceof a.ZodError){s.status(400).json({success:!1,error:"Validation error",details:e.errors});return}console.error("Error creating MCP server:",e),s.status(500).json({success:!1,error:"Failed to create MCP server",message:e.message})}}async function ae(t,s){try{console.log("updateServer called for id:",t.params.id),console.log("updateServer body:",t.body);const{id:e}=t.params,o=$.parse(t.body),r=t.auth?.username||"system";let n;try{n=(await h.get({index:g,id:e}))._source}catch(i){if(i.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}throw i}if(n?.owner==="stkxp"&&r!=="stkxp"){s.status(403).json({success:!1,error:"stkxp servers are read-only"});return}if(n?.owner!==r){s.status(403).json({success:!1,error:"Access denied"});return}const c=L(o,r,n);if(console.log("Replacing Elasticsearch document with:",c),await h.index({index:g,id:e,document:c,refresh:!0}),console.log("Document replaced, syncing tools if remote"),c.config.serverType==="remote"){try{const{synced:i}=await A(e,c.name,c,r);s.json({success:!0,server:_(e,c,r),toolsSynced:i})}catch(i){console.warn(`[MCP Sync] Resync failed for server "${c.name}":`,i.message),s.status(400).json({success:!1,error:`Server updated but tool sync failed: ${i.message}`})}return}s.json({success:!0,server:_(e,c,r)})}catch(e){if(console.error("Error in updateServer:",e),e instanceof a.ZodError){s.status(400).json({success:!1,error:"Validation error",details:e.errors});return}s.status(500).json({success:!1,error:"Failed to update MCP server",message:e.message})}}async function ne(t,s){try{const{id:e}=t.params;let o;try{o=(await h.get({index:g,id:e}))._source?.name||e}catch(n){if(n.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}throw n}const r=await X(e,o);s.json({success:!0,...r})}catch(e){console.error("Error getting server impact:",e),s.status(500).json({success:!1,error:"Failed to get server impact",message:e.message})}}async function ce(t,s){try{const{id:e}=t.params;let o=e;try{o=(await h.get({index:g,id:e}))._source?.name||e}catch{}try{const n=(await z.search({index:D,body:{query:{term:{"mcp_servers_policy.servers.name.keyword":o}},_source:["mcp_servers_policy"],size:500}})).hits.hits;for(const c of n){const i=c._source,u=(i?.mcp_servers_policy?.servers||[]).filter(m=>m.name!==o);await z.update({index:D,id:c._id,body:{doc:{mcp_servers_policy:{...i.mcp_servers_policy,servers:u},updated_at:new Date().toISOString()}},refresh:!1})}n.length>0&&console.log(`[MCP Delete] Removed server "${o}" from ${n.length} assistants`)}catch(r){console.error(`[MCP Delete] Failed to clean assistants for server ${e}:`,r.message)}try{await q.deleteByQuery({index:E,body:{query:{bool:{must:[{term:{type:"mcp_remote"}},{term:{mcpServerId:e}}]}}},refresh:!0})}catch(r){console.error(`[MCP Delete] Failed to delete tools for server ${e}:`,r.message)}await h.delete({index:g,id:e,refresh:!0}),s.json({success:!0,message:"MCP server deleted successfully"})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}console.error("Error deleting MCP server:",e),s.status(500).json({success:!1,error:"Failed to delete MCP server",message:e.message})}}async function ie(t,s){try{const{id:e}=t.params,{enabled:o}=t.body;if(typeof o!="boolean"){s.status(400).json({success:!1,error:"enabled field must be a boolean"});return}const n=t.auth?.username||"system";let c;try{c=(await h.get({index:g,id:e}))._source}catch(u){if(u.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}throw u}if(c?.owner==="stkxp"&&n!=="stkxp"){s.status(403).json({success:!1,error:"stkxp servers are read-only"});return}if(c?.owner!==n){s.status(403).json({success:!1,error:"Access denied"});return}await h.update({index:g,id:e,body:{doc:{enabled:o,updated:new Date().toISOString()}},refresh:!0});const i=await h.get({index:g,id:e});s.json({success:!0,server:_(i._id,i._source,n)})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}console.error("Error toggling MCP server status:",e),s.status(500).json({success:!1,error:"Failed to toggle MCP server status",message:e.message})}}async function ue(t,s){try{const{id:e}=t.params;if(e.startsWith("__personal_tools__:")){const u=e.replace("__personal_tools__:",""),b=(await q.search({index:E,body:{query:{bool:{must:[{term:{owner:u}}],should:[{bool:{must:[{term:{type:"mcp"}}],must_not:[{exists:{field:"system"}}]}},{term:{type:"assistant_mapping"}}],minimum_should_match:1}},size:1e4,_source:["name","label","description","inputSchema"]}})).hits.hits.map(l=>({name:l._source.name,label:l._source.label||l._source.name,description:l._source.description,inputSchema:l._source.inputSchema}));s.json({success:!0,connected:!0,capabilities:{tools:b,prompts:[],resources:[]},toolsCount:b.length,promptsCount:0,resourcesCount:0});return}const r=(await h.get({index:g,id:e}))._source,n=r.config??{};if(r.type==="OpenAPI"||r.type==="Toolbox"){const u=r.name||e,b=(await q.search({index:E,body:{query:{bool:{should:[{term:{platformId:e}},{term:{system:u}}],minimum_should_match:1}},size:1e4,_source:["name","label","description","inputSchema"]}})).hits.hits.map(l=>({name:l._source.name,label:l._source.label||l._source.name,description:l._source.description,inputSchema:l._source.inputSchema}));s.json({success:!0,connected:!0,capabilities:{tools:b,prompts:[],resources:[]},toolsCount:b.length,promptsCount:0,resourcesCount:0});return}const{createMcpClient:c}=await import("../core/mcp/client");let i;try{const{url:u,headers:m,transportType:b}=H(n);i=await c(u,{headers:m,transportType:b,timeout:1e4});let l=[],S=null;try{if(l=((await i.client.listTools()).tools||[]).map(y=>({name:y.name,label:y.name,description:y.description,inputSchema:y.inputSchema})),l.length>0)try{const y=l.map(f=>f.name),x=await h.search({index:".stkxp_tools",body:{query:{terms:{name:y}},size:y.length,_source:["name","label"]}}),P=new Map;for(const f of x.hits.hits){const w=f._source;w.name&&w.label&&P.set(w.name,w.label)}l=l.map(f=>({...f,label:P.get(f.name)||f.name}))}catch{}}catch(d){S=d.message||String(d),console.error("[MCP] Error fetching tools:",S)}const M=i.client.getServerCapabilities();let p=[];if(M?.prompts)try{const d=i.client.listPrompts(),y=new Promise((P,f)=>setTimeout(()=>f(new Error("listPrompts timeout after 15s")),15e3));p=((await Promise.race([d,y])).prompts||[]).map(P=>({name:P.name,description:P.description,arguments:P.arguments}))}catch(d){console.error("[MCP] Error fetching prompts:",d.message||d)}let v=[];if(M?.resources)try{v=((await i.client.listResources()).resources||[]).map(y=>({uri:y.uri,name:y.name,description:y.description,mimeType:y.mimeType}))}catch(d){console.error("[MCP] Error fetching resources:",d.message||d)}s.json({success:!0,connected:!0,capabilities:{tools:l,prompts:p,resources:v,toolsCount:l.length,promptsCount:p.length,resourcesCount:v.length,toolsError:S}}),await i.close()}catch(u){console.error("[MCP] Error fetching server capabilities:",u);const m=u.message?.includes("HTTP 410")||u.message?.includes("Deleted resource");s.status(m?410:503).json({success:!1,error:m?"MCP endpoint deleted":"Failed to connect to MCP server",message:m?"The MCP endpoint returned HTTP 410 \u2014 the remote resource has been permanently deleted. Check the server URL or remove this platform.":u.message})}}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}console.error("Error fetching MCP server capabilities:",e),s.status(500).json({success:!1,error:"Failed to fetch MCP server capabilities",message:e.message})}}async function le(t,s){try{const e=t.body||{},o=t.auth,r=I(o),n=parseInt(e.timeoutMs,10),c=Math.min(Math.max(Number.isFinite(n)?n:5e3,1e3),15e3),i=parseInt(e.concurrency,10),u=Math.min(Math.max(Number.isFinite(i)?i:10,1),20),m={bool:{must:[{terms:{owner:r}},N]}};Array.isArray(e.ids)?m.bool.must.push({ids:{values:e.ids}}):m.bool.must.push({term:{enabled:!0}});const l=(await h.search({index:g,body:{query:m,size:1e3,_source:["config"]}})).hits.hits.map(p=>({id:p._id,config:p._source?.config??{}})),S=await ee(l,u,async p=>({id:p.id,health:await se(p.config,c)})),M={};for(const{id:p,health:v}of S)M[p]=v;s.json({success:!0,results:M})}catch(e){console.error("[MCP Servers] Error running health check:",e),s.status(500).json({success:!1,error:"Failed to run health check",message:e.message})}}async function pe(t,s){try{const{id:e}=t.params,o=t.auth?.username||"system",n=(await h.get({index:g,id:e}))._source;if(n.config?.serverType!=="remote"&&n.config?.serverType!=="gateway"){s.status(400).json({success:!1,error:"Only remote or gateway MCPServer platforms can be synced"});return}await A(e,n.name,n,n.owner||o),s.json({success:!0,message:`Tools synced for server "${n.name}"`})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"MCP server not found"});return}console.error("Error syncing server tools:",e),s.status(500).json({success:!1,error:"Failed to sync server tools",message:e.message})}}async function me(t,s){s.json({success:!0,config:{localDns:process.env.MCP_LOCAL_DNS||"stkxp.host"}})}const Pe=[{method:"get",path:"/api/stack_expert/settings/mcp-config",handler:me,openapi:{summary:"Get MCP configuration",description:"Retrieves the MCP configuration settings.",tags:["mcp"]}},{method:"get",path:"/api/stack_expert/settings/mcp-servers",handler:te,validate:{query:Z},openapi:{summary:"List MCP servers",description:"Retrieves a paginated list of MCP servers. Supports opaque cursor-based continuation (MCP pagination spec): pass the `nextCursor` from a previous response back as `cursor` to fetch the next page.",tags:["mcp"]}},{method:"get",path:"/api/stack_expert/settings/mcp-servers/:id",handler:re,openapi:{summary:"Get MCP server details",description:"Retrieves details for a specific MCP server.",tags:["mcp"]}},{method:"get",path:"/api/stack_expert/settings/mcp-servers/:id/capabilities",handler:ue,openapi:{summary:"Get MCP server capabilities",description:"Retrieves the capabilities of a specific MCP server.",tags:["mcp"]}},{method:"post",path:"/api/stack_expert/settings/mcp-servers/health-check",handler:le,validate:{body:Q},openapi:{summary:"Bulk MCP server health check",description:"Runs a lightweight MCP initialize handshake (connect + close, no listTools) against multiple MCP servers in parallel and reports whether each responded. Omit `ids` to check all enabled servers you own; an empty `ids` array checks nothing. Results are keyed by the ES document id, and any requested id outside the caller's ownership scope is silently omitted from the response rather than erroring. `timeoutMs` is a best-effort bound \u2014 it may not be strictly enforced for servers using the `sse` transport, a limitation of the underlying MCP client's SSE transport.",tags:["mcp"]}},{method:"get",path:"/api/stack_expert/settings/mcp-servers/:id/impact",handler:ne,openapi:{summary:"Get MCP server impact",description:"Retrieves the impact information for a specific MCP server.",tags:["mcp"]}},{method:"post",path:"/api/stack_expert/settings/mcp-servers/:id/sync-tools",handler:pe,openapi:{summary:"Sync MCP server tools",description:"Syncs the tools for a specific MCP server.",tags:["mcp"]}},{method:"post",path:"/api/stack_expert/settings/mcp-servers",handler:oe,validate:{body:F},openapi:{summary:"Create MCP server",description:"Creates a new MCP server.",tags:["mcp"]}},{method:"put",path:"/api/stack_expert/settings/mcp-servers/:id",handler:ae,validate:{body:$},openapi:{summary:"Update MCP server",description:"Updates an existing MCP server.",tags:["mcp"]}},{method:"delete",path:"/api/stack_expert/settings/mcp-servers/:id",handler:ce,openapi:{summary:"Delete MCP server",description:"Deletes a specific MCP server.",tags:["mcp"]}},{method:"patch",path:"/api/stack_expert/settings/mcp-servers/:id/status",handler:ie,openapi:{summary:"Toggle MCP server status",description:"Toggles the status of a specific MCP server.",tags:["mcp"]}}];export{se as checkServerHealth,H as resolveMcpConnection,Pe as routes,ee as runWithConcurrency};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import y from"express";import{randomUUID as m}from"crypto";import{StreamableHTTPServerTransport as I}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as w}from"@modelcontextprotocol/sdk/types.js";import{getTeamMcpConfig as T}from"../core/services/teams-service";import{getOrCreateTeamMcpServer as v}from"../core/services/team-mcp-server";import{verifySlackSignature as M}from"../core/services/slack-signature";import{logWebhookCall as p,sanitiseHeaders as g,hashPayload as f}from"../core/services/webhook-calls-service";const o={};function i(s,r){return`${s}:${r}`}async function P(s,r){const{teamId:a}=s.params,e=await T(a),n=e?.mcpAllowUnsigned===!0;if(!e||!e.mcpEnabled||!e.mcpSigningSecret&&!n){r.status(404).json({jsonrpc:"2.0",error:{code:-32004,message:"Not found"},id:null});return}const d=Buffer.isBuffer(s.body)?s.body:Buffer.from("");if(!(typeof s.headers["x-slack-signature"]=="string"&&s.headers["x-slack-signature"].length>0?M({rawBody:d,timestamp:s.headers["x-slack-request-timestamp"]??"",signature:s.headers["x-slack-signature"]??"",signingSecret:e.mcpSigningSecret??""}):n)){p({webhookCallId:m(),receivedAt:new Date().toISOString(),teamId:a,owner:e.owner??null,sourceIp:s.ip??null,status:"rejected_token",source:"mcp",payload:null,payloadHash:f(d.toString("utf8")),headers:g(s.headers)}).catch(()=>{}),r.status(401).json({jsonrpc:"2.0",error:{code:-32001,message:"Invalid signature"},id:null});return}p({webhookCallId:m(),receivedAt:new Date().toISOString(),teamId:a,owner:e.owner??null,sourceIp:s.ip??null,status:"accepted",source:"mcp",payload:null,payloadHash:f(d.toString("utf8")),headers:g(s.headers)}).catch(()=>{});let l;if(d.length>0)try{l=JSON.parse(d.toString("utf8"))}catch(u){r.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:`Parse error: ${u.message}`},id:null});return}const c=s.headers["mcp-session-id"];let t;if(c&&o[i(c,a)])t=o[i(c,a)];else if(!c&&w(l))t=new I({sessionIdGenerator:()=>m(),onsessioninitialized:S=>{o[i(S,a)]=t}}),t.onclose=()=>{t.sessionId&&delete o[i(t.sessionId,a)]},await(await v(e)).connect(t);else{r.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: no valid session ID"},id:null});return}await t.handleRequest(s,r,l)}async function h(s,r){const{teamId:a}=s.params,e=s.headers["mcp-session-id"];if(!e||!o[i(e,a)]){r.status(400).send("Invalid or missing session ID");return}await o[i(e,a)].handleRequest(s,r)}function R(s){const r=y.raw({type:"*/*",limit:"50mb"});s.post("/api/mcp/team/:teamId",r,(a,e)=>{P(a,e).catch(n=>{console.error("[MCP-Team] POST handler error:",n),e.headersSent||e.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal error"},id:null})})}),s.get("/api/mcp/team/:teamId",(a,e)=>{h(a,e).catch(n=>{console.error("[MCP-Team] GET handler error:",n),e.headersSent||e.status(500).end()})}),s.delete("/api/mcp/team/:teamId",(a,e)=>{h(a,e).catch(n=>{console.error("[MCP-Team] DELETE handler error:",n),e.headersSent||e.status(500).end()})})}export{R as mountTeamMcpRoutes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mcpToolsService as a}from"../core/services/mcp-tools-service";async function n(s,o){try{const e=s.user?.token,t=await a.listTools(e);o.json(t)}catch(e){console.error("Error in listMcpTools route:",e),o.status(500).json({success:!1,tools:[],total:0,error:"Internal server error",message:e.message})}}async function i(s,o){try{const{name:e}=s.params,t=s.user?.token;if(!e){o.status(400).json({success:!1,error:"Tool name is required"});return}const r=await a.getTool(e,t);if(!r.success){o.status(404).json(r);return}o.json(r)}catch(e){console.error("Error in getMcpTool route:",e),o.status(500).json({success:!1,error:"Internal server error",message:e.message})}}const m=[{method:"get",path:"/api/mcp/tools",handler:n,openapi:{summary:"List MCP tools",description:"Retrieves a list of all available MCP tools.",tags:["mcp"]}},{method:"get",path:"/api/mcp/tools/:name",handler:i,openapi:{summary:"Get MCP tool details",description:"Retrieves details for a specific MCP tool.",tags:["mcp"]}}];export{m as routes};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import re from"jsonwebtoken";import{Client as oe}from"@elastic/elasticsearch";import{z as o}from"zod";import{config as ne}from"../core/config";import{getCapabilities as z,isTrialExpired as ie,getTrialDaysLeft as le}from"../core/services/plan-service";import{createPlatform as ce,getPlatforms as pe,updatePlatform as ue}from"../core/services/platforms-service";import{listApiKeys as de,createApiKey as me,revokeApiKey as he,AGENT_SCOPES as ye}from"../core/services/api-keys-service";import{LLMServicesFactory as W}from"../core/services/llm-services";import{BRAND_NAME as B}from"../core/app-config/branding";import{LLM_INDICES as J}from"../core/llm/models";import{createMcpClient as $}from"../core/mcp/client";const F=process.env.STKXP_ONBOARDING_MCP_URL||"https://localhost:3001/mcp/stkxp-onboarding",C=new Set(["create_platform","create_tool","create_skill","create_assistant","create_team","enroll_integration"]);function G(r){let t=r.cookies?.token;if(!t){const s=r.headers.authorization??"";s.startsWith("Bearer ")&&(t=s.slice(7))}if(!t){const s=r.headers["x-api-key"];t=Array.isArray(s)?s[0]:s}return t??null}const v=new oe(ne.elasticsearch);function x(r){const t=r.auth?.username;if(t)return t;let s=r.cookies?.token;if(!s){const a=r.headers.authorization??"";s=a.startsWith("Bearer ")?a.slice(7):a||null}if(!s)return null;try{return re.decode(s)?.username??null}catch{return null}}const ge=o.object({username:o.string(),roles:o.array(o.string()),plan:o.enum(["trial","basic","enterprise","premium"]),planStatus:o.enum(["active","expired","cancelled","past_due"]),planActivatedAt:o.string().nullable(),planExpiresAt:o.string().nullable(),trialDaysLeft:o.number().nullable(),isExpired:o.boolean(),isSuperuser:o.boolean(),stripeCustomerId:o.string().optional(),capabilities:o.record(o.any()).nullable()}),fe=o.object({isComplete:o.boolean(),checks:o.object({platform:o.boolean(),tool:o.boolean(),llmProvider:o.boolean(),llmModel:o.boolean(),routingRule:o.boolean(),skill:o.boolean(),assistant:o.boolean(),team:o.boolean()})}),ke=o.object({apiKey:o.string().describe("Anthropic API key (sk-ant-\u2026). Empty strings rejected by handler with a clearer error.")}),Ne=[{method:"get",path:"/api/me",openapi:{summary:"Get authenticated user profile + plan",description:"Returns the caller's username, roles, current plan, capability flags and trial status. Superusers are reported with plan=`premium` and unlimited capabilities.",tags:["me"],responses:{200:ge}},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});try{const d=(await v.security.getUser({username:s}))[s],u=d?.metadata??{},y=d?.roles??[];if(y.includes("superuser"))return t.json({username:s,roles:y,plan:"premium",planStatus:"active",planActivatedAt:u.planActivatedAt??new Date().toISOString(),planExpiresAt:null,trialDaysLeft:null,isExpired:!1,isSuperuser:!0,capabilities:z("premium")});const k=u.plan??"trial";let l=u.planStatus??"active";const m=ie(u);m&&k==="trial"&&l==="active"&&(l="expired",v.security.putUser({username:s,body:{...d,metadata:{...u,planStatus:"expired"}}}).catch(()=>{}));const g=le(u),_=m||l==="expired"||l==="cancelled"||l==="past_due";return t.json({username:s,roles:y,plan:k,planStatus:l,planActivatedAt:u.planActivatedAt??null,planExpiresAt:u.planExpiresAt??null,trialDaysLeft:g,isExpired:_,isSuperuser:!1,stripeCustomerId:u.stripeCustomerId,capabilities:_?null:z(k)})}catch(a){return console.error("[/api/me] Error:",a),t.status(500).json({error:"Internal server error"})}}},{method:"get",path:"/api/me/api-keys",openapi:{summary:"List the caller's API keys",description:"Returns descriptors (id, name, prefix, createdAt) for the caller's API keys. Never returns the secret token \u2014 that is shown only once at creation.",tags:["me"]},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});try{const a=await de(s);return t.json({keys:a})}catch(a){return console.error("[/api/me/api-keys GET] Error:",a),t.status(500).json({error:"Internal server error"})}}},{method:"post",path:"/api/me/api-keys",validate:{body:o.object({name:o.string().trim().min(1).max(80),scopes:o.array(o.enum(ye)).optional()})},openapi:{summary:"Create an API key for the caller",description:"Mints a long-lived API key (a JWT carrying the caller's identity + roles + scopes). The secret token is returned ONCE in this response and never again \u2014 store it securely. Use it as `Authorization: Bearer <token>` or the `x-api-key` header. The key is owner-scoped exactly like the caller. `scopes` gates which /api/langgraph/* actions this key can perform (agent:execute, agent:stream, agent:cancel, agent:read-result) \u2014 omit it for an unrestricted key (all four).",tags:["me"]},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});try{const a=await me(s,r.body.name,r.body.scopes);return a?t.status(201).json({token:a.token,key:a.key}):t.status(404).json({error:"User not found"})}catch(a){return console.error("[/api/me/api-keys POST] Error:",a),t.status(500).json({error:"Internal server error"})}}},{method:"delete",path:"/api/me/api-keys/:id",validate:{params:o.object({id:o.string().min(1)})},openapi:{summary:"Revoke one of the caller's API keys",description:"Revokes the key identified by `id` (its jti). Subsequent requests using that key are rejected. Idempotent-ish: returns 404 if no such key exists for the caller.",tags:["me"]},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});try{return await he(s,r.params.id)?t.json({success:!0}):t.status(404).json({error:"API key not found"})}catch(a){return console.error("[/api/me/api-keys DELETE] Error:",a),t.status(500).json({error:"Internal server error"})}}},{method:"get",path:"/api/me/setup-status",openapi:{summary:"Onboarding setup status",description:"Checks which of the 8 onboarding prerequisites the user has at least one of: platform, tool, LLM provider (with apiKey), LLM model, routing rule, skill (graph), assistant, team.",tags:["me","onboarding"],responses:{200:fe}},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});const a=async p=>{try{const R=await v.count({index:p,body:{query:{term:{owner:s}}}});return R.count??R.body?.count??0}catch{return 0}},d=async()=>{try{const p=await v.search({index:".stkxp_llm_providers",size:100,body:{query:{term:{owner:s}},_source:["apiKey"]}});return(p.hits?.hits??p.body?.hits?.hits??[]).filter(j=>j._source?.apiKey&&j._source.apiKey!=="").length}catch{return 0}},[u,y,f,k,l,m,g,_]=await Promise.allSettled([a(".stkxp_platforms"),a(".stkxp_tools"),d(),a(".stkxp_llm_models"),a(".stkxp_llm_routing_rules"),a(".stkxp_graphs"),a(".stkxp_assistants"),a(".stkxp_teams")]),i=p=>p.status==="fulfilled"?p.value:0,n={platform:i(u)>0,tool:i(y)>0,llmProvider:i(f)>0,llmModel:i(k)>0,routingRule:i(l)>0,skill:i(m)>0,assistant:i(g)>0,team:i(_)>0};return t.json({isComplete:Object.values(n).every(Boolean),checks:n})}},{method:"post",path:"/api/me/onboarding/anthropic-platform",openapi:{summary:"Bootstrap Anthropic platform + LLM control plane",description:"Verifies the supplied Anthropic key with a Haiku ping, then idempotently persists: (1) the claude-code MCP platform, (2) an Anthropic LLM provider, (3) a Claude Haiku 4.5 model (if user has no models yet), (4) a default routing rule (if user has no rules yet).",tags:["me","onboarding"]},validate:{body:ke},handler:async(r,t)=>{const s=x(r);if(!s)return t.status(401).json({error:"Unauthorized"});const a=typeof r.body?.apiKey=="string"?r.body.apiKey.trim():"";if(!a)return t.status(400).json({error:"apiKey is required"});try{const n=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":a,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:"claude-haiku-4-5-20251001",max_tokens:1,messages:[{role:"user",content:"ping"}]})});if(!n.ok){const p=await n.text().catch(()=>"");return t.status(400).json({error:"Anthropic API key validation failed",status:n.status,details:p.slice(0,500)})}}catch(n){return t.status(502).json({error:"Could not reach Anthropic API",details:n?.message??String(n)})}const d="assistant-support",y={url:"https://51.158.76.76:3001/mcp/claude-code",protocol:"http",serverType:"remote",packageName:"stkxp-claude-code",namespace:`debian-${s}`,headers:{},queryParams:{apiKey:a}};let f,k;try{const p=(await pe(s,"self-hosted","MCPServer",1,50)).platforms.find(R=>R.name===d);p?(f=await ue(p.id,s,{enabled:!0,config:y}),k="updated"):(f=await ce({name:d,type:"MCPServer",managedType:"self-hosted",owner:s,enabled:!0,config:y}),k="created")}catch(n){return console.error("[/api/me/onboarding/anthropic-platform] persist error:",n),t.status(500).json({error:"Failed to persist claude-code platform",details:n?.message??String(n)})}const l=W.getInstance(v),m=l.getProvidersService(),g=l.getModelsService(),_=l.getRoutingService(),i={provider:null,model:null,routingRule:null,actions:{provider:"skipped",model:"skipped",routingRule:"skipped"}};try{const n=await m.listProviders({type:"anthropic",limit:100},s);n.providers.length>0?(i.provider=await m.updateProvider(n.providers[0].id,{apiKey:a,enabled:!0}),i.actions.provider="updated"):(i.provider=await m.createProvider({name:"Anthropic (default)",type:"anthropic",endpoint:"https://api.anthropic.com/v1",apiKey:a,enabled:!0,owner:s,capabilities:{streaming:!0,toolCalling:!0,jsonMode:!0,vision:!0,multimodal:!0},metadata:{owner:s,region:"global",environment:"prod",costTier:"pay-as-you-go"}}),i.actions.provider="created")}catch(n){console.error("[onboarding] provider bootstrap failed:",n),i.actions.provider="failed"}try{await v.count({index:J.MODELS,body:{query:{term:{owner:s}}}}).then(p=>p.count??p.body?.count??0).catch(()=>0)===0&&i.provider&&(i.model=await g.createModel({providerId:i.provider.id,name:"Claude Haiku 4.5",modelId:"claude-haiku-4-5",contextWindow:2e5,capabilities:{streaming:!0,toolCalling:!0,jsonMode:!0,vision:!1,multimodal:!1},pricing:{inputTokens:.8,outputTokens:4,currency:"USD"},parameters:{temperature:0,maxTokens:4096},enabled:!0,owner:s}),i.actions.model="created")}catch(n){console.error("[onboarding] model bootstrap failed:",n),i.actions.model="failed"}try{if(await v.count({index:J.ROUTING_RULES,body:{query:{term:{owner:s}}}}).then(p=>p.count??p.body?.count??0).catch(()=>0)===0){const p=i.model?.id??"claude-haiku-4-5";i.routingRule=await _.createRoutingRule({name:"Default routing",owner:s,description:"Default fallback routing \u2014 created during onboarding. Catches all unmatched requests.",tags:["onboarding","default"],priority:1,enabled:!0,isDefault:!0,modelId:p,conditions:{},fallback:{enabled:!0,maxRetries:3,retryDelay:1e3,onError:"fail"}}),i.actions.routingRule="created"}}catch(n){console.error("[onboarding] routing rule bootstrap failed:",n),i.actions.routingRule="failed"}return t.json({platform:f,action:k,llm:i})}},{method:"post",path:"/api/me/onboarding/ai-builder/message",handler:async(r,t)=>{const s=x(r),a=G(r);if(!s||!a)return t.status(401).json({error:"Unauthorized"});const d=Array.isArray(r.body?.messages)?r.body.messages:null,u=Array.isArray(r.body?.missing)?r.body.missing:[];if(!d)return t.status(400).json({error:"messages array is required"});const l=(await W.getInstance(v).getProvidersService().listProviders({type:"anthropic",limit:10},s)).providers.find(e=>e.apiKey&&e.apiKey!=="");if(!l?.apiKey)return t.status(412).json({error:"No Anthropic API key configured for this user"});const m=async(e,h,I)=>{try{const N=await v.search({index:e,size:h,body:{query:{bool:{should:[{term:{owner:s}},{term:{owner:"stkxp"}},{term:{owner:"system"}}]}},sort:[{updatedAt:{order:"desc",unmapped_type:"date"}}]}});return(N.hits?.hits??N.body?.hits?.hits??[]).map(w=>I({...w._source??{},id:w._id}))}catch{return[]}},g=(e,h=240)=>typeof e=="string"&&e.length>h?e.slice(0,h)+"\u2026":e,_=e=>({id:e.id,name:e.name,type:e.type,managedType:e.managedType,enabled:e.enabled,url:e.config?.url??e.config?.endpoints?.elasticsearch?.url,packageName:e.config?.packageName}),i=e=>({id:e.id,name:e.name,type:e.type,endpoint:e.endpoint,httpMethod:e.httpMethod,platformId:e.platformId,indexPattern:e.indexPattern,tags:e.tags,description:g(e.description)}),n=e=>({id:e.id,name:e.name,description:g(e.description),version:e.version,graphType:e.graphType,nodes:Array.isArray(e.nodes)?e.nodes.slice(0,4).map(h=>({id:h.id,type:h.type})):void 0,edges:Array.isArray(e.edges)?e.edges.slice(0,6).map(h=>({from:h.from,to:h.to})):void 0}),p=e=>({id:e.id,name:e.name,topic:e.topic,description:g(e.description),tags:e.tags}),R=e=>({id:e.id,name:e.name,description:g(e.description),assistantIds:e.assistantIds,graphId:e.graphId}),[j,Y,H,X,V]=await Promise.all([m(".stkxp_platforms",5,_),m(".stkxp_tools",3,i),m(".stkxp_graphs",2,n),m(".stkxp_assistants",2,p),m(".stkxp_teams_v2",2,R)]),Q=`
|
|
2
|
+
|
|
3
|
+
## Snapshot of the user's current ${B} state (use these IDs and shapes \u2014 don't invent)
|
|
4
|
+
|
|
5
|
+
Existing platforms: ${JSON.stringify(j)}
|
|
6
|
+
Existing tools: ${JSON.stringify(Y)}
|
|
7
|
+
Existing skills (graphs): ${JSON.stringify(H)}
|
|
8
|
+
Existing assistants: ${JSON.stringify(X)}
|
|
9
|
+
Existing teams: ${JSON.stringify(V)}
|
|
10
|
+
|
|
11
|
+
These are real documents already in the user's account. When you need to reference one (e.g., a platformId for create_tool, a graphId for create_assistant, an assistantId for create_team), pick from this snapshot first. If you need more or fresher data, call the corresponding list_* MCP tool.
|
|
12
|
+
`,Z=`You are the ${B} onboarding helper. The user is missing these items: ${u.length?u.join(", "):"(none)"}. Walk them through creating each missing item one at a time, in this priority order: tool, skill, assistant, team.
|
|
13
|
+
|
|
14
|
+
You have MCP tools available. Use them \u2014 do NOT emit markdown blocks like \`\`\`proposal\`\`\` or \`\`\`json\`\`\` to describe documents.
|
|
15
|
+
|
|
16
|
+
## Available MCP tools (use them liberally)
|
|
17
|
+
|
|
18
|
+
**Read tools** \u2014 call as often as needed to ground yourself in real state:
|
|
19
|
+
- \`get_setup_status\` \u2014 which of the 8 items the user has configured.
|
|
20
|
+
- \`list_platforms\` (with optional type filter), \`list_tools\`, \`list_skills\`, \`list_assistants\`, \`list_teams\` \u2014 return the actual existing documents with their IDs.
|
|
21
|
+
- \`list_enrolled_integrations\` \u2014 Elastic Agent integrations already enrolled on the user's platforms (Apache, Nginx, \u2026). Check this BEFORE proposing create_tool over Elastic data.
|
|
22
|
+
- \`check_prerequisites\` \u2014 given a target (\`tool\` | \`skill\` | \`assistant\` | \`team\`) and an intent (\`elastic-agent\`, \`mcp_remote\`, etc.), returns missing prerequisites and the recommended next_action.
|
|
23
|
+
- \`search_resources\` \u2014 semantic search across knowledge-base resources (technical recipes, configuration references). Use this when the user asks how to do something, when you need a domain example, or to find the right field names / values.
|
|
24
|
+
|
|
25
|
+
**Write tools** \u2014 these get intercepted and shown to the user as editable proposal cards with a Save & continue button:
|
|
26
|
+
- \`create_platform\`, \`create_tool\`, \`create_skill\`, \`create_assistant\`, \`create_team\`.
|
|
27
|
+
- \`enroll_integration(platformId, packageName)\` \u2014 alternative to \`create_tool\` for Elastic data. Enrolls an Elastic Agent integration AND syncs its tools in one call (30-60s, returns dozens of pre-built tools). Strongly preferred over \`create_tool\` whenever the user's data is in a known Elastic integration package (apache, nginx, aws, kubernetes, mysql, system, \u2026) \u2014 saves the user from hand-crafting query and schema fields.
|
|
28
|
+
|
|
29
|
+
Read-tool calls are cheap. **Always prefer reading reality over guessing.** A snapshot of the user's current platforms/tools/skills/assistants/teams is appended below \u2014 that's the starting point, not the ceiling. Call \`list_*\` whenever you need fresher or more complete data, and \`search_resources\` whenever the user asks anything where the answer might live in the knowledge base.
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
1. **Ground yourself.** If you don't already have what you need from the snapshot or earlier turns, call the relevant \`list_*\` and/or \`search_resources\`. Never tell the user "I don't see any platforms" without having called \`list_platforms\` first.
|
|
34
|
+
2. **Check prerequisites** (\`check_prerequisites\`) before any \`create_*\` for tools/skills/assistants/teams. If \`ok: false\`, address the missing prerequisite first.
|
|
35
|
+
3. **Ask 1\u20132 short clarifying questions** in plain text (role, target system, name\u2026).
|
|
36
|
+
4. **Propose by calling the create tool.** Pass arguments matching the tool's input_schema. The user sees an editable card and clicks Save & continue.
|
|
37
|
+
5. **One write tool call per turn** \u2014 the user reviews them individually.
|
|
38
|
+
6. **After save**, the next user message confirms it. Re-check status if helpful, then propose the next item.
|
|
39
|
+
|
|
40
|
+
## After a save
|
|
41
|
+
|
|
42
|
+
When the user message says something like *"The X 'Y' was saved. Please continue with the next missing item."*, **trust it**. The system has just persisted Y on the user's behalf. Do NOT immediately call \`list_*\` or \`get_setup_status\` to "verify" \u2014 the read may race with index refresh and return stale data, sending you into a confused re-propose loop. Just acknowledge in one sentence and propose the next item.
|
|
43
|
+
|
|
44
|
+
Only re-check state if the user explicitly says something is wrong, missing, or unexpected.
|
|
45
|
+
|
|
46
|
+
## What NOT to do
|
|
47
|
+
|
|
48
|
+
- Do NOT emit \`\`\`proposal\`\`\` or \`\`\`json\`\`\` fenced blocks to describe documents. The system no longer parses them \u2014 proposals come exclusively from intercepted write-tool calls.
|
|
49
|
+
- Do NOT invent fields outside the input_schema each tool advertises. Schemas are strict and enforced server-side.
|
|
50
|
+
- Do NOT use enum values outside the schema (e.g., \`create_tool.type\` is \`mcp\` | \`a2a\` | \`mcp_remote\` | \`assistant_mapping\` \u2014 never \`elasticsearch\` or \`http\`).
|
|
51
|
+
- Do NOT claim something doesn't exist without first calling the relevant read tool.
|
|
52
|
+
- Do NOT re-verify a save you just got confirmation for in the same turn. Race conditions between write and search will lie to you.
|
|
53
|
+
- **Do NOT use placeholder strings for IDs.** When a tool needs an existing item's ID (e.g. \`graphId\` in \`create_assistant\`, \`assistantIds\` in \`create_team\`, \`platformId\` in \`create_tool\`), call the relevant \`list_*\` tool first and copy the **real** ES doc id from the response. Strings like \`your_skill_id_here\`, \`<id>\`, \`xxx\`, \`TODO\` are auto-rejected by the server with a clear error. If you don't have a real id available, propose creating that prerequisite first instead of fudging.
|
|
54
|
+
|
|
55
|
+
## Style
|
|
56
|
+
|
|
57
|
+
Keep replies short and action-oriented. The user sees your text plus the proposal card; don't repeat the card's contents in prose. Two or three sentences explaining what you're proposing and why is enough.`;let A,U;try{A=await $(F,{transportType:"http",headers:{Authorization:`Bearer ${a}`},timeout:3e4}),U=(await A.client.listTools())?.tools??[]}catch(e){return t.status(502).json({error:"Could not connect to onboarding MCP server",details:e?.message??String(e)})}const S=U.map(e=>({name:e.name,description:e.description,input_schema:e.inputSchema??{type:"object",properties:{}}})),T=[],K=[];let P="";const M=d.map(e=>({role:e.role==="assistant"?"assistant":"user",content:typeof e.content=="string"?e.content:String(e.content??"")})),ee=4;let q=!1;const te=S.length>0?[...S.slice(0,-1),{...S[S.length-1],cache_control:{type:"ephemeral"}}]:S,se=[{type:"text",text:Z+Q,cache_control:{type:"ephemeral"}}];try{for(let e=0;e<ee;e++){const h=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":l.apiKey,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:"claude-haiku-4-5-20251001",max_tokens:2048,system:se,tools:te,messages:M})});if(!h.ok){const c=await h.text().catch(()=>"");return t.status(502).json({error:"Anthropic call failed",status:h.status,details:c.slice(0,500)})}const I=await h.json(),N=I?.stop_reason??"",E=I?.content??[],w=E.filter(c=>c?.type==="text").map(c=>c?.text??"").join(""),O=E.filter(c=>c?.type==="tool_use");if(N==="end_turn"||O.length===0){P+=(P&&w?`
|
|
58
|
+
|
|
59
|
+
`:"")+w,q=!0;break}if(O.some(c=>C.has(c.name))){P+=(P&&w?`
|
|
60
|
+
|
|
61
|
+
`:"")+w;for(const c of O)if(C.has(c.name)){const b=c.input??{};K.push({toolName:c.name,arguments:b,title:typeof b?.name=="string"?b.name:c.name.replace("create_",""),summary:typeof b?.description=="string"?b.description:void 0,rationale:w||void 0})}break}M.push({role:"assistant",content:E});const D=[];for(const c of O)try{const b=await A.client.callTool({name:c.name,arguments:c.input??{}}),ae=Array.isArray(b?.content)?b.content.filter(L=>L?.type==="text").map(L=>L.text).join(`
|
|
62
|
+
`):JSON.stringify(b);T.push({tool:c.name,input:c.input,ok:!0}),D.push({type:"tool_result",tool_use_id:c.id,content:ae||"{}"})}catch(b){T.push({tool:c.name,input:c.input,ok:!1}),D.push({type:"tool_result",tool_use_id:c.id,is_error:!0,content:String(b?.message??b)})}M.push({role:"user",content:D})}}catch(e){try{await A.transport.close()}catch{}return t.status(502).json({error:"Tool-use loop failed",details:e?.message??String(e),trace:T})}try{await A.transport.close()}catch{}return t.json({reply:P||(q?"":"(loop reached max turns)"),proposals:K,trace:T})}},{method:"post",path:"/api/me/onboarding/ai-builder/save-proposal",openapi:{summary:"Persist a proposal from the AI-builder onboarding chat",description:"Forwards a create_* call to the stkxp-onboarding MCP server using the user's JWT. `toolName` must be one of: create_platform, create_tool, create_skill, create_assistant, create_team, enroll_integration.",tags:["me","onboarding"]},handler:async(r,t)=>{const s=x(r),a=G(r);if(!s||!a)return t.status(401).json({error:"Unauthorized"});const d=r.body?.toolName,u=r.body?.arguments??{};if(!d||!C.has(d))return t.status(400).json({error:`toolName must be one of: ${Array.from(C).join(", ")}`});let y;try{y=await $(F,{transportType:"http",headers:{Authorization:`Bearer ${a}`},timeout:3e4});const f=await y.client.callTool({name:d,arguments:u}),k=Array.isArray(f?.content)?f.content.filter(g=>g?.type==="text").map(g=>g.text).join(`
|
|
63
|
+
`):"";let l=null;try{l=JSON.parse(k)}catch{}try{await y.transport.close()}catch{}return l&&typeof l=="object"&&l.ok!==!1?t.json({ok:!0,result:l}):t.status(l?.status??502).json({error:"Underlying create call failed",toolName:d,innerError:l?.error??k??"unknown",innerStatus:l?.status})}catch(f){try{await y?.transport.close()}catch{}return t.status(502).json({error:"Save-proposal failed",details:f?.message??String(f)})}}}];export{Ne as routes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import q from"jsonwebtoken";import{Client as z}from"@elastic/elasticsearch";import{z as t}from"zod";import{config as C}from"../core/config";import{MEMORIES_INDEX as I,searchMemoriesRRF as x}from"../core/services/memories-service";const S=new z(C.elasticsearch);function g(r){const s=r.auth?.username;if(s)return s;let o=r.cookies?.token;if(!o){const e=r.headers.authorization??"";o=e.startsWith("Bearer ")?e.slice(7):e||void 0}if(!o)return null;try{return q.decode(o)?.username??null}catch{return null}}const R=["fact","preference","context","decision","episodic","entity","incident","procedural","chat_trace"],w=t.object({q:t.string().optional(),memoryType:t.enum(R).optional(),tag:t.string().optional(),limit:t.string().optional(),from:t.string().optional(),includeChatTraces:t.string().optional(),sortField:t.string().optional(),sortOrder:t.enum(["asc","desc"]).optional()}),A=t.object({q:t.string().min(1),size:t.number().int().min(1).max(50).optional().default(10),memoryType:t.enum(R).optional(),teamId:t.string().nullable().optional(),sourceChatId:t.string().nullable().optional(),includeChatTraces:t.boolean().optional()});async function F(r,s){const o=g(r);if(!o){s.status(401).json({success:!1,error:"Unauthorized"});return}try{const{q:e,memoryType:n,tag:a,limit:m="50",from:u="0",includeChatTraces:l,sortField:i,sortOrder:T}=r.query,d=[{term:{owner:o}}];n&&d.push({term:{memoryType:n}}),a&&d.push({term:{tags:a}});const p=[];l!=="true"&&n!=="chat_trace"&&p.push({term:{memoryType:"chat_trace"}});const h=[];e&&e.trim()&&h.push({multi_match:{query:e,fields:["text","tags^2"],fuzziness:"AUTO"}});const M={createdAt:"createdAt",updatedAt:"updatedAt",salience:"salience"},_=String(i||"createdAt"),y=M[_]||"createdAt",b=T==="asc"?"asc":"desc",c=await S.search({index:I,body:{query:{bool:{filter:d,...h.length?{must:h}:{},...p.length?{must_not:p}:{}}},size:parseInt(m,10),from:parseInt(u,10),sort:[{[y]:{order:b,unmapped_type:y==="salience"?"float":"date"}}],_source:{excludes:["text_semantic"]}}}),j=c.hits.hits.map(f=>({id:f._id,...f._source}));s.json({success:!0,total:typeof c.hits.total=="object"?c.hits.total.value:c.hits.total,memories:j})}catch(e){console.error("[Memories] listMemories error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function v(r,s){const o=g(r);if(!o){s.status(401).json({success:!1,error:"Unauthorized"});return}try{const{q:e,size:n,memoryType:a,teamId:m,sourceChatId:u,includeChatTraces:l}=r.body,i=await x({owner:o,query:e,k:n,teamId:m??null,sourceChatId:u??null,memoryType:a,includeChatTraces:l??!1});s.json({success:!0,total:i.length,memories:i})}catch(e){console.error("[Memories] searchMemories error:",e.message),s.status(500).json({success:!1,error:e.message})}}const D=[{method:"get",path:"/api/memories",handler:F,validate:{query:w}},{method:"post",path:"/api/memories/search",handler:v,validate:{body:A}}];export{D as routes};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import i from"zod";import{getLLMStats as O,getLLMCosts as U,getToolsStats as P,getLLMPerformance as A,getToolsAnalytics as T,getOverviewStats as H,getLLMStatsByProvider as M,getLLMStatsByModel as E,getToolsByServer as q,getToolsByTool as Q,getGraphsStats as _,getAssistantsStats as F,getTeamsStats as B,getRunsDistribution as N,getRunsList as C,getRunDetail as $,getTeamDetail as x,getAssistantDetail as G}from"../core/services/monitoring-analytics-service";import{getToolErrors as J,getFailedRuns as W,getRunErrorTypes as X}from"../core/services/error-analytics-service";import{createAlert as K,listAlerts as V,getAlertById as v,updateAlert as Y,deleteAlert as Z}from"../core/services/alerts-service";import{evaluateAlert as ee}from"../services/alert-evaluator";import{getCostForecastReport as te}from"../services/cost-forecast-service";import{getNodeLatencyReport as re}from"../core/services/node-latency-service";import{getMemoryAnalyticsReport as se}from"../core/services/memory-analytics-service";import{getHITLAnalyticsReport as ne}from"../core/services/hitl-analytics-service";import{getPromptCacheReport as oe}from"../core/services/prompt-cache-service";import{getQuotaReport as ae}from"../core/services/quota-service";import{listActiveRuns as ie,getActiveRunMeta as b}from"../core/runtime/active-runs-registry";import{subscribeToTrace as ue}from"../core/runtime/trace-bus";import{userToken as c}from"../services/auth";const p=i.object({startDate:i.string().optional(),endDate:i.string().optional()}),de=i.object({provider:i.string(),model:i.string()}),ce=i.enum(["run_failure_count","run_failure_rate","tool_error_count","tool_error_rate","avg_run_duration_p95_ms","total_cost_per_day_usd"]),le=i.enum(["gt","gte","lt","lte"]),me=i.enum(["15m","1h","6h","24h","7d"]),S=i.object({name:i.string().min(1).max(200),description:i.string().max(1e3).optional(),enabled:i.boolean(),metric:ce,operator:le,threshold:i.number().finite(),window:me,filters:i.object({assistantId:i.string().optional(),teamId:i.string().optional(),toolName:i.string().optional()}).optional(),notify:i.object({webhook:i.object({url:i.string().url()}).optional(),inApp:i.boolean().optional()})}),R=i.object({id:i.string().min(1)});function l(t){const r=t.auth?.username;if(r)return r;let n=t.cookies?.token;if(!n){const s=t.headers.authorization;s&&s.startsWith("Bearer ")&&(n=s.substring(7))}return n?c(n)?.username||null:(console.warn("[monitoring-analytics] No token found in request"),null)}function h(t){let r=t.cookies?.token;if(!r){const e=t.headers.authorization;e&&e.startsWith("Bearer ")&&(r=e.substring(7))}return r?c(r)?.role==="superuser":!1}const ye=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=t.query,s=e.startDate&&e.endDate?{start:e.startDate,end:e.endDate}:void 0,o=h(t)?void 0:n,a=await O(s,o);r.status(200).json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("Error getting LLM stats:",e),r.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve LLM stats"}}})}},ge=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=t.query,s=e.startDate&&e.endDate?{start:e.startDate,end:e.endDate}:void 0,o=h(t)?void 0:n,a=await U(s,o);r.status(200).json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("Error getting LLM costs:",e),r.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve LLM costs"}}})}},pe=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{provider:e,model:s}=t.params,o=t.query,a=o.startDate&&o.endDate?{start:o.startDate,end:o.endDate}:void 0,d=h(t)?void 0:n,u=await A(e,s,a,d);r.status(200).json(u)}catch(e){console.error("Error getting LLM performance:",e),r.status(500).json({error:e.message||"Failed to retrieve LLM performance"})}},he=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=t.query,s=e.startDate&&e.endDate?{start:e.startDate,end:e.endDate}:void 0,o=h(t)?void 0:n,a=await P(s,o);r.status(200).json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("Error getting tools stats:",e),r.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tools stats"}}})}},fe=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({error:"Unauthorized - Invalid or missing token"});return}try{const e=t.query,s=e.startDate&&e.endDate?{start:e.startDate,end:e.endDate}:void 0,o=h(t)?void 0:n,a=await T(s,o);r.status(200).json(a)}catch(e){console.error("Error getting tools analytics:",e),r.status(500).json({error:e.message||"Failed to retrieve tools analytics"})}},y=i.object({start:i.string().optional(),end:i.string().optional()}),Re=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await H(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting overview stats:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},ve=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await M(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting LLM stats by provider:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},be=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await E(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting LLM stats by model:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},Se=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await q(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting tools by server:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},Ie=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=t.query.source==="chat"?"chat":t.query.source==="playground"?"playground":"all",d=s&&o?{start:s,end:o}:void 0,u=e.role==="superuser"?void 0:e.username,m=Number.parseInt(String(t.query.page??""),10),g=Number.parseInt(String(t.query.pageSize??""),10),f=String(t.query.sortField??""),z=t.query.sortDirection==="asc"?"asc":"desc",k=new Set(["toolName","server","calls","successRate","avgDuration","avgInputBytes","avgOutputBytes"]),L=await Q(d,u,a,{page:Number.isFinite(m)?m:void 0,pageSize:Number.isFinite(g)?g:void 0,sortField:k.has(f)?f:void 0,sortDirection:z});r.json({body:{time:new Date().toISOString(),result:L}})}catch(s){console.error("Error getting tools by tool:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},je=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,[u,m,g]=await Promise.all([J(a,d),W(a,d),X(a,d)]);r.json({body:{time:new Date().toISOString(),result:{toolErrors:u,failedRuns:m,runErrorTypes:g}}})}catch(s){console.error("Error getting error analytics:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},we=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=await V(n);r.json({body:{time:new Date().toISOString(),result:{alerts:e}}})}catch(e){console.error("[alerts] list failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},De=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=t.body,s=await K({...e,owner:n});r.status(201).json({body:{result:{alert:s}}})}catch(e){console.error("[alerts] create failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},ze=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=await v(t.params.id,n);if(!e){r.status(404).json({body:{result:{error:"Alert not found"}}});return}r.json({body:{result:{alert:e}}})}catch(e){r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},ke=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=t.body,s=await Y(t.params.id,n,e);if(!s){r.status(404).json({body:{result:{error:"Alert not found"}}});return}r.json({body:{result:{alert:s}}})}catch(e){r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Le=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{if(!await Z(t.params.id,n)){r.status(404).json({body:{result:{error:"Alert not found"}}});return}r.json({body:{result:{success:!0}}})}catch(e){r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Oe=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}const e=t.cookies?.token||t.headers.authorization?.substring(7),o=c(e)?.role==="superuser"?void 0:n,a=ie(o);r.json({body:{time:new Date().toISOString(),result:{runs:a}}})},Ue=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}const e=t.params.id||t.params.runId;if(!e){r.status(400).json({body:{result:{error:"runId required"}}});return}const s=b(e),o=t.cookies?.token||t.headers.authorization?.substring(7),a=c(o);if(s&&s.username&&s.username!==n&&a?.role!=="superuser"){r.status(403).json({body:{result:{error:"Forbidden \u2014 not your run"}}});return}r.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),r.write(`event: ready
|
|
2
|
+
data: ${JSON.stringify({runId:e,ts:Date.now()})}
|
|
3
|
+
|
|
4
|
+
`);const u=ue(e,f=>{try{r.write(`event: trace
|
|
5
|
+
data: ${JSON.stringify(f)}
|
|
6
|
+
|
|
7
|
+
`)}catch{}}),m=setInterval(()=>{try{r.write(`: keep-alive ${Date.now()}
|
|
8
|
+
|
|
9
|
+
`)}catch{clearInterval(m)}},15e3),g=setInterval(()=>{b(e)||(setTimeout(()=>{try{r.write(`event: end
|
|
10
|
+
data: ${JSON.stringify({runId:e,reason:"completed"})}
|
|
11
|
+
|
|
12
|
+
`),r.end()}catch{}},2e3),clearInterval(g))},5e3);t.on("close",()=>{u(),clearInterval(m),clearInterval(g),console.log(`[live-trace] client disconnected from runId=${e}`)})},Pe=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{start:e,end:s}=t.query,a=await ae(n,e&&s?{start:e,end:s}:void 0);r.json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("[quota] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Ae=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{start:e,end:s}=t.query,a=await oe(n,e&&s?{start:e,end:s}:void 0);r.json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("[prompt-cache] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Te=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{start:e,end:s}=t.query,o=e&&s?{start:e,end:s}:void 0,a=t.cookies?.token||t.headers.authorization?.substring(7),u=c(a)?.role==="superuser"?void 0:n,m=await ne(o,u);r.json({body:{time:new Date().toISOString(),result:m}})}catch(e){console.error("[hitl-analytics] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},He=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{start:e,end:s}=t.query,a=await se(n,e&&s?{start:e,end:s}:void 0);r.json({body:{time:new Date().toISOString(),result:a}})}catch(e){console.error("[memory-analytics] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Me=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{start:e,end:s}=t.query,o=e&&s?{start:e,end:s}:void 0,a=t.query.assistantId?String(t.query.assistantId):void 0,d=t.query.teamId?String(t.query.teamId):void 0,u=(await Promise.resolve(c(t.cookies?.token||t.headers.authorization?.substring(7))))?.role==="superuser"?void 0:n,m=await re(o,u,{assistantId:a,teamId:d});r.json({body:{time:new Date().toISOString(),result:m}})}catch(e){console.error("[node-latency] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Ee=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=Number.parseInt(String(t.query.windowDays??""),10),s=Number.isFinite(e)&&e>0?Math.min(e,90):30,o=await te(n,{windowDays:s});r.json({body:{time:new Date().toISOString(),result:o}})}catch(e){console.error("[cost-forecast] failed:",e),r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},qe=async(t,r)=>{const n=l(t);if(!n){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const e=await v(t.params.id,n);if(!e){r.status(404).json({body:{result:{error:"Alert not found"}}});return}const s=await ee(e);r.json({body:{result:s}})}catch(e){r.status(500).json({body:{result:{error:e.message||"Internal error"}}})}},Qe=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await _(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting graphs stats:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},_e=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await F(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting assistants stats:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},Fe=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await B(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting teams stats:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},Be=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o}=t.query,a=s&&o?{start:s,end:o}:void 0,d=e.role==="superuser"?void 0:e.username,u=await N(a,d);r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting runs distribution:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},I=i.object({start:i.string().optional(),end:i.string().optional(),from:i.string().optional().transform(t=>t?parseInt(t,10):0),size:i.string().optional().transform(t=>t?parseInt(t,10):50)}),Ne=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7),e=c(n);if(!e){r.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{start:s,end:o,from:a,size:d}=I.parse(t.query),u=s&&o?{start:s,end:o}:void 0,m=e.role==="superuser"?void 0:e.username,g=await C(u,m,a,d);r.json({body:{time:new Date().toISOString(),result:g}})}catch(s){console.error("Error getting runs list:",s),r.status(500).json({body:{time:new Date().toISOString(),result:{error:s.message||"Internal error"}}})}},j=i.object({runId:i.string()}),Ce=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7);if(!c(n)){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{runId:s}=j.parse(t.params),o=await $(s);if(!o){r.status(404).json({body:{result:{error:"Run not found"}}});return}r.json({body:{time:new Date().toISOString(),result:o}})}catch(s){console.error("Error getting run detail:",s),r.status(500).json({body:{result:{error:s.message}}})}},w=i.object({teamId:i.string()}),$e=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7);if(!c(n)){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{teamId:s}=w.parse(t.params),{start:o,end:a,teamName:d}=t.query,m=await x(s,o&&a?{start:o,end:a}:void 0,d);if(!m){r.status(404).json({body:{result:{error:"Team not found"}}});return}r.json({body:{time:new Date().toISOString(),result:m}})}catch(s){console.error("Error getting team detail:",s),r.status(500).json({body:{result:{error:s.message}}})}},D=i.object({assistantId:i.string()}),xe=async(t,r)=>{const n=t.cookies?.token||t.headers.authorization?.substring(7);if(!c(n)){r.status(401).json({body:{result:{error:"Unauthorized"}}});return}try{const{assistantId:s}=D.parse(t.params),{start:o,end:a}=t.query,u=await G(s,o&&a?{start:o,end:a}:void 0);if(!u){r.status(404).json({body:{result:{error:"Assistant not found"}}});return}r.json({body:{time:new Date().toISOString(),result:u}})}catch(s){console.error("Error getting assistant detail:",s),r.status(500).json({body:{result:{error:s.message}}})}},lt=[{method:"get",path:"/api/monitoring/llm/stats",handler:ye,validate:{query:p},openapi:{summary:"Retrieves aggregated LLM usage statistics.",description:"Retrieves aggregated statistics for LLM usage.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/llm/costs",handler:ge,validate:{query:p},openapi:{summary:"Retrieves MCP configuration settings.",description:"Retrieves the MCP configuration settings.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/llm/:provider/:model/performance",handler:pe,validate:{params:de,query:p},openapi:{summary:"Retrieves aggregated LLM usage statistics.",description:"Retrieves aggregated statistics for LLM usage.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/tools/stats",handler:he,validate:{query:p},openapi:{summary:"Retrieves aggregated tools usage statistics.",description:"Retrieves aggregated statistics for tools usage.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/tools/analytics",handler:fe,validate:{query:p},openapi:{summary:"Retrieves tools analytics data.",description:"Retrieves detailed analytics data for tools usage.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/overview/stats",handler:Re,validate:{query:y},openapi:{summary:"Retrieves overview statistics.",description:"Retrieves aggregated statistics for the overview.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/llm/by-provider",handler:ve,validate:{query:y},openapi:{summary:"Retrieves LLM usage statistics by provider.",description:"Retrieves statistics for LLM usage grouped by provider.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/llm/by-model",handler:be,validate:{query:y},openapi:{summary:"Retrieves LLM usage statistics by model.",description:"Retrieves statistics for LLM usage grouped by model.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/tools/by-server",handler:Se,validate:{query:y},openapi:{summary:"Retrieves tools usage statistics by server.",description:"Retrieves statistics for tools usage grouped by server.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/tools/by-tool",handler:Ie,validate:{query:y},openapi:{summary:"Retrieves tools usage statistics by tool.",description:"Retrieves statistics for tools usage grouped by tool.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/graphs/stats",handler:Qe,validate:{query:y},openapi:{summary:"Retrieves graph statistics.",description:"Retrieves aggregated statistics for graphs.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/assistants/stats",handler:_e,validate:{query:y},openapi:{summary:"Retrieves assistant statistics.",description:"Retrieves aggregated statistics for assistants.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/teams/stats",handler:Fe,validate:{query:y},openapi:{summary:"Retrieves team statistics.",description:"Retrieves aggregated statistics for teams.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/distribution",handler:Be,validate:{query:y},openapi:{summary:"Retrieves run distribution statistics.",description:"Retrieves statistics for run distribution.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/list",handler:Ne,validate:{query:I},openapi:{summary:"Retrieves list of runs.",description:"Retrieves a list of all runs.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/:runId/detail",handler:Ce,validate:{params:j},openapi:{summary:"Retrieves detail for a specific run.",description:"Retrieves detailed information for a single run.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/teams/:teamId/detail",handler:$e,validate:{params:w},openapi:{summary:"Retrieves detail for a specific team.",description:"Retrieves detailed information for a single team.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/assistants/:assistantId/detail",handler:xe,validate:{params:D},openapi:{summary:"Retrieves detail for a specific assistant.",description:"Retrieves detailed information for a single assistant.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/errors",handler:je,validate:{query:y},openapi:{summary:"Retrieves error analytics.",description:"Retrieves error analytics for the specified time range.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/alerts",handler:we,openapi:{summary:"Retrieves list of alerts.",description:"Retrieves a list of all alerts for the authenticated user.",tags:["analytics"]}},{method:"post",path:"/api/monitoring/alerts",handler:De,validate:{body:S},openapi:{summary:"Creates a new alert.",description:"Creates a new alert for the authenticated user.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/alerts/:id",handler:ze,validate:{params:R},openapi:{summary:"Retrieves a specific alert.",description:"Retrieves detailed information for a single alert.",tags:["analytics"]}},{method:"put",path:"/api/monitoring/alerts/:id",handler:ke,validate:{params:R,body:S},openapi:{summary:"Updates a specific alert.",description:"Updates the details of a single alert.",tags:["analytics"]}},{method:"delete",path:"/api/monitoring/alerts/:id",handler:Le,validate:{params:R},openapi:{summary:"Deletes a specific alert.",description:"Deletes a single alert.",tags:["analytics"]}},{method:"post",path:"/api/monitoring/alerts/:id/evaluate",handler:qe,validate:{params:R},openapi:{summary:"Evaluates a specific alert.",description:"Evaluates the status of a single alert.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/cost-forecast",handler:Ee,openapi:{summary:"Retrieves cost forecast data.",description:"Retrieves cost forecast data for the authenticated user.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/node-latency",handler:Me,validate:{query:y},openapi:{summary:"Retrieves node-level latency data.",description:"Retrieves latency data for individual nodes.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/memory-analytics",handler:He,validate:{query:y},openapi:{summary:"Retrieves long-term memory analytics.",description:"Retrieves analytics for the long-term memory system.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/hitl-analytics",handler:Te,validate:{query:y},openapi:{summary:"Retrieves Human-In-The-Loop analytics.",description:"Retrieves analytics for the Human-In-The-Loop system.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/prompt-cache",handler:Ae,validate:{query:y},openapi:{summary:"Retrieves prompt cache analytics.",description:"Retrieves analytics for the prompt cache system.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/quota",handler:Pe,validate:{query:y},openapi:{summary:"Retrieves quota information.",description:"Retrieves information about the user's quota usage.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/active",handler:Oe,openapi:{summary:"Get monitoring runs active",description:"Retrieves monitoring runs active.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/:id/tail",handler:Ue,openapi:{summary:"Tail a specific run.",description:"Retrieves the tail of a specific run.",tags:["analytics"]}}];export{lt as routes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import o from"zod";import{getRecentRuns as S,getRunDetails as m,getRunsByUser as R,getFailedRuns as h,getRunStats as I}from"../core/services/monitoring-service";import{getToolsByRunId as b}from"../core/services/chat-tools-service";import{userToken as g}from"../services/auth";const v=o.object({from:o.coerce.number().optional().default(0),size:o.coerce.number().optional().default(50),status:o.enum(["running","completed","failed","timeout"]).optional(),topic:o.string().optional(),cluster:o.string().optional()}),f=o.object({runId:o.string()}),K=o.object({start:o.string(),end:o.string()}),w=o.object({start:o.string().optional(),end:o.string().optional()}),V=o.object({startDate:o.string().optional(),endDate:o.string().optional()}),X=o.object({provider:o.string(),model:o.string()});function u(r){const e=r.auth?.username;if(e)return e;let n=r.cookies?.token;if(!n){const s=r.headers.authorization;s&&s.startsWith("Bearer ")&&(n=s.substring(7))}return n?g(n)?.username||null:(console.warn("[monitoring] No token found in request"),null)}function d(r){let e=r.cookies?.token;if(!e){const t=r.headers.authorization;t&&t.startsWith("Bearer ")&&(e=t.substring(7))}return e?g(e)?.role==="superuser":!1}const D=async(r,e)=>{const n=u(r);if(!n){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const t=r.query,s=t.from||0,i=t.size||50,c=d(r)?void 0:n,{runs:p,total:y}=await S(s,i,c);let a=p;t.status&&(a=a.filter(l=>l.status===t.status)),t.topic&&(a=a.filter(l=>l.topic===t.topic)),t.cluster&&(a=a.filter(l=>l.cluster===t.cluster)),e.status(200).json({body:{time:new Date().toISOString(),result:{runs:a,total:y,from:s,size:i}}})}catch(t){console.error("Error getting runs:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve runs"}}})}},j=async(r,e)=>{const n=u(r);if(!n){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{runId:t}=r.params,s=await m(t);if(!s){e.status(404).json({body:{time:new Date().toISOString(),result:{error:"Run not found"}}});return}if(!d(r)&&s.username!==n){e.status(403).json({body:{time:new Date().toISOString(),result:{error:"Access denied"}}});return}e.status(200).json({body:{time:new Date().toISOString(),result:s}})}catch(t){console.error("Error getting run details:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve run details"}}})}},O=async(r,e)=>{const n=u(r);if(!n){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{runId:t}=r.params,s=await m(t);if(!s){e.status(404).json({body:{time:new Date().toISOString(),result:{error:"Run not found"}}});return}if(!d(r)&&s.username!==n){e.status(403).json({body:{time:new Date().toISOString(),result:{error:"Access denied"}}});return}const i=await b(t);e.status(200).json({body:{time:new Date().toISOString(),result:i||{tools:[]}}})}catch(t){console.error("Error getting run tools:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve run tools"}}})}},z=async(r,e)=>{const n=u(r);if(!n){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const t=r.query,s=t.start&&t.end?{start:t.start,end:t.end}:void 0,i=d(r)?void 0:n,c=await I(s,i);e.status(200).json({body:{time:new Date().toISOString(),result:c}})}catch(t){console.error("Error getting run stats:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve run stats"}}})}},U=async(r,e)=>{const n=u(r);if(!n){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const t=d(r)?void 0:n,s=await h(t);e.status(200).json({body:{time:new Date().toISOString(),result:{runs:s,total:s.length}}})}catch(t){console.error("Error getting failed runs:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve failed runs"}}})}},k=async(r,e)=>{if(!u(r)){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}if(!d(r)){e.status(403).json({body:{time:new Date().toISOString(),result:{error:"Access denied - Superuser role required"}}});return}try{const{username:t}=r.params,s=await R(t);e.status(200).json({body:{time:new Date().toISOString(),result:{runs:s,total:s.length,username:t}}})}catch(t){console.error("Error getting runs by user:",t),e.status(500).json({body:{time:new Date().toISOString(),result:{error:t.message||"Failed to retrieve user runs"}}})}},Y=[{method:"get",path:"/api/monitoring/runs/stats",handler:z,validate:{query:w},openapi:{summary:"Get aggregated statistics for runs",description:"Retrieves aggregated statistics for runs.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/failed",handler:U,openapi:{summary:"Retrieves failed runs.",description:"Retrieves a list of failed runs.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/by-user/:username",handler:k,openapi:{summary:"Get runs for a specific user.",description:"Retrieves all runs for a specific user.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/:runId/tools",handler:O,validate:{params:f},openapi:{summary:"Get tools used in a specific run.",description:"Retrieves a list of tools used in a specific run.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs/:runId",handler:j,validate:{params:f},openapi:{summary:"Retrieves details for a specific run.",description:"Retrieves detailed information for a specific run.",tags:["analytics"]}},{method:"get",path:"/api/monitoring/runs",handler:D,validate:{query:v},openapi:{summary:"Retrieves monitoring runs",description:"Retrieves monitoring runs.",tags:["analytics"]}}];export{Y as routes};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import{Client as f}from"@elastic/elasticsearch";const g=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",_=process.env.ELASTICSEARCH_USER||"elastic",y=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",a=new f({node:g,auth:{username:_,password:y},tls:{rejectUnauthorized:!1},requestTimeout:3e4,maxRetries:3}),r=".stkxp_node_types",c=[{id:"check_reset",label:"Check Reset",description:"Detects the start of a new conversation and resets message history if the threadId changed. Place first in any multi-turn graph. No configuration parameters.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1}},{id:"topic_detection",label:"Topic Detection",description:"Classifies the user message into a topic (e.g. clusters_nodes_inventory) to route to the right MCP prompts or sub-graph. Detection priority: manual override \u2192 fixed assistant topic \u2192 RegExp patterns \u2192 hybrid LLM call. Writes: state.topic.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`Tu g\xE9n\xE8res un mot-cl\xE9 (topic) unique parmi la liste fournie, bas\xE9 sur le message du user.
|
|
2
|
+
|
|
3
|
+
Analyse le message de l'utilisateur et retourne le topic le plus appropri\xE9 parmi la liste disponible.
|
|
4
|
+
Sois pr\xE9cis et choisis le topic qui correspond le mieux \xE0 l'intention de l'utilisateur.`},{id:"system",label:"System",description:"Loads and injects the system prompt into message history. Resolution order: assistant.promptId \u2192 inline assistant.context \u2192 MCP topic prompt \u2192 node instructions. Composition modes: replace (default), prepend, append.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!0}},{id:"assistant_tool_executor",label:"Assistant Tool Executor",description:"Calls the LLM with all MCP tools bound. The LLM reasons and produces an AIMessage with tool_calls (or a direct answer). Produces edges: has_tool_calls / no_tool_calls.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`\u{1F527} CRITICAL TOOL EXECUTOR INSTRUCTIONS \u{1F527}
|
|
5
|
+
|
|
6
|
+
YOU ARE A TOOL EXECUTOR AGENT. Your ONLY job is to:
|
|
7
|
+
1. \u2705 Analyze the user's question
|
|
8
|
+
2. \u2705 Select the BEST tools to answer the question
|
|
9
|
+
3. \u2705 Use tool_calls to execute those tools
|
|
10
|
+
4. \u274C DO NOT generate any JSON response directly
|
|
11
|
+
5. \u274C DO NOT write markdown or text content yourself
|
|
12
|
+
|
|
13
|
+
The tools will be executed by another system, and then a separate agent will generate the final response.
|
|
14
|
+
|
|
15
|
+
IGNORE ANY INSTRUCTIONS BELOW that say "Output JSON blocks" or "Start response with JSON" - those are for the FINAL response generator, NOT for you.
|
|
16
|
+
|
|
17
|
+
YOUR TASK: Generate tool_calls for the tools you want to use, then STOP.
|
|
18
|
+
|
|
19
|
+
Available tools: {{availableTools}}`},{id:"code_executor",label:"Code Executor",description:"Calls the LLM with a namespaced tool catalog and asks it to write ONE JS script composing multiple tool calls (branches/loops/aggregation), executed in an isolated-vm sandbox. Counts as a single toolExecRounds increment regardless of how many tools the script calls. Destructive tool calls (tool._destructiveHint) pause for human approval via the same interrupt() mechanism as human_approval.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`\u{1F527} CODE EXECUTOR AGENT \u{1F527}
|
|
20
|
+
|
|
21
|
+
You compose tool calls by writing ONE JavaScript async function body, instead of calling tools one at a time.
|
|
22
|
+
|
|
23
|
+
Rules:
|
|
24
|
+
1. Respond with a single fenced \`\`\`js code block containing the BODY of an async function (statements only, no function wrapper).
|
|
25
|
+
2. Call tools as \`await tools.<namespace>.<toolName>(args)\` \u2014 namespaces and tool names are listed below.
|
|
26
|
+
3. End with a \`return <value>;\` statement \u2014 this becomes the final answer.
|
|
27
|
+
4. Do NOT branch on \`Date.now()\` or \`Math.random()\` before a destructive tool call \u2014 the script may be re-run from the top if a human approval is pending, and non-deterministic branching before that point breaks replay.
|
|
28
|
+
5. Wrap risky calls in try/catch if you want to handle a tool's failure gracefully instead of aborting the whole script.
|
|
29
|
+
6. Need a KB resource's full content? Read it as \`resources.<name>.prompt_text\` (see the list below) \u2014 don't ask for it, it's already in scope.
|
|
30
|
+
|
|
31
|
+
Available tools, grouped by namespace:
|
|
32
|
+
{{availableTools}}
|
|
33
|
+
|
|
34
|
+
Available resources (KB entries \u2014 read resources.<name>.prompt_text in your script for the full content):
|
|
35
|
+
{{availableResources}}`},{id:"static_code_executor",label:"Static Code Executor",description:"Runs a FIXED, user-authored JS script (the node's own \"instructions\" field IS the script \u2014 not a prompt) against the same isolated-vm sandbox and tool bridge as Code Executor. No LLM call is made \u2014 cheaper and faster than Code Executor when the composition logic doesn't need to vary per turn. The script gets a `context` global ({ threadId, topic, customFields }) and a `resources` global (.stkxp_resources KB entries, keyed by sanitized title) for dynamic values \u2014 never the raw state (no mandatoryParams/userToken). Destructive tool calls still pause for human approval via interrupt().",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!0},defaultInstructions:`// This is the script itself \u2014 it runs directly, no LLM involved.
|
|
36
|
+
// Call tools as: await tools.<namespace>.<toolName>(args)
|
|
37
|
+
// Dynamic values: context.threadId, context.topic, context.customFields.<field>
|
|
38
|
+
// Read a KB resource as: resources.<name>.prompt_text (also .description, .use_case, .type, .tags)
|
|
39
|
+
// End with a return statement \u2014 this becomes the final answer.
|
|
40
|
+
// Replace the example below with your own logic.
|
|
41
|
+
|
|
42
|
+
// Example: look up a client's orders, then their support tickets, and
|
|
43
|
+
// combine both into one summary. A state_setter node upstream would set
|
|
44
|
+
// context.customFields.clientId before this node runs.
|
|
45
|
+
const clientId = context.customFields.clientId;
|
|
46
|
+
|
|
47
|
+
let orders;
|
|
48
|
+
try {
|
|
49
|
+
orders = await tools.mysql_toolbox.mysql_client({ client_id: clientId });
|
|
50
|
+
} catch (e) {
|
|
51
|
+
orders = { error: e.message };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const tickets = await tools.zendesk_toolbox.list_tickets({ client_id: clientId });
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
clientId,
|
|
58
|
+
ordersCount: Array.isArray(orders) ? orders.length : 0,
|
|
59
|
+
orders,
|
|
60
|
+
tickets,
|
|
61
|
+
};`},{id:"tool",label:"Tool",description:"Executes tool calls produced by assistant_tool_executor. Parallel execution (MAX_PARALLEL_TOOLS env var), exponential retry on 429/502/503/504. Supports form interruption via _requiresForm. Produces edges: tool_success / tool_error.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1}},{id:"tool_invoker",label:"Tool Invoker (deterministic, no LLM)",description:'Deterministic NO-LLM tool wrapper. Emits a fixed tool_call so the downstream `tool` node executes it WITHOUT any LLM call (zero tokens). Configure two custom fields on the node: `toolName` (exact registered tool name) and `argsMapping` (object mapping tool argument \u2192 Mustache template). Mustache context: {{input}}/{{query}} = latest human message text, {{<key>}} = value from state (variables/toolArgs/context), plus {{#var}}\u2026{{/var}} / {{^var}}\u2026{{/var}} sections. A value that is exactly one placeholder keeps the raw typed value; non-string values (e.g. "limit": 10) pass through as literals. For fan-out, use `toolCalls: [{ toolName, argsMapping }, \u2026]` instead. Typical wrapper graph: __start__ \u2192 tool_invoker \u2192 tool \u2192 __end__ (add assistant_response_generator at the end only if you want NL-formatted output \u2014 that node DOES use an LLM).',builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"raw_output",label:"Raw Output (deterministic, no LLM)",description:'NO-LLM terminal node that dumps the raw state as the final response (zero tokens), so a tool-wrapper graph no longer ends in "(No output generated)". Typical graph: __start__ \u2192 tool_invoker \u2192 tool \u2192 raw_output \u2192 __end__. Custom fields on the node: `stateField` (optional \u2014 which state field to dump; default chain = last tool result \u2192 teamAssistantResults \u2192 remaining state), `jsonata` (optional \u2014 a JSONata expression applied to the resolved source BEFORE dumping, to project/filter down to the exact key(s) you want; source is coerced to JSON leniently, incl. concatenated `}{`/NDJSON tool payloads; fail-open), `format` ("json" default | "text"), and `title`. Examples \u2014 jsonata "department" \u2192 "PUBLIC SAFETY"; jsonata "$[0].{\\"dept\\":department,\\"amount\\":actual_amount}".',builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"state_setter",label:"State Setter (deterministic, no LLM)",description:'NO-LLM node (zero tokens) that extracts a value from the LAST tool result via a JSONata expression and writes it into a whitelisted state field \u2014 the bridge that lets a `tool_invoker \u2192 tool` result reach a typed channel like `compareChatId` that downstream nodes read. Twin of `raw_output`, but it WRITES to state instead of emitting a message. Custom fields on the node: `jsonata` (the expression, applied to the last ToolMessage coerced to JSON \u2014 incl. concatenated `}{`/NDJSON payloads) and `stateField` (the target channel; allowed: compareChatId, enrichChatId, context, variables, toolArgs). Fail-open: missing/invalid config, unparseable source, or a null/empty result writes NOTHING. Example \u2014 jsonata "body.result.chat.id", stateField "compareChatId". Typical graph: \u2026 -> tool_invoker -> tool -> state_setter -> compare_context -> \u2026 Note: compare_context is only auto-injected when deps.compareChatId is set at build time \u2014 for this runtime topology, declare compare_context as an explicit node in your graph document.',builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"assistant_tool_cleaner",label:"Tool Cleaner",description:"Deduplicates and compresses tool results before response generation. Makes a lightweight LLM call to summarise results into compact JSON, reducing LLM context size.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`You are a tool result compression agent. Your job is to analyze tool results and keep ONLY relevant fields.
|
|
62
|
+
|
|
63
|
+
\u26A0\uFE0F CRITICAL INSTRUCTIONS:
|
|
64
|
+
1. Analyze the ToolMessage results in the conversation history
|
|
65
|
+
2. Identify the user's original query intent
|
|
66
|
+
3. Extract ONLY the fields that are relevant to answer the user's question
|
|
67
|
+
4. Remove unnecessary metadata, verbose fields, and redundant information
|
|
68
|
+
5. Preserve the data structure but compress the content
|
|
69
|
+
6. Return compressed ToolMessages that maintain the same tool_call_id
|
|
70
|
+
|
|
71
|
+
COMPRESSION STRATEGY:
|
|
72
|
+
- Keep: fields directly related to the user's query
|
|
73
|
+
- Remove: timestamps, metadata, internal IDs, verbose descriptions
|
|
74
|
+
- Simplify: nested objects when only specific fields are needed
|
|
75
|
+
- Aggregate: repetitive data into summaries when appropriate
|
|
76
|
+
|
|
77
|
+
OUTPUT FORMAT:
|
|
78
|
+
Return the same message structure but with compressed tool results.
|
|
79
|
+
Maintain all tool_call_id references to preserve the conversation flow.`},{id:"assistant_response_generator",label:"Response Generator",description:"Generates the final structured response as NDJSON blocks (markdown, echarts, recharts, eui, table, mermaid, remotion). Terminal node of most read-type graphs.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`You are a helpful assistant that responds in NDJSON format (newline-delimited JSON).
|
|
80
|
+
|
|
81
|
+
\u26A0\uFE0F CRITICAL INSTRUCTIONS:
|
|
82
|
+
You have just executed tool(s) and received results in ToolMessage(s). Your task is to:
|
|
83
|
+
1. CAREFULLY ANALYZE the tool results provided in the conversation history
|
|
84
|
+
2. EXTRACT key insights, metrics, and data from the tool responses
|
|
85
|
+
3. GENERATE a comprehensive, detailed NDJSON response that answers the user's question
|
|
86
|
+
4. CREATE multiple visualization blocks (charts, tables, stats) to present the data clearly
|
|
87
|
+
|
|
88
|
+
DO NOT generate an empty response. You MUST analyze the tool results and provide meaningful content.
|
|
89
|
+
|
|
90
|
+
RESPONSE FORMAT:
|
|
91
|
+
Each line of your response must be a valid JSON object. Use ONLY the following BLOCK types:
|
|
92
|
+
|
|
93
|
+
Types de BLOCKS autoris\xE9s:
|
|
94
|
+
- {"type":"markdown","title":"...","body":"..."}
|
|
95
|
+
- body: texte simple. Autoris\xE9: listes avec tirets, sauts de ligne. Interdit: code ex\xE9cutable.
|
|
96
|
+
- Use this for explanations, analysis, summaries, and insights from tool results
|
|
97
|
+
|
|
98
|
+
- {"type":"echarts","title":"...","description":"...","options":{...}}
|
|
99
|
+
- options: configuration ECharts pure JSON. formatters et labels doivent \xEAtre des cha\xEEnes statiques (ex: "{c}%").
|
|
100
|
+
- Use this for complex visualizations (pie charts, bar charts, line charts, gauges)
|
|
101
|
+
|
|
102
|
+
- {"type":"recharts","title":"...","component":"PieChart|BarChart|LineChart|AreaChart","data":[...],"children":[...]}
|
|
103
|
+
- Aucune fonction dans les props. Donn\xE9es pr\xE9-agr\xE9g\xE9es.
|
|
104
|
+
- Alternative charting library for simpler visualizations
|
|
105
|
+
|
|
106
|
+
- {"type":"eui","title":"...","description":"...","body":"..."}
|
|
107
|
+
- body: composants Elastic EUI en format JSX-string. Exemple de rendu UI attendu :
|
|
108
|
+
<EuiFlexGroup gutterSize="l"><EuiFlexItem></EuiFlexItem></EuiFlexGroup><EuiSpacer />
|
|
109
|
+
----------------
|
|
110
|
+
<EuiFlexGroup><EuiFlexItem><EuiStat title="7,600To" description="Total storage" /></EuiFlexItem></EuiFlexGroup>
|
|
111
|
+
----------------
|
|
112
|
+
<><EuiHealth color="text">Unknown</EuiHealth><EuiSpacer /><EuiHealth color="success">Green</EuiHealth><EuiSpacer /><EuiHealth color="warning">Yellow</EuiHealth><EuiSpacer /><EuiHealth color="danger">Red</EuiHealth><EuiSpacer /><EuiHealth color="#000000">Custom color as hex</EuiHealth></>
|
|
113
|
+
----------------
|
|
114
|
+
<EuiBasicTable tableCaption="Demo of EuiBasicTable" responsiveBreakpoint={false} items={[{id:"id",name: "name", online: true, }]} rowHeader="name" columns={[{"field": "id","name": "ID", "truncateText": true, "mobileOptions": {"show": false}},{"field": "name", "name": "Name" },{"field": "online", "name": "Online", "truncateText": true, "mobileOptions": {"show": false}}]}/>
|
|
115
|
+
----------------
|
|
116
|
+
<EuiAccordion id={simpleAccordionId} buttonContent="Click me to toggle"><EuiPanel color="subdued">Any content inside of <strong>EuiAccordion</strong> will appear here.</EuiPanel></EuiAccordion>
|
|
117
|
+
|
|
118
|
+
- Ic\xF4nes disponibles: "node", "heatmap", "temperature", "stats", "memory", "storage", "network", "database", "clock", "gear"
|
|
119
|
+
- titleColor: "primary" (bleu), "secondary" (gris), "text" (noir)
|
|
120
|
+
- Use this for metrics, node statistics, cluster status cards
|
|
121
|
+
|
|
122
|
+
- {"type":"table","title":"...","data":[...],"columns":[...]}
|
|
123
|
+
- Use this for tabular data extracted from tool results
|
|
124
|
+
|
|
125
|
+
- {"type":"mermaid","title":"...","description":"...","body":"..."}
|
|
126
|
+
- body: Mermaid diagram syntax. Supported types: graph TD/LR, sequenceDiagram, gantt, stateDiagram, gitGraph, journey, C4Context, pie, timeline, mindmap
|
|
127
|
+
- Use this to visualize flows, architectures, sequences, state machines, timelines
|
|
128
|
+
|
|
129
|
+
- {"type":"leaflet","title":"...","description":"...","center":[lat,lng],"zoom":5,"height":400,"tileLayer":"osm","markers":[...],"circles":[...],"polylines":[...],"polygons":[...]}
|
|
130
|
+
- center: required [lat, lng] coordinates for the map center
|
|
131
|
+
- zoom: zoom level (1\u201318, default 5)
|
|
132
|
+
- height: map height in pixels (default 400)
|
|
133
|
+
- tileLayer: "osm" (OpenStreetMap, default) or "mapbox" (requires mapboxToken)
|
|
134
|
+
- markers: [{"lat":...,"lng":...,"popup":"HTML label","color":"#hex"}]
|
|
135
|
+
- circles: [{"lat":...,"lng":...,"radius":meters,"color":"#hex","fillColor":"#hex","fillOpacity":0.2,"popup":"..."}]
|
|
136
|
+
- polylines: [{"points":[[lat,lng],...],"color":"#hex","weight":2,"opacity":0.8,"popup":"..."}]
|
|
137
|
+
- polygons: [{"points":[[lat,lng],...],"color":"#hex","fillColor":"#hex","fillOpacity":0.3,"popup":"..."}]
|
|
138
|
+
- Use this for geographic data: server locations, network topologies, regional distributions, infrastructure maps
|
|
139
|
+
|
|
140
|
+
- {"type":"remotion","templateId":"<one of the IDs below>","title":"...","props":{...}}
|
|
141
|
+
- templateId MUST be exactly one of these 10 values \u2014 any other value shows an error to the user instead of rendering:
|
|
142
|
+
- "stat-reveal": props {value:number, label:string, prefix?, suffix?, color?, durationInFrames?} \u2014 single number counting up to its final value
|
|
143
|
+
- "progress-bar": props {value:number, max?:number, label:string, color?, suffix?, durationInFrames?} \u2014 a bar filling toward value/max
|
|
144
|
+
- "radial-gauge": props {value:number, max?:number, label:string, color?, durationInFrames?} \u2014 a circular gauge filling toward value/max
|
|
145
|
+
- "before-after": props {before:number, after:number, label:string, prefix?, suffix?, color?, durationInFrames?} \u2014 one value morphing into another
|
|
146
|
+
- "ranking-list": props {title:string, items:[{label:string, value:number, color?}] (min 1), suffix?, durationInFrames?} \u2014 a ranked list of bars revealed in order
|
|
147
|
+
- "sparkline-card": props {title:string, value:string, data:number[] (min 2), color?, trendLabel?, durationInFrames?} \u2014 a headline value with a small trend line
|
|
148
|
+
- "donut-breakdown": props {title:string, slices:[{label:string, value:number, color:string}] (min 1), centerLabel?, durationInFrames?} \u2014 a donut chart building up slice by slice
|
|
149
|
+
- "timeline-steps": props {title:string, steps:[{title:string, detail?}] (min 1), color?, durationInFrames?} \u2014 a sequence of steps revealed one after another
|
|
150
|
+
- "insight-card": props {eyebrow?, title:string, body:string, accent?, durationInFrames?} \u2014 a short text callout fading/sliding in
|
|
151
|
+
- "kpi-sequence": props {title:string, items:[{value:string, label:string, color?}] (min 1), durationInFrames?} \u2014 several KPI numbers revealed in sequence
|
|
152
|
+
- Each template plays as a short live animation \u2014 NOT a static chart. Pick the template whose shape matches the data (a single headline number \u2192 stat-reveal, a ranked comparison \u2192 ranking-list, a step-by-step process \u2192 timeline-steps, etc.) \u2014 never force multi-series/tabular data into stat-reveal, use table/echarts/recharts for those instead
|
|
153
|
+
- Not supported on Slack \u2014 never emit this block type when the channel is Slack
|
|
154
|
+
|
|
155
|
+
Commentaire de charts:
|
|
156
|
+
- Apr\xE8s chaque chart produit dans un BLOCK markdown, ajoute un commentaire de ce chart dans un bloc markdown suivant.
|
|
157
|
+
- Le commentaire ne doit pas d\xE9passer une phrase de 50 mots qui r\xE9sume l'information produite.
|
|
158
|
+
|
|
159
|
+
MANDATORY: Start your response NOW with a markdown BLOCK explaining what you found in the tool results, then continue with other BLOCKS (charts, tables, stats) as needed to visualize the data.`},{id:"local_extractor",label:"Local Extractor",description:"Extracts, anonymises, and transforms tool result data server-side before the final response. Auto-anonymises IPs, emails, and hostnames using token substitution. entityTokenMap accumulates across the session.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0},defaultInstructions:`You receive raw tool results from MCP queries.
|
|
160
|
+
All sensitive identifiers (IPs, hostnames, emails) have been pre-tokenized (ip_1, host_2, etc.).
|
|
161
|
+
|
|
162
|
+
Your task: extract a compact, structured JSON summary.
|
|
163
|
+
|
|
164
|
+
Output ONLY valid JSON with these fields:
|
|
165
|
+
{
|
|
166
|
+
"events": [{ "type": string, "severity": "low"|"medium"|"high"|"critical", "count": number, "entities": string[], "pattern": string }],
|
|
167
|
+
"anomalies": [{ "description": string, "affected_entities": string[], "recommendation": string }],
|
|
168
|
+
"summary": string
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Do not include raw values. Do not include PII. Return ONLY the JSON object.`},{id:"compare_context",label:"Compare Context",description:"Injects a reference chat for diff comparison between the current and a previous analysis. One-shot: only activates when deps.compareChatId is provided. Auto-inserted between system and assistant_tool_executor.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1}},{id:"enrich_context",label:"Enrich Context",description:"Injects insights from a previous chat produced by different assistants. Filters to include only assistants absent from the current chat. One-shot (deps.enrichChatId).",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1}},{id:"subgraph",label:"Sub-graph",description:"Embeds a complete graph as an atomic node (recursive compilation from .stkxp_graphs). State is shared with the parent graph. HITL interruptions in sub-graphs work normally via the inherited checkpointer.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!0,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"inventory_context",label:"Inventory Context",description:"Fetches the current user's enabled MCP servers, connectors, and tools from Elasticsearch and injects a structured inventory SystemMessage. Place before the system node to give the LLM full resource awareness.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"skills_context",label:"Skills Context",description:"Fetches the user's active skills (graphs), MCP servers, connectors, and tools and injects a combined SystemMessage. Use before the system node in generator graphs so the LLM can reference real skill IDs and tool names.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"relevant_skills_context",label:"Relevant Skills Context",description:"Relevance-ranked retrieval for generator graphs: ranks the user's skills and tools by full-text relevance to the use-case description (last human message) and injects only the top matches, grouped by MCP server. Use in place of skills_context when the user may own many skills/tools and a recency dump would miss the right one (lets the assistant/team generator find the most relevant skill and its associated tools).",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"relevant_assistants_context",label:"Relevant Assistants Context",description:"Relevance-ranked retrieval for the team generator: ranks the user's assistants by full-text relevance to the use-case description (last human message) and injects only the top matches with their id, role, topic and condition hint. Use in place of assistants_context so the team generator links the right specialists even when the user owns thousands of assistants.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"node_types_context",label:"Node Types Context",description:"Injects the live LangGraph node-type catalog (from .stkxp_node_types) plus the valid edge conditions and structural rules as a SystemMessage. Use before the system node in generator graphs (skill / assistant / team) so the LLM designs runtimes against the real, current node-type set and never invents an unsupported type. No per-user scoping \u2014 node types are a global admin-managed registry.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"assistants_context",label:"Assistants Context",description:"Fetches all non-internal assistants owned by the user and injects a structured SystemMessage listing each assistant with its id, name, topic, role, tags, and a pre-computed condition hint. Use before the system node in team generator graphs.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"namespace_context",label:"Namespace Context",description:"Resolves namespace@platform bindings for the assistant's MCP servers by querying the platform registry. Injects a SystemMessage listing available namespaces per tool server so the LLM can fill the `namespace` field in tool inputSchema correctly. Also stores the namespaceMap in state for per-server header injection. Place before the system node.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"human_approval",label:"Human Approval (HITL)",description:"Suspends graph execution and waits for a human decision. Two interrupt types: 'approval' (simple approve/reject dialog with an optional comment) or 'form' (advanced \u2014 scripted form_schema with typed fields: text, select, multiselect, date, etc.). Optional LLM gate to decide dynamically whether to interrupt. Form mode supports tool_prefill rules, terminal_errors, and form_schema.writeToState to patch submitted values into graph state channels (e.g. a typed namespace into namespaceMap). Use after namespace_context with a namespace_not_resolved conditional edge to ask the user for a namespace when Fleet resolves none.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0,interrupt_type:!0,interrupt_message:!0,interrupt_payload_fields:!0,always_interrupt:!0,form_schema:!0},hitlDefaults:{interrupt_type:"approval",interrupt_message:"Valider cette action ?",interrupt_payload_fields:["topic"]}},{id:"team_pipeline",label:"Team Pipeline",description:"Runs the team's assistants sequentially. After each assistant, its final AIMessage is injected as a SystemMessage context for the next one, so each specialist builds on the previous findings. The assistants list comes from the Team definition \u2014 no configuration needed here. Optional instructions override the context injection template.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!0,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"team_parallel",label:"Team Parallel",description:"Runs all team assistants in parallel (Promise.allSettled). Collects every assistant's output into state.teamAssistantResults. Use before a team_decider node for fan-out/aggregate patterns. Failed assistants are recorded as failed without stopping the others.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"team_router",label:"Team Router",description:"Selects, via one LLM call, the MINIMAL subset of the team's assistants actually needed to answer the question \u2014 avoids waking every assistant when only one or two are relevant (cost + latency). Place BEFORE team_pipeline / team_parallel, which then run only the selected assistants. Forces a structured `select_assistants` tool call and never picks zero; robust fallback runs all assistants on any LLM error. Writes state.selectedAssistantIds + state.teamRouterDecision. Coordination node: requires the team's assistants (injected by the WS handler when team.graphId is set).",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"team_finder",label:"Team Finder",description:"Picks the single best-matching OTHER team for the user's question via hybrid (semantic + lexical) search over every team's name/description, then writes state.selectedTeamId for a downstream team_invoker to launch. Place BEFORE a team_invoker node with no static `teamId` configured \u2014 team_invoker prefers its own static teamId when set, and only falls back to state.selectedTeamId otherwise. Never selects the current team itself. Config fields (edit via the raw JSON node editor): `topK` (default 5, candidates considered), `minScore` (optional RRF score floor below which it falls back instead of guessing), `fallbackMessage` (optional, shown when nothing relevant is found). Coordination node: requires deps.username (owner-scoping the search) and deps.teamId (excluded from candidates).",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,teamId:!1,instructions:!0,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"suggestion_generator",label:"Suggestion Generator",description:"Place after assistant_response_generator (or at the end of a team coordination graph). Proposes 1-3 clickable follow-up suggestions for the chat UI, using the last answer plus (a) an RRF search over the user's other teams (same mechanism as team_finder) and (b) this team's other assistants, if any. Never appears in the conversation transcript \u2014 emitted as a UI-only event. No-ops on channels other than web/embed (Slack, webhooks, MCP, A2A). Requires an LLM routing rule.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,teamId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"team_decider",label:"Team Decider",description:"Synthesises all assistant outputs collected in state.teamAssistantResults into a single final response. Makes one LLM call with all previous outputs as context. Use after team_parallel. Requires an LLM routing rule. Optional instructions guide the synthesis style.",builtIn:!0,applicableProperties:{llm_routing_rule_id:!0,graphId:!1,instructions:!0,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}},{id:"team_invoker",label:"Team Invoker",description:"Launches ANOTHER team (selected via the `teamId` field) with ITS OWN assistants, in parallel, and collects their outputs into state.teamAssistantResults. Unlike team_pipeline / team_parallel \u2014 which coordinate the CURRENT team's assistants \u2014 this node re-resolves the target team's assistants by id. A provenance SystemMessage naming the launching team / graph / assistant is injected into each invoked assistant. Place before a team_decider to synthesise. Refuses direct self-invocation (target == current team).",builtIn:!0,applicableProperties:{llm_routing_rule_id:!1,graphId:!1,teamId:!0,instructions:!0,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1}}];async function b(){try{await a.indices.exists({index:r})||(await a.indices.create({index:r,body:{mappings:{properties:{id:{type:"keyword"},label:{type:"text",fields:{keyword:{type:"keyword"}}},description:{type:"text"},builtIn:{type:"boolean"},applicableProperties:{properties:{llm_routing_rule_id:{type:"boolean"},graphId:{type:"boolean"},teamId:{type:"boolean"},instructions:{type:"boolean"},interrupt_type:{type:"boolean"},interrupt_message:{type:"boolean"},interrupt_payload_fields:{type:"boolean"},always_interrupt:{type:"boolean"},form_schema:{type:"boolean"}}},hitlDefaults:{properties:{interrupt_type:{type:"keyword"},interrupt_message:{type:"text"},interrupt_payload_fields:{type:"keyword"}}},defaultInstructions:{type:"text"},createdAt:{type:"date"},updatedAt:{type:"date"}}}}}),console.log("[NodeTypes] Index created")),await v()}catch(s){console.error("[NodeTypes] Failed to ensure index:",s.message)}}async function v(){try{const s=new Date().toISOString(),t=[];for(const e of c)t.push({update:{_index:r,_id:e.id}}),t.push({doc:{...e,updatedAt:s},upsert:{...e,createdAt:s,updatedAt:s}});await a.bulk({body:t,refresh:!0}),console.log(`[NodeTypes] Upserted ${c.length} built-in node types`)}catch(s){console.error("[NodeTypes] Failed to seed defaults:",s.message)}}b().catch(console.error);async function I(s,t){try{const o=(await a.search({index:r,body:{query:{match_all:{}},size:100,sort:[{label:"asc"}]}})).hits.hits.map(i=>({...i._source,_esId:i._id}));t.json({nodeTypes:o})}catch(e){console.error("[NodeTypes] List error:",e),t.status(500).json({error:e.message})}}async function x(s,t){try{const{id:e,label:o,description:i,applicableProperties:p,hitlDefaults:n,defaultInstructions:l}=s.body;if(!e||!o){t.status(400).json({error:"id and label are required"});return}if(await a.exists({index:r,id:e})){t.status(409).json({error:`Node type '${e}' already exists`});return}const u=new Date().toISOString(),d={id:e,label:o,description:i||"",builtIn:!1,applicableProperties:p||{llm_routing_rule_id:!1,graphId:!1,instructions:!1,interrupt_type:!1,interrupt_message:!1,interrupt_payload_fields:!1},...n&&{hitlDefaults:n},...l!==void 0&&{defaultInstructions:l},createdAt:u,updatedAt:u};await a.index({index:r,id:e,body:d,refresh:!0}),t.status(201).json({nodeType:d})}catch(e){console.error("[NodeTypes] Create error:",e),t.status(500).json({error:e.message})}}async function w(s,t){try{const{id:e}=s.params,{label:o,description:i,applicableProperties:p,hitlDefaults:n,defaultInstructions:l}=s.body;if(!await a.get({index:r,id:e}).catch(()=>null)){t.status(404).json({error:`Node type '${e}' not found`});return}const u=new Date().toISOString(),d={...o!==void 0&&{label:o},...i!==void 0&&{description:i},...p!==void 0&&{applicableProperties:p},...n!==void 0&&{hitlDefaults:n},...l!==void 0&&{defaultInstructions:l},updatedAt:u};await a.update({index:r,id:e,body:{doc:d},refresh:!0});const h=await a.get({index:r,id:e});t.json({nodeType:h._source})}catch(e){console.error("[NodeTypes] Update error:",e),t.status(500).json({error:e.message})}}async function T(s,t){try{const{id:e}=s.params;if(!await a.get({index:r,id:e}).catch(()=>null)){t.status(404).json({error:`Node type '${e}' not found`});return}await a.delete({index:r,id:e,refresh:!0}),t.json({success:!0})}catch(e){console.error("[NodeTypes] Delete error:",e),t.status(500).json({error:e.message})}}const C=[{path:"/api/node-types",method:"get",handler:I},{path:"/api/node-types",method:"post",handler:x},{path:"/api/node-types/:id",method:"put",handler:w},{path:"/api/node-types/:id",method:"delete",handler:T}];export{c as DEFAULT_NODE_TYPES,C as routes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import d from"lodash";import p from"zod";import{ElasticsearchWrapper as u}from"../core/services/elasticsearch-wrapper";import{config as g}from"../core/config";import{filterQueryNodes as b,filterResult as j,parseNowExpression as n}from"../utils";const h={params:p.object({namespace:p.string().optional()}),body:p.object({query:p.object({clusters:p.object({value:p.string().optional()}).optional(),nodes:p.object({value:p.string().optional()}).optional(),roles:p.object({value:p.string().optional()}).optional(),start:p.object({value:p.string().optional()}).optional(),end:p.object({value:p.string().optional()}).optional()}).optional()})},J=[{method:"post",path:"/api/stack_expert/clusters/nodes/stats",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),m="logs-stack_expert.nodes_stats-*",l="stkxp.nodes.name",o=1;let t=null;const r=b(e.body.query);var s=n(e.body.query.start.value),_=n(e.body.query.end.value);r.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const c={query:{bool:{filter:r}},size:0,aggs:{by_term:{terms:{field:l,size:1e3},aggs:{top_doc:{top_hits:{_source:["data_stream.namespace","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:o}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:"10"},aggs:{st_fs_total_in_bytes:{stats:{field:"stkxp.nodes.fs.data.total_in_bytes"}},st_indices_query_total:{stats:{field:"stkxp.nodes.indices.search.query_total"}},st_indices_index_total:{stats:{field:"stkxp.nodes.indices.indexing.index_total"}},st_indices_doc_count:{stats:{field:"stkxp.nodes.indices.docs.count"}},st_heap_percent:{stats:{field:"stkxp.nodes.jvm.mem.heap_used_percent"}},st_cpu_percent:{stats:{field:"stkxp.nodes.os.cpu.percent"}},tm_fs_total_in_bytes:{top_metrics:{metrics:{field:"stkxp.nodes.fs.data.total_in_bytes"},sort:{"@timestamp":"desc"}}},tm_heap_percent:{top_metrics:{metrics:{field:"stkxp.nodes.jvm.mem.heap_used_percent"},sort:{"@timestamp":"desc"}}},tm_indices_doc_count:{top_metrics:{metrics:{field:"stkxp.nodes.indices.docs.count"},sort:{"@timestamp":"desc"}}},tm_indices_query_total:{top_metrics:{metrics:{field:"stkxp.nodes.indices.search.query_total"},sort:{"@timestamp":"desc"}}},tm_indices_index_total:{top_metrics:{metrics:{field:"stkxp.nodes.indices.indexing.index_total"},sort:{"@timestamp":"desc"}}},tm_percent:{top_metrics:{metrics:{field:"stkxp.nodes.os.cpu.percent"},sort:{"@timestamp":"desc"}}}}}}}}};return console.log(JSON.stringify(c)),t=await a.search({index:m,...c}),i.json({body:{time:new Date().toISOString(),result:j(t?.aggregations?.by_term?.buckets)||[]}})}catch(c){console.log(c)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/query",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),m="logs-stack_expert.nodes_stats-*";let l="";const o=b(e.body.query);var t=n(e.body.query.start.value),r=n(e.body.query.end.value);o.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const s={query:{bool:{filter:o}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:r.diff(t,"minutes")/10},aggs:{query_total:{max:{field:"stkxp.nodes.indices.search.query_total"}},query_time_in_millis:{max:{field:"stkxp.nodes.indices.search.query_time_in_millis"}},eps:{derivative:{buckets_path:"query_total"}},time:{derivative:{buckets_path:"query_time_in_millis"}},latency:{bucket_script:{buckets_path:{total_time:"time",total_search:"eps"},script:"params.total_time / params.total_search"}}}}}}}};return console.log(JSON.stringify(s)),l=await a.search({index:m,...s}),i.json({body:{time:new Date().toISOString(),result:j(l?.aggregations?.by_nodename?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/nodes/indexing",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m}=e.params,l="logs-stack_expert.nodes_stats-*",o=b(e.body.query);var t=n(e.body.query.start.value),r=n(e.body.query.end.value);o.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const s={query:{bool:{filter:o}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:r.diff(t,"minutes")/10},aggs:{index_total:{max:{field:"stkxp.nodes.indices.indexing.index_total"}},index_time_in_millis:{max:{field:"stkxp.nodes.indices.indexing.index_time_in_millis"}},eps:{derivative:{buckets_path:"index_total"}},time:{derivative:{buckets_path:"index_time_in_millis"}},latency:{bucket_script:{buckets_path:{total_time:"time",total_search:"eps"},script:"params.total_time / params.total_search"}}}}}}}};console.log(JSON.stringify(s));const _=await a.search({index:l,...s});return i.json({body:{time:new Date().toISOString(),result:j(_?.aggregations?.by_nodename?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/nodes/inventory",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m,name:l}=e.params,o="logs-stack_expert.nodes_info-*",t=b(e.body.query);var r=n(e.body.query.start.value),s=n(e.body.query.end.value);t.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const _={query:{bool:{filter:t}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3}}}};console.log(JSON.stringify(_));const c=await a.search({index:o,..._});return console.log(JSON.stringify(c)),i.json({body:{time:new Date().toISOString(),result:c?.aggregations?.by_nodename?.buckets||[]}})}catch(_){console.log("nodes error",_)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/stats_info",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),m=b(e.body.query);var l=n(e.body.query.start.value),o=n(e.body.query.end.value);m.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const t={query:{bool:{filter:m}},size:0,aggs:{by_nodes_attributes_server_name:{terms:{field:"stkxp.nodes.attributes.server_name",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:o.diff(l,"minutes")/10},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}}}},s=await a.search({index:"logs-stack_expert.nodes_info-*",...t}),c=await a.search({index:"logs-stack_expert.nodes_stats-*",...t}),z=d.merge({},s,c);return i.json({body:{time:new Date().toISOString(),result:z?.aggregations?.by_nodes_attributes_server_name?.buckets||[]}})}catch(t){console.log("nodes error",t)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/jvm",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m,name:l}=e.params,o="logs-stack_expert.nodes_stats-*",t=b(e.body.query);var r=n(e.body.query.start.value),s=n(e.body.query.end.value);t.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const _={query:{bool:{filter:t}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:s.diff(r,"minutes")/10},aggs:{free_in_bytes:{max:{field:"stkxp.nodes.os.mem.free_in_bytes"}},heap_used_in_bytes:{max:{field:"stkxp.nodes.jvm.mem.heap_used_in_bytes"}}}}}}}};try{const c=await a.search({index:o,..._});return i.json({body:{time:new Date().toISOString(),result:j(c?.aggregations?.by_nodename?.buckets)||[]}})}catch(c){console.log("nodes error",c)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/cpu",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m,name:l}=e.params,o="logs-stack_expert.nodes_stats-*",t=b(e.body.query);var r=n(e.body.query.start.value),s=n(e.body.query.end.value);t.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const _={query:{bool:{filter:t}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:s.diff(r,"minutes")/10},aggs:{load_average:{max:{field:"stkxp.nodes.os.cpu.load_average.15m"}},percent:{max:{field:"stkxp.nodes.os.cpu.percent"}}}}}}}};try{const c=await a.search({index:o,..._});return i.json({body:{time:new Date().toISOString(),result:j(c?.aggregations?.by_nodename?.buckets)||[]}})}catch(c){console.log("nodes error",c)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/shards",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m,name:l}=e.params,o="logs-stack_expert.cat_shards-*";var t=n(e.body.query.start.value),r=n(e.body.query.end.value);try{const _={query:{bool:{filter:b(e.body.query)}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}},z=await a.search({index:"logs-stack_expert.nodes_info-*",..._}),v={query:{bool:{filter:[{term:{"data_stream.namespace":e.body.query.clusters.value}},{range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}}]}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.node",size:1e3},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:r.diff(t,"minutes")/10},aggs:{prirep:{terms:{field:"stkxp.prirep"}},store:{sum:{field:"stkxp.store"}}}}}}}},k=await a.search({index:o,...v}),f=d.keyBy(z.aggregations.by_nodename.buckets,"key"),x=d.keyBy(k.aggregations.by_nodename.buckets,"key"),y=d.intersection(d.keys(f),d.keys(x));console.log("commonKeys",y);const O=y.map(w=>({key:w,...d.merge({},f[w],x[w])}));return console.log("mergedBuckets",JSON.stringify(O)),i.json({body:{time:new Date().toISOString(),result:O||[]}})}catch(s){console.log("nodes error",s)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/fs",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),m="logs-stack_expert.nodes_stats-*",l=b(e.body.query);var o=n(e.body.query.start.value),t=n(e.body.query.end.value);l.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});try{const r={query:{bool:{filter:l}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:t.diff(o,"minutes")/10},aggs:{data_free:{max:{field:"stkxp.nodes.fs.data.free_in_bytes"}},data_total:{max:{field:"stkxp.nodes.fs.data.total_in_bytes"}},total_free:{max:{field:"stkxp.nodes.fs.total.available_in_bytes"}},total_total:{max:{field:"stkxp.nodes.fs.total.total_in_bytes"}},data_high_watermark:{max:{field:"stkxp.nodes.fs.data.high_watermark_free_space_in_bytes"}},data_low_watermark:{max:{field:"stkxp.nodes.fs.data.low_watermark_free_space_in_bytes"}}}}}}}};console.log(JSON.stringify(r));const s=await a.search({index:m,...r});return i.json({body:{time:new Date().toISOString(),result:j(s?.aggregations?.by_nodename?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/nodes/thread_pool",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0);var m=n(e.body.query.start.value),l=n(e.body.query.end.value);try{const t={query:{bool:{filter:b(e.body.query)}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}},s=await a.search({index:"logs-stack_expert.nodes_info-*",...t}),c={query:{bool:{filter:[{term:{"data_stream.namespace":e.body.query.clusters.value}},{range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}}]}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.node_name",size:1e3},aggs:{by_name:{terms:{field:"stkxp.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(m,"minutes")/10},aggs:{completed:{max:{field:"stkxp.completed"}},rejected:{max:{field:"stkxp.rejected"}}}}}}}}}},S=await a.search({index:"logs-stack_expert.cat_thread_pool-*",...c}),v=d.keyBy(s.aggregations.by_nodename.buckets,"key"),k=d.keyBy(S.aggregations.by_nodename.buckets,"key"),f=d.intersection(d.keys(v),d.keys(k));console.log("commonKeys",f);const x=f.map(y=>({key:y,...d.merge({},v[y],k[y])}));return i.json({body:{time:new Date().toISOString(),result:x||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/nodes/hot_threads",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0),{namespace:m,name:l}=e.params;var o=n(e.body.query.start.value),t=n(e.body.query.end.value);try{const r=b(e.body.query);r.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const s={query:{bool:{filter:r}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}},c=await a.search({index:"logs-stack_expert.nodes_info-*",...s}),S={query:{bool:{filter:[{term:{"data_stream.namespace":e.body.query.clusters.value}},{range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}}]}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.node_name",size:1e3},aggs:{by_type:{terms:{field:"stkxp.thread_type",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:t.diff(o,"minutes")/10},aggs:{cpu_total_usage:{max:{field:"stkxp.cpu_total_usage"}}}}}}}}}},k=await a.search({index:"logs-stack_expert.nodes_hot_threads-*",...S}),f=d.keyBy(c.aggregations.by_nodename.buckets,"key"),x=d.keyBy(k.aggregations.by_nodename.buckets,"key"),y=d.intersection(d.keys(f),d.keys(x));console.log("commonKeys",y);const O=y.map(w=>({key:w,...d.merge({},f[w],x[w])}));return i.json({body:{time:new Date().toISOString(),result:O||[]}})}catch(r){console.log("nodes error",r)}}},{method:"post",path:"/api/stack_expert/clusters/nodes/tasks",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0);var m=n(e.body.query.start.value),l=n(e.body.query.end.value);try{const t={query:{bool:{filter:b(e.body.query)}},size:0,aggs:{by_nodeid:{terms:{field:"stkxp.nodes.node_id",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}},s=await a.search({index:"logs-stack_expert.nodes_info-*",...t}),c={query:{bool:{filter:[{term:{"data_stream.namespace":e.body.query.clusters.value}},{range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}}]}},size:0,aggs:{by_nodeid:{terms:{field:"stkxp.node",size:1e3},aggs:{by_task:{terms:{field:"stkxp.task.action",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(m,"minutes")/10}}}}}}}},S=await a.search({index:"logs-stack_expert.tasks_list-*",...c}),v=d.keyBy(s.aggregations.by_nodeid.buckets,"key"),k=d.keyBy(S.aggregations.by_nodeid.buckets,"key"),f=d.intersection(d.keys(v),d.keys(k));console.log("commonKeys",f,v,k);const x=f.map(y=>({key:y,...d.merge({},v[y],k[y])}));return console.log("mergedBuckets",x),i.json({body:{time:new Date().toISOString(),result:x||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/nodes",validate:h,handler:async(e,i)=>{const a=new u({...g.elasticsearch,auth:{username:e.auth.username,password:e.auth.password}},!0);console.log("clusters/nodes",e.body.query);var m=n(e.body.query.start.value),l=n(e.body.query.end.value);const o=b(e.body.query);o.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const t="logs-stack_expert.nodes_stats-*";let r=null;try{const s={size:0,query:{bool:{filter:o}},aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:200},aggs:{top_doc:{top_hits:{_source:["data_stream.namespace","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}};return console.log(JSON.stringify(s)),r=await a.search({index:t,...s}),i.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_nodename?.buckets||[]}})}catch(s){console.log("nodes error",s)}}}];export{J as routes};
|