@xopcai/xopc 0.0.121 → 0.0.123

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 (309) hide show
  1. package/README.md +23 -3
  2. package/README.zh-CN.md +23 -3
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-4AW4JgSU.js → agent-avatar-dicebear-Bicr3ZBg.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-CxDSFHGx.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CE6GkGU2.js → app-management-settings-panel-CbEy9sR1.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-BmaUjghw.js → appearance-settings-CTHja6n9.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-CyZdgqvm.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-CX4_KWut.js → archive-plugin-Bpy64GjU.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-jf-PIIUU.js → attachment-load-DMkp4xSb.js} +1 -1
  12. package/dist/gateway/static/root/assets/automation-suggestion-card-KnYaP7NT.js +1 -0
  13. package/dist/gateway/static/root/assets/automations-page-8VLKM4au.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-DG9XQikd.js → binary-plugins-DA9C5mCw.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Up_VIxLJ.js → block-editor-CiSXk_YC.js} +6 -6
  16. package/dist/gateway/static/root/assets/browser-settings-page-5tPg0DcP.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-CGM4OyMw.js → capability-presets-api-CEJ44WCU.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CzWt0LBb.js → capability-presets-settings-panel-BJW3aVST.js} +1 -1
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-BEByL4_o.js → channel-recipient-api-CDhrhfhN.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-CorUCTrs.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-VKstC3sP.js +2 -0
  22. package/dist/gateway/static/root/assets/directory-picker-path-field-7jKfbH0J.js +1 -0
  23. package/dist/gateway/static/root/assets/dreaming-settings-m1-wmz4o.js +1 -0
  24. package/dist/gateway/static/root/assets/{extension-debug-page-DZxa_j7-.js → extension-debug-page-C9qQQrBS.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-page-CseziW53.js → extension-page-DcBBMEGl.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-settings-page-B_9J-xyo.js → extension-settings-page-BQ-jMMP9.js} +1 -1
  27. package/dist/gateway/static/root/assets/{fetch-Cs4ck8zA.js → fetch-Dtpbbe0s.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-config-api-Dl223a7r.js → gateway-config-api-BN6pyX2c.js} +1 -1
  29. package/dist/gateway/static/root/assets/gateway-settings-hQtgVmSR.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-startup-retry-IvePwjFE.js → gateway-startup-retry-BIET2x3y.js} +1 -1
  31. package/dist/gateway/static/root/assets/goal-create-dialog-BuIMDx6C.js +1 -0
  32. package/dist/gateway/static/root/assets/goal-detail-page-FEypCq74.js +1 -0
  33. package/dist/gateway/static/root/assets/{goals-config-api-CUCP6yCl.js → goals-config-api-CQm2r1Ty.js} +1 -1
  34. package/dist/gateway/static/root/assets/goals-page-C4-2_MLG.js +2 -0
  35. package/dist/gateway/static/root/assets/{goals-settings-Dj87qPxv.js → goals-settings-DDrdRMxB.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-BJVok3Sl.js +1 -0
  37. package/dist/gateway/static/root/assets/index-CBDr5xjB.css +1 -0
  38. package/dist/gateway/static/root/assets/index-T0SaNd5a.js +191 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DJgYw0nB.js → keyboard-shortcuts-settings-C2TuQppb.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-DwkwPgid.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-gnQ_EiGo.js → markdown-split-DhONQ81k.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-CQV-mgJn.js → media-plugins-DS44pJzr.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-avfvyZuQ.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-huNY4A1G.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-DdfGuEZ1.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-B0bT9Hqa.js +2 -0
  47. package/dist/gateway/static/root/assets/{preview-open-alternatives-BPMXC-2u.js → preview-open-alternatives-Dej6zHnr.js} +1 -1
  48. package/dist/gateway/static/root/assets/project-detail-page-CjxhPorG.js +1 -0
  49. package/dist/gateway/static/root/assets/projects-page-C4Z5OJMF.js +1 -0
  50. package/dist/gateway/static/root/assets/remote-access-hub-CVCOPUVd.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-BEHqwZjz.js → save-bar-controls-Zu1FzH6z.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schedule-field-CMY25m97.js → schedule-field-BW78mKc0.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-CxjxsFfg.js → schema-form-Cc-IOydE.js} +1 -1
  54. package/dist/gateway/static/root/assets/segmented-styles-BL8oLBEp.js +1 -0
  55. package/dist/gateway/static/root/assets/sessions-page-B4qExXHC.js +1 -0
  56. package/dist/gateway/static/root/assets/settings-advanced-gate-Ce8yLTBi.js +1 -0
  57. package/dist/gateway/static/root/assets/{settings-collapsible-section-s-kiLAxP.js → settings-collapsible-section-DIpIYht_.js} +1 -1
  58. package/dist/gateway/static/root/assets/{settings-form-section-CIxIEYxU.js → settings-form-section-BFxBNxOG.js} +1 -1
  59. package/dist/gateway/static/root/assets/settings-page-C4fivemT.js +1 -0
  60. package/dist/gateway/static/root/assets/settings-page-layout-CLx4o2PN.js +1 -0
  61. package/dist/gateway/static/root/assets/setup-status-panel-Kyc4eUf0.js +1 -0
  62. package/dist/gateway/static/root/assets/share-preview-page-ZswWCrDu.js +2 -0
  63. package/dist/gateway/static/root/assets/shares-settings-Bu76CMiC.js +1 -0
  64. package/dist/gateway/static/root/assets/skill-reload-api-D-CFg86j.js +1 -0
  65. package/dist/gateway/static/root/assets/skills-page-CUU4f6Ss.js +2 -0
  66. package/dist/gateway/static/root/assets/{system-settings-panel-Czjf0kLJ.js → system-settings-panel-3ZNcGRhS.js} +1 -1
  67. package/dist/gateway/static/root/assets/{typed-models-lib-Cmt8SYdI.js → typed-models-lib-bpzZjiVF.js} +1 -1
  68. package/dist/gateway/static/root/assets/{use-channel-catalog-DyM-ckEQ.js → use-channel-catalog-BCjZLZ3O.js} +1 -1
  69. package/dist/gateway/static/root/assets/{user-profile-settings-panel-BygLv8vm.js → user-profile-settings-panel-CIXafITi.js} +1 -1
  70. package/dist/gateway/static/root/assets/{utils-CloW8coB.js → utils-DXMtz5j_.js} +1 -1
  71. package/dist/gateway/static/root/assets/voice-api-key-field-C1MrQD8R.js +1 -0
  72. package/dist/gateway/static/root/assets/workflow-run-setup-panel-DqizIcY5.js +3 -0
  73. package/dist/gateway/static/root/assets/workflow-start-dialog-B4sZpTDQ.js +1 -0
  74. package/dist/gateway/static/root/assets/workflows-page-B3sVNPFk.js +18 -0
  75. package/dist/gateway/static/root/index.html +5 -5
  76. package/dist/package.js +1 -1
  77. package/dist/src/agent/agent-manager.d.ts +2 -0
  78. package/dist/src/agent/agent-manager.js +47 -7
  79. package/dist/src/agent/agent-manager.js.map +1 -1
  80. package/dist/src/agent/context/project-context.d.ts +19 -0
  81. package/dist/src/agent/context/project-context.js +90 -0
  82. package/dist/src/agent/context/project-context.js.map +1 -0
  83. package/dist/src/agent/goals/goal-locale.js +2 -2
  84. package/dist/src/agent/memory/types.d.ts +1 -0
  85. package/dist/src/agent/prompt/service-prompt-builder.d.ts +1 -0
  86. package/dist/src/agent/prompt/service-prompt-builder.js +2 -0
  87. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  88. package/dist/src/agent/prompt/system-prompt-params.d.ts +2 -0
  89. package/dist/src/agent/prompt/system-prompt-params.js +1 -0
  90. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -1
  91. package/dist/src/agent/prompt/system-prompt.d.ts +1 -0
  92. package/dist/src/agent/prompt/system-prompt.js +2 -1
  93. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  94. package/dist/src/agent/service.js +4 -3
  95. package/dist/src/agent/service.js.map +1 -1
  96. package/dist/src/agent/session/session-config-service.js +6 -1
  97. package/dist/src/agent/session/session-config-service.js.map +1 -1
  98. package/dist/src/agent/session/session-hydrator.js +7 -3
  99. package/dist/src/agent/session/session-hydrator.js.map +1 -1
  100. package/dist/src/agent/session/session-inspector.js +9 -5
  101. package/dist/src/agent/session/session-inspector.js.map +1 -1
  102. package/dist/src/agent/starter-agents.js +5 -34
  103. package/dist/src/agent/starter-agents.js.map +1 -1
  104. package/dist/src/agent/tools/factory.js +1 -1
  105. package/dist/src/agent/tools/factory.js.map +1 -1
  106. package/dist/src/agent/tools/goal-tool.d.ts +5 -1
  107. package/dist/src/agent/tools/goal-tool.js +8 -2
  108. package/dist/src/agent/tools/goal-tool.js.map +1 -1
  109. package/dist/src/agent/tools/index.d.ts +1 -1
  110. package/dist/src/agent/workflow/index.js +2 -2
  111. package/dist/src/agent/workflow/runtime.js +1 -1
  112. package/dist/src/automations/api/routes.js +13 -2
  113. package/dist/src/automations/api/routes.js.map +1 -1
  114. package/dist/src/automations/domain/types.d.ts +2 -0
  115. package/dist/src/automations/domain/validation.d.ts +3 -0
  116. package/dist/src/automations/domain/validation.js +1 -0
  117. package/dist/src/automations/domain/validation.js.map +1 -1
  118. package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -0
  119. package/dist/src/automations/service/action-executor.js +3 -1
  120. package/dist/src/automations/service/action-executor.js.map +1 -1
  121. package/dist/src/automations/service/automation-service.d.ts +4 -1
  122. package/dist/src/automations/service/automation-service.js +2 -2
  123. package/dist/src/automations/service/automation-service.js.map +1 -1
  124. package/dist/src/automations/storage/automation-repository.d.ts +3 -1
  125. package/dist/src/automations/storage/automation-repository.js +7 -5
  126. package/dist/src/automations/storage/automation-repository.js.map +1 -1
  127. package/dist/src/automations/storage/automation-run-repository.d.ts +1 -0
  128. package/dist/src/automations/storage/automation-run-repository.js +12 -1
  129. package/dist/src/automations/storage/automation-run-repository.js.map +1 -1
  130. package/dist/src/chat-commands/builtins/goal.js +4 -1
  131. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  132. package/dist/src/chat-commands/context.js +2 -1
  133. package/dist/src/chat-commands/context.js.map +1 -1
  134. package/dist/src/cli/command-loaders.js +1 -0
  135. package/dist/src/cli/command-loaders.js.map +1 -1
  136. package/dist/src/cli/command-manifest.js +4 -0
  137. package/dist/src/cli/command-manifest.js.map +1 -1
  138. package/dist/src/cli/commands/agent.js +2 -2
  139. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  140. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  141. package/dist/src/cli/commands/goal.js +27 -4
  142. package/dist/src/cli/commands/goal.js.map +1 -1
  143. package/dist/src/cli/commands/models.js +1 -1
  144. package/dist/src/cli/commands/onboard.js +1 -1
  145. package/dist/src/cli/commands/profile.js +1 -1
  146. package/dist/src/cli/commands/project.d.ts +4 -0
  147. package/dist/src/cli/commands/project.js +239 -0
  148. package/dist/src/cli/commands/project.js.map +1 -0
  149. package/dist/src/cli/commands/tunnel.js +1 -1
  150. package/dist/src/cli/commands/update.js +1 -1
  151. package/dist/src/cli/extension-cli-register.js +1 -1
  152. package/dist/src/config/index.js +1 -1
  153. package/dist/src/gateway/hono/routes/goals.js +51 -4
  154. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  155. package/dist/src/gateway/hono/routes/index.js +2 -0
  156. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  157. package/dist/src/gateway/hono/routes/memory.js +5 -1
  158. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  159. package/dist/src/gateway/hono/routes/notes.js +5 -0
  160. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  161. package/dist/src/gateway/hono/routes/projects.d.ts +3 -0
  162. package/dist/src/gateway/hono/routes/projects.js +662 -0
  163. package/dist/src/gateway/hono/routes/projects.js.map +1 -0
  164. package/dist/src/gateway/hono/routes/sessions.js +47 -7
  165. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  166. package/dist/src/gateway/hono/routes/workflows.js +27 -4
  167. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  168. package/dist/src/gateway/run-loop.js +1 -1
  169. package/dist/src/gateway/service.d.ts +3 -0
  170. package/dist/src/gateway/service.js +10 -3
  171. package/dist/src/gateway/service.js.map +1 -1
  172. package/dist/src/goals/goal-service.d.ts +1 -1
  173. package/dist/src/goals/goal-service.js +2 -1
  174. package/dist/src/goals/goal-service.js.map +1 -1
  175. package/dist/src/goals/goal-store.d.ts +1 -1
  176. package/dist/src/goals/goal-store.js +12 -6
  177. package/dist/src/goals/goal-store.js.map +1 -1
  178. package/dist/src/goals/goal-workflow-judge.js +1 -1
  179. package/dist/src/goals/types.d.ts +3 -0
  180. package/dist/src/i18n/goals-bundle.js +1 -1
  181. package/dist/src/i18n/index.js +1 -1
  182. package/dist/src/i18n/share-tool-bundle.js +2 -2
  183. package/dist/src/notes/agent-context.js +2 -2
  184. package/dist/src/projects/automation-bind.d.ts +1 -0
  185. package/dist/src/projects/automation-bind.js +16 -0
  186. package/dist/src/projects/automation-bind.js.map +1 -0
  187. package/dist/src/projects/goal-bind.d.ts +4 -0
  188. package/dist/src/projects/goal-bind.js +40 -0
  189. package/dist/src/projects/goal-bind.js.map +1 -0
  190. package/dist/src/projects/index.d.ts +12 -0
  191. package/dist/src/projects/index.js +12 -0
  192. package/dist/src/projects/memory-bind.d.ts +1 -0
  193. package/dist/src/projects/memory-bind.js +16 -0
  194. package/dist/src/projects/memory-bind.js.map +1 -0
  195. package/dist/src/projects/project-agent.d.ts +10 -0
  196. package/dist/src/projects/project-agent.js +27 -0
  197. package/dist/src/projects/project-agent.js.map +1 -0
  198. package/dist/src/projects/project-loop.d.ts +58 -0
  199. package/dist/src/projects/project-loop.js +118 -0
  200. package/dist/src/projects/project-loop.js.map +1 -0
  201. package/dist/src/projects/project-service.d.ts +40 -0
  202. package/dist/src/projects/project-service.js +187 -0
  203. package/dist/src/projects/project-service.js.map +1 -0
  204. package/dist/src/projects/project-store.d.ts +31 -0
  205. package/dist/src/projects/project-store.js +193 -0
  206. package/dist/src/projects/project-store.js.map +1 -0
  207. package/dist/src/projects/session-bind.d.ts +5 -0
  208. package/dist/src/projects/session-bind.js +46 -0
  209. package/dist/src/projects/session-bind.js.map +1 -0
  210. package/dist/src/projects/types.d.ts +62 -0
  211. package/dist/src/projects/types.js +1 -0
  212. package/dist/src/projects/workflow-bind.d.ts +1 -0
  213. package/dist/src/projects/workflow-bind.js +16 -0
  214. package/dist/src/projects/workflow-bind.js.map +1 -0
  215. package/dist/src/projects/workspace-project.d.ts +22 -0
  216. package/dist/src/projects/workspace-project.js +143 -0
  217. package/dist/src/projects/workspace-project.js.map +1 -0
  218. package/dist/src/projects/workspace.d.ts +5 -0
  219. package/dist/src/projects/workspace.js +25 -0
  220. package/dist/src/projects/workspace.js.map +1 -0
  221. package/dist/src/session/index.d.ts +1 -1
  222. package/dist/src/session/index.js +3 -3
  223. package/dist/src/session/session-workspace.d.ts +4 -2
  224. package/dist/src/session/session-workspace.js +10 -3
  225. package/dist/src/session/session-workspace.js.map +1 -1
  226. package/dist/src/session/types.d.ts +3 -0
  227. package/dist/src/session/types.js.map +1 -1
  228. package/dist/src/storage/sqlite/memory-records-repository.d.ts +3 -0
  229. package/dist/src/storage/sqlite/memory-records-repository.js +16 -5
  230. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  231. package/dist/src/storage/sqlite/migrations/019_projects.sql +40 -0
  232. package/dist/src/storage/sqlite/migrations/020_project_default_agent.sql +1 -0
  233. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  234. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  235. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  236. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  237. package/dist/src/storage/sqlite/notes-repository.js +20 -4
  238. package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
  239. package/dist/src/storage/sqlite/row-mappers.d.ts +2 -0
  240. package/dist/src/storage/sqlite/row-mappers.js +2 -0
  241. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  242. package/dist/src/storage/sqlite/schema.js +1 -1
  243. package/dist/src/storage/sqlite/session-repository.js +10 -5
  244. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  245. package/dist/src/storage/sqlite/transcript-repository.js +8 -0
  246. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
  247. package/dist/src/tui/backends/embedded-backend.d.ts +7 -1
  248. package/dist/src/tui/backends/embedded-backend.js +41 -2
  249. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  250. package/dist/src/tui/backends/gateway-sse-backend.d.ts +7 -1
  251. package/dist/src/tui/backends/gateway-sse-backend.js +22 -0
  252. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  253. package/dist/src/tui/tui-backend.d.ts +16 -0
  254. package/dist/src/tui/tui-commands.js +145 -0
  255. package/dist/src/tui/tui-commands.js.map +1 -1
  256. package/dist/src/tui/tui.js +21 -0
  257. package/dist/src/tui/tui.js.map +1 -1
  258. package/dist/src/workflows/domain/run.d.ts +1 -0
  259. package/dist/src/workflows/domain/run.js.map +1 -1
  260. package/dist/src/workflows/index.js +1 -1
  261. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  262. package/dist/src/workflows/service/workflow-run-service.js +18 -4
  263. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  264. package/dist/src/workflows/service/workflow-run-service.types.d.ts +2 -0
  265. package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
  266. package/dist/src/workflows/service/workflow-session-bridge.js +3 -0
  267. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  268. package/dist/src/workflows/store/run-index-store.d.ts +1 -0
  269. package/dist/src/workflows/store/run-index-store.js +13 -3
  270. package/dist/src/workflows/store/run-index-store.js.map +1 -1
  271. package/dist/src/workflows/store/run-store.d.ts +3 -1
  272. package/dist/src/workflows/store/run-store.js +5 -2
  273. package/dist/src/workflows/store/run-store.js.map +1 -1
  274. package/package.json +10 -1
  275. package/dist/gateway/static/root/assets/agents-2SVUUPlp.js +0 -2
  276. package/dist/gateway/static/root/assets/apps-page-BWeQ1I49.js +0 -1
  277. package/dist/gateway/static/root/assets/automation-suggestion-card-BHk5rtJz.js +0 -1
  278. package/dist/gateway/static/root/assets/automations-page-QHotl53D.js +0 -1
  279. package/dist/gateway/static/root/assets/browser-settings-page-Cmwi6IIQ.js +0 -8
  280. package/dist/gateway/static/root/assets/channels-settings-BOMCUe2J.js +0 -1
  281. package/dist/gateway/static/root/assets/connectors-page-C-Dexk_F.js +0 -2
  282. package/dist/gateway/static/root/assets/dreaming-settings-WPEj8pir.js +0 -1
  283. package/dist/gateway/static/root/assets/gateway-settings-DdzbtW9R.js +0 -1
  284. package/dist/gateway/static/root/assets/goal-detail-page-Dxldogms.js +0 -1
  285. package/dist/gateway/static/root/assets/goals-page-DU5Nd6Xp.js +0 -2
  286. package/dist/gateway/static/root/assets/heartbeat-settings-Cgm5JmwH.js +0 -1
  287. package/dist/gateway/static/root/assets/index-BU1pvxmS.js +0 -191
  288. package/dist/gateway/static/root/assets/index-l54WfSqe.css +0 -1
  289. package/dist/gateway/static/root/assets/logs-page-C10aS4XO.js +0 -2
  290. package/dist/gateway/static/root/assets/messages-C6Kc4Jhf.js +0 -3
  291. package/dist/gateway/static/root/assets/models-hub-panel-B35CAfg6.js +0 -2
  292. package/dist/gateway/static/root/assets/note-detail-page-Brlwse8x.js +0 -2
  293. package/dist/gateway/static/root/assets/note-image-lightbox-BthwKjLA.js +0 -1
  294. package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +0 -1
  295. package/dist/gateway/static/root/assets/notes-page-CoAwRc7L.js +0 -1
  296. package/dist/gateway/static/root/assets/remote-access-hub-B36RQ05h.js +0 -1
  297. package/dist/gateway/static/root/assets/sessions-page-73THChnj.js +0 -1
  298. package/dist/gateway/static/root/assets/settings-advanced-gate-CR9VsY-R.js +0 -1
  299. package/dist/gateway/static/root/assets/settings-page-BJ1AhfKI.js +0 -1
  300. package/dist/gateway/static/root/assets/settings-page-layout--6qT7ZJa.js +0 -1
  301. package/dist/gateway/static/root/assets/setup-status-panel-C1TtS1vJ.js +0 -1
  302. package/dist/gateway/static/root/assets/share-preview-page-BZADSbS1.js +0 -2
  303. package/dist/gateway/static/root/assets/shares-settings-gqC8Rmhy.js +0 -1
  304. package/dist/gateway/static/root/assets/skill-reload-api-C9P-WYvS.js +0 -1
  305. package/dist/gateway/static/root/assets/skills-page-CEpSvOhF.js +0 -2
  306. package/dist/gateway/static/root/assets/voice-api-key-field-BsoqJba6.js +0 -1
  307. package/dist/gateway/static/root/assets/voice-player-store-DduWlJYV.js +0 -1
  308. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CcQeX6l5.js +0 -3
  309. package/dist/gateway/static/root/assets/workflows-page-CeA8A9Ef.js +0 -18
@@ -14,15 +14,21 @@ function parseLimit(raw, fallback) {
14
14
  const parsed = raw ? Number.parseInt(raw, 10) : fallback;
15
15
  return Number.isFinite(parsed) ? parsed : fallback;
16
16
  }
17
+ function parseProjectId(raw) {
18
+ return raw?.trim() || void 0;
19
+ }
17
20
  function registerAutomationRoutes(authenticated, deps) {
18
21
  const { service } = deps;
19
22
  authenticated.get("/api/automations", async (c) => {
20
- const automations = await service.automationServiceInstance.list();
23
+ const projectId = parseProjectId(c.req.query("projectId"));
24
+ if (projectId && !service.projects.get(projectId)) return c.json({ error: "Project not found" }, 404);
25
+ const automations = await service.automationServiceInstance.list({ projectId });
21
26
  return c.json({ automations });
22
27
  });
23
28
  authenticated.post("/api/automations", async (c) => {
24
29
  try {
25
30
  const body = await c.req.json();
31
+ if (body.projectId && !service.projects.get(body.projectId)) return c.json({ error: "Project not found" }, 404);
26
32
  const automation = await service.automationServiceInstance.create(body);
27
33
  return c.json({ automation }, 201);
28
34
  } catch (err) {
@@ -63,8 +69,11 @@ function registerAutomationRoutes(authenticated, deps) {
63
69
  });
64
70
  authenticated.get("/api/automation-runs", async (c) => {
65
71
  const automationId = c.req.query("automationId")?.trim();
72
+ const projectId = parseProjectId(c.req.query("projectId"));
73
+ if (projectId && !service.projects.get(projectId)) return c.json({ error: "Project not found" }, 404);
66
74
  const runs = await service.automationServiceInstance.listRuns({
67
75
  automationId: automationId || void 0,
76
+ projectId: automationId ? void 0 : projectId,
68
77
  limit: parseLimit(c.req.query("limit"), 50)
69
78
  });
70
79
  return c.json({ runs });
@@ -149,7 +158,9 @@ function registerAutomationRoutes(authenticated, deps) {
149
158
  });
150
159
  authenticated.patch("/api/automations/:id", async (c) => {
151
160
  try {
152
- const automation = await service.automationServiceInstance.update(c.req.param("id"), await c.req.json());
161
+ const patch = await c.req.json();
162
+ if (patch.projectId && !service.projects.get(patch.projectId)) return c.json({ error: "Project not found" }, 404);
163
+ const automation = await service.automationServiceInstance.update(c.req.param("id"), patch);
153
164
  if (!automation) return c.json({ error: "Automation not found" }, 404);
154
165
  return c.json({ automation });
155
166
  } catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../../../src/automations/api/routes.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { resolveDefaultAgentId } from '../../agent/agent-scope.js';\nimport type { AuthenticatedRouteDeps } from '../../gateway/hono/routes/deps.js';\nimport { logRouteError } from '../../gateway/hono/lib/route-logger.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { AutomationDraftService, simulateAutomation } from '../draft/index.js';\nimport type { CreateAutomationInput } from '../domain/validation.js';\nimport { AutomationAlreadyRunningError } from '../service/automation-service.js';\n\nconst log = createLogger('Gateway:Automations');\n\nfunction parseLimit(raw: string | undefined, fallback: number): number {\n const parsed = raw ? Number.parseInt(raw, 10) : fallback;\n return Number.isFinite(parsed) ? parsed : fallback;\n}\n\nexport function registerAutomationRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/automations', async (c) => {\n const automations = await service.automationServiceInstance.list();\n return c.json({ automations });\n });\n\n authenticated.post('/api/automations', async (c) => {\n try {\n const body = await c.req.json();\n const automation = await service.automationServiceInstance.create(body);\n return c.json({ automation }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'create' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation' }, 400);\n }\n });\n\n authenticated.get('/api/automations/metrics', async (c) => {\n const metrics = await service.automationServiceInstance.getMetrics();\n return c.json(metrics);\n });\n\n authenticated.post('/api/automations/draft', async (c) => {\n const body = await c.req.json().catch(() => null) as {\n prompt?: unknown;\n agentId?: unknown;\n language?: unknown;\n } | null;\n const prompt = typeof body?.prompt === 'string' ? body.prompt.trim() : '';\n if (!prompt) return c.json({ error: 'prompt is required' }, 400);\n const agentId = typeof body?.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim()\n : resolveDefaultAgentId(service.currentConfig);\n const draftService = new AutomationDraftService({ config: service.currentConfig });\n try {\n const draft = await draftService.createDraft({\n prompt,\n agentId,\n language: body?.language === 'zh' ? 'zh' : 'en',\n }, c.req.raw.signal);\n return c.json({ draft }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'draft' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation draft' }, 400);\n }\n });\n\n authenticated.post('/api/automations/simulate', async (c) => {\n try {\n const body = await c.req.json() as CreateAutomationInput;\n return c.json({ simulation: simulateAutomation(body) });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'simulate' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to simulate automation' }, 400);\n }\n });\n\n authenticated.get('/api/automation-runs', async (c) => {\n const automationId = c.req.query('automationId')?.trim();\n const runs = await service.automationServiceInstance.listRuns({\n automationId: automationId || undefined,\n limit: parseLimit(c.req.query('limit'), 50),\n });\n return c.json({ runs });\n });\n\n authenticated.get('/api/automation-runs/product-events', async (c) => {\n const eventType = c.req.query('eventType')?.trim();\n if (!eventType) return c.json({ error: 'eventType is required' }, 400);\n const payloadKey = c.req.query('payloadKey')?.trim();\n const payloadValue = c.req.query('payloadValue')?.trim();\n const items = await service.automationServiceInstance.listRunsForProductEvent({\n eventType,\n source: c.req.query('source')?.trim() || undefined,\n payloadKey: payloadKey || undefined,\n payloadValue: payloadValue || undefined,\n limit: parseLimit(c.req.query('limit'), 10),\n });\n return c.json({ items });\n });\n\n authenticated.get('/api/automation-runs/:runId', async (c) => {\n const run = await service.automationServiceInstance.getRun(c.req.param('runId'));\n if (!run) return c.json({ error: 'Run not found' }, 404);\n return c.json({ run });\n });\n\n authenticated.get('/api/automation-runs/:runId/events', async (c) => {\n const runId = c.req.param('runId');\n const run = await service.automationServiceInstance.getRun(runId);\n if (!run) return c.json({ error: 'Run not found' }, 404);\n const events = await service.automationServiceInstance.listRunEvents(runId);\n return c.json({ events });\n });\n\n authenticated.post('/api/automation-runs/:runId/rerun', async (c) => {\n try {\n const run = await service.automationServiceInstance.rerunFromRun(c.req.param('runId'));\n return c.json({ run }, 201);\n } catch (err) {\n if (err instanceof AutomationAlreadyRunningError) {\n return c.json({\n error: err.message,\n code: 'automation_already_running',\n automationId: err.automationId,\n runningRunId: err.runningRunId,\n }, 409);\n }\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'rerun' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to rerun automation' }, 400);\n }\n });\n\n authenticated.post('/api/automation-runs/:runId/repair-draft', async (c) => {\n const runId = c.req.param('runId');\n const run = await service.automationServiceInstance.getRun(runId);\n if (!run) return c.json({ error: 'Run not found' }, 404);\n if (!['failed', 'timeout', 'cancelled'].includes(run.status)) {\n return c.json({ error: 'Repair draft is only available for failed, timed out, or cancelled runs' }, 400);\n }\n const automation = await service.automationServiceInstance.get(run.automationId);\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n const body = await c.req.json().catch(() => null) as { agentId?: unknown; language?: unknown } | null;\n const agentId = typeof body?.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim()\n : resolveDefaultAgentId(service.currentConfig);\n const events = await service.automationServiceInstance.listRunEvents(runId);\n const draftService = new AutomationDraftService({ config: service.currentConfig });\n try {\n const repair = await draftService.createRepairDraft({\n agentId,\n automation,\n run,\n events,\n language: body?.language === 'zh' ? 'zh' : 'en',\n }, c.req.raw.signal);\n return c.json({ repair }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'repairDraft' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation repair draft' }, 400);\n }\n });\n\n authenticated.post('/api/automation-runs/:runId/cancel', async (c) => {\n const cancelled = await service.automationServiceInstance.cancelRun(c.req.param('runId'));\n return c.json({ cancelled });\n });\n\n authenticated.get('/api/automations/:id', async (c) => {\n const automation = await service.automationServiceInstance.get(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n\n authenticated.patch('/api/automations/:id', async (c) => {\n try {\n const automation = await service.automationServiceInstance.update(c.req.param('id'), await c.req.json());\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'update' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to update automation' }, 400);\n }\n });\n\n authenticated.delete('/api/automations/:id', async (c) => {\n const removed = await service.automationServiceInstance.remove(c.req.param('id'));\n return c.json({ removed });\n });\n\n authenticated.post('/api/automations/:id/run', async (c) => {\n try {\n const run = await service.automationServiceInstance.runNow(c.req.param('id'));\n return c.json({ run });\n } catch (err) {\n if (err instanceof AutomationAlreadyRunningError) {\n return c.json({\n error: err.message,\n code: 'automation_already_running',\n automationId: err.automationId,\n runningRunId: err.runningRunId,\n }, 409);\n }\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'run' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to run automation' }, 400);\n }\n });\n\n authenticated.post('/api/automations/:id/pause', async (c) => {\n const automation = await service.automationServiceInstance.pause(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n\n authenticated.post('/api/automations/:id/resume', async (c) => {\n const automation = await service.automationServiceInstance.resume(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../../../src/automations/api/routes.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { resolveDefaultAgentId } from '../../agent/agent-scope.js';\nimport type { AuthenticatedRouteDeps } from '../../gateway/hono/routes/deps.js';\nimport { logRouteError } from '../../gateway/hono/lib/route-logger.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { AutomationDraftService, simulateAutomation } from '../draft/index.js';\nimport type { CreateAutomationInput } from '../domain/validation.js';\nimport { AutomationAlreadyRunningError } from '../service/automation-service.js';\n\nconst log = createLogger('Gateway:Automations');\n\nfunction parseLimit(raw: string | undefined, fallback: number): number {\n const parsed = raw ? Number.parseInt(raw, 10) : fallback;\n return Number.isFinite(parsed) ? parsed : fallback;\n}\n\nfunction parseProjectId(raw: string | undefined): string | undefined {\n const trimmed = raw?.trim();\n return trimmed || undefined;\n}\n\nexport function registerAutomationRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/automations', async (c) => {\n const projectId = parseProjectId(c.req.query('projectId'));\n if (projectId && !service.projects.get(projectId)) {\n return c.json({ error: 'Project not found' }, 404);\n }\n const automations = await service.automationServiceInstance.list({ projectId });\n return c.json({ automations });\n });\n\n authenticated.post('/api/automations', async (c) => {\n try {\n const body = await c.req.json() as CreateAutomationInput;\n if (body.projectId && !service.projects.get(body.projectId)) {\n return c.json({ error: 'Project not found' }, 404);\n }\n const automation = await service.automationServiceInstance.create(body);\n return c.json({ automation }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'create' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation' }, 400);\n }\n });\n\n authenticated.get('/api/automations/metrics', async (c) => {\n const metrics = await service.automationServiceInstance.getMetrics();\n return c.json(metrics);\n });\n\n authenticated.post('/api/automations/draft', async (c) => {\n const body = await c.req.json().catch(() => null) as {\n prompt?: unknown;\n agentId?: unknown;\n language?: unknown;\n } | null;\n const prompt = typeof body?.prompt === 'string' ? body.prompt.trim() : '';\n if (!prompt) return c.json({ error: 'prompt is required' }, 400);\n const agentId = typeof body?.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim()\n : resolveDefaultAgentId(service.currentConfig);\n const draftService = new AutomationDraftService({ config: service.currentConfig });\n try {\n const draft = await draftService.createDraft({\n prompt,\n agentId,\n language: body?.language === 'zh' ? 'zh' : 'en',\n }, c.req.raw.signal);\n return c.json({ draft }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'draft' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation draft' }, 400);\n }\n });\n\n authenticated.post('/api/automations/simulate', async (c) => {\n try {\n const body = await c.req.json() as CreateAutomationInput;\n return c.json({ simulation: simulateAutomation(body) });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'simulate' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to simulate automation' }, 400);\n }\n });\n\n authenticated.get('/api/automation-runs', async (c) => {\n const automationId = c.req.query('automationId')?.trim();\n const projectId = parseProjectId(c.req.query('projectId'));\n if (projectId && !service.projects.get(projectId)) {\n return c.json({ error: 'Project not found' }, 404);\n }\n const runs = await service.automationServiceInstance.listRuns({\n automationId: automationId || undefined,\n projectId: automationId ? undefined : projectId,\n limit: parseLimit(c.req.query('limit'), 50),\n });\n return c.json({ runs });\n });\n\n authenticated.get('/api/automation-runs/product-events', async (c) => {\n const eventType = c.req.query('eventType')?.trim();\n if (!eventType) return c.json({ error: 'eventType is required' }, 400);\n const payloadKey = c.req.query('payloadKey')?.trim();\n const payloadValue = c.req.query('payloadValue')?.trim();\n const items = await service.automationServiceInstance.listRunsForProductEvent({\n eventType,\n source: c.req.query('source')?.trim() || undefined,\n payloadKey: payloadKey || undefined,\n payloadValue: payloadValue || undefined,\n limit: parseLimit(c.req.query('limit'), 10),\n });\n return c.json({ items });\n });\n\n authenticated.get('/api/automation-runs/:runId', async (c) => {\n const run = await service.automationServiceInstance.getRun(c.req.param('runId'));\n if (!run) return c.json({ error: 'Run not found' }, 404);\n return c.json({ run });\n });\n\n authenticated.get('/api/automation-runs/:runId/events', async (c) => {\n const runId = c.req.param('runId');\n const run = await service.automationServiceInstance.getRun(runId);\n if (!run) return c.json({ error: 'Run not found' }, 404);\n const events = await service.automationServiceInstance.listRunEvents(runId);\n return c.json({ events });\n });\n\n authenticated.post('/api/automation-runs/:runId/rerun', async (c) => {\n try {\n const run = await service.automationServiceInstance.rerunFromRun(c.req.param('runId'));\n return c.json({ run }, 201);\n } catch (err) {\n if (err instanceof AutomationAlreadyRunningError) {\n return c.json({\n error: err.message,\n code: 'automation_already_running',\n automationId: err.automationId,\n runningRunId: err.runningRunId,\n }, 409);\n }\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'rerun' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to rerun automation' }, 400);\n }\n });\n\n authenticated.post('/api/automation-runs/:runId/repair-draft', async (c) => {\n const runId = c.req.param('runId');\n const run = await service.automationServiceInstance.getRun(runId);\n if (!run) return c.json({ error: 'Run not found' }, 404);\n if (!['failed', 'timeout', 'cancelled'].includes(run.status)) {\n return c.json({ error: 'Repair draft is only available for failed, timed out, or cancelled runs' }, 400);\n }\n const automation = await service.automationServiceInstance.get(run.automationId);\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n const body = await c.req.json().catch(() => null) as { agentId?: unknown; language?: unknown } | null;\n const agentId = typeof body?.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim()\n : resolveDefaultAgentId(service.currentConfig);\n const events = await service.automationServiceInstance.listRunEvents(runId);\n const draftService = new AutomationDraftService({ config: service.currentConfig });\n try {\n const repair = await draftService.createRepairDraft({\n agentId,\n automation,\n run,\n events,\n language: body?.language === 'zh' ? 'zh' : 'en',\n }, c.req.raw.signal);\n return c.json({ repair }, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'repairDraft' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to create automation repair draft' }, 400);\n }\n });\n\n authenticated.post('/api/automation-runs/:runId/cancel', async (c) => {\n const cancelled = await service.automationServiceInstance.cancelRun(c.req.param('runId'));\n return c.json({ cancelled });\n });\n\n authenticated.get('/api/automations/:id', async (c) => {\n const automation = await service.automationServiceInstance.get(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n\n authenticated.patch('/api/automations/:id', async (c) => {\n try {\n const patch = await c.req.json() as Partial<CreateAutomationInput> & { enabled?: boolean };\n if (patch.projectId && !service.projects.get(patch.projectId)) {\n return c.json({ error: 'Project not found' }, 404);\n }\n const automation = await service.automationServiceInstance.update(c.req.param('id'), patch);\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'update' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to update automation' }, 400);\n }\n });\n\n authenticated.delete('/api/automations/:id', async (c) => {\n const removed = await service.automationServiceInstance.remove(c.req.param('id'));\n return c.json({ removed });\n });\n\n authenticated.post('/api/automations/:id/run', async (c) => {\n try {\n const run = await service.automationServiceInstance.runNow(c.req.param('id'));\n return c.json({ run });\n } catch (err) {\n if (err instanceof AutomationAlreadyRunningError) {\n return c.json({\n error: err.message,\n code: 'automation_already_running',\n automationId: err.automationId,\n runningRunId: err.runningRunId,\n }, 409);\n }\n logRouteError(log, c, err, 'gateway.route.automations', { operation: 'run' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to run automation' }, 400);\n }\n });\n\n authenticated.post('/api/automations/:id/pause', async (c) => {\n const automation = await service.automationServiceInstance.pause(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n\n authenticated.post('/api/automations/:id/resume', async (c) => {\n const automation = await service.automationServiceInstance.resume(c.req.param('id'));\n if (!automation) return c.json({ error: 'Automation not found' }, 404);\n return c.json({ automation });\n });\n}\n"],"mappings":";;;;;;;;;kBAEmE;aAGd;AAKrD,MAAM,MAAM,aAAa,sBAAsB;AAE/C,SAAS,WAAW,KAAyB,UAA0B;CACrE,MAAM,SAAS,MAAM,OAAO,SAAS,KAAK,GAAG,GAAG;AAChD,QAAO,OAAO,SAAS,OAAO,GAAG,SAAS;;AAG5C,SAAS,eAAe,KAA6C;AAEnE,QADgB,KAAK,MAAM,IACT,KAAA;;AAGpB,SAAgB,yBAAyB,eAAqB,MAAoC;CAChG,MAAM,EAAE,YAAY;AAEpB,eAAc,IAAI,oBAAoB,OAAO,MAAM;EACjD,MAAM,YAAY,eAAe,EAAE,IAAI,MAAM,YAAY,CAAC;AAC1D,MAAI,aAAa,CAAC,QAAQ,SAAS,IAAI,UAAU,CAC/C,QAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;EAEpD,MAAM,cAAc,MAAM,QAAQ,0BAA0B,KAAK,EAAE,WAAW,CAAC;AAC/E,SAAO,EAAE,KAAK,EAAE,aAAa,CAAC;GAC9B;AAEF,eAAc,KAAK,oBAAoB,OAAO,MAAM;AAClD,MAAI;GACF,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAC/B,OAAI,KAAK,aAAa,CAAC,QAAQ,SAAS,IAAI,KAAK,UAAU,CACzD,QAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;GAEpD,MAAM,aAAa,MAAM,QAAQ,0BAA0B,OAAO,KAAK;AACvE,UAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI;WAC3B,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,UAAU,CAAC;AAChF,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,+BAA+B,EAAE,IAAI;;GAEnG;AAEF,eAAc,IAAI,4BAA4B,OAAO,MAAM;EACzD,MAAM,UAAU,MAAM,QAAQ,0BAA0B,YAAY;AACpE,SAAO,EAAE,KAAK,QAAQ;GACtB;AAEF,eAAc,KAAK,0BAA0B,OAAO,MAAM;EACxD,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,CAAC,YAAY,KAAK;EAKjD,MAAM,SAAS,OAAO,MAAM,WAAW,WAAW,KAAK,OAAO,MAAM,GAAG;AACvE,MAAI,CAAC,OAAQ,QAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,EAAE,IAAI;EAChE,MAAM,UAAU,OAAO,MAAM,YAAY,YAAY,KAAK,QAAQ,MAAM,GACpE,KAAK,QAAQ,MAAM,GACnB,sBAAsB,QAAQ,cAAc;EAChD,MAAM,eAAe,IAAI,uBAAuB,EAAE,QAAQ,QAAQ,eAAe,CAAC;AAClF,MAAI;GACF,MAAM,QAAQ,MAAM,aAAa,YAAY;IAC3C;IACA;IACA,UAAU,MAAM,aAAa,OAAO,OAAO;IAC5C,EAAE,EAAE,IAAI,IAAI,OAAO;AACpB,UAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI;WACtB,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,SAAS,CAAC;AAC/E,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,qCAAqC,EAAE,IAAI;;GAEzG;AAEF,eAAc,KAAK,6BAA6B,OAAO,MAAM;AAC3D,MAAI;GACF,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAC/B,UAAO,EAAE,KAAK,EAAE,YAAY,mBAAmB,KAAK,EAAE,CAAC;WAChD,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,YAAY,CAAC;AAClF,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,iCAAiC,EAAE,IAAI;;GAErG;AAEF,eAAc,IAAI,wBAAwB,OAAO,MAAM;EACrD,MAAM,eAAe,EAAE,IAAI,MAAM,eAAe,EAAE,MAAM;EACxD,MAAM,YAAY,eAAe,EAAE,IAAI,MAAM,YAAY,CAAC;AAC1D,MAAI,aAAa,CAAC,QAAQ,SAAS,IAAI,UAAU,CAC/C,QAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;EAEpD,MAAM,OAAO,MAAM,QAAQ,0BAA0B,SAAS;GAC5D,cAAc,gBAAgB,KAAA;GAC9B,WAAW,eAAe,KAAA,IAAY;GACtC,OAAO,WAAW,EAAE,IAAI,MAAM,QAAQ,EAAE,GAAG;GAC5C,CAAC;AACF,SAAO,EAAE,KAAK,EAAE,MAAM,CAAC;GACvB;AAEF,eAAc,IAAI,uCAAuC,OAAO,MAAM;EACpE,MAAM,YAAY,EAAE,IAAI,MAAM,YAAY,EAAE,MAAM;AAClD,MAAI,CAAC,UAAW,QAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,EAAE,IAAI;EACtE,MAAM,aAAa,EAAE,IAAI,MAAM,aAAa,EAAE,MAAM;EACpD,MAAM,eAAe,EAAE,IAAI,MAAM,eAAe,EAAE,MAAM;EACxD,MAAM,QAAQ,MAAM,QAAQ,0BAA0B,wBAAwB;GAC5E;GACA,QAAQ,EAAE,IAAI,MAAM,SAAS,EAAE,MAAM,IAAI,KAAA;GACzC,YAAY,cAAc,KAAA;GAC1B,cAAc,gBAAgB,KAAA;GAC9B,OAAO,WAAW,EAAE,IAAI,MAAM,QAAQ,EAAE,GAAG;GAC5C,CAAC;AACF,SAAO,EAAE,KAAK,EAAE,OAAO,CAAC;GACxB;AAEF,eAAc,IAAI,+BAA+B,OAAO,MAAM;EAC5D,MAAM,MAAM,MAAM,QAAQ,0BAA0B,OAAO,EAAE,IAAI,MAAM,QAAQ,CAAC;AAChF,MAAI,CAAC,IAAK,QAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACxD,SAAO,EAAE,KAAK,EAAE,KAAK,CAAC;GACtB;AAEF,eAAc,IAAI,sCAAsC,OAAO,MAAM;EACnE,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;AAElC,MAAI,CAAC,MADa,QAAQ,0BAA0B,OAAO,MAAM,CACvD,QAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,EAAE,IAAI;EACxD,MAAM,SAAS,MAAM,QAAQ,0BAA0B,cAAc,MAAM;AAC3E,SAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;GACzB;AAEF,eAAc,KAAK,qCAAqC,OAAO,MAAM;AACnE,MAAI;GACF,MAAM,MAAM,MAAM,QAAQ,0BAA0B,aAAa,EAAE,IAAI,MAAM,QAAQ,CAAC;AACtF,UAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI;WACpB,KAAK;AACZ,OAAI,eAAe,8BACjB,QAAO,EAAE,KAAK;IACZ,OAAO,IAAI;IACX,MAAM;IACN,cAAc,IAAI;IAClB,cAAc,IAAI;IACnB,EAAE,IAAI;AAET,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,SAAS,CAAC;AAC/E,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,8BAA8B,EAAE,IAAI;;GAElG;AAEF,eAAc,KAAK,4CAA4C,OAAO,MAAM;EAC1E,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAClC,MAAM,MAAM,MAAM,QAAQ,0BAA0B,OAAO,MAAM;AACjE,MAAI,CAAC,IAAK,QAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACxD,MAAI,CAAC;GAAC;GAAU;GAAW;GAAY,CAAC,SAAS,IAAI,OAAO,CAC1D,QAAO,EAAE,KAAK,EAAE,OAAO,2EAA2E,EAAE,IAAI;EAE1G,MAAM,aAAa,MAAM,QAAQ,0BAA0B,IAAI,IAAI,aAAa;AAChF,MAAI,CAAC,WAAY,QAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,EAAE,IAAI;EACtE,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,CAAC,YAAY,KAAK;EACjD,MAAM,UAAU,OAAO,MAAM,YAAY,YAAY,KAAK,QAAQ,MAAM,GACpE,KAAK,QAAQ,MAAM,GACnB,sBAAsB,QAAQ,cAAc;EAChD,MAAM,SAAS,MAAM,QAAQ,0BAA0B,cAAc,MAAM;EAC3E,MAAM,eAAe,IAAI,uBAAuB,EAAE,QAAQ,QAAQ,eAAe,CAAC;AAClF,MAAI;GACF,MAAM,SAAS,MAAM,aAAa,kBAAkB;IAClD;IACA;IACA;IACA;IACA,UAAU,MAAM,aAAa,OAAO,OAAO;IAC5C,EAAE,EAAE,IAAI,IAAI,OAAO;AACpB,UAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI;WACvB,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,eAAe,CAAC;AACrF,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,4CAA4C,EAAE,IAAI;;GAEhH;AAEF,eAAc,KAAK,sCAAsC,OAAO,MAAM;EACpE,MAAM,YAAY,MAAM,QAAQ,0BAA0B,UAAU,EAAE,IAAI,MAAM,QAAQ,CAAC;AACzF,SAAO,EAAE,KAAK,EAAE,WAAW,CAAC;GAC5B;AAEF,eAAc,IAAI,wBAAwB,OAAO,MAAM;EACrD,MAAM,aAAa,MAAM,QAAQ,0BAA0B,IAAI,EAAE,IAAI,MAAM,KAAK,CAAC;AACjF,MAAI,CAAC,WAAY,QAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,EAAE,IAAI;AACtE,SAAO,EAAE,KAAK,EAAE,YAAY,CAAC;GAC7B;AAEF,eAAc,MAAM,wBAAwB,OAAO,MAAM;AACvD,MAAI;GACF,MAAM,QAAQ,MAAM,EAAE,IAAI,MAAM;AAChC,OAAI,MAAM,aAAa,CAAC,QAAQ,SAAS,IAAI,MAAM,UAAU,CAC3D,QAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;GAEpD,MAAM,aAAa,MAAM,QAAQ,0BAA0B,OAAO,EAAE,IAAI,MAAM,KAAK,EAAE,MAAM;AAC3F,OAAI,CAAC,WAAY,QAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,EAAE,IAAI;AACtE,UAAO,EAAE,KAAK,EAAE,YAAY,CAAC;WACtB,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,UAAU,CAAC;AAChF,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,+BAA+B,EAAE,IAAI;;GAEnG;AAEF,eAAc,OAAO,wBAAwB,OAAO,MAAM;EACxD,MAAM,UAAU,MAAM,QAAQ,0BAA0B,OAAO,EAAE,IAAI,MAAM,KAAK,CAAC;AACjF,SAAO,EAAE,KAAK,EAAE,SAAS,CAAC;GAC1B;AAEF,eAAc,KAAK,4BAA4B,OAAO,MAAM;AAC1D,MAAI;GACF,MAAM,MAAM,MAAM,QAAQ,0BAA0B,OAAO,EAAE,IAAI,MAAM,KAAK,CAAC;AAC7E,UAAO,EAAE,KAAK,EAAE,KAAK,CAAC;WACf,KAAK;AACZ,OAAI,eAAe,8BACjB,QAAO,EAAE,KAAK;IACZ,OAAO,IAAI;IACX,MAAM;IACN,cAAc,IAAI;IAClB,cAAc,IAAI;IACnB,EAAE,IAAI;AAET,iBAAc,KAAK,GAAG,KAAK,6BAA6B,EAAE,WAAW,OAAO,CAAC;AAC7E,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,4BAA4B,EAAE,IAAI;;GAEhG;AAEF,eAAc,KAAK,8BAA8B,OAAO,MAAM;EAC5D,MAAM,aAAa,MAAM,QAAQ,0BAA0B,MAAM,EAAE,IAAI,MAAM,KAAK,CAAC;AACnF,MAAI,CAAC,WAAY,QAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,EAAE,IAAI;AACtE,SAAO,EAAE,KAAK,EAAE,YAAY,CAAC;GAC7B;AAEF,eAAc,KAAK,+BAA+B,OAAO,MAAM;EAC7D,MAAM,aAAa,MAAM,QAAQ,0BAA0B,OAAO,EAAE,IAAI,MAAM,KAAK,CAAC;AACpF,MAAI,CAAC,WAAY,QAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,EAAE,IAAI;AACtE,SAAO,EAAE,KAAK,EAAE,YAAY,CAAC;GAC7B"}
@@ -74,6 +74,7 @@ export interface Automation {
74
74
  id: string;
75
75
  name: string;
76
76
  description?: string;
77
+ projectId?: string;
77
78
  enabled: boolean;
78
79
  trigger: AutomationTrigger;
79
80
  action: AutomationAction;
@@ -146,6 +147,7 @@ export interface AutomationDeps {
146
147
  getModelForSession?: (sessionKey: string) => string | undefined;
147
148
  };
148
149
  getDefaultAgentId?: () => string;
150
+ getProjectWorkspaceRoot?: (projectId: string) => string | undefined;
149
151
  workflowRunService?: {
150
152
  startWorkflowRun(params: StartWorkflowRunServiceParams): Promise<WorkflowRunServiceResult>;
151
153
  };
@@ -99,6 +99,7 @@ export declare const AutomationSchema: z.ZodObject<{
99
99
  id: z.ZodString;
100
100
  name: z.ZodString;
101
101
  description: z.ZodOptional<z.ZodString>;
102
+ projectId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
102
103
  enabled: z.ZodBoolean;
103
104
  trigger: z.ZodDiscriminatedUnion<[z.ZodObject<{
104
105
  kind: z.ZodLiteral<"manual">;
@@ -190,6 +191,7 @@ export declare const AutomationSchema: z.ZodObject<{
190
191
  export declare const CreateAutomationSchema: z.ZodObject<{
191
192
  description: z.ZodOptional<z.ZodString>;
192
193
  name: z.ZodString;
194
+ projectId: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
193
195
  trigger: z.ZodDiscriminatedUnion<[z.ZodObject<{
194
196
  kind: z.ZodLiteral<"manual">;
195
197
  }, z.core.$strict>, z.ZodObject<{
@@ -281,6 +283,7 @@ export declare const UpdateAutomationSchema: z.ZodObject<{
281
283
  description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
282
284
  enabled: z.ZodOptional<z.ZodBoolean>;
283
285
  name: z.ZodOptional<z.ZodString>;
286
+ projectId: z.ZodOptional<z.ZodPreprocess<z.ZodOptional<z.ZodString>>>;
284
287
  trigger: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
285
288
  kind: z.ZodLiteral<"manual">;
286
289
  }, z.core.$strict>, z.ZodObject<{
@@ -117,6 +117,7 @@ const AutomationSchema = z.object({
117
117
  id: nonEmptyString.max(100),
118
118
  name: nonEmptyString.max(200),
119
119
  description: z.string().max(2e3).optional(),
120
+ projectId: optionalTrimmedString(100),
120
121
  enabled: z.boolean(),
121
122
  trigger: AutomationTriggerSchema,
122
123
  action: AutomationActionSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","names":[],"sources":["../../../../src/automations/domain/validation.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { z } from 'zod';\n\nconst nonEmptyString = z.string().trim().min(1);\n\nconst optionalTrimmedString = (max: number) =>\n z.preprocess((value) => {\n if (value == null) return undefined;\n if (typeof value !== 'string') return value;\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n }, z.string().min(1).max(max).optional());\n\nexport const AutomationScheduleSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('once'),\n at: nonEmptyString,\n }).strict(),\n z.object({\n kind: z.literal('interval'),\n everyMs: z.number().int().min(1),\n anchorMs: z.number().int().nonnegative().optional(),\n }).strict(),\n z.object({\n kind: z.literal('cron'),\n expr: nonEmptyString.superRefine((expr, ctx) => {\n try {\n CronExpressionParser.parse(expr);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${expr}`,\n });\n }\n }),\n tz: optionalTrimmedString(100),\n }).strict(),\n]);\n\nexport const AutomationTriggerSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('manual') }).strict(),\n z.object({\n kind: z.literal('schedule'),\n schedule: AutomationScheduleSchema,\n }).strict(),\n z.object({\n kind: z.literal('webhook'),\n secretId: optionalTrimmedString(200),\n }).strict(),\n z.object({\n kind: z.literal('event'),\n eventType: nonEmptyString.max(200),\n source: optionalTrimmedString(200),\n payloadMatch: z.record(\n z.string(),\n z.union([z.string(), z.number(), z.boolean(), z.null()]),\n ).optional(),\n }).strict(),\n]);\n\nconst WorkflowRunInputEnvelopeSchema = z.object({\n payload: z.unknown(),\n goal: optionalTrimmedString(5000),\n variables: z.record(z.string(), z.unknown()).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n}).strict();\n\nexport const AutomationActionSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('agent'),\n agentId: optionalTrimmedString(64),\n instruction: nonEmptyString.max(50000),\n workingDirectory: optionalTrimmedString(4096),\n model: optionalTrimmedString(100),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n z.object({\n kind: z.literal('workflow'),\n workflowId: nonEmptyString.max(200),\n agentId: optionalTrimmedString(64),\n input: z.unknown().optional(),\n inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),\n goal: optionalTrimmedString(5000),\n concurrency: z.number().int().min(1).max(50).optional(),\n maxSubagents: z.number().int().min(1).max(100).optional(),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n]);\n\nexport const AutomationAfterRunSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('none') }).strict(),\n z.object({ kind: z.literal('saveToSession') }).strict(),\n z.object({\n kind: z.literal('webhook'),\n url: nonEmptyString.max(2000),\n }).strict(),\n]);\n\nexport const AutomationReliabilitySchema = z.object({\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n retryCount: z.number().int().min(0).max(10).optional(),\n maxConcurrentRuns: z.number().int().min(1).max(20).optional(),\n disableAfterConsecutiveFailures: z.number().int().min(1).max(100).optional(),\n}).strict();\n\nexport const AutomationSafetyPolicySchema = z.object({\n mode: z.enum(['suggest_only', 'ask_before_apply', 'auto_apply']),\n}).strict();\n\nexport const AutomationStateSchema = z.object({\n nextRunAtMs: z.number().int().nonnegative().optional(),\n runningRunId: optionalTrimmedString(100),\n lastRunAtMs: z.number().int().nonnegative().optional(),\n lastRunStatus: z.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled', 'timeout']).optional(),\n lastError: z.string().optional(),\n consecutiveFailures: z.number().int().nonnegative().optional(),\n}).strict();\n\nexport const AutomationSchema = z.object({\n id: nonEmptyString.max(100),\n name: nonEmptyString.max(200),\n description: z.string().max(2000).optional(),\n enabled: z.boolean(),\n trigger: AutomationTriggerSchema,\n action: AutomationActionSchema,\n safety: AutomationSafetyPolicySchema.optional(),\n afterRun: AutomationAfterRunSchema.optional(),\n reliability: AutomationReliabilitySchema.optional(),\n state: AutomationStateSchema.default({}),\n createdAtMs: z.number().int().nonnegative(),\n updatedAtMs: z.number().int().nonnegative(),\n}).strict();\n\nexport const CreateAutomationSchema = AutomationSchema.omit({\n id: true,\n enabled: true,\n state: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).extend({\n id: optionalTrimmedString(100),\n enabled: z.boolean().optional(),\n state: AutomationStateSchema.partial().optional(),\n});\n\nexport const UpdateAutomationSchema = AutomationSchema.omit({\n id: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).partial().refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided' },\n);\n\nexport type CreateAutomationInput = z.infer<typeof CreateAutomationSchema>;\nexport type UpdateAutomationInput = z.infer<typeof UpdateAutomationSchema>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","names":[],"sources":["../../../../src/automations/domain/validation.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { z } from 'zod';\n\nconst nonEmptyString = z.string().trim().min(1);\n\nconst optionalTrimmedString = (max: number) =>\n z.preprocess((value) => {\n if (value == null) return undefined;\n if (typeof value !== 'string') return value;\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n }, z.string().min(1).max(max).optional());\n\nexport const AutomationScheduleSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('once'),\n at: nonEmptyString,\n }).strict(),\n z.object({\n kind: z.literal('interval'),\n everyMs: z.number().int().min(1),\n anchorMs: z.number().int().nonnegative().optional(),\n }).strict(),\n z.object({\n kind: z.literal('cron'),\n expr: nonEmptyString.superRefine((expr, ctx) => {\n try {\n CronExpressionParser.parse(expr);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid cron expression: ${expr}`,\n });\n }\n }),\n tz: optionalTrimmedString(100),\n }).strict(),\n]);\n\nexport const AutomationTriggerSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('manual') }).strict(),\n z.object({\n kind: z.literal('schedule'),\n schedule: AutomationScheduleSchema,\n }).strict(),\n z.object({\n kind: z.literal('webhook'),\n secretId: optionalTrimmedString(200),\n }).strict(),\n z.object({\n kind: z.literal('event'),\n eventType: nonEmptyString.max(200),\n source: optionalTrimmedString(200),\n payloadMatch: z.record(\n z.string(),\n z.union([z.string(), z.number(), z.boolean(), z.null()]),\n ).optional(),\n }).strict(),\n]);\n\nconst WorkflowRunInputEnvelopeSchema = z.object({\n payload: z.unknown(),\n goal: optionalTrimmedString(5000),\n variables: z.record(z.string(), z.unknown()).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n}).strict();\n\nexport const AutomationActionSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('agent'),\n agentId: optionalTrimmedString(64),\n instruction: nonEmptyString.max(50000),\n workingDirectory: optionalTrimmedString(4096),\n model: optionalTrimmedString(100),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n z.object({\n kind: z.literal('workflow'),\n workflowId: nonEmptyString.max(200),\n agentId: optionalTrimmedString(64),\n input: z.unknown().optional(),\n inputEnvelope: WorkflowRunInputEnvelopeSchema.optional(),\n goal: optionalTrimmedString(5000),\n concurrency: z.number().int().min(1).max(50).optional(),\n maxSubagents: z.number().int().min(1).max(100).optional(),\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n }).strict(),\n]);\n\nexport const AutomationAfterRunSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('none') }).strict(),\n z.object({ kind: z.literal('saveToSession') }).strict(),\n z.object({\n kind: z.literal('webhook'),\n url: nonEmptyString.max(2000),\n }).strict(),\n]);\n\nexport const AutomationReliabilitySchema = z.object({\n timeoutSeconds: z.number().int().min(1).max(86400).optional(),\n retryCount: z.number().int().min(0).max(10).optional(),\n maxConcurrentRuns: z.number().int().min(1).max(20).optional(),\n disableAfterConsecutiveFailures: z.number().int().min(1).max(100).optional(),\n}).strict();\n\nexport const AutomationSafetyPolicySchema = z.object({\n mode: z.enum(['suggest_only', 'ask_before_apply', 'auto_apply']),\n}).strict();\n\nexport const AutomationStateSchema = z.object({\n nextRunAtMs: z.number().int().nonnegative().optional(),\n runningRunId: optionalTrimmedString(100),\n lastRunAtMs: z.number().int().nonnegative().optional(),\n lastRunStatus: z.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled', 'timeout']).optional(),\n lastError: z.string().optional(),\n consecutiveFailures: z.number().int().nonnegative().optional(),\n}).strict();\n\nexport const AutomationSchema = z.object({\n id: nonEmptyString.max(100),\n name: nonEmptyString.max(200),\n description: z.string().max(2000).optional(),\n projectId: optionalTrimmedString(100),\n enabled: z.boolean(),\n trigger: AutomationTriggerSchema,\n action: AutomationActionSchema,\n safety: AutomationSafetyPolicySchema.optional(),\n afterRun: AutomationAfterRunSchema.optional(),\n reliability: AutomationReliabilitySchema.optional(),\n state: AutomationStateSchema.default({}),\n createdAtMs: z.number().int().nonnegative(),\n updatedAtMs: z.number().int().nonnegative(),\n}).strict();\n\nexport const CreateAutomationSchema = AutomationSchema.omit({\n id: true,\n enabled: true,\n state: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).extend({\n id: optionalTrimmedString(100),\n enabled: z.boolean().optional(),\n state: AutomationStateSchema.partial().optional(),\n});\n\nexport const UpdateAutomationSchema = AutomationSchema.omit({\n id: true,\n createdAtMs: true,\n updatedAtMs: true,\n}).partial().refine(\n (data) => Object.keys(data).length > 0,\n { message: 'At least one field must be provided' },\n);\n\nexport type CreateAutomationInput = z.infer<typeof CreateAutomationSchema>;\nexport type UpdateAutomationInput = z.infer<typeof UpdateAutomationSchema>;\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ export declare function buildAutomationDraftResponse(draft: GeneratedAutomationD
12
12
  automation: {
13
13
  name: string;
14
14
  description?: string;
15
+ projectId?: string;
15
16
  trigger?: {
16
17
  kind: "manual";
17
18
  } | {
@@ -71,7 +71,8 @@ var AutomationActionExecutor = class {
71
71
  peerKind: "dm",
72
72
  peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`
73
73
  });
74
- await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(sessionKey, action.workingDirectory);
74
+ const effectiveWorkingDirectory = action.workingDirectory ?? (automation.projectId ? this.deps.getProjectWorkspaceRoot?.(automation.projectId) : void 0);
75
+ await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(sessionKey, effectiveWorkingDirectory);
75
76
  if (agentService.sessionConfig?.applyAutomationModelOverride) {
76
77
  if (!await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model) && action.model) log.warn({
77
78
  automationId: automation.id,
@@ -111,6 +112,7 @@ var AutomationActionExecutor = class {
111
112
  input: action.input,
112
113
  inputEnvelope: action.inputEnvelope,
113
114
  goal: action.goal,
115
+ projectId: automation.projectId,
114
116
  concurrency: action.concurrency,
115
117
  maxSubagents: action.maxSubagents,
116
118
  source: {
@@ -1 +1 @@
1
- {"version":3,"file":"action-executor.js","names":[],"sources":["../../../../src/automations/service/action-executor.ts"],"sourcesContent":["import crypto from 'node:crypto';\n\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { buildSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n Automation,\n AutomationAction,\n AutomationActionOutcome,\n AutomationDeps,\n AutomationRun,\n} from '../domain/types.js';\n\nconst log = createLogger('Automation:ActionExecutor');\n\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, signal: AbortSignal): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('Automation run was cancelled'));\n return;\n }\n const timer = setTimeout(() => reject(new Error(`Automation timed out after ${timeoutMs}ms`)), timeoutMs);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error('Automation run was cancelled'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n}\n\nexport class AutomationActionExecutor {\n private deps: AutomationDeps = {};\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n }\n\n async execute(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n const timeoutMs =\n (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds ?? 0) > 0\n ? (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds)! * 1000\n : DEFAULT_TIMEOUT_MS;\n\n return withTimeout(this.executeWithoutTimeout(automation, run, signal), timeoutMs, signal).catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: message.toLowerCase().includes('cancelled')\n ? 'cancelled'\n : message.toLowerCase().includes('timed out')\n ? 'timeout'\n : 'failed',\n error: message,\n };\n });\n }\n\n private async executeWithoutTimeout(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n if (signal.aborted) {\n return { status: 'cancelled', error: 'Automation run was cancelled' };\n }\n if (automation.action.kind === 'workflow') {\n return this.executeWorkflow(automation, automation.action, run);\n }\n return this.executeAgent(automation, automation.action);\n }\n\n private async executeAgent(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'agent' }>,\n ): Promise<AutomationActionOutcome> {\n const agentService = this.deps.agentService;\n if (!agentService?.turnDispatcher?.processDirect) {\n return { status: 'failed', error: 'Agent service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const sessionKey = buildSessionKey({\n agentId,\n source: 'automation',\n accountId: 'default',\n peerKind: 'dm',\n peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`,\n });\n\n await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(\n sessionKey,\n action.workingDirectory,\n );\n if (agentService.sessionConfig?.applyAutomationModelOverride) {\n const ok = await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model);\n if (!ok && action.model) {\n log.warn(\n { automationId: automation.id, sessionKey, model: action.model },\n 'Automation model override invalid; using agent default',\n );\n }\n }\n\n const response = await agentService.turnDispatcher.processDirect(\n buildSafetyInstruction(automation, action.instruction),\n sessionKey,\n );\n const model = agentService.getModelForSession?.(sessionKey);\n return {\n status: 'succeeded',\n summary: response.slice(0, 500),\n sessionKey,\n model,\n };\n }\n\n private async executeWorkflow(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'workflow' }>,\n run: AutomationRun,\n ): Promise<AutomationActionOutcome> {\n const safetyMode = automation.safety?.mode ?? 'auto_apply';\n if (safetyMode === 'suggest_only') {\n return {\n status: 'succeeded',\n summary: `Suggest only: workflow ${action.workflowId} was not started. Review the automation and upgrade safety mode to run it automatically.`,\n };\n }\n if (safetyMode === 'ask_before_apply') {\n return {\n status: 'succeeded',\n summary: `Ask before applying: workflow ${action.workflowId} is ready, but was not started automatically. Upgrade to Auto apply when you trust this automation.`,\n };\n }\n const workflowRunService = this.deps.workflowRunService;\n if (!workflowRunService) {\n return { status: 'failed', error: 'Workflow service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const idempotencyKey = `automation:${automation.id}:${run.id}`;\n const result = await workflowRunService.startWorkflowRun({\n agentId,\n definitionId: action.workflowId,\n input: action.input,\n inputEnvelope: action.inputEnvelope,\n goal: action.goal,\n concurrency: action.concurrency,\n maxSubagents: action.maxSubagents,\n source: { kind: 'automation', automationId: automation.id, runId: run.id },\n idempotencyKey,\n });\n if (result.ok === false) {\n return { status: 'failed', error: result.message };\n }\n return {\n status: 'succeeded',\n summary: `Started workflow run ${result.runId}`,\n sessionKey: result.sessionKey,\n workflowRunId: result.runId,\n };\n }\n}\n\nfunction buildSafetyInstruction(automation: Automation, instruction: string): string {\n const mode = automation.safety?.mode ?? 'auto_apply';\n if (mode === 'suggest_only') {\n return [\n 'Automation safety mode: Suggest only.',\n 'Only analyze the situation and produce a concise recommendation.',\n 'Do not modify files, notes, goals, workflows, external systems, or persistent state.',\n 'If a change seems useful, describe the exact change for the user to review.',\n '',\n instruction,\n ].join('\\n');\n }\n if (mode === 'ask_before_apply') {\n return [\n 'Automation safety mode: Ask before applying.',\n 'Draft the proposed change or action and clearly ask for user confirmation before applying anything.',\n 'Do not perform irreversible or external side effects unless the user has explicitly approved them in this run.',\n '',\n instruction,\n ].join('\\n');\n }\n return instruction;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"action-executor.js","names":[],"sources":["../../../../src/automations/service/action-executor.ts"],"sourcesContent":["import crypto from 'node:crypto';\n\nimport { DEFAULT_AGENT_ID, normalizeAgentId } from '../../agent/agent-scope.js';\nimport { buildSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n Automation,\n AutomationAction,\n AutomationActionOutcome,\n AutomationDeps,\n AutomationRun,\n} from '../domain/types.js';\n\nconst log = createLogger('Automation:ActionExecutor');\n\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, signal: AbortSignal): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error('Automation run was cancelled'));\n return;\n }\n const timer = setTimeout(() => reject(new Error(`Automation timed out after ${timeoutMs}ms`)), timeoutMs);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error('Automation run was cancelled'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n}\n\nexport class AutomationActionExecutor {\n private deps: AutomationDeps = {};\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n }\n\n async execute(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n const timeoutMs =\n (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds ?? 0) > 0\n ? (automation.action.timeoutSeconds ?? automation.reliability?.timeoutSeconds)! * 1000\n : DEFAULT_TIMEOUT_MS;\n\n return withTimeout(this.executeWithoutTimeout(automation, run, signal), timeoutMs, signal).catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: message.toLowerCase().includes('cancelled')\n ? 'cancelled'\n : message.toLowerCase().includes('timed out')\n ? 'timeout'\n : 'failed',\n error: message,\n };\n });\n }\n\n private async executeWithoutTimeout(\n automation: Automation,\n run: AutomationRun,\n signal: AbortSignal,\n ): Promise<AutomationActionOutcome> {\n if (signal.aborted) {\n return { status: 'cancelled', error: 'Automation run was cancelled' };\n }\n if (automation.action.kind === 'workflow') {\n return this.executeWorkflow(automation, automation.action, run);\n }\n return this.executeAgent(automation, automation.action);\n }\n\n private async executeAgent(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'agent' }>,\n ): Promise<AutomationActionOutcome> {\n const agentService = this.deps.agentService;\n if (!agentService?.turnDispatcher?.processDirect) {\n return { status: 'failed', error: 'Agent service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const sessionKey = buildSessionKey({\n agentId,\n source: 'automation',\n accountId: 'default',\n peerKind: 'dm',\n peerId: `${automation.id}-${crypto.randomUUID().slice(0, 8)}`,\n });\n const effectiveWorkingDirectory = action.workingDirectory\n ?? (automation.projectId ? this.deps.getProjectWorkspaceRoot?.(automation.projectId) : undefined);\n\n await agentService.sessionConfig?.applyAutomationWorkingDirectory?.(\n sessionKey,\n effectiveWorkingDirectory,\n );\n if (agentService.sessionConfig?.applyAutomationModelOverride) {\n const ok = await agentService.sessionConfig.applyAutomationModelOverride(sessionKey, action.model);\n if (!ok && action.model) {\n log.warn(\n { automationId: automation.id, sessionKey, model: action.model },\n 'Automation model override invalid; using agent default',\n );\n }\n }\n\n const response = await agentService.turnDispatcher.processDirect(\n buildSafetyInstruction(automation, action.instruction),\n sessionKey,\n );\n const model = agentService.getModelForSession?.(sessionKey);\n return {\n status: 'succeeded',\n summary: response.slice(0, 500),\n sessionKey,\n model,\n };\n }\n\n private async executeWorkflow(\n automation: Automation,\n action: Extract<AutomationAction, { kind: 'workflow' }>,\n run: AutomationRun,\n ): Promise<AutomationActionOutcome> {\n const safetyMode = automation.safety?.mode ?? 'auto_apply';\n if (safetyMode === 'suggest_only') {\n return {\n status: 'succeeded',\n summary: `Suggest only: workflow ${action.workflowId} was not started. Review the automation and upgrade safety mode to run it automatically.`,\n };\n }\n if (safetyMode === 'ask_before_apply') {\n return {\n status: 'succeeded',\n summary: `Ask before applying: workflow ${action.workflowId} is ready, but was not started automatically. Upgrade to Auto apply when you trust this automation.`,\n };\n }\n const workflowRunService = this.deps.workflowRunService;\n if (!workflowRunService) {\n return { status: 'failed', error: 'Workflow service is not available' };\n }\n const agentId = normalizeAgentId(\n action.agentId || this.deps.getDefaultAgentId?.() || DEFAULT_AGENT_ID,\n );\n const idempotencyKey = `automation:${automation.id}:${run.id}`;\n const result = await workflowRunService.startWorkflowRun({\n agentId,\n definitionId: action.workflowId,\n input: action.input,\n inputEnvelope: action.inputEnvelope,\n goal: action.goal,\n projectId: automation.projectId,\n concurrency: action.concurrency,\n maxSubagents: action.maxSubagents,\n source: { kind: 'automation', automationId: automation.id, runId: run.id },\n idempotencyKey,\n });\n if (result.ok === false) {\n return { status: 'failed', error: result.message };\n }\n return {\n status: 'succeeded',\n summary: `Started workflow run ${result.runId}`,\n sessionKey: result.sessionKey,\n workflowRunId: result.runId,\n };\n }\n}\n\nfunction buildSafetyInstruction(automation: Automation, instruction: string): string {\n const mode = automation.safety?.mode ?? 'auto_apply';\n if (mode === 'suggest_only') {\n return [\n 'Automation safety mode: Suggest only.',\n 'Only analyze the situation and produce a concise recommendation.',\n 'Do not modify files, notes, goals, workflows, external systems, or persistent state.',\n 'If a change seems useful, describe the exact change for the user to review.',\n '',\n instruction,\n ].join('\\n');\n }\n if (mode === 'ask_before_apply') {\n return [\n 'Automation safety mode: Ask before applying.',\n 'Draft the proposed change or action and clearly ask for user confirmation before applying anything.',\n 'Do not perform irreversible or external side effects unless the user has explicitly approved them in this run.',\n '',\n instruction,\n ].join('\\n');\n }\n return instruction;\n}\n"],"mappings":"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"}
@@ -19,7 +19,9 @@ export declare class AutomationService {
19
19
  }): Promise<void>;
20
20
  stop(): Promise<void>;
21
21
  create(input: CreateAutomationInput): Promise<Automation>;
22
- list(): Promise<Automation[]>;
22
+ list(options?: {
23
+ projectId?: string;
24
+ }): Promise<Automation[]>;
23
25
  get(id: string): Promise<Automation | null>;
24
26
  update(id: string, patch: UpdateAutomationInput): Promise<Automation | null>;
25
27
  remove(id: string): Promise<boolean>;
@@ -30,6 +32,7 @@ export declare class AutomationService {
30
32
  triggerEvent(event: AutomationEvent): Promise<AutomationRun[]>;
31
33
  listRuns(options?: {
32
34
  automationId?: string;
35
+ projectId?: string;
33
36
  limit?: number;
34
37
  }): Promise<AutomationRun[]>;
35
38
  listRunsForProductEvent(options: {
@@ -69,8 +69,8 @@ var AutomationService = class {
69
69
  this.armTimer();
70
70
  return automation;
71
71
  }
72
- async list() {
73
- return listAutomations();
72
+ async list(options) {
73
+ return listAutomations(options);
74
74
  }
75
75
  async get(id) {
76
76
  return getAutomation(id);
@@ -1 +1 @@
1
- {"version":3,"file":"automation-service.js","names":[],"sources":["../../../../src/automations/service/automation-service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n computeNextAutomationRunAtMs,\n timerDelayUntil,\n} from '../domain/schedule.js';\nimport type {\n Automation,\n AutomationDeps,\n AutomationEvent,\n AutomationMetrics,\n AutomationProductEventRun,\n AutomationRun,\n AutomationRunEvent,\n AutomationRunEventType,\n AutomationRunStatus,\n} from '../domain/types.js';\nimport {\n CreateAutomationSchema,\n UpdateAutomationSchema,\n type CreateAutomationInput,\n type UpdateAutomationInput,\n AutomationSchema,\n} from '../domain/validation.js';\nimport {\n appendAutomationRunEvent,\n deleteAutomation,\n getAutomation,\n getAutomationRun,\n listAutomationRunEvents,\n listAutomationRuns,\n listAutomationRunsForProductEvent,\n listAutomations,\n saveAutomation,\n saveAutomationRun,\n saveAutomations,\n} from '../storage/index.js';\nimport { AutomationActionExecutor } from './action-executor.js';\n\nconst log = createLogger('AutomationService');\n\ntype TimerHandle = ReturnType<typeof setTimeout> & { unref?: () => void };\n\nconst DEFAULT_MAX_CONCURRENT_RUNS = 5;\nconst MISSED_RUN_STAGGER_MS = 5_000;\n\nexport class AutomationAlreadyRunningError extends Error {\n constructor(readonly automationId: string, readonly runningRunId?: string) {\n super(`Automation is already running: ${automationId}`);\n this.name = 'AutomationAlreadyRunningError';\n }\n}\n\nexport class AutomationService {\n private readonly executor = new AutomationActionExecutor();\n private timer: TimerHandle | null = null;\n private stopped = true;\n private runningScheduler = false;\n private deps: AutomationDeps = {};\n private maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;\n private readonly activeRuns = new Map<string, AbortController>();\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n this.executor.setDeps(this.deps);\n }\n\n async initialize(options?: { maxConcurrentRuns?: number }): Promise<void> {\n this.maxConcurrentRuns = Math.max(1, Math.floor(options?.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS));\n this.stopped = false;\n await this.recomputeNextRuns({ catchUp: true });\n this.armTimer();\n log.info('Automation service initialized');\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n for (const controller of this.activeRuns.values()) {\n controller.abort();\n }\n log.info('Automation service stopped');\n }\n\n async create(input: CreateAutomationInput): Promise<Automation> {\n const parsed = CreateAutomationSchema.parse(input);\n const now = Date.now();\n const automation = AutomationSchema.parse({\n ...parsed,\n id: parsed.id?.trim() || randomUUID().slice(0, 12),\n enabled: parsed.enabled ?? true,\n safety: parsed.safety ?? { mode: 'auto_apply' },\n afterRun: parsed.afterRun ?? { kind: 'none' },\n state: parsed.state ?? {},\n createdAtMs: now,\n updatedAtMs: now,\n }) as Automation;\n automation.state.nextRunAtMs = computeNextAutomationRunAtMs(automation, now);\n saveAutomation(automation);\n this.armTimer();\n return automation;\n }\n\n async list(): Promise<Automation[]> {\n return listAutomations();\n }\n\n async get(id: string): Promise<Automation | null> {\n return getAutomation(id);\n }\n\n async update(id: string, patch: UpdateAutomationInput): Promise<Automation | null> {\n const parsed = UpdateAutomationSchema.parse(patch);\n const current = getAutomation(id);\n if (!current) return null;\n const now = Date.now();\n const next = AutomationSchema.parse({\n ...current,\n ...parsed,\n id: current.id,\n createdAtMs: current.createdAtMs,\n updatedAtMs: now,\n state: {\n ...current.state,\n ...(parsed.state ?? {}),\n },\n }) as Automation;\n if ('enabled' in parsed || 'trigger' in parsed) {\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, now);\n }\n saveAutomation(next);\n this.armTimer();\n return next;\n }\n\n async remove(id: string): Promise<boolean> {\n const automation = getAutomation(id);\n if (automation?.state.runningRunId) {\n this.activeRuns.get(automation.state.runningRunId)?.abort();\n }\n const removed = deleteAutomation(id);\n if (removed) {\n this.armTimer();\n }\n return removed;\n }\n\n async pause(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: false });\n }\n\n async resume(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: true });\n }\n\n async runNow(id: string): Promise<AutomationRun> {\n const automation = getAutomation(id);\n if (!automation) throw new Error(`Automation not found: ${id}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(id, automation.state.runningRunId);\n }\n return this.startRun(automation, { manual: true });\n }\n\n async rerunFromRun(runId: string): Promise<AutomationRun> {\n const previous = getAutomationRun(runId);\n if (!previous) throw new Error(`Automation run not found: ${runId}`);\n const automation = getAutomation(previous.automationId);\n if (!automation) throw new Error(`Automation not found: ${previous.automationId}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(automation.id, automation.state.runningRunId);\n }\n const triggerEvent = listAutomationRunEvents(runId)\n .map((event) => readAutomationEventFromRunEvent(event))\n .find((event): event is AutomationEvent => event !== null);\n return this.startRun(automation, triggerEvent ? { manual: false, event: triggerEvent } : { manual: true });\n }\n\n async triggerEvent(event: AutomationEvent): Promise<AutomationRun[]> {\n const normalized: AutomationEvent = {\n ...event,\n occurredAtMs: event.occurredAtMs ?? Date.now(),\n };\n const automations = listAutomations().filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'event' &&\n !automation.state.runningRunId &&\n matchesAutomationEvent(automation.trigger, normalized),\n );\n const availableSlots = Math.max(0, this.maxConcurrentRuns - this.activeRuns.size);\n const started: AutomationRun[] = [];\n for (const automation of automations.slice(0, availableSlots)) {\n started.push(await this.startRun(automation, { manual: false, event: normalized }));\n }\n return started;\n }\n\n async listRuns(options?: { automationId?: string; limit?: number }): Promise<AutomationRun[]> {\n return listAutomationRuns(options);\n }\n\n async listRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n }): Promise<AutomationProductEventRun[]> {\n return listAutomationRunsForProductEvent(options);\n }\n\n async getRun(runId: string): Promise<AutomationRun | null> {\n return getAutomationRun(runId);\n }\n\n async listRunEvents(runId: string): Promise<AutomationRunEvent[]> {\n return listAutomationRunEvents(runId);\n }\n\n async cancelRun(runId: string): Promise<boolean> {\n const controller = this.activeRuns.get(runId);\n if (!controller) return false;\n controller.abort();\n return true;\n }\n\n async getMetrics(): Promise<AutomationMetrics> {\n const automations = listAutomations();\n const enabled = automations.filter((automation) => automation.enabled);\n const next = enabled\n .filter((automation) => automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n const oneHourAgo = Date.now() - 60 * 60_000;\n return {\n totalAutomations: automations.length,\n enabledAutomations: enabled.length,\n runningRuns: this.activeRuns.size,\n failedLastHour: automations.filter(\n (automation) =>\n (automation.state.lastRunStatus === 'failed' || automation.state.lastRunStatus === 'timeout') &&\n (automation.state.lastRunAtMs ?? 0) >= oneHourAgo,\n ).length,\n nextRun: next?.state.nextRunAtMs\n ? {\n automationId: next.id,\n name: next.name,\n runAtMs: next.state.nextRunAtMs,\n }\n : undefined,\n };\n }\n\n private async recomputeNextRuns(options?: { catchUp?: boolean }): Promise<void> {\n const now = Date.now();\n const automations = listAutomations().map((automation, index) => {\n const next: Automation = {\n ...automation,\n state: { ...automation.state },\n };\n if (next.state.runningRunId) {\n next.state.lastRunStatus = 'failed';\n next.state.lastError = 'Interrupted by gateway restart';\n next.state.consecutiveFailures = (next.state.consecutiveFailures ?? 0) + 1;\n delete next.state.runningRunId;\n }\n const computed = computeNextAutomationRunAtMs(next, now);\n next.state.nextRunAtMs =\n options?.catchUp && computed != null && computed <= now\n ? now + index * MISSED_RUN_STAGGER_MS\n : computed;\n return next;\n });\n saveAutomations(automations);\n }\n\n private armTimer(): void {\n if (this.stopped) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const next = listAutomations()\n .filter((automation) => automation.enabled && automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n if (!next?.state.nextRunAtMs) return;\n this.timer = setTimeout(() => {\n void this.onTimer();\n }, timerDelayUntil(next.state.nextRunAtMs)) as TimerHandle;\n this.timer.unref?.();\n }\n\n private async onTimer(): Promise<void> {\n if (this.stopped) return;\n if (this.runningScheduler) {\n this.armTimer();\n return;\n }\n this.runningScheduler = true;\n try {\n const now = Date.now();\n const due = listAutomations()\n .filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'schedule' &&\n automation.state.nextRunAtMs != null &&\n automation.state.nextRunAtMs <= now &&\n !automation.state.runningRunId,\n )\n .slice(0, this.maxConcurrentRuns);\n await Promise.all(due.map((automation) => this.startRun(automation, { manual: false })));\n } finally {\n this.runningScheduler = false;\n this.armTimer();\n }\n }\n\n private async startRun(\n automation: Automation,\n opts: { manual: boolean; event?: AutomationEvent },\n ): Promise<AutomationRun> {\n const now = Date.now();\n const run: AutomationRun = {\n id: randomUUID(),\n automationId: automation.id,\n automationName: automation.name,\n status: 'queued',\n triggerSnapshot: automation.trigger,\n actionSnapshot: automation.action,\n manual: opts.manual,\n createdAtMs: now,\n };\n saveAutomationRun(run);\n this.appendRunEvent(\n run,\n 'run.queued',\n opts.event\n ? `Event ${opts.event.type} queued automation`\n : opts.manual\n ? 'Manual run queued'\n : 'Scheduled run queued',\n {\n trigger: automation.trigger,\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n event: opts.event,\n },\n );\n const nextAutomation: Automation = {\n ...automation,\n state: {\n ...automation.state,\n runningRunId: run.id,\n },\n };\n saveAutomation(nextAutomation);\n\n void this.executeRun(nextAutomation, run).catch((err) => {\n log.error(\n { err, automationId: automation.id, runId: run.id },\n `Automation run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n return run;\n }\n\n private async executeRun(automation: Automation, initialRun: AutomationRun): Promise<void> {\n const controller = new AbortController();\n this.activeRuns.set(initialRun.id, controller);\n const startedAtMs = Date.now();\n let run: AutomationRun = {\n ...initialRun,\n status: 'running',\n startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.started', 'Automation run started');\n\n let status: AutomationRunStatus = 'failed';\n let error: string | undefined;\n let activePhase: 'action' | 'after_run' = 'action';\n try {\n this.appendRunEvent(run, 'action.started', `Running ${automation.action.kind} action`, {\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n const outcome = await this.executor.execute(automation, run, controller.signal);\n status = outcome.status;\n error = outcome.error;\n this.appendRunEvent(\n run,\n status === 'succeeded' ? 'action.completed' : 'action.failed',\n status === 'succeeded'\n ? `${automation.action.kind} action completed`\n : `${automation.action.kind} action ${status}`,\n {\n summary: outcome.summary,\n error: outcome.error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n },\n );\n run = {\n ...run,\n status,\n summary: outcome.summary,\n error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n };\n if ((automation.safety?.mode ?? 'auto_apply') !== 'auto_apply' && automation.afterRun?.kind === 'webhook') {\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook skipped by safety mode', {\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n } else if (automation.afterRun?.kind === 'webhook') {\n activePhase = 'after_run';\n this.appendRunEvent(run, 'after_run.started', 'Calling after-run webhook', {\n url: automation.afterRun.url,\n });\n await this.postAfterRunWebhook(automation.afterRun.url, run);\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook completed');\n }\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n status = controller.signal.aborted ? 'cancelled' : 'failed';\n this.appendRunEvent(run, activePhase === 'after_run' ? 'after_run.failed' : 'action.failed', `Automation run ${status}`, {\n error,\n });\n run = { ...run, status, error };\n } finally {\n const endedAtMs = Date.now();\n run = {\n ...run,\n status,\n endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.completed', `Automation run ${status}`, {\n status,\n durationMs: run.durationMs,\n error,\n });\n this.activeRuns.delete(initialRun.id);\n this.finishAutomationRun(automation.id, status, error, endedAtMs);\n }\n }\n\n private appendRunEvent(\n run: Pick<AutomationRun, 'id' | 'automationId'>,\n type: AutomationRunEventType,\n message: string,\n data?: unknown,\n ): void {\n appendAutomationRunEvent({\n id: randomUUID(),\n runId: run.id,\n automationId: run.automationId,\n type,\n message,\n data,\n createdAtMs: Date.now(),\n });\n }\n\n private finishAutomationRun(\n automationId: string,\n status: AutomationRunStatus,\n error: string | undefined,\n finishedAtMs: number,\n ): void {\n const current = getAutomation(automationId);\n if (!current) return;\n const failed = status === 'failed' || status === 'timeout';\n const consecutiveFailures = failed ? (current.state.consecutiveFailures ?? 0) + 1 : 0;\n const disableAfter = current.reliability?.disableAfterConsecutiveFailures;\n const shouldDisable = disableAfter != null && consecutiveFailures >= disableAfter;\n const next: Automation = {\n ...current,\n enabled: shouldDisable ? false : current.enabled,\n state: {\n ...current.state,\n lastRunAtMs: finishedAtMs,\n lastRunStatus: status,\n lastError: error,\n consecutiveFailures,\n },\n updatedAtMs: shouldDisable ? finishedAtMs : current.updatedAtMs,\n };\n delete next.state.runningRunId;\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, finishedAtMs);\n saveAutomation(next);\n this.armTimer();\n }\n\n private async postAfterRunWebhook(url: string, run: AutomationRun): Promise<void> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ run }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Automation webhook failed: HTTP ${response.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n }\n}\n\nfunction matchesAutomationEvent(\n trigger: Extract<Automation['trigger'], { kind: 'event' }>,\n event: AutomationEvent,\n): boolean {\n if (trigger.eventType !== event.type) return false;\n if (trigger.source && trigger.source !== event.source) return false;\n const payloadMatch = trigger.payloadMatch;\n if (!payloadMatch) return true;\n const payload = event.payload ?? {};\n return Object.entries(payloadMatch).every(([key, expected]) => Object.is(payload[key], expected));\n}\n\nfunction readAutomationEventFromRunEvent(event: AutomationRunEvent): AutomationEvent | null {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return null;\n const raw = (event.data as { event?: unknown }).event;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const candidate = raw as Partial<AutomationEvent>;\n if (typeof candidate.type !== 'string' || !candidate.type.trim()) return null;\n return {\n type: candidate.type,\n source: typeof candidate.source === 'string' ? candidate.source : undefined,\n payload: candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)\n ? candidate.payload as Record<string, unknown>\n : undefined,\n occurredAtMs: typeof candidate.occurredAtMs === 'number' ? candidate.occurredAtMs : undefined,\n };\n}\n"],"mappings":";;;;;;;;;;aAEqD;AAsCrD,MAAM,MAAM,aAAa,oBAAoB;AAI7C,MAAM,8BAA8B;AACpC,MAAM,wBAAwB;AAE9B,IAAa,gCAAb,cAAmD,MAAM;CACvD,YAAY,cAA+B,cAAgC;AACzE,QAAM,kCAAkC,eAAe;AADpC,OAAA,eAAA;AAA+B,OAAA,eAAA;AAElD,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,MAA+B;CAC7B,WAA4B,IAAI,0BAA0B;CAC1D,QAAoC;CACpC,UAAkB;CAClB,mBAA2B;CAC3B,OAA+B,EAAE;CACjC,oBAA4B;CAC5B,6BAA8B,IAAI,KAA8B;CAEhE,QAAQ,MAA4B;AAClC,OAAK,OAAO;GAAE,GAAG,KAAK;GAAM,GAAG;GAAM;AACrC,OAAK,SAAS,QAAQ,KAAK,KAAK;;CAGlC,MAAM,WAAW,SAAyD;AACxE,OAAK,oBAAoB,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,qBAAqB,4BAA4B,CAAC;AAC3G,OAAK,UAAU;AACf,QAAM,KAAK,kBAAkB,EAAE,SAAS,MAAM,CAAC;AAC/C,OAAK,UAAU;AACf,MAAI,KAAK,iCAAiC;;CAG5C,MAAM,OAAsB;AAC1B,OAAK,UAAU;AACf,MAAI,KAAK,OAAO;AACd,gBAAa,KAAK,MAAM;AACxB,QAAK,QAAQ;;AAEf,OAAK,MAAM,cAAc,KAAK,WAAW,QAAQ,CAC/C,YAAW,OAAO;AAEpB,MAAI,KAAK,6BAA6B;;CAGxC,MAAM,OAAO,OAAmD;EAC9D,MAAM,SAAS,uBAAuB,MAAM,MAAM;EAClD,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,aAAa,iBAAiB,MAAM;GACxC,GAAG;GACH,IAAI,OAAO,IAAI,MAAM,IAAI,YAAY,CAAC,MAAM,GAAG,GAAG;GAClD,SAAS,OAAO,WAAW;GAC3B,QAAQ,OAAO,UAAU,EAAE,MAAM,cAAc;GAC/C,UAAU,OAAO,YAAY,EAAE,MAAM,QAAQ;GAC7C,OAAO,OAAO,SAAS,EAAE;GACzB,aAAa;GACb,aAAa;GACd,CAAC;AACF,aAAW,MAAM,cAAc,6BAA6B,YAAY,IAAI;AAC5E,iBAAe,WAAW;AAC1B,OAAK,UAAU;AACf,SAAO;;CAGT,MAAM,OAA8B;AAClC,SAAO,iBAAiB;;CAG1B,MAAM,IAAI,IAAwC;AAChD,SAAO,cAAc,GAAG;;CAG1B,MAAM,OAAO,IAAY,OAA0D;EACjF,MAAM,SAAS,uBAAuB,MAAM,MAAM;EAClD,MAAM,UAAU,cAAc,GAAG;AACjC,MAAI,CAAC,QAAS,QAAO;EACrB,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,OAAO,iBAAiB,MAAM;GAClC,GAAG;GACH,GAAG;GACH,IAAI,QAAQ;GACZ,aAAa,QAAQ;GACrB,aAAa;GACb,OAAO;IACL,GAAG,QAAQ;IACX,GAAI,OAAO,SAAS,EAAE;IACvB;GACF,CAAC;AACF,MAAI,aAAa,UAAU,aAAa,OACtC,MAAK,MAAM,cAAc,6BAA6B,MAAM,IAAI;AAElE,iBAAe,KAAK;AACpB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAM,OAAO,IAA8B;EACzC,MAAM,aAAa,cAAc,GAAG;AACpC,MAAI,YAAY,MAAM,aACpB,MAAK,WAAW,IAAI,WAAW,MAAM,aAAa,EAAE,OAAO;EAE7D,MAAM,UAAU,iBAAiB,GAAG;AACpC,MAAI,QACF,MAAK,UAAU;AAEjB,SAAO;;CAGT,MAAM,MAAM,IAAwC;AAClD,SAAO,KAAK,OAAO,IAAI,EAAE,SAAS,OAAO,CAAC;;CAG5C,MAAM,OAAO,IAAwC;AACnD,SAAO,KAAK,OAAO,IAAI,EAAE,SAAS,MAAM,CAAC;;CAG3C,MAAM,OAAO,IAAoC;EAC/C,MAAM,aAAa,cAAc,GAAG;AACpC,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,yBAAyB,KAAK;AAC/D,MAAI,WAAW,MAAM,aACnB,OAAM,IAAI,8BAA8B,IAAI,WAAW,MAAM,aAAa;AAE5E,SAAO,KAAK,SAAS,YAAY,EAAE,QAAQ,MAAM,CAAC;;CAGpD,MAAM,aAAa,OAAuC;EACxD,MAAM,WAAW,iBAAiB,MAAM;AACxC,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,6BAA6B,QAAQ;EACpE,MAAM,aAAa,cAAc,SAAS,aAAa;AACvD,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,yBAAyB,SAAS,eAAe;AAClF,MAAI,WAAW,MAAM,aACnB,OAAM,IAAI,8BAA8B,WAAW,IAAI,WAAW,MAAM,aAAa;EAEvF,MAAM,eAAe,wBAAwB,MAAM,CAChD,KAAK,UAAU,gCAAgC,MAAM,CAAC,CACtD,MAAM,UAAoC,UAAU,KAAK;AAC5D,SAAO,KAAK,SAAS,YAAY,eAAe;GAAE,QAAQ;GAAO,OAAO;GAAc,GAAG,EAAE,QAAQ,MAAM,CAAC;;CAG5G,MAAM,aAAa,OAAkD;EACnE,MAAM,aAA8B;GAClC,GAAG;GACH,cAAc,MAAM,gBAAgB,KAAK,KAAK;GAC/C;EACD,MAAM,cAAc,iBAAiB,CAAC,QACnC,eACC,WAAW,WACX,WAAW,QAAQ,SAAS,WAC5B,CAAC,WAAW,MAAM,gBAClB,uBAAuB,WAAW,SAAS,WAAW,CACzD;EACD,MAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,oBAAoB,KAAK,WAAW,KAAK;EACjF,MAAM,UAA2B,EAAE;AACnC,OAAK,MAAM,cAAc,YAAY,MAAM,GAAG,eAAe,CAC3D,SAAQ,KAAK,MAAM,KAAK,SAAS,YAAY;GAAE,QAAQ;GAAO,OAAO;GAAY,CAAC,CAAC;AAErF,SAAO;;CAGT,MAAM,SAAS,SAA+E;AAC5F,SAAO,mBAAmB,QAAQ;;CAGpC,MAAM,wBAAwB,SAMW;AACvC,SAAO,kCAAkC,QAAQ;;CAGnD,MAAM,OAAO,OAA8C;AACzD,SAAO,iBAAiB,MAAM;;CAGhC,MAAM,cAAc,OAA8C;AAChE,SAAO,wBAAwB,MAAM;;CAGvC,MAAM,UAAU,OAAiC;EAC/C,MAAM,aAAa,KAAK,WAAW,IAAI,MAAM;AAC7C,MAAI,CAAC,WAAY,QAAO;AACxB,aAAW,OAAO;AAClB,SAAO;;CAGT,MAAM,aAAyC;EAC7C,MAAM,cAAc,iBAAiB;EACrC,MAAM,UAAU,YAAY,QAAQ,eAAe,WAAW,QAAQ;EACtE,MAAM,OAAO,QACV,QAAQ,eAAe,WAAW,MAAM,eAAe,KAAK,CAC5D,UAAU,GAAG,OAAO,EAAE,MAAM,eAAe,MAAM,EAAE,MAAM,eAAe,GAAG,CAAC;EAC/E,MAAM,aAAa,KAAK,KAAK,GAAG,KAAK;AACrC,SAAO;GACL,kBAAkB,YAAY;GAC9B,oBAAoB,QAAQ;GAC5B,aAAa,KAAK,WAAW;GAC7B,gBAAgB,YAAY,QACzB,gBACE,WAAW,MAAM,kBAAkB,YAAY,WAAW,MAAM,kBAAkB,eAClF,WAAW,MAAM,eAAe,MAAM,WAC1C,CAAC;GACF,SAAS,MAAM,MAAM,cACjB;IACE,cAAc,KAAK;IACnB,MAAM,KAAK;IACX,SAAS,KAAK,MAAM;IACrB,GACD,KAAA;GACL;;CAGH,MAAc,kBAAkB,SAAgD;EAC9E,MAAM,MAAM,KAAK,KAAK;AAmBtB,kBAlBoB,iBAAiB,CAAC,KAAK,YAAY,UAAU;GAC/D,MAAM,OAAmB;IACvB,GAAG;IACH,OAAO,EAAE,GAAG,WAAW,OAAO;IAC/B;AACD,OAAI,KAAK,MAAM,cAAc;AAC3B,SAAK,MAAM,gBAAgB;AAC3B,SAAK,MAAM,YAAY;AACvB,SAAK,MAAM,uBAAuB,KAAK,MAAM,uBAAuB,KAAK;AACzE,WAAO,KAAK,MAAM;;GAEpB,MAAM,WAAW,6BAA6B,MAAM,IAAI;AACxD,QAAK,MAAM,cACT,SAAS,WAAW,YAAY,QAAQ,YAAY,MAChD,MAAM,QAAQ,wBACd;AACN,UAAO;IAEkB,CAAC;;CAG9B,WAAyB;AACvB,MAAI,KAAK,QAAS;AAClB,MAAI,KAAK,OAAO;AACd,gBAAa,KAAK,MAAM;AACxB,QAAK,QAAQ;;EAEf,MAAM,OAAO,iBAAiB,CAC3B,QAAQ,eAAe,WAAW,WAAW,WAAW,MAAM,eAAe,KAAK,CAClF,UAAU,GAAG,OAAO,EAAE,MAAM,eAAe,MAAM,EAAE,MAAM,eAAe,GAAG,CAAC;AAC/E,MAAI,CAAC,MAAM,MAAM,YAAa;AAC9B,OAAK,QAAQ,iBAAiB;AACvB,QAAK,SAAS;KAClB,gBAAgB,KAAK,MAAM,YAAY,CAAC;AAC3C,OAAK,MAAM,SAAS;;CAGtB,MAAc,UAAyB;AACrC,MAAI,KAAK,QAAS;AAClB,MAAI,KAAK,kBAAkB;AACzB,QAAK,UAAU;AACf;;AAEF,OAAK,mBAAmB;AACxB,MAAI;GACF,MAAM,MAAM,KAAK,KAAK;GACtB,MAAM,MAAM,iBAAiB,CAC1B,QACE,eACC,WAAW,WACX,WAAW,QAAQ,SAAS,cAC5B,WAAW,MAAM,eAAe,QAChC,WAAW,MAAM,eAAe,OAChC,CAAC,WAAW,MAAM,aACrB,CACA,MAAM,GAAG,KAAK,kBAAkB;AACnC,SAAM,QAAQ,IAAI,IAAI,KAAK,eAAe,KAAK,SAAS,YAAY,EAAE,QAAQ,OAAO,CAAC,CAAC,CAAC;YAChF;AACR,QAAK,mBAAmB;AACxB,QAAK,UAAU;;;CAInB,MAAc,SACZ,YACA,MACwB;EACxB,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,MAAqB;GACzB,IAAI,YAAY;GAChB,cAAc,WAAW;GACzB,gBAAgB,WAAW;GAC3B,QAAQ;GACR,iBAAiB,WAAW;GAC5B,gBAAgB,WAAW;GAC3B,QAAQ,KAAK;GACb,aAAa;GACd;AACD,oBAAkB,IAAI;AACtB,OAAK,eACH,KACA,cACA,KAAK,QACD,SAAS,KAAK,MAAM,KAAK,sBACzB,KAAK,SACH,sBACA,wBACN;GACE,SAAS,WAAW;GACpB,YAAY,WAAW,OAAO;GAC9B,QAAQ,WAAW,UAAU,EAAE,MAAM,cAAc;GACnD,OAAO,KAAK;GACb,CACF;EACD,MAAM,iBAA6B;GACjC,GAAG;GACH,OAAO;IACL,GAAG,WAAW;IACd,cAAc,IAAI;IACnB;GACF;AACD,iBAAe,eAAe;AAEzB,OAAK,WAAW,gBAAgB,IAAI,CAAC,OAAO,QAAQ;AACvD,OAAI,MACF;IAAE;IAAK,cAAc,WAAW;IAAI,OAAO,IAAI;IAAI,EACnD,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC3E;IACD;AAEF,SAAO;;CAGT,MAAc,WAAW,YAAwB,YAA0C;EACzF,MAAM,aAAa,IAAI,iBAAiB;AACxC,OAAK,WAAW,IAAI,WAAW,IAAI,WAAW;EAC9C,MAAM,cAAc,KAAK,KAAK;EAC9B,IAAI,MAAqB;GACvB,GAAG;GACH,QAAQ;GACR;GACD;AACD,oBAAkB,IAAI;AACtB,OAAK,eAAe,KAAK,eAAe,yBAAyB;EAEjE,IAAI,SAA8B;EAClC,IAAI;EACJ,IAAI,cAAsC;AAC1C,MAAI;AACF,QAAK,eAAe,KAAK,kBAAkB,WAAW,WAAW,OAAO,KAAK,UAAU;IACrF,YAAY,WAAW,OAAO;IAC9B,QAAQ,WAAW,UAAU,EAAE,MAAM,cAAc;IACpD,CAAC;GACF,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,YAAY,KAAK,WAAW,OAAO;AAC/E,YAAS,QAAQ;AACjB,WAAQ,QAAQ;AAChB,QAAK,eACH,KACA,WAAW,cAAc,qBAAqB,iBAC9C,WAAW,cACP,GAAG,WAAW,OAAO,KAAK,qBAC1B,GAAG,WAAW,OAAO,KAAK,UAAU,UACxC;IACE,SAAS,QAAQ;IACjB,OAAO,QAAQ;IACf,YAAY,QAAQ;IACpB,eAAe,QAAQ;IACvB,OAAO,QAAQ;IAChB,CACF;AACD,SAAM;IACJ,GAAG;IACH;IACA,SAAS,QAAQ;IACjB;IACA,YAAY,QAAQ;IACpB,eAAe,QAAQ;IACvB,OAAO,QAAQ;IAChB;AACD,QAAK,WAAW,QAAQ,QAAQ,kBAAkB,gBAAgB,WAAW,UAAU,SAAS,UAC9F,MAAK,eAAe,KAAK,uBAAuB,4CAA4C,EAC1F,QAAQ,WAAW,UAAU,EAAE,MAAM,cAAc,EACpD,CAAC;YACO,WAAW,UAAU,SAAS,WAAW;AAClD,kBAAc;AACd,SAAK,eAAe,KAAK,qBAAqB,6BAA6B,EACzE,KAAK,WAAW,SAAS,KAC1B,CAAC;AACF,UAAM,KAAK,oBAAoB,WAAW,SAAS,KAAK,IAAI;AAC5D,SAAK,eAAe,KAAK,uBAAuB,8BAA8B;;WAEzE,KAAK;AACZ,WAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACxD,YAAS,WAAW,OAAO,UAAU,cAAc;AACnD,QAAK,eAAe,KAAK,gBAAgB,cAAc,qBAAqB,iBAAiB,kBAAkB,UAAU,EACvH,OACD,CAAC;AACF,SAAM;IAAE,GAAG;IAAK;IAAQ;IAAO;YACvB;GACR,MAAM,YAAY,KAAK,KAAK;AAC5B,SAAM;IACJ,GAAG;IACH;IACA;IACA,YAAY,YAAY;IACzB;AACD,qBAAkB,IAAI;AACtB,QAAK,eAAe,KAAK,iBAAiB,kBAAkB,UAAU;IACpE;IACA,YAAY,IAAI;IAChB;IACD,CAAC;AACF,QAAK,WAAW,OAAO,WAAW,GAAG;AACrC,QAAK,oBAAoB,WAAW,IAAI,QAAQ,OAAO,UAAU;;;CAIrE,eACE,KACA,MACA,SACA,MACM;AACN,2BAAyB;GACvB,IAAI,YAAY;GAChB,OAAO,IAAI;GACX,cAAc,IAAI;GAClB;GACA;GACA;GACA,aAAa,KAAK,KAAK;GACxB,CAAC;;CAGJ,oBACE,cACA,QACA,OACA,cACM;EACN,MAAM,UAAU,cAAc,aAAa;AAC3C,MAAI,CAAC,QAAS;EAEd,MAAM,sBADS,WAAW,YAAY,WAAW,aACX,QAAQ,MAAM,uBAAuB,KAAK,IAAI;EACpF,MAAM,eAAe,QAAQ,aAAa;EAC1C,MAAM,gBAAgB,gBAAgB,QAAQ,uBAAuB;EACrE,MAAM,OAAmB;GACvB,GAAG;GACH,SAAS,gBAAgB,QAAQ,QAAQ;GACzC,OAAO;IACL,GAAG,QAAQ;IACX,aAAa;IACb,eAAe;IACf,WAAW;IACX;IACD;GACD,aAAa,gBAAgB,eAAe,QAAQ;GACrD;AACD,SAAO,KAAK,MAAM;AAClB,OAAK,MAAM,cAAc,6BAA6B,MAAM,aAAa;AACzE,iBAAe,KAAK;AACpB,OAAK,UAAU;;CAGjB,MAAc,oBAAoB,KAAa,KAAmC;EAChF,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;GAC9B,CAAC;AACF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,GAAG;AAClD,SAAM,IAAI,MAAM,mCAAmC,SAAS,SAAS,OAAO,IAAI,KAAK,MAAM,GAAG,IAAI,KAAK,KAAK;;;;AAKlH,SAAS,uBACP,SACA,OACS;AACT,KAAI,QAAQ,cAAc,MAAM,KAAM,QAAO;AAC7C,KAAI,QAAQ,UAAU,QAAQ,WAAW,MAAM,OAAQ,QAAO;CAC9D,MAAM,eAAe,QAAQ;AAC7B,KAAI,CAAC,aAAc,QAAO;CAC1B,MAAM,UAAU,MAAM,WAAW,EAAE;AACnC,QAAO,OAAO,QAAQ,aAAa,CAAC,OAAO,CAAC,KAAK,cAAc,OAAO,GAAG,QAAQ,MAAM,SAAS,CAAC;;AAGnG,SAAS,gCAAgC,OAAmD;AAC1F,KAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,SAAS,YAAY,MAAM,QAAQ,MAAM,KAAK,CAAE,QAAO;CACvF,MAAM,MAAO,MAAM,KAA6B;AAChD,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO;CAClE,MAAM,YAAY;AAClB,KAAI,OAAO,UAAU,SAAS,YAAY,CAAC,UAAU,KAAK,MAAM,CAAE,QAAO;AACzE,QAAO;EACL,MAAM,UAAU;EAChB,QAAQ,OAAO,UAAU,WAAW,WAAW,UAAU,SAAS,KAAA;EAClE,SAAS,UAAU,WAAW,OAAO,UAAU,YAAY,YAAY,CAAC,MAAM,QAAQ,UAAU,QAAQ,GACpG,UAAU,UACV,KAAA;EACJ,cAAc,OAAO,UAAU,iBAAiB,WAAW,UAAU,eAAe,KAAA;EACrF"}
1
+ {"version":3,"file":"automation-service.js","names":[],"sources":["../../../../src/automations/service/automation-service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n computeNextAutomationRunAtMs,\n timerDelayUntil,\n} from '../domain/schedule.js';\nimport type {\n Automation,\n AutomationDeps,\n AutomationEvent,\n AutomationMetrics,\n AutomationProductEventRun,\n AutomationRun,\n AutomationRunEvent,\n AutomationRunEventType,\n AutomationRunStatus,\n} from '../domain/types.js';\nimport {\n CreateAutomationSchema,\n UpdateAutomationSchema,\n type CreateAutomationInput,\n type UpdateAutomationInput,\n AutomationSchema,\n} from '../domain/validation.js';\nimport {\n appendAutomationRunEvent,\n deleteAutomation,\n getAutomation,\n getAutomationRun,\n listAutomationRunEvents,\n listAutomationRuns,\n listAutomationRunsForProductEvent,\n listAutomations,\n saveAutomation,\n saveAutomationRun,\n saveAutomations,\n} from '../storage/index.js';\nimport { AutomationActionExecutor } from './action-executor.js';\n\nconst log = createLogger('AutomationService');\n\ntype TimerHandle = ReturnType<typeof setTimeout> & { unref?: () => void };\n\nconst DEFAULT_MAX_CONCURRENT_RUNS = 5;\nconst MISSED_RUN_STAGGER_MS = 5_000;\n\nexport class AutomationAlreadyRunningError extends Error {\n constructor(readonly automationId: string, readonly runningRunId?: string) {\n super(`Automation is already running: ${automationId}`);\n this.name = 'AutomationAlreadyRunningError';\n }\n}\n\nexport class AutomationService {\n private readonly executor = new AutomationActionExecutor();\n private timer: TimerHandle | null = null;\n private stopped = true;\n private runningScheduler = false;\n private deps: AutomationDeps = {};\n private maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;\n private readonly activeRuns = new Map<string, AbortController>();\n\n setDeps(deps: AutomationDeps): void {\n this.deps = { ...this.deps, ...deps };\n this.executor.setDeps(this.deps);\n }\n\n async initialize(options?: { maxConcurrentRuns?: number }): Promise<void> {\n this.maxConcurrentRuns = Math.max(1, Math.floor(options?.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS));\n this.stopped = false;\n await this.recomputeNextRuns({ catchUp: true });\n this.armTimer();\n log.info('Automation service initialized');\n }\n\n async stop(): Promise<void> {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n for (const controller of this.activeRuns.values()) {\n controller.abort();\n }\n log.info('Automation service stopped');\n }\n\n async create(input: CreateAutomationInput): Promise<Automation> {\n const parsed = CreateAutomationSchema.parse(input);\n const now = Date.now();\n const automation = AutomationSchema.parse({\n ...parsed,\n id: parsed.id?.trim() || randomUUID().slice(0, 12),\n enabled: parsed.enabled ?? true,\n safety: parsed.safety ?? { mode: 'auto_apply' },\n afterRun: parsed.afterRun ?? { kind: 'none' },\n state: parsed.state ?? {},\n createdAtMs: now,\n updatedAtMs: now,\n }) as Automation;\n automation.state.nextRunAtMs = computeNextAutomationRunAtMs(automation, now);\n saveAutomation(automation);\n this.armTimer();\n return automation;\n }\n\n async list(options?: { projectId?: string }): Promise<Automation[]> {\n return listAutomations(options);\n }\n\n async get(id: string): Promise<Automation | null> {\n return getAutomation(id);\n }\n\n async update(id: string, patch: UpdateAutomationInput): Promise<Automation | null> {\n const parsed = UpdateAutomationSchema.parse(patch);\n const current = getAutomation(id);\n if (!current) return null;\n const now = Date.now();\n const next = AutomationSchema.parse({\n ...current,\n ...parsed,\n id: current.id,\n createdAtMs: current.createdAtMs,\n updatedAtMs: now,\n state: {\n ...current.state,\n ...(parsed.state ?? {}),\n },\n }) as Automation;\n if ('enabled' in parsed || 'trigger' in parsed) {\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, now);\n }\n saveAutomation(next);\n this.armTimer();\n return next;\n }\n\n async remove(id: string): Promise<boolean> {\n const automation = getAutomation(id);\n if (automation?.state.runningRunId) {\n this.activeRuns.get(automation.state.runningRunId)?.abort();\n }\n const removed = deleteAutomation(id);\n if (removed) {\n this.armTimer();\n }\n return removed;\n }\n\n async pause(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: false });\n }\n\n async resume(id: string): Promise<Automation | null> {\n return this.update(id, { enabled: true });\n }\n\n async runNow(id: string): Promise<AutomationRun> {\n const automation = getAutomation(id);\n if (!automation) throw new Error(`Automation not found: ${id}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(id, automation.state.runningRunId);\n }\n return this.startRun(automation, { manual: true });\n }\n\n async rerunFromRun(runId: string): Promise<AutomationRun> {\n const previous = getAutomationRun(runId);\n if (!previous) throw new Error(`Automation run not found: ${runId}`);\n const automation = getAutomation(previous.automationId);\n if (!automation) throw new Error(`Automation not found: ${previous.automationId}`);\n if (automation.state.runningRunId) {\n throw new AutomationAlreadyRunningError(automation.id, automation.state.runningRunId);\n }\n const triggerEvent = listAutomationRunEvents(runId)\n .map((event) => readAutomationEventFromRunEvent(event))\n .find((event): event is AutomationEvent => event !== null);\n return this.startRun(automation, triggerEvent ? { manual: false, event: triggerEvent } : { manual: true });\n }\n\n async triggerEvent(event: AutomationEvent): Promise<AutomationRun[]> {\n const normalized: AutomationEvent = {\n ...event,\n occurredAtMs: event.occurredAtMs ?? Date.now(),\n };\n const automations = listAutomations().filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'event' &&\n !automation.state.runningRunId &&\n matchesAutomationEvent(automation.trigger, normalized),\n );\n const availableSlots = Math.max(0, this.maxConcurrentRuns - this.activeRuns.size);\n const started: AutomationRun[] = [];\n for (const automation of automations.slice(0, availableSlots)) {\n started.push(await this.startRun(automation, { manual: false, event: normalized }));\n }\n return started;\n }\n\n async listRuns(options?: { automationId?: string; projectId?: string; limit?: number }): Promise<AutomationRun[]> {\n return listAutomationRuns(options);\n }\n\n async listRunsForProductEvent(options: {\n eventType: string;\n source?: string;\n payloadKey?: string;\n payloadValue?: string;\n limit?: number;\n }): Promise<AutomationProductEventRun[]> {\n return listAutomationRunsForProductEvent(options);\n }\n\n async getRun(runId: string): Promise<AutomationRun | null> {\n return getAutomationRun(runId);\n }\n\n async listRunEvents(runId: string): Promise<AutomationRunEvent[]> {\n return listAutomationRunEvents(runId);\n }\n\n async cancelRun(runId: string): Promise<boolean> {\n const controller = this.activeRuns.get(runId);\n if (!controller) return false;\n controller.abort();\n return true;\n }\n\n async getMetrics(): Promise<AutomationMetrics> {\n const automations = listAutomations();\n const enabled = automations.filter((automation) => automation.enabled);\n const next = enabled\n .filter((automation) => automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n const oneHourAgo = Date.now() - 60 * 60_000;\n return {\n totalAutomations: automations.length,\n enabledAutomations: enabled.length,\n runningRuns: this.activeRuns.size,\n failedLastHour: automations.filter(\n (automation) =>\n (automation.state.lastRunStatus === 'failed' || automation.state.lastRunStatus === 'timeout') &&\n (automation.state.lastRunAtMs ?? 0) >= oneHourAgo,\n ).length,\n nextRun: next?.state.nextRunAtMs\n ? {\n automationId: next.id,\n name: next.name,\n runAtMs: next.state.nextRunAtMs,\n }\n : undefined,\n };\n }\n\n private async recomputeNextRuns(options?: { catchUp?: boolean }): Promise<void> {\n const now = Date.now();\n const automations = listAutomations().map((automation, index) => {\n const next: Automation = {\n ...automation,\n state: { ...automation.state },\n };\n if (next.state.runningRunId) {\n next.state.lastRunStatus = 'failed';\n next.state.lastError = 'Interrupted by gateway restart';\n next.state.consecutiveFailures = (next.state.consecutiveFailures ?? 0) + 1;\n delete next.state.runningRunId;\n }\n const computed = computeNextAutomationRunAtMs(next, now);\n next.state.nextRunAtMs =\n options?.catchUp && computed != null && computed <= now\n ? now + index * MISSED_RUN_STAGGER_MS\n : computed;\n return next;\n });\n saveAutomations(automations);\n }\n\n private armTimer(): void {\n if (this.stopped) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const next = listAutomations()\n .filter((automation) => automation.enabled && automation.state.nextRunAtMs != null)\n .toSorted((a, b) => (a.state.nextRunAtMs ?? 0) - (b.state.nextRunAtMs ?? 0))[0];\n if (!next?.state.nextRunAtMs) return;\n this.timer = setTimeout(() => {\n void this.onTimer();\n }, timerDelayUntil(next.state.nextRunAtMs)) as TimerHandle;\n this.timer.unref?.();\n }\n\n private async onTimer(): Promise<void> {\n if (this.stopped) return;\n if (this.runningScheduler) {\n this.armTimer();\n return;\n }\n this.runningScheduler = true;\n try {\n const now = Date.now();\n const due = listAutomations()\n .filter(\n (automation) =>\n automation.enabled &&\n automation.trigger.kind === 'schedule' &&\n automation.state.nextRunAtMs != null &&\n automation.state.nextRunAtMs <= now &&\n !automation.state.runningRunId,\n )\n .slice(0, this.maxConcurrentRuns);\n await Promise.all(due.map((automation) => this.startRun(automation, { manual: false })));\n } finally {\n this.runningScheduler = false;\n this.armTimer();\n }\n }\n\n private async startRun(\n automation: Automation,\n opts: { manual: boolean; event?: AutomationEvent },\n ): Promise<AutomationRun> {\n const now = Date.now();\n const run: AutomationRun = {\n id: randomUUID(),\n automationId: automation.id,\n automationName: automation.name,\n status: 'queued',\n triggerSnapshot: automation.trigger,\n actionSnapshot: automation.action,\n manual: opts.manual,\n createdAtMs: now,\n };\n saveAutomationRun(run);\n this.appendRunEvent(\n run,\n 'run.queued',\n opts.event\n ? `Event ${opts.event.type} queued automation`\n : opts.manual\n ? 'Manual run queued'\n : 'Scheduled run queued',\n {\n trigger: automation.trigger,\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n event: opts.event,\n },\n );\n const nextAutomation: Automation = {\n ...automation,\n state: {\n ...automation.state,\n runningRunId: run.id,\n },\n };\n saveAutomation(nextAutomation);\n\n void this.executeRun(nextAutomation, run).catch((err) => {\n log.error(\n { err, automationId: automation.id, runId: run.id },\n `Automation run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n return run;\n }\n\n private async executeRun(automation: Automation, initialRun: AutomationRun): Promise<void> {\n const controller = new AbortController();\n this.activeRuns.set(initialRun.id, controller);\n const startedAtMs = Date.now();\n let run: AutomationRun = {\n ...initialRun,\n status: 'running',\n startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.started', 'Automation run started');\n\n let status: AutomationRunStatus = 'failed';\n let error: string | undefined;\n let activePhase: 'action' | 'after_run' = 'action';\n try {\n this.appendRunEvent(run, 'action.started', `Running ${automation.action.kind} action`, {\n actionKind: automation.action.kind,\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n const outcome = await this.executor.execute(automation, run, controller.signal);\n status = outcome.status;\n error = outcome.error;\n this.appendRunEvent(\n run,\n status === 'succeeded' ? 'action.completed' : 'action.failed',\n status === 'succeeded'\n ? `${automation.action.kind} action completed`\n : `${automation.action.kind} action ${status}`,\n {\n summary: outcome.summary,\n error: outcome.error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n },\n );\n run = {\n ...run,\n status,\n summary: outcome.summary,\n error,\n sessionKey: outcome.sessionKey,\n workflowRunId: outcome.workflowRunId,\n model: outcome.model,\n };\n if ((automation.safety?.mode ?? 'auto_apply') !== 'auto_apply' && automation.afterRun?.kind === 'webhook') {\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook skipped by safety mode', {\n safety: automation.safety ?? { mode: 'auto_apply' },\n });\n } else if (automation.afterRun?.kind === 'webhook') {\n activePhase = 'after_run';\n this.appendRunEvent(run, 'after_run.started', 'Calling after-run webhook', {\n url: automation.afterRun.url,\n });\n await this.postAfterRunWebhook(automation.afterRun.url, run);\n this.appendRunEvent(run, 'after_run.completed', 'After-run webhook completed');\n }\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n status = controller.signal.aborted ? 'cancelled' : 'failed';\n this.appendRunEvent(run, activePhase === 'after_run' ? 'after_run.failed' : 'action.failed', `Automation run ${status}`, {\n error,\n });\n run = { ...run, status, error };\n } finally {\n const endedAtMs = Date.now();\n run = {\n ...run,\n status,\n endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n saveAutomationRun(run);\n this.appendRunEvent(run, 'run.completed', `Automation run ${status}`, {\n status,\n durationMs: run.durationMs,\n error,\n });\n this.activeRuns.delete(initialRun.id);\n this.finishAutomationRun(automation.id, status, error, endedAtMs);\n }\n }\n\n private appendRunEvent(\n run: Pick<AutomationRun, 'id' | 'automationId'>,\n type: AutomationRunEventType,\n message: string,\n data?: unknown,\n ): void {\n appendAutomationRunEvent({\n id: randomUUID(),\n runId: run.id,\n automationId: run.automationId,\n type,\n message,\n data,\n createdAtMs: Date.now(),\n });\n }\n\n private finishAutomationRun(\n automationId: string,\n status: AutomationRunStatus,\n error: string | undefined,\n finishedAtMs: number,\n ): void {\n const current = getAutomation(automationId);\n if (!current) return;\n const failed = status === 'failed' || status === 'timeout';\n const consecutiveFailures = failed ? (current.state.consecutiveFailures ?? 0) + 1 : 0;\n const disableAfter = current.reliability?.disableAfterConsecutiveFailures;\n const shouldDisable = disableAfter != null && consecutiveFailures >= disableAfter;\n const next: Automation = {\n ...current,\n enabled: shouldDisable ? false : current.enabled,\n state: {\n ...current.state,\n lastRunAtMs: finishedAtMs,\n lastRunStatus: status,\n lastError: error,\n consecutiveFailures,\n },\n updatedAtMs: shouldDisable ? finishedAtMs : current.updatedAtMs,\n };\n delete next.state.runningRunId;\n next.state.nextRunAtMs = computeNextAutomationRunAtMs(next, finishedAtMs);\n saveAutomation(next);\n this.armTimer();\n }\n\n private async postAfterRunWebhook(url: string, run: AutomationRun): Promise<void> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ run }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Automation webhook failed: HTTP ${response.status}${text ? ` ${text.slice(0, 200)}` : ''}`);\n }\n }\n}\n\nfunction matchesAutomationEvent(\n trigger: Extract<Automation['trigger'], { kind: 'event' }>,\n event: AutomationEvent,\n): boolean {\n if (trigger.eventType !== event.type) return false;\n if (trigger.source && trigger.source !== event.source) return false;\n const payloadMatch = trigger.payloadMatch;\n if (!payloadMatch) return true;\n const payload = event.payload ?? {};\n return Object.entries(payloadMatch).every(([key, expected]) => Object.is(payload[key], expected));\n}\n\nfunction readAutomationEventFromRunEvent(event: AutomationRunEvent): AutomationEvent | null {\n if (!event.data || typeof event.data !== 'object' || Array.isArray(event.data)) return null;\n const raw = (event.data as { event?: unknown }).event;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const candidate = raw as Partial<AutomationEvent>;\n if (typeof candidate.type !== 'string' || !candidate.type.trim()) return null;\n return {\n type: candidate.type,\n source: typeof candidate.source === 'string' ? candidate.source : undefined,\n payload: candidate.payload && typeof candidate.payload === 'object' && !Array.isArray(candidate.payload)\n ? candidate.payload as Record<string, unknown>\n : undefined,\n occurredAtMs: typeof candidate.occurredAtMs === 'number' ? candidate.occurredAtMs : undefined,\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import type { Automation } from '../domain/types.js';
2
- export declare function listAutomations(): Automation[];
2
+ export declare function listAutomations(options?: {
3
+ projectId?: string;
4
+ }): Automation[];
3
5
  export declare function getAutomation(automationId: string): Automation | null;
4
6
  export declare function saveAutomation(automation: Automation): void;
5
7
  export declare function saveAutomations(automations: Automation[]): void;
@@ -13,6 +13,7 @@ function rowToAutomation(row) {
13
13
  id: row.automation_id,
14
14
  name: row.name,
15
15
  description: row.description ?? void 0,
16
+ projectId: row.project_id ?? void 0,
16
17
  enabled: row.enabled !== 0,
17
18
  trigger: parseJson(row.trigger_json),
18
19
  action: parseJson(row.action_json),
@@ -26,17 +27,18 @@ function rowToAutomation(row) {
26
27
  }
27
28
  function upsertAutomation(db, automation) {
28
29
  db.prepare(`INSERT OR REPLACE INTO automations (
29
- automation_id, name, description, enabled, trigger_json, action_json,
30
+ automation_id, name, description, project_id, enabled, trigger_json, action_json,
30
31
  safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
31
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(automation.id, automation.name, automation.description ?? null, automation.enabled ? 1 : 0, JSON.stringify(automation.trigger), JSON.stringify(automation.action), bindJson(automation.safety ?? { mode: "auto_apply" }), bindJson(automation.afterRun), bindJson(automation.reliability), JSON.stringify(automation.state ?? {}), automation.createdAtMs, automation.updatedAtMs);
32
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(automation.id, automation.name, automation.description ?? null, automation.projectId ?? null, automation.enabled ? 1 : 0, JSON.stringify(automation.trigger), JSON.stringify(automation.action), bindJson(automation.safety ?? { mode: "auto_apply" }), bindJson(automation.afterRun), bindJson(automation.reliability), JSON.stringify(automation.state ?? {}), automation.createdAtMs, automation.updatedAtMs);
32
33
  }
33
34
  const AUTOMATION_SELECT = `
34
- SELECT automation_id, name, description, enabled, trigger_json, action_json,
35
+ SELECT automation_id, name, description, project_id, enabled, trigger_json, action_json,
35
36
  safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms
36
37
  FROM automations
37
38
  `;
38
- function listAutomations() {
39
- return getSqliteDatabase().prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all().map(rowToAutomation);
39
+ function listAutomations(options) {
40
+ const db = getSqliteDatabase();
41
+ return (options?.projectId ? db.prepare(`${AUTOMATION_SELECT} WHERE project_id = ? ORDER BY created_at_ms DESC`).all(options.projectId) : db.prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all()).map(rowToAutomation);
40
42
  }
41
43
  function getAutomation(automationId) {
42
44
  const row = getSqliteDatabase().prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`).get(automationId);
@@ -1 +1 @@
1
- {"version":3,"file":"automation-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-repository.ts"],"sourcesContent":["import type { Automation } from '../domain/types.js';\nimport { AutomationSchema } from '../domain/validation.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\ntype AutomationRow = {\n automation_id: string;\n name: string;\n description: string | null;\n enabled: number;\n trigger_json: string;\n action_json: string;\n safety_json: string | null;\n after_run_json: string | null;\n reliability_json: string | null;\n state_json: string;\n created_at_ms: number;\n updated_at_ms: number;\n};\n\nfunction parseJson(value: string | null): unknown {\n if (!value) return undefined;\n return JSON.parse(value);\n}\n\nfunction bindJson(value: unknown): string | null {\n return value === undefined ? null : JSON.stringify(value);\n}\n\nfunction rowToAutomation(row: AutomationRow): Automation {\n return AutomationSchema.parse({\n id: row.automation_id,\n name: row.name,\n description: row.description ?? undefined,\n enabled: row.enabled !== 0,\n trigger: parseJson(row.trigger_json),\n action: parseJson(row.action_json),\n safety: parseJson(row.safety_json) ?? { mode: 'auto_apply' },\n afterRun: parseJson(row.after_run_json),\n reliability: parseJson(row.reliability_json),\n state: parseJson(row.state_json) ?? {},\n createdAtMs: row.created_at_ms,\n updatedAtMs: row.updated_at_ms,\n }) as Automation;\n}\n\nfunction upsertAutomation(db: ReturnType<typeof getSqliteDatabase>, automation: Automation): void {\n db.prepare(\n `INSERT OR REPLACE INTO automations (\n automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n automation.id,\n automation.name,\n automation.description ?? null,\n automation.enabled ? 1 : 0,\n JSON.stringify(automation.trigger),\n JSON.stringify(automation.action),\n bindJson(automation.safety ?? { mode: 'auto_apply' }),\n bindJson(automation.afterRun),\n bindJson(automation.reliability),\n JSON.stringify(automation.state ?? {}),\n automation.createdAtMs,\n automation.updatedAtMs,\n );\n}\n\nconst AUTOMATION_SELECT = `\n SELECT automation_id, name, description, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n FROM automations\n`;\n\nexport function listAutomations(): Automation[] {\n const rows = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`)\n .all() as AutomationRow[];\n return rows.map(rowToAutomation);\n}\n\nexport function getAutomation(automationId: string): Automation | null {\n const row = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`)\n .get(automationId) as AutomationRow | undefined;\n return row ? rowToAutomation(row) : null;\n}\n\nexport function saveAutomation(automation: Automation): void {\n runSqliteWriteTransaction((db) => upsertAutomation(db, automation));\n}\n\nexport function saveAutomations(automations: Automation[]): void {\n runSqliteWriteTransaction((db) => {\n for (const automation of automations) {\n upsertAutomation(db, automation);\n }\n });\n}\n\nexport function deleteAutomation(automationId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const result = db.prepare(`DELETE FROM automations WHERE automation_id = ?`).run(automationId);\n return result.changes > 0;\n });\n}\n"],"mappings":";;;AAmBA,SAAS,UAAU,OAA+B;AAChD,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO,KAAK,MAAM,MAAM;;AAG1B,SAAS,SAAS,OAA+B;AAC/C,QAAO,UAAU,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM;;AAG3D,SAAS,gBAAgB,KAAgC;AACvD,QAAO,iBAAiB,MAAM;EAC5B,IAAI,IAAI;EACR,MAAM,IAAI;EACV,aAAa,IAAI,eAAe,KAAA;EAChC,SAAS,IAAI,YAAY;EACzB,SAAS,UAAU,IAAI,aAAa;EACpC,QAAQ,UAAU,IAAI,YAAY;EAClC,QAAQ,UAAU,IAAI,YAAY,IAAI,EAAE,MAAM,cAAc;EAC5D,UAAU,UAAU,IAAI,eAAe;EACvC,aAAa,UAAU,IAAI,iBAAiB;EAC5C,OAAO,UAAU,IAAI,WAAW,IAAI,EAAE;EACtC,aAAa,IAAI;EACjB,aAAa,IAAI;EAClB,CAAC;;AAGJ,SAAS,iBAAiB,IAA0C,YAA8B;AAChG,IAAG,QACD;;;mDAID,CAAC,IACA,WAAW,IACX,WAAW,MACX,WAAW,eAAe,MAC1B,WAAW,UAAU,IAAI,GACzB,KAAK,UAAU,WAAW,QAAQ,EAClC,KAAK,UAAU,WAAW,OAAO,EACjC,SAAS,WAAW,UAAU,EAAE,MAAM,cAAc,CAAC,EACrD,SAAS,WAAW,SAAS,EAC7B,SAAS,WAAW,YAAY,EAChC,KAAK,UAAU,WAAW,SAAS,EAAE,CAAC,EACtC,WAAW,aACX,WAAW,YACZ;;AAGH,MAAM,oBAAoB;;;;;AAM1B,SAAgB,kBAAgC;AAI9C,QAHa,mBAAmB,CAC7B,QAAQ,GAAG,kBAAkB,8BAA8B,CAC3D,KACQ,CAAC,IAAI,gBAAgB;;AAGlC,SAAgB,cAAc,cAAyC;CACrE,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,GAAG,kBAAkB,0BAA0B,CACvD,IAAI,aAAa;AACpB,QAAO,MAAM,gBAAgB,IAAI,GAAG;;AAGtC,SAAgB,eAAe,YAA8B;AAC3D,4BAA2B,OAAO,iBAAiB,IAAI,WAAW,CAAC;;AAGrE,SAAgB,gBAAgB,aAAiC;AAC/D,4BAA2B,OAAO;AAChC,OAAK,MAAM,cAAc,YACvB,kBAAiB,IAAI,WAAW;GAElC;;AAGJ,SAAgB,iBAAiB,cAA+B;AAC9D,QAAO,2BAA2B,OAAO;AAEvC,SADe,GAAG,QAAQ,kDAAkD,CAAC,IAAI,aACpE,CAAC,UAAU;GACxB"}
1
+ {"version":3,"file":"automation-repository.js","names":[],"sources":["../../../../src/automations/storage/automation-repository.ts"],"sourcesContent":["import type { Automation } from '../domain/types.js';\nimport { AutomationSchema } from '../domain/validation.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../../storage/sqlite/transaction.js';\n\ntype AutomationRow = {\n automation_id: string;\n name: string;\n description: string | null;\n project_id: string | null;\n enabled: number;\n trigger_json: string;\n action_json: string;\n safety_json: string | null;\n after_run_json: string | null;\n reliability_json: string | null;\n state_json: string;\n created_at_ms: number;\n updated_at_ms: number;\n};\n\nfunction parseJson(value: string | null): unknown {\n if (!value) return undefined;\n return JSON.parse(value);\n}\n\nfunction bindJson(value: unknown): string | null {\n return value === undefined ? null : JSON.stringify(value);\n}\n\nfunction rowToAutomation(row: AutomationRow): Automation {\n return AutomationSchema.parse({\n id: row.automation_id,\n name: row.name,\n description: row.description ?? undefined,\n projectId: row.project_id ?? undefined,\n enabled: row.enabled !== 0,\n trigger: parseJson(row.trigger_json),\n action: parseJson(row.action_json),\n safety: parseJson(row.safety_json) ?? { mode: 'auto_apply' },\n afterRun: parseJson(row.after_run_json),\n reliability: parseJson(row.reliability_json),\n state: parseJson(row.state_json) ?? {},\n createdAtMs: row.created_at_ms,\n updatedAtMs: row.updated_at_ms,\n }) as Automation;\n}\n\nfunction upsertAutomation(db: ReturnType<typeof getSqliteDatabase>, automation: Automation): void {\n db.prepare(\n `INSERT OR REPLACE INTO automations (\n automation_id, name, description, project_id, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n automation.id,\n automation.name,\n automation.description ?? null,\n automation.projectId ?? null,\n automation.enabled ? 1 : 0,\n JSON.stringify(automation.trigger),\n JSON.stringify(automation.action),\n bindJson(automation.safety ?? { mode: 'auto_apply' }),\n bindJson(automation.afterRun),\n bindJson(automation.reliability),\n JSON.stringify(automation.state ?? {}),\n automation.createdAtMs,\n automation.updatedAtMs,\n );\n}\n\nconst AUTOMATION_SELECT = `\n SELECT automation_id, name, description, project_id, enabled, trigger_json, action_json,\n safety_json, after_run_json, reliability_json, state_json, created_at_ms, updated_at_ms\n FROM automations\n`;\n\nexport function listAutomations(options?: { projectId?: string }): Automation[] {\n const db = getSqliteDatabase();\n const rows = options?.projectId\n ? db.prepare(`${AUTOMATION_SELECT} WHERE project_id = ? ORDER BY created_at_ms DESC`).all(options.projectId)\n : db.prepare(`${AUTOMATION_SELECT} ORDER BY created_at_ms DESC`).all();\n return (rows as AutomationRow[]).map(rowToAutomation);\n}\n\nexport function getAutomation(automationId: string): Automation | null {\n const row = getSqliteDatabase()\n .prepare(`${AUTOMATION_SELECT} WHERE automation_id = ?`)\n .get(automationId) as AutomationRow | undefined;\n return row ? rowToAutomation(row) : null;\n}\n\nexport function saveAutomation(automation: Automation): void {\n runSqliteWriteTransaction((db) => upsertAutomation(db, automation));\n}\n\nexport function saveAutomations(automations: Automation[]): void {\n runSqliteWriteTransaction((db) => {\n for (const automation of automations) {\n upsertAutomation(db, automation);\n }\n });\n}\n\nexport function deleteAutomation(automationId: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const result = db.prepare(`DELETE FROM automations WHERE automation_id = ?`).run(automationId);\n return result.changes > 0;\n });\n}\n"],"mappings":";;;AAoBA,SAAS,UAAU,OAA+B;AAChD,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO,KAAK,MAAM,MAAM;;AAG1B,SAAS,SAAS,OAA+B;AAC/C,QAAO,UAAU,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM;;AAG3D,SAAS,gBAAgB,KAAgC;AACvD,QAAO,iBAAiB,MAAM;EAC5B,IAAI,IAAI;EACR,MAAM,IAAI;EACV,aAAa,IAAI,eAAe,KAAA;EAChC,WAAW,IAAI,cAAc,KAAA;EAC7B,SAAS,IAAI,YAAY;EACzB,SAAS,UAAU,IAAI,aAAa;EACpC,QAAQ,UAAU,IAAI,YAAY;EAClC,QAAQ,UAAU,IAAI,YAAY,IAAI,EAAE,MAAM,cAAc;EAC5D,UAAU,UAAU,IAAI,eAAe;EACvC,aAAa,UAAU,IAAI,iBAAiB;EAC5C,OAAO,UAAU,IAAI,WAAW,IAAI,EAAE;EACtC,aAAa,IAAI;EACjB,aAAa,IAAI;EAClB,CAAC;;AAGJ,SAAS,iBAAiB,IAA0C,YAA8B;AAChG,IAAG,QACD;;;sDAID,CAAC,IACA,WAAW,IACX,WAAW,MACX,WAAW,eAAe,MAC1B,WAAW,aAAa,MACxB,WAAW,UAAU,IAAI,GACzB,KAAK,UAAU,WAAW,QAAQ,EAClC,KAAK,UAAU,WAAW,OAAO,EACjC,SAAS,WAAW,UAAU,EAAE,MAAM,cAAc,CAAC,EACrD,SAAS,WAAW,SAAS,EAC7B,SAAS,WAAW,YAAY,EAChC,KAAK,UAAU,WAAW,SAAS,EAAE,CAAC,EACtC,WAAW,aACX,WAAW,YACZ;;AAGH,MAAM,oBAAoB;;;;;AAM1B,SAAgB,gBAAgB,SAAgD;CAC9E,MAAM,KAAK,mBAAmB;AAI9B,SAHa,SAAS,YAClB,GAAG,QAAQ,GAAG,kBAAkB,mDAAmD,CAAC,IAAI,QAAQ,UAAU,GAC1G,GAAG,QAAQ,GAAG,kBAAkB,8BAA8B,CAAC,KAAK,EACvC,IAAI,gBAAgB;;AAGvD,SAAgB,cAAc,cAAyC;CACrE,MAAM,MAAM,mBAAmB,CAC5B,QAAQ,GAAG,kBAAkB,0BAA0B,CACvD,IAAI,aAAa;AACpB,QAAO,MAAM,gBAAgB,IAAI,GAAG;;AAGtC,SAAgB,eAAe,YAA8B;AAC3D,4BAA2B,OAAO,iBAAiB,IAAI,WAAW,CAAC;;AAGrE,SAAgB,gBAAgB,aAAiC;AAC/D,4BAA2B,OAAO;AAChC,OAAK,MAAM,cAAc,YACvB,kBAAiB,IAAI,WAAW;GAElC;;AAGJ,SAAgB,iBAAiB,cAA+B;AAC9D,QAAO,2BAA2B,OAAO;AAEvC,SADe,GAAG,QAAQ,kDAAkD,CAAC,IAAI,aACpE,CAAC,UAAU;GACxB"}
@@ -5,6 +5,7 @@ export declare function listAutomationRunEvents(runId: string): AutomationRunEve
5
5
  export declare function getAutomationRun(runId: string): AutomationRun | null;
6
6
  export declare function listAutomationRuns(options?: {
7
7
  automationId?: string;
8
+ projectId?: string;
8
9
  limit?: number;
9
10
  }): AutomationRun[];
10
11
  export declare function listAutomationRunsForProductEvent(options: {
@@ -81,7 +81,18 @@ function listAutomationRuns(options) {
81
81
  action_snapshot_json, manual, created_at_ms, started_at_ms, ended_at_ms,
82
82
  duration_ms, summary, error, session_key, workflow_run_id, model
83
83
  FROM automation_runs`;
84
- return (options?.automationId ? db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit) : db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit)).map(rowToRun);
84
+ let rows;
85
+ if (options?.automationId) rows = db.prepare(`${base} WHERE automation_id = ? ORDER BY created_at_ms DESC LIMIT ?`).all(options.automationId, limit);
86
+ else if (options?.projectId) rows = db.prepare(`SELECT r.run_id, r.automation_id, r.automation_name, r.status, r.trigger_snapshot_json,
87
+ r.action_snapshot_json, r.manual, r.created_at_ms, r.started_at_ms, r.ended_at_ms,
88
+ r.duration_ms, r.summary, r.error, r.session_key, r.workflow_run_id, r.model
89
+ FROM automation_runs r
90
+ JOIN automations a ON a.automation_id = r.automation_id
91
+ WHERE a.project_id = ?
92
+ ORDER BY r.created_at_ms DESC
93
+ LIMIT ?`).all(options.projectId, limit);
94
+ else rows = db.prepare(`${base} ORDER BY created_at_ms DESC LIMIT ?`).all(limit);
95
+ return rows.map(rowToRun);
85
96
  }
86
97
  function listAutomationRunsForProductEvent(options) {
87
98
  const limit = Math.min(Math.max(options.limit ?? 10, 1), 100);