@hotmeshio/long-tail 0.1.13 → 0.1.14

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 (260) hide show
  1. package/build/api/files.d.ts +13 -0
  2. package/build/api/files.js +44 -0
  3. package/build/api/mcp-runs.d.ts +2 -0
  4. package/build/api/mcp-runs.js +2 -0
  5. package/build/api/workflow-sets.d.ts +11 -0
  6. package/build/api/workflow-sets.js +62 -0
  7. package/build/api/yaml-workflows.d.ts +9 -0
  8. package/build/api/yaml-workflows.js +40 -11
  9. package/build/lib/storage/local.d.ts +15 -0
  10. package/build/lib/storage/local.js +63 -0
  11. package/build/lib/storage/mime.d.ts +4 -0
  12. package/build/lib/storage/mime.js +31 -0
  13. package/build/lib/storage/s3.d.ts +16 -0
  14. package/build/lib/storage/s3.js +70 -0
  15. package/build/lib/storage/types.d.ts +18 -0
  16. package/build/modules/utils.d.ts +73 -0
  17. package/build/modules/utils.js +84 -0
  18. package/build/routes/file-browser.d.ts +2 -0
  19. package/build/routes/file-browser.js +112 -0
  20. package/build/routes/files.js +25 -17
  21. package/build/routes/index.js +2 -0
  22. package/build/routes/mcp-runs.js +2 -0
  23. package/build/routes/workflow-sets.js +9 -0
  24. package/build/routes/yaml-workflows/deployment.js +8 -0
  25. package/build/services/insight/index.d.ts +13 -0
  26. package/build/services/insight/index.js +4 -7
  27. package/build/services/insight/prompts.d.ts +1 -1
  28. package/build/services/insight/prompts.js +1 -1
  29. package/build/services/mcp-runs/queries.d.ts +2 -0
  30. package/build/services/mcp-runs/queries.js +11 -1
  31. package/build/services/mcp-runs/sql.d.ts +1 -1
  32. package/build/services/mcp-runs/sql.js +2 -2
  33. package/build/services/workflow-sets/db.d.ts +1 -0
  34. package/build/services/workflow-sets/db.js +6 -0
  35. package/build/services/workflow-sets/index.d.ts +1 -1
  36. package/build/services/workflow-sets/index.js +2 -1
  37. package/build/services/workflow-sets/sql.d.ts +1 -0
  38. package/build/services/workflow-sets/sql.js +6 -1
  39. package/build/services/yaml-workflow/db.d.ts +12 -0
  40. package/build/services/yaml-workflow/db.js +18 -0
  41. package/build/services/yaml-workflow/generator.js +4 -4
  42. package/build/services/yaml-workflow/pipeline/build/index.d.ts +2 -1
  43. package/build/services/yaml-workflow/pipeline/build/index.js +3 -2
  44. package/build/services/yaml-workflow/pipeline/build/utils.d.ts +0 -2
  45. package/build/services/yaml-workflow/pipeline/build/utils.js +0 -8
  46. package/build/services/yaml-workflow/sql.d.ts +2 -1
  47. package/build/services/yaml-workflow/sql.js +10 -3
  48. package/build/system/activities/schema-exchange.d.ts +50 -0
  49. package/build/system/activities/schema-exchange.js +210 -0
  50. package/build/system/activities/sql.d.ts +1 -1
  51. package/build/system/activities/sql.js +12 -1
  52. package/build/system/index.js +1 -0
  53. package/build/system/mcp-servers/knowledge.js +35 -6
  54. package/build/system/mcp-servers/schema-exchange.d.ts +4 -0
  55. package/build/system/mcp-servers/schema-exchange.js +93 -0
  56. package/build/system/seed/server-definitions.d.ts +112 -1
  57. package/build/system/seed/server-definitions.js +37 -0
  58. package/build/system/seed/tool-manifests-data.d.ts +87 -0
  59. package/build/system/seed/tool-manifests-data.js +37 -1
  60. package/build/system/seed/tool-manifests-knowledge.d.ts +9 -1
  61. package/build/system/seed/tool-manifests-knowledge.js +6 -5
  62. package/build/system/workflows/mcp-workflow-builder/prompts.d.ts +1 -1
  63. package/build/system/workflows/mcp-workflow-builder/prompts.js +21 -20
  64. package/build/system/workflows/mcp-workflow-planner/activities/persist.d.ts +1 -1
  65. package/build/system/workflows/mcp-workflow-planner/activities/persist.js +3 -3
  66. package/build/system/workflows/mcp-workflow-planner/index.js +39 -5
  67. package/build/system/workflows/mcp-workflow-planner/prompts.d.ts +1 -1
  68. package/build/system/workflows/mcp-workflow-planner/prompts.js +3 -3
  69. package/build/tsconfig.tsbuildinfo +1 -1
  70. package/dashboard/dist/assets/{AdminDashboard-DUrSBQOl.js → AdminDashboard-Cngijp4Q.js} +2 -2
  71. package/dashboard/dist/assets/{AdminDashboard-DUrSBQOl.js.map → AdminDashboard-Cngijp4Q.js.map} +1 -1
  72. package/dashboard/dist/assets/{AvailableEscalationsPage-Dbd1qUK_.js → AvailableEscalationsPage-CpBfGV1h.js} +2 -2
  73. package/dashboard/dist/assets/{AvailableEscalationsPage-Dbd1qUK_.js.map → AvailableEscalationsPage-CpBfGV1h.js.map} +1 -1
  74. package/dashboard/dist/assets/{BotPicker-Cg5iNEkm.js → BotPicker-B8Uvw9Si.js} +2 -2
  75. package/dashboard/dist/assets/{BotPicker-Cg5iNEkm.js.map → BotPicker-B8Uvw9Si.js.map} +1 -1
  76. package/dashboard/dist/assets/{CollapsibleSection-Kd9UIyeU.js → CollapsibleSection-DiFPuWOY.js} +2 -2
  77. package/dashboard/dist/assets/{CollapsibleSection-Kd9UIyeU.js.map → CollapsibleSection-DiFPuWOY.js.map} +1 -1
  78. package/dashboard/dist/assets/{ConfirmDeleteModal-DZMgmlof.js → ConfirmDeleteModal-CpXG9uyu.js} +2 -2
  79. package/dashboard/dist/assets/{ConfirmDeleteModal-DZMgmlof.js.map → ConfirmDeleteModal-CpXG9uyu.js.map} +1 -1
  80. package/dashboard/dist/assets/{CopyableId-cPFTRm8q.js → CopyableId-DKsTR9lK.js} +2 -2
  81. package/dashboard/dist/assets/{CopyableId-cPFTRm8q.js.map → CopyableId-DKsTR9lK.js.map} +1 -1
  82. package/dashboard/dist/assets/CredentialsPage-DlS7Kf40.js +2 -0
  83. package/dashboard/dist/assets/{CredentialsPage-DJablIbs.js.map → CredentialsPage-DlS7Kf40.js.map} +1 -1
  84. package/dashboard/dist/assets/{CustomDurationPicker-NgIP6YDW.js → CustomDurationPicker-B-9eW3pm.js} +2 -2
  85. package/dashboard/dist/assets/{CustomDurationPicker-NgIP6YDW.js.map → CustomDurationPicker-B-9eW3pm.js.map} +1 -1
  86. package/dashboard/dist/assets/{DataTable-CTRhTAfT.js → DataTable-DkOokbtL.js} +2 -2
  87. package/dashboard/dist/assets/{DataTable-CTRhTAfT.js.map → DataTable-DkOokbtL.js.map} +1 -1
  88. package/dashboard/dist/assets/{ElapsedCell-HcSJ_MMs.js → ElapsedCell-DVtHqM-5.js} +2 -2
  89. package/dashboard/dist/assets/{ElapsedCell-HcSJ_MMs.js.map → ElapsedCell-DVtHqM-5.js.map} +1 -1
  90. package/dashboard/dist/assets/{EmptyState-joNbd4gg.js → EmptyState-C7KIMIbE.js} +2 -2
  91. package/dashboard/dist/assets/{EmptyState-joNbd4gg.js.map → EmptyState-C7KIMIbE.js.map} +1 -1
  92. package/dashboard/dist/assets/{EscalationsOverview-DpXDnQux.js → EscalationsOverview-BMKBlkPx.js} +2 -2
  93. package/dashboard/dist/assets/{EscalationsOverview-DpXDnQux.js.map → EscalationsOverview-BMKBlkPx.js.map} +1 -1
  94. package/dashboard/dist/assets/{EventTable-CYem3v8n.js → EventTable-BYZ5OVdQ.js} +2 -2
  95. package/dashboard/dist/assets/{EventTable-CYem3v8n.js.map → EventTable-BYZ5OVdQ.js.map} +1 -1
  96. package/dashboard/dist/assets/{FilterBar-BiO8SOzc.js → FilterBar-C5r3n6YO.js} +2 -2
  97. package/dashboard/dist/assets/{FilterBar-BiO8SOzc.js.map → FilterBar-C5r3n6YO.js.map} +1 -1
  98. package/dashboard/dist/assets/{ListToolbar-6yRDh2e9.js → ListToolbar-BGUajIsW.js} +2 -2
  99. package/dashboard/dist/assets/{ListToolbar-6yRDh2e9.js.map → ListToolbar-BGUajIsW.js.map} +1 -1
  100. package/dashboard/dist/assets/{McpOverview-CUgSxkQe.js → McpOverview-B_kJYHea.js} +2 -2
  101. package/dashboard/dist/assets/{McpOverview-CUgSxkQe.js.map → McpOverview-B_kJYHea.js.map} +1 -1
  102. package/dashboard/dist/assets/McpQueryDetailPage-Czsmovqw.js +5 -0
  103. package/dashboard/dist/assets/McpQueryDetailPage-Czsmovqw.js.map +1 -0
  104. package/dashboard/dist/assets/McpQueryPage-BgAq_bQg.js +2 -0
  105. package/dashboard/dist/assets/{McpQueryPage-lV6kfDG5.js.map → McpQueryPage-BgAq_bQg.js.map} +1 -1
  106. package/dashboard/dist/assets/McpRunDetailPage-B8c0OszR.js +2 -0
  107. package/dashboard/dist/assets/McpRunDetailPage-B8c0OszR.js.map +1 -0
  108. package/dashboard/dist/assets/McpRunsPage-BY8C6k78.js +2 -0
  109. package/dashboard/dist/assets/McpRunsPage-BY8C6k78.js.map +1 -0
  110. package/dashboard/dist/assets/{Modal-BuTvD0pz.js → Modal-E1yRnCeW.js} +2 -2
  111. package/dashboard/dist/assets/{Modal-BuTvD0pz.js.map → Modal-E1yRnCeW.js.map} +1 -1
  112. package/dashboard/dist/assets/OperatorDashboard-C8MSTzey.js +2 -0
  113. package/dashboard/dist/assets/{OperatorDashboard-C9SSV96T.js.map → OperatorDashboard-C8MSTzey.js.map} +1 -1
  114. package/dashboard/dist/assets/{PageHeader-BcTVF9ul.js → PageHeader-Cm5HBQF_.js} +2 -2
  115. package/dashboard/dist/assets/{PageHeader-BcTVF9ul.js.map → PageHeader-Cm5HBQF_.js.map} +1 -1
  116. package/dashboard/dist/assets/{PageHeaderWithStats-BI7JG5x6.js → PageHeaderWithStats-CNmWJFSN.js} +2 -2
  117. package/dashboard/dist/assets/{PageHeaderWithStats-BI7JG5x6.js.map → PageHeaderWithStats-CNmWJFSN.js.map} +1 -1
  118. package/dashboard/dist/assets/{PriorityBadge-DqVaOU65.js → PriorityBadge-HSI4RVhs.js} +2 -2
  119. package/dashboard/dist/assets/{PriorityBadge-DqVaOU65.js.map → PriorityBadge-HSI4RVhs.js.map} +1 -1
  120. package/dashboard/dist/assets/{ProcessDetailPage-hFMhf9qa.js → ProcessDetailPage-Dln8622H.js} +2 -2
  121. package/dashboard/dist/assets/{ProcessDetailPage-hFMhf9qa.js.map → ProcessDetailPage-Dln8622H.js.map} +1 -1
  122. package/dashboard/dist/assets/{ProcessesListPage-ByVoBCQ3.js → ProcessesListPage-bIsd9N_h.js} +2 -2
  123. package/dashboard/dist/assets/{ProcessesListPage-ByVoBCQ3.js.map → ProcessesListPage-bIsd9N_h.js.map} +1 -1
  124. package/dashboard/dist/assets/{RolePill-D9ZIkYiu.js → RolePill-BVUp2bF0.js} +2 -2
  125. package/dashboard/dist/assets/{RolePill-D9ZIkYiu.js.map → RolePill-BVUp2bF0.js.map} +1 -1
  126. package/dashboard/dist/assets/RolesPage-kH-Njt25.js +2 -0
  127. package/dashboard/dist/assets/{RolesPage-SMedMuqa.js.map → RolesPage-kH-Njt25.js.map} +1 -1
  128. package/dashboard/dist/assets/{RowActions-yDhwwDbU.js → RowActions-DbUJPfaW.js} +2 -2
  129. package/dashboard/dist/assets/{RowActions-yDhwwDbU.js.map → RowActions-DbUJPfaW.js.map} +1 -1
  130. package/dashboard/dist/assets/{StatCard-BrBnQFxh.js → StatCard-Bs3JbyAz.js} +2 -2
  131. package/dashboard/dist/assets/{StatCard-BrBnQFxh.js.map → StatCard-Bs3JbyAz.js.map} +1 -1
  132. package/dashboard/dist/assets/{StatusBadge-xgb-lZku.js → StatusBadge-CakDdsCw.js} +2 -2
  133. package/dashboard/dist/assets/{StatusBadge-xgb-lZku.js.map → StatusBadge-CakDdsCw.js.map} +1 -1
  134. package/dashboard/dist/assets/{StepIndicator-B9ps2SvM.js → StepIndicator-Cd_SG_yA.js} +2 -2
  135. package/dashboard/dist/assets/{StepIndicator-B9ps2SvM.js.map → StepIndicator-Cd_SG_yA.js.map} +1 -1
  136. package/dashboard/dist/assets/{StickyPagination-DTIjBKN3.js → StickyPagination-Bz0C18nC.js} +2 -2
  137. package/dashboard/dist/assets/{StickyPagination-DTIjBKN3.js.map → StickyPagination-Bz0C18nC.js.map} +1 -1
  138. package/dashboard/dist/assets/{SwimlaneTimeline-RK4Yu66z.js → SwimlaneTimeline-Cfe-xQRX.js} +2 -2
  139. package/dashboard/dist/assets/{SwimlaneTimeline-RK4Yu66z.js.map → SwimlaneTimeline-Cfe-xQRX.js.map} +1 -1
  140. package/dashboard/dist/assets/{TagInput-CdNUuqk4.js → TagInput-ClFhXG-U.js} +2 -2
  141. package/dashboard/dist/assets/{TagInput-CdNUuqk4.js.map → TagInput-ClFhXG-U.js.map} +1 -1
  142. package/dashboard/dist/assets/{TaskDetailPage-C-nzaNea.js → TaskDetailPage-SuMBdARt.js} +2 -2
  143. package/dashboard/dist/assets/{TaskDetailPage-C-nzaNea.js.map → TaskDetailPage-SuMBdARt.js.map} +1 -1
  144. package/dashboard/dist/assets/{TaskQueuePill-Lvr2-NzS.js → TaskQueuePill-lJR1JW_W.js} +2 -2
  145. package/dashboard/dist/assets/{TaskQueuePill-Lvr2-NzS.js.map → TaskQueuePill-lJR1JW_W.js.map} +1 -1
  146. package/dashboard/dist/assets/{TasksListPage-DSUmD84y.js → TasksListPage-Dkq1Vtbt.js} +2 -2
  147. package/dashboard/dist/assets/{TasksListPage-DSUmD84y.js.map → TasksListPage-Dkq1Vtbt.js.map} +1 -1
  148. package/dashboard/dist/assets/{TimeAgo-BZdLdrIh.js → TimeAgo-DgfDZ1pl.js} +2 -2
  149. package/dashboard/dist/assets/{TimeAgo-BZdLdrIh.js.map → TimeAgo-DgfDZ1pl.js.map} +1 -1
  150. package/dashboard/dist/assets/{TimestampCell-QX_0i5FK.js → TimestampCell-MpHZ1hMD.js} +2 -2
  151. package/dashboard/dist/assets/{TimestampCell-QX_0i5FK.js.map → TimestampCell-MpHZ1hMD.js.map} +1 -1
  152. package/dashboard/dist/assets/{UserName-DyZMXcBm.js → UserName-DqsosA4B.js} +2 -2
  153. package/dashboard/dist/assets/{UserName-DyZMXcBm.js.map → UserName-DqsosA4B.js.map} +1 -1
  154. package/dashboard/dist/assets/{WorkflowExecutionPage-DjVxfZaF.js → WorkflowExecutionPage-CVlg38C3.js} +2 -2
  155. package/dashboard/dist/assets/{WorkflowExecutionPage-DjVxfZaF.js.map → WorkflowExecutionPage-CVlg38C3.js.map} +1 -1
  156. package/dashboard/dist/assets/WorkflowPill-CRpZhjGR.js +2 -0
  157. package/dashboard/dist/assets/WorkflowPill-CRpZhjGR.js.map +1 -0
  158. package/dashboard/dist/assets/{WorkflowsDashboard-DZjuiFZ0.js → WorkflowsDashboard-Ugzbs8mV.js} +2 -2
  159. package/dashboard/dist/assets/{WorkflowsDashboard-DZjuiFZ0.js.map → WorkflowsDashboard-Ugzbs8mV.js.map} +1 -1
  160. package/dashboard/dist/assets/{WorkflowsOverview-CLnLRpOu.js → WorkflowsOverview-CIp_lTNl.js} +2 -2
  161. package/dashboard/dist/assets/{WorkflowsOverview-CLnLRpOu.js.map → WorkflowsOverview-CIp_lTNl.js.map} +1 -1
  162. package/dashboard/dist/assets/YamlWorkflowsPage-BICF0fRO.js +2 -0
  163. package/dashboard/dist/assets/YamlWorkflowsPage-BICF0fRO.js.map +1 -0
  164. package/dashboard/dist/assets/{bots-DIM6lBoY.js → bots-DPfUpVqI.js} +2 -2
  165. package/dashboard/dist/assets/{bots-DIM6lBoY.js.map → bots-DPfUpVqI.js.map} +1 -1
  166. package/dashboard/dist/assets/{escalation-JOTuOqjq.js → escalation-RrCDbMC3.js} +2 -2
  167. package/dashboard/dist/assets/{escalation-JOTuOqjq.js.map → escalation-RrCDbMC3.js.map} +1 -1
  168. package/dashboard/dist/assets/escalation-columns-CDGa9wsD.js +2 -0
  169. package/dashboard/dist/assets/{escalation-columns-Cyg58nkg.js.map → escalation-columns-CDGa9wsD.js.map} +1 -1
  170. package/dashboard/dist/assets/{helpers-B1BDxBZd.js → helpers-ZSKqdkdS.js} +2 -2
  171. package/dashboard/dist/assets/{helpers-B1BDxBZd.js.map → helpers-ZSKqdkdS.js.map} +1 -1
  172. package/dashboard/dist/assets/helpers-rMEcLwKs.js +2 -0
  173. package/dashboard/dist/assets/helpers-rMEcLwKs.js.map +1 -0
  174. package/dashboard/dist/assets/{index-DDYFpi4l.js → index-ABcJHHlN.js} +2 -2
  175. package/dashboard/dist/assets/{index-DDYFpi4l.js.map → index-ABcJHHlN.js.map} +1 -1
  176. package/dashboard/dist/assets/{index-D1wVX50Z.js → index-B91h_jZ0.js} +2 -2
  177. package/dashboard/dist/assets/{index-D1wVX50Z.js.map → index-B91h_jZ0.js.map} +1 -1
  178. package/dashboard/dist/assets/{index-BcR6PfpY.js → index-BbI2dzhJ.js} +2 -2
  179. package/dashboard/dist/assets/{index-BcR6PfpY.js.map → index-BbI2dzhJ.js.map} +1 -1
  180. package/dashboard/dist/assets/{index-BizfauqT.js → index-BmVCyB6C.js} +4 -4
  181. package/dashboard/dist/assets/{index-BizfauqT.js.map → index-BmVCyB6C.js.map} +1 -1
  182. package/dashboard/dist/assets/index-C1E5GTs9.js +2 -0
  183. package/dashboard/dist/assets/index-C1E5GTs9.js.map +1 -0
  184. package/dashboard/dist/assets/{index-Cf60K3x9.js → index-C90ZPzXk.js} +2 -2
  185. package/dashboard/dist/assets/{index-Cf60K3x9.js.map → index-C90ZPzXk.js.map} +1 -1
  186. package/dashboard/dist/assets/{index-BYZX9tOb.js → index-Cmgrk7PQ.js} +58 -58
  187. package/dashboard/dist/assets/index-Cmgrk7PQ.js.map +1 -0
  188. package/dashboard/dist/assets/{index-Ds0JoXS2.js → index-DNytWfSZ.js} +2 -2
  189. package/dashboard/dist/assets/{index-Ds0JoXS2.js.map → index-DNytWfSZ.js.map} +1 -1
  190. package/dashboard/dist/assets/index-DX6zxr6t.js +2 -0
  191. package/dashboard/dist/assets/{index-Cg5nfiYX.js.map → index-DX6zxr6t.js.map} +1 -1
  192. package/dashboard/dist/assets/index-DeX-ezqf.css +1 -0
  193. package/dashboard/dist/assets/index-K40Qw1tk.js +2 -0
  194. package/dashboard/dist/assets/{index-Di12t56M.js.map → index-K40Qw1tk.js.map} +1 -1
  195. package/dashboard/dist/assets/{index-BUK3qR-1.js → index-lCyNr5Xk.js} +2 -2
  196. package/dashboard/dist/assets/{index-BUK3qR-1.js.map → index-lCyNr5Xk.js.map} +1 -1
  197. package/dashboard/dist/assets/{mcp-B_xbczAt.js → mcp-CNE44TSp.js} +2 -2
  198. package/dashboard/dist/assets/{mcp-B_xbczAt.js.map → mcp-CNE44TSp.js.map} +1 -1
  199. package/dashboard/dist/assets/mcp-query-RQX0uN-5.js +2 -0
  200. package/dashboard/dist/assets/mcp-query-RQX0uN-5.js.map +1 -0
  201. package/dashboard/dist/assets/mcp-runs-0w40bdz-.js +2 -0
  202. package/dashboard/dist/assets/mcp-runs-0w40bdz-.js.map +1 -0
  203. package/dashboard/dist/assets/{namespaces-C3WtdO_9.js → namespaces-BbmdXuPp.js} +2 -2
  204. package/dashboard/dist/assets/{namespaces-C3WtdO_9.js.map → namespaces-BbmdXuPp.js.map} +1 -1
  205. package/dashboard/dist/assets/{roles-BDAsPpZG.js → roles-DoHYlhWH.js} +2 -2
  206. package/dashboard/dist/assets/{roles-BDAsPpZG.js.map → roles-DoHYlhWH.js.map} +1 -1
  207. package/dashboard/dist/assets/{settings-Ife_UwAp.js → settings-BAiJiCHS.js} +2 -2
  208. package/dashboard/dist/assets/{settings-Ife_UwAp.js.map → settings-BAiJiCHS.js.map} +1 -1
  209. package/dashboard/dist/assets/{tasks-BquNDHDI.js → tasks-CvroqHtm.js} +2 -2
  210. package/dashboard/dist/assets/{tasks-BquNDHDI.js.map → tasks-CvroqHtm.js.map} +1 -1
  211. package/dashboard/dist/assets/{useEventHooks-anv_B2Yy.js → useEventHooks-BHMbzR_y.js} +2 -2
  212. package/dashboard/dist/assets/{useEventHooks-anv_B2Yy.js.map → useEventHooks-BHMbzR_y.js.map} +1 -1
  213. package/dashboard/dist/assets/useFilterParams-CGRYFw_A.js +2 -0
  214. package/dashboard/dist/assets/useFilterParams-CGRYFw_A.js.map +1 -0
  215. package/dashboard/dist/assets/useYamlActivityEvents-D56KV14X.js +2 -0
  216. package/dashboard/dist/assets/useYamlActivityEvents-D56KV14X.js.map +1 -0
  217. package/dashboard/dist/assets/{users-CFcxB4v6.js → users-CxIMy_xw.js} +2 -2
  218. package/dashboard/dist/assets/{users-CFcxB4v6.js.map → users-CxIMy_xw.js.map} +1 -1
  219. package/dashboard/dist/assets/{vendor-icons-T4r2DSPD.js → vendor-icons-AFGxSeQS.js} +132 -82
  220. package/dashboard/dist/assets/vendor-icons-AFGxSeQS.js.map +1 -0
  221. package/dashboard/dist/assets/{workflows-CeRci9z3.js → workflows-yR9yais7.js} +2 -2
  222. package/dashboard/dist/assets/{workflows-CeRci9z3.js.map → workflows-yR9yais7.js.map} +1 -1
  223. package/dashboard/dist/assets/yaml-workflows-QVF2MZ0l.js +2 -0
  224. package/dashboard/dist/assets/yaml-workflows-QVF2MZ0l.js.map +1 -0
  225. package/dashboard/dist/index.html +3 -3
  226. package/docs/schema-exchange.md +173 -0
  227. package/docs/self-test.md +106 -0
  228. package/package.json +3 -1
  229. package/dashboard/dist/assets/CredentialsPage-DJablIbs.js +0 -2
  230. package/dashboard/dist/assets/McpQueryDetailPage-BWbinTM_.js +0 -5
  231. package/dashboard/dist/assets/McpQueryDetailPage-BWbinTM_.js.map +0 -1
  232. package/dashboard/dist/assets/McpQueryPage-lV6kfDG5.js +0 -2
  233. package/dashboard/dist/assets/McpRunDetailPage-D6gaxH3_.js +0 -2
  234. package/dashboard/dist/assets/McpRunDetailPage-D6gaxH3_.js.map +0 -1
  235. package/dashboard/dist/assets/McpRunsPage-DKvTklh9.js +0 -2
  236. package/dashboard/dist/assets/McpRunsPage-DKvTklh9.js.map +0 -1
  237. package/dashboard/dist/assets/OperatorDashboard-C9SSV96T.js +0 -2
  238. package/dashboard/dist/assets/RolesPage-SMedMuqa.js +0 -2
  239. package/dashboard/dist/assets/WorkflowPill-CZqGslD6.js +0 -2
  240. package/dashboard/dist/assets/WorkflowPill-CZqGslD6.js.map +0 -1
  241. package/dashboard/dist/assets/YamlWorkflowsPage-VjdhnLmO.js +0 -2
  242. package/dashboard/dist/assets/YamlWorkflowsPage-VjdhnLmO.js.map +0 -1
  243. package/dashboard/dist/assets/escalation-columns-Cyg58nkg.js +0 -2
  244. package/dashboard/dist/assets/helpers-BCix9c_m.js +0 -2
  245. package/dashboard/dist/assets/helpers-BCix9c_m.js.map +0 -1
  246. package/dashboard/dist/assets/index-BYZX9tOb.js.map +0 -1
  247. package/dashboard/dist/assets/index-Cg5nfiYX.js +0 -2
  248. package/dashboard/dist/assets/index-DcIKW-cZ.css +0 -1
  249. package/dashboard/dist/assets/index-Di12t56M.js +0 -2
  250. package/dashboard/dist/assets/mcp-query-B8-P_QoG.js +0 -2
  251. package/dashboard/dist/assets/mcp-query-B8-P_QoG.js.map +0 -1
  252. package/dashboard/dist/assets/mcp-runs-CWeZinoF.js +0 -2
  253. package/dashboard/dist/assets/mcp-runs-CWeZinoF.js.map +0 -1
  254. package/dashboard/dist/assets/useFilterParams-BUyLHcx_.js +0 -2
  255. package/dashboard/dist/assets/useFilterParams-BUyLHcx_.js.map +0 -1
  256. package/dashboard/dist/assets/useYamlActivityEvents-DN-PTgVx.js +0 -2
  257. package/dashboard/dist/assets/useYamlActivityEvents-DN-PTgVx.js.map +0 -1
  258. package/dashboard/dist/assets/vendor-icons-T4r2DSPD.js.map +0 -1
  259. package/dashboard/dist/assets/yaml-workflows-DLwd2BOX.js +0 -2
  260. package/dashboard/dist/assets/yaml-workflows-DLwd2BOX.js.map +0 -1
@@ -0,0 +1,13 @@
1
+ import type { LTApiResult } from '../types/sdk';
2
+ export declare function browseFiles(input: {
3
+ prefix?: string;
4
+ pageSize?: number;
5
+ continuationToken?: string;
6
+ }): Promise<LTApiResult>;
7
+ export declare function getFileMetadata(input: {
8
+ filePath: string;
9
+ }): Promise<LTApiResult>;
10
+ export declare function generateSignedUrl(input: {
11
+ filePath: string;
12
+ expiresIn: number;
13
+ }): Promise<LTApiResult>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browseFiles = browseFiles;
4
+ exports.getFileMetadata = getFileMetadata;
5
+ exports.generateSignedUrl = generateSignedUrl;
6
+ const storage_1 = require("../lib/storage");
7
+ const ALLOWED_EXPIRY = [3600, 21600, 86400, 604800, 2592000]; // 1h, 6h, 24h, 7d, 30d
8
+ async function browseFiles(input) {
9
+ try {
10
+ const result = await (0, storage_1.getStorageBackend)().listWithPrefixes(input.prefix, input.pageSize || 100, input.continuationToken);
11
+ return { status: 200, data: result };
12
+ }
13
+ catch (err) {
14
+ return { status: 500, error: err.message };
15
+ }
16
+ }
17
+ async function getFileMetadata(input) {
18
+ try {
19
+ const metadata = await (0, storage_1.getStorageBackend)().getMetadata(input.filePath);
20
+ return { status: 200, data: { path: input.filePath, ...metadata } };
21
+ }
22
+ catch (err) {
23
+ if (err.message?.includes('not found') || err.name === 'NotFound') {
24
+ return { status: 404, error: 'File not found' };
25
+ }
26
+ return { status: 500, error: err.message };
27
+ }
28
+ }
29
+ async function generateSignedUrl(input) {
30
+ if (!ALLOWED_EXPIRY.includes(input.expiresIn)) {
31
+ return {
32
+ status: 400,
33
+ error: `expiresIn must be one of: ${ALLOWED_EXPIRY.join(', ')} (seconds)`,
34
+ };
35
+ }
36
+ try {
37
+ const url = await (0, storage_1.getStorageBackend)().getSignedUrl(input.filePath, input.expiresIn);
38
+ const expiresAt = new Date(Date.now() + input.expiresIn * 1000).toISOString();
39
+ return { status: 200, data: { url, expiresAt } };
40
+ }
41
+ catch (err) {
42
+ return { status: 500, error: err.message };
43
+ }
44
+ }
@@ -26,6 +26,8 @@ export declare function listJobs(input: {
26
26
  entity?: string;
27
27
  search?: string;
28
28
  status?: string;
29
+ sort_by?: string;
30
+ order?: string;
29
31
  }): Promise<LTApiResult>;
30
32
  /**
31
33
  * Get the full execution history for a single job.
@@ -49,6 +49,8 @@ async function listJobs(input) {
49
49
  entity: input.entity,
50
50
  search: input.search,
51
51
  status: input.status,
52
+ sort_by: input.sort_by,
53
+ order: input.order,
52
54
  });
53
55
  return { status: 200, data: result };
54
56
  }
@@ -75,3 +75,14 @@ export declare function buildWorkflowSet(input: {
75
75
  export declare function deployWorkflowSet(input: {
76
76
  id: string;
77
77
  }): Promise<LTApiResult>;
78
+ /**
79
+ * Add additional workflows to an existing set.
80
+ *
81
+ * Invokes the planner with the new specification and existing set context.
82
+ * The planner appends new plan items (offset build_order) and builds them
83
+ * with sibling schema awareness so composition wiring works.
84
+ */
85
+ export declare function addToWorkflowSet(input: {
86
+ id: string;
87
+ specification: string;
88
+ }, auth?: LTApiAuth): Promise<LTApiResult>;
@@ -6,8 +6,10 @@ exports.getWorkflowSet = getWorkflowSet;
6
6
  exports.updateWorkflowSetPlanApi = updateWorkflowSetPlanApi;
7
7
  exports.buildWorkflowSet = buildWorkflowSet;
8
8
  exports.deployWorkflowSet = deployWorkflowSet;
9
+ exports.addToWorkflowSet = addToWorkflowSet;
9
10
  const workflow_sets_1 = require("../services/workflow-sets");
10
11
  const insight_1 = require("../services/insight");
12
+ const yaml_workflows_1 = require("./yaml-workflows");
11
13
  /**
12
14
  * Create a new workflow set and kick off the LLM-powered planner.
13
15
  *
@@ -168,3 +170,63 @@ async function deployWorkflowSet(input) {
168
170
  return { status: 500, error: err.message };
169
171
  }
170
172
  }
173
+ /**
174
+ * Add additional workflows to an existing set.
175
+ *
176
+ * Invokes the planner with the new specification and existing set context.
177
+ * The planner appends new plan items (offset build_order) and builds them
178
+ * with sibling schema awareness so composition wiring works.
179
+ */
180
+ async function addToWorkflowSet(input, auth) {
181
+ try {
182
+ if (!input.specification || typeof input.specification !== 'string') {
183
+ return { status: 400, error: 'specification is required' };
184
+ }
185
+ if (!process.env.OPENAI_API_KEY && !process.env.ANTHROPIC_API_KEY) {
186
+ return { status: 503, error: 'Workflow planner requires an LLM API key' };
187
+ }
188
+ const set = await (0, workflow_sets_1.getWorkflowSet)(input.id);
189
+ if (!set) {
190
+ return { status: 404, error: 'Workflow set not found' };
191
+ }
192
+ // Gather existing plan items and built workflow schemas
193
+ const existingPlan = (set.plan || []).map(p => ({
194
+ name: p.name,
195
+ description: p.description,
196
+ namespace: p.namespace,
197
+ }));
198
+ const yamlResult = await (0, yaml_workflows_1.listYamlWorkflows)({ set_id: input.id, limit: 100 });
199
+ const workflows = yamlResult.data?.workflows || [];
200
+ const existingSchemas = workflows
201
+ .filter((w) => w.status === 'active' || w.status === 'draft')
202
+ .map((w) => ({
203
+ name: w.name,
204
+ input_schema: w.input_schema || {},
205
+ output_schema: w.output_schema || {},
206
+ graph_topic: w.graph_topic,
207
+ }));
208
+ // Append the new spec to the set's specification field so the full
209
+ // history of what was pasted is preserved and visible on step 1.
210
+ await (0, workflow_sets_1.appendWorkflowSetSpecification)(input.id, input.specification);
211
+ const plannerResult = await (0, insight_1.startWorkflowPlanner)({
212
+ specification: input.specification,
213
+ setId: input.id,
214
+ wait: false,
215
+ userId: auth?.userId,
216
+ existingPlan,
217
+ existingSchemas,
218
+ });
219
+ await (0, workflow_sets_1.updateWorkflowSetSourceWorkflow)(input.id, plannerResult.workflow_id);
220
+ return {
221
+ status: 200,
222
+ data: {
223
+ ...set,
224
+ source_workflow_id: plannerResult.workflow_id,
225
+ planner_workflow_id: plannerResult.workflow_id,
226
+ },
227
+ };
228
+ }
229
+ catch (err) {
230
+ return { status: 500, error: err.message };
231
+ }
232
+ }
@@ -200,6 +200,15 @@ export declare function invokeYamlWorkflow(input: {
200
200
  export declare function archiveYamlWorkflow(input: {
201
201
  id: string;
202
202
  }): Promise<LTApiResult>;
203
+ /**
204
+ * Restore an archived YAML workflow back to draft status.
205
+ *
206
+ * Transitions the workflow from "archived" to "draft" so it can be
207
+ * redeployed. The workflow must be in "archived" status.
208
+ */
209
+ export declare function restoreYamlWorkflow(input: {
210
+ id: string;
211
+ }): Promise<LTApiResult>;
203
212
  /**
204
213
  * Retrieve the version history for a YAML workflow.
205
214
  *
@@ -45,6 +45,7 @@ exports.deployYamlWorkflow = deployYamlWorkflow;
45
45
  exports.activateYamlWorkflow = activateYamlWorkflow;
46
46
  exports.invokeYamlWorkflow = invokeYamlWorkflow;
47
47
  exports.archiveYamlWorkflow = archiveYamlWorkflow;
48
+ exports.restoreYamlWorkflow = restoreYamlWorkflow;
48
49
  exports.getVersionHistory = getVersionHistory;
49
50
  exports.getVersionSnapshot = getVersionSnapshot;
50
51
  exports.getYamlContent = getYamlContent;
@@ -58,6 +59,7 @@ const yamlWorkers = __importStar(require("../services/yaml-workflow/workers"));
58
59
  const invoke_1 = require("../services/yaml-workflow/invoke");
59
60
  const task_1 = require("../services/task");
60
61
  const cron_1 = require("../services/cron");
62
+ const utils_1 = require("../modules/utils");
61
63
  /** Return true if a Postgres error indicates an invalid/missing ID */
62
64
  function isNotFoundError(err) {
63
65
  const msg = err?.message ?? '';
@@ -143,8 +145,8 @@ async function createYamlWorkflow(input) {
143
145
  }
144
146
  }
145
147
  // Check for topic collision in the target namespace
146
- const compileAppId = app_id || 'longtail';
147
- const compileTopic = subscribes || name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
148
+ const compileAppId = (0, utils_1.sanitizeServerName)(app_id || 'longtail');
149
+ const compileTopic = (0, utils_1.sanitizeToolName)(subscribes || name);
148
150
  const conflicting = await yamlDb.checkTopicConflict(compileAppId, compileTopic);
149
151
  if (conflicting) {
150
152
  return {
@@ -220,15 +222,15 @@ async function createYamlWorkflowDirect(input) {
220
222
  if (!name || !yaml_content) {
221
223
  return { status: 400, error: 'name and yaml_content are required' };
222
224
  }
223
- // Sanitize name (tool name): lowercase alphanumeric, periods, dashes, underscores
224
- const sanitizedName = name.toLowerCase().replace(/[^a-z0-9._-]/g, '');
225
- // Sanitize app_id (MCP server name): force lowercase alphanumeric only
226
- const targetAppId = (app_id || 'longtail').toLowerCase().replace(/[^a-z0-9]/g, '');
227
- // Sanitize graph topic: lowercase alphanumeric, periods, dashes, underscores
228
- let graphTopic = (graph_topic || sanitizedName).toLowerCase().replace(/[^a-z0-9._-]/g, '');
225
+ // Sanitize name (tool name): snake_case only
226
+ const sanitizedName = (0, utils_1.sanitizeToolName)(name);
227
+ // Sanitize app_id (MCP server name): lowercase alphanumeric, must start with a letter
228
+ const targetAppId = (0, utils_1.sanitizeServerName)(app_id || 'longtail');
229
+ // Sanitize graph topic: snake_case only
230
+ let graphTopic = (0, utils_1.sanitizeToolName)(graph_topic || sanitizedName);
229
231
  const subscribesMatch = yaml_content.match(/subscribes:\s*(.+)/);
230
232
  if (subscribesMatch) {
231
- graphTopic = subscribesMatch[1].trim().replace(/^['"]|['"]$/g, '').toLowerCase().replace(/[^a-z0-9._-]/g, '');
233
+ graphTopic = (0, utils_1.sanitizeToolName)(subscribesMatch[1].trim().replace(/^['"]|['"]$/g, ''));
232
234
  }
233
235
  // Rewrite the subscribes line in the YAML to match the sanitized topic
234
236
  let finalYaml = yaml_content;
@@ -438,8 +440,10 @@ async function deployYamlWorkflow(input) {
438
440
  if (!wf) {
439
441
  return { status: 404, error: 'YAML workflow not found' };
440
442
  }
441
- // Use the version declared in the YAML (like package.json)
442
- const deployVersion = wf.app_version || '1';
443
+ // Compute the next app-level version for the namespace.
444
+ // Each deploy increments regardless of individual tool versions —
445
+ // adding a second tool (v1 of itself) to an app already at v1 produces app v2.
446
+ const deployVersion = await yamlDb.getNextAppVersion(wf.app_id);
443
447
  // Deploy + activate merged YAML for the full app_id
444
448
  const siblings = await yamlDb.listYamlWorkflowsByAppId(wf.app_id);
445
449
  await yamlDeployer.deployAppId(wf.app_id, deployVersion);
@@ -573,6 +577,31 @@ async function archiveYamlWorkflow(input) {
573
577
  return { status: 500, error: err.message };
574
578
  }
575
579
  }
580
+ /**
581
+ * Restore an archived YAML workflow back to draft status.
582
+ *
583
+ * Transitions the workflow from "archived" to "draft" so it can be
584
+ * redeployed. The workflow must be in "archived" status.
585
+ */
586
+ async function restoreYamlWorkflow(input) {
587
+ try {
588
+ const wf = await yamlDb.getYamlWorkflow(input.id);
589
+ if (!wf) {
590
+ return { status: 404, error: 'YAML workflow not found' };
591
+ }
592
+ if (wf.status !== 'archived') {
593
+ return { status: 400, error: 'Only archived workflows can be restored' };
594
+ }
595
+ const updated = await yamlDb.updateYamlWorkflowStatus(wf.id, 'draft');
596
+ return { status: 200, data: updated };
597
+ }
598
+ catch (err) {
599
+ if (isNotFoundError(err)) {
600
+ return { status: 404, error: 'YAML workflow not found' };
601
+ }
602
+ return { status: 500, error: err.message };
603
+ }
604
+ }
576
605
  // ---------------------------------------------------------------------------
577
606
  // Versions
578
607
  // ---------------------------------------------------------------------------
@@ -23,4 +23,19 @@ export declare class LocalStorageBackend implements StorageBackend {
23
23
  size: number;
24
24
  }>;
25
25
  createReadStream(key: string): Promise<NodeJS.ReadableStream>;
26
+ listWithPrefixes(prefix?: string, pageSize?: number, continuationToken?: string): Promise<{
27
+ files: Array<{
28
+ path: string;
29
+ size: number;
30
+ modified_at: string;
31
+ }>;
32
+ directories: string[];
33
+ nextToken?: string;
34
+ }>;
35
+ getMetadata(key: string): Promise<{
36
+ size: number;
37
+ modified_at: string;
38
+ content_type: string;
39
+ }>;
40
+ getSignedUrl(key: string, expiresInSeconds: number): Promise<string>;
26
41
  }
@@ -32,10 +32,15 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.LocalStorageBackend = void 0;
37
40
  const fs = __importStar(require("fs"));
38
41
  const path = __importStar(require("path"));
42
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
43
+ const mime_1 = require("./mime");
39
44
  const BASE_DIR = process.env.LT_FILE_STORAGE_DIR || './data/files';
40
45
  function resolveAndValidate(filePath) {
41
46
  const relative = filePath.replace(/^\/+/, '');
@@ -125,5 +130,63 @@ class LocalStorageBackend {
125
130
  }
126
131
  return fs.createReadStream(resolved);
127
132
  }
133
+ async listWithPrefixes(prefix, pageSize, continuationToken) {
134
+ ensureBaseDir();
135
+ const dir = prefix ? resolveAndValidate(prefix) : path.resolve(BASE_DIR);
136
+ if (!fs.existsSync(dir)) {
137
+ return { files: [], directories: [] };
138
+ }
139
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
140
+ const limit = pageSize || 100;
141
+ const offset = continuationToken ? parseInt(continuationToken, 10) : 0;
142
+ const allDirs = [];
143
+ const allFiles = [];
144
+ for (const entry of entries) {
145
+ const relativePath = path.relative(path.resolve(BASE_DIR), path.join(dir, entry.name));
146
+ if (entry.isDirectory()) {
147
+ allDirs.push(relativePath + '/');
148
+ }
149
+ else if (entry.isFile()) {
150
+ const stat = fs.statSync(path.join(dir, entry.name));
151
+ allFiles.push({
152
+ path: relativePath,
153
+ size: stat.size,
154
+ modified_at: stat.mtime.toISOString(),
155
+ });
156
+ }
157
+ }
158
+ // Directories first, then files — paginate the combined list
159
+ const combined = [...allDirs.map((d) => ({ type: 'dir', value: d })),
160
+ ...allFiles.map((f) => ({ type: 'file', value: f }))];
161
+ const page = combined.slice(offset, offset + limit);
162
+ const hasMore = offset + limit < combined.length;
163
+ const directories = page.filter((e) => e.type === 'dir').map((e) => e.value);
164
+ const files = page.filter((e) => e.type === 'file').map((e) => e.value);
165
+ return {
166
+ files,
167
+ directories,
168
+ nextToken: hasMore ? String(offset + limit) : undefined,
169
+ };
170
+ }
171
+ async getMetadata(key) {
172
+ const resolved = resolveAndValidate(key);
173
+ if (!fs.existsSync(resolved)) {
174
+ throw new Error(`File not found: ${key}`);
175
+ }
176
+ const stat = fs.statSync(resolved);
177
+ return {
178
+ size: stat.size,
179
+ modified_at: stat.mtime.toISOString(),
180
+ content_type: (0, mime_1.mimeFromPath)(key),
181
+ };
182
+ }
183
+ async getSignedUrl(key, expiresInSeconds) {
184
+ const secret = process.env.JWT_SECRET;
185
+ if (!secret) {
186
+ throw new Error('JWT_SECRET not configured — cannot generate signed URLs');
187
+ }
188
+ const token = jsonwebtoken_1.default.sign({ filePath: key.replace(/^\/+/, ''), purpose: 'file-download' }, secret, { expiresIn: expiresInSeconds });
189
+ return `/api/files/${key.replace(/^\/+/, '')}?token=${token}`;
190
+ }
128
191
  }
129
192
  exports.LocalStorageBackend = LocalStorageBackend;
@@ -0,0 +1,4 @@
1
+ /** Shared MIME type map used by file serving routes and storage backends. */
2
+ export declare const MIME_TYPES: Record<string, string>;
3
+ /** Resolve MIME type from file extension. */
4
+ export declare function mimeFromPath(filePath: string): string;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIME_TYPES = void 0;
4
+ exports.mimeFromPath = mimeFromPath;
5
+ /** Shared MIME type map used by file serving routes and storage backends. */
6
+ exports.MIME_TYPES = {
7
+ '.png': 'image/png',
8
+ '.jpg': 'image/jpeg',
9
+ '.jpeg': 'image/jpeg',
10
+ '.gif': 'image/gif',
11
+ '.svg': 'image/svg+xml',
12
+ '.webp': 'image/webp',
13
+ '.pdf': 'application/pdf',
14
+ '.json': 'application/json',
15
+ '.txt': 'text/plain',
16
+ '.html': 'text/html',
17
+ '.css': 'text/css',
18
+ '.js': 'application/javascript',
19
+ '.csv': 'text/csv',
20
+ '.xml': 'application/xml',
21
+ '.yaml': 'text/yaml',
22
+ '.yml': 'text/yaml',
23
+ '.md': 'text/markdown',
24
+ '.zip': 'application/zip',
25
+ '.gz': 'application/gzip',
26
+ };
27
+ /** Resolve MIME type from file extension. */
28
+ function mimeFromPath(filePath) {
29
+ const ext = filePath.includes('.') ? '.' + filePath.split('.').pop().toLowerCase() : '';
30
+ return exports.MIME_TYPES[ext] || 'application/octet-stream';
31
+ }
@@ -1,6 +1,7 @@
1
1
  import type { StorageBackend } from './types';
2
2
  export declare class S3StorageBackend implements StorageBackend {
3
3
  private client;
4
+ private signingClient;
4
5
  private bucket;
5
6
  private bucketReady;
6
7
  constructor();
@@ -28,4 +29,19 @@ export declare class S3StorageBackend implements StorageBackend {
28
29
  size: number;
29
30
  }>;
30
31
  createReadStream(key: string): Promise<NodeJS.ReadableStream>;
32
+ listWithPrefixes(prefix?: string, pageSize?: number, continuationToken?: string): Promise<{
33
+ files: Array<{
34
+ path: string;
35
+ size: number;
36
+ modified_at: string;
37
+ }>;
38
+ directories: string[];
39
+ nextToken?: string;
40
+ }>;
41
+ getMetadata(key: string): Promise<{
42
+ size: number;
43
+ modified_at: string;
44
+ content_type: string;
45
+ }>;
46
+ getSignedUrl(key: string, expiresInSeconds: number): Promise<string>;
31
47
  }
@@ -38,10 +38,14 @@ const fs = __importStar(require("fs"));
38
38
  const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  const client_s3_1 = require("@aws-sdk/client-s3");
41
+ const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
42
+ const mime_1 = require("./mime");
41
43
  const STAGING_DIR = path.join(os.tmpdir(), 'lt-staging');
42
44
  class S3StorageBackend {
43
45
  constructor() {
46
+ this.signingClient = null;
44
47
  const endpoint = process.env.LT_S3_ENDPOINT;
48
+ const publicEndpoint = process.env.LT_S3_PUBLIC_ENDPOINT;
45
49
  const region = process.env.LT_S3_REGION || 'us-east-1';
46
50
  const forcePathStyle = process.env.LT_S3_FORCE_PATH_STYLE === 'true';
47
51
  this.bucket = process.env.LT_S3_BUCKET || 'long-tail-files';
@@ -63,6 +67,12 @@ class S3StorageBackend {
63
67
  };
64
68
  }
65
69
  this.client = new client_s3_1.S3Client(clientConfig);
70
+ // Signed URLs need the public-facing endpoint so browsers can reach them.
71
+ // In Docker, LT_S3_ENDPOINT is the internal hostname (e.g. http://minio:9000)
72
+ // while LT_S3_PUBLIC_ENDPOINT is the host-accessible URL (e.g. http://localhost:9000).
73
+ if (publicEndpoint && publicEndpoint !== endpoint) {
74
+ this.signingClient = new client_s3_1.S3Client({ ...clientConfig, endpoint: publicEndpoint });
75
+ }
66
76
  this.bucketReady = this.ensureBucket();
67
77
  }
68
78
  async ensureBucket() {
@@ -168,6 +178,66 @@ class S3StorageBackend {
168
178
  }));
169
179
  return resp.Body;
170
180
  }
181
+ async listWithPrefixes(prefix, pageSize, continuationToken) {
182
+ await this.bucketReady;
183
+ const normalizedPrefix = prefix ? normalizeKey(prefix) : undefined;
184
+ const listPrefix = normalizedPrefix
185
+ ? (normalizedPrefix.endsWith('/') ? normalizedPrefix : normalizedPrefix + '/')
186
+ : undefined;
187
+ const resp = await this.client.send(new client_s3_1.ListObjectsV2Command({
188
+ Bucket: this.bucket,
189
+ Prefix: listPrefix,
190
+ Delimiter: '/',
191
+ MaxKeys: pageSize || 100,
192
+ ContinuationToken: continuationToken || undefined,
193
+ }));
194
+ const files = [];
195
+ for (const obj of resp.Contents || []) {
196
+ if (!obj.Key)
197
+ continue;
198
+ // Skip the prefix itself if it appears as a "file"
199
+ if (obj.Key === listPrefix)
200
+ continue;
201
+ files.push({
202
+ path: obj.Key,
203
+ size: obj.Size || 0,
204
+ modified_at: obj.LastModified?.toISOString() || new Date().toISOString(),
205
+ });
206
+ }
207
+ const directories = [];
208
+ for (const cp of resp.CommonPrefixes || []) {
209
+ if (cp.Prefix) {
210
+ directories.push(cp.Prefix);
211
+ }
212
+ }
213
+ return {
214
+ files,
215
+ directories,
216
+ nextToken: resp.NextContinuationToken || undefined,
217
+ };
218
+ }
219
+ async getMetadata(key) {
220
+ await this.bucketReady;
221
+ const resp = await this.client.send(new client_s3_1.HeadObjectCommand({
222
+ Bucket: this.bucket,
223
+ Key: normalizeKey(key),
224
+ }));
225
+ return {
226
+ size: resp.ContentLength || 0,
227
+ modified_at: resp.LastModified?.toISOString() || new Date().toISOString(),
228
+ content_type: resp.ContentType || (0, mime_1.mimeFromPath)(key),
229
+ };
230
+ }
231
+ async getSignedUrl(key, expiresInSeconds) {
232
+ await this.bucketReady;
233
+ const command = new client_s3_1.GetObjectCommand({
234
+ Bucket: this.bucket,
235
+ Key: normalizeKey(key),
236
+ });
237
+ // Use the public-facing client so the signed URL hostname is reachable from browsers
238
+ const client = this.signingClient || this.client;
239
+ return (0, s3_request_presigner_1.getSignedUrl)(client, command, { expiresIn: expiresInSeconds });
240
+ }
171
241
  }
172
242
  exports.S3StorageBackend = S3StorageBackend;
173
243
  /** Strip leading slashes to create a valid S3 key; reject traversal. */
@@ -38,4 +38,22 @@ export interface StorageBackend {
38
38
  }>;
39
39
  /** Create a readable stream for HTTP serving. */
40
40
  createReadStream(key: string): Promise<NodeJS.ReadableStream>;
41
+ /** List files and directory prefixes under a path (for directory browsing). */
42
+ listWithPrefixes(prefix?: string, pageSize?: number, continuationToken?: string): Promise<{
43
+ files: Array<{
44
+ path: string;
45
+ size: number;
46
+ modified_at: string;
47
+ }>;
48
+ directories: string[];
49
+ nextToken?: string;
50
+ }>;
51
+ /** Get file metadata without reading content. */
52
+ getMetadata(key: string): Promise<{
53
+ size: number;
54
+ modified_at: string;
55
+ content_type: string;
56
+ }>;
57
+ /** Generate a time-limited signed URL for unauthenticated file access. */
58
+ getSignedUrl(key: string, expiresInSeconds: number): Promise<string>;
41
59
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Sanitize a value for use as an MCP tool name or HotMesh graph topic
3
+ * (the `subscribes` field in a YAML DAG).
4
+ *
5
+ * ## Why snake_case
6
+ *
7
+ * MCP tool names become the `subscribes` topic in a HotMesh YAML DAG.
8
+ * That topic is how the system routes incoming messages to the correct
9
+ * workflow. The same value also appears in `worker` activity `topic`
10
+ * fields and in the `mcp_<server>_<tool>` activity type encoding.
11
+ *
12
+ * Because the activity type uses underscores to delimit server from tool
13
+ * (`mcp_longtail_take_screenshot`), both the server name (letters only)
14
+ * and the tool name (snake_case) must avoid dashes, dots, or other
15
+ * separators that would create ambiguity when parsing activity types.
16
+ *
17
+ * ## Risks if violated
18
+ *
19
+ * - Dashes in tool names cause `parseMcpActivityType()` to mis-split
20
+ * the server/tool boundary in activity type strings.
21
+ * - Dots or special chars can produce invalid HotMesh topic subscriptions
22
+ * that silently fail to route.
23
+ * - Mixed conventions across entry points (builder, planner, discovery)
24
+ * cause topic collisions or deployment failures when merging workflows
25
+ * into a single app namespace.
26
+ *
27
+ * ## Contract
28
+ *
29
+ * Input: any string (LLM output, user input, URL slug, etc.)
30
+ * Output: lowercase letters, digits, and underscores only.
31
+ * Runs of non-alphanumeric chars become a single underscore.
32
+ * No leading or trailing underscores.
33
+ *
34
+ * This is the ONE canonical backend implementation. The dashboard has an
35
+ * identical copy at `dashboard/src/lib/sanitize.ts`. No other file in
36
+ * the platform may inline this logic.
37
+ */
38
+ export declare function sanitizeToolName(value: string): string;
39
+ /**
40
+ * Sanitize a value for use as an MCP server name (the HotMesh `app.id`
41
+ * and Postgres schema name that isolates one server's workflows from another).
42
+ *
43
+ * ## Why lowercase alphanumeric, leading letter
44
+ *
45
+ * The server name becomes:
46
+ * 1. A Postgres schema name — must start with a letter; letters and digits
47
+ * are safe without quoting.
48
+ * 2. The first segment in the `mcp_<server>_<tool>` activity type encoding.
49
+ * Because underscores delimit server from tool, the server name itself
50
+ * must never contain underscores (or dashes, dots, etc.).
51
+ *
52
+ * ## Risks if violated
53
+ *
54
+ * - Underscores or dashes in the server name make `parseMcpActivityType()`
55
+ * split incorrectly — it uses the first `_` after `mcp_` as the boundary.
56
+ * - A leading digit produces an invalid Postgres schema name that requires
57
+ * quoting and breaks HotMesh's unquoted SQL paths.
58
+ * - Special characters can cause silent deployment failures or schema
59
+ * creation errors.
60
+ *
61
+ * ## Contract
62
+ *
63
+ * Input: any string (LLM suggestion, user input, etc.)
64
+ * Output: lowercase letters and digits only, guaranteed to start with a letter.
65
+ * All non-alphanumeric chars are stripped (not replaced).
66
+ * Leading digits are stripped so the result starts with a letter.
67
+ * Default: 'longtail' if the result is empty after sanitization.
68
+ *
69
+ * This is the ONE canonical backend implementation. The dashboard has an
70
+ * identical copy at `dashboard/src/lib/sanitize.ts`. No other file in
71
+ * the platform may inline this logic.
72
+ */
73
+ export declare function sanitizeServerName(value: string): string;