@poetic-ai/poetic 1.42.0
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/.github/SECURITY.md +47 -0
- package/.nvmrc +1 -0
- package/.poetic/README.md +37 -0
- package/.poetic/providers/catalog.json +12051 -0
- package/.poetic/providers/pricing.json +1272 -0
- package/.poetic/providers/registry.json +3369 -0
- package/CHANGELOG.md +552 -0
- package/CODE_OF_CONDUCT.md +40 -0
- package/CONTRIBUTING.md +23 -0
- package/INSTALL.md +454 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/BasicOptimizationCompetitionRunner-5H5TBYO4.js +153 -0
- package/dist/ExecutionTracker-UVONC4C6.js +16 -0
- package/dist/OptimizationConfig-7DFY2TST.js +17 -0
- package/dist/OptimizationEngine-JIMGP3LY.js +1597 -0
- package/dist/PromptStore-U6ZD3ZMQ.js +18 -0
- package/dist/actions-LUJPINLJ.js +322 -0
- package/dist/agent-ingest-3DJUJ7FU.js +53 -0
- package/dist/aggregator-DNCBINFO.js +12 -0
- package/dist/ai-judge-5QT5QJVU.js +156 -0
- package/dist/allowlist-grounding-RO6HKUFI.js +16 -0
- package/dist/allowlist-utils-23V7TJ3G.js +28 -0
- package/dist/anchored-turn-service-JULUCUIK.js +489 -0
- package/dist/api-transport-4TYTNLTJ.js +1087 -0
- package/dist/apply-completion-mode-AW2MOS3R.js +89 -0
- package/dist/artifact-migrator-23L45ISD.js +267 -0
- package/dist/ask-HWVV6QFR.js +135 -0
- package/dist/auth-5KEDKAPP.js +67 -0
- package/dist/auth-7TT3JINV.js +78 -0
- package/dist/auth-E6XUNZ5M.js +68 -0
- package/dist/auth-H3XDZE22.js +134 -0
- package/dist/auth-OC4E6633.js +71 -0
- package/dist/auth-S4CTX4GQ.js +44 -0
- package/dist/auth-YLH3S7EY.js +69 -0
- package/dist/auth-liveness-TDDEZY3T.js +46 -0
- package/dist/auto-optimizer-VPRWOWGU.js +80 -0
- package/dist/autoloop-cleanup-JOGKTLWA.js +109 -0
- package/dist/autoloop-evidence-gates-PCDALGVJ.js +38 -0
- package/dist/autoloop-gate-policy-Z7FVJVXV.js +175 -0
- package/dist/autoloop-helpers-2KTK43GL.js +22 -0
- package/dist/autoloop-ledger-W7QO62PX.js +96 -0
- package/dist/autoloop-ledger-subscriber-Z5RW5EIE.js +162 -0
- package/dist/autoloop-run-defaults-FERGRZGP.js +19 -0
- package/dist/autoloop-run-lock-CT2NWSV3.js +197 -0
- package/dist/autoloop-success-J2VKKBXQ.js +85 -0
- package/dist/autoloop-termination-summary-RN75F422.js +58 -0
- package/dist/autoloop-trajectory-XFIEXP5Z.js +282 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/autonomous-loop-controller-PW4XVFGQ.js +4120 -0
- package/dist/backend-P3DAYRP7.js +29 -0
- package/dist/background-executor-CBN552V4.js +257 -0
- package/dist/backlog-execution-intent-U4XYPTV6.js +15 -0
- package/dist/backup-active-store-5WXG6SXT.js +17 -0
- package/dist/backup-telemetry-db-XGLXAA3N.js +254 -0
- package/dist/basic-competition-master-VCSBJVRN.js +344 -0
- package/dist/branch-archive-manager-RP6FP3ZC.js +254 -0
- package/dist/branch-cleanup-manager-RSHAI7YR.js +20 -0
- package/dist/build-gate-YHGRB4QB.js +71 -0
- package/dist/change-summary-WY7J5TOX.js +33 -0
- package/dist/chunk-23A26CSN.js +123 -0
- package/dist/chunk-27UTJD3M.js +265 -0
- package/dist/chunk-2A5CILN3.js +310 -0
- package/dist/chunk-2AQUWJPW.js +225 -0
- package/dist/chunk-2BZ53J6J.js +2344 -0
- package/dist/chunk-2DY5KNVM.js +276 -0
- package/dist/chunk-2EIA4RBE.js +659 -0
- package/dist/chunk-2IUXIGQD.js +1064 -0
- package/dist/chunk-2N42MS2Z.js +2193 -0
- package/dist/chunk-2QJ3H3L7.js +42 -0
- package/dist/chunk-2QLYTVYX.js +791 -0
- package/dist/chunk-2VSQMRCB.js +18 -0
- package/dist/chunk-2XQXYBM2.js +130 -0
- package/dist/chunk-2YFUIER7.js +426 -0
- package/dist/chunk-34CNP2HB.js +148 -0
- package/dist/chunk-35P3W3JX.js +456 -0
- package/dist/chunk-36PEBZAF.js +18 -0
- package/dist/chunk-3AOKYKA7.js +22 -0
- package/dist/chunk-3BCUNHKF.js +475 -0
- package/dist/chunk-3FLXLLB7.js +26 -0
- package/dist/chunk-3FOZ2VHV.js +100 -0
- package/dist/chunk-3G6FZCRF.js +106 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3S62LLWJ.js +543 -0
- package/dist/chunk-3ZPQLGV7.js +2904 -0
- package/dist/chunk-44N5WZZH.js +45 -0
- package/dist/chunk-45FPSA3B.js +125 -0
- package/dist/chunk-4BJ3O4RQ.js +929 -0
- package/dist/chunk-4BOP7T6L.js +2321 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4CSS7WVI.js +143 -0
- package/dist/chunk-4CUY7AFY.js +247 -0
- package/dist/chunk-4GSN5O5J.js +62 -0
- package/dist/chunk-4HN6DF7V.js +1027 -0
- package/dist/chunk-4IAPOG5V.js +415 -0
- package/dist/chunk-4IYIZQWV.js +48 -0
- package/dist/chunk-4NXPXE62.js +84 -0
- package/dist/chunk-4RCCF5BR.js +712 -0
- package/dist/chunk-4V7NAQ4R.js +19527 -0
- package/dist/chunk-4VJITV4P.js +445 -0
- package/dist/chunk-4YX7J6QL.js +1859 -0
- package/dist/chunk-527OBWQB.js +338 -0
- package/dist/chunk-5D4A7E3D.js +448 -0
- package/dist/chunk-5DLKYTQX.js +4003 -0
- package/dist/chunk-5EERLVZB.js +139 -0
- package/dist/chunk-5H72XE4H.js +256 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5LAMRH4X.js +969 -0
- package/dist/chunk-5LTN43AS.js +1972 -0
- package/dist/chunk-5RBHQYDB.js +2206 -0
- package/dist/chunk-5WCQRDRB.js +588 -0
- package/dist/chunk-5WRLK5KW.js +700 -0
- package/dist/chunk-5ZCXC23G.js +595 -0
- package/dist/chunk-6BM72ZMG.js +31 -0
- package/dist/chunk-6EDAJH2I.js +68 -0
- package/dist/chunk-6EQKFDBH.js +457 -0
- package/dist/chunk-6MFRHTRI.js +115 -0
- package/dist/chunk-6TZJRKNW.js +321 -0
- package/dist/chunk-6V7HGQFZ.js +20 -0
- package/dist/chunk-6VWVO2TU.js +3746 -0
- package/dist/chunk-6Y5TWI7H.js +146 -0
- package/dist/chunk-6Y5U7UFY.js +91 -0
- package/dist/chunk-6ZLAK3XG.js +14 -0
- package/dist/chunk-72XCRQED.js +34 -0
- package/dist/chunk-72YTZMAM.js +589 -0
- package/dist/chunk-73BBJOKA.js +263 -0
- package/dist/chunk-73NCTWKL.js +188 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DHRDWFS.js +3104 -0
- package/dist/chunk-7DNSNKJV.js +78 -0
- package/dist/chunk-7FMJVYBS.js +307 -0
- package/dist/chunk-7GDRQBQC.js +419 -0
- package/dist/chunk-7GPCEFUV.js +270 -0
- package/dist/chunk-7IL4A2PP.js +948 -0
- package/dist/chunk-7MMLMXO6.js +3679 -0
- package/dist/chunk-7QCZSLBH.js +1 -0
- package/dist/chunk-7R2WLDOS.js +62 -0
- package/dist/chunk-7T7RUJI7.js +949 -0
- package/dist/chunk-A2WKSVX7.js +43 -0
- package/dist/chunk-A3YGID55.js +133 -0
- package/dist/chunk-A722DEVA.js +14 -0
- package/dist/chunk-ACDIT7OP.js +1376 -0
- package/dist/chunk-ACKIPAOJ.js +302 -0
- package/dist/chunk-APPNGGN7.js +365 -0
- package/dist/chunk-APV6MK5E.js +137 -0
- package/dist/chunk-ASEP3M2W.js +968 -0
- package/dist/chunk-AT4TNPWW.js +2931 -0
- package/dist/chunk-ATVKCSGT.js +250 -0
- package/dist/chunk-AURFMVCD.js +899 -0
- package/dist/chunk-AX6GWE7V.js +47 -0
- package/dist/chunk-AY5EWJPF.js +385 -0
- package/dist/chunk-BAV2HJXS.js +194 -0
- package/dist/chunk-BBHRN366.js +89 -0
- package/dist/chunk-BBPSTTPA.js +1004 -0
- package/dist/chunk-BDATUDDJ.js +992 -0
- package/dist/chunk-BKE5PG5L.js +20414 -0
- package/dist/chunk-BLPHWMDK.js +797 -0
- package/dist/chunk-BSE4R6XD.js +322 -0
- package/dist/chunk-BXGMAU4A.js +396 -0
- package/dist/chunk-BXI4NXL3.js +507 -0
- package/dist/chunk-BZU2O43F.js +1125 -0
- package/dist/chunk-C3L6YQ7P.js +147 -0
- package/dist/chunk-C57KJOUQ.js +121 -0
- package/dist/chunk-CCV7BPSY.js +118 -0
- package/dist/chunk-CD7ISXA4.js +826 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFBIG37O.js +154 -0
- package/dist/chunk-CJC6GZ46.js +457 -0
- package/dist/chunk-CKCDGXIV.js +408 -0
- package/dist/chunk-CKNX3TTP.js +10338 -0
- package/dist/chunk-CLKSETWE.js +10 -0
- package/dist/chunk-CP7H3HSP.js +1552 -0
- package/dist/chunk-CPLEWXNZ.js +66 -0
- package/dist/chunk-CQM3A35X.js +844 -0
- package/dist/chunk-CS6P76D3.js +103 -0
- package/dist/chunk-CTLUBNCW.js +622 -0
- package/dist/chunk-CUSRN6RJ.js +632 -0
- package/dist/chunk-D5EP5D2W.js +26 -0
- package/dist/chunk-D5RYINO7.js +412 -0
- package/dist/chunk-DBFS4HMH.js +751 -0
- package/dist/chunk-DC4ZYMXJ.js +115 -0
- package/dist/chunk-DCBZP442.js +9243 -0
- package/dist/chunk-DCYF7EKG.js +26 -0
- package/dist/chunk-DDMAFNUK.js +5351 -0
- package/dist/chunk-DF5SLDE4.js +966 -0
- package/dist/chunk-DJFW7SMX.js +348 -0
- package/dist/chunk-DQFLLBJW.js +121 -0
- package/dist/chunk-DSFM56OA.js +1203 -0
- package/dist/chunk-DTY4APYV.js +218 -0
- package/dist/chunk-DXH5JBRS.js +437 -0
- package/dist/chunk-E3NTPHEO.js +194 -0
- package/dist/chunk-E74LUIID.js +302 -0
- package/dist/chunk-EI57M5BI.js +194 -0
- package/dist/chunk-EJ4ZAWLV.js +305 -0
- package/dist/chunk-EJGCZGUF.js +1483 -0
- package/dist/chunk-EK2U3YEG.js +486 -0
- package/dist/chunk-ELMFYNV7.js +109 -0
- package/dist/chunk-ENRGKNR5.js +25 -0
- package/dist/chunk-ETYDXCK7.js +679 -0
- package/dist/chunk-EYRH3W74.js +239 -0
- package/dist/chunk-F23DIWNN.js +1024 -0
- package/dist/chunk-F5JO7HAS.js +94 -0
- package/dist/chunk-F7ER6ANO.js +174 -0
- package/dist/chunk-FATUVIIN.js +374 -0
- package/dist/chunk-FC23CTXV.js +322 -0
- package/dist/chunk-FD4ERYEG.js +22 -0
- package/dist/chunk-FH7WOJAE.js +79 -0
- package/dist/chunk-FHOWVDF5.js +1342 -0
- package/dist/chunk-FKAUPHMR.js +96 -0
- package/dist/chunk-FONNFJXA.js +137 -0
- package/dist/chunk-FQ7FSZRW.js +75 -0
- package/dist/chunk-FRG57FPM.js +240 -0
- package/dist/chunk-FUVP64ER.js +31 -0
- package/dist/chunk-FYOILAN5.js +6818 -0
- package/dist/chunk-G35UYQ6Z.js +69 -0
- package/dist/chunk-G36W2T2S.js +1143 -0
- package/dist/chunk-G3L2L5XZ.js +49 -0
- package/dist/chunk-G4THLFV3.js +148 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GFGYB2ZU.js +1689 -0
- package/dist/chunk-GG6KJYW6.js +85 -0
- package/dist/chunk-GHHFDU2U.js +55 -0
- package/dist/chunk-GHTZSQB7.js +57 -0
- package/dist/chunk-GK37ILCX.js +1231 -0
- package/dist/chunk-GKN3HMN4.js +2809 -0
- package/dist/chunk-GNVDBVJP.js +156 -0
- package/dist/chunk-GPEL3FBM.js +924 -0
- package/dist/chunk-GPSNGPZS.js +1604 -0
- package/dist/chunk-GSGD5E4C.js +30 -0
- package/dist/chunk-GSUQJJXQ.js +331 -0
- package/dist/chunk-GSV226X6.js +110 -0
- package/dist/chunk-H2AYQIHW.js +224 -0
- package/dist/chunk-H66DXFS5.js +1374 -0
- package/dist/chunk-H6G7QRMQ.js +35 -0
- package/dist/chunk-H7JAGHZT.js +59 -0
- package/dist/chunk-H7ZEFECA.js +316 -0
- package/dist/chunk-HAC6EHZZ.js +79 -0
- package/dist/chunk-HIO33P3G.js +19 -0
- package/dist/chunk-HMVQIPH3.js +547 -0
- package/dist/chunk-HOYBYAOA.js +257 -0
- package/dist/chunk-HX5P3CSP.js +1784 -0
- package/dist/chunk-I46EG2XQ.js +65 -0
- package/dist/chunk-I4NPDSCB.js +279 -0
- package/dist/chunk-I5WRT7ZR.js +514 -0
- package/dist/chunk-IC7I6YIJ.js +301 -0
- package/dist/chunk-IDCSOZVN.js +78 -0
- package/dist/chunk-IFCBYECK.js +112 -0
- package/dist/chunk-IFZCPEY2.js +66 -0
- package/dist/chunk-IKFP2KUR.js +48 -0
- package/dist/chunk-IRFBN336.js +878 -0
- package/dist/chunk-IUXNGKA6.js +152 -0
- package/dist/chunk-IV2RVAN7.js +1356 -0
- package/dist/chunk-IZYIQQ7X.js +53 -0
- package/dist/chunk-J3UUR7DE.js +210 -0
- package/dist/chunk-J54VUAY2.js +566 -0
- package/dist/chunk-J62KPGII.js +464 -0
- package/dist/chunk-J6P3FWWW.js +130 -0
- package/dist/chunk-JKBHJQMT.js +434 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JOSYEJKY.js +141 -0
- package/dist/chunk-JPKSXKNB.js +94 -0
- package/dist/chunk-JQNJB2BB.js +2440 -0
- package/dist/chunk-JRVNSWSQ.js +2626 -0
- package/dist/chunk-JSWMRQYG.js +28 -0
- package/dist/chunk-JZKNBBIB.js +152 -0
- package/dist/chunk-K54Y7MLK.js +160 -0
- package/dist/chunk-K65546RE.js +183 -0
- package/dist/chunk-KM3F7SYE.js +702 -0
- package/dist/chunk-KNDYMVWN.js +102 -0
- package/dist/chunk-KNT72VR6.js +132 -0
- package/dist/chunk-KP6YILMU.js +482 -0
- package/dist/chunk-KQEBSK3T.js +274 -0
- package/dist/chunk-KT6VHSPF.js +84 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KZ5CJVBP.js +1813 -0
- package/dist/chunk-L4427KOE.js +26 -0
- package/dist/chunk-L6OM4A22.js +189 -0
- package/dist/chunk-L7E6XCRA.js +1042 -0
- package/dist/chunk-L7JZLXQL.js +173 -0
- package/dist/chunk-LAMKBTXX.js +40 -0
- package/dist/chunk-LDCF2MKN.js +132 -0
- package/dist/chunk-LEIIWPWQ.js +78 -0
- package/dist/chunk-LFTQVYBB.js +31 -0
- package/dist/chunk-LG3PDRTR.js +2872 -0
- package/dist/chunk-LGHII5ET.js +23 -0
- package/dist/chunk-LH5QY22X.js +1205 -0
- package/dist/chunk-LIVJ6266.js +438 -0
- package/dist/chunk-LKRAR3IH.js +276 -0
- package/dist/chunk-LO23NABI.js +3063 -0
- package/dist/chunk-LO3X5NLS.js +326 -0
- package/dist/chunk-LPITVM7M.js +339 -0
- package/dist/chunk-LR5IJIGV.js +810 -0
- package/dist/chunk-LRGMHROI.js +79 -0
- package/dist/chunk-LUVKFFWR.js +315 -0
- package/dist/chunk-LWCBNGH6.js +35 -0
- package/dist/chunk-LWTN3WRV.js +260 -0
- package/dist/chunk-M3HQYKQX.js +308 -0
- package/dist/chunk-M6GVSPEH.js +1016 -0
- package/dist/chunk-MF5HP6XV.js +221 -0
- package/dist/chunk-MHLCBZJB.js +328 -0
- package/dist/chunk-MN6PL4AN.js +120 -0
- package/dist/chunk-MOEY65Z6.js +5678 -0
- package/dist/chunk-MQ4WA34C.js +206 -0
- package/dist/chunk-MWHF5V7U.js +223 -0
- package/dist/chunk-MWHLBPNU.js +211 -0
- package/dist/chunk-MWK5UZQ3.js +1068 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N4VTRA7K.js +131 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-N65O4XTH.js +5011 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NGK6PEOG.js +25 -0
- package/dist/chunk-NJVD4PWE.js +1033 -0
- package/dist/chunk-NM6PMYY4.js +768 -0
- package/dist/chunk-NN4PJDHP.js +708 -0
- package/dist/chunk-NP2V3L7K.js +226 -0
- package/dist/chunk-NRLY536M.js +260 -0
- package/dist/chunk-NRNE225N.js +1270 -0
- package/dist/chunk-NS2K2GTP.js +605 -0
- package/dist/chunk-NS33V3IM.js +51 -0
- package/dist/chunk-NSHASMZZ.js +71 -0
- package/dist/chunk-NWOYOA6H.js +378 -0
- package/dist/chunk-NWRUSEXG.js +322 -0
- package/dist/chunk-NZGDAWIP.js +1905 -0
- package/dist/chunk-O47UOKU3.js +395 -0
- package/dist/chunk-O4G2W3ST.js +9831 -0
- package/dist/chunk-O6V4PNLO.js +63 -0
- package/dist/chunk-OA4GPILI.js +624 -0
- package/dist/chunk-OGMSYL6J.js +227 -0
- package/dist/chunk-OIS7J26S.js +319 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ON3G73BU.js +264 -0
- package/dist/chunk-OPYFYTWQ.js +220 -0
- package/dist/chunk-OQ6K46CK.js +1734 -0
- package/dist/chunk-OYZYO7TV.js +47 -0
- package/dist/chunk-OZ4REHIN.js +4386 -0
- package/dist/chunk-P47VN5F4.js +282 -0
- package/dist/chunk-P5I4X7A7.js +353 -0
- package/dist/chunk-P6VQROCO.js +594 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-PCAGC5MR.js +1763 -0
- package/dist/chunk-PFTQBXS6.js +5181 -0
- package/dist/chunk-PGSPX4SU.js +15 -0
- package/dist/chunk-PIKLF7BM.js +432 -0
- package/dist/chunk-PJWJ3SAV.js +22 -0
- package/dist/chunk-PKIFMV72.js +100 -0
- package/dist/chunk-PL37JDNA.js +189 -0
- package/dist/chunk-PTH5E5XO.js +195 -0
- package/dist/chunk-PW6AXLQC.js +4128 -0
- package/dist/chunk-PYITM4N2.js +79 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3RO7N35.js +45 -0
- package/dist/chunk-Q3WIC6GQ.js +2670 -0
- package/dist/chunk-Q5XIXSHX.js +271 -0
- package/dist/chunk-QGJDMEOV.js +34 -0
- package/dist/chunk-QNOB37UH.js +58 -0
- package/dist/chunk-QOCOVLUH.js +462 -0
- package/dist/chunk-QTCJ5CC5.js +172 -0
- package/dist/chunk-QVFV5IP2.js +232 -0
- package/dist/chunk-QVZMFDYG.js +29 -0
- package/dist/chunk-R35PEUKH.js +511 -0
- package/dist/chunk-R4UWBC35.js +200 -0
- package/dist/chunk-R6MLCREW.js +44 -0
- package/dist/chunk-R6W23LKN.js +254 -0
- package/dist/chunk-R7XDDX2A.js +4007 -0
- package/dist/chunk-RDFRCT64.js +168 -0
- package/dist/chunk-RE5VZDFQ.js +58 -0
- package/dist/chunk-RGROYC2G.js +638 -0
- package/dist/chunk-RGWHUI5E.js +68 -0
- package/dist/chunk-RHVG6UNL.js +205 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RJAAKDRX.js +17 -0
- package/dist/chunk-ROMHOSMQ.js +185 -0
- package/dist/chunk-RWREC3KJ.js +517 -0
- package/dist/chunk-RYNETRDJ.js +118 -0
- package/dist/chunk-RZY7RKM5.js +500 -0
- package/dist/chunk-S2MNWFAG.js +52 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S54MKU6V.js +117 -0
- package/dist/chunk-S5OIDFRM.js +302 -0
- package/dist/chunk-SCW4ZF6R.js +150 -0
- package/dist/chunk-SLS2N4PH.js +2213 -0
- package/dist/chunk-SNLBO4BF.js +247 -0
- package/dist/chunk-SP3LFU6B.js +60 -0
- package/dist/chunk-SQQTDDQA.js +537 -0
- package/dist/chunk-STV6LYYE.js +45 -0
- package/dist/chunk-SURZ2WFE.js +326 -0
- package/dist/chunk-SZ7DL357.js +1047 -0
- package/dist/chunk-T5E4NQCM.js +5742 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TAOOYK3P.js +358 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TFTGP7Z7.js +174 -0
- package/dist/chunk-TGON4N4O.js +197 -0
- package/dist/chunk-TGQZHDY2.js +2373 -0
- package/dist/chunk-TJJZ2OKV.js +6783 -0
- package/dist/chunk-TRSVUPCX.js +25 -0
- package/dist/chunk-TSXGRLPR.js +107 -0
- package/dist/chunk-U2S3YEX3.js +224 -0
- package/dist/chunk-U45RTRGY.js +818 -0
- package/dist/chunk-U6C3VVWS.js +13 -0
- package/dist/chunk-UDMS5AD3.js +307 -0
- package/dist/chunk-UGX7GI37.js +94 -0
- package/dist/chunk-UH3JJSHK.js +1 -0
- package/dist/chunk-UHRBTZYY.js +208 -0
- package/dist/chunk-UHT2KRG5.js +193 -0
- package/dist/chunk-UI2F6DJ5.js +118 -0
- package/dist/chunk-UMAKSB4Z.js +284 -0
- package/dist/chunk-UR2IBGIM.js +521 -0
- package/dist/chunk-UUH2RVKS.js +585 -0
- package/dist/chunk-UVJ4PJDK.js +4533 -0
- package/dist/chunk-V53UG2OT.js +9924 -0
- package/dist/chunk-V72RMBM4.js +48 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VNSYKC25.js +24 -0
- package/dist/chunk-VPHVFK4A.js +1984 -0
- package/dist/chunk-VTLNJQ44.js +133 -0
- package/dist/chunk-VTW7HP3R.js +100 -0
- package/dist/chunk-VVGEPBPS.js +218 -0
- package/dist/chunk-VVLLL7I4.js +166 -0
- package/dist/chunk-VX3UMWB7.js +483 -0
- package/dist/chunk-VXF4Q3FW.js +20 -0
- package/dist/chunk-W3JMT2YY.js +712 -0
- package/dist/chunk-WDUORIHF.js +877 -0
- package/dist/chunk-WDVTY4U6.js +148 -0
- package/dist/chunk-WIFZCHEG.js +12566 -0
- package/dist/chunk-WIRN3F75.js +736 -0
- package/dist/chunk-WMPU2UOV.js +82 -0
- package/dist/chunk-WTOKKE2V.js +784 -0
- package/dist/chunk-WX5IRLF6.js +740 -0
- package/dist/chunk-WZ6U3O7I.js +1581 -0
- package/dist/chunk-XAONGNST.js +57 -0
- package/dist/chunk-XB5YLCLB.js +13 -0
- package/dist/chunk-XEP5NQ5Y.js +688 -0
- package/dist/chunk-XGYF2QMZ.js +351 -0
- package/dist/chunk-XHEKQENB.js +255 -0
- package/dist/chunk-XNO4FM6X.js +1124 -0
- package/dist/chunk-XPHKYHO4.js +878 -0
- package/dist/chunk-XPK2I4NC.js +405 -0
- package/dist/chunk-XPU2XADL.js +2175 -0
- package/dist/chunk-XQD2B6BB.js +507 -0
- package/dist/chunk-XT2ZQVTC.js +558 -0
- package/dist/chunk-XTXJGGUV.js +351 -0
- package/dist/chunk-XUXVDPSZ.js +50 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XZE4XARH.js +563 -0
- package/dist/chunk-XZYR5EDO.js +152 -0
- package/dist/chunk-Y3J4JMTU.js +61 -0
- package/dist/chunk-Y55LZN5U.js +52 -0
- package/dist/chunk-Y5VN7DMP.js +112 -0
- package/dist/chunk-Y67VAHN4.js +459 -0
- package/dist/chunk-YAXSKEUH.js +3544 -0
- package/dist/chunk-YB3JD4E6.js +975 -0
- package/dist/chunk-YESZJFK6.js +220 -0
- package/dist/chunk-YGXKOBQQ.js +292 -0
- package/dist/chunk-YMX76IOS.js +31 -0
- package/dist/chunk-YOTXESEH.js +257 -0
- package/dist/chunk-YTYIEZQY.js +119 -0
- package/dist/chunk-YVORHQ2S.js +579 -0
- package/dist/chunk-Z2CUQDAR.js +3429 -0
- package/dist/chunk-Z2KYOFHS.js +7000 -0
- package/dist/chunk-Z6PC7QX6.js +161 -0
- package/dist/chunk-ZDTGECTN.js +235 -0
- package/dist/chunk-ZIPWI2LY.js +151 -0
- package/dist/chunk-ZSKKMTQJ.js +554 -0
- package/dist/chunk-ZTT5IVPY.js +1160 -0
- package/dist/chunk-ZX65UI5W.js +78 -0
- package/dist/chunk-ZYG7GGKO.js +218 -0
- package/dist/chunk-ZZZUL4OF.js +38 -0
- package/dist/cli-utils-IXL26KJT.js +40 -0
- package/dist/cli-validation-WLXWLUJL.js +111 -0
- package/dist/commit-utils-P7CRJF5N.js +202 -0
- package/dist/compete-config-resolver-R5ULXMYH.js +76 -0
- package/dist/compete-request-62JESVET.js +364 -0
- package/dist/compete-results-QEPG6FZX.js +208 -0
- package/dist/competition-outcome-tracker-R32OOZ43.js +38 -0
- package/dist/config-JCOKXOQT.js +166 -0
- package/dist/config-audit-EL3GHXS7.js +360 -0
- package/dist/config-explain-TNILDEVJ.js +12 -0
- package/dist/config-loader-VPFPFO4B.js +38 -0
- package/dist/config-manager-7T4E2BQO.js +67 -0
- package/dist/config-validator-KKOHOW7O.js +72 -0
- package/dist/cost-YC42XUF3.js +53 -0
- package/dist/coverage-orchestrator-CISNKJC2.js +772 -0
- package/dist/coverage-scanner-YVD62MLH.js +12 -0
- package/dist/createCompetitionRunner-LJG6ZQSO.js +46 -0
- package/dist/data-migration-state-VJK64DLT.js +31 -0
- package/dist/db-migrator-XSEIOGWY.js +34 -0
- package/dist/discovery-JGYGDD25.js +25 -0
- package/dist/doctor-GJOYE7AE.js +186 -0
- package/dist/domain-analyzer-AWF3RP6E.js +29 -0
- package/dist/ensure-initialized-744DIZEZ.js +52 -0
- package/dist/entry.js +940 -0
- package/dist/environment-YS45U5ZI.js +81 -0
- package/dist/error-parser-core-PPZ36A4S.js +50 -0
- package/dist/escalation-ladder-MKTCEGSA.js +130 -0
- package/dist/evaluation-context-KV4GZNN7.js +35 -0
- package/dist/evidence-plan-resolver-GBSYSAPX.js +85 -0
- package/dist/execution-data-writer-3ODTBDLH.js +53 -0
- package/dist/execution-policy-applier-ZHD3DCF3.js +31 -0
- package/dist/execution-preflight-Z4Y64V3I.js +209 -0
- package/dist/execution-roles-R3DPPMD3.js +66 -0
- package/dist/exit-code-error-Z4SW2DVK.js +14 -0
- package/dist/external-temp-cleanup-N2RZH4QP.js +311 -0
- package/dist/factory-XBN7DFIM.js +149 -0
- package/dist/file-utils-XIVR2ZMA.js +47 -0
- package/dist/flywheel-autoloop-executor-UILGOFFK.js +249 -0
- package/dist/flywheel-competition-executor-JOBEXTRB.js +259 -0
- package/dist/flywheel-executor-6JFLA6J5.js +369 -0
- package/dist/flywheel-git-isolation-UC5NIEVU.js +48 -0
- package/dist/flywheel-manifest-5UC4VBMA.js +108 -0
- package/dist/flywheel-model-defaults-7CVKP53V.js +27 -0
- package/dist/flywheel-preflight-XEY5SHM6.js +104 -0
- package/dist/flywheel-resume-7HELH7QS.js +23 -0
- package/dist/flywheel-safety-7U7DPOAP.js +28 -0
- package/dist/flywheel-scope-decision-ZHKF6ZIN.js +11 -0
- package/dist/get-telemetry-logger-DPVWWIYY.js +73 -0
- package/dist/git-worktree-IY6V6DUO.js +18 -0
- package/dist/github-pr-manager-4LXEKJTA.js +211 -0
- package/dist/guidance-profile-config-E7XJL2VP.js +117 -0
- package/dist/guidance-profile-renderer-QK2YMAAP.js +21 -0
- package/dist/index-query-4JEUKA44.js +31 -0
- package/dist/index.js +108661 -0
- package/dist/instructions-6BURLQJC.js +64 -0
- package/dist/invoke-provider-auth-GRUYP44M.js +223 -0
- package/dist/judge-scoring.json +80 -0
- package/dist/judge-test.js +555 -0
- package/dist/lab-mode-OTWVHN63.js +39 -0
- package/dist/lab-utils-BJRFLGK4.js +23 -0
- package/dist/linux-host-class-UY4KVLFD.js +29 -0
- package/dist/llm-judge-executor-F7Z3HO77.js +133 -0
- package/dist/local-executor-CEHWE2PL.js +223 -0
- package/dist/logger-33TR6EE5.js +72 -0
- package/dist/loop-spec-XTLQAVZ7.js +191 -0
- package/dist/manager-2HJG5CLT.js +25 -0
- package/dist/matrix-prompt-builder-6UDN5X3F.js +497 -0
- package/dist/matrix-result-parser-7W35EUOV.js +15 -0
- package/dist/metadata-HQ43NDEK.js +22 -0
- package/dist/model-invocations-db-QBO4Y7WZ.js +38 -0
- package/dist/monitor-XGYBG2DW.js +72 -0
- package/dist/next-PB5ZXZKB.js +90 -0
- package/dist/next-ZBDK725T.js +69 -0
- package/dist/optimization-config-resolver-O6WKR47U.js +75 -0
- package/dist/package-3YCWIQQ5.js +10 -0
- package/dist/parallel-orchestrator-NL7YJJU2.js +297 -0
- package/dist/parallel-worker.js +279 -0
- package/dist/parse-git-status-PIAF3OTP.js +10 -0
- package/dist/path-security-ETTH6TZL.js +39 -0
- package/dist/pattern-bank-UNJR5ADV.js +15 -0
- package/dist/plan-backlog-list-fast-VAGYBV3J.js +270 -0
- package/dist/plan-backlog-show-fast-SG2CZPW3.js +406 -0
- package/dist/plan-sprint-list-fast-LURAX47V.js +162 -0
- package/dist/plan-sprint-status-fast-FJAODCHD.js +208 -0
- package/dist/plan-task-status-fast-HNYBMQI4.js +349 -0
- package/dist/plan-to-flywheel-Z4U3GWPN.js +456 -0
- package/dist/planning-backlog-3CQ5QQBQ.js +319 -0
- package/dist/poetic-root-V5DNXEAE.js +17 -0
- package/dist/preflight-OPANWMME.js +12 -0
- package/dist/process-registry-PDZJPTAS.js +27 -0
- package/dist/process-scanner-C22SYXYD.js +22 -0
- package/dist/processes-KMQUD7HY.js +29 -0
- package/dist/processes-json-fast-IBDXZ6WW.js +135 -0
- package/dist/profile-SE5EVXAP.js +149 -0
- package/dist/prompts-WZZPGWBX.js +168 -0
- package/dist/protected-branches-RNTEX7ET.js +57 -0
- package/dist/provider-aware-resource-manager-QBTZJMNP.js +18 -0
- package/dist/provider-matrix-N5X57V3Y.js +154 -0
- package/dist/provider-matrix-renderer-MZDWKKID.js +139 -0
- package/dist/provider-output-forwarding-DERID3TP.js +30 -0
- package/dist/provider-registry-J5NRBJKI.js +128 -0
- package/dist/provider-temp-cleanup-KYGLF6YU.js +14 -0
- package/dist/prune-engine-Q6E2DXBN.js +451 -0
- package/dist/quality-gate-YRHMQAEI.js +70 -0
- package/dist/quickstart-FIRLKGJI.js +62 -0
- package/dist/readiness-VZTMSG6J.js +96 -0
- package/dist/registry-YODFY77A.js +153 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-2IGD4H7X.js +22 -0
- package/dist/resolution-engine-D6TX6FRG.js +127 -0
- package/dist/resolve-provider-cli-O2P6XZBZ.js +35 -0
- package/dist/restore-telemetry-db-BQL6DTQF.js +360 -0
- package/dist/result-streamer-WEHW476R.js +19 -0
- package/dist/routing-events-DHPUZDDF.js +27 -0
- package/dist/routing-history-store-UWCEXAP3.js +46 -0
- package/dist/routing-planner-F5WQEUOM.js +160 -0
- package/dist/run-poetic-tui-J6LWRJUP.js +13517 -0
- package/dist/run-simulation.js +376 -0
- package/dist/runner-CS2TNGHA.js +1092 -0
- package/dist/runner-HHH7ZN2Z.js +1571 -0
- package/dist/safety-OCC4KJJR.js +64 -0
- package/dist/safety-stanza-7SV3HGG3.js +60 -0
- package/dist/sandbox-32QXJEMV.js +81 -0
- package/dist/sandbox-RSO7VP6V.js +94 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-2KI5XNDG.js +17 -0
- package/dist/setup-bb-plugin-QAQWZCV5.js +738 -0
- package/dist/setup-claude-code-P7BTTSPO.js +317 -0
- package/dist/setup-temp-cleanup-GASCWZ2I.js +20 -0
- package/dist/shared-utils-D6U6UREP.js +37 -0
- package/dist/simple-artifact-goal-OTXNGZY6.js +25 -0
- package/dist/sprint-UIXPYOXQ.js +338 -0
- package/dist/sprint-bridge-4BHMSC3M.js +68 -0
- package/dist/sprint-execution-service-2B7WHL5L.js +311 -0
- package/dist/sprint-manager-IDQUDNQI.js +91 -0
- package/dist/sqlite-wrapper-UDWCTNLJ.js +17 -0
- package/dist/stale-cleanup-orchestrator-XWXV7KCZ.js +62 -0
- package/dist/storage-access-F6ALDN4S.js +22 -0
- package/dist/synthesis-XD6EDVHF.js +185 -0
- package/dist/task-classifier-LNLXSHOM.js +494 -0
- package/dist/task-list-fast-PTMA4LUC.js +147 -0
- package/dist/task-manager-XAT7GOWB.js +93 -0
- package/dist/task-splitter-U7DJ7W6A.js +79 -0
- package/dist/task-type-detector-OLVOMXZO.js +21 -0
- package/dist/task-type-resolver-KKNQYSB4.js +42 -0
- package/dist/telemetry-5HWBBXLX.js +131 -0
- package/dist/telemetry-health-tracker-DMSSGBZ2.js +11 -0
- package/dist/telemetry-path-resolver-7ICSG276.js +28 -0
- package/dist/telemetry-reliability-XUOZNZZ6.js +125 -0
- package/dist/token-estimator-MEQUL3EJ.js +37 -0
- package/dist/transports-KNSSV2V4.js +255 -0
- package/dist/unified-cost-tracker-4TFRBVPR.js +35 -0
- package/dist/unified-state-cleanup-LJPF2BOW.js +276 -0
- package/dist/universal-cost-calculator-BAJV35GA.js +42 -0
- package/dist/user-agents-GHUGCSKN.js +22 -0
- package/dist/variant-delivery-state-KLRNG6XA.js +33 -0
- package/dist/variant-state-cleanup-MZGGLQAJ.js +280 -0
- package/dist/variant-success-PJXWE3ZV.js +12 -0
- package/dist/variant-worker-KUWKBLAH.js +4196 -0
- package/dist/variant-worker.js +24 -0
- package/dist/variants-JE2FSAQS.js +233 -0
- package/dist/verification-toolchains-V24UHW6Q.js +28 -0
- package/dist/verify-commands-H3DTCSGI.js +37 -0
- package/dist/warning-EGQGUUIO.js +12 -0
- package/dist/work-edge-RWEDWJZF.js +49 -0
- package/dist/work-item-writer-WHAVZXAY.js +19 -0
- package/dist/worker-7SA2VS2Y.js +1435 -0
- package/dist/worker.js +19 -0
- package/dist/worktree-metrics-3DFUYVYE.js +26 -0
- package/dist/writeback-VZKHYHSN.js +44 -0
- package/docs/CLI_REFERENCE.md +7042 -0
- package/docs/PROVIDER_SETUP.md +1570 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2232 -0
- package/docs/getting-started/QUICK_START.md +294 -0
- package/docs/getting-started/README.md +188 -0
- package/docs/getting-started/SETUP.md +52 -0
- package/docs/reference/AGENT_CONTEXT.md +38 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/README.md +381 -0
- package/docs/reference/SECURITY.md +262 -0
- package/integrations/bb-plugin-poetic/README.md +362 -0
- package/integrations/bb-plugin-poetic/app.css +341 -0
- package/integrations/bb-plugin-poetic/app.tsx +5059 -0
- package/integrations/bb-plugin-poetic/package.json +45 -0
- package/integrations/bb-plugin-poetic/server.ts +444 -0
- package/integrations/bb-plugin-poetic/src/adapter.ts +3312 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-schema.ts +355 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-service.ts +419 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-view.ts +1026 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-poetic/src/contract.ts +615 -0
- package/integrations/bb-plugin-poetic/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-poetic/src/finalize-schema.ts +158 -0
- package/integrations/bb-plugin-poetic/src/finalize-service.ts +382 -0
- package/integrations/bb-plugin-poetic/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-poetic/src/model.ts +1302 -0
- package/integrations/bb-plugin-poetic/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-poetic/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-poetic/src/planning-readback.ts +204 -0
- package/integrations/bb-plugin-poetic/src/planning-service.ts +594 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace-schema.ts +95 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace.ts +323 -0
- package/integrations/bb-plugin-poetic/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-poetic/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-poetic/src/project-target.ts +45 -0
- package/integrations/bb-plugin-poetic/src/reference-index.ts +192 -0
- package/integrations/bb-plugin-poetic/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-poetic/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-poetic/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-poetic/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-poetic/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-poetic/src/run-page-view.ts +177 -0
- package/integrations/bb-plugin-poetic/src/setup-config-schema.ts +291 -0
- package/integrations/bb-plugin-poetic/src/setup-config-service.ts +590 -0
- package/integrations/bb-plugin-poetic/src/setup-config-view.ts +413 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-schema.ts +136 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-service.ts +341 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-model.ts +714 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-schema.ts +601 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-service.ts +775 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-model.ts +567 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-schema.ts +635 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-poetic/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-poetic/src/task-detail-schema.ts +144 -0
- package/integrations/bb-plugin-poetic/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-poetic/src/workbench-route.ts +74 -0
- package/integrations/bb-plugin-poetic/tests/host-contract.test.ts +773 -0
- package/integrations/bb-plugin-poetic/tsconfig.json +18 -0
- package/integrations/bb-plugin-poetic/types/PROVENANCE.json +52 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk-app.d.ts +1444 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk.d.ts +13030 -0
- package/integrations/bb-plugin-poetic/vitest.host.config.ts +71 -0
- package/npm-shrinkwrap.json +4950 -0
- package/package.json +331 -0
- package/schemas/README.md +80 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +370 -0
- package/schemas/poetic.config.schema.json +2214 -0
- package/schemas/provider-config.schema.json +203 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +140 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/preinstall-node-check.mjs +78 -0
- package/scripts/setup-git-hooks.mjs +24 -0
- package/scripts/sync.sh +2722 -0
- package/scripts/write-node-launcher.sh +108 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.poetic/README.md +37 -0
- package/templates/.poetic/agents/README.md +296 -0
- package/templates/.poetic/agents/api-documenter.md +147 -0
- package/templates/.poetic/agents/backend-architect.md +31 -0
- package/templates/.poetic/agents/code-reviewer.md +157 -0
- package/templates/.poetic/agents/data-scientist.md +179 -0
- package/templates/.poetic/agents/database-optimizer.md +145 -0
- package/templates/.poetic/agents/debugger.md +31 -0
- package/templates/.poetic/agents/deployment-engineer.md +164 -0
- package/templates/.poetic/agents/devops-troubleshooter.md +139 -0
- package/templates/.poetic/agents/frontend-developer.md +150 -0
- package/templates/.poetic/agents/javascript-pro.md +36 -0
- package/templates/.poetic/agents/performance-engineer.md +151 -0
- package/templates/.poetic/agents/python-pro.md +137 -0
- package/templates/.poetic/agents/test-automator.md +147 -0
- package/templates/.poetic/agents/typescript-pro.md +34 -0
- package/templates/.poetic/config/poetic.config.jsonc +69 -0
- package/templates/.poetic/config/project-context.template.json +6 -0
- package/templates/.poetic/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.poetic/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.poetic/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.poetic/config/task-type-aliases.yaml +28 -0
- package/templates/.poetic/gitignore.template +55 -0
- package/templates/.poetic/task-types/analysis.yaml +40 -0
- package/templates/.poetic/task-types/architecture.yaml +38 -0
- package/templates/.poetic/task-types/doc.yaml +35 -0
- package/templates/.poetic/task-types/feature.yaml +23 -0
- package/templates/.poetic/task-types/general.yaml +6 -0
- package/templates/.poetic/task-types/security.yaml +39 -0
- package/templates/AGENTS.template.md +99 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/compete-streak.yaml +78 -0
- package/templates/builtin-workflows/hello-verify.yaml +10 -0
- package/templates/builtin-workflows/judge-regression.yaml +114 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +60 -0
- package/templates/builtin-workflows/skills/hello-verify/SKILL.md +6 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +255 -0
- package/templates/guard-kit/check.mjs.template +777 -0
- package/templates/guard-kit/config.json.template +6 -0
- package/templates/guard-kit/poetic-guard.yml.template +189 -0
- package/templates/profiles/README.md +56 -0
- package/templates/profiles/frontier-claude.json +27 -0
- package/templates/profiles/frontier-codex.json +26 -0
|
@@ -0,0 +1,2344 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findLabRun,
|
|
3
|
+
findLatestLabRun
|
|
4
|
+
} from "./chunk-ROMHOSMQ.js";
|
|
5
|
+
import {
|
|
6
|
+
renderJsonError,
|
|
7
|
+
renderJsonResult
|
|
8
|
+
} from "./chunk-Q3RO7N35.js";
|
|
9
|
+
import {
|
|
10
|
+
UNIFIED_COST_METRIC_SOURCE_SAMPLES
|
|
11
|
+
} from "./chunk-NJVD4PWE.js";
|
|
12
|
+
import {
|
|
13
|
+
ensurePendingInvocationCostEnrichment,
|
|
14
|
+
formatCostProjectionNotice,
|
|
15
|
+
fromVisibilitySummary,
|
|
16
|
+
projectCostVisibility,
|
|
17
|
+
summarizeInvocationCostVisibility
|
|
18
|
+
} from "./chunk-GKN3HMN4.js";
|
|
19
|
+
import {
|
|
20
|
+
parseDuration
|
|
21
|
+
} from "./chunk-TSXGRLPR.js";
|
|
22
|
+
import {
|
|
23
|
+
ValidationError
|
|
24
|
+
} from "./chunk-LUVKFFWR.js";
|
|
25
|
+
import {
|
|
26
|
+
BooleanFlagSchema,
|
|
27
|
+
FilePathSchema,
|
|
28
|
+
ModelNameSchema,
|
|
29
|
+
PositiveIntegerSchema,
|
|
30
|
+
createSafeErrorMessage,
|
|
31
|
+
formatValidationErrors,
|
|
32
|
+
validateCommandOptions
|
|
33
|
+
} from "./chunk-QOCOVLUH.js";
|
|
34
|
+
import {
|
|
35
|
+
checkAndWarnLabContext
|
|
36
|
+
} from "./chunk-7GDRQBQC.js";
|
|
37
|
+
import {
|
|
38
|
+
emitWarning
|
|
39
|
+
} from "./chunk-EJGCZGUF.js";
|
|
40
|
+
|
|
41
|
+
// src/cli/commands/cost.ts
|
|
42
|
+
import fs from "fs";
|
|
43
|
+
import path from "path";
|
|
44
|
+
import chalk from "chalk";
|
|
45
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
46
|
+
import { z } from "zod";
|
|
47
|
+
|
|
48
|
+
// src/cost-tracking/pricing-verifier.ts
|
|
49
|
+
function isExpectedFreeZeroCostModel(model) {
|
|
50
|
+
if (!model) return false;
|
|
51
|
+
return /:free(?:$|[:/])/i.test(model) || model.toLowerCase().endsWith(":free");
|
|
52
|
+
}
|
|
53
|
+
function isExpectedFreeZeroCostWarning(warning) {
|
|
54
|
+
if (warning.type !== "zero_cost") return false;
|
|
55
|
+
if (warning.details?.expectedFreeCatalog === true) return true;
|
|
56
|
+
return isExpectedFreeZeroCostModel(warning.model);
|
|
57
|
+
}
|
|
58
|
+
var PricingVerifier = class {
|
|
59
|
+
calculator;
|
|
60
|
+
logger;
|
|
61
|
+
config;
|
|
62
|
+
costHistory = /* @__PURE__ */ new Map();
|
|
63
|
+
constructor(calculator, logger, config) {
|
|
64
|
+
this.calculator = calculator;
|
|
65
|
+
this.logger = logger;
|
|
66
|
+
this.config = {
|
|
67
|
+
maxAgeDays: 30,
|
|
68
|
+
enableFreshnessChecks: true,
|
|
69
|
+
enableAnomalyDetection: true,
|
|
70
|
+
anomalyThresholdPercent: 50,
|
|
71
|
+
...config
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Verify pricing configuration for a specific provider
|
|
76
|
+
*/
|
|
77
|
+
async verifyProvider(provider) {
|
|
78
|
+
const warnings = [];
|
|
79
|
+
const errors = [];
|
|
80
|
+
try {
|
|
81
|
+
this.calculator.ensurePricingLoadedSync();
|
|
82
|
+
const models = this.calculator.getAvailableModels(provider);
|
|
83
|
+
if (models.length === 0) {
|
|
84
|
+
const availability = this.getProviderPricingAvailability(provider);
|
|
85
|
+
if (isAdvisoryMissingPricing(availability)) {
|
|
86
|
+
warnings.push({
|
|
87
|
+
type: "pricing_unavailable",
|
|
88
|
+
severity: "medium",
|
|
89
|
+
message: `Provider "${provider}" has a model catalog but no per-token pricing because billing is ${availability.billingMode}`,
|
|
90
|
+
provider,
|
|
91
|
+
details: { availability }
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
valid: true,
|
|
95
|
+
warnings,
|
|
96
|
+
errors,
|
|
97
|
+
metadata: {
|
|
98
|
+
version: "unknown",
|
|
99
|
+
providersChecked: [provider],
|
|
100
|
+
modelsChecked: 0
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
errors.push({
|
|
105
|
+
type: "missing_pricing",
|
|
106
|
+
message: `No models found for provider "${provider}"`,
|
|
107
|
+
provider
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
warnings,
|
|
112
|
+
errors,
|
|
113
|
+
metadata: {
|
|
114
|
+
version: "unknown",
|
|
115
|
+
providersChecked: [provider],
|
|
116
|
+
modelsChecked: 0
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
for (const model of models) {
|
|
121
|
+
const modelWarnings = this.verifyModelPricing(provider, model);
|
|
122
|
+
warnings.push(...modelWarnings);
|
|
123
|
+
}
|
|
124
|
+
if (this.config.enableFreshnessChecks) {
|
|
125
|
+
const freshnessWarnings = this.checkPricingFreshness(provider);
|
|
126
|
+
warnings.push(...freshnessWarnings);
|
|
127
|
+
}
|
|
128
|
+
const freshness = resolveProviderFreshness(
|
|
129
|
+
this.calculator.getDynamicPricingManifest?.() ?? null,
|
|
130
|
+
provider
|
|
131
|
+
);
|
|
132
|
+
const ageDays = freshness.lastUpdated !== void 0 ? (Date.now() - new Date(freshness.lastUpdated).getTime()) / (1e3 * 60 * 60 * 24) : void 0;
|
|
133
|
+
const metadata = {
|
|
134
|
+
version: "unknown",
|
|
135
|
+
// TODO: Extract from calculator
|
|
136
|
+
providersChecked: [provider],
|
|
137
|
+
modelsChecked: models.length
|
|
138
|
+
};
|
|
139
|
+
if (freshness.lastUpdated !== void 0) {
|
|
140
|
+
metadata.lastUpdated = new Date(freshness.lastUpdated).toISOString();
|
|
141
|
+
}
|
|
142
|
+
if (ageDays !== void 0 && Number.isFinite(ageDays)) {
|
|
143
|
+
metadata.ageDays = Math.round(ageDays);
|
|
144
|
+
}
|
|
145
|
+
this.logger.logCostEvent("pricing_verification_complete", {
|
|
146
|
+
provider,
|
|
147
|
+
modelsChecked: models.length,
|
|
148
|
+
warningsCount: warnings.length,
|
|
149
|
+
errorsCount: errors.length
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
valid: errors.length === 0,
|
|
153
|
+
warnings,
|
|
154
|
+
errors,
|
|
155
|
+
metadata
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
+
errors.push({
|
|
160
|
+
type: "invalid_pricing",
|
|
161
|
+
message: `Pricing verification failed: ${message}`,
|
|
162
|
+
provider
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
valid: false,
|
|
166
|
+
warnings,
|
|
167
|
+
errors,
|
|
168
|
+
metadata: {
|
|
169
|
+
version: "unknown",
|
|
170
|
+
providersChecked: [provider],
|
|
171
|
+
modelsChecked: 0
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Verify pricing for a specific model
|
|
178
|
+
*/
|
|
179
|
+
verifyModelPricing(provider, model) {
|
|
180
|
+
const warnings = [];
|
|
181
|
+
try {
|
|
182
|
+
const pricing = this.getModelPricingSafe(provider, model);
|
|
183
|
+
if (!pricing) {
|
|
184
|
+
warnings.push({
|
|
185
|
+
type: "missing_model",
|
|
186
|
+
severity: "high",
|
|
187
|
+
message: `No pricing found for model "${model}"`,
|
|
188
|
+
provider,
|
|
189
|
+
model
|
|
190
|
+
});
|
|
191
|
+
return warnings;
|
|
192
|
+
}
|
|
193
|
+
if (pricing.inputTokens === 0 && pricing.outputTokens === 0 && pricing.billingMode === "metered" && !isIncludedPlanPricing(pricing)) {
|
|
194
|
+
const expectedFreeCatalog = isExpectedFreeZeroCostModel(model);
|
|
195
|
+
warnings.push({
|
|
196
|
+
type: "zero_cost",
|
|
197
|
+
severity: expectedFreeCatalog ? "low" : "high",
|
|
198
|
+
message: expectedFreeCatalog ? `Model "${model}" is a :free catalog entry with $0+$0 token prices` : `Model "${model}" has zero cost for input and output tokens`,
|
|
199
|
+
provider,
|
|
200
|
+
model,
|
|
201
|
+
details: { pricing, expectedFreeCatalog }
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const typicalMaxInputCost = 100;
|
|
205
|
+
const typicalMaxOutputCost = 500;
|
|
206
|
+
if (pricing.inputTokens > typicalMaxInputCost || pricing.outputTokens > typicalMaxOutputCost) {
|
|
207
|
+
warnings.push({
|
|
208
|
+
type: "high_cost_variance",
|
|
209
|
+
severity: "medium",
|
|
210
|
+
message: `Model "${model}" has unusually high token costs`,
|
|
211
|
+
provider,
|
|
212
|
+
model,
|
|
213
|
+
details: {
|
|
214
|
+
inputTokens: pricing.inputTokens,
|
|
215
|
+
outputTokens: pricing.outputTokens,
|
|
216
|
+
thresholds: { typicalMaxInputCost, typicalMaxOutputCost }
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
+
warnings.push({
|
|
223
|
+
type: "missing_model",
|
|
224
|
+
severity: "high",
|
|
225
|
+
message: `Failed to verify pricing for "${model}": ${message}`,
|
|
226
|
+
provider,
|
|
227
|
+
model
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return warnings;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Check if pricing data is fresh (not stale)
|
|
234
|
+
*/
|
|
235
|
+
checkPricingFreshness(provider) {
|
|
236
|
+
const warnings = [];
|
|
237
|
+
const manifest = this.calculator.getDynamicPricingManifest?.();
|
|
238
|
+
if (!manifest) {
|
|
239
|
+
return warnings;
|
|
240
|
+
}
|
|
241
|
+
const providerFreshness = resolveProviderFreshness(manifest, provider);
|
|
242
|
+
const timestamp = providerFreshness.lastSuccessfulFetch ?? providerFreshness.lastUpdated;
|
|
243
|
+
if (!timestamp) {
|
|
244
|
+
return warnings;
|
|
245
|
+
}
|
|
246
|
+
const updatedDate = new Date(timestamp);
|
|
247
|
+
if (Number.isNaN(updatedDate.getTime())) {
|
|
248
|
+
return warnings;
|
|
249
|
+
}
|
|
250
|
+
const ageDays = (Date.now() - updatedDate.getTime()) / (1e3 * 60 * 60 * 24);
|
|
251
|
+
if (providerFreshness.stale || ageDays > this.config.maxAgeDays) {
|
|
252
|
+
warnings.push({
|
|
253
|
+
type: "stale_data",
|
|
254
|
+
severity: providerFreshness.stale ? "high" : "medium",
|
|
255
|
+
message: providerFreshness.stale ? `Pricing data for "${provider}" is using stale fallback data from ${Math.round(ageDays)} days ago` : `Pricing data for "${provider}" is ${Math.round(ageDays)} days old`,
|
|
256
|
+
provider,
|
|
257
|
+
details: {
|
|
258
|
+
ageDays: Math.round(ageDays),
|
|
259
|
+
maxAgeDays: this.config.maxAgeDays,
|
|
260
|
+
stale: providerFreshness.stale,
|
|
261
|
+
failureReason: providerFreshness.failureReason
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return warnings;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Detect cost calculation anomalies
|
|
269
|
+
*/
|
|
270
|
+
detectAnomaly(provider, model, actualCost) {
|
|
271
|
+
if (!this.config.enableAnomalyDetection) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const historyKey = `${provider}:${model}`;
|
|
275
|
+
const history = this.costHistory.get(historyKey) || [];
|
|
276
|
+
if (history.length < 5) {
|
|
277
|
+
history.push(actualCost);
|
|
278
|
+
this.costHistory.set(historyKey, history);
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const mean = history.reduce((sum, cost) => sum + cost, 0) / history.length;
|
|
282
|
+
if (mean === 0) {
|
|
283
|
+
history.push(actualCost);
|
|
284
|
+
this.costHistory.set(historyKey, history);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const threshold = mean * (this.config.anomalyThresholdPercent / 100);
|
|
288
|
+
const expectedRange = {
|
|
289
|
+
min: mean - threshold,
|
|
290
|
+
max: mean + threshold
|
|
291
|
+
};
|
|
292
|
+
if (actualCost < expectedRange.min || actualCost > expectedRange.max) {
|
|
293
|
+
const deviationPercent = Math.abs(actualCost - mean) / mean * 100;
|
|
294
|
+
const alert = {
|
|
295
|
+
provider,
|
|
296
|
+
model,
|
|
297
|
+
expectedCostRange: expectedRange,
|
|
298
|
+
actualCost,
|
|
299
|
+
deviationPercent,
|
|
300
|
+
severity: deviationPercent > this.config.anomalyThresholdPercent * 2 ? "critical" : "warning",
|
|
301
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
302
|
+
};
|
|
303
|
+
this.logger.logCostEvent("cost_anomaly_detected", {
|
|
304
|
+
provider,
|
|
305
|
+
model,
|
|
306
|
+
actualCost,
|
|
307
|
+
expectedMin: expectedRange.min,
|
|
308
|
+
expectedMax: expectedRange.max,
|
|
309
|
+
deviationPercent,
|
|
310
|
+
severity: alert.severity
|
|
311
|
+
});
|
|
312
|
+
return alert;
|
|
313
|
+
}
|
|
314
|
+
history.push(actualCost);
|
|
315
|
+
if (history.length > 20) {
|
|
316
|
+
history.shift();
|
|
317
|
+
}
|
|
318
|
+
this.costHistory.set(historyKey, history);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Safe method to get model pricing without throwing
|
|
323
|
+
*/
|
|
324
|
+
getModelPricingSafe(provider, model) {
|
|
325
|
+
try {
|
|
326
|
+
const calculator = this.calculator;
|
|
327
|
+
if (typeof calculator.getModelPricingWithFallback === "function") {
|
|
328
|
+
return calculator.getModelPricingWithFallback(provider, model);
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
getProviderPricingAvailability(provider) {
|
|
336
|
+
const calculator = this.calculator;
|
|
337
|
+
if (typeof calculator.getProviderPricingAvailability !== "function") {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
return calculator.getProviderPricingAvailability(
|
|
342
|
+
provider
|
|
343
|
+
);
|
|
344
|
+
} catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Reset cost history (useful for testing or manual reset)
|
|
350
|
+
*/
|
|
351
|
+
resetHistory() {
|
|
352
|
+
this.costHistory.clear();
|
|
353
|
+
this.logger.logCostEvent("pricing_verifier_history_reset", {
|
|
354
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
function isIncludedPlanPricing(pricing) {
|
|
359
|
+
const explicitIncluded = pricing.billingSource === "included_plan" || pricing.pricingSource === "subscription_included";
|
|
360
|
+
if (explicitIncluded) {
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
const hasExplicitProvenance = pricing.billingSource !== void 0 || pricing.pricingSource !== void 0 && pricing.pricingSource !== "yaml" && pricing.pricingSource !== "dynamic";
|
|
364
|
+
if (hasExplicitProvenance) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return hasIncludedPlanNote(pricing.pricingNotes);
|
|
368
|
+
}
|
|
369
|
+
function isAdvisoryMissingPricing(availability) {
|
|
370
|
+
if (!availability?.hasModelCatalog || availability.hasTokenPricing) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return availability.billingMode === "flat_rate" || availability.billingMode === "non_metered";
|
|
374
|
+
}
|
|
375
|
+
function hasIncludedPlanNote(note) {
|
|
376
|
+
if (!note) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
const normalized = note.toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
380
|
+
return /\bincluded\s+(with|in|under)\b.{0,80}\b(plan|subscription|license|seat)\b/.test(normalized) || /\bincluded\s+as\s+part\s+of\b.{0,80}\b(plan|subscription|license|seat)\b/.test(normalized) || /\b(plan|subscription)\s+included\b/.test(normalized);
|
|
381
|
+
}
|
|
382
|
+
function resolveProviderFreshness(manifest, provider) {
|
|
383
|
+
if (!manifest) {
|
|
384
|
+
return { stale: false };
|
|
385
|
+
}
|
|
386
|
+
const providerLower = provider.toLowerCase();
|
|
387
|
+
const providerEntry = manifest.providers.find(
|
|
388
|
+
(entry) => entry.providerId === provider || entry.providerId.toLowerCase() === providerLower
|
|
389
|
+
);
|
|
390
|
+
if (!providerEntry) {
|
|
391
|
+
return {
|
|
392
|
+
lastUpdated: manifest.generatedAt,
|
|
393
|
+
stale: false
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
lastUpdated: providerEntry.lastUpdated,
|
|
398
|
+
lastSuccessfulFetch: providerEntry.lastSuccessfulFetch,
|
|
399
|
+
stale: providerEntry.stale,
|
|
400
|
+
failureReason: providerEntry.failureReason
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/cli/formatters/currency.ts
|
|
405
|
+
var formatterCache = /* @__PURE__ */ new Map();
|
|
406
|
+
function getFormatter(currency) {
|
|
407
|
+
const normalized = currency.toUpperCase();
|
|
408
|
+
let formatter = formatterCache.get(normalized);
|
|
409
|
+
if (!formatter) {
|
|
410
|
+
try {
|
|
411
|
+
formatter = new Intl.NumberFormat("en-US", {
|
|
412
|
+
style: "currency",
|
|
413
|
+
currency: normalized,
|
|
414
|
+
minimumFractionDigits: 2,
|
|
415
|
+
maximumFractionDigits: 2
|
|
416
|
+
});
|
|
417
|
+
} catch {
|
|
418
|
+
formatter = new Intl.NumberFormat("en-US", {
|
|
419
|
+
minimumFractionDigits: 2,
|
|
420
|
+
maximumFractionDigits: 2
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
formatterCache.set(normalized, formatter);
|
|
424
|
+
}
|
|
425
|
+
return formatter;
|
|
426
|
+
}
|
|
427
|
+
var subcentFormatterCache = /* @__PURE__ */ new Map();
|
|
428
|
+
function getSubcentFormatter(currency) {
|
|
429
|
+
const normalized = currency.toUpperCase();
|
|
430
|
+
let formatter = subcentFormatterCache.get(normalized);
|
|
431
|
+
if (!formatter) {
|
|
432
|
+
try {
|
|
433
|
+
formatter = new Intl.NumberFormat("en-US", {
|
|
434
|
+
style: "currency",
|
|
435
|
+
currency: normalized,
|
|
436
|
+
minimumFractionDigits: 4,
|
|
437
|
+
maximumFractionDigits: 4
|
|
438
|
+
});
|
|
439
|
+
} catch {
|
|
440
|
+
formatter = new Intl.NumberFormat("en-US", {
|
|
441
|
+
minimumFractionDigits: 4,
|
|
442
|
+
maximumFractionDigits: 4
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
subcentFormatterCache.set(normalized, formatter);
|
|
446
|
+
}
|
|
447
|
+
return formatter;
|
|
448
|
+
}
|
|
449
|
+
function formatCurrency(amount, currency = "USD") {
|
|
450
|
+
const formatter = getFormatter(currency);
|
|
451
|
+
return formatter.format(amount);
|
|
452
|
+
}
|
|
453
|
+
function formatCostDisplay(amount, currency = "USD") {
|
|
454
|
+
if (amount !== 0 && Math.abs(amount) < 0.01) {
|
|
455
|
+
return getSubcentFormatter(currency).format(amount);
|
|
456
|
+
}
|
|
457
|
+
return getFormatter(currency).format(amount);
|
|
458
|
+
}
|
|
459
|
+
function formatCurrencyDelta(amount, currency = "USD") {
|
|
460
|
+
const formatted = formatCurrency(Math.abs(amount), currency);
|
|
461
|
+
if (amount > 0) {
|
|
462
|
+
return `+${formatted}`;
|
|
463
|
+
}
|
|
464
|
+
if (amount < 0) {
|
|
465
|
+
return `-${formatted}`;
|
|
466
|
+
}
|
|
467
|
+
return formatted;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/cli/formatters/tokens.ts
|
|
471
|
+
var numberFormatter = new Intl.NumberFormat("en-US");
|
|
472
|
+
function formatTokenCount(tokens) {
|
|
473
|
+
return `${numberFormatter.format(tokens)} tokens`;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/cli/commands/cost.ts
|
|
477
|
+
function writeCostOutput(value) {
|
|
478
|
+
console.log(value);
|
|
479
|
+
}
|
|
480
|
+
function logCostEmptyStateHint(modelInvocationsDb, startTimestampMs, attempt) {
|
|
481
|
+
const projection = projectCostVisibility(modelInvocationsDb, startTimestampMs, attempt);
|
|
482
|
+
const notice = formatCostProjectionNotice(projection);
|
|
483
|
+
if (notice) {
|
|
484
|
+
writeCostOutput(chalk.gray(notice));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function formatDailyAggregationCost(value) {
|
|
488
|
+
return value === null ? "incomplete" : formatCostDisplay(value);
|
|
489
|
+
}
|
|
490
|
+
function sumCompleteDailyCosts(rows) {
|
|
491
|
+
let total = 0;
|
|
492
|
+
for (const row of rows) {
|
|
493
|
+
if (row.totalCost === null) {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
total += row.totalCost;
|
|
497
|
+
}
|
|
498
|
+
return total;
|
|
499
|
+
}
|
|
500
|
+
var ToolUsageSchema = z.object({
|
|
501
|
+
name: z.string().min(1, "Tool name cannot be empty").max(100, "Tool name too long").regex(/^[a-zA-Z0-9._-]+$/, "Invalid tool name format"),
|
|
502
|
+
estimatedRequests: PositiveIntegerSchema
|
|
503
|
+
});
|
|
504
|
+
var StatusOptionsSchema = z.object({
|
|
505
|
+
json: BooleanFlagSchema.optional(),
|
|
506
|
+
swarm: BooleanFlagSchema.optional(),
|
|
507
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
508
|
+
outputFile: FilePathSchema.optional(),
|
|
509
|
+
since: z.string().regex(/^\d+[mhdw]$/, "--since must match pattern like 7d or 24h").optional(),
|
|
510
|
+
lab: z.union([z.string(), z.boolean()]).optional(),
|
|
511
|
+
labId: z.string().optional(),
|
|
512
|
+
runId: z.string().optional(),
|
|
513
|
+
verbose: BooleanFlagSchema.optional()
|
|
514
|
+
}).strict().superRefine((data, ctx) => {
|
|
515
|
+
if (data.lab && (data.labId || data.runId)) {
|
|
516
|
+
ctx.addIssue({
|
|
517
|
+
code: z.ZodIssueCode.custom,
|
|
518
|
+
message: "--lab cannot be combined with --lab-id/--run-id"
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
if (data.labId && !data.runId) {
|
|
522
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "--lab-id requires --run-id" });
|
|
523
|
+
}
|
|
524
|
+
if (data.runId && !data.labId) {
|
|
525
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "--run-id requires --lab-id" });
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
var PredictOptionsSchema = z.object({
|
|
529
|
+
input: PositiveIntegerSchema,
|
|
530
|
+
output: PositiveIntegerSchema,
|
|
531
|
+
tool: z.array(ToolUsageSchema).optional(),
|
|
532
|
+
json: BooleanFlagSchema.optional()
|
|
533
|
+
}).strict();
|
|
534
|
+
var CompareOptionsSchema = z.object({
|
|
535
|
+
input: PositiveIntegerSchema,
|
|
536
|
+
output: PositiveIntegerSchema,
|
|
537
|
+
json: BooleanFlagSchema.optional()
|
|
538
|
+
}).strict();
|
|
539
|
+
var DailyOptionsSchema = z.object({
|
|
540
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
541
|
+
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Start date must be in YYYY-MM-DD format").optional(),
|
|
542
|
+
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "End date must be in YYYY-MM-DD format").optional(),
|
|
543
|
+
group: z.enum(["date", "provider", "model"]).optional().default("date"),
|
|
544
|
+
json: BooleanFlagSchema.optional()
|
|
545
|
+
}).strict().refine((data) => {
|
|
546
|
+
if (data.start && data.end) {
|
|
547
|
+
const start = new Date(data.start);
|
|
548
|
+
const end = new Date(data.end);
|
|
549
|
+
return start <= end;
|
|
550
|
+
}
|
|
551
|
+
return true;
|
|
552
|
+
}, "Start date cannot be after end date");
|
|
553
|
+
var ForecastOptionsSchema = z.object({
|
|
554
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
555
|
+
horizon: z.string().regex(/^\d+$/, "Horizon must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
556
|
+
json: BooleanFlagSchema.optional()
|
|
557
|
+
}).strict();
|
|
558
|
+
var HistoryOptionsSchema = z.object({
|
|
559
|
+
task: z.string().min(1).max(100).optional(),
|
|
560
|
+
competition: z.string().min(1).max(100).optional(),
|
|
561
|
+
recalculate: z.union([
|
|
562
|
+
z.enum(["current", "snapshot"]),
|
|
563
|
+
z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format")
|
|
564
|
+
]).optional(),
|
|
565
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
566
|
+
json: BooleanFlagSchema.optional()
|
|
567
|
+
}).strict();
|
|
568
|
+
var TasksOptionsSchema = z.object({
|
|
569
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
570
|
+
top: z.string().regex(/^\d+$/, "Top must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(10),
|
|
571
|
+
json: BooleanFlagSchema.optional()
|
|
572
|
+
}).strict();
|
|
573
|
+
var TokensOptionsSchema = z.object({
|
|
574
|
+
days: z.string().regex(/^\d+$/, "Days must be a positive integer").transform(Number).pipe(PositiveIntegerSchema).optional().default(7),
|
|
575
|
+
provider: z.string().min(1).max(100).optional(),
|
|
576
|
+
model: ModelNameSchema.optional(),
|
|
577
|
+
json: BooleanFlagSchema.optional()
|
|
578
|
+
}).strict();
|
|
579
|
+
var ComparePricingOptionsSchema = z.object({
|
|
580
|
+
json: BooleanFlagSchema.optional()
|
|
581
|
+
}).strict();
|
|
582
|
+
function mergePersistedCostMetrics(metrics, recentExecutions, now = Date.now()) {
|
|
583
|
+
if (recentExecutions.length === 0) {
|
|
584
|
+
return metrics;
|
|
585
|
+
}
|
|
586
|
+
const sourceSamples = metrics[UNIFIED_COST_METRIC_SOURCE_SAMPLES] ?? [];
|
|
587
|
+
const remainingSourceSamples = sourceSamples.map((sample) => ({
|
|
588
|
+
...sample,
|
|
589
|
+
// Use sub-cent precision (6 dp) to match the stored CostSnapshot.totalCostCents,
|
|
590
|
+
// which is REAL since i29 fix (12a0435bb). Whole-cent rounding here caused
|
|
591
|
+
// sub-cent invocations (< 0.5 cent) to produce totalCostCents=0 in the
|
|
592
|
+
// deduplication key, mismatching the persisted record and double-counting them.
|
|
593
|
+
totalCostCents: Math.round(sample.totalCost * 100 * 1e6) / 1e6
|
|
594
|
+
}));
|
|
595
|
+
const nonOverlappingExecutions = recentExecutions.filter((execution) => {
|
|
596
|
+
const executionCostCents = execution.costSnapshot.totalCostCents;
|
|
597
|
+
const exactExecutionIdIndex = remainingSourceSamples.findIndex(
|
|
598
|
+
(sample) => sample.executionId && sample.executionId === execution.executionId
|
|
599
|
+
);
|
|
600
|
+
if (exactExecutionIdIndex >= 0) {
|
|
601
|
+
remainingSourceSamples.splice(exactExecutionIdIndex, 1);
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
const matchingSampleIndex = remainingSourceSamples.findIndex(
|
|
605
|
+
(sample) => sample.provider === execution.provider && sample.model === execution.model && sample.totalCostCents === executionCostCents && Math.abs(sample.timestamp - execution.timestamp.getTime()) <= 2e3
|
|
606
|
+
);
|
|
607
|
+
if (matchingSampleIndex >= 0) {
|
|
608
|
+
remainingSourceSamples.splice(matchingSampleIndex, 1);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
return true;
|
|
612
|
+
});
|
|
613
|
+
if (nonOverlappingExecutions.length === 0) {
|
|
614
|
+
return metrics;
|
|
615
|
+
}
|
|
616
|
+
const dayWindow = now - 24 * 60 * 60 * 1e3;
|
|
617
|
+
const monthWindow = now - 30 * 24 * 60 * 60 * 1e3;
|
|
618
|
+
const persistedDailyCost = nonOverlappingExecutions.filter((execution) => execution.timestamp.getTime() >= dayWindow).reduce((sum, execution) => sum + execution.costSnapshot.totalCostCents / 100, 0);
|
|
619
|
+
const persistedMonthlyCost = nonOverlappingExecutions.filter((execution) => execution.timestamp.getTime() >= monthWindow).reduce((sum, execution) => sum + execution.costSnapshot.totalCostCents / 100, 0);
|
|
620
|
+
const persistedWindowCost = nonOverlappingExecutions.reduce(
|
|
621
|
+
(sum, execution) => sum + execution.costSnapshot.totalCostCents / 100,
|
|
622
|
+
0
|
|
623
|
+
);
|
|
624
|
+
const byProviderModel = /* @__PURE__ */ new Map();
|
|
625
|
+
for (const execution of nonOverlappingExecutions) {
|
|
626
|
+
const key = `${execution.provider}::${execution.model}`;
|
|
627
|
+
const current = byProviderModel.get(key);
|
|
628
|
+
const cost = execution.costSnapshot.totalCostCents / 100;
|
|
629
|
+
if (current) {
|
|
630
|
+
current.cost += cost;
|
|
631
|
+
} else {
|
|
632
|
+
byProviderModel.set(key, {
|
|
633
|
+
provider: execution.provider,
|
|
634
|
+
model: execution.model,
|
|
635
|
+
cost
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const top = [...byProviderModel.values()].sort((a, b) => b.cost - a.cost)[0];
|
|
640
|
+
const dailyCost = metrics.dailyCost + persistedDailyCost;
|
|
641
|
+
const monthlyCost = metrics.monthlyCost + persistedMonthlyCost;
|
|
642
|
+
const sessionCost = metrics.sessionCost + persistedWindowCost;
|
|
643
|
+
return {
|
|
644
|
+
...metrics,
|
|
645
|
+
sessionCost,
|
|
646
|
+
dailyCost,
|
|
647
|
+
monthlyCost,
|
|
648
|
+
topCostProvider: top ? {
|
|
649
|
+
provider: top.provider,
|
|
650
|
+
model: top.model,
|
|
651
|
+
cost: top.cost,
|
|
652
|
+
percentage: sessionCost > 0 ? top.cost / sessionCost * 100 : 0
|
|
653
|
+
} : metrics.topCostProvider,
|
|
654
|
+
costTrend: metrics.costTrend.length > 0 ? metrics.costTrend : nonOverlappingExecutions.slice(-10).map((execution) => execution.costSnapshot.totalCostCents / 100)
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
async function verifyPricingForStatus(costCalculator, telemetry) {
|
|
658
|
+
const calculatorWithProviders = costCalculator;
|
|
659
|
+
if (typeof calculatorWithProviders.getAvailableProviders !== "function") {
|
|
660
|
+
return { valid: true, providersChecked: [], warnings: [], errors: [] };
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
const providers = calculatorWithProviders.getAvailableProviders();
|
|
664
|
+
const verifier = new PricingVerifier(costCalculator, telemetry);
|
|
665
|
+
const results = await Promise.all(
|
|
666
|
+
providers.map((provider) => verifier.verifyProvider(provider))
|
|
667
|
+
);
|
|
668
|
+
return {
|
|
669
|
+
valid: results.every((result) => result.valid),
|
|
670
|
+
providersChecked: providers,
|
|
671
|
+
warnings: results.flatMap((result) => result.warnings),
|
|
672
|
+
errors: results.flatMap((result) => result.errors)
|
|
673
|
+
};
|
|
674
|
+
} catch (error) {
|
|
675
|
+
return {
|
|
676
|
+
valid: false,
|
|
677
|
+
providersChecked: [],
|
|
678
|
+
warnings: [],
|
|
679
|
+
errors: [
|
|
680
|
+
{
|
|
681
|
+
type: "calculation_error",
|
|
682
|
+
message: error instanceof Error ? error.message : String(error),
|
|
683
|
+
provider: "all"
|
|
684
|
+
}
|
|
685
|
+
]
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function resolveCostLabRun(options) {
|
|
690
|
+
if (options.lab) {
|
|
691
|
+
const selector = typeof options.lab === "string" ? options.lab.trim().toLowerCase() : options.lab === true ? "latest" : null;
|
|
692
|
+
if (!selector || selector === "latest") {
|
|
693
|
+
const latest = findLatestLabRun();
|
|
694
|
+
if (!latest) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"No lab runs found. Start a lab run with `poetic compete --lab` before querying cost status."
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
return latest;
|
|
700
|
+
}
|
|
701
|
+
throw new Error("Unsupported --lab value. Supported value: latest.");
|
|
702
|
+
}
|
|
703
|
+
if (options.labId && options.runId) {
|
|
704
|
+
const run = findLabRun(options.labId, options.runId);
|
|
705
|
+
if (!run) {
|
|
706
|
+
throw new Error(`Lab run not found: ${options.labId}/${options.runId}`);
|
|
707
|
+
}
|
|
708
|
+
return run;
|
|
709
|
+
}
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
function listJsonlFiles(dir) {
|
|
713
|
+
let entries;
|
|
714
|
+
try {
|
|
715
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
716
|
+
} catch {
|
|
717
|
+
return [];
|
|
718
|
+
}
|
|
719
|
+
const files = [];
|
|
720
|
+
for (const entry of entries) {
|
|
721
|
+
const filePath = path.join(dir, entry.name);
|
|
722
|
+
if (entry.isDirectory()) {
|
|
723
|
+
files.push(...listJsonlFiles(filePath));
|
|
724
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
725
|
+
files.push(filePath);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return files;
|
|
729
|
+
}
|
|
730
|
+
function readLabCostSummary(run, startTimeMs) {
|
|
731
|
+
const executions = [];
|
|
732
|
+
for (const filePath of listJsonlFiles(path.join(run.runPath, "logs"))) {
|
|
733
|
+
let contents;
|
|
734
|
+
try {
|
|
735
|
+
contents = fs.readFileSync(filePath, "utf8");
|
|
736
|
+
} catch {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
for (const line of contents.split(/\n/)) {
|
|
740
|
+
if (!line.trim()) continue;
|
|
741
|
+
let record;
|
|
742
|
+
try {
|
|
743
|
+
record = JSON.parse(line);
|
|
744
|
+
} catch {
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (record.event !== "model_invocation") continue;
|
|
748
|
+
const timestamp = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : typeof record.timestamp === "number" ? record.timestamp : Number.NaN;
|
|
749
|
+
if (!Number.isFinite(timestamp) || timestamp < startTimeMs) continue;
|
|
750
|
+
const totalCost = typeof record.totalCost === "number" ? record.totalCost : 0;
|
|
751
|
+
if (!Number.isFinite(totalCost)) continue;
|
|
752
|
+
executions.push({
|
|
753
|
+
timestamp: new Date(timestamp),
|
|
754
|
+
provider: typeof record.provider === "string" ? record.provider : "unknown",
|
|
755
|
+
model: typeof record.model === "string" ? record.model : "unknown",
|
|
756
|
+
totalCost
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return { run, executions };
|
|
761
|
+
}
|
|
762
|
+
function mergeLabCostMetrics(metrics, summary, now = Date.now()) {
|
|
763
|
+
const dayWindow = now - 24 * 60 * 60 * 1e3;
|
|
764
|
+
const monthWindow = now - 30 * 24 * 60 * 60 * 1e3;
|
|
765
|
+
const sessionCost = summary.executions.reduce((sum, execution) => sum + execution.totalCost, 0);
|
|
766
|
+
const dailyCost = summary.executions.filter((execution) => execution.timestamp.getTime() >= dayWindow).reduce((sum, execution) => sum + execution.totalCost, 0);
|
|
767
|
+
const monthlyCost = summary.executions.filter((execution) => execution.timestamp.getTime() >= monthWindow).reduce((sum, execution) => sum + execution.totalCost, 0);
|
|
768
|
+
const byProviderModel = /* @__PURE__ */ new Map();
|
|
769
|
+
for (const execution of summary.executions) {
|
|
770
|
+
const key = `${execution.provider}::${execution.model}`;
|
|
771
|
+
const current = byProviderModel.get(key);
|
|
772
|
+
if (current) {
|
|
773
|
+
current.cost += execution.totalCost;
|
|
774
|
+
} else {
|
|
775
|
+
byProviderModel.set(key, {
|
|
776
|
+
provider: execution.provider,
|
|
777
|
+
model: execution.model,
|
|
778
|
+
cost: execution.totalCost
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const top = [...byProviderModel.values()].sort((a, b) => b.cost - a.cost)[0];
|
|
783
|
+
return {
|
|
784
|
+
...metrics,
|
|
785
|
+
sessionCost,
|
|
786
|
+
dailyCost,
|
|
787
|
+
monthlyCost,
|
|
788
|
+
topCostProvider: top ? {
|
|
789
|
+
provider: top.provider,
|
|
790
|
+
model: top.model,
|
|
791
|
+
cost: top.cost,
|
|
792
|
+
percentage: sessionCost > 0 ? top.cost / sessionCost * 100 : 0
|
|
793
|
+
} : {
|
|
794
|
+
provider: "none",
|
|
795
|
+
model: "none",
|
|
796
|
+
cost: 0,
|
|
797
|
+
percentage: 0
|
|
798
|
+
},
|
|
799
|
+
costTrend: summary.executions.slice(-10).map((execution) => execution.totalCost)
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function parseNonNegativeInteger(value) {
|
|
803
|
+
const parsed = Number.parseInt(value, 10);
|
|
804
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
805
|
+
throw new InvalidArgumentError("Value must be a non-negative integer");
|
|
806
|
+
}
|
|
807
|
+
return parsed;
|
|
808
|
+
}
|
|
809
|
+
function collectRawToolUsage(value, previous = []) {
|
|
810
|
+
return [...previous, value];
|
|
811
|
+
}
|
|
812
|
+
function parseWholeNonNegativeInteger(value, message) {
|
|
813
|
+
if (!/^\d+$/.test(value)) {
|
|
814
|
+
throw new InvalidArgumentError(message);
|
|
815
|
+
}
|
|
816
|
+
return Number(value);
|
|
817
|
+
}
|
|
818
|
+
function parsePredictTokenOption(value) {
|
|
819
|
+
return parseWholeNonNegativeInteger(value, "Value must be a non-negative integer");
|
|
820
|
+
}
|
|
821
|
+
function parsePredictToolUsageEntry(value) {
|
|
822
|
+
const [name, requests] = value.split(":");
|
|
823
|
+
if (!name || !requests) {
|
|
824
|
+
throw new InvalidArgumentError("Tool usage must be formatted as name:requests");
|
|
825
|
+
}
|
|
826
|
+
const parsed = parseWholeNonNegativeInteger(
|
|
827
|
+
requests,
|
|
828
|
+
"Tool requests must be a non-negative integer"
|
|
829
|
+
);
|
|
830
|
+
return { name, estimatedRequests: parsed };
|
|
831
|
+
}
|
|
832
|
+
function toPredictJsonError(error) {
|
|
833
|
+
if (error instanceof InvalidArgumentError) {
|
|
834
|
+
return new ValidationError(error.message);
|
|
835
|
+
}
|
|
836
|
+
if (error instanceof z.ZodError) {
|
|
837
|
+
return new ValidationError(formatValidationErrors(error));
|
|
838
|
+
}
|
|
839
|
+
if (error instanceof Error && error.message.startsWith("Invalid command options")) {
|
|
840
|
+
return new ValidationError(error.message);
|
|
841
|
+
}
|
|
842
|
+
return error;
|
|
843
|
+
}
|
|
844
|
+
function toToolUsage(estimates) {
|
|
845
|
+
return estimates.map((estimate) => ({
|
|
846
|
+
name: estimate.name,
|
|
847
|
+
requests: estimate.estimatedRequests
|
|
848
|
+
}));
|
|
849
|
+
}
|
|
850
|
+
function printCostBreakdown(label, breakdown) {
|
|
851
|
+
console.log(chalk.bold.cyan(`
|
|
852
|
+
${label}
|
|
853
|
+
`));
|
|
854
|
+
console.log(`${chalk.gray("Provider")}: ${breakdown.provider}`);
|
|
855
|
+
console.log(`${chalk.gray("Model")}: ${breakdown.model}`);
|
|
856
|
+
if (isPricingUnavailable(breakdown)) {
|
|
857
|
+
console.log(`${chalk.gray("Total Cost")}: unavailable`);
|
|
858
|
+
console.log(`${chalk.gray("Breakdown")}: unavailable`);
|
|
859
|
+
} else {
|
|
860
|
+
const total = breakdown.totalCostAmount.status === "available" ? breakdown.totalCostAmount.amount : breakdown.totalCost;
|
|
861
|
+
console.log(`${chalk.gray("Total Cost")}: ${formatCurrency(total, breakdown.currency)}`);
|
|
862
|
+
console.log(
|
|
863
|
+
`${chalk.gray("Breakdown")}: input ${formatCurrency(breakdown.inputCost, breakdown.currency)} | output ${formatCurrency(breakdown.outputCost, breakdown.currency)} | cache ${formatCurrency(breakdown.cacheCost, breakdown.currency)} | tools ${formatCurrency(breakdown.toolCosts, breakdown.currency)} | infra ${formatCurrency(breakdown.infrastructureCost, breakdown.currency)}`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
console.log(`${chalk.gray("Pricing Version")}: ${breakdown.pricingVersion}`);
|
|
867
|
+
if (isPricingUnavailable(breakdown)) {
|
|
868
|
+
console.log(chalk.yellow(`${chalk.gray("Pricing Status")}: unavailable`));
|
|
869
|
+
if (breakdown.pricingError) {
|
|
870
|
+
console.log(chalk.yellow(`${chalk.gray("Pricing Error")}: ${breakdown.pricingError}`));
|
|
871
|
+
}
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (breakdown.billingMode !== "metered") {
|
|
875
|
+
const modeDescription = breakdown.billingMode === "non_metered" ? "non-metered" : breakdown.billingMode.replace(/_/g, " ");
|
|
876
|
+
console.log(
|
|
877
|
+
chalk.yellow(
|
|
878
|
+
`Note: ${breakdown.provider}/${breakdown.model} is tracked as ${modeDescription}. Per-token cost fields are shown as $0 because billing is handled via seat or flat-rate licensing.`
|
|
879
|
+
)
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function isPricingUnavailable(breakdown) {
|
|
884
|
+
return breakdown.pricingStatus === "unavailable" || breakdown.totalCostAmount.status !== "available";
|
|
885
|
+
}
|
|
886
|
+
function availableBreakdownUsd(breakdown) {
|
|
887
|
+
return breakdown.totalCostAmount.status === "available" ? breakdown.totalCostAmount.amount : null;
|
|
888
|
+
}
|
|
889
|
+
function createCostCommand(deps) {
|
|
890
|
+
const cost = new Command("cost");
|
|
891
|
+
cost.alias("$");
|
|
892
|
+
cost.description(
|
|
893
|
+
"Cost tracking, forecasting, and comparison utilities\n\nExamples:\n poetic cost status\n poetic cost predict cursor auto --input 1000 --output 500\n poetic cost compare cursor:auto cursor:auto --input 1000 --output 500"
|
|
894
|
+
);
|
|
895
|
+
cost.command("status").description(
|
|
896
|
+
"Show current real-time cost tracking metrics\n\nExamples:\n poetic cost status\n poetic cost status --swarm --days 14"
|
|
897
|
+
).option("--json", "(OPTIONAL) Output metrics as JSON").option("-v, --verbose", "(OPTIONAL) Include unused OpenRouter :free catalog $0+$0 warnings").option(
|
|
898
|
+
"--swarm",
|
|
899
|
+
"(OPTIONAL) Show cost breakdown by agent role (for swarm executions)\n Example: --swarm"
|
|
900
|
+
).option(
|
|
901
|
+
"--days <days>",
|
|
902
|
+
"(OPTIONAL) Number of days to look back for swarm costs (default: 7)\n Example: --days 14",
|
|
903
|
+
"7"
|
|
904
|
+
).option(
|
|
905
|
+
"--since <window>",
|
|
906
|
+
"(OPTIONAL) Filter cost data to a time window (e.g. 7d, 24h, 30m, 1w)\n Example: --since 7d"
|
|
907
|
+
).option(
|
|
908
|
+
"--output-file <path>",
|
|
909
|
+
"(OPTIONAL) Save the cost report as JSON to the specified file path\n Example: --output-file cost-report.json"
|
|
910
|
+
).option("--lab [selector]", "(OPTIONAL) Use lab cost data (value: latest)").option("--lab-id <labId>", "(OPTIONAL) Specify lab ID (requires --run-id)").option("--run-id <runId>", "(OPTIONAL) Specify run ID (requires --lab-id)").action(async (options) => {
|
|
911
|
+
const validatedOptions = validateCommandOptions(StatusOptionsSchema, options);
|
|
912
|
+
const costTracker = await deps.costTracker;
|
|
913
|
+
const telemetry = await deps.telemetry;
|
|
914
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
915
|
+
const costCalculator = await deps.costCalculator;
|
|
916
|
+
if (telemetry.databaseModule && !validatedOptions.json) {
|
|
917
|
+
telemetry.databaseModule.printDbBanner();
|
|
918
|
+
}
|
|
919
|
+
if (!validatedOptions.json) {
|
|
920
|
+
checkAndWarnLabContext();
|
|
921
|
+
}
|
|
922
|
+
if (!costTracker) {
|
|
923
|
+
console.error(chalk.red("Cost tracker not available"));
|
|
924
|
+
process.exit(1);
|
|
925
|
+
}
|
|
926
|
+
let startTime;
|
|
927
|
+
let windowLabel;
|
|
928
|
+
if (validatedOptions.since) {
|
|
929
|
+
const parsedMs = parseDuration(validatedOptions.since);
|
|
930
|
+
startTime = Date.now() - (parsedMs ?? 7 * 24 * 60 * 60 * 1e3);
|
|
931
|
+
windowLabel = validatedOptions.since;
|
|
932
|
+
} else {
|
|
933
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
934
|
+
startTime = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
935
|
+
windowLabel = `${daysBack} days`;
|
|
936
|
+
}
|
|
937
|
+
const labRun = resolveCostLabRun({
|
|
938
|
+
lab: validatedOptions.lab,
|
|
939
|
+
labId: validatedOptions.labId,
|
|
940
|
+
runId: validatedOptions.runId
|
|
941
|
+
});
|
|
942
|
+
const enrichmentAttempt = await ensurePendingInvocationCostEnrichment(modelInvocationsDb);
|
|
943
|
+
const labCostSummary = labRun ? readLabCostSummary(labRun, startTime) : null;
|
|
944
|
+
const recentExecutions = labCostSummary ? [] : modelInvocationsDb.getExecutionCostsSince(startTime);
|
|
945
|
+
const metrics = labCostSummary ? mergeLabCostMetrics(costTracker.getCurrentMetrics(), labCostSummary) : mergePersistedCostMetrics(costTracker.getCurrentMetrics(), recentExecutions);
|
|
946
|
+
const pricingVerification = await verifyPricingForStatus(costCalculator, telemetry);
|
|
947
|
+
const invocationVisibility = labCostSummary ? {
|
|
948
|
+
rawInvocationCount: labCostSummary.executions.length,
|
|
949
|
+
enrichedInvocationCount: labCostSummary.executions.length,
|
|
950
|
+
pendingEnrichmentCount: 0,
|
|
951
|
+
visibility: labCostSummary.executions.length > 0 ? "enriched" : "no_invocations",
|
|
952
|
+
hint: labCostSummary.executions.length > 0 ? void 0 : "No lab model invocations recorded in this window."
|
|
953
|
+
} : summarizeInvocationCostVisibility(modelInvocationsDb, startTime);
|
|
954
|
+
const costProjection = fromVisibilitySummary(
|
|
955
|
+
invocationVisibility,
|
|
956
|
+
labCostSummary ? void 0 : enrichmentAttempt
|
|
957
|
+
);
|
|
958
|
+
const pricingWarningsForView = validatedOptions.verbose ? pricingVerification.warnings : pricingVerification.warnings.filter((warning) => !isExpectedFreeZeroCostWarning(warning));
|
|
959
|
+
const pricingVerificationForView = {
|
|
960
|
+
...pricingVerification,
|
|
961
|
+
warnings: pricingWarningsForView
|
|
962
|
+
};
|
|
963
|
+
const statusPayload = {
|
|
964
|
+
...metrics,
|
|
965
|
+
pricingVerification: pricingVerificationForView,
|
|
966
|
+
invocationVisibility,
|
|
967
|
+
costProjection,
|
|
968
|
+
labContext: labCostSummary ? {
|
|
969
|
+
labId: labCostSummary.run.labId,
|
|
970
|
+
runId: labCostSummary.run.runId,
|
|
971
|
+
source: "lab-jsonl"
|
|
972
|
+
} : null
|
|
973
|
+
};
|
|
974
|
+
telemetry.logCostEvent("cli_cost_status_viewed", {
|
|
975
|
+
sessionCost: metrics.sessionCost,
|
|
976
|
+
dailyCost: metrics.dailyCost,
|
|
977
|
+
monthlyCost: metrics.monthlyCost,
|
|
978
|
+
alertCount: metrics.activeAlerts.length,
|
|
979
|
+
optimizationCount: metrics.availableOptimizations.length,
|
|
980
|
+
pricingWarningCount: pricingVerification.warnings.length,
|
|
981
|
+
pricingErrorCount: pricingVerification.errors.length,
|
|
982
|
+
swarmView: validatedOptions.swarm ?? false,
|
|
983
|
+
rawInvocationCount: invocationVisibility.rawInvocationCount,
|
|
984
|
+
enrichedInvocationCount: invocationVisibility.enrichedInvocationCount,
|
|
985
|
+
pendingEnrichmentCount: invocationVisibility.pendingEnrichmentCount,
|
|
986
|
+
visibility: invocationVisibility.visibility,
|
|
987
|
+
labId: labCostSummary?.run.labId ?? null,
|
|
988
|
+
runId: labCostSummary?.run.runId ?? null
|
|
989
|
+
});
|
|
990
|
+
if (validatedOptions.outputFile) {
|
|
991
|
+
const { writeFile } = await import("fs/promises");
|
|
992
|
+
try {
|
|
993
|
+
await writeFile(
|
|
994
|
+
validatedOptions.outputFile,
|
|
995
|
+
JSON.stringify(statusPayload, null, 2),
|
|
996
|
+
"utf8"
|
|
997
|
+
);
|
|
998
|
+
} catch (err) {
|
|
999
|
+
console.error(
|
|
1000
|
+
chalk.red(
|
|
1001
|
+
`Failed to write output file: ${err instanceof Error ? err.message : String(err)}`
|
|
1002
|
+
)
|
|
1003
|
+
);
|
|
1004
|
+
process.exitCode = 1;
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (validatedOptions.json) {
|
|
1009
|
+
console.log(JSON.stringify(statusPayload, null, 2));
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
console.log(chalk.bold.cyan("\nCost: Cost Tracking Status\n"));
|
|
1013
|
+
if (labCostSummary) {
|
|
1014
|
+
console.log(
|
|
1015
|
+
`${chalk.gray("Lab Run")}: ${labCostSummary.run.labId}/${labCostSummary.run.runId}`
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
console.log(`${chalk.gray("Session Cost")}: ${formatCurrency(metrics.sessionCost)}`);
|
|
1019
|
+
console.log(`${chalk.gray("Daily Cost")}: ${formatCurrency(metrics.dailyCost)}`);
|
|
1020
|
+
console.log(`${chalk.gray("Monthly Cost")}: ${formatCurrency(metrics.monthlyCost)}`);
|
|
1021
|
+
console.log(`${chalk.gray("Cost / Minute")}: ${formatCurrency(metrics.costPerMinute)}`);
|
|
1022
|
+
const top = metrics.topCostProvider;
|
|
1023
|
+
console.log(
|
|
1024
|
+
`${chalk.gray("Top Contributor")}: ${top.provider}/${top.model} (${formatCurrency(top.cost)} | ${top.percentage.toFixed(1)}%)`
|
|
1025
|
+
);
|
|
1026
|
+
if (metrics.costTrend.length > 0) {
|
|
1027
|
+
const trend = metrics.costTrend.map((value) => formatCurrency(value)).join(chalk.gray(" -> "));
|
|
1028
|
+
console.log(`${chalk.gray("Recent Trend")}: ${trend}`);
|
|
1029
|
+
} else {
|
|
1030
|
+
console.log(`${chalk.gray("Recent Trend")}: ${chalk.gray("No executions recorded")}`);
|
|
1031
|
+
}
|
|
1032
|
+
if (metrics.sessionCost === 0 || costProjection.state !== "enriched") {
|
|
1033
|
+
const projectionNotice = formatCostProjectionNotice(costProjection);
|
|
1034
|
+
if (projectionNotice) {
|
|
1035
|
+
console.log(chalk.gray(projectionNotice));
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (modelInvocationsDb.hasNonMeteredBilling()) {
|
|
1039
|
+
console.log(
|
|
1040
|
+
chalk.gray(
|
|
1041
|
+
"Info: Seat-licensed providers (e.g., Copilot CLI) are tracked for usage but excluded from per-token totals."
|
|
1042
|
+
)
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
if (recentExecutions.length > 0) {
|
|
1046
|
+
const snapshotCost = recentExecutions.reduce(
|
|
1047
|
+
(sum, exec) => sum + exec.costSnapshot.totalCostCents,
|
|
1048
|
+
0
|
|
1049
|
+
);
|
|
1050
|
+
let recalculatedCost = 0;
|
|
1051
|
+
for (const exec of recentExecutions) {
|
|
1052
|
+
const newCost = modelInvocationsDb.recalculateCost(
|
|
1053
|
+
exec.executionId,
|
|
1054
|
+
(provider, model, usage) => {
|
|
1055
|
+
try {
|
|
1056
|
+
const breakdown = costCalculator.calculateCost(provider, model, {
|
|
1057
|
+
inputTokens: usage.inputTokens,
|
|
1058
|
+
outputTokens: usage.outputTokens,
|
|
1059
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
1060
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
1061
|
+
tools: usage.toolCalls?.map((t) => ({
|
|
1062
|
+
name: t.name,
|
|
1063
|
+
requests: t.requests
|
|
1064
|
+
})) || [],
|
|
1065
|
+
executionTimeMinutes: usage.executionTimeMs ? usage.executionTimeMs / 6e4 : void 0
|
|
1066
|
+
});
|
|
1067
|
+
const availableUsd = availableBreakdownUsd(breakdown);
|
|
1068
|
+
if (availableUsd === null) {
|
|
1069
|
+
return exec.costSnapshot.totalCostCents;
|
|
1070
|
+
}
|
|
1071
|
+
return availableUsd * 100;
|
|
1072
|
+
} catch {
|
|
1073
|
+
return exec.costSnapshot.totalCostCents;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
recalculatedCost += newCost ?? exec.costSnapshot.totalCostCents;
|
|
1078
|
+
}
|
|
1079
|
+
console.log(chalk.bold.cyan("\nCost: Cost Basis Comparison\n"));
|
|
1080
|
+
console.log(
|
|
1081
|
+
`${chalk.gray("Historical (snapshot)")}: ${formatCurrency(snapshotCost / 100)}`
|
|
1082
|
+
);
|
|
1083
|
+
console.log(`${chalk.gray("Current pricing")}: ${formatCurrency(recalculatedCost / 100)}`);
|
|
1084
|
+
if (Math.abs(recalculatedCost - snapshotCost) > 0.01) {
|
|
1085
|
+
const delta = recalculatedCost - snapshotCost;
|
|
1086
|
+
const pct = (delta / snapshotCost * 100).toFixed(1);
|
|
1087
|
+
const direction = delta > 0 ? "up" : "down";
|
|
1088
|
+
const color = delta > 0 ? chalk.red : chalk.green;
|
|
1089
|
+
console.log(
|
|
1090
|
+
`${chalk.gray("Price change")}: ${color(`${direction} ${formatCurrency(Math.abs(delta) / 100)} (${pct}%)`)}`
|
|
1091
|
+
);
|
|
1092
|
+
} else {
|
|
1093
|
+
console.log(`${chalk.gray("Price change")}: ${chalk.gray("No change")}`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (validatedOptions.swarm) {
|
|
1097
|
+
const roleBreakdown = modelInvocationsDb.getRoleCostBreakdown(startTime);
|
|
1098
|
+
if (roleBreakdown.length > 0) {
|
|
1099
|
+
console.log(chalk.bold.cyan(`
|
|
1100
|
+
Swarm Cost Breakdown (last ${windowLabel})
|
|
1101
|
+
`));
|
|
1102
|
+
const totalCost = roleBreakdown.reduce((sum, role) => sum + role.avgCostCents, 0);
|
|
1103
|
+
roleBreakdown.forEach((role) => {
|
|
1104
|
+
const costDollars = role.avgCostCents / 100;
|
|
1105
|
+
const percentage = totalCost > 0 ? role.avgCostCents / totalCost * 100 : 0;
|
|
1106
|
+
console.log(
|
|
1107
|
+
`${chalk.bold(role.agentRole.padEnd(15))} ${formatCurrency(costDollars)} (${percentage.toFixed(1)}%) [${role.totalInvocations} tasks]`
|
|
1108
|
+
);
|
|
1109
|
+
console.log(
|
|
1110
|
+
`${chalk.gray(" tokens")}: ${formatTokenCount(role.totalTokensIn)} in, ${formatTokenCount(role.totalTokensOut)} out`
|
|
1111
|
+
);
|
|
1112
|
+
});
|
|
1113
|
+
console.log(
|
|
1114
|
+
chalk.gray("\nTip: Use role-specific providers to optimize costs per domain")
|
|
1115
|
+
);
|
|
1116
|
+
} else {
|
|
1117
|
+
console.log(chalk.gray(`
|
|
1118
|
+
No swarm executions found in the last ${windowLabel}`));
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (metrics.activeAlerts.length > 0) {
|
|
1122
|
+
console.log(chalk.bold.yellow("\nWarning: Internal Cost Heuristics"));
|
|
1123
|
+
metrics.activeAlerts.forEach((alert) => {
|
|
1124
|
+
console.log(
|
|
1125
|
+
`${alert.severity.toUpperCase()} | ${alert.type} | ${alert.message} (${formatCurrency(alert.currentCost)} / ${formatCurrency(alert.threshold)})`
|
|
1126
|
+
);
|
|
1127
|
+
});
|
|
1128
|
+
} else {
|
|
1129
|
+
console.log(chalk.gray("\nNo internal cost heuristic alerts"));
|
|
1130
|
+
}
|
|
1131
|
+
if (pricingVerificationForView.warnings.length > 0 || pricingVerification.errors.length > 0) {
|
|
1132
|
+
console.log(chalk.bold.yellow("\nWarning: Pricing Data"));
|
|
1133
|
+
for (const warning of pricingVerificationForView.warnings) {
|
|
1134
|
+
const provider = warning.provider ? `${warning.provider}: ` : "";
|
|
1135
|
+
console.log(`${warning.severity.toUpperCase()} | ${provider}${warning.message}`);
|
|
1136
|
+
}
|
|
1137
|
+
for (const error of pricingVerification.errors) {
|
|
1138
|
+
console.log(`ERROR | ${error.provider}: ${error.message}`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (metrics.availableOptimizations.length > 0) {
|
|
1142
|
+
console.log(chalk.bold.green("\nTip: Optimization Suggestions"));
|
|
1143
|
+
metrics.availableOptimizations.forEach((suggestion) => {
|
|
1144
|
+
console.log(
|
|
1145
|
+
`${suggestion.title} | saves ${formatCurrency(suggestion.estimatedSavings)} (${suggestion.estimatedSavingsPercent.toFixed(1)}%) | confidence ${(suggestion.confidence * 100).toFixed(0)}%`
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
cost.command("predict").description(
|
|
1151
|
+
"Predict execution cost for a provider/model pair\n\nExamples:\n poetic cost predict cursor auto --input 1000 --output 500\n poetic cost predict claude sonnet --input 2000 --output 1000 --tool search:5"
|
|
1152
|
+
).argument("<provider>", "(REQUIRED) Provider identifier (e.g. anthropic)").argument("<model>", "(REQUIRED) Model name or alias (e.g. auto)").requiredOption(
|
|
1153
|
+
"--input <tokens>",
|
|
1154
|
+
"(REQUIRED) Estimated input tokens\n Example: --input 1000"
|
|
1155
|
+
).requiredOption(
|
|
1156
|
+
"--output <tokens>",
|
|
1157
|
+
"(REQUIRED) Estimated output tokens\n Example: --output 500"
|
|
1158
|
+
).option(
|
|
1159
|
+
"--tool <usage>",
|
|
1160
|
+
"(OPTIONAL) Tool usage in name:requests format (repeatable)\n Example: --tool search:5",
|
|
1161
|
+
collectRawToolUsage,
|
|
1162
|
+
[]
|
|
1163
|
+
).option("--json", "(OPTIONAL) Output breakdown as JSON").action(async (provider, model, options) => {
|
|
1164
|
+
const jsonMode = Boolean(options.json);
|
|
1165
|
+
try {
|
|
1166
|
+
const parsedInput = parsePredictTokenOption(options.input);
|
|
1167
|
+
const parsedOutput = parsePredictTokenOption(options.output);
|
|
1168
|
+
const parsedTools = (options.tool ?? []).map(parsePredictToolUsageEntry);
|
|
1169
|
+
const costCalculator = await deps.costCalculator;
|
|
1170
|
+
const telemetry = await deps.telemetry;
|
|
1171
|
+
const validatedProvider = z.string().min(1, "Provider cannot be empty").max(100, "Provider name too long").regex(/^[a-zA-Z0-9._-]+$/, "Invalid provider name format").parse(provider);
|
|
1172
|
+
const validatedModel = ModelNameSchema.parse(model);
|
|
1173
|
+
const validatedOptions = validateCommandOptions(PredictOptionsSchema, {
|
|
1174
|
+
input: parsedInput,
|
|
1175
|
+
output: parsedOutput,
|
|
1176
|
+
tool: parsedTools.length > 0 ? parsedTools : void 0,
|
|
1177
|
+
json: options.json
|
|
1178
|
+
});
|
|
1179
|
+
await deps.ensurePricingLoaded();
|
|
1180
|
+
const tools = validatedOptions.tool ? toToolUsage(validatedOptions.tool) : [];
|
|
1181
|
+
const breakdown = costCalculator.predictCost(validatedProvider, validatedModel, {
|
|
1182
|
+
input: validatedOptions.input,
|
|
1183
|
+
output: validatedOptions.output,
|
|
1184
|
+
tools: tools.map((tool) => ({
|
|
1185
|
+
name: tool.name,
|
|
1186
|
+
estimatedRequests: tool.requests
|
|
1187
|
+
}))
|
|
1188
|
+
});
|
|
1189
|
+
telemetry.logCostEvent("cli_cost_predict", {
|
|
1190
|
+
provider: validatedProvider,
|
|
1191
|
+
model: validatedModel,
|
|
1192
|
+
inputTokens: validatedOptions.input,
|
|
1193
|
+
outputTokens: validatedOptions.output,
|
|
1194
|
+
tools: tools.length,
|
|
1195
|
+
totalCost: availableBreakdownUsd(breakdown),
|
|
1196
|
+
costStatus: breakdown.totalCostAmount.status,
|
|
1197
|
+
currency: breakdown.currency,
|
|
1198
|
+
pricingStatus: breakdown.pricingStatus
|
|
1199
|
+
});
|
|
1200
|
+
if (validatedOptions.json) {
|
|
1201
|
+
renderJsonResult(breakdown);
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
printCostBreakdown("Predictive: Cost Prediction", breakdown);
|
|
1205
|
+
console.log(`${chalk.gray("Input Tokens")}: ${formatTokenCount(validatedOptions.input)}`);
|
|
1206
|
+
console.log(`${chalk.gray("Output Tokens")}: ${formatTokenCount(validatedOptions.output)}`);
|
|
1207
|
+
if (tools.length > 0) {
|
|
1208
|
+
const toolSummary = tools.map((tool) => `${tool.name} \xD7 ${tool.requests}`).join(chalk.gray(", "));
|
|
1209
|
+
console.log(`${chalk.gray("Tools")}: ${toolSummary}`);
|
|
1210
|
+
}
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
if (jsonMode) {
|
|
1213
|
+
renderJsonError(toPredictJsonError(error));
|
|
1214
|
+
process.exitCode = 1;
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
throw error;
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
cost.command("compare").description(
|
|
1221
|
+
"Compare predicted costs across multiple provider/model pairs\n\nExamples:\n poetic cost compare cursor:auto cursor:auto --input 1000 --output 500\n poetic cost compare cursor:auto cursor:auto --input 2000 --output 1000 --json"
|
|
1222
|
+
).argument(
|
|
1223
|
+
"[providerModels...]",
|
|
1224
|
+
"(REQUIRED) Provider/model pairs (format provider:model, at least 2 required)"
|
|
1225
|
+
).requiredOption(
|
|
1226
|
+
"--input <tokens>",
|
|
1227
|
+
"(REQUIRED) Estimated input tokens\n Example: --input 1000",
|
|
1228
|
+
parseNonNegativeInteger
|
|
1229
|
+
).requiredOption(
|
|
1230
|
+
"--output <tokens>",
|
|
1231
|
+
"(REQUIRED) Estimated output tokens\n Example: --output 500",
|
|
1232
|
+
parseNonNegativeInteger
|
|
1233
|
+
).option("--json", "(OPTIONAL) Output comparison as JSON").action(async (providerModels, options) => {
|
|
1234
|
+
try {
|
|
1235
|
+
const costCalculator = await deps.costCalculator;
|
|
1236
|
+
const telemetry = await deps.telemetry;
|
|
1237
|
+
if (!Array.isArray(providerModels) || providerModels.length < 2) {
|
|
1238
|
+
throw new InvalidArgumentError("Provide at least two provider:model pairs to compare");
|
|
1239
|
+
}
|
|
1240
|
+
const validatedOptions = validateCommandOptions(CompareOptionsSchema, options);
|
|
1241
|
+
const ProviderModelPairSchema = z.string().regex(/^[a-zA-Z0-9._-]+:[a-zA-Z0-9._\-/]+$/, "Invalid provider:model format").refine((value) => {
|
|
1242
|
+
const parts = value.split(":");
|
|
1243
|
+
return parts.length === 2 && (parts[0]?.length ?? 0) > 0 && (parts[1]?.length ?? 0) > 0;
|
|
1244
|
+
}, "Provider:model must have both provider and model specified");
|
|
1245
|
+
const validatedPairs = providerModels.map((pair) => ProviderModelPairSchema.parse(pair));
|
|
1246
|
+
await deps.ensurePricingLoaded();
|
|
1247
|
+
const breakdowns = validatedPairs.map((entry) => {
|
|
1248
|
+
const [provider, model] = entry.split(":");
|
|
1249
|
+
if (!provider || !model) {
|
|
1250
|
+
throw new InvalidArgumentError(
|
|
1251
|
+
`Invalid provider:model pair '${entry}'. Expected format provider:model`
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
return costCalculator.predictCost(provider, model, {
|
|
1255
|
+
input: validatedOptions.input,
|
|
1256
|
+
output: validatedOptions.output
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
const pricedBreakdowns = breakdowns.filter((breakdown) => !isPricingUnavailable(breakdown));
|
|
1260
|
+
const cheapestModel = pricedBreakdowns.reduce((min, current) => {
|
|
1261
|
+
if (!min) return current;
|
|
1262
|
+
const minUsd = availableBreakdownUsd(min) ?? Number.POSITIVE_INFINITY;
|
|
1263
|
+
const currentUsd = availableBreakdownUsd(current) ?? Number.POSITIVE_INFINITY;
|
|
1264
|
+
return currentUsd < minUsd ? current : min;
|
|
1265
|
+
}, void 0);
|
|
1266
|
+
telemetry.logCostEvent("cli_cost_compare", {
|
|
1267
|
+
comparisons: validatedPairs.length,
|
|
1268
|
+
inputTokens: validatedOptions.input,
|
|
1269
|
+
outputTokens: validatedOptions.output,
|
|
1270
|
+
cheapest: cheapestModel?.model ?? null,
|
|
1271
|
+
unavailableComparisons: breakdowns.length - pricedBreakdowns.length
|
|
1272
|
+
});
|
|
1273
|
+
if (validatedOptions.json) {
|
|
1274
|
+
console.log(JSON.stringify(breakdowns, null, 2));
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
console.log(chalk.bold.cyan("\nCost Comparison\n"));
|
|
1278
|
+
const unavailableBreakdowns = breakdowns.filter(isPricingUnavailable);
|
|
1279
|
+
const sorted = [
|
|
1280
|
+
...pricedBreakdowns.sort((a, b) => {
|
|
1281
|
+
const aUsd = availableBreakdownUsd(a) ?? Number.POSITIVE_INFINITY;
|
|
1282
|
+
const bUsd = availableBreakdownUsd(b) ?? Number.POSITIVE_INFINITY;
|
|
1283
|
+
return aUsd - bUsd;
|
|
1284
|
+
}),
|
|
1285
|
+
...unavailableBreakdowns
|
|
1286
|
+
];
|
|
1287
|
+
if (sorted.length === 0) {
|
|
1288
|
+
console.log(chalk.red("No breakdowns available"));
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
const cheapest = pricedBreakdowns[0];
|
|
1292
|
+
const cheapestUsd = cheapest && cheapest.totalCostAmount.status === "available" ? cheapest.totalCostAmount.amount : null;
|
|
1293
|
+
sorted.forEach((breakdown, index) => {
|
|
1294
|
+
const totalAmount = breakdown.totalCostAmount;
|
|
1295
|
+
const isUnavailable = breakdown.pricingStatus === "unavailable" || totalAmount.status !== "available";
|
|
1296
|
+
const isCheapest = !isUnavailable && breakdown === cheapest;
|
|
1297
|
+
const rankLabel = `${index + 1}. ${breakdown.provider}/${breakdown.model}`;
|
|
1298
|
+
console.log(isCheapest ? chalk.green(rankLabel) : rankLabel);
|
|
1299
|
+
if (breakdown.pricingStatus === "unavailable" || totalAmount.status !== "available") {
|
|
1300
|
+
console.log(" total unavailable");
|
|
1301
|
+
if (breakdown.pricingError) {
|
|
1302
|
+
console.log(` pricing unavailable: ${breakdown.pricingError}`);
|
|
1303
|
+
}
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
const totalUsd = totalAmount.amount;
|
|
1307
|
+
if (cheapestUsd === null) {
|
|
1308
|
+
console.log(` total ${formatCurrency(totalUsd, breakdown.currency)}`);
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
console.log(
|
|
1312
|
+
` total ${formatCurrency(totalUsd, breakdown.currency)} (${formatCurrencyDelta(totalUsd - cheapestUsd, breakdown.currency)})`
|
|
1313
|
+
);
|
|
1314
|
+
console.log(
|
|
1315
|
+
` input ${formatCurrency(breakdown.inputCost, breakdown.currency)} | output ${formatCurrency(breakdown.outputCost, breakdown.currency)}`
|
|
1316
|
+
);
|
|
1317
|
+
if (breakdown.toolCosts > 0 || breakdown.cacheCost > 0 || breakdown.infrastructureCost > 0) {
|
|
1318
|
+
console.log(
|
|
1319
|
+
` cache ${formatCurrency(breakdown.cacheCost, breakdown.currency)} | tools ${formatCurrency(breakdown.toolCosts, breakdown.currency)} | infra ${formatCurrency(breakdown.infrastructureCost, breakdown.currency)}`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
if (options.json) {
|
|
1325
|
+
console.log(
|
|
1326
|
+
JSON.stringify(
|
|
1327
|
+
{
|
|
1328
|
+
error: "cost_comparison_failed",
|
|
1329
|
+
message: createSafeErrorMessage(error, "cost compare")
|
|
1330
|
+
},
|
|
1331
|
+
null,
|
|
1332
|
+
2
|
|
1333
|
+
)
|
|
1334
|
+
);
|
|
1335
|
+
process.exitCode = 1;
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
throw error;
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
cost.command("daily").description(
|
|
1342
|
+
"Generate and display daily cost reports\n\nExamples:\n poetic cost daily\n poetic cost daily --days 14 --group provider\n poetic cost daily --start 2024-01-01 --end 2024-01-31"
|
|
1343
|
+
).option(
|
|
1344
|
+
"--days <days>",
|
|
1345
|
+
"(OPTIONAL) Number of recent days to include (default: 7)\n Example: --days 14",
|
|
1346
|
+
"7"
|
|
1347
|
+
).option(
|
|
1348
|
+
"--start <date>",
|
|
1349
|
+
"(OPTIONAL) Start date in YYYY-MM-DD format (overrides --days)\n Example: --start 2024-01-01"
|
|
1350
|
+
).option(
|
|
1351
|
+
"--end <date>",
|
|
1352
|
+
"(OPTIONAL) End date in YYYY-MM-DD format (default: today)\n Example: --end 2024-01-31"
|
|
1353
|
+
).option(
|
|
1354
|
+
"--group <grouping>",
|
|
1355
|
+
"(OPTIONAL) Group results by date, provider, or model (default: date)\n Example: --group provider",
|
|
1356
|
+
"date"
|
|
1357
|
+
).option("--json", "(OPTIONAL) Output report as JSON").action(async (options) => {
|
|
1358
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
1359
|
+
const telemetry = await deps.telemetry;
|
|
1360
|
+
if (!modelInvocationsDb) {
|
|
1361
|
+
console.log(chalk.yellow("Daily cost reports require a telemetry database."));
|
|
1362
|
+
console.log(chalk.gray("Ensure cost tracking is enabled and try again."));
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
const validatedOptions = validateCommandOptions(DailyOptionsSchema, options);
|
|
1366
|
+
const today = /* @__PURE__ */ new Date();
|
|
1367
|
+
const endDate = validatedOptions.end ? new Date(validatedOptions.end) : today;
|
|
1368
|
+
let startDate;
|
|
1369
|
+
if (validatedOptions.start) {
|
|
1370
|
+
startDate = new Date(validatedOptions.start);
|
|
1371
|
+
if (Number.isNaN(startDate.getTime())) {
|
|
1372
|
+
throw new InvalidArgumentError(
|
|
1373
|
+
`Invalid --start date: ${validatedOptions.start}. Use YYYY-MM-DD format.`
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
} else {
|
|
1377
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
1378
|
+
startDate = new Date(endDate);
|
|
1379
|
+
startDate.setDate(startDate.getDate() - daysBack + 1);
|
|
1380
|
+
}
|
|
1381
|
+
if (Number.isNaN(endDate.getTime())) {
|
|
1382
|
+
throw new InvalidArgumentError(
|
|
1383
|
+
`Invalid --end date: ${validatedOptions.end}. Use YYYY-MM-DD format.`
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
if (startDate > endDate) {
|
|
1387
|
+
throw new InvalidArgumentError(
|
|
1388
|
+
"Invalid date range: --start date cannot be after --end date."
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
const startDateStr = startDate.toISOString().split("T")[0];
|
|
1392
|
+
const endDateStr = endDate.toISOString().split("T")[0];
|
|
1393
|
+
const dailyResult = modelInvocationsDb.getDailyCostAggregations(
|
|
1394
|
+
startDateStr,
|
|
1395
|
+
endDateStr,
|
|
1396
|
+
validatedOptions.group
|
|
1397
|
+
);
|
|
1398
|
+
const dailyData = dailyResult.rows;
|
|
1399
|
+
if (dailyResult.status === "error") {
|
|
1400
|
+
if (validatedOptions.json) {
|
|
1401
|
+
writeCostOutput(
|
|
1402
|
+
JSON.stringify(
|
|
1403
|
+
{
|
|
1404
|
+
status: "error",
|
|
1405
|
+
error: dailyResult.error ?? "daily cost aggregation failed",
|
|
1406
|
+
detail: dailyResult.detail,
|
|
1407
|
+
period: {
|
|
1408
|
+
start: startDateStr,
|
|
1409
|
+
end: endDateStr,
|
|
1410
|
+
days: 0
|
|
1411
|
+
},
|
|
1412
|
+
grouping: validatedOptions.group,
|
|
1413
|
+
data: []
|
|
1414
|
+
},
|
|
1415
|
+
null,
|
|
1416
|
+
2
|
|
1417
|
+
)
|
|
1418
|
+
);
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
writeCostOutput(
|
|
1422
|
+
chalk.yellow(
|
|
1423
|
+
`Error: Daily cost aggregation failed${dailyResult.error ? `: ${dailyResult.error}` : "."}`
|
|
1424
|
+
)
|
|
1425
|
+
);
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
if (dailyData.length === 0) {
|
|
1429
|
+
if (validatedOptions.json) {
|
|
1430
|
+
console.log(
|
|
1431
|
+
JSON.stringify(
|
|
1432
|
+
{
|
|
1433
|
+
status: "complete",
|
|
1434
|
+
period: {
|
|
1435
|
+
start: startDateStr,
|
|
1436
|
+
end: endDateStr,
|
|
1437
|
+
days: 0
|
|
1438
|
+
},
|
|
1439
|
+
summary: {
|
|
1440
|
+
totalCost: 0,
|
|
1441
|
+
totalExecutions: 0,
|
|
1442
|
+
pricedExecutions: 0,
|
|
1443
|
+
totalTokensIn: 0,
|
|
1444
|
+
totalTokensOut: 0,
|
|
1445
|
+
avgDailyCost: 0
|
|
1446
|
+
},
|
|
1447
|
+
grouping: validatedOptions.group,
|
|
1448
|
+
data: []
|
|
1449
|
+
},
|
|
1450
|
+
null,
|
|
1451
|
+
2
|
|
1452
|
+
)
|
|
1453
|
+
);
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
console.log(chalk.yellow("No cost data found for the specified period."));
|
|
1457
|
+
console.log(chalk.gray(`Query range: ${startDateStr} to ${endDateStr}`));
|
|
1458
|
+
logCostEmptyStateHint(modelInvocationsDb, startDate.getTime());
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const totalExecutions = dailyResult.executionCount;
|
|
1462
|
+
const pricedExecutions = dailyResult.pricedCount;
|
|
1463
|
+
const totalTokensIn = dailyData.reduce((sum, day) => sum + day.totalTokensIn, 0);
|
|
1464
|
+
const totalTokensOut = dailyData.reduce((sum, day) => sum + day.totalTokensOut, 0);
|
|
1465
|
+
const totalCost = dailyResult.status === "complete" ? sumCompleteDailyCosts(dailyData) : null;
|
|
1466
|
+
const avgDailyCost = totalCost !== null && dailyData.length > 0 ? totalCost / dailyData.length : null;
|
|
1467
|
+
telemetry.logCostEvent("cli_cost_daily_viewed", {
|
|
1468
|
+
startDate: startDateStr ?? null,
|
|
1469
|
+
endDate: endDateStr ?? null,
|
|
1470
|
+
grouping: validatedOptions.group,
|
|
1471
|
+
recordCount: dailyData.length,
|
|
1472
|
+
status: dailyResult.status,
|
|
1473
|
+
totalCost,
|
|
1474
|
+
totalExecutions,
|
|
1475
|
+
pricedExecutions
|
|
1476
|
+
});
|
|
1477
|
+
if (validatedOptions.json) {
|
|
1478
|
+
const summary = dailyResult.status === "complete" && totalCost !== null && avgDailyCost !== null ? {
|
|
1479
|
+
totalCost,
|
|
1480
|
+
totalExecutions,
|
|
1481
|
+
pricedExecutions,
|
|
1482
|
+
totalTokensIn,
|
|
1483
|
+
totalTokensOut,
|
|
1484
|
+
avgDailyCost
|
|
1485
|
+
} : {
|
|
1486
|
+
totalExecutions,
|
|
1487
|
+
pricedExecutions,
|
|
1488
|
+
totalTokensIn,
|
|
1489
|
+
totalTokensOut
|
|
1490
|
+
};
|
|
1491
|
+
console.log(
|
|
1492
|
+
JSON.stringify(
|
|
1493
|
+
{
|
|
1494
|
+
status: dailyResult.status,
|
|
1495
|
+
...dailyResult.detail ? { detail: dailyResult.detail } : {},
|
|
1496
|
+
period: {
|
|
1497
|
+
start: startDateStr,
|
|
1498
|
+
end: endDateStr,
|
|
1499
|
+
days: dailyData.length
|
|
1500
|
+
},
|
|
1501
|
+
summary,
|
|
1502
|
+
grouping: validatedOptions.group,
|
|
1503
|
+
data: dailyData
|
|
1504
|
+
},
|
|
1505
|
+
null,
|
|
1506
|
+
2
|
|
1507
|
+
)
|
|
1508
|
+
);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
console.log(chalk.bold.cyan("\nDaily Cost Report\n"));
|
|
1512
|
+
console.log(
|
|
1513
|
+
`${chalk.gray("Period")}: ${startDateStr} to ${endDateStr} (${dailyData.length} days)`
|
|
1514
|
+
);
|
|
1515
|
+
console.log(`${chalk.gray("Grouping")}: ${validatedOptions.group}`);
|
|
1516
|
+
if (dailyResult.status === "incomplete") {
|
|
1517
|
+
writeCostOutput(
|
|
1518
|
+
chalk.yellow(
|
|
1519
|
+
`Evidence: incomplete (${pricedExecutions} of ${totalExecutions} invocations priced)`
|
|
1520
|
+
)
|
|
1521
|
+
);
|
|
1522
|
+
writeCostOutput(
|
|
1523
|
+
chalk.gray("Totals and averages omitted until all invocations are priced.")
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
console.log(chalk.bold("\nSummary"));
|
|
1527
|
+
if (totalCost !== null && avgDailyCost !== null) {
|
|
1528
|
+
console.log(`${chalk.gray("Total Cost")}: ${formatCostDisplay(totalCost)}`);
|
|
1529
|
+
console.log(`${chalk.gray("Avg Daily Cost")}: ${formatCostDisplay(avgDailyCost)}`);
|
|
1530
|
+
} else {
|
|
1531
|
+
writeCostOutput(`${chalk.gray("Total Cost")}: incomplete`);
|
|
1532
|
+
writeCostOutput(`${chalk.gray("Avg Daily Cost")}: incomplete`);
|
|
1533
|
+
}
|
|
1534
|
+
console.log(`${chalk.gray("Total Executions")}: ${totalExecutions.toLocaleString()}`);
|
|
1535
|
+
if (dailyResult.status === "incomplete") {
|
|
1536
|
+
writeCostOutput(`${chalk.gray("Priced Executions")}: ${pricedExecutions.toLocaleString()}`);
|
|
1537
|
+
}
|
|
1538
|
+
console.log(
|
|
1539
|
+
`${chalk.gray("Total Tokens")}: ${formatTokenCount(totalTokensIn + totalTokensOut)}`
|
|
1540
|
+
);
|
|
1541
|
+
if (dailyResult.status === "complete" && dailyData.length > 1) {
|
|
1542
|
+
const sortedByCost = [...dailyData].sort((a, b) => (b.totalCost ?? 0) - (a.totalCost ?? 0));
|
|
1543
|
+
const mostExpensive = sortedByCost[0];
|
|
1544
|
+
const leastExpensive = sortedByCost[sortedByCost.length - 1];
|
|
1545
|
+
if (mostExpensive && leastExpensive && mostExpensive.totalCost !== null && leastExpensive.totalCost !== null && mostExpensive.totalCost > leastExpensive.totalCost) {
|
|
1546
|
+
console.log(
|
|
1547
|
+
`${chalk.gray("Highest Cost Day")}: ${mostExpensive.date} (${formatCostDisplay(mostExpensive.totalCost)})`
|
|
1548
|
+
);
|
|
1549
|
+
console.log(
|
|
1550
|
+
`${chalk.gray("Lowest Cost Day")}: ${leastExpensive.date} (${formatCostDisplay(leastExpensive.totalCost)})`
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
console.log(chalk.bold("\nDetailed Breakdown\n"));
|
|
1555
|
+
if (validatedOptions.group === "date") {
|
|
1556
|
+
console.log(
|
|
1557
|
+
`${chalk.bold("Date".padEnd(12))} ${chalk.bold("Cost".padEnd(12))} ${chalk.bold("Executions".padEnd(12))} ${chalk.bold("Tokens".padEnd(12))} ${chalk.bold("Avg Cost")}`
|
|
1558
|
+
);
|
|
1559
|
+
console.log(chalk.gray("-".repeat(60)));
|
|
1560
|
+
dailyData.forEach((day) => {
|
|
1561
|
+
const totalTokens = day.totalTokensIn + day.totalTokensOut;
|
|
1562
|
+
console.log(
|
|
1563
|
+
`${day.date.padEnd(12)} ${formatDailyAggregationCost(day.totalCost).padEnd(12)} ${day.executionCount.toLocaleString().padEnd(12)} ${formatTokenCount(totalTokens).padEnd(12)} ${formatDailyAggregationCost(day.avgCostPerExecution)}`
|
|
1564
|
+
);
|
|
1565
|
+
});
|
|
1566
|
+
} else if (validatedOptions.group === "provider") {
|
|
1567
|
+
const providerGroups = /* @__PURE__ */ new Map();
|
|
1568
|
+
dailyData.forEach((item) => {
|
|
1569
|
+
if (!item.provider) return;
|
|
1570
|
+
if (!providerGroups.has(item.provider)) {
|
|
1571
|
+
providerGroups.set(item.provider, []);
|
|
1572
|
+
}
|
|
1573
|
+
providerGroups.get(item.provider)?.push(item);
|
|
1574
|
+
});
|
|
1575
|
+
console.log(
|
|
1576
|
+
`${chalk.bold("Provider".padEnd(20))} ${chalk.bold("Total Cost".padEnd(12))} ${chalk.bold("Executions".padEnd(12))} ${chalk.bold("Tokens".padEnd(12))} ${chalk.bold("Avg Cost")}`
|
|
1577
|
+
);
|
|
1578
|
+
console.log(chalk.gray("-".repeat(70)));
|
|
1579
|
+
providerGroups.forEach((items, provider) => {
|
|
1580
|
+
const providerCost = sumCompleteDailyCosts(items);
|
|
1581
|
+
const providerExecutions = items.reduce((sum, item) => sum + item.executionCount, 0);
|
|
1582
|
+
const providerTokens = items.reduce(
|
|
1583
|
+
(sum, item) => sum + item.totalTokensIn + item.totalTokensOut,
|
|
1584
|
+
0
|
|
1585
|
+
);
|
|
1586
|
+
const avgCost = providerCost !== null && providerExecutions > 0 ? providerCost / providerExecutions : null;
|
|
1587
|
+
console.log(
|
|
1588
|
+
`${provider.padEnd(20)} ${formatDailyAggregationCost(providerCost).padEnd(12)} ${providerExecutions.toLocaleString().padEnd(12)} ${formatTokenCount(providerTokens).padEnd(12)} ${formatDailyAggregationCost(avgCost)}`
|
|
1589
|
+
);
|
|
1590
|
+
});
|
|
1591
|
+
} else if (validatedOptions.group === "model") {
|
|
1592
|
+
const modelGroups = /* @__PURE__ */ new Map();
|
|
1593
|
+
dailyData.forEach((item) => {
|
|
1594
|
+
if (!item.model) return;
|
|
1595
|
+
if (!modelGroups.has(item.model)) {
|
|
1596
|
+
modelGroups.set(item.model, []);
|
|
1597
|
+
}
|
|
1598
|
+
modelGroups.get(item.model)?.push(item);
|
|
1599
|
+
});
|
|
1600
|
+
console.log(
|
|
1601
|
+
`${chalk.bold("Model".padEnd(25))} ${chalk.bold("Total Cost".padEnd(12))} ${chalk.bold("Executions".padEnd(12))} ${chalk.bold("Tokens".padEnd(12))} ${chalk.bold("Avg Cost")}`
|
|
1602
|
+
);
|
|
1603
|
+
console.log(chalk.gray("-".repeat(75)));
|
|
1604
|
+
modelGroups.forEach((items, model) => {
|
|
1605
|
+
const modelCost = sumCompleteDailyCosts(items);
|
|
1606
|
+
const modelExecutions = items.reduce((sum, item) => sum + item.executionCount, 0);
|
|
1607
|
+
const modelTokens = items.reduce(
|
|
1608
|
+
(sum, item) => sum + item.totalTokensIn + item.totalTokensOut,
|
|
1609
|
+
0
|
|
1610
|
+
);
|
|
1611
|
+
const avgCost = modelCost !== null && modelExecutions > 0 ? modelCost / modelExecutions : null;
|
|
1612
|
+
console.log(
|
|
1613
|
+
`${model.padEnd(25)} ${formatDailyAggregationCost(modelCost).padEnd(12)} ${modelExecutions.toLocaleString().padEnd(12)} ${formatTokenCount(modelTokens).padEnd(12)} ${formatDailyAggregationCost(avgCost)}`
|
|
1614
|
+
);
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
console.log(
|
|
1618
|
+
chalk.gray("\nTip: Use --group provider or --group model to see different breakdowns")
|
|
1619
|
+
);
|
|
1620
|
+
});
|
|
1621
|
+
cost.command("history").description(
|
|
1622
|
+
"Show cost history with optional recalculation\n\nExamples:\n poetic cost history --task task-123\n poetic cost history --competition comp-456 --recalculate current\n poetic cost history --days 30 --recalculate 2024-01-01"
|
|
1623
|
+
).option("--task <taskId>", "(OPTIONAL) Show costs for specific task").option("--competition <competitionId>", "(OPTIONAL) Show costs for specific competition").option(
|
|
1624
|
+
"--recalculate <mode>",
|
|
1625
|
+
"(OPTIONAL) Recalculation mode: current | snapshot | YYYY-MM-DD\n Example: --recalculate current"
|
|
1626
|
+
).option(
|
|
1627
|
+
"--days <days>",
|
|
1628
|
+
"(OPTIONAL) Number of recent days (default: 7)\n Example: --days 14",
|
|
1629
|
+
"7"
|
|
1630
|
+
).option("--json", "(OPTIONAL) Output as JSON").action(async (options) => {
|
|
1631
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
1632
|
+
const costCalculator = await deps.costCalculator;
|
|
1633
|
+
const telemetry = await deps.telemetry;
|
|
1634
|
+
const validatedOptions = validateCommandOptions(HistoryOptionsSchema, options);
|
|
1635
|
+
let executions = [];
|
|
1636
|
+
if (validatedOptions.task) {
|
|
1637
|
+
const taskExecutions = modelInvocationsDb.getTaskExecutionCosts(validatedOptions.task);
|
|
1638
|
+
executions = taskExecutions.map((exec) => ({
|
|
1639
|
+
executionId: exec.executionId,
|
|
1640
|
+
provider: exec.provider,
|
|
1641
|
+
model: exec.model,
|
|
1642
|
+
snapshotCost: exec.costSnapshot.totalCostCents,
|
|
1643
|
+
currentCost: exec.costSnapshot.totalCostCents,
|
|
1644
|
+
timestamp: exec.timestamp
|
|
1645
|
+
}));
|
|
1646
|
+
} else if (validatedOptions.competition) {
|
|
1647
|
+
const competitionExecutions = modelInvocationsDb.getCompetitionExecutionCosts(
|
|
1648
|
+
validatedOptions.competition
|
|
1649
|
+
);
|
|
1650
|
+
executions = competitionExecutions.map((exec) => ({
|
|
1651
|
+
executionId: exec.executionId,
|
|
1652
|
+
provider: exec.provider,
|
|
1653
|
+
model: exec.model,
|
|
1654
|
+
snapshotCost: exec.costSnapshot.totalCostCents,
|
|
1655
|
+
currentCost: exec.costSnapshot.totalCostCents,
|
|
1656
|
+
timestamp: exec.timestamp
|
|
1657
|
+
}));
|
|
1658
|
+
} else {
|
|
1659
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
1660
|
+
const startTime = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
1661
|
+
const allExecutions = modelInvocationsDb.getExecutionCostsSince(startTime);
|
|
1662
|
+
executions = allExecutions.map((exec) => ({
|
|
1663
|
+
executionId: exec.executionId,
|
|
1664
|
+
provider: exec.provider,
|
|
1665
|
+
model: exec.model,
|
|
1666
|
+
snapshotCost: exec.costSnapshot.totalCostCents,
|
|
1667
|
+
currentCost: exec.costSnapshot.totalCostCents,
|
|
1668
|
+
timestamp: exec.timestamp
|
|
1669
|
+
}));
|
|
1670
|
+
}
|
|
1671
|
+
if (validatedOptions.recalculate === "current") {
|
|
1672
|
+
for (const exec of executions) {
|
|
1673
|
+
const newCost = modelInvocationsDb.recalculateCost(
|
|
1674
|
+
exec.executionId,
|
|
1675
|
+
(provider, model, usage) => {
|
|
1676
|
+
try {
|
|
1677
|
+
const breakdown = costCalculator.calculateCost(provider, model, {
|
|
1678
|
+
inputTokens: usage.inputTokens,
|
|
1679
|
+
outputTokens: usage.outputTokens,
|
|
1680
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
1681
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
1682
|
+
tools: usage.toolCalls?.map((t) => ({
|
|
1683
|
+
name: t.name,
|
|
1684
|
+
requests: t.requests
|
|
1685
|
+
})) || [],
|
|
1686
|
+
executionTimeMinutes: usage.executionTimeMs ? usage.executionTimeMs / 6e4 : void 0
|
|
1687
|
+
});
|
|
1688
|
+
const availableUsd = availableBreakdownUsd(breakdown);
|
|
1689
|
+
if (availableUsd === null) {
|
|
1690
|
+
return exec.snapshotCost;
|
|
1691
|
+
}
|
|
1692
|
+
return availableUsd * 100;
|
|
1693
|
+
} catch (error) {
|
|
1694
|
+
emitWarning(
|
|
1695
|
+
"PR-003",
|
|
1696
|
+
{
|
|
1697
|
+
taskId: validatedOptions.task || "cost-history",
|
|
1698
|
+
subsystem: "cost-tracking",
|
|
1699
|
+
executionPhase: "execution"
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
severity: "WARN" /* WARN */,
|
|
1703
|
+
metadata: {
|
|
1704
|
+
executionId: exec.executionId,
|
|
1705
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1706
|
+
operation: "recalculateCost"
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
);
|
|
1710
|
+
return exec.snapshotCost;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
);
|
|
1714
|
+
exec.currentCost = newCost ?? exec.snapshotCost;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
telemetry.logCostEvent("cli_cost_history_viewed", {
|
|
1718
|
+
taskId: validatedOptions.task ?? null,
|
|
1719
|
+
competitionId: validatedOptions.competition ?? null,
|
|
1720
|
+
recalculateMode: validatedOptions.recalculate ?? "snapshot",
|
|
1721
|
+
executionCount: executions.length
|
|
1722
|
+
});
|
|
1723
|
+
if (validatedOptions.json) {
|
|
1724
|
+
console.log(
|
|
1725
|
+
JSON.stringify(
|
|
1726
|
+
{
|
|
1727
|
+
executions: executions.map((e) => ({
|
|
1728
|
+
executionId: e.executionId,
|
|
1729
|
+
provider: e.provider,
|
|
1730
|
+
model: e.model,
|
|
1731
|
+
snapshotCost: e.snapshotCost / 100,
|
|
1732
|
+
currentCost: e.currentCost / 100,
|
|
1733
|
+
delta: (e.currentCost - e.snapshotCost) / 100,
|
|
1734
|
+
deltaPercent: e.snapshotCost > 0 ? (e.currentCost - e.snapshotCost) / e.snapshotCost * 100 : 0,
|
|
1735
|
+
timestamp: e.timestamp.toISOString()
|
|
1736
|
+
})),
|
|
1737
|
+
summary: {
|
|
1738
|
+
totalSnapshot: executions.reduce((sum, e) => sum + e.snapshotCost, 0) / 100,
|
|
1739
|
+
totalCurrent: executions.reduce((sum, e) => sum + e.currentCost, 0) / 100
|
|
1740
|
+
}
|
|
1741
|
+
},
|
|
1742
|
+
null,
|
|
1743
|
+
2
|
|
1744
|
+
)
|
|
1745
|
+
);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
console.log(chalk.bold.cyan("\nCost History\n"));
|
|
1749
|
+
if (executions.length === 0) {
|
|
1750
|
+
console.log(chalk.yellow("No execution history found."));
|
|
1751
|
+
if (!validatedOptions.task && !validatedOptions.competition) {
|
|
1752
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
1753
|
+
const startTime = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
1754
|
+
logCostEmptyStateHint(modelInvocationsDb, startTime);
|
|
1755
|
+
}
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
console.log(
|
|
1759
|
+
`${chalk.bold("Execution".padEnd(30))} ${chalk.bold("Snapshot".padEnd(12))} ${chalk.bold("Current".padEnd(12))} ${chalk.bold("Delta")}`
|
|
1760
|
+
);
|
|
1761
|
+
console.log(chalk.gray("-".repeat(70)));
|
|
1762
|
+
executions.forEach((exec) => {
|
|
1763
|
+
const delta = exec.currentCost - exec.snapshotCost;
|
|
1764
|
+
const deltaPercent = exec.snapshotCost > 0 ? (delta / exec.snapshotCost * 100).toFixed(1) : "0.0";
|
|
1765
|
+
const direction = delta > 0 ? "up" : delta < 0 ? "down" : "flat";
|
|
1766
|
+
const color = delta > 0 ? chalk.red : delta < 0 ? chalk.green : chalk.gray;
|
|
1767
|
+
const label = `${exec.executionId.substring(0, 8)}... (${exec.provider}/${exec.model})`;
|
|
1768
|
+
console.log(
|
|
1769
|
+
`${label.padEnd(30)} ${formatCurrency(exec.snapshotCost / 100).padEnd(12)} ${formatCurrency(exec.currentCost / 100).padEnd(12)} ` + color(`${direction} ${deltaPercent}%`)
|
|
1770
|
+
);
|
|
1771
|
+
});
|
|
1772
|
+
const totalSnapshot = executions.reduce((sum, e) => sum + e.snapshotCost, 0);
|
|
1773
|
+
const totalCurrent = executions.reduce((sum, e) => sum + e.currentCost, 0);
|
|
1774
|
+
const totalDelta = totalCurrent - totalSnapshot;
|
|
1775
|
+
console.log(chalk.gray("-".repeat(70)));
|
|
1776
|
+
console.log(`${chalk.bold("Total (Snapshot)")}: ${formatCurrency(totalSnapshot / 100)}`);
|
|
1777
|
+
console.log(`${chalk.bold("Total (Current)")}: ${formatCurrency(totalCurrent / 100)}`);
|
|
1778
|
+
if (Math.abs(totalDelta) > 0.01) {
|
|
1779
|
+
const color = totalDelta > 0 ? chalk.red : chalk.green;
|
|
1780
|
+
console.log(
|
|
1781
|
+
`${chalk.bold("Savings/Cost")}: ${color(formatCurrency(Math.abs(totalDelta) / 100))}`
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
console.log(
|
|
1785
|
+
chalk.gray(
|
|
1786
|
+
"\nTip: Token usage unchanged - cost difference due to pricing updates or recalculation mode"
|
|
1787
|
+
)
|
|
1788
|
+
);
|
|
1789
|
+
});
|
|
1790
|
+
cost.command("tasks").description(
|
|
1791
|
+
"Show cost breakdown by task\n\nExamples:\n poetic cost tasks\n poetic cost tasks --days 30 --top 20\n poetic cost tasks --json"
|
|
1792
|
+
).option(
|
|
1793
|
+
"--days <days>",
|
|
1794
|
+
"(OPTIONAL) Number of recent days (default: 7)\n Example: --days 30",
|
|
1795
|
+
"7"
|
|
1796
|
+
).option(
|
|
1797
|
+
"--top <n>",
|
|
1798
|
+
"(OPTIONAL) Show top N most expensive tasks (default: 10)\n Example: --top 20",
|
|
1799
|
+
"10"
|
|
1800
|
+
).option("--json", "(OPTIONAL) Output as JSON").action(async (options) => {
|
|
1801
|
+
const telemetry = await deps.telemetry;
|
|
1802
|
+
const validatedOptions = validateCommandOptions(TasksOptionsSchema, options);
|
|
1803
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
1804
|
+
const startTime = Date.now() - (validatedOptions.days ?? 7) * 24 * 60 * 60 * 1e3;
|
|
1805
|
+
const executions = modelInvocationsDb.getExecutionCostsSince(startTime);
|
|
1806
|
+
const taskSummaries = /* @__PURE__ */ new Map();
|
|
1807
|
+
for (const execution of executions) {
|
|
1808
|
+
const taskId = execution.taskId ?? execution.competitionId ?? "unattributed";
|
|
1809
|
+
const existing = taskSummaries.get(taskId);
|
|
1810
|
+
if (existing) {
|
|
1811
|
+
existing.executionCount += 1;
|
|
1812
|
+
existing.totalCostCents += execution.costSnapshot.totalCostCents;
|
|
1813
|
+
existing.providers.add(execution.provider);
|
|
1814
|
+
if (execution.timestamp > existing.lastExecutionAt) {
|
|
1815
|
+
existing.lastExecutionAt = execution.timestamp;
|
|
1816
|
+
}
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
taskSummaries.set(taskId, {
|
|
1820
|
+
taskId,
|
|
1821
|
+
executionCount: 1,
|
|
1822
|
+
totalCostCents: execution.costSnapshot.totalCostCents,
|
|
1823
|
+
providers: /* @__PURE__ */ new Set([execution.provider]),
|
|
1824
|
+
lastExecutionAt: execution.timestamp
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
const rows = [...taskSummaries.values()].sort((a, b) => b.totalCostCents - a.totalCostCents).slice(0, validatedOptions.top ?? 10);
|
|
1828
|
+
if (validatedOptions.json) {
|
|
1829
|
+
console.log(
|
|
1830
|
+
JSON.stringify(
|
|
1831
|
+
{
|
|
1832
|
+
tasks: rows.map((row) => ({
|
|
1833
|
+
taskId: row.taskId,
|
|
1834
|
+
executionCount: row.executionCount,
|
|
1835
|
+
totalCost: row.totalCostCents / 100,
|
|
1836
|
+
providers: [...row.providers].sort(),
|
|
1837
|
+
lastExecutionAt: row.lastExecutionAt.toISOString()
|
|
1838
|
+
})),
|
|
1839
|
+
summary: {
|
|
1840
|
+
windowDays: validatedOptions.days ?? 7,
|
|
1841
|
+
totalTasks: taskSummaries.size,
|
|
1842
|
+
totalExecutions: executions.length
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
null,
|
|
1846
|
+
2
|
|
1847
|
+
)
|
|
1848
|
+
);
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
console.log(chalk.bold.cyan("\nTask Cost Breakdown\n"));
|
|
1852
|
+
if (rows.length === 0) {
|
|
1853
|
+
console.log(chalk.yellow("No task cost data found for the requested window."));
|
|
1854
|
+
logCostEmptyStateHint(modelInvocationsDb, startTime);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
console.log(
|
|
1858
|
+
`${chalk.bold("Task".padEnd(34))} ${chalk.bold("Cost".padEnd(12))} ${chalk.bold("Execs".padEnd(7))} ${chalk.bold("Providers")}`
|
|
1859
|
+
);
|
|
1860
|
+
console.log(chalk.gray("-".repeat(74)));
|
|
1861
|
+
for (const row of rows) {
|
|
1862
|
+
const label = row.taskId.length > 32 ? `${row.taskId.slice(0, 29)}...` : row.taskId.padEnd(32);
|
|
1863
|
+
console.log(
|
|
1864
|
+
`${label.padEnd(34)} ${formatCurrency(row.totalCostCents / 100).padEnd(12)} ${String(row.executionCount).padEnd(7)} ${[...row.providers].sort().join(", ")}`
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
telemetry.logCostEvent("cli_cost_tasks_viewed", {
|
|
1868
|
+
days: validatedOptions.days ?? 7,
|
|
1869
|
+
top: validatedOptions.top ?? 10,
|
|
1870
|
+
taskCount: taskSummaries.size,
|
|
1871
|
+
executionCount: executions.length
|
|
1872
|
+
});
|
|
1873
|
+
});
|
|
1874
|
+
cost.command("tokens").description(
|
|
1875
|
+
"Show token usage trends over time\n\nExamples:\n poetic cost tokens\n poetic cost tokens --days 30 --provider anthropic\n poetic cost tokens --provider cursor --model auto"
|
|
1876
|
+
).option(
|
|
1877
|
+
"--days <days>",
|
|
1878
|
+
"(OPTIONAL) Number of recent days (default: 7)\n Example: --days 30",
|
|
1879
|
+
"7"
|
|
1880
|
+
).option(
|
|
1881
|
+
"--provider <provider>",
|
|
1882
|
+
"(OPTIONAL) Filter by provider\n Example: --provider anthropic"
|
|
1883
|
+
).option("--model <model>", "(OPTIONAL) Filter by model\n Example: --model auto").option("--json", "(OPTIONAL) Output as JSON").action(async (options) => {
|
|
1884
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
1885
|
+
const telemetry = await deps.telemetry;
|
|
1886
|
+
const validatedOptions = validateCommandOptions(TokensOptionsSchema, options);
|
|
1887
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
1888
|
+
const endDate = /* @__PURE__ */ new Date();
|
|
1889
|
+
const startDate = new Date(endDate);
|
|
1890
|
+
startDate.setDate(startDate.getDate() - daysBack + 1);
|
|
1891
|
+
const startDateStr = startDate.toISOString().split("T")[0];
|
|
1892
|
+
const endDateStr = endDate.toISOString().split("T")[0];
|
|
1893
|
+
const dailyResult = modelInvocationsDb.getDailyCostAggregations(
|
|
1894
|
+
startDateStr,
|
|
1895
|
+
endDateStr,
|
|
1896
|
+
"date"
|
|
1897
|
+
);
|
|
1898
|
+
if (dailyResult.status === "error") {
|
|
1899
|
+
if (validatedOptions.json) {
|
|
1900
|
+
writeCostOutput(
|
|
1901
|
+
JSON.stringify(
|
|
1902
|
+
{
|
|
1903
|
+
status: "error",
|
|
1904
|
+
error: dailyResult.error ?? "daily cost aggregation failed",
|
|
1905
|
+
detail: dailyResult.detail,
|
|
1906
|
+
period: {
|
|
1907
|
+
start: startDateStr,
|
|
1908
|
+
end: endDateStr,
|
|
1909
|
+
days: daysBack
|
|
1910
|
+
},
|
|
1911
|
+
data: []
|
|
1912
|
+
},
|
|
1913
|
+
null,
|
|
1914
|
+
2
|
|
1915
|
+
)
|
|
1916
|
+
);
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
writeCostOutput(
|
|
1920
|
+
chalk.yellow(
|
|
1921
|
+
`Error: Token trends unavailable${dailyResult.error ? `: ${dailyResult.error}` : "."}`
|
|
1922
|
+
)
|
|
1923
|
+
);
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
const filteredData = dailyResult.rows.filter((day) => {
|
|
1927
|
+
if (validatedOptions.provider && day.provider !== validatedOptions.provider) {
|
|
1928
|
+
return false;
|
|
1929
|
+
}
|
|
1930
|
+
if (validatedOptions.model && day.model !== validatedOptions.model) {
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1933
|
+
return true;
|
|
1934
|
+
});
|
|
1935
|
+
telemetry.logCostEvent("cli_cost_tokens_viewed", {
|
|
1936
|
+
days: daysBack,
|
|
1937
|
+
provider: validatedOptions.provider ?? null,
|
|
1938
|
+
model: validatedOptions.model ?? null,
|
|
1939
|
+
recordCount: filteredData.length,
|
|
1940
|
+
status: dailyResult.status
|
|
1941
|
+
});
|
|
1942
|
+
if (validatedOptions.json) {
|
|
1943
|
+
console.log(
|
|
1944
|
+
JSON.stringify(
|
|
1945
|
+
{
|
|
1946
|
+
status: dailyResult.status,
|
|
1947
|
+
period: {
|
|
1948
|
+
start: startDateStr,
|
|
1949
|
+
end: endDateStr,
|
|
1950
|
+
days: daysBack
|
|
1951
|
+
},
|
|
1952
|
+
filters: {
|
|
1953
|
+
provider: validatedOptions.provider ?? null,
|
|
1954
|
+
model: validatedOptions.model ?? null
|
|
1955
|
+
},
|
|
1956
|
+
data: filteredData.map((day) => ({
|
|
1957
|
+
date: day.date,
|
|
1958
|
+
totalTokensIn: day.totalTokensIn,
|
|
1959
|
+
totalTokensOut: day.totalTokensOut,
|
|
1960
|
+
totalTokens: day.totalTokensIn + day.totalTokensOut,
|
|
1961
|
+
executionCount: day.executionCount,
|
|
1962
|
+
avgTokensPerExecution: day.executionCount > 0 ? (day.totalTokensIn + day.totalTokensOut) / day.executionCount : 0
|
|
1963
|
+
}))
|
|
1964
|
+
},
|
|
1965
|
+
null,
|
|
1966
|
+
2
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
console.log(chalk.bold.cyan("\nToken Usage Trends\n"));
|
|
1972
|
+
if (filteredData.length === 0) {
|
|
1973
|
+
console.log(chalk.yellow("No token usage data found for the specified period."));
|
|
1974
|
+
const startTimeMs = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
1975
|
+
logCostEmptyStateHint(modelInvocationsDb, startTimeMs);
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
console.log(`${chalk.gray("Period")}: ${startDateStr} to ${endDateStr} (${daysBack} days)`);
|
|
1979
|
+
if (validatedOptions.provider) {
|
|
1980
|
+
console.log(`${chalk.gray("Provider")}: ${validatedOptions.provider}`);
|
|
1981
|
+
}
|
|
1982
|
+
if (validatedOptions.model) {
|
|
1983
|
+
console.log(`${chalk.gray("Model")}: ${validatedOptions.model}`);
|
|
1984
|
+
}
|
|
1985
|
+
console.log(
|
|
1986
|
+
`
|
|
1987
|
+
${chalk.bold("Date".padEnd(12))} ${chalk.bold("Input".padEnd(12))} ${chalk.bold("Output".padEnd(12))} ${chalk.bold("Total".padEnd(12))} ${chalk.bold("Execs")}`
|
|
1988
|
+
);
|
|
1989
|
+
console.log(chalk.gray("-".repeat(65)));
|
|
1990
|
+
filteredData.forEach((day) => {
|
|
1991
|
+
const totalTokens = day.totalTokensIn + day.totalTokensOut;
|
|
1992
|
+
console.log(
|
|
1993
|
+
`${day.date.padEnd(12)} ${formatTokenCount(day.totalTokensIn).padEnd(12)} ${formatTokenCount(day.totalTokensOut).padEnd(12)} ${formatTokenCount(totalTokens).padEnd(12)} ${day.executionCount.toLocaleString()}`
|
|
1994
|
+
);
|
|
1995
|
+
});
|
|
1996
|
+
const totalIn = filteredData.reduce((sum, day) => sum + day.totalTokensIn, 0);
|
|
1997
|
+
const totalOut = filteredData.reduce((sum, day) => sum + day.totalTokensOut, 0);
|
|
1998
|
+
const totalExecs = filteredData.reduce((sum, day) => sum + day.executionCount, 0);
|
|
1999
|
+
console.log(chalk.gray("-".repeat(65)));
|
|
2000
|
+
console.log(`${chalk.bold("Total Input")}: ${formatTokenCount(totalIn)}`);
|
|
2001
|
+
console.log(`${chalk.bold("Total Output")}: ${formatTokenCount(totalOut)}`);
|
|
2002
|
+
console.log(`${chalk.bold("Total Tokens")}: ${formatTokenCount(totalIn + totalOut)}`);
|
|
2003
|
+
console.log(`${chalk.bold("Total Executions")}: ${totalExecs.toLocaleString()}`);
|
|
2004
|
+
console.log(
|
|
2005
|
+
chalk.gray(
|
|
2006
|
+
"\nTip: Token usage is independent of cost - use `poetic cost daily` for cost analysis"
|
|
2007
|
+
)
|
|
2008
|
+
);
|
|
2009
|
+
});
|
|
2010
|
+
cost.command("compare-pricing").description(
|
|
2011
|
+
"Compare recorded snapshot cost against current pricing\n\nExamples:\n poetic cost compare-pricing inv_123456\n poetic cost compare-pricing inv_123456 --json"
|
|
2012
|
+
).argument("<executionId>", "(REQUIRED) Execution ID to analyze").option("--json", "(OPTIONAL) Output as JSON").action(async (executionId, options) => {
|
|
2013
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
2014
|
+
const costCalculator = await deps.costCalculator;
|
|
2015
|
+
const telemetry = await deps.telemetry;
|
|
2016
|
+
const validatedOptions = validateCommandOptions(ComparePricingOptionsSchema, options);
|
|
2017
|
+
const execution = modelInvocationsDb.getExecutionCost(executionId);
|
|
2018
|
+
if (!execution) {
|
|
2019
|
+
if (validatedOptions.json) {
|
|
2020
|
+
console.log(
|
|
2021
|
+
JSON.stringify(
|
|
2022
|
+
{
|
|
2023
|
+
error: "execution_not_found",
|
|
2024
|
+
message: `Execution not found: ${executionId}`,
|
|
2025
|
+
executionId
|
|
2026
|
+
},
|
|
2027
|
+
null,
|
|
2028
|
+
2
|
|
2029
|
+
)
|
|
2030
|
+
);
|
|
2031
|
+
process.exitCode = 1;
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
console.error(chalk.red(`Execution not found: ${executionId}`));
|
|
2035
|
+
process.exitCode = 1;
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
const currentCost = modelInvocationsDb.recalculateCost(
|
|
2039
|
+
executionId,
|
|
2040
|
+
(provider, model, usage) => {
|
|
2041
|
+
try {
|
|
2042
|
+
const breakdown = costCalculator.calculateCost(provider, model, {
|
|
2043
|
+
inputTokens: usage.inputTokens,
|
|
2044
|
+
outputTokens: usage.outputTokens,
|
|
2045
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
2046
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
2047
|
+
tools: usage.toolCalls?.map((t) => ({
|
|
2048
|
+
name: t.name,
|
|
2049
|
+
requests: t.requests
|
|
2050
|
+
})) || [],
|
|
2051
|
+
executionTimeMinutes: usage.executionTimeMs ? usage.executionTimeMs / 6e4 : void 0
|
|
2052
|
+
});
|
|
2053
|
+
const availableUsd = availableBreakdownUsd(breakdown);
|
|
2054
|
+
if (availableUsd === null) {
|
|
2055
|
+
return execution.costSnapshot.totalCostCents;
|
|
2056
|
+
}
|
|
2057
|
+
return availableUsd * 100;
|
|
2058
|
+
} catch (error) {
|
|
2059
|
+
emitWarning(
|
|
2060
|
+
"PR-003",
|
|
2061
|
+
{
|
|
2062
|
+
taskId: executionId,
|
|
2063
|
+
subsystem: "cost-tracking",
|
|
2064
|
+
executionPhase: "execution"
|
|
2065
|
+
},
|
|
2066
|
+
{
|
|
2067
|
+
severity: "WARN" /* WARN */,
|
|
2068
|
+
metadata: {
|
|
2069
|
+
executionId,
|
|
2070
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2071
|
+
operation: "recalculateCostComparePricing"
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
);
|
|
2075
|
+
return execution.costSnapshot.totalCostCents;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
);
|
|
2079
|
+
telemetry.logCostEvent("cli_cost_compare_pricing_viewed", {
|
|
2080
|
+
executionId,
|
|
2081
|
+
snapshotCost: execution.costSnapshot.totalCostCents,
|
|
2082
|
+
currentCost: currentCost ?? execution.costSnapshot.totalCostCents
|
|
2083
|
+
});
|
|
2084
|
+
if (validatedOptions.json) {
|
|
2085
|
+
console.log(
|
|
2086
|
+
JSON.stringify(
|
|
2087
|
+
{
|
|
2088
|
+
executionId,
|
|
2089
|
+
provider: execution.provider,
|
|
2090
|
+
model: execution.model,
|
|
2091
|
+
usage: execution.usage,
|
|
2092
|
+
comparisons: {
|
|
2093
|
+
snapshot: {
|
|
2094
|
+
version: execution.pricingVersion,
|
|
2095
|
+
costCents: execution.costSnapshot.totalCostCents
|
|
2096
|
+
},
|
|
2097
|
+
current: {
|
|
2098
|
+
version: "current",
|
|
2099
|
+
costCents: currentCost ?? execution.costSnapshot.totalCostCents
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
null,
|
|
2104
|
+
2
|
|
2105
|
+
)
|
|
2106
|
+
);
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
console.log(chalk.bold.cyan("\nPricing Comparison\n"));
|
|
2110
|
+
console.log(`${chalk.gray("Execution ID")}: ${executionId}`);
|
|
2111
|
+
console.log(`${chalk.gray("Provider/Model")}: ${execution.provider}/${execution.model}`);
|
|
2112
|
+
console.log(
|
|
2113
|
+
`${chalk.gray("Tokens")}: ${formatTokenCount(execution.usage.inputTokens)} in, ${formatTokenCount(execution.usage.outputTokens)} out`
|
|
2114
|
+
);
|
|
2115
|
+
console.log(chalk.bold("\nCost Comparison\n"));
|
|
2116
|
+
console.log(
|
|
2117
|
+
`${chalk.bold("Version".padEnd(20))} ${chalk.bold("Cost".padEnd(12))} ${chalk.bold("\u0394 from snapshot")}`
|
|
2118
|
+
);
|
|
2119
|
+
console.log(chalk.gray("-".repeat(50)));
|
|
2120
|
+
const snapshotCost = execution.costSnapshot.totalCostCents;
|
|
2121
|
+
console.log(
|
|
2122
|
+
`${"Snapshot".padEnd(20)} ${formatCurrency(snapshotCost / 100).padEnd(12)} ${chalk.gray("(baseline)")}`
|
|
2123
|
+
);
|
|
2124
|
+
if (currentCost !== null) {
|
|
2125
|
+
const delta = currentCost - snapshotCost;
|
|
2126
|
+
const deltaPercent = snapshotCost > 0 ? (delta / snapshotCost * 100).toFixed(1) : "0.0";
|
|
2127
|
+
const direction = delta > 0 ? "up" : delta < 0 ? "down" : "flat";
|
|
2128
|
+
const color = delta > 0 ? chalk.red : delta < 0 ? chalk.green : chalk.gray;
|
|
2129
|
+
console.log(
|
|
2130
|
+
`${"Current".padEnd(20)} ${formatCurrency(currentCost / 100).padEnd(12)} ` + color(`${direction} ${deltaPercent}%`)
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
console.log(
|
|
2134
|
+
chalk.gray(
|
|
2135
|
+
"\nTip: Pricing versions reflect changes in provider pricing over time. Snapshot cost preserves historical accuracy."
|
|
2136
|
+
)
|
|
2137
|
+
);
|
|
2138
|
+
});
|
|
2139
|
+
cost.command("forecast").description(
|
|
2140
|
+
"Project future costs based on historical burn rate\n\nExamples:\n poetic cost forecast\n poetic cost forecast --days 14 --horizon 14\n poetic cost forecast --json"
|
|
2141
|
+
).option(
|
|
2142
|
+
"--days <days>",
|
|
2143
|
+
"(OPTIONAL) Historical lookback window in days (default: 7)\n Example: --days 14",
|
|
2144
|
+
"7"
|
|
2145
|
+
).option(
|
|
2146
|
+
"--horizon <days>",
|
|
2147
|
+
"(OPTIONAL) Number of future days to project (default: 7)\n Example: --horizon 14",
|
|
2148
|
+
"7"
|
|
2149
|
+
).option("--json", "(OPTIONAL) Output forecast as JSON").action(async (options) => {
|
|
2150
|
+
const modelInvocationsDb = await deps.modelInvocationsDb;
|
|
2151
|
+
const telemetry = await deps.telemetry;
|
|
2152
|
+
if (!modelInvocationsDb) {
|
|
2153
|
+
console.log(chalk.yellow("Cost forecasting requires a telemetry database."));
|
|
2154
|
+
console.log(chalk.gray("Ensure cost tracking is enabled and try again."));
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
const validatedOptions = validateCommandOptions(ForecastOptionsSchema, options);
|
|
2158
|
+
const today = /* @__PURE__ */ new Date();
|
|
2159
|
+
const endDate = today;
|
|
2160
|
+
const daysBack = validatedOptions.days ?? 7;
|
|
2161
|
+
const startDate = new Date(endDate);
|
|
2162
|
+
startDate.setDate(startDate.getDate() - daysBack + 1);
|
|
2163
|
+
const startDateStr = startDate.toISOString().split("T")[0];
|
|
2164
|
+
const endDateStr = endDate.toISOString().split("T")[0];
|
|
2165
|
+
const dailyResult = modelInvocationsDb.getDailyCostAggregations(
|
|
2166
|
+
startDateStr,
|
|
2167
|
+
endDateStr,
|
|
2168
|
+
"date"
|
|
2169
|
+
);
|
|
2170
|
+
const dailyData = dailyResult.rows;
|
|
2171
|
+
const horizonDays = validatedOptions.horizon ?? 7;
|
|
2172
|
+
if (dailyResult.status === "error" || dailyResult.status === "incomplete") {
|
|
2173
|
+
const reason = dailyResult.status === "error" ? dailyResult.error ?? "daily cost aggregation failed" : dailyResult.detail ?? `incomplete cost evidence (${dailyResult.pricedCount}/${dailyResult.executionCount} priced)`;
|
|
2174
|
+
if (validatedOptions.json) {
|
|
2175
|
+
writeCostOutput(
|
|
2176
|
+
JSON.stringify(
|
|
2177
|
+
{
|
|
2178
|
+
status: dailyResult.status,
|
|
2179
|
+
period: {
|
|
2180
|
+
start: startDateStr,
|
|
2181
|
+
end: endDateStr,
|
|
2182
|
+
days: dailyData.length
|
|
2183
|
+
},
|
|
2184
|
+
evidence: {
|
|
2185
|
+
executionCount: dailyResult.executionCount,
|
|
2186
|
+
pricedCount: dailyResult.pricedCount
|
|
2187
|
+
},
|
|
2188
|
+
message: dailyResult.status === "error" ? `Cost forecast unavailable: ${reason}` : `Cost forecast requires complete daily cost evidence. ${reason}`,
|
|
2189
|
+
...dailyResult.status === "error" ? { error: reason } : { detail: reason }
|
|
2190
|
+
},
|
|
2191
|
+
null,
|
|
2192
|
+
2
|
|
2193
|
+
)
|
|
2194
|
+
);
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
writeCostOutput(
|
|
2198
|
+
chalk.yellow(
|
|
2199
|
+
dailyResult.status === "error" ? `Error: Cost forecast unavailable: ${reason}` : `Cost forecast unavailable: incomplete cost evidence (${dailyResult.pricedCount} of ${dailyResult.executionCount} priced).`
|
|
2200
|
+
)
|
|
2201
|
+
);
|
|
2202
|
+
writeCostOutput(
|
|
2203
|
+
chalk.gray("Burn rate and projections are omitted until evidence is complete.")
|
|
2204
|
+
);
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
if (dailyData.length === 0) {
|
|
2208
|
+
if (validatedOptions.json) {
|
|
2209
|
+
console.log(
|
|
2210
|
+
JSON.stringify(
|
|
2211
|
+
{
|
|
2212
|
+
status: "complete",
|
|
2213
|
+
period: {
|
|
2214
|
+
start: startDateStr,
|
|
2215
|
+
end: endDateStr,
|
|
2216
|
+
days: 0
|
|
2217
|
+
},
|
|
2218
|
+
burnRate: {
|
|
2219
|
+
avgDailyCost: 0,
|
|
2220
|
+
trendSlope: 0
|
|
2221
|
+
},
|
|
2222
|
+
forecast: {
|
|
2223
|
+
horizonDays,
|
|
2224
|
+
dailyProjections: [],
|
|
2225
|
+
totalProjectedCost: 0
|
|
2226
|
+
}
|
|
2227
|
+
},
|
|
2228
|
+
null,
|
|
2229
|
+
2
|
|
2230
|
+
)
|
|
2231
|
+
);
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
console.log(chalk.yellow("No historical cost data found for the specified period."));
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
const totalCostSum = sumCompleteDailyCosts(dailyData);
|
|
2238
|
+
if (totalCostSum === null) {
|
|
2239
|
+
if (validatedOptions.json) {
|
|
2240
|
+
writeCostOutput(
|
|
2241
|
+
JSON.stringify(
|
|
2242
|
+
{
|
|
2243
|
+
status: "incomplete",
|
|
2244
|
+
period: {
|
|
2245
|
+
start: startDateStr,
|
|
2246
|
+
end: endDateStr,
|
|
2247
|
+
days: dailyData.length
|
|
2248
|
+
},
|
|
2249
|
+
message: "Cost forecast requires complete daily cost evidence."
|
|
2250
|
+
},
|
|
2251
|
+
null,
|
|
2252
|
+
2
|
|
2253
|
+
)
|
|
2254
|
+
);
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
writeCostOutput(chalk.yellow("Cost forecast unavailable: incomplete cost evidence."));
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
const avgDailyCost = totalCostSum / Math.max(dailyData.length, 1);
|
|
2261
|
+
const n = dailyData.length;
|
|
2262
|
+
let sumI = 0;
|
|
2263
|
+
let sumCost = 0;
|
|
2264
|
+
let sumICost = 0;
|
|
2265
|
+
let sumI2 = 0;
|
|
2266
|
+
for (let i = 0; i < n; i++) {
|
|
2267
|
+
const cost2 = dailyData[i]?.totalCost ?? 0;
|
|
2268
|
+
sumI += i;
|
|
2269
|
+
sumCost += cost2;
|
|
2270
|
+
sumICost += i * cost2;
|
|
2271
|
+
sumI2 += i * i;
|
|
2272
|
+
}
|
|
2273
|
+
const slopeDenominator = n * sumI2 - sumI * sumI;
|
|
2274
|
+
const slope = slopeDenominator !== 0 ? (n * sumICost - sumI * sumCost) / slopeDenominator : 0;
|
|
2275
|
+
const dailyProjections = [];
|
|
2276
|
+
for (let d = 0; d < horizonDays; d++) {
|
|
2277
|
+
const projDate = new Date(today);
|
|
2278
|
+
projDate.setDate(projDate.getDate() + d + 1);
|
|
2279
|
+
const projDateStr = projDate.toISOString().split("T")[0] ?? "";
|
|
2280
|
+
dailyProjections.push({
|
|
2281
|
+
date: projDateStr,
|
|
2282
|
+
projectedCost: Math.max(0, avgDailyCost)
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
const totalProjectedCost = dailyProjections.reduce((sum, p) => sum + p.projectedCost, 0);
|
|
2286
|
+
telemetry.logCostEvent("cli_cost_forecast_viewed", {
|
|
2287
|
+
days: validatedOptions.days,
|
|
2288
|
+
horizon: validatedOptions.horizon,
|
|
2289
|
+
status: "complete",
|
|
2290
|
+
avgDailyCost,
|
|
2291
|
+
totalProjectedCost,
|
|
2292
|
+
trendSlope: slope
|
|
2293
|
+
});
|
|
2294
|
+
if (validatedOptions.json) {
|
|
2295
|
+
console.log(
|
|
2296
|
+
JSON.stringify(
|
|
2297
|
+
{
|
|
2298
|
+
status: "complete",
|
|
2299
|
+
period: {
|
|
2300
|
+
start: startDateStr,
|
|
2301
|
+
end: endDateStr,
|
|
2302
|
+
days: dailyData.length
|
|
2303
|
+
},
|
|
2304
|
+
burnRate: {
|
|
2305
|
+
avgDailyCost,
|
|
2306
|
+
trendSlope: slope
|
|
2307
|
+
},
|
|
2308
|
+
forecast: {
|
|
2309
|
+
horizonDays,
|
|
2310
|
+
dailyProjections,
|
|
2311
|
+
totalProjectedCost
|
|
2312
|
+
}
|
|
2313
|
+
},
|
|
2314
|
+
null,
|
|
2315
|
+
2
|
|
2316
|
+
)
|
|
2317
|
+
);
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
const trendDirection = Math.abs(slope) < 1e-3 ? "stable" : slope > 0 ? "increasing" : "decreasing";
|
|
2321
|
+
console.log(chalk.bold.cyan(`
|
|
2322
|
+
Cost Forecast (next ${horizonDays} days)
|
|
2323
|
+
`));
|
|
2324
|
+
console.log(
|
|
2325
|
+
`${chalk.gray("Period analyzed")}: ${startDateStr} to ${endDateStr} (${dailyData.length} days)`
|
|
2326
|
+
);
|
|
2327
|
+
console.log(`${chalk.gray("Avg daily burn rate")}: ${formatCurrency(avgDailyCost)}`);
|
|
2328
|
+
console.log(`${chalk.gray("Trend")}: ${trendDirection}`);
|
|
2329
|
+
console.log(`
|
|
2330
|
+
${chalk.bold("Date".padEnd(12))} ${chalk.bold("Projected Cost")}`);
|
|
2331
|
+
console.log(chalk.gray("-".repeat(30)));
|
|
2332
|
+
dailyProjections.forEach((proj) => {
|
|
2333
|
+
console.log(`${proj.date.padEnd(12)} ${formatCurrency(proj.projectedCost)}`);
|
|
2334
|
+
});
|
|
2335
|
+
console.log(chalk.gray("-".repeat(30)));
|
|
2336
|
+
console.log(`${chalk.bold("Total projected")}: ${formatCurrency(totalProjectedCost)}`);
|
|
2337
|
+
});
|
|
2338
|
+
return cost;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
export {
|
|
2342
|
+
createCostCommand
|
|
2343
|
+
};
|
|
2344
|
+
//# sourceMappingURL=chunk-2BZ53J6J.js.map
|