@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.
- package/CHANGELOG.md +205 -0
- package/console-app/assets/{abnfDiagram-N423BO3Z-WNIAg_Oa.js → abnfDiagram-N423BO3Z-Df3BCQUl.js} +1 -1
- package/console-app/assets/architecture-TIHT7OUA-i2kvkWiG.js +1 -0
- package/console-app/assets/{architectureDiagram-T3A2C74G-DM698mgf.js → architectureDiagram-T3A2C74G-BlV2VA0B.js} +1 -1
- package/console-app/assets/{blockDiagram-VBNYF7ZC-DPfy9K5S.js → blockDiagram-VBNYF7ZC-BZ7GFw7a.js} +1 -1
- package/console-app/assets/{c4Diagram-5PPSVZJV-DhLbbBbX.js → c4Diagram-5PPSVZJV-uXLYnA16.js} +1 -1
- package/console-app/assets/channel-BjjIBjrV.js +1 -0
- package/console-app/assets/{chunk-2GRJ4B5K-C1RQ1gvs.js → chunk-2GRJ4B5K-CU6Szqvt.js} +1 -1
- package/console-app/assets/{chunk-4I5QYGJK-BkzSdNc_.js → chunk-4I5QYGJK-Bf8ARw9p.js} +1 -1
- package/console-app/assets/{chunk-5RXB4S5H-DZpBpdcU.js → chunk-5RXB4S5H-CuRjyaQR.js} +1 -1
- package/console-app/assets/{chunk-6Q2QTUOP-mZObxdld.js → chunk-6Q2QTUOP-DMFott3W.js} +1 -1
- package/console-app/assets/{chunk-7Z6QIM7H-C0HbxZEa.js → chunk-7Z6QIM7H-YJMK1J3W.js} +1 -1
- package/console-app/assets/{chunk-GF5L2VYU-DXPVwWqW.js → chunk-GF5L2VYU-Co4slpk_.js} +1 -1
- package/console-app/assets/{chunk-I66GZJ75-pVpq9ckD.js → chunk-I66GZJ75-CE6Jk-ML.js} +1 -1
- package/console-app/assets/{chunk-JQJVKLGR-DJQ8K4wK.js → chunk-JQJVKLGR-BjkITZJq.js} +1 -1
- package/console-app/assets/{chunk-KBJHAD2P-B8XuxRCO.js → chunk-KBJHAD2P-DdHZUbE2.js} +1 -1
- package/console-app/assets/{chunk-NSK5VX7P-cYzQU4bD.js → chunk-NSK5VX7P-BB0gp55h.js} +1 -1
- package/console-app/assets/{chunk-QR6OTTB3-CNoTFsqn.js → chunk-QR6OTTB3-BhAzJcX6.js} +1 -1
- package/console-app/assets/{chunk-RYQCIY6F-DoKRUO_N.js → chunk-RYQCIY6F-BaRFYCcu.js} +1 -1
- package/console-app/assets/{chunk-UBXNYLIW-BqcIoUpU.js → chunk-UBXNYLIW-DN_ayb-W.js} +1 -1
- package/console-app/assets/{chunk-W5SLKNZC-p0Qalbjj.js → chunk-W5SLKNZC-DQ0C1K0m.js} +1 -1
- package/console-app/assets/{chunk-WRU74C26-DUMn96Dr.js → chunk-WRU74C26-Dej4aDDK.js} +1 -1
- package/console-app/assets/chunk-XXDRQBXY-COHoQA_g.js +1 -0
- package/console-app/assets/classDiagram-JCYQIIEL-BkQzFYJq.js +1 -0
- package/console-app/assets/classDiagram-v2-OCEON4UE-BkQzFYJq.js +1 -0
- package/console-app/assets/{cose-bilkent-JH36ORCC-BCh3BlPI.js → cose-bilkent-JH36ORCC-CaCBDIGn.js} +1 -1
- package/console-app/assets/{cynefin-VYW2F7L2-ZfghUZsD.js → cynefin-VYW2F7L2-DvNj3Ktd.js} +1 -1
- package/console-app/assets/{cynefinDiagram-MW4NZA55-DcV3QXCe.js → cynefinDiagram-MW4NZA55-2M1I4KVc.js} +1 -1
- package/console-app/assets/{dagre-BPFWXLBN.js → dagre-BX6AcUT6.js} +1 -1
- package/console-app/assets/{dagre-VZM6K2ZE-DETshBO-.js → dagre-VZM6K2ZE-CeiMExQ0.js} +1 -1
- package/console-app/assets/{diagram-7IWD3JNH-CsLX3P2o.js → diagram-7IWD3JNH-DFb1QCit.js} +1 -1
- package/console-app/assets/{diagram-B4RE2ZJO-o2zcxm5S.js → diagram-B4RE2ZJO-CcmxcWm0.js} +1 -1
- package/console-app/assets/{diagram-LBJQPF4R-BD2zKU21.js → diagram-LBJQPF4R-DpxWXRoq.js} +1 -1
- package/console-app/assets/{diagram-Q27KOJAE-DHaacyna.js → diagram-Q27KOJAE-Cu_MNubg.js} +1 -1
- package/console-app/assets/{diagram-UB23O5K3-pSLCMUyV.js → diagram-UB23O5K3-B-R4JZVn.js} +1 -1
- package/console-app/assets/{ebnfDiagram-BXEA7PRR-BDzawX3O.js → ebnfDiagram-BXEA7PRR-C26G-5vz.js} +1 -1
- package/console-app/assets/{erDiagram-JOGREHBK-CyYnPmbN.js → erDiagram-JOGREHBK-DJaz9wds.js} +1 -1
- package/console-app/assets/eventmodeling-45OFAUF4-Cge15uFd.js +1 -0
- package/console-app/assets/flowDiagram-UKHOOZJN-DN8OJeAP.js +1 -0
- package/console-app/assets/{ganttDiagram-PKOTCBZU-3fhCBbPA.js → ganttDiagram-PKOTCBZU-BmHvLxmw.js} +1 -1
- package/console-app/assets/{gitGraph-TEB2WS4Q-CdLRkVux.js → gitGraph-TEB2WS4Q-gjOfZX1j.js} +1 -1
- package/console-app/assets/{gitGraphDiagram-DS77QQ5N-CC1epdKw.js → gitGraphDiagram-DS77QQ5N-Cn2aQfgw.js} +1 -1
- package/console-app/assets/{index-BRmokG9t.js → index-Cx39I_BR.js} +184 -184
- package/console-app/assets/{index-GruDnAhJ.css → index-Mq-YbqNO.css} +1 -1
- package/console-app/assets/{info-DKCQHKI2-BJs8WkzC.js → info-DKCQHKI2-Dxvvr3Du.js} +1 -1
- package/console-app/assets/{infoDiagram-6WML65LV-uoOwoIeI.js → infoDiagram-6WML65LV-CU2DYprj.js} +1 -1
- package/console-app/assets/{ishikawaDiagram-WSZJBQD7-vCUJhwTi.js → ishikawaDiagram-WSZJBQD7-CEIhGAXe.js} +1 -1
- package/console-app/assets/{journeyDiagram-NVQOT4AX-D2QcIlLG.js → journeyDiagram-NVQOT4AX-vDLEtm6u.js} +1 -1
- package/console-app/assets/{kanban-definition-27J2QSJJ-JwwKowmR.js → kanban-definition-27J2QSJJ-rfZdVBY0.js} +1 -1
- package/console-app/assets/{line-h2oRreYF.js → line-CK7XHoXo.js} +1 -1
- package/console-app/assets/{linear-Dz002X_a.js → linear-DeA9Np_F.js} +1 -1
- package/console-app/assets/{mermaid-parser.core-DTgmT0AA.js → mermaid-parser.core-Bkwm3osM.js} +3 -3
- package/console-app/assets/{mermaid.core-1jSEDr-j.js → mermaid.core-DvVwOMEV.js} +4 -4
- package/console-app/assets/{mindmap-definition-FAOFIHXS-C3Txxhj4.js → mindmap-definition-FAOFIHXS-DYMAQz7X.js} +1 -1
- package/console-app/assets/{packet-7NZHBO7P-BGG7zIK2.js → packet-7NZHBO7P-CtoDJZ2L.js} +1 -1
- package/console-app/assets/{pegDiagram-VL7TDLO6--g1r_rov.js → pegDiagram-VL7TDLO6-BRxy7Znf.js} +1 -1
- package/console-app/assets/{pie-RZYD4A2V-C5OepMH7.js → pie-RZYD4A2V-NEqmtqwU.js} +1 -1
- package/console-app/assets/{pieDiagram-7S7Q4E2Y-DGD_LzGm.js → pieDiagram-7S7Q4E2Y-eekm9APm.js} +1 -1
- package/console-app/assets/{quadrantDiagram-CIZ2JOQS-CyBQQlD2.js → quadrantDiagram-CIZ2JOQS-BSew_APH.js} +1 -1
- package/console-app/assets/{radar-I7S5WNFK-CaBOAO95.js → radar-I7S5WNFK-BgvnQqhv.js} +1 -1
- package/console-app/assets/{railroad-3IZDKUUU-2aTWN4Pw.js → railroad-3IZDKUUU-DqG1o6ll.js} +1 -1
- package/console-app/assets/railroad-abnf-AHOZXSZD-DQHwBMhW.js +1 -0
- package/console-app/assets/railroad-ebnf-EBAXGLYW-cu_kDg2r.js +1 -0
- package/console-app/assets/railroad-peg-LSFZ7HO6-CJh8d3dM.js +1 -0
- package/console-app/assets/{railroadDiagram-AXF67PYL-3Nr0rlTp.js → railroadDiagram-AXF67PYL-Rb5V_7ZT.js} +1 -1
- package/console-app/assets/{requirementDiagram-LRYGKXZP-BN7sEwSa.js → requirementDiagram-LRYGKXZP-BONrWtQh.js} +1 -1
- package/console-app/assets/{sankeyDiagram-W5VNT64P-CXZR46wO.js → sankeyDiagram-W5VNT64P-CUPWhFm-.js} +1 -1
- package/console-app/assets/{sequenceDiagram-SI44F4Z6-DNtmdJ4U.js → sequenceDiagram-SI44F4Z6-DXwluQXT.js} +1 -1
- package/console-app/assets/{src-x1E3pJJ-.js → src-CyNQB37R.js} +1 -1
- package/console-app/assets/{stateDiagram-OKZ733FA-B_GyYdl_.js → stateDiagram-OKZ733FA-DXopCa6U.js} +1 -1
- package/console-app/assets/stateDiagram-v2-UEYNNEHI-BCi_SQzZ.js +1 -0
- package/console-app/assets/{swimlanes-SLNWSIFB-D4N7VdF7.js → swimlanes-SLNWSIFB-C_oImvSp.js} +1 -1
- package/console-app/assets/swimlanesDiagram-ULZ7WXOC-C01TBFdM.js +8 -0
- package/console-app/assets/{timeline-definition-Z64GVDOM-DU5rsOWQ.js → timeline-definition-Z64GVDOM-BEx4FmJK.js} +1 -1
- package/console-app/assets/{treeView-QDETBFTQ-DaKM_9sB.js → treeView-QDETBFTQ-DIQFLe_6.js} +1 -1
- package/console-app/assets/{treemap-6X3UGDF4-BabsKtZY.js → treemap-6X3UGDF4-Dz8_vGjV.js} +1 -1
- package/console-app/assets/{vennDiagram-T6HMQDX7-Dniftpq0.js → vennDiagram-T6HMQDX7-_sMSuxYe.js} +1 -1
- package/console-app/assets/{wardley-OPB4EBWU-BWffQNys.js → wardley-OPB4EBWU-BkFD9f0r.js} +1 -1
- package/console-app/assets/{wardleyDiagram-T6FBY63Y-CQ74fVdn.js → wardleyDiagram-T6FBY63Y-DhqBDV2O.js} +1 -1
- package/console-app/assets/{xychartDiagram-ELKLHX3M-D-XxbGIL.js → xychartDiagram-ELKLHX3M-D8Jav5f0.js} +1 -1
- package/console-app/index.html +2 -2
- package/dist/.pikku/addon/index.d.ts +1 -1
- package/dist/.pikku/addon/index.js +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
- package/dist/.pikku/agent/index.d.ts +1 -1
- package/dist/.pikku/agent/index.js +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
- package/dist/.pikku/auth/index.d.ts +1 -1
- package/dist/.pikku/auth/index.js +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
- package/dist/.pikku/channel/index.d.ts +1 -1
- package/dist/.pikku/channel/index.js +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/index.d.ts +1 -1
- package/dist/.pikku/cli/index.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +2 -9
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/error/index.d.ts +1 -1
- package/dist/.pikku/error/index.js +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
- package/dist/.pikku/function/index.d.ts +1 -1
- package/dist/.pikku/function/index.js +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +8 -6
- package/dist/.pikku/function/pikku-functions.gen.js +1 -3
- package/dist/.pikku/gateway/index.d.ts +1 -1
- package/dist/.pikku/gateway/index.js +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
- package/dist/.pikku/http/index.d.ts +1 -1
- package/dist/.pikku/http/index.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/index.d.ts +1 -1
- package/dist/.pikku/mcp/index.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/middleware/index.d.ts +1 -1
- package/dist/.pikku/middleware/index.js +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/queue/index.d.ts +1 -1
- package/dist/.pikku/queue/index.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/scenarios/index.d.ts +1 -1
- package/dist/.pikku/scenarios/index.js +1 -1
- package/dist/.pikku/scenarios/pikku-personas.gen.d.ts +11 -0
- package/dist/.pikku/scenarios/pikku-personas.gen.js +12 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
- package/dist/.pikku/scheduler/index.d.ts +1 -1
- package/dist/.pikku/scheduler/index.js +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +1 -1
- package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -1
- package/dist/.pikku/scopes/index.d.ts +1 -1
- package/dist/.pikku/scopes/index.js +1 -1
- package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/index.d.ts +1 -1
- package/dist/.pikku/secrets/index.js +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/setup/index.d.ts +1 -1
- package/dist/.pikku/setup/index.js +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
- package/dist/.pikku/trigger/index.d.ts +1 -1
- package/dist/.pikku/trigger/index.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/index.d.ts +1 -1
- package/dist/.pikku/variables/index.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/index.d.ts +1 -1
- package/dist/.pikku/workflow/index.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +1 -9
- package/dist/src/fabric/lib/git.js +31 -1
- package/dist/src/functions/commands/dev.js +2 -0
- package/dist/src/functions/commands/persona-sync.d.ts +14 -22
- package/dist/src/functions/commands/persona-sync.js +29 -107
- package/dist/src/functions/commands/serve.js +2 -0
- package/dist/src/functions/validate/auth-plugin-checks.d.ts +16 -0
- package/dist/src/functions/validate/auth-plugin-checks.js +165 -0
- package/dist/src/functions/validate/persona-checks.js +1 -1
- package/dist/src/functions/validate/shared-checks.js +12 -0
- package/dist/src/functions/validate/websocket-deps-checks.d.ts +30 -0
- package/dist/src/functions/validate/websocket-deps-checks.js +42 -0
- package/dist/src/functions/wirings/personas/serialize-personas-secrets.d.ts +1 -1
- package/dist/src/functions/wirings/personas/serialize-personas-secrets.js +1 -1
- package/dist/src/functions/wirings/scopes/pikku-command-scopes.d.ts +10 -0
- package/dist/src/functions/wirings/scopes/pikku-command-scopes.js +20 -3
- package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +72 -26
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +1 -1
- package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +2 -1
- package/dist/src/functions/wirings/workflow/serialize-personas.js +20 -1
- package/dist/src/server/actor-sign-in.d.ts +5 -0
- package/dist/src/server/actor-sign-in.js +27 -0
- package/dist/src/server/node-server-runner.d.ts +11 -0
- package/dist/src/server/node-server-runner.js +33 -8
- package/dist/src/services.js +3 -1
- package/dist/src/utils/resolve-from-project.d.ts +29 -0
- package/dist/src/utils/resolve-from-project.js +45 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -10
- package/snippets.json +1 -1
- package/surface.json +3 -3
- package/console-app/assets/architecture-TIHT7OUA-CIT-Nug2.js +0 -1
- package/console-app/assets/channel-DQC6cL1c.js +0 -1
- package/console-app/assets/chunk-XXDRQBXY-Dgz5VbWO.js +0 -1
- package/console-app/assets/classDiagram-JCYQIIEL-1ASFocNV.js +0 -1
- package/console-app/assets/classDiagram-v2-OCEON4UE-1ASFocNV.js +0 -1
- package/console-app/assets/eventmodeling-45OFAUF4-BYJ0A5k-.js +0 -1
- package/console-app/assets/flowDiagram-UKHOOZJN-CVn87uiP.js +0 -1
- package/console-app/assets/railroad-abnf-AHOZXSZD-TU7k5gB_.js +0 -1
- package/console-app/assets/railroad-ebnf-EBAXGLYW-DbArPBkM.js +0 -1
- package/console-app/assets/railroad-peg-LSFZ7HO6-TzB93z4K.js +0 -1
- package/console-app/assets/stateDiagram-v2-UEYNNEHI-D7V_HITc.js +0 -1
- 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 })]', '
|
|
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
|
|
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
|
|
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
|
|
40
|
+
definitions,
|
|
24
41
|
});
|
|
25
42
|
await writeFileInDir(logger, scopesFile, content);
|
|
26
43
|
if (scopesClientFile) {
|
|
27
|
-
await writeFileInDir(logger, scopesClientFile, serializeScopesClient({ definitions
|
|
44
|
+
await writeFileInDir(logger, scopesClientFile, serializeScopesClient({ definitions }));
|
|
28
45
|
}
|
|
29
46
|
if (scopesMetaJsonFile) {
|
|
30
|
-
const meta = validateAndBuildScopeDefinitionsMeta(
|
|
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
|
-
|
|
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
|
|
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
|
|
530
|
-
|
|
531
|
-
disposition
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
:
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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({
|
|
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
|
}
|