@pikku/cli 0.12.162 → 0.12.163
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/{HelpPanel-DR6oJReA.js → HelpPanel-C__s-OXa.js} +1 -1
- package/console-app/assets/{abnfDiagram-VCTEODGH-C6mt0F-I.js → abnfDiagram-VCTEODGH-DLIFOOvF.js} +1 -1
- package/console-app/assets/architecture-7GRP2DOG-C3aOtis2.js +1 -0
- package/console-app/assets/{architectureDiagram-5GKGNRK7-BPUTuzrJ.js → architectureDiagram-5GKGNRK7-DTilS9zr.js} +1 -1
- package/console-app/assets/{blockDiagram-I7D4REHJ-IE8X3BZC.js → blockDiagram-I7D4REHJ-CCTMqaSo.js} +1 -1
- package/console-app/assets/{c4Diagram-7LVT6UL2--HhbjXSJ.js → c4Diagram-7LVT6UL2-HTxceuVS.js} +1 -1
- package/console-app/assets/channel-C9rJmzgz.js +1 -0
- package/console-app/assets/{chunk-4HAMMTFA-B2Cljp_-.js → chunk-4HAMMTFA-CB4W3eSt.js} +1 -1
- package/console-app/assets/{chunk-75Z2AOVW-D4684ofZ.js → chunk-75Z2AOVW-C9TP1VXB.js} +1 -1
- package/console-app/assets/{chunk-DU6HZSFF-BXipkMhY.js → chunk-DU6HZSFF-84ww5mPe.js} +1 -1
- package/console-app/assets/{chunk-F27PBJKO-BsSS2TqC.js → chunk-F27PBJKO-B6nEGqlt.js} +1 -1
- package/console-app/assets/{chunk-GMAD6QVW-DJqDw8yU.js → chunk-GMAD6QVW-SJ1Qwv1p.js} +1 -1
- package/console-app/assets/{chunk-GVQU2GXP-CguL9TtP.js → chunk-GVQU2GXP-CpvfZAir.js} +1 -1
- package/console-app/assets/{chunk-IMKFNOWR-DoB5kv29.js → chunk-IMKFNOWR-CdZpInOk.js} +1 -1
- package/console-app/assets/{chunk-L3NEJ4N5-BspKBmrG.js → chunk-L3NEJ4N5-DDwTgcch.js} +1 -1
- package/console-app/assets/{chunk-OSK3NFVY-DzLs9ozF.js → chunk-OSK3NFVY-Ce8laAeV.js} +1 -1
- package/console-app/assets/{chunk-P2QGCYS3-DAxcyv2x.js → chunk-P2QGCYS3-CfBNms3b.js} +1 -1
- package/console-app/assets/{chunk-POPQ4Y6H-DyLFn-sn.js → chunk-POPQ4Y6H-BZI0KZav.js} +1 -1
- package/console-app/assets/{chunk-PWAF6VOD-VPwoG6QJ.js → chunk-PWAF6VOD-Car_F-5i.js} +1 -1
- package/console-app/assets/{chunk-SHT3W25Y-C0R9Ptyj.js → chunk-SHT3W25Y-B7o3qSVw.js} +1 -1
- package/console-app/assets/{chunk-SVP7TREG-cLgcNTyh.js → chunk-SVP7TREG-8bi0TugS.js} +1 -1
- package/console-app/assets/{chunk-TICWLB2K-C3se3Npx.js → chunk-TICWLB2K-BT94BHEH.js} +1 -1
- package/console-app/assets/chunk-XXDRQBXY-CN-XariJ.js +1 -0
- package/console-app/assets/classDiagram-ZZMXUADV-C-G692c4.js +1 -0
- package/console-app/assets/classDiagram-v2-VYDZK3BY-C-G692c4.js +1 -0
- package/console-app/assets/{cose-bilkent-JH36ORCC-L5Nhs9N1.js → cose-bilkent-JH36ORCC-Bpn1bn9K.js} +1 -1
- package/console-app/assets/{cynefin-OW5HDTMX-jbgNKIs_.js → cynefin-OW5HDTMX-CfFkO8xq.js} +1 -1
- package/console-app/assets/{cynefinDiagram-5FMLGOSQ-D10NQDH4.js → cynefinDiagram-5FMLGOSQ-E66AVAFL.js} +1 -1
- package/console-app/assets/{dagre-DrsKN-Kd.js → dagre-54kLhRyV.js} +1 -1
- package/console-app/assets/{dagre-GXQ25YYZ-NDp4fO1z.js → dagre-GXQ25YYZ-D4En0mXW.js} +1 -1
- package/console-app/assets/{diagram-S7CK7UJ4-B-9PMbK8.js → diagram-S7CK7UJ4-DhFYC_I5.js} +1 -1
- package/console-app/assets/{diagram-UQ7AKVKN-NXSn7aYB.js → diagram-UQ7AKVKN-QV1teZ2Z.js} +1 -1
- package/console-app/assets/{diagram-VSXAHHWV-DrZgXRIZ.js → diagram-VSXAHHWV-CTZbzeS4.js} +1 -1
- package/console-app/assets/{diagram-VX7I27RA-V3OLFF3v.js → diagram-VX7I27RA-BFoMlx0h.js} +1 -1
- package/console-app/assets/{diagram-Z3DM3KII-CawCD3BR.js → diagram-Z3DM3KII-DXYj_9Q8.js} +1 -1
- package/console-app/assets/{ebnfDiagram-PWID7BFC-QScQWlzV.js → ebnfDiagram-PWID7BFC-C7TLD6M3.js} +1 -1
- package/console-app/assets/{erDiagram-RLTQ6QDP-DIhmCYb6.js → erDiagram-RLTQ6QDP-hPkM-ewN.js} +1 -1
- package/console-app/assets/eventmodeling-NTZA5JFV-U-EsOY6h.js +1 -0
- package/console-app/assets/flowDiagram-HODETNUW-LmjHrpKd.js +1 -0
- package/console-app/assets/{ganttDiagram-EL5Y4UJY-Bcg0H_3-.js → ganttDiagram-EL5Y4UJY-B6TmOQZX.js} +1 -1
- package/console-app/assets/{gitGraph-4MIJSDKK-BUYXp4LH.js → gitGraph-4MIJSDKK-BwbHr20B.js} +1 -1
- package/console-app/assets/{gitGraphDiagram-WWUBYQGX-DRxwgliX.js → gitGraphDiagram-WWUBYQGX-Dl1TcmWc.js} +1 -1
- package/console-app/assets/{graphlib-BXZn3TnO.js → graphlib-DHZpPJUN.js} +1 -1
- package/console-app/assets/{index-D7ATEpJh.js → index-BzStIuLU.js} +137 -137
- package/console-app/assets/{info-A6RAGUB7-BAfA_BVB.js → info-A6RAGUB7-D708VGlV.js} +1 -1
- package/console-app/assets/{infoDiagram-27XIBGKW-JPbCbDjB.js → infoDiagram-27XIBGKW-UHD7ySe3.js} +1 -1
- package/console-app/assets/{ishikawaDiagram-5VMMS53U-528xNFds.js → ishikawaDiagram-5VMMS53U-GyFZr765.js} +1 -1
- package/console-app/assets/{journeyDiagram-3NMN7TZE-CpnC76my.js → journeyDiagram-3NMN7TZE-CoewHpEg.js} +1 -1
- package/console-app/assets/{kanban-definition-UXKFOSKX-7v_OexVf.js → kanban-definition-UXKFOSKX-F0W6Aec5.js} +1 -1
- package/console-app/assets/{line-BXTtRzTv.js → line-C4wYKh6f.js} +1 -1
- package/console-app/assets/{linear-BXIxFuy8.js → linear-nPQacEjt.js} +1 -1
- package/console-app/assets/{mermaid-parser.core-Bn81UzgU.js → mermaid-parser.core-BVBt0Hmw.js} +3 -3
- package/console-app/assets/{mermaid.core-CShTFU_y.js → mermaid.core-BMrHbMBm.js} +4 -4
- package/console-app/assets/{mindmap-definition-YA3MSWOX-Dozk2qNE.js → mindmap-definition-YA3MSWOX-Dhdn4hmh.js} +1 -1
- package/console-app/assets/{packet-AYTQ26CC-CQ1T9gCW.js → packet-AYTQ26CC-CleVGuC-.js} +1 -1
- package/console-app/assets/{pegDiagram-XKGWAZYB-CAKNYwkF.js → pegDiagram-XKGWAZYB-NDJ5URs9.js} +1 -1
- package/console-app/assets/{pie-WAS4IAKB-XBhMekP0.js → pie-WAS4IAKB-DCgq6z8V.js} +1 -1
- package/console-app/assets/{pieDiagram-E7YTZNPT-CQH9g9Im.js → pieDiagram-E7YTZNPT-BPo__c-e.js} +1 -1
- package/console-app/assets/{quadrantDiagram-AXDQQJYC-BYry9NEL.js → quadrantDiagram-AXDQQJYC-C6AlGJWn.js} +1 -1
- package/console-app/assets/{radar-RG4KPBEZ-BHk3GM2h.js → radar-RG4KPBEZ-iKELPo6S.js} +1 -1
- package/console-app/assets/{railroad-74A4TZTK-B-GsJq3u.js → railroad-74A4TZTK-D7o-4Y2z.js} +1 -1
- package/console-app/assets/railroad-abnf-HS5TGJTU-SJ0ZNV52.js +1 -0
- package/console-app/assets/railroad-ebnf-LZEXJU2U-CoxgM6s2.js +1 -0
- package/console-app/assets/railroad-peg-WCYAUIDC-Dt1jr0ie.js +1 -0
- package/console-app/assets/{railroadDiagram-O6MQD6OU-CAIv58C8.js → railroadDiagram-O6MQD6OU-Bxb416Ip.js} +1 -1
- package/console-app/assets/{requirementDiagram-BXWQKSXE-DJ3f5870.js → requirementDiagram-BXWQKSXE-wpPz84xe.js} +1 -1
- package/console-app/assets/{sankeyDiagram-P5KCCOFB-YrQnQsb7.js → sankeyDiagram-P5KCCOFB-DCtqI-U8.js} +1 -1
- package/console-app/assets/{sequenceDiagram-WJ2MYXX4-B-tyzF2R.js → sequenceDiagram-WJ2MYXX4-B9qErfQU.js} +1 -1
- package/console-app/assets/{src-DV6LWVlf.js → src-CrUEtLgb.js} +1 -1
- package/console-app/assets/{stateDiagram-D77RDMKH-vMePRkxV.js → stateDiagram-D77RDMKH-DVQO4UZZ.js} +1 -1
- package/console-app/assets/stateDiagram-v2-MP3YSRHH-qOQfgOOB.js +1 -0
- package/console-app/assets/{swimlanes-42K2YHIH-CqQsTH0R.js → swimlanes-42K2YHIH-CfBqlUg-.js} +1 -1
- package/console-app/assets/swimlanesDiagram-VR7AAH4N-D6fTU5Cq.js +8 -0
- package/console-app/assets/{timeline-definition-24CTP7MA-D3OGq7c8.js → timeline-definition-24CTP7MA-CPFn_B-h.js} +1 -1
- package/console-app/assets/{treeView-Q6P3EWNA-DZA143I6.js → treeView-Q6P3EWNA-CVWAH-EI.js} +1 -1
- package/console-app/assets/{treemap-WGGIJYW6-N5Dr2J8o.js → treemap-WGGIJYW6-DEBgfaYx.js} +1 -1
- package/console-app/assets/{vennDiagram-4TSXK5OY-CjQrrdPE.js → vennDiagram-4TSXK5OY-BA4GGrwY.js} +1 -1
- package/console-app/assets/{wardley-WFR3VGLG-CWrUebhA.js → wardley-WFR3VGLG-TajUo-aF.js} +1 -1
- package/console-app/assets/{wardleyDiagram-VM6X3IG4-Bd-nerY3.js → wardleyDiagram-VM6X3IG4-Bd1y8DdK.js} +1 -1
- package/console-app/assets/{xychartDiagram-S5SC5T6Z-CK3-JaeF.js → xychartDiagram-S5SC5T6Z-CWQmkOFg.js} +1 -1
- package/console-app/index.html +1 -1
- package/dist/.pikku/addon/index.d.ts +1 -1
- package/dist/.pikku/addon/index.js +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
- package/dist/.pikku/agent/index.d.ts +1 -1
- package/dist/.pikku/agent/index.js +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
- package/dist/.pikku/analytics/index.d.ts +1 -1
- package/dist/.pikku/analytics/index.js +1 -1
- package/dist/.pikku/analytics/pikku-analytics-types.gen.d.ts +1 -1
- package/dist/.pikku/analytics/pikku-analytics-types.gen.js +1 -1
- package/dist/.pikku/auth/index.d.ts +1 -1
- package/dist/.pikku/auth/index.js +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
- package/dist/.pikku/channel/index.d.ts +1 -1
- package/dist/.pikku/channel/index.js +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/index.d.ts +1 -1
- package/dist/.pikku/cli/index.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +20 -2
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/error/index.d.ts +1 -1
- package/dist/.pikku/error/index.js +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
- package/dist/.pikku/function/index.d.ts +1 -1
- package/dist/.pikku/function/index.js +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +14 -14
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/gateway/index.d.ts +1 -1
- package/dist/.pikku/gateway/index.js +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
- package/dist/.pikku/http/index.d.ts +1 -1
- package/dist/.pikku/http/index.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/index.d.ts +1 -1
- package/dist/.pikku/mcp/index.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/middleware/index.d.ts +1 -1
- package/dist/.pikku/middleware/index.js +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/queue/index.d.ts +1 -1
- package/dist/.pikku/queue/index.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/scenarios/index.d.ts +1 -1
- package/dist/.pikku/scenarios/index.js +1 -1
- package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
- package/dist/.pikku/scheduler/index.d.ts +1 -1
- package/dist/.pikku/scheduler/index.js +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +1 -1
- package/dist/.pikku/schemas/schemas/KnowledgePlanProgressOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/KnowledgePlanSetOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/KnowledgePlanShowOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuNewAddonInput.schema.json +1 -1
- package/dist/.pikku/scopes/index.d.ts +1 -1
- package/dist/.pikku/scopes/index.js +1 -1
- package/dist/.pikku/scopes/pikku-flags.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/index.d.ts +1 -1
- package/dist/.pikku/secrets/index.js +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/setup/index.d.ts +1 -1
- package/dist/.pikku/setup/index.js +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
- package/dist/.pikku/trigger/index.d.ts +1 -1
- package/dist/.pikku/trigger/index.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/index.d.ts +1 -1
- package/dist/.pikku/variables/index.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/index.d.ts +1 -1
- package/dist/.pikku/workflow/index.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +20 -2
- package/dist/src/functions/commands/dev-credentials.d.ts +18 -0
- package/dist/src/functions/commands/dev-credentials.js +59 -0
- package/dist/src/functions/commands/dev.js +11 -2
- package/dist/src/functions/commands/install-addon.d.ts +45 -0
- package/dist/src/functions/commands/install-addon.js +225 -0
- package/dist/src/functions/commands/knowledge-plan.d.ts +9 -0
- package/dist/src/functions/commands/knowledge-plan.js +13 -3
- package/dist/src/functions/commands/new-addon.d.ts +47 -1
- package/dist/src/functions/commands/new-addon.js +284 -106
- package/dist/src/functions/db/better-auth-schema.d.ts +1 -0
- package/dist/src/functions/db/better-auth-schema.js +75 -2
- package/dist/src/functions/db/ensure-db-types.d.ts +12 -0
- package/dist/src/functions/db/ensure-db-types.js +29 -0
- package/dist/src/functions/knowledge/console-url.d.ts +13 -0
- package/dist/src/functions/knowledge/console-url.js +18 -0
- package/dist/src/functions/knowledge/render.d.ts +7 -3
- package/dist/src/functions/knowledge/render.js +10 -3
- package/dist/src/functions/knowledge/schemas.d.ts +3 -0
- package/dist/src/functions/knowledge/schemas.js +4 -3
- package/dist/src/functions/wirings/rpc/serialize-react-query-hooks.js +6 -0
- package/dist/src/functions/workflows/all.workflow.js +7 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/surface.json +31 -1
- package/console-app/assets/architecture-7GRP2DOG-C3gcY2os.js +0 -1
- package/console-app/assets/channel-CGrTWPVh.js +0 -1
- package/console-app/assets/chunk-XXDRQBXY-DLdC0jYP.js +0 -1
- package/console-app/assets/classDiagram-ZZMXUADV-yt7BCzQS.js +0 -1
- package/console-app/assets/classDiagram-v2-VYDZK3BY-yt7BCzQS.js +0 -1
- package/console-app/assets/eventmodeling-NTZA5JFV-BbwPCOs2.js +0 -1
- package/console-app/assets/flowDiagram-HODETNUW-B-xUNJdv.js +0 -1
- package/console-app/assets/railroad-abnf-HS5TGJTU-Dqco3Udm.js +0 -1
- package/console-app/assets/railroad-ebnf-LZEXJU2U-DZqyOzxq.js +0 -1
- package/console-app/assets/railroad-peg-WCYAUIDC-DtfiBwRd.js +0 -1
- package/console-app/assets/stateDiagram-v2-MP3YSRHH-Cojsq9SW.js +0 -1
- package/console-app/assets/swimlanesDiagram-VR7AAH4N-0DUuvodn.js +0 -8
|
@@ -5,7 +5,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
5
5
|
import { findInstallRoot } from './update.js';
|
|
6
6
|
import { createEmptyManifest, saveManifest, } from '../../utils/contract-versions.js';
|
|
7
7
|
import { pikkuSessionlessFunc } from '#pikku/function';
|
|
8
|
-
import { parseOpenAPISpec, computeContractHash, generateAddonFromOpenAPI, loadAuthConfig, } from '@pikku/openapi-parser';
|
|
8
|
+
import { parseOpenAPISpec, computeContractHash, detectLoginOperation, filterOperations, generateAddonFromOpenAPI, loadAuthConfig, specCoverageWarning, } from '@pikku/openapi-parser';
|
|
9
|
+
import { installAddonIntoApp } from './install-addon.js';
|
|
9
10
|
/**
|
|
10
11
|
* Pick the protocol the generated test app uses to depend on its parent addon.
|
|
11
12
|
*
|
|
@@ -128,6 +129,27 @@ const literal = (value) => JSON.stringify(value);
|
|
|
128
129
|
const inTemplate = (value) => value.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
129
130
|
/** The same prose on a `//` comment line, where a newline ends the comment. */
|
|
130
131
|
const inComment = (value) => value.replace(/\s+/g, ' ').trim();
|
|
132
|
+
const CREDENTIAL_SHAPES = {
|
|
133
|
+
apikey: '{ apiKey: string }',
|
|
134
|
+
bearer: '{ token: string }',
|
|
135
|
+
basic: '{ username: string; password: string }',
|
|
136
|
+
};
|
|
137
|
+
const CREDENTIAL_FIELDS = { apikey: 'apiKey', bearer: 'token' };
|
|
138
|
+
const ICON_COLOURS = [
|
|
139
|
+
'#2563eb',
|
|
140
|
+
'#7c3aed',
|
|
141
|
+
'#db2777',
|
|
142
|
+
'#ea580c',
|
|
143
|
+
'#16a34a',
|
|
144
|
+
'#0891b2',
|
|
145
|
+
];
|
|
146
|
+
function placeholderIcon(displayName) {
|
|
147
|
+
const letter = (displayName.match(/[A-Za-z0-9]/)?.[0] ?? '?').toUpperCase();
|
|
148
|
+
const colour = ICON_COLOURS[[...displayName].reduce((sum, c) => sum + c.charCodeAt(0), 0) %
|
|
149
|
+
ICON_COLOURS.length];
|
|
150
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="${colour}"/><text x="32" y="43" font-family="system-ui, sans-serif" font-size="30" font-weight="600" fill="#fff" text-anchor="middle">${letter}</text></svg>
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
131
153
|
export function getAddonFiles(vars, flags) {
|
|
132
154
|
const { name, camelName, pascalName, screamingName, displayName, description, category, } = vars;
|
|
133
155
|
const files = {};
|
|
@@ -183,14 +205,15 @@ export function getAddonFiles(vars, flags) {
|
|
|
183
205
|
tsconfig: './tsconfig.json',
|
|
184
206
|
srcDirectories: ['src', 'types'],
|
|
185
207
|
outDir: './.pikku',
|
|
186
|
-
addon:
|
|
187
|
-
node: {
|
|
208
|
+
addon: {
|
|
188
209
|
displayName,
|
|
189
210
|
description,
|
|
190
211
|
categories: [category],
|
|
191
212
|
icon: `./${name}.svg`,
|
|
192
213
|
},
|
|
214
|
+
forceRequiredServices: [camelName],
|
|
193
215
|
}, null, 2);
|
|
216
|
+
files[`${name}.svg`] = placeholderIcon(displayName);
|
|
194
217
|
// tsconfig.json
|
|
195
218
|
files['tsconfig.json'] = JSON.stringify({
|
|
196
219
|
compilerOptions: {
|
|
@@ -222,7 +245,7 @@ ${description}
|
|
|
222
245
|
|
|
223
246
|
## Setup
|
|
224
247
|
|
|
225
|
-
1.
|
|
248
|
+
1. Replace the placeholder icon at \`${name}.svg\`
|
|
226
249
|
2. Update secret schema with required fields
|
|
227
250
|
3. Implement API service methods
|
|
228
251
|
4. Create function files for each operation
|
|
@@ -234,11 +257,7 @@ ${description}
|
|
|
234
257
|
// export { ${camelName}Operation } from './functions/operation.function.js'
|
|
235
258
|
`;
|
|
236
259
|
// src/services.ts
|
|
237
|
-
|
|
238
|
-
// Delegated login: the credential is the upstream token captured at
|
|
239
|
-
// sign-in; an expired token is the re-auth signal (UnauthorizedError).
|
|
240
|
-
files['src/services.ts'] =
|
|
241
|
-
`import { UnauthorizedError } from '@pikku/core/errors'
|
|
260
|
+
const wireCredential = (shape, check, missing, extra = '') => `import { ${missing.startsWith('new MissingCredentialError') ? 'MissingCredentialError' : 'CredentialRejectedError'} } from '@pikku/core/errors'
|
|
242
261
|
import { ${pascalName}Service } from './${name}-api.service.js'
|
|
243
262
|
import { pikkuAddonWireServices } from '#pikku/addon/setup'
|
|
244
263
|
|
|
@@ -247,67 +266,36 @@ export const createWireServices = pikkuAddonWireServices(
|
|
|
247
266
|
if (!wire.getCredential) {
|
|
248
267
|
throw new Error('Credential resolution is not available in this runtime')
|
|
249
268
|
}
|
|
250
|
-
const cred = await wire.getCredential
|
|
251
|
-
if (
|
|
252
|
-
throw
|
|
253
|
-
}
|
|
254
|
-
if (cred.expiresAt && cred.expiresAt * 1000 < Date.now()) {
|
|
255
|
-
throw new UnauthorizedError(${literal(`${displayName} session expired — sign in again`)})
|
|
256
|
-
}
|
|
269
|
+
const cred = await wire.getCredential<${shape}>('${camelName}')
|
|
270
|
+
if (!${check}) {
|
|
271
|
+
throw ${missing}
|
|
272
|
+
}${extra}
|
|
257
273
|
const ${camelName} = new ${pascalName}Service(cred, variables)
|
|
258
274
|
|
|
259
275
|
return { ${camelName} }
|
|
260
276
|
}
|
|
261
277
|
)
|
|
262
278
|
`;
|
|
279
|
+
if (flags.delegated) {
|
|
280
|
+
// Delegated login: the credential is the upstream token captured at
|
|
281
|
+
// sign-in, so a missing or expired one means signing in again.
|
|
282
|
+
files['src/services.ts'] = wireCredential('{ token: string; expiresAt?: number }', 'cred?.token', `new CredentialRejectedError('${camelName}', 'sign-in', ${literal(`No ${displayName} session — sign in again`)})`, `
|
|
283
|
+
if (cred.expiresAt && cred.expiresAt * 1000 < Date.now()) {
|
|
284
|
+
throw new CredentialRejectedError('${camelName}', 'sign-in', ${literal(`${displayName} session expired — sign in again`)})
|
|
285
|
+
}`);
|
|
263
286
|
}
|
|
264
287
|
else if (flags.credential && flags.credential !== 'oauth2') {
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
export const createWireServices = pikkuAddonWireServices(
|
|
272
|
-
async ({ variables }, wire) => {
|
|
273
|
-
if (!wire.getCredential) {
|
|
274
|
-
throw new Error('Credential resolution is not available in this runtime')
|
|
275
|
-
}
|
|
276
|
-
const cred = await wire.getCredential<{ ${credField}: string }>('${camelName}')
|
|
277
|
-
if (!cred?.${credField}) {
|
|
278
|
-
throw new Error('Missing ${camelName} credential')
|
|
279
|
-
}
|
|
280
|
-
const ${camelName} = new ${pascalName}Service(cred, variables)
|
|
281
|
-
|
|
282
|
-
return { ${camelName} }
|
|
283
|
-
}
|
|
284
|
-
)
|
|
285
|
-
`;
|
|
288
|
+
const shape = CREDENTIAL_SHAPES[flags.credential];
|
|
289
|
+
const field = flags.credential === 'basic'
|
|
290
|
+
? 'username'
|
|
291
|
+
: CREDENTIAL_FIELDS[flags.credential];
|
|
292
|
+
files['src/services.ts'] = wireCredential(shape, `cred?.${field}`, `new MissingCredentialError('${camelName}', 'apikey')`);
|
|
286
293
|
}
|
|
287
294
|
else if (flags.oauth || flags.credential === 'oauth2') {
|
|
288
295
|
// The OAuth2 access token is owned and refreshed by the platform credential
|
|
289
296
|
// service (better-auth). Resolve a ready token per-request via the wire and
|
|
290
297
|
// hand it to the service — the addon does not do its own token exchange.
|
|
291
|
-
files['src/services.ts'] =
|
|
292
|
-
`import { UnauthorizedError } from '@pikku/core/errors'
|
|
293
|
-
import { ${pascalName}Service } from './${name}-api.service.js'
|
|
294
|
-
import { pikkuAddonWireServices } from '#pikku/addon/setup'
|
|
295
|
-
|
|
296
|
-
export const createWireServices = pikkuAddonWireServices(
|
|
297
|
-
async ({ variables }, wire) => {
|
|
298
|
-
if (!wire.getCredential) {
|
|
299
|
-
throw new Error('Credential resolution is not available in this runtime')
|
|
300
|
-
}
|
|
301
|
-
const cred = await wire.getCredential<{ accessToken: string }>('${camelName}')
|
|
302
|
-
if (!cred?.accessToken) {
|
|
303
|
-
throw new UnauthorizedError(${literal(`No ${displayName} connection — connect ${displayName} first`)})
|
|
304
|
-
}
|
|
305
|
-
const ${camelName} = new ${pascalName}Service(cred, variables)
|
|
306
|
-
|
|
307
|
-
return { ${camelName} }
|
|
308
|
-
}
|
|
309
|
-
)
|
|
310
|
-
`;
|
|
298
|
+
files['src/services.ts'] = wireCredential('{ accessToken: string }', 'cred?.accessToken', `new MissingCredentialError('${camelName}', 'oauth2')`);
|
|
311
299
|
}
|
|
312
300
|
else if (flags.secret) {
|
|
313
301
|
files['src/services.ts'] =
|
|
@@ -317,24 +305,25 @@ import { pikkuAddonServices } from '#pikku/addon/setup'
|
|
|
317
305
|
|
|
318
306
|
export const createSingletonServices = pikkuAddonServices(async (
|
|
319
307
|
config,
|
|
320
|
-
{ secrets }
|
|
308
|
+
{ secrets, variables }
|
|
321
309
|
) => {
|
|
322
310
|
const creds = await secrets.getSecret<${pascalName}Secrets>('${screamingName}_CREDENTIALS')
|
|
323
|
-
const ${camelName} = new ${pascalName}Service(creds.reveal())
|
|
311
|
+
const ${camelName} = new ${pascalName}Service(creds.reveal(), variables)
|
|
324
312
|
|
|
325
313
|
return { ${camelName} }
|
|
326
314
|
})
|
|
327
315
|
`;
|
|
328
316
|
}
|
|
329
317
|
else {
|
|
330
|
-
// The plain service takes no constructor argument — the secret, credential
|
|
331
|
-
// and oauth variants are the ones handed something to authenticate with.
|
|
332
318
|
files['src/services.ts'] =
|
|
333
319
|
`import { ${pascalName}Service } from './${name}-api.service.js'
|
|
334
320
|
import { pikkuAddonServices } from '#pikku/addon/setup'
|
|
335
321
|
|
|
336
|
-
export const createSingletonServices = pikkuAddonServices(async (
|
|
337
|
-
|
|
322
|
+
export const createSingletonServices = pikkuAddonServices(async (
|
|
323
|
+
config,
|
|
324
|
+
{ variables }
|
|
325
|
+
) => {
|
|
326
|
+
const ${camelName} = new ${pascalName}Service(variables)
|
|
338
327
|
|
|
339
328
|
return { ${camelName} }
|
|
340
329
|
})
|
|
@@ -342,16 +331,16 @@ export const createSingletonServices = pikkuAddonServices(async () => {
|
|
|
342
331
|
}
|
|
343
332
|
// src/{name}-api.service.ts
|
|
344
333
|
if (flags.credential && flags.credential !== 'oauth2') {
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
: `this.creds.
|
|
350
|
-
|
|
351
|
-
? `'Authorization': ${authHeader},`
|
|
352
|
-
: `'Authorization': \`Bearer \${this.creds.apiKey}\`,`;
|
|
334
|
+
const credType = CREDENTIAL_SHAPES[flags.credential];
|
|
335
|
+
const authLine = {
|
|
336
|
+
bearer: `'Authorization': \`Bearer \${this.creds.token}\`,`,
|
|
337
|
+
apikey: `'Authorization': \`Bearer \${this.creds.apiKey}\`,`,
|
|
338
|
+
basic: `'Authorization': \`Basic \${btoa(\`\${this.creds.username}:\${this.creds.password}\`)}\`,`,
|
|
339
|
+
}[flags.credential];
|
|
353
340
|
files[`src/${name}-api.service.ts`] =
|
|
354
|
-
`
|
|
341
|
+
`import type { TypedVariablesService } from '#pikku/addon/variables/pikku-variables.gen.js'
|
|
342
|
+
|
|
343
|
+
const BASE_URL = 'https://api.example.com/v1'
|
|
355
344
|
|
|
356
345
|
export interface RequestOptions {
|
|
357
346
|
body?: unknown
|
|
@@ -359,7 +348,10 @@ export interface RequestOptions {
|
|
|
359
348
|
}
|
|
360
349
|
|
|
361
350
|
export class ${pascalName}Service {
|
|
362
|
-
constructor(
|
|
351
|
+
constructor(
|
|
352
|
+
private creds: ${credType},
|
|
353
|
+
_variables?: TypedVariablesService
|
|
354
|
+
) {}
|
|
363
355
|
|
|
364
356
|
async request<T>(
|
|
365
357
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
|
@@ -451,6 +443,7 @@ export class ${pascalName}Service {
|
|
|
451
443
|
else if (flags.secret) {
|
|
452
444
|
files[`src/${name}-api.service.ts`] =
|
|
453
445
|
`import type { ${pascalName}Secrets } from './${name}.secret.js'
|
|
446
|
+
import type { TypedVariablesService } from '#pikku/addon/variables/pikku-variables.gen.js'
|
|
454
447
|
|
|
455
448
|
const BASE_URL = 'https://api.example.com/v1'
|
|
456
449
|
|
|
@@ -460,7 +453,10 @@ export interface RequestOptions {
|
|
|
460
453
|
}
|
|
461
454
|
|
|
462
455
|
export class ${pascalName}Service {
|
|
463
|
-
constructor(
|
|
456
|
+
constructor(
|
|
457
|
+
private creds: ${pascalName}Secrets,
|
|
458
|
+
_variables?: TypedVariablesService
|
|
459
|
+
) {}
|
|
464
460
|
|
|
465
461
|
async request<T>(
|
|
466
462
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
|
@@ -498,7 +494,9 @@ export class ${pascalName}Service {
|
|
|
498
494
|
}
|
|
499
495
|
else {
|
|
500
496
|
files[`src/${name}-api.service.ts`] =
|
|
501
|
-
`
|
|
497
|
+
`import type { TypedVariablesService } from '#pikku/addon/variables/pikku-variables.gen.js'
|
|
498
|
+
|
|
499
|
+
const BASE_URL = 'https://api.example.com/v1'
|
|
502
500
|
|
|
503
501
|
export interface RequestOptions {
|
|
504
502
|
body?: unknown
|
|
@@ -506,6 +504,8 @@ export interface RequestOptions {
|
|
|
506
504
|
}
|
|
507
505
|
|
|
508
506
|
export class ${pascalName}Service {
|
|
507
|
+
constructor(_variables?: TypedVariablesService) {}
|
|
508
|
+
|
|
509
509
|
async request<T>(
|
|
510
510
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
|
511
511
|
endpoint: string,
|
|
@@ -580,6 +580,24 @@ export const ${camelName}CredentialSchema = z.object({
|
|
|
580
580
|
apiKey: z.string().describe(${literal(`${displayName} API key`)}),
|
|
581
581
|
})
|
|
582
582
|
|
|
583
|
+
defineCredential({
|
|
584
|
+
name: '${camelName}',
|
|
585
|
+
displayName: ${literal(displayName)},
|
|
586
|
+
description: ${literal(description)},
|
|
587
|
+
type: 'wire',
|
|
588
|
+
schema: ${camelName}CredentialSchema,
|
|
589
|
+
})
|
|
590
|
+
`;
|
|
591
|
+
}
|
|
592
|
+
else if (flags.credential === 'basic') {
|
|
593
|
+
files[`src/${name}.credential.ts`] = `import { z } from 'zod'
|
|
594
|
+
import { defineCredential } from '#pikku/addon/auth'
|
|
595
|
+
|
|
596
|
+
export const ${camelName}CredentialSchema = z.object({
|
|
597
|
+
username: z.string().describe(${literal(`${displayName} username`)}),
|
|
598
|
+
password: z.string().describe(${literal(`${displayName} password`)}),
|
|
599
|
+
})
|
|
600
|
+
|
|
583
601
|
defineCredential({
|
|
584
602
|
name: '${camelName}',
|
|
585
603
|
displayName: ${literal(displayName)},
|
|
@@ -926,8 +944,95 @@ export const buildGeneratedAddon = (addonDir, logger, run = spawnAddonBuildStep)
|
|
|
926
944
|
}
|
|
927
945
|
return true;
|
|
928
946
|
};
|
|
947
|
+
/**
|
|
948
|
+
* `string[]` options split on commas, which would cut a header value like
|
|
949
|
+
* `Accept: a, b` in two; a piece that does not start a new `Name:` rejoins the
|
|
950
|
+
* previous one.
|
|
951
|
+
*/
|
|
952
|
+
export function parseHeaderOptions(raw = []) {
|
|
953
|
+
const joined = [];
|
|
954
|
+
for (const piece of raw) {
|
|
955
|
+
if (/^[A-Za-z0-9!#$%&'*+.^_`|~-]+\s*:/.test(piece) || joined.length === 0) {
|
|
956
|
+
joined.push(piece);
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
joined[joined.length - 1] += `, ${piece}`;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const headers = {};
|
|
963
|
+
for (const header of joined) {
|
|
964
|
+
const at = header.indexOf(':');
|
|
965
|
+
if (at <= 0) {
|
|
966
|
+
throw new Error(`--openapi-header "${header}" is not "Name: value"`);
|
|
967
|
+
}
|
|
968
|
+
headers[header.slice(0, at).trim()] = header.slice(at + 1).trim();
|
|
969
|
+
}
|
|
970
|
+
return headers;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* How the addon authenticates, from the flags first and the spec second.
|
|
974
|
+
* Per-user is the default for anything the spec can authenticate: each user
|
|
975
|
+
* acts upstream as themselves. `--auth shared` puts one secret behind every
|
|
976
|
+
* user; `--auth none` is for public APIs.
|
|
977
|
+
*/
|
|
978
|
+
export function resolveAddonAuth(spec, flags) {
|
|
979
|
+
if (flags.auth && !['user', 'shared', 'none'].includes(flags.auth)) {
|
|
980
|
+
throw new Error(`--auth must be user, shared or none (got "${flags.auth}")`);
|
|
981
|
+
}
|
|
982
|
+
if (flags.authConfig?.delegated) {
|
|
983
|
+
return {
|
|
984
|
+
mode: 'delegated',
|
|
985
|
+
credential: 'bearer',
|
|
986
|
+
secret: false,
|
|
987
|
+
oauth: false,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
if (flags.auth === 'none') {
|
|
991
|
+
return { mode: 'none', secret: false, oauth: false };
|
|
992
|
+
}
|
|
993
|
+
if (flags.auth === 'shared' ||
|
|
994
|
+
(flags.secret && !flags.credential && !flags.oauth)) {
|
|
995
|
+
return { mode: 'shared', secret: true, oauth: false };
|
|
996
|
+
}
|
|
997
|
+
const explicit = flags.credential ?? (flags.oauth ? 'oauth2' : undefined);
|
|
998
|
+
const fromSpec = flags.authConfig?.headerName
|
|
999
|
+
? 'apikey'
|
|
1000
|
+
: {
|
|
1001
|
+
oauth2: 'oauth2',
|
|
1002
|
+
apiKey: 'apikey',
|
|
1003
|
+
bearer: 'bearer',
|
|
1004
|
+
basic: 'basic',
|
|
1005
|
+
none: undefined,
|
|
1006
|
+
}[spec.authType];
|
|
1007
|
+
const credential = (explicit ?? fromSpec);
|
|
1008
|
+
if (!credential) {
|
|
1009
|
+
throw new Error(`${spec.info.title} declares no security scheme, so the addon cannot tell how to authenticate. ` +
|
|
1010
|
+
'Pass --auth none for a public API, --credential apikey|bearer|basic|oauth2 for a per-user key, ' +
|
|
1011
|
+
'--auth shared for one key behind every user, or --auth-config <file> for a custom header or a delegated login (see pikku-build references/openapi.md).');
|
|
1012
|
+
}
|
|
1013
|
+
if (!['apikey', 'bearer', 'basic', 'oauth2'].includes(credential)) {
|
|
1014
|
+
throw new Error(`Invalid credential type "${credential}": must be one of apikey, bearer, basic, oauth2`);
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
mode: credential === 'oauth2' ? 'oauth2' : 'connect',
|
|
1018
|
+
credential,
|
|
1019
|
+
secret: false,
|
|
1020
|
+
oauth: credential === 'oauth2',
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
/** The project `pikku new addon` runs in, when it is an app rather than an addon. */
|
|
1024
|
+
function findAppProject(config) {
|
|
1025
|
+
const root = config?.rootDir;
|
|
1026
|
+
if (!root || config.addon || !existsSync(join(root, 'pikku.config.json'))) {
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
const src = config.srcDirectories?.[0];
|
|
1030
|
+
if (!src)
|
|
1031
|
+
return undefined;
|
|
1032
|
+
return { root, srcDir: join(root, src) };
|
|
1033
|
+
}
|
|
929
1034
|
export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
930
|
-
func: async ({ logger, config }, { name, displayName, description, category = 'General', dir, secret = false, variable = false, oauth = false, credential, test = true, openapi, authConfig, mcp = false, camelCase = false, build = true, }) => {
|
|
1035
|
+
func: async ({ logger, config }, { name, displayName, description, category = 'General', dir, secret = false, variable = false, oauth = false, credential, test = true, openapi, openapiHeader, tags, include, exclude, auth, install, authConfig, mcp = false, camelCase = false, build = true, }) => {
|
|
931
1036
|
name = sanitizeAddonName(name);
|
|
932
1037
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
933
1038
|
logger.error(`Invalid addon name "${name}": must start with a lowercase letter and contain only lowercase alphanumerics, hyphens, and underscores`);
|
|
@@ -936,8 +1041,19 @@ export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
|
936
1041
|
const pascalName = toPascalCase(name);
|
|
937
1042
|
const resolvedDisplayName = displayName || pascalName;
|
|
938
1043
|
const resolvedDescription = description || `${resolvedDisplayName} integration for Pikku`;
|
|
1044
|
+
const app = findAppProject(config);
|
|
1045
|
+
const installing = Boolean(openapi) && (install ?? Boolean(app));
|
|
1046
|
+
if (installing && !app) {
|
|
1047
|
+
logger.error('--install needs to run inside a pikku app (a pikku.config.json that is not an addon)');
|
|
1048
|
+
process.exit(1);
|
|
1049
|
+
}
|
|
939
1050
|
// Resolve target directory
|
|
940
|
-
const
|
|
1051
|
+
const workspacePackages = app ? join(app.root, 'packages') : undefined;
|
|
1052
|
+
const baseDir = dir ||
|
|
1053
|
+
config.scaffold?.addonDir ||
|
|
1054
|
+
(installing && workspacePackages && existsSync(workspacePackages)
|
|
1055
|
+
? workspacePackages
|
|
1056
|
+
: process.cwd());
|
|
941
1057
|
// Folder mirrors the package name (@pikku/addon-<name>) so a packages/
|
|
942
1058
|
// listing reads as packages/addon-<name>, distinct from app workspaces.
|
|
943
1059
|
const addonDir = join(baseDir, `addon-${name}`);
|
|
@@ -955,53 +1071,88 @@ export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
|
955
1071
|
category,
|
|
956
1072
|
addonDepProtocol: resolveAddonDepProtocol(baseDir),
|
|
957
1073
|
};
|
|
958
|
-
// Load the auth config (custom auth header / delegated login overrides)
|
|
959
1074
|
let loadedAuthConfig;
|
|
960
1075
|
if (authConfig) {
|
|
961
1076
|
loadedAuthConfig = await loadAuthConfig(authConfig);
|
|
962
|
-
if (loadedAuthConfig.delegated) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1077
|
+
if (loadedAuthConfig.delegated && credential && credential !== 'bearer') {
|
|
1078
|
+
logger.error(`--auth-config with delegated login stores the upstream token per user, so it cannot be combined with --credential ${credential}`);
|
|
1079
|
+
process.exit(1);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
let spec;
|
|
1083
|
+
let resolved;
|
|
1084
|
+
try {
|
|
1085
|
+
if (openapi) {
|
|
1086
|
+
spec = await parseOpenAPISpec(openapi, {
|
|
1087
|
+
headers: parseHeaderOptions(openapiHeader),
|
|
1088
|
+
});
|
|
1089
|
+
const total = spec.operations.length;
|
|
1090
|
+
spec = filterOperations(spec, { tags, include, exclude });
|
|
1091
|
+
if (spec.operations.length === 0) {
|
|
1092
|
+
throw new Error(`No operations left out of ${total} after --tags/--include/--exclude`);
|
|
1093
|
+
}
|
|
1094
|
+
const warning = specCoverageWarning(spec);
|
|
1095
|
+
if (warning) {
|
|
1096
|
+
logger.warn(`\n⚠️ ${warning}\n`);
|
|
968
1097
|
}
|
|
969
|
-
|
|
1098
|
+
resolved = resolveAddonAuth(spec, {
|
|
1099
|
+
auth,
|
|
1100
|
+
credential,
|
|
1101
|
+
oauth,
|
|
1102
|
+
secret,
|
|
1103
|
+
authConfig: loadedAuthConfig,
|
|
1104
|
+
});
|
|
1105
|
+
const login = detectLoginOperation(spec);
|
|
1106
|
+
if (login && resolved.mode !== 'delegated') {
|
|
1107
|
+
logger.info(`${spec.info.title} has a login route (${login.method.toUpperCase()} ${login.path}). To let users sign in to the app with their ${resolvedDisplayName} account, pass --auth-config with a "delegated" block (see pikku-build references/openapi.md).`);
|
|
1108
|
+
}
|
|
1109
|
+
logger.info(`${spec.operations.length} operations, auth: ${resolved.mode}${resolved.credential ? ` (${resolved.credential})` : ''}`);
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
const credentialType = credential;
|
|
1113
|
+
if (credentialType &&
|
|
1114
|
+
!['apikey', 'bearer', 'basic', 'oauth2'].includes(credentialType)) {
|
|
1115
|
+
throw new Error(`Invalid credential type "${credential}": must be one of apikey, bearer, basic, oauth2`);
|
|
1116
|
+
}
|
|
1117
|
+
const effectiveOAuth = oauth || credentialType === 'oauth2';
|
|
1118
|
+
resolved = {
|
|
1119
|
+
mode: effectiveOAuth
|
|
1120
|
+
? 'oauth2'
|
|
1121
|
+
: credentialType
|
|
1122
|
+
? 'connect'
|
|
1123
|
+
: secret
|
|
1124
|
+
? 'shared'
|
|
1125
|
+
: 'none',
|
|
1126
|
+
credential: loadedAuthConfig?.delegated ? 'bearer' : credentialType,
|
|
1127
|
+
secret: (secret || effectiveOAuth) && !credentialType,
|
|
1128
|
+
oauth: effectiveOAuth,
|
|
1129
|
+
};
|
|
1130
|
+
if (loadedAuthConfig?.delegated)
|
|
1131
|
+
resolved.mode = 'delegated';
|
|
970
1132
|
}
|
|
971
1133
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
if (credentialType &&
|
|
975
|
-
!['apikey', 'bearer', 'oauth2'].includes(credentialType)) {
|
|
976
|
-
logger.error(`Invalid credential type "${credential}": must be one of apikey, bearer, oauth2`);
|
|
1134
|
+
catch (error) {
|
|
1135
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
977
1136
|
process.exit(1);
|
|
978
1137
|
}
|
|
979
|
-
// oauth implies secret (unless credential flag is used); credential oauth2 implies oauth
|
|
980
|
-
const effectiveOAuth = oauth || credentialType === 'oauth2';
|
|
981
1138
|
const addonFiles = getAddonFiles(vars, {
|
|
982
|
-
secret:
|
|
1139
|
+
secret: resolved.secret,
|
|
983
1140
|
variable,
|
|
984
|
-
oauth:
|
|
985
|
-
credential:
|
|
986
|
-
delegated:
|
|
1141
|
+
oauth: resolved.oauth,
|
|
1142
|
+
credential: resolved.credential,
|
|
1143
|
+
delegated: resolved.mode === 'delegated',
|
|
987
1144
|
});
|
|
988
|
-
|
|
989
|
-
if (openapi) {
|
|
990
|
-
const spec = await parseOpenAPISpec(openapi);
|
|
1145
|
+
if (spec) {
|
|
991
1146
|
const openapiFiles = generateAddonFromOpenAPI(spec, vars, {
|
|
992
|
-
oauth:
|
|
993
|
-
secret:
|
|
994
|
-
credential:
|
|
1147
|
+
oauth: resolved.oauth,
|
|
1148
|
+
secret: resolved.secret,
|
|
1149
|
+
credential: resolved.credential,
|
|
995
1150
|
mcp,
|
|
996
1151
|
camelCase,
|
|
997
1152
|
authConfig: loadedAuthConfig,
|
|
998
1153
|
});
|
|
999
1154
|
Object.assign(addonFiles, openapiFiles);
|
|
1000
|
-
// Inject openapi metadata into pikku.config.json
|
|
1001
1155
|
const config = JSON.parse(addonFiles['pikku.config.json']);
|
|
1002
|
-
if (typeof config.addon === 'boolean' || !config.addon) {
|
|
1003
|
-
config.addon = {};
|
|
1004
|
-
}
|
|
1005
1156
|
config.addon.openapi = {
|
|
1006
1157
|
version: spec.info.version,
|
|
1007
1158
|
hash: computeContractHash(spec),
|
|
@@ -1024,6 +1175,33 @@ export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
|
1024
1175
|
for (const f of written) {
|
|
1025
1176
|
logger.debug({ message: ` ${f}`, type: 'success' });
|
|
1026
1177
|
}
|
|
1178
|
+
if (installing && app && spec) {
|
|
1179
|
+
const functions = Object.fromEntries(Object.entries(addonFiles)
|
|
1180
|
+
.filter(([path]) => /^src\/functions\/[^/]+\.function\.ts$/.test(path))
|
|
1181
|
+
.map(([path, source]) => [
|
|
1182
|
+
path.slice('src/functions/'.length, -'.function.ts'.length),
|
|
1183
|
+
source,
|
|
1184
|
+
]));
|
|
1185
|
+
const baseUrl = spec.serverUrls.find((url) => /^https?:\/\//.test(url));
|
|
1186
|
+
const { written: installed, notes } = installAddonIntoApp({
|
|
1187
|
+
projectRoot: app.root,
|
|
1188
|
+
srcDir: app.srcDir,
|
|
1189
|
+
name,
|
|
1190
|
+
camelName: vars.camelName,
|
|
1191
|
+
pascalName,
|
|
1192
|
+
screamingName: vars.screamingName,
|
|
1193
|
+
packageName: `@pikku/addon-${name}`,
|
|
1194
|
+
addonDir,
|
|
1195
|
+
inWorkspace: workspaceCovers(app.root, addonDir),
|
|
1196
|
+
mode: resolved.mode,
|
|
1197
|
+
functions,
|
|
1198
|
+
baseUrl: baseUrl?.replace(/\/+$/, ''),
|
|
1199
|
+
});
|
|
1200
|
+
for (const f of installed)
|
|
1201
|
+
logger.info(` updated ${f}`);
|
|
1202
|
+
for (const note of notes)
|
|
1203
|
+
logger.warn(note);
|
|
1204
|
+
}
|
|
1027
1205
|
if (build && !buildGeneratedAddon(addonDir, logger)) {
|
|
1028
1206
|
process.exit(1);
|
|
1029
1207
|
}
|
|
@@ -12,6 +12,7 @@ export interface GetMigrationsResult {
|
|
|
12
12
|
runMigrations: () => Promise<void>;
|
|
13
13
|
compileMigrations: () => Promise<string>;
|
|
14
14
|
}
|
|
15
|
+
export declare function withoutAuthSchemaCheck<DB>(kysely: Kysely<DB>): Kysely<DB>;
|
|
15
16
|
export declare function loadAuthOptions(opts: {
|
|
16
17
|
rootDir: string;
|
|
17
18
|
srcDirectories: string[];
|
|
@@ -106,11 +106,28 @@ function fakeSecretService() {
|
|
|
106
106
|
setSecret: async () => { },
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
+
// Better Auth checks its tables against the database the moment it is built,
|
|
110
|
+
// and logs "Database schema mismatch" when they are missing. Here they are
|
|
111
|
+
// always missing: the database is a scratch one the auth migrations are about
|
|
112
|
+
// to run into. The check reaches the database through `connection()` alone,
|
|
113
|
+
// so parking that call keeps the instance quiet without touching the queries
|
|
114
|
+
// the migrator runs.
|
|
115
|
+
export function withoutAuthSchemaCheck(kysely) {
|
|
116
|
+
return new Proxy(kysely, {
|
|
117
|
+
get: (target, prop) => {
|
|
118
|
+
if (prop === 'connection') {
|
|
119
|
+
return () => ({ execute: () => new Promise(() => { }) });
|
|
120
|
+
}
|
|
121
|
+
const value = Reflect.get(target, prop, target);
|
|
122
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
109
126
|
function schemaServicesStub(kysely, logger) {
|
|
110
127
|
const variables = new LocalVariablesService();
|
|
111
128
|
const secrets = fakeSecretService();
|
|
112
129
|
const base = {
|
|
113
|
-
kysely,
|
|
130
|
+
kysely: withoutAuthSchemaCheck(kysely),
|
|
114
131
|
logger,
|
|
115
132
|
secrets,
|
|
116
133
|
variables,
|
|
@@ -144,6 +161,62 @@ async function loadAuthConfig(opts) {
|
|
|
144
161
|
return undefined;
|
|
145
162
|
return userCreateConfig(new LocalVariablesService());
|
|
146
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Build the auth instance without a plugin's background init being able to kill
|
|
166
|
+
* the process.
|
|
167
|
+
*
|
|
168
|
+
* Reading the schema means reading `options`, which Better Auth assembles while
|
|
169
|
+
* the constructor runs — but from 1.7 a plugin's `init` also starts real work
|
|
170
|
+
* and does not wait for it. The OAuth provider behind `@better-auth/mcp` seeds
|
|
171
|
+
* its `oauthResource` rows this way. Here that work has nowhere to go: the
|
|
172
|
+
* database it is handed is a throwaway whose auth tables do not exist yet, and
|
|
173
|
+
* on the SQLite path the handle is closed as soon as the options have been
|
|
174
|
+
* read. The seed then rejects with nothing awaiting it, and Node's default for
|
|
175
|
+
* an unhandled rejection is to terminate — so `pikku db generate` died on
|
|
176
|
+
* `database is not open`, from a write the schema derivation never wanted, in a
|
|
177
|
+
* project whose only offence was configuring MCP.
|
|
178
|
+
*
|
|
179
|
+
* Rejections are reported rather than swallowed: they say a plugin tried to
|
|
180
|
+
* touch the database while being introspected, which is worth seeing, but never
|
|
181
|
+
* worth failing the command over. Whatever the plugin was doing is irrelevant
|
|
182
|
+
* to the shape of its tables, which is all that is being read.
|
|
183
|
+
*
|
|
184
|
+
* The handler is installed only around the call and only added to whatever the
|
|
185
|
+
* host already has, so a real unhandled rejection anywhere else still behaves
|
|
186
|
+
* exactly as it did.
|
|
187
|
+
*/
|
|
188
|
+
async function withoutPluginInitCrashing(build) {
|
|
189
|
+
const existing = process.listeners('unhandledRejection');
|
|
190
|
+
const swallow = (reason) => {
|
|
191
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
192
|
+
debugInitRejection(message);
|
|
193
|
+
};
|
|
194
|
+
for (const listener of existing) {
|
|
195
|
+
process.off('unhandledRejection', listener);
|
|
196
|
+
}
|
|
197
|
+
process.on('unhandledRejection', swallow);
|
|
198
|
+
try {
|
|
199
|
+
const instance = await build();
|
|
200
|
+
// One turn of the loop, so an init that rejects immediately does so while
|
|
201
|
+
// the guard is still the only listener. A slower one is caught by the
|
|
202
|
+
// `swallow` handler that outlives this only if the host had none of its
|
|
203
|
+
// own — which is why the originals go back on before it comes off.
|
|
204
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
205
|
+
return instance;
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
for (const listener of existing) {
|
|
209
|
+
process.on('unhandledRejection', listener);
|
|
210
|
+
}
|
|
211
|
+
process.off('unhandledRejection', swallow);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function debugInitRejection(message) {
|
|
215
|
+
if (process.env.PIKKU_DEBUG) {
|
|
216
|
+
// eslint-disable-next-line no-console
|
|
217
|
+
console.debug(`[pikku] a Better Auth plugin's init failed while its schema was being read, which does not affect the schema: ${message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
147
220
|
export async function loadAuthOptions(opts) {
|
|
148
221
|
const sourceFile = findAuthSourceFile(opts.rootDir, opts.srcDirectories);
|
|
149
222
|
if (!sourceFile)
|
|
@@ -153,7 +226,7 @@ export async function loadAuthOptions(opts) {
|
|
|
153
226
|
return null;
|
|
154
227
|
const services = schemaServicesStub(opts.kysely, opts.logger);
|
|
155
228
|
services.config = await loadAuthConfig(opts);
|
|
156
|
-
const instance = await factory(services);
|
|
229
|
+
const instance = await withoutPluginInitCrashing(() => factory(services));
|
|
157
230
|
const options = instance.options;
|
|
158
231
|
return options ?? null;
|
|
159
232
|
}
|