@pikku/core 0.11.2 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (436) hide show
  1. package/CHANGELOG.md +57 -2
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/errors.d.ts +14 -0
  5. package/dist/errors/errors.js +26 -0
  6. package/dist/errors/index.d.ts +1 -1
  7. package/dist/errors/index.js +1 -1
  8. package/dist/function/function-runner.d.ts +10 -4
  9. package/dist/function/function-runner.js +126 -45
  10. package/dist/function/functions.types.d.ts +32 -87
  11. package/dist/function/functions.types.js +16 -46
  12. package/dist/function/index.d.ts +2 -2
  13. package/dist/function/index.js +1 -2
  14. package/dist/handle-error.d.ts +2 -2
  15. package/dist/handle-error.js +3 -12
  16. package/dist/index.d.ts +30 -26
  17. package/dist/index.js +19 -29
  18. package/dist/internal.d.ts +3 -0
  19. package/dist/internal.js +2 -0
  20. package/dist/middleware/auth-apikey.d.ts +2 -2
  21. package/dist/middleware/auth-apikey.js +3 -3
  22. package/dist/middleware/auth-bearer.d.ts +3 -3
  23. package/dist/middleware/auth-bearer.js +4 -7
  24. package/dist/middleware/auth-cookie.d.ts +4 -4
  25. package/dist/middleware/auth-cookie.js +7 -9
  26. package/dist/middleware/cors.d.ts +46 -0
  27. package/dist/middleware/cors.js +81 -0
  28. package/dist/middleware/index.d.ts +5 -3
  29. package/dist/middleware/index.js +5 -3
  30. package/dist/middleware/remote-auth.d.ts +5 -0
  31. package/dist/middleware/remote-auth.js +55 -0
  32. package/dist/middleware/timeout.d.ts +2 -2
  33. package/dist/middleware-runner.d.ts +1 -1
  34. package/dist/middleware-runner.js +8 -10
  35. package/dist/permissions.d.ts +2 -2
  36. package/dist/permissions.js +11 -15
  37. package/dist/pikku-state.d.ts +10 -8
  38. package/dist/pikku-state.js +48 -25
  39. package/dist/schema.d.ts +4 -4
  40. package/dist/schema.js +20 -15
  41. package/dist/services/ai-agent-runner-service.d.ts +43 -0
  42. package/dist/services/ai-agent-runner-service.js +1 -0
  43. package/dist/services/ai-run-state-service.d.ts +13 -0
  44. package/dist/services/ai-run-state-service.js +1 -0
  45. package/dist/services/ai-storage-service.d.ts +18 -0
  46. package/dist/services/ai-storage-service.js +1 -0
  47. package/dist/services/content-service.d.ts +6 -0
  48. package/dist/services/deployment-service.d.ts +19 -0
  49. package/dist/services/deployment-service.js +1 -0
  50. package/dist/services/gopass-secrets.d.ts +47 -0
  51. package/dist/services/gopass-secrets.js +97 -0
  52. package/dist/services/in-memory-ai-run-state-service.d.ts +15 -0
  53. package/dist/services/in-memory-ai-run-state-service.js +44 -0
  54. package/dist/services/in-memory-trigger-service.d.ts +22 -0
  55. package/dist/services/in-memory-trigger-service.js +48 -0
  56. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  57. package/dist/services/in-memory-workflow-service.js +287 -0
  58. package/dist/services/index.d.ts +26 -11
  59. package/dist/services/index.js +11 -13
  60. package/dist/services/local-content.d.ts +2 -1
  61. package/dist/services/local-content.js +6 -1
  62. package/dist/services/local-secrets.d.ts +25 -2
  63. package/dist/services/local-secrets.js +45 -0
  64. package/dist/services/local-variables.d.ts +6 -1
  65. package/dist/services/local-variables.js +18 -0
  66. package/dist/services/logger-console.d.ts +2 -1
  67. package/dist/services/scheduler-service.d.ts +9 -1
  68. package/dist/services/scheduler-service.js +8 -0
  69. package/dist/services/schema-service.d.ts +6 -0
  70. package/dist/services/scoped-secret-service.d.ts +16 -0
  71. package/dist/services/scoped-secret-service.js +35 -0
  72. package/dist/services/secret-service.d.ts +25 -4
  73. package/dist/services/trigger-service.d.ts +16 -0
  74. package/dist/services/trigger-service.js +1 -0
  75. package/dist/services/typed-secret-service.d.ts +30 -0
  76. package/dist/services/typed-secret-service.js +40 -0
  77. package/dist/services/typed-variables-service.d.ts +27 -0
  78. package/dist/services/typed-variables-service.js +46 -0
  79. package/dist/services/user-session-service.d.ts +15 -2
  80. package/dist/services/user-session-service.js +17 -0
  81. package/dist/services/variables-service.d.ts +5 -0
  82. package/dist/services/workflow-service.d.ts +9 -5
  83. package/dist/types/core.types.d.ts +67 -25
  84. package/dist/types/core.types.js +8 -1
  85. package/dist/types/state.types.d.ts +53 -59
  86. package/dist/utils.d.ts +7 -6
  87. package/dist/utils.js +27 -12
  88. package/dist/version.d.ts +6 -0
  89. package/dist/version.js +19 -0
  90. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +5 -0
  91. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +146 -0
  92. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +28 -0
  93. package/dist/wirings/ai-agent/ai-agent-helpers.js +40 -0
  94. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  95. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  96. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  97. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  98. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +59 -0
  99. package/dist/wirings/ai-agent/ai-agent-prepare.js +348 -0
  100. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  101. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  102. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  103. package/dist/wirings/ai-agent/ai-agent-runner.js +195 -0
  104. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +12 -0
  105. package/dist/wirings/ai-agent/ai-agent-stream.js +662 -0
  106. package/dist/wirings/ai-agent/ai-agent.types.d.ts +313 -0
  107. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  108. package/dist/wirings/ai-agent/index.d.ts +7 -0
  109. package/dist/wirings/ai-agent/index.js +6 -0
  110. package/dist/wirings/channel/channel-common.d.ts +8 -5
  111. package/dist/wirings/channel/channel-common.js +15 -8
  112. package/dist/wirings/channel/channel-handler.d.ts +3 -3
  113. package/dist/wirings/channel/channel-handler.js +15 -6
  114. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  115. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  116. package/dist/wirings/channel/channel-runner.d.ts +5 -5
  117. package/dist/wirings/channel/channel-runner.js +10 -9
  118. package/dist/wirings/channel/channel-store.d.ts +1 -1
  119. package/dist/wirings/channel/channel.types.d.ts +9 -7
  120. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  121. package/dist/wirings/channel/define-channel-routes.js +23 -0
  122. package/dist/wirings/channel/index.d.ts +10 -8
  123. package/dist/wirings/channel/index.js +7 -8
  124. package/dist/wirings/channel/local/index.d.ts +3 -3
  125. package/dist/wirings/channel/local/index.js +3 -3
  126. package/dist/wirings/channel/local/local-channel-runner.d.ts +7 -2
  127. package/dist/wirings/channel/local/local-channel-runner.js +22 -9
  128. package/dist/wirings/channel/local/local-eventhub-service.d.ts +2 -2
  129. package/dist/wirings/channel/log-channels.d.ts +1 -1
  130. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +1 -1
  131. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  132. package/dist/wirings/channel/serverless/index.js +1 -1
  133. package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +4 -4
  134. package/dist/wirings/channel/serverless/serverless-channel-runner.js +40 -23
  135. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +1 -1
  136. package/dist/wirings/cli/channel/index.d.ts +1 -1
  137. package/dist/wirings/cli/channel/index.js +1 -1
  138. package/dist/wirings/cli/cli-runner.d.ts +3 -3
  139. package/dist/wirings/cli/cli-runner.js +12 -8
  140. package/dist/wirings/cli/cli.types.d.ts +9 -9
  141. package/dist/wirings/cli/command-parser.d.ts +1 -1
  142. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  143. package/dist/wirings/cli/define-cli-commands.js +20 -0
  144. package/dist/wirings/cli/index.d.ts +2 -3
  145. package/dist/wirings/cli/index.js +1 -3
  146. package/dist/wirings/http/http-routes.d.ts +69 -0
  147. package/dist/wirings/http/http-routes.js +139 -0
  148. package/dist/wirings/http/http-runner.d.ts +9 -9
  149. package/dist/wirings/http/http-runner.js +79 -34
  150. package/dist/wirings/http/http.types.d.ts +77 -41
  151. package/dist/wirings/http/index.d.ts +5 -4
  152. package/dist/wirings/http/index.js +4 -3
  153. package/dist/wirings/http/log-http-routes.d.ts +1 -1
  154. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -1
  155. package/dist/wirings/http/pikku-fetch-http-response.d.ts +2 -2
  156. package/dist/wirings/http/pikku-fetch-http-response.js +6 -1
  157. package/dist/wirings/http/routers/http-router.d.ts +1 -1
  158. package/dist/wirings/http/routers/path-to-regex.d.ts +2 -2
  159. package/dist/wirings/mcp/index.d.ts +4 -3
  160. package/dist/wirings/mcp/index.js +3 -3
  161. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  162. package/dist/wirings/mcp/mcp-runner.d.ts +1 -6
  163. package/dist/wirings/mcp/mcp-runner.js +28 -40
  164. package/dist/wirings/mcp/mcp.types.d.ts +5 -5
  165. package/dist/wirings/node/index.d.ts +2 -0
  166. package/dist/wirings/node/index.js +1 -0
  167. package/dist/wirings/node/node.types.d.ts +20 -0
  168. package/dist/wirings/node/node.types.js +1 -0
  169. package/dist/wirings/oauth2/index.d.ts +3 -0
  170. package/dist/wirings/oauth2/index.js +2 -0
  171. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  172. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  173. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  174. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  175. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  176. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  177. package/dist/wirings/queue/index.d.ts +1 -1
  178. package/dist/wirings/queue/queue-runner.d.ts +1 -5
  179. package/dist/wirings/queue/queue-runner.js +11 -8
  180. package/dist/wirings/queue/queue.types.d.ts +5 -5
  181. package/dist/wirings/queue/register-queue-helper.d.ts +2 -2
  182. package/dist/wirings/queue/register-queue-helper.js +1 -1
  183. package/dist/wirings/queue/validate-worker-config.d.ts +1 -1
  184. package/dist/wirings/rpc/index.d.ts +3 -1
  185. package/dist/wirings/rpc/index.js +2 -1
  186. package/dist/wirings/rpc/rpc-runner.d.ts +58 -8
  187. package/dist/wirings/rpc/rpc-runner.js +177 -31
  188. package/dist/wirings/rpc/rpc-types.d.ts +20 -4
  189. package/dist/wirings/rpc/wire-addon.d.ts +10 -0
  190. package/dist/wirings/rpc/wire-addon.js +9 -0
  191. package/dist/wirings/scheduler/index.d.ts +2 -2
  192. package/dist/wirings/scheduler/index.js +1 -2
  193. package/dist/wirings/scheduler/log-schedulers.d.ts +1 -1
  194. package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -5
  195. package/dist/wirings/scheduler/scheduler-runner.js +11 -14
  196. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
  197. package/dist/wirings/secret/index.d.ts +3 -0
  198. package/dist/wirings/secret/index.js +2 -0
  199. package/dist/wirings/secret/secret.types.d.ts +27 -0
  200. package/dist/wirings/secret/secret.types.js +1 -0
  201. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  202. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  203. package/dist/wirings/trigger/index.d.ts +3 -2
  204. package/dist/wirings/trigger/index.js +2 -2
  205. package/dist/wirings/trigger/pikku-trigger-service.d.ts +22 -0
  206. package/dist/wirings/trigger/pikku-trigger-service.js +70 -0
  207. package/dist/wirings/trigger/trigger-runner.d.ts +18 -9
  208. package/dist/wirings/trigger/trigger-runner.js +48 -19
  209. package/dist/wirings/trigger/trigger.types.d.ts +99 -11
  210. package/dist/wirings/trigger/trigger.types.js +40 -1
  211. package/dist/wirings/variable/index.d.ts +3 -0
  212. package/dist/wirings/variable/index.js +2 -0
  213. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  214. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  215. package/dist/wirings/variable/variable.types.d.ts +18 -0
  216. package/dist/wirings/variable/variable.types.js +1 -0
  217. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  218. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
  219. package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
  220. package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
  221. package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
  222. package/dist/wirings/workflow/graph/graph-runner.js +384 -296
  223. package/dist/wirings/workflow/graph/index.d.ts +4 -3
  224. package/dist/wirings/workflow/graph/index.js +3 -3
  225. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  226. package/dist/wirings/workflow/graph/template.js +27 -0
  227. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  228. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  229. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
  230. package/dist/wirings/workflow/index.d.ts +6 -6
  231. package/dist/wirings/workflow/index.js +6 -4
  232. package/dist/wirings/workflow/pikku-workflow-service.d.ts +29 -19
  233. package/dist/wirings/workflow/pikku-workflow-service.js +217 -82
  234. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  235. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  236. package/dist/wirings/workflow/workflow.types.d.ts +42 -95
  237. package/package.json +14 -15
  238. package/run-tests.sh +4 -1
  239. package/src/crypto-utils.test.ts +116 -0
  240. package/src/crypto-utils.ts +99 -0
  241. package/src/errors/error.test.ts +143 -2
  242. package/src/errors/errors.ts +27 -0
  243. package/src/errors/index.ts +1 -1
  244. package/src/factory-functions.test.ts +42 -2
  245. package/src/function/function-runner.test.ts +6 -13
  246. package/src/function/function-runner.ts +161 -53
  247. package/src/function/functions.types.ts +63 -132
  248. package/src/function/index.ts +5 -2
  249. package/src/handle-error.test.ts +361 -0
  250. package/src/handle-error.ts +6 -14
  251. package/src/index.ts +97 -30
  252. package/src/internal.ts +6 -0
  253. package/src/middleware/auth-apikey.test.ts +16 -13
  254. package/src/middleware/auth-apikey.ts +3 -3
  255. package/src/middleware/auth-bearer.test.ts +18 -15
  256. package/src/middleware/auth-bearer.ts +27 -32
  257. package/src/middleware/auth-cookie.test.ts +17 -14
  258. package/src/middleware/auth-cookie.ts +9 -13
  259. package/src/middleware/cors.test.ts +424 -0
  260. package/src/middleware/cors.ts +104 -0
  261. package/src/middleware/index.ts +5 -3
  262. package/src/middleware/remote-auth.test.ts +488 -0
  263. package/src/middleware/remote-auth.ts +68 -0
  264. package/src/middleware-runner.test.ts +123 -1
  265. package/src/middleware-runner.ts +13 -13
  266. package/src/permissions.test.ts +83 -9
  267. package/src/permissions.ts +17 -19
  268. package/src/pikku-state.test.ts +224 -0
  269. package/src/pikku-state.ts +59 -35
  270. package/src/run-tests-script.test.ts +49 -0
  271. package/src/schema.test.ts +198 -6
  272. package/src/schema.ts +28 -21
  273. package/src/services/ai-agent-runner-service.ts +41 -0
  274. package/src/services/ai-run-state-service.ts +20 -0
  275. package/src/services/ai-storage-service.ts +39 -0
  276. package/src/services/content-service.ts +7 -0
  277. package/src/services/deployment-service.ts +22 -0
  278. package/src/services/gopass-secrets.ts +98 -0
  279. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  280. package/src/services/in-memory-trigger-service.ts +64 -0
  281. package/src/services/in-memory-workflow-service.test.ts +351 -0
  282. package/src/services/in-memory-workflow-service.ts +395 -0
  283. package/src/services/index.ts +50 -14
  284. package/src/services/local-content.ts +9 -3
  285. package/src/services/local-secrets.test.ts +80 -0
  286. package/src/services/local-secrets.ts +53 -2
  287. package/src/services/local-variables.test.ts +61 -0
  288. package/src/services/local-variables.ts +23 -1
  289. package/src/services/logger-console.test.ts +118 -0
  290. package/src/services/logger-console.ts +2 -1
  291. package/src/services/scheduler-service.ts +11 -1
  292. package/src/services/schema-service.ts +7 -0
  293. package/src/services/scoped-secret-service.test.ts +74 -0
  294. package/src/services/scoped-secret-service.ts +41 -0
  295. package/src/services/secret-service.ts +25 -4
  296. package/src/services/trigger-service.ts +17 -0
  297. package/src/services/typed-secret-service.test.ts +93 -0
  298. package/src/services/typed-secret-service.ts +70 -0
  299. package/src/services/typed-variables-service.test.ts +73 -0
  300. package/src/services/typed-variables-service.ts +81 -0
  301. package/src/services/user-session-service.test.ts +113 -0
  302. package/src/services/user-session-service.ts +25 -2
  303. package/src/services/variables-service.ts +5 -0
  304. package/src/services/workflow-service.ts +23 -13
  305. package/src/time-utils.test.ts +1 -1
  306. package/src/types/core.types.ts +95 -24
  307. package/src/types/state.types.ts +56 -73
  308. package/src/utils.test.ts +350 -0
  309. package/src/utils.ts +33 -18
  310. package/src/version.test.ts +80 -0
  311. package/src/version.ts +25 -0
  312. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +363 -0
  313. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +214 -0
  314. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  315. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  316. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  317. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  318. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  319. package/src/wirings/ai-agent/ai-agent-prepare.ts +504 -0
  320. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  321. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  322. package/src/wirings/ai-agent/ai-agent-runner.test.ts +317 -0
  323. package/src/wirings/ai-agent/ai-agent-runner.ts +251 -0
  324. package/src/wirings/ai-agent/ai-agent-stream.test.ts +652 -0
  325. package/src/wirings/ai-agent/ai-agent-stream.ts +1049 -0
  326. package/src/wirings/ai-agent/ai-agent.types.ts +328 -0
  327. package/src/wirings/ai-agent/index.ts +39 -0
  328. package/src/wirings/channel/channel-common.ts +33 -20
  329. package/src/wirings/channel/channel-handler.ts +26 -9
  330. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  331. package/src/wirings/channel/channel-runner.ts +15 -16
  332. package/src/wirings/channel/channel-store.ts +1 -1
  333. package/src/wirings/channel/channel.types.ts +21 -9
  334. package/src/wirings/channel/define-channel-routes.ts +25 -0
  335. package/src/wirings/channel/index.ts +23 -8
  336. package/src/wirings/channel/local/index.ts +3 -3
  337. package/src/wirings/channel/local/local-channel-runner.test.ts +57 -7
  338. package/src/wirings/channel/local/local-channel-runner.ts +40 -19
  339. package/src/wirings/channel/local/local-eventhub-service.test.ts +2 -2
  340. package/src/wirings/channel/local/local-eventhub-service.ts +2 -2
  341. package/src/wirings/channel/log-channels.ts +1 -1
  342. package/src/wirings/channel/pikku-abstract-channel-handler.ts +1 -1
  343. package/src/wirings/channel/serverless/index.ts +5 -1
  344. package/src/wirings/channel/serverless/serverless-channel-runner.ts +57 -36
  345. package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
  346. package/src/wirings/cli/channel/index.ts +1 -1
  347. package/src/wirings/cli/cli-runner.test.ts +10 -10
  348. package/src/wirings/cli/cli-runner.ts +28 -14
  349. package/src/wirings/cli/cli.types.ts +14 -14
  350. package/src/wirings/cli/command-parser.test.ts +9 -9
  351. package/src/wirings/cli/command-parser.ts +1 -1
  352. package/src/wirings/cli/define-cli-commands.ts +24 -0
  353. package/src/wirings/cli/index.ts +10 -4
  354. package/src/wirings/http/http-routes.test.ts +322 -0
  355. package/src/wirings/http/http-routes.ts +198 -0
  356. package/src/wirings/http/http-runner.test.ts +12 -20
  357. package/src/wirings/http/http-runner.ts +101 -51
  358. package/src/wirings/http/http.types.ts +119 -48
  359. package/src/wirings/http/index.ts +16 -4
  360. package/src/wirings/http/log-http-routes.ts +1 -1
  361. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  362. package/src/wirings/http/pikku-fetch-http-response.ts +12 -5
  363. package/src/wirings/http/routers/http-router.ts +1 -1
  364. package/src/wirings/http/routers/path-to-regex.test.ts +1 -1
  365. package/src/wirings/http/routers/path-to-regex.ts +4 -3
  366. package/src/wirings/mcp/index.ts +27 -3
  367. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  368. package/src/wirings/mcp/mcp-endpoint-registry.ts +5 -1
  369. package/src/wirings/mcp/mcp-runner.ts +48 -65
  370. package/src/wirings/mcp/mcp.types.ts +5 -5
  371. package/src/wirings/node/index.ts +2 -0
  372. package/src/wirings/node/node.types.ts +23 -0
  373. package/src/wirings/oauth2/index.ts +3 -0
  374. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  375. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  376. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  377. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  378. package/src/wirings/queue/index.ts +13 -2
  379. package/src/wirings/queue/queue-runner.test.ts +142 -73
  380. package/src/wirings/queue/queue-runner.ts +18 -14
  381. package/src/wirings/queue/queue.types.ts +5 -5
  382. package/src/wirings/queue/register-queue-helper.ts +3 -5
  383. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  384. package/src/wirings/queue/validate-worker-config.ts +4 -1
  385. package/src/wirings/rpc/index.ts +3 -1
  386. package/src/wirings/rpc/rpc-runner.ts +254 -41
  387. package/src/wirings/rpc/rpc-types.ts +28 -4
  388. package/src/wirings/rpc/wire-addon.ts +20 -0
  389. package/src/wirings/scheduler/index.ts +6 -4
  390. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  391. package/src/wirings/scheduler/scheduler-runner.test.ts +78 -45
  392. package/src/wirings/scheduler/scheduler-runner.ts +20 -27
  393. package/src/wirings/scheduler/scheduler.types.ts +2 -2
  394. package/src/wirings/secret/index.ts +9 -0
  395. package/src/wirings/secret/secret.types.ts +32 -0
  396. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  397. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  398. package/src/wirings/trigger/index.ts +10 -2
  399. package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
  400. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  401. package/src/wirings/trigger/trigger-runner.ts +80 -41
  402. package/src/wirings/trigger/trigger.types.ts +144 -22
  403. package/src/wirings/variable/index.ts +8 -0
  404. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  405. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  406. package/src/wirings/variable/variable.types.ts +22 -0
  407. package/src/wirings/workflow/dsl/index.ts +1 -0
  408. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
  409. package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
  410. package/src/wirings/workflow/graph/graph-node.ts +1 -6
  411. package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
  412. package/src/wirings/workflow/graph/graph-runner.ts +496 -374
  413. package/src/wirings/workflow/graph/index.ts +14 -3
  414. package/src/wirings/workflow/graph/template.test.ts +49 -0
  415. package/src/wirings/workflow/graph/template.ts +42 -0
  416. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  417. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -22
  418. package/src/wirings/workflow/index.ts +21 -10
  419. package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
  420. package/src/wirings/workflow/pikku-workflow-service.ts +308 -105
  421. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  422. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  423. package/src/wirings/workflow/workflow.types.ts +47 -92
  424. package/tsconfig.tsbuildinfo +1 -1
  425. package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
  426. package/dist/wirings/forge-node/forge-node.types.js +0 -38
  427. package/dist/wirings/forge-node/index.d.ts +0 -1
  428. package/dist/wirings/forge-node/index.js +0 -1
  429. package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
  430. package/dist/wirings/workflow/wire-workflow.js +0 -53
  431. package/dist/wirings/workflow/workflow-utils.d.ts +0 -23
  432. package/dist/wirings/workflow/workflow-utils.js +0 -66
  433. package/src/wirings/forge-node/forge-node.types.ts +0 -135
  434. package/src/wirings/forge-node/index.ts +0 -1
  435. package/src/wirings/workflow/wire-workflow.ts +0 -94
  436. package/src/wirings/workflow/workflow-utils.ts +0 -120
@@ -1,112 +1,112 @@
1
- import { createRef, isRef } from './workflow-graph.types.js';
2
1
  import { pikkuState } from '../../../pikku-state.js';
3
- /**
4
- * Add a workflow graph to the system
5
- * This is called by the generated workflow wirings
6
- */
7
- export const addWorkflowGraph = (workflowName, graphResult) => {
8
- // Get workflow metadata from inspector
9
- const meta = pikkuState(null, 'workflows', 'meta');
10
- const workflowMeta = meta[workflowName];
11
- if (!workflowMeta) {
12
- throw new Error(`Workflow metadata not found for '${workflowName}'. Make sure to run the CLI to generate metadata.`);
13
- }
14
- // Store workflow graph definition in state
15
- const registrations = pikkuState(null, 'workflows', 'graphRegistrations');
16
- registrations.set(workflowName, {
17
- name: workflowName,
18
- wires: graphResult.wires || {},
19
- graph: graphResult.graph,
2
+ import { RPCNotFoundError } from '../../rpc/rpc-runner.js';
3
+ function buildTemplateRegex(nodeId) {
4
+ if (!nodeId.includes('${'))
5
+ return null;
6
+ const escaped = nodeId
7
+ .split(/\$\{[^}]+\}/)
8
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
9
+ .join('.+');
10
+ return new RegExp(`^${escaped}$`);
11
+ }
12
+ function remapStepNamesToNodeIds(stepNames, nodes, graphName) {
13
+ const templatePatterns = new Map();
14
+ for (const nodeId of Object.keys(nodes)) {
15
+ const regex = buildTemplateRegex(nodeId);
16
+ if (regex)
17
+ templatePatterns.set(nodeId, regex);
18
+ }
19
+ if (templatePatterns.size === 0)
20
+ return stepNames;
21
+ return stepNames.map((name) => {
22
+ if (nodes[name])
23
+ return name;
24
+ const matches = [];
25
+ for (const [nodeId, regex] of templatePatterns) {
26
+ if (regex.test(name))
27
+ matches.push(nodeId);
28
+ }
29
+ if (matches.length > 1) {
30
+ throw new Error(`Workflow graph '${graphName}': ambiguous template node match for '${name}' (${matches.join(', ')})`);
31
+ }
32
+ if (matches.length === 1) {
33
+ return matches[0];
34
+ }
35
+ return name;
20
36
  });
21
- };
22
- /**
23
- * Get a registered workflow graph by name
24
- */
25
- export function getWorkflowGraph(name) {
26
- const registrations = pikkuState(null, 'workflows', 'graphRegistrations');
27
- return registrations.get(name);
28
37
  }
29
- /**
30
- * Resolve next config to array of node IDs
31
- * For branching (Record), uses the branch key from step
32
- */
33
- function resolveNextFromConfig(next, branchKey) {
34
- if (!next)
35
- return [];
36
- if (typeof next === 'string')
37
- return [next];
38
- if (Array.isArray(next))
39
- return next;
40
- // Record (branching) - use branch key set by graph.branch()
41
- if (!branchKey || !(branchKey in next))
42
- return [];
43
- const branchNext = next[branchKey];
44
- return Array.isArray(branchNext) ? branchNext : [branchNext];
38
+ function remapBranchKeys(branchKeys, nodes, graphName) {
39
+ const templatePatterns = new Map();
40
+ for (const nodeId of Object.keys(nodes)) {
41
+ const regex = buildTemplateRegex(nodeId);
42
+ if (regex)
43
+ templatePatterns.set(nodeId, regex);
44
+ }
45
+ if (templatePatterns.size === 0)
46
+ return branchKeys;
47
+ const remapped = {};
48
+ for (const [key, value] of Object.entries(branchKeys)) {
49
+ let mappedKey = key;
50
+ if (!nodes[key]) {
51
+ const matches = [];
52
+ for (const [nodeId, regex] of templatePatterns) {
53
+ if (regex.test(key))
54
+ matches.push(nodeId);
55
+ }
56
+ if (matches.length > 1) {
57
+ throw new Error(`Workflow graph '${graphName}': ambiguous template branch key match for '${key}' (${matches.join(', ')})`);
58
+ }
59
+ if (matches.length === 1) {
60
+ mappedKey = matches[0];
61
+ }
62
+ }
63
+ remapped[mappedKey] = value;
64
+ }
65
+ return remapped;
66
+ }
67
+ function isDataRef(value) {
68
+ return (typeof value === 'object' &&
69
+ value !== null &&
70
+ '$ref' in value &&
71
+ typeof value.$ref === 'string');
45
72
  }
46
- /**
47
- * Check if a value is a template
48
- */
49
73
  function isTemplate(value) {
50
74
  return (typeof value === 'object' &&
51
75
  value !== null &&
52
76
  '$template' in value &&
53
77
  typeof value.$template === 'object');
54
78
  }
55
- /**
56
- * Create a template function for use in input callbacks
57
- */
58
- function createTemplate(templateStr, refs) {
59
- const parts = [];
60
- const expressions = [];
61
- const regex = /\$(\d+)/g;
62
- let lastIndex = 0;
63
- let match;
64
- while ((match = regex.exec(templateStr)) !== null) {
65
- parts.push(templateStr.slice(lastIndex, match.index));
66
- const refIndex = parseInt(match[1], 10);
67
- const refValue = refs[refIndex];
68
- if (refValue) {
69
- expressions.push({ $ref: refValue.nodeId, path: refValue.path });
70
- }
71
- else {
72
- expressions.push({ $ref: 'unknown' });
73
- }
74
- lastIndex = regex.lastIndex;
75
- }
76
- parts.push(templateStr.slice(lastIndex));
77
- return { $template: { parts, expressions } };
79
+ function getWorkflowMeta(name) {
80
+ const meta = pikkuState(null, 'workflows', 'meta');
81
+ return meta[name];
78
82
  }
79
- /**
80
- * Evaluate a node's input callback to get the input mapping
81
- */
82
- function evaluateInputCallback(node) {
83
- if (!node.input)
84
- return {};
85
- const ref = (targetNodeId, path) => createRef(targetNodeId, path);
86
- const template = (templateStr, refs) => createTemplate(templateStr, refs);
87
- // Call with both ref and template - input callback may accept 1 or 2 params
88
- return node.input(ref, template);
83
+ function resolveNextFromConfig(next, branchKey) {
84
+ if (!next)
85
+ return [];
86
+ if (typeof next === 'string')
87
+ return [next];
88
+ if (Array.isArray(next))
89
+ return next;
90
+ if (typeof next === 'object' && next !== null) {
91
+ if (!branchKey || !(branchKey in next))
92
+ return [];
93
+ const branchNext = next[branchKey];
94
+ return Array.isArray(branchNext) ? branchNext : [branchNext];
95
+ }
96
+ return [];
89
97
  }
90
- /**
91
- * Extract node IDs referenced in an input mapping (including from templates)
92
- */
93
- function extractReferencedNodeIds(inputMapping) {
94
- const nodeIds = [];
95
- for (const value of Object.values(inputMapping)) {
96
- if (isRef(value)) {
97
- nodeIds.push(value.nodeId);
98
- }
99
- else if (isTemplate(value)) {
100
- for (const expr of value.$template.expressions) {
101
- nodeIds.push(expr.$ref);
102
- }
103
- }
98
+ function getValueAtPath(obj, path) {
99
+ if (!path)
100
+ return obj;
101
+ const parts = path.split('.');
102
+ let current = obj;
103
+ for (const part of parts) {
104
+ if (current == null)
105
+ return undefined;
106
+ current = current[part];
104
107
  }
105
- return [...new Set(nodeIds)];
108
+ return current;
106
109
  }
107
- /**
108
- * Resolve a template value using node results
109
- */
110
110
  function resolveTemplate(template, nodeResults) {
111
111
  const { parts, expressions } = template.$template;
112
112
  let result = '';
@@ -123,113 +123,192 @@ function resolveTemplate(template, nodeResults) {
123
123
  }
124
124
  return result;
125
125
  }
126
- /**
127
- * Resolve input mapping using node results
128
- */
129
- function resolveInputMapping(inputMapping, nodeResults) {
126
+ function resolveValue(value, nodeResults) {
127
+ if (isDataRef(value)) {
128
+ const source = nodeResults[value.$ref];
129
+ return value.path ? getValueAtPath(source, value.path) : source;
130
+ }
131
+ if (isTemplate(value)) {
132
+ return resolveTemplate(value, nodeResults);
133
+ }
134
+ if (Array.isArray(value)) {
135
+ return value.map((item) => resolveValue(item, nodeResults));
136
+ }
137
+ if (typeof value === 'object' && value !== null) {
138
+ const resolved = {};
139
+ for (const [k, v] of Object.entries(value)) {
140
+ resolved[k] = resolveValue(v, nodeResults);
141
+ }
142
+ return resolved;
143
+ }
144
+ return value;
145
+ }
146
+ function resolveSerializedInput(input, nodeResults) {
147
+ if (!input || Object.keys(input).length === 0)
148
+ return {};
130
149
  const resolved = {};
131
- for (const [key, value] of Object.entries(inputMapping)) {
132
- if (isRef(value)) {
133
- const nodeResult = nodeResults[value.nodeId];
134
- resolved[key] = value.path
135
- ? getValueAtPath(nodeResult, value.path)
136
- : nodeResult;
150
+ for (const [key, value] of Object.entries(input)) {
151
+ resolved[key] = resolveValue(value, nodeResults);
152
+ }
153
+ return resolved;
154
+ }
155
+ function collectReferencedNodeIds(value, nodeIds) {
156
+ if (isDataRef(value)) {
157
+ nodeIds.push(value.$ref);
158
+ }
159
+ else if (isTemplate(value)) {
160
+ for (const expr of value.$template.expressions) {
161
+ nodeIds.push(expr.$ref);
137
162
  }
138
- else if (isTemplate(value)) {
139
- resolved[key] = resolveTemplate(value, nodeResults);
163
+ }
164
+ else if (Array.isArray(value)) {
165
+ for (const item of value) {
166
+ collectReferencedNodeIds(item, nodeIds);
140
167
  }
141
- else {
142
- resolved[key] = value;
168
+ }
169
+ else if (typeof value === 'object' && value !== null) {
170
+ for (const v of Object.values(value)) {
171
+ collectReferencedNodeIds(v, nodeIds);
143
172
  }
144
173
  }
145
- return resolved;
146
174
  }
147
- /**
148
- * Get value at a dot-notation path from an object
149
- */
150
- function getValueAtPath(obj, path) {
151
- if (!path)
152
- return obj;
153
- const parts = path.split('.');
154
- let current = obj;
155
- for (const part of parts) {
156
- if (current == null)
157
- return undefined;
158
- current = current[part];
175
+ function extractReferencedNodeIds(input) {
176
+ if (!input)
177
+ return [];
178
+ const nodeIds = [];
179
+ for (const value of Object.values(input)) {
180
+ collectReferencedNodeIds(value, nodeIds);
181
+ }
182
+ return [...new Set(nodeIds)];
183
+ }
184
+ const IGNORED_REFS = new Set(['trigger', '$item', 'unknown']);
185
+ function normalizeNodeTargets(value) {
186
+ if (!value)
187
+ return [];
188
+ if (typeof value === 'string')
189
+ return [value];
190
+ if (Array.isArray(value))
191
+ return value.filter((v) => typeof v === 'string');
192
+ if (typeof value === 'object') {
193
+ const targets = [];
194
+ for (const branchTarget of Object.values(value)) {
195
+ targets.push(...normalizeNodeTargets(branchTarget));
196
+ }
197
+ return targets;
198
+ }
199
+ return [];
200
+ }
201
+ function validateGraphReferences(graphName, nodes, entryNodes) {
202
+ const nodeIds = new Set(Object.keys(nodes));
203
+ for (const entryId of entryNodes) {
204
+ if (!nodeIds.has(entryId)) {
205
+ throw new Error(`Workflow graph '${graphName}': entry node '${entryId}' is not defined`);
206
+ }
207
+ }
208
+ for (const [nodeId, node] of Object.entries(nodes)) {
209
+ const inputRefs = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
210
+ for (const refId of inputRefs) {
211
+ if (!nodeIds.has(refId)) {
212
+ throw new Error(`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`);
213
+ }
214
+ }
215
+ const nextTargets = normalizeNodeTargets(node.next);
216
+ for (const nextId of nextTargets) {
217
+ if (!nodeIds.has(nextId)) {
218
+ throw new Error(`Workflow graph '${graphName}': node '${nodeId}' routes to unknown node '${nextId}'`);
219
+ }
220
+ }
221
+ const errorTargets = normalizeNodeTargets(node.onError);
222
+ for (const errorId of errorTargets) {
223
+ if (!nodeIds.has(errorId)) {
224
+ throw new Error(`Workflow graph '${graphName}': node '${nodeId}' onError targets unknown node '${errorId}'`);
225
+ }
226
+ }
159
227
  }
160
- return current;
161
228
  }
162
- /**
163
- * Get the RPC name from a graph node's func
164
- * Supports both function references (func.name) and string RPC names
165
- */
166
- function getRpcName(node) {
167
- const func = node.func;
168
- // If func is a string, use it directly as the RPC name
169
- if (typeof func === 'string') {
170
- return func;
171
- }
172
- // Otherwise, it's a function reference - use its name
173
- return func?.name || 'unknown';
229
+ function areDependenciesSatisfied(node, completedNodeIds) {
230
+ const deps = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
231
+ return deps.every((dep) => completedNodeIds.has(dep));
174
232
  }
175
- /**
176
- * Queue a graph node for execution
177
- */
178
233
  async function queueGraphNode(workflowService, runId, _graphName, nodeId, rpcName, input) {
179
- // Step name convention: node:<nodeId>
180
- // Graph name is stored as the workflow name on the run
181
- await workflowService.insertStepState(runId, `node:${nodeId}`, rpcName, input, { retries: 3 });
182
- await workflowService.resumeWorkflow(runId);
234
+ await workflowService.insertStepState(runId, nodeId, rpcName, input, {
235
+ retries: 0,
236
+ });
237
+ await workflowService.queueStepWorker(runId, nodeId, rpcName, input);
183
238
  }
184
- /**
185
- * Continue graph execution
186
- * Non-blocking - finds pending nodes, resolves inputs, queues them for execution
187
- */
188
- export async function continueGraph(workflowService, runId, graphName) {
189
- const definition = getWorkflowGraph(graphName);
190
- if (!definition) {
191
- throw new Error(`Workflow graph '${graphName}' not found`);
239
+ export async function continueGraph(workflowService, runId, graphName, overrideMeta) {
240
+ const meta = overrideMeta ?? getWorkflowMeta(graphName);
241
+ if (!meta?.nodes) {
242
+ throw new Error(`Workflow graph meta '${graphName}' not found`);
243
+ }
244
+ const nodes = meta.nodes;
245
+ validateGraphReferences(graphName, nodes, meta.entryNodeIds ?? []);
246
+ const { completedNodeIds: rawCompleted, failedNodeIds: rawFailed, branchKeys: rawBranch, } = await workflowService.getCompletedGraphState(runId);
247
+ const completedNodeIds = remapStepNamesToNodeIds(rawCompleted, nodes, graphName);
248
+ const completedNodeIdSet = new Set(completedNodeIds);
249
+ const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName);
250
+ const branchKeys = remapBranchKeys(rawBranch, nodes, graphName);
251
+ if (failedNodeIds.length > 0) {
252
+ const failedNode = failedNodeIds[0];
253
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
254
+ message: `Graph node '${failedNode}' failed after exhausting retries`,
255
+ stack: '',
256
+ code: 'GRAPH_NODE_FAILED',
257
+ });
258
+ return;
259
+ }
260
+ const currentRun = await workflowService.getRun(runId);
261
+ if (currentRun?.status === 'suspended') {
262
+ return;
192
263
  }
193
- const graph = definition.graph;
194
- // Get completed node IDs + branch keys (lightweight, no results)
195
- const { completedNodeIds, branchKeys } = await workflowService.getCompletedGraphState(runId);
196
- // Find candidate next nodes from completed nodes
197
- const candidateNodes = [];
264
+ const candidateNodes = new Set();
198
265
  for (const nodeId of completedNodeIds) {
199
- const node = graph[nodeId];
266
+ const node = nodes[nodeId];
200
267
  if (!node?.next)
201
268
  continue;
202
269
  const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId]);
203
- candidateNodes.push(...nextNodes);
270
+ for (const nextNode of nextNodes) {
271
+ candidateNodes.add(nextNode);
272
+ }
204
273
  }
205
- if (candidateNodes.length === 0 && completedNodeIds.length > 0) {
206
- // No more nodes to run - graph complete
274
+ for (const entryId of meta.entryNodeIds ?? []) {
275
+ candidateNodes.add(entryId);
276
+ }
277
+ if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
207
278
  await workflowService.updateRunStatus(runId, 'completed');
208
279
  return;
209
280
  }
210
- // Filter to only nodes that don't have a step yet
211
- const nodesToQueue = await workflowService.getNodesWithoutSteps(runId, candidateNodes);
212
- // Queue each node for execution
281
+ const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
282
+ ...candidateNodes,
283
+ ]);
284
+ const nodesToQueue = unstartedNodes.filter((nodeId) => {
285
+ const node = nodes[nodeId];
286
+ return node && areDependenciesSatisfied(node, completedNodeIdSet);
287
+ });
288
+ if (nodesToQueue.length === 0) {
289
+ const allRpcNodes = Object.entries(nodes)
290
+ .filter(([_, n]) => n.rpcName)
291
+ .map(([id]) => id);
292
+ const allRpcCompleted = allRpcNodes.every((id) => completedNodeIdSet.has(id));
293
+ if (allRpcCompleted) {
294
+ await workflowService.updateRunStatus(runId, 'completed');
295
+ }
296
+ return;
297
+ }
298
+ const run = await workflowService.getRun(runId);
299
+ const triggerInput = run?.input;
213
300
  for (const nodeId of nodesToQueue) {
214
- const node = graph[nodeId];
215
- if (!node)
301
+ const node = nodes[nodeId];
302
+ if (!node?.rpcName)
216
303
  continue;
217
- // Evaluate input callback to get the mapping
218
- const inputMapping = evaluateInputCallback(node);
219
- // Only fetch results for nodes referenced in this node's input
220
- const referencedNodeIds = extractReferencedNodeIds(inputMapping);
221
- const nodeResults = await workflowService.getNodeResults(runId, referencedNodeIds);
222
- const resolvedInput = resolveInputMapping(inputMapping, nodeResults);
223
- const rpcName = getRpcName(node);
224
- await queueGraphNode(workflowService, runId, graphName, nodeId, rpcName, resolvedInput);
304
+ const referencedNodeIds = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
305
+ const fetchedResults = await workflowService.getNodeResults(runId, referencedNodeIds);
306
+ const nodeResults = { trigger: triggerInput, ...fetchedResults };
307
+ const resolvedInput = resolveSerializedInput(node.input, nodeResults);
308
+ await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput);
225
309
  }
226
310
  }
227
- /**
228
- * Execute a graph step with wire context.
229
- * Called by the step worker when executing graph nodes.
230
- */
231
- export async function executeGraphStep(workflowService, rpcService, runId, stepId, stepName, rpcName, data, graphName) {
232
- const nodeId = stepName.replace(/^node:/, '');
311
+ export async function executeGraphStep(workflowService, rpcService, runId, stepId, nodeId, rpcName, data, graphName) {
233
312
  const wireState = {};
234
313
  const graphWire = {
235
314
  runId,
@@ -242,61 +321,54 @@ export async function executeGraphStep(workflowService, rpcService, runId, stepI
242
321
  getState: () => workflowService.getRunState(runId),
243
322
  };
244
323
  try {
245
- // Execute the RPC with graph wire context
246
324
  const result = await rpcService.rpcWithWire(rpcName, data, {
247
325
  graph: graphWire,
248
326
  });
249
- // If branch was called, store the branch key
250
327
  if (wireState.branchKey) {
251
328
  await workflowService.setBranchTaken(stepId, wireState.branchKey);
252
329
  }
253
330
  return result;
254
331
  }
255
332
  catch (error) {
256
- // Check if this node has an onError handler
257
- const definition = getWorkflowGraph(graphName);
258
- if (definition) {
259
- const node = definition.graph[nodeId];
333
+ if (error instanceof RPCNotFoundError) {
334
+ await workflowService.updateRunStatus(runId, 'suspended', undefined, {
335
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
336
+ code: 'RPC_NOT_FOUND',
337
+ });
338
+ throw error;
339
+ }
340
+ const meta = getWorkflowMeta(graphName);
341
+ if (meta?.nodes) {
342
+ const node = meta.nodes[nodeId];
260
343
  if (node?.onError) {
261
- // Route to error handler nodes
262
344
  const errorNodes = Array.isArray(node.onError)
263
345
  ? node.onError
264
346
  : [node.onError];
265
347
  for (const errorNodeId of errorNodes) {
266
- const errorNode = definition.graph[errorNodeId];
348
+ const errorNode = meta.nodes[errorNodeId];
267
349
  if (errorNode) {
268
- const errorRpcName = getRpcName(errorNode);
269
- // Queue error node with the error as input
270
- await queueGraphNode(workflowService, runId, graphName, errorNodeId, errorRpcName, { error: { message: error.message } });
350
+ await queueGraphNode(workflowService, runId, graphName, errorNodeId, errorNode.rpcName, { error: { message: error.message } });
271
351
  }
272
352
  }
273
- // Don't rethrow - error was handled by routing to error nodes
274
- return;
353
+ throw error;
275
354
  }
276
355
  }
277
- // No error handler - rethrow
278
356
  throw error;
279
357
  }
280
358
  }
281
- /**
282
- * Handle node completion - re-triggers graph continuation
283
- */
284
359
  export async function onGraphNodeComplete(workflowService, runId, graphName) {
285
360
  await continueGraph(workflowService, runId, graphName);
286
361
  }
287
- /**
288
- * Execute a graph node inline (without queue)
289
- */
290
- async function executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, input, graph) {
291
- const node = graph[nodeId];
362
+ export async function runFromMeta(workflowService, runId, meta, _rpcService) {
363
+ await continueGraph(workflowService, runId, meta.name, meta);
364
+ }
365
+ async function executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, input, nodes) {
366
+ const node = nodes[nodeId];
292
367
  if (!node)
293
368
  return;
294
- const rpcName = getRpcName(node);
295
- const stepName = `node:${nodeId}`;
296
- // Insert step state
297
- const stepState = await workflowService.insertStepState(runId, stepName, rpcName, input, { retries: 3 });
369
+ const rpcName = node.rpcName;
370
+ const stepState = await workflowService.insertStepState(runId, nodeId, rpcName, input, { retries: 3 });
298
371
  await workflowService.setStepRunning(stepState.stepId);
299
- // Execute with graph wire context
300
372
  const wireState = {};
301
373
  const graphWire = {
302
374
  runId,
@@ -312,141 +384,157 @@ async function executeGraphNodeInline(workflowService, rpcService, runId, graphN
312
384
  const result = await rpcService.rpcWithWire(rpcName, input, {
313
385
  graph: graphWire,
314
386
  });
315
- // If branch was called, store the branch key
316
387
  if (wireState.branchKey) {
317
388
  await workflowService.setBranchTaken(stepState.stepId, wireState.branchKey);
318
389
  }
319
390
  await workflowService.setStepResult(stepState.stepId, result);
320
391
  }
321
392
  catch (error) {
393
+ if (error instanceof RPCNotFoundError) {
394
+ await workflowService.setStepError(stepState.stepId, error);
395
+ await workflowService.updateRunStatus(runId, 'suspended', undefined, {
396
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
397
+ code: 'RPC_NOT_FOUND',
398
+ });
399
+ return;
400
+ }
322
401
  await workflowService.setStepError(stepState.stepId, error);
323
- // Check if this node has an onError handler
324
- const definition = getWorkflowGraph(graphName);
325
- if (definition) {
326
- const node = definition.graph[nodeId];
327
- if (node?.onError) {
328
- // Route to error handler nodes (inline)
329
- const errorNodes = Array.isArray(node.onError)
330
- ? node.onError
331
- : [node.onError];
332
- await Promise.all(errorNodes.map((errorNodeId) => executeGraphNodeInline(workflowService, rpcService, runId, graphName, errorNodeId, { error: { message: error.message } }, graph)));
333
- return;
334
- }
402
+ if (node?.onError) {
403
+ const errorNodes = Array.isArray(node.onError)
404
+ ? node.onError
405
+ : [node.onError];
406
+ await Promise.all(errorNodes.map((errorNodeId) => executeGraphNodeInline(workflowService, rpcService, runId, graphName, errorNodeId, { error: { message: error.message } }, nodes)));
407
+ return;
335
408
  }
336
- // No error handler - rethrow
337
409
  throw error;
338
410
  }
339
411
  }
340
- /**
341
- * Continue graph execution inline (without queue)
342
- * Executes nodes in parallel where possible using Promise.all
343
- */
344
- async function continueGraphInline(workflowService, rpcService, runId, graphName, graph, triggerInput) {
412
+ async function continueGraphInline(workflowService, rpcService, runId, graphName, nodes, triggerInput, entryNodeIds) {
345
413
  while (true) {
346
- // Get completed node IDs + branch keys (lightweight, no results)
347
- const { completedNodeIds, branchKeys } = await workflowService.getCompletedGraphState(runId);
348
- // Find candidate next nodes from completed nodes
349
- const candidateNodes = [];
414
+ const { completedNodeIds: rawCompleted, failedNodeIds: rawFailed, branchKeys: rawBranch, } = await workflowService.getCompletedGraphState(runId);
415
+ const completedNodeIds = remapStepNamesToNodeIds(rawCompleted, nodes, graphName);
416
+ const completedNodeIdSet = new Set(completedNodeIds);
417
+ const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName);
418
+ const branchKeys = remapBranchKeys(rawBranch, nodes, graphName);
419
+ if (failedNodeIds.length > 0) {
420
+ const failedNode = failedNodeIds[0];
421
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
422
+ message: `Graph node '${failedNode}' failed after exhausting retries`,
423
+ stack: '',
424
+ code: 'GRAPH_NODE_FAILED',
425
+ });
426
+ return;
427
+ }
428
+ const candidateNodes = new Set();
350
429
  for (const nodeId of completedNodeIds) {
351
- const node = graph[nodeId];
430
+ const node = nodes[nodeId];
352
431
  if (!node?.next)
353
432
  continue;
354
433
  const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId]);
355
- candidateNodes.push(...nextNodes);
434
+ for (const nextNode of nextNodes) {
435
+ candidateNodes.add(nextNode);
436
+ }
356
437
  }
357
- if (candidateNodes.length === 0 && completedNodeIds.length > 0) {
358
- // No more nodes to run - graph complete
438
+ for (const entryId of entryNodeIds) {
439
+ candidateNodes.add(entryId);
440
+ }
441
+ if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
359
442
  await workflowService.updateRunStatus(runId, 'completed');
360
443
  return;
361
444
  }
362
- // Filter to only nodes that don't have a step yet
363
- const nodesToExecute = await workflowService.getNodesWithoutSteps(runId, candidateNodes);
445
+ const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
446
+ ...candidateNodes,
447
+ ]);
448
+ const nodesToExecute = unstartedNodes.filter((nodeId) => {
449
+ const node = nodes[nodeId];
450
+ return node && areDependenciesSatisfied(node, completedNodeIdSet);
451
+ });
364
452
  if (nodesToExecute.length === 0) {
365
- // No more nodes to execute - if we've completed at least one node, we're done
366
- // This handles branching where only some paths are taken
367
- if (completedNodeIds.length > 0) {
453
+ if (completedNodeIds.length > 0 && unstartedNodes.length === 0) {
368
454
  await workflowService.updateRunStatus(runId, 'completed');
369
455
  }
370
456
  return;
371
457
  }
372
- // Execute all nodes in parallel
373
458
  await Promise.all(nodesToExecute.map(async (nodeId) => {
374
- const node = graph[nodeId];
375
- if (!node)
459
+ const node = nodes[nodeId];
460
+ if (!node?.rpcName)
376
461
  return;
377
- // Evaluate input callback to get the mapping
378
- const inputMapping = evaluateInputCallback(node);
379
- // Only fetch results for nodes referenced in this node's input (excluding trigger)
380
- const referencedNodeIds = extractReferencedNodeIds(inputMapping).filter((id) => id !== 'trigger');
462
+ const referencedNodeIds = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
381
463
  const fetchedResults = await workflowService.getNodeResults(runId, referencedNodeIds);
382
- // Merge fetched results with trigger input
383
464
  const nodeResults = { trigger: triggerInput, ...fetchedResults };
384
- const resolvedInput = resolveInputMapping(inputMapping, nodeResults);
385
- await executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, resolvedInput, graph);
465
+ const resolvedInput = resolveSerializedInput(node.input, nodeResults);
466
+ await executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, resolvedInput, nodes);
386
467
  }));
387
468
  }
388
469
  }
389
- /**
390
- * Start a workflow graph execution
391
- * @param startNode - Optional starting node ID (from wire config). If not provided, uses wires.api from graph definition.
392
- */
393
- export async function runWorkflowGraph(workflowService, graphName, triggerInput, rpcService, inline, startNode) {
394
- const definition = getWorkflowGraph(graphName);
395
- if (!definition) {
470
+ export async function runWorkflowGraph(workflowService, graphName, triggerInput, rpcService, inline, startNode, wire) {
471
+ const meta = getWorkflowMeta(graphName);
472
+ if (!meta?.nodes) {
396
473
  throw new Error(`Workflow graph '${graphName}' not found`);
397
474
  }
398
- // Use startNode from caller if provided, otherwise use wires.api from graph definition
399
- const apiStartNode = startNode ?? definition.wires?.api;
400
- const entryNodes = apiStartNode ? [apiStartNode] : [];
475
+ const nodes = meta.nodes;
476
+ const entryNodes = startNode
477
+ ? [startNode]
478
+ : (meta.entryNodeIds ?? []);
479
+ validateGraphReferences(graphName, nodes, entryNodes);
401
480
  if (entryNodes.length === 0) {
402
- throw new Error(`Workflow graph '${graphName}' has no wires.api defined and no startNode was provided`);
481
+ throw new Error(`Workflow graph '${graphName}': no entry nodes found in meta or startNode`);
482
+ }
483
+ const readyEntryNodes = entryNodes.filter((nodeId) => {
484
+ const node = nodes[nodeId];
485
+ return node && areDependenciesSatisfied(node, new Set());
486
+ });
487
+ if (readyEntryNodes.length === 0) {
488
+ throw new Error(`Workflow graph '${graphName}': no entry nodes have satisfied dependencies`);
403
489
  }
404
- const graph = definition.graph;
405
- const runId = await workflowService.createRun(graphName, triggerInput, inline);
406
- // Register as inline for fast lookup
490
+ if (!meta.graphHash) {
491
+ throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`);
492
+ }
493
+ const runId = await workflowService.createRun(graphName, triggerInput, inline ?? false, meta.graphHash, wire ?? { type: 'unknown' });
407
494
  if (inline) {
408
495
  workflowService.registerInlineRun(runId);
409
496
  }
410
- // Create nodeResults with trigger for resolving entry node inputs
411
497
  const triggerNodeResults = { trigger: triggerInput };
412
- try {
413
- if (inline && rpcService) {
414
- // Inline mode - execute entry nodes in parallel
415
- await Promise.all(entryNodes.map(async (nodeId) => {
416
- const node = graph[nodeId];
417
- if (!node)
418
- return;
419
- // Evaluate and resolve entry node input
420
- const inputMapping = evaluateInputCallback(node);
421
- const resolvedInput = Object.keys(inputMapping).length > 0
422
- ? resolveInputMapping(inputMapping, triggerNodeResults)
423
- : triggerInput;
424
- await executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, resolvedInput, graph);
425
- }));
426
- // Continue executing remaining nodes inline
427
- await continueGraphInline(workflowService, rpcService, runId, graphName, graph, triggerInput);
428
- }
429
- else {
430
- // Queue-based mode
431
- for (const nodeId of entryNodes) {
432
- const node = graph[nodeId];
433
- if (!node)
434
- continue;
435
- // Evaluate and resolve entry node input
436
- const inputMapping = evaluateInputCallback(node);
437
- const resolvedInput = Object.keys(inputMapping).length > 0
438
- ? resolveInputMapping(inputMapping, triggerNodeResults)
439
- : triggerInput;
440
- const rpcName = getRpcName(node);
441
- await queueGraphNode(workflowService, runId, graphName, nodeId, rpcName, resolvedInput);
498
+ if (inline && rpcService) {
499
+ const executeInline = async () => {
500
+ try {
501
+ await Promise.all(readyEntryNodes.map(async (nodeId) => {
502
+ const node = nodes[nodeId];
503
+ if (!node?.rpcName)
504
+ return;
505
+ const resolvedInput = node.input && Object.keys(node.input).length > 0
506
+ ? resolveSerializedInput(node.input, triggerNodeResults)
507
+ : triggerInput;
508
+ await executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, resolvedInput, nodes);
509
+ }));
510
+ await continueGraphInline(workflowService, rpcService, runId, graphName, nodes, triggerInput, entryNodes);
442
511
  }
443
- }
444
- return { runId };
512
+ catch (error) {
513
+ await workflowService.updateRunStatus(runId, 'failed', undefined, {
514
+ message: error.message,
515
+ stack: error.stack || '',
516
+ code: 'GRAPH_NODE_FAILED',
517
+ });
518
+ }
519
+ finally {
520
+ workflowService.unregisterInlineRun(runId);
521
+ }
522
+ };
523
+ await executeInline();
445
524
  }
446
- finally {
447
- // Clean up inline tracking
525
+ else {
526
+ for (const nodeId of readyEntryNodes) {
527
+ const node = nodes[nodeId];
528
+ if (!node?.rpcName)
529
+ continue;
530
+ const resolvedInput = node.input && Object.keys(node.input).length > 0
531
+ ? resolveSerializedInput(node.input, triggerNodeResults)
532
+ : triggerInput;
533
+ await queueGraphNode(workflowService, runId, graphName, nodeId, node.rpcName, resolvedInput);
534
+ }
448
535
  if (inline) {
449
536
  workflowService.unregisterInlineRun(runId);
450
537
  }
451
538
  }
539
+ return { runId };
452
540
  }