@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.
- package/build/api/files.d.ts +13 -0
- package/build/api/files.js +44 -0
- package/build/api/mcp-runs.d.ts +2 -0
- package/build/api/mcp-runs.js +2 -0
- package/build/api/workflow-sets.d.ts +11 -0
- package/build/api/workflow-sets.js +62 -0
- package/build/api/yaml-workflows.d.ts +9 -0
- package/build/api/yaml-workflows.js +40 -11
- package/build/lib/storage/local.d.ts +15 -0
- package/build/lib/storage/local.js +63 -0
- package/build/lib/storage/mime.d.ts +4 -0
- package/build/lib/storage/mime.js +31 -0
- package/build/lib/storage/s3.d.ts +16 -0
- package/build/lib/storage/s3.js +70 -0
- package/build/lib/storage/types.d.ts +18 -0
- package/build/modules/utils.d.ts +73 -0
- package/build/modules/utils.js +84 -0
- package/build/routes/file-browser.d.ts +2 -0
- package/build/routes/file-browser.js +112 -0
- package/build/routes/files.js +25 -17
- package/build/routes/index.js +2 -0
- package/build/routes/mcp-runs.js +2 -0
- package/build/routes/workflow-sets.js +9 -0
- package/build/routes/yaml-workflows/deployment.js +8 -0
- package/build/services/insight/index.d.ts +13 -0
- package/build/services/insight/index.js +4 -7
- package/build/services/insight/prompts.d.ts +1 -1
- package/build/services/insight/prompts.js +1 -1
- package/build/services/mcp-runs/queries.d.ts +2 -0
- package/build/services/mcp-runs/queries.js +11 -1
- package/build/services/mcp-runs/sql.d.ts +1 -1
- package/build/services/mcp-runs/sql.js +2 -2
- package/build/services/workflow-sets/db.d.ts +1 -0
- package/build/services/workflow-sets/db.js +6 -0
- package/build/services/workflow-sets/index.d.ts +1 -1
- package/build/services/workflow-sets/index.js +2 -1
- package/build/services/workflow-sets/sql.d.ts +1 -0
- package/build/services/workflow-sets/sql.js +6 -1
- package/build/services/yaml-workflow/db.d.ts +12 -0
- package/build/services/yaml-workflow/db.js +18 -0
- package/build/services/yaml-workflow/generator.js +4 -4
- package/build/services/yaml-workflow/pipeline/build/index.d.ts +2 -1
- package/build/services/yaml-workflow/pipeline/build/index.js +3 -2
- package/build/services/yaml-workflow/pipeline/build/utils.d.ts +0 -2
- package/build/services/yaml-workflow/pipeline/build/utils.js +0 -8
- package/build/services/yaml-workflow/sql.d.ts +2 -1
- package/build/services/yaml-workflow/sql.js +10 -3
- package/build/system/activities/schema-exchange.d.ts +50 -0
- package/build/system/activities/schema-exchange.js +210 -0
- package/build/system/activities/sql.d.ts +1 -1
- package/build/system/activities/sql.js +12 -1
- package/build/system/index.js +1 -0
- package/build/system/mcp-servers/knowledge.js +35 -6
- package/build/system/mcp-servers/schema-exchange.d.ts +4 -0
- package/build/system/mcp-servers/schema-exchange.js +93 -0
- package/build/system/seed/server-definitions.d.ts +112 -1
- package/build/system/seed/server-definitions.js +37 -0
- package/build/system/seed/tool-manifests-data.d.ts +87 -0
- package/build/system/seed/tool-manifests-data.js +37 -1
- package/build/system/seed/tool-manifests-knowledge.d.ts +9 -1
- package/build/system/seed/tool-manifests-knowledge.js +6 -5
- package/build/system/workflows/mcp-workflow-builder/prompts.d.ts +1 -1
- package/build/system/workflows/mcp-workflow-builder/prompts.js +21 -20
- package/build/system/workflows/mcp-workflow-planner/activities/persist.d.ts +1 -1
- package/build/system/workflows/mcp-workflow-planner/activities/persist.js +3 -3
- package/build/system/workflows/mcp-workflow-planner/index.js +39 -5
- package/build/system/workflows/mcp-workflow-planner/prompts.d.ts +1 -1
- package/build/system/workflows/mcp-workflow-planner/prompts.js +3 -3
- package/build/tsconfig.tsbuildinfo +1 -1
- package/dashboard/dist/assets/{AdminDashboard-DUrSBQOl.js → AdminDashboard-Cngijp4Q.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DUrSBQOl.js.map → AdminDashboard-Cngijp4Q.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-Dbd1qUK_.js → AvailableEscalationsPage-CpBfGV1h.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-Dbd1qUK_.js.map → AvailableEscalationsPage-CpBfGV1h.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-Cg5iNEkm.js → BotPicker-B8Uvw9Si.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-Cg5iNEkm.js.map → BotPicker-B8Uvw9Si.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-Kd9UIyeU.js → CollapsibleSection-DiFPuWOY.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-Kd9UIyeU.js.map → CollapsibleSection-DiFPuWOY.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-DZMgmlof.js → ConfirmDeleteModal-CpXG9uyu.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-DZMgmlof.js.map → ConfirmDeleteModal-CpXG9uyu.js.map} +1 -1
- package/dashboard/dist/assets/{CopyableId-cPFTRm8q.js → CopyableId-DKsTR9lK.js} +2 -2
- package/dashboard/dist/assets/{CopyableId-cPFTRm8q.js.map → CopyableId-DKsTR9lK.js.map} +1 -1
- package/dashboard/dist/assets/CredentialsPage-DlS7Kf40.js +2 -0
- package/dashboard/dist/assets/{CredentialsPage-DJablIbs.js.map → CredentialsPage-DlS7Kf40.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-NgIP6YDW.js → CustomDurationPicker-B-9eW3pm.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-NgIP6YDW.js.map → CustomDurationPicker-B-9eW3pm.js.map} +1 -1
- package/dashboard/dist/assets/{DataTable-CTRhTAfT.js → DataTable-DkOokbtL.js} +2 -2
- package/dashboard/dist/assets/{DataTable-CTRhTAfT.js.map → DataTable-DkOokbtL.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-HcSJ_MMs.js → ElapsedCell-DVtHqM-5.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-HcSJ_MMs.js.map → ElapsedCell-DVtHqM-5.js.map} +1 -1
- package/dashboard/dist/assets/{EmptyState-joNbd4gg.js → EmptyState-C7KIMIbE.js} +2 -2
- package/dashboard/dist/assets/{EmptyState-joNbd4gg.js.map → EmptyState-C7KIMIbE.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-DpXDnQux.js → EscalationsOverview-BMKBlkPx.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DpXDnQux.js.map → EscalationsOverview-BMKBlkPx.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CYem3v8n.js → EventTable-BYZ5OVdQ.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CYem3v8n.js.map → EventTable-BYZ5OVdQ.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-BiO8SOzc.js → FilterBar-C5r3n6YO.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-BiO8SOzc.js.map → FilterBar-C5r3n6YO.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-6yRDh2e9.js → ListToolbar-BGUajIsW.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-6yRDh2e9.js.map → ListToolbar-BGUajIsW.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CUgSxkQe.js → McpOverview-B_kJYHea.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CUgSxkQe.js.map → McpOverview-B_kJYHea.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryDetailPage-Czsmovqw.js +5 -0
- package/dashboard/dist/assets/McpQueryDetailPage-Czsmovqw.js.map +1 -0
- package/dashboard/dist/assets/McpQueryPage-BgAq_bQg.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-lV6kfDG5.js.map → McpQueryPage-BgAq_bQg.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-B8c0OszR.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-B8c0OszR.js.map +1 -0
- package/dashboard/dist/assets/McpRunsPage-BY8C6k78.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-BY8C6k78.js.map +1 -0
- package/dashboard/dist/assets/{Modal-BuTvD0pz.js → Modal-E1yRnCeW.js} +2 -2
- package/dashboard/dist/assets/{Modal-BuTvD0pz.js.map → Modal-E1yRnCeW.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-C8MSTzey.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-C9SSV96T.js.map → OperatorDashboard-C8MSTzey.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-BcTVF9ul.js → PageHeader-Cm5HBQF_.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-BcTVF9ul.js.map → PageHeader-Cm5HBQF_.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-BI7JG5x6.js → PageHeaderWithStats-CNmWJFSN.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-BI7JG5x6.js.map → PageHeaderWithStats-CNmWJFSN.js.map} +1 -1
- package/dashboard/dist/assets/{PriorityBadge-DqVaOU65.js → PriorityBadge-HSI4RVhs.js} +2 -2
- package/dashboard/dist/assets/{PriorityBadge-DqVaOU65.js.map → PriorityBadge-HSI4RVhs.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-hFMhf9qa.js → ProcessDetailPage-Dln8622H.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-hFMhf9qa.js.map → ProcessDetailPage-Dln8622H.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-ByVoBCQ3.js → ProcessesListPage-bIsd9N_h.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-ByVoBCQ3.js.map → ProcessesListPage-bIsd9N_h.js.map} +1 -1
- package/dashboard/dist/assets/{RolePill-D9ZIkYiu.js → RolePill-BVUp2bF0.js} +2 -2
- package/dashboard/dist/assets/{RolePill-D9ZIkYiu.js.map → RolePill-BVUp2bF0.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-kH-Njt25.js +2 -0
- package/dashboard/dist/assets/{RolesPage-SMedMuqa.js.map → RolesPage-kH-Njt25.js.map} +1 -1
- package/dashboard/dist/assets/{RowActions-yDhwwDbU.js → RowActions-DbUJPfaW.js} +2 -2
- package/dashboard/dist/assets/{RowActions-yDhwwDbU.js.map → RowActions-DbUJPfaW.js.map} +1 -1
- package/dashboard/dist/assets/{StatCard-BrBnQFxh.js → StatCard-Bs3JbyAz.js} +2 -2
- package/dashboard/dist/assets/{StatCard-BrBnQFxh.js.map → StatCard-Bs3JbyAz.js.map} +1 -1
- package/dashboard/dist/assets/{StatusBadge-xgb-lZku.js → StatusBadge-CakDdsCw.js} +2 -2
- package/dashboard/dist/assets/{StatusBadge-xgb-lZku.js.map → StatusBadge-CakDdsCw.js.map} +1 -1
- package/dashboard/dist/assets/{StepIndicator-B9ps2SvM.js → StepIndicator-Cd_SG_yA.js} +2 -2
- package/dashboard/dist/assets/{StepIndicator-B9ps2SvM.js.map → StepIndicator-Cd_SG_yA.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-DTIjBKN3.js → StickyPagination-Bz0C18nC.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-DTIjBKN3.js.map → StickyPagination-Bz0C18nC.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-RK4Yu66z.js → SwimlaneTimeline-Cfe-xQRX.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-RK4Yu66z.js.map → SwimlaneTimeline-Cfe-xQRX.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CdNUuqk4.js → TagInput-ClFhXG-U.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CdNUuqk4.js.map → TagInput-ClFhXG-U.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-C-nzaNea.js → TaskDetailPage-SuMBdARt.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-C-nzaNea.js.map → TaskDetailPage-SuMBdARt.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-Lvr2-NzS.js → TaskQueuePill-lJR1JW_W.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-Lvr2-NzS.js.map → TaskQueuePill-lJR1JW_W.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DSUmD84y.js → TasksListPage-Dkq1Vtbt.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DSUmD84y.js.map → TasksListPage-Dkq1Vtbt.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-BZdLdrIh.js → TimeAgo-DgfDZ1pl.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-BZdLdrIh.js.map → TimeAgo-DgfDZ1pl.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-QX_0i5FK.js → TimestampCell-MpHZ1hMD.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-QX_0i5FK.js.map → TimestampCell-MpHZ1hMD.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DyZMXcBm.js → UserName-DqsosA4B.js} +2 -2
- package/dashboard/dist/assets/{UserName-DyZMXcBm.js.map → UserName-DqsosA4B.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-DjVxfZaF.js → WorkflowExecutionPage-CVlg38C3.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-DjVxfZaF.js.map → WorkflowExecutionPage-CVlg38C3.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowPill-CRpZhjGR.js +2 -0
- package/dashboard/dist/assets/WorkflowPill-CRpZhjGR.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-DZjuiFZ0.js → WorkflowsDashboard-Ugzbs8mV.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DZjuiFZ0.js.map → WorkflowsDashboard-Ugzbs8mV.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CLnLRpOu.js → WorkflowsOverview-CIp_lTNl.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CLnLRpOu.js.map → WorkflowsOverview-CIp_lTNl.js.map} +1 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-BICF0fRO.js +2 -0
- package/dashboard/dist/assets/YamlWorkflowsPage-BICF0fRO.js.map +1 -0
- package/dashboard/dist/assets/{bots-DIM6lBoY.js → bots-DPfUpVqI.js} +2 -2
- package/dashboard/dist/assets/{bots-DIM6lBoY.js.map → bots-DPfUpVqI.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-JOTuOqjq.js → escalation-RrCDbMC3.js} +2 -2
- package/dashboard/dist/assets/{escalation-JOTuOqjq.js.map → escalation-RrCDbMC3.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-CDGa9wsD.js +2 -0
- package/dashboard/dist/assets/{escalation-columns-Cyg58nkg.js.map → escalation-columns-CDGa9wsD.js.map} +1 -1
- package/dashboard/dist/assets/{helpers-B1BDxBZd.js → helpers-ZSKqdkdS.js} +2 -2
- package/dashboard/dist/assets/{helpers-B1BDxBZd.js.map → helpers-ZSKqdkdS.js.map} +1 -1
- package/dashboard/dist/assets/helpers-rMEcLwKs.js +2 -0
- package/dashboard/dist/assets/helpers-rMEcLwKs.js.map +1 -0
- package/dashboard/dist/assets/{index-DDYFpi4l.js → index-ABcJHHlN.js} +2 -2
- package/dashboard/dist/assets/{index-DDYFpi4l.js.map → index-ABcJHHlN.js.map} +1 -1
- package/dashboard/dist/assets/{index-D1wVX50Z.js → index-B91h_jZ0.js} +2 -2
- package/dashboard/dist/assets/{index-D1wVX50Z.js.map → index-B91h_jZ0.js.map} +1 -1
- package/dashboard/dist/assets/{index-BcR6PfpY.js → index-BbI2dzhJ.js} +2 -2
- package/dashboard/dist/assets/{index-BcR6PfpY.js.map → index-BbI2dzhJ.js.map} +1 -1
- package/dashboard/dist/assets/{index-BizfauqT.js → index-BmVCyB6C.js} +4 -4
- package/dashboard/dist/assets/{index-BizfauqT.js.map → index-BmVCyB6C.js.map} +1 -1
- package/dashboard/dist/assets/index-C1E5GTs9.js +2 -0
- package/dashboard/dist/assets/index-C1E5GTs9.js.map +1 -0
- package/dashboard/dist/assets/{index-Cf60K3x9.js → index-C90ZPzXk.js} +2 -2
- package/dashboard/dist/assets/{index-Cf60K3x9.js.map → index-C90ZPzXk.js.map} +1 -1
- package/dashboard/dist/assets/{index-BYZX9tOb.js → index-Cmgrk7PQ.js} +58 -58
- package/dashboard/dist/assets/index-Cmgrk7PQ.js.map +1 -0
- package/dashboard/dist/assets/{index-Ds0JoXS2.js → index-DNytWfSZ.js} +2 -2
- package/dashboard/dist/assets/{index-Ds0JoXS2.js.map → index-DNytWfSZ.js.map} +1 -1
- package/dashboard/dist/assets/index-DX6zxr6t.js +2 -0
- package/dashboard/dist/assets/{index-Cg5nfiYX.js.map → index-DX6zxr6t.js.map} +1 -1
- package/dashboard/dist/assets/index-DeX-ezqf.css +1 -0
- package/dashboard/dist/assets/index-K40Qw1tk.js +2 -0
- package/dashboard/dist/assets/{index-Di12t56M.js.map → index-K40Qw1tk.js.map} +1 -1
- package/dashboard/dist/assets/{index-BUK3qR-1.js → index-lCyNr5Xk.js} +2 -2
- package/dashboard/dist/assets/{index-BUK3qR-1.js.map → index-lCyNr5Xk.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-B_xbczAt.js → mcp-CNE44TSp.js} +2 -2
- package/dashboard/dist/assets/{mcp-B_xbczAt.js.map → mcp-CNE44TSp.js.map} +1 -1
- package/dashboard/dist/assets/mcp-query-RQX0uN-5.js +2 -0
- package/dashboard/dist/assets/mcp-query-RQX0uN-5.js.map +1 -0
- package/dashboard/dist/assets/mcp-runs-0w40bdz-.js +2 -0
- package/dashboard/dist/assets/mcp-runs-0w40bdz-.js.map +1 -0
- package/dashboard/dist/assets/{namespaces-C3WtdO_9.js → namespaces-BbmdXuPp.js} +2 -2
- package/dashboard/dist/assets/{namespaces-C3WtdO_9.js.map → namespaces-BbmdXuPp.js.map} +1 -1
- package/dashboard/dist/assets/{roles-BDAsPpZG.js → roles-DoHYlhWH.js} +2 -2
- package/dashboard/dist/assets/{roles-BDAsPpZG.js.map → roles-DoHYlhWH.js.map} +1 -1
- package/dashboard/dist/assets/{settings-Ife_UwAp.js → settings-BAiJiCHS.js} +2 -2
- package/dashboard/dist/assets/{settings-Ife_UwAp.js.map → settings-BAiJiCHS.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-BquNDHDI.js → tasks-CvroqHtm.js} +2 -2
- package/dashboard/dist/assets/{tasks-BquNDHDI.js.map → tasks-CvroqHtm.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-anv_B2Yy.js → useEventHooks-BHMbzR_y.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-anv_B2Yy.js.map → useEventHooks-BHMbzR_y.js.map} +1 -1
- package/dashboard/dist/assets/useFilterParams-CGRYFw_A.js +2 -0
- package/dashboard/dist/assets/useFilterParams-CGRYFw_A.js.map +1 -0
- package/dashboard/dist/assets/useYamlActivityEvents-D56KV14X.js +2 -0
- package/dashboard/dist/assets/useYamlActivityEvents-D56KV14X.js.map +1 -0
- package/dashboard/dist/assets/{users-CFcxB4v6.js → users-CxIMy_xw.js} +2 -2
- package/dashboard/dist/assets/{users-CFcxB4v6.js.map → users-CxIMy_xw.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-T4r2DSPD.js → vendor-icons-AFGxSeQS.js} +132 -82
- package/dashboard/dist/assets/vendor-icons-AFGxSeQS.js.map +1 -0
- package/dashboard/dist/assets/{workflows-CeRci9z3.js → workflows-yR9yais7.js} +2 -2
- package/dashboard/dist/assets/{workflows-CeRci9z3.js.map → workflows-yR9yais7.js.map} +1 -1
- package/dashboard/dist/assets/yaml-workflows-QVF2MZ0l.js +2 -0
- package/dashboard/dist/assets/yaml-workflows-QVF2MZ0l.js.map +1 -0
- package/dashboard/dist/index.html +3 -3
- package/docs/schema-exchange.md +173 -0
- package/docs/self-test.md +106 -0
- package/package.json +3 -1
- package/dashboard/dist/assets/CredentialsPage-DJablIbs.js +0 -2
- package/dashboard/dist/assets/McpQueryDetailPage-BWbinTM_.js +0 -5
- package/dashboard/dist/assets/McpQueryDetailPage-BWbinTM_.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-lV6kfDG5.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-D6gaxH3_.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-D6gaxH3_.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage-DKvTklh9.js +0 -2
- package/dashboard/dist/assets/McpRunsPage-DKvTklh9.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-C9SSV96T.js +0 -2
- package/dashboard/dist/assets/RolesPage-SMedMuqa.js +0 -2
- package/dashboard/dist/assets/WorkflowPill-CZqGslD6.js +0 -2
- package/dashboard/dist/assets/WorkflowPill-CZqGslD6.js.map +0 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-VjdhnLmO.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowsPage-VjdhnLmO.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-Cyg58nkg.js +0 -2
- package/dashboard/dist/assets/helpers-BCix9c_m.js +0 -2
- package/dashboard/dist/assets/helpers-BCix9c_m.js.map +0 -1
- package/dashboard/dist/assets/index-BYZX9tOb.js.map +0 -1
- package/dashboard/dist/assets/index-Cg5nfiYX.js +0 -2
- package/dashboard/dist/assets/index-DcIKW-cZ.css +0 -1
- package/dashboard/dist/assets/index-Di12t56M.js +0 -2
- package/dashboard/dist/assets/mcp-query-B8-P_QoG.js +0 -2
- package/dashboard/dist/assets/mcp-query-B8-P_QoG.js.map +0 -1
- package/dashboard/dist/assets/mcp-runs-CWeZinoF.js +0 -2
- package/dashboard/dist/assets/mcp-runs-CWeZinoF.js.map +0 -1
- package/dashboard/dist/assets/useFilterParams-BUyLHcx_.js +0 -2
- package/dashboard/dist/assets/useFilterParams-BUyLHcx_.js.map +0 -1
- package/dashboard/dist/assets/useYamlActivityEvents-DN-PTgVx.js +0 -2
- package/dashboard/dist/assets/useYamlActivityEvents-DN-PTgVx.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-T4r2DSPD.js.map +0 -1
- package/dashboard/dist/assets/yaml-workflows-DLwd2BOX.js +0 -2
- 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
|
+
}
|
package/build/api/mcp-runs.d.ts
CHANGED
package/build/api/mcp-runs.js
CHANGED
|
@@ -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 =
|
|
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):
|
|
224
|
-
const sanitizedName =
|
|
225
|
-
// Sanitize app_id (MCP server name):
|
|
226
|
-
const targetAppId = (app_id || 'longtail')
|
|
227
|
-
// Sanitize graph topic:
|
|
228
|
-
let graphTopic = (graph_topic || sanitizedName)
|
|
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, '')
|
|
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
|
-
//
|
|
442
|
-
|
|
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,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
|
}
|
package/build/lib/storage/s3.js
CHANGED
|
@@ -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;
|