@pikku/cli 0.12.118 → 0.12.120

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 (248) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/console-app/assets/{abnfDiagram-N423BO3Z-WNIAg_Oa.js → abnfDiagram-N423BO3Z-Df3BCQUl.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-i2kvkWiG.js +1 -0
  4. package/console-app/assets/{architectureDiagram-T3A2C74G-DM698mgf.js → architectureDiagram-T3A2C74G-BlV2VA0B.js} +1 -1
  5. package/console-app/assets/{blockDiagram-VBNYF7ZC-DPfy9K5S.js → blockDiagram-VBNYF7ZC-BZ7GFw7a.js} +1 -1
  6. package/console-app/assets/{c4Diagram-5PPSVZJV-DhLbbBbX.js → c4Diagram-5PPSVZJV-uXLYnA16.js} +1 -1
  7. package/console-app/assets/channel-BjjIBjrV.js +1 -0
  8. package/console-app/assets/{chunk-2GRJ4B5K-C1RQ1gvs.js → chunk-2GRJ4B5K-CU6Szqvt.js} +1 -1
  9. package/console-app/assets/{chunk-4I5QYGJK-BkzSdNc_.js → chunk-4I5QYGJK-Bf8ARw9p.js} +1 -1
  10. package/console-app/assets/{chunk-5RXB4S5H-DZpBpdcU.js → chunk-5RXB4S5H-CuRjyaQR.js} +1 -1
  11. package/console-app/assets/{chunk-6Q2QTUOP-mZObxdld.js → chunk-6Q2QTUOP-DMFott3W.js} +1 -1
  12. package/console-app/assets/{chunk-7Z6QIM7H-C0HbxZEa.js → chunk-7Z6QIM7H-YJMK1J3W.js} +1 -1
  13. package/console-app/assets/{chunk-GF5L2VYU-DXPVwWqW.js → chunk-GF5L2VYU-Co4slpk_.js} +1 -1
  14. package/console-app/assets/{chunk-I66GZJ75-pVpq9ckD.js → chunk-I66GZJ75-CE6Jk-ML.js} +1 -1
  15. package/console-app/assets/{chunk-JQJVKLGR-DJQ8K4wK.js → chunk-JQJVKLGR-BjkITZJq.js} +1 -1
  16. package/console-app/assets/{chunk-KBJHAD2P-B8XuxRCO.js → chunk-KBJHAD2P-DdHZUbE2.js} +1 -1
  17. package/console-app/assets/{chunk-NSK5VX7P-cYzQU4bD.js → chunk-NSK5VX7P-BB0gp55h.js} +1 -1
  18. package/console-app/assets/{chunk-QR6OTTB3-CNoTFsqn.js → chunk-QR6OTTB3-BhAzJcX6.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-DoKRUO_N.js → chunk-RYQCIY6F-BaRFYCcu.js} +1 -1
  20. package/console-app/assets/{chunk-UBXNYLIW-BqcIoUpU.js → chunk-UBXNYLIW-DN_ayb-W.js} +1 -1
  21. package/console-app/assets/{chunk-W5SLKNZC-p0Qalbjj.js → chunk-W5SLKNZC-DQ0C1K0m.js} +1 -1
  22. package/console-app/assets/{chunk-WRU74C26-DUMn96Dr.js → chunk-WRU74C26-Dej4aDDK.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-COHoQA_g.js +1 -0
  24. package/console-app/assets/classDiagram-JCYQIIEL-BkQzFYJq.js +1 -0
  25. package/console-app/assets/classDiagram-v2-OCEON4UE-BkQzFYJq.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-BCh3BlPI.js → cose-bilkent-JH36ORCC-CaCBDIGn.js} +1 -1
  27. package/console-app/assets/{cynefin-VYW2F7L2-ZfghUZsD.js → cynefin-VYW2F7L2-DvNj3Ktd.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-MW4NZA55-DcV3QXCe.js → cynefinDiagram-MW4NZA55-2M1I4KVc.js} +1 -1
  29. package/console-app/assets/{dagre-BPFWXLBN.js → dagre-BX6AcUT6.js} +1 -1
  30. package/console-app/assets/{dagre-VZM6K2ZE-DETshBO-.js → dagre-VZM6K2ZE-CeiMExQ0.js} +1 -1
  31. package/console-app/assets/{diagram-7IWD3JNH-CsLX3P2o.js → diagram-7IWD3JNH-DFb1QCit.js} +1 -1
  32. package/console-app/assets/{diagram-B4RE2ZJO-o2zcxm5S.js → diagram-B4RE2ZJO-CcmxcWm0.js} +1 -1
  33. package/console-app/assets/{diagram-LBJQPF4R-BD2zKU21.js → diagram-LBJQPF4R-DpxWXRoq.js} +1 -1
  34. package/console-app/assets/{diagram-Q27KOJAE-DHaacyna.js → diagram-Q27KOJAE-Cu_MNubg.js} +1 -1
  35. package/console-app/assets/{diagram-UB23O5K3-pSLCMUyV.js → diagram-UB23O5K3-B-R4JZVn.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-BXEA7PRR-BDzawX3O.js → ebnfDiagram-BXEA7PRR-C26G-5vz.js} +1 -1
  37. package/console-app/assets/{erDiagram-JOGREHBK-CyYnPmbN.js → erDiagram-JOGREHBK-DJaz9wds.js} +1 -1
  38. package/console-app/assets/eventmodeling-45OFAUF4-Cge15uFd.js +1 -0
  39. package/console-app/assets/flowDiagram-UKHOOZJN-DN8OJeAP.js +1 -0
  40. package/console-app/assets/{ganttDiagram-PKOTCBZU-3fhCBbPA.js → ganttDiagram-PKOTCBZU-BmHvLxmw.js} +1 -1
  41. package/console-app/assets/{gitGraph-TEB2WS4Q-CdLRkVux.js → gitGraph-TEB2WS4Q-gjOfZX1j.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-DS77QQ5N-CC1epdKw.js → gitGraphDiagram-DS77QQ5N-Cn2aQfgw.js} +1 -1
  43. package/console-app/assets/{index-BRmokG9t.js → index-Cx39I_BR.js} +184 -184
  44. package/console-app/assets/{index-GruDnAhJ.css → index-Mq-YbqNO.css} +1 -1
  45. package/console-app/assets/{info-DKCQHKI2-BJs8WkzC.js → info-DKCQHKI2-Dxvvr3Du.js} +1 -1
  46. package/console-app/assets/{infoDiagram-6WML65LV-uoOwoIeI.js → infoDiagram-6WML65LV-CU2DYprj.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-WSZJBQD7-vCUJhwTi.js → ishikawaDiagram-WSZJBQD7-CEIhGAXe.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-NVQOT4AX-D2QcIlLG.js → journeyDiagram-NVQOT4AX-vDLEtm6u.js} +1 -1
  49. package/console-app/assets/{kanban-definition-27J2QSJJ-JwwKowmR.js → kanban-definition-27J2QSJJ-rfZdVBY0.js} +1 -1
  50. package/console-app/assets/{line-h2oRreYF.js → line-CK7XHoXo.js} +1 -1
  51. package/console-app/assets/{linear-Dz002X_a.js → linear-DeA9Np_F.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-DTgmT0AA.js → mermaid-parser.core-Bkwm3osM.js} +3 -3
  53. package/console-app/assets/{mermaid.core-1jSEDr-j.js → mermaid.core-DvVwOMEV.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-FAOFIHXS-C3Txxhj4.js → mindmap-definition-FAOFIHXS-DYMAQz7X.js} +1 -1
  55. package/console-app/assets/{packet-7NZHBO7P-BGG7zIK2.js → packet-7NZHBO7P-CtoDJZ2L.js} +1 -1
  56. package/console-app/assets/{pegDiagram-VL7TDLO6--g1r_rov.js → pegDiagram-VL7TDLO6-BRxy7Znf.js} +1 -1
  57. package/console-app/assets/{pie-RZYD4A2V-C5OepMH7.js → pie-RZYD4A2V-NEqmtqwU.js} +1 -1
  58. package/console-app/assets/{pieDiagram-7S7Q4E2Y-DGD_LzGm.js → pieDiagram-7S7Q4E2Y-eekm9APm.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-CIZ2JOQS-CyBQQlD2.js → quadrantDiagram-CIZ2JOQS-BSew_APH.js} +1 -1
  60. package/console-app/assets/{radar-I7S5WNFK-CaBOAO95.js → radar-I7S5WNFK-BgvnQqhv.js} +1 -1
  61. package/console-app/assets/{railroad-3IZDKUUU-2aTWN4Pw.js → railroad-3IZDKUUU-DqG1o6ll.js} +1 -1
  62. package/console-app/assets/railroad-abnf-AHOZXSZD-DQHwBMhW.js +1 -0
  63. package/console-app/assets/railroad-ebnf-EBAXGLYW-cu_kDg2r.js +1 -0
  64. package/console-app/assets/railroad-peg-LSFZ7HO6-CJh8d3dM.js +1 -0
  65. package/console-app/assets/{railroadDiagram-AXF67PYL-3Nr0rlTp.js → railroadDiagram-AXF67PYL-Rb5V_7ZT.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-LRYGKXZP-BN7sEwSa.js → requirementDiagram-LRYGKXZP-BONrWtQh.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-W5VNT64P-CXZR46wO.js → sankeyDiagram-W5VNT64P-CUPWhFm-.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-SI44F4Z6-DNtmdJ4U.js → sequenceDiagram-SI44F4Z6-DXwluQXT.js} +1 -1
  69. package/console-app/assets/{src-x1E3pJJ-.js → src-CyNQB37R.js} +1 -1
  70. package/console-app/assets/{stateDiagram-OKZ733FA-B_GyYdl_.js → stateDiagram-OKZ733FA-DXopCa6U.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-UEYNNEHI-BCi_SQzZ.js +1 -0
  72. package/console-app/assets/{swimlanes-SLNWSIFB-D4N7VdF7.js → swimlanes-SLNWSIFB-C_oImvSp.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-C01TBFdM.js +8 -0
  74. package/console-app/assets/{timeline-definition-Z64GVDOM-DU5rsOWQ.js → timeline-definition-Z64GVDOM-BEx4FmJK.js} +1 -1
  75. package/console-app/assets/{treeView-QDETBFTQ-DaKM_9sB.js → treeView-QDETBFTQ-DIQFLe_6.js} +1 -1
  76. package/console-app/assets/{treemap-6X3UGDF4-BabsKtZY.js → treemap-6X3UGDF4-Dz8_vGjV.js} +1 -1
  77. package/console-app/assets/{vennDiagram-T6HMQDX7-Dniftpq0.js → vennDiagram-T6HMQDX7-_sMSuxYe.js} +1 -1
  78. package/console-app/assets/{wardley-OPB4EBWU-BWffQNys.js → wardley-OPB4EBWU-BkFD9f0r.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-T6FBY63Y-CQ74fVdn.js → wardleyDiagram-T6FBY63Y-DhqBDV2O.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-ELKLHX3M-D-XxbGIL.js → xychartDiagram-ELKLHX3M-D8Jav5f0.js} +1 -1
  81. package/console-app/index.html +2 -2
  82. package/dist/.pikku/addon/index.d.ts +1 -1
  83. package/dist/.pikku/addon/index.js +1 -1
  84. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  85. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  86. package/dist/.pikku/agent/index.d.ts +1 -1
  87. package/dist/.pikku/agent/index.js +1 -1
  88. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  89. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  90. package/dist/.pikku/auth/index.d.ts +1 -1
  91. package/dist/.pikku/auth/index.js +1 -1
  92. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
  93. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  94. package/dist/.pikku/channel/index.d.ts +1 -1
  95. package/dist/.pikku/channel/index.js +1 -1
  96. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  97. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  98. package/dist/.pikku/cli/index.d.ts +1 -1
  99. package/dist/.pikku/cli/index.js +1 -1
  100. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  101. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  102. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  103. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  104. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  105. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  106. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  107. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  108. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +2 -9
  109. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  110. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  111. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  112. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  113. package/dist/.pikku/error/index.d.ts +1 -1
  114. package/dist/.pikku/error/index.js +1 -1
  115. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  116. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  117. package/dist/.pikku/function/index.d.ts +1 -1
  118. package/dist/.pikku/function/index.js +1 -1
  119. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  120. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  121. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  122. package/dist/.pikku/function/pikku-functions-meta.gen.json +8 -6
  123. package/dist/.pikku/function/pikku-functions.gen.js +1 -3
  124. package/dist/.pikku/gateway/index.d.ts +1 -1
  125. package/dist/.pikku/gateway/index.js +1 -1
  126. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  127. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  128. package/dist/.pikku/http/index.d.ts +1 -1
  129. package/dist/.pikku/http/index.js +1 -1
  130. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  131. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  132. package/dist/.pikku/mcp/index.d.ts +1 -1
  133. package/dist/.pikku/mcp/index.js +1 -1
  134. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  135. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  136. package/dist/.pikku/middleware/index.d.ts +1 -1
  137. package/dist/.pikku/middleware/index.js +1 -1
  138. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
  139. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  140. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  141. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  142. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  143. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  144. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  145. package/dist/.pikku/queue/index.d.ts +1 -1
  146. package/dist/.pikku/queue/index.js +1 -1
  147. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  148. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  149. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  150. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  151. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  152. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  153. package/dist/.pikku/scenarios/index.d.ts +1 -1
  154. package/dist/.pikku/scenarios/index.js +1 -1
  155. package/dist/.pikku/scenarios/pikku-personas.gen.d.ts +11 -0
  156. package/dist/.pikku/scenarios/pikku-personas.gen.js +12 -1
  157. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  158. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  159. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  160. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  161. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  162. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  163. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  164. package/dist/.pikku/scheduler/index.d.ts +1 -1
  165. package/dist/.pikku/scheduler/index.js +1 -1
  166. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  167. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  168. package/dist/.pikku/schemas/register.gen.js +1 -1
  169. package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -1
  170. package/dist/.pikku/scopes/index.d.ts +1 -1
  171. package/dist/.pikku/scopes/index.js +1 -1
  172. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  173. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  174. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  175. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  176. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  177. package/dist/.pikku/secrets/index.d.ts +1 -1
  178. package/dist/.pikku/secrets/index.js +1 -1
  179. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  180. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  181. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  182. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  183. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  184. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  185. package/dist/.pikku/setup/index.d.ts +1 -1
  186. package/dist/.pikku/setup/index.js +1 -1
  187. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  188. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  189. package/dist/.pikku/trigger/index.d.ts +1 -1
  190. package/dist/.pikku/trigger/index.js +1 -1
  191. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  192. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  193. package/dist/.pikku/variables/index.d.ts +1 -1
  194. package/dist/.pikku/variables/index.js +1 -1
  195. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  196. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  197. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  198. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  199. package/dist/.pikku/workflow/index.d.ts +1 -1
  200. package/dist/.pikku/workflow/index.js +1 -1
  201. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  202. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  203. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  204. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  205. package/dist/bin/pikku-bin.mjs +2 -2
  206. package/dist/src/cli.wiring.js +1 -9
  207. package/dist/src/fabric/lib/git.js +31 -1
  208. package/dist/src/functions/commands/dev.js +2 -0
  209. package/dist/src/functions/commands/persona-sync.d.ts +14 -22
  210. package/dist/src/functions/commands/persona-sync.js +29 -107
  211. package/dist/src/functions/commands/serve.js +2 -0
  212. package/dist/src/functions/validate/auth-plugin-checks.d.ts +16 -0
  213. package/dist/src/functions/validate/auth-plugin-checks.js +165 -0
  214. package/dist/src/functions/validate/persona-checks.js +1 -1
  215. package/dist/src/functions/validate/shared-checks.js +12 -0
  216. package/dist/src/functions/validate/websocket-deps-checks.d.ts +30 -0
  217. package/dist/src/functions/validate/websocket-deps-checks.js +42 -0
  218. package/dist/src/functions/wirings/personas/serialize-personas-secrets.d.ts +1 -1
  219. package/dist/src/functions/wirings/personas/serialize-personas-secrets.js +1 -1
  220. package/dist/src/functions/wirings/scopes/pikku-command-scopes.d.ts +10 -0
  221. package/dist/src/functions/wirings/scopes/pikku-command-scopes.js +20 -3
  222. package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +72 -26
  223. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +1 -1
  224. package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +2 -1
  225. package/dist/src/functions/wirings/workflow/serialize-personas.js +20 -1
  226. package/dist/src/server/actor-sign-in.d.ts +5 -0
  227. package/dist/src/server/actor-sign-in.js +27 -0
  228. package/dist/src/server/node-server-runner.d.ts +11 -0
  229. package/dist/src/server/node-server-runner.js +33 -8
  230. package/dist/src/services.js +3 -1
  231. package/dist/src/utils/resolve-from-project.d.ts +29 -0
  232. package/dist/src/utils/resolve-from-project.js +45 -0
  233. package/dist/tsconfig.tsbuildinfo +1 -1
  234. package/package.json +18 -10
  235. package/snippets.json +1 -1
  236. package/surface.json +3 -3
  237. package/console-app/assets/architecture-TIHT7OUA-CIT-Nug2.js +0 -1
  238. package/console-app/assets/channel-DQC6cL1c.js +0 -1
  239. package/console-app/assets/chunk-XXDRQBXY-Dgz5VbWO.js +0 -1
  240. package/console-app/assets/classDiagram-JCYQIIEL-1ASFocNV.js +0 -1
  241. package/console-app/assets/classDiagram-v2-OCEON4UE-1ASFocNV.js +0 -1
  242. package/console-app/assets/eventmodeling-45OFAUF4-BYJ0A5k-.js +0 -1
  243. package/console-app/assets/flowDiagram-UKHOOZJN-CVn87uiP.js +0 -1
  244. package/console-app/assets/railroad-abnf-AHOZXSZD-TU7k5gB_.js +0 -1
  245. package/console-app/assets/railroad-ebnf-EBAXGLYW-DbArPBkM.js +0 -1
  246. package/console-app/assets/railroad-peg-LSFZ7HO6-TzB93z4K.js +0 -1
  247. package/console-app/assets/stateDiagram-v2-UEYNNEHI-D7V_HITc.js +0 -1
  248. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-C4XcIHKT.js +0 -8
@@ -0,0 +1,165 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ const lines = (...parts) => parts.join('\n');
5
+ /** Local rather than imported from shared-checks, which imports this module. */
6
+ const readTextSafe = async (path) => {
7
+ if (!existsSync(path))
8
+ return null;
9
+ try {
10
+ return await readFile(path, 'utf8');
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ };
16
+ const stringArray = (value) => Array.isArray(value)
17
+ ? value.filter((v) => typeof v === 'string')
18
+ : [];
19
+ /**
20
+ * Source files under one directory, generated files included — better-auth is
21
+ * routinely configured in a file the auth scaffold wrote.
22
+ */
23
+ const listSourceFiles = async (dir) => {
24
+ if (!existsSync(dir))
25
+ return [];
26
+ try {
27
+ return (await readdir(dir, { recursive: true }))
28
+ .filter((f) => typeof f === 'string' &&
29
+ (f.endsWith('.ts') || f.endsWith('.tsx')) &&
30
+ !f.includes('node_modules'))
31
+ .map((f) => join(dir, f));
32
+ }
33
+ catch {
34
+ return [];
35
+ }
36
+ };
37
+ /**
38
+ * The text inside every `plugins: [...]` array in the file.
39
+ *
40
+ * Bracket-counted rather than parsed: `pikku validate` runs without a TypeScript
41
+ * program, and a `]` inside a string literal in a plugin's options is the only
42
+ * way this misreads an array — at which point the check under-reports rather
43
+ * than inventing a finding.
44
+ */
45
+ const pluginArrays = (text) => {
46
+ const arrays = [];
47
+ const opener = /plugins\s*:\s*\[/g;
48
+ let match;
49
+ while ((match = opener.exec(text))) {
50
+ const start = match.index + match[0].length;
51
+ let depth = 1;
52
+ let i = start;
53
+ while (i < text.length && depth > 0) {
54
+ if (text[i] === '[')
55
+ depth++;
56
+ else if (text[i] === ']')
57
+ depth--;
58
+ i++;
59
+ }
60
+ if (depth === 0)
61
+ arrays.push(text.slice(start, i - 1));
62
+ }
63
+ return arrays;
64
+ };
65
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
+ /**
67
+ * How `exported` from `module` can be called in this file: the local name a
68
+ * named import bound it to, or `<namespace>.<exported>` for a namespace import.
69
+ * Empty when the file never imports it, so a local helper of the same name is
70
+ * not mistaken for the package's.
71
+ */
72
+ const importedCallees = (text, module, exported) => {
73
+ const callees = [];
74
+ const imports = /import\s+([\s\S]*?)\s+from\s*['"]([^'"]+)['"]/g;
75
+ let match;
76
+ while ((match = imports.exec(text))) {
77
+ if (match[2] !== module)
78
+ continue;
79
+ const clause = match[1];
80
+ const namespace = clause.match(/\*\s+as\s+([A-Za-z_$][\w$]*)/);
81
+ if (namespace) {
82
+ callees.push(`${namespace[1]}.${exported}`);
83
+ continue;
84
+ }
85
+ const named = clause.match(/\{([\s\S]*?)\}/);
86
+ if (!named)
87
+ continue;
88
+ for (const entry of named[1].split(',')) {
89
+ const [imported, alias] = entry.split(/\s+as\s+/).map((v) => v.trim());
90
+ if (imported === exported)
91
+ callees.push(alias || imported);
92
+ }
93
+ }
94
+ return callees;
95
+ };
96
+ /**
97
+ * Whether `exported` from `module` is wired as a plugin — imported from that
98
+ * package *and* called inside a `plugins: [...]` array. An import left behind
99
+ * after the call was removed configures nothing, and a call somewhere else in
100
+ * the file is not a plugin.
101
+ */
102
+ const configuresPlugin = (text, module, exported) => {
103
+ const callees = importedCallees(text, module, exported);
104
+ if (callees.length === 0)
105
+ return false;
106
+ return pluginArrays(text).some((array) => callees.some((callee) => new RegExp(`(^|[^\\w$.])${escapeRegExp(callee)}\\s*\\(`).test(array)));
107
+ };
108
+ /**
109
+ * Whether better-auth is configured the way pikku supports.
110
+ *
111
+ * Pikku authorizes on scopes, so better-auth's `admin()` — which authorizes its
112
+ * endpoints against a `user.role` column — is unsupported: wiring it means
113
+ * running two authorization models and projecting one onto the other. The
114
+ * inspector refuses it outright at prebuild; this reports the same thing
115
+ * without one, and additionally catches the quieter half of the migration, an
116
+ * app that dropped `admin()` and never wired `ban()`, which keeps its ban
117
+ * columns and its ban UI while silently enforcing nothing.
118
+ */
119
+ export const runAuthPluginChecks = async (root, config) => {
120
+ const findings = [];
121
+ const srcDirectories = stringArray(config?.srcDirectories);
122
+ const searchDirs = srcDirectories.length
123
+ ? srcDirectories
124
+ : [join('packages', 'functions', 'src')];
125
+ const sourceFiles = (await Promise.all(searchDirs.map((dir) => listSourceFiles(join(root, dir))))).flat();
126
+ let bansAnywhere = false;
127
+ const adminFiles = [];
128
+ for (const file of sourceFiles) {
129
+ const text = await readTextSafe(file);
130
+ if (!text)
131
+ continue;
132
+ if (configuresPlugin(text, 'better-auth/plugins', 'admin'))
133
+ adminFiles.push(file);
134
+ if (configuresPlugin(text, '@pikku/better-auth', 'ban'))
135
+ bansAnywhere = true;
136
+ }
137
+ for (const file of adminFiles) {
138
+ findings.push({
139
+ id: 'better-auth-admin-plugin',
140
+ severity: 'error',
141
+ message: "better-auth's admin() plugin is not supported — it authorizes against a user.role column while pikku authorizes on scopes, so one has to be projected onto the other",
142
+ path: file,
143
+ fixHint: lines('Replace it with ban(), which keeps the one capability admin() had that', 'pikku cannot supply from outside better-auth — refusing a banned user a', 'session:', " import { ban } from '@pikku/better-auth'", ' betterAuth({ plugins: [ban()] })', 'User management is already scoped RPCs in @pikku/addon-admin: list,', 'create, ban, remove, revoke sessions and set password.'),
144
+ });
145
+ }
146
+ if (!bansAnywhere && adminFiles.length === 0) {
147
+ const configuresAuth = sourceFiles.length > 0;
148
+ if (configuresAuth) {
149
+ for (const file of sourceFiles) {
150
+ const text = await readTextSafe(file);
151
+ if (!text || !/\bpikkuBetterAuth\s*\(/.test(text))
152
+ continue;
153
+ findings.push({
154
+ id: 'better-auth-no-ban-plugin',
155
+ severity: 'warn',
156
+ message: 'better-auth is configured without ban() — the banned/banReason/banExpires columns do not exist, so banning a user through @pikku/addon-admin has nothing to write and nothing refuses their next sign-in',
157
+ path: file,
158
+ fixHint: lines(" import { ban } from '@pikku/better-auth'", ' betterAuth({ plugins: [ban()] })', 'Wire it wherever the rest of your plugins are, then run', '`pikku db migrate` to add the columns.'),
159
+ });
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ return findings;
165
+ };
@@ -123,7 +123,7 @@ export const runPersonaChecks = async (root, config) => {
123
123
  return findings;
124
124
  }
125
125
  if (!(await hasActorSignIn(sourceFiles))) {
126
- w('personas-no-actor-sign-in', `${personas} persona${personas === 1 ? ' is' : 's are'} declared, but nothing wires actor sign-in — a scenario run signs in over POST /auth/sign-in/actor, so every persona fails to acquire a session`, pikkuConfigPath, lines('Wire the actor plugin where better-auth is configured:', " import { actor } from '@pikku/better-auth'", ' plugins: [actor({ secret: SCENARIO_ACTOR_SECRET })]', 'The plugin refuses every sign-in while the secret is unset, so a stage', 'that must not run scenarios simply leaves SCENARIO_ACTOR_SECRET out.'));
126
+ w('personas-no-actor-sign-in', `${personas} persona${personas === 1 ? ' is' : 's are'} declared, but nothing wires actor sign-in — a scenario run signs in over POST /auth/sign-in/actor, so every persona fails to acquire a session`, pikkuConfigPath, lines('Wire the actor plugin where better-auth is configured:', " import { actor } from '@pikku/better-auth'", ' plugins: [actor({ secret: SCENARIO_ACTOR_SECRET })]', '`pikku dev` turns the endpoint on and mints the secret itself, so there', 'is nothing to configure locally. A deployed stage that must run', 'scenarios opts in with', 'PIKKU_ALLOW_ACTOR_SIGN_IN=passwordless-actor-sign-in; everywhere else', 'the endpoint stays off whatever the environment happens to contain.'));
127
127
  }
128
128
  const environments = config?.environments;
129
129
  const hasEnvironment = typeof environments === 'object' &&
@@ -4,7 +4,10 @@ import { join } from 'node:path';
4
4
  import { runKnowledgeValidate } from '@pikku/knowledge';
5
5
  import { runBootstrapChecks } from './bootstrap-checks.js';
6
6
  import { runPersonaChecks } from './persona-checks.js';
7
+ import { runAuthPluginChecks } from './auth-plugin-checks.js';
7
8
  import { runScenarioFileChecks } from './scenario-file-checks.js';
9
+ import { projectWiresChannels, runWebsocketDepsChecks, } from './websocket-deps-checks.js';
10
+ import { resolveFromProject } from '../../utils/resolve-from-project.js';
8
11
  /**
9
12
  * A malformed config is named, never reported as absent.
10
13
  *
@@ -311,8 +314,17 @@ export async function runSharedProjectChecks(root) {
311
314
  }
312
315
  // ── server bootstrap ───────────────────────────────────────────────────
313
316
  findings.push(...(await runBootstrapChecks(root, rootPkg, pikkuConfig)));
317
+ findings.push(...runWebsocketDepsChecks({
318
+ root,
319
+ runtime: typeof globalThis.Bun !== 'undefined'
320
+ ? 'bun'
321
+ : 'node',
322
+ hasChannels: await projectWiresChannels(root, typeof pikkuConfig?.outDir === 'string' ? pikkuConfig.outDir : '.pikku'),
323
+ resolve: (specifier) => resolveFromProject(root, specifier),
324
+ }));
314
325
  // ── personas, scenario files and the knowledge base ────────────────────
315
326
  findings.push(...(await runPersonaChecks(root, pikkuConfig)));
327
+ findings.push(...(await runAuthPluginChecks(root, pikkuConfig)));
316
328
  const srcDirectories = Array.isArray(pikkuConfig?.srcDirectories)
317
329
  ? pikkuConfig.srcDirectories.filter((d) => typeof d === 'string')
318
330
  : [];
@@ -0,0 +1,30 @@
1
+ import type { ValidateFinding } from './persona-checks.js';
2
+ /**
3
+ * The packages the Node dev/serve path needs to speak WebSocket. Both are
4
+ * optional peers of `@pikku/cli`, because Bun serves WebSockets natively
5
+ * through `@pikku/bun-server` and a Bun project has no use for either.
6
+ */
7
+ export declare const NODE_WEBSOCKET_PACKAGES: readonly ["@pikku/ws", "ws"];
8
+ export type WebsocketDepsInput = {
9
+ root: string;
10
+ /** The runtime the CLI is running on. Bun needs neither package. */
11
+ runtime: 'node' | 'bun';
12
+ /** Whether the project wires any channels. Nothing is reported when not. */
13
+ hasChannels: boolean;
14
+ /** Resolves a specifier from the project. `undefined` when not installed. */
15
+ resolve: (specifier: string) => string | undefined;
16
+ };
17
+ /**
18
+ * Reports the optional WebSocket peers when a Node-hosted project that wires
19
+ * channels is missing them.
20
+ *
21
+ * `pikku dev` / `pikku serve` under Node start over plain HTTP when the
22
+ * packages are absent, rather than failing: a project with no channels does not
23
+ * need them, and making them hard dependencies of the CLI is what put a second
24
+ * `@pikku/core` in the tree. That leaves a project that *does* wire channels
25
+ * with sockets that never connect and nothing pointing at the cause — which is
26
+ * this check.
27
+ */
28
+ export declare function runWebsocketDepsChecks(input: WebsocketDepsInput): ValidateFinding[];
29
+ /** True when codegen produced at least one channel for this project. */
30
+ export declare function projectWiresChannels(root: string, outDir: string): Promise<boolean>;
@@ -0,0 +1,42 @@
1
+ import { join } from 'node:path';
2
+ import { readJsonSafe } from './shared-checks.js';
3
+ /**
4
+ * The packages the Node dev/serve path needs to speak WebSocket. Both are
5
+ * optional peers of `@pikku/cli`, because Bun serves WebSockets natively
6
+ * through `@pikku/bun-server` and a Bun project has no use for either.
7
+ */
8
+ export const NODE_WEBSOCKET_PACKAGES = ['@pikku/ws', 'ws'];
9
+ /**
10
+ * Reports the optional WebSocket peers when a Node-hosted project that wires
11
+ * channels is missing them.
12
+ *
13
+ * `pikku dev` / `pikku serve` under Node start over plain HTTP when the
14
+ * packages are absent, rather than failing: a project with no channels does not
15
+ * need them, and making them hard dependencies of the CLI is what put a second
16
+ * `@pikku/core` in the tree. That leaves a project that *does* wire channels
17
+ * with sockets that never connect and nothing pointing at the cause — which is
18
+ * this check.
19
+ */
20
+ export function runWebsocketDepsChecks(input) {
21
+ const { root, runtime, hasChannels, resolve } = input;
22
+ if (runtime === 'bun' || !hasChannels)
23
+ return [];
24
+ const missing = NODE_WEBSOCKET_PACKAGES.filter((name) => !resolve(name));
25
+ if (missing.length === 0)
26
+ return [];
27
+ const named = missing.join(' and ');
28
+ return [
29
+ {
30
+ id: 'websocket-deps-missing',
31
+ severity: 'error',
32
+ message: `${named} ${missing.length > 1 ? 'are' : 'is'} not installed, but this project wires channels — \`pikku dev\` and \`pikku serve\` will start without WebSocket support and every channel connection will fail`,
33
+ path: join(root, 'package.json'),
34
+ fixHint: `Install ${NODE_WEBSOCKET_PACKAGES.join(' ')} in this project. They are optional peer dependencies of @pikku/cli so that Bun projects, which serve WebSockets natively, do not pull them in`,
35
+ },
36
+ ];
37
+ }
38
+ /** True when codegen produced at least one channel for this project. */
39
+ export async function projectWiresChannels(root, outDir) {
40
+ const meta = await readJsonSafe(join(root, outDir, 'channel', 'pikku-channels-meta.gen.json'));
41
+ return !!meta && Object.keys(meta).length > 0;
42
+ }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * `SCENARIO_ACTOR_SECRET` is the platform's, not the application's: nothing in
5
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
6
+ * actor in, by `pikku scenario` / `pikku persona`, and by
7
7
  * the Playwright provider. Declaring it here — rather than leaving every project
8
8
  * to hand-write the same `defineSecret` — is the same reasoning that puts
9
9
  * `BETTER_AUTH_SECRET` in the auth scaffold: a value the framework requires
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * `SCENARIO_ACTOR_SECRET` is the platform's, not the application's: nothing in
5
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
6
+ * actor in, by `pikku scenario` / `pikku persona`, and by
7
7
  * the Playwright provider. Declaring it here — rather than leaving every project
8
8
  * to hand-write the same `defineSecret` — is the same reasoning that puts
9
9
  * `BETTER_AUTH_SECRET` in the auth scaffold: a value the framework requires
@@ -1,3 +1,13 @@
1
+ import type { PersonaDefinitions } from '@pikku/core/persona';
2
+ import type { ScopeDefinitions } from '@pikku/core/scope';
3
+ /**
4
+ * The declared scopes, plus the `app` tree derived from the personas.
5
+ *
6
+ * Appended here rather than in the inspector because it is not a declaration
7
+ * the AST can find: it is the personas' `app` field read as a scope, and this
8
+ * is the one place the two states are both in hand.
9
+ */
10
+ export declare const withAppScopes: (scopes: ScopeDefinitions, personas: PersonaDefinitions) => ScopeDefinitions;
1
11
  export declare const pikkuScopes: import("#pikku/function").PikkuFunctionConfig<{
2
12
  bootstrap?: boolean;
3
13
  }, void, "rpc" | "session", import("#pikku/function").PikkuFunctionSessionless<{
@@ -4,6 +4,22 @@ import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-
4
4
  import { serializeScopesTypes } from './serialize-scopes-types.js';
5
5
  import { serializeScopesClient } from './serialize-scopes-client.js';
6
6
  import { validateAndBuildScopeDefinitionsMeta } from '@pikku/core/scope';
7
+ import { APP_SCOPE_ROOT, buildAppScopeDefinition } from '@pikku/core/persona';
8
+ /**
9
+ * The declared scopes, plus the `app` tree derived from the personas.
10
+ *
11
+ * Appended here rather than in the inspector because it is not a declaration
12
+ * the AST can find: it is the personas' `app` field read as a scope, and this
13
+ * is the one place the two states are both in hand.
14
+ */
15
+ export const withAppScopes = (scopes, personas) => {
16
+ const declared = scopes.find((scope) => scope.name === APP_SCOPE_ROOT);
17
+ if (declared) {
18
+ throw new Error(`'${APP_SCOPE_ROOT}' is reserved: it is derived from the apps your personas name, and ${declared.sourceFile ?? 'a defineScope call'} declares it too. Rename that scope root — two trees answering to one name make a grant ambiguous.`);
19
+ }
20
+ const appScopes = buildAppScopeDefinition(personas);
21
+ return appScopes ? [...scopes, appScopes] : scopes;
22
+ };
7
23
  export const pikkuScopes = pikkuSessionlessFunc({
8
24
  func: async ({ logger, config, getInspectorState }, data) => {
9
25
  const { scopesFile, scopesMetaJsonFile } = config;
@@ -19,15 +35,16 @@ export const pikkuScopes = pikkuSessionlessFunc({
19
35
  // Scopes step regenerates it with the declarations once setup has run.
20
36
  const bootstrap = data?.bootstrap ?? false;
21
37
  const state = await getInspectorState(false, bootstrap, bootstrap);
38
+ const definitions = withAppScopes(state.scopes.definitions, state.personas.definitions);
22
39
  const content = serializeScopesTypes({
23
- definitions: state.scopes.definitions,
40
+ definitions,
24
41
  });
25
42
  await writeFileInDir(logger, scopesFile, content);
26
43
  if (scopesClientFile) {
27
- await writeFileInDir(logger, scopesClientFile, serializeScopesClient({ definitions: state.scopes.definitions }));
44
+ await writeFileInDir(logger, scopesClientFile, serializeScopesClient({ definitions }));
28
45
  }
29
46
  if (scopesMetaJsonFile) {
30
- const meta = validateAndBuildScopeDefinitionsMeta(state.scopes.definitions);
47
+ const meta = validateAndBuildScopeDefinitionsMeta(definitions);
31
48
  await writeFileInDir(logger, scopesMetaJsonFile, JSON.stringify(meta, null, 2));
32
49
  }
33
50
  },
@@ -206,6 +206,19 @@ const virtualUserScheduleFields = {
206
206
  nextRunAt: z.string(),
207
207
  lastRunId: z.string().nullable(),
208
208
  lastRunAt: z.string().nullable(),
209
+ /**
210
+ * What \`definePersonas()\` says about this persona now, alongside what the
211
+ * row is actually running.
212
+ *
213
+ * A schedule is turned on once and then outlives the declaration it was
214
+ * written from: someone edits \`personas.ts\`, redeploys, and the row keeps
215
+ * running last month's goals with nothing anywhere to say so. Both sides
216
+ * cross the wire so that difference is readable rather than inferred.
217
+ */
218
+ declared: z.object({
219
+ disposition: Disposition,
220
+ goals: z.array(z.string()),
221
+ }),
209
222
  }
210
223
 
211
224
  export const SetVirtualUserScheduleOutput = z.object(virtualUserScheduleFields)
@@ -291,6 +304,16 @@ const API_URL_VARIABLE = 'VIRTUAL_USER_API_URL'
291
304
  const SECRET_VARIABLE = 'SCENARIO_ACTOR_SECRET'
292
305
  const MODEL_VARIABLE = 'VIRTUAL_USER_MODEL'
293
306
 
307
+ /**
308
+ * The same two variables a scenario run reads, because a virtual user signs in
309
+ * and calls through exactly the doors a scenario does. An app that mounts auth
310
+ * somewhere other than the root — \`/api/auth\` is the common one — has no other
311
+ * way to say so, and without them the run signs in against a 404 and spends its
312
+ * whole budget thinking about why nothing works.
313
+ */
314
+ const SIGN_IN_PATH_VARIABLE = 'SCENARIO_SIGN_IN_PATH'
315
+ const RPC_PATH_VARIABLE = 'SCENARIO_RPC_PATH'
316
+
294
317
  /**
295
318
  * One run on the wire. Findings and intents are free-form by design — the
296
319
  * engine records what it noticed, not a fixed row shape — so they cross as the
@@ -340,9 +363,16 @@ const serializeRun = (run: VirtualUserRunRecord) => ({
340
363
  const startVirtualUserRun = async (
341
364
  services: {
342
365
  virtualUserRunStore?: VirtualUserRunStore
343
- config: { nodeEnv?: string }
366
+ // \`unknown\` because an application's Config is its own interface and need
367
+ // not declare nodeEnv at all. A structural \`{ nodeEnv?: string }\` shares no
368
+ // property with such a config and TypeScript rejects the whole call.
369
+ config: unknown
370
+ },
371
+ // Named literally rather than as \`string\`: a project's generated rpc.invoke
372
+ // is generic over its own map's keys, and \`string\` is not one of them.
373
+ rpc: {
374
+ invoke: (name: 'executeVirtualUserRun', data: any) => Promise<unknown>
344
375
  },
345
- rpc: { invoke: (name: string, data: any) => Promise<unknown> },
346
376
  input: {
347
377
  persona: string
348
378
  disposition?: string
@@ -384,7 +414,7 @@ const startVirtualUserRun = async (
384
414
  // does wrong, which is not a thing to do to real customers' data. Checked
385
415
  // against the effective disposition, so the override cannot smuggle one in.
386
416
  if (
387
- config.nodeEnv === 'production' &&
417
+ (config as { nodeEnv?: string } | undefined)?.nodeEnv === 'production' &&
388
418
  disposition !== PRODUCTION_DISPOSITION
389
419
  ) {
390
420
  throw new Error(
@@ -525,27 +555,38 @@ export const getVirtualUserRunSteps = pikkuFunc({
525
555
  * what every other call here takes; the engine's own duration also accepts
526
556
  * \`'30m'\`, which nothing on this side ever writes.
527
557
  */
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
- })
558
+ const serializeSchedule = (schedule: VirtualUserScheduleRecord) => {
559
+ const persona = personaConfigs[
560
+ schedule.persona as keyof typeof personaConfigs
561
+ ] as { goals?: string[]; disposition?: string } | undefined
562
+ const declared = {
563
+ disposition: (persona?.disposition ??
564
+ 'realistic') as VirtualUserDisposition,
565
+ goals: persona?.goals ?? [],
566
+ }
567
+ return {
568
+ persona: schedule.persona,
569
+ enabled: schedule.enabled,
570
+ disposition: schedule.disposition,
571
+ goals: schedule.goals,
572
+ budget: schedule.budget
573
+ ? {
574
+ steps: schedule.budget.steps,
575
+ mutations: schedule.budget.mutations,
576
+ durationMs:
577
+ typeof schedule.budget.duration === 'number'
578
+ ? schedule.budget.duration
579
+ : undefined,
580
+ }
581
+ : null,
582
+ minIntervalMs: schedule.minIntervalMs,
583
+ maxIntervalMs: schedule.maxIntervalMs,
584
+ nextRunAt: schedule.nextRunAt.toISOString(),
585
+ lastRunId: schedule.lastRunId,
586
+ lastRunAt: schedule.lastRunAt ? schedule.lastRunAt.toISOString() : null,
587
+ declared,
588
+ }
589
+ }
549
590
 
550
591
  export const setVirtualUserSchedule = pikkuFunc({
551
592
  tags: ['pikku'],
@@ -612,7 +653,6 @@ export const listVirtualUserSchedules = pikkuFunc({
612
653
  'Which personas are on a clock, how often they run, and when each is next due.',
613
654
  expose: true,
614
655
  scopes: ['virtualUser:read'],
615
- input: null,
616
656
  output: ListVirtualUserSchedulesOutput,
617
657
  func: async ({ virtualUserScheduleStore }) => {
618
658
  if (!virtualUserScheduleStore) {
@@ -759,7 +799,13 @@ export const executeVirtualUserRun = pikkuSessionlessFunc({
759
799
  agentsMeta: await metaService.getAgentsMeta(),
760
800
  })
761
801
 
762
- const signedIn = createPersonas({ apiUrl, secret, model })
802
+ const signedIn = createPersonas({
803
+ apiUrl,
804
+ secret,
805
+ model,
806
+ signInPath: (await variables.get(SIGN_IN_PATH_VARIABLE)) ?? undefined,
807
+ rpcPath: (await variables.get(RPC_PATH_VARIABLE)) ?? undefined,
808
+ })
763
809
  const target = signedIn[personaId as keyof typeof signedIn]
764
810
  if (!target) {
765
811
  throw new Error(\`Persona "\${personaId}" cannot sign in\`)
@@ -103,7 +103,7 @@ export const pikkuWorkflow = pikkuSessionlessFunc({
103
103
  {
104
104
  const agentMapImportPath = getFileImportRelativePath(config.personasWiringFile, config.agentMapDeclarationFile, packageMappings);
105
105
  const exposedRpcMapImportPath = getFileImportRelativePath(config.personasWiringFile, config.rpcMapDeclarationFile, packageMappings);
106
- await writeFileInDir(logger, config.personasWiringFile, serializePersonas(personas, agentMapImportPath, exposedRpcMapImportPath));
106
+ await writeFileInDir(logger, config.personasWiringFile, serializePersonas(personas, agentMapImportPath, exposedRpcMapImportPath, config.environments ?? {}));
107
107
  // Same reason as the scenario meta above: a project generated before this
108
108
  // moved to `scenarios/` still has a copy under `workflow/`, and tsc
109
109
  // compiles every file in the output tree whether anything imports it.
@@ -1,3 +1,4 @@
1
+ import type { PersonaEnvironment } from '@pikku/core/persona';
1
2
  import type { ResolvedPersona } from '@pikku/core/services';
2
3
  /**
3
4
  * The runtime half of `definePersonas()`.
@@ -7,4 +8,4 @@ import type { ResolvedPersona } from '@pikku/core/services';
7
8
  * to arrive at the same one. Baking them here is what makes that a single fact
8
9
  * rather than three implementations of the same rule.
9
10
  */
10
- export declare const serializePersonas: (personas: Record<string, ResolvedPersona>, agentMapImportPath: string, rpcMapImportPath: string) => string;
11
+ export declare const serializePersonas: (personas: Record<string, ResolvedPersona>, agentMapImportPath: string, rpcMapImportPath: string, environments?: Readonly<Record<string, PersonaEnvironment>>) => string;
@@ -6,7 +6,14 @@
6
6
  * to arrive at the same one. Baking them here is what makes that a single fact
7
7
  * rather than three implementations of the same rule.
8
8
  */
9
- export const serializePersonas = (personas, agentMapImportPath, rpcMapImportPath) => {
9
+ export const serializePersonas = (personas, agentMapImportPath, rpcMapImportPath, environments = {}) => {
10
+ // Only the flag the rule reads. The rest of an environment entry is its
11
+ // apiUrl and paths, which belong to the machine running `pikku scenario` and
12
+ // have no business inside the deployed bundle.
13
+ const projected = Object.fromEntries(Object.entries(environments).map(([name, environment]) => [
14
+ name,
15
+ environment.production ? { production: true } : {},
16
+ ]));
10
17
  return `/** Personas declared with definePersonas() */
11
18
  import { createHttpPersonas } from '@pikku/core/persona'
12
19
  import type { HttpPersonasConfig } from '@pikku/core/persona'
@@ -27,6 +34,18 @@ export type PersonaName = keyof typeof personaConfigs
27
34
  */
28
35
  export const personaList: ResolvedPersona[] = Object.values(personaConfigs)
29
36
 
37
+ /**
38
+ * The \`environments\` block of pikku.config.json, carried into the runtime.
39
+ *
40
+ * A persona's environments are checked twice: at build time against the config,
41
+ * and again wherever a persona is signed in or provisioned, against the
42
+ * environment the running process says it is (\`PIKKU_ENV\`). The second check
43
+ * is not the first one repeated — the build check trusts the file, and the run
44
+ * check does not trust which artifact got deployed — so the rule needs the
45
+ * config on both sides of the build.
46
+ */
47
+ export const personaEnvironments = ${JSON.stringify(projected, null, 2)} as const satisfies Record<string, { production?: boolean }>
48
+
30
49
  export type AgentName = keyof AgentMap & string
31
50
  /**
32
51
  * The personas this project declares, keyed by name — the \`actors\` a scenario
@@ -0,0 +1,5 @@
1
+ import type { Logger } from '@pikku/core/services';
2
+ export declare const ACTOR_SECRET_ENV = "SCENARIO_ACTOR_SECRET";
3
+ export declare const VITE_ACTOR_SECRET_ENV = "VITE_SCENARIO_ACTOR_SECRET";
4
+ export declare const enableDevActorSignIn: (logger: Logger) => void;
5
+ export declare const disableDevActorSignIn: (logger: Logger) => void;
@@ -0,0 +1,27 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { ACTOR_SIGN_IN_OPT_IN_ENV, DEV_ACTOR_SIGN_IN_ENV, } from '@pikku/better-auth';
3
+ export const ACTOR_SECRET_ENV = 'SCENARIO_ACTOR_SECRET';
4
+ export const VITE_ACTOR_SECRET_ENV = 'VITE_SCENARIO_ACTOR_SECRET';
5
+ const mintEphemeralSecret = () => randomBytes(32).toString('base64url');
6
+ export const enableDevActorSignIn = (logger) => {
7
+ process.env[DEV_ACTOR_SIGN_IN_ENV] = 'true';
8
+ const configured = process.env[ACTOR_SECRET_ENV] || undefined;
9
+ const configuredForFrontend = process.env[VITE_ACTOR_SECRET_ENV] || undefined;
10
+ if (configured &&
11
+ configuredForFrontend &&
12
+ configured !== configuredForFrontend) {
13
+ logger.warn(`${ACTOR_SECRET_ENV} and ${VITE_ACTOR_SECRET_ENV} are set to different values — using ${ACTOR_SECRET_ENV}, since that is the one the server compares against`);
14
+ }
15
+ const secret = configured ?? configuredForFrontend ?? mintEphemeralSecret();
16
+ process.env[ACTOR_SECRET_ENV] = secret;
17
+ process.env[VITE_ACTOR_SECRET_ENV] = secret;
18
+ logger.info((configured ?? configuredForFrontend)
19
+ ? `Actor quick login enabled, using the ${configured ? ACTOR_SECRET_ENV : VITE_ACTOR_SECRET_ENV} already in this environment`
20
+ : 'Actor quick login enabled with a secret minted for this run — nothing to configure, and it is gone when the server stops');
21
+ };
22
+ export const disableDevActorSignIn = (logger) => {
23
+ if (process.env[DEV_ACTOR_SIGN_IN_ENV] !== undefined) {
24
+ logger.warn(`${DEV_ACTOR_SIGN_IN_ENV} was set in this environment and has been cleared — \`pikku serve\` never enables actor sign-in. Set ${ACTOR_SIGN_IN_OPT_IN_ENV} if this stage is meant to run scenarios.`);
25
+ }
26
+ delete process.env[DEV_ACTOR_SIGN_IN_ENV];
27
+ };
@@ -2,11 +2,22 @@
2
2
  * Node dev server runner — `@pikku/node-http-server` + the `ws` WebSocketServer.
3
3
  * Used when the CLI runs under Node. Owns the WebSocketServer so teardown
4
4
  * (close ws then the http server) is encapsulated in the returned instance.
5
+ *
6
+ * `@pikku/ws` and `ws` are optional peers of the CLI, so they are imported
7
+ * dynamically and resolved from the *project* rather than from the CLI: both
8
+ * peer on `@pikku/core`, and a copy loaded out of the CLI's own tree is paired
9
+ * with the CLI's core instead of the project's. A project without them served
10
+ * over plain HTTP — `pikku validate` reports the missing packages.
5
11
  */
6
12
  import type { EventHubService } from '@pikku/core/channel';
7
13
  import type { Logger } from '@pikku/core/services';
8
14
  import type { DevServerRunner, DevServerInstance, DevServerConfig, DevServerOptions } from './dev-server-runner.interface.js';
15
+ export declare const WEBSOCKET_PACKAGES_MISSING = "WebSocket support is disabled: @pikku/ws and ws are not installed in this project. Install them (npm install @pikku/ws ws) to serve channels over Node.";
9
16
  export declare class NodeServerRunner implements DevServerRunner {
17
+ private readonly rootDir;
18
+ private pikkuWs?;
19
+ private ws?;
20
+ constructor(rootDir: string);
10
21
  createEventHub(): Promise<EventHubService<any>>;
11
22
  createServer(config: DevServerConfig, logger: Logger, options?: DevServerOptions): DevServerInstance;
12
23
  }