@koda-sl/baker-cli 0.476.0 → 0.481.0-dev.2d33e194f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -57
- package/canvas/avocado-tutorial.json +3 -3
- package/canvas/hello-world-overlay.json +1 -1
- package/dist/account-67PZBEP6.js +79 -0
- package/dist/account-HZC6X2PH.js +49 -0
- package/dist/account-TQUV64XP.js +80 -0
- package/dist/account-reads-IRBTERTY.js +218 -0
- package/dist/account-writes-ALDSHAEV.js +66 -0
- package/dist/accounts-4P54UY6F.js +115 -0
- package/dist/accounts-6UNBLIUH.js +75 -0
- package/dist/accounts-GVTZN4VE.js +114 -0
- package/dist/accounts-NJ6TF6WH.js +611 -0
- package/dist/accounts-WTB737BM.js +77 -0
- package/dist/actions-D3BOWDEB.js +55 -0
- package/dist/active-entities-J2O76SRP.js +103 -0
- package/dist/activities-2J7FTZMN.js +74 -0
- package/dist/ad-group-writes-PQKDCMQF.js +170 -0
- package/dist/ad-groups-NGYEJMV4.js +68 -0
- package/dist/ad-writes-77FVHJTS.js +174 -0
- package/dist/add-2FHPLALV.js +105 -0
- package/dist/add-2XJYOGKW.js +104 -0
- package/dist/add-62JPGFD4.js +302 -0
- package/dist/add-JP76KCCY.js +97 -0
- package/dist/add-accounts-M5Y4DQFM.js +91 -0
- package/dist/ads-DB5NDHX5.js +128 -0
- package/dist/ads-J2F7BSHR.js +45 -0
- package/dist/ads-J2F7BSHR.js.map +1 -0
- package/dist/ads-LYNA2I7B.js +77 -0
- package/dist/ads-W2IPUWHQ.js +113 -0
- package/dist/adsets-PY5ZKGBO.js +176 -0
- package/dist/advertisers-BVAZ4XUX.js +125 -0
- package/dist/advertisers-NZPJQF7W.js +120 -0
- package/dist/analytics-K2QIWKBN.js +444 -0
- package/dist/analytics-K2QIWKBN.js.map +1 -0
- package/dist/analytics-NTPZKMYK.js +1976 -0
- package/dist/animate-EIV2OHXT.js +480 -0
- package/dist/assign-UNIA5YDC.js +77 -0
- package/dist/audience-size-4NN6ENP5.js +94 -0
- package/dist/audiences-45ZCY2VZ.js +268 -0
- package/dist/audiences-UUIYQFS6.js +78 -0
- package/dist/audiences-VYFX7OFS.js +85 -0
- package/dist/audit-NSVHNA5J.js +151 -0
- package/dist/audit-OWXTLQC4.js +88 -0
- package/dist/autocomplete-EHWAQYVB.js +111 -0
- package/dist/avatars-7WZPW7BV.js +38 -0
- package/dist/bid-pricing-F5NDMS6W.js +99 -0
- package/dist/brand-QAJ5VWFM.js +22 -0
- package/dist/brief-V2OB5OJW.js +490 -0
- package/dist/businesses-ZH67AXR6.js +53 -0
- package/dist/campaign-groups-KFJKLNNU.js +95 -0
- package/dist/campaign-writes-BDGQ6XB3.js +170 -0
- package/dist/campaign-writes-FNMAGCKB.js +165 -0
- package/dist/campaigns-66OOGMDW.js +110 -0
- package/dist/campaigns-6HMHOXXY.js +109 -0
- package/dist/campaigns-HJTGX5WW.js +76 -0
- package/dist/campaigns-KT3J2O7S.js +107 -0
- package/dist/canvas-MQ6ZE5CZ.js +46 -0
- package/dist/capabilities-RVUMPP7C.js +144 -0
- package/dist/cards-I4BZIYUV.js +85 -0
- package/dist/catalog-NLL5VDRK.js +64 -0
- package/dist/catalogs-B2NKQO24.js +128 -0
- package/dist/changes-FX3TPYGT.js +135 -0
- package/dist/chatgpt-3NS2R442.js +47 -0
- package/dist/chats-72L7U4ML.js +262 -0
- package/dist/chunk-2KIF3U2C.js +51 -0
- package/dist/chunk-35ENAFJX.js +46 -0
- package/dist/chunk-3ZWHAIMU.js +207 -0
- package/dist/chunk-3ZWHAIMU.js.map +1 -0
- package/dist/chunk-4DM6OAQ6.js +2065 -0
- package/dist/chunk-4DM6OAQ6.js.map +1 -0
- package/dist/chunk-4TU2MWBJ.js +275 -0
- package/dist/chunk-57UECM77.js +98 -0
- package/dist/chunk-65BQAJMH.js +136 -0
- package/dist/chunk-6GFJFTG6.js +75 -0
- package/dist/chunk-6HPIWZHS.js +76 -0
- package/dist/chunk-73B3RQJC.js +610 -0
- package/dist/chunk-7GYM4I5I.js +206 -0
- package/dist/chunk-7NTION5F.js +26 -0
- package/dist/chunk-7SX73SH2.js +2902 -0
- package/dist/chunk-7SX73SH2.js.map +1 -0
- package/dist/chunk-AFDPISNJ.js +111 -0
- package/dist/chunk-AIDXJHZ4.js +433 -0
- package/dist/chunk-AR7PU7BB.js +76 -0
- package/dist/chunk-AXVOBRQT.js +1442 -0
- package/dist/chunk-BBBZR7MF.js +160 -0
- package/dist/chunk-BVYPRGPG.js +92 -0
- package/dist/chunk-CAMX6OHF.js +3182 -0
- package/dist/chunk-CAMX6OHF.js.map +1 -0
- package/dist/chunk-CBCGVPHB.js +125 -0
- package/dist/chunk-CECS3DHZ.js +333 -0
- package/dist/chunk-CZYUZL6R.js +3151 -0
- package/dist/chunk-D5MF337I.js +97 -0
- package/dist/chunk-DDTILZXI.js +103 -0
- package/dist/chunk-DL2W4N7J.js +52 -0
- package/dist/chunk-DLUWHZCE.js +225 -0
- package/dist/chunk-EOGPNV25.js +509 -0
- package/dist/chunk-F3FSD6BO.js +96 -0
- package/dist/chunk-FDG2LLX7.js +494 -0
- package/dist/chunk-FE7T32YL.js +163 -0
- package/dist/chunk-FE7T32YL.js.map +1 -0
- package/dist/chunk-FIN4ZXGJ.js +70 -0
- package/dist/chunk-FIN4ZXGJ.js.map +1 -0
- package/dist/chunk-FO5EP6DA.js +24008 -0
- package/dist/chunk-FO5EP6DA.js.map +1 -0
- package/dist/chunk-FSGTITIW.js +622 -0
- package/dist/chunk-FSGTITIW.js.map +1 -0
- package/dist/chunk-FSPV2DUG.js +310 -0
- package/dist/chunk-FSPV2DUG.js.map +1 -0
- package/dist/chunk-FYAWA3FO.js +1830 -0
- package/dist/chunk-G3KO2HFZ.js +35 -0
- package/dist/chunk-GKXMIXEV.js +166 -0
- package/dist/chunk-GT4UFORX.js +296 -0
- package/dist/chunk-GZ6YS7TG.js +106 -0
- package/dist/chunk-H7JOVRHO.js +106 -0
- package/dist/chunk-I2DNBEV6.js +4227 -0
- package/dist/chunk-I2DNBEV6.js.map +1 -0
- package/dist/chunk-IJDF7D4U.js +139 -0
- package/dist/chunk-INJHF7AF.js +172 -0
- package/dist/chunk-IZFGTGFR.js +67 -0
- package/dist/chunk-J2QOKD2F.js +76 -0
- package/dist/chunk-J5WZ7SAD.js +346 -0
- package/dist/chunk-JC6GNMA7.js +297 -0
- package/dist/chunk-KB5HBQEK.js +25 -0
- package/dist/chunk-KB5XJHQX.js +216 -0
- package/dist/chunk-KM4AAAHZ.js +5187 -0
- package/dist/chunk-KM4AAAHZ.js.map +1 -0
- package/dist/chunk-LFZTGIPJ.js +384 -0
- package/dist/chunk-LFZTGIPJ.js.map +1 -0
- package/dist/chunk-LHNL6JM2.js +309 -0
- package/dist/chunk-LMHVLUD4.js +175 -0
- package/dist/chunk-LMHVLUD4.js.map +1 -0
- package/dist/chunk-NAMEFVWL.js +1743 -0
- package/dist/chunk-NORAJYUI.js +172 -0
- package/dist/chunk-NQTSHC7Z.js +304 -0
- package/dist/chunk-O3LHCQ7Q.js +136 -0
- package/dist/chunk-O4ABJ352.js +4410 -0
- package/dist/chunk-O4ABJ352.js.map +1 -0
- package/dist/chunk-O5XLQUDS.js +846 -0
- package/dist/chunk-OI7KSXZM.js +6265 -0
- package/dist/chunk-OI7KSXZM.js.map +1 -0
- package/dist/chunk-OO67YTPM.js +35 -0
- package/dist/chunk-OO67YTPM.js.map +1 -0
- package/dist/chunk-OXBPKBSK.js +6667 -0
- package/dist/chunk-OXBPKBSK.js.map +1 -0
- package/dist/chunk-P3XJUOWT.js +474 -0
- package/dist/chunk-P3ZJIIDS.js +57 -0
- package/dist/chunk-P5T7OZKV.js +26 -0
- package/dist/chunk-P66JWKAL.js +533 -0
- package/dist/chunk-P66JWKAL.js.map +1 -0
- package/dist/chunk-PA5MYVQY.js +66 -0
- package/dist/chunk-PELW56D7.js +45 -0
- package/dist/chunk-PGVF4SB7.js +169 -0
- package/dist/chunk-Q676JHJA.js +58 -0
- package/dist/chunk-QDOVBGKZ.js +149 -0
- package/dist/chunk-QHIUJXU7.js +363 -0
- package/dist/chunk-QJPPPQPD.js +98 -0
- package/dist/chunk-QNEX7FEC.js +174 -0
- package/dist/chunk-QTL4NJ5I.js +65 -0
- package/dist/chunk-QTL4NJ5I.js.map +1 -0
- package/dist/chunk-QWX3QJNM.js +170 -0
- package/dist/chunk-QXDEZKAR.js +455 -0
- package/dist/chunk-QXDEZKAR.js.map +1 -0
- package/dist/chunk-RG7OMI5W.js +1621 -0
- package/dist/chunk-RG7OMI5W.js.map +1 -0
- package/dist/chunk-RUHMLPH5.js +455 -0
- package/dist/chunk-SEPPFM5S.js +228 -0
- package/dist/chunk-SJLIV6PF.js +19 -0
- package/dist/chunk-SLCSLVGS.js +274 -0
- package/dist/chunk-SQ7DRYCC.js +150 -0
- package/dist/chunk-THU74XR6.js +96 -0
- package/dist/chunk-TY7TEU3T.js +1079 -0
- package/dist/chunk-UXTZ5MBE.js +56 -0
- package/dist/chunk-V43RLLZE.js +55 -0
- package/dist/chunk-V7PMSMPF.js +817 -0
- package/dist/chunk-VFSLIDXA.js +42 -0
- package/dist/chunk-VHMNSO2B.js +84 -0
- package/dist/chunk-VKH6LSC2.js +188 -0
- package/dist/chunk-VTCBPYGE.js +592 -0
- package/dist/chunk-W6ASM6UP.js +197 -0
- package/dist/chunk-W7KB3LJE.js +60 -0
- package/dist/chunk-WESRVXFS.js +133 -0
- package/dist/chunk-WILN7TD6.js +84 -0
- package/dist/chunk-YGK7ZHNI.js +337 -0
- package/dist/chunk-YGK7ZHNI.js.map +1 -0
- package/dist/chunk-YGL5LTEA.js +665 -0
- package/dist/chunk-YONSWWCF.js +215 -0
- package/dist/chunk-Z5KBRTB2.js +191 -0
- package/dist/chunk-Z62XZCBE.js +541 -0
- package/dist/chunk-Z62XZCBE.js.map +1 -0
- package/dist/chunk-ZBKBTTHD.js +74 -0
- package/dist/chunk-ZJR5E53N.js +23 -0
- package/dist/chunk-ZLU5QDLU.js +486 -0
- package/dist/chunk-ZNNC6B75.js +94 -0
- package/dist/chunk-ZO2YLHRW.js +74 -0
- package/dist/chunk-ZPJE6D7W.js +74 -0
- package/dist/claim-DIITRUIN.js +197 -0
- package/dist/cli.js +38 -39
- package/dist/cli.js.map +1 -1
- package/dist/client-LY2JCQS2.js +19 -0
- package/dist/code-7CBNYUPU.js +76 -0
- package/dist/companies-HHSYHL74.js +195 -0
- package/dist/complete-M3TURPU5.js +81 -0
- package/dist/config-7COTFJWK.js +88 -0
- package/dist/connect-TQL5EVVE.js +42 -0
- package/dist/content-OXMONMHJ.js +85 -0
- package/dist/conversation-GE273GC4.js +93 -0
- package/dist/conversion-report-BH4HH2Q3.js +125 -0
- package/dist/conversion-upload-command-QWWYCTNV.js +438 -0
- package/dist/conversion-writes-HG2O6JI7.js +120 -0
- package/dist/conversions-DP6ZF57D.js +135 -0
- package/dist/conversions-XPSN7SOW.js +76 -0
- package/dist/countries-UXAUGSBR.js +82 -0
- package/dist/create-ENQWVWGB.js +59 -0
- package/dist/create-NVXJP477.js +207 -0
- package/dist/create-PITWGVAS.js +130 -0
- package/dist/create-PITWGVAS.js.map +1 -0
- package/dist/create-XLZYNFKN.js +235 -0
- package/dist/creatives-O3444SBT.js +182 -0
- package/dist/creatives-ONKKGW5T.js +87 -0
- package/dist/creatives-U6IKIQBT.js +51 -0
- package/dist/credentials-3UY7RVUL.js +287 -0
- package/dist/critique-3LMW73QJ.js +1449 -0
- package/dist/critique-KB32Y4AW.js +213 -0
- package/dist/crm-OX67TBWD.js +224 -0
- package/dist/crm-XXU5XZIQ.js +318 -0
- package/dist/crop-3ZV5BFCA.js +113 -0
- package/dist/currency-XP4EMU4M.js +88 -0
- package/dist/custom-conversions-TV23XQHW.js +125 -0
- package/dist/customers-EJ7FVCTC.js +56 -0
- package/dist/decide-EGQ3CRHI.js +199 -0
- package/dist/delete-55IHV25V.js +63 -0
- package/dist/delete-GXLPIGTC.js +54 -0
- package/dist/delete-MUPTLWHI.js +71 -0
- package/dist/delete-OW2N4WIA.js +66 -0
- package/dist/delete-R2SEUSLV.js +69 -0
- package/dist/delete-ZGBBWFAW.js +71 -0
- package/dist/demographics-K6DEWUE5.js +138 -0
- package/dist/describe-4O3VCV6L.js +97 -0
- package/dist/describe-FCTM53MM.js +97 -0
- package/dist/dimensions-AD4VS5AH.js +101 -0
- package/dist/discard-NZ7FQJEE.js +66 -0
- package/dist/discover-OISU5EJF.js +172 -0
- package/dist/discover-UQXQUPNO.js +93 -0
- package/dist/domain-DXBHNZLO.js +98 -0
- package/dist/download-IE6XLFHM.js +299 -0
- package/dist/draft-DB6KMBCH.js +306 -0
- package/dist/draft-ENA7S4Y7.js +103 -0
- package/dist/draft-HJBAG3SN.js +307 -0
- package/dist/draft-ICEVLF4E.js +328 -0
- package/dist/draft-MGKJ5RYI.js +100 -0
- package/dist/draft-OBFKQEPK.js +281 -0
- package/dist/draft-OOHU6NMA.js +217 -0
- package/dist/draft-S6VPKZYY.js +233 -0
- package/dist/engine/index.js +13 -13
- package/dist/env-YLOVBOAE.js +43 -0
- package/dist/experiment-PHFP6IBM.js +1397 -0
- package/dist/export-R2IMUCQO.js +168 -0
- package/dist/extract-OQP3TSQG.js +113 -0
- package/dist/facets-NUKR7I57.js +114 -0
- package/dist/favorites-NS73FDG5.js +178 -0
- package/dist/feed-SQPOB7SP.js +177 -0
- package/dist/feedback-SQAAAKOQ.js +66 -0
- package/dist/feedback-record-7W7WBHSQ.js +175 -0
- package/dist/fetch-PQ3NOPIE.js +139 -0
- package/dist/fetch-PTSAVQW5.js +176 -0
- package/dist/find-Y22AANDE.js +134 -0
- package/dist/find-Y22AANDE.js.map +1 -0
- package/dist/fit-IITU6UWC.js +45 -0
- package/dist/flows-ZLF3QJ76.js +247 -0
- package/dist/flows-ZLF3QJ76.js.map +1 -0
- package/dist/follow-N3DQZV7M.js +94 -0
- package/dist/follow-competitors-BZPCBGIB.js +112 -0
- package/dist/following-KESWTEBR.js +113 -0
- package/dist/fonts-FG6XMK5A.js +34 -0
- package/dist/forecast-43XBSHJR.js +168 -0
- package/dist/forecast-MAFZQ4UF.js +104 -0
- package/dist/funding-HYSCYS62.js +85 -0
- package/dist/funnel-32CNV57L.js +394 -0
- package/dist/ga4-J2KTNCD4.js +55 -0
- package/dist/generate-G3I5BLLW.js +262 -0
- package/dist/generate-G3I5BLLW.js.map +1 -0
- package/dist/get-3P2EQLP3.js +63 -0
- package/dist/get-FJB3X44Z.js +61 -0
- package/dist/get-GDF5V3RN.js +78 -0
- package/dist/get-MBGO4VJD.js +63 -0
- package/dist/get-NOKSZPIV.js +59 -0
- package/dist/get-W7MKBMQ3.js +56 -0
- package/dist/get-WHC4SRYK.js +92 -0
- package/dist/gif-SFYVDLCU.js +94 -0
- package/dist/google-THWFDDHJ.js +3191 -0
- package/dist/google-THWFDDHJ.js.map +1 -0
- package/dist/google-XRAMIES5.js +136 -0
- package/dist/google-ads-N55SE2CB.js +195 -0
- package/dist/group-6RDJTWVN.js +43 -0
- package/dist/group-TZJCKHKS.js +43 -0
- package/dist/gsc-HD6UFRYM.js +30 -0
- package/dist/history-4RWCE7WW.js +67 -0
- package/dist/history-IROFQ6LA.js +133 -0
- package/dist/hubspot-75N4AI43.js +779 -0
- package/dist/icon-BMCDFGMN.js +137 -0
- package/dist/ideas-7UQMXLI5.js +32 -0
- package/dist/images-OFR3S2U4.js +90 -0
- package/dist/improve-P6F2HXYQ.js +86 -0
- package/dist/ingest-HGR7KSG4.js +107 -0
- package/dist/ingest-XU4WXE4U.js +23 -0
- package/dist/insights-7SI2TB72.js +128 -0
- package/dist/insights-7SI2TB72.js.map +1 -0
- package/dist/insights-P5VIDOZN.js +432 -0
- package/dist/insights-P5VIDOZN.js.map +1 -0
- package/dist/inspect-TL2VZBN7.js +189 -0
- package/dist/inspiration-7WTS3AVI.js +53 -0
- package/dist/intent-OCZE4O7R.js +95 -0
- package/dist/job-XMPQ27SJ.js +202 -0
- package/dist/job-create-HTDQHPEF.js +109 -0
- package/dist/job-status-L7WUTZ2C.js +74 -0
- package/dist/keep-42SLV4CR.js +78 -0
- package/dist/keyword-gap-323NE4AK.js +130 -0
- package/dist/keyword-metrics-V6DGO7U5.js +197 -0
- package/dist/keywords-for-site-ANSLXBHB.js +128 -0
- package/dist/landing-K25GUBLZ.js +28 -0
- package/dist/landings-5IYXHA5Z.js +151 -0
- package/dist/languages-SRFGW6Z3.js +55 -0
- package/dist/languages-US6JUATO.js +40 -0
- package/dist/launches-7U4TPNO3.js +108 -0
- package/dist/launches-7U4TPNO3.js.map +1 -0
- package/dist/layerize-U672AXED.js +190 -0
- package/dist/leads-MXUQISOC.js +293 -0
- package/dist/leads-UJVKPRXX.js +90 -0
- package/dist/learn-XTEU4K66.js +203 -0
- package/dist/library-RPFXNER4.js +109 -0
- package/dist/library-RPFXNER4.js.map +1 -0
- package/dist/lighthouse-SJ4QNLUS.js +75 -0
- package/dist/line-item-writes-NURRHJ7P.js +208 -0
- package/dist/line-items-PC6QWV5J.js +103 -0
- package/dist/link-ZSXKCLQD.js +64 -0
- package/dist/linkedin-EZJJBKY3.js +1008 -0
- package/dist/list-ECF3RA26.js +303 -0
- package/dist/list-GQ65MC2N.js +65 -0
- package/dist/list-K56J7FZW.js +51 -0
- package/dist/list-M4Z3WYPM.js +76 -0
- package/dist/list-N7ZLO2LE.js +63 -0
- package/dist/list-Q5ZZRPHO.js +151 -0
- package/dist/list-SXYVDDMU.js +57 -0
- package/dist/list-XFIOXFB2.js +50 -0
- package/dist/locations-LZUHWKZY.js +40 -0
- package/dist/locations-NG4YXIBT.js +67 -0
- package/dist/log-EEBJQ6X2.js +92 -0
- package/dist/logo-4JOWGJ5A.js +18 -0
- package/dist/lookup-7NBZ64TE.js +22 -0
- package/dist/make-3J5EK52U.js +633 -0
- package/dist/map-JVVNH2RI.js +404 -0
- package/dist/map-JVVNH2RI.js.map +1 -0
- package/dist/mcp-DAKGMA2Z.js +463 -0
- package/dist/media-2SWDJDA6.js +15 -0
- package/dist/media-R64YP7T2.js +89 -0
- package/dist/media-sources-L2X3KRDU.js +32 -0
- package/dist/members-22BOEGQS.js +44 -0
- package/dist/meta-RBHF2RG3.js +69 -0
- package/dist/metrics-IXRQZVSM.js +152 -0
- package/dist/models-BDXZ56GI.js +155 -0
- package/dist/normalize-JAE3NVOX.js +141 -0
- package/dist/normalize-P5Q3F7FU.js +806 -0
- package/dist/options-YTIU3BDC.js +121 -0
- package/dist/options-YTIU3BDC.js.map +1 -0
- package/dist/output-ZL62UZHQ.js +24 -0
- package/dist/page-FZ7IZ5CV.js +105 -0
- package/dist/pages-SS7TZTHV.js +66 -0
- package/dist/performance-FOOV624A.js +24 -0
- package/dist/performance-VYEANYQ5.js +4707 -0
- package/dist/performance-VYEANYQ5.js.map +1 -0
- package/dist/pinterest-UETS7U7U.js +96 -0
- package/dist/pixels-LCTMLKGP.js +108 -0
- package/dist/pixels-MPZYWGFD.js +60 -0
- package/dist/plan-V74EVXTW.js +207 -0
- package/dist/preview-RU54Z4EM.js +169 -0
- package/dist/profile-NQOIBLJL.js +150 -0
- package/dist/profile-NQOIBLJL.js.map +1 -0
- package/dist/promoted-tweet-writes-FQRFU7PB.js +180 -0
- package/dist/promoted-tweets-L2L3V5SF.js +119 -0
- package/dist/properties-ZUDQH3LE.js +101 -0
- package/dist/publish-5XTAHTM4.js +322 -0
- package/dist/query-26T7XATH.js +236 -0
- package/dist/query-SHUOHXTO.js +247 -0
- package/dist/query-XISUHMBR.js +62 -0
- package/dist/questionnaire-UG55JLVW.js +93 -0
- package/dist/read-UYSQAXRV.js +141 -0
- package/dist/reddit-BCB4FT6J.js +26 -0
- package/dist/refresh-WY5NPNLX.js +94 -0
- package/dist/release-JBSPPNQE.js +60 -0
- package/dist/relevant-pages-OU5H56KY.js +99 -0
- package/dist/remove-2KACTXKP.js +48 -0
- package/dist/remove-M5P7M5JU.js +50 -0
- package/dist/reopen-J3VW5766.js +63 -0
- package/dist/rerun-5MRYJG6U.js +84 -0
- package/dist/research-4CAA5CA2.js +262 -0
- package/dist/research-7LBTJ2B2.js +62 -0
- package/dist/reset-DBWWGCXY.js +54 -0
- package/dist/reset-DJSSXC4Y.js +54 -0
- package/dist/resolve-BNRUYS56.js +121 -0
- package/dist/retry-TMRIFGVT.js +62 -0
- package/dist/retry-WRBGLB7O.js +60 -0
- package/dist/retry-failed-NWLH6WHK.js +45 -0
- package/dist/run-TA3DP6QG.js +59 -0
- package/dist/run-VVTLDRRW.js +65 -0
- package/dist/scaffold-ad-7DSV6XDB.js +1548 -0
- package/dist/scaffold-static-ad-BEZ37QWN.js +971 -0
- package/dist/scaffold-video-SPCMTYK3.js +872 -0
- package/dist/schedule-BQQGYGT7.js +61 -0
- package/dist/schedule-S547HPUT.js +66 -0
- package/dist/scheduled-agents-4Q6WS333.js +49 -0
- package/dist/scheduled-agents-4Q6WS333.js.map +1 -0
- package/dist/schema-DDEWM5ZX.js +132 -0
- package/dist/schema-SQIP75WG.js +63 -0
- package/dist/scrape-TI6S2W7I.js +2241 -0
- package/dist/screenshot-XFAYY4WW.js +102 -0
- package/dist/search-4HIOKYFR.js +26 -0
- package/dist/search-D2IV7KFJ.js +92 -0
- package/dist/search-IZPDHRJ4.js +224 -0
- package/dist/search-KFDL2IGG.js +91 -0
- package/dist/search-VJBKRD2U.js +174 -0
- package/dist/search-copy-QZXCAGK4.js +168 -0
- package/dist/seeds-VUNZMHZH.js +101 -0
- package/dist/select-U5TPAXGY.js +94 -0
- package/dist/sends-7HWLIOUO.js +309 -0
- package/dist/sequences-5RAGZE7L.js +146 -0
- package/dist/set-prompt-MHU4WI3Q.js +185 -0
- package/dist/setup-links-SPIGXQTY.js +141 -0
- package/dist/shared-3CGGKICB.js +31 -0
- package/dist/site-FW6K6L6M.js +177 -0
- package/dist/sitemaps-3WDYQHYR.js +87 -0
- package/dist/sites-G4DQ2Z2X.js +101 -0
- package/dist/skills-PA26HXEP.js +47 -0
- package/dist/sources-4NAC6HV6.js +421 -0
- package/dist/sources-4NAC6HV6.js.map +1 -0
- package/dist/sources-PMBWG4KO.js +30 -0
- package/dist/stale-5IMY6MWC.js +63 -0
- package/dist/stats-Q35I5DRW.js +34 -0
- package/dist/status-62SUXUWW.js +62 -0
- package/dist/sticker-MC2IAIQB.js +94 -0
- package/dist/stock-B7LKMKUK.js +179 -0
- package/dist/studio-BB2EVW5I.js +49 -0
- package/dist/sync-CLZDZL5F.js +54 -0
- package/dist/sync-XCHBEBZG.js +279 -0
- package/dist/sync-XCHBEBZG.js.map +1 -0
- package/dist/sync-YTHNOAAM.js +54 -0
- package/dist/sync-definition-DYSVT7BJ.js +52 -0
- package/dist/tag-manager-EAREWVYR.js +43 -0
- package/dist/tags-53AG3FYG.js +20 -0
- package/dist/tags-7TH7TVR6.js +224 -0
- package/dist/tags-EB4RE3ML.js +20 -0
- package/dist/tags-EUEWDTTN.js +20 -0
- package/dist/tags-ICAWBCOR.js +26 -0
- package/dist/targeting-constants-IFE72XKL.js +118 -0
- package/dist/targeting-criteria-WZDGJKOI.js +113 -0
- package/dist/targets-OKJ6A4YL.js +102 -0
- package/dist/templates-IA5TDCCZ.js +178 -0
- package/dist/templates-IA5TDCCZ.js.map +1 -0
- package/dist/testimonials-THHR5JH5.js +32 -0
- package/dist/themes-ZFRM6AVO.js +94 -0
- package/dist/top-companies-63Q475IA.js +108 -0
- package/dist/translate-XKUWIIOS.js +598 -0
- package/dist/triggers-XLKO2QXQ.js +141 -0
- package/dist/tweets-7EZRFG3Y.js +123 -0
- package/dist/unfollow-WBBTVEHE.js +46 -0
- package/dist/unlink-P7KU54YU.js +67 -0
- package/dist/unstage-EG7AUE5X.js +84 -0
- package/dist/unstage-GHXGDOT2.js +95 -0
- package/dist/update-7EWDO32L.js +98 -0
- package/dist/update-BVYGB4YL.js +142 -0
- package/dist/update-KVMQOVQK.js +63 -0
- package/dist/update-N74F5KXB.js +155 -0
- package/dist/upload-6PYPHPCZ.js +126 -0
- package/dist/upload-NA62W5RS.js +150 -0
- package/dist/upscale-RXJUBHXN.js +109 -0
- package/dist/url-options-EUDXLDBD.js +130 -0
- package/dist/url-params-DBJSM2LP.js +98 -0
- package/dist/use-MHPNS4V4.js +95 -0
- package/dist/validate-TTAGVGM2.js +88 -0
- package/dist/variant-ZFCWILPG.js +338 -0
- package/dist/videos-DUG7JYUA.js +39 -0
- package/dist/view-PYCGDGT2.js +121 -0
- package/dist/viewer-check-HIMNCZGY.js +68 -0
- package/dist/web-SDG42OBC.js +140 -0
- package/dist/winners-AI4PVSOI.js +113 -0
- package/dist/winning-ads-KGAYAIPG.js +84 -0
- package/dist/write-commands-34L5LJIB.js +713 -0
- package/dist/write-commands-5TJ3LQ5F.js +737 -0
- package/dist/write-commands-OYMWOFEY.js +1015 -0
- package/dist/write-commands-R3UCCBUI.js +206 -0
- package/dist/x-VNRVGVLI.js +47 -0
- package/package.json +1 -1
- package/dist/account-MLAW7QO4.js +0 -49
- package/dist/account-QR5LCE74.js +0 -79
- package/dist/account-UP67V2FH.js +0 -80
- package/dist/account-reads-5RGYKIIJ.js +0 -218
- package/dist/account-writes-EGRMVWVQ.js +0 -66
- package/dist/accounts-CU4Q2AV7.js +0 -75
- package/dist/accounts-GUHRV3RA.js +0 -115
- package/dist/accounts-L2SNBNOO.js +0 -611
- package/dist/accounts-LG4B7NWO.js +0 -77
- package/dist/accounts-WH7SRFCB.js +0 -114
- package/dist/actions-LMKQCN4E.js +0 -55
- package/dist/active-entities-2CQHGACO.js +0 -103
- package/dist/activecampaign-RZEJ25GO.js +0 -935
- package/dist/activecampaign-RZEJ25GO.js.map +0 -1
- package/dist/activities-EDCSBYGM.js +0 -74
- package/dist/ad-group-writes-WUAYGWDY.js +0 -170
- package/dist/ad-groups-BVEJJHX6.js +0 -68
- package/dist/ad-writes-WTWRSFLW.js +0 -174
- package/dist/add-DDKP3OOS.js +0 -104
- package/dist/add-HQERVTGX.js +0 -302
- package/dist/add-PGD6PNXH.js +0 -97
- package/dist/add-ZMZJSBQT.js +0 -105
- package/dist/add-accounts-A2YRT5NR.js +0 -91
- package/dist/ads-HRCVRXPO.js +0 -77
- package/dist/ads-WB2X5SYO.js +0 -40
- package/dist/ads-WB2X5SYO.js.map +0 -1
- package/dist/ads-ZOQM52YG.js +0 -128
- package/dist/ads-ZOYLP5R3.js +0 -113
- package/dist/adsets-RKSBDAHV.js +0 -176
- package/dist/advertisers-34GDHZC7.js +0 -125
- package/dist/advertisers-OEHGT3U2.js +0 -120
- package/dist/analytics-ILWIWU7C.js +0 -433
- package/dist/analytics-ILWIWU7C.js.map +0 -1
- package/dist/analytics-JYBKFGP4.js +0 -1976
- package/dist/animate-NVEQOLHK.js +0 -480
- package/dist/assign-2HJHKJKL.js +0 -77
- package/dist/audience-size-W74YHNBX.js +0 -94
- package/dist/audiences-FNDYOAAW.js +0 -85
- package/dist/audiences-SXBA4R3I.js +0 -78
- package/dist/audiences-ZUFCFB7M.js +0 -268
- package/dist/audit-76SDQ7C6.js +0 -151
- package/dist/audit-MADJMBNF.js +0 -88
- package/dist/autocomplete-GZ4XVGC4.js +0 -111
- package/dist/avatars-AILCY4T2.js +0 -38
- package/dist/bid-pricing-5435DI5H.js +0 -99
- package/dist/brand-4RWOQ7EN.js +0 -22
- package/dist/brief-2Y6PG6QI.js +0 -490
- package/dist/businesses-OFQG3YSL.js +0 -53
- package/dist/campaign-groups-XWQX7PSF.js +0 -95
- package/dist/campaign-writes-F4LVAD5N.js +0 -165
- package/dist/campaign-writes-GFOOQDZR.js +0 -170
- package/dist/campaigns-AFWP56ST.js +0 -109
- package/dist/campaigns-FHZLLATX.js +0 -76
- package/dist/campaigns-W7DMOOXW.js +0 -107
- package/dist/campaigns-YBOTJUV2.js +0 -110
- package/dist/canvas-RCRBTZKW.js +0 -46
- package/dist/capabilities-7DSVIC2L.js +0 -144
- package/dist/cards-WZSPTO4I.js +0 -85
- package/dist/catalog-YY33VIDD.js +0 -64
- package/dist/catalogs-NQX6UJ7C.js +0 -128
- package/dist/changes-OZ2D64FV.js +0 -135
- package/dist/chatgpt-33BYXDJP.js +0 -47
- package/dist/chats-4GAFW3QN.js +0 -262
- package/dist/chunk-2MTAKAHE.js +0 -509
- package/dist/chunk-2XQ5HU6D.js +0 -3088
- package/dist/chunk-2XQ5HU6D.js.map +0 -1
- package/dist/chunk-2YXY56FK.js +0 -455
- package/dist/chunk-33STLGET.js +0 -67
- package/dist/chunk-365Z5TYR.js +0 -275
- package/dist/chunk-3RS4M7AR.js +0 -817
- package/dist/chunk-3SF5KBIO.js +0 -42
- package/dist/chunk-3UA4THEF.js +0 -76
- package/dist/chunk-3WQ4ANUO.js +0 -84
- package/dist/chunk-3YNHIJCI.js +0 -75
- package/dist/chunk-42ESQ4AJ.js +0 -1079
- package/dist/chunk-4E27H5EG.js +0 -149
- package/dist/chunk-5B3AEQDZ.js +0 -125
- package/dist/chunk-5H2H77HR.js +0 -903
- package/dist/chunk-5H2H77HR.js.map +0 -1
- package/dist/chunk-6DE3EHEF.js +0 -296
- package/dist/chunk-6QNEVETC.js +0 -46
- package/dist/chunk-6SOB7QMD.js +0 -172
- package/dist/chunk-7JFGOMJN.js +0 -363
- package/dist/chunk-7VHPAGPK.js +0 -228
- package/dist/chunk-7YOPIYCL.js +0 -92
- package/dist/chunk-AHQJAKAV.js +0 -544
- package/dist/chunk-AHQJAKAV.js.map +0 -1
- package/dist/chunk-BC2BXRXY.js +0 -2887
- package/dist/chunk-BC2BXRXY.js.map +0 -1
- package/dist/chunk-BCKSBIQ4.js +0 -592
- package/dist/chunk-BEJ2AVPJ.js +0 -106
- package/dist/chunk-BKE32DQB.js +0 -96
- package/dist/chunk-CGOVXSSY.js +0 -309
- package/dist/chunk-CHZ3ZNGJ.js +0 -172
- package/dist/chunk-CNAPU47I.js +0 -433
- package/dist/chunk-D7KNUZ6Z.js +0 -1830
- package/dist/chunk-DRPSJE62.js +0 -216
- package/dist/chunk-DRPSJE62.js.map +0 -1
- package/dist/chunk-EMDUW6QN.js +0 -206
- package/dist/chunk-ENY76SPM.js +0 -74
- package/dist/chunk-F4H6VVPE.js +0 -215
- package/dist/chunk-FD66LSFW.js +0 -392
- package/dist/chunk-FD66LSFW.js.map +0 -1
- package/dist/chunk-FEELV5IV.js +0 -150
- package/dist/chunk-FHY3BM2O.js +0 -98
- package/dist/chunk-FJIUMOVD.js +0 -225
- package/dist/chunk-FK6GBCQ4.js +0 -66
- package/dist/chunk-FSZEVFMI.js +0 -159
- package/dist/chunk-FSZEVFMI.js.map +0 -1
- package/dist/chunk-G3AOJ5FE.js +0 -35
- package/dist/chunk-G3UCXCC2.js +0 -455
- package/dist/chunk-G3UCXCC2.js.map +0 -1
- package/dist/chunk-H5GLPZ36.js +0 -103
- package/dist/chunk-HDYF3EIN.js +0 -166
- package/dist/chunk-IBCTXMQD.js +0 -98
- package/dist/chunk-IMH2ZKKC.js +0 -60
- package/dist/chunk-IPYG4OYK.js +0 -111
- package/dist/chunk-IWLWY2UD.js +0 -2122
- package/dist/chunk-IWLWY2UD.js.map +0 -1
- package/dist/chunk-IZNTYRKB.js +0 -55
- package/dist/chunk-J5BHSJYL.js +0 -547
- package/dist/chunk-J5BHSJYL.js.map +0 -1
- package/dist/chunk-JA4PDZNY.js +0 -106
- package/dist/chunk-JHIVRENV.js +0 -346
- package/dist/chunk-JLCK7K6Y.js +0 -216
- package/dist/chunk-JQUPL6IQ.js +0 -136
- package/dist/chunk-K2DBAN3C.js +0 -26
- package/dist/chunk-KKXHGAYA.js +0 -74
- package/dist/chunk-KMMVQ2MR.js +0 -84
- package/dist/chunk-L66QWXPF.js +0 -97
- package/dist/chunk-LHKCZG2R.js +0 -136
- package/dist/chunk-LK64CCN4.js +0 -474
- package/dist/chunk-LL6T64OU.js +0 -174
- package/dist/chunk-LLN7NP3O.js +0 -52
- package/dist/chunk-LNZM6SUE.js +0 -56
- package/dist/chunk-M62WN522.js +0 -188
- package/dist/chunk-M6CI2HYA.js +0 -74
- package/dist/chunk-M6UNISGO.js +0 -66
- package/dist/chunk-M6UNISGO.js.map +0 -1
- package/dist/chunk-MCDU24JX.js +0 -57
- package/dist/chunk-MGKEUB7P.js +0 -96
- package/dist/chunk-ML7DLFFY.js +0 -4227
- package/dist/chunk-ML7DLFFY.js.map +0 -1
- package/dist/chunk-MOMZOWYC.js +0 -23992
- package/dist/chunk-MOMZOWYC.js.map +0 -1
- package/dist/chunk-MSKB6I5E.js +0 -304
- package/dist/chunk-MU23X3VU.js +0 -23
- package/dist/chunk-MX5TREEM.js +0 -197
- package/dist/chunk-MZQBL7QU.js +0 -1442
- package/dist/chunk-NC3GOGUT.js +0 -19
- package/dist/chunk-NDITGFPZ.js +0 -5181
- package/dist/chunk-NDITGFPZ.js.map +0 -1
- package/dist/chunk-NFDAIY6J.js +0 -191
- package/dist/chunk-OKHSMDCY.js +0 -494
- package/dist/chunk-OLV66PDA.js +0 -139
- package/dist/chunk-OW6TNVRC.js +0 -382
- package/dist/chunk-OW6TNVRC.js.map +0 -1
- package/dist/chunk-P4UQBJNY.js +0 -486
- package/dist/chunk-P6SFEIA5.js +0 -3151
- package/dist/chunk-PAJJXZ3Z.js +0 -170
- package/dist/chunk-PHY3AERY.js +0 -6217
- package/dist/chunk-PHY3AERY.js.map +0 -1
- package/dist/chunk-RCWEJUSF.js +0 -337
- package/dist/chunk-RCWEJUSF.js.map +0 -1
- package/dist/chunk-RDQI3YD2.js +0 -66
- package/dist/chunk-RDQI3YD2.js.map +0 -1
- package/dist/chunk-RMB2MIOJ.js +0 -94
- package/dist/chunk-RV222F63.js +0 -51
- package/dist/chunk-SKYXCMVX.js +0 -846
- package/dist/chunk-SS445HDO.js +0 -25
- package/dist/chunk-TJ5N6IVL.js +0 -665
- package/dist/chunk-TLOFNP4Q.js +0 -45
- package/dist/chunk-TUVS5SPF.js +0 -133
- package/dist/chunk-TVKE3OXR.js +0 -76
- package/dist/chunk-U6RTBCU7.js +0 -26
- package/dist/chunk-UNHCWWAI.js +0 -111
- package/dist/chunk-UNHCWWAI.js.map +0 -1
- package/dist/chunk-V4H6Q4TB.js +0 -1599
- package/dist/chunk-V4H6Q4TB.js.map +0 -1
- package/dist/chunk-VPBCR6DT.js +0 -1743
- package/dist/chunk-VTK2W2NI.js +0 -160
- package/dist/chunk-WBCQEXEH.js +0 -76
- package/dist/chunk-WJOQ6IEK.js +0 -4408
- package/dist/chunk-WJOQ6IEK.js.map +0 -1
- package/dist/chunk-WOLE67OJ.js +0 -6667
- package/dist/chunk-WOLE67OJ.js.map +0 -1
- package/dist/chunk-WSD63XGR.js +0 -169
- package/dist/chunk-X3PVW3RM.js +0 -610
- package/dist/chunk-XA2S53QZ.js +0 -274
- package/dist/chunk-XLZMKMKD.js +0 -333
- package/dist/chunk-YDAEGV42.js +0 -297
- package/dist/chunk-Z2HNETUP.js +0 -58
- package/dist/chunk-ZZS6IUOY.js +0 -175
- package/dist/chunk-ZZS6IUOY.js.map +0 -1
- package/dist/claim-XBVKGN7A.js +0 -197
- package/dist/client-2ABVGUQB.js +0 -19
- package/dist/code-GHG7DEPV.js +0 -76
- package/dist/companies-IGDIQ64M.js +0 -195
- package/dist/complete-CYPVVQWT.js +0 -81
- package/dist/config-L26WNV2G.js +0 -88
- package/dist/connect-FVP4HT6T.js +0 -42
- package/dist/content-2SL3VHIK.js +0 -85
- package/dist/conversation-DDZKRMCI.js +0 -93
- package/dist/conversion-report-VARF3FYY.js +0 -125
- package/dist/conversion-upload-command-ZWSNQFGS.js +0 -438
- package/dist/conversion-writes-7SXCEVCV.js +0 -120
- package/dist/conversions-5S5IMXGM.js +0 -135
- package/dist/conversions-ZCOIEPNI.js +0 -76
- package/dist/countries-D5JPHJ4F.js +0 -82
- package/dist/create-3WWCI3KY.js +0 -235
- package/dist/create-GLPBZEQO.js +0 -59
- package/dist/create-GNVYQPBW.js +0 -126
- package/dist/create-GNVYQPBW.js.map +0 -1
- package/dist/create-VHZWE4JQ.js +0 -207
- package/dist/creatives-5C4PRGFT.js +0 -87
- package/dist/creatives-7VEIUD3G.js +0 -182
- package/dist/creatives-CW3FDOPI.js +0 -51
- package/dist/credentials-2QHNU3SD.js +0 -287
- package/dist/critique-HU7ZONQ3.js +0 -213
- package/dist/critique-ZYSNLKNA.js +0 -1449
- package/dist/crm-BD4RK26E.js +0 -318
- package/dist/crm-LYRBIQOO.js +0 -224
- package/dist/crop-KR3LOQBX.js +0 -113
- package/dist/currency-EQ6PB7VF.js +0 -88
- package/dist/custom-conversions-YCOWWTQ7.js +0 -125
- package/dist/customers-MO6ORQAN.js +0 -56
- package/dist/decide-DZG2JUMG.js +0 -199
- package/dist/delete-FIDIBB2H.js +0 -69
- package/dist/delete-NHN73V54.js +0 -66
- package/dist/delete-O3ZTHK3M.js +0 -63
- package/dist/delete-OHTGL7ZJ.js +0 -71
- package/dist/delete-YKR5Y7EQ.js +0 -71
- package/dist/delete-YY6YJ3A6.js +0 -54
- package/dist/demographics-YU75YZFO.js +0 -138
- package/dist/describe-I7VNACXY.js +0 -97
- package/dist/describe-NAINLKWG.js +0 -97
- package/dist/dimensions-K6MDXS4B.js +0 -101
- package/dist/discard-DGK7PLWF.js +0 -66
- package/dist/discover-2UKANLTP.js +0 -172
- package/dist/discover-E7SVCXZ2.js +0 -93
- package/dist/domain-6NZKJW4W.js +0 -98
- package/dist/download-5FCLTMLA.js +0 -299
- package/dist/draft-67OFDA53.js +0 -306
- package/dist/draft-I7H6FVZT.js +0 -281
- package/dist/draft-NJNAGVVV.js +0 -103
- package/dist/draft-QSO3DQG6.js +0 -233
- package/dist/draft-RJ5HOJIR.js +0 -217
- package/dist/draft-RPCIHVRS.js +0 -307
- package/dist/draft-UNCGJCUN.js +0 -100
- package/dist/draft-W5BXN7H3.js +0 -328
- package/dist/env-FKVPPL35.js +0 -43
- package/dist/experiment-JF2QTPRV.js +0 -1397
- package/dist/export-3ROTRKFE.js +0 -168
- package/dist/extract-VXGXR5RD.js +0 -113
- package/dist/facets-3ZIRFE6P.js +0 -114
- package/dist/favorites-DI2EY34L.js +0 -178
- package/dist/feed-EH4FENF4.js +0 -177
- package/dist/feedback-Y7MDEA5O.js +0 -66
- package/dist/feedback-record-SLU5RROR.js +0 -175
- package/dist/fetch-LUOENFIX.js +0 -176
- package/dist/fetch-V3BFDMZF.js +0 -139
- package/dist/find-T7XJV535.js +0 -134
- package/dist/find-T7XJV535.js.map +0 -1
- package/dist/fit-VKSQTJ3Q.js +0 -45
- package/dist/flows-JVU42DAA.js +0 -247
- package/dist/flows-JVU42DAA.js.map +0 -1
- package/dist/follow-PIEAXZE7.js +0 -94
- package/dist/follow-competitors-7N2TPSLA.js +0 -112
- package/dist/following-74CI7UC6.js +0 -113
- package/dist/fonts-BTOLDILR.js +0 -34
- package/dist/forecast-35EBPP2K.js +0 -104
- package/dist/forecast-MHGSW73Z.js +0 -168
- package/dist/funding-FTWMCGZ7.js +0 -85
- package/dist/funnel-YH5LOG65.js +0 -394
- package/dist/ga4-T4ZHANT2.js +0 -55
- package/dist/generate-KVQLIBAX.js +0 -263
- package/dist/generate-KVQLIBAX.js.map +0 -1
- package/dist/get-2IKPCXEM.js +0 -59
- package/dist/get-ABPBK4P3.js +0 -63
- package/dist/get-GSLIPHMD.js +0 -78
- package/dist/get-NE3EEHN6.js +0 -92
- package/dist/get-QOACNGXP.js +0 -56
- package/dist/get-V4BZ4NTK.js +0 -63
- package/dist/get-WZVNWFQA.js +0 -61
- package/dist/gif-O3FLM3CF.js +0 -94
- package/dist/google-I2Z7QOHV.js +0 -3190
- package/dist/google-I2Z7QOHV.js.map +0 -1
- package/dist/google-Z5UAFS3C.js +0 -136
- package/dist/google-ads-E6VNIUEK.js +0 -195
- package/dist/group-34IDEULM.js +0 -43
- package/dist/group-HJMQ5H3L.js +0 -43
- package/dist/gsc-LDZKY5FW.js +0 -30
- package/dist/history-6TYCUXG2.js +0 -133
- package/dist/history-EBJTST3V.js +0 -67
- package/dist/hubspot-LO7ZJPNO.js +0 -779
- package/dist/icon-37BBWKJO.js +0 -137
- package/dist/ideas-2MSUALCW.js +0 -32
- package/dist/images-TZKGJVQ2.js +0 -90
- package/dist/improve-6J2UJ4JZ.js +0 -86
- package/dist/ingest-3RQSRCO5.js +0 -23
- package/dist/ingest-65T56KUI.js +0 -107
- package/dist/insights-5IBNZFHT.js +0 -423
- package/dist/insights-5IBNZFHT.js.map +0 -1
- package/dist/insights-Y7V2UL7E.js +0 -122
- package/dist/insights-Y7V2UL7E.js.map +0 -1
- package/dist/inspect-QLFYFQU2.js +0 -189
- package/dist/inspiration-LYCDPQEY.js +0 -53
- package/dist/intent-PFN2OQEE.js +0 -95
- package/dist/job-ZEI4FC5Y.js +0 -202
- package/dist/job-create-QVTI3TP6.js +0 -109
- package/dist/job-status-GVL7X6EY.js +0 -74
- package/dist/keep-GD5LZFGG.js +0 -78
- package/dist/keyword-gap-WPVHYPVP.js +0 -130
- package/dist/keyword-metrics-Y5LWCCUJ.js +0 -197
- package/dist/keywords-for-site-HGIX4S4F.js +0 -128
- package/dist/landing-EP23XIYL.js +0 -28
- package/dist/landings-4YBHALDA.js +0 -151
- package/dist/languages-5WPQX3DS.js +0 -40
- package/dist/languages-KRXEHLM4.js +0 -55
- package/dist/launches-FOJQPCD2.js +0 -19
- package/dist/layerize-M6ZRXR7M.js +0 -190
- package/dist/leads-2VKYMGP4.js +0 -90
- package/dist/leads-NV57FPKX.js +0 -293
- package/dist/learn-6W55U4ZG.js +0 -203
- package/dist/library-HC5US7CE.js +0 -109
- package/dist/library-HC5US7CE.js.map +0 -1
- package/dist/lighthouse-DXZIQLLK.js +0 -75
- package/dist/line-item-writes-LYWNJWZ2.js +0 -208
- package/dist/line-items-Q47IZGAD.js +0 -103
- package/dist/link-4CQUXZPK.js +0 -64
- package/dist/linkedin-TQCPNOIL.js +0 -1008
- package/dist/list-3RZZFCIR.js +0 -151
- package/dist/list-IZNAL5LS.js +0 -57
- package/dist/list-JHNUOEG6.js +0 -63
- package/dist/list-LC7BZRD4.js +0 -65
- package/dist/list-MEGAZDWZ.js +0 -51
- package/dist/list-RCOUIFKY.js +0 -303
- package/dist/list-RGMBFJHB.js +0 -76
- package/dist/list-VXNYIVS5.js +0 -50
- package/dist/locations-CB6KK3ZU.js +0 -40
- package/dist/locations-ZMJ5MJ6Z.js +0 -67
- package/dist/log-5V5YMZGV.js +0 -92
- package/dist/logo-SOCGJLY5.js +0 -18
- package/dist/lookup-MQBDRZZT.js +0 -22
- package/dist/make-M5LD3MX2.js +0 -633
- package/dist/map-GD2BT7RO.js +0 -433
- package/dist/map-GD2BT7RO.js.map +0 -1
- package/dist/mcp-WFRPWK3W.js +0 -463
- package/dist/media-OYAN23YG.js +0 -15
- package/dist/media-QCWYLIO2.js +0 -89
- package/dist/media-sources-YQURN7ZH.js +0 -32
- package/dist/members-4UCL6OKS.js +0 -44
- package/dist/meta-B5XDM6F6.js +0 -69
- package/dist/metrics-W4SLBSPD.js +0 -152
- package/dist/models-IUO3XGQK.js +0 -155
- package/dist/normalize-2OUEDA6D.js +0 -806
- package/dist/normalize-O6ATWOWP.js +0 -141
- package/dist/options-U4PY24PS.js +0 -122
- package/dist/options-U4PY24PS.js.map +0 -1
- package/dist/output-ZKNCKP7T.js +0 -24
- package/dist/page-EMJESGQZ.js +0 -105
- package/dist/pages-OMKAYKCF.js +0 -66
- package/dist/performance-WGCN2XIC.js +0 -24
- package/dist/pinterest-I6JO7LXL.js +0 -96
- package/dist/pixels-5JBTFWCO.js +0 -108
- package/dist/pixels-G64Y3J22.js +0 -60
- package/dist/plan-KNUKCIS7.js +0 -207
- package/dist/preview-TBINZJ2C.js +0 -169
- package/dist/profile-UV3FTEUC.js +0 -159
- package/dist/profile-UV3FTEUC.js.map +0 -1
- package/dist/promoted-tweet-writes-YVSGOBAS.js +0 -180
- package/dist/promoted-tweets-DQDEHTP4.js +0 -119
- package/dist/properties-CTFQ7IK3.js +0 -101
- package/dist/publish-I2XOKEHC.js +0 -322
- package/dist/query-JMVLWKVY.js +0 -236
- package/dist/query-L3TGHKKM.js +0 -61
- package/dist/query-T5IBVQ3B.js +0 -247
- package/dist/questionnaire-APZ6FMPE.js +0 -93
- package/dist/read-W5ZK5G7Q.js +0 -141
- package/dist/reddit-GYVC7XPJ.js +0 -26
- package/dist/refresh-XYZ2Q2SN.js +0 -94
- package/dist/release-HOSW3VHR.js +0 -60
- package/dist/relevant-pages-F5AGXYC5.js +0 -99
- package/dist/remove-7QYX2OYJ.js +0 -50
- package/dist/remove-RFOFNOYU.js +0 -48
- package/dist/reopen-HIVOKJOR.js +0 -63
- package/dist/rerun-U6F4TEPA.js +0 -84
- package/dist/research-43V2DWVK.js +0 -62
- package/dist/research-7TN24S4D.js +0 -262
- package/dist/reset-B3FAY2EH.js +0 -54
- package/dist/reset-R5RM7ZTG.js +0 -54
- package/dist/resolve-I5YOKTRV.js +0 -121
- package/dist/retry-A335Q6HS.js +0 -62
- package/dist/retry-R4OWR7IK.js +0 -60
- package/dist/retry-failed-5XDCVPQA.js +0 -45
- package/dist/run-TNO2VF2U.js +0 -59
- package/dist/run-VZGXOWFL.js +0 -65
- package/dist/scaffold-ad-VKOXZENI.js +0 -1548
- package/dist/scaffold-static-ad-CMYP2ZZ6.js +0 -971
- package/dist/scaffold-video-AKA5LMEI.js +0 -872
- package/dist/schedule-4N6JTJJT.js +0 -66
- package/dist/schedule-HKIY6JI2.js +0 -61
- package/dist/scheduled-agents-QM5Z3UNW.js +0 -50
- package/dist/scheduled-agents-QM5Z3UNW.js.map +0 -1
- package/dist/schema-J6AIGXZ7.js +0 -132
- package/dist/schema-RNVEYR2U.js +0 -63
- package/dist/scrape-VK7RZO4M.js +0 -2241
- package/dist/screenshot-PWUROUXU.js +0 -102
- package/dist/search-3ZW3RRPX.js +0 -92
- package/dist/search-J2VQNY4A.js +0 -91
- package/dist/search-LRY5SYDM.js +0 -224
- package/dist/search-M23XX3N5.js +0 -26
- package/dist/search-PK4IUZ4C.js +0 -174
- package/dist/search-copy-AVQESTYJ.js +0 -168
- package/dist/seeds-C6JI3WTG.js +0 -101
- package/dist/select-NBRJ6WLY.js +0 -94
- package/dist/sends-DYIE7W5U.js +0 -309
- package/dist/sequences-CQVKVRC4.js +0 -146
- package/dist/set-prompt-VNJ334YW.js +0 -185
- package/dist/setup-links-77534G7K.js +0 -141
- package/dist/shared-2SQRS476.js +0 -31
- package/dist/site-EFQ2QFZX.js +0 -177
- package/dist/sitemaps-XFLHKP2D.js +0 -87
- package/dist/sites-5JNZWY3A.js +0 -101
- package/dist/skills-43P4WK6Y.js +0 -47
- package/dist/sources-JQM4AKNL.js +0 -30
- package/dist/sources-WJ3EZ46E.js +0 -454
- package/dist/sources-WJ3EZ46E.js.map +0 -1
- package/dist/stale-Y5HC7BNS.js +0 -63
- package/dist/stats-LLTHL5VR.js +0 -34
- package/dist/status-T25VZANV.js +0 -62
- package/dist/sticker-B5DJ56YG.js +0 -94
- package/dist/stock-WXAFJFAC.js +0 -179
- package/dist/studio-X546AIF2.js +0 -49
- package/dist/sync-GCVG6QTL.js +0 -54
- package/dist/sync-JLECDZYS.js +0 -265
- package/dist/sync-JLECDZYS.js.map +0 -1
- package/dist/sync-VDPGFUZS.js +0 -54
- package/dist/sync-definition-GKF2KKFC.js +0 -54
- package/dist/tag-manager-HG76EBNX.js +0 -43
- package/dist/tags-6KXDDSBW.js +0 -20
- package/dist/tags-NFBMGARL.js +0 -20
- package/dist/tags-SJIXKMQH.js +0 -20
- package/dist/tags-T4OK7QXX.js +0 -26
- package/dist/tags-UE2S4UCB.js +0 -224
- package/dist/targeting-constants-GR7RUKE3.js +0 -118
- package/dist/targeting-criteria-NZVF6ZOK.js +0 -113
- package/dist/targets-4II2CNRP.js +0 -102
- package/dist/testimonials-NODH7BWK.js +0 -32
- package/dist/themes-NO3DM5M3.js +0 -94
- package/dist/top-companies-MWOOBUSM.js +0 -108
- package/dist/translate-26KMJSWL.js +0 -598
- package/dist/triggers-LYPP4FKZ.js +0 -141
- package/dist/tweets-ZWC7YOQ4.js +0 -123
- package/dist/unfollow-LBMJXF4E.js +0 -46
- package/dist/unlink-OL7UNUYB.js +0 -67
- package/dist/unstage-5AZAD2KF.js +0 -95
- package/dist/unstage-6GUUIEWL.js +0 -84
- package/dist/update-2YZVDSWQ.js +0 -142
- package/dist/update-HSMB6JMZ.js +0 -155
- package/dist/update-PCVBEGSU.js +0 -98
- package/dist/update-QWWAXFWH.js +0 -63
- package/dist/upload-MPMP3JRS.js +0 -150
- package/dist/upload-VBPWOAKD.js +0 -126
- package/dist/upscale-TXJFZLUU.js +0 -109
- package/dist/url-options-ML2D74NM.js +0 -130
- package/dist/url-params-4AD467SA.js +0 -98
- package/dist/use-SUY57ZJA.js +0 -95
- package/dist/validate-W4QZZFXV.js +0 -88
- package/dist/variant-TE6CEHZW.js +0 -338
- package/dist/videos-DJHFHZI5.js +0 -39
- package/dist/view-EQZB7Z6N.js +0 -121
- package/dist/viewer-check-GL2UNAGR.js +0 -68
- package/dist/viewer-check-GL2UNAGR.js.map +0 -1
- package/dist/web-N4RNU7UH.js +0 -140
- package/dist/winners-UPM6DFRB.js +0 -113
- package/dist/winning-ads-SVUUR443.js +0 -84
- package/dist/write-commands-5HSNHURZ.js +0 -713
- package/dist/write-commands-BCU5YATY.js +0 -737
- package/dist/write-commands-WVJJJFBP.js +0 -1015
- package/dist/write-commands-YNI6KS2B.js +0 -206
- package/dist/x-65R63NFK.js +0 -47
- /package/dist/{account-QR5LCE74.js.map → account-67PZBEP6.js.map} +0 -0
- /package/dist/{account-MLAW7QO4.js.map → account-HZC6X2PH.js.map} +0 -0
- /package/dist/{account-UP67V2FH.js.map → account-TQUV64XP.js.map} +0 -0
- /package/dist/{account-reads-5RGYKIIJ.js.map → account-reads-IRBTERTY.js.map} +0 -0
- /package/dist/{account-writes-EGRMVWVQ.js.map → account-writes-ALDSHAEV.js.map} +0 -0
- /package/dist/{accounts-GUHRV3RA.js.map → accounts-4P54UY6F.js.map} +0 -0
- /package/dist/{accounts-CU4Q2AV7.js.map → accounts-6UNBLIUH.js.map} +0 -0
- /package/dist/{accounts-WH7SRFCB.js.map → accounts-GVTZN4VE.js.map} +0 -0
- /package/dist/{accounts-L2SNBNOO.js.map → accounts-NJ6TF6WH.js.map} +0 -0
- /package/dist/{accounts-LG4B7NWO.js.map → accounts-WTB737BM.js.map} +0 -0
- /package/dist/{actions-LMKQCN4E.js.map → actions-D3BOWDEB.js.map} +0 -0
- /package/dist/{active-entities-2CQHGACO.js.map → active-entities-J2O76SRP.js.map} +0 -0
- /package/dist/{activities-EDCSBYGM.js.map → activities-2J7FTZMN.js.map} +0 -0
- /package/dist/{ad-group-writes-WUAYGWDY.js.map → ad-group-writes-PQKDCMQF.js.map} +0 -0
- /package/dist/{ad-groups-BVEJJHX6.js.map → ad-groups-NGYEJMV4.js.map} +0 -0
- /package/dist/{ad-writes-WTWRSFLW.js.map → ad-writes-77FVHJTS.js.map} +0 -0
- /package/dist/{add-ZMZJSBQT.js.map → add-2FHPLALV.js.map} +0 -0
- /package/dist/{add-DDKP3OOS.js.map → add-2XJYOGKW.js.map} +0 -0
- /package/dist/{add-HQERVTGX.js.map → add-62JPGFD4.js.map} +0 -0
- /package/dist/{add-PGD6PNXH.js.map → add-JP76KCCY.js.map} +0 -0
- /package/dist/{add-accounts-A2YRT5NR.js.map → add-accounts-M5Y4DQFM.js.map} +0 -0
- /package/dist/{ads-ZOQM52YG.js.map → ads-DB5NDHX5.js.map} +0 -0
- /package/dist/{ads-HRCVRXPO.js.map → ads-LYNA2I7B.js.map} +0 -0
- /package/dist/{ads-ZOYLP5R3.js.map → ads-W2IPUWHQ.js.map} +0 -0
- /package/dist/{adsets-RKSBDAHV.js.map → adsets-PY5ZKGBO.js.map} +0 -0
- /package/dist/{advertisers-34GDHZC7.js.map → advertisers-BVAZ4XUX.js.map} +0 -0
- /package/dist/{advertisers-OEHGT3U2.js.map → advertisers-NZPJQF7W.js.map} +0 -0
- /package/dist/{analytics-JYBKFGP4.js.map → analytics-NTPZKMYK.js.map} +0 -0
- /package/dist/{animate-NVEQOLHK.js.map → animate-EIV2OHXT.js.map} +0 -0
- /package/dist/{assign-2HJHKJKL.js.map → assign-UNIA5YDC.js.map} +0 -0
- /package/dist/{audience-size-W74YHNBX.js.map → audience-size-4NN6ENP5.js.map} +0 -0
- /package/dist/{audiences-ZUFCFB7M.js.map → audiences-45ZCY2VZ.js.map} +0 -0
- /package/dist/{audiences-SXBA4R3I.js.map → audiences-UUIYQFS6.js.map} +0 -0
- /package/dist/{audiences-FNDYOAAW.js.map → audiences-VYFX7OFS.js.map} +0 -0
- /package/dist/{audit-76SDQ7C6.js.map → audit-NSVHNA5J.js.map} +0 -0
- /package/dist/{audit-MADJMBNF.js.map → audit-OWXTLQC4.js.map} +0 -0
- /package/dist/{autocomplete-GZ4XVGC4.js.map → autocomplete-EHWAQYVB.js.map} +0 -0
- /package/dist/{avatars-AILCY4T2.js.map → avatars-7WZPW7BV.js.map} +0 -0
- /package/dist/{bid-pricing-5435DI5H.js.map → bid-pricing-F5NDMS6W.js.map} +0 -0
- /package/dist/{brand-4RWOQ7EN.js.map → brand-QAJ5VWFM.js.map} +0 -0
- /package/dist/{brief-2Y6PG6QI.js.map → brief-V2OB5OJW.js.map} +0 -0
- /package/dist/{businesses-OFQG3YSL.js.map → businesses-ZH67AXR6.js.map} +0 -0
- /package/dist/{campaign-groups-XWQX7PSF.js.map → campaign-groups-KFJKLNNU.js.map} +0 -0
- /package/dist/{campaign-writes-GFOOQDZR.js.map → campaign-writes-BDGQ6XB3.js.map} +0 -0
- /package/dist/{campaign-writes-F4LVAD5N.js.map → campaign-writes-FNMAGCKB.js.map} +0 -0
- /package/dist/{campaigns-YBOTJUV2.js.map → campaigns-66OOGMDW.js.map} +0 -0
- /package/dist/{campaigns-AFWP56ST.js.map → campaigns-6HMHOXXY.js.map} +0 -0
- /package/dist/{campaigns-FHZLLATX.js.map → campaigns-HJTGX5WW.js.map} +0 -0
- /package/dist/{campaigns-W7DMOOXW.js.map → campaigns-KT3J2O7S.js.map} +0 -0
- /package/dist/{canvas-RCRBTZKW.js.map → canvas-MQ6ZE5CZ.js.map} +0 -0
- /package/dist/{capabilities-7DSVIC2L.js.map → capabilities-RVUMPP7C.js.map} +0 -0
- /package/dist/{cards-WZSPTO4I.js.map → cards-I4BZIYUV.js.map} +0 -0
- /package/dist/{catalog-YY33VIDD.js.map → catalog-NLL5VDRK.js.map} +0 -0
- /package/dist/{catalogs-NQX6UJ7C.js.map → catalogs-B2NKQO24.js.map} +0 -0
- /package/dist/{changes-OZ2D64FV.js.map → changes-FX3TPYGT.js.map} +0 -0
- /package/dist/{chatgpt-33BYXDJP.js.map → chatgpt-3NS2R442.js.map} +0 -0
- /package/dist/{chats-4GAFW3QN.js.map → chats-72L7U4ML.js.map} +0 -0
- /package/dist/{chunk-RV222F63.js.map → chunk-2KIF3U2C.js.map} +0 -0
- /package/dist/{chunk-6QNEVETC.js.map → chunk-35ENAFJX.js.map} +0 -0
- /package/dist/{chunk-365Z5TYR.js.map → chunk-4TU2MWBJ.js.map} +0 -0
- /package/dist/{chunk-IBCTXMQD.js.map → chunk-57UECM77.js.map} +0 -0
- /package/dist/{chunk-JQUPL6IQ.js.map → chunk-65BQAJMH.js.map} +0 -0
- /package/dist/{chunk-3YNHIJCI.js.map → chunk-6GFJFTG6.js.map} +0 -0
- /package/dist/{chunk-WBCQEXEH.js.map → chunk-6HPIWZHS.js.map} +0 -0
- /package/dist/{chunk-X3PVW3RM.js.map → chunk-73B3RQJC.js.map} +0 -0
- /package/dist/{chunk-EMDUW6QN.js.map → chunk-7GYM4I5I.js.map} +0 -0
- /package/dist/{chunk-K2DBAN3C.js.map → chunk-7NTION5F.js.map} +0 -0
- /package/dist/{chunk-IPYG4OYK.js.map → chunk-AFDPISNJ.js.map} +0 -0
- /package/dist/{chunk-CNAPU47I.js.map → chunk-AIDXJHZ4.js.map} +0 -0
- /package/dist/{chunk-3UA4THEF.js.map → chunk-AR7PU7BB.js.map} +0 -0
- /package/dist/{chunk-MZQBL7QU.js.map → chunk-AXVOBRQT.js.map} +0 -0
- /package/dist/{chunk-VTK2W2NI.js.map → chunk-BBBZR7MF.js.map} +0 -0
- /package/dist/{chunk-7YOPIYCL.js.map → chunk-BVYPRGPG.js.map} +0 -0
- /package/dist/{chunk-5B3AEQDZ.js.map → chunk-CBCGVPHB.js.map} +0 -0
- /package/dist/{chunk-XLZMKMKD.js.map → chunk-CECS3DHZ.js.map} +0 -0
- /package/dist/{chunk-P6SFEIA5.js.map → chunk-CZYUZL6R.js.map} +0 -0
- /package/dist/{chunk-L66QWXPF.js.map → chunk-D5MF337I.js.map} +0 -0
- /package/dist/{chunk-H5GLPZ36.js.map → chunk-DDTILZXI.js.map} +0 -0
- /package/dist/{chunk-LLN7NP3O.js.map → chunk-DL2W4N7J.js.map} +0 -0
- /package/dist/{chunk-FJIUMOVD.js.map → chunk-DLUWHZCE.js.map} +0 -0
- /package/dist/{chunk-2MTAKAHE.js.map → chunk-EOGPNV25.js.map} +0 -0
- /package/dist/{chunk-MGKEUB7P.js.map → chunk-F3FSD6BO.js.map} +0 -0
- /package/dist/{chunk-OKHSMDCY.js.map → chunk-FDG2LLX7.js.map} +0 -0
- /package/dist/{chunk-D7KNUZ6Z.js.map → chunk-FYAWA3FO.js.map} +0 -0
- /package/dist/{chunk-G3AOJ5FE.js.map → chunk-G3KO2HFZ.js.map} +0 -0
- /package/dist/{chunk-HDYF3EIN.js.map → chunk-GKXMIXEV.js.map} +0 -0
- /package/dist/{chunk-6DE3EHEF.js.map → chunk-GT4UFORX.js.map} +0 -0
- /package/dist/{chunk-JA4PDZNY.js.map → chunk-GZ6YS7TG.js.map} +0 -0
- /package/dist/{chunk-BEJ2AVPJ.js.map → chunk-H7JOVRHO.js.map} +0 -0
- /package/dist/{chunk-OLV66PDA.js.map → chunk-IJDF7D4U.js.map} +0 -0
- /package/dist/{chunk-CHZ3ZNGJ.js.map → chunk-INJHF7AF.js.map} +0 -0
- /package/dist/{chunk-33STLGET.js.map → chunk-IZFGTGFR.js.map} +0 -0
- /package/dist/{chunk-TVKE3OXR.js.map → chunk-J2QOKD2F.js.map} +0 -0
- /package/dist/{chunk-JHIVRENV.js.map → chunk-J5WZ7SAD.js.map} +0 -0
- /package/dist/{chunk-YDAEGV42.js.map → chunk-JC6GNMA7.js.map} +0 -0
- /package/dist/{chunk-SS445HDO.js.map → chunk-KB5HBQEK.js.map} +0 -0
- /package/dist/{chunk-JLCK7K6Y.js.map → chunk-KB5XJHQX.js.map} +0 -0
- /package/dist/{chunk-CGOVXSSY.js.map → chunk-LHNL6JM2.js.map} +0 -0
- /package/dist/{chunk-VPBCR6DT.js.map → chunk-NAMEFVWL.js.map} +0 -0
- /package/dist/{chunk-6SOB7QMD.js.map → chunk-NORAJYUI.js.map} +0 -0
- /package/dist/{chunk-MSKB6I5E.js.map → chunk-NQTSHC7Z.js.map} +0 -0
- /package/dist/{chunk-LHKCZG2R.js.map → chunk-O3LHCQ7Q.js.map} +0 -0
- /package/dist/{chunk-SKYXCMVX.js.map → chunk-O5XLQUDS.js.map} +0 -0
- /package/dist/{chunk-LK64CCN4.js.map → chunk-P3XJUOWT.js.map} +0 -0
- /package/dist/{chunk-MCDU24JX.js.map → chunk-P3ZJIIDS.js.map} +0 -0
- /package/dist/{chunk-U6RTBCU7.js.map → chunk-P5T7OZKV.js.map} +0 -0
- /package/dist/{chunk-FK6GBCQ4.js.map → chunk-PA5MYVQY.js.map} +0 -0
- /package/dist/{chunk-TLOFNP4Q.js.map → chunk-PELW56D7.js.map} +0 -0
- /package/dist/{chunk-WSD63XGR.js.map → chunk-PGVF4SB7.js.map} +0 -0
- /package/dist/{chunk-Z2HNETUP.js.map → chunk-Q676JHJA.js.map} +0 -0
- /package/dist/{chunk-4E27H5EG.js.map → chunk-QDOVBGKZ.js.map} +0 -0
- /package/dist/{chunk-7JFGOMJN.js.map → chunk-QHIUJXU7.js.map} +0 -0
- /package/dist/{chunk-FHY3BM2O.js.map → chunk-QJPPPQPD.js.map} +0 -0
- /package/dist/{chunk-LL6T64OU.js.map → chunk-QNEX7FEC.js.map} +0 -0
- /package/dist/{chunk-PAJJXZ3Z.js.map → chunk-QWX3QJNM.js.map} +0 -0
- /package/dist/{chunk-2YXY56FK.js.map → chunk-RUHMLPH5.js.map} +0 -0
- /package/dist/{chunk-7VHPAGPK.js.map → chunk-SEPPFM5S.js.map} +0 -0
- /package/dist/{chunk-NC3GOGUT.js.map → chunk-SJLIV6PF.js.map} +0 -0
- /package/dist/{chunk-XA2S53QZ.js.map → chunk-SLCSLVGS.js.map} +0 -0
- /package/dist/{chunk-FEELV5IV.js.map → chunk-SQ7DRYCC.js.map} +0 -0
- /package/dist/{chunk-BKE32DQB.js.map → chunk-THU74XR6.js.map} +0 -0
- /package/dist/{chunk-42ESQ4AJ.js.map → chunk-TY7TEU3T.js.map} +0 -0
- /package/dist/{chunk-LNZM6SUE.js.map → chunk-UXTZ5MBE.js.map} +0 -0
- /package/dist/{chunk-IZNTYRKB.js.map → chunk-V43RLLZE.js.map} +0 -0
- /package/dist/{chunk-3RS4M7AR.js.map → chunk-V7PMSMPF.js.map} +0 -0
- /package/dist/{chunk-3SF5KBIO.js.map → chunk-VFSLIDXA.js.map} +0 -0
- /package/dist/{chunk-KMMVQ2MR.js.map → chunk-VHMNSO2B.js.map} +0 -0
- /package/dist/{chunk-M62WN522.js.map → chunk-VKH6LSC2.js.map} +0 -0
- /package/dist/{chunk-BCKSBIQ4.js.map → chunk-VTCBPYGE.js.map} +0 -0
- /package/dist/{chunk-MX5TREEM.js.map → chunk-W6ASM6UP.js.map} +0 -0
- /package/dist/{chunk-IMH2ZKKC.js.map → chunk-W7KB3LJE.js.map} +0 -0
- /package/dist/{chunk-TUVS5SPF.js.map → chunk-WESRVXFS.js.map} +0 -0
- /package/dist/{chunk-3WQ4ANUO.js.map → chunk-WILN7TD6.js.map} +0 -0
- /package/dist/{chunk-TJ5N6IVL.js.map → chunk-YGL5LTEA.js.map} +0 -0
- /package/dist/{chunk-F4H6VVPE.js.map → chunk-YONSWWCF.js.map} +0 -0
- /package/dist/{chunk-NFDAIY6J.js.map → chunk-Z5KBRTB2.js.map} +0 -0
- /package/dist/{chunk-ENY76SPM.js.map → chunk-ZBKBTTHD.js.map} +0 -0
- /package/dist/{chunk-MU23X3VU.js.map → chunk-ZJR5E53N.js.map} +0 -0
- /package/dist/{chunk-P4UQBJNY.js.map → chunk-ZLU5QDLU.js.map} +0 -0
- /package/dist/{chunk-RMB2MIOJ.js.map → chunk-ZNNC6B75.js.map} +0 -0
- /package/dist/{chunk-KKXHGAYA.js.map → chunk-ZO2YLHRW.js.map} +0 -0
- /package/dist/{chunk-M6CI2HYA.js.map → chunk-ZPJE6D7W.js.map} +0 -0
- /package/dist/{claim-XBVKGN7A.js.map → claim-DIITRUIN.js.map} +0 -0
- /package/dist/{client-2ABVGUQB.js.map → client-LY2JCQS2.js.map} +0 -0
- /package/dist/{code-GHG7DEPV.js.map → code-7CBNYUPU.js.map} +0 -0
- /package/dist/{companies-IGDIQ64M.js.map → companies-HHSYHL74.js.map} +0 -0
- /package/dist/{complete-CYPVVQWT.js.map → complete-M3TURPU5.js.map} +0 -0
- /package/dist/{config-L26WNV2G.js.map → config-7COTFJWK.js.map} +0 -0
- /package/dist/{connect-FVP4HT6T.js.map → connect-TQL5EVVE.js.map} +0 -0
- /package/dist/{content-2SL3VHIK.js.map → content-OXMONMHJ.js.map} +0 -0
- /package/dist/{conversation-DDZKRMCI.js.map → conversation-GE273GC4.js.map} +0 -0
- /package/dist/{conversion-report-VARF3FYY.js.map → conversion-report-BH4HH2Q3.js.map} +0 -0
- /package/dist/{conversion-upload-command-ZWSNQFGS.js.map → conversion-upload-command-QWWYCTNV.js.map} +0 -0
- /package/dist/{conversion-writes-7SXCEVCV.js.map → conversion-writes-HG2O6JI7.js.map} +0 -0
- /package/dist/{conversions-5S5IMXGM.js.map → conversions-DP6ZF57D.js.map} +0 -0
- /package/dist/{conversions-ZCOIEPNI.js.map → conversions-XPSN7SOW.js.map} +0 -0
- /package/dist/{countries-D5JPHJ4F.js.map → countries-UXAUGSBR.js.map} +0 -0
- /package/dist/{create-GLPBZEQO.js.map → create-ENQWVWGB.js.map} +0 -0
- /package/dist/{create-VHZWE4JQ.js.map → create-NVXJP477.js.map} +0 -0
- /package/dist/{create-3WWCI3KY.js.map → create-XLZYNFKN.js.map} +0 -0
- /package/dist/{creatives-7VEIUD3G.js.map → creatives-O3444SBT.js.map} +0 -0
- /package/dist/{creatives-5C4PRGFT.js.map → creatives-ONKKGW5T.js.map} +0 -0
- /package/dist/{creatives-CW3FDOPI.js.map → creatives-U6IKIQBT.js.map} +0 -0
- /package/dist/{credentials-2QHNU3SD.js.map → credentials-3UY7RVUL.js.map} +0 -0
- /package/dist/{critique-ZYSNLKNA.js.map → critique-3LMW73QJ.js.map} +0 -0
- /package/dist/{critique-HU7ZONQ3.js.map → critique-KB32Y4AW.js.map} +0 -0
- /package/dist/{crm-LYRBIQOO.js.map → crm-OX67TBWD.js.map} +0 -0
- /package/dist/{crm-BD4RK26E.js.map → crm-XXU5XZIQ.js.map} +0 -0
- /package/dist/{crop-KR3LOQBX.js.map → crop-3ZV5BFCA.js.map} +0 -0
- /package/dist/{currency-EQ6PB7VF.js.map → currency-XP4EMU4M.js.map} +0 -0
- /package/dist/{custom-conversions-YCOWWTQ7.js.map → custom-conversions-TV23XQHW.js.map} +0 -0
- /package/dist/{customers-MO6ORQAN.js.map → customers-EJ7FVCTC.js.map} +0 -0
- /package/dist/{decide-DZG2JUMG.js.map → decide-EGQ3CRHI.js.map} +0 -0
- /package/dist/{delete-O3ZTHK3M.js.map → delete-55IHV25V.js.map} +0 -0
- /package/dist/{delete-YY6YJ3A6.js.map → delete-GXLPIGTC.js.map} +0 -0
- /package/dist/{delete-OHTGL7ZJ.js.map → delete-MUPTLWHI.js.map} +0 -0
- /package/dist/{delete-NHN73V54.js.map → delete-OW2N4WIA.js.map} +0 -0
- /package/dist/{delete-FIDIBB2H.js.map → delete-R2SEUSLV.js.map} +0 -0
- /package/dist/{delete-YKR5Y7EQ.js.map → delete-ZGBBWFAW.js.map} +0 -0
- /package/dist/{demographics-YU75YZFO.js.map → demographics-K6DEWUE5.js.map} +0 -0
- /package/dist/{describe-I7VNACXY.js.map → describe-4O3VCV6L.js.map} +0 -0
- /package/dist/{describe-NAINLKWG.js.map → describe-FCTM53MM.js.map} +0 -0
- /package/dist/{dimensions-K6MDXS4B.js.map → dimensions-AD4VS5AH.js.map} +0 -0
- /package/dist/{discard-DGK7PLWF.js.map → discard-NZ7FQJEE.js.map} +0 -0
- /package/dist/{discover-2UKANLTP.js.map → discover-OISU5EJF.js.map} +0 -0
- /package/dist/{discover-E7SVCXZ2.js.map → discover-UQXQUPNO.js.map} +0 -0
- /package/dist/{domain-6NZKJW4W.js.map → domain-DXBHNZLO.js.map} +0 -0
- /package/dist/{download-5FCLTMLA.js.map → download-IE6XLFHM.js.map} +0 -0
- /package/dist/{draft-67OFDA53.js.map → draft-DB6KMBCH.js.map} +0 -0
- /package/dist/{draft-NJNAGVVV.js.map → draft-ENA7S4Y7.js.map} +0 -0
- /package/dist/{draft-RPCIHVRS.js.map → draft-HJBAG3SN.js.map} +0 -0
- /package/dist/{draft-W5BXN7H3.js.map → draft-ICEVLF4E.js.map} +0 -0
- /package/dist/{draft-UNCGJCUN.js.map → draft-MGKJ5RYI.js.map} +0 -0
- /package/dist/{draft-I7H6FVZT.js.map → draft-OBFKQEPK.js.map} +0 -0
- /package/dist/{draft-RJ5HOJIR.js.map → draft-OOHU6NMA.js.map} +0 -0
- /package/dist/{draft-QSO3DQG6.js.map → draft-S6VPKZYY.js.map} +0 -0
- /package/dist/{env-FKVPPL35.js.map → env-YLOVBOAE.js.map} +0 -0
- /package/dist/{experiment-JF2QTPRV.js.map → experiment-PHFP6IBM.js.map} +0 -0
- /package/dist/{export-3ROTRKFE.js.map → export-R2IMUCQO.js.map} +0 -0
- /package/dist/{extract-VXGXR5RD.js.map → extract-OQP3TSQG.js.map} +0 -0
- /package/dist/{facets-3ZIRFE6P.js.map → facets-NUKR7I57.js.map} +0 -0
- /package/dist/{favorites-DI2EY34L.js.map → favorites-NS73FDG5.js.map} +0 -0
- /package/dist/{feed-EH4FENF4.js.map → feed-SQPOB7SP.js.map} +0 -0
- /package/dist/{feedback-Y7MDEA5O.js.map → feedback-SQAAAKOQ.js.map} +0 -0
- /package/dist/{feedback-record-SLU5RROR.js.map → feedback-record-7W7WBHSQ.js.map} +0 -0
- /package/dist/{fetch-V3BFDMZF.js.map → fetch-PQ3NOPIE.js.map} +0 -0
- /package/dist/{fetch-LUOENFIX.js.map → fetch-PTSAVQW5.js.map} +0 -0
- /package/dist/{fit-VKSQTJ3Q.js.map → fit-IITU6UWC.js.map} +0 -0
- /package/dist/{follow-PIEAXZE7.js.map → follow-N3DQZV7M.js.map} +0 -0
- /package/dist/{follow-competitors-7N2TPSLA.js.map → follow-competitors-BZPCBGIB.js.map} +0 -0
- /package/dist/{following-74CI7UC6.js.map → following-KESWTEBR.js.map} +0 -0
- /package/dist/{fonts-BTOLDILR.js.map → fonts-FG6XMK5A.js.map} +0 -0
- /package/dist/{forecast-MHGSW73Z.js.map → forecast-43XBSHJR.js.map} +0 -0
- /package/dist/{forecast-35EBPP2K.js.map → forecast-MAFZQ4UF.js.map} +0 -0
- /package/dist/{funding-FTWMCGZ7.js.map → funding-HYSCYS62.js.map} +0 -0
- /package/dist/{funnel-YH5LOG65.js.map → funnel-32CNV57L.js.map} +0 -0
- /package/dist/{ga4-T4ZHANT2.js.map → ga4-J2KTNCD4.js.map} +0 -0
- /package/dist/{get-V4BZ4NTK.js.map → get-3P2EQLP3.js.map} +0 -0
- /package/dist/{get-WZVNWFQA.js.map → get-FJB3X44Z.js.map} +0 -0
- /package/dist/{get-GSLIPHMD.js.map → get-GDF5V3RN.js.map} +0 -0
- /package/dist/{get-ABPBK4P3.js.map → get-MBGO4VJD.js.map} +0 -0
- /package/dist/{get-2IKPCXEM.js.map → get-NOKSZPIV.js.map} +0 -0
- /package/dist/{get-QOACNGXP.js.map → get-W7MKBMQ3.js.map} +0 -0
- /package/dist/{get-NE3EEHN6.js.map → get-WHC4SRYK.js.map} +0 -0
- /package/dist/{gif-O3FLM3CF.js.map → gif-SFYVDLCU.js.map} +0 -0
- /package/dist/{google-Z5UAFS3C.js.map → google-XRAMIES5.js.map} +0 -0
- /package/dist/{google-ads-E6VNIUEK.js.map → google-ads-N55SE2CB.js.map} +0 -0
- /package/dist/{group-34IDEULM.js.map → group-6RDJTWVN.js.map} +0 -0
- /package/dist/{group-HJMQ5H3L.js.map → group-TZJCKHKS.js.map} +0 -0
- /package/dist/{gsc-LDZKY5FW.js.map → gsc-HD6UFRYM.js.map} +0 -0
- /package/dist/{history-EBJTST3V.js.map → history-4RWCE7WW.js.map} +0 -0
- /package/dist/{history-6TYCUXG2.js.map → history-IROFQ6LA.js.map} +0 -0
- /package/dist/{hubspot-LO7ZJPNO.js.map → hubspot-75N4AI43.js.map} +0 -0
- /package/dist/{icon-37BBWKJO.js.map → icon-BMCDFGMN.js.map} +0 -0
- /package/dist/{ideas-2MSUALCW.js.map → ideas-7UQMXLI5.js.map} +0 -0
- /package/dist/{images-TZKGJVQ2.js.map → images-OFR3S2U4.js.map} +0 -0
- /package/dist/{improve-6J2UJ4JZ.js.map → improve-P6F2HXYQ.js.map} +0 -0
- /package/dist/{ingest-65T56KUI.js.map → ingest-HGR7KSG4.js.map} +0 -0
- /package/dist/{ingest-3RQSRCO5.js.map → ingest-XU4WXE4U.js.map} +0 -0
- /package/dist/{inspect-QLFYFQU2.js.map → inspect-TL2VZBN7.js.map} +0 -0
- /package/dist/{inspiration-LYCDPQEY.js.map → inspiration-7WTS3AVI.js.map} +0 -0
- /package/dist/{intent-PFN2OQEE.js.map → intent-OCZE4O7R.js.map} +0 -0
- /package/dist/{job-ZEI4FC5Y.js.map → job-XMPQ27SJ.js.map} +0 -0
- /package/dist/{job-create-QVTI3TP6.js.map → job-create-HTDQHPEF.js.map} +0 -0
- /package/dist/{job-status-GVL7X6EY.js.map → job-status-L7WUTZ2C.js.map} +0 -0
- /package/dist/{keep-GD5LZFGG.js.map → keep-42SLV4CR.js.map} +0 -0
- /package/dist/{keyword-gap-WPVHYPVP.js.map → keyword-gap-323NE4AK.js.map} +0 -0
- /package/dist/{keyword-metrics-Y5LWCCUJ.js.map → keyword-metrics-V6DGO7U5.js.map} +0 -0
- /package/dist/{keywords-for-site-HGIX4S4F.js.map → keywords-for-site-ANSLXBHB.js.map} +0 -0
- /package/dist/{landing-EP23XIYL.js.map → landing-K25GUBLZ.js.map} +0 -0
- /package/dist/{landings-4YBHALDA.js.map → landings-5IYXHA5Z.js.map} +0 -0
- /package/dist/{languages-KRXEHLM4.js.map → languages-SRFGW6Z3.js.map} +0 -0
- /package/dist/{languages-5WPQX3DS.js.map → languages-US6JUATO.js.map} +0 -0
- /package/dist/{layerize-M6ZRXR7M.js.map → layerize-U672AXED.js.map} +0 -0
- /package/dist/{leads-NV57FPKX.js.map → leads-MXUQISOC.js.map} +0 -0
- /package/dist/{leads-2VKYMGP4.js.map → leads-UJVKPRXX.js.map} +0 -0
- /package/dist/{learn-6W55U4ZG.js.map → learn-XTEU4K66.js.map} +0 -0
- /package/dist/{lighthouse-DXZIQLLK.js.map → lighthouse-SJ4QNLUS.js.map} +0 -0
- /package/dist/{line-item-writes-LYWNJWZ2.js.map → line-item-writes-NURRHJ7P.js.map} +0 -0
- /package/dist/{line-items-Q47IZGAD.js.map → line-items-PC6QWV5J.js.map} +0 -0
- /package/dist/{link-4CQUXZPK.js.map → link-ZSXKCLQD.js.map} +0 -0
- /package/dist/{linkedin-TQCPNOIL.js.map → linkedin-EZJJBKY3.js.map} +0 -0
- /package/dist/{list-RCOUIFKY.js.map → list-ECF3RA26.js.map} +0 -0
- /package/dist/{list-LC7BZRD4.js.map → list-GQ65MC2N.js.map} +0 -0
- /package/dist/{list-MEGAZDWZ.js.map → list-K56J7FZW.js.map} +0 -0
- /package/dist/{list-RGMBFJHB.js.map → list-M4Z3WYPM.js.map} +0 -0
- /package/dist/{list-JHNUOEG6.js.map → list-N7ZLO2LE.js.map} +0 -0
- /package/dist/{list-3RZZFCIR.js.map → list-Q5ZZRPHO.js.map} +0 -0
- /package/dist/{list-IZNAL5LS.js.map → list-SXYVDDMU.js.map} +0 -0
- /package/dist/{list-VXNYIVS5.js.map → list-XFIOXFB2.js.map} +0 -0
- /package/dist/{locations-CB6KK3ZU.js.map → locations-LZUHWKZY.js.map} +0 -0
- /package/dist/{locations-ZMJ5MJ6Z.js.map → locations-NG4YXIBT.js.map} +0 -0
- /package/dist/{log-5V5YMZGV.js.map → log-EEBJQ6X2.js.map} +0 -0
- /package/dist/{launches-FOJQPCD2.js.map → logo-4JOWGJ5A.js.map} +0 -0
- /package/dist/{logo-SOCGJLY5.js.map → lookup-7NBZ64TE.js.map} +0 -0
- /package/dist/{make-M5LD3MX2.js.map → make-3J5EK52U.js.map} +0 -0
- /package/dist/{mcp-WFRPWK3W.js.map → mcp-DAKGMA2Z.js.map} +0 -0
- /package/dist/{media-OYAN23YG.js.map → media-2SWDJDA6.js.map} +0 -0
- /package/dist/{media-QCWYLIO2.js.map → media-R64YP7T2.js.map} +0 -0
- /package/dist/{media-sources-YQURN7ZH.js.map → media-sources-L2X3KRDU.js.map} +0 -0
- /package/dist/{members-4UCL6OKS.js.map → members-22BOEGQS.js.map} +0 -0
- /package/dist/{meta-B5XDM6F6.js.map → meta-RBHF2RG3.js.map} +0 -0
- /package/dist/{metrics-W4SLBSPD.js.map → metrics-IXRQZVSM.js.map} +0 -0
- /package/dist/{models-IUO3XGQK.js.map → models-BDXZ56GI.js.map} +0 -0
- /package/dist/{normalize-O6ATWOWP.js.map → normalize-JAE3NVOX.js.map} +0 -0
- /package/dist/{normalize-2OUEDA6D.js.map → normalize-P5Q3F7FU.js.map} +0 -0
- /package/dist/{lookup-MQBDRZZT.js.map → output-ZL62UZHQ.js.map} +0 -0
- /package/dist/{page-EMJESGQZ.js.map → page-FZ7IZ5CV.js.map} +0 -0
- /package/dist/{pages-OMKAYKCF.js.map → pages-SS7TZTHV.js.map} +0 -0
- /package/dist/{output-ZKNCKP7T.js.map → performance-FOOV624A.js.map} +0 -0
- /package/dist/{pinterest-I6JO7LXL.js.map → pinterest-UETS7U7U.js.map} +0 -0
- /package/dist/{pixels-5JBTFWCO.js.map → pixels-LCTMLKGP.js.map} +0 -0
- /package/dist/{pixels-G64Y3J22.js.map → pixels-MPZYWGFD.js.map} +0 -0
- /package/dist/{plan-KNUKCIS7.js.map → plan-V74EVXTW.js.map} +0 -0
- /package/dist/{preview-TBINZJ2C.js.map → preview-RU54Z4EM.js.map} +0 -0
- /package/dist/{promoted-tweet-writes-YVSGOBAS.js.map → promoted-tweet-writes-FQRFU7PB.js.map} +0 -0
- /package/dist/{promoted-tweets-DQDEHTP4.js.map → promoted-tweets-L2L3V5SF.js.map} +0 -0
- /package/dist/{properties-CTFQ7IK3.js.map → properties-ZUDQH3LE.js.map} +0 -0
- /package/dist/{publish-I2XOKEHC.js.map → publish-5XTAHTM4.js.map} +0 -0
- /package/dist/{query-JMVLWKVY.js.map → query-26T7XATH.js.map} +0 -0
- /package/dist/{query-T5IBVQ3B.js.map → query-SHUOHXTO.js.map} +0 -0
- /package/dist/{performance-WGCN2XIC.js.map → query-XISUHMBR.js.map} +0 -0
- /package/dist/{questionnaire-APZ6FMPE.js.map → questionnaire-UG55JLVW.js.map} +0 -0
- /package/dist/{read-W5ZK5G7Q.js.map → read-UYSQAXRV.js.map} +0 -0
- /package/dist/{query-L3TGHKKM.js.map → reddit-BCB4FT6J.js.map} +0 -0
- /package/dist/{refresh-XYZ2Q2SN.js.map → refresh-WY5NPNLX.js.map} +0 -0
- /package/dist/{release-HOSW3VHR.js.map → release-JBSPPNQE.js.map} +0 -0
- /package/dist/{relevant-pages-F5AGXYC5.js.map → relevant-pages-OU5H56KY.js.map} +0 -0
- /package/dist/{remove-RFOFNOYU.js.map → remove-2KACTXKP.js.map} +0 -0
- /package/dist/{remove-7QYX2OYJ.js.map → remove-M5P7M5JU.js.map} +0 -0
- /package/dist/{reopen-HIVOKJOR.js.map → reopen-J3VW5766.js.map} +0 -0
- /package/dist/{rerun-U6F4TEPA.js.map → rerun-5MRYJG6U.js.map} +0 -0
- /package/dist/{research-7TN24S4D.js.map → research-4CAA5CA2.js.map} +0 -0
- /package/dist/{research-43V2DWVK.js.map → research-7LBTJ2B2.js.map} +0 -0
- /package/dist/{reset-B3FAY2EH.js.map → reset-DBWWGCXY.js.map} +0 -0
- /package/dist/{reset-R5RM7ZTG.js.map → reset-DJSSXC4Y.js.map} +0 -0
- /package/dist/{resolve-I5YOKTRV.js.map → resolve-BNRUYS56.js.map} +0 -0
- /package/dist/{retry-A335Q6HS.js.map → retry-TMRIFGVT.js.map} +0 -0
- /package/dist/{retry-R4OWR7IK.js.map → retry-WRBGLB7O.js.map} +0 -0
- /package/dist/{retry-failed-5XDCVPQA.js.map → retry-failed-NWLH6WHK.js.map} +0 -0
- /package/dist/{reddit-GYVC7XPJ.js.map → run-TA3DP6QG.js.map} +0 -0
- /package/dist/{run-VZGXOWFL.js.map → run-VVTLDRRW.js.map} +0 -0
- /package/dist/{scaffold-ad-VKOXZENI.js.map → scaffold-ad-7DSV6XDB.js.map} +0 -0
- /package/dist/{scaffold-static-ad-CMYP2ZZ6.js.map → scaffold-static-ad-BEZ37QWN.js.map} +0 -0
- /package/dist/{scaffold-video-AKA5LMEI.js.map → scaffold-video-SPCMTYK3.js.map} +0 -0
- /package/dist/{schedule-HKIY6JI2.js.map → schedule-BQQGYGT7.js.map} +0 -0
- /package/dist/{schedule-4N6JTJJT.js.map → schedule-S547HPUT.js.map} +0 -0
- /package/dist/{schema-J6AIGXZ7.js.map → schema-DDEWM5ZX.js.map} +0 -0
- /package/dist/{schema-RNVEYR2U.js.map → schema-SQIP75WG.js.map} +0 -0
- /package/dist/{scrape-VK7RZO4M.js.map → scrape-TI6S2W7I.js.map} +0 -0
- /package/dist/{screenshot-PWUROUXU.js.map → screenshot-XFAYY4WW.js.map} +0 -0
- /package/dist/{run-TNO2VF2U.js.map → search-4HIOKYFR.js.map} +0 -0
- /package/dist/{search-3ZW3RRPX.js.map → search-D2IV7KFJ.js.map} +0 -0
- /package/dist/{search-LRY5SYDM.js.map → search-IZPDHRJ4.js.map} +0 -0
- /package/dist/{search-J2VQNY4A.js.map → search-KFDL2IGG.js.map} +0 -0
- /package/dist/{search-PK4IUZ4C.js.map → search-VJBKRD2U.js.map} +0 -0
- /package/dist/{search-copy-AVQESTYJ.js.map → search-copy-QZXCAGK4.js.map} +0 -0
- /package/dist/{seeds-C6JI3WTG.js.map → seeds-VUNZMHZH.js.map} +0 -0
- /package/dist/{select-NBRJ6WLY.js.map → select-U5TPAXGY.js.map} +0 -0
- /package/dist/{sends-DYIE7W5U.js.map → sends-7HWLIOUO.js.map} +0 -0
- /package/dist/{sequences-CQVKVRC4.js.map → sequences-5RAGZE7L.js.map} +0 -0
- /package/dist/{set-prompt-VNJ334YW.js.map → set-prompt-MHU4WI3Q.js.map} +0 -0
- /package/dist/{setup-links-77534G7K.js.map → setup-links-SPIGXQTY.js.map} +0 -0
- /package/dist/{search-M23XX3N5.js.map → shared-3CGGKICB.js.map} +0 -0
- /package/dist/{site-EFQ2QFZX.js.map → site-FW6K6L6M.js.map} +0 -0
- /package/dist/{sitemaps-XFLHKP2D.js.map → sitemaps-3WDYQHYR.js.map} +0 -0
- /package/dist/{sites-5JNZWY3A.js.map → sites-G4DQ2Z2X.js.map} +0 -0
- /package/dist/{skills-43P4WK6Y.js.map → skills-PA26HXEP.js.map} +0 -0
- /package/dist/{sources-JQM4AKNL.js.map → sources-PMBWG4KO.js.map} +0 -0
- /package/dist/{stale-Y5HC7BNS.js.map → stale-5IMY6MWC.js.map} +0 -0
- /package/dist/{stats-LLTHL5VR.js.map → stats-Q35I5DRW.js.map} +0 -0
- /package/dist/{status-T25VZANV.js.map → status-62SUXUWW.js.map} +0 -0
- /package/dist/{sticker-B5DJ56YG.js.map → sticker-MC2IAIQB.js.map} +0 -0
- /package/dist/{stock-WXAFJFAC.js.map → stock-B7LKMKUK.js.map} +0 -0
- /package/dist/{studio-X546AIF2.js.map → studio-BB2EVW5I.js.map} +0 -0
- /package/dist/{sync-VDPGFUZS.js.map → sync-CLZDZL5F.js.map} +0 -0
- /package/dist/{sync-GCVG6QTL.js.map → sync-YTHNOAAM.js.map} +0 -0
- /package/dist/{shared-2SQRS476.js.map → sync-definition-DYSVT7BJ.js.map} +0 -0
- /package/dist/{tag-manager-HG76EBNX.js.map → tag-manager-EAREWVYR.js.map} +0 -0
- /package/dist/{tags-6KXDDSBW.js.map → tags-53AG3FYG.js.map} +0 -0
- /package/dist/{tags-UE2S4UCB.js.map → tags-7TH7TVR6.js.map} +0 -0
- /package/dist/{tags-NFBMGARL.js.map → tags-EB4RE3ML.js.map} +0 -0
- /package/dist/{tags-SJIXKMQH.js.map → tags-EUEWDTTN.js.map} +0 -0
- /package/dist/{tags-T4OK7QXX.js.map → tags-ICAWBCOR.js.map} +0 -0
- /package/dist/{targeting-constants-GR7RUKE3.js.map → targeting-constants-IFE72XKL.js.map} +0 -0
- /package/dist/{targeting-criteria-NZVF6ZOK.js.map → targeting-criteria-WZDGJKOI.js.map} +0 -0
- /package/dist/{targets-4II2CNRP.js.map → targets-OKJ6A4YL.js.map} +0 -0
- /package/dist/{testimonials-NODH7BWK.js.map → testimonials-THHR5JH5.js.map} +0 -0
- /package/dist/{themes-NO3DM5M3.js.map → themes-ZFRM6AVO.js.map} +0 -0
- /package/dist/{top-companies-MWOOBUSM.js.map → top-companies-63Q475IA.js.map} +0 -0
- /package/dist/{translate-26KMJSWL.js.map → translate-XKUWIIOS.js.map} +0 -0
- /package/dist/{triggers-LYPP4FKZ.js.map → triggers-XLKO2QXQ.js.map} +0 -0
- /package/dist/{tweets-ZWC7YOQ4.js.map → tweets-7EZRFG3Y.js.map} +0 -0
- /package/dist/{unfollow-LBMJXF4E.js.map → unfollow-WBBTVEHE.js.map} +0 -0
- /package/dist/{unlink-OL7UNUYB.js.map → unlink-P7KU54YU.js.map} +0 -0
- /package/dist/{unstage-6GUUIEWL.js.map → unstage-EG7AUE5X.js.map} +0 -0
- /package/dist/{unstage-5AZAD2KF.js.map → unstage-GHXGDOT2.js.map} +0 -0
- /package/dist/{update-PCVBEGSU.js.map → update-7EWDO32L.js.map} +0 -0
- /package/dist/{update-2YZVDSWQ.js.map → update-BVYGB4YL.js.map} +0 -0
- /package/dist/{update-QWWAXFWH.js.map → update-KVMQOVQK.js.map} +0 -0
- /package/dist/{update-HSMB6JMZ.js.map → update-N74F5KXB.js.map} +0 -0
- /package/dist/{upload-VBPWOAKD.js.map → upload-6PYPHPCZ.js.map} +0 -0
- /package/dist/{upload-MPMP3JRS.js.map → upload-NA62W5RS.js.map} +0 -0
- /package/dist/{upscale-TXJFZLUU.js.map → upscale-RXJUBHXN.js.map} +0 -0
- /package/dist/{url-options-ML2D74NM.js.map → url-options-EUDXLDBD.js.map} +0 -0
- /package/dist/{url-params-4AD467SA.js.map → url-params-DBJSM2LP.js.map} +0 -0
- /package/dist/{use-SUY57ZJA.js.map → use-MHPNS4V4.js.map} +0 -0
- /package/dist/{validate-W4QZZFXV.js.map → validate-TTAGVGM2.js.map} +0 -0
- /package/dist/{variant-TE6CEHZW.js.map → variant-ZFCWILPG.js.map} +0 -0
- /package/dist/{videos-DJHFHZI5.js.map → videos-DUG7JYUA.js.map} +0 -0
- /package/dist/{view-EQZB7Z6N.js.map → view-PYCGDGT2.js.map} +0 -0
- /package/dist/{sync-definition-GKF2KKFC.js.map → viewer-check-HIMNCZGY.js.map} +0 -0
- /package/dist/{web-N4RNU7UH.js.map → web-SDG42OBC.js.map} +0 -0
- /package/dist/{winners-UPM6DFRB.js.map → winners-AI4PVSOI.js.map} +0 -0
- /package/dist/{winning-ads-SVUUR443.js.map → winning-ads-KGAYAIPG.js.map} +0 -0
- /package/dist/{write-commands-5HSNHURZ.js.map → write-commands-34L5LJIB.js.map} +0 -0
- /package/dist/{write-commands-BCU5YATY.js.map → write-commands-5TJ3LQ5F.js.map} +0 -0
- /package/dist/{write-commands-WVJJJFBP.js.map → write-commands-OYMWOFEY.js.map} +0 -0
- /package/dist/{write-commands-YNI6KS2B.js.map → write-commands-R3UCCBUI.js.map} +0 -0
- /package/dist/{x-65R63NFK.js.map → x-VNRVGVLI.js.map} +0 -0
|
@@ -0,0 +1,3151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
outputRatioWarningsFor
|
|
3
|
+
} from "./chunk-THU74XR6.js";
|
|
4
|
+
import {
|
|
5
|
+
nodeParamsPreview
|
|
6
|
+
} from "./chunk-W7KB3LJE.js";
|
|
7
|
+
import {
|
|
8
|
+
waitForSheet
|
|
9
|
+
} from "./chunk-WN4KX7GE.js";
|
|
10
|
+
import {
|
|
11
|
+
readAvatars,
|
|
12
|
+
writeAvatars
|
|
13
|
+
} from "./chunk-KB5HBQEK.js";
|
|
14
|
+
import {
|
|
15
|
+
fetchBrandLogo
|
|
16
|
+
} from "./chunk-IJDF7D4U.js";
|
|
17
|
+
import {
|
|
18
|
+
advanceDefinitionStatus
|
|
19
|
+
} from "./chunk-3ACMDRSE.js";
|
|
20
|
+
import {
|
|
21
|
+
correctedPicture,
|
|
22
|
+
ingestedNow,
|
|
23
|
+
isStaticAdCanvas,
|
|
24
|
+
keepsChosen,
|
|
25
|
+
pictureRecord,
|
|
26
|
+
previousAdImage,
|
|
27
|
+
previousDelivered,
|
|
28
|
+
relaysSizesOnly,
|
|
29
|
+
runBaker,
|
|
30
|
+
seriesLeadsAbove,
|
|
31
|
+
seriesMembers,
|
|
32
|
+
seriesRunHints,
|
|
33
|
+
variantImage,
|
|
34
|
+
versionRun,
|
|
35
|
+
writeSeriesMemberCanvas
|
|
36
|
+
} from "./chunk-EOGPNV25.js";
|
|
37
|
+
import {
|
|
38
|
+
buildFailedRunRecord,
|
|
39
|
+
buildInitialRunRecord,
|
|
40
|
+
buildRunRecord,
|
|
41
|
+
creativeSlugFromCanvasPath,
|
|
42
|
+
finalFiles,
|
|
43
|
+
finalOutputLabelsFromCanvas,
|
|
44
|
+
nodeOutputsToRecord
|
|
45
|
+
} from "./chunk-NQTSHC7Z.js";
|
|
46
|
+
import {
|
|
47
|
+
clearLegacyDirtyMarker,
|
|
48
|
+
readRenderState,
|
|
49
|
+
recordRenderAttempt,
|
|
50
|
+
recordRendered,
|
|
51
|
+
recordReviewOutcome,
|
|
52
|
+
recordSeriesLeadRun,
|
|
53
|
+
recordSnapshot
|
|
54
|
+
} from "./chunk-AFDPISNJ.js";
|
|
55
|
+
import {
|
|
56
|
+
BRAND_LOGO_ROLE,
|
|
57
|
+
CLAIMS_MODEL,
|
|
58
|
+
CLAIMS_REQUEST,
|
|
59
|
+
CanvasPrepareError,
|
|
60
|
+
GLOBAL_PROMPT_FILE,
|
|
61
|
+
PEOPLE_PROMPT_VERSION,
|
|
62
|
+
REBUILD_FILE,
|
|
63
|
+
computeSourceSha,
|
|
64
|
+
exitOnPrepareError,
|
|
65
|
+
findBrandRoot,
|
|
66
|
+
fixBriefNode,
|
|
67
|
+
fixStaticAd,
|
|
68
|
+
formatOfAdaptId,
|
|
69
|
+
isNativeCanvas,
|
|
70
|
+
listSceneFiles,
|
|
71
|
+
peoplePrompt,
|
|
72
|
+
plannedPlacements,
|
|
73
|
+
prepareCanvas,
|
|
74
|
+
readPeople,
|
|
75
|
+
readStaticBrandKit,
|
|
76
|
+
readStaticPromptJson,
|
|
77
|
+
rederiveFromVariant,
|
|
78
|
+
seriesOf,
|
|
79
|
+
uncastPeople,
|
|
80
|
+
wireNamedPeople,
|
|
81
|
+
wireProofBadges
|
|
82
|
+
} from "./chunk-OXBPKBSK.js";
|
|
83
|
+
import {
|
|
84
|
+
ViewerAnswerSchema,
|
|
85
|
+
describeViewer,
|
|
86
|
+
gatewayViewerModel,
|
|
87
|
+
jevReadJudge,
|
|
88
|
+
judgeViewerAnswer,
|
|
89
|
+
liveViewerLookups,
|
|
90
|
+
marketQuery,
|
|
91
|
+
mayBeCorrection,
|
|
92
|
+
misreadings,
|
|
93
|
+
readTurnRenders,
|
|
94
|
+
recordTurnRender,
|
|
95
|
+
recordViewerFail,
|
|
96
|
+
roundFitsTurn,
|
|
97
|
+
sureEnough,
|
|
98
|
+
turnRenderRefusal,
|
|
99
|
+
updateTurnRenders,
|
|
100
|
+
viewerCheckBeforeRender,
|
|
101
|
+
viewerCheckSkip,
|
|
102
|
+
viewerChecksSpent
|
|
103
|
+
} from "./chunk-V7PMSMPF.js";
|
|
104
|
+
import {
|
|
105
|
+
holdUnbilledSpend,
|
|
106
|
+
holdWhenSettled,
|
|
107
|
+
sendBillingSpend,
|
|
108
|
+
takeUnbilledSpend
|
|
109
|
+
} from "./chunk-VHMNSO2B.js";
|
|
110
|
+
import {
|
|
111
|
+
canvasOutputsRoot,
|
|
112
|
+
describeRewrites,
|
|
113
|
+
findWorkspaceRoot,
|
|
114
|
+
localPathParam
|
|
115
|
+
} from "./chunk-65BQAJMH.js";
|
|
116
|
+
import {
|
|
117
|
+
ValidationError,
|
|
118
|
+
createEngineFromEnv,
|
|
119
|
+
resolveConcurrency,
|
|
120
|
+
ulid
|
|
121
|
+
} from "./chunk-KM4AAAHZ.js";
|
|
122
|
+
import {
|
|
123
|
+
BackendClient,
|
|
124
|
+
LOGO_COMPOSITE_ROLE,
|
|
125
|
+
SERIES_LEAD_ROLE,
|
|
126
|
+
STATIC_ADAPT_FORMAT_ROLE,
|
|
127
|
+
STATIC_ADAPT_ROLE,
|
|
128
|
+
STATIC_FIX_ROLE,
|
|
129
|
+
STATIC_MASTER_ROLE,
|
|
130
|
+
STATIC_PICK_ROLE,
|
|
131
|
+
STATIC_PLACE_ROLE,
|
|
132
|
+
STATIC_SAFE_FIT_ROLE,
|
|
133
|
+
STATIC_SIZE_ROLE,
|
|
134
|
+
STATIC_VARIANTS_ROLE,
|
|
135
|
+
STATIC_VARIANT_CALL_ROLE,
|
|
136
|
+
VARIANT_PREFILTER_ROLE,
|
|
137
|
+
adaptPlan,
|
|
138
|
+
formatSlug,
|
|
139
|
+
nodesWithRole,
|
|
140
|
+
regenerateTargets,
|
|
141
|
+
rolesById,
|
|
142
|
+
staticMasterFormat
|
|
143
|
+
} from "./chunk-O4ABJ352.js";
|
|
144
|
+
import {
|
|
145
|
+
bytesOf,
|
|
146
|
+
judgeWinner,
|
|
147
|
+
nearIdenticalFindings,
|
|
148
|
+
parseJudgeScores,
|
|
149
|
+
reviewRenderedStatic,
|
|
150
|
+
samePictureSizes,
|
|
151
|
+
staticVerdict
|
|
152
|
+
} from "./chunk-AXVOBRQT.js";
|
|
153
|
+
import {
|
|
154
|
+
AdSpecSchema,
|
|
155
|
+
adSpecJsonSchema,
|
|
156
|
+
conceptSummary,
|
|
157
|
+
isAdSpec,
|
|
158
|
+
jevCitationJudge,
|
|
159
|
+
nativeChrome,
|
|
160
|
+
nativeCopy,
|
|
161
|
+
nativeSurfaceName,
|
|
162
|
+
readAdSpec,
|
|
163
|
+
readInterfaceWords
|
|
164
|
+
} from "./chunk-FYAWA3FO.js";
|
|
165
|
+
import {
|
|
166
|
+
GLANCE_CHECK_MODEL,
|
|
167
|
+
VIEWER_CHECK_MODEL,
|
|
168
|
+
canonicalize,
|
|
169
|
+
nearestSupportedAspectRatio
|
|
170
|
+
} from "./chunk-CAMX6OHF.js";
|
|
171
|
+
import {
|
|
172
|
+
RENDER_REVIEW_MODEL,
|
|
173
|
+
RENDER_REVIEW_UNAVAILABLE,
|
|
174
|
+
askAboutImages,
|
|
175
|
+
askAboutText,
|
|
176
|
+
reviewContextBeside,
|
|
177
|
+
reviewRenderedVideo,
|
|
178
|
+
sceneWindowsBeside,
|
|
179
|
+
videoPathFromOutput
|
|
180
|
+
} from "./chunk-73B3RQJC.js";
|
|
181
|
+
import {
|
|
182
|
+
describeMarket
|
|
183
|
+
} from "./chunk-5HYSKH77.js";
|
|
184
|
+
import {
|
|
185
|
+
LayerExecutionError,
|
|
186
|
+
RunAbortedError,
|
|
187
|
+
describeFailureReason,
|
|
188
|
+
requireCredentialsFromEnv,
|
|
189
|
+
runFailureCode,
|
|
190
|
+
sha256Hex
|
|
191
|
+
} from "./chunk-NAMEFVWL.js";
|
|
192
|
+
import {
|
|
193
|
+
apiPost
|
|
194
|
+
} from "./chunk-KB5XJHQX.js";
|
|
195
|
+
import {
|
|
196
|
+
optionalChatId,
|
|
197
|
+
optionalTurnId
|
|
198
|
+
} from "./chunk-QXDEZKAR.js";
|
|
199
|
+
|
|
200
|
+
// src/commands/canvas/run.ts
|
|
201
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
202
|
+
import path7 from "path";
|
|
203
|
+
import { defineCommand } from "citty";
|
|
204
|
+
|
|
205
|
+
// src/commands/canvas/run-progress.ts
|
|
206
|
+
function clampNodeError(error) {
|
|
207
|
+
return { ...error, message: error.message.slice(0, 2e3) };
|
|
208
|
+
}
|
|
209
|
+
var RunProgressTracker = class {
|
|
210
|
+
runId;
|
|
211
|
+
meta;
|
|
212
|
+
startedAt;
|
|
213
|
+
nodes = /* @__PURE__ */ new Map();
|
|
214
|
+
finalLabels;
|
|
215
|
+
planned = false;
|
|
216
|
+
constructor(runId, meta, finalLabels) {
|
|
217
|
+
this.runId = runId;
|
|
218
|
+
this.meta = meta;
|
|
219
|
+
this.finalLabels = finalLabels;
|
|
220
|
+
this.startedAt = Date.now();
|
|
221
|
+
}
|
|
222
|
+
apply(event) {
|
|
223
|
+
if (event.kind === "plan") {
|
|
224
|
+
for (const node of event.nodes) {
|
|
225
|
+
this.nodes.set(node.node_id, {
|
|
226
|
+
nodeId: node.node_id,
|
|
227
|
+
nodeType: node.node_type,
|
|
228
|
+
cached: false,
|
|
229
|
+
credits: 0,
|
|
230
|
+
durationMs: 0,
|
|
231
|
+
outputs: [],
|
|
232
|
+
deps: node.deps,
|
|
233
|
+
status: "pending",
|
|
234
|
+
paramsPreview: nodeParamsPreview(node.params, node.deps),
|
|
235
|
+
...node.role !== void 0 ? { role: node.role } : {}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
this.planned = true;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (event.kind === "node_start") {
|
|
242
|
+
this.patchNode(event.node_id, { status: "running" });
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (event.kind === "node_settled") {
|
|
246
|
+
this.patchNode(event.run.node_id, {
|
|
247
|
+
status: "completed",
|
|
248
|
+
cached: event.run.cached,
|
|
249
|
+
credits: event.run.credits,
|
|
250
|
+
durationMs: event.run.duration_ms,
|
|
251
|
+
outputs: nodeOutputsToRecord(
|
|
252
|
+
event.outputs,
|
|
253
|
+
event.run.node_id === this.finalLabels?.nodeId ? this.finalLabels.labels : void 0
|
|
254
|
+
)
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.patchNode(event.node_id, { status: "failed", error: clampNodeError(event.error) });
|
|
259
|
+
}
|
|
260
|
+
/** True once the plan event landed — before that there is nothing worth posting. */
|
|
261
|
+
hasPlan() {
|
|
262
|
+
return this.planned;
|
|
263
|
+
}
|
|
264
|
+
/** Plan facts (deps + params preview) for stamping the terminal record's nodes. */
|
|
265
|
+
planInfo() {
|
|
266
|
+
const info = /* @__PURE__ */ new Map();
|
|
267
|
+
for (const node of this.nodes.values()) {
|
|
268
|
+
info.set(node.nodeId, {
|
|
269
|
+
deps: node.deps ?? [],
|
|
270
|
+
paramsPreview: node.paramsPreview,
|
|
271
|
+
...node.role !== void 0 ? { role: node.role } : {}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return info;
|
|
275
|
+
}
|
|
276
|
+
/** The current in-flight state as a postable full record. */
|
|
277
|
+
snapshot() {
|
|
278
|
+
const nodes = [...this.nodes.values()];
|
|
279
|
+
return {
|
|
280
|
+
runId: this.runId,
|
|
281
|
+
...this.meta,
|
|
282
|
+
status: "running",
|
|
283
|
+
stats: {
|
|
284
|
+
totalNodes: nodes.length,
|
|
285
|
+
cachedNodes: nodes.filter((n) => n.cached).length,
|
|
286
|
+
totalCredits: nodes.reduce((sum, n) => sum + n.credits, 0),
|
|
287
|
+
durationMs: Date.now() - this.startedAt
|
|
288
|
+
},
|
|
289
|
+
nodes
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Terminal record for a failed run, preserving what each node got to —
|
|
294
|
+
* completed nodes keep their outputs so the graph shows exactly where the
|
|
295
|
+
* run died instead of an empty husk.
|
|
296
|
+
*/
|
|
297
|
+
failedSnapshot(errorMessage) {
|
|
298
|
+
const snapshot = this.snapshot();
|
|
299
|
+
return {
|
|
300
|
+
...snapshot,
|
|
301
|
+
status: "failed",
|
|
302
|
+
errorMessage: errorMessage.slice(0, 2e3),
|
|
303
|
+
stats: { ...snapshot.stats, durationMs: Date.now() - this.startedAt }
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
patchNode(nodeId, patch) {
|
|
307
|
+
const existing = this.nodes.get(nodeId);
|
|
308
|
+
if (!existing) return;
|
|
309
|
+
this.nodes.set(nodeId, { ...existing, ...patch, status: patch.status ?? existing.status });
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var RunRecordPoster = class {
|
|
313
|
+
post;
|
|
314
|
+
latest = null;
|
|
315
|
+
inflight = null;
|
|
316
|
+
warned = false;
|
|
317
|
+
keepaliveTimer = null;
|
|
318
|
+
constructor(post) {
|
|
319
|
+
this.post = post;
|
|
320
|
+
}
|
|
321
|
+
/** Queue a progress snapshot; returns immediately. */
|
|
322
|
+
enqueue(payload) {
|
|
323
|
+
this.latest = payload;
|
|
324
|
+
if (!this.inflight) this.inflight = this.pump();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Re-post the latest snapshot on an interval even with no new node events, so
|
|
328
|
+
* the backend's `canvasRuns.updatedAt` heartbeat stays fresh during a long
|
|
329
|
+
* single-clip poll (a video_generate clip can run minutes with no
|
|
330
|
+
* intervening node events). When this process dies the keepalive stops → the
|
|
331
|
+
* run's `updatedAt` goes stale → the backend reconciliation sweep force-fails
|
|
332
|
+
* it as interrupted and surfaces the clips that finished. `produce` returns
|
|
333
|
+
* null before the plan lands (nothing worth posting yet).
|
|
334
|
+
*/
|
|
335
|
+
startKeepalive(produce, intervalMs = 6e4) {
|
|
336
|
+
if (this.keepaliveTimer) return;
|
|
337
|
+
this.keepaliveTimer = setInterval(() => {
|
|
338
|
+
const snapshot = produce();
|
|
339
|
+
if (snapshot) this.enqueue(snapshot);
|
|
340
|
+
}, intervalMs);
|
|
341
|
+
this.keepaliveTimer.unref?.();
|
|
342
|
+
}
|
|
343
|
+
stopKeepalive() {
|
|
344
|
+
if (this.keepaliveTimer) {
|
|
345
|
+
clearInterval(this.keepaliveTimer);
|
|
346
|
+
this.keepaliveTimer = null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Post the terminal record (awaited, errors surfaced to the caller). Any
|
|
351
|
+
* queued progress snapshot is superseded — the terminal record is the full
|
|
352
|
+
* state — but an in-flight POST is awaited first so it can't land after.
|
|
353
|
+
*/
|
|
354
|
+
async flush(terminal) {
|
|
355
|
+
this.stopKeepalive();
|
|
356
|
+
this.latest = null;
|
|
357
|
+
if (this.inflight) await this.inflight;
|
|
358
|
+
await this.post(terminal);
|
|
359
|
+
}
|
|
360
|
+
async pump() {
|
|
361
|
+
while (this.latest) {
|
|
362
|
+
const payload = this.latest;
|
|
363
|
+
this.latest = null;
|
|
364
|
+
try {
|
|
365
|
+
await this.post(payload);
|
|
366
|
+
} catch (e) {
|
|
367
|
+
if (!this.warned) {
|
|
368
|
+
this.warned = true;
|
|
369
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
370
|
+
process.stderr.write(`[warn] live run progress not streaming (${msg})
|
|
371
|
+
`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
this.inflight = null;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// src/commands/canvas/run-retention.ts
|
|
380
|
+
import { rm } from "fs/promises";
|
|
381
|
+
import path from "path";
|
|
382
|
+
function runDirsToPrune(entries, keep, currentRunId) {
|
|
383
|
+
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
384
|
+
if (keep <= 0) return runs;
|
|
385
|
+
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
386
|
+
}
|
|
387
|
+
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
388
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
389
|
+
let entries;
|
|
390
|
+
try {
|
|
391
|
+
entries = await readdir2(outputsDir);
|
|
392
|
+
} catch {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
396
|
+
if (toPrune.length === 0) return;
|
|
397
|
+
for (const dir of toPrune) {
|
|
398
|
+
await rm(path.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
399
|
+
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
log(`[prune ] removed ${toPrune.length} old run dir(s), kept the ${keep} newest`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/commands/canvas/run-resume.ts
|
|
406
|
+
import { randomUUID } from "crypto";
|
|
407
|
+
import { rmSync } from "fs";
|
|
408
|
+
import { mkdir, readFile, rm as rm2, writeFile } from "fs/promises";
|
|
409
|
+
import path2 from "path";
|
|
410
|
+
function markerKey(canvasPath) {
|
|
411
|
+
const slug = creativeSlugFromCanvasPath(canvasPath);
|
|
412
|
+
const identity = slug ?? path2.relative(process.cwd(), path2.resolve(canvasPath));
|
|
413
|
+
return sha256Hex(Buffer.from(identity)).slice(0, 32);
|
|
414
|
+
}
|
|
415
|
+
function markerFile(outputsDir, canvasPath) {
|
|
416
|
+
return path2.join(outputsDir, ".inflight", `${markerKey(canvasPath)}.json`);
|
|
417
|
+
}
|
|
418
|
+
function classifyRemoteRun(run) {
|
|
419
|
+
if (run.interrupted === true || run.status === "awaiting_approval") return "adopt";
|
|
420
|
+
if (run.status !== "running") return "ignore";
|
|
421
|
+
return run.adoptable === true ? "adopt" : "concurrent";
|
|
422
|
+
}
|
|
423
|
+
var HEARTBEAT_MS = 2e4;
|
|
424
|
+
var MARKER_LIVE_MS = 9e4;
|
|
425
|
+
function isLiveRender(marker, now = Date.now()) {
|
|
426
|
+
if (marker.pid === void 0) return false;
|
|
427
|
+
const beat = marker.heartbeatAt ?? marker.startedAt;
|
|
428
|
+
if (beat === void 0 || now - beat > MARKER_LIVE_MS) return false;
|
|
429
|
+
try {
|
|
430
|
+
process.kill(marker.pid, 0);
|
|
431
|
+
return true;
|
|
432
|
+
} catch (e) {
|
|
433
|
+
return e.code === "EPERM";
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async function resolveRunId(opts) {
|
|
437
|
+
const existing = await readMarker(opts.outputsDir, opts.canvasPath);
|
|
438
|
+
if (existing?.pid !== void 0 && existing.pid !== process.pid && (opts.isRendering ?? ((marker) => isLiveRender(marker)))(existing)) {
|
|
439
|
+
return { runId: existing.runId, resumed: false, rendering: { runId: existing.runId, pid: existing.pid } };
|
|
440
|
+
}
|
|
441
|
+
if (opts.explicitRunId) return { runId: opts.explicitRunId, resumed: false };
|
|
442
|
+
if (!opts.fresh) {
|
|
443
|
+
if (existing) {
|
|
444
|
+
const sameSource = existing.sourceSha !== void 0 && existing.sourceSha === opts.sourceSha;
|
|
445
|
+
return { runId: existing.runId, resumed: true, source: "local", sameSource };
|
|
446
|
+
}
|
|
447
|
+
if (opts.adoptedRunId) return { runId: opts.adoptedRunId, resumed: true, source: "remote", sameSource: true };
|
|
448
|
+
if (opts.remote) {
|
|
449
|
+
try {
|
|
450
|
+
const active = await opts.remote.getActiveRun(opts.remote.creativeSlug, opts.remote.sourceSha);
|
|
451
|
+
if (active) {
|
|
452
|
+
const decision = classifyRemoteRun(active);
|
|
453
|
+
if (decision === "adopt") return { runId: active.runId, resumed: true, source: "remote", sameSource: true };
|
|
454
|
+
if (decision === "concurrent") {
|
|
455
|
+
return { runId: `r_${ulid()}`, resumed: false, concurrentRunId: active.runId };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} catch (e) {
|
|
459
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
460
|
+
process.stderr.write(`[warn] resumable-run lookup failed (${msg}) \u2014 starting a fresh run
|
|
461
|
+
`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return { runId: `r_${ulid()}`, resumed: false };
|
|
466
|
+
}
|
|
467
|
+
async function readMarker(outputsDir, canvasPath) {
|
|
468
|
+
try {
|
|
469
|
+
const parsed = JSON.parse(await readFile(markerFile(outputsDir, canvasPath), "utf8"));
|
|
470
|
+
if (typeof parsed.runId !== "string" || parsed.runId.length === 0) return null;
|
|
471
|
+
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
472
|
+
return {
|
|
473
|
+
runId: parsed.runId,
|
|
474
|
+
...Number.isInteger(parsed.pid) ? { pid: parsed.pid } : {},
|
|
475
|
+
...typeof parsed.token === "string" ? { token: parsed.token } : {},
|
|
476
|
+
...num(parsed.startedAt) !== void 0 ? { startedAt: num(parsed.startedAt) } : {},
|
|
477
|
+
...num(parsed.heartbeatAt) !== void 0 ? { heartbeatAt: num(parsed.heartbeatAt) } : {},
|
|
478
|
+
...typeof parsed.sourceSha === "string" ? { sourceSha: parsed.sourceSha } : {}
|
|
479
|
+
};
|
|
480
|
+
} catch {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function markRunInFlight(outputsDir, canvasPath, runId, sourceSha) {
|
|
485
|
+
const file = markerFile(outputsDir, canvasPath);
|
|
486
|
+
const token = randomUUID();
|
|
487
|
+
const startedAt = Date.now();
|
|
488
|
+
const write = (heartbeatAt) => writeFile(
|
|
489
|
+
file,
|
|
490
|
+
JSON.stringify({
|
|
491
|
+
runId,
|
|
492
|
+
pid: process.pid,
|
|
493
|
+
token,
|
|
494
|
+
canvasPath: path2.resolve(canvasPath),
|
|
495
|
+
startedAt,
|
|
496
|
+
heartbeatAt,
|
|
497
|
+
...sourceSha ? { sourceSha } : {}
|
|
498
|
+
})
|
|
499
|
+
);
|
|
500
|
+
try {
|
|
501
|
+
await mkdir(path2.dirname(file), { recursive: true });
|
|
502
|
+
await write(startedAt);
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
const timer = setInterval(() => {
|
|
506
|
+
void readMarker(outputsDir, canvasPath).then((marker) => marker?.token === token ? write(Date.now()) : void 0).catch(() => void 0);
|
|
507
|
+
}, HEARTBEAT_MS);
|
|
508
|
+
timer.unref();
|
|
509
|
+
return { token, stop: () => clearInterval(timer) };
|
|
510
|
+
}
|
|
511
|
+
function busyFile(outputsDir, canvasPath) {
|
|
512
|
+
return path2.join(outputsDir, ".inflight", `${markerKey(canvasPath)}.busy.json`);
|
|
513
|
+
}
|
|
514
|
+
async function markCanvasBusy(outputsDir, canvasPath, runId, pid = process.pid) {
|
|
515
|
+
const file = busyFile(outputsDir, canvasPath);
|
|
516
|
+
const write = () => writeFile(file, JSON.stringify({ runId, pid, heartbeatAt: Date.now() }));
|
|
517
|
+
try {
|
|
518
|
+
await mkdir(path2.dirname(file), { recursive: true });
|
|
519
|
+
await write();
|
|
520
|
+
} catch {
|
|
521
|
+
}
|
|
522
|
+
const timer = setInterval(() => void write().catch(() => void 0), HEARTBEAT_MS);
|
|
523
|
+
timer.unref();
|
|
524
|
+
let released = false;
|
|
525
|
+
const release = () => {
|
|
526
|
+
if (released) return;
|
|
527
|
+
released = true;
|
|
528
|
+
clearInterval(timer);
|
|
529
|
+
try {
|
|
530
|
+
rmSync(file, { force: true });
|
|
531
|
+
} catch {
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
return { release };
|
|
535
|
+
}
|
|
536
|
+
async function canvasBusyElsewhere(outputsDir, canvasPath, now = Date.now()) {
|
|
537
|
+
try {
|
|
538
|
+
const held = JSON.parse(await readFile(busyFile(outputsDir, canvasPath), "utf8"));
|
|
539
|
+
if (typeof held.runId !== "string" || held.pid === void 0 || held.pid === process.pid) return null;
|
|
540
|
+
return isLiveRender(held, now) ? { runId: held.runId, pid: held.pid } : null;
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
async function clearRunMarker(outputsDir, canvasPath, token) {
|
|
546
|
+
try {
|
|
547
|
+
if (token !== void 0) {
|
|
548
|
+
const marker = await readMarker(outputsDir, canvasPath);
|
|
549
|
+
if (marker?.token !== void 0 && marker.token !== token) return;
|
|
550
|
+
}
|
|
551
|
+
await rm2(markerFile(outputsDir, canvasPath), { force: true });
|
|
552
|
+
} catch {
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/commands/canvas/run-snapshot.ts
|
|
557
|
+
import { existsSync } from "fs";
|
|
558
|
+
import { mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
|
|
559
|
+
import path3 from "path";
|
|
560
|
+
var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
|
|
561
|
+
var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
|
|
562
|
+
var EXT_TO_MIME = {
|
|
563
|
+
json: "application/json",
|
|
564
|
+
html: "text/html",
|
|
565
|
+
css: "text/css",
|
|
566
|
+
js: "text/javascript",
|
|
567
|
+
txt: "text/plain",
|
|
568
|
+
md: "text/markdown",
|
|
569
|
+
png: "image/png",
|
|
570
|
+
jpg: "image/jpeg",
|
|
571
|
+
jpeg: "image/jpeg",
|
|
572
|
+
webp: "image/webp",
|
|
573
|
+
gif: "image/gif",
|
|
574
|
+
avif: "image/avif",
|
|
575
|
+
mp3: "audio/mpeg",
|
|
576
|
+
wav: "audio/wav",
|
|
577
|
+
mp4: "video/mp4",
|
|
578
|
+
webm: "video/webm",
|
|
579
|
+
mov: "video/quicktime",
|
|
580
|
+
ttf: "font/ttf",
|
|
581
|
+
otf: "font/otf",
|
|
582
|
+
woff: "font/woff",
|
|
583
|
+
woff2: "font/woff2"
|
|
584
|
+
};
|
|
585
|
+
function mimeForFile(filePath) {
|
|
586
|
+
const ext = path3.extname(filePath).slice(1).toLowerCase();
|
|
587
|
+
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
588
|
+
}
|
|
589
|
+
function toPosix(p) {
|
|
590
|
+
return p.split(path3.sep).join("/");
|
|
591
|
+
}
|
|
592
|
+
function isInside(dir, target) {
|
|
593
|
+
const rel = path3.relative(dir, target);
|
|
594
|
+
return !rel.startsWith("..") && !path3.isAbsolute(rel);
|
|
595
|
+
}
|
|
596
|
+
function localPathRefsFromCanvas(parsed) {
|
|
597
|
+
const nodes = parsed?.nodes;
|
|
598
|
+
if (!Array.isArray(nodes)) return [];
|
|
599
|
+
const refs = /* @__PURE__ */ new Set();
|
|
600
|
+
for (const node of nodes) {
|
|
601
|
+
const n = node;
|
|
602
|
+
const params = n.params;
|
|
603
|
+
if (!params || typeof params !== "object") continue;
|
|
604
|
+
const key = localPathParam(n);
|
|
605
|
+
const value = key ? params[key] : void 0;
|
|
606
|
+
if (isSnapshotablePath(value)) refs.add(value);
|
|
607
|
+
}
|
|
608
|
+
return [...refs];
|
|
609
|
+
}
|
|
610
|
+
function isSnapshotablePath(value) {
|
|
611
|
+
return typeof value === "string" && value.length > 0 && !value.includes("[TODO");
|
|
612
|
+
}
|
|
613
|
+
var FINGERPRINTED_FILES = ["prompt.json", "_definition.md"];
|
|
614
|
+
async function sourceRefsToSnapshot(canvasDir, parsed) {
|
|
615
|
+
const refs = new Set(localPathRefsFromCanvas(parsed));
|
|
616
|
+
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
617
|
+
refs.add(toPosix(path3.relative(canvasDir, sceneFile)));
|
|
618
|
+
}
|
|
619
|
+
refs.add(REBUILD_FILE);
|
|
620
|
+
for (const fingerprinted of FINGERPRINTED_FILES) {
|
|
621
|
+
if (existsSync(path3.join(canvasDir, fingerprinted))) refs.add(fingerprinted);
|
|
622
|
+
}
|
|
623
|
+
return [...refs];
|
|
624
|
+
}
|
|
625
|
+
async function uploadRunSnapshot(client, opts) {
|
|
626
|
+
try {
|
|
627
|
+
const canvasDir = path3.dirname(opts.canvasPath);
|
|
628
|
+
const canvasBytes = Buffer.from(opts.raw);
|
|
629
|
+
const canvasEntry = {
|
|
630
|
+
path: path3.basename(opts.canvasPath),
|
|
631
|
+
bytes: canvasBytes,
|
|
632
|
+
sha256: sha256Hex(canvasBytes),
|
|
633
|
+
mime: "application/json"
|
|
634
|
+
};
|
|
635
|
+
const files = [];
|
|
636
|
+
const skipped = [];
|
|
637
|
+
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
638
|
+
const abs = path3.isAbsolute(refPath) ? refPath : path3.resolve(canvasDir, refPath);
|
|
639
|
+
if (!isInside(canvasDir, abs)) {
|
|
640
|
+
skipped.push({
|
|
641
|
+
path: toPosix(path3.relative(canvasDir, abs)),
|
|
642
|
+
reason: "outside the creative folder \u2014 read from the workspace on rerun"
|
|
643
|
+
});
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
let st;
|
|
647
|
+
try {
|
|
648
|
+
st = await stat(abs);
|
|
649
|
+
} catch {
|
|
650
|
+
skipped.push({ path: toPosix(path3.relative(canvasDir, abs)), reason: "missing" });
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
|
|
654
|
+
for (const file of fileList) {
|
|
655
|
+
const rel = toPosix(path3.relative(canvasDir, file));
|
|
656
|
+
const size = (await stat(file)).size;
|
|
657
|
+
if (size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
658
|
+
skipped.push({ path: rel, reason: `too large (${size} bytes)` });
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const bytes = await readFile2(file);
|
|
662
|
+
files.push({ path: rel, bytes, sha256: sha256Hex(bytes), mime: mimeForFile(file) });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const identity = sha256Hex(
|
|
666
|
+
Buffer.from(
|
|
667
|
+
JSON.stringify({
|
|
668
|
+
creativeSlug: opts.creativeSlug,
|
|
669
|
+
entries: [canvasEntry, ...files].map((entry) => [entry.path, entry.sha256]),
|
|
670
|
+
skipped
|
|
671
|
+
})
|
|
672
|
+
)
|
|
673
|
+
);
|
|
674
|
+
if (opts.previous?.identity === identity) return opts.previous;
|
|
675
|
+
const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
|
|
676
|
+
const canvasUpload = await put(canvasEntry.bytes, canvasEntry.mime);
|
|
677
|
+
const uploaded = [];
|
|
678
|
+
for (const file of files) {
|
|
679
|
+
const upload = await put(file.bytes, file.mime);
|
|
680
|
+
uploaded.push({ path: file.path, sha256: upload.sha256, url: upload.url });
|
|
681
|
+
}
|
|
682
|
+
const manifest = {
|
|
683
|
+
schema: SNAPSHOT_SCHEMA,
|
|
684
|
+
creativeSlug: opts.creativeSlug,
|
|
685
|
+
canvasSha: canvasUpload.sha256,
|
|
686
|
+
canvas: { path: canvasEntry.path, sha256: canvasUpload.sha256, url: canvasUpload.url },
|
|
687
|
+
files: uploaded,
|
|
688
|
+
skipped: skipped.length > 0 ? skipped : void 0
|
|
689
|
+
};
|
|
690
|
+
const manifestUpload = await put(Buffer.from(JSON.stringify(manifest)), "application/json");
|
|
691
|
+
return { identity, url: manifestUpload.url };
|
|
692
|
+
} catch (e) {
|
|
693
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
694
|
+
process.stderr.write(`[warn] canvas snapshot not uploaded (${msg}) \u2014 this run cannot be re-run from another workspace
|
|
695
|
+
`);
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
var MAX_GRAPH_BYTES = 4 * 1024 * 1024;
|
|
700
|
+
async function uploadRunGraph(client, graph, signal) {
|
|
701
|
+
try {
|
|
702
|
+
const bytes = Buffer.from(JSON.stringify(graph));
|
|
703
|
+
if (bytes.length > MAX_GRAPH_BYTES) return null;
|
|
704
|
+
return (await putContentAddressed(client, bytes, "application/json", signal)).url;
|
|
705
|
+
} catch (e) {
|
|
706
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
707
|
+
process.stderr.write(`[warn] run graph not kept (${msg}) \u2014 a correction from another workspace starts from its picture
|
|
708
|
+
`);
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
async function putContentAddressed(client, bytes, mime, signal) {
|
|
713
|
+
const sha256 = sha256Hex(bytes);
|
|
714
|
+
const { putUrl, publicUrl } = await client.presignAssetUpload(sha256, mime, signal, "blueprint");
|
|
715
|
+
const held = await fetch(publicUrl, { method: "HEAD", signal }).then(
|
|
716
|
+
(res2) => res2.ok,
|
|
717
|
+
() => false
|
|
718
|
+
);
|
|
719
|
+
if (held) return { sha256, url: publicUrl };
|
|
720
|
+
const res = await fetch(putUrl, {
|
|
721
|
+
method: "PUT",
|
|
722
|
+
body: new Uint8Array(bytes),
|
|
723
|
+
headers: { "Content-Type": mime },
|
|
724
|
+
signal
|
|
725
|
+
});
|
|
726
|
+
if (!res.ok) throw new Error(`snapshot PUT failed ${res.status} ${res.statusText}`);
|
|
727
|
+
return { sha256, url: publicUrl };
|
|
728
|
+
}
|
|
729
|
+
async function listFilesRecursive(dir) {
|
|
730
|
+
const entries = await readdir(dir, { recursive: true, withFileTypes: true });
|
|
731
|
+
return entries.filter((d) => d.isFile()).map((d) => path3.join(d.parentPath, d.name));
|
|
732
|
+
}
|
|
733
|
+
var SnapshotConflictError = class extends Error {
|
|
734
|
+
conflicts;
|
|
735
|
+
constructor(conflicts) {
|
|
736
|
+
super(`local files differ from the recorded snapshot: ${conflicts.join(", ")}`);
|
|
737
|
+
this.name = "SnapshotConflictError";
|
|
738
|
+
this.conflicts = conflicts;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
742
|
+
const entries = [manifest.canvas, ...manifest.files];
|
|
743
|
+
const resolvedTarget = path3.resolve(targetDir);
|
|
744
|
+
const planned = [];
|
|
745
|
+
const conflicts = [];
|
|
746
|
+
const upToDate = [];
|
|
747
|
+
for (const entry of entries) {
|
|
748
|
+
if (path3.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
749
|
+
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
750
|
+
}
|
|
751
|
+
const target = path3.resolve(resolvedTarget, entry.path);
|
|
752
|
+
if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path3.sep)) {
|
|
753
|
+
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
754
|
+
}
|
|
755
|
+
const existing = await readFile2(target).catch(() => null);
|
|
756
|
+
if (existing) {
|
|
757
|
+
if (sha256Hex(existing) === entry.sha256) {
|
|
758
|
+
upToDate.push(entry.path);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
if (!opts.force) {
|
|
762
|
+
conflicts.push(entry.path);
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
planned.push({ entry, target });
|
|
767
|
+
}
|
|
768
|
+
if (conflicts.length > 0) throw new SnapshotConflictError(conflicts);
|
|
769
|
+
const restored = [];
|
|
770
|
+
for (const { entry, target } of planned) {
|
|
771
|
+
const res = await fetch(entry.url);
|
|
772
|
+
if (!res.ok) throw new Error(`snapshot download failed for ${entry.path}: ${res.status} ${res.statusText}`);
|
|
773
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
774
|
+
if (sha256Hex(bytes) !== entry.sha256) {
|
|
775
|
+
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
776
|
+
}
|
|
777
|
+
await mkdir2(path3.dirname(target), { recursive: true });
|
|
778
|
+
await writeFile2(target, bytes);
|
|
779
|
+
restored.push(entry.path);
|
|
780
|
+
}
|
|
781
|
+
return { canvasPath: path3.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/engine/scaffold/static-history.ts
|
|
785
|
+
var PICTURE_ROLES = /* @__PURE__ */ new Set([
|
|
786
|
+
STATIC_VARIANT_CALL_ROLE,
|
|
787
|
+
STATIC_VARIANTS_ROLE,
|
|
788
|
+
VARIANT_PREFILTER_ROLE,
|
|
789
|
+
STATIC_PICK_ROLE,
|
|
790
|
+
STATIC_FIX_ROLE,
|
|
791
|
+
SERIES_LEAD_ROLE
|
|
792
|
+
]);
|
|
793
|
+
var EDIT_ID = /^edit_(\d+)$/;
|
|
794
|
+
function refNode(ref) {
|
|
795
|
+
return typeof ref === "string" ? ref.match(/^\$ref:([a-z][a-z0-9_]*)\./)?.[1] ?? null : null;
|
|
796
|
+
}
|
|
797
|
+
function depsOf(node) {
|
|
798
|
+
const found = [];
|
|
799
|
+
const walk = (v) => {
|
|
800
|
+
if (typeof v === "string") {
|
|
801
|
+
const id = refNode(v);
|
|
802
|
+
if (id) found.push(id);
|
|
803
|
+
} else if (Array.isArray(v)) v.forEach(walk);
|
|
804
|
+
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
805
|
+
};
|
|
806
|
+
walk(node.inputs);
|
|
807
|
+
walk(node.params);
|
|
808
|
+
return found;
|
|
809
|
+
}
|
|
810
|
+
function ancestors(nodes, id) {
|
|
811
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
812
|
+
const seen = /* @__PURE__ */ new Set();
|
|
813
|
+
const visit = (at) => {
|
|
814
|
+
const n = byId.get(at);
|
|
815
|
+
if (!n || seen.has(at)) return;
|
|
816
|
+
seen.add(at);
|
|
817
|
+
for (const dep of depsOf(n)) visit(dep);
|
|
818
|
+
};
|
|
819
|
+
visit(id);
|
|
820
|
+
return nodes.filter((n) => seen.has(n.id));
|
|
821
|
+
}
|
|
822
|
+
var isPicture = (n) => PICTURE_ROLES.has(n.tags?.role ?? "");
|
|
823
|
+
function framingPrefilter(nodes, master) {
|
|
824
|
+
const reads = JSON.stringify(master.inputs ?? {});
|
|
825
|
+
return nodes.find((n) => n.tags?.role === VARIANT_PREFILTER_ROLE && reads.includes(`$ref:${n.id}.`));
|
|
826
|
+
}
|
|
827
|
+
function frameNode(prefilter, id, image) {
|
|
828
|
+
if (!prefilter) return null;
|
|
829
|
+
const { judge_model: _judge, judge_prompt: _prompt, ...params } = prefilter.params ?? {};
|
|
830
|
+
return { ...prefilter, id, inputs: { ...prefilter.inputs, images: [image] }, params };
|
|
831
|
+
}
|
|
832
|
+
function variantIndex(outputs, sha) {
|
|
833
|
+
const images = Array.isArray(outputs?.images) ? outputs.images : [];
|
|
834
|
+
const want = sha.trim().toLowerCase();
|
|
835
|
+
if (!/^[0-9a-f]{8,64}$/.test(want)) return null;
|
|
836
|
+
const hits = images.flatMap((img, i) => typeof img?.sha256 === "string" && img.sha256.startsWith(want) ? [i] : []);
|
|
837
|
+
return hits.length === 1 ? hits[0] ?? null : null;
|
|
838
|
+
}
|
|
839
|
+
function variantSources(nodes) {
|
|
840
|
+
const calls = nodes.filter((n) => n.tags?.role === STATIC_VARIANT_CALL_ROLE);
|
|
841
|
+
const ids = new Set(calls.flatMap((call) => ancestors(nodes, call.id).map((n) => n.id)));
|
|
842
|
+
return nodes.filter((n) => ids.has(n.id));
|
|
843
|
+
}
|
|
844
|
+
function variantFiles(canvas) {
|
|
845
|
+
return variantSources(canvas.nodes).flatMap((n) => {
|
|
846
|
+
const { source, path: path8, expect } = n.params ?? {};
|
|
847
|
+
return n.type === "ingest" && source === "path" && typeof path8 === "string" ? [{ id: n.id, path: path8, expect: String(expect ?? "") }] : [];
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
function declared(n) {
|
|
851
|
+
const { path: _where, ...params } = n.params ?? {};
|
|
852
|
+
const own = n.type === "ingest" && n.params?.source === "path" ? params : n.params ?? {};
|
|
853
|
+
return canonicalize({ type: n.type, inputs: n.inputs ?? {}, params: own });
|
|
854
|
+
}
|
|
855
|
+
function makesSameVariants(current, version, ingested) {
|
|
856
|
+
const sources = variantSources(current.nodes);
|
|
857
|
+
if (sources.length === 0) return false;
|
|
858
|
+
const was = new Map(version.canvas.nodes.map((n) => [n.id, n]));
|
|
859
|
+
const sameDeclaration = sources.every((n) => {
|
|
860
|
+
const before = was.get(n.id);
|
|
861
|
+
return before !== void 0 && declared(before) === declared(n);
|
|
862
|
+
});
|
|
863
|
+
return sameDeclaration && variantFiles(current).every((file) => {
|
|
864
|
+
const asset = version.outputs[file.id]?.asset;
|
|
865
|
+
return typeof asset?.sha256 === "string" && (ingested[file.id] ?? []).includes(asset.sha256);
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
function madeThisRun(grown) {
|
|
869
|
+
const master = grown.canvas.nodes.find((n) => n.tags?.role === STATIC_MASTER_ROLE);
|
|
870
|
+
const head = refNode(master?.inputs?.image);
|
|
871
|
+
const nodes = grown.canvas.nodes.filter((n) => !(n.id in grown.pinned) || n.id === head);
|
|
872
|
+
return { ...grown.canvas, nodes };
|
|
873
|
+
}
|
|
874
|
+
function growFromVersion(current, version, growth) {
|
|
875
|
+
const master = current.nodes.find((n) => n.tags?.role === STATIC_MASTER_ROLE);
|
|
876
|
+
const history = master ? versionHistory(current, version, growth) : null;
|
|
877
|
+
if (!master || !history) return null;
|
|
878
|
+
const { kept, pinned } = history;
|
|
879
|
+
const keptIds = new Set(kept.map((n) => n.id));
|
|
880
|
+
const stage = new Set(
|
|
881
|
+
ancestors(current.nodes, master.id).filter((n) => n.id !== master.id && isPicture(n)).map((n) => n.id)
|
|
882
|
+
);
|
|
883
|
+
const rest = current.nodes.filter((n) => !stage.has(n.id) && !keptIds.has(n.id));
|
|
884
|
+
const top = stepsOnTop({ current, master, kept, rest }, history.picture, growth);
|
|
885
|
+
if (!top) return null;
|
|
886
|
+
const at = rest.findIndex((n) => n.id === master.id);
|
|
887
|
+
const nodes = rest.map((n) => n.id === master.id ? { ...n, inputs: { ...n.inputs, image: top.picture } } : n);
|
|
888
|
+
return {
|
|
889
|
+
canvas: { ...current, nodes: [...nodes.slice(0, at), ...kept, ...top.added, ...nodes.slice(at)] },
|
|
890
|
+
pinned
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function versionHistory(current, version, growth) {
|
|
894
|
+
const was = version.canvas.nodes;
|
|
895
|
+
const image = was.find((n) => n.tags?.role === STATIC_MASTER_ROLE)?.inputs?.image;
|
|
896
|
+
if (typeof image !== "string") return null;
|
|
897
|
+
let picture = image;
|
|
898
|
+
let from = refNode(image) ?? "";
|
|
899
|
+
if (growth.kind === "variant") {
|
|
900
|
+
const variants = was.find((n) => n.tags?.role === STATIC_VARIANTS_ROLE);
|
|
901
|
+
const at = variants ? variantIndex(version.outputs[variants.id], growth.sha256) : null;
|
|
902
|
+
if (!variants || at === null) return null;
|
|
903
|
+
picture = `$ref:${variants.id}.images#${at}`;
|
|
904
|
+
from = variants.id;
|
|
905
|
+
}
|
|
906
|
+
const own = new Set(current.nodes.map((n) => n.id));
|
|
907
|
+
const kept = ancestors(was, from).filter((n) => isPicture(n) || !own.has(n.id));
|
|
908
|
+
const pinned = {};
|
|
909
|
+
for (const n of kept) {
|
|
910
|
+
const outputs = version.outputs[n.id];
|
|
911
|
+
if (!outputs) return null;
|
|
912
|
+
pinned[n.id] = outputs;
|
|
913
|
+
}
|
|
914
|
+
return { kept, pinned, picture };
|
|
915
|
+
}
|
|
916
|
+
function stepsOnTop(at, picture, growth) {
|
|
917
|
+
if (growth.kind === "keep") return { added: [], picture };
|
|
918
|
+
const prefilter = framingPrefilter(at.current.nodes, at.master);
|
|
919
|
+
const step = growth.kind === "edit" ? editStep(at, picture, growth) : chosenStep(picture);
|
|
920
|
+
if (!step) return null;
|
|
921
|
+
const frame2 = frameNode(prefilter, `${step.id}_frame`, `$ref:${step.id}.images#0`);
|
|
922
|
+
return frame2 ? { added: [step, frame2], picture: `$ref:${frame2.id}.image` } : { added: [step], picture: `$ref:${step.id}.images#0` };
|
|
923
|
+
}
|
|
924
|
+
function editStep(at, picture, growth) {
|
|
925
|
+
const n = 1 + Math.max(0, ...at.kept.map((k) => Number(k.id.match(EDIT_ID)?.[1] ?? 0)));
|
|
926
|
+
const template = at.current.nodes.find(
|
|
927
|
+
(v) => v.type === "image_generate" && [STATIC_VARIANT_CALL_ROLE, STATIC_VARIANTS_ROLE].includes(v.tags?.role ?? "")
|
|
928
|
+
);
|
|
929
|
+
if (!template) return null;
|
|
930
|
+
const masterFormat = staticMasterFormat(at.current);
|
|
931
|
+
const brief = fixBriefNode({ nodes: [...at.kept, ...at.rest] }, template, growth.promptJson, {
|
|
932
|
+
instruction: growth.instruction,
|
|
933
|
+
previous: picture,
|
|
934
|
+
...masterFormat ? { format: masterFormat } : {}
|
|
935
|
+
});
|
|
936
|
+
const { n: _n, ...params } = brief.params ?? {};
|
|
937
|
+
return {
|
|
938
|
+
id: `edit_${n}`,
|
|
939
|
+
type: brief.type,
|
|
940
|
+
...brief.inputs ? { inputs: brief.inputs } : {},
|
|
941
|
+
params,
|
|
942
|
+
tags: { role: STATIC_FIX_ROLE }
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
function chosenStep(picture) {
|
|
946
|
+
return { id: "chosen", type: "collect", inputs: { images: [picture] }, tags: { role: STATIC_PICK_ROLE } };
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// src/commands/canvas/named-people-read.ts
|
|
950
|
+
import { createHash } from "crypto";
|
|
951
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
952
|
+
import path4 from "path";
|
|
953
|
+
var PEOPLE_TIMEOUT_MS = 3e4;
|
|
954
|
+
function peopleReader(cacheDir, spent) {
|
|
955
|
+
const dir = path4.join(cacheDir, "named-people");
|
|
956
|
+
return async (lines) => {
|
|
957
|
+
const said = lines.filter((l) => l.trim());
|
|
958
|
+
if (said.length === 0) return [];
|
|
959
|
+
const key = createHash("sha256").update(JSON.stringify([PEOPLE_PROMPT_VERSION, CLAIMS_MODEL, said])).digest("hex");
|
|
960
|
+
const file = path4.join(dir, `${key}.json`);
|
|
961
|
+
const cached = await readFile3(file, "utf8").then((text) => readPeople(JSON.parse(text), said)).catch(() => null);
|
|
962
|
+
if (cached) return cached;
|
|
963
|
+
const answer = await askAboutText(CLAIMS_MODEL, peoplePrompt(said), spent, {
|
|
964
|
+
timeoutMs: PEOPLE_TIMEOUT_MS,
|
|
965
|
+
request: CLAIMS_REQUEST
|
|
966
|
+
});
|
|
967
|
+
const people = readPeople(answer, said);
|
|
968
|
+
if (people) {
|
|
969
|
+
await mkdir3(dir, { recursive: true });
|
|
970
|
+
await writeFile3(file, JSON.stringify(answer), "utf8");
|
|
971
|
+
}
|
|
972
|
+
return people;
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// src/commands/avatars/namedPerson.ts
|
|
977
|
+
var RESEARCH_TIMEOUT_MS = 18e4;
|
|
978
|
+
async function avatarOfNamedPerson(who) {
|
|
979
|
+
const research = await writeAvatars(
|
|
980
|
+
"/api/avatars/research",
|
|
981
|
+
{ who, create: true },
|
|
982
|
+
{ timeoutMs: RESEARCH_TIMEOUT_MS }
|
|
983
|
+
);
|
|
984
|
+
const handle = research.avatar?.handle;
|
|
985
|
+
if (research.status !== "found" || !handle) return null;
|
|
986
|
+
const read = () => readAvatars("/api/avatars/get", { handle });
|
|
987
|
+
const avatar = await waitForSheet(await read(), read);
|
|
988
|
+
return avatar.sheetUrl ? { handle, sheetUrl: avatar.sheetUrl } : null;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/commands/canvas/slug-restore.ts
|
|
992
|
+
import { existsSync as existsSync2 } from "fs";
|
|
993
|
+
import path5 from "path";
|
|
994
|
+
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
995
|
+
var SlugRestoreError = class extends Error {
|
|
996
|
+
code;
|
|
997
|
+
fix;
|
|
998
|
+
constructor(code, message, fix) {
|
|
999
|
+
super(message);
|
|
1000
|
+
this.name = "SlugRestoreError";
|
|
1001
|
+
this.code = code;
|
|
1002
|
+
this.fix = fix;
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
async function canvasForSlug(slug, opts) {
|
|
1006
|
+
if (!SLUG_PATTERN.test(slug)) {
|
|
1007
|
+
throw new SlugRestoreError("invalid_slug", `"${slug}" is not a valid creative slug`);
|
|
1008
|
+
}
|
|
1009
|
+
const targetDir = path5.join(opts.workspaceRoot, "src", "creatives", slug);
|
|
1010
|
+
const local = path5.join(targetDir, `${slug}.canvas.json`);
|
|
1011
|
+
if (!opts.forceRemote && existsSync2(local)) return { canvasPath: local };
|
|
1012
|
+
const client = opts.client();
|
|
1013
|
+
if (!client) {
|
|
1014
|
+
throw new SlugRestoreError(
|
|
1015
|
+
"missing_credentials",
|
|
1016
|
+
`"${slug}" is not in this workspace, and restoring it from run history needs Baker credentials (BAKER_API_URL / BAKER_API_KEY).`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
const latest = await client.getLatestSnapshotRun(slug);
|
|
1020
|
+
if (!latest) {
|
|
1021
|
+
throw new SlugRestoreError(
|
|
1022
|
+
"no_snapshot_run",
|
|
1023
|
+
`No creative "${slug}" in this workspace, and no recorded run of it to restore from.`,
|
|
1024
|
+
"Check the slug against `ls src/creatives`, or scaffold the creative first."
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
const manifest = await fetchManifest(latest.canvasSnapshotUrl);
|
|
1028
|
+
if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
|
|
1029
|
+
throw new SlugRestoreError("snapshot_mismatch", `the snapshot of run ${latest.runId} does not match its recorded canvas`);
|
|
1030
|
+
}
|
|
1031
|
+
try {
|
|
1032
|
+
const restore = await restoreRunSnapshot(manifest, targetDir, { force: opts.forceRemote });
|
|
1033
|
+
opts.log(
|
|
1034
|
+
restore.restored.length > 0 ? `[restore] ${restore.restored.length} file(s) from run ${latest.runId}: ${restore.restored.join(", ")}` : `[restore] local files already match run ${latest.runId}, nothing downloaded`
|
|
1035
|
+
);
|
|
1036
|
+
for (const skipped of manifest.skipped ?? []) {
|
|
1037
|
+
opts.log(`[warn] snapshot omitted ${skipped.path} (${skipped.reason}); supply it locally if the run needs it fresh`);
|
|
1038
|
+
}
|
|
1039
|
+
return { canvasPath: restore.canvasPath, adoptRunId: await adoptableRun(client, slug, latest.runId) };
|
|
1040
|
+
} catch (e) {
|
|
1041
|
+
if (e instanceof SnapshotConflictError) {
|
|
1042
|
+
throw new SlugRestoreError(
|
|
1043
|
+
"local_files_differ",
|
|
1044
|
+
`local files differ from the recorded snapshot: ${e.conflicts.join(", ")}`,
|
|
1045
|
+
"Re-run with --force-remote to overwrite them, or run the local canvas file directly with `baker canvas run <file>`."
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
throw e;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
async function adoptableRun(client, slug, restoredRunId) {
|
|
1052
|
+
const active = await client.getActiveRun(slug).catch(() => null);
|
|
1053
|
+
return active?.runId === restoredRunId && active.adoptable === true ? restoredRunId : void 0;
|
|
1054
|
+
}
|
|
1055
|
+
async function fetchManifest(url) {
|
|
1056
|
+
const res = await fetch(url);
|
|
1057
|
+
if (!res.ok) {
|
|
1058
|
+
throw new SlugRestoreError("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
|
|
1059
|
+
}
|
|
1060
|
+
const manifest = await res.json();
|
|
1061
|
+
if (manifest?.schema !== SNAPSHOT_SCHEMA || typeof manifest.canvas?.path !== "string") {
|
|
1062
|
+
throw new SlugRestoreError("snapshot_invalid", "snapshot manifest has an unexpected shape");
|
|
1063
|
+
}
|
|
1064
|
+
return manifest;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/commands/canvas/series-run.ts
|
|
1068
|
+
async function runSeries(slugs, deps) {
|
|
1069
|
+
return Promise.all(
|
|
1070
|
+
slugs.map(async (slug) => {
|
|
1071
|
+
const plan = await deps.plan(slug);
|
|
1072
|
+
if ("refused" in plan) return { slug, status: "refused", error: plan.refused };
|
|
1073
|
+
if (!plan.run) return { slug, status: "up_to_date", ...plan.runId ? { run_id: plan.runId } : {} };
|
|
1074
|
+
return resultOf(slug, await deps.run(slug));
|
|
1075
|
+
})
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
function resultOf(slug, answer) {
|
|
1079
|
+
if (answer.ok !== true) {
|
|
1080
|
+
const error = answer.error ?? {};
|
|
1081
|
+
return {
|
|
1082
|
+
slug,
|
|
1083
|
+
status: "failed",
|
|
1084
|
+
error: {
|
|
1085
|
+
code: error.code ?? "render_failed",
|
|
1086
|
+
message: error.message ?? "The render did not finish.",
|
|
1087
|
+
...error.fix ? { fix: error.fix } : {}
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const verdict = answer.verdict;
|
|
1092
|
+
const status = verdict === "ready" || verdict === "ready_with_notes" || verdict === "needs_fixes" ? verdict : "ready";
|
|
1093
|
+
return {
|
|
1094
|
+
slug,
|
|
1095
|
+
status,
|
|
1096
|
+
...typeof answer.run_id === "string" ? { run_id: answer.run_id } : {},
|
|
1097
|
+
...answer.files !== void 0 ? { files: answer.files } : {}
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function seriesEnvelope(lead, results) {
|
|
1101
|
+
const ready = results.filter((r) => r.status === "ready" || r.status === "ready_with_notes");
|
|
1102
|
+
const fixes = results.filter((r) => r.status === "needs_fixes");
|
|
1103
|
+
const stopped = results.filter((r) => r.status === "refused" || r.status === "failed");
|
|
1104
|
+
return {
|
|
1105
|
+
ok: true,
|
|
1106
|
+
data: { lead, ready: ready.map((r) => r.slug), ads: results },
|
|
1107
|
+
hints: [
|
|
1108
|
+
...ready.filter((r) => r.run_id).map((r) => `Save: baker creatives publish --slug ${r.slug} --run-id ${r.run_id} --title "<title>" --output <lead size>`),
|
|
1109
|
+
...fixes.length > 0 ? [
|
|
1110
|
+
`Still needs fixes after its own corrections: ${fixes.map((r) => r.slug).join(", ")}. Correct each by itself (baker canvas run --slug <slug> --fix "<what to change>"); the others stay as they are.`
|
|
1111
|
+
] : [],
|
|
1112
|
+
...stopped.map((r) => `${r.slug}: ${r.error?.message ?? "not rendered"}${r.error?.fix ? ` ${r.error.fix}` : ""}`),
|
|
1113
|
+
`A new version of the lead (${lead}) reaches every ad of the series on the next \`baker canvas run --series ${lead}\`.`
|
|
1114
|
+
]
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/commands/canvas/static-edit.ts
|
|
1119
|
+
function specOf(raw) {
|
|
1120
|
+
try {
|
|
1121
|
+
return raw ? readAdSpec(raw) : null;
|
|
1122
|
+
} catch {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function frame(spec) {
|
|
1127
|
+
const { copy: _copy, colors: _colors, brand, notes: _notes, source: _source, reference: _reference, ...rest } = spec;
|
|
1128
|
+
const concept = rest.concept ? { ...rest.concept, transfer_note: void 0 } : void 0;
|
|
1129
|
+
const brandShape = brand ? { ...brand, ground: void 0, ink: void 0, accent: void 0, cta: void 0, palette: [] } : void 0;
|
|
1130
|
+
return JSON.stringify({ ...rest, concept, brand: brandShape });
|
|
1131
|
+
}
|
|
1132
|
+
var ROLE_WORDS = {
|
|
1133
|
+
headline: "the headline",
|
|
1134
|
+
cta: "the button",
|
|
1135
|
+
button: "the button",
|
|
1136
|
+
price: "the price",
|
|
1137
|
+
body: "body text",
|
|
1138
|
+
caption: "a caption",
|
|
1139
|
+
label: "a label",
|
|
1140
|
+
fine_print: "the fine print"
|
|
1141
|
+
};
|
|
1142
|
+
function copyEdits(before, after) {
|
|
1143
|
+
const out = [];
|
|
1144
|
+
const shared = Math.min(before.length, after.length);
|
|
1145
|
+
for (let i = 0; i < shared; i++) {
|
|
1146
|
+
const a = before[i];
|
|
1147
|
+
const b = after[i];
|
|
1148
|
+
if (a.text !== b.text) out.push(`Change the words "${a.text}" to exactly "${b.text}".`);
|
|
1149
|
+
if ((a.position ?? "") !== (b.position ?? "") && b.position) out.push(`Move "${b.text}" to ${b.position}.`);
|
|
1150
|
+
if ((a.look ?? "") !== (b.look ?? "") && b.look) out.push(`Set "${b.text}" ${b.look}.`);
|
|
1151
|
+
if (a.lettering !== b.lettering || (a.role ?? "") !== (b.role ?? "") || (a.on ?? "") !== (b.on ?? "")) {
|
|
1152
|
+
out.push(`Set "${b.text}" as ${ROLE_WORDS[b.role ?? ""] ?? "it is described"}${b.on ? `, lettered on ${b.on}` : ""}.`);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
for (const added of after.slice(shared)) {
|
|
1156
|
+
const role = ROLE_WORDS[added.role ?? ""];
|
|
1157
|
+
out.push(`Add the words "${added.text}"${role ? ` (${role})` : ""}${added.position ? `, ${added.position}` : ""}.`);
|
|
1158
|
+
}
|
|
1159
|
+
for (const removed of before.slice(shared)) out.push(`Remove the words "${removed.text}".`);
|
|
1160
|
+
return out;
|
|
1161
|
+
}
|
|
1162
|
+
function colourEdits(before, after) {
|
|
1163
|
+
const out = [];
|
|
1164
|
+
for (const use of after.colors) {
|
|
1165
|
+
const was = before.colors.find((c) => c.object === use.object);
|
|
1166
|
+
if (was && was.name === use.name && was.hex === use.hex) continue;
|
|
1167
|
+
out.push(`Make ${use.object} ${use.name}${use.hex ? ` (${use.hex})` : ""}.`);
|
|
1168
|
+
}
|
|
1169
|
+
const roles = [
|
|
1170
|
+
["cta", "the button"],
|
|
1171
|
+
["ground", "the background"],
|
|
1172
|
+
["ink", "the designed type"],
|
|
1173
|
+
["accent", "the accent colour"]
|
|
1174
|
+
];
|
|
1175
|
+
for (const [key, object] of roles) {
|
|
1176
|
+
const was = before.brand?.[key];
|
|
1177
|
+
const now = after.brand?.[key];
|
|
1178
|
+
if (typeof now === "string" && was !== now) out.push(`Make ${object} ${now}.`);
|
|
1179
|
+
}
|
|
1180
|
+
return out;
|
|
1181
|
+
}
|
|
1182
|
+
function specEditInstruction(previous, next) {
|
|
1183
|
+
const before = specOf(previous);
|
|
1184
|
+
const after = specOf(next);
|
|
1185
|
+
if (!before || !after || frame(before) !== frame(after)) return null;
|
|
1186
|
+
const edits = [...copyEdits(before.copy, after.copy), ...colourEdits(before, after)];
|
|
1187
|
+
return edits.length > 0 ? edits.join(" ") : null;
|
|
1188
|
+
}
|
|
1189
|
+
var normalized = (text) => text.trim().toLowerCase().replace(/\s+/g, " ");
|
|
1190
|
+
function repeatsFailedFix(last, instruction) {
|
|
1191
|
+
return Boolean(last && last.blocking.length > 0 && normalized(last.instruction) === normalized(instruction));
|
|
1192
|
+
}
|
|
1193
|
+
function fixMemoryOf(shown, from) {
|
|
1194
|
+
if (from !== void 0 && from !== shown.rendered?.runId) return { blocking: [] };
|
|
1195
|
+
return { ...shown.lastFix ? { lastFix: shown.lastFix } : {}, blocking: shown.blocking ?? [] };
|
|
1196
|
+
}
|
|
1197
|
+
function persistingDefects(before, after) {
|
|
1198
|
+
const now = new Set((after ?? []).filter((f) => f.severity === "blocking").map((f) => f.what));
|
|
1199
|
+
return before.filter((what) => now.has(what));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/commands/canvas/static-autofix.ts
|
|
1203
|
+
var AUTO_FIX_ROUNDS = 3;
|
|
1204
|
+
function wholePicture(f) {
|
|
1205
|
+
const kind = f.kind;
|
|
1206
|
+
if (!kind) return false;
|
|
1207
|
+
return kind.item === "subject" || kind.item === "one_idea" || kind.item === "concept" || (kind.item === "picture" || kind.item === "native") && kind.element === "picture";
|
|
1208
|
+
}
|
|
1209
|
+
var BRIEF_ITEMS = /* @__PURE__ */ new Set(["subject", "concept", "proof", "person"]);
|
|
1210
|
+
var fromBrief = (f) => f.kind !== void 0 && BRIEF_ITEMS.has(f.kind.item);
|
|
1211
|
+
var comparesSizes = (f) => f.kind?.item === "sizes";
|
|
1212
|
+
var cutCopy = (f) => f.kind?.defect === "cut" && (f.kind.element === "logo" || f.kind.element === "button" || f.kind.element === "copy");
|
|
1213
|
+
var MANY_DEFECTS = 3;
|
|
1214
|
+
var withoutSize = (what) => what.replace(/^\d+(?:\.\d+)?:\d+(?:\.\d+)?(?:,\s*\d+(?:\.\d+)?:\d+(?:\.\d+)?)*:\s*/, "");
|
|
1215
|
+
var sizesOf = (what) => what.match(/^(\d+(?:\.\d+)?:\d+(?:\.\d+)?(?:,\s*\d+(?:\.\d+)?:\d+(?:\.\d+)?)*):\s/)?.[1]?.split(/,\s*/) ?? [];
|
|
1216
|
+
function carryUnchanged(now, prior, unchanged) {
|
|
1217
|
+
const settled = (f) => {
|
|
1218
|
+
const sizes = sizesOf(f.what);
|
|
1219
|
+
return sizes.length > 0 && sizes.every((s) => unchanged.includes(s)) && !comparesSizes(f);
|
|
1220
|
+
};
|
|
1221
|
+
return [...now.filter((f) => !settled(f)), ...prior.filter(settled)];
|
|
1222
|
+
}
|
|
1223
|
+
var pct = (share) => `${Math.round(share * 100)}%`;
|
|
1224
|
+
var PAST_BAND = /^(.+?) sits? (?:close to )?where (.+?) covers? (?:it|them) \((.+)\)\.?$/;
|
|
1225
|
+
var OFF_EDGE = /^(.+?) runs? off the edge of the picture \((.+)\)\.?$/;
|
|
1226
|
+
var OUTSIDE_SPACE = /^(.+?) sits? outside the space every size keeps clear \((.+)\)\.?$/;
|
|
1227
|
+
var EXTRA_WORDS = /^the picture adds words that aren't in the copy: (.+?)\. Remove them, or add them to the copy\.?$/;
|
|
1228
|
+
var SCENE_PRICES = /^the picture shows prices the copy does not hold: (.+?)\. Make them unreadable\.?$/;
|
|
1229
|
+
var PLACED = [PAST_BAND, OFF_EDGE, OUTSIDE_SPACE];
|
|
1230
|
+
var MISREAD = /^Text: (".+") reads (".*")\.?$/;
|
|
1231
|
+
var MISSING = /^Text: (".+") is missing\.?$/;
|
|
1232
|
+
var MAKE_ROOM = "Make the room by moving what is next to it with it and shortening the picture or the gaps, keeping every block in its order.";
|
|
1233
|
+
var OVERSHOOT = 0.06;
|
|
1234
|
+
function moveInto(what, edges, band) {
|
|
1235
|
+
const at = (edge) => edges.includes(edge);
|
|
1236
|
+
const top = band ? band.top + OVERSHOOT : 0;
|
|
1237
|
+
const bottom = band ? band.bottom + OVERSHOOT : 0;
|
|
1238
|
+
const vertical = at("bottom") && at("top") ? band ? `so all of it sits between ${pct(top)} and ${pct(1 - bottom)} of the height` : "into the middle of the frame, away from its top and bottom" : at("bottom") && band ? `up so all of it sits above ${pct(1 - bottom)} of the height, the bottom ${pct(bottom)} showing only the picture` : at("top") && band ? `down so all of it sits below ${pct(top)} of the height, the top ${pct(top)} showing only the picture` : at("bottom") ? "up, away from the bottom of the frame" : at("top") ? "down, away from the top of the frame" : "";
|
|
1239
|
+
const sides = ["right", "left"].filter((e) => at(e === "right" ? "right edge" : "left edge"));
|
|
1240
|
+
const side = sides.map((e) => band ? `at least ${pct(band[e])} in from the ${e} edge` : `away from the ${e} edge`).join(" and ");
|
|
1241
|
+
if (!vertical) return `Move ${what} ${side}`;
|
|
1242
|
+
return side ? `Move ${what} ${vertical}, and ${side}` : `Move ${what} ${vertical}`;
|
|
1243
|
+
}
|
|
1244
|
+
function correctionOf(finding, band, kind) {
|
|
1245
|
+
const what = withoutSize(finding).trim();
|
|
1246
|
+
if (/could not be read back/.test(what)) return null;
|
|
1247
|
+
const past = what.match(PAST_BAND);
|
|
1248
|
+
if (past) {
|
|
1249
|
+
const [, block = "", surface = "", edges = ""] = past;
|
|
1250
|
+
const covers = band && edges.includes("bottom") && edges.includes("top") ? `: ${surface} covers the top ${pct(band.top)} and the bottom ${pct(band.bottom)} of the frame` : band && edges.includes("bottom") ? `: ${surface} covers the bottom ${pct(band.bottom)} of the frame` : band && edges.includes("top") ? `: ${surface} covers the top ${pct(band.top)} of the frame` : "";
|
|
1251
|
+
return `${moveInto(block, edges, band)}${covers}. ${MAKE_ROOM}`;
|
|
1252
|
+
}
|
|
1253
|
+
const off = what.match(OFF_EDGE) ?? what.match(OUTSIDE_SPACE);
|
|
1254
|
+
if (off) {
|
|
1255
|
+
const [, block = "", edges = ""] = off;
|
|
1256
|
+
const named = edges.replace(/the (top|bottom) of the screen/g, "the $1 edge").replace(/ and /g, " and ");
|
|
1257
|
+
return `Move ${block} fully inside the frame, with clear space between it and ${named}, smaller if it needs to be.`;
|
|
1258
|
+
}
|
|
1259
|
+
const extra = what.match(EXTRA_WORDS);
|
|
1260
|
+
if (extra) {
|
|
1261
|
+
return `Erase these words wherever they appear, leaving the surface they are on blank or with plain lines: ${extra[1]}.`;
|
|
1262
|
+
}
|
|
1263
|
+
const prices = what.match(SCENE_PRICES);
|
|
1264
|
+
if (prices) {
|
|
1265
|
+
return `Blur these figures until no digit can be read, keeping the document or screen they are on as it is: ${prices[1]}.`;
|
|
1266
|
+
}
|
|
1267
|
+
const misread = what.match(MISREAD);
|
|
1268
|
+
if (misread) return `Re-letter ${misread[1]} exactly as written, letter for letter (it now reads ${misread[2]}).`;
|
|
1269
|
+
const missing = what.match(MISSING);
|
|
1270
|
+
if (missing) return `Add the line ${missing[1]}, spelled exactly, where it belongs in the layout.`;
|
|
1271
|
+
if (kind?.item === "sizes") return FOLLOW_PICTURE;
|
|
1272
|
+
const described = what.replace(/\.$/, "");
|
|
1273
|
+
return `Fix this: ${described.charAt(0).toLowerCase()}${described.slice(1)}.`;
|
|
1274
|
+
}
|
|
1275
|
+
function autoFixStep(canvas, review, opts = {}) {
|
|
1276
|
+
if ((opts.round ?? 0) >= AUTO_FIX_ROUNDS) return null;
|
|
1277
|
+
const flagged = (review ?? []).filter((f) => f.severity === "blocking");
|
|
1278
|
+
if (flagged.length === 0) return null;
|
|
1279
|
+
if (isNativeCanvas(canvas)) return { kind: "respec", findings: unique(flagged.map((f) => f.what)) };
|
|
1280
|
+
const relaid = relaidSizes(canvas);
|
|
1281
|
+
const blocking = onDerivedSizes(flagged, relaid);
|
|
1282
|
+
const unplaceable = new Set((opts.tooFar ?? []).filter((format) => relaid.some((r) => r.format === format)));
|
|
1283
|
+
const misplaced = (f) => {
|
|
1284
|
+
const what = withoutSize(f.what);
|
|
1285
|
+
const sizes = sizesOf(f.what);
|
|
1286
|
+
return sizes.length > 0 && !sizes.every((size) => unplaceable.has(size)) && (PLACED.some((p) => p.test(what)) || cutCopy(f));
|
|
1287
|
+
};
|
|
1288
|
+
const extend = unique(blocking.filter(misplaced).flatMap((f) => sizesOf(f.what))).filter((size) => !unplaceable.has(size));
|
|
1289
|
+
const placing = extend.length > 0 ? { extend } : {};
|
|
1290
|
+
const rest = blocking.filter((f) => !misplaced(f));
|
|
1291
|
+
const at = (f) => relaid.find((r) => f.what.startsWith(`${r.format}:`));
|
|
1292
|
+
const keepRight = keptRules(opts.fixedBefore);
|
|
1293
|
+
const edits = {};
|
|
1294
|
+
for (const r of relaid) {
|
|
1295
|
+
const lines = unique(rest.filter((f) => at(f) === r).flatMap((f) => correctionOf(f.what, r.band, f.kind) ?? []));
|
|
1296
|
+
if (lines.length > 0) edits[r.id] = [...lines, ...keepRight ? [keepRight] : []].join(" ");
|
|
1297
|
+
}
|
|
1298
|
+
const sizesOnly = () => {
|
|
1299
|
+
const ids = Object.keys(edits);
|
|
1300
|
+
if (ids.length > 0) return { kind: "relayout", ids, edits, ...placing };
|
|
1301
|
+
return extend.length > 0 ? { kind: "extend", sizes: extend } : null;
|
|
1302
|
+
};
|
|
1303
|
+
const onSeveralSizes = (f) => {
|
|
1304
|
+
const what = withoutSize(f.what);
|
|
1305
|
+
return new Set(rest.filter((g) => withoutSize(g.what) === what).flatMap((g) => sizesOf(g.what))).size >= 2;
|
|
1306
|
+
};
|
|
1307
|
+
const settledPicture = opts.previous === "relayout" || opts.previous === "extend";
|
|
1308
|
+
const pictureFindings = rest.filter((f) => !at(f) && (!settledPicture || onSeveralSizes(f)));
|
|
1309
|
+
const onPicture = unique(pictureFindings.map((f) => withoutSize(f.what)));
|
|
1310
|
+
if (onPicture.length === 0) return sizesOnly();
|
|
1311
|
+
const every = unique([...extend, ...deliveredSizes(canvas)]);
|
|
1312
|
+
const afterNewPicture = every.length > 0 ? { extend: every } : {};
|
|
1313
|
+
const persisting = new Set((opts.lastFix?.blocking ?? []).map(withoutSize));
|
|
1314
|
+
const stillWrong = opts.previous === "fix" && onPicture.some((w) => persisting.has(w));
|
|
1315
|
+
const brief = unique(pictureFindings.filter(fromBrief).map((f) => withoutSize(f.what)));
|
|
1316
|
+
const rollAgain = stillWrong || brief.length > 0 || onPicture.length >= MANY_DEFECTS || pictureFindings.some(wholePicture);
|
|
1317
|
+
const last = (opts.round ?? 0) >= AUTO_FIX_ROUNDS - 1;
|
|
1318
|
+
if (rollAgain && (!last || brief.length > 0) && !opts.keepPicture && nodesWithRole(canvas, STATIC_VARIANTS_ROLE).length > 0) {
|
|
1319
|
+
return {
|
|
1320
|
+
kind: "regenerate",
|
|
1321
|
+
ids: [STATIC_VARIANTS_ROLE],
|
|
1322
|
+
note: [`The last picture made from this brief had this wrong, so avoid it: ${onPicture.join(" ")}`, ...keepRight ? [keepRight] : []].join(" "),
|
|
1323
|
+
...brief.length > 0 ? { brief } : {},
|
|
1324
|
+
...afterNewPicture
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
const changes = unique(
|
|
1328
|
+
pictureFindings.filter((f) => !fromBrief(f)).flatMap((f) => correctionOf(f.what, void 0, f.kind) ?? [])
|
|
1329
|
+
);
|
|
1330
|
+
if (changes.length === 0) return sizesOnly();
|
|
1331
|
+
const named = new Set(blocking.flatMap((f) => sizesOf(f.what)));
|
|
1332
|
+
const keep = relaid.map((r) => r.format).filter((format) => !named.has(format));
|
|
1333
|
+
const remade = every.filter((size) => !keep.includes(size));
|
|
1334
|
+
const kept = {
|
|
1335
|
+
...keep.length > 0 ? { keep } : {},
|
|
1336
|
+
...remade.length > 0 ? { extend: remade } : {}
|
|
1337
|
+
};
|
|
1338
|
+
const asked = `Change only this: ${changes.join(" ")}`;
|
|
1339
|
+
const again = onPicture.filter((w) => persisting.has(w)).map((w) => w.replace(/\.$/, ""));
|
|
1340
|
+
const instruction = [
|
|
1341
|
+
again.length > 0 ? `${asked} The last edit was already asked for this and Image 1 still shows it, so make it clearly: ${again.join("; ")}.` : asked,
|
|
1342
|
+
...keepRight ? [keepRight] : []
|
|
1343
|
+
].join(" ");
|
|
1344
|
+
if (repeatsFailedFix(opts.lastFix, asked) || repeatsFailedFix(opts.lastFix, instruction)) return null;
|
|
1345
|
+
return { kind: "fix", instruction, ...Object.keys(edits).length > 0 ? { relay: edits } : {}, ...kept };
|
|
1346
|
+
}
|
|
1347
|
+
var unique = (lines) => [...new Set(lines)];
|
|
1348
|
+
var blockingOf = (review) => unique(review.filter((f) => f.severity === "blocking").map((f) => f.what));
|
|
1349
|
+
function correctFrom(attempts) {
|
|
1350
|
+
const latest = attempts[attempts.length - 1];
|
|
1351
|
+
const fewest = Math.min(...attempts.map((a) => blockingOf(a.review).length));
|
|
1352
|
+
const base = blockingOf(latest.review).length > fewest ? attempts.find((a) => blockingOf(a.review).length === fewest) : latest;
|
|
1353
|
+
const now = new Set(blockingOf(base.review).map(withoutSize));
|
|
1354
|
+
const fixedBefore = unique(
|
|
1355
|
+
attempts.filter((a) => a !== base).flatMap((a) => blockingOf(a.review)).filter((what) => !now.has(withoutSize(what)))
|
|
1356
|
+
);
|
|
1357
|
+
return { ...base, fixedBefore };
|
|
1358
|
+
}
|
|
1359
|
+
function keptRules(fixedBefore) {
|
|
1360
|
+
const rules = unique((fixedBefore ?? []).map((f) => withoutSize(f).replace(/\.$/, "")));
|
|
1361
|
+
return rules.length > 0 ? `These were wrong in an earlier version and are right now, so keep them right: ${rules.join("; ")}.` : null;
|
|
1362
|
+
}
|
|
1363
|
+
var FOLLOW_PICTURE = "Show exactly what the finished ad (Image 2) shows: its people, products, panels and their order, and nothing it does not show.";
|
|
1364
|
+
function onDerivedSizes(findings, relaid) {
|
|
1365
|
+
if (relaid.length === 0) return [...findings];
|
|
1366
|
+
return findings.flatMap((f) => {
|
|
1367
|
+
const sizes = sizesOf(f.what);
|
|
1368
|
+
const onPicture = sizes.length > 0 && sizes.every((size) => !relaid.some((r) => r.format === size));
|
|
1369
|
+
if (!onPicture || !comparesSizes(f)) return [f];
|
|
1370
|
+
return relaid.map((r) => ({ ...f, what: `${r.format}: ${withoutSize(f.what)}` }));
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function deliveredSizes(canvas) {
|
|
1374
|
+
const nodes = canvas?.nodes;
|
|
1375
|
+
const labels = Array.isArray(nodes) ? nodes.find((n) => n.id === "final" && n.type === "collect")?.params?.labels : void 0;
|
|
1376
|
+
return Array.isArray(labels) ? labels.filter((l) => typeof l === "string") : [];
|
|
1377
|
+
}
|
|
1378
|
+
function relaidSizes(canvas) {
|
|
1379
|
+
const nodes = canvas?.nodes;
|
|
1380
|
+
const placements = Array.isArray(nodes) ? plannedPlacements(nodes) : void 0;
|
|
1381
|
+
return nodesWithRole(canvas, STATIC_ADAPT_FORMAT_ROLE).flatMap((n) => {
|
|
1382
|
+
const format = formatOfAdaptId(n.id);
|
|
1383
|
+
if (!format) return [];
|
|
1384
|
+
const model = String(n.params?.model ?? "");
|
|
1385
|
+
return [{ id: n.id, format, band: adaptPlan(model, format, placements).safe }];
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
function placedTooFar(canvas, outputsByNode) {
|
|
1389
|
+
return nodesWithRole(canvas, STATIC_PLACE_ROLE).flatMap((n) => {
|
|
1390
|
+
const format = n.params?.format;
|
|
1391
|
+
const unmoved = outputsByNode[n.id]?.reason;
|
|
1392
|
+
return (unmoved === "too_far" || unmoved === "inside_band") && typeof format === "string" ? [format] : [];
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
function rendersNewPicture(run) {
|
|
1396
|
+
return !run.fix && !run.useVariant && !run.sizesOnly;
|
|
1397
|
+
}
|
|
1398
|
+
function paidFor(run) {
|
|
1399
|
+
if (run.fix) return "edit";
|
|
1400
|
+
return run.useVariant || run.sizesOnly ? "sizes" : "options";
|
|
1401
|
+
}
|
|
1402
|
+
function withRelayNote(canvas, ids, note) {
|
|
1403
|
+
const holder = canvas;
|
|
1404
|
+
if (!Array.isArray(holder.nodes)) return canvas;
|
|
1405
|
+
return {
|
|
1406
|
+
...holder,
|
|
1407
|
+
nodes: holder.nodes.map((n) => {
|
|
1408
|
+
if (typeof n.id !== "string" || !ids.includes(n.id) || typeof n.params?.prompt !== "string") return n;
|
|
1409
|
+
const [lead = "", ...rest] = n.params.prompt.split("\n\n");
|
|
1410
|
+
return { ...n, params: { ...n.params, prompt: [lead, note, ...rest].join("\n\n") } };
|
|
1411
|
+
})
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
function withRelayEdit(canvas, id, opts) {
|
|
1415
|
+
const holder = canvas;
|
|
1416
|
+
if (!Array.isArray(holder.nodes)) return canvas;
|
|
1417
|
+
const target = holder.nodes.find((n) => n.id === id);
|
|
1418
|
+
const prompt = target?.params?.prompt;
|
|
1419
|
+
const refs = target?.inputs?.reference;
|
|
1420
|
+
if (!target || typeof prompt !== "string" || !Array.isArray(refs)) return canvas;
|
|
1421
|
+
const lastId = `last_${id}`;
|
|
1422
|
+
const remote = /^(?:https?|data):/.test(opts.previous);
|
|
1423
|
+
const last = {
|
|
1424
|
+
id: lastId,
|
|
1425
|
+
type: "ingest",
|
|
1426
|
+
params: remote ? { source: "url", url: opts.previous, expect: "image" } : { source: "path", path: opts.previous, expect: "image" }
|
|
1427
|
+
};
|
|
1428
|
+
const again = opts.asked?.includes(opts.instruction) ? "The last edit was asked for this and Image 1 still shows it wrong, so make the change clearly: " : "";
|
|
1429
|
+
const lead = `Edit: Image 1 is this ad as it was last laid out for this size. ${again}${opts.instruction} Keep the rest of Image 1 as it is: the picture, the colours, the lettering and every word, its layout changed only as much as this needs.`;
|
|
1430
|
+
const kept = prompt.split("\n\n").slice(1).filter((p) => !p.startsWith("Scale:")).map((p) => p.replace(/\bImage (\d+)\b/g, (_, n) => `Image ${Number(n) + 1}`)).map(
|
|
1431
|
+
(p) => p.startsWith("References:") ? p.replace("References:", "References:\nImage 1 (THIS SIZE): the last layout of this size, to edit.").replace(/^Image 2 \(AD\):.*$/m, "Image 2 (AD): the finished ad this size is made from: its words, logo and picture are the ones to keep.") : p
|
|
1432
|
+
);
|
|
1433
|
+
const leads = new Set(
|
|
1434
|
+
holder.nodes.flatMap((n) => n.tags?.role === SERIES_LEAD_ROLE ? [n.id] : [])
|
|
1435
|
+
);
|
|
1436
|
+
const own = refs.map((r) => typeof r === "string" && leads.has(r.match(/^\$ref:([a-z][a-z0-9_]*)\./)?.[1]) ? "$ref:master.image" : r);
|
|
1437
|
+
const edited = {
|
|
1438
|
+
...target,
|
|
1439
|
+
inputs: { ...target.inputs, reference: [`$ref:${lastId}.asset`, ...own] },
|
|
1440
|
+
params: { ...target.params, prompt: [lead, ...kept].join("\n\n") }
|
|
1441
|
+
};
|
|
1442
|
+
const at = holder.nodes.indexOf(target);
|
|
1443
|
+
return { ...holder, nodes: [...holder.nodes.slice(0, at), last, edited, ...holder.nodes.slice(at + 1)] };
|
|
1444
|
+
}
|
|
1445
|
+
function withSizesFollowingFix(canvas, opts) {
|
|
1446
|
+
let out = canvas;
|
|
1447
|
+
for (const n of nodesWithRole(canvas, STATIC_ADAPT_FORMAT_ROLE)) {
|
|
1448
|
+
const also = opts.also?.[n.id];
|
|
1449
|
+
const previous = opts.last[n.id];
|
|
1450
|
+
if (previous) {
|
|
1451
|
+
const instruction = `Make the change the finished ad (Image 2) just had: ${opts.change}${also ? ` ${also}` : ""}`;
|
|
1452
|
+
out = withRelayEdit(out, n.id, { previous, instruction });
|
|
1453
|
+
} else if (also) out = withRelayNote(out, [n.id], `This size must also change this: ${also}`);
|
|
1454
|
+
}
|
|
1455
|
+
return out;
|
|
1456
|
+
}
|
|
1457
|
+
var EXTEND_PROMPT = [
|
|
1458
|
+
"Deliverable: Image 1 is a finished ad set inside a frame with flat grey margins around it. Paint only the grey: continue the picture outward into it so the whole frame reads as one picture, the scene going on (the wall, the floor, the sky, the room) or, where the ad has no scene, its own plain ground and colours.",
|
|
1459
|
+
"Keep everything that is not grey exactly where and as it is: every word, the logo, the button, the people, the products. Nothing in it moves, grows or is redrawn.",
|
|
1460
|
+
"Add nothing in the grey: no words, no letters, no logos, no buttons, no new people or objects, no frames, borders or shadows around the ad. Never repeat any part of the ad in it: no second logo, button, line of text, card or person; the grey is only more of the room, the table, the floor, the sky or the plain ground."
|
|
1461
|
+
].join("\n\n");
|
|
1462
|
+
function withExtendedSizes(canvas, formats) {
|
|
1463
|
+
const holder = canvas;
|
|
1464
|
+
if (!Array.isArray(holder.nodes)) return canvas;
|
|
1465
|
+
const nodes = holder.nodes;
|
|
1466
|
+
const final = nodes.find((n) => n.id === "final" && n.type === "collect");
|
|
1467
|
+
const labels = final?.params?.labels;
|
|
1468
|
+
const images = final?.inputs?.images;
|
|
1469
|
+
if (!final || !Array.isArray(labels) || !Array.isArray(images)) return canvas;
|
|
1470
|
+
const painter = nodesWithRole(canvas, STATIC_ADAPT_FORMAT_ROLE)[0] ?? nodesWithRole(canvas, STATIC_VARIANT_CALL_ROLE)[0];
|
|
1471
|
+
const params = painter?.params ?? {};
|
|
1472
|
+
const model = typeof params.model === "string" ? params.model : void 0;
|
|
1473
|
+
if (!model) return canvas;
|
|
1474
|
+
const placements = plannedPlacements(nodes) ?? nodes.find((n) => n.type === "static_safe_fit" && Array.isArray(n.params?.placements))?.params?.placements;
|
|
1475
|
+
const copy = placingCopy(nodes);
|
|
1476
|
+
const added = [];
|
|
1477
|
+
const delivered = [...images];
|
|
1478
|
+
for (const format of formats) {
|
|
1479
|
+
const at2 = labels.indexOf(format);
|
|
1480
|
+
const source = images[at2];
|
|
1481
|
+
if (at2 < 0 || typeof source !== "string") continue;
|
|
1482
|
+
const slug = formatSlug(format);
|
|
1483
|
+
const ratio = nearestSupportedAspectRatio("image_generate", model, format);
|
|
1484
|
+
added.push(
|
|
1485
|
+
{
|
|
1486
|
+
id: `place_${slug}`,
|
|
1487
|
+
type: "static_place",
|
|
1488
|
+
inputs: { image: source },
|
|
1489
|
+
params: {
|
|
1490
|
+
format,
|
|
1491
|
+
render_ratio: ratio,
|
|
1492
|
+
...placements ? { placements: [...placements] } : {},
|
|
1493
|
+
...copy ? { copy } : {}
|
|
1494
|
+
},
|
|
1495
|
+
tags: { role: STATIC_PLACE_ROLE }
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
id: `merge_${slug}`,
|
|
1499
|
+
type: "static_extend_merge",
|
|
1500
|
+
inputs: { placed: `$ref:place_${slug}.placed`, shown: `$ref:place_${slug}.shown`, keep: `$ref:place_${slug}.keep` },
|
|
1501
|
+
params: {
|
|
1502
|
+
render_ratio: ratio,
|
|
1503
|
+
paint: {
|
|
1504
|
+
model,
|
|
1505
|
+
aspect_ratio: ratio,
|
|
1506
|
+
...params.quality !== void 0 ? { quality: params.quality } : {},
|
|
1507
|
+
...params.image_size !== void 0 ? { image_size: params.image_size } : {},
|
|
1508
|
+
prompt: EXTEND_PROMPT
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
);
|
|
1513
|
+
delivered[at2] = `$ref:merge_${slug}.image`;
|
|
1514
|
+
}
|
|
1515
|
+
if (added.length === 0) return canvas;
|
|
1516
|
+
const at = nodes.indexOf(final);
|
|
1517
|
+
return {
|
|
1518
|
+
...holder,
|
|
1519
|
+
nodes: [...nodes.slice(0, at), ...added, { ...final, inputs: { ...final.inputs, images: delivered } }, ...nodes.slice(at + 1)]
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
function placingCopy(nodes) {
|
|
1523
|
+
const told = nodes.find(
|
|
1524
|
+
(n) => (n.type === "static_safe_fit" || n.type === "static_place") && Array.isArray(n.params?.copy)
|
|
1525
|
+
)?.params?.copy;
|
|
1526
|
+
if (Array.isArray(told)) return told;
|
|
1527
|
+
const kept = nodes.find((n) => n.type === "image_aspect_adapt" && Array.isArray(n.params?.copy))?.params?.copy;
|
|
1528
|
+
return Array.isArray(kept) ? kept.filter((t) => typeof t === "string").map((text) => ({ text })) : void 0;
|
|
1529
|
+
}
|
|
1530
|
+
function withDeliveredSizes(canvas, last) {
|
|
1531
|
+
const holder = canvas;
|
|
1532
|
+
if (!Array.isArray(holder.nodes)) return canvas;
|
|
1533
|
+
const final = holder.nodes.find((n) => n.id === "final" && n.type === "collect");
|
|
1534
|
+
const labels = final?.params?.labels;
|
|
1535
|
+
const images = final?.inputs?.images;
|
|
1536
|
+
if (!final || !Array.isArray(labels) || !Array.isArray(images)) return canvas;
|
|
1537
|
+
const ingests = [];
|
|
1538
|
+
const roles = rolesById(canvas);
|
|
1539
|
+
const delivered = images.map((ref, i) => {
|
|
1540
|
+
const format = labels[i];
|
|
1541
|
+
const file = typeof format === "string" ? last[format] : void 0;
|
|
1542
|
+
if (!file || typeof format !== "string") return ref;
|
|
1543
|
+
const id = `last_${formatSlug(format)}`;
|
|
1544
|
+
const remote = /^(?:https?|data):/.test(file);
|
|
1545
|
+
const from = typeof ref === "string" ? /^\$ref:([^.]+)\./.exec(ref)?.[1] : void 0;
|
|
1546
|
+
const role = from ? roles[from] : void 0;
|
|
1547
|
+
ingests.push({
|
|
1548
|
+
id,
|
|
1549
|
+
type: "ingest",
|
|
1550
|
+
params: remote ? { source: "url", url: file, expect: "image" } : { source: "path", path: file, expect: "image" },
|
|
1551
|
+
...role === STATIC_MASTER_ROLE ? { tags: { role } } : {}
|
|
1552
|
+
});
|
|
1553
|
+
return `$ref:${id}.asset`;
|
|
1554
|
+
});
|
|
1555
|
+
const at = holder.nodes.indexOf(final);
|
|
1556
|
+
return {
|
|
1557
|
+
...holder,
|
|
1558
|
+
nodes: [
|
|
1559
|
+
...holder.nodes.slice(0, at),
|
|
1560
|
+
...ingests,
|
|
1561
|
+
{ ...final, inputs: { ...final.inputs, images: delivered } },
|
|
1562
|
+
...holder.nodes.slice(at + 1)
|
|
1563
|
+
]
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// src/commands/canvas/shown-version.ts
|
|
1568
|
+
import { z } from "zod";
|
|
1569
|
+
var ShownResponse = z.object({
|
|
1570
|
+
data: z.object({
|
|
1571
|
+
runId: z.string(),
|
|
1572
|
+
sourceSha: z.string().optional(),
|
|
1573
|
+
createdAt: z.number(),
|
|
1574
|
+
canvasSnapshotUrl: z.string().optional()
|
|
1575
|
+
})
|
|
1576
|
+
});
|
|
1577
|
+
var SnapshotFiles = z.object({ files: z.array(z.object({ path: z.string(), url: z.string() })) });
|
|
1578
|
+
var HISTORY = {
|
|
1579
|
+
shown: async (slug) => ShownResponse.parse(await apiPost("/api/creatives/run", { slug })).data,
|
|
1580
|
+
file: async (url) => {
|
|
1581
|
+
const res = await fetch(url);
|
|
1582
|
+
return res.ok ? res.json() : null;
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
async function shownVersion(canvasRoot, slug, sources = HISTORY) {
|
|
1586
|
+
const here = await readRenderState(canvasRoot, slug);
|
|
1587
|
+
if (here.rendered) return here;
|
|
1588
|
+
const run = await sources.shown(slug).catch(() => null);
|
|
1589
|
+
if (!run) return here;
|
|
1590
|
+
const prompt = run.canvasSnapshotUrl ? await drawnPrompt(run.canvasSnapshotUrl, sources) : void 0;
|
|
1591
|
+
return {
|
|
1592
|
+
...here,
|
|
1593
|
+
rendered: { sourceSha: run.sourceSha ?? "", runId: run.runId, at: run.createdAt },
|
|
1594
|
+
...prompt !== void 0 ? { prompt } : {}
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
async function drawnPrompt(manifestUrl, sources) {
|
|
1598
|
+
const manifest = SnapshotFiles.safeParse(await sources.file(manifestUrl).catch(() => null));
|
|
1599
|
+
const entry = manifest.success ? manifest.data.files.find((f) => f.path === GLOBAL_PROMPT_FILE) : void 0;
|
|
1600
|
+
if (!entry) return void 0;
|
|
1601
|
+
return await sources.file(entry.url).catch(() => null) ?? void 0;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/commands/canvas/static-run-review.ts
|
|
1605
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1606
|
+
import path6 from "path";
|
|
1607
|
+
|
|
1608
|
+
// src/commands/canvas/glance-check.ts
|
|
1609
|
+
import sharp from "sharp";
|
|
1610
|
+
import { z as z2 } from "zod";
|
|
1611
|
+
var GLANCE_REQUIRED = {
|
|
1612
|
+
offer: "guess",
|
|
1613
|
+
for_whom: "unclear",
|
|
1614
|
+
next_step: "unclear"
|
|
1615
|
+
};
|
|
1616
|
+
var GLANCE_WIDTH = 400;
|
|
1617
|
+
var GlanceAnswerSchema = ViewerAnswerSchema.extend({
|
|
1618
|
+
/** What kind of product or service they take it for (D4). */
|
|
1619
|
+
category: z2.object({ answer: z2.string().default(""), clarity: z2.enum(["clear", "guess", "unclear"]).default("unclear") }).default({ answer: "", clarity: "unclear" }),
|
|
1620
|
+
stopped: z2.string().default(""),
|
|
1621
|
+
me: z2.enum(["yes", "no", "unsure"]).default("unsure")
|
|
1622
|
+
});
|
|
1623
|
+
function wherePhrase(placements) {
|
|
1624
|
+
const fullScreen = placements.length === 0 || placements.some((p) => /automatic|stor|reel/i.test(p));
|
|
1625
|
+
return fullScreen ? "your Instagram or Facebook feed, Stories or Reels" : "your Instagram or Facebook feed";
|
|
1626
|
+
}
|
|
1627
|
+
function knowledge(viewer) {
|
|
1628
|
+
if (!viewer.knows.includes("brand")) return "You have never heard of the advertiser.";
|
|
1629
|
+
const offer = viewer.knows.includes("offer") ? " You know roughly what they sell." : "";
|
|
1630
|
+
return `You have come across the advertiser before.${offer}`;
|
|
1631
|
+
}
|
|
1632
|
+
function buildGlancePrompt(input) {
|
|
1633
|
+
const who = input.market ? `You are ${input.market}. ` : "";
|
|
1634
|
+
return [
|
|
1635
|
+
`${who}You are scrolling ${wherePhrase(input.viewer.placements)} on your phone. ${knowledge(input.viewer)}`,
|
|
1636
|
+
"This ad passes under your thumb and holds you for about three seconds: you take in the picture and read the biggest words, not the small print. The image is shown at the size your phone shows it.",
|
|
1637
|
+
"Answer as this person, from those three seconds alone. Do not fill gaps with what ads like this usually sell.",
|
|
1638
|
+
'For each question give your answer in English and how sure you are: "clear" when the ad says it outright or you already know it; "guess" when you are piecing it together; "unclear" when you cannot tell.',
|
|
1639
|
+
"offer: what is being sold or offered?",
|
|
1640
|
+
"for_whom: who is it for?",
|
|
1641
|
+
"next_step: what would you do next if you wanted it?",
|
|
1642
|
+
"category: what kind of product or service is this for (a payroll app, a trade course, a phone)?",
|
|
1643
|
+
'missing: in one short sentence, what you would need to be told to get it; "" when nothing.',
|
|
1644
|
+
"stopped: what made you stop on it, or what nearly made you scroll past, in a few words.",
|
|
1645
|
+
'me: is this for someone like you? "yes", "no" or "unsure".',
|
|
1646
|
+
"",
|
|
1647
|
+
'Reply with only this JSON: {"offer":{"answer":"","clarity":"clear|guess|unclear"},"for_whom":{"answer":"","clarity":"clear|guess|unclear"},"next_step":{"answer":"","clarity":"clear|guess|unclear"},"category":{"answer":"","clarity":"clear|guess|unclear"},"missing":"","stopped":"","me":"yes|no|unsure"}'
|
|
1648
|
+
].join("\n");
|
|
1649
|
+
}
|
|
1650
|
+
var QUESTION_WORDS = {
|
|
1651
|
+
offer: "what is offered",
|
|
1652
|
+
for_whom: "who it is for",
|
|
1653
|
+
next_step: "what to do next"
|
|
1654
|
+
};
|
|
1655
|
+
function missWords(miss) {
|
|
1656
|
+
if (miss.question === "presumes") return `it talks to them as a customer already ("${miss.viewerSaid}")`;
|
|
1657
|
+
const said = miss.viewerSaid.trim() ? `"${miss.viewerSaid.trim()}"` : "nothing";
|
|
1658
|
+
const what = QUESTION_WORDS[miss.question];
|
|
1659
|
+
return miss.reason === "misread" ? `${what} (they were sure it is ${said})` : `${what} (they said ${said})`;
|
|
1660
|
+
}
|
|
1661
|
+
function glanceFindings(answer, intended, format, viewer, misread = { offer: false, category: false }) {
|
|
1662
|
+
const verdict = judgeViewerAnswer({ ...answer, presumes: "" }, intended, GLANCE_REQUIRED, misread.offer);
|
|
1663
|
+
const category = answer.category.answer.trim();
|
|
1664
|
+
const kind = intended.recognition && category && misread.category ? [
|
|
1665
|
+
{
|
|
1666
|
+
severity: "warning",
|
|
1667
|
+
what: `${format}: At a glance, a stranger took it for "${category}"; it is meant as ${intended.recognition}.`
|
|
1668
|
+
}
|
|
1669
|
+
] : [];
|
|
1670
|
+
if (verdict.pass) return kind;
|
|
1671
|
+
const needed = answer.missing.trim() ? ` They needed: ${answer.missing.trim().replace(/[.!]+$/, "")}.` : "";
|
|
1672
|
+
const missed = verdict.missed.map(missWords).join("; ");
|
|
1673
|
+
return [
|
|
1674
|
+
{
|
|
1675
|
+
severity: "blocking",
|
|
1676
|
+
what: `${format}: At a glance, ${describeViewer(viewer).replace(/, on .* placements$/, "")} could not tell ${missed}.${needed}`
|
|
1677
|
+
},
|
|
1678
|
+
...kind
|
|
1679
|
+
];
|
|
1680
|
+
}
|
|
1681
|
+
async function glanceMisreads(answer, intended, judge) {
|
|
1682
|
+
const sure = sureEnough(answer.offer.clarity, GLANCE_REQUIRED.offer);
|
|
1683
|
+
const [offer = false, category = false] = await misreadings(
|
|
1684
|
+
[
|
|
1685
|
+
{ said: sure ? answer.offer.answer : "", meant: intended.offer, sells: intended.sells },
|
|
1686
|
+
{ said: answer.category.answer, meant: intended.recognition }
|
|
1687
|
+
],
|
|
1688
|
+
judge
|
|
1689
|
+
);
|
|
1690
|
+
return { offer, category };
|
|
1691
|
+
}
|
|
1692
|
+
function gatewayGlanceModel(onSpend) {
|
|
1693
|
+
return (jpeg, prompt) => askAboutImages(GLANCE_CHECK_MODEL, [jpeg], prompt, onSpend);
|
|
1694
|
+
}
|
|
1695
|
+
async function glanceCheck(opts) {
|
|
1696
|
+
let jpeg;
|
|
1697
|
+
try {
|
|
1698
|
+
const bytes = await bytesOf(opts.image);
|
|
1699
|
+
jpeg = (await sharp(bytes).resize({ width: GLANCE_WIDTH }).jpeg({ quality: 85 }).toBuffer()).toString("base64");
|
|
1700
|
+
} catch {
|
|
1701
|
+
return null;
|
|
1702
|
+
}
|
|
1703
|
+
const prompt = buildGlancePrompt({ viewer: opts.viewer, ...opts.market ? { market: opts.market } : {} });
|
|
1704
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1705
|
+
const parsed = GlanceAnswerSchema.safeParse(await opts.ask(jpeg, prompt));
|
|
1706
|
+
if (parsed.success) {
|
|
1707
|
+
const misread = await glanceMisreads(parsed.data, opts.intended, opts.judge ?? jevReadJudge());
|
|
1708
|
+
return glanceFindings(parsed.data, opts.intended, opts.format, opts.viewer, misread);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/commands/canvas/static-run-review.ts
|
|
1715
|
+
function imagesOf(output) {
|
|
1716
|
+
const list = Array.isArray(output) ? output : [output];
|
|
1717
|
+
const images = list.filter((o) => Boolean(o) && typeof o === "object");
|
|
1718
|
+
return images.length > 0 && images.every((i) => i.kind === "image") ? images : null;
|
|
1719
|
+
}
|
|
1720
|
+
function promptPathOf(parsed) {
|
|
1721
|
+
const nodes = parsed?.nodes;
|
|
1722
|
+
if (!Array.isArray(nodes)) return null;
|
|
1723
|
+
const prompt = nodes.find((n) => n?.id === "prompt" && n?.type === "ingest");
|
|
1724
|
+
const p = prompt?.params;
|
|
1725
|
+
return p?.source === "path" && p.expect === "json" && typeof p.path === "string" ? p.path : null;
|
|
1726
|
+
}
|
|
1727
|
+
function staticReviewTargets(parsed, output) {
|
|
1728
|
+
const promptPath = promptPathOf(parsed);
|
|
1729
|
+
const images = imagesOf(output);
|
|
1730
|
+
if (!promptPath || !images) return null;
|
|
1731
|
+
const labels = finalOutputLabelsFromCanvas(parsed)?.labels ?? [];
|
|
1732
|
+
return {
|
|
1733
|
+
promptPath,
|
|
1734
|
+
// A local file when the run downloaded it, else its durable URL.
|
|
1735
|
+
images: images.flatMap((image, i) => {
|
|
1736
|
+
const source = typeof image.path === "string" ? image.path : typeof image.url === "string" ? image.url : null;
|
|
1737
|
+
return source ? [{ path: source, format: labels[i] ?? "" }] : [];
|
|
1738
|
+
})
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
function nodesOf(parsed) {
|
|
1742
|
+
const nodes = parsed?.nodes;
|
|
1743
|
+
return Array.isArray(nodes) ? nodes : [];
|
|
1744
|
+
}
|
|
1745
|
+
var refId = (ref) => typeof ref === "string" ? ref.match(/^\$ref:([a-z][a-z0-9_]*)\./)?.[1] ?? null : null;
|
|
1746
|
+
var hasRole = (n, role) => n.tags?.role === role;
|
|
1747
|
+
function variantCount(nodes) {
|
|
1748
|
+
const calls = nodes.filter((n) => hasRole(n, STATIC_VARIANT_CALL_ROLE)).length;
|
|
1749
|
+
if (calls > 0) return calls;
|
|
1750
|
+
const one = nodes.find((n) => hasRole(n, STATIC_VARIANTS_ROLE) && n.type === "image_generate");
|
|
1751
|
+
if (one) return typeof one.params?.n === "number" ? one.params.n : 1;
|
|
1752
|
+
if (nodes.some((n) => hasRole(n, STATIC_FIX_ROLE))) return 1;
|
|
1753
|
+
return nodes.filter((n) => n.type === "image_generate" && /^ad(_[a-z])?$/.test(String(n.id))).length;
|
|
1754
|
+
}
|
|
1755
|
+
function staticRunShape(parsed) {
|
|
1756
|
+
const nodes = nodesOf(parsed);
|
|
1757
|
+
const renders = nodes.filter((n) => n.type === "image_generate" && typeof n.id === "string");
|
|
1758
|
+
const candidates = variantCount(nodes);
|
|
1759
|
+
const logoIds = new Set(nodes.filter((n) => hasRole(n, BRAND_LOGO_ROLE)).map((n) => String(n.id)));
|
|
1760
|
+
const logo = nodes.some((n) => hasRole(n, LOGO_COMPOSITE_ROLE)) || renders.some((n) => {
|
|
1761
|
+
const refs = n.inputs?.reference;
|
|
1762
|
+
return (Array.isArray(refs) ? refs : [refs]).some((r) => logoIds.has(refId(r) ?? ""));
|
|
1763
|
+
});
|
|
1764
|
+
const masterId = nodes.find((n) => hasRole(n, STATIC_MASTER_ROLE))?.id;
|
|
1765
|
+
const adapt = nodes.find((n) => hasRole(n, STATIC_ADAPT_ROLE));
|
|
1766
|
+
return {
|
|
1767
|
+
candidates,
|
|
1768
|
+
judged: candidates > 1 && nodes.some(
|
|
1769
|
+
(n) => n.type === "image_select" && (hasRole(n, STATIC_PICK_ROLE) || n.id === "pick") || hasRole(n, VARIANT_PREFILTER_ROLE) && typeof n.params?.judge_model === "string"
|
|
1770
|
+
),
|
|
1771
|
+
relays: masterId !== void 0 ? 0 : Math.max(0, renders.length - candidates),
|
|
1772
|
+
cuts: nodes.filter((n) => {
|
|
1773
|
+
const from = refId(n.inputs?.image);
|
|
1774
|
+
return hasRole(n, STATIC_SIZE_ROLE) && from !== null && from === masterId;
|
|
1775
|
+
}).length,
|
|
1776
|
+
relaid: nodes.filter((n) => hasRole(n, STATIC_ADAPT_FORMAT_ROLE)).length,
|
|
1777
|
+
adapted: Array.isArray(adapt?.params?.formats) ? adapt.params.formats.length : 0,
|
|
1778
|
+
logo
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function shapeHint(shape, copy) {
|
|
1782
|
+
const variants = `${shape.candidates} variant${shape.candidates === 1 ? "" : "s"} rendered${shape.judged ? ", ranked by the checks with a judge on a tie" : ", no judge"}`;
|
|
1783
|
+
const sizes = [
|
|
1784
|
+
...shape.relaid > 0 ? [`${shape.relaid} size${shape.relaid === 1 ? "" : "s"} re-laid out from the chosen picture (one request each)`] : [],
|
|
1785
|
+
...shape.cuts > 0 ? [`${shape.cuts} size${shape.cuts === 1 ? "" : "s"} cut from the chosen picture`] : [],
|
|
1786
|
+
...shape.adapted > 0 ? [`${shape.adapted} adapted from it`] : [],
|
|
1787
|
+
...shape.relays > 0 ? [`${shape.relays} other format${shape.relays === 1 ? "" : "s"} re-laid from the winner`] : []
|
|
1788
|
+
];
|
|
1789
|
+
const text = copy === 0 ? "no copy (the picture is the ad)" : "the copy set by the model and read back word for word";
|
|
1790
|
+
return `This run: ${variants}${sizes.length > 0 ? `; ${sizes.join("; ")}` : ""}; ${text}; ${shape.logo ? "the brand logo on the ad from its real file" : "no logo"}.`;
|
|
1791
|
+
}
|
|
1792
|
+
var PAID_FOR = {
|
|
1793
|
+
edit: "one edit of the version shown",
|
|
1794
|
+
sizes: "only the sizes asked for, laid out again",
|
|
1795
|
+
options: "new options from scratch, every size made from the one chosen"
|
|
1796
|
+
};
|
|
1797
|
+
function runCostHint(stats, kind) {
|
|
1798
|
+
if (!stats) return null;
|
|
1799
|
+
const cached = stats.cached_nodes > 0 ? ` (${stats.cached_nodes} of ${stats.total_nodes} steps came from the cache, free)` : "";
|
|
1800
|
+
return `This run cost ${Math.round(stats.total_credits)} credits: ${PAID_FOR[kind]}${cached}.`;
|
|
1801
|
+
}
|
|
1802
|
+
async function textOf(ref) {
|
|
1803
|
+
const r = ref;
|
|
1804
|
+
try {
|
|
1805
|
+
if (typeof r?.path === "string") return await readFile4(r.path, "utf8");
|
|
1806
|
+
if (typeof r?.url === "string") {
|
|
1807
|
+
const res = await fetch(r.url);
|
|
1808
|
+
return res.ok ? await res.text() : null;
|
|
1809
|
+
}
|
|
1810
|
+
} catch {
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
return null;
|
|
1814
|
+
}
|
|
1815
|
+
var FIX = "One thing wrong in a version that is otherwise right: `baker canvas run \u2026 --fix \"<what to change>\"` edits that version and changes only that, keeping the picture, the layout and the other words (one render). Wrong or missing words: check the lines in prompt.json `copy` are exactly what should be printed, shorten a line the model keeps breaking, and re-run: a change only to copy, positions or colours is made as an edit of the version shown. Wrong picture, colour or subject: edit prompt.json's `scene`, `colors` or `references`. Layout or a logo drawn wrong: say in each line's `position` (and `scene.layout`) where it sits, by what is around it. A right prompt that rolled badly: `baker canvas run \u2026 --regenerate static_variants` renders new options (every size follows from the new pick). One size wrong while the chosen picture is right (words cut or too small, the button small, something drawn twice): `baker canvas run \u2026 --regenerate adapt_<size>` (for example `adapt_9x16`) lays out that size again from the same picture (one request).";
|
|
1816
|
+
function verdictHints(verdict) {
|
|
1817
|
+
if (verdict === "needs_fixes") {
|
|
1818
|
+
return [
|
|
1819
|
+
'NOT READY: the finished ad has blocking problems. Do not present it as done, and never ask the person whether to fix them: fix them now, inside this version. A defect in a version that is otherwise right is a `--fix`: it edits the chosen picture only, and every size is made from it again (each re-laid-out size takes one request; a cut is free). The same defect after a fix means the fix does not work: change the spec (prompt.json) or the approach, never the same retry. If it still cannot be fixed, show the person the best version and say in one plain sentence what is left, in their words ("the logo sits where Stories covers it"), without a question; never record your own checks as their feedback.',
|
|
1820
|
+
FIX
|
|
1821
|
+
];
|
|
1822
|
+
}
|
|
1823
|
+
if (verdict === "not_reviewed") {
|
|
1824
|
+
return [
|
|
1825
|
+
"The finished ad was NOT reviewed: no model gateway key here, or the review model did not answer twice. Look at every word of every format yourself before showing it, or re-run the same command (every node comes from the cache) to review it again."
|
|
1826
|
+
];
|
|
1827
|
+
}
|
|
1828
|
+
if (verdict === "ready") {
|
|
1829
|
+
return ["Reviewed every format of the finished ad: every word read back exactly, nothing to fix.", SHOW_IT];
|
|
1830
|
+
}
|
|
1831
|
+
return verdict === "ready_with_notes" ? [SHOW_IT] : [];
|
|
1832
|
+
}
|
|
1833
|
+
var SHOW_IT = `Show it as a Version ("Version 2"), never a run id or a file, in one line: the idea's message, who it is for, the customer quote it stands on (or that it is an explore bet), and the finding it answers. Name the versions you would run first; never ask which to keep or whether to publish: the person answers in their own time.`;
|
|
1834
|
+
function allowedSceneWords(spec) {
|
|
1835
|
+
return [.../* @__PURE__ */ new Set([...spec.scene_text, ...spec.third_party_brands.map((b) => b.name)])];
|
|
1836
|
+
}
|
|
1837
|
+
function brandPalette(brand) {
|
|
1838
|
+
if (!brand) return [];
|
|
1839
|
+
const named = [brand.ink, brand.accent, brand.cta, brand.ground, ...brand.palette];
|
|
1840
|
+
return [...new Set(named.filter((hex) => typeof hex === "string"))];
|
|
1841
|
+
}
|
|
1842
|
+
function safeAreaPlacements(viewer) {
|
|
1843
|
+
const named = (viewer?.placements ?? []).filter((p) => p !== "automatic");
|
|
1844
|
+
return named.length > 0 && named.length === viewer?.placements.length ? named : void 0;
|
|
1845
|
+
}
|
|
1846
|
+
function canvasPlacements(parsed) {
|
|
1847
|
+
const node = nodesWithRole(parsed, VARIANT_PREFILTER_ROLE)[0];
|
|
1848
|
+
const placements = node?.params?.placements;
|
|
1849
|
+
return Array.isArray(placements) && placements.length > 0 && placements.every((p) => typeof p === "string") ? placements : void 0;
|
|
1850
|
+
}
|
|
1851
|
+
async function reviewStaticRun(opts) {
|
|
1852
|
+
const targets = staticReviewTargets(opts.parsed, opts.output);
|
|
1853
|
+
if (!targets) return null;
|
|
1854
|
+
const prefilterId = nodesWithRole(opts.parsed, VARIANT_PREFILTER_ROLE)[0]?.id;
|
|
1855
|
+
const tieBreak = prefilterId ? opts.outputsByNode[prefilterId]?.reasoning : void 0;
|
|
1856
|
+
const pickId = nodesWithRole(opts.parsed, STATIC_PICK_ROLE)[0]?.id ?? "pick";
|
|
1857
|
+
const reasoning = typeof tieBreak === "string" ? tieBreak : await textOf(opts.outputsByNode[pickId]?.reasoning);
|
|
1858
|
+
const judge = reasoning ? parseJudgeScores(reasoning) : null;
|
|
1859
|
+
let copy = [];
|
|
1860
|
+
let mechanisms = [];
|
|
1861
|
+
let intended = {};
|
|
1862
|
+
let expectLogo = false;
|
|
1863
|
+
let concept = null;
|
|
1864
|
+
let uncast = [];
|
|
1865
|
+
let brand = opts.brandName;
|
|
1866
|
+
let lockup = [...opts.lockup ?? []];
|
|
1867
|
+
let sceneText = [];
|
|
1868
|
+
let lines = [];
|
|
1869
|
+
let native;
|
|
1870
|
+
let palette = [];
|
|
1871
|
+
try {
|
|
1872
|
+
const spec = readAdSpec(JSON.parse(await readFile4(targets.promptPath, "utf8")));
|
|
1873
|
+
copy = spec.copy.map((l) => l.text);
|
|
1874
|
+
lines = spec.copy.map((l) => ({ text: l.text, lettering: l.lettering, ...l.role ? { role: l.role } : {} }));
|
|
1875
|
+
if (spec.native) {
|
|
1876
|
+
copy = nativeCopy(spec.native);
|
|
1877
|
+
lines = copy.map((text) => ({ text, lettering: "designed" }));
|
|
1878
|
+
native = nativeSurfaceName(spec.native);
|
|
1879
|
+
}
|
|
1880
|
+
palette = spec.native ? [] : brandPalette(spec.brand);
|
|
1881
|
+
mechanisms = spec.mechanism.map((m) => m.device);
|
|
1882
|
+
brand = spec.brand?.name ?? opts.brandName;
|
|
1883
|
+
const sells = marketQuery(
|
|
1884
|
+
await readFile4(path6.resolve(path6.dirname(targets.promptPath), "../../../COMPANY.md"), "utf8").catch(() => "")
|
|
1885
|
+
);
|
|
1886
|
+
intended = {
|
|
1887
|
+
...spec.message?.trim() ? { offer: spec.message.trim() } : {},
|
|
1888
|
+
...spec.viewer?.who.trim() ? { persona: spec.viewer.who.trim() } : {},
|
|
1889
|
+
...spec.recognition?.trim() ? { recognition: spec.recognition.trim() } : {},
|
|
1890
|
+
...sells ? { sells } : {}
|
|
1891
|
+
};
|
|
1892
|
+
lockup = [.../* @__PURE__ */ new Set([...spec.brand?.lockup ?? [], ...lockup])];
|
|
1893
|
+
sceneText = [
|
|
1894
|
+
...allowedSceneWords(spec),
|
|
1895
|
+
...spec.native ? nativeChrome(spec.native, spec.viewer?.market?.language, interfaceWordsOf(opts.parsed)) : []
|
|
1896
|
+
];
|
|
1897
|
+
expectLogo = spec.logo && !spec.native && staticRunShape(opts.parsed).logo;
|
|
1898
|
+
concept = conceptSummary(spec);
|
|
1899
|
+
const cacheDir = path6.join(findWorkspaceRoot(path6.dirname(targets.promptPath)) ?? path6.dirname(targets.promptPath), "canvas", ".cache");
|
|
1900
|
+
const people = spec.native ? [] : await peopleReader(cacheDir, (u) => opts.onSpend?.(u))(copy) ?? [];
|
|
1901
|
+
uncast = uncastPeople(spec, people);
|
|
1902
|
+
} catch {
|
|
1903
|
+
copy = [];
|
|
1904
|
+
}
|
|
1905
|
+
const shape = staticRunShape(opts.parsed);
|
|
1906
|
+
const placements = canvasPlacements(opts.parsed) ?? safeAreaPlacements(opts.viewer);
|
|
1907
|
+
const masterFormat = staticMasterFormat(opts.parsed) ?? void 0;
|
|
1908
|
+
const glancing = opts.glance === false ? Promise.resolve(null) : glanceAtFinished(
|
|
1909
|
+
targets.images,
|
|
1910
|
+
opts.viewer ?? STRANGER,
|
|
1911
|
+
intended,
|
|
1912
|
+
opts.glance ?? gatewayGlanceModel(opts.onSpend),
|
|
1913
|
+
opts.onSpend
|
|
1914
|
+
).catch(() => null);
|
|
1915
|
+
const comparing = opts.previous ? nearIdenticalFindings(targets.images, opts.previous).catch(() => []) : Promise.resolve([]);
|
|
1916
|
+
const untouched = opts.settled ? await samePictureSizes(targets.images, opts.settled.images) : [];
|
|
1917
|
+
const changed = targets.images.filter((i) => !untouched.includes(i.format));
|
|
1918
|
+
const review = await reviewRenderedStatic({
|
|
1919
|
+
images: changed.length > 0 ? changed : targets.images,
|
|
1920
|
+
copy,
|
|
1921
|
+
lines,
|
|
1922
|
+
palette,
|
|
1923
|
+
...placements ? { placements } : {},
|
|
1924
|
+
...brand ? { brand } : {},
|
|
1925
|
+
...lockup.length > 0 ? { lockup } : {},
|
|
1926
|
+
...sceneText.length > 0 ? { sceneText } : {},
|
|
1927
|
+
...masterFormat ? { masterFormat } : {},
|
|
1928
|
+
...expectLogo ? { expectLogo } : {},
|
|
1929
|
+
...concept && concept.mustKeep.length > 0 && referenceSource(opts.parsed) ? { concept: { ...concept, reference: referenceSource(opts.parsed) } } : {},
|
|
1930
|
+
logo: shape.logo,
|
|
1931
|
+
...native ? { native } : {},
|
|
1932
|
+
...uncast.length > 0 ? { uncastPeople: uncast } : {},
|
|
1933
|
+
...native ? { scrolled: scrolledLines(opts.parsed, opts.outputsByNode) } : {},
|
|
1934
|
+
mechanisms,
|
|
1935
|
+
...opts.viewer ? { viewer: describeViewer(opts.viewer) } : {},
|
|
1936
|
+
onSpend: opts.onSpend
|
|
1937
|
+
});
|
|
1938
|
+
const [glanced, compared] = await Promise.all([glancing, comparing]);
|
|
1939
|
+
const glance = review === null ? null : glanced;
|
|
1940
|
+
const unchanged = review === null ? [] : compared;
|
|
1941
|
+
const fresh = review === null ? null : [...review, ...glance ?? [], ...unchanged];
|
|
1942
|
+
const reviewed = fresh === null || !opts.settled ? fresh : carryUnchanged(fresh, opts.settled.review, untouched);
|
|
1943
|
+
const verdict = staticVerdict(reviewed);
|
|
1944
|
+
const hints = verdictHints(verdict);
|
|
1945
|
+
const sizeHints = verdict === "needs_fixes" ? sizeFixHints(opts.parsed, reviewed) : [];
|
|
1946
|
+
const sizesFirst = sizeHints[0]?.startsWith("Do this first") === true;
|
|
1947
|
+
if (sizesFirst) hints.push(...sizeHints);
|
|
1948
|
+
for (const f of reviewed ?? []) hints.push(`${f.severity === "blocking" ? "Must fix" : "Should fix"}: ${f.what}`);
|
|
1949
|
+
if (review !== null && glance === null && opts.glance !== false) {
|
|
1950
|
+
hints.push("The finished ad was not shown to a stranger at a glance (the model did not answer): look at the 4:5 at phone size yourself and say what it sells in three seconds.");
|
|
1951
|
+
}
|
|
1952
|
+
if (!sizesFirst) hints.push(...sizeHints);
|
|
1953
|
+
hints.push(shapeHint(shape, copy.length));
|
|
1954
|
+
hints.push(...variantHints(judge, prefilterOutcome(opts.parsed, opts.outputsByNode)));
|
|
1955
|
+
return { review: reviewed, verdict, judge, hints };
|
|
1956
|
+
}
|
|
1957
|
+
function scrolledLines(parsed, outputsByNode) {
|
|
1958
|
+
return Object.fromEntries(
|
|
1959
|
+
nodesOf(parsed).flatMap((n) => {
|
|
1960
|
+
const out = typeof n.id === "string" ? outputsByNode[n.id] : void 0;
|
|
1961
|
+
const format = n.params?.format;
|
|
1962
|
+
return n.type === "static_native" && typeof format === "string" && Array.isArray(out?.scrolled) ? [[format, out.scrolled.filter((t) => typeof t === "string")]] : [];
|
|
1963
|
+
})
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
function referenceSource(parsed) {
|
|
1967
|
+
const original = nodesOf(parsed).find((n) => n.id === "original" && n.type === "ingest");
|
|
1968
|
+
const source = original?.params?.url ?? original?.params?.path;
|
|
1969
|
+
return typeof source === "string" && !source.includes("[TODO") ? source : null;
|
|
1970
|
+
}
|
|
1971
|
+
function interfaceWordsOf(parsed) {
|
|
1972
|
+
const nodes = parsed?.nodes;
|
|
1973
|
+
const screen = Array.isArray(nodes) ? nodes.find((n) => n?.type === "static_native") : void 0;
|
|
1974
|
+
return readInterfaceWords(screen?.params?.interface_words) ?? void 0;
|
|
1975
|
+
}
|
|
1976
|
+
var STRANGER = { temperature: "cold", knows: [], placements: ["automatic"], source: "knowledge", reasons: [] };
|
|
1977
|
+
async function glanceAtFinished(images, viewer, intended, ask, onSpend) {
|
|
1978
|
+
const shown = images.find((i) => i.format === "4:5") ?? images[0];
|
|
1979
|
+
if (!shown) return null;
|
|
1980
|
+
return await glanceCheck({
|
|
1981
|
+
image: shown.path,
|
|
1982
|
+
format: shown.format,
|
|
1983
|
+
viewer,
|
|
1984
|
+
intended,
|
|
1985
|
+
ask,
|
|
1986
|
+
judge: jevReadJudge(onSpend),
|
|
1987
|
+
...viewer.market ? { market: describeMarket(viewer.market) } : {}
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
function sizeFixHints(parsed, review) {
|
|
1991
|
+
const relaid = nodesWithRole(parsed, STATIC_ADAPT_FORMAT_ROLE).map(
|
|
1992
|
+
(n) => n.id
|
|
1993
|
+
);
|
|
1994
|
+
const blocking = (review ?? []).filter((f) => f.severity === "blocking");
|
|
1995
|
+
const regenerate = relaid.flatMap((id) => {
|
|
1996
|
+
const format = formatOfAdaptId(id);
|
|
1997
|
+
if (!format || !blocking.some((f) => f.what.startsWith(`${format}:`))) return [];
|
|
1998
|
+
return [
|
|
1999
|
+
`The ${format} alone needs fixing and the chosen picture is right: \`baker canvas run \u2026 --regenerate ${id}\` lays out the ${format} again from the same picture (one request). If the chosen picture itself is wrong, \`--fix\` it instead: every size follows.`
|
|
2000
|
+
];
|
|
2001
|
+
});
|
|
2002
|
+
const relaidFormats = relaid.map(formatOfAdaptId).filter((f) => Boolean(f));
|
|
2003
|
+
const sizesOnly = regenerate.length > 0 && blocking.every((f) => relaidFormats.some((format) => f.what.startsWith(`${format}:`)));
|
|
2004
|
+
const lead = sizesOnly ? regenerate.map((h, i) => i === 0 ? `Do this first, not a --fix: ${h}` : h) : regenerate;
|
|
2005
|
+
return [...lead, ...safeFitHints(parsed, blocking)];
|
|
2006
|
+
}
|
|
2007
|
+
var COVERED = / sits? where .+? covers? (?:it|them) /;
|
|
2008
|
+
function safeFitHints(parsed, blocking) {
|
|
2009
|
+
return nodesWithRole(parsed, STATIC_SAFE_FIT_ROLE).flatMap((n) => {
|
|
2010
|
+
const format = typeof n.params?.format === "string" ? n.params.format : formatOfSlug(n.id.replace(/^safe_/, ""));
|
|
2011
|
+
if (!blocking.some((f) => f.what.startsWith(`${format}:`) && COVERED.test(f.what))) return [];
|
|
2012
|
+
return [
|
|
2013
|
+
`The ${format} was already shrunk and shifted automatically to clear what the app covers; small misses are corrected that way, so this one is too large for it. Change the layout itself (shorter copy, fewer lines, a smaller block of text) rather than a \`--fix\` asking to move it, which moves it only about 1%.`
|
|
2014
|
+
];
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
function formatOfSlug(slug) {
|
|
2018
|
+
return slug.replace("x", ":").replace("_", ".");
|
|
2019
|
+
}
|
|
2020
|
+
function prefilterNodeId(parsed) {
|
|
2021
|
+
return nodesWithRole(parsed, VARIANT_PREFILTER_ROLE)[0]?.id ?? null;
|
|
2022
|
+
}
|
|
2023
|
+
function prefilterOutcome(parsed, outputsByNode) {
|
|
2024
|
+
const id = prefilterNodeId(parsed);
|
|
2025
|
+
const out = id ? outputsByNode[id] : void 0;
|
|
2026
|
+
return out && Array.isArray(out.dropped) ? out : null;
|
|
2027
|
+
}
|
|
2028
|
+
function prefilterSpend(parsed, outputsByNode, nodeRuns) {
|
|
2029
|
+
const cached = (id2) => nodeRuns.some((r) => r.node_id === id2 && r.cached);
|
|
2030
|
+
const id = prefilterNodeId(parsed);
|
|
2031
|
+
const checks = id && !cached(id) ? prefilterOutcome(parsed, outputsByNode)?.usage ?? [] : [];
|
|
2032
|
+
const fits = [...nodesWithRole(parsed, STATIC_SAFE_FIT_ROLE), ...nodesWithRole(parsed, STATIC_PLACE_ROLE)].filter((n) => !cached(n.id)).flatMap((n) => {
|
|
2033
|
+
const usage = outputsByNode[n.id]?.usage;
|
|
2034
|
+
return Array.isArray(usage) ? usage : [];
|
|
2035
|
+
});
|
|
2036
|
+
return [...checks, ...fits];
|
|
2037
|
+
}
|
|
2038
|
+
function variantHints(judge, prefilter) {
|
|
2039
|
+
const hints = [];
|
|
2040
|
+
const dropped = prefilter?.dropped ?? [];
|
|
2041
|
+
const kept = Array.isArray(prefilter?.images) ? prefilter.images.length : judge?.length ?? 0;
|
|
2042
|
+
const total = dropped.length + kept;
|
|
2043
|
+
if (prefilter?.flagged) {
|
|
2044
|
+
hints.push(
|
|
2045
|
+
`Every variant failed the checks; variant ${prefilter.flagged.index + 1} had the fewest problems and was kept: ${prefilter.flagged.reasons.join(" ")}`
|
|
2046
|
+
);
|
|
2047
|
+
} else if (dropped.length > 0) {
|
|
2048
|
+
const list = dropped.map((d) => `variant ${d.index + 1} (${d.reasons.join(" ")})`).join("; ");
|
|
2049
|
+
hints.push(`${dropped.length} of ${total} variants set aside by the checks before the judge: ${list}.`);
|
|
2050
|
+
}
|
|
2051
|
+
const alone = prefilter?.order?.length === 1 && !prefilter.flagged ? prefilter.order[0] : void 0;
|
|
2052
|
+
if (alone !== void 0 && dropped.length > 0) {
|
|
2053
|
+
hints.push(
|
|
2054
|
+
`The checks ranked variant ${alone + 1} best, so no judge was needed; the others stay in this run as options the person can switch to (\`baker canvas run --use-variant\`).`
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
const winner = judge ? judgeWinner(judge) : null;
|
|
2058
|
+
if (judge && winner) {
|
|
2059
|
+
const gone = new Set(dropped.map((d) => d.index));
|
|
2060
|
+
const shown = prefilter?.order ?? Array.from({ length: total }, (_, i) => i).filter((i) => !gone.has(i));
|
|
2061
|
+
const variant = (shown[winner.image - 1] ?? winner.image - 1) + 1;
|
|
2062
|
+
const why = winner.of === void 0 ? `${winner.total}/${Object.keys(winner.scores).length * 5}` : `first on ${Object.entries(winner.scores).filter(([, beaten]) => beaten === winner.of).map(([criterion]) => criterion.replace(/_/g, " ")).join(", ") || "none of the criteria alone"}`;
|
|
2063
|
+
hints.push(
|
|
2064
|
+
`The judge picked variant ${variant} of the ${judge.length} it compared (${why}); the others it compared stay in this run as options the person can switch to (\`baker canvas run --use-variant\`).`
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2067
|
+
return hints;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/commands/canvas/static-spec-repair.ts
|
|
2071
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
2072
|
+
import sharp2 from "sharp";
|
|
2073
|
+
import { z as z3 } from "zod";
|
|
2074
|
+
var Answer = z3.object({
|
|
2075
|
+
changed: z3.string().min(1),
|
|
2076
|
+
set: z3.array(z3.object({ path: z3.string().min(1), value: z3.unknown() }))
|
|
2077
|
+
});
|
|
2078
|
+
function withValueAt(root, path8, value) {
|
|
2079
|
+
const [key, ...rest] = path8;
|
|
2080
|
+
if (key === void 0) return value;
|
|
2081
|
+
if (Array.isArray(root)) {
|
|
2082
|
+
const at = Number(key);
|
|
2083
|
+
if (!Number.isInteger(at) || at < 0 || at > root.length) return root;
|
|
2084
|
+
const next = [...root];
|
|
2085
|
+
next[at] = withValueAt(root[at], rest, value);
|
|
2086
|
+
return next;
|
|
2087
|
+
}
|
|
2088
|
+
const holder = root && typeof root === "object" ? root : {};
|
|
2089
|
+
return { ...holder, [key]: withValueAt(holder[key], rest, value) };
|
|
2090
|
+
}
|
|
2091
|
+
function printedWords(spec) {
|
|
2092
|
+
return [...spec.copy.map((l) => l.text), ...spec.native ? nativeCopy(spec.native) : []];
|
|
2093
|
+
}
|
|
2094
|
+
function acceptRepair(before, answer) {
|
|
2095
|
+
const parsed = Answer.safeParse(answer);
|
|
2096
|
+
if (!parsed.success || !isAdSpec(before)) return null;
|
|
2097
|
+
const spec = parsed.data.set.reduce((at, { path: path8, value }) => withValueAt(at, path8.split("."), value), before);
|
|
2098
|
+
if (!isAdSpec(spec)) return null;
|
|
2099
|
+
const was = AdSpecSchema.safeParse(before);
|
|
2100
|
+
const now = AdSpecSchema.safeParse(spec);
|
|
2101
|
+
if (!was.success || !now.success) return null;
|
|
2102
|
+
if (JSON.stringify(was.data) === JSON.stringify(now.data)) return null;
|
|
2103
|
+
for (const key of ["concept", "mechanism", "message", "brand"]) {
|
|
2104
|
+
if (JSON.stringify(was.data[key]) !== JSON.stringify(now.data[key])) return null;
|
|
2105
|
+
}
|
|
2106
|
+
if (was.data.native && was.data.native.surface !== now.data.native?.surface) return null;
|
|
2107
|
+
const kept = printedWords(now.data);
|
|
2108
|
+
if (!printedWords(was.data).every((text) => kept.includes(text))) return null;
|
|
2109
|
+
return { changed: parsed.data.changed, spec };
|
|
2110
|
+
}
|
|
2111
|
+
function buildSpecRepairPrompt(spec, findings, schema) {
|
|
2112
|
+
return [
|
|
2113
|
+
"This is the spec (prompt.json) a static ad was drawn from, and the image is the ad it gave.",
|
|
2114
|
+
"A review found these defects in it, and they come from what the spec asks for, so drawing it again or editing the picture brings them back:",
|
|
2115
|
+
...findings.map((f) => `- ${f}`),
|
|
2116
|
+
"",
|
|
2117
|
+
"Correct the spec so these defects cannot happen, changing as little as you can:",
|
|
2118
|
+
"- Drop, replace or reword the element, photo, reference, layout or scene instruction that causes a defect. A second subject the viewer would read as another one (another pet, another person) is dropped, or made unmistakably the same one.",
|
|
2119
|
+
"- A rating or stars shown with no source: name the source only if the spec already states one anywhere; otherwise drop the stars or the rating. Never invent a source, a number or a claim.",
|
|
2120
|
+
"- A native screen's layout (where its headline, bubbles or cards sit, what it shows) is changed in its `native` block.",
|
|
2121
|
+
"- Keep every line of copy word for word: never reword, shorten or drop one. Keep the schema, the brand and everything the defects do not touch.",
|
|
2122
|
+
"- Preserve `concept`, `mechanism`, `message` and `brand` exactly, including the CTA colour. Fix how the ad executes its idea; never change why it works or remove its load-bearing elements to make a defect pass.",
|
|
2123
|
+
"- Keep a native screen on the same `native.surface`. Notes stays Notes, a review stays on its review platform. Change its layout or content only; never replace it with a physical document or a generic ad.",
|
|
2124
|
+
"",
|
|
2125
|
+
'Reply with only this JSON, naming only the fields you replace, each by its dotted path in the spec ("scene.layout", "references", "copy.2.position", "native.messages"), with its whole new value: {"changed": "<one plain sentence a marketer reads: what you changed and why>", "set": [{"path": "<dotted path>", "value": <its new value>}]}',
|
|
2126
|
+
"",
|
|
2127
|
+
"The spec:",
|
|
2128
|
+
spec,
|
|
2129
|
+
// Bench, Oct 2026: without it the model set a field the spec does not have ("native.layout").
|
|
2130
|
+
...schema ? ["", "Its JSON schema: set only fields and values it allows.", JSON.stringify(schema)] : []
|
|
2131
|
+
].join("\n");
|
|
2132
|
+
}
|
|
2133
|
+
var REPAIR_TIMEOUT_MS = 15e4;
|
|
2134
|
+
var SHOWN_WIDTH = 768;
|
|
2135
|
+
async function repairSpec(opts) {
|
|
2136
|
+
const text = await readFile5(opts.promptPath, "utf8").catch(() => null);
|
|
2137
|
+
if (text === null) return null;
|
|
2138
|
+
let before;
|
|
2139
|
+
try {
|
|
2140
|
+
before = JSON.parse(text);
|
|
2141
|
+
} catch {
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
const shown = opts.image ? await bytesOf(opts.image).then(async (bytes) => (await sharp2(bytes).resize({ width: SHOWN_WIDTH }).jpeg({ quality: 85 }).toBuffer()).toString("base64")).catch(() => null) : null;
|
|
2145
|
+
const prompt = buildSpecRepairPrompt(text, opts.findings, adSpecJsonSchema());
|
|
2146
|
+
const repair = acceptRepair(before, await askAboutImages(RENDER_REVIEW_MODEL, shown ? [shown] : [], prompt, opts.onSpend, REPAIR_TIMEOUT_MS));
|
|
2147
|
+
if (!repair) return null;
|
|
2148
|
+
await writeFile4(opts.promptPath, `${JSON.stringify(repair.spec, null, 2)}
|
|
2149
|
+
`, "utf8");
|
|
2150
|
+
return repair.changed;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// src/commands/canvas/run.ts
|
|
2154
|
+
var OPERATOR = "Operator only, agents never need it.";
|
|
2155
|
+
var runCommand = defineCommand({
|
|
2156
|
+
meta: {
|
|
2157
|
+
name: "run",
|
|
2158
|
+
description: "Validate and execute a canvas. Pass the canvas file, or `--slug <slug>` to run a creative by name: its canvas in this workspace, or, when this workspace does not have it (a fresh workspace, another chat's creative), its files restored from its latest recorded run first. A run that was interrupted resumes; a finished one re-renders from the cache."
|
|
2159
|
+
},
|
|
2160
|
+
args: {
|
|
2161
|
+
file: { type: "positional", required: false, description: "Path to canvas JSON (or use --slug)" },
|
|
2162
|
+
slug: {
|
|
2163
|
+
type: "string",
|
|
2164
|
+
description: "Run the creative src/creatives/<slug>/ by name instead of passing its file. Restores its files from run history when they are not in this workspace; local files are never overwritten unless you pass --force-remote."
|
|
2165
|
+
},
|
|
2166
|
+
series: {
|
|
2167
|
+
type: "string",
|
|
2168
|
+
description: "Render every ad of the series this lead ad leads, all at once (`baker creatives make <idea> --series <lead>` made them): each is the lead's ready version with its own words, reviewed and corrected on its own. An ad already ready from the lead's current version is not rendered again. Start here for several messages in one style: baker canvas run --series spring-offer"
|
|
2169
|
+
},
|
|
2170
|
+
"force-remote": {
|
|
2171
|
+
type: "boolean",
|
|
2172
|
+
default: false,
|
|
2173
|
+
description: "With --slug: restore the recorded files even over local ones, replacing those that differ"
|
|
2174
|
+
},
|
|
2175
|
+
until: {
|
|
2176
|
+
type: "string",
|
|
2177
|
+
description: "Stop before the expensive half and leave the run resumable. `frames` settles the pictures and stops on the doorstep of the clips, so the look can be checked for the price of a few images instead of a whole ad. Continue with `baker canvas run <file> --run-id <the same id>` \u2014 everything already done is cached and is not paid for twice."
|
|
2178
|
+
},
|
|
2179
|
+
brief: {
|
|
2180
|
+
type: "string",
|
|
2181
|
+
description: "What the person asked for, IN THEIR OWN WORDS \u2014 paste their message, do not summarise it. The review compares the finished video against this and reports what they asked for that is missing or contradicted. Without it the review can only say whether the video is well made, not whether it is the one they wanted: every other check grades the render against the spec, and a spec that misread the request agrees with itself."
|
|
2182
|
+
},
|
|
2183
|
+
"run-id": { type: "string", description: "Override run id (also resumes that run, re-attaching its in-flight jobs)" },
|
|
2184
|
+
fix: {
|
|
2185
|
+
type: "string",
|
|
2186
|
+
description: `Static ad only: correct a finished version instead of rendering a new one. One render takes that version as its first reference and changes only what you say here ("the CTA sits in the Stories band: move it up", "'Analitica' is missing its accent"), keeping the picture, layout and every other word. It is added on top of that version: its options, pick and earlier corrections are kept as they were made (free), the edit after them. Every size is made again from it (each other size re-laid out from it, one request each) and the review follows as usual. The version is the creative's last render, or --from <run_id>.`
|
|
2187
|
+
},
|
|
2188
|
+
from: {
|
|
2189
|
+
type: "string",
|
|
2190
|
+
description: "With --fix, --use-variant or a --regenerate of sizes only: the run id of the version (default: the one the creative shows, its last render here, or in a new chat the version chosen, else its newest)"
|
|
2191
|
+
},
|
|
2192
|
+
"use-variant": {
|
|
2193
|
+
type: "string",
|
|
2194
|
+
description: "Static ad only: the person asked in the chat for another of a version's variants over the judge's pick. Give its sha256 (the picture's, never the option's number): every size is made again from that picture, the judge's pick replaced. The only requests re-lay out each other size from that picture (one per size). The version is --from <run_id>, else the creative's last render."
|
|
2195
|
+
},
|
|
2196
|
+
autofix: {
|
|
2197
|
+
type: "boolean",
|
|
2198
|
+
default: true,
|
|
2199
|
+
description: `Static ad: when the review finds a blocking defect, the run corrects it itself before it reports (up to ${AUTO_FIX_ROUNDS} more renders: the chosen picture fixed, or only the size at fault laid out again). --no-autofix reports after the first render.`
|
|
2200
|
+
},
|
|
2201
|
+
"skip-viewer-check": {
|
|
2202
|
+
type: "boolean",
|
|
2203
|
+
default: false,
|
|
2204
|
+
description: "Static ad only: render without the viewer check (see `baker canvas viewer-check`). Only when the person has heard what the viewer missed and asked for this copy as it is."
|
|
2205
|
+
},
|
|
2206
|
+
fresh: {
|
|
2207
|
+
type: "boolean",
|
|
2208
|
+
default: false,
|
|
2209
|
+
description: "Ignore any interrupted-run marker and start a new run id instead of resuming"
|
|
2210
|
+
},
|
|
2211
|
+
"cache-policy": { type: "string", description: "read_write | bypass | read_only" },
|
|
2212
|
+
regenerate: {
|
|
2213
|
+
type: "string",
|
|
2214
|
+
description: "Comma-separated node ids or roles to force fresh THIS run (e.g. --regenerate pick,adapt; on a static ad --regenerate static_variants, or its id gen, renders the variants again, --regenerate adapt_9x16 lays out the 9:16 again from the picture the last version shows, its --fix correction or chosen variant included, and --regenerate static_adapt_format every re-laid-out size), bypassing the content cache for just those nodes + everything downstream. For a persistent re-render, bump a node's `regenerate` field in the canvas JSON instead."
|
|
2215
|
+
},
|
|
2216
|
+
concurrency: {
|
|
2217
|
+
type: "string",
|
|
2218
|
+
description: "Max nodes running at once (default 8; env BAKER_CANVAS_CONCURRENCY)"
|
|
2219
|
+
},
|
|
2220
|
+
parallel: {
|
|
2221
|
+
type: "string",
|
|
2222
|
+
description: "Alias for --concurrency: independent clips (and every other same-layer node) already fan out in parallel up to this bound"
|
|
2223
|
+
},
|
|
2224
|
+
// citty consumes any `--no-<flag>` as a negation of `<flag>`, so the
|
|
2225
|
+
// opt-out spelling `--no-record` requires the flag to be named `record`
|
|
2226
|
+
// (a literal "no-record" arg would never receive a value).
|
|
2227
|
+
record: {
|
|
2228
|
+
type: "boolean",
|
|
2229
|
+
default: true,
|
|
2230
|
+
description: "Post the durable run-history record to Baker (disable with --no-record)"
|
|
2231
|
+
},
|
|
2232
|
+
"cache-dir": { type: "string", description: `${OPERATOR} Cache root (default <workspace root>/canvas/.cache)` },
|
|
2233
|
+
"outputs-dir": { type: "string", description: `${OPERATOR} Per-run outputs root (default <workspace root>/canvas)` },
|
|
2234
|
+
"keep-runs": {
|
|
2235
|
+
type: "string",
|
|
2236
|
+
description: `${OPERATOR} After the run, prune old r_* run dirs, keeping the N newest (off by default)`
|
|
2237
|
+
},
|
|
2238
|
+
"remote-cache": {
|
|
2239
|
+
type: "string",
|
|
2240
|
+
description: `${OPERATOR} on | off: company-scoped remote cache + durable asset persistence (default on; env BAKER_CANVAS_REMOTE_CACHE)`
|
|
2241
|
+
}
|
|
2242
|
+
},
|
|
2243
|
+
async run({ args }) {
|
|
2244
|
+
if (args.series) {
|
|
2245
|
+
await executeSeriesRun(String(args.series));
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
await executeCanvasRun({
|
|
2249
|
+
file: args.file ? String(args.file) : void 0,
|
|
2250
|
+
slug: args.slug ? String(args.slug) : void 0,
|
|
2251
|
+
forceRemote: args["force-remote"] === true,
|
|
2252
|
+
cacheDir: args["cache-dir"] ? String(args["cache-dir"]) : void 0,
|
|
2253
|
+
outputsDir: args["outputs-dir"] ? String(args["outputs-dir"]) : void 0,
|
|
2254
|
+
runId: args["run-id"] ? String(args["run-id"]) : void 0,
|
|
2255
|
+
fresh: args.fresh === true,
|
|
2256
|
+
fix: args.fix ? String(args.fix) : void 0,
|
|
2257
|
+
from: args.from ? String(args.from) : void 0,
|
|
2258
|
+
useVariant: args["use-variant"] ? String(args["use-variant"]) : void 0,
|
|
2259
|
+
skipViewerCheck: args["skip-viewer-check"] === true,
|
|
2260
|
+
autoFix: args.autofix !== false,
|
|
2261
|
+
brief: args.brief ? String(args.brief) : void 0,
|
|
2262
|
+
until: args.until ? String(args.until) : void 0,
|
|
2263
|
+
cachePolicy: args["cache-policy"] ? String(args["cache-policy"]) : void 0,
|
|
2264
|
+
regenerate: args.regenerate !== void 0 ? String(args.regenerate) : void 0,
|
|
2265
|
+
// --concurrency wins; --parallel is the discoverable alias for the same bound.
|
|
2266
|
+
concurrency: (args.concurrency ?? args.parallel) !== void 0 ? String(args.concurrency ?? args.parallel) : void 0,
|
|
2267
|
+
keepRuns: args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0,
|
|
2268
|
+
remoteCache: args["remote-cache"] !== void 0 ? String(args["remote-cache"]) !== "off" : void 0,
|
|
2269
|
+
record: args.record !== false
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
function stopBeforeKinds(until) {
|
|
2274
|
+
if (until?.trim() === "frames") return /* @__PURE__ */ new Set(["video_generate"]);
|
|
2275
|
+
return null;
|
|
2276
|
+
}
|
|
2277
|
+
function failPreflight(code, message, fix) {
|
|
2278
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
2279
|
+
`);
|
|
2280
|
+
process.exit(2);
|
|
2281
|
+
}
|
|
2282
|
+
async function canvasFileFor(opts) {
|
|
2283
|
+
if (opts.file) return { canvasPath: path7.resolve(opts.file) };
|
|
2284
|
+
if (!opts.slug) failPreflight("missing_canvas", "Pass the canvas file, or --slug <slug> to run a creative by name.");
|
|
2285
|
+
const cwd = process.cwd();
|
|
2286
|
+
try {
|
|
2287
|
+
return await canvasForSlug(opts.slug, {
|
|
2288
|
+
workspaceRoot: findWorkspaceRoot(cwd) ?? cwd,
|
|
2289
|
+
forceRemote: opts.forceRemote === true,
|
|
2290
|
+
client: buildBackendClient,
|
|
2291
|
+
log: (line) => process.stdout.write(`${line}
|
|
2292
|
+
`)
|
|
2293
|
+
});
|
|
2294
|
+
} catch (e) {
|
|
2295
|
+
if (e instanceof SlugRestoreError) failPreflight(e.code, e.message, e.fix);
|
|
2296
|
+
throw e;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
async function executeSeriesRun(lead) {
|
|
2300
|
+
const root = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
2301
|
+
const members = await seriesMembers(root, lead);
|
|
2302
|
+
if (members.length === 0) {
|
|
2303
|
+
failPreflight(
|
|
2304
|
+
"no_series_ads",
|
|
2305
|
+
`No ad names "${lead}" as its lead (\`series: ${lead}\` in its _definition.md).`,
|
|
2306
|
+
`Make each other message as an ad of the series first (baker creatives make <idea> --series ${lead}), or each language (baker creatives translate --slug ${lead} --to es-MX,de).`
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
const canvasRoot = path7.join(root, "canvas");
|
|
2310
|
+
process.stdout.write(`[series] rendering ${members.length} ads of ${lead} at once: ${members.join(", ")}
|
|
2311
|
+
`);
|
|
2312
|
+
const results = await runSeries(members, {
|
|
2313
|
+
plan: async (slug) => {
|
|
2314
|
+
const made = await writeSeriesMemberCanvas(root, slug, { takeLeadDesign: true });
|
|
2315
|
+
if (made === null) return { refused: { code: "not_in_series", message: `${slug} is no ad of this series.`, fix: "" } };
|
|
2316
|
+
if ("refused" in made) return made;
|
|
2317
|
+
if (made.designFromLead) process.stdout.write(`[series] ${slug} takes the lead's design, its own words kept
|
|
2318
|
+
`);
|
|
2319
|
+
try {
|
|
2320
|
+
const prepared = await prepareCanvas(made.canvasPath, { write: false });
|
|
2321
|
+
const state = await readRenderState(canvasRoot, slug);
|
|
2322
|
+
const fresh = state.rendered?.sourceSha === prepared.sourceSha && (state.blocking ?? []).length === 0;
|
|
2323
|
+
return fresh ? { run: false, runId: state.rendered?.runId } : { run: true };
|
|
2324
|
+
} catch (e) {
|
|
2325
|
+
const envelope = e instanceof CanvasPrepareError ? e.toEnvelope().error : null;
|
|
2326
|
+
return {
|
|
2327
|
+
refused: {
|
|
2328
|
+
code: String(envelope?.code ?? "prepare_failed"),
|
|
2329
|
+
message: String(envelope?.message ?? (e instanceof Error ? e.message : e)),
|
|
2330
|
+
fix: String(envelope?.fix ?? "")
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
},
|
|
2335
|
+
run: (slug) => runBaker(["canvas", "run", "--slug", slug])
|
|
2336
|
+
});
|
|
2337
|
+
process.stdout.write(`${JSON.stringify(seriesEnvelope(lead, results), null, 2)}
|
|
2338
|
+
`);
|
|
2339
|
+
}
|
|
2340
|
+
async function refreshSeriesAd(opts) {
|
|
2341
|
+
const slug = opts.slug ?? (opts.file ? creativeSlugFromCanvasPath(path7.resolve(opts.file)) : null);
|
|
2342
|
+
if (!slug) return false;
|
|
2343
|
+
const root = findWorkspaceRoot(opts.file ? path7.dirname(path7.resolve(opts.file)) : process.cwd());
|
|
2344
|
+
if (!root) return false;
|
|
2345
|
+
const made = await writeSeriesMemberCanvas(root, slug);
|
|
2346
|
+
if (made === null) return false;
|
|
2347
|
+
if ("refused" in made) failPreflight(made.refused.code, made.refused.message, made.refused.fix);
|
|
2348
|
+
process.stdout.write(
|
|
2349
|
+
`[series] ${slug} is the lead ${made.lead}'s version ${made.leadRunId} with this ad's words${made.written ? " (canvas written)" : ""}
|
|
2350
|
+
`
|
|
2351
|
+
);
|
|
2352
|
+
return made.newLeadVersion;
|
|
2353
|
+
}
|
|
2354
|
+
var canvasHeld = false;
|
|
2355
|
+
async function executeCanvasRun(opts) {
|
|
2356
|
+
const startedAt = opts.autoFixStartedAt ?? Date.now();
|
|
2357
|
+
const newAsk = (opts.autoFixRound ?? 0) === 0 ? await refreshSeriesAd(opts) : false;
|
|
2358
|
+
const { canvasPath: filePath, adoptRunId } = await canvasFileFor(opts);
|
|
2359
|
+
const canvasRoot = canvasOutputsRoot(filePath);
|
|
2360
|
+
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
2361
|
+
if (creativeSlug) await clearLegacyDirtyMarker(canvasRoot, creativeSlug);
|
|
2362
|
+
const turnId = optionalTurnId();
|
|
2363
|
+
const turnCache = opts.cacheDir ?? path7.join(canvasRoot, ".cache");
|
|
2364
|
+
const refuseTurnRender = async (correction) => {
|
|
2365
|
+
if (!creativeSlug || (opts.autoFixRound ?? 0) > 0) return;
|
|
2366
|
+
const refusal = turnRenderRefusal(await readTurnRenders(turnCache), turnId, creativeSlug, { correction });
|
|
2367
|
+
if (!refusal) return;
|
|
2368
|
+
process.stderr.write(
|
|
2369
|
+
`${JSON.stringify({ ok: false, error: { code: refusal.code, message: refusal.message, fix: refusal.fix } }, null, 2)}
|
|
2370
|
+
`
|
|
2371
|
+
);
|
|
2372
|
+
process.exit(2);
|
|
2373
|
+
};
|
|
2374
|
+
const shown = creativeSlug ? await shownVersion(canvasRoot, creativeSlug) : {};
|
|
2375
|
+
const promptOnDisk = await readFile6(path7.join(path7.dirname(filePath), "prompt.json"), "utf8").then((text) => JSON.parse(text)).catch(() => null);
|
|
2376
|
+
await refuseTurnRender(mayBeCorrection(opts, shown.prompt, promptOnDisk));
|
|
2377
|
+
const citing = [];
|
|
2378
|
+
await wireProofBadges(
|
|
2379
|
+
path7.join(path7.dirname(filePath), "prompt.json"),
|
|
2380
|
+
fetchBrandLogo,
|
|
2381
|
+
(line) => process.stderr.write(`${line}
|
|
2382
|
+
`),
|
|
2383
|
+
jevCitationJudge((u) => citing.push(u))
|
|
2384
|
+
);
|
|
2385
|
+
await holdUnbilledSpend(turnCache, citing);
|
|
2386
|
+
if ((opts.autoFixRound ?? 0) === 0) {
|
|
2387
|
+
const reading = [];
|
|
2388
|
+
await wireNamedPeople(
|
|
2389
|
+
path7.join(path7.dirname(filePath), "prompt.json"),
|
|
2390
|
+
avatarOfNamedPerson,
|
|
2391
|
+
(line) => process.stderr.write(`${line}
|
|
2392
|
+
`),
|
|
2393
|
+
peopleReader(turnCache, (u) => reading.push(u))
|
|
2394
|
+
);
|
|
2395
|
+
await holdUnbilledSpend(turnCache, reading);
|
|
2396
|
+
}
|
|
2397
|
+
let prepared;
|
|
2398
|
+
try {
|
|
2399
|
+
prepared = await prepareCanvas(filePath, {
|
|
2400
|
+
requireSuppliedAssets: true,
|
|
2401
|
+
gate: true,
|
|
2402
|
+
log: (line) => process.stderr.write(`${line}
|
|
2403
|
+
`)
|
|
2404
|
+
});
|
|
2405
|
+
} catch (e) {
|
|
2406
|
+
if (creativeSlug) await recordRenderAttempt(canvasRoot, creativeSlug, await computeSourceSha(filePath));
|
|
2407
|
+
exitOnPrepareError(e);
|
|
2408
|
+
}
|
|
2409
|
+
if (creativeSlug) await recordRenderAttempt(canvasRoot, creativeSlug, prepared.sourceSha);
|
|
2410
|
+
if (prepared.rewrites.length > 0) {
|
|
2411
|
+
process.stderr.write(
|
|
2412
|
+
`[canvas] rewrote ${prepared.rewrites.length} asset reference(s) in the canvas file so it says what runs (absolute paths \u2192 canvas-relative, a URL in an ingest path \u2192 source "url", a path into canvas/ \u2192 the durable URL of the same bytes):
|
|
2413
|
+
${describeRewrites(prepared.rewrites)}
|
|
2414
|
+
`
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
let parsed = prepared.canvas;
|
|
2418
|
+
const promptNow = isStaticAdCanvas(parsed) ? await readStaticPromptJson(parsed).catch(() => null) : null;
|
|
2419
|
+
const native = isNativeCanvas(parsed);
|
|
2420
|
+
if (native && (opts.fix !== void 0 || opts.useVariant !== void 0)) {
|
|
2421
|
+
process.stderr.write(
|
|
2422
|
+
`${JSON.stringify(
|
|
2423
|
+
{
|
|
2424
|
+
ok: false,
|
|
2425
|
+
error: {
|
|
2426
|
+
code: "nothing_to_fix",
|
|
2427
|
+
message: "This ad is a native screen drawn exactly from prompt.json, not a painted picture: there is nothing to correct by --fix or to pick by --use-variant.",
|
|
2428
|
+
fix: "Edit prompt.json's `native` block (its words, people, photos, frame) and run again: nothing renders but a photo whose description changed. A photo with a defect: `--regenerate photo_<n>`."
|
|
2429
|
+
}
|
|
2430
|
+
},
|
|
2431
|
+
null,
|
|
2432
|
+
2
|
|
2433
|
+
)}
|
|
2434
|
+
`
|
|
2435
|
+
);
|
|
2436
|
+
process.exit(2);
|
|
2437
|
+
}
|
|
2438
|
+
const asEdit = !native && opts.fix === void 0 && opts.useVariant === void 0 && opts.regenerate === void 0 && opts.from === void 0 && shown.rendered ? specEditInstruction(shown.prompt, promptNow) : null;
|
|
2439
|
+
const editOf = asEdit && shown.rendered ? await previousAdImage(canvasRoot, shown.rendered.runId, void 0, rolesById(parsed)) : null;
|
|
2440
|
+
const editInstruction = editOf ? asEdit : null;
|
|
2441
|
+
if (editInstruction) {
|
|
2442
|
+
process.stdout.write(`[edit] only the words or colours changed: editing the version of run ${shown.rendered?.runId}: ${editInstruction}
|
|
2443
|
+
`);
|
|
2444
|
+
}
|
|
2445
|
+
const fixInstruction = opts.fix ?? editInstruction ?? void 0;
|
|
2446
|
+
const requested = opts.regenerate?.split(",").map((id) => id.trim()) ?? [];
|
|
2447
|
+
await refuseTurnRender(
|
|
2448
|
+
fixInstruction !== void 0 || requested.length > 0 && isStaticAdCanvas(parsed) && relaysSizesOnly(parsed, regenerateTargets(parsed, requested).ids)
|
|
2449
|
+
);
|
|
2450
|
+
const fromRun = opts.from ?? shown.rendered?.runId;
|
|
2451
|
+
const memory = fixMemoryOf(shown, opts.from);
|
|
2452
|
+
if (opts.fix !== void 0 && repeatsFailedFix(memory.lastFix, opts.fix)) {
|
|
2453
|
+
const left = memory.lastFix?.blocking ?? [];
|
|
2454
|
+
process.stderr.write(
|
|
2455
|
+
`${JSON.stringify(
|
|
2456
|
+
{
|
|
2457
|
+
ok: false,
|
|
2458
|
+
error: {
|
|
2459
|
+
code: "no_progress",
|
|
2460
|
+
message: `Not rendered: this exact fix already ran and left ${left.length === 1 ? "this" : "these"} in place: ${left.join(" | ")}`,
|
|
2461
|
+
fix: "Do not ask for the same correction again. Change the approach: edit prompt.json so the problem cannot happen (shorter words, a line moved or dropped, a different position), re-lay out just the size at fault with `--regenerate adapt_<size>`, or ask for new options with `--regenerate static_variants`."
|
|
2462
|
+
}
|
|
2463
|
+
},
|
|
2464
|
+
null,
|
|
2465
|
+
2
|
|
2466
|
+
)}
|
|
2467
|
+
`
|
|
2468
|
+
);
|
|
2469
|
+
process.exit(2);
|
|
2470
|
+
}
|
|
2471
|
+
let grown = null;
|
|
2472
|
+
const growOn = async (from, growth) => {
|
|
2473
|
+
const version = await versionRun(canvasRoot, from);
|
|
2474
|
+
return version ? growFromVersion(parsed, version, growth) : null;
|
|
2475
|
+
};
|
|
2476
|
+
if (fixInstruction !== void 0) {
|
|
2477
|
+
const instruction = fixInstruction.trim();
|
|
2478
|
+
const from = opts.from ?? shown.rendered?.runId;
|
|
2479
|
+
const previous = editOf ?? (from ? await previousAdImage(canvasRoot, from, void 0, rolesById(parsed)) : null);
|
|
2480
|
+
if (!instruction || !isStaticAdCanvas(parsed) || !from || !previous) {
|
|
2481
|
+
const message = !instruction ? "--fix needs the correction in words." : !isStaticAdCanvas(parsed) ? "--fix corrects a static ad; this canvas is not one." : !from ? "--fix needs a version to correct: this creative has no recorded render here. Pass --from <run_id>." : `--fix could not find the finished ad of run ${from}: pass --from with a run that rendered one.`;
|
|
2482
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "nothing_to_fix", message } }, null, 2)}
|
|
2483
|
+
`);
|
|
2484
|
+
process.exit(2);
|
|
2485
|
+
}
|
|
2486
|
+
grown = await growOn(from, { kind: "edit", instruction, promptJson: promptNow });
|
|
2487
|
+
parsed = grown ? grown.canvas : fixStaticAd(parsed, { instruction, previous, promptJson: promptNow });
|
|
2488
|
+
const labels = finalOutputLabelsFromCanvas(parsed);
|
|
2489
|
+
const delivered = await previousDelivered(canvasRoot, from, labels?.nodeId ?? "final", labels?.labels ?? [], {
|
|
2490
|
+
prefer: "url"
|
|
2491
|
+
});
|
|
2492
|
+
const last = Object.fromEntries(
|
|
2493
|
+
nodesWithRole(parsed, STATIC_ADAPT_FORMAT_ROLE).flatMap((n) => {
|
|
2494
|
+
const path8 = delivered.find((d) => d.format === formatOfAdaptId(n.id))?.path;
|
|
2495
|
+
return path8 ? [[n.id, path8]] : [];
|
|
2496
|
+
})
|
|
2497
|
+
);
|
|
2498
|
+
parsed = withSizesFollowingFix(parsed, {
|
|
2499
|
+
last,
|
|
2500
|
+
change: instruction.replace(/^Change only this: /, ""),
|
|
2501
|
+
...opts.relayAlso ? { also: opts.relayAlso } : {}
|
|
2502
|
+
});
|
|
2503
|
+
const kept = delivered.filter((d) => opts.keepSizes?.includes(d.format));
|
|
2504
|
+
if (kept.length > 0) {
|
|
2505
|
+
parsed = withDeliveredSizes(parsed, Object.fromEntries(kept.map((d) => [d.format, d.path])));
|
|
2506
|
+
process.stdout.write(`[fix] keeping ${kept.map((d) => d.format).join(", ")} as it was: nothing blocking on it
|
|
2507
|
+
`);
|
|
2508
|
+
}
|
|
2509
|
+
if (!editInstruction) process.stdout.write(`[fix] correcting the version of run ${from}: ${instruction}
|
|
2510
|
+
`);
|
|
2511
|
+
}
|
|
2512
|
+
if (opts.useVariant !== void 0 && opts.fix === void 0) {
|
|
2513
|
+
const sha = opts.useVariant.trim();
|
|
2514
|
+
const from = opts.from ?? shown.rendered?.runId;
|
|
2515
|
+
grown = from && isStaticAdCanvas(parsed) ? await growOn(from, { kind: "variant", sha256: sha }) : null;
|
|
2516
|
+
const url = from && !grown && isStaticAdCanvas(parsed) ? await variantImage(canvasRoot, from, sha, void 0, rolesById(parsed)) : null;
|
|
2517
|
+
if (!from || !grown && !url) {
|
|
2518
|
+
const message = !isStaticAdCanvas(parsed) ? "--use-variant re-makes the sizes of a static ad; this canvas is not one." : !from ? "--use-variant needs the version the variant belongs to: this creative has no recorded render here. Pass --from <run_id>." : `--use-variant found no variant ${sha} in run ${from}: pass the sha256 \`baker creatives feedback --slug <slug>\` prints, and --from with that version's run.`;
|
|
2519
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "no_such_variant", message } }, null, 2)}
|
|
2520
|
+
`);
|
|
2521
|
+
process.exit(2);
|
|
2522
|
+
}
|
|
2523
|
+
if (grown) parsed = grown.canvas;
|
|
2524
|
+
else if (url) parsed = rederiveFromVariant(parsed, { url });
|
|
2525
|
+
process.stdout.write(`[variant] making every size of run ${from} again from the chosen variant ${sha.slice(0, 12)}
|
|
2526
|
+
`);
|
|
2527
|
+
}
|
|
2528
|
+
let relaidFrom;
|
|
2529
|
+
if (opts.regenerate !== void 0 && opts.fix === void 0 && opts.useVariant === void 0 && isStaticAdCanvas(parsed)) {
|
|
2530
|
+
const requested2 = opts.regenerate.split(",").map((id) => id.trim());
|
|
2531
|
+
const from = opts.from ?? shown.rendered?.runId;
|
|
2532
|
+
const sizesOnly = relaysSizesOnly(parsed, regenerateTargets(parsed, requested2).ids);
|
|
2533
|
+
grown = from && sizesOnly ? await growOn(from, { kind: "keep" }) : null;
|
|
2534
|
+
const url = from && sizesOnly && !grown ? await correctedPicture(canvasRoot, from) ?? (shown.picture?.redrawn && shown.picture.runId === from ? await previousAdImage(canvasRoot, from, void 0, rolesById(parsed)) : null) : null;
|
|
2535
|
+
if (from && grown) {
|
|
2536
|
+
parsed = grown.canvas;
|
|
2537
|
+
relaidFrom = from;
|
|
2538
|
+
process.stdout.write(`[regenerate] re-laying out from the picture run ${from} shows, kept as it was made
|
|
2539
|
+
`);
|
|
2540
|
+
} else if (from && url) {
|
|
2541
|
+
parsed = rederiveFromVariant(parsed, { url });
|
|
2542
|
+
relaidFrom = from;
|
|
2543
|
+
process.stdout.write(`[regenerate] re-laying out from the picture run ${from} shows (its correction or the person's choice)
|
|
2544
|
+
`);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
const plain = fixInstruction === void 0 && opts.useVariant === void 0 && opts.regenerate === void 0 && (opts.extendSizes?.length ?? 0) === 0;
|
|
2548
|
+
if (plain && fromRun && !native && isStaticAdCanvas(parsed) && !seriesOf(parsed)) {
|
|
2549
|
+
const version = await versionRun(canvasRoot, fromRun);
|
|
2550
|
+
const same = version && makesSameVariants(parsed, version, await ingestedNow(variantFiles(parsed), path7.dirname(filePath)));
|
|
2551
|
+
grown = version && same ? growFromVersion(parsed, version, { kind: "keep" }) : null;
|
|
2552
|
+
if (grown) {
|
|
2553
|
+
parsed = grown.canvas;
|
|
2554
|
+
relaidFrom = fromRun;
|
|
2555
|
+
process.stdout.write(`[version] nothing the variants are made from changed: keeping the picture of run ${fromRun}
|
|
2556
|
+
`);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
if (grown) {
|
|
2560
|
+
const kept = Object.keys(grown.pinned);
|
|
2561
|
+
process.stdout.write(`[version] built on top of the version before: ${kept.join(", ")} kept as they were made (free)
|
|
2562
|
+
`);
|
|
2563
|
+
}
|
|
2564
|
+
const canvasText = prepared.text;
|
|
2565
|
+
let regenerate;
|
|
2566
|
+
if (opts.regenerate !== void 0) {
|
|
2567
|
+
const requested2 = opts.regenerate.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
|
|
2568
|
+
const known = new Set(canvasNodeIds(parsed));
|
|
2569
|
+
const { ids: resolved, unknown } = regenerateTargets(parsed, requested2);
|
|
2570
|
+
if (unknown.length > 0) {
|
|
2571
|
+
process.stderr.write(
|
|
2572
|
+
`${JSON.stringify(
|
|
2573
|
+
{
|
|
2574
|
+
ok: false,
|
|
2575
|
+
error: {
|
|
2576
|
+
code: "unknown_regenerate_node",
|
|
2577
|
+
message: `--regenerate names node id(s) not in this canvas: ${unknown.join(", ")}. Known ids: ${[...known].join(", ")}`
|
|
2578
|
+
}
|
|
2579
|
+
},
|
|
2580
|
+
null,
|
|
2581
|
+
2
|
|
2582
|
+
)}
|
|
2583
|
+
`
|
|
2584
|
+
);
|
|
2585
|
+
process.exit(2);
|
|
2586
|
+
}
|
|
2587
|
+
if (resolved.length > 0) {
|
|
2588
|
+
regenerate = new Set(resolved);
|
|
2589
|
+
process.stdout.write(`[regenerate] forcing fresh this run: ${[...regenerate].join(", ")} (+ downstream)
|
|
2590
|
+
`);
|
|
2591
|
+
if (opts.relayNote) parsed = withRelayNote(parsed, resolved, opts.relayNote);
|
|
2592
|
+
const editFrom = opts.from ?? shown.rendered?.runId;
|
|
2593
|
+
if (opts.relayEdits && editFrom) {
|
|
2594
|
+
const labels = finalOutputLabelsFromCanvas(parsed);
|
|
2595
|
+
const delivered = await previousDelivered(canvasRoot, editFrom, labels?.nodeId ?? "final", labels?.labels ?? [], {
|
|
2596
|
+
prefer: "url"
|
|
2597
|
+
});
|
|
2598
|
+
for (const [id, instruction] of Object.entries(opts.relayEdits)) {
|
|
2599
|
+
if (!resolved.includes(id)) continue;
|
|
2600
|
+
const last = delivered.find((d) => d.format === formatOfAdaptId(id))?.path;
|
|
2601
|
+
parsed = last ? withRelayEdit(parsed, id, { previous: last, instruction, asked: opts.relayAsked?.[id] ?? [] }) : withRelayNote(parsed, [id], instruction);
|
|
2602
|
+
}
|
|
2603
|
+
const relaid = resolved.flatMap((id) => formatOfAdaptId(id) ?? []);
|
|
2604
|
+
parsed = withDeliveredSizes(
|
|
2605
|
+
parsed,
|
|
2606
|
+
Object.fromEntries(delivered.filter((d) => !relaid.includes(d.format)).map((d) => [d.format, d.path]))
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
if (opts.extendSizes && opts.extendSizes.length > 0 && isStaticAdCanvas(parsed)) {
|
|
2612
|
+
const from = opts.from ?? shown.rendered?.runId;
|
|
2613
|
+
if (opts.extendOnly && from) {
|
|
2614
|
+
const labels = finalOutputLabelsFromCanvas(parsed);
|
|
2615
|
+
const delivered = await previousDelivered(canvasRoot, from, labels?.nodeId ?? "final", labels?.labels ?? [], {
|
|
2616
|
+
prefer: "url"
|
|
2617
|
+
});
|
|
2618
|
+
parsed = withDeliveredSizes(parsed, Object.fromEntries(delivered.map((d) => [d.format, d.path])));
|
|
2619
|
+
}
|
|
2620
|
+
parsed = withExtendedSizes(parsed, opts.extendSizes);
|
|
2621
|
+
process.stdout.write(`[extend] placing ${opts.extendSizes.join(", ")} clear of what covers or cuts it, its margins painted
|
|
2622
|
+
`);
|
|
2623
|
+
}
|
|
2624
|
+
const cacheDir = opts.cacheDir ?? path7.join(canvasRoot, ".cache");
|
|
2625
|
+
const engine = createEngineFromEnv({
|
|
2626
|
+
cacheDir,
|
|
2627
|
+
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
2628
|
+
log: (line) => process.stdout.write(`${line}
|
|
2629
|
+
`),
|
|
2630
|
+
remoteCache: opts.remoteCache
|
|
2631
|
+
});
|
|
2632
|
+
const validation = await engine.validateDeep(parsed);
|
|
2633
|
+
if (!validation.ok) {
|
|
2634
|
+
process.stderr.write(
|
|
2635
|
+
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
2636
|
+
`
|
|
2637
|
+
);
|
|
2638
|
+
process.exit(2);
|
|
2639
|
+
}
|
|
2640
|
+
const advisories = (validation.warnings ?? []).filter((w) => w.code !== "ORPHAN_NODE");
|
|
2641
|
+
for (const w of [...advisories, ...await outputRatioWarningsFor(parsed, filePath)]) {
|
|
2642
|
+
process.stdout.write(`[warn] ${w.code}: ${w.message}
|
|
2643
|
+
`);
|
|
2644
|
+
}
|
|
2645
|
+
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
2646
|
+
const sourceSha = prepared.sourceSha;
|
|
2647
|
+
const client = opts.record === false ? null : buildBackendClient();
|
|
2648
|
+
const outputsDir = opts.outputsDir ? path7.resolve(opts.outputsDir) : canvasRoot;
|
|
2649
|
+
const { runId, resumed, source, sameSource, concurrentRunId, rendering } = await resolveRunId({
|
|
2650
|
+
explicitRunId: opts.runId,
|
|
2651
|
+
adoptedRunId: adoptRunId,
|
|
2652
|
+
fresh: opts.fresh === true || fixInstruction !== void 0 || opts.useVariant !== void 0 || relaidFrom !== void 0,
|
|
2653
|
+
outputsDir,
|
|
2654
|
+
canvasPath: filePath,
|
|
2655
|
+
sourceSha,
|
|
2656
|
+
remote: client && creativeSlug && sourceSha ? { creativeSlug, sourceSha, getActiveRun: (slug, sha) => client.getActiveRun(slug, sha) } : void 0
|
|
2657
|
+
});
|
|
2658
|
+
const holder = rendering ?? await canvasBusyElsewhere(outputsDir, filePath);
|
|
2659
|
+
if (holder) {
|
|
2660
|
+
process.stderr.write(
|
|
2661
|
+
`${JSON.stringify(
|
|
2662
|
+
{
|
|
2663
|
+
ok: false,
|
|
2664
|
+
error: {
|
|
2665
|
+
code: "CONFLICT",
|
|
2666
|
+
message: `This canvas is already rendering in another process (run ${holder.runId}, pid ${holder.pid}).`,
|
|
2667
|
+
fix: "Wait for that render to finish and use its result instead of starting a second one: two renders of one canvas share a run and overwrite each other. If you started it in the background, read its output when it completes."
|
|
2668
|
+
}
|
|
2669
|
+
},
|
|
2670
|
+
null,
|
|
2671
|
+
2
|
|
2672
|
+
)}
|
|
2673
|
+
`
|
|
2674
|
+
);
|
|
2675
|
+
process.exit(2);
|
|
2676
|
+
}
|
|
2677
|
+
const viewerUsage = [];
|
|
2678
|
+
const viewerCheck = creativeSlug && isStaticAdCanvas(parsed) ? await viewerCheckBeforeRender({
|
|
2679
|
+
creativeDir: path7.dirname(filePath),
|
|
2680
|
+
cacheDir,
|
|
2681
|
+
slug: creativeSlug,
|
|
2682
|
+
skip: viewerCheckSkip({
|
|
2683
|
+
fix: opts.fix !== void 0,
|
|
2684
|
+
useVariant: opts.useVariant !== void 0,
|
|
2685
|
+
relaid: relaidFrom !== void 0,
|
|
2686
|
+
autoFixRound: opts.autoFixRound ?? 0,
|
|
2687
|
+
resumed: resumed && sameSource === true,
|
|
2688
|
+
asked: opts.skipViewerCheck === true
|
|
2689
|
+
}),
|
|
2690
|
+
spent: viewerChecksSpent(await readTurnRenders(turnCache), turnId, creativeSlug),
|
|
2691
|
+
lookups: liveViewerLookups(path7.dirname(canvasRoot)),
|
|
2692
|
+
model: VIEWER_CHECK_MODEL,
|
|
2693
|
+
ask: gatewayViewerModel(VIEWER_CHECK_MODEL, (usage) => viewerUsage.push(usage)),
|
|
2694
|
+
judge: jevReadJudge((usage) => viewerUsage.push(usage))
|
|
2695
|
+
}) : null;
|
|
2696
|
+
for (const line of viewerCheck?.lines ?? []) process.stdout.write(`${line}
|
|
2697
|
+
`);
|
|
2698
|
+
await holdUnbilledSpend(cacheDir, viewerUsage);
|
|
2699
|
+
if (viewerCheck?.failure) {
|
|
2700
|
+
if (creativeSlug && turnId && viewerCheck.failure.error.code === "viewer_check_failed") {
|
|
2701
|
+
await updateTurnRenders(turnCache, (ledger) => recordViewerFail(ledger, turnId, creativeSlug));
|
|
2702
|
+
}
|
|
2703
|
+
process.stderr.write(`${JSON.stringify(viewerCheck.failure, null, 2)}
|
|
2704
|
+
`);
|
|
2705
|
+
process.exit(2);
|
|
2706
|
+
}
|
|
2707
|
+
if (resumed) {
|
|
2708
|
+
process.stdout.write(
|
|
2709
|
+
`[resume] continuing interrupted run ${runId}${source === "remote" ? " (adopted from run history)" : ""} \u2014 in-flight jobs re-attach, cached nodes skip
|
|
2710
|
+
`
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2713
|
+
if (concurrentRunId) {
|
|
2714
|
+
process.stderr.write(
|
|
2715
|
+
`[concurrent] this canvas appears to be running elsewhere right now (run ${concurrentRunId}) \u2014 starting a separate run; pass --run-id ${concurrentRunId} to adopt it instead
|
|
2716
|
+
`
|
|
2717
|
+
);
|
|
2718
|
+
}
|
|
2719
|
+
const abort = new AbortController();
|
|
2720
|
+
let abortedBy = null;
|
|
2721
|
+
for (const signalName of ["SIGINT", "SIGTERM"]) {
|
|
2722
|
+
process.once(signalName, () => {
|
|
2723
|
+
abortedBy = signalName;
|
|
2724
|
+
process.stderr.write(`[interrupt] ${signalName} received \u2014 flushing a resumable snapshot (repeat to force-quit)
|
|
2725
|
+
`);
|
|
2726
|
+
abort.abort(new Error(`aborted by ${signalName}`));
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
const inFlight = await markRunInFlight(outputsDir, filePath, runId, sourceSha);
|
|
2730
|
+
if (!canvasHeld) {
|
|
2731
|
+
const held = await markCanvasBusy(outputsDir, filePath, runId);
|
|
2732
|
+
canvasHeld = true;
|
|
2733
|
+
process.once("exit", held.release);
|
|
2734
|
+
}
|
|
2735
|
+
const clearMarker = () => {
|
|
2736
|
+
inFlight.stop();
|
|
2737
|
+
return clearRunMarker(outputsDir, filePath, inFlight.token);
|
|
2738
|
+
};
|
|
2739
|
+
const snapshot = client && creativeSlug ? await uploadRunSnapshot(client, {
|
|
2740
|
+
canvasPath: filePath,
|
|
2741
|
+
raw: canvasText,
|
|
2742
|
+
creativeSlug,
|
|
2743
|
+
parsed,
|
|
2744
|
+
previous: (await readRenderState(canvasRoot, creativeSlug)).snapshot
|
|
2745
|
+
}) : null;
|
|
2746
|
+
if (snapshot && creativeSlug) await recordSnapshot(canvasRoot, creativeSlug, snapshot);
|
|
2747
|
+
const graphUrl = client && creativeSlug && isStaticAdCanvas(parsed) ? await uploadRunGraph(client, parsed) : null;
|
|
2748
|
+
const recordMeta = {
|
|
2749
|
+
creativeSlug,
|
|
2750
|
+
canvasPath: path7.relative(path7.dirname(canvasRoot), filePath) || void 0,
|
|
2751
|
+
canvasSha,
|
|
2752
|
+
// The fingerprint the dashboard compares against the current source to flag
|
|
2753
|
+
// "edited since last render": the same `computeSourceSha` the edit-sync posts,
|
|
2754
|
+
// taken after the preflight wrote the projected graph, so the two are comparable.
|
|
2755
|
+
sourceSha,
|
|
2756
|
+
chatId: optionalChatId(),
|
|
2757
|
+
canvasSnapshotUrl: snapshot?.url,
|
|
2758
|
+
...graphUrl ? { graphUrl } : {},
|
|
2759
|
+
...newAsk ? { newAsk: true } : {}
|
|
2760
|
+
};
|
|
2761
|
+
if (client) {
|
|
2762
|
+
const registered = await postInitialRunRecord(client, buildInitialRunRecord(runId, recordMeta));
|
|
2763
|
+
if (!registered) {
|
|
2764
|
+
process.stderr.write(
|
|
2765
|
+
"[warn] run start not registered \u2014 this run stays invisible to the dashboard until a later progress post lands, and cannot be resumed from another workspace\n"
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
const leadRun = seriesOf(parsed)?.run_id;
|
|
2770
|
+
if (creativeSlug && leadRun) await recordSeriesLeadRun(canvasRoot, creativeSlug, leadRun);
|
|
2771
|
+
const record = client ? (payload) => client.recordRun(payload) : null;
|
|
2772
|
+
const finalLabels = finalOutputLabelsFromCanvas(parsed) ?? void 0;
|
|
2773
|
+
const progress = record ? new RunProgressTracker(runId, recordMeta, finalLabels) : null;
|
|
2774
|
+
const poster = record ? new RunRecordPoster(record) : null;
|
|
2775
|
+
const flushBilled = async (p, payload) => {
|
|
2776
|
+
const failure = await sendBillingSpend(cacheDir, payload.reviewUsage ?? [], () => p.flush(payload));
|
|
2777
|
+
if (failure !== null) process.stderr.write(`[warn] run record not persisted (${failure})
|
|
2778
|
+
`);
|
|
2779
|
+
};
|
|
2780
|
+
if (progress && poster) {
|
|
2781
|
+
poster.startKeepalive(() => progress.hasPlan() ? progress.snapshot() : null);
|
|
2782
|
+
}
|
|
2783
|
+
const frameUrls = [];
|
|
2784
|
+
let next = null;
|
|
2785
|
+
const madeHere = grown ? madeThisRun({ canvas: parsed, pinned: grown.pinned }) : parsed;
|
|
2786
|
+
const readerSpend = holdWhenSettled(cacheDir, (run, outputs) => prefilterSpend(madeHere, { [run.node_id]: outputs }, [run]));
|
|
2787
|
+
const isFrameNode = (id) => parsed.nodes?.find((n) => n.id === id)?.type === "image_generate";
|
|
2788
|
+
try {
|
|
2789
|
+
const policy = opts.cachePolicy ?? "read_write";
|
|
2790
|
+
const result = await engine.run(parsed, {
|
|
2791
|
+
run_id: runId,
|
|
2792
|
+
signal: abort.signal,
|
|
2793
|
+
cache_policy: policy,
|
|
2794
|
+
...stopBeforeKinds(opts.until) ? { stop_before_kinds: stopBeforeKinds(opts.until) } : {},
|
|
2795
|
+
concurrency: resolveConcurrency(opts.concurrency, process.env.BAKER_CANVAS_CONCURRENCY),
|
|
2796
|
+
regenerate,
|
|
2797
|
+
...grown ? { pinned: grown.pinned } : {},
|
|
2798
|
+
// Always set, because the frame collector has to run whether or not this run is
|
|
2799
|
+
// reporting progress to a backend — without it a checkpoint stops with nothing to
|
|
2800
|
+
// show, which is the only thing a checkpoint is for.
|
|
2801
|
+
onProgress: (event) => {
|
|
2802
|
+
if (event.kind === "node_settled") readerSpend.settled(event.run, event.outputs);
|
|
2803
|
+
if (event.kind === "node_settled" && isFrameNode(event.run.node_id)) {
|
|
2804
|
+
for (const out of Object.values(event.outputs ?? {})) {
|
|
2805
|
+
const url = out?.url;
|
|
2806
|
+
if (url) frameUrls.push(url);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
if (progress && poster) {
|
|
2810
|
+
progress.apply(event);
|
|
2811
|
+
if (progress.hasPlan()) poster.enqueue(progress.snapshot());
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2815
|
+
await clearMarker();
|
|
2816
|
+
const beforeRunId = shown.rendered?.runId;
|
|
2817
|
+
if (creativeSlug) {
|
|
2818
|
+
await recordRendered(canvasRoot, creativeSlug, sourceSha, result.run_id, {
|
|
2819
|
+
...promptNow ? { prompt: promptNow } : {},
|
|
2820
|
+
...seriesOf(parsed)?.run_id ? { seriesLeadRun: seriesOf(parsed)?.run_id } : {},
|
|
2821
|
+
picture: pictureRecord(result.run_id, {
|
|
2822
|
+
chose: opts.useVariant !== void 0,
|
|
2823
|
+
of: shown.picture,
|
|
2824
|
+
from: fromRun,
|
|
2825
|
+
keeps: fixInstruction !== void 0 || relaidFrom !== void 0,
|
|
2826
|
+
redrawn: opts.autoFixKind === "regenerate"
|
|
2827
|
+
})
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
const keepRuns = opts.keepRuns;
|
|
2831
|
+
if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
|
|
2832
|
+
await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
|
|
2833
|
+
`));
|
|
2834
|
+
}
|
|
2835
|
+
const reviewable = videoPathFromOutput(result.output);
|
|
2836
|
+
await readerSpend.drained();
|
|
2837
|
+
const reviewUsage = poster ? await takeUnbilledSpend(cacheDir) : [];
|
|
2838
|
+
const review = reviewable ? await reviewRenderedVideo({
|
|
2839
|
+
video: reviewable,
|
|
2840
|
+
windows: await sceneWindowsBeside(filePath),
|
|
2841
|
+
...await reviewContextBeside(filePath),
|
|
2842
|
+
brief: opts.brief?.trim() || void 0,
|
|
2843
|
+
onSpend: (usage) => reviewUsage.push(usage)
|
|
2844
|
+
}) : null;
|
|
2845
|
+
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(path7.dirname(filePath)) ?? process.cwd());
|
|
2846
|
+
const runShape = {
|
|
2847
|
+
fix: fixInstruction !== void 0,
|
|
2848
|
+
useVariant: opts.useVariant !== void 0,
|
|
2849
|
+
sizesOnly: relaidFrom !== void 0 || opts.extendOnly === true || regenerate !== void 0 && relaysSizesOnly(parsed, [...regenerate])
|
|
2850
|
+
};
|
|
2851
|
+
const staticReview = reviewable ? null : await reviewStaticRun({
|
|
2852
|
+
parsed: madeHere,
|
|
2853
|
+
output: result.output,
|
|
2854
|
+
outputsByNode: result.outputs_by_node,
|
|
2855
|
+
onSpend: (usage) => reviewUsage.push(usage),
|
|
2856
|
+
viewer: viewerCheck?.viewer ?? null,
|
|
2857
|
+
...brandKit?.name ? { brandName: brandKit.name } : {},
|
|
2858
|
+
...brandKit?.lockup ? { lockup: brandKit.lockup } : {},
|
|
2859
|
+
// Only a new picture is compared with the last one: an edit, a fix, a chosen option or a
|
|
2860
|
+
// re-layout keeps it on purpose, and would read as "this retry changed nothing".
|
|
2861
|
+
...beforeRunId && beforeRunId !== result.run_id && rendersNewPicture(runShape) ? { previous: await previousDelivered(canvasRoot, beforeRunId, finalLabels?.nodeId ?? "final", finalLabels?.labels ?? []) } : {},
|
|
2862
|
+
// A size this round did not touch keeps the last round's verdict: the review's second look at it is no defect of the round.
|
|
2863
|
+
...opts.settledReview && opts.from ? {
|
|
2864
|
+
settled: {
|
|
2865
|
+
review: opts.settledReview,
|
|
2866
|
+
images: await previousDelivered(canvasRoot, opts.from, finalLabels?.nodeId ?? "final", finalLabels?.labels ?? [])
|
|
2867
|
+
}
|
|
2868
|
+
} : {}
|
|
2869
|
+
});
|
|
2870
|
+
const blockingNow = (staticReview?.review ?? []).filter((f) => f.severity === "blocking").map((f) => f.what);
|
|
2871
|
+
const stillThere = fixInstruction !== void 0 && staticReview?.review ? persistingDefects(memory.blocking, staticReview.review) : [];
|
|
2872
|
+
if (creativeSlug && staticReview) {
|
|
2873
|
+
await recordReviewOutcome(canvasRoot, creativeSlug, {
|
|
2874
|
+
blocking: blockingNow,
|
|
2875
|
+
...fixInstruction !== void 0 ? { lastFix: { instruction: fixInstruction.trim(), blocking: stillThere } } : {}
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
2878
|
+
const costHint = staticReview ? runCostHint(result.stats, paidFor(runShape)) : null;
|
|
2879
|
+
const corrected = opts.autoFixed ?? [];
|
|
2880
|
+
const seriesRoot = creativeSlug && staticReview ? findWorkspaceRoot(path7.dirname(filePath)) : null;
|
|
2881
|
+
const seriesHints = seriesRoot && creativeSlug ? seriesRunHints(creativeSlug, {
|
|
2882
|
+
leads: await seriesLeadsAbove(seriesRoot, creativeSlug),
|
|
2883
|
+
members: await seriesMembers(seriesRoot, creativeSlug)
|
|
2884
|
+
}) : [];
|
|
2885
|
+
const hints = staticReview ? [
|
|
2886
|
+
...corrected.length > 0 ? [
|
|
2887
|
+
`Corrected automatically before this result, ${corrected.length} more render${corrected.length === 1 ? "" : "s"}: ${corrected.join(" Then ")} What follows is the review of the corrected version.`
|
|
2888
|
+
] : [],
|
|
2889
|
+
...staticReview.hints,
|
|
2890
|
+
...costHint ? [costHint] : [],
|
|
2891
|
+
...seriesHints,
|
|
2892
|
+
...stillThere.length > 0 ? [
|
|
2893
|
+
`The fix did not remove ${stillThere.length === 1 ? "this" : "these"}: ${stillThere.join(" | ")}. The same instruction will not run again; change the approach (edit prompt.json so it cannot happen, or re-lay out only that size).`
|
|
2894
|
+
] : []
|
|
2895
|
+
] : reviewable ? review === null ? [RENDER_REVIEW_UNAVAILABLE] : review.length === 0 ? ["Reviewed the finished video frame by frame \u2014 nothing to report."] : [
|
|
2896
|
+
`Reviewed the finished video and found ${review.length} thing${review.length === 1 ? "" : "s"} to fix. Show the user what is wrong before you show them the ad.`,
|
|
2897
|
+
...review.map((f) => `${f.severity === "blocking" ? "Must fix" : "Should fix"} \u2014 ${f.what}`),
|
|
2898
|
+
...review.some((f) => f.scene) ? [
|
|
2899
|
+
`Anything about the mark or the closing card is a composition fix, not a re-render: both are drawn as HTML by the caption composition, so editing it and re-running re-renders only that pass \u2014 the clips are cached and no generation is paid for again. Fix them, then re-run: edit the \`show\` of the scene files named above \u2014 a shot with a person handling equipment is where limbs and objects go wrong, so widen it, move the hands out of frame, or drop the person from that beat. Re-running re-renders only the scenes you edited; everything else comes from the cache (this run: ${result.stats?.cached_nodes ?? 0} of ${result.stats?.total_nodes ?? 0} nodes were cached).`
|
|
2900
|
+
] : []
|
|
2901
|
+
] : [];
|
|
2902
|
+
const findings = staticReview ? staticReview.review : review;
|
|
2903
|
+
const ready = staticReview ? staticReview.verdict === "ready" || staticReview.verdict === "ready_with_notes" : review !== null && !review.some((f) => f.severity === "blocking");
|
|
2904
|
+
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(path7.join(path7.dirname(filePath), "_definition.md"), "rendered") : null;
|
|
2905
|
+
if (poster) {
|
|
2906
|
+
await flushBilled(
|
|
2907
|
+
poster,
|
|
2908
|
+
// `review` is null only when nobody looked (no key, no ffmpeg, unreadable video);
|
|
2909
|
+
// an empty array is a render that was reviewed and came back clean. The record
|
|
2910
|
+
// has to keep those apart.
|
|
2911
|
+
buildRunRecord(
|
|
2912
|
+
result,
|
|
2913
|
+
{
|
|
2914
|
+
...recordMeta,
|
|
2915
|
+
...findings ? { review: findings } : {},
|
|
2916
|
+
...reviewUsage.length > 0 ? { reviewUsage } : {},
|
|
2917
|
+
// A static ad that failed a check is never shown as a version (the backend reads both).
|
|
2918
|
+
...staticReview ? { verdict: staticReview.verdict, presentable: staticReview.verdict !== "needs_fixes" } : {}
|
|
2919
|
+
},
|
|
2920
|
+
progress?.planInfo(),
|
|
2921
|
+
finalLabels
|
|
2922
|
+
)
|
|
2923
|
+
);
|
|
2924
|
+
}
|
|
2925
|
+
const round = opts.autoFixRound ?? 0;
|
|
2926
|
+
const keepPicture = opts.useVariant !== void 0 || opts.autoFixKeepPicture === true || (fixInstruction !== void 0 || relaidFrom !== void 0) && keepsChosen(shown.picture, fromRun);
|
|
2927
|
+
const attempts = staticReview?.review ? [...opts.autoFixAttempts ?? [], { runId: result.run_id, review: staticReview.review }] : [];
|
|
2928
|
+
const base = attempts.length > 0 ? correctFrom(attempts) : null;
|
|
2929
|
+
const reverted = base !== null && base.runId !== result.run_id;
|
|
2930
|
+
let step = staticReview && base && opts.autoFix !== false ? (
|
|
2931
|
+
// The graph the next round re-prepares (the file's), never this run's rewrite of it: a fix or a
|
|
2932
|
+
// re-layout takes the variants out of `parsed`, and only the file's graph can draw a new picture.
|
|
2933
|
+
autoFixStep(prepared.canvas, base.review, {
|
|
2934
|
+
round,
|
|
2935
|
+
keepPicture,
|
|
2936
|
+
// Gone back to an earlier attempt: what this round did and left is no longer the picture's.
|
|
2937
|
+
...!reverted && opts.autoFixKind ? { previous: opts.autoFixKind } : {},
|
|
2938
|
+
...!reverted && fixInstruction !== void 0 ? { lastFix: { instruction: fixInstruction.trim(), blocking: stillThere } } : {},
|
|
2939
|
+
tooFar: reverted ? [] : placedTooFar(parsed, result.outputs_by_node),
|
|
2940
|
+
fixedBefore: base.fixedBefore
|
|
2941
|
+
})
|
|
2942
|
+
) : null;
|
|
2943
|
+
if (step && reverted && base) {
|
|
2944
|
+
process.stdout.write(
|
|
2945
|
+
`[autofix] run ${result.run_id} came out worse than run ${base.runId}: the next correction starts from run ${base.runId}
|
|
2946
|
+
`
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2949
|
+
const outOfTime = step !== null && !roundFitsTurn(await readTurnRenders(turnCache), turnId, creativeSlug, Date.now() - startedAt, step.kind);
|
|
2950
|
+
if (outOfTime) {
|
|
2951
|
+
process.stdout.write(
|
|
2952
|
+
`[autofix] not started: this ad has had its time in this turn (${Math.round((Date.now() - startedAt) / 6e4)} min in this render). It stands as it is.
|
|
2953
|
+
`
|
|
2954
|
+
);
|
|
2955
|
+
hints.unshift(
|
|
2956
|
+
"Out of time for this ad in this turn: no further correction or render. Show this version as it stands and say in one plain sentence what is left; the person's next message says what changes."
|
|
2957
|
+
);
|
|
2958
|
+
step = null;
|
|
2959
|
+
}
|
|
2960
|
+
if (!step && reverted && base) {
|
|
2961
|
+
hints.unshift(
|
|
2962
|
+
`Run ${base.runId} has fewer problems than this one: correct that one, never this one (add \`--from ${base.runId}\` to a --fix).`
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
let respecced = null;
|
|
2966
|
+
const briefFindings = step?.kind === "respec" ? step.findings : step?.kind === "regenerate" ? step.brief ?? [] : [];
|
|
2967
|
+
if (briefFindings.length > 0) {
|
|
2968
|
+
const target = staticReviewTargets(parsed, result.output);
|
|
2969
|
+
const spent = [];
|
|
2970
|
+
respecced = target ? await repairSpec({
|
|
2971
|
+
promptPath: target.promptPath,
|
|
2972
|
+
findings: briefFindings,
|
|
2973
|
+
...target.images[0] ? { image: target.images[0].path } : {},
|
|
2974
|
+
onSpend: (usage) => spent.push(usage)
|
|
2975
|
+
}) : null;
|
|
2976
|
+
if (spent.length > 0) await holdUnbilledSpend(cacheDir, spent);
|
|
2977
|
+
if (step?.kind === "respec" && respecced === null) step = null;
|
|
2978
|
+
}
|
|
2979
|
+
if (step) {
|
|
2980
|
+
const did = step.kind === "respec" ? `The spec was corrected: ${respecced}` : step.kind === "fix" ? `The picture was corrected for: ${step.instruction.replace(/^[^:]*: /, "")}` : step.kind === "regenerate" ? `${respecced ? `The spec was corrected (${respecced}) and a` : "A"} new picture was made, avoiding: ${step.note.replace(/^[^:]*: /, "")}` : step.kind === "extend" ? `The ${step.sizes.join(" and ")} ${step.sizes.length === 1 ? "was" : "were"} placed clear of what covers or cuts ${step.sizes.length === 1 ? "it" : "them"}, the margins painted to match.` : `The ${step.ids.map((id) => id.replace(/^adapt_/, "").replace("x", ":")).join(" and ")} was edited: ${Object.values(step.edits).join(" ")}`;
|
|
2981
|
+
const files = finalFiles(result.output, finalLabels?.labels);
|
|
2982
|
+
process.stdout.write(
|
|
2983
|
+
`[autofix] round ${round + 1} of ${AUTO_FIX_ROUNDS}: run ${result.run_id} needs fixes. ${did} If the correction fails, run ${result.run_id} stands as the latest version: ${JSON.stringify(files)}
|
|
2984
|
+
`
|
|
2985
|
+
);
|
|
2986
|
+
next = {
|
|
2987
|
+
file: filePath,
|
|
2988
|
+
cacheDir: opts.cacheDir,
|
|
2989
|
+
outputsDir: opts.outputsDir,
|
|
2990
|
+
concurrency: opts.concurrency,
|
|
2991
|
+
keepRuns: opts.keepRuns,
|
|
2992
|
+
remoteCache: opts.remoteCache,
|
|
2993
|
+
record: opts.record,
|
|
2994
|
+
brief: opts.brief,
|
|
2995
|
+
cachePolicy: opts.cachePolicy,
|
|
2996
|
+
fresh: true,
|
|
2997
|
+
from: base?.runId ?? result.run_id,
|
|
2998
|
+
autoFixRound: round + 1,
|
|
2999
|
+
autoFixAttempts: attempts,
|
|
3000
|
+
autoFixStartedAt: startedAt,
|
|
3001
|
+
autoFixed: [...corrected, did],
|
|
3002
|
+
autoFixKind: step.kind,
|
|
3003
|
+
...base ? { settledReview: [...base.review] } : {},
|
|
3004
|
+
...keepPicture ? { autoFixKeepPicture: true } : {},
|
|
3005
|
+
...step.kind === "fix" ? {
|
|
3006
|
+
fix: step.instruction,
|
|
3007
|
+
...step.relay ? { relayAlso: step.relay } : {},
|
|
3008
|
+
...step.keep ? { keepSizes: step.keep } : {},
|
|
3009
|
+
...step.extend ? { extendSizes: step.extend } : {}
|
|
3010
|
+
} : step.kind === "extend" ? { extendSizes: step.sizes, extendOnly: true } : step.kind === "relayout" ? {
|
|
3011
|
+
...step.extend ? { extendSizes: step.extend } : {},
|
|
3012
|
+
regenerate: step.ids.join(","),
|
|
3013
|
+
relayEdits: step.edits,
|
|
3014
|
+
// A second edit of a size is told what the first asked for.
|
|
3015
|
+
...opts.autoFixKind === "relayout" && opts.relayEdits ? { relayAsked: Object.fromEntries(Object.entries(opts.relayEdits).map(([id, asked]) => [id, [asked]])) } : {}
|
|
3016
|
+
} : step.kind === "regenerate" ? { regenerate: step.ids.join(","), relayNote: step.note, ...step.extend ? { extendSizes: step.extend } : {} } : {}
|
|
3017
|
+
};
|
|
3018
|
+
} else {
|
|
3019
|
+
if (creativeSlug && turnId && staticReview) {
|
|
3020
|
+
const tookMs = Date.now() - startedAt;
|
|
3021
|
+
await updateTurnRenders(
|
|
3022
|
+
turnCache,
|
|
3023
|
+
(ledger) => recordTurnRender(ledger, turnId, creativeSlug, staticReview.verdict, tookMs)
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
process.stdout.write(
|
|
3027
|
+
`${JSON.stringify(
|
|
3028
|
+
{
|
|
3029
|
+
ok: true,
|
|
3030
|
+
outputs_dir: result.outputs_dir,
|
|
3031
|
+
output: result.output,
|
|
3032
|
+
stats: result.stats,
|
|
3033
|
+
// The agent reads a finding's words; the review model's typed fields are the correction's.
|
|
3034
|
+
...findings ? { review: findings.map(({ kind: _kind, ...finding }) => finding) } : {},
|
|
3035
|
+
...staticReview?.judge?.length ? { judge: staticReview.judge } : {},
|
|
3036
|
+
...definitionStatus === "updated" ? { definition_status: "rendered" } : {},
|
|
3037
|
+
...hints.length ? { hints } : {},
|
|
3038
|
+
// Last, so a `| tail` keeps them: production agents cut them off and re-ran to get them back.
|
|
3039
|
+
// A static ad whose review found a blocking problem is finished but NOT ready.
|
|
3040
|
+
...staticReview ? { verdict: staticReview.verdict } : {},
|
|
3041
|
+
run_id: result.run_id,
|
|
3042
|
+
files: finalFiles(result.output, finalLabels?.labels)
|
|
3043
|
+
},
|
|
3044
|
+
null,
|
|
3045
|
+
2
|
|
3046
|
+
)}
|
|
3047
|
+
`
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
} catch (e) {
|
|
3051
|
+
const failedPayload = async (message) => {
|
|
3052
|
+
await readerSpend.drained();
|
|
3053
|
+
const payload = progress?.hasPlan() ? progress.failedSnapshot(message) : buildFailedRunRecord(runId, message, recordMeta);
|
|
3054
|
+
const held = await takeUnbilledSpend(cacheDir);
|
|
3055
|
+
return held.length > 0 ? { ...payload, reviewUsage: held } : payload;
|
|
3056
|
+
};
|
|
3057
|
+
if (e instanceof RunAbortedError && e.reason === "signal") {
|
|
3058
|
+
const message = `Interrupted${abortedBy ? ` by ${abortedBy}` : ""} \u2014 re-run \`baker canvas run\` to resume; in-flight jobs re-attach, completed nodes come from the cache.`;
|
|
3059
|
+
if (poster) await flushBilled(poster, { ...await failedPayload(message), interrupted: true });
|
|
3060
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "interrupted", message } }, null, 2)}
|
|
3061
|
+
`);
|
|
3062
|
+
process.exit(abortedBy === "SIGTERM" ? 143 : 130);
|
|
3063
|
+
}
|
|
3064
|
+
if (e instanceof RunAbortedError && e.reason === "checkpoint") {
|
|
3065
|
+
await clearMarker();
|
|
3066
|
+
if (poster) {
|
|
3067
|
+
await flushBilled(poster, {
|
|
3068
|
+
...await failedPayload(`Frames ready to review before the clips. Resume with --run-id ${runId}.`),
|
|
3069
|
+
status: "awaiting_approval"
|
|
3070
|
+
});
|
|
3071
|
+
}
|
|
3072
|
+
const frames = frameUrls;
|
|
3073
|
+
process.stdout.write(
|
|
3074
|
+
`${JSON.stringify(
|
|
3075
|
+
{
|
|
3076
|
+
ok: true,
|
|
3077
|
+
data: { stopped: "frames", runId, frames },
|
|
3078
|
+
hints: [
|
|
3079
|
+
`${e.message}`,
|
|
3080
|
+
frames.length > 0 ? `Review these ${frames.length} frames against the brief and Brand, fix any defects, then resume immediately with the same run id. Wait for the user only when they explicitly requested storyboards only or a review before continuing; do not ask them to choose a version.` : "No frame outputs were captured \u2014 carry on rather than blocking on nothing.",
|
|
3081
|
+
`Carry on with: baker canvas run ${filePath} --run-id ${runId}`
|
|
3082
|
+
]
|
|
3083
|
+
},
|
|
3084
|
+
null,
|
|
3085
|
+
2
|
|
3086
|
+
)}
|
|
3087
|
+
`
|
|
3088
|
+
);
|
|
3089
|
+
process.exit(0);
|
|
3090
|
+
}
|
|
3091
|
+
await clearMarker();
|
|
3092
|
+
if (e instanceof ValidationError) {
|
|
3093
|
+
process.stderr.write(
|
|
3094
|
+
`${JSON.stringify({ ok: false, error: { code: "validation", issues: e.issues } }, null, 2)}
|
|
3095
|
+
`
|
|
3096
|
+
);
|
|
3097
|
+
process.exit(2);
|
|
3098
|
+
}
|
|
3099
|
+
if (e instanceof LayerExecutionError) {
|
|
3100
|
+
const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
|
|
3101
|
+
if (poster) await flushBilled(poster, await failedPayload(e.message));
|
|
3102
|
+
process.stderr.write(
|
|
3103
|
+
`${JSON.stringify({ ok: false, error: { code: runFailureCode(e), message: e.message, failures } }, null, 2)}
|
|
3104
|
+
`
|
|
3105
|
+
);
|
|
3106
|
+
process.exit(1);
|
|
3107
|
+
}
|
|
3108
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3109
|
+
if (poster) await flushBilled(poster, await failedPayload(msg));
|
|
3110
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: runFailureCode(e), message: msg } }, null, 2)}
|
|
3111
|
+
`);
|
|
3112
|
+
process.exit(1);
|
|
3113
|
+
}
|
|
3114
|
+
if (next) await executeCanvasRun(next);
|
|
3115
|
+
}
|
|
3116
|
+
function canvasNodeIds(parsed) {
|
|
3117
|
+
const nodes = parsed?.nodes;
|
|
3118
|
+
if (!Array.isArray(nodes)) return [];
|
|
3119
|
+
return nodes.map((node) => node?.id).filter((id) => typeof id === "string");
|
|
3120
|
+
}
|
|
3121
|
+
function buildBackendClient() {
|
|
3122
|
+
try {
|
|
3123
|
+
const creds = requireCredentialsFromEnv();
|
|
3124
|
+
return new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
3125
|
+
} catch (e) {
|
|
3126
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3127
|
+
process.stderr.write(`[warn] run history disabled (${msg})
|
|
3128
|
+
`);
|
|
3129
|
+
return null;
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3133
|
+
async function postInitialRunRecord(client, payload) {
|
|
3134
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3135
|
+
try {
|
|
3136
|
+
await client.recordRun(payload);
|
|
3137
|
+
return true;
|
|
3138
|
+
} catch {
|
|
3139
|
+
if (attempt < 2) await sleep(500 * 2 ** attempt);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
return false;
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
export {
|
|
3146
|
+
runCommand,
|
|
3147
|
+
stopBeforeKinds,
|
|
3148
|
+
executeCanvasRun,
|
|
3149
|
+
buildBackendClient
|
|
3150
|
+
};
|
|
3151
|
+
//# sourceMappingURL=chunk-CZYUZL6R.js.map
|