@pikku/core 0.11.2 → 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 (344) hide show
  1. package/CHANGELOG.md +33 -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 +8 -0
  5. package/dist/errors/errors.js +16 -0
  6. package/dist/errors/index.d.ts +0 -1
  7. package/dist/errors/index.js +0 -1
  8. package/dist/function/function-runner.d.ts +7 -1
  9. package/dist/function/function-runner.js +82 -23
  10. package/dist/function/functions.types.d.ts +31 -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.js +1 -10
  15. package/dist/index.d.ts +28 -26
  16. package/dist/index.js +18 -29
  17. package/dist/middleware/auth-apikey.d.ts +2 -2
  18. package/dist/middleware/auth-apikey.js +3 -3
  19. package/dist/middleware/auth-bearer.d.ts +2 -2
  20. package/dist/middleware/auth-bearer.js +3 -6
  21. package/dist/middleware/auth-cookie.d.ts +2 -2
  22. package/dist/middleware/auth-cookie.js +6 -8
  23. package/dist/middleware/cors.d.ts +46 -0
  24. package/dist/middleware/cors.js +73 -0
  25. package/dist/middleware/index.d.ts +5 -3
  26. package/dist/middleware/index.js +5 -3
  27. package/dist/middleware/remote-auth.d.ts +5 -0
  28. package/dist/middleware/remote-auth.js +55 -0
  29. package/dist/middleware/timeout.d.ts +2 -2
  30. package/dist/middleware-runner.js +8 -10
  31. package/dist/permissions.js +11 -15
  32. package/dist/pikku-state.d.ts +1 -0
  33. package/dist/pikku-state.js +18 -8
  34. package/dist/schema.js +12 -11
  35. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  36. package/dist/services/ai-agent-runner-service.js +1 -0
  37. package/dist/services/ai-run-state-service.d.ts +13 -0
  38. package/dist/services/ai-run-state-service.js +1 -0
  39. package/dist/services/ai-storage-service.d.ts +18 -0
  40. package/dist/services/ai-storage-service.js +1 -0
  41. package/dist/services/deployment-service.d.ts +19 -0
  42. package/dist/services/deployment-service.js +1 -0
  43. package/dist/services/gopass-secrets.d.ts +47 -0
  44. package/dist/services/gopass-secrets.js +94 -0
  45. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  46. package/dist/services/in-memory-trigger-service.js +50 -0
  47. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  48. package/dist/services/in-memory-workflow-service.js +287 -0
  49. package/dist/services/index.d.ts +25 -11
  50. package/dist/services/index.js +10 -13
  51. package/dist/services/local-secrets.d.ts +23 -0
  52. package/dist/services/local-secrets.js +45 -0
  53. package/dist/services/local-variables.d.ts +5 -0
  54. package/dist/services/local-variables.js +18 -0
  55. package/dist/services/scheduler-service.d.ts +21 -1
  56. package/dist/services/scheduler-service.js +14 -0
  57. package/dist/services/schema-service.d.ts +6 -0
  58. package/dist/services/scoped-secret-service.d.ts +16 -0
  59. package/dist/services/scoped-secret-service.js +35 -0
  60. package/dist/services/secret-service.d.ts +25 -4
  61. package/dist/services/trigger-service.d.ts +23 -0
  62. package/dist/services/trigger-service.js +1 -0
  63. package/dist/services/typed-secret-service.d.ts +30 -0
  64. package/dist/services/typed-secret-service.js +40 -0
  65. package/dist/services/typed-variables-service.d.ts +27 -0
  66. package/dist/services/typed-variables-service.js +46 -0
  67. package/dist/services/user-session-service.d.ts +13 -0
  68. package/dist/services/user-session-service.js +17 -0
  69. package/dist/services/variables-service.d.ts +5 -0
  70. package/dist/services/workflow-service.d.ts +9 -4
  71. package/dist/types/core.types.d.ts +48 -12
  72. package/dist/types/core.types.js +8 -1
  73. package/dist/types/state.types.d.ts +43 -51
  74. package/dist/utils.d.ts +3 -2
  75. package/dist/utils.js +15 -3
  76. package/dist/version.d.ts +6 -0
  77. package/dist/version.js +19 -0
  78. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  79. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  80. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  81. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  82. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  83. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  84. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  85. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  86. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  87. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  88. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  89. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  90. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  91. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  92. package/dist/wirings/ai-agent/index.d.ts +5 -0
  93. package/dist/wirings/ai-agent/index.js +4 -0
  94. package/dist/wirings/channel/channel-common.d.ts +7 -4
  95. package/dist/wirings/channel/channel-common.js +15 -8
  96. package/dist/wirings/channel/channel-handler.js +12 -3
  97. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  98. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  99. package/dist/wirings/channel/channel-runner.js +7 -7
  100. package/dist/wirings/channel/channel.types.d.ts +5 -1
  101. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  102. package/dist/wirings/channel/define-channel-routes.js +23 -0
  103. package/dist/wirings/channel/index.d.ts +10 -8
  104. package/dist/wirings/channel/index.js +7 -8
  105. package/dist/wirings/channel/local/index.d.ts +3 -3
  106. package/dist/wirings/channel/local/index.js +3 -3
  107. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  108. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  109. package/dist/wirings/channel/serverless/index.js +1 -1
  110. package/dist/wirings/channel/serverless/serverless-channel-runner.js +15 -4
  111. package/dist/wirings/cli/channel/index.d.ts +1 -1
  112. package/dist/wirings/cli/channel/index.js +1 -1
  113. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  114. package/dist/wirings/cli/cli-runner.js +12 -8
  115. package/dist/wirings/cli/cli.types.d.ts +6 -6
  116. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  117. package/dist/wirings/cli/define-cli-commands.js +20 -0
  118. package/dist/wirings/cli/index.d.ts +2 -3
  119. package/dist/wirings/cli/index.js +1 -3
  120. package/dist/wirings/http/http-routes.d.ts +69 -0
  121. package/dist/wirings/http/http-routes.js +139 -0
  122. package/dist/wirings/http/http-runner.js +49 -23
  123. package/dist/wirings/http/http.types.d.ts +78 -38
  124. package/dist/wirings/http/index.d.ts +5 -4
  125. package/dist/wirings/http/index.js +4 -3
  126. package/dist/wirings/mcp/index.d.ts +4 -3
  127. package/dist/wirings/mcp/index.js +3 -3
  128. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  129. package/dist/wirings/mcp/mcp-runner.js +25 -39
  130. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  131. package/dist/wirings/node/index.d.ts +2 -0
  132. package/dist/wirings/node/index.js +1 -0
  133. package/dist/wirings/node/node.types.d.ts +20 -0
  134. package/dist/wirings/node/node.types.js +1 -0
  135. package/dist/wirings/oauth2/index.d.ts +3 -0
  136. package/dist/wirings/oauth2/index.js +2 -0
  137. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  138. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  139. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  140. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  141. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  142. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  143. package/dist/wirings/queue/index.d.ts +2 -2
  144. package/dist/wirings/queue/index.js +1 -1
  145. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  146. package/dist/wirings/queue/queue-runner.js +17 -6
  147. package/dist/wirings/queue/queue.types.d.ts +8 -2
  148. package/dist/wirings/rpc/index.d.ts +1 -1
  149. package/dist/wirings/rpc/index.js +1 -1
  150. package/dist/wirings/rpc/rpc-runner.d.ts +33 -5
  151. package/dist/wirings/rpc/rpc-runner.js +134 -16
  152. package/dist/wirings/rpc/rpc-types.d.ts +8 -4
  153. package/dist/wirings/scheduler/index.d.ts +3 -3
  154. package/dist/wirings/scheduler/index.js +2 -3
  155. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  156. package/dist/wirings/scheduler/scheduler-runner.js +23 -5
  157. package/dist/wirings/secret/index.d.ts +3 -0
  158. package/dist/wirings/secret/index.js +2 -0
  159. package/dist/wirings/secret/secret.types.d.ts +27 -0
  160. package/dist/wirings/secret/secret.types.js +1 -0
  161. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  162. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  163. package/dist/wirings/trigger/index.d.ts +3 -2
  164. package/dist/wirings/trigger/index.js +2 -2
  165. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  166. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  167. package/dist/wirings/trigger/trigger-runner.d.ts +21 -8
  168. package/dist/wirings/trigger/trigger-runner.js +46 -18
  169. package/dist/wirings/trigger/trigger.types.d.ts +99 -11
  170. package/dist/wirings/trigger/trigger.types.js +40 -1
  171. package/dist/wirings/variable/index.d.ts +3 -0
  172. package/dist/wirings/variable/index.js +2 -0
  173. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  174. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  175. package/dist/wirings/variable/variable.types.d.ts +18 -0
  176. package/dist/wirings/variable/variable.types.js +1 -0
  177. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  178. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
  179. package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
  180. package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
  181. package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
  182. package/dist/wirings/workflow/graph/graph-runner.js +384 -296
  183. package/dist/wirings/workflow/graph/index.d.ts +4 -3
  184. package/dist/wirings/workflow/graph/index.js +3 -3
  185. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  186. package/dist/wirings/workflow/graph/template.js +27 -0
  187. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  188. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  189. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
  190. package/dist/wirings/workflow/index.d.ts +6 -6
  191. package/dist/wirings/workflow/index.js +6 -4
  192. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -14
  193. package/dist/wirings/workflow/pikku-workflow-service.js +200 -56
  194. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  195. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  196. package/dist/wirings/workflow/workflow-utils.d.ts +1 -23
  197. package/dist/wirings/workflow/workflow-utils.js +1 -66
  198. package/dist/wirings/workflow/workflow.types.d.ts +40 -93
  199. package/package.json +13 -15
  200. package/run-tests.sh +4 -1
  201. package/src/crypto-utils.ts +99 -0
  202. package/src/errors/errors.ts +17 -0
  203. package/src/errors/index.ts +0 -1
  204. package/src/factory-functions.test.ts +42 -2
  205. package/src/function/function-runner.test.ts +4 -11
  206. package/src/function/function-runner.ts +114 -29
  207. package/src/function/functions.types.ts +62 -132
  208. package/src/function/index.ts +5 -2
  209. package/src/handle-error.ts +2 -10
  210. package/src/index.ts +99 -25
  211. package/src/middleware/auth-apikey.test.ts +15 -12
  212. package/src/middleware/auth-apikey.ts +3 -3
  213. package/src/middleware/auth-bearer.test.ts +17 -14
  214. package/src/middleware/auth-bearer.ts +25 -27
  215. package/src/middleware/auth-cookie.test.ts +16 -13
  216. package/src/middleware/auth-cookie.ts +6 -8
  217. package/src/middleware/cors.ts +96 -0
  218. package/src/middleware/index.ts +5 -3
  219. package/src/middleware/remote-auth.ts +68 -0
  220. package/src/middleware-runner.test.ts +122 -0
  221. package/src/middleware-runner.ts +12 -12
  222. package/src/permissions.test.ts +81 -7
  223. package/src/permissions.ts +15 -17
  224. package/src/pikku-state.ts +20 -8
  225. package/src/run-tests-script.test.ts +49 -0
  226. package/src/schema.ts +18 -15
  227. package/src/services/ai-agent-runner-service.ts +29 -0
  228. package/src/services/ai-run-state-service.ts +20 -0
  229. package/src/services/ai-storage-service.ts +39 -0
  230. package/src/services/deployment-service.ts +22 -0
  231. package/src/services/gopass-secrets.ts +95 -0
  232. package/src/services/in-memory-trigger-service.ts +68 -0
  233. package/src/services/in-memory-workflow-service.ts +395 -0
  234. package/src/services/index.ts +49 -14
  235. package/src/services/local-secrets.ts +51 -0
  236. package/src/services/local-variables.ts +22 -0
  237. package/src/services/scheduler-service.ts +29 -1
  238. package/src/services/schema-service.ts +7 -0
  239. package/src/services/scoped-secret-service.ts +41 -0
  240. package/src/services/secret-service.ts +25 -4
  241. package/src/services/trigger-service.ts +38 -0
  242. package/src/services/typed-secret-service.ts +70 -0
  243. package/src/services/typed-variables-service.ts +81 -0
  244. package/src/services/user-session-service.ts +23 -0
  245. package/src/services/variables-service.ts +5 -0
  246. package/src/services/workflow-service.ts +22 -2
  247. package/src/types/core.types.ts +76 -11
  248. package/src/types/state.types.ts +37 -63
  249. package/src/utils.ts +20 -6
  250. package/src/version.test.ts +80 -0
  251. package/src/version.ts +25 -0
  252. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  253. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  254. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  255. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  256. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  257. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  258. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  259. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  260. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  261. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  262. package/src/wirings/ai-agent/index.ts +28 -0
  263. package/src/wirings/channel/channel-common.ts +32 -19
  264. package/src/wirings/channel/channel-handler.ts +17 -4
  265. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  266. package/src/wirings/channel/channel-runner.ts +9 -10
  267. package/src/wirings/channel/channel.types.ts +17 -1
  268. package/src/wirings/channel/define-channel-routes.ts +25 -0
  269. package/src/wirings/channel/index.ts +23 -8
  270. package/src/wirings/channel/local/index.ts +3 -3
  271. package/src/wirings/channel/local/local-channel-runner.test.ts +44 -1
  272. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  273. package/src/wirings/channel/serverless/index.ts +5 -1
  274. package/src/wirings/channel/serverless/serverless-channel-runner.ts +20 -4
  275. package/src/wirings/cli/channel/index.ts +1 -1
  276. package/src/wirings/cli/cli-runner.test.ts +9 -9
  277. package/src/wirings/cli/cli-runner.ts +17 -10
  278. package/src/wirings/cli/cli.types.ts +11 -11
  279. package/src/wirings/cli/command-parser.test.ts +8 -8
  280. package/src/wirings/cli/define-cli-commands.ts +24 -0
  281. package/src/wirings/cli/index.ts +10 -4
  282. package/src/wirings/http/http-routes.test.ts +322 -0
  283. package/src/wirings/http/http-routes.ts +198 -0
  284. package/src/wirings/http/http-runner.test.ts +3 -3
  285. package/src/wirings/http/http-runner.ts +63 -29
  286. package/src/wirings/http/http.types.ts +121 -37
  287. package/src/wirings/http/index.ts +16 -4
  288. package/src/wirings/mcp/index.ts +27 -3
  289. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  290. package/src/wirings/mcp/mcp-runner.ts +39 -50
  291. package/src/wirings/mcp/mcp.types.ts +3 -3
  292. package/src/wirings/node/index.ts +2 -0
  293. package/src/wirings/node/node.types.ts +23 -0
  294. package/src/wirings/oauth2/index.ts +3 -0
  295. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  296. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  297. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  298. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  299. package/src/wirings/queue/index.ts +14 -2
  300. package/src/wirings/queue/queue-runner.test.ts +97 -55
  301. package/src/wirings/queue/queue-runner.ts +33 -7
  302. package/src/wirings/queue/queue.types.ts +12 -2
  303. package/src/wirings/rpc/index.ts +1 -1
  304. package/src/wirings/rpc/rpc-runner.ts +187 -17
  305. package/src/wirings/rpc/rpc-types.ts +13 -4
  306. package/src/wirings/scheduler/index.ts +7 -4
  307. package/src/wirings/scheduler/scheduler-runner.test.ts +70 -26
  308. package/src/wirings/scheduler/scheduler-runner.ts +45 -5
  309. package/src/wirings/secret/index.ts +9 -0
  310. package/src/wirings/secret/secret.types.ts +32 -0
  311. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  312. package/src/wirings/trigger/index.ts +10 -2
  313. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  314. package/src/wirings/trigger/trigger-runner.ts +80 -38
  315. package/src/wirings/trigger/trigger.types.ts +144 -22
  316. package/src/wirings/variable/index.ts +8 -0
  317. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  318. package/src/wirings/variable/variable.types.ts +22 -0
  319. package/src/wirings/workflow/dsl/index.ts +1 -0
  320. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
  321. package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
  322. package/src/wirings/workflow/graph/graph-node.ts +1 -6
  323. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  324. package/src/wirings/workflow/graph/graph-runner.ts +496 -374
  325. package/src/wirings/workflow/graph/index.ts +14 -3
  326. package/src/wirings/workflow/graph/template.ts +42 -0
  327. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  328. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -22
  329. package/src/wirings/workflow/index.ts +21 -10
  330. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  331. package/src/wirings/workflow/pikku-workflow-service.ts +285 -66
  332. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  333. package/src/wirings/workflow/workflow-utils.ts +0 -120
  334. package/src/wirings/workflow/workflow.types.ts +45 -90
  335. package/tsconfig.tsbuildinfo +1 -1
  336. package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
  337. package/dist/wirings/forge-node/forge-node.types.js +0 -38
  338. package/dist/wirings/forge-node/index.d.ts +0 -1
  339. package/dist/wirings/forge-node/index.js +0 -1
  340. package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
  341. package/dist/wirings/workflow/wire-workflow.js +0 -53
  342. package/src/wirings/forge-node/forge-node.types.ts +0 -135
  343. package/src/wirings/forge-node/index.ts +0 -1
  344. package/src/wirings/workflow/wire-workflow.ts +0 -94
@@ -0,0 +1,69 @@
1
+ import type {
2
+ VariableDefinitions,
3
+ VariableDefinitionsMeta,
4
+ } from './variable.types.js'
5
+
6
+ export interface SchemaRefLike {
7
+ variableName: string
8
+ sourceFile: string
9
+ }
10
+
11
+ export function validateAndBuildVariableDefinitionsMeta(
12
+ definitions: VariableDefinitions,
13
+ schemaLookup: Map<string, SchemaRefLike>
14
+ ): VariableDefinitionsMeta {
15
+ const meta: VariableDefinitionsMeta = {}
16
+ const variableIdToDefinition: Map<string, VariableDefinitions[0]> = new Map()
17
+
18
+ for (const def of definitions) {
19
+ const existingDef = variableIdToDefinition.get(def.variableId)
20
+
21
+ if (existingDef) {
22
+ if (def.schema && existingDef.schema) {
23
+ const defSchemaRef = schemaLookup.get(def.schema as string)
24
+ const existingSchemaRef = schemaLookup.get(existingDef.schema as string)
25
+
26
+ if (defSchemaRef && existingSchemaRef) {
27
+ if (
28
+ defSchemaRef.variableName !== existingSchemaRef.variableName ||
29
+ defSchemaRef.sourceFile !== existingSchemaRef.sourceFile
30
+ ) {
31
+ throw new Error(
32
+ `Variable '${def.variableId}' is defined with different schemas.\n` +
33
+ ` First definition: ${existingDef.sourceFile} (schema: ${existingSchemaRef.variableName})\n` +
34
+ ` Second definition: ${def.sourceFile} (schema: ${defSchemaRef.variableName})\n` +
35
+ `Variables sharing a variableId must use the same schema.`
36
+ )
37
+ }
38
+ }
39
+ }
40
+
41
+ if (!meta[def.name]) {
42
+ meta[def.name] = {
43
+ name: def.name,
44
+ displayName: def.displayName,
45
+ description: def.description,
46
+ variableId: def.variableId,
47
+ schema: def.schema,
48
+ sourceFile: def.sourceFile,
49
+ }
50
+ }
51
+ continue
52
+ }
53
+
54
+ variableIdToDefinition.set(def.variableId, def)
55
+
56
+ if (!meta[def.name]) {
57
+ meta[def.name] = {
58
+ name: def.name,
59
+ displayName: def.displayName,
60
+ description: def.description,
61
+ variableId: def.variableId,
62
+ schema: def.schema,
63
+ sourceFile: def.sourceFile,
64
+ }
65
+ }
66
+ }
67
+
68
+ return meta
69
+ }
@@ -0,0 +1,22 @@
1
+ export type CoreVariable<T = unknown> = {
2
+ name: string
3
+ displayName: string
4
+ description?: string
5
+ variableId: string
6
+ schema: T
7
+ }
8
+
9
+ export type VariableDefinitionMeta = {
10
+ name: string
11
+ displayName: string
12
+ description?: string
13
+ variableId: string
14
+ schema?: Record<string, unknown> | string
15
+ sourceFile?: string
16
+ }
17
+
18
+ export type VariableDefinitionsMeta = Record<string, VariableDefinitionMeta>
19
+
20
+ export type VariableDefinitions = VariableDefinitionMeta[]
21
+
22
+ export const wireVariable = <T>(_config: CoreVariable<T>): void => {}
@@ -8,6 +8,7 @@ export type {
8
8
  WorkflowWireDoRPC,
9
9
  WorkflowWireDoInline,
10
10
  WorkflowWireSleep,
11
+ WorkflowWireSuspend,
11
12
  InputSource,
12
13
  OutputBinding,
13
14
  RpcStepMeta,
@@ -45,6 +45,11 @@ export type WorkflowWireSleep = (
45
45
  duration: string
46
46
  ) => Promise<void>
47
47
 
48
+ /**
49
+ * Type signature for workflow.suspend() - used by inspector
50
+ */
51
+ export type WorkflowWireSuspend = (reason: string) => Promise<void>
52
+
48
53
  /**
49
54
  * Input source for step arguments in DSL workflows
50
55
  */
@@ -298,7 +303,7 @@ export interface WorkflowStepWire {
298
303
  */
299
304
  export interface PikkuWorkflowWire {
300
305
  /** The workflow name */
301
- workflowName: string
306
+ name: string
302
307
  /** The current run ID */
303
308
  runId: string
304
309
  /** Get the current workflow run */
@@ -309,4 +314,7 @@ export interface PikkuWorkflowWire {
309
314
 
310
315
  /** Sleep for a duration */
311
316
  sleep: WorkflowWireSleep
317
+
318
+ /** Suspend workflow until explicitly resumed */
319
+ suspend: WorkflowWireSuspend
312
320
  }
@@ -1,5 +1,6 @@
1
1
  import { pikkuState } from '../../../pikku-state.js'
2
2
  import { addFunction } from '../../../function/function-runner.js'
3
+ import { PikkuMissingMetaError } from '../../../errors/errors.js'
3
4
 
4
5
  /**
5
6
  * Add a workflow to the system
@@ -10,8 +11,8 @@ export const addWorkflow = (workflowName: string, workflowFunc: any) => {
10
11
  const meta = pikkuState(null, 'workflows', 'meta')
11
12
  const workflowMeta = meta[workflowName]
12
13
  if (!workflowMeta) {
13
- throw new Error(
14
- `Workflow metadata not found for '${workflowName}'. Make sure to run the CLI to generate metadata.`
14
+ throw new PikkuMissingMetaError(
15
+ `Missing generated metadata for workflow '${workflowName}'`
15
16
  )
16
17
  }
17
18
 
@@ -23,5 +24,5 @@ export const addWorkflow = (workflowName: string, workflowFunc: any) => {
23
24
  })
24
25
 
25
26
  // Register the function with pikku
26
- addFunction(workflowMeta.pikkuFuncName, workflowFunc)
27
+ addFunction(workflowMeta.pikkuFuncId, workflowFunc)
27
28
  }
@@ -54,7 +54,7 @@ export interface GraphNodeDef<
54
54
  * @example
55
55
  * ```typescript
56
56
  * // Import the typed graph from generated types
57
- * import { pikkuWorkflowGraph, wireWorkflow } from './.pikku/workflow/pikku-workflow-types.gen.js'
57
+ * import { pikkuWorkflowGraph } from './.pikku/workflow/pikku-workflow-types.gen.js'
58
58
  *
59
59
  * export const myGraph = pikkuWorkflowGraph({
60
60
  * name: 'myWorkflow',
@@ -71,11 +71,6 @@ export interface GraphNodeDef<
71
71
  * },
72
72
  * },
73
73
  * })
74
- *
75
- * wireWorkflow({
76
- * wires: { http: { route: '/start', method: 'post' } },
77
- * graph: myGraph,
78
- * })
79
74
  * ```
80
75
  */
81
76
  /**
@@ -0,0 +1,495 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { InMemoryWorkflowService } from '../../../services/in-memory-workflow-service.js'
5
+ import {
6
+ continueGraph,
7
+ executeGraphStep,
8
+ runWorkflowGraph,
9
+ } from './graph-runner.js'
10
+ import type { WorkflowRuntimeMeta } from '../workflow.types.js'
11
+ import { pikkuState } from '../../../pikku-state.js'
12
+ import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
13
+
14
+ describe('graph-runner bugs', () => {
15
+ test('continueGraph should NOT mark workflow completed while nodes are still running', async () => {
16
+ const ws = new InMemoryWorkflowService()
17
+
18
+ const meta: WorkflowRuntimeMeta = {
19
+ name: 'testPrematureCompletion',
20
+ pikkuFuncId: 'testPrematureCompletion',
21
+ source: 'graph',
22
+ entryNodeIds: ['a'],
23
+ graphHash: 'test-hash',
24
+ nodes: {
25
+ a: { nodeId: 'a', rpcName: 'doA', next: 'b' },
26
+ b: { nodeId: 'b', rpcName: 'doB' },
27
+ },
28
+ }
29
+
30
+ const runId = await ws.createRun(
31
+ 'testPrematureCompletion',
32
+ {},
33
+ false,
34
+ 'test-hash',
35
+ { type: 'test' }
36
+ )
37
+
38
+ const stepA = await ws.insertStepState(runId, 'a', 'doA', {})
39
+ await ws.setStepRunning(stepA.stepId)
40
+ await ws.setStepResult(stepA.stepId, { ok: true })
41
+
42
+ const stepB = await ws.insertStepState(runId, 'b', 'doB', {})
43
+ await ws.setStepRunning(stepB.stepId)
44
+
45
+ await continueGraph(ws, runId, 'testPrematureCompletion', meta)
46
+
47
+ const run = await ws.getRun(runId)
48
+ assert.notEqual(
49
+ run?.status,
50
+ 'completed',
51
+ 'workflow should NOT be completed while node b is still running'
52
+ )
53
+ assert.equal(run?.status, 'running')
54
+ })
55
+
56
+ test('runWorkflowGraph should throw and not create a run for unknown input refs', async () => {
57
+ const ws = new InMemoryWorkflowService()
58
+
59
+ const metaState = pikkuState(null, 'workflows', 'meta')
60
+ metaState['testOrphanedRun'] = {
61
+ name: 'testOrphanedRun',
62
+ pikkuFuncId: 'testOrphanedRun',
63
+ source: 'graph',
64
+ entryNodeIds: ['a'],
65
+ graphHash: 'orphan-hash',
66
+ nodes: {
67
+ a: {
68
+ nodeId: 'a',
69
+ rpcName: 'doA',
70
+ input: { dep: { $ref: 'someOtherNode', path: 'value' } },
71
+ },
72
+ },
73
+ }
74
+
75
+ await assert.rejects(() => runWorkflowGraph(ws, 'testOrphanedRun', {}), {
76
+ message: /references unknown node 'someOtherNode' in input/,
77
+ })
78
+
79
+ const runs: string[] = [...(ws as any).runs.keys()]
80
+ assert.equal(
81
+ runs.length,
82
+ 0,
83
+ 'no run should be created when entry nodes have unsatisfied dependencies'
84
+ )
85
+
86
+ delete metaState['testOrphanedRun']
87
+ })
88
+
89
+ test('runWorkflowGraph should throw and not create a run for unknown next targets', async () => {
90
+ const ws = new InMemoryWorkflowService()
91
+
92
+ const metaState = pikkuState(null, 'workflows', 'meta')
93
+ metaState['testUnknownNextTarget'] = {
94
+ name: 'testUnknownNextTarget',
95
+ pikkuFuncId: 'testUnknownNextTarget',
96
+ source: 'graph',
97
+ entryNodeIds: ['a'],
98
+ graphHash: 'unknown-next-hash',
99
+ nodes: {
100
+ a: { nodeId: 'a', rpcName: 'doA', next: 'missingNode' },
101
+ },
102
+ }
103
+
104
+ await assert.rejects(
105
+ () => runWorkflowGraph(ws, 'testUnknownNextTarget', {}),
106
+ {
107
+ message: /routes to unknown node 'missingNode'/,
108
+ }
109
+ )
110
+
111
+ const runs: string[] = [...(ws as any).runs.keys()]
112
+ assert.equal(
113
+ runs.length,
114
+ 0,
115
+ 'no run should be created for invalid next targets'
116
+ )
117
+
118
+ delete metaState['testUnknownNextTarget']
119
+ })
120
+
121
+ test('continueGraph should throw for ambiguous template step remapping', async () => {
122
+ const ws = new InMemoryWorkflowService()
123
+
124
+ const meta: WorkflowRuntimeMeta = {
125
+ name: 'testAmbiguousTemplateStepRemap',
126
+ pikkuFuncId: 'testAmbiguousTemplateStepRemap',
127
+ source: 'graph',
128
+ entryNodeIds: ['start'],
129
+ graphHash: 'ambiguous-step-remap-hash',
130
+ nodes: {
131
+ start: { nodeId: 'start', rpcName: 'doStart' },
132
+ 'task-${id}': { nodeId: 'task-${id}', rpcName: 'doTaskById' },
133
+ 'task-${name}': { nodeId: 'task-${name}', rpcName: 'doTaskByName' },
134
+ },
135
+ }
136
+
137
+ const runId = await ws.createRun(
138
+ 'testAmbiguousTemplateStepRemap',
139
+ {},
140
+ false,
141
+ 'ambiguous-step-remap-hash',
142
+ { type: 'test' }
143
+ )
144
+
145
+ const step = await ws.insertStepState(runId, 'task-123', 'doTask', {})
146
+ await ws.setStepRunning(step.stepId)
147
+ await ws.setStepResult(step.stepId, { ok: true })
148
+
149
+ await assert.rejects(
150
+ () => continueGraph(ws, runId, 'testAmbiguousTemplateStepRemap', meta),
151
+ {
152
+ message: /ambiguous template node match for 'task-123'/i,
153
+ }
154
+ )
155
+ })
156
+
157
+ test('continueGraph should throw for ambiguous template branch key remapping', async () => {
158
+ const ws = new InMemoryWorkflowService()
159
+
160
+ const meta: WorkflowRuntimeMeta = {
161
+ name: 'testAmbiguousTemplateBranchRemap',
162
+ pikkuFuncId: 'testAmbiguousTemplateBranchRemap',
163
+ source: 'graph',
164
+ entryNodeIds: ['start'],
165
+ graphHash: 'ambiguous-branch-remap-hash',
166
+ nodes: {
167
+ start: { nodeId: 'start', rpcName: 'doStart' },
168
+ 'branch-${id}': { nodeId: 'branch-${id}', rpcName: 'doBranchById' },
169
+ 'branch-${name}': {
170
+ nodeId: 'branch-${name}',
171
+ rpcName: 'doBranchByName',
172
+ },
173
+ },
174
+ }
175
+
176
+ const runId = await ws.createRun(
177
+ 'testAmbiguousTemplateBranchRemap',
178
+ {},
179
+ false,
180
+ 'ambiguous-branch-remap-hash',
181
+ { type: 'test' }
182
+ )
183
+
184
+ const step = await ws.insertStepState(runId, 'branch-1', 'doBranch', {})
185
+ await ws.setBranchTaken(step.stepId, 'next')
186
+
187
+ await assert.rejects(
188
+ () => continueGraph(ws, runId, 'testAmbiguousTemplateBranchRemap', meta),
189
+ {
190
+ message: /ambiguous template branch key match for 'branch-1'/i,
191
+ }
192
+ )
193
+ })
194
+
195
+ test('continueGraph should queue converging next node only once', async () => {
196
+ const ws = new InMemoryWorkflowService()
197
+ const queuedNodes: string[] = []
198
+
199
+ ws.setServices(
200
+ {
201
+ queueService: {
202
+ add: async (_queueName: string, data: any) => {
203
+ if (data?.stepName) {
204
+ queuedNodes.push(data.stepName)
205
+ }
206
+ },
207
+ },
208
+ } as any,
209
+ (() => ({})) as any,
210
+ {} as any
211
+ )
212
+
213
+ const meta: WorkflowRuntimeMeta = {
214
+ name: 'testConvergingNextNode',
215
+ pikkuFuncId: 'testConvergingNextNode',
216
+ source: 'graph',
217
+ entryNodeIds: ['a', 'b'],
218
+ graphHash: 'converging-hash',
219
+ nodes: {
220
+ a: { nodeId: 'a', rpcName: 'doA', next: 'c' },
221
+ b: { nodeId: 'b', rpcName: 'doB', next: 'c' },
222
+ c: { nodeId: 'c', rpcName: 'doC' },
223
+ },
224
+ }
225
+
226
+ const runId = await ws.createRun(
227
+ 'testConvergingNextNode',
228
+ {},
229
+ false,
230
+ 'converging-hash',
231
+ { type: 'test' }
232
+ )
233
+
234
+ const stepA = await ws.insertStepState(runId, 'a', 'doA', {})
235
+ await ws.setStepRunning(stepA.stepId)
236
+ await ws.setStepResult(stepA.stepId, { ok: true })
237
+
238
+ const stepB = await ws.insertStepState(runId, 'b', 'doB', {})
239
+ await ws.setStepRunning(stepB.stepId)
240
+ await ws.setStepResult(stepB.stepId, { ok: true })
241
+
242
+ await continueGraph(ws, runId, 'testConvergingNextNode', meta)
243
+
244
+ assert.deepEqual(queuedNodes, ['c'])
245
+ })
246
+
247
+ test('inline graph should execute converging next node only once', async () => {
248
+ const ws = new InMemoryWorkflowService()
249
+ let cCalls = 0
250
+
251
+ const mockRpcService = {
252
+ rpcWithWire: async (rpcName: string) => {
253
+ if (rpcName === 'doA' || rpcName === 'doB') {
254
+ return { ok: true }
255
+ }
256
+ if (rpcName === 'doC') {
257
+ cCalls += 1
258
+ return { ok: true }
259
+ }
260
+ return {}
261
+ },
262
+ }
263
+
264
+ const metaState = pikkuState(null, 'workflows', 'meta')
265
+ metaState['testInlineConvergingSingleExecution'] = {
266
+ name: 'testInlineConvergingSingleExecution',
267
+ pikkuFuncId: 'testInlineConvergingSingleExecution',
268
+ source: 'graph',
269
+ entryNodeIds: ['a', 'b'],
270
+ graphHash: 'inline-converging-hash',
271
+ nodes: {
272
+ a: { nodeId: 'a', rpcName: 'doA', next: 'c' },
273
+ b: { nodeId: 'b', rpcName: 'doB', next: 'c' },
274
+ c: { nodeId: 'c', rpcName: 'doC' },
275
+ },
276
+ }
277
+
278
+ await runWorkflowGraph(
279
+ ws,
280
+ 'testInlineConvergingSingleExecution',
281
+ {},
282
+ mockRpcService,
283
+ true
284
+ )
285
+
286
+ assert.equal(cCalls, 1)
287
+
288
+ delete metaState['testInlineConvergingSingleExecution']
289
+ })
290
+
291
+ test('inline graph node failure should mark workflow as failed', async () => {
292
+ const ws = new InMemoryWorkflowService()
293
+
294
+ const mockRpcService = {
295
+ rpcWithWire: async (rpcName: string, _data: any, _wire: any) => {
296
+ if (rpcName === 'doA') return { ok: true }
297
+ if (rpcName === 'doB') throw new Error('doB exploded')
298
+ return {}
299
+ },
300
+ }
301
+
302
+ const metaState = pikkuState(null, 'workflows', 'meta')
303
+ metaState['testInlineFailure'] = {
304
+ name: 'testInlineFailure',
305
+ pikkuFuncId: 'testInlineFailure',
306
+ source: 'graph',
307
+ entryNodeIds: ['a'],
308
+ graphHash: 'inline-fail-hash',
309
+ nodes: {
310
+ a: { nodeId: 'a', rpcName: 'doA', next: 'b' },
311
+ b: { nodeId: 'b', rpcName: 'doB' },
312
+ },
313
+ }
314
+
315
+ let runId: string | undefined
316
+ try {
317
+ const result = await runWorkflowGraph(
318
+ ws,
319
+ 'testInlineFailure',
320
+ {},
321
+ mockRpcService,
322
+ true
323
+ )
324
+ runId = result.runId
325
+ } catch (_) {
326
+ const runs = (ws as any).runs as Map<string, any>
327
+ for (const [id] of runs) {
328
+ runId = id
329
+ break
330
+ }
331
+ }
332
+
333
+ assert.ok(runId, 'should have created a run')
334
+ const run = await ws.getRun(runId!)
335
+ assert.equal(
336
+ run?.status,
337
+ 'failed',
338
+ 'workflow should be failed after inline node error, not left running'
339
+ )
340
+
341
+ delete metaState['testInlineFailure']
342
+ })
343
+
344
+ test('executeGraphStep should throw after queueing onError nodes', async () => {
345
+ const ws = new InMemoryWorkflowService()
346
+
347
+ ws.setServices(
348
+ {
349
+ queueService: {
350
+ add: async () => {},
351
+ },
352
+ } as any,
353
+ (() => ({})) as any,
354
+ {} as any
355
+ )
356
+
357
+ const metaState = pikkuState(null, 'workflows', 'meta')
358
+ metaState['testQueuedOnErrorThrow'] = {
359
+ name: 'testQueuedOnErrorThrow',
360
+ pikkuFuncId: 'testQueuedOnErrorThrow',
361
+ source: 'graph',
362
+ entryNodeIds: ['a'],
363
+ graphHash: 'queued-on-error-hash',
364
+ nodes: {
365
+ a: { nodeId: 'a', rpcName: 'doA', onError: 'onErr' },
366
+ onErr: { nodeId: 'onErr', rpcName: 'handleErr' },
367
+ },
368
+ }
369
+
370
+ const runId = await ws.createRun(
371
+ 'testQueuedOnErrorThrow',
372
+ {},
373
+ false,
374
+ 'queued-on-error-hash',
375
+ { type: 'test' }
376
+ )
377
+ const step = await ws.insertStepState(runId, 'a', 'doA', {})
378
+
379
+ const rpcService = {
380
+ rpcWithWire: async () => {
381
+ throw new Error('boom')
382
+ },
383
+ }
384
+
385
+ await assert.rejects(
386
+ () =>
387
+ executeGraphStep(
388
+ ws,
389
+ rpcService,
390
+ runId,
391
+ step.stepId,
392
+ 'a',
393
+ 'doA',
394
+ {},
395
+ 'testQueuedOnErrorThrow'
396
+ ),
397
+ /boom/
398
+ )
399
+
400
+ const errorStep = await ws.getStepState(runId, 'onErr')
401
+ assert.notEqual(
402
+ errorStep.stepId,
403
+ '',
404
+ 'onError node should be queued as a step'
405
+ )
406
+
407
+ delete metaState['testQueuedOnErrorThrow']
408
+ })
409
+
410
+ test('executeWorkflowStep should mark graph step failed and suspend run on RPCNotFoundError', async () => {
411
+ const ws = new InMemoryWorkflowService()
412
+
413
+ const metaState = pikkuState(null, 'workflows', 'meta')
414
+ metaState['testQueuedRpcMissing'] = {
415
+ name: 'testQueuedRpcMissing',
416
+ pikkuFuncId: 'testQueuedRpcMissing',
417
+ source: 'graph',
418
+ entryNodeIds: ['a'],
419
+ graphHash: 'queued-rpc-missing-hash',
420
+ nodes: {
421
+ a: { nodeId: 'a', rpcName: 'missingRpc' },
422
+ },
423
+ }
424
+
425
+ const runId = await ws.createRun(
426
+ 'testQueuedRpcMissing',
427
+ {},
428
+ false,
429
+ 'queued-rpc-missing-hash',
430
+ { type: 'test' }
431
+ )
432
+ await ws.insertStepState(runId, 'a', 'missingRpc', {})
433
+
434
+ const rpcService = {
435
+ rpcWithWire: async () => {
436
+ throw new RPCNotFoundError('missingRpc')
437
+ },
438
+ }
439
+
440
+ await ws.executeWorkflowStep(runId, 'a', 'missingRpc', {}, rpcService)
441
+
442
+ const step = await ws.getStepState(runId, 'a')
443
+ const run = await ws.getRun(runId)
444
+
445
+ assert.equal(step.status, 'failed')
446
+ assert.equal(step.error?.message, 'RPC function not found: missingRpc')
447
+ assert.equal(run?.status, 'suspended')
448
+ assert.equal(run?.error?.code, 'RPC_NOT_FOUND')
449
+
450
+ delete metaState['testQueuedRpcMissing']
451
+ })
452
+
453
+ test('inline graph missing RPC should mark step failed and suspend run', async () => {
454
+ const ws = new InMemoryWorkflowService()
455
+
456
+ const metaState = pikkuState(null, 'workflows', 'meta')
457
+ metaState['testInlineRpcMissing'] = {
458
+ name: 'testInlineRpcMissing',
459
+ pikkuFuncId: 'testInlineRpcMissing',
460
+ source: 'graph',
461
+ entryNodeIds: ['a'],
462
+ graphHash: 'inline-rpc-missing-hash',
463
+ nodes: {
464
+ a: { nodeId: 'a', rpcName: 'missingInlineRpc' },
465
+ },
466
+ }
467
+
468
+ const rpcService = {
469
+ rpcWithWire: async () => {
470
+ throw new RPCNotFoundError('missingInlineRpc')
471
+ },
472
+ }
473
+
474
+ const { runId } = await runWorkflowGraph(
475
+ ws,
476
+ 'testInlineRpcMissing',
477
+ {},
478
+ rpcService,
479
+ true
480
+ )
481
+
482
+ const step = await ws.getStepState(runId, 'a')
483
+ const run = await ws.getRun(runId)
484
+
485
+ assert.equal(step.status, 'failed')
486
+ assert.equal(
487
+ step.error?.message,
488
+ 'RPC function not found: missingInlineRpc'
489
+ )
490
+ assert.equal(run?.status, 'suspended')
491
+ assert.equal(run?.error?.code, 'RPC_NOT_FOUND')
492
+
493
+ delete metaState['testInlineRpcMissing']
494
+ })
495
+ })