@pikku/core 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/error-handler.js +3 -3
  5. package/dist/errors/errors.d.ts +8 -0
  6. package/dist/errors/errors.js +16 -0
  7. package/dist/errors/index.d.ts +0 -1
  8. package/dist/errors/index.js +0 -1
  9. package/dist/function/function-runner.d.ts +10 -3
  10. package/dist/function/function-runner.js +136 -31
  11. package/dist/function/functions.types.d.ts +35 -12
  12. package/dist/function/functions.types.js +16 -6
  13. package/dist/function/index.d.ts +2 -2
  14. package/dist/function/index.js +1 -2
  15. package/dist/handle-error.js +1 -10
  16. package/dist/index.d.ts +30 -24
  17. package/dist/index.js +19 -27
  18. package/dist/middleware/auth-apikey.d.ts +1 -1
  19. package/dist/middleware/auth-apikey.js +3 -3
  20. package/dist/middleware/auth-bearer.d.ts +1 -1
  21. package/dist/middleware/auth-bearer.js +3 -6
  22. package/dist/middleware/auth-cookie.d.ts +1 -1
  23. package/dist/middleware/auth-cookie.js +6 -8
  24. package/dist/middleware/cors.d.ts +46 -0
  25. package/dist/middleware/cors.js +73 -0
  26. package/dist/middleware/index.d.ts +5 -3
  27. package/dist/middleware/index.js +5 -3
  28. package/dist/middleware/remote-auth.d.ts +5 -0
  29. package/dist/middleware/remote-auth.js +55 -0
  30. package/dist/middleware/timeout.d.ts +1 -1
  31. package/dist/middleware-runner.d.ts +3 -14
  32. package/dist/middleware-runner.js +14 -16
  33. package/dist/permissions.d.ts +3 -86
  34. package/dist/permissions.js +20 -80
  35. package/dist/pikku-state.d.ts +33 -104
  36. package/dist/pikku-state.js +150 -66
  37. package/dist/schema.d.ts +8 -6
  38. package/dist/schema.js +31 -18
  39. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  40. package/dist/services/ai-agent-runner-service.js +1 -0
  41. package/dist/services/ai-run-state-service.d.ts +13 -0
  42. package/dist/services/ai-run-state-service.js +1 -0
  43. package/dist/services/ai-storage-service.d.ts +18 -0
  44. package/dist/services/ai-storage-service.js +1 -0
  45. package/dist/services/deployment-service.d.ts +19 -0
  46. package/dist/services/deployment-service.js +1 -0
  47. package/dist/services/gopass-secrets.d.ts +47 -0
  48. package/dist/services/gopass-secrets.js +94 -0
  49. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  50. package/dist/services/in-memory-trigger-service.js +50 -0
  51. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  52. package/dist/services/in-memory-workflow-service.js +287 -0
  53. package/dist/services/index.d.ts +25 -11
  54. package/dist/services/index.js +10 -13
  55. package/dist/services/local-secrets.d.ts +23 -0
  56. package/dist/services/local-secrets.js +45 -0
  57. package/dist/services/local-variables.d.ts +5 -0
  58. package/dist/services/local-variables.js +18 -0
  59. package/dist/services/scheduler-service.d.ts +21 -1
  60. package/dist/services/scheduler-service.js +14 -0
  61. package/dist/services/schema-service.d.ts +6 -0
  62. package/dist/services/scoped-secret-service.d.ts +16 -0
  63. package/dist/services/scoped-secret-service.js +35 -0
  64. package/dist/services/secret-service.d.ts +25 -4
  65. package/dist/services/trigger-service.d.ts +23 -0
  66. package/dist/services/trigger-service.js +1 -0
  67. package/dist/services/typed-secret-service.d.ts +30 -0
  68. package/dist/services/typed-secret-service.js +40 -0
  69. package/dist/services/typed-variables-service.d.ts +27 -0
  70. package/dist/services/typed-variables-service.js +46 -0
  71. package/dist/services/user-session-service.d.ts +13 -0
  72. package/dist/services/user-session-service.js +17 -0
  73. package/dist/services/variables-service.d.ts +5 -0
  74. package/dist/services/workflow-service.d.ts +43 -0
  75. package/dist/services/workflow-service.js +1 -0
  76. package/dist/types/core.types.d.ts +54 -10
  77. package/dist/types/core.types.js +7 -0
  78. package/dist/types/state.types.d.ts +127 -0
  79. package/dist/types/state.types.js +1 -0
  80. package/dist/utils.d.ts +10 -1
  81. package/dist/utils.js +60 -3
  82. package/dist/version.d.ts +6 -0
  83. package/dist/version.js +19 -0
  84. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  85. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  86. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  87. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  88. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  89. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  90. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  91. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  92. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  93. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  94. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  95. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  96. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  97. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  98. package/dist/wirings/ai-agent/index.d.ts +5 -0
  99. package/dist/wirings/ai-agent/index.js +4 -0
  100. package/dist/wirings/channel/channel-common.d.ts +7 -4
  101. package/dist/wirings/channel/channel-common.js +15 -8
  102. package/dist/wirings/channel/channel-handler.js +13 -4
  103. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  104. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  105. package/dist/wirings/channel/channel-runner.js +12 -12
  106. package/dist/wirings/channel/channel.types.d.ts +5 -1
  107. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  108. package/dist/wirings/channel/define-channel-routes.js +23 -0
  109. package/dist/wirings/channel/index.d.ts +10 -8
  110. package/dist/wirings/channel/index.js +7 -8
  111. package/dist/wirings/channel/local/index.d.ts +3 -3
  112. package/dist/wirings/channel/local/index.js +3 -3
  113. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  114. package/dist/wirings/channel/log-channels.js +1 -1
  115. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  116. package/dist/wirings/channel/serverless/index.js +1 -1
  117. package/dist/wirings/channel/serverless/serverless-channel-runner.js +17 -6
  118. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
  119. package/dist/wirings/cli/channel/index.d.ts +1 -1
  120. package/dist/wirings/cli/channel/index.js +1 -1
  121. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  122. package/dist/wirings/cli/cli-runner.js +22 -18
  123. package/dist/wirings/cli/cli.types.d.ts +7 -6
  124. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  125. package/dist/wirings/cli/define-cli-commands.js +20 -0
  126. package/dist/wirings/cli/index.d.ts +2 -3
  127. package/dist/wirings/cli/index.js +1 -3
  128. package/dist/wirings/http/http-routes.d.ts +69 -0
  129. package/dist/wirings/http/http-routes.js +139 -0
  130. package/dist/wirings/http/http-runner.d.ts +2 -2
  131. package/dist/wirings/http/http-runner.js +62 -27
  132. package/dist/wirings/http/http.types.d.ts +77 -24
  133. package/dist/wirings/http/index.d.ts +5 -4
  134. package/dist/wirings/http/index.js +4 -3
  135. package/dist/wirings/http/log-http-routes.js +1 -1
  136. package/dist/wirings/http/routers/path-to-regex.js +2 -2
  137. package/dist/wirings/mcp/index.d.ts +4 -3
  138. package/dist/wirings/mcp/index.js +3 -3
  139. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  140. package/dist/wirings/mcp/mcp-runner.js +42 -56
  141. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  142. package/dist/wirings/node/index.d.ts +2 -0
  143. package/dist/wirings/node/index.js +1 -0
  144. package/dist/wirings/node/node.types.d.ts +20 -0
  145. package/dist/wirings/node/node.types.js +1 -0
  146. package/dist/wirings/oauth2/index.d.ts +3 -0
  147. package/dist/wirings/oauth2/index.js +2 -0
  148. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  149. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  150. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  151. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  152. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  153. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  154. package/dist/wirings/queue/index.d.ts +2 -2
  155. package/dist/wirings/queue/index.js +1 -1
  156. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  157. package/dist/wirings/queue/queue-runner.js +23 -12
  158. package/dist/wirings/queue/queue.types.d.ts +8 -2
  159. package/dist/wirings/rpc/index.d.ts +1 -1
  160. package/dist/wirings/rpc/index.js +1 -1
  161. package/dist/wirings/rpc/rpc-runner.d.ts +40 -13
  162. package/dist/wirings/rpc/rpc-runner.js +191 -25
  163. package/dist/wirings/rpc/rpc-types.d.ts +15 -4
  164. package/dist/wirings/scheduler/index.d.ts +3 -3
  165. package/dist/wirings/scheduler/index.js +2 -3
  166. package/dist/wirings/scheduler/log-schedulers.js +1 -1
  167. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  168. package/dist/wirings/scheduler/scheduler-runner.js +28 -10
  169. package/dist/wirings/secret/index.d.ts +3 -0
  170. package/dist/wirings/secret/index.js +2 -0
  171. package/dist/wirings/secret/secret.types.d.ts +27 -0
  172. package/dist/wirings/secret/secret.types.js +1 -0
  173. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  174. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  175. package/dist/wirings/trigger/index.d.ts +3 -0
  176. package/dist/wirings/trigger/index.js +2 -0
  177. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  178. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  179. package/dist/wirings/trigger/trigger-runner.d.ts +42 -0
  180. package/dist/wirings/trigger/trigger-runner.js +85 -0
  181. package/dist/wirings/trigger/trigger.types.d.ts +130 -0
  182. package/dist/wirings/trigger/trigger.types.js +40 -0
  183. package/dist/wirings/variable/index.d.ts +3 -0
  184. package/dist/wirings/variable/index.js +2 -0
  185. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  186. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  187. package/dist/wirings/variable/variable.types.d.ts +18 -0
  188. package/dist/wirings/variable/variable.types.js +1 -0
  189. package/dist/wirings/workflow/dsl/index.d.ts +5 -0
  190. package/dist/wirings/workflow/dsl/index.js +4 -0
  191. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +292 -0
  192. package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
  193. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
  194. package/dist/wirings/workflow/dsl/workflow-runner.js +23 -0
  195. package/dist/wirings/workflow/graph/graph-node.d.ts +117 -0
  196. package/dist/wirings/workflow/graph/graph-node.js +58 -0
  197. package/dist/wirings/workflow/graph/graph-runner.d.ts +9 -0
  198. package/dist/wirings/workflow/graph/graph-runner.js +540 -0
  199. package/dist/wirings/workflow/graph/index.d.ts +4 -0
  200. package/dist/wirings/workflow/graph/index.js +3 -0
  201. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  202. package/dist/wirings/workflow/graph/template.js +27 -0
  203. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  204. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  205. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +82 -0
  206. package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
  207. package/dist/wirings/workflow/index.d.ts +7 -12
  208. package/dist/wirings/workflow/index.js +8 -11
  209. package/dist/wirings/workflow/pikku-workflow-service.d.ts +114 -10
  210. package/dist/wirings/workflow/pikku-workflow-service.js +386 -115
  211. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  212. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  213. package/dist/wirings/workflow/workflow-utils.d.ts +1 -0
  214. package/dist/wirings/workflow/workflow-utils.js +1 -0
  215. package/dist/wirings/workflow/workflow.types.d.ts +83 -214
  216. package/package.json +13 -6
  217. package/run-tests.sh +4 -1
  218. package/src/crypto-utils.ts +99 -0
  219. package/src/errors/error-handler.ts +3 -3
  220. package/src/errors/errors.ts +17 -0
  221. package/src/errors/index.ts +0 -1
  222. package/src/factory-functions.test.ts +42 -2
  223. package/src/function/function-runner.test.ts +5 -12
  224. package/src/function/function-runner.ts +195 -40
  225. package/src/function/functions.types.ts +66 -14
  226. package/src/function/index.ts +5 -2
  227. package/src/handle-error.ts +2 -10
  228. package/src/index.ts +106 -27
  229. package/src/middleware/auth-apikey.test.ts +15 -12
  230. package/src/middleware/auth-apikey.ts +3 -3
  231. package/src/middleware/auth-bearer.test.ts +17 -14
  232. package/src/middleware/auth-bearer.ts +25 -27
  233. package/src/middleware/auth-cookie.test.ts +16 -13
  234. package/src/middleware/auth-cookie.ts +6 -8
  235. package/src/middleware/cors.ts +96 -0
  236. package/src/middleware/index.ts +5 -3
  237. package/src/middleware/remote-auth.ts +68 -0
  238. package/src/middleware-runner.test.ts +122 -0
  239. package/src/middleware-runner.ts +24 -17
  240. package/src/permissions.test.ts +102 -86
  241. package/src/permissions.ts +32 -93
  242. package/src/pikku-state.ts +168 -201
  243. package/src/run-tests-script.test.ts +49 -0
  244. package/src/schema.ts +50 -18
  245. package/src/services/ai-agent-runner-service.ts +29 -0
  246. package/src/services/ai-run-state-service.ts +20 -0
  247. package/src/services/ai-storage-service.ts +39 -0
  248. package/src/services/deployment-service.ts +22 -0
  249. package/src/services/gopass-secrets.ts +95 -0
  250. package/src/services/in-memory-trigger-service.ts +68 -0
  251. package/src/services/in-memory-workflow-service.ts +395 -0
  252. package/src/services/index.ts +49 -14
  253. package/src/services/local-secrets.ts +51 -0
  254. package/src/services/local-variables.ts +22 -0
  255. package/src/services/scheduler-service.ts +29 -1
  256. package/src/services/schema-service.ts +7 -0
  257. package/src/services/scoped-secret-service.ts +41 -0
  258. package/src/services/secret-service.ts +25 -4
  259. package/src/services/trigger-service.ts +38 -0
  260. package/src/services/typed-secret-service.ts +70 -0
  261. package/src/services/typed-variables-service.ts +81 -0
  262. package/src/services/user-session-service.ts +23 -0
  263. package/src/services/variables-service.ts +5 -0
  264. package/src/services/workflow-service.ts +94 -0
  265. package/src/types/core.types.ts +82 -8
  266. package/src/types/state.types.ts +169 -0
  267. package/src/utils.ts +74 -5
  268. package/src/version.test.ts +80 -0
  269. package/src/version.ts +25 -0
  270. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  271. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  272. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  273. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  274. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  275. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  276. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  277. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  278. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  279. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  280. package/src/wirings/ai-agent/index.ts +28 -0
  281. package/src/wirings/channel/channel-common.ts +32 -19
  282. package/src/wirings/channel/channel-handler.ts +18 -5
  283. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  284. package/src/wirings/channel/channel-runner.ts +14 -15
  285. package/src/wirings/channel/channel.types.ts +17 -1
  286. package/src/wirings/channel/define-channel-routes.ts +25 -0
  287. package/src/wirings/channel/index.ts +23 -8
  288. package/src/wirings/channel/local/index.ts +3 -3
  289. package/src/wirings/channel/local/local-channel-runner.test.ts +47 -2
  290. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  291. package/src/wirings/channel/log-channels.ts +1 -1
  292. package/src/wirings/channel/serverless/index.ts +5 -1
  293. package/src/wirings/channel/serverless/serverless-channel-runner.ts +22 -6
  294. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
  295. package/src/wirings/cli/channel/index.ts +1 -1
  296. package/src/wirings/cli/cli-runner.test.ts +28 -28
  297. package/src/wirings/cli/cli-runner.ts +27 -19
  298. package/src/wirings/cli/cli.types.ts +12 -11
  299. package/src/wirings/cli/command-parser.test.ts +8 -8
  300. package/src/wirings/cli/define-cli-commands.ts +24 -0
  301. package/src/wirings/cli/index.ts +10 -4
  302. package/src/wirings/http/http-routes.test.ts +322 -0
  303. package/src/wirings/http/http-routes.ts +198 -0
  304. package/src/wirings/http/http-runner.test.ts +5 -5
  305. package/src/wirings/http/http-runner.ts +80 -29
  306. package/src/wirings/http/http.types.ts +121 -23
  307. package/src/wirings/http/index.ts +16 -4
  308. package/src/wirings/http/log-http-routes.ts +1 -1
  309. package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
  310. package/src/wirings/http/routers/path-to-regex.ts +2 -2
  311. package/src/wirings/mcp/index.ts +27 -3
  312. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  313. package/src/wirings/mcp/mcp-runner.ts +56 -67
  314. package/src/wirings/mcp/mcp.types.ts +3 -3
  315. package/src/wirings/node/index.ts +2 -0
  316. package/src/wirings/node/node.types.ts +23 -0
  317. package/src/wirings/oauth2/index.ts +3 -0
  318. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  319. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  320. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  321. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  322. package/src/wirings/queue/index.ts +14 -2
  323. package/src/wirings/queue/queue-runner.test.ts +115 -73
  324. package/src/wirings/queue/queue-runner.ts +39 -13
  325. package/src/wirings/queue/queue.types.ts +12 -2
  326. package/src/wirings/rpc/index.ts +1 -1
  327. package/src/wirings/rpc/rpc-runner.ts +270 -34
  328. package/src/wirings/rpc/rpc-types.ts +21 -4
  329. package/src/wirings/scheduler/index.ts +7 -4
  330. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  331. package/src/wirings/scheduler/scheduler-runner.test.ts +97 -53
  332. package/src/wirings/scheduler/scheduler-runner.ts +50 -10
  333. package/src/wirings/secret/index.ts +9 -0
  334. package/src/wirings/secret/secret.types.ts +32 -0
  335. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  336. package/src/wirings/trigger/index.ts +10 -0
  337. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  338. package/src/wirings/trigger/trigger-runner.ts +138 -0
  339. package/src/wirings/trigger/trigger.types.ts +178 -0
  340. package/src/wirings/variable/index.ts +8 -0
  341. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  342. package/src/wirings/variable/variable.types.ts +22 -0
  343. package/src/wirings/workflow/dsl/index.ts +31 -0
  344. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
  345. package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
  346. package/src/wirings/workflow/graph/graph-node.ts +222 -0
  347. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  348. package/src/wirings/workflow/graph/graph-runner.ts +816 -0
  349. package/src/wirings/workflow/graph/index.ts +14 -0
  350. package/src/wirings/workflow/graph/template.ts +42 -0
  351. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  352. package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
  353. package/src/wirings/workflow/index.ts +64 -26
  354. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  355. package/src/wirings/workflow/pikku-workflow-service.ts +571 -136
  356. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  357. package/src/wirings/workflow/workflow-utils.ts +0 -0
  358. package/src/wirings/workflow/workflow.types.ts +123 -279
  359. package/tsconfig.tsbuildinfo +1 -1
  360. package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
  361. package/dist/wirings/workflow/workflow-runner.js +0 -65
  362. package/src/function/function-runner.ts.bak +0 -188
  363. package/src/wirings/workflow/workflow-runner.ts +0 -72
@@ -0,0 +1,816 @@
1
+ import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
2
+ import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
3
+ import { pikkuState } from '../../../pikku-state.js'
4
+ import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
5
+ import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
6
+
7
+ function buildTemplateRegex(nodeId: string): RegExp | null {
8
+ if (!nodeId.includes('${')) return null
9
+ const escaped = nodeId
10
+ .split(/\$\{[^}]+\}/)
11
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
12
+ .join('.+')
13
+ return new RegExp(`^${escaped}$`)
14
+ }
15
+
16
+ function remapStepNamesToNodeIds(
17
+ stepNames: string[],
18
+ nodes: Record<string, any>,
19
+ graphName: string
20
+ ): string[] {
21
+ const templatePatterns = new Map<string, RegExp>()
22
+ for (const nodeId of Object.keys(nodes)) {
23
+ const regex = buildTemplateRegex(nodeId)
24
+ if (regex) templatePatterns.set(nodeId, regex)
25
+ }
26
+ if (templatePatterns.size === 0) return stepNames
27
+ return stepNames.map((name) => {
28
+ if (nodes[name]) return name
29
+ const matches: string[] = []
30
+ for (const [nodeId, regex] of templatePatterns) {
31
+ if (regex.test(name)) matches.push(nodeId)
32
+ }
33
+ if (matches.length > 1) {
34
+ throw new Error(
35
+ `Workflow graph '${graphName}': ambiguous template node match for '${name}' (${matches.join(', ')})`
36
+ )
37
+ }
38
+ if (matches.length === 1) {
39
+ return matches[0]!
40
+ }
41
+ return name
42
+ })
43
+ }
44
+
45
+ function remapBranchKeys(
46
+ branchKeys: Record<string, string>,
47
+ nodes: Record<string, any>,
48
+ graphName: string
49
+ ): Record<string, string> {
50
+ const templatePatterns = new Map<string, RegExp>()
51
+ for (const nodeId of Object.keys(nodes)) {
52
+ const regex = buildTemplateRegex(nodeId)
53
+ if (regex) templatePatterns.set(nodeId, regex)
54
+ }
55
+ if (templatePatterns.size === 0) return branchKeys
56
+ const remapped: Record<string, string> = {}
57
+ for (const [key, value] of Object.entries(branchKeys)) {
58
+ let mappedKey = key
59
+ if (!nodes[key]) {
60
+ const matches: string[] = []
61
+ for (const [nodeId, regex] of templatePatterns) {
62
+ if (regex.test(key)) matches.push(nodeId)
63
+ }
64
+ if (matches.length > 1) {
65
+ throw new Error(
66
+ `Workflow graph '${graphName}': ambiguous template branch key match for '${key}' (${matches.join(', ')})`
67
+ )
68
+ }
69
+ if (matches.length === 1) {
70
+ mappedKey = matches[0]!
71
+ }
72
+ }
73
+ remapped[mappedKey] = value
74
+ }
75
+ return remapped
76
+ }
77
+
78
+ function isDataRef(value: unknown): value is { $ref: string; path?: string } {
79
+ return (
80
+ typeof value === 'object' &&
81
+ value !== null &&
82
+ '$ref' in value &&
83
+ typeof (value as any).$ref === 'string'
84
+ )
85
+ }
86
+
87
+ interface TemplateValue {
88
+ $template: {
89
+ parts: string[]
90
+ expressions: Array<{ $ref: string; path?: string }>
91
+ }
92
+ }
93
+
94
+ function isTemplate(value: unknown): value is TemplateValue {
95
+ return (
96
+ typeof value === 'object' &&
97
+ value !== null &&
98
+ '$template' in value &&
99
+ typeof (value as any).$template === 'object'
100
+ )
101
+ }
102
+
103
+ function getWorkflowMeta(name: string): WorkflowRuntimeMeta | undefined {
104
+ const meta = pikkuState(null, 'workflows', 'meta')
105
+ return meta[name]
106
+ }
107
+
108
+ function resolveNextFromConfig(next: unknown, branchKey?: string): string[] {
109
+ if (!next) return []
110
+
111
+ if (typeof next === 'string') return [next]
112
+ if (Array.isArray(next)) return next
113
+
114
+ if (typeof next === 'object' && next !== null) {
115
+ if (!branchKey || !(branchKey in next)) return []
116
+ const branchNext = (next as Record<string, string | string[]>)[branchKey]!
117
+ return Array.isArray(branchNext) ? branchNext : [branchNext]
118
+ }
119
+
120
+ return []
121
+ }
122
+
123
+ function getValueAtPath(obj: any, path: string): any {
124
+ if (!path) return obj
125
+ const parts = path.split('.')
126
+ let current = obj
127
+ for (const part of parts) {
128
+ if (current == null) return undefined
129
+ current = current[part]
130
+ }
131
+ return current
132
+ }
133
+
134
+ function resolveTemplate(
135
+ template: TemplateValue,
136
+ nodeResults: Record<string, any>
137
+ ): string {
138
+ const { parts, expressions } = template.$template
139
+ let result = ''
140
+ for (let i = 0; i < parts.length; i++) {
141
+ result += parts[i]
142
+ if (i < expressions.length) {
143
+ const expr = expressions[i]!
144
+ const nodeResult = nodeResults[expr.$ref]
145
+ const value = expr.path
146
+ ? getValueAtPath(nodeResult, expr.path)
147
+ : nodeResult
148
+ result += String(value ?? '')
149
+ }
150
+ }
151
+ return result
152
+ }
153
+
154
+ function resolveValue(value: unknown, nodeResults: Record<string, any>): any {
155
+ if (isDataRef(value)) {
156
+ const source = nodeResults[value.$ref]
157
+ return value.path ? getValueAtPath(source, value.path) : source
158
+ }
159
+ if (isTemplate(value)) {
160
+ return resolveTemplate(value, nodeResults)
161
+ }
162
+ if (Array.isArray(value)) {
163
+ return value.map((item) => resolveValue(item, nodeResults))
164
+ }
165
+ if (typeof value === 'object' && value !== null) {
166
+ const resolved: Record<string, any> = {}
167
+ for (const [k, v] of Object.entries(value)) {
168
+ resolved[k] = resolveValue(v, nodeResults)
169
+ }
170
+ return resolved
171
+ }
172
+ return value
173
+ }
174
+
175
+ function resolveSerializedInput(
176
+ input: Record<string, unknown> | undefined,
177
+ nodeResults: Record<string, any>
178
+ ): Record<string, any> {
179
+ if (!input || Object.keys(input).length === 0) return {}
180
+
181
+ const resolved: Record<string, any> = {}
182
+ for (const [key, value] of Object.entries(input)) {
183
+ resolved[key] = resolveValue(value, nodeResults)
184
+ }
185
+ return resolved
186
+ }
187
+
188
+ function collectReferencedNodeIds(value: unknown, nodeIds: string[]): void {
189
+ if (isDataRef(value)) {
190
+ nodeIds.push(value.$ref)
191
+ } else if (isTemplate(value)) {
192
+ for (const expr of value.$template.expressions) {
193
+ nodeIds.push(expr.$ref)
194
+ }
195
+ } else if (Array.isArray(value)) {
196
+ for (const item of value) {
197
+ collectReferencedNodeIds(item, nodeIds)
198
+ }
199
+ } else if (typeof value === 'object' && value !== null) {
200
+ for (const v of Object.values(value)) {
201
+ collectReferencedNodeIds(v, nodeIds)
202
+ }
203
+ }
204
+ }
205
+
206
+ function extractReferencedNodeIds(
207
+ input: Record<string, unknown> | undefined
208
+ ): string[] {
209
+ if (!input) return []
210
+ const nodeIds: string[] = []
211
+ for (const value of Object.values(input)) {
212
+ collectReferencedNodeIds(value, nodeIds)
213
+ }
214
+ return [...new Set(nodeIds)]
215
+ }
216
+
217
+ const IGNORED_REFS = new Set(['trigger', '$item', 'unknown'])
218
+
219
+ function normalizeNodeTargets(value: unknown): string[] {
220
+ if (!value) return []
221
+ if (typeof value === 'string') return [value]
222
+ if (Array.isArray(value)) return value.filter((v) => typeof v === 'string')
223
+ if (typeof value === 'object') {
224
+ const targets: string[] = []
225
+ for (const branchTarget of Object.values(
226
+ value as Record<string, unknown>
227
+ )) {
228
+ targets.push(...normalizeNodeTargets(branchTarget))
229
+ }
230
+ return targets
231
+ }
232
+ return []
233
+ }
234
+
235
+ function validateGraphReferences(
236
+ graphName: string,
237
+ nodes: Record<string, any>,
238
+ entryNodes: string[]
239
+ ): void {
240
+ const nodeIds = new Set(Object.keys(nodes))
241
+
242
+ for (const entryId of entryNodes) {
243
+ if (!nodeIds.has(entryId)) {
244
+ throw new Error(
245
+ `Workflow graph '${graphName}': entry node '${entryId}' is not defined`
246
+ )
247
+ }
248
+ }
249
+
250
+ for (const [nodeId, node] of Object.entries(nodes)) {
251
+ const inputRefs = extractReferencedNodeIds(node.input).filter(
252
+ (id) => !IGNORED_REFS.has(id)
253
+ )
254
+ for (const refId of inputRefs) {
255
+ if (!nodeIds.has(refId)) {
256
+ throw new Error(
257
+ `Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`
258
+ )
259
+ }
260
+ }
261
+
262
+ const nextTargets = normalizeNodeTargets(node.next)
263
+ for (const nextId of nextTargets) {
264
+ if (!nodeIds.has(nextId)) {
265
+ throw new Error(
266
+ `Workflow graph '${graphName}': node '${nodeId}' routes to unknown node '${nextId}'`
267
+ )
268
+ }
269
+ }
270
+
271
+ const errorTargets = normalizeNodeTargets(node.onError)
272
+ for (const errorId of errorTargets) {
273
+ if (!nodeIds.has(errorId)) {
274
+ throw new Error(
275
+ `Workflow graph '${graphName}': node '${nodeId}' onError targets unknown node '${errorId}'`
276
+ )
277
+ }
278
+ }
279
+ }
280
+ }
281
+
282
+ function areDependenciesSatisfied(
283
+ node: { input?: Record<string, unknown> },
284
+ completedNodeIds: Set<string>
285
+ ): boolean {
286
+ const deps = extractReferencedNodeIds(node.input).filter(
287
+ (id) => !IGNORED_REFS.has(id)
288
+ )
289
+ return deps.every((dep) => completedNodeIds.has(dep))
290
+ }
291
+
292
+ async function queueGraphNode(
293
+ workflowService: PikkuWorkflowService,
294
+ runId: string,
295
+ _graphName: string,
296
+ nodeId: string,
297
+ rpcName: string,
298
+ input: any
299
+ ): Promise<void> {
300
+ await workflowService.insertStepState(runId, nodeId, rpcName, input, {
301
+ retries: 0,
302
+ })
303
+ await workflowService.queueStepWorker(runId, nodeId, rpcName, input)
304
+ }
305
+
306
+ export async function continueGraph(
307
+ workflowService: PikkuWorkflowService,
308
+ runId: string,
309
+ graphName: string,
310
+ overrideMeta?: WorkflowRuntimeMeta
311
+ ): Promise<void> {
312
+ const meta = overrideMeta ?? getWorkflowMeta(graphName)
313
+ if (!meta?.nodes) {
314
+ throw new Error(`Workflow graph meta '${graphName}' not found`)
315
+ }
316
+
317
+ const nodes = meta.nodes
318
+ validateGraphReferences(graphName, nodes, meta.entryNodeIds ?? [])
319
+
320
+ const {
321
+ completedNodeIds: rawCompleted,
322
+ failedNodeIds: rawFailed,
323
+ branchKeys: rawBranch,
324
+ } = await workflowService.getCompletedGraphState(runId)
325
+ const completedNodeIds = remapStepNamesToNodeIds(
326
+ rawCompleted,
327
+ nodes,
328
+ graphName
329
+ )
330
+ const completedNodeIdSet = new Set(completedNodeIds)
331
+ const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
332
+ const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
333
+
334
+ if (failedNodeIds.length > 0) {
335
+ const failedNode = failedNodeIds[0]!
336
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
337
+ message: `Graph node '${failedNode}' failed after exhausting retries`,
338
+ stack: '',
339
+ code: 'GRAPH_NODE_FAILED',
340
+ })
341
+ return
342
+ }
343
+
344
+ const currentRun = await workflowService.getRun(runId)
345
+ if (currentRun?.status === 'suspended') {
346
+ return
347
+ }
348
+
349
+ const candidateNodes = new Set<string>()
350
+
351
+ for (const nodeId of completedNodeIds) {
352
+ const node = nodes[nodeId]
353
+ if (!node?.next) continue
354
+
355
+ const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
356
+ for (const nextNode of nextNodes) {
357
+ candidateNodes.add(nextNode)
358
+ }
359
+ }
360
+
361
+ for (const entryId of meta.entryNodeIds ?? []) {
362
+ candidateNodes.add(entryId)
363
+ }
364
+
365
+ if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
366
+ await workflowService.updateRunStatus(runId, 'completed')
367
+ return
368
+ }
369
+
370
+ const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
371
+ ...candidateNodes,
372
+ ])
373
+
374
+ const nodesToQueue = unstartedNodes.filter((nodeId) => {
375
+ const node = nodes[nodeId]
376
+ return node && areDependenciesSatisfied(node, completedNodeIdSet)
377
+ })
378
+
379
+ if (nodesToQueue.length === 0) {
380
+ const allRpcNodes = Object.entries(nodes)
381
+ .filter(([_, n]) => n.rpcName)
382
+ .map(([id]) => id)
383
+ const allRpcCompleted = allRpcNodes.every((id) =>
384
+ completedNodeIdSet.has(id)
385
+ )
386
+ if (allRpcCompleted) {
387
+ await workflowService.updateRunStatus(runId, 'completed')
388
+ }
389
+ return
390
+ }
391
+
392
+ const run = await workflowService.getRun(runId)
393
+ const triggerInput = run?.input
394
+
395
+ for (const nodeId of nodesToQueue) {
396
+ const node = nodes[nodeId]
397
+ if (!node?.rpcName) continue
398
+
399
+ const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
400
+ (id) => !IGNORED_REFS.has(id)
401
+ )
402
+ const fetchedResults = await workflowService.getNodeResults(
403
+ runId,
404
+ referencedNodeIds
405
+ )
406
+
407
+ const nodeResults = { trigger: triggerInput, ...fetchedResults }
408
+ const resolvedInput = resolveSerializedInput(node.input, nodeResults)
409
+
410
+ await queueGraphNode(
411
+ workflowService,
412
+ runId,
413
+ graphName,
414
+ nodeId,
415
+ node.rpcName,
416
+ resolvedInput
417
+ )
418
+ }
419
+ }
420
+
421
+ export async function executeGraphStep(
422
+ workflowService: PikkuWorkflowService,
423
+ rpcService: any,
424
+ runId: string,
425
+ stepId: string,
426
+ nodeId: string,
427
+ rpcName: string,
428
+ data: any,
429
+ graphName: string
430
+ ): Promise<any> {
431
+ const wireState: GraphWireState = {}
432
+ const graphWire: PikkuGraphWire = {
433
+ runId,
434
+ graphName,
435
+ nodeId,
436
+ branch: (key: string) => {
437
+ wireState.branchKey = key
438
+ },
439
+ setState: (name: string, value: unknown) =>
440
+ workflowService.updateRunState(runId, name, value),
441
+ getState: () => workflowService.getRunState(runId),
442
+ }
443
+
444
+ try {
445
+ const result = await rpcService.rpcWithWire(rpcName, data, {
446
+ graph: graphWire,
447
+ })
448
+
449
+ if (wireState.branchKey) {
450
+ await workflowService.setBranchTaken(stepId, wireState.branchKey)
451
+ }
452
+
453
+ return result
454
+ } catch (error) {
455
+ if (error instanceof RPCNotFoundError) {
456
+ await workflowService.updateRunStatus(runId, 'suspended', undefined, {
457
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
458
+ code: 'RPC_NOT_FOUND',
459
+ })
460
+ throw error
461
+ }
462
+ const meta = getWorkflowMeta(graphName)
463
+ if (meta?.nodes) {
464
+ const node = meta.nodes[nodeId]
465
+ if (node?.onError) {
466
+ const errorNodes = Array.isArray(node.onError)
467
+ ? node.onError
468
+ : [node.onError]
469
+ for (const errorNodeId of errorNodes) {
470
+ const errorNode = meta.nodes[errorNodeId]
471
+ if (errorNode) {
472
+ await queueGraphNode(
473
+ workflowService,
474
+ runId,
475
+ graphName,
476
+ errorNodeId,
477
+ errorNode.rpcName,
478
+ { error: { message: (error as Error).message } }
479
+ )
480
+ }
481
+ }
482
+ throw error
483
+ }
484
+ }
485
+ throw error
486
+ }
487
+ }
488
+
489
+ export async function onGraphNodeComplete(
490
+ workflowService: PikkuWorkflowService,
491
+ runId: string,
492
+ graphName: string
493
+ ): Promise<void> {
494
+ await continueGraph(workflowService, runId, graphName)
495
+ }
496
+
497
+ export async function runFromMeta(
498
+ workflowService: PikkuWorkflowService,
499
+ runId: string,
500
+ meta: WorkflowRuntimeMeta,
501
+ _rpcService: any
502
+ ): Promise<void> {
503
+ await continueGraph(workflowService, runId, meta.name, meta)
504
+ }
505
+
506
+ async function executeGraphNodeInline(
507
+ workflowService: PikkuWorkflowService,
508
+ rpcService: any,
509
+ runId: string,
510
+ graphName: string,
511
+ nodeId: string,
512
+ input: any,
513
+ nodes: Record<string, any>
514
+ ): Promise<void> {
515
+ const node = nodes[nodeId]
516
+ if (!node) return
517
+
518
+ const rpcName = node.rpcName
519
+
520
+ const stepState = await workflowService.insertStepState(
521
+ runId,
522
+ nodeId,
523
+ rpcName,
524
+ input,
525
+ { retries: 3 }
526
+ )
527
+
528
+ await workflowService.setStepRunning(stepState.stepId)
529
+
530
+ const wireState: GraphWireState = {}
531
+ const graphWire: PikkuGraphWire = {
532
+ runId,
533
+ graphName,
534
+ nodeId,
535
+ branch: (key: string) => {
536
+ wireState.branchKey = key
537
+ },
538
+ setState: (name: string, value: unknown) =>
539
+ workflowService.updateRunState(runId, name, value),
540
+ getState: () => workflowService.getRunState(runId),
541
+ }
542
+
543
+ try {
544
+ const result = await rpcService.rpcWithWire(rpcName, input, {
545
+ graph: graphWire,
546
+ })
547
+
548
+ if (wireState.branchKey) {
549
+ await workflowService.setBranchTaken(
550
+ stepState.stepId,
551
+ wireState.branchKey
552
+ )
553
+ }
554
+
555
+ await workflowService.setStepResult(stepState.stepId, result)
556
+ } catch (error) {
557
+ if (error instanceof RPCNotFoundError) {
558
+ await workflowService.setStepError(stepState.stepId, error as Error)
559
+ await workflowService.updateRunStatus(runId, 'suspended', undefined, {
560
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
561
+ code: 'RPC_NOT_FOUND',
562
+ })
563
+ return
564
+ }
565
+ await workflowService.setStepError(stepState.stepId, error as Error)
566
+
567
+ if (node?.onError) {
568
+ const errorNodes = Array.isArray(node.onError)
569
+ ? node.onError
570
+ : [node.onError]
571
+ await Promise.all(
572
+ errorNodes.map((errorNodeId: string) =>
573
+ executeGraphNodeInline(
574
+ workflowService,
575
+ rpcService,
576
+ runId,
577
+ graphName,
578
+ errorNodeId,
579
+ { error: { message: (error as Error).message } },
580
+ nodes
581
+ )
582
+ )
583
+ )
584
+ return
585
+ }
586
+ throw error
587
+ }
588
+ }
589
+
590
+ async function continueGraphInline(
591
+ workflowService: PikkuWorkflowService,
592
+ rpcService: any,
593
+ runId: string,
594
+ graphName: string,
595
+ nodes: Record<string, any>,
596
+ triggerInput: any,
597
+ entryNodeIds: string[]
598
+ ): Promise<void> {
599
+ while (true) {
600
+ const {
601
+ completedNodeIds: rawCompleted,
602
+ failedNodeIds: rawFailed,
603
+ branchKeys: rawBranch,
604
+ } = await workflowService.getCompletedGraphState(runId)
605
+ const completedNodeIds = remapStepNamesToNodeIds(
606
+ rawCompleted,
607
+ nodes,
608
+ graphName
609
+ )
610
+ const completedNodeIdSet = new Set(completedNodeIds)
611
+ const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
612
+ const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
613
+
614
+ if (failedNodeIds.length > 0) {
615
+ const failedNode = failedNodeIds[0]!
616
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
617
+ message: `Graph node '${failedNode}' failed after exhausting retries`,
618
+ stack: '',
619
+ code: 'GRAPH_NODE_FAILED',
620
+ })
621
+ return
622
+ }
623
+
624
+ const candidateNodes = new Set<string>()
625
+
626
+ for (const nodeId of completedNodeIds) {
627
+ const node = nodes[nodeId]
628
+ if (!node?.next) continue
629
+
630
+ const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
631
+ for (const nextNode of nextNodes) {
632
+ candidateNodes.add(nextNode)
633
+ }
634
+ }
635
+
636
+ for (const entryId of entryNodeIds) {
637
+ candidateNodes.add(entryId)
638
+ }
639
+
640
+ if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
641
+ await workflowService.updateRunStatus(runId, 'completed')
642
+ return
643
+ }
644
+
645
+ const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
646
+ ...candidateNodes,
647
+ ])
648
+
649
+ const nodesToExecute = unstartedNodes.filter((nodeId) => {
650
+ const node = nodes[nodeId]
651
+ return node && areDependenciesSatisfied(node, completedNodeIdSet)
652
+ })
653
+
654
+ if (nodesToExecute.length === 0) {
655
+ if (completedNodeIds.length > 0 && unstartedNodes.length === 0) {
656
+ await workflowService.updateRunStatus(runId, 'completed')
657
+ }
658
+ return
659
+ }
660
+
661
+ await Promise.all(
662
+ nodesToExecute.map(async (nodeId) => {
663
+ const node = nodes[nodeId]
664
+ if (!node?.rpcName) return
665
+
666
+ const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
667
+ (id) => !IGNORED_REFS.has(id)
668
+ )
669
+ const fetchedResults = await workflowService.getNodeResults(
670
+ runId,
671
+ referencedNodeIds
672
+ )
673
+
674
+ const nodeResults = { trigger: triggerInput, ...fetchedResults }
675
+ const resolvedInput = resolveSerializedInput(node.input, nodeResults)
676
+
677
+ await executeGraphNodeInline(
678
+ workflowService,
679
+ rpcService,
680
+ runId,
681
+ graphName,
682
+ nodeId,
683
+ resolvedInput,
684
+ nodes
685
+ )
686
+ })
687
+ )
688
+ }
689
+ }
690
+
691
+ export async function runWorkflowGraph(
692
+ workflowService: PikkuWorkflowService,
693
+ graphName: string,
694
+ triggerInput: any,
695
+ rpcService?: any,
696
+ inline?: boolean,
697
+ startNode?: string,
698
+ wire?: WorkflowRunWire
699
+ ): Promise<{ runId: string }> {
700
+ const meta = getWorkflowMeta(graphName)
701
+ if (!meta?.nodes) {
702
+ throw new Error(`Workflow graph '${graphName}' not found`)
703
+ }
704
+
705
+ const nodes = meta.nodes
706
+ const entryNodes: string[] = startNode
707
+ ? [startNode]
708
+ : (meta.entryNodeIds ?? [])
709
+ validateGraphReferences(graphName, nodes, entryNodes)
710
+
711
+ if (entryNodes.length === 0) {
712
+ throw new Error(
713
+ `Workflow graph '${graphName}': no entry nodes found in meta or startNode`
714
+ )
715
+ }
716
+
717
+ const readyEntryNodes = entryNodes.filter((nodeId) => {
718
+ const node = nodes[nodeId]
719
+ return node && areDependenciesSatisfied(node, new Set())
720
+ })
721
+
722
+ if (readyEntryNodes.length === 0) {
723
+ throw new Error(
724
+ `Workflow graph '${graphName}': no entry nodes have satisfied dependencies`
725
+ )
726
+ }
727
+
728
+ if (!meta.graphHash) {
729
+ throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`)
730
+ }
731
+
732
+ const runId = await workflowService.createRun(
733
+ graphName,
734
+ triggerInput,
735
+ inline ?? false,
736
+ meta.graphHash,
737
+ wire ?? { type: 'unknown' }
738
+ )
739
+
740
+ if (inline) {
741
+ workflowService.registerInlineRun(runId)
742
+ }
743
+
744
+ const triggerNodeResults = { trigger: triggerInput }
745
+
746
+ if (inline && rpcService) {
747
+ const executeInline = async () => {
748
+ try {
749
+ await Promise.all(
750
+ readyEntryNodes.map(async (nodeId) => {
751
+ const node = nodes[nodeId]
752
+ if (!node?.rpcName) return
753
+
754
+ const resolvedInput =
755
+ node.input && Object.keys(node.input).length > 0
756
+ ? resolveSerializedInput(node.input, triggerNodeResults)
757
+ : triggerInput
758
+
759
+ await executeGraphNodeInline(
760
+ workflowService,
761
+ rpcService,
762
+ runId,
763
+ graphName,
764
+ nodeId,
765
+ resolvedInput,
766
+ nodes
767
+ )
768
+ })
769
+ )
770
+
771
+ await continueGraphInline(
772
+ workflowService,
773
+ rpcService,
774
+ runId,
775
+ graphName,
776
+ nodes,
777
+ triggerInput,
778
+ entryNodes
779
+ )
780
+ } catch (error) {
781
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
782
+ message: (error as Error).message,
783
+ stack: (error as Error).stack || '',
784
+ code: 'GRAPH_NODE_FAILED',
785
+ })
786
+ } finally {
787
+ workflowService.unregisterInlineRun(runId)
788
+ }
789
+ }
790
+ await executeInline()
791
+ } else {
792
+ for (const nodeId of readyEntryNodes) {
793
+ const node = nodes[nodeId]
794
+ if (!node?.rpcName) continue
795
+
796
+ const resolvedInput =
797
+ node.input && Object.keys(node.input).length > 0
798
+ ? resolveSerializedInput(node.input, triggerNodeResults)
799
+ : triggerInput
800
+
801
+ await queueGraphNode(
802
+ workflowService,
803
+ runId,
804
+ graphName,
805
+ nodeId,
806
+ node.rpcName,
807
+ resolvedInput
808
+ )
809
+ }
810
+ if (inline) {
811
+ workflowService.unregisterInlineRun(runId)
812
+ }
813
+ }
814
+
815
+ return { runId }
816
+ }