@hotmeshio/long-tail 0.26.0 → 0.28.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/README.md +9 -2
- package/build/api/settings.d.ts +4 -1
- package/build/api/settings.js +7 -1
- package/build/index.d.ts +4 -0
- package/build/index.js +35 -8
- package/build/lib/db/config-lock.d.ts +10 -0
- package/build/lib/db/config-lock.js +28 -0
- package/build/lib/db/schemas/041_machine_lines.sql +26 -0
- package/build/lib/events/browser-view.js +8 -2
- package/build/lib/events/nats.d.ts +11 -0
- package/build/lib/events/nats.js +33 -2
- package/build/lib/events/publish.d.ts +33 -0
- package/build/lib/events/publish.js +52 -1
- package/build/lib/machines/index.d.ts +1 -0
- package/build/lib/machines/index.js +1 -0
- package/build/lib/machines/line.d.ts +4 -1
- package/build/lib/machines/line.js +18 -1
- package/build/lib/machines/protocol/codec.d.ts +24 -0
- package/build/lib/machines/protocol/codec.js +10 -0
- package/build/lib/machines/protocol/constants.d.ts +1 -1
- package/build/lib/machines/protocol/constants.js +1 -1
- package/build/lib/machines/protocol/contract.js +12 -3
- package/build/lib/machines/site/edge-account.d.ts +11 -0
- package/build/lib/machines/site/edge-account.js +24 -0
- package/build/lib/machines/site/mosquitto-conf.d.ts +5 -4
- package/build/lib/machines/site/mosquitto-conf.js +31 -16
- package/build/lib/machines/site/site-conf.d.ts +16 -6
- package/build/lib/machines/site/site-conf.js +39 -13
- package/build/lib/machines/transport/callback-feed.d.ts +1 -0
- package/build/lib/machines/transport/callback-feed.js +4 -1
- package/build/lib/machines/transport/delivery.d.ts +3 -4
- package/build/lib/machines/transport/delivery.js +3 -8
- package/build/lib/machines/transport/index.d.ts +2 -0
- package/build/lib/machines/transport/index.js +2 -0
- package/build/lib/machines/transport/memory.d.ts +2 -0
- package/build/lib/machines/transport/memory.js +6 -0
- package/build/lib/machines/transport/mqtt-bus.d.ts +2 -0
- package/build/lib/machines/transport/mqtt-bus.js +4 -0
- package/build/lib/machines/transport/nats-bus.d.ts +8 -13
- package/build/lib/machines/transport/nats-bus.js +26 -43
- package/build/lib/machines/transport/nats-feed.d.ts +1 -0
- package/build/lib/machines/transport/nats-feed.js +6 -3
- package/build/lib/machines/transport/pubsub-feed.d.ts +18 -0
- package/build/lib/machines/transport/pubsub-feed.js +42 -0
- package/build/lib/machines/transport/stream-bus.d.ts +42 -0
- package/build/lib/machines/transport/stream-bus.js +61 -0
- package/build/lib/machines/transport/streams.d.ts +10 -14
- package/build/lib/machines/transport/streams.js +18 -61
- package/build/lib/messaging/config.d.ts +25 -0
- package/build/lib/messaging/config.js +55 -0
- package/build/lib/messaging/constants.d.ts +26 -0
- package/build/lib/messaging/constants.js +26 -0
- package/build/lib/messaging/index.d.ts +24 -0
- package/build/lib/messaging/index.js +65 -0
- package/build/lib/messaging/memory.d.ts +16 -0
- package/build/lib/messaging/memory.js +147 -0
- package/build/lib/messaging/nats/permissions.d.ts +13 -0
- package/build/lib/messaging/nats/permissions.js +22 -0
- package/build/lib/messaging/nats/provider.d.ts +30 -0
- package/build/lib/messaging/nats/provider.js +77 -0
- package/build/lib/messaging/nats/render-conf.d.ts +68 -0
- package/build/lib/messaging/nats/render-conf.js +58 -0
- package/build/lib/messaging/nats/streams.d.ts +26 -0
- package/build/lib/messaging/nats/streams.js +97 -0
- package/build/lib/messaging/subjects.d.ts +8 -0
- package/build/lib/messaging/subjects.js +25 -0
- package/build/modules/machines.d.ts +8 -0
- package/build/modules/machines.js +16 -0
- package/build/routes/index.js +2 -0
- package/build/routes/machines.d.ts +2 -0
- package/build/routes/machines.js +220 -0
- package/build/scripts/bambu-boat.d.ts +17 -0
- package/build/scripts/bambu-boat.js +134 -0
- package/build/scripts/bambu-print.d.ts +18 -0
- package/build/scripts/bambu-print.js +114 -0
- package/build/services/machines/commands.d.ts +5 -5
- package/build/services/machines/commands.js +3 -2
- package/build/services/machines/config.d.ts +8 -1
- package/build/services/machines/config.js +17 -7
- package/build/services/machines/contracts.d.ts +29 -2
- package/build/services/machines/contracts.js +12 -3
- package/build/services/machines/gateway/actions.d.ts +2 -1
- package/build/services/machines/gateway/admission.d.ts +15 -3
- package/build/services/machines/gateway/admission.js +29 -14
- package/build/services/machines/gateway/dispatcher.d.ts +19 -3
- package/build/services/machines/gateway/dispatcher.js +61 -13
- package/build/services/machines/gateway/executor.d.ts +18 -4
- package/build/services/machines/gateway/executor.js +99 -34
- package/build/services/machines/gateway/gateway.d.ts +22 -1
- package/build/services/machines/gateway/gateway.js +83 -28
- package/build/services/machines/gateway/index.d.ts +3 -0
- package/build/services/machines/gateway/index.js +3 -0
- package/build/services/machines/gateway/inspect.d.ts +17 -0
- package/build/services/machines/gateway/inspect.js +68 -0
- package/build/services/machines/gateway/lifecycle.d.ts +21 -0
- package/build/services/machines/gateway/lifecycle.js +89 -0
- package/build/services/machines/gateway/log.d.ts +8 -0
- package/build/services/machines/gateway/log.js +18 -0
- package/build/services/machines/gateway/pacing.d.ts +14 -0
- package/build/services/machines/gateway/pacing.js +27 -2
- package/build/services/machines/gateway/plan-escalation.js +21 -7
- package/build/services/machines/gateway/plan-machine.d.ts +5 -4
- package/build/services/machines/gateway/plan-machine.js +70 -19
- package/build/services/machines/gateway/project.d.ts +9 -0
- package/build/services/machines/gateway/project.js +55 -0
- package/build/services/machines/gateway/receipts.d.ts +3 -0
- package/build/services/machines/gateway/receipts.js +3 -1
- package/build/services/machines/gateway/reconcile.d.ts +2 -0
- package/build/services/machines/gateway/reconcile.js +16 -3
- package/build/services/machines/gateway/registration.js +1 -1
- package/build/services/machines/gateway/reply-waiter.d.ts +6 -3
- package/build/services/machines/gateway/reply-waiter.js +37 -16
- package/build/services/machines/gateway/state.d.ts +50 -4
- package/build/services/machines/gateway/state.js +77 -10
- package/build/services/machines/gateway/wire.d.ts +58 -0
- package/build/services/machines/gateway/wire.js +171 -0
- package/build/services/machines/index.d.ts +3 -0
- package/build/services/machines/index.js +2 -0
- package/build/services/machines/leadership.d.ts +17 -4
- package/build/services/machines/leadership.js +43 -18
- package/build/services/machines/line-index.d.ts +16 -0
- package/build/services/machines/line-index.js +39 -0
- package/build/services/machines/registry-sql.d.ts +31 -0
- package/build/services/machines/registry-sql.js +110 -0
- package/build/services/machines/registry-sync.d.ts +31 -0
- package/build/services/machines/registry-sync.js +81 -0
- package/build/services/machines/registry.d.ts +77 -0
- package/build/services/machines/registry.js +158 -0
- package/build/services/machines/rows.js +26 -2
- package/build/services/machines/select.d.ts +9 -2
- package/build/services/machines/select.js +20 -7
- package/build/services/machines/surface.d.ts +45 -5
- package/build/services/machines/surface.js +102 -16
- package/build/services/machines/testing/fake-long-tail.d.ts +1 -0
- package/build/services/machines/testing/fake-long-tail.js +6 -3
- package/build/start/adapters.js +11 -2
- package/build/start/apply-report.d.ts +1 -1
- package/build/start/apply-report.js +3 -25
- package/build/start/index.js +23 -5
- package/build/system/index.js +13 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +22 -22
- package/build/system/mcp-servers/human-queue-schemas.d.ts +7 -7
- package/build/system/mcp-servers/machines-schemas.d.ts +35 -0
- package/build/system/mcp-servers/machines-schemas.js +35 -0
- package/build/system/mcp-servers/machines.d.ts +20 -0
- package/build/system/mcp-servers/machines.js +154 -0
- package/build/system/seed/tool-manifests-machines.d.ts +2 -0
- package/build/system/seed/tool-manifests-machines.js +88 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/events.d.ts +6 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/machines-view.d.ts +159 -0
- package/build/types/machines-view.js +7 -0
- package/build/types/machines.d.ts +6 -1
- package/build/types/messaging.d.ts +72 -0
- package/build/types/messaging.js +11 -0
- package/build/types/startup.d.ts +26 -1
- package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js → AdminDashboard-Bm7VG65H.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js.map → AdminDashboard-Bm7VG65H.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js → AgentConfigPage-aJF7Sdbc.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js.map → AgentConfigPage-aJF7Sdbc.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js → AgentDetailPage-GwwghhxT.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js.map → AgentDetailPage-GwwghhxT.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-DVvCzZyg.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-C9UuUKhr.js.map → AgentsPage-DVvCzZyg.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-Cxmcji_K.js +2 -0
- package/dashboard/dist/assets/{AvailableEscalationsPage-DrvfXz70.js.map → AvailableEscalationsPage-Cxmcji_K.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js → BotPicker-CofRxk4L.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js.map → BotPicker-CofRxk4L.js.map} +1 -1
- package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js → BulkAssignModal-BARWuz4L.js} +2 -2
- package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js.map → BulkAssignModal-BARWuz4L.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js → CapabilitiesPage-BJAWVNEx.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js.map → CapabilitiesPage-BJAWVNEx.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js → CollapsibleSection-CtgoaG75.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js.map → CollapsibleSection-CtgoaG75.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js → ConfirmDeleteModal-BYXXBWs6.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js.map → ConfirmDeleteModal-BYXXBWs6.js.map} +1 -1
- package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js → ConnectedAppsPage-D0JkzWa7.js} +2 -2
- package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js.map → ConnectedAppsPage-D0JkzWa7.js.map} +1 -1
- package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js → CountdownTimer-BcFxdZYN.js} +2 -2
- package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js.map → CountdownTimer-BcFxdZYN.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js → CredentialsPage-DPSY20rV.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js.map → CredentialsPage-DPSY20rV.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js → CronLabel-Dg_Oew_Y.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js.map → CronLabel-Dg_Oew_Y.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-u1JGfR9G.js → DropZone-Cl_Flb16.js} +2 -2
- package/dashboard/dist/assets/{DropZone-u1JGfR9G.js.map → DropZone-Cl_Flb16.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js → ElapsedCell-Dm3G5fK2.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js.map → ElapsedCell-Dm3G5fK2.js.map} +1 -1
- package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js → EntityTimelinePanel-CrEEP04e.js} +2 -2
- package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js.map → EntityTimelinePanel-CrEEP04e.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js → EscalationListSchemaPage-wOInXcjs.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js.map → EscalationListSchemaPage-wOInXcjs.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js → EscalationSchemaPage-CrIj_g9d.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js.map → EscalationSchemaPage-CrIj_g9d.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js → EscalationTimeline-CeAsl5IT.js} +2 -2
- package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js.map → EscalationTimeline-CeAsl5IT.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js → EscalationTitleSelect-D8qpCYiy.js} +2 -2
- package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js.map → EscalationTitleSelect-D8qpCYiy.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js → EscalationsOverview-CvaqVPsr.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js.map → EscalationsOverview-CvaqVPsr.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-V4RF_n58.js → EventTable-Be0JuEl6.js} +2 -2
- package/dashboard/dist/assets/{EventTable-V4RF_n58.js.map → EventTable-Be0JuEl6.js.map} +1 -1
- package/dashboard/dist/assets/FilterBar-BVyAjIgv.js +2 -0
- package/dashboard/dist/assets/FilterBar-BVyAjIgv.js.map +1 -0
- package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js → GraphInvokePage-D_QigJeD.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js.map → GraphInvokePage-D_QigJeD.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-EjcNxlKx.js → HomePage-C1haeuNy.js} +2 -2
- package/dashboard/dist/assets/{HomePage-EjcNxlKx.js.map → HomePage-C1haeuNy.js.map} +1 -1
- package/dashboard/dist/assets/LinePage-CWD5pv1g.js +5 -0
- package/dashboard/dist/assets/LinePage-CWD5pv1g.js.map +1 -0
- package/dashboard/dist/assets/LinesPage-NqChsKcO.js +3 -0
- package/dashboard/dist/assets/LinesPage-NqChsKcO.js.map +1 -0
- package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js → ListToolbar-BZCNrTh6.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js.map → ListToolbar-BZCNrTh6.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js → McpOverview-yJTYnh1z.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js.map → McpOverview-yJTYnh1z.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js → McpQueryDetailPage-ChuLpJ4u.js} +3 -3
- package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js.map → McpQueryDetailPage-ChuLpJ4u.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js → McpQueryPage-9S-v9soF.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js.map → McpQueryPage-9S-v9soF.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-BhrcfxD5.js +2 -0
- package/dashboard/dist/assets/{McpRunDetailPage-80fQn9iX.js.map → McpRunDetailPage-BhrcfxD5.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js → McpRunsPage-C5_i8_Zx.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js.map → McpRunsPage-C5_i8_Zx.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js → NamespacePill-OTZD2hzt.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js.map → NamespacePill-OTZD2hzt.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js → OperationsPage-C03CeU5P.js} +2 -2
- package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js.map → OperationsPage-C03CeU5P.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js → OperatorDashboard-BzC134Jt.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js.map → OperatorDashboard-BzC134Jt.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-BSWIspNB.js → PageHeader-Hx8APPwE.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-BSWIspNB.js.map → PageHeader-Hx8APPwE.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js → PageHeaderWithStats-BEQYYBJv.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js.map → PageHeaderWithStats-BEQYYBJv.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js → PersonaDetailPage-D0mY9L6j.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js.map → PersonaDetailPage-D0mY9L6j.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js → PersonasPage-FFlm5Eug.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js.map → PersonasPage-FFlm5Eug.js.map} +1 -1
- package/dashboard/dist/assets/{PortalPage-CucNZn_w.js → PortalPage-Cpc25uO-.js} +2 -2
- package/dashboard/dist/assets/{PortalPage-CucNZn_w.js.map → PortalPage-Cpc25uO-.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js → ProcessDetailPage-Di2J7KwY.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js.map → ProcessDetailPage-Di2J7KwY.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js → ProcessesListPage-BEXwU3Z0.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js.map → ProcessesListPage-BEXwU3Z0.js.map} +1 -1
- package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js → ResolverForm-2ks-3jXC.js} +6 -6
- package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js.map → ResolverForm-2ks-3jXC.js.map} +1 -1
- package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js → RoleDetailPage-DqoOk0xc.js} +3 -3
- package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js.map → RoleDetailPage-DqoOk0xc.js.map} +1 -1
- package/dashboard/dist/assets/{RolePill-CoETDpjF.js → RolePill-R5_ctGFy.js} +2 -2
- package/dashboard/dist/assets/{RolePill-CoETDpjF.js.map → RolePill-R5_ctGFy.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-DBKxeam4.js → RolesPage-CPmo8Srr.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-DBKxeam4.js.map → RolesPage-CPmo8Srr.js.map} +1 -1
- package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js → RosterWidget-D2yovCKw.js} +2 -2
- package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js.map → RosterWidget-D2yovCKw.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js → RunAsSelector-CMv96On6.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js.map → RunAsSelector-CMv96On6.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js → ScanCodesPage-CA6cTT-M.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js.map → ScanCodesPage-CA6cTT-M.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js → ScanSchemeDetailPage-DnctN5-H.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js.map → ScanSchemeDetailPage-DnctN5-H.js.map} +1 -1
- package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js → ScanStationPage-Bk8RwkKL.js} +2 -2
- package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js.map → ScanStationPage-Bk8RwkKL.js.map} +1 -1
- package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js → ScopeBadge-BXZ084nD.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js.map → ScopeBadge-BXZ084nD.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js → StickyPagination-B0FSuUgs.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js.map → StickyPagination-B0FSuUgs.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js → StreamMessageDetail-BwZEvMpP.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js.map → StreamMessageDetail-BwZEvMpP.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js → SwimlaneTimeline-BDVQEaMw.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js.map → SwimlaneTimeline-BDVQEaMw.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-S7UuVT_M.js → TagInput-DjPUDp2M.js} +2 -2
- package/dashboard/dist/assets/{TagInput-S7UuVT_M.js.map → TagInput-DjPUDp2M.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js → TaskDetailPage-BOlv3iGB.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js.map → TaskDetailPage-BOlv3iGB.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js → TaskQueuePill-D-4Ynf3K.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js.map → TaskQueuePill-D-4Ynf3K.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js → TasksListPage-e2teY0bD.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js.map → TasksListPage-e2teY0bD.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js → TimeAgo-Cvkpf_b3.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js.map → TimeAgo-Cvkpf_b3.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js → TimestampCell-B3btAV0N.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js.map → TimestampCell-B3btAV0N.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-D40uLM_v.js → ToolPill-GsKQ80CO.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-D40uLM_v.js.map → ToolPill-GsKQ80CO.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js → ToolTestPanel-B03nebR7.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js.map → ToolTestPanel-B03nebR7.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js → TopicDetailPage-DBRWne3O.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js.map → TopicDetailPage-DBRWne3O.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js → TopicsPage-CVe2M9HD.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js.map → TopicsPage-CVe2M9HD.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DQFjuoib.js → UserName-B_q_qp6V.js} +2 -2
- package/dashboard/dist/assets/{UserName-DQFjuoib.js.map → UserName-B_q_qp6V.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-Bh_dr9tM.js +2 -0
- package/dashboard/dist/assets/{WorkflowExecutionPage-Di08wCbs.js.map → WorkflowExecutionPage-Bh_dr9tM.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js → WorkflowPill-D7244fKp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js.map → WorkflowPill-D7244fKp.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js → WorkflowsDashboard-DA6Mj7qH.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js.map → WorkflowsDashboard-DA6Mj7qH.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js → WorkflowsOverview-B1kBLj-a.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js.map → WorkflowsOverview-B1kBLj-a.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js → YamlWorkflowDetailPage-6GJM8QIX.js} +8 -8
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js.map → YamlWorkflowDetailPage-6GJM8QIX.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js → YamlWorkflowsPage-Bu811C7f.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js.map → YamlWorkflowsPage-Bu811C7f.js.map} +1 -1
- package/dashboard/dist/assets/{agents-PuftRTB2.js → agents-MPk_GnYz.js} +2 -2
- package/dashboard/dist/assets/{agents-PuftRTB2.js.map → agents-MPk_GnYz.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BmEiPAWY.js → bots-2RihE2Tc.js} +2 -2
- package/dashboard/dist/assets/{bots-BmEiPAWY.js.map → bots-2RihE2Tc.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CRii4Soa.js → capabilities-CC4iwJ1P.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CRii4Soa.js.map → capabilities-CC4iwJ1P.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DO_czaYo.js → controlplane-Bmkic8BH.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DO_czaYo.js.map → controlplane-Bmkic8BH.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js → escalation-columns-B0lhUUrQ.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js.map → escalation-columns-B0lhUUrQ.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js → escalation-items-7WFpPo46.js} +2 -2
- package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js.map → escalation-items-7WFpPo46.js.map} +1 -1
- package/dashboard/dist/assets/index-B9uv-euR.css +1 -0
- package/dashboard/dist/assets/{index-DhSCCpqM.js → index-B_XV1rwu.js} +2 -2
- package/dashboard/dist/assets/{index-DhSCCpqM.js.map → index-B_XV1rwu.js.map} +1 -1
- package/dashboard/dist/assets/{index-Cx8xb-oU.js → index-C2CLfGwL.js} +2 -2
- package/dashboard/dist/assets/{index-Cx8xb-oU.js.map → index-C2CLfGwL.js.map} +1 -1
- package/dashboard/dist/assets/{index-Cga5IW3D.js → index-C3AeFihz.js} +2 -2
- package/dashboard/dist/assets/{index-Cga5IW3D.js.map → index-C3AeFihz.js.map} +1 -1
- package/dashboard/dist/assets/{index-DiaRlFYI.js → index-CBri8fYM.js} +2 -2
- package/dashboard/dist/assets/{index-DiaRlFYI.js.map → index-CBri8fYM.js.map} +1 -1
- package/dashboard/dist/assets/{index-CL_1VpGb.js → index-CRWsB7ZC.js} +2 -2
- package/dashboard/dist/assets/{index-CL_1VpGb.js.map → index-CRWsB7ZC.js.map} +1 -1
- package/dashboard/dist/assets/{index-XntldBd0.js → index-CTOv3G7a.js} +2 -2
- package/dashboard/dist/assets/{index-XntldBd0.js.map → index-CTOv3G7a.js.map} +1 -1
- package/dashboard/dist/assets/{index-CfP2nKRc.js → index-CcArGkzI.js} +3 -3
- package/dashboard/dist/assets/{index-CfP2nKRc.js.map → index-CcArGkzI.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dz6R8-Dw.js → index-CkMHS-hG.js} +25 -25
- package/dashboard/dist/assets/index-CkMHS-hG.js.map +1 -0
- package/dashboard/dist/assets/index-CmsmwCyv.js +2 -0
- package/dashboard/dist/assets/{index-4YaDUcKd.js.map → index-CmsmwCyv.js.map} +1 -1
- package/dashboard/dist/assets/{index-2SM2_onB.js → index-CvJsvqIc.js} +2 -2
- package/dashboard/dist/assets/{index-2SM2_onB.js.map → index-CvJsvqIc.js.map} +1 -1
- package/dashboard/dist/assets/{index-Rwqrflm-.js → index-D-ilRyfy.js} +2 -2
- package/dashboard/dist/assets/{index-Rwqrflm-.js.map → index-D-ilRyfy.js.map} +1 -1
- package/dashboard/dist/assets/{index-CZ7A3AcD.js → index-DSUtfDXx.js} +2 -2
- package/dashboard/dist/assets/{index-CZ7A3AcD.js.map → index-DSUtfDXx.js.map} +1 -1
- package/dashboard/dist/assets/{index-jL2K6us4.js → index-Da6ycKyf.js} +2 -2
- package/dashboard/dist/assets/{index-jL2K6us4.js.map → index-Da6ycKyf.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-CeUqGPuo.js → knowledge-Bpbg20DC.js} +2 -2
- package/dashboard/dist/assets/{knowledge-CeUqGPuo.js.map → knowledge-Bpbg20DC.js.map} +1 -1
- package/dashboard/dist/assets/machines-BqOZZU-3.js +2 -0
- package/dashboard/dist/assets/machines-BqOZZU-3.js.map +1 -0
- package/dashboard/dist/assets/{mcp-Cx96NTeD.js → mcp-CmcUXYce.js} +2 -2
- package/dashboard/dist/assets/{mcp-Cx96NTeD.js.map → mcp-CmcUXYce.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js → mcp-query-C8buQxN9.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js.map → mcp-query-C8buQxN9.js.map} +1 -1
- package/dashboard/dist/assets/{personas--4kjwyOw.js → personas-C2n5oDmY.js} +2 -2
- package/dashboard/dist/assets/{personas--4kjwyOw.js.map → personas-C2n5oDmY.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-PgkwvG7f.js → pipelines-RKpSkFz7.js} +2 -2
- package/dashboard/dist/assets/{pipelines-PgkwvG7f.js.map → pipelines-RKpSkFz7.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-CcYxdBa3.js → tasks-Dab4hExW.js} +2 -2
- package/dashboard/dist/assets/{tasks-CcYxdBa3.js.map → tasks-Dab4hExW.js.map} +1 -1
- package/dashboard/dist/assets/{topics-_lik7cNY.js → topics-FTMNP7r3.js} +2 -2
- package/dashboard/dist/assets/{topics-_lik7cNY.js.map → topics-FTMNP7r3.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js → useDebouncedValue-B_1u1YIk.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js.map → useDebouncedValue-B_1u1YIk.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js → useNamespace-HUeAI7Qb.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js.map → useNamespace-HUeAI7Qb.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js → useYamlActivityEvents-Cax54qHH.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js.map → useYamlActivityEvents-Cax54qHH.js.map} +1 -1
- package/dashboard/dist/assets/{validate-FlXCBBT6.js → validate-DxOaV7QS.js} +2 -2
- package/dashboard/dist/assets/{validate-FlXCBBT6.js.map → validate-DxOaV7QS.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-DUYcKTjP.js → vendor-icons-fxBvvHBi.js} +187 -182
- package/dashboard/dist/assets/vendor-icons-fxBvvHBi.js.map +1 -0
- package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js → yaml-workflows-BgY1XHJx.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js.map → yaml-workflows-BgY1XHJx.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/architecture.md +1 -0
- package/docs/code-owned-configuration.md +6 -0
- package/docs/dashboard.md +1 -0
- package/docs/events.md +83 -4
- package/docs/machines.md +129 -23
- package/docs/mcp.md +19 -0
- package/package.json +3 -1
- package/build/lib/machines/transport/nats-durable.d.ts +0 -11
- package/build/lib/machines/transport/nats-durable.js +0 -28
- package/dashboard/dist/assets/AgentsPage-C9UuUKhr.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DrvfXz70.js +0 -2
- package/dashboard/dist/assets/FilterBar-BygWrnB1.js +0 -2
- package/dashboard/dist/assets/FilterBar-BygWrnB1.js.map +0 -1
- package/dashboard/dist/assets/McpRunDetailPage-80fQn9iX.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-Di08wCbs.js +0 -2
- package/dashboard/dist/assets/index-4YaDUcKd.js +0 -2
- package/dashboard/dist/assets/index-CnU7Jjz3.css +0 -1
- package/dashboard/dist/assets/index-Dz6R8-Dw.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-DUYcKTjP.js.map +0 -1
package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js.map → yaml-workflows-BgY1XHJx.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-workflows-B0_JxRYQ.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"yaml-workflows-BgY1XHJx.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-CkMHS-hG.js"></script>
|
|
12
12
|
<link rel="modulepreload" crossorigin href="./assets/vendor-query-DvWWuNIB.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="./assets/vendor-react-_9U4mKFr.js">
|
|
14
|
-
<link rel="modulepreload" crossorigin href="./assets/vendor-icons-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
14
|
+
<link rel="modulepreload" crossorigin href="./assets/vendor-icons-fxBvvHBi.js">
|
|
15
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B9uv-euR.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/docs/architecture.md
CHANGED
|
@@ -173,6 +173,7 @@ Long Tail ships with the following built-in servers:
|
|
|
173
173
|
|--------|---------|
|
|
174
174
|
| `long-tail-db-query` | Query tasks, escalations, process summaries, system health |
|
|
175
175
|
| `long-tail-human-queue` | Route work to humans, check resolution, claim/resolve, escalate-and-wait |
|
|
176
|
+
| `long-tail-machines` | The lines index with health, a line as its leader sees it, a wire dry run, machine-command rows, declare and retire lines |
|
|
176
177
|
| `mcp-workflows-longtail` | Discover and invoke compiled workflow tools |
|
|
177
178
|
| `long-tail-workflow-compiler` | Convert executions to YAML, deploy, list |
|
|
178
179
|
| `long-tail-translation` | Translate content between languages |
|
|
@@ -66,6 +66,7 @@ The domain dictionary's override lives beside its declaration:
|
|
|
66
66
|
| Scan schemes | `scanSchemes[]` | whole scheme + its rules (one ownership unit; `enabled` is declarative here) | — |
|
|
67
67
|
| Domain dictionary | `mcp.domainDictionary` (inline) or `mcp.domainDictionaryPath` | whole document (version bumps once per change) | — |
|
|
68
68
|
| Graph workflows | `graphWorkflows[]` | always compare-and-apply: description/schema sync, redeploy on YAML version bump | run history |
|
|
69
|
+
| Machine lines | `machines.lines[]` | the whole line definition (`lt_machine_lines`; version bumps once per change) | lines declared through the API, their bot accounts, their rows |
|
|
69
70
|
|
|
70
71
|
`certified` on a workflow profile is explicit-only under code ownership: an
|
|
71
72
|
omitted flag registers the workflow as **not** certified. (The db-owned seed
|
|
@@ -133,6 +134,10 @@ declaration are reported as `version/category` orphans. The pass runs whether
|
|
|
133
134
|
or not the dashboard scan affordances (`features.scanCodes`) are enabled —
|
|
134
135
|
declaring schemes states intent.
|
|
135
136
|
|
|
137
|
+
## Machine lines
|
|
138
|
+
|
|
139
|
+
Lines ([machines](machines.md#declaration)) follow the dial too. Each row of `lt_machine_lines` keeps the line's definition, who declared it (`source`: `code` from `start({ machines })`, `api` from `PUT /api/machines/lines/:siteId/:lineId`), and a `version` that advances once per change. Db-owned, a code line is inserted when absent and left as it is afterwards, with a drift warning when the declaration differs. Code-owned, a code line is written whenever its declaration differs, advancing the version once, and dashboard or API edits to it are replaced at the next deploy. Boot then runs every enabled line the registry holds, code and API lines alike, and never deletes one: a line declared in code before and not now is reported and left standing, and a line leaves the registry only through `DELETE /api/machines/lines/:siteId/:lineId`.
|
|
140
|
+
|
|
136
141
|
## The boot report
|
|
137
142
|
|
|
138
143
|
Each surface logs one line summarizing its pass:
|
|
@@ -140,6 +145,7 @@ Each surface logs one line summarizing its pass:
|
|
|
140
145
|
```
|
|
141
146
|
[long-tail] config apply (workflows): applied 2, unchanged 31, db-owned 0
|
|
142
147
|
[long-tail] config apply (roles): applied 1, unchanged 11, db-owned 0, orphans: [old-station]
|
|
148
|
+
[lt-machines] lines: applied 2, unchanged 0, db-owned 0
|
|
143
149
|
```
|
|
144
150
|
|
|
145
151
|
Orphans — database records of a declared surface with no matching declaration
|
package/docs/dashboard.md
CHANGED
|
@@ -14,6 +14,7 @@ The reactive, event-driven surface — where operations watch the floor and buil
|
|
|
14
14
|
|------|-------|------|---------|
|
|
15
15
|
| **Pace Board** | `/pace` | Gauge | Actual-vs-target flow across a segment of roles — a pace chart over the sequence, a live role table, and a role detail panel. Readable by every login (aggregate counts and trends) while `features.publicPaceBoard`, default on, stands. |
|
|
16
16
|
| **Trend Board** | `/trends` | TrendingUp | Where the time went — the entity lens over a metadata facet (`serialNumber`, `orderId`): a state-mix summary, dwell rankings, and per-entity cross-queue timelines. Shares the combined board selector with the Pace Board. |
|
|
17
|
+
| **Lines** | `/lines` | Factory | The index of every machine line with its health, searched and paged on the server, grouped by site, lines needing attention first, with a panel to declare, edit and retire lines. A line shows its machines as tiles or rows above a boxed wire tape with its own filters, pause and full screen, and a panel per machine with its snapshot, open rows, timeline, command and inspect tools. Present when `start({ machines })` runs a surface; see [Machines](machines.md#lines-view). Readable by the ops tier (superadmin, admin type, engineer); declaring and retiring takes an administrator. |
|
|
17
18
|
| **Event Topics** | `/topics` | Radio | Topic catalog — browse all known event topics with descriptions, schemas, and subscriber counts. |
|
|
18
19
|
| **Agents** | `/agents` | Bot | Autonomous event-driven automations (labeled **Automations** when AI is not configured). Configure subscriptions, schedules, and knowledge domains. |
|
|
19
20
|
| **Capabilities** | `/capabilities` | Zap | Browse MCP tools grouped by capability category, with a live run panel. |
|
package/docs/events.md
CHANGED
|
@@ -16,6 +16,13 @@ await start({
|
|
|
16
16
|
events: { nats: { url: 'nats://localhost:4222' } },
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
// The same NATS as Long Tail's messaging, shared with the machine surface
|
|
20
|
+
await start({
|
|
21
|
+
database: { connectionString: process.env.DATABASE_URL },
|
|
22
|
+
workers: [ ... ],
|
|
23
|
+
messaging: { nats: { url: 'nats://nats:4222', token: process.env.NATS_TOKEN, ws: { proxy: 'ws://nats:9222' } } },
|
|
24
|
+
});
|
|
25
|
+
|
|
19
26
|
// Custom adapters (multiple supported)
|
|
20
27
|
await start({
|
|
21
28
|
database: { connectionString: process.env.DATABASE_URL },
|
|
@@ -26,6 +33,32 @@ await start({
|
|
|
26
33
|
|
|
27
34
|
`start()` handles adapter connection and graceful disconnection on shutdown automatically.
|
|
28
35
|
|
|
36
|
+
## Messaging
|
|
37
|
+
|
|
38
|
+
Messaging is one connection that Long Tail opens once and shares: events publish over its pub/sub, and the machine surface keeps the line's durable streams in it. NATS is the provider to deploy, and with JetStream enabled on the server it also provides durable streams. `start()` connects it before events and the machine surface, and closes it after both at shutdown. If it cannot connect, startup logs `messaging.provider.connect_failed` and continues, as events always have.
|
|
39
|
+
|
|
40
|
+
Configure it in one of these ways, in this order of precedence:
|
|
41
|
+
|
|
42
|
+
| Configuration | Meaning |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `messaging.provider` | A provider of your own (`LTMessagingProvider`) |
|
|
45
|
+
| `messaging.nats` | `{ url, token? \| user? + password?, ws? }`; `ws` is `{ url?, proxy?, token? }` for browsers |
|
|
46
|
+
| `messaging.memory` | In process only, for tests and single-process development |
|
|
47
|
+
| `LT_MESSAGING=nats` with `LT_NATS_URL`, `LT_NATS_TOKEN` or `LT_NATS_USER` and `LT_NATS_PASSWORD`, `LT_NATS_WS_URL`, `LT_NATS_WS_PROXY` | The same from the environment; `LT_MESSAGING=memory` for in process |
|
|
48
|
+
| `events.nats` | The same NATS connection, configured as events have been |
|
|
49
|
+
|
|
50
|
+
`events.nats` and `messaging.nats` configure the same connection, so an existing `events: { nats: { url, token, wsProxy } }` keeps working as it is. With none of these, Long Tail runs without messaging, events go over Socket.IO, and anything that asks for messaging (`messagingRegistry.pubsub` or `.streams`) throws, naming what to set.
|
|
51
|
+
|
|
52
|
+
**One server, separate accounts.** A NATS server that only Long Tail and its browsers use needs only a token. When a site's machine broker also connects to it, give each party a NATS account so it reaches only its own subjects. `renderNatsConf` writes such a server's configuration:
|
|
53
|
+
|
|
54
|
+
| Account | Who logs in | What it reaches |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `LT` | Long Tail; optionally a subscribe-only dashboard user, and a standalone gateway user scoped by `jsPermissions` to its streams and consumer | Everything of Long Tail's, including the durable streams |
|
|
57
|
+
| `EDGE_<site>_<line>` (`Machines.edgeAccountFor(line, root)`) | The machine broker's bridge, over MQTT only | Publishing the line's subjects; nothing else. `LT` imports exactly those subjects |
|
|
58
|
+
| `SYS` | Monitoring | The server's own |
|
|
59
|
+
|
|
60
|
+
The edge's subjects reach Long Tail's streams, and nothing of Long Tail's reaches the edge.
|
|
61
|
+
|
|
29
62
|
## Dashboard Transport Selection
|
|
30
63
|
|
|
31
64
|
The dashboard auto-detects its event transport via `GET /api/settings`. By default, Socket.IO is reported — it works in-process with no additional infrastructure.
|
|
@@ -162,15 +195,28 @@ The built-in `NatsEventAdapter` publishes events as JSON-encoded strings to NATS
|
|
|
162
195
|
| Option | Default | Description |
|
|
163
196
|
|-----------------|----------------------------------------------|----------------------------------|
|
|
164
197
|
| `url` | `process.env.NATS_URL` or `nats://localhost:4222` | NATS server URL |
|
|
198
|
+
| `token` | `process.env.NATS_TOKEN` | NATS token |
|
|
199
|
+
| `wsUrl` | `process.env.NATS_WS_URL` | Public WebSocket URL for browsers |
|
|
200
|
+
| `wsProxy` | | NATS WebSocket target that Long Tail proxies browsers to |
|
|
165
201
|
| `subjectPrefix` | `lt.events` | Prefix for NATS subject names |
|
|
202
|
+
| `messaging` | | Long Tail's messaging; the adapter publishes over it instead of opening a connection of its own, and skips publishing while it is disconnected |
|
|
166
203
|
|
|
167
204
|
### Subject Format
|
|
168
205
|
|
|
169
|
-
Events are published to `{subjectPrefix}.{event.type}`.
|
|
206
|
+
Events are published to `{subjectPrefix}.{event.type}`. Every built-in type starts with `system.`:
|
|
170
207
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
208
|
+
| Event | Subject |
|
|
209
|
+
|---|---|
|
|
210
|
+
| Milestone | `lt.events.system.milestone.<workflowId>` |
|
|
211
|
+
| Task | `lt.events.system.task.<taskId>.<action>` |
|
|
212
|
+
| Escalation | `lt.events.system.escalation.<role>.<escalationId>.<action>` |
|
|
213
|
+
| Activity | `lt.events.system.activity.<workflowId>.<activityName>.<action>` |
|
|
214
|
+
| Workflow | `lt.events.system.workflow.<workflowId>.<action>` |
|
|
215
|
+
| Agent | `lt.events.system.agent.<agentName>.<action>` |
|
|
216
|
+
| Knowledge | `lt.events.system.knowledge.<domain>.<action>` |
|
|
217
|
+
| File | `lt.events.system.file.<action>` |
|
|
218
|
+
|
|
219
|
+
A role is written as one subject token: each run of characters other than letters, digits, `_` and `-` becomes `-`. Subscribe to `lt.events.system.escalation.<role>.>` for one role's escalations. The machine surface publishes under `lt.events.machine.` ([machine surface events](#machine-surface-events)).
|
|
174
220
|
|
|
175
221
|
### Usage
|
|
176
222
|
|
|
@@ -314,6 +360,39 @@ class WebhookEventAdapter implements LTEventAdapter {
|
|
|
314
360
|
}
|
|
315
361
|
```
|
|
316
362
|
|
|
363
|
+
## Machine surface events
|
|
364
|
+
|
|
365
|
+
A deployment that declares lines ([machines](machines.md)) publishes three event families under `machine.`, with the site, line and machine written as subject tokens the way roles are.
|
|
366
|
+
|
|
367
|
+
| Event | Subject | Carries |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| Wire | `lt.events.machine.wire.<site>.<line>.<machine\|unknown>.<in\|out>` | One record per message a line's gateway reads (`in`) and per command, snapshot request or receipt it sends (`out`) |
|
|
370
|
+
| Log | `lt.events.machine.log.<site>.<line>.<event>` | One per gateway log entry: its level, the event name and its context; `<event>` is the log event without its `machine_gateway.` prefix as one token |
|
|
371
|
+
| Line | `lt.events.machine.line.<site>.<line>.<declared\|retired>` | A change to the line registry: the site, line, `source` (`code` or `api`), `version` and `action` |
|
|
372
|
+
|
|
373
|
+
The wire record is the event's `data`:
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
interface WireEvent {
|
|
377
|
+
type: 'machine.wire'; siteId: string; lineId: string; machineId: string | null; address: string | null;
|
|
378
|
+
direction: 'in' | 'out'; channel: string; topic: string; id: string | null; at: string; retained: boolean;
|
|
379
|
+
kind: 'presence' | 'snapshot' | 'job' | 'alarm' | 'telemetry' | 'report' | 'cmd' | 'reply' | 'receipt' | 'unknown';
|
|
380
|
+
event: string | null; // job event, alarm action, command op, reply status
|
|
381
|
+
summary: string; // one line from the kind, the event and the ids the body names
|
|
382
|
+
violations: string[]; // what the contract check found
|
|
383
|
+
actions: { verb: 'claim' | 'accumulate' | 'resolve' | 'create-row' | 'refuse' | 'hold' | 'unmatched' | 'unreadable' | 'ack' | 'none'; rowId?: string; itemKey?: string; detail?: string }[];
|
|
384
|
+
payload?: unknown; // the raw wire payload
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`actions` is what the gateway did with the message: the verbs it performed on rows with their row ids (`claim`, `accumulate` with its `itemKey`, `resolve`, `create-row`), a verb Long Tail refused (`refuse`), a message held for a person under a strict codec (`hold`), one no row claimed (`unmatched`), one the codec or the planner could not read (`unreadable`), one admitted with nothing to do (`ack`), and `none` with a `detail` for everything else. Telemetry messages are many and join the wire record only with `MACHINE_WIRE_TELEMETRY=on`.
|
|
389
|
+
|
|
390
|
+
A line event names a line and nothing more: each replica reads the registry row back and follows what the row says, so the row, never the event, decides whether a line runs.
|
|
391
|
+
|
|
392
|
+
In a browser, builders receive every event whole. Anyone else receives `machine.wire` without `payload`, and `machine.log` and `machine.line` with their type and routing fields only, the same filter escalation events pass through on both the Socket.IO path and the NATS WebSocket proxy. A browser connected straight to a NATS WebSocket listener bypasses that filter, so production runs the proxy.
|
|
393
|
+
|
|
394
|
+
The dashboard's line page subscribes to `lt.events.machine.wire.<site>.<line>.>`; the Lines index refreshes on `lt.events.machine.line.>` and `lt.events.machine.log.>`. A subscriber on `lt.events.>` sees all three families.
|
|
395
|
+
|
|
317
396
|
## When Events Fire
|
|
318
397
|
|
|
319
398
|
Regardless of which adapter receives them, all events originate from three places in the system.
|
package/docs/machines.md
CHANGED
|
@@ -6,22 +6,33 @@ This page is the contract for a host. A runnable factory lives in [examples/work
|
|
|
6
6
|
|
|
7
7
|
## Declaration
|
|
8
8
|
|
|
9
|
+
Lines are declared two ways, and both land in one registry (`lt_machine_lines`) that every replica reads: in code, through `start({ machines })`, and through the API, `PUT /api/machines/lines/:siteId/:lineId`.
|
|
10
|
+
|
|
9
11
|
```typescript
|
|
10
12
|
import { start, Machines } from '@hotmeshio/long-tail';
|
|
11
13
|
|
|
12
14
|
await start({
|
|
13
15
|
// ...database, workers, roles
|
|
14
16
|
machines: {
|
|
15
|
-
lines: [Machines.loadLine('/etc/line.json')],
|
|
17
|
+
lines: [Machines.loadLine('/etc/line.json')], // code lines; [] when every line comes through the API
|
|
16
18
|
codec: myCodec, // optional: the machines' wire protocol (default: the generic line protocol)
|
|
17
19
|
rows: myRows, // optional: how your rows read as handoffs (default: the standard handoff facets)
|
|
18
20
|
},
|
|
19
21
|
});
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
Transports come from the environment (see [Configuration](#configuration)). `mqtt` is an optional peer dependency; install it in the host when a line runs.
|
|
24
|
+
Transports come from the environment (see [Configuration](#configuration)). `mqtt` is an optional peer dependency; install it in the host when a line runs. Declaring `machines` opens the dashboard's [Lines view](#lines-view), with or without code lines.
|
|
25
|
+
|
|
26
|
+
The registry keeps each line's definition, who declared it (`source`: `code` or `api`), and a `version` that advances once per change. Ownership follows `configSource`, as roles do:
|
|
27
|
+
|
|
28
|
+
- `db` (the default): a code line is inserted when absent and left as it is afterwards; a declaration that differs from the row is logged as drift and the row stands.
|
|
29
|
+
- `code`: a code line is written whenever its declaration differs, advancing the version once; the row then matches the code again.
|
|
30
|
+
|
|
31
|
+
Boot applies the code lines under these rules, logs one report (applied, unchanged, db-owned, and code lines the registry still holds that code no longer declares), then runs every enabled line the registry holds, code and API alike. Boot never deletes a line: a line leaves the registry only through `DELETE /api/machines/lines/:siteId/:lineId`. An API declaration applies the line's station, exit and operator roles where none exist (the host's own role declarations win), runs the line on the replica that took the request, and publishes `machine.line.<site>.<line>.declared`; every other replica reads the row back and runs it too. Retiring a line publishes `machine.line.<site>.<line>.retired` and each replica stops it; the line's roles and bot accounts stay.
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
The registry is Long Tail's side of a line. The site's side, its broker's certificates and ACL and its NATS permissions, is rendered from the lines it serves (see [Topology and identity](#topology-and-identity)); a line declared through the API leads only once the site knows it too. Until then the replica stands by and logs `machine_gateway.line.leadership_lost` with the broker's refusal, and the registry row waits.
|
|
34
|
+
|
|
35
|
+
For each line, one replica leads (a Postgres advisory lock held on its own connection), makes sure the line's bot accounts exist, and runs the gateway. Other replicas wait on the lock and take over when the leader's session ends. A replica whose connection drops or is refused, as while Postgres restarts, contends again every 5 seconds.
|
|
25
36
|
|
|
26
37
|
## The line
|
|
27
38
|
|
|
@@ -58,12 +69,13 @@ A machine handoff is a row on the receiving machine's role (a handoff to a perso
|
|
|
58
69
|
| Facet | Meaning |
|
|
59
70
|
|---|---|
|
|
60
71
|
| `siteId`, `lineId` | The line; a gateway acts only on its own line's rows |
|
|
61
|
-
| `units` | `[{ unitId, carrierId, artifact?, parameters? }]`: the units that move together; any of a route's units, all or some. A unit's `artifact`
|
|
72
|
+
| `units` | `[{ unitId, carrierId, artifact?, artifacts?, parameters?, refs? }]`: the units that move together; any of a route's units, all or some. A unit's `artifact` is its own file, such as a per-unit model, and `artifacts` its files when it has several; each is `{ url, sha256, id?, contentType?, sizeBytes? }`. Its `parameters` are its own settings and its `refs` its own references |
|
|
62
73
|
| `jobId`, `senderJobId` | The receiver's job and the sender's. Each open handoff into a machine names its own job; a second open row naming the same job there resolves `dispatch-refused`. A row for some of a sender job's units still receives their releases, however the codec split that job |
|
|
63
74
|
| `fromMachine`, `toMachine` | Machine ids, or null for line entry and for a person |
|
|
64
75
|
| `recipeId`, `recipeVersion`, `process` | What the receiver runs |
|
|
65
76
|
| `artifactUrl`, `artifactSha256` | The job file, by reference |
|
|
66
77
|
| `parameters` | Settings for the job, sent to the machine as given. Long Tail does not read them; what they mean is between the host and its codec |
|
|
78
|
+
| `refs` | The host's references for the job by name, `{ orderId: 'ORD-9', batch: 'B-2' }`: string values only, others are dropped. Sent with the dispatch as `refs` (and each unit's as `units[].refs`) and containment-searchable on the row: `GET /escalations?facets={"refs":{"orderId":"ORD-9"}}` |
|
|
67
79
|
| `attempt` | Retry number |
|
|
68
80
|
|
|
69
81
|
The gateway dispatches to `toMachine` when the row is created, or, under a codec that dispatches on arrival, once every unit has arrived there. It claims the row as that machine once the machine accepts. It records each unit's release by the sender and each arrival at the receiver as items. It resolves the row as the receiver once every unit has arrived:
|
|
@@ -78,15 +90,15 @@ When a handoff ends without every unit (a failure, a timeout, a cancel), the gat
|
|
|
78
90
|
|
|
79
91
|
On arrival dispatch, the units are already at the receiver, so its acceptance resolves the row the same way.
|
|
80
92
|
|
|
81
|
-
When the handoff ends without a receipt, the row resolves `{ received: false, errorCode, unitId?, reason?, detail }
|
|
93
|
+
When the handoff ends without a receipt, the row resolves `{ received: false, errorCode, unitId?, result?, reason?, detail, code? }`. `detail` is text for a person; `code` is the machine's structured code when its reply carried one, so a workflow can branch on it:
|
|
82
94
|
|
|
83
95
|
| `errorCode` | Cause |
|
|
84
96
|
|---|---|
|
|
85
|
-
| `dispatch-refused` | The machine refused or failed the dispatch (mode, recipe, queue, artifact), or the machine requires an artifact and the row has none. The reason is in `detail` |
|
|
86
|
-
| `dispatch-failed` | The machine accepted the job, then could not prepare it (artifact, recipe). The reason is in `detail` |
|
|
97
|
+
| `dispatch-refused` | The machine refused or failed the dispatch (mode, recipe, queue, artifact), or the machine requires an artifact and the row has none. The reason is in `detail`, and `code` is the reply's code when it had one |
|
|
98
|
+
| `dispatch-failed` | The machine accepted the job, then could not prepare it (artifact, recipe). The reason is in `detail`, and `code` is the reply's code when it had one |
|
|
87
99
|
| `no-reply` | No reply after every attempt with the same command id, and the machine's snapshot does not list the job, or it sent none (`detail` then says the state is unknown). A job the snapshot lists counts as accepted. The dispatch is cancelled, so a copy still queued for an absent machine never runs |
|
|
88
100
|
| `software-unsupported` | The machine runs a version the line does not allow |
|
|
89
|
-
| `sender-rejected` | The sender finished a unit as reject, hold, scrap or rework. Its reason is in `reason` and `detail` |
|
|
101
|
+
| `sender-rejected` | The sender finished a unit as reject, hold, scrap or rework; `result` says which. Its reason is in `reason` and `detail` |
|
|
90
102
|
| `jam`, `incomplete-handshake`, `carrier-mismatch`, `unreadable-id` | The move between machines failed |
|
|
91
103
|
| `withdrawn` | The receiver gave the job back |
|
|
92
104
|
|
|
@@ -103,6 +115,22 @@ When a sender finishes a unit, the gateway records the release on the row and, i
|
|
|
103
115
|
|
|
104
116
|
Both are containment-searchable, so the rows that used a lot, and with them the units, are one query: `GET /escalations?facets={"lots":["LOT-7"]}`. A unit's whole path is every handoff row whose `units` holds it. The sender's full finished record stays on the row as its `released:<unit>` item. A codec maps its own lot fields into `materials`.
|
|
105
117
|
|
|
118
|
+
### Job lifecycle on the row
|
|
119
|
+
|
|
120
|
+
With `MACHINE_LIFECYCLE=on`, while a machine works a job, the gateway notes the job's milestones as items on the open row that holds it, as the machine's bot:
|
|
121
|
+
- the dispatch into the machine, until its units arrive;
|
|
122
|
+
- after that, the handoff out of the machine.
|
|
123
|
+
|
|
124
|
+
| Item | When |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `started:<unit>` | The machine reports `started` |
|
|
127
|
+
| `progress:<unit>:<step>` | `progress` crosses a step of `MACHINE_PROGRESS_STEP` percent (25 by default, so 25, 50 and 75); the row's `progress` facet holds the latest `{ percent, remainingSec, stage, detail, at }` |
|
|
128
|
+
| `stage:<unit>:<stage>` | `progress` names a `stage` the job has not reached before, for machines that report named steps rather than a percent |
|
|
129
|
+
| `paused:<alarm>:<raisedAt>` | A stopping alarm is raised at the machine (the alarm row still goes to the operator role) |
|
|
130
|
+
| `resumed:<alarm>:<raisedAt>` | The machine clears that alarm |
|
|
131
|
+
|
|
132
|
+
Every other progress message stays in the stream, so an hour-long job leaves a handful of items on its row. A row parked with a limit on its items (`accumulate: { max }`) gets no notes, so they never take a slot its units need. Off, the rows hold only their releases and arrivals. A machine whose job runs before its units arrive, such as a printer whose part exists only once the print finishes, reports the arrival at the end, and the whole job lands on the one row. A `progress` job event may carry `percent` (0 to 100), `remainingSec`, a `stage` name and a `detail` object.
|
|
133
|
+
|
|
106
134
|
### Decisions on a failed handoff
|
|
107
135
|
|
|
108
136
|
`Machines.handoffDecisions(errorCode)` gives a default set of decisions for a failed handoff. A failure in the move leaves the units on the line, so `retry`, `manual` and `scrap` all fit. Units the sender finished as not ok (`sender-rejected`) leave the line and never re-enter it, so only `manual` and `scrap` fit. After a `partial-arrival` (a timeout once only some units arrived) the units are split between two stations, and a retry of the same hop would ask the sender for units it no longer holds, so the default offers `manual` and `scrap`. A workflow is free to offer its own decisions instead, such as moving only the missing units on; the platform reports the facts and does not choose.
|
|
@@ -131,7 +159,7 @@ A unit leaves when the machine releases it, when it arrives at the next machine,
|
|
|
131
159
|
|
|
132
160
|
## Commands
|
|
133
161
|
|
|
134
|
-
A host asks a machine to start, stop, pause, resume, change over
|
|
162
|
+
A host asks a machine to start, stop, pause, resume, change over, cancel a job (`cancel` needs `args.jobId`), report a `snapshot`, or do anything its codec declares (`rowOps`, see [The wire](#the-wire)) by parking a `machine-command` row on the machine's role, from a workflow step or through the API, the way it asks a person for anything. `stop` with `args.jobId` stops that job at a safe point and holds its units; without it, the machine finishes the current unit and takes no more:
|
|
135
163
|
|
|
136
164
|
```typescript
|
|
137
165
|
const reply = await conditional<Machines.CommandResolution>(`changeover-${orderId}`, {
|
|
@@ -151,9 +179,13 @@ The gateway sends the command and resolves the row with the reply, as the machin
|
|
|
151
179
|
| `status` | Meaning |
|
|
152
180
|
|---|---|
|
|
153
181
|
| `accepted` | The machine took the command |
|
|
154
|
-
| `refused`, `failed` | The machine said no; its reason is in `reason` (a changeover to a version it does not hold is refused) |
|
|
182
|
+
| `refused`, `failed` | The machine said no; its reason is in `reason` (a changeover to a version it does not hold is refused), and `code` is the reply's structured code when it carried one |
|
|
155
183
|
| `no-reply` | No reply after every attempt. The command expired at the end of the asking, so a copy that reaches the machine later is refused, never run |
|
|
156
|
-
| `unsupported` | Not a command a row may ask for, not a machine on this line, or a command the line's codec has no message for; nothing was sent |
|
|
184
|
+
| `unsupported` | Not a command a row may ask for (a control op, `snapshot`, or one the codec declares), not a machine on this line, or a command the line's codec has no message for; nothing was sent |
|
|
185
|
+
|
|
186
|
+
A `snapshot` row resolves with the reply like any command, and the machine's snapshot follows on its own channel; a dashboard or a workflow asks for a machine's state this way.
|
|
187
|
+
|
|
188
|
+
A command is sent again, under the same `cmdId` and with the same arguments, by a gateway that takes over the line while its row is open. Its `expiresAt` counts in rounds of the asking from the row's creation, and a round begins only once the one before it has expired; a command sent in a later round goes out as a new one, `<row>:command:r<n>`, never as an old one with changed arguments. Dispatches work the same way with `MACHINE_DISPATCH_TTL_MS`, counted from the row's creation, or for `dispatchOn: 'arrival'` from the last unit's arrival, so a machine never holds two valid commands for one row.
|
|
157
189
|
|
|
158
190
|
The example's `machineCommand` workflow is this one step.
|
|
159
191
|
|
|
@@ -169,6 +201,9 @@ interface LineCodec {
|
|
|
169
201
|
unitsPerDispatch: number | null; // 1: one dispatch per unit
|
|
170
202
|
dispatchOn?: 'created' | 'arrival'; // when a handoff's job goes out
|
|
171
203
|
strict?: boolean; // broken messages go to a person, never acted on
|
|
204
|
+
snapshotOnStart?: boolean; // ask every machine for a snapshot on taking the line
|
|
205
|
+
retainedAlarms?: boolean; // active alarms stay retained on the broker until they clear
|
|
206
|
+
rowOps?: readonly string[]; // the machines' own commands a machine-command row may ask for
|
|
172
207
|
decode(topic: string, payload: string, retained?: boolean): Inspection[];
|
|
173
208
|
encode(ident: MachineIdent, command: MachineCommand): WireMessage[]; // [] when the machines lack the command
|
|
174
209
|
receipt?(ident: MachineIdent, messageIds: string[]): WireMessage[]; // for machines that keep an outbox
|
|
@@ -184,49 +219,116 @@ interface LineCodec {
|
|
|
184
219
|
- `strict: true` holds a message with contract violations: it is confirmed, never acted on, and one `contract-violation` row shows it to the operator role. Without it, known fields are acted on and the violations are logged.
|
|
185
220
|
- An alarm body may name the catalog edition its code comes from, `catalog: { domain, key, version }`. The alarm row then carries it as a lookup ref, so the row reads the code's meaning as of that edition.
|
|
186
221
|
- Event channels go to the durable stream and telemetry channels are read live. A machine reads its commands on the command channels. The broker ACL is rendered from these channels, so a codec with its own topics gets its own ACL. Unknown body fields are allowed and known ones are checked. Payloads may be up to 256 KB.
|
|
222
|
+
- `snapshotOnStart: true` is for machines that keep no retained state and report only what changes. The gateway asks each machine for a snapshot once it takes the line, so it learns every machine's current job before it acts on a change.
|
|
223
|
+
- `retainedAlarms: true` says active alarms stay retained on the broker until they clear, as in the line protocol. A gateway taking the line then reads every active alarm back, and an alarm row whose alarm is missing has cleared. Without it, a machine's snapshot lists its active alarms (`alarms`), and an alarm row whose alarm a snapshot the machine sends no longer lists resolves `cleared`; a snapshot read back from the broker is never used for this, since it may be older than the alarms.
|
|
224
|
+
- A receiver that reads only the carrier may report `arrived`, or `transfer-failed` with `unreadable-id`, with `carrierId` and no `unitId` or `jobId`. The gateway finds the unit an open handoff into that machine carries on that carrier.
|
|
225
|
+
- Each command reaches `encode` with `attempt`, how many times this gateway has sent that `cmdId`, and `retry`, true when the machine may already have it: sent earlier, or its row existed before this gateway took the line. A protocol with a retransmission flag sets it from `retry`.
|
|
187
226
|
- When `encode` returns no messages, the machines have no such command. The gateway sends nothing and waits for nothing: a machine-command row resolves `unsupported`, and a cancel or snapshot the machines lack is logged as unsupported.
|
|
227
|
+
- `rowOps` names the machines' own commands, beyond the line protocol's control ops and `snapshot`, that a machine-command row may ask for. Each goes through `encode` with the row's `args` and the row resolves with the reply. Every entry is a non-empty string; the gateway refuses to start on a codec whose entries are not.
|
|
228
|
+
- A reply may carry `code`, a string the machine chooses, beside its `reason` text. The gateway puts it on the row as `code`, on a command row's resolution and on a `dispatch-refused` or `dispatch-failed` handoff, so a workflow branches on the code and a person reads the reason.
|
|
229
|
+
- Presence may carry `sessionId`, one per connection, and `reason` on a goodbye. A presence with a new `sessionId` while the machine is online is a reconnect whose offline presence was never heard: the gateway asks for a snapshot, as it does when a machine comes back. An offline row records the `reason` the machine gave.
|
|
188
230
|
|
|
189
231
|
The generic protocol's topics are `{root}/{site}/{line}/{machine}/{channel}`. Every message is wrapped in `{ v, id, at, source, body }`.
|
|
190
232
|
|
|
191
233
|
| Channel | Purpose |
|
|
192
234
|
|---|---|
|
|
193
|
-
| `presence` (retained) | Birth and will, with the software version |
|
|
235
|
+
| `presence` (retained) | Birth and will, with the software version, a `sessionId` per connection, and on a goodbye the `reason` |
|
|
194
236
|
| `snapshot` (retained) | Mode, recipe, jobs, alarms, counters, boot id |
|
|
195
237
|
| `job` | Per unit: `queued`, `ready`, `arrived`, `bound`, `started`, `progress`, `finished`, `released`, `transfer-failed`, `withdrawn` |
|
|
196
238
|
| `alarm/<id>` (retained while active) | Raise, acknowledgement, clear (with who cleared it) |
|
|
197
239
|
| `telemetry` | Readings |
|
|
198
240
|
| `report/<name>` | Production, availability, inventory and other reporting, kept in the stream |
|
|
199
|
-
| `cmd` / `cmd/reply` | `dispatch`, `cancel`, `start`, `stop
|
|
241
|
+
| `cmd` / `cmd/reply` | `dispatch`, `cancel`, `start`, `stop` (with `jobId`: that job, at a safe point), `pause`, `resume`, `changeover`, `ack-alarm`, `snapshot`; replies `accepted`, `completed`, `refused`, `failed`, with a `reason` and an optional `code`. Every command carries a `cmdId` (a repeat is answered, never run twice) and an expiry |
|
|
200
242
|
|
|
201
243
|
## Rows for people
|
|
202
244
|
|
|
203
245
|
| Row | Raised when | Resolved when |
|
|
204
246
|
|---|---|---|
|
|
205
247
|
| `line-alarm` | A stopping alarm (critical or emergency) is raised | The machine clears it (with who cleared it). If a person resolves it first, the gateway sends `ack-alarm` |
|
|
206
|
-
| `machine-offline` | Presence goes offline past a debounce | The machine is heard again |
|
|
248
|
+
| `machine-offline` | Presence goes offline past a debounce; the row's `reason` is the machine's, when its goodbye gave one | The machine is heard again |
|
|
207
249
|
| `machine-quiet` | A connected machine sends nothing for `MACHINE_QUIET_MS` | It is heard again |
|
|
208
250
|
| `contract-violation` | A strict codec decodes a message that breaks the contract. The row holds the raw message and the violations | A person resolves it |
|
|
209
251
|
| `machine-registration` | On a line that learns addresses, an address claims a station bound to another address, or an address bound to one station claims another | A person resolves it with `{ decision: 'approve' }`, which binds the station to the claimant, or `reject` |
|
|
210
252
|
|
|
211
253
|
A replica taking a line reads back its learned addresses, its open handoff, command, alarm, offline, quiet and registration rows, and each open handoff's arrivals, so the receiver holds those units again. It then reads the machines' retained state, so a clear or a return that happened while no gateway was listening still resolves its row. A machine that restarts with a new boot id gets its accepted jobs dispatched again under the same command ids. An alarm id reused with a new raise time counts as a new alarm.
|
|
212
254
|
|
|
255
|
+
## Lines view
|
|
256
|
+
|
|
257
|
+
The dashboard shows the lines under Monitor. The view exists only when `start({ machines })` runs a surface: `GET /api/settings`, which is public, then carries `machines: { declared: n }`, the number of lines this replica runs, and names no line; without a surface the key is absent and the dashboard shows no Lines entry. Reading the view takes the ops tier (superadmin, admin type, or engineer); declaring and retiring a line takes an administrator.
|
|
258
|
+
|
|
259
|
+
**The index** (`/lines`) lists every line in the registry with how it is right now, searched and paged on the server. Each row carries the site and line, the machines with any offline or quiet in the warning tone, active alarms, rows waiting on a person, work in flight (handoff and command rows), whether this replica leads it, and at wide widths the machine chain and the source with its version. Lines needing attention sort first; the order can also be site and line, or recently changed. The filter band searches site, line, machine ids, processes, roles and addresses, narrows to one site (the sites give the index its facet, each with its lines and how many need attention), and keeps only lines that need attention. Until a search or a site narrows the page, rows group under a heading per site. On a narrow container the table folds into console cards. A panel declares a line from a JSON definition, edits and saves one already declared (the version you edited is sent, and a newer one on the server refuses the save), and retires it after a confirmation inside the panel.
|
|
260
|
+
|
|
261
|
+
**A line** (`/lines/:siteId/:lineId`) shows its machines above and the wire beneath. The machines region lists the machines as tiles (presence ring, id, process and address, software, held units, open rows by kind, active alarms) or as rows (one dense line per machine in a list that scrolls within the region, with a search over id, process, role and address). The view picks itself by count, tiles up to twelve machines and rows beyond, and the choice can be overridden per line from the two icon buttons; "Needs attention" keeps only machines that are offline, quiet, alarmed or holding open rows. The wire is a boxed section beneath: its own filter band (machine, channel, direction, kind, violations only; folded behind one Filters button on a narrow container), the count shown and on the tape, pause (new arrivals wait while you read), clear, full screen, and "Watch on the event stream", which adds the line's wire to the shell's bottom event feed so the line stays watchable anywhere in the app. The tape keeps the newest two thousand events and scrolls inside its box; a row opens the event's record. Selecting a machine opens its panel and follows it on the tape until the chip beneath the band releases the filter. The panel holds the machine's last snapshot (address, software, since and last seen, boot and session ids, jobs, alarms, held units), its open rows with links, a timeline over a window of the rows that moved work to or from it, and three tools: ask for a snapshot (a `snapshot` command row), send a command (a control op, `snapshot`, or one the codec declares, with JSON args), and inspect a message (a dry run of a topic and payload, nothing applied). A replica standing by says so and keeps the tape live from the leader.
|
|
262
|
+
|
|
263
|
+
### The wire record
|
|
264
|
+
|
|
265
|
+
Every message a line's gateway reads, and every command, snapshot request and receipt it sends, is published as one event, `machine.wire.<site>.<line>.<machineId|unknown>.<in|out>` (on NATS `lt.events.machine.wire...`, tokens sanitized as escalation subjects are). The dashboard subscribes to `lt.events.machine.wire.<site>.<line>.>`. The event's `data` is the record:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
interface WireEvent {
|
|
269
|
+
type: 'machine.wire'; siteId: string; lineId: string; machineId: string | null; address: string | null;
|
|
270
|
+
direction: 'in' | 'out'; channel: string; topic: string; id: string | null; at: string; retained: boolean;
|
|
271
|
+
kind: 'presence' | 'snapshot' | 'job' | 'alarm' | 'telemetry' | 'report' | 'cmd' | 'reply' | 'receipt' | 'unknown';
|
|
272
|
+
event: string | null; // job event, alarm raised/acked/cleared, command op, reply status, presence online/offline, snapshot mode
|
|
273
|
+
summary: string; // one line from the kind, the event and the ids the body names
|
|
274
|
+
violations: string[]; // what the contract check found
|
|
275
|
+
actions: { verb: 'claim' | 'accumulate' | 'resolve' | 'create-row' | 'refuse' | 'hold' | 'unmatched' | 'unreadable' | 'ack' | 'none'; rowId?: string; itemKey?: string; detail?: string }[];
|
|
276
|
+
payload?: unknown; // the raw wire payload; builders only, stripped before it reaches anyone else's browser
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`actions` is what the gateway did with the message: the verbs it performed on rows, with the row ids (`claim`, `accumulate` with its `itemKey`, `resolve`, `create-row`), a verb Long Tail refused (`refuse`), a message held for a person under a strict codec (`hold`), one no row claimed after every delivery (`unmatched`), one the codec or the planner could not read (`unreadable`), one admitted with nothing to do (`ack`), and `none` with a `detail` for everything else: a message waiting for its row, a dispatch or command that went out because of it (the send is its own `out` record), an address no station claims. A record of a message read back from the broker on taking the line says `retained: true`.
|
|
281
|
+
|
|
282
|
+
Telemetry messages are read live and are many: they join the wire record only with `MACHINE_WIRE_TELEMETRY=on`.
|
|
283
|
+
|
|
284
|
+
The gateway log is published the same way, `machine.log.<site>.<line>.<event>` with the entry's level and context, where `<event>` is the log event without its `machine_gateway.` prefix as one token (`dispatch-accepted`, `verb-row_not_visible`). The console log keeps every entry as before.
|
|
285
|
+
|
|
286
|
+
### Routes
|
|
287
|
+
|
|
288
|
+
| Route | Answer |
|
|
289
|
+
|---|---|
|
|
290
|
+
| `GET /api/machines/lines?q=&site=&attention=&sort=&limit=&offset=` | The index, `LineIndex`: one page of `LineIndexRow` (the `LineSummary` with its machines, process, role, address and the codec's `rowOps`; `source`, `version`, `enabled`, `updatedAt`; `leading` on this replica; and `health`: machines, and the pending rows counted as `offline`, `quiet`, `alarms`, `people` (handoff errors, contract violations, registrations), `handoff`, `command`, with `attention` when a machine is offline, an alarm is active or a person is needed), `total`, `sites` (every site with its lines and how many need attention), `limit`, `offset`. `q` searches site, line, machine ids, processes, roles and addresses (the first 200 characters count); `attention=true` keeps only lines that need it; `sort` is `attention` (default), `site` or `updated`, anything else 400; `limit` is 50 by default and 200 at most. 404 when `machines` is not declared at all |
|
|
291
|
+
| `PUT /api/machines/lines/:siteId/:lineId` `{ definition: LineDef, version? }` | Declares the line through the API (administrators): the definition is validated as `loadLine` validates a file and must name the site and line in the path, stored with `source: api`, its roles applied, and the line run on every replica. A `version` names the version the caller last read; a different one is 409. Answers `{ line: LineIndexRow }`; 400 names what the definition lacks, a version that is not a whole number, or a site or line that is not a topic level |
|
|
292
|
+
| `DELETE /api/machines/lines/:siteId/:lineId` | Retires the line (administrators): the registry row goes, every replica stops it, its roles and bot accounts stay. 204, or 404 when there is no such line or the path is not a site and line |
|
|
293
|
+
| `GET /api/machines/lines/:siteId/:lineId` | `LineViewResponse`: the `LineView` from the replica leading the line (`takenAt`, each machine's link with online, since, last seen, software, boot and session ids, held units with the rows they came in on, open row counts by kind with the offline and quiet row ids, alarms with their rows, the last snapshot's jobs, and the contested claims waiting for a person), or `{ siteId, lineId, leading: false }` from a replica standing by. Both carry `summary`, the `LineSummary` (machines, roles, addresses and the codec's ops), so the Lines view reads the line from any replica |
|
|
294
|
+
| `POST /api/machines/lines/:siteId/:lineId/inspect` `{ topic, payload, retained? }` | A dry run on the leader: what the codec reads (`kind`, `event`, `summary`, `inspections`, `violations`) and the actions the gateway would take, planned on a copy of its state. Nothing is applied and no row is created; 409 on a replica that is not leading |
|
|
295
|
+
| `POST /api/machines/lines/:siteId/:lineId/machines/:machineId/commands` `{ op, args }` | Parks a `machine-command` row on the machine's role as the caller, so that role's write access decides, and answers `{ rowId }`. `op` is a control op, `snapshot`, or one the codec declares (400 otherwise); an unknown machine is 404 |
|
|
296
|
+
|
|
297
|
+
Declaring a line from a shell:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
curl -X PUT http://localhost:3000/api/machines/lines/site-2/line-1 \
|
|
301
|
+
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
|
302
|
+
-d "{\"definition\": $(cat line.json)}"
|
|
303
|
+
curl "http://localhost:3000/api/machines/lines?q=press&attention=true" -H "authorization: Bearer $TOKEN"
|
|
304
|
+
curl -X DELETE http://localhost:3000/api/machines/lines/site-2/line-1 -H "authorization: Bearer $TOKEN"
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Each replica answers the index and the line's summary; only the leader of a line holds its state. A request-reply to the leader, so any replica can serve the live view and the dry run, and a per-line runtime switch for telemetry on the wire are the next steps.
|
|
308
|
+
|
|
309
|
+
### MCP
|
|
310
|
+
|
|
311
|
+
The built-in `long-tail-machines` server offers the same surface to agents, gated as the routes are: `list_machine_lines` (the index with health, search, site and attention filters, sort and paging; `declared: false` without a surface), `get_machine_line` (one line as its leader sees it or standing by, with its summary and command ops), `inspect_machine_message` (the dry run), `send_machine_command` (parks a `machine-command` row on the machine's role as the caller), and for administrators `declare_machine_line` (versioned; a stale version is a conflict) and `retire_machine_line`. Read-scoped callers see the three reads only. See [MCP](mcp.md#machines-server).
|
|
312
|
+
|
|
213
313
|
## Topology and identity
|
|
214
314
|
|
|
215
315
|
```
|
|
216
|
-
machines ──MQTT, TLS, client cert──▶ machine broker ──bridge──▶
|
|
217
|
-
(Mosquitto)
|
|
316
|
+
machines ──MQTT, TLS, client cert──▶ machine broker ──bridge──▶ NATS with JetStream
|
|
317
|
+
(Mosquitto) (Long Tail's messaging, or a site NATS)
|
|
218
318
|
▲ ▲
|
|
219
319
|
└── gateway (commands) └── gateway (streams, read only)
|
|
220
320
|
```
|
|
221
321
|
|
|
222
322
|
- **Machines:** each machine logs in over MQTT 3.1.1 or 5 with its own certificate; the CN (`{site}-{line}-{address}`) is its username. The broker ACL lets it write only its own channels and read only its own commands. The line controller may command every machine on the line.
|
|
223
|
-
- **Durable streams:** the bridge carries the line's topics one way
|
|
224
|
-
- **
|
|
323
|
+
- **Durable streams:** the bridge carries the line's topics one way into NATS, where only the bridge may publish them. JetStream keeps `LINE_EVENTS` (30 days) and `LINE_TELEMETRY` (3 days). The gateway reads through a durable consumer, `<controller>-events`, and may publish only JetStream requests and acks for its own streams and consumer; it cannot delete or purge them.
|
|
324
|
+
- **Where the streams live:** by default in Long Tail's messaging ([events](events.md#messaging)), whose NATS server must run JetStream. The bridge then logs in to that server's edge account for the line (`Machines.edgeAccountFor`), which may publish only the line's subjects, so the edge reaches nothing of Long Tail's. With `MACHINE_SITE_NATS_URL`, the streams live instead in a NATS server of the site's own (`Machines.renderSiteConf`), and Long Tail's NATS stays inside Long Tail. Either way the gateway reads the same streams under the same consumer name.
|
|
325
|
+
- **Escalation events:** the gateway inside Long Tail reads them in process. A standalone gateway subscribes to them over Long Tail's messaging.
|
|
225
326
|
- **Commands:** the gateway publishes commands to the machine broker at QoS 1, so they queue for a machine that is away.
|
|
226
327
|
- **Bot accounts:** in Long Tail each machine is a bot account on its station and receiver roles, so `claimed_by` and `resolved_by` name the machine. The controller bot holds those roles and the operator role, and raises the operator rows. The bots are member accounts with no password, under names the line derives; the line will not start while a person's account holds one of those names.
|
|
227
328
|
- **Rows are the authority:** an escalation event only tells the gateway which row to look at. It reads the row back as the controller and acts on the stored role, status and metadata. A row on a role the controller does not hold, or an event its stored row contradicts, does nothing.
|
|
228
329
|
- **Roles carry the access:** dispatching to a machine or commanding one means parking a row on that machine's role, so Long Tail's role access decides who can. The gateway acts on a handoff into a machine only on that machine's role, and on a handoff to a person only on a receiver role. A row commands only the machine whose role it is on: cancelling a handoff withdraws the receiver's dispatch, and the sender's job is stopped only by a `cancel` command row on the sender's role.
|
|
229
|
-
- **Rendering:** `Machines.renderSiteConf`, `renderMosquittoConf` and `renderMosquittoAcl(
|
|
330
|
+
- **Rendering:** `Machines.renderSiteConf`, `renderMosquittoConf` and `renderMosquittoAcl(lines, codec)` render the configuration from one line or several on the same broker and site NATS: one bridge identity per line, one gateway user allowed every line's streams, and each line's stations and controller in the ACL. The ACL takes its topics from the codec's channels.
|
|
331
|
+
- **Machines that host their own broker:** the machine broker connects out to each machine's broker with a Mosquitto bridge, carrying the machine's messages in and its commands out under the machine's level of the line's topics, and its codec reads them there. The bridge's notification topic, kept on the machine broker, is the machine's presence. `examples/workflows/bambu-printer` does this for 3D printers.
|
|
230
332
|
|
|
231
333
|
### Learned addresses
|
|
232
334
|
|
|
@@ -243,11 +345,11 @@ A line with `addressing: 'learned'` names its stations and lets each machine tel
|
|
|
243
345
|
|
|
244
346
|
| Variable | Default | Meaning |
|
|
245
347
|
|---|---|---|
|
|
246
|
-
| `MACHINE_TRANSPORT` | | `nats` (
|
|
247
|
-
| `MACHINE_ESCALATION_FEED` | `in-process` inside Long Tail, `core` for a standalone gateway | Where escalation events come from; `core` subscribes
|
|
348
|
+
| `MACHINE_TRANSPORT` | | `nats` (durable streams) or `mqtt` (any MQTT broker, no replay beyond the session) |
|
|
349
|
+
| `MACHINE_ESCALATION_FEED` | `in-process` inside Long Tail, `core` for a standalone gateway | Where escalation events come from; `core` subscribes over Long Tail's messaging, or to `NATS_URL` with `NATS_TOKEN` when the gateway has none |
|
|
248
350
|
| `MACHINE_TOPIC_ROOT` | `line/v1` | The generic codec's root |
|
|
249
|
-
| `MACHINE_LINE_CONFIG` | |
|
|
250
|
-
| `MACHINE_SITE_NATS_URL`, `_USER`, `_PASSWORD` | |
|
|
351
|
+
| `MACHINE_LINE_CONFIG` | | The line JSON files, one path or several separated by commas; each site and line once |
|
|
352
|
+
| `MACHINE_SITE_NATS_URL`, `_USER`, `_PASSWORD` | | A site NATS of its own for the streams; unset, `nats` keeps them in Long Tail's messaging |
|
|
251
353
|
| `MACHINE_MQTT_URL`, `_PROTOCOL` | `_PROTOCOL` 5 | Machine broker, for commands and retained reads; 4 for a broker that speaks only MQTT 3.1.1 |
|
|
252
354
|
| `MACHINE_MQTT_CA`, `_CERT`, `_KEY` | | TLS files; `{clientId}` in a path picks each identity's certificate |
|
|
253
355
|
| `MACHINE_REPLY_TIMEOUT_MS` | `5000` | Wait for a command reply |
|
|
@@ -256,6 +358,10 @@ A line with `addressing: 'learned'` names its stations and lets each machine tel
|
|
|
256
358
|
| `MACHINE_OFFLINE_DEBOUNCE_MS` | `15000` | Offline this long before a row |
|
|
257
359
|
| `MACHINE_QUIET_MS` | `90000` | Silent this long before a row; `0` turns it off |
|
|
258
360
|
| `MACHINE_RECEIPT_DELAY_MS` | `250` | How long confirmed message ids gather before a receipt goes out |
|
|
361
|
+
| `MACHINE_LIFECYCLE` | `off` | `on` notes each job's start, progress steps and pauses on the row that holds it |
|
|
362
|
+
| `MACHINE_PROGRESS_STEP` | `25` | With lifecycle notes on, progress is noted each time it crosses this many percent; `0` notes none |
|
|
363
|
+
| `MACHINE_WIRE_TELEMETRY` | `off` | `on` publishes telemetry messages on the wire record too ([Lines view](#lines-view)) |
|
|
364
|
+
| `MACHINE_CODEC_MODULE` | | Development server only: a module whose `createCodec(line)` speaks the machines' protocol, for a single line |
|
|
259
365
|
|
|
260
366
|
## Testing a codec or adapter
|
|
261
367
|
|
package/docs/mcp.md
CHANGED
|
@@ -11,6 +11,7 @@ Your agents speak MCP. Long Tail makes their tool calls durable and exposes huma
|
|
|
11
11
|
- [Connecting an Agent to Long Tail](#connecting-an-agent-to-long-tail) — the `/mcp` endpoint, service accounts, multi-environment `.mcp.json`, example prompts
|
|
12
12
|
- [Connecting Long Tail Instances](#connecting-long-tail-instances): one instance calling another's tools over MCP
|
|
13
13
|
- [Human Queue Server](#human-queue-server) — escalation as MCP tools
|
|
14
|
+
- [Machines Server](#machines-server): the machine surface as MCP tools
|
|
14
15
|
- [Document Vision Server](#document-vision-server): AI tools as MCP tools
|
|
15
16
|
- [Server Registration Lifecycle](#server-registration-lifecycle) — two paths, startup sequence, when things connect
|
|
16
17
|
- [External MCP Servers](#external-mcp-servers) — Path B: dashboard/API registration
|
|
@@ -422,6 +423,23 @@ const work = await client.callTool({
|
|
|
422
423
|
|
|
423
424
|
The Human Queue handles the people side. The Vision server handles the AI side, wrapping model capabilities as MCP tools.
|
|
424
425
|
|
|
426
|
+
## Machines Server
|
|
427
|
+
|
|
428
|
+
The machines server is a built-in MCP server over the [machine surface](machines.md): the lines a deployment runs, their health, the wire, and the registry that declares them. It is listed when `start({ machines })` runs a surface; without one its reads answer `declared: false`.
|
|
429
|
+
|
|
430
|
+
### Tools
|
|
431
|
+
|
|
432
|
+
| Tool | Purpose | Who |
|
|
433
|
+
|------|---------|-----|
|
|
434
|
+
| `list_machine_lines` | The lines index: every line with its health (machines, offline, quiet, alarms, people, work in flight), searched by site, line, machine, process, role or address, narrowed to a site or to lines needing attention, sorted and paged | any caller |
|
|
435
|
+
| `get_machine_line` | One line as its leader sees it (links, held units, open rows, alarms, the last snapshot's jobs, contested claims), or `leading: false` from a replica standing by; always with the line's summary and the command ops its codec allows | any caller |
|
|
436
|
+
| `inspect_machine_message` | A dry run of one wire message on the leader's state: what the codec reads and what the gateway would do, nothing applied | any caller |
|
|
437
|
+
| `send_machine_command` | Parks a `machine-command` row on the machine's role as the caller, so that role's write access decides; the gateway resolves the row with the machine's reply, and reading the row gives the outcome | any caller |
|
|
438
|
+
| `declare_machine_line` | Declares or updates a line in the registry: validated, versioned, its roles applied, run on every replica; a stale `version` is a conflict | administrators |
|
|
439
|
+
| `retire_machine_line` | Removes a line from the registry; every replica stops it, its roles and bot accounts stay | administrators |
|
|
440
|
+
|
|
441
|
+
Read-scoped callers (`mcp:read`, or a read-only exposure) see the three reads only. The tools mirror the [routes](machines.md#routes) under `/api/machines`.
|
|
442
|
+
|
|
425
443
|
## Document Vision Server
|
|
426
444
|
|
|
427
445
|
The Vision server (`system/mcp-servers/vision.ts`, server ID `long-tail-vision`) wraps LLM vision calls as MCP tools. Zod schemas sit at module level, and `createVisionServer()` returns a fresh `McpServer` instance per call because the MCP SDK allows one transport per server. Full parameter reference: [Vision tools](api/mcp/vision.md).
|
|
@@ -826,6 +844,7 @@ Long Tail ships with built-in MCP servers. For descriptions, see the [architectu
|
|
|
826
844
|
|--------|------|
|
|
827
845
|
| `long-tail-db-query` | database, query, analytics |
|
|
828
846
|
| `long-tail-human-queue` | escalation, human-queue, routing |
|
|
847
|
+
| `long-tail-machines` | machines, lines, mqtt, factory |
|
|
829
848
|
| `mcp-workflows-longtail` | workflows, compiled, deterministic |
|
|
830
849
|
| `long-tail-workflow-compiler` | compilation, yaml, codegen |
|
|
831
850
|
| `long-tail-translation` | translation, language, text-processing |
|