@pikku/cli 0.12.96 → 0.12.99

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 (271) hide show
  1. package/console-app/assets/abnfDiagram-VRR7QNED-CytV-CG1.js +1 -0
  2. package/console-app/assets/arc-C5PvmnaA.js +1 -0
  3. package/console-app/assets/architecture-TIHT7OUA-C1_ScvPX.js +1 -0
  4. package/console-app/assets/architectureDiagram-ZJ3FMSHR-CJ2xb7C-.js +36 -0
  5. package/console-app/assets/array-BifhSqXX.js +1 -0
  6. package/console-app/assets/blockDiagram-677ZJIJ3-CCecrBrk.js +132 -0
  7. package/console-app/assets/c4Diagram-LMCZKHZV-CBnpukvQ.js +10 -0
  8. package/console-app/assets/channel-CwDOkXGI.js +1 -0
  9. package/console-app/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  10. package/console-app/assets/chunk-32BRIVSS-D8qWWZRQ.js +1 -0
  11. package/console-app/assets/chunk-52WLFC77-BEpXHMsp.js +10 -0
  12. package/console-app/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  13. package/console-app/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  14. package/console-app/assets/chunk-C7G6YPKG-DsHUMkOp.js +1 -0
  15. package/console-app/assets/chunk-EX3LRPZG-mM7TjVx9.js +231 -0
  16. package/console-app/assets/chunk-FWX5IMBZ-CndRAn18.js +2 -0
  17. package/console-app/assets/chunk-HOUHSVGY-D1kT8umn.js +1 -0
  18. package/console-app/assets/chunk-ICXQ74PX-CeSaGyro.js +2 -0
  19. package/console-app/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  20. package/console-app/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
  21. package/console-app/assets/chunk-MOJQB5TN-CaZOCHYr.js +88 -0
  22. package/console-app/assets/chunk-OGEWGWER-CnB1CsPt.js +1 -0
  23. package/console-app/assets/chunk-PUDLZKDR-BbGulHJm.js +156 -0
  24. package/console-app/assets/chunk-Q4XR5HBZ-BywwzeeR.js +70 -0
  25. package/console-app/assets/chunk-RYQCIY6F-CV0R1Adv.js +1 -0
  26. package/console-app/assets/chunk-V7JOEXUC-DGFaAWzB.js +206 -0
  27. package/console-app/assets/chunk-VAUOI2AC-DxPUd8Yb.js +1 -0
  28. package/console-app/assets/chunk-VR4S4FIN-D4-UaW_J.js +1 -0
  29. package/console-app/assets/chunk-WYO6CB5R-D_TFN0d9.js +127 -0
  30. package/console-app/assets/chunk-XXDRQBXY-BzVuweA_.js +1 -0
  31. package/console-app/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  32. package/console-app/assets/chunk-ZGVPDNZ5-C6hdkGQb.js +62 -0
  33. package/console-app/assets/chunk-ZIRB5QZD-H-kepumB.js +32 -0
  34. package/console-app/assets/classDiagram-OUVF2IWQ-CTYJkTT0.js +1 -0
  35. package/console-app/assets/classDiagram-v2-EOCWNBFH-CTYJkTT0.js +1 -0
  36. package/console-app/assets/cose-bilkent-JH36ORCC-B6ZPxN6v.js +1 -0
  37. package/console-app/assets/cynefin-VYW2F7L2-D_nmxNz_.js +1 -0
  38. package/console-app/assets/cynefinDiagram-TSTJHNR4-CHy5QN0S.js +62 -0
  39. package/console-app/assets/cytoscape.esm-h6BdjjI9.js +321 -0
  40. package/console-app/assets/dagre-CXRCoUWR.js +1 -0
  41. package/console-app/assets/dagre-VKFMJZFB-DYfukM34.js +4 -0
  42. package/console-app/assets/defaultLocale-C8Fc0cco.js +1 -0
  43. package/console-app/assets/diagram-FQU43EPY-Dgb9TPSB.js +3 -0
  44. package/console-app/assets/diagram-G47NLZAW-CnFNZs9Z.js +24 -0
  45. package/console-app/assets/diagram-NH7WQ7WH-DN7h2DU-.js +24 -0
  46. package/console-app/assets/diagram-OA4YK3LP-RLaQaNtK.js +30 -0
  47. package/console-app/assets/diagram-WEI45ONY-DxHWhVGd.js +41 -0
  48. package/console-app/assets/dist-BaJNIo4I.js +1 -0
  49. package/console-app/assets/ebnfDiagram-CCIWWBDH-UN3Z81-l.js +1 -0
  50. package/console-app/assets/erDiagram-Q63AITRT-C9lGAY3i.js +85 -0
  51. package/console-app/assets/eventmodeling-45OFAUF4-Dc5wNSSK.js +1 -0
  52. package/console-app/assets/flowDiagram-23GEKE2U-zPyGHMi4.js +1 -0
  53. package/console-app/assets/ganttDiagram-NO4QXBWP-CvBjBcdK.js +292 -0
  54. package/console-app/assets/gitGraph-TEB2WS4Q-Cs5Z9jMo.js +1 -0
  55. package/console-app/assets/gitGraphDiagram-IHSO6WYX-B0i_IJ5r.js +106 -0
  56. package/console-app/assets/graphlib-B8gBHxth.js +1 -0
  57. package/console-app/assets/{index-CeQodYfg.js → index-71U8EEj7.js} +155 -153
  58. package/console-app/assets/{index-CyYHTGWZ.css → index-BXMjqLQ5.css} +1 -1
  59. package/console-app/assets/info-DKCQHKI2-DHL0afNl.js +1 -0
  60. package/console-app/assets/infoDiagram-FWYZ7A6U-CBWykodX.js +2 -0
  61. package/console-app/assets/init-D6jRqBbL.js +1 -0
  62. package/console-app/assets/ishikawaDiagram-FXEZZL3T-DM377eW_.js +70 -0
  63. package/console-app/assets/journeyDiagram-5HDEW3XC-BpX_-Cns.js +139 -0
  64. package/console-app/assets/kanban-definition-HUTT4EX6-D3xFsQkO.js +89 -0
  65. package/console-app/assets/katex-Vhh-h91d.js +257 -0
  66. package/console-app/assets/line-CTSKHTE0.js +1 -0
  67. package/console-app/assets/linear-R5GlBvXT.js +1 -0
  68. package/console-app/assets/map-DsCK-0Cs.js +1 -0
  69. package/console-app/assets/mermaid-parser.core-CkHk4BPi.js +7 -0
  70. package/console-app/assets/mermaid.core-CcjGXuV6.js +11 -0
  71. package/console-app/assets/mindmap-definition-LN4V7U3C-CtolazqQ.js +96 -0
  72. package/console-app/assets/ordinal-hYBb2elL.js +1 -0
  73. package/console-app/assets/packet-7NZHBO7P-BxSOj_Ny.js +1 -0
  74. package/console-app/assets/path-BWPyau1x.js +1 -0
  75. package/console-app/assets/pegDiagram-2B236MQR-BNsMtxsM.js +1 -0
  76. package/console-app/assets/pie-RZYD4A2V-BbkKQBA5.js +1 -0
  77. package/console-app/assets/pieDiagram-ENE6RG2P-BAZLIz2f.js +39 -0
  78. package/console-app/assets/quadrantDiagram-ABIIQ3AL-Dpvn-1Fs.js +7 -0
  79. package/console-app/assets/radar-I7S5WNFK-B-uKXIAH.js +1 -0
  80. package/console-app/assets/railroad-3IZDKUUU-BHXXK04t.js +1 -0
  81. package/console-app/assets/railroad-abnf-AHOZXSZD-tXSQaBvY.js +1 -0
  82. package/console-app/assets/railroad-ebnf-EBAXGLYW-DG3aohR0.js +1 -0
  83. package/console-app/assets/railroad-peg-LSFZ7HO6-DY_gBA-x.js +1 -0
  84. package/console-app/assets/railroadDiagram-RFXS5EU6-DfUApEQj.js +1 -0
  85. package/console-app/assets/requirementDiagram-TGXJPOKE-B2zX0jqo.js +84 -0
  86. package/console-app/assets/rough.esm-CSKSodPl.js +1 -0
  87. package/console-app/assets/sankeyDiagram-HTMAVEWB-CbQ2zc7z.js +40 -0
  88. package/console-app/assets/sequenceDiagram-DBY2YBRQ-BLT8PXI_.js +162 -0
  89. package/console-app/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  90. package/console-app/assets/src-BLuPUFWW.js +1 -0
  91. package/console-app/assets/stateDiagram-2N3HPSRC-BvEyVp85.js +1 -0
  92. package/console-app/assets/stateDiagram-v2-6OUMAXLB-RGKS9iU4.js +1 -0
  93. package/console-app/assets/swimlanes-5IMT3BWC-1AZ459s6.js +2 -0
  94. package/console-app/assets/swimlanesDiagram-G3AALYLV-Ck_0-rfN.js +8 -0
  95. package/console-app/assets/timeline-definition-FHXFAJF6-dLuvH4s2.js +120 -0
  96. package/console-app/assets/treeView-QDETBFTQ-xL2msjo2.js +1 -0
  97. package/console-app/assets/treemap-6X3UGDF4-C98ichVP.js +1 -0
  98. package/console-app/assets/vennDiagram-L72KCM5P-HbUmidhv.js +34 -0
  99. package/console-app/assets/wardley-OPB4EBWU-C4gJS-m2.js +1 -0
  100. package/console-app/assets/wardleyDiagram-EHGQE667-B7b-LB02.js +78 -0
  101. package/console-app/assets/xychartDiagram-FW5EYKEG-Cm03zUGy.js +7 -0
  102. package/console-app/index.html +2 -2
  103. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  104. package/dist/.pikku/auth/auth.types.d.ts +1 -1
  105. package/dist/.pikku/auth/auth.types.js +1 -1
  106. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  107. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  108. package/dist/.pikku/cli/pikku-cli-channel.js +1 -6
  109. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  110. package/dist/.pikku/cli/pikku-cli-client.gen.js +36 -9
  111. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  112. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  113. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  114. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  115. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  116. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +7 -8
  117. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  118. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  119. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  120. package/dist/.pikku/cli/pikku-cli.gen.js +18 -2
  121. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  122. package/dist/.pikku/function/pikku-function-types.gen.d.ts +12 -6
  123. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  124. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  125. package/dist/.pikku/function/pikku-functions-meta.gen.json +39 -30
  126. package/dist/.pikku/function/pikku-functions.gen.js +3 -3
  127. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  128. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  129. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  130. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  131. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  132. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  133. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  134. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  135. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  136. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  137. package/dist/.pikku/pikku-services.gen.d.ts +2 -1
  138. package/dist/.pikku/pikku-services.gen.js +1 -0
  139. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  140. package/dist/.pikku/pikku-types.gen.js +1 -1
  141. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  142. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  143. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  144. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  145. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  146. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  147. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -1
  148. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  149. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  150. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  151. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  152. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  153. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  154. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  155. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  156. package/dist/.pikku/schemas/register.gen.js +3 -3
  157. package/dist/.pikku/schemas/schemas/DbResetInput.schema.json +1 -1
  158. package/dist/.pikku/schemas/schemas/{DbSeedInput.schema.json → PikkuVirtualUserFunctionsOutput.schema.json} +1 -1
  159. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  160. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  161. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  162. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  163. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  164. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  165. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  166. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  167. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  168. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  169. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  170. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  171. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  172. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  173. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  174. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +20 -14
  175. package/dist/.pikku/workflow/pikku-personas.gen.js +1 -1
  176. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +6 -6
  177. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  178. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  179. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  180. package/dist/bin/pikku-bin.mjs +2 -2
  181. package/dist/src/cli.wiring.js +8 -7
  182. package/dist/src/fabric/functions/validate.function.js +76 -176
  183. package/dist/src/functions/commands/all.d.ts +1 -1
  184. package/dist/src/functions/commands/bootstrap.d.ts +1 -1
  185. package/dist/src/functions/commands/db-audit.js +1 -1
  186. package/dist/src/functions/commands/db-baseline.js +1 -1
  187. package/dist/src/functions/commands/db-check.js +6 -5
  188. package/dist/src/functions/commands/db-codegen.js +1 -1
  189. package/dist/src/functions/commands/db-generate.js +1 -1
  190. package/dist/src/functions/commands/db-migrate.js +1 -1
  191. package/dist/src/functions/commands/db-reset.d.ts +15 -1
  192. package/dist/src/functions/commands/db-reset.js +18 -6
  193. package/dist/src/functions/commands/db-shared.d.ts +14 -0
  194. package/dist/src/functions/commands/deploy-info.d.ts +1 -1
  195. package/dist/src/functions/commands/dev.js +1 -1
  196. package/dist/src/functions/commands/enable.d.ts +8 -8
  197. package/dist/src/functions/commands/new-addon.js +2 -2
  198. package/dist/src/functions/commands/persona-sync.d.ts +1 -1
  199. package/dist/src/functions/commands/persona-sync.js +1 -1
  200. package/dist/src/functions/commands/persona.js +16 -18
  201. package/dist/src/functions/commands/scenario-ladder.js +1 -1
  202. package/dist/src/functions/commands/scenario.js +4 -2
  203. package/dist/src/functions/commands/scopes-shared.d.ts +4 -0
  204. package/dist/src/functions/commands/scopes-shared.js +1 -1
  205. package/dist/src/functions/commands/serve.js +1 -1
  206. package/dist/src/functions/commands/versions-check.js +3 -3
  207. package/dist/src/functions/db/annotation-parser.d.ts +14 -0
  208. package/dist/src/functions/db/annotation-parser.js +50 -2
  209. package/dist/src/functions/db/better-auth-schema.js +2 -1
  210. package/dist/src/functions/db/db-codegen.d.ts +12 -0
  211. package/dist/src/functions/db/db-codegen.js +149 -16
  212. package/dist/src/functions/db/local-db.d.ts +87 -6
  213. package/dist/src/functions/db/local-db.js +202 -35
  214. package/dist/src/functions/db/sqlite/dev-seed.d.ts +12 -0
  215. package/dist/src/functions/db/sqlite/dev-seed.js +23 -0
  216. package/dist/src/functions/db/sqlite/sqlite-runtime-bun.js +1 -1
  217. package/dist/src/functions/validate/bootstrap-checks.d.ts +19 -0
  218. package/dist/src/functions/validate/bootstrap-checks.js +90 -0
  219. package/dist/src/functions/validate/persona-checks.d.ts +24 -0
  220. package/dist/src/functions/validate/persona-checks.js +136 -0
  221. package/dist/src/functions/validate/scenario-file-checks.d.ts +17 -0
  222. package/dist/src/functions/validate/scenario-file-checks.js +123 -0
  223. package/dist/src/functions/validate/shared-checks.d.ts +59 -0
  224. package/dist/src/functions/validate/shared-checks.js +311 -0
  225. package/dist/src/functions/validate/workspace-validate.d.ts +1 -2
  226. package/dist/src/functions/validate/workspace-validate.js +4 -196
  227. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +16 -1
  228. package/dist/src/functions/wirings/auth/serialize-auth-types.d.ts +3 -1
  229. package/dist/src/functions/wirings/auth/serialize-auth-types.js +3 -1
  230. package/dist/src/functions/wirings/channels/pikku-channels.d.ts +1 -1
  231. package/dist/src/functions/wirings/channels/serialize-typed-channel-map.js +10 -2
  232. package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +9 -4
  233. package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +32 -10
  234. package/dist/src/functions/wirings/cli/serialize-cli-entrypoint-guard.d.ts +12 -0
  235. package/dist/src/functions/wirings/cli/serialize-cli-entrypoint-guard.js +28 -0
  236. package/dist/src/functions/wirings/cli/serialize-local-cli-bootstrap.js +4 -1
  237. package/dist/src/functions/wirings/console/serialize-console-functions.js +1 -1
  238. package/dist/src/functions/wirings/functions/serialize-addon-types.js +1 -1
  239. package/dist/src/functions/wirings/functions/serialize-function-types.js +19 -5
  240. package/dist/src/functions/wirings/package/pikku-command-package-types.d.ts +3 -3
  241. package/dist/src/functions/wirings/personas/personas-secrets-file.d.ts +8 -0
  242. package/dist/src/functions/wirings/personas/personas-secrets-file.js +9 -0
  243. package/dist/src/functions/wirings/personas/pikku-command-personas.js +14 -0
  244. package/dist/src/functions/wirings/personas/serialize-personas-secrets.d.ts +16 -0
  245. package/dist/src/functions/wirings/personas/serialize-personas-secrets.js +34 -0
  246. package/dist/src/functions/wirings/queue/serialize-queue-map.js +2 -1
  247. package/dist/src/functions/wirings/queue/serialize-queue-wrapper.js +2 -1
  248. package/dist/src/functions/wirings/virtual-user/pikku-command-virtual-user-functions.d.ts +1 -0
  249. package/dist/src/functions/wirings/virtual-user/pikku-command-virtual-user-functions.js +42 -0
  250. package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.d.ts +28 -0
  251. package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +439 -0
  252. package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +2 -2
  253. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +5 -11
  254. package/dist/src/functions/workflows/all.workflow.js +49 -16
  255. package/dist/src/server/bun-server-runner.d.ts +2 -2
  256. package/dist/src/server/bun-server-runner.js +5 -1
  257. package/dist/src/services/cli-logger.service.d.ts +28 -16
  258. package/dist/src/services/cli-logger.service.js +25 -16
  259. package/dist/src/services.js +8 -0
  260. package/dist/src/utils/file-import-path.js +9 -0
  261. package/dist/src/utils/pikku-cli-config.js +7 -0
  262. package/dist/src/utils/resolve-personas.d.ts +4 -9
  263. package/dist/src/utils/resolve-personas.js +4 -15
  264. package/dist/tsconfig.tsbuildinfo +1 -1
  265. package/package.json +11 -11
  266. package/dist/.pikku/schemas/schemas/BootstrapInput.schema.json +0 -1
  267. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +0 -1
  268. package/dist/src/functions/commands/db-seed.d.ts +0 -1
  269. package/dist/src/functions/commands/db-seed.js +0 -24
  270. package/dist/src/functions/db/sqlite/seed.d.ts +0 -11
  271. package/dist/src/functions/db/sqlite/seed.js +0 -22
@@ -1,8 +1,12 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
1
3
  import { pikkuSessionlessFunc } from '#pikku';
2
4
  import { writeFileInDir } from '../../../utils/file-writer.js';
3
5
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
4
6
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
7
  import { serializePersonasTypes } from './serialize-personas-types.js';
8
+ import { serializePersonasSecrets } from './serialize-personas-secrets.js';
9
+ import { personasSecretsFilePath } from './personas-secrets-file.js';
6
10
  import { personaEnvironmentErrors, validateAndBuildPersonasMeta, } from '@pikku/core/persona';
7
11
  export const pikkuPersonas = pikkuSessionlessFunc({
8
12
  func: async ({ logger, config, getInspectorState }, data) => {
@@ -29,6 +33,16 @@ export const pikkuPersonas = pikkuSessionlessFunc({
29
33
  environmentNames: Object.keys(environments),
30
34
  });
31
35
  await writeFileInDir(logger, personasFile, content);
36
+ // The actor sign-in secret only means anything once someone is declared
37
+ // to sign in. Drop the file again when the last persona goes, so a stale
38
+ // declaration can't keep asking a deploy for a value nothing reads.
39
+ const secretsFile = personasSecretsFilePath(personasFile);
40
+ if (state.personas.definitions.length > 0) {
41
+ await writeFileInDir(logger, secretsFile, serializePersonasSecrets());
42
+ }
43
+ else if (existsSync(secretsFile)) {
44
+ await rm(secretsFile);
45
+ }
32
46
  if (personasMetaJsonFile) {
33
47
  const meta = validateAndBuildPersonasMeta(state.personas.definitions);
34
48
  await writeFileInDir(logger, personasMetaJsonFile, JSON.stringify(meta, null, 2));
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Generates the sibling secrets file for a project that declares personas.
3
+ *
4
+ * `SCENARIO_ACTOR_SECRET` is the platform's, not the application's: nothing in
5
+ * app code reads it. It is consumed by the scenario service when it signs an
6
+ * actor in, by `pikku scenario` / `pikku persona` / `pikku persona sync`, and by
7
+ * the Playwright provider. Declaring it here — rather than leaving every project
8
+ * to hand-write the same `defineSecret` — is the same reasoning that puts
9
+ * `BETTER_AUTH_SECRET` in the auth scaffold: a value the framework requires
10
+ * regardless of what the user wired should be collected by the platform, not
11
+ * discovered missing at the first sign-in.
12
+ *
13
+ * The schema is a named const because the inspector only accepts a variable
14
+ * reference for `schema`, never an inline expression.
15
+ */
16
+ export declare const serializePersonasSecrets: () => string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Generates the sibling secrets file for a project that declares personas.
3
+ *
4
+ * `SCENARIO_ACTOR_SECRET` is the platform's, not the application's: nothing in
5
+ * app code reads it. It is consumed by the scenario service when it signs an
6
+ * actor in, by `pikku scenario` / `pikku persona` / `pikku persona sync`, and by
7
+ * the Playwright provider. Declaring it here — rather than leaving every project
8
+ * to hand-write the same `defineSecret` — is the same reasoning that puts
9
+ * `BETTER_AUTH_SECRET` in the auth scaffold: a value the framework requires
10
+ * regardless of what the user wired should be collected by the platform, not
11
+ * discovered missing at the first sign-in.
12
+ *
13
+ * The schema is a named const because the inspector only accepts a variable
14
+ * reference for `schema`, never an inline expression.
15
+ */
16
+ export const serializePersonasSecrets = () => {
17
+ return `/**
18
+ * This file was generated by the @pikku/cli
19
+ */
20
+ import { defineSecret } from '@pikku/core/secret'
21
+ import { z } from 'zod'
22
+
23
+ export const ScenarioActorSecretSchema = z.string()
24
+
25
+ defineSecret({
26
+ name: 'scenarioActorSecret',
27
+ displayName: 'Scenario Actor Secret',
28
+ description:
29
+ 'Signing secret that lets scenarios and virtual users sign in as a declared persona',
30
+ secretId: 'SCENARIO_ACTOR_SECRET',
31
+ schema: ScenarioActorSecretSchema,
32
+ })
33
+ `;
34
+ };
@@ -13,6 +13,7 @@ ${serializedImportMap}
13
13
  ${serializedCustomTypes}
14
14
 
15
15
  import type { QueueJob } from '@pikku/core/queue'
16
+ import type { Safe } from '@pikku/core'
16
17
 
17
18
  interface QueueHandler<I, O> {
18
19
  input: I;
@@ -23,7 +24,7 @@ ${serializedQueues}
23
24
 
24
25
  type QueueAdd = <Name extends keyof QueueMap>(
25
26
  name: Name,
26
- data: QueueMap[Name]['input'],
27
+ data: Safe<QueueMap[Name]['input']>,
27
28
  options?: {
28
29
  priority?: number
29
30
  delay?: number
@@ -1,6 +1,7 @@
1
1
  export const serializeQueueWrapper = (queueMapPath) => {
2
2
  return `
3
3
  import type { QueueService, QueueJob } from '@pikku/core/queue'
4
+ import type { Safe } from '@pikku/core'
4
5
  import type { QueueMap, TypedPikkuQueue } from '${queueMapPath}'
5
6
 
6
7
  export class PikkuQueue implements TypedPikkuQueue {
@@ -8,7 +9,7 @@ export class PikkuQueue implements TypedPikkuQueue {
8
9
 
9
10
  public async add<Name extends keyof QueueMap>(
10
11
  queueName: Name,
11
- data: QueueMap[Name]['input'],
12
+ data: Safe<QueueMap[Name]['input']>,
12
13
  options?: {
13
14
  priority?: number
14
15
  delay?: number
@@ -0,0 +1 @@
1
+ export declare const pikkuVirtualUserFunctions: import("#pikku").PikkuFunctionConfig<void, boolean, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<void, boolean, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<void, boolean, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
@@ -0,0 +1,42 @@
1
+ import { pikkuSessionlessFunc } from '#pikku';
2
+ import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
+ import { writeFileInDir } from '../../../utils/file-writer.js';
4
+ import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
+ import { serializeVirtualUserFunctions } from './serialize-virtual-user-functions.js';
6
+ import { resolveScaffoldFeature } from '../../../utils/resolve-scaffold-feature.js';
7
+ export const pikkuVirtualUserFunctions = pikkuSessionlessFunc({
8
+ func: async ({ logger, config, getInspectorState }) => {
9
+ if (!config.scaffold?.virtualUser ||
10
+ !config.virtualUserFunctionsFile ||
11
+ !config.virtualUserSchemasFile) {
12
+ logger.debug({
13
+ message: 'Skipping virtual user scaffold (set scaffold.virtualUser in pikku.config.json to enable).',
14
+ type: 'skip',
15
+ });
16
+ return false;
17
+ }
18
+ const state = await getInspectorState();
19
+ // A virtual user IS a persona — the generated functions sign in as one and
20
+ // run as them. With none declared the scaffold would emit RPCs whose only
21
+ // possible answer is "unknown persona". Fail the codegen instead of
22
+ // shipping that.
23
+ if (Object.keys(state.personas?.definitions ?? {}).length === 0) {
24
+ throw new Error(`"scaffold.virtualUser" is enabled but no personas are declared.\n` +
25
+ `A virtual user runs AS a persona, so there is nobody to run.\n` +
26
+ `Fix: declare one with definePersonas({ ... }), or remove ` +
27
+ `"scaffold.virtualUser" from pikku.config.json.`);
28
+ }
29
+ const pathToPikkuTypes = getFileImportRelativePath(config.virtualUserFunctionsFile, config.typesDeclarationFile, config.packageMappings);
30
+ const pathToPersonas = getFileImportRelativePath(config.virtualUserFunctionsFile, config.personasWiringFile, config.packageMappings);
31
+ const { schemas, functions } = serializeVirtualUserFunctions(pathToPikkuTypes, pathToPersonas, resolveScaffoldFeature('virtualUser', config.scaffold.virtualUser).auth);
32
+ await writeFileInDir(logger, config.virtualUserSchemasFile, schemas);
33
+ await writeFileInDir(logger, config.virtualUserFunctionsFile, functions);
34
+ return true;
35
+ },
36
+ middleware: [
37
+ logCommandInfoAndTime({
38
+ commandStart: 'Generating virtual user functions',
39
+ commandEnd: 'Generated virtual user functions',
40
+ }),
41
+ ],
42
+ });
@@ -0,0 +1,28 @@
1
+ export interface VirtualUserGenOutput {
2
+ schemas: string;
3
+ functions: string;
4
+ }
5
+ /**
6
+ * Generate the virtual-user functions into the project scaffold: start a run,
7
+ * read one back, and the sessionless function that does the work.
8
+ *
9
+ * Scaffolded rather than shipped in an addon because the whole feature is
10
+ * derived from what the project already generates — its personas, its function
11
+ * meta, its scenarios — so there is nothing to configure and nothing to author.
12
+ * `pikku persona run` does the same thing from a terminal; this is the same run
13
+ * started over RPC, from CI or from a console, with the result kept.
14
+ *
15
+ * A virtual user is NOT a workflow and NOT a queued job. It explores, so no two
16
+ * attempts take the same steps and there is nothing to replay; and the run
17
+ * record already carries the progress a queue would only be holding on the way
18
+ * here. So `runVirtualUser` writes the record, kicks the run off without
19
+ * awaiting it, and returns the id. The one cost is stated on the record: a
20
+ * restart mid-run strands it at `running`.
21
+ *
22
+ * Emitted as two files. The schemas are zod, and the inspector reads a zod
23
+ * schema by importing the module that declares it — which it cannot do for the
24
+ * functions file, whose relative pikku-types import per-unit deploy codegen
25
+ * rewrites. Keeping the schemas in a sibling module that imports nothing but
26
+ * zod sidesteps that entirely.
27
+ */
28
+ export declare const serializeVirtualUserFunctions: (pathToPikkuTypes: string, pathToPersonas: string, requireAuth?: boolean) => VirtualUserGenOutput;
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Generate the virtual-user functions into the project scaffold: start a run,
3
+ * read one back, and the sessionless function that does the work.
4
+ *
5
+ * Scaffolded rather than shipped in an addon because the whole feature is
6
+ * derived from what the project already generates — its personas, its function
7
+ * meta, its scenarios — so there is nothing to configure and nothing to author.
8
+ * `pikku persona run` does the same thing from a terminal; this is the same run
9
+ * started over RPC, from CI or from a console, with the result kept.
10
+ *
11
+ * A virtual user is NOT a workflow and NOT a queued job. It explores, so no two
12
+ * attempts take the same steps and there is nothing to replay; and the run
13
+ * record already carries the progress a queue would only be holding on the way
14
+ * here. So `runVirtualUser` writes the record, kicks the run off without
15
+ * awaiting it, and returns the id. The one cost is stated on the record: a
16
+ * restart mid-run strands it at `running`.
17
+ *
18
+ * Emitted as two files. The schemas are zod, and the inspector reads a zod
19
+ * schema by importing the module that declares it — which it cannot do for the
20
+ * functions file, whose relative pikku-types import per-unit deploy codegen
21
+ * rewrites. Keeping the schemas in a sibling module that imports nothing but
22
+ * zod sidesteps that entirely.
23
+ */
24
+ export const serializeVirtualUserFunctions = (pathToPikkuTypes, pathToPersonas, requireAuth = true) => {
25
+ const authFlag = requireAuth ? 'true' : 'false';
26
+ const schemas = `/**
27
+ * Auto-generated virtual user schemas
28
+ * Do not edit manually - regenerate with 'npx pikku'
29
+ */
30
+ import { z } from 'zod'
31
+
32
+ /** Every disposition \`DISPOSITIONS\` declares. */
33
+ export const Disposition = z.enum([
34
+ 'realistic',
35
+ 'careless',
36
+ 'newcomer',
37
+ 'stale',
38
+ 'auditor',
39
+ 'adversarial',
40
+ 'accountable',
41
+ ])
42
+
43
+ export const RunVirtualUserInput = z.object({
44
+ /** A persona id from \`definePersonas()\`. */
45
+ persona: z.string().min(1),
46
+ /**
47
+ * Situational goals in the caller's own words, run alongside the intents
48
+ * derived from the app's scenarios. Appended to what the persona durably
49
+ * wants, never a replacement — a run that replaces their goals is not them.
50
+ */
51
+ goals: z.array(z.string().min(1)).max(20).optional(),
52
+ /**
53
+ * Ids and slugs from an earlier run, replayed as this user's notes on turn
54
+ * one. The point of a \`stale\` run: some of them no longer resolve, and what
55
+ * the product does about that is the thing being tested.
56
+ */
57
+ memory: z.record(z.string(), z.string()).optional(),
58
+ /** Overrides the persona's declared disposition for this run only. */
59
+ disposition: Disposition.optional(),
60
+ budget: z
61
+ .object({
62
+ steps: z.number().int().min(1).max(500).optional(),
63
+ mutations: z.number().int().min(0).max(500).optional(),
64
+ durationMs: z.number().int().min(1000).max(3_600_000).optional(),
65
+ })
66
+ .optional(),
67
+ /** Fixed seed, so a run replays into the same finding. */
68
+ seed: z.number().int().optional(),
69
+ })
70
+
71
+ export const RunVirtualUserOutput = z.object({
72
+ runId: z.string(),
73
+ })
74
+
75
+ export const GetVirtualUserRunInput = z.object({
76
+ runId: z.string(),
77
+ })
78
+
79
+ /**
80
+ * What the user noticed. \`kind\` is a plain string rather than an enum of
81
+ * today's finding kinds: a run that turns up something new should be readable
82
+ * by a client built before that kind existed.
83
+ */
84
+ export const Finding = z.object({
85
+ kind: z.string(),
86
+ detail: z.string(),
87
+ rpcName: z.string().optional(),
88
+ status: z.number().optional(),
89
+ intentId: z.string().optional(),
90
+ /** Step index within the run, so the transcript can be replayed to here. */
91
+ step: z.number(),
92
+ })
93
+
94
+ export const GetVirtualUserRunOutput = z.object({
95
+ runId: z.string(),
96
+ persona: z.string(),
97
+ disposition: Disposition,
98
+ seed: z.number(),
99
+ status: z.enum(['running', 'completed', 'failed']),
100
+ goals: z.array(z.string()),
101
+ memory: z.record(z.string(), z.string()),
102
+ findings: z.array(Finding),
103
+ /** Steps, calls, mutations, tokens and elapsed time, as the engine counted them. */
104
+ tally: z.record(z.string(), z.unknown()).nullable(),
105
+ stoppedBy: z.string().nullable(),
106
+ error: z.string().nullable(),
107
+ createdAt: z.string(),
108
+ finishedAt: z.string().nullable(),
109
+ })
110
+
111
+ /** The internal dispatch payload — everything the run was resolved down to. */
112
+ export const ExecuteVirtualUserRunInput = z.object({
113
+ runId: z.string(),
114
+ persona: z.string(),
115
+ disposition: Disposition,
116
+ goals: z.array(z.string()),
117
+ memory: z.record(z.string(), z.string()),
118
+ budget: z
119
+ .object({
120
+ steps: z.number().optional(),
121
+ mutations: z.number().optional(),
122
+ durationMs: z.number().optional(),
123
+ })
124
+ .optional(),
125
+ seed: z.number(),
126
+ })
127
+
128
+ export const ExecuteVirtualUserRunOutput = z.object({
129
+ findings: z.number(),
130
+ })
131
+ `;
132
+ const functions = `/**
133
+ * Auto-generated virtual user functions
134
+ * Do not edit manually - regenerate with 'npx pikku'
135
+ */
136
+ import { pikkuFunc, pikkuSessionlessFunc, defineScope } from '${pathToPikkuTypes}'
137
+ import {
138
+ personaVirtualUserTarget,
139
+ prepareVirtualUserRun,
140
+ runVirtualUser as runVirtualUserEngine,
141
+ PRODUCTION_DISPOSITION,
142
+ type SchemaMap,
143
+ type VirtualUserDisposition,
144
+ } from '@pikku/core/virtual-user'
145
+ import { createPersonas, personaConfigs } from '${pathToPersonas}'
146
+ import {
147
+ ExecuteVirtualUserRunInput,
148
+ ExecuteVirtualUserRunOutput,
149
+ GetVirtualUserRunInput,
150
+ GetVirtualUserRunOutput,
151
+ RunVirtualUserInput,
152
+ RunVirtualUserOutput,
153
+ } from './virtual-user.schemas.gen.js'
154
+
155
+ defineScope({
156
+ virtualUser: {
157
+ displayName: 'Virtual Users',
158
+ description: 'Run personas against this application and read what they found',
159
+ scopes: {
160
+ run: { description: 'Start a virtual user run' },
161
+ read: { description: "Read a run's findings" },
162
+ },
163
+ },
164
+ })
165
+
166
+ /**
167
+ * Where the virtual user signs in. Its own variable rather than a guess at the
168
+ * host's origin: a run drives real traffic through the real front door, and a
169
+ * server that cannot name its own public URL would be signing in somewhere it
170
+ * only assumed was itself.
171
+ */
172
+ const API_URL_VARIABLE = 'VIRTUAL_USER_API_URL'
173
+ const SECRET_VARIABLE = 'SCENARIO_ACTOR_SECRET'
174
+ const MODEL_VARIABLE = 'VIRTUAL_USER_MODEL'
175
+
176
+ export const runVirtualUser = pikkuFunc({
177
+ tags: ['pikku'],
178
+ title: 'Run a Virtual User',
179
+ description:
180
+ '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.',
181
+ expose: true,
182
+ auth: ${authFlag},
183
+ scopes: ['virtualUser:run'],
184
+ input: RunVirtualUserInput,
185
+ output: RunVirtualUserOutput,
186
+ func: async (
187
+ { virtualUserRunStore, config },
188
+ input,
189
+ { session, rpc }
190
+ ) => {
191
+ if (!virtualUserRunStore) {
192
+ throw new Error(
193
+ 'No virtualUserRunStore is wired — a run has nowhere to be recorded. ' +
194
+ 'Wire KyselyVirtualUserRunStore from @pikku/kysely, or your own implementation of VirtualUserRunStore.'
195
+ )
196
+ }
197
+
198
+ const persona = personaConfigs[input.persona as keyof typeof personaConfigs] as
199
+ | { id: string; runnable: boolean; disposition?: string }
200
+ | undefined
201
+ if (!persona) {
202
+ throw new Error(
203
+ \`Unknown persona "\${input.persona}" — declare it with definePersonas()\`
204
+ )
205
+ }
206
+ // An acted-upon persona has no session of its own, and running one would
207
+ // race whatever scenario acts on it.
208
+ if (!persona.runnable) {
209
+ throw new Error(
210
+ \`Persona "\${input.persona}" is declared as acted upon, never run\`
211
+ )
212
+ }
213
+
214
+ const disposition = (input.disposition ??
215
+ persona.disposition ??
216
+ 'realistic') as VirtualUserDisposition
217
+ // Every disposition other than this one exists to find out what the product
218
+ // does wrong, which is not a thing to do to real customers' data. Checked
219
+ // against the effective disposition, so the override cannot smuggle one in.
220
+ if (
221
+ config.nodeEnv === 'production' &&
222
+ disposition !== PRODUCTION_DISPOSITION
223
+ ) {
224
+ throw new Error(
225
+ \`Only the '\${PRODUCTION_DISPOSITION}' disposition may run against production; "\${input.persona}" is \${disposition}\`
226
+ )
227
+ }
228
+
229
+ // Seeded here rather than inside the engine so the record carries the seed
230
+ // even if the run dies before returning — an unreproducible crash costs the
231
+ // most.
232
+ const seed = input.seed ?? Math.floor(Math.random() * 2_147_483_647)
233
+
234
+ const runId = await virtualUserRunStore.start({
235
+ persona: persona.id,
236
+ disposition,
237
+ seed,
238
+ goals: input.goals ?? [],
239
+ memory: input.memory ?? {},
240
+ startedBy: session?.userId ?? null,
241
+ })
242
+
243
+ // Deliberately not awaited: a run takes minutes, and a held-open request
244
+ // survives neither a rollout nor a proxy timeout. executeVirtualUserRun
245
+ // writes both outcomes to the record itself, so the only thing left to
246
+ // handle here is a rejection escaping the promise — without the catch it
247
+ // becomes an unhandled rejection and takes the process with it.
248
+ void rpc!
249
+ .invoke('executeVirtualUserRun', {
250
+ runId,
251
+ persona: persona.id,
252
+ disposition,
253
+ goals: input.goals ?? [],
254
+ memory: input.memory ?? {},
255
+ budget: input.budget,
256
+ seed,
257
+ })
258
+ .catch(() => {})
259
+
260
+ return { runId }
261
+ },
262
+ })
263
+
264
+ export const getVirtualUserRun = pikkuFunc({
265
+ tags: ['pikku'],
266
+ title: 'Read a Virtual User Run',
267
+ description:
268
+ 'Reads a run back: its status, what it found, and the counts the engine kept.',
269
+ expose: true,
270
+ auth: ${authFlag},
271
+ // Its own scope, and not the one that starts a run: an adversarial run's
272
+ // findings are working exploits carrying live ids, so reading them is the
273
+ // more sensitive of the two.
274
+ scopes: ['virtualUser:read'],
275
+ input: GetVirtualUserRunInput,
276
+ output: GetVirtualUserRunOutput,
277
+ func: async ({ virtualUserRunStore }, { runId }) => {
278
+ if (!virtualUserRunStore) {
279
+ throw new Error('No virtualUserRunStore is wired — there are no runs to read.')
280
+ }
281
+ const run = await virtualUserRunStore.get(runId)
282
+ if (!run) {
283
+ throw new Error(\`No virtual user run \${runId}\`)
284
+ }
285
+ return {
286
+ runId: run.runId,
287
+ persona: run.persona,
288
+ disposition: run.disposition,
289
+ seed: run.seed,
290
+ status: run.status,
291
+ goals: run.goals,
292
+ memory: run.memory,
293
+ // Findings are free-form by design — the engine records what it noticed,
294
+ // not a fixed row shape — so they cross the wire as the schema's open
295
+ // object rather than being narrowed to whatever kinds exist today.
296
+ findings: run.findings.map((finding) => ({
297
+ kind: finding.kind as string,
298
+ detail: finding.detail,
299
+ rpcName: finding.rpcName,
300
+ status: finding.status,
301
+ intentId: finding.intentId,
302
+ step: finding.step,
303
+ })),
304
+ tally: (run.tally ?? null) as Record<string, unknown> | null,
305
+ stoppedBy: run.stoppedBy,
306
+ error: run.error,
307
+ createdAt: run.createdAt.toISOString(),
308
+ finishedAt: run.finishedAt ? run.finishedAt.toISOString() : null,
309
+ }
310
+ },
311
+ })
312
+
313
+ /**
314
+ * The run itself. Not exposed: it is dispatched by \`runVirtualUser\` and has no
315
+ * caller of its own.
316
+ *
317
+ * Everything it needs is derived through \`metaService\` and the generated
318
+ * personas — the same public surface any consumer has. Nothing reaches into
319
+ * pikku's internals, because an app could not, and a feature built on what only
320
+ * the framework can see would not be this feature.
321
+ */
322
+ export const executeVirtualUserRun = pikkuSessionlessFunc({
323
+ tags: ['pikku'],
324
+ input: ExecuteVirtualUserRunInput,
325
+ output: ExecuteVirtualUserRunOutput,
326
+ func: async (
327
+ { virtualUserRunStore, metaService, aiAgentRunner, variables, logger },
328
+ { runId, persona: personaId, disposition, goals, memory, budget, seed }
329
+ ) => {
330
+ if (!virtualUserRunStore) {
331
+ throw new Error('No virtualUserRunStore is wired.')
332
+ }
333
+ try {
334
+ if (!metaService) {
335
+ throw new Error('metaService is not wired — there is no catalogue to derive')
336
+ }
337
+ if (!aiAgentRunner) {
338
+ throw new Error('aiAgentRunner is not wired — there is nothing to think with')
339
+ }
340
+
341
+ const apiUrl = await variables.get(API_URL_VARIABLE)
342
+ if (!apiUrl) {
343
+ throw new Error(
344
+ \`\${API_URL_VARIABLE} is not set — a virtual user has no address to sign in at.\`
345
+ )
346
+ }
347
+ const secret = await variables.get(SECRET_VARIABLE)
348
+ if (!secret) {
349
+ throw new Error(
350
+ \`\${SECRET_VARIABLE} is not set — actor sign-in is disabled, so there is nobody for the virtual user to be.\`
351
+ )
352
+ }
353
+ const model = await variables.get(MODEL_VARIABLE)
354
+ if (!model) {
355
+ throw new Error(\`\${MODEL_VARIABLE} is not set — no model to think with.\`)
356
+ }
357
+
358
+ const functionsMeta = await metaService.getFunctionsMeta()
359
+ // Only the schemas the catalogue can actually refer to, so a large app
360
+ // does not pull every schema it has ever generated into one run.
361
+ const schemaNames = [
362
+ ...new Set(
363
+ Object.values(functionsMeta).flatMap((meta: any) =>
364
+ [meta.inputSchemaName, meta.outputSchemaName].filter(
365
+ (name: unknown): name is string => !!name
366
+ )
367
+ )
368
+ ),
369
+ ]
370
+ const schemas = (await metaService.getSchemas(schemaNames)) as SchemaMap
371
+
372
+ const persona = personaConfigs[personaId as keyof typeof personaConfigs]
373
+ if (!persona) {
374
+ throw new Error(\`Persona "\${personaId}" is no longer declared\`)
375
+ }
376
+
377
+ const { catalogue, intents, agents } = prepareVirtualUserRun({
378
+ persona,
379
+ functionsMeta,
380
+ schemas,
381
+ workflowsMeta: await metaService.getWorkflowMeta(),
382
+ systemRoles: await metaService.getSystemRolesMeta(),
383
+ agentsMeta: await metaService.getAgentsMeta(),
384
+ })
385
+
386
+ const signedIn = createPersonas({ apiUrl, secret, model })
387
+ const target = signedIn[personaId as keyof typeof signedIn]
388
+ if (!target) {
389
+ throw new Error(\`Persona "\${personaId}" cannot sign in\`)
390
+ }
391
+
392
+ const result = await runVirtualUserEngine({
393
+ persona,
394
+ personaId,
395
+ disposition: disposition as VirtualUserDisposition,
396
+ catalogue,
397
+ intents,
398
+ goals,
399
+ memory,
400
+ seed,
401
+ agents,
402
+ target: personaVirtualUserTarget(target, {
403
+ model,
404
+ agents: agents.map((agent) => agent.name),
405
+ }),
406
+ // \`AIAgentRunnerService.run\` IS the engine's \`ActorLLM\` — same params,
407
+ // same result — so a virtual user thinks through the same runner every
408
+ // agent in the app does, provider quirks and all.
409
+ llm: (params) => aiAgentRunner.run(params),
410
+ model,
411
+ budget: {
412
+ steps: budget?.steps,
413
+ mutations: budget?.mutations,
414
+ duration: budget?.durationMs,
415
+ },
416
+ })
417
+
418
+ await virtualUserRunStore.complete(runId, {
419
+ findings: result.findings,
420
+ tally: result.tally,
421
+ memory: result.memory,
422
+ stoppedBy: result.stoppedBy ?? null,
423
+ })
424
+
425
+ return { findings: result.findings.length }
426
+ } catch (error) {
427
+ // A crashed run and a run that found nothing are different states, and the
428
+ // record is the only place that distinction survives — leaving it at
429
+ // 'running' forever is what \`fail\` exists to prevent.
430
+ const message = error instanceof Error ? error.message : String(error)
431
+ logger.error(\`Virtual user run \${runId} (\${personaId}) failed: \${message}\`)
432
+ await virtualUserRunStore.fail(runId, message)
433
+ throw error
434
+ }
435
+ },
436
+ })
437
+ `;
438
+ return { schemas, functions };
439
+ };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Generate the scenario step map: the typed set of names a scenario may pass to
3
- * `scenario.step/given/when/then`.
3
+ * `scenario.given/when/then`.
4
4
  *
5
5
  * Steps are referenced by string, exactly like `workflow.do` references an RPC,
6
6
  * so type safety has to come from a generated map rather than from importing
@@ -54,7 +54,7 @@ export const serializeScenarioStepMap = (logger, relativeToPath, packageMappings
54
54
  .filter((line) => !line.startsWith('import '))
55
55
  .join('\n');
56
56
  return `/**
57
- * Scenario step map: the names \`scenario.step/given/when/then\` accepts, with
57
+ * Scenario step map: the names \`scenario.given/when/then\` accepts, with
58
58
  * the input and output types of each step.
59
59
  */
60
60
 
@@ -43,18 +43,12 @@ export interface TypedWorkflow extends PikkuWorkflowWire {
43
43
  }
44
44
 
45
45
  /**
46
- * The typed half of a scenario wire: \`step\` and its \`given\`/\`when\`/\`then\`
47
- * sugar, narrowed to the names declared by \`pikkuScenarioStep\` in this project.
48
- * The phase only changes the prose the reporter renders.
46
+ * The typed half of a scenario wire: \`given\`/\`when\`/\`then\`, narrowed to the
47
+ * names declared by \`pikkuScenarioStep\` in this project. \`given\` and \`when\`
48
+ * differ only in the prose the reporter renders; \`then\` additionally makes the
49
+ * step's bindings witnesses rather than alternatives.
49
50
  */
50
51
  export interface TypedScenarioSteps {
51
- step<K extends keyof FlattenedScenarioStepMap>(
52
- stepName: string,
53
- stepFunc: K,
54
- data?: FlattenedScenarioStepMap[K]['input'],
55
- options?: ScenarioStepOptions
56
- ): Promise<FlattenedScenarioStepMap[K]['output']>
57
-
58
52
  given<K extends keyof FlattenedScenarioStepMap>(
59
53
  stepName: string,
60
54
  stepFunc: K,
@@ -329,7 +323,7 @@ export type PikkuScenarioStepConfigWithSchema<
329
323
  InputSchema extends StandardSchemaV1 | undefined = undefined,
330
324
  OutputSchema extends StandardSchemaV1 | undefined = undefined
331
325
  > = {
332
- /** Registered name — this is the string \`scenario.step()\` references. */
326
+ /** Registered name — this is the string \`scenario.given()\` references. */
333
327
  name: string
334
328
  /**
335
329
  * What this step does, for the console and for whoever reads the source. It