@koda-sl/baker-cli 0.449.0-dev.bd86e5817 → 0.450.0-dev.b454c3dd2

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 (443) hide show
  1. package/README.md +11 -6
  2. package/dist/{account-AL2ZDSR7.js → account-XQXC7BR6.js} +5 -4
  3. package/dist/{account-AL2ZDSR7.js.map → account-XQXC7BR6.js.map} +1 -1
  4. package/dist/{accounts-HAFCLSR5.js → accounts-722UY53A.js} +4 -3
  5. package/dist/{accounts-HAFCLSR5.js.map → accounts-722UY53A.js.map} +1 -1
  6. package/dist/{accounts-O5MFO3QQ.js → accounts-E67BMK7T.js} +6 -5
  7. package/dist/{accounts-O5MFO3QQ.js.map → accounts-E67BMK7T.js.map} +1 -1
  8. package/dist/{actions-P35MEMGN.js → actions-6TQMSVWD.js} +18 -18
  9. package/dist/{add-5GAN6CVO.js → add-2BEGRHUX.js} +28 -5
  10. package/dist/add-2BEGRHUX.js.map +1 -0
  11. package/dist/{add-6DTC6X5G.js → add-LEMKEOPO.js} +3 -2
  12. package/dist/{add-6DTC6X5G.js.map → add-LEMKEOPO.js.map} +1 -1
  13. package/dist/{ads-73PGA4YC.js → ads-AGYCVD62.js} +4 -4
  14. package/dist/{advertisers-AE75CGVE.js → advertisers-2VQKAYKG.js} +4 -3
  15. package/dist/{advertisers-AE75CGVE.js.map → advertisers-2VQKAYKG.js.map} +1 -1
  16. package/dist/{analytics-FKCXIMPO.js → analytics-IFELDWLL.js} +24 -20
  17. package/dist/analytics-IFELDWLL.js.map +1 -0
  18. package/dist/{analytics-FPWQQGRM.js → analytics-OH5QEH7F.js} +4 -3
  19. package/dist/{analytics-FPWQQGRM.js.map → analytics-OH5QEH7F.js.map} +1 -1
  20. package/dist/{assign-ABN2AQ4J.js → assign-PTUJUCEL.js} +4 -3
  21. package/dist/{assign-ABN2AQ4J.js.map → assign-PTUJUCEL.js.map} +1 -1
  22. package/dist/{audience-size-GNBJY35X.js → audience-size-LRI6GJRT.js} +4 -3
  23. package/dist/{audience-size-GNBJY35X.js.map → audience-size-LRI6GJRT.js.map} +1 -1
  24. package/dist/{audit-3VS63TBW.js → audit-HWP7OPB7.js} +4 -3
  25. package/dist/{audit-3VS63TBW.js.map → audit-HWP7OPB7.js.map} +1 -1
  26. package/dist/{autocomplete-VS3XG3B7.js → autocomplete-J6DHU6MR.js} +4 -3
  27. package/dist/{autocomplete-VS3XG3B7.js.map → autocomplete-J6DHU6MR.js.map} +1 -1
  28. package/dist/{avatars-EKCKHCC3.js → avatars-467OSBD6.js} +4 -4
  29. package/dist/{bid-pricing-Q72B65AY.js → bid-pricing-EO5WLHGV.js} +4 -3
  30. package/dist/{bid-pricing-Q72B65AY.js.map → bid-pricing-EO5WLHGV.js.map} +1 -1
  31. package/dist/{brand-C6PFGGQN.js → brand-YRTEPGBK.js} +2 -2
  32. package/dist/{brief-YFJHQ5VQ.js → brief-OSFJ2V7F.js} +8 -7
  33. package/dist/{brief-YFJHQ5VQ.js.map → brief-OSFJ2V7F.js.map} +1 -1
  34. package/dist/{campaign-groups-KFFYQPU3.js → campaign-groups-PQ6KSQJR.js} +9 -8
  35. package/dist/{campaign-groups-KFFYQPU3.js.map → campaign-groups-PQ6KSQJR.js.map} +1 -1
  36. package/dist/{campaign-writes-FHOS5XZY.js → campaign-writes-5OVLUD5E.js} +4 -3
  37. package/dist/{campaign-writes-FHOS5XZY.js.map → campaign-writes-5OVLUD5E.js.map} +1 -1
  38. package/dist/{campaigns-2NHWBF5C.js → campaigns-6T3NSUT2.js} +12 -11
  39. package/dist/{campaigns-2NHWBF5C.js.map → campaigns-6T3NSUT2.js.map} +1 -1
  40. package/dist/{campaigns-KZYZTXDS.js → campaigns-OYMXDGWX.js} +5 -5
  41. package/dist/{canvas-UJNSHD3B.js → canvas-YQFG52IG.js} +13 -13
  42. package/dist/{capabilities-DJFTRJKS.js → capabilities-PXBKUN2G.js} +4 -3
  43. package/dist/{capabilities-DJFTRJKS.js.map → capabilities-PXBKUN2G.js.map} +1 -1
  44. package/dist/{catalog-ZQX6BGXS.js → catalog-XLXIM7IW.js} +4 -4
  45. package/dist/{chats-D2CSDX5Q.js → chats-UK47UE7C.js} +4 -3
  46. package/dist/{chats-D2CSDX5Q.js.map → chats-UK47UE7C.js.map} +1 -1
  47. package/dist/{chunk-7YCRFMXF.js → chunk-24EB7EDR.js} +3 -3
  48. package/dist/{chunk-5ZDLTE5T.js → chunk-2AOE2MZQ.js} +3 -3
  49. package/dist/{chunk-4SG4RW2P.js → chunk-2VPBIT65.js} +8 -8
  50. package/dist/chunk-4PDHHHUJ.js +537 -0
  51. package/dist/chunk-4PDHHHUJ.js.map +1 -0
  52. package/dist/{chunk-NPY4QUMQ.js → chunk-54C6YDGJ.js} +2 -2
  53. package/dist/{chunk-LZ73CZGP.js → chunk-5WCSKQ7Z.js} +6 -6
  54. package/dist/{chunk-3G3ZDLMI.js → chunk-5ZNTADVR.js} +2 -2
  55. package/dist/{chunk-AEOSAVVL.js → chunk-6F7DVPOS.js} +3 -3
  56. package/dist/{chunk-CJCVIEJ5.js → chunk-6NY2GPR4.js} +3 -3
  57. package/dist/{chunk-CXWFX2AM.js → chunk-6Y7ZBENQ.js} +6 -6
  58. package/dist/{chunk-M242LHLX.js → chunk-7CTMH5AK.js} +3 -3
  59. package/dist/{chunk-XF4JUKVF.js → chunk-7EPXVICM.js} +38 -5
  60. package/dist/chunk-7EPXVICM.js.map +1 -0
  61. package/dist/{chunk-T2PC5AVH.js → chunk-ASI4P6X2.js} +6 -6
  62. package/dist/chunk-ASI4P6X2.js.map +1 -0
  63. package/dist/{chunk-QD7ESRLN.js → chunk-C46GUI2S.js} +2 -2
  64. package/dist/{chunk-6N6WKAJM.js → chunk-CWYAELVG.js} +2 -2
  65. package/dist/{chunk-MXYBTMKB.js → chunk-DCRPXOJJ.js} +2 -2
  66. package/dist/{chunk-GKZ2WU75.js → chunk-FPOGPFAG.js} +3 -14
  67. package/dist/chunk-FPOGPFAG.js.map +1 -0
  68. package/dist/{chunk-JQJDXPCO.js → chunk-FUHN4MFY.js} +2 -2
  69. package/dist/{chunk-S77PZVYM.js → chunk-FYGR3I2A.js} +3 -3
  70. package/dist/{chunk-OZRUJVIQ.js → chunk-H3427GPU.js} +2 -2
  71. package/dist/{chunk-RPUSTYHL.js → chunk-HDLYPC6K.js} +5 -3
  72. package/dist/{chunk-RPUSTYHL.js.map → chunk-HDLYPC6K.js.map} +1 -1
  73. package/dist/{chunk-SSJK7FEV.js → chunk-HR6IWNKE.js} +1196 -1140
  74. package/dist/chunk-HR6IWNKE.js.map +1 -0
  75. package/dist/{chunk-YRQYA2LR.js → chunk-HROZYQ5H.js} +63 -5
  76. package/dist/chunk-HROZYQ5H.js.map +1 -0
  77. package/dist/{chunk-IR6D7K4H.js → chunk-I3ZR655O.js} +2 -2
  78. package/dist/{chunk-YK32GUBN.js → chunk-KMKEYI37.js} +3 -3
  79. package/dist/{chunk-W3ZHJJVT.js → chunk-KS2QY6MK.js} +4 -4
  80. package/dist/{chunk-WNRV4QH7.js → chunk-LYQJ4PZW.js} +5 -5
  81. package/dist/{chunk-B5SCU7MJ.js → chunk-MACCF4JZ.js} +16 -22
  82. package/dist/{chunk-B5SCU7MJ.js.map → chunk-MACCF4JZ.js.map} +1 -1
  83. package/dist/{chunk-CSYSX4D2.js → chunk-N4LAYIIN.js} +2 -2
  84. package/dist/{chunk-4AUMTNVB.js → chunk-NA63BH7V.js} +3 -3
  85. package/dist/{chunk-4BQXAE7Z.js → chunk-NDEE6G2J.js} +2 -2
  86. package/dist/{chunk-EDLZGTGN.js → chunk-NYIGXDUG.js} +1 -39
  87. package/dist/chunk-NYIGXDUG.js.map +1 -0
  88. package/dist/{chunk-DWHIOO7E.js → chunk-QE7W555R.js} +163 -12
  89. package/dist/chunk-QE7W555R.js.map +1 -0
  90. package/dist/{chunk-MELOSESI.js → chunk-R25YLW6H.js} +2 -2
  91. package/dist/{chunk-Y6O7EWYY.js → chunk-RL5CP7N6.js} +5 -5
  92. package/dist/{chunk-DGUNI37S.js → chunk-RX2DKU3W.js} +2 -2
  93. package/dist/{chunk-3HJLHTWP.js → chunk-SJYEY5MV.js} +12 -3
  94. package/dist/{chunk-3HJLHTWP.js.map → chunk-SJYEY5MV.js.map} +1 -1
  95. package/dist/{chunk-VTDMKARH.js → chunk-ULG4QRTV.js} +2 -2
  96. package/dist/{chunk-QRP3PXWN.js → chunk-VL43TNIP.js} +2 -2
  97. package/dist/{chunk-SHL7LOC3.js → chunk-VLMBTPXU.js} +3 -3
  98. package/dist/{chunk-KHRXPQID.js → chunk-VYQ3QJ3C.js} +2 -2
  99. package/dist/{chunk-A4LOJ2VD.js → chunk-WGUQHAJY.js} +2 -2
  100. package/dist/{chunk-ZEYEWPK6.js → chunk-WKQG6TER.js} +2 -2
  101. package/dist/{chunk-3IBYKLXR.js → chunk-ZDMTXQ2Q.js} +2 -2
  102. package/dist/{claim-FZ5OQLMU.js → claim-W6G6VEG5.js} +4 -3
  103. package/dist/{claim-FZ5OQLMU.js.map → claim-W6G6VEG5.js.map} +1 -1
  104. package/dist/cli.js +30 -30
  105. package/dist/{companies-DNI634RC.js → companies-TS6KBATJ.js} +4 -3
  106. package/dist/{companies-DNI634RC.js.map → companies-TS6KBATJ.js.map} +1 -1
  107. package/dist/{complete-2FZMGJV5.js → complete-LYR2HMQU.js} +4 -3
  108. package/dist/{complete-2FZMGJV5.js.map → complete-LYR2HMQU.js.map} +1 -1
  109. package/dist/{connect-QCOGEPP4.js → connect-FCXNVTXG.js} +13 -6
  110. package/dist/{conversation-6IOGV7B7.js → conversation-6ZJGPCXM.js} +4 -3
  111. package/dist/{conversation-6IOGV7B7.js.map → conversation-6ZJGPCXM.js.map} +1 -1
  112. package/dist/{conversions-4SRZPBBX.js → conversions-UHB55MMQ.js} +8 -7
  113. package/dist/{conversions-4SRZPBBX.js.map → conversions-UHB55MMQ.js.map} +1 -1
  114. package/dist/{create-BJNMO6HT.js → create-EX6OWLNM.js} +3 -2
  115. package/dist/{create-BJNMO6HT.js.map → create-EX6OWLNM.js.map} +1 -1
  116. package/dist/{create-RM7HV77Y.js → create-JCA7JPLP.js} +4 -3
  117. package/dist/{create-RM7HV77Y.js.map → create-JCA7JPLP.js.map} +1 -1
  118. package/dist/{create-FX4OJ2XF.js → create-JFSK4BOP.js} +4 -3
  119. package/dist/{create-FX4OJ2XF.js.map → create-JFSK4BOP.js.map} +1 -1
  120. package/dist/{create-VXM34ZWH.js → create-WU22YYU2.js} +4 -3
  121. package/dist/{create-VXM34ZWH.js.map → create-WU22YYU2.js.map} +1 -1
  122. package/dist/{creatives-NJF2U763.js → creatives-FXUQ5WSD.js} +10 -10
  123. package/dist/{creatives-JSWBFQ5R.js → creatives-KGXUOYPE.js} +9 -8
  124. package/dist/{creatives-JSWBFQ5R.js.map → creatives-KGXUOYPE.js.map} +1 -1
  125. package/dist/{credentials-C33K6XCJ.js → credentials-MJC7PGKR.js} +4 -3
  126. package/dist/{credentials-C33K6XCJ.js.map → credentials-MJC7PGKR.js.map} +1 -1
  127. package/dist/{critique-YDKPYZLD.js → critique-GS5VV5ZC.js} +7 -6
  128. package/dist/{critique-YDKPYZLD.js.map → critique-GS5VV5ZC.js.map} +1 -1
  129. package/dist/{critique-ZS4ON3SU.js → critique-TPHR27KH.js} +4 -3
  130. package/dist/{critique-ZS4ON3SU.js.map → critique-TPHR27KH.js.map} +1 -1
  131. package/dist/{crm-R5IXIFIV.js → crm-AVCM5D4G.js} +5 -4
  132. package/dist/{crm-R5IXIFIV.js.map → crm-AVCM5D4G.js.map} +1 -1
  133. package/dist/{crm-52ROSWH5.js → crm-VK44O3ED.js} +5 -4
  134. package/dist/{crm-52ROSWH5.js.map → crm-VK44O3ED.js.map} +1 -1
  135. package/dist/{customers-A36LOZOB.js → customers-XH2DMTFH.js} +6 -5
  136. package/dist/{delete-M2GDLPYN.js → delete-E3R3TKIL.js} +3 -2
  137. package/dist/{delete-M2GDLPYN.js.map → delete-E3R3TKIL.js.map} +1 -1
  138. package/dist/{delete-HGHXY24R.js → delete-KRH2YMSZ.js} +4 -3
  139. package/dist/{delete-HGHXY24R.js.map → delete-KRH2YMSZ.js.map} +1 -1
  140. package/dist/{delete-AIGE4Y4H.js → delete-WI5E3DR3.js} +4 -3
  141. package/dist/{delete-AIGE4Y4H.js.map → delete-WI5E3DR3.js.map} +1 -1
  142. package/dist/{demographics-NTWB2IMG.js → demographics-XV5PBA77.js} +4 -3
  143. package/dist/{demographics-NTWB2IMG.js.map → demographics-XV5PBA77.js.map} +1 -1
  144. package/dist/{discard-2WWQG6OU.js → discard-HTQUUHJR.js} +4 -3
  145. package/dist/{discard-2WWQG6OU.js.map → discard-HTQUUHJR.js.map} +1 -1
  146. package/dist/{domain-Y2LT6JLT.js → domain-RECGZXJB.js} +4 -3
  147. package/dist/{domain-Y2LT6JLT.js.map → domain-RECGZXJB.js.map} +1 -1
  148. package/dist/{draft-SLHAKJ2A.js → draft-2IBSJEKC.js} +4 -3
  149. package/dist/{draft-SLHAKJ2A.js.map → draft-2IBSJEKC.js.map} +1 -1
  150. package/dist/{draft-FJ7WHIXW.js → draft-PVTX6QT6.js} +4 -3
  151. package/dist/{draft-FJ7WHIXW.js.map → draft-PVTX6QT6.js.map} +1 -1
  152. package/dist/{draft-EJXNNJ3Y.js → draft-Q546QPPU.js} +5 -4
  153. package/dist/{draft-EJXNNJ3Y.js.map → draft-Q546QPPU.js.map} +1 -1
  154. package/dist/engine/index.js +3 -3
  155. package/dist/{facets-U7AQC4H6.js → facets-7EVPJMKS.js} +4 -3
  156. package/dist/{facets-U7AQC4H6.js.map → facets-7EVPJMKS.js.map} +1 -1
  157. package/dist/{feedback-PPQMOQ46.js → feedback-EIZKS4JU.js} +2 -2
  158. package/dist/{feedback-record-V66HWVRR.js → feedback-record-TKO54BOE.js} +9 -8
  159. package/dist/{feedback-record-V66HWVRR.js.map → feedback-record-TKO54BOE.js.map} +1 -1
  160. package/dist/{fetch-W7NULDEO.js → fetch-4BY6HYUM.js} +4 -3
  161. package/dist/{fetch-W7NULDEO.js.map → fetch-4BY6HYUM.js.map} +1 -1
  162. package/dist/{find-OTDIX6V4.js → find-FRRHAPWX.js} +4 -4
  163. package/dist/{flows-KABCBS3S.js → flows-KTFAML22.js} +21 -17
  164. package/dist/flows-KTFAML22.js.map +1 -0
  165. package/dist/{fonts-EMMX3KII.js → fonts-MXR3ZKDX.js} +6 -5
  166. package/dist/{forecast-GNODEIIC.js → forecast-3QTJYVND.js} +4 -3
  167. package/dist/{forecast-GNODEIIC.js.map → forecast-3QTJYVND.js.map} +1 -1
  168. package/dist/{funnel-AAX27NH4.js → funnel-I6XIAEHG.js} +11 -455
  169. package/dist/funnel-I6XIAEHG.js.map +1 -0
  170. package/dist/{ga4-UDQ2HA2J.js → ga4-DV2AXCSL.js} +1 -1
  171. package/dist/{get-QEYJRLV3.js → get-5PKNKJ5S.js} +7 -4
  172. package/dist/get-5PKNKJ5S.js.map +1 -0
  173. package/dist/{get-RTVUZM6E.js → get-72YPHJGV.js} +4 -3
  174. package/dist/{get-RTVUZM6E.js.map → get-72YPHJGV.js.map} +1 -1
  175. package/dist/{google-M7KEXVK5.js → google-6OGZX47I.js} +2 -2
  176. package/dist/{google-KTO3PLEU.js → google-QYYYSSXI.js} +5 -5
  177. package/dist/{google-ads-OK6IG3UV.js → google-ads-QGMQKZQD.js} +4 -3
  178. package/dist/{google-ads-OK6IG3UV.js.map → google-ads-QGMQKZQD.js.map} +1 -1
  179. package/dist/{gsc-P3QYGDV2.js → gsc-TAKOF7J5.js} +1 -1
  180. package/dist/{history-PQUJKK26.js → history-6AVQX2OQ.js} +4 -3
  181. package/dist/{history-PQUJKK26.js.map → history-6AVQX2OQ.js.map} +1 -1
  182. package/dist/{hubspot-Y77VLY6Y.js → hubspot-SVEMFK5S.js} +9 -8
  183. package/dist/{hubspot-Y77VLY6Y.js.map → hubspot-SVEMFK5S.js.map} +1 -1
  184. package/dist/{icon-I43F77FV.js → icon-L4RSS6PZ.js} +4 -4
  185. package/dist/{ideas-MIDU7YOA.js → ideas-NFOW3HKJ.js} +3 -2
  186. package/dist/{images-S254MS6O.js → images-2EFFUNNV.js} +9 -9
  187. package/dist/{ingest-YV2NLDBE.js → ingest-2EIKN4QC.js} +4 -3
  188. package/dist/{ingest-J4I3HKM3.js → ingest-2SO62RRI.js} +3 -2
  189. package/dist/{ingest-J4I3HKM3.js.map → ingest-2SO62RRI.js.map} +1 -1
  190. package/dist/{inspect-Q5NHBBHX.js → inspect-4OOS4APT.js} +4 -3
  191. package/dist/{inspect-Q5NHBBHX.js.map → inspect-4OOS4APT.js.map} +1 -1
  192. package/dist/{inspiration-P265QJ2H.js → inspiration-S3DU4CMQ.js} +1 -1
  193. package/dist/{keyword-metrics-PDY4ZXSP.js → keyword-metrics-SF4CSNZ2.js} +4 -3
  194. package/dist/{keyword-metrics-PDY4ZXSP.js.map → keyword-metrics-SF4CSNZ2.js.map} +1 -1
  195. package/dist/{keywords-for-site-GALSCV77.js → keywords-for-site-WDAOPNKC.js} +4 -3
  196. package/dist/{keywords-for-site-GALSCV77.js.map → keywords-for-site-WDAOPNKC.js.map} +1 -1
  197. package/dist/{landing-CJYGWCK4.js → landing-2ZB46LXH.js} +3 -3
  198. package/dist/{leads-PHKG6CP3.js → leads-A4W5KTWB.js} +4 -3
  199. package/dist/{leads-PHKG6CP3.js.map → leads-A4W5KTWB.js.map} +1 -1
  200. package/dist/{learn-FUQSY7DN.js → learn-IPULBCOD.js} +5 -4
  201. package/dist/{learn-FUQSY7DN.js.map → learn-IPULBCOD.js.map} +1 -1
  202. package/dist/{line-item-writes-E6MZPIZZ.js → line-item-writes-EMYSOJKM.js} +4 -3
  203. package/dist/{line-item-writes-E6MZPIZZ.js.map → line-item-writes-EMYSOJKM.js.map} +1 -1
  204. package/dist/{line-items-7ZGIKEF2.js → line-items-5NECXUZT.js} +5 -5
  205. package/dist/{link-QYAW6A2W.js → link-CI45HWFN.js} +4 -3
  206. package/dist/{link-QYAW6A2W.js.map → link-CI45HWFN.js.map} +1 -1
  207. package/dist/{linkedin-FFSDSGGS.js → linkedin-B7SYR75X.js} +27 -26
  208. package/dist/{linkedin-FFSDSGGS.js.map → linkedin-B7SYR75X.js.map} +1 -1
  209. package/dist/{list-NVQC5SNY.js → list-2BARXOT5.js} +3 -2
  210. package/dist/{list-NVQC5SNY.js.map → list-2BARXOT5.js.map} +1 -1
  211. package/dist/{list-SAV3LQHY.js → list-BIEVLEHS.js} +4 -3
  212. package/dist/{list-SAV3LQHY.js.map → list-BIEVLEHS.js.map} +1 -1
  213. package/dist/{list-5IC6KIKO.js → list-ZBT3EBVZ.js} +4 -3
  214. package/dist/{list-5IC6KIKO.js.map → list-ZBT3EBVZ.js.map} +1 -1
  215. package/dist/{log-XZPVOALD.js → log-C23OOAJZ.js} +4 -3
  216. package/dist/{log-XZPVOALD.js.map → log-C23OOAJZ.js.map} +1 -1
  217. package/dist/{logo-2IP663QO.js → logo-Q5D7PZY6.js} +3 -3
  218. package/dist/{lookup-XK45W2SH.js → lookup-ETWY47F7.js} +6 -5
  219. package/dist/{make-J3WBO62B.js → make-TPKKJXGL.js} +18 -17
  220. package/dist/{make-J3WBO62B.js.map → make-TPKKJXGL.js.map} +1 -1
  221. package/dist/{map-4J5WIWIG.js → map-SYZMPHLL.js} +5 -4
  222. package/dist/{map-4J5WIWIG.js.map → map-SYZMPHLL.js.map} +1 -1
  223. package/dist/{mcp-XXD7ZQCP.js → mcp-6FQBVJ6F.js} +3 -2
  224. package/dist/{mcp-XXD7ZQCP.js.map → mcp-6FQBVJ6F.js.map} +1 -1
  225. package/dist/{media-sources-O6ZRUA3N.js → media-sources-ZXHEEP6T.js} +2 -2
  226. package/dist/{members-D5ZYLS4Y.js → members-UDY4OOJW.js} +4 -3
  227. package/dist/{members-D5ZYLS4Y.js.map → members-UDY4OOJW.js.map} +1 -1
  228. package/dist/{normalize-PCNC6E2N.js → normalize-DAKHYS3V.js} +7 -3
  229. package/dist/{normalize-PCNC6E2N.js.map → normalize-DAKHYS3V.js.map} +1 -1
  230. package/dist/{options-MQ65VEVD.js → options-QL3ULED5.js} +7 -6
  231. package/dist/{options-MQ65VEVD.js.map → options-QL3ULED5.js.map} +1 -1
  232. package/dist/{performance-6QW2KEEQ.js → performance-UF5VTY2U.js} +5 -4
  233. package/dist/plan-FT632COT.js +51 -0
  234. package/dist/plan-FT632COT.js.map +1 -0
  235. package/dist/{promoted-tweet-writes-JPLHY3GM.js → promoted-tweet-writes-7HGESJ46.js} +4 -3
  236. package/dist/{promoted-tweet-writes-JPLHY3GM.js.map → promoted-tweet-writes-7HGESJ46.js.map} +1 -1
  237. package/dist/{promoted-tweets-SURKMHFI.js → promoted-tweets-LZKS5JIF.js} +3 -3
  238. package/dist/{publish-HCQ6MXLB.js → publish-NPD3SMZL.js} +4 -3
  239. package/dist/{publish-HCQ6MXLB.js.map → publish-NPD3SMZL.js.map} +1 -1
  240. package/dist/questionnaire-JNGYJTAY.js +37 -0
  241. package/dist/questionnaire-JNGYJTAY.js.map +1 -0
  242. package/dist/{reddit-GIP3ADMS.js → reddit-NU2N3LZD.js} +5 -4
  243. package/dist/{refresh-UXQSLCR3.js → refresh-EBVJ2C6L.js} +3 -2
  244. package/dist/{refresh-UXQSLCR3.js.map → refresh-EBVJ2C6L.js.map} +1 -1
  245. package/dist/{release-5KVLXNP6.js → release-V43YB2PY.js} +4 -3
  246. package/dist/{release-5KVLXNP6.js.map → release-V43YB2PY.js.map} +1 -1
  247. package/dist/{remove-6FIVKCJI.js → remove-QPSWLFI4.js} +3 -2
  248. package/dist/{remove-6FIVKCJI.js.map → remove-QPSWLFI4.js.map} +1 -1
  249. package/dist/{reopen-YM5LNOLB.js → reopen-GQ66NMWF.js} +4 -3
  250. package/dist/{reopen-YM5LNOLB.js.map → reopen-GQ66NMWF.js.map} +1 -1
  251. package/dist/{rerun-WYDRHIUJ.js → rerun-PZK275DY.js} +18 -17
  252. package/dist/{rerun-WYDRHIUJ.js.map → rerun-PZK275DY.js.map} +1 -1
  253. package/dist/{research-2BLEISBG.js → research-FMM6AWZC.js} +10 -10
  254. package/dist/{research-YOGE7GT2.js → research-GEOCUBXB.js} +3 -2
  255. package/dist/{research-YOGE7GT2.js.map → research-GEOCUBXB.js.map} +1 -1
  256. package/dist/{reset-C4UHQ3EU.js → reset-REXJ6AUK.js} +3 -2
  257. package/dist/{reset-C4UHQ3EU.js.map → reset-REXJ6AUK.js.map} +1 -1
  258. package/dist/{reset-OLVB6AE6.js → reset-Y72IVESB.js} +3 -2
  259. package/dist/{reset-OLVB6AE6.js.map → reset-Y72IVESB.js.map} +1 -1
  260. package/dist/{resolve-XYH2GDAE.js → resolve-OGX7C4TV.js} +4 -3
  261. package/dist/{resolve-XYH2GDAE.js.map → resolve-OGX7C4TV.js.map} +1 -1
  262. package/dist/{retry-7EELDEQP.js → retry-CD6JAQ3F.js} +3 -2
  263. package/dist/{retry-7EELDEQP.js.map → retry-CD6JAQ3F.js.map} +1 -1
  264. package/dist/{retry-726GKI6V.js → retry-TCUBBFR5.js} +3 -2
  265. package/dist/{retry-726GKI6V.js.map → retry-TCUBBFR5.js.map} +1 -1
  266. package/dist/{run-P4KR2MXC.js → run-A5ZDXTOI.js} +4 -3
  267. package/dist/{run-P4KR2MXC.js.map → run-A5ZDXTOI.js.map} +1 -1
  268. package/dist/{run-MODGUNGT.js → run-EUO34T2J.js} +18 -17
  269. package/dist/{scaffold-ad-5ZE7KE62.js → scaffold-ad-SW2HEWM3.js} +13 -12
  270. package/dist/{scaffold-ad-5ZE7KE62.js.map → scaffold-ad-SW2HEWM3.js.map} +1 -1
  271. package/dist/{scaffold-static-ad-HJY5HFDL.js → scaffold-static-ad-ECTJL54Y.js} +37 -36
  272. package/dist/scaffold-static-ad-ECTJL54Y.js.map +1 -0
  273. package/dist/{scaffold-video-P2PYPWG2.js → scaffold-video-EI7U7RBK.js} +12 -11
  274. package/dist/{scaffold-video-P2PYPWG2.js.map → scaffold-video-EI7U7RBK.js.map} +1 -1
  275. package/dist/{schedule-RDK4GEPI.js → schedule-S5YZJZ5U.js} +3 -2
  276. package/dist/{schedule-RDK4GEPI.js.map → schedule-S5YZJZ5U.js.map} +1 -1
  277. package/dist/{scheduled-agents-6U44QE76.js → scheduled-agents-N4LJRM4C.js} +11 -11
  278. package/dist/{schema-CHJDC7GQ.js → schema-R7JWWOF4.js} +8 -4
  279. package/dist/{schema-CHJDC7GQ.js.map → schema-R7JWWOF4.js.map} +1 -1
  280. package/dist/{search-PMJRU3C4.js → search-KHMQH3AT.js} +3 -2
  281. package/dist/{search-PMJRU3C4.js.map → search-KHMQH3AT.js.map} +1 -1
  282. package/dist/{select-AB2L4GAA.js → select-O2FN2JEQ.js} +9 -8
  283. package/dist/{select-AB2L4GAA.js.map → select-O2FN2JEQ.js.map} +1 -1
  284. package/dist/{sends-WGJVQUMF.js → sends-BQZM5PLS.js} +5 -4
  285. package/dist/{sends-WGJVQUMF.js.map → sends-BQZM5PLS.js.map} +1 -1
  286. package/dist/{set-prompt-DU2G2WTB.js → set-prompt-2HASHGWC.js} +9 -8
  287. package/dist/{set-prompt-DU2G2WTB.js.map → set-prompt-2HASHGWC.js.map} +1 -1
  288. package/dist/{setup-links-REPREN3C.js → setup-links-GYSE56GX.js} +4 -3
  289. package/dist/{setup-links-REPREN3C.js.map → setup-links-GYSE56GX.js.map} +1 -1
  290. package/dist/{site-WFSWIA3S.js → site-4SKM7RKG.js} +5 -4
  291. package/dist/{site-WFSWIA3S.js.map → site-4SKM7RKG.js.map} +1 -1
  292. package/dist/{sources-YBQ6KPFM.js → sources-FQ2UZTHL.js} +6 -6
  293. package/dist/{sources-AMI2AWKZ.js → sources-R2NKNC6J.js} +10 -9
  294. package/dist/{sources-AMI2AWKZ.js.map → sources-R2NKNC6J.js.map} +1 -1
  295. package/dist/{stale-6MJHVEG4.js → stale-W756ACKC.js} +8 -7
  296. package/dist/{stale-6MJHVEG4.js.map → stale-W756ACKC.js.map} +1 -1
  297. package/dist/{status-7F2KHFGA.js → status-SE2VZJJB.js} +4 -3
  298. package/dist/{status-7F2KHFGA.js.map → status-SE2VZJJB.js.map} +1 -1
  299. package/dist/{stock-2YCDN3N6.js → stock-KXJ26BY4.js} +5 -5
  300. package/dist/{studio-RA7QQH4O.js → studio-SYKOSNDB.js} +1 -1
  301. package/dist/{sync-GWTLKZTU.js → sync-LJGSODK3.js} +3 -2
  302. package/dist/{sync-GWTLKZTU.js.map → sync-LJGSODK3.js.map} +1 -1
  303. package/dist/{sync-definition-IPUFS5XS.js → sync-definition-QR2N233R.js} +12 -11
  304. package/dist/{tags-KFYLZLB3.js → tags-4BAVFH4S.js} +3 -2
  305. package/dist/{tags-KFYLZLB3.js.map → tags-4BAVFH4S.js.map} +1 -1
  306. package/dist/{tags-ABHSX6M5.js → tags-5SBMRC2A.js} +4 -3
  307. package/dist/{tags-ABHSX6M5.js.map → tags-5SBMRC2A.js.map} +1 -1
  308. package/dist/{tags-3WPW6PTV.js → tags-PYWFSBTR.js} +4 -4
  309. package/dist/{tags-6342FQEB.js → tags-QQU3OKMZ.js} +4 -3
  310. package/dist/{tags-6342FQEB.js.map → tags-QQU3OKMZ.js.map} +1 -1
  311. package/dist/{tags-FJIBBBRG.js → tags-YYLNF3PK.js} +4 -3
  312. package/dist/{tags-FJIBBBRG.js.map → tags-YYLNF3PK.js.map} +1 -1
  313. package/dist/{targeting-criteria-NR47AF5C.js → targeting-criteria-RJFLFICN.js} +3 -3
  314. package/dist/{targets-L56KOVQE.js → targets-S2MT2PNB.js} +4 -3
  315. package/dist/{targets-L56KOVQE.js.map → targets-S2MT2PNB.js.map} +1 -1
  316. package/dist/{templates-B6XGDYUF.js → templates-ZE5TOGRT.js} +4 -3
  317. package/dist/{templates-B6XGDYUF.js.map → templates-ZE5TOGRT.js.map} +1 -1
  318. package/dist/{testimonials-CVC4YIXO.js → testimonials-DII5DYLR.js} +6 -6
  319. package/dist/{top-companies-45X3DT7Z.js → top-companies-PGIAEHDF.js} +4 -3
  320. package/dist/{top-companies-45X3DT7Z.js.map → top-companies-PGIAEHDF.js.map} +1 -1
  321. package/dist/{translate-Z66FDB4K.js → translate-GXTS633J.js} +12 -11
  322. package/dist/{translate-Z66FDB4K.js.map → translate-GXTS633J.js.map} +1 -1
  323. package/dist/{triggers-22EMIVFD.js → triggers-2PTONXRU.js} +4 -3
  324. package/dist/{triggers-22EMIVFD.js.map → triggers-2PTONXRU.js.map} +1 -1
  325. package/dist/{unlink-65KUN6ID.js → unlink-XRMYJ6DB.js} +4 -3
  326. package/dist/{unlink-65KUN6ID.js.map → unlink-XRMYJ6DB.js.map} +1 -1
  327. package/dist/{unstage-2E3I5SWF.js → unstage-FY7A3YFF.js} +4 -3
  328. package/dist/{unstage-2E3I5SWF.js.map → unstage-FY7A3YFF.js.map} +1 -1
  329. package/dist/{unstage-4ZEULXEQ.js → unstage-RM2RLXKD.js} +5 -4
  330. package/dist/{unstage-4ZEULXEQ.js.map → unstage-RM2RLXKD.js.map} +1 -1
  331. package/dist/{update-KREBPGBH.js → update-E5SRBXQR.js} +4 -3
  332. package/dist/{update-KREBPGBH.js.map → update-E5SRBXQR.js.map} +1 -1
  333. package/dist/{update-D6DJC6T7.js → update-PJ67D2OG.js} +3 -2
  334. package/dist/{update-D6DJC6T7.js.map → update-PJ67D2OG.js.map} +1 -1
  335. package/dist/{update-2S7CSEAW.js → update-S4562V3O.js} +4 -3
  336. package/dist/{update-2S7CSEAW.js.map → update-S4562V3O.js.map} +1 -1
  337. package/dist/{update-7NCOTAQZ.js → update-V6YVEUI2.js} +4 -3
  338. package/dist/{update-7NCOTAQZ.js.map → update-V6YVEUI2.js.map} +1 -1
  339. package/dist/{upload-O6NHRPS7.js → upload-R6CQVLKF.js} +4 -3
  340. package/dist/{upload-O6NHRPS7.js.map → upload-R6CQVLKF.js.map} +1 -1
  341. package/dist/{url-options-GGXAL3J4.js → url-options-JOKU46F2.js} +4 -3
  342. package/dist/{url-options-GGXAL3J4.js.map → url-options-JOKU46F2.js.map} +1 -1
  343. package/dist/{url-params-3EFGVTZZ.js → url-params-7U2XMOWF.js} +4 -3
  344. package/dist/{url-params-3EFGVTZZ.js.map → url-params-7U2XMOWF.js.map} +1 -1
  345. package/dist/{validate-UMHHNTVU.js → validate-7YPOUKPB.js} +10 -9
  346. package/dist/{validate-UMHHNTVU.js.map → validate-7YPOUKPB.js.map} +1 -1
  347. package/dist/{videos-YSXYGTBM.js → videos-LICG6ACJ.js} +5 -5
  348. package/dist/{web-IGIGVEKS.js → web-BA2PGTWU.js} +4 -3
  349. package/dist/{web-IGIGVEKS.js.map → web-BA2PGTWU.js.map} +1 -1
  350. package/dist/{winning-ads-2WS7FI4E.js → winning-ads-C6S2C5VP.js} +2 -2
  351. package/dist/{write-commands-5C7FUOVN.js → write-commands-7SJ3BOW5.js} +5 -4
  352. package/dist/{write-commands-5C7FUOVN.js.map → write-commands-7SJ3BOW5.js.map} +1 -1
  353. package/dist/{x-BFSKFCZD.js → x-CZHRC6U7.js} +6 -6
  354. package/package.json +1 -1
  355. package/dist/add-5GAN6CVO.js.map +0 -1
  356. package/dist/analytics-FKCXIMPO.js.map +0 -1
  357. package/dist/chunk-DWHIOO7E.js.map +0 -1
  358. package/dist/chunk-EDLZGTGN.js.map +0 -1
  359. package/dist/chunk-GKZ2WU75.js.map +0 -1
  360. package/dist/chunk-SSJK7FEV.js.map +0 -1
  361. package/dist/chunk-T2PC5AVH.js.map +0 -1
  362. package/dist/chunk-XF4JUKVF.js.map +0 -1
  363. package/dist/chunk-YRQYA2LR.js.map +0 -1
  364. package/dist/flows-KABCBS3S.js.map +0 -1
  365. package/dist/funnel-AAX27NH4.js.map +0 -1
  366. package/dist/get-QEYJRLV3.js.map +0 -1
  367. package/dist/scaffold-static-ad-HJY5HFDL.js.map +0 -1
  368. /package/dist/{actions-P35MEMGN.js.map → actions-6TQMSVWD.js.map} +0 -0
  369. /package/dist/{ads-73PGA4YC.js.map → ads-AGYCVD62.js.map} +0 -0
  370. /package/dist/{avatars-EKCKHCC3.js.map → avatars-467OSBD6.js.map} +0 -0
  371. /package/dist/{brand-C6PFGGQN.js.map → brand-YRTEPGBK.js.map} +0 -0
  372. /package/dist/{campaigns-KZYZTXDS.js.map → campaigns-OYMXDGWX.js.map} +0 -0
  373. /package/dist/{canvas-UJNSHD3B.js.map → canvas-YQFG52IG.js.map} +0 -0
  374. /package/dist/{catalog-ZQX6BGXS.js.map → catalog-XLXIM7IW.js.map} +0 -0
  375. /package/dist/{chunk-7YCRFMXF.js.map → chunk-24EB7EDR.js.map} +0 -0
  376. /package/dist/{chunk-5ZDLTE5T.js.map → chunk-2AOE2MZQ.js.map} +0 -0
  377. /package/dist/{chunk-4SG4RW2P.js.map → chunk-2VPBIT65.js.map} +0 -0
  378. /package/dist/{chunk-NPY4QUMQ.js.map → chunk-54C6YDGJ.js.map} +0 -0
  379. /package/dist/{chunk-LZ73CZGP.js.map → chunk-5WCSKQ7Z.js.map} +0 -0
  380. /package/dist/{chunk-3G3ZDLMI.js.map → chunk-5ZNTADVR.js.map} +0 -0
  381. /package/dist/{chunk-AEOSAVVL.js.map → chunk-6F7DVPOS.js.map} +0 -0
  382. /package/dist/{chunk-CJCVIEJ5.js.map → chunk-6NY2GPR4.js.map} +0 -0
  383. /package/dist/{chunk-CXWFX2AM.js.map → chunk-6Y7ZBENQ.js.map} +0 -0
  384. /package/dist/{chunk-M242LHLX.js.map → chunk-7CTMH5AK.js.map} +0 -0
  385. /package/dist/{chunk-QD7ESRLN.js.map → chunk-C46GUI2S.js.map} +0 -0
  386. /package/dist/{chunk-6N6WKAJM.js.map → chunk-CWYAELVG.js.map} +0 -0
  387. /package/dist/{chunk-MXYBTMKB.js.map → chunk-DCRPXOJJ.js.map} +0 -0
  388. /package/dist/{chunk-JQJDXPCO.js.map → chunk-FUHN4MFY.js.map} +0 -0
  389. /package/dist/{chunk-S77PZVYM.js.map → chunk-FYGR3I2A.js.map} +0 -0
  390. /package/dist/{chunk-OZRUJVIQ.js.map → chunk-H3427GPU.js.map} +0 -0
  391. /package/dist/{chunk-IR6D7K4H.js.map → chunk-I3ZR655O.js.map} +0 -0
  392. /package/dist/{chunk-YK32GUBN.js.map → chunk-KMKEYI37.js.map} +0 -0
  393. /package/dist/{chunk-W3ZHJJVT.js.map → chunk-KS2QY6MK.js.map} +0 -0
  394. /package/dist/{chunk-WNRV4QH7.js.map → chunk-LYQJ4PZW.js.map} +0 -0
  395. /package/dist/{chunk-CSYSX4D2.js.map → chunk-N4LAYIIN.js.map} +0 -0
  396. /package/dist/{chunk-4AUMTNVB.js.map → chunk-NA63BH7V.js.map} +0 -0
  397. /package/dist/{chunk-4BQXAE7Z.js.map → chunk-NDEE6G2J.js.map} +0 -0
  398. /package/dist/{chunk-MELOSESI.js.map → chunk-R25YLW6H.js.map} +0 -0
  399. /package/dist/{chunk-Y6O7EWYY.js.map → chunk-RL5CP7N6.js.map} +0 -0
  400. /package/dist/{chunk-DGUNI37S.js.map → chunk-RX2DKU3W.js.map} +0 -0
  401. /package/dist/{chunk-VTDMKARH.js.map → chunk-ULG4QRTV.js.map} +0 -0
  402. /package/dist/{chunk-QRP3PXWN.js.map → chunk-VL43TNIP.js.map} +0 -0
  403. /package/dist/{chunk-SHL7LOC3.js.map → chunk-VLMBTPXU.js.map} +0 -0
  404. /package/dist/{chunk-KHRXPQID.js.map → chunk-VYQ3QJ3C.js.map} +0 -0
  405. /package/dist/{chunk-A4LOJ2VD.js.map → chunk-WGUQHAJY.js.map} +0 -0
  406. /package/dist/{chunk-ZEYEWPK6.js.map → chunk-WKQG6TER.js.map} +0 -0
  407. /package/dist/{chunk-3IBYKLXR.js.map → chunk-ZDMTXQ2Q.js.map} +0 -0
  408. /package/dist/{connect-QCOGEPP4.js.map → connect-FCXNVTXG.js.map} +0 -0
  409. /package/dist/{creatives-NJF2U763.js.map → creatives-FXUQ5WSD.js.map} +0 -0
  410. /package/dist/{customers-A36LOZOB.js.map → customers-XH2DMTFH.js.map} +0 -0
  411. /package/dist/{feedback-PPQMOQ46.js.map → feedback-EIZKS4JU.js.map} +0 -0
  412. /package/dist/{find-OTDIX6V4.js.map → find-FRRHAPWX.js.map} +0 -0
  413. /package/dist/{fonts-EMMX3KII.js.map → fonts-MXR3ZKDX.js.map} +0 -0
  414. /package/dist/{ga4-UDQ2HA2J.js.map → ga4-DV2AXCSL.js.map} +0 -0
  415. /package/dist/{google-M7KEXVK5.js.map → google-6OGZX47I.js.map} +0 -0
  416. /package/dist/{google-KTO3PLEU.js.map → google-QYYYSSXI.js.map} +0 -0
  417. /package/dist/{gsc-P3QYGDV2.js.map → gsc-TAKOF7J5.js.map} +0 -0
  418. /package/dist/{icon-I43F77FV.js.map → icon-L4RSS6PZ.js.map} +0 -0
  419. /package/dist/{ideas-MIDU7YOA.js.map → ideas-NFOW3HKJ.js.map} +0 -0
  420. /package/dist/{images-S254MS6O.js.map → images-2EFFUNNV.js.map} +0 -0
  421. /package/dist/{ingest-YV2NLDBE.js.map → ingest-2EIKN4QC.js.map} +0 -0
  422. /package/dist/{inspiration-P265QJ2H.js.map → inspiration-S3DU4CMQ.js.map} +0 -0
  423. /package/dist/{landing-CJYGWCK4.js.map → landing-2ZB46LXH.js.map} +0 -0
  424. /package/dist/{line-items-7ZGIKEF2.js.map → line-items-5NECXUZT.js.map} +0 -0
  425. /package/dist/{logo-2IP663QO.js.map → logo-Q5D7PZY6.js.map} +0 -0
  426. /package/dist/{lookup-XK45W2SH.js.map → lookup-ETWY47F7.js.map} +0 -0
  427. /package/dist/{media-sources-O6ZRUA3N.js.map → media-sources-ZXHEEP6T.js.map} +0 -0
  428. /package/dist/{performance-6QW2KEEQ.js.map → performance-UF5VTY2U.js.map} +0 -0
  429. /package/dist/{promoted-tweets-SURKMHFI.js.map → promoted-tweets-LZKS5JIF.js.map} +0 -0
  430. /package/dist/{reddit-GIP3ADMS.js.map → reddit-NU2N3LZD.js.map} +0 -0
  431. /package/dist/{research-2BLEISBG.js.map → research-FMM6AWZC.js.map} +0 -0
  432. /package/dist/{run-MODGUNGT.js.map → run-EUO34T2J.js.map} +0 -0
  433. /package/dist/{scheduled-agents-6U44QE76.js.map → scheduled-agents-N4LJRM4C.js.map} +0 -0
  434. /package/dist/{sources-YBQ6KPFM.js.map → sources-FQ2UZTHL.js.map} +0 -0
  435. /package/dist/{stock-2YCDN3N6.js.map → stock-KXJ26BY4.js.map} +0 -0
  436. /package/dist/{studio-RA7QQH4O.js.map → studio-SYKOSNDB.js.map} +0 -0
  437. /package/dist/{sync-definition-IPUFS5XS.js.map → sync-definition-QR2N233R.js.map} +0 -0
  438. /package/dist/{tags-3WPW6PTV.js.map → tags-PYWFSBTR.js.map} +0 -0
  439. /package/dist/{targeting-criteria-NR47AF5C.js.map → targeting-criteria-RJFLFICN.js.map} +0 -0
  440. /package/dist/{testimonials-CVC4YIXO.js.map → testimonials-DII5DYLR.js.map} +0 -0
  441. /package/dist/{videos-YSXYGTBM.js.map → videos-LICG6ACJ.js.map} +0 -0
  442. /package/dist/{winning-ads-2WS7FI4E.js.map → winning-ads-C6S2C5VP.js.map} +0 -0
  443. /package/dist/{x-BFSKFCZD.js.map → x-CZHRC6U7.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/flows/shared.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n FLOW_ACCOUNT_ONLY_KINDS,\n FLOW_AGENT_DESTINATION_KINDS,\n FLOW_RESOURCE_NODE_TYPES,\n FLOW_SECRET_FIELDS,\n FLOW_SECRET_SIDE_EFFECT_TYPES,\n type FlowConfigStatus,\n type FlowSideEffectType,\n isFlowOauthSideEffect,\n} from \"@baker/api\";\nimport { writeJson } from \"../../output.ts\";\n\nconst FLOWS_DIR = \"src/lib/flow-engine/flows\";\n\n/** Resolve the flows directory from cwd (walk up a few levels to find the repo root). */\nexport function flowsDir(): string {\n let dir = process.cwd();\n for (let i = 0; i < 6; i++) {\n const candidate = join(dir, FLOWS_DIR);\n if (existsSync(candidate)) {\n return candidate;\n }\n const parent = join(dir, \"..\");\n if (parent === dir) {\n break;\n }\n dir = parent;\n }\n return join(process.cwd(), FLOWS_DIR);\n}\n\nexport function failLocal(message: string): never {\n writeJson({ ok: false, error: { code: \"NOT_FOUND\", message } });\n process.exit(1);\n}\n\nexport function listFlowSlugs(): string[] {\n const dir = flowsDir();\n if (!existsSync(dir)) {\n return [];\n }\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith(\"_\") && entry.name !== \".gitkeep\")\n .map((entry) => entry.name)\n .sort();\n}\n\ntype AnyRecord = Record<string, unknown>;\n\n/** Absolute path to a form's `_data.json` — the one file the agent may edit. */\nexport function flowDataPath(slug: string): string {\n return join(flowsDir(), slug, \"_data.json\");\n}\n\n/**\n * Write a form tree back. Two-space JSON with a trailing newline, matching what\n * the scaffolder and the repo's markdown/JSON formatting already produce — a\n * write in any other shape turns a one-field edit into a whole-file diff.\n */\nexport function writeFlowTree(slug: string, tree: AnyRecord): void {\n writeFileSync(flowDataPath(slug), `${JSON.stringify(tree, null, 2)}\\n`, \"utf-8\");\n}\n\n/**\n * Every node in the tree, depth-first, parents before children.\n *\n * `Array.isArray`, not `?? []`: `_data.json` is hand-authored, so `children`\n * can be an object or a string, and `??` only guards null. These walkers are\n * what `flows show` and the pre-commit `normalize` run over, which are the two\n * things reached for when a form file is already wrong — throwing there would\n * take out the diagnosis along with the form.\n */\nexport function walkNodes(node: AnyRecord, acc: AnyRecord[] = []): AnyRecord[] {\n if (!node || typeof node !== \"object\") return acc;\n acc.push(node);\n if (Array.isArray(node.children)) {\n for (const child of node.children as AnyRecord[]) walkNodes(child, acc);\n }\n return acc;\n}\n\n/**\n * Every side effect in the tree, with the node that owns it.\n *\n * Walked rather than read off the root: side effects hang off whichever node\n * fires them, so a multi-step form keeps them on a child step. Reading\n * `tree.sideEffects` alone reports \"no side effects\" for a form that plainly\n * has one.\n */\nexport function collectSideEffects(tree: AnyRecord): Array<{ node: AnyRecord; sideEffect: AnyRecord }> {\n return walkNodes(tree).flatMap((node) =>\n Array.isArray(node.sideEffects)\n ? (node.sideEffects as AnyRecord[])\n .filter((sideEffect): sideEffect is AnyRecord => Boolean(sideEffect) && typeof sideEffect === \"object\")\n .map((sideEffect) => ({ node, sideEffect }))\n : [],\n );\n}\n\n/** Whether this workspace holds a Form by that slug. Never exits. */\nexport function flowExists(slug: string): boolean {\n return existsSync(join(flowsDir(), slug, \"_data.json\"));\n}\n\nexport function readFlowTree(slug: string): AnyRecord {\n const path = join(flowsDir(), slug, \"_data.json\");\n if (!existsSync(path)) {\n failLocal(`No form \"${slug}\". Run \"baker flows list\" to see the forms in this workspace.`);\n }\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as AnyRecord;\n } catch (error) {\n failLocal(`Could not read form \"${slug}\": ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nfunction isConfigured(value: unknown): boolean {\n return typeof value === \"string\" && value.length > 0;\n}\n\ntype NodeMeta = { nodeId: string; nodeName?: string };\n\nfunction nodeMeta(record: AnyRecord): NodeMeta {\n const nodeId = typeof record.id === \"string\" ? record.id : \"\";\n const nodeName = typeof record.name === \"string\" ? record.name : undefined;\n return { nodeId, ...(nodeName ? { nodeName } : {}) };\n}\n\nfunction widgetNodeStatus(record: AnyRecord, meta: NodeMeta): FlowConfigStatus | null {\n const nodeData = record.nodeData as AnyRecord | undefined;\n const nodeType = nodeData?.type;\n if (typeof nodeType !== \"string\" || !(FLOW_RESOURCE_NODE_TYPES as readonly string[]).includes(nodeType)) {\n return null;\n }\n const external = (nodeData?.form as AnyRecord | undefined)?.external;\n const selected = external != null && typeof external === \"object\";\n const name = selected ? (external as AnyRecord).name : undefined;\n return {\n ...meta,\n target: \"node\",\n kind: nodeType,\n status: selected\n ? `${nodeType} — selected${typeof name === \"string\" ? ` \"${name}\"` : \"\"}`\n : `${nodeType} — not selected`,\n needsInput: !selected,\n };\n}\n\n/**\n * Whether this side effect still needs something only the user can give.\n *\n * `false` for a type that has no confidential half at all (a pixel takes a tag\n * ref and nothing else), so a caller can ask the question of any side effect\n * without first checking whether the question applies.\n */\nexport function sideEffectNeedsInput(raw: unknown): boolean {\n const effect = raw as AnyRecord | null;\n const type = effect?.type;\n if (typeof type !== \"string\" || !(FLOW_SECRET_SIDE_EFFECT_TYPES as readonly string[]).includes(type)) {\n return false;\n }\n const configured = isConfigured(effect?.encryptedConfig);\n if (!isFlowOauthSideEffect(type as FlowSideEffectType)) return !configured;\n return !isConfigured(effect?.oauthProviderId) || !configured;\n}\n\nfunction sideEffectStatus(raw: unknown, meta: NodeMeta): FlowConfigStatus | null {\n const effect = raw as AnyRecord | null;\n const type = effect?.type;\n if (typeof type !== \"string\" || !(FLOW_SECRET_SIDE_EFFECT_TYPES as readonly string[]).includes(type)) {\n return null;\n }\n const configured = isConfigured(effect?.encryptedConfig);\n const oauth = isFlowOauthSideEffect(type as FlowSideEffectType);\n const connected = isConfigured(effect?.oauthProviderId);\n const needs = FLOW_SECRET_FIELDS[type as FlowSideEffectType];\n const status = oauth\n ? `${type} — connection ${connected ? \"[connected]\" : \"[needs connection]\"}, config ${configured ? \"[set]\" : \"[missing]\"}`\n : configured\n ? `${type} — [configured]`\n : `${type} — [missing]${needs.length > 0 ? ` (needs: ${needs.join(\", \")})` : \"\"}`;\n return {\n ...meta,\n target: \"sideEffect\",\n ...(typeof effect?.id === \"string\" ? { sideEffectId: effect.id } : {}),\n kind: type,\n status,\n needsInput: sideEffectNeedsInput(effect),\n };\n}\n\n/**\n * The exact call that fills a still-unconfigured item — `baker flows connect`\n * for anything the agent can wire itself, `request_flow_input` for the rest\n * and as the fallback.\n *\n * Emitted as a ready-to-issue call rather than a reminder that one exists. The\n * argument set is not guessable — it needs the node id, and for a side effect\n * the side-effect id too — so a hint that only named the tool would still cost\n * the agent a `flows show --full` to assemble.\n */\nexport function requestFlowInputCall(slug: string, status: FlowConfigStatus): string {\n const args =\n status.target === \"node\"\n ? `{ target: \"node\", flowSlug: \"${slug}\", nodeId: \"${status.nodeId}\", nodeType: \"${status.kind}\" }`\n : `{ target: \"sideEffect\", flowSlug: \"${slug}\", nodeId: \"${status.nodeId}\", sideEffectId: \"${status.sideEffectId}\", sideEffectType: \"${status.kind}\" }`;\n const card = `request_flow_input with ${args}`;\n const target = status.target === \"node\" ? `--node ${status.nodeId}` : `--side-effect ${status.sideEffectId}`;\n const fallback = \"only if nothing is connected or several could be meant\";\n if (status.kind === \"hubspotForm\") {\n return `baker flows connect ${slug} ${target} --hubspot-form <formId from baker hubspot forms list> — ${fallback}: ${card}`;\n }\n if (status.kind === \"hubspot\" || status.kind === \"hubspotMeeting\") {\n const list = status.kind === \"hubspot\" ? \"forms view <id>\" : \"meetings view <slug>\";\n return `write \\`baker hubspot ${list} --as-node\\` into the step's form.external — ${fallback}: ${card}`;\n }\n if ((FLOW_ACCOUNT_ONLY_KINDS as readonly string[]).includes(status.kind)) {\n return `baker flows connect ${slug} ${target} — ${fallback}: ${card}`;\n }\n if ((FLOW_AGENT_DESTINATION_KINDS as readonly string[]).includes(status.kind)) {\n const sheet = status.kind === \"googleSpreadsheet\" ? ' (or --new-sheet \"<title>\" --columns \"…\")' : \"\";\n return `baker flows connect ${slug} ${target} --resource <id from baker flows options ${status.kind}>${sheet} — ${fallback}: ${card}`;\n }\n return card;\n}\n\n/**\n * Walk a local flow tree collecting the configuration status of every\n * confidential field — WITHOUT decrypting anything. A present `encryptedConfig`\n * string / `oauthProviderId` / `form.external` object means \"configured\"; null /\n * absent means the agent still needs to run `request_flow_input` for it.\n */\nexport function collectConfigStatus(node: unknown, acc: FlowConfigStatus[]): void {\n if (!node || typeof node !== \"object\") {\n return;\n }\n const record = node as AnyRecord;\n const meta = nodeMeta(record);\n\n const widget = widgetNodeStatus(record, meta);\n if (widget) {\n acc.push(widget);\n }\n if (Array.isArray(record.sideEffects)) {\n for (const raw of record.sideEffects) {\n const status = sideEffectStatus(raw, meta);\n if (status) {\n acc.push(status);\n }\n }\n }\n if (Array.isArray(record.children)) {\n for (const child of record.children) {\n collectConfigStatus(child, acc);\n }\n }\n}\n\n/** Replace opaque ciphertext with a status marker so `--full` stays readable and never leaks noise. */\nexport function redactTree(node: unknown): unknown {\n if (Array.isArray(node)) {\n return node.map(redactTree);\n }\n if (!node || typeof node !== \"object\") {\n return node;\n }\n const out: AnyRecord = {};\n for (const [key, value] of Object.entries(node as AnyRecord)) {\n if (key === \"encryptedConfig\") {\n out[key] = isConfigured(value) ? \"[configured]\" : value;\n } else {\n out[key] = redactTree(value);\n }\n }\n return out;\n}\n\n/**\n * The constant a step's name becomes in the generated `_query.ts`.\n *\n * Byte-for-byte the scaffold's own `toScreamingSnake`\n * (`__tooling__/checks/base/flow-typed/flow-typed.mjs`), which is the only implementation\n * that decides anything: the watcher writes `export const <key> = \"<node id>\";`\n * for every step and throws `Duplicate flow node name` when two steps produce\n * the same one. `flows-step-name-key.test.ts` pins the two together.\n *\n * It is copied rather than imported because the CLI ships as a binary into a\n * Session Runtime and the scaffold ships as files — there is no module boundary\n * they share. The copy that was here before was NOT this rule: it uppercased and\n * squashed every non-alphanumeric run, which agrees on `Contact Info` and\n * disagrees on `contactInfo` (`CONTACTINFO` here, `CONTACT_INFO` there). So\n * `add-node` waved through a name that collided in the generator, the watcher\n * then threw, and `_query.ts` stopped regenerating for the whole form.\n */\nexport function stepNameKey(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, \"$1_$2\")\n .replace(/[-\\s]+/g, \"_\")\n .toUpperCase();\n}\n\n/**\n * Whether that constant is one TypeScript will accept.\n *\n * `flow-typed.mjs` interpolates the key straight into `export const <key> = …`\n * without checking, so a step called \"1. Your details\" or \"Thanks!\" writes a\n * `_query.ts` that is a syntax error, and the whole workspace stops building —\n * a failure whose message names the generated file rather than the step that\n * caused it. `add-node` is where a name is minted, so it is where this is worth\n * catching.\n *\n * The real identifier grammar, not `[A-Z0-9_]`: this product is Spanish-first\n * and `INFORMACIÓN_DE_CONTACTO` is both a legal identifier and an ordinary step\n * name. Rejecting it would be the check being wrong, not the name.\n */\nexport function isUsableStepNameKey(key: string): boolean {\n return /^[\\p{ID_Start}$_][\\p{ID_Continue}$‌‍]*$/u.test(key);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/flows/index.ts"],"sourcesContent":["import {\n driftRepairHint,\n FLOW_AGENT_DESTINATION_KINDS,\n type FlowConfigStatus,\n type FlowDrift,\n type FlowSummary,\n type FlowsDriftResponse,\n type FlowsShowResponse,\n flowNodeTriggers,\n} from \"@baker/api\";\nimport { defineCommand } from \"citty\";\nimport { apiPost } from \"../../client.ts\";\nimport { writeJson } from \"../../output.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\nimport {\n collectConfigStatus,\n collectSideEffects,\n listFlowSlugs,\n readFlowTree,\n redactTree,\n requestFlowInputCall,\n} from \"./shared.ts\";\n\nregisterSchema({\n command: \"flows.list\",\n description:\n \"List the Forms (flows) in this workspace and how many confidential fields each still needs configured. Read-only.\",\n args: {},\n});\n\nregisterSchema({\n command: \"flows.show\",\n description:\n \"Show a Form's confidential fields and whether each is configured (secrets/connections/third-party resources), AND whether its destination has changed under it — never reveals secret values. Destination drift is the one problem a Form cannot show you itself: when a HubSpot field a Form maps is deleted, HubSpot refuses the WHOLE submission and the visitor still sees the thank-you screen, so every lead is lost silently. Run this before changing any Form. Author structure by editing the flow's _data.json; configure confidential fields with the request_flow_input tool.\",\n args: {\n slug: { type: \"positional\", description: \"Form slug (folder under flows/)\", required: true },\n full: { type: \"boolean\", description: \"Include the full flow tree (secret values redacted)\", required: false },\n },\n});\n\nfunction statusFor(slug: string): FlowConfigStatus[] {\n const tree = readFlowTree(slug);\n const config: FlowConfigStatus[] = [];\n collectConfigStatus(tree, config);\n return config;\n}\n\n/**\n * The calls that close out everything this form is still waiting on.\n *\n * `show` is the command an agent runs to answer \"is this form wired up?\", and\n * it answered with `needsInput: true` and nothing else — leaving the agent to\n * work out that a tool exists, which tool, and how to address the exact node or\n * side effect. The whole point of asking is to find out what to do next, so the\n * answer is the call itself.\n */\nfunction pendingInputHints(slug: string, config: FlowConfigStatus[]): string[] {\n const pending = config.filter((row) => row.needsInput);\n if (pending.length === 0) return [];\n const calls = pending.slice(0, MAX_PENDING_HINTS).map((row) => `${row.kind}: ${requestFlowInputCall(slug, row)}`);\n const rest = pending.length - calls.length;\n return [\n ...repeatedKindHints(slug, pending),\n `${pending.length} item${pending.length === 1 ? \"\" : \"s\"} still unwired. Wire each yourself with the command below when you know which resource is meant; the card is the fallback, and its result carries the destination — read the names from there rather than looking them up again.`,\n ...calls,\n ...(rest > 0 ? [`…and ${rest} more — see \\`config\\` above.`] : []),\n ];\n}\n\n/**\n * Several unwired items of one kind usually share one destination — chat\n * r57ed43j had 24 HubSpot side effects all sending to the same form — so\n * name the call that fills them all at once.\n */\nfunction repeatedKindHints(slug: string, pending: FlowConfigStatus[]): string[] {\n const counts = new Map<string, number>();\n for (const row of pending) counts.set(row.kind, (counts.get(row.kind) ?? 0) + 1);\n return [...counts].flatMap(([kind, count]) => {\n if (count < 2) return [];\n if (kind === \"hubspotForm\") {\n return [\n `${count} hubspotForm side effects are unwired. When they all send to the same HubSpot form: baker flows connect ${slug} --all --hubspot-form <formId>.`,\n ];\n }\n if (!(FLOW_AGENT_DESTINATION_KINDS as readonly string[]).includes(kind)) return [];\n return [\n `${count} ${kind} items are unwired. When they share one destination: baker flows connect ${slug} --all --type ${kind} [--resource <id>].`,\n ];\n });\n}\n\n/** Enough for any real form; the cap only stops a pathological tree flooding the response. */\nconst MAX_PENDING_HINTS = 6;\n\n/**\n * Side effects wired to a moment their own step never raises.\n *\n * `fireSideEffects` filters on `triggerId` equality, so one that does not match\n * is inert: written, valid, green on every check in the repo, and it never\n * runs. Nothing else in the toolchain looks at the pair — `validate-flow.mjs`\n * only checks the key is present and `validate-flow-schema.mjs` does not read\n * it at all — so a form can report \"fully configured\" while a side effect on it\n * has never fired once. This is the one command that is asked whether a form is\n * wired up, so it is the one that has to say.\n *\n * Silent about a node type the trigger table does not cover: no opinion is the\n * honest answer there, and a false \"this never fires\" on a working form would\n * cost more than the warning is worth.\n */\nexport function inertSideEffectHints(tree: Record<string, unknown>): string[] {\n const dead = collectSideEffects(tree).flatMap(({ node, sideEffect }) => {\n const triggers = flowNodeTriggers(node.nodeData);\n if (!triggers.known) return [];\n const triggerId = String(sideEffect.triggerId ?? \"\");\n if (triggers.available.includes(triggerId)) return [];\n const where = `${String(sideEffect.id ?? sideEffect.type)} on \"${String(node.name ?? node.id)}\"`;\n const raises =\n triggers.available.length > 0\n ? `It raises: ${triggers.available.join(\", \")}.`\n : (triggers.why ?? \"That step raises nothing.\");\n return [`${where} fires on \\`${triggerId || \"(none)\"}\\`, which that step never raises. ${raises}`];\n });\n\n if (dead.length === 0) return [];\n return [\n `${dead.length} side effect${dead.length === 1 ? \" has\" : \"s have\"} a triggerId their step never raises, so ${dead.length === 1 ? \"it never runs\" : \"they never run\"} — no error, nothing half-written, just nothing. A triggerId belongs to the STEP, not to the side-effect type.`,\n ...dead.slice(0, MAX_PENDING_HINTS),\n ...(dead.length > MAX_PENDING_HINTS ? [`…and ${dead.length - MAX_PENDING_HINTS} more.`] : []),\n \"Fix each by setting the id its own step raises: `baker flows schema node <nodeType>` lists them.\",\n ];\n}\n\n/**\n * Where the company funnel stands on this Form.\n *\n * A Form moves nobody along the company funnel until a step names one of its\n * endings, and nothing places it for the agent — so a Form that is configured,\n * fires every pixel and counts its conversions can still leave \"Became a lead\"\n * at zero. `show` reads only the local file and cannot tell whether a step\n * names it; `baker funnel` can, so the hint names that read and the write.\n */\nexport function funnelPlacementHint(slug: string): string {\n return `Whether this Form moves anyone along the company funnel is not in _data.json either: \\`baker funnel\\` names every Form no step counts. If it names this one, place its ending on the step it moves people to — \\`submit:${slug}\\`, or its booking trigger, both listed by \\`baker funnel --moments\\` — with \\`baker funnel --step\\`, which replaces the step it names, so keep that step's other ways in.`;\n}\n\n/**\n * What this Form's destination has changed under it, asked of Baker.\n *\n * A read the Company Workspace cannot do for itself twice over: the findings\n * live on the `flows` row, and the HubSpot form id a repair needs is inside an\n * encrypted config. Before this, detection had no reader in the Runtime at all\n * — 47 live findings across 11 Forms, and an agent editing one of those Forms\n * could not see a single one.\n *\n * A failure returns null rather than `[]`. They are different answers and\n * collapsing them would report a Form healthy on the strength of a network\n * error, which is exactly the silence the drift check exists to break.\n */\nasync function readDrift(slug: string): Promise<FlowsDriftResponse | null> {\n try {\n const response = await apiPost<{ ok: true; data: FlowsDriftResponse }>(\"/api/flows/drift\", { slug });\n return response.data;\n } catch {\n return null;\n }\n}\n\n/**\n * Drift said to an agent: what is wrong, and the command that fixes it.\n *\n * The summary comes from `@baker/api`, which the dashboard badge reads too, so\n * this and the badge cannot come apart about whether a Form is healthy. What is\n * added here is the half that is the CLI's: the repair, spelled as a command\n * line, because an agent that has to go and work out `--prune-missing` for\n * itself will instead reach for `--replace` and delete the mappings that still\n * work.\n */\nexport function driftHints(slug: string, drift: FlowsDriftResponse | null): string[] {\n if (drift === null) {\n return [\n \"Could not check whether this Form's destination has changed under it. Treat that as UNKNOWN, not as healthy — a deleted HubSpot field refuses every submission while the visitor still sees the thank-you screen.\",\n ];\n }\n\n const forms = drift.forms.filter((form) => form.slug === slug);\n if (forms.length === 0) {\n return drift.checkedAt === null\n ? [\n \"This Form's destination has never been checked, so nothing here says it is healthy — only that nothing was compared.\",\n ]\n : [];\n }\n\n return forms.flatMap((form: FlowDrift) => {\n const repair = driftRepairHint(slug, form.formGuid, form.deadFields, form.sideEffectId);\n return [`DESTINATION DRIFT (${form.severity}): ${form.summary}`, ...(repair === null ? [] : [repair])];\n });\n}\n\nfunction displayName(slug: string): string {\n const tree = readFlowTree(slug);\n const name = tree.displayName;\n return typeof name === \"string\" && name.trim() ? name.trim() : slug;\n}\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list\",\n },\n run: () => {\n const flows: FlowSummary[] = listFlowSlugs().map((slug) => ({\n slug,\n name: displayName(slug),\n needsConfig: statusFor(slug).filter((c) => c.needsInput).length,\n }));\n const pending = flows.filter((flow) => flow.needsConfig > 0);\n writeJson({\n ok: true,\n data: { flows },\n hints: [\n ...(pending.length > 0\n ? [\n `Still waiting on the user: ${pending.map((flow) => `${flow.slug} (${flow.needsConfig})`).join(\", \")}. \\`baker flows show <slug>\\` returns the request_flow_input call for each.`,\n ]\n : []),\n // \"Set the conversions on the Forms\" lands here first, and nothing in\n // this family can do it — the conversion side effects it does offer\n // fire a pixel and are not what any Baker report counts.\n \"Setting what a Form COUNTS is not in this family: a conversion is a named event held in Baker. `baker analytics conversions` lists what this company counts; `--flow <slug>` spells one Form's endings as keys you can count, traffic or no traffic.\",\n ],\n });\n },\n});\n\nconst showCommand = defineCommand({\n meta: {\n name: \"show\",\n description:\n \"Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact\",\n },\n args: {\n slug: { type: \"positional\", description: \"Form slug\", required: true },\n full: { type: \"boolean\", description: \"Include the full flow tree (secret values redacted)\" },\n },\n run: async ({ args }) => {\n const slug = String(args.slug);\n const config = statusFor(slug);\n const tree = readFlowTree(slug);\n const drift = await readDrift(slug);\n const response: FlowsShowResponse = {\n slug,\n name: displayName(slug),\n config,\n drift: drift === null ? null : drift.forms.filter((form) => form.slug === slug),\n ...(drift === null ? {} : { driftCheckedAt: drift.checkedAt }),\n ...(args.full === true ? { tree: redactTree(tree) } : {}),\n };\n writeJson({\n ok: true,\n data: response,\n // Drift first, above everything. The other hints are about work still to\n // do; this one is about leads already being lost, and it is the only\n // thing here that is true of a Form that looks completely finished.\n hints: [\n ...driftHints(slug, drift),\n ...inertSideEffectHints(tree),\n ...pendingInputHints(slug, config),\n // The question this command is asked — \"is this Form wired up?\" — has\n // an answer that is not in this file at all. What a Form counts is a\n // named event in Baker, and a Form can be perfectly configured, fire\n // every pixel it has, and still report converting nobody.\n `What this Form counts as a conversion is NOT in _data.json and not in any side effect here: it is a named event held in Baker. Read it, and this Form's own endings spelled as event keys, with \\`baker analytics conversions --flow ${slug}\\`.`,\n funnelPlacementHint(slug),\n ],\n });\n },\n});\n\nexport const flowsCommand = defineCommand({\n meta: {\n name: \"flows\",\n description: `Read and shape this workspace's Forms (flows).\n\nReads never reveal a secret value. Writes touch only the form's own _data.json — they stage structure, never a live system.\n\nWire a connected platform yourself once you know which resource is meant — no card for the user: \\`baker flows options <kind>\\` lists what the account holds, \\`baker flows connect\\` writes it (Calendly, SavvyCal, GoHighLevel steps; Google Sheets, Pipedrive, GoHighLevel and HubSpot destinations; \\`--same-as\\` copies another Form's). A HubSpot step takes \\`baker hubspot forms view <id> --as-node\\`. Anything sealed is written sealed; you never see a secret.\n\nThe request_flow_input tool (baker_ui MCP server) is the fallback: typed secrets (webhooks, Zapier, Crmble), a platform nobody connected, the user's own existing spreadsheet, or several resources that could be meant.\n\nExamples:\n baker flows list # every Form + how many fields still need setup\n baker flows show contact # one Form's confidential fields and their status\n baker flows schema node customForm # the shape of a step, before you author one\n baker flows add-node contact --type text --name \"Thanks\"\n baker flows add-side-effect contact --node <id> --type googleSpreadsheet\n baker flows options calendly # what the connected account holds\n baker flows connect contact --node book --resource <id>\n baker flows connect contact --side-effect se-hubspotForm-1 --hubspot-form <formId> # --all: every unwired one\n baker flows map contact --map A=name --map B=email --write\nFull guide: __tooling__/docs/tools/baker/flows.md`,\n },\n subCommands: {\n list: listCommand,\n show: showCommand,\n schema: () => import(\"./schema.ts\").then((m) => m.schemaCommand),\n \"add-node\": () => import(\"./add.ts\").then((m) => m.addNodeCommand),\n \"add-side-effect\": () => import(\"./add.ts\").then((m) => m.addSideEffectCommand),\n options: () => import(\"./options.ts\").then((m) => m.optionsCommand),\n connect: () => import(\"./connect.ts\").then((m) => m.connectCommand),\n map: () => import(\"./map.ts\").then((m) => m.mapCommand),\n normalize: () => import(\"./normalize.ts\").then((m) => m.normalizeCommand),\n },\n // Bare `baker flows` lists the Forms. This must be `default`, not `run`: citty falls\n // through to `run` after dispatching a subcommand, so a `run` here would also print\n // the list on every `baker flows show <slug>`.\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/funnel/index.ts","../src/commands/funnel/formMoments.ts","../../api/src/funnel/vocabulary.ts","../../api/src/funnel/stepTemplates.ts","../../api/src/funnel/wire.ts","../src/commands/funnel/funnelArgs.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { FunnelRequest, FunnelResponse } from \"@baker/api/funnel\";\nimport { defineCommand } from \"citty\";\nimport { ApiError, apiPost } from \"../../client.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\nimport { writeJsonEnvelope } from \"../ads/output.ts\";\nimport { flowDataPath, listFlowSlugs } from \"../flows/shared.ts\";\nimport { formsNamedHere, unplacedFormHints, type WorkspaceForm, withFormMoments } from \"./formMoments.ts\";\nimport { FunnelArgsError, funnelWrite, parseFunnelArgs } from \"./funnelArgs.ts\";\n\n/**\n * `baker funnel` — the company's own funnel: the steps it has, in its own\n * words, wired the way its business actually runs.\n *\n * One command rather than a family, because there is one thing to say: this is\n * the funnel. A read with no flags, a write that restates the whole graph. It\n * is the only staged write in the CLI that does not take operations, and the\n * reason is in `@baker/api/funnel/wire.ts`: a funnel is a graph of a handful of\n * steps, so restating it costs nothing and the person reviewing it sees the\n * funnel they will get rather than a list of edits.\n */\nconst args = {\n steps: {\n type: \"string\",\n description:\n 'The WHOLE funnel, as a JSON array. One object per step: {\"key\":\"meeting_held\",\"displayName\":\"Demo held\",\"stepClass\":\"engaged\",\"after\":[\"lead\"],\"triggers\":[{\"id\":\"t1\",\"kind\":\"event\",\"eventKey\":\"page:demo_held\"}]}. It REPLACES the funnel rather than adding to it, so read first and restate everything you want to keep. `key` is permanent and reaches reporting and URLs (lowercase, digits, underscores); `displayName` is the client\\'s own words and can be anything. `stepClass` is the stage, one of visited, lead, engaged, customer, repeat, lost — six plain words every business has, used to compare one company\\'s funnel with another\\'s. `after` is a LIST, so two ways into one step is expressible and normal. `triggers` is what MOVES somebody onto the step, any one of them being enough, and there are three kinds: `{\"id\":\"t1\",\"kind\":\"event\",\"eventKey\":\"submit:contact\"}` for anything the company\\'s Forms, pages or systems produced, used by its own key with nothing named first; `{\"id\":\"t2\",\"kind\":\"landing\",\"landingId\":\"<from --moments>\"}` for one landing page being viewed; `{\"id\":\"t3\",\"kind\":\"any_landing\"}` for any landing being viewed. Take every key and id from `--moments`. A trigger carries no amount: an event is worth what it says, and a step is worth the sum. Where a step SENDS its people is not written here: a send belongs to the system\\'s event (`baker analytics sends`), and the step reads it back as `sends[]`',\n required: false,\n },\n step: {\n type: \"string\",\n description:\n 'ONE step as a JSON object, in the same shape as an entry of --steps. It is added to the funnel as it now stands, or replaces the step with the same `key`, and every other step is left exactly as it is — the door for adding or changing a stage without restating the rest. Example: --step \\'{\"key\":\"demo\",\"displayName\":\"Demo held\",\"stepClass\":\"engaged\",\"after\":[\"lead\"],\"triggers\":[{\"id\":\"t1\",\"kind\":\"event\",\"eventKey\":\"page:demo_held\"}]}\\'',\n required: false,\n },\n \"add-trigger\": {\n type: \"string\",\n description:\n 'The key of ONE step to add a way in to, with `--event` naming what gets somebody there: `--add-trigger lead --event \"submit:contact\"`. Start here for \"this Form should count as a lead\" — it adds that one way in and leaves the step\\'s name, stage, `after[]` and every other trigger exactly as they are. Prefer it to `--step`, which takes a WHOLE step and therefore silently removes any trigger the restatement forgets. Adding a key the step already listens for changes nothing. Take the key from `--moments`, never from memory',\n required: false,\n },\n \"remove-trigger\": {\n type: \"string\",\n description:\n 'The key of ONE step to take a way in OFF, with `--event` naming which: `--remove-trigger lead --event \"submit:contact\"`. By event key rather than by trigger id, because the id is not something you ever saw. Everything else about the step is untouched',\n required: false,\n },\n event: {\n type: \"string\",\n description:\n \"The event key `--add-trigger` or `--remove-trigger` acts on — an `events[].eventKey` from `--moments`, never invented. Every Form's endings are listed there by the step each happens on, at zero before anybody has filled the Form in; which one means the step is your decision\",\n required: false,\n },\n \"remove-step\": {\n type: \"string\",\n description:\n \"The key of one step to take off the funnel. Whatever came after it moves up to whatever came before it, so the chain stays joined. The response lists it under `removed`\",\n required: false,\n },\n preset: {\n type: \"string\",\n description:\n \"Draw one of the ready-made graphs instead of stating steps: b2b_sales, b2b_inbound, saas_self_serve, saas_sales_assisted, ecommerce_purchase. It draws over the whole funnel — every company already has one, so prefer --step to change a step. Run --templates first to see what each one contains\",\n required: false,\n },\n moments: {\n type: \"boolean\",\n description:\n \"Also return everything a trigger can name, so a trigger is PICKED rather than invented: `events` (every event this company's Forms, pages and systems produced in the window, with volume, the Form it belongs to, `origin` — `site` for a page or Form, `systems` for the client's own server, `both` — and `countedAs` when the company also counts it as a page conversion, which a trigger does not need; PLUS **every workspace Form's endings** — each step's completion and its `submit:` — with the `step` each happens on, at zero and `seen: false` until somebody fires one, so a Form built minutes ago is pickable with nothing set up first, and `countedAs` when the company already counts it. Nothing is marked as the outcome: which ending means a step is YOUR decision — read the step names, and ask the person when they do not make it obvious (a Form that branches into “Not a fit” and “Quote confirmed”). If the Form is already counted, use that same key, so the step and the conversion agree. `baker analytics conversions --flow <slug>` also lists a widget's housekeeping), `sources` (the client's own systems that post events to Baker, by the `page:<name>` key each raises, pickable before its first send arrives) and `landings` (its pages, by the id a `landing` trigger takes). Use this before writing any `triggers`: a key typed from memory saves, publishes and counts nobody forever. A Form's events need NOTHING set up first — no Source, no conversion: a Baker page raises them by itself\",\n required: false,\n },\n days: {\n type: \"string\",\n description:\n \"How many days the counts beside each step cover (default 30, up to 400). Every step comes back with `people` (reached it), `atLeast` (reached it or anything after it), `before` (got as far as the step before) and `share` (atLeast / before) — counted in PEOPLE across visits and devices, not in visits\",\n required: false,\n },\n templates: {\n type: \"boolean\",\n description:\n \"Also return the libraries: the suggested steps with their stages, the presets, and `stages[]` (the six stages with the test to apply). Where a step can be sent is not here — a send belongs to the system's event, and `baker analytics sends` lists the platforms and the tags that can send\",\n required: false,\n },\n} as const;\n\nregisterSchema({\n command: \"funnel\",\n description: \"Read the company's funnel, or draw one\",\n args,\n});\n\nexport const funnelCommand = defineCommand({\n meta: {\n name: \"funnel\",\n description:\n 'Start here: run with no flags to see the funnel this company has, with the last 30 days of PEOPLE counted beside every step — `people` reached it, `atLeast` got at least that far, `before` got as far as the step before, `share` is the ratio, and `worstStep` names where the most people are lost. Every company has one from the day it exists: Visited (any landing) → Became a lead → Became a customer, the last two with no way in until somebody gives them one. Then `--moments` for everything a trigger can name (every event the company\\'s Forms, pages and systems produced, its sources, its landings, plus each Form\\'s own outcome at zero). **To make an event count on a step — the usual job — use `--add-trigger <step> --event <key>`:** it adds that one way in and touches nothing else about the step. `--step \\'<json>\\'` is for adding or renaming a STEP; it replaces the step with that key, so it means restating its `after` and every trigger it keeps, and one left out is gone. A step is reached by three kinds of thing and nothing else: an event by its own key (`{\"id\":\"t1\",\"kind\":\"event\",\"eventKey\":\"<from moments.events or moments.sources>\"}`), one landing being viewed (`{\"id\":\"t2\",\"kind\":\"landing\",\"landingId\":\"<from moments.landings>\"}`) or any landing (`{\"id\":\"t3\",\"kind\":\"any_landing\"}`). Nothing has to be named first. Pick every trigger from `--moments`, never from memory. A step with no `triggers` advances NOBODY and reports zero forever without warning — that is what the hints keep pointing at. There is no default door: a Form is only counted where a trigger names it. Keep it true: a chat that adds a Form, a Source or an event that moves people forward places it on its step in the same chat. Each step comes back with `sends[]`, read-only: where the people who reach it are sent and what happened, read off the system events behind its ways in. A send is set on the event with `baker analytics sends`, never on a step, and once published it spends ad budget — a failed one is named in the hints. Everything here is STAGED: it becomes the company\\'s funnel in one piece when the chat is published — the client sees it on the canvas in this chat and can edit it there too — and the previous version stays on file so what was already measured under it is not lost.',\n },\n args,\n run: async ({ args: raw }) => {\n try {\n const parsed = parseFunnelArgs(raw as Record<string, unknown>);\n // Read once: every Form in this workspace, including the one built in\n // this Session that the backend has never seen.\n const forms = workspaceForms();\n const namedByCaller = formsNamedHere(forms);\n const request: FunnelRequest = {\n ...funnelWrite(parsed),\n ...(namedByCaller.length === 0 ? {} : { namedByCaller }),\n };\n const response = await apiPost<FunnelResponse>(\"/api/funnel\", request);\n // What the company already counts, so a Form's ending can say so: the\n // agent choosing a way in should put the same key on the step.\n const counted = new Map((response.data.counted ?? []).map((row) => [row.eventKey, row.name] as const));\n const data =\n response.data.moments === undefined\n ? response.data\n : { ...response.data, moments: withFormMoments(response.data.moments, forms, counted) };\n const hints = [...(response.hints ?? []), ...unplacedFormHints(response.data.steps ?? [], forms, counted)];\n writeJsonEnvelope({\n ok: true,\n data,\n ...(hints.length > 0 ? { hints } : {}),\n });\n } catch (err) {\n handleError(err);\n }\n },\n});\n\n/**\n * A malformed `--steps` is the caller's own JSON, so it comes back as a\n * validation error carrying the parser's own sentence and its `fix` — the\n * request was never made, and \"that did not parse\" is something the agent can\n * act on in the same turn.\n */\nfunction handleError(err: unknown): never {\n if (err instanceof FunnelArgsError) {\n writeJsonEnvelope({\n ok: false,\n error: { code: \"VALIDATION_ERROR\", message: err.message, ...(err.fix ? { fix: err.fix } : {}) },\n });\n process.exit(1);\n }\n if (err instanceof ApiError) {\n writeJsonEnvelope({ ok: false, error: { code: err.code, message: err.message } });\n process.exit(1);\n }\n writeJsonEnvelope({\n ok: false,\n error: { code: \"NETWORK_ERROR\", message: err instanceof Error ? err.message : \"Unexpected error\" },\n });\n process.exit(1);\n}\n\n/**\n * Every Form in this workspace with its tree, skipping whatever cannot be read.\n *\n * Skipping rather than failing: `baker funnel` is one of the commands reached\n * for while a Form is half-written, and a flow-builder loop that has created\n * the folder but not yet the file would otherwise take the whole funnel down\n * with it. A Form left out costs one unfired ending in a list; a throw costs\n * the command.\n */\nfunction workspaceForms(): WorkspaceForm[] {\n const forms: WorkspaceForm[] = [];\n for (const slug of listFlowSlugs()) {\n try {\n forms.push({ slug, tree: JSON.parse(readFileSync(flowDataPath(slug), \"utf-8\")) });\n } catch {\n // Unreadable or not yet written.\n }\n }\n return forms;\n}\n","import { type FlowEventKey, flowEventKeys } from \"@baker/api/analytics/flowEventKeys\";\nimport type { FunnelResponse } from \"@baker/api/funnel\";\n\n/**\n * A Form's endings, added to `--moments` before any of them has fired.\n *\n * `moments.events` is built from the event store, so it knows an event only\n * once somebody has produced one. A Form published this morning offered\n * nothing, and the picker's remaining affordances — type the key by hand, or\n * add a Source — are both wrong: a Source is a door for the company's own\n * systems, and a Baker page already raises its events natively. That is how a\n * funnel step ends up naming a key nothing produces, which never errors and\n * counts zero forever.\n *\n * Facts only. Which ending is the point of a Form is the agent's decision:\n * it is a reading of what the steps mean — \"Quote confirmed\" against \"Not a\n * fit\" — and a rule over the tree's shape picked the step both branches pass\n * through, which would have counted every rejected visitor as a lead. So\n * nothing here is marked as the outcome; each ending says the step it happens\n * on and whether the company already counts it, and the agent chooses.\n *\n * Pure, and the reading of the trees is the caller's: a half-written\n * `_data.json` under a flow-builder loop must not take out `baker funnel`.\n */\n\ntype Moments = NonNullable<FunnelResponse[\"data\"][\"moments\"]>;\ntype MomentEvent = Moments[\"events\"][number];\n\nexport interface WorkspaceForm {\n slug: string;\n tree: unknown;\n}\n\n/** Event key → the name the company counts it under, this chat's staged counts included. */\nexport type CountedNames = ReadonlyMap<string, string>;\n\n/**\n * Each step's completion and the Form's own submit — the endings a step could\n * mean. A widget's housekeeping (\"the booking page was viewed\") is left out:\n * it is never what a person means by reaching a step, it roughly doubled the\n * list, and `baker analytics conversions --flow <slug>` still has it.\n */\nfunction offeredEndings(form: WorkspaceForm): FlowEventKey[] {\n return flowEventKeys(form.tree, form.slug).filter((ending) => ending.isCompletion || ending.nodeId === null);\n}\n\n/**\n * The store's events plus every workspace Form's endings, each with its step.\n *\n * An ending that has fired stays the store's own row, never replaced: it\n * carries a real volume, and a zero in its place would say the opposite of\n * what is true. It gains the step it happens on, which only the Form knows. An\n * ending nothing has fired yet is appended at zero, `seen: false`, with the\n * name the company counts it under when it counts it.\n */\nexport function formMoments(\n events: readonly MomentEvent[],\n forms: readonly WorkspaceForm[],\n counted: CountedNames,\n): MomentEvent[] {\n const endings = new Map<string, { slug: string; ending: FlowEventKey }>();\n for (const form of forms) {\n for (const ending of offeredEndings(form)) endings.set(ending.eventKey, { slug: form.slug, ending });\n }\n\n const known = events.map((event) => {\n const found = endings.get(event.eventKey);\n if (found === undefined) return event;\n endings.delete(event.eventKey);\n return { ...event, step: found.ending.nodeName };\n });\n const unfired = [...endings.values()].map(({ slug, ending }): MomentEvent => {\n const countedAs = counted.get(ending.eventKey);\n return {\n eventKey: ending.eventKey,\n origin: \"site\",\n flowSlug: slug,\n ...(ending.nodeType === null ? {} : { nodeType: ending.nodeType }),\n events: 0,\n sessions: 0,\n step: ending.nodeName,\n seen: false,\n ...(countedAs === undefined ? {} : { countedAs }),\n };\n });\n return [...known, ...unfired];\n}\n\n/** The moments as answered, with every workspace Form's endings alongside. */\nexport function withFormMoments(moments: Moments, forms: readonly WorkspaceForm[], counted: CountedNames): Moments {\n return { ...moments, events: formMoments(moments.events, forms, counted) };\n}\n\n/**\n * The Forms {@link unplacedFormHints} speaks for, by slug — every one that\n * raises an ending at all.\n *\n * Sent with the request so the backend leaves them out of its own \"on no step\"\n * hint, and every unplaced Form is named once. A Form whose tree holds no step\n * is left to the backend, so it is still named.\n */\nexport function formsNamedHere(forms: readonly WorkspaceForm[]): string[] {\n return forms.filter((form) => flowEventKeys(form.tree, form.slug).length > 0).map((form) => form.slug);\n}\n\n/**\n * The Forms in this workspace that no step counts, as ONE hint.\n *\n * The backend knows only PUBLISHED Forms, so a Form built in this Session was\n * named by nobody — on the one turn where the reminder is worth most. So this\n * owns the reminder for every Form in the workspace ({@link formsNamedHere})\n * and the backend's hint covers only the rest.\n *\n * It names Forms and never a key to place: the key is the agent's decision.\n * The one key it does print is a fact — the ending the company already counts\n * — because a step on a different ending of the same Form saves, publishes and\n * disagrees with the conversion for ever.\n *\n * One hint rather than one per Form, measured: a 27-Form client got 27\n * near-identical paragraphs, which is how an agent learns to skip the hints.\n * But every Form is named in it: the backend leaves all of them to this hint,\n * so a Form cut from the list here is named nowhere.\n */\nexport function unplacedFormHints(\n steps: readonly { key: string; triggers?: readonly { eventKey?: string }[] }[],\n forms: readonly WorkspaceForm[],\n counted: CountedNames,\n): string[] {\n const onAStep = new Set(\n steps.flatMap((step) => (step.triggers ?? []).flatMap((trigger) => (trigger.eventKey ? [trigger.eventKey] : []))),\n );\n const unplaced: string[] = [];\n for (const form of forms) {\n const endings = flowEventKeys(form.tree, form.slug);\n if (endings.length === 0 || endings.some((ending) => onAStep.has(ending.eventKey))) continue;\n const countedOn = endings.filter((ending) => counted.has(ending.eventKey));\n unplaced.push(\n countedOn.length === 0\n ? form.slug\n : `${form.slug} (counted as ${countedOn.map((ending) => `\"${counted.get(ending.eventKey)}\" on \\`${ending.eventKey}\\``).join(\", \")})`,\n );\n }\n if (unplaced.length === 0) return [];\n\n return [\n `${unplaced.length} Form(s) in this workspace reach no funnel step, so they move nobody along it however many people finish them: ${unplaced.join(\"; \")}. Which ending moves somebody to which step is your call: \\`baker funnel --moments\\` lists every Form's endings by the step each happens on, and when the step names do not make it obvious — a Form that branches into a rejection and a confirmation — ask the person which one means a lead or a customer. Place it with \\`baker funnel --add-trigger <step> --event <key>\\`. A Form already counted as a page conversion goes on the funnel by THAT SAME key, or the two numbers disagree for ever.`,\n ];\n}\n","/**\n * The one closed vocabulary in a funnel that is otherwise the company's own.\n *\n * A company invents its own steps, in its own words, wired its own way\n * (`graph.ts`). What it cannot invent is the *stage* a step stands for: the\n * six words below are what keeps one company's funnel comparable with\n * another's, and they can stay closed because they describe the shape every\n * business has — somebody saw the offer, told you who they are, did something\n * real with you, paid, paid again, or went away — rather than any one sector's\n * name for a rung on it.\n *\n * Six, down from eight. The old list split \"qualified\", \"engaged\" and\n * \"committed\" apart, and \"lost\" from \"disqualified\", which read as a sales\n * team's pipeline and meant nothing to a shop, a clinic or a course. Every\n * step that was one of those three is a real forward interaction and is\n * `engaged`; every step that ends the journey without money is `lost`.\n *\n * These lists are mirrored from `packages/backend/convex/lib/validators.ts`,\n * which is the source of truth for what can be stored. They are re-declared\n * here rather than imported because `@baker/api` must not depend on `convex`;\n * `vocabulary.parity.test.ts` reads that file and fails if the two ever drift.\n */\n\n/**\n * The stage a funnel step stands for, never the step itself.\n *\n * Ordered as a journey runs, and **the order is meaningful**: it is the\n * fallback for comparing one company's funnel against another's when their\n * graphs cannot be laid over each other, and the tie-break for \"how far did\n * this person get\" between two steps at the same depth.\n *\n * Where a step is sent is never derived from this list — that lives per step\n * — which is exactly why it does not have to be exhaustive.\n */\nexport const FUNNEL_STEP_CLASSES = [\n /** Saw the offer: a landing opened, an ad clicked through. */\n \"visited\",\n /** We know who they are: a form sent, an account created. */\n \"lead\",\n /** Something real happened: a call booked, a trial started, a quote asked for, a cart filled. */\n \"engaged\",\n /** Paid, or signed. */\n \"customer\",\n /** Paid again, renewed, or grew. */\n \"repeat\",\n /** Terminal. Did not go ahead, or was never a fit. */\n \"lost\",\n] as const;\nexport type FunnelStepClass = (typeof FUNNEL_STEP_CLASSES)[number];\n\n/**\n * Each stage in plain words, with an example from three shapes of business.\n *\n * The stage is the only thing in a funnel a company does not get to invent, so\n * it is the only thing anybody has to be *taught* — and it was taught in five\n * words on a `<select>`: \"Engaged — did something real with you\". A marketer\n * setting up a demo-led funnel, a self-serve trial and a shop cannot tell from\n * that whether a trial is engaged or customer, and getting it wrong is\n * invisible: nothing on their own reports changes, and the cross-company\n * comparison this list exists for quietly stops working.\n *\n * So each one says what it means in a sentence a child could follow, and then\n * shows the same stage in three businesses that look nothing like each other.\n * Three, because that is the range that makes the rule legible — a shop has no\n * demo and a self-serve product has no salesperson, and a stage that survives\n * all three is a stage somebody can place their own step into.\n *\n * Here rather than in the dashboard because the agent needs the same words:\n * `baker funnel --templates` returns it, the stage picker renders it, and\n * `docs/product-model.md` quotes it.\n */\nexport interface FunnelStageGuide {\n /** One word for the chip on a card. */\n title: string;\n /** What it means, said once, plainly. */\n meaning: string;\n /** The test somebody applies to their own step to decide if it belongs here. */\n test: string;\n /** The same stage in three businesses: an online shop, a self-serve product, a sales-led business. */\n examples: { shop: string; selfServe: string; salesLed: string };\n}\n\nexport const FUNNEL_STAGE_GUIDE: Readonly<Record<FunnelStepClass, FunnelStageGuide>> = {\n visited: {\n title: \"Visited\",\n meaning: \"They looked, and that is all. You still have no idea who they are.\",\n test: \"Could this have happened without them telling you anything about themselves?\",\n examples: {\n shop: \"Opened a product page\",\n selfServe: \"Read the pricing page\",\n salesLed: \"Read a case study\",\n },\n },\n lead: {\n title: \"Lead\",\n meaning: \"Now you know who they are. They handed over a way to reach them.\",\n test: \"Do you have their email, their phone, or an account in their name?\",\n examples: {\n shop: \"Joined the newsletter\",\n selfServe: \"Created a free account\",\n salesLed: \"Asked for a demo\",\n },\n },\n engaged: {\n title: \"Engaged\",\n meaning: \"They did something real — more than reading, not yet paying.\",\n test: \"Did they spend effort or time on you, without any money changing hands?\",\n examples: {\n shop: \"Put something in the basket\",\n selfServe: \"Started a trial, or invited a colleague\",\n salesLed: \"Turned up to the demo, or asked for a quote\",\n },\n },\n customer: {\n title: \"Customer\",\n meaning: \"Money. They paid, or they signed something that means they will.\",\n test: \"Is there a first payment or a signed contract behind this?\",\n examples: {\n shop: \"Placed their first order\",\n selfServe: \"Started paying for a plan\",\n salesLed: \"Signed the contract\",\n },\n },\n repeat: {\n title: \"Repeat\",\n meaning: \"Money again. They came back, renewed, or bought more.\",\n test: \"Had they already paid you once before this happened?\",\n examples: {\n shop: \"Ordered a second time\",\n selfServe: \"Renewed, or moved to a bigger plan\",\n salesLed: \"Renewed the contract, or added seats\",\n },\n },\n lost: {\n title: \"Lost\",\n meaning: \"It ended, and no money came. Nothing comes after this step.\",\n test: \"Is this journey over without a sale — for now or for good?\",\n examples: {\n shop: \"Left the basket and never came back\",\n selfServe: \"The trial ran out unused\",\n salesLed: \"They went with somebody else\",\n },\n },\n};\n\n/**\n * The stages that end a journey without money. One word now: the old split\n * between \"it was real and did not close\" and \"it never was real\" is a\n * judgement a sales team makes and nobody else does, and the two read as one\n * loss rate on every report anyway.\n */\nexport const TERMINAL_NEGATIVE_CLASSES = [\"lost\"] as const;\n\nexport function isTerminalNegative(stepClass: FunnelStepClass): boolean {\n return (TERMINAL_NEGATIVE_CLASSES as readonly string[]).includes(stepClass);\n}\n\n/** The stages that mean money arrived: where a funnel is expected to end. */\nexport const MONEY_CLASSES = [\"customer\", \"repeat\"] as const;\n\nexport function isMoneyClass(stepClass: FunnelStepClass): boolean {\n return (MONEY_CLASSES as readonly string[]).includes(stepClass);\n}\n\n/** Where a stage sits in the journey's order — the cross-company fallback. */\nexport function stepClassRank(stepClass: FunnelStepClass): number {\n return FUNNEL_STEP_CLASSES.indexOf(stepClass);\n}\n\n/**\n * A company step key: lowercase letters, digits and underscores, 1–64\n * characters, no leading or trailing underscore.\n *\n * Constrained on purpose even though the vocabulary is open. The key is the\n * stable identifier for the step — it ends up in reporting arguments, CLI\n * arguments and URLs — so it must not be able to be `\"Deal — won (EU)\"`. The\n * client's own words live in `displayName`, which has no constraint at all,\n * and renaming a step never touches its key.\n */\nconst STEP_KEY = /^[a-z0-9](?:[a-z0-9_]{0,62}[a-z0-9])?$/;\n\nexport function isFunnelStepKey(value: string): boolean {\n return STEP_KEY.test(value);\n}\n\nexport function isFunnelStepClass(value: string): value is FunnelStepClass {\n return (FUNNEL_STEP_CLASSES as readonly string[]).includes(value);\n}\n","/**\n * Step templates — a curated library of steps most funnels have, kept as\n * suggestions and never as law.\n *\n * The vocabulary is the company's: it invents its own steps, in its own words.\n * That does not mean everybody should have to invent \"Meeting booked\" from\n * nothing at kickoff, and it does not mean the research behind what each of\n * these moments means, whether it carries money, and what it becomes on Meta,\n * Google Ads and LinkedIn has to be re-derived per client.\n *\n * So the tables live here, as suggestion rather than schema:\n *\n * - **Nothing validates against this file.** A company step needs a class and a\n * graph, and neither has to come from a template. `graph.ts` never reads this\n * module.\n * - The funnel canvas offers templates when a step is created, and\n * `VERTICAL_PRESETS` offers four small ready-made graphs for the cold start.\n * - `platformDefaults` is the **prefill** for an offline conversion added to a\n * templated step — \"send this one to Meta\" on a won-like step should propose\n * `Purchase`, not an empty box (`defaultOfflineEvent`). Every value is\n * overridable; it is a default, not a mapping, and it is deliberately per\n * template rather than per class, so that where a conversion is sent never\n * becomes something the class decides.\n *\n * Pure data plus two derivations, tier-1 tested.\n */\n\nimport type { FunnelStepDefinition } from \"./graph\";\nimport type { FunnelStepClass } from \"./vocabulary\";\n\n/**\n * What a step usually becomes on Meta. The event name travels in the payload,\n * so anything goes — but a standard event optimizes better than a custom one,\n * which is why `standard` is recorded rather than left to be guessed from the\n * name.\n */\nexport interface MetaEventDefault {\n eventName: string;\n standard: boolean;\n}\n\n/**\n * The category Google Ads makes you pick when the conversion action is created.\n * `alternative` exists for the one genuine fork: a closed deal is \"Converted\n * lead\" in the lead funnel and \"Purchase\" once a revenue amount is reported,\n * and only the company knows which it is reporting.\n */\nexport interface GoogleAdsConversionDefault {\n category: string;\n alternative?: string;\n}\n\n/**\n * The `type` LinkedIn makes you pick when the conversion rule is created.\n *\n * `verified: false` carries a type taken from the documentation and never\n * checked against the live enum, so a creation attempt has to fall back to\n * `fallback` instead of failing.\n */\nexport interface LinkedinConversionDefault {\n type: string;\n verified: boolean;\n fallback?: string;\n}\n\nexport interface FunnelPlatformDefaults {\n meta?: MetaEventDefault;\n googleAds?: GoogleAdsConversionDefault;\n linkedin?: LinkedinConversionDefault;\n}\n\nexport interface FunnelStepTemplate {\n /** Suggested key. A company may rename it; nothing here is a stable identifier. */\n key: string;\n /** Suggested name, in a marketer's words. The company's own name wins. */\n displayName: string;\n /** One line, shown next to the suggestion so nobody has to guess what it covers. */\n meaning: string;\n stepClass: FunnelStepClass;\n /**\n * Other names for the same moment, lowercase and in English.\n *\n * Read only by `suggestStepForName`, which guesses a step for a name that has\n * arrived from somewhere else — a CRM stage, a spreadsheet column, whatever a\n * later source turns out to speak. Nothing validates against them and a match\n * is never applied on its own; a person still confirms it.\n */\n aliases: readonly string[];\n /**\n * Prefill for a destination created on a step made from this template.\n * Absent per platform where the old table said \"never sent\".\n */\n platformDefaults: FunnelPlatformDefaults;\n /** Why this class, where the answer is not obvious. Shown to nobody; read by whoever edits this file. */\n classNote?: string;\n}\n\n/**\n * Sixteen steps, each with the stage it belongs to.\n *\n * On the stage mapping, where it took a decision rather than a lookup:\n *\n * - **`lead` is \"we know who they are\", not \"they did something small\".**\n * `lead` and `signed_up` are the same fact reached two ways, so both are\n * `lead` even though one is a form and the other an account.\n * - **`engaged` is the wide one, and that is deliberate.** Qualified, cart,\n * checkout, trial, outreach, meeting booked, meeting held and an open\n * opportunity are all \"something real happened\" before any money. Splitting\n * them further is what the old eight-word list did, and it read as a sales\n * team's pipeline to everybody who was not one.\n * - **`customer` is the first money, `repeat` is the money after it.**\n * `subscribed` is therefore `customer`, not `repeat`: it is the moment a\n * subscription starts paying. `renewed` is what `repeat` is for.\n */\nexport const FUNNEL_STEP_TEMPLATES: readonly FunnelStepTemplate[] = [\n {\n key: \"saw_offer\",\n displayName: \"Saw the offer\",\n meaning: \"Someone opened one of your landing pages — where most journeys start\",\n stepClass: \"visited\",\n classNote: \"The only `visited` template: a landing being viewed is a step, never an outcome.\",\n aliases: [\"saw the offer\", \"visited\", \"landing viewed\", \"page viewed\", \"visit\", \"visitor\"],\n // Never sent anywhere: every platform already counts its own page views,\n // and uploading a view as a conversion would teach it to optimise for\n // nothing.\n platformDefaults: {},\n },\n {\n key: \"lead\",\n displayName: \"Lead\",\n meaning: \"Someone left their details — the starting point of most journeys\",\n stepClass: \"lead\",\n aliases: [\"lead\", \"new lead\", \"inbound lead\", \"enquiry\", \"inquiry\", \"form submission\"],\n platformDefaults: {\n meta: { eventName: \"Lead\", standard: true },\n googleAds: { category: \"Submit lead form\" },\n linkedin: { type: \"LEAD\", verified: true },\n },\n },\n {\n key: \"signed_up\",\n displayName: \"Signed up\",\n meaning: \"Someone created an account\",\n stepClass: \"lead\",\n classNote: \"Same fact as a lead reached a different way — we now know who they are.\",\n aliases: [\"signed up\", \"sign up\", \"registered\", \"registration\", \"account created\", \"new user\"],\n platformDefaults: {\n meta: { eventName: \"CompleteRegistration\", standard: true },\n googleAds: { category: \"Sign-up\" },\n linkedin: { type: \"SIGN_UP\", verified: true },\n },\n },\n {\n key: \"add_to_cart\",\n displayName: \"Added to cart\",\n meaning: \"Something went into the cart\",\n stepClass: \"engaged\",\n aliases: [\"added to cart\", \"add to cart\", \"cart\"],\n platformDefaults: {\n meta: { eventName: \"AddToCart\", standard: true },\n googleAds: { category: \"Add to cart\" },\n linkedin: { type: \"ADD_TO_CART\", verified: true },\n },\n },\n {\n key: \"checkout_started\",\n displayName: \"Started checkout\",\n meaning: \"The checkout began\",\n stepClass: \"engaged\",\n aliases: [\"started checkout\", \"checkout started\", \"checkout\", \"begin checkout\"],\n platformDefaults: {\n meta: { eventName: \"InitiateCheckout\", standard: true },\n googleAds: { category: \"Begin checkout\" },\n // Written from the documentation and never checked against the live\n // enum, which is what `verified: false` is for — carried as a caveat\n // rather than quietly resolved.\n linkedin: { type: \"START_CHECKOUT\", verified: false, fallback: \"OTHER\" },\n },\n },\n {\n key: \"trial_started\",\n displayName: \"Trial started\",\n meaning: \"A free trial began\",\n stepClass: \"engaged\",\n classNote: \"A trial is use, not payment — engaged, never customer.\",\n aliases: [\"trial\", \"trial started\", \"free trial\", \"in trial\"],\n platformDefaults: {\n meta: { eventName: \"StartTrial\", standard: true },\n googleAds: { category: \"Sign-up\" },\n linkedin: { type: \"OTHER\", verified: true },\n },\n },\n {\n key: \"qualified_lead\",\n displayName: \"Qualified lead\",\n meaning: \"The lead matches who you sell to\",\n stepClass: \"engaged\",\n aliases: [\"qualified lead\", \"qualified\", \"mql\", \"sql\", \"sales qualified lead\", \"marketing qualified lead\"],\n platformDefaults: {\n meta: { eventName: \"QualifiedLead\", standard: false },\n googleAds: { category: \"Qualified lead\" },\n linkedin: { type: \"QUALIFIED_LEAD\", verified: true },\n },\n },\n {\n key: \"contacted\",\n displayName: \"Contacted\",\n meaning: \"Your team reached out\",\n stepClass: \"engaged\",\n classNote: \"Engaged even though the move came from the seller: it is a real forward interaction.\",\n aliases: [\"contacted\", \"contact\", \"outreach\", \"follow up\", \"first contact\", \"attempting contact\"],\n platformDefaults: {\n meta: { eventName: \"Contact\", standard: true },\n googleAds: { category: \"Contact\" },\n linkedin: { type: \"CONTACT\", verified: true },\n },\n },\n {\n key: \"meeting_scheduled\",\n displayName: \"Meeting booked\",\n meaning: \"A meeting is on the calendar\",\n stepClass: \"engaged\",\n aliases: [\n \"meeting booked\",\n \"meeting scheduled\",\n \"demo scheduled\",\n \"demo booked\",\n \"appointment booked\",\n \"call booked\",\n ],\n platformDefaults: {\n meta: { eventName: \"Schedule\", standard: true },\n googleAds: { category: \"Book appointment\" },\n // LinkedIn has SCHEDULE and BOOK_APPOINTMENT separately; the second is the\n // one that means a reservation.\n linkedin: { type: \"BOOK_APPOINTMENT\", verified: true },\n },\n },\n {\n key: \"meeting_held\",\n displayName: \"Meeting held\",\n meaning: \"The meeting actually happened — kept apart from booking because no-show is a real number\",\n stepClass: \"engaged\",\n aliases: [\"meeting held\", \"meeting done\", \"demo held\", \"demo done\", \"demo completed\", \"call held\"],\n platformDefaults: {\n meta: { eventName: \"MeetingHeld\", standard: false },\n googleAds: { category: \"Other\" },\n linkedin: { type: \"OTHER\", verified: true },\n },\n },\n {\n key: \"opportunity\",\n displayName: \"Opportunity open\",\n meaning: \"A deal is open — send the estimated value if you have one\",\n stepClass: \"engaged\",\n classNote: \"A number behind an intention is still no money, so engaged rather than customer.\",\n aliases: [\"opportunity\", \"proposal sent\", \"proposal\", \"negotiation\", \"in negotiation\", \"quote sent\", \"deal open\"],\n platformDefaults: {\n meta: { eventName: \"Opportunity\", standard: false },\n googleAds: { category: \"Request quote\" },\n linkedin: { type: \"REQUEST_QUOTE\", verified: true },\n },\n },\n {\n key: \"won\",\n displayName: \"Deal won\",\n meaning: \"The deal closed — the amount is required here\",\n stepClass: \"customer\",\n aliases: [\"won\", \"closed won\", \"deal won\", \"purchase\", \"sale\"],\n platformDefaults: {\n meta: { eventName: \"Purchase\", standard: true },\n // Which of the two depends on whether the company reports a revenue\n // amount. It is a per-company answer, so it stays a fork, not a constant.\n googleAds: { category: \"Purchase\", alternative: \"Converted lead\" },\n linkedin: { type: \"PURCHASE\", verified: true },\n },\n },\n {\n key: \"subscribed\",\n displayName: \"Subscription started\",\n meaning: \"A paying subscription began — the amount is the recurring one\",\n stepClass: \"customer\",\n classNote: \"Customer, not repeat: it is the first money. Repeat is what happens after — see the renewed template.\",\n aliases: [\"subscribed\", \"subscription started\", \"new subscription\", \"paying\"],\n platformDefaults: {\n meta: { eventName: \"Subscribe\", standard: true },\n googleAds: { category: \"Purchase\" },\n linkedin: { type: \"PURCHASE\", verified: true },\n },\n },\n {\n key: \"renewed\",\n displayName: \"Subscription renewed\",\n meaning: \"An existing customer paid again or expanded — the amount is what they paid this time\",\n stepClass: \"repeat\",\n classNote:\n \"The smallest honest member of `repeat`. Without it every SaaS funnel drawn from this library ends at `subscribed` and the stage has nothing to hold. The recurring lifecycle beyond renewal — upgrades, churn, refunds — is a bigger conversation than a template.\",\n aliases: [\"renewed\", \"renewal\", \"subscription renewed\", \"expansion\", \"upsell\"],\n platformDefaults: {\n meta: { eventName: \"Renewed\", standard: false },\n googleAds: { category: \"Purchase\" },\n linkedin: { type: \"PURCHASE\", verified: true },\n },\n },\n {\n key: \"application_submitted\",\n displayName: \"Application submitted\",\n meaning: \"Someone submitted an application — a course, a policy, a credit line\",\n stepClass: \"engaged\",\n classNote: \"For education, events, fintech and insurance. Strong intent, no money yet.\",\n aliases: [\"application submitted\", \"application\", \"applied\", \"application received\"],\n platformDefaults: {\n meta: { eventName: \"SubmitApplication\", standard: true },\n googleAds: { category: \"Submit lead form\" },\n linkedin: { type: \"SUBMIT_APPLICATION\", verified: true },\n },\n },\n {\n key: \"lost\",\n displayName: \"Lost\",\n meaning: \"It did not go ahead — the deal fell through, or the lead was never a fit\",\n stepClass: \"lost\",\n classNote:\n \"Terminal and never sent anywhere. It can happen at any depth, which is why the order check leaves it alone.\",\n aliases: [\n \"lost\",\n \"closed lost\",\n \"deal lost\",\n \"no budget\",\n \"not interested\",\n \"disqualified\",\n \"unqualified\",\n \"not a fit\",\n \"spam\",\n \"duplicate\",\n \"junk\",\n ],\n platformDefaults: {},\n },\n];\n\nconst TEMPLATES_BY_KEY: ReadonlyMap<string, FunnelStepTemplate> = new Map(\n FUNNEL_STEP_TEMPLATES.map((template) => [template.key, template] as const),\n);\n\n/**\n * A name reduced to comparable words: lowercase, no separators, single spaces.\n * \"Won - Closed\", \"won_closed\" and \"WON / CLOSED\" all become the same two\n * words, which is the whole trick — every system writes the same moment its own\n * way.\n */\nfunction nameWords(value: string): string[] {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .split(\" \")\n .filter((word) => word !== \"\");\n}\n\n/** Whether `needle`'s words appear, in order and adjacent, inside `haystack`. */\nfunction containsWords(haystack: readonly string[], needle: readonly string[]): boolean {\n if (needle.length === 0 || needle.length > haystack.length) {\n return false;\n }\n for (let start = 0; start <= haystack.length - needle.length; start++) {\n if (needle.every((word, offset) => haystack[start + offset] === word)) {\n return true;\n }\n }\n return false;\n}\n\ninterface NameCandidate {\n stepKey: string;\n phrases: readonly string[];\n}\n\n/** The longest phrase that matches wins — \"closed won\" beats a bare \"won\". */\nfunction bestCandidate(subject: readonly string[], candidates: readonly NameCandidate[]): string | null {\n let best: string | null = null;\n let bestLength = 0;\n for (const candidate of candidates) {\n for (const phrase of candidate.phrases) {\n const words = nameWords(phrase);\n const matches = containsWords(subject, words) || containsWords(words, subject);\n if (matches && words.length > bestLength) {\n best = candidate.stepKey;\n bestLength = words.length;\n }\n }\n }\n return best;\n}\n\n/**\n * A guess at which of *this company's* steps some other system's name means.\n *\n * The company's own names are tried first, because a company that calls a step\n * \"Lead that fits\" has already said what its words are and no library should\n * talk over it. Only then do the template aliases get a turn, and only for\n * templates whose key the funnel actually contains — a suggestion has to be a\n * step the person can pick, so an alias of a step they never drew is no match\n * at all.\n *\n * `null` means \"no idea\", which is a fine answer: the picker just opens empty.\n */\nexport function suggestStepForName(\n incomingName: string,\n steps: readonly { key: string; displayName: string }[],\n templates: readonly FunnelStepTemplate[] = FUNNEL_STEP_TEMPLATES,\n): string | null {\n const subject = nameWords(incomingName);\n if (subject.length === 0) {\n return null;\n }\n const own = bestCandidate(\n subject,\n steps.map((step) => ({ stepKey: step.key, phrases: [step.displayName] })),\n );\n if (own !== null) {\n return own;\n }\n const drawn = new Set(steps.map((step) => step.key));\n return bestCandidate(\n subject,\n templates.filter((t) => drawn.has(t.key)).map((t) => ({ stepKey: t.key, phrases: t.aliases })),\n );\n}\n\n/** `undefined` rather than a throw: a caller offering suggestions can simply offer fewer. */\nexport function stepTemplate(key: string): FunnelStepTemplate | undefined {\n return TEMPLATES_BY_KEY.get(key);\n}\n\n/**\n * The preset a company picks once, at the cold start, to get a graph instead of\n * a blank canvas.\n *\n * **Two families, because that is who Baker sells to today.** There is no\n * e-commerce preset: no client runs that funnel and none is on the way, and a\n * preset nobody picks is a graph nobody checks. The e-commerce *templates* stay\n * in the library above — `add_to_cart`, `checkout_started`, `signed_up` are\n * offered one by one on the canvas like any other suggestion, and the day an\n * e-commerce client arrives the preset is four lines here, not new research.\n *\n * Within each family there are variants rather than one average funnel, because\n * the difference between the two B2B shapes — whether a human runs meetings —\n * changes almost every node, and a preset that splits the difference fits\n * neither.\n */\nexport const FUNNEL_PRESET_FAMILIES = [\"b2b\", \"saas\", \"ecommerce\"] as const;\nexport type FunnelPresetFamily = (typeof FUNNEL_PRESET_FAMILIES)[number];\n\nexport const FUNNEL_VERTICALS = [\n \"b2b_sales\",\n \"b2b_inbound\",\n \"saas_self_serve\",\n \"saas_sales_assisted\",\n \"ecommerce_purchase\",\n] as const;\nexport type FunnelVertical = (typeof FUNNEL_VERTICALS)[number];\n\nexport interface FunnelPresetStep {\n templateKey: string;\n /** Predecessors, by template key. Empty means the funnel starts here. */\n after: readonly string[];\n /** Overrides the template's name where the vertical says it differently — \"Purchase\", not \"Deal won\". */\n displayName?: string;\n}\n\nexport interface FunnelVerticalPreset {\n vertical: FunnelVertical;\n family: FunnelPresetFamily;\n label: string;\n /** One line for the picker, in the company's language rather than ours. */\n description: string;\n steps: readonly FunnelPresetStep[];\n}\n\n/**\n * Four small graphs — two shapes of B2B, two of SaaS — each the shortest funnel\n * of its kind that is still honest, not the fullest.\n *\n * Small on purpose. A preset is the first thing a company sees on the canvas,\n * and a fourteen-node suggestion invites deleting rather than editing; a step\n * that exists because a preset put it there and that nothing ever reaches shows\n * up as a gap in the funnel forever after.\n */\nexport const VERTICAL_PRESETS: readonly FunnelVerticalPreset[] = [\n {\n vertical: \"b2b_sales\",\n family: \"b2b\",\n label: \"B2B sales team\",\n description: \"Leads a salesperson qualifies, meets, quotes and closes.\",\n steps: [\n { templateKey: \"lead\", after: [] },\n { templateKey: \"qualified_lead\", after: [\"lead\"] },\n { templateKey: \"meeting_scheduled\", after: [\"qualified_lead\"] },\n // Booked and held stay apart because the gap between them is the no-show\n // rate, which is a number this kind of team actually manages.\n { templateKey: \"meeting_held\", after: [\"meeting_scheduled\"] },\n { templateKey: \"opportunity\", after: [\"meeting_held\"] },\n { templateKey: \"won\", after: [\"opportunity\"] },\n // The end hangs off the earliest step at which it becomes possible; the\n // order check ignores it anyway (`isOutOfOrder`), so the edge is there to\n // be drawn, not to be enforced.\n { templateKey: \"lost\", after: [\"opportunity\"] },\n ],\n },\n {\n vertical: \"b2b_inbound\",\n family: \"b2b\",\n label: \"B2B inbound, no meetings\",\n description: \"Enquiries answered and closed without a meeting stage.\",\n steps: [\n { templateKey: \"lead\", after: [] },\n { templateKey: \"qualified_lead\", after: [\"lead\"] },\n { templateKey: \"contacted\", after: [\"qualified_lead\"] },\n { templateKey: \"won\", after: [\"contacted\"] },\n { templateKey: \"lost\", after: [\"contacted\"] },\n ],\n },\n {\n vertical: \"saas_self_serve\",\n family: \"saas\",\n label: \"SaaS, self-serve\",\n description: \"People who sign up, trial the product and start paying on their own.\",\n steps: [\n { templateKey: \"signed_up\", after: [] },\n { templateKey: \"trial_started\", after: [\"signed_up\"] },\n { templateKey: \"subscribed\", after: [\"trial_started\"] },\n { templateKey: \"renewed\", after: [\"subscribed\"] },\n { templateKey: \"lost\", after: [\"trial_started\"], displayName: \"Trial expired\" },\n ],\n },\n {\n vertical: \"saas_sales_assisted\",\n family: \"saas\",\n label: \"SaaS, sales-assisted\",\n description: \"Sign-ups and demo requests, with a salesperson in the middle of the bigger ones.\",\n steps: [\n // Two entry points, which is the whole reason `after` is a list: people\n // arrive by signing up or by asking for a demo, never both, and a single\n // position integer calls that ordinary path an anomaly.\n { templateKey: \"signed_up\", after: [] },\n { templateKey: \"lead\", after: [], displayName: \"Demo requested\" },\n { templateKey: \"meeting_held\", after: [\"lead\"], displayName: \"Demo held\" },\n { templateKey: \"trial_started\", after: [\"signed_up\", \"meeting_held\"] },\n { templateKey: \"subscribed\", after: [\"trial_started\", \"meeting_held\"] },\n { templateKey: \"renewed\", after: [\"subscribed\"] },\n ],\n },\n {\n vertical: \"ecommerce_purchase\",\n family: \"ecommerce\",\n label: \"Online shop\",\n description: \"People who browse, fill a basket, check out and come back for a second order.\",\n steps: [\n { templateKey: \"saw_offer\", after: [], displayName: \"Viewed a product\" },\n { templateKey: \"add_to_cart\", after: [\"saw_offer\"] },\n { templateKey: \"checkout_started\", after: [\"add_to_cart\"] },\n // A shop's \"customer\" is the order, and its \"repeat\" is the next one —\n // the same two stages every other preset ends on, in a shop's words.\n { templateKey: \"won\", after: [\"checkout_started\"], displayName: \"Order placed\" },\n { templateKey: \"renewed\", after: [\"won\"], displayName: \"Ordered again\" },\n ],\n },\n];\n\n/**\n * The funnel every company starts with.\n *\n * Every company has a funnel from the day it exists — \"no funnel\" was a real\n * state, and it meant every \"how far did they get\" question had no answer\n * until somebody drew one. The default is the smallest honest journey: seeing\n * the offer, becoming a lead, becoming a customer. The first step listens for\n * any landing being viewed, so it counts from the first visit; the other two\n * start unwired and the canvas says so, because what a lead IS is the\n * company's to say.\n */\nexport function defaultFunnelSteps(): FunnelStepDefinition[] {\n return [\n {\n key: \"visited\",\n displayName: \"Visited\",\n stepClass: \"visited\",\n after: [],\n triggers: [{ id: \"t1\", kind: \"any_landing\" }],\n },\n { key: \"lead\", displayName: \"Became a lead\", stepClass: \"lead\", after: [\"visited\"], triggers: [] },\n { key: \"customer\", displayName: \"Became a customer\", stepClass: \"customer\", after: [\"lead\"], triggers: [] },\n ];\n}\n\nexport function verticalPreset(vertical: FunnelVertical): FunnelVerticalPreset | undefined {\n return VERTICAL_PRESETS.find((preset) => preset.vertical === vertical);\n}\n\n/**\n * A template turned into a real step of a company's funnel.\n *\n * The template stops existing at this point: what comes out is an ordinary\n * `FunnelStepDefinition` the company owns and can rename, reclassify or rewire,\n * with no link back. That is what makes this a suggestion library and not a\n * second schema.\n */\nexport function stepFromTemplate(\n template: FunnelStepTemplate,\n after: readonly string[],\n displayName?: string,\n): FunnelStepDefinition {\n return {\n key: template.key,\n displayName: displayName ?? template.displayName,\n stepClass: template.stepClass,\n after: [...after],\n // A template says what a moment IS, never which of this company's events\n // announces it — that is a per-company answer, and prefilling a guess would\n // make a step look wired when it advances nobody.\n triggers: [],\n };\n}\n\n/**\n * The steps a preset produces, ready to hand to `validateDefinition` or draw.\n *\n * A preset step whose template does not exist is dropped rather than faked: the\n * pair is checked by a test, so a miss here is a bug in this file and a\n * half-invented step would hide it.\n */\nexport function presetDefinition(preset: FunnelVerticalPreset): FunnelStepDefinition[] {\n return preset.steps.flatMap((step) => {\n const template = stepTemplate(step.templateKey);\n return template ? [stepFromTemplate(template, step.after, step.displayName)] : [];\n });\n}\n","/**\n * The request and response `baker funnel` and its Convex route both speak.\n *\n * One schema, used by both sides, because the funnel is the one payload in this\n * feature that is a graph rather than a list of fields: a shape the CLI and the\n * backend agreed on separately would disagree about a predecessor naming a step\n * that does not exist, and that disagreement is invisible until a client's\n * funnel comes back drawn wrong.\n */\n\nimport { z } from \"zod\";\nimport { FUNNEL_VERTICALS } from \"./stepTemplates\";\nimport { FUNNEL_STEP_CLASSES } from \"./vocabulary\";\n\n/** How many steps one funnel may have. */\nconst MAX_STEPS = 40;\n/** How many triggers one step may have. */\nconst MAX_PER_STEP = 20;\n\n/**\n * One trigger: an event, by its key, a landing page being viewed, or any\n * landing being viewed.\n */\nexport const funnelTriggerSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n id: z.string().min(1).max(64),\n kind: z.literal(\"event\"),\n /** The event's key, from `--moments`: `form:…`, `submit:…`, `page:…`, `exit:…`. */\n eventKey: z.string().min(1).max(320),\n }),\n z.object({\n id: z.string().min(1).max(64),\n kind: z.literal(\"landing\"),\n /** The landing's internal id — what the page stamps on its rows, never its slug. */\n landingId: z.string().min(1).max(120),\n }),\n /** Any landing being viewed — what a funnel's first step usually listens for. */\n z.object({\n id: z.string().min(1).max(64),\n kind: z.literal(\"any_landing\"),\n }),\n]);\n\nexport const funnelStepSchema = z.object({\n /** Lowercase letters, digits and underscores. Stable — renaming never changes it. */\n key: z.string().min(1).max(64),\n /** The client's own words for this step. Shown everywhere; unconstrained. */\n displayName: z.string().min(1).max(120),\n stepClass: z.enum(FUNNEL_STEP_CLASSES),\n /** Predecessor keys. Empty means people come in here. */\n after: z.array(z.string().max(64)).max(MAX_STEPS).default([]),\n /** What moves somebody onto this step. Any one of them is enough. */\n triggers: z.array(funnelTriggerSchema).max(MAX_PER_STEP).default([]),\n});\nexport type FunnelStepWire = z.infer<typeof funnelStepSchema>;\n\n/**\n * With none of `steps`, `preset`, `step`, `removeStep`, `addTrigger` and\n * `removeTrigger` this is a read, which is what makes `baker funnel` with no\n * flags show the funnel rather than erroring.\n *\n * `steps` restates the WHOLE funnel, and the review screen shows the funnel\n * the client will get rather than a set of edits whose result has to be\n * computed. `step` and `removeStep` change one step and leave the rest as it\n * stands — the door for an agent that wants to add a stage without restating\n * six others, because a restatement that forgot one removed it. Whichever\n * door is used, the response names every step the proposal dropped.\n */\nexport const funnelRequestSchema = z.object({\n steps: z.array(funnelStepSchema).max(MAX_STEPS).optional(),\n /** One step, added or replaced by its key. Everything else is untouched. */\n step: funnelStepSchema.optional(),\n /** One step taken out by key; whatever came after it moves up to whatever came before it. */\n removeStep: z.string().min(1).max(64).optional(),\n /**\n * ONE way in added to a step, leaving the rest of that step alone.\n *\n * `step` takes a whole step, so adding a way in through it means restating\n * the step's name, stage, `after[]` and every trigger it already had — and a\n * trigger left out of that restatement is silently removed. This is the door\n * for the thing that is actually being asked (\"this Form's booking should\n * count here\"), and it cannot drop anything.\n */\n addTrigger: z.object({ stepKey: z.string().min(1).max(64), eventKey: z.string().min(1).max(320) }).optional(),\n /** ONE way in taken off a step, by the event key it listens for. */\n removeTrigger: z.object({ stepKey: z.string().min(1).max(64), eventKey: z.string().min(1).max(320) }).optional(),\n /** One of the ready-made graphs, instead of stating steps. */\n preset: z.enum(FUNNEL_VERTICALS).optional(),\n /** Also return the suggestion library and the presets. */\n templates: z.boolean().optional(),\n /**\n * Also return everything a trigger can name: every event this company's\n * Forms, pages and systems have actually produced, its sources, and its\n * landings. The list to pick a trigger FROM, so nobody has to guess.\n */\n moments: z.boolean().optional(),\n /** How many days the counts beside each step cover. Default 30. */\n days: z.number().int().min(1).max(400).optional(),\n /**\n * The Forms, by slug, the caller names itself when no step counts them —\n * `baker funnel` reads its own workspace and names each with the key that\n * places it. The backend leaves them out of its own hint, so no Form is\n * named twice.\n */\n namedByCaller: z.array(z.string().min(1).max(200)).max(1000).optional(),\n});\nexport type FunnelRequest = z.infer<typeof funnelRequestSchema>;\n\n/**\n * What comes back is loosely typed on purpose.\n *\n * The response is read by an agent and by a person, never re-submitted, so a\n * strict mirror of the request would only make the route fail on a field the\n * store has and this file has not caught up with. The request is where the\n * shape is enforced.\n */\nexport const funnelResponseSchema = z.object({\n ok: z.literal(true),\n data: z.object({\n /** The published version, or 0 when this company has never drawn a funnel. */\n version: z.number(),\n /** Whether `steps` is a proposal waiting on this chat's publish rather than what is live. */\n staged: z.boolean(),\n steps: z.array(\n z.object({\n key: z.string(),\n displayName: z.string(),\n stepClass: z.string(),\n after: z.array(z.string()),\n triggers: z.array(z.record(z.string(), z.unknown())),\n /**\n * Where the people who reach this step are sent: the sends of every\n * system event among its triggers. Read-only here — a send belongs to\n * the event and is set with `baker analytics sends`.\n */\n sends: z.array(\n z.object({\n id: z.string(),\n eventKey: z.string(),\n platform: z.string(),\n event: z.string(),\n /** What happened to it in the window, in one line. */\n outcome: z.string().optional(),\n }),\n ),\n /** How many people reached this step in the window. Counted in people, across visits and devices. */\n people: z.number().optional(),\n /** How many reached this step or any step after it. */\n atLeast: z.number().optional(),\n /** How many got as far as the step before it. Absent for a way in. */\n before: z.number().optional(),\n /** `atLeast / before`, 0–1. Absent where there is no before. */\n share: z.number().optional(),\n events: z.number().optional(),\n money: z.record(z.string(), z.number()).optional(),\n }),\n ),\n /** The window `people` and friends were counted over. */\n window: z.object({ startDate: z.string(), endDate: z.string(), days: z.number() }).optional(),\n /** The step that loses the most people, by key. Absent while nothing is lost. */\n worstStep: z.string().optional(),\n /**\n * The steps this write took off the funnel, by key. Present on every write,\n * empty when nothing was dropped — the answer to \"did my restatement forget\n * one\", given in the same turn.\n */\n removed: z.array(z.string()).optional(),\n /**\n * What this company counts as page conversions, this chat's staged counts\n * included, by event key and name. Here so a Form's ending can say it is\n * already counted — the agent choosing a step's way in should use that\n * same key — and outside `moments` because the plain read's hints say it too.\n */\n counted: z.array(z.object({ eventKey: z.string(), name: z.string() })).optional(),\n /** Everything a trigger can name, when asked for with `moments`. */\n moments: z\n .object({\n events: z.array(\n z.object({\n eventKey: z.string(),\n /** `site` for a page or Form, `systems` for the client's own server, `both` when both sent it. */\n origin: z.string().optional(),\n /** The Form it belongs to, when it belongs to one. */\n flowSlug: z.string().optional(),\n nodeType: z.string().optional(),\n events: z.number(),\n sessions: z.number(),\n /** The name it is counted under as a page conversion, when the company counts it. Informational. */\n countedAs: z.string().optional(),\n /**\n * The step of the Form this happens on, in the Form's own words —\n * never a node id. Present on a Form's ending read from the Form\n * itself, which is the only place the name is known.\n */\n step: z.string().optional(),\n /**\n * `false` on an ending a Form raises natively but nobody has fired\n * yet — derived from the Form, offered at zero, exactly as a\n * Source is offered before its first send. Absent means it came\n * from the event store and therefore happened.\n */\n seen: z.boolean().optional(),\n }),\n ),\n /** The company's sources: a system of theirs that posts events to Baker, by the event name it raises. */\n sources: z.array(z.object({ eventKey: z.string(), name: z.string(), status: z.string() })),\n landings: z.array(z.object({ landingId: z.string(), title: z.string(), slug: z.string() })),\n })\n .optional(),\n templates: z\n .array(\n z.object({\n key: z.string(),\n displayName: z.string(),\n meaning: z.string(),\n stepClass: z.string(),\n }),\n )\n .optional(),\n presets: z\n .array(z.object({ vertical: z.string(), label: z.string(), description: z.string(), steps: z.array(z.string()) }))\n .optional(),\n /**\n * What each stage means, the test to apply, and the same stage in three\n * shapes of business.\n *\n * The stage is the one thing in a funnel a company does not invent, so it\n * is the one thing the agent has to get right on the company's behalf —\n * and \"engaged: did something real\" cannot settle whether a trial is\n * engaged or customer. Same source as the dashboard's stage picker, so the\n * two cannot teach it differently.\n */\n stages: z\n .array(\n z.object({\n stepClass: z.string(),\n title: z.string(),\n meaning: z.string(),\n test: z.string(),\n examples: z.object({ shop: z.string(), selfServe: z.string(), salesLed: z.string() }),\n }),\n )\n .optional(),\n }),\n hints: z.array(z.string()).optional(),\n});\nexport type FunnelResponse = z.infer<typeof funnelResponseSchema>;\n","import { parseEventKey } from \"@baker/api/analytics\";\nimport { FUNNEL_VERTICALS, type FunnelRequest, funnelRequestSchema } from \"@baker/api/funnel\";\n\n/**\n * `baker funnel`'s flags, turned into the one request the route takes.\n *\n * Pure and tier-1 tested, because everything that can go wrong with this\n * command goes wrong here: hand-written JSON, a preset spelled from memory, and\n * a step whose ways in are half-written. Each of those has to come back as a\n * sentence the agent can act on in the same turn rather than as a request that\n * failed for a reason only the server can see.\n *\n * There is no `--forms` any more. It used to say which step each Form's\n * submissions entered, alongside a rule that dropped an unplaced Form on the\n * funnel's first step — so a funnel that measured a client's Forms\n * by accident was indistinguishable from one that measured them deliberately.\n * A Form reaches a step because a trigger on that step names it, and `baker\n * funnel`'s hints name every Form no step counts.\n */\n\nexport class FunnelArgsError extends Error {\n /** Structured remediation, in the shape the CLI's error envelope carries. */\n readonly fix?: { action: string; explanation: string };\n\n constructor(message: string, fix?: { action: string; explanation: string }) {\n super(message);\n this.name = \"FunnelArgsError\";\n if (fix) {\n this.fix = fix;\n }\n }\n}\n\nexport interface FunnelArgs {\n steps?: unknown[];\n /** One step, added or replaced by key; the rest of the funnel is left as it stands. */\n step?: unknown;\n /** One step taken off by key. */\n removeStep?: string;\n /** One way in added to a step, by the event key it listens for. */\n addTrigger?: { stepKey: string; eventKey: string };\n /** One way in taken off a step. */\n removeTrigger?: { stepKey: string; eventKey: string };\n preset?: string;\n templates: boolean;\n moments: boolean;\n days?: number;\n}\n\nexport function parseFunnelArgs(raw: Record<string, unknown>): FunnelArgs {\n const steps = parseSteps(raw.steps);\n const step = parseStep(raw.step);\n const removeStep = parseRemoveStep(raw[\"remove-step\"] ?? raw.removeStep);\n const preset = parsePreset(raw.preset);\n const addTrigger = parseWayIn(raw[\"add-trigger\"] ?? raw.addTrigger, raw.event, \"--add-trigger\");\n const removeTrigger = parseWayIn(raw[\"remove-trigger\"] ?? raw.removeTrigger, raw.event, \"--remove-trigger\");\n\n const doors = [steps, step, removeStep, preset, addTrigger, removeTrigger].filter(\n (door) => door !== undefined,\n ).length;\n if (doors > 1) {\n throw new FunnelArgsError(\n \"Give one of --steps, --step, --remove-step, --preset, --add-trigger or --remove-trigger, not two.\",\n );\n }\n\n const days = raw.days === undefined ? undefined : Number(raw.days);\n if (days !== undefined && (!Number.isInteger(days) || days <= 0 || days > 400)) {\n throw new FunnelArgsError(\"--days must be a whole number of days, from 1 to 400.\");\n }\n\n return {\n ...(steps === undefined ? {} : { steps }),\n ...(step === undefined ? {} : { step }),\n ...(removeStep === undefined ? {} : { removeStep }),\n ...(preset === undefined ? {} : { preset }),\n ...(addTrigger === undefined ? {} : { addTrigger }),\n ...(removeTrigger === undefined ? {} : { removeTrigger }),\n templates: raw.templates === true,\n moments: raw.moments === true,\n ...(days === undefined ? {} : { days }),\n };\n}\n\n/**\n * The request body. Validated against the shared schema here rather than only\n * on the server, so a malformed step is named before a round trip — and named\n * as the step it is, which the server's first-issue message cannot always do.\n */\nexport function funnelWrite(args: FunnelArgs): FunnelRequest {\n const body = {\n ...(args.steps === undefined ? {} : { steps: args.steps }),\n ...(args.step === undefined ? {} : { step: args.step }),\n ...(args.removeStep === undefined ? {} : { removeStep: args.removeStep }),\n ...(args.preset === undefined ? {} : { preset: args.preset }),\n ...(args.addTrigger === undefined ? {} : { addTrigger: args.addTrigger }),\n ...(args.removeTrigger === undefined ? {} : { removeTrigger: args.removeTrigger }),\n ...(args.templates ? { templates: true } : {}),\n ...(args.moments ? { moments: true } : {}),\n ...(args.days === undefined ? {} : { days: args.days }),\n };\n const parsed = funnelRequestSchema.safeParse(body);\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n throw new FunnelArgsError(\n `That funnel cannot be sent — ${issue?.path.join(\".\") || \"steps\"}: ${issue?.message ?? \"invalid\"}`,\n {\n action: \"read_the_schema\",\n explanation:\n \"Run `baker schema funnel` for the exact shape of one step, or `baker funnel --templates` for sixteen worked examples plus every field an event carries.\",\n },\n );\n }\n return parsed.data;\n}\n\nfunction parseSteps(raw: unknown): unknown[] | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (typeof raw !== \"string\") {\n throw new FunnelArgsError(\"--steps takes a JSON array of steps.\");\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new FunnelArgsError(`--steps is not valid JSON: ${cause instanceof Error ? cause.message : \"parse failed\"}`, {\n action: \"quote_the_json\",\n explanation:\n 'Wrap the whole array in single quotes so the shell leaves the double quotes alone: --steps \\'[{\"key\":\"lead\",...}]\\'.',\n });\n }\n if (!Array.isArray(parsed)) {\n throw new FunnelArgsError(\"--steps takes an ARRAY of steps, even when the funnel has one step.\");\n }\n refuseRetiredShapes(parsed);\n return parsed;\n}\n\n/**\n * One step, as JSON. The same shape one entry of `--steps` takes, and the same\n * refusals — a step written the old way is refused here too.\n */\nfunction parseStep(raw: unknown): unknown | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (typeof raw !== \"string\") {\n throw new FunnelArgsError(\"--step takes one step as a JSON object.\");\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new FunnelArgsError(`--step is not valid JSON: ${cause instanceof Error ? cause.message : \"parse failed\"}`, {\n action: \"quote_the_json\",\n explanation:\n 'Wrap the object in single quotes so the shell leaves the double quotes alone: --step \\'{\"key\":\"demo\",...}\\'.',\n });\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new FunnelArgsError(\"--step takes ONE step as an object. To restate several, use --steps with an array.\");\n }\n refuseRetiredShapes([parsed]);\n return parsed;\n}\n\nfunction parseRemoveStep(raw: unknown): string | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (typeof raw !== \"string\" || raw.trim() === \"\") {\n throw new FunnelArgsError(\"--remove-step takes the key of the step to take off, as `baker funnel` lists it.\");\n }\n return raw.trim();\n}\n\n/**\n * The shapes a step used to have and no longer does, refused by name.\n *\n * The wire schema would strip them silently: a step written with\n * `offlineConversions` would be stored without them and answer `ok`, and an\n * `analytics` or `conversion` trigger would be refused with zod's generic\n * discriminator message. Each has a home now — a send is set on the event, and\n * a way in is an event by its key with nothing to name first — so the refusal\n * says where.\n */\nfunction refuseRetiredShapes(steps: readonly unknown[]): void {\n for (const step of steps) {\n if (typeof step !== \"object\" || step === null) continue;\n const key = String((step as { key?: unknown }).key ?? \"?\");\n if (\"offlineConversions\" in step) {\n throw new FunnelArgsError(`Step \"${key}\" puts sends on the step, and a send belongs to the conversion.`, {\n action: \"set_sends_on_the_conversion\",\n explanation:\n \"Drop `offlineConversions`. Name the outcome with `baker analytics conversions`, then `baker analytics sends --set --conversion <name> --platform <p> --tag <tagId> --event <e>`; the step reads its conversions' sends back as `sends[]`.\",\n });\n }\n const triggers = (step as { triggers?: unknown }).triggers;\n if (!Array.isArray(triggers)) continue;\n const kinds = triggers.map((trigger) =>\n typeof trigger === \"object\" && trigger !== null ? (trigger as { kind?: unknown }).kind : undefined,\n );\n if (kinds.includes(\"analytics\") || kinds.includes(\"conversion\")) {\n const word = kinds.includes(\"analytics\") ? \"analytics\" : \"conversion\";\n throw new FunnelArgsError(\n `Step \"${key}\" has a \\`${word}\\` trigger, and a step is reached by an event or a landing only.`,\n {\n action: \"use_an_event_trigger\",\n explanation:\n 'Pick the moment from `baker funnel --moments` and write it as `{\"kind\":\"event\",\"eventKey\":\"<from moments.events or moments.sources>\"}` — nothing has to be named first. A landing is `{\"kind\":\"landing\",\"landingId\":\"<from moments.landings>\"}`.',\n },\n );\n }\n }\n}\n\nfunction parsePreset(raw: unknown): string | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (typeof raw !== \"string\" || !(FUNNEL_VERTICALS as readonly string[]).includes(raw)) {\n throw new FunnelArgsError(`--preset must be one of: ${FUNNEL_VERTICALS.join(\", \")}`, {\n action: \"list_the_presets\",\n explanation: \"Run `baker funnel --templates` to see what each preset contains before picking one.\",\n });\n }\n return raw;\n}\n\n/**\n * `--add-trigger <step> --event <key>`, or a refusal naming the missing half.\n *\n * Two flags rather than one JSON object, because that is the whole point of\n * this door: the thing being asked is \"this event should count on that step\",\n * and asking for JSON here would put the caller back in the business of\n * hand-writing a shape. The event key is still never invented — `--moments`\n * is where it comes from, and the refusal says so.\n */\nfunction parseWayIn(\n stepKey: unknown,\n eventKey: unknown,\n flag: string,\n): { stepKey: string; eventKey: string } | undefined {\n if (stepKey === undefined) return undefined;\n if (typeof stepKey !== \"string\" || stepKey.trim() === \"\") {\n throw new FunnelArgsError(`${flag} takes the key of the step to change: \\`${flag} lead\\`.`);\n }\n if (typeof eventKey !== \"string\" || eventKey.trim() === \"\") {\n throw new FunnelArgsError(`${flag} needs --event, naming what gets somebody there.`, {\n action: \"read_the_moments\",\n explanation: `Run \\`baker funnel --moments\\` and copy an \\`events[].eventKey\\` — a Form's own outcome is listed there at zero before anybody has filled it in. Then: \\`${flag} ${stepKey.trim()} --event \"<key>\"\\`.`,\n });\n }\n const key = eventKey.trim();\n // Refused here rather than at the backend, and only when ADDING: a key\n // nothing can produce does not error downstream, it counts zero for ever.\n // Removing is the opposite case — a key already on a step precisely because\n // it was wrong has to stay removable, whatever it parses as.\n if (flag === \"--add-trigger\" && parseEventKey(key) === null) {\n throw new FunnelArgsError(`\"${key}\" is not an event key, so nothing would ever reach that step through it.`, {\n action: \"read_the_moments\",\n explanation:\n \"Run `baker funnel --moments` and copy an `events[].eventKey`. They look like form:<form>:<step>:<trigger>, submit:<form>, page:<name> or view:<landing>.\",\n });\n }\n return { stepKey: stepKey.trim(), eventKey: key };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,oBAAoB;AAE7B,SAAS,qBAAqB;;;ACwC9B,SAAS,eAAe,MAAqC;AAC3D,SAAO,cAAc,KAAK,MAAM,KAAK,IAAI,EAAE,OAAO,CAAC,WAAW,OAAO,gBAAgB,OAAO,WAAW,IAAI;AAC7G;AAWO,SAAS,YACd,QACA,OACA,SACe;AACf,QAAM,UAAU,oBAAI,IAAoD;AACxE,aAAW,QAAQ,OAAO;AACxB,eAAW,UAAU,eAAe,IAAI,EAAG,SAAQ,IAAI,OAAO,UAAU,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC;AAAA,EACrG;AAEA,QAAM,QAAQ,OAAO,IAAI,CAAC,UAAU;AAClC,UAAM,QAAQ,QAAQ,IAAI,MAAM,QAAQ;AACxC,QAAI,UAAU,OAAW,QAAO;AAChC,YAAQ,OAAO,MAAM,QAAQ;AAC7B,WAAO,EAAE,GAAG,OAAO,MAAM,MAAM,OAAO,SAAS;AAAA,EACjD,CAAC;AACD,QAAM,UAAU,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,OAAO,MAAmB;AAC3E,UAAM,YAAY,QAAQ,IAAI,OAAO,QAAQ;AAC7C,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,GAAI,OAAO,aAAa,OAAO,CAAC,IAAI,EAAE,UAAU,OAAO,SAAS;AAAA,MAChE,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,MAAM,OAAO;AAAA,MACb,MAAM;AAAA,MACN,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,IACjD;AAAA,EACF,CAAC;AACD,SAAO,CAAC,GAAG,OAAO,GAAG,OAAO;AAC9B;AAGO,SAAS,gBAAgB,SAAkB,OAAiC,SAAgC;AACjH,SAAO,EAAE,GAAG,SAAS,QAAQ,YAAY,QAAQ,QAAQ,OAAO,OAAO,EAAE;AAC3E;AAUO,SAAS,eAAe,OAA2C;AACxE,SAAO,MAAM,OAAO,CAAC,SAAS,cAAc,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,IAAI;AACvG;AAoBO,SAAS,kBACd,OACA,OACA,SACU;AACV,QAAM,UAAU,IAAI;AAAA,IAClB,MAAM,QAAQ,CAAC,UAAU,KAAK,YAAY,CAAC,GAAG,QAAQ,CAAC,YAAa,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,CAAC,CAAE,CAAC;AAAA,EAClH;AACA,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,cAAc,KAAK,MAAM,KAAK,IAAI;AAClD,QAAI,QAAQ,WAAW,KAAK,QAAQ,KAAK,CAAC,WAAW,QAAQ,IAAI,OAAO,QAAQ,CAAC,EAAG;AACpF,UAAM,YAAY,QAAQ,OAAO,CAAC,WAAW,QAAQ,IAAI,OAAO,QAAQ,CAAC;AACzE,aAAS;AAAA,MACP,UAAU,WAAW,IACjB,KAAK,OACL,GAAG,KAAK,IAAI,gBAAgB,UAAU,IAAI,CAAC,WAAW,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,UAAU,OAAO,QAAQ,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACrI;AAAA,EACF;AACA,MAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,SAAO;AAAA,IACL,GAAG,SAAS,MAAM,kHAAkH,SAAS,KAAK,IAAI,CAAC;AAAA,EACzJ;AACF;;;ACjHO,IAAM,sBAAsB;AAAA;AAAA,EAEjC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;;;ACmEO,IAAM,wBAAuD;AAAA,EAClE;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,iBAAiB,WAAW,kBAAkB,eAAe,SAAS,SAAS;AAAA;AAAA;AAAA;AAAA,IAIzF,kBAAkB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,QAAQ,YAAY,gBAAgB,WAAW,WAAW,iBAAiB;AAAA,IACrF,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,QAAQ,UAAU,KAAK;AAAA,MAC1C,WAAW,EAAE,UAAU,mBAAmB;AAAA,MAC1C,UAAU,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC3C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,aAAa,WAAW,cAAc,gBAAgB,mBAAmB,UAAU;AAAA,IAC7F,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,wBAAwB,UAAU,KAAK;AAAA,MAC1D,WAAW,EAAE,UAAU,UAAU;AAAA,MACjC,UAAU,EAAE,MAAM,WAAW,UAAU,KAAK;AAAA,IAC9C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,iBAAiB,eAAe,MAAM;AAAA,IAChD,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,aAAa,UAAU,KAAK;AAAA,MAC/C,WAAW,EAAE,UAAU,cAAc;AAAA,MACrC,UAAU,EAAE,MAAM,eAAe,UAAU,KAAK;AAAA,IAClD;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,oBAAoB,oBAAoB,YAAY,gBAAgB;AAAA,IAC9E,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,oBAAoB,UAAU,KAAK;AAAA,MACtD,WAAW,EAAE,UAAU,iBAAiB;AAAA;AAAA;AAAA;AAAA,MAIxC,UAAU,EAAE,MAAM,kBAAkB,UAAU,OAAO,UAAU,QAAQ;AAAA,IACzE;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,SAAS,iBAAiB,cAAc,UAAU;AAAA,IAC5D,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,cAAc,UAAU,KAAK;AAAA,MAChD,WAAW,EAAE,UAAU,UAAU;AAAA,MACjC,UAAU,EAAE,MAAM,SAAS,UAAU,KAAK;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,kBAAkB,aAAa,OAAO,OAAO,wBAAwB,0BAA0B;AAAA,IACzG,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,iBAAiB,UAAU,MAAM;AAAA,MACpD,WAAW,EAAE,UAAU,iBAAiB;AAAA,MACxC,UAAU,EAAE,MAAM,kBAAkB,UAAU,KAAK;AAAA,IACrD;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,aAAa,WAAW,YAAY,aAAa,iBAAiB,oBAAoB;AAAA,IAChG,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,WAAW,UAAU,KAAK;AAAA,MAC7C,WAAW,EAAE,UAAU,UAAU;AAAA,MACjC,UAAU,EAAE,MAAM,WAAW,UAAU,KAAK;AAAA,IAC9C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,YAAY,UAAU,KAAK;AAAA,MAC9C,WAAW,EAAE,UAAU,mBAAmB;AAAA;AAAA;AAAA,MAG1C,UAAU,EAAE,MAAM,oBAAoB,UAAU,KAAK;AAAA,IACvD;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,gBAAgB,gBAAgB,aAAa,aAAa,kBAAkB,WAAW;AAAA,IACjG,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,eAAe,UAAU,MAAM;AAAA,MAClD,WAAW,EAAE,UAAU,QAAQ;AAAA,MAC/B,UAAU,EAAE,MAAM,SAAS,UAAU,KAAK;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,eAAe,iBAAiB,YAAY,eAAe,kBAAkB,cAAc,WAAW;AAAA,IAChH,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,eAAe,UAAU,MAAM;AAAA,MAClD,WAAW,EAAE,UAAU,gBAAgB;AAAA,MACvC,UAAU,EAAE,MAAM,iBAAiB,UAAU,KAAK;AAAA,IACpD;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS,CAAC,OAAO,cAAc,YAAY,YAAY,MAAM;AAAA,IAC7D,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,YAAY,UAAU,KAAK;AAAA;AAAA;AAAA,MAG9C,WAAW,EAAE,UAAU,YAAY,aAAa,iBAAiB;AAAA,MACjE,UAAU,EAAE,MAAM,YAAY,UAAU,KAAK;AAAA,IAC/C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,cAAc,wBAAwB,oBAAoB,QAAQ;AAAA,IAC5E,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,aAAa,UAAU,KAAK;AAAA,MAC/C,WAAW,EAAE,UAAU,WAAW;AAAA,MAClC,UAAU,EAAE,MAAM,YAAY,UAAU,KAAK;AAAA,IAC/C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WACE;AAAA,IACF,SAAS,CAAC,WAAW,WAAW,wBAAwB,aAAa,QAAQ;AAAA,IAC7E,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,WAAW,UAAU,MAAM;AAAA,MAC9C,WAAW,EAAE,UAAU,WAAW;AAAA,MAClC,UAAU,EAAE,MAAM,YAAY,UAAU,KAAK;AAAA,IAC/C;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS,CAAC,yBAAyB,eAAe,WAAW,sBAAsB;AAAA,IACnF,kBAAkB;AAAA,MAChB,MAAM,EAAE,WAAW,qBAAqB,UAAU,KAAK;AAAA,MACvD,WAAW,EAAE,UAAU,mBAAmB;AAAA,MAC1C,UAAU,EAAE,MAAM,sBAAsB,UAAU,KAAK;AAAA,IACzD;AAAA,EACF;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WACE;AAAA,IACF,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB,CAAC;AAAA,EACrB;AACF;AAEA,IAAM,mBAA4D,IAAI;AAAA,EACpE,sBAAsB,IAAI,CAAC,aAAa,CAAC,SAAS,KAAK,QAAQ,CAAU;AAC3E;AA8GO,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACjcA,SAAS,SAAS;AAKlB,IAAM,YAAY;AAElB,IAAM,eAAe;AAMd,IAAM,sBAAsB,EAAE,mBAAmB,QAAQ;AAAA,EAC9D,EAAE,OAAO;AAAA,IACP,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,IAC5B,MAAM,EAAE,QAAQ,OAAO;AAAA;AAAA,IAEvB,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACrC,CAAC;AAAA,EACD,EAAE,OAAO;AAAA,IACP,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,IAC5B,MAAM,EAAE,QAAQ,SAAS;AAAA;AAAA,IAEzB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACtC,CAAC;AAAA;AAAA,EAED,EAAE,OAAO;AAAA,IACP,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,IAC5B,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC/B,CAAC;AACH,CAAC;AAEM,IAAM,mBAAmB,EAAE,OAAO;AAAA;AAAA,EAEvC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA;AAAA,EAE7B,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACtC,WAAW,EAAE,KAAK,mBAAmB;AAAA;AAAA,EAErC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA,EAE5D,UAAU,EAAE,MAAM,mBAAmB,EAAE,IAAI,YAAY,EAAE,QAAQ,CAAC,CAAC;AACrE,CAAC;AAeM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,OAAO,EAAE,MAAM,gBAAgB,EAAE,IAAI,SAAS,EAAE,SAAS;AAAA;AAAA,EAEzD,MAAM,iBAAiB,SAAS;AAAA;AAAA,EAEhC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU/C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC,EAAE,SAAS;AAAA;AAAA,EAE5G,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC,EAAE,SAAS;AAAA;AAAA,EAE/G,QAAQ,EAAE,KAAK,gBAAgB,EAAE,SAAS;AAAA;AAAA,EAE1C,WAAW,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EAE9B,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhD,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,GAAI,EAAE,SAAS;AACxE,CAAC;AAWM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,MAAM,EAAE,OAAO;AAAA;AAAA,IAEb,SAAS,EAAE,OAAO;AAAA;AAAA,IAElB,QAAQ,EAAE,QAAQ;AAAA,IAClB,OAAO,EAAE;AAAA,MACP,EAAE,OAAO;AAAA,QACP,KAAK,EAAE,OAAO;AAAA,QACd,aAAa,EAAE,OAAO;AAAA,QACtB,WAAW,EAAE,OAAO;AAAA,QACpB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,QACzB,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMnD,OAAO,EAAE;AAAA,UACP,EAAE,OAAO;AAAA,YACP,IAAI,EAAE,OAAO;AAAA,YACb,UAAU,EAAE,OAAO;AAAA,YACnB,UAAU,EAAE,OAAO;AAAA,YACnB,OAAO,EAAE,OAAO;AAAA;AAAA,YAEhB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,UAC/B,CAAC;AAAA,QACH;AAAA;AAAA,QAEA,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,QAE5B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,QAE7B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,QAE5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,QAC3B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,QAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,MACnD,CAAC;AAAA,IACH;AAAA;AAAA,IAEA,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS;AAAA;AAAA,IAE5F,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAM/B,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOtC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,SAAS;AAAA;AAAA,IAEhF,SAAS,EACN,OAAO;AAAA,MACN,QAAQ,EAAE;AAAA,QACR,EAAE,OAAO;AAAA,UACP,UAAU,EAAE,OAAO;AAAA;AAAA,UAEnB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,UAE5B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,UAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,UAC9B,QAAQ,EAAE,OAAO;AAAA,UACjB,UAAU,EAAE,OAAO;AAAA;AAAA,UAEnB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAM/B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAO1B,MAAM,EAAE,QAAQ,EAAE,SAAS;AAAA,QAC7B,CAAC;AAAA,MACH;AAAA;AAAA,MAEA,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AAAA,MACzF,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAAA,IAC5F,CAAC,EACA,SAAS;AAAA,IACZ,WAAW,EACR;AAAA,MACC,EAAE,OAAO;AAAA,QACP,KAAK,EAAE,OAAO;AAAA,QACd,aAAa,EAAE,OAAO;AAAA,QACtB,SAAS,EAAE,OAAO;AAAA,QAClB,WAAW,EAAE,OAAO;AAAA,MACtB,CAAC;AAAA,IACH,EACC,SAAS;AAAA,IACZ,SAAS,EACN,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,aAAa,EAAE,OAAO,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAChH,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWZ,QAAQ,EACL;AAAA,MACC,EAAE,OAAO;AAAA,QACP,WAAW,EAAE,OAAO;AAAA,QACpB,OAAO,EAAE,OAAO;AAAA,QAChB,SAAS,EAAE,OAAO;AAAA,QAClB,MAAM,EAAE,OAAO;AAAA,QACf,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,GAAG,UAAU,EAAE,OAAO,EAAE,CAAC;AAAA,MACtF,CAAC;AAAA,IACH,EACC,SAAS;AAAA,EACd,CAAC;AAAA,EACD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AACtC,CAAC;;;ACjOM,IAAM,kBAAN,cAA8B,MAAM;AAAA;AAAA,EAEhC;AAAA,EAET,YAAY,SAAiB,KAA+C;AAC1E,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,QAAI,KAAK;AACP,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AACF;AAkBO,SAAS,gBAAgB,KAA0C;AACxE,QAAM,QAAQ,WAAW,IAAI,KAAK;AAClC,QAAM,OAAO,UAAU,IAAI,IAAI;AAC/B,QAAM,aAAa,gBAAgB,IAAI,aAAa,KAAK,IAAI,UAAU;AACvE,QAAM,SAAS,YAAY,IAAI,MAAM;AACrC,QAAM,aAAa,WAAW,IAAI,aAAa,KAAK,IAAI,YAAY,IAAI,OAAO,eAAe;AAC9F,QAAM,gBAAgB,WAAW,IAAI,gBAAgB,KAAK,IAAI,eAAe,IAAI,OAAO,kBAAkB;AAE1G,QAAM,QAAQ,CAAC,OAAO,MAAM,YAAY,QAAQ,YAAY,aAAa,EAAE;AAAA,IACzE,CAAC,SAAS,SAAS;AAAA,EACrB,EAAE;AACF,MAAI,QAAQ,GAAG;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,SAAS,SAAY,SAAY,OAAO,IAAI,IAAI;AACjE,MAAI,SAAS,WAAc,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAO,MAAM;AAC9E,UAAM,IAAI,gBAAgB,uDAAuD;AAAA,EACnF;AAEA,SAAO;AAAA,IACL,GAAI,UAAU,SAAY,CAAC,IAAI,EAAE,MAAM;AAAA,IACvC,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK;AAAA,IACrC,GAAI,eAAe,SAAY,CAAC,IAAI,EAAE,WAAW;AAAA,IACjD,GAAI,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,IACzC,GAAI,eAAe,SAAY,CAAC,IAAI,EAAE,WAAW;AAAA,IACjD,GAAI,kBAAkB,SAAY,CAAC,IAAI,EAAE,cAAc;AAAA,IACvD,WAAW,IAAI,cAAc;AAAA,IAC7B,SAAS,IAAI,YAAY;AAAA,IACzB,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK;AAAA,EACvC;AACF;AAOO,SAAS,YAAYA,OAAiC;AAC3D,QAAM,OAAO;AAAA,IACX,GAAIA,MAAK,UAAU,SAAY,CAAC,IAAI,EAAE,OAAOA,MAAK,MAAM;AAAA,IACxD,GAAIA,MAAK,SAAS,SAAY,CAAC,IAAI,EAAE,MAAMA,MAAK,KAAK;AAAA,IACrD,GAAIA,MAAK,eAAe,SAAY,CAAC,IAAI,EAAE,YAAYA,MAAK,WAAW;AAAA,IACvE,GAAIA,MAAK,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQA,MAAK,OAAO;AAAA,IAC3D,GAAIA,MAAK,eAAe,SAAY,CAAC,IAAI,EAAE,YAAYA,MAAK,WAAW;AAAA,IACvE,GAAIA,MAAK,kBAAkB,SAAY,CAAC,IAAI,EAAE,eAAeA,MAAK,cAAc;AAAA,IAChF,GAAIA,MAAK,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,IAC5C,GAAIA,MAAK,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IACxC,GAAIA,MAAK,SAAS,SAAY,CAAC,IAAI,EAAE,MAAMA,MAAK,KAAK;AAAA,EACvD;AACA,QAAM,SAAS,oBAAoB,UAAU,IAAI;AACjD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,QAAQ,OAAO,MAAM,OAAO,CAAC;AACnC,UAAM,IAAI;AAAA,MACR,qCAAgC,OAAO,KAAK,KAAK,GAAG,KAAK,OAAO,KAAK,OAAO,WAAW,SAAS;AAAA,MAChG;AAAA,QACE,QAAQ;AAAA,QACR,aACE;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,WAAW,KAAqC;AACvD,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,gBAAgB,sCAAsC;AAAA,EAClE;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,OAAO;AACd,UAAM,IAAI,gBAAgB,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,cAAc,IAAI;AAAA,MACjH,QAAQ;AAAA,MACR,aACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,MAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,UAAM,IAAI,gBAAgB,qEAAqE;AAAA,EACjG;AACA,sBAAoB,MAAM;AAC1B,SAAO;AACT;AAMA,SAAS,UAAU,KAAmC;AACpD,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,gBAAgB,yCAAyC;AAAA,EACrE;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,OAAO;AACd,UAAM,IAAI,gBAAgB,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,cAAc,IAAI;AAAA,MAChH,QAAQ;AAAA,MACR,aACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC1E,UAAM,IAAI,gBAAgB,oFAAoF;AAAA,EAChH;AACA,sBAAoB,CAAC,MAAM,CAAC;AAC5B,SAAO;AACT;AAEA,SAAS,gBAAgB,KAAkC;AACzD,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,MAAM,IAAI;AAChD,UAAM,IAAI,gBAAgB,kFAAkF;AAAA,EAC9G;AACA,SAAO,IAAI,KAAK;AAClB;AAYA,SAAS,oBAAoB,OAAiC;AAC5D,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAC/C,UAAM,MAAM,OAAQ,KAA2B,OAAO,GAAG;AACzD,QAAI,wBAAwB,MAAM;AAChC,YAAM,IAAI,gBAAgB,SAAS,GAAG,mEAAmE;AAAA,QACvG,QAAQ;AAAA,QACR,aACE;AAAA,MACJ,CAAC;AAAA,IACH;AACA,UAAM,WAAY,KAAgC;AAClD,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,UAAM,QAAQ,SAAS;AAAA,MAAI,CAAC,YAC1B,OAAO,YAAY,YAAY,YAAY,OAAQ,QAA+B,OAAO;AAAA,IAC3F;AACA,QAAI,MAAM,SAAS,WAAW,KAAK,MAAM,SAAS,YAAY,GAAG;AAC/D,YAAM,OAAO,MAAM,SAAS,WAAW,IAAI,cAAc;AACzD,YAAM,IAAI;AAAA,QACR,SAAS,GAAG,aAAa,IAAI;AAAA,QAC7B;AAAA,UACE,QAAQ;AAAA,UACR,aACE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,YAAY,KAAkC;AACrD,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,YAAY,CAAE,iBAAuC,SAAS,GAAG,GAAG;AACrF,UAAM,IAAI,gBAAgB,4BAA4B,iBAAiB,KAAK,IAAI,CAAC,IAAI;AAAA,MACnF,QAAQ;AAAA,MACR,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAWA,SAAS,WACP,SACA,UACA,MACmD;AACnD,MAAI,YAAY,OAAW,QAAO;AAClC,MAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM,IAAI;AACxD,UAAM,IAAI,gBAAgB,GAAG,IAAI,2CAA2C,IAAI,UAAU;AAAA,EAC5F;AACA,MAAI,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,IAAI;AAC1D,UAAM,IAAI,gBAAgB,GAAG,IAAI,oDAAoD;AAAA,MACnF,QAAQ;AAAA,MACR,aAAa,iKAA4J,IAAI,IAAI,QAAQ,KAAK,CAAC;AAAA,IACjM,CAAC;AAAA,EACH;AACA,QAAM,MAAM,SAAS,KAAK;AAK1B,MAAI,SAAS,mBAAmB,cAAc,GAAG,MAAM,MAAM;AAC3D,UAAM,IAAI,gBAAgB,IAAI,GAAG,4EAA4E;AAAA,MAC3G,QAAQ;AAAA,MACR,aACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,SAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,UAAU,IAAI;AAClD;;;ALvPA,IAAM,OAAO;AAAA,EACX,OAAO;AAAA,IACL,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,kBAAkB;AAAA,IAChB,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,EACZ;AACF;AAEA,eAAe;AAAA,EACb,SAAS;AAAA,EACT,aAAa;AAAA,EACb;AACF,CAAC;AAEM,IAAM,gBAAgB,cAAc;AAAA,EACzC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,EACA,KAAK,OAAO,EAAE,MAAM,IAAI,MAAM;AAC5B,QAAI;AACF,YAAM,SAAS,gBAAgB,GAA8B;AAG7D,YAAM,QAAQ,eAAe;AAC7B,YAAM,gBAAgB,eAAe,KAAK;AAC1C,YAAM,UAAyB;AAAA,QAC7B,GAAG,YAAY,MAAM;AAAA,QACrB,GAAI,cAAc,WAAW,IAAI,CAAC,IAAI,EAAE,cAAc;AAAA,MACxD;AACA,YAAM,WAAW,MAAM,QAAwB,eAAe,OAAO;AAGrE,YAAM,UAAU,IAAI,KAAK,SAAS,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,UAAU,IAAI,IAAI,CAAU,CAAC;AACrG,YAAM,OACJ,SAAS,KAAK,YAAY,SACtB,SAAS,OACT,EAAE,GAAG,SAAS,MAAM,SAAS,gBAAgB,SAAS,KAAK,SAAS,OAAO,OAAO,EAAE;AAC1F,YAAM,QAAQ,CAAC,GAAI,SAAS,SAAS,CAAC,GAAI,GAAG,kBAAkB,SAAS,KAAK,SAAS,CAAC,GAAG,OAAO,OAAO,CAAC;AACzG,wBAAkB;AAAA,QAChB,IAAI;AAAA,QACJ;AAAA,QACA,GAAI,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,MACtC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,kBAAY,GAAG;AAAA,IACjB;AAAA,EACF;AACF,CAAC;AAQD,SAAS,YAAY,KAAqB;AACxC,MAAI,eAAe,iBAAiB;AAClC,sBAAkB;AAAA,MAChB,IAAI;AAAA,MACJ,OAAO,EAAE,MAAM,oBAAoB,SAAS,IAAI,SAAS,GAAI,IAAI,MAAM,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,EAAG;AAAA,IAChG,CAAC;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,eAAe,UAAU;AAC3B,sBAAkB,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,oBAAkB;AAAA,IAChB,IAAI;AAAA,IACJ,OAAO,EAAE,MAAM,iBAAiB,SAAS,eAAe,QAAQ,IAAI,UAAU,mBAAmB;AAAA,EACnG,CAAC;AACD,UAAQ,KAAK,CAAC;AAChB;AAWA,SAAS,iBAAkC;AACzC,QAAM,QAAyB,CAAC;AAChC,aAAW,QAAQ,cAAc,GAAG;AAClC,QAAI;AACF,YAAM,KAAK,EAAE,MAAM,MAAM,KAAK,MAAM,aAAa,aAAa,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC;AAAA,IAClF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;","names":["args"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/actions/get.ts"],"sourcesContent":["import type { ActionsGetResponse } from \"@baker/api\";\nimport { defineCommand } from \"citty\";\nimport { apiPost, validateConvexId } from \"../../client.ts\";\nimport { writeJson } from \"../../output.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\nimport { failApi, failValidation } from \"./shared.ts\";\n\nregisterSchema({\n command: \"actions.get\",\n description: \"Get a single action with tags, blockers, blocked-by, and active-chat info.\",\n args: {\n id: { type: \"string\", description: \"Action ID\", required: true },\n },\n});\n\nexport const getCommand = defineCommand({\n meta: { name: \"get\", description: \"Get a single action by ID. Example: baker actions get <action-id>\" },\n args: {\n id: { type: \"positional\", description: \"Action ID\", required: false },\n \"action-id\": { type: \"string\", description: \"Action ID (alternative to positional)\", required: false },\n },\n run: async ({ args }) => {\n try {\n const id = (args.id as string | undefined) || (args[\"action-id\"] as string | undefined);\n if (!id) {\n failValidation(\"Action ID is required.\");\n }\n validateConvexId(id);\n const response = await apiPost<ActionsGetResponse>(\"/api/actions/get\", { actionId: id });\n writeJson(response);\n } catch (err) {\n failApi(err);\n }\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,qBAAqB;AAM9B,eAAe;AAAA,EACb,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM;AAAA,IACJ,IAAI,EAAE,MAAM,UAAU,aAAa,aAAa,UAAU,KAAK;AAAA,EACjE;AACF,CAAC;AAEM,IAAM,aAAa,cAAc;AAAA,EACtC,MAAM,EAAE,MAAM,OAAO,aAAa,oEAAoE;AAAA,EACtG,MAAM;AAAA,IACJ,IAAI,EAAE,MAAM,cAAc,aAAa,aAAa,UAAU,MAAM;AAAA,IACpE,aAAa,EAAE,MAAM,UAAU,aAAa,yCAAyC,UAAU,MAAM;AAAA,EACvG;AAAA,EACA,KAAK,OAAO,EAAE,KAAK,MAAM;AACvB,QAAI;AACF,YAAM,KAAM,KAAK,MAA8B,KAAK,WAAW;AAC/D,UAAI,CAAC,IAAI;AACP,uBAAe,wBAAwB;AAAA,MACzC;AACA,uBAAiB,EAAE;AACnB,YAAM,WAAW,MAAM,QAA4B,oBAAoB,EAAE,UAAU,GAAG,CAAC;AACvF,gBAAU,QAAQ;AAAA,IACpB,SAAS,KAAK;AACZ,cAAQ,GAAG;AAAA,IACb;AAAA,EACF;AACF,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/canvas/scaffold-static-ad.ts","../src/commands/canvas/scaffold-viewer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n AD_DESCRIBE_FOCUS,\n AD_DESCRIBE_MODEL,\n AD_FORMAT_PLATFORMS,\n AD_PLATFORMS,\n AD_SELECT_PROMPT,\n AD_SELECT_SYSTEM,\n type AdFormatPlatform,\n BLUEPRINT_ANALYSIS_MODEL,\n deliverySet,\n} from \"@baker/canvas-contract\";\nimport { referenceReadResponseSchema } from \"@baker/api/ad-library\";\nimport { defineCommand } from \"citty\";\nimport { apiPost } from \"../../client.ts\";\nimport { enforceNewCreativeLimit } from \"./creative-limit.ts\";\nimport type { BackendClient } from \"../../engine/client/backend-client.ts\";\nimport type { ValidationIssue } from \"../../engine/engine/errors.ts\";\nimport { createEngineFromEnv, defaultRegistry, ValidationError, validateCanvasDeep } from \"../../engine/index.ts\";\nimport { toModelSafeImage } from \"../../engine/nodes/ingest.ts\";\nimport { sha256Hex } from \"../../engine/storage/sha256.ts\";\nimport { fetchExternalBytes, MAX_REMOTE_IMAGE_BYTES } from \"../../lib/externalFetch.ts\";\nimport { brandBlock, readNamedTypefaces, readStaticBrandKit } from \"../../engine/scaffold/static-brand.ts\";\nimport { brandHeadlineFont, type ResolvedFont, webFontUrl } from \"../../engine/scaffold/static-fonts.ts\";\nimport { pickImageModel } from \"../../engine/lib/image-model-router.ts\";\nimport { conceptClassOf, conceptProblems, readAdSpec } from \"../../engine/scaffold/static-ad-spec.ts\";\nimport {\n castAvatarsOnStaticElements,\n type StaticAdOpts,\n scaffoldStaticAd,\n staticAdReport,\n staticAdSpec,\n staticAvatarsNotCast,\n} from \"../../engine/scaffold/staticAd.ts\";\nimport {\n fail,\n fileExists,\n loadAssetText,\n parseElements,\n resolveModel,\n} from \"./scaffold-common.ts\";\nimport { fetchJson, lookupGoogleCss } from \"../brand/fonts.ts\";\nimport { findBrandRoot } from \"./static-copy-projection.ts\";\nimport {\n buildCreativeDefinition,\n describeBlueprintIntent,\n resolveFormatList,\n resolvePlatform,\n setDefinitionViewer,\n titleFromSlug,\n} from \"./creative-definition.ts\";\nimport { buildBackendClient } from \"./run.ts\";\nimport { isValidScaffoldSlug, resolveScaffoldStaticAdPaths } from \"./scaffold-static-ad-paths.ts\";\nimport { resolveRelativeCanvasPaths } from \"./resolve-paths.ts\";\nimport { computeSourceSha } from \"./source-version.ts\";\nimport { syncCreativeDefinitionBestEffort } from \"./sync-definition.ts\";\nimport { type ScaffoldViewer, scaffoldViewer } from \"./scaffold-viewer.ts\";\nimport { describeViewer, liveViewerLookups } from \"./viewer-check.ts\";\nimport { resolveCastAvatars } from \"../avatars/resolveCast.ts\";\nimport {\n fetchLibraryReference,\n LIBRARY_PLATFORMS,\n type LibraryReference,\n libraryReferenceBlock,\n mergeReferenceIntoBlueprint,\n referenceDescribeContext,\n resolveSourcePlatform,\n} from \"./library-reference.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\nimport { conceptFromFit, fitHints, NO_FIT_HINT, readCachedFit } from \"../winning-ads/fit.ts\";\nimport { findWorkspaceRoot } from \"./normalize-paths.ts\";\nimport { marketFromCompanyDoc, VIEWER_TEMPERATURE_VALUES } from \"../../viewer-profile.ts\";\n\n/** A workspace file as the canvas refers to it: relative to the canvas, never absolute. */\nfunction toCanvasRelative(canvasDir: string, file: string): string {\n const rel = path.relative(canvasDir, file).split(path.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\n/**\n * Localize the source image to the durable content-addressed R2 store — downloading a URL first,\n * since winning-ad references are presigned and expire within the hour — so both the vision passes\n * and the emitted canvas ingest a stable URL that re-runs in any future sandbox. Raster source\n * images NEVER enter git (they bloated every client repo when this wrote a committed\n * `references/` copy); the R2 URL is content-addressed, so it survives forever and re-scaffolds\n * of the same source upload nothing new.\n *\n * The bytes are normalized through `toModelSafeImage` up front (rasterizing SVG/AVIF/HEIC/… to\n * PNG), so the first `baker canvas run` ingests a model-safe reference.\n */\nexport async function uploadSourceAsReference(\n source: string,\n isUrl: boolean,\n client: BackendClient,\n): Promise<string> {\n let bytes: Buffer;\n if (isUrl) {\n // Somebody else's server, so it climbs on a refusal. The R2 PUT below is\n // ours and deliberately stays a bare `fetch` — routing our own storage\n // through a metered exit would bill us for our own bytes.\n try {\n ({ buffer: bytes } = await fetchExternalBytes(source, { maxBytes: MAX_REMOTE_IMAGE_BYTES }));\n } catch (e) {\n throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);\n }\n } else {\n bytes = await readFile(source);\n }\n const safe = await toModelSafeImage(bytes);\n const sha256 = sha256Hex(safe.bytes);\n // \"source\": wired into the COMMITTED canvas.json — the GC registry pins it forever.\n const { putUrl, publicUrl } = await client.presignAssetUpload(sha256, safe.mime, undefined, \"source\");\n const putRes = await fetch(putUrl, {\n method: \"PUT\",\n body: new Uint8Array(safe.bytes),\n headers: { \"Content-Type\": safe.mime },\n });\n if (!putRes.ok) throw new Error(`source upload failed (${putRes.status})`);\n return publicUrl;\n}\n\n/**\n * The typeface the model is shown: the brand's font file, else the family tokens.json or\n * BRAND.md names (fetched as a web font file). Null when the brand names none: the model\n * then follows the blueprint's `fonts`, and no neutral default is put in front of it.\n */\nasync function brandFace(\n root: string,\n kit: { headlineFont?: string },\n toCanvas: (file: string) => string,\n): Promise<ResolvedFont | null> {\n const font = await brandHeadlineFont({\n file: kit.headlineFont,\n named: await readNamedTypefaces(root),\n resolveUrl: (family, weight) => webFontUrl(family, weight, { lookupGoogleCss, fetchJson }),\n });\n if (!font) return null;\n return \"path\" in font.slot ? { ...font, slot: { path: toCanvas(font.slot.path) } } : font;\n}\n\ntype Report = ReturnType<typeof staticAdReport>;\n\n/** What the checklist says about the copy. */\nfunction staticTextNote(report: Report): string {\n if (report.text_forward) {\n return \"The reference pictures nothing but copy, so this ad is its words on the brand's colours: keep prompt.json's \\`references\\` empty unless the concept wants a picture.\";\n }\n const face = report.font ? `in ${report.font}, shown to it as a specimen` : \"in the face `brand.typeface` names (the brand has no font file)\";\n return `The copy is set by the image model: every line of \\`copy\\` in prompt.json, exactly as written, once. A line marked \\`\"lettering\": \"designed\"\\` is set ${face}; a line marked \\`\"in_scene\"\\` is lettered on the object it names (\\`on\\`), the way \\`look\\` says. Edit the copy in prompt.json; every run compiles the render prompts from it again and the review reads every word back against it.`;\n}\n\n/** How each delivered format is made. */\nfunction staticFormatsNote(report: Report, genModel: string): string {\n const set =\n report.placements === null && report.platforms === null\n ? \" Pass --platforms meta,tiktok,google_display,… for every size where the ad runs.\"\n : \"\";\n const banners =\n report.banners.length > 0\n ? ` The fixed banners ${report.banners.join(\", \")} are not made from the picture: make them apart, from the delivered sizes, before staging a display campaign.`\n : \"\";\n const plan = report.format_plan;\n const made: Record<string, (format: string) => string> = {\n master: (format) => `${format} is the chosen picture (rendered at ${plan.master.renderRatio})`,\n relayout: (format) =>\n `${format} is the chosen ad re-laid out for that frame by the image model (one billed request): the same words, people, product and logo, re-scaled for the frame, inside its safe area`,\n crop: (format) => `${format} is cut from it, every word kept inside its safe area (free)`,\n adapt: (format) => `${format} is adapted from it by the reframe model (one billed request)`,\n };\n const how = plan.sizes.map((f) => made[f.from]?.(f.format) ?? f.format).join(\"; \");\n const again = plan.sizes.some((s) => s.from === \"relayout\") ? \"\" : \" No size is rendered again.\";\n return `Delivers ${report.output_formats.join(\", \")}, each at its exact size, all from one picture (${genModel}): ${how}.${again}${set}${banners}`;\n}\n\n/** Who the ad is for, in the output: the ad set and the viewer it reaches, when known. */\nfunction viewerOutput(who: ScaffoldViewer): Record<string, unknown> {\n return {\n ...(who.targetAdSet ? { target_adset: who.targetAdSet } : {}),\n ...(who.viewer\n ? {\n viewer: {\n temperature: who.viewer.temperature,\n knows: who.viewer.knows,\n placements: who.viewer.placements,\n source: who.viewer.source,\n },\n }\n : {}),\n };\n}\n\n/** What the checklist says about the viewer. */\nfunction viewerNote(who: ScaffoldViewer): string {\n if (!who.viewer) {\n return \"Nobody said who sees this ad: pass --target-adset <id> (or, with no ad account, --viewer-temperature from the Knowledge) so the sizes keep clear of its placements and every run checks the ad against that viewer. Until then it is checked against a cold stranger.\";\n }\n const sizes = who.adPlacements ? `the sizes keep clear of ${who.adPlacements.join(\", \")}` : \"the sizes keep clear of every placement (Advantage+)\";\n return `Made for ${describeViewer(who.viewer)}: write prompt.json's \\`viewer\\` for them, ${sizes}, and every run checks the ad against this viewer before it spends.`;\n}\n\n/** Resolve every model id, honoring overrides; an unknown override throws (see `resolveModel`). */\nfunction resolveModels(args: Record<string, unknown>) {\n return {\n describeModel: resolveModel(\"image_describe\", args[\"describe-model\"], AD_DESCRIBE_MODEL),\n selectModel: resolveModel(\"text_generate\", args[\"select-model\"], BLUEPRINT_ANALYSIS_MODEL),\n // The render model: `--gen-model`, else the router's model for the concept (decided once\n // the blueprint is read, `renderModelFor`). Resolved here only so a bad override fails for free.\n genModel: args[\"gen-model\"] ? resolveModel(\"image_generate\", args[\"gen-model\"], String(args[\"gen-model\"])) : undefined,\n };\n}\n\n// Each `_definition.md` platform maps to the placement preset a static ad on that\n// channel should ship. Channels whose default static placement is a single ratio\n// (x's 1.91:1 card is off-grid; \"other\" is unknown) get no fan-out — the base ad only.\nexport const PLATFORM_PLACEMENTS: Record<string, AdFormatPlatform | undefined> = {\n meta: \"meta_feed_stories\",\n google: \"google_demandgen\",\n linkedin: \"linkedin\",\n tiktok: \"tiktok\",\n youtube: \"youtube\",\n x: undefined,\n other: undefined,\n};\n\n/** A validated placement resolution — a preset (or undefined for no fan-out), or an error to fail fast on. */\nexport type PlacementsResolution =\n | { ok: true; placements: AdFormatPlatform | undefined }\n | { ok: false; message: string };\n\n/**\n * The placement set to fan the ad out to. `--placements` wins when given —\n * `none` disables fan-out, any AD_FORMAT_PLATFORMS value forces that set, and an\n * UNRECOGNIZED value is an error (it silently changes what gets billed/rendered,\n * so we surface the typo instead of falling back). With no flag, it's derived\n * from the definition platform (default meta → feed+stories).\n */\nexport function resolvePlacements(placementsFlag: string | undefined, definitionPlatform: string): PlacementsResolution {\n const flag = placementsFlag?.trim();\n if (flag === undefined || flag === \"\") return { ok: true, placements: PLATFORM_PLACEMENTS[definitionPlatform] };\n if (flag === \"none\") return { ok: true, placements: undefined };\n if ((AD_FORMAT_PLATFORMS as readonly string[]).includes(flag)) return { ok: true, placements: flag as AdFormatPlatform };\n return {\n ok: false,\n message: `--placements \"${flag}\" is not a valid placement set. Use one of: ${AD_FORMAT_PLATFORMS.join(\", \")}, or \"none\" for a single base ad.`,\n };\n}\n\n/**\n * `--platforms`: where the ad runs, as platforms or placement ids from the registry\n * (`AD_PLATFORMS`, `AD_PLACEMENTS`). A name the registry does not hold is refused rather than\n * dropped, since it silently changes the sizes made and billed.\n */\nexport function resolvePlatforms(\n flag: string | undefined,\n): { ok: true; platforms: string[] | undefined } | { ok: false; message: string } {\n const names = (flag ?? \"\").split(\",\").map((n) => n.trim()).filter(Boolean);\n if (names.length === 0) return { ok: true, platforms: undefined };\n const { unknown, formats, banners } = deliverySet(names);\n if (unknown.length > 0) {\n return { ok: false, message: `--platforms names ${unknown.join(\", \")}, which is no platform here. Use any of: ${AD_PLATFORMS.join(\", \")}.` };\n }\n if (formats.length === 0 && banners.length > 0) {\n return {\n ok: false,\n message: `--platforms ${names.join(\",\")} requires only fixed-size banners (${banners.map((b) => b.format).join(\", \")}), which are never made from the picture: add a platform with a feed or story size (meta, google_display, …).`,\n };\n }\n return { ok: true, platforms: names };\n}\n\n/**\n * Validate the scaffolded canvas as `canvas validate` will read it: canvas-relative paths\n * (`./prompt.json`) resolved against the canvas's own folder. A prompt over the model's cap\n * is the blueprint the describe pass wrote — already paid for, and fixed by editing\n * prompt.json — so it is reported as a hint on a written canvas, never an exit that\n * discards it. Every other issue still blocks.\n */\nexport async function validateScaffoldedStaticAd(\n canvas: unknown,\n canvasDir: string,\n): Promise<{ blocking: ValidationIssue[]; hints: string[]; estimatedCredits?: number }> {\n const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, canvasDir), defaultRegistry());\n if (validation.ok) return { blocking: [], hints: [], estimatedCredits: validation.estimatedCredits };\n const overCap = validation.issues.filter((i) => i.code === \"PROMPT_OVER_MODEL_LIMIT\");\n return {\n blocking: validation.issues.filter((i) => i.code !== \"PROMPT_OVER_MODEL_LIMIT\"),\n hints: overCap.map((i) => `${i.code}: ${i.message} Trim prompt.json, then \\`baker canvas validate\\`.`),\n };\n}\n\n/** The describe + element-selection canvas, run once up front; we read both node outputs after. */\nexport function buildDescribeCanvas(\n imageSource: string,\n imageIsUrl: boolean,\n describeModel: string,\n selectModel: string,\n context?: string,\n) {\n const originalParams = imageIsUrl\n ? { source: \"url\" as const, url: imageSource, expect: \"image\" as const }\n : { source: \"path\" as const, path: imageSource, expect: \"image\" as const };\n return {\n schema: \"baker-canvas/1\" as const,\n metadata: { name: \"static-ad describe pass\" },\n nodes: [\n { id: \"original\", type: \"ingest\", params: originalParams },\n {\n id: \"describe\",\n type: \"image_describe\",\n inputs: { image: \"$ref:original.asset\" },\n params: { model: describeModel, max_tokens: 9000, ...(context ? { context } : {}), focus: AD_DESCRIBE_FOCUS },\n },\n {\n id: \"select\",\n type: \"text_generate\",\n inputs: { blueprint: \"$ref:describe.description\" },\n params: {\n model: selectModel,\n max_tokens: 4000,\n temperature: 0,\n response_format: \"json_object\",\n system: AD_SELECT_SYSTEM,\n prompt: AD_SELECT_PROMPT,\n },\n },\n ],\n // No declared output: runVisionPasses reads both node outputs after the run.\n };\n}\n\n/** Run the vision passes and read back the blueprint and the selected elements. */\nasync function runVisionPasses(canvas: ReturnType<typeof buildDescribeCanvas>): Promise<{\n blueprint: unknown;\n elements: unknown[];\n creditsSpent: number | undefined;\n}> {\n const engine = createEngineFromEnv({ log: (line) => process.stderr.write(`${line}\\n`) });\n let outputsByNode: Record<string, Record<string, unknown>>;\n let creditsSpent: number | undefined;\n try {\n const result = await engine.run(canvas, {});\n outputsByNode = result.outputs_by_node;\n creditsSpent = (result.stats as { total_credits?: number })?.total_credits;\n } catch (e) {\n if (e instanceof ValidationError) return fail(\"validation\", JSON.stringify(e.issues));\n return fail(\"describe\", e instanceof Error ? e.message : String(e));\n }\n try {\n const blueprint = JSON.parse(await loadAssetText(outputsByNode.describe?.description, \"describe output\"));\n const elements = parseElements(await loadAssetText(outputsByNode.select?.text, \"selection output\"));\n return { blueprint, elements, creditsSpent };\n } catch (e) {\n return fail(\"read_outputs\", e instanceof Error ? e.message : String(e));\n }\n}\n\n/**\n * Whether the picture's read is the library's shared one: a library ad's own picture read with nothing of the\n * company's own (its context, another model) depends only on the ad, so it is read once for every\n * company (`/api/ad-library/reference-read`; dev, Oct 2026: each company paid ~40 s for the same read).\n */\nexport function sharesLibraryRead(args: Record<string, unknown>, fromLibrary: boolean): boolean {\n return fromLibrary && !args.file && !args.context && !args[\"describe-model\"] && !args[\"select-model\"];\n}\n\n/**\n * The library's shared read of the ad, null when it cannot be had (the run then reads the\n * picture itself). A first read takes as long as a local one; a reused one is instant and free.\n */\nasync function sharedLibraryRead(reference: LibraryReference): Promise<Awaited<ReturnType<typeof runVisionPasses>> | null> {\n try {\n const read = referenceReadResponseSchema.parse(\n await apiPost<unknown>(\n \"/api/ad-library/reference-read\",\n { ad_id: reference.ad_id, platform: reference.platform },\n { timeoutMs: 240_000 },\n ),\n );\n process.stderr.write(`[read ] ${read.reused ? \"the library's read of this ad, already made\" : \"read this ad for the library\"}\\n`);\n return {\n blueprint: read.blueprint,\n elements: parseElements(JSON.stringify({ elements: read.elements })),\n creditsSpent: read.credits_charged,\n };\n } catch (e) {\n process.stderr.write(`[read ] the library's read is unavailable (${e instanceof Error ? e.message : String(e)}); reading the picture here\\n`);\n return null;\n }\n}\n\nconst SOURCE_AD_ID_DESCRIPTION =\n \"The winners-library ad this creative adapts (the `ad_id` from `baker winning-ads search`). The library's read of \" +\n \"that ad (container, layout, colours, text blocks with their roles, persuasion mechanism, offer) is fetched and \" +\n \"written into prompt.json as `reference`, the describe pass is told it so the two reads agree, and the source is \" +\n \"recorded on the creative from this scaffold on. With it, the image is optional: the library's picture is used \" +\n \"when none is given. Pass it whenever the reference came from the library.\";\nconst PLATFORMS_DESCRIPTION =\n \"Where the ad runs, comma-separated platforms or placements (e.g. `meta,tiktok`, `google_display`, `meta_reels`): \" +\n \"every size they require is delivered, each kept clear of its own placement's interface (a 9:16 for TikTok keeps clear of TikTok's buttons). \" +\n `Platforms: ${AD_PLATFORMS.join(\", \")}. Wins over --placements. Fixed banner sizes (728x90…) are listed in the report, not made.`;\n\nconst SOURCE_PLATFORM_DESCRIPTION = `Which platform the library ad is on: ${LIBRARY_PLATFORMS.join(\"|\")} (default meta).`;\nconst TARGET_ADSET_DESCRIPTION =\n \"The Meta ad set this ad will run in (an id from `baker ads meta adsets`). Who it reaches is read off its targeting, \" +\n \"as `baker ads meta adsets --audience` reads it, and recorded in _definition.md as `targetAdSet` and `viewer`; the \" +\n \"sizes and the render brief keep clear of its placements, and every run checks the ad against that viewer. Free, \" +\n \"read before anything is billed. Start here whenever the ad is for a known ad set.\";\nconst VIEWER_TEMPERATURE_DESCRIPTION =\n \"With no ad set to read (no ad account, or not decided yet): how warm the viewer is, from the Knowledge (personas, \" +\n \"funnel): cold (never heard of the brand), warm (knows the brand), hot (a customer). Recorded as `viewer` with \" +\n \"`source: knowledge`. With --target-adset it corrects the temperature read off the ad set.\";\nconst VIEWER_KNOWS_DESCRIPTION =\n \"What the viewer already knows, comma-separated from brand,offer,product (e.g. `brand,offer`). Needs \" +\n \"--viewer-temperature or --target-adset.\";\nconst VIEWER_PLACEMENTS_DESCRIPTION =\n \"Where the viewer meets the ad, comma-separated Meta placements (e.g. `instagram_feed,facebook_feed`, \" +\n \"`instagram_stories`); default automatic (Advantage+, Reels included). The sizes keep clear of these placements' \" +\n \"interface. Needs --viewer-temperature or --target-adset.\";\nconst SOURCE_ADVERTISER_ID_DESCRIPTION =\n \"The library advertiser id of the reference (the `advertiser_id` beside the ad in search results). Defaults to the one the library returns for the ad.\";\n\nregisterSchema({\n command: \"canvas.scaffold-static-ad\",\n description:\n \"Turn a reference image into a runnable static-ad canvas: billed image_describe + element-selection passes, then a canvas on which the image model renders the finished ad from prompt.json. Start here for a static ad adapted from a winning ad: pass `--source-ad-id <ad_id> --source-platform <platform>` from the search result, so the library's structured read grounds the describe pass (and so prompt.json) and the source is recorded on the creative; the image may then be omitted. Use `--avatar <handle>` by default whenever the ad needs a person: inspect `baker avatars list`, choose a ready actor who fits the buyer, or create one with explicit identity and setting. Description-only casting remains available when explicitly requested.\",\n args: {\n file: {\n type: \"positional\",\n required: false,\n description: \"Path or http(s) URL to the reference image. Optional with --source-ad-id: the library's picture is used.\",\n },\n \"source-ad-id\": { type: \"string\", required: false, description: SOURCE_AD_ID_DESCRIPTION },\n \"source-platform\": {\n type: \"string\",\n required: false,\n description: SOURCE_PLATFORM_DESCRIPTION,\n enum: [...LIBRARY_PLATFORMS],\n },\n \"source-advertiser-id\": { type: \"string\", required: false, description: SOURCE_ADVERTISER_ID_DESCRIPTION },\n context: {\n type: \"string\",\n required: false,\n description: \"Known provenance (advertiser, category, market, their brand palette) to ground the describe pass\",\n },\n slug: {\n type: \"string\",\n required: false,\n description: \"Creative slug: writes src/creatives/<slug>/<slug>.canvas.json, prompt.json and _definition.md\",\n },\n title: { type: \"string\", required: false, description: \"Creative title for _definition.md (default: title-cased slug)\" },\n platform: {\n type: \"string\",\n required: false,\n description: \"Ad platform for _definition.md and the default placement set (meta|google|linkedin|tiktok|youtube|x|other; default meta)\",\n },\n advertiser: {\n type: \"string\",\n required: false,\n description: \"Source advertiser name for _definition.md (default: the library ad's advertiser when --source-ad-id is given)\",\n },\n placements: {\n type: \"string\",\n required: false,\n description: \"Formats delivered: meta_feed|meta_feed_stories|meta_all|linkedin|google_demandgen|tiktok|youtube, or none for a single base ad (default from --platform)\",\n },\n platforms: { type: \"string\", required: false, description: PLATFORMS_DESCRIPTION },\n aspect: { type: \"string\", required: false, description: \"Force the lead format (else inferred from the image)\" },\n \"target-adset\": { type: \"string\", required: false, description: TARGET_ADSET_DESCRIPTION },\n \"viewer-temperature\": {\n type: \"string\",\n required: false,\n description: VIEWER_TEMPERATURE_DESCRIPTION,\n enum: [...VIEWER_TEMPERATURE_VALUES],\n },\n \"viewer-knows\": { type: \"string\", required: false, description: VIEWER_KNOWS_DESCRIPTION },\n \"viewer-placements\": { type: \"string\", required: false, description: VIEWER_PLACEMENTS_DESCRIPTION },\n takes: {\n type: \"string\",\n required: false,\n description: \"Variants of the finished ad rendered (one request each) and judged, 1-3 (default 3). 1 skips the judge.\",\n },\n avatar: {\n type: \"string\",\n required: false,\n description: \"Cast company avatars as the ad's people, comma-separated in detection order (`marta` or `marta,joan`)\",\n },\n out: { type: \"string\", required: false, description: \"Output canvas path (default <image-dir>/static-ad.canvas.json)\" },\n \"describe-model\": { type: \"string\", required: false, description: \"Override the image_describe model id\" },\n \"select-model\": { type: \"string\", required: false, description: \"Override the element-selection text_generate model id\" },\n \"gen-model\": { type: \"string\", required: false, description: \"Override the image_generate model id\" },\n },\n});\n\n/** What the library said about the reference, or why nothing was fetched. Never fails the scaffold. */\ntype ReferenceLookup =\n | { status: \"none\" }\n | { status: \"grounded\"; reference: LibraryReference; mediaUrl: string | undefined }\n | { status: \"unavailable\"; adId: string; platform: string; message: string };\n\nasync function lookupReference(args: Record<string, unknown>): Promise<ReferenceLookup> {\n const adId = args[\"source-ad-id\"] ? String(args[\"source-ad-id\"]).trim() : \"\";\n if (!adId) return { status: \"none\" };\n const platformResult = resolveSourcePlatform(args[\"source-platform\"] ? String(args[\"source-platform\"]) : undefined);\n if (!platformResult.ok) return fail(\"invalid_source_platform\", platformResult.message);\n const platform = platformResult.platform;\n const advertiserId = args[\"source-advertiser-id\"] ? String(args[\"source-advertiser-id\"]).trim() : undefined;\n try {\n const ad = await fetchLibraryReference(adId, platform);\n return {\n status: \"grounded\",\n reference: libraryReferenceBlock(ad, { adId, platform, advertiserId }),\n mediaUrl: ad.media_url ?? undefined,\n };\n } catch (e) {\n return { status: \"unavailable\", adId, platform, message: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/**\n * A static ad started from an ad spec rather than a reference picture: the spec is the ad (its\n * message, viewer and whatever the agent still has to write), the brand and formats are ours,\n * and the canvas has no source picture. Free: nothing is described or billed.\n */\nasync function scaffoldFromSpec(args: Record<string, unknown>, slug: string | undefined): Promise<void> {\n if (!slug) return fail(\"missing_slug\", \"--spec needs --slug: an ad started from a spec is a creative.\");\n const specPath = path.resolve(process.cwd(), String(args.spec));\n let started: ReturnType<typeof readAdSpec>;\n try {\n started = readAdSpec(JSON.parse(await readFile(specPath, \"utf8\")));\n } catch (e) {\n return fail(\"invalid_spec\", `${specPath} is not an ad spec: ${e instanceof Error ? e.message : String(e)}`);\n }\n const outPath = path.join(process.cwd(), \"src\", \"creatives\", slug, `${slug}.canvas.json`);\n const dir = path.dirname(outPath);\n const blueprintPath = path.join(dir, \"prompt.json\");\n const definitionPath = path.join(dir, \"_definition.md\");\n await enforceNewCreativeLimit(slug, outPath);\n const existingDefinition = (await fileExists(definitionPath)) ? await readFile(definitionPath, \"utf8\") : null;\n const who = await scaffoldViewer(args, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);\n if (!who.ok) return fail(\"invalid_viewer\", who.message);\n await mkdir(dir, { recursive: true });\n\n const workspaceRoot = (await findBrandRoot(dir)) ?? process.cwd();\n const kit = await readStaticBrandKit(workspaceRoot);\n const market =\n who.viewer?.market ?? marketFromCompanyDoc(await readFile(path.join(workspaceRoot, \"COMPANY.md\"), \"utf8\").catch(() => null));\n const brand = brandBlock(kit);\n const platform = resolvePlatform(args.platform ? String(args.platform) : undefined);\n const placementsResult = resolvePlacements(args.placements ? String(args.placements) : undefined, platform);\n if (!placementsResult.ok) return fail(\"invalid_placements\", placementsResult.message);\n const font = await brandFace(workspaceRoot, kit, (file) => toCanvasRelative(dir, file));\n const genModel = pickImageModel(conceptClassOf(started), \"generate\");\n const opts: StaticAdOpts = {\n genModel,\n blueprintPath: \"./prompt.json\",\n placements: placementsResult.placements,\n ...(who.adPlacements ? { adPlacements: who.adPlacements } : {}),\n brand: { logoPath: kit.logo ? toCanvasRelative(dir, kit.logo) : undefined, ...(font ? { font } : {}) },\n };\n const report = staticAdReport({}, [], { ...opts, spec: started });\n const spec = {\n ...started,\n ...(brand ? { brand: { ...brand, ...started.brand } } : {}),\n formats: report.output_formats,\n logo: Boolean(kit.logo),\n ...(market ? { viewer: { ...(started.viewer ?? { who: \"the people this ad is for\" }), market } } : {}),\n };\n await writeFile(blueprintPath, `${JSON.stringify(spec, null, 2)}\\n`, \"utf8\");\n const canvas = scaffoldStaticAd({}, [], { ...opts, spec });\n const validation = await validateScaffoldedStaticAd(canvas, dir);\n if (validation.blocking.length > 0) return fail(\"validation\", validation.blocking.map((i) => i.message).join(\"; \"));\n await writeFile(outPath, `${JSON.stringify(canvas, null, 2)}\\n`, \"utf8\");\n\n const title = args.title ? String(args.title) : titleFromSlug(slug);\n const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};\n if (existingDefinition === null) {\n const formats = resolveFormatList(report.output_formats);\n await writeFile(definitionPath, buildCreativeDefinition({ title, kind: \"static\", platform, formats, ...recorded }), \"utf8\");\n }\n await syncCreativeDefinitionBestEffort({\n slug,\n title,\n platform,\n formats: resolveFormatList(report.output_formats),\n canvas,\n sourceSha: await computeSourceSha(outPath),\n });\n process.stdout.write(\n `${JSON.stringify(\n {\n ok: true,\n canvas_path: outPath,\n prompt_path: blueprintPath,\n definition_path: definitionPath,\n output: canvas.output,\n hints: [...who.notes, ...validation.hints],\n ...viewerOutput(who),\n output_formats: report.output_formats,\n stats: { total_nodes: canvas.nodes.length, describe_credits_spent: 0, run_estimated_credits: validation.estimatedCredits },\n },\n null,\n 2,\n )}\\n`,\n );\n}\n\nexport const scaffoldStaticAdCommand = defineCommand({\n meta: {\n name: \"scaffold-static-ad\",\n description:\n \"Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes — image_describe (read \" +\n \"into prompt.json, the ad spec you rewrite) and an AI selection of the image's MAIN identity elements — then \" +\n \"scaffolds a canvas on which the image model renders the FINISHED ad: up to three variants, one request each, \" +\n \"from a brief compiled from prompt.json, the copy quoted verbatim, the brand's typeface shown as a specimen and \" +\n 'the logo from src/brand/logos/ as a reference (only when prompt.json has \"logo\": true). A prefilter drops ' +\n \"variants whose words or placement fail the checks, and a judge compares the rest pairwise against the brief; \" +\n \"the chosen picture is delivered at the lead format (4:5 for a feed) and every other placement format is that \" +\n \"ad re-laid out for its own frame by one image request, with the chosen picture as the reference: the same \" +\n \"words, people, product and logo, re-scaled for the frame, never cut or shrunk onto a background. The run reads every word back and refuses to call the ad ready when one is off. No copy means the picture is the ad. Pass --placements none for a single base ad, \" +\n \"or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it. A person \" +\n \"is cast from a suitable company Avatar by default: pass `--avatar <handle>` (`baker avatars list`), \" +\n \"or create one with explicit identity and buyer setting. A description-only one-off remains available when \" +\n \"explicitly requested. Never cast a stranger from a searched photo. A reference from the library: pass \" +\n \"`--source-ad-id <ad_id> --source-platform <platform>` so its structured read grounds the describe pass and the \" +\n \"source is recorded on the creative.\",\n },\n args: {\n file: {\n type: \"positional\",\n required: false,\n description: \"Path or http(s) URL to the source/inspiration image (optional with --source-ad-id)\",\n },\n \"source-ad-id\": { type: \"string\", description: SOURCE_AD_ID_DESCRIPTION },\n spec: {\n type: \"string\",\n description:\n \"Start from an ad spec (a prompt.json) instead of a reference picture: no describe pass, no source. `baker creatives make` writes one from an idea when no proven ad fits. Needs --slug.\",\n },\n \"source-platform\": { type: \"string\", description: SOURCE_PLATFORM_DESCRIPTION },\n \"source-advertiser-id\": { type: \"string\", description: SOURCE_ADVERTISER_ID_DESCRIPTION },\n context: { type: \"string\", description: \"Known provenance (advertiser, category, market) to ground the describe\" },\n out: { type: \"string\", description: \"Output canvas path (default <image-dir>/static-ad.canvas.json)\" },\n slug: {\n type: \"string\",\n description: \"Creative slug — writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)\",\n },\n title: { type: \"string\", description: \"Creative title for _definition.md (default: title-cased slug)\" },\n platform: {\n type: \"string\",\n description:\n \"Ad platform (meta|google|linkedin|tiktok|youtube|x|other; default meta). Sets _definition.md AND, unless --placements is given, the formats the ad is delivered in (meta → 4:5+9:16, google → 1:1+4:5+16:9, linkedin → 1:1, tiktok → 9:16, youtube → 16:9; x/other → one format).\",\n },\n advertiser: {\n type: \"string\",\n description: \"Source advertiser recorded in _definition.md (default: the library ad's advertiser)\",\n },\n \"describe-model\": { type: \"string\", description: \"Override the image_describe model id\" },\n \"select-model\": { type: \"string\", description: \"Override the text_generate model id for element selection\" },\n \"gen-model\": { type: \"string\", description: \"Override the image_generate model id\" },\n aspect: {\n type: \"string\",\n description:\n \"Force the ad's lead format (else inferred from the image). Delivered exactly: the ad renders at the model's nearest shape and is cropped to it.\",\n },\n \"target-adset\": { type: \"string\", description: TARGET_ADSET_DESCRIPTION },\n \"viewer-temperature\": { type: \"string\", description: VIEWER_TEMPERATURE_DESCRIPTION },\n \"viewer-knows\": { type: \"string\", description: VIEWER_KNOWS_DESCRIPTION },\n \"viewer-placements\": { type: \"string\", description: VIEWER_PLACEMENTS_DESCRIPTION },\n placements: {\n type: \"string\",\n description:\n \"Formats the ad is delivered in. One of meta_feed | meta_feed_stories | meta_all | linkedin | google_demandgen | tiktok | youtube, or `none` for a single base ad. Default: derived from --platform (meta → feed + stories). Every format is made from the one chosen picture: the picture itself (the 4:5 when it is delivered), or that ad re-laid out for the format's own frame (one billed request per format).\",\n },\n platforms: { type: \"string\", description: PLATFORMS_DESCRIPTION },\n takes: {\n type: \"string\",\n description: \"Variants of the finished ad to render (one request each) and judge, 1-3 (default 3). 1 skips the judge.\",\n },\n avatar: {\n type: \"string\",\n description:\n \"Cast company avatars as the ad's people — preferred whenever an ad needs an actor, including a single ad. \" +\n \"Choose a ready avatar who fits the buyer, or create one with explicit identity and setting. One handle, or \" +\n \"several comma-separated in the order the people were detected (`--avatar marta` / `--avatar marta,joan`). \" +\n \"Each person slot grounds on that avatar's identity sheet and prompt.json describes them in the avatar's own \" +\n \"words, so the ad shows the company's cast instead of the source ad's. `baker avatars list` shows who there is; \" +\n \"checked before anything is billed.\",\n },\n },\n async run({ args }) {\n const slug = args.slug ? String(args.slug) : undefined;\n if (slug && !isValidScaffoldSlug(slug)) {\n process.stderr.write(\n `${JSON.stringify({ ok: false, error: { code: \"invalid_slug\", message: \"--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars\" } }, null, 2)}\\n`,\n );\n process.exit(2);\n }\n // The library's read of the reference, fetched before anything bills. A failed fetch\n // with an image given degrades to a scaffold without the DNA (warned, and said in hints);\n // with no image there is nothing to describe, so it stops here.\n if (args.spec) return scaffoldFromSpec(args, slug);\n const lookup = await lookupReference(args);\n if (lookup.status === \"unavailable\") {\n process.stderr.write(\n `[warn] winners-library ad ${lookup.adId} (${lookup.platform}) could not be read (${lookup.message}); scaffolding without its structured read\\n`,\n );\n }\n const reference = lookup.status === \"grounded\" ? lookup.reference : undefined;\n const fileArg = args.file ? String(args.file) : undefined;\n const sourceFile = fileArg ?? (lookup.status === \"grounded\" ? lookup.mediaUrl : undefined);\n if (!sourceFile) {\n if (lookup.status === \"none\") return fail(\"missing_source\", \"Pass the reference image, or --source-ad-id <ad_id> to use a library ad.\");\n if (lookup.status === \"unavailable\") return fail(\"source_unavailable\", `No image given and the library ad could not be read (${lookup.message}).`);\n return fail(\"source_unavailable\", `The library holds no picture for ad ${reference?.ad_id}; pass the image.`);\n }\n\n const { imageIsUrl, imageSource, outPath, blueprintPath, definitionPath, referencesDir } =\n resolveScaffoldStaticAdPaths(sourceFile, args.out ? String(args.out) : undefined, process.cwd(), slug);\n\n if (slug) await enforceNewCreativeLimit(slug, outPath);\n\n // Who the ad is for, read before anything is billed: a bad flag fails for free, and an ad\n // set that cannot be read only costs the ad its narrower placements.\n const existingDefinition =\n definitionPath && (await fileExists(definitionPath)) ? await readFile(definitionPath, \"utf8\") : null;\n const who = await scaffoldViewer(args, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);\n if (!who.ok) return fail(\"invalid_viewer\", who.message);\n\n // The fit made of this reference (`baker winning-ads fit`), read before anything bills: a\n // reference judged not to fit is refused here, and one that fits seeds prompt.json's concept.\n const fitRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();\n const fit =\n reference || lookup.status === \"unavailable\"\n ? await readCachedFit(fitRoot, reference?.platform ?? (lookup.status === \"unavailable\" ? lookup.platform : \"meta\"), reference?.ad_id ?? (lookup.status === \"unavailable\" ? lookup.adId : \"\"))\n : null;\n const fitProblems = fit ? fitHints(fit, reference?.ad_id ?? \"\").filter((h) => h.startsWith(\"Pick another reference\")) : [];\n if (fitProblems.length > 0) return fail(\"concept_does_not_fit\", fitProblems.join(\" \"));\n\n // Create the creative folder up front (was missing — the agent had to `mkdir -p` and re-run,\n // double-billing the vision passes) and fail before any billed work if the path is unwritable.\n await mkdir(path.dirname(outPath), { recursive: true });\n\n let models: ReturnType<typeof resolveModels>;\n try {\n models = resolveModels(args);\n } catch (e) {\n return fail(\"unknown_model\", e instanceof Error ? e.message : String(e));\n }\n const { describeModel, selectModel } = models;\n\n // Resolved before the billed passes: a handle that cannot be cast fails for free.\n const cast = await resolveCastAvatars(args.avatar);\n if (\"error\" in cast) return fail(\"avatar\", cast.error);\n\n // Localize the source to durable storage BEFORE any billed pass: presigned\n // winning-ad URLs expire within the hour, the raster bytes must never land\n // in git, and the vision passes + emitted canvas then read one stable URL\n // (a dead source aborts here, before any credits are spent).\n let durableSourceUrl: string | undefined;\n if (referencesDir) {\n const client = buildBackendClient();\n if (client) {\n try {\n durableSourceUrl = await uploadSourceAsReference(imageSource, imageIsUrl, client);\n } catch (e) {\n return fail(\"source_unavailable\", e instanceof Error ? e.message : String(e));\n }\n }\n }\n\n // One run: ingest → describe → select. We read both node outputs after.\n const describeCanvas = buildDescribeCanvas(\n durableSourceUrl ?? imageSource,\n durableSourceUrl !== undefined || imageIsUrl,\n describeModel,\n selectModel,\n referenceDescribeContext(reference, args.context ? String(args.context) : undefined),\n );\n const shared =\n reference && sharesLibraryRead(args, lookup.status === \"grounded\") ? await sharedLibraryRead(reference) : null;\n const vision = shared ?? (await runVisionPasses(describeCanvas));\n const { creditsSpent } = vision;\n // The library's read joins the blueprint as `reference` (bookkeeping, never a mechanism).\n const blueprint = mergeReferenceIntoBlueprint(vision.blueprint, reference);\n // Cast by who the ad is for (D2): the definition's persona, else the viewer the ad set reaches.\n const persona = existingDefinition?.match(/^persona:\\s*\"?([^\"\\n]+)\"?\\s*$/m)?.[1];\n const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars, persona);\n const uncast = staticAvatarsNotCast(elements, cast.handles);\n\n // The brand's colours ride in prompt.json as `brand`: the brief ties each to an object and\n // never borrows a colour from the reference ad.\n const canvasDir = path.dirname(outPath);\n const workspaceRoot = (await findBrandRoot(canvasDir)) ?? process.cwd();\n const kit = await readStaticBrandKit(workspaceRoot);\n // Where the ad runs: the ad set's countries, else the market COMPANY.md names (D1).\n const market =\n who.viewer?.market ?? marketFromCompanyDoc(await readFile(path.join(workspaceRoot, \"COMPANY.md\"), \"utf8\").catch(() => null));\n const brand = brandBlock(kit);\n\n // Creative convention: point the canvas at the durable source URL (content-addressed,\n // never expires, re-runs in any fresh sandbox with zero git bloat) and at a canvas-relative\n // blueprint path. Ad-hoc canvases (no slug) keep their verbatim source + absolute blueprint path.\n let canvasImagePath = imageSource;\n let canvasImageIsUrl = imageIsUrl;\n let canvasBlueprintPath = blueprintPath;\n if (referencesDir) {\n canvasBlueprintPath = \"./prompt.json\";\n if (durableSourceUrl) {\n canvasImagePath = durableSourceUrl;\n canvasImageIsUrl = true;\n }\n }\n\n const definitionPlatform = resolvePlatform(args.platform ? String(args.platform) : undefined);\n const placementsResult = resolvePlacements(\n args.placements ? String(args.placements) : undefined,\n definitionPlatform,\n );\n if (!placementsResult.ok) return fail(\"invalid_placements\", placementsResult.message);\n const placements = placementsResult.placements;\n const platformsResult = resolvePlatforms(args.platforms ? String(args.platforms) : undefined);\n if (!platformsResult.ok) return fail(\"invalid_platforms\", platformsResult.message);\n\n // The brand's typeface and logo: the canvas points at them by canvas-relative path, so\n // the render reads the real files.\n const relative = (file: string | undefined) => (file ? toCanvasRelative(canvasDir, file) : undefined);\n const takes = args.takes !== undefined ? Number.parseInt(String(args.takes), 10) : undefined;\n if (takes !== undefined && !(takes >= 1 && takes <= 3)) return fail(\"invalid_takes\", \"--takes must be 1, 2 or 3\");\n const font = await brandFace(workspaceRoot, kit, (file) => toCanvasRelative(canvasDir, file));\n\n // The render model the router picks for this kind of concept, unless `--gen-model` pinned one.\n const genModel = models.genModel ?? pickImageModel(conceptClassOf(staticAdSpec(blueprint, elements)), \"generate\");\n const opts: StaticAdOpts = {\n genModel,\n imagePath: canvasImagePath,\n imageIsUrl: canvasImageIsUrl,\n blueprintPath: canvasBlueprintPath,\n aspectRatio: args.aspect ? String(args.aspect) : undefined,\n placements,\n ...(platformsResult.platforms ? { platforms: platformsResult.platforms } : {}),\n ...(who.adPlacements ? { adPlacements: who.adPlacements } : {}),\n brand: { logoPath: relative(kit.logo), ...(font ? { font } : {}) },\n takes,\n };\n\n // prompt.json is the ad spec the agent rewrites into our ad; every render prompt is\n // compiled from it (static-brief.ts), here and again on every run.\n let canvas: ReturnType<typeof scaffoldStaticAd>;\n let report: ReturnType<typeof staticAdReport>;\n let conceptNotes: string[] = [];\n try {\n report = staticAdReport(blueprint, elements, { ...opts, spec: staticAdSpec(blueprint, elements, { logo: Boolean(kit.logo) }) });\n const spec = staticAdSpec(blueprint, elements, {\n brand,\n formats: report.output_formats,\n logo: Boolean(kit.logo),\n ...(fit ? { fit: conceptFromFit(fit, reference?.ad_id ?? \"\") } : {}),\n ...(market ? { market } : {}),\n });\n await writeFile(blueprintPath, `${JSON.stringify(spec, null, 2)}\\n`, \"utf8\");\n conceptNotes = conceptProblems(spec).map(\n (p) => `CONCEPT: ${p} Then \\`baker canvas run\\`; until then it refuses to render.`,\n );\n if (!fit && reference) {\n conceptNotes.push(\n `${NO_FIT_HINT}: \\`baker winning-ads fit ${reference.ad_id}\\` says what it wins on and what we have for it, and re-scaffolding seeds prompt.json's \\`concept\\` from it.`,\n );\n }\n canvas = scaffoldStaticAd(blueprint, elements, { ...opts, spec });\n } catch (e) {\n return fail(\"scaffold\", e instanceof Error ? e.message : String(e));\n }\n\n const validation = await validateScaffoldedStaticAd(canvas, path.dirname(outPath));\n if (validation.blocking.length > 0) {\n process.stderr.write(\n `${JSON.stringify({ ok: false, error: { code: \"validation\", issues: validation.blocking } }, null, 2)}\\n`,\n );\n process.exit(2);\n }\n\n await writeFile(outPath, `${JSON.stringify(canvas, null, 2)}\\n`, \"utf8\");\n\n // The library ids, recorded on the creative from this scaffold on (the definition and the\n // sync alike), so `baker_uses` in reference search counts this adaptation before Publish.\n const sourceFields = reference\n ? {\n sourceAdId: reference.ad_id,\n sourcePlatform: reference.platform,\n ...(reference.advertiser_id ? { sourceAdvertiserId: reference.advertiser_id } : {}),\n }\n : lookup.status === \"unavailable\"\n ? {\n sourceAdId: lookup.adId,\n sourcePlatform: lookup.platform,\n ...(args[\"source-advertiser-id\"] ? { sourceAdvertiserId: String(args[\"source-advertiser-id\"]).trim() } : {}),\n }\n : {};\n\n // Auto-write _definition.md so the creative joins the content collection immediately (title,\n // platform, source metadata) — the agent no longer hand-writes it. Preserve one on re-scaffold.\n const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};\n if (definitionPath && existingDefinition !== null && who.record) {\n await writeFile(definitionPath, setDefinitionViewer(existingDefinition, recorded), \"utf8\");\n }\n if (definitionPath && existingDefinition === null) {\n await writeFile(\n definitionPath,\n buildCreativeDefinition({\n title: args.title ? String(args.title) : titleFromSlug(slug ?? \"\"),\n kind: \"static\",\n platform: definitionPlatform,\n formats: resolveFormatList(report.output_formats),\n sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : undefined),\n sourceAdvertiser: args.advertiser\n ? String(args.advertiser)\n : (reference?.advertiser ?? (args.context ? String(args.context) : undefined)),\n ...sourceFields,\n sourceKind: \"image\",\n ...recorded,\n description: describeBlueprintIntent(blueprint),\n }),\n \"utf8\",\n );\n }\n\n // Push the parsed node-graph so the dashboard can draw this creative's\n // workflow immediately — before the first (billed) run. Best-effort.\n if (slug) {\n await syncCreativeDefinitionBestEffort({\n slug,\n title: args.title ? String(args.title) : titleFromSlug(slug),\n platform: definitionPlatform,\n formats: resolveFormatList(report.output_formats),\n sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : undefined),\n ...sourceFields,\n canvas,\n sourceSha: await computeSourceSha(outPath),\n });\n }\n\n const hints = [\n ...who.notes,\n ...(lookup.status === \"unavailable\"\n ? [\n `REFERENCE NOT READ: the winners library could not return ad ${lookup.adId} (${lookup.message}). prompt.json is read from the picture alone and has no \\`reference\\` block; the source ids are still recorded. Check the ad id and platform, or continue from the picture.`,\n ]\n : []),\n ...(uncast.length\n ? [\n `NOT CAST: ${uncast.map((h) => `\\`${h}\\``).join(\", \")} — the element selection found fewer people than avatars passed, so ${uncast.length === 1 ? \"this avatar plays\" : \"these avatars play\"} nobody in this ad. Tell the user who is cast and who is not.`,\n ]\n : []),\n ...validation.hints,\n ...conceptNotes,\n ...report.describe_gaps.map((gap) =>\n gap === \"winning_mechanisms\"\n ? \"The describe pass named no winning mechanism. Look at the source ad, add a `mechanism` entry to prompt.json ({ device, why_it_works }) and rebuild that device for our brand; a rewrite without it keeps the look and loses what made the ad work.\"\n : \"The describe pass recorded no typefaces. The designed lines are set in the brand's font when one loads (src/brand/fonts/, tokens.json or BRAND.md); with none, name our face in prompt.json's `brand.typeface` (for example \\\"a heavy geometric sans\\\"), or they are set in a plain sans.\",\n ),\n ];\n process.stdout.write(\n `${JSON.stringify(\n {\n ok: true,\n canvas_path: outPath,\n prompt_path: blueprintPath,\n definition_path: definitionPath ?? undefined,\n source_reference: durableSourceUrl ?? undefined,\n ...(reference\n ? {\n reference: {\n ad_id: reference.ad_id,\n platform: reference.platform,\n advertiser_id: reference.advertiser_id,\n format_pattern: reference.format_pattern,\n mechanism: reference.message.mechanism,\n },\n }\n : {}),\n output: canvas.output,\n ...(hints.length ? { hints } : {}),\n models: { describe: describeModel, select: selectModel, gen: opts.genModel },\n aspect_ratio: report.aspect_ratio,\n placements: report.placements,\n ...viewerOutput(who),\n output_formats: report.output_formats,\n stats: {\n total_nodes: canvas.nodes.length,\n describe_credits_spent: creditsSpent,\n run_estimated_credits: validation.estimatedCredits,\n },\n checklist: {\n edit_prompt: `Edit ${path.basename(blueprintPath)}: it is the ad spec, read from your image, to rewrite into the ad you want. \\`viewer\\` (who sees it and what they already know), \\`message\\` (the one thing it tells them), \\`copy\\` (each line, \\`in_scene\\` or \\`designed\\`), \\`scene\\`, \\`references\\` (one picture each, by role), \\`colors\\` (each tied to an object, from the brand) and an optional \\`direction\\`. You never write a model prompt: every render prompt is compiled from this file and shown on its node.${reference ? \" Its `reference` block is the library's read of the source ad (container, layout, mechanism, offer, text blocks): keep it as the account of why the ad won, and rebuild it for our brand.\" : \"\"}`,\n mechanism: report.winning_mechanisms,\n rebuild_the_mechanism:\n \"`mechanism` is WHY the source ad works. Rebuild each device for our brand in prompt.json's copy and scene (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.\",\n assets_to_supply: report.elements,\n summary: report.summary,\n text: staticTextNote(report),\n font: report.font,\n in_scene_text: report.in_scene_text,\n logo: report.logo.why,\n takes:\n report.takes === 1\n ? \"1 finished ad is rendered and used as it is (no judge).\"\n : `${report.takes} variants of the finished ad are rendered, one request each on its own direction. A prefilter drops any whose words or placement fail the checks, and the judge compares the rest pairwise against the brief (words, picture, space, one idea, the brief itself, brand); the winner becomes the ad and the others stay in the run as options the person can switch to (\\`baker canvas run --use-variant\\`).`,\n pictures:\n \"The reference's own people, products, animals and badges are not slots to fill: they are the source's world. Cast a person from a suitable ready company Avatar by default (--avatar <handle>); create one from the buyer context if none fits. A description-only one-off is available when explicitly requested; never use a stranger's found photo. To show our own product or another picture, add it to prompt.json's `references` with its `image` (a path from this folder, or a URL): every run wires it.\",\n formats: staticFormatsNote(report, opts.genModel),\n viewer: viewerNote(who),\n note: `Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away, one at a time. The logo is optional and never a slot to fill: it is given to the model only when prompt.json has \\`\"logo\": true\\` and src/brand/logos/ has the real file; never fetch or draw a stand-in. When every slot is filled, \\`baker canvas validate\\` then \\`baker canvas run\\`. Running generates billed images: it is not free.`,\n },\n },\n null,\n 2,\n )}\\n`,\n );\n },\n});\n","import {\n VIEWER_KNOWS_VALUES,\n VIEWER_TEMPERATURE_VALUES,\n type ViewerKnows,\n type ViewerProfile,\n type ViewerTemperature,\n} from \"../../viewer-profile.ts\";\nimport { parseDefinitionViewerFields } from \"./creative-definition.ts\";\nimport { resolveViewer } from \"./viewer-check.ts\";\n\n/**\n * Who a scaffolded static ad is for, decided before anything is billed: the viewer the\n * ad set it will run in reaches (`--target-adset`, read as `baker ads meta adsets\n * --audience` reads it). The viewer's placements are what the sizes, the brief and the\n * prefilter keep clear of; the viewer itself is recorded in `_definition.md`, where the\n * viewer check reads it before every render.\n */\n\n/** The scaffold flags that say who the ad is for, as citty hands them over. */\nexport type ScaffoldViewerFlags = {\n \"target-adset\"?: unknown;\n \"viewer-temperature\"?: unknown;\n \"viewer-knows\"?: unknown;\n \"viewer-placements\"?: unknown;\n};\n\nexport type ScaffoldViewer = {\n ok: true;\n targetAdSet?: string;\n viewer?: ViewerProfile;\n /** Write `targetAdSet` / `viewer` into `_definition.md`: they came from this scaffold's flags. */\n record: boolean;\n /** The placements the sizes are planned around; absent = Advantage+ (Reels included). */\n adPlacements?: string[];\n /** What could not be read, for the hints. */\n notes: string[];\n};\n\n/** The viewer one ad set reaches, or null when no connected account holds it. */\nexport type AdSetViewerLookup = (adSetId: string) => Promise<ViewerProfile | null>;\n\nfunction flagText(value: unknown): string | undefined {\n const text = typeof value === \"string\" || typeof value === \"number\" ? String(value).trim() : \"\";\n return text || undefined;\n}\n\n/** Placements that narrow what Advantage+ runs; `automatic` alone is Advantage+ itself. */\nfunction narrows(placements: readonly string[]): boolean {\n return placements.some((placement) => placement !== \"automatic\");\n}\n\nfunction flagList(value: unknown): string[] | undefined {\n const list = (flagText(value) ?? \"\")\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n return list.length > 0 ? list : undefined;\n}\n\n/** What the `--viewer-*` flags say, checked. */\ntype ViewerFields = { temperature?: ViewerTemperature; knows?: ViewerKnows[]; placements?: string[] };\n\nconst isTemperature = (value: string): value is ViewerTemperature =>\n (VIEWER_TEMPERATURE_VALUES as readonly string[]).includes(value);\nconst isKnows = (value: string): value is ViewerKnows => (VIEWER_KNOWS_VALUES as readonly string[]).includes(value);\n\nfunction viewerFields(flags: ScaffoldViewerFlags): { ok: true; fields: ViewerFields } | { ok: false; message: string } {\n const temperature = flagText(flags[\"viewer-temperature\"]);\n if (temperature !== undefined && !isTemperature(temperature)) {\n return { ok: false, message: `--viewer-temperature must be one of ${VIEWER_TEMPERATURE_VALUES.join(\", \")}` };\n }\n const knows = flagList(flags[\"viewer-knows\"]);\n if (knows && !knows.every(isKnows)) {\n return { ok: false, message: `--viewer-knows takes ${VIEWER_KNOWS_VALUES.join(\", \")}` };\n }\n const placements = flagList(flags[\"viewer-placements\"]);\n return {\n ok: true,\n fields: {\n ...(temperature ? { temperature } : {}),\n ...(knows ? { knows: knows.filter(isKnows) } : {}),\n ...(placements ? { placements } : {}),\n },\n };\n}\n\n/** The viewer the agent read from the Knowledge, with no ad set to read it from. */\nfunction givenViewer(fields: ViewerFields): ViewerProfile | undefined {\n if (!fields.temperature) return undefined;\n return {\n temperature: fields.temperature,\n knows: fields.knows ?? [],\n placements: fields.placements ?? [\"automatic\"],\n source: \"knowledge\",\n reasons: [\"No ad set was read: this viewer was given when the ad was scaffolded, from the Knowledge.\"],\n };\n}\n\nconst FIELD_WORDS: Record<keyof ViewerFields, string> = {\n temperature: \"Temperature\",\n knows: \"What they know\",\n placements: \"Placements\",\n};\n\n/** The viewer read off the ad set, with whatever the agent stated in its place. */\nfunction correctedViewer(read: ViewerProfile, fields: ViewerFields): ViewerProfile {\n const stated = (Object.keys(FIELD_WORDS) as (keyof ViewerFields)[]).filter((key) => fields[key] !== undefined);\n if (stated.length === 0) return read;\n return {\n ...read,\n ...fields,\n reasons: [...read.reasons, ...stated.map((key) => `${FIELD_WORDS[key]} given when the ad was scaffolded.`)],\n };\n}\n\nconst EVERY_PLACEMENT = \"so the sizes keep clear of every placement\";\n\nasync function readAdSet(\n adSetId: string,\n adSetViewer: AdSetViewerLookup,\n notes: string[],\n): Promise<ViewerProfile | undefined> {\n try {\n const viewer = await adSetViewer(adSetId);\n if (viewer) return viewer;\n notes.push(`Ad set ${adSetId} is not in any connected Meta ad account, ${EVERY_PLACEMENT}.`);\n } catch (e) {\n notes.push(`Could not read ad set ${adSetId} (${e instanceof Error ? e.message : String(e)}), ${EVERY_PLACEMENT}.`);\n }\n return undefined;\n}\n\n/**\n * The viewer a creative's `_definition.md` already names, resolved as the viewer check\n * resolves it (the recorded viewer, else its ad set's); nothing is written back.\n */\nasync function recordedViewer(definition: string, adSetViewer: AdSetViewerLookup): Promise<ScaffoldViewer> {\n const { targetAdSet, viewer: recorded } = parseDefinitionViewerFields(definition);\n if (!targetAdSet && recorded === undefined) return { ok: true, record: false, notes: [] };\n const resolved = await resolveViewer({ recorded, targetAdSet }, { adSetViewer, personas: async () => [] });\n const viewer = resolved.from === \"recorded\" || resolved.from === \"ad_set\" ? resolved.viewer : undefined;\n return outcome(targetAdSet, viewer, false, resolved.notes);\n}\n\nfunction outcome(\n targetAdSet: string | undefined,\n viewer: ViewerProfile | undefined,\n record: boolean,\n notes: string[],\n): ScaffoldViewer {\n return {\n ok: true,\n ...(targetAdSet ? { targetAdSet } : {}),\n ...(viewer ? { viewer } : {}),\n record,\n ...(viewer && narrows(viewer.placements) ? { adPlacements: viewer.placements } : {}),\n notes,\n };\n}\n\n/**\n * Who the ad is for, from the scaffold's flags: `--target-adset` read off the ad set (any\n * `--viewer-*` flag correcting that read), else the `--viewer-*` flags alone (a viewer from\n * the Knowledge); with neither, whatever the creative's `_definition.md` already records.\n * A bad flag is refused before anything is billed; an ad set that cannot be read is not.\n */\nexport async function scaffoldViewer(\n flags: ScaffoldViewerFlags,\n existingDefinition: string | null,\n adSetViewer: AdSetViewerLookup,\n): Promise<ScaffoldViewer | { ok: false; message: string }> {\n const targetAdSet = flagText(flags[\"target-adset\"]);\n const given = viewerFields(flags);\n if (!given.ok) return given;\n const { fields } = given;\n if (!targetAdSet && !fields.temperature && (fields.knows || fields.placements)) {\n return {\n ok: false,\n message: \"--viewer-knows and --viewer-placements describe a viewer: pass --viewer-temperature too\",\n };\n }\n if (!targetAdSet && !fields.temperature && existingDefinition !== null) {\n return await recordedViewer(existingDefinition, adSetViewer);\n }\n const notes: string[] = [];\n const read = targetAdSet ? await readAdSet(targetAdSet, adSetViewer, notes) : undefined;\n const viewer = read ? correctedViewer(read, fields) : givenViewer(fields);\n return outcome(targetAdSet, viewer, targetAdSet !== undefined || viewer !== undefined, notes);\n}\n"],"mappings":"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","names":[]}