@pikku/cli 0.12.93 → 0.12.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console-app/assets/abnfDiagram-VRR7QNED-CytV-CG1.js +1 -0
- package/console-app/assets/arc-C5PvmnaA.js +1 -0
- package/console-app/assets/architecture-TIHT7OUA-C1_ScvPX.js +1 -0
- package/console-app/assets/architectureDiagram-ZJ3FMSHR-CJ2xb7C-.js +36 -0
- package/console-app/assets/array-BifhSqXX.js +1 -0
- package/console-app/assets/blockDiagram-677ZJIJ3-CCecrBrk.js +132 -0
- package/console-app/assets/c4Diagram-LMCZKHZV-CBnpukvQ.js +10 -0
- package/console-app/assets/channel-CwDOkXGI.js +1 -0
- package/console-app/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/console-app/assets/chunk-32BRIVSS-D8qWWZRQ.js +1 -0
- package/console-app/assets/chunk-52WLFC77-BEpXHMsp.js +10 -0
- package/console-app/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/console-app/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
- package/console-app/assets/chunk-C7G6YPKG-DsHUMkOp.js +1 -0
- package/console-app/assets/chunk-EX3LRPZG-mM7TjVx9.js +231 -0
- package/console-app/assets/chunk-FWX5IMBZ-CndRAn18.js +2 -0
- package/console-app/assets/chunk-HOUHSVGY-D1kT8umn.js +1 -0
- package/console-app/assets/chunk-ICXQ74PX-CeSaGyro.js +2 -0
- package/console-app/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/console-app/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
- package/console-app/assets/chunk-MOJQB5TN-CaZOCHYr.js +88 -0
- package/console-app/assets/chunk-OGEWGWER-CnB1CsPt.js +1 -0
- package/console-app/assets/chunk-PUDLZKDR-BbGulHJm.js +156 -0
- package/console-app/assets/chunk-Q4XR5HBZ-BywwzeeR.js +70 -0
- package/console-app/assets/chunk-RYQCIY6F-CV0R1Adv.js +1 -0
- package/console-app/assets/chunk-V7JOEXUC-DGFaAWzB.js +206 -0
- package/console-app/assets/chunk-VAUOI2AC-DxPUd8Yb.js +1 -0
- package/console-app/assets/chunk-VR4S4FIN-D4-UaW_J.js +1 -0
- package/console-app/assets/chunk-WYO6CB5R-D_TFN0d9.js +127 -0
- package/console-app/assets/chunk-XXDRQBXY-BzVuweA_.js +1 -0
- package/console-app/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/console-app/assets/chunk-ZGVPDNZ5-C6hdkGQb.js +62 -0
- package/console-app/assets/chunk-ZIRB5QZD-H-kepumB.js +32 -0
- package/console-app/assets/classDiagram-OUVF2IWQ-CTYJkTT0.js +1 -0
- package/console-app/assets/classDiagram-v2-EOCWNBFH-CTYJkTT0.js +1 -0
- package/console-app/assets/cose-bilkent-JH36ORCC-B6ZPxN6v.js +1 -0
- package/console-app/assets/cynefin-VYW2F7L2-D_nmxNz_.js +1 -0
- package/console-app/assets/cynefinDiagram-TSTJHNR4-CHy5QN0S.js +62 -0
- package/console-app/assets/cytoscape.esm-h6BdjjI9.js +321 -0
- package/console-app/assets/dagre-CXRCoUWR.js +1 -0
- package/console-app/assets/dagre-VKFMJZFB-DYfukM34.js +4 -0
- package/console-app/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/console-app/assets/diagram-FQU43EPY-Dgb9TPSB.js +3 -0
- package/console-app/assets/diagram-G47NLZAW-CnFNZs9Z.js +24 -0
- package/console-app/assets/diagram-NH7WQ7WH-DN7h2DU-.js +24 -0
- package/console-app/assets/diagram-OA4YK3LP-RLaQaNtK.js +30 -0
- package/console-app/assets/diagram-WEI45ONY-DxHWhVGd.js +41 -0
- package/console-app/assets/dist-BaJNIo4I.js +1 -0
- package/console-app/assets/ebnfDiagram-CCIWWBDH-UN3Z81-l.js +1 -0
- package/console-app/assets/erDiagram-Q63AITRT-C9lGAY3i.js +85 -0
- package/console-app/assets/eventmodeling-45OFAUF4-Dc5wNSSK.js +1 -0
- package/console-app/assets/flowDiagram-23GEKE2U-zPyGHMi4.js +1 -0
- package/console-app/assets/ganttDiagram-NO4QXBWP-CvBjBcdK.js +292 -0
- package/console-app/assets/gitGraph-TEB2WS4Q-Cs5Z9jMo.js +1 -0
- package/console-app/assets/gitGraphDiagram-IHSO6WYX-B0i_IJ5r.js +106 -0
- package/console-app/assets/graphlib-B8gBHxth.js +1 -0
- package/console-app/assets/{index-CmAplHjZ.js → index-71U8EEj7.js} +155 -153
- package/console-app/assets/{index-BYj7ZbUU.css → index-BXMjqLQ5.css} +1 -1
- package/console-app/assets/info-DKCQHKI2-DHL0afNl.js +1 -0
- package/console-app/assets/infoDiagram-FWYZ7A6U-CBWykodX.js +2 -0
- package/console-app/assets/init-D6jRqBbL.js +1 -0
- package/console-app/assets/ishikawaDiagram-FXEZZL3T-DM377eW_.js +70 -0
- package/console-app/assets/journeyDiagram-5HDEW3XC-BpX_-Cns.js +139 -0
- package/console-app/assets/kanban-definition-HUTT4EX6-D3xFsQkO.js +89 -0
- package/console-app/assets/katex-Vhh-h91d.js +257 -0
- package/console-app/assets/line-CTSKHTE0.js +1 -0
- package/console-app/assets/linear-R5GlBvXT.js +1 -0
- package/console-app/assets/map-DsCK-0Cs.js +1 -0
- package/console-app/assets/mermaid-parser.core-CkHk4BPi.js +7 -0
- package/console-app/assets/mermaid.core-CcjGXuV6.js +11 -0
- package/console-app/assets/mindmap-definition-LN4V7U3C-CtolazqQ.js +96 -0
- package/console-app/assets/ordinal-hYBb2elL.js +1 -0
- package/console-app/assets/packet-7NZHBO7P-BxSOj_Ny.js +1 -0
- package/console-app/assets/path-BWPyau1x.js +1 -0
- package/console-app/assets/pegDiagram-2B236MQR-BNsMtxsM.js +1 -0
- package/console-app/assets/pie-RZYD4A2V-BbkKQBA5.js +1 -0
- package/console-app/assets/pieDiagram-ENE6RG2P-BAZLIz2f.js +39 -0
- package/console-app/assets/quadrantDiagram-ABIIQ3AL-Dpvn-1Fs.js +7 -0
- package/console-app/assets/radar-I7S5WNFK-B-uKXIAH.js +1 -0
- package/console-app/assets/railroad-3IZDKUUU-BHXXK04t.js +1 -0
- package/console-app/assets/railroad-abnf-AHOZXSZD-tXSQaBvY.js +1 -0
- package/console-app/assets/railroad-ebnf-EBAXGLYW-DG3aohR0.js +1 -0
- package/console-app/assets/railroad-peg-LSFZ7HO6-DY_gBA-x.js +1 -0
- package/console-app/assets/railroadDiagram-RFXS5EU6-DfUApEQj.js +1 -0
- package/console-app/assets/requirementDiagram-TGXJPOKE-B2zX0jqo.js +84 -0
- package/console-app/assets/rough.esm-CSKSodPl.js +1 -0
- package/console-app/assets/sankeyDiagram-HTMAVEWB-CbQ2zc7z.js +40 -0
- package/console-app/assets/sequenceDiagram-DBY2YBRQ-BLT8PXI_.js +162 -0
- package/console-app/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
- package/console-app/assets/src-BLuPUFWW.js +1 -0
- package/console-app/assets/stateDiagram-2N3HPSRC-BvEyVp85.js +1 -0
- package/console-app/assets/stateDiagram-v2-6OUMAXLB-RGKS9iU4.js +1 -0
- package/console-app/assets/swimlanes-5IMT3BWC-1AZ459s6.js +2 -0
- package/console-app/assets/swimlanesDiagram-G3AALYLV-Ck_0-rfN.js +8 -0
- package/console-app/assets/timeline-definition-FHXFAJF6-dLuvH4s2.js +120 -0
- package/console-app/assets/treeView-QDETBFTQ-xL2msjo2.js +1 -0
- package/console-app/assets/treemap-6X3UGDF4-C98ichVP.js +1 -0
- package/console-app/assets/vennDiagram-L72KCM5P-HbUmidhv.js +34 -0
- package/console-app/assets/wardley-OPB4EBWU-C4gJS-m2.js +1 -0
- package/console-app/assets/wardleyDiagram-EHGQE667-B7b-LB02.js +78 -0
- package/console-app/assets/xychartDiagram-FW5EYKEG-Cm03zUGy.js +7 -0
- package/console-app/index.html +2 -2
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/auth/auth.types.d.ts +1 -1
- package/dist/.pikku/auth/auth.types.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/pikku-cli-channel.d.ts +13 -15
- package/dist/.pikku/cli/pikku-cli-channel.js +62 -8
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +14 -4
- package/dist/.pikku/cli/pikku-cli-client.gen.js +55 -18
- 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 +136 -10
- 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 +18 -2
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +64 -23
- package/dist/.pikku/function/pikku-function-types.gen.js +27 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +171 -37
- package/dist/.pikku/function/pikku-functions.gen.js +13 -3
- 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/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-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-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +2 -1
- package/dist/.pikku/pikku-services.gen.js +1 -0
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.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/rpc/pikku-rpc-wirings-meta.internal.gen.json +8 -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-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/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 +15 -5
- package/dist/.pikku/schemas/schemas/DbResetInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PersonaRunInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuPersonasInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuRolesInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuVirtualUserFunctionsOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/RolesPruneInput.schema.json +1 -0
- package/dist/.pikku/scopes/pikku-personas-meta.gen.json +1 -0
- package/dist/.pikku/scopes/pikku-personas.gen.d.ts +36 -0
- package/dist/.pikku/scopes/pikku-personas.gen.js +17 -0
- package/dist/.pikku/scopes/pikku-roles-meta.gen.json +1 -0
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +30 -0
- package/dist/.pikku/scopes/pikku-roles.gen.js +18 -0
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +4 -2
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +3 -2
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +2 -2
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +2 -2
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.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/pikku-variable-types.gen.d.ts +2 -2
- package/dist/.pikku/variables/pikku-variable-types.gen.js +2 -2
- 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/meta/allWorkflow.gen.json +46 -16
- package/dist/.pikku/workflow/pikku-personas.gen.d.ts +15 -0
- package/dist/.pikku/workflow/pikku-personas.gen.js +17 -0
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +101 -24
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +22 -2
- 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/bin/pikku.js +0 -0
- package/dist/src/cli.wiring.js +108 -8
- package/dist/src/deploy/analyzer/analyzer.js +14 -0
- package/dist/src/deploy/analyzer/manifest.d.ts +11 -0
- package/dist/src/fabric/functions/validate.function.js +77 -177
- package/dist/src/functions/commands/all.d.ts +1 -1
- package/dist/src/functions/commands/bootstrap.d.ts +1 -1
- package/dist/src/functions/commands/bootstrap.js +3 -0
- package/dist/src/functions/commands/db-audit.js +1 -1
- package/dist/src/functions/commands/db-baseline.js +1 -1
- package/dist/src/functions/commands/db-check.js +6 -5
- package/dist/src/functions/commands/db-codegen.js +1 -1
- package/dist/src/functions/commands/db-generate.js +1 -1
- package/dist/src/functions/commands/db-migrate.js +1 -1
- package/dist/src/functions/commands/db-reset.d.ts +15 -1
- package/dist/src/functions/commands/db-reset.js +18 -6
- package/dist/src/functions/commands/db-shared.d.ts +14 -0
- package/dist/src/functions/commands/deploy-info.d.ts +1 -1
- package/dist/src/functions/commands/dev-ai-runner.d.ts +16 -4
- package/dist/src/functions/commands/dev-ai-runner.js +63 -29
- package/dist/src/functions/commands/dev.js +10 -5
- package/dist/src/functions/commands/enable.d.ts +8 -8
- package/dist/src/functions/commands/enable.js +15 -19
- package/dist/src/functions/commands/{scenario-environment.d.ts → environment.d.ts} +9 -6
- package/dist/src/functions/commands/{scenario-environment.js → environment.js} +5 -4
- package/dist/src/functions/commands/new-addon.js +14 -14
- package/dist/src/functions/commands/persona-sync.d.ts +33 -0
- package/dist/src/functions/commands/persona-sync.js +151 -0
- package/dist/src/functions/commands/persona.d.ts +58 -0
- package/dist/src/functions/commands/persona.js +215 -0
- package/dist/src/functions/commands/roles-audit.d.ts +1 -0
- package/dist/src/functions/commands/roles-audit.js +37 -0
- package/dist/src/functions/commands/roles-prune.d.ts +16 -0
- package/dist/src/functions/commands/roles-prune.js +52 -0
- package/dist/src/functions/commands/roles-shared.d.ts +40 -0
- package/dist/src/functions/commands/roles-shared.js +77 -0
- package/dist/src/functions/commands/scenario-browser.d.ts +3 -3
- package/dist/src/functions/commands/scenario-browser.js +1 -1
- package/dist/src/functions/commands/scenario-ladder.d.ts +44 -2
- package/dist/src/functions/commands/scenario-ladder.js +52 -3
- package/dist/src/functions/commands/scenario.d.ts +7 -0
- package/dist/src/functions/commands/scenario.js +82 -18
- package/dist/src/functions/commands/scopes-shared.d.ts +12 -0
- package/dist/src/functions/commands/scopes-shared.js +2 -2
- package/dist/src/functions/commands/serve.js +8 -4
- package/dist/src/functions/commands/versions-check.js +3 -3
- package/dist/src/functions/commands/virtual-user-formatter.d.ts +30 -0
- package/dist/src/functions/commands/virtual-user-formatter.js +65 -0
- package/dist/src/functions/db/annotation-parser.d.ts +14 -0
- package/dist/src/functions/db/annotation-parser.js +50 -2
- package/dist/src/functions/db/better-auth-schema.js +2 -1
- package/dist/src/functions/db/db-codegen.d.ts +12 -0
- package/dist/src/functions/db/db-codegen.js +149 -16
- package/dist/src/functions/db/local-db.d.ts +87 -6
- package/dist/src/functions/db/local-db.js +239 -35
- package/dist/src/functions/db/sqlite/dev-seed.d.ts +12 -0
- package/dist/src/functions/db/sqlite/dev-seed.js +23 -0
- package/dist/src/functions/db/sqlite/sqlite-runtime-bun.js +1 -1
- package/dist/src/functions/validate/bootstrap-checks.d.ts +19 -0
- package/dist/src/functions/validate/bootstrap-checks.js +90 -0
- package/dist/src/functions/validate/persona-checks.d.ts +24 -0
- package/dist/src/functions/validate/persona-checks.js +136 -0
- package/dist/src/functions/validate/scenario-file-checks.d.ts +17 -0
- package/dist/src/functions/validate/scenario-file-checks.js +123 -0
- package/dist/src/functions/validate/shared-checks.d.ts +59 -0
- package/dist/src/functions/validate/shared-checks.js +311 -0
- package/dist/src/functions/validate/workspace-validate.d.ts +1 -2
- package/dist/src/functions/validate/workspace-validate.js +4 -196
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +2 -1
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +1 -0
- package/dist/src/functions/wirings/auth/serialize-auth-gen.d.ts +2 -2
- package/dist/src/functions/wirings/auth/serialize-auth-gen.js +24 -9
- package/dist/src/functions/wirings/auth/serialize-auth-types.d.ts +3 -1
- package/dist/src/functions/wirings/auth/serialize-auth-types.js +3 -1
- package/dist/src/functions/wirings/channels/pikku-channels.d.ts +1 -1
- package/dist/src/functions/wirings/channels/serialize-typed-channel-map.js +10 -2
- package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +9 -4
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.d.ts +7 -3
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +64 -28
- package/dist/src/functions/wirings/cli/serialize-channel-cli.js +63 -6
- package/dist/src/functions/wirings/cli/serialize-cli-entrypoint-guard.d.ts +12 -0
- package/dist/src/functions/wirings/cli/serialize-cli-entrypoint-guard.js +28 -0
- package/dist/src/functions/wirings/cli/serialize-local-cli-bootstrap.js +4 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.js +5 -8
- package/dist/src/functions/wirings/functions/serialize-addon-types.js +1 -1
- package/dist/src/functions/wirings/functions/serialize-function-types.js +106 -32
- package/dist/src/functions/wirings/middleware/serialize-middleware-imports.js +25 -9
- package/dist/src/functions/wirings/package/pikku-command-package-types.d.ts +3 -3
- package/dist/src/functions/wirings/package/serialize-package-types.js +5 -3
- package/dist/src/functions/wirings/personas/personas-secrets-file.d.ts +8 -0
- package/dist/src/functions/wirings/personas/personas-secrets-file.js +9 -0
- package/dist/src/functions/wirings/personas/pikku-command-personas.d.ts +7 -0
- package/dist/src/functions/wirings/personas/pikku-command-personas.js +57 -0
- package/dist/src/functions/wirings/personas/serialize-personas-secrets.d.ts +16 -0
- package/dist/src/functions/wirings/personas/serialize-personas-secrets.js +34 -0
- package/dist/src/functions/wirings/personas/serialize-personas-types.d.ts +18 -0
- package/dist/src/functions/wirings/personas/serialize-personas-types.js +76 -0
- package/dist/src/functions/wirings/queue/serialize-queue-map.js +2 -1
- package/dist/src/functions/wirings/queue/serialize-queue-wrapper.js +2 -1
- package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +2 -1
- package/dist/src/functions/wirings/roles/pikku-command-roles.d.ts +7 -0
- package/dist/src/functions/wirings/roles/pikku-command-roles.js +33 -0
- package/dist/src/functions/wirings/roles/serialize-roles-types.d.ts +14 -0
- package/dist/src/functions/wirings/roles/serialize-roles-types.js +55 -0
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +2 -1
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +3 -3
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +3 -3
- package/dist/src/functions/wirings/scopes/serialize-scopes-types.d.ts +1 -1
- package/dist/src/functions/wirings/scopes/serialize-scopes-types.js +1 -1
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +2 -1
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +2 -2
- package/dist/src/functions/wirings/virtual-user/pikku-command-virtual-user-functions.d.ts +1 -0
- package/dist/src/functions/wirings/virtual-user/pikku-command-virtual-user-functions.js +42 -0
- package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.d.ts +28 -0
- package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +439 -0
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +2 -1
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +13 -13
- package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +10 -0
- package/dist/src/functions/wirings/workflow/serialize-personas.js +46 -0
- package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +13 -4
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +170 -40
- package/dist/src/functions/workflows/all.workflow.js +55 -16
- package/dist/src/server/bun-server-runner.d.ts +2 -2
- package/dist/src/server/bun-server-runner.js +5 -1
- package/dist/src/server/content-signing-jwt.d.ts +13 -0
- package/dist/src/server/content-signing-jwt.js +51 -0
- package/dist/src/server/dev-server-runner.interface.d.ts +10 -2
- package/dist/src/server/node-server-runner.d.ts +2 -2
- package/dist/src/server/node-server-runner.js +2 -1
- package/dist/src/services/cli-logger.service.d.ts +28 -16
- package/dist/src/services/cli-logger.service.js +25 -16
- package/dist/src/services.d.ts +3 -1
- package/dist/src/services.js +10 -1
- package/dist/src/utils/file-import-path.js +9 -0
- package/dist/src/utils/pikku-cli-config.js +52 -2
- package/dist/src/utils/resolve-personas.d.ts +27 -0
- package/dist/src/utils/resolve-personas.js +35 -0
- package/dist/src/utils/resolve-scaffold-feature.d.ts +19 -0
- package/dist/src/utils/resolve-scaffold-feature.js +49 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -17
- package/dist/.pikku/schemas/schemas/BootstrapInput.schema.json +0 -1
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +0 -1
- package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +0 -1
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +0 -19
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +0 -17
- package/dist/src/functions/commands/db-seed.d.ts +0 -1
- package/dist/src/functions/commands/db-seed.js +0 -24
- package/dist/src/functions/db/sqlite/seed.d.ts +0 -11
- package/dist/src/functions/db/sqlite/seed.js +0 -22
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.d.ts +0 -2
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.js +0 -40
- package/dist/src/utils/resolve-scenario-actors.d.ts +0 -23
- package/dist/src/utils/resolve-scenario-actors.js +0 -74
- /package/dist/.pikku/schemas/schemas/{DbSeedInput.schema.json → RolesAuditInput.schema.json} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run one declared persona against a real stage as a virtual user.
|
|
3
|
+
*
|
|
4
|
+
* Everything it needs is already in the project: the catalogue is the function
|
|
5
|
+
* meta, the intents are the scenarios' own prose, the identity is the persona
|
|
6
|
+
* signing in over the app's real auth, and the scopes come from the roles they
|
|
7
|
+
* declared. The only new input is which person to be — everything else is a
|
|
8
|
+
* flag for narrowing or replaying a run.
|
|
9
|
+
*
|
|
10
|
+
* This is deliberately not a test runner. It asserts nothing, and a green run
|
|
11
|
+
* proves nothing — what it produces is findings, and the absence of them is
|
|
12
|
+
* only ever "not this time, not with this seed".
|
|
13
|
+
*/
|
|
14
|
+
export declare const personaRun: import("#pikku").PikkuFunctionConfig<{
|
|
15
|
+
environment: string;
|
|
16
|
+
persona: string;
|
|
17
|
+
disposition?: string;
|
|
18
|
+
goals?: string;
|
|
19
|
+
steps?: string;
|
|
20
|
+
mutations?: string;
|
|
21
|
+
duration?: string;
|
|
22
|
+
seed?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
apiUrl?: string;
|
|
25
|
+
allowApproval?: boolean;
|
|
26
|
+
skipRoleCheck?: boolean;
|
|
27
|
+
out?: string;
|
|
28
|
+
}, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
|
|
29
|
+
environment: string;
|
|
30
|
+
persona: string;
|
|
31
|
+
disposition?: string;
|
|
32
|
+
goals?: string;
|
|
33
|
+
steps?: string;
|
|
34
|
+
mutations?: string;
|
|
35
|
+
duration?: string;
|
|
36
|
+
seed?: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
apiUrl?: string;
|
|
39
|
+
allowApproval?: boolean;
|
|
40
|
+
skipRoleCheck?: boolean;
|
|
41
|
+
out?: string;
|
|
42
|
+
}, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<{
|
|
43
|
+
environment: string;
|
|
44
|
+
persona: string;
|
|
45
|
+
disposition?: string;
|
|
46
|
+
goals?: string;
|
|
47
|
+
steps?: string;
|
|
48
|
+
mutations?: string;
|
|
49
|
+
duration?: string;
|
|
50
|
+
seed?: string;
|
|
51
|
+
model?: string;
|
|
52
|
+
apiUrl?: string;
|
|
53
|
+
allowApproval?: boolean;
|
|
54
|
+
skipRoleCheck?: boolean;
|
|
55
|
+
out?: string;
|
|
56
|
+
}, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
|
|
57
|
+
/** Who this app is for, and what each of them wants, without running anything. */
|
|
58
|
+
export declare const personaList: import("#pikku").PikkuFunctionConfig<void, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<void, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<void, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { resolve, dirname } from 'node:path';
|
|
2
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
4
|
+
import { createHttpPersonas, personaEnvironmentRefusal, roleMismatchMessage, verifyPersonaRoles, } from '@pikku/core/persona';
|
|
5
|
+
import { personaVirtualUserTarget, catalogueClassification, DISPOSITIONS, prepareVirtualUserRun, runVirtualUser, } from '@pikku/core/virtual-user';
|
|
6
|
+
import { resolvePersonas } from '../../utils/resolve-personas.js';
|
|
7
|
+
import { resolveEnvironment } from './environment.js';
|
|
8
|
+
import { createDevAIAgentRunner } from './dev-ai-runner.js';
|
|
9
|
+
import { formatVirtualUserReport } from './virtual-user-formatter.js';
|
|
10
|
+
const dispositionNames = Object.keys(DISPOSITIONS);
|
|
11
|
+
/**
|
|
12
|
+
* What a declaration overrode, in the order it was written.
|
|
13
|
+
*
|
|
14
|
+
* Listed rather than summarised as "tuned": the whole reason to override a dial
|
|
15
|
+
* is that the stock profile was wrong for this product, and which one you moved
|
|
16
|
+
* is the interesting part.
|
|
17
|
+
*/
|
|
18
|
+
const describeTuning = (tuning) => Object.entries(tuning)
|
|
19
|
+
.map(([dial, value]) => dial === 'moves' && value && typeof value === 'object'
|
|
20
|
+
? Object.entries(value)
|
|
21
|
+
.map(([move, weight]) => `${move} ${weight}`)
|
|
22
|
+
.join(', ')
|
|
23
|
+
: `${dial} ${dial === 'instructions' ? '+' : value}`)
|
|
24
|
+
.join(', ');
|
|
25
|
+
/**
|
|
26
|
+
* Run one declared persona against a real stage as a virtual user.
|
|
27
|
+
*
|
|
28
|
+
* Everything it needs is already in the project: the catalogue is the function
|
|
29
|
+
* meta, the intents are the scenarios' own prose, the identity is the persona
|
|
30
|
+
* signing in over the app's real auth, and the scopes come from the roles they
|
|
31
|
+
* declared. The only new input is which person to be — everything else is a
|
|
32
|
+
* flag for narrowing or replaying a run.
|
|
33
|
+
*
|
|
34
|
+
* This is deliberately not a test runner. It asserts nothing, and a green run
|
|
35
|
+
* proves nothing — what it produces is findings, and the absence of them is
|
|
36
|
+
* only ever "not this time, not with this seed".
|
|
37
|
+
*/
|
|
38
|
+
export const personaRun = pikkuSessionlessFunc({
|
|
39
|
+
func: async ({ logger, config, getInspectorState, variables }, { environment, persona: personaId, disposition: dispositionFlag, goals, steps, mutations, duration, seed, model, apiUrl, allowApproval, skipRoleCheck, out, }) => {
|
|
40
|
+
const state = await getInspectorState(true, false, false, true);
|
|
41
|
+
const personas = resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain);
|
|
42
|
+
const known = Object.keys(personas);
|
|
43
|
+
const persona = personas[personaId];
|
|
44
|
+
if (!persona) {
|
|
45
|
+
throw new Error(`No persona '${personaId}'.${known.length
|
|
46
|
+
? ` Declared: ${known.join(', ')}`
|
|
47
|
+
: ' None are declared — add a definePersonas({ … }) call.'}`);
|
|
48
|
+
}
|
|
49
|
+
// A provider login needs a human at a consent screen, and someone declared
|
|
50
|
+
// `runnable: false` exists to be acted upon. Refused here rather than
|
|
51
|
+
// failing partway through a sign-in that was never going to work.
|
|
52
|
+
if (!persona.runnable) {
|
|
53
|
+
throw new Error(`Persona '${personaId}' is not runnable — ${persona.account?.provider
|
|
54
|
+
? `their login is a '${persona.account.provider}' account, and driving its consent screen needs a person`
|
|
55
|
+
: 'they are declared `runnable: false`, to be acted upon rather than to act'}.`);
|
|
56
|
+
}
|
|
57
|
+
const declared = dispositionFlag ?? persona.disposition ?? 'realistic';
|
|
58
|
+
if (!dispositionNames.includes(declared)) {
|
|
59
|
+
throw new Error(`Unknown disposition '${declared}'. One of: ${dispositionNames.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
// Narrowed by the guard above; `dispositionNames` is the runtime list and
|
|
62
|
+
// does not carry the union through `includes`.
|
|
63
|
+
const disposition = declared;
|
|
64
|
+
const environments = config.environments ?? {};
|
|
65
|
+
const env = resolveEnvironment({
|
|
66
|
+
environment,
|
|
67
|
+
environments,
|
|
68
|
+
apiUrl,
|
|
69
|
+
});
|
|
70
|
+
// Checked against the *effective* disposition, so `--disposition` cannot
|
|
71
|
+
// turn an accountable persona adversarial and point it at production. The
|
|
72
|
+
// build check already looked at the declaration; this one exists because
|
|
73
|
+
// the declaration is not what is running — the deployed artifact is.
|
|
74
|
+
const refusal = personaEnvironmentRefusal(personaId, { ...persona, disposition }, environment, environments);
|
|
75
|
+
if (refusal) {
|
|
76
|
+
throw new Error(refusal);
|
|
77
|
+
}
|
|
78
|
+
const secret = await variables.get('SCENARIO_ACTOR_SECRET');
|
|
79
|
+
if (!secret) {
|
|
80
|
+
throw new Error('SCENARIO_ACTOR_SECRET is not set — a virtual user cannot sign in. ' +
|
|
81
|
+
'Export it in the environment running this command (never put it in pikku.config.json).');
|
|
82
|
+
}
|
|
83
|
+
const resolvedModel = model ?? config.scenarios?.model;
|
|
84
|
+
if (!resolvedModel) {
|
|
85
|
+
throw new Error('No model to think with — pass --model, or set scenarios.model in pikku.config.json.');
|
|
86
|
+
}
|
|
87
|
+
const aiAgentRunner = await createDevAIAgentRunner({
|
|
88
|
+
logger,
|
|
89
|
+
projectRoot: config.rootDir,
|
|
90
|
+
variables,
|
|
91
|
+
});
|
|
92
|
+
if (!aiAgentRunner) {
|
|
93
|
+
throw new Error('No AI provider is configured. Set OPENAI_BASE_URL + OPENAI_API_KEY (or LITELLM_PROXY_URL + LITELLM_API_KEY).');
|
|
94
|
+
}
|
|
95
|
+
// Shared with the scaffolded `runVirtualUser` RPC, which does the same
|
|
96
|
+
// derivation from `metaService` at runtime — the two have to agree, or the
|
|
97
|
+
// same persona and seed explore a different API depending on how it was
|
|
98
|
+
// started.
|
|
99
|
+
const { catalogue, intents, scopes, agents } = prepareVirtualUserRun({
|
|
100
|
+
persona,
|
|
101
|
+
functionsMeta: state.functions?.meta ?? {},
|
|
102
|
+
schemas: (state.schemas ?? {}),
|
|
103
|
+
workflowsMeta: state.workflows?.meta ?? {},
|
|
104
|
+
systemRoles: state.systemRoles?.definitions ?? [],
|
|
105
|
+
agentsMeta: state.agents?.agentsMeta ?? {},
|
|
106
|
+
});
|
|
107
|
+
if (catalogue.length === 0) {
|
|
108
|
+
throw new Error('This project exposes no RPCs, so there is nothing for a virtual user to do.');
|
|
109
|
+
}
|
|
110
|
+
const signedIn = createHttpPersonas({
|
|
111
|
+
apiUrl: env.apiUrl,
|
|
112
|
+
secret,
|
|
113
|
+
personas,
|
|
114
|
+
signInPath: env.signInPath,
|
|
115
|
+
rpcPath: env.rpcPath,
|
|
116
|
+
model: resolvedModel,
|
|
117
|
+
});
|
|
118
|
+
// Before the first step, not after: findings from a persona whose roles
|
|
119
|
+
// drifted are about the seed, and reading them as product bugs is how a
|
|
120
|
+
// whole run gets thrown away. `--skip-role-check` exists for a stage whose
|
|
121
|
+
// auth reports roles somewhere this cannot read.
|
|
122
|
+
if (!skipRoleCheck) {
|
|
123
|
+
const actual = await signedIn[personaId].sessionRoles();
|
|
124
|
+
if (actual === null) {
|
|
125
|
+
logger.warn(`Could not read '${personaId}' roles back from ${env.apiUrl} — running unverified. ` +
|
|
126
|
+
`A stage that does not report roles cannot tell a permissions finding from seed drift.`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const message = roleMismatchMessage(verifyPersonaRoles(personaId, persona.roles, actual));
|
|
130
|
+
if (message) {
|
|
131
|
+
throw new Error(message);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const flagGoals = goals?.split(',').map((goal) => goal.trim());
|
|
136
|
+
const result = await runVirtualUser({
|
|
137
|
+
persona,
|
|
138
|
+
personaId,
|
|
139
|
+
disposition,
|
|
140
|
+
// Tuning belongs to the declared person, so `--disposition` drops it: you
|
|
141
|
+
// asked to run them differently, not to bend their dials to another shape.
|
|
142
|
+
...(dispositionFlag ? {} : { tuning: persona.tuning }),
|
|
143
|
+
catalogue,
|
|
144
|
+
intents,
|
|
145
|
+
goals: [...persona.goals, ...(flagGoals ?? [])],
|
|
146
|
+
scopes,
|
|
147
|
+
fixtures: persona.fixtures,
|
|
148
|
+
// Both halves, or `talkTo` is wired up and never advertised: the target
|
|
149
|
+
// decides whether a name is callable, the instructions are what tell the
|
|
150
|
+
// persona the assistant exists at all.
|
|
151
|
+
agents,
|
|
152
|
+
target: personaVirtualUserTarget(signedIn[personaId], {
|
|
153
|
+
model: resolvedModel,
|
|
154
|
+
agents: agents.map((agent) => agent.name),
|
|
155
|
+
}),
|
|
156
|
+
llm: (params) => aiAgentRunner.run(params),
|
|
157
|
+
model: resolvedModel,
|
|
158
|
+
seed: seed ? Number(seed) : undefined,
|
|
159
|
+
allowApprovalRequired: allowApproval ?? false,
|
|
160
|
+
budget: {
|
|
161
|
+
steps: steps ? Number(steps) : undefined,
|
|
162
|
+
mutations: mutations ? Number(mutations) : undefined,
|
|
163
|
+
duration,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
for (const { level, text } of formatVirtualUserReport(result, {
|
|
167
|
+
persona: `${persona.name} (${personaId})`,
|
|
168
|
+
disposition,
|
|
169
|
+
environment,
|
|
170
|
+
apiUrl: env.apiUrl,
|
|
171
|
+
catalogue: catalogueClassification(catalogue),
|
|
172
|
+
})) {
|
|
173
|
+
const write = logger[level].bind(logger);
|
|
174
|
+
write(text);
|
|
175
|
+
}
|
|
176
|
+
if (out) {
|
|
177
|
+
const path = resolve(config.rootDir, out);
|
|
178
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
179
|
+
writeFileSync(path, JSON.stringify(result, null, 2));
|
|
180
|
+
logger.info(`Wrote the full run to ${path}`);
|
|
181
|
+
}
|
|
182
|
+
// A finding is the whole reason this ran, so it has to be able to fail a
|
|
183
|
+
// pipeline. Nothing else here does: giving up on an intent is a user being
|
|
184
|
+
// a user, not a defect.
|
|
185
|
+
if (result.findings.length > 0) {
|
|
186
|
+
process.exitCode = 1;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
/** Who this app is for, and what each of them wants, without running anything. */
|
|
191
|
+
export const personaList = pikkuSessionlessFunc({
|
|
192
|
+
func: async ({ logger, config, getInspectorState }) => {
|
|
193
|
+
const state = await getInspectorState(true, false, false, true);
|
|
194
|
+
const personas = Object.values(resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain));
|
|
195
|
+
if (personas.length === 0) {
|
|
196
|
+
logger.info('No personas declared. Add a definePersonas({ susan: { name, roles, goals } }) call.');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
for (const persona of personas) {
|
|
200
|
+
logger.info(`${persona.id} — ${persona.name}${persona.jobTitle ? `, ${persona.jobTitle}` : ''}`);
|
|
201
|
+
logger.info(` ${persona.email}${persona.roles.length
|
|
202
|
+
? `, roles: ${persona.roles.join(', ')}`
|
|
203
|
+
: ', no roles'}${persona.runnable ? '' : ' (not runnable)'}`);
|
|
204
|
+
if (persona.disposition || persona.tuning) {
|
|
205
|
+
logger.info(` ${persona.disposition ?? 'realistic'}${persona.tuning ? ` (tuned: ${describeTuning(persona.tuning)})` : ''}`);
|
|
206
|
+
}
|
|
207
|
+
if (persona.description) {
|
|
208
|
+
logger.info(` ${persona.description}`);
|
|
209
|
+
}
|
|
210
|
+
for (const goal of persona.goals) {
|
|
211
|
+
logger.info(` · ${goal}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const rolesAudit: import("#pikku").PikkuFunctionConfig<{}, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{}, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<{}, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
2
|
+
import { loadDeclaredRoles, openScopeServiceForRoles, reportStaleRoles, } from './roles-shared.js';
|
|
3
|
+
export const rolesAudit = pikkuSessionlessFunc({
|
|
4
|
+
remote: true,
|
|
5
|
+
func: async ({ logger, config }) => {
|
|
6
|
+
const declared = await loadDeclaredRoles(config.rolesMetaJsonFile, logger);
|
|
7
|
+
if (!declared) {
|
|
8
|
+
throw new Error('role metadata not found');
|
|
9
|
+
}
|
|
10
|
+
const opened = await openScopeServiceForRoles({ config, logger }, declared, 'pikku roles audit');
|
|
11
|
+
if (!opened) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const stale = await opened.service.findStaleSystemRoles();
|
|
16
|
+
logger.info(`Role audit: ${declared.length} system role(s) declared in code`);
|
|
17
|
+
if (stale.length === 0) {
|
|
18
|
+
logger.info(' every system role in the database is still declared');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
logger.info('');
|
|
22
|
+
logger.info(`${stale.length} system role(s) no longer declared in code:`);
|
|
23
|
+
reportStaleRoles(stale, logger);
|
|
24
|
+
const held = stale.filter((s) => s.users > 0);
|
|
25
|
+
logger.info('');
|
|
26
|
+
if (held.length > 0) {
|
|
27
|
+
logger.warn(`${held.length} undeclared role(s) are still held by somebody. ` +
|
|
28
|
+
`They are inert — nothing offers them for new grants — but the ` +
|
|
29
|
+
`existing grants persist, and so do whatever scopes the role still carries.`);
|
|
30
|
+
}
|
|
31
|
+
logger.info('Run `pikku roles prune --yes` to remove them.');
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await opened.destroy();
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes system roles that are no longer declared in code, cascading them out
|
|
3
|
+
* of every user grant that holds them.
|
|
4
|
+
*
|
|
5
|
+
* This revokes access, so it never runs implicitly — not at boot, not as part
|
|
6
|
+
* of `pikku all`. Without `--yes` it only reports the blast radius. That is the
|
|
7
|
+
* whole point of the additive sync: deleting a `defineSystemRole` declaration
|
|
8
|
+
* leaves people holding the role until somebody decides otherwise here.
|
|
9
|
+
*/
|
|
10
|
+
export declare const rolesPrune: import("#pikku").PikkuFunctionConfig<{
|
|
11
|
+
yes?: boolean;
|
|
12
|
+
}, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
|
|
13
|
+
yes?: boolean;
|
|
14
|
+
}, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<{
|
|
15
|
+
yes?: boolean;
|
|
16
|
+
}, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
2
|
+
import { loadDeclaredRoles, openScopeServiceForRoles, reportStaleRoles, } from './roles-shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* Removes system roles that are no longer declared in code, cascading them out
|
|
5
|
+
* of every user grant that holds them.
|
|
6
|
+
*
|
|
7
|
+
* This revokes access, so it never runs implicitly — not at boot, not as part
|
|
8
|
+
* of `pikku all`. Without `--yes` it only reports the blast radius. That is the
|
|
9
|
+
* whole point of the additive sync: deleting a `defineSystemRole` declaration
|
|
10
|
+
* leaves people holding the role until somebody decides otherwise here.
|
|
11
|
+
*/
|
|
12
|
+
export const rolesPrune = pikkuSessionlessFunc({
|
|
13
|
+
remote: true,
|
|
14
|
+
func: async ({ logger, config }, { yes }) => {
|
|
15
|
+
const declared = await loadDeclaredRoles(config.rolesMetaJsonFile, logger);
|
|
16
|
+
if (!declared) {
|
|
17
|
+
throw new Error('role metadata not found');
|
|
18
|
+
}
|
|
19
|
+
const opened = await openScopeServiceForRoles({ config, logger }, declared, 'pikku roles prune');
|
|
20
|
+
if (!opened) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const stale = await opened.service.findStaleSystemRoles();
|
|
25
|
+
if (stale.length === 0) {
|
|
26
|
+
logger.info('roles prune: nothing to prune');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
logger.info(`${stale.length} system role(s) no longer declared in code:`);
|
|
30
|
+
reportStaleRoles(stale, logger);
|
|
31
|
+
const held = stale.filter((s) => s.users > 0);
|
|
32
|
+
const affectedUsers = stale.reduce((sum, s) => sum + s.users, 0);
|
|
33
|
+
if (!yes) {
|
|
34
|
+
logger.info('');
|
|
35
|
+
if (held.length > 0) {
|
|
36
|
+
logger.warn(`Pruning would revoke ${held.length} role(s) from ${affectedUsers} user grant(s).`);
|
|
37
|
+
}
|
|
38
|
+
logger.info('Re-run with --yes to remove them.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const pruned = await opened.service.pruneSystemRoles();
|
|
42
|
+
logger.info('');
|
|
43
|
+
logger.info(`roles prune: removed ${pruned.length} system role(s)`);
|
|
44
|
+
if (held.length > 0) {
|
|
45
|
+
logger.warn(`Revoked from ${affectedUsers} user grant(s). They lose the role's scopes on their next request.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await opened.destroy();
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SystemRole } from '@pikku/core/role';
|
|
2
|
+
import { openScopeService } from './scopes-shared.js';
|
|
3
|
+
import type { OpenedScopeService } from './scopes-shared.js';
|
|
4
|
+
type Logger = {
|
|
5
|
+
info: (msg: string) => void;
|
|
6
|
+
warn: (msg: string) => void;
|
|
7
|
+
error: (msg: string) => void;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Reads the generated role metadata and flattens it to the declared role set.
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` — never an empty array — when the file cannot be read, for the
|
|
13
|
+
* same reason `loadDeclaredScopes` does: `prune` removes everything not in this
|
|
14
|
+
* set, so "nothing is declared" and "we failed to find out" have to stay
|
|
15
|
+
* distinguishable or a missing file wipes every system role in the database.
|
|
16
|
+
*/
|
|
17
|
+
export declare const loadDeclaredRoles: (rolesMetaJsonFile: string | undefined, logger: Logger) => Promise<SystemRole[] | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Opens a ScopeService and brings both the scope and the role `declared` marks
|
|
20
|
+
* up to date with the code.
|
|
21
|
+
*
|
|
22
|
+
* Scopes are synced first and not as a courtesy: `pikku_role_scopes` has a
|
|
23
|
+
* foreign key onto `pikku_scopes`, so a role granting a freshly-declared scope
|
|
24
|
+
* cannot be written until that scope's row exists. Both syncs are additive, so
|
|
25
|
+
* doing them inside a read-only command is safe.
|
|
26
|
+
*/
|
|
27
|
+
export declare const openScopeServiceForRoles: (args: Parameters<typeof openScopeService>[0] & {
|
|
28
|
+
config: {
|
|
29
|
+
scopesMetaJsonFile: string;
|
|
30
|
+
};
|
|
31
|
+
}, roles: SystemRole[], command: string) => Promise<OpenedScopeService | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Renders undeclared system roles and how many people still hold each — the
|
|
34
|
+
* blast radius, shown before anything is deleted.
|
|
35
|
+
*/
|
|
36
|
+
export declare const reportStaleRoles: (stale: Array<{
|
|
37
|
+
role: string;
|
|
38
|
+
users: number;
|
|
39
|
+
}>, logger: Logger) => void;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { flattenSystemRoleDefinitions } from '@pikku/core/role';
|
|
3
|
+
import { loadDeclaredScopes, openScopeService } from './scopes-shared.js';
|
|
4
|
+
const isRoleDefinitionsMeta = (value) => typeof value === 'object' &&
|
|
5
|
+
value !== null &&
|
|
6
|
+
!Array.isArray(value) &&
|
|
7
|
+
Object.values(value).every((def) => typeof def === 'object' &&
|
|
8
|
+
def !== null &&
|
|
9
|
+
!Array.isArray(def) &&
|
|
10
|
+
typeof def.name === 'string' &&
|
|
11
|
+
Array.isArray(def.scopes));
|
|
12
|
+
/**
|
|
13
|
+
* Reads the generated role metadata and flattens it to the declared role set.
|
|
14
|
+
*
|
|
15
|
+
* Returns `null` — never an empty array — when the file cannot be read, for the
|
|
16
|
+
* same reason `loadDeclaredScopes` does: `prune` removes everything not in this
|
|
17
|
+
* set, so "nothing is declared" and "we failed to find out" have to stay
|
|
18
|
+
* distinguishable or a missing file wipes every system role in the database.
|
|
19
|
+
*/
|
|
20
|
+
export const loadDeclaredRoles = async (rolesMetaJsonFile, logger) => {
|
|
21
|
+
if (!rolesMetaJsonFile) {
|
|
22
|
+
logger.error('pikku roles: no role metadata path is configured.\n' +
|
|
23
|
+
' Run `pikku all` to generate it.');
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(await readFile(rolesMetaJsonFile, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
logger.error(`pikku roles: no role metadata at ${rolesMetaJsonFile}.\n` +
|
|
32
|
+
` Run \`pikku all\` to generate it.`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (!isRoleDefinitionsMeta(parsed)) {
|
|
36
|
+
logger.error(`pikku roles: the role metadata at ${rolesMetaJsonFile} is malformed.\n` +
|
|
37
|
+
` Run \`pikku all\` to regenerate it.`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return flattenSystemRoleDefinitions(Object.values(parsed));
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Opens a ScopeService and brings both the scope and the role `declared` marks
|
|
44
|
+
* up to date with the code.
|
|
45
|
+
*
|
|
46
|
+
* Scopes are synced first and not as a courtesy: `pikku_role_scopes` has a
|
|
47
|
+
* foreign key onto `pikku_scopes`, so a role granting a freshly-declared scope
|
|
48
|
+
* cannot be written until that scope's row exists. Both syncs are additive, so
|
|
49
|
+
* doing them inside a read-only command is safe.
|
|
50
|
+
*/
|
|
51
|
+
export const openScopeServiceForRoles = async (args, roles, command) => {
|
|
52
|
+
const declaredScopes = await loadDeclaredScopes(args.config.scopesMetaJsonFile, args.logger);
|
|
53
|
+
if (!declaredScopes) {
|
|
54
|
+
throw new Error('scope metadata not found');
|
|
55
|
+
}
|
|
56
|
+
const opened = await openScopeService(args, declaredScopes, command);
|
|
57
|
+
if (!opened) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
await opened.service.syncSystemRoles(roles);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
await opened.destroy();
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
return opened;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Renders undeclared system roles and how many people still hold each — the
|
|
71
|
+
* blast radius, shown before anything is deleted.
|
|
72
|
+
*/
|
|
73
|
+
export const reportStaleRoles = (stale, logger) => {
|
|
74
|
+
for (const { role, users } of stale) {
|
|
75
|
+
logger.info(` ${role.padEnd(40)} held by ${users} user(s)`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
* unconditionally.
|
|
9
9
|
*/
|
|
10
10
|
import type { ScenarioBrowserFailure, ScenarioBrowserProvider } from '@pikku/core/workflow';
|
|
11
|
-
import type {
|
|
11
|
+
import type { ResolvedPersona } from '@pikku/core/services';
|
|
12
12
|
/** The default driver, used when a project names no other. */
|
|
13
13
|
export declare const DEFAULT_BROWSER_DRIVER = "@pikku/playwright";
|
|
14
14
|
export interface ScenarioBrowserDriverOptions {
|
|
15
15
|
secret: string;
|
|
16
|
-
actors: Record<string,
|
|
16
|
+
actors: Record<string, ResolvedPersona>;
|
|
17
17
|
signInPath?: string;
|
|
18
18
|
failureDir?: string;
|
|
19
19
|
config: unknown;
|
|
@@ -49,7 +49,7 @@ export interface ResolveScenarioBrowserProviderOptions {
|
|
|
49
49
|
apiUrl: string;
|
|
50
50
|
appUrl?: string;
|
|
51
51
|
secret: string;
|
|
52
|
-
actors: Record<string,
|
|
52
|
+
actors: Record<string, ResolvedPersona>;
|
|
53
53
|
signInPath?: string;
|
|
54
54
|
/** Where failure screenshots are written. */
|
|
55
55
|
failureDir: string;
|
|
@@ -26,7 +26,7 @@ export const resolveScenarioBrowserProvider = async ({ environment, apiUrl, appU
|
|
|
26
26
|
});
|
|
27
27
|
if (!config.appUrl || config.appUrlSource === 'default') {
|
|
28
28
|
throw new Error(`Scenario environment '${environment}' has browser steps but no 'appUrl', and '${driver}' resolved none from the environment. ` +
|
|
29
|
-
`Add it to
|
|
29
|
+
`Add it to environments.${environment} in pikku.config.json, pass --app-url for a target that only exists at run time, or run with --no-browser to skip them.`);
|
|
30
30
|
}
|
|
31
31
|
const options = {
|
|
32
32
|
secret,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScenarioStepPhase } from '@pikku/core/workflow';
|
|
1
|
+
import type { ScenarioStepPhase, ScenarioSurface } from '@pikku/core/workflow';
|
|
2
2
|
import type { WorkflowStepMeta } from '@pikku/core/workflow/types';
|
|
3
3
|
import type { FunctionsMeta } from '@pikku/core';
|
|
4
4
|
import type { ScenarioFailureDetail, ScenarioStepRow } from './scenario-formatter.js';
|
|
@@ -53,10 +53,52 @@ export interface ScenarioProse {
|
|
|
53
53
|
export declare const collectScenarioStepProse: (workflowMeta: {
|
|
54
54
|
steps?: WorkflowStepMeta[];
|
|
55
55
|
} | undefined, functionsMeta: FunctionsMeta) => ScenarioProse;
|
|
56
|
-
/** The step functions in this scenario that declare `browser
|
|
56
|
+
/** The step functions in this scenario that declare a `browser` binding. */
|
|
57
57
|
export declare const scenarioBrowserSteps: (workflowMeta: {
|
|
58
58
|
steps?: WorkflowStepMeta[];
|
|
59
59
|
} | undefined, functionsMeta: FunctionsMeta) => string[];
|
|
60
|
+
/**
|
|
61
|
+
* Steps this scenario cannot run on `surface` at all — they declare neither a
|
|
62
|
+
* binding for it nor a `default` to fall back to.
|
|
63
|
+
*
|
|
64
|
+
* Every phase counts. An action with nothing to run is a broken ladder; an
|
|
65
|
+
* assertion with nothing to run would report a pass having checked nothing,
|
|
66
|
+
* which the engine now refuses outright. Both are better caught here, so the
|
|
67
|
+
* flow is held back with a reason than failed halfway through.
|
|
68
|
+
*
|
|
69
|
+
* A step that *can* run, just not on the run surface, is the different case — a
|
|
70
|
+
* coverage gap, counted by {@link scenarioSurfaceCoverage} rather than a reason
|
|
71
|
+
* to skip.
|
|
72
|
+
*/
|
|
73
|
+
export declare const scenarioStepsWithoutBinding: (workflowMeta: {
|
|
74
|
+
steps?: WorkflowStepMeta[];
|
|
75
|
+
} | undefined, functionsMeta: FunctionsMeta, surface: ScenarioSurface) => string[];
|
|
76
|
+
/**
|
|
77
|
+
* How much of this scenario actually ran on the surface the run targeted.
|
|
78
|
+
*
|
|
79
|
+
* Every step counts, not just the assertions. A ladder of
|
|
80
|
+
* `browser/browser/default/browser` is 3/4 — the step that fell back to the
|
|
81
|
+
* server shows up by lowering the ratio, which is a truer signal than a green
|
|
82
|
+
* assertion count with a footnote. It also makes surfaces comparable: the same
|
|
83
|
+
* scenario is 4/4 on a default run and 3/4 on a browser one, over one
|
|
84
|
+
* denominator.
|
|
85
|
+
*
|
|
86
|
+
* Assertions still get named separately. A `given` or `when` driven server-side
|
|
87
|
+
* took a shortcut; a `then` driven server-side is a sentence claiming the actor
|
|
88
|
+
* saw something nobody looked at. Same effect on the ratio, different kind of
|
|
89
|
+
* problem, so `--strict` gates on the latter only.
|
|
90
|
+
*/
|
|
91
|
+
export interface ScenarioSurfaceCoverage {
|
|
92
|
+
/** Steps that ran on the run surface. */
|
|
93
|
+
onSurface: number;
|
|
94
|
+
/** Steps that ran at all. */
|
|
95
|
+
total: number;
|
|
96
|
+
/** The `then` steps that ran, but somewhere other than the run surface. */
|
|
97
|
+
unwitnessed: string[];
|
|
98
|
+
}
|
|
99
|
+
export declare const scenarioSurfaceCoverage: (workflowMeta: {
|
|
100
|
+
steps?: WorkflowStepMeta[];
|
|
101
|
+
} | undefined, functionsMeta: FunctionsMeta, runSurface: ScenarioSurface) => ScenarioSurfaceCoverage;
|
|
60
102
|
/** The run's steps as renderable rows, in the order they were recorded. */
|
|
61
103
|
export declare const scenarioStepRows: (steps: ScenarioStepOutcome[], prose: ScenarioProse) => ScenarioStepRow[];
|
|
62
104
|
/**
|