@pikku/cli 0.12.110 → 0.12.112

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 (252) hide show
  1. package/CHANGELOG.md +271 -0
  2. package/console-app/assets/{abnfDiagram-N423BO3Z-Bz2Ycfpe.js → abnfDiagram-N423BO3Z-Bltf5dmS.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-DgRqRaqo.js +1 -0
  4. package/console-app/assets/{architectureDiagram-T3A2C74G-CvAkxhy0.js → architectureDiagram-T3A2C74G-BTtx6R7-.js} +1 -1
  5. package/console-app/assets/{blockDiagram-VBNYF7ZC-CuVOo2PB.js → blockDiagram-VBNYF7ZC-DgeTIOaM.js} +1 -1
  6. package/console-app/assets/{c4Diagram-5PPSVZJV-4Vg8SxCa.js → c4Diagram-5PPSVZJV-CaswnNob.js} +1 -1
  7. package/console-app/assets/channel-37lNznWk.js +1 -0
  8. package/console-app/assets/{chunk-2GRJ4B5K-CKKR4_dC.js → chunk-2GRJ4B5K-OnpnkpQT.js} +1 -1
  9. package/console-app/assets/{chunk-4I5QYGJK-C-m_kVjm.js → chunk-4I5QYGJK-CWmfNXFr.js} +1 -1
  10. package/console-app/assets/{chunk-5RXB4S5H-CyUGtd0h.js → chunk-5RXB4S5H-M8QmKeuk.js} +1 -1
  11. package/console-app/assets/{chunk-6Q2QTUOP-BuzItInf.js → chunk-6Q2QTUOP-C_h5lS68.js} +1 -1
  12. package/console-app/assets/{chunk-7Z6QIM7H-BGiBPuWt.js → chunk-7Z6QIM7H-DSnVb09k.js} +1 -1
  13. package/console-app/assets/{chunk-GF5L2VYU-D8jaliB9.js → chunk-GF5L2VYU-BkX038sq.js} +1 -1
  14. package/console-app/assets/{chunk-I66GZJ75-C-Lq_UXl.js → chunk-I66GZJ75-b3yMIljX.js} +1 -1
  15. package/console-app/assets/{chunk-JQJVKLGR-D7viK-tf.js → chunk-JQJVKLGR-Bihcz4nv.js} +1 -1
  16. package/console-app/assets/{chunk-KBJHAD2P-BS9N9gG4.js → chunk-KBJHAD2P-CS7eflbk.js} +1 -1
  17. package/console-app/assets/{chunk-NSK5VX7P-Fi_H0vaS.js → chunk-NSK5VX7P-CfigFGDQ.js} +1 -1
  18. package/console-app/assets/{chunk-QR6OTTB3-BJdg-tcL.js → chunk-QR6OTTB3-BBy8NfTW.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-CVbGA6YZ.js → chunk-RYQCIY6F-BCfZVQef.js} +1 -1
  20. package/console-app/assets/{chunk-UBXNYLIW-BF-sAqEK.js → chunk-UBXNYLIW-DkMKDHW4.js} +1 -1
  21. package/console-app/assets/{chunk-W5SLKNZC-CXrJJ0BX.js → chunk-W5SLKNZC-BSLytTSJ.js} +1 -1
  22. package/console-app/assets/{chunk-WRU74C26-CGzysGBy.js → chunk-WRU74C26-Dic7ZP8H.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-B1prMCvW.js +1 -0
  24. package/console-app/assets/classDiagram-JCYQIIEL-BDCX9ZLm.js +1 -0
  25. package/console-app/assets/classDiagram-v2-OCEON4UE-BDCX9ZLm.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-BvFZIDHb.js → cose-bilkent-JH36ORCC-CEpWcE3M.js} +1 -1
  27. package/console-app/assets/{cynefin-VYW2F7L2-DRQ8xGmN.js → cynefin-VYW2F7L2-irPiPhgz.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-MW4NZA55-BwxQ4C1z.js → cynefinDiagram-MW4NZA55-CIkqQ912.js} +1 -1
  29. package/console-app/assets/{dagre-J22zmNeE.js → dagre-FZyyw3H3.js} +1 -1
  30. package/console-app/assets/{dagre-VZM6K2ZE-DPtUyvjD.js → dagre-VZM6K2ZE-Dxnnu7Rh.js} +1 -1
  31. package/console-app/assets/{diagram-7IWD3JNH-DGAzJ2Ln.js → diagram-7IWD3JNH-DaT47sVs.js} +1 -1
  32. package/console-app/assets/{diagram-B4RE2ZJO-D14cIfto.js → diagram-B4RE2ZJO-Dmht03tx.js} +1 -1
  33. package/console-app/assets/{diagram-LBJQPF4R-CUscJ_pO.js → diagram-LBJQPF4R-B--jwILh.js} +1 -1
  34. package/console-app/assets/{diagram-Q27KOJAE-CehBHMt3.js → diagram-Q27KOJAE-D9H0euUr.js} +1 -1
  35. package/console-app/assets/{diagram-UB23O5K3-BvazI_qH.js → diagram-UB23O5K3-DloUQ8tO.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-BXEA7PRR-CBZPYFwZ.js → ebnfDiagram-BXEA7PRR-gaOXzNBU.js} +1 -1
  37. package/console-app/assets/{erDiagram-JOGREHBK-BN3FevvT.js → erDiagram-JOGREHBK-3X9eTLsh.js} +1 -1
  38. package/console-app/assets/eventmodeling-45OFAUF4-BarrKD55.js +1 -0
  39. package/console-app/assets/flowDiagram-UKHOOZJN-B6RiRjcD.js +1 -0
  40. package/console-app/assets/{ganttDiagram-PKOTCBZU-03mWH2kB.js → ganttDiagram-PKOTCBZU-khAFwSBy.js} +1 -1
  41. package/console-app/assets/{gitGraph-TEB2WS4Q-By2486wn.js → gitGraph-TEB2WS4Q-Bb7uENmk.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-DS77QQ5N-CCzKPRBJ.js → gitGraphDiagram-DS77QQ5N-OkSBM0h0.js} +1 -1
  43. package/console-app/assets/{index-snBzz_U8.js → index-Bn1BoT3w.js} +184 -184
  44. package/console-app/assets/{info-DKCQHKI2-C_vsavqC.js → info-DKCQHKI2-BH5dbX2B.js} +1 -1
  45. package/console-app/assets/{infoDiagram-6WML65LV-BKvU8du7.js → infoDiagram-6WML65LV-IbUl8J2C.js} +1 -1
  46. package/console-app/assets/{ishikawaDiagram-WSZJBQD7-CnF0qm0n.js → ishikawaDiagram-WSZJBQD7-CX0gA3de.js} +1 -1
  47. package/console-app/assets/{journeyDiagram-NVQOT4AX-DxKe9OZ7.js → journeyDiagram-NVQOT4AX--k2etXu8.js} +1 -1
  48. package/console-app/assets/{kanban-definition-27J2QSJJ-CB8lgPxU.js → kanban-definition-27J2QSJJ-D1FvtPpN.js} +1 -1
  49. package/console-app/assets/{line-DFNLso1I.js → line-JLMY_DDK.js} +1 -1
  50. package/console-app/assets/{linear-D1cspVtk.js → linear-CSvLB3cd.js} +1 -1
  51. package/console-app/assets/{mermaid-parser.core-BtmN233C.js → mermaid-parser.core-CwwjcAjz.js} +3 -3
  52. package/console-app/assets/{mermaid.core-37H5_HoB.js → mermaid.core-hmS_M4K5.js} +4 -4
  53. package/console-app/assets/{mindmap-definition-FAOFIHXS-Czf82JOB.js → mindmap-definition-FAOFIHXS-rTDa_CTp.js} +1 -1
  54. package/console-app/assets/{packet-7NZHBO7P-CErYxAQM.js → packet-7NZHBO7P-DBUJcYfI.js} +1 -1
  55. package/console-app/assets/{pegDiagram-VL7TDLO6-CRKX6HOt.js → pegDiagram-VL7TDLO6-D42PxXnS.js} +1 -1
  56. package/console-app/assets/{pie-RZYD4A2V-YmMmh6Nq.js → pie-RZYD4A2V-dM0uFAE4.js} +1 -1
  57. package/console-app/assets/{pieDiagram-7S7Q4E2Y-Cot0Fu5q.js → pieDiagram-7S7Q4E2Y-wCln1ucl.js} +1 -1
  58. package/console-app/assets/{quadrantDiagram-CIZ2JOQS-DPEonVYT.js → quadrantDiagram-CIZ2JOQS-DrRwS1g5.js} +1 -1
  59. package/console-app/assets/{radar-I7S5WNFK-Pf_KiQJM.js → radar-I7S5WNFK-BW0V-EGR.js} +1 -1
  60. package/console-app/assets/{railroad-3IZDKUUU-BYoOMoQY.js → railroad-3IZDKUUU-FiTHaDbb.js} +1 -1
  61. package/console-app/assets/railroad-abnf-AHOZXSZD-lmUjOixE.js +1 -0
  62. package/console-app/assets/railroad-ebnf-EBAXGLYW-ZjdA7OHp.js +1 -0
  63. package/console-app/assets/railroad-peg-LSFZ7HO6-VHSlvwI8.js +1 -0
  64. package/console-app/assets/{railroadDiagram-AXF67PYL-Di8N9tHD.js → railroadDiagram-AXF67PYL-9ef7j5U7.js} +1 -1
  65. package/console-app/assets/{requirementDiagram-LRYGKXZP-2yZZ753q.js → requirementDiagram-LRYGKXZP-Cu90DbEZ.js} +1 -1
  66. package/console-app/assets/{sankeyDiagram-W5VNT64P-BwYA-xq1.js → sankeyDiagram-W5VNT64P-Bhxmyxaw.js} +1 -1
  67. package/console-app/assets/{sequenceDiagram-SI44F4Z6-odIxlx8J.js → sequenceDiagram-SI44F4Z6-CBsbRTz8.js} +1 -1
  68. package/console-app/assets/{src-CxJwpjvz.js → src-BR8qR3lM.js} +1 -1
  69. package/console-app/assets/{stateDiagram-OKZ733FA-Du2S0qU9.js → stateDiagram-OKZ733FA-Dr0frETZ.js} +1 -1
  70. package/console-app/assets/stateDiagram-v2-UEYNNEHI-BSXWhCvf.js +1 -0
  71. package/console-app/assets/{swimlanes-SLNWSIFB-JZ3swzI2.js → swimlanes-SLNWSIFB-CXMjvd_a.js} +1 -1
  72. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-BUdsPDKC.js +8 -0
  73. package/console-app/assets/{timeline-definition-Z64GVDOM-CsppAEWi.js → timeline-definition-Z64GVDOM-DpmEZPV0.js} +1 -1
  74. package/console-app/assets/{treeView-QDETBFTQ-DkkO8peQ.js → treeView-QDETBFTQ-CjmuinMk.js} +1 -1
  75. package/console-app/assets/{treemap-6X3UGDF4-hjLkPCJI.js → treemap-6X3UGDF4-BP_fU6Cn.js} +1 -1
  76. package/console-app/assets/{vennDiagram-T6HMQDX7-BOfgM1xp.js → vennDiagram-T6HMQDX7-DgZS7iuW.js} +1 -1
  77. package/console-app/assets/{wardley-OPB4EBWU-C5ZtGfBa.js → wardley-OPB4EBWU-CgMPoouN.js} +1 -1
  78. package/console-app/assets/{wardleyDiagram-T6FBY63Y-ymf3Vm6A.js → wardleyDiagram-T6FBY63Y-6y-xxP1R.js} +1 -1
  79. package/console-app/assets/{xychartDiagram-ELKLHX3M-DdxXIoKz.js → xychartDiagram-ELKLHX3M-DOeIQjYI.js} +1 -1
  80. package/console-app/index.html +1 -1
  81. package/dist/.pikku/addon/index.d.ts +1 -1
  82. package/dist/.pikku/addon/index.js +1 -1
  83. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  84. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  85. package/dist/.pikku/agent/index.d.ts +1 -1
  86. package/dist/.pikku/agent/index.js +1 -1
  87. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  88. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  89. package/dist/.pikku/auth/index.d.ts +1 -1
  90. package/dist/.pikku/auth/index.js +1 -1
  91. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +9 -2
  92. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  93. package/dist/.pikku/channel/index.d.ts +1 -1
  94. package/dist/.pikku/channel/index.js +1 -1
  95. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  96. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  97. package/dist/.pikku/cli/index.d.ts +1 -1
  98. package/dist/.pikku/cli/index.js +1 -1
  99. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  100. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  101. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  102. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  103. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  104. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  105. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  106. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  107. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  108. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  109. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  110. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  111. package/dist/.pikku/error/index.d.ts +1 -1
  112. package/dist/.pikku/error/index.js +1 -1
  113. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  114. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  115. package/dist/.pikku/function/index.d.ts +1 -1
  116. package/dist/.pikku/function/index.js +1 -1
  117. package/dist/.pikku/function/pikku-function-types.gen.d.ts +12 -2
  118. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  119. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  120. package/dist/.pikku/function/pikku-functions-meta.gen.json +3 -3
  121. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  122. package/dist/.pikku/gateway/index.d.ts +1 -1
  123. package/dist/.pikku/gateway/index.js +1 -1
  124. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  125. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  126. package/dist/.pikku/http/index.d.ts +1 -1
  127. package/dist/.pikku/http/index.js +1 -1
  128. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  129. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  130. package/dist/.pikku/mcp/index.d.ts +1 -1
  131. package/dist/.pikku/mcp/index.js +1 -1
  132. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  133. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  134. package/dist/.pikku/middleware/index.d.ts +1 -1
  135. package/dist/.pikku/middleware/index.js +1 -1
  136. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +9 -2
  137. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  138. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  139. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  140. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  141. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  142. package/dist/.pikku/pikku-services.gen.d.ts +2 -1
  143. package/dist/.pikku/pikku-services.gen.js +1 -0
  144. package/dist/.pikku/queue/index.d.ts +1 -1
  145. package/dist/.pikku/queue/index.js +1 -1
  146. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  147. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  148. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  149. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  150. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  151. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  152. package/dist/.pikku/scenarios/index.d.ts +1 -1
  153. package/dist/.pikku/scenarios/index.js +1 -1
  154. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  155. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  156. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  157. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  158. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  159. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  160. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  161. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  162. package/dist/.pikku/scheduler/index.d.ts +1 -1
  163. package/dist/.pikku/scheduler/index.js +1 -1
  164. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  165. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  166. package/dist/.pikku/schemas/register.gen.js +1 -1
  167. package/dist/.pikku/scopes/index.d.ts +1 -1
  168. package/dist/.pikku/scopes/index.js +1 -1
  169. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  170. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  171. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  172. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  173. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  174. package/dist/.pikku/secrets/index.d.ts +1 -1
  175. package/dist/.pikku/secrets/index.js +1 -1
  176. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  177. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  178. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  179. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  180. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  181. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  182. package/dist/.pikku/setup/index.d.ts +1 -1
  183. package/dist/.pikku/setup/index.js +1 -1
  184. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  185. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  186. package/dist/.pikku/trigger/index.d.ts +1 -1
  187. package/dist/.pikku/trigger/index.js +1 -1
  188. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  189. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  190. package/dist/.pikku/variables/index.d.ts +1 -1
  191. package/dist/.pikku/variables/index.js +1 -1
  192. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  193. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  194. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  195. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  196. package/dist/.pikku/workflow/index.d.ts +1 -1
  197. package/dist/.pikku/workflow/index.js +1 -1
  198. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  199. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  200. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  201. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  202. package/dist/bin/pikku-bin.mjs +2 -2
  203. package/dist/src/deploy/analyzer/analyzer.d.ts +1 -1
  204. package/dist/src/deploy/analyzer/index.d.ts +0 -1
  205. package/dist/src/deploy/build-pipeline.d.ts +2 -2
  206. package/dist/src/deploy/bundler/types.d.ts +1 -1
  207. package/dist/src/deploy/codegen/per-unit-codegen.d.ts +1 -1
  208. package/dist/src/deploy/plan/executor.d.ts +1 -1
  209. package/dist/src/deploy/plan/planner.d.ts +1 -1
  210. package/dist/src/deploy/plan/provider.d.ts +1 -1
  211. package/dist/src/deploy/server-entry.d.ts +1 -1
  212. package/dist/src/fabric/fabric-commands.d.ts +3 -3
  213. package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
  214. package/dist/src/functions/commands/db-generate.js +1 -1
  215. package/dist/src/functions/commands/deploy-apply.d.ts +1 -1
  216. package/dist/src/functions/commands/new-addon.js +14 -12
  217. package/dist/src/functions/commands/persona.js +6 -0
  218. package/dist/src/functions/db/better-auth-schema.js +5 -0
  219. package/dist/src/functions/db/local-db.d.ts +11 -3
  220. package/dist/src/functions/db/local-db.js +17 -8
  221. package/dist/src/functions/wirings/auth/serialize-auth-guards.d.ts +1 -1
  222. package/dist/src/functions/wirings/auth/serialize-auth-guards.js +9 -2
  223. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.js +2 -2
  224. package/dist/src/functions/wirings/functions/serialize-function-types.js +11 -1
  225. package/dist/src/functions/wirings/middleware/serialize-middleware-types.d.ts +1 -1
  226. package/dist/src/functions/wirings/middleware/serialize-middleware-types.js +10 -2
  227. package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +485 -102
  228. package/dist/src/utils/pikku-cli-config-error.d.ts +9 -0
  229. package/dist/src/utils/pikku-cli-config-error.js +9 -0
  230. package/dist/src/utils/pikku-cli-config.d.ts +2 -3
  231. package/dist/src/utils/pikku-cli-config.js +2 -3
  232. package/dist/src/utils/resolve-scaffold-feature.d.ts +6 -0
  233. package/dist/src/utils/resolve-scaffold-feature.js +22 -2
  234. package/dist/tsconfig.tsbuildinfo +1 -1
  235. package/package.json +8 -7
  236. package/surface.json +4 -26
  237. package/console-app/assets/architecture-TIHT7OUA-5QhJKy4P.js +0 -1
  238. package/console-app/assets/channel-DoE3nnYB.js +0 -1
  239. package/console-app/assets/chunk-XXDRQBXY-BDALKVU4.js +0 -1
  240. package/console-app/assets/classDiagram-JCYQIIEL-BgrrHQtN.js +0 -1
  241. package/console-app/assets/classDiagram-v2-OCEON4UE-BgrrHQtN.js +0 -1
  242. package/console-app/assets/eventmodeling-45OFAUF4-D2SxzJUl.js +0 -1
  243. package/console-app/assets/flowDiagram-UKHOOZJN-CQXuJ0z2.js +0 -1
  244. package/console-app/assets/railroad-abnf-AHOZXSZD-CCRXsRRD.js +0 -1
  245. package/console-app/assets/railroad-ebnf-EBAXGLYW-DVRSVgn8.js +0 -1
  246. package/console-app/assets/railroad-peg-LSFZ7HO6-COUBoNH3.js +0 -1
  247. package/console-app/assets/stateDiagram-v2-UEYNNEHI-CTXOGDRR.js +0 -1
  248. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-DRrggQgK.js +0 -8
  249. package/dist/src/deploy/analyzer/manifest.d.ts +0 -170
  250. package/dist/src/deploy/analyzer/manifest.js +0 -8
  251. package/dist/src/deploy/provider-adapter.d.ts +0 -176
  252. package/dist/src/deploy/provider-adapter.js +0 -10
@@ -39,6 +39,16 @@ export const Disposition = z.enum([
39
39
  'accountable',
40
40
  ])
41
41
 
42
+ /**
43
+ * Where a run stops. Not a schedule: these cap one outing, and how often the
44
+ * outings happen is \`setVirtualUserSchedule\`'s business.
45
+ */
46
+ export const Budget = z.object({
47
+ steps: z.number().int().min(1).max(500).optional(),
48
+ mutations: z.number().int().min(0).max(500).optional(),
49
+ durationMs: z.number().int().min(1000).max(3_600_000).optional(),
50
+ })
51
+
42
52
  export const RunVirtualUserInput = z.object({
43
53
  /** A persona id from \`definePersonas()\`. */
44
54
  persona: z.string().min(1),
@@ -56,13 +66,7 @@ export const RunVirtualUserInput = z.object({
56
66
  memory: z.record(z.string(), z.string()).optional(),
57
67
  /** Overrides the persona's declared disposition for this run only. */
58
68
  disposition: Disposition.optional(),
59
- budget: z
60
- .object({
61
- steps: z.number().int().min(1).max(500).optional(),
62
- mutations: z.number().int().min(0).max(500).optional(),
63
- durationMs: z.number().int().min(1000).max(3_600_000).optional(),
64
- })
65
- .optional(),
69
+ budget: Budget.optional(),
66
70
  /** Fixed seed, so a run replays into the same finding. */
67
71
  seed: z.number().int().optional(),
68
72
  })
@@ -90,7 +94,45 @@ export const Finding = z.object({
90
94
  step: z.number(),
91
95
  })
92
96
 
93
- export const GetVirtualUserRunOutput = z.object({
97
+ /**
98
+ * What the user set out to do, and how far each one got. The spine a transcript
99
+ * hangs off — the steps alone are a list of calls with no account of what they
100
+ * were for.
101
+ */
102
+ export const Intent = z.object({
103
+ id: z.string(),
104
+ sourceId: z.string(),
105
+ title: z.string(),
106
+ /** open | suspended | completed | abandoned | stuck, open-ended for the same
107
+ * reason a finding's \`kind\` is. */
108
+ status: z.string(),
109
+ /** Step indices at which this intent was the active one. */
110
+ steps: z.array(z.number()),
111
+ /** How many times it was put down and picked back up. */
112
+ suspensions: z.number(),
113
+ summary: z.string().optional(),
114
+ })
115
+
116
+ /** One turn: what the engine scheduled, what the model did, what came back. */
117
+ export const Step = z.object({
118
+ index: z.number(),
119
+ intentId: z.string().optional(),
120
+ /** The call it chose, or the \`invalid\` shape for a turn it got wrong. */
121
+ action: z.record(z.string(), z.unknown()),
122
+ status: z.number().optional(),
123
+ ok: z.boolean().optional(),
124
+ /** Truncated by the engine, so a transcript stays readable. */
125
+ response: z.string().optional(),
126
+ findingKinds: z.array(z.string()).optional(),
127
+ tokensIn: z.number(),
128
+ tokensOut: z.number(),
129
+ })
130
+
131
+ /**
132
+ * Shared so a run reads the same whether it arrived one at a time or in a list.
133
+ * Not exported: the schemas the inspector names are the ones a function wires.
134
+ */
135
+ const virtualUserRunFields = {
94
136
  runId: z.string(),
95
137
  persona: z.string(),
96
138
  disposition: Disposition,
@@ -99,12 +141,77 @@ export const GetVirtualUserRunOutput = z.object({
99
141
  goals: z.array(z.string()),
100
142
  memory: z.record(z.string(), z.string()),
101
143
  findings: z.array(Finding),
144
+ intents: z.array(Intent),
102
145
  /** Steps, calls, mutations, tokens and elapsed time, as the engine counted them. */
103
146
  tally: z.record(z.string(), z.unknown()).nullable(),
104
147
  stoppedBy: z.string().nullable(),
105
148
  error: z.string().nullable(),
106
149
  createdAt: z.string(),
107
150
  finishedAt: z.string().nullable(),
151
+ }
152
+
153
+ export const GetVirtualUserRunOutput = z.object(virtualUserRunFields)
154
+
155
+ export const ListVirtualUserRunsInput = z.object({
156
+ /** Narrows to one persona's history. */
157
+ persona: z.string().min(1).optional(),
158
+ limit: z.number().int().min(1).max(100).optional(),
159
+ offset: z.number().int().min(0).optional(),
160
+ })
161
+
162
+ export const ListVirtualUserRunsOutput = z.object({
163
+ /** Newest first. The transcript is not here — read it with getVirtualUserRunSteps. */
164
+ runs: z.array(z.object(virtualUserRunFields)),
165
+ })
166
+
167
+ export const GetVirtualUserRunStepsInput = z.object({
168
+ runId: z.string(),
169
+ limit: z.number().int().min(1).max(500).optional(),
170
+ offset: z.number().int().min(0).optional(),
171
+ })
172
+
173
+ export const GetVirtualUserRunStepsOutput = z.object({
174
+ steps: z.array(Step),
175
+ })
176
+
177
+ /**
178
+ * One persona's standing instruction to keep using the app.
179
+ *
180
+ * The interval is a range rather than a number: a user who arrives at exactly
181
+ * 09:00 every day exercises one cache state and one cron neighbourhood, and a
182
+ * real one does not keep an appointment.
183
+ */
184
+ export const SetVirtualUserScheduleInput = z.object({
185
+ persona: z.string().min(1),
186
+ /** Off until something says otherwise — a schedule costs money on every tick. */
187
+ enabled: z.boolean().optional(),
188
+ disposition: Disposition.optional(),
189
+ goals: z.array(z.string().min(1)).max(20).optional(),
190
+ budget: Budget.nullable().optional(),
191
+ minIntervalMs: z.number().int().min(60_000).optional(),
192
+ maxIntervalMs: z.number().int().min(60_000).optional(),
193
+ /** ISO. Omitted on a new schedule means the persona is due at once. */
194
+ nextRunAt: z.string().optional(),
195
+ })
196
+
197
+ /** Not exported for the same reason the run fields are not. */
198
+ const virtualUserScheduleFields = {
199
+ persona: z.string(),
200
+ enabled: z.boolean(),
201
+ disposition: Disposition,
202
+ goals: z.array(z.string()),
203
+ budget: Budget.nullable(),
204
+ minIntervalMs: z.number(),
205
+ maxIntervalMs: z.number(),
206
+ nextRunAt: z.string(),
207
+ lastRunId: z.string().nullable(),
208
+ lastRunAt: z.string().nullable(),
209
+ }
210
+
211
+ export const SetVirtualUserScheduleOutput = z.object(virtualUserScheduleFields)
212
+
213
+ export const ListVirtualUserSchedulesOutput = z.object({
214
+ schedules: z.array(z.object(virtualUserScheduleFields)),
108
215
  })
109
216
 
110
217
  /** The internal dispatch payload — everything the run was resolved down to. */
@@ -138,6 +245,10 @@ import { personaVirtualUserTarget, runVirtualUser as runVirtualUserEngine, type
138
245
  import {
139
246
  prepareVirtualUserRun,
140
247
  PRODUCTION_DISPOSITION,
248
+ tickVirtualUserSchedules as tick,
249
+ type VirtualUserRunRecord,
250
+ type VirtualUserRunStore,
251
+ type VirtualUserScheduleRecord,
141
252
  } from '@pikku/core/virtual-user'
142
253
  import { createPersonas, personaConfigs } from '${pathToPersonas}'
143
254
  import {
@@ -145,8 +256,15 @@ import {
145
256
  ExecuteVirtualUserRunOutput,
146
257
  GetVirtualUserRunInput,
147
258
  GetVirtualUserRunOutput,
259
+ GetVirtualUserRunStepsInput,
260
+ GetVirtualUserRunStepsOutput,
261
+ ListVirtualUserRunsInput,
262
+ ListVirtualUserRunsOutput,
263
+ ListVirtualUserSchedulesOutput,
148
264
  RunVirtualUserInput,
149
265
  RunVirtualUserOutput,
266
+ SetVirtualUserScheduleInput,
267
+ SetVirtualUserScheduleOutput,
150
268
  } from './virtual-user.schemas.gen.js'
151
269
 
152
270
  defineScope({
@@ -156,6 +274,9 @@ defineScope({
156
274
  scopes: {
157
275
  run: { description: 'Start a virtual user run' },
158
276
  read: { description: "Read a run's findings" },
277
+ schedule: {
278
+ description: 'Decide that a persona keeps running on its own',
279
+ },
159
280
  },
160
281
  },
161
282
  })
@@ -170,89 +291,156 @@ const API_URL_VARIABLE = 'VIRTUAL_USER_API_URL'
170
291
  const SECRET_VARIABLE = 'SCENARIO_ACTOR_SECRET'
171
292
  const MODEL_VARIABLE = 'VIRTUAL_USER_MODEL'
172
293
 
173
- export const runVirtualUser = pikkuFunc({
174
- tags: ['pikku'],
175
- title: 'Run a Virtual User',
176
- description:
177
- 'Turns a declared persona loose on this application and records what it finds. Returns immediately with a run id; read the result back with getVirtualUserRun.',
178
- expose: true,
179
- scopes: ['virtualUser:run'],
180
- input: RunVirtualUserInput,
181
- output: RunVirtualUserOutput,
182
- func: async (
183
- { virtualUserRunStore, config },
184
- input,
185
- { session, rpc }
186
- ) => {
187
- if (!virtualUserRunStore) {
188
- throw new Error(
189
- 'No virtualUserRunStore is wired — a run has nowhere to be recorded. ' +
190
- 'Wire KyselyVirtualUserRunStore from @pikku/kysely, or your own implementation of VirtualUserRunStore.'
191
- )
192
- }
294
+ /**
295
+ * One run on the wire. Findings and intents are free-form by design — the
296
+ * engine records what it noticed, not a fixed row shape — so they cross as the
297
+ * schema's open objects rather than being narrowed to whatever kinds exist
298
+ * today.
299
+ */
300
+ const serializeRun = (run: VirtualUserRunRecord) => ({
301
+ runId: run.runId,
302
+ persona: run.persona,
303
+ disposition: run.disposition,
304
+ seed: run.seed,
305
+ status: run.status,
306
+ goals: run.goals,
307
+ memory: run.memory,
308
+ findings: run.findings.map((finding) => ({
309
+ kind: finding.kind as string,
310
+ detail: finding.detail,
311
+ rpcName: finding.rpcName,
312
+ status: finding.status,
313
+ intentId: finding.intentId,
314
+ step: finding.step,
315
+ })),
316
+ intents: run.intents.map((intent) => ({
317
+ id: intent.id,
318
+ sourceId: intent.sourceId,
319
+ title: intent.title,
320
+ status: intent.status as string,
321
+ steps: intent.steps,
322
+ suspensions: intent.suspensions,
323
+ summary: intent.summary,
324
+ })),
325
+ tally: (run.tally ?? null) as Record<string, unknown> | null,
326
+ stoppedBy: run.stoppedBy,
327
+ error: run.error,
328
+ createdAt: run.createdAt.toISOString(),
329
+ finishedAt: run.finishedAt ? run.finishedAt.toISOString() : null,
330
+ })
193
331
 
194
- const persona = personaConfigs[input.persona as keyof typeof personaConfigs] as
195
- | { id: string; runnable: boolean; disposition?: string }
196
- | undefined
197
- if (!persona) {
198
- throw new Error(
199
- \`Unknown persona "\${input.persona}" declare it with definePersonas()\`
200
- )
201
- }
202
- // An acted-upon persona has no session of its own, and running one would
203
- // race whatever scenario acts on it.
204
- if (!persona.runnable) {
205
- throw new Error(
206
- \`Persona "\${input.persona}" is declared as acted upon, never run\`
207
- )
208
- }
332
+ /**
333
+ * Resolves a persona down to a run and kicks it off, for the two callers that
334
+ * start one: a person with the scope, and the tick acting on a schedule.
335
+ *
336
+ * Shared rather than duplicated because the checks are the point — an acted-upon
337
+ * persona has no session, and only one disposition may ever touch production.
338
+ * A second copy of that would eventually disagree with this one.
339
+ */
340
+ const startVirtualUserRun = async (
341
+ services: {
342
+ virtualUserRunStore?: VirtualUserRunStore
343
+ config: { nodeEnv?: string }
344
+ },
345
+ rpc: { invoke: (name: string, data: any) => Promise<unknown> },
346
+ input: {
347
+ persona: string
348
+ disposition?: string
349
+ goals?: string[]
350
+ memory?: Record<string, string>
351
+ budget?: { steps?: number; mutations?: number; durationMs?: number }
352
+ seed?: number
353
+ startedBy?: string | null
354
+ }
355
+ ): Promise<string> => {
356
+ const { virtualUserRunStore, config } = services
357
+ if (!virtualUserRunStore) {
358
+ throw new Error(
359
+ 'No virtualUserRunStore is wired — a run has nowhere to be recorded. ' +
360
+ 'Wire KyselyVirtualUserRunStore from @pikku/kysely, or your own implementation of VirtualUserRunStore.'
361
+ )
362
+ }
209
363
 
210
- const disposition = (input.disposition ??
211
- persona.disposition ??
212
- 'realistic') as VirtualUserDisposition
213
- // Every disposition other than this one exists to find out what the product
214
- // does wrong, which is not a thing to do to real customers' data. Checked
215
- // against the effective disposition, so the override cannot smuggle one in.
216
- if (
217
- config.nodeEnv === 'production' &&
218
- disposition !== PRODUCTION_DISPOSITION
219
- ) {
220
- throw new Error(
221
- \`Only the '\${PRODUCTION_DISPOSITION}' disposition may run against production; "\${input.persona}" is \${disposition}\`
222
- )
223
- }
364
+ const persona = personaConfigs[input.persona as keyof typeof personaConfigs] as
365
+ | { id: string; runnable: boolean; disposition?: string }
366
+ | undefined
367
+ if (!persona) {
368
+ throw new Error(
369
+ \`Unknown persona "\${input.persona}" declare it with definePersonas()\`
370
+ )
371
+ }
372
+ // An acted-upon persona has no session of its own, and running one would
373
+ // race whatever scenario acts on it.
374
+ if (!persona.runnable) {
375
+ throw new Error(
376
+ \`Persona "\${input.persona}" is declared as acted upon, never run\`
377
+ )
378
+ }
379
+
380
+ const disposition = (input.disposition ??
381
+ persona.disposition ??
382
+ 'realistic') as VirtualUserDisposition
383
+ // Every disposition other than this one exists to find out what the product
384
+ // does wrong, which is not a thing to do to real customers' data. Checked
385
+ // against the effective disposition, so the override cannot smuggle one in.
386
+ if (
387
+ config.nodeEnv === 'production' &&
388
+ disposition !== PRODUCTION_DISPOSITION
389
+ ) {
390
+ throw new Error(
391
+ \`Only the '\${PRODUCTION_DISPOSITION}' disposition may run against production; "\${input.persona}" is \${disposition}\`
392
+ )
393
+ }
394
+
395
+ // Seeded here rather than inside the engine so the record carries the seed
396
+ // even if the run dies before returning — an unreproducible crash costs the
397
+ // most.
398
+ const seed = input.seed ?? Math.floor(Math.random() * 2_147_483_647)
224
399
 
225
- // Seeded here rather than inside the engine so the record carries the seed
226
- // even if the run dies before returning — an unreproducible crash costs the
227
- // most.
228
- const seed = input.seed ?? Math.floor(Math.random() * 2_147_483_647)
400
+ const runId = await virtualUserRunStore.start({
401
+ persona: persona.id,
402
+ disposition,
403
+ seed,
404
+ goals: input.goals ?? [],
405
+ memory: input.memory ?? {},
406
+ startedBy: input.startedBy ?? null,
407
+ })
229
408
 
230
- const runId = await virtualUserRunStore.start({
409
+ // Deliberately not awaited: a run takes minutes, and a held-open request
410
+ // survives neither a rollout nor a proxy timeout. executeVirtualUserRun
411
+ // writes both outcomes to the record itself, so the only thing left to
412
+ // handle here is a rejection escaping the promise — without the catch it
413
+ // becomes an unhandled rejection and takes the process with it.
414
+ void rpc
415
+ .invoke('executeVirtualUserRun', {
416
+ runId,
231
417
  persona: persona.id,
232
418
  disposition,
233
- seed,
234
419
  goals: input.goals ?? [],
235
420
  memory: input.memory ?? {},
236
- startedBy: session?.userId ?? null,
421
+ budget: input.budget,
422
+ seed,
237
423
  })
424
+ .catch(() => {})
238
425
 
239
- // Deliberately not awaited: a run takes minutes, and a held-open request
240
- // survives neither a rollout nor a proxy timeout. executeVirtualUserRun
241
- // writes both outcomes to the record itself, so the only thing left to
242
- // handle here is a rejection escaping the promise — without the catch it
243
- // becomes an unhandled rejection and takes the process with it.
244
- void rpc!
245
- .invoke('executeVirtualUserRun', {
246
- runId,
247
- persona: persona.id,
248
- disposition,
249
- goals: input.goals ?? [],
250
- memory: input.memory ?? {},
251
- budget: input.budget,
252
- seed,
253
- })
254
- .catch(() => {})
426
+ return runId
427
+ }
255
428
 
429
+ export const runVirtualUser = pikkuFunc({
430
+ tags: ['pikku'],
431
+ title: 'Run a Virtual User',
432
+ description:
433
+ 'Turns a declared persona loose on this application and records what it finds. Returns immediately with a run id; read the result back with getVirtualUserRun.',
434
+ expose: true,
435
+ scopes: ['virtualUser:run'],
436
+ input: RunVirtualUserInput,
437
+ output: RunVirtualUserOutput,
438
+ func: async ({ virtualUserRunStore, config }, input, { session, rpc }) => {
439
+ const runId = await startVirtualUserRun(
440
+ { virtualUserRunStore, config },
441
+ rpc!,
442
+ { ...input, startedBy: session?.userId ?? null }
443
+ )
256
444
  return { runId }
257
445
  },
258
446
  })
@@ -277,30 +465,223 @@ export const getVirtualUserRun = pikkuFunc({
277
465
  if (!run) {
278
466
  throw new Error(\`No virtual user run \${runId}\`)
279
467
  }
468
+ return serializeRun(run)
469
+ },
470
+ })
471
+
472
+ export const listVirtualUserRuns = pikkuFunc({
473
+ tags: ['pikku'],
474
+ title: 'List Virtual User Runs',
475
+ description:
476
+ "Reads back what the virtual users have been doing, newest first. Narrow with \`persona\` for one user's history.",
477
+ expose: true,
478
+ scopes: ['virtualUser:read'],
479
+ input: ListVirtualUserRunsInput,
480
+ output: ListVirtualUserRunsOutput,
481
+ func: async ({ virtualUserRunStore }, { persona, limit, offset }) => {
482
+ if (!virtualUserRunStore) {
483
+ throw new Error('No virtualUserRunStore is wired — there are no runs to read.')
484
+ }
485
+ const runs = await virtualUserRunStore.list({ persona, limit, offset })
486
+ return { runs: runs.map(serializeRun) }
487
+ },
488
+ })
489
+
490
+ export const getVirtualUserRunSteps = pikkuFunc({
491
+ tags: ['pikku'],
492
+ title: 'Read a Virtual User Transcript',
493
+ description:
494
+ 'Every turn one run took, in order: what it called, what came back, and what that cost.',
495
+ expose: true,
496
+ // The transcript is strictly more sensitive than the summary it belongs to —
497
+ // it carries the live ids and payloads the run actually sent — so it sits
498
+ // behind the same read scope rather than a looser one.
499
+ scopes: ['virtualUser:read'],
500
+ input: GetVirtualUserRunStepsInput,
501
+ output: GetVirtualUserRunStepsOutput,
502
+ func: async ({ virtualUserRunStore }, { runId, limit, offset }) => {
503
+ if (!virtualUserRunStore) {
504
+ throw new Error('No virtualUserRunStore is wired — there are no runs to read.')
505
+ }
506
+ const steps = await virtualUserRunStore.steps(runId, { limit, offset })
280
507
  return {
281
- runId: run.runId,
282
- persona: run.persona,
283
- disposition: run.disposition,
284
- seed: run.seed,
285
- status: run.status,
286
- goals: run.goals,
287
- memory: run.memory,
288
- // Findings are free-form by design — the engine records what it noticed,
289
- // not a fixed row shape — so they cross the wire as the schema's open
290
- // object rather than being narrowed to whatever kinds exist today.
291
- findings: run.findings.map((finding) => ({
292
- kind: finding.kind as string,
293
- detail: finding.detail,
294
- rpcName: finding.rpcName,
295
- status: finding.status,
296
- intentId: finding.intentId,
297
- step: finding.step,
508
+ steps: steps.map((step) => ({
509
+ index: step.index,
510
+ intentId: step.intentId,
511
+ action: step.action as unknown as Record<string, unknown>,
512
+ status: step.status,
513
+ ok: step.ok,
514
+ response: step.response,
515
+ findingKinds: step.findingKinds as string[] | undefined,
516
+ tokensIn: step.tokensIn,
517
+ tokensOut: step.tokensOut,
298
518
  })),
299
- tally: (run.tally ?? null) as Record<string, unknown> | null,
300
- stoppedBy: run.stoppedBy,
301
- error: run.error,
302
- createdAt: run.createdAt.toISOString(),
303
- finishedAt: run.finishedAt ? run.finishedAt.toISOString() : null,
519
+ }
520
+ },
521
+ })
522
+
523
+ /**
524
+ * One schedule on the wire. The budget crosses as \`durationMs\` because that is
525
+ * what every other call here takes; the engine's own duration also accepts
526
+ * \`'30m'\`, which nothing on this side ever writes.
527
+ */
528
+ const serializeSchedule = (schedule: VirtualUserScheduleRecord) => ({
529
+ persona: schedule.persona,
530
+ enabled: schedule.enabled,
531
+ disposition: schedule.disposition,
532
+ goals: schedule.goals,
533
+ budget: schedule.budget
534
+ ? {
535
+ steps: schedule.budget.steps,
536
+ mutations: schedule.budget.mutations,
537
+ durationMs:
538
+ typeof schedule.budget.duration === 'number'
539
+ ? schedule.budget.duration
540
+ : undefined,
541
+ }
542
+ : null,
543
+ minIntervalMs: schedule.minIntervalMs,
544
+ maxIntervalMs: schedule.maxIntervalMs,
545
+ nextRunAt: schedule.nextRunAt.toISOString(),
546
+ lastRunId: schedule.lastRunId,
547
+ lastRunAt: schedule.lastRunAt ? schedule.lastRunAt.toISOString() : null,
548
+ })
549
+
550
+ export const setVirtualUserSchedule = pikkuFunc({
551
+ tags: ['pikku'],
552
+ title: 'Put a Virtual User on a Clock',
553
+ description:
554
+ 'Says how often a persona should use the application on its own. Off until enabled, and every field left out keeps what it already had.',
555
+ expose: true,
556
+ // Its own scope, and the most powerful one here: starting a run spends money
557
+ // once with a caller present to see it, while writing a schedule spends it
558
+ // repeatedly with nobody there.
559
+ scopes: ['virtualUser:schedule'],
560
+ input: SetVirtualUserScheduleInput,
561
+ output: SetVirtualUserScheduleOutput,
562
+ func: async ({ virtualUserScheduleStore }, input) => {
563
+ if (!virtualUserScheduleStore) {
564
+ throw new Error(
565
+ 'No virtualUserScheduleStore is wired — a cadence has nowhere to live. ' +
566
+ 'Wire KyselyVirtualUserScheduleStore from @pikku/kysely, or your own implementation of VirtualUserScheduleStore.'
567
+ )
568
+ }
569
+ const persona = personaConfigs[
570
+ input.persona as keyof typeof personaConfigs
571
+ ] as { runnable: boolean } | undefined
572
+ if (!persona) {
573
+ throw new Error(
574
+ \`Unknown persona "\${input.persona}" — declare it with definePersonas()\`
575
+ )
576
+ }
577
+ // The same rule \`runVirtualUser\` enforces, applied at the point the row is
578
+ // written rather than every hour afterwards: an acted-upon persona has no
579
+ // session, so a cadence for one is a tick that can only ever fail to start.
580
+ if (!persona.runnable) {
581
+ throw new Error(
582
+ \`Persona "\${input.persona}" is declared as acted upon, never run\`
583
+ )
584
+ }
585
+ const schedule = await virtualUserScheduleStore.set({
586
+ persona: input.persona,
587
+ enabled: input.enabled,
588
+ disposition: input.disposition as VirtualUserDisposition | undefined,
589
+ goals: input.goals,
590
+ budget:
591
+ input.budget === undefined
592
+ ? undefined
593
+ : input.budget === null
594
+ ? null
595
+ : {
596
+ steps: input.budget.steps,
597
+ mutations: input.budget.mutations,
598
+ duration: input.budget.durationMs,
599
+ },
600
+ minIntervalMs: input.minIntervalMs,
601
+ maxIntervalMs: input.maxIntervalMs,
602
+ nextRunAt: input.nextRunAt ? new Date(input.nextRunAt) : undefined,
603
+ })
604
+ return serializeSchedule(schedule)
605
+ },
606
+ })
607
+
608
+ export const listVirtualUserSchedules = pikkuFunc({
609
+ tags: ['pikku'],
610
+ title: 'List Virtual User Schedules',
611
+ description:
612
+ 'Which personas are on a clock, how often they run, and when each is next due.',
613
+ expose: true,
614
+ scopes: ['virtualUser:read'],
615
+ input: null,
616
+ output: ListVirtualUserSchedulesOutput,
617
+ func: async ({ virtualUserScheduleStore }) => {
618
+ if (!virtualUserScheduleStore) {
619
+ return { schedules: [] }
620
+ }
621
+ const schedules = await virtualUserScheduleStore.list()
622
+ return { schedules: schedules.map(serializeSchedule) }
623
+ },
624
+ })
625
+
626
+ /**
627
+ * Acts on whichever personas are due, once.
628
+ *
629
+ * Not exposed, and not wired to anything: pikku does not start a timer on an
630
+ * application's behalf. A scaffolded scheduled task would begin spending model
631
+ * budget the moment a project ran \`pikku all\`, which is not a thing a codegen
632
+ * step gets to decide. Wire it when you mean it:
633
+ *
634
+ * \`\`\`ts
635
+ * wireScheduler({
636
+ * name: 'virtualUsers',
637
+ * schedule: '0 * * * *',
638
+ * func: tickVirtualUserSchedules,
639
+ * })
640
+ * \`\`\`
641
+ *
642
+ * An hourly tick is plenty for intervals measured in hours: the tick
643
+ * decides nothing except which rows are already due, so running it more often
644
+ * costs a query and changes no cadence.
645
+ */
646
+ export const tickVirtualUserSchedules = pikkuSessionlessFunc<void, void>({
647
+ tags: ['pikku'],
648
+ func: async (services, _data, { rpc }) => {
649
+ const { virtualUserScheduleStore, virtualUserRunStore, logger } = services
650
+ if (!virtualUserScheduleStore || !virtualUserRunStore) {
651
+ return
652
+ }
653
+ const result = await tick({
654
+ schedules: virtualUserScheduleStore,
655
+ runs: virtualUserRunStore,
656
+ dispatch: (schedule) =>
657
+ startVirtualUserRun(services, rpc!, {
658
+ persona: schedule.persona,
659
+ disposition: schedule.disposition,
660
+ goals: schedule.goals,
661
+ budget: schedule.budget
662
+ ? {
663
+ steps: schedule.budget.steps,
664
+ mutations: schedule.budget.mutations,
665
+ durationMs:
666
+ typeof schedule.budget.duration === 'number'
667
+ ? schedule.budget.duration
668
+ : undefined,
669
+ }
670
+ : undefined,
671
+ }),
672
+ })
673
+ // Logged rather than returned: the caller is a cron, and a run this started
674
+ // is otherwise the only trace that a persona is still out there working.
675
+ for (const { persona, runId } of result.dispatched) {
676
+ logger.info(\`Virtual user \${persona} started run \${runId} on schedule\`)
677
+ }
678
+ for (const runId of result.reaped) {
679
+ logger.warn(
680
+ \`Virtual user run \${runId} was abandoned — marked failed so its persona can run again\`
681
+ )
682
+ }
683
+ for (const { persona, reason } of result.skipped) {
684
+ logger.info(\`Virtual user \${persona} skipped this tick: \${reason}\`)
304
685
  }
305
686
  },
306
687
  })
@@ -415,6 +796,8 @@ export const executeVirtualUserRun = pikkuSessionlessFunc({
415
796
  tally: result.tally,
416
797
  memory: result.memory,
417
798
  stoppedBy: result.stoppedBy ?? null,
799
+ intents: result.intents,
800
+ steps: result.steps,
418
801
  })
419
802
 
420
803
  return { findings: result.findings.length }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A config that was found and parsed, but says something impossible.
3
+ *
4
+ * Its message reaches the developer verbatim — every other error out of the
5
+ * loader is reported as "failed to load", which sends the reader hunting for a
6
+ * broken file rather than the line that is wrong.
7
+ */
8
+ export declare class PikkuCLIConfigError extends Error {
9
+ }