@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
|
@@ -49,7 +49,7 @@ export const collectScenarioStepProse = (workflowMeta, functionsMeta) => {
|
|
|
49
49
|
for (const step of walkScenarioSteps(workflowMeta?.steps)) {
|
|
50
50
|
const stepMeta = functionsMeta[step.stepFunc];
|
|
51
51
|
const parts = {
|
|
52
|
-
phase: step.phase
|
|
52
|
+
phase: step.phase,
|
|
53
53
|
description: step.options?.description ?? stepMeta?.description ?? step.stepName,
|
|
54
54
|
// A call-site description is an explicit override, so it wins over the
|
|
55
55
|
// step's template the same way it wins over its description.
|
|
@@ -76,16 +76,65 @@ const sameProse = (a, b) => a.phase === b.phase &&
|
|
|
76
76
|
a.description === b.description &&
|
|
77
77
|
a.template === b.template &&
|
|
78
78
|
a.actor === b.actor;
|
|
79
|
-
/** The step functions in this scenario that declare `browser
|
|
79
|
+
/** The step functions in this scenario that declare a `browser` binding. */
|
|
80
80
|
export const scenarioBrowserSteps = (workflowMeta, functionsMeta) => {
|
|
81
81
|
const names = new Set();
|
|
82
82
|
for (const step of walkScenarioSteps(workflowMeta?.steps)) {
|
|
83
|
-
if (functionsMeta[step.stepFunc]?.
|
|
83
|
+
if (functionsMeta[step.stepFunc]?.scenarioStepSurfaces?.includes('browser')) {
|
|
84
84
|
names.add(step.stepFunc);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
return [...names];
|
|
88
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* Steps this scenario cannot run on `surface` at all — they declare neither a
|
|
91
|
+
* binding for it nor a `default` to fall back to.
|
|
92
|
+
*
|
|
93
|
+
* Every phase counts. An action with nothing to run is a broken ladder; an
|
|
94
|
+
* assertion with nothing to run would report a pass having checked nothing,
|
|
95
|
+
* which the engine now refuses outright. Both are better caught here, so the
|
|
96
|
+
* flow is held back with a reason than failed halfway through.
|
|
97
|
+
*
|
|
98
|
+
* A step that *can* run, just not on the run surface, is the different case — a
|
|
99
|
+
* coverage gap, counted by {@link scenarioSurfaceCoverage} rather than a reason
|
|
100
|
+
* to skip.
|
|
101
|
+
*/
|
|
102
|
+
export const scenarioStepsWithoutBinding = (workflowMeta, functionsMeta, surface) => {
|
|
103
|
+
const names = new Set();
|
|
104
|
+
for (const step of walkScenarioSteps(workflowMeta?.steps)) {
|
|
105
|
+
const surfaces = functionsMeta[step.stepFunc]?.scenarioStepSurfaces ?? [
|
|
106
|
+
'default',
|
|
107
|
+
];
|
|
108
|
+
if (!surfaces.includes(surface) && !surfaces.includes('default')) {
|
|
109
|
+
names.add(step.stepFunc);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [...names];
|
|
113
|
+
};
|
|
114
|
+
export const scenarioSurfaceCoverage = (workflowMeta, functionsMeta, runSurface) => {
|
|
115
|
+
let onSurface = 0;
|
|
116
|
+
let total = 0;
|
|
117
|
+
const unwitnessed = new Set();
|
|
118
|
+
for (const step of walkScenarioSteps(workflowMeta?.steps)) {
|
|
119
|
+
const surfaces = functionsMeta[step.stepFunc]?.scenarioStepSurfaces ?? [
|
|
120
|
+
'default',
|
|
121
|
+
];
|
|
122
|
+
// A step with no binding this run can execute never runs, so it is not a
|
|
123
|
+
// statistic about this run — `scenarioStepsWithoutBinding` holds the flow
|
|
124
|
+
// back for it. Counting it here would score a run on work it refuses to do.
|
|
125
|
+
if (!surfaces.includes(runSurface) && !surfaces.includes('default')) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
total += 1;
|
|
129
|
+
if (runSurface === 'default' || surfaces.includes(runSurface)) {
|
|
130
|
+
onSurface += 1;
|
|
131
|
+
}
|
|
132
|
+
else if (step.phase === 'then') {
|
|
133
|
+
unwitnessed.add(step.stepFunc);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { onSurface, total, unwitnessed: [...unwitnessed] };
|
|
137
|
+
};
|
|
89
138
|
/**
|
|
90
139
|
* Render one step's gherkin sentence.
|
|
91
140
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ScenarioSurface } from '@pikku/core/workflow';
|
|
1
2
|
export declare const scenarioList: 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>;
|
|
2
3
|
export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
|
|
3
4
|
environment: string;
|
|
@@ -7,6 +8,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
|
|
|
7
8
|
excludeTags?: string;
|
|
8
9
|
coverage?: boolean;
|
|
9
10
|
browser?: boolean;
|
|
11
|
+
run?: ScenarioSurface;
|
|
12
|
+
strict?: boolean;
|
|
10
13
|
spawn?: boolean;
|
|
11
14
|
keepAlive?: boolean;
|
|
12
15
|
trace?: boolean;
|
|
@@ -20,6 +23,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
|
|
|
20
23
|
excludeTags?: string;
|
|
21
24
|
coverage?: boolean;
|
|
22
25
|
browser?: boolean;
|
|
26
|
+
run?: ScenarioSurface;
|
|
27
|
+
strict?: boolean;
|
|
23
28
|
spawn?: boolean;
|
|
24
29
|
keepAlive?: boolean;
|
|
25
30
|
trace?: boolean;
|
|
@@ -33,6 +38,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
|
|
|
33
38
|
excludeTags?: string;
|
|
34
39
|
coverage?: boolean;
|
|
35
40
|
browser?: boolean;
|
|
41
|
+
run?: ScenarioSurface;
|
|
42
|
+
strict?: boolean;
|
|
36
43
|
spawn?: boolean;
|
|
37
44
|
keepAlive?: boolean;
|
|
38
45
|
trace?: boolean;
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { resolve, join } from 'node:path';
|
|
2
2
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { pikkuSessionlessFunc } from '#pikku';
|
|
4
|
-
import { InMemoryWorkflowService
|
|
4
|
+
import { InMemoryWorkflowService } from '@pikku/core/services';
|
|
5
|
+
import { createHttpPersonas } from '@pikku/core/persona';
|
|
5
6
|
import { PikkuScenarioService } from '@pikku/core/scenario';
|
|
6
7
|
import { pikkuState, getAllPackageStates } from '@pikku/core/internal';
|
|
7
8
|
import { resolveFeatureScenarios } from '@pikku/core/workflow';
|
|
8
9
|
import { loadScenarioBootstrap } from './load-user-project.js';
|
|
9
|
-
import { collectScenarioStepProse, scenarioBrowserSteps, scenarioFailureFromSteps, scenarioStepRows, } from './scenario-ladder.js';
|
|
10
|
+
import { collectScenarioStepProse, scenarioBrowserSteps, scenarioFailureFromSteps, scenarioStepRows, scenarioStepsWithoutBinding, scenarioSurfaceCoverage, } from './scenario-ladder.js';
|
|
11
|
+
import { SCENARIO_SURFACES } from '@pikku/core/workflow';
|
|
10
12
|
import { formatScenarioReport } from './scenario-formatter.js';
|
|
11
13
|
import { resolveScenarioBrowserProvider, scenarioBrowserLifecycle, } from './scenario-browser.js';
|
|
12
|
-
import {
|
|
14
|
+
import { resolvePersonas } from '../../utils/resolve-personas.js';
|
|
13
15
|
import { spawnDevServer } from '../../server/spawn-dev-server.js';
|
|
14
16
|
import { buildScenarioPlan } from './scenario-plan.js';
|
|
15
|
-
import {
|
|
17
|
+
import { resolveEnvironment } from './environment.js';
|
|
18
|
+
import { createDevAIAgentRunner } from './dev-ai-runner.js';
|
|
16
19
|
const isScenario = (wf) => wf?.scenario === true;
|
|
17
20
|
const listScenarios = (state) => Object.entries(state.workflows?.meta ?? {})
|
|
18
21
|
.filter(([, wf]) => isScenario(wf))
|
|
@@ -84,14 +87,17 @@ export const scenarioList = pikkuSessionlessFunc({
|
|
|
84
87
|
},
|
|
85
88
|
});
|
|
86
89
|
export const scenarioRun = pikkuSessionlessFunc({
|
|
87
|
-
func: async ({ logger, config, getInspectorState, variables }, { environment, flows, features, tags, excludeTags, coverage, browser = true, spawn = false, keepAlive = false, trace = false, apiUrl, appUrl, }) => {
|
|
90
|
+
func: async ({ logger, config, getInspectorState, variables }, { environment, flows, features, tags, excludeTags, coverage, browser = true, run: runSurface = 'default', strict = false, spawn = false, keepAlive = false, trace = false, apiUrl, appUrl, }) => {
|
|
91
|
+
if (!SCENARIO_SURFACES.includes(runSurface)) {
|
|
92
|
+
throw new Error(`Unknown --run surface '${runSurface}'. Expected one of: ${SCENARIO_SURFACES.join(', ')}.`);
|
|
93
|
+
}
|
|
88
94
|
const state = await getInspectorState(true, false, false, true);
|
|
89
95
|
// Resolved once, so actors, step env, the browser driver and any spawned
|
|
90
96
|
// server all target the same place — including when the target only exists
|
|
91
97
|
// at run time and arrives through --api-url/--app-url.
|
|
92
|
-
const env =
|
|
98
|
+
const env = resolveEnvironment({
|
|
93
99
|
environment,
|
|
94
|
-
environments: config.
|
|
100
|
+
environments: config.environments ?? {},
|
|
95
101
|
apiUrl,
|
|
96
102
|
appUrl,
|
|
97
103
|
spawn,
|
|
@@ -164,16 +170,17 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
164
170
|
throw new Error('SCENARIO_ACTOR_SECRET is not set — scenario actors cannot sign in. ' +
|
|
165
171
|
'Export it in the environment running this command (never put it in pikku.config.json).');
|
|
166
172
|
}
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// registry, and codegen resolved the same way to type `
|
|
170
|
-
const scenarioActors =
|
|
171
|
-
const actors =
|
|
173
|
+
// Every declared persona, with its address filled in. Resolved once: the
|
|
174
|
+
// HTTP personas and the Playwright provider below must see the same
|
|
175
|
+
// registry, and codegen resolved the same way to type `PersonaName`.
|
|
176
|
+
const scenarioActors = resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain);
|
|
177
|
+
const actors = createHttpPersonas({
|
|
172
178
|
apiUrl: env.apiUrl,
|
|
173
179
|
secret,
|
|
174
|
-
|
|
180
|
+
personas: scenarioActors,
|
|
175
181
|
signInPath: env.signInPath,
|
|
176
182
|
rpcPath: env.rpcPath,
|
|
183
|
+
model: config.scenarios?.model,
|
|
177
184
|
});
|
|
178
185
|
const functionsMeta = state.functions?.meta ?? {};
|
|
179
186
|
const scenarioNames = new Set(groups.flatMap((group) => group.entries.map((e) => e.scenarioName)));
|
|
@@ -183,6 +190,15 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
183
190
|
scenarioBrowserSteps(state.workflows?.meta?.[name], functionsMeta),
|
|
184
191
|
])
|
|
185
192
|
.filter(([, steps]) => steps.length > 0));
|
|
193
|
+
// `--no-browser` is the blunt form of `--run default`: a machine with no
|
|
194
|
+
// browser takes the server-side path rather than skipping the scenario.
|
|
195
|
+
const effectiveSurface = browser === false && runSurface === 'browser' ? 'default' : runSurface;
|
|
196
|
+
const unrunnableStepsByFlow = new Map([...scenarioNames]
|
|
197
|
+
.map((name) => [
|
|
198
|
+
name,
|
|
199
|
+
scenarioStepsWithoutBinding(state.workflows?.meta?.[name], functionsMeta, effectiveSurface),
|
|
200
|
+
])
|
|
201
|
+
.filter(([, steps]) => steps.length > 0));
|
|
186
202
|
// Two reasons a scenario is held back, both reported as SKIP rather than
|
|
187
203
|
// failed. `--no-browser` is the direct replacement for cucumber's `@console`
|
|
188
204
|
// tag, so a standard run stays green on a machine with no browser; a `skip`
|
|
@@ -191,8 +207,9 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
191
207
|
const skipReasonFor = (entry) => {
|
|
192
208
|
if (entry.skip)
|
|
193
209
|
return entry.skip;
|
|
194
|
-
|
|
195
|
-
|
|
210
|
+
const unrunnable = unrunnableStepsByFlow.get(entry.scenarioName);
|
|
211
|
+
if (unrunnable?.length) {
|
|
212
|
+
return `no ${effectiveSurface} or default binding: ${unrunnable.join(', ')}`;
|
|
196
213
|
}
|
|
197
214
|
return undefined;
|
|
198
215
|
};
|
|
@@ -215,10 +232,27 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
215
232
|
apiUrl: env.apiUrl,
|
|
216
233
|
appUrl: env.appUrl,
|
|
217
234
|
});
|
|
235
|
+
scenarioService.setRunSurface(effectiveSurface);
|
|
236
|
+
// Scenario steps run here, not on the target — everything they touch of the
|
|
237
|
+
// app goes over HTTP through an actor, which is what `guardRpc` below
|
|
238
|
+
// enforces. `actor.converse` is the exception, and the reason this is not
|
|
239
|
+
// just the three services above: the persona's own turns are LLM calls made
|
|
240
|
+
// in this process, and without a runner every conversing scenario fails
|
|
241
|
+
// before it says anything. Built the same way `pikku dev` builds its own,
|
|
242
|
+
// and only when the project declares agents, so a project with no agents
|
|
243
|
+
// does not have to have AI env set to run scenarios.
|
|
244
|
+
const aiAgentRunner = Object.keys(state.agents?.agentsMeta ?? {}).length > 0
|
|
245
|
+
? await createDevAIAgentRunner({
|
|
246
|
+
logger,
|
|
247
|
+
projectRoot: config.rootDir,
|
|
248
|
+
variables,
|
|
249
|
+
})
|
|
250
|
+
: undefined;
|
|
218
251
|
pikkuState(null, 'package', 'singletonServices', {
|
|
219
252
|
logger,
|
|
220
253
|
workflowService,
|
|
221
254
|
workflowRunService: workflowService,
|
|
255
|
+
...(aiAgentRunner ? { aiAgentRunner } : {}),
|
|
222
256
|
});
|
|
223
257
|
const guardRpc = {
|
|
224
258
|
rpcWithWire: async (rpcName) => {
|
|
@@ -227,7 +261,11 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
227
261
|
`'${environment}' (${env.apiUrl}), not local services.`);
|
|
228
262
|
},
|
|
229
263
|
};
|
|
230
|
-
|
|
264
|
+
// Only a browser run launches one. Under `--run default` a step with a
|
|
265
|
+
// browser binding takes its default path instead, so there is nothing to
|
|
266
|
+
// drive and nothing to pay for.
|
|
267
|
+
const needsBrowser = effectiveSurface === 'browser' &&
|
|
268
|
+
groups.some((group) => group.entries.some((entry) => browserStepsByFlow.has(entry.scenarioName)));
|
|
231
269
|
const failureDir = join(resolve(config.rootDir, config.outDir), 'scenario-failures');
|
|
232
270
|
const browserLifecycle = scenarioBrowserLifecycle(needsBrowser
|
|
233
271
|
? await (async () => {
|
|
@@ -446,10 +484,36 @@ export const scenarioRun = pikkuSessionlessFunc({
|
|
|
446
484
|
trace,
|
|
447
485
|
projectRoot: config.rootDir,
|
|
448
486
|
})) {
|
|
449
|
-
logger[level](
|
|
487
|
+
const write = logger[level].bind(logger);
|
|
488
|
+
write(text);
|
|
489
|
+
}
|
|
490
|
+
// How much of the run actually happened on the surface it targeted. Every
|
|
491
|
+
// step counts, so a step that fell back to the server lowers the ratio
|
|
492
|
+
// rather than needing a footnote. Assertions that fell back are named
|
|
493
|
+
// separately — those are sentences claiming an observation nobody made.
|
|
494
|
+
const surfaceCoverage = { onSurface: 0, total: 0 };
|
|
495
|
+
const unwitnessed = new Set();
|
|
496
|
+
for (const name of scenarioNames) {
|
|
497
|
+
const scenario = scenarioSurfaceCoverage(state.workflows?.meta?.[name], functionsMeta, effectiveSurface);
|
|
498
|
+
surfaceCoverage.onSurface += scenario.onSurface;
|
|
499
|
+
surfaceCoverage.total += scenario.total;
|
|
500
|
+
for (const step of scenario.unwitnessed)
|
|
501
|
+
unwitnessed.add(step);
|
|
502
|
+
}
|
|
503
|
+
if (effectiveSurface !== 'default' && surfaceCoverage.total > 0) {
|
|
504
|
+
const line = `${surfaceCoverage.onSurface}/${surfaceCoverage.total} steps ran on ${effectiveSurface}`;
|
|
505
|
+
if (unwitnessed.size === 0) {
|
|
506
|
+
logger.info(line);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
const write = logger[strict ? 'error' : 'warn'].bind(logger);
|
|
510
|
+
write(`${line} — asserted server-side only: ${[...unwitnessed].join(', ')}`);
|
|
511
|
+
}
|
|
450
512
|
}
|
|
451
513
|
const failed = results.filter((r) => r.status === 'failed');
|
|
452
|
-
if (failed.length > 0 ||
|
|
514
|
+
if (failed.length > 0 ||
|
|
515
|
+
hookFailures.length > 0 ||
|
|
516
|
+
(strict && unwitnessed.size > 0)) {
|
|
453
517
|
process.exitCode = 1;
|
|
454
518
|
}
|
|
455
519
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
1
2
|
import type { FlatScope } from '@pikku/core/scope';
|
|
2
3
|
import { KyselyScopeService } from '@pikku/kysely';
|
|
4
|
+
import type { KyselyPikkuDB } from '@pikku/kysely';
|
|
3
5
|
type Logger = {
|
|
4
6
|
info: (msg: string) => void;
|
|
5
7
|
warn: (msg: string) => void;
|
|
@@ -8,6 +10,12 @@ type Logger = {
|
|
|
8
10
|
export declare const loadDeclaredScopes: (scopesMetaJsonFile: string, logger: Logger) => Promise<FlatScope[] | null>;
|
|
9
11
|
export type OpenedScopeService = {
|
|
10
12
|
service: KyselyScopeService;
|
|
13
|
+
/**
|
|
14
|
+
* The same connection the service holds, for the one thing it cannot answer:
|
|
15
|
+
* a user's id from their address. Grants are keyed by id, and the `user`
|
|
16
|
+
* table those ids come from is better-auth's, not the scope schema's.
|
|
17
|
+
*/
|
|
18
|
+
db: Kysely<KyselyPikkuDB>;
|
|
11
19
|
destroy: () => Promise<void>;
|
|
12
20
|
};
|
|
13
21
|
/**
|
|
@@ -25,6 +33,10 @@ export declare const openScopeService: ({ config, logger, }: {
|
|
|
25
33
|
outDir: string;
|
|
26
34
|
runtimeDir?: string;
|
|
27
35
|
srcDirectories: string[];
|
|
36
|
+
db?: {
|
|
37
|
+
schema?: string;
|
|
38
|
+
defaultSchema?: string;
|
|
39
|
+
};
|
|
28
40
|
};
|
|
29
41
|
logger: Logger;
|
|
30
42
|
}, declared: FlatScope[], command: string) => Promise<OpenedScopeService | null>;
|
|
@@ -52,7 +52,7 @@ export const openScopeService = async ({ config, logger, }, declared, command) =
|
|
|
52
52
|
if (!userConfig) {
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
-
const resolved = resolveDb(userConfig, config.rootDir, config.outDir, config.runtimeDir);
|
|
55
|
+
const resolved = resolveDb(userConfig, config.rootDir, config.outDir, config.runtimeDir, config.db);
|
|
56
56
|
if (!resolved) {
|
|
57
57
|
logger.error(`${command}: no database configured — set sqliteDb or postgresUrl in your createConfig.`);
|
|
58
58
|
throw new Error('no database configured');
|
|
@@ -62,7 +62,7 @@ export const openScopeService = async ({ config, logger, }, declared, command) =
|
|
|
62
62
|
const service = new KyselyScopeService(db);
|
|
63
63
|
await service.init();
|
|
64
64
|
await service.syncScopes(declared);
|
|
65
|
-
return { service, destroy: () => db.destroy() };
|
|
65
|
+
return { service, db, destroy: () => db.destroy() };
|
|
66
66
|
}
|
|
67
67
|
catch (e) {
|
|
68
68
|
await db.destroy();
|
|
@@ -12,6 +12,7 @@ import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
|
|
|
12
12
|
import { createDevAIAgentRunner } from './dev-ai-runner.js';
|
|
13
13
|
import { resolveConsoleMount } from './serve-console.js';
|
|
14
14
|
import { serverReadyLine } from '../../server/server-ready.js';
|
|
15
|
+
import { createEphemeralContentSigningJWT } from '../../server/content-signing-jwt.js';
|
|
15
16
|
export const serve = pikkuSessionlessFunc({
|
|
16
17
|
remote: true,
|
|
17
18
|
func: async ({ logger, config, getInspectorState, variables, devServerRunner }, { port, console: serveConsole }) => {
|
|
@@ -37,7 +38,7 @@ export const serve = pikkuSessionlessFunc({
|
|
|
37
38
|
const effectiveDbConfig = envDatabaseUrl
|
|
38
39
|
? parseDatabaseUrl(envDatabaseUrl)
|
|
39
40
|
: userConfig;
|
|
40
|
-
const resolvedDb = resolveDb(effectiveDbConfig, config.rootDir, config.outDir, config.runtimeDir);
|
|
41
|
+
const resolvedDb = resolveDb(effectiveDbConfig, config.rootDir, config.outDir, config.runtimeDir, config.db);
|
|
41
42
|
const resolvedLocalDb = resolvedDb ?? undefined;
|
|
42
43
|
const kysely = resolvedLocalDb
|
|
43
44
|
? await createKysely(resolvedLocalDb)
|
|
@@ -54,8 +55,11 @@ export const serve = pikkuSessionlessFunc({
|
|
|
54
55
|
sizeLimit: userConfig.content.sizeLimit,
|
|
55
56
|
}
|
|
56
57
|
: undefined;
|
|
57
|
-
const
|
|
58
|
-
?
|
|
58
|
+
const contentSigningJWT = localContentConfig
|
|
59
|
+
? createEphemeralContentSigningJWT()
|
|
60
|
+
: undefined;
|
|
61
|
+
const localContent = localContentConfig && contentSigningJWT
|
|
62
|
+
? new LocalContent(localContentConfig, logger, contentSigningJWT)
|
|
59
63
|
: undefined;
|
|
60
64
|
const schedulerService = new InMemorySchedulerService();
|
|
61
65
|
const aiStorage = kysely
|
|
@@ -128,7 +132,7 @@ export const serve = pikkuSessionlessFunc({
|
|
|
128
132
|
port: resolvedPort,
|
|
129
133
|
content: localContentConfig,
|
|
130
134
|
...(consoleMount ? { staticMounts: [consoleMount] } : {}),
|
|
131
|
-
}, logger);
|
|
135
|
+
}, logger, { contentSigningJWT });
|
|
132
136
|
const lifecycle = await loadLifecycle();
|
|
133
137
|
await pikkuServer.init();
|
|
134
138
|
await lifecycle?.beforeStart?.(resolvedServices);
|
|
@@ -30,7 +30,7 @@ export const pikkuVersionsCheck = pikkuSessionlessFunc({
|
|
|
30
30
|
if (error.nextVersion !== undefined) {
|
|
31
31
|
logger.info(` Set \`version: ${error.nextVersion}\` on the function, then run:`);
|
|
32
32
|
}
|
|
33
|
-
logger.info(` npx pikku versions
|
|
33
|
+
logger.info(` npx pikku versions update`);
|
|
34
34
|
}
|
|
35
35
|
else if (error.code === ErrorCode.FUNCTION_VERSION_MODIFIED &&
|
|
36
36
|
error.functionKey) {
|
|
@@ -54,7 +54,7 @@ export const pikkuVersionsCheck = pikkuSessionlessFunc({
|
|
|
54
54
|
logger.info(``);
|
|
55
55
|
logger.info(` This usually means a merge conflict in versions.pikku.json.`);
|
|
56
56
|
logger.info(` Resolve the conflict, then run:`);
|
|
57
|
-
logger.info(` npx pikku versions
|
|
57
|
+
logger.info(` npx pikku versions update`);
|
|
58
58
|
}
|
|
59
59
|
else if (error.code === ErrorCode.VERSION_GAP_NOT_ALLOWED &&
|
|
60
60
|
error.functionKey) {
|
|
@@ -85,7 +85,7 @@ export const pikkuVersionsCheck = pikkuSessionlessFunc({
|
|
|
85
85
|
}
|
|
86
86
|
logger.info(``);
|
|
87
87
|
logger.info(` The manifest is corrupted. Run:`);
|
|
88
|
-
logger.info(` npx pikku versions
|
|
88
|
+
logger.info(` npx pikku versions update`);
|
|
89
89
|
}
|
|
90
90
|
else {
|
|
91
91
|
logger.info(`[${error.code}] ${error.message}`);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The virtual user run report, and how it is rendered.
|
|
3
|
+
*
|
|
4
|
+
* A run is not a pass/fail: a virtual user pursuing plausible goals against a
|
|
5
|
+
* real stage produces a story — what it tried, what it gave up on, and what
|
|
6
|
+
* came back that should not have. The findings are the product; everything else
|
|
7
|
+
* is context for reading them.
|
|
8
|
+
*
|
|
9
|
+
* Kept a pure function over plain data for the same reason the scenario
|
|
10
|
+
* reporter is: a second formatter (JSON for fabric, JUnit for CI) is then one
|
|
11
|
+
* function rather than an unpicking of the runner.
|
|
12
|
+
*/
|
|
13
|
+
import type { VirtualUserRunResult } from '@pikku/core/virtual-user';
|
|
14
|
+
export interface VirtualUserReportContext {
|
|
15
|
+
persona: string;
|
|
16
|
+
disposition: string;
|
|
17
|
+
environment: string;
|
|
18
|
+
apiUrl: string;
|
|
19
|
+
/** How much of the catalogue's read/write split had to be guessed at. */
|
|
20
|
+
catalogue: {
|
|
21
|
+
total: number;
|
|
22
|
+
annotated: number;
|
|
23
|
+
inferred: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface VirtualUserReportLine {
|
|
27
|
+
level: 'info' | 'error' | 'warn';
|
|
28
|
+
text: string;
|
|
29
|
+
}
|
|
30
|
+
export declare const formatVirtualUserReport: (result: VirtualUserRunResult, context: VirtualUserReportContext) => VirtualUserReportLine[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const STATUS_MARK = {
|
|
2
|
+
completed: 'DONE',
|
|
3
|
+
abandoned: 'DROP',
|
|
4
|
+
stuck: 'STUCK',
|
|
5
|
+
suspended: 'OPEN',
|
|
6
|
+
open: 'OPEN',
|
|
7
|
+
};
|
|
8
|
+
/** Why a run ended, in words rather than an enum. */
|
|
9
|
+
const STOPPED_BY = {
|
|
10
|
+
'no-intents': 'this persona had nothing to want — no scenario names them',
|
|
11
|
+
'budget-steps': 'ran out of steps',
|
|
12
|
+
'budget-mutations': 'ran out of mutations',
|
|
13
|
+
'budget-duration': 'ran out of time',
|
|
14
|
+
'stop-hook': 'the app called a halt',
|
|
15
|
+
exhausted: 'saw everything it came for through',
|
|
16
|
+
};
|
|
17
|
+
const findingLine = (finding) => ` ${finding.kind.padEnd(18)} ${finding.detail} (step ${finding.step})`;
|
|
18
|
+
const plural = (count, noun) => `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
19
|
+
export const formatVirtualUserReport = (result, context) => {
|
|
20
|
+
const lines = [];
|
|
21
|
+
const info = (text) => lines.push({ level: 'info', text });
|
|
22
|
+
const error = (text) => lines.push({ level: 'error', text });
|
|
23
|
+
const warn = (text) => lines.push({ level: 'warn', text });
|
|
24
|
+
info(`Virtual user '${context.persona}' (${context.disposition}) against '${context.environment}' — ${context.apiUrl}`);
|
|
25
|
+
info(` seed ${result.seed} · ${plural(context.catalogue.total, 'endpoint')} offered` +
|
|
26
|
+
(context.catalogue.inferred
|
|
27
|
+
? ` · read/write guessed for ${context.catalogue.inferred} of them`
|
|
28
|
+
: ''));
|
|
29
|
+
if (result.intents.length) {
|
|
30
|
+
info('');
|
|
31
|
+
info('What it tried');
|
|
32
|
+
for (const intent of result.intents) {
|
|
33
|
+
const mark = STATUS_MARK[intent.status] ?? intent.status.toUpperCase();
|
|
34
|
+
const interrupted = intent.suspensions
|
|
35
|
+
? `, put down ${plural(intent.suspensions, 'time')}`
|
|
36
|
+
: '';
|
|
37
|
+
const summary = intent.summary ? ` — ${intent.summary}` : '';
|
|
38
|
+
info(` ${mark.padEnd(5)} ${intent.title} (${plural(intent.steps.length, 'step')}${interrupted})${summary}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
info('');
|
|
42
|
+
if (result.findings.length === 0) {
|
|
43
|
+
info('Nothing came back that should not have.');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
error(`${plural(result.findings.length, 'finding')}`);
|
|
47
|
+
for (const finding of result.findings) {
|
|
48
|
+
error(findingLine(finding));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const { tally } = result;
|
|
52
|
+
info('');
|
|
53
|
+
info(`${plural(tally.steps, 'step')} · ${plural(tally.calls, 'call')} · ` +
|
|
54
|
+
`${plural(tally.mutations, 'mutation')} · ${tally.tokensIn}/${tally.tokensOut} tokens ` +
|
|
55
|
+
`(${tally.model}) · ${Math.round(tally.elapsedMs / 1000)}s`);
|
|
56
|
+
info(`Stopped because it ${STOPPED_BY[result.stoppedBy]}.`);
|
|
57
|
+
if (result.stoppedBy === 'no-intents') {
|
|
58
|
+
warn(`Give it something to want: declare a scenario naming '${context.persona}', or pass --goals.`);
|
|
59
|
+
}
|
|
60
|
+
// A seed is only worth printing when there is something to reproduce.
|
|
61
|
+
if (result.findings.length) {
|
|
62
|
+
info(`Replay this exact run with --seed ${result.seed}.`);
|
|
63
|
+
}
|
|
64
|
+
return lines;
|
|
65
|
+
};
|
|
@@ -2,6 +2,8 @@ import type { ColumnKind } from './coercion-plugin.js';
|
|
|
2
2
|
import { type ZodFormat } from './zod-codegen.js';
|
|
3
3
|
type Classification = 'public' | 'private' | 'pii' | 'secret';
|
|
4
4
|
type AnonymizeStrategy = 'fake:email' | 'fake:name' | 'hash' | 'keep' | null;
|
|
5
|
+
type ColumnForm = 'plain' | 'hashed' | 'wrapped' | 'sealed';
|
|
6
|
+
export declare const COLUMN_FORMS: readonly ["plain", "hashed", "wrapped", "sealed"];
|
|
5
7
|
export interface ColAnnotation {
|
|
6
8
|
/** Column kind override: `date`, `bool`, `json`, or `uuid`. */
|
|
7
9
|
kind?: ColumnKind;
|
|
@@ -15,6 +17,11 @@ export interface ColAnnotation {
|
|
|
15
17
|
format?: ZodFormat;
|
|
16
18
|
classification?: Classification;
|
|
17
19
|
anonymize?: AnonymizeStrategy;
|
|
20
|
+
/**
|
|
21
|
+
* At-rest representation, independent of `classification`. Drives the
|
|
22
|
+
* insert/update brand on the generated column type. Absent means `plain`.
|
|
23
|
+
*/
|
|
24
|
+
form?: ColumnForm;
|
|
18
25
|
}
|
|
19
26
|
/** Per-table, per-column annotation map sourced from `db/annotations.ts`. */
|
|
20
27
|
export type AnnotationMap = Record<string, Record<string, ColAnnotation>>;
|
|
@@ -37,6 +44,13 @@ export declare function nameSuggestsKind(colName: string): ColumnKind | null;
|
|
|
37
44
|
* where `security` is the privacy level and `classification` is the anonymize
|
|
38
45
|
* strategy. Returns `{}` if the sidecar doesn't exist yet (first migrate run,
|
|
39
46
|
* before it has been generated).
|
|
47
|
+
*
|
|
48
|
+
* `DbClassificationMap` nests under the schema name whenever the project has
|
|
49
|
+
* one (`{ app: { user: { email: … } } }`), so the sidecar is flattened back to
|
|
50
|
+
* bare table names here — the codegen looks columns up by `bareTableName`.
|
|
51
|
+
* Reading a schema-qualified map as though it were flat silently yields an
|
|
52
|
+
* annotation for every column of every table, which is how a project could
|
|
53
|
+
* classify a column `secret` and still generate it as `private`.
|
|
40
54
|
*/
|
|
41
55
|
export declare function loadAnnotations(rootDir: string): AnnotationMap;
|
|
42
56
|
export {};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ZOD_FORMATS } from './zod-codegen.js';
|
|
4
|
+
export const COLUMN_FORMS = [
|
|
5
|
+
'plain',
|
|
6
|
+
'hashed',
|
|
7
|
+
'wrapped',
|
|
8
|
+
'sealed',
|
|
9
|
+
];
|
|
4
10
|
/**
|
|
5
11
|
* Warn-only naming heuristic. We no longer *infer* a column's kind from its
|
|
6
12
|
* name (it produced wrong types — e.g. SQLite stores `*_at` as ISO TEXT, not a
|
|
@@ -23,6 +29,17 @@ function parseStrategy(s) {
|
|
|
23
29
|
? s
|
|
24
30
|
: null;
|
|
25
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* A column entry's own values are all primitives; a table's are objects. That
|
|
34
|
+
* is the only structural difference between the two levels, and it is what
|
|
35
|
+
* tells a schema-qualified map (`{ app: { user: { … } } }`) from a bare one
|
|
36
|
+
* (`{ user: { … } }`) without having to be told which shape was authored.
|
|
37
|
+
*/
|
|
38
|
+
function isColumnEntry(value) {
|
|
39
|
+
if (typeof value !== 'object' || value === null)
|
|
40
|
+
return false;
|
|
41
|
+
return Object.values(value).every((v) => typeof v !== 'object' || v === null);
|
|
42
|
+
}
|
|
26
43
|
/**
|
|
27
44
|
* Load annotations from the `db/annotations.gen.json` sidecar, which is
|
|
28
45
|
* compiled from the developer-authored `db/annotations.ts` (`DbClassificationMap`)
|
|
@@ -34,6 +51,13 @@ function parseStrategy(s) {
|
|
|
34
51
|
* where `security` is the privacy level and `classification` is the anonymize
|
|
35
52
|
* strategy. Returns `{}` if the sidecar doesn't exist yet (first migrate run,
|
|
36
53
|
* before it has been generated).
|
|
54
|
+
*
|
|
55
|
+
* `DbClassificationMap` nests under the schema name whenever the project has
|
|
56
|
+
* one (`{ app: { user: { email: … } } }`), so the sidecar is flattened back to
|
|
57
|
+
* bare table names here — the codegen looks columns up by `bareTableName`.
|
|
58
|
+
* Reading a schema-qualified map as though it were flat silently yields an
|
|
59
|
+
* annotation for every column of every table, which is how a project could
|
|
60
|
+
* classify a column `secret` and still generate it as `private`.
|
|
37
61
|
*/
|
|
38
62
|
export function loadAnnotations(rootDir) {
|
|
39
63
|
const jsonPath = join(rootDir, 'db', 'annotations.gen.json');
|
|
@@ -41,8 +65,24 @@ export function loadAnnotations(rootDir) {
|
|
|
41
65
|
return {};
|
|
42
66
|
try {
|
|
43
67
|
const raw = JSON.parse(readFileSync(jsonPath, 'utf8'));
|
|
68
|
+
const tables = [];
|
|
69
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
70
|
+
if (!value || typeof value !== 'object')
|
|
71
|
+
continue;
|
|
72
|
+
const isTable = Object.values(value).every((v) => v === null || isColumnEntry(v));
|
|
73
|
+
if (isTable) {
|
|
74
|
+
tables.push([key, value]);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// `key` is a schema; its values are the tables.
|
|
78
|
+
for (const [table, cols] of Object.entries(value)) {
|
|
79
|
+
if (cols && typeof cols === 'object') {
|
|
80
|
+
tables.push([table, cols]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
44
84
|
const result = {};
|
|
45
|
-
for (const [table, cols] of
|
|
85
|
+
for (const [table, cols] of tables) {
|
|
46
86
|
result[table] = {};
|
|
47
87
|
for (const [col, ann] of Object.entries(cols)) {
|
|
48
88
|
if (!ann)
|
|
@@ -57,7 +97,9 @@ export function loadAnnotations(rootDir) {
|
|
|
57
97
|
entry.tsType = ann.tsType;
|
|
58
98
|
if (ann.format && ann.format in ZOD_FORMATS)
|
|
59
99
|
entry.format = ann.format;
|
|
60
|
-
// `security` is the privacy level. `encrypted`
|
|
100
|
+
// `security` is the privacy level. The legacy `encrypted` value
|
|
101
|
+
// predates `form` and conflated the two axes; it still parses, as the
|
|
102
|
+
// pair it always meant.
|
|
61
103
|
switch (ann.security) {
|
|
62
104
|
case 'public':
|
|
63
105
|
case 'private':
|
|
@@ -67,8 +109,14 @@ export function loadAnnotations(rootDir) {
|
|
|
67
109
|
break;
|
|
68
110
|
case 'encrypted':
|
|
69
111
|
entry.classification = 'secret';
|
|
112
|
+
entry.form = 'wrapped';
|
|
70
113
|
break;
|
|
71
114
|
}
|
|
115
|
+
// An explicit `form` wins over the one implied by legacy `encrypted`.
|
|
116
|
+
if (ann.form &&
|
|
117
|
+
COLUMN_FORMS.includes(ann.form)) {
|
|
118
|
+
entry.form = ann.form;
|
|
119
|
+
}
|
|
72
120
|
const strategy = parseStrategy(ann.classification);
|
|
73
121
|
if (strategy !== null)
|
|
74
122
|
entry.anonymize = strategy;
|
|
@@ -3,6 +3,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
3
3
|
import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
|
|
4
4
|
import { join, extname, dirname } from 'node:path';
|
|
5
5
|
import { PIKKU_BETTER_AUTH } from '@pikku/better-auth';
|
|
6
|
+
import { createSecretValue } from '@pikku/core';
|
|
6
7
|
import { LocalVariablesService } from '@pikku/core/services';
|
|
7
8
|
import { loadUserModule } from '../commands/load-user-project.js';
|
|
8
9
|
let cachedGetMigrations = null;
|
|
@@ -95,7 +96,7 @@ async function loadAuthFactory(sourceFile) {
|
|
|
95
96
|
function fakeSecretService() {
|
|
96
97
|
const placeholder = 'schema-introspection-only';
|
|
97
98
|
return {
|
|
98
|
-
getSecret: async () => placeholder,
|
|
99
|
+
getSecret: async () => createSecretValue(placeholder),
|
|
99
100
|
hasSecret: async () => true,
|
|
100
101
|
setSecret: async () => { },
|
|
101
102
|
};
|