@antelopejs/dms-automation 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +95 -0
  3. package/dist/cluster/bus.d.ts +17 -0
  4. package/dist/cluster/bus.js +82 -0
  5. package/dist/cluster/bus.js.map +1 -0
  6. package/dist/cluster/instance.d.ts +1 -0
  7. package/dist/cluster/instance.js +6 -0
  8. package/dist/cluster/instance.js.map +1 -0
  9. package/dist/cluster/leader.d.ts +7 -0
  10. package/dist/cluster/leader.js +123 -0
  11. package/dist/cluster/leader.js.map +1 -0
  12. package/dist/cluster/mode.d.ts +3 -0
  13. package/dist/cluster/mode.js +25 -0
  14. package/dist/cluster/mode.js.map +1 -0
  15. package/dist/data/procedures-table.d.ts +66 -0
  16. package/dist/data/procedures-table.js +271 -0
  17. package/dist/data/procedures-table.js.map +1 -0
  18. package/dist/data/runs-table.d.ts +23 -0
  19. package/dist/data/runs-table.js +125 -0
  20. package/dist/data/runs-table.js.map +1 -0
  21. package/dist/db/index.d.ts +7 -0
  22. package/dist/db/index.js +24 -0
  23. package/dist/db/index.js.map +1 -0
  24. package/dist/db/models/automation_template.model.d.ts +58 -0
  25. package/dist/db/models/automation_template.model.js +134 -0
  26. package/dist/db/models/automation_template.model.js.map +1 -0
  27. package/dist/db/models/index.d.ts +3 -0
  28. package/dist/db/models/index.js +20 -0
  29. package/dist/db/models/index.js.map +1 -0
  30. package/dist/db/models/procedure.model.d.ts +25 -0
  31. package/dist/db/models/procedure.model.js +28 -0
  32. package/dist/db/models/procedure.model.js.map +1 -0
  33. package/dist/db/models/procedure_run.model.d.ts +34 -0
  34. package/dist/db/models/procedure_run.model.js +39 -0
  35. package/dist/db/models/procedure_run.model.js.map +1 -0
  36. package/dist/db/models/stats.model.d.ts +73 -0
  37. package/dist/db/models/stats.model.js +267 -0
  38. package/dist/db/models/stats.model.js.map +1 -0
  39. package/dist/db/models/type_config.model.d.ts +47 -0
  40. package/dist/db/models/type_config.model.js +51 -0
  41. package/dist/db/models/type_config.model.js.map +1 -0
  42. package/dist/db/tables/action_type_config.table.d.ts +7 -0
  43. package/dist/db/tables/action_type_config.table.js +35 -0
  44. package/dist/db/tables/action_type_config.table.js.map +1 -0
  45. package/dist/db/tables/automation_template.table.d.ts +11 -0
  46. package/dist/db/tables/automation_template.table.js +52 -0
  47. package/dist/db/tables/automation_template.table.js.map +1 -0
  48. package/dist/db/tables/index.d.ts +5 -0
  49. package/dist/db/tables/index.js +22 -0
  50. package/dist/db/tables/index.js.map +1 -0
  51. package/dist/db/tables/procedure.table.d.ts +10 -0
  52. package/dist/db/tables/procedure.table.js +48 -0
  53. package/dist/db/tables/procedure.table.js.map +1 -0
  54. package/dist/db/tables/procedure_run.table.d.ts +13 -0
  55. package/dist/db/tables/procedure_run.table.js +57 -0
  56. package/dist/db/tables/procedure_run.table.js.map +1 -0
  57. package/dist/db/tables/trigger_type_config.table.d.ts +7 -0
  58. package/dist/db/tables/trigger_type_config.table.js +35 -0
  59. package/dist/db/tables/trigger_type_config.table.js.map +1 -0
  60. package/dist/implementations/dms-automation/index.d.ts +19 -0
  61. package/dist/implementations/dms-automation/index.js +50 -0
  62. package/dist/implementations/dms-automation/index.js.map +1 -0
  63. package/dist/index.d.ts +14 -0
  64. package/dist/index.js +121 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/pages/builder.d.ts +10 -0
  67. package/dist/pages/builder.js +28 -0
  68. package/dist/pages/builder.js.map +1 -0
  69. package/dist/pages/category.d.ts +1 -0
  70. package/dist/pages/category.js +10 -0
  71. package/dist/pages/category.js.map +1 -0
  72. package/dist/pages/index.d.ts +6 -0
  73. package/dist/pages/index.js +9 -0
  74. package/dist/pages/index.js.map +1 -0
  75. package/dist/pages/library.d.ts +10 -0
  76. package/dist/pages/library.js +28 -0
  77. package/dist/pages/library.js.map +1 -0
  78. package/dist/pages/overview.d.ts +10 -0
  79. package/dist/pages/overview.js +28 -0
  80. package/dist/pages/overview.js.map +1 -0
  81. package/dist/pages/procedures.d.ts +10 -0
  82. package/dist/pages/procedures.js +72 -0
  83. package/dist/pages/procedures.js.map +1 -0
  84. package/dist/pages/runs.d.ts +11 -0
  85. package/dist/pages/runs.js +61 -0
  86. package/dist/pages/runs.js.map +1 -0
  87. package/dist/routes/action-types.d.ts +23 -0
  88. package/dist/routes/action-types.js +80 -0
  89. package/dist/routes/action-types.js.map +1 -0
  90. package/dist/routes/data-node-types.d.ts +6 -0
  91. package/dist/routes/data-node-types.js +36 -0
  92. package/dist/routes/data-node-types.js.map +1 -0
  93. package/dist/routes/graphSchema.d.ts +92 -0
  94. package/dist/routes/graphSchema.js +60 -0
  95. package/dist/routes/graphSchema.js.map +1 -0
  96. package/dist/routes/index.d.ts +8 -0
  97. package/dist/routes/index.js +11 -0
  98. package/dist/routes/index.js.map +1 -0
  99. package/dist/routes/node-kinds.d.ts +9 -0
  100. package/dist/routes/node-kinds.js +35 -0
  101. package/dist/routes/node-kinds.js.map +1 -0
  102. package/dist/routes/procedures.d.ts +23 -0
  103. package/dist/routes/procedures.js +181 -0
  104. package/dist/routes/procedures.js.map +1 -0
  105. package/dist/routes/runs.d.ts +7 -0
  106. package/dist/routes/runs.js +81 -0
  107. package/dist/routes/runs.js.map +1 -0
  108. package/dist/routes/stats.d.ts +6 -0
  109. package/dist/routes/stats.js +35 -0
  110. package/dist/routes/stats.js.map +1 -0
  111. package/dist/routes/templates.d.ts +20 -0
  112. package/dist/routes/templates.js +243 -0
  113. package/dist/routes/templates.js.map +1 -0
  114. package/dist/routes/trigger-types.d.ts +24 -0
  115. package/dist/routes/trigger-types.js +80 -0
  116. package/dist/routes/trigger-types.js.map +1 -0
  117. package/dist/routes/typeConfigShared.d.ts +22 -0
  118. package/dist/routes/typeConfigShared.js +37 -0
  119. package/dist/routes/typeConfigShared.js.map +1 -0
  120. package/dist/runtime/builtins/actions/http.d.ts +13 -0
  121. package/dist/runtime/builtins/actions/http.js +47 -0
  122. package/dist/runtime/builtins/actions/http.js.map +1 -0
  123. package/dist/runtime/builtins/actions/index.d.ts +2 -0
  124. package/dist/runtime/builtins/actions/index.js +7 -0
  125. package/dist/runtime/builtins/actions/index.js.map +1 -0
  126. package/dist/runtime/builtins/actions/log.d.ts +13 -0
  127. package/dist/runtime/builtins/actions/log.js +49 -0
  128. package/dist/runtime/builtins/actions/log.js.map +1 -0
  129. package/dist/runtime/builtins/data/boolean.d.ts +2 -0
  130. package/dist/runtime/builtins/data/boolean.js +39 -0
  131. package/dist/runtime/builtins/data/boolean.js.map +1 -0
  132. package/dist/runtime/builtins/data/compare.d.ts +2 -0
  133. package/dist/runtime/builtins/data/compare.js +29 -0
  134. package/dist/runtime/builtins/data/compare.js.map +1 -0
  135. package/dist/runtime/builtins/data/constant.d.ts +2 -0
  136. package/dist/runtime/builtins/data/constant.js +29 -0
  137. package/dist/runtime/builtins/data/constant.js.map +1 -0
  138. package/dist/runtime/builtins/data/index.d.ts +2 -0
  139. package/dist/runtime/builtins/data/index.js +20 -0
  140. package/dist/runtime/builtins/data/index.js.map +1 -0
  141. package/dist/runtime/builtins/data/json.d.ts +2 -0
  142. package/dist/runtime/builtins/data/json.js +46 -0
  143. package/dist/runtime/builtins/data/json.js.map +1 -0
  144. package/dist/runtime/builtins/data/math.d.ts +2 -0
  145. package/dist/runtime/builtins/data/math.js +53 -0
  146. package/dist/runtime/builtins/data/math.js.map +1 -0
  147. package/dist/runtime/builtins/data/object.d.ts +2 -0
  148. package/dist/runtime/builtins/data/object.js +121 -0
  149. package/dist/runtime/builtins/data/object.js.map +1 -0
  150. package/dist/runtime/builtins/data/string.d.ts +2 -0
  151. package/dist/runtime/builtins/data/string.js +143 -0
  152. package/dist/runtime/builtins/data/string.js.map +1 -0
  153. package/dist/runtime/builtins/index.d.ts +17 -0
  154. package/dist/runtime/builtins/index.js +24 -0
  155. package/dist/runtime/builtins/index.js.map +1 -0
  156. package/dist/runtime/builtins/nodes/action.d.ts +1 -0
  157. package/dist/runtime/builtins/nodes/action.js +36 -0
  158. package/dist/runtime/builtins/nodes/action.js.map +1 -0
  159. package/dist/runtime/builtins/nodes/data.d.ts +1 -0
  160. package/dist/runtime/builtins/nodes/data.js +28 -0
  161. package/dist/runtime/builtins/nodes/data.js.map +1 -0
  162. package/dist/runtime/builtins/nodes/delay.d.ts +1 -0
  163. package/dist/runtime/builtins/nodes/delay.js +41 -0
  164. package/dist/runtime/builtins/nodes/delay.js.map +1 -0
  165. package/dist/runtime/builtins/nodes/forRange.d.ts +1 -0
  166. package/dist/runtime/builtins/nodes/forRange.js +52 -0
  167. package/dist/runtime/builtins/nodes/forRange.js.map +1 -0
  168. package/dist/runtime/builtins/nodes/foreach.d.ts +1 -0
  169. package/dist/runtime/builtins/nodes/foreach.js +31 -0
  170. package/dist/runtime/builtins/nodes/foreach.js.map +1 -0
  171. package/dist/runtime/builtins/nodes/groupInput.d.ts +1 -0
  172. package/dist/runtime/builtins/nodes/groupInput.js +11 -0
  173. package/dist/runtime/builtins/nodes/groupInput.js.map +1 -0
  174. package/dist/runtime/builtins/nodes/groupOutput.d.ts +1 -0
  175. package/dist/runtime/builtins/nodes/groupOutput.js +11 -0
  176. package/dist/runtime/builtins/nodes/groupOutput.js.map +1 -0
  177. package/dist/runtime/builtins/nodes/if.d.ts +1 -0
  178. package/dist/runtime/builtins/nodes/if.js +23 -0
  179. package/dist/runtime/builtins/nodes/if.js.map +1 -0
  180. package/dist/runtime/builtins/nodes/parallel.d.ts +1 -0
  181. package/dist/runtime/builtins/nodes/parallel.js +34 -0
  182. package/dist/runtime/builtins/nodes/parallel.js.map +1 -0
  183. package/dist/runtime/builtins/nodes/retry.d.ts +1 -0
  184. package/dist/runtime/builtins/nodes/retry.js +46 -0
  185. package/dist/runtime/builtins/nodes/retry.js.map +1 -0
  186. package/dist/runtime/builtins/nodes/setVariable.d.ts +1 -0
  187. package/dist/runtime/builtins/nodes/setVariable.js +31 -0
  188. package/dist/runtime/builtins/nodes/setVariable.js.map +1 -0
  189. package/dist/runtime/builtins/nodes/switch.d.ts +1 -0
  190. package/dist/runtime/builtins/nodes/switch.js +33 -0
  191. package/dist/runtime/builtins/nodes/switch.js.map +1 -0
  192. package/dist/runtime/builtins/nodes/trigger.d.ts +1 -0
  193. package/dist/runtime/builtins/nodes/trigger.js +25 -0
  194. package/dist/runtime/builtins/nodes/trigger.js.map +1 -0
  195. package/dist/runtime/builtins/nodes/tryCatch.d.ts +1 -0
  196. package/dist/runtime/builtins/nodes/tryCatch.js +35 -0
  197. package/dist/runtime/builtins/nodes/tryCatch.js.map +1 -0
  198. package/dist/runtime/builtins/triggers/cron.d.ts +9 -0
  199. package/dist/runtime/builtins/triggers/cron.js +52 -0
  200. package/dist/runtime/builtins/triggers/cron.js.map +1 -0
  201. package/dist/runtime/builtins/triggers/index.d.ts +2 -0
  202. package/dist/runtime/builtins/triggers/index.js +12 -0
  203. package/dist/runtime/builtins/triggers/index.js.map +1 -0
  204. package/dist/runtime/builtins/triggers/manual.d.ts +3 -0
  205. package/dist/runtime/builtins/triggers/manual.js +20 -0
  206. package/dist/runtime/builtins/triggers/manual.js.map +1 -0
  207. package/dist/runtime/builtins/triggers/webhook.d.ts +14 -0
  208. package/dist/runtime/builtins/triggers/webhook.js +90 -0
  209. package/dist/runtime/builtins/triggers/webhook.js.map +1 -0
  210. package/dist/runtime/executionResult.d.ts +6 -0
  211. package/dist/runtime/executionResult.js +3 -0
  212. package/dist/runtime/executionResult.js.map +1 -0
  213. package/dist/runtime/executor.d.ts +9 -0
  214. package/dist/runtime/executor.js +300 -0
  215. package/dist/runtime/executor.js.map +1 -0
  216. package/dist/runtime/flatten.d.ts +3 -0
  217. package/dist/runtime/flatten.js +188 -0
  218. package/dist/runtime/flatten.js.map +1 -0
  219. package/dist/runtime/nodeKinds.d.ts +64 -0
  220. package/dist/runtime/nodeKinds.js +43 -0
  221. package/dist/runtime/nodeKinds.js.map +1 -0
  222. package/dist/runtime/reconcileDiff.d.ts +9 -0
  223. package/dist/runtime/reconcileDiff.js +26 -0
  224. package/dist/runtime/reconcileDiff.js.map +1 -0
  225. package/dist/runtime/registry.d.ts +19 -0
  226. package/dist/runtime/registry.js +47 -0
  227. package/dist/runtime/registry.js.map +1 -0
  228. package/dist/runtime/resolveInputs.d.ts +14 -0
  229. package/dist/runtime/resolveInputs.js +64 -0
  230. package/dist/runtime/resolveInputs.js.map +1 -0
  231. package/dist/runtime/resolveSubgraph.d.ts +7 -0
  232. package/dist/runtime/resolveSubgraph.js +15 -0
  233. package/dist/runtime/resolveSubgraph.js.map +1 -0
  234. package/dist/runtime/subscriptions.d.ts +16 -0
  235. package/dist/runtime/subscriptions.js +419 -0
  236. package/dist/runtime/subscriptions.js.map +1 -0
  237. package/dist/runtime/subwalk.d.ts +17 -0
  238. package/dist/runtime/subwalk.js +74 -0
  239. package/dist/runtime/subwalk.js.map +1 -0
  240. package/dist/runtime/topo.d.ts +3 -0
  241. package/dist/runtime/topo.js +75 -0
  242. package/dist/runtime/topo.js.map +1 -0
  243. package/dist/runtime/typeOrigin.d.ts +7 -0
  244. package/dist/runtime/typeOrigin.js +24 -0
  245. package/dist/runtime/typeOrigin.js.map +1 -0
  246. package/dist/runtime/validate.d.ts +10 -0
  247. package/dist/runtime/validate.js +185 -0
  248. package/dist/runtime/validate.js.map +1 -0
  249. package/dist/runtime/validateTemplate.d.ts +3 -0
  250. package/dist/runtime/validateTemplate.js +54 -0
  251. package/dist/runtime/validateTemplate.js.map +1 -0
  252. package/dist/types/constants.d.ts +6 -0
  253. package/dist/types/constants.js +10 -0
  254. package/dist/types/constants.js.map +1 -0
  255. package/dist/types/graph.d.ts +51 -0
  256. package/dist/types/graph.js +23 -0
  257. package/dist/types/graph.js.map +1 -0
  258. package/dist/types/pagination.d.ts +8 -0
  259. package/dist/types/pagination.js +3 -0
  260. package/dist/types/pagination.js.map +1 -0
  261. package/dist/types/runLog.d.ts +26 -0
  262. package/dist/types/runLog.js +3 -0
  263. package/dist/types/runLog.js.map +1 -0
  264. package/frontend-vue/.prettierignore +4 -0
  265. package/frontend-vue/app/components/Actions.vue +141 -0
  266. package/frontend-vue/app/components/Catalog/TypeConfigForm.vue +166 -0
  267. package/frontend-vue/app/components/Catalog/TypeList.vue +198 -0
  268. package/frontend-vue/app/components/DataNodes.vue +259 -0
  269. package/frontend-vue/app/components/Editor/BuilderPalette.vue +234 -0
  270. package/frontend-vue/app/components/Editor/GraphCanvas.vue +994 -0
  271. package/frontend-vue/app/components/Editor/GroupPortEditor.vue +289 -0
  272. package/frontend-vue/app/components/Editor/JsonField.vue +63 -0
  273. package/frontend-vue/app/components/Editor/NodeInspector.vue +356 -0
  274. package/frontend-vue/app/components/Editor/ProcedureList.vue +156 -0
  275. package/frontend-vue/app/components/Editor/RunsDrawer.vue +312 -0
  276. package/frontend-vue/app/components/Editor/WiredField.vue +42 -0
  277. package/frontend-vue/app/components/Editor/nodes/GenericNode.vue +161 -0
  278. package/frontend-vue/app/components/Editor/nodes/GroupInputNode.vue +19 -0
  279. package/frontend-vue/app/components/Editor/nodes/GroupNode.vue +200 -0
  280. package/frontend-vue/app/components/Editor/nodes/GroupOutputNode.vue +19 -0
  281. package/frontend-vue/app/components/Editor/nodes/GroupSentinel.vue +161 -0
  282. package/frontend-vue/app/components/Editor/nodes/styles.ts +53 -0
  283. package/frontend-vue/app/components/Editor.vue +2504 -0
  284. package/frontend-vue/app/components/Library.vue +67 -0
  285. package/frontend-vue/app/components/Overview.vue +503 -0
  286. package/frontend-vue/app/components/RunDetailDrawer.vue +165 -0
  287. package/frontend-vue/app/components/RunFireTree.vue +170 -0
  288. package/frontend-vue/app/components/RunTraceDrawer.vue +59 -0
  289. package/frontend-vue/app/components/RunsStats.vue +126 -0
  290. package/frontend-vue/app/components/RunsTimelineDisplay.vue +287 -0
  291. package/frontend-vue/app/components/Templates.vue +663 -0
  292. package/frontend-vue/app/components/Triggers.vue +137 -0
  293. package/frontend-vue/app/composables/useAutomationNodeKinds.ts +48 -0
  294. package/frontend-vue/app/composables/useAutomationRuns.ts +75 -0
  295. package/frontend-vue/app/composables/useGraphConnect.ts +542 -0
  296. package/frontend-vue/app/plugins/runs-timeline-display.client.ts +16 -0
  297. package/frontend-vue/app/utils/automation.ts +51 -0
  298. package/frontend-vue/dms.frontend.ts +26 -0
  299. package/frontend-vue/eslint.config.mjs +24 -0
  300. package/frontend-vue/i18n/locales/dms-automation-en-GB.json +369 -0
  301. package/frontend-vue/i18n/locales/dms-automation-fr-FR.json +369 -0
  302. package/frontend-vue/package.json +33 -0
  303. package/frontend-vue/pnpm-lock.yaml +6483 -0
  304. package/frontend-vue/pnpm-workspace.yaml +2 -0
  305. package/package.json +81 -0
@@ -0,0 +1,165 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type {
4
+ FireNode,
5
+ LogEntry,
6
+ RunDetail,
7
+ } from '../composables/useAutomationRuns'
8
+ import RunFireTree from './RunFireTree.vue'
9
+
10
+ const props = defineProps<{
11
+ run: RunDetail
12
+ procedureName?: string
13
+ }>()
14
+
15
+ const statusColor = computed<'success' | 'error' | 'warning' | 'neutral'>(
16
+ () => {
17
+ if (props.run.status === 'ok') return 'success'
18
+ if (props.run.status === 'failed') return 'error'
19
+ return 'neutral'
20
+ },
21
+ )
22
+
23
+ const startedAt = computed(() =>
24
+ props.run.startedAt ? new Date(props.run.startedAt).toLocaleString() : '—',
25
+ )
26
+
27
+ const durationMs = computed<string>(() => {
28
+ if (!props.run.startedAt || !props.run.endedAt) return '—'
29
+ const s = new Date(props.run.startedAt).getTime()
30
+ const e = new Date(props.run.endedAt).getTime()
31
+ if (Number.isNaN(s) || Number.isNaN(e)) return '—'
32
+ return `${e - s} ms`
33
+ })
34
+
35
+ function prettyJson(text?: string | null): string {
36
+ if (!text) return ''
37
+ try {
38
+ return JSON.stringify(JSON.parse(text), null, 2)
39
+ } catch {
40
+ return text
41
+ }
42
+ }
43
+
44
+ const payloadJson = computed(() => prettyJson(props.run.triggerPayload))
45
+ const hasPayload = computed(
46
+ () => !!props.run.triggerPayload && props.run.triggerPayload !== '{}',
47
+ )
48
+
49
+ const fires = computed<FireNode[]>(() => props.run.logs?.fires ?? [])
50
+ const entries = computed<LogEntry[]>(() => props.run.logs?.entries ?? [])
51
+ const hasLogs = computed(
52
+ () => fires.value.length > 0 || entries.value.length > 0,
53
+ )
54
+
55
+ // Index entries and child fires by their parent fire id so the recursive
56
+ // RunFireTree component can do constant-time lookups while rendering.
57
+ const childrenOf = computed<Map<string, FireNode[]>>(() => {
58
+ const m = new Map<string, FireNode[]>()
59
+ for (const f of fires.value) {
60
+ if (f.parentFireId === null) continue
61
+ let list = m.get(f.parentFireId)
62
+ if (!list) {
63
+ list = []
64
+ m.set(f.parentFireId, list)
65
+ }
66
+ list.push(f)
67
+ }
68
+ return m
69
+ })
70
+
71
+ const entriesOf = computed<Map<string, LogEntry[]>>(() => {
72
+ const m = new Map<string, LogEntry[]>()
73
+ for (const e of entries.value) {
74
+ let list = m.get(e.fireId)
75
+ if (!list) {
76
+ list = []
77
+ m.set(e.fireId, list)
78
+ }
79
+ list.push(e)
80
+ }
81
+ return m
82
+ })
83
+
84
+ const rootFires = computed<FireNode[]>(() =>
85
+ fires.value.filter((f) => f.parentFireId === null),
86
+ )
87
+ </script>
88
+
89
+ <template>
90
+ <div class="flex flex-col gap-4 p-1">
91
+ <div class="flex flex-col gap-2">
92
+ <div class="flex flex-wrap items-center gap-2">
93
+ <UBadge :color="statusColor" variant="subtle" class="font-semibold">
94
+ {{ run.status.toUpperCase() }}
95
+ </UBadge>
96
+ <code
97
+ class="rounded-md bg-muted px-2 py-1 font-mono text-sm text-highlighted"
98
+ >
99
+ {{ procedureName ?? run.procedureId }}
100
+ </code>
101
+ </div>
102
+ <div class="flex flex-wrap items-center gap-2 text-xs text-dimmed">
103
+ <span class="tabular-nums">{{ durationMs }}</span>
104
+ <span>·</span>
105
+ <span class="tabular-nums">{{ startedAt }}</span>
106
+ <span v-if="run.triggerNodeId">
107
+ · {{ $t('dms_automation.runs.detail.trigger') }}
108
+ <code class="font-mono">{{ run.triggerNodeId }}</code>
109
+ </span>
110
+ </div>
111
+ <p class="text-xs text-muted">
112
+ {{ $t('dms_automation.runs.detail.runId') }}
113
+ <code class="font-mono">{{ run._id }}</code>
114
+ </p>
115
+ </div>
116
+
117
+ <USeparator />
118
+
119
+ <section v-if="run.errorMessage" class="flex flex-col gap-2">
120
+ <h4 class="text-sm font-semibold text-error">
121
+ {{ $t('dms_automation.runs.detail.error') }}
122
+ </h4>
123
+ <pre
124
+ class="max-h-48 overflow-auto rounded-lg border border-default bg-muted p-3 text-[11px] text-toned"
125
+ >{{ run.errorMessage }}</pre>
126
+ </section>
127
+
128
+ <section v-if="hasPayload" class="flex flex-col gap-2">
129
+ <h4 class="text-sm font-semibold text-highlighted">
130
+ {{ $t('dms_automation.runs.detail.triggerPayload') }}
131
+ </h4>
132
+ <pre
133
+ class="max-h-64 overflow-auto rounded-lg border border-default bg-muted p-3 text-[11px] text-toned"
134
+ >{{ payloadJson }}</pre>
135
+ </section>
136
+
137
+ <section class="flex flex-col gap-2">
138
+ <h4 class="text-sm font-semibold text-highlighted">
139
+ {{ $t('dms_automation.runs.detail.logs') }}
140
+ </h4>
141
+ <p v-if="!hasLogs" class="text-xs italic text-dimmed">
142
+ {{ $t('dms_automation.runs.detail.noLogs') }}
143
+ </p>
144
+ <div
145
+ v-else
146
+ class="flex flex-col overflow-hidden rounded-lg border border-default"
147
+ >
148
+ <RunFireTree
149
+ v-for="fire in rootFires"
150
+ :key="fire.id"
151
+ :fire="fire"
152
+ :children-of="childrenOf"
153
+ :entries-of="entriesOf"
154
+ />
155
+ </div>
156
+ </section>
157
+
158
+ <p
159
+ v-if="!run.errorMessage && !hasPayload && !hasLogs"
160
+ class="text-xs italic text-dimmed"
161
+ >
162
+ {{ $t('dms_automation.runs.detail.none') }}
163
+ </p>
164
+ </div>
165
+ </template>
@@ -0,0 +1,170 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import type {
4
+ FireNode,
5
+ LogEntry,
6
+ } from '../composables/useAutomationRuns'
7
+
8
+ const props = defineProps<{
9
+ fire: FireNode
10
+ childrenOf: Map<string, FireNode[]>
11
+ entriesOf: Map<string, LogEntry[]>
12
+ }>()
13
+
14
+ defineOptions({ name: 'RunFireTree' })
15
+
16
+ const open = ref(true)
17
+ function toggle() {
18
+ open.value = !open.value
19
+ }
20
+
21
+ const ownEntries = computed<LogEntry[]>(
22
+ () => props.entriesOf.get(props.fire.id) ?? [],
23
+ )
24
+ const childFires = computed<FireNode[]>(
25
+ () => props.childrenOf.get(props.fire.id) ?? [],
26
+ )
27
+
28
+ type Item =
29
+ | { kind: 'entry'; ts: number; seq: number; entry: LogEntry }
30
+ | { kind: 'fire'; ts: number; seq: number; fire: FireNode }
31
+
32
+ // Chronological interleave of own log entries and child fires within this
33
+ // fire's scope. Sort by ts first; break ties with `seq` (the executor's
34
+ // monotonic event counter) so sub-millisecond iterations preserve their
35
+ // logical order — without this, "run ok" sometimes appears between
36
+ // iterations because the entry was pushed into the merge first.
37
+ const items = computed<Item[]>(() => {
38
+ const merged: Item[] = []
39
+ for (const entry of ownEntries.value) {
40
+ merged.push({ kind: 'entry', ts: entry.ts, seq: entry.seq, entry })
41
+ }
42
+ for (const child of childFires.value) {
43
+ merged.push({ kind: 'fire', ts: child.ts, seq: child.seq, fire: child })
44
+ }
45
+ merged.sort((a, b) => a.ts - b.ts || a.seq - b.seq)
46
+ return merged
47
+ })
48
+
49
+ const headerLabel = computed(() => {
50
+ const f = props.fire
51
+ const port = f.port ? ` · ${f.port}` : ''
52
+ const iter =
53
+ typeof f.iteration === 'number' ? ` · iter ${f.iteration}` : ''
54
+ return `${f.sourceNodeId}${port}${iter}`
55
+ })
56
+
57
+ const durationLabel = computed(() => {
58
+ const f = props.fire
59
+ if (typeof f.closedAt !== 'number') return '…'
60
+ return `${f.closedAt - f.ts} ms`
61
+ })
62
+
63
+ // Track expanded entries by `seq` (executor-emitted, stable across
64
+ // reflows). Index-based keying flips state every time logs arrive in
65
+ // a different order.
66
+ const expandedEntrySeq = ref<Set<number>>(new Set())
67
+ function toggleExpanded(seq: number) {
68
+ if (expandedEntrySeq.value.has(seq)) expandedEntrySeq.value.delete(seq)
69
+ else expandedEntrySeq.value.add(seq)
70
+ expandedEntrySeq.value = new Set(expandedEntrySeq.value)
71
+ }
72
+
73
+ function iconFor(level: LogEntry['level']): { name: string; class: string } {
74
+ if (level === 'error') return { name: 'i-ph-x-circle', class: 'text-error' }
75
+ if (level === 'warn') return { name: 'i-ph-warning', class: 'text-warning' }
76
+ return { name: 'i-ph-info', class: 'text-muted' }
77
+ }
78
+
79
+ function formatTs(ms: number): string {
80
+ return `+${ms} ms`
81
+ }
82
+
83
+ function nodeLabel(entry: LogEntry): string {
84
+ if (!entry.nodeId) return 'run'
85
+ return entry.nodeKind ? `${entry.nodeKind} · ${entry.nodeId}` : entry.nodeId
86
+ }
87
+
88
+ function prettyValue(v: unknown): string {
89
+ try {
90
+ return JSON.stringify(v, null, 2)
91
+ } catch {
92
+ return String(v)
93
+ }
94
+ }
95
+
96
+ // Short inline summary shown next to the message for `log`-source entries
97
+ // (e.g. `Body alpha`, `Body 42`). Falls back to a compact JSON snippet
98
+ // for arrays/objects; truncates anything longer than 40 chars.
99
+ function inlineSummary(v: unknown): string {
100
+ if (v === undefined || v === null) return ''
101
+ if (typeof v === 'string') return v.length > 40 ? `${v.slice(0, 40)}…` : v
102
+ if (typeof v === 'number' || typeof v === 'boolean') return String(v)
103
+ try {
104
+ const j = JSON.stringify(v)
105
+ return j.length > 40 ? `${j.slice(0, 40)}…` : j
106
+ } catch {
107
+ return ''
108
+ }
109
+ }
110
+ </script>
111
+
112
+ <template>
113
+ <div class="flex flex-col">
114
+ <button
115
+ type="button"
116
+ class="grid grid-cols-[auto_auto_1fr_auto] items-center gap-2 px-2 py-1 text-left text-xs hover:bg-elevated border-b border-default"
117
+ @click="toggle"
118
+ >
119
+ <UIcon
120
+ :name="open ? 'i-ph-caret-down' : 'i-ph-caret-right'"
121
+ class="text-dimmed"
122
+ />
123
+ <span class="font-mono tabular-nums text-dimmed">{{ formatTs(fire.ts) }}</span>
124
+ <span class="font-mono text-default truncate">{{ headerLabel }}</span>
125
+ <span class="font-mono tabular-nums text-dimmed">{{ durationLabel }}</span>
126
+ </button>
127
+ <div v-if="open" class="border-l-2 border-default pl-2 ml-2">
128
+ <template v-for="item in items" :key="`${item.kind}-${item.seq}`">
129
+ <RunFireTree
130
+ v-if="item.kind === 'fire'"
131
+ :fire="item.fire"
132
+ :children-of="childrenOf"
133
+ :entries-of="entriesOf"
134
+ />
135
+ <div
136
+ v-else
137
+ class="border-b border-default last:border-b-0"
138
+ >
139
+ <button
140
+ type="button"
141
+ class="grid w-full grid-cols-[auto_auto_auto_1fr_auto] items-center gap-2 px-2 py-1 text-left text-xs hover:bg-elevated"
142
+ :disabled="item.entry.value === undefined"
143
+ @click="toggleExpanded(item.seq)"
144
+ >
145
+ <span class="font-mono tabular-nums text-dimmed">{{ formatTs(item.entry.ts) }}</span>
146
+ <UIcon :name="iconFor(item.entry.level).name" :class="iconFor(item.entry.level).class" />
147
+ <span class="font-mono text-muted truncate">{{ nodeLabel(item.entry) }}</span>
148
+ <span class="truncate text-default">
149
+ <span v-if="item.entry.source === 'exec'" class="mr-1 inline-block rounded bg-elevated px-1 py-px text-[10px] uppercase text-dimmed">exec</span>
150
+ {{ item.entry.message }}
151
+ <span
152
+ v-if="item.entry.source === 'log' && inlineSummary(item.entry.value)"
153
+ class="ml-2 font-mono text-dimmed"
154
+ >{{ inlineSummary(item.entry.value) }}</span>
155
+ </span>
156
+ <UIcon
157
+ v-if="item.entry.value !== undefined"
158
+ :name="expandedEntrySeq.has(item.seq) ? 'i-ph-caret-down' : 'i-ph-caret-right'"
159
+ class="text-dimmed"
160
+ />
161
+ </button>
162
+ <pre
163
+ v-if="item.entry.value !== undefined && expandedEntrySeq.has(item.seq)"
164
+ class="max-h-48 overflow-auto bg-elevated px-3 py-2 text-[11px]"
165
+ >{{ prettyValue(item.entry.value) }}</pre>
166
+ </div>
167
+ </template>
168
+ </div>
169
+ </div>
170
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, ref } from 'vue'
3
+ import type { RunDetail, RunSummary } from '../composables/useAutomationRuns'
4
+ import RunDetailDrawer from './RunDetailDrawer.vue'
5
+
6
+ // Drawer body for a single run, opened by the timeline display's Trace
7
+ // button via `useDrawer` (which passes `rowData` and `procedureName`). The
8
+ // summary row carries no logs, so we re-fetch the full run by id here.
9
+ // Attribute fallthrough is disabled so extra injected props don't leak
10
+ // onto the DOM.
11
+ defineOptions({ inheritAttrs: false })
12
+
13
+ const props = defineProps<{
14
+ rowData: RunSummary
15
+ procedureName?: string
16
+ }>()
17
+
18
+ const { getRun, listProcedures } = useAutomationRuns()
19
+
20
+ const run = ref<RunDetail | null>(null)
21
+ const resolvedName = ref<string | undefined>(props.procedureName)
22
+ const loading = ref(true)
23
+
24
+ onMounted(async () => {
25
+ const [full] = await Promise.all([
26
+ // Fall back to the summary row (no logs) if the detail fetch fails, so the
27
+ // drawer still shows status/timing/error.
28
+ getRun(props.rowData._id).catch(() => props.rowData as RunDetail),
29
+ props.procedureName
30
+ ? Promise.resolve()
31
+ : listProcedures()
32
+ .then((ps) => {
33
+ resolvedName.value = ps.find(
34
+ (p) => p._id === props.rowData.procedureId,
35
+ )?.name
36
+ })
37
+ .catch(() => {
38
+ /* name lookup is best-effort; falls back to procedureId */
39
+ }),
40
+ ])
41
+ run.value = full
42
+ loading.value = false
43
+ })
44
+ </script>
45
+
46
+ <template>
47
+ <div class="p-1">
48
+ <div v-if="loading" class="flex flex-col gap-3 p-1">
49
+ <USkeleton class="h-6 w-2/3" />
50
+ <USkeleton class="h-4 w-1/2" />
51
+ <USkeleton class="h-32 w-full" />
52
+ </div>
53
+ <RunDetailDrawer
54
+ v-else-if="run"
55
+ :run="run"
56
+ :procedure-name="resolvedName"
57
+ />
58
+ </div>
59
+ </template>
@@ -0,0 +1,126 @@
1
+ <script setup lang="ts">
2
+ import { computed, onMounted, ref } from 'vue'
3
+ import { formatDuration } from '../utils/automation'
4
+
5
+ // Page-level header + KPI cards for the Runs page, rendered ABOVE the TableView
6
+ // (registered as `dms-automation-runs-stats`, wired via `static stats` in
7
+ // src/pages/runs.ts). Self-contained: fetches its own stats snapshot. The run
8
+ // list, tabs, filters, export and pagination are owned by the TableView below.
9
+
10
+ const { $authFetch } = useAuthFetch()
11
+
12
+ interface StatsSnapshot {
13
+ health: { '24h': { avgDurationMs: number } }
14
+ runsPerDay7d: Array<{ day: string; ok: number; failed: number }>
15
+ }
16
+
17
+ const stats = ref<StatsSnapshot | null>(null)
18
+ const loading = ref(false)
19
+
20
+ const kpis = computed(() => {
21
+ const days = stats.value?.runsPerDay7d ?? []
22
+ const today = days[days.length - 1] ?? { ok: 0, failed: 0 }
23
+ return [
24
+ {
25
+ key: 'today',
26
+ title: '$dms_automation.runs.kpis.today',
27
+ value: today.ok + today.failed,
28
+ icon: 'i-ph-activity',
29
+ },
30
+ {
31
+ key: 'success',
32
+ title: '$dms_automation.runs.kpis.success',
33
+ value: today.ok,
34
+ icon: 'i-ph-check-circle',
35
+ },
36
+ {
37
+ key: 'failed',
38
+ title: '$dms_automation.runs.kpis.failed',
39
+ value: today.failed,
40
+ icon: 'i-ph-x-circle',
41
+ },
42
+ ]
43
+ })
44
+
45
+ const avgDurationLabel = computed(() =>
46
+ formatDuration(stats.value?.health?.['24h']?.avgDurationMs ?? 0),
47
+ )
48
+
49
+ async function loadStats() {
50
+ loading.value = true
51
+ try {
52
+ stats.value = await $authFetch<StatsSnapshot>('/api/automation/stats')
53
+ } catch {
54
+ /* KPIs are best-effort. */
55
+ } finally {
56
+ loading.value = false
57
+ }
58
+ }
59
+
60
+ onMounted(loadStats)
61
+ </script>
62
+
63
+ <template>
64
+ <!--
65
+ Title / icon / description come from the page's native dms header
66
+ (src/pages/runs.ts) — only the refresh action lives here.
67
+ -->
68
+ <div class="runs-stats flex flex-col gap-5">
69
+ <section class="flex flex-wrap items-center justify-end gap-4">
70
+ <UButton
71
+ color="neutral"
72
+ variant="ghost"
73
+ icon="i-ph-arrows-clockwise"
74
+ :loading="loading"
75
+ :aria-label="$t('dms_automation.common.refresh')"
76
+ @click="loadStats"
77
+ />
78
+ </section>
79
+
80
+ <!-- KPI stat cards -->
81
+ <div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
82
+ <template v-if="stats">
83
+ <DmsKpiCard
84
+ v-for="k in kpis"
85
+ :key="k.key"
86
+ variant="stat"
87
+ page-id="automation.runs"
88
+ :component-id="`runs-kpi-${k.key}`"
89
+ :title="k.title"
90
+ :icon="k.icon"
91
+ :static-value="k.value"
92
+ value-format="compact"
93
+ :show-delta="false"
94
+ />
95
+ <DmsCard :padded="false" class="flex flex-col justify-between gap-2 p-4">
96
+ <div class="flex items-center justify-between">
97
+ <span class="font-mono text-[11px] uppercase tracking-wider text-dimmed">
98
+ {{ $t('dms_automation.runs.kpis.avgDuration') }}
99
+ </span>
100
+ <UIcon name="i-ph-clock" class="size-4 text-dimmed" />
101
+ </div>
102
+ <span class="text-2xl font-semibold tabular-nums text-highlighted">
103
+ {{ avgDurationLabel }}
104
+ </span>
105
+ </DmsCard>
106
+ </template>
107
+ <template v-else>
108
+ <DmsCard v-for="i in 4" :key="`skel-${i}`" :padded="false" class="p-5">
109
+ <USkeleton class="h-10 w-full" />
110
+ </DmsCard>
111
+ </template>
112
+ </div>
113
+ </div>
114
+ </template>
115
+
116
+ <style>
117
+ /* The Runs TableView sits directly under these KPI cards. Drop its native card
118
+ frame (background/border/shadow) so the whole page reads as one flat section;
119
+ the card's inner padding is kept so the table content stays aligned with the
120
+ KPI grid above. Scoped to the runs page via the .runs-stats marker. */
121
+ .dms-page-stack:has(.runs-stats) .dms-card.flow-root {
122
+ background-color: transparent;
123
+ border-color: transparent;
124
+ box-shadow: none;
125
+ }
126
+ </style>