@hotmeshio/long-tail 0.7.4 → 0.8.0
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/roles.d.ts +4 -0
- package/build/api/roles.js +7 -1
- package/build/api/workflows/config.d.ts +7 -1
- package/build/api/workflows/config.js +7 -1
- package/build/api/workflows/discovery.js +1 -4
- package/build/lib/db/schemas/019_role_priority.sql +28 -0
- package/build/lib/db/schemas/020_workflow_certified.sql +34 -0
- package/build/modules/ltconfig.d.ts +3 -2
- package/build/modules/ltconfig.js +4 -4
- package/build/routes/roles.js +1 -1
- package/build/routes/workflows/config.js +1 -0
- package/build/scripts/ortho-populate.d.ts +7 -1
- package/build/scripts/ortho-populate.js +21 -6
- package/build/services/config/cache.js +1 -0
- package/build/services/config/read.js +2 -0
- package/build/services/config/sql.d.ts +2 -2
- package/build/services/config/sql.js +6 -5
- package/build/services/config/write.js +13 -0
- package/build/services/escalation/bulk.d.ts +3 -1
- package/build/services/escalation/bulk.js +3 -1
- package/build/services/escalation/crud.d.ts +4 -3
- package/build/services/escalation/crud.js +4 -3
- package/build/services/escalation/facet-sql.d.ts +1 -0
- package/build/services/escalation/facet-sql.js +7 -6
- package/build/services/escalation/queries.js +27 -5
- package/build/services/escalation/sql.d.ts +19 -2
- package/build/services/escalation/sql.js +32 -9
- package/build/services/escalation/types.d.ts +7 -1
- package/build/services/role/index.js +2 -0
- package/build/services/role/sql.d.ts +4 -4
- package/build/services/role/sql.js +12 -6
- package/build/services/role/types.d.ts +14 -0
- package/build/start/workers.js +1 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +9 -0
- package/build/system/mcp-servers/admin/schemas.js +6 -1
- package/build/system/mcp-servers/admin/users.js +4 -1
- package/build/system/mcp-servers/admin/workflow-config.js +1 -0
- package/build/system/seed/tool-manifests-admin.d.ts +152 -0
- package/build/system/seed/tool-manifests-admin.js +1 -1
- package/build/types/config.d.ts +16 -0
- package/build/types/startup.d.ts +11 -1
- package/dashboard/dist/assets/{AdminDashboard-C9eE1VEp.js → AdminDashboard-9JLg41f-.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-C9eE1VEp.js.map → AdminDashboard-9JLg41f-.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Ba8QfXM2.js → AgentConfigPage-D5P1hcOc.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-Ba8QfXM2.js.map → AgentConfigPage-D5P1hcOc.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-DVmec0CP.js → AgentDetailPage-DrijeAV8.js} +4 -4
- package/dashboard/dist/assets/{AgentDetailPage-DVmec0CP.js.map → AgentDetailPage-DrijeAV8.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-DNiCYMh3.js → AgentsPage-BW5d0JOt.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DNiCYMh3.js.map → AgentsPage-BW5d0JOt.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-gDIF8ocC.js → AvailableEscalationsPage-CoZ4Lufr.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-gDIF8ocC.js.map → AvailableEscalationsPage-CoZ4Lufr.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-3wBcBavT.js → BotPicker-xGUCxJo-.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-3wBcBavT.js.map → BotPicker-xGUCxJo-.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-WoPkND2y.js → CapabilitiesPage-ChaRJiyg.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-WoPkND2y.js.map → CapabilitiesPage-ChaRJiyg.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-Cx99mqNN.js → CollapsibleSection-CXMPecjj.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-Cx99mqNN.js.map → CollapsibleSection-CXMPecjj.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-DbNaoPbF.js → CredentialsPage-BbdfZh1D.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-DbNaoPbF.js.map → CredentialsPage-BbdfZh1D.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-DLW4_Zg4.js → CronLabel-CJUL50eO.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-DLW4_Zg4.js.map → CronLabel-CJUL50eO.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-BRUFjkiu.js → CustomDurationPicker-id6Tq64R.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-BRUFjkiu.js.map → CustomDurationPicker-id6Tq64R.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-DEX0BBqt.js → DropZone-CZDmNBn-.js} +2 -2
- package/dashboard/dist/assets/{DropZone-DEX0BBqt.js.map → DropZone-CZDmNBn-.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BtmDQaym.js → ElapsedCell-B8TrnyT5.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BtmDQaym.js.map → ElapsedCell-B8TrnyT5.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-Bm4yBEGN.js → EscalationSchemaPage-C6UuZgCD.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-Bm4yBEGN.js.map → EscalationSchemaPage-C6UuZgCD.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-B3xZwTQA.js → EscalationsOverview-CjLyujJ3.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-B3xZwTQA.js.map → EscalationsOverview-CjLyujJ3.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-Bh9FKXLI.js → EventTable-D_rNmyGG.js} +2 -2
- package/dashboard/dist/assets/{EventTable-Bh9FKXLI.js.map → EventTable-D_rNmyGG.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-ov_ANlaE.js → GraphInvokePage-BMyrTnI6.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-ov_ANlaE.js.map → GraphInvokePage-BMyrTnI6.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-D0DV6izb.js → HomePage-MBv6pwk4.js} +2 -2
- package/dashboard/dist/assets/{HomePage-D0DV6izb.js.map → HomePage-MBv6pwk4.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-FpcvqNkN.js → ListToolbar--FkPpYDY.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-FpcvqNkN.js.map → ListToolbar--FkPpYDY.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-DcuRiAff.js → McpOverview-CtAFXv-T.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-DcuRiAff.js.map → McpOverview-CtAFXv-T.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-BcYl67KP.js → McpQueryDetailPage-5Jzz2q2g.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-BcYl67KP.js.map → McpQueryDetailPage-5Jzz2q2g.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-C3GDzqZM.js → McpQueryPage-DveeRF5I.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-C3GDzqZM.js.map → McpQueryPage-DveeRF5I.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-SzOyhDBW.js → McpRunDetailPage-BYIxBcA7.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-SzOyhDBW.js.map → McpRunDetailPage-BYIxBcA7.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-BwgB6mRV.js → McpRunsPage-Cgke6ask.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BwgB6mRV.js.map → McpRunsPage-Cgke6ask.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-CwKdoyRM.js → NamespacePill-DlqBTvFR.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-CwKdoyRM.js.map → NamespacePill-DlqBTvFR.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-D2i7xrAI.js +2 -0
- package/dashboard/dist/assets/OperationsPage-D2i7xrAI.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-DSXh-yYw.js → OperatorDashboard-DMQwodfo.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-DSXh-yYw.js.map → OperatorDashboard-DMQwodfo.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-BhaWTWcM.js → PageHeader-CFta2VaS.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-BhaWTWcM.js.map → PageHeader-CFta2VaS.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-yex5WmHB.js → PageHeaderWithStats-CcVAji-7.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-yex5WmHB.js.map → PageHeaderWithStats-CcVAji-7.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-iIB1rwEt.js → ProcessDetailPage-Dodrk5-z.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-iIB1rwEt.js.map → ProcessDetailPage-Dodrk5-z.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-BrqWR2zr.js → ProcessesListPage-BVtkWVLY.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-BrqWR2zr.js.map → ProcessesListPage-BVtkWVLY.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-8NldZI49.js +11 -0
- package/dashboard/dist/assets/RoleDetailPage-8NldZI49.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-Cl71eDUA.js → RolePill-CXEyPMqb.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Cl71eDUA.js.map → RolePill-CXEyPMqb.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-Bzp9FhAl.js → RolesPage-BJBjtFNY.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-Bzp9FhAl.js.map → RolesPage-BJBjtFNY.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-DXbHRxQs.js → RunAsSelector-Cc1i017Z.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-DXbHRxQs.js.map → RunAsSelector-Cc1i017Z.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-D8XuMSWD.js → StickyPagination-GQKFFTjL.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-D8XuMSWD.js.map → StickyPagination-GQKFFTjL.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-D5v95pmz.js → StreamMessageDetail-BYzfjWe_.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-D5v95pmz.js.map → StreamMessageDetail-BYzfjWe_.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-CUEf2R5R.js → SwimlaneTimeline-DLVmRrd-.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CUEf2R5R.js.map → SwimlaneTimeline-DLVmRrd-.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-B_WQiuer.js → TagInput-CQg-TrV9.js} +2 -2
- package/dashboard/dist/assets/{TagInput-B_WQiuer.js.map → TagInput-CQg-TrV9.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BJvKNwEe.js → TaskDetailPage-HzQpzp6D.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BJvKNwEe.js.map → TaskDetailPage-HzQpzp6D.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-Lgn2obNU.js → TaskQueuePill-DyvtL8X4.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-Lgn2obNU.js.map → TaskQueuePill-DyvtL8X4.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CM7BWh_h.js → TasksListPage-DpqevgTz.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CM7BWh_h.js.map → TasksListPage-DpqevgTz.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-D1K7xKq5.js → TimeAgo-CftB8aJE.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-D1K7xKq5.js.map → TimeAgo-CftB8aJE.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-7G0echgP.js → TimestampCell-BSKj1jqT.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-7G0echgP.js.map → TimestampCell-BSKj1jqT.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-uVqcbHPE.js → ToolPill-miuRcj1Q.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-uVqcbHPE.js.map → ToolPill-miuRcj1Q.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-LxuJvURK.js → ToolTestPanel-DVQICZUQ.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-LxuJvURK.js.map → ToolTestPanel-DVQICZUQ.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-JvAX6rrR.js → TopicDetailPage-B2LAQDEg.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-JvAX6rrR.js.map → TopicDetailPage-B2LAQDEg.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-tFT2AcTz.js → TopicsPage-SwPAnN2C.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-tFT2AcTz.js.map → TopicsPage-SwPAnN2C.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Djz0nmaZ.js → UserName-Ce9gvlT5.js} +2 -2
- package/dashboard/dist/assets/{UserName-Djz0nmaZ.js.map → UserName-Ce9gvlT5.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-f53hilbI.js → WorkflowExecutionPage-BI9NzeaH.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-f53hilbI.js.map → WorkflowExecutionPage-BI9NzeaH.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-IDjTRfQ8.js → WorkflowPill-BuNkWUVM.js} +2 -2
- package/dashboard/dist/assets/WorkflowPill-BuNkWUVM.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-DM9Xe665.js → WorkflowsDashboard-CvWSgQdE.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DM9Xe665.js.map → WorkflowsDashboard-CvWSgQdE.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CUGSrx9N.js → WorkflowsOverview-CjBWlwlH.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CUGSrx9N.js.map → WorkflowsOverview-CjBWlwlH.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B1_yqxAw.js → YamlWorkflowDetailPage-Ds5o1UK9.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B1_yqxAw.js.map → YamlWorkflowDetailPage-Ds5o1UK9.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-NtGvqiu7.js → YamlWorkflowsPage-ChwvZS4Z.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-NtGvqiu7.js.map → YamlWorkflowsPage-ChwvZS4Z.js.map} +1 -1
- package/dashboard/dist/assets/{agents-tML6AjDj.js → agents-A1U43853.js} +2 -2
- package/dashboard/dist/assets/{agents-tML6AjDj.js.map → agents-A1U43853.js.map} +1 -1
- package/dashboard/dist/assets/{bots-Cb0WTwhY.js → bots-CyzN6Uxi.js} +2 -2
- package/dashboard/dist/assets/{bots-Cb0WTwhY.js.map → bots-CyzN6Uxi.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-BGZogsi_.js → capabilities-BE1X5FVI.js} +2 -2
- package/dashboard/dist/assets/{capabilities-BGZogsi_.js.map → capabilities-BE1X5FVI.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-B7VJTlZD.js → controlplane-BrwK_MT8.js} +2 -2
- package/dashboard/dist/assets/{controlplane-B7VJTlZD.js.map → controlplane-BrwK_MT8.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-Bl-SQV_u.js → escalation-DDdt7UdU.js} +2 -2
- package/dashboard/dist/assets/{escalation-Bl-SQV_u.js.map → escalation-DDdt7UdU.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-BlJOswRx.js → escalation-columns-D_zFY1zF.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-BlJOswRx.js.map → escalation-columns-D_zFY1zF.js.map} +1 -1
- package/dashboard/dist/assets/{index-DcbEIXit.js → index-6MALVAEY.js} +2 -2
- package/dashboard/dist/assets/{index-DcbEIXit.js.map → index-6MALVAEY.js.map} +1 -1
- package/dashboard/dist/assets/{index-CBv3UxF2.js → index-BNWKksZ6.js} +2 -2
- package/dashboard/dist/assets/{index-CBv3UxF2.js.map → index-BNWKksZ6.js.map} +1 -1
- package/dashboard/dist/assets/{index-DfDfJivY.js → index-BtrNxebh.js} +2 -2
- package/dashboard/dist/assets/{index-DfDfJivY.js.map → index-BtrNxebh.js.map} +1 -1
- package/dashboard/dist/assets/{index-BorpZbZA.js → index-C-rJQ-vB.js} +2 -2
- package/dashboard/dist/assets/{index-BorpZbZA.js.map → index-C-rJQ-vB.js.map} +1 -1
- package/dashboard/dist/assets/{index-Il_g7kw5.js → index-CGUBekEU.js} +2 -2
- package/dashboard/dist/assets/{index-Il_g7kw5.js.map → index-CGUBekEU.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dgw1KKpq.js → index-CKXSKsVZ.js} +4 -4
- package/dashboard/dist/assets/{index-Dgw1KKpq.js.map → index-CKXSKsVZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-Wny0bRgq.css → index-CZ6-l8_0.css} +1 -1
- package/dashboard/dist/assets/{index--WeZDfeN.js → index-D2dDPGNk.js} +2 -2
- package/dashboard/dist/assets/{index--WeZDfeN.js.map → index-D2dDPGNk.js.map} +1 -1
- package/dashboard/dist/assets/{index-0o71lRuY.js → index-D4U--3Xo.js} +2 -2
- package/dashboard/dist/assets/{index-0o71lRuY.js.map → index-D4U--3Xo.js.map} +1 -1
- package/dashboard/dist/assets/{index-e97M6GoI.js → index-D4gAsCGh.js} +2 -2
- package/dashboard/dist/assets/{index-e97M6GoI.js.map → index-D4gAsCGh.js.map} +1 -1
- package/dashboard/dist/assets/{index-SCrD3l-K.js → index-DEXouwBQ.js} +2 -2
- package/dashboard/dist/assets/{index-SCrD3l-K.js.map → index-DEXouwBQ.js.map} +1 -1
- package/dashboard/dist/assets/{index-3A_5CL81.js → index-NnGsWGNS.js} +2 -2
- package/dashboard/dist/assets/{index-3A_5CL81.js.map → index-NnGsWGNS.js.map} +1 -1
- package/dashboard/dist/assets/index-U27KBbTV.js +9 -0
- package/dashboard/dist/assets/index-U27KBbTV.js.map +1 -0
- package/dashboard/dist/assets/index-fwHQSJCp.js +6 -0
- package/dashboard/dist/assets/index-fwHQSJCp.js.map +1 -0
- package/dashboard/dist/assets/{knowledge-CDzw4E6q.js → knowledge-B3ZIRTeu.js} +2 -2
- package/dashboard/dist/assets/{knowledge-CDzw4E6q.js.map → knowledge-B3ZIRTeu.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-D_-NQtnG.js → mcp-BJjWiHdc.js} +2 -2
- package/dashboard/dist/assets/{mcp-D_-NQtnG.js.map → mcp-BJjWiHdc.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BqZXdf9A.js → mcp-query-ksjBztwq.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BqZXdf9A.js.map → mcp-query-ksjBztwq.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-CNLhgj8b.js → pipelines-DT1astmP.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CNLhgj8b.js.map → pipelines-DT1astmP.js.map} +1 -1
- package/dashboard/dist/assets/{roles-CFD2eYap.js → roles-UboiQxXT.js} +2 -2
- package/dashboard/dist/assets/roles-UboiQxXT.js.map +1 -0
- package/dashboard/dist/assets/{tasks-DcSvF636.js → tasks-aL0K8WW3.js} +2 -2
- package/dashboard/dist/assets/{tasks-DcSvF636.js.map → tasks-aL0K8WW3.js.map} +1 -1
- package/dashboard/dist/assets/{topics-DI8TFSZv.js → topics-CZO06VJl.js} +2 -2
- package/dashboard/dist/assets/{topics-DI8TFSZv.js.map → topics-CZO06VJl.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-DcIgMDh3.js → useEventHooks-DlWkui_n.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-DcIgMDh3.js.map → useEventHooks-DlWkui_n.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-C88ZxGoX.js → useNamespace-BBzCmCIN.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-C88ZxGoX.js.map → useNamespace-BBzCmCIN.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-D2-yVjJ7.js → useYamlActivityEvents-ZS6wRSSc.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-D2-yVjJ7.js.map → useYamlActivityEvents-ZS6wRSSc.js.map} +1 -1
- package/dashboard/dist/assets/{users-C0WV36X8.js → users-Dzh04pRB.js} +2 -2
- package/dashboard/dist/assets/{users-C0WV36X8.js.map → users-Dzh04pRB.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-CEWCGPeJ.js → vendor-icons-BaQqUm-e.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-CEWCGPeJ.js.map → vendor-icons-BaQqUm-e.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-CkPv7xk-.js → workflows-BTkHs6VT.js} +2 -2
- package/dashboard/dist/assets/workflows-BTkHs6VT.js.map +1 -0
- package/dashboard/dist/assets/{yaml-workflows-BjrMQLTK.js → yaml-workflows-Df8u3lgx.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-BjrMQLTK.js.map → yaml-workflows-Df8u3lgx.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +6 -6
- package/docs/api/http/roles.md +6 -0
- package/docs/api/http/workflows.md +2 -1
- package/docs/api/mcp/admin.md +15 -7
- package/docs/api/sdk/escalations.md +5 -3
- package/docs/api/sdk/roles.md +7 -1
- package/docs/api/sdk/workflows.md +3 -2
- package/docs/architecture.md +2 -2
- package/docs/dashboard.md +11 -9
- package/docs/data.md +2 -0
- package/docs/iam.md +3 -3
- package/docs/operations.md +16 -3
- package/docs/workflows.md +7 -5
- package/package.json +2 -2
- package/dashboard/dist/assets/OperationsPage-BCr21p8I.js +0 -2
- package/dashboard/dist/assets/OperationsPage-BCr21p8I.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-DS37tJhq.js +0 -11
- package/dashboard/dist/assets/RoleDetailPage-DS37tJhq.js.map +0 -1
- package/dashboard/dist/assets/WorkflowPill-IDjTRfQ8.js.map +0 -1
- package/dashboard/dist/assets/index-D0-8byNu.js +0 -6
- package/dashboard/dist/assets/index-D0-8byNu.js.map +0 -1
- package/dashboard/dist/assets/index-TiKh2Ctj.js +0 -9
- package/dashboard/dist/assets/index-TiKh2Ctj.js.map +0 -1
- package/dashboard/dist/assets/roles-CFD2eYap.js.map +0 -1
- package/dashboard/dist/assets/workflows-CkPv7xk-.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-SCrD3l-K.js","sources":["../../src/pages/admin/streams/StreamMessagesPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { X } from 'lucide-react';\nimport { useStreamMessages, type StreamMessage } from '../../../api/stream-messages';\nimport { useFilterParams } from '../../../hooks/useFilterParams';\nimport { useNamespace } from '../../../hooks/useNamespace';\nimport { buildApiPath } from '../../../lib/api-path';\nimport { DataTable, type Column } from '../../../components/common/data/DataTable';\nimport { StickyPagination } from '../../../components/common/data/StickyPagination';\nimport { FilterBar, FilterSelect, FilterInput } from '../../../components/common/data/FilterBar';\nimport { TimestampCell } from '../../../components/common/display/TimestampCell';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { ListToolbar } from '../../../components/common/data/ListToolbar';\nimport { StreamMessageDetail } from './StreamMessageDetail';\nimport { STATUS_DOT, STATUS_LABEL, STATUS_OPTIONS, SOURCE_OPTIONS, SOURCE_BADGE } from './constants';\n\nfunction truncateMiddle(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor((max - 1) / 2);\n return `${str.slice(0, half)}…${str.slice(-half)}`;\n}\n\nexport function StreamMessagesPage() {\n const { namespace, available } = useNamespace('namespace');\n\n const { filters, setFilter, pagination, sort, setSort } = useFilterParams({\n filters: { namespace: '', source: 'worker', status: '', stream_name: '', msg_type: '', topic: '', workflow_name: '', jid: '', aid: '' },\n });\n\n const [selected, setSelected] = useState<StreamMessage | null>(null);\n\n const namespaceOptions = useMemo(\n () => available.map((id) => ({ value: id, label: id })),\n [available],\n );\n\n const { data, isLoading, refetch, isFetching } = useStreamMessages({\n namespace: filters.namespace || namespace,\n source: (filters.source as 'engine' | 'worker') || 'worker',\n limit: pagination.pageSize,\n offset: pagination.offset,\n sort_by: sort.sort_by || 'created_at',\n order: sort.order || 'desc',\n status: (filters.status as any) || undefined,\n stream_name: filters.stream_name || undefined,\n msg_type: filters.msg_type || undefined,\n topic: filters.topic || undefined,\n workflow_name: filters.workflow_name || undefined,\n jid: filters.jid || undefined,\n aid: filters.aid || undefined,\n });\n\n const messages = data?.messages ?? [];\n const total = data?.total ?? 0;\n\n const activeMessage = useMemo(() => {\n if (!selected) return null;\n return messages.find((m) => m.id === selected.id && m.source === selected.source) ?? selected;\n }, [messages, selected]);\n\n const panelOpen = !!activeMessage;\n\n const columns: Column<StreamMessage>[] = [\n {\n key: 'status',\n label: 'Status',\n render: (row) => (\n <div className=\"flex items-center gap-2\">\n <span className={`w-2 h-2 rounded-full shrink-0 ${STATUS_DOT[row.status]}`} />\n <span className=\"text-xs\">{STATUS_LABEL[row.status]}</span>\n </div>\n ),\n className: 'w-28',\n },\n {\n key: 'source',\n label: 'Source',\n render: (row) => <span className={SOURCE_BADGE}>{row.source}</span>,\n className: 'w-20',\n },\n {\n key: 'stream_name',\n label: 'Stream',\n sortable: true,\n render: (row) => (\n <span className=\"font-mono text-xs text-text-secondary truncate block max-w-[240px]\" title={row.stream_name}>\n {row.stream_name}\n </span>\n ),\n },\n {\n key: 'msg_type',\n label: 'Type',\n render: (row) => (\n <span className=\"text-xs text-text-secondary\">{row.msg_type || '—'}</span>\n ),\n className: 'w-24',\n },\n {\n key: 'created_at',\n label: 'Created',\n sortable: true,\n render: (row) => <TimestampCell date={row.created_at} />,\n className: 'w-44',\n },\n {\n key: 'reserved_at',\n label: 'Reserved',\n render: (row) => row.reserved_at ? <TimestampCell date={row.reserved_at} /> : <span className=\"text-xs text-text-tertiary\">—</span>,\n className: 'w-44',\n },\n {\n key: 'expired_at',\n label: 'Processed',\n render: (row) => row.expired_at ? <TimestampCell date={row.expired_at} /> : <span className=\"text-xs text-text-tertiary\">—</span>,\n className: 'w-44',\n },\n {\n key: 'priority',\n label: 'Pri',\n sortable: true,\n render: (row) => <span className=\"text-xs text-text-secondary\">{row.priority}</span>,\n className: 'w-12 text-right',\n },\n {\n key: 'retry_attempt',\n label: 'Retries',\n render: (row) => (\n <span className=\"text-xs text-text-secondary\">\n {row.retry_attempt}/{row.max_retry_attempts}\n </span>\n ),\n className: 'w-16',\n },\n ];\n\n return (\n <div>\n <PageHeader title=\"Messages\" docsHash=\"#docs:dashboard.md:messages\" />\n\n <FilterBar actions={\n <ListToolbar\n onRefresh={() => refetch()}\n isFetching={isFetching}\n apiPath={buildApiPath('/controlplane/stream-messages', {\n namespace: filters.namespace || namespace,\n source: filters.source || 'worker',\n limit: pagination.pageSize,\n offset: pagination.offset,\n sort_by: sort.sort_by || 'created_at',\n order: sort.order || 'desc',\n status: filters.status || undefined,\n stream_name: filters.stream_name || undefined,\n msg_type: filters.msg_type || undefined,\n topic: filters.topic || undefined,\n workflow_name: filters.workflow_name || undefined,\n jid: filters.jid || undefined,\n aid: filters.aid || undefined,\n })}\n />\n }>\n <FilterSelect\n label=\"Namespace\"\n value={filters.namespace}\n onChange={(v) => setFilter('namespace', v)}\n options={namespaceOptions}\n required\n />\n <FilterSelect\n label=\"Source\"\n value={filters.source}\n onChange={(v) => setFilter('source', v)}\n options={SOURCE_OPTIONS}\n required\n />\n <FilterSelect\n label=\"Status\"\n value={filters.status}\n onChange={(v) => setFilter('status', v)}\n options={STATUS_OPTIONS}\n />\n <FilterInput\n label=\"Stream\"\n value={filters.stream_name}\n onChange={(v) => setFilter('stream_name', v)}\n placeholder=\"Filter by stream name…\"\n />\n <FilterInput\n label=\"Job ID\"\n value={filters.jid}\n onChange={(v) => setFilter('jid', v)}\n placeholder=\"Filter by jid…\"\n />\n {/* Active dimension filter pills — inline in the sticky bar */}\n {[\n { key: 'topic', label: 'Topic', value: filters.topic },\n { key: 'workflow_name', label: 'Workflow', value: filters.workflow_name },\n { key: 'aid', label: 'Activity', value: filters.aid },\n { key: 'msg_type', label: 'Type', value: filters.msg_type },\n ].filter((f) => f.value).map((f) => (\n <button\n key={f.key}\n onClick={() => setFilter(f.key as any, '')}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-[10px] font-mono rounded-full bg-accent/15 text-accent hover:bg-accent/25 transition-colors\"\n title={`Clear ${f.label} filter: ${f.value}`}\n >\n {f.label}: {truncateMiddle(f.value, 12)}\n <X className=\"w-2.5 h-2.5\" />\n </button>\n ))}\n </FilterBar>\n\n <DataTable\n columns={columns}\n data={messages}\n keyFn={(row) => `${row.source}:${row.id}`}\n isLoading={isLoading}\n emptyMessage=\"No stream messages found\"\n onRowClick={(row) => setSelected(row)}\n activeRowKey={activeMessage ? `${activeMessage.source}:${activeMessage.id}` : null}\n sort={sort}\n onSort={setSort}\n />\n\n <StickyPagination\n page={pagination.page}\n totalPages={pagination.totalPages(total)}\n onPageChange={pagination.setPage}\n total={total}\n pageSize={pagination.pageSize}\n onPageSizeChange={pagination.setPageSize}\n />\n\n {/* Detail panel — portaled to body so fixed positioning works */}\n {panelOpen && createPortal(\n <div className=\"fixed right-0 bottom-0 w-[400px] z-40 border-l border-surface-border bg-surface overflow-y-auto shadow-lg\" style={{ top: '3.5rem' }}>\n <div className=\"sticky top-0 z-10 flex items-center justify-between px-4 py-3 bg-surface border-b border-surface-border/50\">\n <span className=\"text-xs font-medium text-text-primary\">Message Detail</span>\n <button\n onClick={() => setSelected(null)}\n className=\"p-1 rounded hover:bg-surface-hover text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Close\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n <div className=\"px-4 py-4\">\n <StreamMessageDetail\n message={activeMessage}\n filters={{\n onFilterStatus: (v) => setFilter('status', v),\n onFilterStreamName: (v) => setFilter('stream_name', v),\n onFilterMsgType: (v) => setFilter('msg_type', v),\n onFilterTopic: (v) => setFilter('topic', v),\n onFilterWorkflow: (v) => setFilter('workflow_name', v),\n onFilterJid: (v) => setFilter('jid', v),\n onFilterAid: (v) => setFilter('aid', v),\n }}\n />\n </div>\n </div>,\n document.body,\n )}\n </div>\n );\n}\n"],"names":["truncateMiddle","str","max","half","StreamMessagesPage","namespace","available","useNamespace","filters","setFilter","pagination","sort","setSort","useFilterParams","selected","setSelected","useState","namespaceOptions","useMemo","id","data","isLoading","refetch","isFetching","useStreamMessages","messages","total","activeMessage","m","panelOpen","columns","row","jsxs","jsx","STATUS_DOT","STATUS_LABEL","SOURCE_BADGE","TimestampCell","PageHeader","FilterBar","ListToolbar","buildApiPath","FilterSelect","v","SOURCE_OPTIONS","STATUS_OPTIONS","FilterInput","f","X","DataTable","StickyPagination","createPortal","StreamMessageDetail"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DEXouwBQ.js","sources":["../../src/pages/admin/streams/StreamMessagesPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { X } from 'lucide-react';\nimport { useStreamMessages, type StreamMessage } from '../../../api/stream-messages';\nimport { useFilterParams } from '../../../hooks/useFilterParams';\nimport { useNamespace } from '../../../hooks/useNamespace';\nimport { buildApiPath } from '../../../lib/api-path';\nimport { DataTable, type Column } from '../../../components/common/data/DataTable';\nimport { StickyPagination } from '../../../components/common/data/StickyPagination';\nimport { FilterBar, FilterSelect, FilterInput } from '../../../components/common/data/FilterBar';\nimport { TimestampCell } from '../../../components/common/display/TimestampCell';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { ListToolbar } from '../../../components/common/data/ListToolbar';\nimport { StreamMessageDetail } from './StreamMessageDetail';\nimport { STATUS_DOT, STATUS_LABEL, STATUS_OPTIONS, SOURCE_OPTIONS, SOURCE_BADGE } from './constants';\n\nfunction truncateMiddle(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor((max - 1) / 2);\n return `${str.slice(0, half)}…${str.slice(-half)}`;\n}\n\nexport function StreamMessagesPage() {\n const { namespace, available } = useNamespace('namespace');\n\n const { filters, setFilter, pagination, sort, setSort } = useFilterParams({\n filters: { namespace: '', source: 'worker', status: '', stream_name: '', msg_type: '', topic: '', workflow_name: '', jid: '', aid: '' },\n });\n\n const [selected, setSelected] = useState<StreamMessage | null>(null);\n\n const namespaceOptions = useMemo(\n () => available.map((id) => ({ value: id, label: id })),\n [available],\n );\n\n const { data, isLoading, refetch, isFetching } = useStreamMessages({\n namespace: filters.namespace || namespace,\n source: (filters.source as 'engine' | 'worker') || 'worker',\n limit: pagination.pageSize,\n offset: pagination.offset,\n sort_by: sort.sort_by || 'created_at',\n order: sort.order || 'desc',\n status: (filters.status as any) || undefined,\n stream_name: filters.stream_name || undefined,\n msg_type: filters.msg_type || undefined,\n topic: filters.topic || undefined,\n workflow_name: filters.workflow_name || undefined,\n jid: filters.jid || undefined,\n aid: filters.aid || undefined,\n });\n\n const messages = data?.messages ?? [];\n const total = data?.total ?? 0;\n\n const activeMessage = useMemo(() => {\n if (!selected) return null;\n return messages.find((m) => m.id === selected.id && m.source === selected.source) ?? selected;\n }, [messages, selected]);\n\n const panelOpen = !!activeMessage;\n\n const columns: Column<StreamMessage>[] = [\n {\n key: 'status',\n label: 'Status',\n render: (row) => (\n <div className=\"flex items-center gap-2\">\n <span className={`w-2 h-2 rounded-full shrink-0 ${STATUS_DOT[row.status]}`} />\n <span className=\"text-xs\">{STATUS_LABEL[row.status]}</span>\n </div>\n ),\n className: 'w-28',\n },\n {\n key: 'source',\n label: 'Source',\n render: (row) => <span className={SOURCE_BADGE}>{row.source}</span>,\n className: 'w-20',\n },\n {\n key: 'stream_name',\n label: 'Stream',\n sortable: true,\n render: (row) => (\n <span className=\"font-mono text-xs text-text-secondary truncate block max-w-[240px]\" title={row.stream_name}>\n {row.stream_name}\n </span>\n ),\n },\n {\n key: 'msg_type',\n label: 'Type',\n render: (row) => (\n <span className=\"text-xs text-text-secondary\">{row.msg_type || '—'}</span>\n ),\n className: 'w-24',\n },\n {\n key: 'created_at',\n label: 'Created',\n sortable: true,\n render: (row) => <TimestampCell date={row.created_at} />,\n className: 'w-44',\n },\n {\n key: 'reserved_at',\n label: 'Reserved',\n render: (row) => row.reserved_at ? <TimestampCell date={row.reserved_at} /> : <span className=\"text-xs text-text-tertiary\">—</span>,\n className: 'w-44',\n },\n {\n key: 'expired_at',\n label: 'Processed',\n render: (row) => row.expired_at ? <TimestampCell date={row.expired_at} /> : <span className=\"text-xs text-text-tertiary\">—</span>,\n className: 'w-44',\n },\n {\n key: 'priority',\n label: 'Pri',\n sortable: true,\n render: (row) => <span className=\"text-xs text-text-secondary\">{row.priority}</span>,\n className: 'w-12 text-right',\n },\n {\n key: 'retry_attempt',\n label: 'Retries',\n render: (row) => (\n <span className=\"text-xs text-text-secondary\">\n {row.retry_attempt}/{row.max_retry_attempts}\n </span>\n ),\n className: 'w-16',\n },\n ];\n\n return (\n <div>\n <PageHeader title=\"Messages\" docsHash=\"#docs:dashboard.md:messages\" />\n\n <FilterBar actions={\n <ListToolbar\n onRefresh={() => refetch()}\n isFetching={isFetching}\n apiPath={buildApiPath('/controlplane/stream-messages', {\n namespace: filters.namespace || namespace,\n source: filters.source || 'worker',\n limit: pagination.pageSize,\n offset: pagination.offset,\n sort_by: sort.sort_by || 'created_at',\n order: sort.order || 'desc',\n status: filters.status || undefined,\n stream_name: filters.stream_name || undefined,\n msg_type: filters.msg_type || undefined,\n topic: filters.topic || undefined,\n workflow_name: filters.workflow_name || undefined,\n jid: filters.jid || undefined,\n aid: filters.aid || undefined,\n })}\n />\n }>\n <FilterSelect\n label=\"Namespace\"\n value={filters.namespace}\n onChange={(v) => setFilter('namespace', v)}\n options={namespaceOptions}\n required\n />\n <FilterSelect\n label=\"Source\"\n value={filters.source}\n onChange={(v) => setFilter('source', v)}\n options={SOURCE_OPTIONS}\n required\n />\n <FilterSelect\n label=\"Status\"\n value={filters.status}\n onChange={(v) => setFilter('status', v)}\n options={STATUS_OPTIONS}\n />\n <FilterInput\n label=\"Stream\"\n value={filters.stream_name}\n onChange={(v) => setFilter('stream_name', v)}\n placeholder=\"Filter by stream name…\"\n />\n <FilterInput\n label=\"Job ID\"\n value={filters.jid}\n onChange={(v) => setFilter('jid', v)}\n placeholder=\"Filter by jid…\"\n />\n {/* Active dimension filter pills — inline in the sticky bar */}\n {[\n { key: 'topic', label: 'Topic', value: filters.topic },\n { key: 'workflow_name', label: 'Workflow', value: filters.workflow_name },\n { key: 'aid', label: 'Activity', value: filters.aid },\n { key: 'msg_type', label: 'Type', value: filters.msg_type },\n ].filter((f) => f.value).map((f) => (\n <button\n key={f.key}\n onClick={() => setFilter(f.key as any, '')}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-[10px] font-mono rounded-full bg-accent/15 text-accent hover:bg-accent/25 transition-colors\"\n title={`Clear ${f.label} filter: ${f.value}`}\n >\n {f.label}: {truncateMiddle(f.value, 12)}\n <X className=\"w-2.5 h-2.5\" />\n </button>\n ))}\n </FilterBar>\n\n <DataTable\n columns={columns}\n data={messages}\n keyFn={(row) => `${row.source}:${row.id}`}\n isLoading={isLoading}\n emptyMessage=\"No stream messages found\"\n onRowClick={(row) => setSelected(row)}\n activeRowKey={activeMessage ? `${activeMessage.source}:${activeMessage.id}` : null}\n sort={sort}\n onSort={setSort}\n />\n\n <StickyPagination\n page={pagination.page}\n totalPages={pagination.totalPages(total)}\n onPageChange={pagination.setPage}\n total={total}\n pageSize={pagination.pageSize}\n onPageSizeChange={pagination.setPageSize}\n />\n\n {/* Detail panel — portaled to body so fixed positioning works */}\n {panelOpen && createPortal(\n <div className=\"fixed right-0 bottom-0 w-[400px] z-40 border-l border-surface-border bg-surface overflow-y-auto shadow-lg\" style={{ top: '3.5rem' }}>\n <div className=\"sticky top-0 z-10 flex items-center justify-between px-4 py-3 bg-surface border-b border-surface-border/50\">\n <span className=\"text-xs font-medium text-text-primary\">Message Detail</span>\n <button\n onClick={() => setSelected(null)}\n className=\"p-1 rounded hover:bg-surface-hover text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Close\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n <div className=\"px-4 py-4\">\n <StreamMessageDetail\n message={activeMessage}\n filters={{\n onFilterStatus: (v) => setFilter('status', v),\n onFilterStreamName: (v) => setFilter('stream_name', v),\n onFilterMsgType: (v) => setFilter('msg_type', v),\n onFilterTopic: (v) => setFilter('topic', v),\n onFilterWorkflow: (v) => setFilter('workflow_name', v),\n onFilterJid: (v) => setFilter('jid', v),\n onFilterAid: (v) => setFilter('aid', v),\n }}\n />\n </div>\n </div>,\n document.body,\n )}\n </div>\n );\n}\n"],"names":["truncateMiddle","str","max","half","StreamMessagesPage","namespace","available","useNamespace","filters","setFilter","pagination","sort","setSort","useFilterParams","selected","setSelected","useState","namespaceOptions","useMemo","id","data","isLoading","refetch","isFetching","useStreamMessages","messages","total","activeMessage","m","panelOpen","columns","row","jsxs","jsx","STATUS_DOT","STATUS_LABEL","SOURCE_BADGE","TimestampCell","PageHeader","FilterBar","ListToolbar","buildApiPath","FilterSelect","v","SOURCE_OPTIONS","STATUS_OPTIONS","FilterInput","f","X","DataTable","StickyPagination","createPortal","StreamMessageDetail"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as k,u as R,b as O,j as e,a as d}from"./vendor-query-B2UbickB.js";import{P as F}from"./PageHeader-BhaWTWcM.js";import{d as b}from"./index-Dgw1KKpq.js";import{M}from"./Modal-CSrxpXeM.js";import{R as g,C}from"./constants-BHkpVaqx.js";import{at as E,am as T,ad as W,a9 as j,p as I,a6 as D}from"./vendor-icons-CEWCGPeJ.js";import"./vendor-react-CXumBFUA.js";function $(){return R({queryKey:["maintenance"],queryFn:()=>b("/config/maintenance")})}function _(){const t=O();return k({mutationFn:r=>b("/config/maintenance",{method:"PUT",body:JSON.stringify(r)}),onSuccess:()=>{t.invalidateQueries({queryKey:["maintenance"]})}})}function q(){return k({mutationFn:t=>b("/dba/prune",{method:"POST",body:JSON.stringify(t)})})}function v({checked:t,onToggle:r,label:a,hint:s,safety:i,value:l,onChange:c}){const n=i==="safe"?W:i==="moderate"?E:T,o=i==="safe"?"text-status-success":i==="moderate"?"text-status-warning":"text-status-error",m=i==="safe"?"Safe":i==="moderate"?"Careful":"Permanent";return e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("label",{className:"flex items-center gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"checkbox",checked:t,onChange:x=>r(x.target.checked),className:"w-4 h-4 rounded border-border accent-accent shrink-0"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:a}),e.jsxs("span",{className:`flex items-center gap-0.5 text-[9px] ${o}`,children:[e.jsx(n,{className:"w-3 h-3",strokeWidth:1.5}),m]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:s})]})]}),e.jsx("select",{value:l,onChange:x=>c(x.target.value),disabled:!t,className:`select text-xs w-36 transition-opacity ${t?"":"opacity-40 cursor-not-allowed"}`,children:g.map(x=>e.jsx("option",{value:x.value,children:x.label},x.value))})]})}function L(){return e.jsxs("div",{className:"flex items-start gap-2 mt-2 px-3 py-2 rounded bg-surface-sunken",children:[e.jsx(j,{className:"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5"}),e.jsx("p",{className:"text-[10px] text-text-tertiary leading-relaxed",children:"Stripping preserves workflow results and timeline data needed for the execution detail view. Transient jobs are internal bookkeeping that accumulates over time."})]})}const f={pruneJobs:!0,expire:"30 days",engineStreams:!0,engineStreamsExpire:"1 day",workerStreams:!0,workerStreamsExpire:"90 days",stripAttributes:!1,pruneTransient:!1};function N(t){return t.pruneJobs||t.engineStreams||t.workerStreams||t.stripAttributes||t.pruneTransient}function P({fields:t,onChange:r}){const a=(s,i)=>r({...t,[s]:i});return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Stream Messages"}),e.jsx("p",{className:"text-[10px] text-text-tertiary mb-4",children:"Processed routing messages. Already consumed — safe to remove after a short retention window."}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(v,{checked:t.engineStreams,onToggle:s=>a("engineStreams",s),label:"Engine messages",hint:"Internal orchestration signals",safety:"safe",value:t.engineStreamsExpire,onChange:s=>a("engineStreamsExpire",s)}),e.jsx(v,{checked:t.workerStreams,onToggle:s=>a("workerStreams",s),label:"Worker messages",hint:"Activity dispatch and response payloads",safety:"safe",value:t.workerStreamsExpire,onChange:s=>a("workerStreamsExpire",s)})]})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Completed Workflows"}),e.jsx("p",{className:"text-[10px] text-text-tertiary mb-4",children:"Choose how to handle completed workflow records. Reducing strips step-level detail but keeps the workflow and its results. Deleting removes the record entirely."}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:!t.stripAttributes&&!t.pruneJobs,onChange:()=>r({...t,stripAttributes:!1,pruneJobs:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Keep as-is"}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"No changes to completed workflow records."})]})]}),e.jsxs("div",{className:"flex items-start gap-2.5",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:t.stripAttributes&&!t.pruneJobs,onChange:()=>r({...t,stripAttributes:!0,pruneJobs:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Reduce completed workflows"}),e.jsxs("span",{className:"flex items-center gap-0.5 text-[9px] text-status-warning",children:[e.jsx(E,{className:"w-3 h-3",strokeWidth:1.5}),"Careful"]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"Strips step-level execution detail (activity inputs/outputs, internal state). Workflow results and timeline are preserved."})]})]}),e.jsx("select",{value:t.expire,onChange:s=>a("expire",s.target.value),disabled:!t.stripAttributes||t.pruneJobs,className:`select text-xs w-36 transition-opacity mt-0.5 ${!t.stripAttributes||t.pruneJobs?"opacity-40 cursor-not-allowed":""}`,children:g.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))})]}),e.jsxs("div",{className:"flex items-start gap-2.5",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:t.pruneJobs,onChange:()=>r({...t,pruneJobs:!0,stripAttributes:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Delete completed workflows"}),e.jsxs("span",{className:"flex items-center gap-0.5 text-[9px] text-status-error",children:[e.jsx(T,{className:"w-3 h-3",strokeWidth:1.5}),"Permanent"]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"Permanently removes workflow records. Results, timeline, and all execution data are deleted and cannot be recovered."})]})]}),e.jsx("select",{value:t.expire,onChange:s=>a("expire",s.target.value),disabled:!t.pruneJobs,className:`select text-xs w-36 transition-opacity mt-0.5 ${t.pruneJobs?"":"opacity-40 cursor-not-allowed"}`,children:g.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))})]})]}),e.jsx(L,{})]})]})}function p({label:t,value:r}){return e.jsxs("div",{children:[e.jsx("p",{className:"text-lg font-light text-text-primary",children:r.toLocaleString()}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:t})]})}function U(){const t=q(),[r,a]=d.useState(f),[s,i]=d.useState(null),[l,c]=d.useState(!1),n=()=>{i(null),t.mutate({expire:r.expire,jobs:r.pruneJobs,engineStreams:r.engineStreams,engineStreamsExpire:r.engineStreamsExpire,workerStreams:r.workerStreams,workerStreamsExpire:r.workerStreamsExpire,attributes:r.stripAttributes,pruneTransient:r.pruneTransient},{onSuccess:o=>{i(o),c(!1)}})};return e.jsxs("div",{className:"space-y-8",children:[e.jsx(P,{fields:r,onChange:a}),e.jsxs("div",{className:"flex items-center justify-between pt-2 border-t border-surface-border",children:[e.jsx("p",{className:"text-[10px] text-text-tertiary",children:N(r)?"This action permanently deletes data and cannot be undone.":"Select at least one operation to enable pruning."}),e.jsx("button",{onClick:()=>c(!0),disabled:!N(r)||t.isPending,className:"bg-status-error text-white px-4 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity disabled:opacity-40 shrink-0",children:t.isPending?"Pruning...":"Prune Now"})]}),s&&e.jsxs("div",{className:"bg-surface-sunken rounded-md px-4 py-3",children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-2",children:"Results"}),e.jsxs("div",{className:"flex flex-wrap gap-6",children:[s.jobs!=null&&s.jobs>0&&e.jsx(p,{label:"Jobs deleted",value:s.jobs}),s.engineStreams!=null&&s.engineStreams>0&&e.jsx(p,{label:"Engine streams",value:s.engineStreams}),s.workerStreams!=null&&s.workerStreams>0&&e.jsx(p,{label:"Worker streams",value:s.workerStreams}),s.streams!=null&&s.engineStreams==null&&s.streams>0&&e.jsx(p,{label:"Streams",value:s.streams}),s.attributes!=null&&s.attributes>0&&e.jsx(p,{label:"Artifacts stripped",value:s.attributes}),s.transient!=null&&s.transient>0&&e.jsx(p,{label:"Transient deleted",value:s.transient}),s.marked!=null&&s.marked>0&&e.jsx(p,{label:"Marked pruned",value:s.marked}),Object.values(s).every(o=>!o||o===0)&&e.jsx("p",{className:"text-xs text-text-tertiary",children:"Nothing to prune within the selected retention windows."})]})]}),t.error&&e.jsx("p",{className:"text-xs text-status-error",children:t.error.message}),e.jsx(M,{open:l,onClose:()=>c(!1),title:"Confirm Prune",children:e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{className:"text-sm text-text-secondary",children:"This will permanently delete data. This action cannot be undone."}),e.jsxs("ul",{className:"text-xs text-text-secondary space-y-1 list-disc list-inside",children:[r.pruneJobs&&e.jsxs("li",{children:["Delete jobs older than ",r.expire]}),r.engineStreams&&e.jsxs("li",{children:["Delete engine streams older than ",r.engineStreamsExpire]}),r.workerStreams&&e.jsxs("li",{children:["Delete worker streams older than ",r.workerStreamsExpire]}),r.stripAttributes&&e.jsx("li",{children:"Strip execution artifacts from completed jobs"}),r.pruneTransient&&e.jsx("li",{children:"Delete transient (entity-less) jobs"})]}),e.jsxs("div",{className:"flex justify-end gap-3 pt-2",children:[e.jsx("button",{onClick:()=>c(!1),className:"btn-secondary text-xs",children:"Cancel"}),e.jsx("button",{onClick:n,className:"bg-status-error text-white px-3 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity",disabled:t.isPending,children:t.isPending?"Pruning...":"Confirm Prune"})]})]})})]})}function w(t){var c;if(!((c=t==null?void 0:t.rules)!=null&&c.length))return f;const r=t.rules,a=r.find(n=>n.target==="streams"&&n.action==="delete"),s=r.find(n=>n.target==="jobs"&&n.action==="delete"&&n.hasEntity===!1),i=r.find(n=>n.target==="jobs"&&n.action==="prune"&&n.hasEntity===!0),l=r.find(n=>n.target==="jobs"&&n.action==="delete"&&n.pruned===!0);return{pruneJobs:!!l,expire:(l==null?void 0:l.olderThan)??"180 days",engineStreams:!!a,engineStreamsExpire:(a==null?void 0:a.olderThan)??"1 day",workerStreams:!!a,workerStreamsExpire:(a==null?void 0:a.olderThan)??"90 days",stripAttributes:!!i,pruneTransient:!!s}}function H(t){const r=[];return t.engineStreams&&r.push({target:"streams",action:"delete",olderThan:t.engineStreamsExpire}),t.workerStreams&&t.workerStreamsExpire!==t.engineStreamsExpire&&r.push({target:"streams",action:"delete",olderThan:t.workerStreamsExpire}),t.pruneTransient&&r.push({target:"jobs",action:"delete",olderThan:t.expire,hasEntity:!1}),t.stripAttributes&&r.push({target:"jobs",action:"prune",olderThan:t.expire,hasEntity:!0}),t.pruneJobs&&r.push({target:"jobs",action:"delete",olderThan:t.expire,pruned:!0}),r}function S(t){var r;return((r=C.find(a=>a.value===t))==null?void 0:r.label)??""}function K(){const{data:t,isLoading:r}=$(),a=_(),[s,i]=d.useState(""),[l,c]=d.useState(f),[n,o]=d.useState(""),m=d.useCallback(()=>{t!=null&&t.config&&(i(t.config.schedule),c(w(t.config)),o(JSON.stringify({schedule:t.config.schedule,fields:w(t.config)})))},[t]);d.useEffect(()=>{m()},[m]);const x=JSON.stringify({schedule:s,fields:l}),y=n!==""&&x!==n,h=(t==null?void 0:t.active)??!1,J=()=>{const u=H(l);a.mutate({schedule:s,rules:u},{onSuccess:()=>{o(JSON.stringify({schedule:s,fields:l}))}})},A=()=>{m()};return r?e.jsx("div",{className:"animate-pulse h-40 bg-surface-sunken rounded"}):e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[1fr_auto] gap-10",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4",children:"Schedule"}),e.jsxs("div",{className:"flex items-end gap-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] text-text-tertiary mb-1",children:"Cron Expression"}),e.jsx("input",{type:"text",value:s,onChange:u=>i(u.target.value),placeholder:"0 2 * * *",className:"input text-xs font-mono w-48"})]}),e.jsx("div",{children:e.jsxs("div",{className:"flex items-center gap-1.5 h-8",children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${h?"bg-status-success":"bg-text-tertiary"}`}),e.jsx("span",{className:`text-xs ${h?"text-status-success":"text-text-tertiary"}`,children:h?"Active":"Inactive"})]})}),S(s)&&e.jsx("p",{className:"text-xs text-text-tertiary h-8 flex items-center",children:S(s)})]}),e.jsx("div",{className:"flex flex-wrap gap-1.5 mt-3",children:C.map(u=>e.jsx("button",{type:"button",onClick:()=>i(u.value),className:`px-2.5 py-1 text-[10px] rounded-full transition-colors ${s===u.value?"bg-accent/10 text-accent font-medium":"bg-surface-sunken text-text-tertiary hover:text-text-secondary"}`,children:u.label},u.value))})]}),e.jsxs("div",{className:"lg:w-72",children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4",children:"How It Works"}),e.jsxs("div",{className:"flex items-start gap-2 px-3 py-2 rounded bg-surface-sunken",children:[e.jsx(j,{className:"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5"}),e.jsx("p",{className:"text-[10px] text-text-tertiary leading-relaxed",children:"Rules execute sequentially on each cron cycle. Engine streams can be pruned aggressively since they only contain internal routing data. Worker streams should be retained longer to preserve execution playback and input enrichment."})]})]})]}),e.jsx(P,{fields:l,onChange:c}),e.jsxs("div",{className:"flex items-center justify-between pt-2 border-t border-surface-border",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[y&&e.jsx("button",{onClick:A,className:"text-[10px] text-text-tertiary hover:text-text-primary transition-colors",children:"Revert changes"}),a.error&&e.jsx("p",{className:"text-xs text-status-error",children:a.error.message})]}),e.jsx("button",{onClick:J,disabled:!y||!s.trim()||a.isPending,className:"btn-primary text-xs disabled:opacity-40 shrink-0",children:a.isPending?"Saving...":"Save Schedule"})]})]})}function Q({mode:t,onChange:r}){const a=(s,i,l)=>e.jsxs("button",{onClick:()=>r(s),className:`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md transition-colors ${t===s?"bg-accent/10 text-accent font-medium":"text-text-tertiary hover:text-text-secondary hover:bg-surface-hover"}`,children:[i,l]});return e.jsxs("div",{className:"flex gap-1 p-0.5 bg-surface-sunken rounded-lg w-fit",children:[a("prune",e.jsx(I,{className:"w-3.5 h-3.5"}),"Prune Now"),a("schedule",e.jsx(D,{className:"w-3.5 h-3.5"}),"Schedule")]})}function ee(){const[t,r]=d.useState("schedule");return e.jsxs("div",{children:[e.jsx(F,{title:"DB Maintenance",docsHash:"#docs:dashboard.md:db-maintenance",actions:e.jsx(Q,{mode:t,onChange:r})}),e.jsxs("div",{className:"flex items-start gap-2 px-4 py-3 mb-8 rounded-md bg-accent/5 border border-accent/10",children:[e.jsx(j,{className:"w-4 h-4 text-accent shrink-0 mt-0.5"}),e.jsx("p",{className:"text-xs text-text-secondary leading-relaxed",children:"Workflow data grows as jobs complete. Operations are ordered safest-first: stream messages are always safe to prune, workflow reduction preserves results, and deletion is permanent."})]}),t==="prune"?e.jsx(U,{}):e.jsx(K,{})]})}export{ee as MaintenancePage};
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{c as k,u as R,b as O,j as e,a as d}from"./vendor-query-B2UbickB.js";import{P as F}from"./PageHeader-CFta2VaS.js";import{d as b}from"./index-CKXSKsVZ.js";import{M}from"./Modal-CSrxpXeM.js";import{R as g,C}from"./constants-BHkpVaqx.js";import{at as E,am as T,ad as W,a9 as j,p as I,a6 as D}from"./vendor-icons-BaQqUm-e.js";import"./vendor-react-CXumBFUA.js";function $(){return R({queryKey:["maintenance"],queryFn:()=>b("/config/maintenance")})}function _(){const t=O();return k({mutationFn:r=>b("/config/maintenance",{method:"PUT",body:JSON.stringify(r)}),onSuccess:()=>{t.invalidateQueries({queryKey:["maintenance"]})}})}function q(){return k({mutationFn:t=>b("/dba/prune",{method:"POST",body:JSON.stringify(t)})})}function v({checked:t,onToggle:r,label:a,hint:s,safety:i,value:l,onChange:c}){const n=i==="safe"?W:i==="moderate"?E:T,o=i==="safe"?"text-status-success":i==="moderate"?"text-status-warning":"text-status-error",m=i==="safe"?"Safe":i==="moderate"?"Careful":"Permanent";return e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("label",{className:"flex items-center gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"checkbox",checked:t,onChange:x=>r(x.target.checked),className:"w-4 h-4 rounded border-border accent-accent shrink-0"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:a}),e.jsxs("span",{className:`flex items-center gap-0.5 text-[9px] ${o}`,children:[e.jsx(n,{className:"w-3 h-3",strokeWidth:1.5}),m]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:s})]})]}),e.jsx("select",{value:l,onChange:x=>c(x.target.value),disabled:!t,className:`select text-xs w-36 transition-opacity ${t?"":"opacity-40 cursor-not-allowed"}`,children:g.map(x=>e.jsx("option",{value:x.value,children:x.label},x.value))})]})}function L(){return e.jsxs("div",{className:"flex items-start gap-2 mt-2 px-3 py-2 rounded bg-surface-sunken",children:[e.jsx(j,{className:"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5"}),e.jsx("p",{className:"text-[10px] text-text-tertiary leading-relaxed",children:"Stripping preserves workflow results and timeline data needed for the execution detail view. Transient jobs are internal bookkeeping that accumulates over time."})]})}const f={pruneJobs:!0,expire:"30 days",engineStreams:!0,engineStreamsExpire:"1 day",workerStreams:!0,workerStreamsExpire:"90 days",stripAttributes:!1,pruneTransient:!1};function N(t){return t.pruneJobs||t.engineStreams||t.workerStreams||t.stripAttributes||t.pruneTransient}function P({fields:t,onChange:r}){const a=(s,i)=>r({...t,[s]:i});return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Stream Messages"}),e.jsx("p",{className:"text-[10px] text-text-tertiary mb-4",children:"Processed routing messages. Already consumed — safe to remove after a short retention window."}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(v,{checked:t.engineStreams,onToggle:s=>a("engineStreams",s),label:"Engine messages",hint:"Internal orchestration signals",safety:"safe",value:t.engineStreamsExpire,onChange:s=>a("engineStreamsExpire",s)}),e.jsx(v,{checked:t.workerStreams,onToggle:s=>a("workerStreams",s),label:"Worker messages",hint:"Activity dispatch and response payloads",safety:"safe",value:t.workerStreamsExpire,onChange:s=>a("workerStreamsExpire",s)})]})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Completed Workflows"}),e.jsx("p",{className:"text-[10px] text-text-tertiary mb-4",children:"Choose how to handle completed workflow records. Reducing strips step-level detail but keeps the workflow and its results. Deleting removes the record entirely."}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:!t.stripAttributes&&!t.pruneJobs,onChange:()=>r({...t,stripAttributes:!1,pruneJobs:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Keep as-is"}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"No changes to completed workflow records."})]})]}),e.jsxs("div",{className:"flex items-start gap-2.5",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:t.stripAttributes&&!t.pruneJobs,onChange:()=>r({...t,stripAttributes:!0,pruneJobs:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Reduce completed workflows"}),e.jsxs("span",{className:"flex items-center gap-0.5 text-[9px] text-status-warning",children:[e.jsx(E,{className:"w-3 h-3",strokeWidth:1.5}),"Careful"]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"Strips step-level execution detail (activity inputs/outputs, internal state). Workflow results and timeline are preserved."})]})]}),e.jsx("select",{value:t.expire,onChange:s=>a("expire",s.target.value),disabled:!t.stripAttributes||t.pruneJobs,className:`select text-xs w-36 transition-opacity mt-0.5 ${!t.stripAttributes||t.pruneJobs?"opacity-40 cursor-not-allowed":""}`,children:g.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))})]}),e.jsxs("div",{className:"flex items-start gap-2.5",children:[e.jsxs("label",{className:"flex items-start gap-2.5 cursor-pointer w-80 shrink-0",children:[e.jsx("input",{type:"radio",name:"workflow-cleanup",checked:t.pruneJobs,onChange:()=>r({...t,pruneJobs:!0,stripAttributes:!1}),className:"w-4 h-4 mt-0.5 accent-accent shrink-0"}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-text-primary",children:"Delete completed workflows"}),e.jsxs("span",{className:"flex items-center gap-0.5 text-[9px] text-status-error",children:[e.jsx(T,{className:"w-3 h-3",strokeWidth:1.5}),"Permanent"]})]}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"Permanently removes workflow records. Results, timeline, and all execution data are deleted and cannot be recovered."})]})]}),e.jsx("select",{value:t.expire,onChange:s=>a("expire",s.target.value),disabled:!t.pruneJobs,className:`select text-xs w-36 transition-opacity mt-0.5 ${t.pruneJobs?"":"opacity-40 cursor-not-allowed"}`,children:g.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))})]})]}),e.jsx(L,{})]})]})}function p({label:t,value:r}){return e.jsxs("div",{children:[e.jsx("p",{className:"text-lg font-light text-text-primary",children:r.toLocaleString()}),e.jsx("p",{className:"text-[10px] text-text-tertiary",children:t})]})}function U(){const t=q(),[r,a]=d.useState(f),[s,i]=d.useState(null),[l,c]=d.useState(!1),n=()=>{i(null),t.mutate({expire:r.expire,jobs:r.pruneJobs,engineStreams:r.engineStreams,engineStreamsExpire:r.engineStreamsExpire,workerStreams:r.workerStreams,workerStreamsExpire:r.workerStreamsExpire,attributes:r.stripAttributes,pruneTransient:r.pruneTransient},{onSuccess:o=>{i(o),c(!1)}})};return e.jsxs("div",{className:"space-y-8",children:[e.jsx(P,{fields:r,onChange:a}),e.jsxs("div",{className:"flex items-center justify-between pt-2 border-t border-surface-border",children:[e.jsx("p",{className:"text-[10px] text-text-tertiary",children:N(r)?"This action permanently deletes data and cannot be undone.":"Select at least one operation to enable pruning."}),e.jsx("button",{onClick:()=>c(!0),disabled:!N(r)||t.isPending,className:"bg-status-error text-white px-4 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity disabled:opacity-40 shrink-0",children:t.isPending?"Pruning...":"Prune Now"})]}),s&&e.jsxs("div",{className:"bg-surface-sunken rounded-md px-4 py-3",children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-2",children:"Results"}),e.jsxs("div",{className:"flex flex-wrap gap-6",children:[s.jobs!=null&&s.jobs>0&&e.jsx(p,{label:"Jobs deleted",value:s.jobs}),s.engineStreams!=null&&s.engineStreams>0&&e.jsx(p,{label:"Engine streams",value:s.engineStreams}),s.workerStreams!=null&&s.workerStreams>0&&e.jsx(p,{label:"Worker streams",value:s.workerStreams}),s.streams!=null&&s.engineStreams==null&&s.streams>0&&e.jsx(p,{label:"Streams",value:s.streams}),s.attributes!=null&&s.attributes>0&&e.jsx(p,{label:"Artifacts stripped",value:s.attributes}),s.transient!=null&&s.transient>0&&e.jsx(p,{label:"Transient deleted",value:s.transient}),s.marked!=null&&s.marked>0&&e.jsx(p,{label:"Marked pruned",value:s.marked}),Object.values(s).every(o=>!o||o===0)&&e.jsx("p",{className:"text-xs text-text-tertiary",children:"Nothing to prune within the selected retention windows."})]})]}),t.error&&e.jsx("p",{className:"text-xs text-status-error",children:t.error.message}),e.jsx(M,{open:l,onClose:()=>c(!1),title:"Confirm Prune",children:e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{className:"text-sm text-text-secondary",children:"This will permanently delete data. This action cannot be undone."}),e.jsxs("ul",{className:"text-xs text-text-secondary space-y-1 list-disc list-inside",children:[r.pruneJobs&&e.jsxs("li",{children:["Delete jobs older than ",r.expire]}),r.engineStreams&&e.jsxs("li",{children:["Delete engine streams older than ",r.engineStreamsExpire]}),r.workerStreams&&e.jsxs("li",{children:["Delete worker streams older than ",r.workerStreamsExpire]}),r.stripAttributes&&e.jsx("li",{children:"Strip execution artifacts from completed jobs"}),r.pruneTransient&&e.jsx("li",{children:"Delete transient (entity-less) jobs"})]}),e.jsxs("div",{className:"flex justify-end gap-3 pt-2",children:[e.jsx("button",{onClick:()=>c(!1),className:"btn-secondary text-xs",children:"Cancel"}),e.jsx("button",{onClick:n,className:"bg-status-error text-white px-3 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity",disabled:t.isPending,children:t.isPending?"Pruning...":"Confirm Prune"})]})]})})]})}function w(t){var c;if(!((c=t==null?void 0:t.rules)!=null&&c.length))return f;const r=t.rules,a=r.find(n=>n.target==="streams"&&n.action==="delete"),s=r.find(n=>n.target==="jobs"&&n.action==="delete"&&n.hasEntity===!1),i=r.find(n=>n.target==="jobs"&&n.action==="prune"&&n.hasEntity===!0),l=r.find(n=>n.target==="jobs"&&n.action==="delete"&&n.pruned===!0);return{pruneJobs:!!l,expire:(l==null?void 0:l.olderThan)??"180 days",engineStreams:!!a,engineStreamsExpire:(a==null?void 0:a.olderThan)??"1 day",workerStreams:!!a,workerStreamsExpire:(a==null?void 0:a.olderThan)??"90 days",stripAttributes:!!i,pruneTransient:!!s}}function H(t){const r=[];return t.engineStreams&&r.push({target:"streams",action:"delete",olderThan:t.engineStreamsExpire}),t.workerStreams&&t.workerStreamsExpire!==t.engineStreamsExpire&&r.push({target:"streams",action:"delete",olderThan:t.workerStreamsExpire}),t.pruneTransient&&r.push({target:"jobs",action:"delete",olderThan:t.expire,hasEntity:!1}),t.stripAttributes&&r.push({target:"jobs",action:"prune",olderThan:t.expire,hasEntity:!0}),t.pruneJobs&&r.push({target:"jobs",action:"delete",olderThan:t.expire,pruned:!0}),r}function S(t){var r;return((r=C.find(a=>a.value===t))==null?void 0:r.label)??""}function K(){const{data:t,isLoading:r}=$(),a=_(),[s,i]=d.useState(""),[l,c]=d.useState(f),[n,o]=d.useState(""),m=d.useCallback(()=>{t!=null&&t.config&&(i(t.config.schedule),c(w(t.config)),o(JSON.stringify({schedule:t.config.schedule,fields:w(t.config)})))},[t]);d.useEffect(()=>{m()},[m]);const x=JSON.stringify({schedule:s,fields:l}),y=n!==""&&x!==n,h=(t==null?void 0:t.active)??!1,J=()=>{const u=H(l);a.mutate({schedule:s,rules:u},{onSuccess:()=>{o(JSON.stringify({schedule:s,fields:l}))}})},A=()=>{m()};return r?e.jsx("div",{className:"animate-pulse h-40 bg-surface-sunken rounded"}):e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-[1fr_auto] gap-10",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4",children:"Schedule"}),e.jsxs("div",{className:"flex items-end gap-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] text-text-tertiary mb-1",children:"Cron Expression"}),e.jsx("input",{type:"text",value:s,onChange:u=>i(u.target.value),placeholder:"0 2 * * *",className:"input text-xs font-mono w-48"})]}),e.jsx("div",{children:e.jsxs("div",{className:"flex items-center gap-1.5 h-8",children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${h?"bg-status-success":"bg-text-tertiary"}`}),e.jsx("span",{className:`text-xs ${h?"text-status-success":"text-text-tertiary"}`,children:h?"Active":"Inactive"})]})}),S(s)&&e.jsx("p",{className:"text-xs text-text-tertiary h-8 flex items-center",children:S(s)})]}),e.jsx("div",{className:"flex flex-wrap gap-1.5 mt-3",children:C.map(u=>e.jsx("button",{type:"button",onClick:()=>i(u.value),className:`px-2.5 py-1 text-[10px] rounded-full transition-colors ${s===u.value?"bg-accent/10 text-accent font-medium":"bg-surface-sunken text-text-tertiary hover:text-text-secondary"}`,children:u.label},u.value))})]}),e.jsxs("div",{className:"lg:w-72",children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4",children:"How It Works"}),e.jsxs("div",{className:"flex items-start gap-2 px-3 py-2 rounded bg-surface-sunken",children:[e.jsx(j,{className:"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5"}),e.jsx("p",{className:"text-[10px] text-text-tertiary leading-relaxed",children:"Rules execute sequentially on each cron cycle. Engine streams can be pruned aggressively since they only contain internal routing data. Worker streams should be retained longer to preserve execution playback and input enrichment."})]})]})]}),e.jsx(P,{fields:l,onChange:c}),e.jsxs("div",{className:"flex items-center justify-between pt-2 border-t border-surface-border",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[y&&e.jsx("button",{onClick:A,className:"text-[10px] text-text-tertiary hover:text-text-primary transition-colors",children:"Revert changes"}),a.error&&e.jsx("p",{className:"text-xs text-status-error",children:a.error.message})]}),e.jsx("button",{onClick:J,disabled:!y||!s.trim()||a.isPending,className:"btn-primary text-xs disabled:opacity-40 shrink-0",children:a.isPending?"Saving...":"Save Schedule"})]})]})}function Q({mode:t,onChange:r}){const a=(s,i,l)=>e.jsxs("button",{onClick:()=>r(s),className:`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md transition-colors ${t===s?"bg-accent/10 text-accent font-medium":"text-text-tertiary hover:text-text-secondary hover:bg-surface-hover"}`,children:[i,l]});return e.jsxs("div",{className:"flex gap-1 p-0.5 bg-surface-sunken rounded-lg w-fit",children:[a("prune",e.jsx(I,{className:"w-3.5 h-3.5"}),"Prune Now"),a("schedule",e.jsx(D,{className:"w-3.5 h-3.5"}),"Schedule")]})}function ee(){const[t,r]=d.useState("schedule");return e.jsxs("div",{children:[e.jsx(F,{title:"DB Maintenance",docsHash:"#docs:dashboard.md:db-maintenance",actions:e.jsx(Q,{mode:t,onChange:r})}),e.jsxs("div",{className:"flex items-start gap-2 px-4 py-3 mb-8 rounded-md bg-accent/5 border border-accent/10",children:[e.jsx(j,{className:"w-4 h-4 text-accent shrink-0 mt-0.5"}),e.jsx("p",{className:"text-xs text-text-secondary leading-relaxed",children:"Workflow data grows as jobs complete. Operations are ordered safest-first: stream messages are always safe to prune, workflow reduction preserves results, and deletion is permanent."})]}),t==="prune"?e.jsx(U,{}):e.jsx(K,{})]})}export{ee as MaintenancePage};
|
|
2
|
+
//# sourceMappingURL=index-NnGsWGNS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-3A_5CL81.js","sources":["../../src/api/maintenance.ts","../../src/pages/admin/maintenance/controls.tsx","../../src/pages/admin/maintenance/PruneSection.tsx","../../src/pages/admin/maintenance/ScheduleSection.tsx","../../src/pages/admin/maintenance/MaintenancePage.tsx"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface MaintenanceRule {\n target: 'streams' | 'jobs';\n action: 'delete' | 'prune';\n olderThan: string;\n hasEntity?: boolean;\n pruned?: boolean;\n}\n\nexport interface MaintenanceConfig {\n schedule: string;\n rules: MaintenanceRule[];\n}\n\nexport interface PruneOptions {\n expire?: string;\n jobs?: boolean;\n streams?: boolean;\n engineStreams?: boolean;\n engineStreamsExpire?: string;\n workerStreams?: boolean;\n workerStreamsExpire?: string;\n attributes?: boolean;\n entities?: string[];\n pruneTransient?: boolean;\n keepHmark?: boolean;\n}\n\nexport interface PruneResult {\n jobs?: number;\n streams?: number;\n engineStreams?: number;\n workerStreams?: number;\n attributes?: number;\n transient?: number;\n marked?: number;\n}\n\n// ── Maintenance config ────────────────────────────────────────────────────────\n\nexport function useMaintenanceConfig() {\n return useQuery<{ config: MaintenanceConfig; active: boolean }>({\n queryKey: ['maintenance'],\n queryFn: () => apiFetch('/config/maintenance'),\n });\n}\n\nexport function useUpdateMaintenanceConfig() {\n const queryClient = useQueryClient();\n return useMutation<\n { config: MaintenanceConfig; restarted: boolean },\n Error,\n MaintenanceConfig\n >({\n mutationFn: (config) =>\n apiFetch('/config/maintenance', {\n method: 'PUT',\n body: JSON.stringify(config),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['maintenance'] });\n },\n });\n}\n\n// ── DBA operations ────────────────────────────────────────────────────────────\n\nexport function usePrune() {\n return useMutation<PruneResult, Error, PruneOptions>({\n mutationFn: (options) =>\n apiFetch('/dba/prune', {\n method: 'POST',\n body: JSON.stringify(options),\n }),\n });\n}\n\nexport function useDeploy() {\n return useMutation<{ ok: boolean }, Error, void>({\n mutationFn: () =>\n apiFetch('/dba/deploy', { method: 'POST' }),\n });\n}\n","import { Info, ShieldCheck, ShieldAlert, ShieldOff } from 'lucide-react';\nimport { RETENTION_PERIOD_OPTIONS } from '../../../lib/constants';\n\n// ── Retention row (checkbox + label + select) ───────────────────────────────\n\nexport function RetentionRow({ checked, onToggle, label, hint, safety, value, onChange }: {\n checked: boolean;\n onToggle: (v: boolean) => void;\n label: string;\n hint: string;\n safety: 'safe' | 'moderate' | 'destructive';\n value: string;\n onChange: (v: string) => void;\n}) {\n const SafetyIcon = safety === 'safe' ? ShieldCheck : safety === 'moderate' ? ShieldAlert : ShieldOff;\n const safetyColor = safety === 'safe' ? 'text-status-success' : safety === 'moderate' ? 'text-status-warning' : 'text-status-error';\n const safetyLabel = safety === 'safe' ? 'Safe' : safety === 'moderate' ? 'Careful' : 'Permanent';\n\n return (\n <div className=\"flex items-center gap-4\">\n <label className=\"flex items-center gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"w-4 h-4 rounded border-border accent-accent shrink-0\"\n />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">{label}</span>\n <span className={`flex items-center gap-0.5 text-[9px] ${safetyColor}`}>\n <SafetyIcon className=\"w-3 h-3\" strokeWidth={1.5} />\n {safetyLabel}\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">{hint}</p>\n </div>\n </label>\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n disabled={!checked}\n className={`select text-xs w-36 transition-opacity ${!checked ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n );\n}\n\n// ── Cleanup checkbox ────────────────────────────────────────────────────────\n\nexport function CleanupCheck({ checked, onChange, label, description, safety }: {\n checked: boolean;\n onChange: (v: boolean) => void;\n label: string;\n description: string;\n safety: 'safe' | 'moderate' | 'destructive';\n}) {\n const SafetyIcon = safety === 'safe' ? ShieldCheck : safety === 'moderate' ? ShieldAlert : ShieldOff;\n const safetyColor = safety === 'safe' ? 'text-status-success' : safety === 'moderate' ? 'text-status-warning' : 'text-status-error';\n const safetyLabel = safety === 'safe' ? 'Safe' : safety === 'moderate' ? 'Careful' : 'Permanent';\n\n return (\n <label className=\"flex items-start gap-2.5 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => onChange(e.target.checked)}\n className=\"w-4 h-4 mt-0.5 rounded border-border accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">{label}</span>\n <span className={`flex items-center gap-0.5 text-[9px] ${safetyColor}`}>\n <SafetyIcon className=\"w-3 h-3\" strokeWidth={1.5} />\n {safetyLabel}\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">{description}</p>\n </div>\n </label>\n );\n}\n\n// ── Cleanup callout ─────────────────────────────────────────────────────────\n\nexport function CleanupCallout() {\n return (\n <div className=\"flex items-start gap-2 mt-2 px-3 py-2 rounded bg-surface-sunken\">\n <Info className=\"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5\" />\n <p className=\"text-[10px] text-text-tertiary leading-relaxed\">\n Stripping preserves workflow results and timeline data needed for the execution detail view.\n Transient jobs are internal bookkeeping that accumulates over time.\n </p>\n </div>\n );\n}\n\n// ── Prune fields state shape (shared between both panels) ───────────────────\n\nexport interface PruneFields {\n pruneJobs: boolean;\n expire: string;\n engineStreams: boolean;\n engineStreamsExpire: string;\n workerStreams: boolean;\n workerStreamsExpire: string;\n stripAttributes: boolean;\n pruneTransient: boolean;\n}\n\nexport const DEFAULT_PRUNE_FIELDS: PruneFields = {\n pruneJobs: true,\n expire: '30 days',\n engineStreams: true,\n engineStreamsExpire: '1 day',\n workerStreams: true,\n workerStreamsExpire: '90 days',\n stripAttributes: false,\n pruneTransient: false,\n};\n\nexport function hasSelection(f: PruneFields): boolean {\n return f.pruneJobs || f.engineStreams || f.workerStreams || f.stripAttributes || f.pruneTransient;\n}\n\n// ── Prune fields editor (reused by both Prune Now and Schedule) ─────────────\n\nexport function PruneFieldsEditor({ fields, onChange }: {\n fields: PruneFields;\n onChange: (f: PruneFields) => void;\n}) {\n const set = <K extends keyof PruneFields>(key: K, value: PruneFields[K]) =>\n onChange({ ...fields, [key]: value });\n\n return (\n <div className=\"space-y-8\">\n {/* Stream messages — always safe to prune */}\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Stream Messages\n </p>\n <p className=\"text-[10px] text-text-tertiary mb-4\">\n Processed routing messages. Already consumed — safe to remove after a short retention window.\n </p>\n <div className=\"space-y-3\">\n <RetentionRow\n checked={fields.engineStreams} onToggle={(v) => set('engineStreams', v)}\n label=\"Engine messages\" hint=\"Internal orchestration signals\"\n safety=\"safe\"\n value={fields.engineStreamsExpire} onChange={(v) => set('engineStreamsExpire', v)}\n />\n <RetentionRow\n checked={fields.workerStreams} onToggle={(v) => set('workerStreams', v)}\n label=\"Worker messages\" hint=\"Activity dispatch and response payloads\"\n safety=\"safe\"\n value={fields.workerStreamsExpire} onChange={(v) => set('workerStreamsExpire', v)}\n />\n </div>\n </div>\n\n {/* Workflow data — mutually exclusive: reduce OR delete */}\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Completed Workflows\n </p>\n <p className=\"text-[10px] text-text-tertiary mb-4\">\n Choose how to handle completed workflow records. Reducing strips step-level detail\n but keeps the workflow and its results. Deleting removes the record entirely.\n </p>\n <div className=\"space-y-3\">\n <label className=\"flex items-start gap-2.5 cursor-pointer\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={!fields.stripAttributes && !fields.pruneJobs}\n onChange={() => onChange({ ...fields, stripAttributes: false, pruneJobs: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <span className=\"text-xs text-text-primary\">Keep as-is</span>\n <p className=\"text-[10px] text-text-tertiary\">No changes to completed workflow records.</p>\n </div>\n </label>\n\n <div className=\"flex items-start gap-2.5\">\n <label className=\"flex items-start gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={fields.stripAttributes && !fields.pruneJobs}\n onChange={() => onChange({ ...fields, stripAttributes: true, pruneJobs: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">Reduce completed workflows</span>\n <span className=\"flex items-center gap-0.5 text-[9px] text-status-warning\">\n <ShieldAlert className=\"w-3 h-3\" strokeWidth={1.5} />\n Careful\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">\n Strips step-level execution detail (activity inputs/outputs, internal state).\n Workflow results and timeline are preserved.\n </p>\n </div>\n </label>\n <select\n value={fields.expire}\n onChange={(e) => set('expire', e.target.value)}\n disabled={!fields.stripAttributes || fields.pruneJobs}\n className={`select text-xs w-36 transition-opacity mt-0.5 ${!fields.stripAttributes || fields.pruneJobs ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n\n <div className=\"flex items-start gap-2.5\">\n <label className=\"flex items-start gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={fields.pruneJobs}\n onChange={() => onChange({ ...fields, pruneJobs: true, stripAttributes: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">Delete completed workflows</span>\n <span className=\"flex items-center gap-0.5 text-[9px] text-status-error\">\n <ShieldOff className=\"w-3 h-3\" strokeWidth={1.5} />\n Permanent\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">\n Permanently removes workflow records. Results, timeline, and all\n execution data are deleted and cannot be recovered.\n </p>\n </div>\n </label>\n <select\n value={fields.expire}\n onChange={(e) => set('expire', e.target.value)}\n disabled={!fields.pruneJobs}\n className={`select text-xs w-36 transition-opacity mt-0.5 ${!fields.pruneJobs ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n </div>\n <CleanupCallout />\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { usePrune, type PruneResult } from '../../../api/maintenance';\nimport { Modal } from '../../../components/common/modal/Modal';\nimport { PruneFieldsEditor, DEFAULT_PRUNE_FIELDS, hasSelection, type PruneFields } from './controls';\n\nfunction ResultCard({ label, value }: { label: string; value: number }) {\n return (\n <div>\n <p className=\"text-lg font-light text-text-primary\">{value.toLocaleString()}</p>\n <p className=\"text-[10px] text-text-tertiary\">{label}</p>\n </div>\n );\n}\n\nexport function PruneSection() {\n const prune = usePrune();\n const [fields, setFields] = useState<PruneFields>(DEFAULT_PRUNE_FIELDS);\n const [result, setResult] = useState<PruneResult | null>(null);\n const [showConfirm, setShowConfirm] = useState(false);\n\n const handlePrune = () => {\n setResult(null);\n prune.mutate(\n {\n expire: fields.expire,\n jobs: fields.pruneJobs,\n engineStreams: fields.engineStreams,\n engineStreamsExpire: fields.engineStreamsExpire,\n workerStreams: fields.workerStreams,\n workerStreamsExpire: fields.workerStreamsExpire,\n attributes: fields.stripAttributes,\n pruneTransient: fields.pruneTransient,\n },\n {\n onSuccess: (data) => {\n setResult(data);\n setShowConfirm(false);\n },\n },\n );\n };\n\n return (\n <div className=\"space-y-8\">\n <PruneFieldsEditor fields={fields} onChange={setFields} />\n\n {/* Action bar */}\n <div className=\"flex items-center justify-between pt-2 border-t border-surface-border\">\n <p className=\"text-[10px] text-text-tertiary\">\n {!hasSelection(fields)\n ? 'Select at least one operation to enable pruning.'\n : 'This action permanently deletes data and cannot be undone.'}\n </p>\n <button\n onClick={() => setShowConfirm(true)}\n disabled={!hasSelection(fields) || prune.isPending}\n className=\"bg-status-error text-white px-4 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity disabled:opacity-40 shrink-0\"\n >\n {prune.isPending ? 'Pruning...' : 'Prune Now'}\n </button>\n </div>\n\n {/* Results */}\n {result && (\n <div className=\"bg-surface-sunken rounded-md px-4 py-3\">\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-2\">\n Results\n </p>\n <div className=\"flex flex-wrap gap-6\">\n {result.jobs != null && result.jobs > 0 && <ResultCard label=\"Jobs deleted\" value={result.jobs} />}\n {result.engineStreams != null && result.engineStreams > 0 && <ResultCard label=\"Engine streams\" value={result.engineStreams} />}\n {result.workerStreams != null && result.workerStreams > 0 && <ResultCard label=\"Worker streams\" value={result.workerStreams} />}\n {result.streams != null && result.engineStreams == null && result.streams > 0 && <ResultCard label=\"Streams\" value={result.streams} />}\n {result.attributes != null && result.attributes > 0 && <ResultCard label=\"Artifacts stripped\" value={result.attributes} />}\n {result.transient != null && result.transient > 0 && <ResultCard label=\"Transient deleted\" value={result.transient} />}\n {result.marked != null && result.marked > 0 && <ResultCard label=\"Marked pruned\" value={result.marked} />}\n {Object.values(result).every((v) => !v || v === 0) && (\n <p className=\"text-xs text-text-tertiary\">Nothing to prune within the selected retention windows.</p>\n )}\n </div>\n </div>\n )}\n\n {prune.error && (\n <p className=\"text-xs text-status-error\">{(prune.error as Error).message}</p>\n )}\n\n {/* Confirm modal */}\n <Modal open={showConfirm} onClose={() => setShowConfirm(false)} title=\"Confirm Prune\">\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n This will permanently delete data. This action cannot be undone.\n </p>\n <ul className=\"text-xs text-text-secondary space-y-1 list-disc list-inside\">\n {fields.pruneJobs && <li>Delete jobs older than {fields.expire}</li>}\n {fields.engineStreams && <li>Delete engine streams older than {fields.engineStreamsExpire}</li>}\n {fields.workerStreams && <li>Delete worker streams older than {fields.workerStreamsExpire}</li>}\n {fields.stripAttributes && <li>Strip execution artifacts from completed jobs</li>}\n {fields.pruneTransient && <li>Delete transient (entity-less) jobs</li>}\n </ul>\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={() => setShowConfirm(false)} className=\"btn-secondary text-xs\">Cancel</button>\n <button\n onClick={handlePrune}\n className=\"bg-status-error text-white px-3 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity\"\n disabled={prune.isPending}\n >\n {prune.isPending ? 'Pruning...' : 'Confirm Prune'}\n </button>\n </div>\n </div>\n </Modal>\n </div>\n );\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { Info } from 'lucide-react';\nimport {\n useMaintenanceConfig,\n useUpdateMaintenanceConfig,\n type MaintenanceConfig,\n} from '../../../api/maintenance';\nimport { CRON_PRESETS } from '../../../lib/constants';\nimport { PruneFieldsEditor, DEFAULT_PRUNE_FIELDS, type PruneFields } from './controls';\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Convert a MaintenanceConfig (rules array) into PruneFields for the editor. */\nfunction configToFields(config: MaintenanceConfig | undefined): PruneFields {\n if (!config?.rules?.length) return DEFAULT_PRUNE_FIELDS;\n const r = config.rules;\n const streamDelete = r.find((x) => x.target === 'streams' && x.action === 'delete');\n const jobDeleteTransient = r.find((x) => x.target === 'jobs' && x.action === 'delete' && x.hasEntity === false);\n const jobPrune = r.find((x) => x.target === 'jobs' && x.action === 'prune' && x.hasEntity === true);\n const jobDeletePruned = r.find((x) => x.target === 'jobs' && x.action === 'delete' && x.pruned === true);\n return {\n pruneJobs: !!jobDeletePruned,\n expire: jobDeletePruned?.olderThan ?? '180 days',\n engineStreams: !!streamDelete,\n engineStreamsExpire: streamDelete?.olderThan ?? '1 day',\n workerStreams: !!streamDelete,\n workerStreamsExpire: streamDelete?.olderThan ?? '90 days',\n stripAttributes: !!jobPrune,\n pruneTransient: !!jobDeleteTransient,\n };\n}\n\n/** Convert PruneFields back to a MaintenanceConfig rules array. */\nfunction fieldsToRules(f: PruneFields): MaintenanceConfig['rules'] {\n const rules: MaintenanceConfig['rules'] = [];\n if (f.engineStreams) {\n rules.push({ target: 'streams', action: 'delete', olderThan: f.engineStreamsExpire });\n }\n if (f.workerStreams && f.workerStreamsExpire !== f.engineStreamsExpire) {\n rules.push({ target: 'streams', action: 'delete', olderThan: f.workerStreamsExpire });\n }\n if (f.pruneTransient) {\n rules.push({ target: 'jobs', action: 'delete', olderThan: f.expire, hasEntity: false });\n }\n if (f.stripAttributes) {\n rules.push({ target: 'jobs', action: 'prune', olderThan: f.expire, hasEntity: true });\n }\n if (f.pruneJobs) {\n rules.push({ target: 'jobs', action: 'delete', olderThan: f.expire, pruned: true });\n }\n return rules;\n}\n\nfunction describeCron(expr: string): string {\n return CRON_PRESETS.find((p) => p.value === expr)?.label ?? '';\n}\n\n// ── Main ────────────────────────────────────────────────────────────────────\n\nexport function ScheduleSection() {\n const { data, isLoading } = useMaintenanceConfig();\n const updateConfig = useUpdateMaintenanceConfig();\n\n const [schedule, setSchedule] = useState('');\n const [fields, setFields] = useState<PruneFields>(DEFAULT_PRUNE_FIELDS);\n const [savedSnapshot, setSavedSnapshot] = useState('');\n\n // Sync from server config when loaded\n const syncFromServer = useCallback(() => {\n if (!data?.config) return;\n setSchedule(data.config.schedule);\n setFields(configToFields(data.config));\n setSavedSnapshot(JSON.stringify({ schedule: data.config.schedule, fields: configToFields(data.config) }));\n }, [data]);\n\n useEffect(() => { syncFromServer(); }, [syncFromServer]);\n\n const currentSnapshot = JSON.stringify({ schedule, fields });\n const isDirty = savedSnapshot !== '' && currentSnapshot !== savedSnapshot;\n const active = data?.active ?? false;\n\n const handleSave = () => {\n const rules = fieldsToRules(fields);\n updateConfig.mutate(\n { schedule, rules },\n {\n onSuccess: () => {\n setSavedSnapshot(JSON.stringify({ schedule, fields }));\n },\n },\n );\n };\n\n const handleRevert = () => {\n syncFromServer();\n };\n\n if (isLoading) {\n return <div className=\"animate-pulse h-40 bg-surface-sunken rounded\" />;\n }\n\n return (\n <div className=\"space-y-8\">\n {/* Cron schedule + status */}\n <div className=\"grid grid-cols-1 lg:grid-cols-[1fr_auto] gap-10\">\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4\">\n Schedule\n </p>\n <div className=\"flex items-end gap-4\">\n <div>\n <label className=\"block text-[10px] text-text-tertiary mb-1\">Cron Expression</label>\n <input\n type=\"text\"\n value={schedule}\n onChange={(e) => setSchedule(e.target.value)}\n placeholder=\"0 2 * * *\"\n className=\"input text-xs font-mono w-48\"\n />\n </div>\n <div>\n <div className=\"flex items-center gap-1.5 h-8\">\n <span className={`w-1.5 h-1.5 rounded-full ${active ? 'bg-status-success' : 'bg-text-tertiary'}`} />\n <span className={`text-xs ${active ? 'text-status-success' : 'text-text-tertiary'}`}>\n {active ? 'Active' : 'Inactive'}\n </span>\n </div>\n </div>\n {describeCron(schedule) && (\n <p className=\"text-xs text-text-tertiary h-8 flex items-center\">{describeCron(schedule)}</p>\n )}\n </div>\n\n {/* Preset pills */}\n <div className=\"flex flex-wrap gap-1.5 mt-3\">\n {CRON_PRESETS.map((preset) => (\n <button\n key={preset.value}\n type=\"button\"\n onClick={() => setSchedule(preset.value)}\n className={`px-2.5 py-1 text-[10px] rounded-full transition-colors ${\n schedule === preset.value\n ? 'bg-accent/10 text-accent font-medium'\n : 'bg-surface-sunken text-text-tertiary hover:text-text-secondary'\n }`}\n >\n {preset.label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"lg:w-72\">\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4\">\n How It Works\n </p>\n <div className=\"flex items-start gap-2 px-3 py-2 rounded bg-surface-sunken\">\n <Info className=\"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5\" />\n <p className=\"text-[10px] text-text-tertiary leading-relaxed\">\n Rules execute sequentially on each cron cycle. Engine streams can be pruned aggressively\n since they only contain internal routing data. Worker streams should be retained longer\n to preserve execution playback and input enrichment.\n </p>\n </div>\n </div>\n </div>\n\n {/* Same prune fields as Prune Now */}\n <PruneFieldsEditor fields={fields} onChange={setFields} />\n\n {/* Action bar */}\n <div className=\"flex items-center justify-between pt-2 border-t border-surface-border\">\n <div className=\"flex items-center gap-3\">\n {isDirty && (\n <button\n onClick={handleRevert}\n className=\"text-[10px] text-text-tertiary hover:text-text-primary transition-colors\"\n >\n Revert changes\n </button>\n )}\n {updateConfig.error && (\n <p className=\"text-xs text-status-error\">{(updateConfig.error as Error).message}</p>\n )}\n </div>\n <button\n onClick={handleSave}\n disabled={!isDirty || !schedule.trim() || updateConfig.isPending}\n className=\"btn-primary text-xs disabled:opacity-40 shrink-0\"\n >\n {updateConfig.isPending ? 'Saving...' : 'Save Schedule'}\n </button>\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { Trash2, Clock, Info } from 'lucide-react';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { PruneSection } from './PruneSection';\nimport { ScheduleSection } from './ScheduleSection';\n\ntype Mode = 'prune' | 'schedule';\n\nfunction ModeToggle({ mode, onChange }: { mode: Mode; onChange: (m: Mode) => void }) {\n const btn = (m: Mode, icon: React.ReactNode, label: string) => (\n <button\n onClick={() => onChange(m)}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md transition-colors ${\n mode === m\n ? 'bg-accent/10 text-accent font-medium'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-surface-hover'\n }`}\n >\n {icon}\n {label}\n </button>\n );\n\n return (\n <div className=\"flex gap-1 p-0.5 bg-surface-sunken rounded-lg w-fit\">\n {btn('prune', <Trash2 className=\"w-3.5 h-3.5\" />, 'Prune Now')}\n {btn('schedule', <Clock className=\"w-3.5 h-3.5\" />, 'Schedule')}\n </div>\n );\n}\n\nexport function MaintenancePage() {\n const [mode, setMode] = useState<Mode>('schedule');\n\n return (\n <div>\n <PageHeader\n title=\"DB Maintenance\"\n docsHash=\"#docs:dashboard.md:db-maintenance\"\n actions={<ModeToggle mode={mode} onChange={setMode} />}\n />\n\n <div className=\"flex items-start gap-2 px-4 py-3 mb-8 rounded-md bg-accent/5 border border-accent/10\">\n <Info className=\"w-4 h-4 text-accent shrink-0 mt-0.5\" />\n <p className=\"text-xs text-text-secondary leading-relaxed\">\n Workflow data grows as jobs complete. Operations are ordered safest-first: stream messages are\n always safe to prune, workflow reduction preserves results, and deletion is permanent.\n </p>\n </div>\n\n {mode === 'prune' ? <PruneSection /> : <ScheduleSection />}\n </div>\n );\n}\n"],"names":["useMaintenanceConfig","useQuery","apiFetch","useUpdateMaintenanceConfig","queryClient","useQueryClient","useMutation","config","usePrune","options","RetentionRow","checked","onToggle","label","hint","safety","value","onChange","SafetyIcon","ShieldCheck","ShieldAlert","ShieldOff","safetyColor","safetyLabel","jsxs","jsx","e","RETENTION_PERIOD_OPTIONS","opt","CleanupCallout","Info","DEFAULT_PRUNE_FIELDS","hasSelection","f","PruneFieldsEditor","fields","set","key","v","ResultCard","PruneSection","prune","setFields","useState","result","setResult","showConfirm","setShowConfirm","handlePrune","data","Modal","configToFields","_a","streamDelete","x","jobDeleteTransient","jobPrune","jobDeletePruned","fieldsToRules","rules","describeCron","expr","CRON_PRESETS","p","ScheduleSection","isLoading","updateConfig","schedule","setSchedule","savedSnapshot","setSavedSnapshot","syncFromServer","useCallback","useEffect","currentSnapshot","isDirty","active","handleSave","handleRevert","preset","ModeToggle","mode","btn","m","icon","Trash2","Clock","MaintenancePage","setMode","PageHeader"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-NnGsWGNS.js","sources":["../../src/api/maintenance.ts","../../src/pages/admin/maintenance/controls.tsx","../../src/pages/admin/maintenance/PruneSection.tsx","../../src/pages/admin/maintenance/ScheduleSection.tsx","../../src/pages/admin/maintenance/MaintenancePage.tsx"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface MaintenanceRule {\n target: 'streams' | 'jobs';\n action: 'delete' | 'prune';\n olderThan: string;\n hasEntity?: boolean;\n pruned?: boolean;\n}\n\nexport interface MaintenanceConfig {\n schedule: string;\n rules: MaintenanceRule[];\n}\n\nexport interface PruneOptions {\n expire?: string;\n jobs?: boolean;\n streams?: boolean;\n engineStreams?: boolean;\n engineStreamsExpire?: string;\n workerStreams?: boolean;\n workerStreamsExpire?: string;\n attributes?: boolean;\n entities?: string[];\n pruneTransient?: boolean;\n keepHmark?: boolean;\n}\n\nexport interface PruneResult {\n jobs?: number;\n streams?: number;\n engineStreams?: number;\n workerStreams?: number;\n attributes?: number;\n transient?: number;\n marked?: number;\n}\n\n// ── Maintenance config ────────────────────────────────────────────────────────\n\nexport function useMaintenanceConfig() {\n return useQuery<{ config: MaintenanceConfig; active: boolean }>({\n queryKey: ['maintenance'],\n queryFn: () => apiFetch('/config/maintenance'),\n });\n}\n\nexport function useUpdateMaintenanceConfig() {\n const queryClient = useQueryClient();\n return useMutation<\n { config: MaintenanceConfig; restarted: boolean },\n Error,\n MaintenanceConfig\n >({\n mutationFn: (config) =>\n apiFetch('/config/maintenance', {\n method: 'PUT',\n body: JSON.stringify(config),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['maintenance'] });\n },\n });\n}\n\n// ── DBA operations ────────────────────────────────────────────────────────────\n\nexport function usePrune() {\n return useMutation<PruneResult, Error, PruneOptions>({\n mutationFn: (options) =>\n apiFetch('/dba/prune', {\n method: 'POST',\n body: JSON.stringify(options),\n }),\n });\n}\n\nexport function useDeploy() {\n return useMutation<{ ok: boolean }, Error, void>({\n mutationFn: () =>\n apiFetch('/dba/deploy', { method: 'POST' }),\n });\n}\n","import { Info, ShieldCheck, ShieldAlert, ShieldOff } from 'lucide-react';\nimport { RETENTION_PERIOD_OPTIONS } from '../../../lib/constants';\n\n// ── Retention row (checkbox + label + select) ───────────────────────────────\n\nexport function RetentionRow({ checked, onToggle, label, hint, safety, value, onChange }: {\n checked: boolean;\n onToggle: (v: boolean) => void;\n label: string;\n hint: string;\n safety: 'safe' | 'moderate' | 'destructive';\n value: string;\n onChange: (v: string) => void;\n}) {\n const SafetyIcon = safety === 'safe' ? ShieldCheck : safety === 'moderate' ? ShieldAlert : ShieldOff;\n const safetyColor = safety === 'safe' ? 'text-status-success' : safety === 'moderate' ? 'text-status-warning' : 'text-status-error';\n const safetyLabel = safety === 'safe' ? 'Safe' : safety === 'moderate' ? 'Careful' : 'Permanent';\n\n return (\n <div className=\"flex items-center gap-4\">\n <label className=\"flex items-center gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"w-4 h-4 rounded border-border accent-accent shrink-0\"\n />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">{label}</span>\n <span className={`flex items-center gap-0.5 text-[9px] ${safetyColor}`}>\n <SafetyIcon className=\"w-3 h-3\" strokeWidth={1.5} />\n {safetyLabel}\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">{hint}</p>\n </div>\n </label>\n <select\n value={value}\n onChange={(e) => onChange(e.target.value)}\n disabled={!checked}\n className={`select text-xs w-36 transition-opacity ${!checked ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n );\n}\n\n// ── Cleanup checkbox ────────────────────────────────────────────────────────\n\nexport function CleanupCheck({ checked, onChange, label, description, safety }: {\n checked: boolean;\n onChange: (v: boolean) => void;\n label: string;\n description: string;\n safety: 'safe' | 'moderate' | 'destructive';\n}) {\n const SafetyIcon = safety === 'safe' ? ShieldCheck : safety === 'moderate' ? ShieldAlert : ShieldOff;\n const safetyColor = safety === 'safe' ? 'text-status-success' : safety === 'moderate' ? 'text-status-warning' : 'text-status-error';\n const safetyLabel = safety === 'safe' ? 'Safe' : safety === 'moderate' ? 'Careful' : 'Permanent';\n\n return (\n <label className=\"flex items-start gap-2.5 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => onChange(e.target.checked)}\n className=\"w-4 h-4 mt-0.5 rounded border-border accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">{label}</span>\n <span className={`flex items-center gap-0.5 text-[9px] ${safetyColor}`}>\n <SafetyIcon className=\"w-3 h-3\" strokeWidth={1.5} />\n {safetyLabel}\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">{description}</p>\n </div>\n </label>\n );\n}\n\n// ── Cleanup callout ─────────────────────────────────────────────────────────\n\nexport function CleanupCallout() {\n return (\n <div className=\"flex items-start gap-2 mt-2 px-3 py-2 rounded bg-surface-sunken\">\n <Info className=\"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5\" />\n <p className=\"text-[10px] text-text-tertiary leading-relaxed\">\n Stripping preserves workflow results and timeline data needed for the execution detail view.\n Transient jobs are internal bookkeeping that accumulates over time.\n </p>\n </div>\n );\n}\n\n// ── Prune fields state shape (shared between both panels) ───────────────────\n\nexport interface PruneFields {\n pruneJobs: boolean;\n expire: string;\n engineStreams: boolean;\n engineStreamsExpire: string;\n workerStreams: boolean;\n workerStreamsExpire: string;\n stripAttributes: boolean;\n pruneTransient: boolean;\n}\n\nexport const DEFAULT_PRUNE_FIELDS: PruneFields = {\n pruneJobs: true,\n expire: '30 days',\n engineStreams: true,\n engineStreamsExpire: '1 day',\n workerStreams: true,\n workerStreamsExpire: '90 days',\n stripAttributes: false,\n pruneTransient: false,\n};\n\nexport function hasSelection(f: PruneFields): boolean {\n return f.pruneJobs || f.engineStreams || f.workerStreams || f.stripAttributes || f.pruneTransient;\n}\n\n// ── Prune fields editor (reused by both Prune Now and Schedule) ─────────────\n\nexport function PruneFieldsEditor({ fields, onChange }: {\n fields: PruneFields;\n onChange: (f: PruneFields) => void;\n}) {\n const set = <K extends keyof PruneFields>(key: K, value: PruneFields[K]) =>\n onChange({ ...fields, [key]: value });\n\n return (\n <div className=\"space-y-8\">\n {/* Stream messages — always safe to prune */}\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Stream Messages\n </p>\n <p className=\"text-[10px] text-text-tertiary mb-4\">\n Processed routing messages. Already consumed — safe to remove after a short retention window.\n </p>\n <div className=\"space-y-3\">\n <RetentionRow\n checked={fields.engineStreams} onToggle={(v) => set('engineStreams', v)}\n label=\"Engine messages\" hint=\"Internal orchestration signals\"\n safety=\"safe\"\n value={fields.engineStreamsExpire} onChange={(v) => set('engineStreamsExpire', v)}\n />\n <RetentionRow\n checked={fields.workerStreams} onToggle={(v) => set('workerStreams', v)}\n label=\"Worker messages\" hint=\"Activity dispatch and response payloads\"\n safety=\"safe\"\n value={fields.workerStreamsExpire} onChange={(v) => set('workerStreamsExpire', v)}\n />\n </div>\n </div>\n\n {/* Workflow data — mutually exclusive: reduce OR delete */}\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Completed Workflows\n </p>\n <p className=\"text-[10px] text-text-tertiary mb-4\">\n Choose how to handle completed workflow records. Reducing strips step-level detail\n but keeps the workflow and its results. Deleting removes the record entirely.\n </p>\n <div className=\"space-y-3\">\n <label className=\"flex items-start gap-2.5 cursor-pointer\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={!fields.stripAttributes && !fields.pruneJobs}\n onChange={() => onChange({ ...fields, stripAttributes: false, pruneJobs: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <span className=\"text-xs text-text-primary\">Keep as-is</span>\n <p className=\"text-[10px] text-text-tertiary\">No changes to completed workflow records.</p>\n </div>\n </label>\n\n <div className=\"flex items-start gap-2.5\">\n <label className=\"flex items-start gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={fields.stripAttributes && !fields.pruneJobs}\n onChange={() => onChange({ ...fields, stripAttributes: true, pruneJobs: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">Reduce completed workflows</span>\n <span className=\"flex items-center gap-0.5 text-[9px] text-status-warning\">\n <ShieldAlert className=\"w-3 h-3\" strokeWidth={1.5} />\n Careful\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">\n Strips step-level execution detail (activity inputs/outputs, internal state).\n Workflow results and timeline are preserved.\n </p>\n </div>\n </label>\n <select\n value={fields.expire}\n onChange={(e) => set('expire', e.target.value)}\n disabled={!fields.stripAttributes || fields.pruneJobs}\n className={`select text-xs w-36 transition-opacity mt-0.5 ${!fields.stripAttributes || fields.pruneJobs ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n\n <div className=\"flex items-start gap-2.5\">\n <label className=\"flex items-start gap-2.5 cursor-pointer w-80 shrink-0\">\n <input\n type=\"radio\"\n name=\"workflow-cleanup\"\n checked={fields.pruneJobs}\n onChange={() => onChange({ ...fields, pruneJobs: true, stripAttributes: false })}\n className=\"w-4 h-4 mt-0.5 accent-accent shrink-0\"\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-primary\">Delete completed workflows</span>\n <span className=\"flex items-center gap-0.5 text-[9px] text-status-error\">\n <ShieldOff className=\"w-3 h-3\" strokeWidth={1.5} />\n Permanent\n </span>\n </div>\n <p className=\"text-[10px] text-text-tertiary\">\n Permanently removes workflow records. Results, timeline, and all\n execution data are deleted and cannot be recovered.\n </p>\n </div>\n </label>\n <select\n value={fields.expire}\n onChange={(e) => set('expire', e.target.value)}\n disabled={!fields.pruneJobs}\n className={`select text-xs w-36 transition-opacity mt-0.5 ${!fields.pruneJobs ? 'opacity-40 cursor-not-allowed' : ''}`}\n >\n {RETENTION_PERIOD_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n </div>\n <CleanupCallout />\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { usePrune, type PruneResult } from '../../../api/maintenance';\nimport { Modal } from '../../../components/common/modal/Modal';\nimport { PruneFieldsEditor, DEFAULT_PRUNE_FIELDS, hasSelection, type PruneFields } from './controls';\n\nfunction ResultCard({ label, value }: { label: string; value: number }) {\n return (\n <div>\n <p className=\"text-lg font-light text-text-primary\">{value.toLocaleString()}</p>\n <p className=\"text-[10px] text-text-tertiary\">{label}</p>\n </div>\n );\n}\n\nexport function PruneSection() {\n const prune = usePrune();\n const [fields, setFields] = useState<PruneFields>(DEFAULT_PRUNE_FIELDS);\n const [result, setResult] = useState<PruneResult | null>(null);\n const [showConfirm, setShowConfirm] = useState(false);\n\n const handlePrune = () => {\n setResult(null);\n prune.mutate(\n {\n expire: fields.expire,\n jobs: fields.pruneJobs,\n engineStreams: fields.engineStreams,\n engineStreamsExpire: fields.engineStreamsExpire,\n workerStreams: fields.workerStreams,\n workerStreamsExpire: fields.workerStreamsExpire,\n attributes: fields.stripAttributes,\n pruneTransient: fields.pruneTransient,\n },\n {\n onSuccess: (data) => {\n setResult(data);\n setShowConfirm(false);\n },\n },\n );\n };\n\n return (\n <div className=\"space-y-8\">\n <PruneFieldsEditor fields={fields} onChange={setFields} />\n\n {/* Action bar */}\n <div className=\"flex items-center justify-between pt-2 border-t border-surface-border\">\n <p className=\"text-[10px] text-text-tertiary\">\n {!hasSelection(fields)\n ? 'Select at least one operation to enable pruning.'\n : 'This action permanently deletes data and cannot be undone.'}\n </p>\n <button\n onClick={() => setShowConfirm(true)}\n disabled={!hasSelection(fields) || prune.isPending}\n className=\"bg-status-error text-white px-4 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity disabled:opacity-40 shrink-0\"\n >\n {prune.isPending ? 'Pruning...' : 'Prune Now'}\n </button>\n </div>\n\n {/* Results */}\n {result && (\n <div className=\"bg-surface-sunken rounded-md px-4 py-3\">\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-2\">\n Results\n </p>\n <div className=\"flex flex-wrap gap-6\">\n {result.jobs != null && result.jobs > 0 && <ResultCard label=\"Jobs deleted\" value={result.jobs} />}\n {result.engineStreams != null && result.engineStreams > 0 && <ResultCard label=\"Engine streams\" value={result.engineStreams} />}\n {result.workerStreams != null && result.workerStreams > 0 && <ResultCard label=\"Worker streams\" value={result.workerStreams} />}\n {result.streams != null && result.engineStreams == null && result.streams > 0 && <ResultCard label=\"Streams\" value={result.streams} />}\n {result.attributes != null && result.attributes > 0 && <ResultCard label=\"Artifacts stripped\" value={result.attributes} />}\n {result.transient != null && result.transient > 0 && <ResultCard label=\"Transient deleted\" value={result.transient} />}\n {result.marked != null && result.marked > 0 && <ResultCard label=\"Marked pruned\" value={result.marked} />}\n {Object.values(result).every((v) => !v || v === 0) && (\n <p className=\"text-xs text-text-tertiary\">Nothing to prune within the selected retention windows.</p>\n )}\n </div>\n </div>\n )}\n\n {prune.error && (\n <p className=\"text-xs text-status-error\">{(prune.error as Error).message}</p>\n )}\n\n {/* Confirm modal */}\n <Modal open={showConfirm} onClose={() => setShowConfirm(false)} title=\"Confirm Prune\">\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n This will permanently delete data. This action cannot be undone.\n </p>\n <ul className=\"text-xs text-text-secondary space-y-1 list-disc list-inside\">\n {fields.pruneJobs && <li>Delete jobs older than {fields.expire}</li>}\n {fields.engineStreams && <li>Delete engine streams older than {fields.engineStreamsExpire}</li>}\n {fields.workerStreams && <li>Delete worker streams older than {fields.workerStreamsExpire}</li>}\n {fields.stripAttributes && <li>Strip execution artifacts from completed jobs</li>}\n {fields.pruneTransient && <li>Delete transient (entity-less) jobs</li>}\n </ul>\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={() => setShowConfirm(false)} className=\"btn-secondary text-xs\">Cancel</button>\n <button\n onClick={handlePrune}\n className=\"bg-status-error text-white px-3 py-1.5 rounded-md text-xs hover:opacity-90 transition-opacity\"\n disabled={prune.isPending}\n >\n {prune.isPending ? 'Pruning...' : 'Confirm Prune'}\n </button>\n </div>\n </div>\n </Modal>\n </div>\n );\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { Info } from 'lucide-react';\nimport {\n useMaintenanceConfig,\n useUpdateMaintenanceConfig,\n type MaintenanceConfig,\n} from '../../../api/maintenance';\nimport { CRON_PRESETS } from '../../../lib/constants';\nimport { PruneFieldsEditor, DEFAULT_PRUNE_FIELDS, type PruneFields } from './controls';\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Convert a MaintenanceConfig (rules array) into PruneFields for the editor. */\nfunction configToFields(config: MaintenanceConfig | undefined): PruneFields {\n if (!config?.rules?.length) return DEFAULT_PRUNE_FIELDS;\n const r = config.rules;\n const streamDelete = r.find((x) => x.target === 'streams' && x.action === 'delete');\n const jobDeleteTransient = r.find((x) => x.target === 'jobs' && x.action === 'delete' && x.hasEntity === false);\n const jobPrune = r.find((x) => x.target === 'jobs' && x.action === 'prune' && x.hasEntity === true);\n const jobDeletePruned = r.find((x) => x.target === 'jobs' && x.action === 'delete' && x.pruned === true);\n return {\n pruneJobs: !!jobDeletePruned,\n expire: jobDeletePruned?.olderThan ?? '180 days',\n engineStreams: !!streamDelete,\n engineStreamsExpire: streamDelete?.olderThan ?? '1 day',\n workerStreams: !!streamDelete,\n workerStreamsExpire: streamDelete?.olderThan ?? '90 days',\n stripAttributes: !!jobPrune,\n pruneTransient: !!jobDeleteTransient,\n };\n}\n\n/** Convert PruneFields back to a MaintenanceConfig rules array. */\nfunction fieldsToRules(f: PruneFields): MaintenanceConfig['rules'] {\n const rules: MaintenanceConfig['rules'] = [];\n if (f.engineStreams) {\n rules.push({ target: 'streams', action: 'delete', olderThan: f.engineStreamsExpire });\n }\n if (f.workerStreams && f.workerStreamsExpire !== f.engineStreamsExpire) {\n rules.push({ target: 'streams', action: 'delete', olderThan: f.workerStreamsExpire });\n }\n if (f.pruneTransient) {\n rules.push({ target: 'jobs', action: 'delete', olderThan: f.expire, hasEntity: false });\n }\n if (f.stripAttributes) {\n rules.push({ target: 'jobs', action: 'prune', olderThan: f.expire, hasEntity: true });\n }\n if (f.pruneJobs) {\n rules.push({ target: 'jobs', action: 'delete', olderThan: f.expire, pruned: true });\n }\n return rules;\n}\n\nfunction describeCron(expr: string): string {\n return CRON_PRESETS.find((p) => p.value === expr)?.label ?? '';\n}\n\n// ── Main ────────────────────────────────────────────────────────────────────\n\nexport function ScheduleSection() {\n const { data, isLoading } = useMaintenanceConfig();\n const updateConfig = useUpdateMaintenanceConfig();\n\n const [schedule, setSchedule] = useState('');\n const [fields, setFields] = useState<PruneFields>(DEFAULT_PRUNE_FIELDS);\n const [savedSnapshot, setSavedSnapshot] = useState('');\n\n // Sync from server config when loaded\n const syncFromServer = useCallback(() => {\n if (!data?.config) return;\n setSchedule(data.config.schedule);\n setFields(configToFields(data.config));\n setSavedSnapshot(JSON.stringify({ schedule: data.config.schedule, fields: configToFields(data.config) }));\n }, [data]);\n\n useEffect(() => { syncFromServer(); }, [syncFromServer]);\n\n const currentSnapshot = JSON.stringify({ schedule, fields });\n const isDirty = savedSnapshot !== '' && currentSnapshot !== savedSnapshot;\n const active = data?.active ?? false;\n\n const handleSave = () => {\n const rules = fieldsToRules(fields);\n updateConfig.mutate(\n { schedule, rules },\n {\n onSuccess: () => {\n setSavedSnapshot(JSON.stringify({ schedule, fields }));\n },\n },\n );\n };\n\n const handleRevert = () => {\n syncFromServer();\n };\n\n if (isLoading) {\n return <div className=\"animate-pulse h-40 bg-surface-sunken rounded\" />;\n }\n\n return (\n <div className=\"space-y-8\">\n {/* Cron schedule + status */}\n <div className=\"grid grid-cols-1 lg:grid-cols-[1fr_auto] gap-10\">\n <div>\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4\">\n Schedule\n </p>\n <div className=\"flex items-end gap-4\">\n <div>\n <label className=\"block text-[10px] text-text-tertiary mb-1\">Cron Expression</label>\n <input\n type=\"text\"\n value={schedule}\n onChange={(e) => setSchedule(e.target.value)}\n placeholder=\"0 2 * * *\"\n className=\"input text-xs font-mono w-48\"\n />\n </div>\n <div>\n <div className=\"flex items-center gap-1.5 h-8\">\n <span className={`w-1.5 h-1.5 rounded-full ${active ? 'bg-status-success' : 'bg-text-tertiary'}`} />\n <span className={`text-xs ${active ? 'text-status-success' : 'text-text-tertiary'}`}>\n {active ? 'Active' : 'Inactive'}\n </span>\n </div>\n </div>\n {describeCron(schedule) && (\n <p className=\"text-xs text-text-tertiary h-8 flex items-center\">{describeCron(schedule)}</p>\n )}\n </div>\n\n {/* Preset pills */}\n <div className=\"flex flex-wrap gap-1.5 mt-3\">\n {CRON_PRESETS.map((preset) => (\n <button\n key={preset.value}\n type=\"button\"\n onClick={() => setSchedule(preset.value)}\n className={`px-2.5 py-1 text-[10px] rounded-full transition-colors ${\n schedule === preset.value\n ? 'bg-accent/10 text-accent font-medium'\n : 'bg-surface-sunken text-text-tertiary hover:text-text-secondary'\n }`}\n >\n {preset.label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"lg:w-72\">\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-4\">\n How It Works\n </p>\n <div className=\"flex items-start gap-2 px-3 py-2 rounded bg-surface-sunken\">\n <Info className=\"w-3.5 h-3.5 text-text-tertiary shrink-0 mt-0.5\" />\n <p className=\"text-[10px] text-text-tertiary leading-relaxed\">\n Rules execute sequentially on each cron cycle. Engine streams can be pruned aggressively\n since they only contain internal routing data. Worker streams should be retained longer\n to preserve execution playback and input enrichment.\n </p>\n </div>\n </div>\n </div>\n\n {/* Same prune fields as Prune Now */}\n <PruneFieldsEditor fields={fields} onChange={setFields} />\n\n {/* Action bar */}\n <div className=\"flex items-center justify-between pt-2 border-t border-surface-border\">\n <div className=\"flex items-center gap-3\">\n {isDirty && (\n <button\n onClick={handleRevert}\n className=\"text-[10px] text-text-tertiary hover:text-text-primary transition-colors\"\n >\n Revert changes\n </button>\n )}\n {updateConfig.error && (\n <p className=\"text-xs text-status-error\">{(updateConfig.error as Error).message}</p>\n )}\n </div>\n <button\n onClick={handleSave}\n disabled={!isDirty || !schedule.trim() || updateConfig.isPending}\n className=\"btn-primary text-xs disabled:opacity-40 shrink-0\"\n >\n {updateConfig.isPending ? 'Saving...' : 'Save Schedule'}\n </button>\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { Trash2, Clock, Info } from 'lucide-react';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { PruneSection } from './PruneSection';\nimport { ScheduleSection } from './ScheduleSection';\n\ntype Mode = 'prune' | 'schedule';\n\nfunction ModeToggle({ mode, onChange }: { mode: Mode; onChange: (m: Mode) => void }) {\n const btn = (m: Mode, icon: React.ReactNode, label: string) => (\n <button\n onClick={() => onChange(m)}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md transition-colors ${\n mode === m\n ? 'bg-accent/10 text-accent font-medium'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-surface-hover'\n }`}\n >\n {icon}\n {label}\n </button>\n );\n\n return (\n <div className=\"flex gap-1 p-0.5 bg-surface-sunken rounded-lg w-fit\">\n {btn('prune', <Trash2 className=\"w-3.5 h-3.5\" />, 'Prune Now')}\n {btn('schedule', <Clock className=\"w-3.5 h-3.5\" />, 'Schedule')}\n </div>\n );\n}\n\nexport function MaintenancePage() {\n const [mode, setMode] = useState<Mode>('schedule');\n\n return (\n <div>\n <PageHeader\n title=\"DB Maintenance\"\n docsHash=\"#docs:dashboard.md:db-maintenance\"\n actions={<ModeToggle mode={mode} onChange={setMode} />}\n />\n\n <div className=\"flex items-start gap-2 px-4 py-3 mb-8 rounded-md bg-accent/5 border border-accent/10\">\n <Info className=\"w-4 h-4 text-accent shrink-0 mt-0.5\" />\n <p className=\"text-xs text-text-secondary leading-relaxed\">\n Workflow data grows as jobs complete. Operations are ordered safest-first: stream messages are\n always safe to prune, workflow reduction preserves results, and deletion is permanent.\n </p>\n </div>\n\n {mode === 'prune' ? <PruneSection /> : <ScheduleSection />}\n </div>\n );\n}\n"],"names":["useMaintenanceConfig","useQuery","apiFetch","useUpdateMaintenanceConfig","queryClient","useQueryClient","useMutation","config","usePrune","options","RetentionRow","checked","onToggle","label","hint","safety","value","onChange","SafetyIcon","ShieldCheck","ShieldAlert","ShieldOff","safetyColor","safetyLabel","jsxs","jsx","e","RETENTION_PERIOD_OPTIONS","opt","CleanupCallout","Info","DEFAULT_PRUNE_FIELDS","hasSelection","f","PruneFieldsEditor","fields","set","key","v","ResultCard","PruneSection","prune","setFields","useState","result","setResult","showConfirm","setShowConfirm","handlePrune","data","Modal","configToFields","_a","streamDelete","x","jobDeleteTransient","jobPrune","jobDeletePruned","fieldsToRules","rules","describeCron","expr","CRON_PRESETS","p","ScheduleSection","isLoading","updateConfig","schedule","setSchedule","savedSnapshot","setSavedSnapshot","syncFromServer","useCallback","useEffect","currentSnapshot","isDirty","active","handleSave","handleRevert","preset","ModeToggle","mode","btn","m","icon","Trash2","Clock","MaintenancePage","setMode","PageHeader"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{a as m,j as e}from"./vendor-query-B2UbickB.js";import{d as te,h as Q,u as se,i as re,b as ne}from"./workflows-BTkHs6VT.js";import{C as M}from"./ConfirmDeleteModal-D9_1b4MW.js";import{R as $}from"./RolePill-CXEyPMqb.js";import{W as oe}from"./WorkflowPill-BuNkWUVM.js";import{P as ae}from"./PageHeader-CFta2VaS.js";import{ai as le,t as L,ab as ie,aj as D,ad as U,ak as ce,P as z,al as de,am as B,y as xe,U as O,X as H,i as G,V as K,W as T,C as me}from"./vendor-icons-BaQqUm-e.js";import{c as V,e as ue,f as pe}from"./vendor-react-CXumBFUA.js";import{u as he}from"./roles-UboiQxXT.js";import{B as fe}from"./BotPicker-xGUCxJo-.js";import{N as we}from"./NamespacePill-DlqBTvFR.js";import"./index-CKXSKsVZ.js";import"./Modal-CSrxpXeM.js";import"./bots-CyzN6Uxi.js";function ge({tier:t}){return t==="certified"?e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] font-medium text-status-success",children:[e.jsx(U,{className:"w-3 h-3"}),"Certified"]}):t==="registered"?e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] font-medium text-text-secondary",children:[e.jsx(xe,{className:"w-3 h-3"}),"Registered"]}):e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] font-medium text-text-quaternary",children:[e.jsx(D,{className:"w-3 h-3"}),"Durable"]})}function Le(){const t=V(),{data:a,isLoading:x}=te(),h=Q(),[c,g]=m.useState(""),[o,j]=m.useState(null),[v,k]=m.useState(null),n=a??[],d=m.useMemo(()=>[...new Set(n.map(l=>l.task_queue).filter(Boolean))].sort(),[n]),i=m.useMemo(()=>{const l=c.toLowerCase().trim();return(o?[o]:d).map(f=>({queue:f,workflows:n.filter(_=>{var p;return _.task_queue===f&&(!l||_.workflow_type.toLowerCase().includes(l)||((p=_.description)==null?void 0:p.toLowerCase().includes(l)))})})).filter(f=>f.workflows.length>0)},[n,d,c,o]),u=m.useMemo(()=>{const l=c.toLowerCase().trim();return o?[]:n.filter(w=>{var f;return!w.task_queue&&(!l||w.workflow_type.toLowerCase().includes(l)||((f=w.description)==null?void 0:f.toLowerCase().includes(l)))})},[n,c,o]),r=l=>{l.registered?t(`/workflows/registry/${encodeURIComponent(l.workflow_type)}`):t(`/workflows/registry/new?workflow_type=${encodeURIComponent(l.workflow_type)}&task_queue=${encodeURIComponent(l.task_queue??"")}`)},C=()=>{v&&h.mutate(v,{onSuccess:()=>k(null)})};return e.jsxs("div",{children:[e.jsx(ae,{title:"Registered Workflows",docsHash:"#docs:dashboard.md:workflow-registry",actions:e.jsx("button",{onClick:()=>t("/workflows/registry/new"),className:"btn-primary text-xs",children:"Register Workflow"})}),!x&&d.length>0&&e.jsx("div",{className:"sticky top-0 z-20 bg-surface pt-3 pb-3",children:e.jsxs("div",{className:"bg-[#F7F7F7] rounded-lg px-5 pt-3 pb-3 flex items-center gap-5",children:[e.jsxs("button",{onClick:()=>j(null),className:`flex flex-col items-center gap-1 transition-colors ${o===null?"text-accent":"text-text-quaternary hover:text-text-secondary"}`,children:[e.jsx(le,{className:"w-3 h-3",strokeWidth:1.5}),e.jsx("span",{className:"text-[9px] font-medium",children:"All"})]}),d.map(l=>{const w=o===l,f=l.length>16?l.slice(0,14)+"…":l;return e.jsxs("button",{onClick:()=>j(w?null:l),className:`flex flex-col items-center gap-1 transition-colors ${w?"text-accent":"text-text-quaternary hover:text-text-secondary"}`,title:l,children:[e.jsx(L,{className:"w-3 h-3",strokeWidth:1.5}),e.jsx("span",{className:"text-[9px] font-medium",children:f})]},l)}),e.jsx("span",{className:"flex-1"}),e.jsxs("div",{className:"relative",children:[e.jsx(ie,{className:"absolute left-0 top-1/2 -translate-y-1/2 w-3 h-3 text-text-quaternary"}),e.jsx("input",{type:"text",value:c,onChange:l=>g(l.target.value),placeholder:`Search ${n.length} workflows…`,className:"pl-5 py-1 text-sm bg-transparent border-b border-surface-border/60 text-text-primary placeholder:text-text-quaternary focus:outline-none focus:border-accent/50 transition-colors w-52"})]})]})}),x?e.jsx(je,{}):i.length===0&&u.length===0?e.jsx("p",{className:"text-sm text-text-tertiary mt-8",children:c||o?"No workflows match your filter.":"No workflows discovered yet."}):e.jsxs("div",{className:"space-y-10",children:[i.map(({queue:l,workflows:w})=>e.jsxs("div",{children:[e.jsxs("div",{className:"sticky top-[78px] z-10 bg-surface flex items-center gap-2 py-2 mb-2 border-b border-surface-border",children:[e.jsx(L,{className:"w-3 h-3 text-accent",strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:l}),e.jsx("span",{className:"text-xs text-text-quaternary",children:w.length})]}),e.jsx(F,{}),e.jsx("div",{className:"divide-y divide-surface-border/30",children:w.map(f=>e.jsx(J,{wf:f,onRowClick:r,onDelete:k,onNavigate:t},f.workflow_type))})]},l)),u.length>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"sticky top-[78px] z-10 bg-surface flex items-center gap-2 py-2 mb-2 border-b border-surface-border",children:[e.jsx(D,{className:"w-3 h-3 text-text-quaternary",strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:"No Queue"}),e.jsx("span",{className:"text-xs text-text-quaternary",children:u.length})]}),e.jsx(F,{}),e.jsx("div",{className:"divide-y divide-surface-border/30",children:u.map(l=>e.jsx(J,{wf:l,onRowClick:r,onDelete:k,onNavigate:t},l.workflow_type))})]})]}),e.jsx(M,{open:!!v,onClose:()=>k(null),onConfirm:C,title:"Unregister Workflow",description:e.jsxs(e.Fragment,{children:["Delete the registration for"," ",e.jsx("span",{className:"font-mono font-medium text-text-primary",children:v}),"? This removes its invocation settings, certification, and interceptor treatment; the workflow keeps running as a plain durable workflow. To demote certified → registered instead, open the workflow and uncheck Certify — that keeps every setting."]}),isPending:h.isPending,error:h.error})]})}const X="grid grid-cols-[minmax(0,1fr)_7rem_13rem_4.5rem] gap-x-6 items-start";function F(){return e.jsxs("div",{className:`${X} px-3 pb-2 pt-1`,children:[e.jsx("span",{className:"text-[10px] font-medium text-text-quaternary",children:"Workflow"}),e.jsx("span",{className:"text-[10px] font-medium text-text-quaternary",children:"Tier"}),e.jsx("span",{className:"text-[10px] font-medium text-text-quaternary",children:"Access"}),e.jsx("span",{})]})}function J({wf:t,onRowClick:a,onDelete:x,onNavigate:h}){const c=t.roles??[],g=t.invocation_roles??[];return e.jsxs("div",{role:"button",tabIndex:0,onClick:()=>a(t),onKeyDown:o=>{(o.key==="Enter"||o.key===" ")&&a(t)},className:`group ${X} py-2.5 px-3 -mx-3 rounded-md cursor-pointer transition-colors`,children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx(oe,{type:t.workflow_type,size:"md",variant:t.tier==="certified"?"certified":t.tier==="registered"?"registered":"durable"}),t.description&&e.jsx("p",{className:"mt-0.5 text-[10px] text-text-tertiary group-hover:text-text-secondary leading-snug line-clamp-2 transition-colors",children:t.description})]}),e.jsx("div",{className:"pt-0.5",children:e.jsx(ge,{tier:t.tier??"durable"})}),e.jsxs("div",{className:"min-w-0 space-y-1 pt-0.5",children:[c.length>0&&e.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap",children:[e.jsx("span",{title:"Escalation roles",children:e.jsx(U,{className:"w-3 h-3 text-text-quaternary shrink-0"})}),c.map(o=>e.jsx($,{role:o},`e-${o}`))]}),g.length>0&&e.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap",children:[e.jsx("span",{title:"Invocation roles",children:e.jsx(ce,{className:"w-3 h-3 text-text-quaternary shrink-0"})}),g.map(o=>e.jsx($,{role:o},`i-${o}`))]}),c.length===0&&g.length===0&&e.jsx("span",{className:"text-xs text-text-quaternary",children:"—"})]}),e.jsxs("div",{className:"flex items-center justify-end gap-2.5 pt-0.5 opacity-0 group-hover:opacity-100 transition-opacity",children:[t.invocable&&e.jsx("button",{title:"Invoke workflow",onClick:o=>{o.stopPropagation(),h(`/workflows/start?type=${encodeURIComponent(t.workflow_type)}&mode=now`)},className:"text-text-quaternary hover:text-status-success transition-colors",children:e.jsx(z,{className:"w-3.5 h-3.5"})}),t.tier==="durable"&&e.jsx("button",{title:"Configure workflow",onClick:o=>{o.stopPropagation(),h(`/workflows/registry/new?workflow_type=${encodeURIComponent(t.workflow_type)}&task_queue=${encodeURIComponent(t.task_queue??"")}`)},className:"text-text-quaternary hover:text-status-info transition-colors",children:e.jsx(D,{className:"w-3.5 h-3.5"})}),t.tier==="registered"&&e.jsx("button",{title:"Certify workflow",onClick:o=>{o.stopPropagation(),h(`/workflows/registry/${encodeURIComponent(t.workflow_type)}`)},className:"text-text-quaternary hover:text-status-success transition-colors",children:e.jsx(de,{className:"w-3.5 h-3.5"})}),t.registered&&e.jsx("button",{title:"Unregister workflow",onClick:o=>{o.stopPropagation(),x(t.workflow_type)},className:"text-text-quaternary hover:text-status-warning transition-colors",children:e.jsx(B,{className:"w-3.5 h-3.5"})})]})]})}function je(){return e.jsx("div",{className:"animate-pulse space-y-8 mt-4",children:[1,2].map(t=>e.jsxs("div",{children:[e.jsx("div",{className:"h-4 bg-surface-sunken rounded w-36 mb-4"}),e.jsx("div",{className:"space-y-4",children:[1,2,3].map(a=>e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("div",{className:"h-3 bg-surface-sunken rounded w-48"}),e.jsx("div",{className:"h-3 bg-surface-sunken rounded w-full"})]},a))})]},t))})}function q({selected:t,onChange:a,single:x,placeholder:h}){const{data:c}=he(),g=(c==null?void 0:c.roles)??[],[o,j]=m.useState(!1),v=m.useRef(null);m.useEffect(()=>{const i=u=>{v.current&&!v.current.contains(u.target)&&j(!1)};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[]);const k=i=>{x?(a(t.includes(i)?[]:[i]),j(!1)):a(t.includes(i)?t.filter(u=>u!==i):[...t,i])},n=i=>{a(t.filter(u=>u!==i))},d=h??(x?"Select role...":"Add roles...");return e.jsxs("div",{ref:v,className:"relative",children:[e.jsxs("button",{type:"button",onClick:()=>j(!o),className:"flex flex-wrap items-center gap-1.5 w-full min-h-[34px] px-2 py-1.5 bg-surface-sunken border border-surface-border rounded-md text-left cursor-pointer hover:border-accent/40 transition-colors focus:ring-1 focus:ring-accent focus:outline-none",children:[t.length===0&&e.jsx("span",{className:"text-xs text-text-tertiary",children:d}),t.map(i=>e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-0.5 rounded-lg bg-accent/[0.08] text-text-secondary text-[11px]",children:[e.jsx(O,{className:"w-2.5 h-2.5 shrink-0 text-accent/75"}),i,e.jsx("span",{role:"button",onClick:u=>{u.stopPropagation(),n(i)},className:"ml-0.5 hover:text-status-error transition-colors",children:e.jsx(H,{className:"w-2.5 h-2.5"})})]},i)),e.jsx(G,{className:`w-3.5 h-3.5 ml-auto shrink-0 text-text-tertiary transition-transform ${o?"rotate-180":""}`})]}),o&&e.jsx("div",{className:"absolute z-20 mt-1 w-full bg-white border border-surface-border rounded-md shadow-lg max-h-48 overflow-y-auto",children:g.length===0?e.jsx("p",{className:"px-3 py-2 text-xs text-text-tertiary",children:"No roles defined"}):g.map(i=>{const u=t.includes(i);return e.jsxs("button",{type:"button",onClick:()=>k(i),className:`flex items-center gap-2 w-full px-3 py-1.5 text-left text-xs transition-colors ${u?"bg-accent/[0.06] text-accent":"text-text-primary hover:bg-surface-sunken"}`,children:[e.jsx(O,{className:"w-3 h-3 shrink-0 text-accent/60"}),e.jsx("span",{className:"flex-1",children:i}),u&&e.jsx(K,{className:"w-3 h-3 shrink-0"})]},i)})})]})}function ve({options:t,selected:a,onChange:x,placeholder:h}){const[c,g]=m.useState(!1),o=m.useRef(null);m.useEffect(()=>{const n=d=>{o.current&&!o.current.contains(d.target)&&g(!1)};return document.addEventListener("mousedown",n),()=>document.removeEventListener("mousedown",n)},[]);const j=n=>{x(a.includes(n)?a.filter(d=>d!==n):[...a,n])},v=n=>{x(a.filter(d=>d!==n))},k=h??"Add workflows...";return e.jsxs("div",{ref:o,className:"relative",children:[e.jsxs("button",{type:"button",onClick:()=>g(!c),className:"flex flex-wrap items-center gap-1.5 w-full min-h-[34px] px-2 py-1.5 bg-surface-sunken border border-surface-border rounded-md text-left cursor-pointer hover:border-accent/40 transition-colors focus:ring-1 focus:ring-accent focus:outline-none",children:[a.length===0&&e.jsx("span",{className:"text-xs text-text-tertiary",children:k}),a.map(n=>e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-0.5 rounded-lg bg-accent/[0.08] text-text-secondary text-[11px] font-mono",children:[e.jsx(T,{className:"w-2.5 h-2.5 shrink-0 text-accent/75"}),n,e.jsx("span",{role:"button",onClick:d=>{d.stopPropagation(),v(n)},className:"ml-0.5 hover:text-status-error transition-colors",children:e.jsx(H,{className:"w-2.5 h-2.5"})})]},n)),e.jsx(G,{className:`w-3.5 h-3.5 ml-auto shrink-0 text-text-tertiary transition-transform ${c?"rotate-180":""}`})]}),c&&e.jsx("div",{className:"absolute z-20 mt-1 w-full bg-white border border-surface-border rounded-md shadow-lg max-h-48 overflow-y-auto",children:t.length===0?e.jsx("p",{className:"px-3 py-2 text-xs text-text-tertiary",children:"No registered workflows"}):t.map(n=>{const d=a.includes(n);return e.jsxs("button",{type:"button",onClick:()=>j(n),className:`flex items-center gap-2 w-full px-3 py-1.5 text-left text-xs font-mono transition-colors ${d?"bg-accent/[0.06] text-accent":"text-text-primary hover:bg-surface-sunken"}`,children:[e.jsx(T,{className:"w-3 h-3 shrink-0 text-accent/60"}),e.jsx("span",{className:"flex-1",children:n}),d&&e.jsx(K,{className:"w-3 h-3 shrink-0"})]},n)})})]})}function P(t){return t.split(",").map(a=>a.trim()).filter(Boolean)}const W={workflow_type:"",description:"",task_queue:"",default_role:"reviewer",invocable:!1,roles:"",invocation_roles:"",consumes:"",envelope_schema:"",resolver_schema:"",cron_schedule:"",execute_as:"",certified:!1};function ke(t){const a=(t.roles??[]).join(", "),x=(t.consumes??[]).join(", ");return{workflow_type:t.workflow_type,description:t.description??"",task_queue:t.task_queue??"",default_role:t.default_role,invocable:t.invocable,roles:a,invocation_roles:(t.invocation_roles??[]).join(", "),consumes:x,envelope_schema:t.envelope_schema?JSON.stringify(t.envelope_schema,null,2):"",resolver_schema:t.resolver_schema?JSON.stringify(t.resolver_schema,null,2):"",cron_schedule:t.cron_schedule??"",execute_as:t.execute_as??"",certified:t.certified}}function S(t){if(!t.trim())return!0;try{return JSON.parse(t),!0}catch{return!1}}function E({icon:t,color:a,children:x}){return e.jsxs("div",{className:"flex items-center gap-2 mb-4 pb-2 border-b border-surface-border",children:[e.jsx(t,{className:`w-4 h-4 ${a}`,strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:x})]})}function y({label:t,hint:a,children:x}){return e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"label",children:t}),x,a&&e.jsx("p",{className:"hint",children:a})]})}const A="input-json w-full";function R(t){return P(t)}function I(t){return t.join(", ")}const ye=new Set(["mcpQuery","mcpDeterministic","mcpQueryRouter","mcpTriage","mcpTriageRouter","mcpTriageDeterministic","insightQuery"]);function Oe(){const{workflowType:t}=ue(),a=!t,x=V(),[h]=pe(),{data:c,isLoading:g}=se(),o=re(),j=Q(),[v,k]=m.useState(!1),n=(c==null?void 0:c.find(s=>s.workflow_type===t))??null,{data:d}=ne({limit:500}),i=m.useMemo(()=>{const s=new Set((c??[]).map(b=>b.workflow_type));return[...new Set(((d==null?void 0:d.jobs)??[]).map(b=>b.entity))].filter(b=>!s.has(b)&&!ye.has(b)).sort()},[c,d]),u=m.useMemo(()=>{if(!a)return W;const s=h.get("workflow_type")??"",N=h.get("task_queue")??"";return s||N?{...W,workflow_type:s,task_queue:N}:W},[a,h]),[r,C]=m.useState(u),[l,w]=m.useState(""),[f,_]=m.useState(!1),p=m.useCallback((s,N)=>C(b=>({...b,[s]:N})),[]);m.useEffect(()=>{if(!f){if(a){C(u),_(!0);return}n&&(C(ke(n)),_(!0))}},[n,a,f,u]);const Y=()=>{if(!r.workflow_type.trim())return;w("");let s=null,N=null;try{r.envelope_schema.trim()&&(s=JSON.parse(r.envelope_schema))}catch{w("Invalid JSON in Envelope Schema");return}try{r.resolver_schema.trim()&&(N=JSON.parse(r.resolver_schema))}catch{w("Invalid JSON in Resolver Schema");return}o.mutate({workflow_type:r.workflow_type.trim(),description:r.description.trim()||null,task_queue:r.task_queue.trim()||null,default_role:r.default_role.trim()||"reviewer",invocable:r.invocable,certified:r.certified,roles:P(r.roles),invocation_roles:P(r.invocation_roles),consumes:P(r.consumes),envelope_schema:s,resolver_schema:N,cron_schedule:r.cron_schedule.trim()||null,execute_as:r.execute_as.trim()||null},{onSuccess:()=>x("/workflows/registry")})};if(g)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-40 bg-surface-sunken rounded"})]});if(!a&&!n)return e.jsx("p",{className:"text-sm text-text-secondary",children:"Config not found."});const Z=!!r.workflow_type.trim()&&S(r.envelope_schema)&&S(r.resolver_schema),ee=a&&!r.workflow_type&&i.length>0;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-8",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-light font-mono text-text-primary",children:a?"New Workflow":(n==null?void 0:n.workflow_type)??""}),n&&e.jsx("div",{className:"flex items-center gap-2 mt-2",children:e.jsx(we,{namespace:"durable"})})]}),e.jsxs("div",{className:"flex items-center gap-3 mt-1",children:[e.jsx("button",{onClick:()=>x("/workflows/registry"),className:"btn-ghost text-xs",children:"Cancel"}),e.jsx("button",{onClick:Y,disabled:!Z||o.isPending,className:"btn-primary text-xs",children:o.isPending?"Saving…":a?"Register":"Save"})]})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-x-14 gap-y-10",children:[e.jsxs("div",{children:[e.jsx(E,{icon:me,color:"text-accent",children:"Identity"}),e.jsxs("div",{className:"space-y-5",children:[e.jsx(y,{label:"Workflow Type",hint:"Register a workflow to configure invocation and HITL escalation routing.",children:ee?e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-xs text-text-secondary",children:"Select a durable workflow to register:"}),e.jsx("div",{className:"grid gap-1",children:i.map(s=>e.jsx("button",{onClick:()=>p("workflow_type",s),className:"flex items-center gap-2 px-3 py-2 text-left text-xs font-mono rounded-md border border-surface-border hover:border-accent/50 hover:bg-accent/[0.04] transition-colors",children:s},s))}),e.jsxs("div",{className:"flex items-center gap-2 pt-1",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary",children:"or"}),e.jsx("input",{type:"text",onChange:s=>p("workflow_type",s.target.value),placeholder:"Enter type manually",className:"input font-mono flex-1"})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("input",{type:"text",value:r.workflow_type,onChange:s=>p("workflow_type",s.target.value),disabled:!a,placeholder:"reviewContent",className:"input font-mono w-full"}),a&&r.workflow_type&&i.length>0&&e.jsx("button",{onClick:()=>p("workflow_type",""),className:"text-[10px] text-accent hover:underline mt-1",children:"Choose from durable workflows"})]})}),e.jsx(y,{label:"Description",children:e.jsx("input",{type:"text",value:r.description,onChange:s=>p("description",s.target.value),placeholder:"Describe what this workflow does",className:"input text-xs w-full"})}),e.jsx(y,{label:"Task Queue",hint:"Durable task queue this workflow listens on.",children:e.jsx("input",{type:"text",value:r.task_queue,onChange:s=>p("task_queue",s.target.value),placeholder:"lt-review",className:"input font-mono w-full"})})]})]}),e.jsxs("div",{children:[e.jsx(E,{icon:z,color:"text-emerald-400",children:"Invocation"}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:r.invocable,onChange:s=>p("invocable",s.target.checked),className:"w-4 h-4 rounded border-border accent-accent"}),e.jsx("span",{className:"text-xs text-text-primary font-medium",children:"Invocable"})]}),e.jsx("p",{className:"hint",children:"Allow this workflow to be started from the dashboard or API."})]}),r.invocable?e.jsxs(e.Fragment,{children:[e.jsx(y,{label:"Run As",hint:"Bot identity to run under. Default runs as the invoking user.",children:e.jsx(fe,{selected:r.execute_as,onChange:s=>p("execute_as",s)})}),e.jsx(y,{label:"Invocation Roles",hint:"Only users with these roles can start this workflow. Empty = all authenticated users.",children:e.jsx(q,{selected:R(r.invocation_roles),onChange:s=>p("invocation_roles",I(s)),placeholder:"Select roles…"})}),e.jsxs(y,{label:"Envelope Schema",hint:e.jsxs(e.Fragment,{children:["Pre-fills the JSON editor when invoking. Include ",e.jsx("code",{className:"font-mono",children:"data"})," (input) and optional ",e.jsx("code",{className:"font-mono",children:"metadata"}),"."]}),children:[e.jsx("textarea",{value:r.envelope_schema,onChange:s=>p("envelope_schema",s.target.value),placeholder:`{
|
|
2
|
+
"data": {},
|
|
3
|
+
"metadata": {}
|
|
4
|
+
}`,className:A,rows:8,spellCheck:!1}),r.envelope_schema.trim()&&!S(r.envelope_schema)&&e.jsx("p",{className:"text-[10px] text-status-error mt-1",children:"Invalid JSON"})]})]}):e.jsxs("p",{className:"text-[11px] text-text-tertiary py-2",children:["Enable ",e.jsx("span",{className:"font-medium text-text-secondary",children:"Invocable"})," to configure invocation roles and an input template."]})]})]}),e.jsxs("div",{children:[e.jsx(E,{icon:U,color:"text-violet-400",children:"Certification"}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs(y,{label:"Resolver Schema",hint:e.jsxs(e.Fragment,{children:["Default form for resolving escalations. Use ",e.jsx("code",{className:"font-mono",children:"properties"})," with ",e.jsx("code",{className:"font-mono",children:"type"}),", ",e.jsx("code",{className:"font-mono",children:"default"}),", ",e.jsx("code",{className:"font-mono",children:"description"}),"."]}),children:[e.jsx("textarea",{value:r.resolver_schema,onChange:s=>p("resolver_schema",s.target.value),placeholder:`{
|
|
5
|
+
"properties": {
|
|
6
|
+
"approved": { "type": "boolean", "default": false }
|
|
7
|
+
}
|
|
8
|
+
}`,className:A,rows:6,spellCheck:!1}),r.resolver_schema.trim()&&!S(r.resolver_schema)&&e.jsx("p",{className:"text-[10px] text-status-error mt-1",children:"Invalid JSON"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:r.certified,onChange:s=>p("certified",s.target.checked),className:"w-4 h-4 rounded border-border accent-accent"}),e.jsx("span",{className:"text-xs text-text-primary font-medium",children:"Certify for HITL Escalation"})]}),e.jsx("p",{className:"hint",children:"Certified workflows use the interceptor — failures escalate to human reviewers. Unchecking demotes this workflow to registered on save; escalation roles and dependencies below are kept."})]}),r.certified?e.jsxs(e.Fragment,{children:[e.jsx(y,{label:"Default Escalation Role",hint:"The role surface interceptor-raised escalations target.",children:e.jsx(q,{selected:R(r.default_role),onChange:s=>p("default_role",s[0]??""),single:!0,placeholder:"Select role…"})}),e.jsx(y,{label:"Escalation Roles",hint:"Interceptor default for who can claim and resolve. Escalations raised in workflow code choose their own role, whose versioned schema takes precedence.",children:e.jsx(q,{selected:R(r.roles),onChange:s=>p("roles",I(s)),placeholder:"Select roles…"})}),e.jsx(y,{label:"Consumes",hint:"Upstream workflows whose output is injected into this workflow's envelope.",children:e.jsx(ve,{options:(c??[]).map(s=>s.workflow_type).filter(s=>s!==r.workflow_type),selected:R(r.consumes),onChange:s=>p("consumes",I(s)),placeholder:"Select dependencies…"})})]}):e.jsxs("p",{className:"text-[11px] text-text-tertiary py-2",children:["Enable ",e.jsx("span",{className:"font-medium text-text-secondary",children:"Certify"})," to add automatic escalation routing and role-based resolution."]}),n&&e.jsx("div",{className:"pt-4 border-t border-surface-border/50",children:e.jsxs("button",{onClick:()=>k(!0),className:"flex items-center gap-1.5 text-[11px] text-status-warning hover:underline",title:"Delete this registration — the workflow returns to plain durable",children:[e.jsx(B,{className:"w-3 h-3"})," Unregister workflow"]})})]})]})]}),(l||o.error)&&e.jsx("p",{className:"text-xs text-status-error mt-8",children:l||o.error.message}),e.jsx(M,{open:v,onClose:()=>k(!1),onConfirm:()=>j.mutate(n.workflow_type,{onSuccess:()=>x("/workflows/registry")}),title:"Unregister Workflow",description:e.jsxs(e.Fragment,{children:["Delete the registration for"," ",e.jsx("span",{className:"font-mono font-medium text-text-primary",children:n==null?void 0:n.workflow_type}),"? This removes its invocation settings, certification, and interceptor treatment; the workflow keeps running as a plain durable workflow. Escalation schemas versioned on roles are untouched."]}),isPending:j.isPending,error:j.error})]})}export{Oe as WorkflowConfigDetailPage,Le as WorkflowConfigsPage};
|
|
9
|
+
//# sourceMappingURL=index-U27KBbTV.js.map
|