@pikku/cli 0.12.117 → 0.12.119

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 (253) hide show
  1. package/CHANGELOG.md +216 -0
  2. package/console-app/assets/{abnfDiagram-N423BO3Z-WNIAg_Oa.js → abnfDiagram-N423BO3Z-CiK5F2ml.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-FMO1RByN.js +1 -0
  4. package/console-app/assets/{architectureDiagram-T3A2C74G-DM698mgf.js → architectureDiagram-T3A2C74G-g4Elcs_0.js} +1 -1
  5. package/console-app/assets/{blockDiagram-VBNYF7ZC-DPfy9K5S.js → blockDiagram-VBNYF7ZC-CI6muc3M.js} +1 -1
  6. package/console-app/assets/{c4Diagram-5PPSVZJV-DhLbbBbX.js → c4Diagram-5PPSVZJV-CjzL9yGp.js} +1 -1
  7. package/console-app/assets/channel-Dp8A5NJg.js +1 -0
  8. package/console-app/assets/{chunk-2GRJ4B5K-C1RQ1gvs.js → chunk-2GRJ4B5K-C68TKOLb.js} +1 -1
  9. package/console-app/assets/{chunk-4I5QYGJK-BkzSdNc_.js → chunk-4I5QYGJK-D_DINIPO.js} +1 -1
  10. package/console-app/assets/{chunk-5RXB4S5H-DZpBpdcU.js → chunk-5RXB4S5H-D8mEuiea.js} +1 -1
  11. package/console-app/assets/{chunk-6Q2QTUOP-mZObxdld.js → chunk-6Q2QTUOP-CVNqLnb8.js} +1 -1
  12. package/console-app/assets/{chunk-7Z6QIM7H-C0HbxZEa.js → chunk-7Z6QIM7H-Bnih6HyS.js} +1 -1
  13. package/console-app/assets/{chunk-GF5L2VYU-DXPVwWqW.js → chunk-GF5L2VYU-C5efrbFt.js} +1 -1
  14. package/console-app/assets/{chunk-I66GZJ75-pVpq9ckD.js → chunk-I66GZJ75-dckv50K3.js} +1 -1
  15. package/console-app/assets/{chunk-JQJVKLGR-DJQ8K4wK.js → chunk-JQJVKLGR-HirBo6LQ.js} +1 -1
  16. package/console-app/assets/{chunk-KBJHAD2P-B8XuxRCO.js → chunk-KBJHAD2P-lknhmPj0.js} +1 -1
  17. package/console-app/assets/{chunk-NSK5VX7P-cYzQU4bD.js → chunk-NSK5VX7P-tGAl7zxF.js} +1 -1
  18. package/console-app/assets/{chunk-QR6OTTB3-CNoTFsqn.js → chunk-QR6OTTB3-BlSKCrEq.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-DoKRUO_N.js → chunk-RYQCIY6F-CwQmwEV-.js} +1 -1
  20. package/console-app/assets/{chunk-UBXNYLIW-BqcIoUpU.js → chunk-UBXNYLIW-D1Dn6ZK0.js} +1 -1
  21. package/console-app/assets/{chunk-W5SLKNZC-p0Qalbjj.js → chunk-W5SLKNZC-DOkZHhST.js} +1 -1
  22. package/console-app/assets/{chunk-WRU74C26-DUMn96Dr.js → chunk-WRU74C26-CB4orIKm.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-BMybY6E-.js +1 -0
  24. package/console-app/assets/classDiagram-JCYQIIEL-C-K-UAUR.js +1 -0
  25. package/console-app/assets/classDiagram-v2-OCEON4UE-C-K-UAUR.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-BCh3BlPI.js → cose-bilkent-JH36ORCC-stBhaqpU.js} +1 -1
  27. package/console-app/assets/{cynefin-VYW2F7L2-ZfghUZsD.js → cynefin-VYW2F7L2-Bb1nmKWZ.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-MW4NZA55-DcV3QXCe.js → cynefinDiagram-MW4NZA55-Bp8fMJi-.js} +1 -1
  29. package/console-app/assets/{dagre-BPFWXLBN.js → dagre-DshmzimC.js} +1 -1
  30. package/console-app/assets/{dagre-VZM6K2ZE-DETshBO-.js → dagre-VZM6K2ZE-DIl1klf5.js} +1 -1
  31. package/console-app/assets/{diagram-7IWD3JNH-CsLX3P2o.js → diagram-7IWD3JNH-DdkSOxKT.js} +1 -1
  32. package/console-app/assets/{diagram-B4RE2ZJO-o2zcxm5S.js → diagram-B4RE2ZJO-9Dj9O4L8.js} +1 -1
  33. package/console-app/assets/{diagram-LBJQPF4R-BD2zKU21.js → diagram-LBJQPF4R-CxBb73tU.js} +1 -1
  34. package/console-app/assets/{diagram-Q27KOJAE-DHaacyna.js → diagram-Q27KOJAE-CPpUmnvb.js} +1 -1
  35. package/console-app/assets/{diagram-UB23O5K3-pSLCMUyV.js → diagram-UB23O5K3-7wV_Db6W.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-BXEA7PRR-BDzawX3O.js → ebnfDiagram-BXEA7PRR-Cxzbf8pk.js} +1 -1
  37. package/console-app/assets/{erDiagram-JOGREHBK-CyYnPmbN.js → erDiagram-JOGREHBK-BBPLq83P.js} +1 -1
  38. package/console-app/assets/eventmodeling-45OFAUF4-CLUnygIv.js +1 -0
  39. package/console-app/assets/flowDiagram-UKHOOZJN-D3mDcWWx.js +1 -0
  40. package/console-app/assets/{ganttDiagram-PKOTCBZU-3fhCBbPA.js → ganttDiagram-PKOTCBZU-Erh7WK4H.js} +1 -1
  41. package/console-app/assets/{gitGraph-TEB2WS4Q-CdLRkVux.js → gitGraph-TEB2WS4Q-o5FLRF57.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-DS77QQ5N-CC1epdKw.js → gitGraphDiagram-DS77QQ5N-BFbsaenX.js} +1 -1
  43. package/console-app/assets/{index-GruDnAhJ.css → index-Mq-YbqNO.css} +1 -1
  44. package/console-app/assets/{index-BRmokG9t.js → index-w2ZoJr8X.js} +178 -178
  45. package/console-app/assets/{info-DKCQHKI2-BJs8WkzC.js → info-DKCQHKI2-Dkcrdr1w.js} +1 -1
  46. package/console-app/assets/{infoDiagram-6WML65LV-uoOwoIeI.js → infoDiagram-6WML65LV-iZVC5Wzy.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-WSZJBQD7-vCUJhwTi.js → ishikawaDiagram-WSZJBQD7-BjV8LLeA.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-NVQOT4AX-D2QcIlLG.js → journeyDiagram-NVQOT4AX-D3rpLjYi.js} +1 -1
  49. package/console-app/assets/{kanban-definition-27J2QSJJ-JwwKowmR.js → kanban-definition-27J2QSJJ-Dds5TFyv.js} +1 -1
  50. package/console-app/assets/{line-h2oRreYF.js → line-D1rUAT18.js} +1 -1
  51. package/console-app/assets/{linear-Dz002X_a.js → linear-fQ_6Sc79.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-DTgmT0AA.js → mermaid-parser.core-B9Q-4IXF.js} +3 -3
  53. package/console-app/assets/{mermaid.core-1jSEDr-j.js → mermaid.core-bPhADShw.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-FAOFIHXS-C3Txxhj4.js → mindmap-definition-FAOFIHXS-CULlU6Fy.js} +1 -1
  55. package/console-app/assets/{packet-7NZHBO7P-BGG7zIK2.js → packet-7NZHBO7P-DpwzoQpc.js} +1 -1
  56. package/console-app/assets/{pegDiagram-VL7TDLO6--g1r_rov.js → pegDiagram-VL7TDLO6-B9fKKc4F.js} +1 -1
  57. package/console-app/assets/{pie-RZYD4A2V-C5OepMH7.js → pie-RZYD4A2V-DkXpqWAI.js} +1 -1
  58. package/console-app/assets/{pieDiagram-7S7Q4E2Y-DGD_LzGm.js → pieDiagram-7S7Q4E2Y-Bcpn2rUM.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-CIZ2JOQS-CyBQQlD2.js → quadrantDiagram-CIZ2JOQS-Bd5wWFj_.js} +1 -1
  60. package/console-app/assets/{radar-I7S5WNFK-CaBOAO95.js → radar-I7S5WNFK-ylw-u3HI.js} +1 -1
  61. package/console-app/assets/{railroad-3IZDKUUU-2aTWN4Pw.js → railroad-3IZDKUUU-D4LMucL-.js} +1 -1
  62. package/console-app/assets/railroad-abnf-AHOZXSZD-CRTVzK1g.js +1 -0
  63. package/console-app/assets/railroad-ebnf-EBAXGLYW-CB13ugAF.js +1 -0
  64. package/console-app/assets/railroad-peg-LSFZ7HO6-AN8zyQYp.js +1 -0
  65. package/console-app/assets/{railroadDiagram-AXF67PYL-3Nr0rlTp.js → railroadDiagram-AXF67PYL-CZukQql9.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-LRYGKXZP-BN7sEwSa.js → requirementDiagram-LRYGKXZP-Ou4nRENK.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-W5VNT64P-CXZR46wO.js → sankeyDiagram-W5VNT64P-BKdG-c_o.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-SI44F4Z6-DNtmdJ4U.js → sequenceDiagram-SI44F4Z6-Dc7d5obe.js} +1 -1
  69. package/console-app/assets/{src-x1E3pJJ-.js → src-BFb2oeKx.js} +1 -1
  70. package/console-app/assets/{stateDiagram-OKZ733FA-B_GyYdl_.js → stateDiagram-OKZ733FA-BA9fuN7b.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-UEYNNEHI-D13a6mUd.js +1 -0
  72. package/console-app/assets/{swimlanes-SLNWSIFB-D4N7VdF7.js → swimlanes-SLNWSIFB-l-ETgM83.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-BsC-B2Z9.js +8 -0
  74. package/console-app/assets/{timeline-definition-Z64GVDOM-DU5rsOWQ.js → timeline-definition-Z64GVDOM-Uj_ByeVr.js} +1 -1
  75. package/console-app/assets/{treeView-QDETBFTQ-DaKM_9sB.js → treeView-QDETBFTQ-BvFRkVGY.js} +1 -1
  76. package/console-app/assets/{treemap-6X3UGDF4-BabsKtZY.js → treemap-6X3UGDF4-BV5T7Dw9.js} +1 -1
  77. package/console-app/assets/{vennDiagram-T6HMQDX7-Dniftpq0.js → vennDiagram-T6HMQDX7-DAFU8gAo.js} +1 -1
  78. package/console-app/assets/{wardley-OPB4EBWU-BWffQNys.js → wardley-OPB4EBWU-MLNuhuiA.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-T6FBY63Y-CQ74fVdn.js → wardleyDiagram-T6FBY63Y-B1lCTfi1.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-ELKLHX3M-D-XxbGIL.js → xychartDiagram-ELKLHX3M-Bw3zhuWc.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 +6 -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 +14 -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 +26 -7
  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/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  154. package/dist/.pikku/scenarios/index.d.ts +1 -1
  155. package/dist/.pikku/scenarios/index.js +1 -1
  156. package/dist/.pikku/scenarios/pikku-personas.gen.d.ts +11 -0
  157. package/dist/.pikku/scenarios/pikku-personas.gen.js +12 -1
  158. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  159. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  160. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  161. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  162. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  163. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  164. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  165. package/dist/.pikku/scheduler/index.d.ts +1 -1
  166. package/dist/.pikku/scheduler/index.js +1 -1
  167. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  168. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  169. package/dist/.pikku/schemas/register.gen.js +3 -1
  170. package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -1
  171. package/dist/.pikku/schemas/schemas/PikkuMetaApplyInput.schema.json +1 -0
  172. package/dist/.pikku/scopes/index.d.ts +1 -1
  173. package/dist/.pikku/scopes/index.js +1 -1
  174. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  175. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  176. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  177. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  178. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  179. package/dist/.pikku/secrets/index.d.ts +1 -1
  180. package/dist/.pikku/secrets/index.js +1 -1
  181. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  182. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  183. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  184. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  185. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  186. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  187. package/dist/.pikku/setup/index.d.ts +1 -1
  188. package/dist/.pikku/setup/index.js +1 -1
  189. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  190. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  191. package/dist/.pikku/trigger/index.d.ts +1 -1
  192. package/dist/.pikku/trigger/index.js +1 -1
  193. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  194. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  195. package/dist/.pikku/variables/index.d.ts +1 -1
  196. package/dist/.pikku/variables/index.js +1 -1
  197. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  198. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  199. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  200. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  201. package/dist/.pikku/workflow/index.d.ts +1 -1
  202. package/dist/.pikku/workflow/index.js +1 -1
  203. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  204. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  205. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  206. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  207. package/dist/bin/pikku-bin.mjs +2 -2
  208. package/dist/src/cli.wiring.js +7 -10
  209. package/dist/src/fabric/functions/smoke.function.js +2 -2
  210. package/dist/src/fabric/functions/validate.function.js +2 -2
  211. package/dist/src/fabric/lib/git.js +31 -1
  212. package/dist/src/functions/commands/dev.js +2 -0
  213. package/dist/src/functions/commands/meta.d.ts +7 -0
  214. package/dist/src/functions/commands/meta.js +65 -0
  215. package/dist/src/functions/commands/persona-sync.d.ts +14 -22
  216. package/dist/src/functions/commands/persona-sync.js +29 -107
  217. package/dist/src/functions/commands/serve.js +2 -0
  218. package/dist/src/functions/validate/auth-plugin-checks.d.ts +16 -0
  219. package/dist/src/functions/validate/auth-plugin-checks.js +165 -0
  220. package/dist/src/functions/validate/persona-checks.js +1 -1
  221. package/dist/src/functions/validate/shared-checks.js +12 -0
  222. package/dist/src/functions/validate/websocket-deps-checks.d.ts +30 -0
  223. package/dist/src/functions/validate/websocket-deps-checks.js +42 -0
  224. package/dist/src/functions/wirings/personas/serialize-personas-secrets.d.ts +1 -1
  225. package/dist/src/functions/wirings/personas/serialize-personas-secrets.js +1 -1
  226. package/dist/src/functions/wirings/scopes/pikku-command-scopes.d.ts +10 -0
  227. package/dist/src/functions/wirings/scopes/pikku-command-scopes.js +20 -3
  228. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +1 -1
  229. package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +2 -1
  230. package/dist/src/functions/wirings/workflow/serialize-personas.js +20 -1
  231. package/dist/src/server/actor-sign-in.d.ts +5 -0
  232. package/dist/src/server/actor-sign-in.js +27 -0
  233. package/dist/src/server/node-server-runner.d.ts +11 -0
  234. package/dist/src/server/node-server-runner.js +33 -8
  235. package/dist/src/services.js +3 -1
  236. package/dist/src/utils/resolve-from-project.d.ts +29 -0
  237. package/dist/src/utils/resolve-from-project.js +45 -0
  238. package/dist/tsconfig.tsbuildinfo +1 -1
  239. package/package.json +20 -11
  240. package/snippets.json +1 -1
  241. package/surface.json +2 -2
  242. package/console-app/assets/architecture-TIHT7OUA-CIT-Nug2.js +0 -1
  243. package/console-app/assets/channel-DQC6cL1c.js +0 -1
  244. package/console-app/assets/chunk-XXDRQBXY-Dgz5VbWO.js +0 -1
  245. package/console-app/assets/classDiagram-JCYQIIEL-1ASFocNV.js +0 -1
  246. package/console-app/assets/classDiagram-v2-OCEON4UE-1ASFocNV.js +0 -1
  247. package/console-app/assets/eventmodeling-45OFAUF4-BYJ0A5k-.js +0 -1
  248. package/console-app/assets/flowDiagram-UKHOOZJN-CVn87uiP.js +0 -1
  249. package/console-app/assets/railroad-abnf-AHOZXSZD-TU7k5gB_.js +0 -1
  250. package/console-app/assets/railroad-ebnf-EBAXGLYW-DbArPBkM.js +0 -1
  251. package/console-app/assets/railroad-peg-LSFZ7HO6-TzB93z4K.js +0 -1
  252. package/console-app/assets/stateDiagram-v2-UEYNNEHI-D7V_HITc.js +0 -1
  253. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-C4XcIHKT.js +0 -8
@@ -1,51 +1,26 @@
1
1
  import { pikkuSessionlessFunc } from '#pikku/function';
2
- import { createHttpPersonas } from '@pikku/core/persona';
3
2
  import { personaEnvironmentRefusal } from '@pikku/core/persona';
4
3
  import { resolvePersonas } from '../../utils/resolve-personas.js';
5
- import { resolvePersonaCredentials } from '../../utils/persona-credentials.js';
6
- import { resolveEnvironment } from './environment.js';
7
- import { loadDeclaredRoles, openScopeServiceForRoles } from './roles-shared.js';
8
4
  /**
9
- * The user id behind a persona's address.
5
+ * Report which personas an environment will provision, and which it will not.
10
6
  *
11
- * Read straight off better-auth's own `user` table rather than through the
12
- * ScopeService, because the ScopeService has no notion of an address — its
13
- * grants are keyed by user id, and `pikku_user_role.user_id` already FKs into
14
- * this exact table. Looking it up anywhere else would mean inventing a second
15
- * account concept for personas, which is the one thing this command exists to
16
- * avoid: a persona is a user, provisioned the way users are.
17
- */
18
- const userIdByEmail = async (db, email) => {
19
- const row = await db
20
- .selectFrom('user')
21
- .select('id')
22
- .where('email', '=', email)
23
- .executeTakeFirst();
24
- return row ? String(row.id) : null;
25
- };
26
- /**
27
- * Provision the declared personas into an environment: their accounts, and the
28
- * roles they declare.
29
- *
30
- * This is deployment, not seeding. `pikku db reset` applies a project's test
31
- * fixtures and does not run in production; an accountable persona doing a real
32
- * job in production still needs an account and its grants, and this is where
33
- * they come from.
34
- *
35
- * Additive in the same way `syncSystemRoles` is: it creates and grants, and
36
- * never deletes or revokes. Removing a persona from the code leaves their
37
- * account and their grants standing until somebody decides otherwise — which is
38
- * the point, because the alternative is a rolling deploy quietly locking out the
39
- * teammate that the older replica is still running.
7
+ * It reports rather than writes, because the writing happens somewhere this
8
+ * command cannot reach. Provisioning a persona means creating a user row and
9
+ * granting it roles, and both of those live in the deployment's database — one
10
+ * the CLI has no connection to for any environment that is not the developer's
11
+ * own. This command used to open a Kysely connection from the local project
12
+ * config and write through it, which provisioned a local stage correctly and
13
+ * silently provisioned the wrong database for every other environment.
40
14
  *
41
- * It needs both halves of an environment: its API, to sign the persona in, and
42
- * its database, to write the grants. They must be the same deployment — the
43
- * command has no way to check that, so pointing it at a stage's API and a local
44
- * database provisions nobody usefully.
15
+ * So the deployment provisions itself: an app calls `provisionPersonas` from
16
+ * `@pikku/better-auth` in its server lifecycle, where the database connection
17
+ * and better-auth's own adapter are both already open, and a deploy carries its
18
+ * personas with it. What is left here is the half that needs no database — the
19
+ * declaration, and the environment rule applied to it — which is what tells you
20
+ * before a deploy whether the accounts you expect will appear.
45
21
  */
46
22
  export const personaSync = pikkuSessionlessFunc({
47
- remote: true,
48
- func: async ({ logger, config, getInspectorState, variables }, { environment, apiUrl, dryRun }) => {
23
+ func: async ({ logger, config, getInspectorState }, { environment }) => {
49
24
  const state = await getInspectorState(true, false, false, true);
50
25
  const personas = resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain);
51
26
  if (Object.keys(personas).length === 0) {
@@ -53,9 +28,11 @@ export const personaSync = pikkuSessionlessFunc({
53
28
  return;
54
29
  }
55
30
  const environments = config.environments ?? {};
56
- const env = resolveEnvironment({ environment, environments, apiUrl });
31
+ if (!environments[environment]) {
32
+ throw new Error(`Unknown environment '${environment}'. Configured environments: ${Object.keys(environments).join(', ') || 'none'}.`);
33
+ }
57
34
  // The same rule that decides who may *run* against an environment decides
58
- // who may be provisioned into it. Two rules would drift, and the one that
35
+ // who is provisioned into it. Two rules would drift, and the one that
59
36
  // drifted would leave an account standing in production for a persona the
60
37
  // engine then refuses to sign in.
61
38
  //
@@ -73,76 +50,21 @@ export const personaSync = pikkuSessionlessFunc({
73
50
  }
74
51
  if (eligible.length === 0) {
75
52
  logger.info(`persona sync: no persona may act in '${environment}' — ${skipped.length} declared persona(s) were all skipped.`);
76
- logger.info(' Run with --dry-run for the reasons, or give the persona this environment and disposition `accountable` if it is production.');
77
- return;
78
- }
79
- if (dryRun) {
80
- logger.info(`persona sync (dry run): ${eligible.length} persona(s) would be provisioned into '${environment}' at ${env.apiUrl}:`);
81
- for (const [id, persona] of eligible) {
82
- logger.info(` ${id.padEnd(20)} ${persona.email}${persona.roles.length
83
- ? ` -> ${persona.roles.join(' + ')}`
84
- : ' (no roles)'}`);
85
- }
86
53
  for (const refusal of skipped) {
87
- logger.info(` skipped: ${refusal}`);
54
+ logger.info(` ${refusal}`);
88
55
  }
89
- logger.info('Re-run without --dry-run to apply.');
90
- return;
91
- }
92
- const credentials = await resolvePersonaCredentials(variables, 'a persona account');
93
- const declaredRoles = await loadDeclaredRoles(config.rolesMetaJsonFile, logger);
94
- if (!declaredRoles) {
95
- throw new Error('role metadata not found');
96
- }
97
- const signedIn = createHttpPersonas({
98
- apiUrl: env.apiUrl,
99
- ...credentials,
100
- personas: Object.fromEntries(eligible),
101
- signInPath: env.signInPath,
102
- rpcPath: env.rpcPath,
103
- });
104
- // Roles are synced on the way in, so a grant can name a role this deploy
105
- // introduced. Both that sync and everything below are additive.
106
- const opened = await openScopeServiceForRoles({ config, logger }, declaredRoles, 'pikku persona sync');
107
- if (!opened) {
56
+ logger.info(' Give the persona this environment, and disposition `accountable` if it is production.');
108
57
  return;
109
58
  }
110
- try {
111
- let accounts = 0;
112
- let granted = 0;
113
- let held = 0;
114
- for (const [id, persona] of eligible) {
115
- // Signing in is what creates the account: the actor plugin upserts an
116
- // `actor: true` user row on first sign-in, and nothing else does.
117
- // `sessionRoles()` is the public call that forces it — its answer is a
118
- // by-product here, not the reason for the call.
119
- await signedIn[id].sessionRoles();
120
- accounts++;
121
- if (persona.roles.length === 0) {
122
- logger.info(` ${id.padEnd(20)} ${persona.email} (no roles)`);
123
- continue;
124
- }
125
- const userId = await userIdByEmail(opened.db, persona.email);
126
- if (!userId) {
127
- throw new Error(`persona sync: signed '${id}' in at ${env.apiUrl}, but no user row for ${persona.email} exists in the database this command opened. ` +
128
- `That database is not the one '${environment}' runs on, so its grants would land nowhere.`);
129
- }
130
- const already = new Set(await opened.service.listUserRoles(userId));
131
- const missing = persona.roles.filter((role) => !already.has(role));
132
- for (const role of missing) {
133
- await opened.service.addUserToRole(userId, role);
134
- }
135
- granted += missing.length;
136
- held += persona.roles.length - missing.length;
137
- logger.info(` ${id.padEnd(20)} ${persona.email} -> ${persona.roles.join(' + ')}${missing.length === 0 ? ' (already held)' : ''}`);
138
- }
139
- logger.info(`persona sync: ${accounts} account(s) in '${environment}', ${granted} role grant(s) applied, ${held} already held`);
140
- if (skipped.length > 0) {
141
- logger.info(` ${skipped.length} persona(s) skipped — they do not act in '${environment}'. Run with --dry-run for the reasons.`);
142
- }
59
+ logger.info(`persona sync: ${eligible.length} persona(s) will be provisioned when a deployment of '${environment}' starts:`);
60
+ for (const [id, persona] of eligible) {
61
+ logger.info(` ${id.padEnd(20)} ${persona.email}${persona.roles.length
62
+ ? ` -> ${persona.roles.join(' + ')}`
63
+ : ' (no roles)'}`);
143
64
  }
144
- finally {
145
- await opened.destroy();
65
+ for (const refusal of skipped) {
66
+ logger.info(` skipped: ${refusal}`);
146
67
  }
68
+ logger.info(" Provisioning runs in the deployment: call `provisionPersonas` from '@pikku/better-auth' in your server lifecycle, passing `personaConfigs` and `personaEnvironments` from the generated personas file.");
147
69
  },
148
70
  });
@@ -18,11 +18,13 @@ import { createDevAgentRunner } from './dev-agent-runner.js';
18
18
  import { resolveConsoleMount } from './serve-console.js';
19
19
  import { serverReadyLine } from '../../server/server-ready.js';
20
20
  import { createEphemeralContentSigningJWT } from '../../server/content-signing-jwt.js';
21
+ import { disableDevActorSignIn } from '../../server/actor-sign-in.js';
21
22
  import { applyModelAliasOverride } from '../../utils/model-alias-override.js';
22
23
  export const serve = pikkuSessionlessFunc({
23
24
  remote: true,
24
25
  func: async ({ logger, config, getInspectorState, variables, devServerRunner }, { port, console: serveConsole, model }) => {
25
26
  process.env.PIKKU_DEV_QUICK_LOGIN ??= 'true';
27
+ disableDevActorSignIn(logger);
26
28
  applyModelAliasOverride(logger, model, config.models);
27
29
  const resolvedPort = parseInt(port || '3000', 10);
28
30
  const hostname = 'localhost';
@@ -0,0 +1,16 @@
1
+ import type { ValidateFinding } from './persona-checks.js';
2
+ export type AuthPluginCheckConfig = {
3
+ srcDirectories?: unknown;
4
+ };
5
+ /**
6
+ * Whether better-auth is configured the way pikku supports.
7
+ *
8
+ * Pikku authorizes on scopes, so better-auth's `admin()` — which authorizes its
9
+ * endpoints against a `user.role` column — is unsupported: wiring it means
10
+ * running two authorization models and projecting one onto the other. The
11
+ * inspector refuses it outright at prebuild; this reports the same thing
12
+ * without one, and additionally catches the quieter half of the migration, an
13
+ * app that dropped `admin()` and never wired `ban()`, which keeps its ban
14
+ * columns and its ban UI while silently enforcing nothing.
15
+ */
16
+ export declare const runAuthPluginChecks: (root: string, config: AuthPluginCheckConfig | null) => Promise<ValidateFinding[]>;
@@ -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
  },
@@ -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;