@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.
Files changed (371) hide show
  1. package/README.md +273 -0
  2. package/bin/stkxp.mjs +198 -0
  3. package/package.json +97 -0
  4. package/src/bootstrap.mjs +37 -0
  5. package/src/bootstrap.test.mjs +73 -0
  6. package/src/deploy.mjs +145 -0
  7. package/src/deploy.test.mjs +131 -0
  8. package/src/export.mjs +30 -0
  9. package/src/export.test.mjs +56 -0
  10. package/src/secret-input.mjs +64 -0
  11. package/src/secret-input.test.mjs +58 -0
  12. package/src/serve.mjs +83 -0
  13. package/src/serve.test.mjs +93 -0
  14. package/vendor/dist-server/asyncapi/generator.js +1 -0
  15. package/vendor/dist-server/asyncapi/messages.js +1 -0
  16. package/vendor/dist-server/asyncapi/viewer.html +43 -0
  17. package/vendor/dist-server/core/app-config/branding.js +1 -0
  18. package/vendor/dist-server/core/app-config/mantine-theme.js +1 -0
  19. package/vendor/dist-server/core/app-config/mermaid-theme.js +2 -0
  20. package/vendor/dist-server/core/app-config/prompt-optimization.js +1 -0
  21. package/vendor/dist-server/core/app-config/settings.js +1 -0
  22. package/vendor/dist-server/core/config.js +1 -0
  23. package/vendor/dist-server/core/graph/a2a-agent-executor.js +2 -0
  24. package/vendor/dist-server/core/graph/app.js +120 -0
  25. package/vendor/dist-server/core/graph/delegated-agent-adapter.js +1 -0
  26. package/vendor/dist-server/core/graph/graph-builder.js +3 -0
  27. package/vendor/dist-server/core/graph/kibana-agent-executor.js +1 -0
  28. package/vendor/dist-server/core/graph/nodes/context/assistants-context.js +5 -0
  29. package/vendor/dist-server/core/graph/nodes/context/compare-context.js +10 -0
  30. package/vendor/dist-server/core/graph/nodes/context/enrich-context.js +8 -0
  31. package/vendor/dist-server/core/graph/nodes/context/inventory-context.js +4 -0
  32. package/vendor/dist-server/core/graph/nodes/context/namespace-context.js +2 -0
  33. package/vendor/dist-server/core/graph/nodes/context/node-types-context.js +4 -0
  34. package/vendor/dist-server/core/graph/nodes/context/relevant-assistants-context.js +5 -0
  35. package/vendor/dist-server/core/graph/nodes/context/relevant-skills-context.js +5 -0
  36. package/vendor/dist-server/core/graph/nodes/context/skills-context.js +8 -0
  37. package/vendor/dist-server/core/graph/nodes/context/state-setter.js +1 -0
  38. package/vendor/dist-server/core/graph/nodes/control/check-reset.js +1 -0
  39. package/vendor/dist-server/core/graph/nodes/control/topic-detection.js +1 -0
  40. package/vendor/dist-server/core/graph/nodes/governance/human-approval.js +16 -0
  41. package/vendor/dist-server/core/graph/nodes/index.js +1 -0
  42. package/vendor/dist-server/core/graph/nodes/orchestration/suggestion-generator.js +17 -0
  43. package/vendor/dist-server/core/graph/nodes/orchestration/team-decider.js +1 -0
  44. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-error.js +4 -0
  45. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-standalone-runner.js +1 -0
  46. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-synthesis.js +29 -0
  47. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-memory.js +2 -0
  48. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-runner.js +7 -0
  49. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/team-assistant-utils.js +1 -0
  50. package/vendor/dist-server/core/graph/nodes/orchestration/team-finder.js +13 -0
  51. package/vendor/dist-server/core/graph/nodes/orchestration/team-invoker.js +3 -0
  52. package/vendor/dist-server/core/graph/nodes/orchestration/team-parallel.js +1 -0
  53. package/vendor/dist-server/core/graph/nodes/orchestration/team-pipeline.js +6 -0
  54. package/vendor/dist-server/core/graph/nodes/orchestration/team-router.js +26 -0
  55. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/index.js +6 -0
  56. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/resource-bridge.js +2 -0
  57. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/sandbox.js +6 -0
  58. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/tool-bridge.js +1 -0
  59. package/vendor/dist-server/core/graph/nodes/reasoning/local-extractor.js +5 -0
  60. package/vendor/dist-server/core/graph/nodes/reasoning/response-generator.js +47 -0
  61. package/vendor/dist-server/core/graph/nodes/reasoning/static-code-executor/index.js +1 -0
  62. package/vendor/dist-server/core/graph/nodes/reasoning/system.js +54 -0
  63. package/vendor/dist-server/core/graph/nodes/reasoning/tool-executor.js +9 -0
  64. package/vendor/dist-server/core/graph/nodes/tools/raw-output.js +4 -0
  65. package/vendor/dist-server/core/graph/nodes/tools/tool-cleaner.js +7 -0
  66. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/execute-tool-calls.js +3 -0
  67. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/invoke.js +1 -0
  68. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/namespace-guard.js +1 -0
  69. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/result-limits.js +3 -0
  70. package/vendor/dist-server/core/graph/nodes/tools/tool-invoker.js +1 -0
  71. package/vendor/dist-server/core/graph/nodes/tools/tool.js +4 -0
  72. package/vendor/dist-server/core/graph/schemas.js +1 -0
  73. package/vendor/dist-server/core/graph/topic-variants.js +1 -0
  74. package/vendor/dist-server/core/graph/types.js +1 -0
  75. package/vendor/dist-server/core/graph/utils/content-utils.js +17 -0
  76. package/vendor/dist-server/core/graph/utils/edge-jsonata-condition.js +1 -0
  77. package/vendor/dist-server/core/graph/utils/engine-vars.js +1 -0
  78. package/vendor/dist-server/core/graph/utils/logging-utils.js +4 -0
  79. package/vendor/dist-server/core/graph/utils/message-utils.js +5 -0
  80. package/vendor/dist-server/core/graph/utils/node-type-defaults-cache.js +1 -0
  81. package/vendor/dist-server/core/graph/utils/sandbox-catalog.js +1 -0
  82. package/vendor/dist-server/core/graph/utils/schema-utils.js +1 -0
  83. package/vendor/dist-server/core/graph/utils/team-context-policy.js +5 -0
  84. package/vendor/dist-server/core/graph/utils/team-node-resolvers.js +1 -0
  85. package/vendor/dist-server/core/graph/utils/tool-result.js +4 -0
  86. package/vendor/dist-server/core/graph/utils/tool-wrapper.js +14 -0
  87. package/vendor/dist-server/core/llm/init-indices.js +1 -0
  88. package/vendor/dist-server/core/llm/models/index.js +1 -0
  89. package/vendor/dist-server/core/llm/models/model.model.js +1 -0
  90. package/vendor/dist-server/core/llm/models/provider.model.js +1 -0
  91. package/vendor/dist-server/core/llm/models/routing-rule.model.js +1 -0
  92. package/vendor/dist-server/core/llm/providers.js +1 -0
  93. package/vendor/dist-server/core/mcp/client.js +1 -0
  94. package/vendor/dist-server/core/runtime/active-runs-registry.js +1 -0
  95. package/vendor/dist-server/core/runtime/system-prompt-cache.js +1 -0
  96. package/vendor/dist-server/core/runtime/trace-bus.js +1 -0
  97. package/vendor/dist-server/core/schema/cluster_health_report.js +1 -0
  98. package/vendor/dist-server/core/schema/cluster_info.js +1 -0
  99. package/vendor/dist-server/core/schema/cluster_license.js +5 -0
  100. package/vendor/dist-server/core/schema/cluster_stats.js +12 -0
  101. package/vendor/dist-server/core/schema/dashboard.js +1 -0
  102. package/vendor/dist-server/core/schema/indices_settings.js +1 -0
  103. package/vendor/dist-server/core/schema/indices_shards.js +1 -0
  104. package/vendor/dist-server/core/schema/indices_stats.js +1 -0
  105. package/vendor/dist-server/core/schema/nodes_info.js +2 -0
  106. package/vendor/dist-server/core/schema/nodes_stats.js +11 -0
  107. package/vendor/dist-server/core/schema/pricing.js +1 -0
  108. package/vendor/dist-server/core/schema/ui.js +1 -0
  109. package/vendor/dist-server/core/schema/zod_client.js +83 -0
  110. package/vendor/dist-server/core/services/a2a-client.js +2 -0
  111. package/vendor/dist-server/core/services/alerts-service.js +1 -0
  112. package/vendor/dist-server/core/services/api-keys-service.js +1 -0
  113. package/vendor/dist-server/core/services/assistant-resolver.js +1 -0
  114. package/vendor/dist-server/core/services/assistants-index-service.js +1 -0
  115. package/vendor/dist-server/core/services/async-conversations-service.js +22 -0
  116. package/vendor/dist-server/core/services/asyncapi-dispatch.js +1 -0
  117. package/vendor/dist-server/core/services/asyncapi-drivers/driver-manager.js +1 -0
  118. package/vendor/dist-server/core/services/asyncapi-drivers/driver-types.js +0 -0
  119. package/vendor/dist-server/core/services/asyncapi-drivers/http-driver.js +1 -0
  120. package/vendor/dist-server/core/services/asyncapi-drivers/nats-driver.js +1 -0
  121. package/vendor/dist-server/core/services/asyncapi-drivers/websocket-driver.js +1 -0
  122. package/vendor/dist-server/core/services/asyncapi-events-service.js +1 -0
  123. package/vendor/dist-server/core/services/asyncapi-reply-service.js +1 -0
  124. package/vendor/dist-server/core/services/asyncapi-sink.js +1 -0
  125. package/vendor/dist-server/core/services/asyncapi-tools-service.js +1 -0
  126. package/vendor/dist-server/core/services/block-repair-service.js +13 -0
  127. package/vendor/dist-server/core/services/chart-renderer.js +1 -0
  128. package/vendor/dist-server/core/services/chat-llms-service.js +1 -0
  129. package/vendor/dist-server/core/services/chat-runs-service.js +1 -0
  130. package/vendor/dist-server/core/services/chat-tools-service.js +1 -0
  131. package/vendor/dist-server/core/services/chat-traces-service.js +1 -0
  132. package/vendor/dist-server/core/services/chats-service.js +5 -0
  133. package/vendor/dist-server/core/services/comparison-service.js +107 -0
  134. package/vendor/dist-server/core/services/default-model.service.js +1 -0
  135. package/vendor/dist-server/core/services/elastic-stack-sync-service.js +2 -0
  136. package/vendor/dist-server/core/services/elasticsearch-wrapper.js +1 -0
  137. package/vendor/dist-server/core/services/embed-rate-limiter.js +1 -0
  138. package/vendor/dist-server/core/services/error-analytics-service.js +1 -0
  139. package/vendor/dist-server/core/services/es-field-resolver.js +1 -0
  140. package/vendor/dist-server/core/services/gateway-platform-types.js +1 -0
  141. package/vendor/dist-server/core/services/gliner-tagging-service.js +1 -0
  142. package/vendor/dist-server/core/services/golden-questions-scoring.js +1 -0
  143. package/vendor/dist-server/core/services/golden-questions-service.js +1 -0
  144. package/vendor/dist-server/core/services/graph-drilldown-service.js +1 -0
  145. package/vendor/dist-server/core/services/graph-registry-service.js +1 -0
  146. package/vendor/dist-server/core/services/hitl-analytics-service.js +1 -0
  147. package/vendor/dist-server/core/services/ingestion-jobs-service.js +1 -0
  148. package/vendor/dist-server/core/services/ingestion-service.js +5 -0
  149. package/vendor/dist-server/core/services/kibana-client-factory.js +1 -0
  150. package/vendor/dist-server/core/services/kibana-client.js +2 -0
  151. package/vendor/dist-server/core/services/kibana-dashboard-extractor.js +1 -0
  152. package/vendor/dist-server/core/services/kibana-service.js +1 -0
  153. package/vendor/dist-server/core/services/llm-analytics-service.js +1 -0
  154. package/vendor/dist-server/core/services/llm-client-resolver.js +1 -0
  155. package/vendor/dist-server/core/services/llm-models-service.js +1 -0
  156. package/vendor/dist-server/core/services/llm-pricing-pure.js +1 -0
  157. package/vendor/dist-server/core/services/llm-pricing-service.js +1 -0
  158. package/vendor/dist-server/core/services/llm-providers-service.js +1 -0
  159. package/vendor/dist-server/core/services/llm-routing-service.js +1 -0
  160. package/vendor/dist-server/core/services/llm-services.js +1 -0
  161. package/vendor/dist-server/core/services/llm-sync-service.js +1 -0
  162. package/vendor/dist-server/core/services/manifest-loader.js +1 -0
  163. package/vendor/dist-server/core/services/mcp-servers-service.js +1 -0
  164. package/vendor/dist-server/core/services/mcp-sync-service.js +1 -0
  165. package/vendor/dist-server/core/services/mcp-tools-service.js +1 -0
  166. package/vendor/dist-server/core/services/memories-service.js +2 -0
  167. package/vendor/dist-server/core/services/memory-analytics-service.js +1 -0
  168. package/vendor/dist-server/core/services/monitoring-analytics-service.js +1 -0
  169. package/vendor/dist-server/core/services/monitoring-service.js +1 -0
  170. package/vendor/dist-server/core/services/multi-kibana-service.js +1 -0
  171. package/vendor/dist-server/core/services/node-latency-service.js +1 -0
  172. package/vendor/dist-server/core/services/package-policy-service.js +2 -0
  173. package/vendor/dist-server/core/services/pdf-service.js +572 -0
  174. package/vendor/dist-server/core/services/plan-service.js +1 -0
  175. package/vendor/dist-server/core/services/platform-direction.js +1 -0
  176. package/vendor/dist-server/core/services/platform-secrets.js +1 -0
  177. package/vendor/dist-server/core/services/platforms-service.js +1 -0
  178. package/vendor/dist-server/core/services/policy-factory.js +1 -0
  179. package/vendor/dist-server/core/services/prompt-cache-service.js +1 -0
  180. package/vendor/dist-server/core/services/provider-rate-limits.js +1 -0
  181. package/vendor/dist-server/core/services/quota-service.js +1 -0
  182. package/vendor/dist-server/core/services/report-assets-service.js +1 -0
  183. package/vendor/dist-server/core/services/run-analytics-collector.js +1 -0
  184. package/vendor/dist-server/core/services/run-stream-sink.js +1 -0
  185. package/vendor/dist-server/core/services/send_mail.js +460 -0
  186. package/vendor/dist-server/core/services/share-service.js +1 -0
  187. package/vendor/dist-server/core/services/slack-signature.js +1 -0
  188. package/vendor/dist-server/core/services/sources-service.js +6 -0
  189. package/vendor/dist-server/core/services/team-mcp-result.js +4 -0
  190. package/vendor/dist-server/core/services/team-mcp-run-tokens.js +1 -0
  191. package/vendor/dist-server/core/services/team-mcp-runner.js +1 -0
  192. package/vendor/dist-server/core/services/team-mcp-server.js +1 -0
  193. package/vendor/dist-server/core/services/team-mcp-tools.js +1 -0
  194. package/vendor/dist-server/core/services/team-mcp-ui.js +551 -0
  195. package/vendor/dist-server/core/services/team-run-progress.js +1 -0
  196. package/vendor/dist-server/core/services/team-runner-headless.js +7 -0
  197. package/vendor/dist-server/core/services/team-search-service.js +1 -0
  198. package/vendor/dist-server/core/services/teams-service.js +1 -0
  199. package/vendor/dist-server/core/services/token-projection-service.js +1 -0
  200. package/vendor/dist-server/core/services/tool-analytics-service.js +1 -0
  201. package/vendor/dist-server/core/services/tool-history-service.js +1 -0
  202. package/vendor/dist-server/core/services/tool-metrics-service.js +1 -0
  203. package/vendor/dist-server/core/services/tool-scoring.js +1 -0
  204. package/vendor/dist-server/core/services/toolbox-import-mappers.js +1 -0
  205. package/vendor/dist-server/core/services/toolbox-import-service.js +1 -0
  206. package/vendor/dist-server/core/services/trigger-service.js +1 -0
  207. package/vendor/dist-server/core/services/version-snapshot-service.js +1 -0
  208. package/vendor/dist-server/core/services/webhook-calls-service.js +1 -0
  209. package/vendor/dist-server/core/tools/compare-chat.js +46 -0
  210. package/vendor/dist-server/core/tools/enrich-chat.js +50 -0
  211. package/vendor/dist-server/core/tools/variable-encoding.js +1 -0
  212. package/vendor/dist-server/core/types/settings.js +1 -0
  213. package/vendor/dist-server/core/utils/ab-evaluator.js +5 -0
  214. package/vendor/dist-server/core/utils/condition-evaluator.js +1 -0
  215. package/vendor/dist-server/core/utils/http-client.js +1 -0
  216. package/vendor/dist-server/core/utils/json-schema-to-form.js +1 -0
  217. package/vendor/dist-server/core/utils/logger.js +3 -0
  218. package/vendor/dist-server/core/utils/owner-scope.js +1 -0
  219. package/vendor/dist-server/core/utils/ownership.js +1 -0
  220. package/vendor/dist-server/core/utils/parallel-tool-executor.js +1 -0
  221. package/vendor/dist-server/core/utils/performance-tracker.js +3 -0
  222. package/vendor/dist-server/core/utils/prompt-optimizer.js +40 -0
  223. package/vendor/dist-server/core/utils/response-cache.js +1 -0
  224. package/vendor/dist-server/core/utils/schema-validator.js +1 -0
  225. package/vendor/dist-server/core/utils/streaming-optimizer.js +2 -0
  226. package/vendor/dist-server/core/utils/string-helpers.js +1 -0
  227. package/vendor/dist-server/core/utils/test-responses.js +9 -0
  228. package/vendor/dist-server/core/utils/text-utils.js +1 -0
  229. package/vendor/dist-server/core/utils/tool-form-trigger.js +1 -0
  230. package/vendor/dist-server/entrypoints/cli-bootstrap-run.js +1 -0
  231. package/vendor/dist-server/entrypoints/cli-deploy-run.js +1 -0
  232. package/vendor/dist-server/entrypoints/cli-deploy.js +1 -0
  233. package/vendor/dist-server/entrypoints/team-runner.js +1 -0
  234. package/vendor/dist-server/generated/toolbox-catalog.js +1 -0
  235. package/vendor/dist-server/index.js +2 -0
  236. package/vendor/dist-server/middleware/integration-logos.js +1 -0
  237. package/vendor/dist-server/middleware/plan-guard.js +1 -0
  238. package/vendor/dist-server/openapi/generator.js +1 -0
  239. package/vendor/dist-server/openapi/html-tool-spec.js +1 -0
  240. package/vendor/dist-server/routes/a2a-routes.js +1 -0
  241. package/vendor/dist-server/routes/a2a-server-routes.js +5 -0
  242. package/vendor/dist-server/routes/admin.js +1 -0
  243. package/vendor/dist-server/routes/assistants-routes.js +1 -0
  244. package/vendor/dist-server/routes/asyncapi-routes.js +1 -0
  245. package/vendor/dist-server/routes/auth.js +1 -0
  246. package/vendor/dist-server/routes/billing-routes.js +1 -0
  247. package/vendor/dist-server/routes/chat-llms.js +1 -0
  248. package/vendor/dist-server/routes/chat-tools.js +1 -0
  249. package/vendor/dist-server/routes/chat-traces.js +1 -0
  250. package/vendor/dist-server/routes/chats.js +1 -0
  251. package/vendor/dist-server/routes/clusters.js +1 -0
  252. package/vendor/dist-server/routes/compare.js +43 -0
  253. package/vendor/dist-server/routes/connectors-routes.js +1 -0
  254. package/vendor/dist-server/routes/consumptions-routes.js +1 -0
  255. package/vendor/dist-server/routes/data-admin-routes.js +1 -0
  256. package/vendor/dist-server/routes/data-transfer-routes.js +1 -0
  257. package/vendor/dist-server/routes/elastic-tool-execution-routes.js +1 -0
  258. package/vendor/dist-server/routes/geo-proxy-routes.js +1 -0
  259. package/vendor/dist-server/routes/golden-questions-routes.js +1 -0
  260. package/vendor/dist-server/routes/graph-registry.js +1 -0
  261. package/vendor/dist-server/routes/graph-templates-routes.js +1 -0
  262. package/vendor/dist-server/routes/helpdesk-routes.js +35 -0
  263. package/vendor/dist-server/routes/html-routes.js +1 -0
  264. package/vendor/dist-server/routes/index.js +1 -0
  265. package/vendor/dist-server/routes/indices.js +1 -0
  266. package/vendor/dist-server/routes/integrations-routes.js +1 -0
  267. package/vendor/dist-server/routes/langgraph.js +7 -0
  268. package/vendor/dist-server/routes/live-resources-routes.js +1 -0
  269. package/vendor/dist-server/routes/llm-analytics.js +1 -0
  270. package/vendor/dist-server/routes/llm-control-plane.js +1 -0
  271. package/vendor/dist-server/routes/llm.js +1 -0
  272. package/vendor/dist-server/routes/mcp-gateway-routes.js +1 -0
  273. package/vendor/dist-server/routes/mcp-query-routes.js +1 -0
  274. package/vendor/dist-server/routes/mcp-servers-routes.js +1 -0
  275. package/vendor/dist-server/routes/mcp-team-routes.js +1 -0
  276. package/vendor/dist-server/routes/mcp-tools-routes.js +1 -0
  277. package/vendor/dist-server/routes/me-routes.js +63 -0
  278. package/vendor/dist-server/routes/memories-routes.js +1 -0
  279. package/vendor/dist-server/routes/monitoring-analytics.js +12 -0
  280. package/vendor/dist-server/routes/monitoring.js +1 -0
  281. package/vendor/dist-server/routes/node-types-routes.js +171 -0
  282. package/vendor/dist-server/routes/nodes.js +1 -0
  283. package/vendor/dist-server/routes/packages.js +1 -0
  284. package/vendor/dist-server/routes/pdf.js +3 -0
  285. package/vendor/dist-server/routes/plan-routes.js +1 -0
  286. package/vendor/dist-server/routes/platform-requests-routes.js +1 -0
  287. package/vendor/dist-server/routes/platforms-routes.js +1 -0
  288. package/vendor/dist-server/routes/prompts-routes.js +1 -0
  289. package/vendor/dist-server/routes/proxy-logos.js +1 -0
  290. package/vendor/dist-server/routes/quality-routes.js +1 -0
  291. package/vendor/dist-server/routes/resources-ingest-routes.js +1 -0
  292. package/vendor/dist-server/routes/resources-routes.js +1 -0
  293. package/vendor/dist-server/routes/schema-routes.js +1 -0
  294. package/vendor/dist-server/routes/settings-original.js +1 -0
  295. package/vendor/dist-server/routes/settings.js +1 -0
  296. package/vendor/dist-server/routes/share-routes.js +1 -0
  297. package/vendor/dist-server/routes/sources-catalog-routes.js +1 -0
  298. package/vendor/dist-server/routes/sources-routes.js +1 -0
  299. package/vendor/dist-server/routes/team-optimizer-routes.js +1 -0
  300. package/vendor/dist-server/routes/team-schedule-routes.js +1 -0
  301. package/vendor/dist-server/routes/teams-routes.js +1 -0
  302. package/vendor/dist-server/routes/tool-analytics.js +1 -0
  303. package/vendor/dist-server/routes/tool-history-routes.js +1 -0
  304. package/vendor/dist-server/routes/tool-metrics-routes.js +1 -0
  305. package/vendor/dist-server/routes/toolbox-import-routes.js +1 -0
  306. package/vendor/dist-server/routes/tools-routes.js +5 -0
  307. package/vendor/dist-server/routes/transcribe-routes.js +1 -0
  308. package/vendor/dist-server/routes/triggers.js +1 -0
  309. package/vendor/dist-server/routes/versions-routes.js +1 -0
  310. package/vendor/dist-server/routes/webhooks-routes.js +1 -0
  311. package/vendor/dist-server/scripts/add-label-to-tools.js +4 -0
  312. package/vendor/dist-server/scripts/migrate-assistants-to-mcp.js +10 -0
  313. package/vendor/dist-server/scripts/migrate-chat-runs.js +8 -0
  314. package/vendor/dist-server/scripts/migrate-mcp-protocol.js +8 -0
  315. package/vendor/dist-server/scripts/migrate-mcp-to-platforms.js +15 -0
  316. package/vendor/dist-server/services/alert-evaluator.js +3 -0
  317. package/vendor/dist-server/services/auth.js +1 -0
  318. package/vendor/dist-server/services/billing-service.js +1 -0
  319. package/vendor/dist-server/services/chat-title-generator.js +12 -0
  320. package/vendor/dist-server/services/clone/clone-executor.js +1 -0
  321. package/vendor/dist-server/services/clone/closure-resolver.js +3 -0
  322. package/vendor/dist-server/services/clone/entity-graph.js +3 -0
  323. package/vendor/dist-server/services/clone/team-bundle-bootstrap.js +1 -0
  324. package/vendor/dist-server/services/clone/team-bundle-crypto.js +1 -0
  325. package/vendor/dist-server/services/clone/team-bundle-encrypted.js +1 -0
  326. package/vendor/dist-server/services/clone/team-bundle.js +1 -0
  327. package/vendor/dist-server/services/cluster-service.js +1 -0
  328. package/vendor/dist-server/services/connection-adapters.js +1 -0
  329. package/vendor/dist-server/services/connectors-service.js +10 -0
  330. package/vendor/dist-server/services/cost-forecast-service.js +1 -0
  331. package/vendor/dist-server/services/eui-ssr-renderer.js +4 -0
  332. package/vendor/dist-server/services/graph-canvas/full-structure-builder.js +1 -0
  333. package/vendor/dist-server/services/graph-templates-service.js +13 -0
  334. package/vendor/dist-server/services/guest-service.js +1 -0
  335. package/vendor/dist-server/services/html-service.js +1 -0
  336. package/vendor/dist-server/services/langgraph-service.js +2 -0
  337. package/vendor/dist-server/services/leaflet-render-service.js +80 -0
  338. package/vendor/dist-server/services/live-resources-service.js +16 -0
  339. package/vendor/dist-server/services/mcp-app-tester-package.js +1 -0
  340. package/vendor/dist-server/services/mcp-gateway/executors/openapi-executor.js +1 -0
  341. package/vendor/dist-server/services/mcp-gateway/gateway-grant-service.js +2 -0
  342. package/vendor/dist-server/services/team-optimizer-service.js +1 -0
  343. package/vendor/dist-server/services/trace-bus-sink.js +1 -0
  344. package/vendor/dist-server/services/user-provisioning-service.js +3 -0
  345. package/vendor/dist-server/templates/mcp-app-tester/src/mcp-http.js +3 -0
  346. package/vendor/dist-server/templates/mcp-app-tester/src/server.js +1 -0
  347. package/vendor/dist-server/utils.js +1 -0
  348. package/vendor/dist-server/ws/assistant-executor.js +7 -0
  349. package/vendor/dist-server/ws/classify-streamed-json-block.js +1 -0
  350. package/vendor/dist-server/ws/collect-response-generator-node-ids.js +1 -0
  351. package/vendor/dist-server/ws/extractors/blockkit-extractor.js +1 -0
  352. package/vendor/dist-server/ws/extractors/echarts-extractor.js +1 -0
  353. package/vendor/dist-server/ws/extractors/eui-extractor.js +1 -0
  354. package/vendor/dist-server/ws/extractors/form-extractor.js +1 -0
  355. package/vendor/dist-server/ws/extractors/index.js +1 -0
  356. package/vendor/dist-server/ws/extractors/leaflet-extractor.js +1 -0
  357. package/vendor/dist-server/ws/extractors/mantine-extractor.js +1 -0
  358. package/vendor/dist-server/ws/extractors/markdown-extractor.js +7 -0
  359. package/vendor/dist-server/ws/extractors/mermaid-extractor.js +1 -0
  360. package/vendor/dist-server/ws/extractors/recharts-extractor.js +1 -0
  361. package/vendor/dist-server/ws/extractors/remotion-extractor.js +1 -0
  362. package/vendor/dist-server/ws/handler.js +66 -0
  363. package/vendor/dist-server/ws/parsers/anthropic-parser.js +7 -0
  364. package/vendor/dist-server/ws/parsers/base-parser.js +3 -0
  365. package/vendor/dist-server/ws/parsers/gemini-parser.js +8 -0
  366. package/vendor/dist-server/ws/parsers/index.js +1 -0
  367. package/vendor/dist-server/ws/parsers/parse-json-blocks.js +1 -0
  368. package/vendor/dist-server/ws/types.js +0 -0
  369. package/vendor/dist-server/ws/utils.js +1 -0
  370. package/vendor/shared/engine-vars.ts +51 -0
  371. package/vendor/shared/llm-providers-config.ts +69 -0
@@ -0,0 +1 @@
1
+ import{Router as $}from"express";import{KibanaService as _}from"../core/services/kibana-service";import{MultiKibanaService as D}from"../core/services/multi-kibana-service";import{PackagePolicyService as K}from"../core/services/package-policy-service";import{ManifestLoader as R}from"../core/services/manifest-loader";import{getPlatforms as S,getPlatformById as j}from"../core/services/platforms-service";import{isSuperuser as M}from"../core/utils/ownership";import{KibanaClientFactory as L}from"../core/services/kibana-client-factory";import{settingsConfig as A}from"../core/app-config/settings";const B=`${A.kibana.hostname}:${A.kibana.port}`;import{ElasticsearchWrapper as z}from"../core/services/elasticsearch-wrapper";import{config as G}from"../core/config";const h=$(),v=$(),T=new _,F=new D,Y=new z(G.elasticsearch,!0);async function le(r){const{platforms:a}=await S(r,void 0,void 0,1,1e3),e=a.find(t=>t.enabled&&t.type==="ElasticStack");return e?L.createFromPlatform(e):null}const U=new K,ce=new R;function x(r){const a=r.inputs?.[0],e={integrationName:r.name,description:r.description,packageName:r.package?.name,packageVersion:r.package?.version,policyTemplate:a?.policy_template||"",vars:{},inputs:{}};return a?.vars&&Object.keys(a.vars).forEach(t=>{e.vars[t]=a.vars[t].value}),r.inputs?.forEach(t=>{e.inputs[t.type]={enabled:t.enabled,vars:{}},t.vars&&Object.keys(t.vars).forEach(o=>{e.inputs[t.type].vars[o]=t.vars[o].value})}),e}h.get("/installed",async(r,a)=>{try{console.log("[INSTALLED] Fetching installed packages...");const e=await T.getEpmPackages(!1),t=await T.getPackagePolicies();if(console.log("[INSTALLED] Raw policies from Kibana:",JSON.stringify(t?.items?.[0],null,2)),t&&t.items&&e&&e.items){const o=new Map(e.items.map(s=>[s.name,s])),n=t.items.map(s=>{console.log("[INSTALLED] Processing policy:",s.id,s.package?.name);const l=o.get(s.package?.name);return{id:s.id,name:s.package?.name,version:s.package?.version,title:s.package?.title||l?.title||s.name,description:s.description||l?.description,status:"installed",icons:l?.icons||[]}});console.log("[INSTALLED] Returning packages:",JSON.stringify(n,null,2)),a.json({body:{response:n}})}else console.log("[INSTALLED] No packages found"),a.json({body:{response:[]}})}catch(e){console.error("Error fetching installed packages:",e),a.status(500).json({error:"Failed to fetch installed packages"})}}),h.get("/enrolled",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{search:t,packageName:o,status:n,from:s="0",size:l="50",aggregate:d="true"}=r.query,c=d==="true";if(console.log(`[ENROLLED] Fetching enrolled packages for user: ${e} (aggregate: ${c})`),c)try{const p=await F.getAllPackagesFromAllPlatforms(e,!1);let y=p.packages;const g=p.errors;if(console.log(`[ENROLLED] Multi-platform fetch: ${y.length} packages, ${g.length} platform errors`),y=y.filter(C=>C.platforms.some(w=>w.status==="enrolled")),console.log(`[ENROLLED] Filtered to ${y.length} enrolled packages`),t){const C=t.toLowerCase();y=y.filter(w=>w.name?.toLowerCase().includes(C)||w.title?.toLowerCase().includes(C)||w.description?.toLowerCase().includes(C))}o&&(y=y.filter(C=>C.name===o)),n&&(y=y.filter(C=>C.platforms.some(w=>w.status===n)));const f=parseInt(s),b=parseInt(l),P=y.length,O=y.slice(f,f+b);return console.log(`[ENROLLED] Returning ${O.length} of ${P} enrolled packages (multi-platform mode)`),a.json({body:{response:O,total:P,from:f,size:O.length,mode:"multi-platform",platformErrors:g.length>0?g:void 0}})}catch(p){console.error("[ENROLLED] Multi-platform mode failed:",p),console.log("[ENROLLED] Falling back to legacy mode")}console.log("[ENROLLED] Using legacy user.metadata.policies fallback");const m=(await Y.security.getUser({username:e}))[e];if(!m?.metadata?.policies)return a.json({body:{response:[],total:0,from:0,size:0,mode:"fallback"}});let u=[];for(const[p,y]of Object.entries(m.metadata.policies)){const g=y;!g||!g.package_name||g.status==="deleted"||u.push({id:g.policy_id,name:g.package_name,title:g.package_title||g.package_name,description:g.description||g.package_description||"",version:g.package_version||"unknown",status:"enrolled",icons:g.icons||[],installationInfo:{install_status:"enrolled",install_source:"registry"},policy_id:g.policy_id,policy_name:g.policy_name,cluster_name:g.cluster_name,cluster_uuid:g.cluster_uuid,namespace:p,created_at:g.created_at,platforms:[{platformId:"settings",platformName:B,status:"enrolled",policyId:g.policy_id,namespace:p}]})}if(t){const p=t.toLowerCase();u=u.filter(y=>y.name?.toLowerCase().includes(p)||y.title?.toLowerCase().includes(p)||y.description?.toLowerCase().includes(p))}o&&(u=u.filter(p=>p.name===o)),n&&(u=u.filter(p=>p.status===n));const k=parseInt(s),I=parseInt(l),E=u.length,N=u.slice(k,k+I);return console.log(`[ENROLLED] Returning ${N.length} of ${E} enrolled packages (fallback mode)`),a.json({body:{response:N,total:E,from:k,size:N.length,mode:"fallback"}})}catch(e){console.error("[ENROLLED] Error fetching enrolled packages:",e),a.status(500).json({error:e.message||"Failed to fetch enrolled packages"})}}),h.get("/",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{packages:t,allCategories:o,errors:n}=await F.getEpmCatalogFromAllPlatforms(e);if(t.length===0)return a.json({body:{response:[],total:0,allCategories:[],platformErrors:n}});let s=t;const l=r.query.status;l&&(s=s.filter(k=>k.status===l));const d=r.query.search;if(d){const k=d.toLowerCase();s=s.filter(I=>I.name.toLowerCase().includes(k)||I.title?.toLowerCase().includes(k)||I.description?.toLowerCase().includes(k)||I.categories?.some(E=>E.toLowerCase().includes(k)))}const c=s.length,i=parseInt(r.query.from)||0,m=parseInt(r.query.size)||50,u=s.slice(i,i+m);a.json({body:{response:u,total:c,allCategories:o,platformErrors:n}})}catch(e){console.error("Error fetching packages:",e),a.status(500).json({error:"Failed to fetch packages"})}}),h.get("/policy/:policyId",async(r,a)=>{try{const{policyId:e}=r.params,{platformId:t}=r.query,o=r.auth?.username;console.log(`[POLICY] Fetching policy by ID: ${e}, platformId: ${t||"default"}`);let n=T;if(t&&o){const l=await j(t,o,!0);if(!l)return a.status(404).json({error:`Platform ${t} not found`});n=L.createFromPlatform(l)}const s=await n.getPackagePolicy(e);if(console.log("[POLICY] Result from KibanaService:",JSON.stringify(s,null,2)),s&&s.data&&s.data.item){const l=s.data.item;console.log("[POLICY] Retrieved policy from Fleet API"),console.log(`[POLICY] Policy name: ${l.name}`),console.log(`[POLICY] Policy description: ${l.description}`),a.json({body:{policy:l}})}else s&&s.error?(console.error("[POLICY] Error from Kibana API:",s.error),a.status(s.statusCode||404).json({error:"Package policy not found",details:`Policy ${e} does not exist in Fleet API. It may have been deleted.`,kibanaError:s.error})):(console.log("[POLICY] No policy data returned from Fleet API"),a.status(404).json({error:"Package policy not found",details:`Policy ${e} could not be retrieved from Fleet API. It may have been deleted or never existed.`}))}catch(e){console.error("[POLICY] Error fetching package policy:",e),a.status(500).json({error:"Failed to fetch package policy",details:e.message||"Unknown error"})}}),h.get("/:packageName/config",async(r,a)=>{try{const{packageName:e}=r.params,t=r.auth?.username;if(console.log(`[CONFIG] Fetching config for package: ${e}, user: ${t}`),!t)return a.status(401).json({error:"Unauthorized",details:"User authentication required"});let o=null,n=null,s=null;try{const c=await F.findPolicyByPackageName(e,t);c&&(o=c.policyId,n={platformId:c.platformId,platformName:c.platformName},s=c.policy,console.log(`[CONFIG] Found policy ${o} on platform ${c.platformName}`))}catch(c){console.warn("[CONFIG] Platform search failed, trying legacy fallback:",c.message)}if(!o)try{const{Client:c}=await import("@elastic/elasticsearch"),{config:i}=await import("../core/config"),k=(await new c(i.elasticsearch).security.getUser({username:t}))[t];if(k?.metadata?.policies){for(const[I,E]of Object.entries(k.metadata.policies))if(E?.package_name===e){o=E?.policy_id,n=E,console.log(`[CONFIG] Found policy ID in legacy user metadata: ${o}`);break}}}catch(c){console.error("[CONFIG] Error fetching user metadata fallback:",c.message)}if(!o)return console.log(`[CONFIG] No policy found for package ${e}`),a.status(404).json({error:"Package configuration not found",details:`No integration found for package ${e}. Please install it first.`});let l=s;if(!l)try{console.log(`[CONFIG] Fetching policy from Fleet API: ${o}`),l=await T.getPackagePolicy(o),console.log("[CONFIG] Retrieved policy from Fleet API")}catch(c){return console.error("[CONFIG] Error fetching policy by ID:",c.message),a.status(500).json({error:"Failed to fetch policy configuration",details:c.message})}const d=l?.data?.item||l;if(d){const c=x(d);a.json({body:{config:{packageFormData:c,connectionSettings:{api_url:d.inputs?.[0]?.vars?.api_url?.value||"",api_key:d.inputs?.[0]?.vars?.api_key?.value||"",ssl:d.inputs?.[0]?.vars?.ssl?.value||"",interval:d.inputs?.[0]?.vars?.interval?.value||"30"},policyMetadata:n}}})}else console.log("[CONFIG] No policy data returned"),a.status(404).json({error:"Package configuration not found",details:`Policy ${o} could not be retrieved`})}catch(e){console.error("[CONFIG] Error fetching package config:",e),a.status(500).json({error:"Failed to fetch package configuration",details:e.message||"Unknown error"})}}),h.post("/:packageName/install",async(r,a)=>{try{const e=r.auth?.username,{packageFormData:t,fleetPolicyId:o,platformId:n}=r.body;if(!t)return a.status(400).json({error:"Package form data is required"});if(!n)return a.status(400).json({error:"platformId is required"});const s=await j(n,e,!0);if(!s)return a.status(404).json({error:`Platform ${n} not found`});const l=L.createFromPlatform(s);console.log("[INSTALL] Installing package:",t.packageName,"fleetPolicyId:",o,"platformId:",n);const d=await U.installPackage(t,o,l);if(d?.error)return a.status(d.statusCode||500).json({error:"Failed to install package",details:d.error});console.log("[INSTALL] Policy created successfully."),a.json({success:!0,policy:d})}catch(e){console.error("[INSTALL] Error installing package:",e),a.status(500).json({error:"Failed to install package",details:e.message||e})}}),h.post("/:packageName/update",async(r,a)=>{try{const{packageName:e}=r.params,{packageFormData:t,policyId:o,platformId:n}=r.body,s=r.auth?.username;if(console.log(`[UPDATE] Updating package: ${e}, policyId: ${o}, platformId: ${n||"default"}`),!t)return a.status(400).json({error:"Package form data is required"});if(!o)return a.status(400).json({error:"Policy ID is required for update operation",details:"Cannot update package without a valid policy ID"});let l;if(n&&s){const c=await j(n,s,!0);if(!c)return a.status(404).json({error:`Platform ${n} not found`});l=L.createFromPlatform(c)}const d=await U.updatePackagePolicy(o,t,l);if(d?.error)return a.status(d.statusCode||500).json({error:"Failed to update package",details:d.error});console.log("[UPDATE] Successfully updated policy"),a.json({success:!0,policy:d,updated:!0})}catch(e){console.error("Error updating package:",e);const t=e.statusCode||500;a.status(t).json({error:"Failed to update package",details:e.message||e})}}),h.get("/check-name",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{integrationName:t,platformId:o}=r.query;if(!t||!o)return a.status(400).json({error:"integrationName and platformId are required"});const n=await j(o,e,!0);if(!n)return a.status(404).json({error:`Platform ${o} not found`});const s=L.createFromPlatform(n),d=`name:"${t.replace(/"/g,'\\"')}"`;let c=await s.getPackagePolicies(d),i=[];if(c?.error?console.warn(`[CHECK-NAME] kuery failed (${c.statusCode}), falling back to full scan:`,c.error?.message||c.error):(i=c?.data?.items||c?.items||[],console.log(`[CHECK-NAME] platform=${o} kuery="${d}" items=${i.length}`)),i.length===0){console.log("[CHECK-NAME] falling back to full scan");const u=await s.getPackagePolicies();if(u?.error)return console.error("[CHECK-NAME] full scan also failed:",u.statusCode,u.error),a.status(502).json({error:"Failed to query Kibana Fleet API",details:u.error?.message||u.error,statusCode:u.statusCode});i=u?.data?.items||u?.items||[],console.log(`[CHECK-NAME] full scan total items=${i.length}`)}const m=i.some(u=>u.name===t);a.json({exists:m})}catch(e){console.error("[CHECK-NAME] Error:",e),a.status(500).json({error:"Failed to check integration name",details:e.message})}}),h.post("/test-connection",async(r,a)=>{try{const{api_url:e,api_key:t,ssl:o,interval:n,username:s,password:l}=r.body;if(console.log("[TEST-CONNECTION] Starting connection test"),console.log("[TEST-CONNECTION] API URL:",e),console.log("[TEST-CONNECTION] API Key provided:",!!t),console.log("[TEST-CONNECTION] Username provided:",!!s),console.log("[TEST-CONNECTION] Password provided:",!!l),!e)return a.status(400).json({success:!1,error:"API URL is required"});const{Client:d}=require("@elastic/elasticsearch");let c=null,i=null,m="";if(t){console.log("[TEST-CONNECTION] Attempting connection with API key");try{c=new d({node:e,auth:{apiKey:t},tls:{rejectUnauthorized:o==="true"||o===!0}});const u=await c.cluster.health();return console.log("[TEST-CONNECTION] API key connection successful"),a.json({success:!0,data:{status:u.status,cluster_name:u.cluster_name,authMethod:"api_key"}})}catch(u){console.error("[TEST-CONNECTION] API key connection failed:",u.message),i=u,m="api_key"}}if(s&&l){console.log("[TEST-CONNECTION] Attempting connection with username/password");try{c=new d({node:e,auth:{username:s,password:l},tls:{rejectUnauthorized:o==="true"||o===!0}});const u=await c.cluster.health();return console.log("[TEST-CONNECTION] Username/password connection successful"),a.json({success:!0,data:{status:u.status,cluster_name:u.cluster_name,authMethod:"basic"}})}catch(u){console.error("[TEST-CONNECTION] Username/password connection failed:",u.message),i=u,m="basic"}}return i?(console.error("[TEST-CONNECTION] All connection attempts failed"),a.json({success:!1,data:{error:i,authMethod:m}})):(console.error("[TEST-CONNECTION] No valid credentials provided"),a.status(400).json({success:!1,error:"Either API Key or Username/Password must be provided"}))}catch(e){console.error("[TEST-CONNECTION] Unexpected error:",e),a.status(500).json({success:!1,error:"Connection test failed",details:e.message||e})}}),h.delete("/policy/:policyId",async(r,a)=>{try{const{policyId:e}=r.params,t=r.auth?.username,{platformId:o}=r.body||{};if(console.log(`[DELETE] Deleting policy: ${e} for user: ${t}, platformId: ${o||"default"}`),!t)return a.status(401).json({error:"Unauthorized"});let n=T;if(o){const l=await j(o,t,!0);if(!l)return a.status(404).json({error:`Platform ${o} not found`});n=L.createFromPlatform(l)}console.log(`[DELETE] Calling Kibana to delete policy: ${e}`);const s=await n.deletePackagePolicy(e);if(s.error)return console.error("[DELETE] Kibana delete failed:",s.error),a.status(s.statusCode||500).json({error:"Failed to delete policy from Kibana",details:s.error});console.log("[DELETE] Policy deleted from Kibana successfully"),a.json({success:!0,message:"Package uninstalled successfully",policyId:e})}catch(e){console.error("[DELETE] Error deleting package:",e),a.status(500).json({error:"Failed to delete package",details:e.message||"Unknown error"})}}),h.get("/:packageName/platforms",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{packageName:t}=r.params,o=r.query.managedType,{platforms:n,errors:s}=await F.getPlatformsWithPackage(t,e,o);s.length>0&&console.warn(`[PKG-PLATFORMS] ${s.length} platform error(s) for package "${t}":`,s),a.json({body:{platforms:n,platformErrors:s}})}catch(e){console.error("[PKG-PLATFORMS] Error:",e),a.status(500).json({error:"Failed to fetch platforms for package",details:e.message})}}),h.get("/:packageName/:version",async(r,a)=>{try{const{packageName:e,version:t}=r.params,{platformId:o}=r.query,n=r.auth?.username;if(!o)return a.status(400).json({error:"platformId query param is required"});const s=await j(o,n,!0);if(!s)return a.status(404).json({error:`Platform ${o} not found`});const d=await L.createFromPlatform(s).getPackageByVersion(e,t);if(d.error)return a.status(d.statusCode||404).json({error:"Package not found",details:d.error});a.json({body:{item:d.data?.item||d.data}})}catch(e){console.error("Error fetching package details:",e),a.status(500).json({error:"Failed to fetch package details",details:e.message||e})}}),h.post("/bulk-install",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{platformId:t}=r.body||{};let o;if(t){const i=M(r),m=await j(t,e,!0,i);if(!m)return a.status(404).json({error:`Platform ${t} not found`});if(!m.enabled||m.type!=="ElasticStack")return a.status(400).json({error:"Platform is not an enabled ElasticStack platform"});o=[m],console.log(`[BULK-INSTALL] Admin single-platform mode: ${m.name} (${t})`)}else{const{platforms:i}=await S(e,void 0,void 0,1,1e3);o=i.filter(m=>m.enabled&&m.type==="ElasticStack")}if(o.length===0)return a.status(404).json({error:"No enabled ElasticStack platform found"});console.log(`[BULK-INSTALL] Starting bulk install on ${o.length} platform(s)`);const n=5,s=3e3,l=[];for(const i of o){const m=L.createFromPlatform(i);console.log(`[BULK-INSTALL] Platform: ${i.name} (${i.id})`);const u=await m.getEpmPackages(!1),k=u?.data?.items||u?.items||[];if(k.length===0){console.warn(`[BULK-INSTALL] No packages returned from platform ${i.name}`),l.push({platformId:i.id,platformName:i.name,error:"No packages returned"});continue}const I=await m.getPackagePolicies(),E=new Set((I?.data?.items||I?.items||[]).map(f=>f.package?.name)),N=k.filter(f=>f.status!=="installed"&&!E.has(f.name)).map(f=>f.name);if(console.log(`[BULK-INSTALL] ${i.name}: ${N.length} to install, ${E.size} already installed`),N.length===0){l.push({platformId:i.id,platformName:i.name,installed:0,skipped:k.length,batches:0});continue}const p=[];for(let f=0;f<N.length;f+=n)p.push(N.slice(f,f+n));const y=[],g=[];for(let f=0;f<p.length;f++){const b=p[f];console.log(`[BULK-INSTALL] ${i.name} batch ${f+1}/${p.length}: [${b.join(", ")}]`);try{const P=await m.bulkInstallEpmPackages(b);P.error?g.push({batch:f+1,packages:b,error:P.error.message||JSON.stringify(P.error)}):y.push(...b)}catch(P){g.push({batch:f+1,packages:b,error:P.message||P.toString()})}f<p.length-1&&await new Promise(P=>setTimeout(P,s))}l.push({platformId:i.id,platformName:i.name,installed:y.length,failed:g.length,total:N.length,batches:p.length,successfulPackages:y,failedBatches:g})}const d=l.reduce((i,m)=>i+(m.installed||0),0),c=l.reduce((i,m)=>i+(m.failed||0),0);console.log(`[BULK-INSTALL] Done: ${d} installed, ${c} failed across ${o.length} platform(s)`),a.json({success:c===0,message:`Bulk installation completed: ${d} packages installed across ${o.length} platform(s)`,platforms:l})}catch(e){console.error("[BULK-INSTALL] Error during bulk installation:",e),a.status(500).json({error:"Failed to bulk install packages",details:e.message||e})}}),h.post("/enrolled/:packageId/sync-tools",async(r,a)=>{try{const e=r.auth?.username;if(!e)return a.status(401).json({error:"Unauthorized"});const{packageId:t}=r.params,{mcpBaseUrl:o}=r.body||{},{syncElasticStackPackageTools:n}=await import("../core/services/elastic-stack-sync-service"),s=await n(t,e,o?{mcpBaseUrl:o}:void 0);a.json({success:!0,...s})}catch(e){console.error("[ENROLLED-SYNC] Error syncing package tools:",e);const t=e.message?.includes("not found")?404:500;a.status(t).json({error:e.message||"Failed to sync package tools"})}}),v.get("/",async(r,a)=>{try{console.log("[PUBLIC-PACKAGES] Fetching all EPM packages...");const e=await T.getEpmPackages(!1);if(e&&e.data&&e.data.items){const t=e.data.items.map(o=>({name:o.name,version:o.version,title:o.title,description:o.description,icons:o.icons||[],categories:o.categories||[]}));console.log(`[PUBLIC-PACKAGES] Returning ${t.length} packages`),a.json({body:{response:t}})}else console.log("[PUBLIC-PACKAGES] No packages found or error:",e),a.json({body:{response:[]}})}catch(e){console.error("[PUBLIC-PACKAGES] Error fetching packages:",e),a.status(500).json({error:"Failed to fetch packages"})}});var ie=h;export{ie as default,v as publicRouter};
@@ -0,0 +1,3 @@
1
+ import m from"express";import{PDFService as s}from"../core/services/pdf-service";import{z as e}from"zod";const l=m.Router(),f=e.object({id:e.string(),content:e.any(),role:e.enum(["user","assistant","agent"]),timestamp:e.string().datetime().or(e.date()).transform(r=>new Date(r))}).required(),p=e.object({filename:e.string().optional(),title:e.string().optional(),includeTimestamp:e.boolean().optional().default(!0),format:e.enum(["A4","A3","Letter"]).optional().default("A4"),orientation:e.enum(["portrait","landscape"]).optional().default("portrait"),margin:e.object({top:e.string().optional(),right:e.string().optional(),bottom:e.string().optional(),left:e.string().optional()}).optional(),quality:e.number().min(1).max(10).optional().default(2),displayHeaderFooter:e.boolean().optional().default(!1),headerTemplate:e.string().optional(),footerTemplate:e.string().optional()}),g=e.object({messageContent:f,options:p.optional()}),d=e.object({blocks:e.array(e.any()),options:p.optional()}),u=e.object({markdown:e.string(),options:p.optional()});l.post("/export",async(r,o)=>{try{const{messageContent:t,options:n}=g.parse(r.body);console.log("PDF Export request:",{messageId:t.id,role:t.role,contentType:typeof t.content,options:n});const a=await s.generatePDF(t,n||{}),i=n?.filename||s.getDefaultFilename(n?.title,t.timestamp);o.set({"Content-Type":"application/pdf","Content-Disposition":`attachment; filename="${i}.pdf"`,"Content-Length":a.length.toString()}),o.send(a)}catch(t){if(console.error("PDF export error:",t),t instanceof e.ZodError)return o.status(400).json({error:"Validation error",details:t.errors});o.status(500).json({error:"Internal server error",message:t instanceof Error?t.message:"Unknown error"})}}),l.post("/export-blocks",async(r,o)=>{try{const{blocks:t,options:n}=d.parse(r.body);console.log("PDF Blocks export request:",{blocksCount:t.length,blockTypes:t.map(c=>c?.type||"unknown"),options:n});const a=await s.generatePDFFromBlocks(t,n||{}),i=n?.filename||s.getDefaultFilename(n?.title);o.set({"Content-Type":"application/pdf","Content-Disposition":`attachment; filename="${i}.pdf"`,"Content-Length":a.length.toString()}),o.send(a)}catch(t){if(console.error("PDF blocks export error:",t),t instanceof e.ZodError)return o.status(400).json({error:"Validation error",details:t.errors});o.status(500).json({error:"Internal server error",message:t instanceof Error?t.message:"Unknown error"})}}),l.post("/export-markdown",async(r,o)=>{try{const{markdown:t,options:n}=u.parse(r.body);console.log("PDF Markdown export request:",{markdownLength:t.length,options:n});const a=await s.generatePDFFromMarkdown(t,n||{}),i=n?.filename||s.getDefaultFilename(n?.title);o.set({"Content-Type":"application/pdf","Content-Disposition":`attachment; filename="${i}.pdf"`,"Content-Length":a.length.toString()}),o.send(a)}catch(t){if(console.error("PDF markdown export error:",t),t instanceof e.ZodError)return o.status(400).json({error:"Validation error",details:t.errors});o.status(500).json({error:"Internal server error",message:t instanceof Error?t.message:"Unknown error"})}}),l.get("/health",async(r,o)=>{try{const t={id:"health-check",content:`# Health Check
2
+
3
+ PDF service is working correctly.`,role:"agent",timestamp:new Date},n=await s.generatePDF(t,{title:"Health Check",includeTimestamp:!0});o.json({status:"healthy",pdfSize:n.length,timestamp:new Date().toISOString()})}catch(t){console.error("PDF health check error:",t),o.status(500).json({status:"unhealthy",error:t instanceof Error?t.message:"Unknown error",timestamp:new Date().toISOString()})}});var w=l;export{w as default};
@@ -0,0 +1 @@
1
+ import p from"jsonwebtoken";import{z as u}from"zod";import{Client as m}from"@elastic/elasticsearch";import{config as d}from"../core/config";import{getQuotaReport as y}from"../core/services/plan-service";const o=new m(d.elasticsearch);function l(r){const t=r.auth?.username;if(t)return t;let e=r.cookies?.token;if(!e){const a=r.headers.authorization??"";e=a.startsWith("Bearer ")?a.slice(7):a||null}if(!e)return null;try{return p.decode(e)?.username??null}catch{return null}}const g=u.object({username:u.string().min(1),plan:u.enum(["trial","basic","enterprise","premium"])}),q=[{method:"get",path:"/api/plan/quotas",openapi:{summary:"Get the caller's current plan quota usage",description:"Returns usage-vs-limit counters for the authenticated user, computed against their current plan (trial/basic/enterprise/premium).",tags:["plan"]},handler:async(r,t)=>{const e=l(r);if(!e)return t.status(401).json({error:"Unauthorized"});try{const s=((await o.security.getUser({username:e}))[e]?.metadata??{}).plan??"trial",n=await y(e,s);return t.json({success:!0,quotas:n})}catch(a){return console.error("[plan/quotas] Error:",a),t.status(500).json({error:"Internal server error"})}}},{method:"post",path:"/api/admin/plan/assign",validate:{body:g},openapi:{summary:"Manually assign a plan to a user",description:"Updates a target user's `metadata.plan` and resets `planStatus` to `active`. Used for premium upsell where Stripe checkout isn't applicable.",tags:["plan","admin"],requiredRoles:["superuser"]},handler:async(r,t)=>{const e=l(r);if(!e)return t.status(401).json({error:"Unauthorized"});try{if(!((await o.security.getUser({username:e}))[e]?.roles??[]).includes("superuser"))return t.status(403).json({error:"Superuser access required"});const{username:s,plan:n}=r.body,i=(await o.security.getUser({username:s}))[s];return i?(await o.security.putUser({username:s,body:{...i,metadata:{...i.metadata??{},plan:n,planStatus:"active",planActivatedAt:new Date().toISOString(),...n==="trial"?{planExpiresAt:new Date(Date.now()+360*3600*1e3).toISOString()}:{planExpiresAt:void 0}}}}),console.log(`[admin/plan/assign] ${e} assigned plan '${n}' to '${s}'`),t.json({success:!0,username:s,plan:n})):t.status(404).json({error:`User '${s}' not found`})}catch(a){return console.error("[admin/plan/assign] Error:",a),t.status(500).json({error:"Internal server error"})}}}];export{q as routes};
@@ -0,0 +1 @@
1
+ import{z as t}from"zod";import{sendPlatformRequestNotification as p}from"../core/services/send_mail";import{getAuthUsername as l}from"../core/utils/ownership";const u={ElasticStackHosting:"Elastic Stack Hosting",OpenAPIGateway:"OpenAPI Gateway",AsyncAPIGateway:"AsyncAPI Gateway",MCPServerGateway:"MCP Server Gateway",ToolboxGateway:"Toolbox Gateway"},r=t.object({platformType:t.enum(["ElasticStackHosting","OpenAPIGateway","AsyncAPIGateway","MCPServerGateway","ToolboxGateway"]),description:t.string().min(10,"Description must be at least 10 characters"),dailyTransactions:t.string().min(1,"Please provide an estimate")}),y=async(a,s)=>{const n=l(a),e=r.safeParse(a.body);if(!e.success){s.status(400).json({success:!1,error:e.error.format()});return}const{platformType:o,description:i,dailyTransactions:c}=e.data,m=u[o]??o;await p(n,m,i,c),s.json({success:!0})},g=[{method:"post",path:"/api/platform-requests",handler:y,validate:{body:r},openapi:{summary:"Submit a managed platform request",tags:["Platforms"],audience:"public"}}];export{g as routes};
@@ -0,0 +1 @@
1
+ import $ from"express";import{createPlatform as z,getPlatforms as v,getPlatformById as y,updatePlatform as U,deletePlatform as x,testPlatformHealth as E,CommandValidationError as F}from"../core/services/platforms-service";import{findTeamOwningCommand as K,getTeamInvocationConfig as D,getTeamChannelCommandConfig as B}from"../core/services/teams-service";import{getTriggersBySubscriberPlatform as N}from"../core/services/trigger-service";import{KibanaClientFactory as C}from"../core/services/kibana-client-factory";import{userToken as T}from"../services/auth";import{maskPlatformSecrets as O}from"../core/services/platform-secrets";function R(a){const{config:e,secretFieldsSet:s}=O(a.type,a.config);return{...a,config:e,secretFieldsSet:s}}const m=$.Router();function f(a){const e=a.auth?.username;if(e)return e;let s=a.cookies?.token;if(!s){const o=a.headers.authorization;o&&o.startsWith("Bearer ")&&(s=o.substring(7))}return s?T(s)?.username||null:(console.warn("[platforms] No token found in request"),null)}async function H(a,e){try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const t=a.query.managedType,o=a.query.type,c=a.query.name,n=a.query.direction,r=Math.max(1,parseInt(a.query.page)||1),i=Math.min(100,Math.max(1,parseInt(a.query.pageSize)||20)),l=await v(s,t,o,r,i,c,n);e.json({success:!0,body:{platforms:l.platforms.map(R),total:l.total,page:l.page,pageSize:l.pageSize,totalPages:Math.ceil(l.total/l.pageSize)}})}catch(s){console.error("Failed to get platforms:",s),e.status(500).json({success:!1,message:s.message||"Failed to get platforms"})}}m.get("/platforms",H),m.get("/platforms/:id",async(a,e)=>{try{const s=f(a),{id:t}=a.params;if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const o=await y(t,s,!0);if(!o)return e.status(404).json({success:!1,message:"Platform not found"});e.json({success:!0,body:{platform:R(o)}})}catch(s){console.error("Failed to get platform:",s),e.status(500).json({success:!1,message:s.message||"Failed to get platform"})}});async function L(a,e){try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const{platformId:t,command:o}=a.params,c=decodeURIComponent(o),n=await y(t,s,!0);if(!n)return e.status(404).json({success:!1,message:"Platform not found"});const r=(n.commands??[]).find(u=>u.command===c);if(!r)return e.status(404).json({success:!1,message:`Command "${c}" is not defined on this platform`});const i=await K(n.owner,t,r.id);if(!i)return e.status(404).json({success:!1,message:`Command "${c}" is not assigned to any team`});const[l,p]=await Promise.all([D(i.teamId),B(i.teamId,t)]);e.json({success:!0,body:{teamId:i.teamId,teamName:i.teamName,assistantIds:l?.assistantIds??[],namespace:p?.namespace,blockKitMode:p?.blockKitMode===!0}})}catch(s){console.error("Failed to resolve command team:",s),e.status(500).json({success:!1,message:s.message||"Failed to resolve command team"})}}m.get("/platforms/:platformId/commands/:command/team",L);async function V(a,e){try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const{name:t,type:o,managedType:c,enabled:n,config:r,allowedIndexPatterns:i,commands:l}=a.body;if(!t||!o||!r)return e.status(400).json({success:!1,message:"Missing required fields: name, type, config"});if(!["ElasticStack","OpenSearch","OpenAPI","AsyncAPI","MCPServer","Toolbox","Slack"].includes(o))return e.status(400).json({success:!1,message:"Invalid platform type. Must be ElasticStack, OpenSearch, OpenAPI, AsyncAPI, MCPServer, Toolbox, or Slack"});const u=await z({name:t,type:o,managedType:c==="managed"?"managed":"self-hosted",owner:s,enabled:n!==void 0?n:!0,config:r,...typeof i=="string"?{allowedIndexPatterns:i}:{},...Array.isArray(l)?{commands:l}:{}});e.status(201).json({success:!0,body:{platform:R(u)}})}catch(s){if(console.error("Failed to create platform:",s),s instanceof F)return e.status(400).json({success:!1,message:s.message});e.status(500).json({success:!1,message:s.message||"Failed to create platform"})}}m.post("/platforms",V),m.put("/platforms/:id",async(a,e)=>{try{const s=f(a),{id:t}=a.params;if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const{name:o,type:c,managedType:n,enabled:r,config:i,allowedIndexPatterns:l,commands:p}=a.body,u={};o!==void 0&&(u.name=o),c!==void 0&&(u.type=c),n!==void 0&&(u.managedType=n),r!==void 0&&(u.enabled=r),i!==void 0&&(u.config=i),l!==void 0&&(u.allowedIndexPatterns=l),p!==void 0&&(u.commands=p);const b=await U(t,s,u);if(!b)return e.status(404).json({success:!1,message:"Platform not found"});e.json({success:!0,body:{platform:R(b)}})}catch(s){if(console.error("Failed to update platform:",s),s instanceof F)return e.status(400).json({success:!1,message:s.message});e.status(500).json({success:!1,message:s.message||"Failed to update platform"})}}),m.delete("/platforms/:id",async(a,e)=>{try{const s=f(a),{id:t}=a.params;if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});try{const n=await N(t,s);if(n.length>0)return e.status(409).json({success:!1,message:`Platform has ${n.length} attached AsyncAPI subscriber trigger(s). Delete or detach them first.`,attachedTriggers:n.map(r=>({id:r.id,name:r.name,targetTeamId:r.targetTeamId,enabled:r.enabled}))})}catch(n){return console.warn(`[platforms/delete] failed to check attached triggers for ${t}: ${n?.message??n}`),e.status(500).json({success:!1,message:"Failed to verify trigger attachments before delete"})}const{deleted:o,toolsDeleted:c}=await x(t,s);if(!o)return e.status(404).json({success:!1,message:"Platform not found"});e.json({success:!0,body:{message:c?`Platform deleted successfully (${c} linked tool${c>1?"s":""} removed)`:"Platform deleted successfully",toolsDeleted:c}})}catch(s){console.error("Failed to delete platform:",s),e.status(500).json({success:!1,message:s.message||"Failed to delete platform"})}}),m.get("/platforms/:id/version",async(a,e)=>{try{const s=f(a),{id:t}=a.params;if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const o=await y(t,s,!0);if(!o)return e.status(404).json({success:!1,message:"Platform not found"});const n=await C.createFromPlatform(o).getVersion();if(n.error||n.statusCode&&n.statusCode>=400)return e.status(n.statusCode||500).json({success:!1,body:{error:n.error||n.data}});const r=n.data?.version?.number;return e.json({success:!0,body:{version:r}})}catch(s){return console.error("[PLATFORM-VERSION] Error:",s),e.status(500).json({success:!1,message:s.message||"Unknown error"})}}),m.post("/platforms/:id/test",async(a,e)=>{try{const s=f(a),{id:t}=a.params;if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const o=a.cookies?.token||a.headers.authorization?.substring(7),n=(o?T(o):null)?.roles?.includes("superuser")??!1,r=await y(t,s,!0,n);if(!r)return e.status(404).json({success:!1,message:"Platform not found"});const i=await E(r);e.json({success:!0,body:{healthStatus:i}})}catch(s){console.error("Failed to test platform:",s),e.status(500).json({success:!1,message:s.message||"Failed to test platform"})}}),m.get("/platforms/:id/kibana-agents",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const{platforms:t}=await v(s,void 0,void 0,1,1e3),o=t.find(r=>r.id===a.params.id);if(!o)return e.status(404).json({success:!1,message:"Platform not found"});if(o.type!=="ElasticStack")return e.status(400).json({success:!1,message:"Kibana Agent Builder is only available on ElasticStack platforms"});const n=await C.createFromPlatform(o).getAgentBuilderAgents();e.json({success:!0,agents:n})}catch(s){console.error("[platforms] Failed to list Kibana agents:",s),e.status(500).json({success:!1,message:s.message||"Failed to list Kibana agents"})}}),m.get("/platforms/:id/kibana-agents/:agentId",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const{platforms:t}=await v(s,void 0,void 0,1,1e3),o=t.find(r=>r.id===a.params.id);if(!o)return e.status(404).json({success:!1,message:"Platform not found"});if(o.type!=="ElasticStack")return e.status(400).json({success:!1,message:"Kibana Agent Builder is only available on ElasticStack platforms"});const n=await C.createFromPlatform(o).getAgentBuilderAgentDetail(a.params.agentId);e.json({success:!0,agentCard:n})}catch(s){console.error("[platforms] Failed to fetch Kibana agent detail:",s),e.status(500).json({success:!1,message:s.message||"Failed to fetch Kibana agent detail"})}}),m.get("/platforms/:id/sync-preview",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const t=await y(a.params.id,s);if(!t)return e.status(404).json({success:!1,message:"Platform not found"});if(t.managedType!=="managed")return e.status(400).json({success:!1,message:"sync-preview is only available for managed (serverless) platforms"});const{previewManagedSync:o}=await import("../core/services/mcp-sync-service"),c=await o(a.params.id,t,s);e.json({success:!0,...c})}catch(s){console.error("[platforms] sync-preview error:",s),e.status(500).json({success:!1,message:s.message||"Failed to compute sync preview"})}}),m.get("/platforms/:id/mcp-capabilities",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const t=await y(a.params.id,s);if(!t)return e.status(404).json({success:!1,message:"Platform not found"});const o=t.config;if(!(t.type==="MCPServer"&&(o?.serverType==="remote"||o?.serverType==="gateway")))return e.status(400).json({success:!1,message:"mcp-capabilities is only available for remote MCPServer platforms"});const{getRemoteMcpCapabilityCounts:n}=await import("../core/services/mcp-sync-service"),r=await n(t);e.json({success:!0,...r})}catch(s){console.error("[platforms] mcp-capabilities error:",s),e.status(500).json({success:!1,message:s.message||"Failed to fetch MCP capabilities"})}}),m.post("/platforms/:id/sync-tools",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const t=await y(a.params.id,s);if(!t)return e.status(404).json({success:!1,message:"Platform not found"});const o=t.config,c=t.type==="MCPServer"&&(o?.serverType==="remote"||o?.serverType==="gateway"),n=t.type==="MCPServer"&&o?.serverType==="local",r=t.managedType==="managed";if(n)return e.status(400).json({success:!1,message:"This MCP server is auto-managed by the ElasticStack sync. Its tools are already provisioned \u2014 no manual sync needed."});if(!c&&!r)return e.status(400).json({success:!1,message:"sync-tools is only available for remote MCPServer platforms or managed (serverless) platforms"});if(c){const{syncRemoteToolsForPlatform:p,syncRemotePromptsForPlatform:u,syncRemoteResourcesForPlatform:b}=await import("../core/services/mcp-sync-service"),{syncPrompts:k,syncResources:S}=a.body||{},P={success:!0,...await p(a.params.id,t,s)};if(k){const g=await u(a.params.id,t,s);P.promptsSynced=g.synced}if(S){const g=await b(a.params.id,t,s);P.resourcesSynced=g.synced}return e.json(P)}const{syncManagedToolsForPlatform:i}=await import("../core/services/mcp-sync-service"),l=await i(a.params.id,t,s);e.json({success:!0,...l})}catch(s){console.error("[platforms] sync-tools error:",s),e.status(500).json({success:!1,message:s.message||"Failed to sync tools"})}}),m.post("/platforms/:id/sync-kibana-tools",async(a,e)=>{try{const s=f(a);if(!s)return e.status(401).json({success:!1,message:"Unauthorized"});const t=await y(a.params.id,s,!0,!1);if(!t)return e.status(404).json({success:!1,message:"Platform not found"});if(t.type!=="ElasticStack")return e.status(400).json({success:!1,message:"sync-kibana-tools is only available for ElasticStack platforms"});const o=t.config,c=o.endpoints?.kibana?.url||"",n=o.endpoints?.kibana?.port||"5601";let r=c.startsWith("http")?c:`https://${c}`;try{new URL(r).port||(r=`${r}:${n}`)}catch{r=`https://${c}:${n}`}o.endpoints?.kibana?.basePath&&(r=`${r}/${o.endpoints.kibana.basePath.replace(/^\/+|\/+$/g,"")}`);const i=o.access?.apiKeys?.[0]?.name??"";if(!i)return e.status(400).json({success:!1,message:`Platform "${t.name}" has no API key configured`});const l=await import("https"),p=(await import("axios")).default,u=new l.Agent({rejectUnauthorized:!1}),k=await p.create({httpsAgent:u,headers:{Authorization:`ApiKey ${i}`,"kbn-xsrf":"reporting","Content-Type":"application/json"},timeout:3e4}).get(`${r}/api/fleet/epm/packages`),w=(k.data?.items??k.data?.response??[]).filter(d=>d.status==="installed");console.log(`[sync-kibana-tools] Platform "${t.name}" \u2014 ${w.length} installed packages`);const{syncElasticStackPackageTools:P}=await import("../core/services/elastic-stack-sync-service"),g=[];for(const d of w){const j=Buffer.from(`${t.id}|${d.name}|default`).toString("base64");try{const h=await P(j,s);g.push({packageName:d.name,success:!0,apiDocs:(h.apiDocs?.created??0)+(h.apiDocs?.updated??0),tools:(h.tools?.created??0)+(h.tools?.updated??0)})}catch(h){console.warn(`[sync-kibana-tools] Failed for package "${d.name}": ${h.message}`),g.push({packageName:d.name,success:!1,error:h.message})}}const I=g.reduce((d,j)=>d+(j.apiDocs??0),0),M=g.reduce((d,j)=>d+(j.tools??0),0),A=g.filter(d=>!d.success).length;e.json({success:!0,packages:w.length,failed:A,apiDocs:I,tools:M,details:g})}catch(s){console.error("[platforms] sync-kibana-tools error:",s),e.status(500).json({success:!1,message:s.message||"Failed to sync kibana tools"})}});var es=m;export{V as createPlatformHandler,es as default,H as getPlatformsHandler,L as resolveCommandTeamHandler};
@@ -0,0 +1 @@
1
+ import{randomUUID as h}from"crypto";import{z as t}from"zod";import{Client as S}from"@elastic/elasticsearch";const x=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",E=process.env.ELASTICSEARCH_USER||"elastic",R=process.env.ELASTICSEARCH_PASSWORD||"",l=".stkxp_prompts",c=new S({node:x,auth:{username:E,password:R},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),v=t.object({name:t.string().min(1),content:t.string().optional(),format:t.string().optional(),role:t.string().optional(),rules:t.string().optional(),task:t.string().optional(),tools:t.string().optional(),topic:t.string().optional(),owner:t.string().optional(),tags:t.array(t.string()).optional(),context:t.any().optional(),behavior:t.any().optional(),output_policy:t.any().optional(),error_policy:t.any().optional(),routing:t.any().optional(),tools_policy:t.any().optional(),metadata:t.any().optional(),description:t.string().optional()}),j=t.object({name:t.string().optional(),content:t.string().optional(),format:t.string().optional(),role:t.string().optional(),rules:t.string().optional(),task:t.string().optional(),tools:t.string().optional(),topic:t.string().optional(),owner:t.string().optional(),tags:t.array(t.string()).optional(),context:t.any().optional(),behavior:t.any().optional(),output_policy:t.any().optional(),error_policy:t.any().optional(),routing:t.any().optional(),tools_policy:t.any().optional(),metadata:t.any().optional(),description:t.string().optional()});function b(o){return!o||o.trim()===""?[]:o.split(",").map(s=>s.trim()).filter(s=>s.length>0).map(s=>({id:s}))}function C(o){return{id:o.id||h(),name:o.name,owner:o.owner||"system",version:o.version||"1.0.0",created_at:o.timestamp||new Date().toISOString(),updated_at:new Date().toISOString(),tags:o.tags||["system","prompt"],topic:o.topic||"",context:{role:o.role||"",style:{language:"fr-FR",tone:"technique",audience:"SRE / architectes Elastic"},constraints:[]},behavior:{task_brief:void 0,detailed_instructions:o.task||o.content||""},tools_policy:{tools_raw:o.tools||"",allowed_tools:b(o.tools||""),auto_tool_calling:"dynamic"},output_policy:{raw:o.format||"",mode:(o.format||"").toLowerCase().includes("jsonl")?"jsonl":"markdown",no_wrapper:(o.format||"").includes("wrapper")},error_policy:{raw:o.rules||""},routing:{match_keywords:o.topic?[o.topic]:void 0,priority:50},metadata:{status:"active",cluster_scope:"single_cluster"},temporal_logic:{enabled:!1}}}async function k(o,s){try{const{search:e,tag:i,owner:n,owners:r,limit:u="100"}=o.query,p={bool:{must:[]}};if(e&&p.bool.must.push({multi_match:{query:e,fields:["name^2","content","task","role","rules"]}}),i&&p.bool.must.push({term:{tags:i}}),n&&p.bool.must.push({term:{owner:n}}),r){const d=r.split(",").map(y=>y.trim()).filter(Boolean);d.length>0&&p.bool.must.push({bool:{should:d.map(y=>({term:{owner:y}})),minimum_should_match:1}})}const a={name:"name.keyword",createdAt:"created_at"},w=String(o.query.sortField||"name"),f=a[w]||"name.keyword",g=o.query.sortOrder==="desc"?"desc":"asc",P=f==="created_at"?[{created_at:{order:g,unmapped_type:"date"}}]:[{[f]:{order:g,unmapped_type:"keyword"}}],m=await c.search({index:l,body:{query:p.bool.must.length>0?p:{match_all:{}},size:parseInt(u),sort:P}}),_=m.hits.hits.map(d=>({id:d._id,...d._source}));s.json({success:!0,total:typeof m.hits.total=="object"?m.hits.total.value:m.hits.total,prompts:_,profiles:_})}catch(e){console.error("Error fetching prompts:",e),s.status(500).json({success:!1,error:"Failed to fetch prompts",message:e.message})}}async function D(o,s){try{const{id:e}=o.params,i=await c.get({index:l,id:e}),n={id:i._id,...i._source};s.json({success:!0,prompt:n,profile:n})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Prompt not found"});return}console.error("Error fetching prompt:",e),s.status(500).json({success:!1,error:"Failed to fetch prompt",message:e.message})}}async function F(o,s){try{const i=o.body.context||o.body.behavior||o.body.output_policy?{...o.body,id:o.body.id||h(),created_at:new Date().toISOString(),updated_at:new Date().toISOString()}:C(o.body),n=await c.index({index:l,id:i.id,body:i,refresh:!0});s.status(201).json({success:!0,prompt:{id:n._id,...i},profile:{id:n._id,...i}})}catch(e){console.error("Error creating prompt:",e),s.status(500).json({success:!1,error:"Failed to create prompt",message:e.message})}}const I=["name","topic","owner","tags","description"],O=["context","behavior","output_policy","error_policy","routing","tools_policy","metadata","temporal_logic"];async function A(o,s){try{const{id:e}=o.params,n=(await c.get({index:l,id:e}))._source,r={updated_at:new Date().toISOString()};for(const a of I)o.body[a]!==void 0&&(r[a]=o.body[a]);for(const a of O)o.body[a]!==void 0&&(r[a]={...n[a],...o.body[a]});o.body.role!==void 0&&(r.context={...n.context,...r.context,role:o.body.role}),(o.body.task!==void 0||o.body.content!==void 0)&&(r.behavior={...n.behavior,...r.behavior,detailed_instructions:o.body.task||o.body.content||""}),o.body.tools!==void 0&&(r.tools_policy={...n.tools_policy,...r.tools_policy,tools_raw:o.body.tools||"",allowed_tools:b(o.body.tools||"")}),o.body.format!==void 0&&(r.output_policy={...n.output_policy,...r.output_policy,raw:o.body.format||"",mode:(o.body.format||"").toLowerCase().includes("jsonl")?"jsonl":"markdown",no_wrapper:(o.body.format||"").includes("wrapper")}),o.body.rules!==void 0&&(r.error_policy={...n.error_policy,...r.error_policy,raw:o.body.rules||""}),await c.update({index:l,id:e,body:{doc:r},refresh:!0});const u=await c.get({index:l,id:e}),p={id:u._id,...u._source};s.json({success:!0,prompt:p,profile:p})}catch(e){if(console.error("Error in updatePrompt:",e),e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Prompt not found"});return}s.status(500).json({success:!1,error:"Failed to update prompt",message:e.message})}}async function T(o,s){try{const{id:e}=o.params;await c.delete({index:l,id:e,refresh:!0}),s.json({success:!0,message:"Prompt deleted successfully"})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Prompt not found"});return}console.error("Error deleting prompt:",e),s.status(500).json({success:!1,error:"Failed to delete prompt",message:e.message})}}const W=[{method:"get",path:"/api/prompts",handler:k},{method:"get",path:"/api/prompts/:id",handler:D},{method:"post",path:"/api/prompts",handler:F,validate:{body:v}},{method:"put",path:"/api/prompts/:id",handler:A,validate:{body:j}},{method:"delete",path:"/api/prompts/:id",handler:T}];export{W as routes};
@@ -0,0 +1 @@
1
+ import{Router as O}from"express";import{settingsConfig as t}from"../core/app-config/settings";import{HttpsClient as $}from"../core/utils/http-client";import{createAuthHeader as d}from"../core/utils/string-helpers";import*as m from"fs";import*as h from"path";import*as k from"https";import{fileURLToPath as b}from"url";const C=b(import.meta.url),P=h.dirname(C),u=O(),L=new $(t.certificates,t.defaults.timeout);u.get("/package/:packageName/:version/img/:filename",async(l,e)=>{const{packageName:o,version:i,filename:a}=l.params;if(!a.match(/\.(svg|png|jpg|jpeg|gif)$/i))return e.status(400).send("Invalid file type");const s=`/package/${o}/${i}/img/${a}`;console.log(`[LOGO-PROXY] Proxying package logo: ${s}`);try{const c=m.readFileSync(t.certificates.cert),g=m.readFileSync(t.certificates.key),r=m.readFileSync(t.certificates.ca),f={hostname:t.kibana.hostname,port:t.kibana.port,path:`${t.kibana.basePath}/api/fleet/epm/packages/${o}/${i}/img/${a}`,method:"GET",headers:{Authorization:(()=>{const n=t.kibana;return n.credentials?d(n.credentials.username,n.credentials.password):`ApiKey ${n.apiKey}`})(),"kbn-xsrf":"true"},cert:c,key:g,ca:r,rejectUnauthorized:!1};k.get(f,n=>{if(n.statusCode===200){const p=a.split(".").pop()?.toLowerCase(),y=p==="svg"?"image/svg+xml":p==="png"?"image/png":p==="jpg"||p==="jpeg"?"image/jpeg":"image/gif";e.setHeader("Content-Type",y),e.setHeader("Cache-Control","public, max-age=86400"),n.pipe(e)}else console.warn(`[LOGO-PROXY] Package logo not found (${n.statusCode}): ${s}`),e.status(404).send("Logo not found")}).on("error",n=>{console.error(`[LOGO-PROXY] Error proxying ${s}:`,n.message),e.status(500).send("Error fetching logo")})}catch(c){return console.error(`[LOGO-PROXY] Error setting up request for ${s}:`,c.message),e.status(500).send("Error fetching logo")}}),u.get("/img/:filename",async(l,e)=>{const{filename:o}=l.params;if(!o.match(/\.(svg|png|jpg|jpeg|gif)$/i))return e.status(400).send("Invalid file type");const i=h.join(P,"../../public/img",o);if(m.existsSync(i))return e.sendFile(i);console.log(`[LOGO-PROXY] Proxying ${o} from Kibana...`);try{const a=t.kibana,s=a.credentials?d(a.credentials.username,a.credentials.password):`ApiKey ${a.apiKey}`,c={hostname:t.kibana.hostname,port:t.kibana.port,path:`${t.kibana.basePath}/img/${o}`,method:"GET",headers:{Authorization:s}},g=await L.request(c);if(g.statusCode===200&&g.data){const r=o.split(".").pop()?.toLowerCase(),f=r==="svg"?"image/svg+xml":r==="png"?"image/png":r==="jpg"||r==="jpeg"?"image/jpeg":"image/gif";return e.setHeader("Content-Type",f),e.setHeader("Cache-Control","public, max-age=86400"),e.send(g.data)}else return console.warn(`[LOGO-PROXY] Logo not found on Kibana: ${o}`),e.status(404).send("Logo not found")}catch(a){return console.error(`[LOGO-PROXY] Error proxying ${o}:`,a.message),e.status(500).send("Error fetching logo")}});var H=u;export{H as default};
@@ -0,0 +1 @@
1
+ import{z as o}from"zod";import{Client as q}from"@elastic/elasticsearch";import{config as w}from"../core/config";import{SCORING_WINDOWS as d}from"../core/services/tool-metrics-service";const m=new q(w.elasticsearch),y=".stkxp_tool_metrics",g=".stkxp_golden_questions";function c(s){return s.auth?.username??null}const b=o.object({window:o.enum(["7d","30d","90d"]).optional(),quadrant:o.enum(["boost","fix","disable","niche"]).optional(),system:o.string().optional(),sort:o.enum(["Q","volume","errorRate","emptyResultRate","avgDurationMs"]).optional(),order:o.enum(["asc","desc"]).optional(),size:o.coerce.number().int().min(1).max(1e3).optional(),from:o.coerce.number().int().min(0).optional()}),R=o.object({window:o.enum(["7d","30d","90d"]).optional()}),j=o.object({tool:o.string().optional(),system:o.string().optional(),size:o.coerce.number().int().min(1).max(1e3).optional(),from:o.coerce.number().int().min(0).optional()}),Q=[{method:"get",path:"/api/monitoring/quality/tool-metrics",validate:{query:b},openapi:{summary:"List my tool-quality metrics",description:"Per-user mirror of /api/admin/tool-metrics \u2014 always filtered to the caller's username.",tags:["monitoring","tool-quality"]},handler:async(s,e)=>{const n=c(s);if(!n){e.status(401).json({error:"Unauthorized"});return}try{const t=s.query,i=t.window??"30d",r=[{term:{owner:n}},{term:{window:i}}];t.quadrant&&r.push({term:{quadrant:t.quadrant}}),t.system&&r.push({term:{system:t.system}});const a=t.sort??"volume",u=t.order??"desc",l=await m.search({index:y,body:{query:{bool:{filter:r}},sort:[{[a]:{order:u,missing:"_last"}}],size:t.size??100,from:t.from??0}}),p=typeof l.hits?.total=="number"?l.hits.total:l.hits?.total?.value??0,f=(l.hits?.hits??[]).map(h=>({toolIds:[],hasDef:!1,defMatchCount:0,sampleErrors:[],scoreArgs:null,malformedArgRate:null,avgDurationMs:null,...h._source}));e.json({success:!0,window:i,total:p,items:f})}catch(t){if(t?.meta?.statusCode===404){e.json({success:!0,window:s.query.window??"30d",total:0,items:[]});return}console.error("[quality] list failed:",t),e.status(500).json({error:"List failed"})}}},{method:"get",path:"/api/monitoring/quality/tool-metrics/summary",validate:{query:R},openapi:{summary:"My tool-quality quadrant summary",description:"Counts of my tools per action quadrant (boost/fix/disable/niche) for a window.",tags:["monitoring","tool-quality"]},handler:async(s,e)=>{const n=c(s);if(!n){e.status(401).json({error:"Unauthorized"});return}try{const t=s.query.window??"30d",r=(await m.search({index:y,body:{size:0,query:{bool:{filter:[{term:{owner:n}},{term:{window:t}}]}},aggs:{by_quadrant:{terms:{field:"quadrant",size:10}}}}})).aggregations?.by_quadrant?.buckets??[],a={boost:0,fix:0,disable:0,niche:0};for(const u of r)a[u.key]=u.doc_count;e.json({success:!0,window:t,counts:a,windows:d})}catch(t){if(t?.meta?.statusCode===404){e.json({success:!0,window:s.query.window??"30d",counts:{boost:0,fix:0,disable:0,niche:0},windows:d});return}console.error("[quality] summary failed:",t),e.status(500).json({error:"Summary failed"})}}},{method:"get",path:"/api/monitoring/quality/golden-questions",validate:{query:j},openapi:{summary:"List my golden questions",description:"Per-user mirror of /api/admin/golden-questions \u2014 always filtered to the caller's username. The `mine` write action is admin-only.",tags:["monitoring","tool-quality"]},handler:async(s,e)=>{const n=c(s);if(!n){e.status(401).json({error:"Unauthorized"});return}try{const t=s.query,i=[{term:{owner:n}}];t.tool&&i.push({term:{toolName:t.tool}}),t.system&&i.push({term:{system:t.system}});const r=await m.search({index:g,body:{query:{bool:{filter:i}},sort:[{toolName:{order:"asc"}},{rank:{order:"asc"}}],size:t.size??100,from:t.from??0}}),a=typeof r.hits?.total=="number"?r.hits.total:r.hits?.total?.value??0,u=(r.hits?.hits??[]).map(l=>l._source);e.json({success:!0,total:a,items:u})}catch(t){if(t?.meta?.statusCode===404){e.json({success:!0,total:0,items:[]});return}console.error("[quality] golden-questions list failed:",t),e.status(500).json({error:"List failed"})}}}];export{Q as routes};
@@ -0,0 +1 @@
1
+ import{randomUUID as R}from"crypto";import{z as s}from"zod";import{Client as S}from"@elastic/elasticsearch";import{getAuthUsername as k}from"../core/utils/ownership";import{ES_INDEX as v}from"./resources-routes";import{extractPagesFromText as T,extractPagesFromPdfBuffer as C,buildResourceDocs as E}from"../core/services/ingestion-service";import{createIngestionJob as A,completeIngestionJob as w,getIngestionJobById as P}from"../core/services/ingestion-jobs-service";import{extractTagsForChunks as D}from"../core/services/gliner-tagging-service";const _=30*1024*1024,F=new S({node:process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",auth:{username:process.env.ELASTICSEARCH_USER||"elastic",password:process.env.ELASTICSEARCH_PASSWORD||""},tls:{rejectUnauthorized:!1},requestTimeout:3e5,maxRetries:0}),B=s.object({content_base64:s.string().min(1).describe("Base64-encoded file content"),filename:s.string().min(1).describe("Original filename, used to detect the source type (.pdf/.txt/.md)"),title:s.string().min(1).describe("Human-readable title for the ingested resource"),use_case:s.string().optional().describe('Free-text use case, defaults to "knowledge base reference"'),tags:s.array(s.string()).optional().default([]),author:s.string().optional().default(""),version:s.string().optional().default(""),owner:s.string().optional().describe("Overrides the resource owner; defaults to the authenticated user"),auto_tag:s.boolean().optional().default(!0).describe("Auto-tag chunks via GLiNER zero-shot NER; set to false to skip")}),O=s.object({jobId:s.string().min(1)});function J(n){const e=n.toLowerCase();return e.endsWith(".pdf")?"pdf":e.endsWith(".txt")||e.endsWith(".md")?"text":null}async function M(n){const{jobId:e,owner:t,filename:g,title:u,sourceKind:d,buffer:l,useCase:m,tags:p,author:h,version:b,autoTag:f}=n;try{const r=d==="pdf"?await C(l):T(l.toString("utf-8")),i=r.reduce((a,o)=>a+o.length,0);i<20&&console.warn(`[resources-ingest] job ${e}: extracted text is nearly empty (${i} chars) \u2014 scanned PDFs without OCR are out of v1 scope`);const c=E(r,{title:u,owner:t,sourceFile:g,useCase:m,tags:p,author:h,version:b});if(c.length===0){await w(e,"error",{errorMessage:"No text could be extracted from the file"});return}if(f){const a=await D(c.map(o=>o.prompt_text));c.forEach((o,x)=>{const I=a[x]||[];I.length>0&&(o.tags=Array.from(new Set([...o.tags,...I])))})}const y=[];for(const a of c){const{_id:o,...x}=a;y.push({index:{_index:v,_id:o}}),y.push(x)}const j=await F.bulk({refresh:"wait_for",body:y});if(j.errors){const a=j.items.find(o=>o.index?.error)?.index?.error;throw new Error(`Bulk index had errors: ${JSON.stringify(a)}`)}await w(e,"success",{chunksCreated:c.length,resourceIds:c.map(a=>a._id)})}catch(r){console.error(`[resources-ingest] job ${e} failed:`,r.message),await w(e,"error",{errorMessage:r.message?.slice(0,1e3)})}}async function U(n,e){try{const t=n.body.owner||k(n)||"unknown",{content_base64:g,filename:u,title:d,use_case:l,tags:m,author:p,version:h,auto_tag:b}=n.body,f=J(u);if(!f){e.status(400).json({success:!1,error:"Unsupported file type \u2014 only .pdf, .txt and .md are accepted in v1"});return}let r;try{r=Buffer.from(g,"base64")}catch{e.status(400).json({success:!1,error:"content_base64 is not valid base64"});return}if(r.length===0){e.status(400).json({success:!1,error:"Decoded file content is empty"});return}if(r.length>_){e.status(400).json({success:!1,error:`File exceeds the ${_/(1024*1024)}MB limit`});return}const i=R();await A(i,t,{filename:u,title:d}),e.status(202).json({success:!0,jobId:i}),setImmediate(()=>{M({jobId:i,owner:t,filename:u,title:d,sourceKind:f,buffer:r,useCase:l,tags:m??[],author:p??"",version:h??"",autoTag:b??!0})})}catch(t){console.error("[resources-ingest] ingestFile error:",t.message),e.status(500).json({success:!1,error:t.message})}}async function H(n,e){try{const t=await P(String(n.params.jobId));if(!t){e.status(404).json({success:!1,error:"Ingestion job not found"});return}e.json({success:!0,job:t})}catch(t){console.error("[resources-ingest] getIngestJobStatus error:",t.message),e.status(500).json({success:!1,error:t.message})}}const V=[{method:"post",path:"/api/stack_expert/resources/ingest",handler:U,validate:{body:B},openapi:{summary:"Ingests a file (PDF/TXT/MD) into the knowledge base asynchronously.",description:"Accepts a base64-encoded PDF, TXT or MD file, chunks and embeds it in the background, and writes the resulting chunks into .stkxp_resources. Returns a jobId immediately (202); poll GET /api/stack_expert/resources/ingest/{jobId} for completion status.",tags:["resources"]}},{method:"get",path:"/api/stack_expert/resources/ingest/:jobId",handler:H,validate:{params:O},openapi:{summary:"Retrieves the status of a resource ingestion job.",description:"Returns the current status (running/success/error) of a file ingestion job started via POST /api/stack_expert/resources/ingest, including chunksCreated and resourceIds once completed.",tags:["resources"]}}];export{V as routes};
@@ -0,0 +1 @@
1
+ import{z as r}from"zod";import{Client as E}from"@elastic/elasticsearch";const I=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",C=process.env.ELASTICSEARCH_USER||"elastic",T=process.env.ELASTICSEARCH_PASSWORD||"",i=".stkxp_resources",b=".multilingual-e5-small-elasticsearch",c=new E({node:I,auth:{username:C,password:T},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3});async function z(){await c.indices.exists({index:i})||(await c.indices.create({index:i,body:{settings:{number_of_shards:1,number_of_replicas:0},mappings:{properties:{title:{type:"text",analyzer:"english",fields:{keyword:{type:"keyword"}}},description:{type:"text",analyzer:"english"},prompt_text:{type:"text",analyzer:"english"},use_case:{type:"text",analyzer:"english",fields:{keyword:{type:"keyword"}}},tags:{type:"keyword"},variables:{type:"keyword"},examples:{type:"text"},type:{type:"keyword"},owner:{type:"keyword"},author:{type:"keyword"},source_file:{type:"keyword"},page_no:{type:"integer"},chunk_idx:{type:"integer"},created_at:{type:"date"},updated_at:{type:"date"},semantic_content:{type:"text",analyzer:"english",copy_to:"semantic_content_vector"},semantic_content_vector:{type:"semantic_text",inference_id:b}}}}}),console.log(`[resources] Index ${i} created`))}async function D(){try{await c.indices.putMapping({index:i,body:{properties:{semantic_content:{type:"text",analyzer:"english",copy_to:"semantic_content_vector"},semantic_content_vector:{type:"semantic_text",inference_id:b}}}})}catch(e){console.error("[resources] ensureSemanticField error:",e.message)}}z().then(D).catch(e=>console.error("[resources] ensureIndex error:",e.message));const R=r.object({title:r.string().min(1),description:r.string().optional().default(""),prompt_text:r.string().optional().default(""),use_case:r.string().optional().default(""),type:r.enum(["live_data","knowledge_base","configuration"]).default("knowledge_base"),tags:r.array(r.string()).optional().default([]),variables:r.array(r.string()).optional().default([]),examples:r.string().optional().default(""),owner:r.string().optional()}),A=R.partial(),F=r.object({q:r.string().min(1),size:r.number().int().min(1).max(50).optional().default(10),type:r.enum(["live_data","knowledge_base","configuration"]).optional(),tags:r.array(r.string()).optional(),owner:r.string().optional()});function S(e){return[e.title,e.description,e.prompt_text].filter(Boolean).join(" ").trim()}function P(e){const{owner:s,owners:t}=e.query;if(t){const o=t.split(",").map(a=>a.trim()).filter(Boolean);if(o.length>0)return[{bool:{should:o.map(a=>({term:{owner:a}})),minimum_should_match:1}}]}return s?[{term:{owner:s}}]:[]}async function O(e,s){try{const{search:t,type:o,types:a,notType:p,tag:y,sourceFile:u,limit:f="100",from:_="0"}=e.query,n=[],l=[];if(t&&n.push({multi_match:{query:t,fields:["title^3","tags^2","use_case^2","description","prompt_text"]}}),o&&n.push({term:{type:o}}),a){const d=String(a).split(",").map(g=>g.trim()).filter(Boolean);d.length>0&&n.push({terms:{type:d}})}if(p){const d=String(p).split(",").map(g=>g.trim()).filter(Boolean);for(const g of d)l.push({term:{type:g}})}y&&n.push({term:{tags:y}}),u&&n.push({wildcard:{source_file:{value:`*${u}*`,case_insensitive:!0}}}),n.push(...P(e));const m={title:"title.keyword",createdAt:"created_at",source_file:"source_file"},v=String(e.query.sortField||"title"),x=m[v]||"title.keyword",w=e.query.sortOrder==="desc"?"desc":"asc",k=x==="created_at"?[{created_at:{order:w,unmapped_type:"date"}}]:[{[x]:{order:w}}],j=n.length>0||l.length>0,h=await c.search({index:i,body:{query:j?{bool:{...n.length?{must:n}:{},...l.length?{must_not:l}:{}}}:{match_all:{}},size:parseInt(f),from:parseInt(_),sort:k,_source:{excludes:["semantic_content"]}}}),q=h.hits.hits.map(d=>({id:d._id,...d._source}));s.json({success:!0,total:typeof h.hits.total=="object"?h.hits.total.value:h.hits.total,resources:q})}catch(t){console.error("[resources] listResources error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function B(e,s){try{const t=await c.get({index:i,id:e.params.id});s.json({success:!0,resource:{id:t._id,...t._source}})}catch(t){if(t.meta?.statusCode===404){s.status(404).json({success:!1,error:"Resource not found"});return}console.error("[resources] getResource error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function M(e,s){try{const t=new Date().toISOString(),o={...e.body,semantic_content:S(e.body),created_at:t,updated_at:t},a=await c.index({index:i,body:o,refresh:"wait_for"});s.status(201).json({success:!0,id:a._id,resource:{id:a._id,...o}})}catch(t){console.error("[resources] createResource error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function U(e,s){try{const t={...e.body,updated_at:new Date().toISOString()};if(e.body.title||e.body.description||e.body.prompt_text){const a={...(await c.get({index:i,id:e.params.id}))._source,...t};t.semantic_content=S(a)}await c.update({index:i,id:e.params.id,body:{doc:t},refresh:"wait_for"}),s.json({success:!0,id:e.params.id})}catch(t){if(t.meta?.statusCode===404){s.status(404).json({success:!1,error:"Resource not found"});return}console.error("[resources] updateResource error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function H(e,s){try{await c.delete({index:i,id:e.params.id,refresh:"wait_for"}),s.json({success:!0})}catch(t){if(t.meta?.statusCode===404){s.status(404).json({success:!1,error:"Resource not found"});return}console.error("[resources] deleteResource error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function N(e,s){try{const{q:t,size:o=10,type:a,tags:p,owner:y}=e.body,u=[];a&&u.push({term:{type:a}}),p?.length&&u.push({terms:{tags:p}}),y&&u.push({term:{owner:y}});const _={query:{bool:{must:[{multi_match:{query:t,fields:["title^3","tags^2","use_case^2","description^2","prompt_text","semantic_content"],type:"best_fields",fuzziness:"AUTO"}}],...u.length?{filter:u}:{}}},size:o,_source:{excludes:["semantic_content"]}},n=await c.search({index:i,body:_}),l=n.hits.hits.map(m=>({id:m._id,_score:m._score,...m._source}));s.json({success:!0,total:typeof n.hits.total=="object"?n.hits.total.value:n.hits.total,resources:l})}catch(t){console.error("[resources] searchResources error:",t.message),s.status(500).json({success:!1,error:t.message})}}async function J(e){if(!e.owner||!e.query)return[];const s=[{term:{owner:e.owner}}];e.type&&s.push({term:{type:e.type}});try{return((await c.search({index:i,body:{size:e.k??5,retriever:{rrf:{retrievers:[{standard:{query:{bool:{must:[{semantic:{field:"semantic_content_vector",query:e.query}}],filter:s}}}},{standard:{query:{bool:{must:[{multi_match:{query:e.query,fields:["title^3","tags^2","use_case^2","description^2","prompt_text"]}}],filter:s}}}}],rank_window_size:50,rank_constant:20}},_source:{excludes:["semantic_content","semantic_content_vector"]}}})).hits?.hits??[]).map(o=>({id:o._id,...o._source,score:typeof o._score=="number"?o._score:void 0}))}catch(t){return console.error("[resources] searchResourcesRRF failed:",t.message),[]}}const K=[{method:"get",path:"/api/stack_expert/resources",handler:O,openapi:{summary:"Lists resources with optional BM25 search and filters. Supports pagination and sorting.",description:"This endpoint allows clients to retrieve a list of resources from the Stack Expert system. It supports optional search queries, filtering by type and tags, pagination, and sorting. The search functionality uses BM25 ranking to provide relevant results based on the query.",tags:["resources"]}},{method:"get",path:"/api/stack_expert/resources/:id",handler:B,validate:{params:r.object({id:r.string()})},openapi:{summary:"Retrieves a specific resource by ID.",description:"This endpoint allows clients to retrieve a single resource from the Stack Expert system by its unique ID.",tags:["resources"]}},{method:"post",path:"/api/stack_expert/resources",handler:M,validate:{body:R},openapi:{summary:"Creates a new resource.",description:"This endpoint allows clients to create a new resource in the Stack Expert system.",tags:["resources"]}},{method:"put",path:"/api/stack_expert/resources/:id",handler:U,validate:{params:r.object({id:r.string()}),body:A},openapi:{summary:"Updates an existing resource by ID.",description:"This endpoint allows clients to update an existing resource in the Stack Expert system by its unique ID.",tags:["resources"]}},{method:"delete",path:"/api/stack_expert/resources/:id",handler:H,validate:{params:r.object({id:r.string()})},openapi:{summary:"Deletes a resource by ID.",description:"This endpoint allows clients to delete a resource from the Stack Expert system by its unique ID.",tags:["resources"]}},{method:"post",path:"/api/stack_expert/resources/search",handler:N,validate:{body:F},openapi:{summary:"Searches resources using BM25 ranking.",description:"This endpoint allows clients to search for resources in the Stack Expert system using a query string. It supports optional filtering by resource type and tags, and returns results ranked by relevance using the BM25 algorithm.",tags:["resources"]}}];export{i as ES_INDEX,b as SEMANTIC_INFERENCE_ID,c as esClient,K as routes,J as searchResourcesRRF};
@@ -0,0 +1 @@
1
+ import r from"fs";import m from"path";const l="/root/stkxp-mcp-server/src/schema";async function h(n,s){try{const{toolName:e}=n.params;if(!e){s.status(400).json({success:!1,error:"Tool name is required"});return}const t=e.split("_"),a=t[0],c=t.slice(1).join("_")+".ts",o=m.join(l,a,c);if(console.log(`[Schema] Resolving tool: ${e}`),console.log(`[Schema] Category: ${a}`),console.log(`[Schema] Schema file: ${c}`),console.log(`[Schema] Full path: ${o}`),!r.existsSync(o)){s.status(404).json({success:!1,error:"Schema file not found",path:o});return}const i=r.readFileSync(o,"utf-8");s.json({success:!0,toolName:e,schemaPath:o,content:i})}catch(e){console.error("Error reading schema file:",e),s.status(500).json({success:!1,error:"Failed to read schema file",message:e.message})}}const d=[{method:"get",path:"/api/schema/:toolName",handler:h}];export{d as routes};
@@ -0,0 +1 @@
1
+ import r from"zod";import w from"https";import x from"fs";import{ElasticsearchWrapper as C}from"../core/services/elasticsearch-wrapper";import{config as S}from"../core/config";import b from"slugify";const h={lower:!0,strict:!0},k=new C(S.elasticsearch,!0),j=async function(t){const e=new URL(t.api_url),a=e.hostname,s=e.port||"443",o=e.pathname||"",d="ApiKey "+t.api_key;var n={timeout:5e3,hostname:a,port:s,path:`${o}`,method:"GET",headers:{Authorization:d,"Content-Type":"application/json"},strictSSL:!1,cert:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.crt"),key:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.key"),rejectUnauthorized:!1};console.log("checkCluster",n);try{return await new Promise((_,l)=>{const i=w.request(n,y=>{let g="";y.on("data",p=>{g+=p}),i.on("socket",function(p){p.setTimeout(n.timeout),p.on("timeout",function(){i.abort()})}),y.on("end",()=>{if(console.log("end",g),y.statusCode===200)_(JSON.parse(g));else{let p=JSON.parse(g);_(p)}})});i.on("timeout",function(){console.log("timeout! "+n.timeout/1e3+" seconds expired"),i.destroy()}),i.on("error",y=>{_({error:y})}),i.end()})}catch(v){return v}},O=async(t,e)=>{console.log("makePolicy",t,e);const a=Math.random().toString(36).slice(2),s={id:b(t.cluster_name+"_"+t.cluster_uuid+"_"+t.version.number+"_"+a,h).split("-").join("_"),name:"Erretegia "+b(t.cluster_name+"_"+t.cluster_uuid+"_"+t.version.number+"_"+a,h).split("-").join("_")+" policy",description:JSON.stringify(t),namespace:b(t.cluster_name+"_"+t.cluster_uuid+"_"+a,h).split("-").join("_")+"",policy_id:"fleet-server-policy",enabled:!0,policy_ids:["fleet-server-policy"],package:{name:"stack_expert",title:"StackExpert",version:"0.0.99"},inputs:[{type:"cel",policy_template:"check",enabled:!0,streams:[{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.health_report"},vars:{interval:{value:"10m",type:"text"},tags:{value:["health_report","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_ilm"},vars:{interval:{type:"text",value:"10m"},tags:{value:["indices_ilm","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_stats"},vars:{interval:{type:"text",value:"10m"},tags:{value:["indices_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_hot_threads"},vars:{interval:{type:"text",value:"10m"},tags:{value:["nodes_hot_threads","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.slm"},vars:{interval:{value:"10m",type:"text"},tags:{value:["slm","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.tasks_list"},vars:{interval:{type:"text",value:"10m"},tags:{value:["tasks_list","stack_expert"],type:"text"}}}],vars:{api_url:{type:"text",value:e.api_url},api_key:{type:"text",value:e.api_key},interval:{value:e.interval,type:"text"},ssl:{value:e.ssl,type:"yaml"},condition:{type:"text"},scope:{value:"node",type:"select"}}},{type:"httpjson",policy_template:"check",enabled:!0,streams:[{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cat_shards"},vars:{interval:{type:"text",value:"10m"},tags:{value:["cat_shards","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cat_thread_pool"},vars:{interval:{type:"text",value:"10m"},tags:{value:["cat_thread_pool","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cluster_get_settings"},vars:{interval:{value:"10m",type:"text"},tags:{value:["cluster_get_settings","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cluster_stats"},vars:{interval:{type:"text",value:"10m"},tags:{value:["cluster_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.data_streams_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["data_streams_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.ilm"},vars:{interval:{value:"10m",type:"text"},tags:{value:["ilm","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_get_settings"},vars:{interval:{type:"text",value:"10m"},tags:{value:["indices_get_settings","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.info"},vars:{interval:{type:"text",value:"10m"},tags:{value:["info","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.ingest_pipelines"},vars:{interval:{value:"10m",type:"text"},tags:{value:["ingest_pipelines","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.license_get"},vars:{interval:{value:"10m",type:"text"},tags:{value:["license_get","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_info"},vars:{interval:{type:"text",value:"10m"},tags:{value:["nodes_info","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_stats"},vars:{interval:{type:"text",value:"10m"},tags:{value:["nodes_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_usage"},vars:{interval:{value:"10m",type:"text"},tags:{value:["nodes_usage","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.xpack_info"},vars:{interval:{value:"10m",type:"text"},tags:{value:["xpack_info","stack_expert"],type:"text"}}}],vars:{api_url:{type:"text",value:e.api_url},interval:{value:e.interval,type:"text"},ssl:{value:e.ssl,type:"yaml"},condition:{type:"text"},scope:{value:"node",type:"select"},api_key:{type:"text",value:e.api_key}}}]};return console.log("makePolicy policy",s),s},R=[{method:"post",path:"/api/stack_expert/settings/connect",validate:{body:r.object({api_url:r.string().min(1),api_key:r.string().min(1),interval:r.string().min(1),ssl:r.string().min(1)})},handler:async(t,e)=>{try{const a=await j(t.body);return console.log("clusterChecked",a),e.json({body:a})}catch(a){return console.log("error",a),e.json({body:a})}}},{method:"post",path:"/api/stack_expert/settings/accounts",validate:{},handler:async(t,e)=>{try{const a=await k.security.getUser();return e.json({body:{time:new Date().toISOString(),result:a||[]}})}catch(a){return console.log("getUser error",a),e.json({body:{time:new Date().toISOString(),result:a||[]}})}}},{method:"post",path:"/api/stack_expert/settings/accounts/signup",validate:{body:r.object({username:r.string().min(1),password:r.string().min(1),email:r.string().min(1),full_name:r.string().min(1)})},handler:async(t,e)=>{console.log(t.body);const a=t.body.username,s=t.body.password,o=t.body.email,d=t.body.full_name,n=b(a,h).split("-").join("_"),v=[n],_=t.body.metadata;let l=null;try{return console.log("role",{role_name:n}),l=await k.security.getRole({role_name:n}),console.log("role exists",{role_name:n}),e.json({body:{time:new Date().toISOString(),result:l}})}catch{console.log("role not exists",{role_name:n});try{var i={name:n,cluster:[],indices:[{names:["logs-stack_expert.*-"+n+"_*"],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:[{application:"kibana-.kibana",privileges:["feature_dev_tools.read"],resources:["space:plugin"]}],run_as:[],metadata:{},transient_metadata:{enabled:!0}};console.log("role",i),l=await k.security.putRole(i),console.log("putRole",l);try{return l=await k.security.getUser({username:a}),console.log("user exists",{username:a}),e.json({body:{time:new Date().toISOString(),result:l}})}catch{console.log("user not exists",{username:a});try{return l=await k.security.putUser({username:a,password:s,roles:v,metadata:_}),console.log("putUser",l),e.json({body:{time:new Date().toISOString(),result:l}})}catch(p){return console.log("putUser error",p),e.json({body:{time:new Date().toISOString(),result:p}})}}}catch(g){return console.log("erreur",g),e.json({body:{time:new Date().toISOString(),result:g}})}}}},{method:"post",path:"/api/stack_expert/settings/account/link",validate:{body:r.object({namespace:r.string().min(1)})},handler:async(t,e)=>{const a=new Client({...S.elasticsearch});try{const s=await a.security.getRole({name:t.auth.username});console.log("rolesResponse",s);const o=s[t.auth.username],d={name:t.auth.username,cluster:o.cluster||[],indices:[...o.indices,{names:["*"+t.body.namespace+"*"],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:o.applications||[],run_as:o.run_as||[],metadata:o.metadata||{},transient_metadata:o.transient_metadata||{enabled:!0}};console.log("role",JSON.stringify(d));const n=await a.security.putRole(d);return console.log("putRole",n),e.json({body:{time:new Date().toISOString(),result:n}})}catch(s){return console.log("error",s),e.json({body:{time:new Date().toISOString(),result:s}})}}},{method:"post",path:"/api/stack_expert/settings/account/role",validate:{body:r.object({namespace:r.string().min(1)})},handler:async(t,e)=>{const a=new Client({...S.elasticsearch});try{var s={name:t.body.namespace,cluster:[],indices:[{names:["*"+t.body.namespace+"*"],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:[{application:"kibana-.kibana",privileges:["feature_dev_tools.read"],resources:["space:default"]}],run_as:[],metadata:{},transient_metadata:{enabled:!0}};console.log("role",s);const o=await a.security.putRole(s);return console.log("putRole",o),e.json({body:{time:new Date().toISOString(),result:o}})}catch(o){return console.log("erreur",o),e.json({body:{time:new Date().toISOString(),result:o}})}}},{method:"post",path:"/api/stack_expert/settings/accounts/update",validate:{body:r.object({username:r.string().min(1),password:r.string().min(1),email:r.string().min(1),full_name:r.string().min(1),metadata:r.object({namespace:r.string().min(1)})})},handler:async(t,e)=>{console.log(t.body);const a=t.body.username,s=t.body.password,o=t.body.email,d=t.body.full_name,n=["plugin"],v=t.body.metadata;let _=null;try{_=await k.security.getUser({username:a});try{return _=await k.security.putUser({username:a,password:s,roles:n,metadata:v}),e.json({body:{time:new Date().toISOString(),result:_}})}catch(l){return console.log(l),e.json({body:{time:new Date().toISOString(),result:l}})}}catch{return e.json({body:{time:new Date().toISOString(),result:_}})}}},{method:"post",path:"/api/stack_expert/settings/policies/check",validate:{body:r.object({api_url:r.string().min(1),api_key:r.string().min(1),interval:r.string().min(1),ssl:r.string().min(1)})},handler:async(t,e)=>{try{const o=await k.transport.request({method:"GET",path:"/_security/_authenticate"}),d=await j(t.body);if(d.cluster_uuid&&d.cluster_name){var a={id:b(t.body.api_key+"_"+d.cluster_name+"_"+d.cluster_uuid,h).split("-").join("_")};console.log("policy check",a.id);const n="51.158.76.76",v=5602,_="/ec",l="Basic "+Buffer.from("elastic:diagnostics").toString("base64");var s={hostname:n,port:v,path:`${_}/api/fleet/package_policies/${a.id}`,method:"GET",headers:{Authorization:l,"kbn-xsrf":"reporting","Content-Type":"application/json"},cert:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.crt"),key:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.key"),ca:x.readFileSync("/root/docker/diagnostics/certificates/ca/ca.crt"),rejectUnauthorized:!1};console.log("options",s);try{return await new Promise((y,g)=>{const p=w.request(s,c=>{let u="";c.on("data",m=>{u+=m}),c.on("end",()=>{if(c.statusCode===200)y(e.json({body:JSON.parse(u)}));else{console.log("error___________",c,u);let m=JSON.parse(u);console.log(m),y(e.json({statusCode:m.statusCode||500,body:m}))}})});p.on("error",c=>{console.log("errorxxxx",c),g(e.json({body:c}))}),p.end()})}catch(i){return console.log("error",i),i}}else return e.json({body:d})}catch(o){return console.log("error",o),e.json({body:o})}}},{method:"post",path:"/api/stack_expert/settings/policies/prepare",validate:{body:r.object({api_url:r.string().min(1),api_key:r.string().min(1),interval:r.string().min(1),ssl:r.string().min(1)})},handler:async(t,e)=>{try{const a=await k.transport.request({method:"GET",path:"/_security/_authenticate"});console.log("settings/policies/prepare",a,t.body);const s=await j(t.body);if(s.cluster_uuid&&s.cluster_name){const o=await O(s,t.body);return console.log("policy prepare----",o),e.json({body:o})}else return e.json({body:s})}catch(a){return console.log("error",a),e.json({body:a})}}},{method:"post",path:"/api/stack_expert/settings/policies",validate:{body:r.object({api_url:r.string().min(1),api_key:r.string().min(1),interval:r.string().min(1),ssl:r.string().min(1)})},handler:async(t,e)=>{try{const s=await k.transport.request({method:"GET",path:"/_security/_authenticate"});console.log("settings/policies",t.body);const o=await j(t.body);if(o.cluster_uuid&&o.cluster_name){const d=await O(o,t.body),n=JSON.stringify(d);console.log("postData",n);const v="https:",_="51.158.76.76",l=`Basic ${btoa("elastic:diagnostics")}`;var a={hostname:_,port:5602,strictSSL:!1,path:"/ec/api/fleet/package_policies",method:"POST",headers:{Authorization:l,"kbn-xsrf":"reporting","Content-Type":"application/json","Content-Length":Buffer.byteLength(n)},cert:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.crt"),key:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.key"),ca:x.readFileSync("/root/docker/diagnostics/certificates/ca/ca.crt"),rejectUnauthorized:!1};console.log("options",a);try{return await new Promise((y,g)=>{const p=w.request(a,c=>{let u="";c.on("data",m=>{u+=m}),c.on("end",()=>{c.statusCode===200?y(e.json({body:JSON.parse(u)})):(console.log("error",c,u),y(e.json({body:u})))})});p.on("error",c=>{console.log("error",c),g(e.json({body:c}))}),p.write(n),p.end()})}catch(i){return console.log("error",i),i}}else return e.json({body:o})}catch(s){return console.log("error",s),e.json({body:s})}}},{method:"put",path:"/api/stack_expert/settings/policies",validate:{body:r.object({api_url:r.string().min(1),api_key:r.string().min(1),interval:r.string().min(1),ssl:r.string().min(1)})},handler:async(t,e)=>{try{const s=await j(t.body);if(s.cluster_uuid&&s.cluster_name){console.log("clusterChecked",s.cluster_name);let o=b(t.body.api_key+"_"+s.cluster_name+"_"+s.cluster_uuid,h).split("-").join("_"),d={name:"Erretegia "+b(s.cluster_name+"_"+s.cluster_uuid,h).split("-").join("_")+" policy by "+t.body.api_key,description:JSON.stringify(s),namespace:o,policy_id:"fleet-server-policy",enabled:!0,inputs:[{type:"cel",policy_template:"check",enabled:!0,streams:[{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_hot_threads"},vars:{interval:{value:"10m",type:"text"},tags:{value:["nodes_hot_threads","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.tasks_list"},vars:{interval:{value:"10m",type:"text"},tags:{value:["tasks_list","stack_expert"],type:"text"}}}]},{type:"httpjson",policy_template:"check",enabled:!0,streams:[{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cat_shards"},vars:{interval:{value:"10m",type:"text"},tags:{value:["cat_shards","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cluster_get_settings"},vars:{interval:{value:"10m",type:"text"},tags:{value:["cluster_get_settings","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.cluster_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["cluster_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.health_report"},vars:{interval:{value:"10m",type:"text"},tags:{value:["health_report","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.ilm_policies"},vars:{interval:{value:"10m",type:"text"},tags:{value:["ilm_policies","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_get_settings"},vars:{interval:{value:"10m",type:"text"},tags:{value:["indices_get_settings","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_segments"},vars:{interval:{value:"10m",type:"text"},tags:{value:["indices_segments","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.indices_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["indices_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.ingest_pipelines"},vars:{interval:{value:"10m",type:"text"},tags:{value:["ingest_pipelines","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.license_get"},vars:{interval:{value:"10m",type:"text"},tags:{value:["license_get","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.mappings"},vars:{interval:{value:"10m",type:"text"},tags:{value:["mappings","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_info"},vars:{interval:{value:"10m",type:"text"},tags:{value:["nodes_info","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["nodes_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.nodes_usage"},vars:{interval:{value:"10m",type:"text"},tags:{value:["nodes_usage","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.slm_get_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["slm_get_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.slm_get_status"},vars:{interval:{value:"10m",type:"text"},tags:{value:["slm_get_status","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.snapshot_status"},vars:{interval:{value:"10m",type:"text"},tags:{value:["snapshot_status","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.tasks_list"},vars:{interval:{value:"10m",type:"text"},tags:{value:["tasks_list","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.watcher_stats"},vars:{interval:{value:"10m",type:"text"},tags:{value:["watcher_stats","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.xpack_info"},vars:{interval:{value:"10m",type:"text"},tags:{value:["xpack_info","stack_expert"],type:"text"}}},{enabled:!0,data_stream:{type:"logs",dataset:"stack_expert.xpack_usage"},vars:{interval:{value:"10m",type:"text"},tags:{value:["xpack_usage","stack_expert"],type:"text"}}}],vars:{api_url:{value:[t.body.api_url],type:"text"},api_key:{value:t.body.api_key,type:"text"},interval:{value:t.body.interval,type:"text"},ssl:{value:t.body.ssl,type:"yaml"},condition:{type:"text"},scope:{value:"cluster",type:"select"}}}],package:{name:"stack_expert",title:"Erretegia",version:"0.0.81"},force:!0};console.log("policy policies",d);const n=JSON.stringify(d),v="51.158.76.76",_=5602,l="/ec",i="Basic "+Buffer.from("elastic:diagnostics").toString("base64");var a={hostname:v,port:_,path:`${l}/api/fleet/package_policies/`+o,method:"PUT",headers:{Authorization:i,"kbn-xsrf":"reporting","Content-Type":"application/json","Content-Length":Buffer.byteLength(n)},cert:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.crt"),key:x.readFileSync("/root/docker/diagnostics/certificates/kib002/kib002.key"),ca:x.readFileSync("/root/docker/diagnostics/certificates/ca/ca.crt"),rejectUnauthorized:!1};try{return await new Promise((g,p)=>{const c=w.request(a,u=>{let m="";u.on("data",f=>{m+=f}),u.on("end",()=>{if(u.statusCode===200)console.log(m),g(e.json({body:JSON.parse(m)}));else{console.log("error___________",u,m);let f=JSON.parse(m);console.log(f),g(e.json({statusCode:f.statusCode||500,body:f}))}})});c.on("error",u=>{console.log("errorxxxx",u),p(e.json({body:u}))}),c.write(n),c.end()})}catch(y){return console.log("error",y),y}}else return e.json({body:s})}catch(s){return console.log("error",s),e.json({body:s})}}}];export{R as routes};
@@ -0,0 +1 @@
1
+ import{ElasticsearchWrapper as O}from"../core/services/elasticsearch-wrapper";import{config as G}from"../core/config";import{createSlug as $,createNamespace as x}from"../core/utils/string-helpers";import{ClusterService as K}from"../services/cluster-service";import{KibanaService as M}from"../core/services/kibana-service";import{MultiKibanaService as V}from"../core/services/multi-kibana-service";import{KibanaClientFactory as _}from"../core/services/kibana-client-factory";import{PolicyFactory as z}from"../core/services/policy-factory";import{PackagePolicyService as H}from"../core/services/package-policy-service";import{getPlatformById as L,getPlatforms as D}from"../core/services/platforms-service";import{connectionSchema as T,flexibleConnectionSchema as B,accountSignupSchema as Y,accountUpdateSchema as J,namespaceSchema as R}from"../core/types/settings";const E=new O(G.elasticsearch,!0),W=new K,P=new M,N=new V,U=new z,A=new H;class Q{async handleGetAccounts(r,t){try{const e=await E.security.getUser();return t.json({body:{time:new Date().toISOString(),result:e||[]}})}catch(e){return console.error("Get accounts error:",e),t.json({body:{time:new Date().toISOString(),result:e||[]}})}}async handleAccountSignup(r,t){try{const e=r.body,{username:n,password:a,metadata:o}=e,s=$(n),l=[s];try{const m=await E.security.getRole({name:s});return console.log("Role exists:",s),t.json({body:{time:new Date().toISOString(),result:m}})}catch{console.log("Creating role:",s);const y={cluster:[],indices:[{names:[`logs-stack_expert.*-${s}_*`],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:[{application:"kibana-.kibana",privileges:["feature_dev_tools.read"],resources:["space:plugin"]}],run_as:[],metadata:{},transient_metadata:{enabled:!0}};await E.security.putRole({name:s,body:y}),console.log("Role created:",s);try{const i=await E.security.getUser({username:n});return console.log("User exists:",n),t.json({body:{time:new Date().toISOString(),result:i}})}catch{console.log("Creating user:",n);const c=await E.security.putUser({username:n,password:a,roles:l,metadata:o});return console.log("User created:",n),t.json({body:{time:new Date().toISOString(),result:c}})}}}catch(e){return console.error("Signup error:",e),t.json({body:{time:new Date().toISOString(),result:e}})}}async handleAccountLink(r,t){try{const{namespace:e}=r.body,n=r.auth.username;let a;try{a=(await E.security.getRole({name:n}))[n]}catch(l){if(l.statusCode===404||l.meta?.statusCode===404)console.log(`Role ${n} not found, creating minimal role structure`),a={cluster:[],indices:[],applications:[],run_as:[],metadata:{},transient_metadata:{enabled:!0}};else throw l}const o={cluster:a.cluster||[],indices:[...a.indices||[],{names:[`*${e}*`],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:a.applications||[],run_as:a.run_as||[],metadata:a.metadata||{},transient_metadata:a.transient_metadata||{enabled:!0}},s=await E.security.putRole({name:n,body:o});return t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){return console.error("Account link error:",e),t.json({body:{time:new Date().toISOString(),result:e}})}}async handleCreateRole(r,t){try{const{namespace:e}=r.body,n={cluster:[],indices:[{names:[`*${e}*`],privileges:["all"],field_security:{grant:["*"]},allow_restricted_indices:!1}],applications:[{application:"kibana-.kibana",privileges:["feature_dev_tools.read"],resources:["space:default"]}],run_as:[],metadata:{},transient_metadata:{enabled:!0}},a=await E.security.putRole({name:e,body:n});return t.json({body:{time:new Date().toISOString(),result:a}})}catch(e){return console.error("Create role error:",e),t.json({body:{time:new Date().toISOString(),result:e}})}}async handleAccountUpdate(r,t){try{const{username:e,password:n,metadata:a}=r.body,o=["plugin"];await E.security.getUser({username:e});const s=await E.security.putUser({username:e,password:n,roles:o,metadata:a});return t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){return console.error("Account update error:",e),t.json({body:{time:new Date().toISOString(),result:e}})}}async handlePolicyCheck(r,t){try{const e=r.body;await E.transport.request({method:"GET",path:"/_security/_authenticate"});const n=await W.checkCluster(e);if("error"in n)return t.json({body:n});if(n.cluster_uuid&&n.cluster_name){const a=e.api_key||`basicauth_${Date.now()}`,o=$(`${a}_${n.cluster_name}_${n.cluster_uuid}`),s=await P.checkPolicy(o);return s.error?t.json({statusCode:s.statusCode||500,body:s.error}):t.json({body:s.data})}else return t.json({body:n})}catch(e){return console.error("Policy check error:",e),t.json({body:e})}}async handlePolicyCreate(r,t){try{const e=r.body,n=r.auth.username;await E.transport.request({method:"GET",path:"/_security/_authenticate"});const a=e.packageName||e.package?.config?.packageName||e.package?.name,o=e.packageVersion||e.package?.config?.packageVersion||e.package?.version;console.log(`[CREATE] Creating policy for package: ${a} v${o}`);const s=await P.getEpmPackage(a,o);console.log("[CREATE] Package info:",s);const l={cluster_name:a||"unknown",cluster_uuid:"generic-"+Date.now(),version:{number:"1.0.0"},serviceType:a||"generic"};console.log("[CREATE] Cluster info (no connection test):",l);const m=U.createPolicy(l,e);console.log("[CREATE] Creating policy:",JSON.stringify(m,null,2));const y=await P.createPolicy(m);return console.log("[CREATE] Policy create result:",y),y.error?t.json({statusCode:y.statusCode||500,body:y.error}):t.json({body:y.data})}catch(e){return console.error("[CREATE] Policy create error:",e),t.json({body:e})}}async handlePolicyUpdate(r,t){try{const e=r.body,n=r.auth.username,a=e.packageName||e.package?.config?.packageName||e.package?.name,o=e.packageVersion||e.package?.config?.packageVersion||e.package?.version;console.log(`[UPDATE] Updating policy for package: ${a} v${o}`);const s={cluster_name:a||"unknown",cluster_uuid:"generic-"+Date.now(),version:{number:"1.0.0"},serviceType:a||"generic"};console.log("[UPDATE] Cluster info (no connection test):",s);let l=e.api_key||e.vars?.api_key;if(!l&&e.inputs){for(const c of Object.values(e.inputs))if(c.enabled&&c.vars?.api_key){l=c.vars.api_key;break}}const m=x(l,s.cluster_name,s.cluster_uuid),y=U.createMinimalPolicy(s,e,l),i=await P.updatePolicy(m,y);return i.error?t.json({statusCode:i.statusCode||500,body:i.error}):t.json({body:i.data})}catch(e){return console.error("[UPDATE] Policy update error:",e),t.json({body:e})}}async handleGetEpmPackages(r,t){try{const{prerelease:e,search:n,category:a,from:o="0",size:s="50",platformId:l,status:m}=r.query||{},y=e==="true",i=r.auth?.username;let c=[];if(l&&i){const k=await L(l,i,!0);if(!k)return t.json({statusCode:404,body:{error:`Platform ${l} not found`}});const u=await _.createFromPlatform(k).getEpmPackages(y);if(u.error)return t.json({statusCode:u.statusCode||500,body:u.error});c=u.data?.items||u.items||[]}else{if(!i)return t.status(401).json({body:{error:"Unauthorized"}});const{platforms:k}=await D(i,void 0,void 0,1,1e3),f=k.filter(u=>u.enabled&&u.type==="ElasticStack");for(const u of f)try{const b=await _.createFromPlatform(u).getEpmPackages(y);if(!b.error&&!(b.statusCode&&b.statusCode>=400)){c=b.data?.items||b.items||[],console.log(`[EPM] Got ${c.length} packages from platform: ${u.name}`);break}}catch(p){console.warn(`[EPM] Platform ${u.name} failed: ${p.message}`)}if(c.length===0&&f.length===0)return t.json({statusCode:404,body:{error:"No enabled ElasticStack platform found for this user"}})}if(m&&(c=c.filter(k=>k.status===m)),n){const k=n.toLowerCase();c=c.filter(f=>{const u=f.title?.toLowerCase().includes(k),p=f.name?.toLowerCase().includes(k),b=f.description?.toLowerCase().includes(k),v=f.categories?.some(j=>j.toLowerCase().includes(k));return u||p||b||v})}a&&(c=c.filter(k=>k.categories?.includes(a)));const d=parseInt(o),h=parseInt(s),C=c.length,S=c.slice(d,d+h);return t.json({body:{response:S,total:C,from:d,size:S.length}})}catch(e){return console.error("Get EPM packages error:",e),t.json({body:e})}}async handleGetEpmPackage(r,t){try{const{packageName:e,packageVersion:n}=r.params||{},a=r.auth?.username,o=await P.getEpmPackage(e,n);if(!(o.error||o.statusCode&&o.statusCode>=400))return t.json({body:o.data});if(console.warn(`[EPM] Default Kibana failed for ${e}/${n} (${o.statusCode}), trying registered platforms`),a){const{platforms:l}=await D(a,void 0,void 0,1,1e3),m=l.filter(y=>y.enabled&&y.type==="ElasticStack");for(const y of m)try{const c=await _.createFromPlatform(y).getEpmPackage(e,n);if(!c.error&&!(c.statusCode&&c.statusCode>=400))return console.log(`[EPM] Got package ${e}/${n} from platform: ${y.name}`),t.json({body:c.data})}catch(i){console.warn(`[EPM] Platform ${y.name} failed for ${e}/${n}: ${i.message}`)}}return t.json({statusCode:o.statusCode||500,body:o.error})}catch(e){return console.error("Get EPM package error:",e),t.json({body:e})}}async handleGetEnrolledPackages(r,t){try{const e=r.auth?.username,{search:n,packageName:a,status:o,from:s="0",size:l="50",aggregate:m="true"}=r.query||{};if(!e)return t.status(401).json({statusCode:401,body:{error:"Unauthorized - no username found"}});const y=m==="true";if(console.log(`[ENROLLED] Fetching enrolled packages for user: ${e} (aggregate: ${y})`),y){console.log("[ENROLLED] Using multi-platform mode");try{const f=await N.getAllPackagesFromAllPlatforms(e,!1);console.log(JSON.stringify(f,null,2));let u=f.packages;const p=f.errors;if(console.log(`[ENROLLED] Multi-platform fetch: ${u.length} packages, ${p.length} platform errors`),u=u.filter(w=>w.platforms.some(I=>I.status==="enrolled")),console.log(`[ENROLLED] Filtered to ${u.length} enrolled packages`),n){const w=n.toLowerCase();u=u.filter(I=>I.name?.toLowerCase().includes(w)||I.title?.toLowerCase().includes(w)||I.description?.toLowerCase().includes(w))}a&&(u=u.filter(w=>w.name===a)),o&&(u=u.filter(w=>w.platforms.some(I=>I.status===o)));const b=parseInt(s),v=parseInt(l),j=u.length,F=u.slice(b,b+v);return console.log(`[ENROLLED] Returning ${F.length} of ${j} enrolled packages (multi-platform mode)`),t.json({body:{response:F,total:j,from:b,size:F.length,mode:"multi-platform",platformErrors:p.length>0?p:void 0}})}catch(f){console.error("[ENROLLED] Multi-platform mode failed:",f),console.log("[ENROLLED] Falling back to legacy mode")}}console.log("[ENROLLED] Multi-platform mode failed, using legacy user.metadata.policies fallback");const c=(await E.security.getUser({username:e}))[e];if(!c?.metadata?.policies)return t.json({body:{response:[],total:0,from:0,size:0,mode:"fallback"}});let d=[];for(const[f,u]of Object.entries(c.metadata.policies)){const p=u;if(!p)continue;const b=p.package_name;b&&p.status!=="deleted"&&d.push({id:p.policy_id,name:b,title:p.package_title||b,description:p.description||p.package_description||"",version:p.package_version||"unknown",status:"enrolled",icons:p.icons||[],installationInfo:{install_status:"enrolled",install_source:"registry"},policy_id:p.policy_id,policy_name:p.policy_name,cluster_name:p.cluster_name,cluster_uuid:p.cluster_uuid,namespace:f,created_at:p.created_at,platforms:[{platformId:"settings",platformName:"Default (settings.ts)",status:"enrolled",policyId:p.policy_id,namespace:f}]})}if(n){const f=n.toLowerCase();d=d.filter(u=>u.name?.toLowerCase().includes(f)||u.title?.toLowerCase().includes(f)||u.description?.toLowerCase().includes(f))}a&&(d=d.filter(f=>f.name===a)),o&&(d=d.filter(f=>f.status===o));const h=parseInt(s),C=parseInt(l),S=d.length,k=d.slice(h,h+C);return console.log(`[ENROLLED] Returning ${k.length} of ${S} enrolled packages (fallback mode)`),t.json({body:{response:k,total:S,from:h,size:k.length,mode:"fallback"}})}catch(e){return console.error("[ENROLLED] Error fetching enrolled packages:",e),t.status(500).json({statusCode:500,body:{error:e.message||"Failed to fetch enrolled packages"}})}}async handleGetInstalledPackages(r,t){try{const e=r.auth?.username;if(!e)return t.status(401).json({statusCode:401,body:{error:"Unauthorized - no username found"}});console.log(`[INSTALLED] Fetching installed packages for user: ${e} via platforms`);try{const{packageMap:i,errors:c}=await N.getInstalledPackagesFromAllPlatforms(e),d=Array.from(i.values());if(c.length>0&&console.warn("[INSTALLED] Platform errors:",c.map(h=>`${h.platformName}: ${h.error}`)),d.length>0)return console.log(`[INSTALLED] Returning ${d.length} installed packages (platforms mode)`),t.json({body:{response:d,total:d.length,mode:"platforms"}})}catch(i){console.error("[INSTALLED] Platforms mode failed, falling back to user metadata:",i.message)}console.log("[INSTALLED] Using legacy user.metadata.policies fallback");const a=(await E.security.getUser({username:e}))[e];if(!a?.metadata?.policies)return t.json({body:{response:[],total:0,mode:"fallback"}});const o=new Map,s=new Map;for(const[i,c]of Object.entries(a.metadata.policies)){const d=c;if(!d)continue;const h=d.package_name,C=d.package_version;!h||!C||d.status!=="deleted"&&(s.has(h)||s.set(h,{name:h,version:C}),o.has(h)||o.set(h,{id:d.policy_id,name:h,title:d.package_title||h,description:d.description||d.package_description||"",version:C,status:"installed",icons:[],installationInfo:{install_status:"installed",install_source:"registry"},policy_id:d.policy_id,namespace:i}))}const l=Array.from(s.values()).map(async({name:i,version:c})=>{try{const d=await P.getEpmPackage(i,c);return{name:i,icons:d?.data?.item?.icons||[],title:d?.data?.item?.title,description:d?.data?.item?.description}}catch{return{name:i,icons:[],title:null,description:null}}});(await Promise.all(l)).forEach(({name:i,icons:c,title:d,description:h})=>{const C=o.get(i);C&&(C.icons=c,d&&!C.title&&(C.title=d),h&&!C.description&&(C.description=h))});const y=Array.from(o.values());return console.log(`[INSTALLED] Returning ${y.length} installed packages (fallback mode)`),t.json({body:{response:y,total:y.length,mode:"fallback"}})}catch(e){return console.error("[INSTALLED] Error fetching installed packages:",e),t.status(500).json({statusCode:500,body:{error:"Failed to fetch installed packages",details:e.message}})}}async handleGetFleetPolicies(r,t){try{const{platformId:e}=r.query||{},n=r.auth?.username;let a;if(e&&n){console.log(`[FLEET-POLICIES] Fetching Fleet agent policies for platform: ${e}`);const l=await L(e,n,!0);if(!l)return t.json({statusCode:404,body:{error:"Platform not found"}});a=await _.createFromPlatform(l).getAgentPolicies()}else console.log("[FLEET-POLICIES] Fetching Fleet agent policies (static config)"),a=await P.getAgentPolicies();if(a.error)return console.error("[FLEET-POLICIES] Error fetching policies:",a.error),t.json({statusCode:a.statusCode||500,body:{error:"Failed to fetch Fleet policies",details:a.error}});const o=a.data?.items||[],s=o.filter(l=>!l.is_managed&&!l.is_default_fleet_server);return console.log(`[FLEET-POLICIES] Successfully fetched policies: ${o.length} total, ${s.length} after filtering reserved`),t.json({statusCode:200,body:{...a.data,items:s}})}catch(e){return console.error("[FLEET-POLICIES] Error fetching Fleet policies:",e),t.json({statusCode:500,body:{error:"Failed to fetch Fleet policies",details:e.message||"Unknown error"}})}}async handleGetFleetServerHosts(r,t){try{const{platformId:e}=r.query||{},n=r.auth?.username;let a;if(e&&n){const o=await L(e,n,!0);if(!o)return t.status(404).json({statusCode:404,body:{error:"Platform not found"}});a=await _.createFromPlatform(o).getFleetServerHosts()}else a=await P.getFleetServerHosts();return a.error||a.statusCode&&a.statusCode>=400?t.status(a.statusCode||500).json({statusCode:a.statusCode||500,body:{error:"Failed to fetch Fleet Server hosts",details:a.error||a.data}}):t.json({statusCode:200,body:a.data})}catch(e){return console.error("[GET-FLEET-SERVER-HOSTS] Error:",e),t.status(500).json({statusCode:500,body:{error:e.message||"Unknown error"}})}}async handleGetFleetAgents(r,t){try{const{platformId:e,kuery:n}=r.query||{},a=r.auth?.username;let o;if(e&&a){const s=await L(e,a,!0);if(!s)return t.status(404).json({statusCode:404,body:{error:"Platform not found"}});o=await _.createFromPlatform(s).getFleetAgents(n||"fleet_server:true")}else o=await P.getFleetAgents(n||"fleet_server:true");return o.error||o.statusCode&&o.statusCode>=400?t.status(o.statusCode||500).json({statusCode:o.statusCode||500,body:{error:"Failed to fetch Fleet agents",details:o.error||o.data}}):t.json({statusCode:200,body:o.data})}catch(e){return console.error("[GET-FLEET-AGENTS] Error:",e),t.status(500).json({statusCode:500,body:{error:e.message||"Unknown error"}})}}async handleGetEnrollmentApiKeys(r,t){try{const{platformId:e,policyId:n}=r.query||{},a=r.auth?.username;let o;if(e&&a){const l=await L(e,a,!0);if(!l)return t.status(404).json({statusCode:404,body:{error:"Platform not found"}});o=await _.createFromPlatform(l).getEnrollmentApiKeys(n||void 0)}else o=await P.getEnrollmentApiKeys(n||void 0);if(o.error||o.statusCode&&o.statusCode>=400)return t.status(o.statusCode||500).json({statusCode:o.statusCode||500,body:{error:"Failed to fetch enrollment API keys",details:o.error||o.data}});const s=o.data?.list??o.data?.items??[];return t.json({statusCode:200,body:{items:s}})}catch(e){return console.error("[GET-ENROLLMENT-API-KEYS] Error:",e),t.status(500).json({statusCode:500,body:{error:e.message||"Unknown error"}})}}async handleCreateFleetPolicy(r,t){try{const{name:e,description:n,platformId:a}=r.body||{},o=r.auth?.username;if(!e)return t.status(400).json({statusCode:400,body:{error:"name is required"}});let s;if(a&&o){const l=await L(a,o,!0);if(!l)return t.status(404).json({statusCode:404,body:{error:"Platform not found"}});s=await _.createFromPlatform(l).createAgentPolicy(e,n)}else s=await P.createAgentPolicy(e,n);if(s.error||s.statusCode&&s.statusCode>=400){const l=s.data||s.error;console.error("[CREATE-FLEET-POLICY] Error:",l);const m=s.statusCode||l?.statusCode||500;return t.status(m).json({statusCode:m,body:{error:l?.error||"Failed to create Fleet policy",message:l?.message||l?.error||"Failed to create Fleet policy",details:l}})}return t.json({statusCode:200,body:s.data})}catch(e){return console.error("[CREATE-FLEET-POLICY] Error:",e),t.status(500).json({statusCode:500,body:{error:e.message||"Unknown error"}})}}async handleDeletePackagePolicy(r,t){try{const{policyId:e}=r.params,n=r.auth?.username,a=r.body?.platformId||r.query?.platformId;if(console.log(`[DELETE] Deleting policy: ${e} for user: ${n}, platformId: ${a}`),!n)return t.json({statusCode:401,body:{error:"Unauthorized",details:"User authentication required"}});const o=await this.resolveKibanaClient(a,n);console.log(`[DELETE] Calling Kibana to delete policy: ${e}`);const s=await A.deletePackagePolicy(e,o);return s.error?(console.error("[DELETE] Kibana delete failed:",s.error),t.json({statusCode:s.statusCode||500,body:{error:"Failed to delete policy from Kibana",details:s.error}})):(console.log("[DELETE] Policy deleted from Kibana successfully"),t.json({statusCode:200,body:{success:!0,message:"Package deleted successfully",policyId:e}}))}catch(e){return console.error("[DELETE] Error deleting package:",e),t.json({statusCode:500,body:{error:"Failed to delete package",details:e.message||"Unknown error"}})}}async handleInstallPackage(r,t){try{const{packageName:e}=r.params,{connectionSettings:n,packageFormData:a,fleetPolicyId:o,platformId:s}=r.body,l=r.auth.username,m=a?.packageVersion||"latest";console.log(`[INSTALL] Installing package: ${e} version ${m} platformId: ${s}`);const y=await this.resolveKibanaClient(s,l);if(s&&!y&&console.warn(`[INSTALL] Could not resolve client for platformId=${s}, falling back to default`),a){console.log("[INSTALL] Wizard flow detected - creating policy via PackagePolicyService");const i=this.mergeConnectionSettingsIntoFormData(a,n),c=await A.installPackage(i,o,y);return c?.error?t.json({statusCode:c.statusCode||500,body:c.error}):(console.log("[INSTALL] Policy created successfully:",c?.data?.item?.id),t.json({statusCode:200,body:{success:!0,message:"Package installed and policy created successfully",packageName:e,packageVersion:m,data:c.data}}))}else{const c=await(y??P).installEpmPackage(e,m);return c.error?t.json({statusCode:c.statusCode||500,body:{error:"Failed to install package",details:c.error}}):(console.log(`[INSTALL] Package installed successfully: ${e}`),t.json({statusCode:200,body:{success:!0,message:"Package installed successfully",packageName:e,packageVersion:m,data:c.data}}))}}catch(e){return console.error("[INSTALL] Error installing package:",e),t.json({statusCode:500,body:{error:"Failed to install package",details:e.message||"Unknown error"}})}}async handleUpdatePackagePolicy(r,t){try{const{packageName:e}=r.params,{connectionSettings:n,packageFormData:a,policyId:o,platformId:s}=r.body,l=r.auth?.username;if(console.log(`[UPDATE] Updating policy for package: ${e}, policyId: ${o}, platformId: ${s}`),!o)return t.json({statusCode:400,body:{error:"Policy ID is required for update"}});const m=await this.resolveKibanaClient(s,l),y=this.mergeConnectionSettingsIntoFormData(a,n),i=await A.updatePackagePolicy(o,y,m);return i?.error?t.json({statusCode:i.statusCode||500,body:i.error}):(console.log("[UPDATE] Policy updated successfully:",o),t.json({statusCode:200,body:{success:!0,message:"Policy updated successfully",packageName:e,data:i.data}}))}catch(e){return console.error("[UPDATE] Error updating policy:",e),t.json({statusCode:e.statusCode||500,body:{error:"Failed to update policy",details:e.message||"Unknown error"}})}}async resolveKibanaClient(r,t){if(!r||!t)return null;try{const e=await L(r,t,!0);return e?_.createFromPlatform(e):(console.warn(`[resolveKibanaClient] Platform ${r} not found for user ${t}`),null)}catch(e){return console.warn(`[resolveKibanaClient] Failed to create client for platform ${r}: ${e.message}`),null}}mergeConnectionSettingsIntoFormData(r,t){if(!t)return r;const e={...r.vars||{}},n=["api_url","api_key","ssl","interval","username","password"];for(const a of n)t[a]&&(e[a]=t[a]);return{...r,vars:e}}}const g=new Q,ue=[{method:"post",path:"/api/stack_expert/settings/accounts",validate:{},handler:g.handleGetAccounts.bind(g)},{method:"post",path:"/api/stack_expert/settings/accounts/signup",validate:{body:Y},handler:g.handleAccountSignup.bind(g)},{method:"post",path:"/api/stack_expert/settings/account/link",validate:{body:R},handler:g.handleAccountLink.bind(g)},{method:"post",path:"/api/stack_expert/settings/account/role",validate:{body:R},handler:g.handleCreateRole.bind(g)},{method:"post",path:"/api/stack_expert/settings/accounts/update",validate:{body:J},handler:g.handleAccountUpdate.bind(g)},{method:"post",path:"/api/stack_expert/settings/policies/check",validate:{body:T},handler:g.handlePolicyCheck.bind(g)},{method:"post",path:"/api/stack_expert/settings/policies",validate:{body:B},handler:g.handlePolicyCreate.bind(g)},{method:"put",path:"/api/stack_expert/settings/policies",validate:{body:T},handler:g.handlePolicyUpdate.bind(g)},{method:"get",path:"/api/stack_expert/settings/fleet/policies",validate:{},handler:g.handleGetFleetPolicies.bind(g)},{method:"post",path:"/api/stack_expert/settings/fleet/policies",validate:{},handler:g.handleCreateFleetPolicy.bind(g)},{method:"get",path:"/api/stack_expert/settings/fleet/agents",validate:{},handler:g.handleGetFleetAgents.bind(g)},{method:"get",path:"/api/stack_expert/settings/fleet/server-hosts",validate:{},handler:g.handleGetFleetServerHosts.bind(g)},{method:"get",path:"/api/stack_expert/settings/fleet/enrollment-api-keys",validate:{},handler:g.handleGetEnrollmentApiKeys.bind(g)}];export{ue as routes};
@@ -0,0 +1 @@
1
+ import{z as o}from"zod";import*as d from"../core/services/share-service";import*as h from"../services/guest-service";import*as k from"../core/services/chats-service";import{userToken as g,getUserEmailByUsername as b}from"../services/auth";import{verifyGuestJWT as I}from"../core/services/share-service";import{getPlatforms as R}from"../core/services/platforms-service";import{checkEmbedRateLimit as v}from"../core/services/embed-rate-limiter";import{randomUUID as j}from"node:crypto";const f="https://app.erretegia.com";function u(n){const s=n.auth?.username;if(s)return s;let t=n.cookies?.token;if(!t){const e=n.headers.authorization;e?.startsWith("Bearer ")&&(t=e.substring(7))}return t?g(t)?.username??"":""}function w(n){let s=n.cookies?.token;if(!s){const e=n.headers.authorization;e?.startsWith("Bearer ")&&(s=e.substring(7))}if(s){const e=g(s);if(e)return e.isGuest?{username:e.username,isGuest:!0,guestEmail:e.guestEmail,ownerId:e.ownerId,shareLinkId:e.shareLinkId}:{username:e.username??"",isGuest:!1}}const t=n.cookies?.guestToken;if(t){const e=I(t);if(e)return{username:`guest:${e.guestEmail}`,isGuest:!0,guestEmail:e.guestEmail,ownerId:e.ownerId,shareLinkId:e.shareLinkId}}return{username:"",isGuest:!1}}const x=o.object({teamId:o.string(),teamName:o.string(),label:o.string().optional(),expiresAt:o.string().optional(),maxUses:o.number().int().positive().optional(),allowedEmails:o.array(o.string()).optional().default([]),allowedDomains:o.array(o.string()).optional().default([])}),L=o.object({teamId:o.string(),teamName:o.string(),label:o.string().optional(),expiresAt:o.string().optional(),allowedOrigins:o.array(o.string()).min(1),rateLimit:o.object({perMinute:o.number().int().positive()}).optional(),budget:o.object({maxTokensPerDay:o.number().int().positive().optional(),maxRunsPerDay:o.number().int().positive().optional()}).optional()}),S=o.object({dashboardLabel:o.string().optional(),label:o.string().optional(),expiresAt:o.string().optional(),maxUses:o.number().int().positive().optional(),allowedEmails:o.array(o.string()).optional().default([]),allowedDomains:o.array(o.string()).optional().default([])}),T=o.object({chatId:o.string().min(1),label:o.string().optional(),expiresAt:o.string().optional(),maxUses:o.number().int().positive().optional(),allowedEmails:o.array(o.string()).optional().default([]),allowedDomains:o.array(o.string()).optional().default([])}),D=o.object({email:o.string().email()}),U=o.object({token:o.string().uuid()}),A=o.object({email:o.string().email(),name:o.string().optional(),notes:o.string().optional()}),C=o.object({name:o.string().optional(),notes:o.string().optional(),status:o.enum(["active","revoked"]).optional()}),q=[{method:"get",path:"/api/share-links/:id/info",openapi:{summary:"Get public info for a share link",description:"Returns landing-page info for a share link to either its owner or an authorised guest. For `team` links it also resolves the owner's platform namespaces. `allowedEmails`/`allowedDomains` are only echoed back to the owner. No JWT required.",tags:["share"],security:"none",audience:"internal"},handler:async(n,s)=>{const t=w(n),e=n.params.id,a=await d.getShareLinkById(e);if(!a)return s.status(404).json({success:!1,error:"Share link not found"});if(a.revoked)return s.status(403).json({success:!1,error:"Share link has been revoked"});if(a.expiresAt&&new Date(a.expiresAt)<new Date)return s.status(403).json({success:!1,error:"Share link has expired"});const r=!!t.username&&t.username===a.ownerId,i=t.isGuest&&t.shareLinkId===a.id;let l=[];if(a.type==="team")try{l=(await R(a.ownerId)).platforms.map(c=>c.namespace).filter(c=>typeof c=="string"&&c.length>0)}catch{}s.json({success:!0,isOwner:r,isGuest:i??!1,info:{id:a.id,type:a.type,teamId:a.teamId,teamName:a.teamName,dashboardLabel:a.dashboardLabel,chatId:a.chatId,chatTitle:a.chatTitle,ownerId:a.ownerId,namespaces:[...new Set(l)],allowedEmails:r?a.allowedEmails:[],allowedDomains:r?a.allowedDomains:[]}})}},{method:"post",path:"/api/share-links/:id/request-access",validate:{body:D},openapi:{summary:"Request access to a share link",description:"Sends a magic-link email to the supplied address if it satisfies the link's `allowedEmails` (usernames are resolved to their email) or, when no emails are listed, its `allowedDomains`. Returns `403` when the email is not authorised. No JWT required.",tags:["share"],security:"none",audience:"internal"},handler:async(n,s)=>{const{id:t}=n.params,{email:e}=n.body,a=await d.getShareLinkById(t);if(!a)return s.status(404).json({success:!1,error:"Share link not found"});if(a.revoked)return s.status(403).json({success:!1,error:"Share link has been revoked"});if(a.expiresAt&&new Date(a.expiresAt)<new Date)return s.status(403).json({success:!1,error:"Share link has expired"});if(a.allowedEmails.length>0&&!(await Promise.all(a.allowedEmails.map(async m=>{if(!m.includes("@")){const y=await b(m);return y?y.toLowerCase():m.toLowerCase()}return m.toLowerCase()}))).includes(e.toLowerCase()))return s.status(403).json({success:!1,error:"This email is not authorised for this share link"});if(a.allowedEmails.length===0&&a.allowedDomains.length>0){const c=e.split("@")[1]?.toLowerCase();if(!c||!a.allowedDomains.map(m=>m.toLowerCase()).includes(c))return s.status(403).json({success:!1,error:"Your email domain is not authorised"})}const r=a.type==="dashboard"?a.dashboardLabel||"Live Dashboard":a.type==="run"?a.chatTitle||"Chat conversation":a.teamName||"Team",i=a.type==="dashboard"?`${f}/share/dashboard-view/${t}`:a.type==="run"?`${f}/share/run-view/${t}`:`${f}/share/team/${t}`,{sent:l,error:p}=await h.generateAndSendMagicToken(e,a.ownerId,t,r,i);if(!l)return s.status(403).json({success:!1,error:p});s.json({success:!0})}},{method:"get",path:"/api/chats/shared/:shareTokenId",openapi:{summary:"Read the frozen chat snapshot of a run share link",description:"Returns the chat snapshot captured when a `run` share link was created (no live updates). Access requires either the owner's JWT or a guest JWT bound to this share link; enforces revoked / expired / max-uses gates. No JWT required at the route level (guest cookie auth).",tags:["share"],security:"none",audience:"internal"},handler:async(n,s)=>{const{shareTokenId:t}=n.params,e=await d.getShareLinkById(t);if(!e)return s.status(404).json({success:!1,error:"Share link not found"});if(e.type!=="run")return s.status(400).json({success:!1,error:"Share link is not a chat run"});if(e.revoked)return s.status(403).json({success:!1,error:"Share link has been revoked"});if(e.expiresAt&&new Date(e.expiresAt)<new Date)return s.status(403).json({success:!1,error:"Share link has expired"});if(e.maxUses!=null&&e.usesCount>=e.maxUses)return s.status(403).json({success:!1,error:"Share link has reached maximum uses"});const a=w(n),r=!!a.username&&a.username===e.ownerId,i=a.isGuest&&a.shareLinkId===e.id;if(!r&&!i)return s.status(403).json({success:!1,error:"Not authorised for this share link"});if(!e.chatSnapshot)return s.status(500).json({success:!1,error:"Chat snapshot is missing"});s.json({success:!0,ownerId:e.ownerId,chatTitle:e.chatTitle,snapshot:e.chatSnapshot})}},{method:"post",path:"/api/share-links/verify-magic-token",validate:{body:U},openapi:{summary:"Verify a magic token and issue a guest session",description:"Validates a magic token sent via email and, on success, sets a 1h `guestToken` HttpOnly cookie and returns the redirect URL plus the resolved share-link type and `teamId`. No JWT required.",tags:["share"],security:"none",audience:"internal"},handler:async(n,s)=>{const{token:t}=n.body,e=await h.validateMagicToken(t);if(console.log("[share] verify-magic-token result:",{valid:e.valid,shareLinkType:e.shareLinkType,teamId:e.teamId,redirectUrl:e.redirectUrl,hasJWT:!!e.guestJWT,error:e.error}),!e.valid)return s.status(403).json({success:!1,error:e.error});s.cookie("guestToken",e.guestJWT,{httpOnly:!0,secure:!0,sameSite:"lax",maxAge:3600*1e3}),console.log("[share] verify-magic-token: guestToken cookie set, sending response"),s.json({success:!0,redirectUrl:e.redirectUrl,guestToken:e.guestJWT,type:e.shareLinkType,teamId:e.teamId})}},{method:"post",path:"/api/embed/:linkId/session",openapi:{summary:"Mint an anonymous embed session token",description:"Public endpoint used by the embedded widget to obtain a 1h anonymous guest JWT for a `team` embed link. Gated by per-IP+link rate limiting and a durable daily budget; host-binding itself is enforced via the CSP `frame-ancestors` header on `GET /embed/:linkId`. No JWT required.",tags:["share","embed"],security:"none",audience:"internal"},handler:async(n,s)=>{const t=n.params.linkId,e=await d.getShareLinkById(t);if(!e||!e.embed||e.revoked)return s.status(404).json({success:!1,error:"Embed not found"});if(e.expiresAt&&new Date(e.expiresAt)<new Date)return s.status(410).json({success:!1,error:"This embed link has expired"});const r=n.headers["x-forwarded-for"]?.split(",")[0]?.trim()||n.socket.remoteAddress||"unknown",i=e.rateLimit?.perMinute??20;if(!v(`${r}:${t}`,i))return s.status(429).json({success:!1,error:"Too many requests, please slow down"});if(!d.checkEmbedBudget(e))return s.status(429).json({success:!1,error:"This assistant has reached its usage limit for today"});const l=`embed-${t}-${j().slice(0,8)}`,p=d.issueGuestJWT(`embed@${t}`,e.ownerId,t,l);d.consumeEmbedBudget(t,{runs:1}).catch(()=>{}),s.json({success:!0,token:p,teamId:e.teamId,teamName:e.teamName})}}],W=[{method:"get",path:"/api/share-links",openapi:{summary:"List the caller's share links",description:"Returns all share links owned by the authenticated user, optionally filtered by `type` (`team` | `dashboard` | `run`) via the query string.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=n.query.type,a=await d.getShareLinksByOwner(t,e);s.json({success:!0,links:a})}},{method:"post",path:"/api/share-links/team",validate:{body:x},openapi:{summary:"Create a team share link",description:"Creates a share link to a team. Access can be restricted via `allowedEmails` / `allowedDomains`, time-boxed with `expiresAt`, and capped with `maxUses`.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=n.body,a=e.label?.trim()||`${e.teamName} #${Math.floor(Date.now()/1e3)}`,r=await d.createShareLink({type:"team",teamId:e.teamId,teamName:e.teamName,ownerId:t,label:a,expiresAt:e.expiresAt,maxUses:e.maxUses,allowedEmails:e.allowedEmails??[],allowedDomains:e.allowedDomains??[]});s.json({success:!0,link:r})}},{method:"post",path:"/api/share-links/embed",validate:{body:L},openapi:{summary:"Create an embed share link",description:"Creates an anonymous public widget link for a team. Access is governed by web `allowedOrigins` (CSP `frame-ancestors`) plus an optional per-minute `rateLimit` and daily `budget`, not by email/domain \u2014 those are kept empty.",tags:["share","embed"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=n.body,a=e.label?.trim()||`${e.teamName} embed #${Math.floor(Date.now()/1e3)}`,r=await d.createShareLink({type:"team",teamId:e.teamId,teamName:e.teamName,ownerId:t,label:a,expiresAt:e.expiresAt,allowedEmails:[],allowedDomains:[],embed:!0,allowedOrigins:e.allowedOrigins.map(i=>i.trim()).filter(Boolean),rateLimit:e.rateLimit,budget:e.budget});s.json({success:!0,link:r})}},{method:"post",path:"/api/share-links/dashboard",validate:{body:S},openapi:{summary:"Create a dashboard share link",description:"Creates a share link to a live dashboard. Access can be restricted via `allowedEmails` / `allowedDomains`, time-boxed with `expiresAt`, and capped with `maxUses`.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=n.body,a=e.dashboardLabel?.trim()||"Live Dashboard",r=e.label?.trim()||`${a} #${Math.floor(Date.now()/1e3)}`,i=await d.createShareLink({type:"dashboard",dashboardLabel:a,ownerId:t,label:r,expiresAt:e.expiresAt,maxUses:e.maxUses,allowedEmails:e.allowedEmails??[],allowedDomains:e.allowedDomains??[]});s.json({success:!0,link:i})}},{method:"post",path:"/api/share-links/run",validate:{body:T},openapi:{summary:"Create a chat-run share link",description:"Creates a share link that captures a frozen snapshot of an owned chat (`chatId`) at creation time. Access can be restricted via `allowedEmails` / `allowedDomains`, time-boxed with `expiresAt`, and capped with `maxUses`. The snapshot is stripped from the response.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=n.body,a=await k.getChatById(e.chatId,t);if(!a)return s.status(404).json({success:!1,error:"Chat not found"});const r=a.title||"Chat conversation",i=e.label?.trim()||`${r} #${Math.floor(Date.now()/1e3)}`,l=await d.createShareLink({type:"run",chatId:e.chatId,chatTitle:r,chatSnapshot:{title:r,teamName:a.teamName||"",messages:a.messages||[],capturedAt:new Date().toISOString()},ownerId:t,label:i,expiresAt:e.expiresAt,maxUses:e.maxUses,allowedEmails:e.allowedEmails??[],allowedDomains:e.allowedDomains??[]}),{chatSnapshot:p,...c}=l;s.json({success:!0,link:c})}},{method:"put",path:"/api/share-links/:id/revoke",openapi:{summary:"Revoke a share link",description:"Marks a share link owned by the caller as revoked. Revoked links reject all subsequent access. Returns `404` if the link does not exist or is not owned by the caller.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});if(!await d.revokeShareLink(n.params.id,t))return s.status(404).json({success:!1,error:"Link not found or not owned by you"});s.json({success:!0})}},{method:"delete",path:"/api/share-links/:id",openapi:{summary:"Delete a share link",description:"Permanently deletes a share link owned by the caller. Returns `404` if the link does not exist or is not owned by the caller.",tags:["share"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});if(!await d.deleteShareLink(n.params.id,t))return s.status(404).json({success:!1,error:"Link not found or not owned by you"});s.json({success:!0})}},{method:"post",path:"/api/guests",validate:{body:A},openapi:{summary:"Create a guest",description:"Creates a guest (by email) owned by the caller, with optional `name` and `notes`. Returns `201` when newly created, `200` when an existing guest with the same email is returned (`created` flag distinguishes the two).",tags:["guests"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const{email:e,name:a,notes:r}=n.body,{guest:i,created:l}=await h.createGuest(e,t,a,r);s.status(l?201:200).json({success:!0,guest:i,created:l})}},{method:"get",path:"/api/guests",openapi:{summary:"List the caller's guests",description:"Returns all guests owned by the authenticated user.",tags:["guests"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});const e=await h.getGuestsByOwner(t);s.json({success:!0,guests:e})}},{method:"put",path:"/api/guests/:id",validate:{body:C},openapi:{summary:"Update a guest",description:"Updates an owned guest's `name`, `notes`, or `status` (`active` | `revoked`). Returns `404` if the guest does not exist or is not owned by the caller.",tags:["guests"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});if(!await h.updateGuest(n.params.id,t,n.body))return s.status(404).json({success:!1,error:"Guest not found or not owned by you"});s.json({success:!0})}},{method:"delete",path:"/api/guests/:id",openapi:{summary:"Delete a guest",description:"Permanently deletes an owned guest. Returns `404` if the guest does not exist or is not owned by the caller.",tags:["guests"]},handler:async(n,s)=>{const t=u(n);if(!t)return s.status(401).json({success:!1,error:"Unauthorized"});if(!await h.deleteGuest(n.params.id,t))return s.status(404).json({success:!1,error:"Guest not found or not owned by you"});s.json({success:!0})}}];export{q as publicRoutes,W as routes};
@@ -0,0 +1 @@
1
+ import n from"express";import{TOOLBOX_CATALOG as l,getToolboxIntegration as u}from"../generated/toolbox-catalog";const t=n.Router();t.get("/catalog",(o,e)=>{e.json({success:!0,integrations:l.map(s=>({slug:s.slug,sourceType:s.sourceType,title:s.title,description:s.description,fields:s.fields,toolCount:s.tools.length,customToolType:s.customToolType,customToolShape:s.customToolShape}))})}),t.get("/catalog/:slug",(o,e)=>{const s=u(o.params.slug);return s?e.json({success:!0,integration:s}):e.status(404).json({success:!1,message:"Unknown source type"})}),t.get("/catalog/:slug/tools",(o,e)=>{const s=u(o.params.slug);return s?e.json({success:!0,tools:s.tools,customToolType:s.customToolType,customToolShape:s.customToolShape}):e.status(404).json({success:!1,message:"Unknown source type"})});var g=t;export{g as default};
@@ -0,0 +1 @@
1
+ import m from"express";import{z as e,ZodError as u}from"zod";import{userToken as l}from"../services/auth";import{listSources as d,getSource as p,installSource as f,updateSource as g,deleteSource as y,pingSource as j,createSourceTool as h,testSourceStatement as R}from"../core/services/sources-service";const a=m.Router();function c(r){const t=r.auth?.username;if(t)return t;let o=r.cookies?.token;if(!o){const s=r.headers.authorization;s&&s.startsWith("Bearer ")&&(o=s.substring(7))}return o?l(o)?.username??null:null}const b=e.object({platformId:e.string().min(1),name:e.string().min(1).max(64),slug:e.string().min(1),config:e.record(e.any()).default({}),fileName:e.string().max(128).optional()});a.get("/",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=await d(o);return t.json({success:!0,sources:s})}catch(s){return console.error("[sources] list error:",s),t.status(500).json({success:!1,message:s?.message||"Internal error"})}}),a.get("/:id",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});const s=await p(o,r.params.id);return s?t.json({success:!0,source:s}):t.status(404).json({success:!1,message:"Source not found"})}),a.post("/",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=b.parse(r.body),n=await f(o,s);return t.json({success:!0,source:n})}catch(s){return s instanceof u?t.status(400).json({success:!1,message:"Invalid body",errors:s.errors}):(console.error("[sources] install error:",s),t.status(500).json({success:!1,message:s?.message||"Internal error"}))}});const i=e.object({name:e.string().min(1).max(64),type:e.string().min(1),description:e.string().optional()}).passthrough(),S=e.object({statement:e.string().optional(),toolType:e.string().min(1),params:e.record(e.any()).optional(),commands:e.array(e.array(e.string())).optional(),parameters:e.array(i).optional(),extra:e.record(e.any()).optional()});a.post("/:id/test-statement",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=S.parse(r.body),n=await R(o,String(r.params.id),s);return t.json(n)}catch(s){if(s instanceof u)return t.status(400).json({success:!1,message:"Invalid body",errors:s.errors});const n=/not found/i.test(s?.message||"")?404:500;return n>=500&&console.error("[sources] test-statement error:",s),t.status(n).json({success:!1,message:s?.message||"Internal error"})}}),a.post("/:id/ping",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=await j(o,r.params.id);return t.json({success:s.status==="verified",source:s})}catch(s){const n=/not found/i.test(s?.message||"")?404:500;return n>=500&&console.error("[sources] ping error:",s),t.status(n).json({success:!1,message:s?.message||"Internal error"})}});const w=e.object({name:e.string().min(1).max(80),label:e.string().max(120).optional(),description:e.string().max(4e3).optional(),type:e.string().min(1),statement:e.string().optional(),parameters:e.array(i).optional(),templateParameters:e.array(i).optional(),authRequired:e.array(e.string()).optional(),commands:e.array(e.array(e.string())).optional(),extra:e.record(e.any()).optional(),outputSchema:e.string().optional()});a.post("/:id/tools",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=w.parse(r.body),n=await h(o,r.params.id,s);return t.json({success:!0,...n})}catch(s){if(s instanceof u)return t.status(400).json({success:!1,message:"Invalid body",errors:s.errors});const n=/not found/i.test(s?.message||"")?404:500;return n>=500&&console.error("[sources] create tool error:",s),t.status(n).json({success:!1,message:s?.message||"Internal error"})}});const x=e.object({name:e.string().min(1).max(64).optional(),config:e.record(e.any()).optional()});a.put("/:id",async(r,t)=>{const o=c(r);if(!o)return t.status(401).json({success:!1,message:"Unauthorized"});try{const s=x.parse(r.body),n=await g(o,r.params.id,s);return t.json({success:!0,source:n})}catch(s){if(s instanceof u)return t.status(400).json({success:!1,message:"Invalid body",errors:s.errors});const n=/not found/i.test(s?.message||"")?404:500;return n>=500&&console.error("[sources] update error:",s),t.status(n).json({success:!1,message:s?.message||"Internal error"})}}),a.delete("/:id",async(r,t)=>{const o=c(r);return o?await y(o,r.params.id)?t.json({success:!0}):t.status(404).json({success:!1,message:"Source not found"}):t.status(401).json({success:!1,message:"Unauthorized"})});var P=a;export{w as CreateToolSchema,S as TestStatementSchema,P as default};
@@ -0,0 +1 @@
1
+ import{Client as R}from"@elastic/elasticsearch";import{z as e}from"zod";import{assertOwner as u,getAuthUsername as m}from"../core/utils/ownership";import{analyzeTeam as j,applyRewrite as v,applyClone as z}from"../services/team-optimizer-service";import{resolveClosure as S,createEsStore as I}from"../services/clone/closure-resolver";const C=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",l=new R({node:C,auth:{username:process.env.ELASTICSEARCH_USER||"elastic",password:process.env.ELASTICSEARCH_PASSWORD||""},tls:{rejectUnauthorized:!1}}),d=".stkxp_teams",p=e.enum(["assistant","team","graph","prompt","routingRule","model","provider","platform","tool","apiDoc","nodeTemplate"]).describe("Entity type in the account-clone entity graph (server/services/clone/entity-graph.ts)"),E=e.object({key:p,esId:e.string(),name:e.string().optional()}).describe("Another entity in the closure that references a proposed-change target \u2014 present only when conflict=true"),k=e.object({key:p,esId:e.string().describe("Elasticsearch _id of the entity this entry proposes changing"),name:e.string().optional(),proposedFields:e.record(e.any()).describe("Partial field set the LLM proposes writing onto this entity"),rationale:e.string().describe("LLM-generated justification for this change"),conflict:e.boolean().describe("true if this entity is referenced by more than one doc in the closure (shared entity)"),conflictReferrers:e.array(E).optional()}),h=e.object({objective:e.string().describe("The natural-language objective passed to /analyze"),entries:e.array(k)}).describe("Cross-layer alignment diff produced by analyzeTeam (team \u2192 assistants \u2192 graphs \u2192 routing rules)"),A=e.object({success:e.literal(!0),diff:h}),T=e.object({success:e.literal(!0),updated:e.array(e.object({key:p,esId:e.string()})).describe('Entities actually written in-place (conflict entries resolved to "clone" are skipped here)')}),_=e.object({success:e.literal(!0),newTeamEsId:e.string().describe("_id of the newly cloned team (root of the cloned hierarchy)")}),y=e.object({success:e.literal(!1),error:e.string(),message:e.string().optional()});async function x(s,o){try{const{teamId:t}=s.params,{objective:r}=s.body,n=await u(s,o,l,d,t);if(!n)return;const a=n.source.owner||m(s)||"system",i=await j(t,a,r);o.status(200).json({success:!0,diff:i})}catch(t){console.error("[TeamOptimizer API] analyze error:",t),o.status(500).json({success:!1,error:"Failed to analyze team",message:t.message})}}async function L(s,o){try{const{teamId:t}=s.params,{diff:r,mode:n,conflictResolutions:a}=s.body,i=await u(s,o,l,d,t);if(!i)return;const c=i.source.owner||m(s)||"system";if(n==="clone"){const g=I(l),b=await S([{index:d,esId:t}],c,g),{newTeamEsId:w}=await z(b,r,c);return o.status(200).json({success:!0,newTeamEsId:w})}const{updated:f}=await v(r,a??{},c);o.status(200).json({success:!0,updated:f})}catch(t){console.error("[TeamOptimizer API] apply error:",t),o.status(500).json({success:!1,error:"Failed to apply team optimizer diff",message:t.message})}}const W=[{path:"/api/team-optimizer/:teamId/analyze",method:"post",handler:x,validate:{params:e.object({teamId:e.string().min(1).describe("_id of the .stkxp_teams doc to analyze")}),body:e.object({objective:e.string().min(1).describe('Natural-language goal to align the team hierarchy against, e.g. "reduce tool-call latency for the RCA path"')})},openapi:{summary:"Analyze a team hierarchy against an objective",description:"Walks the full closure (team \u2192 assistants \u2192 graphs \u2192 routing rules \u2192 prompts \u2192 tools) via resolveClosure, then makes a single structured LLM call to propose a cross-layer diff aligned with the given objective. Flags any entity shared by more than one doc in the closure as a conflict (proposedFields still returned, but applying it requires an explicit per-entry conflictResolution). Does not write anything \u2014 analyze is read-only; call /apply with the returned diff to persist changes. Can take up to ~2 minutes for large hierarchies (use a client-side timeout \u2265120s).",tags:["team-optimizer"],responses:{200:A,500:y}}},{path:"/api/team-optimizer/:teamId/apply",method:"post",handler:L,validate:{params:e.object({teamId:e.string().min(1).describe("_id of the .stkxp_teams doc (root of the hierarchy being applied)")}),body:e.object({diff:h.describe("The diff object returned by /analyze \u2014 pass it back verbatim (only conflictResolutions selects the outcome per conflicting entry)"),mode:e.enum(["rewrite","clone"]).describe('"rewrite" applies non-conflicting entries in place (each touched entity snapshotted to .stkxp_versions first); "clone" re-runs executeClone over the whole closure into a brand-new hierarchy with the diff applied, leaving the source untouched'),conflictResolutions:e.record(e.enum(["clone","rewrite"])).optional().describe('Per-entry resolution for entries where conflict=true, keyed by esId. Only read in "rewrite" mode \u2014 entries resolved to "clone" are skipped rather than rewritten in place.')})},openapi:{summary:"Apply a team optimizer diff",description:`Persists a diff previously returned by /analyze. In "rewrite" mode, writes proposedFields directly onto each non-conflicting entity (snapshotting first) and returns the list of entities actually updated. In "clone" mode, clones the entire team hierarchy (reusing the account-clone closure/executor) with the diff applied on top, and returns the new team's _id \u2014 the source hierarchy is never modified in this mode.`,tags:["team-optimizer"],responses:{200:e.union([T,_]),500:y}}}];export{x as analyzeHandler,L as applyHandler,W as routes};
@@ -0,0 +1 @@
1
+ import{randomBytes as f}from"crypto";import{z as h}from"zod";import{assertOwner as l}from"../core/utils/ownership";import{makeOwnerScopedClient as g}from"../core/utils/owner-scope";import{getTeamWebhookConfig as w}from"../core/services/teams-service";import{createMcpClient as y}from"../core/mcp/client";const S=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",C=process.env.ELASTICSEARCH_USER||"elastic",R=process.env.ELASTICSEARCH_PASSWORD||"",a=".stkxp_teams",_=process.env.MCP_BULLMQ_URL||"http://127.0.0.1:3743/mcp",r=g({node:S,auth:{username:C,password:R},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),b=h.object({cron:h.string().describe("Cron expression for scheduling")}).passthrough();class m extends Error{}async function p(t,s){let e;try{e=await y(_,{timeout:15e3})}catch(o){throw new m(o?.message||"Failed to connect to scheduling service")}try{return await e.callTool(t,s)}finally{await e.close()}}async function k(t,s){try{const e=t.params.id;if(!await l(t,s,r,a,e))return;const n=await r.get({index:a,id:e,_source:["scheduleCron"]});s.json({success:!0,cron:n?._source?.scheduleCron??null})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Team not found"});return}s.status(500).json({success:!1,error:"Failed to read schedule",message:e.message})}}async function v(t,s){try{const e=t.params.id,o=t.body?.cron;if(typeof o!="string"||o.trim()===""){s.status(400).json({success:!1,error:"cron is required"});return}if(!await l(t,s,r,a,e))return;const i=await w(e);if(!i){s.status(404).json({success:!1,error:"Team not found"});return}const u=new Date().toISOString();let c=i.webhookToken;(!i.webhookEnabled||!c)&&(c=f(32).toString("hex"),await r.update({index:a,id:e,body:{doc:{webhookToken:c,webhookEnabled:!0,webhookCreatedAt:u,updated_at:u}},refresh:"wait_for"}));try{await p("schedule_team_run",{teamId:e,cron:o.trim(),webhookToken:c})}catch(d){d instanceof m?s.status(502).json({success:!1,error:"Scheduling service unavailable",message:d.message}):s.status(400).json({success:!1,error:"Failed to schedule",message:d?.message});return}await r.update({index:a,id:e,body:{doc:{scheduleCron:o.trim(),updated_at:u}},refresh:"wait_for"}),s.json({success:!0,cron:o.trim()})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Team not found"});return}s.status(500).json({success:!1,error:"Failed to set schedule",message:e.message})}}async function T(t,s){try{const e=t.params.id;if(!await l(t,s,r,a,e))return;try{await p("remove_team_schedule",{scheduleId:`team-${e}`})}catch(n){if(n instanceof m){s.status(502).json({success:!1,error:"Scheduling service unavailable",message:n.message});return}}await r.update({index:a,id:e,body:{doc:{scheduleCron:null,updated_at:new Date().toISOString()}},refresh:"wait_for"}),s.json({success:!0})}catch(e){if(e.meta?.statusCode===404){s.status(404).json({success:!1,error:"Team not found"});return}s.status(500).json({success:!1,error:"Failed to remove schedule",message:e.message})}}const D=[{method:"get",path:"/api/teams/:id/schedule",handler:k,openapi:{summary:"Get a team's recurring run schedule",description:"Returns the cron pattern of the team schedule (display cache), or null when none is set.",tags:["teams","scheduling"]}},{method:"put",path:"/api/teams/:id/schedule",handler:v,validate:{body:b},openapi:{summary:"Set (create or replace) a team recurring run schedule",description:"Body { cron }. Auto-activates the team webhook token if missing, schedules a recurring headless run via mcp-bullmq, and caches the cron on the team doc. Invalid cron \u2192 400; scheduling service unreachable \u2192 502.",tags:["teams","scheduling"]}},{method:"delete",path:"/api/teams/:id/schedule",handler:T,openapi:{summary:"Remove a team recurring run schedule",description:"Removes the schedule from mcp-bullmq and clears the cached cron. Idempotent.",tags:["teams","scheduling"]}}];export{m as BullmqUnavailable,T as deleteTeamSchedule,k as getTeamSchedule,v as putTeamSchedule,D as routes};
@@ -0,0 +1 @@
1
+ import{Client as q}from"@elastic/elasticsearch";import{z as u}from"zod";import{randomBytes as O,randomUUID as K}from"crypto";import{assertOwner as A,getAuthUsername as C,isSuperuser as V}from"../core/utils/ownership";import{makeOwnerScopedClient as Q}from"../core/utils/owner-scope";import{resolveClosure as $,createEsStore as j,PERSONAL_TOOLS_REACHED_VIA as U}from"../services/clone/closure-resolver";import{executeClone as X,createEsCloneWriter as Z}from"../services/clone/clone-executor";import{serializeTeamBundle as J,parseTeamBundle as Y,TeamBundleParseError as ee}from"../services/clone/team-bundle";import{serializeTeamBundleEncrypted as se}from"../services/clone/team-bundle-encrypted";import{buildFullStructureGraph as te}from"../services/graph-canvas/full-structure-builder";import{MultiKibanaService as ae}from"../core/services/multi-kibana-service";import{projectAssistantTokens as ne}from"../core/services/token-projection-service";import{buildMcpAppTesterZip as oe,slugifyTeamName as re}from"../services/mcp-app-tester-package";import{createGatewayGrant as D,redeemGatewayGrant as ie,GATEWAY_UNREACHABLE_PREFIX as N}from"../services/mcp-gateway/gateway-grant-service";import{isGatewayEligiblePlatformType as z}from"../core/services/gateway-platform-types";import{syncRemoteToolsForPlatform as ce}from"../core/services/mcp-sync-service";import{snapshotBeforeUpdate as de}from"../core/services/version-snapshot-service";import{findTeamOwningCommand as le,getChannelCommandOwnership as me}from"../core/services/teams-service";const ue=new ae,F=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",G=process.env.ELASTICSEARCH_USER||"elastic",B=process.env.ELASTICSEARCH_PASSWORD||"",w=".stkxp_teams",v=".stkxp_assistants",pe=".stkxp_chats",p=Q({node:F,auth:{username:G,password:B},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),P=new q({node:F,auth:{username:G,password:B},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),L=u.object({enabled:u.string().optional().transform(s=>{if(s!==void 0)return s==="true"}),page:u.string().optional().transform(s=>s?parseInt(s,10):1),pageSize:u.string().optional().transform(s=>s?parseInt(s,10):25),search:u.string().optional().describe("Hybrid lexical + semantic search across name and description"),packageName:u.string().optional(),owners:u.string().optional(),currentUser:u.string().optional(),includeAllSystem:u.string().optional().transform(s=>s==="true"),includeOwned:u.union([u.boolean(),u.string()]).optional().transform(s=>s===!0||s==="true"),assistantId:u.string().optional()});async function ge(){try{await p.indices.create({index:w,body:{mappings:{properties:{name:{type:"keyword",copy_to:"search_semantic"},description:{type:"text",copy_to:"search_semantic"},search_semantic:{type:"semantic_text",inference_id:".multilingual-e5-small-elasticsearch"},owner:{type:"keyword"},enabled:{type:"boolean"},graphId:{type:"keyword"},packageName:{type:"keyword"},assistantIds:{type:"keyword"},assistants:{properties:{id:{type:"keyword"},enabled:{type:"boolean"},condition:{type:"text",index:!1},runAlways:{type:"boolean"}}},sharedContext:{properties:{context:{type:"text"},priority:{type:"keyword"},policy:{properties:{shareToAssistants:{type:"boolean"},shareToDecider:{type:"boolean"},useAssistantProfiles:{type:"boolean"},applyOnDelegation:{type:"boolean"}}}}},webhookToken:{type:"keyword",index:!1},webhookEnabled:{type:"boolean"},webhookCreatedAt:{type:"date"},scheduleCron:{type:"keyword",index:!1},mcpEnabled:{type:"boolean"},mcpSigningSecret:{type:"keyword",index:!1},mcpExposeAssistants:{type:"boolean"},mcpAllowUnsigned:{type:"boolean"},mcpCreatedAt:{type:"date"},channelCommands:{type:"object",dynamic:!0},created_at:{type:"date"},updated_at:{type:"date"}}}}}),console.log("[Teams API] Created index:",w)}catch(s){s.meta?.statusCode!==400&&console.error("[Teams API] Error creating index:",s)}try{await p.indices.putMapping({index:w,body:{properties:{webhookToken:{type:"keyword",index:!1},webhookEnabled:{type:"boolean"},webhookCreatedAt:{type:"date"},scheduleCron:{type:"keyword",index:!1},mcpEnabled:{type:"boolean"},mcpSigningSecret:{type:"keyword",index:!1},mcpExposeAssistants:{type:"boolean"},mcpAllowUnsigned:{type:"boolean"},mcpCreatedAt:{type:"date"},channelCommands:{type:"object",dynamic:!0}}}})}catch(s){String(s?.message||"").includes("illegal_argument_exception")||console.warn("[Teams API] putMapping warning for webhook fields:",s?.message)}try{await p.indices.putMapping({index:w,body:{properties:{search_semantic:{type:"semantic_text",inference_id:".multilingual-e5-small-elasticsearch"},name:{type:"keyword",copy_to:"search_semantic"},description:{type:"text",copy_to:"search_semantic"}}}})}catch(s){String(s?.message||"").includes("illegal_argument_exception")||console.warn("[Teams API] putMapping warning for search_semantic field:",s?.message)}}import{getTeamById as ds,getTeamWebhookConfig as ls,getTeamMcpConfig as ms,getTeamSharedContext as us}from"../core/services/teams-service";async function E(s){if(!s||s.length===0)return[];const t=new Map(s.map(n=>[n.id,n])),e=s.map(n=>n.id);try{return(await p.mget({index:v,body:{ids:e}})).docs.filter(a=>a.found).map(a=>{const o=t.get(a._id);return{id:a._id,name:a._source?.name||"Unknown",topic:a._source?.topic,enabled:o?.enabled??!0,condition:o?.condition??null,runAlways:o?.runAlways??!1}})}catch(n){return console.error("[Teams API] Error joining assistants:",n),e.map(a=>({id:a,name:a,enabled:t.get(a)?.enabled??!0}))}}function fe(s){return s==="clusters"||s==="stack_expert"?["clusters","stack_expert"]:[s]}async function he(s,t){const e=fe(s);console.log(`[Teams API] Package filter: ${s} \u2192 [${e.join(", ")}]${t?` owner: ${t}`:""}`);try{const a=(await p.search({index:".stkxp_platforms",body:{query:{terms:{"config.packageName":e}},size:1e4,_source:["name"]}})).hits.hits.map(g=>g._source?.name).filter(Boolean),o=[...new Set(a)];if(o.includes("stack_expert")&&o.push("clusters"),console.log(`[Teams API] Found ${a.length} package servers for packageName: ${e.join(",")} \u2192 [${o.join(", ")}]`),a.length===0)return new Set;const r={bool:{should:o.map(g=>({term:{"mcp_servers_policy.servers.name.keyword":g}})),minimum_should_match:1}},d=t?{bool:{must:[r,{bool:{should:[{term:{owner:"stkxp"}},{term:{owner:t}}],minimum_should_match:1}}]}}:{bool:{must:[r]}},c=await p.search({index:v,body:{query:d,size:1e4,_source:[]}}),i=new Set(c.hits.hits.map(g=>g._id));return console.log(`[Teams API] Found ${i.size} visible assistants for packageName: ${e.join(",")}`),i}catch(n){return console.error("[Teams API] Error fetching visible assistants for packageName filter:",n),null}}function H(s){return Array.isArray(s.assistants)&&s.assistants.length>0&&s.assistants[0]?.id!==void 0?s.assistants.map(t=>({id:t.id,enabled:t.enabled!==!1,condition:t.condition??null,runAlways:t.runAlways??!1})):(s.assistantIds||[]).map(t=>({id:t,enabled:!0,condition:null,runAlways:!1}))}async function ye(s){try{const[t,e]=await Promise.all([p.search({index:v,body:{query:{term:{owner:s}},size:1e4,_source:[]}}),ue.getAllPackagesFromAllPlatforms(s,!1).catch(()=>({packages:[],errors:[]}))]),n=t.hits.hits.map(d=>d._id),a=[...new Set(e.packages.filter(d=>d.platforms?.some(c=>c.status==="enrolled")).map(d=>d.name).filter(Boolean))];a.includes("stack_expert")&&a.push("clusters"),console.log(`[Teams API] User "${s}": ${a.length} enrolled packages from platforms`,a);let o=[];a.length>0&&(o=(await p.search({index:".stkxp_platforms",body:{query:{terms:{"config.packageName":a}},size:1e4,_source:["name"]}})).hits.hits.map(c=>c._source?.name).filter(Boolean));let r=[];return o.length>0&&(r=(await p.search({index:v,body:{query:{bool:{should:o.map(c=>({term:{"mcp_servers_policy.servers.name.keyword":c}})),minimum_should_match:1}},size:1e4,_source:[]}})).hits.hits.map(c=>c._id)),console.log(`[Teams API] User "${s}": ${a.length} enrolled packages, ${o.length} MCP servers, ${n.length} owned assistants, ${r.length} MCP-linked assistants`),{userMcpPackageNames:a,userOwnedAssistantIds:n,mcpLinkedAssistantIds:r}}catch(t){return console.error("[Teams API] Error fetching user visibility data:",t),{userMcpPackageNames:[],userOwnedAssistantIds:[],mcpLinkedAssistantIds:[]}}}async function we(s,t){try{const e=L.parse(s.query);console.log(`[Teams API] getTeams query: ${JSON.stringify(e)}`);const n=[];if(e.enabled!==void 0&&n.push({term:{enabled:e.enabled}}),e.search){const k=e.search.toLowerCase().trim(),T=k.split("||").map(_=>_.trim()).filter(Boolean),I=T[0]||k,S=[];let R=0,M=5;S.push({multi_match:{query:`*${I}*`,fields:["name^5","description^2"],type:"best_fields",operator:"or",minimum_should_match:"60%",fuzziness:"AUTO",boost:3}}),S.push({match:{description:{query:e.search}}}),S.push({semantic:{field:"search_semantic",query:e.search,boost:2}});for(const _ of T)S.push({match_phrase:{name:{query:_,slop:R++,boost:Math.max(M--,1)}}});n.push({bool:{should:S,minimum_should_match:1}})}if(e.currentUser&&e.includeAllSystem)n.push({bool:{should:[{term:{owner:e.currentUser}},{term:{owner:"stkxp"}}],minimum_should_match:1}});else if(e.currentUser&&e.includeOwned)n.push({term:{owner:e.currentUser}});else if(e.currentUser){const{mcpLinkedAssistantIds:k}=await ye(e.currentUser);n.push({term:{owner:e.currentUser}}),k.length>0?n.push({terms:{assistantIds:k}}):n.push({term:{assistantIds:"__none__"}})}else if(e.owners){const k=e.owners.split(",").map(T=>T.trim()).filter(Boolean);k.length>0&&n.push({terms:{owner:k}})}let a=null;if(e.packageName&&(a=await he(e.packageName,e.currentUser),a!==null)){if(a.size===0)return t.json({success:!0,count:0,total:0,page:e.page||1,pageSize:e.pageSize||25,totalPages:0,teams:[]});n.push({terms:{assistantIds:Array.from(a)}})}e.assistantId&&n.push({term:{assistantIds:e.assistantId}});const o=e.page||1,r=e.pageSize||25,d=(o-1)*r,c={name:"name",updated_at:"updated_at"},i=String(s.query.sortField||"name"),g=c[i]||"name",b=s.query.sortOrder==="desc"?"desc":"asc",m=g==="updated_at"?[{updated_at:{order:b,unmapped_type:"date"}}]:[{[g]:{order:b,unmapped_type:"keyword"}}],h={index:w,body:{track_scores:!0,query:{bool:{must:n.length>0?n:[{match_all:{}}]}},sort:[{_score:{order:"desc"}},...m],from:d,size:r,_source:{excludes:["webhookToken"]}}};console.log("[Teams API] getTeams query:",JSON.stringify(h.body.query));const f=await p.search(h),l=typeof f.hits.total=="number"?f.hits.total:f.hits.total?.value||0,y=await Promise.all(f.hits.hits.map(async k=>{const T=k._source,I=Array.isArray(T.assistants)&&T.assistants.length>0?T.assistants:(T.assistantIds||[]).map(_=>({id:_,enabled:!0})),S=a?I.filter(_=>a.has(_.id)):I,R=e.packageName?S.filter(_=>_.enabled!==!1):S,M=await E(R);return{id:k._id,...T,assistants:M}})),x=e.packageName?y.filter(k=>k.assistants.length>0):y;t.json({success:!0,count:x.length,total:l,page:o,pageSize:r,totalPages:Math.ceil(l/r),teams:x})}catch(e){if(console.error("[Teams API] Error fetching teams:",e),e instanceof u.ZodError)return t.status(400).json({success:!1,error:"Invalid query parameters",details:e.errors});if(e.meta?.statusCode===404)return t.json({success:!0,count:0,total:0,page:1,pageSize:25,totalPages:0,teams:[]});t.status(500).json({success:!1,error:"Failed to fetch teams",message:e.message})}}async function be(s,t){try{const{id:e}=s.params,n=await p.get({index:w,id:e,_source_excludes:["webhookToken"]});if(!n.found)return t.status(404).json({success:!1,error:"Team not found"});const a=n._source,o=Array.isArray(a.assistants)&&a.assistants.length>0?a.assistants:(a.assistantIds||[]).map(d=>({id:d,enabled:!0})),r=await E(o);t.json({success:!0,team:{id:n._id,...a,assistants:r}})}catch(e){if(console.error("[Teams API] Error fetching team:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to fetch team",message:e.message})}}async function ke(s,t){try{await ge();const e=s.body,n=new Date().toISOString(),a=H(e),o=a.map(b=>b.id),d=s.auth?.username||e.owner||"system",c={name:e.name,description:e.description||"",assistantIds:o,assistants:a,owner:d,enabled:e.enabled!==void 0?e.enabled:!0,created_at:n,updated_at:n};e.sharedContext?.context?.trim()&&(c.sharedContext={context:e.sharedContext.context.trim(),priority:e.sharedContext.priority==="after"?"after":"before",...e.sharedContext.policy?{policy:e.sharedContext.policy}:{}}),e.graphId?.trim()&&(c.graphId=e.graphId.trim()),console.log("[Teams API] Creating team:",c.name);const i=await p.index({index:w,body:c,refresh:"wait_for"}),g=await E(a);t.status(201).json({success:!0,team:{id:i._id,...c,assistants:g}})}catch(e){console.error("[Teams API] Error creating team:",e),t.status(500).json({success:!1,error:"Failed to create team",message:e.message})}}async function Ie(s,t){try{const{id:e}=s.params,n=await A(s,t,p,w,e);if(!n)return;await de("team",e,n.source,"manual_edit",C(s)||"system");const a=s.body,o=H(a),r=o.map(g=>g.id),d={name:a.name,description:a.description||"",assistantIds:r,assistants:o,owner:a.owner||"system",enabled:a.enabled!==void 0?a.enabled:!0,updated_at:new Date().toISOString()};a.sharedContext?.context?.trim()?d.sharedContext={context:a.sharedContext.context.trim(),priority:a.sharedContext.priority==="after"?"after":"before",...a.sharedContext.policy?{policy:a.sharedContext.policy}:{}}:d.sharedContext=null,d.graphId=a.graphId?.trim()||null,console.log("[Teams API] Updating team:",e);const c=await p.update({index:w,id:e,body:{doc:d},refresh:"wait_for"}),i=await E(o);t.json({success:!0,team:{id:c._id,...d,assistants:i}})}catch(e){if(console.error("[Teams API] Error updating team:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to update team",message:e.message})}}async function Te(s,t){try{const{id:e}=s.params;if(!await A(s,t,p,w,e))return;console.log("[Teams API] Deleting team:",e),await p.delete({index:w,id:e,refresh:"wait_for"}),t.json({success:!0,message:"Team deleted successfully"})}catch(e){if(console.error("[Teams API] Error deleting team:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to delete team",message:e.message})}}async function Ae(s,t){try{const{id:e}=s.params;if(!await A(s,t,p,w,e))return;const a=s.body?.enabled!==!1,o=O(32).toString("hex"),r=new Date().toISOString();await p.update({index:w,id:e,body:{doc:{webhookToken:o,webhookEnabled:a,webhookCreatedAt:r,updated_at:r}},refresh:"wait_for"}),console.log(`[Teams API] Generated webhook token for team: ${e}`),t.json({success:!0,teamId:e,webhookToken:o,webhookEnabled:a,webhookCreatedAt:r,webhookUrl:`/api/webhooks/team/${e}`})}catch(e){if(console.error("[Teams API] Error generating webhook token:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to generate webhook token",message:e.message})}}async function Ce(s,t){try{const{id:e}=s.params;if(!await A(s,t,p,w,e))return;const a=new Date().toISOString();await p.update({index:w,id:e,body:{doc:{webhookToken:null,webhookEnabled:!1,webhookCreatedAt:null,updated_at:a}},refresh:"wait_for"}),console.log(`[Teams API] Revoked webhook token for team: ${e}`),t.json({success:!0,teamId:e,webhookEnabled:!1})}catch(e){if(console.error("[Teams API] Error revoking webhook token:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to revoke webhook token",message:e.message})}}async function xe(s,t){try{const{id:e}=s.params,n=await A(s,t,p,w,e);if(!n)return;const a=n.source;if(!a.mcpEnabled)return t.status(400).json({success:!1,error:"MCP server is not enabled for this team \u2014 generate/enable it in the MCP App tab first."});const o=a.name||"team",r=`${s.protocol}://${s.get("host")}/api/mcp/team/${e}`,d=await oe({teamName:o,teamId:e,mcpUrl:r,allowUnsigned:a.mcpAllowUnsigned===!0}),c=`mcp-app-tester-${re(o)}.zip`;t.set({"Content-Type":"application/zip","Content-Disposition":`attachment; filename="${c}"`}),t.send(d)}catch(e){console.error("[Teams API] Error generating MCP App tester package:",e),t.status(500).json({success:!1,error:"Failed to generate test client package",message:e.message})}}async function _e(s,t){try{const{id:e}=s.params;if(!await A(s,t,p,w,e))return;const a=s.body?.enabled!==!1,o=s.body?.exposeAssistants!==!1,r=s.body?.allowUnsigned===!0,d=typeof s.body?.signingSecret=="string"?s.body.signingSecret.trim():void 0;if(d!==void 0&&d.length<8)return t.status(400).json({success:!1,error:"signingSecret must be at least 8 characters (use your Slack app Signing Secret to integrate with Slack)"});const c=d||O(32).toString("hex"),i=new Date().toISOString();await p.update({index:w,id:e,body:{doc:{mcpSigningSecret:c,mcpEnabled:a,mcpExposeAssistants:o,mcpAllowUnsigned:r,mcpCreatedAt:i,updated_at:i}},refresh:"wait_for"}),console.log(`[Teams API] Generated MCP signing secret for team: ${e}`),t.json({success:!0,teamId:e,mcpSigningSecret:c,mcpEnabled:a,mcpExposeAssistants:o,mcpAllowUnsigned:r,mcpCreatedAt:i,mcpUrl:`/api/mcp/team/${e}`})}catch(e){if(console.error("[Teams API] Error generating MCP config:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to generate MCP config",message:e.message})}}async function Se(s,t){try{const{id:e}=s.params;if(!await A(s,t,p,w,e))return;const a=new Date().toISOString();await p.update({index:w,id:e,body:{doc:{mcpSigningSecret:null,mcpEnabled:!1,mcpAllowUnsigned:!1,mcpCreatedAt:null,updated_at:a}},refresh:"wait_for"}),console.log(`[Teams API] Revoked MCP config for team: ${e}`),t.json({success:!0,teamId:e,mcpEnabled:!1})}catch(e){if(console.error("[Teams API] Error revoking MCP config:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to revoke MCP config",message:e.message})}}async function Pe(s,t){try{const{id:e,platformId:n}=s.params,a=await A(s,t,p,w,e);if(!a)return;const o=Array.isArray(s.body?.commandIds)?s.body.commandIds:[],r=typeof s.body?.namespace=="string"?s.body.namespace:void 0,d=s.body?.blockKitMode===!0,c=a.source.owner;for(const m of o){const h=await le(c,n,m,e);if(h)return t.status(409).json({success:!1,message:`Command is already assigned to team "${h.teamName}"`})}const i=new Date().toISOString(),b={...a.source.channelCommands??{},[n]:{commandIds:o,namespace:r,blockKitMode:d}};await p.update({index:w,id:e,body:{doc:{channelCommands:b,updated_at:i}},refresh:"wait_for"}),console.log(`[Teams API] Updated channel commands for team ${e} / platform ${n}`),t.json({success:!0,teamId:e,platformId:n,commandIds:o,namespace:r,blockKitMode:d})}catch(e){if(console.error("[Teams API] Error setting channel commands:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to set channel commands",message:e.message})}}async function ve(s,t){try{const{id:e,platformId:n}=s.params,a=await A(s,t,p,w,e);if(!a)return;const o=new Date().toISOString(),r={...a.source.channelCommands??{}};delete r[n],await p.update({index:w,id:e,body:{doc:{channelCommands:r,updated_at:o}},refresh:"wait_for"}),console.log(`[Teams API] Cleared channel commands for team ${e} / platform ${n}`),t.json({success:!0,teamId:e,platformId:n})}catch(e){if(console.error("[Teams API] Error clearing channel commands:",e),e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to clear channel commands",message:e.message})}}async function je(s,t){try{const e=C(s);if(!e)return t.status(401).json({success:!1,message:"Unauthorized"});const n=s.query.platformId;if(!n)return t.status(400).json({success:!1,message:"platformId is required"});const a=await me(e,n);t.json({success:!0,body:{ownership:a}})}catch(e){console.error("[Teams API] Error fetching channel command ownership:",e),t.status(500).json({success:!1,message:e.message||"Failed to fetch channel command ownership"})}}async function Ee(s,t){try{const{id:e}=s.params,n=await A(s,t,p,w,e);if(!n)return;const a=n.source.owner||C(s)||"system",o=j(P),r=await $([{index:w,esId:e}],a,o),d=te(r);t.json({success:!0,graph:d})}catch(e){if(console.error("[Teams API] Error building full-structure graph:",e),/Required dependencies/.test(e?.message??""))return t.status(422).json({success:!1,error:e.message});if(e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to build full-structure graph",message:e.message})}}async function Re(s,t){try{const{id:e}=s.params,n=await A(s,t,p,w,e);if(!n)return;const a=n.source.owner||C(s)||"system",o=n.source.name||"(unnamed team)",r=j(P),d=await $([{index:w,esId:e}],a,r),c=J(d,{exportedAt:new Date().toISOString(),rootName:o}),i=d.docs.filter(m=>m.key==="platform"&&z(m.source?.type)&&m.source?.owner===a).map(m=>m.esId),g=d.docs.some(m=>m.key==="tool"&&m.reachedVia?.includes(U)&&m.source?.owner===a);let b;if(i.length>0||g){const m=await D(d,a,e);if(!m)return t.status(422).json({success:!1,error:"Failed to create the MCP gateway grant: could not connect to one or more MCP server platforms in this team."});for(const f of c.closure.docs)f.key==="platform"&&i.includes(f.esId)&&(f.source.config={...f.source.config,serverType:"gateway",url:""});const h=c.closure.docs.filter(f=>f.key==="tool"&&f.source?.type==="mcp_remote"&&(i.includes(f.source?.platformId)||i.includes(f.source?.mcpServerId))).length;h>0&&(c.closure.docs=c.closure.docs.filter(f=>!(f.key==="tool"&&f.source?.type==="mcp_remote"&&(i.includes(f.source?.platformId)||i.includes(f.source?.mcpServerId)))),c.summary.totalDocs-=h,c.summary.byEntity.tool=(c.summary.byEntity.tool??0)-h),c.mcpGateway={grantId:m.grantId,platformIds:m.platformIds},b=m.secret}console.log(`[Teams API] ${C(s)??"?"} exported team "${o}" (${e}): ${c.summary.totalDocs} docs, ${d.unresolved.length} unresolved ref(s)`+(b?`, gateway grant for ${i.length} platform(s)`:"")),t.json({success:!0,bundle:c,gatewaySecret:b,unresolved:d.unresolved})}catch(e){if(console.error("[Teams API] Error exporting team:",e),/Required dependencies/.test(e?.message??""))return t.status(422).json({success:!1,error:e.message});if((e?.message??"").startsWith(N))return t.status(422).json({success:!1,error:e.message});if(e.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to export team",message:e.message})}}const W=u.object({secret:u.string().min(8)});async function Me(s,t){const e=W.safeParse(s.body);if(!e.success)return t.status(400).json({success:!1,error:'Body must include a "secret" of at least 8 characters'});const{secret:n}=e.data;try{const{id:a}=s.params,o=await A(s,t,p,w,a);if(!o)return;const r=o.source.owner||C(s)||"system",d=o.source.name||"(unnamed team)",c=j(P),i=await $([{index:w,esId:a}],r,c),g=i.docs.filter(l=>l.key==="platform"&&z(l.source?.type)&&l.source?.owner===r).map(l=>l.esId),b=i.docs.some(l=>l.key==="tool"&&l.reachedVia?.includes(U)&&l.source?.owner===r);let m,h;if(g.length>0||b){const l=await D(i,r,a);if(!l)return t.status(422).json({success:!1,error:"Failed to create the MCP gateway grant: could not connect to one or more MCP server platforms in this team."});for(const y of i.docs)y.key==="platform"&&g.includes(y.esId)&&(y.source.config={...y.source.config,serverType:"gateway",url:""});i.docs=i.docs.filter(y=>!(y.key==="tool"&&y.source?.type==="mcp_remote"&&(g.includes(y.source?.platformId)||g.includes(y.source?.mcpServerId)))),h={grantId:l.grantId,platformIds:l.platformIds},m=l.secret}const f=se(i,{exportedAt:new Date().toISOString(),rootName:d,passphrase:n,gatewaySecret:m});f.mcpGateway=h,console.log(`[Teams API] ${C(s)??"?"} exported (encrypted) team "${d}" (${a}): ${f.summary.totalDocs} docs, ${i.unresolved.length} unresolved ref(s)`),t.json({success:!0,bundle:f,unresolved:i.unresolved})}catch(a){if(console.error("[Teams API] Error exporting team (encrypted):",a),/Required dependencies/.test(a?.message??""))return t.status(422).json({success:!1,error:a.message});if((a?.message??"").startsWith(N))return t.status(422).json({success:!1,error:a.message});if(a.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});t.status(500).json({success:!1,error:"Failed to export team (encrypted)",message:a.message})}}async function $e(s,t){try{const e=C(s);if(!e)return t.status(401).json({success:!1,error:"Unauthorized"});const n=(s.body&&s.body.bundle)??s.body,a=s.body&&typeof s.body.gatewaySecret=="string"?s.body.gatewaySecret:void 0;let o;try{o=Y(n)}catch(g){const b=g instanceof ee?g.message:"Invalid bundle";return t.status(400).json({success:!1,error:b})}let r;if(o.mcpGateway){if(!a)return t.status(400).json({success:!1,error:"This bundle requires a gateway key to activate its MCP server platform(s). Paste the key you received from the exporting owner."});const g=await ie(o.mcpGateway.grantId,a,e);if("error"in g)return t.status(400).json({success:!1,error:g.error});r=g.gatewayToken}const d=j(P),c=Z(P),i=await X(o.closure,e,d,c,{blankSecrets:!0});if(o.mcpGateway&&r){const g=process.env.MCP_GATEWAY_HOST||"https://mcp.erretegia.com",b=new Date().toISOString();for(const h of i.created){if(h.key!=="platform"||!o.mcpGateway.platformIds.includes(h.oldEsId))continue;const f=`${g}/mcp/gateway:${o.mcpGateway.grantId}:${h.oldEsId}`;await P.update({index:h.index,id:h.newEsId,body:{doc:{managedType:"managed",config:{serverType:"gateway",url:f,authKey:r}}},refresh:!0});try{await ce(h.newEsId,{id:h.newEsId,name:h.name||"MCP Server",type:"MCPServer",managedType:"self-hosted",owner:e,created:b,updated:b,enabled:!0,config:{serverType:"gateway",url:f,authKey:r}},e)}catch(l){console.warn(`[Teams API] Post-import gateway tool sync failed for platform ${h.newEsId}: ${l.message}`)}}const m=`personal:${o.sourceOwner}`;if(o.mcpGateway.platformIds.includes(m)){const h=`${g}/mcp/gateway:${o.mcpGateway.grantId}:${m}`,f=K();await P.index({index:".stkxp_platforms",id:f,document:{id:f,name:"My MCP Tools (gateway)",type:"MCPServer",managedType:"managed",owner:e,enabled:!0,created:b,updated:b,config:{serverType:"gateway",url:h,authKey:r,protocol:"http"}},refresh:!0});const l=`__personal_tools__:${o.sourceOwner}`;for(const y of i.created){if(y.key!=="assistant")continue;const x=o.closure.docs.find(I=>I.key==="assistant"&&I.esId===y.oldEsId),k=x?.source?.mcp_servers_policy?.servers;if(!Array.isArray(k)||!k.some(I=>I?.id===l))continue;const T=k.map(I=>I?.id===l?{...I,id:f}:I);await P.update({index:y.index,id:y.newEsId,body:{doc:{mcp_servers_policy:{...x.source.mcp_servers_policy,servers:T}}},refresh:!0})}}}console.log(`[Teams API] ${e} imported team "${o.rootName}" (from owner "${o.sourceOwner}"): ${i.created.length} created, ${i.skipped.length} reused, ${i.referenced.length} referenced, ${i.droppedRefs.length} refs dropped`),t.status(201).json({success:!0,rootName:o.rootName,report:i,unresolved:o.closure.unresolved})}catch(e){if(console.error("[Teams API] Error importing team:",e),/Required dependencies/.test(e?.message??""))return t.status(422).json({success:!1,error:e.message});t.status(500).json({success:!1,error:"Failed to import team",message:e.message})}}async function Oe(s,t){try{const{id:e}=s.params;if(!C(s))return t.status(401).json({success:!1,error:"Unauthorized"});let a;try{a=(await p.get({index:w,id:e}))._source}catch(m){if(m?.meta?.statusCode===404)return t.status(404).json({success:!1,error:"Team not found"});throw m}const r=(a?.assistants||[]).filter(m=>m.enabled!==!1).map(m=>m.id);if(r.length===0)return t.json({success:!0,teamId:e,teamName:a?.name,singlePassMax:0,assistants:[]});const c=(await p.mget({index:v,body:{ids:r}})).docs.filter(m=>m.found),i=new Map,g=[];let b=0;for(const m of c){const h=await ne(p,m._id,m._source||{},i);b+=h.singlePassMax,g.push(h)}t.json({success:!0,teamId:e,teamName:a?.name,singlePassMax:b,assistants:g,note:"Single-pass upper bound. Tool loops can exceed it \u2014 multiply by an empirical factor (2-3\xD7) when sizing a budget."})}catch(e){console.error("[Teams API] Error computing token projection:",e),t.status(500).json({success:!1,error:"Failed to compute token projection",message:e.message})}}async function Ue(s,t){try{const{id:e}=s.params,n=C(s);if(!n)return t.status(401).json({success:!1,error:"Unauthorized"});const a=Math.min(parseInt(s.query.limit||"20",10)||20,100),o=[{term:{teamId:e}}];V(s)||o.push({term:{username:n}});const c=((await p.search({index:pe,body:{size:a,sort:[{createdAt:{order:"desc"}}],_source:["id","title","createdAt","metadata.tokenUsage","assistants"],query:{bool:{must:o}}}})).hits.hits||[]).map(l=>{const y=l._source||{},x=y.metadata?.tokenUsage||{};return{chatId:l._id,title:y.title,createdAt:y.createdAt,totalTokens:Number(x.totalTokens||0),inputTokens:Number(x.totalInputTokens||0),outputTokens:Number(x.totalOutputTokens||0),assistantCount:Array.isArray(y.assistants)?y.assistants.length:0}}),i=c.map(l=>l.totalTokens).filter(l=>l>0),g=i.reduce((l,y)=>l+y,0),b=[...i].sort((l,y)=>l-y),m=b.length?b[Math.min(b.length-1,Math.floor(b.length*.95))]:0,h=c.map(l=>l.inputTokens),f=c.map(l=>l.outputTokens);t.json({success:!0,runCount:i.length,avgTotalTokens:i.length?Math.round(g/i.length):0,maxTotalTokens:i.length?Math.max(...i):0,p95TotalTokens:m,avgInputTokens:h.length?Math.round(h.reduce((l,y)=>l+y,0)/h.length):0,avgOutputTokens:f.length?Math.round(f.reduce((l,y)=>l+y,0)/f.length):0,recentRuns:c})}catch(e){console.error("[Teams API] Error fetching token stats:",e),t.status(500).json({success:!1,error:"Failed to fetch token stats",message:e.message})}}const De=u.object({name:u.string().describe("Team name"),description:u.string().optional().describe("Human description"),assistantIds:u.array(u.string()).optional().describe("Ids of assistants in the team"),assistants:u.array(u.any()).optional().describe("Full assistant objects (alternative to assistantIds)"),graphId:u.string().optional().describe("Id of a .stkxp_graphs coordination graph"),enabled:u.boolean().optional().describe("Enabled flag (default true)"),sharedContext:u.any().optional().describe("Shared context object injected into every assistant")}).passthrough(),Ne=u.object({name:u.string().optional().describe("Team name"),description:u.string().optional().describe("Human description"),assistantIds:u.array(u.string()).optional().describe("Ids of assistants in the team"),assistants:u.array(u.any()).optional().describe("Full assistant objects (alternative to assistantIds)"),graphId:u.string().optional().describe("Id of a .stkxp_graphs coordination graph"),enabled:u.boolean().optional().describe("Enabled flag (default true)"),sharedContext:u.any().optional().describe("Shared context object injected into every assistant")}).passthrough(),rs=[{method:"get",path:"/api/teams",handler:we,validate:{query:L},openapi:{summary:"List teams for the caller",description:"Returns all teams owned by the authenticated user. A team groups assistants and optionally references a coordination graph (`graphId`) for multi-assistant orchestration. `search` runs a hybrid lexical + semantic query (semantic_text field `search_semantic`).",tags:["teams"]}},{method:"post",path:"/api/teams/import",handler:$e,openapi:{summary:"Import a team from an export bundle",description:"Reconstructs a team and its full dependency closure under the authenticated caller from a bundle produced by `GET /api/teams/:id/export`. New ids are minted for owned docs (links rebuilt), shared/system docs are referenced in place, same-named docs the caller already owns are reused, and secrets stay blank (re-enter LLM keys / platform authKeys and regenerate the webhook token afterwards).",tags:["teams"],audience:"internal"}},{method:"get",path:"/api/teams/channel-command-ownership",handler:je,openapi:{summary:"Get command\u2192team ownership map for a platform",description:"Query param `platformId`. Returns `{ [commandId]: { teamId, teamName } }` for every command on that platform currently owned by one of the caller's teams \u2014 used by the Team Edit Channels tab to disable already-claimed commands.",tags:["teams","channels"],audience:"internal"}},{method:"get",path:"/api/teams/:id",handler:be,openapi:{summary:"Get a team by id",tags:["teams"]}},{method:"get",path:"/api/teams/:id/export",handler:Re,openapi:{summary:"Export a team as a portable bundle",description:"Resolves the team's full transitive dependency closure (assistants, graphs, sub-graphs, prompts, node templates, routing rules, models, providers, platforms, tools) and returns a secret-free JSON bundle for re-import via `POST /api/teams/import`. Limited to teams the caller owns.",tags:["teams"],audience:"internal"}},{method:"post",path:"/api/teams/:id/export-encrypted",handler:Me,validate:{body:W},openapi:{summary:"Export a team as a fully self-contained, passphrase-encrypted bundle",description:"Like GET /api/teams/:id/export, but instead of blanking secrets (LLM keys, platform creds, webhook token), encrypts them with the caller-supplied passphrase (AES-256-GCM per field). Intended for the `stkxp export` CLI, which produces a bundle deployable standalone via `stkxp deploy` \u2014 see docs/superpowers/specs/2026-09-15-team-standalone-export-design.md.",tags:["teams"],audience:"internal"}},{method:"get",path:"/api/teams/:id/full-structure",handler:Ee,openapi:{summary:"Get a team's full structure as a flattened canvas graph",description:"Resolves the team's full transitive dependency closure (assistants, runtime graphs, nested subgraphs, tools, platforms, models, providers, routing rules) and returns it flattened into nodes/edges/groups for the Team Canvas Overview mode. Read-only. Limited to teams the caller owns.",tags:["teams"],audience:"internal"}},{method:"post",path:"/api/teams",handler:ke,validate:{body:De},openapi:{summary:"Create a team",description:"Creates a team with the supplied `name`, optional `description`, `assistantIds[]`, and optional `graphId` pointing to a `.stkxp_graphs` coordination graph.",tags:["teams"]}},{method:"put",path:"/api/teams/:id",handler:Ie,validate:{body:Ne},openapi:{summary:"Update a team",tags:["teams"]}},{method:"delete",path:"/api/teams/:id",handler:Te,openapi:{summary:"Delete a team",tags:["teams"]}},{method:"get",path:"/api/teams/:id/token-projection",handler:Oe,openapi:{summary:"Theoretical single-pass max token usage for a team",description:"Resolves each enabled assistant graph (`assistant.graphId`), applies `assistant.llm_overrides[node.type]` on top of node-level routing rules, and sums `maxTokens` across nodes with a routing rule. Single-pass upper bound only \u2014 tool loops can exceed it.",tags:["teams"],audience:"internal"}},{method:"get",path:"/api/teams/:id/token-stats",handler:Ue,openapi:{summary:"Aggregate token usage for the caller's recent runs of a team",description:"Reads `.stkxp_chats` filtered by `teamId` (and `username` unless superuser) and returns avg / max / p95 total tokens plus the last N runs. Used by the Team flyout to surface real consumption before setting a future budget.",tags:["teams"],audience:"internal"}},{method:"post",path:"/api/teams/:id/webhook-token",handler:Ae,openapi:{summary:"Generate (or rotate) a webhook token for a team",description:"Creates a new inbound webhook token. Returns the clear-text token **once** \u2014 the UI must surface it immediately to the operator. Subsequent fetches of the team do not expose the token. Optional body `{ enabled?: boolean }` (defaults to true) controls whether the webhook is active.",tags:["teams","webhooks"],audience:"internal"}},{method:"delete",path:"/api/teams/:id/webhook-token",handler:Ce,openapi:{summary:"Revoke the webhook token for a team",description:"Clears the webhook token and disables the webhook. POSTs to `/api/webhooks/team/:id` will be rejected with 401 until a new token is generated.",tags:["teams","webhooks"],audience:"internal"}},{method:"post",path:"/api/teams/:id/mcp-config",handler:_e,openapi:{summary:"Enable (or rotate) the MCP server for a team",description:'Owner-only. Generates a signing secret (returned once) and enables the per-team MCP endpoint at /api/mcp/team/:id. Body `{ enabled?: boolean, exposeAssistants?: boolean, signingSecret?: string }` \u2014 enabled/exposeAssistants default true; if `signingSecret` is provided (\u22658 chars) it is used verbatim (e.g. a Slack app Signing Secret to integrate with Slack), otherwise a random 32-byte secret is minted. `allowUnsigned` (default false) accepts unsigned requests \u2014 needed for Slack\'s MCP client, which sends no signature for auth_type "no_auth"; a signature, when present, is always verified.',tags:["teams","mcp"],audience:"internal"}},{method:"delete",path:"/api/teams/:id/mcp-config",handler:Se,openapi:{summary:"Disable the MCP server for a team",description:"Owner-only. Clears the signing secret and disables the MCP endpoint. Calls to /api/mcp/team/:id return 404 until re-enabled.",tags:["teams","mcp"],audience:"internal"}},{method:"post",path:"/api/teams/:id/channel-commands/:platformId",handler:Pe,openapi:{summary:"Claim one or more commands on a platform for a team",description:"Body `{ commandIds: string[], namespace?: string, blockKitMode?: boolean }`. Rejects with 409 if any commandId is already owned by a different team. A command can be owned by at most one team at a time, scoped to (platformId, commandId).",tags:["teams","channels"],audience:"internal"}},{method:"delete",path:"/api/teams/:id/channel-commands/:platformId",handler:ve,openapi:{summary:"Clear a team's channel-command config for a platform",description:"Removes this team's ownership of any commands claimed on the given platform, freeing them for other teams.",tags:["teams","channels"],audience:"internal"}},{method:"get",path:"/api/teams/:id/mcp-app-tester.zip",handler:xe,openapi:{summary:"Download a pre-configured MCP App test client for this team",description:"Owner-only. Generates a ZIP \u2014 a minimal fork of mcp-apps-tester wired to this team's ask_ tool, with Slack-style HMAC request signing baked in. Requires the team's MCP server to be enabled (mcpEnabled). Ships without the signing secret \u2014 the caller pastes their own copy into the generated .env.",tags:["teams","mcp"],audience:"internal"}}];export{ge as ensureTeamsIndex,ds as getTeamById,ms as getTeamMcpConfig,us as getTeamSharedContext,ls as getTeamWebhookConfig,rs as routes};
@@ -0,0 +1 @@
1
+ import s from"zod";import{getToolUsageStats as g,getToolCallsByRun as y,getToolFailures as f,getToolPerformance as h,getToolAnalytics as S}from"../core/services/tool-analytics-service";import{userToken as c}from"../services/auth";const m=s.object({start:s.string().optional(),end:s.string().optional()}),p=s.object({toolName:s.string()}),R=s.object({runId:s.string()}),b=s.object({toolName:s.string().optional(),limit:s.coerce.number().optional().default(100)});function u(o){const t=o.auth?.username;if(t)return t;let n=o.cookies?.token;if(!n){const r=o.headers.authorization;r&&r.startsWith("Bearer ")&&(n=r.substring(7))}return n?c(n)?.username||null:(console.warn("[tool-analytics] No token found in request"),null)}function l(o){let t=o.cookies?.token;if(!t){const e=o.headers.authorization;e&&e.startsWith("Bearer ")&&(t=e.substring(7))}return t?c(t)?.role==="superuser":!1}const I=async(o,t)=>{const n=u(o);if(!n){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=o.query,r=e.start&&e.end?{start:e.start,end:e.end}:void 0,a=l(o)?void 0:n,i=await g(r,a);t.status(200).json({body:{time:new Date().toISOString(),result:{tools:i,total:i.length}}})}catch(e){console.error("Error getting tool stats:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tool statistics"}}})}},T=async(o,t)=>{const n=u(o);if(!n){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=o.query,r=e.start&&e.end?{start:e.start,end:e.end}:void 0,a=l(o)?void 0:n,i=await S(r,a);t.status(200).json({body:{time:new Date().toISOString(),result:i}})}catch(e){console.error("Error getting tool analytics:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tool analytics"}}})}},w=async(o,t)=>{const n=u(o);if(!n){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=o.query,r=l(o)?void 0:n,a=await f(e.toolName,r,e.limit);t.status(200).json({body:{time:new Date().toISOString(),result:{failures:a,total:a.length}}})}catch(e){console.error("Error getting tool failures:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tool failures"}}})}},v=async(o,t)=>{const n=u(o);if(!n){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{toolName:e}=o.params,r=o.query,a=r.start&&r.end?{start:r.start,end:r.end}:void 0,i=l(o)?void 0:n,d=await h(e,a,i);if(!d){t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Tool not found or no data available"}}});return}t.status(200).json({body:{time:new Date().toISOString(),result:d}})}catch(e){console.error("Error getting tool performance:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tool performance"}}})}},j=async(o,t)=>{if(!u(o)){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{runId:e}=o.params,r=await y(e);t.status(200).json({body:{time:new Date().toISOString(),result:{tools:r,total:r.length}}})}catch(e){console.error("Error getting tool calls by run:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve tool calls"}}})}},H=[{method:"get",path:"/api/monitoring/tools/stats",handler:I,validate:{query:m}},{method:"get",path:"/api/monitoring/tools/analytics",handler:T,validate:{query:m}},{method:"get",path:"/api/monitoring/tools/failures",handler:w,validate:{query:b}},{method:"get",path:"/api/monitoring/tools/:toolName/performance",handler:v,validate:{params:p,query:m}},{method:"get",path:"/api/monitoring/tools/run/:runId",handler:j,validate:{params:R}}];export{H as routes};
@@ -0,0 +1 @@
1
+ import l from"jsonwebtoken";import{z as a}from"zod";import{Client as u}from"@elastic/elasticsearch";import{config as c}from"../core/config";import{snapshotToolVersion as d,rollbackToolVersion as p,listToolHistory as h}from"../core/services/tool-history-service";const y=new u(c.elasticsearch);function i(s){const t=s.auth?.username;if(t)return t;let o=s.cookies?.token;if(!o){const r=s.headers.authorization??"";o=r.startsWith("Bearer ")?r.slice(7):r||null}if(!o)return null;try{return l.decode(o)?.username??null}catch{return null}}async function b(s,t){const o=i(s);if(!o)return t.status(401).json({error:"Unauthorized"}),null;try{return((await y.security.getUser({username:o}))[o]?.roles??[]).includes("superuser")?o:(t.status(403).json({error:"Superuser access required"}),null)}catch(r){return console.error("[tool-history] role check failed:",r),t.status(500).json({error:"Internal server error"}),null}}async function e(s,t){const o=i(s);if(!o)return t.status(401).json({error:"Unauthorized"}),null;try{return o}catch(r){return console.error("[tool-history] role check failed:",r),t.status(500).json({error:"Internal server error"}),null}}const m=a.object({reason:a.string().min(1)}),w=[{method:"post",path:"/api/admin/tools/:id/history/snapshot",validate:{body:m},openapi:{summary:"Snapshot a tool definition into version history",description:"Writes the current .stkxp_tools doc + its latest Q into .stkxp_tools_history, capped at 20 versions/tool. Standalone \u2014 nothing calls this automatically yet.",tags:["admin","tool-history"],requiredRoles:["superuser"]},handler:async(s,t)=>{const o=await e(s,t);if(o)try{const{reason:r}=s.body,n=await d(s.params.id,r,o);t.json({success:!0,history:n})}catch(r){if(r?.meta?.statusCode===404){t.status(404).json({error:"Tool not found"});return}console.error("[tool-history] snapshot failed:",r),t.status(500).json({error:"Snapshot failed"})}}},{method:"post",path:"/api/admin/tools/:id/history/:historyId/rollback",openapi:{summary:"Roll back a tool definition to a snapshotted version",description:"Writes a .stkxp_tools_history snapshot's fields back onto the live .stkxp_tools doc.",tags:["admin","tool-history"],requiredRoles:["superuser"]},handler:async(s,t)=>{if(await e(s,t))try{await p(s.params.id,s.params.historyId),t.json({success:!0})}catch(o){if(o?.meta?.statusCode===404){t.status(404).json({error:"History snapshot not found"});return}console.error("[tool-history] rollback failed:",o),t.status(500).json({error:"Rollback failed"})}}},{method:"get",path:"/api/admin/tools/:id/history",openapi:{summary:"List version history for a tool",description:"Returns up to 20 most recent .stkxp_tools_history snapshots for a tool.",tags:["admin","tool-history"],requiredRoles:["superuser"]},handler:async(s,t)=>{if(await e(s,t))try{const o=await h(s.params.id);t.json({success:!0,items:o})}catch(o){console.error("[tool-history] list failed:",o),t.status(500).json({error:"List failed"})}}}];export{w as routes};
@@ -0,0 +1 @@
1
+ import w from"jsonwebtoken";import{z as r}from"zod";import{Client as h}from"@elastic/elasticsearch";import{config as R}from"../core/config";import{recomputeToolMetrics as q,recomputeAllToolMetrics as b,SCORING_WINDOWS as d}from"../core/services/tool-metrics-service";const c=new h(R.elasticsearch),m=".stkxp_tool_metrics";function g(o){const t=o.auth?.username;if(t)return t;let e=o.cookies?.token;if(!e){const s=o.headers.authorization??"";e=s.startsWith("Bearer ")?s.slice(7):s||null}if(!e)return null;try{return w.decode(e)?.username??null}catch{return null}}async function l(o,t){const e=g(o);if(!e)return t.status(401).json({error:"Unauthorized"}),!1;try{return((await c.security.getUser({username:e}))[e]?.roles??[]).includes("superuser")?!0:(t.status(403).json({error:"Superuser access required"}),!1)}catch(s){return console.error("[tool-metrics] role check failed:",s),t.status(500).json({error:"Internal server error"}),!1}}const j=r.object({window:r.enum(["7d","30d","90d"]).optional()}),_=r.object({window:r.enum(["7d","30d","90d"]).optional(),quadrant:r.enum(["boost","fix","disable","niche"]).optional(),system:r.string().optional(),sort:r.enum(["Q","volume","errorRate","emptyResultRate","avgDurationMs"]).optional(),order:r.enum(["asc","desc"]).optional(),size:r.coerce.number().int().min(1).max(1e3).optional(),from:r.coerce.number().int().min(0).optional()}),v=r.object({window:r.enum(["7d","30d","90d"]).optional()}),D=[{method:"post",path:"/api/admin/tool-metrics/recompute",validate:{body:j},openapi:{summary:"Recompute tool-quality metrics",description:"Runs the read-only tool-quality scoring job over `.stkxp_chat_tools` and upserts `.stkxp_tool_metrics`. Optionally scope to a single window; otherwise all windows (7d/30d/90d) are recomputed.",tags:["admin","tool-metrics"],requiredRoles:["superuser"]},handler:async(o,t)=>{if(await l(o,t))try{const{window:e}=o.body;if(e){const s=await q(e);t.json({success:!0,windows:{[e]:s.length}})}else{const s=await b();t.json({success:!0,windows:s})}}catch(e){console.error("[tool-metrics] recompute failed:",e),t.status(500).json({error:"Recompute failed"})}}},{method:"get",path:"/api/admin/tool-metrics",validate:{query:_},openapi:{summary:"List tool-quality metrics",description:"Returns per-tool quality (Q), volume (V), error/empty rates and action quadrant. Filter by window/quadrant/system; sort by Q, volume, errorRate, emptyResultRate or avgDurationMs.",tags:["admin","tool-metrics"],requiredRoles:["superuser"]},handler:async(o,t)=>{if(await l(o,t))try{const e=o.query,s=e.window??"30d",n=[{term:{window:s}}];e.quadrant&&n.push({term:{quadrant:e.quadrant}}),e.system&&n.push({term:{system:e.system}});const a=e.sort??"volume",i=e.order??"desc",u=await c.search({index:m,body:{query:{bool:{filter:n}},sort:[{[a]:{order:i,missing:"_last"}}],size:e.size??100,from:e.from??0}}),p=typeof u.hits?.total=="number"?u.hits.total:u.hits?.total?.value??0,y=(u.hits?.hits??[]).map(f=>({toolIds:[],hasDef:!1,defMatchCount:0,sampleErrors:[],scoreArgs:null,malformedArgRate:null,avgDurationMs:null,...f._source}));t.json({success:!0,window:s,total:p,items:y})}catch(e){if(e?.meta?.statusCode===404){t.json({success:!0,window:o.query.window??"30d",total:0,items:[]});return}console.error("[tool-metrics] list failed:",e),t.status(500).json({error:"List failed"})}}},{method:"get",path:"/api/admin/tool-metrics/summary",validate:{query:v},openapi:{summary:"Tool-quality quadrant summary",description:"Counts of tools per action quadrant (boost/fix/disable/niche) for a window.",tags:["admin","tool-metrics"],requiredRoles:["superuser"]},handler:async(o,t)=>{if(await l(o,t))try{const e=o.query.window??"30d",n=(await c.search({index:m,body:{size:0,query:{term:{window:e}},aggs:{by_quadrant:{terms:{field:"quadrant",size:10}}}}})).aggregations?.by_quadrant?.buckets??[],a={boost:0,fix:0,disable:0,niche:0};for(const i of n)a[i.key]=i.doc_count;t.json({success:!0,window:e,counts:a,windows:d})}catch(e){if(e?.meta?.statusCode===404){t.json({success:!0,window:o.query.window??"30d",counts:{boost:0,fix:0,disable:0,niche:0},windows:d});return}console.error("[tool-metrics] summary failed:",e),t.status(500).json({error:"Summary failed"})}}}];export{D as routes};