@agimon-ai/doompi-workflow 0.0.1-alpha.76 → 0.0.1-alpha.78

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 (332) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/api-contracts.cjs +7 -0
  3. package/dist/api-contracts.d.cts +2 -0
  4. package/dist/api-contracts.d.mts +2 -0
  5. package/dist/api-contracts.mjs +2 -0
  6. package/dist/extensions/pi.cjs +63 -0
  7. package/dist/extensions/pi.cjs.map +1 -0
  8. package/dist/extensions/pi.d.cts +6 -0
  9. package/dist/extensions/pi.d.cts.map +1 -0
  10. package/dist/extensions/pi.d.mts +6 -0
  11. package/dist/extensions/pi.d.mts.map +1 -0
  12. package/dist/extensions/pi.mjs +58 -0
  13. package/dist/extensions/pi.mjs.map +1 -0
  14. package/dist/extensions/server.cjs +96 -0
  15. package/dist/extensions/server.cjs.map +1 -0
  16. package/dist/extensions/server.d.cts +5 -0
  17. package/dist/extensions/server.d.cts.map +1 -0
  18. package/dist/extensions/server.d.mts +5 -0
  19. package/dist/extensions/server.d.mts.map +1 -0
  20. package/dist/extensions/server.mjs +91 -0
  21. package/dist/extensions/server.mjs.map +1 -0
  22. package/dist/extensions/web.mjs +3219 -0
  23. package/dist/extensions/web.mjs.map +1 -0
  24. package/dist/index.cjs +4 -0
  25. package/dist/index.d.cts +2 -0
  26. package/dist/index.d.mts +2 -0
  27. package/dist/index.mjs +2 -0
  28. package/dist/src/constants/workflow.cjs +32 -0
  29. package/dist/src/constants/workflow.cjs.map +1 -0
  30. package/dist/src/constants/workflow.mjs +21 -0
  31. package/dist/src/constants/workflow.mjs.map +1 -0
  32. package/dist/src/extensions/(backend)/_lib/index.server.cjs +12 -0
  33. package/dist/src/extensions/(backend)/_lib/index.server.cjs.map +1 -0
  34. package/dist/src/extensions/(backend)/_lib/index.server.mjs +12 -0
  35. package/dist/src/extensions/(backend)/_lib/index.server.mjs.map +1 -0
  36. package/dist/src/extensions/(backend)/api/workflow/route.server.cjs +7 -0
  37. package/dist/src/extensions/(backend)/api/workflow/route.server.cjs.map +1 -0
  38. package/dist/src/extensions/(backend)/api/workflow/route.server.mjs +8 -0
  39. package/dist/src/extensions/(backend)/api/workflow/route.server.mjs.map +1 -0
  40. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs +7 -0
  41. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs.map +1 -0
  42. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs +8 -0
  43. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs.map +1 -0
  44. package/dist/src/extensions/(backend)/channel/workflows.server.cjs +7 -0
  45. package/dist/src/extensions/(backend)/channel/workflows.server.cjs.map +1 -0
  46. package/dist/src/extensions/(backend)/channel/workflows.server.mjs +8 -0
  47. package/dist/src/extensions/(backend)/channel/workflows.server.mjs.map +1 -0
  48. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs +100 -0
  49. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs.map +1 -0
  50. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs +100 -0
  51. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs.map +1 -0
  52. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs +11 -0
  53. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs.map +1 -0
  54. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs +11 -0
  55. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs.map +1 -0
  56. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs +12 -0
  57. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs.map +1 -0
  58. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs +12 -0
  59. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs.map +1 -0
  60. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs +6 -0
  61. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs.map +1 -0
  62. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs +7 -0
  63. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs.map +1 -0
  64. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs +6 -0
  65. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs.map +1 -0
  66. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs +7 -0
  67. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs.map +1 -0
  68. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs +6 -0
  69. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs.map +1 -0
  70. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs +7 -0
  71. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs.map +1 -0
  72. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs +6 -0
  73. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs.map +1 -0
  74. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs +7 -0
  75. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs.map +1 -0
  76. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs +6 -0
  77. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs.map +1 -0
  78. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs +7 -0
  79. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs.map +1 -0
  80. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs +6 -0
  81. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs.map +1 -0
  82. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs +7 -0
  83. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs.map +1 -0
  84. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs +6 -0
  85. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs.map +1 -0
  86. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs +7 -0
  87. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs.map +1 -0
  88. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs +6 -0
  89. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs.map +1 -0
  90. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs +7 -0
  91. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs.map +1 -0
  92. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs +6 -0
  93. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs.map +1 -0
  94. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs +7 -0
  95. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs.map +1 -0
  96. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs +6 -0
  97. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs.map +1 -0
  98. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs +7 -0
  99. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs.map +1 -0
  100. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs +7 -0
  101. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs.map +1 -0
  102. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs +8 -0
  103. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs.map +1 -0
  104. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs +15 -0
  105. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs.map +1 -0
  106. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs +16 -0
  107. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs.map +1 -0
  108. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +17 -0
  109. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +1 -0
  110. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +18 -0
  111. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +1 -0
  112. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs +6 -0
  113. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs.map +1 -0
  114. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs +7 -0
  115. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs.map +1 -0
  116. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs +6 -0
  117. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs.map +1 -0
  118. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs +7 -0
  119. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs.map +1 -0
  120. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs +6 -0
  121. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs.map +1 -0
  122. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs +7 -0
  123. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs.map +1 -0
  124. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs +6 -0
  125. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs.map +1 -0
  126. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs +7 -0
  127. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs.map +1 -0
  128. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs +6 -0
  129. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs.map +1 -0
  130. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs +7 -0
  131. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs.map +1 -0
  132. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs +6 -0
  133. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs.map +1 -0
  134. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs +7 -0
  135. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs.map +1 -0
  136. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs +6 -0
  137. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs.map +1 -0
  138. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs +7 -0
  139. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs.map +1 -0
  140. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs +6 -0
  141. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs.map +1 -0
  142. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs +7 -0
  143. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs.map +1 -0
  144. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs +50 -0
  145. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs.map +1 -0
  146. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs +47 -0
  147. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs.map +1 -0
  148. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs +36 -0
  149. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs.map +1 -0
  150. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs +35 -0
  151. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs.map +1 -0
  152. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs +7 -0
  153. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs.map +1 -0
  154. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs +8 -0
  155. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs.map +1 -0
  156. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs +7 -0
  157. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs.map +1 -0
  158. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs +8 -0
  159. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs.map +1 -0
  160. package/dist/src/schemas/apiContracts.cjs +330 -0
  161. package/dist/src/schemas/apiContracts.cjs.map +1 -0
  162. package/dist/src/schemas/apiContracts.d.cts +53 -0
  163. package/dist/src/schemas/apiContracts.d.cts.map +1 -0
  164. package/dist/src/schemas/apiContracts.d.mts +53 -0
  165. package/dist/src/schemas/apiContracts.d.mts.map +1 -0
  166. package/dist/src/schemas/apiContracts.mjs +327 -0
  167. package/dist/src/schemas/apiContracts.mjs.map +1 -0
  168. package/dist/src/schemas/workflowPi.cjs +77 -0
  169. package/dist/src/schemas/workflowPi.cjs.map +1 -0
  170. package/dist/src/schemas/workflowPi.d.cts +1 -0
  171. package/dist/src/schemas/workflowPi.d.mts +1 -0
  172. package/dist/src/schemas/workflowPi.mjs +74 -0
  173. package/dist/src/schemas/workflowPi.mjs.map +1 -0
  174. package/dist/src/services/backgroundWork/index.cjs +61 -0
  175. package/dist/src/services/backgroundWork/index.cjs.map +1 -0
  176. package/dist/src/services/backgroundWork/index.mjs +61 -0
  177. package/dist/src/services/backgroundWork/index.mjs.map +1 -0
  178. package/dist/src/services/piToolBridge/index.cjs +205 -0
  179. package/dist/src/services/piToolBridge/index.cjs.map +1 -0
  180. package/dist/src/services/piToolBridge/index.mjs +195 -0
  181. package/dist/src/services/piToolBridge/index.mjs.map +1 -0
  182. package/dist/src/services/serverRuntime/index.cjs +387 -0
  183. package/dist/src/services/serverRuntime/index.cjs.map +1 -0
  184. package/dist/src/services/serverRuntime/index.mjs +387 -0
  185. package/dist/src/services/serverRuntime/index.mjs.map +1 -0
  186. package/dist/src/services/webWorkflowCatalog/index.cjs +53 -0
  187. package/dist/src/services/webWorkflowCatalog/index.cjs.map +1 -0
  188. package/dist/src/services/webWorkflowCatalog/index.mjs +52 -0
  189. package/dist/src/services/webWorkflowCatalog/index.mjs.map +1 -0
  190. package/dist/src/services/workflowCatalogChannel/index.cjs +74 -0
  191. package/dist/src/services/workflowCatalogChannel/index.cjs.map +1 -0
  192. package/dist/src/services/workflowCatalogChannel/index.mjs +74 -0
  193. package/dist/src/services/workflowCatalogChannel/index.mjs.map +1 -0
  194. package/dist/src/services/workflowCatalogDeps/index.cjs +25 -0
  195. package/dist/src/services/workflowCatalogDeps/index.cjs.map +1 -0
  196. package/dist/src/services/workflowCatalogDeps/index.mjs +25 -0
  197. package/dist/src/services/workflowCatalogDeps/index.mjs.map +1 -0
  198. package/dist/src/services/workflowExecution/index.cjs +205 -0
  199. package/dist/src/services/workflowExecution/index.cjs.map +1 -0
  200. package/dist/src/services/workflowExecution/index.d.cts +6 -0
  201. package/dist/src/services/workflowExecution/index.d.mts +6 -0
  202. package/dist/src/services/workflowExecution/index.mjs +200 -0
  203. package/dist/src/services/workflowExecution/index.mjs.map +1 -0
  204. package/dist/src/services/workflowFence/index.cjs +170 -0
  205. package/dist/src/services/workflowFence/index.cjs.map +1 -0
  206. package/dist/src/services/workflowFence/index.mjs +166 -0
  207. package/dist/src/services/workflowFence/index.mjs.map +1 -0
  208. package/dist/src/services/workflowHubApi/index.cjs +415 -0
  209. package/dist/src/services/workflowHubApi/index.cjs.map +1 -0
  210. package/dist/src/services/workflowHubApi/index.mjs +411 -0
  211. package/dist/src/services/workflowHubApi/index.mjs.map +1 -0
  212. package/dist/src/services/workflowLaunchCommand/index.cjs +121 -0
  213. package/dist/src/services/workflowLaunchCommand/index.cjs.map +1 -0
  214. package/dist/src/services/workflowLaunchCommand/index.d.cts +52 -0
  215. package/dist/src/services/workflowLaunchCommand/index.d.cts.map +1 -0
  216. package/dist/src/services/workflowLaunchCommand/index.d.mts +52 -0
  217. package/dist/src/services/workflowLaunchCommand/index.d.mts.map +1 -0
  218. package/dist/src/services/workflowLaunchCommand/index.mjs +118 -0
  219. package/dist/src/services/workflowLaunchCommand/index.mjs.map +1 -0
  220. package/dist/src/services/workflowLauncher/index.cjs +181 -0
  221. package/dist/src/services/workflowLauncher/index.cjs.map +1 -0
  222. package/dist/src/services/workflowLauncher/index.mjs +177 -0
  223. package/dist/src/services/workflowLauncher/index.mjs.map +1 -0
  224. package/dist/src/services/workflowNarration/index.cjs +19 -0
  225. package/dist/src/services/workflowNarration/index.cjs.map +1 -0
  226. package/dist/src/services/workflowNarration/index.mjs +19 -0
  227. package/dist/src/services/workflowNarration/index.mjs.map +1 -0
  228. package/dist/src/services/workflowResource/index.cjs +27 -0
  229. package/dist/src/services/workflowResource/index.cjs.map +1 -0
  230. package/dist/src/services/workflowResource/index.mjs +24 -0
  231. package/dist/src/services/workflowResource/index.mjs.map +1 -0
  232. package/dist/src/services/workflowRuns/index.cjs +333 -0
  233. package/dist/src/services/workflowRuns/index.cjs.map +1 -0
  234. package/dist/src/services/workflowRuns/index.mjs +319 -0
  235. package/dist/src/services/workflowRuns/index.mjs.map +1 -0
  236. package/dist/src/services/workflowTerminal/index.cjs +69 -0
  237. package/dist/src/services/workflowTerminal/index.cjs.map +1 -0
  238. package/dist/src/services/workflowTerminal/index.mjs +69 -0
  239. package/dist/src/services/workflowTerminal/index.mjs.map +1 -0
  240. package/dist/src/services/workflowTools/index.cjs +221 -0
  241. package/dist/src/services/workflowTools/index.cjs.map +1 -0
  242. package/dist/src/services/workflowTools/index.mjs +221 -0
  243. package/dist/src/services/workflowTools/index.mjs.map +1 -0
  244. package/dist/src/services/workflowWatcher/index.cjs +78 -0
  245. package/dist/src/services/workflowWatcher/index.cjs.map +1 -0
  246. package/dist/src/services/workflowWatcher/index.mjs +74 -0
  247. package/dist/src/services/workflowWatcher/index.mjs.map +1 -0
  248. package/dist/src/services/workflowsHubChannel/index.cjs +58 -0
  249. package/dist/src/services/workflowsHubChannel/index.cjs.map +1 -0
  250. package/dist/src/services/workflowsHubChannel/index.mjs +58 -0
  251. package/dist/src/services/workflowsHubChannel/index.mjs.map +1 -0
  252. package/dist/src/tui/leader.cjs +84 -0
  253. package/dist/src/tui/leader.cjs.map +1 -0
  254. package/dist/src/tui/leader.mjs +83 -0
  255. package/dist/src/tui/leader.mjs.map +1 -0
  256. package/dist/src/tui/workflow/doomOverlay.cjs +159 -0
  257. package/dist/src/tui/workflow/doomOverlay.cjs.map +1 -0
  258. package/dist/src/tui/workflow/doomOverlay.mjs +157 -0
  259. package/dist/src/tui/workflow/doomOverlay.mjs.map +1 -0
  260. package/dist/src/tui/workflow/overlayText.cjs +83 -0
  261. package/dist/src/tui/workflow/overlayText.cjs.map +1 -0
  262. package/dist/src/tui/workflow/overlayText.mjs +78 -0
  263. package/dist/src/tui/workflow/overlayText.mjs.map +1 -0
  264. package/dist/src/tui/workflow/rendering.cjs +22 -0
  265. package/dist/src/tui/workflow/rendering.cjs.map +1 -0
  266. package/dist/src/tui/workflow/rendering.mjs +20 -0
  267. package/dist/src/tui/workflow/rendering.mjs.map +1 -0
  268. package/dist/src/tui/workflow/workflowCatalog.cjs +336 -0
  269. package/dist/src/tui/workflow/workflowCatalog.cjs.map +1 -0
  270. package/dist/src/tui/workflow/workflowCatalog.mjs +333 -0
  271. package/dist/src/tui/workflow/workflowCatalog.mjs.map +1 -0
  272. package/dist/src/tui/workflow/workflowFinishedMessage.cjs +50 -0
  273. package/dist/src/tui/workflow/workflowFinishedMessage.cjs.map +1 -0
  274. package/dist/src/tui/workflow/workflowFinishedMessage.mjs +47 -0
  275. package/dist/src/tui/workflow/workflowFinishedMessage.mjs.map +1 -0
  276. package/dist/src/tui/workflow/workflowInspector.cjs +276 -0
  277. package/dist/src/tui/workflow/workflowInspector.cjs.map +1 -0
  278. package/dist/src/tui/workflow/workflowInspector.mjs +276 -0
  279. package/dist/src/tui/workflow/workflowInspector.mjs.map +1 -0
  280. package/dist/src/tui/workflow/workflowOverlay.cjs +126 -0
  281. package/dist/src/tui/workflow/workflowOverlay.cjs.map +1 -0
  282. package/dist/src/tui/workflow/workflowOverlay.mjs +125 -0
  283. package/dist/src/tui/workflow/workflowOverlay.mjs.map +1 -0
  284. package/dist/src/tui/workflow/workflowPicker.cjs +138 -0
  285. package/dist/src/tui/workflow/workflowPicker.cjs.map +1 -0
  286. package/dist/src/tui/workflow/workflowPicker.mjs +136 -0
  287. package/dist/src/tui/workflow/workflowPicker.mjs.map +1 -0
  288. package/dist/src/tui/workflow/workflowProgressOverlay.cjs +197 -0
  289. package/dist/src/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
  290. package/dist/src/tui/workflow/workflowProgressOverlay.mjs +192 -0
  291. package/dist/src/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
  292. package/dist/src/tui/workflow/workflowPrompt.cjs +142 -0
  293. package/dist/src/tui/workflow/workflowPrompt.cjs.map +1 -0
  294. package/dist/src/tui/workflow/workflowPrompt.mjs +139 -0
  295. package/dist/src/tui/workflow/workflowPrompt.mjs.map +1 -0
  296. package/dist/src/tui/workflow/workflowRunPanel.cjs +95 -0
  297. package/dist/src/tui/workflow/workflowRunPanel.cjs.map +1 -0
  298. package/dist/src/tui/workflow/workflowRunPanel.mjs +94 -0
  299. package/dist/src/tui/workflow/workflowRunPanel.mjs.map +1 -0
  300. package/dist/src/tui/workflow/workflowStatusRow.cjs +20 -0
  301. package/dist/src/tui/workflow/workflowStatusRow.cjs.map +1 -0
  302. package/dist/src/tui/workflow/workflowStatusRow.mjs +20 -0
  303. package/dist/src/tui/workflow/workflowStatusRow.mjs.map +1 -0
  304. package/dist/src/tui/workflow/workflowStepMessage.cjs +32 -0
  305. package/dist/src/tui/workflow/workflowStepMessage.cjs.map +1 -0
  306. package/dist/src/tui/workflow/workflowStepMessage.mjs +31 -0
  307. package/dist/src/tui/workflow/workflowStepMessage.mjs.map +1 -0
  308. package/dist/src/tui/workflow/workflowToolRender.cjs +491 -0
  309. package/dist/src/tui/workflow/workflowToolRender.cjs.map +1 -0
  310. package/dist/src/tui/workflow/workflowToolRender.mjs +490 -0
  311. package/dist/src/tui/workflow/workflowToolRender.mjs.map +1 -0
  312. package/dist/src/tui/workflowRuntime.cjs +1934 -0
  313. package/dist/src/tui/workflowRuntime.cjs.map +1 -0
  314. package/dist/src/tui/workflowRuntime.d.cts +33 -0
  315. package/dist/src/tui/workflowRuntime.d.cts.map +1 -0
  316. package/dist/src/tui/workflowRuntime.d.mts +33 -0
  317. package/dist/src/tui/workflowRuntime.d.mts.map +1 -0
  318. package/dist/src/tui/workflowRuntime.mjs +1930 -0
  319. package/dist/src/tui/workflowRuntime.mjs.map +1 -0
  320. package/dist/src/types/apiRoutes.cjs +82 -0
  321. package/dist/src/types/apiRoutes.cjs.map +1 -0
  322. package/dist/src/types/apiRoutes.mjs +80 -0
  323. package/dist/src/types/apiRoutes.mjs.map +1 -0
  324. package/dist/src/types/webWorkflowTerminal.cjs +23 -0
  325. package/dist/src/types/webWorkflowTerminal.cjs.map +1 -0
  326. package/dist/src/types/webWorkflowTerminal.mjs +22 -0
  327. package/dist/src/types/webWorkflowTerminal.mjs.map +1 -0
  328. package/dist/src/types/webWorkflows.cjs +8 -0
  329. package/dist/src/types/webWorkflows.cjs.map +1 -0
  330. package/dist/src/types/webWorkflows.mjs +7 -0
  331. package/dist/src/types/webWorkflows.mjs.map +1 -0
  332. package/package.json +8 -8
@@ -0,0 +1,411 @@
1
+ import { createWorkflowTerminalService } from "../workflowTerminal/index.mjs";
2
+ import apiRoutes_default from "../../types/apiRoutes.mjs";
3
+ import { WORKFLOW_API_BASE_PATH, WORKFLOW_SCREEN_EVENT } from "../../types/webWorkflowTerminal.mjs";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { WorkflowRegistryService, WorkflowTerminalService } from "@agimon-ai/workflow-mcp";
7
+ import { randomUUID } from "node:crypto";
8
+ import { Readable } from "node:stream";
9
+ import { Hono } from "hono";
10
+ import { streamSSE } from "hono/streaming";
11
+ //#region src/services/workflowHubApi/index.ts
12
+ /** Stages a run can be recorded under, newest first: a live run is the common case. */
13
+ const STAGES = [
14
+ "running",
15
+ "error",
16
+ "completed"
17
+ ];
18
+ /** Lines of screen the stream sends, which is a terminal's visible height plus room. */
19
+ const SCREEN_LINES = 48;
20
+ /** How often the stream re-reads; the service coalesces, so this is an upper bound. */
21
+ const STREAM_TICK_MS = 500;
22
+ /** How often a settled run is re-checked before the stream closes itself. */
23
+ const SETTLED_POLL_TICKS = 4;
24
+ /** Bytes of one textual artifact returned as JSON; binary previews use a stream. */
25
+ const MAX_ARTIFACT_BYTES = 524288;
26
+ /** Path segments a client supplies are names, never paths. */
27
+ const SAFE_SEGMENT = /^[\w.@-]+$/;
28
+ const ARTIFACT_MIME_TYPES = {
29
+ ".aac": "audio/aac",
30
+ ".bmp": "image/bmp",
31
+ ".css": "text/css",
32
+ ".csv": "text/csv",
33
+ ".gif": "image/gif",
34
+ ".htm": "text/html",
35
+ ".html": "text/html",
36
+ ".ico": "image/x-icon",
37
+ ".jpeg": "image/jpeg",
38
+ ".jpg": "image/jpeg",
39
+ ".js": "text/javascript",
40
+ ".json": "application/json",
41
+ ".jsx": "text/jsx",
42
+ ".m4a": "audio/mp4",
43
+ ".markdown": "text/markdown",
44
+ ".md": "text/markdown",
45
+ ".mov": "video/quicktime",
46
+ ".mp3": "audio/mpeg",
47
+ ".mp4": "video/mp4",
48
+ ".oga": "audio/ogg",
49
+ ".ogg": "audio/ogg",
50
+ ".ogv": "video/ogg",
51
+ ".pdf": "application/pdf",
52
+ ".png": "image/png",
53
+ ".sh": "text/x-shellscript",
54
+ ".svg": "image/svg+xml",
55
+ ".toml": "text/toml",
56
+ ".ts": "text/typescript",
57
+ ".tsv": "text/tab-separated-values",
58
+ ".tsx": "text/tsx",
59
+ ".txt": "text/plain",
60
+ ".wav": "audio/wav",
61
+ ".webm": "video/webm",
62
+ ".webp": "image/webp",
63
+ ".xml": "application/xml",
64
+ ".yaml": "text/yaml",
65
+ ".yml": "text/yaml"
66
+ };
67
+ /**
68
+ * One request's JSON body as a plain record.
69
+ *
70
+ * A body that is absent, malformed, or not an object reads as empty rather
71
+ * than failing the route: every field is checked by the route anyway, and a
72
+ * 400 naming the missing field beats a parse error naming nothing.
73
+ */
74
+ async function jsonBody(context) {
75
+ const parsed = await context.req.json().catch(() => void 0);
76
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
77
+ }
78
+ /** Rejects anything that could climb out of the registry before a path is built. */
79
+ function isSafeSegment(value) {
80
+ return value !== "" && value !== "../adapters" && value !== ".." && SAFE_SEGMENT.test(value);
81
+ }
82
+ /** One run's identity for caches and leases, stable across stage moves. */
83
+ function runIdentityOf(workspace, runKey) {
84
+ return `${workspace}/${runKey}`;
85
+ }
86
+ /**
87
+ * This package's HTTP surface: one workflow run's terminal, and the files its
88
+ * run directory holds.
89
+ *
90
+ * Mounted in the hub rather than in a session's server, because a run's
91
+ * terminal belongs to its multiplexer rather than to the session that launched
92
+ * it: any process can read a tmux pane, and the session's own server cannot
93
+ * reach anything more. The routes therefore name a run, never a session.
94
+ */
95
+ function createWorkflowHubApi(options = {}) {
96
+ const registry = options.registry ?? new WorkflowRegistryService();
97
+ const facade = options.terminal ?? new WorkflowTerminalService();
98
+ const terminal = createWorkflowTerminalService({
99
+ terminal: facade,
100
+ now: options.now ?? (() => Date.now())
101
+ });
102
+ const app = new Hono();
103
+ /** The record behind a request, whichever stage it now sits in. */
104
+ const runOf = async (workspace, runKey) => {
105
+ if (!isSafeSegment(workspace) || !isSafeSegment(runKey)) return void 0;
106
+ for (const stage of STAGES) try {
107
+ return await registry.readRunByKey(workspace, stage, runKey);
108
+ } catch {}
109
+ };
110
+ const missing = (workspace, runKey) => ({ error: `No workflow run '${runKey}' in workspace '${workspace}'.` });
111
+ app.get(apiRoutes_default.screen.path, async (context) => {
112
+ const workspace = context.req.param("workspace");
113
+ const runKey = context.req.param("runKey");
114
+ const record = await runOf(workspace, runKey);
115
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
116
+ const identity = runIdentityOf(workspace, runKey);
117
+ return streamSSE(context, async (stream) => {
118
+ let settledTicks = 0;
119
+ let running = true;
120
+ stream.onAbort(() => {
121
+ running = false;
122
+ });
123
+ while (running) {
124
+ const current = await runOf(workspace, runKey) ?? record;
125
+ const capabilities = terminal.capabilities(current);
126
+ const lines = capabilities.readable ? await terminal.screen(identity, current, SCREEN_LINES) : [];
127
+ const settled = current.stage !== "running";
128
+ if (settled) settledTicks += 1;
129
+ const ended = settled && settledTicks >= SETTLED_POLL_TICKS;
130
+ const event = {
131
+ lines,
132
+ capabilities,
133
+ ...ended ? { ended: true } : {}
134
+ };
135
+ await stream.writeSSE({
136
+ event: WORKFLOW_SCREEN_EVENT,
137
+ data: JSON.stringify(event)
138
+ });
139
+ if (ended) break;
140
+ await stream.sleep(STREAM_TICK_MS);
141
+ }
142
+ terminal.forget(/* @__PURE__ */ new Set());
143
+ });
144
+ });
145
+ app.post(apiRoutes_default.control.path, async (context) => {
146
+ const workspace = context.req.param("workspace");
147
+ const runKey = context.req.param("runKey");
148
+ const record = await runOf(workspace, runKey);
149
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
150
+ const identity = runIdentityOf(workspace, runKey);
151
+ const body = await jsonBody(context);
152
+ const capabilities = terminal.capabilities(record);
153
+ if (body.release === true) {
154
+ if (typeof body.token === "string") terminal.releaseControl(identity, body.token);
155
+ return context.json({ held: false });
156
+ }
157
+ if (!capabilities.writable) {
158
+ const refused = {
159
+ held: false,
160
+ reason: capabilities.reason
161
+ };
162
+ return context.json(refused, 409);
163
+ }
164
+ const token = typeof body.token === "string" && body.token !== "" ? body.token : randomUUID();
165
+ if (!terminal.takeControl(identity, token)) return context.json({
166
+ held: false,
167
+ reason: "Another reader holds the keyboard."
168
+ }, 409);
169
+ const held = {
170
+ held: true,
171
+ token
172
+ };
173
+ return context.json(held);
174
+ });
175
+ app.post(apiRoutes_default.keys.path, async (context) => {
176
+ const workspace = context.req.param("workspace");
177
+ const runKey = context.req.param("runKey");
178
+ const record = await runOf(workspace, runKey);
179
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
180
+ const body = await jsonBody(context);
181
+ if (typeof body.token !== "string" || typeof body.data !== "string") return context.json({ error: "A keystroke needs a control token and its data." }, 400);
182
+ try {
183
+ await terminal.write(runIdentityOf(workspace, runKey), record, body.token, body.data);
184
+ } catch (error) {
185
+ return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);
186
+ }
187
+ return context.body(null, 204);
188
+ });
189
+ app.post(apiRoutes_default.resize.path, async (context) => {
190
+ const workspace = context.req.param("workspace");
191
+ const runKey = context.req.param("runKey");
192
+ const record = await runOf(workspace, runKey);
193
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
194
+ const { token, columns, rows } = await jsonBody(context);
195
+ if (typeof token !== "string" || typeof columns !== "number" || typeof rows !== "number") return context.json({ error: "A resize needs a control token, columns and rows." }, 400);
196
+ try {
197
+ const resized = await terminal.resize(runIdentityOf(workspace, runKey), record, token, columns, rows);
198
+ return context.json({ resized });
199
+ } catch (error) {
200
+ return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);
201
+ }
202
+ });
203
+ app.delete(apiRoutes_default.remove.path, async (context) => {
204
+ const workspace = context.req.param("workspace");
205
+ const runKey = context.req.param("runKey");
206
+ const record = await runOf(workspace, runKey);
207
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
208
+ if (record.stage === "running") return context.json({ error: "A running workflow must be stopped before it can be deleted." }, 409);
209
+ try {
210
+ fs.rmSync(registry.runDirectoryFor(record), {
211
+ recursive: true,
212
+ force: true
213
+ });
214
+ } catch (error) {
215
+ return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);
216
+ }
217
+ return context.json({ deleted: true });
218
+ });
219
+ app.get(apiRoutes_default.artifacts.path, async (context) => {
220
+ const workspace = context.req.param("workspace");
221
+ const runKey = context.req.param("runKey");
222
+ const record = await runOf(workspace, runKey);
223
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
224
+ const runDir = registry.runDirectoryFor(record);
225
+ const body = {
226
+ runDir,
227
+ description: record.runDirectory?.description ?? "",
228
+ artifacts: readArtifacts(runDir, record)
229
+ };
230
+ return context.json(body);
231
+ });
232
+ app.get(apiRoutes_default.artifact.path, async (context) => {
233
+ const workspace = context.req.param("workspace");
234
+ const runKey = context.req.param("runKey");
235
+ const record = await runOf(workspace, runKey);
236
+ if (record === void 0) return context.json(missing(workspace, runKey), 404);
237
+ const runDir = registry.runDirectoryFor(record);
238
+ const requested = context.req.param("name");
239
+ const resolved = resolveInside(runDir, requested);
240
+ if (resolved === void 0) return context.json({ error: `'${requested}' is not inside this run.` }, 400);
241
+ if (context.req.query("raw") === "1") return streamArtifact(resolved, requested, context.req.header("range"), context.req.query("download") === "1") ?? context.json({ error: `'${requested}' has not been written.` }, 404);
242
+ const body = readArtifact(resolved, requested);
243
+ if (body === void 0) return context.json({ error: `'${requested}' has not been written.` }, 404);
244
+ return context.json(body);
245
+ });
246
+ return app;
247
+ }
248
+ /**
249
+ * A path inside the run directory, or undefined when it points anywhere else.
250
+ *
251
+ * The client supplies this one verbatim, unlike the run key, so it is resolved
252
+ * and then checked rather than trusted: without the check the route would read
253
+ * any file the hub can reach.
254
+ */
255
+ function resolveInside(runDir, requested) {
256
+ const resolved = path.resolve(runDir, requested);
257
+ const root = path.resolve(runDir);
258
+ if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) return void 0;
259
+ return resolved;
260
+ }
261
+ /** Size and modification time of one path, or undefined when it is not there. */
262
+ function statOf(target) {
263
+ try {
264
+ return fs.statSync(target);
265
+ } catch {
266
+ return;
267
+ }
268
+ }
269
+ function stateOf(stats) {
270
+ if (stats === void 0) return "pending";
271
+ if (stats.isDirectory()) return "written";
272
+ return stats.size === 0 ? "empty" : "written";
273
+ }
274
+ function viewOf(runDir, entry) {
275
+ const stats = statOf(path.join(runDir, entry.path));
276
+ return {
277
+ ...entry,
278
+ state: stateOf(stats),
279
+ ...stats === void 0 ? {} : {
280
+ size: stats.size,
281
+ modifiedAt: stats.mtime.toISOString()
282
+ }
283
+ };
284
+ }
285
+ /**
286
+ * What the run directory holds: the workflow's own declaration first, then the
287
+ * files nobody declared.
288
+ *
289
+ * The declaration is the point of the folder, so it leads and keeps its order
290
+ * even for entries no job has written yet. The rest follows rather than being
291
+ * hidden: the engine's own context.md and progress log are often exactly what a
292
+ * reader is looking for.
293
+ */
294
+ function readArtifacts(runDir, record) {
295
+ const declared = (record.runDirectory?.entries ?? []).map((entry) => viewOf(runDir, {
296
+ path: entry.path,
297
+ kind: entry.kind,
298
+ description: entry.description,
299
+ producedBy: entry["produced-by"] ?? [],
300
+ declared: true
301
+ }));
302
+ const claimed = new Set(declared.map((entry) => entry.path));
303
+ let names;
304
+ try {
305
+ names = fs.readdirSync(runDir, { withFileTypes: true });
306
+ } catch {
307
+ return declared;
308
+ }
309
+ const found = names.filter((entry) => !claimed.has(entry.name)).map((entry) => viewOf(runDir, {
310
+ path: entry.name,
311
+ kind: entry.isDirectory() ? "directory" : "file",
312
+ description: "",
313
+ producedBy: [],
314
+ declared: false
315
+ })).sort((left, right) => left.path.localeCompare(right.path));
316
+ return [...declared, ...found];
317
+ }
318
+ /** Browser content type inferred from a filename without trusting client input. */
319
+ function artifactMimeType(requested) {
320
+ return ARTIFACT_MIME_TYPES[path.extname(requested).toLowerCase()] ?? "application/octet-stream";
321
+ }
322
+ function isTextArtifact(mimeType) {
323
+ return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/xml";
324
+ }
325
+ /** One artifact's metadata and bounded text, or undefined when it is not readable. */
326
+ function readArtifact(resolved, requested) {
327
+ const stats = statOf(resolved);
328
+ if (stats === void 0 || stats.isDirectory()) return void 0;
329
+ const mimeType = artifactMimeType(requested);
330
+ let text;
331
+ if (isTextArtifact(mimeType)) try {
332
+ const handle = fs.openSync(resolved, "r");
333
+ try {
334
+ const buffer = Buffer.alloc(Math.min(stats.size, MAX_ARTIFACT_BYTES));
335
+ fs.readSync(handle, buffer, 0, buffer.length, 0);
336
+ text = buffer.toString("utf8");
337
+ } finally {
338
+ fs.closeSync(handle);
339
+ }
340
+ } catch {
341
+ return;
342
+ }
343
+ return {
344
+ path: requested,
345
+ size: stats.size,
346
+ modifiedAt: stats.mtime.toISOString(),
347
+ mimeType,
348
+ ...text === void 0 ? {} : { text },
349
+ truncated: text !== void 0 && stats.size > MAX_ARTIFACT_BYTES
350
+ };
351
+ }
352
+ /** Streams media with byte-range support so browser audio, video, and PDF controls can seek. */
353
+ function streamArtifact(resolved, requested, rangeHeader, download) {
354
+ const stats = statOf(resolved);
355
+ if (stats === void 0 || stats.isDirectory()) return void 0;
356
+ const mimeType = artifactMimeType(requested);
357
+ let start = 0;
358
+ let end = Math.max(0, stats.size - 1);
359
+ let partial = false;
360
+ const match = rangeHeader?.match(/^bytes=(\d*)-(\d*)$/);
361
+ if (match !== null && match !== void 0 && stats.size > 0) {
362
+ const requestedStart = match[1] === "" ? void 0 : Number(match[1]);
363
+ const requestedEnd = match[2] === "" ? void 0 : Number(match[2]);
364
+ if (requestedStart === void 0 && requestedEnd !== void 0) start = Math.max(0, stats.size - requestedEnd);
365
+ else if (requestedStart !== void 0) {
366
+ start = requestedStart;
367
+ end = requestedEnd === void 0 ? end : Math.min(requestedEnd, end);
368
+ }
369
+ if (start >= stats.size || start > end) return new Response(null, {
370
+ status: 416,
371
+ headers: { "content-range": `bytes */${String(stats.size)}` }
372
+ });
373
+ partial = true;
374
+ }
375
+ const filename = path.basename(requested).replace(/["\\\r\n]/g, "_");
376
+ const headers = new Headers({
377
+ "accept-ranges": "bytes",
378
+ "content-disposition": `${download || mimeType === "text/html" ? "attachment" : "inline"}; filename="${filename}"`,
379
+ "content-length": String(stats.size === 0 ? 0 : end - start + 1),
380
+ "content-type": mimeType,
381
+ "x-content-type-options": "nosniff"
382
+ });
383
+ if (partial) headers.set("content-range", `bytes ${String(start)}-${String(end)}/${String(stats.size)}`);
384
+ if (stats.size === 0) return new Response(null, {
385
+ status: 200,
386
+ headers
387
+ });
388
+ const body = Readable.toWeb(fs.createReadStream(resolved, {
389
+ start,
390
+ end
391
+ }));
392
+ return new Response(body, {
393
+ status: partial ? 206 : 200,
394
+ headers
395
+ });
396
+ }
397
+ /** The named export a host imports from this package's built hub entry. */
398
+ const api = {
399
+ basePath: WORKFLOW_API_BASE_PATH,
400
+ start(_context) {
401
+ const app = createWorkflowHubApi();
402
+ return {
403
+ fetch: (request) => app.fetch(request),
404
+ close: () => void 0
405
+ };
406
+ }
407
+ };
408
+ //#endregion
409
+ export { api, createWorkflowHubApi };
410
+
411
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["WorkflowTerminalFacade","routes"],"sources":["../../../../src/services/workflowHubApi/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport {\n WorkflowRegistryService,\n WorkflowTerminalService as WorkflowTerminalFacade,\n type WorkflowRunRecord,\n type WorkflowStage,\n} from '@agimon-ai/workflow-mcp';\nimport { type Context, Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { createWorkflowTerminalService } from '../../services/workflowTerminal';\nimport routes, { ARTIFACT_DOWNLOAD_PARAM, ARTIFACT_RAW_PARAM } from '../../types/apiRoutes';\nimport {\n WORKFLOW_API_BASE_PATH,\n WORKFLOW_SCREEN_EVENT,\n type WorkflowArtifactContentResponse,\n type WorkflowArtifactsResponse,\n type WorkflowArtifactView,\n type WorkflowControlResponse,\n type WorkflowDeleteResponse,\n type WorkflowScreenEvent,\n} from '../../types/webWorkflowTerminal';\n\n/** Stages a run can be recorded under, newest first: a live run is the common case. */\nconst STAGES: readonly WorkflowStage[] = ['running', 'error', 'completed'];\n/** Lines of screen the stream sends, which is a terminal's visible height plus room. */\nconst SCREEN_LINES = 48;\n/** How often the stream re-reads; the service coalesces, so this is an upper bound. */\nconst STREAM_TICK_MS = 500;\n/** How often a settled run is re-checked before the stream closes itself. */\nconst SETTLED_POLL_TICKS = 4;\n/** Bytes of one textual artifact returned as JSON; binary previews use a stream. */\nconst MAX_ARTIFACT_BYTES = 512 * 1024;\n/** Path segments a client supplies are names, never paths. */\nconst SAFE_SEGMENT = /^[\\w.@-]+$/;\nconst ARTIFACT_MIME_TYPES: Readonly<Record<string, string>> = {\n '.aac': 'audio/aac',\n '.bmp': 'image/bmp',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.gif': 'image/gif',\n '.htm': 'text/html',\n '.html': 'text/html',\n '.ico': 'image/x-icon',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.js': 'text/javascript',\n '.json': 'application/json',\n '.jsx': 'text/jsx',\n '.m4a': 'audio/mp4',\n '.markdown': 'text/markdown',\n '.md': 'text/markdown',\n '.mov': 'video/quicktime',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n '.oga': 'audio/ogg',\n '.ogg': 'audio/ogg',\n '.ogv': 'video/ogg',\n '.pdf': 'application/pdf',\n '.png': 'image/png',\n '.sh': 'text/x-shellscript',\n '.svg': 'image/svg+xml',\n '.toml': 'text/toml',\n '.ts': 'text/typescript',\n '.tsv': 'text/tab-separated-values',\n '.tsx': 'text/tsx',\n '.txt': 'text/plain',\n '.wav': 'audio/wav',\n '.webm': 'video/webm',\n '.webp': 'image/webp',\n '.xml': 'application/xml',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n};\n\n/**\n * One request's JSON body as a plain record.\n *\n * A body that is absent, malformed, or not an object reads as empty rather\n * than failing the route: every field is checked by the route anyway, and a\n * 400 naming the missing field beats a parse error naming nothing.\n */\nasync function jsonBody(context: Context): Promise<Record<string, unknown>> {\n const parsed: unknown = await context.req.json().catch(() => undefined);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n}\n\n/** Rejects anything that could climb out of the registry before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '../adapters' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** One run's identity for caches and leases, stable across stage moves. */\nfunction runIdentityOf(workspace: string, runKey: string): string {\n return `${workspace}/${runKey}`;\n}\n\nexport interface WorkflowHubApiOptions {\n registry?: WorkflowRegistryService;\n terminal?: WorkflowTerminalFacade;\n now?: () => number;\n}\n\n/**\n * This package's HTTP surface: one workflow run's terminal, and the files its\n * run directory holds.\n *\n * Mounted in the hub rather than in a session's server, because a run's\n * terminal belongs to its multiplexer rather than to the session that launched\n * it: any process can read a tmux pane, and the session's own server cannot\n * reach anything more. The routes therefore name a run, never a session.\n */\nexport function createWorkflowHubApi(options: WorkflowHubApiOptions = {}): Hono {\n const registry = options.registry ?? new WorkflowRegistryService();\n const facade = options.terminal ?? new WorkflowTerminalFacade();\n const terminal = createWorkflowTerminalService<WorkflowRunRecord>({\n terminal: facade,\n now: options.now ?? (() => Date.now()),\n });\n const app = new Hono();\n\n /** The record behind a request, whichever stage it now sits in. */\n const runOf = async (workspace: string, runKey: string): Promise<WorkflowRunRecord | undefined> => {\n if (!isSafeSegment(workspace) || !isSafeSegment(runKey)) return undefined;\n for (const stage of STAGES) {\n try {\n return await registry.readRunByKey(workspace, stage, runKey);\n } catch {\n // Not in this stage. A run moves between them as it settles, so a miss\n // here is ordinary; only a miss in every stage means no such run.\n }\n }\n return undefined;\n };\n\n const missing = (workspace: string, runKey: string): { error: string } => ({\n error: `No workflow run '${runKey}' in workspace '${workspace}'.`,\n });\n\n app.get(routes.screen.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n\n return streamSSE(context, async (stream) => {\n let settledTicks = 0;\n let running = true;\n stream.onAbort(() => {\n running = false;\n });\n while (running) {\n const current = (await runOf(workspace, runKey)) ?? record;\n const capabilities = terminal.capabilities(current);\n const lines = capabilities.readable ? await terminal.screen(identity, current, SCREEN_LINES) : [];\n // A settled run is read a few more times before the stream closes: the\n // last thing a failing step printed is what the reader came for, and it\n // lands after the record has already moved to its final stage.\n const settled = current.stage !== 'running';\n if (settled) settledTicks += 1;\n const ended = settled && settledTicks >= SETTLED_POLL_TICKS;\n const event: WorkflowScreenEvent = { lines, capabilities, ...(ended ? { ended: true } : {}) };\n await stream.writeSSE({ event: WORKFLOW_SCREEN_EVENT, data: JSON.stringify(event) });\n if (ended) break;\n await stream.sleep(STREAM_TICK_MS);\n }\n terminal.forget(new Set());\n });\n });\n\n app.post(routes.control.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const identity = runIdentityOf(workspace, runKey);\n const body = await jsonBody(context);\n const capabilities = terminal.capabilities(record);\n\n if (body.release === true) {\n if (typeof body.token === 'string') terminal.releaseControl(identity, body.token);\n const released: WorkflowControlResponse = { held: false };\n return context.json(released);\n }\n if (!capabilities.writable) {\n const refused: WorkflowControlResponse = { held: false, reason: capabilities.reason };\n return context.json(refused, 409);\n }\n const token = typeof body.token === 'string' && body.token !== '' ? body.token : randomUUID();\n if (!terminal.takeControl(identity, token)) {\n const taken: WorkflowControlResponse = { held: false, reason: 'Another reader holds the keyboard.' };\n return context.json(taken, 409);\n }\n const held: WorkflowControlResponse = { held: true, token };\n return context.json(held);\n });\n\n app.post(routes.keys.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n if (typeof body.token !== 'string' || typeof body.data !== 'string') {\n return context.json({ error: 'A keystroke needs a control token and its data.' }, 400);\n }\n try {\n await terminal.write(runIdentityOf(workspace, runKey), record, body.token, body.data);\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n return context.body(null, 204);\n });\n\n app.post(routes.resize.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const body = await jsonBody(context);\n const { token, columns, rows } = body;\n if (typeof token !== 'string' || typeof columns !== 'number' || typeof rows !== 'number') {\n return context.json({ error: 'A resize needs a control token, columns and rows.' }, 400);\n }\n try {\n const resized = await terminal.resize(runIdentityOf(workspace, runKey), record, token, columns, rows);\n return context.json({ resized });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n });\n\n app.delete(routes.remove.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n if (record.stage === 'running') {\n return context.json({ error: 'A running workflow must be stopped before it can be deleted.' }, 409);\n }\n try {\n fs.rmSync(registry.runDirectoryFor(record), { recursive: true, force: true });\n } catch (error) {\n return context.json({ error: error instanceof Error ? error.message : String(error) }, 409);\n }\n const response: WorkflowDeleteResponse = { deleted: true };\n return context.json(response);\n });\n\n app.get(routes.artifacts.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const body: WorkflowArtifactsResponse = {\n runDir,\n description: record.runDirectory?.description ?? '',\n artifacts: readArtifacts(runDir, record),\n };\n return context.json(body);\n });\n\n app.get(routes.artifact.path, async (context) => {\n const workspace = context.req.param('workspace');\n const runKey = context.req.param('runKey');\n const record = await runOf(workspace, runKey);\n if (record === undefined) return context.json(missing(workspace, runKey), 404);\n const runDir = registry.runDirectoryFor(record);\n const requested = context.req.param('name');\n const resolved = resolveInside(runDir, requested);\n if (resolved === undefined) return context.json({ error: `'${requested}' is not inside this run.` }, 400);\n if (context.req.query(ARTIFACT_RAW_PARAM) === '1') {\n const response = streamArtifact(\n resolved,\n requested,\n context.req.header('range'),\n context.req.query(ARTIFACT_DOWNLOAD_PARAM) === '1',\n );\n return response ?? context.json({ error: `'${requested}' has not been written.` }, 404);\n }\n const body = readArtifact(resolved, requested);\n if (body === undefined) return context.json({ error: `'${requested}' has not been written.` }, 404);\n return context.json(body);\n });\n\n return app;\n}\n\n/**\n * A path inside the run directory, or undefined when it points anywhere else.\n *\n * The client supplies this one verbatim, unlike the run key, so it is resolved\n * and then checked rather than trusted: without the check the route would read\n * any file the hub can reach.\n */\nfunction resolveInside(runDir: string, requested: string): string | undefined {\n const resolved = path.resolve(runDir, requested);\n const root = path.resolve(runDir);\n if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) return undefined;\n return resolved;\n}\n\n/** Size and modification time of one path, or undefined when it is not there. */\nfunction statOf(target: string): fs.Stats | undefined {\n try {\n return fs.statSync(target);\n } catch {\n return undefined; // Not written yet, which is an ordinary state for an artifact.\n }\n}\n\nfunction stateOf(stats: fs.Stats | undefined): WorkflowArtifactView['state'] {\n if (stats === undefined) return 'pending';\n if (stats.isDirectory()) return 'written';\n return stats.size === 0 ? 'empty' : 'written';\n}\n\nfunction viewOf(\n runDir: string,\n entry: Omit<WorkflowArtifactView, 'state' | 'size' | 'modifiedAt'>,\n): WorkflowArtifactView {\n const stats = statOf(path.join(runDir, entry.path));\n return {\n ...entry,\n state: stateOf(stats),\n ...(stats === undefined ? {} : { size: stats.size, modifiedAt: stats.mtime.toISOString() }),\n };\n}\n\n/**\n * What the run directory holds: the workflow's own declaration first, then the\n * files nobody declared.\n *\n * The declaration is the point of the folder, so it leads and keeps its order\n * even for entries no job has written yet. The rest follows rather than being\n * hidden: the engine's own context.md and progress log are often exactly what a\n * reader is looking for.\n */\nfunction readArtifacts(runDir: string, record: WorkflowRunRecord): WorkflowArtifactView[] {\n const declared = (record.runDirectory?.entries ?? []).map((entry) =>\n viewOf(runDir, {\n path: entry.path,\n kind: entry.kind,\n description: entry.description,\n producedBy: entry['produced-by'] ?? [],\n declared: true,\n }),\n );\n const claimed = new Set(declared.map((entry) => entry.path));\n let names: fs.Dirent[];\n try {\n names = fs.readdirSync(runDir, { withFileTypes: true });\n } catch {\n return declared; // The directory is gone; the declaration is still worth showing.\n }\n const found = names\n .filter((entry) => !claimed.has(entry.name))\n .map((entry) =>\n viewOf(runDir, {\n path: entry.name,\n kind: entry.isDirectory() ? 'directory' : 'file',\n description: '',\n producedBy: [],\n declared: false,\n }),\n )\n .sort((left, right) => left.path.localeCompare(right.path));\n return [...declared, ...found];\n}\n\n/** Browser content type inferred from a filename without trusting client input. */\nfunction artifactMimeType(requested: string): string {\n return ARTIFACT_MIME_TYPES[path.extname(requested).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction isTextArtifact(mimeType: string): boolean {\n return mimeType.startsWith('text/') || mimeType === 'application/json' || mimeType === 'application/xml';\n}\n\n/** One artifact's metadata and bounded text, or undefined when it is not readable. */\nfunction readArtifact(resolved: string, requested: string): WorkflowArtifactContentResponse | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let text: string | undefined;\n if (isTextArtifact(mimeType)) {\n try {\n const handle = fs.openSync(resolved, 'r');\n try {\n const buffer = Buffer.alloc(Math.min(stats.size, MAX_ARTIFACT_BYTES));\n fs.readSync(handle, buffer, 0, buffer.length, 0);\n text = buffer.toString('utf8');\n } finally {\n fs.closeSync(handle);\n }\n } catch {\n return undefined;\n }\n }\n return {\n path: requested,\n size: stats.size,\n modifiedAt: stats.mtime.toISOString(),\n mimeType,\n ...(text === undefined ? {} : { text }),\n truncated: text !== undefined && stats.size > MAX_ARTIFACT_BYTES,\n };\n}\n\n/** Streams media with byte-range support so browser audio, video, and PDF controls can seek. */\nfunction streamArtifact(\n resolved: string,\n requested: string,\n rangeHeader: string | undefined,\n download: boolean,\n): Response | undefined {\n const stats = statOf(resolved);\n if (stats === undefined || stats.isDirectory()) return undefined;\n const mimeType = artifactMimeType(requested);\n let start = 0;\n let end = Math.max(0, stats.size - 1);\n let partial = false;\n const match = rangeHeader?.match(/^bytes=(\\d*)-(\\d*)$/);\n if (match !== null && match !== undefined && stats.size > 0) {\n const requestedStart = match[1] === '' ? undefined : Number(match[1]);\n const requestedEnd = match[2] === '' ? undefined : Number(match[2]);\n if (requestedStart === undefined && requestedEnd !== undefined) {\n start = Math.max(0, stats.size - requestedEnd);\n } else if (requestedStart !== undefined) {\n start = requestedStart;\n end = requestedEnd === undefined ? end : Math.min(requestedEnd, end);\n }\n if (start >= stats.size || start > end) {\n return new Response(null, { status: 416, headers: { 'content-range': `bytes */${String(stats.size)}` } });\n }\n partial = true;\n }\n const filename = path.basename(requested).replace(/[\"\\\\\\r\\n]/g, '_');\n const forceDownload = download || mimeType === 'text/html';\n const headers = new Headers({\n 'accept-ranges': 'bytes',\n 'content-disposition': `${forceDownload ? 'attachment' : 'inline'}; filename=\"${filename}\"`,\n 'content-length': String(stats.size === 0 ? 0 : end - start + 1),\n 'content-type': mimeType,\n 'x-content-type-options': 'nosniff',\n });\n if (partial) headers.set('content-range', `bytes ${String(start)}-${String(end)}/${String(stats.size)}`);\n if (stats.size === 0) return new Response(null, { status: 200, headers });\n const body = Readable.toWeb(fs.createReadStream(resolved, { start, end }));\n return new Response(body, { status: partial ? 206 : 200, headers });\n}\n\n/** The named export a host imports from this package's built hub entry. */\nexport const api: DoomApi = {\n basePath: WORKFLOW_API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n // Hub-scoped: the host hands no session, and these routes want none. A run\n // is addressed by its registry identity, which is machine-wide.\n const app = createWorkflowHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a stream's loop ends when its own socket\n // aborts, and the caches it touched are per run rather than per client.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ //#region src/services/workflowLaunchCommand/index.ts
2
+ /**
3
+ * The `/workflow-launch` line, parsed.
4
+ *
5
+ * WHY A VERB AT ALL:
6
+ * A browser can only send a session a prompt frame, so every cockpit action
7
+ * that has to run inside the session travels as a slash line, the way the
8
+ * agent catalog sends `/run`. The workflow tools answer to the agent, not to a
9
+ * page, and the leader board answers to a keyboard, so neither of them can be
10
+ * what the launch dialog sends.
11
+ *
12
+ * THE SHAPE:
13
+ * /workflow-launch <workflow> [key=value ...] [prompt words]
14
+ *
15
+ * Key-value pairs come first and stop at the first token that is not one, so
16
+ * everything after them is the prompt exactly as it was typed, including any
17
+ * `=` inside it. `runner` is the one reserved key; the rest are the workflow's
18
+ * own `workflow_dispatch` inputs. Values may be double quoted to hold spaces.
19
+ */
20
+ /** The reserved key: which runner map entry the run executes with. */
21
+ const RUNNER_KEY = "runner";
22
+ const KEY_VALUE = /^([A-Za-z_][\w.-]*)=(.*)$/s;
23
+ function isLaunchParseFailure(value) {
24
+ return "error" in value;
25
+ }
26
+ /** Strips one layer of surrounding double quotes, which is how a value holds spaces. */
27
+ function unquote(value) {
28
+ return value.length > 1 && value.startsWith("\"") && value.endsWith("\"") ? value.slice(1, -1) : value;
29
+ }
30
+ /**
31
+ * Splits on whitespace, keeping double-quoted runs together.
32
+ *
33
+ * Returns each token with the offset it started at, so the caller can take the
34
+ * rest of the line verbatim rather than rebuilding it from tokens and losing
35
+ * the spacing a prompt was written with.
36
+ */
37
+ function tokenize(line) {
38
+ const tokens = [];
39
+ let index = 0;
40
+ while (index < line.length) {
41
+ while (index < line.length && /\s/.test(line[index])) index += 1;
42
+ if (index >= line.length) break;
43
+ const start = index;
44
+ let quoted = false;
45
+ while (index < line.length && (quoted || !/\s/.test(line[index]))) {
46
+ if (line[index] === "\"") quoted = !quoted;
47
+ index += 1;
48
+ }
49
+ tokens.push({
50
+ text: line.slice(start, index),
51
+ start
52
+ });
53
+ }
54
+ return tokens;
55
+ }
56
+ function parseWorkflowLaunchCommand(args) {
57
+ const tokens = tokenize(args.trim());
58
+ const first = tokens[0];
59
+ if (first === void 0) return { error: "Usage: /workflow-launch <workflow> [key=value …] [prompt]" };
60
+ const inputs = {};
61
+ let runner;
62
+ let index = 1;
63
+ for (; index < tokens.length; index += 1) {
64
+ const token = tokens[index];
65
+ const match = KEY_VALUE.exec(token.text);
66
+ if (!match) break;
67
+ const key = match[1];
68
+ const value = unquote(match[2]);
69
+ if (key === RUNNER_KEY) runner = value;
70
+ else inputs[key] = value;
71
+ }
72
+ const rest = tokens[index];
73
+ const prompt = rest === void 0 ? void 0 : args.trim().slice(rest.start).trim();
74
+ return {
75
+ workflow: unquote(first.text),
76
+ ...runner === void 0 ? {} : { runner },
77
+ inputs,
78
+ ...prompt === void 0 || prompt === "" ? {} : { prompt }
79
+ };
80
+ }
81
+ /**
82
+ * The workflow a token names: its name, its path in the repository, or the
83
+ * tail of that path. Case-insensitive on the name, because the catalog shows
84
+ * names as their author capitalised them and nobody types that back exactly.
85
+ */
86
+ function resolveWorkflowEntry(entries, token) {
87
+ const needle = token.trim();
88
+ const lower = needle.toLocaleLowerCase();
89
+ return entries.find((entry) => entry.name.toLocaleLowerCase() === lower) ?? entries.find((entry) => entry.relativePath === needle || entry.path === needle) ?? entries.find((entry) => entry.relativePath.endsWith(`/${needle}`) || entry.path.endsWith(`/${needle}`));
90
+ }
91
+ /** The trigger that makes a prompt mandatory: the run waits for one otherwise. */
92
+ const USER_PROMPT_TRIGGER = "user_prompt";
93
+ /**
94
+ * Everything wrong with a launch, before it is started.
95
+ *
96
+ * Checked here rather than left to the engine because the failure would
97
+ * otherwise be a run that starts and then waits forever for terminal input, or
98
+ * one that dies on a runner its steps never declared. Both read as the launch
99
+ * having worked.
100
+ */
101
+ function validateWorkflowLaunch(requirements, parsed) {
102
+ const problems = [];
103
+ if (requirements.triggers.includes(USER_PROMPT_TRIGGER) && (parsed.prompt ?? "") === "") problems.push("This workflow is triggered by a prompt, so it needs one.");
104
+ const missing = requirements.inputs.filter((input) => input.required === true && (parsed.inputs[input.name] ?? "") === "").map((input) => input.name);
105
+ if (missing.length > 0) problems.push(`Missing required input${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}.`);
106
+ for (const input of requirements.inputs) {
107
+ const value = parsed.inputs[input.name];
108
+ if (value === void 0 || input.options === void 0 || input.options.includes(value)) continue;
109
+ problems.push(`Input ${input.name} must be one of: ${input.options.join(", ")}.`);
110
+ }
111
+ const declared = requirements.runners;
112
+ if (parsed.runner !== void 0 && declared !== void 0 && !declared.includes(parsed.runner)) problems.push(declared.length === 0 ? "This workflow declares no runner its steps agree on." : `Runner ${parsed.runner} is not one this workflow declares: ${declared.join(", ")}.`);
113
+ return problems;
114
+ }
115
+ //#endregion
116
+ exports.isLaunchParseFailure = isLaunchParseFailure;
117
+ exports.parseWorkflowLaunchCommand = parseWorkflowLaunchCommand;
118
+ exports.resolveWorkflowEntry = resolveWorkflowEntry;
119
+ exports.validateWorkflowLaunch = validateWorkflowLaunch;
120
+
121
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/workflowLaunchCommand/index.ts"],"sourcesContent":["/**\n * The `/workflow-launch` line, parsed.\n *\n * WHY A VERB AT ALL:\n * A browser can only send a session a prompt frame, so every cockpit action\n * that has to run inside the session travels as a slash line, the way the\n * agent catalog sends `/run`. The workflow tools answer to the agent, not to a\n * page, and the leader board answers to a keyboard, so neither of them can be\n * what the launch dialog sends.\n *\n * THE SHAPE:\n * /workflow-launch <workflow> [key=value ...] [prompt words]\n *\n * Key-value pairs come first and stop at the first token that is not one, so\n * everything after them is the prompt exactly as it was typed, including any\n * `=` inside it. `runner` is the one reserved key; the rest are the workflow's\n * own `workflow_dispatch` inputs. Values may be double quoted to hold spaces.\n */\n\n/** The reserved key: which runner map entry the run executes with. */\nconst RUNNER_KEY = 'runner';\nconst KEY_VALUE = /^([A-Za-z_][\\w.-]*)=(.*)$/s;\n\nexport interface ParsedWorkflowLaunch {\n /** The workflow's name or its path, as typed; resolution belongs to the caller. */\n workflow: string;\n runner?: string;\n inputs: Record<string, string>;\n prompt?: string;\n}\n\nexport interface WorkflowLaunchParseFailure {\n error: string;\n}\n\nexport function isLaunchParseFailure(\n value: ParsedWorkflowLaunch | WorkflowLaunchParseFailure,\n): value is WorkflowLaunchParseFailure {\n return 'error' in value;\n}\n\n/** Strips one layer of surrounding double quotes, which is how a value holds spaces. */\nfunction unquote(value: string): string {\n return value.length > 1 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n}\n\n/**\n * Splits on whitespace, keeping double-quoted runs together.\n *\n * Returns each token with the offset it started at, so the caller can take the\n * rest of the line verbatim rather than rebuilding it from tokens and losing\n * the spacing a prompt was written with.\n */\nfunction tokenize(line: string): { text: string; start: number }[] {\n const tokens: { text: string; start: number }[] = [];\n let index = 0;\n while (index < line.length) {\n while (index < line.length && /\\s/.test(line[index] as string)) index += 1;\n if (index >= line.length) break;\n const start = index;\n let quoted = false;\n while (index < line.length && (quoted || !/\\s/.test(line[index] as string))) {\n if (line[index] === '\"') quoted = !quoted;\n index += 1;\n }\n tokens.push({ text: line.slice(start, index), start });\n }\n return tokens;\n}\n\nexport function parseWorkflowLaunchCommand(args: string): ParsedWorkflowLaunch | WorkflowLaunchParseFailure {\n const tokens = tokenize(args.trim());\n const first = tokens[0];\n if (first === undefined) return { error: 'Usage: /workflow-launch <workflow> [key=value …] [prompt]' };\n\n const inputs: Record<string, string> = {};\n let runner: string | undefined;\n let index = 1;\n for (; index < tokens.length; index += 1) {\n const token = tokens[index] as { text: string; start: number };\n const match = KEY_VALUE.exec(token.text);\n if (!match) break;\n const key = match[1] as string;\n const value = unquote(match[2] as string);\n if (key === RUNNER_KEY) runner = value;\n else inputs[key] = value;\n }\n\n const rest = tokens[index];\n const prompt = rest === undefined ? undefined : args.trim().slice(rest.start).trim();\n return {\n workflow: unquote(first.text),\n ...(runner === undefined ? {} : { runner }),\n inputs,\n ...(prompt === undefined || prompt === '' ? {} : { prompt }),\n };\n}\n\n/** The line the cockpit sends, built from what its dialog collected. */\nexport function workflowLaunchCommand(request: ParsedWorkflowLaunch): string {\n const quote = (value: string): string => (/\\s/.test(value) ? `\"${value}\"` : value);\n const pairs = [\n ...(request.runner === undefined ? [] : [`${RUNNER_KEY}=${quote(request.runner)}`]),\n ...Object.entries(request.inputs).map(([key, value]) => `${key}=${quote(value)}`),\n ];\n const prompt = request.prompt?.trim();\n return ['/workflow-launch', quote(request.workflow), ...pairs, ...(prompt ? [prompt] : [])].join(' ');\n}\n\n/** One catalog row, as much of it as resolution needs. */\nexport interface LaunchResolvableWorkflow {\n name: string;\n path: string;\n relativePath: string;\n}\n\n/**\n * The workflow a token names: its name, its path in the repository, or the\n * tail of that path. Case-insensitive on the name, because the catalog shows\n * names as their author capitalised them and nobody types that back exactly.\n */\nexport function resolveWorkflowEntry<T extends LaunchResolvableWorkflow>(\n entries: readonly T[],\n token: string,\n): T | undefined {\n const needle = token.trim();\n const lower = needle.toLocaleLowerCase();\n return (\n entries.find((entry) => entry.name.toLocaleLowerCase() === lower) ??\n entries.find((entry) => entry.relativePath === needle || entry.path === needle) ??\n entries.find((entry) => entry.relativePath.endsWith(`/${needle}`) || entry.path.endsWith(`/${needle}`))\n );\n}\n\n/** What a workflow declares, as much of it as a launch has to satisfy. */\nexport interface LaunchRequirements {\n triggers: readonly string[];\n inputs: readonly { name: string; required?: boolean; options?: readonly string[] }[];\n /** Absent means the workflow names no runner map, so any runner will do. */\n runners?: readonly string[];\n}\n\n/** The trigger that makes a prompt mandatory: the run waits for one otherwise. */\nconst USER_PROMPT_TRIGGER = 'user_prompt';\n\n/**\n * Everything wrong with a launch, before it is started.\n *\n * Checked here rather than left to the engine because the failure would\n * otherwise be a run that starts and then waits forever for terminal input, or\n * one that dies on a runner its steps never declared. Both read as the launch\n * having worked.\n */\nexport function validateWorkflowLaunch(\n requirements: LaunchRequirements,\n parsed: Pick<ParsedWorkflowLaunch, 'inputs' | 'prompt' | 'runner'>,\n): string[] {\n const problems: string[] = [];\n if (requirements.triggers.includes(USER_PROMPT_TRIGGER) && (parsed.prompt ?? '') === '') {\n problems.push('This workflow is triggered by a prompt, so it needs one.');\n }\n const missing = requirements.inputs\n .filter((input) => input.required === true && (parsed.inputs[input.name] ?? '') === '')\n .map((input) => input.name);\n if (missing.length > 0)\n problems.push(`Missing required input${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}.`);\n for (const input of requirements.inputs) {\n const value = parsed.inputs[input.name];\n if (value === undefined || input.options === undefined || input.options.includes(value)) continue;\n problems.push(`Input ${input.name} must be one of: ${input.options.join(', ')}.`);\n }\n const declared = requirements.runners;\n if (parsed.runner !== undefined && declared !== undefined && !declared.includes(parsed.runner)) {\n problems.push(\n declared.length === 0\n ? 'This workflow declares no runner its steps agree on.'\n : `Runner ${parsed.runner} is not one this workflow declares: ${declared.join(', ')}.`,\n );\n }\n return problems;\n}\n"],"mappings":"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"}