@koda-sl/baker-cli 0.481.0-dev.2d33e194f → 0.495.0-dev.c2b64ccca

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 (1417) hide show
  1. package/README.md +66 -131
  2. package/canvas/avocado-tutorial.json +3 -3
  3. package/canvas/hello-world-overlay.json +1 -1
  4. package/dist/account-5VPZ3XQ3.js +79 -0
  5. package/dist/account-SYUEXERA.js +49 -0
  6. package/dist/account-ZQH535IP.js +80 -0
  7. package/dist/account-reads-GUYNJEZA.js +218 -0
  8. package/dist/account-writes-DLCRTL6Y.js +66 -0
  9. package/dist/accounts-3TNEMLLC.js +115 -0
  10. package/dist/accounts-GBRGCLE3.js +611 -0
  11. package/dist/accounts-IPOCFAKK.js +114 -0
  12. package/dist/accounts-QBWYIJ7D.js +77 -0
  13. package/dist/accounts-TSM5EHRD.js +75 -0
  14. package/dist/actions-XZMLYLTK.js +55 -0
  15. package/dist/active-entities-MFWTN4VJ.js +103 -0
  16. package/dist/activecampaign-77V5X3FQ.js +935 -0
  17. package/dist/activecampaign-77V5X3FQ.js.map +1 -0
  18. package/dist/activities-ZXA53KY7.js +74 -0
  19. package/dist/ad-group-writes-2JBCTSJY.js +170 -0
  20. package/dist/ad-groups-6JQSO32O.js +68 -0
  21. package/dist/ad-writes-P34Z3ZTX.js +174 -0
  22. package/dist/add-F2BF2HAK.js +105 -0
  23. package/dist/add-JAXBNDLC.js +97 -0
  24. package/dist/add-KAMQP5RM.js +302 -0
  25. package/dist/add-Y6QCWI6Q.js +104 -0
  26. package/dist/add-accounts-M4R7ZVHN.js +91 -0
  27. package/dist/ads-6MIIAUFS.js +40 -0
  28. package/dist/ads-6MIIAUFS.js.map +1 -0
  29. package/dist/ads-KPUP4WG4.js +128 -0
  30. package/dist/ads-UVQN4BTT.js +113 -0
  31. package/dist/ads-VPSHNIWG.js +77 -0
  32. package/dist/adsets-JAQKFDLX.js +176 -0
  33. package/dist/advertisers-2ETFYRKA.js +120 -0
  34. package/dist/advertisers-6ONURKP2.js +125 -0
  35. package/dist/analytics-AVDCFCGF.js +433 -0
  36. package/dist/analytics-AVDCFCGF.js.map +1 -0
  37. package/dist/analytics-XZ56777C.js +1976 -0
  38. package/dist/animate-KZQCCL65.js +480 -0
  39. package/dist/assign-DDXB7A6W.js +77 -0
  40. package/dist/audience-size-5QQAABZQ.js +94 -0
  41. package/dist/audiences-I4KJ6DZ7.js +85 -0
  42. package/dist/audiences-IFBZCTME.js +78 -0
  43. package/dist/audiences-THTWZOMA.js +268 -0
  44. package/dist/audit-2KOD4YCF.js +151 -0
  45. package/dist/audit-MFTERSQZ.js +88 -0
  46. package/dist/autocomplete-JC46QNHQ.js +111 -0
  47. package/dist/avatars-ZXQRZJM2.js +38 -0
  48. package/dist/bid-pricing-46W7LSU5.js +99 -0
  49. package/dist/brand-XROP2WL2.js +22 -0
  50. package/dist/brief-B7C3SYDP.js +537 -0
  51. package/dist/brief-B7C3SYDP.js.map +1 -0
  52. package/dist/businesses-JWOIY3OR.js +53 -0
  53. package/dist/campaign-groups-5O3SR5X2.js +95 -0
  54. package/dist/campaign-writes-4D4256DP.js +170 -0
  55. package/dist/campaign-writes-5BEK2QBJ.js +165 -0
  56. package/dist/campaigns-6DX2MACO.js +76 -0
  57. package/dist/campaigns-EJT7IMDT.js +109 -0
  58. package/dist/campaigns-OKU4HN5Q.js +107 -0
  59. package/dist/campaigns-QVPLPTH3.js +110 -0
  60. package/dist/canvas-XCP2WPYO.js +46 -0
  61. package/dist/capabilities-W4XDJVDW.js +144 -0
  62. package/dist/cards-2QCVG77U.js +85 -0
  63. package/dist/catalog-LTH6SDYM.js +65 -0
  64. package/dist/catalog-LTH6SDYM.js.map +1 -0
  65. package/dist/catalogs-RVW4L6C6.js +128 -0
  66. package/dist/changes-XRPDY2K4.js +135 -0
  67. package/dist/chatgpt-RNGSOVHP.js +47 -0
  68. package/dist/chats-JKCUA26S.js +262 -0
  69. package/dist/check-NDPR32FH.js +102 -0
  70. package/dist/check-NDPR32FH.js.map +1 -0
  71. package/dist/chunk-2C36DHQH.js +96 -0
  72. package/dist/chunk-2LIAXB3W.js +1079 -0
  73. package/dist/chunk-2MT3AWDE.js +125 -0
  74. package/dist/chunk-2WRK44LZ.js +296 -0
  75. package/dist/chunk-34NQKHKY.js +1599 -0
  76. package/dist/chunk-34NQKHKY.js.map +1 -0
  77. package/dist/chunk-35WKYJQY.js +382 -0
  78. package/dist/chunk-35WKYJQY.js.map +1 -0
  79. package/dist/chunk-3AOMKBDC.js +3194 -0
  80. package/dist/chunk-3AOMKBDC.js.map +1 -0
  81. package/dist/chunk-3FDGC2UH.js +846 -0
  82. package/dist/chunk-3FFJYMKM.js +333 -0
  83. package/dist/chunk-3PL2QRF2.js +96 -0
  84. package/dist/chunk-3XDZMLX6.js +139 -0
  85. package/dist/chunk-4R7H4RV6.js +35 -0
  86. package/dist/chunk-4TBDEV7X.js +75 -0
  87. package/dist/chunk-4WYEGE4N.js +2123 -0
  88. package/dist/chunk-4WYEGE4N.js.map +1 -0
  89. package/dist/chunk-5IGQ6IQO.js +1837 -0
  90. package/dist/chunk-5IGQ6IQO.js.map +1 -0
  91. package/dist/chunk-5VD5GK6A.js +58 -0
  92. package/dist/chunk-5ZIGEHQQ.js +166 -0
  93. package/dist/chunk-66ZIZ7QP.js +26 -0
  94. package/dist/chunk-6DSLFOKJ.js +74 -0
  95. package/dist/chunk-6EV5MXLQ.js +66 -0
  96. package/dist/chunk-6EV5MXLQ.js.map +1 -0
  97. package/dist/chunk-6FHP2HIK.js +84 -0
  98. package/dist/chunk-6VI2DDGZ.js +106 -0
  99. package/dist/chunk-72QBQS63.js +111 -0
  100. package/dist/chunk-7DBOLSUL.js +51 -0
  101. package/dist/chunk-7FDDOOM4.js +19 -0
  102. package/dist/chunk-7XH4UQWS.js +302 -0
  103. package/dist/chunk-7XH4UQWS.js.map +1 -0
  104. package/dist/chunk-AN7CMLYY.js +25 -0
  105. package/dist/chunk-B2SAOVMU.js +148 -0
  106. package/dist/chunk-B2SAOVMU.js.map +1 -0
  107. package/dist/chunk-BC2BXRXY.js +2887 -0
  108. package/dist/chunk-BC2BXRXY.js.map +1 -0
  109. package/dist/chunk-BDMZRWQI.js +433 -0
  110. package/dist/chunk-BITS62MF.js +77 -0
  111. package/dist/chunk-BVMXTD4B.js +169 -0
  112. package/dist/chunk-BVMXTD4B.js.map +1 -0
  113. package/dist/chunk-CCFZB42B.js +76 -0
  114. package/dist/chunk-CKRSSMRT.js +191 -0
  115. package/dist/chunk-D53AAUG4.js +215 -0
  116. package/dist/chunk-DGUYFROS.js +76 -0
  117. package/dist/chunk-DS2ZRTAL.js +136 -0
  118. package/dist/chunk-EIJDXZW5.js +1743 -0
  119. package/dist/chunk-EJH2JOVG.js +98 -0
  120. package/dist/chunk-EJWFD6T3.js +188 -0
  121. package/dist/chunk-ELNN4SNW.js +52 -0
  122. package/dist/chunk-ELYP6BOS.js +156 -0
  123. package/dist/chunk-ELYP6BOS.js.map +1 -0
  124. package/dist/chunk-EUB327FC.js +57 -0
  125. package/dist/chunk-EWRGZZLS.js +159 -0
  126. package/dist/chunk-EWRGZZLS.js.map +1 -0
  127. package/dist/chunk-FBF5XDJX.js +106 -0
  128. package/dist/chunk-FP3GNJN2.js +98 -0
  129. package/dist/chunk-FZKUUI3F.js +455 -0
  130. package/dist/chunk-FZKUUI3F.js.map +1 -0
  131. package/dist/chunk-G2AZCDX4.js +216 -0
  132. package/dist/chunk-G2AZCDX4.js.map +1 -0
  133. package/dist/chunk-G373L4GL.js +45 -0
  134. package/dist/chunk-GFFV6YS7.js +903 -0
  135. package/dist/chunk-GFFV6YS7.js.map +1 -0
  136. package/dist/chunk-GN6WIZDD.js +392 -0
  137. package/dist/chunk-GN6WIZDD.js.map +1 -0
  138. package/dist/chunk-GTOHNTTI.js +274 -0
  139. package/dist/chunk-HDNZSVJJ.js +665 -0
  140. package/dist/chunk-HLAAGGDS.js +346 -0
  141. package/dist/chunk-HX7PM34O.js +197 -0
  142. package/dist/chunk-IGOOLUE2.js +821 -0
  143. package/dist/chunk-IGOOLUE2.js.map +1 -0
  144. package/dist/chunk-IKTKWXP6.js +172 -0
  145. package/dist/chunk-IOGKJZ4Z.js +74 -0
  146. package/dist/chunk-IVRXPYQ7.js +60 -0
  147. package/dist/chunk-JIJJCU6E.js +363 -0
  148. package/dist/chunk-JOCKYH6U.js +297 -0
  149. package/dist/chunk-KPYDTIAI.js +317 -0
  150. package/dist/chunk-KPYDTIAI.js.map +1 -0
  151. package/dist/chunk-L44WSWSZ.js +23 -0
  152. package/dist/chunk-LF3MFO2N.js +23992 -0
  153. package/dist/chunk-LF3MFO2N.js.map +1 -0
  154. package/dist/chunk-M525HJTA.js +275 -0
  155. package/dist/chunk-M6UNISGO.js +66 -0
  156. package/dist/chunk-M6UNISGO.js.map +1 -0
  157. package/dist/chunk-MBYTIGZE.js +111 -0
  158. package/dist/chunk-MBYTIGZE.js.map +1 -0
  159. package/dist/chunk-MMU6XHCH.js +97 -0
  160. package/dist/chunk-MNPBPU3G.js +225 -0
  161. package/dist/chunk-MNPBPU3G.js.map +1 -0
  162. package/dist/chunk-MRRSKRZZ.js +84 -0
  163. package/dist/chunk-MV2J73HP.js +6238 -0
  164. package/dist/chunk-MV2J73HP.js.map +1 -0
  165. package/dist/chunk-NEHQGYZS.js +5181 -0
  166. package/dist/chunk-NEHQGYZS.js.map +1 -0
  167. package/dist/chunk-NFHUJRDE.js +136 -0
  168. package/dist/chunk-NG2DMLU6.js +175 -0
  169. package/dist/chunk-NG2DMLU6.js.map +1 -0
  170. package/dist/chunk-NSHJ6OOK.js +67 -0
  171. package/dist/chunk-OCBAKPHH.js +76 -0
  172. package/dist/chunk-OFTU5XBQ.js +92 -0
  173. package/dist/chunk-OSWCEDFE.js +216 -0
  174. package/dist/chunk-OTNGMKI6.js +309 -0
  175. package/dist/chunk-PAJJXZ3Z.js +170 -0
  176. package/dist/chunk-PALFKSJV.js +237 -0
  177. package/dist/chunk-PALFKSJV.js.map +1 -0
  178. package/dist/chunk-PGSXR5UM.js +206 -0
  179. package/dist/chunk-PPMSWLQC.js +158 -0
  180. package/dist/chunk-PPMSWLQC.js.map +1 -0
  181. package/dist/chunk-QAOEVZYY.js +509 -0
  182. package/dist/chunk-QEW64BBR.js +777 -0
  183. package/dist/chunk-QEW64BBR.js.map +1 -0
  184. package/dist/chunk-QK5PEHFX.js +3088 -0
  185. package/dist/chunk-QK5PEHFX.js.map +1 -0
  186. package/dist/chunk-QVQKCVP7.js +85 -0
  187. package/dist/chunk-QVQKCVP7.js.map +1 -0
  188. package/dist/chunk-QW2Q4C2X.js +547 -0
  189. package/dist/chunk-QW2Q4C2X.js.map +1 -0
  190. package/dist/chunk-R5HMAIZM.js +169 -0
  191. package/dist/chunk-RPRLCIHB.js +103 -0
  192. package/dist/chunk-RV4GIY23.js +149 -0
  193. package/dist/chunk-RVXN2YTM.js +544 -0
  194. package/dist/chunk-RVXN2YTM.js.map +1 -0
  195. package/dist/chunk-RXUEYD2S.js +592 -0
  196. package/dist/chunk-S4ROJTDY.js +580 -0
  197. package/dist/chunk-S4ROJTDY.js.map +1 -0
  198. package/dist/chunk-SJOWZKNB.js +56 -0
  199. package/dist/chunk-SQQ4VBNI.js +304 -0
  200. package/dist/chunk-SQQ4VBNI.js.map +1 -0
  201. package/dist/chunk-SYED65QF.js +46 -0
  202. package/dist/chunk-SYED65QF.js.map +1 -0
  203. package/dist/chunk-T5HB6R4I.js +518 -0
  204. package/dist/chunk-T5HB6R4I.js.map +1 -0
  205. package/dist/chunk-TE3N5AGN.js +94 -0
  206. package/dist/chunk-TPQH4UXJ.js +174 -0
  207. package/dist/chunk-TQUNJFGF.js +225 -0
  208. package/dist/chunk-TYSN5VVJ.js +55 -0
  209. package/dist/chunk-UKMG6YWX.js +26 -0
  210. package/dist/chunk-WDAWTHQK.js +66 -0
  211. package/dist/chunk-WJNLQEOZ.js +131 -0
  212. package/dist/chunk-WJNLQEOZ.js.map +1 -0
  213. package/dist/chunk-WZDKROPU.js +6995 -0
  214. package/dist/chunk-WZDKROPU.js.map +1 -0
  215. package/dist/chunk-X3PCCHP2.js +74 -0
  216. package/dist/chunk-XQ4KKF5E.js +1448 -0
  217. package/dist/chunk-XQ4KKF5E.js.map +1 -0
  218. package/dist/chunk-XXU7XRMM.js +4434 -0
  219. package/dist/chunk-XXU7XRMM.js.map +1 -0
  220. package/dist/chunk-YJAQWTS6.js +455 -0
  221. package/dist/chunk-YNOJN5Z6.js +42 -0
  222. package/dist/chunk-YTV5VW7S.js +494 -0
  223. package/dist/chunk-ZO4GMUO3.js +172 -0
  224. package/dist/chunk-ZSRLDTDC.js +46 -0
  225. package/dist/claim-JBNO7M6J.js +197 -0
  226. package/dist/cli.js +39 -38
  227. package/dist/cli.js.map +1 -1
  228. package/dist/client-CPAPILIF.js +19 -0
  229. package/dist/code-GHOVETWX.js +76 -0
  230. package/dist/companies-47T2PCRZ.js +195 -0
  231. package/dist/complete-T3PKN4B7.js +81 -0
  232. package/dist/config-AFNXR5FA.js +88 -0
  233. package/dist/connect-T7R43GIH.js +42 -0
  234. package/dist/content-M3U2SCFJ.js +85 -0
  235. package/dist/conversation-BG7WEMXQ.js +93 -0
  236. package/dist/conversion-report-URDTPXQ7.js +125 -0
  237. package/dist/conversion-upload-command-BWF6MAOK.js +438 -0
  238. package/dist/conversion-writes-7MGIFJ3H.js +120 -0
  239. package/dist/conversions-QY6COYPP.js +76 -0
  240. package/dist/conversions-UTXG5YEO.js +135 -0
  241. package/dist/countries-MUZPFSYZ.js +82 -0
  242. package/dist/create-6QASIASQ.js +235 -0
  243. package/dist/create-FIHEJRAF.js +59 -0
  244. package/dist/create-JXZBMXUS.js +207 -0
  245. package/dist/create-LN23WUB5.js +126 -0
  246. package/dist/create-LN23WUB5.js.map +1 -0
  247. package/dist/creatives-6FG6VAB4.js +87 -0
  248. package/dist/creatives-NJ7SZ3S4.js +58 -0
  249. package/dist/creatives-NJ7SZ3S4.js.map +1 -0
  250. package/dist/creatives-UD6TFATS.js +182 -0
  251. package/dist/credentials-ECPD2A7X.js +287 -0
  252. package/dist/critique-LINKOKOE.js +220 -0
  253. package/dist/critique-LINKOKOE.js.map +1 -0
  254. package/dist/critique-WQ3EP6E5.js +1450 -0
  255. package/dist/critique-WQ3EP6E5.js.map +1 -0
  256. package/dist/crm-4EWUWZNZ.js +318 -0
  257. package/dist/crm-ASWKMKF3.js +224 -0
  258. package/dist/crop-MIVSA2YF.js +113 -0
  259. package/dist/currency-5LU3D62R.js +88 -0
  260. package/dist/custom-conversions-3QGJQRYS.js +125 -0
  261. package/dist/customers-EEBZXQ4J.js +56 -0
  262. package/dist/decide-ZODJU7DY.js +199 -0
  263. package/dist/delete-CTH3TGNM.js +71 -0
  264. package/dist/delete-D6GNTBYI.js +54 -0
  265. package/dist/delete-IWIDUJNS.js +69 -0
  266. package/dist/delete-TLVYBVRP.js +71 -0
  267. package/dist/delete-XPHIQY5Z.js +63 -0
  268. package/dist/delete-XZEPLAKT.js +66 -0
  269. package/dist/demographics-L3TJJAFF.js +138 -0
  270. package/dist/describe-5PM5OSYK.js +97 -0
  271. package/dist/describe-KQLRHGFX.js +97 -0
  272. package/dist/dimensions-6H24D7CM.js +101 -0
  273. package/dist/discard-74I3M2DF.js +66 -0
  274. package/dist/discover-6LGIHWVP.js +93 -0
  275. package/dist/discover-HBQO4RSF.js +172 -0
  276. package/dist/domain-E5DTPAZI.js +98 -0
  277. package/dist/download-ED52W77T.js +299 -0
  278. package/dist/draft-3XYF2L4K.js +306 -0
  279. package/dist/draft-5IEMZ4W3.js +103 -0
  280. package/dist/draft-6UVGEERY.js +281 -0
  281. package/dist/draft-FDBE36CP.js +233 -0
  282. package/dist/draft-PE4DN44Y.js +307 -0
  283. package/dist/draft-QBR22TG2.js +328 -0
  284. package/dist/draft-VZYSJVF5.js +217 -0
  285. package/dist/draft-YWTLO2ON.js +100 -0
  286. package/dist/engine/index.js +15 -14
  287. package/dist/env-GS2TUVCM.js +43 -0
  288. package/dist/experiment-XDJHSOBC.js +1397 -0
  289. package/dist/export-GV5CF5UM.js +168 -0
  290. package/dist/extract-4OXK7L7R.js +113 -0
  291. package/dist/facets-XPB2ELM2.js +114 -0
  292. package/dist/favorites-VKPXGTK6.js +178 -0
  293. package/dist/feed-B2YLZQNU.js +177 -0
  294. package/dist/feedback-HT2M63QW.js +66 -0
  295. package/dist/feedback-record-V7UU6CG3.js +246 -0
  296. package/dist/feedback-record-V7UU6CG3.js.map +1 -0
  297. package/dist/fetch-2O7EUCCZ.js +139 -0
  298. package/dist/fetch-GMNE6KOJ.js +176 -0
  299. package/dist/find-LZZNPFJH.js +134 -0
  300. package/dist/find-LZZNPFJH.js.map +1 -0
  301. package/dist/fit-S5ZGPNE3.js +56 -0
  302. package/dist/flows-5SXT5F22.js +247 -0
  303. package/dist/flows-5SXT5F22.js.map +1 -0
  304. package/dist/follow-UNIHLZKA.js +94 -0
  305. package/dist/follow-competitors-CQL2B55H.js +112 -0
  306. package/dist/following-GMXZIEDZ.js +113 -0
  307. package/dist/fonts-GPJQLNQL.js +35 -0
  308. package/dist/forecast-6AHSHRDJ.js +168 -0
  309. package/dist/forecast-V6BCMR4Y.js +104 -0
  310. package/dist/funding-WPREDQVA.js +85 -0
  311. package/dist/funnel-QDYX3UH5.js +394 -0
  312. package/dist/ga4-6AUOX2UW.js +55 -0
  313. package/dist/generate-LWSAKRIT.js +263 -0
  314. package/dist/generate-LWSAKRIT.js.map +1 -0
  315. package/dist/get-5OEDAT2Q.js +63 -0
  316. package/dist/get-7WEE5V7M.js +56 -0
  317. package/dist/get-DXGJSEZZ.js +63 -0
  318. package/dist/get-GQWOLALT.js +59 -0
  319. package/dist/get-W7F4FOAR.js +92 -0
  320. package/dist/get-WY6JXFP6.js +61 -0
  321. package/dist/get-X6DSFXN6.js +78 -0
  322. package/dist/gif-JXWSR2TC.js +94 -0
  323. package/dist/google-44QA3M4N.js +3190 -0
  324. package/dist/google-44QA3M4N.js.map +1 -0
  325. package/dist/google-6QFL6GV3.js +136 -0
  326. package/dist/google-ads-ZG3BDUEG.js +195 -0
  327. package/dist/group-7ADT2GWA.js +43 -0
  328. package/dist/group-VD3UIBKE.js +43 -0
  329. package/dist/gsc-WHMPFTYA.js +30 -0
  330. package/dist/history-33JKOKAV.js +133 -0
  331. package/dist/history-M3IKDCEO.js +67 -0
  332. package/dist/hubspot-B4IUIOYX.js +779 -0
  333. package/dist/icon-R2K27DKI.js +137 -0
  334. package/dist/ideas-HHTLPC6Y.js +39 -0
  335. package/dist/images-PV6TZQLL.js +90 -0
  336. package/dist/improve-TVFAR27Q.js +86 -0
  337. package/dist/ingest-25ZX36RL.js +23 -0
  338. package/dist/ingest-MFIT3L23.js +107 -0
  339. package/dist/insights-CE3GICP7.js +423 -0
  340. package/dist/insights-CE3GICP7.js.map +1 -0
  341. package/dist/insights-TGAQLNHO.js +122 -0
  342. package/dist/insights-TGAQLNHO.js.map +1 -0
  343. package/dist/inspect-2IASA5YV.js +189 -0
  344. package/dist/inspiration-O56SOIQP.js +53 -0
  345. package/dist/intent-JOW24MTF.js +95 -0
  346. package/dist/job-QUO6KULI.js +202 -0
  347. package/dist/job-create-JTOG5M3Q.js +109 -0
  348. package/dist/job-status-3B4X5IPG.js +74 -0
  349. package/dist/keep-5TVGX7EG.js +78 -0
  350. package/dist/keyword-gap-SHLXRIHO.js +130 -0
  351. package/dist/keyword-metrics-NYH6DXKJ.js +197 -0
  352. package/dist/keywords-for-site-JTK64W4I.js +128 -0
  353. package/dist/landing-4V7HMYCZ.js +28 -0
  354. package/dist/landings-LZZO4VVW.js +151 -0
  355. package/dist/languages-TINK472E.js +55 -0
  356. package/dist/languages-ZIGJHY2T.js +40 -0
  357. package/dist/launches-C5XXNF7C.js +19 -0
  358. package/dist/layerize-YVCA4JO7.js +190 -0
  359. package/dist/leads-2WTL7F24.js +90 -0
  360. package/dist/leads-QUO3YNHO.js +293 -0
  361. package/dist/learn-RL66WKHT.js +204 -0
  362. package/dist/learn-RL66WKHT.js.map +1 -0
  363. package/dist/library-EEHRR7WX.js +109 -0
  364. package/dist/library-EEHRR7WX.js.map +1 -0
  365. package/dist/lighthouse-QLFI7PSQ.js +75 -0
  366. package/dist/line-item-writes-SYSYGV3L.js +208 -0
  367. package/dist/line-items-H3M6T7P2.js +103 -0
  368. package/dist/link-EHSSSV6K.js +64 -0
  369. package/dist/linkedin-BGZIR43P.js +1008 -0
  370. package/dist/list-2IAOPMQC.js +76 -0
  371. package/dist/list-BGT2BEWD.js +57 -0
  372. package/dist/list-G2VYAKYF.js +151 -0
  373. package/dist/list-IFXQWBBE.js +65 -0
  374. package/dist/list-LK3CVZ3B.js +50 -0
  375. package/dist/list-X2TMGY7C.js +303 -0
  376. package/dist/list-X3SEZDKE.js +51 -0
  377. package/dist/list-ZFS7XBD7.js +63 -0
  378. package/dist/locations-JC2H5JMY.js +67 -0
  379. package/dist/locations-M4L7FUOB.js +40 -0
  380. package/dist/log-4EINR63D.js +92 -0
  381. package/dist/logo-7GDRTSLI.js +18 -0
  382. package/dist/lookup-SJXNPUXG.js +22 -0
  383. package/dist/make-4XCSN76S.js +1137 -0
  384. package/dist/make-4XCSN76S.js.map +1 -0
  385. package/dist/map-EFEKUPNJ.js +433 -0
  386. package/dist/map-EFEKUPNJ.js.map +1 -0
  387. package/dist/mcp-RQJAEJCJ.js +463 -0
  388. package/dist/media-FYGHJAEG.js +89 -0
  389. package/dist/media-YYYCKXMZ.js +15 -0
  390. package/dist/media-sources-ZLBIC4RM.js +32 -0
  391. package/dist/members-SXATNT7V.js +44 -0
  392. package/dist/meta-OGB3NB2W.js +69 -0
  393. package/dist/metrics-52ZARN6M.js +152 -0
  394. package/dist/models-LSEMO4XH.js +155 -0
  395. package/dist/normalize-3HI26S34.js +806 -0
  396. package/dist/normalize-SOFYZQSJ.js +141 -0
  397. package/dist/options-GKSCCFM7.js +122 -0
  398. package/dist/options-GKSCCFM7.js.map +1 -0
  399. package/dist/output-YJ4YBUU4.js +24 -0
  400. package/dist/page-ZGCK6MOQ.js +105 -0
  401. package/dist/pages-QMP3W2R3.js +66 -0
  402. package/dist/performance-BQSWGNFO.js +24 -0
  403. package/dist/pinterest-LCVUB2ZL.js +96 -0
  404. package/dist/pixels-UQGAPBLG.js +60 -0
  405. package/dist/pixels-YM2DX7EN.js +108 -0
  406. package/dist/plan-Z5TZRRCH.js +207 -0
  407. package/dist/preview-DZQUUQGD.js +169 -0
  408. package/dist/profile-SXX36XVS.js +159 -0
  409. package/dist/profile-SXX36XVS.js.map +1 -0
  410. package/dist/promoted-tweet-writes-VUNJIUA3.js +180 -0
  411. package/dist/promoted-tweets-55X4H7NG.js +119 -0
  412. package/dist/properties-CSW67FJG.js +101 -0
  413. package/dist/publish-U73OR33K.js +322 -0
  414. package/dist/query-UQQNESTT.js +236 -0
  415. package/dist/query-V4I3VCJ7.js +61 -0
  416. package/dist/query-XPCC7OUP.js +247 -0
  417. package/dist/questionnaire-UQNRYZK7.js +93 -0
  418. package/dist/read-RXAOLPG5.js +141 -0
  419. package/dist/reddit-C2B7NT7X.js +26 -0
  420. package/dist/refresh-JN4HKE4H.js +94 -0
  421. package/dist/release-HSAOBN5K.js +60 -0
  422. package/dist/relevant-pages-XBWFEHHE.js +99 -0
  423. package/dist/remove-G5UXVQ2W.js +50 -0
  424. package/dist/remove-Q4TL5ZFS.js +48 -0
  425. package/dist/reopen-D26VSRRL.js +63 -0
  426. package/dist/rerun-NWOGRYHU.js +89 -0
  427. package/dist/rerun-NWOGRYHU.js.map +1 -0
  428. package/dist/research-CRYX2CAR.js +262 -0
  429. package/dist/research-XRVD2GSG.js +62 -0
  430. package/dist/reset-2VVN4CY7.js +54 -0
  431. package/dist/reset-FWDPBHYO.js +54 -0
  432. package/dist/resolve-D4PCGIWO.js +121 -0
  433. package/dist/retry-AHZFHMTH.js +60 -0
  434. package/dist/retry-FV6ASVO5.js +62 -0
  435. package/dist/retry-failed-AENQRGML.js +45 -0
  436. package/dist/rules-DSLE6LVU.js +95 -0
  437. package/dist/rules-DSLE6LVU.js.map +1 -0
  438. package/dist/rules-add-NBOMKFAI.js +446 -0
  439. package/dist/rules-add-NBOMKFAI.js.map +1 -0
  440. package/dist/run-AGSHZQND.js +65 -0
  441. package/dist/run-IRJWKZ3K.js +64 -0
  442. package/dist/scaffold-ad-3BSA7BWT.js +1553 -0
  443. package/dist/scaffold-ad-3BSA7BWT.js.map +1 -0
  444. package/dist/scaffold-static-ad-Q26LLBWQ.js +976 -0
  445. package/dist/scaffold-static-ad-Q26LLBWQ.js.map +1 -0
  446. package/dist/scaffold-video-WE52C7YZ.js +877 -0
  447. package/dist/scaffold-video-WE52C7YZ.js.map +1 -0
  448. package/dist/schedule-DTKBBIEH.js +66 -0
  449. package/dist/schedule-ESHQ4II7.js +61 -0
  450. package/dist/scheduled-agents-AREYFW23.js +50 -0
  451. package/dist/scheduled-agents-AREYFW23.js.map +1 -0
  452. package/dist/schema-OBF4T2QN.js +63 -0
  453. package/dist/schema-SWG2WKYX.js +132 -0
  454. package/dist/scrape-AGGDUPPD.js +2241 -0
  455. package/dist/screenshot-6GGCQCIV.js +102 -0
  456. package/dist/search-FRSFGJLZ.js +92 -0
  457. package/dist/search-I64WW2QC.js +174 -0
  458. package/dist/search-L2KHX5DO.js +224 -0
  459. package/dist/search-MBDEQ6EE.js +26 -0
  460. package/dist/search-UCDN4TOT.js +91 -0
  461. package/dist/search-copy-HX3FYZGT.js +168 -0
  462. package/dist/seeds-VFEFE4AK.js +101 -0
  463. package/dist/select-4IQQNKLN.js +100 -0
  464. package/dist/select-4IQQNKLN.js.map +1 -0
  465. package/dist/sends-WDSOXBSS.js +309 -0
  466. package/dist/sequences-NMJ55HS5.js +146 -0
  467. package/dist/set-prompt-DE4TD2V7.js +192 -0
  468. package/dist/set-prompt-DE4TD2V7.js.map +1 -0
  469. package/dist/setup-links-NAAUQD7O.js +141 -0
  470. package/dist/shared-3GVQQQNL.js +31 -0
  471. package/dist/site-VTJU5CRE.js +177 -0
  472. package/dist/sitemaps-KS57YDGB.js +87 -0
  473. package/dist/sites-PUEEVW7H.js +101 -0
  474. package/dist/skills-N6NRI2LC.js +47 -0
  475. package/dist/sources-CMUU4FER.js +454 -0
  476. package/dist/sources-CMUU4FER.js.map +1 -0
  477. package/dist/sources-YUMNEFK5.js +30 -0
  478. package/dist/stale-E34H24C3.js +70 -0
  479. package/dist/stale-E34H24C3.js.map +1 -0
  480. package/dist/stats-SY6GRHKB.js +34 -0
  481. package/dist/status-PVW64CLX.js +62 -0
  482. package/dist/sticker-YS6W5TSZ.js +94 -0
  483. package/dist/stock-DWL5E6K4.js +179 -0
  484. package/dist/studio-SXBK2IJJ.js +49 -0
  485. package/dist/sync-FOLYGQDI.js +54 -0
  486. package/dist/sync-NORMNZEE.js +54 -0
  487. package/dist/sync-XHJDCWWP.js +265 -0
  488. package/dist/sync-XHJDCWWP.js.map +1 -0
  489. package/dist/sync-definition-DTWEW6ZC.js +61 -0
  490. package/dist/tag-manager-ARFZU46G.js +43 -0
  491. package/dist/tags-77JUO75W.js +20 -0
  492. package/dist/tags-DOJUSHJB.js +224 -0
  493. package/dist/tags-IESRRMGW.js +20 -0
  494. package/dist/tags-MRTUBMRD.js +20 -0
  495. package/dist/tags-SP6GW4R7.js +26 -0
  496. package/dist/targeting-constants-DVGRIRKD.js +118 -0
  497. package/dist/targeting-criteria-SOK3HUE2.js +113 -0
  498. package/dist/targets-V2HNKVUL.js +102 -0
  499. package/dist/testimonials-PA2FUV4H.js +32 -0
  500. package/dist/themes-IDB7YPHW.js +94 -0
  501. package/dist/top-companies-XDP4SZN7.js +108 -0
  502. package/dist/translate-V6XPDX4M.js +604 -0
  503. package/dist/translate-V6XPDX4M.js.map +1 -0
  504. package/dist/triggers-QDXCA5P4.js +141 -0
  505. package/dist/tweets-AISMWXM3.js +123 -0
  506. package/dist/unfollow-PHLMILG2.js +46 -0
  507. package/dist/unlink-GDZV5GVL.js +67 -0
  508. package/dist/unstage-WCC2TI5Z.js +95 -0
  509. package/dist/unstage-WDJXHSAV.js +84 -0
  510. package/dist/update-KBF35KYV.js +63 -0
  511. package/dist/update-MRG5MPHR.js +98 -0
  512. package/dist/update-XNNQGZBO.js +155 -0
  513. package/dist/update-ZGDQJPDL.js +142 -0
  514. package/dist/upload-ID35OCCY.js +126 -0
  515. package/dist/upload-R7YJORED.js +150 -0
  516. package/dist/upscale-XPGVJWW2.js +109 -0
  517. package/dist/url-options-6X7M2J7D.js +130 -0
  518. package/dist/url-params-WRLCJRJQ.js +98 -0
  519. package/dist/use-VCL3PRCB.js +95 -0
  520. package/dist/validate-VTAKQGD5.js +95 -0
  521. package/dist/validate-VTAKQGD5.js.map +1 -0
  522. package/dist/variant-2NADVT6Q.js +338 -0
  523. package/dist/videos-M6VK66NI.js +39 -0
  524. package/dist/view-Q5B57K42.js +121 -0
  525. package/dist/viewer-check-3T2EZOSH.js +79 -0
  526. package/dist/viewer-check-3T2EZOSH.js.map +1 -0
  527. package/dist/web-GOD7BF7X.js +140 -0
  528. package/dist/winners-PK6EMR6B.js +113 -0
  529. package/dist/winning-ads-F765TFLS.js +84 -0
  530. package/dist/write-commands-4KWMNH6A.js +1015 -0
  531. package/dist/write-commands-7ECRGXLK.js +737 -0
  532. package/dist/write-commands-GL2PHXSU.js +713 -0
  533. package/dist/write-commands-NMV65XXB.js +206 -0
  534. package/dist/x-MDENBZ3X.js +47 -0
  535. package/package.json +1 -1
  536. package/dist/account-67PZBEP6.js +0 -79
  537. package/dist/account-HZC6X2PH.js +0 -49
  538. package/dist/account-TQUV64XP.js +0 -80
  539. package/dist/account-reads-IRBTERTY.js +0 -218
  540. package/dist/account-writes-ALDSHAEV.js +0 -66
  541. package/dist/accounts-4P54UY6F.js +0 -115
  542. package/dist/accounts-6UNBLIUH.js +0 -75
  543. package/dist/accounts-GVTZN4VE.js +0 -114
  544. package/dist/accounts-NJ6TF6WH.js +0 -611
  545. package/dist/accounts-WTB737BM.js +0 -77
  546. package/dist/actions-D3BOWDEB.js +0 -55
  547. package/dist/active-entities-J2O76SRP.js +0 -103
  548. package/dist/activities-2J7FTZMN.js +0 -74
  549. package/dist/ad-group-writes-PQKDCMQF.js +0 -170
  550. package/dist/ad-groups-NGYEJMV4.js +0 -68
  551. package/dist/ad-writes-77FVHJTS.js +0 -174
  552. package/dist/add-2FHPLALV.js +0 -105
  553. package/dist/add-2XJYOGKW.js +0 -104
  554. package/dist/add-62JPGFD4.js +0 -302
  555. package/dist/add-JP76KCCY.js +0 -97
  556. package/dist/add-accounts-M5Y4DQFM.js +0 -91
  557. package/dist/ads-DB5NDHX5.js +0 -128
  558. package/dist/ads-J2F7BSHR.js +0 -45
  559. package/dist/ads-J2F7BSHR.js.map +0 -1
  560. package/dist/ads-LYNA2I7B.js +0 -77
  561. package/dist/ads-W2IPUWHQ.js +0 -113
  562. package/dist/adsets-PY5ZKGBO.js +0 -176
  563. package/dist/advertisers-BVAZ4XUX.js +0 -125
  564. package/dist/advertisers-NZPJQF7W.js +0 -120
  565. package/dist/analytics-K2QIWKBN.js +0 -444
  566. package/dist/analytics-K2QIWKBN.js.map +0 -1
  567. package/dist/analytics-NTPZKMYK.js +0 -1976
  568. package/dist/animate-EIV2OHXT.js +0 -480
  569. package/dist/assign-UNIA5YDC.js +0 -77
  570. package/dist/audience-size-4NN6ENP5.js +0 -94
  571. package/dist/audiences-45ZCY2VZ.js +0 -268
  572. package/dist/audiences-UUIYQFS6.js +0 -78
  573. package/dist/audiences-VYFX7OFS.js +0 -85
  574. package/dist/audit-NSVHNA5J.js +0 -151
  575. package/dist/audit-OWXTLQC4.js +0 -88
  576. package/dist/autocomplete-EHWAQYVB.js +0 -111
  577. package/dist/avatars-7WZPW7BV.js +0 -38
  578. package/dist/bid-pricing-F5NDMS6W.js +0 -99
  579. package/dist/brand-QAJ5VWFM.js +0 -22
  580. package/dist/brief-V2OB5OJW.js +0 -490
  581. package/dist/brief-V2OB5OJW.js.map +0 -1
  582. package/dist/businesses-ZH67AXR6.js +0 -53
  583. package/dist/campaign-groups-KFJKLNNU.js +0 -95
  584. package/dist/campaign-writes-BDGQ6XB3.js +0 -170
  585. package/dist/campaign-writes-FNMAGCKB.js +0 -165
  586. package/dist/campaigns-66OOGMDW.js +0 -110
  587. package/dist/campaigns-6HMHOXXY.js +0 -109
  588. package/dist/campaigns-HJTGX5WW.js +0 -76
  589. package/dist/campaigns-KT3J2O7S.js +0 -107
  590. package/dist/canvas-MQ6ZE5CZ.js +0 -46
  591. package/dist/capabilities-RVUMPP7C.js +0 -144
  592. package/dist/cards-I4BZIYUV.js +0 -85
  593. package/dist/catalog-NLL5VDRK.js +0 -64
  594. package/dist/catalog-NLL5VDRK.js.map +0 -1
  595. package/dist/catalogs-B2NKQO24.js +0 -128
  596. package/dist/changes-FX3TPYGT.js +0 -135
  597. package/dist/chatgpt-3NS2R442.js +0 -47
  598. package/dist/chats-72L7U4ML.js +0 -262
  599. package/dist/chunk-2KIF3U2C.js +0 -51
  600. package/dist/chunk-35ENAFJX.js +0 -46
  601. package/dist/chunk-3ZWHAIMU.js +0 -207
  602. package/dist/chunk-3ZWHAIMU.js.map +0 -1
  603. package/dist/chunk-4DM6OAQ6.js +0 -2065
  604. package/dist/chunk-4DM6OAQ6.js.map +0 -1
  605. package/dist/chunk-4TU2MWBJ.js +0 -275
  606. package/dist/chunk-57UECM77.js +0 -98
  607. package/dist/chunk-5HYSKH77.js +0 -315
  608. package/dist/chunk-5HYSKH77.js.map +0 -1
  609. package/dist/chunk-65BQAJMH.js +0 -136
  610. package/dist/chunk-6GFJFTG6.js +0 -75
  611. package/dist/chunk-6HPIWZHS.js +0 -76
  612. package/dist/chunk-73B3RQJC.js +0 -610
  613. package/dist/chunk-73B3RQJC.js.map +0 -1
  614. package/dist/chunk-7GYM4I5I.js +0 -206
  615. package/dist/chunk-7NTION5F.js +0 -26
  616. package/dist/chunk-7SX73SH2.js +0 -2902
  617. package/dist/chunk-7SX73SH2.js.map +0 -1
  618. package/dist/chunk-AFDPISNJ.js +0 -111
  619. package/dist/chunk-AIDXJHZ4.js +0 -433
  620. package/dist/chunk-AR7PU7BB.js +0 -76
  621. package/dist/chunk-AXVOBRQT.js +0 -1442
  622. package/dist/chunk-AXVOBRQT.js.map +0 -1
  623. package/dist/chunk-BBBZR7MF.js +0 -160
  624. package/dist/chunk-BBBZR7MF.js.map +0 -1
  625. package/dist/chunk-BVYPRGPG.js +0 -92
  626. package/dist/chunk-CAMX6OHF.js +0 -3182
  627. package/dist/chunk-CAMX6OHF.js.map +0 -1
  628. package/dist/chunk-CBCGVPHB.js +0 -125
  629. package/dist/chunk-CECS3DHZ.js +0 -333
  630. package/dist/chunk-CZYUZL6R.js +0 -3151
  631. package/dist/chunk-CZYUZL6R.js.map +0 -1
  632. package/dist/chunk-D5MF337I.js +0 -97
  633. package/dist/chunk-DDTILZXI.js +0 -103
  634. package/dist/chunk-DL2W4N7J.js +0 -52
  635. package/dist/chunk-DLUWHZCE.js +0 -225
  636. package/dist/chunk-EOGPNV25.js +0 -509
  637. package/dist/chunk-F3FSD6BO.js +0 -96
  638. package/dist/chunk-FDG2LLX7.js +0 -494
  639. package/dist/chunk-FE7T32YL.js +0 -163
  640. package/dist/chunk-FE7T32YL.js.map +0 -1
  641. package/dist/chunk-FIN4ZXGJ.js +0 -70
  642. package/dist/chunk-FIN4ZXGJ.js.map +0 -1
  643. package/dist/chunk-FO5EP6DA.js +0 -24008
  644. package/dist/chunk-FO5EP6DA.js.map +0 -1
  645. package/dist/chunk-FSGTITIW.js +0 -622
  646. package/dist/chunk-FSGTITIW.js.map +0 -1
  647. package/dist/chunk-FSPV2DUG.js +0 -310
  648. package/dist/chunk-FSPV2DUG.js.map +0 -1
  649. package/dist/chunk-FYAWA3FO.js +0 -1830
  650. package/dist/chunk-FYAWA3FO.js.map +0 -1
  651. package/dist/chunk-G3KO2HFZ.js +0 -35
  652. package/dist/chunk-GKXMIXEV.js +0 -166
  653. package/dist/chunk-GT4UFORX.js +0 -296
  654. package/dist/chunk-GZ6YS7TG.js +0 -106
  655. package/dist/chunk-H7JOVRHO.js +0 -106
  656. package/dist/chunk-IJDF7D4U.js +0 -139
  657. package/dist/chunk-INJHF7AF.js +0 -172
  658. package/dist/chunk-IZFGTGFR.js +0 -67
  659. package/dist/chunk-J2QOKD2F.js +0 -76
  660. package/dist/chunk-J5WZ7SAD.js +0 -346
  661. package/dist/chunk-JC6GNMA7.js +0 -297
  662. package/dist/chunk-KB5HBQEK.js +0 -25
  663. package/dist/chunk-KB5XJHQX.js +0 -216
  664. package/dist/chunk-KM4AAAHZ.js +0 -5187
  665. package/dist/chunk-KM4AAAHZ.js.map +0 -1
  666. package/dist/chunk-LFZTGIPJ.js +0 -384
  667. package/dist/chunk-LFZTGIPJ.js.map +0 -1
  668. package/dist/chunk-LHNL6JM2.js +0 -309
  669. package/dist/chunk-LMHVLUD4.js +0 -175
  670. package/dist/chunk-LMHVLUD4.js.map +0 -1
  671. package/dist/chunk-NAMEFVWL.js +0 -1743
  672. package/dist/chunk-NFSLRRIE.js +0 -77
  673. package/dist/chunk-NORAJYUI.js +0 -172
  674. package/dist/chunk-NQTSHC7Z.js +0 -304
  675. package/dist/chunk-NQTSHC7Z.js.map +0 -1
  676. package/dist/chunk-O3LHCQ7Q.js +0 -136
  677. package/dist/chunk-O4ABJ352.js +0 -4410
  678. package/dist/chunk-O4ABJ352.js.map +0 -1
  679. package/dist/chunk-O5XLQUDS.js +0 -846
  680. package/dist/chunk-OI7KSXZM.js +0 -6265
  681. package/dist/chunk-OI7KSXZM.js.map +0 -1
  682. package/dist/chunk-OO67YTPM.js +0 -35
  683. package/dist/chunk-OO67YTPM.js.map +0 -1
  684. package/dist/chunk-OXBPKBSK.js +0 -6667
  685. package/dist/chunk-OXBPKBSK.js.map +0 -1
  686. package/dist/chunk-P3XJUOWT.js +0 -474
  687. package/dist/chunk-P3XJUOWT.js.map +0 -1
  688. package/dist/chunk-P3ZJIIDS.js +0 -57
  689. package/dist/chunk-P5T7OZKV.js +0 -26
  690. package/dist/chunk-P66JWKAL.js +0 -533
  691. package/dist/chunk-P66JWKAL.js.map +0 -1
  692. package/dist/chunk-PA5MYVQY.js +0 -66
  693. package/dist/chunk-PELW56D7.js +0 -45
  694. package/dist/chunk-PGVF4SB7.js +0 -169
  695. package/dist/chunk-Q676JHJA.js +0 -58
  696. package/dist/chunk-QDOVBGKZ.js +0 -149
  697. package/dist/chunk-QHIUJXU7.js +0 -363
  698. package/dist/chunk-QJPPPQPD.js +0 -98
  699. package/dist/chunk-QNEX7FEC.js +0 -174
  700. package/dist/chunk-QTL4NJ5I.js +0 -65
  701. package/dist/chunk-QTL4NJ5I.js.map +0 -1
  702. package/dist/chunk-QWX3QJNM.js +0 -170
  703. package/dist/chunk-QXDEZKAR.js +0 -455
  704. package/dist/chunk-QXDEZKAR.js.map +0 -1
  705. package/dist/chunk-RG7OMI5W.js +0 -1621
  706. package/dist/chunk-RG7OMI5W.js.map +0 -1
  707. package/dist/chunk-RUHMLPH5.js +0 -455
  708. package/dist/chunk-SJLIV6PF.js +0 -19
  709. package/dist/chunk-SLCSLVGS.js +0 -274
  710. package/dist/chunk-THU74XR6.js +0 -96
  711. package/dist/chunk-TY7TEU3T.js +0 -1079
  712. package/dist/chunk-UXTZ5MBE.js +0 -56
  713. package/dist/chunk-V43RLLZE.js +0 -55
  714. package/dist/chunk-V7PMSMPF.js +0 -817
  715. package/dist/chunk-V7PMSMPF.js.map +0 -1
  716. package/dist/chunk-VFSLIDXA.js +0 -42
  717. package/dist/chunk-VHMNSO2B.js +0 -84
  718. package/dist/chunk-VKH6LSC2.js +0 -188
  719. package/dist/chunk-VTCBPYGE.js +0 -592
  720. package/dist/chunk-W6ASM6UP.js +0 -197
  721. package/dist/chunk-W7KB3LJE.js +0 -60
  722. package/dist/chunk-WESRVXFS.js +0 -133
  723. package/dist/chunk-WESRVXFS.js.map +0 -1
  724. package/dist/chunk-WILN7TD6.js +0 -84
  725. package/dist/chunk-YGK7ZHNI.js +0 -337
  726. package/dist/chunk-YGK7ZHNI.js.map +0 -1
  727. package/dist/chunk-YGL5LTEA.js +0 -665
  728. package/dist/chunk-YONSWWCF.js +0 -215
  729. package/dist/chunk-Z5KBRTB2.js +0 -191
  730. package/dist/chunk-Z62XZCBE.js +0 -541
  731. package/dist/chunk-Z62XZCBE.js.map +0 -1
  732. package/dist/chunk-ZBKBTTHD.js +0 -74
  733. package/dist/chunk-ZJR5E53N.js +0 -23
  734. package/dist/chunk-ZLU5QDLU.js +0 -486
  735. package/dist/chunk-ZLU5QDLU.js.map +0 -1
  736. package/dist/chunk-ZNNC6B75.js +0 -94
  737. package/dist/chunk-ZO2YLHRW.js +0 -74
  738. package/dist/chunk-ZPJE6D7W.js +0 -74
  739. package/dist/claim-DIITRUIN.js +0 -197
  740. package/dist/client-LY2JCQS2.js +0 -19
  741. package/dist/code-7CBNYUPU.js +0 -76
  742. package/dist/companies-HHSYHL74.js +0 -195
  743. package/dist/complete-M3TURPU5.js +0 -81
  744. package/dist/config-7COTFJWK.js +0 -88
  745. package/dist/connect-TQL5EVVE.js +0 -42
  746. package/dist/content-OXMONMHJ.js +0 -85
  747. package/dist/conversation-GE273GC4.js +0 -93
  748. package/dist/conversion-report-BH4HH2Q3.js +0 -125
  749. package/dist/conversion-upload-command-QWWYCTNV.js +0 -438
  750. package/dist/conversion-writes-HG2O6JI7.js +0 -120
  751. package/dist/conversions-DP6ZF57D.js +0 -135
  752. package/dist/conversions-XPSN7SOW.js +0 -76
  753. package/dist/countries-UXAUGSBR.js +0 -82
  754. package/dist/create-ENQWVWGB.js +0 -59
  755. package/dist/create-NVXJP477.js +0 -207
  756. package/dist/create-PITWGVAS.js +0 -130
  757. package/dist/create-PITWGVAS.js.map +0 -1
  758. package/dist/create-XLZYNFKN.js +0 -235
  759. package/dist/creatives-O3444SBT.js +0 -182
  760. package/dist/creatives-ONKKGW5T.js +0 -87
  761. package/dist/creatives-U6IKIQBT.js +0 -51
  762. package/dist/creatives-U6IKIQBT.js.map +0 -1
  763. package/dist/credentials-3UY7RVUL.js +0 -287
  764. package/dist/critique-3LMW73QJ.js +0 -1449
  765. package/dist/critique-3LMW73QJ.js.map +0 -1
  766. package/dist/critique-KB32Y4AW.js +0 -213
  767. package/dist/critique-KB32Y4AW.js.map +0 -1
  768. package/dist/crm-OX67TBWD.js +0 -224
  769. package/dist/crm-XXU5XZIQ.js +0 -318
  770. package/dist/crop-3ZV5BFCA.js +0 -113
  771. package/dist/currency-XP4EMU4M.js +0 -88
  772. package/dist/custom-conversions-TV23XQHW.js +0 -125
  773. package/dist/customers-EJ7FVCTC.js +0 -56
  774. package/dist/decide-EGQ3CRHI.js +0 -199
  775. package/dist/delete-55IHV25V.js +0 -63
  776. package/dist/delete-GXLPIGTC.js +0 -54
  777. package/dist/delete-MUPTLWHI.js +0 -71
  778. package/dist/delete-OW2N4WIA.js +0 -66
  779. package/dist/delete-R2SEUSLV.js +0 -69
  780. package/dist/delete-ZGBBWFAW.js +0 -71
  781. package/dist/demographics-K6DEWUE5.js +0 -138
  782. package/dist/describe-4O3VCV6L.js +0 -97
  783. package/dist/describe-FCTM53MM.js +0 -97
  784. package/dist/dimensions-AD4VS5AH.js +0 -101
  785. package/dist/discard-NZ7FQJEE.js +0 -66
  786. package/dist/discover-OISU5EJF.js +0 -172
  787. package/dist/discover-UQXQUPNO.js +0 -93
  788. package/dist/domain-DXBHNZLO.js +0 -98
  789. package/dist/download-IE6XLFHM.js +0 -299
  790. package/dist/draft-DB6KMBCH.js +0 -306
  791. package/dist/draft-ENA7S4Y7.js +0 -103
  792. package/dist/draft-HJBAG3SN.js +0 -307
  793. package/dist/draft-ICEVLF4E.js +0 -328
  794. package/dist/draft-MGKJ5RYI.js +0 -100
  795. package/dist/draft-OBFKQEPK.js +0 -281
  796. package/dist/draft-OOHU6NMA.js +0 -217
  797. package/dist/draft-S6VPKZYY.js +0 -233
  798. package/dist/env-YLOVBOAE.js +0 -43
  799. package/dist/experiment-PHFP6IBM.js +0 -1397
  800. package/dist/export-R2IMUCQO.js +0 -168
  801. package/dist/extract-OQP3TSQG.js +0 -113
  802. package/dist/facets-NUKR7I57.js +0 -114
  803. package/dist/favorites-NS73FDG5.js +0 -178
  804. package/dist/feed-SQPOB7SP.js +0 -177
  805. package/dist/feedback-SQAAAKOQ.js +0 -66
  806. package/dist/feedback-record-7W7WBHSQ.js +0 -175
  807. package/dist/feedback-record-7W7WBHSQ.js.map +0 -1
  808. package/dist/fetch-PQ3NOPIE.js +0 -139
  809. package/dist/fetch-PTSAVQW5.js +0 -176
  810. package/dist/find-Y22AANDE.js +0 -134
  811. package/dist/find-Y22AANDE.js.map +0 -1
  812. package/dist/fit-IITU6UWC.js +0 -45
  813. package/dist/flows-ZLF3QJ76.js +0 -247
  814. package/dist/flows-ZLF3QJ76.js.map +0 -1
  815. package/dist/follow-N3DQZV7M.js +0 -94
  816. package/dist/follow-competitors-BZPCBGIB.js +0 -112
  817. package/dist/following-KESWTEBR.js +0 -113
  818. package/dist/fonts-FG6XMK5A.js +0 -34
  819. package/dist/forecast-43XBSHJR.js +0 -168
  820. package/dist/forecast-MAFZQ4UF.js +0 -104
  821. package/dist/funding-HYSCYS62.js +0 -85
  822. package/dist/funnel-32CNV57L.js +0 -394
  823. package/dist/ga4-J2KTNCD4.js +0 -55
  824. package/dist/generate-G3I5BLLW.js +0 -262
  825. package/dist/generate-G3I5BLLW.js.map +0 -1
  826. package/dist/get-3P2EQLP3.js +0 -63
  827. package/dist/get-FJB3X44Z.js +0 -61
  828. package/dist/get-GDF5V3RN.js +0 -78
  829. package/dist/get-MBGO4VJD.js +0 -63
  830. package/dist/get-NOKSZPIV.js +0 -59
  831. package/dist/get-W7MKBMQ3.js +0 -56
  832. package/dist/get-WHC4SRYK.js +0 -92
  833. package/dist/gif-SFYVDLCU.js +0 -94
  834. package/dist/google-THWFDDHJ.js +0 -3191
  835. package/dist/google-THWFDDHJ.js.map +0 -1
  836. package/dist/google-XRAMIES5.js +0 -136
  837. package/dist/google-ads-N55SE2CB.js +0 -195
  838. package/dist/group-6RDJTWVN.js +0 -43
  839. package/dist/group-TZJCKHKS.js +0 -43
  840. package/dist/gsc-HD6UFRYM.js +0 -30
  841. package/dist/history-4RWCE7WW.js +0 -67
  842. package/dist/history-IROFQ6LA.js +0 -133
  843. package/dist/hubspot-75N4AI43.js +0 -779
  844. package/dist/icon-BMCDFGMN.js +0 -137
  845. package/dist/ideas-7UQMXLI5.js +0 -32
  846. package/dist/images-OFR3S2U4.js +0 -90
  847. package/dist/improve-P6F2HXYQ.js +0 -86
  848. package/dist/ingest-HGR7KSG4.js +0 -107
  849. package/dist/ingest-XU4WXE4U.js +0 -23
  850. package/dist/insights-7SI2TB72.js +0 -128
  851. package/dist/insights-7SI2TB72.js.map +0 -1
  852. package/dist/insights-P5VIDOZN.js +0 -432
  853. package/dist/insights-P5VIDOZN.js.map +0 -1
  854. package/dist/inspect-TL2VZBN7.js +0 -189
  855. package/dist/inspiration-7WTS3AVI.js +0 -53
  856. package/dist/intent-OCZE4O7R.js +0 -95
  857. package/dist/job-XMPQ27SJ.js +0 -202
  858. package/dist/job-create-HTDQHPEF.js +0 -109
  859. package/dist/job-status-L7WUTZ2C.js +0 -74
  860. package/dist/keep-42SLV4CR.js +0 -78
  861. package/dist/keyword-gap-323NE4AK.js +0 -130
  862. package/dist/keyword-metrics-V6DGO7U5.js +0 -197
  863. package/dist/keywords-for-site-ANSLXBHB.js +0 -128
  864. package/dist/landing-K25GUBLZ.js +0 -28
  865. package/dist/landings-5IYXHA5Z.js +0 -151
  866. package/dist/languages-SRFGW6Z3.js +0 -55
  867. package/dist/languages-US6JUATO.js +0 -40
  868. package/dist/launches-7U4TPNO3.js +0 -108
  869. package/dist/launches-7U4TPNO3.js.map +0 -1
  870. package/dist/layerize-U672AXED.js +0 -190
  871. package/dist/leads-MXUQISOC.js +0 -293
  872. package/dist/leads-UJVKPRXX.js +0 -90
  873. package/dist/learn-XTEU4K66.js +0 -203
  874. package/dist/learn-XTEU4K66.js.map +0 -1
  875. package/dist/library-RPFXNER4.js +0 -109
  876. package/dist/library-RPFXNER4.js.map +0 -1
  877. package/dist/lighthouse-SJ4QNLUS.js +0 -75
  878. package/dist/line-item-writes-NURRHJ7P.js +0 -208
  879. package/dist/line-items-PC6QWV5J.js +0 -103
  880. package/dist/link-ZSXKCLQD.js +0 -64
  881. package/dist/linkedin-EZJJBKY3.js +0 -1008
  882. package/dist/list-ECF3RA26.js +0 -303
  883. package/dist/list-GQ65MC2N.js +0 -65
  884. package/dist/list-K56J7FZW.js +0 -51
  885. package/dist/list-M4Z3WYPM.js +0 -76
  886. package/dist/list-N7ZLO2LE.js +0 -63
  887. package/dist/list-Q5ZZRPHO.js +0 -151
  888. package/dist/list-SXYVDDMU.js +0 -57
  889. package/dist/list-XFIOXFB2.js +0 -50
  890. package/dist/locations-LZUHWKZY.js +0 -40
  891. package/dist/locations-NG4YXIBT.js +0 -67
  892. package/dist/log-EEBJQ6X2.js +0 -92
  893. package/dist/logo-4JOWGJ5A.js +0 -18
  894. package/dist/lookup-7NBZ64TE.js +0 -22
  895. package/dist/make-3J5EK52U.js +0 -633
  896. package/dist/make-3J5EK52U.js.map +0 -1
  897. package/dist/map-JVVNH2RI.js +0 -404
  898. package/dist/map-JVVNH2RI.js.map +0 -1
  899. package/dist/mcp-DAKGMA2Z.js +0 -463
  900. package/dist/media-2SWDJDA6.js +0 -15
  901. package/dist/media-R64YP7T2.js +0 -89
  902. package/dist/media-sources-L2X3KRDU.js +0 -32
  903. package/dist/members-22BOEGQS.js +0 -44
  904. package/dist/meta-RBHF2RG3.js +0 -69
  905. package/dist/metrics-IXRQZVSM.js +0 -152
  906. package/dist/models-BDXZ56GI.js +0 -155
  907. package/dist/normalize-JAE3NVOX.js +0 -141
  908. package/dist/normalize-P5Q3F7FU.js +0 -806
  909. package/dist/options-YTIU3BDC.js +0 -121
  910. package/dist/options-YTIU3BDC.js.map +0 -1
  911. package/dist/output-ZL62UZHQ.js +0 -24
  912. package/dist/page-FZ7IZ5CV.js +0 -105
  913. package/dist/pages-SS7TZTHV.js +0 -66
  914. package/dist/performance-FOOV624A.js +0 -24
  915. package/dist/performance-VYEANYQ5.js +0 -4707
  916. package/dist/performance-VYEANYQ5.js.map +0 -1
  917. package/dist/pinterest-UETS7U7U.js +0 -96
  918. package/dist/pixels-LCTMLKGP.js +0 -108
  919. package/dist/pixels-MPZYWGFD.js +0 -60
  920. package/dist/plan-V74EVXTW.js +0 -207
  921. package/dist/preview-RU54Z4EM.js +0 -169
  922. package/dist/profile-NQOIBLJL.js +0 -150
  923. package/dist/profile-NQOIBLJL.js.map +0 -1
  924. package/dist/promoted-tweet-writes-FQRFU7PB.js +0 -180
  925. package/dist/promoted-tweets-L2L3V5SF.js +0 -119
  926. package/dist/properties-ZUDQH3LE.js +0 -101
  927. package/dist/publish-5XTAHTM4.js +0 -322
  928. package/dist/query-26T7XATH.js +0 -236
  929. package/dist/query-SHUOHXTO.js +0 -247
  930. package/dist/query-XISUHMBR.js +0 -62
  931. package/dist/questionnaire-UG55JLVW.js +0 -93
  932. package/dist/read-UYSQAXRV.js +0 -141
  933. package/dist/reddit-BCB4FT6J.js +0 -26
  934. package/dist/refresh-WY5NPNLX.js +0 -94
  935. package/dist/release-JBSPPNQE.js +0 -60
  936. package/dist/relevant-pages-OU5H56KY.js +0 -99
  937. package/dist/remove-2KACTXKP.js +0 -48
  938. package/dist/remove-M5P7M5JU.js +0 -50
  939. package/dist/reopen-J3VW5766.js +0 -63
  940. package/dist/rerun-5MRYJG6U.js +0 -84
  941. package/dist/rerun-5MRYJG6U.js.map +0 -1
  942. package/dist/research-4CAA5CA2.js +0 -262
  943. package/dist/research-7LBTJ2B2.js +0 -62
  944. package/dist/reset-DBWWGCXY.js +0 -54
  945. package/dist/reset-DJSSXC4Y.js +0 -54
  946. package/dist/resolve-BNRUYS56.js +0 -121
  947. package/dist/retry-TMRIFGVT.js +0 -62
  948. package/dist/retry-WRBGLB7O.js +0 -60
  949. package/dist/retry-failed-NWLH6WHK.js +0 -45
  950. package/dist/run-TA3DP6QG.js +0 -59
  951. package/dist/run-VVTLDRRW.js +0 -65
  952. package/dist/scaffold-ad-7DSV6XDB.js +0 -1548
  953. package/dist/scaffold-ad-7DSV6XDB.js.map +0 -1
  954. package/dist/scaffold-static-ad-BEZ37QWN.js +0 -971
  955. package/dist/scaffold-static-ad-BEZ37QWN.js.map +0 -1
  956. package/dist/scaffold-video-SPCMTYK3.js +0 -872
  957. package/dist/scaffold-video-SPCMTYK3.js.map +0 -1
  958. package/dist/schedule-BQQGYGT7.js +0 -61
  959. package/dist/schedule-S547HPUT.js +0 -66
  960. package/dist/scheduled-agents-4Q6WS333.js +0 -49
  961. package/dist/scheduled-agents-4Q6WS333.js.map +0 -1
  962. package/dist/schema-DDEWM5ZX.js +0 -132
  963. package/dist/schema-SQIP75WG.js +0 -63
  964. package/dist/scrape-TI6S2W7I.js +0 -2241
  965. package/dist/screenshot-XFAYY4WW.js +0 -102
  966. package/dist/search-4HIOKYFR.js +0 -26
  967. package/dist/search-D2IV7KFJ.js +0 -92
  968. package/dist/search-IZPDHRJ4.js +0 -224
  969. package/dist/search-KFDL2IGG.js +0 -91
  970. package/dist/search-VJBKRD2U.js +0 -174
  971. package/dist/search-copy-QZXCAGK4.js +0 -168
  972. package/dist/seeds-VUNZMHZH.js +0 -101
  973. package/dist/select-U5TPAXGY.js +0 -94
  974. package/dist/select-U5TPAXGY.js.map +0 -1
  975. package/dist/sends-7HWLIOUO.js +0 -309
  976. package/dist/sequences-5RAGZE7L.js +0 -146
  977. package/dist/set-prompt-MHU4WI3Q.js +0 -185
  978. package/dist/set-prompt-MHU4WI3Q.js.map +0 -1
  979. package/dist/setup-links-SPIGXQTY.js +0 -141
  980. package/dist/shared-3CGGKICB.js +0 -31
  981. package/dist/site-FW6K6L6M.js +0 -177
  982. package/dist/sitemaps-3WDYQHYR.js +0 -87
  983. package/dist/sites-G4DQ2Z2X.js +0 -101
  984. package/dist/skills-PA26HXEP.js +0 -47
  985. package/dist/sources-4NAC6HV6.js +0 -421
  986. package/dist/sources-4NAC6HV6.js.map +0 -1
  987. package/dist/sources-PMBWG4KO.js +0 -30
  988. package/dist/stale-5IMY6MWC.js +0 -63
  989. package/dist/stale-5IMY6MWC.js.map +0 -1
  990. package/dist/stats-Q35I5DRW.js +0 -34
  991. package/dist/status-62SUXUWW.js +0 -62
  992. package/dist/sticker-MC2IAIQB.js +0 -94
  993. package/dist/stock-B7LKMKUK.js +0 -179
  994. package/dist/studio-BB2EVW5I.js +0 -49
  995. package/dist/sync-CLZDZL5F.js +0 -54
  996. package/dist/sync-XCHBEBZG.js +0 -279
  997. package/dist/sync-XCHBEBZG.js.map +0 -1
  998. package/dist/sync-YTHNOAAM.js +0 -54
  999. package/dist/sync-definition-DYSVT7BJ.js +0 -52
  1000. package/dist/tag-manager-EAREWVYR.js +0 -43
  1001. package/dist/tags-53AG3FYG.js +0 -20
  1002. package/dist/tags-7TH7TVR6.js +0 -224
  1003. package/dist/tags-EB4RE3ML.js +0 -20
  1004. package/dist/tags-EUEWDTTN.js +0 -20
  1005. package/dist/tags-ICAWBCOR.js +0 -26
  1006. package/dist/targeting-constants-IFE72XKL.js +0 -118
  1007. package/dist/targeting-criteria-WZDGJKOI.js +0 -113
  1008. package/dist/targets-OKJ6A4YL.js +0 -102
  1009. package/dist/templates-IA5TDCCZ.js +0 -178
  1010. package/dist/templates-IA5TDCCZ.js.map +0 -1
  1011. package/dist/testimonials-THHR5JH5.js +0 -32
  1012. package/dist/themes-ZFRM6AVO.js +0 -94
  1013. package/dist/top-companies-63Q475IA.js +0 -108
  1014. package/dist/translate-XKUWIIOS.js +0 -598
  1015. package/dist/translate-XKUWIIOS.js.map +0 -1
  1016. package/dist/triggers-XLKO2QXQ.js +0 -141
  1017. package/dist/tweets-7EZRFG3Y.js +0 -123
  1018. package/dist/unfollow-WBBTVEHE.js +0 -46
  1019. package/dist/unlink-P7KU54YU.js +0 -67
  1020. package/dist/unstage-EG7AUE5X.js +0 -84
  1021. package/dist/unstage-GHXGDOT2.js +0 -95
  1022. package/dist/update-7EWDO32L.js +0 -98
  1023. package/dist/update-BVYGB4YL.js +0 -142
  1024. package/dist/update-KVMQOVQK.js +0 -63
  1025. package/dist/update-N74F5KXB.js +0 -155
  1026. package/dist/upload-6PYPHPCZ.js +0 -126
  1027. package/dist/upload-NA62W5RS.js +0 -150
  1028. package/dist/upscale-RXJUBHXN.js +0 -109
  1029. package/dist/url-options-EUDXLDBD.js +0 -130
  1030. package/dist/url-params-DBJSM2LP.js +0 -98
  1031. package/dist/use-MHPNS4V4.js +0 -95
  1032. package/dist/validate-TTAGVGM2.js +0 -88
  1033. package/dist/validate-TTAGVGM2.js.map +0 -1
  1034. package/dist/variant-ZFCWILPG.js +0 -338
  1035. package/dist/videos-DUG7JYUA.js +0 -39
  1036. package/dist/view-PYCGDGT2.js +0 -121
  1037. package/dist/viewer-check-HIMNCZGY.js +0 -68
  1038. package/dist/web-SDG42OBC.js +0 -140
  1039. package/dist/winners-AI4PVSOI.js +0 -113
  1040. package/dist/winning-ads-KGAYAIPG.js +0 -84
  1041. package/dist/write-commands-34L5LJIB.js +0 -713
  1042. package/dist/write-commands-5TJ3LQ5F.js +0 -737
  1043. package/dist/write-commands-OYMWOFEY.js +0 -1015
  1044. package/dist/write-commands-R3UCCBUI.js +0 -206
  1045. package/dist/x-VNRVGVLI.js +0 -47
  1046. /package/dist/{account-67PZBEP6.js.map → account-5VPZ3XQ3.js.map} +0 -0
  1047. /package/dist/{account-HZC6X2PH.js.map → account-SYUEXERA.js.map} +0 -0
  1048. /package/dist/{account-TQUV64XP.js.map → account-ZQH535IP.js.map} +0 -0
  1049. /package/dist/{account-reads-IRBTERTY.js.map → account-reads-GUYNJEZA.js.map} +0 -0
  1050. /package/dist/{account-writes-ALDSHAEV.js.map → account-writes-DLCRTL6Y.js.map} +0 -0
  1051. /package/dist/{accounts-4P54UY6F.js.map → accounts-3TNEMLLC.js.map} +0 -0
  1052. /package/dist/{accounts-NJ6TF6WH.js.map → accounts-GBRGCLE3.js.map} +0 -0
  1053. /package/dist/{accounts-GVTZN4VE.js.map → accounts-IPOCFAKK.js.map} +0 -0
  1054. /package/dist/{accounts-WTB737BM.js.map → accounts-QBWYIJ7D.js.map} +0 -0
  1055. /package/dist/{accounts-6UNBLIUH.js.map → accounts-TSM5EHRD.js.map} +0 -0
  1056. /package/dist/{actions-D3BOWDEB.js.map → actions-XZMLYLTK.js.map} +0 -0
  1057. /package/dist/{active-entities-J2O76SRP.js.map → active-entities-MFWTN4VJ.js.map} +0 -0
  1058. /package/dist/{activities-2J7FTZMN.js.map → activities-ZXA53KY7.js.map} +0 -0
  1059. /package/dist/{ad-group-writes-PQKDCMQF.js.map → ad-group-writes-2JBCTSJY.js.map} +0 -0
  1060. /package/dist/{ad-groups-NGYEJMV4.js.map → ad-groups-6JQSO32O.js.map} +0 -0
  1061. /package/dist/{ad-writes-77FVHJTS.js.map → ad-writes-P34Z3ZTX.js.map} +0 -0
  1062. /package/dist/{add-2FHPLALV.js.map → add-F2BF2HAK.js.map} +0 -0
  1063. /package/dist/{add-JP76KCCY.js.map → add-JAXBNDLC.js.map} +0 -0
  1064. /package/dist/{add-62JPGFD4.js.map → add-KAMQP5RM.js.map} +0 -0
  1065. /package/dist/{add-2XJYOGKW.js.map → add-Y6QCWI6Q.js.map} +0 -0
  1066. /package/dist/{add-accounts-M5Y4DQFM.js.map → add-accounts-M4R7ZVHN.js.map} +0 -0
  1067. /package/dist/{ads-DB5NDHX5.js.map → ads-KPUP4WG4.js.map} +0 -0
  1068. /package/dist/{ads-W2IPUWHQ.js.map → ads-UVQN4BTT.js.map} +0 -0
  1069. /package/dist/{ads-LYNA2I7B.js.map → ads-VPSHNIWG.js.map} +0 -0
  1070. /package/dist/{adsets-PY5ZKGBO.js.map → adsets-JAQKFDLX.js.map} +0 -0
  1071. /package/dist/{advertisers-NZPJQF7W.js.map → advertisers-2ETFYRKA.js.map} +0 -0
  1072. /package/dist/{advertisers-BVAZ4XUX.js.map → advertisers-6ONURKP2.js.map} +0 -0
  1073. /package/dist/{analytics-NTPZKMYK.js.map → analytics-XZ56777C.js.map} +0 -0
  1074. /package/dist/{animate-EIV2OHXT.js.map → animate-KZQCCL65.js.map} +0 -0
  1075. /package/dist/{assign-UNIA5YDC.js.map → assign-DDXB7A6W.js.map} +0 -0
  1076. /package/dist/{audience-size-4NN6ENP5.js.map → audience-size-5QQAABZQ.js.map} +0 -0
  1077. /package/dist/{audiences-VYFX7OFS.js.map → audiences-I4KJ6DZ7.js.map} +0 -0
  1078. /package/dist/{audiences-UUIYQFS6.js.map → audiences-IFBZCTME.js.map} +0 -0
  1079. /package/dist/{audiences-45ZCY2VZ.js.map → audiences-THTWZOMA.js.map} +0 -0
  1080. /package/dist/{audit-NSVHNA5J.js.map → audit-2KOD4YCF.js.map} +0 -0
  1081. /package/dist/{audit-OWXTLQC4.js.map → audit-MFTERSQZ.js.map} +0 -0
  1082. /package/dist/{autocomplete-EHWAQYVB.js.map → autocomplete-JC46QNHQ.js.map} +0 -0
  1083. /package/dist/{avatars-7WZPW7BV.js.map → avatars-ZXQRZJM2.js.map} +0 -0
  1084. /package/dist/{bid-pricing-F5NDMS6W.js.map → bid-pricing-46W7LSU5.js.map} +0 -0
  1085. /package/dist/{brand-QAJ5VWFM.js.map → brand-XROP2WL2.js.map} +0 -0
  1086. /package/dist/{businesses-ZH67AXR6.js.map → businesses-JWOIY3OR.js.map} +0 -0
  1087. /package/dist/{campaign-groups-KFJKLNNU.js.map → campaign-groups-5O3SR5X2.js.map} +0 -0
  1088. /package/dist/{campaign-writes-BDGQ6XB3.js.map → campaign-writes-4D4256DP.js.map} +0 -0
  1089. /package/dist/{campaign-writes-FNMAGCKB.js.map → campaign-writes-5BEK2QBJ.js.map} +0 -0
  1090. /package/dist/{campaigns-HJTGX5WW.js.map → campaigns-6DX2MACO.js.map} +0 -0
  1091. /package/dist/{campaigns-6HMHOXXY.js.map → campaigns-EJT7IMDT.js.map} +0 -0
  1092. /package/dist/{campaigns-KT3J2O7S.js.map → campaigns-OKU4HN5Q.js.map} +0 -0
  1093. /package/dist/{campaigns-66OOGMDW.js.map → campaigns-QVPLPTH3.js.map} +0 -0
  1094. /package/dist/{canvas-MQ6ZE5CZ.js.map → canvas-XCP2WPYO.js.map} +0 -0
  1095. /package/dist/{capabilities-RVUMPP7C.js.map → capabilities-W4XDJVDW.js.map} +0 -0
  1096. /package/dist/{cards-I4BZIYUV.js.map → cards-2QCVG77U.js.map} +0 -0
  1097. /package/dist/{catalogs-B2NKQO24.js.map → catalogs-RVW4L6C6.js.map} +0 -0
  1098. /package/dist/{changes-FX3TPYGT.js.map → changes-XRPDY2K4.js.map} +0 -0
  1099. /package/dist/{chatgpt-3NS2R442.js.map → chatgpt-RNGSOVHP.js.map} +0 -0
  1100. /package/dist/{chats-72L7U4ML.js.map → chats-JKCUA26S.js.map} +0 -0
  1101. /package/dist/{chunk-F3FSD6BO.js.map → chunk-2C36DHQH.js.map} +0 -0
  1102. /package/dist/{chunk-TY7TEU3T.js.map → chunk-2LIAXB3W.js.map} +0 -0
  1103. /package/dist/{chunk-CBCGVPHB.js.map → chunk-2MT3AWDE.js.map} +0 -0
  1104. /package/dist/{chunk-GT4UFORX.js.map → chunk-2WRK44LZ.js.map} +0 -0
  1105. /package/dist/{chunk-O5XLQUDS.js.map → chunk-3FDGC2UH.js.map} +0 -0
  1106. /package/dist/{chunk-CECS3DHZ.js.map → chunk-3FFJYMKM.js.map} +0 -0
  1107. /package/dist/{chunk-THU74XR6.js.map → chunk-3PL2QRF2.js.map} +0 -0
  1108. /package/dist/{chunk-IJDF7D4U.js.map → chunk-3XDZMLX6.js.map} +0 -0
  1109. /package/dist/{chunk-G3KO2HFZ.js.map → chunk-4R7H4RV6.js.map} +0 -0
  1110. /package/dist/{chunk-6GFJFTG6.js.map → chunk-4TBDEV7X.js.map} +0 -0
  1111. /package/dist/{chunk-Q676JHJA.js.map → chunk-5VD5GK6A.js.map} +0 -0
  1112. /package/dist/{chunk-GKXMIXEV.js.map → chunk-5ZIGEHQQ.js.map} +0 -0
  1113. /package/dist/{chunk-P5T7OZKV.js.map → chunk-66ZIZ7QP.js.map} +0 -0
  1114. /package/dist/{chunk-ZO2YLHRW.js.map → chunk-6DSLFOKJ.js.map} +0 -0
  1115. /package/dist/{chunk-VHMNSO2B.js.map → chunk-6FHP2HIK.js.map} +0 -0
  1116. /package/dist/{chunk-H7JOVRHO.js.map → chunk-6VI2DDGZ.js.map} +0 -0
  1117. /package/dist/{chunk-AFDPISNJ.js.map → chunk-72QBQS63.js.map} +0 -0
  1118. /package/dist/{chunk-2KIF3U2C.js.map → chunk-7DBOLSUL.js.map} +0 -0
  1119. /package/dist/{chunk-SJLIV6PF.js.map → chunk-7FDDOOM4.js.map} +0 -0
  1120. /package/dist/{chunk-KB5HBQEK.js.map → chunk-AN7CMLYY.js.map} +0 -0
  1121. /package/dist/{chunk-AIDXJHZ4.js.map → chunk-BDMZRWQI.js.map} +0 -0
  1122. /package/dist/{chunk-NFSLRRIE.js.map → chunk-BITS62MF.js.map} +0 -0
  1123. /package/dist/{chunk-AR7PU7BB.js.map → chunk-CCFZB42B.js.map} +0 -0
  1124. /package/dist/{chunk-Z5KBRTB2.js.map → chunk-CKRSSMRT.js.map} +0 -0
  1125. /package/dist/{chunk-YONSWWCF.js.map → chunk-D53AAUG4.js.map} +0 -0
  1126. /package/dist/{chunk-6HPIWZHS.js.map → chunk-DGUYFROS.js.map} +0 -0
  1127. /package/dist/{chunk-65BQAJMH.js.map → chunk-DS2ZRTAL.js.map} +0 -0
  1128. /package/dist/{chunk-NAMEFVWL.js.map → chunk-EIJDXZW5.js.map} +0 -0
  1129. /package/dist/{chunk-57UECM77.js.map → chunk-EJH2JOVG.js.map} +0 -0
  1130. /package/dist/{chunk-VKH6LSC2.js.map → chunk-EJWFD6T3.js.map} +0 -0
  1131. /package/dist/{chunk-DL2W4N7J.js.map → chunk-ELNN4SNW.js.map} +0 -0
  1132. /package/dist/{chunk-P3ZJIIDS.js.map → chunk-EUB327FC.js.map} +0 -0
  1133. /package/dist/{chunk-GZ6YS7TG.js.map → chunk-FBF5XDJX.js.map} +0 -0
  1134. /package/dist/{chunk-QJPPPQPD.js.map → chunk-FP3GNJN2.js.map} +0 -0
  1135. /package/dist/{chunk-PELW56D7.js.map → chunk-G373L4GL.js.map} +0 -0
  1136. /package/dist/{chunk-SLCSLVGS.js.map → chunk-GTOHNTTI.js.map} +0 -0
  1137. /package/dist/{chunk-YGL5LTEA.js.map → chunk-HDNZSVJJ.js.map} +0 -0
  1138. /package/dist/{chunk-J5WZ7SAD.js.map → chunk-HLAAGGDS.js.map} +0 -0
  1139. /package/dist/{chunk-W6ASM6UP.js.map → chunk-HX7PM34O.js.map} +0 -0
  1140. /package/dist/{chunk-INJHF7AF.js.map → chunk-IKTKWXP6.js.map} +0 -0
  1141. /package/dist/{chunk-ZBKBTTHD.js.map → chunk-IOGKJZ4Z.js.map} +0 -0
  1142. /package/dist/{chunk-W7KB3LJE.js.map → chunk-IVRXPYQ7.js.map} +0 -0
  1143. /package/dist/{chunk-QHIUJXU7.js.map → chunk-JIJJCU6E.js.map} +0 -0
  1144. /package/dist/{chunk-JC6GNMA7.js.map → chunk-JOCKYH6U.js.map} +0 -0
  1145. /package/dist/{chunk-ZJR5E53N.js.map → chunk-L44WSWSZ.js.map} +0 -0
  1146. /package/dist/{chunk-4TU2MWBJ.js.map → chunk-M525HJTA.js.map} +0 -0
  1147. /package/dist/{chunk-D5MF337I.js.map → chunk-MMU6XHCH.js.map} +0 -0
  1148. /package/dist/{chunk-WILN7TD6.js.map → chunk-MRRSKRZZ.js.map} +0 -0
  1149. /package/dist/{chunk-O3LHCQ7Q.js.map → chunk-NFHUJRDE.js.map} +0 -0
  1150. /package/dist/{chunk-IZFGTGFR.js.map → chunk-NSHJ6OOK.js.map} +0 -0
  1151. /package/dist/{chunk-J2QOKD2F.js.map → chunk-OCBAKPHH.js.map} +0 -0
  1152. /package/dist/{chunk-BVYPRGPG.js.map → chunk-OFTU5XBQ.js.map} +0 -0
  1153. /package/dist/{chunk-KB5XJHQX.js.map → chunk-OSWCEDFE.js.map} +0 -0
  1154. /package/dist/{chunk-LHNL6JM2.js.map → chunk-OTNGMKI6.js.map} +0 -0
  1155. /package/dist/{chunk-QWX3QJNM.js.map → chunk-PAJJXZ3Z.js.map} +0 -0
  1156. /package/dist/{chunk-7GYM4I5I.js.map → chunk-PGSXR5UM.js.map} +0 -0
  1157. /package/dist/{chunk-EOGPNV25.js.map → chunk-QAOEVZYY.js.map} +0 -0
  1158. /package/dist/{chunk-PGVF4SB7.js.map → chunk-R5HMAIZM.js.map} +0 -0
  1159. /package/dist/{chunk-DDTILZXI.js.map → chunk-RPRLCIHB.js.map} +0 -0
  1160. /package/dist/{chunk-QDOVBGKZ.js.map → chunk-RV4GIY23.js.map} +0 -0
  1161. /package/dist/{chunk-VTCBPYGE.js.map → chunk-RXUEYD2S.js.map} +0 -0
  1162. /package/dist/{chunk-UXTZ5MBE.js.map → chunk-SJOWZKNB.js.map} +0 -0
  1163. /package/dist/{chunk-ZNNC6B75.js.map → chunk-TE3N5AGN.js.map} +0 -0
  1164. /package/dist/{chunk-QNEX7FEC.js.map → chunk-TPQH4UXJ.js.map} +0 -0
  1165. /package/dist/{chunk-DLUWHZCE.js.map → chunk-TQUNJFGF.js.map} +0 -0
  1166. /package/dist/{chunk-V43RLLZE.js.map → chunk-TYSN5VVJ.js.map} +0 -0
  1167. /package/dist/{chunk-7NTION5F.js.map → chunk-UKMG6YWX.js.map} +0 -0
  1168. /package/dist/{chunk-PA5MYVQY.js.map → chunk-WDAWTHQK.js.map} +0 -0
  1169. /package/dist/{chunk-ZPJE6D7W.js.map → chunk-X3PCCHP2.js.map} +0 -0
  1170. /package/dist/{chunk-RUHMLPH5.js.map → chunk-YJAQWTS6.js.map} +0 -0
  1171. /package/dist/{chunk-VFSLIDXA.js.map → chunk-YNOJN5Z6.js.map} +0 -0
  1172. /package/dist/{chunk-FDG2LLX7.js.map → chunk-YTV5VW7S.js.map} +0 -0
  1173. /package/dist/{chunk-NORAJYUI.js.map → chunk-ZO4GMUO3.js.map} +0 -0
  1174. /package/dist/{chunk-35ENAFJX.js.map → chunk-ZSRLDTDC.js.map} +0 -0
  1175. /package/dist/{claim-DIITRUIN.js.map → claim-JBNO7M6J.js.map} +0 -0
  1176. /package/dist/{client-LY2JCQS2.js.map → client-CPAPILIF.js.map} +0 -0
  1177. /package/dist/{code-7CBNYUPU.js.map → code-GHOVETWX.js.map} +0 -0
  1178. /package/dist/{companies-HHSYHL74.js.map → companies-47T2PCRZ.js.map} +0 -0
  1179. /package/dist/{complete-M3TURPU5.js.map → complete-T3PKN4B7.js.map} +0 -0
  1180. /package/dist/{config-7COTFJWK.js.map → config-AFNXR5FA.js.map} +0 -0
  1181. /package/dist/{connect-TQL5EVVE.js.map → connect-T7R43GIH.js.map} +0 -0
  1182. /package/dist/{content-OXMONMHJ.js.map → content-M3U2SCFJ.js.map} +0 -0
  1183. /package/dist/{conversation-GE273GC4.js.map → conversation-BG7WEMXQ.js.map} +0 -0
  1184. /package/dist/{conversion-report-BH4HH2Q3.js.map → conversion-report-URDTPXQ7.js.map} +0 -0
  1185. /package/dist/{conversion-upload-command-QWWYCTNV.js.map → conversion-upload-command-BWF6MAOK.js.map} +0 -0
  1186. /package/dist/{conversion-writes-HG2O6JI7.js.map → conversion-writes-7MGIFJ3H.js.map} +0 -0
  1187. /package/dist/{conversions-XPSN7SOW.js.map → conversions-QY6COYPP.js.map} +0 -0
  1188. /package/dist/{conversions-DP6ZF57D.js.map → conversions-UTXG5YEO.js.map} +0 -0
  1189. /package/dist/{countries-UXAUGSBR.js.map → countries-MUZPFSYZ.js.map} +0 -0
  1190. /package/dist/{create-XLZYNFKN.js.map → create-6QASIASQ.js.map} +0 -0
  1191. /package/dist/{create-ENQWVWGB.js.map → create-FIHEJRAF.js.map} +0 -0
  1192. /package/dist/{create-NVXJP477.js.map → create-JXZBMXUS.js.map} +0 -0
  1193. /package/dist/{creatives-ONKKGW5T.js.map → creatives-6FG6VAB4.js.map} +0 -0
  1194. /package/dist/{creatives-O3444SBT.js.map → creatives-UD6TFATS.js.map} +0 -0
  1195. /package/dist/{credentials-3UY7RVUL.js.map → credentials-ECPD2A7X.js.map} +0 -0
  1196. /package/dist/{crm-XXU5XZIQ.js.map → crm-4EWUWZNZ.js.map} +0 -0
  1197. /package/dist/{crm-OX67TBWD.js.map → crm-ASWKMKF3.js.map} +0 -0
  1198. /package/dist/{crop-3ZV5BFCA.js.map → crop-MIVSA2YF.js.map} +0 -0
  1199. /package/dist/{currency-XP4EMU4M.js.map → currency-5LU3D62R.js.map} +0 -0
  1200. /package/dist/{custom-conversions-TV23XQHW.js.map → custom-conversions-3QGJQRYS.js.map} +0 -0
  1201. /package/dist/{customers-EJ7FVCTC.js.map → customers-EEBZXQ4J.js.map} +0 -0
  1202. /package/dist/{decide-EGQ3CRHI.js.map → decide-ZODJU7DY.js.map} +0 -0
  1203. /package/dist/{delete-MUPTLWHI.js.map → delete-CTH3TGNM.js.map} +0 -0
  1204. /package/dist/{delete-GXLPIGTC.js.map → delete-D6GNTBYI.js.map} +0 -0
  1205. /package/dist/{delete-R2SEUSLV.js.map → delete-IWIDUJNS.js.map} +0 -0
  1206. /package/dist/{delete-ZGBBWFAW.js.map → delete-TLVYBVRP.js.map} +0 -0
  1207. /package/dist/{delete-55IHV25V.js.map → delete-XPHIQY5Z.js.map} +0 -0
  1208. /package/dist/{delete-OW2N4WIA.js.map → delete-XZEPLAKT.js.map} +0 -0
  1209. /package/dist/{demographics-K6DEWUE5.js.map → demographics-L3TJJAFF.js.map} +0 -0
  1210. /package/dist/{describe-FCTM53MM.js.map → describe-5PM5OSYK.js.map} +0 -0
  1211. /package/dist/{describe-4O3VCV6L.js.map → describe-KQLRHGFX.js.map} +0 -0
  1212. /package/dist/{dimensions-AD4VS5AH.js.map → dimensions-6H24D7CM.js.map} +0 -0
  1213. /package/dist/{discard-NZ7FQJEE.js.map → discard-74I3M2DF.js.map} +0 -0
  1214. /package/dist/{discover-UQXQUPNO.js.map → discover-6LGIHWVP.js.map} +0 -0
  1215. /package/dist/{discover-OISU5EJF.js.map → discover-HBQO4RSF.js.map} +0 -0
  1216. /package/dist/{domain-DXBHNZLO.js.map → domain-E5DTPAZI.js.map} +0 -0
  1217. /package/dist/{download-IE6XLFHM.js.map → download-ED52W77T.js.map} +0 -0
  1218. /package/dist/{draft-DB6KMBCH.js.map → draft-3XYF2L4K.js.map} +0 -0
  1219. /package/dist/{draft-ENA7S4Y7.js.map → draft-5IEMZ4W3.js.map} +0 -0
  1220. /package/dist/{draft-OBFKQEPK.js.map → draft-6UVGEERY.js.map} +0 -0
  1221. /package/dist/{draft-S6VPKZYY.js.map → draft-FDBE36CP.js.map} +0 -0
  1222. /package/dist/{draft-HJBAG3SN.js.map → draft-PE4DN44Y.js.map} +0 -0
  1223. /package/dist/{draft-ICEVLF4E.js.map → draft-QBR22TG2.js.map} +0 -0
  1224. /package/dist/{draft-OOHU6NMA.js.map → draft-VZYSJVF5.js.map} +0 -0
  1225. /package/dist/{draft-MGKJ5RYI.js.map → draft-YWTLO2ON.js.map} +0 -0
  1226. /package/dist/{env-YLOVBOAE.js.map → env-GS2TUVCM.js.map} +0 -0
  1227. /package/dist/{experiment-PHFP6IBM.js.map → experiment-XDJHSOBC.js.map} +0 -0
  1228. /package/dist/{export-R2IMUCQO.js.map → export-GV5CF5UM.js.map} +0 -0
  1229. /package/dist/{extract-OQP3TSQG.js.map → extract-4OXK7L7R.js.map} +0 -0
  1230. /package/dist/{facets-NUKR7I57.js.map → facets-XPB2ELM2.js.map} +0 -0
  1231. /package/dist/{favorites-NS73FDG5.js.map → favorites-VKPXGTK6.js.map} +0 -0
  1232. /package/dist/{feed-SQPOB7SP.js.map → feed-B2YLZQNU.js.map} +0 -0
  1233. /package/dist/{feedback-SQAAAKOQ.js.map → feedback-HT2M63QW.js.map} +0 -0
  1234. /package/dist/{fetch-PQ3NOPIE.js.map → fetch-2O7EUCCZ.js.map} +0 -0
  1235. /package/dist/{fetch-PTSAVQW5.js.map → fetch-GMNE6KOJ.js.map} +0 -0
  1236. /package/dist/{fit-IITU6UWC.js.map → fit-S5ZGPNE3.js.map} +0 -0
  1237. /package/dist/{follow-N3DQZV7M.js.map → follow-UNIHLZKA.js.map} +0 -0
  1238. /package/dist/{follow-competitors-BZPCBGIB.js.map → follow-competitors-CQL2B55H.js.map} +0 -0
  1239. /package/dist/{following-KESWTEBR.js.map → following-GMXZIEDZ.js.map} +0 -0
  1240. /package/dist/{fonts-FG6XMK5A.js.map → fonts-GPJQLNQL.js.map} +0 -0
  1241. /package/dist/{forecast-43XBSHJR.js.map → forecast-6AHSHRDJ.js.map} +0 -0
  1242. /package/dist/{forecast-MAFZQ4UF.js.map → forecast-V6BCMR4Y.js.map} +0 -0
  1243. /package/dist/{funding-HYSCYS62.js.map → funding-WPREDQVA.js.map} +0 -0
  1244. /package/dist/{funnel-32CNV57L.js.map → funnel-QDYX3UH5.js.map} +0 -0
  1245. /package/dist/{ga4-J2KTNCD4.js.map → ga4-6AUOX2UW.js.map} +0 -0
  1246. /package/dist/{get-MBGO4VJD.js.map → get-5OEDAT2Q.js.map} +0 -0
  1247. /package/dist/{get-W7MKBMQ3.js.map → get-7WEE5V7M.js.map} +0 -0
  1248. /package/dist/{get-3P2EQLP3.js.map → get-DXGJSEZZ.js.map} +0 -0
  1249. /package/dist/{get-NOKSZPIV.js.map → get-GQWOLALT.js.map} +0 -0
  1250. /package/dist/{get-WHC4SRYK.js.map → get-W7F4FOAR.js.map} +0 -0
  1251. /package/dist/{get-FJB3X44Z.js.map → get-WY6JXFP6.js.map} +0 -0
  1252. /package/dist/{get-GDF5V3RN.js.map → get-X6DSFXN6.js.map} +0 -0
  1253. /package/dist/{gif-SFYVDLCU.js.map → gif-JXWSR2TC.js.map} +0 -0
  1254. /package/dist/{google-XRAMIES5.js.map → google-6QFL6GV3.js.map} +0 -0
  1255. /package/dist/{google-ads-N55SE2CB.js.map → google-ads-ZG3BDUEG.js.map} +0 -0
  1256. /package/dist/{group-TZJCKHKS.js.map → group-7ADT2GWA.js.map} +0 -0
  1257. /package/dist/{group-6RDJTWVN.js.map → group-VD3UIBKE.js.map} +0 -0
  1258. /package/dist/{gsc-HD6UFRYM.js.map → gsc-WHMPFTYA.js.map} +0 -0
  1259. /package/dist/{history-IROFQ6LA.js.map → history-33JKOKAV.js.map} +0 -0
  1260. /package/dist/{history-4RWCE7WW.js.map → history-M3IKDCEO.js.map} +0 -0
  1261. /package/dist/{hubspot-75N4AI43.js.map → hubspot-B4IUIOYX.js.map} +0 -0
  1262. /package/dist/{icon-BMCDFGMN.js.map → icon-R2K27DKI.js.map} +0 -0
  1263. /package/dist/{ideas-7UQMXLI5.js.map → ideas-HHTLPC6Y.js.map} +0 -0
  1264. /package/dist/{images-OFR3S2U4.js.map → images-PV6TZQLL.js.map} +0 -0
  1265. /package/dist/{improve-P6F2HXYQ.js.map → improve-TVFAR27Q.js.map} +0 -0
  1266. /package/dist/{ingest-XU4WXE4U.js.map → ingest-25ZX36RL.js.map} +0 -0
  1267. /package/dist/{ingest-HGR7KSG4.js.map → ingest-MFIT3L23.js.map} +0 -0
  1268. /package/dist/{inspect-TL2VZBN7.js.map → inspect-2IASA5YV.js.map} +0 -0
  1269. /package/dist/{inspiration-7WTS3AVI.js.map → inspiration-O56SOIQP.js.map} +0 -0
  1270. /package/dist/{intent-OCZE4O7R.js.map → intent-JOW24MTF.js.map} +0 -0
  1271. /package/dist/{job-XMPQ27SJ.js.map → job-QUO6KULI.js.map} +0 -0
  1272. /package/dist/{job-create-HTDQHPEF.js.map → job-create-JTOG5M3Q.js.map} +0 -0
  1273. /package/dist/{job-status-L7WUTZ2C.js.map → job-status-3B4X5IPG.js.map} +0 -0
  1274. /package/dist/{keep-42SLV4CR.js.map → keep-5TVGX7EG.js.map} +0 -0
  1275. /package/dist/{keyword-gap-323NE4AK.js.map → keyword-gap-SHLXRIHO.js.map} +0 -0
  1276. /package/dist/{keyword-metrics-V6DGO7U5.js.map → keyword-metrics-NYH6DXKJ.js.map} +0 -0
  1277. /package/dist/{keywords-for-site-ANSLXBHB.js.map → keywords-for-site-JTK64W4I.js.map} +0 -0
  1278. /package/dist/{landing-K25GUBLZ.js.map → landing-4V7HMYCZ.js.map} +0 -0
  1279. /package/dist/{landings-5IYXHA5Z.js.map → landings-LZZO4VVW.js.map} +0 -0
  1280. /package/dist/{languages-SRFGW6Z3.js.map → languages-TINK472E.js.map} +0 -0
  1281. /package/dist/{languages-US6JUATO.js.map → languages-ZIGJHY2T.js.map} +0 -0
  1282. /package/dist/{logo-4JOWGJ5A.js.map → launches-C5XXNF7C.js.map} +0 -0
  1283. /package/dist/{layerize-U672AXED.js.map → layerize-YVCA4JO7.js.map} +0 -0
  1284. /package/dist/{leads-UJVKPRXX.js.map → leads-2WTL7F24.js.map} +0 -0
  1285. /package/dist/{leads-MXUQISOC.js.map → leads-QUO3YNHO.js.map} +0 -0
  1286. /package/dist/{lighthouse-SJ4QNLUS.js.map → lighthouse-QLFI7PSQ.js.map} +0 -0
  1287. /package/dist/{line-item-writes-NURRHJ7P.js.map → line-item-writes-SYSYGV3L.js.map} +0 -0
  1288. /package/dist/{line-items-PC6QWV5J.js.map → line-items-H3M6T7P2.js.map} +0 -0
  1289. /package/dist/{link-ZSXKCLQD.js.map → link-EHSSSV6K.js.map} +0 -0
  1290. /package/dist/{linkedin-EZJJBKY3.js.map → linkedin-BGZIR43P.js.map} +0 -0
  1291. /package/dist/{list-M4Z3WYPM.js.map → list-2IAOPMQC.js.map} +0 -0
  1292. /package/dist/{list-SXYVDDMU.js.map → list-BGT2BEWD.js.map} +0 -0
  1293. /package/dist/{list-Q5ZZRPHO.js.map → list-G2VYAKYF.js.map} +0 -0
  1294. /package/dist/{list-GQ65MC2N.js.map → list-IFXQWBBE.js.map} +0 -0
  1295. /package/dist/{list-XFIOXFB2.js.map → list-LK3CVZ3B.js.map} +0 -0
  1296. /package/dist/{list-ECF3RA26.js.map → list-X2TMGY7C.js.map} +0 -0
  1297. /package/dist/{list-K56J7FZW.js.map → list-X3SEZDKE.js.map} +0 -0
  1298. /package/dist/{list-N7ZLO2LE.js.map → list-ZFS7XBD7.js.map} +0 -0
  1299. /package/dist/{locations-NG4YXIBT.js.map → locations-JC2H5JMY.js.map} +0 -0
  1300. /package/dist/{locations-LZUHWKZY.js.map → locations-M4L7FUOB.js.map} +0 -0
  1301. /package/dist/{log-EEBJQ6X2.js.map → log-4EINR63D.js.map} +0 -0
  1302. /package/dist/{lookup-7NBZ64TE.js.map → logo-7GDRTSLI.js.map} +0 -0
  1303. /package/dist/{output-ZL62UZHQ.js.map → lookup-SJXNPUXG.js.map} +0 -0
  1304. /package/dist/{mcp-DAKGMA2Z.js.map → mcp-RQJAEJCJ.js.map} +0 -0
  1305. /package/dist/{media-R64YP7T2.js.map → media-FYGHJAEG.js.map} +0 -0
  1306. /package/dist/{media-2SWDJDA6.js.map → media-YYYCKXMZ.js.map} +0 -0
  1307. /package/dist/{media-sources-L2X3KRDU.js.map → media-sources-ZLBIC4RM.js.map} +0 -0
  1308. /package/dist/{members-22BOEGQS.js.map → members-SXATNT7V.js.map} +0 -0
  1309. /package/dist/{meta-RBHF2RG3.js.map → meta-OGB3NB2W.js.map} +0 -0
  1310. /package/dist/{metrics-IXRQZVSM.js.map → metrics-52ZARN6M.js.map} +0 -0
  1311. /package/dist/{models-BDXZ56GI.js.map → models-LSEMO4XH.js.map} +0 -0
  1312. /package/dist/{normalize-P5Q3F7FU.js.map → normalize-3HI26S34.js.map} +0 -0
  1313. /package/dist/{normalize-JAE3NVOX.js.map → normalize-SOFYZQSJ.js.map} +0 -0
  1314. /package/dist/{performance-FOOV624A.js.map → output-YJ4YBUU4.js.map} +0 -0
  1315. /package/dist/{page-FZ7IZ5CV.js.map → page-ZGCK6MOQ.js.map} +0 -0
  1316. /package/dist/{pages-SS7TZTHV.js.map → pages-QMP3W2R3.js.map} +0 -0
  1317. /package/dist/{query-XISUHMBR.js.map → performance-BQSWGNFO.js.map} +0 -0
  1318. /package/dist/{pinterest-UETS7U7U.js.map → pinterest-LCVUB2ZL.js.map} +0 -0
  1319. /package/dist/{pixels-MPZYWGFD.js.map → pixels-UQGAPBLG.js.map} +0 -0
  1320. /package/dist/{pixels-LCTMLKGP.js.map → pixels-YM2DX7EN.js.map} +0 -0
  1321. /package/dist/{plan-V74EVXTW.js.map → plan-Z5TZRRCH.js.map} +0 -0
  1322. /package/dist/{preview-RU54Z4EM.js.map → preview-DZQUUQGD.js.map} +0 -0
  1323. /package/dist/{promoted-tweet-writes-FQRFU7PB.js.map → promoted-tweet-writes-VUNJIUA3.js.map} +0 -0
  1324. /package/dist/{promoted-tweets-L2L3V5SF.js.map → promoted-tweets-55X4H7NG.js.map} +0 -0
  1325. /package/dist/{properties-ZUDQH3LE.js.map → properties-CSW67FJG.js.map} +0 -0
  1326. /package/dist/{publish-5XTAHTM4.js.map → publish-U73OR33K.js.map} +0 -0
  1327. /package/dist/{query-26T7XATH.js.map → query-UQQNESTT.js.map} +0 -0
  1328. /package/dist/{reddit-BCB4FT6J.js.map → query-V4I3VCJ7.js.map} +0 -0
  1329. /package/dist/{query-SHUOHXTO.js.map → query-XPCC7OUP.js.map} +0 -0
  1330. /package/dist/{questionnaire-UG55JLVW.js.map → questionnaire-UQNRYZK7.js.map} +0 -0
  1331. /package/dist/{read-UYSQAXRV.js.map → read-RXAOLPG5.js.map} +0 -0
  1332. /package/dist/{run-TA3DP6QG.js.map → reddit-C2B7NT7X.js.map} +0 -0
  1333. /package/dist/{refresh-WY5NPNLX.js.map → refresh-JN4HKE4H.js.map} +0 -0
  1334. /package/dist/{release-JBSPPNQE.js.map → release-HSAOBN5K.js.map} +0 -0
  1335. /package/dist/{relevant-pages-OU5H56KY.js.map → relevant-pages-XBWFEHHE.js.map} +0 -0
  1336. /package/dist/{remove-M5P7M5JU.js.map → remove-G5UXVQ2W.js.map} +0 -0
  1337. /package/dist/{remove-2KACTXKP.js.map → remove-Q4TL5ZFS.js.map} +0 -0
  1338. /package/dist/{reopen-J3VW5766.js.map → reopen-D26VSRRL.js.map} +0 -0
  1339. /package/dist/{research-4CAA5CA2.js.map → research-CRYX2CAR.js.map} +0 -0
  1340. /package/dist/{research-7LBTJ2B2.js.map → research-XRVD2GSG.js.map} +0 -0
  1341. /package/dist/{reset-DJSSXC4Y.js.map → reset-2VVN4CY7.js.map} +0 -0
  1342. /package/dist/{reset-DBWWGCXY.js.map → reset-FWDPBHYO.js.map} +0 -0
  1343. /package/dist/{resolve-BNRUYS56.js.map → resolve-D4PCGIWO.js.map} +0 -0
  1344. /package/dist/{retry-WRBGLB7O.js.map → retry-AHZFHMTH.js.map} +0 -0
  1345. /package/dist/{retry-TMRIFGVT.js.map → retry-FV6ASVO5.js.map} +0 -0
  1346. /package/dist/{retry-failed-NWLH6WHK.js.map → retry-failed-AENQRGML.js.map} +0 -0
  1347. /package/dist/{run-VVTLDRRW.js.map → run-AGSHZQND.js.map} +0 -0
  1348. /package/dist/{search-4HIOKYFR.js.map → run-IRJWKZ3K.js.map} +0 -0
  1349. /package/dist/{schedule-S547HPUT.js.map → schedule-DTKBBIEH.js.map} +0 -0
  1350. /package/dist/{schedule-BQQGYGT7.js.map → schedule-ESHQ4II7.js.map} +0 -0
  1351. /package/dist/{schema-SQIP75WG.js.map → schema-OBF4T2QN.js.map} +0 -0
  1352. /package/dist/{schema-DDEWM5ZX.js.map → schema-SWG2WKYX.js.map} +0 -0
  1353. /package/dist/{scrape-TI6S2W7I.js.map → scrape-AGGDUPPD.js.map} +0 -0
  1354. /package/dist/{screenshot-XFAYY4WW.js.map → screenshot-6GGCQCIV.js.map} +0 -0
  1355. /package/dist/{search-D2IV7KFJ.js.map → search-FRSFGJLZ.js.map} +0 -0
  1356. /package/dist/{search-VJBKRD2U.js.map → search-I64WW2QC.js.map} +0 -0
  1357. /package/dist/{search-IZPDHRJ4.js.map → search-L2KHX5DO.js.map} +0 -0
  1358. /package/dist/{shared-3CGGKICB.js.map → search-MBDEQ6EE.js.map} +0 -0
  1359. /package/dist/{search-KFDL2IGG.js.map → search-UCDN4TOT.js.map} +0 -0
  1360. /package/dist/{search-copy-QZXCAGK4.js.map → search-copy-HX3FYZGT.js.map} +0 -0
  1361. /package/dist/{seeds-VUNZMHZH.js.map → seeds-VFEFE4AK.js.map} +0 -0
  1362. /package/dist/{sends-7HWLIOUO.js.map → sends-WDSOXBSS.js.map} +0 -0
  1363. /package/dist/{sequences-5RAGZE7L.js.map → sequences-NMJ55HS5.js.map} +0 -0
  1364. /package/dist/{setup-links-SPIGXQTY.js.map → setup-links-NAAUQD7O.js.map} +0 -0
  1365. /package/dist/{sync-definition-DYSVT7BJ.js.map → shared-3GVQQQNL.js.map} +0 -0
  1366. /package/dist/{site-FW6K6L6M.js.map → site-VTJU5CRE.js.map} +0 -0
  1367. /package/dist/{sitemaps-3WDYQHYR.js.map → sitemaps-KS57YDGB.js.map} +0 -0
  1368. /package/dist/{sites-G4DQ2Z2X.js.map → sites-PUEEVW7H.js.map} +0 -0
  1369. /package/dist/{skills-PA26HXEP.js.map → skills-N6NRI2LC.js.map} +0 -0
  1370. /package/dist/{sources-PMBWG4KO.js.map → sources-YUMNEFK5.js.map} +0 -0
  1371. /package/dist/{stats-Q35I5DRW.js.map → stats-SY6GRHKB.js.map} +0 -0
  1372. /package/dist/{status-62SUXUWW.js.map → status-PVW64CLX.js.map} +0 -0
  1373. /package/dist/{sticker-MC2IAIQB.js.map → sticker-YS6W5TSZ.js.map} +0 -0
  1374. /package/dist/{stock-B7LKMKUK.js.map → stock-DWL5E6K4.js.map} +0 -0
  1375. /package/dist/{studio-BB2EVW5I.js.map → studio-SXBK2IJJ.js.map} +0 -0
  1376. /package/dist/{sync-YTHNOAAM.js.map → sync-FOLYGQDI.js.map} +0 -0
  1377. /package/dist/{sync-CLZDZL5F.js.map → sync-NORMNZEE.js.map} +0 -0
  1378. /package/dist/{viewer-check-HIMNCZGY.js.map → sync-definition-DTWEW6ZC.js.map} +0 -0
  1379. /package/dist/{tag-manager-EAREWVYR.js.map → tag-manager-ARFZU46G.js.map} +0 -0
  1380. /package/dist/{tags-53AG3FYG.js.map → tags-77JUO75W.js.map} +0 -0
  1381. /package/dist/{tags-7TH7TVR6.js.map → tags-DOJUSHJB.js.map} +0 -0
  1382. /package/dist/{tags-EUEWDTTN.js.map → tags-IESRRMGW.js.map} +0 -0
  1383. /package/dist/{tags-EB4RE3ML.js.map → tags-MRTUBMRD.js.map} +0 -0
  1384. /package/dist/{tags-ICAWBCOR.js.map → tags-SP6GW4R7.js.map} +0 -0
  1385. /package/dist/{targeting-constants-IFE72XKL.js.map → targeting-constants-DVGRIRKD.js.map} +0 -0
  1386. /package/dist/{targeting-criteria-WZDGJKOI.js.map → targeting-criteria-SOK3HUE2.js.map} +0 -0
  1387. /package/dist/{targets-OKJ6A4YL.js.map → targets-V2HNKVUL.js.map} +0 -0
  1388. /package/dist/{testimonials-THHR5JH5.js.map → testimonials-PA2FUV4H.js.map} +0 -0
  1389. /package/dist/{themes-ZFRM6AVO.js.map → themes-IDB7YPHW.js.map} +0 -0
  1390. /package/dist/{top-companies-63Q475IA.js.map → top-companies-XDP4SZN7.js.map} +0 -0
  1391. /package/dist/{triggers-XLKO2QXQ.js.map → triggers-QDXCA5P4.js.map} +0 -0
  1392. /package/dist/{tweets-7EZRFG3Y.js.map → tweets-AISMWXM3.js.map} +0 -0
  1393. /package/dist/{unfollow-WBBTVEHE.js.map → unfollow-PHLMILG2.js.map} +0 -0
  1394. /package/dist/{unlink-P7KU54YU.js.map → unlink-GDZV5GVL.js.map} +0 -0
  1395. /package/dist/{unstage-GHXGDOT2.js.map → unstage-WCC2TI5Z.js.map} +0 -0
  1396. /package/dist/{unstage-EG7AUE5X.js.map → unstage-WDJXHSAV.js.map} +0 -0
  1397. /package/dist/{update-KVMQOVQK.js.map → update-KBF35KYV.js.map} +0 -0
  1398. /package/dist/{update-7EWDO32L.js.map → update-MRG5MPHR.js.map} +0 -0
  1399. /package/dist/{update-N74F5KXB.js.map → update-XNNQGZBO.js.map} +0 -0
  1400. /package/dist/{update-BVYGB4YL.js.map → update-ZGDQJPDL.js.map} +0 -0
  1401. /package/dist/{upload-6PYPHPCZ.js.map → upload-ID35OCCY.js.map} +0 -0
  1402. /package/dist/{upload-NA62W5RS.js.map → upload-R7YJORED.js.map} +0 -0
  1403. /package/dist/{upscale-RXJUBHXN.js.map → upscale-XPGVJWW2.js.map} +0 -0
  1404. /package/dist/{url-options-EUDXLDBD.js.map → url-options-6X7M2J7D.js.map} +0 -0
  1405. /package/dist/{url-params-DBJSM2LP.js.map → url-params-WRLCJRJQ.js.map} +0 -0
  1406. /package/dist/{use-MHPNS4V4.js.map → use-VCL3PRCB.js.map} +0 -0
  1407. /package/dist/{variant-ZFCWILPG.js.map → variant-2NADVT6Q.js.map} +0 -0
  1408. /package/dist/{videos-DUG7JYUA.js.map → videos-M6VK66NI.js.map} +0 -0
  1409. /package/dist/{view-PYCGDGT2.js.map → view-Q5B57K42.js.map} +0 -0
  1410. /package/dist/{web-SDG42OBC.js.map → web-GOD7BF7X.js.map} +0 -0
  1411. /package/dist/{winners-AI4PVSOI.js.map → winners-PK6EMR6B.js.map} +0 -0
  1412. /package/dist/{winning-ads-KGAYAIPG.js.map → winning-ads-F765TFLS.js.map} +0 -0
  1413. /package/dist/{write-commands-OYMWOFEY.js.map → write-commands-4KWMNH6A.js.map} +0 -0
  1414. /package/dist/{write-commands-5TJ3LQ5F.js.map → write-commands-7ECRGXLK.js.map} +0 -0
  1415. /package/dist/{write-commands-34L5LJIB.js.map → write-commands-GL2PHXSU.js.map} +0 -0
  1416. /package/dist/{write-commands-R3UCCBUI.js.map → write-commands-NMV65XXB.js.map} +0 -0
  1417. /package/dist/{x-VNRVGVLI.js.map → x-MDENBZ3X.js.map} +0 -0
@@ -1,4707 +0,0 @@
1
- import {
2
- ANALYTICS_BUSY_FIX,
3
- isAnalyticsBusy,
4
- retryOnceIfBusy
5
- } from "./chunk-7SX73SH2.js";
6
- import "./chunk-EDLZGTGN.js";
7
- import {
8
- failApi
9
- } from "./chunk-NORAJYUI.js";
10
- import {
11
- calendarDaySchema
12
- } from "./chunk-OI7KSXZM.js";
13
- import "./chunk-GXDLWBOY.js";
14
- import "./chunk-GIWHHCD6.js";
15
- import "./chunk-XQIOCIDO.js";
16
- import "./chunk-I2DNBEV6.js";
17
- import {
18
- ApiError,
19
- apiPost
20
- } from "./chunk-KB5XJHQX.js";
21
- import "./chunk-YONSWWCF.js";
22
- import {
23
- optionalChatId,
24
- requireChatId
25
- } from "./chunk-QXDEZKAR.js";
26
- import {
27
- registerSchema,
28
- writeJson
29
- } from "./chunk-4DM6OAQ6.js";
30
- import "./chunk-YQSBHWOI.js";
31
-
32
- // ../api/src/ads-performance/accounts.ts
33
- import { z as z3 } from "zod";
34
-
35
- // ../api/src/ads-performance/wire.ts
36
- import { z as z2 } from "zod";
37
-
38
- // ../api/src/ads-performance/currency.ts
39
- import { z } from "zod";
40
- var adReportCurrencySchema = z.string().regex(/^([A-Za-z]{3}|native)$/, "use a currency code like EUR, or native").transform((value) => value === "native" ? value : value.toUpperCase());
41
-
42
- // ../api/src/ads-performance/wire.ts
43
- var AD_PERFORMANCE_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
44
- var AD_ENTITY_LEVELS = ["campaign", "ad_group", "ad", "keyword"];
45
- var AD_FUNNEL_LEVELS = ["campaign", "ad_group", "ad"];
46
- var AD_ENTITY_SORTS = [
47
- "spend",
48
- "impressions",
49
- "clicks",
50
- "link_clicks",
51
- "results",
52
- "result_value",
53
- "cost_per_result",
54
- "ctr",
55
- "first_delivery",
56
- "last_delivery",
57
- "name"
58
- ];
59
- var AD_CHANGE_KIND_NAMES = [
60
- "created",
61
- "status",
62
- "budget",
63
- "bid",
64
- "schedule",
65
- "name",
66
- "creative",
67
- "targeting",
68
- "goal",
69
- "removed",
70
- "other"
71
- ];
72
- var AD_CHANGE_SOURCES = ["baker", "platform", "observed"];
73
- var AD_ACTOR_KINDS = ["person", "platform_automatic", "baker", "unknown"];
74
- var AD_BREAKDOWN_DIMENSIONS = ["placement", "country", "device", "audience_segment", "age", "gender"];
75
- var AD_SEARCH_TERM_SORTS = ["spend", "clicks", "conversions", "cost_per_conversion"];
76
- var AD_BRAND_SEGMENT_NAMES = ["brand", "competitor", "non_brand", "other", "unsorted"];
77
- var AD_STATUS_NAMES = [
78
- "active",
79
- "paused",
80
- "scheduled",
81
- "ended",
82
- "in_review",
83
- "rejected",
84
- "draft",
85
- "archived",
86
- "removed",
87
- "unknown"
88
- ];
89
- var AD_PERFORMANCE_MAX_DAYS = 1130;
90
- var idList = z2.array(
91
- z2.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
92
- ).max(200);
93
- var adPerformanceScopeFields = {
94
- days: z2.number().int().min(1).max(AD_PERFORMANCE_MAX_DAYS).optional(),
95
- start: calendarDaySchema.optional(),
96
- end: calendarDaySchema.optional(),
97
- platforms: z2.array(z2.enum(AD_PERFORMANCE_PLATFORMS)).optional(),
98
- adAccountIds: idList.optional(),
99
- campaignIds: idList.optional(),
100
- /** Show every amount in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
101
- currency: adReportCurrencySchema.optional()
102
- };
103
- function scopeRules(req, ctx) {
104
- if (req.days !== void 0 && req.start !== void 0) {
105
- ctx.addIssue({ code: "custom", path: ["days"], message: "pass days or start/end, not both" });
106
- }
107
- if (req.end !== void 0 && req.start === void 0) {
108
- ctx.addIssue({ code: "custom", path: ["end"], message: "end needs a start" });
109
- }
110
- if (req.start !== void 0 && req.end !== void 0 && req.end < req.start) {
111
- ctx.addIssue({ code: "custom", path: ["end"], message: "end is before start" });
112
- }
113
- }
114
- var adPerformanceOverviewRequestSchema = z2.object(adPerformanceScopeFields).superRefine(scopeRules);
115
- var adPerformanceEntitiesRequestSchema = z2.object({
116
- ...adPerformanceScopeFields,
117
- level: z2.enum(AD_ENTITY_LEVELS).default("campaign"),
118
- adGroupIds: idList.optional(),
119
- statuses: z2.array(z2.enum(AD_STATUS_NAMES)).optional(),
120
- search: z2.string().max(200).optional(),
121
- sort: z2.enum(AD_ENTITY_SORTS).default("spend"),
122
- order: z2.enum(["desc", "asc"]).default("desc"),
123
- limit: z2.number().int().min(1).max(500).default(25),
124
- offset: z2.number().int().min(0).default(0),
125
- includeIdle: z2.boolean().default(false),
126
- /** Compare each row's settled days with as many days before it; also lists what stopped delivering. */
127
- compare: z2.boolean().default(false),
128
- /** Only campaigns in these brand segments (Google Search: `brand`, `competitor`, `non_brand`; `other`, `unsorted`). */
129
- segments: z2.array(z2.enum(AD_BRAND_SEGMENT_NAMES)).optional()
130
- }).superRefine(scopeRules);
131
- var adPerformanceEntityRequestSchema = z2.object({
132
- ...adPerformanceScopeFields,
133
- platform: z2.enum(AD_PERFORMANCE_PLATFORMS),
134
- level: z2.enum(AD_ENTITY_LEVELS),
135
- entityId: z2.string().min(1),
136
- /** Narrows the lookup of an ad or keyword when known. */
137
- adGroupId: z2.string().min(1).optional(),
138
- /**
139
- * Read it over its first to last day of delivery (true) or the period
140
- * asked (false). Absent: its whole run when it is off or did not deliver
141
- * in the period.
142
- */
143
- wholeRun: z2.boolean().optional()
144
- }).superRefine(scopeRules);
145
- var adPerformanceChangesRequestSchema = z2.object({
146
- ...adPerformanceScopeFields,
147
- kinds: z2.array(z2.enum(AD_CHANGE_KIND_NAMES)).optional(),
148
- levels: z2.array(z2.enum(["account", ...AD_ENTITY_LEVELS])).optional(),
149
- entityIds: idList.optional(),
150
- sources: z2.array(z2.enum(AD_CHANGE_SOURCES)).optional(),
151
- actorKinds: z2.array(z2.enum(AD_ACTOR_KINDS)).optional(),
152
- limit: z2.number().int().min(1).max(500).default(50),
153
- offset: z2.number().int().min(0).default(0)
154
- }).superRefine(scopeRules);
155
- var adPerformanceImpactRequestSchema = z2.object({
156
- ...adPerformanceScopeFields,
157
- kinds: z2.array(z2.enum(AD_CHANGE_KIND_NAMES)).optional(),
158
- actorKinds: z2.array(z2.enum(AD_ACTOR_KINDS)).optional(),
159
- /** Days compared on each side of a change. */
160
- impactDays: z2.number().int().min(3).max(28).default(7),
161
- limit: z2.number().int().min(1).max(200).default(30),
162
- offset: z2.number().int().min(0).default(0)
163
- }).superRefine(scopeRules);
164
- var adPerformanceCreativesRequestSchema = z2.object({
165
- ...adPerformanceScopeFields,
166
- groupBy: z2.enum(["creative", "ad"]).default("creative"),
167
- /** The ads of these ad groups (Meta's ad sets) only. */
168
- adGroupIds: idList.optional(),
169
- statuses: z2.array(z2.enum(AD_STATUS_NAMES)).optional(),
170
- minSpend: z2.number().min(0).default(0),
171
- minImpressions: z2.number().int().min(0).default(1e3),
172
- limit: z2.number().int().min(1).max(200).default(25)
173
- }).superRefine(scopeRules);
174
- var adPerformanceBreakdownRequestSchema = z2.object({ ...adPerformanceScopeFields, dimension: z2.enum(AD_BREAKDOWN_DIMENSIONS) }).superRefine(scopeRules);
175
- var adPerformanceSearchTermsRequestSchema = z2.object({
176
- ...adPerformanceScopeFields,
177
- adGroupIds: idList.optional(),
178
- minClicks: z2.number().int().min(0).default(1),
179
- /** Only terms with no conversions: the negative-keyword candidates. */
180
- noResults: z2.boolean().default(false),
181
- search: z2.string().max(200).optional(),
182
- sort: z2.enum(AD_SEARCH_TERM_SORTS).default("spend"),
183
- order: z2.enum(["desc", "asc"]).default("desc"),
184
- limit: z2.number().int().min(1).max(500).default(50),
185
- offset: z2.number().int().min(0).default(0),
186
- /** Only terms read as these: `brand`, `competitor`, `non_brand`, or `unsorted` (not read yet). */
187
- termSegments: z2.array(z2.enum(["brand", "competitor", "non_brand", "unsorted"])).optional(),
188
- /** Only terms bought by campaigns in these segments. */
189
- campaignSegments: z2.array(z2.enum(AD_BRAND_SEGMENT_NAMES)).optional(),
190
- /** Only terms Baker was unsure how to sort: a person's choice settles them. */
191
- unsureOnly: z2.boolean().default(false)
192
- }).superRefine(scopeRules);
193
- var adPerformancePacingRequestSchema = z2.object({
194
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
195
- currency: adReportCurrencySchema.optional(),
196
- /** `YYYY-MM`; the current month when absent. */
197
- month: z2.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/, "use YYYY-MM").optional(),
198
- platforms: z2.array(z2.enum(AD_PERFORMANCE_PLATFORMS)).optional(),
199
- adAccountIds: idList.optional()
200
- });
201
- var adPerformanceFunnelRequestSchema = z2.object({
202
- ...adPerformanceScopeFields,
203
- level: z2.enum(AD_FUNNEL_LEVELS).default("campaign"),
204
- /** Split the visit side by the page each visit entered on. */
205
- byLanding: z2.boolean().default(false),
206
- limit: z2.number().int().min(1).max(2e3).default(50)
207
- }).superRefine(scopeRules);
208
- var adPerformanceTrackingRequestSchema = z2.object(adPerformanceScopeFields).superRefine(scopeRules);
209
- var adPerformanceLandingsRequestSchema = z2.object(adPerformanceScopeFields).superRefine(scopeRules);
210
- var adPerformanceGoalsRequestSchema = z2.object({
211
- /** How far back to look for the conversion actions each platform reported. */
212
- days: z2.number().int().min(7).max(400).default(90)
213
- });
214
- var AD_PROGRESS_STATUSES = [
215
- "worsening",
216
- "mixed",
217
- "not_comparable",
218
- "steady",
219
- "improving",
220
- "too_early",
221
- "too_little_data",
222
- "stopped"
223
- ];
224
- var adPerformanceProgressRequestSchema = z2.object({
225
- ...adPerformanceScopeFields,
226
- level: z2.enum(AD_FUNNEL_LEVELS).default("campaign"),
227
- statuses: z2.array(z2.enum(AD_PROGRESS_STATUSES)).optional(),
228
- limit: z2.number().int().min(1).max(200).default(25)
229
- }).superRefine(scopeRules);
230
- var adPerformanceDiagnosisRequestSchema = z2.object({
231
- ...adPerformanceScopeFields,
232
- platform: z2.enum(AD_PERFORMANCE_PLATFORMS),
233
- level: z2.enum(AD_FUNNEL_LEVELS),
234
- entityId: z2.string().min(1)
235
- }).superRefine(scopeRules);
236
- var adPerformancePulseRequestSchema = z2.object({
237
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
238
- currency: adReportCurrencySchema.optional(),
239
- platforms: z2.array(z2.enum(AD_PERFORMANCE_PLATFORMS)).optional(),
240
- adAccountIds: idList.optional()
241
- });
242
- var AD_PERFORMANCE_NOT_CONNECTED_MESSAGE = "No ad platform is connected with an ad account selected, so there are no ad results to read yet.";
243
-
244
- // ../api/src/ads-performance/accounts.ts
245
- var adPerformanceAccountsRequestSchema = z3.object(adPerformanceScopeFields).superRefine(scopeRules);
246
-
247
- // ../api/src/ads-performance/attention.ts
248
- import { z as z4 } from "zod";
249
- var ATTENTION_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
250
- var AD_ATTENTION_TIERS = ["fix_now", "worth_a_look", "opportunity"];
251
- var idList2 = z4.array(
252
- z4.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
253
- ).max(200);
254
- var adPerformanceAttentionRequestSchema = z4.object({
255
- platforms: z4.array(z4.enum(ATTENTION_PLATFORMS)).optional(),
256
- adAccountIds: idList2.optional(),
257
- /** ISO code to show money in, or `native` for each account's own; absent → the company's reporting currency. */
258
- currency: adReportCurrencySchema.optional(),
259
- tiers: z4.array(z4.enum(AD_ATTENTION_TIERS)).optional(),
260
- limit: z4.number().int().min(1).max(100).default(20),
261
- /** Also return the dismissed items, with how and why each was dismissed. */
262
- includeDismissed: z4.boolean().optional()
263
- });
264
- var adAlertClaimRequestSchema = z4.object({
265
- chatId: z4.string().min(1),
266
- alertId: z4.string().min(1).max(64)
267
- });
268
- var adAlertVerdictRequestSchema = z4.object({
269
- chatId: z4.string().min(1),
270
- alertId: z4.string().min(1).max(64),
271
- verdict: z4.enum(["fixed", "not_now", "wrong"]),
272
- note: z4.string().trim().max(500).optional()
273
- }).refine((body) => body.verdict !== "wrong" || (body.note ?? "") !== "", {
274
- message: "Say why it is wrong with --reason: that is what Baker learns from.",
275
- path: ["reason"]
276
- });
277
-
278
- // ../api/src/ads-performance/changeWords.ts
279
- var FIELD_WORDS = [
280
- [/^(campaign_budget\.)?amount_micros$|^daily_budget$/, { label: "Daily budget", shape: "money" }],
281
- [/total_amount_micros$|^lifetime_budget$/, { label: "Total budget", shape: "money" }],
282
- [/^budget$/, { label: "Budget", shape: "money" }],
283
- [/^spend_cap$|spend_limit/, { label: "Spending limit", shape: "money" }],
284
- [/budget_scheduling_state$/, { label: "Budget scheduling", shape: "state" }],
285
- [/target_cpa(_micros)?$|^cost_cap$/, { label: "Target cost per result", shape: "money" }],
286
- [/target_roas$/, { label: "Target return on ad spend", shape: "multiple" }],
287
- [/^cpc_bid_micros$|^bid_amount$|^bid$/, { label: "Max cost per click", shape: "money" }],
288
- [/^cpm_bid_micros$/, { label: "Max cost per 1,000 views", shape: "money" }],
289
- [/^cpv_bid_micros$/, { label: "Max cost per video view", shape: "money" }],
290
- [/^bid_modifier$/, { label: "Bid adjustments changed", shape: "none" }],
291
- [
292
- /bid_strategy|bidding_strategy|^manual_cp|^maximize_conversion|^target_spend/,
293
- { label: "Bid strategy", shape: "state" }
294
- ],
295
- [/target_impression_share/, { label: "Target impression share", shape: "none" }],
296
- [/^(status|run_status)$/, { label: "Status", shape: "state" }],
297
- [/^name$/, { label: "Name", shape: "text" }],
298
- [/^(start_date|start_date_time|start_time)$/, { label: "Start date", shape: "text" }],
299
- [/^(end_date|end_date_time|end_time)$/, { label: "End date", shape: "text" }],
300
- [/run_dates|schedule/, { label: "Schedule changed", shape: "none" }],
301
- [/_criterion$/, { label: "Targeting changed", shape: "none" }],
302
- [/target_spec$|targeting/, { label: "Targeting changed", shape: "none" }],
303
- [/geo_target|network_settings/, { label: "Where ads show changed", shape: "none" }],
304
- [/language_code$|^languages?$/, { label: "Language", shape: "text" }],
305
- [/_asset$/, { label: "Assets changed", shape: "none" }],
306
- [/^final_urls$|^final_mobile_urls$/, { label: "Landing page link changed", shape: "none" }],
307
- [
308
- /^(tracking_url_template|final_url_suffix|url_custom_parameters|url_tags)$/,
309
- { label: "Tracking settings changed", shape: "none" }
310
- ],
311
- [/^ad(\..+)?$|^creative$/, { label: "Ad edited", shape: "none" }],
312
- [/optimization_goal$/, { label: "Optimizing for", shape: "state" }],
313
- [/^objective$/, { label: "Objective", shape: "state" }],
314
- [/billing_charge$/, { label: "Billing charge", shape: "money" }],
315
- [/^first_delivery_event$/, { label: "Started running", shape: "none" }],
316
- // Google's brand guidelines are a campaign's main and accent colour and its font.
317
- [/^brand_guidelines/, { label: "Brand colors and font changed", shape: "none" }],
318
- // Google's text guidelines: words and claims its auto-written ad text must avoid.
319
- [/^text_guidelines/, { label: "Rules for auto-written text changed", shape: "none" }],
320
- [/^(ai_max_setting|asset_automation_settings)/, { label: "Automated assets changed", shape: "none" }],
321
- [/^shopping_setting/, { label: "Shopping settings changed", shape: "none" }],
322
- [/^frequency_caps?$/, { label: "Frequency cap changed", shape: "none" }]
323
- ];
324
- var KIND_WORDS = {
325
- created: "Created",
326
- status: "Status",
327
- budget: "Budget",
328
- bid: "Bidding",
329
- schedule: "Schedule",
330
- name: "Name",
331
- creative: "Ad edited",
332
- targeting: "Targeting changed",
333
- goal: "Goal",
334
- removed: "Removed",
335
- other: "Changed"
336
- };
337
- function bareField(field) {
338
- return field.trim().replace(/^(update|create)_(ad_account|campaign_group|campaign|ad_set|adset|ad)_/, "").replace(/^ad_account_/, "").replace(/^(maximize_conversions|maximize_conversion_value|target_cpa|target_roas)\./, "").replace(/^campaign_budget\./, "");
339
- }
340
- function wordsFor(field) {
341
- const bare = bareField(field);
342
- return FIELD_WORDS.find(([pattern]) => pattern.test(bare) || pattern.test(field))?.[1] ?? null;
343
- }
344
- function humanize(value) {
345
- const spaced = value.replace(/_micros\b/g, "").replace(/[_.]+/g, " ").trim().toLowerCase();
346
- return spaced.charAt(0).toUpperCase() + spaced.slice(1);
347
- }
348
- var STATUS_WORDS = {
349
- pending_review: "In review",
350
- in_review: "In review",
351
- pending_process: "Getting ready",
352
- in_process: "Getting ready",
353
- processing: "Getting ready",
354
- active: "Running",
355
- enabled: "Running",
356
- with_issues: "Has issues",
357
- disapproved: "Rejected",
358
- preapproved: "Approved",
359
- pending_billing_info: "Waiting for billing details",
360
- campaign_paused: "Paused with its campaign",
361
- adset_paused: "Paused with its ad set",
362
- deleted: "Deleted",
363
- archived: "Archived"
364
- };
365
- function stateWords(value) {
366
- const trimmed = value.trim();
367
- const status = STATUS_WORDS[trimmed.toLowerCase().replace(/[\s-]+/g, "_")];
368
- if (status) return status;
369
- if (/^[a-z]+\/\S+\/\S+/i.test(trimmed)) return "";
370
- if (/^true$/i.test(trimmed)) return "On";
371
- if (/^false$/i.test(trimmed)) return "Off";
372
- if (/^[A-Z][A-Z_]*$|^[a-z][a-z_]*$/.test(trimmed)) return humanize(trimmed.replace(/_bid_strategy$/i, ""));
373
- return trimmed;
374
- }
375
- var CENTS = new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
376
- var WHOLE = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
377
- function signed(currency, cents) {
378
- try {
379
- const digits = cents ? 2 : 0;
380
- return new Intl.NumberFormat("en", {
381
- style: "currency",
382
- currency,
383
- minimumFractionDigits: digits,
384
- maximumFractionDigits: digits
385
- });
386
- } catch {
387
- return null;
388
- }
389
- }
390
- function amounts(oldValue, newValue, sign = "") {
391
- const read = (value) => value.trim() === "" || !Number.isFinite(Number(value)) ? null : Number(value);
392
- const sides = [read(oldValue), read(newValue)];
393
- const whole2 = sides.every((n) => n === null || Number.isInteger(n));
394
- const format = (sign === "" ? null : signed(sign, !whole2)) ?? (whole2 ? WHOLE : CENTS);
395
- const [from, to] = sides.map((n) => n === null ? null : format.format(n));
396
- return [from ?? null, to ?? null];
397
- }
398
- function convertedAmounts(change) {
399
- const target = change.shownIn;
400
- if (!target || !signed(target.currency, false)) return null;
401
- const side = (value) => {
402
- if (value.trim() === "" || !Number.isFinite(Number(value))) return null;
403
- const amount = Number(value) * target.rate;
404
- return signed(target.currency, Math.abs(amount) < 100)?.format(amount) ?? null;
405
- };
406
- const from = side(change.oldValue);
407
- const to = side(change.newValue);
408
- return from === null && to === null ? null : { from, to };
409
- }
410
- function originalAmounts(change, words) {
411
- if (words.shape !== "money" || !convertedAmounts(change)) return null;
412
- const currency = (change.valueCurrency ?? "").trim().toUpperCase();
413
- const [from, to] = amounts(change.oldValue, change.newValue, currency);
414
- const said = from !== null && to !== null ? `${from} \u2192 ${to}` : to ?? from;
415
- return said === null ? null : `${said} in the account's currency`;
416
- }
417
- function multiple(value) {
418
- if (value.trim() === "") return null;
419
- const n = Number(value);
420
- return Number.isFinite(n) ? `${Math.round(n * 100)}%` : null;
421
- }
422
- function moved(label2, from, to, unit = "") {
423
- const suffix = unit ? ` ${unit}` : "";
424
- if (from !== null && to !== null) return `${label2}: ${from} \u2192 ${to}${suffix}`;
425
- if (to !== null) return `${label2}: ${to}${suffix}`;
426
- if (from !== null) return `${label2} removed (was ${from}${suffix})`;
427
- return label2;
428
- }
429
- function valueWords(words, change, options) {
430
- const said = valuesSaid(words, change, options);
431
- return said === words.label && words.shape !== "none" ? `${words.label} changed` : said;
432
- }
433
- function valuesSaid(words, change, options) {
434
- const currency = (change.valueCurrency ?? "").trim().toUpperCase();
435
- switch (words.shape) {
436
- case "money": {
437
- const converted = options.money === "symbol" ? convertedAmounts(change) : null;
438
- if (converted) return moved(words.label, converted.from, converted.to);
439
- if (options.money === "symbol" && signed(currency, false)) {
440
- return moved(words.label, ...amounts(change.oldValue, change.newValue, currency));
441
- }
442
- return moved(words.label, ...amounts(change.oldValue, change.newValue), currency);
443
- }
444
- case "multiple":
445
- return moved(words.label, multiple(change.oldValue), multiple(change.newValue));
446
- case "state":
447
- return moved(words.label, blankToNull(stateWords(change.oldValue)), blankToNull(stateWords(change.newValue)));
448
- case "text":
449
- return moved(words.label, blankToNull(change.oldValue.trim()), blankToNull(change.newValue.trim()));
450
- default:
451
- return words.label;
452
- }
453
- }
454
- function blankToNull(value) {
455
- return value === "" ? null : value;
456
- }
457
- function jsonStrings(text3) {
458
- const out = [];
459
- for (const match of text3.matchAll(/"((?:[^"\\]|\\.)*)("|$)/g)) {
460
- const raw = match[1] ?? "";
461
- let value;
462
- try {
463
- value = JSON.parse(`"${raw.replace(/\\$/, "")}"`);
464
- } catch {
465
- value = raw;
466
- }
467
- out.push(match[2] === "" ? `${value.trimEnd()}\u2026` : value);
468
- }
469
- return out;
470
- }
471
- function targetingLines(value) {
472
- if (!value.trimStart().startsWith("[{")) return null;
473
- const lines = /* @__PURE__ */ new Map();
474
- for (const item of value.matchAll(/\{"content":"((?:[^"\\]|\\.)*)"(?:,"children":\[([^\]]*))?/g)) {
475
- const label2 = (jsonStrings(`"${item[1] ?? ""}"`)[0] ?? "").replace(/:\s*$/, "").trim();
476
- if (label2 !== "") lines.set(label2, jsonStrings(item[2] ?? "").join(", "));
477
- }
478
- return lines.size > 0 ? lines : null;
479
- }
480
- function linesText(lines) {
481
- return [...lines].map(([label2, value]) => value ? `${label2}: ${value}` : label2).join("\n");
482
- }
483
- var TARGETING_PARTS = {
484
- "advantage+ audience": "Advantage+ audience",
485
- "advantage audience": "Advantage+ audience",
486
- "advantage+ placements": "Advantage+ placements",
487
- "advantage detailed targeting": "Advantage+ interests",
488
- location: "locations",
489
- locations: "locations",
490
- age: "age",
491
- gender: "gender",
492
- genders: "gender",
493
- language: "languages",
494
- languages: "languages",
495
- placements: "placements",
496
- placement: "placements",
497
- "detailed targeting": "interests",
498
- interests: "interests",
499
- "custom audience": "custom audiences",
500
- "custom audiences": "custom audiences",
501
- "excluded custom audience": "excluded audiences",
502
- "excluded custom audiences": "excluded audiences",
503
- "lookalike audience": "lookalike audiences",
504
- devices: "devices",
505
- "people who match": "interests",
506
- "exclude people who match": "excluded interests"
507
- };
508
- function targetingPart(label2) {
509
- const key = label2.trim().toLowerCase();
510
- const known = TARGETING_PARTS[key];
511
- if (known) return known;
512
- return /^(Advantage|Facebook|Instagram|Messenger|Threads|Audience Network)/i.test(label2.trim()) ? label2.trim() : key;
513
- }
514
- function targetingWords(change) {
515
- const before = targetingLines(change.oldValue) ?? /* @__PURE__ */ new Map();
516
- const after = targetingLines(change.newValue);
517
- if (!after) return null;
518
- if (before.size === 0) return { summary: "Targeting set", full: linesText(after) };
519
- const moved2 = [.../* @__PURE__ */ new Set([...before.keys(), ...after.keys()])].filter((key) => before.get(key) !== after.get(key));
520
- const what = [...new Set(moved2.map(targetingPart))];
521
- const summary = what.length === 0 ? "Targeting changed" : `Targeting changed: ${what.length > 2 ? `${what.slice(0, 2).join(", ")} and ${what.length - 2} more` : what.join(" and ")}`;
522
- return { summary, full: `Before
523
- ${linesText(before)}
524
-
525
- After
526
- ${linesText(after)}` };
527
- }
528
- function creativeParts(value) {
529
- if (!value.trimStart().startsWith("[")) return null;
530
- const text3 = [];
531
- const media = [];
532
- for (const part of jsonStrings(value)) {
533
- if (part.trim() === "") continue;
534
- if (/^https?:\/\//.test(part)) media.push(part.split("?")[0] ?? part);
535
- else if (/^\d+$/.test(part)) media.push(part);
536
- else text3.push(part.replace(/\\n/g, "\n"));
537
- }
538
- return { text: text3, media };
539
- }
540
- function creativeWords(change) {
541
- const after = creativeParts(change.newValue);
542
- if (!after) return null;
543
- const before = creativeParts(change.oldValue) ?? { text: [], media: [] };
544
- const textMoved = before.text.join("\n") !== after.text.join("\n");
545
- const mediaMoved = before.media.join("\n") !== after.media.join("\n");
546
- const summary = textMoved && mediaMoved ? "Ad text and picture or video changed" : textMoved ? "Ad text edited" : mediaMoved ? "Picture or video changed" : "Ad edited";
547
- return { summary, full: textMoved ? after.text.join("\n\n") : null };
548
- }
549
- var INLINE_LIMIT = 80;
550
- function severalWords(fields) {
551
- const nouns = [
552
- ...new Set(fields.map((field) => (wordsFor(field)?.label ?? humanize(bareField(field))).replace(/ changed$/, "")))
553
- ];
554
- const [first = "Settings", second] = nouns;
555
- if (second === void 0) return { summary: `${first} changed`, full: null };
556
- const rest = nouns.length === 2 ? second.charAt(0).toLowerCase() + second.slice(1) : `${nouns.length - 1} more settings`;
557
- return { summary: `${first} and ${rest} changed`, full: null };
558
- }
559
- function structuredWords(bare, change) {
560
- if (/target_spec$/.test(bare)) return targetingWords(change);
561
- if (bare === "creative") return creativeWords(change);
562
- return null;
563
- }
564
- function lineWords(change, options) {
565
- const fields = change.field.split(",").map((field2) => field2.trim()).filter((field2) => field2 !== "");
566
- if (fields.length > 1) return severalWords(fields);
567
- const field = fields[0] ?? "";
568
- const bare = bareField(field);
569
- const structured = structuredWords(bare, change);
570
- if (structured) return structured;
571
- if (field === "" || change.kind === "created") return { summary: KIND_WORDS[change.kind] ?? "Changed", full: null };
572
- const words = wordsFor(field) ?? { label: humanize(bare), shape: "text" };
573
- const summary = valueWords(words, change, options);
574
- if (summary.length <= INLINE_LIMIT + words.label.length) return { summary, full: null };
575
- return {
576
- summary: `${words.label} changed`,
577
- full: [change.oldValue, change.newValue].filter((value) => value !== "").join("\n\n\u2192\n\n")
578
- };
579
- }
580
- function adChangeWords(change, options = {}) {
581
- const line = lineWords(change, options);
582
- const field = change.field.trim();
583
- const words = options.money === "symbol" && change.kind !== "created" && field !== "" && !field.includes(",") ? wordsFor(field) : null;
584
- return { ...line, original: words ? originalAmounts(change, words) : null };
585
- }
586
-
587
- // ../api/src/ads-performance/creativeAssets.ts
588
- import { z as z5 } from "zod";
589
- var PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
590
- var AD_CREATIVE_ASSET_KINDS = ["image", "video"];
591
- var AD_CREATIVE_ASSET_STATUSES = ["stored", "pending", "unavailable", "too_large", "failed"];
592
- var ids = z5.array(
593
- z5.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
594
- ).max(200);
595
- var adCreativeAssetsRequestSchema = z5.object({
596
- platforms: z5.array(z5.enum(PLATFORMS)).optional(),
597
- adAccountIds: ids.optional(),
598
- /** Only what these ads show. */
599
- adIds: ids.optional(),
600
- /** Only what these ad groups (Performance Max asset groups) show. */
601
- adGroupIds: ids.optional(),
602
- kinds: z5.array(z5.enum(AD_CREATIVE_ASSET_KINDS)).optional(),
603
- statuses: z5.array(z5.enum(AD_CREATIVE_ASSET_STATUSES)).optional(),
604
- limit: z5.number().int().min(1).max(500).default(100)
605
- });
606
-
607
- // ../api/src/ads-performance/creativeHealth.ts
608
- import { z as z6 } from "zod";
609
- var PLATFORMS2 = ["meta", "google", "linkedin", "x", "chatgpt"];
610
- var AD_CREATIVE_HEALTH_SECTIONS = ["trending", "poor", "hit_rate", "freshness"];
611
- var AD_FRESHNESS_NEW_DAYS = [7, 14, 30];
612
- var AD_FRESHNESS_PERIOD_DAYS = [1, 7, 14];
613
- var accountIds = z6.array(
614
- z6.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
615
- ).max(200);
616
- var adCreativeHealthRequestSchema = z6.object({
617
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
618
- currency: adReportCurrencySchema.optional(),
619
- platforms: z6.array(z6.enum(PLATFORMS2)).optional(),
620
- adAccountIds: accountIds.optional(),
621
- /** `creative` counts the same picture or video once across its ads; `ad` keeps every ad apart. */
622
- groupBy: z6.enum(["creative", "ad"]).default("creative"),
623
- /** The reports to read; all when absent. */
624
- sections: z6.array(z6.enum(AD_CREATIVE_HEALTH_SECTIONS)).min(1).optional(),
625
- /** Launch months the hit rate covers, this one included. */
626
- months: z6.number().int().min(2).max(12).default(6)
627
- });
628
-
629
- // ../api/src/ads-performance/diagnostic.ts
630
- import { z as z7 } from "zod";
631
- var DIAGNOSTIC_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
632
- var idList3 = z7.array(
633
- z7.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
634
- ).max(200);
635
- var adPerformanceAnomaliesRequestSchema = z7.object({
636
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
637
- currency: adReportCurrencySchema.optional(),
638
- days: z7.number().int().min(7).max(28).default(14),
639
- end: calendarDaySchema.optional(),
640
- platforms: z7.array(z7.enum(DIAGNOSTIC_PLATFORMS)).optional(),
641
- adAccountIds: idList3.optional(),
642
- cpaSpike: z7.number().min(0.1).max(5).optional()
643
- });
644
- var adPerformanceRecapRequestSchema = z7.object({
645
- days: z7.number().int().min(1).max(31).default(7),
646
- platforms: z7.array(z7.enum(DIAGNOSTIC_PLATFORMS)).optional(),
647
- adAccountIds: idList3.optional()
648
- });
649
-
650
- // ../api/src/ads-performance/health.ts
651
- import { z as z8 } from "zod";
652
- var HEALTH_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
653
- var idList4 = z8.array(
654
- z8.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
655
- ).max(200);
656
- var adPerformanceHealthRequestSchema = z8.object({
657
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
658
- currency: adReportCurrencySchema.optional(),
659
- platforms: z8.array(z8.enum(HEALTH_PLATFORMS)).optional(),
660
- adAccountIds: idList4.optional()
661
- });
662
- var AD_LEARNING_SHARE_THRESHOLDS = { monitor: 0.2, act: 0.5 };
663
- var AD_COPY_VARIANTS_MAX = 5;
664
-
665
- // ../api/src/ads-performance/library.ts
666
- import { z as z9 } from "zod";
667
- var AD_CREATIVE_GROUPS = [
668
- "family",
669
- "hook",
670
- "angle",
671
- "framework",
672
- "offer",
673
- "awareness",
674
- "style",
675
- "creator",
676
- "mechanism"
677
- ];
678
- var adPerformanceFamiliesRequestSchema = z9.object({
679
- ...adPerformanceScopeFields,
680
- /** `family` (one row per library family), or a part of the reading: `hook`, `angle`, `framework`, … */
681
- by: z9.enum(AD_CREATIVE_GROUPS).default("family"),
682
- /** Leave out creatives that spent less than this in the window. */
683
- minSpend: z9.number().min(0).default(0),
684
- limit: z9.number().int().min(1).max(100).default(25)
685
- }).superRefine(scopeRules);
686
-
687
- // ../api/src/ads-performance/liveIn.ts
688
- import { z as z10 } from "zod";
689
- var slug = z10.string().trim().min(1).max(200).regex(/^[^/\s]+(\/[^/\s]+)*$/, "a slug has no spaces");
690
- var adPerformanceLiveInRequestSchema = z10.object({
691
- /** Only this landing (its slug, as in `src/pages/<slug>/`). */
692
- landing: slug.optional(),
693
- /** Only this creative (its slug, as in `src/creatives/<slug>/`). */
694
- creative: slug.optional()
695
- });
696
-
697
- // ../api/src/ads-performance/people.ts
698
- import { z as z11 } from "zod";
699
- var PEOPLE_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
700
- var PEOPLE_MAX_DAYS = 1130;
701
- var AD_PEOPLE_LEVELS = ["campaign", "ad_group", "ad"];
702
- var AD_PEOPLE_TOUCHES = ["first", "last"];
703
- var AD_PERSONA_FIELD_DIMENSIONS = [
704
- "role",
705
- "seniority",
706
- "company_size",
707
- "industry",
708
- "use_case",
709
- "heard_from",
710
- "budget",
711
- "country"
712
- ];
713
- var AD_PERSONA_DIMENSIONS = [...AD_PERSONA_FIELD_DIMENSIONS, "email_kind", "visit_country", "device"];
714
- var idList5 = z11.array(
715
- z11.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
716
- ).max(200);
717
- var scopeFields = {
718
- days: z11.number().int().min(1).max(PEOPLE_MAX_DAYS).optional(),
719
- start: calendarDaySchema.optional(),
720
- end: calendarDaySchema.optional(),
721
- platforms: z11.array(z11.enum(PEOPLE_PLATFORMS)).optional(),
722
- adAccountIds: idList5.optional(),
723
- campaignIds: idList5.optional(),
724
- /** Ad sets (ad groups) to read inside: their ads, or the ad sets themselves. */
725
- adGroupIds: idList5.optional(),
726
- /** Amounts in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
727
- currency: adReportCurrencySchema.optional(),
728
- level: z11.enum(AD_PEOPLE_LEVELS).default("campaign"),
729
- touch: z11.enum(AD_PEOPLE_TOUCHES).default("last")
730
- };
731
- function scopeRules2(req, ctx) {
732
- if (req.days !== void 0 && req.start !== void 0) {
733
- ctx.addIssue({ code: "custom", path: ["days"], message: "pass days or start/end, not both" });
734
- }
735
- if (req.end !== void 0 && req.start === void 0) {
736
- ctx.addIssue({ code: "custom", path: ["end"], message: "end needs a start" });
737
- }
738
- if (req.start !== void 0 && req.end !== void 0 && req.end < req.start) {
739
- ctx.addIssue({ code: "custom", path: ["end"], message: "end is before start" });
740
- }
741
- }
742
- var adPeopleRequestSchema = z11.object({ ...scopeFields, limit: z11.number().int().min(1).max(500).default(50) }).superRefine(scopeRules2);
743
- var adPersonasRequestSchema = z11.object({
744
- ...scopeFields,
745
- dimension: z11.enum(AD_PERSONA_DIMENSIONS),
746
- /** How many distinct answers to name before the rest read "other". */
747
- top: z11.number().int().min(1).max(30).default(12)
748
- }).superRefine(scopeRules2);
749
- var adPeopleSettingsRequestSchema = z11.object({ days: z11.number().int().min(7).max(300).default(90) });
750
-
751
- // ../api/src/ads-performance/search.ts
752
- import { z as z12 } from "zod";
753
- var adPerformanceBrandSplitRequestSchema = z12.object({
754
- ...adPerformanceScopeFields,
755
- /** Months of history the trend draws, ending with the current one. */
756
- months: z12.number().int().min(1).max(13).default(6)
757
- }).superRefine(scopeRules);
758
- var adPerformanceImpressionShareRequestSchema = z12.object({
759
- ...adPerformanceScopeFields,
760
- limit: z12.number().int().min(1).max(200).default(50)
761
- }).superRefine(scopeRules);
762
- var adPerformanceKeywordQualityRequestSchema = z12.object({
763
- ...adPerformanceScopeFields,
764
- /** Only keywords scored at or under this. */
765
- maxQualityScore: z12.number().int().min(1).max(10).default(10),
766
- limit: z12.number().int().min(1).max(500).default(100)
767
- }).superRefine(scopeRules);
768
- var AD_BUDGET_SEGMENTS = ["all", "brand", "competitor", "non_brand"];
769
- var adPerformanceBudgetsRequestSchema = z12.object({
770
- /** `YYYY-MM`; the current month when absent. */
771
- month: z12.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/, "use YYYY-MM").optional(),
772
- platforms: z12.array(z12.enum(AD_PERFORMANCE_PLATFORMS)).optional(),
773
- adAccountIds: idList.optional()
774
- });
775
- var adPerformanceBudgetsSetRequestSchema = z12.object({
776
- /** Where the agent stages the change; required, applied at Publish. */
777
- chatId: z12.string().min(1),
778
- platform: z12.enum(AD_PERFORMANCE_PLATFORMS).nullable().default(null),
779
- adAccountId: z12.string().min(1).nullable().default(null),
780
- segment: z12.enum(AD_BUDGET_SEGMENTS).default("all"),
781
- /** Null clears the budget. */
782
- amount: z12.number().positive().nullable(),
783
- currency: z12.string().regex(/^[A-Za-z]{3}$/).transform((value) => value.toUpperCase()).optional()
784
- });
785
- var adPerformanceSegmentsRequestSchema = z12.object({});
786
- var adPerformanceSegmentsSetRequestSchema = z12.object({
787
- chatId: z12.string().min(1),
788
- /** Replace the brand's own names (company, website, products); empty goes back to its name and website. */
789
- brandNames: z12.array(z12.string().min(1).max(80)).max(40).optional(),
790
- /** Replace the competitor names. */
791
- competitorNames: z12.array(z12.string().min(1).max(80)).max(40).optional(),
792
- /** A campaign's or a search term's segment; `segment` null clears a person's choice. */
793
- choice: z12.object({
794
- kind: z12.enum(["term", "campaign"]),
795
- adAccountId: z12.string().default(""),
796
- key: z12.string().min(1).max(300),
797
- label: z12.string().max(300).optional(),
798
- segment: z12.enum(["brand", "competitor", "non_brand", "other"]).nullable()
799
- }).optional(),
800
- /** A segment's target; `value` null clears it. */
801
- target: z12.object({
802
- segment: z12.enum(["brand", "competitor", "non_brand"]),
803
- metric: z12.enum(["cost_per_result", "roas"]),
804
- value: z12.number().positive().nullable(),
805
- currency: z12.string().regex(/^[A-Za-z]{3}$/).transform((value) => value.toUpperCase()).optional()
806
- }).optional()
807
- }).refine((req) => req.brandNames || req.competitorNames || req.choice || req.target, {
808
- message: "name what to change: --brand, --competitors, a --campaign or --term choice, or a --target"
809
- });
810
- var adPerformanceGoogleReportRequestSchema = z12.object({ ...adPerformanceScopeFields, months: z12.number().int().min(1).max(13).default(6) }).superRefine(scopeRules);
811
-
812
- // ../api/src/ads-performance/setup.ts
813
- import { z as z13 } from "zod";
814
- var adPerformanceSetupRequestSchema = z13.object({
815
- platforms: z13.array(z13.enum(AD_PERFORMANCE_PLATFORMS)).optional(),
816
- adAccountIds: idList.optional()
817
- });
818
-
819
- // ../api/src/ads-performance/targets.ts
820
- import { z as z14 } from "zod";
821
-
822
- // ../api/src/ads-performance/resultNoun.ts
823
- var RESULT_NAME_MAX_LENGTH = 40;
824
-
825
- // ../api/src/ads-performance/targets.ts
826
- var TARGET_PLATFORMS = ["meta", "google", "linkedin", "x", "chatgpt"];
827
- var AD_TARGET_FOCUSES = ["cost_per_result", "roas"];
828
- var AD_BUDGET_MOVES = ["scale", "hold", "cut", "pause"];
829
- var idList6 = z14.array(
830
- z14.string().min(1).regex(/^[^,]+$/, "ids cannot contain commas")
831
- ).max(200);
832
- var adPerformanceTargetsRequestSchema = z14.object({
833
- platforms: z14.array(z14.enum(TARGET_PLATFORMS)).optional(),
834
- /** The period the per-event numbers cover; 30 days when absent. */
835
- days: z14.number().int().min(7).max(400).optional(),
836
- start: calendarDaySchema.optional(),
837
- end: calendarDaySchema.optional(),
838
- currency: adReportCurrencySchema.optional(),
839
- /** Show what this chat staged on top. */
840
- chatId: z14.string().min(1).optional()
841
- });
842
- var adPerformanceBudgetRecommendationsRequestSchema = z14.object({
843
- platforms: z14.array(z14.enum(TARGET_PLATFORMS)).optional(),
844
- adAccountIds: idList6.optional(),
845
- /** Money in this currency (ISO code), or `native` for each account's own; absent → the company's reporting currency. */
846
- currency: adReportCurrencySchema.optional(),
847
- /** Only these moves; all when absent. */
848
- moves: z14.array(z14.enum(AD_BUDGET_MOVES)).optional(),
849
- limit: z14.number().int().min(1).max(200).default(50)
850
- });
851
- var currencyCode = z14.string().regex(/^[A-Za-z]{3}$/, "use a currency code like EUR").transform((value) => value.toUpperCase());
852
- var adTargetStageRequestSchema = z14.object({
853
- chatId: z14.string().min(1),
854
- company: z14.boolean().optional(),
855
- platform: z14.enum(TARGET_PLATFORMS).optional(),
856
- adAccountId: z14.string().min(1).max(100).optional(),
857
- event: z14.string().trim().min(1).max(300).optional(),
858
- metric: z14.enum(AD_TARGET_FOCUSES).optional(),
859
- value: z14.number().positive().max(1e9).nullable(),
860
- currency: currencyCode.optional()
861
- }).superRefine((body, ctx) => {
862
- const account = body.platform !== void 0 || body.adAccountId !== void 0;
863
- if (body.company === true && (account || body.event !== void 0))
864
- ctx.addIssue({ code: "custom", message: "A company default names no platform, ad account or event." });
865
- if (body.company !== true && (body.platform === void 0 || body.adAccountId === void 0))
866
- ctx.addIssue({
867
- code: "custom",
868
- message: "Name the ad account (platform and account id), or the company default."
869
- });
870
- if (body.value !== null && body.metric === void 0)
871
- ctx.addIssue({ code: "custom", message: "Say what the target judges: cost per result or return on ad spend." });
872
- if (body.value !== null && body.metric === "cost_per_result" && body.currency === void 0)
873
- ctx.addIssue({ code: "custom", message: "A cost per result target needs its currency." });
874
- });
875
- var adResultsStageRequestSchema = z14.object({
876
- chatId: z14.string().min(1),
877
- platform: z14.enum(TARGET_PLATFORMS),
878
- adAccountId: z14.string().min(1).max(100),
879
- /** Conversion action keys to start counting. */
880
- count: z14.array(z14.string().trim().min(1).max(300)).max(50).default([]),
881
- /** Conversion action keys to stop counting. */
882
- uncount: z14.array(z14.string().trim().min(1).max(300)).max(50).default([]),
883
- /** Back to what the platform counts for every account (its own choice gone). */
884
- reset: z14.boolean().optional()
885
- }).refine((body) => body.reset === true || body.count.length + body.uncount.length > 0, {
886
- message: "Name actions to count or stop counting, or reset."
887
- });
888
- var resultWord = z14.string().trim().min(1).max(RESULT_NAME_MAX_LENGTH);
889
- var adResultNameStageRequestSchema = z14.object({
890
- chatId: z14.string().min(1),
891
- singular: resultWord.optional(),
892
- plural: resultWord.optional(),
893
- reset: z14.boolean().optional()
894
- }).refine((body) => body.reset === true !== (body.singular !== void 0 && body.plural !== void 0), {
895
- message: "Name one result and several (--singular lead --plural leads), or reset."
896
- });
897
- var adGoalUnstageRequestSchema = z14.object({ chatId: z14.string().min(1), key: z14.string().min(1).optional(), all: z14.boolean().optional() }).refine((body) => body.key === void 0 !== (body.all !== true), {
898
- message: "Pass one staged key or all."
899
- });
900
-
901
- // src/commands/ads/performance/index.ts
902
- import { defineCommand as defineCommand9 } from "citty";
903
- import { z as z15 } from "zod";
904
-
905
- // src/commands/ads/performance/args.ts
906
- var REPORT_CURRENCY_ARG = {
907
- type: "string",
908
- description: "Show every amount in this currency (ISO code, e.g. EUR), converted at the period's average ECB rate, or native for each ad account's own (default: the company's reporting currency, as the dashboard shows it)",
909
- required: false
910
- };
911
- var SCOPE_ARGS = {
912
- days: {
913
- type: "string",
914
- description: "Complete days back from yesterday (default 30). Use this or --start/--end",
915
- required: false
916
- },
917
- start: { type: "string", description: "First day, YYYY-MM-DD (instead of --days)", required: false },
918
- end: { type: "string", description: "Last day, YYYY-MM-DD (default yesterday; needs --start)", required: false },
919
- platforms: {
920
- type: "string",
921
- description: "Comma list of meta,google,linkedin,x,chatgpt (default all connected)",
922
- required: false
923
- },
924
- "ad-account-ids": {
925
- type: "string",
926
- description: "Comma list of ad account ids (default all selected)",
927
- required: false
928
- },
929
- "campaign-ids": { type: "string", description: "Comma list of campaign ids", required: false },
930
- currency: REPORT_CURRENCY_ARG,
931
- full: {
932
- type: "boolean",
933
- description: "Every field the store returns, unrounded, instead of the compact view",
934
- required: false,
935
- default: false
936
- }
937
- };
938
- var PAGE_ARGS = {
939
- limit: { type: "string", description: "Rows to return", required: false },
940
- offset: { type: "string", description: "Rows to skip, for the next page", required: false }
941
- };
942
- var ORDER_ARG = { type: "string", description: "desc (default) or asc", required: false };
943
- var ENTITIES_ARGS = {
944
- ...SCOPE_ARGS,
945
- level: { type: "string", description: "campaign (default), ad_group, ad or keyword", required: false },
946
- "ad-group-ids": { type: "string", description: "Comma list of ad group / ad set ids", required: false },
947
- statuses: {
948
- type: "string",
949
- description: "Comma list: active,paused,scheduled,ended,in_review,rejected,draft,archived,removed",
950
- required: false
951
- },
952
- search: { type: "string", description: "Only names containing this text", required: false },
953
- sort: {
954
- type: "string",
955
- description: "spend (default), impressions, clicks, link_clicks, results, result_value, cost_per_result, ctr, first_delivery, last_delivery, name",
956
- required: false
957
- },
958
- order: ORDER_ARG,
959
- ...PAGE_ARGS,
960
- "include-idle": {
961
- type: "boolean",
962
- description: "Also list entities that did not deliver in the window",
963
- required: false,
964
- default: false
965
- },
966
- compare: {
967
- type: "boolean",
968
- description: "Compare each row's settled days with as many days before (comparison.deltas); also lists what stopped delivering",
969
- required: false,
970
- default: false
971
- },
972
- segments: {
973
- type: "string",
974
- description: "Google Search only: comma list of brand, competitor, non_brand, other, unsorted",
975
- required: false
976
- }
977
- };
978
- var ENTITY_ARGS = {
979
- id: { type: "positional", description: "The campaign, ad group, ad or keyword id", required: true },
980
- platform: { type: "string", description: "meta, google, linkedin, x or chatgpt", required: true },
981
- level: { type: "string", description: "campaign, ad_group, ad or keyword", required: true },
982
- "ad-group-id": { type: "string", description: "The ad group of an ad or keyword, when known", required: false },
983
- ...SCOPE_ARGS
984
- };
985
- var CHANGE_FILTER_ARGS = {
986
- kinds: {
987
- type: "string",
988
- description: "Comma list: created,status,budget,bid,schedule,name,creative,targeting,goal,removed,other (goal = what it optimises for or how results are counted)",
989
- required: false
990
- },
991
- "actor-kinds": {
992
- type: "string",
993
- description: "Comma list: person,platform_automatic,baker,unknown (baker = changes Baker published)",
994
- required: false
995
- }
996
- };
997
- var CHANGES_ARGS = {
998
- ...SCOPE_ARGS,
999
- ...CHANGE_FILTER_ARGS,
1000
- levels: { type: "string", description: "Comma list: account,campaign,ad_group,ad,keyword", required: false },
1001
- "entity-ids": { type: "string", description: "Comma list of entity ids", required: false },
1002
- sources: {
1003
- type: "string",
1004
- description: "Comma list: baker (published from Baker), platform (the platform's change log), observed",
1005
- required: false
1006
- },
1007
- ...PAGE_ARGS
1008
- };
1009
- var IMPACT_ARGS = {
1010
- ...SCOPE_ARGS,
1011
- ...CHANGE_FILTER_ARGS,
1012
- "impact-days": {
1013
- type: "string",
1014
- description: "Days compared on each side of a change, 3-28 (default 7)",
1015
- required: false
1016
- },
1017
- ...PAGE_ARGS
1018
- };
1019
- var CREATIVES_ARGS = {
1020
- ...SCOPE_ARGS,
1021
- "group-by": {
1022
- type: "string",
1023
- description: "creative (default: the same creative across ads counts once) or ad",
1024
- required: false
1025
- },
1026
- statuses: ENTITIES_ARGS.statuses,
1027
- "min-spend": { type: "string", description: "Least spend to be listed (default 0)", required: false },
1028
- "min-impressions": { type: "string", description: "Least impressions to be listed (default 1000)", required: false },
1029
- limit: PAGE_ARGS.limit
1030
- };
1031
- var BREAKDOWN_ARGS = {
1032
- ...SCOPE_ARGS,
1033
- dimension: {
1034
- type: "string",
1035
- description: "placement, country or device; Meta only: age, gender or audience_segment",
1036
- required: true
1037
- }
1038
- };
1039
- var SEARCH_TERMS_ARGS = {
1040
- ...SCOPE_ARGS,
1041
- "ad-group-ids": ENTITIES_ARGS["ad-group-ids"],
1042
- "min-clicks": { type: "string", description: "Least clicks to be listed (default 1)", required: false },
1043
- "zero-results": {
1044
- type: "boolean",
1045
- description: "Only terms with no conversions: the negative-keyword candidates",
1046
- required: false,
1047
- default: false
1048
- },
1049
- search: { type: "string", description: "Only terms containing this text", required: false },
1050
- sort: {
1051
- type: "string",
1052
- description: "spend (default), clicks, conversions or cost_per_conversion",
1053
- required: false
1054
- },
1055
- order: ORDER_ARG,
1056
- ...PAGE_ARGS,
1057
- "term-segments": {
1058
- type: "string",
1059
- description: "Comma list: brand, competitor, non_brand, unsorted \u2014 what each term was read as",
1060
- required: false
1061
- },
1062
- "campaign-segments": {
1063
- type: "string",
1064
- description: "Comma list: brand, competitor, non_brand, other \u2014 the segment of the campaign that bought it",
1065
- required: false
1066
- },
1067
- unsure: {
1068
- type: "boolean",
1069
- description: "Only terms Baker was unsure how to sort: settle them with segments set --term",
1070
- required: false,
1071
- default: false
1072
- }
1073
- };
1074
- var PACING_ARGS = {
1075
- month: { type: "string", description: "YYYY-MM (default the current month)", required: false },
1076
- platforms: SCOPE_ARGS.platforms,
1077
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
1078
- currency: REPORT_CURRENCY_ARG,
1079
- full: SCOPE_ARGS.full
1080
- };
1081
- var FUNNEL_ARGS = {
1082
- ...SCOPE_ARGS,
1083
- level: { type: "string", description: "campaign (default), ad_group or ad", required: false },
1084
- "by-landing": {
1085
- type: "boolean",
1086
- description: "Split the visits by the page they entered on",
1087
- required: false,
1088
- default: false
1089
- },
1090
- limit: PAGE_ARGS.limit
1091
- };
1092
- var GOALS_ARGS = {
1093
- days: {
1094
- type: "string",
1095
- description: "How far back to look for conversion actions, 7-400 (default 90)",
1096
- required: false
1097
- },
1098
- full: SCOPE_ARGS.full
1099
- };
1100
- var STATUS_ARGS = { full: SCOPE_ARGS.full };
1101
- function optionalString(raw) {
1102
- return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : void 0;
1103
- }
1104
- function optionalNumber(raw) {
1105
- return typeof raw === "string" && raw.trim() !== "" ? Number(raw) : void 0;
1106
- }
1107
- function commaList(raw) {
1108
- const value = optionalString(raw);
1109
- if (value === void 0) return void 0;
1110
- return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
1111
- }
1112
- var ENUM_ALIASES = {
1113
- adset: "ad_group",
1114
- ad_set: "ad_group",
1115
- adgroup: "ad_group",
1116
- google_ads: "google",
1117
- facebook: "meta",
1118
- twitter: "x"
1119
- };
1120
- function enumValue(raw) {
1121
- const value = optionalString(raw)?.toLowerCase().replaceAll("-", "_");
1122
- return value === void 0 ? void 0 : ENUM_ALIASES[value] ?? value;
1123
- }
1124
- function enumList(raw) {
1125
- return commaList(raw)?.map((item) => enumValue(item) ?? item);
1126
- }
1127
- function scopeInput(args) {
1128
- return {
1129
- days: optionalNumber(args.days),
1130
- start: optionalString(args.start),
1131
- end: optionalString(args.end),
1132
- platforms: enumList(args.platforms),
1133
- adAccountIds: commaList(args["ad-account-ids"]),
1134
- campaignIds: commaList(args["campaign-ids"]),
1135
- currency: optionalString(args.currency)
1136
- };
1137
- }
1138
- function entitiesInput(args) {
1139
- return {
1140
- ...scopeInput(args),
1141
- level: enumValue(args.level),
1142
- adGroupIds: commaList(args["ad-group-ids"]),
1143
- statuses: enumList(args.statuses),
1144
- search: optionalString(args.search),
1145
- sort: enumValue(args.sort),
1146
- order: enumValue(args.order),
1147
- limit: optionalNumber(args.limit),
1148
- offset: optionalNumber(args.offset),
1149
- includeIdle: args["include-idle"] === true,
1150
- compare: args.compare === true,
1151
- segments: enumList(args.segments)
1152
- };
1153
- }
1154
- function entityInput(args) {
1155
- return {
1156
- ...scopeInput(args),
1157
- platform: enumValue(args.platform),
1158
- level: enumValue(args.level),
1159
- entityId: optionalString(args.id),
1160
- adGroupId: optionalString(args["ad-group-id"])
1161
- };
1162
- }
1163
- function changesInput(args) {
1164
- return {
1165
- ...scopeInput(args),
1166
- kinds: enumList(args.kinds),
1167
- levels: enumList(args.levels),
1168
- entityIds: commaList(args["entity-ids"]),
1169
- sources: enumList(args.sources),
1170
- actorKinds: enumList(args["actor-kinds"]),
1171
- limit: optionalNumber(args.limit),
1172
- offset: optionalNumber(args.offset)
1173
- };
1174
- }
1175
- function impactInput(args) {
1176
- return {
1177
- ...scopeInput(args),
1178
- kinds: enumList(args.kinds),
1179
- actorKinds: enumList(args["actor-kinds"]),
1180
- impactDays: optionalNumber(args["impact-days"]),
1181
- limit: optionalNumber(args.limit),
1182
- offset: optionalNumber(args.offset)
1183
- };
1184
- }
1185
- function creativesInput(args) {
1186
- return {
1187
- ...scopeInput(args),
1188
- groupBy: enumValue(args["group-by"]),
1189
- statuses: enumList(args.statuses),
1190
- minSpend: optionalNumber(args["min-spend"]),
1191
- minImpressions: optionalNumber(args["min-impressions"]),
1192
- limit: optionalNumber(args.limit)
1193
- };
1194
- }
1195
- function breakdownInput(args) {
1196
- return { ...scopeInput(args), dimension: enumValue(args.dimension) };
1197
- }
1198
- function searchTermsInput(args) {
1199
- return {
1200
- ...scopeInput(args),
1201
- adGroupIds: commaList(args["ad-group-ids"]),
1202
- minClicks: optionalNumber(args["min-clicks"]),
1203
- noResults: args["zero-results"] === true,
1204
- search: optionalString(args.search),
1205
- sort: enumValue(args.sort),
1206
- order: enumValue(args.order),
1207
- limit: optionalNumber(args.limit),
1208
- offset: optionalNumber(args.offset),
1209
- termSegments: enumList(args["term-segments"]),
1210
- campaignSegments: enumList(args["campaign-segments"]),
1211
- unsureOnly: args.unsure === true
1212
- };
1213
- }
1214
- function pacingInput(args) {
1215
- return {
1216
- month: optionalString(args.month),
1217
- platforms: enumList(args.platforms),
1218
- adAccountIds: commaList(args["ad-account-ids"]),
1219
- currency: optionalString(args.currency)
1220
- };
1221
- }
1222
- function funnelInput(args) {
1223
- return {
1224
- ...scopeInput(args),
1225
- level: enumValue(args.level),
1226
- byLanding: args["by-landing"] === true,
1227
- limit: optionalNumber(args.limit)
1228
- };
1229
- }
1230
- function goalsInput(args) {
1231
- return { days: optionalNumber(args.days) };
1232
- }
1233
- var FLAG_OF_FIELD = {
1234
- adAccountIds: "--ad-account-ids",
1235
- campaignIds: "--campaign-ids",
1236
- adGroupIds: "--ad-group-ids",
1237
- adGroupId: "--ad-group-id",
1238
- entityId: "id",
1239
- entityIds: "--entity-ids",
1240
- includeIdle: "--include-idle",
1241
- actorKinds: "--actor-kinds",
1242
- impactDays: "--impact-days",
1243
- groupBy: "--group-by",
1244
- minSpend: "--min-spend",
1245
- minImpressions: "--min-impressions",
1246
- minClicks: "--min-clicks",
1247
- noResults: "--zero-results",
1248
- byLanding: "--by-landing"
1249
- };
1250
- function invalidArgsMessage(issues) {
1251
- return issues.map((issue) => {
1252
- const [field, ...rest] = issue.path.map(String);
1253
- if (field === void 0) return `args: ${issue.message}`;
1254
- const flag = FLAG_OF_FIELD[field] ?? `--${field}`;
1255
- const item = rest.length > 0 ? ` (item ${rest.join(".")})` : "";
1256
- return `${flag}${item}: ${issue.message}`;
1257
- }).join("; ");
1258
- }
1259
-
1260
- // src/commands/ads/performance/reportCommand.ts
1261
- import { defineCommand } from "citty";
1262
- function failInvalid(issues) {
1263
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(issues) } });
1264
- process.exit(1);
1265
- }
1266
- function failReport(err) {
1267
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
1268
- if (err instanceof ApiError && refusal !== null) {
1269
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
1270
- process.exit(1);
1271
- }
1272
- if (err instanceof ApiError && isAnalyticsBusy(err.message)) {
1273
- writeJson({ ok: false, error: { code: err.code, message: err.message, fix: ANALYTICS_BUSY_FIX } });
1274
- process.exit(1);
1275
- }
1276
- failApi(err);
1277
- }
1278
- function report(spec) {
1279
- registerSchema({
1280
- command: `ads.performance.${spec.schemaName}`,
1281
- description: spec.schemaDescription,
1282
- args: spec.args
1283
- });
1284
- return defineCommand({
1285
- meta: { name: spec.name, description: spec.help },
1286
- args: spec.args,
1287
- run: async ({ args }) => {
1288
- const parsed = spec.request.safeParse(spec.input(args));
1289
- if (!parsed.success) failInvalid(parsed.error.issues);
1290
- try {
1291
- const response = await retryOnceIfBusy(
1292
- () => apiPost(`/api/ads/performance/${spec.path}`, parsed.data),
1293
- (caught) => caught instanceof Error ? caught.message : ""
1294
- );
1295
- const hints = spec.hints?.(response.data, parsed.data) ?? [];
1296
- const data = args.full === true ? response.data : (spec.compact ?? roundDeep)(response.data);
1297
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
1298
- } catch (err) {
1299
- failReport(err);
1300
- }
1301
- }
1302
- });
1303
- }
1304
- var STORE_NOTE = "Reads Baker's stored copy of every connected ad platform (busy accounts refreshed every few hours, quiet ones once or twice a day; no platform call). Results are the company's chosen goals, else each platform's headline conversions. Money is in the company's reporting currency, converted at the period's average ECB rate (--currency to pick another, native for each account's own); a currency with no rate keeps its own, so never add amounts across currencies. --full returns every field unrounded.";
1305
-
1306
- // src/commands/ads/performance/library.ts
1307
- function compactLibraryLink(link) {
1308
- if (!link) return "unchecked";
1309
- const analysis = link.analysis;
1310
- const parts = {
1311
- status: link.status,
1312
- familyId: link.familyId,
1313
- libraryAdId: link.adId,
1314
- ...link.otherFamilies > 0 ? { otherFamilies: link.otherFamilies } : {},
1315
- ...link.notFoundReason ? { reason: link.notFoundReason } : {},
1316
- ...link.chosen ? { chosenByPerson: true } : {},
1317
- hook: analysis?.hook,
1318
- angle: analysis?.angleType,
1319
- framework: analysis?.framework,
1320
- offer: analysis?.offer,
1321
- style: analysis?.style,
1322
- libraryWinnerScore: link.libraryWinnerScore === null ? void 0 : roundNumber(link.libraryWinnerScore)
1323
- };
1324
- return Object.fromEntries(Object.entries(parts).filter(([, value]) => value !== null && value !== void 0));
1325
- }
1326
- function averageCosts(rows) {
1327
- const totals = /* @__PURE__ */ new Map();
1328
- for (const row of rows) {
1329
- const total = totals.get(row.currency) ?? { spend: 0, results: 0 };
1330
- totals.set(row.currency, { spend: total.spend + row.metrics.spend, results: total.results + row.metrics.results });
1331
- }
1332
- return new Map([...totals].map(([currency, t]) => [currency, t.results >= 1 ? t.spend / t.results : null]));
1333
- }
1334
- function vsAverage(cost, average) {
1335
- return cost === null || !average ? {} : { vsAverage: cost / average };
1336
- }
1337
- function compactFamilies(data) {
1338
- const averages = averageCosts(data.rows);
1339
- return {
1340
- window: data.window,
1341
- by: data.by,
1342
- brand: data.brand && { label: data.brand.label, status: data.brand.status, platforms: data.brand.platforms },
1343
- coverage: roundDeep(data.coverage),
1344
- rows: data.rows.map(
1345
- (row) => roundDeep({
1346
- key: row.key,
1347
- label: row.label,
1348
- currency: row.currency,
1349
- creatives: row.creatives,
1350
- activeCreatives: row.activeCreatives,
1351
- spend: row.metrics.spend,
1352
- results: row.metrics.results,
1353
- costPerResult: row.ratios.costPerResult,
1354
- ...vsAverage(row.ratios.costPerResult, averages.get(row.currency)),
1355
- spendShare: row.spendShare,
1356
- resultsShare: row.resultsShare,
1357
- abovePeers: row.abovePeers,
1358
- belowPeers: row.belowPeers,
1359
- ...row.analysis ? {
1360
- hook: row.analysis.hook,
1361
- angle: row.analysis.angleType,
1362
- framework: row.analysis.framework,
1363
- offer: row.analysis.offer,
1364
- style: row.analysis.style
1365
- } : {},
1366
- ...row.agreement ? { agreement: row.agreement } : {},
1367
- ...row.library.bestWinnerScore !== null ? { libraryWinnerScore: row.library.bestWinnerScore } : {},
1368
- topCreative: row.top.groupKey
1369
- })
1370
- )
1371
- };
1372
- }
1373
- function familiesHints(data) {
1374
- if (!data.brand) {
1375
- return [
1376
- "The brand's own ads are not in the winning-ads library yet: Baker follows the Facebook Page its Meta ads spend most on after the next sync. To add it now, or another platform's account: baker winning-ads follow <the brand's website or page>."
1377
- ];
1378
- }
1379
- const hints = [];
1380
- if (data.brand.status === "scanning")
1381
- hints.push("The library is still collecting the brand's ads: links and readings fill in as it finishes.");
1382
- const libraryOnly = data.rows.filter((row) => row.agreement === "library_only").length;
1383
- if (libraryOnly > 0)
1384
- hints.push(
1385
- `${libraryOnly} families the library calls winners did no better than their ad-set peers here: its score reads how long and widely an ad ran, not what it brought \u2014 trust the money, and say which you used.`
1386
- );
1387
- if (data.coverage.ambiguous > 0)
1388
- hints.push(
1389
- `${data.coverage.ambiguous} creatives fit more than one family (videos launched together under the same words); they sit under "Unsorted" until a person picks one on the Ads screen.`
1390
- );
1391
- hints.push(
1392
- "Open a family's library ad with baker winning-ads content <libraryAdId> --platform <platform> for the full reading (transcript, on-screen text, pacing)."
1393
- );
1394
- return hints;
1395
- }
1396
- var familiesCommand = report({
1397
- name: "families",
1398
- schemaName: "families",
1399
- schemaDescription: `The creatives the company launched, grouped by their family in the winning-ads library (--by family: the same creative across ad sets, accounts and re-uploads) or by what the library read in them (--by hook | angle | framework | offer | awareness | style | creator | mechanism), each group with its real spend, results, cost per result and vsAverage (its cost per result over the average of every group: 0.77 = 23% cheaper), share of spend and results, and how many of its creatives beat or fell behind their ad-group peers. Per family: the library's reading, its winner score (how long and widely the ad ran publicly \u2014 a guess) and agreement: both | library_only | ours_only | neither | unknown, the guess against the money. coverage says how many creatives are linked, likely, ambiguous, not in the library, or not checked (platforms the brand is not in the library on). Defaults to the platforms the brand is in the library on. ${STORE_NOTE}`,
1400
- help: "Launched creatives by library family or by hook, angle, framework\u2026 Example: baker ads performance families --by hook --days 90",
1401
- path: "families",
1402
- args: {
1403
- ...SCOPE_ARGS,
1404
- by: {
1405
- type: "string",
1406
- description: `Group by: ${AD_CREATIVE_GROUPS.join(" | ")} (default family)`,
1407
- required: false
1408
- },
1409
- "min-spend": { type: "string", description: "Leave out creatives that spent less than this", required: false },
1410
- limit: { type: "string", description: "Groups to return (default 25, max 100)", required: false }
1411
- },
1412
- request: adPerformanceFamiliesRequestSchema,
1413
- input: (args) => ({
1414
- ...scopeInput(args),
1415
- by: enumValue(args.by),
1416
- minSpend: optionalNumber(args["min-spend"]),
1417
- limit: optionalNumber(args.limit)
1418
- }),
1419
- compact: compactFamilies,
1420
- hints: familiesHints
1421
- });
1422
-
1423
- // src/commands/ads/performance/report.ts
1424
- function adPerformanceRefusal(code, message) {
1425
- if (code !== "CONFLICT" || !message.includes(AD_PERFORMANCE_NOT_CONNECTED_MESSAGE)) return null;
1426
- return {
1427
- code: "ADS_NOT_CONNECTED",
1428
- fix: {
1429
- action: "ask_user",
1430
- explanation: "Connect an ad platform in Integrations and select an ad account; history starts loading at once."
1431
- }
1432
- };
1433
- }
1434
- function roundNumber(value) {
1435
- if (!Number.isFinite(value) || Number.isInteger(value)) return value;
1436
- if (Math.abs(value) >= 1) return Math.round(value * 100) / 100;
1437
- return Number(value.toPrecision(4));
1438
- }
1439
- function roundDeep(value) {
1440
- if (typeof value === "number") return roundNumber(value);
1441
- if (Array.isArray(value)) return value.map((item) => roundDeep(item));
1442
- if (value !== null && typeof value === "object") {
1443
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, roundDeep(item)]));
1444
- }
1445
- return value;
1446
- }
1447
- function isoOrNull(millis) {
1448
- return millis === null ? null : new Date(millis).toISOString();
1449
- }
1450
- function dropEmpty(record) {
1451
- return Object.fromEntries(
1452
- Object.entries(record).filter(
1453
- ([, value]) => value !== "" && value !== null && value !== void 0 && !(Array.isArray(value) && value.length === 0)
1454
- )
1455
- );
1456
- }
1457
- var CORE_METRICS = ["spend", "impressions", "clicks", "linkClicks", "results", "resultValue"];
1458
- var CORE_RATIOS = ["ctr", "cpc", "costPerResult", "resultValuePerSpend"];
1459
- var OPTIONAL_RATIOS = [
1460
- "impressionShare",
1461
- "lostShareBudget",
1462
- "lostShareRank",
1463
- "topImpressionRate",
1464
- "absoluteTopImpressionRate",
1465
- "allConversionsPerConversion",
1466
- "viewRate",
1467
- "costPerView",
1468
- "frequency"
1469
- ];
1470
- var HIDDEN_METRICS = /* @__PURE__ */ new Set([
1471
- "reach",
1472
- "searchTopMeasuredImpressions",
1473
- "allConversionsMeasuredConversions"
1474
- ]);
1475
- function compactMetrics(metrics) {
1476
- const out = {};
1477
- for (const key of CORE_METRICS) out[key] = roundNumber(metrics[key]);
1478
- for (const [key, value] of Object.entries(metrics)) {
1479
- if (!(key in out) && value !== 0 && !HIDDEN_METRICS.has(key)) out[key] = roundNumber(value);
1480
- }
1481
- return out;
1482
- }
1483
- function compactRatios(ratios) {
1484
- const out = {};
1485
- for (const key of CORE_RATIOS) out[key] = ratios[key] === null ? null : roundNumber(ratios[key]);
1486
- for (const key of OPTIONAL_RATIOS) {
1487
- const value = ratios[key];
1488
- if (value !== null) out[key] = roundNumber(value);
1489
- }
1490
- return out;
1491
- }
1492
- function entityPath(row) {
1493
- if (row.level === "campaign") return null;
1494
- const parts = [row.campaignName || row.campaignId];
1495
- if (row.level !== "ad_group") parts.push(row.adGroupName || row.adGroupId);
1496
- return [...parts.filter(Boolean), row.name || row.entityId].join(" \u203A ");
1497
- }
1498
- function compactEntityRow(row) {
1499
- const childLevel = row.level === "ad" || row.level === "keyword";
1500
- return {
1501
- ...dropEmpty({
1502
- platform: row.platform,
1503
- adAccountId: row.adAccountId,
1504
- level: row.level,
1505
- entityId: row.entityId,
1506
- name: row.name,
1507
- campaignId: row.level === "campaign" ? "" : row.campaignId,
1508
- campaignName: row.level === "campaign" ? "" : row.campaignName,
1509
- adGroupId: childLevel ? row.adGroupId : "",
1510
- adGroupName: childLevel ? row.adGroupName : "",
1511
- status: row.status,
1512
- objective: row.objective,
1513
- bidStrategy: row.bidStrategy,
1514
- headline: row.headline,
1515
- destinationUrl: row.destinationUrl,
1516
- currency: row.currency,
1517
- budgetDaily: row.budgetDaily === null ? null : roundNumber(row.budgetDaily),
1518
- budgetLifetime: row.budgetLifetime === null ? null : roundNumber(row.budgetLifetime)
1519
- }),
1520
- metrics: compactMetrics(row.metrics),
1521
- ratios: compactRatios(row.ratios),
1522
- ...entityPath(row) ? { path: entityPath(row) } : {},
1523
- firstDelivery: row.firstDelivery,
1524
- lastDelivery: row.lastDelivery,
1525
- ...row.beforeHistory ? { beforeHistory: true } : {},
1526
- peer: roundDeep(row.peer),
1527
- ...compactSearchParts(row)
1528
- };
1529
- }
1530
- function compactSearchParts(row) {
1531
- return {
1532
- ...dropEmpty({ campaignKind: row.campaignKind, segment: row.segment }),
1533
- ...row.segmentMixed ? { segmentMixed: true } : {},
1534
- ...row.comparison ? { change: compactChangeOf(row.comparison) } : {},
1535
- ...row.stop ? { stopped: compactStop(row.stop) } : {}
1536
- };
1537
- }
1538
- function compactStop(stop) {
1539
- const by = stop.actorKind === "baker" ? `Baker (${stop.chatTitle || "a chat"})` : stop.actor || stop.actorKind;
1540
- return dropEmpty({
1541
- at: new Date(stop.changedAt).toISOString(),
1542
- how: stop.kind === "removed" ? "removed" : stop.newValue,
1543
- by
1544
- });
1545
- }
1546
- function compactChangeOf(comparison) {
1547
- const out = { previousSpend: roundNumber(comparison.previous.spend) };
1548
- for (const [key, value] of Object.entries(comparison.deltas)) if (value !== null) out[key] = roundNumber(value);
1549
- return out;
1550
- }
1551
- function compactChange(row) {
1552
- const words = adChangeWords(row);
1553
- const raw = words.full === null ? { oldValue: row.oldValue, newValue: row.newValue } : {};
1554
- return dropEmpty({
1555
- changedAt: new Date(row.changedAt).toISOString(),
1556
- platform: row.platform,
1557
- level: row.level,
1558
- entityId: row.entityId,
1559
- entityName: row.entityName,
1560
- campaignName: row.campaignName,
1561
- kind: row.kind,
1562
- field: row.field,
1563
- summary: words.summary,
1564
- fullText: words.full ?? "",
1565
- ...raw,
1566
- valueCurrency: row.valueCurrency,
1567
- actor: row.actor,
1568
- actorKind: row.actorKind,
1569
- source: row.source,
1570
- chatId: row.chatId,
1571
- chatTitle: row.chatTitle,
1572
- platformCheck: row.platformCheck
1573
- });
1574
- }
1575
- function compactAlert(alert) {
1576
- return dropEmpty({
1577
- severity: alert.severity,
1578
- platform: alert.platform,
1579
- level: alert.level,
1580
- entityId: alert.entityId,
1581
- entityName: alert.entityName,
1582
- title: alert.title,
1583
- message: alert.message,
1584
- action: alert.action,
1585
- moneyAtStake: alert.moneyAtStake === null ? null : roundNumber(alert.moneyAtStake),
1586
- currency: alert.moneyAtStake === null ? "" : alert.currency,
1587
- annotation: alert.annotation
1588
- });
1589
- }
1590
- function compactDataStatus(status) {
1591
- return {
1592
- historyLoading: status.historyLoading,
1593
- platforms: status.platforms.map(
1594
- (platform) => dropEmpty({
1595
- platform: platform.platform,
1596
- connected: platform.connected,
1597
- syncedAt: isoOrNull(platform.lastSuccessAt),
1598
- error: platform.error,
1599
- accounts: platform.accounts.filter((account) => account.selected).map(
1600
- (account) => dropEmpty({
1601
- adAccountId: account.adAccountId,
1602
- name: account.name,
1603
- currency: account.currency,
1604
- historyFrom: account.historyFrom,
1605
- newestDate: account.newestDate,
1606
- historyComplete: account.backfillComplete,
1607
- error: account.error,
1608
- adsInManyAdGroups: account.adsInManyAdGroups
1609
- })
1610
- )
1611
- })
1612
- )
1613
- };
1614
- }
1615
- function compactOverview(data) {
1616
- return {
1617
- window: data.window,
1618
- platforms: data.platforms.map((score) => ({
1619
- platform: score.platform,
1620
- currency: score.currency,
1621
- activeCampaigns: score.activeCampaigns,
1622
- matureThrough: score.matureThrough,
1623
- metrics: compactMetrics(score.metrics),
1624
- ratios: compactRatios(score.ratios),
1625
- comparison: score.comparison === null ? null : {
1626
- current: score.comparison.current,
1627
- previous: score.comparison.previous,
1628
- deltas: roundDeep(score.comparison.deltas)
1629
- }
1630
- })),
1631
- totalsByCurrency: data.totalsByCurrency.map((total) => ({
1632
- currency: total.currency,
1633
- metrics: compactMetrics(total.metrics),
1634
- ratios: compactRatios(total.ratios)
1635
- })),
1636
- trend: data.trend.map((point) => ({
1637
- bucket: point.bucket,
1638
- platform: point.platform,
1639
- currency: point.currency,
1640
- spend: roundNumber(point.metrics.spend),
1641
- results: roundNumber(point.metrics.results),
1642
- costPerResult: point.ratios.costPerResult === null ? null : roundNumber(point.ratios.costPerResult),
1643
- mature: point.mature
1644
- })),
1645
- goals: data.goals.map((goal) => ({ platform: goal.platform, actions: goal.actions, countValue: goal.countValue })),
1646
- resultNoun: { singular: data.resultNoun.singular, plural: data.resultNoun.plural },
1647
- openAlerts: data.openAlerts,
1648
- topAlerts: data.topAlerts.map(compactAlert),
1649
- dataStatus: compactDataStatus(data.dataStatus)
1650
- };
1651
- }
1652
- function compactEntities(data) {
1653
- return {
1654
- window: data.window,
1655
- level: data.level,
1656
- totalRows: data.totalRows,
1657
- spentRows: data.spentRows,
1658
- stoppedRows: data.stoppedRows,
1659
- rows: data.rows.map(compactEntityRow)
1660
- };
1661
- }
1662
- function compactEntity(data) {
1663
- return {
1664
- window: data.window,
1665
- matureThrough: data.matureThrough,
1666
- verdict: roundDeep(data.verdict),
1667
- entity: data.entity === null ? null : compactEntityRow(data.entity),
1668
- daily: data.daily.map((day) => ({
1669
- date: day.date,
1670
- spend: roundNumber(day.metrics.spend),
1671
- impressions: day.metrics.impressions,
1672
- linkClicks: day.metrics.linkClicks,
1673
- results: roundNumber(day.metrics.results),
1674
- costPerResult: day.ratios.costPerResult === null ? null : roundNumber(day.ratios.costPerResult),
1675
- mature: day.mature
1676
- })),
1677
- changes: data.changes.map(compactChange),
1678
- children: data.children === null ? null : {
1679
- level: data.children.level,
1680
- totalRows: data.children.totalRows,
1681
- rows: data.children.rows.map(compactEntityRow)
1682
- },
1683
- openAlerts: data.openAlerts.map(compactAlert)
1684
- };
1685
- }
1686
- function compactChanges(data) {
1687
- return { window: data.window, totalRows: data.totalRows, changes: data.changes.map(compactChange) };
1688
- }
1689
- function compactImpactRow(row) {
1690
- return {
1691
- ...compactChange({ ...row, campaignName: "" }),
1692
- currency: row.currency,
1693
- verdict: row.verdict,
1694
- effect: row.effect === null ? null : roundNumber(row.effect),
1695
- basis: row.basis,
1696
- reason: row.reason,
1697
- ...row.confounded ? { confounded: true } : {},
1698
- beforeDays: row.beforeDays,
1699
- afterDays: row.afterDays,
1700
- before: roundDeep(row.before),
1701
- after: roundDeep(row.after)
1702
- };
1703
- }
1704
- function compactImpact(data) {
1705
- return {
1706
- window: data.window,
1707
- impactDays: data.impactDays,
1708
- totalRows: data.totalRows,
1709
- changes: data.changes.map(compactImpactRow)
1710
- };
1711
- }
1712
- function compactMadeInBaker(made) {
1713
- if (!made) return null;
1714
- return {
1715
- slug: made.slug,
1716
- title: made.title,
1717
- ...made.versionNumber !== null ? { version: made.versionNumber } : {},
1718
- ...made.confidence === "likely" ? { likely: true } : {}
1719
- };
1720
- }
1721
- function compactCreativeRow(row) {
1722
- return {
1723
- ...dropEmpty({
1724
- groupKey: row.groupKey,
1725
- platforms: row.platforms,
1726
- adAccountId: row.adAccountId,
1727
- topAdId: row.topAdId,
1728
- name: row.name,
1729
- headline: row.headline,
1730
- creativeFormat: row.creativeFormat,
1731
- destinationUrl: row.destinationUrl,
1732
- currency: row.currency
1733
- }),
1734
- ads: row.ads,
1735
- activeAds: row.activeAds,
1736
- metrics: compactMetrics(row.metrics),
1737
- ratios: compactRatios(row.ratios),
1738
- peerStanding: row.peerStanding,
1739
- peerIndex: row.peerIndex === null ? null : roundNumber(row.peerIndex),
1740
- peerBasis: row.peerBasis,
1741
- expected: roundNumber(row.peerBasis === "link_clicks" ? row.expectedLinkClicks : row.expectedResults),
1742
- firstDelivery: row.firstDelivery,
1743
- lastDelivery: row.lastDelivery,
1744
- library: compactLibraryLink(row.library),
1745
- madeInBaker: compactMadeInBaker(row.madeInBaker)
1746
- };
1747
- }
1748
- function compactCreatives(data) {
1749
- return { window: data.window, groupBy: data.groupBy, creatives: data.creatives.map(compactCreativeRow) };
1750
- }
1751
- function compactStatus(status) {
1752
- return compactDataStatus(status);
1753
- }
1754
- var PLATFORM_LABEL = {
1755
- meta: "Meta",
1756
- google: "Google Ads",
1757
- linkedin: "LinkedIn",
1758
- x: "X",
1759
- chatgpt: "ChatGPT Ads"
1760
- };
1761
- function label(platform) {
1762
- return PLATFORM_LABEL[platform] ?? platform;
1763
- }
1764
- function historyLoadingHint(status) {
1765
- if (!status.historyLoading) return null;
1766
- const loading = status.platforms.flatMap(
1767
- (platform) => platform.accounts.filter((account) => account.selected && !account.backfillComplete).map(
1768
- (account) => `${account.name || account.adAccountId} (${label(platform.platform)}${account.historyFrom ? `, stored from ${account.historyFrom}` : ""})`
1769
- )
1770
- );
1771
- const which = loading.length > 0 ? loading.join(", ") : "some ad accounts";
1772
- return `History is still loading for ${which}; older periods are incomplete. Say so before comparing them, or read a recent window.`;
1773
- }
1774
- var NO_GOALS_HINT = "No result goals are set, so results are each platform's headline conversions. Run `baker ads performance goals` to see every conversion action each platform reported.";
1775
- function resultNounHint(noun) {
1776
- if (noun.source === "company")
1777
- return `This company calls a result a "${noun.singular}": say "${noun.plural}" and "cost per ${noun.singular}", as its Ads screens do.`;
1778
- if (noun.source === "mixed")
1779
- return 'Some ad accounts in view have no goal and count every conversion, or the goals count different things: say "results", never "leads", for these totals.';
1780
- return null;
1781
- }
1782
- function overviewHints(data) {
1783
- const hints = [];
1784
- const loading = historyLoadingHint(data.dataStatus);
1785
- if (loading !== null) hints.push(loading);
1786
- if (data.goals.length === 0 && data.platforms.length > 0) hints.push(NO_GOALS_HINT);
1787
- const noun = resultNounHint(data.resultNoun);
1788
- if (noun !== null) hints.push(noun);
1789
- const unsettled = data.matureThrough.filter((mature) => mature.date < data.window.to);
1790
- if (unsettled.length > 0) {
1791
- const through = unsettled.map((mature) => `${label(mature.platform)} through ${mature.date}`).join(", ");
1792
- hints.push(
1793
- `Results after these days are still settling and will rise: ${through}. Compare only settled days (comparison already does); never judge the last days against older ones.`
1794
- );
1795
- }
1796
- if (data.totalsByCurrency.length > 1) {
1797
- hints.push("Spend is in more than one currency. Report each currency on its own line; never add them up.");
1798
- }
1799
- return hints;
1800
- }
1801
- function settlingHint(window, platforms, matureThrough) {
1802
- const notes = [];
1803
- const present = new Set(platforms);
1804
- for (const mature of matureThrough) {
1805
- if (present.has(mature.platform) && window.to > mature.date)
1806
- notes.push(`${label(mature.platform)} after ${mature.date}`);
1807
- }
1808
- if (notes.length === 0) return null;
1809
- return `Results for the window's last days are still settling (${notes.join(", ")}), so cost per result there reads high. Compare only settled days.`;
1810
- }
1811
- function entitiesHints(data, request) {
1812
- const hints = [];
1813
- const settling = settlingHint(
1814
- data.window,
1815
- data.rows.map((row) => row.platform),
1816
- data.matureThrough
1817
- );
1818
- if (settling !== null) hints.push(settling);
1819
- const shown = request.offset + data.rows.length;
1820
- if (data.totalRows > shown) {
1821
- hints.push(
1822
- `Showing ${request.offset + 1}-${shown} of ${data.totalRows}. Read the next page with --offset ${shown}.`
1823
- );
1824
- }
1825
- if (new Set(data.rows.map((row) => row.currency)).size > 1) {
1826
- hints.push("Rows are in more than one currency. Never add or rank money across currencies.");
1827
- }
1828
- const levels = new Set(data.rows.map((row) => row.level));
1829
- if (levels.has("campaign"))
1830
- hints.push(
1831
- "To read what is inside one campaign: --campaign-ids <entityId> --level ad_group; inside one ad set: --ad-group-ids <entityId> --level ad. Say where a row sits by its path."
1832
- );
1833
- else if (levels.has("ad_group"))
1834
- hints.push("To read the ads inside one ad set: --ad-group-ids <entityId> --level ad.");
1835
- return hints;
1836
- }
1837
- function entityHints(data, request) {
1838
- if (data.entity === null) {
1839
- return [
1840
- `No ${request.level} with id ${request.entityId} was found on that platform. Check the id, platform and level with \`baker ads performance entities --level ${request.level} --include-idle\`.`
1841
- ];
1842
- }
1843
- const verdict = data.verdict;
1844
- if (verdict === null) return [];
1845
- if (!verdict.applies) return verdict.summary ? [verdict.summary] : [];
1846
- const hints = [];
1847
- if (!verdict.enoughData) {
1848
- hints.push(
1849
- `Not enough settled data to judge it yet (${verdict.enoughDataReason}). Wait before changing it; any change now is a guess.`
1850
- );
1851
- }
1852
- if (verdict.learning) hints.push(`It is still learning (${verdict.learningReason}). A change now restarts learning.`);
1853
- const step = verdict.safeBudgetStep;
1854
- if (step.waitDays > 0) {
1855
- hints.push(`Wait ${step.waitDays} more day(s) before changing its budget: ${step.reason}`);
1856
- } else if (step.suggestedDailyBudget !== null) {
1857
- hints.push(
1858
- `A budget step up to ${step.maxIncreasePct}% (about ${roundNumber(step.suggestedDailyBudget)} ${data.entity.currency} a day) should not reset learning.`
1859
- );
1860
- }
1861
- return hints;
1862
- }
1863
- function impactHints(data) {
1864
- const hints = [];
1865
- const tooEarly = data.changes.filter((row) => row.verdict === "too_early");
1866
- if (tooEarly.length > 0) {
1867
- hints.push(
1868
- `${tooEarly.length} change(s) are too early to judge: their days after the change have not settled yet (each row's \`reason\` says when to look again). Do not call them a win or a loss yet.`
1869
- );
1870
- }
1871
- if (data.changes.some((row) => row.confounded)) {
1872
- hints.push(
1873
- "Changes marked confounded overlap another change on the same entity; their effect cannot be told apart."
1874
- );
1875
- }
1876
- return hints;
1877
- }
1878
- function creativesHints(data) {
1879
- const hints = [];
1880
- const settling = settlingHint(
1881
- data.window,
1882
- data.creatives.flatMap((row) => row.platforms),
1883
- data.matureThrough
1884
- );
1885
- if (settling !== null) hints.push(settling);
1886
- if (data.creatives.some((row) => row.peerStanding !== "too_little_data")) {
1887
- hints.push(
1888
- "peerIndex compares each creative with its ad-group siblings (1 = like them, 1.3 = 30% better), shrunk toward 1 when the data is thin. Rank by it, not by raw cost per result."
1889
- );
1890
- }
1891
- return hints;
1892
- }
1893
- var TRACKING_FIX = {
1894
- meta: '`baker ads meta ads retag <ad-id> --set "\u2026"` (see ads-meta.md)',
1895
- google: '`baker ads google campaigns update <campaign-id> --customer-id <id> --final-url-suffix "\u2026"` (read `baker ads google url-options` first)',
1896
- linkedin: '`baker ads linkedin account url-params --param "\u2026"`',
1897
- x: null,
1898
- chatgpt: null
1899
- };
1900
- var LOW_CAMPAIGN_ID_SHARE = 0.8;
1901
- var MIN_PAID_SESSIONS = 20;
1902
- function clippedWindowHint(data) {
1903
- return data.windowClipped ? [
1904
- `Visits are kept for 400 days, so this covers ${data.window.from} to ${data.window.to} (${data.window.days} days), not the longer window asked for.`
1905
- ] : [];
1906
- }
1907
- function trackingHints(data) {
1908
- const hints = [...clippedWindowHint(data)];
1909
- for (const row of data.rows) {
1910
- const untagged = row.activeAdsWithoutParams > 0;
1911
- const share2 = row.campaignIdShare;
1912
- const lowShare = share2 !== null && row.paidSessions >= MIN_PAID_SESSIONS && share2 < LOW_CAMPAIGN_ID_SHARE;
1913
- if (!untagged && !lowShare) continue;
1914
- const problem = untagged ? `${row.activeAdsWithoutParams} of the ${row.activeAds} ${label(row.platform)} ads that spent in this window send no tracking parameters` : `only ${Math.round((share2 ?? 0) * 100)}% of ${label(row.platform)} paid visits carry a campaign id`;
1915
- const fix = TRACKING_FIX[row.platform];
1916
- hints.push(
1917
- fix ? `${problem}. Stage the fix with ${fix}; it applies when the chat is published.` : `${problem}. Baker cannot set them on ${label(row.platform)} yet; the user adds them in the platform's ads manager.`
1918
- );
1919
- }
1920
- return hints;
1921
- }
1922
- function funnelHints(data) {
1923
- const leaky = data.rows.filter(
1924
- (row) => row.clickToSession !== null && row.clickToSession < 0.6 && row.linkClicks >= 50
1925
- );
1926
- if (leaky.length === 0) return clippedWindowHint(data);
1927
- return [
1928
- ...clippedWindowHint(data),
1929
- `${leaky.length} row(s) turn fewer than 6 in 10 link clicks into visits: clicks are not arriving, or arriving without tracking. Check \`baker ads performance tracking\`.`
1930
- ];
1931
- }
1932
- function goalsHints(data) {
1933
- if (data.goals.length > 0) return [];
1934
- return ["No result goals are set, so every report counts each platform's headline conversions as results."];
1935
- }
1936
- function splitAdsHints(status) {
1937
- return status.platforms.flatMap(
1938
- (platform) => platform.accounts.filter((account) => account.selected && account.adsInManyAdGroups.length > 0).map((account) => {
1939
- const count = account.adsInManyAdGroups.length;
1940
- return `${count} ${label(platform.platform)} ${count === 1 ? "ad" : "ads"} in ${account.name || account.adAccountId} ran in more than one ad group or campaign in the last 30 days (adsInManyAdGroups): Baker adds each one's numbers together under its live ad group. Say so before judging an ad group by those ads.`;
1941
- })
1942
- );
1943
- }
1944
- function statusHints(status) {
1945
- const hints = [];
1946
- const loading = historyLoadingHint(status);
1947
- if (loading !== null) hints.push(loading);
1948
- for (const platform of status.platforms) {
1949
- if (platform.connected && platform.error)
1950
- hints.push(`${label(platform.platform)} last sync failed: ${platform.error}`);
1951
- }
1952
- hints.push(...splitAdsHints(status));
1953
- return hints;
1954
- }
1955
-
1956
- // src/commands/ads/performance/accounts.ts
1957
- function percent(share2) {
1958
- if (share2 === null) return null;
1959
- return `${share2 >= 0 ? "+" : "\u2212"}${Math.round(Math.abs(share2) * 100)}%`;
1960
- }
1961
- function compactAccounts(data) {
1962
- return {
1963
- headline: data.headline,
1964
- window: data.window,
1965
- accounts: data.accounts.map((row) => ({
1966
- platform: row.platform,
1967
- adAccountId: row.adAccountId,
1968
- name: row.name,
1969
- currency: row.currency,
1970
- spend: Math.round(row.spend),
1971
- results: roundNumber(row.results),
1972
- costPerResult: row.costPerResult === null ? null : roundNumber(row.costPerResult),
1973
- ...row.change ? {
1974
- change: {
1975
- spend: percent(row.change.spend),
1976
- results: percent(row.change.results),
1977
- costPerResult: percent(row.change.costPerResult)
1978
- }
1979
- } : {},
1980
- ...row.target ? { target: { value: row.target.value, from: row.target.source } } : {},
1981
- status: row.status,
1982
- ...row.bySearch.length > 0 ? { bySearch: row.bySearch } : {},
1983
- weekly: row.weekly.map((week) => week.costPerResult === null ? null : roundNumber(week.costPerResult))
1984
- })),
1985
- ...data.notSpending.length > 0 ? { notSpending: data.notSpending.map((account) => account.name) } : {}
1986
- };
1987
- }
1988
- function accountsHints(data) {
1989
- const hints = [
1990
- "Tell the person the headline as it is. weekly is cost per result week by week, oldest first, the last week maybe partial."
1991
- ];
1992
- const off = data.accounts.find((row) => row.status === "above_target" || row.status === "below_target");
1993
- if (off)
1994
- hints.push(
1995
- `To see what drives ${off.name}: baker ads performance entities --ad-account-ids ${off.adAccountId} --platforms ${off.platform}, then baker ads performance attention for what to fix.`
1996
- );
1997
- if (data.accounts.some((row) => row.bySearch.length > 0))
1998
- hints.push(
1999
- "bySearch: a Google account judged by kind of search (its Search targets are split): report those verdicts, as the dashboard does."
2000
- );
2001
- return hints;
2002
- }
2003
- var accountsCommand = report({
2004
- name: "accounts",
2005
- schemaName: "accounts",
2006
- schemaDescription: `Per ad account, the Summary's accounts table: spend, results and cost per result for the window, change against as many days before, the target that judges it (from: account, platform, segment or company) and its status (on_target, above_target, below_target, not_enough_data, no_target), its cost per result week by week, and the accounts that did not spend. Leads with a headline. Default the last 30 complete days. ${STORE_NOTE}`,
2007
- help: "Each ad account against its target. Example: baker ads performance accounts --days 30",
2008
- path: "accounts",
2009
- args: SCOPE_ARGS,
2010
- request: adPerformanceAccountsRequestSchema,
2011
- input: scopeInput,
2012
- compact: compactAccounts,
2013
- hints: accountsHints
2014
- });
2015
-
2016
- // src/commands/ads/performance/goalWrites.ts
2017
- import { defineCommand as defineCommand2 } from "citty";
2018
- var text = (raw) => typeof raw === "string" && raw.trim() !== "" ? raw.trim() : void 0;
2019
- function targetSetInput(args, chatId) {
2020
- const cost = optionalNumber(args.cost);
2021
- const roas = optionalNumber(args.roas);
2022
- const platform = text(args.platform);
2023
- return {
2024
- chatId,
2025
- ...args.company === true ? { company: true } : {},
2026
- ...platform ? { platform: enumValue(platform) ?? platform } : {},
2027
- ...text(args.account) ? { adAccountId: text(args.account) } : {},
2028
- ...text(args.event) ? { event: text(args.event) } : {},
2029
- ...roas !== void 0 ? { metric: "roas" } : cost !== void 0 ? { metric: "cost_per_result" } : {},
2030
- value: args.clear === true ? null : roas ?? cost,
2031
- ...text(args.currency) ? { currency: text(args.currency) } : {}
2032
- };
2033
- }
2034
- function goalsSetInput(args, chatId) {
2035
- const platform = text(args.platform);
2036
- return {
2037
- chatId,
2038
- ...platform ? { platform: enumValue(platform) ?? platform } : {},
2039
- ...text(args.account) ? { adAccountId: text(args.account) } : {},
2040
- count: commaList(args.count) ?? [],
2041
- uncount: commaList(args.uncount) ?? [],
2042
- ...args.reset === true ? { reset: true } : {}
2043
- };
2044
- }
2045
- function resultNameInput(args, chatId) {
2046
- return {
2047
- chatId,
2048
- ...text(args.singular) ? { singular: text(args.singular) } : {},
2049
- ...text(args.plural) ? { plural: text(args.plural) } : {},
2050
- ...args.reset === true ? { reset: true } : {}
2051
- };
2052
- }
2053
- var ACCOUNT_ARGS = {
2054
- platform: { type: "string", description: "meta | google | linkedin | x | chatgpt", required: false },
2055
- account: {
2056
- type: "string",
2057
- description: "The ad account id, as baker ads performance targets lists it",
2058
- required: false
2059
- }
2060
- };
2061
- var TARGET_SET_ARGS = {
2062
- ...ACCOUNT_ARGS,
2063
- company: { type: "boolean", description: "The company default instead of one ad account", required: false },
2064
- event: {
2065
- type: "string",
2066
- description: "A conversion action key from baker ads performance targets (events[].event): the target for that event only. Meta and LinkedIn only; elsewhere omit it to set the account's target",
2067
- required: false
2068
- },
2069
- cost: { type: "string", description: "Target cost per result (needs --currency)", required: false },
2070
- currency: { type: "string", description: "The cost's currency, e.g. EUR", required: false },
2071
- roas: { type: "string", description: "Target return on ad spend, as a ratio (3 = 3\xD7)", required: false },
2072
- clear: { type: "boolean", description: "Remove the target", required: false }
2073
- };
2074
- var GOALS_SET_ARGS = {
2075
- ...ACCOUNT_ARGS,
2076
- count: { type: "string", description: "Comma list of conversion action keys to count as results", required: false },
2077
- uncount: { type: "string", description: "Comma list of conversion action keys to stop counting", required: false },
2078
- reset: {
2079
- type: "boolean",
2080
- description: "Drop this account's own choice: it follows what its platform counts for every account",
2081
- required: false
2082
- }
2083
- };
2084
- var RESULT_NAME_ARGS = {
2085
- singular: {
2086
- type: "string",
2087
- description: 'One result, as the person says it: "lead", "demo request"',
2088
- required: false
2089
- },
2090
- plural: { type: "string", description: 'Several: "leads", "demo requests"', required: false },
2091
- reset: {
2092
- type: "boolean",
2093
- description: "Drop the company's own name: Baker names results from what the goals count again",
2094
- required: false
2095
- }
2096
- };
2097
- var UNSTAGE_ARGS = {
2098
- key: { type: "positional", description: "A staged key from baker ads performance targets \u2192 staged", required: false },
2099
- all: { type: "boolean", description: "Take back every staged goal and target on this chat", required: false }
2100
- };
2101
- function writeCommand(spec) {
2102
- registerSchema({
2103
- command: `ads.performance.${spec.schemaName}`,
2104
- description: spec.schemaDescription,
2105
- args: spec.args
2106
- });
2107
- return defineCommand2({
2108
- meta: { name: spec.name, description: spec.help },
2109
- args: spec.args,
2110
- run: async ({ args }) => {
2111
- try {
2112
- const parsed = spec.request.safeParse(spec.input(args, requireChatId()));
2113
- if (!parsed.success) {
2114
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
2115
- process.exit(1);
2116
- }
2117
- writeJson(await apiPost(spec.path, parsed.data));
2118
- } catch (err) {
2119
- failApi(err);
2120
- }
2121
- }
2122
- });
2123
- }
2124
- var targetsSetCommand = writeCommand({
2125
- name: "set",
2126
- schemaName: "targets.set",
2127
- schemaDescription: "Stage one target on this chat; it applies when the person publishes the chat, never before. The target of one event at an ad account (--platform --account --event, Meta and LinkedIn only), of an ad account's whole result (--platform --account), or the company default (--company). --cost 25 --currency EUR judges cost per result; --roas 3 judges return on ad spend; --clear removes it. Restaging the same target replaces it. Publish leaves a target a person changed in the dashboard meanwhile as they set it and reports yours as not applied. Ask the person before staging: a target is what the business can afford, and every alert and budget recommendation judges against it.",
2128
- help: "Stage a target; it applies when the chat is published.\n\nExamples:\n baker ads performance targets set --platform meta --account 123 --event offsite_conversion.fb_pixel_lead --cost 25 --currency EUR\n baker ads performance targets set --platform google --account 456 --roas 4\n baker ads performance targets set --company --cost 30 --currency EUR\n baker ads performance targets set --platform meta --account 123 --clear",
2129
- path: "/api/ads/performance/targets/set",
2130
- args: TARGET_SET_ARGS,
2131
- request: adTargetStageRequestSchema,
2132
- input: targetSetInput
2133
- });
2134
- var goalsSetCommand = writeCommand({
2135
- name: "set",
2136
- schemaName: "goals.set",
2137
- schemaDescription: "Stage what counts as a result at one ad account (--platform --account): --count adds conversion action keys, --uncount removes them, --reset drops the account's own choice so it follows its platform's. Applies when the person publishes the chat, and then re-scores the account's whole history; a choice a person changed meanwhile stays as they set it. Use the action keys baker ads performance targets (events[].event, available[].event) or baker ads performance goals lists.",
2138
- help: "Stage what counts as a result at an ad account.\n\nExamples:\n baker ads performance goals set --platform meta --account 123 --count offsite_conversion.fb_pixel_lead\n baker ads performance goals set --platform google --account 456 --uncount Calls\n baker ads performance goals set --platform meta --account 123 --reset",
2139
- path: "/api/ads/performance/goals/set",
2140
- args: GOALS_SET_ARGS,
2141
- request: adResultsStageRequestSchema,
2142
- input: goalsSetInput
2143
- });
2144
- var goalsNameCommand = writeCommand({
2145
- name: "name",
2146
- schemaName: "goals.name",
2147
- schemaDescription: 'Stage what the company calls one result, for every ad account: --singular "demo request" --plural "demo requests" (both), or --reset to name results from what the goals count again. Applies when the person publishes the chat; then every Ads screen, the Measure overview and baker ads performance overview (resultNoun) use it. Only when the person says what they call a result ("we call them sign-ups"); never to relabel a count that mixes kinds.',
2148
- help: 'Stage what the company calls one result.\n\nExamples:\n baker ads performance goals name --singular "demo request" --plural "demo requests"\n baker ads performance goals name --reset',
2149
- path: "/api/ads/performance/goals/name",
2150
- args: RESULT_NAME_ARGS,
2151
- request: adResultNameStageRequestSchema,
2152
- input: resultNameInput
2153
- });
2154
- var targetsUnstageCommand = writeCommand({
2155
- name: "unstage",
2156
- schemaName: "targets.unstage",
2157
- schemaDescription: "Take back a goal or target this chat staged, before publish (a key from baker ads performance targets \u2192 staged, or --all). Never touches what is already in force.",
2158
- help: "Take back a staged goal or target.\n\nExamples:\n baker ads performance targets unstage target:meta:123:offsite_conversion.fb_pixel_lead\n baker ads performance targets unstage --all",
2159
- path: "/api/ads/performance/targets/unstage",
2160
- args: UNSTAGE_ARGS,
2161
- request: adGoalUnstageRequestSchema,
2162
- input: (args, chatId) => ({
2163
- chatId,
2164
- ...text(args.key) ? { key: text(args.key) } : {},
2165
- ...args.all === true ? { all: true } : {}
2166
- })
2167
- });
2168
- function withWrites(read, writes) {
2169
- return defineCommand2({
2170
- meta: read.meta,
2171
- args: read.args,
2172
- subCommands: { show: read, ...writes },
2173
- default: "show"
2174
- });
2175
- }
2176
-
2177
- // src/commands/ads/performance/targets.ts
2178
- import { defineCommand as defineCommand3 } from "citty";
2179
- var HOW_TO_CHANGE = "To change a target, ask the person what the business can afford, then stage it: baker ads performance targets set (per event with --event on Meta and LinkedIn, per ad account, or --company). What counts as a result: baker ads performance goals set. Both apply when the person publishes this chat. Alert lines are set by a person in Measure \u2192 Ads \u2192 Goals.";
2180
- var round = (value) => value === null ? null : Math.round(value * 100) / 100;
2181
- function compactNumbers(row) {
2182
- return {
2183
- spend: round(row.spend),
2184
- results: round(row.results),
2185
- ...row.target?.metric === "roas" ? { roas: round(row.roas) } : { costPerResult: round(row.costPerResult) },
2186
- target: row.target ? { value: round(row.target.value), metric: row.target.metric, from: row.target.source } : null,
2187
- status: row.status
2188
- };
2189
- }
2190
- function idle(account) {
2191
- return (account.total.spend ?? 0) === 0 && account.events.length === 0;
2192
- }
2193
- function standing(account) {
2194
- const statuses = account.searchSplit.length > 0 ? account.searchSplit.map((e) => e.status) : [account.total.status];
2195
- if (statuses.includes("above_target")) return "above_target";
2196
- if (statuses.includes("below_target")) return "below_target";
2197
- return statuses.includes("on_target") ? "on_target" : "not_judged";
2198
- }
2199
- var STANDING_WORDS = {
2200
- on_target: "on target",
2201
- above_target: "above target",
2202
- below_target: "below target",
2203
- not_judged: "not judged yet"
2204
- };
2205
- function targetsHeadline(goals) {
2206
- const spending = goals.accounts.filter((account) => !idle(account));
2207
- const quiet = goals.accounts.length - spending.length;
2208
- const counts = /* @__PURE__ */ new Map();
2209
- for (const account of spending) counts.set(standing(account), (counts.get(standing(account)) ?? 0) + 1);
2210
- const parts = Object.keys(STANDING_WORDS).filter((key) => counts.has(key)).map((key) => `${counts.get(key)} ${STANDING_WORDS[key]}`);
2211
- const lead = `${spending.length} ad account${spending.length === 1 ? "" : "s"} spent in the last ${goals.window.days} days`;
2212
- return `${lead}${parts.length > 0 ? `: ${parts.join(", ")}` : ""}.${quiet > 0 ? ` ${quiet} did not spend.` : ""}`;
2213
- }
2214
- function compactGoals(goals) {
2215
- const quiet = goals.accounts.filter(idle);
2216
- return {
2217
- window: goals.window,
2218
- ...quiet.length > 0 ? {
2219
- notSpending: quiet.map((account) => ({
2220
- platform: account.platform,
2221
- adAccountId: account.adAccountId,
2222
- name: account.name,
2223
- ...account.total.target ? { target: compactNumbers(account.total).target } : {}
2224
- }))
2225
- } : {},
2226
- accounts: goals.accounts.filter((account) => !idle(account)).map((account) => ({
2227
- platform: account.platform,
2228
- adAccountId: account.adAccountId,
2229
- name: account.name,
2230
- currency: account.currency,
2231
- countsAsResult: account.results.actions.length > 0 ? account.results.actions : "platform conversions",
2232
- ...account.results.source === "account" ? {} : { resultChoice: account.results.source },
2233
- total: compactNumbers(account.total),
2234
- // Split Search targets judge the account, as the dashboard does: read these, not `total.status`.
2235
- ...account.searchSplit.length > 0 ? {
2236
- judgedBy: account.searchSplit.map((entry) => ({
2237
- search: entry.segment,
2238
- target: round(entry.value),
2239
- actual: round(entry.actual),
2240
- status: entry.status
2241
- }))
2242
- } : {},
2243
- ...account.split ? {} : { note: account.note },
2244
- events: account.events.map((event) => ({
2245
- event: event.event,
2246
- label: event.label,
2247
- counted: event.counted,
2248
- ...event.optimisedBy ? { optimisedBy: event.optimisedBy } : {},
2249
- ...event.spend === null ? { results: round(event.results), judgedOn: "account" } : compactNumbers(event)
2250
- })),
2251
- ...account.mismatch ? { mismatch: account.mismatch } : {},
2252
- ...account.available.length > 0 ? { available: account.available.slice(0, 10).map((entry) => entry.event) } : {}
2253
- }))
2254
- };
2255
- }
2256
- function compactTargets(data) {
2257
- return {
2258
- headline: targetsHeadline(data.goals),
2259
- companyDefault: data.targets.target ? { metric: data.targets.focus, ...data.targets.target } : null,
2260
- ...data.targets.platformTargets.length > 0 ? { platformTargets: data.targets.platformTargets } : {},
2261
- ...data.targets.resultName ? { resultName: data.targets.resultName } : {},
2262
- goals: compactGoals(data.goals),
2263
- ...data.staged.length > 0 ? { staged: data.staged } : {},
2264
- alerts: data.targets.alerts,
2265
- currencies: data.currencies,
2266
- optimisation: data.focus.platforms.map((platform) => ({
2267
- platform: platform.platform,
2268
- groups: platform.groups.map((group) => ({
2269
- optimisesFor: group.label,
2270
- conversion: group.conversion,
2271
- entities: group.entities,
2272
- spend: roundDeep(group.spend),
2273
- ...group.countedByGoal === null ? {} : { countedByResult: group.countedByGoal }
2274
- })),
2275
- result: platform.goalActions.length > 0 ? platform.goalActions : "platform conversions",
2276
- ...platform.suggestion ? { suggestedResult: platform.suggestion.actions } : {},
2277
- ...platform.mismatch ? { mismatch: platform.mismatch.message } : {}
2278
- })),
2279
- ...accountMismatches(data).length > 0 ? { accountMismatches: accountMismatches(data) } : {}
2280
- };
2281
- }
2282
- function accountMismatches(data) {
2283
- return (data.focus.accounts ?? []).flatMap(
2284
- (account) => account.mismatch ? [{ platform: account.platform, adAccountId: account.adAccountId, mismatch: account.mismatch.message }] : []
2285
- );
2286
- }
2287
- function targetsHints(data) {
2288
- const hints = [HOW_TO_CHANGE];
2289
- if (!data.targets.target && data.targets.platformTargets.length === 0 && data.targets.goalTargets.length === 0)
2290
- hints.push(
2291
- "No target is set, so there are no budget recommendations. Suggest one per event from what each account achieves (goals.accounts[].events) and what the business can afford."
2292
- );
2293
- if (data.staged.length > 0)
2294
- hints.push("staged lists what this chat changed: it is not in force until the person publishes the chat.");
2295
- if (data.goals.accounts.some((account) => !account.split))
2296
- hints.push(
2297
- "Where an account has a note, its platform cannot split spend by event: judge and target the account as a whole."
2298
- );
2299
- if (data.focus.platforms.some((platform) => platform.mismatch))
2300
- hints.push(
2301
- "A mismatch means cost per result there counts something other than what the platform optimises for: say so before judging those numbers."
2302
- );
2303
- if (data.focus.platforms.some((platform) => platform.suggestion))
2304
- hints.push(
2305
- "suggestedResult is a suggestion: stage it with baker ads performance goals set only after the person agrees."
2306
- );
2307
- hints.push(
2308
- "Read as the Goals page does: targets first (what a result may cost, and whether each account is on it), then the monthly budget with baker ads performance budgets (what each may spend, and where it is heading)."
2309
- );
2310
- return hints;
2311
- }
2312
- var LEVEL_COMMAND = {
2313
- meta: { campaign: "campaigns", ad_group: "adsets" },
2314
- google: { campaign: "campaigns", ad_group: "ad-groups" }
2315
- };
2316
- function stageCommand(row) {
2317
- const level = LEVEL_COMMAND[row.platform]?.[row.level];
2318
- if (!level || row.waitUntil) return null;
2319
- const where = row.platform === "google" ? `--customer-id ${row.adAccountId}` : `--account-id ${row.adAccountId}`;
2320
- if (row.move === "pause") return `baker ads ${row.platform} ${level} pause ${row.entityId} ${where}`;
2321
- if (row.move !== "scale" && row.move !== "cut" || row.budget.suggestedDaily === null) return null;
2322
- const amount = Math.round(row.budget.suggestedDaily * 100) / 100;
2323
- if (row.platform === "google")
2324
- return row.level === "campaign" ? `baker ads google budgets update <its budget id, from baker ads google campaigns> ${where} --amount ${amount}` : null;
2325
- return `baker ads meta ${level} update ${row.entityId} ${where} --daily-budget ${amount}`;
2326
- }
2327
- function compactRecommendations(data) {
2328
- if (!data.hasTarget) return { hasTarget: false };
2329
- return {
2330
- focus: data.focus,
2331
- counts: data.counts,
2332
- rows: data.rows.map((row) => ({
2333
- move: row.move,
2334
- platform: row.platform,
2335
- level: row.level,
2336
- entityId: row.entityId,
2337
- name: row.name,
2338
- ...row.level === "ad_group" ? { campaignName: row.campaignName } : {},
2339
- currency: row.currency,
2340
- reason: row.reason,
2341
- confidence: row.confidence,
2342
- ...row.metric === data.focus ? {} : { judgedOn: row.metric },
2343
- last7: roundDeep({ spend: row.last7.spend, results: row.last7.results, metric: row.last7.metric }),
2344
- last3: roundDeep({ spend: row.last3.spend, results: row.last3.results, metric: row.last3.metric }),
2345
- target: row.target,
2346
- targetFrom: row.targetSegment ?? row.targetSource,
2347
- ...row.budget.suggestedDaily === null ? {} : { budget: roundDeep(row.budget) },
2348
- ...row.alert ? { alertId: row.alert.id } : {},
2349
- ...row.alert?.claimedByChatId ? { claimedBy: "a chat" } : {},
2350
- ...row.alert?.fixing ? { fixed: "checking it worked" } : {},
2351
- ...stageCommand(row) ? { stage: stageCommand(row) } : {}
2352
- })),
2353
- ...data.untargeted.length > 0 ? { untargeted: data.untargeted } : {}
2354
- };
2355
- }
2356
- function recommendationsHints(data) {
2357
- if (!data.hasTarget)
2358
- return [
2359
- "No target is set. Ask the person what one result is worth to them, then stage it with baker ads performance targets set (or they set it in Measure \u2192 Ads \u2192 Goals)."
2360
- ];
2361
- const hints = [
2362
- "Windows are each platform's last 7 and 3 settled days. A recommendation is advice: apply a budget change only when the person agrees, by staging it with baker ads <platform>, one step at a time (budget.changePct), then wait budget.waitDays."
2363
- ];
2364
- if (data.counts.scale + data.counts.cut + data.counts.pause > 0)
2365
- hints.push("Before acting on one, read it: baker ads performance entity <id> --platform <p> --level <level>.");
2366
- if (data.rows.some((row) => row.alert))
2367
- hints.push(
2368
- 'alertId: the move is also an open alert in What needs attention. Claim it (baker ads performance attention claim <alertId>), stage the `stage` command once the person agrees, then baker ads performance attention fixed <alertId> --note "<what you changed>". If they decline it, attention dismiss <alertId> --reason "<why>".'
2369
- );
2370
- if (data.untargeted.length > 0)
2371
- hints.push(
2372
- `Not judged: campaigns in ${data.untargeted.map((entry) => entry.currency).join(", ")} have no target that applies (none set for their account, platform or company, or no exchange rate to compare).`
2373
- );
2374
- return hints;
2375
- }
2376
- var TARGETS_ARGS = {
2377
- days: SCOPE_ARGS.days,
2378
- start: SCOPE_ARGS.start,
2379
- end: SCOPE_ARGS.end,
2380
- platforms: SCOPE_ARGS.platforms,
2381
- currency: SCOPE_ARGS.currency,
2382
- full: SCOPE_ARGS.full
2383
- };
2384
- var BUDGET_RECOMMENDATIONS_ARGS = {
2385
- platforms: SCOPE_ARGS.platforms,
2386
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
2387
- currency: SCOPE_ARGS.currency,
2388
- moves: { type: "string", description: "Comma list of scale,hold,cut,pause (default all)", required: false },
2389
- limit: { type: "string", description: "Rows to return, 1-200 (default 50)", required: false },
2390
- full: SCOPE_ARGS.full
2391
- };
2392
- function targetsInput(args) {
2393
- const chatId = optionalChatId();
2394
- return {
2395
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
2396
- days: optionalNumber(args.days),
2397
- start: typeof args.start === "string" && args.start !== "" ? args.start : void 0,
2398
- end: typeof args.end === "string" && args.end !== "" ? args.end : void 0,
2399
- currency: typeof args.currency === "string" && args.currency !== "" ? args.currency : void 0,
2400
- ...chatId ? { chatId } : {}
2401
- };
2402
- }
2403
- function budgetRecommendationsInput(args) {
2404
- return {
2405
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
2406
- adAccountIds: commaList(args["ad-account-ids"]),
2407
- currency: typeof args.currency === "string" && args.currency !== "" ? args.currency : void 0,
2408
- moves: commaList(args.moves)?.map((item) => item.toLowerCase()),
2409
- limit: optionalNumber(args.limit)
2410
- };
2411
- }
2412
- function targetCommand(spec) {
2413
- registerSchema({
2414
- command: `ads.performance.${spec.name}`,
2415
- description: spec.schemaDescription,
2416
- args: spec.args
2417
- });
2418
- return defineCommand3({
2419
- meta: { name: spec.name, description: spec.help },
2420
- args: spec.args,
2421
- run: async ({ args }) => {
2422
- const parsed = spec.request.safeParse(spec.input(args));
2423
- if (!parsed.success) {
2424
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
2425
- process.exit(1);
2426
- }
2427
- try {
2428
- const response = await apiPost(`/api/ads/performance/${spec.name}`, parsed.data);
2429
- const hints = spec.hints(response.data);
2430
- const data = args.full === true ? response.data : spec.compact(response.data);
2431
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
2432
- } catch (err) {
2433
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
2434
- if (err instanceof ApiError && refusal !== null) {
2435
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
2436
- process.exit(1);
2437
- }
2438
- failApi(err);
2439
- }
2440
- }
2441
- });
2442
- }
2443
- var targetsCommand = targetCommand({
2444
- name: "targets",
2445
- schemaDescription: "Start here for targets and what counts as a result. Per ad account (goals.accounts): what counts as a result there, the period's spend, results and cost per result (or return) against the account's target with a status (on_target, above_target, below_target, not_enough_data, no_target), and one row per event its ads optimise for or count \u2014 Meta and LinkedIn cost each event by the spend of the ad groups optimising for it over that event's own count; Google, X and ChatGPT Ads cannot split spend by event (note), so only the account is judged. Targets resolve event at its account \u2192 account \u2192 platform (older) \u2192 company default, each with its own metric (cost_per_result or roas); money targets are converted to compare. Also the alert lines, the currencies a target can be in, what live ad groups optimise for per platform with a suggested result or mismatch, and what this chat staged. Default the last 30 days. Change targets with targets set and results with goals set \u2014 staged until the chat is published.",
2446
- help: "Targets per ad account and event, against the period's numbers. Example: baker ads performance targets --days 30",
2447
- args: TARGETS_ARGS,
2448
- request: adPerformanceTargetsRequestSchema,
2449
- input: targetsInput,
2450
- compact: compactTargets,
2451
- hints: targetsHints
2452
- });
2453
- var budgetRecommendationsCommand = targetCommand({
2454
- name: "budget-recommendations",
2455
- schemaDescription: "Per active campaign or ad group, scale | hold | cut | pause against the company's target, worst first: cost per result (or ROAS) over each platform's last 7 and 3 settled days against the target and the ad account's spend-weighted average, a one-line reason, a confidence, and a budget step of at most \xB120% with the days to wait before the next. Scale only where budget holds it back (Google impression share lost to budget, or \u226590% of the daily budget spent). hasTarget false when no target is set. Read-only: nothing is changed.",
2456
- help: "Scale, hold or cut each campaign against the target. Example: baker ads performance budget-recommendations --moves scale,cut",
2457
- args: BUDGET_RECOMMENDATIONS_ARGS,
2458
- request: adPerformanceBudgetRecommendationsRequestSchema,
2459
- input: budgetRecommendationsInput,
2460
- compact: compactRecommendations,
2461
- hints: recommendationsHints
2462
- });
2463
-
2464
- // src/commands/ads/performance/attention.ts
2465
- function historyOf(item) {
2466
- return {
2467
- ...item.cause ? { why: item.cause } : {},
2468
- ...item.previousCause ? { newReason: { was: item.previousCause } } : {},
2469
- ...item.timesOpened > 1 ? { timesOpened: item.timesOpened } : {},
2470
- ...item.more > 0 ? { moreOnThis: item.more } : {},
2471
- ...item.back && item.back !== "new_reason" ? { back: item.back } : {},
2472
- ...item.handled ? {
2473
- fixed: {
2474
- checkingUntil: new Date(item.handled.checkUntil).toISOString().slice(0, 10),
2475
- ...item.handled.note ? { note: item.handled.note } : {}
2476
- }
2477
- } : {}
2478
- };
2479
- }
2480
- function claimOf(claimedBy, chatId) {
2481
- if (claimedBy === null) return {};
2482
- return { claimedBy: claimedBy === chatId ? "this chat" : "another chat" };
2483
- }
2484
- function compactAttention(data, chatId) {
2485
- return {
2486
- currency: data.currency,
2487
- counts: data.counts,
2488
- ...data.dismissedItems.length > 0 ? {
2489
- dismissedItems: data.dismissedItems.map((item) => ({
2490
- id: item.alertId,
2491
- title: item.title,
2492
- account: item.accountName,
2493
- dismissed: item.dismissal?.kind ?? "not_now",
2494
- ...item.dismissal?.reason ? { reason: item.dismissal.reason } : {}
2495
- }))
2496
- } : {},
2497
- items: data.items.map((item) => ({
2498
- id: item.alertId,
2499
- tier: item.tier,
2500
- rule: item.rule,
2501
- platform: item.platform,
2502
- account: item.accountName,
2503
- title: item.title,
2504
- ...historyOf(item),
2505
- ...claimOf(item.claimedByChatId, chatId),
2506
- ...item.target ? {
2507
- target: {
2508
- value: Math.round(item.target.value * 100) / 100,
2509
- from: item.target.segment ?? item.target.source
2510
- }
2511
- } : {},
2512
- ...item.moneyAtStake === null ? {} : { moneyAtStake: Math.round(item.moneyAtStake) },
2513
- ...item.currency !== data.currency ? { currency: item.currency } : {},
2514
- ...item.action.open === "entity" ? { entity: { level: item.action.level, id: item.action.entityId } } : { see: item.action.section }
2515
- }))
2516
- };
2517
- }
2518
- function attentionHints(data) {
2519
- const hints = [];
2520
- if (data.items.some((item) => item.action.open === "entity"))
2521
- hints.push(
2522
- "Before acting on an item, read it: baker ads performance entity <entity.id> --platform <platform> --level <entity.level>. Any change is staged with baker ads <platform> and applies at Publish."
2523
- );
2524
- if (data.counts.fix_now > 0)
2525
- hints.push("Lead with fix_now items when you tell the person: money is being lost there.");
2526
- if (data.items.some((item) => item.claimedByChatId !== null))
2527
- hints.push(
2528
- "claimedBy another chat: someone is already working on it; leave it. To work on an item yourself: baker ads performance attention claim <id>, then release it if you decide not to act."
2529
- );
2530
- if (data.items.some((item) => item.previousCause !== null || item.timesOpened > 1))
2531
- hints.push(
2532
- "newReason: the same problem is back for a different reason than before, so what fixed or excused it then may not apply. timesOpened > 1: it went away and came back; say so."
2533
- );
2534
- if (data.items.some((item) => item.handled !== null))
2535
- hints.push(
2536
- "fixed: someone already fixed it and Baker is checking the fix worked until checkingUntil \u2014 do not act on it again. It closes on its own, or comes back as back: fix_did_not_hold."
2537
- );
2538
- if (data.items.some((item) => item.back === "fix_did_not_hold"))
2539
- hints.push(
2540
- "back: fix_did_not_hold \u2014 it was marked fixed and the problem is still there once the fix had time to show. Read what was changed (baker ads performance changes) before trying the same fix again."
2541
- );
2542
- if (!data.hasTarget)
2543
- hints.push(
2544
- "No target is set, so nothing is judged against one. Ask the person what a result is worth to them, then stage a target per account or event with baker ads performance targets set."
2545
- );
2546
- if (data.dismissed > 0 && data.dismissedItems.length === 0)
2547
- hints.push(
2548
- `${data.dismissed} more were dismissed by a person (not_now, or wrong with a reason): add --dismissed to read why before raising one of them again.`
2549
- );
2550
- if (data.items.length === 0) hints.push("Nothing needs attention right now: say so in one line.");
2551
- return hints;
2552
- }
2553
- var ATTENTION_ARGS = {
2554
- platforms: SCOPE_ARGS.platforms,
2555
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
2556
- currency: SCOPE_ARGS.currency,
2557
- tiers: {
2558
- type: "string",
2559
- description: "Comma list of fix_now,worth_a_look,opportunity (default all)",
2560
- required: false
2561
- },
2562
- limit: { type: "string", description: "Items to return, 1-100 (default 20)", required: false },
2563
- dismissed: {
2564
- type: "boolean",
2565
- description: "Also list what people dismissed (not_now or wrong) and their reason: read it before raising one again",
2566
- required: false
2567
- },
2568
- full: SCOPE_ARGS.full
2569
- };
2570
- function attentionInput(args) {
2571
- return {
2572
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
2573
- adAccountIds: commaList(args["ad-account-ids"]),
2574
- currency: typeof args.currency === "string" && args.currency !== "" ? args.currency : void 0,
2575
- tiers: commaList(args.tiers)?.map((item) => item.toLowerCase()),
2576
- limit: optionalNumber(args.limit),
2577
- ...args.dismissed === true ? { includeDismissed: true } : {}
2578
- };
2579
- }
2580
- var attentionReadCommand = targetCommand({
2581
- name: "attention",
2582
- schemaDescription: "What needs attention in the ads now, ranked: fix_now (money wasted, delivery or tracking broke), worth_a_look (off target, getting worse, ads not earning their spend), opportunity (a campaign beating its target that its budget holds back); then by money at stake in the reporting currency. Each item: its id, tier, rule, platform, ad account, one plain sentence, why (the cause, when Baker can tell), newReason when it came back for a different reason than before, timesOpened when it recurred, moreOnThis when the same campaign has other problems behind it, money at stake, and the campaign/ad to read next (entity) or the section to see. Every item is a stored alert, judged again after each sync and within seconds of a target change: the rule alerts, budget moves against each ad account's target, accounts off target or worse than their 4 weeks before, ads not earning their spend, and accounts that stopped. One item per campaign, ad set, ad or account. Read-only.",
2583
- help: "The few things that matter most right now. Example: baker ads performance attention --tiers fix_now",
2584
- args: ATTENTION_ARGS,
2585
- request: adPerformanceAttentionRequestSchema,
2586
- input: attentionInput,
2587
- compact: (data) => compactAttention(data, optionalChatId()),
2588
- hints: attentionHints
2589
- });
2590
- function alertClaimInput(args, chatId) {
2591
- return { chatId, alertId: typeof args.id === "string" ? args.id.trim() : "" };
2592
- }
2593
- var CLAIM_ARGS = {
2594
- id: { type: "positional", description: "The item's id, from baker ads performance attention", required: true }
2595
- };
2596
- var attentionClaimCommand = writeCommand({
2597
- name: "claim",
2598
- schemaName: "attention.claim",
2599
- schemaDescription: "Say this chat is working on an alert: the dashboard shows an agent is on it, and no other chat takes it. Applies at once (it is coordination, not a change); it lapses when this chat ends. Refused while another chat works on it. Claim before you start on an item, not to read it.",
2600
- help: "Work on an alert.\n\nExample:\n baker ads performance attention claim k57abc",
2601
- path: "/api/ads/performance/alerts/claim",
2602
- args: CLAIM_ARGS,
2603
- request: adAlertClaimRequestSchema,
2604
- input: alertClaimInput
2605
- });
2606
- var attentionReleaseCommand = writeCommand({
2607
- name: "release",
2608
- schemaName: "attention.release",
2609
- schemaDescription: "Stop working on an alert this chat claimed, so another chat or a person can take it: when you decide not to act, or hand it back. Another chat's claim is left alone.",
2610
- help: "Let go of an alert.\n\nExample:\n baker ads performance attention release k57abc",
2611
- path: "/api/ads/performance/alerts/release",
2612
- args: CLAIM_ARGS,
2613
- request: adAlertClaimRequestSchema,
2614
- input: alertClaimInput
2615
- });
2616
- function alertFixedInput(args, chatId) {
2617
- return {
2618
- chatId,
2619
- alertId: typeof args.id === "string" ? args.id.trim() : "",
2620
- verdict: "fixed",
2621
- ...typeof args.note === "string" && args.note.trim() !== "" ? { note: args.note } : {}
2622
- };
2623
- }
2624
- function alertDismissInput(args, chatId) {
2625
- return {
2626
- chatId,
2627
- alertId: typeof args.id === "string" ? args.id.trim() : "",
2628
- verdict: args.wrong === true ? "wrong" : "not_now",
2629
- ...typeof args.reason === "string" && args.reason.trim() !== "" ? { note: args.reason } : {}
2630
- };
2631
- }
2632
- var attentionFixedCommand = writeCommand({
2633
- name: "fixed",
2634
- schemaName: "attention.fixed",
2635
- schemaDescription: 'Mark an alert fixed once you staged the fix (a budget, bid or status change with baker ads <platform>), with a note of what you changed. Staged on this chat and applied at Publish, with the fix itself. Afterwards the alert shows "Fixed, checking it worked" until the fix had time to show in the results, then closes as fixed or comes back as fix_did_not_hold.',
2636
- help: 'Mark an alert fixed.\n\nExample:\n baker ads performance attention fixed k57abc --note "Cut the daily budget from \u20AC80 to \u20AC64"',
2637
- path: "/api/ads/performance/alerts/verdict",
2638
- args: {
2639
- ...CLAIM_ARGS,
2640
- note: { type: "string", description: "What you changed, in one line the person reads", required: false }
2641
- },
2642
- request: adAlertVerdictRequestSchema,
2643
- input: alertFixedInput
2644
- });
2645
- var attentionDismissCommand = writeCommand({
2646
- name: "dismiss",
2647
- schemaName: "attention.dismiss",
2648
- schemaDescription: 'Dismiss an alert that needs no fix, staged on this chat and applied at Publish. Default "not now": true but not worth acting on yet; it comes back when it gets worse, the item is edited, its target moves, or for a new reason. --wrong: it is not a real problem (the person says it is false, or it is on purpose); needs --reason, stays hidden unless it turns critical, and the reason is what the rules are tuned from.',
2649
- help: 'Dismiss an alert.\n\nExamples:\n baker ads performance attention dismiss k57abc --reason "Waiting for the new creatives"\n baker ads performance attention dismiss k57abc --wrong --reason "Paused on purpose for the holidays"',
2650
- path: "/api/ads/performance/alerts/verdict",
2651
- args: {
2652
- ...CLAIM_ARGS,
2653
- wrong: { type: "boolean", description: "It is not a real problem (needs --reason)", required: false },
2654
- reason: { type: "string", description: "Why: optional for not now, required with --wrong", required: false }
2655
- },
2656
- request: adAlertVerdictRequestSchema,
2657
- input: alertDismissInput
2658
- });
2659
- var attentionCommand = withWrites(attentionReadCommand, {
2660
- claim: attentionClaimCommand,
2661
- release: attentionReleaseCommand,
2662
- fixed: attentionFixedCommand,
2663
- dismiss: attentionDismissCommand
2664
- });
2665
-
2666
- // src/commands/ads/performance/creativeAssets.ts
2667
- import { defineCommand as defineCommand4 } from "citty";
2668
- var CREATIVE_ASSETS_ARGS = {
2669
- platforms: SCOPE_ARGS.platforms,
2670
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
2671
- "ad-ids": { type: "string", description: "Comma list: only what these ads show", required: false },
2672
- "ad-group-ids": {
2673
- type: "string",
2674
- description: "Comma list: only what these ad groups show (Performance Max asset groups have no ads)",
2675
- required: false
2676
- },
2677
- kinds: { type: "string", description: "Comma list of image,video (default both)", required: false },
2678
- statuses: {
2679
- type: "string",
2680
- description: "Comma list of stored,pending,unavailable,too-large,failed (default all)",
2681
- required: false
2682
- },
2683
- limit: { type: "string", description: "Assets returned, 1-500 (default 100)", required: false },
2684
- full: {
2685
- type: "boolean",
2686
- description: "Every field: the platform's own link, every use with its role and placement, timestamps",
2687
- required: false,
2688
- default: false
2689
- }
2690
- };
2691
- function creativeAssetsInput(args) {
2692
- return {
2693
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
2694
- adAccountIds: commaList(args["ad-account-ids"]),
2695
- adIds: commaList(args["ad-ids"]),
2696
- adGroupIds: commaList(args["ad-group-ids"]),
2697
- kinds: commaList(args.kinds)?.map((item) => enumValue(item) ?? item),
2698
- statuses: commaList(args.statuses)?.map((item) => enumValue(item) ?? item),
2699
- limit: optionalNumber(args.limit)
2700
- };
2701
- }
2702
- function compactAsset(asset) {
2703
- const roles = [...new Set(asset.uses.map((use) => use.placement ? `${use.role} (${use.placement})` : use.role))];
2704
- return Object.fromEntries(
2705
- Object.entries({
2706
- platform: asset.platform,
2707
- kind: asset.kind,
2708
- id: asset.platformAssetId,
2709
- status: asset.status,
2710
- url: asset.url,
2711
- posterUrl: asset.posterUrl,
2712
- youtubeUrl: asset.youtubeUrl,
2713
- size: asset.width && asset.height ? `${asset.width}x${asset.height}` : null,
2714
- aspectRatio: asset.aspectRatio,
2715
- seconds: asset.durationSeconds === null ? null : Math.round(asset.durationSeconds * 10) / 10,
2716
- mb: asset.bytes === null ? null : Math.round(asset.bytes / 1e4) / 100,
2717
- name: asset.name,
2718
- roles: roles.length > 0 ? roles : null,
2719
- ads: asset.useCount,
2720
- note: asset.note
2721
- }).filter(([, value]) => value !== null)
2722
- );
2723
- }
2724
- function compactCreativeAssets(data) {
2725
- return { counts: data.counts, assets: data.assets.map(compactAsset), ...data.more ? { more: true } : {} };
2726
- }
2727
- function creativeAssetsHints(data) {
2728
- const hints = [];
2729
- if (data.assets.length === 0) {
2730
- hints.push(
2731
- "No creative files are stored yet. They are copied after each ad sync (every few hours); check baker ads performance status."
2732
- );
2733
- return hints;
2734
- }
2735
- hints.push(
2736
- "url is Baker's permanent copy of the original: download it to make a variant (keep the aspect ratio of the placement it was for)."
2737
- );
2738
- if (data.counts.pending > 0) hints.push(`${data.counts.pending} copies are still on their way; ask again later.`);
2739
- if (data.assets.some((asset) => asset.youtubeUrl !== null))
2740
- hints.push(
2741
- "Google Ads videos live on YouTube and cannot be downloaded: use youtubeUrl and the cover in posterUrl."
2742
- );
2743
- if (data.assets.some((asset) => asset.platform === "linkedin"))
2744
- hints.push("LinkedIn assets are the ad account's media library: LinkedIn does not say which ad shows which.");
2745
- if (data.more) hints.push("More assets match: narrow with --ad-ids, --kinds or --platforms, or raise --limit.");
2746
- return hints;
2747
- }
2748
- registerSchema({
2749
- command: "ads.performance.creativeAssets",
2750
- description: "Every picture and video the ads run, copied by Baker at the largest size each platform hands over \u2014 the originals to create variants from. One row per file (deduped across ads): kind, status (stored | pending | unavailable | too_large | failed), url (Baker's permanent copy), posterUrl (a video's cover), size and aspectRatio, seconds, mb, roles (what it is in the ad: image, video, carousel_card, square_marketing_image, logo\u2026 with the placements a placement version is for) and how many ads use it. Meta: every image at its uploaded size, each placement version, carousel card and asset-feed variant; videos at the quality Meta streams (up to 720p), and only from Pages the connection manages. Google: every image asset in every ratio, Performance Max included; videos are YouTube links plus cover. LinkedIn: the ad account's media library, not tied to ads. X: the promoted posts' photos (original size) and videos (highest bitrate). ChatGPT Ads: each ad's image. --full adds the platform links, every use and timestamps.",
2751
- args: CREATIVE_ASSETS_ARGS
2752
- });
2753
- var creativeAssetsCommand = defineCommand4({
2754
- meta: {
2755
- name: "creative-assets",
2756
- description: "Start here to make a variant of an existing ad: every version of its pictures and videos, stored at full size. Example: baker ads performance creative-assets --ad-ids <ad id>"
2757
- },
2758
- args: CREATIVE_ASSETS_ARGS,
2759
- run: async ({ args }) => {
2760
- const parsed = adCreativeAssetsRequestSchema.safeParse(creativeAssetsInput(args));
2761
- if (!parsed.success) {
2762
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
2763
- process.exit(1);
2764
- }
2765
- try {
2766
- const response = await apiPost(
2767
- "/api/ads/performance/creative-assets",
2768
- parsed.data
2769
- );
2770
- const hints = creativeAssetsHints(response.data);
2771
- const data = args.full === true ? response.data : compactCreativeAssets(response.data);
2772
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
2773
- } catch (err) {
2774
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
2775
- if (err instanceof ApiError && refusal !== null) {
2776
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
2777
- process.exit(1);
2778
- }
2779
- failApi(err);
2780
- }
2781
- }
2782
- });
2783
-
2784
- // src/commands/ads/performance/creativeHealth.ts
2785
- import { defineCommand as defineCommand5 } from "citty";
2786
- var COMPACT_ROWS = 15;
2787
- var CREATIVE_HEALTH_ARGS = {
2788
- sections: {
2789
- type: "string",
2790
- description: "Comma list of trending,poor,hit-rate,freshness (default all)",
2791
- required: false
2792
- },
2793
- platforms: SCOPE_ARGS.platforms,
2794
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
2795
- currency: SCOPE_ARGS.currency,
2796
- "group-by": {
2797
- type: "string",
2798
- description: "creative (default: the same picture or video counts once across its ads) or ad",
2799
- required: false
2800
- },
2801
- months: { type: "string", description: "Launch months for the hit rate, 2-12 (default 6)", required: false },
2802
- new: {
2803
- type: "string",
2804
- description: "Freshness: a creative counts as new up to 7, 14 (default) or 30 days after launch",
2805
- required: false
2806
- },
2807
- over: {
2808
- type: "string",
2809
- description: "Freshness: measure the share over 1 (yesterday), 7 or 14 (default) days",
2810
- required: false
2811
- },
2812
- full: {
2813
- type: "boolean",
2814
- description: "Every list, tile and creative, unrounded, instead of the compact view",
2815
- required: false,
2816
- default: false
2817
- }
2818
- };
2819
- function creativeHealthInput(args) {
2820
- return {
2821
- sections: commaList(args.sections)?.map((item) => enumValue(item) ?? item),
2822
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
2823
- adAccountIds: commaList(args["ad-account-ids"]),
2824
- groupBy: enumValue(args["group-by"]),
2825
- months: optionalNumber(args.months),
2826
- currency: optionalString(args.currency)
2827
- };
2828
- }
2829
- function pick(allowed, raw, fallback) {
2830
- const value = optionalNumber(raw);
2831
- return allowed.find((entry) => entry === value) ?? fallback;
2832
- }
2833
- function freshnessChoice(args) {
2834
- return {
2835
- newDays: pick(AD_FRESHNESS_NEW_DAYS, args.new, 14),
2836
- periodDays: pick(AD_FRESHNESS_PERIOD_DAYS, args.over, 14)
2837
- };
2838
- }
2839
- function nullableRound(value) {
2840
- return value === null ? null : roundNumber(value);
2841
- }
2842
- function compactFreshness(currency, choice) {
2843
- const tile = currency.tiles.find((t) => t.newDays === choice.newDays && t.periodDays === choice.periodDays) ?? null;
2844
- return {
2845
- currency: currency.currency,
2846
- platforms: currency.platforms,
2847
- newDays: choice.newDays,
2848
- tile: tile ? roundDeep(tile) : null,
2849
- weekly: currency.weeks.map((week) => ({
2850
- to: week.to,
2851
- spend: roundNumber(week.spend),
2852
- freshShare: nullableRound(week.freshShares.find((s) => s.newDays === choice.newDays)?.share ?? null),
2853
- costPerResult: nullableRound(week.costPerResult),
2854
- settled: week.settled
2855
- })),
2856
- comparison: roundDeep(currency.comparisons.find((c) => c.newDays === choice.newDays) ?? null),
2857
- ...currency.beforeHistoryShare ? { beforeHistoryShare: roundNumber(currency.beforeHistoryShare) } : {}
2858
- };
2859
- }
2860
- function compactHitRate(account) {
2861
- return {
2862
- platform: account.platform,
2863
- adAccountId: account.adAccountId,
2864
- currency: account.currency,
2865
- accountCostPerResult: nullableRound(account.accountCostPerResult),
2866
- winnerSpend: nullableRound(account.winnerSpend),
2867
- months: account.months.map((month) => ({
2868
- month: month.month,
2869
- launches: month.launches,
2870
- winners: month.winners,
2871
- ...month.maturing ? { maturing: true } : {},
2872
- top: month.creatives.slice(0, 3).map((creative) => ({
2873
- name: creative.name || creative.headline,
2874
- spend: roundNumber(creative.spend),
2875
- ...creative.winner ? { winner: true } : {}
2876
- }))
2877
- }))
2878
- };
2879
- }
2880
- function compactCreativeHealth(data, choice) {
2881
- return {
2882
- groupBy: data.groupBy,
2883
- asOf: data.matureThrough.map((entry) => ({ platform: entry.platform, date: entry.date })),
2884
- ...data.trending ? {
2885
- trending: data.trending.rows.slice(0, COMPACT_ROWS).map((row) => ({
2886
- name: row.name || row.headline,
2887
- platform: row.platform,
2888
- adAccountId: row.adAccountId,
2889
- topAdId: row.topAdId,
2890
- currency: row.currency,
2891
- spend: roundNumber(row.spend),
2892
- previousSpend: roundNumber(row.previousSpend),
2893
- change: row.isNew ? "new" : row.restarted ? "restarted" : roundNumber(row.spendChange ?? 0),
2894
- costPerResult: nullableRound(row.costPerResult),
2895
- ctr: nullableRound(row.ctr),
2896
- ...row.videoViewRate === null ? {} : { videoViewRate: roundNumber(row.videoViewRate) }
2897
- }))
2898
- } : {},
2899
- ...data.poor ? {
2900
- poor: {
2901
- checkedAds: data.poor.checkedAds,
2902
- rows: data.poor.rows.slice(0, COMPACT_ROWS).map((row) => ({
2903
- name: row.name || row.headline,
2904
- platform: row.platform,
2905
- adId: row.adId,
2906
- adGroup: row.adGroupName,
2907
- currency: row.currency,
2908
- reason: row.reason,
2909
- spend: roundNumber(row.spend),
2910
- results: roundNumber(row.results),
2911
- costPerResult: nullableRound(row.costPerResult),
2912
- baseline: row.baseline,
2913
- baselineCostPerResult: roundNumber(row.baselineCostPerResult),
2914
- ...row.costRatio === null ? {} : { costRatio: roundNumber(row.costRatio) },
2915
- ageDays: row.ageDays
2916
- }))
2917
- }
2918
- } : {},
2919
- ...data.hitRate ? { hitRate: data.hitRate.accounts.map(compactHitRate) } : {},
2920
- ...data.freshness ? { freshness: data.freshness.currencies.map((c) => compactFreshness(c, choice)) } : {}
2921
- };
2922
- }
2923
- function money(amount, currency) {
2924
- return `${roundNumber(amount)} ${currency}`;
2925
- }
2926
- function pct(share2) {
2927
- return `${Math.round(share2 * 100)}%`;
2928
- }
2929
- function freshnessHints(currencies, choice) {
2930
- return currencies.flatMap((currency) => {
2931
- const c = currency.comparisons.find((entry) => entry.newDays === choice.newDays);
2932
- const hints = [];
2933
- if (c) {
2934
- hints.push(
2935
- `${currency.currency}: in the ${c.weeks} settled weeks with the most spend on creatives up to ${c.newDays} days old (about ${pct(c.freshWeeksShare)}), the median cost per result was ${money(c.freshWeeksMedianCostPerResult, currency.currency)}, against ${money(c.staleWeeksMedianCostPerResult, currency.currency)} in the ${c.weeks} weeks with the least (about ${pct(c.staleWeeksShare)}). Say it is a correlation between weeks, not proof that fresh creative caused it.`
2936
- );
2937
- }
2938
- if ((currency.beforeHistoryShare ?? 0) > 0.2) {
2939
- hints.push(
2940
- `${currency.currency}: ${pct(currency.beforeHistoryShare ?? 0)} of the spend went to creatives already running when Baker's stored history begins; their age is a lower bound.`
2941
- );
2942
- }
2943
- return hints;
2944
- });
2945
- }
2946
- function creativeHealthHints(data, choice) {
2947
- const hints = [];
2948
- if (data.trending && data.trending.rows.length > 0) {
2949
- hints.push(
2950
- "Trending means the platform is putting more money behind it, not that it works: read its costPerResult before calling it a winner. videoViewRate is each platform's own video view (Meta ThruPlays, Google views, LinkedIn and X video views) over impressions."
2951
- );
2952
- }
2953
- if (data.poor && data.poor.rows.length > 0) {
2954
- const first = data.poor.rows[0];
2955
- hints.push(
2956
- `Before pausing a poor performer, read it: \`baker ads performance entity ${first?.adId ?? "<ad id>"} --platform ${first?.platform ?? "<platform>"} --level ad\`. Pausing is a staged change applied at Publish.`
2957
- );
2958
- }
2959
- if (data.hitRate?.accounts.some((account) => account.months.some((month) => month.maturing))) {
2960
- hints.push(
2961
- `Months marked maturing still have launches inside their first ${data.hitRate.cohortDays} days: their winner count can still rise.`
2962
- );
2963
- }
2964
- if (data.freshness) hints.push(...freshnessHints(data.freshness.currencies, choice));
2965
- hints.push("Money is in the reporting currency (--currency); never add amounts across currencies.");
2966
- return hints;
2967
- }
2968
- registerSchema({
2969
- command: "ads.performance.creativeHealth",
2970
- description: "Creative health from Baker's stored ad data, every platform with creatives. trending: creatives whose spend grew more than 35% over the last 7 settled days against the 7 before (change: new | restarted | relative growth), with cost per result, CTR and video view rate. poor: active ads at least 14 days old costing far more per result than their ad set (shrunk, costRatio) or with no results after spending 3\xD7 the ad set's cost per result. hitRate: per ad account and launch month, creatives launched and winners (spend \u2265 10\xD7 the account's cost per result within 90 days; winnerSpend is that bar); maturing months can still gain winners. freshness: share of spend on creatives up to --new days old over --over days against the period before, spend-weighted creative age, fresh vs stale cost per result, 13 weekly shares and a fresh-vs-stale weeks comparison (a correlation, never a cause). --full returns every list.",
2971
- args: CREATIVE_HEALTH_ARGS
2972
- });
2973
- var creativeHealthCommand = defineCommand5({
2974
- meta: {
2975
- name: "creative-health",
2976
- description: "Trending creatives, poor performers, launch hit rate and creative freshness. Example: baker ads performance creative-health --sections trending,poor"
2977
- },
2978
- args: CREATIVE_HEALTH_ARGS,
2979
- run: async ({ args }) => {
2980
- const parsed = adCreativeHealthRequestSchema.safeParse(creativeHealthInput(args));
2981
- if (!parsed.success) {
2982
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
2983
- process.exit(1);
2984
- }
2985
- const choice = freshnessChoice(args);
2986
- try {
2987
- const response = await apiPost(
2988
- "/api/ads/performance/creative-health",
2989
- parsed.data
2990
- );
2991
- const hints = creativeHealthHints(response.data, choice);
2992
- const data = args.full === true ? response.data : compactCreativeHealth(response.data, choice);
2993
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
2994
- } catch (err) {
2995
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
2996
- if (err instanceof ApiError && refusal !== null) {
2997
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
2998
- process.exit(1);
2999
- }
3000
- failApi(err);
3001
- }
3002
- }
3003
- });
3004
-
3005
- // src/commands/ads/performance/diagnostic.ts
3006
- import { defineCommand as defineCommand6 } from "citty";
3007
- var ANOMALIES_ARGS = {
3008
- days: { type: "string", description: "Settled days to scan, 7-28 (default 14)", required: false },
3009
- end: {
3010
- type: "string",
3011
- description: "Last day to scan, YYYY-MM-DD (default each platform's last settled day)",
3012
- required: false
3013
- },
3014
- platforms: SCOPE_ARGS.platforms,
3015
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
3016
- currency: SCOPE_ARGS.currency,
3017
- "cpa-spike": {
3018
- type: "string",
3019
- description: "How far above its usual a day's cost per result must be to count, e.g. 0.3 = 30% (default 0.5). Pass the company's own threshold when it has one",
3020
- required: false
3021
- },
3022
- full: SCOPE_ARGS.full
3023
- };
3024
- var RECAP_ARGS = {
3025
- days: { type: "string", description: "Days back, today included, 1-31 (default 7)", required: false },
3026
- platforms: SCOPE_ARGS.platforms,
3027
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
3028
- full: SCOPE_ARGS.full
3029
- };
3030
- var platformList = (args) => commaList(args.platforms)?.map((item) => enumValue(item) ?? item);
3031
- function anomaliesInput(args) {
3032
- return {
3033
- days: optionalNumber(args.days),
3034
- end: optionalString(args.end),
3035
- platforms: platformList(args),
3036
- adAccountIds: commaList(args["ad-account-ids"]),
3037
- cpaSpike: optionalNumber(args["cpa-spike"]),
3038
- currency: optionalString(args.currency)
3039
- };
3040
- }
3041
- function recapInput(args) {
3042
- return {
3043
- days: optionalNumber(args.days),
3044
- platforms: platformList(args),
3045
- adAccountIds: commaList(args["ad-account-ids"])
3046
- };
3047
- }
3048
- var nullableRound2 = (value) => value === null ? null : roundNumber(value);
3049
- function compactAccount(account) {
3050
- return {
3051
- platform: account.platform,
3052
- adAccountId: account.adAccountId,
3053
- name: account.accountName,
3054
- currency: account.currency,
3055
- status: account.status,
3056
- headline: account.headline,
3057
- window: { from: account.window.from, to: account.window.to },
3058
- spend: roundNumber(account.window.spend),
3059
- costPerResult: nullableRound2(account.window.costPerResult),
3060
- usualCostPerResult: nullableRound2(account.baseline.costPerResult),
3061
- anomalies: account.anomalies.map((period) => ({
3062
- kind: period.kind,
3063
- from: period.anomaly.from,
3064
- to: period.anomaly.to,
3065
- spend: roundNumber(period.anomaly.spend),
3066
- results: roundNumber(period.anomaly.results),
3067
- expectedResults: roundNumber(period.expectedResults),
3068
- costPerResult: nullableRound2(period.anomaly.costPerResult),
3069
- before: {
3070
- from: period.before.from,
3071
- to: period.before.to,
3072
- spend: roundNumber(period.before.spend),
3073
- costPerResult: nullableRound2(period.before.costPerResult)
3074
- },
3075
- factors: period.factors.map((factor) => ({
3076
- level: factor.level,
3077
- entityId: factor.entityId,
3078
- name: factor.name,
3079
- ...factor.adGroupName ? { adGroup: factor.adGroupName } : {},
3080
- campaign: factor.campaignName,
3081
- spend: roundNumber(factor.spend),
3082
- previousSpend: roundNumber(factor.previousSpend),
3083
- spendChangePct: nullableRound2(factor.spendChangePct),
3084
- costPerResult: nullableRound2(factor.costPerResult),
3085
- previousCostPerResult: nullableRound2(factor.previousCostPerResult)
3086
- })),
3087
- changes: period.changes.map((change) => change.summary)
3088
- }))
3089
- };
3090
- }
3091
- function compactAnomalies(data) {
3092
- return {
3093
- through: data.through,
3094
- accounts: data.accounts.map(compactAccount),
3095
- events: data.events.map((event) => ({
3096
- platform: event.platform,
3097
- adAccountId: event.adAccountId,
3098
- event: event.label,
3099
- status: event.status,
3100
- lastDay: event.lastDay,
3101
- sentence: event.sentence
3102
- }))
3103
- };
3104
- }
3105
- function compactRecap(data) {
3106
- return { window: data.window, lines: data.lines.map((line) => line.sentence) };
3107
- }
3108
- function anomaliesHints(data) {
3109
- const hints = [];
3110
- const broken = data.events.filter((event) => event.status === "stale" || event.status === "never");
3111
- if (broken.length > 0) {
3112
- hints.push(
3113
- `Check these conversion events first \u2014 while one is not recording, every cost per result built on it is wrong: ${broken.map((event) => `"${event.label}" (${event.platform} ${event.adAccountId}, ${event.status})`).join("; ")}. Ask the user to submit a test conversion on the site and check the event is still connected on the platform.`
3114
- );
3115
- }
3116
- if (data.accounts.some((account) => account.status === "anomaly")) {
3117
- hints.push(
3118
- "Report each unusual stretch with its days and numbers, then the contributing factors and the nearby changes as facts. Do not say a change caused it: `baker ads performance impact` judges a named change, `baker ads performance diagnose` one entity over weeks."
3119
- );
3120
- }
3121
- if (data.accounts.some((account) => account.status === "too_few_results")) {
3122
- hints.push(
3123
- "too_few_results: the four weeks before hold under 10 results, so no day can be called unusual; judge those accounts weekly with `baker ads performance progress`."
3124
- );
3125
- }
3126
- hints.push(
3127
- "Event health is by whole day (no time of day is stored): 'none since 3 Oct' means no record on any day after it, through yesterday. --full adds the daily series."
3128
- );
3129
- return hints;
3130
- }
3131
- function recapHints(data) {
3132
- const hints = [
3133
- "These are facts \u2014 what changed, when, by whom \u2014 never why. Add your own reading only from other reads (e.g. `baker ads performance impact`), and say it is yours."
3134
- ];
3135
- if (data.readChanges < data.totalChanges) {
3136
- hints.push(
3137
- `Only the newest ${data.readChanges} of ${data.totalChanges} changes were read; pass a shorter --days or --platforms for the rest.`
3138
- );
3139
- }
3140
- return hints;
3141
- }
3142
- function diagnosticCommand(spec) {
3143
- registerSchema({
3144
- command: `ads.performance.${spec.schemaName}`,
3145
- description: spec.schemaDescription,
3146
- args: spec.args
3147
- });
3148
- return defineCommand6({
3149
- meta: { name: spec.name, description: spec.help },
3150
- args: spec.args,
3151
- run: async ({ args }) => {
3152
- const parsed = spec.request.safeParse(spec.input(args));
3153
- if (!parsed.success) {
3154
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
3155
- process.exit(1);
3156
- }
3157
- try {
3158
- const response = await apiPost(`/api/ads/performance/${spec.path}`, parsed.data);
3159
- const hints = spec.hints(response.data);
3160
- const data = args.full === true ? response.data : spec.compact(response.data);
3161
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
3162
- } catch (err) {
3163
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
3164
- if (err instanceof ApiError && refusal !== null) {
3165
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
3166
- process.exit(1);
3167
- }
3168
- failApi(err);
3169
- }
3170
- }
3171
- });
3172
- }
3173
- var anomaliesCommand = diagnosticCommand({
3174
- name: "anomalies",
3175
- schemaName: "anomalies",
3176
- schemaDescription: "Did something break on particular days? Per ad account, its last 14 settled days (each platform's last settled day: matureThrough) scanned day by day: status anomaly | clear | too_few_results | no_spend, a headline, and each unusual stretch \u2014 spend, results and cost per result against the same number of days before, the ad groups and ads that carried over 25% of its spend and moved over 15% (factors), and budget steps over 25% and targeting, goal, status and launch changes near it (changes). A stretch is unusual when cost per result is 50% above the four weeks before (--cpa-spike) and so few results are unlikely at the usual rate \u2014 small accounts are not flagged for an ordinary empty day. Days still being counted are never flagged. Plus event health: each conversion event the ads optimise for (and each goal action) with the last day it recorded and whether the silence since is longer than its usual rate allows (recording | stale | quiet | never | rare). Complements progress (weekly, per entity). Read-only, from Baker's store. --full adds the daily series.",
3177
- help: "Unusual days in the last two weeks, and whether conversion events still record. Example: baker ads performance anomalies",
3178
- path: "anomalies",
3179
- args: ANOMALIES_ARGS,
3180
- request: adPerformanceAnomaliesRequestSchema,
3181
- input: anomaliesInput,
3182
- compact: compactAnomalies,
3183
- hints: anomaliesHints
3184
- });
3185
- var recapCommand = diagnosticCommand({
3186
- name: "recap",
3187
- schemaName: "recap",
3188
- schemaDescription: "The last 7 days of changes (--days) as a media buyer's recap: one plain sentence per day, ad account, who and kind of change \u2014 launches, ads or ad sets turned on or paused, budgets raised or lowered by how much, targeting, goal, creative and name edits \u2014 saying who made them (a person in the platform, Baker with its chat, or the platform automatically) and when. Facts only, never a reason. Newest first. --full adds counts, kinds and entity ids per line. Read-only, from Baker's store.",
3189
- help: "This week's changes in plain sentences. Example: baker ads performance recap --days 7",
3190
- path: "recap",
3191
- args: RECAP_ARGS,
3192
- request: adPerformanceRecapRequestSchema,
3193
- input: recapInput,
3194
- compact: compactRecap,
3195
- hints: recapHints
3196
- });
3197
-
3198
- // src/commands/ads/performance/health.ts
3199
- import { defineCommand as defineCommand7 } from "citty";
3200
- var HEALTH_ARGS = {
3201
- platforms: SCOPE_ARGS.platforms,
3202
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
3203
- currency: SCOPE_ARGS.currency,
3204
- full: {
3205
- type: "boolean",
3206
- description: "Every table: learning ad sets, audience table, top ads' copy, weekly reach and segments",
3207
- required: false,
3208
- default: false
3209
- }
3210
- };
3211
- function healthInput(args) {
3212
- return {
3213
- platforms: commaList(args.platforms),
3214
- adAccountIds: commaList(args["ad-account-ids"]),
3215
- currency: typeof args.currency === "string" && args.currency.trim() !== "" ? args.currency.trim() : void 0
3216
- };
3217
- }
3218
- function share(part, total) {
3219
- return total > 0 ? part / total : null;
3220
- }
3221
- function healthHeadline(checks) {
3222
- const asking = checks.filter((check) => check.state === "act" || check.state === "look");
3223
- if (asking.length === 0) return `None of ${checks.length} health checks needs a person.`;
3224
- return `${asking.length} of ${checks.length} health checks need a person: ${asking.map((check) => check.title).join(", ")}.`;
3225
- }
3226
- function compactHealth(data) {
3227
- return roundDeep({
3228
- headline: healthHeadline(data.checks),
3229
- checks: data.checks.map(({ section, state, line, window }) => ({ section, state, line, window })),
3230
- asOf: data.asOf,
3231
- spend: data.spend.map((row) => ({
3232
- platform: row.platform,
3233
- currency: row.currency,
3234
- thisMonth: row.thisMonth.spend,
3235
- lastMonthSameDays: row.lastMonth.spend,
3236
- change: row.change
3237
- })),
3238
- costPerResult: data.costPerResult.map((row) => ({
3239
- platform: row.platform,
3240
- currency: row.currency,
3241
- last7Settled: row.current.costPerResult,
3242
- prior7: row.previous.costPerResult,
3243
- change: row.change,
3244
- settledThrough: row.current.to
3245
- })),
3246
- traffic30d: data.traffic.map((row) => ({
3247
- platform: row.platform,
3248
- currency: row.currency,
3249
- costPerLinkClick: row.costPerLinkClick,
3250
- linkCtr: row.linkCtr,
3251
- cpm: row.cpm
3252
- })),
3253
- learning7d: data.learning.map((row) => ({
3254
- platform: row.platform,
3255
- currency: row.currency,
3256
- share: row.share,
3257
- verdict: row.verdict,
3258
- learningSpend: row.learningSpend + row.limitedSpend,
3259
- totalSpend: row.totalSpend,
3260
- inLearning: row.groups.length
3261
- })),
3262
- audience30d: data.audience.map((row) => ({
3263
- platform: row.platform,
3264
- currency: row.currency,
3265
- prospecting: share(row.prospectingSpend, row.totalSpend),
3266
- retargeting: share(row.retargetingSpend, row.totalSpend),
3267
- ...row.unknownSpend > 0 ? { notReadYet: share(row.unknownSpend, row.totalSpend) } : {},
3268
- ...row.segmentShares ? { segments: row.segmentShares } : {},
3269
- topExclusions: row.topExclusions.slice(0, 3)
3270
- })),
3271
- copy: data.copy.map((row) => ({ platform: row.platform, maximized: row.maximized, of: row.ads.length })),
3272
- frequency: data.frequency.map((row) => ({
3273
- platform: row.platform,
3274
- averageWeeklyFrequency: row.averageFrequency,
3275
- averageFirstTimeRate: row.averageFirstTimeRate,
3276
- weeks: row.weeks.filter((week) => !week.partial).length
3277
- })),
3278
- monthly: data.monthly.map((row) => ({
3279
- platform: row.platform,
3280
- currency: row.currency,
3281
- months: row.months.map((month) => ({
3282
- month: month.month,
3283
- spend: month.spend,
3284
- costPerResult: month.costPerResult,
3285
- ...month.partial ? { partial: true } : {}
3286
- }))
3287
- })),
3288
- segments: data.segments.map((row) => ({
3289
- platform: row.platform,
3290
- currency: row.currency,
3291
- lastWeek: row.weeks.filter((week) => !week.partial).at(-1)?.segments.map((cell) => ({
3292
- segment: cell.segment,
3293
- share: cell.share,
3294
- costPerResult: cell.costPerResult
3295
- })),
3296
- moving: row.moving
3297
- })),
3298
- unavailable: data.unavailable
3299
- });
3300
- }
3301
- var LEARNING_ADVICE = {
3302
- monitor: "avoid more big edits (budget jumps, new targeting, new ads) until it settles",
3303
- act: "consolidate ad sets or slow new launches so fewer of them restart learning"
3304
- };
3305
- function healthHints(data) {
3306
- const hints = [];
3307
- for (const row of data.learning) {
3308
- if (row.verdict !== "monitor" && row.verdict !== "act") continue;
3309
- const where = row.basis === "campaign" ? "campaigns whose bidding is still learning or held back by too little conversion data" : "ad sets still learning or learning-limited";
3310
- hints.push(
3311
- `${Math.round((row.share ?? 0) * 100)}% of ${row.platform}'s last-7-day spend (${row.currency}) is in ${where} (healthy below ${AD_LEARNING_SHARE_THRESHOLDS.monitor * 100}%): ${LEARNING_ADVICE[row.verdict]}. --full lists them.`
3312
- );
3313
- }
3314
- for (const row of data.copy) {
3315
- if (row.maximized < row.ads.length) {
3316
- hints.push(
3317
- `${row.ads.length - row.maximized} of the top ${row.ads.length} ${row.platform} ads leave copy slots empty (up to ${AD_COPY_VARIANTS_MAX} primary texts and ${AD_COPY_VARIANTS_MAX} headlines): more variants give delivery more to test. --full lists the counts.`
3318
- );
3319
- }
3320
- }
3321
- for (const row of data.segments) if (row.moving) hints.push(row.moving);
3322
- hints.push(
3323
- "Cost per result compares each platform's last 7 settled days; reach of several ad accounts is added. Money is in the reporting currency (--currency); never add amounts across currencies."
3324
- );
3325
- return hints;
3326
- }
3327
- registerSchema({
3328
- command: "ads.performance.health",
3329
- description: `An ad account's health from Baker's stored ad data. headline + checks: one verdict per section (act, look, ok, info = read but not judged, unavailable), problems first \u2014 the same checklist Measure \u2192 Ads \u2192 Summary shows under "More about these accounts". spend: this month so far vs the same days last month. costPerResult: last 7 settled days vs the 7 before. traffic30d: cost per link click, link CTR, CPM. learning7d: share of the last 7 days' spend in ad sets (Meta) or campaigns (Google Ads) still learning or learning-limited; verdict healthy <20%, monitor 20\u201350%, act >50%. audience30d (Meta): spend on prospecting (broad, interests, lookalikes) vs retargeting (site, engagement or customer-list audiences), Advantage+ customer segments where set up, most-excluded audiences. copy (Meta): top 10 ads by 30-day spend using all 5 primary texts and 5 headlines. frequency (Meta): average weekly frequency (impressions \xF7 people reached) and first-time impression rate (reach \xF7 impressions) over 12 whole weeks. monthly: spend and cost per result for 6 months. segments (Meta): weekly share of spend, cost per result and return by new / engaged / existing customers. unavailable says what a platform cannot fill and why. --full returns every table.`,
3330
- args: HEALTH_ARGS
3331
- });
3332
- var healthCommand = defineCommand7({
3333
- meta: {
3334
- name: "health",
3335
- description: "An ad account's health: spend, cost per result, learning, audiences, copy, frequency. Example: baker ads performance health --platforms meta"
3336
- },
3337
- args: HEALTH_ARGS,
3338
- run: async ({ args }) => {
3339
- const parsed = adPerformanceHealthRequestSchema.safeParse(healthInput(args));
3340
- if (!parsed.success) {
3341
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
3342
- process.exit(1);
3343
- }
3344
- try {
3345
- const response = await apiPost(
3346
- "/api/ads/performance/health",
3347
- parsed.data
3348
- );
3349
- const hints = healthHints(response.data);
3350
- const data = args.full === true ? response.data : compactHealth(response.data);
3351
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
3352
- } catch (err) {
3353
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
3354
- if (err instanceof ApiError && refusal !== null) {
3355
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
3356
- process.exit(1);
3357
- }
3358
- failApi(err);
3359
- }
3360
- }
3361
- });
3362
-
3363
- // src/commands/ads/performance/liveIn.ts
3364
- var LIVE_IN_ARGS = {
3365
- landing: { type: "string", description: "Only this landing: its slug (src/pages/<slug>/)", required: false },
3366
- creative: { type: "string", description: "Only this creative: its slug (src/creatives/<slug>/)", required: false },
3367
- full: {
3368
- type: "boolean",
3369
- description: "Every live ad showing a creative, with its ad group and version",
3370
- required: false,
3371
- default: false
3372
- }
3373
- };
3374
- var plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
3375
- function compactLanding(landing) {
3376
- return {
3377
- slug: landing.slug,
3378
- campaigns: landing.campaigns.map((campaign) => ({
3379
- platform: campaign.platform,
3380
- id: campaign.campaignId,
3381
- name: campaign.name,
3382
- ...campaign.ads > 0 ? { ads: campaign.ads } : {},
3383
- ...campaign.assetGroups > 0 ? { assetGroups: campaign.assetGroups } : {},
3384
- ...campaign.keywords > 0 ? { keywords: campaign.keywords } : {},
3385
- spend30d: campaign.spend30d
3386
- }))
3387
- };
3388
- }
3389
- function compactCreative(creative) {
3390
- return {
3391
- slug: creative.slug,
3392
- title: creative.title,
3393
- liveAds: creative.liveAds,
3394
- ...creative.versions.length > 0 ? { versions: creative.versions } : {},
3395
- ...creative.likely ? { likely: true } : {},
3396
- campaigns: creative.campaigns.map((campaign) => ({
3397
- platform: campaign.platform,
3398
- id: campaign.campaignId,
3399
- name: campaign.name,
3400
- ads: campaign.ads.length
3401
- })),
3402
- spend30d: creative.spend30d
3403
- };
3404
- }
3405
- function compactLiveIn(data) {
3406
- return { landings: data.landings.map(compactLanding), creatives: data.creatives.map(compactCreative) };
3407
- }
3408
- function landingHint(landing) {
3409
- const [first] = landing.campaigns;
3410
- if (!first) return `No live campaign sends people to ${landing.slug} now: changing it touches no running ad.`;
3411
- const count = landing.campaigns.length;
3412
- return `${plural(count, "live campaign")} ${count === 1 ? "sends" : "send"} people to ${landing.slug}: changing the page changes what ${count === 1 ? "its" : "their"} visitors see once you publish. Read ${count === 1 ? "it" : "them"} first (baker ads performance entity ${first.campaignId} --platform ${first.platform} --level campaign) and name ${count === 1 ? "it" : "them"} to the person.`;
3413
- }
3414
- function creativeHint(creative) {
3415
- if (creative.liveAds === 0)
3416
- return `No live ad shows ${creative.slug} now: a new version of it touches no running ad.`;
3417
- const platforms = creative.platforms.map((platform) => `baker ads ${platform}`).join(" / ");
3418
- return `${creative.slug} runs in ${plural(creative.liveAds, "live ad")} across ${plural(creative.campaigns.length, "campaign")}. A new version reaches them only when you stage it into those ads (${platforms}); judge the running one first with baker ads performance creatives (its row's madeInBaker names it).`;
3419
- }
3420
- function liveInHints(data, request) {
3421
- const hints = [];
3422
- if (request.landing !== void 0) hints.push(...data.landings.map(landingHint));
3423
- if (request.creative !== void 0) hints.push(...data.creatives.map(creativeHint));
3424
- if (data.creatives.some((creative) => creative.likely))
3425
- hints.push('likely: some ads show a close look-alike, not the same file. Say "likely runs in".');
3426
- if (request.landing === void 0 && request.creative === void 0) {
3427
- if (data.landings.length === 0 && data.creatives.length === 0)
3428
- hints.push(
3429
- "No landing or creative is in a live campaign now. If ads were launched in the last hours, check baker ads performance status."
3430
- );
3431
- else
3432
- hints.push(
3433
- "Before changing a landing or creative listed here, tell the person which live campaigns it reaches. --landing <slug> or --creative <slug> for one."
3434
- );
3435
- }
3436
- return hints;
3437
- }
3438
- var liveInCommand = report({
3439
- name: "live-in",
3440
- schemaName: "liveIn",
3441
- schemaDescription: "Which live campaigns use a landing or a creative. Ask it before changing either. --landing <slug>: the live ads, Performance Max asset groups and keywords sending people to that page, per campaign, with 30 days of spend. --creative <slug>: the live ads showing that creative (any of its versions, matched by picture), per campaign, with the versions they show; likely: a close look-alike rather than the same file. Neither flag: every landing and creative that is live now. Read from Baker's synced ad data, refreshed every few hours; no platform call. --full lists every live ad of a creative.",
3442
- help: "Which live campaigns use a landing or a creative. Example: baker ads performance live-in --landing demo",
3443
- path: "live-in",
3444
- args: LIVE_IN_ARGS,
3445
- request: adPerformanceLiveInRequestSchema,
3446
- input: (args) => ({ landing: optionalString(args.landing), creative: optionalString(args.creative) }),
3447
- compact: compactLiveIn,
3448
- hints: liveInHints
3449
- });
3450
-
3451
- // src/commands/ads/performance/people.ts
3452
- import { defineCommand as defineCommand8 } from "citty";
3453
- var VOCABULARY_HINT = 'Say "brought" for --touch first and "last paid touch" for --touch last; each person counts once, for one entity. Never call it attributed revenue or ROI, and never add money across currencies.';
3454
- function compactRow(row, level) {
3455
- const deals = row.companyDeals.opened + row.companyDeals.won > 0 ? { companyDeals: roundDeep(row.companyDeals) } : {};
3456
- const path = row.entityId === "" ? null : entityPath({ ...row, level });
3457
- return {
3458
- platform: row.platform,
3459
- ...row.entityId === "" ? { untagged: true } : { entityId: row.entityId, name: row.name },
3460
- ...path ? { path } : {},
3461
- ...row.entityId !== "" && !row.known ? { unknownId: true } : {},
3462
- ...row.spend > 0 ? { spend: roundNumber(row.spend), currency: row.currency } : {},
3463
- paidSessions: row.paidSessions,
3464
- people: row.people,
3465
- steps: row.stages.map((stage) => ({
3466
- name: stage.name,
3467
- people: stage.people,
3468
- ...stage.perLead === null ? {} : { perLead: roundNumber(stage.perLead) },
3469
- ...stage.costPer === null ? {} : { costPer: roundNumber(stage.costPer) },
3470
- ...stage.value.length > 0 ? { value: roundDeep(stage.value) } : {},
3471
- ...stage.medianDays === null ? {} : { medianDays: roundNumber(stage.medianDays) }
3472
- })),
3473
- ...deals
3474
- };
3475
- }
3476
- function compactPeople(data) {
3477
- if (!data.collected) return { collected: false, reason: data.notCollectedReason };
3478
- return {
3479
- window: data.window,
3480
- level: data.level,
3481
- touch: data.touch,
3482
- ladder: data.ladder.stages.map((stage) => stage.name),
3483
- rows: data.rows.map((row) => compactRow(row, data.level)),
3484
- untagged: data.untagged.map((row) => compactRow(row, data.level)),
3485
- coverage: roundDeep(data.coverage),
3486
- maturity: data.maturity.map((step) => ({
3487
- name: step.name,
3488
- p75Days: step.p75Days === null ? null : roundNumber(step.p75Days),
3489
- maturingFrom: step.maturingFrom
3490
- }))
3491
- };
3492
- }
3493
- function peopleHints(data) {
3494
- if (!data.collected) return [data.notCollectedReason ?? "People-level data is not collected for this company."];
3495
- const hints = [VOCABULARY_HINT];
3496
- if (data.windowClipped) {
3497
- hints.push(
3498
- `Visits are kept 400 days and a first touch is looked for 90 days before the window, so this covers ${data.window.from} to ${data.window.to}.`
3499
- );
3500
- }
3501
- if (data.ladder.isDefault) {
3502
- hints.push(
3503
- "The ladder is the default (Lead = gave an email or phone, Customer = an amount from a CRM or the company's server) because the company's Funnel names no event after a visit. Run `baker ads performance ladder` to see which events could move people onto its lead and later steps; adding them to the Funnel (`baker funnel`) makes them this ladder."
3504
- );
3505
- }
3506
- for (const step of data.maturity) {
3507
- if (step.maturingFrom !== null && step.maturingFrom <= data.window.to && step.index > 1) {
3508
- hints.push(
3509
- `People who arrived from ${step.maturingFrom} on have not had the usual ${Math.ceil(step.p75Days ?? 0)} days to reach "${step.name}"; read their numbers as early, not as failing.`
3510
- );
3511
- }
3512
- }
3513
- const { resolvedShare, paidTouchShare } = data.coverage;
3514
- if (resolvedShare !== null && data.coverage.paidSessions >= 20 && resolvedShare < 0.8) {
3515
- hints.push(
3516
- `Only ${Math.round(resolvedShare * 100)}% of paid visits carry an id that names a synced campaign, ad set or ad; the rest show under untagged (or unknownId). Check \`baker ads performance tracking\`.`
3517
- );
3518
- }
3519
- if (paidTouchShare !== null && data.coverage.leads >= 10) {
3520
- hints.push(
3521
- `${Math.round(paidTouchShare * 100)}% of the window's ${data.coverage.leads} leads had a paid visit in the 90 days before; the others came another way.`
3522
- );
3523
- }
3524
- return hints;
3525
- }
3526
- function compactPersonas(data) {
3527
- if (!data.collected) return { collected: false, reason: data.notCollectedReason };
3528
- return {
3529
- window: data.window,
3530
- level: data.level,
3531
- touch: data.touch,
3532
- dimension: data.dimension,
3533
- fieldKeys: data.fieldKeys,
3534
- ladder: data.ladderStages,
3535
- rows: data.rows.map((row) => ({
3536
- platform: row.platform,
3537
- ...row.entityId === "" ? { untagged: true } : { entityId: row.entityId, name: row.name },
3538
- people: row.people,
3539
- values: row.values.map((value) => ({
3540
- value: value.kind === "answer" ? value.value : value.kind === "other" ? "(other answers)" : "(no answer)",
3541
- people: value.people,
3542
- steps: value.stagePeople
3543
- }))
3544
- }))
3545
- };
3546
- }
3547
- function personasHints(data) {
3548
- if (!data.collected) return [data.notCollectedReason ?? "People-level data is not collected for this company."];
3549
- const hints = [];
3550
- if (data.fieldKeys.length === 0 && !["email_kind", "visit_country", "device"].includes(data.dimension)) {
3551
- hints.push(
3552
- `No Form field is known to answer "${data.dimension}". Run \`baker ads performance persona-fields\` to see the fields people answered; a Form field named for the question (job_title for role, company_size, industry\u2026) is read automatically.`
3553
- );
3554
- } else if (data.fieldsSuggested) {
3555
- hints.push(`Read from ${data.fieldKeys.join(", ")}, chosen from the field names.`);
3556
- }
3557
- if (data.dimension === "email_kind") {
3558
- hints.push(
3559
- "A work address is recognised only when Baker keeps a copy of what visitors typed; otherwise it reads as personal."
3560
- );
3561
- }
3562
- hints.push(
3563
- "steps are people per ladder step, the lead first. Answers fewer than three people gave are folded into (other answers), so no value points at one person."
3564
- );
3565
- return hints;
3566
- }
3567
- function ladderHints(data) {
3568
- return [
3569
- data.ladder.isDefault ? "The company's Funnel names no event after a visit, so the default is in force. Suggest which of these events move people onto the Funnel's lead and later steps; adding them to the Funnel (`baker funnel`) makes them the ladder." : "This ladder is the company's Funnel: its first step after a visit is the lead and later steps follow in order (steps at the same point are one rung; at most four, keeping the step where money arrives). To change it, change the Funnel (`baker funnel`).",
3570
- "A HubSpot Source's stage values can narrow a step: <member>|hubspot_value=<value>."
3571
- ];
3572
- }
3573
- function personaFieldsHints(data) {
3574
- return [
3575
- data.suggested ? "This map is Baker's reading of the Form field names (job_title \u2192 role, company_size, industry\u2026); a field named for a question is read for it." : "This is the company's stored persona map.",
3576
- "Fields whose answers are mostly kept sealed (masked) identify people and are never a persona."
3577
- ];
3578
- }
3579
- var LEVEL_ARGS = {
3580
- level: { type: "string", description: "campaign (default), ad_group or ad", required: false },
3581
- touch: {
3582
- type: "string",
3583
- description: "last (default: the last paid touch before they became a lead) or first (what brought them)",
3584
- required: false
3585
- }
3586
- };
3587
- var PEOPLE_ARGS = {
3588
- ...SCOPE_ARGS,
3589
- "ad-group-ids": ENTITIES_ARGS["ad-group-ids"],
3590
- ...LEVEL_ARGS,
3591
- limit: { type: "string", description: "Entities to return, 1-500 (default 50)", required: false }
3592
- };
3593
- var PERSONAS_ARGS = {
3594
- ...SCOPE_ARGS,
3595
- "ad-group-ids": ENTITIES_ARGS["ad-group-ids"],
3596
- ...LEVEL_ARGS,
3597
- dimension: {
3598
- type: "string",
3599
- description: "role, seniority, company_size, industry, use_case, heard_from, budget, country (from Form answers), or email_kind, visit_country, device",
3600
- required: true
3601
- },
3602
- top: {
3603
- type: "string",
3604
- description: "Answers to name before the rest read other, 1-30 (default 12)",
3605
- required: false
3606
- }
3607
- };
3608
- var SETTINGS_ARGS = {
3609
- days: { type: "string", description: "How far back to look, 7-300 (default 90)", required: false },
3610
- full: SCOPE_ARGS.full
3611
- };
3612
- function peopleCommand(spec) {
3613
- registerSchema({
3614
- command: `ads.performance.${spec.schemaName}`,
3615
- description: spec.schemaDescription,
3616
- args: spec.args
3617
- });
3618
- return defineCommand8({
3619
- meta: { name: spec.name, description: spec.help },
3620
- args: spec.args,
3621
- run: async ({ args }) => {
3622
- const parsed = spec.request.safeParse(spec.input(args));
3623
- if (!parsed.success) {
3624
- writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(parsed.error.issues) } });
3625
- process.exit(1);
3626
- }
3627
- try {
3628
- const response = await apiPost(`/api/ads/performance/${spec.path}`, parsed.data);
3629
- const hints = spec.hints(response.data);
3630
- const data = args.full === true ? response.data : spec.compact(response.data);
3631
- writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
3632
- } catch (err) {
3633
- const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
3634
- if (err instanceof ApiError && refusal !== null) {
3635
- writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
3636
- process.exit(1);
3637
- }
3638
- failApi(err);
3639
- }
3640
- }
3641
- });
3642
- }
3643
- var PEOPLE_NOTE = "One person counts once, for one entity (--touch last: the last paid visit before they became a lead; first: the visit that brought them), and their later steps count when they happen within 90 days of it. Steps are the company's own ladder. Money in the reporting currency (--currency), never added across currencies. --full returns every field.";
3644
- var levelTouchInput = (args) => ({
3645
- ...scopeInput(args),
3646
- adGroupIds: commaList(args["ad-group-ids"]),
3647
- level: enumValue(args.level),
3648
- touch: enumValue(args.touch)
3649
- });
3650
- var peopleOutcomesCommand = peopleCommand({
3651
- name: "people",
3652
- schemaName: "people",
3653
- schemaDescription: `Who each campaign, ad set or ad brought and how far they got: paid visits, people, and per ladder step (lead, then e.g. qualified, customer) the people, the share of leads, the cost per person, the value per currency and the median days to it; untagged paid traffic per platform; coverage (paid visits tied to an ad, leads with a known paid visit); and when recent cohorts are still maturing. ${PEOPLE_NOTE}`,
3654
- help: "People each campaign brought and how far they got. Example: baker ads performance people --level campaign --days 90. Inside one campaign: --level ad_group --campaign-ids <id>; inside one ad set: --level ad --ad-group-ids <id>",
3655
- path: "people",
3656
- args: PEOPLE_ARGS,
3657
- request: adPeopleRequestSchema,
3658
- input: (args) => ({ ...levelTouchInput(args), limit: optionalNumber(args.limit) }),
3659
- compact: compactPeople,
3660
- hints: peopleHints
3661
- });
3662
- var personasCommand = peopleCommand({
3663
- name: "personas",
3664
- schemaName: "personas",
3665
- schemaDescription: `Who the ads brought, split by one persona question \u2014 role, company size, industry\u2026 from the company's Form answers, or work vs personal address, the visit's country or device \u2014 with how many of each reached each ladder step: "which roles from this campaign become customers". Answers fewer than three people gave read as other. ${PEOPLE_NOTE}`,
3666
- help: "People each campaign brought, by role, size, industry... Example: baker ads performance personas --dimension role",
3667
- path: "personas",
3668
- args: PERSONAS_ARGS,
3669
- request: adPersonasRequestSchema,
3670
- input: (args) => ({
3671
- ...levelTouchInput(args),
3672
- dimension: enumValue(args.dimension),
3673
- top: optionalNumber(args.top)
3674
- }),
3675
- compact: compactPersonas,
3676
- hints: personasHints
3677
- });
3678
- var ladderCommand = peopleCommand({
3679
- name: "ladder",
3680
- schemaName: "ladder",
3681
- schemaDescription: "The company's lead-to-customer ladder in force, read off its Funnel (default when the Funnel names no event after a visit: Lead = gave an email or phone, Customer = an amount from a CRM or the company's server), and every event it could be made of from the last --days: Form steps, page events, HubSpot Source events with their stage values, with people per event and the step each counts for now. Read-only; the ladder is the company's Funnel, changed with `baker funnel`.",
3682
- help: "The lead-to-customer steps and what they can be made of. Example: baker ads performance ladder",
3683
- path: "ladder",
3684
- args: SETTINGS_ARGS,
3685
- request: adPeopleSettingsRequestSchema,
3686
- input: (args) => ({ days: optionalNumber(args.days) }),
3687
- compact: roundDeep,
3688
- hints: ladderHints
3689
- });
3690
- var personaFieldsCommand = peopleCommand({
3691
- name: "persona-fields",
3692
- schemaName: "personaFields",
3693
- schemaDescription: "Which Form fields answer which persona question (role, seniority, company_size, industry, use_case, heard_from, budget, country) \u2014 the company's map, else Baker's suggestion from the field names \u2014 and every field people answered in the last --days with its common answers and how often it was kept sealed. Read-only.",
3694
- help: "Which Form fields say who people are. Example: baker ads performance persona-fields",
3695
- path: "persona-fields",
3696
- args: SETTINGS_ARGS,
3697
- request: adPeopleSettingsRequestSchema,
3698
- input: (args) => ({ days: optionalNumber(args.days) }),
3699
- compact: roundDeep,
3700
- hints: personaFieldsHints
3701
- });
3702
-
3703
- // src/commands/ads/performance/progress.ts
3704
- var PROGRESS_ARGS = {
3705
- ...SCOPE_ARGS,
3706
- level: { type: "string", description: "campaign (default), ad_group or ad", required: false },
3707
- status: {
3708
- type: "string",
3709
- description: "Comma list: worsening,mixed,not_comparable,steady,improving,too_early,too_little_data,stopped (default all)",
3710
- required: false
3711
- },
3712
- limit: { type: "string", description: "Rows to return, worst first (default 25, at most 200)", required: false }
3713
- };
3714
- var DIAGNOSE_ARGS = {
3715
- id: { type: "positional", description: "The campaign, ad group or ad id", required: true },
3716
- platform: { type: "string", description: "meta, google, linkedin, x or chatgpt", required: true },
3717
- level: { type: "string", description: "campaign, ad_group or ad", required: true },
3718
- ...SCOPE_ARGS
3719
- };
3720
- var PULSE_ARGS = {
3721
- platforms: SCOPE_ARGS.platforms,
3722
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
3723
- currency: SCOPE_ARGS.currency,
3724
- full: SCOPE_ARGS.full
3725
- };
3726
- function progressInput(args) {
3727
- return {
3728
- ...scopeInput(args),
3729
- level: enumValue(args.level),
3730
- statuses: commaList(args.status)?.map((item) => enumValue(item) ?? item),
3731
- limit: optionalNumber(args.limit)
3732
- };
3733
- }
3734
- function diagnoseInput(args) {
3735
- return {
3736
- ...scopeInput(args),
3737
- platform: enumValue(args.platform),
3738
- level: enumValue(args.level),
3739
- entityId: optionalString(args.id)
3740
- };
3741
- }
3742
- function pulseInput(args) {
3743
- return {
3744
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
3745
- adAccountIds: commaList(args["ad-account-ids"]),
3746
- currency: optionalString(args.currency)
3747
- };
3748
- }
3749
- function nullableRound3(value) {
3750
- return value === null ? null : roundNumber(value);
3751
- }
3752
- function compactRow2(row) {
3753
- return {
3754
- platform: row.platform,
3755
- level: row.level,
3756
- entityId: row.entityId,
3757
- name: row.name,
3758
- status: row.status,
3759
- ...row.confidence ? { confidence: row.confidence } : {},
3760
- ...row.judged ? {
3761
- judged: {
3762
- metric: row.judged.metric,
3763
- recent: nullableRound3(row.judged.recent),
3764
- baseline: nullableRound3(row.judged.baseline),
3765
- changePct: nullableRound3(row.judged.changePct),
3766
- ...row.judged.fallbackFrom ? { fallbackFrom: row.judged.fallbackFrom } : {}
3767
- }
3768
- } : {},
3769
- spend: roundNumber(row.spend),
3770
- currency: row.currency,
3771
- ...row.atStake === null ? {} : { atStake: roundNumber(row.atStake) },
3772
- headline: row.headline,
3773
- next: row.next,
3774
- weekly: row.sparkline.map((point) => nullableRound3(point.value))
3775
- };
3776
- }
3777
- function compactProgress(data) {
3778
- return {
3779
- level: data.level,
3780
- asOf: data.asOf.map((a) => ({ platform: a.platform, date: a.date })),
3781
- counts: Object.fromEntries(Object.entries(data.counts).filter(([, n]) => n > 0)),
3782
- rows: data.rows.map(compactRow2)
3783
- };
3784
- }
3785
- function compactDiagnosis(data) {
3786
- if (data.entity === null || data.diagnosis === null) return { asOf: data.asOf, entity: null };
3787
- const d = data.diagnosis;
3788
- return {
3789
- asOf: data.asOf,
3790
- entity: data.entity,
3791
- status: d.status,
3792
- ...d.confidence ? { confidence: d.confidence } : {},
3793
- headline: d.headline,
3794
- next: d.next,
3795
- judged: roundDeep(d.judged),
3796
- windows: d.windows,
3797
- spend: { recent: roundNumber(d.recentSpend), baseline: roundNumber(d.baselineSpend) },
3798
- ...d.atStake === null ? {} : { atStake: roundNumber(d.atStake) },
3799
- drivers: roundDeep(d.drivers),
3800
- ...d.marginal ? { marginal: roundDeep(d.marginal) } : {},
3801
- ...d.impressionShare ? { impressionShare: roundDeep(d.impressionShare) } : {},
3802
- findings: d.findings.map((f) => ({ lens: f.lens, effect: f.effect, sentence: f.sentence, check: f.check })),
3803
- weekly: data.trajectory.map((p) => ({
3804
- weekEnd: p.weekEnd,
3805
- value: nullableRound3(p.value),
3806
- spend: roundNumber(p.spend)
3807
- })),
3808
- milestones: data.milestones.map((m) => ({ day: m.day, label: m.label }))
3809
- };
3810
- }
3811
- function compactPulse(data) {
3812
- return {
3813
- rows: data.rows.map((row) => {
3814
- const { platform, adAccountId, currency, day, ...metrics } = row;
3815
- return {
3816
- platform,
3817
- adAccountId,
3818
- currency,
3819
- day,
3820
- metrics: Object.fromEntries(
3821
- Object.entries(metrics).map(([name, m]) => [
3822
- name,
3823
- { value: nullableRound3(m.value), mean: nullableRound3(m.mean), ...m.watch ? { watch: true } : {} }
3824
- ])
3825
- )
3826
- };
3827
- })
3828
- };
3829
- }
3830
- var METRIC_WORDS = {
3831
- result_value_per_spend: "return on ad spend",
3832
- cost_per_result: "cost per result",
3833
- cost_per_visit: "cost per landing visit",
3834
- cost_per_link_click: "cost per click",
3835
- cost_per_video_view: "cost per video view",
3836
- cost_per_engagement: "cost per engagement",
3837
- cpm: "cost per 1,000 impressions"
3838
- };
3839
- var SAY_IT = "Report the status with its confidence and the headline's reason; never call a number worse on its own, and never add money across currencies.";
3840
- function progressHints(data) {
3841
- const hints = [SAY_IT];
3842
- for (const row of data.rows.filter((r) => r.status === "not_comparable").slice(0, 3)) {
3843
- hints.push(`"${row.name}" is not_comparable: its goal or how results are counted changed. ${row.next}`);
3844
- }
3845
- const fallbacks = data.rows.filter((r) => r.judged?.fallbackFrom);
3846
- if (fallbacks.length > 0) {
3847
- const sample = fallbacks[0];
3848
- hints.push(
3849
- `${fallbacks.length} row(s) are judged on an upstream metric (e.g. "${sample?.name}" on ${METRIC_WORDS[sample?.judged?.metric ?? ""] ?? "clicks"}): too few results to judge them on results. Say so.`
3850
- );
3851
- }
3852
- if (data.rows.some((r) => r.status === "worsening")) {
3853
- hints.push(
3854
- "Start with the worsening rows: `baker ads performance diagnose <id> --platform <p> --level <l>` shows why, with what to check."
3855
- );
3856
- }
3857
- return hints;
3858
- }
3859
- function diagnosisHints(data, request) {
3860
- if (data.entity === null) {
3861
- return [
3862
- `No ${request.level} ${request.entityId} delivered on ${request.platform} in the six settled weeks before ${data.asOf}. Check the id with \`baker ads performance entities --level ${request.level} --include-idle\`, or pass --end on a day it ran.`
3863
- ];
3864
- }
3865
- const status = data.diagnosis?.status;
3866
- if (status === "not_comparable") return [`Not comparable with before: ${data.diagnosis?.next ?? ""}`];
3867
- if (status === "too_early" || status === "too_little_data")
3868
- return ["Do not change it on these numbers; say when to look again."];
3869
- return [SAY_IT];
3870
- }
3871
- function pulseHints(data) {
3872
- const watched = data.rows.flatMap(
3873
- (row) => Object.entries(row).filter(([, m]) => typeof m === "object" && m !== null && "watch" in m && m.watch === true).map(([name]) => `${row.platform} ${row.adAccountId} ${name} on ${row.day}`)
3874
- );
3875
- return watched.length > 0 ? [
3876
- `Outside the usual range of the 7 days before: ${watched.join("; ")}. One day is a signal to look, not a verdict.`
3877
- ] : [];
3878
- }
3879
-
3880
- // src/commands/ads/performance/search.ts
3881
- var round2 = (value) => value === null ? null : roundNumber(value);
3882
- var money2 = (amount, currency) => `${roundNumber(amount)} ${currency}`;
3883
- function compactSegment(row) {
3884
- return {
3885
- segment: row.segment,
3886
- currency: row.currency,
3887
- spend: roundNumber(row.metrics.spend),
3888
- spendShare: round2(row.spendShare),
3889
- results: roundNumber(row.metrics.results),
3890
- costPerResult: round2(row.ratios.costPerResult),
3891
- ...row.metrics.resultValue > 0 ? { roas: round2(row.ratios.resultValuePerSpend) } : {},
3892
- ctr: round2(row.ratios.ctr),
3893
- cpc: round2(row.ratios.cpc),
3894
- conversionRate: round2(row.ratios.conversionRate),
3895
- ...row.ratios.impressionShare !== null ? { impressionShare: round2(row.ratios.impressionShare) } : {},
3896
- ...row.comparison ? {
3897
- change: roundDeep({
3898
- spend: row.comparison.deltas.spend,
3899
- results: row.comparison.deltas.results,
3900
- costPerResult: row.comparison.deltas.costPerResult,
3901
- conversionRate: row.comparison.deltas.conversionRate,
3902
- impressionSharePoints: row.comparison.impressionSharePoints
3903
- })
3904
- } : {},
3905
- ...row.target ? {
3906
- target: {
3907
- ...roundDeep({ metric: row.target.metric, value: row.target.value }),
3908
- source: row.target.source
3909
- }
3910
- } : {},
3911
- status: row.status
3912
- };
3913
- }
3914
- function compactTerm(row) {
3915
- return {
3916
- searchTerm: row.searchTerm,
3917
- campaignName: row.campaignName,
3918
- termSegment: row.termSegment,
3919
- campaignSegment: row.campaignSegment,
3920
- spend: roundNumber(row.spend),
3921
- clicks: row.clicks,
3922
- conversions: roundNumber(row.conversions)
3923
- };
3924
- }
3925
- function compactBrandSplit(data) {
3926
- return {
3927
- answer: data.answer.sentence,
3928
- window: data.window,
3929
- comparison: data.comparison,
3930
- segments: data.segments.map(compactSegment),
3931
- trend: data.trend.map((point) => ({
3932
- month: point.bucket.slice(0, 7),
3933
- segment: point.segment,
3934
- currency: point.currency,
3935
- spend: roundNumber(point.metrics.spend),
3936
- results: roundNumber(point.metrics.results),
3937
- costPerResult: round2(point.ratios.costPerResult),
3938
- ...point.mature ? {} : { stillCounting: true }
3939
- })),
3940
- leakage: {
3941
- totalRows: data.leakage.totalRows,
3942
- spend: roundDeep(data.leakage.spend),
3943
- top: data.leakage.rows.slice(0, 10).map(compactTerm)
3944
- },
3945
- unsure: { totalRows: data.unsure.totalRows, top: data.unsure.rows.slice(0, 10).map(compactTerm) },
3946
- ...data.reviewed.count > 0 ? { reviewed: data.reviewed.count } : {},
3947
- names: data.names,
3948
- ...data.stillSorting ? { stillSorting: true } : {}
3949
- };
3950
- }
3951
- function brandSplitHints(data) {
3952
- const hints = ["Lead with answer: it is the question this read answers, in one line."];
3953
- if (data.names.brandNamesDefault)
3954
- hints.push(
3955
- `Brand searches are judged against only the company's name and website (${data.names.brandNames.join(", ")}). If it sells products under other names, add them: baker ads performance segments set --brand "<names>".`
3956
- );
3957
- if (data.leakage.totalRows > 0)
3958
- hints.push(
3959
- `${data.leakage.totalRows} brand searches were bought by non-brand or competitor campaigns (${data.leakage.spend.map((s) => money2(s.amount, s.currency)).join(", ")}): brand traffic paid at non-brand prices, and it flatters their cost per result. Adding the brand as a negative keyword there sends it to the brand campaign (a staged Google Ads change). Above pocket change it is also an alert (rule brand_leak) in baker ads performance attention: claim it there, and mark it fixed once the negatives are staged.`
3960
- );
3961
- if (data.unsure.totalRows > 0)
3962
- hints.push(
3963
- `${data.unsure.totalRows} search terms could be brand or not and nobody has answered them yet: list them with baker ads performance search-terms --unsure and settle each with baker ads performance segments set --term "<term>" --segment brand|competitor|non_brand. ${data.reviewed.count > 0 ? `${data.reviewed.count} were already answered by a person: do not ask about those again.` : ""}`.trim()
3964
- );
3965
- if (data.stillSorting)
3966
- hints.push(
3967
- "Some Search campaigns are still being sorted (segment unsorted): their spend joins brand, competitor or non-brand within the hour."
3968
- );
3969
- const nonBrand = data.segments.find((s) => s.segment === "non_brand");
3970
- if (nonBrand && nonBrand.target?.source !== "segment")
3971
- hints.push(
3972
- `Non-brand has no target of its own, so it is ${nonBrand.target ? `judged against the ${nonBrand.target.source === "company" ? "company default" : `${nonBrand.target.source} target`}` : "not judged"}: ask the person what a new customer from search may cost, then baker ads performance segments set --target non_brand --cost <amount> --currency <ISO>.`
3973
- );
3974
- return hints;
3975
- }
3976
- function compactImpressionShare(data) {
3977
- return {
3978
- window: data.window,
3979
- comparison: data.comparison,
3980
- topMeasuredFrom: data.topMeasuredFrom,
3981
- campaigns: data.campaigns.map(
3982
- (row) => roundDeep({
3983
- adAccountId: row.adAccountId,
3984
- campaignId: row.campaignId,
3985
- name: row.name,
3986
- segment: row.segment,
3987
- bidStrategy: row.bidStrategy,
3988
- currency: row.currency,
3989
- spend: row.spend,
3990
- results: row.results,
3991
- now: row.now,
3992
- ...row.points ? { points: row.points } : {},
3993
- ...row.limitedBy ? { limitedBy: row.limitedBy } : {},
3994
- ...row.sharePrimary ? { sharePrimary: true } : {}
3995
- })
3996
- )
3997
- };
3998
- }
3999
- function impressionShareHints(data) {
4000
- const hints = [];
4001
- const budget = data.campaigns.filter((row) => row.limitedBy === "budget");
4002
- const rank = data.campaigns.filter((row) => row.limitedBy === "rank");
4003
- if (budget.length > 0)
4004
- hints.push(
4005
- `Held back by budget: ${budget.map((row) => row.name).join(", ")}. More budget buys the searches they miss \u2014 check which earn it with baker ads performance budget-recommendations before raising any.`
4006
- );
4007
- if (rank.length > 0)
4008
- hints.push(
4009
- `Held back by ad rank (bids and quality): ${rank.map((row) => row.name).join(", ")}. Budget will not fix it; look at bids, ad relevance and landing pages (baker ads performance keyword-quality).`
4010
- );
4011
- if (data.topMeasuredFrom && data.topMeasuredFrom > data.window.from)
4012
- hints.push(
4013
- `Top-of-page rates are measured from ${data.topMeasuredFrom}; earlier days of the window have none, so they read the days since.`
4014
- );
4015
- if (data.campaigns.some((row) => !row.sharePrimary))
4016
- hints.push(
4017
- "Impression share is the goal only for campaigns bidding for it (TARGET_IMPRESSION_SHARE); for the rest it explains volume, it is not a target."
4018
- );
4019
- return hints;
4020
- }
4021
- function compactKeyword(row) {
4022
- return {
4023
- keyword: row.name,
4024
- campaignName: row.campaignName,
4025
- adGroupName: row.adGroupName,
4026
- qualityScore: row.qualityScore,
4027
- expectedCtr: row.expectedCtr,
4028
- adRelevance: row.adRelevance,
4029
- landingPageExperience: row.landingPageExperience,
4030
- ...row.page ? { page: `${row.page.host}${row.page.path}`, bakerPage: row.page.baker } : {},
4031
- currency: row.currency,
4032
- spend: roundNumber(row.spend),
4033
- clicks: row.clicks,
4034
- results: roundNumber(row.results)
4035
- };
4036
- }
4037
- function compactKeywordQuality(data) {
4038
- return {
4039
- window: data.window,
4040
- summary: roundDeep(data.summary),
4041
- landings: roundDeep(data.landings.slice(0, 15)),
4042
- keywords: data.rows.slice(0, 50).map(compactKeyword),
4043
- totalRows: data.totalRows
4044
- };
4045
- }
4046
- function keywordQualityHints(data) {
4047
- const weakBaker = data.landings.filter((page) => page.baker && page.belowAverageSpend > 0);
4048
- const weakOther = data.landings.filter((page) => !page.baker && page.belowAverageSpend > 0);
4049
- const hints = [];
4050
- if (weakBaker.length > 0)
4051
- hints.push(
4052
- `Google rates the landing page experience below average on Baker pages: ${weakBaker.slice(0, 3).map((page) => `${page.host}${page.path} (${money2(page.belowAverageSpend, page.currency)})`).join(", ")}. Improve them: baker landing critique, then make the page answer what those keywords search for.`
4053
- );
4054
- if (weakOther.length > 0)
4055
- hints.push(
4056
- `Pages Baker does not build also rate below average (${weakOther.length}); a Baker landing matched to those keywords is one fix.`
4057
- );
4058
- const weakCtr = data.summary.some((s) => s.belowAverage.expectedCtr > 0);
4059
- if (weakCtr)
4060
- hints.push(
4061
- "Below-average expected click rate means the ads do not match what people search: tighten the ad groups' headlines to the keywords."
4062
- );
4063
- return hints;
4064
- }
4065
- function compactGoogleReport(data) {
4066
- return {
4067
- window: data.window,
4068
- snapshot: roundDeep(
4069
- data.snapshot.map((s) => ({
4070
- currency: s.currency,
4071
- metrics: s.metrics,
4072
- ratios: s.ratios,
4073
- ...s.comparison ? { change: s.comparison.deltas } : {}
4074
- }))
4075
- ),
4076
- budgets: data.budgets ? compactBudgets(data.budgets).rows : null,
4077
- brandSplit: compactBrandSplit(data.brandSplit),
4078
- byCampaignType: data.byCampaignType.map((point) => ({
4079
- month: point.bucket.slice(0, 7),
4080
- type: point.groupKey,
4081
- currency: point.currency,
4082
- spend: roundNumber(point.metrics.spend),
4083
- results: roundNumber(point.metrics.results)
4084
- })),
4085
- campaigns: data.campaigns.map(compactEntityRow),
4086
- impressionShare: compactImpressionShare(data.impressionShare).campaigns,
4087
- topAdGroups: data.topAdGroups.map(compactEntityRow),
4088
- topKeywords: data.topKeywords.map(compactEntityRow),
4089
- keywordQuality: roundDeep(data.keywordQuality),
4090
- attention: data.attention.map((item) => ({
4091
- tier: item.tier,
4092
- title: item.title,
4093
- detail: item.detail,
4094
- ...item.moneyAtStake !== null ? { moneyAtStake: roundNumber(item.moneyAtStake) } : {}
4095
- }))
4096
- };
4097
- }
4098
- function googleReportHints(data) {
4099
- const stopped = data.campaigns.filter((row) => row.metrics.spend === 0 && (row.comparison?.previous.spend ?? 0) > 0);
4100
- return [
4101
- "Write the report from these sections, each number labelled with where it comes from (reported by Google, seen on the site, from the CRM). Judge brand and non-brand apart: never blend their cost per result.",
4102
- ...stopped.length > 0 ? [
4103
- `${stopped.length} campaigns spent in the period before and nothing now: check baker ads performance changes --entity-ids <id> for who paused them before calling it a drop.`
4104
- ] : [],
4105
- ...brandSplitHints(data.brandSplit),
4106
- ...impressionShareHints(data.impressionShare)
4107
- ];
4108
- }
4109
- function budgetMoney(amount, currency) {
4110
- return new Intl.NumberFormat("en-US", { style: "currency", currency, maximumFractionDigits: 0 }).format(amount);
4111
- }
4112
- function shortDay(day) {
4113
- return (/* @__PURE__ */ new Date(`${day}T00:00:00Z`)).toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" });
4114
- }
4115
- var STATUS_PHRASE = {
4116
- on_track: "on track",
4117
- will_underspend: "will underspend",
4118
- will_run_out: "will run out early",
4119
- over_budget: "already over budget",
4120
- no_rate: "no exchange rate converts the spend, so it is not judged"
4121
- };
4122
- function budgetHeadline(row, asOf) {
4123
- const money3 = (amount) => budgetMoney(amount, row.budget.currency);
4124
- if (row.spent === null) return `${row.label}: ${STATUS_PHRASE.no_rate}.`;
4125
- const pace = row.projected === null ? "" : `; at the last 7 days' pace the month ends near ${money3(row.projected)}`;
4126
- const runsOut = row.runsOutOn ? ` around ${shortDay(row.runsOutOn)}` : "";
4127
- return `${row.label}: ${money3(row.spent)} of ${money3(row.budget.amount)} spent by ${shortDay(asOf)}, against ${money3(row.plannedByNow)} planned by then${pace} \u2014 ${STATUS_PHRASE[row.status]}${runsOut}.`;
4128
- }
4129
- var whole = (amount) => amount === null ? null : Math.round(amount);
4130
- var CAPPED_FROM = "Google Ads impression share: lost more searches to budget than to rank";
4131
- function compactBudgets(data) {
4132
- const labels = new Map(data.rows.map((row) => [row.key, row.label]));
4133
- return {
4134
- month: data.month,
4135
- asOf: data.asOf,
4136
- daysLeft: data.daysLeft,
4137
- rows: data.rows.map(
4138
- (row) => roundDeep({
4139
- headline: budgetHeadline(row, data.asOf),
4140
- label: row.label,
4141
- status: row.status,
4142
- budget: row.budget.amount,
4143
- currency: row.budget.currency,
4144
- spent: whole(row.spent),
4145
- plannedByNow: Math.round(row.plannedByNow),
4146
- projected: whole(row.projected),
4147
- ...row.runsOutOn ? { runsOutOn: row.runsOutOn } : {},
4148
- remaining: whole(row.remaining),
4149
- dailyNeeded: row.dailyNeeded,
4150
- ...row.partOf ? { partOf: labels.get(row.partOf) ?? row.partOf } : {},
4151
- ...row.partsTotal !== null && row.partsTotal > row.budget.amount ? { partsTotal: row.partsTotal } : {},
4152
- ...row.cappedCampaigns ? { cappedCampaigns: { count: row.cappedCampaigns, from: CAPPED_FROM } } : {}
4153
- })
4154
- )
4155
- };
4156
- }
4157
- function budgetsHints(data) {
4158
- if (data.budgets.length === 0)
4159
- return [
4160
- "No monthly budget is set, so nothing paces against a plan. Ask the person what they plan to spend a month, then: baker ads performance budgets set --amount <n> --currency <ISO>."
4161
- ];
4162
- const hints = [
4163
- "Tell the person each headline as it is. Budgets are independent: a budget inside another (partOf) is paced on its own, and is shown nested only for reading."
4164
- ];
4165
- for (const row of data.rows)
4166
- if (row.partsTotal !== null && row.partsTotal > row.budget.amount)
4167
- hints.push(
4168
- `The budgets inside ${row.label} add up to more than ${row.label} itself: ask which number is the real plan before judging the pace.`
4169
- );
4170
- if (data.rows.some((row) => (row.cappedCampaigns ?? 0) > 0))
4171
- hints.push(
4172
- "cappedCampaigns: Search campaigns whose daily budget ran out on some days this month. Name them with baker ads performance impression-share; next to will_underspend it means the monthly plan has room and those daily budgets are low."
4173
- );
4174
- return hints;
4175
- }
4176
- var text2 = (raw) => typeof raw === "string" && raw.trim() !== "" ? raw.trim() : void 0;
4177
- function choiceOf(args) {
4178
- const key = text2(args.campaign) ?? text2(args.term);
4179
- if (!key || text2(args.target)) return void 0;
4180
- return {
4181
- kind: text2(args.campaign) ? "campaign" : "term",
4182
- adAccountId: text2(args.account) ?? "",
4183
- key,
4184
- ...text2(args.label) ? { label: text2(args.label) } : {},
4185
- segment: args.clear === true ? null : enumValue(args.segment) ?? null
4186
- };
4187
- }
4188
- function targetOf(args) {
4189
- const segment = text2(args.target);
4190
- if (!segment) return void 0;
4191
- const roas = optionalNumber(args.roas);
4192
- return {
4193
- segment: enumValue(segment) ?? segment,
4194
- metric: roas !== void 0 ? "roas" : "cost_per_result",
4195
- value: args.clear === true ? null : roas ?? optionalNumber(args.cost) ?? null,
4196
- ...text2(args.currency) ? { currency: text2(args.currency) } : {}
4197
- };
4198
- }
4199
- function segmentsSetInput(args, chatId) {
4200
- const choice = choiceOf(args);
4201
- const target = targetOf(args);
4202
- return {
4203
- chatId,
4204
- ...args.brand !== void 0 ? { brandNames: commaList(args.brand) ?? [] } : {},
4205
- ...args.competitors !== void 0 ? { competitorNames: commaList(args.competitors) ?? [] } : {},
4206
- ...choice ? { choice } : {},
4207
- ...target ? { target } : {}
4208
- };
4209
- }
4210
- function budgetSetInput(args, chatId) {
4211
- const platform = text2(args.platform);
4212
- return {
4213
- chatId,
4214
- ...platform ? { platform: enumValue(platform) ?? platform } : {},
4215
- ...text2(args.account) ? { adAccountId: text2(args.account) } : {},
4216
- segment: enumValue(args.segment) ?? "all",
4217
- amount: args.clear === true ? null : optionalNumber(args.amount) ?? null,
4218
- ...text2(args.currency) ? { currency: text2(args.currency) } : {}
4219
- };
4220
- }
4221
- var MONTHS_ARG = {
4222
- months: {
4223
- type: "string",
4224
- description: "Months of history for the trend, ending this month (default 6)",
4225
- required: false
4226
- }
4227
- };
4228
- var brandSplitCommand = report({
4229
- name: "brand-split",
4230
- schemaName: "brandSplit",
4231
- schemaDescription: `Google Search split into brand (searches for the company or its products), competitor and non-brand, each with spend, its share, results, cost per result, click rate, cost per click, conversion rate and impression share, against the settled days before, and against its own target (segments set --target). Never blend brand and non-brand: brand converts cheaply because people already chose the company. Also: the month-by-month trend per segment, brand searches bought by non-brand campaigns (leakage), and terms Baker was unsure of. Segments are read by Baker from the brand and competitor names (segments) and per campaign from what its keywords spent on; other = not Search (Performance Max, Shopping, Video, Demand Gen). ${STORE_NOTE}`,
4232
- help: "Brand against non-brand Google Search. Example: baker ads performance brand-split --days 30",
4233
- path: "brand-split",
4234
- args: { ...SCOPE_ARGS, ...MONTHS_ARG },
4235
- request: adPerformanceBrandSplitRequestSchema,
4236
- input: (args) => ({ ...scopeInput(args), months: optionalNumber(args.months) }),
4237
- compact: compactBrandSplit,
4238
- hints: brandSplitHints
4239
- });
4240
- var impressionShareCommand = report({
4241
- name: "impression-share",
4242
- schemaName: "impressionShare",
4243
- schemaDescription: `Per Google Search campaign: the share of the searches it could have shown on (impressionShare), the share lost to budget and to ad rank, and how often its ads showed above the organic results and first of all \u2014 now, and the change in points against the settled days before. limitedBy names the reason that dominates: budget (more budget buys the missed searches) or rank (bids, ad relevance and landing pages do). Impression share is a goal only for campaigns bidding for it (sharePrimary). ${STORE_NOTE}`,
4244
- help: "Where Search ads show and why they miss searches. Example: baker ads performance impression-share --days 30",
4245
- path: "impression-share",
4246
- args: { ...SCOPE_ARGS, limit: { type: "string", description: "Campaigns to return (default 50)", required: false } },
4247
- request: adPerformanceImpressionShareRequestSchema,
4248
- input: (args) => ({ ...scopeInput(args), limit: optionalNumber(args.limit) }),
4249
- compact: compactImpressionShare,
4250
- hints: impressionShareHints
4251
- });
4252
- var keywordQualityCommand = report({
4253
- name: "keyword-quality",
4254
- schemaName: "keywordQuality",
4255
- schemaDescription: `Google Search keywords with their quality score (1\u201310) and its three parts as Google rates them \u2014 expected click rate, ad relevance, landing page experience \u2014 with spend, clicks and results, and the page each sends people to (bakerPage: a Baker landing the agent can improve). The summary weighs the score by spend and counts spend on weak parts; landings lists pages by spend on below-average landing experience. ${STORE_NOTE}`,
4256
- help: "Keyword quality and the pages they lead to. Example: baker ads performance keyword-quality --max-quality-score 5",
4257
- path: "keyword-quality",
4258
- args: {
4259
- ...SCOPE_ARGS,
4260
- "max-quality-score": {
4261
- type: "string",
4262
- description: "Only keywords scored at or under this (1\u201310)",
4263
- required: false
4264
- },
4265
- limit: { type: "string", description: "Keywords to return (default 100)", required: false }
4266
- },
4267
- request: adPerformanceKeywordQualityRequestSchema,
4268
- input: (args) => ({
4269
- ...scopeInput(args),
4270
- maxQualityScore: optionalNumber(args["max-quality-score"]),
4271
- limit: optionalNumber(args.limit)
4272
- }),
4273
- compact: compactKeywordQuality,
4274
- hints: keywordQualityHints
4275
- });
4276
- var googleReportCommand = report({
4277
- name: "google-report",
4278
- schemaName: "googleReport",
4279
- schemaDescription: `Every section of a Google Ads review in one read, for writing a weekly or monthly report: the snapshot against the settled days before, monthly budgets and pacing, brand against non-brand (with leakage), spend by campaign type month by month, every campaign with its change (and the ones that stopped), impression share, the ad groups and keywords that brought most results, keyword quality, and what needs attention. Each section is what its own command answers. ${STORE_NOTE}`,
4280
- help: "A whole Google Ads review in one read. Example: baker ads performance google-report --days 7",
4281
- path: "google-report",
4282
- args: { ...SCOPE_ARGS, ...MONTHS_ARG },
4283
- request: adPerformanceGoogleReportRequestSchema,
4284
- input: (args) => ({ ...scopeInput(args), months: optionalNumber(args.months) }),
4285
- compact: compactGoogleReport,
4286
- hints: googleReportHints
4287
- });
4288
- var budgetsShowCommand = report({
4289
- name: "budgets",
4290
- schemaName: "budgets",
4291
- schemaDescription: `The company's monthly ad budgets and how this month paces against each, each led by a headline to tell the person as is. status is one rule, the same the dashboard shows: over_budget, will_run_out (runsOutOn: the day the money ends at the last 7 days' pace), will_underspend (the month ends more than 5% short), on_track. The numbers behind it, in the budget's currency: spent so far, plannedByNow (the budget spread evenly over the month up to today), projected (where the last 7 days' pace ends the month), what is left and what each day left can spend. A budget covers one ad account or every one, all its spend or one Google Search segment (brand, competitor, non_brand); partOf names the wider budget it sits in (budgets stay independent). cappedCampaigns: Search campaigns whose daily budget ran out on some days, counted under the narrowest budget. ${STORE_NOTE}`,
4292
- help: "Monthly budgets and pacing. Example: baker ads performance budgets",
4293
- path: "budgets",
4294
- args: {
4295
- month: { type: "string", description: "YYYY-MM (default the current month)", required: false },
4296
- platforms: SCOPE_ARGS.platforms,
4297
- "ad-account-ids": SCOPE_ARGS["ad-account-ids"],
4298
- full: SCOPE_ARGS.full
4299
- },
4300
- request: adPerformanceBudgetsRequestSchema,
4301
- input: (args) => ({
4302
- month: optionalString(args.month),
4303
- platforms: commaList(args.platforms)?.map((p) => enumValue(p) ?? p),
4304
- adAccountIds: commaList(args["ad-account-ids"])
4305
- }),
4306
- compact: compactBudgets,
4307
- hints: budgetsHints
4308
- });
4309
- var budgetsSetCommand = writeCommand({
4310
- name: "set",
4311
- schemaName: "budgets.set",
4312
- schemaDescription: "Stage a monthly ad budget on this chat; it applies when the person publishes the chat. Every account (no --platform/--account) or one ad account (--platform --account), all its spend (default) or one Google Search segment (--segment brand|competitor|non_brand). --amount with --currency (one of the accounts' currencies); --clear removes it. A budget a person changed meanwhile stays as they set it. Ask the person: a budget is their plan, not a guess.",
4313
- help: "Stage a monthly budget.\n\nExamples:\n baker ads performance budgets set --amount 45000 --currency USD\n baker ads performance budgets set --platform google --account 123 --segment brand --amount 30000 --currency USD\n baker ads performance budgets set --platform google --account 123 --segment brand --clear",
4314
- path: "/api/ads/performance/budgets/set",
4315
- args: {
4316
- platform: {
4317
- type: "string",
4318
- description: "meta | google | linkedin | x | chatgpt (omit for every account)",
4319
- required: false
4320
- },
4321
- account: { type: "string", description: "The ad account id (with --platform)", required: false },
4322
- segment: {
4323
- type: "string",
4324
- description: "all (default), brand, competitor or non_brand (Google Search)",
4325
- required: false
4326
- },
4327
- amount: { type: "string", description: "What the month may spend", required: false },
4328
- currency: { type: "string", description: "The amount's currency, e.g. EUR", required: false },
4329
- clear: { type: "boolean", description: "Remove the budget", required: false }
4330
- },
4331
- request: adPerformanceBudgetsSetRequestSchema,
4332
- input: budgetSetInput
4333
- });
4334
- var segmentsShowCommand = report({
4335
- name: "segments",
4336
- schemaName: "segments",
4337
- schemaDescription: "What decides brand, competitor and non-brand Google searches: the company's own names (its name and website unless set), its competitors' names (plus the brands it follows in the Ads library), a person's choices for single campaigns and search terms (they always win over Baker's reading), and each segment's target.",
4338
- help: "What decides brand and competitor searches. Example: baker ads performance segments",
4339
- path: "segments",
4340
- args: { full: SCOPE_ARGS.full },
4341
- request: adPerformanceSegmentsRequestSchema,
4342
- input: () => ({}),
4343
- compact: (data) => roundDeep(data)
4344
- });
4345
- var segmentsSetCommand = writeCommand({
4346
- name: "set",
4347
- schemaName: "segments.set",
4348
- schemaDescription: `Stage on this chat what decides brand and non-brand; applies when the person publishes it, then every search term and campaign is sorted again. --brand replaces the company's own names (company, website, product names; comma list); --competitors replaces competitor names. --campaign <id> --account <id> --segment brand|competitor|non_brand|other (other leaves it out of the split), or --term "<search term>" --segment brand|competitor|non_brand, sets a person's choice that wins over Baker's reading; --clear removes the choice. --target brand|competitor|non_brand with --cost <amount> --currency <ISO> or --roas <ratio> sets that segment's own target (--clear removes it). Ask the person before changing names or targets.`,
4349
- help: 'Stage brand names, a segment choice or a segment target.\n\nExamples:\n baker ads performance segments set --brand "Cubbo, cubbo.com, Cubbo Engage" --competitors "Skydropx, Melonn"\n baker ads performance segments set --campaign 987 --account 123 --segment brand\n baker ads performance segments set --term "cubo envios" --segment brand\n baker ads performance segments set --target non_brand --cost 40 --currency EUR',
4350
- path: "/api/ads/performance/segments/set",
4351
- args: {
4352
- brand: {
4353
- type: "string",
4354
- description: "Comma list: the company's own names, website and products",
4355
- required: false
4356
- },
4357
- competitors: { type: "string", description: "Comma list: competitor companies and products", required: false },
4358
- campaign: { type: "string", description: "A Google campaign id (with --account and --segment)", required: false },
4359
- account: { type: "string", description: "The campaign's Google Ads account id", required: false },
4360
- term: { type: "string", description: "A search term or keyword text (with --segment)", required: false },
4361
- label: { type: "string", description: "How the review names it (default the term or id)", required: false },
4362
- segment: {
4363
- type: "string",
4364
- description: "brand, competitor, non_brand, or other (campaigns only)",
4365
- required: false
4366
- },
4367
- target: {
4368
- type: "string",
4369
- description: "brand, competitor or non_brand: set that segment's target",
4370
- required: false
4371
- },
4372
- cost: { type: "string", description: "Target cost per result (with --target and --currency)", required: false },
4373
- roas: { type: "string", description: "Target return on ad spend as a ratio (with --target)", required: false },
4374
- currency: { type: "string", description: "The target's currency, e.g. EUR", required: false },
4375
- clear: { type: "boolean", description: "Remove the choice or the target", required: false }
4376
- },
4377
- request: adPerformanceSegmentsSetRequestSchema,
4378
- input: segmentsSetInput
4379
- });
4380
- var searchCommands = {
4381
- "brand-split": brandSplitCommand,
4382
- "impression-share": impressionShareCommand,
4383
- "keyword-quality": keywordQualityCommand,
4384
- "google-report": googleReportCommand,
4385
- budgets: withWrites(budgetsShowCommand, { set: budgetsSetCommand }),
4386
- segments: withWrites(segmentsShowCommand, { set: segmentsSetCommand })
4387
- };
4388
-
4389
- // src/commands/ads/performance/setup.ts
4390
- var CHECKS = ["connected", "synced", "conversions"];
4391
- function problemsOf(account) {
4392
- return Object.fromEntries(
4393
- CHECKS.flatMap((check) => account[check].state === "problem" ? [[check, account[check].label]] : [])
4394
- );
4395
- }
4396
- function compactSetup(data) {
4397
- const problems = data.accounts.filter((account) => Object.keys(problemsOf(account)).length > 0);
4398
- const waiting = data.accounts.filter(
4399
- (account) => !problems.includes(account) && CHECKS.some((check) => account[check].state === "waiting")
4400
- );
4401
- return {
4402
- headline: data.headline,
4403
- toFix: data.toFix,
4404
- ...problems.length > 0 ? {
4405
- problems: problems.map((account) => ({
4406
- account: account.name,
4407
- platform: account.platform,
4408
- ...problemsOf(account)
4409
- }))
4410
- } : {},
4411
- ...waiting.length > 0 ? {
4412
- waiting: waiting.map((account) => ({
4413
- account: account.name,
4414
- platform: account.platform,
4415
- ...Object.fromEntries(
4416
- CHECKS.flatMap((check) => account[check].state === "waiting" ? [[check, account[check].label]] : [])
4417
- )
4418
- }))
4419
- } : {},
4420
- fine: data.accounts.length - problems.length - waiting.length,
4421
- ...data.tracking.length > 0 ? { tracking: data.tracking } : {}
4422
- };
4423
- }
4424
- function setupHints(data) {
4425
- const hints = ["Tell the person the headline as it is, then the fixes in the order of toFix."];
4426
- if (data.toFix.some((entry) => entry.fix.startsWith("Reconnect")))
4427
- hints.push(
4428
- "A reconnection is a person's to do in Brain \u2192 Integrations: you cannot do it. Say which platform and what stops until they do."
4429
- );
4430
- if (data.toFix.some((entry) => entry.fix.includes("Goals")))
4431
- hints.push(
4432
- "For an account with no conversions, read what it optimises for with baker ads performance targets, then stage what counts as a result with baker ads performance goals set."
4433
- );
4434
- return hints;
4435
- }
4436
- var setupCommand = report({
4437
- name: "setup",
4438
- schemaName: "setup",
4439
- schemaDescription: `Whether the ads are set up to be read, in one headline: per ad account connected, numbers synced and conversions counted over 30 days; per platform whether visits from its ads name their ad. toFix lists what a person has to do, in order, with the fix; problems names the accounts with one; fine counts the rest. The same checks as Measure \u2192 Ads \u2192 Setup. ${STORE_NOTE}`,
4440
- help: "Is everything set up? Example: baker ads performance setup",
4441
- path: "setup",
4442
- args: { platforms: SCOPE_ARGS.platforms, "ad-account-ids": SCOPE_ARGS["ad-account-ids"] },
4443
- request: adPerformanceSetupRequestSchema,
4444
- input: (args) => ({
4445
- platforms: commaList(args.platforms)?.map((item) => enumValue(item) ?? item),
4446
- adAccountIds: commaList(args["ad-account-ids"])
4447
- }),
4448
- compact: compactSetup,
4449
- hints: setupHints
4450
- });
4451
-
4452
- // src/commands/ads/performance/index.ts
4453
- var overviewCommand = report({
4454
- name: "overview",
4455
- schemaName: "overview",
4456
- schemaDescription: `Start here for any ad performance question. Every connected platform's score for the window (spend, results, cost per result, against the settled days before), totals per currency, a trend, matureThrough per platform (the last day whose results have settled), the result goals in force, open alerts and data status. Default the last 30 complete days. ${STORE_NOTE}`,
4457
- help: "Every ad platform's results at a glance. Example: baker ads performance overview --days 30",
4458
- path: "overview",
4459
- args: SCOPE_ARGS,
4460
- request: adPerformanceOverviewRequestSchema,
4461
- input: scopeInput,
4462
- compact: compactOverview,
4463
- hints: overviewHints
4464
- });
4465
- var entitiesCommand = report({
4466
- name: "entities",
4467
- schemaName: "entities",
4468
- schemaDescription: `Campaigns, ad groups / ad sets, ads or keywords across platforms, ranked (default by spend), with metrics, budget, first delivery (beforeHistory: it ran before the stored history starts) and peer shares against its parent. Paged with --limit/--offset; totalRows counts every match. ${STORE_NOTE}`,
4469
- help: "Campaigns, ad groups, ads or keywords ranked. Example: baker ads performance entities --level ad --sort cost_per_result --order asc",
4470
- path: "entities",
4471
- args: ENTITIES_ARGS,
4472
- request: adPerformanceEntitiesRequestSchema,
4473
- input: entitiesInput,
4474
- compact: compactEntities,
4475
- hints: (data, request) => entitiesHints(data, request)
4476
- });
4477
- var entityCommand = report({
4478
- name: "entity",
4479
- schemaName: "entity",
4480
- schemaDescription: `Read this before changing a campaign, ad group, ad or keyword. One entity's numbers, its daily series (mature: settled), its change history, its children and open alerts, and a verdict: enoughData (is there enough settled data to judge it), daysSinceChange, learning, and safeBudgetStep (the largest budget step that keeps learning, and how many days to wait). ${STORE_NOTE}`,
4481
- help: "One entity before you change it, with a verdict. Example: baker ads performance entity 120211 --platform meta --level campaign",
4482
- path: "entity",
4483
- args: ENTITY_ARGS,
4484
- request: adPerformanceEntityRequestSchema,
4485
- input: entityInput,
4486
- compact: compactEntity,
4487
- hints: entityHints
4488
- });
4489
- var changesCommand = report({
4490
- name: "changes",
4491
- schemaName: "changes",
4492
- schemaDescription: `What changed in the ad accounts and who changed it: status, budget, bid, schedule, creative, targeting edits, by a person, by the platform's automation, or published from Baker (--actor-kinds baker). A Baker change also seen in the platform's own log or sync is one row, Baker's: chatTitle names the chat it was published from, platformCheck says whether the platform shows it (seen | not_seen). Every other row was made outside Baker. Newest first, paged with --limit/--offset; totalRows counts every match. ${STORE_NOTE}`,
4493
- help: "Changes made to the ad accounts, and by whom. Example: baker ads performance changes --kinds budget,status --days 14",
4494
- path: "changes",
4495
- args: CHANGES_ARGS,
4496
- request: adPerformanceChangesRequestSchema,
4497
- input: changesInput,
4498
- compact: compactChanges
4499
- });
4500
- var impactCommand = report({
4501
- name: "impact",
4502
- schemaName: "impact",
4503
- schemaDescription: `Did a change work? Each change's settled days before against the days after (--impact-days, default 7), against what the rest of the ad account did meanwhile. verdict: better | worse | unclear | too_early; effect is relative (0.2 = +20%); confounded when another change overlaps. --actor-kinds baker checks the changes Baker published. Newest first, paged with --limit/--offset; totalRows counts every match. ${STORE_NOTE}`,
4504
- help: "Whether recent changes worked. Example: baker ads performance impact --actor-kinds baker --days 60",
4505
- path: "impact",
4506
- args: IMPACT_ARGS,
4507
- request: adPerformanceImpactRequestSchema,
4508
- input: impactInput,
4509
- compact: compactImpact,
4510
- hints: impactHints
4511
- });
4512
- var creativesCommand = report({
4513
- name: "creatives",
4514
- schemaName: "creatives",
4515
- schemaDescription: `Which creatives win. The same creative across ads counts once (--group-by ad to split). peerIndex compares each with its ad-group siblings and is shrunk toward 1 when data is thin; peerStanding says above_peers | like_peers | below_peers | too_little_data. Rank by it, not by raw cost per result. ${STORE_NOTE}`,
4516
- help: "Creatives against their siblings. Example: baker ads performance creatives --platforms meta --min-spend 50",
4517
- path: "creatives",
4518
- args: CREATIVES_ARGS,
4519
- request: adPerformanceCreativesRequestSchema,
4520
- input: creativesInput,
4521
- compact: compactCreatives,
4522
- hints: creativesHints
4523
- });
4524
- var breakdownCommand = report({
4525
- name: "breakdown",
4526
- schemaName: "breakdown",
4527
- schemaDescription: `Spend and results split by placement, country or device, with each value's share of spend and of results. Meta only: age, gender, and audience_segment (prospecting | engaged | existing | unclassified \u2014 Advantage+ sales campaigns' customer segments; unclassified elsewhere). ${STORE_NOTE}`,
4528
- help: "Results by placement, country, device, age, gender or audience segment. Example: baker ads performance breakdown --dimension placement",
4529
- path: "breakdown",
4530
- args: BREAKDOWN_ARGS,
4531
- request: adPerformanceBreakdownRequestSchema,
4532
- input: breakdownInput
4533
- });
4534
- var searchTermsCommand = report({
4535
- name: "search-terms",
4536
- schemaName: "searchTerms",
4537
- schemaDescription: `Google Ads search terms that triggered the ads, with the keyword they matched, spend, clicks and conversions. --zero-results keeps only terms with no conversions: the negative-keyword candidates. Paged with --limit/--offset. ${STORE_NOTE}`,
4538
- help: "Google Ads search terms. Example: baker ads performance search-terms --zero-results --min-clicks 5",
4539
- path: "search-terms",
4540
- args: SEARCH_TERMS_ARGS,
4541
- request: adPerformanceSearchTermsRequestSchema,
4542
- input: searchTermsInput
4543
- });
4544
- var pacingCommand = report({
4545
- name: "pacing",
4546
- schemaName: "pacing",
4547
- schemaDescription: `Month-to-date spend per ad account, the last 7 days, the active daily budgets, and the spend projected to the month's end. ${STORE_NOTE}`,
4548
- help: "Month spend so far and where it lands. Example: baker ads performance pacing --month 2026-10",
4549
- path: "pacing",
4550
- args: PACING_ARGS,
4551
- request: adPerformancePacingRequestSchema,
4552
- input: pacingInput
4553
- });
4554
- var funnelCommand = report({
4555
- name: "funnel",
4556
- schemaName: "funnel",
4557
- schemaDescription: `From ad to site: spend and link clicks beside the visits, engaged visits and site conversions they brought, per campaign, ad group or ad (--by-landing splits by the page entered). clickToSession below ~0.6 means clicks are not arriving, or not arriving tagged. ${STORE_NOTE}`,
4558
- help: "Ad spend to site visits and conversions. Example: baker ads performance funnel --level campaign --by-landing",
4559
- path: "funnel",
4560
- args: FUNNEL_ARGS,
4561
- request: adPerformanceFunnelRequestSchema,
4562
- input: funnelInput,
4563
- hints: funnelHints
4564
- });
4565
- var trackingCommand = report({
4566
- name: "tracking",
4567
- schemaName: "tracking",
4568
- schemaDescription: `Can paid visits be tied back to their ads? Per platform: the share of paid visits carrying a click id, campaign id and ad id, and the active ads that send no tracking parameters (with their last 7 days of spend). ${STORE_NOTE}`,
4569
- help: "Whether ad clicks arrive tagged. Example: baker ads performance tracking",
4570
- path: "tracking",
4571
- args: SCOPE_ARGS,
4572
- request: adPerformanceTrackingRequestSchema,
4573
- input: scopeInput,
4574
- hints: trackingHints
4575
- });
4576
- var landingsCommand = report({
4577
- name: "landings",
4578
- schemaName: "landings",
4579
- schemaDescription: `Which landing pages the active ads send people to: ads, campaigns and platforms per page, spend per currency, paid visits and their site conversions. ${STORE_NOTE}`,
4580
- help: "Landing pages the ads point at. Example: baker ads performance landings --days 14",
4581
- path: "landings",
4582
- args: SCOPE_ARGS,
4583
- request: adPerformanceLandingsRequestSchema,
4584
- input: scopeInput,
4585
- hints: clippedWindowHint
4586
- });
4587
- var goalsCommand = report({
4588
- name: "goals",
4589
- schemaName: "goals",
4590
- schemaDescription: "Which conversion actions count as results today (goals, per platform) and every action each platform reported in the last --days (default 90), with inGoal. Read-only.",
4591
- help: "Conversion actions and which count as results. Example: baker ads performance goals",
4592
- path: "goals",
4593
- args: GOALS_ARGS,
4594
- request: adPerformanceGoalsRequestSchema,
4595
- input: goalsInput,
4596
- hints: goalsHints
4597
- });
4598
- var statusRequest = z15.object({});
4599
- var statusCommand = report({
4600
- name: "status",
4601
- schemaName: "status",
4602
- schemaDescription: "How fresh the stored ad data is: per platform, whether it is connected, when it last synced, any sync error, and per selected ad account the days stored, whether its history has finished loading, and adsInManyAdGroups \u2014 ads whose numbers in the last 30 days came from more than one ad group or campaign and are added together under one ad.",
4603
- help: "How fresh the stored ad data is. Example: baker ads performance status",
4604
- path: "status",
4605
- args: STATUS_ARGS,
4606
- request: statusRequest,
4607
- input: () => ({}),
4608
- compact: compactStatus,
4609
- hints: statusHints
4610
- });
4611
- var GETTING_WORSE_NOTE = "Recent = the last 2 settled weeks, baseline = the 4 before (each platform's last settled day: asOf). Judged on what the entity is for (ROAS for sales with value, cost per result for leads, cost per click for traffic, CPM for awareness), falling back to an upstream metric when results are too few (judged.fallbackFrom). Status: worsening | improving | steady | mixed | not_comparable (its goal or counting changed) | too_early | too_little_data | stopped; confidence strong | moderate | weak.";
4612
- var progressCommand = report({
4613
- name: "progress",
4614
- schemaName: "progress",
4615
- schemaDescription: `Is it really getting better or worse? Every campaign, ad group or ad, worst first: status, confidence, the judged metric recent vs baseline, money at stake, a headline that says why (a measurement change, the market, scaling, fatigue, one child, the landing page\u2026), what to check next, and the weekly line. --status filters, e.g. worsening,mixed. ${GETTING_WORSE_NOTE} ${STORE_NOTE}`,
4616
- help: "Worst-first: what is really getting worse, and why. Example: baker ads performance progress --level campaign --status worsening",
4617
- path: "progress",
4618
- args: PROGRESS_ARGS,
4619
- request: adPerformanceProgressRequestSchema,
4620
- input: progressInput,
4621
- compact: compactProgress,
4622
- hints: progressHints
4623
- });
4624
- var diagnoseCommand = report({
4625
- name: "diagnose",
4626
- schemaName: "diagnose",
4627
- schemaDescription: `Why one campaign, ad group or ad is getting better or worse: status and confidence, the judged metric, which part of the chain moved (impression prices, click rate, conversion rate after the click), the marginal cost when scaling, every finding with what to check, its weekly line up to a year and the changes on it. Read it before acting on a decline. ${GETTING_WORSE_NOTE} ${STORE_NOTE}`,
4628
- help: "Why one entity is getting better or worse. Example: baker ads performance diagnose 120211 --platform meta --level campaign",
4629
- path: "diagnose",
4630
- args: DIAGNOSE_ARGS,
4631
- request: adPerformanceDiagnosisRequestSchema,
4632
- input: diagnoseInput,
4633
- compact: compactDiagnosis,
4634
- hints: diagnosisHints
4635
- });
4636
- var pulseCommand = report({
4637
- name: "pulse",
4638
- schemaName: "pulse",
4639
- schemaDescription: "The daily pulse: per ad account, the last settled day against the 7 settled days before it \u2014 spend, results, cost per result, link CTR, CPM, frequency \u2014 with watch: true where the day is outside their usual range.",
4640
- help: "Yesterday's settled day against the week before. Example: baker ads performance pulse",
4641
- path: "pulse",
4642
- args: PULSE_ARGS,
4643
- request: adPerformancePulseRequestSchema,
4644
- input: pulseInput,
4645
- compact: compactPulse,
4646
- hints: pulseHints
4647
- });
4648
- var performanceCommand = defineCommand9({
4649
- meta: {
4650
- name: "performance",
4651
- description: `Ad results across every connected platform (Meta, Google Ads, LinkedIn, X, ChatGPT Ads), from Baker's stored copy: no platform call, history-complete. Read-only.
4652
-
4653
- Start here: baker ads performance overview
4654
- baker ads performance attention \u2014 the few things that matter most right now
4655
- baker ads performance entities --level ad --sort cost_per_result --order asc
4656
- baker ads performance entity <id> --platform meta --level campaign \u2014 before any change
4657
- baker ads performance impact --actor-kinds baker \u2014 did Baker's changes work
4658
- baker ads performance progress --status worsening \u2014 what is really getting worse, and why
4659
- baker ads performance anomalies \u2014 unusual days, and whether conversion events still record
4660
- baker ads performance recap \u2014 this week's changes in plain sentences
4661
- baker ads performance google-report --days 7 \u2014 a whole Google Ads review: brand vs non-brand, impression share, keywords, budgets
4662
- baker ads performance live-in --landing <slug> \u2014 which live campaigns use a landing (or --creative <slug>) before you change it
4663
- baker ads performance status \u2014 how fresh the synced copy is
4664
- Read every number here first: no rate limit, full history, a few hours behind. Live baker ads <platform> reads only for today's numbers or what this lacks.
4665
- Full guide: __tooling__/docs/tools/baker/ads-performance.md`
4666
- },
4667
- subCommands: {
4668
- overview: overviewCommand,
4669
- accounts: accountsCommand,
4670
- setup: setupCommand,
4671
- entities: entitiesCommand,
4672
- entity: entityCommand,
4673
- changes: changesCommand,
4674
- impact: impactCommand,
4675
- creatives: creativesCommand,
4676
- families: familiesCommand,
4677
- "creative-health": creativeHealthCommand,
4678
- "creative-assets": creativeAssetsCommand,
4679
- health: healthCommand,
4680
- breakdown: breakdownCommand,
4681
- "search-terms": searchTermsCommand,
4682
- pacing: pacingCommand,
4683
- funnel: funnelCommand,
4684
- tracking: trackingCommand,
4685
- landings: landingsCommand,
4686
- "live-in": liveInCommand,
4687
- status: statusCommand,
4688
- goals: withWrites(goalsCommand, { set: goalsSetCommand, name: goalsNameCommand }),
4689
- people: peopleOutcomesCommand,
4690
- personas: personasCommand,
4691
- ladder: ladderCommand,
4692
- "persona-fields": personaFieldsCommand,
4693
- progress: progressCommand,
4694
- diagnose: diagnoseCommand,
4695
- pulse: pulseCommand,
4696
- anomalies: anomaliesCommand,
4697
- recap: recapCommand,
4698
- targets: withWrites(targetsCommand, { set: targetsSetCommand, unstage: targetsUnstageCommand }),
4699
- "budget-recommendations": budgetRecommendationsCommand,
4700
- attention: attentionCommand,
4701
- ...searchCommands
4702
- }
4703
- });
4704
- export {
4705
- performanceCommand
4706
- };
4707
- //# sourceMappingURL=performance-VYEANYQ5.js.map