@pikku/cli 0.12.126 → 0.12.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/console-app/assets/{abnfDiagram-N423BO3Z-B2suyn6i.js → abnfDiagram-N423BO3Z-BY2LM8wi.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-CWwmvxZF.js +1 -0
  4. package/console-app/assets/{architectureDiagram-T3A2C74G-BQ5Dfvpo.js → architectureDiagram-T3A2C74G-CeqjY3Kw.js} +1 -1
  5. package/console-app/assets/{blockDiagram-VBNYF7ZC-D4pVRDiF.js → blockDiagram-VBNYF7ZC-BW5174xj.js} +1 -1
  6. package/console-app/assets/{c4Diagram-5PPSVZJV-Da04jGpq.js → c4Diagram-5PPSVZJV-B7zXAFL0.js} +1 -1
  7. package/console-app/assets/channel-pOkwZ_Ot.js +1 -0
  8. package/console-app/assets/{chunk-2GRJ4B5K-CwODIPP4.js → chunk-2GRJ4B5K-BfRYzQ4t.js} +1 -1
  9. package/console-app/assets/{chunk-4I5QYGJK-Bep4akjR.js → chunk-4I5QYGJK-DBA7-Nu4.js} +1 -1
  10. package/console-app/assets/{chunk-5RXB4S5H-CGyCRU9L.js → chunk-5RXB4S5H-D03lNzwi.js} +1 -1
  11. package/console-app/assets/{chunk-6Q2QTUOP-D0IK0SMT.js → chunk-6Q2QTUOP-DloGRHJZ.js} +1 -1
  12. package/console-app/assets/{chunk-7Z6QIM7H-D12vS1jX.js → chunk-7Z6QIM7H-DH3uHtl2.js} +1 -1
  13. package/console-app/assets/{chunk-GF5L2VYU-kOLwTzIA.js → chunk-GF5L2VYU-Cu252lza.js} +1 -1
  14. package/console-app/assets/{chunk-I66GZJ75-Cb2nDoa3.js → chunk-I66GZJ75-DH54FcuO.js} +1 -1
  15. package/console-app/assets/{chunk-JQJVKLGR-B3a_ctfQ.js → chunk-JQJVKLGR-CeFuTL-V.js} +1 -1
  16. package/console-app/assets/{chunk-KBJHAD2P-DeP2Lqwp.js → chunk-KBJHAD2P-B-prFo0-.js} +1 -1
  17. package/console-app/assets/{chunk-NSK5VX7P-BO8vlGrO.js → chunk-NSK5VX7P-C10iA4GS.js} +1 -1
  18. package/console-app/assets/{chunk-QR6OTTB3-sX5V0Lok.js → chunk-QR6OTTB3-Cd8apAsA.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-DoCObEhk.js → chunk-RYQCIY6F-rfPUp2Fj.js} +1 -1
  20. package/console-app/assets/{chunk-UBXNYLIW-BndMXU5r.js → chunk-UBXNYLIW-D25wqJBc.js} +1 -1
  21. package/console-app/assets/{chunk-W5SLKNZC--VrZN8EC.js → chunk-W5SLKNZC-CJvxkxuK.js} +1 -1
  22. package/console-app/assets/{chunk-WRU74C26-DdS0mzXO.js → chunk-WRU74C26-B9Wju92P.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-BU_UP6f5.js +1 -0
  24. package/console-app/assets/classDiagram-JCYQIIEL-CLBKUuxj.js +1 -0
  25. package/console-app/assets/classDiagram-v2-OCEON4UE-CLBKUuxj.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-C5EnHkFn.js → cose-bilkent-JH36ORCC-CwA1VCP5.js} +1 -1
  27. package/console-app/assets/{cynefin-VYW2F7L2-9irdkTt4.js → cynefin-VYW2F7L2-C_qsD7eR.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-MW4NZA55-DY19660c.js → cynefinDiagram-MW4NZA55-CkSLreFW.js} +1 -1
  29. package/console-app/assets/{dagre-VZM6K2ZE-BYFZSaSb.js → dagre-VZM6K2ZE-0ssqLoHs.js} +1 -1
  30. package/console-app/assets/{dagre-nXgDYqvf.js → dagre-WqgBlKb7.js} +1 -1
  31. package/console-app/assets/{diagram-7IWD3JNH-oDftiTp1.js → diagram-7IWD3JNH-DmRL3lmL.js} +1 -1
  32. package/console-app/assets/{diagram-B4RE2ZJO-Cbj-pPbH.js → diagram-B4RE2ZJO-Bsr9wG4I.js} +1 -1
  33. package/console-app/assets/{diagram-LBJQPF4R-Ch53oSKw.js → diagram-LBJQPF4R-DdMw1FSY.js} +1 -1
  34. package/console-app/assets/{diagram-Q27KOJAE-DA7K814v.js → diagram-Q27KOJAE-TPx5TB2B.js} +1 -1
  35. package/console-app/assets/{diagram-UB23O5K3-B-_C-ctS.js → diagram-UB23O5K3-e_1zSlZR.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-BXEA7PRR-DsOdsKXy.js → ebnfDiagram-BXEA7PRR-BACajh2b.js} +1 -1
  37. package/console-app/assets/{erDiagram-JOGREHBK-XB1mP5_A.js → erDiagram-JOGREHBK-CojomKqL.js} +1 -1
  38. package/console-app/assets/eventmodeling-45OFAUF4-DkN2KCrT.js +1 -0
  39. package/console-app/assets/flowDiagram-UKHOOZJN-DL0M9lBc.js +1 -0
  40. package/console-app/assets/{ganttDiagram-PKOTCBZU-knk0XSHi.js → ganttDiagram-PKOTCBZU-yoOX4Xj2.js} +1 -1
  41. package/console-app/assets/{gitGraph-TEB2WS4Q-COJnK2tM.js → gitGraph-TEB2WS4Q-BcUY5hIc.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-DS77QQ5N-sF0v605q.js → gitGraphDiagram-DS77QQ5N-BtuIPjuB.js} +1 -1
  43. package/console-app/assets/{index-DMnbsgzs.js → index-CzWW052c.js} +151 -151
  44. package/console-app/assets/{info-DKCQHKI2-Cbatr0w6.js → info-DKCQHKI2-B-iR9PUz.js} +1 -1
  45. package/console-app/assets/{infoDiagram-6WML65LV-93TFPCu2.js → infoDiagram-6WML65LV-CuXKIFH2.js} +1 -1
  46. package/console-app/assets/{ishikawaDiagram-WSZJBQD7-BBR8_Qms.js → ishikawaDiagram-WSZJBQD7-CSueMFD6.js} +1 -1
  47. package/console-app/assets/{journeyDiagram-NVQOT4AX-zG55yDoc.js → journeyDiagram-NVQOT4AX-kvnKStZp.js} +1 -1
  48. package/console-app/assets/{kanban-definition-27J2QSJJ-DonGcEAY.js → kanban-definition-27J2QSJJ-CGGDEz30.js} +1 -1
  49. package/console-app/assets/{line-D1WdVm1S.js → line-mtMM5-xO.js} +1 -1
  50. package/console-app/assets/{linear-D2GQL4Cm.js → linear-CLSje0O_.js} +1 -1
  51. package/console-app/assets/{mermaid-parser.core-b4yKyO56.js → mermaid-parser.core-Cx6yvn3j.js} +3 -3
  52. package/console-app/assets/{mermaid.core-Cwfec1gr.js → mermaid.core-BebGyklS.js} +4 -4
  53. package/console-app/assets/{mindmap-definition-FAOFIHXS-D0G4yKgw.js → mindmap-definition-FAOFIHXS-C2_WUUJ9.js} +1 -1
  54. package/console-app/assets/{packet-7NZHBO7P-DDa4HL2T.js → packet-7NZHBO7P-Dh5yNCA8.js} +1 -1
  55. package/console-app/assets/{pegDiagram-VL7TDLO6-fPdN5VZs.js → pegDiagram-VL7TDLO6-XgUxesi8.js} +1 -1
  56. package/console-app/assets/{pie-RZYD4A2V-DRHURUqo.js → pie-RZYD4A2V-D2x2RAXI.js} +1 -1
  57. package/console-app/assets/{pieDiagram-7S7Q4E2Y-DMYn8ga_.js → pieDiagram-7S7Q4E2Y-Bbor0KbG.js} +1 -1
  58. package/console-app/assets/{quadrantDiagram-CIZ2JOQS-C190OamC.js → quadrantDiagram-CIZ2JOQS-DZmnMHpM.js} +1 -1
  59. package/console-app/assets/{radar-I7S5WNFK-Djvqzz9A.js → radar-I7S5WNFK-CPeriDtN.js} +1 -1
  60. package/console-app/assets/{railroad-3IZDKUUU-C9YvFeFq.js → railroad-3IZDKUUU-2iKAyaie.js} +1 -1
  61. package/console-app/assets/railroad-abnf-AHOZXSZD-D26xz4MM.js +1 -0
  62. package/console-app/assets/railroad-ebnf-EBAXGLYW-CCAiMPWc.js +1 -0
  63. package/console-app/assets/railroad-peg-LSFZ7HO6-BA1zcisq.js +1 -0
  64. package/console-app/assets/{railroadDiagram-AXF67PYL-IRFg1VrU.js → railroadDiagram-AXF67PYL-DVEqCXoo.js} +1 -1
  65. package/console-app/assets/{requirementDiagram-LRYGKXZP-L-7ubbcT.js → requirementDiagram-LRYGKXZP-CSgSNL8p.js} +1 -1
  66. package/console-app/assets/{sankeyDiagram-W5VNT64P-DBttV6Gt.js → sankeyDiagram-W5VNT64P-CFk11odN.js} +1 -1
  67. package/console-app/assets/{sequenceDiagram-SI44F4Z6-Bg15oKAB.js → sequenceDiagram-SI44F4Z6-Cb5u6FsJ.js} +1 -1
  68. package/console-app/assets/{src-DUC7tLWb.js → src-LceHEY_Q.js} +1 -1
  69. package/console-app/assets/{stateDiagram-OKZ733FA-PWl0XGs5.js → stateDiagram-OKZ733FA-DM1jWlkU.js} +1 -1
  70. package/console-app/assets/stateDiagram-v2-UEYNNEHI-COVnPYic.js +1 -0
  71. package/console-app/assets/{swimlanes-SLNWSIFB-CyXEtGBV.js → swimlanes-SLNWSIFB-DD6MchlE.js} +1 -1
  72. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-B5q8iRCQ.js +8 -0
  73. package/console-app/assets/{timeline-definition-Z64GVDOM-BLA-DhBt.js → timeline-definition-Z64GVDOM-zu5cNnVv.js} +1 -1
  74. package/console-app/assets/{treeView-QDETBFTQ-Bfnmn2UY.js → treeView-QDETBFTQ-D_4IbvCG.js} +1 -1
  75. package/console-app/assets/{treemap-6X3UGDF4-DTA0wveO.js → treemap-6X3UGDF4-DNc6bjUc.js} +1 -1
  76. package/console-app/assets/{vennDiagram-T6HMQDX7-CLXYZln0.js → vennDiagram-T6HMQDX7-DfqoG28f.js} +1 -1
  77. package/console-app/assets/{wardley-OPB4EBWU-zwxqisG0.js → wardley-OPB4EBWU-BqpI2Lx-.js} +1 -1
  78. package/console-app/assets/{wardleyDiagram-T6FBY63Y-hKtKvNlv.js → wardleyDiagram-T6FBY63Y-CLIZSvNi.js} +1 -1
  79. package/console-app/assets/{xychartDiagram-ELKLHX3M-BGf8p4bo.js → xychartDiagram-ELKLHX3M-Cni-XXa6.js} +1 -1
  80. package/console-app/index.html +1 -1
  81. package/dist/.pikku/addon/index.d.ts +1 -1
  82. package/dist/.pikku/addon/index.js +1 -1
  83. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  84. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  85. package/dist/.pikku/agent/index.d.ts +1 -1
  86. package/dist/.pikku/agent/index.js +1 -1
  87. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  88. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  89. package/dist/.pikku/auth/index.d.ts +1 -1
  90. package/dist/.pikku/auth/index.js +1 -1
  91. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
  92. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  93. package/dist/.pikku/channel/index.d.ts +1 -1
  94. package/dist/.pikku/channel/index.js +1 -1
  95. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  96. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  97. package/dist/.pikku/cli/index.d.ts +1 -1
  98. package/dist/.pikku/cli/index.js +1 -1
  99. package/dist/.pikku/cli/pikku-cli-channel.js +21 -1
  100. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  101. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  102. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  103. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  104. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +103 -0
  105. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  106. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  107. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  108. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +103 -0
  109. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  110. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  111. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  112. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  113. package/dist/.pikku/error/index.d.ts +1 -1
  114. package/dist/.pikku/error/index.js +1 -1
  115. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  116. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  117. package/dist/.pikku/function/index.d.ts +1 -1
  118. package/dist/.pikku/function/index.js +1 -1
  119. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  120. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  121. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  122. package/dist/.pikku/function/pikku-functions-meta.gen.json +79 -3
  123. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  124. package/dist/.pikku/gateway/index.d.ts +1 -1
  125. package/dist/.pikku/gateway/index.js +1 -1
  126. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  127. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  128. package/dist/.pikku/http/index.d.ts +1 -1
  129. package/dist/.pikku/http/index.js +1 -1
  130. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  131. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  132. package/dist/.pikku/mcp/index.d.ts +1 -1
  133. package/dist/.pikku/mcp/index.js +1 -1
  134. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  135. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  136. package/dist/.pikku/middleware/index.d.ts +1 -1
  137. package/dist/.pikku/middleware/index.js +1 -1
  138. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
  139. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  140. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  141. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  142. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  143. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  144. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  145. package/dist/.pikku/queue/index.d.ts +1 -1
  146. package/dist/.pikku/queue/index.js +1 -1
  147. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  148. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  149. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  150. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  151. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  152. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  153. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +4 -0
  154. package/dist/.pikku/scenarios/index.d.ts +1 -1
  155. package/dist/.pikku/scenarios/index.js +1 -1
  156. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  157. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  158. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  159. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  160. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  161. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  162. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  163. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  164. package/dist/.pikku/scheduler/index.d.ts +1 -1
  165. package/dist/.pikku/scheduler/index.js +1 -1
  166. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  167. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  168. package/dist/.pikku/schemas/register.gen.js +17 -1
  169. package/dist/.pikku/schemas/schemas/FabricFindingsClearInput.schema.json +1 -0
  170. package/dist/.pikku/schemas/schemas/FabricFindingsClearOutput.schema.json +1 -0
  171. package/dist/.pikku/schemas/schemas/FabricFindingsFlushInput.schema.json +1 -0
  172. package/dist/.pikku/schemas/schemas/FabricFindingsFlushOutput.schema.json +1 -0
  173. package/dist/.pikku/schemas/schemas/FabricFindingsListInput.schema.json +1 -0
  174. package/dist/.pikku/schemas/schemas/FabricFindingsListOutput.schema.json +1 -0
  175. package/dist/.pikku/schemas/schemas/FabricReportInput.schema.json +1 -0
  176. package/dist/.pikku/schemas/schemas/FabricReportOutput.schema.json +1 -0
  177. package/dist/.pikku/scopes/index.d.ts +1 -1
  178. package/dist/.pikku/scopes/index.js +1 -1
  179. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  180. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  181. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  182. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  183. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  184. package/dist/.pikku/secrets/index.d.ts +1 -1
  185. package/dist/.pikku/secrets/index.js +1 -1
  186. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  187. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  188. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  189. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  190. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  191. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  192. package/dist/.pikku/setup/index.d.ts +1 -1
  193. package/dist/.pikku/setup/index.js +1 -1
  194. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  195. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  196. package/dist/.pikku/trigger/index.d.ts +1 -1
  197. package/dist/.pikku/trigger/index.js +1 -1
  198. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  199. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  200. package/dist/.pikku/variables/index.d.ts +1 -1
  201. package/dist/.pikku/variables/index.js +1 -1
  202. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  203. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  204. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  205. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  206. package/dist/.pikku/workflow/index.d.ts +1 -1
  207. package/dist/.pikku/workflow/index.js +1 -1
  208. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  209. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  210. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  211. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  212. package/dist/bin/pikku-bin.mjs +2 -2
  213. package/dist/src/fabric/fabric-commands.d.ts +138 -0
  214. package/dist/src/fabric/fabric-commands.js +65 -0
  215. package/dist/src/fabric/functions/findings-clear.function.d.ts +12 -0
  216. package/dist/src/fabric/functions/findings-clear.function.js +17 -0
  217. package/dist/src/fabric/functions/findings-flush.function.d.ts +33 -0
  218. package/dist/src/fabric/functions/findings-flush.function.js +36 -0
  219. package/dist/src/fabric/functions/findings-list.function.d.ts +55 -0
  220. package/dist/src/fabric/functions/findings-list.function.js +52 -0
  221. package/dist/src/fabric/functions/report.function.d.ts +121 -0
  222. package/dist/src/fabric/functions/report.function.js +84 -0
  223. package/dist/src/fabric/lib/finding-spool.d.ts +56 -0
  224. package/dist/src/fabric/lib/finding-spool.js +105 -0
  225. package/dist/src/fabric/lib/finding.d.ts +92 -0
  226. package/dist/src/fabric/lib/finding.js +156 -0
  227. package/dist/src/fabric/lib/report-environment.d.ts +53 -0
  228. package/dist/src/fabric/lib/report-environment.js +119 -0
  229. package/dist/src/functions/commands/environment.d.ts +5 -0
  230. package/dist/src/functions/commands/persona.js +1 -0
  231. package/dist/src/functions/commands/scenario.js +1 -0
  232. package/dist/src/functions/validate/auth-plugin-checks.d.ts +1 -1
  233. package/dist/src/functions/validate/auth-plugin-checks.js +8 -5
  234. package/dist/src/functions/validate/persona-checks.js +8 -6
  235. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +10 -5
  236. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +1 -1
  237. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +3 -3
  238. package/dist/src/services.js +9 -0
  239. package/dist/tsconfig.tsbuildinfo +1 -1
  240. package/package.json +8 -8
  241. package/snippets.json +1 -1
  242. package/surface.json +2 -2
  243. package/console-app/assets/architecture-TIHT7OUA-DmG0bXDT.js +0 -1
  244. package/console-app/assets/channel-BgCT5gon.js +0 -1
  245. package/console-app/assets/chunk-XXDRQBXY-EP2C9tTO.js +0 -1
  246. package/console-app/assets/classDiagram-JCYQIIEL-D9wGqB4J.js +0 -1
  247. package/console-app/assets/classDiagram-v2-OCEON4UE-D9wGqB4J.js +0 -1
  248. package/console-app/assets/eventmodeling-45OFAUF4-NKFCZ8Fj.js +0 -1
  249. package/console-app/assets/flowDiagram-UKHOOZJN-BpdPkmum.js +0 -1
  250. package/console-app/assets/railroad-abnf-AHOZXSZD-B3CYVW4p.js +0 -1
  251. package/console-app/assets/railroad-ebnf-EBAXGLYW-DrZ2yF62.js +0 -1
  252. package/console-app/assets/railroad-peg-LSFZ7HO6-CWAmMyxd.js +0 -1
  253. package/console-app/assets/stateDiagram-v2-UEYNNEHI-Celbgz4u.js +0 -1
  254. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-zxpNaB4U.js +0 -8
@@ -46,6 +46,10 @@ import { FabricValidate, renderValidate, } from './functions/validate.function.j
46
46
  import { FabricSmoke, renderSmoke } from './functions/smoke.function.js';
47
47
  import { FabricPublish } from './functions/publish.function.js';
48
48
  import { FabricAdd } from './functions/add.function.js';
49
+ import { FabricReport } from './functions/report.function.js';
50
+ import { FabricFindingsList, renderFindingsList, } from './functions/findings-list.function.js';
51
+ import { FabricFindingsFlush } from './functions/findings-flush.function.js';
52
+ import { FabricFindingsClear } from './functions/findings-clear.function.js';
49
53
  import { FabricAddonVerify, renderAddonVerify, } from './functions/addon-verify.function.js';
50
54
  export const fabricCommands = defineCLICommands({
51
55
  validate: pikkuCLICommand({
@@ -302,6 +306,67 @@ export const fabricCommands = defineCLICommands({
302
306
  json: { description: 'Machine-readable output', default: false },
303
307
  },
304
308
  }),
309
+ report: pikkuCLICommand({
310
+ parameters: '[title]',
311
+ func: FabricReport,
312
+ description: 'Report a finding — something about pikku that cost time — to fabric',
313
+ options: {
314
+ stdin: {
315
+ description: 'Read the whole finding as JSON on stdin instead of from flags (prose survives the shell intact)',
316
+ default: false,
317
+ },
318
+ kind: {
319
+ description: 'product (fix pikku) or harness (fix the skill that misled you)',
320
+ },
321
+ model: { description: 'The model that hit this' },
322
+ expected: { description: 'What you expected pikku to do' },
323
+ actual: { description: 'What it did instead' },
324
+ skill: {
325
+ description: 'For a harness finding, the skill that misled you',
326
+ },
327
+ passage: { description: 'The passage in that skill it contradicts' },
328
+ command: { description: 'The command you ran' },
329
+ error: { description: "The error's message line, verbatim" },
330
+ repro: { description: 'The shortest way to reach it again' },
331
+ workaround: { description: 'What you did instead, inside the app' },
332
+ proposal: {
333
+ description: 'What pikku should do — named file and function, mechanism, suggested change',
334
+ },
335
+ tried: {
336
+ description: 'For an unresolved finding, what you tried and how each attempt failed',
337
+ },
338
+ unresolved: {
339
+ description: 'No workaround was found — a blocker, not a tax',
340
+ default: false,
341
+ },
342
+ area: { description: 'The part of pikku this is about' },
343
+ surface: { description: 'Where it showed up: local, deployed or both' },
344
+ cost: { description: 'What it cost, measured or estimated' },
345
+ run: { description: 'Run id, to group findings from one build' },
346
+ deployTarget: { description: 'The deploy target in use' },
347
+ },
348
+ }),
349
+ findings: {
350
+ description: 'Inspect the findings held locally because they could not be sent',
351
+ subcommands: {
352
+ list: pikkuCLICommand({
353
+ func: FabricFindingsList,
354
+ render: renderFindingsList,
355
+ description: 'List the findings queued locally, waiting to be sent',
356
+ }),
357
+ flush: pikkuCLICommand({
358
+ func: FabricFindingsFlush,
359
+ description: 'Send every finding queued locally',
360
+ options: {
361
+ apiUrl: { description: 'Override the fabric-api URL for this call' },
362
+ },
363
+ }),
364
+ clear: pikkuCLICommand({
365
+ func: FabricFindingsClear,
366
+ description: 'Discard every queued finding without sending it',
367
+ }),
368
+ },
369
+ },
305
370
  metrics: pikkuCLICommand({
306
371
  func: FabricMetrics,
307
372
  description: 'Show request rate / error rate / latency for a stage',
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricFindingsClearInput: z.ZodObject<{}, z.core.$strip>;
3
+ export declare const FabricFindingsClearOutput: z.ZodObject<{
4
+ dropped: z.ZodNumber;
5
+ }, z.core.$strip>;
6
+ export declare const FabricFindingsClear: import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionConfig<Record<string, never>, {
7
+ dropped: number;
8
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<Record<string, never>, {
9
+ dropped: number;
10
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<Record<string, never>, {
11
+ dropped: number;
12
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/function/index.js';
3
+ import { clearSpool } from '../lib/finding-spool.js';
4
+ export const FabricFindingsClearInput = z.object({});
5
+ export const FabricFindingsClearOutput = z.object({
6
+ dropped: z.number(),
7
+ });
8
+ export const FabricFindingsClear = pikkuSessionlessFunc({
9
+ description: 'Discard every finding queued locally without sending it.',
10
+ input: FabricFindingsClearInput,
11
+ output: FabricFindingsClearOutput,
12
+ func: async () => {
13
+ const dropped = await clearSpool();
14
+ console.log(`[fabric] dropped ${dropped} queued finding(s)`);
15
+ return { dropped };
16
+ },
17
+ });
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricFindingsFlushInput: z.ZodObject<{
3
+ apiUrl: z.ZodOptional<z.ZodString>;
4
+ }, z.core.$strip>;
5
+ export declare const FabricFindingsFlushOutput: z.ZodObject<{
6
+ sent: z.ZodNumber;
7
+ remaining: z.ZodNumber;
8
+ reason: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ /**
11
+ * Explicitly asked for, so unlike `report` this one says plainly that it
12
+ * cannot run: someone who typed `flush` wants to know the login is missing,
13
+ * not to have the queue quietly stay put.
14
+ */
15
+ export declare const FabricFindingsFlush: import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionConfig<{
16
+ apiUrl?: string | undefined;
17
+ }, {
18
+ sent: number;
19
+ remaining: number;
20
+ reason?: string | undefined;
21
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<{
22
+ apiUrl?: string | undefined;
23
+ }, {
24
+ sent: number;
25
+ remaining: number;
26
+ reason?: string | undefined;
27
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<{
28
+ apiUrl?: string | undefined;
29
+ }, {
30
+ sent: number;
31
+ remaining: number;
32
+ reason?: string | undefined;
33
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>;
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/function/index.js';
3
+ import { resolveApiContext } from '../lib/config.js';
4
+ import { flushSpool } from '../lib/finding-spool.js';
5
+ export const FabricFindingsFlushInput = z.object({
6
+ apiUrl: z.string().optional(),
7
+ });
8
+ export const FabricFindingsFlushOutput = z.object({
9
+ sent: z.number(),
10
+ remaining: z.number(),
11
+ reason: z.string().optional(),
12
+ });
13
+ /**
14
+ * Explicitly asked for, so unlike `report` this one says plainly that it
15
+ * cannot run: someone who typed `flush` wants to know the login is missing,
16
+ * not to have the queue quietly stay put.
17
+ */
18
+ export const FabricFindingsFlush = pikkuSessionlessFunc({
19
+ description: 'Send every finding queued locally.',
20
+ input: FabricFindingsFlushInput,
21
+ output: FabricFindingsFlushOutput,
22
+ func: async (_services, { apiUrl: apiUrlOverride }) => {
23
+ const ctx = await resolveApiContext({ apiUrlOverride });
24
+ if (!ctx.token)
25
+ throw new Error('Not logged in. Run `pikku fabric login` first.');
26
+ const result = await flushSpool({
27
+ apiUrl: ctx.apiUrl,
28
+ token: ctx.token,
29
+ projectId: ctx.projectId,
30
+ });
31
+ console.log(result.remaining === 0
32
+ ? `[fabric] sent ${result.sent} finding(s)`
33
+ : `[fabric] sent ${result.sent}, ${result.remaining} still queued (${result.reason})`);
34
+ return result;
35
+ },
36
+ });
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricFindingsListInput: z.ZodObject<{}, z.core.$strip>;
3
+ export declare const FabricFindingsListOutput: z.ZodObject<{
4
+ findings: z.ZodArray<z.ZodObject<{
5
+ title: z.ZodString;
6
+ kind: z.ZodString;
7
+ reason: z.ZodString;
8
+ reportedAt: z.ZodString;
9
+ projectId: z.ZodNullable<z.ZodString>;
10
+ file: z.ZodString;
11
+ }, z.core.$strip>>;
12
+ }, z.core.$strip>;
13
+ /**
14
+ * What is waiting to be sent. Without this the spool is invisible, and an
15
+ * agent has no way to tell a finding that reached fabric from one still
16
+ * sitting on the machine.
17
+ */
18
+ export declare const FabricFindingsList: import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionConfig<Record<string, never>, {
19
+ findings: {
20
+ title: string;
21
+ kind: string;
22
+ reason: string;
23
+ reportedAt: string;
24
+ projectId: string | null;
25
+ file: string;
26
+ }[];
27
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<Record<string, never>, {
28
+ findings: {
29
+ title: string;
30
+ kind: string;
31
+ reason: string;
32
+ reportedAt: string;
33
+ projectId: string | null;
34
+ file: string;
35
+ }[];
36
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<Record<string, never>, {
37
+ findings: {
38
+ title: string;
39
+ kind: string;
40
+ reason: string;
41
+ reportedAt: string;
42
+ projectId: string | null;
43
+ file: string;
44
+ }[];
45
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>;
46
+ type QueuedFinding = {
47
+ title: string;
48
+ kind: string;
49
+ reason: string;
50
+ reportedAt: string;
51
+ };
52
+ export declare const renderFindingsList: (_s: unknown, { findings }: {
53
+ findings: QueuedFinding[];
54
+ }) => void;
55
+ export {};
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/function/index.js';
3
+ import { readSpool } from '../lib/finding-spool.js';
4
+ import { dim, table } from '../lib/output.js';
5
+ export const FabricFindingsListInput = z.object({});
6
+ export const FabricFindingsListOutput = z.object({
7
+ findings: z.array(z.object({
8
+ title: z.string(),
9
+ kind: z.string(),
10
+ reason: z.string(),
11
+ reportedAt: z.string(),
12
+ projectId: z.string().nullable(),
13
+ file: z.string(),
14
+ })),
15
+ });
16
+ /**
17
+ * What is waiting to be sent. Without this the spool is invisible, and an
18
+ * agent has no way to tell a finding that reached fabric from one still
19
+ * sitting on the machine.
20
+ */
21
+ export const FabricFindingsList = pikkuSessionlessFunc({
22
+ description: 'List the findings queued locally, waiting to be sent.',
23
+ input: FabricFindingsListInput,
24
+ output: FabricFindingsListOutput,
25
+ func: async () => ({
26
+ findings: (await readSpool()).map((entry) => ({
27
+ title: entry.payload.title,
28
+ kind: entry.payload.kind,
29
+ reason: entry.reason,
30
+ reportedAt: entry.payload.reportedAt,
31
+ projectId: entry.projectId,
32
+ file: entry.file,
33
+ })),
34
+ }),
35
+ });
36
+ export const renderFindingsList = (_s, { findings }) => {
37
+ console.log('');
38
+ if (findings.length === 0) {
39
+ console.log(dim(' Nothing queued — every finding you filed was sent.'));
40
+ console.log('');
41
+ return;
42
+ }
43
+ console.log(table(['REPORTED', 'KIND', 'HELD BECAUSE', 'TITLE'], findings.map((f) => [
44
+ f.reportedAt.slice(0, 16).replace('T', ' '),
45
+ f.kind,
46
+ f.reason,
47
+ f.title,
48
+ ])));
49
+ console.log('');
50
+ console.log(dim(' `pikku fabric findings flush` sends them.'));
51
+ console.log('');
52
+ };
@@ -0,0 +1,121 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Every field is optional here and the strictness lives in `FindingInput`,
4
+ * because `--stdin` supplies the whole finding at once and the flags then
5
+ * carry nothing. Whichever path was used, the same schema decides whether a
6
+ * finding is well-formed.
7
+ */
8
+ export declare const FabricReportInput: z.ZodObject<{
9
+ title: z.ZodOptional<z.ZodString>;
10
+ kind: z.ZodOptional<z.ZodEnum<{
11
+ product: "product";
12
+ harness: "harness";
13
+ }>>;
14
+ model: z.ZodOptional<z.ZodString>;
15
+ expected: z.ZodOptional<z.ZodString>;
16
+ actual: z.ZodOptional<z.ZodString>;
17
+ skill: z.ZodOptional<z.ZodOptional<z.ZodString>>;
18
+ passage: z.ZodOptional<z.ZodOptional<z.ZodString>>;
19
+ command: z.ZodOptional<z.ZodOptional<z.ZodString>>;
20
+ error: z.ZodOptional<z.ZodOptional<z.ZodString>>;
21
+ repro: z.ZodOptional<z.ZodOptional<z.ZodString>>;
22
+ workaround: z.ZodOptional<z.ZodOptional<z.ZodString>>;
23
+ proposal: z.ZodOptional<z.ZodOptional<z.ZodString>>;
24
+ tried: z.ZodOptional<z.ZodOptional<z.ZodString>>;
25
+ unresolved: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
26
+ area: z.ZodOptional<z.ZodOptional<z.ZodString>>;
27
+ surface: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
28
+ local: "local";
29
+ deployed: "deployed";
30
+ both: "both";
31
+ }>>>;
32
+ cost: z.ZodOptional<z.ZodOptional<z.ZodString>>;
33
+ run: z.ZodOptional<z.ZodOptional<z.ZodString>>;
34
+ deployTarget: z.ZodOptional<z.ZodOptional<z.ZodString>>;
35
+ stdin: z.ZodOptional<z.ZodBoolean>;
36
+ }, z.core.$strip>;
37
+ export declare const FabricReportOutput: z.ZodObject<{
38
+ sent: z.ZodBoolean;
39
+ reason: z.ZodOptional<z.ZodString>;
40
+ spooled: z.ZodBoolean;
41
+ queued: z.ZodNumber;
42
+ }, z.core.$strip>;
43
+ export declare const FabricReport: import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionConfig<{
44
+ title?: string | undefined;
45
+ kind?: "product" | "harness" | undefined;
46
+ model?: string | undefined;
47
+ expected?: string | undefined;
48
+ actual?: string | undefined;
49
+ skill?: string | undefined;
50
+ passage?: string | undefined;
51
+ command?: string | undefined;
52
+ error?: string | undefined;
53
+ repro?: string | undefined;
54
+ workaround?: string | undefined;
55
+ proposal?: string | undefined;
56
+ tried?: string | undefined;
57
+ unresolved?: boolean | undefined;
58
+ area?: string | undefined;
59
+ surface?: "local" | "deployed" | "both" | undefined;
60
+ cost?: string | undefined;
61
+ run?: string | undefined;
62
+ deployTarget?: string | undefined;
63
+ stdin?: boolean | undefined;
64
+ }, {
65
+ sent: boolean;
66
+ spooled: boolean;
67
+ queued: number;
68
+ reason?: string | undefined;
69
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<{
70
+ title?: string | undefined;
71
+ kind?: "product" | "harness" | undefined;
72
+ model?: string | undefined;
73
+ expected?: string | undefined;
74
+ actual?: string | undefined;
75
+ skill?: string | undefined;
76
+ passage?: string | undefined;
77
+ command?: string | undefined;
78
+ error?: string | undefined;
79
+ repro?: string | undefined;
80
+ workaround?: string | undefined;
81
+ proposal?: string | undefined;
82
+ tried?: string | undefined;
83
+ unresolved?: boolean | undefined;
84
+ area?: string | undefined;
85
+ surface?: "local" | "deployed" | "both" | undefined;
86
+ cost?: string | undefined;
87
+ run?: string | undefined;
88
+ deployTarget?: string | undefined;
89
+ stdin?: boolean | undefined;
90
+ }, {
91
+ sent: boolean;
92
+ spooled: boolean;
93
+ queued: number;
94
+ reason?: string | undefined;
95
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<{
96
+ title?: string | undefined;
97
+ kind?: "product" | "harness" | undefined;
98
+ model?: string | undefined;
99
+ expected?: string | undefined;
100
+ actual?: string | undefined;
101
+ skill?: string | undefined;
102
+ passage?: string | undefined;
103
+ command?: string | undefined;
104
+ error?: string | undefined;
105
+ repro?: string | undefined;
106
+ workaround?: string | undefined;
107
+ proposal?: string | undefined;
108
+ tried?: string | undefined;
109
+ unresolved?: boolean | undefined;
110
+ area?: string | undefined;
111
+ surface?: "local" | "deployed" | "both" | undefined;
112
+ cost?: string | undefined;
113
+ run?: string | undefined;
114
+ deployTarget?: string | undefined;
115
+ stdin?: boolean | undefined;
116
+ }, {
117
+ sent: boolean;
118
+ spooled: boolean;
119
+ queued: number;
120
+ reason?: string | undefined;
121
+ }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>;
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/function/index.js';
3
+ import { resolveApiContext } from '../lib/config.js';
4
+ import { collectReportEnvironment } from '../lib/report-environment.js';
5
+ import { FindingInput, buildFindingPayload, parseFinding, parseFindingJson, postFinding, renderReceipt, validateFinding, } from '../lib/finding.js';
6
+ import { flushSpool, readSpool, spoolFinding } from '../lib/finding-spool.js';
7
+ /**
8
+ * Every field is optional here and the strictness lives in `FindingInput`,
9
+ * because `--stdin` supplies the whole finding at once and the flags then
10
+ * carry nothing. Whichever path was used, the same schema decides whether a
11
+ * finding is well-formed.
12
+ */
13
+ export const FabricReportInput = FindingInput.partial().extend({
14
+ stdin: z.boolean().optional(),
15
+ });
16
+ export const FabricReportOutput = z.object({
17
+ sent: z.boolean(),
18
+ reason: z.string().optional(),
19
+ spooled: z.boolean(),
20
+ queued: z.number(),
21
+ });
22
+ const readStdin = async () => {
23
+ if (process.stdin.isTTY) {
24
+ throw new Error('--stdin expects the finding as JSON on standard input, and nothing was piped in.');
25
+ }
26
+ const chunks = [];
27
+ for await (const chunk of process.stdin)
28
+ chunks.push(Buffer.from(chunk));
29
+ return Buffer.concat(chunks).toString('utf8');
30
+ };
31
+ /**
32
+ * Every path that cannot send holds the finding instead. The states that stop
33
+ * a send — logged out, fabric unreachable — are the states a finding is most
34
+ * likely to be describing, so dropping it loses exactly the reports worth
35
+ * having.
36
+ */
37
+ const hold = async (payload, reason, projectId, message) => {
38
+ await spoolFinding({ payload, reason, projectId });
39
+ const queued = (await readSpool()).length;
40
+ console.log(`[fabric] ${message} — finding queued (${queued} waiting, sent on your next report)`);
41
+ return { sent: false, reason, spooled: true, queued };
42
+ };
43
+ export const FabricReport = pikkuSessionlessFunc({
44
+ description: 'Report a finding — something about pikku that cost time — to fabric.',
45
+ input: FabricReportInput,
46
+ output: FabricReportOutput,
47
+ func: async (_services, { stdin, ...flags }) => {
48
+ const parsed = stdin
49
+ ? parseFindingJson(await readStdin())
50
+ : parseFinding(flags);
51
+ if ('problems' in parsed) {
52
+ throw new Error(parsed.problems.join('\n'));
53
+ }
54
+ const problems = validateFinding(parsed.finding);
55
+ if (problems.length > 0) {
56
+ throw new Error(problems.join('\n'));
57
+ }
58
+ const payload = buildFindingPayload(parsed.finding, await collectReportEnvironment());
59
+ console.log(renderReceipt(payload));
60
+ const ctx = await resolveApiContext();
61
+ if (!ctx.token) {
62
+ return hold(payload, 'not-logged-in', ctx.projectId, 'not logged in');
63
+ }
64
+ const flushed = await flushSpool({
65
+ apiUrl: ctx.apiUrl,
66
+ token: ctx.token,
67
+ projectId: ctx.projectId,
68
+ });
69
+ if (flushed.sent > 0) {
70
+ console.log(`[fabric] sent ${flushed.sent} finding(s) held from earlier`);
71
+ }
72
+ const result = await postFinding({
73
+ apiUrl: ctx.apiUrl,
74
+ token: ctx.token,
75
+ projectId: ctx.projectId,
76
+ payload,
77
+ });
78
+ if (!result.sent) {
79
+ return hold(payload, result.reason ?? 'send-failed', ctx.projectId, `fabric did not accept it (${result.reason})`);
80
+ }
81
+ console.log('[fabric] finding sent');
82
+ return { sent: true, spooled: false, queued: flushed.remaining };
83
+ },
84
+ });
@@ -0,0 +1,56 @@
1
+ import { type FindingPayload } from './finding.js';
2
+ /**
3
+ * A finding that could not be sent when it was filed, held until it can be.
4
+ *
5
+ * The cases that keep a finding from leaving — no token, no linked project, a
6
+ * fabric that is down — are the same cases the finding is most likely to be
7
+ * about: a scaffold that never got far enough to log in is exactly the thing
8
+ * worth hearing about, and printing it and dropping it loses it. The spool
9
+ * lives in `~/.fabric`, not the repo, so nothing goes stale on an abandoned
10
+ * branch and git never sees it.
11
+ */
12
+ export interface SpooledFinding {
13
+ file: string;
14
+ spooledAt: string;
15
+ reason: string;
16
+ projectId: string | null;
17
+ payload: FindingPayload;
18
+ }
19
+ /** Oldest entries past this are dropped, so a logged-out machine stays bounded. */
20
+ export declare const SPOOL_LIMIT = 100;
21
+ export declare function spoolDir(): string;
22
+ export declare function spoolFinding(entry: {
23
+ payload: FindingPayload;
24
+ reason: string;
25
+ projectId: string | null;
26
+ }): Promise<string>;
27
+ /**
28
+ * Chronological, by the timestamp inside each finding rather than the one its
29
+ * name carries — the name is for a human reading the directory, and ordering
30
+ * the queue by it would make the send order a property of how a filename
31
+ * happens to sort. A file that no longer parses is skipped rather than thrown
32
+ * for: one corrupt entry is not a reason to lose the rest, or to fail the
33
+ * command that happened to notice.
34
+ */
35
+ export declare function readSpool(): Promise<SpooledFinding[]>;
36
+ export declare function clearSpool(): Promise<number>;
37
+ export interface FlushResult {
38
+ sent: number;
39
+ remaining: number;
40
+ reason?: string;
41
+ }
42
+ /**
43
+ * Drain the spool oldest-first, stopping at the first entry fabric does not
44
+ * accept — a refusal usually means the next one will be refused too, and
45
+ * hammering an endpoint that just said no is the wrong thing for a command
46
+ * whose real job is something else.
47
+ *
48
+ * An entry keeps the project it was filed against; one spooled before the repo
49
+ * was linked adopts the project linked now, and sends without one if there is
50
+ * still none.
51
+ */
52
+ export declare function flushSpool(ctx: {
53
+ apiUrl: string;
54
+ token: string;
55
+ projectId: string | null;
56
+ }): Promise<FlushResult>;
@@ -0,0 +1,105 @@
1
+ import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { randomBytes } from 'node:crypto';
6
+ import { postFinding } from './finding.js';
7
+ /** Oldest entries past this are dropped, so a logged-out machine stays bounded. */
8
+ export const SPOOL_LIMIT = 100;
9
+ export function spoolDir() {
10
+ return (process.env.FABRIC_FINDINGS_DIR ?? join(homedir(), '.fabric', 'findings'));
11
+ }
12
+ export async function spoolFinding(entry) {
13
+ const dir = spoolDir();
14
+ await mkdir(dir, { recursive: true });
15
+ const stamp = entry.payload.reportedAt.replace(/[:.]/g, '-');
16
+ const file = join(dir, `${stamp}-${randomBytes(3).toString('hex')}.json`);
17
+ await writeFile(file, JSON.stringify({
18
+ spooledAt: new Date().toISOString(),
19
+ reason: entry.reason,
20
+ projectId: entry.projectId,
21
+ payload: entry.payload,
22
+ }, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
23
+ await pruneSpool();
24
+ return file;
25
+ }
26
+ /**
27
+ * Chronological, by the timestamp inside each finding rather than the one its
28
+ * name carries — the name is for a human reading the directory, and ordering
29
+ * the queue by it would make the send order a property of how a filename
30
+ * happens to sort. A file that no longer parses is skipped rather than thrown
31
+ * for: one corrupt entry is not a reason to lose the rest, or to fail the
32
+ * command that happened to notice.
33
+ */
34
+ export async function readSpool() {
35
+ const dir = spoolDir();
36
+ if (!existsSync(dir))
37
+ return [];
38
+ const names = (await readdir(dir)).filter((n) => n.endsWith('.json'));
39
+ const entries = [];
40
+ for (const name of names) {
41
+ const file = join(dir, name);
42
+ try {
43
+ const parsed = JSON.parse(await readFile(file, 'utf8'));
44
+ if (!parsed?.payload)
45
+ continue;
46
+ entries.push({
47
+ file,
48
+ spooledAt: parsed.spooledAt ?? '',
49
+ reason: parsed.reason ?? 'unknown',
50
+ projectId: parsed.projectId ?? null,
51
+ payload: parsed.payload,
52
+ });
53
+ }
54
+ catch {
55
+ continue;
56
+ }
57
+ }
58
+ return entries.sort((a, b) => a.payload.reportedAt.localeCompare(b.payload.reportedAt) ||
59
+ a.file.localeCompare(b.file));
60
+ }
61
+ export async function clearSpool() {
62
+ const entries = await readSpool();
63
+ for (const entry of entries)
64
+ await rm(entry.file, { force: true });
65
+ return entries.length;
66
+ }
67
+ async function pruneSpool() {
68
+ const entries = await readSpool();
69
+ const excess = Math.max(0, entries.length - SPOOL_LIMIT);
70
+ for (const entry of entries.slice(0, excess)) {
71
+ await rm(entry.file, { force: true });
72
+ }
73
+ }
74
+ /**
75
+ * Drain the spool oldest-first, stopping at the first entry fabric does not
76
+ * accept — a refusal usually means the next one will be refused too, and
77
+ * hammering an endpoint that just said no is the wrong thing for a command
78
+ * whose real job is something else.
79
+ *
80
+ * An entry keeps the project it was filed against; one spooled before the repo
81
+ * was linked adopts the project linked now, and sends without one if there is
82
+ * still none.
83
+ */
84
+ export async function flushSpool(ctx) {
85
+ const entries = await readSpool();
86
+ let sent = 0;
87
+ for (const [index, entry] of entries.entries()) {
88
+ const result = await postFinding({
89
+ apiUrl: ctx.apiUrl,
90
+ token: ctx.token,
91
+ projectId: entry.projectId ?? ctx.projectId,
92
+ payload: entry.payload,
93
+ });
94
+ if (!result.sent) {
95
+ return {
96
+ sent,
97
+ remaining: entries.length - index,
98
+ reason: result.reason,
99
+ };
100
+ }
101
+ await rm(entry.file, { force: true });
102
+ sent++;
103
+ }
104
+ return { sent, remaining: 0 };
105
+ }