chainlesschain 0.162.29 → 0.162.31
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/package.json +2 -2
- package/src/assets/web-panel/assets/AIOps-BqWP6FKu.js +1 -0
- package/src/assets/web-panel/assets/AIOps-CSpp7iGY.css +1 -0
- package/src/assets/web-panel/assets/{ActionButton-wRdLFzBQ.js → ActionButton-CXwMgOvX.js} +1 -1
- package/src/assets/web-panel/assets/Analytics-DAebZ4IY.js +3 -0
- package/src/assets/web-panel/assets/Analytics-DJcHjy5J.css +1 -0
- package/src/assets/web-panel/assets/AppLayout-CYsqYoME.js +9 -0
- package/src/assets/web-panel/assets/AppLayout-VlanTjPw.css +1 -0
- package/src/assets/web-panel/assets/Audit-BbTtX1Nf.js +1 -0
- package/src/assets/web-panel/assets/Audit-D8oxP0wr.css +1 -0
- package/src/assets/web-panel/assets/Backup-DgqY2Eb-.js +1 -0
- package/src/assets/web-panel/assets/Backup-Wi9CaP3d.css +1 -0
- package/src/assets/web-panel/assets/BaseInput-Cq2ZuSoO.js +1 -0
- package/src/assets/web-panel/assets/Chat-D2kqpUyO.js +8 -0
- package/src/assets/web-panel/assets/Chat-DDh58PCf.css +1 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C-svYkrC.js +1 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-gy5SDGw2.css +1 -0
- package/src/assets/web-panel/assets/{Checkbox-DoGqmF9g.js → Checkbox-_9swHpyc.js} +1 -1
- package/src/assets/web-panel/assets/Codegen-BB08p1fn.css +1 -0
- package/src/assets/web-panel/assets/Codegen-Cr9YbCPl.js +1 -0
- package/src/assets/web-panel/assets/{Col-C-9-jORK.js → Col--wdpCMxx.js} +1 -1
- package/src/assets/web-panel/assets/Community-Cbq1nrpo.css +1 -0
- package/src/assets/web-panel/assets/Community-DuFcVnLu.js +1 -0
- package/src/assets/web-panel/assets/{Compact-CemvxU3z.js → Compact-1yzYeT04.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-Dq3aU9Df.js +1 -0
- package/src/assets/web-panel/assets/Compliance-NoXr2Vof.css +1 -0
- package/src/assets/web-panel/assets/Cowork-BlXJRu6L.css +1 -0
- package/src/assets/web-panel/assets/Cowork-CrWcnIg8.js +6 -0
- package/src/assets/web-panel/assets/Cron-Bh6fKZ0h.js +2 -0
- package/src/assets/web-panel/assets/Crosschain-8ofPaWVW.js +1 -0
- package/src/assets/web-panel/assets/Crosschain-CgRAecoR.css +1 -0
- package/src/assets/web-panel/assets/DID-D3EiYm3w.js +2 -0
- package/src/assets/web-panel/assets/DID-DVsYcgvs.css +1 -0
- package/src/assets/web-panel/assets/Dashboard-BFjEdFne.js +3 -0
- package/src/assets/web-panel/assets/Dashboard-CAPSdFR4.css +1 -0
- package/src/assets/web-panel/assets/Dropdown-pYVPcP6O.js +1 -0
- package/src/assets/web-panel/assets/EmailListRenderer-DTPZPx_S.css +1 -0
- package/src/assets/web-panel/assets/EmailListRenderer-zBPodwJ1.js +1 -0
- package/src/assets/web-panel/assets/FamilyGuardDashboard-CyQTW6PW.js +1 -0
- package/src/assets/web-panel/assets/FamilyGuardDashboard-nd3idUVb.css +1 -0
- package/src/assets/web-panel/assets/Federation-Ctaq3zYq.js +1 -0
- package/src/assets/web-panel/assets/Federation-fGN6iMX_.css +1 -0
- package/src/assets/web-panel/assets/FormItemContext-CWYJCLq1.js +2 -0
- package/src/assets/web-panel/assets/GenericCardRenderer-B1g6t9R9.js +1 -0
- package/src/assets/web-panel/assets/GenericCardRenderer-o85VDYQS.css +1 -0
- package/src/assets/web-panel/assets/{Git-DPuaGtg7.css → Git-BdHhxBWR.css} +1 -1
- package/src/assets/web-panel/assets/Git-DH-v8iwd.js +2 -0
- package/src/assets/web-panel/assets/Governance-DtCS4NDm.css +1 -0
- package/src/assets/web-panel/assets/Governance-jZxXvOs5.js +1 -0
- package/src/assets/web-panel/assets/Inference-D07LRghn.js +1 -0
- package/src/assets/web-panel/assets/Inference-t-w0NgIS.css +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-4NU1S2Xm.css +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-DnGtRZhx.js +1 -0
- package/src/assets/web-panel/assets/Logs-D2pM9C4W.js +2 -0
- package/src/assets/web-panel/assets/Logs-DQbCaYhL.css +1 -0
- package/src/assets/web-panel/assets/Marketplace-Bvo5cuGa.css +1 -0
- package/src/assets/web-panel/assets/Marketplace-UyIO7C7r.js +1 -0
- package/src/assets/web-panel/assets/McpTools-534Q95Mx.css +1 -0
- package/src/assets/web-panel/assets/McpTools-Bf1gvZPf.js +7 -0
- package/src/assets/web-panel/assets/Memory-C1bWj4RN.js +2 -0
- package/src/assets/web-panel/assets/Memory-CleW_R6V.css +1 -0
- package/src/assets/web-panel/assets/MobileBridge-C_Ot1H_a.js +3 -0
- package/src/assets/web-panel/assets/MobileBridge-zJpPuf4E.css +1 -0
- package/src/assets/web-panel/assets/MobileProjects-BD4alw6-.css +1 -0
- package/src/assets/web-panel/assets/MobileProjects-zr-PpsT_.js +1 -0
- package/src/assets/web-panel/assets/Mtc-CXZGO9Sh.css +1 -0
- package/src/assets/web-panel/assets/Mtc-CnzFUz5J.js +6 -0
- package/src/assets/web-panel/assets/MtcAudit-C1JM8esp.css +1 -0
- package/src/assets/web-panel/assets/MtcAudit-CAAh99wz.js +19 -0
- package/src/assets/web-panel/assets/Multisig-BryR2Jpr.css +1 -0
- package/src/assets/web-panel/assets/Multisig-D6IAg6HE.js +13 -0
- package/src/assets/web-panel/assets/NLProgramming-BFMarxb0.js +1 -0
- package/src/assets/web-panel/assets/NLProgramming-DGDHq6Z9.css +1 -0
- package/src/assets/web-panel/assets/Notes-BKKmMtqV.css +1 -0
- package/src/assets/web-panel/assets/Notes-BRp9ro3t.js +7 -0
- package/src/assets/web-panel/assets/NotificationSettings-C0Au3Cxb.js +1 -0
- package/src/assets/web-panel/assets/NotificationSettings-CG3Kfuef.css +1 -0
- package/src/assets/web-panel/assets/OrderTableRenderer-CfrDjSHT.css +1 -0
- package/src/assets/web-panel/assets/OrderTableRenderer-ISp6btRY.js +1 -0
- package/src/assets/web-panel/assets/Organization-DYoxLBRX.js +4 -0
- package/src/assets/web-panel/assets/Organization-LVvqwG0D.css +1 -0
- package/src/assets/web-panel/assets/{Overflow-BFxt7NlK.js → Overflow-rO8JJWGJ.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-C_4H9tGA.js → OverrideContext-C2r4vyBb.js} +1 -1
- package/src/assets/web-panel/assets/P2P-DJleeXIK.js +2 -0
- package/src/assets/web-panel/assets/P2P-DcFvP7F-.css +1 -0
- package/src/assets/web-panel/assets/PdhVaultBrowser-DM5qghFp.js +7 -0
- package/src/assets/web-panel/assets/PdhVaultBrowser-DZZrUOr1.css +1 -0
- package/src/assets/web-panel/assets/Permissions-D5v4Beya.js +4 -0
- package/src/assets/web-panel/assets/Permissions-DsPE_IQy.css +1 -0
- package/src/assets/web-panel/assets/PersonalDataHub-BhpRlh9D.css +1 -0
- package/src/assets/web-panel/assets/PersonalDataHub-c2ZTX0Pv.js +9 -0
- package/src/assets/web-panel/assets/Pipeline-BItr5CyP.css +1 -0
- package/src/assets/web-panel/assets/Pipeline-Crrkyhpz.js +1 -0
- package/src/assets/web-panel/assets/Privacy-DZVyrJKa.js +1 -0
- package/src/assets/web-panel/assets/Privacy-F_ENsxZk.css +1 -0
- package/src/assets/web-panel/assets/ProjectInit-B86LEzDL.css +1 -0
- package/src/assets/web-panel/assets/ProjectInit-DKg7J0gz.js +2 -0
- package/src/assets/web-panel/assets/ProjectSettings-3ndmTvVH.js +2 -0
- package/src/assets/web-panel/assets/ProjectSettings-DK7KA_iX.css +1 -0
- package/src/assets/web-panel/assets/Projects-CmJeUDBW.css +1 -0
- package/src/assets/web-panel/assets/Projects-ll5wnj2L.js +1 -0
- package/src/assets/web-panel/assets/Providers-BeqBVMhB.js +1 -0
- package/src/assets/web-panel/assets/Providers-C6q_fvea.css +1 -0
- package/src/assets/web-panel/assets/QuickAsk-DKAAxzuA.js +1 -0
- package/src/assets/web-panel/assets/Recommend-Byu7IGei.js +1 -0
- package/src/assets/web-panel/assets/Recommend-D_bf91K6.css +1 -0
- package/src/assets/web-panel/assets/Reputation-BKhWAmCu.js +1 -0
- package/src/assets/web-panel/assets/Reputation-DiOsOleI.css +1 -0
- package/src/assets/web-panel/assets/Row-BFtn11O6.js +1 -0
- package/src/assets/web-panel/assets/RssFeed-CjcbkyUb.css +1 -0
- package/src/assets/web-panel/assets/RssFeed-D5a0PT0k.js +3 -0
- package/src/assets/web-panel/assets/Search-DAkuaZNe.js +1 -0
- package/src/assets/web-panel/assets/Search-Klk29S7C.css +1 -0
- package/src/assets/web-panel/assets/Security-BmrukMHn.css +1 -0
- package/src/assets/web-panel/assets/Security-C79Ml2Ms.js +4 -0
- package/src/assets/web-panel/assets/Services-BBk_jH6-.js +2 -0
- package/src/assets/web-panel/assets/Services-DAnVNhUG.css +1 -0
- package/src/assets/web-panel/assets/{Skeleton-DL5ebkDK.js → Skeleton-Cy0VvL0M.js} +1 -1
- package/src/assets/web-panel/assets/Skills-BUu9owbR.css +1 -0
- package/src/assets/web-panel/assets/Skills-OQNky3uI.js +1 -0
- package/src/assets/web-panel/assets/Sla-CbX1f8xN.js +1 -0
- package/src/assets/web-panel/assets/Sla-sodqCU4a.css +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-BIkoUjws.js +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-CgB9nFyq.css +1 -0
- package/src/assets/web-panel/assets/SyncSettings-B3hJqmBP.css +1 -0
- package/src/assets/web-panel/assets/SyncSettings-DG6Swk7G.js +2 -0
- package/src/assets/web-panel/assets/Tasks-C9R8sgyi.js +1 -0
- package/src/assets/web-panel/assets/Tasks-nbTUVXvt.css +1 -0
- package/src/assets/web-panel/assets/Templates-AaJPeCIz.js +1 -0
- package/src/assets/web-panel/assets/Templates-CpFsKk_l.css +1 -0
- package/src/assets/web-panel/assets/Tenant-MuPJnOjk.css +1 -0
- package/src/assets/web-panel/assets/Tenant-jVFRofww.js +1 -0
- package/src/assets/web-panel/assets/Terminal-DHBMzfK6.js +3 -0
- package/src/assets/web-panel/assets/Terminal-DHoveJwj.css +1 -0
- package/src/assets/web-panel/assets/TimelineRenderer-9RFfOHSI.js +1 -0
- package/src/assets/web-panel/assets/TimelineRenderer-BlTAtkOc.css +1 -0
- package/src/assets/web-panel/assets/Tokens-CynVae-o.css +1 -0
- package/src/assets/web-panel/assets/Tokens-ZTfwuABF.js +1 -0
- package/src/assets/web-panel/assets/{Trigger-Rzs-a8Xf.js → Trigger-Xo7uZNQs.js} +1 -1
- package/src/assets/web-panel/assets/Trust-C-WrW0Fj.css +1 -0
- package/src/assets/web-panel/assets/Trust-C0cTPYvn.js +1 -0
- package/src/assets/web-panel/assets/UkeySign-DmMKio71.js +1 -0
- package/src/assets/web-panel/assets/VideoEditing-BDFghpzP.css +1 -0
- package/src/assets/web-panel/assets/VideoEditing-DP7B-oGT.js +1 -0
- package/src/assets/web-panel/assets/Wallet-B1kZDARo.js +4 -0
- package/src/assets/web-panel/assets/Wallet-DUbpTRcg.css +1 -0
- package/src/assets/web-panel/assets/WebAuthn-Bo5kBx27.js +5 -0
- package/src/assets/web-panel/assets/WebAuthn-CVAFRfCQ.css +1 -0
- package/src/assets/web-panel/assets/WorkflowEditor-429lPWYk.css +1 -0
- package/src/assets/web-panel/assets/WorkflowEditor-DGI9SNHH.js +1 -0
- package/src/assets/web-panel/assets/{_getTag-BVc6NQ_K.js → _getTag-DnrRH4E6.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C7tndXfg.js → chat-y97W1CIG.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-DfnRZex1.js → collapseMotion-mxzzcDsg.js} +1 -1
- package/src/assets/web-panel/assets/{colors-dUAGx-eS.js → colors-DtTNo0sH.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-X13u92RU.js → compact-item-D0q0exuS.js} +1 -1
- package/src/assets/web-panel/assets/createContext-D7pLFs2I.js +1 -0
- package/src/assets/web-panel/assets/{debounce-D9xTw4Sx.js → debounce-CFGTTtN3.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BDK34w0I.js +1 -0
- package/src/assets/web-panel/assets/echarts-jxgIOFUZ.js +25 -0
- package/src/assets/web-panel/assets/{hasIn-DGFXWm7Y.js → hasIn-CXjG5B2j.js} +1 -1
- package/src/assets/web-panel/assets/{icons--SBOhPAi.js → icons-DP3uiYxy.js} +4 -4
- package/src/assets/web-panel/assets/{index-Deh_HoQ3.js → index-1dwtkcJv.js} +2 -2
- package/src/assets/web-panel/assets/index-4mWZhCzz.js +1 -0
- package/src/assets/web-panel/assets/index-6np5ESBM.js +1 -0
- package/src/assets/web-panel/assets/{index-Bewc5jaO.js → index-8jxbZupG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DW1GR68q.js → index-B3y_4OdG.js} +1 -1
- package/src/assets/web-panel/assets/index-B4dPdrvC.js +1 -0
- package/src/assets/web-panel/assets/index-B6SaRuCI.js +1 -0
- package/src/assets/web-panel/assets/index-B9ekWb3I.js +1 -0
- package/src/assets/web-panel/assets/index-BJUf19Wd.js +73 -0
- package/src/assets/web-panel/assets/index-BO644Q4S.js +21 -0
- package/src/assets/web-panel/assets/index-BPXhU-jp.js +7 -0
- package/src/assets/web-panel/assets/index-BU944DeT.js +31 -0
- package/src/assets/web-panel/assets/index-B_hjkMtX.js +3 -0
- package/src/assets/web-panel/assets/index-BdhEYW2a.js +1 -0
- package/src/assets/web-panel/assets/{index-DBfn83eM.js → index-BgmvrPJH.js} +1 -1
- package/src/assets/web-panel/assets/index-BgyrM0UN.js +19 -0
- package/src/assets/web-panel/assets/{index-Cq-fdWWC.js → index-BnLrbXDA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C54Ydkuz.js → index-BqVjUN8b.js} +4 -4
- package/src/assets/web-panel/assets/index-BzCPx1cq.js +21 -0
- package/src/assets/web-panel/assets/index-CFsPe2N7.js +3 -0
- package/src/assets/web-panel/assets/{index-C14mc-46.js → index-CKrbutAQ.js} +1 -1
- package/src/assets/web-panel/assets/index-CSdhC7Qo.js +1 -0
- package/src/assets/web-panel/assets/{index-BqX3jeij.js → index-Cbqu804A.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvlrcZKy.js → index-CkGFqlYX.js} +4 -4
- package/src/assets/web-panel/assets/{index-CfX1DEtk.css → index-Cq93VfoF.css} +1 -1
- package/src/assets/web-panel/assets/index-Ct6xtKkc.js +1 -0
- package/src/assets/web-panel/assets/index-DY6KLlgG.js +1 -0
- package/src/assets/web-panel/assets/index-D_4WcI1V.js +13 -0
- package/src/assets/web-panel/assets/{index-Ced-t9AY.js → index-DjCawXk1.js} +3 -3
- package/src/assets/web-panel/assets/index-Dr45Nm9V.js +55 -0
- package/src/assets/web-panel/assets/index-EaIfumgW.js +1 -0
- package/src/assets/web-panel/assets/index-POaFzYGS.js +1 -0
- package/src/assets/web-panel/assets/index-TrBGgrwG.js +1 -0
- package/src/assets/web-panel/assets/{index-DrH9uW99.js → index-YWOEx3rP.js} +2 -2
- package/src/assets/web-panel/assets/{index-DZMiVgXE.js → index-_3wPBMKt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBnkvGxd.js → index-aarO4HT9.js} +1 -1
- package/src/assets/web-panel/assets/index-bVJvqDAz.js +1 -0
- package/src/assets/web-panel/assets/{index-BipKV1Yr.js → index-gFLQe31v.js} +2 -2
- package/src/assets/web-panel/assets/index-kvV0f4tV.js +12 -0
- package/src/assets/web-panel/assets/index-qoB3whR9.js +12 -0
- package/src/assets/web-panel/assets/{initDefaultProps-B8zdOlFC.js → initDefaultProps-BnXISaAa.js} +1 -1
- package/src/assets/web-panel/assets/{isPlainObject-_gz-soRW.js → isPlainObject-BWRMT62c.js} +1 -1
- package/src/assets/web-panel/assets/isSymbol-S5WZT7lS.js +1 -0
- package/src/assets/web-panel/assets/motion-ChY7C0zJ.js +11 -0
- package/src/assets/web-panel/assets/{move-BioqdqDn.js → move-ByFZMFM5.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Cc3pB9Xm.js → mtc-parser-BbAbUB-9.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C1hn14Vc.js → omit-BYeliY1H.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DgVG8oMj.js → pickAttrs-B9dcAKnu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-cJY6hoXZ.js → placementArrow-D3F_txz7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DbUdURRx.js → responsiveObserve-ClkwY7wS.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C2OEWnC4.js → slide-BNgy2Eea.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DghgcGqm.js → statusUtils-Bv3heMCD.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-GoQ7uwU1.js → styleChecker-DVdlHbQm.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-alrRY5BK.js +1 -0
- package/src/assets/web-panel/assets/{useFs-CZCJDMup.js → useFs-CcVh0-Vu.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-WwedrFR0.js → useMergedState-Cl2q1fes.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BjAS9b7w.js → usePersonalDataHub-CkkHPhyq.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-Cdq8EWeF.js → useRefs-DD9q4mOT.js} +1 -1
- package/src/assets/web-panel/assets/{useState-DGS1NOyn.js → useState-BK4hy8Ma.js} +1 -1
- package/src/assets/web-panel/assets/vendor-BvqAck49.js +42 -0
- package/src/assets/web-panel/assets/vnode-DWi0X9WN.js +1 -0
- package/src/assets/web-panel/assets/{zoom-CzyiNxpd.js → zoom-DCbqxxLH.js} +1 -1
- package/src/assets/web-panel/index.html +4 -4
- package/src/commands/agent.js +306 -1
- package/src/commands/ask.js +35 -1
- package/src/commands/checkpoint.js +239 -0
- package/src/commands/cost.js +114 -0
- package/src/commands/session.js +22 -2
- package/src/index.js +4 -0
- package/src/lib/file-checkpoint.js +300 -0
- package/src/lib/llm-pricing.js +227 -0
- package/src/lib/personal-data-hub-wiring.js +30 -0
- package/src/lib/recent-session.js +72 -0
- package/src/lib/session-picker.js +68 -0
- package/src/repl/agent-repl.js +58 -2
- package/src/repl/chat-repl.js +16 -1
- package/src/runtime/agent-core.js +68 -31
- package/src/runtime/fallback-model.js +109 -0
- package/src/runtime/file-ref-expander.js +258 -0
- package/src/runtime/headless-runner.js +576 -0
- package/src/runtime/headless-stream.js +302 -0
- package/src/runtime/policies/agent-policy.js +6 -0
- package/src/runtime/quiet-stdout.js +35 -0
- package/src/runtime/system-prompt.js +60 -0
- package/src/assets/web-panel/assets/AIOps-BWWsdf3s.js +0 -1
- package/src/assets/web-panel/assets/AIOps-Cchx1iXI.css +0 -1
- package/src/assets/web-panel/assets/Analytics-C1-TXmTC.css +0 -1
- package/src/assets/web-panel/assets/Analytics-CU1ellhG.js +0 -3
- package/src/assets/web-panel/assets/AppLayout-9pWRQa_t.css +0 -1
- package/src/assets/web-panel/assets/AppLayout-cXY-mRTd.js +0 -3
- package/src/assets/web-panel/assets/Audit-6ZMsXmrO.css +0 -1
- package/src/assets/web-panel/assets/Audit-CtQuC6zy.js +0 -1
- package/src/assets/web-panel/assets/Backup-CY9QozR7.css +0 -1
- package/src/assets/web-panel/assets/Backup-WeATa8CV.js +0 -1
- package/src/assets/web-panel/assets/BaseInput-BCu2Cejw.js +0 -1
- package/src/assets/web-panel/assets/Chat-Bj0XPlH8.js +0 -7
- package/src/assets/web-panel/assets/Chat-IlrLB9ZV.css +0 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Bpp2sN9W.js +0 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CO908iBt.css +0 -1
- package/src/assets/web-panel/assets/Codegen-BLP7id2a.css +0 -1
- package/src/assets/web-panel/assets/Codegen-PeBhNei5.js +0 -1
- package/src/assets/web-panel/assets/Community-BvaB_Wcs.js +0 -1
- package/src/assets/web-panel/assets/Community-C2RejeOY.css +0 -1
- package/src/assets/web-panel/assets/Compliance-DOys4Ov1.css +0 -1
- package/src/assets/web-panel/assets/Compliance-DjquEqsU.js +0 -1
- package/src/assets/web-panel/assets/Cowork-Bv6Tm40_.js +0 -6
- package/src/assets/web-panel/assets/Cowork-DsOst4XK.css +0 -1
- package/src/assets/web-panel/assets/Cron-D9fPIXV8.js +0 -2
- package/src/assets/web-panel/assets/Crosschain-C0WilDBF.js +0 -1
- package/src/assets/web-panel/assets/Crosschain-C7Le4Pte.css +0 -1
- package/src/assets/web-panel/assets/DID-BX6k3jZi.css +0 -1
- package/src/assets/web-panel/assets/DID-CiGeAI1K.js +0 -2
- package/src/assets/web-panel/assets/Dashboard-MFDcsVcM.css +0 -1
- package/src/assets/web-panel/assets/Dashboard-zqQuizB8.js +0 -3
- package/src/assets/web-panel/assets/Dropdown-_Mjb8yOR.js +0 -1
- package/src/assets/web-panel/assets/EmailListRenderer-C369EVhV.js +0 -1
- package/src/assets/web-panel/assets/EmailListRenderer-CVJ1j3Ll.css +0 -1
- package/src/assets/web-panel/assets/FamilyGuardDashboard-D8WsrCmn.js +0 -1
- package/src/assets/web-panel/assets/FamilyGuardDashboard-Df-D09Q4.css +0 -1
- package/src/assets/web-panel/assets/Federation-CgmfLbx1.css +0 -1
- package/src/assets/web-panel/assets/Federation-Dq7CrAzj.js +0 -1
- package/src/assets/web-panel/assets/FormItemContext-C7TvuLVK.js +0 -1
- package/src/assets/web-panel/assets/GenericCardRenderer-BJUR1Emh.js +0 -1
- package/src/assets/web-panel/assets/GenericCardRenderer-DtJFcJYl.css +0 -1
- package/src/assets/web-panel/assets/Git-B5MdAp15.js +0 -2
- package/src/assets/web-panel/assets/Governance-BIbPWlSM.js +0 -1
- package/src/assets/web-panel/assets/Governance-BoipmXaM.css +0 -1
- package/src/assets/web-panel/assets/Inference-BWxYJF9-.css +0 -1
- package/src/assets/web-panel/assets/Inference-vOz9J05E.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-B_vUO-Td.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-CztPDA96.css +0 -1
- package/src/assets/web-panel/assets/Logs-BwPfxX3p.js +0 -2
- package/src/assets/web-panel/assets/Logs-Gf_Mv9Nx.css +0 -1
- package/src/assets/web-panel/assets/Marketplace-Djp5q9dS.css +0 -1
- package/src/assets/web-panel/assets/Marketplace-T5HNwUjX.js +0 -1
- package/src/assets/web-panel/assets/McpTools-CDjHmzxH.css +0 -1
- package/src/assets/web-panel/assets/McpTools-CuKeBPxy.js +0 -5
- package/src/assets/web-panel/assets/Memory-Bcc2hxOA.css +0 -1
- package/src/assets/web-panel/assets/Memory-DmRW_sTE.js +0 -2
- package/src/assets/web-panel/assets/MobileBridge-DK2N_MJA.js +0 -3
- package/src/assets/web-panel/assets/MobileBridge-DwxX55b_.css +0 -1
- package/src/assets/web-panel/assets/MobileProjects-Ceaxrqzc.css +0 -1
- package/src/assets/web-panel/assets/MobileProjects-wl3bXCnJ.js +0 -1
- package/src/assets/web-panel/assets/Mtc-C8e0941i.js +0 -6
- package/src/assets/web-panel/assets/Mtc-DHwbdUqA.css +0 -1
- package/src/assets/web-panel/assets/MtcAudit-B0x8gahq.css +0 -1
- package/src/assets/web-panel/assets/MtcAudit-Cwigy2IR.js +0 -19
- package/src/assets/web-panel/assets/Multisig-BSfh_ncy.js +0 -7
- package/src/assets/web-panel/assets/Multisig-kwPDnXnl.css +0 -1
- package/src/assets/web-panel/assets/NLProgramming-BwzJAfJg.js +0 -1
- package/src/assets/web-panel/assets/NLProgramming-CLOvy-35.css +0 -1
- package/src/assets/web-panel/assets/Notes-COE2q2gD.css +0 -1
- package/src/assets/web-panel/assets/Notes-uilHH6hE.js +0 -7
- package/src/assets/web-panel/assets/NotificationSettings-Co0e0uEO.css +0 -1
- package/src/assets/web-panel/assets/NotificationSettings-DvM3DTPW.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BIEm3hZl.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BnOISpKI.css +0 -1
- package/src/assets/web-panel/assets/Organization-BX-cIO8M.css +0 -1
- package/src/assets/web-panel/assets/Organization-zxWoIwYM.js +0 -4
- package/src/assets/web-panel/assets/P2P-BsbToYKZ.js +0 -2
- package/src/assets/web-panel/assets/P2P-Cx88UaiD.css +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-B9ZGFpn4.css +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-VU8vfhsS.js +0 -7
- package/src/assets/web-panel/assets/Permissions-C10Sd26B.js +0 -4
- package/src/assets/web-panel/assets/Permissions-C9WlkGl-.css +0 -1
- package/src/assets/web-panel/assets/PersonalDataHub-Bz5u9Gth.css +0 -1
- package/src/assets/web-panel/assets/PersonalDataHub-edJB7VeU.js +0 -2
- package/src/assets/web-panel/assets/Pipeline-DxkXqrH2.css +0 -1
- package/src/assets/web-panel/assets/Pipeline-qNmVxn5A.js +0 -1
- package/src/assets/web-panel/assets/Privacy-CrsfSFKd.css +0 -1
- package/src/assets/web-panel/assets/Privacy-h78XF7eO.js +0 -1
- package/src/assets/web-panel/assets/ProjectInit-C9-vm1no.js +0 -2
- package/src/assets/web-panel/assets/ProjectInit-CteEo-wF.css +0 -1
- package/src/assets/web-panel/assets/ProjectSettings-DpDNsQTb.js +0 -2
- package/src/assets/web-panel/assets/ProjectSettings-pLSae-wy.css +0 -1
- package/src/assets/web-panel/assets/Projects-BX-SG9R4.css +0 -1
- package/src/assets/web-panel/assets/Projects-CwU3W4RD.js +0 -1
- package/src/assets/web-panel/assets/Providers-BNI-WkmA.css +0 -1
- package/src/assets/web-panel/assets/Providers-C3KZlUEI.js +0 -1
- package/src/assets/web-panel/assets/QuickAsk-CILvLGze.js +0 -1
- package/src/assets/web-panel/assets/Recommend-CH6wKzGo.css +0 -1
- package/src/assets/web-panel/assets/Recommend-XfFlk3BY.js +0 -1
- package/src/assets/web-panel/assets/Reputation-BqmWvaMD.js +0 -1
- package/src/assets/web-panel/assets/Reputation-D6VPNEd0.css +0 -1
- package/src/assets/web-panel/assets/Row-BEbjCI_X.js +0 -1
- package/src/assets/web-panel/assets/RssFeed-BlFC20eg.css +0 -1
- package/src/assets/web-panel/assets/RssFeed-CFukIl-a.js +0 -3
- package/src/assets/web-panel/assets/Search-B6RalzTB.css +0 -1
- package/src/assets/web-panel/assets/Search-dVPkf1o2.js +0 -1
- package/src/assets/web-panel/assets/Security-13K57V_v.css +0 -1
- package/src/assets/web-panel/assets/Security-DlRFaUrn.js +0 -4
- package/src/assets/web-panel/assets/Services-C8Qs6KXv.css +0 -1
- package/src/assets/web-panel/assets/Services-DKy7AjpA.js +0 -2
- package/src/assets/web-panel/assets/Skills-BEqnpg2j.js +0 -1
- package/src/assets/web-panel/assets/Skills-BdjRyorN.css +0 -1
- package/src/assets/web-panel/assets/Sla-CZ9_lzPI.js +0 -1
- package/src/assets/web-panel/assets/Sla-K19oOyQc.css +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DVSY_ehZ.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DYPJTDKz.css +0 -1
- package/src/assets/web-panel/assets/SyncSettings-C6cDrwDR.css +0 -1
- package/src/assets/web-panel/assets/SyncSettings-DgvsRpGt.js +0 -1
- package/src/assets/web-panel/assets/Tasks-BJjN_YEm.css +0 -1
- package/src/assets/web-panel/assets/Tasks-L5qXKsxK.js +0 -1
- package/src/assets/web-panel/assets/Templates-BWTV8-2E.css +0 -1
- package/src/assets/web-panel/assets/Templates-DR4VBx7q.js +0 -1
- package/src/assets/web-panel/assets/Tenant-D3zkSAV0.css +0 -1
- package/src/assets/web-panel/assets/Tenant-jMyaK184.js +0 -1
- package/src/assets/web-panel/assets/Terminal-DA5gjCiE.js +0 -3
- package/src/assets/web-panel/assets/Terminal-G1DrFtKr.css +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-BM17JHyr.js +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-DmztdsbF.css +0 -1
- package/src/assets/web-panel/assets/Tokens-BBOdNRHQ.css +0 -1
- package/src/assets/web-panel/assets/Tokens-CCJIrnhi.js +0 -1
- package/src/assets/web-panel/assets/Trust-BHeJEfNG.js +0 -1
- package/src/assets/web-panel/assets/Trust-DeOo0xAh.css +0 -1
- package/src/assets/web-panel/assets/UkeySign-DjcIVRk4.js +0 -1
- package/src/assets/web-panel/assets/VideoEditing-BPhptg3q.js +0 -1
- package/src/assets/web-panel/assets/VideoEditing-DksiizfS.css +0 -1
- package/src/assets/web-panel/assets/Wallet-D_xDgfaz.js +0 -4
- package/src/assets/web-panel/assets/Wallet-gR0ZvZbK.css +0 -1
- package/src/assets/web-panel/assets/WebAuthn-SSh5VhVO.css +0 -1
- package/src/assets/web-panel/assets/WebAuthn-pd961lgM.js +0 -5
- package/src/assets/web-panel/assets/WorkflowEditor-BmXDUZ8M.js +0 -1
- package/src/assets/web-panel/assets/WorkflowEditor-IiwsD8Kh.css +0 -1
- package/src/assets/web-panel/assets/createContext-BBLoTzN8.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CNIS3FrJ.js +0 -1
- package/src/assets/web-panel/assets/echarts-Dj_pBaVI.js +0 -19
- package/src/assets/web-panel/assets/favicon-FSU2gSy6.ico +0 -0
- package/src/assets/web-panel/assets/index-1xUFGcnI.js +0 -21
- package/src/assets/web-panel/assets/index-4BeUpRaU.js +0 -1
- package/src/assets/web-panel/assets/index-AVHrO1io.js +0 -21
- package/src/assets/web-panel/assets/index-B2fc2n_u.js +0 -3
- package/src/assets/web-panel/assets/index-B5aXiiOG.js +0 -1
- package/src/assets/web-panel/assets/index-B78VsLFL.js +0 -1
- package/src/assets/web-panel/assets/index-BECv44dY.js +0 -12
- package/src/assets/web-panel/assets/index-BGn5-2TK.js +0 -1
- package/src/assets/web-panel/assets/index-BM9IHnrf.js +0 -13
- package/src/assets/web-panel/assets/index-BlMshk9V.js +0 -1
- package/src/assets/web-panel/assets/index-BoWoWh9f.js +0 -19
- package/src/assets/web-panel/assets/index-Bt79ud6Y.js +0 -1
- package/src/assets/web-panel/assets/index-CO9Tt0Bo.js +0 -1
- package/src/assets/web-panel/assets/index-CSZvB2uy.js +0 -12
- package/src/assets/web-panel/assets/index-CjeazYU2.js +0 -1
- package/src/assets/web-panel/assets/index-Cw-TEBru.js +0 -1
- package/src/assets/web-panel/assets/index-CyObfnlZ.js +0 -3
- package/src/assets/web-panel/assets/index-D0d2TS5o.js +0 -69
- package/src/assets/web-panel/assets/index-DV3zNhRX.js +0 -1
- package/src/assets/web-panel/assets/index-DnHElN_A.js +0 -7
- package/src/assets/web-panel/assets/index-DspW4reY.js +0 -55
- package/src/assets/web-panel/assets/index-DxUFdvJ5.js +0 -1
- package/src/assets/web-panel/assets/index-La4F-U-s.js +0 -1
- package/src/assets/web-panel/assets/index-azbNWBMo.js +0 -1
- package/src/assets/web-panel/assets/index-squRDiXd.js +0 -31
- package/src/assets/web-panel/assets/isSymbol-DAy2hlR5.js +0 -1
- package/src/assets/web-panel/assets/motion-ChVLXdRp.js +0 -11
- package/src/assets/web-panel/assets/useFlexGapSupport-CzD0s7wN.js +0 -1
- package/src/assets/web-panel/assets/vendor-DhFY8mDK.js +0 -1
- package/src/assets/web-panel/assets/vnode-CnXQ7gJ2.js +0 -1
- package/src/assets/web-panel/assets/warning-Pq00owYb.js +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cc cost [sessionId] — estimated $ spend from recorded token usage.
|
|
3
|
+
*
|
|
4
|
+
* Builds on `cc session usage` (token aggregation) by layering the
|
|
5
|
+
* llm-pricing rate table on top. Reads the same JSONL token_usage events,
|
|
6
|
+
* so no new data is collected — this is purely a reporting view.
|
|
7
|
+
*
|
|
8
|
+
* cc cost # global rollup across all sessions
|
|
9
|
+
* cc cost <sessionId> # one session
|
|
10
|
+
* cc cost --json # machine-readable
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
import { logger } from "../lib/logger.js";
|
|
15
|
+
|
|
16
|
+
function fmtUsd(n) {
|
|
17
|
+
const v = Number(n) || 0;
|
|
18
|
+
// Sub-cent costs are common with cheap models — show enough precision.
|
|
19
|
+
if (v === 0) return "$0.00";
|
|
20
|
+
if (v < 0.01) return `$${v.toFixed(6)}`;
|
|
21
|
+
return `$${v.toFixed(4)}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function registerCostCommand(program) {
|
|
25
|
+
program
|
|
26
|
+
.command("cost")
|
|
27
|
+
.description(
|
|
28
|
+
"Estimated $ cost from recorded token usage (per-session or global)",
|
|
29
|
+
)
|
|
30
|
+
.argument("[id]", "Session ID (omit for global rollup)")
|
|
31
|
+
.option("--json", "Output as JSON")
|
|
32
|
+
.option("--limit <n>", "Max sessions for global rollup", "1000")
|
|
33
|
+
.action(async (id, options) => {
|
|
34
|
+
try {
|
|
35
|
+
const { sessionUsage, allSessionsUsage } =
|
|
36
|
+
await import("../lib/session-usage.js");
|
|
37
|
+
const { priceRollup, mergePricing } =
|
|
38
|
+
await import("../lib/llm-pricing.js");
|
|
39
|
+
const { loadConfig } = await import("../lib/config-manager.js");
|
|
40
|
+
|
|
41
|
+
// User price overrides live under config.llm.pricing — no need to edit
|
|
42
|
+
// source to correct/add a model rate.
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
const overrides = config?.llm?.pricing;
|
|
45
|
+
const table = mergePricing(overrides);
|
|
46
|
+
|
|
47
|
+
const raw = id
|
|
48
|
+
? sessionUsage(id)
|
|
49
|
+
: allSessionsUsage({ limit: parseInt(options.limit, 10) || 1000 });
|
|
50
|
+
const result = priceRollup(raw, { table });
|
|
51
|
+
|
|
52
|
+
if (options.json) {
|
|
53
|
+
console.log(JSON.stringify(result, null, 2));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const header = id
|
|
58
|
+
? `Cost — session ${chalk.gray(id.slice(0, 16))}`
|
|
59
|
+
: "Cost — global";
|
|
60
|
+
logger.log(chalk.bold(header));
|
|
61
|
+
logger.log(
|
|
62
|
+
` total: ${chalk.green(fmtUsd(result.cost.totalCost))} USD ` +
|
|
63
|
+
`(${result.total.totalTokens.toLocaleString()} tokens, ${result.total.calls} calls)`,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (result.byModel.length === 0) {
|
|
67
|
+
logger.log(chalk.gray(" (no token_usage events recorded)"));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const row of result.byModel) {
|
|
72
|
+
const provider = (row.provider || "?").padEnd(10);
|
|
73
|
+
const model = (row.model || "?").padEnd(24);
|
|
74
|
+
const tokens = `in=${row.inputTokens} out=${row.outputTokens}`;
|
|
75
|
+
if (row.free) {
|
|
76
|
+
logger.log(
|
|
77
|
+
` ${chalk.gray(provider)} ${chalk.white(model)} ${chalk.gray("free (local)")} ${chalk.gray(tokens)}`,
|
|
78
|
+
);
|
|
79
|
+
} else if (row.matched) {
|
|
80
|
+
logger.log(
|
|
81
|
+
` ${chalk.gray(provider)} ${chalk.white(model)} ${chalk.green(fmtUsd(row.cost).padStart(12))} ${chalk.gray(tokens)}`,
|
|
82
|
+
);
|
|
83
|
+
} else {
|
|
84
|
+
logger.log(
|
|
85
|
+
` ${chalk.gray(provider)} ${chalk.white(model)} ${chalk.yellow("unpriced".padStart(12))} ${chalk.gray(tokens)}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (result.unpriced.length > 0) {
|
|
91
|
+
logger.log(
|
|
92
|
+
chalk.yellow(
|
|
93
|
+
` note: ${result.unpriced.length} model(s) have no rate — tokens excluded from total. Add rates via config: llm.pricing.`,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (overrides && typeof overrides === "object") {
|
|
98
|
+
logger.log(
|
|
99
|
+
chalk.gray(
|
|
100
|
+
` (price overrides active from config.llm.pricing: ${Object.keys(overrides).join(", ")})`,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
logger.log(
|
|
105
|
+
chalk.gray(
|
|
106
|
+
" prices are estimates of public list rates (USD/1M tokens).",
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
logger.error(chalk.red(`cost failed: ${err.message}`));
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
package/src/commands/session.js
CHANGED
|
@@ -209,8 +209,13 @@ export function registerSessionCommand(program) {
|
|
|
209
209
|
// session resume
|
|
210
210
|
session
|
|
211
211
|
.command("resume")
|
|
212
|
-
.description(
|
|
213
|
-
|
|
212
|
+
.description(
|
|
213
|
+
"Resume a session in chat mode (id optional — interactive picker, or most recent when piped)",
|
|
214
|
+
)
|
|
215
|
+
.argument(
|
|
216
|
+
"[id]",
|
|
217
|
+
"Session ID (or prefix); omit to pick / resume the most recent",
|
|
218
|
+
)
|
|
214
219
|
.option("--model <model>", "Model name")
|
|
215
220
|
.option("--provider <provider>", "LLM provider")
|
|
216
221
|
.action(async (id, options) => {
|
|
@@ -218,6 +223,21 @@ export function registerSessionCommand(program) {
|
|
|
218
223
|
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
219
224
|
let sess = null;
|
|
220
225
|
|
|
226
|
+
// No id → shared interactive picker (TTY + >1) or most-recent fallback
|
|
227
|
+
// (single / non-TTY / Ctrl-C). Same helper as `cc agent --resume`.
|
|
228
|
+
if (!id) {
|
|
229
|
+
const { pickRecentSession } =
|
|
230
|
+
await import("../lib/session-picker.js");
|
|
231
|
+
const picked = await pickRecentSession(ctx);
|
|
232
|
+
if (!picked.id) {
|
|
233
|
+
logger.error(
|
|
234
|
+
"No saved sessions to resume. Use 'chat' or 'agent' to create one.",
|
|
235
|
+
);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
id = picked.id;
|
|
239
|
+
}
|
|
240
|
+
|
|
221
241
|
// Try JSONL first
|
|
222
242
|
if (feature("JSONL_SESSION") && sessionExists(id)) {
|
|
223
243
|
const events = readEvents(id);
|
package/src/index.js
CHANGED
|
@@ -56,6 +56,8 @@ import { registerMemoryCommand } from "./commands/memory.js";
|
|
|
56
56
|
import { registerPermMemCommand } from "./commands/permmem.js";
|
|
57
57
|
import { registerRCacheCommand } from "./commands/rcache.js";
|
|
58
58
|
import { registerSessionCommand } from "./commands/session.js";
|
|
59
|
+
import { registerCostCommand } from "./commands/cost.js";
|
|
60
|
+
import { registerCheckpointCommand } from "./commands/checkpoint.js";
|
|
59
61
|
import { registerConsolCommand } from "./commands/consol.js";
|
|
60
62
|
import { registerImportCommand } from "./commands/import.js";
|
|
61
63
|
import { registerExportCommand } from "./commands/export.js";
|
|
@@ -448,6 +450,8 @@ export function createProgram(opts = {}) {
|
|
|
448
450
|
registerPermMemCommand(program);
|
|
449
451
|
registerRCacheCommand(program);
|
|
450
452
|
registerSessionCommand(program);
|
|
453
|
+
registerCostCommand(program);
|
|
454
|
+
registerCheckpointCommand(program);
|
|
451
455
|
registerConsolCommand(program);
|
|
452
456
|
|
|
453
457
|
// Phase 2: Knowledge & content management
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* file-checkpoint — manual file-state snapshot / rewind for `cc checkpoint`.
|
|
3
|
+
*
|
|
4
|
+
* Claude-Code "rewind" parity at the CLI level: snapshot a set of files (or
|
|
5
|
+
* directories) before a risky agentic run, then restore them if it goes wrong.
|
|
6
|
+
* This is the standalone store + ops; auto-snapshotting inside the agent
|
|
7
|
+
* tool-loop is a separate follow-up (it lives in the churn-prone agent-core).
|
|
8
|
+
*
|
|
9
|
+
* On-disk layout (under <home>/checkpoints, overridable via opts.root for tests):
|
|
10
|
+
* <root>/<id>.json manifest { id, label, createdAt, cwd, files:[...] }
|
|
11
|
+
* <root>/<id>/<sha256> raw bytes of each distinct file (content-addressed,
|
|
12
|
+
* so duplicate contents are stored once)
|
|
13
|
+
*
|
|
14
|
+
* Distinct from `cc workflow checkpoint` (which snapshots workflow EXECUTION
|
|
15
|
+
* state in the DB, not files).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { getHomeDir } from "./paths.js";
|
|
22
|
+
|
|
23
|
+
/** Directories never walked into when a checkpoint path is a directory. */
|
|
24
|
+
export const SKIP_DIRS = new Set([
|
|
25
|
+
"node_modules",
|
|
26
|
+
".git",
|
|
27
|
+
"dist",
|
|
28
|
+
"build",
|
|
29
|
+
".chainlesschain",
|
|
30
|
+
".next",
|
|
31
|
+
".cache",
|
|
32
|
+
"coverage",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** Safety cap so `checkpoint create <huge-dir>` can't snapshot the universe. */
|
|
36
|
+
export const DEFAULT_MAX_FILES = 2000;
|
|
37
|
+
|
|
38
|
+
function defaultRoot() {
|
|
39
|
+
return path.join(getHomeDir(), "checkpoints");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ensureDir(dir) {
|
|
43
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function sha256(buf) {
|
|
47
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function newId() {
|
|
51
|
+
// Date.now/random are fine here (plain CLI lib, not a resumable workflow).
|
|
52
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
53
|
+
return `cp-${Date.now()}-${rand}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Recursively collect regular files under an absolute path, honoring SKIP_DIRS
|
|
58
|
+
* and a running maxFiles budget. Symlinks are not followed.
|
|
59
|
+
*/
|
|
60
|
+
function collectFiles(abs, { maxFiles, acc }) {
|
|
61
|
+
let stat;
|
|
62
|
+
try {
|
|
63
|
+
stat = fs.lstatSync(abs);
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (stat.isSymbolicLink()) return;
|
|
68
|
+
if (stat.isFile()) {
|
|
69
|
+
if (acc.length >= maxFiles) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`checkpoint exceeds ${maxFiles} files — narrow the paths or raise maxFiles`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
acc.push(abs);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (stat.isDirectory()) {
|
|
78
|
+
if (SKIP_DIRS.has(path.basename(abs))) return;
|
|
79
|
+
let entries;
|
|
80
|
+
try {
|
|
81
|
+
entries = fs.readdirSync(abs);
|
|
82
|
+
} catch {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
for (const name of entries) {
|
|
86
|
+
collectFiles(path.join(abs, name), { maxFiles, acc });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create a checkpoint snapshotting the given paths (files and/or dirs).
|
|
93
|
+
*
|
|
94
|
+
* @param {string[]} paths
|
|
95
|
+
* @param {object} [opts] { cwd, label, root, maxFiles }
|
|
96
|
+
* @returns {{ id, label, createdAt, cwd, fileCount, files:Array }}
|
|
97
|
+
*/
|
|
98
|
+
export function createCheckpoint(paths, opts = {}) {
|
|
99
|
+
const cwd = opts.cwd || process.cwd();
|
|
100
|
+
const root = opts.root || defaultRoot();
|
|
101
|
+
const maxFiles = Number.isFinite(opts.maxFiles)
|
|
102
|
+
? opts.maxFiles
|
|
103
|
+
: DEFAULT_MAX_FILES;
|
|
104
|
+
const list = Array.isArray(paths) ? paths : [paths];
|
|
105
|
+
if (list.filter(Boolean).length === 0) {
|
|
106
|
+
throw new Error("createCheckpoint requires at least one path");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const absFiles = [];
|
|
110
|
+
for (const p of list) {
|
|
111
|
+
if (!p) continue;
|
|
112
|
+
const abs = path.resolve(cwd, p);
|
|
113
|
+
if (!fs.existsSync(abs)) {
|
|
114
|
+
throw new Error(`no such path: ${p}`);
|
|
115
|
+
}
|
|
116
|
+
collectFiles(abs, { maxFiles, acc: absFiles });
|
|
117
|
+
}
|
|
118
|
+
// De-dupe (overlapping paths) while preserving order.
|
|
119
|
+
const uniqueAbs = [...new Set(absFiles)];
|
|
120
|
+
|
|
121
|
+
const id = opts.id || newId();
|
|
122
|
+
const blobDir = path.join(root, id);
|
|
123
|
+
ensureDir(blobDir);
|
|
124
|
+
|
|
125
|
+
const files = [];
|
|
126
|
+
for (const abs of uniqueAbs) {
|
|
127
|
+
const buf = fs.readFileSync(abs);
|
|
128
|
+
const hash = sha256(buf);
|
|
129
|
+
const blobPath = path.join(blobDir, hash);
|
|
130
|
+
if (!fs.existsSync(blobPath)) fs.writeFileSync(blobPath, buf);
|
|
131
|
+
files.push({
|
|
132
|
+
rel: path.relative(cwd, abs) || path.basename(abs),
|
|
133
|
+
abs,
|
|
134
|
+
bytes: buf.length,
|
|
135
|
+
sha256: hash,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const manifest = {
|
|
140
|
+
id,
|
|
141
|
+
label: opts.label || "",
|
|
142
|
+
createdAt: new Date().toISOString(),
|
|
143
|
+
cwd,
|
|
144
|
+
fileCount: files.length,
|
|
145
|
+
files,
|
|
146
|
+
};
|
|
147
|
+
ensureDir(root);
|
|
148
|
+
fs.writeFileSync(
|
|
149
|
+
path.join(root, `${id}.json`),
|
|
150
|
+
JSON.stringify(manifest, null, 2),
|
|
151
|
+
"utf-8",
|
|
152
|
+
);
|
|
153
|
+
return manifest;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Load a checkpoint manifest by id, or null. */
|
|
157
|
+
export function getCheckpoint(id, opts = {}) {
|
|
158
|
+
const root = opts.root || defaultRoot();
|
|
159
|
+
const file = path.join(root, `${id}.json`);
|
|
160
|
+
if (!fs.existsSync(file)) return null;
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
163
|
+
} catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** List all checkpoint manifests, newest first. */
|
|
169
|
+
export function listCheckpoints(opts = {}) {
|
|
170
|
+
const root = opts.root || defaultRoot();
|
|
171
|
+
if (!fs.existsSync(root)) return [];
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const name of fs.readdirSync(root)) {
|
|
174
|
+
if (!name.endsWith(".json")) continue;
|
|
175
|
+
const m = getCheckpoint(name.slice(0, -5), { root });
|
|
176
|
+
if (m) {
|
|
177
|
+
out.push({
|
|
178
|
+
id: m.id,
|
|
179
|
+
label: m.label,
|
|
180
|
+
createdAt: m.createdAt,
|
|
181
|
+
cwd: m.cwd,
|
|
182
|
+
fileCount: m.fileCount,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out.sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Compare the current on-disk state against a checkpoint.
|
|
191
|
+
* @returns {{ id, modified:[], unchanged:[], deleted:[] }}
|
|
192
|
+
* modified = content differs; deleted = file is gone now.
|
|
193
|
+
*/
|
|
194
|
+
export function diffCheckpoint(id, opts = {}) {
|
|
195
|
+
const root = opts.root || defaultRoot();
|
|
196
|
+
const m = getCheckpoint(id, { root });
|
|
197
|
+
if (!m) throw new Error(`no such checkpoint: ${id}`);
|
|
198
|
+
const modified = [];
|
|
199
|
+
const unchanged = [];
|
|
200
|
+
const deleted = [];
|
|
201
|
+
for (const f of m.files) {
|
|
202
|
+
if (!fs.existsSync(f.abs)) {
|
|
203
|
+
deleted.push(f.rel);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const cur = sha256(fs.readFileSync(f.abs));
|
|
207
|
+
if (cur === f.sha256) unchanged.push(f.rel);
|
|
208
|
+
else modified.push(f.rel);
|
|
209
|
+
}
|
|
210
|
+
return { id, modified, unchanged, deleted };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Restore files from a checkpoint to their original paths. By default a safety
|
|
215
|
+
* checkpoint of the CURRENT contents is taken first, so a restore is itself
|
|
216
|
+
* reversible. `dryRun` reports what would change without writing.
|
|
217
|
+
*
|
|
218
|
+
* @param {string} id
|
|
219
|
+
* @param {object} [opts] { root, dryRun, skipSafety, cwd }
|
|
220
|
+
* @returns {{ id, restored:[], unchanged:[], missingBlob:[], safetyId:string|null }}
|
|
221
|
+
*/
|
|
222
|
+
export function restoreCheckpoint(id, opts = {}) {
|
|
223
|
+
const root = opts.root || defaultRoot();
|
|
224
|
+
const m = getCheckpoint(id, { root });
|
|
225
|
+
if (!m) throw new Error(`no such checkpoint: ${id}`);
|
|
226
|
+
|
|
227
|
+
const restored = [];
|
|
228
|
+
const unchanged = [];
|
|
229
|
+
const missingBlob = [];
|
|
230
|
+
const toWrite = [];
|
|
231
|
+
|
|
232
|
+
for (const f of m.files) {
|
|
233
|
+
const blobPath = path.join(root, id, f.sha256);
|
|
234
|
+
if (!fs.existsSync(blobPath)) {
|
|
235
|
+
missingBlob.push(f.rel);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const blob = fs.readFileSync(blobPath);
|
|
239
|
+
const cur = fs.existsSync(f.abs) ? fs.readFileSync(f.abs) : null;
|
|
240
|
+
if (cur && sha256(cur) === f.sha256) {
|
|
241
|
+
unchanged.push(f.rel);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
toWrite.push({ abs: f.abs, rel: f.rel, blob });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (opts.dryRun) {
|
|
248
|
+
return {
|
|
249
|
+
id,
|
|
250
|
+
restored: toWrite.map((w) => w.rel),
|
|
251
|
+
unchanged,
|
|
252
|
+
missingBlob,
|
|
253
|
+
safetyId: null,
|
|
254
|
+
dryRun: true,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Snapshot the current state of the files we're about to overwrite, so the
|
|
259
|
+
// restore can itself be rewound. Only the files that actually change and
|
|
260
|
+
// currently exist need protecting.
|
|
261
|
+
let safetyId = null;
|
|
262
|
+
if (!opts.skipSafety) {
|
|
263
|
+
const existing = toWrite
|
|
264
|
+
.filter((w) => fs.existsSync(w.abs))
|
|
265
|
+
.map((w) => w.abs);
|
|
266
|
+
if (existing.length > 0) {
|
|
267
|
+
const safety = createCheckpoint(existing, {
|
|
268
|
+
root,
|
|
269
|
+
cwd: m.cwd,
|
|
270
|
+
label: `auto-before-restore-${id}`,
|
|
271
|
+
});
|
|
272
|
+
safetyId = safety.id;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
for (const w of toWrite) {
|
|
277
|
+
ensureDir(path.dirname(w.abs));
|
|
278
|
+
fs.writeFileSync(w.abs, w.blob);
|
|
279
|
+
restored.push(w.rel);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { id, restored, unchanged, missingBlob, safetyId };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Delete a checkpoint (manifest + blobs). Returns true if it existed. */
|
|
286
|
+
export function deleteCheckpoint(id, opts = {}) {
|
|
287
|
+
const root = opts.root || defaultRoot();
|
|
288
|
+
const file = path.join(root, `${id}.json`);
|
|
289
|
+
const blobDir = path.join(root, id);
|
|
290
|
+
let existed = false;
|
|
291
|
+
if (fs.existsSync(file)) {
|
|
292
|
+
fs.rmSync(file);
|
|
293
|
+
existed = true;
|
|
294
|
+
}
|
|
295
|
+
if (fs.existsSync(blobDir)) {
|
|
296
|
+
fs.rmSync(blobDir, { recursive: true, force: true });
|
|
297
|
+
existed = true;
|
|
298
|
+
}
|
|
299
|
+
return existed;
|
|
300
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* llm-pricing — turn token counts into estimated $ cost.
|
|
3
|
+
*
|
|
4
|
+
* Layers a price table on top of the token roll-ups produced by
|
|
5
|
+
* `session-usage.js`, so `cc cost` can show spend per provider/model.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: these rates are ESTIMATES of public list prices (USD per 1M
|
|
8
|
+
* tokens) and drift over time. Local providers (ollama / local) are free.
|
|
9
|
+
* An unmatched model is reported as `matched:false` with zero cost rather
|
|
10
|
+
* than guessed — the caller surfaces it as "unpriced" so the number is never
|
|
11
|
+
* silently wrong. Override or extend PRICE_TABLE below as prices change.
|
|
12
|
+
*
|
|
13
|
+
* Rate shape: { in: <USD per 1M input tokens>, out: <USD per 1M output> }.
|
|
14
|
+
* Matching is provider-scoped, longest-pattern-first, case-insensitive
|
|
15
|
+
* substring against the model id.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Providers whose models run locally and cost nothing to call. */
|
|
19
|
+
export const FREE_PROVIDERS = Object.freeze([
|
|
20
|
+
"ollama",
|
|
21
|
+
"local",
|
|
22
|
+
"llamacpp",
|
|
23
|
+
"mediapipe",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Per-provider model price patterns. Each entry: { match, in, out }.
|
|
28
|
+
* Order within a provider matters — more specific patterns first so
|
|
29
|
+
* "gpt-4o-mini" is not shadowed by "gpt-4o".
|
|
30
|
+
*/
|
|
31
|
+
export const PRICE_TABLE = Object.freeze({
|
|
32
|
+
anthropic: [
|
|
33
|
+
{ match: "opus", in: 15, out: 75 },
|
|
34
|
+
{ match: "sonnet", in: 3, out: 15 },
|
|
35
|
+
{ match: "haiku", in: 1, out: 5 },
|
|
36
|
+
],
|
|
37
|
+
openai: [
|
|
38
|
+
{ match: "gpt-4o-mini", in: 0.15, out: 0.6 },
|
|
39
|
+
{ match: "gpt-4o", in: 2.5, out: 10 },
|
|
40
|
+
{ match: "gpt-4-turbo", in: 10, out: 30 },
|
|
41
|
+
{ match: "gpt-4", in: 30, out: 60 },
|
|
42
|
+
{ match: "gpt-3.5", in: 0.5, out: 1.5 },
|
|
43
|
+
{ match: "o1-mini", in: 1.1, out: 4.4 },
|
|
44
|
+
{ match: "o1", in: 15, out: 60 },
|
|
45
|
+
],
|
|
46
|
+
deepseek: [
|
|
47
|
+
{ match: "reasoner", in: 0.55, out: 2.19 },
|
|
48
|
+
{ match: "chat", in: 0.27, out: 1.1 },
|
|
49
|
+
],
|
|
50
|
+
// Volcengine Doubao — rough USD conversion of public RMB list pricing.
|
|
51
|
+
volcengine: [
|
|
52
|
+
{ match: "seed-1-6", in: 0.11, out: 0.28 },
|
|
53
|
+
{ match: "seed", in: 0.11, out: 0.28 },
|
|
54
|
+
{ match: "pro", in: 0.11, out: 0.28 },
|
|
55
|
+
{ match: "lite", in: 0.04, out: 0.08 },
|
|
56
|
+
{ match: "doubao", in: 0.11, out: 0.28 },
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const round = (n, dp = 6) => {
|
|
61
|
+
const f = Math.pow(10, dp);
|
|
62
|
+
return Math.round((Number(n) + Number.EPSILON) * f) / f;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Merge user-supplied price overrides (typically `config.llm.pricing`) onto the
|
|
67
|
+
* built-in table. Override shape mirrors PRICE_TABLE:
|
|
68
|
+
* { "<provider>": [ { match: "<substr>", in: <num>, out: <num> }, ... ] }
|
|
69
|
+
*
|
|
70
|
+
* Per provider, a user entry whose `match` equals a built-in pattern REPLACES
|
|
71
|
+
* it; brand-new patterns are prepended so they win ties; unknown providers are
|
|
72
|
+
* added. Malformed entries (missing match / non-numeric rate) are skipped so a
|
|
73
|
+
* bad config line can't crash cost reporting. Returns a new table; never
|
|
74
|
+
* mutates PRICE_TABLE or the input.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} [overrides]
|
|
77
|
+
* @param {object} [base=PRICE_TABLE]
|
|
78
|
+
* @returns {object} merged price table
|
|
79
|
+
*/
|
|
80
|
+
export function mergePricing(overrides, base = PRICE_TABLE) {
|
|
81
|
+
const merged = {};
|
|
82
|
+
for (const [p, entries] of Object.entries(base)) {
|
|
83
|
+
merged[p] = entries.map((e) => ({ ...e }));
|
|
84
|
+
}
|
|
85
|
+
if (!overrides || typeof overrides !== "object") return merged;
|
|
86
|
+
|
|
87
|
+
for (const [provider, rawEntries] of Object.entries(overrides)) {
|
|
88
|
+
if (!Array.isArray(rawEntries)) continue;
|
|
89
|
+
const valid = rawEntries
|
|
90
|
+
.filter(
|
|
91
|
+
(e) =>
|
|
92
|
+
e &&
|
|
93
|
+
typeof e.match === "string" &&
|
|
94
|
+
e.match.trim() &&
|
|
95
|
+
Number.isFinite(Number(e.in)) &&
|
|
96
|
+
Number.isFinite(Number(e.out)),
|
|
97
|
+
)
|
|
98
|
+
.map((e) => ({
|
|
99
|
+
match: e.match.toLowerCase(),
|
|
100
|
+
in: Number(e.in),
|
|
101
|
+
out: Number(e.out),
|
|
102
|
+
}));
|
|
103
|
+
if (valid.length === 0) continue;
|
|
104
|
+
const overridden = new Set(valid.map((v) => v.match));
|
|
105
|
+
const kept = (merged[provider] || []).filter(
|
|
106
|
+
(e) => !overridden.has(e.match.toLowerCase()),
|
|
107
|
+
);
|
|
108
|
+
merged[provider] = [...valid, ...kept];
|
|
109
|
+
}
|
|
110
|
+
return merged;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Look up the rate for a provider/model, or null if unpriced.
|
|
115
|
+
* @param {string} provider
|
|
116
|
+
* @param {string} model
|
|
117
|
+
* @param {object} [table=PRICE_TABLE] merged table from mergePricing()
|
|
118
|
+
* @returns {{ in:number, out:number, pattern:string }|null}
|
|
119
|
+
*/
|
|
120
|
+
export function lookupRate(provider, model, table = PRICE_TABLE) {
|
|
121
|
+
const p = String(provider || "").toLowerCase();
|
|
122
|
+
const m = String(model || "").toLowerCase();
|
|
123
|
+
if (FREE_PROVIDERS.includes(p)) {
|
|
124
|
+
return { in: 0, out: 0, pattern: "free" };
|
|
125
|
+
}
|
|
126
|
+
const entries = (table || PRICE_TABLE)[p];
|
|
127
|
+
if (!entries) return null;
|
|
128
|
+
// Longest pattern first so specific beats generic regardless of table order.
|
|
129
|
+
const sorted = [...entries].sort((a, b) => b.match.length - a.match.length);
|
|
130
|
+
for (const e of sorted) {
|
|
131
|
+
if (m.includes(e.match)) {
|
|
132
|
+
return { in: e.in, out: e.out, pattern: e.match };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Estimate cost for a single usage record.
|
|
140
|
+
* @returns {{ inputCost:number, outputCost:number, totalCost:number,
|
|
141
|
+
* currency:string, matched:boolean, free:boolean, rate:object|null }}
|
|
142
|
+
*/
|
|
143
|
+
export function estimateCost({
|
|
144
|
+
provider,
|
|
145
|
+
model,
|
|
146
|
+
inputTokens = 0,
|
|
147
|
+
outputTokens = 0,
|
|
148
|
+
table,
|
|
149
|
+
} = {}) {
|
|
150
|
+
const rate = lookupRate(provider, model, table);
|
|
151
|
+
if (!rate) {
|
|
152
|
+
return {
|
|
153
|
+
inputCost: 0,
|
|
154
|
+
outputCost: 0,
|
|
155
|
+
totalCost: 0,
|
|
156
|
+
currency: "USD",
|
|
157
|
+
matched: false,
|
|
158
|
+
free: false,
|
|
159
|
+
rate: null,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const inputCost = round((Number(inputTokens) / 1e6) * rate.in);
|
|
163
|
+
const outputCost = round((Number(outputTokens) / 1e6) * rate.out);
|
|
164
|
+
return {
|
|
165
|
+
inputCost,
|
|
166
|
+
outputCost,
|
|
167
|
+
totalCost: round(inputCost + outputCost),
|
|
168
|
+
currency: "USD",
|
|
169
|
+
matched: true,
|
|
170
|
+
free: rate.pattern === "free",
|
|
171
|
+
rate: { in: rate.in, out: rate.out, pattern: rate.pattern },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Augment a session-usage aggregate ({ total, byModel:[...] }) with cost.
|
|
177
|
+
* Returns a new object — does not mutate the input.
|
|
178
|
+
*
|
|
179
|
+
* Each byModel row gains: cost, currency, matched. The returned `cost`
|
|
180
|
+
* top-level sums priced rows; `unpriced` lists provider/model rows that had
|
|
181
|
+
* usage but no matching rate (their tokens are excluded from `cost.totalCost`).
|
|
182
|
+
*/
|
|
183
|
+
export function priceRollup(aggregate, { table } = {}) {
|
|
184
|
+
const byModel = (aggregate?.byModel || []).map((row) => {
|
|
185
|
+
const est = estimateCost({
|
|
186
|
+
provider: row.provider,
|
|
187
|
+
model: row.model,
|
|
188
|
+
inputTokens: row.inputTokens,
|
|
189
|
+
outputTokens: row.outputTokens,
|
|
190
|
+
table,
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
...row,
|
|
194
|
+
cost: est.totalCost,
|
|
195
|
+
inputCost: est.inputCost,
|
|
196
|
+
outputCost: est.outputCost,
|
|
197
|
+
currency: est.currency,
|
|
198
|
+
matched: est.matched,
|
|
199
|
+
free: est.free,
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
let totalCost = 0;
|
|
204
|
+
const unpriced = [];
|
|
205
|
+
for (const row of byModel) {
|
|
206
|
+
if (row.matched) {
|
|
207
|
+
totalCost = round(totalCost + row.cost);
|
|
208
|
+
} else if (row.totalTokens > 0) {
|
|
209
|
+
unpriced.push({
|
|
210
|
+
provider: row.provider,
|
|
211
|
+
model: row.model,
|
|
212
|
+
totalTokens: row.totalTokens,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
...aggregate,
|
|
219
|
+
byModel,
|
|
220
|
+
cost: {
|
|
221
|
+
totalCost,
|
|
222
|
+
currency: "USD",
|
|
223
|
+
unpricedCount: unpriced.length,
|
|
224
|
+
},
|
|
225
|
+
unpriced,
|
|
226
|
+
};
|
|
227
|
+
}
|