@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 i from"jsonwebtoken";import{z as n}from"zod";import{createCheckoutSession as u,createPortalSession as c,handleWebhookEvent as p}from"../services/billing-service";function a(s){const e=s.auth?.username;if(e)return e;let r=s.cookies?.token;if(!r){const t=s.headers.authorization??"";r=t.startsWith("Bearer ")?t.slice(7):t||null}if(!r)return null;try{return i.decode(r)?.username??null}catch{return null}}const l=n.object({plan:n.enum(["basic","enterprise"])}),y=[{method:"post",path:"/api/billing/checkout",validate:{body:l},openapi:{summary:"Create a Stripe Checkout Session",description:"Creates a Stripe Checkout Session for the requested plan (`basic` or `enterprise`) and returns a redirect URL the client should navigate to. Subscription activation happens via the Stripe webhook at `/buy-subscription`.",tags:["billing"]},handler:async(s,e)=>{const r=a(s);if(!r)return e.status(401).json({error:"Unauthorized"});const{plan:t}=s.body;try{const o=await u(r,t);return e.json({success:!0,url:o})}catch(o){return console.error("[billing/checkout] Error:",o),e.status(500).json({error:o.message||"Failed to create checkout session"})}}},{method:"post",path:"/api/billing/portal",openapi:{summary:"Open the Stripe customer portal",description:"Creates a Stripe Customer Portal session URL where the user can update payment methods, view invoices, and cancel/upgrade their subscription. Requires the user to already have a Stripe customer record (created at first checkout).",tags:["billing"]},handler:async(s,e)=>{const r=a(s);if(!r)return e.status(401).json({error:"Unauthorized"});try{const t=await c(r);return e.json({success:!0,url:t})}catch(t){return console.error("[billing/portal] Error:",t),e.status(500).json({error:t.message||"Failed to create portal session"})}}}];async function f(s,e){const r=s.headers["stripe-signature"];if(!r){e.status(400).json({error:"Missing stripe-signature header"});return}try{await p(s.body,r),e.json({received:!0})}catch(t){console.error("[Stripe webhook] Error:",t.message),e.status(400).json({error:t.message})}}export{y as routes,f as stripeWebhookHandler};
@@ -0,0 +1 @@
1
+ import*as i from"../core/services/chat-llms-service";import{userToken as m}from"../services/auth";import{loadModelIdPricingTable as d}from"../core/services/llm-pricing-service";import{lookupModelIdPrice as h,computeCostUsdFromPricing as p}from"../core/services/llm-pricing-pure";import o from"zod";const l=o.object({chatId:o.string()}),y=o.object({runId:o.string()});function u(e){const t=e.auth?.username;if(t)return t;let a=e.cookies?.token;if(!a){const r=e.headers.authorization;r?.startsWith("Bearer ")&&(a=r.substring(7))}return m(a)?.username||null}function g(e,t){const a=h(e,t.modelId);return p(a,{input:t.inputTokens??0,output:t.outputTokens??0,cacheRead:t.cacheReadTokens,cacheCreate:t.cacheCreationTokens})}async function c(e){if(e.length===0)return e;const t=await d();return e.map(a=>({...a,llms:(a.llms||[]).map(r=>({...r,costUsd:g(t,r)}))}))}const I=async(e,t)=>{if(!u(e)){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{chatId:r}=e.params,s=await i.getLLMsByChatId(r),n=await c(s);t.status(200).json({body:{time:new Date().toISOString(),result:{records:n,count:n.length}}})}catch(r){t.status(500).json({body:{time:new Date().toISOString(),result:{error:r.message}}})}},f=async(e,t)=>{if(!u(e)){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{runId:r}=e.params,s=await i.getLLMsByRunId(r),n=s?(await c([s]))[0]:null;t.status(200).json({body:{time:new Date().toISOString(),result:n}})}catch(r){t.status(500).json({body:{time:new Date().toISOString(),result:{error:r.message}}})}},k=[{method:"get",path:"/api/stack_expert/chat-llms/by-chat/:chatId",handler:I,validate:{params:l}},{method:"get",path:"/api/stack_expert/chat-llms/by-run/:runId",handler:f,validate:{params:y}}];export{k as routes};
@@ -0,0 +1 @@
1
+ import t from"zod";import*as n from"../core/services/chat-tools-service";import{userToken as l}from"../services/auth";const m=t.object({name:t.string(),input:t.any(),output:t.any(),timestamp:t.string()}),h=t.object({cluster:t.string().optional(),topic:t.string().optional(),llmProvider:t.string().optional(),llmModel:t.string().optional()}).optional(),g=t.object({chatId:t.string(),runId:t.string(),tools:t.array(m),metadata:h}),p=t.object({chatId:t.string()}),y=t.object({runId:t.string()});function i(r){const e=r.auth?.username;if(e)return e;let s=r.cookies?.token;if(!s){const a=r.headers.authorization;a&&a.startsWith("Bearer ")&&(s=a.substring(7))}return l(s)?.username||null}const I=async(r,e)=>{if(!i(r)){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{chatId:o}=r.params,a=await n.getToolsByChatId(o);e.status(200).json({body:{time:new Date().toISOString(),result:{tools:a,count:a.length}}})}catch(o){console.error("Error getting chat tools:",o),e.status(500).json({body:{time:new Date().toISOString(),result:{error:o.message||"Failed to retrieve chat tools"}}})}},S=async(r,e)=>{if(!i(r)){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{runId:o}=r.params,a=await n.getToolsByRunId(o);if(!a){e.status(404).json({body:{time:new Date().toISOString(),result:{error:"Tools not found"}}});return}e.status(200).json({body:{time:new Date().toISOString(),result:a}})}catch(o){console.error("Error getting tools by run ID:",o),e.status(500).json({body:{time:new Date().toISOString(),result:{error:o.message||"Failed to retrieve tools"}}})}},f=async(r,e)=>{const s=i(r);if(!s){e.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{chatId:o,runId:a,tools:c,metadata:u}=r.body,d=await n.createChatToolsRecord({chatId:o,runId:a,username:s,tools:c,metadata:u||{},createdAt:new Date().toISOString()});e.status(201).json({body:{time:new Date().toISOString(),result:d}})}catch(o){console.error("Error creating chat tools record:",o),e.status(500).json({body:{time:new Date().toISOString(),result:{error:o.message||"Failed to create chat tools record"}}})}},C=[{method:"get",path:"/api/stack_expert/chat-tools/by-chat/:chatId",handler:I,validate:{params:p}},{method:"get",path:"/api/stack_expert/chat-tools/by-run/:runId",handler:S,validate:{params:y}},{method:"post",path:"/api/stack_expert/chat-tools",handler:f,validate:{body:g}}];export{C as routes};
@@ -0,0 +1 @@
1
+ import{getTraceByRunId as n,getTracesByChatId as o}from"../core/services/chat-traces-service";import{userToken as u}from"../services/auth";function s(r){const t=r.auth?.username;if(t)return t;let a=r.cookies?.token;if(!a){const e=r.headers.authorization;e?.startsWith("Bearer ")&&(a=e.substring(7))}return u(a)?.username||null}const c=async(r,t)=>{if(!s(r)){t.status(401).json({error:"Unauthorized"});return}try{const e=await o(r.params.chatId);t.json({traces:e})}catch(e){t.status(500).json({error:e.message})}},i=async(r,t)=>{if(!s(r)){t.status(401).json({error:"Unauthorized"});return}try{const e=await n(r.params.runId);if(!e){t.status(404).json({error:"Trace not found"});return}t.json({trace:e})}catch(e){t.status(500).json({error:e.message})}},g=[{method:"get",path:"/api/stack_expert/chat-traces/by-chat/:chatId",handler:c},{method:"get",path:"/api/stack_expert/chat-traces/by-run/:runId",handler:i}];export{g as chatTracesRoutes};
@@ -0,0 +1 @@
1
+ import t from"zod";import{createChat as O,getUserChats as R,getChatById as S,getLastChatByTeam as z,getLastChatByAssistant as T,updateChat as P,updateMessageBlock as k,getMessageBlocks as U,MessageBlockNotFoundError as B,deleteChat as M,searchChats as x}from"../core/services/chats-service";import{repairBlock as E,BlockRepairError as F}from"../core/services/block-repair-service";import{userToken as N}from"../services/auth";import{generateChatTitle as L}from"../services/chat-title-generator";import{getSystemPromptForRun as H}from"../core/runtime/system-prompt-cache";import{completeChatRun as A}from"../core/services/chat-runs-service";import{calculateCost as G}from"../core/services/llm-pricing-service";import{validateShareToken as q}from"../core/services/share-service";const w=t.object({role:t.enum(["user","assistant","agent"]),content:t.any().optional().default(""),timestamp:t.string(),id:t.string(),toolsRunId:t.string().optional(),assistantId:t.string().optional(),assistantName:t.string().optional()}),C=t.object({assistantId:t.string(),assistantName:t.string(),llm:t.object({provider:t.string(),modelId:t.string(),temperature:t.number().optional(),inputTokens:t.number(),outputTokens:t.number(),totalTokens:t.number()}).optional(),toolCallsCount:t.number().optional(),success:t.boolean().optional()}),j=t.object({cluster:t.string().optional(),nodes:t.string().optional(),roles:t.string().optional(),phases:t.string().optional(),topic:t.string().optional(),namespace:t.string().optional(),packageName:t.string().optional(),llmProvider:t.string().optional(),llmModel:t.string().optional(),systemPrompt:t.string().optional(),tokenUsage:t.object({totalInputTokens:t.number(),totalOutputTokens:t.number(),totalTokens:t.number()}).optional()}).optional(),W=t.object({id:t.string().min(1).max(64).optional(),title:t.string().min(1).max(200).optional(),teamId:t.string().optional(),teamName:t.string().optional(),memoryScoped:t.boolean().optional(),messages:t.array(w),assistants:t.array(C).optional(),metadata:j}),$=t.object({title:t.string().min(1).max(200).optional(),teamId:t.string().optional(),teamName:t.string().optional(),memoryScoped:t.boolean().optional(),messages:t.array(w).optional(),assistants:t.array(C).optional(),metadata:j}),f=t.object({id:t.string()}),D=t.object({chatId:t.string(),messageId:t.string(),blockIndex:t.coerce.number().int().nonnegative()}),Q=t.object({block:t.object({type:t.string()}).passthrough()}),_=t.object({from:t.coerce.number().optional(),size:t.coerce.number().optional(),teamId:t.string().optional()}),J=t.object({teamId:t.string().optional(),assistantId:t.string().optional()}),K=t.object({q:t.string().min(1),from:t.coerce.number().optional(),size:t.coerce.number().optional()});function h(s){const a=s.auth?.username;if(a)return a;let n=s.cookies?.token;if(!n){const r=s.headers.authorization;r&&r.startsWith("Bearer ")&&(n=r.substring(7))}return n?N(n)?.username||null:(console.warn("[chats] No token found in request"),null)}const V=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}let e=n;const r=s.body?.metadata?.shareTokenId;if(r){const{valid:o,link:i,error:l}=await q(r,n);if(!o||!i){a.status(403).json({body:{time:new Date().toISOString(),result:{error:l??"Invalid share link"}}});return}e=i.ownerId,s.body.metadata={...s.body.metadata,sharedRun:!0,guestId:n,shareTokenId:r,shareLinkLabel:i.label,ownerId:i.ownerId}}try{const o=s.body,i=o.messages.filter(c=>c.role==="agent");if(i.length>0){const c=i[i.length-1].id;if(console.log("[chats] Last agent message ID:",c),c&&c.startsWith("run-")){const g=c.replace("run-","");console.log("[chats] Extracted runId:",g);const u=H(g);u?(o.metadata=o.metadata||{},o.metadata.systemPrompt=u,console.log("[chats] \u2705 Retrieved system prompt from backend cache, length:",u.length)):console.log("[chats] \u26A0\uFE0F No system prompt found in cache for runId:",g)}}const l=o.messages.find(c=>c.role==="user");let m=o.title;if(!m||m.startsWith("Chat -")||m.startsWith("give information about"))if(l&&typeof l.content=="string"){console.log("[chats] Generating chat title from first user message...");try{m=await L(l.content,e),console.log("[chats] Generated chat title:",m)}catch(c){console.warn("[chats] \u26A0\uFE0F Title generation failed, using fallback:",c.message?.slice(0,80)),m=l.content.slice(0,60).trim()||"New Chat"}}else o.metadata?.topic?m=`Chat - ${o.metadata.topic.replace(/_/g," ")}`:m="New Chat";const y={...o,title:m,username:e,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()},b=await O(y);if(i.length>0){const c=i[i.length-1].id;if(c&&c.startsWith("run-")){const g=c.replace("run-","");try{const p=(Array.isArray(o.assistants)?o.assistants:[]).filter(d=>d.llm).map(d=>({provider:d.llm.provider||"unknown",model:d.llm.modelId||d.llm.model||"unknown",inputTokens:d.llm.inputTokens||0,outputTokens:d.llm.outputTokens||0,totalTokens:d.llm.totalTokens||0,assistantId:d.assistantId,assistantName:d.assistantName}));let I=0;for(const d of p){const v=await G(d.provider,d.model,d.inputTokens,d.outputTokens);I+=v}await A(g,"success",{...p.length>0?{llmUsage:p}:{},totalTokens:o.metadata?.tokenUsage?.totalTokens||0,messageCount:o.messages.length,...I>0?{estimatedCost:I}:{}}),console.log(`[chats] \u2705 Updated chat run record with final data: ${g}`)}catch(u){console.error("[chats] \u26A0\uFE0F Failed to update chat run record:",u)}}}a.status(201).json({body:{time:new Date().toISOString(),result:b}})}catch(o){console.error("Error creating chat:",o),a.status(500).json({body:{time:new Date().toISOString(),result:{error:o.message||"Failed to create chat"}}})}},X=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=s.query.from?parseInt(s.query.from,10):0,r=s.query.size?parseInt(s.query.size,10):50,o=s.query.teamId,{chats:i,total:l}=await R(n,e,r,o);a.status(200).json({body:{time:new Date().toISOString(),result:{chats:i,total:l,from:e,size:r}}})}catch(e){console.error("Error getting chats:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve chats"}}})}},Y=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{id:e}=s.params,r=await S(e,n);if(!r){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}a.status(200).json({body:{time:new Date().toISOString(),result:r}})}catch(e){console.error("Error getting chat by ID:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve chat"}}})}},Z=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{id:e}=s.params,r=s.body,o=await P(e,n,r);if(!o){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}a.status(200).json({body:{time:new Date().toISOString(),result:o}})}catch(e){console.error("Error updating chat:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to update chat"}}})}},tt=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{chatId:e,messageId:r}=s.params,o=Number(s.params.blockIndex),i=await S(e,n);if(!i){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}const l=i.messages.find(u=>u.id===r);if(!l){a.status(404).json({body:{time:new Date().toISOString(),result:{error:`Message "${r}" not found`}}});return}const m=U(l);if(!m||o>=m.blocks.length){a.status(404).json({body:{time:new Date().toISOString(),result:{error:`Block ${o} not found on message "${r}"`}}});return}const y=m.blocks[o],b=m.blocks.filter((u,p)=>p!==o);let c;try{c=await E({block:y,otherBlocks:b,assistantId:l.assistantId,owner:n})}catch(u){const p=u instanceof F?u.message:u.message||"Failed to repair block";a.status(422).json({body:{time:new Date().toISOString(),result:{error:p}}});return}if(!await k(e,n,r,o,c)){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}a.status(200).json({body:{time:new Date().toISOString(),result:{block:c}}})}catch(e){console.error("Error repairing message block:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to repair block"}}})}},et=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{chatId:e,messageId:r}=s.params,o=Number(s.params.blockIndex),{block:i}=s.body;if(!await k(e,n,r,o,i)){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}a.status(200).json({body:{time:new Date().toISOString(),result:{block:i}}})}catch(e){if(e instanceof B){a.status(404).json({body:{time:new Date().toISOString(),result:{error:e.message}}});return}console.error("Error setting message block:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to update block"}}})}},at=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{id:e}=s.params;if(!await M(e,n)){a.status(404).json({body:{time:new Date().toISOString(),result:{error:"Chat not found or access denied"}}});return}a.status(200).json({body:{time:new Date().toISOString(),result:{success:!0,message:"Chat deleted successfully"}}})}catch(e){console.error("Error deleting chat:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to delete chat"}}})}},st=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=s.query.q,r=s.query.from?parseInt(s.query.from,10):0,o=s.query.size?parseInt(s.query.size,10):20,{chats:i,total:l}=await x(n,e,r,o);a.status(200).json({body:{time:new Date().toISOString(),result:{chats:i,total:l,query:e,from:r,size:o}}})}catch(e){console.error("Error searching chats:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to search chats"}}})}},ot=async(s,a)=>{const n=h(s);if(!n){a.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});return}try{const{teamId:e,assistantId:r}=s.query;let o=null;r?o=await T(n,r):e&&(o=await z(n,e)),a.status(200).json({body:{time:new Date().toISOString(),result:{chat:o}}})}catch(e){console.error("Error getting last chat:",e),a.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to get last chat"}}})}},St=[{method:"post",path:"/api/chats",handler:V,validate:{body:W},openapi:{summary:"Create or upsert a chat document",description:"Persists a chat (conversation) document in `.stkxp_chats`. Re-using an existing `id` upserts. The chat carries `teamId`, `messages[]`, an `assistants[]` per-assistant run summary, and aggregated token usage in `metadata.tokenUsage`.",tags:["chats"]}},{method:"get",path:"/api/chats/search",handler:st,validate:{query:K},openapi:{summary:"Full-text search chats",description:"Hybrid search over the caller's chats. Supports `q`, pagination, date range, and team filter.",tags:["chats"]}},{method:"get",path:"/api/chats/last",handler:ot,validate:{query:J},openapi:{summary:"Get the most recent chat",description:"Returns the caller's most recently updated chat \u2014 used by the UI to resume the previous conversation on home reload.",tags:["chats"]}},{method:"get",path:"/api/chats/:id",handler:Y,validate:{params:f},openapi:{summary:"Get a chat by id",tags:["chats"]}},{method:"get",path:"/api/chats",handler:X,validate:{query:_},openapi:{summary:"List the caller's chats",description:"Paginated list of the caller's chats, sorted by most recent first.",tags:["chats"]}},{method:"put",path:"/api/chats/:id",handler:Z,validate:{params:f,body:$},openapi:{summary:"Update a chat (partial)",description:"Partial update \u2014 only the supplied fields are written. Typically used to rename a chat or toggle `memoryScoped`.",tags:["chats"]}},{method:"post",path:"/api/chats/:chatId/messages/:messageId/blocks/:blockIndex/repair",handler:tt,validate:{params:D},openapi:{summary:"Repair a malformed chat block with AI",description:"Calls the message's assistant's own configured LLM (using the rest of the message as context) to fix a block that rendered incorrectly, then persists the corrected block into the chat.",tags:["chats"]}},{method:"put",path:"/api/chats/:chatId/messages/:messageId/blocks/:blockIndex",handler:et,validate:{params:D,body:Q},openapi:{summary:"Set a chat block's content directly",description:"Persists the given block at the given index with no LLM call \u2014 used to undo a repair.",tags:["chats"]}},{method:"delete",path:"/api/chats/:id",handler:at,validate:{params:f},openapi:{summary:"Delete a chat",tags:["chats"]}}];export{St as routes};
@@ -0,0 +1 @@
1
+ import u from"zod";import{ElasticsearchWrapper as h}from"../core/services/elasticsearch-wrapper";import{config as T}from"../core/config";import{filterQueryClusters as x,filterQueryNodes as q,parseNowExpression as g}from"../utils";function y(){const e=process.env.ELASTICSEARCH_CLUSTERS_API_KEY;return{...T.elasticsearch,...e?{auth:{apiKey:e}}:{}}}const f={params:u.object({namespace:u.string().optional()}),body:u.object({query:u.object({clusters:u.object({value:u.string().optional()}),start:u.object({value:u.string().optional()}),end:u.object({value:u.string().optional()})}).optional()})},Q=[{method:"post",path:"/api/stack_expert/clusters/ratios",validate:f,handler:async(e,c)=>{const o=new h(y(),!0),p=g(e.body.query.start.value),d=g(e.body.query.end.value),r="logs-stack_expert.cluster_stats-*";let i=null;const _=x(e.body.query);_.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const l={query:{bool:{filter:_}},size:0,sort:[{"@timestamp":{order:"desc"}}],aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],size:1}}}},b="logs-stack_expert.nodes_stats-*";let v=null;const z=q(e.body.query);z.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const O={query:{bool:{filter:z}},size:0,sort:[{"@timestamp":{order:"desc"}}],aggs:{cpu_percent_stats:{stats:{field:"stkxp.nodes.os.cpu.percent"}},heap_used_percent_stats:{stats:{field:"stkxp.nodes.jvm.mem.heap_used_percent"}},swap_used_in_bytes:{stats:{field:"stkxp.nodes.os.swap.used_in_bytes"}},by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],size:1}}}}}},I="logs-stack_expert.indices_stats-*";let w=null;const R=q(e.body.query);R.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const U={query:{bool:{filter:R}},size:0,sort:[{"@timestamp":{order:"desc"}}],aggs:{by_indexname:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],size:1}}}}}},C="logs-stack_expert.indices_get_settings-*";let j=null;const S=q(e.body.query);S.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const D={query:{bool:{filter:S}},size:0,sort:[{"@timestamp":{order:"desc"}}],aggs:{by_indexname:{terms:{field:"stkxp.name",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],size:1}}}}}};try{i=await o.search({index:r,...l});const m=i?.aggregations?.top_doc?.hits?.hits[0]?._source?.stkxp||{};v=await o.search({index:b,...O});const n=v?.aggregations?.by_nodename?.buckets||[];w=await o.search({index:I,...U});const E=w?.aggregations?.by_indexname?.buckets||[];j=await o.search({index:C,...D});const L=j?.aggregations?.by_indexname?.buckets||[],a={data:{},replication_ratio:{description:"Ratio de R\xE9plicationObjectif : Assure la redondance des donn\xE9es et leur disponibilit\xE9 en cas de d\xE9faillance de n\u0153uds.Ratio : Nombre de copies (shards de r\xE9plication) par rapport aux shards primaires. Par d\xE9faut, Elasticsearch a un facteur de r\xE9plication de 1 (1 shard principal + 1 shard r\xE9pliqu\xE9).Recommandation : Ajuster selon les besoins de r\xE9silience. Un ratio de 1:1 est souvent suffisant, mais pour des clusters critiques, une r\xE9plication suppl\xE9mentaire peut \xEAtre n\xE9cessaire."},read_write_ratio:{description:"Ratio Lecture/\xC9critureObjectif : Identifier si l'architecture du cluster est optimis\xE9e pour sa charge de travail (lecture intensive vs \xE9criture intensive).Ratio : Nombre de requ\xEAtes de lecture par rapport au nombre d'\xE9critures (indexations, mises \xE0 jour).Recommandation : Si le ratio est fortement en faveur des lectures, une optimisation des caches (query, shard) peut am\xE9liorer les performances."},heap_mem_usage_ratio:{description:"Heap Usage / Memory Usage RatioObjectif : S'assurer que la m\xE9moire JVM et la m\xE9moire syst\xE8me sont correctement allou\xE9es pour les n\u0153uds Elasticsearch.Ratio : Utilisation du heap par rapport \xE0 la taille maximale allou\xE9e (heap max).Recommandation : Le heap ne doit pas d\xE9passer 75% de la m\xE9moire allou\xE9e pour \xE9viter le 'garbage collection' excessif, qui peut provoquer des arr\xEAts longs (long GC pauses)."},disk_utilization_shards_ratio:{description:"Disk Utilization / Shard-to-Node RatioObjectif : \xC9valuer l'efficacit\xE9 du stockage et la distribution des donn\xE9es dans le cluster.Ratio : Utilisation des disques par rapport \xE0 la capacit\xE9 totale et nombre de shards par n\u0153ud.Recommandation : Garder l'utilisation du disque en dessous de 80%, et \xE9viter d'avoir trop de shards par n\u0153ud (id\xE9alement 20 shards/GB de RAM par n\u0153ud)."},doc_count_shard_size_ratio:{description:"Document Count vs Shard SizeObjectif : Optimiser la taille des shards pour la performance.Ratio : Nombre de documents par shard et taille moyenne du shard.Recommandation : Des shards trop petits ou trop grands peuvent affecter les performances. Id\xE9alement, chaque shard doit faire entre 10 Go et 50 Go selon les besoins."},refresh_interval_query_latency_ratio:{description:"Refresh Interval vs Query LatencyObjectif : Trouver un compromis entre la fra\xEEcheur des donn\xE9es et la latence des requ\xEAtes.Ratio : Temps d'actualisation des index (refresh interval) par rapport \xE0 la latence moyenne des requ\xEAtes.Recommandation : Un intervalle de rafra\xEEchissement plus long (par exemple 30s ou plus) peut r\xE9duire la charge d'\xE9criture tout en maintenant des performances de requ\xEAtes acceptables pour des donn\xE9es non critiques."},cpu_load_node_capacity_ratio:{description:"CPU Load vs Node CapacityObjectif : S'assurer que les n\u0153uds du cluster ne sont pas surcharg\xE9s.Ratio : Utilisation du CPU par rapport aux capacit\xE9s des n\u0153uds.Recommandation : L\u2019utilisation CPU ne doit pas d\xE9passer 80% de la capacit\xE9 disponible sur un n\u0153ud de fa\xE7on prolong\xE9e."},query_time_indexing_rate_ratio:{description:"Objectif : Comparer la charge des requ\xEAtes par rapport \xE0 la charge d'indexation.Ratio : Temps moyen de requ\xEAte par rapport au taux d'indexation (nombre de documents index\xE9s par seconde).Recommandation : Une dominance de requ\xEAtes lourdes peut signifier la n\xE9cessit\xE9 de revoir l'architecture ou d'optimiser les requ\xEAtes."},cache_hit_ratio:{description:"Objectif : Mesurer l'efficacit\xE9 des caches.Ratio : Nombre de hits de cache (requ\xEAtes satisfaites par les caches) par rapport aux requ\xEAtes totales.Recommandation : Plus le ratio est \xE9lev\xE9, plus les performances du cluster seront bonnes. Un cache hit ratio sup\xE9rieur \xE0 80% est g\xE9n\xE9ralement consid\xE9r\xE9 comme efficace."},throughput_latency_ratio:{description:"Objectif : Trouver l\u2019\xE9quilibre entre la vitesse de traitement (throughput) et la latence des requ\xEAtes.Ratio : Quantit\xE9 de donn\xE9es trait\xE9es par seconde par rapport au temps de r\xE9ponse moyen des requ\xEAtes.Recommandation : Maximiser le throughput sans d\xE9passer une latence acceptable pour l'utilisateur final (souvent <200ms pour des applications critiques)."}};a.data=v.aggregations,a.replication_ratio={...a.replication_ratio,primaries:m?.indices?.shards.primaries,replicates:m?.indices?.shards.replication},a.read_write_ratio={...a.read_write_ratio,search:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_total,0),indexing:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.indexing.index_total,0)},a.heap_mem_usage_ratio={...a.heap_mem_usage_ratio,heap:m?.nodes?.jvm.mem.heap_used_in_bytes,mem:m?.nodes?.os.mem.total_in_bytes};for(const s of E){const t=s.top_doc.hits.hits[0]._source.stkxp.ilm?.phase||"unknown",k=s.top_doc.hits.hits[0]._source.stkxp.stats?.total?.store?.size_in_bytes||0;a.disk_utilization_shards_ratio[t]||(a.disk_utilization_shards_ratio[t]=0),a.disk_utilization_shards_ratio[t]+=k}return a.doc_count_shard_size_ratio={...a.doc_count_shard_size_ratio,store:m?.indices.store.size_in_bytes,docs:m?.indices.docs.count},a.refresh_interval_query_latency_ratio={...a.refresh_interval_query_latency_ratio,refresh_interval:L.reduce((s,t)=>{const k=t.top_doc.hits.hits[0]._source.stkxp.settings,N=t.top_doc.hits.hits[0]._source.stkxp.defaults;return s+(k?.index?.refresh_interval?parseInt(k.index.refresh_interval,10):parseInt(N.index.refresh_interval,10))},0),query_latency:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_time_in_millis,0)},a.cpu_load_node_capacity_ratio={...a.cpu_load_node_capacity_ratio,load_1m:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.os.cpu.load_average["1m"],0),load_5m:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.os.cpu.load_average["5m"],0),load_15m:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.os.cpu.load_average["15m"],0)},a.query_time_indexing_rate_ratio={...a.query_time_indexing_rate_ratio,query_time:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_time_in_millis,0),query_total:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_total,0),indexing_time:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.indexing.index_time_in_millis,0),indexing_total:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.indexing.index_total,0)},a.cache_hit_ratio={...a.cache_hit_ratio,query_cache_hit:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.query_cache.hit_count,0),query_cache_miss:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.query_cache.miss_count,0),request_cache_hit:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.request_cache.hit_count,0),request_cache_miss:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.request_cache.miss_count,0)},a.throughput_latency_ratio={...a.throughput_latency_ratio,throughput_rps:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_total,0),latency_ms:n.reduce((s,t)=>s+t.top_doc.hits.hits[0]._source.stkxp.nodes.indices.search.query_time_in_millis,0)},c.json({body:{time:new Date().toISOString(),result:a}})}catch(m){console.log("clusters error",m)}}},{method:"post",path:"/api/stack_expert/clusters/check",validate:f,handler:async(e,c)=>{const o=new h(y(),!0),{namespace:p}=e.params,d="logs-stack_expert.health_report-*";let r="";try{const i=x(e.body.query),_=g(e.body.query.start.value),l=g(e.body.query.end.value);i.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const b={query:{bool:{filter:i}},size:0,aggs:{by_indicdator:{terms:{field:"stkxp.indicator",size:100},aggs:{over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(_,"minutes")/10},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],size:1}}}}}}}};return r=await o.search({index:d,body:b}),c.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_indicdator.buckets||[]}})}catch(i){console.log("clusters error",i)}}},{method:"post",path:"/api/stack_expert/clusters/stats",validate:f,handler:async(e,c)=>{const o=new h(y(),!0),{namespace:p}=e.params,d="logs-stack_expert.cluster_stats-*";let r="";try{const i=x(e.body.query),_=g(e.body.query.start.value),l=g(e.body.query.end.value);i.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const b={query:{bool:{filter:i}},size:0,aggs:{by_namespace:{terms:{field:"data_stream.namespace",size:1},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:30}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(_,"minutes")/10},aggs:{total_size_in_bytes:{max:{field:"stkxp.indices.docs.total_size_in_bytes"}},total_size_in_bytes_by_time:{derivative:{buckets_path:"total_size_in_bytes"}},docs_count:{max:{field:"stkxp.indices.docs.count"}},docs_count_by_time:{derivative:{buckets_path:"docs_count"}},search_total:{max:{field:"stkxp.indices.search.total"}},search_total_by_time:{derivative:{buckets_path:"search_total"}},stats_nodes_heap:{stats:{field:"stkxp.nodes.jvm.mem.heap_used_in_bytes"}},stats_nodes_cpu:{stats:{field:"stkxp.nodes.process.cpu.percent"}},indices_count:{max:{field:"stkxp.indices.count"}},indices_count_by_time:{derivative:{buckets_path:"indices_count"}},shards_total:{max:{field:"stkxp.indices.shards.total"}},shards_total_by_time:{derivative:{buckets_path:"shards_total"}}}}}}}};r=await o.search({index:d,...b})}catch(i){console.log("clusters error",i)}return c.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_namespace?.buckets[0]||[]}})}},{method:"post",path:"/api/stack_expert/clusters/info",validate:f,handler:async(e,c)=>{const o=new h(y(),!0),{namespace:p}=e.params,d="logs-stack_expert.info-*";let r="";const i=x(e.body.query);i.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const _={query:{bool:{filter:i}},size:0,aggs:{by_namespace:{terms:{field:"data_stream.namespace",size:1},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:5}}}}}};try{r=await o.search({index:d,..._})}catch(l){console.log("clusters error",l)}return c.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_namespace?.buckets[0]?.top_doc?.hits?.hits||[]}})}},{method:"post",path:"/api/stack_expert/clusters/license",validate:f,handler:async(e,c)=>{const o=new h(y(),!0),{namespace:p}=e.params,d="logs-stack_expert.license_get-*";let r="";const i=x(e.body.query);i.push({range:{"@timestamp":{gte:e.body.query.start.value,lte:e.body.query.end.value}}});const _={query:{bool:{filter:i}},size:0,aggs:{by_namespace:{terms:{field:"data_stream.namespace",size:1},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:5}}}}}};try{r=await o.search({index:d,..._})}catch(l){console.log("clusters error",l)}return c.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_namespace?.buckets[0]?.top_doc?.hits?.hits||[]}})}},{method:"get",path:"/api/stack_expert/clusters",validate:{},handler:async(e,c)=>{const o=new h(y(),!0),p="logs-stack_expert.cluster_stats-*";let d="";try{const r={index:p,size:0,query:{range:{"@timestamp":{gte:"now-30d/d",lte:"now/d"}}},aggs:{by_namespace:{terms:{field:"data_stream.namespace",size:100},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}};console.log("CLUSTER LIST ################",JSON.stringify(r)),d=await o.search(r)}catch(r){console.log("clusters error",r)}return c.json({body:{time:new Date().toISOString(),result:d?.aggregations?.by_namespace?.buckets||[]}})}},{method:"post",path:"/api/stack_expert/clusters/usage",validate:{params:u.object({namespace:u.string()})},handler:async(e,c)=>{const o=new h(y(),!0),{namespace:p}=e.params,d="logs-stack_expert.xpack_usage-*";let r="";try{r=await o.search({index:d,size:0,query:{bool:{filter:[{term:{"data_stream.namespace":p}}]}},aggs:{by_namespace:{terms:{field:"data_stream.namespace",size:1},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:100}}}}}})}catch(i){console.log("clusters error",i)}return c.json({body:{time:new Date().toISOString(),result:r?.aggregations?.by_namespace?.buckets[0]?.top_doc?.hits?.hits||[]}})}}];export{Q as routes};
@@ -0,0 +1,43 @@
1
+ import{Router as y}from"express";import{getChatById as $}from"../core/services/chats-service";import{createLLMInstance as C}from"../core/llm/providers";import{getOwnerDefaultLLMConfig as w}from"../core/services/default-model.service";import{SystemMessage as b,HumanMessage as M}from"@langchain/core/messages";const l=y();l.post("/",async(s,e)=>{try{const{referenceChatId:n,currentContext:r,comparisonAspects:o,username:i}=s.body;if(!n)return e.status(400).json({body:{result:{error:!0,message:"referenceChatId is required"}}});if(!r)return e.status(400).json({body:{result:{error:!0,message:"currentContext is required"}}});const a=s.user?.username||i;if(!a)return e.status(401).json({body:{result:{error:!0,message:"User not authenticated"}}});console.log(`[CompareAPI] User ${a} comparing with chat ${n}`);const t=await $(n);if(!t)return e.status(404).json({body:{result:{error:!0,message:`Chat with ID ${n} not found`}}});if(t.username!==a)return e.status(403).json({body:{result:{error:!0,message:"You don't have permission to access this chat"}}});const g=v(t),m=L(t.updatedAt),p=o||["overall"],c=await w(a);if(!c)return e.status(400).json({body:{result:{error:!0,message:"Aucune cl\xE9 API LLM n'est configur\xE9e pour votre compte. Ajoutez votre cl\xE9 dans Param\xE8tres \u2192 LLM Providers."}}});const d=C({provider:c.provider,model:c.model,apiKey:c.apiKey,baseURL:c.baseURL,temperature:.3,maxTokens:2500}),f=x(t,g,r,p,m);console.log("[CompareAPI] Generating comparison with LLM...");const h=await d.invoke([new b("You are an expert system analyst specialized in comparing conversation contexts and identifying meaningful changes, improvements, and regressions."),new M(f)]);e.json({body:{result:{success:!0,referenceChatId:n,referenceChatTitle:t.title,referenceChatDate:t.updatedAt,timeDelta:m,comparisonAspects:p,analysis:h.content,metadata:{referenceMessagesCount:t.messages.length,referenceTopic:t.metadata?.topic,referenceNamespace:t.metadata?.namespace,referencePackage:t.metadata?.packageName}}}})}catch(n){console.error("[CompareAPI] Error:",n),e.status(500).json({body:{result:{error:!0,message:`Failed to compare chats: ${n.message}`}}})}});function v(s){const e=[];e.push("# Chat Metadata"),e.push(`Title: ${s.title}`),e.push(`Created: ${s.createdAt}`),e.push(`Updated: ${s.updatedAt}`),s.metadata&&(s.metadata.topic&&e.push(`Topic: ${s.metadata.topic}`),s.metadata.namespace&&e.push(`Namespace: ${s.metadata.namespace}`),s.metadata.packageName&&e.push(`Package: ${s.metadata.packageName}`)),e.push(""),e.push("# Messages Summary"),e.push(`Total Messages: ${s.messages.length}`);const n=s.messages.filter(t=>t.role==="user");n.length>0&&(e.push(""),e.push(`## User Questions (${n.length} total)`),e.push(`First: ${u(n[0].content,200)}`),n.length>1&&e.push(`Last: ${u(n[n.length-1].content,200)}`));const r=s.messages.filter(t=>t.role==="assistant");if(r.length>0){e.push(""),e.push(`## Assistant Responses (${r.length} total)`);const t=r[r.length-1];e.push(`Last Response: ${u(t.content,400)}`)}const o=s.messages.map(t=>typeof t.content=="string"?t.content:JSON.stringify(t.content)).join(`
2
+ `),i=o.match(/(\d+(?:\.\d+)?)\s*(ms|MB|GB|%|req\/s|errors?|warnings?)/gi);i&&i.length>0&&(e.push(""),e.push("## Detected Metrics"),e.push(i.slice(0,15).join(", ")));const a=o.split(`
3
+ `).filter(t=>t.toLowerCase().includes("error")&&t.length<200);return a.length>0&&(e.push(""),e.push(`## Errors Detected (${a.length} total)`),a.slice(0,5).forEach(t=>{e.push(`- ${t.trim()}`)})),e.join(`
4
+ `)}function u(s,e){const n=typeof s=="string"?s:JSON.stringify(s);return n.length>e?n.substring(0,e)+"...":n}function L(s){const e=new Date(s),r=new Date().getTime()-e.getTime(),o=Math.floor(r/(1e3*60)),i=Math.floor(r/(1e3*60*60)),a=Math.floor(r/(1e3*60*60*24));if(a>0){const t=Math.floor(r%864e5/36e5);return`${a} day${a>1?"s":""}${t>0?` and ${t} hour${t>1?"s":""}`:""} ago`}else if(i>0){const t=Math.floor(r%36e5/6e4);return`${i} hour${i>1?"s":""}${t>0?` and ${t} minute${t>1?"s":""}`:""} ago`}else return`${o} minute${o>1?"s":""} ago`}function x(s,e,n,r,o){return`You are comparing two system states/conversations to identify changes, improvements, and regressions.
5
+
6
+ # Reference Context (${o})
7
+ ${e}
8
+
9
+ # Current Context (Now)
10
+ ${n}
11
+
12
+ # Comparison Aspects to Focus On
13
+ ${r.join(", ")}
14
+
15
+ # Your Task
16
+ Provide a comprehensive comparison analysis with the following structure:
17
+
18
+ ## 1. Executive Summary
19
+ Brief overview of the most significant changes (2-3 sentences)
20
+
21
+ ## 2. Key Changes Detected
22
+ List the most important differences between the two contexts
23
+
24
+ ## 3. Improvements \u2705
25
+ What has gotten better? Include specific metrics if available
26
+
27
+ ## 4. Regressions \u26A0\uFE0F
28
+ What has gotten worse? Highlight critical issues
29
+
30
+ ## 5. New Issues Found \u{1F534}
31
+ Problems that weren't present in the reference context
32
+
33
+ ## 6. Resolved Issues \u2705
34
+ Problems from the reference context that are now fixed
35
+
36
+ ## 7. Recommendations \u{1F4A1}
37
+ Actionable steps based on this comparison
38
+
39
+ ## 8. Metrics Comparison
40
+ If metrics are available, provide a side-by-side comparison table
41
+
42
+ Be specific, use numbers and percentages when available, and prioritize critical information.
43
+ Use emojis to make the analysis more readable.`}var R=l;export{R as default};
@@ -0,0 +1 @@
1
+ import C from"express";import{userToken as z}from"../services/auth";import{createConnector as w,getConnectors as F,getConnectorById as U,updateConnector as k,deleteConnector as I,fetchSpecContent as S,analyzeOpenApiSpec as H,generateConnectorTools as O,getConnectorLinked as q}from"../services/connectors-service";const a=C.Router();function c(t){const s=t.auth?.username;if(s)return s;let e=t.cookies?.token;if(!e){const n=t.headers.authorization;n?.startsWith("Bearer ")&&(e=n.substring(7))}return e?z(e)?.username??null:null}a.post("/connectors/analyze",async(t,s)=>{try{if(!c(t))return s.status(401).json({success:!1,message:"Unauthorized"});const{source:o,url:n,content:u}=t.body;if(!o)return s.status(400).json({success:!1,message:"Missing required field: source"});if(!u&&!n)return s.status(400).json({success:!1,message:"Provide either content or url"});const r=await S(o,n,u),i=H(r);s.json({success:!0,body:{analysis:i,rawContent:r}})}catch(e){console.error("[connectors] analyze error:",e),s.status(400).json({success:!1,message:e.message??"Failed to analyze spec"})}}),a.get("/connectors",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const o=await F(e);s.json({success:!0,body:{connectors:o,total:o.length}})}catch(e){console.error("[connectors] list error:",e),s.status(500).json({success:!1,message:e.message??"Failed to list connectors"})}}),a.get("/connectors/:id",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const o=await U(t.params.id,e);if(!o)return s.status(404).json({success:!1,message:"Connector not found"});s.json({success:!0,body:{connector:o}})}catch(e){console.error("[connectors] get error:",e),s.status(500).json({success:!1,message:e.message??"Failed to get connector"})}}),a.post("/connectors",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const{name:o,namespace:n,description:u,domain:r,tags:i,platformId:l,platformName:d,spec:m,analysis:f,toolStrategy:g,selectedGroups:p,selectedOperationIds:j,enabled:y,bodyFormat:h,customHeaders:R}=t.body;if(!o||!l||!d||!m||!f||!g)return s.status(400).json({success:!1,message:"Missing required fields: name, platformId, platformName, spec, analysis, toolStrategy"});const b=await w({name:o,namespace:n,description:u,domain:r,tags:i??[],platformId:l,platformName:d,spec:m,analysis:f,toolStrategy:g,selectedGroups:p,selectedOperationIds:j,bodyFormat:h,customHeaders:R,owner:e,enabled:y!==void 0?y:!0});s.status(201).json({success:!0,body:{connector:b}})}catch(e){console.error("[connectors] create error:",e),s.status(500).json({success:!1,message:e.message??"Failed to create connector"})}}),a.put("/connectors/:id",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const o=["name","namespace","description","domain","tags","spec","analysis","toolStrategy","selectedGroups","selectedOperationIds","enabled","bodyFormat","customHeaders"],n={};for(const r of o)t.body[r]!==void 0&&(n[r]=t.body[r]);const u=await k(t.params.id,e,n);if(!u)return s.status(404).json({success:!1,message:"Connector not found"});s.json({success:!0,body:{connector:u}})}catch(e){console.error("[connectors] update error:",e),s.status(500).json({success:!1,message:e.message??"Failed to update connector"})}}),a.get("/connectors/:id/linked",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const o=await q(t.params.id,e);s.json({success:!0,body:o})}catch(e){console.error("[connectors] linked error:",e),s.status(500).json({success:!1,message:e.message??"Failed to get linked data"})}}),a.post("/connectors/:id/generate-tools",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});const o=await O(t.params.id,e);s.json({success:!0,body:o})}catch(e){console.error("[connectors] generate-tools error:",e);const o=e.message==="Connector not found"?404:400;s.status(o).json({success:!1,message:e.message??"Failed to generate tools"})}}),a.delete("/connectors/:id",async(t,s)=>{try{const e=c(t);if(!e)return s.status(401).json({success:!1,message:"Unauthorized"});if(!await I(t.params.id,e))return s.status(404).json({success:!1,message:"Connector not found"});s.json({success:!0,body:{message:"Connector deleted successfully"}})}catch(e){console.error("[connectors] delete error:",e),s.status(500).json({success:!1,message:e.message??"Failed to delete connector"})}});var T=a;export{T as default};
@@ -0,0 +1 @@
1
+ import{z as s}from"zod";import{Client as w}from"@elastic/elasticsearch";import A from"axios";import q from"https";import{userToken as I}from"../services/auth";const x=process.env.STKXP_API_URL||"http://localhost:4000",U=process.env.ELASTICSEARCH_USER||"elastic",k=process.env.ELASTICSEARCH_PASSWORD||"",N=new q.Agent({rejectUnauthorized:!1}),z=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",D=process.env.ELASTICSEARCH_USER||"elastic",M=process.env.ELASTICSEARCH_PASSWORD||"",c=".stkxp_api",l=new w({node:z,auth:{username:D,password:M},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),O=s.object({page:s.string().optional().transform(o=>o?parseInt(o,10):1),pageSize:s.string().optional().transform(o=>o?parseInt(o,10):25),search:s.string().optional(),system:s.string().optional(),systems:s.string().optional()}),p=s.object({id:s.string().min(1)}),P=s.object({name:s.string().min(1),system:s.string().min(1),description:s.string().optional().nullable(),status:s.string().optional().nullable(),category:s.string().optional().nullable(),unique_path:s.string().optional().nullable(),full_path:s.string().optional().nullable(),http_method:s.string().optional().nullable(),route_type:s.string().optional().nullable(),route_file:s.string().optional().nullable(),is_reserved:s.boolean().optional().nullable(),implementation_type:s.string().optional().nullable(),service_function:s.string().optional().nullable(),sources:s.string().optional().nullable(),query:s.string().optional().nullable(),index_template:s.string().optional().nullable(),query_file_path:s.string().optional().nullable(),template_file_path:s.string().optional().nullable(),has_template:s.boolean().optional().default(!1),dashboard_id:s.string().optional().nullable(),dashboard_title:s.string().optional().nullable(),dashboard_ref:s.string().optional().nullable(),control_panels:s.string().optional().nullable()});async function H(o,e){try{const t=O.parse(o.query),{page:a,pageSize:n,search:r,system:f,systems:h}=t,g=(a-1)*n,_=o.headers.authorization?.split(" ")[1],S=(_?I(_):null)?.username,d=[],u=[];if(S&&u.push({term:{owner:S}}),r&&d.push({multi_match:{query:r,fields:["name","system","dashboard_title","unique_path"],type:"best_fields",fuzziness:"AUTO"}}),f&&u.push({term:{system:f}}),h){const y=h.split(",").map(j=>j.trim()).filter(Boolean);y.length>0&&u.push({bool:{should:[{terms:{system:y}},{term:{route_type:"reserved"}}],minimum_should_match:1}})}const C={bool:{...d.length>0?{must:d}:{must:[{match_all:{}}]},...u.length>0?{filter:u}:{}}},R=await l.search({index:c,from:0,size:1e4,query:C,sort:[{name:{order:"asc"}}],_source:!0}),i=new Set,b=[];for(const y of R.hits.hits){const j=`${y._source.name}::${y._source.system}`;i.has(j)||(i.add(j),b.push({id:y._id,...y._source}))}const v=b.length,T=b.slice(g,g+n);e.json({success:!0,consumptions:T,total:v,page:a,pageSize:n,totalPages:Math.ceil(v/n)})}catch(t){console.error("[CONSUMPTIONS] Error listing:",t),e.status(500).json({error:"Failed to fetch consumptions",details:t.message})}}async function L(o,e){try{const{id:t}=p.parse(o.params),a=await l.get({index:c,id:t});e.json({success:!0,consumption:{id:a._id,...a._source}})}catch(t){if(t.meta?.statusCode===404){e.status(404).json({error:"Consumption not found"});return}console.error("[CONSUMPTIONS] Error getting:",t),e.status(500).json({error:"Failed to fetch consumption",details:t.message})}}async function $(o,e){try{const t=P.parse(o.body),a=new Date().toISOString(),r=o.headers.authorization?.split(" ")[1],h=(r?I(r):null)?.username,g={...t,owner:h||"unknown",created_at:a,updated_at:a},m=await l.index({index:c,document:g,refresh:!0});e.status(201).json({success:!0,consumption:{id:m._id,...g}})}catch(t){if(t instanceof s.ZodError){e.status(400).json({error:"Validation failed",issues:t.format()});return}console.error("[CONSUMPTIONS] Error creating:",t),e.status(500).json({error:"Failed to create consumption",details:t.message})}}async function F(o,e){try{const{id:t}=p.parse(o.params),n={...P.parse(o.body),updated_at:new Date().toISOString()};await l.update({index:c,id:t,doc:n,refresh:!0}),e.json({success:!0,consumption:{id:t,...n}})}catch(t){if(t instanceof s.ZodError){e.status(400).json({error:"Validation failed",issues:t.format()});return}if(t.meta?.statusCode===404){e.status(404).json({error:"Consumption not found"});return}console.error("[CONSUMPTIONS] Error updating:",t),e.status(500).json({error:"Failed to update consumption",details:t.message})}}async function X(o,e){try{const{id:t}=p.parse(o.params);await l.delete({index:c,id:t,refresh:!0}),e.json({success:!0,id:t})}catch(t){if(t.meta?.statusCode===404){e.status(404).json({error:"Consumption not found"});return}console.error("[CONSUMPTIONS] Error deleting:",t),e.status(500).json({error:"Failed to delete consumption",details:t.message})}}async function B(o,e){try{const a=(await l.search({index:c,size:0,aggs:{by_system:{terms:{field:"system",size:1e3}}}})).aggregations?.by_system?.buckets??[],n={};for(const r of a)n[r.key]=r.doc_count;e.json({success:!0,counts:n})}catch(t){console.error("[CONSUMPTIONS] Error fetching system counts:",t),e.status(500).json({error:"Failed to fetch system counts",details:t.message})}}async function K(o,e){try{const n=((await l.search({index:c,size:0,aggs:{distinct_systems:{terms:{field:"system",size:100}}}})).aggregations?.distinct_systems?.buckets??[]).map(r=>r.key);e.json({success:!0,systems:n})}catch(t){console.error("[CONSUMPTIONS] Error fetching systems:",t),e.status(500).json({error:"Failed to fetch systems",details:t.message})}}const W=s.object({namespace:s.string().min(1),platformId:s.string().optional(),params:s.record(s.string()).optional().default({}),start:s.string().optional().default("now-4h"),end:s.string().optional().default("now")});async function V(o,e){try{const{id:t}=p.parse(o.params),{namespace:a,platformId:n,params:r,start:f,end:h}=W.parse(o.body),m=(await l.get({index:c,id:t}))._source;if(!m){e.status(404).json({error:"Consumption not found"});return}const _=m.system,E=m.unique_path||m.name;if(!_||!E){e.status(400).json({error:"Missing system or unique_path on this consumption"});return}const S={namespace:{value:a},start:{value:f},end:{value:h}};for(const[i,b]of Object.entries(r))S[i]={value:b};const d=`${x}/api/stack_expert/${_}/${a}/${E}`,u=Date.now();let C,R;try{const i=await A.post(d,{query:S},{headers:{"Content-Type":"application/json",Authorization:`Basic ${Buffer.from(`${U}:${k}`).toString("base64")}`,"kbn-xsrf":"true","x-elastic-internal-origin":"Kibana",...n?{"X-Platform-Id":n}:{}},httpsAgent:N,timeout:3e4,validateStatus:()=>!0});C=i.data,R=i.status}catch(i){e.json({success:!1,url:d,durationMs:Date.now()-u,error:i.message});return}e.json({success:!0,url:d,statusCode:R,durationMs:Date.now()-u,data:C})}catch(t){if(t instanceof s.ZodError){e.status(400).json({error:"Validation failed",issues:t.format()});return}console.error("[CONSUMPTIONS] Error testing:",t),e.status(500).json({error:"Test failed",details:t.message})}}const ot=[{method:"get",path:"/api/consumptions/counts",handler:B,openapi:{summary:"Count consumptions grouped by system",tags:["consumptions"]}},{method:"get",path:"/api/consumptions/systems",handler:K,openapi:{summary:"List distinct system identifiers across consumptions",tags:["consumptions"]}},{method:"get",path:"/api/consumptions",handler:H,validate:{query:O},openapi:{summary:"List consumptions (paginated, filtered)",tags:["consumptions"]}},{method:"get",path:"/api/consumptions/:id",handler:L,validate:{params:p},openapi:{summary:"Get a single consumption by id",tags:["consumptions"]}},{method:"post",path:"/api/consumptions/:id/test",handler:V,validate:{params:p},openapi:{summary:"Dry-run a consumption against its target",tags:["consumptions"]}},{method:"post",path:"/api/consumptions",handler:$,validate:{body:P},openapi:{summary:"Create a consumption",tags:["consumptions"]}},{method:"put",path:"/api/consumptions/:id",handler:F,validate:{params:p,body:P},openapi:{summary:"Update a consumption",tags:["consumptions"]}},{method:"delete",path:"/api/consumptions/:id",handler:X,validate:{params:p},openapi:{summary:"Delete a consumption",tags:["consumptions"]}}];export{ot as routes};
@@ -0,0 +1 @@
1
+ import{Client as h}from"@elastic/elasticsearch";const b=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",_=process.env.ELASTICSEARCH_USER||"elastic",F=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",c=new h({node:b,auth:{username:_,password:F},tls:{rejectUnauthorized:!1},requestTimeout:3e4,maxRetries:3}),y=[{index:".stkxp_assistants",label:"Assistants",ownerField:"owner",displayField:"name"},{index:".stkxp_tools",label:"Tools",ownerField:"owner",displayField:"name"},{index:".stkxp_graphs",label:"Graphs",ownerField:"owner",displayField:"name"},{index:".stkxp_node_templates",label:"Node Templates",ownerField:"owner",displayField:"name"},{index:".stkxp_execution_profiles",label:"Execution Profiles",ownerField:"owner",displayField:"name"},{index:".stkxp_api",label:"Consumptions",ownerField:"owner",displayField:"name"},{index:".stkxp_resources",label:"Resources",ownerField:"owner",displayField:"title"},{index:".stkxp_prompts",label:"Prompts",ownerField:"owner",displayField:"name"},{index:".stkxp_teams",label:"Teams",ownerField:"owner",displayField:"name"},{index:".stkxp_platforms",label:"Platforms",ownerField:"owner",displayField:"name"},{index:".stkxp_node_types",label:"Node Types",ownerField:"owner",displayField:"label"},{index:".stkxp_llm_routing_rules",label:"LLM Routing Rules",ownerField:"owner",displayField:"name"},{index:".stkxp_llm_models",label:"LLM Models",ownerField:"owner",displayField:"name"},{index:".stkxp_llm_providers",label:"LLM Providers",ownerField:"owner",displayField:"name"},{index:".stkxp_llm_deployments",label:"LLM Deployments",ownerField:"owner",displayField:"name"},{index:".stkxp_connectors",label:"Connectors",ownerField:"owner",displayField:"name"},{index:".stkxp_chats",label:"Chats",ownerField:"username",displayField:"title"},{index:".stkxp_chat_runs",label:"Chat Runs",ownerField:"username",displayField:"teamName"},{index:".stkxp_chat_tools",label:"Chat Tools",ownerField:"username",displayField:"tool"}],m=new Map(y.map(s=>[s.index,s]));function p(s){return s?.body??s}async function k(s,e){try{const t=y.map(n=>n.index);let i={};try{const n=p(await c.indices.stats({index:t.join(","),metric:"docs"}));for(const[r,a]of Object.entries(n.indices??{}))i[r]=a?.total?.docs?.count??0}catch{}const o=y.map(n=>({index:n.index,label:n.label,ownerField:n.ownerField,displayField:n.displayField,docCount:i[n.index]??0}));e.json({body:{result:o}})}catch(t){e.status(500).json({error:t.message})}}async function R(s,e){const{index:t}=s.params,i=m.get(t);if(!i){e.status(400).json({error:"Unknown index"});return}const o=i.ownerField;try{const r=p(await c.search({index:t,size:0,body:{aggs:{owners:{terms:{field:o,size:200}}}}})).aggregations?.owners?.buckets??[];e.json({body:{result:r.map(a=>a.key)}})}catch(n){e.status(500).json({error:n.message})}}async function j(s,e){const{index:t}=s.params,i=m.get(t);if(!i){e.status(400).json({error:"Unknown index"});return}const o=parseInt(s.query.from||"0"),n=parseInt(s.query.size||"20"),r=s.query.owner,a=(s.query.q||"").trim(),d=[];if(r){const l=i.ownerField;d.push({term:{[l]:r}})}a&&d.push({wildcard:{[i.displayField]:{value:`*${a}*`,case_insensitive:!0}}});try{const l=p(await c.search({index:t,from:o,size:n,body:{query:d.length?{bool:{must:d}}:{match_all:{}},sort:[{_score:{order:"desc"}}]}})),g=l.hits.hits.map(f=>({_id:f._id,_source:f._source})),x=typeof l.hits.total=="object"?l.hits.total.value:l.hits.total;e.json({body:{result:g,total:x}})}catch(l){e.status(500).json({error:l.message})}}async function v(s,e){const{index:t,id:i}=s.params;if(!m.has(t)){e.status(400).json({error:"Unknown index"});return}const o=s.body;if(!o||typeof o!="object"){e.status(400).json({error:"Request body must be the new document source (JSON object)"});return}try{await c.index({index:t,id:i,body:{...o,updatedAt:new Date().toISOString()},refresh:!0}),e.json({body:{result:"updated"}})}catch(n){e.status(500).json({error:n.message})}}async function S(s,e){const{index:t}=s.params;if(!m.has(t)){e.status(400).json({error:"Unknown index"});return}const{documentIds:i}=s.body;if(!Array.isArray(i)||i.length===0){e.status(400).json({error:"documentIds must be a non-empty array"});return}try{const o=i.flatMap(d=>[{delete:{_index:t,_id:d}}]),r=p(await c.bulk({body:o,refresh:!0})).items.filter(d=>d.delete?.result==="deleted").length,a=i.length-r;e.json({body:{result:{deleted:r,failed:a}}})}catch(o){e.status(500).json({error:o.message})}}const w=".stkxp_resources";async function C(s){const e=[{exists:{field:"source_file"}}];return s&&e.push({term:{owner:s}}),(p(await c.search({index:w,size:0,body:{query:{bool:{must:e}},aggs:{files:{composite:{size:1e4,sources:[{sourceFile:{terms:{field:"source_file"}}},{owner:{terms:{field:"owner"}}}]},aggs:{createdAt:{min:{field:"created_at"}}}}}}})).aggregations?.files?.buckets??[]).map(o=>({sourceFile:o.key.sourceFile,owner:o.key.owner,chunkCount:o.doc_count,createdAt:o.createdAt?.value_as_string??null}))}async function A(s,e){try{const i=p(await c.search({index:w,size:0,body:{query:{bool:{must:[{exists:{field:"source_file"}}]}},aggs:{owners:{terms:{field:"owner",size:200}}}}})).aggregations?.owners?.buckets??[];e.json({body:{result:i.map(o=>o.key)}})}catch(t){e.status(500).json({error:t.message})}}async function q(s,e){const t=parseInt(s.query.from||"0"),i=parseInt(s.query.size||"20"),o=s.query.owner;try{const n=await C(o);n.sort((a,d)=>(d.createdAt||"").localeCompare(a.createdAt||""));const r=n.slice(t,t+i);e.json({body:{result:r,total:n.length}})}catch(n){e.status(500).json({error:n.message})}}async function I(s,e){const{files:t}=s.body;if(!Array.isArray(t)||t.length===0){e.status(400).json({error:"files must be a non-empty array of { sourceFile, owner }"});return}let i=0,o=0;for(const n of t){if(!n?.sourceFile||!n?.owner){o++;continue}try{const r=p(await c.deleteByQuery({index:w,refresh:!0,body:{query:{bool:{must:[{term:{source_file:n.sourceFile}},{term:{owner:n.owner}}]}}}}));i+=r.deleted??0}catch{o++}}e.json({body:{result:{deleted:i,failed:o}}})}const D=["data-admin","admin"],u={tags:D,requiredRoles:["superuser"]},N=[{method:"get",path:"/api/admin/data-admin/indices",handler:k,openapi:{...u,summary:"List `.stkxp_*` indices available for direct admin editing"}},{method:"get",path:"/api/admin/data-admin/indices/:index/owners",handler:R,openapi:{...u,summary:"List distinct `owner` values present in an index"}},{method:"get",path:"/api/admin/data-admin/indices/:index/documents",handler:j,openapi:{...u,summary:"List documents in an index (paginated, owner-filtered)",description:"Direct read across `.stkxp_*` indices \u2014 bypasses the normal owner-scoping. For audits, migrations and event response."}},{method:"put",path:"/api/admin/data-admin/indices/:index/documents/:id",handler:v,openapi:{...u,summary:"Replace a document by id in any `.stkxp_*` index"}},{method:"delete",path:"/api/admin/data-admin/indices/:index/documents",handler:S,openapi:{...u,summary:"Delete documents from an index (by id or query)",description:"Destructive \u2014 pass `ids[]` or a `query` body. No soft-delete: documents are removed from the underlying ES index."}},{method:"get",path:"/api/admin/data-admin/files/owners",handler:A,openapi:{...u,summary:"List distinct `owner` values among ingested resource files"}},{method:"get",path:"/api/admin/data-admin/files",handler:q,openapi:{...u,summary:"List ingested source files (grouped view of `.stkxp_resources` by `source_file`)",description:"Aggregates `.stkxp_resources` chunks by their originating `source_file` + `owner`, paginated."}},{method:"delete",path:"/api/admin/data-admin/files",handler:I,openapi:{...u,summary:"Delete all resource chunks belonging to the given source files",description:"Destructive \u2014 pass `files: [{ sourceFile, owner }]`. Deletes every `.stkxp_resources` chunk matching each pair."}}];export{N as routes};
@@ -0,0 +1 @@
1
+ import{z as w}from"zod";import{v4 as O}from"uuid";import{Client as I}from"@elastic/elasticsearch";const j=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",S=process.env.ELASTICSEARCH_USER||"elastic",v=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",l=new I({node:j,auth:{username:S,password:v},tls:{rejectUnauthorized:!1},requestTimeout:3e4,maxRetries:3}),f=".stkxp_transfer_logs",F=[{index:".stkxp_assistants",label:"Assistants",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_tools",label:"Tools",ownerField:"owner",displayField:"name",extraOwnerFields:["createdBy","updatedBy"]},{index:".stkxp_graphs",label:"Graphs",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_node_templates",label:"Node Templates",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_execution_profiles",label:"Execution Profiles",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_api",label:"Consumptions",ownerField:"owner",displayField:"title",extraOwnerFields:[]},{index:".stkxp_resources",label:"Resources",ownerField:"owner",displayField:"title",extraOwnerFields:[]},{index:".stkxp_prompts",label:"Prompts",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_teams",label:"Teams",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_platforms",label:"Platforms",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_node_types",label:"Node Types",ownerField:"owner",displayField:"label",extraOwnerFields:[]},{index:".stkxp_llm_routing_rules",label:"LLM Routing Rules",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_llm_models",label:"LLM Models",ownerField:"owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_llm_providers",label:"LLM Providers",ownerField:"metadata.owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_llm_deployments",label:"LLM Deployments",ownerField:"metadata.owner",displayField:"name",extraOwnerFields:[]},{index:".stkxp_chats",label:"Chats",ownerField:"username",displayField:"title",extraOwnerFields:[]},{index:".stkxp_chat_runs",label:"Chat Runs",ownerField:"username",displayField:"teamName",extraOwnerFields:[]},{index:".stkxp_chat_tools",label:"Chat Tools",ownerField:"username",displayField:"tool",extraOwnerFields:[]}],_=new Map(F.map(t=>[t.index,t]));function c(t){return t?.body??t}function K(t,o){return o.split(".").reduce((n,s)=>n?.[s],t)}function b(t,o,n){const s=o.split(".");let r=t;for(let e=0;e<s.length-1;e++)r[s[e]]||(r[s[e]]={}),r=r[s[e]];r[s[s.length-1]]=n}async function k(){try{c(await l.indices.exists({index:f}))||await l.indices.create({index:f,body:{mappings:{properties:{timestamp:{type:"date"},performedBy:{type:"keyword"},targetUser:{type:"keyword"},index:{type:"keyword"},ownerField:{type:"keyword"},documentIds:{type:"keyword"},clonedIds:{type:"keyword"},failedIds:{type:"keyword"},count:{type:"integer"}}}}})}catch{}}async function C(t,o){try{const n=F.map(e=>e.index);let s={};try{const e=c(await l.indices.stats({index:n.join(","),metric:"docs"}));for(const[a,i]of Object.entries(e.indices??{}))s[a]=i?.total?.docs?.count??0}catch{}const r=F.map(e=>({index:e.index,label:e.label,ownerField:e.ownerField,displayField:e.displayField,docCount:s[e.index]??0}));o.json({body:{result:r}})}catch(n){o.status(500).json({error:n.message})}}async function L(t,o){const{index:n}=t.params,s=_.get(n);if(!s){o.status(400).json({error:"Unknown index"});return}const r=s.ownerField;try{const a=c(await l.search({index:n,size:0,body:{aggs:{owners:{terms:{field:r,size:200}}}}})).aggregations?.owners?.buckets??[];o.json({body:{result:a.map(i=>i.key)}})}catch(e){o.status(500).json({error:e.message})}}async function q(t,o){const{index:n}=t.params,s=_.get(n);if(!s){o.status(400).json({error:"Unknown index"});return}const r=parseInt(t.query.from||"0"),e=parseInt(t.query.size||"20"),a=t.query.owner,i=[];if(a){const d=s.ownerField;i.push({term:{[d]:a}})}try{const d=c(await l.search({index:n,from:r,size:e,body:{query:i.length?{bool:{must:i}}:{match_all:{}},sort:[{_score:{order:"desc"}}]}})),m=d.hits.hits.map(p=>({_id:p._id,_source:p._source})),y=typeof d.hits.total=="object"?d.hits.total.value:d.hits.total;o.json({body:{result:m,total:y}})}catch(d){o.status(500).json({error:d.message})}}async function E(t,o){try{const n=c(await l.security.getUser({})),s=Object.entries(n).map(([r,e])=>({username:r,fullName:e.full_name||"",email:e.email||"",roles:e.roles||[]}));o.json({body:{result:s}})}catch(n){o.status(500).json({error:n.message})}}async function A(t,o){const{index:n,documentIds:s,targetUser:r}=t.body,e=t.auth?.username||"unknown";if(!n||!Array.isArray(s)||s.length===0||!r){o.status(400).json({error:"Missing required fields: index, documentIds, targetUser"});return}const a=_.get(n);if(!a){o.status(400).json({error:"Unknown index"});return}try{const i=c(await l.search({index:n,size:s.length,body:{query:{ids:{values:s}}}})),d=[],m=[],y=new Date().toISOString(),p=[];for(const g of i.hits.hits)try{const x=O(),h={...g._source,id:x,createdAt:y,updatedAt:y};b(h,a.ownerField,r);for(const R of a.extraOwnerFields??[])b(h,R,r);p.push({index:{_index:n,_id:x}}),p.push(h),d.push(x)}catch{m.push(g._id)}p.length>0&&c(await l.bulk({body:p,refresh:!0})).errors&&console.error(`[DataTransfer] Bulk errors cloning ${n} \u2192 ${r}`),await k(),await l.index({index:f,body:{timestamp:y,performedBy:e,targetUser:r,index:n,ownerField:a.ownerField,documentIds:s,clonedIds:d,failedIds:m,count:d.length},refresh:!0}),o.json({body:{result:{cloned:d.length,failed:m.length}}})}catch(i){o.status(500).json({error:i.message})}}async function P(t,o){const n=parseInt(t.query.from||"0"),s=parseInt(t.query.size||"20");try{await k();const r=c(await l.search({index:f,from:n,size:s,body:{query:{match_all:{}},sort:[{timestamp:{order:"desc"}}]}})),e=r.hits.hits.map(i=>({_id:i._id,...i._source})),a=typeof r.hits.total=="object"?r.hits.total.value:r.hits.total;o.json({body:{result:e,total:a}})}catch(r){o.status(500).json({error:r.message})}}const U=["data-transfer","admin"],u={tags:U,requiredRoles:["superuser"]},T=w.object({index:w.string().describe("Source `.stkxp_*` index to clone documents from"),documentIds:w.array(w.string()).describe("Ids of the documents to clone"),targetUser:w.string().describe("Username to assign as the new owner of the cloned docs")}),W=[{method:"get",path:"/api/admin/data-transfer/indices",handler:C,openapi:{...u,summary:"List `.stkxp_*` indices eligible for cross-user cloning"}},{method:"get",path:"/api/admin/data-transfer/indices/:index/owners",handler:L,openapi:{...u,summary:"List distinct owners in an index (source picker)"}},{method:"get",path:"/api/admin/data-transfer/indices/:index/documents",handler:q,openapi:{...u,summary:"Preview documents in an index before cloning"}},{method:"get",path:"/api/admin/data-transfer/users",handler:E,openapi:{...u,summary:"List candidate destination users"}},{method:"post",path:"/api/admin/data-transfer/clone",handler:A,validate:{body:T},openapi:{...u,summary:"Clone `.stkxp_*` documents from one owner to another",description:"Copies selected docs (by `ids[]` or `query`) into another user's scope, rewriting the `owner` field and refreshing related foreign keys. Used for provisioning trial accounts or restoring data after support cases."}},{method:"get",path:"/api/admin/data-transfer/logs",handler:P,openapi:{...u,summary:"List previous clone operations (audit trail)"}}];export{W as routes};
@@ -0,0 +1 @@
1
+ import{Router as H}from"express";import L from"https";import{Client as C}from"@elastic/elasticsearch";import{getPlatformById as D}from"../core/services/platforms-service";const E=H(),B=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",Q=process.env.ELASTICSEARCH_USER||"elastic",W=process.env.ELASTICSEARCH_PASSWORD||"",$=new C({node:B,auth:{username:Q,password:W},tls:{rejectUnauthorized:!1},requestTimeout:3e4}),N=new L.Agent({rejectUnauthorized:!1});function w(s){if(!s)return null;if(typeof s=="object")return s;try{return JSON.parse(s)}catch{return null}}async function F(s,l,r,f){const o=await D(s,l,!0,!1);if(!o||o.type!=="ElasticStack")throw new Error(`Platform "${s}" not found or not an ElasticStack platform`);const c=o.config,i=c.endpoints.elasticsearch.url||"",m=c.endpoints.elasticsearch.port||"9200",n=c.endpoints.elasticsearch.basePath?`/${c.endpoints.elasticsearch.basePath.replace(/^\/+|\/+$/g,"")}`:"";let u=i.startsWith("http")?i:`https://${i}`;try{new URL(u).port||(u=`${u}:${m}`)}catch{u=`https://${i}:${m}`}return u=`${u}${n}`,await new C({node:u,auth:{username:c.endpoints.elasticsearch.username,password:c.endpoints.elasticsearch.password},tls:{rejectUnauthorized:!1},requestTimeout:3e4}).search({index:r,body:f})}async function O(s,l,r,f,o){try{const c=s.auth?.username||"__system__";console.log(`[Tool Exec] \u2192 ${r}/${f}/${o} (user=${c})`);let i=null;try{const t=await $.get({index:".stkxp_api",id:o});t.found&&(i=t._source)}catch{const t=await $.search({index:".stkxp_api",body:{query:{bool:{must:[{term:{system:r}},{term:{unique_path:o}}]}},size:1}});t.hits.hits.length>0&&(i=t.hits.hits[0]._source)}if(!i){l.status(404).json({success:!1,error:`No API doc found for ${r}/${o}`});return}let m=s.headers["x-platform-id"]||i.platformId||"";if(!m)try{const t=await $.search({index:".stkxp_tools",body:{query:{bool:{must:[{term:{system:r}},{match_phrase:{path:o}}]}},size:1,_source:["platformId"]}});t.hits.hits.length>0&&(m=t.hits.hits[0]._source?.platformId||"")}catch{}if(!m){l.status(400).json({success:!1,error:`Cannot determine platform for ${r}/${o}. Pass X-Platform-Id header.`});return}let n=w(i.query)||{match_all:{}};n.query&&!n.bool&&!n.match_all&&!n.term&&!n.match&&(n=n.query);const u=w(i.aggs)||{},h=s.body,_=h.query&&typeof h.query=="object"?h.query:null,p={};if(_)for(const[t,e]of Object.entries(_))p[t]=e&&typeof e=="object"&&"value"in e?e.value:e;else for(const[t,e]of Object.entries(h))p[t]=e;const d=h.timeRange??(p.start||p.end?{from:p.start||void 0,to:p.end||void 0}:void 0),y=[],x=n?.bool?.filter||(n?.bool?[]:[]);y.push(...x),(d?.from||d?.to)&&y.push({range:{"@timestamp":{...d.from?{gte:d.from}:{},...d.to?{lte:d.to}:{}}}});let A=new Set,j=[];try{const t=await $.search({index:".stkxp_tools",body:{query:{bool:{must:[{term:{system:r}},{match_phrase:{path:o}}]}},size:1,_source:["inputSchema"]}});if(t.hits.hits.length>0){const e=t.hits.hits[0]._source?.inputSchema,a=w(e);if(a?.properties&&typeof a.properties=="object"){j=Object.keys(a.properties);for(const S of Array.isArray(a.required)?a.required:[])A.add(S)}}}catch(t){console.warn(`[Tool Exec] Could not load inputSchema for ${r}/${o}: ${t.message}`)}const v=[...j].sort((t,e)=>e.length-t.length),I=t=>{for(const e of v)if(t===e||t.startsWith(e+"."))return e;return null},z=new Set(["timeRange","userToken","executeQuery","packageName","platform","namespace","cluster","clusters","start","end","interval","roles","nodes","prefix","suffix","phases","topic","token","Token","authorization","Authorization","apiKey","api_key"]),q=new Set(["timeRange"]),k=(t,e="")=>{const a={};for(const[S,g]of Object.entries(t)){const T=e?`${e}.${S}`:S;g!==null&&typeof g=="object"&&!Array.isArray(g)?Object.assign(a,k(g,T)):a[T]=g}return a},U=k(p);for(const[t,e]of Object.entries(U)){if(z.has(t)||e===void 0||e===null||e==="")continue;const a=I(t);a&&(q.has(a)||A.has(a)&&y.push({term:{[t]:e}}))}const P={query:y.length>0?{bool:{filter:y,must:n?.bool?.must||[]}}:n,size:0};Object.keys(u).length>0&&(P.aggs=u);let b="metrics-*";for(const t of x){const e=t?.match_phrase?.["data_stream.dataset"]||t?.term?.["data_stream.dataset"];if(e){b=`metrics-${e}-*`;break}}console.log(`[Tool Exec] Querying index=${b} via platform=${m}`);const R=await F(m,c,b,P);console.log(`[Tool Exec] \u2705 ${r}/${o} \u2014 took=${R.took}ms hits=${R.hits?.total?.value??0}`),l.json({success:!0,packageName:r,namespace:f,uniquePath:o,result:R})}catch(c){console.error(`[Tool Exec] ${r}/${o}:`,c.message),l.status(500).json({success:!1,error:c.message})}}E.post("/:packageName/:namespace/:uniquePath",(s,l)=>{const{packageName:r,namespace:f,uniquePath:o}=s.params;return O(s,l,r,f,o)}),E.post("/:packageName/:uniquePath",(s,l)=>{const{packageName:r,uniquePath:f}=s.params;return O(s,l,r,"default",f)});var tt=E;export{tt as default};
@@ -0,0 +1 @@
1
+ import{lookup as f}from"node:dns/promises";import{z as i}from"zod";const c=20*1024*1024,p=12e3;function l(a){const t=a.toLowerCase();if(t==="::1"||t==="::"||t.startsWith("fe80:")||t.startsWith("fc")||t.startsWith("fd"))return!0;const s=t.startsWith("::ffff:")?t.slice(7):t,o=s.split(".");if(o.length!==4)return s!==t?l(s):!1;const e=o.map(u=>parseInt(u,10));if(e.some(u=>Number.isNaN(u)||u<0||u>255))return!0;const[r,n]=e;return r===10||r===127||r===0||r===169&&n===254||r===172&&n>=16&&n<=31||r===192&&n===168||r===100&&n>=64&&n<=127}const m=i.object({url:i.string().url()});async function d(a){let t;try{t=new URL(a)}catch{return{ok:!1,status:400,error:"Invalid or missing 'url' query parameter"}}if(t.protocol!=="https:"&&t.protocol!=="http:")return{ok:!1,status:400,error:"Only http(s) URLs are allowed"};try{const e=await f(t.hostname,{all:!0});if(!e.length||e.some(r=>l(r.address)))return{ok:!1,status:403,error:"Target host is not allowed"}}catch{return{ok:!1,status:400,error:"Could not resolve target host"}}const s=new AbortController,o=setTimeout(()=>s.abort(),p);try{const e=await fetch(t.toString(),{signal:s.signal,redirect:"follow",headers:{Accept:"application/geo+json, application/json, text/plain, */*"}});if(!e.ok)return{ok:!1,status:502,error:`Upstream responded ${e.status}`};const r=await e.text();if(r.length>c)return{ok:!1,status:413,error:"GeoJSON payload too large"};try{return{ok:!0,data:JSON.parse(r)}}catch{return{ok:!1,status:415,error:"Upstream did not return valid JSON"}}}catch(e){const r=e?.name==="AbortError";return{ok:!1,status:r?504:502,error:r?"Upstream fetch timed out":`Fetch failed: ${e?.message??String(e)}`}}finally{clearTimeout(o)}}const y=[{method:"get",path:"/api/geo/proxy",handler:async(a,t)=>{const s=typeof a.query.url=="string"?a.query.url:"",o=await d(s);if(!o.ok){t.status(o.status).json({error:o.error});return}t.set("Cache-Control","public, max-age=86400"),t.json(o.data)},validate:{query:m}}];export{d as fetchGeoJsonSafely,y as routes};
@@ -0,0 +1 @@
1
+ import d from"jsonwebtoken";import{z as r}from"zod";import{Client as m}from"@elastic/elasticsearch";import{config as p}from"../core/config";import{mineGoldenQuestions as f}from"../core/services/golden-questions-service";const a=new m(p.elasticsearch),y=".stkxp_golden_questions";function h(s){const o=s.auth?.username;if(o)return o;let e=s.cookies?.token;if(!e){const t=s.headers.authorization??"";e=t.startsWith("Bearer ")?t.slice(7):t||null}if(!e)return null;try{return d.decode(e)?.username??null}catch{return null}}async function l(s,o){const e=h(s);if(!e)return o.status(401).json({error:"Unauthorized"}),!1;try{return((await a.security.getUser({username:e}))[e]?.roles??[]).includes("superuser")?!0:(o.status(403).json({error:"Superuser access required"}),!1)}catch(t){return console.error("[golden-questions] role check failed:",t),o.status(500).json({error:"Internal server error"}),!1}}const g=r.object({window:r.enum(["7d","30d","90d"]).optional()}),q=r.object({tool:r.string().optional(),system:r.string().optional(),size:r.coerce.number().int().min(1).max(1e3).optional(),from:r.coerce.number().int().min(0).optional()}),k=[{method:"post",path:"/api/admin/golden-questions/mine",validate:{body:g},openapi:{summary:"Mine golden questions from boost-quadrant tool traces",description:"Reads .stkxp_tool_metrics boost-quadrant tools for the given window, mines up to 5 real (question, tool call, response) examples per tool from .stkxp_chat_tools/.stkxp_chat_runs/.stkxp_chats, and upserts .stkxp_golden_questions. Manual trigger only \u2014 no scheduler.",tags:["admin","golden-questions"],requiredRoles:["superuser"]},handler:async(s,o)=>{if(await l(s,o))try{const{window:e}=s.body,t=await f(e??"30d"),n={};for(const i of t)n[i.toolName]=(n[i.toolName]??0)+1;o.json({success:!0,window:e??"30d",total:t.length,perTool:n})}catch(e){console.error("[golden-questions] mine failed:",e),o.status(500).json({error:"Mining failed"})}}},{method:"get",path:"/api/admin/golden-questions",validate:{query:q},openapi:{summary:"List mined golden questions",description:"Returns mined (question, tool call, response) examples, filterable by tool/system.",tags:["admin","golden-questions"],requiredRoles:["superuser"]},handler:async(s,o)=>{if(await l(s,o))try{const e=s.query,t=[];e.tool&&t.push({term:{toolName:e.tool}}),e.system&&t.push({term:{system:e.system}});const n=await a.search({index:y,body:{query:t.length?{bool:{filter:t}}:{match_all:{}},sort:[{toolName:{order:"asc"}},{rank:{order:"asc"}}],size:e.size??100,from:e.from??0}}),i=typeof n.hits?.total=="number"?n.hits.total:n.hits?.total?.value??0,u=(n.hits?.hits??[]).map(c=>c._source);o.json({success:!0,total:i,items:u})}catch(e){if(e?.meta?.statusCode===404){o.json({success:!0,total:0,items:[]});return}console.error("[golden-questions] list failed:",e),o.status(500).json({error:"List failed"})}}}];export{k as routes};
@@ -0,0 +1 @@
1
+ import{randomUUID as q}from"crypto";import{Client as A}from"@elastic/elasticsearch";import{createGraph as y,getGraph as p,listGraphs as b,updateGraph as f,deleteGraph as R,cloneGraphForOwner as D,validateGraphDefinition as G,detectNonComposableSubgraphs as I,getGraphStats as C,resolveNodeConfigs as P,createNodeTemplate as _,getNodeTemplate as N,listNodeTemplates as S,deleteNodeTemplate as B,createValidator as E,getValidator as O,listValidators as T,deleteValidator as U,ensureIndices as x,detectCircularDependency as j,getGraphDependencyTree as L,setDefaultGraphForOwner as V}from"../core/services/graph-registry-service";import{resolveAssistantRuntimeGraphId as $,resolveTeamCoordinationGraphId as F}from"../core/services/graph-drilldown-service";import{snapshotBeforeUpdate as k}from"../core/services/version-snapshot-service";const z=new A({node:process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",auth:{username:process.env.ELASTICSEARCH_USER||"elastic",password:process.env.ELASTICSEARCH_PASSWORD||"diagnostics"},tls:{rejectUnauthorized:!1}});async function H(t,r){const e={};for(const n of t)e[n]=0;if(t.length===0)return e;const s=r?[{term:{owner:r}}]:[],a=await z.msearch({body:[{index:".stkxp_teams"},{size:0,query:{bool:{filter:[{terms:{graphId:t}},...s]}},aggs:{byGraph:{terms:{field:"graphId",size:t.length,include:t}}}},{index:".stkxp_assistants"},{size:0,query:{bool:{filter:[{terms:{graphId:t}},...s]}},aggs:{byGraph:{terms:{field:"graphId",size:t.length,include:t}}}}]});for(const n of a.responses){const c=n?.aggregations?.byGraph?.buckets??[];for(const i of c){const d=String(i.key);e[d]=(e[d]||0)+(i.doc_count||0)}}return e}x().catch(t=>{console.error("[GraphRegistry] Failed to initialize indices:",t)});const oe=[{path:"/api/graphs",method:"get",handler:async(t,r)=>{try{const e=t.auth,{owner:s,owners:a,enabled:n,tags:c,search:i,sortField:d,sortOrder:u}=t.query,o={};s&&(o.owner=s),a&&(o.owners=a.split(",").map(h=>h.trim()).filter(Boolean)),n!==void 0&&(o.enabled=n==="true"),c&&(o.tags=c.split(",")),i&&(o.search=i),d&&(o.sortField=d),u&&(o.sortOrder=u==="desc"?"desc":"asc");const l=await b(o);let m=l;if(t.query.withUsage==="true"&&l.length>0)try{const h=l.map(g=>g.id).filter(Boolean),w=o.owner??e?.username,v=await H(h,w);m=l.map(g=>({...g,usageCount:v[g.id]??0}))}catch(h){console.error("[GraphRegistry API] Usage count error:",h)}return r.json({success:!0,count:m.length,graphs:m})}catch(e){return console.error("[GraphRegistry API] List graphs error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/resolve",method:"get",handler:async(t,r)=>{try{const{assistantId:e,teamId:s}=t.query;let a=null;if(e)a=await $(String(e));else if(s)a=await F(String(s));else return r.status(400).json({error:"assistantId or teamId required"});return r.json({graphId:a})}catch(e){return r.status(500).json({error:e?.message||"resolve failed"})}}},{path:"/api/graphs/:id",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params;if(!e)return r.status(400).json({error:"Bad request: Graph ID is required"});const s=await p(e);return s?r.json({success:!0,graph:s}):r.status(404).json({error:"Not found: Graph not found",id:e})}catch(e){return console.error("[GraphRegistry API] Get graph error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs",method:"post",handler:async(t,r)=>{try{const e=t.auth,s=t.body;if(s.id||(s.id=q()),!s.name||!s.version)return r.status(400).json({error:"Bad request: name and version are required"});s.owner=e.username;const a=G(s);if(!a.valid)return r.status(400).json({error:"Bad request: Invalid graph definition",errors:a.errors});if(s.nodes?.some(i=>i.type==="subgraph")){try{await j(s.id,s.nodes)}catch(i){return r.status(400).json({error:"Bad request: Circular sub-graph dependency detected",message:i.message})}try{await I(s.nodes)}catch(i){return r.status(400).json({error:"Bad request: Non-composable subgraph reference",message:i.message})}}const n=await y(s),c=await p(n);return r.status(201).json({success:!0,id:n,graph:c,message:"Graph created successfully"})}catch(e){return console.error("[GraphRegistry API] Create graph error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:templateId/clone-for-assistant",method:"post",handler:async(t,r)=>{try{const e=t.auth,{templateId:s}=t.params,{previousCloneId:a}=t.body,n=await p(s);if(!n)return r.status(404).json({error:`Graph '${s}' not found`});const c=await D(n,e.username);return a&&(await p(a))?.clonedFrom&&await R(a),r.status(201).json({success:!0,graphId:c})}catch(e){return console.error("[GraphRegistry API] Clone-for-assistant error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:id/set-as-default",method:"patch",handler:async(t,r)=>{try{const e=t.auth,{id:s}=t.params,a=await p(s);return a?a.owner!==e.username?r.status(403).json({error:"Only the graph owner can set it as default"}):(await V(s,e.username),r.json({success:!0,message:`Graph '${a.name}' is now the default for ${e.username}`})):r.status(404).json({error:`Graph '${s}' not found`})}catch(e){return console.error("[GraphRegistry API] Set-as-default error:",e),r.status(500).json({error:e.message})}}},{path:"/api/graphs/:id/routing-rule",method:"patch",handler:async(t,r)=>{try{const{id:e}=t.params,{routingRuleId:s}=t.body;if(!s)return r.status(400).json({error:"routingRuleId is required"});const a=["assistant-generator","team-generator","skill-generator","prompt-generator","resource-generator"];let n=await p(e);if(!n){const u=a.find(o=>e.endsWith(`-${o}`));if(u){const o=e.slice(0,-(u.length+1)),l=await p(u);l&&(await y({...l,id:e,owner:o,isDefault:!1}),console.log(`[GraphRegistry API] Auto-provisioned generator graph "${e}" from template "${u}" for owner "${o}"`),n=await p(e))}}if(!n)return r.status(404).json({error:`Graph '${e}' not found`});const c=new Set(["assistant_tool_executor","assistant_response_generator","topic_detection","assistant_tool_cleaner","team_decider"]),i=u=>c.has(u.type)||u.id==="response_generator"||u.type==="human_approval",d=n.nodes.map(u=>i(u)?{...u,llm_routing_rule_id:s}:u);return await f(e,{nodes:d}),r.json({success:!0,nodesUpdated:d.filter(i).length})}catch(e){return console.error("[GraphRegistry API] Patch routing-rule error:",e),r.status(500).json({error:e.message})}}},{path:"/api/graphs/:id",method:"put",handler:async(t,r)=>{try{const e=t.auth,{id:s}=t.params,a=t.body;if(!s)return r.status(400).json({error:"Bad request: Graph ID is required"});const n=await p(s);if(!n)return r.status(404).json({error:"Not found: Graph not found",id:s});delete a.owner,delete a.createdAt,delete a.updatedAt;const c={...n,...a,id:s},i=G(c);if(!i.valid)return r.status(400).json({error:"Bad request: Invalid graph definition",errors:i.errors});const d=a.nodes??n.nodes;if(d?.some(o=>o.type==="subgraph")){try{await j(s,d)}catch(o){return r.status(400).json({error:"Bad request: Circular sub-graph dependency detected",message:o.message})}try{await I(d)}catch(o){return r.status(400).json({error:"Bad request: Non-composable subgraph reference",message:o.message})}}await k("graph",s,n,"manual_edit",e?.username),await f(s,a);const u=await p(s);return r.json({success:!0,id:s,graph:u,message:"Graph updated successfully"})}catch(e){return console.error("[GraphRegistry API] Update graph error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:id",method:"delete",handler:async(t,r)=>{try{const e=t.auth,{id:s}=t.params;return s?await p(s)?(await R(s),r.json({success:!0,id:s,message:"Graph deleted successfully"})):r.status(404).json({error:"Not found: Graph not found",id:s}):r.status(400).json({error:"Bad request: Graph ID is required"})}catch(e){return console.error("[GraphRegistry API] Delete graph error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:id/dependency-tree",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params;if(!e)return r.status(400).json({error:"Bad request: Graph ID is required"});const s=await L(e);return r.json({success:!0,graphId:e,tree:s})}catch(e){return console.error("[GraphRegistry API] Get dependency tree error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:id/node-configs",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params;if(!e)return r.status(400).json({error:"Bad request: Graph ID is required"});const s=await P(e);return r.json({success:!0,graphId:e,nodeConfigs:s})}catch(e){return console.error("[GraphRegistry API] Get node configs error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/graphs/:id/stats",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params;if(!e)return r.status(400).json({error:"Bad request: Graph ID is required"});const s=await C(e);return r.json({success:!0,stats:s})}catch(e){return console.error("[GraphRegistry API] Get stats error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/node-templates",method:"get",handler:async(t,r)=>{try{const{type:e,owner:s}=t.query,a={};e&&(a.type=e),s&&(a.owner=s);const n=await S(a);return r.json({success:!0,count:n.length,templates:n})}catch(e){return console.error("[GraphRegistry API] List node templates error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/node-templates/:id",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params,s=await N(e);return s?r.json({success:!0,template:s}):r.status(404).json({error:"Not found: Node template not found",id:e})}catch(e){return console.error("[GraphRegistry API] Get node template error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/node-templates",method:"post",handler:async(t,r)=>{try{const e=t.auth,s=t.body;if(!s.id||!s.name||!s.type)return r.status(400).json({error:"Bad request: id, name, and type are required"});s.owner=e.username;const a=await _(s);return r.status(201).json({success:!0,id:a,message:"Node template created successfully"})}catch(e){return console.error("[GraphRegistry API] Create node template error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/node-templates/:id",method:"delete",handler:async(t,r)=>{try{const{id:e}=t.params;return await B(e),r.json({success:!0,id:e,message:"Node template deleted successfully"})}catch(e){return console.error("[GraphRegistry API] Delete node template error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/validators",method:"get",handler:async(t,r)=>{try{const{type:e,owner:s}=t.query,a={};e&&(a.type=e),s&&(a.owner=s);const n=await T(a);return r.json({success:!0,count:n.length,validators:n})}catch(e){return console.error("[GraphRegistry API] List validators error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/validators/:id",method:"get",handler:async(t,r)=>{try{const{id:e}=t.params,s=await O(e);return s?r.json({success:!0,validator:s}):r.status(404).json({error:"Not found: Validator not found",id:e})}catch(e){return console.error("[GraphRegistry API] Get validator error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/validators",method:"post",handler:async(t,r)=>{try{const e=t.auth,s=t.body;if(!s.id||!s.name||!s.type)return r.status(400).json({error:"Bad request: id, name, and type are required"});s.owner=e.username;const a=await E(s);return r.status(201).json({success:!0,id:a,message:"Validator created successfully"})}catch(e){return console.error("[GraphRegistry API] Create validator error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}},{path:"/api/validators/:id",method:"delete",handler:async(t,r)=>{try{const{id:e}=t.params;return await U(e),r.json({success:!0,id:e,message:"Validator deleted successfully"})}catch(e){return console.error("[GraphRegistry API] Delete validator error:",e),r.status(500).json({error:"Internal server error",message:e.message})}}}];export{oe as routes};
@@ -0,0 +1 @@
1
+ import l from"jsonwebtoken";import{listGraphTemplates as p,seedGraphTemplatesFromProvisioning as u,applyGraphTemplate as i}from"../services/graph-templates-service";function c(r){let e=r.cookies?.token??null;if(!e){const s=r.headers.authorization??"";e=s.startsWith("Bearer ")?s.slice(7):s||null}return e||null}function n(r){const e=r.auth?.username??null,s=r.auth?.roles??null;if(e&&s)return{username:e,roles:s};const a=c(r);if(!a)return{username:null,roles:[]};try{const t=l.decode(a);return{username:t?.username??null,roles:Array.isArray(t?.roles)?t.roles:[]}}catch{return{username:null,roles:[]}}}const h=[{method:"get",path:"/api/graph-templates",openapi:{summary:"List graph templates",description:"Returns all Runtime templates from .stkxp_graph_templates, grouped by category.",tags:["graph-templates"]},handler:async(r,e)=>{try{const s=await p();e.json({success:!0,templates:s})}catch(s){console.error("[graph-templates] list failed:",s),e.status(500).json({success:!1,error:s.message||"Failed to list templates"})}}},{method:"post",path:"/api/graph-templates/:id/apply",openapi:{summary:"Apply a graph template",description:"Provisions any subgraph dependencies into the caller's account (idempotent) and returns nodes/edges with remapped graphId references ready for the Create Runtime flyout.",tags:["graph-templates"]},handler:async(r,e)=>{const{username:s}=n(r);if(!s){e.status(401).json({success:!1,error:"Authentication required"});return}const a=String(r.params.id);try{const{nodes:t,edges:o}=await i(a,s);e.json({success:!0,nodes:t,edges:o})}catch(t){console.error(`[graph-templates] apply "${a}" failed:`,t),e.status(500).json({success:!1,error:t.message||"Apply failed"})}}},{method:"post",path:"/api/admin/graph-templates/seed",openapi:{summary:"Seed graph templates",description:"Seeds .stkxp_graph_templates from __test_provisioning__ graphs. Generates AI descriptions using the caller's default LLM. Superuser only.",tags:["admin","graph-templates"],requiredRoles:["superuser"]},handler:async(r,e)=>{const{username:s,roles:a}=n(r);if(!a.includes("superuser")){e.status(403).json({success:!1,error:"Superuser role required"});return}if(!s){e.status(401).json({success:!1,error:"Authentication required"});return}try{const t=await u(s);e.json({success:!0,seeded:t.seeded,updated:t.updated,errors:t.errors,total:t.seeded+t.updated})}catch(t){console.error("[graph-templates] seed failed:",t),e.status(500).json({success:!1,error:t.message||"Seed failed"})}}}];export{h as routes};
@@ -0,0 +1,35 @@
1
+ import _ from"jsonwebtoken";import{Client as k}from"@elastic/elasticsearch";import{config as w}from"../core/config";import{LLMServicesFactory as R}from"../core/services/llm-services";import{searchResourcesRRF as S}from"./resources-routes";import{BRAND_NAME as l}from"../core/app-config/branding";const E=new k(w.elasticsearch),h=process.env.STKXP_HELPDESK_MODEL||"claude-haiku-4-5-20251001",x=1024,m=30,P="__helpdesk_kb__",L=6;function b(s){const t=s.auth?.username;if(t)return t;let n=s.cookies?.token;if(!n){const r=s.headers.authorization??"";n=r.startsWith("Bearer ")?r.slice(7):r||null}if(!n)return null;try{return _.decode(n)?.username??null}catch{return null}}const A=`You are the ${l} helpdesk assistant for end-users who just signed up or are discovering the platform.
2
+
3
+ Audience: business users \u2014 never developers, never operators. They want to USE ${l}, not build it. Reformulate any technical content from your reference corpus into plain, action-oriented user voice.
4
+
5
+ Vocabulary \u2014 user-facing names ONLY:
6
+ - Use: Platform, Tool, Runtime (or Skill \u2014 same thing), Assistant, Team, LLM Provider, LLM Model, Routing Rule, Chat, Settings page.
7
+ - NEVER use these internal terms in your reply, even if they appear in the reference corpus:
8
+ - "LangGraph", "graph", "node", "edge", "subgraph"
9
+ - "system node", "tool_executor", "response_generator", "prompt_adapter", "topic_detection", "check_reset", "local_extractor", "tool_cleaner", "team_pipeline", "team_parallel", "team_decider"
10
+ - "WebSocket", "WS handler", "stream", "stream events"
11
+ - "MCP", "MCP server", "MCP package", "MCP tool", any name starting with "stkxp-" or ".stkxp_"
12
+ - "Elasticsearch", "ES index", "RRF", "BM25", "semantic_text", "e5", "embeddings"
13
+ - "JWT", "API endpoint", "REST", "HTTP", "JSON-LD", "frontmatter"
14
+ - File paths, code identifiers, function names, class names.
15
+ - "owner", "tenant", "scope filter" \u2014 say "your account" / "shared with you" instead.
16
+ - If a corpus entry uses one of these terms, translate it. "LangGraph runtime" \u2192 "the AI workflow". "MCP server" \u2192 "external service". "memory_search tool" \u2192 "memory recall feature".
17
+
18
+ Style:
19
+ - Reply in the language the user wrote in (French or English).
20
+ - 2\u20134 short sentences by default. Lists allowed when the user explicitly asks "how to" steps.
21
+ - Action-oriented: tell the user what to click or where to go in the app ("Settings \u2192 Runtimes", "the home page wizard", "the bell icon in the header").
22
+ - If the user asks something outside ${l} scope (general programming, weather, infrastructure ops), politely redirect to the product topics.
23
+ - If the retrieved context below doesn't cover the question, say so directly ("Cette fonctionnalit\xE9 n'est pas encore couverte par mon aide \u2014 contactez le support") and suggest the closest related feature.
24
+ - Never invent feature names. Use only the names listed above and in the retrieved context.
25
+
26
+ Below this preamble you will find the **top-K knowledge chunks retrieved for the current question**. They are pre-filtered by semantic relevance \u2014 treat them as ground truth, but rewrite in user voice.
27
+ `;function M(s){return!s||s.length===0?"[No relevant knowledge retrieved for this query.]":s.map((t,n)=>{const r=(t.tags||[]).find(i=>i!=="helpdesk"&&i!=="kb");return`## ${r?`KB#${n+1} [${r}]`:`KB#${n+1}`}
28
+
29
+ ${t.prompt_text??""}`}).join(`
30
+
31
+ ---
32
+
33
+ `)}const O=[{method:"post",path:"/api/me/helpdesk/message",handler:async(s,t)=>{const n=b(s);if(!n)return t.status(401).json({error:"Unauthorized"});const r=Array.isArray(s.body?.messages)?s.body.messages:null;if(!r||r.length===0)return t.status(400).json({error:"messages array is required"});if(r.length>m)return t.status(413).json({error:`conversation too long (>${m} turns)`});const u=(await R.getInstance(E).getProvidersService().listProviders({type:"anthropic",limit:10},n)).providers.find(e=>e.apiKey&&e.apiKey!=="");if(!u?.apiKey)return t.status(412).json({error:"anthropic_provider_required",message:"Configurez votre cl\xE9 Anthropic dans les LLM providers pour utiliser le helpdesk."});const p=[...r].reverse().find(e=>e.role==="user"),d=typeof p?.content=="string"?p.content:"";let c=[];if(d.length>=2)try{c=await S({owner:P,query:d,k:L,type:"knowledge_base"})}catch(e){console.error(`[helpdesk] retrieval failed: ${e?.message??e}`)}const g=[{type:"text",text:A+`
34
+
35
+ `+M(c)}],f=r.map(e=>({role:e.role==="assistant"?"assistant":"user",content:typeof e.content=="string"?e.content:String(e.content??"")}));try{const e=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":u.apiKey,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:h,max_tokens:x,system:g,messages:f})});if(!e.ok){const o=await e.text();return t.status(502).json({error:"anthropic_call_failed",status:e.status,details:o.slice(0,500)})}const a=await e.json(),v=Array.isArray(a?.content)?a.content.filter(o=>o?.type==="text").map(o=>o.text).join(""):"";return t.json({ok:!0,message:{role:"assistant",content:v},model:a?.model??h,usage:a?.usage??null,retrievedCount:c.length})}catch(e){return t.status(502).json({error:"helpdesk_call_failed",details:e?.message??String(e)})}}}];export{O as routes};
@@ -0,0 +1 @@
1
+ import i from"express";import{z as e}from"zod";import{exportChatToEmailHtml as h}from"../services/html-service";import{getAuthUsername as p}from"../core/utils/ownership";const a=i.Router(),d=e.object({chatId:e.string(),messageId:e.string().optional()});function l(t){if("html"in t)return{status:200,body:{html:t.html}};switch(t.error){case"not_found":return{status:404,body:{error:"Chat not found"}};case"no_message":return{status:404,body:{error:"No matching message found in this chat"}};case"system_message":return{status:400,body:{error:"Cannot export a system message"}}}}a.post("/export-chat",async(t,o)=>{try{const{chatId:r,messageId:n}=d.parse(t.body),s=p(t);if(!s){o.status(401).json({error:"Not authenticated"});return}const u=await h({chatId:r,messageId:n,username:s}),{status:m,body:c}=l(u);o.status(m).json(c)}catch(r){if(r instanceof e.ZodError){o.status(400).json({error:"Validation error",details:r.errors});return}console.error("[html-routes] export-chat error:",r),o.status(500).json({error:"Internal server error"})}});var j=a;export{j as default,l as mapResultToResponse};
@@ -0,0 +1 @@
1
+ import{Router as c}from"express";import{runWithOwnerScope as f}from"../core/utils/owner-scope";import{getAuthUsername as R,isSuperuser as l}from"../core/utils/ownership";import{routes as y}from"./clusters";import{routes as d}from"./nodes";import{routes as g}from"./indices";import{routes as b}from"./auth";import{routes as h}from"./settings";import{routes as E}from"./llm";import{routes as $}from"./llm-control-plane";import{routes as U}from"./chats";import{routes as q}from"./chat-tools";import{routes as T}from"./chat-llms";import{chatTracesRoutes as x}from"./chat-traces";import{routes as P}from"./prompts-routes";import{routes as A}from"./mcp-tools-routes";import{routes as D}from"./mcp-query-routes";import{routes as w}from"./schema-routes";import{routes as C}from"./langgraph";import{routes as S}from"./a2a-server-routes";import{routes as j}from"./tools-routes";import{routes as I}from"./mcp-servers-routes";import{routes as Z}from"./admin";import{routes as z}from"./graph-registry";import{routes as k}from"./versions-routes";import{routes as v}from"./triggers";import{routes as G}from"./monitoring";import{routes as L}from"./monitoring-analytics";import{routes as M}from"./tool-analytics";import{routes as O}from"./llm-analytics";import{routes as Q}from"./assistants-routes";import{routes as H}from"./teams-routes";import{routes as W}from"./team-schedule-routes";import{routes as _}from"./consumptions-routes";import{routes as B}from"./resources-routes";import{routes as F}from"./resources-ingest-routes";import{routes as J}from"./memories-routes";import{routes as K}from"./node-types-routes";import{routes as N}from"./data-transfer-routes";import{routes as V}from"./data-admin-routes";import{routes as X,publicRoutes as Y}from"./share-routes";import{routes as oo,publicRoutes as so}from"./mcp-gateway-routes";import{routes as ro}from"./me-routes";import{routes as eo}from"./helpdesk-routes";import{routes as to}from"./plan-routes";import{routes as uo}from"./tool-metrics-routes";import{routes as io}from"./golden-questions-routes";import{routes as ao}from"./quality-routes";import{routes as mo}from"./tool-history-routes";import{routes as no}from"./billing-routes";import{routes as po,publicRoutes as co}from"./live-resources-routes";import{routes as fo,publicRoutes as Ro}from"./asyncapi-routes";import{routes as lo}from"./webhooks-routes";import{routes as yo}from"./geo-proxy-routes";import{routes as go}from"./platform-requests-routes";import{routes as bo}from"./graph-templates-routes";import{routes as ho}from"./team-optimizer-routes";function Zs(){const u=c();return u.get("/health",(s,o)=>{o.json({status:"ok"})}),u.post("/chat",async(s,o)=>{const{message:m}=s.body;o.json({response:`Echo: ${m}`})}),u}const n=[...y,...d,...g,...h,...E,...$,...U,...q,...T,...x,...P,...A,...D,...w,...j,...I,...Z,...z,...k,...v,...L,...G,...M,...O,...Q,...H,...W,..._,...B,...F,...J,...K,...N,...V,...X,...oo,...yo,...ro,...eo,...to,...no,...po,...fo,...uo,...io,...ao,...mo,...go,...bo,...ho],p=[...b,...C,...S,...Y,...so,...co,...Ro,...lo],zs=n,ks=p;function a(u,s){const o=u.safeParse(s);return o.success?{success:!0,data:o.data}:{success:!1,error:o.error}}function vs(u){n.forEach(({method:s,path:o,handler:m,validate:e})=>{console.log(`Defining Protected route: ${s.toUpperCase()} ${o}`),u[s](o,async(t,i)=>{try{if(e?.params){const r=a(e.params,t.params);if(!r.success)return i.status(400).json({error:"Invalid params",issues:r.error.format()})}if(e?.query){const r=a(e.query,t.query);if(!r.success)return i.status(400).json({error:"Invalid query",issues:r.error.format()})}if(e?.body){const r=a(e.body,t.body);if(!r.success)return i.status(400).json({error:"Invalid body",issues:r.error.format()})}await f({owner:R(t),isSuperuser:l(t)},()=>m(t,i))}catch(r){console.error(`[${s.toUpperCase()}] ${o}`,r),i.status(500).json({error:"Internal Server Error"})}})})}function Gs(u){p.forEach(({method:s,path:o,handler:m,validate:e})=>{console.log(`Defining Unprotected route: ${s.toUpperCase()} ${o}`),u[s](o,async(t,i)=>{try{e?.params&&(a(e.params,t.params).success||console.log(`Error params Unprotected route: ${s.toUpperCase()} ${o}`)),e?.query&&(a(e.query,t.query).success||console.log(`Error query Unprotected route: ${s.toUpperCase()} ${o}`)),e?.body&&(a(e.body,t.body).success||console.log(`Error body Unprotected route: ${s.toUpperCase()} ${o}`)),await m(t,i)}catch(r){console.error(`[${s.toUpperCase()}] ${o}`,r),i.status(500).json({error:"Internal Server Error"})}})})}export{Zs as createApiRouter,vs as defineProtectedRoutes,Gs as defineUnprotectedRoutes,zs as protectedRouteDefinitions,ks as unprotectedRouteDefinitions};
@@ -0,0 +1 @@
1
+ import O from"lodash";import z from"moment";import _ from"zod";import{ElasticsearchWrapper as v}from"../core/services/elasticsearch-wrapper";import{config as k}from"../core/config";import{filterQueryShards as N,filterQueryIndices as w,filterResult as D,parseNowExpression as d}from"../utils";function T(t){const c=new Set(t),a={};for(let r of c){r=r.replace(/partial-/,"");const o=typeof r=="string"?r.match(/^(.*?)-\d{4}\.\d{2}\.\d{2}-\d{5,}$/):null;if(o){const i=o[1]+"-*";a[i]=(a[i]||0)+1}else a[r]=(a[r]||0)+1}return a}const h={params:_.object({namespace:_.string().optional()}),body:_.object({query:_.object({clusters:_.object({value:_.string().optional()}),phases:_.object({value:_.string().optional()}).optional(),prefix:_.object({value:_.string().optional()}).optional(),suffix:_.object({value:_.string().optional()}).optional(),start:_.object({value:_.string().optional()}),end:_.object({value:_.string()}),source:_.object({value:_.any().optional()}).optional()}).optional()})},U=[{method:"post",path:"/api/stack_expert/clusters/indices/shards",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0);var r=d(t.body.query.start.value),o=d(t.body.query.end.value);try{const l={query:{bool:{filter:w(t.body.query)}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.nodes.name",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}}}}}},s=await a.search({index:"logs-stack_expert.nodes_info-*",...l}),y=N(t.body.query);y.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});const u={query:{bool:{filter:y,must_not:[]}},size:0,aggs:{by_nodename:{terms:{field:"stkxp.node",size:1e3},aggs:{by_index:{terms:{field:"stkxp.index",size:1e3},aggs:{by_prirep:{terms:{field:"stkxp.prirep",size:50},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:o.diff(r,"minutes")/10},aggs:{store:{max:{field:"stkxp.store"}},derivative_store:{derivative:{buckets_path:"store",gap_policy:"keep_values"}}}}}}}}}}}};console.log("xxxxxxxxxxxxx",JSON.stringify(u));const S=await a.search({index:"logs-stack_expert.cat_shards-*",...u});console.log("result_info",s),console.log("result_shards",S);const g=O.keyBy(s.aggregations.by_nodename.buckets,"key"),n=O.keyBy(S.aggregations.by_nodename.buckets,"key");console.log("byKey1",g),console.log("byKey2",n);const f=O.intersection(O.keys(g),O.keys(n));console.log("commonKeys",f);const x=f.map(b=>({key:b,...O.merge({},g[b],n[b])}));return c.json({body:{time:new Date().toISOString(),result:x||[]}})}catch(i){console.log(i)}}},{method:"post",path:"/api/stack_expert/clusters/indices/settings",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0);var r=d(t.body.query.start.value),o=d(t.body.query.end.value);try{const i=N(t.body.query);i.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});const l={query:{bool:{filter:i,must_not:[]}},size:1},s=await a.search({index:"logs-stack_expert.indices_get_settings-*",...l});return console.log("result_indices",s),c.json({body:{time:new Date().toISOString(),result:s||[]}})}catch(i){console.log(i)}}},{method:"post",path:"/api/stack_expert/clusters/indices/test",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0);function r(e){const s=e.aggregations.by_day.buckets.start.by_index.buckets,y=e.aggregations.by_day.buckets.end.by_index.buckets,u=Object.fromEntries(s.map(m=>[m.key,m])),p=Object.fromEntries(y.map(m=>[m.key,m])),S=new Set([...Object.keys(u),...Object.keys(p)]);let g=0,n=0,f=0,x=0,b=0;S.forEach(m=>{const q=u[m]?.top_valeur?.top?.[0]?.metrics,j=p[m]?.top_valeur?.top?.[0]?.metrics;console.log(m,q,j);const I=!u[m]&&!!p[m],J=!!u[m]&&!p[m];if(I&&g++,J&&n++,q&&j){const M=q["stkxp.stats.total.store.size_in_bytes"]??0,K=(j["stkxp.stats.total.store.size_in_bytes"]??0)-M;x+=K,b++;const B=q["stkxp.stats.total.search.query_total"]??0,C=(j["stkxp.stats.total.search.query_total"]??0)-B;f+=C}});const E=b>0?x/b:0;return{total_indexes_created:g,total_indexes_deleted:n,volume_moyen_ingere_bytes:E,total_recherches:f}}function o(e){const s=e.aggregations.by_day.buckets.start.by_index.buckets,y=e.aggregations.by_day.buckets.end.by_index.buckets,u=Object.fromEntries(s.map(n=>[n.key,n])),p=Object.fromEntries(y.map(n=>[n.key,n])),S=new Set([...Object.keys(u),...Object.keys(p)]),g=[];return S.forEach(n=>{const f=u[n]?.top_valeur?.top?.[0]?.metrics,x=p[n]?.top_valeur?.top?.[0]?.metrics,b=f?.["stkxp.stats.primaries.docs.count"]??null,E=x?.["stkxp.stats.primaries.docs.count"]??null,m=f?.["stkxp.stats.total.search.query_total"]??null,q=x?.["stkxp.stats.total.search.query_total"]??null,j=f?.["stkxp.stats.total.store.size_in_bytes"]??null,I=x?.["stkxp.stats.total.store.size_in_bytes"]??null;g.push({index:n,docs_diff:b!=null&&E!=null?E-b:null,query_diff:m!=null&&q!=null?q-m:null,size_diff:j!=null&&I!=null?I-j:null,created:!!u[n]&&!p[n],deleted:!u[n]&&!!p[n]})}),g}try{const e="logs-stack_expert.indices_stats-*",s=w(t.body.query);var i=d(t.body.query.start.value),l=d(t.body.query.end.value);const y={query:{bool:{filter:s,must:[{wildcard:{"stkxp.index":"*stack_expert*"}}]}},size:0,aggs:{by_day:{filters:{filters:{end:{range:{"@timestamp":{gte:z(l).subtract(10,"minutes").format(),lte:z(l).format()}}},start:{range:{"@timestamp":{gte:z(i).format(),lt:z(l).subtract(10,"minutes").format()}}}}},aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_valeur:{top_metrics:{metrics:[{field:"@timestamp"},{field:"stkxp.stats.primaries.docs.count"},{field:"stkxp.stats.total.store.total_data_set_size_in_bytes"},{field:"stkxp.stats.total.store.size_in_bytes"},{field:"stkxp.stats.total.search.query_total"},{field:"stkxp.stats.total.search.query_time_in_millis"},{field:"stkxp.stats.primaries.indexing.index_total"},{field:"stkxp.stats.primaries.indexing.index_time_in_millis"}],sort:{"@timestamp":"desc"}}}}}}}}};console.log(JSON.stringify(y));const u=await a.search({index:e,...y}),p={query:{bool:{filter:s,must:[{wildcard:{"stkxp.index":"*stack_expert*"}}]}},size:0,aggs:{by_day:{filters:{filters:{end:{range:{"@timestamp":{gte:z(l).subtract(10,"minutes").format(),lte:z(l).format()}}},start:{range:{"@timestamp":{gte:z(i).subtract(10,"minutes").format(),lte:z(i).format()}}}}},aggs:{by_index:{terms:{field:"stkxp.name",size:1e4},aggs:{top_valeur:{top_metrics:{metrics:[{field:"@timestamp"},{field:"stkxp.settings.index.number_of_shards"},{field:"stkxp.settings.index.number_of_replicas"},{field:"stkxp.settings.index.creation_date"}],sort:{"@timestamp":"desc"}}}}}}}}},g=await a.search({index:"logs-stack_expert.indices_get_settings-*",...p}),n=O.merge({},u,g);return c.json({body:{time:new Date().toISOString(),result:r(n)||[]}})}catch(e){console.log(e)}}},{method:"post",path:"/api/stack_expert/clusters/indices/operations",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.indices_stats-*",o=w(t.body.query),i=d(t.body.query.start.value),l=d(t.body.query.end.value),e=t.body.query.source.value;console.log("source",e),o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const s={query:{bool:{filter:o,must_not:[]}},size:0,aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:e,sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(i,"minutes")/10},aggs:{bulk_total:{max:{field:"stkxp.stats.total.bulk.total_operations"}},derivative_bulk_total:{derivative:{buckets_path:"bulk_total",gap_policy:"keep_values"}},bulk_time_in_millis:{max:{field:"stkxp.stats.total.bulk.total_time_in_millis"}},derivative_bulk_time_in_millis:{derivative:{buckets_path:"bulk_time_in_millis"}},fetch_total:{max:{field:"stkxp.stats.total.search.fetch_total"}},derivative_fetch_total:{derivative:{buckets_path:"fetch_total"}},fetch_time_in_millis:{max:{field:"stkxp.stats.total.search.fetch_time_in_millis"}},derivative_fetch_time_in_millis:{derivative:{buckets_path:"fetch_time_in_millis"}},latency_fetch_total:{bucket_script:{buckets_path:{time:"derivative_fetch_time_in_millis",count:"derivative_fetch_total"},script:"params.time / params.count"}},query_total:{max:{field:"stkxp.stats.total.search.query_total"}},derivative_query_total:{derivative:{buckets_path:"query_total"}},query_time_in_millis:{max:{field:"stkxp.stats.total.search.query_time_in_millis"}},derivative_query_time_in_millis:{derivative:{buckets_path:"query_time_in_millis"}},latency_query_total:{bucket_script:{buckets_path:{time:"derivative_query_time_in_millis",count:"derivative_query_total"},script:"params.time / params.count"}},get_total:{max:{field:"stkxp.stats.total.get.total"}},derivative_get_total:{derivative:{buckets_path:"get_total"}},get_time_in_millis:{max:{field:"stkxp.stats.total.get.time_in_millis"}},derivative_get_time_in_millis:{derivative:{buckets_path:"get_time_in_millis"}},latency_get_total:{bucket_script:{buckets_path:{time:"derivative_get_time_in_millis",count:"derivative_get_total"},script:"params.time / params.count"}}}}}}}};console.log(JSON.stringify(s));const y=await a.search({index:r,...s});return c.json({body:{time:new Date().toISOString(),result:D(y?.aggregations?.by_index?.buckets)||[]}})}catch(s){console.log(s)}}},{method:"post",path:"/api/stack_expert/clusters/indices/storage",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.indices_stats-*",o=w(t.body.query),i=d(t.body.query.start.value),l=d(t.body.query.end.value);o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const e={query:{bool:{filter:o,must_not:[]}},size:0,aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(i,"minutes")/10},aggs:{primaries_store_size_in_bytes:{max:{field:"stkxp.stats.primaries.store.size_in_bytes"}},derivative_primaries_store_size_in_bytes:{derivative:{buckets_path:"primaries_store_size_in_bytes",gap_policy:"keep_values"}},store_size_in_bytes:{max:{field:"stkxp.stats.total.store.size_in_bytes"}},derivative_store_size_in_bytes:{derivative:{buckets_path:"store_size_in_bytes",gap_policy:"keep_values"}}}}}}}};console.log(JSON.stringify(e));const s=await a.search({index:r,...e});return c.json({body:{time:new Date().toISOString(),result:D(s?.aggregations?.by_index?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/indices/segments",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.indices_stats-*",o=w(t.body.query);var i=d(t.body.query.start.value),l=d(t.body.query.end.value);o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const e={query:{bool:{filter:o,must_not:[]}},size:0,aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(i,"minutes")/10},aggs:{total_segments_count:{max:{field:"stkxp.stats.total.segments.count"}},derivative_total_segments_count:{derivative:{buckets_path:"total_segments_count",gap_policy:"keep_values"}},merges_total:{max:{field:"stkxp.stats.total.merges.total"}},derivative_merges_total:{derivative:{buckets_path:"merges_total",gap_policy:"keep_values"}},merges_time_in_millis:{max:{field:"stkxp.stats.total.merges.total_time_in_millis"}},derivative_merges_time_in_millis:{derivative:{buckets_path:"merges_time_in_millis",gap_policy:"keep_values"}},latency_merges_total:{bucket_script:{buckets_path:{time:"derivative_merges_time_in_millis",count:"derivative_merges_total"},script:"params.time / params.count"}}}}}}}};console.log(JSON.stringify(e));const s=await a.search({index:r,...e});return c.json({body:{time:new Date().toISOString(),result:D(s?.aggregations?.by_index?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/indices/documents",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.indices_stats-*",o=w(t.body.query),i=d(t.body.query.start.value),l=d(t.body.query.end.value);o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const e={query:{bool:{filter:o,must_not:[]}},size:0,aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(i,"minutes")/10},aggs:{total_docs_count:{max:{field:"stkxp.stats.total.docs.count"}},derivative_total_docs_count:{derivative:{buckets_path:"total_docs_count"}},store_size_in_bytes:{max:{field:"stkxp.stats.total.store.size_in_bytes"}},derivative_store_size_in_bytes:{derivative:{buckets_path:"store_size_in_bytes"}}}}}}}};console.log(JSON.stringify(e));const s=await a.search({index:r,...e});return c.json({body:{time:new Date().toISOString(),result:D(s?.aggregations?.by_index?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/indices/mapping",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.indices_stats-*",o=w(t.body.query),i=d(t.body.query.start.value),l=d(t.body.query.end.value);o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const e={query:{bool:{filter:o,must_not:[]}},size:0,aggs:{by_index:{terms:{field:"stkxp.index",size:1e4},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"@timestamp":{order:"desc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:l.diff(i,"minutes")/10},aggs:{primaries_store_size_in_bytes:{max:{field:"stkxp.stats.primaries.store.size_in_bytes"}},derivative_primaries_store_size_in_bytes:{derivative:{buckets_path:"primaries_store_size_in_bytes"}},store_size_in_bytes:{max:{field:"stkxp.stats.total.store.size_in_bytes"}},derivative_store_size_in_bytes:{derivative:{buckets_path:"store_size_in_bytes"}}}}}}}};console.log(JSON.stringify(e));const s=await a.search({index:r,...e});return c.json({body:{time:new Date().toISOString(),result:D(s?.aggregations?.by_index?.buckets)||[]}})}catch{}}},{method:"post",path:"/api/stack_expert/clusters/indices/data_streams",validate:h,handler:async(t,c)=>{const a=new v({...k.elasticsearch,auth:{username:t.auth.username,password:t.auth.password}},!0),r="logs-stack_expert.data_streams_stats-*",o=w(t.body.query),i=d(t.body.query.start.value),l=d(t.body.query.end.value);o.push({range:{"@timestamp":{gte:t.body.query.start.value,lte:t.body.query.end.value}}});try{const e={query:{bool:{filter:o}},size:0,aggs:{by_datastream:{terms:{field:"stkxp.data_stream",size:1e3},aggs:{top_doc:{top_hits:{_source:["@timestamp","data_stream.*","stkxp.*"],sort:[{"stkxp.data_stream":{order:"asc"}}],size:1}},over_time:{auto_date_histogram:{field:"@timestamp",buckets:10},aggs:{store_size_bytes:{max:{field:"stkxp.store_size_bytes"}},backing_indices:{max:{field:"stkxp.backing_indices"}},maximum_timestamp:{max:{field:"stkxp.maximum_timestamp"}}}}}}}};console.log(JSON.stringify(e));const s=await a.search({index:r,...e});return c.json({body:{time:new Date().toISOString(),result:D(s?.aggregations?.by_datastream?.buckets)||[]}})}catch(e){console.log(e)}}}];export{U as routes};
@@ -0,0 +1 @@
1
+ import{Router as d}from"express";import{BRAND_NAME as y}from"../core/app-config/branding";const i=d(),m=process.env.STACK_EXPERT_API_URL||"http://localhost:4000",h=`${m}/api/stack_expert/inventory`;async function u(r){try{const t=await fetch(`${h}${r}`);if(!t.ok)throw new Error(`API returned ${t.status}: ${t.statusText}`);return await t.json()}catch(t){throw console.error(`Error fetching from inventory API (${r}):`,t.message),t}}i.get("/systems",async(r,t)=>{try{const s=await u("/systems"),a=s.systems.map(o=>o.system).sort();t.json({success:!0,systems:a,total:a.length,route_counts:s.systems.reduce((o,n)=>(o[n.system]=n.route_count,o),{}),mode:s.mode})}catch(s){console.error("Error fetching integration systems:",s),t.status(500).json({success:!1,error:"Failed to fetch integration systems from API",details:s.message})}}),i.get("/systems/:system/routes",async(r,t)=>{try{const{system:s}=r.params,a=await u("/paths?format=grouped"),o=a.grouped_routes.find(n=>n.system===s);if(!o)return t.status(404).json({success:!1,error:`System '${s}' not found`});t.json({success:!0,system:s,routes:o.routes,total:o.count,mode:a.mode})}catch(s){console.error(`Error fetching routes for system ${r.params.system}:`,s),t.status(500).json({success:!1,error:"Failed to fetch routes from API",details:s.message})}}),i.get("/systems/:system/topics",async(r,t)=>{try{const{system:s}=r.params,a=await u("/paths?format=grouped"),o=a.grouped_routes.find(c=>c.system===s);if(!o)return t.status(404).json({success:!1,error:`System '${s}' not found`});const n=o.routes.map((c,e)=>({name:c.endpoint,fileName:`${c.endpoint}.ts`,path:c.path,method:c.method,dashboard_title:c.dashboard_title}));t.json({success:!0,system:s,topics:n,total:n.length,note:"This endpoint is deprecated. Use /systems/:system/routes instead",mode:a.mode})}catch(s){console.error(`Error fetching topics for system ${r.params.system}:`,s),t.status(500).json({success:!1,error:"Failed to fetch topics from API",details:s.message})}}),i.get("/systems/:system/topics/:topic/endpoints",async(r,t)=>{try{const{system:s,topic:a}=r.params,o=await u(`/paths?format=detailed&search=${s}`);console.log(`Fetched ${o.routes.length} routes from API for system '${s}'`,o.routes);const n=o.routes.filter(e=>e.system===s&&e.endpoint===a);if(n.length===0)return t.status(404).json({success:!1,error:`Route '${a}' not found in system '${s}'`});const c=n.map(e=>({method:e.method,path:e.path,endpoint:e.endpoint,source:e.source,dashboard:e.dashboard}));t.json({success:!0,system:s,topic:a,endpoints:c,total:c.length,note:"This endpoint is deprecated. Routes are now accessed directly",mode:o.mode})}catch(s){console.error(`Error fetching endpoints for ${r.params.system}/${r.params.topic}:`,s),t.status(500).json({success:!1,error:"Failed to fetch endpoints from API",details:s.message})}}),i.get("/search",async(r,t)=>{try{const{query:s="",type:a="all"}=r.query,o=String(s).toLowerCase();if(!o)return t.status(400).json({success:!1,error:"Search query is required"});const n=[];(a==="all"||a==="system")&&(await u("/systems")).systems.forEach(e=>{e.system.toLowerCase().includes(o)&&n.push({type:"system",system:e.system,match:e.system,route_count:e.route_count,dashboard_count:e.dashboard_count})}),(a==="all"||a==="endpoint"||a==="route")&&(await u(`/paths?format=detailed&search=${o}`)).routes.forEach(e=>{n.push({type:"endpoint",system:e.system,endpoint:e.endpoint,path:e.path,method:e.method,match:e.endpoint,dashboard_title:e.dashboard?.title})}),t.json({success:!0,query:o,results:n,total:n.length,mode:routesData?.mode||"unknown"})}catch(s){console.error("Error searching integrations:",s),t.status(500).json({success:!1,error:"Failed to search integrations via API",details:s.message})}}),i.get("/stats",async(r,t)=>{try{const s=await u("/stats");t.json({success:!0,...s})}catch(s){console.error("Error fetching integration stats:",s),t.status(500).json({success:!1,error:"Failed to fetch integration stats from API",details:s.message})}}),i.get("/mode",async(r,t)=>{try{const s=await u("/mode");t.json({success:!0,...s})}catch(s){console.error("Error fetching route mode:",s),t.status(500).json({success:!1,error:"Failed to fetch route mode from API",details:s.message})}}),i.get("/health",async(r,t)=>{try{const s=await fetch(`${m}/health`),a=await s.json();t.json({success:!0,api_url:m,api_status:s.ok?"reachable":"error",api_response:a})}catch(s){t.status(503).json({success:!1,api_url:m,api_status:"unreachable",error:`Cannot reach ${y} API`,details:s.message,hint:"Make sure stkxp-api server is running on port 4000"})}});var R=i;export{R as default};
@@ -0,0 +1,7 @@
1
+ import{createLangGraphService as A}from"../services/langgraph-service";import{userToken as w}from"../services/auth";import{isApiKeyActive as R}from"../core/services/api-keys-service";let o=null;async function p(){return o||(o=await A()),o}async function g(n){const e=n.headers["x-api-key"],r=n.headers.authorization,t=e||(r&&r.startsWith("Bearer ")?r.substring(7):void 0);if(!t)return null;const a=w(t);if(!a||!a.username||a.type==="apikey"&&!await R(a.username,a.jti))return null;const s=Array.isArray(a.scopes)?a.scopes:null;return{token:t,username:a.username,scopes:s}}function c(n,e){return n.scopes===null||n.scopes.includes(e)}function d(n,e){return n.status(403).json({error:`Forbidden: this API key is missing the required scope '${e}'`})}const b=[{path:"/api/langgraph/invoke",method:"post",requiresAuth:!1,handler:async(n,e)=>{try{const r=await g(n);if(!r)return e.status(401).json({error:"Unauthorized: missing, invalid, or revoked credentials (Bearer JWT or X-Api-Key)"});if(!c(r,"agent:execute"))return d(e,"agent:execute");const{input:t,threadId:a,runId:s,checkpointNs:l,queryParams:h,graphId:f,teamId:y,test:m}=n.body;if(!t||typeof t!="string")return e.status(400).json({error:"Bad request: 'input' field is required and must be a string"});const v=await p(),u={input:t,graphId:f,teamId:y,threadId:a,runId:s,checkpointNs:l,queryParams:h,userToken:r.token,test:m},i=[];let k=null;for await(const I of v.invoke(u))i.push(I),I.type==="done"&&(k=I.data);return e.json({success:!0,runId:u.runId,threadId:u.threadId,result:k,events:i})}catch(r){return console.error("[LangGraph API] Invoke error:",r),e.status(500).json({error:"Internal server error",message:r.message})}}},{path:"/api/langgraph/stream",method:"post",requiresAuth:!1,handler:async(n,e)=>{try{const r=await g(n);if(!r)return e.status(401).json({error:"Unauthorized: missing, invalid, or revoked credentials (Bearer JWT or X-Api-Key)"});if(!c(r,"agent:execute"))return d(e,"agent:execute");if(!c(r,"agent:stream"))return d(e,"agent:stream");const{input:t,threadId:a,runId:s,checkpointNs:l,queryParams:h,graphId:f,teamId:y,test:m}=n.body;if(!t||typeof t!="string")return e.status(400).json({error:"Bad request: 'input' field is required and must be a string"});e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),e.setHeader("X-Accel-Buffering","no");const v=await p(),u={input:t,graphId:f,teamId:y,threadId:a,runId:s,checkpointNs:l,queryParams:h,userToken:r.token,test:m};e.write(`data: ${JSON.stringify({type:"connected"})}
2
+
3
+ `);for await(const i of v.invoke(u))if(e.write(`data: ${JSON.stringify(i)}
4
+
5
+ `),i.type==="done"||i.type==="error")break;e.end()}catch(r){console.error("[LangGraph API] Stream error:",r),e.write(`data: ${JSON.stringify({type:"error",error:r.message||"Internal server error"})}
6
+
7
+ `),e.end()}}},{path:"/api/langgraph/status/:runId",method:"get",requiresAuth:!1,handler:async(n,e)=>{const r=await g(n);if(!r)return e.status(401).json({error:"Unauthorized: missing, invalid, or revoked credentials (Bearer JWT or X-Api-Key)"});if(!c(r,"agent:read-result"))return d(e,"agent:read-result");try{const{runId:t}=n.params;if(!t)return e.status(400).json({error:"Bad request: runId parameter is required"});const s=await(await p()).getRunStatus(t,r.username);return e.json(s)}catch(t){return console.error("[LangGraph API] Status error:",t),e.status(500).json({error:"Internal server error",message:t.message})}}},{path:"/api/langgraph/cancel/:runId",method:"post",requiresAuth:!1,handler:async(n,e)=>{const r=await g(n);if(!r)return e.status(401).json({error:"Unauthorized: missing, invalid, or revoked credentials (Bearer JWT or X-Api-Key)"});if(!c(r,"agent:cancel"))return d(e,"agent:cancel");try{const{runId:t}=n.params;return t?await(await p()).cancelRun(t,r.username)?e.json({success:!0,runId:t,message:"Run cancelled successfully"}):e.status(404).json({error:"Not found: Run not found or already completed",runId:t}):e.status(400).json({error:"Bad request: runId parameter is required"})}catch(t){return console.error("[LangGraph API] Cancel error:",t),e.status(500).json({error:"Internal server error",message:t.message})}}},{path:"/api/langgraph/health",method:"get",requiresAuth:!1,handler:async(n,e)=>e.json({status:"healthy",service:"langgraph",timestamp:new Date().toISOString()})}];process.on("SIGTERM",async()=>{o&&await o.close()}),process.on("SIGINT",async()=>{o&&await o.close()});export{d as forbiddenScope,c as hasScope,g as resolveCaller,b as routes};
@@ -0,0 +1 @@
1
+ import{z as r}from"zod";import*as n from"../services/live-resources-service";import*as d from"../core/services/share-service";import*as h from"../services/guest-service";import{userToken as m}from"../services/auth";const R=r.object({key:r.string(),value:r.string(),label:r.string().optional()}),v=r.object({chatId:r.string(),runId:r.string(),toolCallId:r.string().optional(),toolName:r.string(),toolInput:r.string(),toolInputVariables:r.array(R).optional().default([]),mcpServerId:r.string().optional()}),y=r.object({blockType:r.enum(["table","echarts","mermaid","markdown"]),blockStructure:r.record(r.any()),promptId:r.string().optional(),renderMode:r.enum(["llm","direct"]).default("llm")}),j=r.object({name:r.string().min(1),tags:r.array(r.string()).default([]),teamId:r.string().optional(),assistantId:r.string().optional(),assistantName:r.string().optional(),source:v,template:y,lastData:r.object({toolOutput:r.string(),renderedBlock:r.record(r.any()),refreshedAt:r.string()}).optional()}),L=r.object({toolName:r.string(),toolOutput:r.string(),blockType:r.string(),blockTitle:r.string()});function w(o){let s=o.cookies?.token;if(!s){const t=o.headers.authorization;t?.startsWith("Bearer ")&&(s=t.substring(7))}const e=o.cookies?.guestToken,a=s||e;if(console.log("[LR getCallerFromRequest] hasToken:",!!s,"hasGuestCookie:",!!e,"cookieHeader:",o.headers.cookie?o.headers.cookie.substring(0,80):"none"),!a)return null;try{const t=m(a);return console.log("[LR getCallerFromRequest] decoded:",t?{isGuest:t.isGuest,ownerId:t.ownerId,guestEmail:t.guestEmail}:null),t?t.isGuest?{username:t.username,isGuest:!0,guestEmail:t.guestEmail,ownerId:t.ownerId}:{username:t.username??"",isGuest:!1}:null}catch{return null}}function i(o){try{let s=o.cookies?.token;if(!s){const e=o.headers.authorization;e?.startsWith("Bearer ")&&(s=e.substring(7))}return s&&m(s)?.username||null}catch{return null}}async function k(o,s){try{const e=i(o);if(!e){s.status(401).json({success:!1,error:"Unauthorized"});return}const a=o.query.tags?o.query.tags.split(",").filter(Boolean):void 0,t=o.query.from?parseInt(o.query.from,10):0,u=o.query.size?parseInt(o.query.size,10):20,{items:c,total:l}=await n.listLiveResources(e,a,t,u);s.json({success:!0,resources:c,total:l})}catch(e){console.error("[LiveResources] listLiveResources error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function I(o,s){try{const e=i(o);if(!e){s.status(401).json({success:!1,error:"Unauthorized"});return}const a=await n.getAllTags(e);s.json({success:!0,tags:a})}catch(e){console.error("[LiveResources] getLiveTags error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function b(o,s){try{const e=await n.getLiveResource(o.params.id);if(!e){s.status(404).json({success:!1,error:"Not found"});return}s.json({success:!0,resource:e})}catch(e){console.error("[LiveResources] getLiveResource error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function S(o,s){try{const e=i(o);if(!e){s.status(401).json({success:!1,error:"Unauthorized"});return}const a=j.parse(o.body),t=await n.createLiveResource({...a,owner:e,refreshConfig:{mode:"ondemand"}});s.status(201).json({success:!0,resource:t})}catch(e){if(e.name==="ZodError"){s.status(400).json({success:!1,error:e.errors});return}console.error("[LiveResources] createLiveResource error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function T(o,s){try{const{toolName:e,toolOutput:a,blockType:t,blockTitle:u}=L.parse(o.body),c=i(o),l=await n.generateResourceTags(e,a,t,u,c??void 0);s.json({success:!0,tags:l})}catch(e){if(e.name==="ZodError"){s.status(400).json({success:!1,error:e.errors});return}console.error("[LiveResources] prepareResource error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function P(o,s){try{if(!await n.updateLiveResource(o.params.id,o.body)){s.status(404).json({success:!1,error:"Not found"});return}s.json({success:!0})}catch(e){console.error("[LiveResources] updateLiveResource error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function G(o,s){try{if(!await n.deleteLiveResource(o.params.id)){s.status(404).json({success:!1,error:"Not found"});return}s.json({success:!0})}catch(e){console.error("[LiveResources] deleteLiveResource error:",e.message),s.status(500).json({success:!1,error:e.message})}}async function z(o,s){s.status(501).json({success:!1,error:"Refresh not yet implemented (Phase 4)"})}async function q(o,s){try{const e=i(o);if(!e){s.status(401).json({success:!1,error:"Unauthorized"});return}const{orderedIds:a,layouts:t}=await n.getLiveResourcesLayout(e);s.json({success:!0,orderedIds:a,layouts:t})}catch(e){s.status(500).json({success:!1,error:e.message})}}async function E(o,s){try{const e=i(o);if(!e){s.status(401).json({success:!1,error:"Unauthorized"});return}const{layouts:a,orderedIds:t}=r.object({layouts:r.record(r.array(r.any())).default({}),orderedIds:r.array(r.string()).default([])}).parse(o.body);await n.setLiveResourcesLayout(e,a,t),s.json({success:!0})}catch(e){if(e.name==="ZodError"){s.status(400).json({success:!1,error:e.errors});return}s.status(500).json({success:!1,error:e.message})}}async function O(o,s){try{const{shareTokenId:e}=o.params,a=w(o);console.log("[LR getSharedResources] shareTokenId:",e,"caller:",a);const t=await d.getShareLinkById(e);if(console.log("[LR getSharedResources] link:",t?{id:t.id,type:t.type,ownerId:t.ownerId,revoked:t.revoked}:null),!t||t.type!=="dashboard"){s.status(404).json({success:!1,error:"Share link not found"});return}if(t.revoked){s.status(403).json({success:!1,error:"Share link has been revoked"});return}if(t.expiresAt&&new Date(t.expiresAt)<new Date){s.status(403).json({success:!1,error:"Share link has expired"});return}const u=!!a&&!a.isGuest&&a.username===t.ownerId;if(console.log("[LR getSharedResources] isOwner:",u,"isGuest:",a?.isGuest,"callerOwnerId:",a?.ownerId,"linkOwnerId:",t.ownerId),!u){if(!a?.isGuest||a.ownerId!==t.ownerId){console.warn("[LR getSharedResources] Auth failed \u2014 caller:",a),s.status(401).json({success:!1,error:"Authentication required"});return}const{valid:p,error:f}=await d.validateShareToken(e,a.guestEmail,!1);if(!p){s.status(403).json({success:!1,error:f});return}h.recordAccess(a.guestEmail,t.ownerId).catch(()=>{}),d.incrementUsesCount(e).catch(()=>{})}const{items:c,total:l}=await n.listLiveResources(t.ownerId,void 0,0,100),{layouts:g}=await n.getLiveResourcesLayout(t.ownerId);s.json({success:!0,resources:c,total:l,layouts:g,ownerId:t.ownerId,dashboardLabel:t.dashboardLabel})}catch(e){console.error("[LiveResources] getSharedResources error:",e.message),s.status(500).json({success:!1,error:e.message})}}const U=[{method:"get",path:"/api/live-resources/shared/:shareTokenId",handler:O}],B=[{method:"get",path:"/api/live-resources",handler:k},{method:"get",path:"/api/live-resources/tags",handler:I},{method:"get",path:"/api/live-resources/order",handler:q},{method:"put",path:"/api/live-resources/order",handler:E},{method:"get",path:"/api/live-resources/:id",handler:b,validate:{params:r.object({id:r.string()})}},{method:"post",path:"/api/live-resources",handler:S},{method:"post",path:"/api/live-resources/prepare",handler:T},{method:"put",path:"/api/live-resources/:id",handler:P,validate:{params:r.object({id:r.string()})}},{method:"delete",path:"/api/live-resources/:id",handler:G,validate:{params:r.object({id:r.string()})}},{method:"post",path:"/api/live-resources/:id/refresh",handler:z,validate:{params:r.object({id:r.string()})}}];export{U as publicRoutes,B as routes};
@@ -0,0 +1 @@
1
+ import a from"zod";import{getLLMUsageStats as f,getLLMCostBreakdown as y,getLLMPerformance as p,getModelComparison as h}from"../core/services/llm-analytics-service";import{userToken as c}from"../services/auth";const m=a.object({start:a.string().optional(),end:a.string().optional()}),S=a.object({provider:a.string(),model:a.string()}),R=a.object({groupBy:a.enum(["provider","model","user","topic"]).optional().default("model"),start:a.string().optional(),end:a.string().optional()});function u(n){const t=n.auth?.username;if(t)return t;let r=n.cookies?.token;if(!r){const o=n.headers.authorization;o&&o.startsWith("Bearer ")&&(r=o.substring(7))}return r?c(r)?.username||null:(console.warn("[llm-analytics] No token found in request"),null)}function d(n){let t=n.cookies?.token;if(!t){const e=n.headers.authorization;e&&e.startsWith("Bearer ")&&(t=e.substring(7))}return t?c(t)?.role==="superuser":!1}const w=async(n,t)=>{const r=u(n);if(!r){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=n.query,o=e.start&&e.end?{start:e.start,end:e.end}:void 0,s=d(n)?void 0:r,i=await f(o,s);t.status(200).json({body:{time:new Date().toISOString(),result:{llms:i,total:i.length}}})}catch(e){console.error("Error getting LLM stats:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve LLM statistics"}}})}},L=async(n,t)=>{const r=u(n);if(!r){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=n.query,o=e.start&&e.end?{start:e.start,end:e.end}:void 0,s=d(n)?void 0:r,i=await y(e.groupBy,o,s);t.status(200).json({body:{time:new Date().toISOString(),result:i}})}catch(e){console.error("Error getting cost breakdown:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve cost breakdown"}}})}},b=async(n,t)=>{const r=u(n);if(!r){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const{provider:e,model:o}=n.params,s=n.query,i=s.start&&s.end?{start:s.start,end:s.end}:void 0,g=d(n)?void 0:r,l=await p(e,o,i,g);if(!l){t.status(404).json({body:{time:new Date().toISOString(),result:{error:"LLM not found or no data available"}}});return}t.status(200).json({body:{time:new Date().toISOString(),result:l}})}catch(e){console.error("Error getting LLM performance:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve LLM performance"}}})}},v=async(n,t)=>{const r=u(n);if(!r){t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized - Invalid or missing token"}}});return}try{const e=n.query,o=e.start&&e.end?{start:e.start,end:e.end}:void 0,s=d(n)?void 0:r,i=await h(o,s);t.status(200).json({body:{time:new Date().toISOString(),result:i}})}catch(e){console.error("Error getting model comparison:",e),t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message||"Failed to retrieve model comparison"}}})}},q=[{method:"get",path:"/api/monitoring/llm/stats",handler:w,validate:{query:m}},{method:"get",path:"/api/monitoring/llm/costs",handler:L,validate:{query:R}},{method:"get",path:"/api/monitoring/llm/:provider/:model/performance",handler:b,validate:{params:S,query:m}},{method:"get",path:"/api/monitoring/llm/comparison",handler:v,validate:{query:m}}];export{q as routes};
@@ -0,0 +1 @@
1
+ import{Client as I}from"@elastic/elasticsearch";import{config as a}from"../core/config";import{createLLMServices as D}from"../core/services/llm-services";import{userToken as O}from"../services/auth";function i(o){const t=o.cookies?.token||o.headers.authorization?.substring(7);return t?O(t)?.username:void 0}import{invalidateMonitoringPricingCache as p}from"../core/services/monitoring-analytics-service";import{CreateProviderSchema as c,UpdateProviderSchema as m,ProviderQuerySchema as g,CreateModelSchema as y,UpdateModelSchema as h,ModelQuerySchema as S,CreateRoutingRuleSchema as w,UpdateRoutingRuleSchema as v,RoutingRuleQuerySchema as b,RoutingContextSchema as R,CallLLMSchema as L}from"../core/llm/models";import{createLLMInstance as j}from"../core/llm/providers";import{loadLLMConfigForNode as M}from"../core/graph/graph-builder";const T={provider:"anthropic",model:"claude-sonnet-4-5",temperature:0,maxTokens:8e3,streaming:!1,apiKey:a.llm.apiKey||"",baseURL:a.llm.baseURL},u=new I({node:process.env.ELASTICSEARCH_HOST||a.elasticsearch.node,auth:{username:process.env.ELASTICSEARCH_USER||a.elasticsearch.auth.username,password:process.env.ELASTICSEARCH_PASSWORD||a.elasticsearch.auth.password},tls:{rejectUnauthorized:!1}}),n=D(u),F=[{method:"get",path:"/api/llm/providers",handler:async(o,t)=>{try{const e=g.parse(o.query),s=await n.getProvidersService().listProviders(e,i(o));t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{query:g.partial()},openapi:{summary:"Lists all LLM providers with optional filters and pagination.",description:"This endpoint allows clients to retrieve a list of LLM providers registered in the system. It supports optional filtering by provider type, status, and other attributes, as well as pagination parameters to control the number of results returned.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/providers/:id",handler:async(o,t)=>{try{const e=await n.getProvidersService().getProvider(o.params.id);if(!e)return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Provider not found"}}});t.json({body:{time:new Date().toISOString(),result:e}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Retrieves a specific LLM provider by ID.",description:"This endpoint allows clients to fetch detailed information about a single LLM provider identified by its unique ID.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/providers",handler:async(o,t)=>{try{const e=c.parse(o.body),s=await n.getProvidersService().createProvider(e);t.status(201).json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(400).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:c},openapi:{summary:"Creates a new LLM provider.",description:"This endpoint allows clients to register a new LLM provider in the system. The request body must contain all required fields for the provider.",tags:["llm-control-plane"]}},{method:"put",path:"/api/llm/providers/:id",handler:async(o,t)=>{try{const e=m.parse(o.body),s=await n.getProvidersService().updateProvider(o.params.id,e);t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){const s=e.message.includes("not found")?404:400;t.status(s).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:m},openapi:{summary:"Updates an existing LLM provider.",description:"This endpoint allows clients to update the details of an existing LLM provider identified by its unique ID. The request body must contain the updated information.",tags:["llm-control-plane"]}},{method:"delete",path:"/api/llm/providers/:id",handler:async(o,t)=>{try{if(!await n.getProvidersService().deleteProvider(o.params.id))return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Provider not found"}}});t.json({body:{time:new Date().toISOString(),result:{success:!0}}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Deletes an existing LLM provider.",description:"This endpoint allows clients to remove an existing LLM provider from the system identified by its unique ID.",tags:["llm-control-plane"]}},{method:"patch",path:"/api/llm/providers/:id/status",handler:async(o,t)=>{try{const{enabled:e}=o.body,s=await n.getProvidersService().toggleProvider(o.params.id,e);t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(400).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Retrieves statistics for all LLM providers.",description:"This endpoint allows clients to fetch aggregated statistics about the LLM providers in the system.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/providers/stats",handler:async(o,t)=>{try{const e=await n.getProvidersService().getStatistics();t.json({body:{time:new Date().toISOString(),result:e}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Retrieves statistics for all LLM providers.",description:"This endpoint allows clients to fetch aggregated statistics about the LLM providers in the system.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/models/sync-preview",handler:async(o,t)=>{try{const e=i(o);if(!e)return t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});const{previewSync:s}=await import("../core/services/llm-sync-service"),r=await s(u,e);t.json({body:{time:new Date().toISOString(),result:r}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Previews the changes that would be applied to LLM models during a sync operation.",description:"This endpoint allows clients to preview the changes that would be made to LLM models during a synchronization operation. It provides a list of models that would be added, updated, or deprecated based on the current state of the system.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/models/sync-apply",handler:async(o,t)=>{try{const e=i(o);if(!e)return t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});const{toAdd:s=[],toUpdate:r=[]}=o.body,{applySync:l}=await import("../core/services/llm-sync-service"),d=await l(u,e,s,r);p(),t.json({body:{time:new Date().toISOString(),result:d}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Applies the confirmed changes to LLM models during a sync operation.",description:"This endpoint allows clients to apply the changes that were previewed during a synchronization operation. It updates the LLM models in the system based on the provided list of additions and updates.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/models",handler:async(o,t)=>{try{const e=S.parse(o.query),s=await n.getModelsService().listModels(e,i(o));t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{query:S.partial()},openapi:{summary:"Lists all LLM models with optional filters and pagination.",description:"This endpoint allows clients to list all LLM models with optional filters and pagination.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/models/:id",handler:async(o,t)=>{try{const e=await n.getModelsService().getModel(o.params.id);if(!e)return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Model not found"}}});t.json({body:{time:new Date().toISOString(),result:{model:e}}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Retrieves a specific LLM model by its ID.",description:"This endpoint allows clients to retrieve a specific LLM model by its ID.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/models",handler:async(o,t)=>{try{const e=y.parse(o.body),s=await n.getModelsService().createModel(e);t.status(201).json({body:{time:new Date().toISOString(),result:{model:s}}})}catch(e){t.status(400).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:y},openapi:{summary:"Creates a new LLM model.",description:"This endpoint allows clients to create a new LLM model with the provided details.",tags:["llm-control-plane"]}},{method:"put",path:"/api/llm/models/:id",handler:async(o,t)=>{try{const e=h.parse(o.body),s=await n.getModelsService().updateModel(o.params.id,e);p(),t.json({body:{time:new Date().toISOString(),result:{model:s}}})}catch(e){const s=e.message.includes("not found")?404:400;t.status(s).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:h},openapi:{summary:"Updates an existing LLM model.",description:"This endpoint allows clients to update an existing LLM model with the provided details.",tags:["llm-control-plane"]}},{method:"delete",path:"/api/llm/models/:id",handler:async(o,t)=>{try{if(!await n.getModelsService().deleteModel(o.params.id))return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Model not found"}}});t.json({body:{time:new Date().toISOString(),result:{success:!0}}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Deletes an existing LLM model.",description:"This endpoint allows clients to delete an existing LLM model identified by its unique ID.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/routing-rules",handler:async(o,t)=>{try{const e=b.parse(o.query),s=await n.getRoutingService().listRoutingRules(e,i(o));t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{query:b.partial()},openapi:{summary:"Lists all routing rules with optional filters and pagination.",description:"This endpoint allows clients to list all routing rules with optional filters and pagination.",tags:["llm-control-plane"]}},{method:"get",path:"/api/llm/routing-rules/:id",handler:async(o,t)=>{try{const e=await n.getRoutingService().getRoutingRule(o.params.id,i(o));if(!e)return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Routing rule not found"}}});t.json({body:{time:new Date().toISOString(),result:e}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Retrieves a specific routing rule by its ID.",description:"This endpoint allows clients to retrieve a specific routing rule by its ID.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/routing-rules",handler:async(o,t)=>{try{const e=w.parse(o.body),s=await n.getRoutingService().createRoutingRule(e);t.status(201).json({body:{time:new Date().toISOString(),result:s}})}catch(e){t.status(400).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:w},openapi:{summary:"Creates a new routing rule.",description:"This endpoint allows clients to create a new routing rule with the provided details.",tags:["llm-control-plane"]}},{method:"put",path:"/api/llm/routing-rules/:id",handler:async(o,t)=>{try{const e=v.parse(o.body),s=await n.getRoutingService().updateRoutingRule(o.params.id,e,i(o));t.json({body:{time:new Date().toISOString(),result:s}})}catch(e){const s=e.message.includes("not found")?404:400;t.status(s).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:v},openapi:{summary:"Updates an existing routing rule.",description:"This endpoint allows clients to update an existing routing rule with the provided details.",tags:["llm-control-plane"]}},{method:"delete",path:"/api/llm/routing-rules/:id",handler:async(o,t)=>{try{if(!await n.getRoutingService().deleteRoutingRule(o.params.id,i(o)))return t.status(404).json({body:{time:new Date().toISOString(),result:{error:"Routing rule not found"}}});t.json({body:{time:new Date().toISOString(),result:{success:!0}}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Deletes an existing routing rule.",description:"This endpoint allows clients to delete an existing routing rule identified by its unique ID.",tags:["llm-control-plane"]}},{method:"patch",path:"/api/llm/routing-rules/:id/set-as-default",handler:async(o,t)=>{try{const e=i(o);if(!e)return t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});await n.getRoutingService().setDefaultRoutingRule(o.params.id,e),t.json({body:{time:new Date().toISOString(),result:{success:!0}}})}catch(e){const s=e.message.includes("not found")?404:e.message.includes("Unauthorized")?403:500;t.status(s).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},openapi:{summary:"Sets a routing rule as the default.",description:"This endpoint allows clients to set a specific routing rule as the default for the authenticated user.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/call",handler:async(o,t)=>{try{const{id:e,prompt:s}=L.parse(o.body),r=i(o);if(!(r?await n.getRoutingService().getRoutingRuleByBusinessId(e,r):null))return t.status(404).json({body:{time:new Date().toISOString(),result:{error:`Unknown LLM: "${e}"`}}});const d=await M("static_code_executor_llm_call",e,T,r),f=await j({...d,streaming:!1}).invoke(s);t.json({body:{time:new Date().toISOString(),result:{content:String(f.content)}}})}catch(e){t.status(500).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:L},openapi:{summary:"Invoke a configured LLM routing rule with a text prompt.",description:"Resolves an LLM routing rule by its business id (owner-scoped) and returns the model's text response to the given prompt. Text in, text out \u2014 no structured output.",tags:["llm-control-plane"]}},{method:"post",path:"/api/llm/routing/select",handler:async(o,t)=>{try{const e=i(o);if(!e)return t.status(401).json({body:{time:new Date().toISOString(),result:{error:"Unauthorized"}}});const s={...R.parse(o.body),owner:e},r=await n.getRoutingService().selectDeployment(s),l={...r,provider:r.provider?{...r.provider,apiKey:r.provider.apiKey?"***redacted***":void 0}:r.provider};t.json({body:{time:new Date().toISOString(),result:l}})}catch(e){t.status(400).json({body:{time:new Date().toISOString(),result:{error:e.message}}})}},validate:{body:R},openapi:{summary:"Selects the appropriate LLM deployment based on the provided routing context.",description:"This endpoint allows clients to test and debug the routing logic by providing a routing context. It returns the selected LLM deployment based on the given context.",tags:["llm-control-plane"]}}];export{F as routes};
@@ -0,0 +1 @@
1
+ import{z as o}from"zod";import{getAvailableModels as i,getDefaultModel as p,DEFAULT_LLM_CONFIG as l}from"../core/llm/providers";import{config as n}from"../core/config";import{fetchAllLLMPricing as d}from"../core/services/llm-pricing-service";const c=o.object({provider:o.enum(["openai","anthropic","groq","local"]),model:o.string(),temperature:o.number().min(0).max(2).optional(),streaming:o.boolean().optional(),apiKey:o.string().optional(),baseURL:o.string().url().optional()}),R=[{method:"get",path:"/api/llm-models",handler:async(r,t)=>{try{const e=await d();t.json({body:{result:e,total:e.length,time:new Date().toISOString()}})}catch(e){console.error("Error fetching LLM models:",e),t.status(500).json({error:"Failed to fetch LLM models",message:e.message})}}},{method:"get",path:"/api/llm/models/:provider",handler:async(r,t)=>{const e=r.params.provider;if(!l[e])return t.status(404).json({error:"Provider non trouv\xE9",availableProviders:Object.keys(l)});const s=i(e),a=p(e);t.json({provider:e,models:s,defaultModel:a})},validate:{params:o.object({provider:o.enum(["openai","anthropic","groq","local"])})}},{method:"post",path:"/api/llm/config",handler:async(r,t)=>{const e=r.body,s=i(e.provider);if(!s.includes(e.model))return t.status(400).json({error:`Mod\xE8le '${e.model}' non disponible pour le provider '${e.provider}'`,availableModels:s});Object.assign(n.llm,e),t.json({message:"Configuration LLM mise \xE0 jour",config:n.llm,note:"Red\xE9marrage requis pour une persistance compl\xE8te"})},validate:{body:c}},{method:"post",path:"/api/llm/test",handler:async(r,t)=>{try{const{provider:e,model:s,message:a="Dis bonjour en fran\xE7ais"}=r.body,m={provider:e||n.llm.provider,model:s||n.llm.model,temperature:0,streaming:!1};t.json({message:"Test de connexion simul\xE9",config:m,status:"success",note:"Impl\xE9mentation compl\xE8te du test \xE0 faire avec le LLM r\xE9el"})}catch(e){t.status(500).json({error:"Erreur lors du test",message:e.message})}},validate:{body:o.object({provider:o.enum(["openai","anthropic","groq","local"]).optional(),model:o.string().optional(),message:o.string().optional()})}}];export{R as routes};
@@ -0,0 +1 @@
1
+ import{Client as l}from"@elastic/elasticsearch";import{assertOwner as p,getAuthUsername as c}from"../core/utils/ownership";import{listGatewayGrantsForTeam as m,revokeGatewayGrant as d,listAllowlistedTools as g,callAllowlistedTool as f}from"../services/mcp-gateway/gateway-grant-service";const y=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",w=process.env.ELASTICSEARCH_USER||"elastic",h=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",R=".stkxp_teams",I=new l({node:y,auth:{username:w,password:h},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3});function u(e){const t=e.headers.authorization||"";return t.startsWith("ApiKey ")?t.slice(7).trim():""}async function S(e,t){const s=c(e);if(!s){t.status(401).json({success:!1,error:"Unauthorized"});return}const{teamId:n}=e.params,a=await p(e,t,I,R,n);if(!a)return;const o=a.source.owner||s,i=await m(n,o);t.json({success:!0,grants:i})}async function T(e,t){const s=c(e);if(!s){t.status(401).json({success:!1,error:"Unauthorized"});return}const{grantId:n}=e.params;if(!await d(n,s)){t.status(404).json({success:!1,error:"Gateway grant not found or not owned by you"});return}t.json({success:!0})}async function k(e,t){const{grantId:s,platformId:n}=e.params,a=u(e);if(!a){t.status(401).json({success:!1,error:"Missing gateway token"});return}const o=await g(s,n,a);if("error"in o){t.status(403).json({success:!1,error:o.error});return}t.json({success:!0,tools:o.tools})}async function j(e,t){const{grantId:s,platformId:n}=e.params,a=u(e);if(!a){t.status(401).json({success:!1,error:"Missing gateway token"});return}const{name:o,arguments:i}=e.body||{};if(!o){t.status(400).json({success:!1,error:"Missing tool name"});return}const r=await f(s,n,a,o,i||{});if(r&&typeof r=="object"&&"error"in r){t.status(403).json({success:!1,error:r.error});return}t.json({success:!0,result:r})}const v=[{method:"get",path:"/api/teams/:teamId/gateway-grants",handler:S,openapi:{summary:"List MCP gateway grants issued from exports of this team",tags:["teams"],audience:"internal"}},{method:"post",path:"/api/mcp-gateway-grants/:grantId/revoke",handler:T,openapi:{summary:"Revoke an MCP gateway grant",tags:["teams"],audience:"internal"}}],x=[{method:"get",path:"/api/mcp-gateway/internal/:grantId/:platformId/tools",handler:k,openapi:{summary:"Internal: list allowlisted tools for a gateway grant (called by stkxp-mcp-server)",tags:["teams"],audience:"hidden"}},{method:"post",path:"/api/mcp-gateway/internal/:grantId/:platformId/call-tool",handler:j,openapi:{summary:"Internal: relay a tool call through a gateway grant (called by stkxp-mcp-server)",tags:["teams"],audience:"hidden"}}];export{x as publicRoutes,v as routes};