@stigmer/react 0.3.3 → 0.4.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/billing/AutoRechargeCard.d.ts +38 -0
- package/billing/AutoRechargeCard.d.ts.map +1 -0
- package/billing/AutoRechargeCard.js +90 -0
- package/billing/AutoRechargeCard.js.map +1 -0
- package/billing/BillingSection.d.ts +32 -0
- package/billing/BillingSection.d.ts.map +1 -0
- package/billing/BillingSection.js +81 -0
- package/billing/BillingSection.js.map +1 -0
- package/billing/CreditBalanceCard.d.ts +25 -0
- package/billing/CreditBalanceCard.d.ts.map +1 -0
- package/billing/CreditBalanceCard.js +28 -0
- package/billing/CreditBalanceCard.js.map +1 -0
- package/billing/CreditLedgerTable.d.ts +22 -0
- package/billing/CreditLedgerTable.d.ts.map +1 -0
- package/billing/CreditLedgerTable.js +75 -0
- package/billing/CreditLedgerTable.js.map +1 -0
- package/billing/CreditPackGrid.d.ts +31 -0
- package/billing/CreditPackGrid.d.ts.map +1 -0
- package/billing/CreditPackGrid.js +35 -0
- package/billing/CreditPackGrid.js.map +1 -0
- package/billing/LowBalanceBanner.d.ts +26 -0
- package/billing/LowBalanceBanner.d.ts.map +1 -0
- package/billing/LowBalanceBanner.js +33 -0
- package/billing/LowBalanceBanner.js.map +1 -0
- package/billing/PaymentMethodCard.d.ts +35 -0
- package/billing/PaymentMethodCard.d.ts.map +1 -0
- package/billing/PaymentMethodCard.js +48 -0
- package/billing/PaymentMethodCard.js.map +1 -0
- package/billing/credit-packs.d.ts +25 -0
- package/billing/credit-packs.d.ts.map +1 -0
- package/billing/credit-packs.js +39 -0
- package/billing/credit-packs.js.map +1 -0
- package/billing/format.d.ts +39 -0
- package/billing/format.d.ts.map +1 -0
- package/billing/format.js +90 -0
- package/billing/format.js.map +1 -0
- package/billing/index.d.ts +32 -0
- package/billing/index.d.ts.map +1 -0
- package/billing/index.js +21 -0
- package/billing/index.js.map +1 -0
- package/billing/useBillingAccount.d.ts +40 -0
- package/billing/useBillingAccount.d.ts.map +1 -0
- package/billing/useBillingAccount.js +35 -0
- package/billing/useBillingAccount.js.map +1 -0
- package/billing/useBillingUsageReport.d.ts +42 -0
- package/billing/useBillingUsageReport.d.ts.map +1 -0
- package/billing/useBillingUsageReport.js +43 -0
- package/billing/useBillingUsageReport.js.map +1 -0
- package/billing/useCreateBillingPortalSession.d.ts +35 -0
- package/billing/useCreateBillingPortalSession.d.ts.map +1 -0
- package/billing/useCreateBillingPortalSession.js +50 -0
- package/billing/useCreateBillingPortalSession.js.map +1 -0
- package/billing/useCreateCheckoutSession.d.ts +54 -0
- package/billing/useCreateCheckoutSession.d.ts.map +1 -0
- package/billing/useCreateCheckoutSession.js +58 -0
- package/billing/useCreateCheckoutSession.js.map +1 -0
- package/billing/useCreditLedger.d.ts +48 -0
- package/billing/useCreditLedger.d.ts.map +1 -0
- package/billing/useCreditLedger.js +39 -0
- package/billing/useCreditLedger.js.map +1 -0
- package/billing/useCustomerModelPricing.d.ts +41 -0
- package/billing/useCustomerModelPricing.d.ts.map +1 -0
- package/billing/useCustomerModelPricing.js +37 -0
- package/billing/useCustomerModelPricing.js.map +1 -0
- package/billing/useSetAutoRechargeConfig.d.ts +50 -0
- package/billing/useSetAutoRechargeConfig.d.ts.map +1 -0
- package/billing/useSetAutoRechargeConfig.js +53 -0
- package/billing/useSetAutoRechargeConfig.js.map +1 -0
- package/composer/ComposerToolbar.js +1 -1
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +1 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +19 -4
- package/composer/SessionComposer.js.map +1 -1
- package/composer/__tests__/SessionComposer-memo.test.d.ts +2 -0
- package/composer/__tests__/SessionComposer-memo.test.d.ts.map +1 -0
- package/composer/__tests__/SessionComposer-memo.test.js +23 -0
- package/composer/__tests__/SessionComposer-memo.test.js.map +1 -0
- package/execution/ApprovalCard.d.ts +5 -1
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +7 -3
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ExecutionPhaseBadge.d.ts +1 -1
- package/execution/ExecutionPhaseBadge.d.ts.map +1 -1
- package/execution/ExecutionPhaseBadge.js +3 -2
- package/execution/ExecutionPhaseBadge.js.map +1 -1
- package/execution/MessageEntry.d.ts +7 -3
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +19 -8
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +84 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +113 -65
- package/execution/MessageThread.js.map +1 -1
- package/execution/SetupProgress.d.ts +1 -1
- package/execution/SetupProgress.d.ts.map +1 -1
- package/execution/SetupProgress.js +3 -3
- package/execution/SetupProgress.js.map +1 -1
- package/execution/SubAgentSection.d.ts +5 -1
- package/execution/SubAgentSection.d.ts.map +1 -1
- package/execution/SubAgentSection.js +13 -7
- package/execution/SubAgentSection.js.map +1 -1
- package/execution/ThreadSkeleton.d.ts +22 -0
- package/execution/ThreadSkeleton.d.ts.map +1 -0
- package/execution/ThreadSkeleton.js +26 -0
- package/execution/ThreadSkeleton.js.map +1 -0
- package/execution/ToolCallGroup.d.ts +16 -1
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +31 -3
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/UsageWidget.d.ts +1 -1
- package/execution/__tests__/message-entry.test.d.ts +2 -0
- package/execution/__tests__/message-entry.test.d.ts.map +1 -0
- package/execution/__tests__/message-entry.test.js +178 -0
- package/execution/__tests__/message-entry.test.js.map +1 -0
- package/execution/__tests__/thread-keys.test.d.ts +2 -0
- package/execution/__tests__/thread-keys.test.d.ts.map +1 -0
- package/execution/__tests__/thread-keys.test.js +289 -0
- package/execution/__tests__/thread-keys.test.js.map +1 -0
- package/execution/__tests__/thread-memoization.test.d.ts +2 -0
- package/execution/__tests__/thread-memoization.test.d.ts.map +1 -0
- package/execution/__tests__/thread-memoization.test.js +262 -0
- package/execution/__tests__/thread-memoization.test.js.map +1 -0
- package/execution/__tests__/thread-skeleton.test.d.ts +2 -0
- package/execution/__tests__/thread-skeleton.test.d.ts.map +1 -0
- package/execution/__tests__/thread-skeleton.test.js +35 -0
- package/execution/__tests__/thread-skeleton.test.js.map +1 -0
- package/execution/__tests__/useExecutionStream.test.js +73 -10
- package/execution/__tests__/useExecutionStream.test.js.map +1 -1
- package/execution/__tests__/useSessionVariables-stability.test.d.ts +2 -0
- package/execution/__tests__/useSessionVariables-stability.test.d.ts.map +1 -0
- package/execution/__tests__/useSessionVariables-stability.test.js +69 -0
- package/execution/__tests__/useSessionVariables-stability.test.js.map +1 -0
- package/execution/__tests__/virtualized-thread.test.d.ts +2 -0
- package/execution/__tests__/virtualized-thread.test.d.ts.map +1 -0
- package/execution/__tests__/virtualized-thread.test.js +274 -0
- package/execution/__tests__/virtualized-thread.test.js.map +1 -0
- package/execution/index.d.ts +2 -0
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +1 -0
- package/execution/index.js.map +1 -1
- package/execution/useExecutionStream.d.ts +35 -10
- package/execution/useExecutionStream.d.ts.map +1 -1
- package/execution/useExecutionStream.js +79 -40
- package/execution/useExecutionStream.js.map +1 -1
- package/execution/useSessionVariables.d.ts.map +1 -1
- package/execution/useSessionVariables.js +4 -3
- package/execution/useSessionVariables.js.map +1 -1
- package/github/useGitHubConnection.d.ts.map +1 -1
- package/github/useGitHubConnection.js +5 -4
- package/github/useGitHubConnection.js.map +1 -1
- package/identity-account/index.d.ts +2 -0
- package/identity-account/index.d.ts.map +1 -0
- package/identity-account/index.js +2 -0
- package/identity-account/index.js.map +1 -0
- package/identity-account/useIdentityAccountGate.d.ts +81 -0
- package/identity-account/useIdentityAccountGate.d.ts.map +1 -0
- package/identity-account/useIdentityAccountGate.js +100 -0
- package/identity-account/useIdentityAccountGate.js.map +1 -0
- package/index.d.ts +10 -4
- package/index.d.ts.map +1 -1
- package/index.js +8 -2
- package/index.js.map +1 -1
- package/internal/FetchCacheProvider.d.ts +44 -0
- package/internal/FetchCacheProvider.d.ts.map +1 -0
- package/internal/FetchCacheProvider.js +61 -0
- package/internal/FetchCacheProvider.js.map +1 -0
- package/internal/JumpToLatestButton.d.ts +14 -0
- package/internal/JumpToLatestButton.d.ts.map +1 -0
- package/internal/JumpToLatestButton.js +19 -0
- package/internal/JumpToLatestButton.js.map +1 -0
- package/internal/ThreadItemWrapper.d.ts +20 -0
- package/internal/ThreadItemWrapper.d.ts.map +1 -0
- package/internal/ThreadItemWrapper.js +44 -0
- package/internal/ThreadItemWrapper.js.map +1 -0
- package/internal/VirtualizedThread.d.ts +25 -0
- package/internal/VirtualizedThread.d.ts.map +1 -0
- package/internal/VirtualizedThread.js +58 -0
- package/internal/VirtualizedThread.js.map +1 -0
- package/internal/__tests__/fetch-cache.test.d.ts +2 -0
- package/internal/__tests__/fetch-cache.test.d.ts.map +1 -0
- package/internal/__tests__/fetch-cache.test.js +182 -0
- package/internal/__tests__/fetch-cache.test.js.map +1 -0
- package/internal/__tests__/stream-controller.test.d.ts +2 -0
- package/internal/__tests__/stream-controller.test.d.ts.map +1 -0
- package/internal/__tests__/stream-controller.test.js +294 -0
- package/internal/__tests__/stream-controller.test.js.map +1 -0
- package/internal/__tests__/thread-animation.test.d.ts +2 -0
- package/internal/__tests__/thread-animation.test.d.ts.map +1 -0
- package/internal/__tests__/thread-animation.test.js +79 -0
- package/internal/__tests__/thread-animation.test.js.map +1 -0
- package/internal/__tests__/useAutoScroll.test.d.ts +2 -0
- package/internal/__tests__/useAutoScroll.test.d.ts.map +1 -0
- package/internal/__tests__/useAutoScroll.test.js +188 -0
- package/internal/__tests__/useAutoScroll.test.js.map +1 -0
- package/internal/__tests__/useFetch-cache.test.d.ts +2 -0
- package/internal/__tests__/useFetch-cache.test.d.ts.map +1 -0
- package/internal/__tests__/useFetch-cache.test.js +137 -0
- package/internal/__tests__/useFetch-cache.test.js.map +1 -0
- package/internal/dev/__tests__/use-key-stability.test.d.ts +2 -0
- package/internal/dev/__tests__/use-key-stability.test.d.ts.map +1 -0
- package/internal/dev/__tests__/use-key-stability.test.js +72 -0
- package/internal/dev/__tests__/use-key-stability.test.js.map +1 -0
- package/internal/dev/__tests__/use-render-tracer.test.d.ts +2 -0
- package/internal/dev/__tests__/use-render-tracer.test.d.ts.map +1 -0
- package/internal/dev/__tests__/use-render-tracer.test.js +55 -0
- package/internal/dev/__tests__/use-render-tracer.test.js.map +1 -0
- package/internal/dev/dom-counter.d.ts +14 -0
- package/internal/dev/dom-counter.d.ts.map +1 -0
- package/internal/dev/dom-counter.js +39 -0
- package/internal/dev/dom-counter.js.map +1 -0
- package/internal/dev/index.d.ts +6 -0
- package/internal/dev/index.d.ts.map +1 -0
- package/internal/dev/index.js +6 -0
- package/internal/dev/index.js.map +1 -0
- package/internal/dev/profiler-wrapper.d.ts +16 -0
- package/internal/dev/profiler-wrapper.d.ts.map +1 -0
- package/internal/dev/profiler-wrapper.js +31 -0
- package/internal/dev/profiler-wrapper.js.map +1 -0
- package/internal/dev/use-key-stability.d.ts +22 -0
- package/internal/dev/use-key-stability.d.ts.map +1 -0
- package/internal/dev/use-key-stability.js +67 -0
- package/internal/dev/use-key-stability.js.map +1 -0
- package/internal/dev/use-render-tracer.d.ts +13 -0
- package/internal/dev/use-render-tracer.d.ts.map +1 -0
- package/internal/dev/use-render-tracer.js +57 -0
- package/internal/dev/use-render-tracer.js.map +1 -0
- package/internal/dev/use-stream-rate.d.ts +23 -0
- package/internal/dev/use-stream-rate.d.ts.map +1 -0
- package/internal/dev/use-stream-rate.js +94 -0
- package/internal/dev/use-stream-rate.js.map +1 -0
- package/internal/fetch-cache.d.ts +72 -0
- package/internal/fetch-cache.d.ts.map +1 -0
- package/internal/fetch-cache.js +118 -0
- package/internal/fetch-cache.js.map +1 -0
- package/internal/store/__tests__/conversation-store.test.d.ts +2 -0
- package/internal/store/__tests__/conversation-store.test.d.ts.map +1 -0
- package/internal/store/__tests__/conversation-store.test.js +200 -0
- package/internal/store/__tests__/conversation-store.test.js.map +1 -0
- package/internal/store/__tests__/structural-share.test.d.ts +2 -0
- package/internal/store/__tests__/structural-share.test.d.ts.map +1 -0
- package/internal/store/__tests__/structural-share.test.js +368 -0
- package/internal/store/__tests__/structural-share.test.js.map +1 -0
- package/internal/store/conversation-store.d.ts +62 -0
- package/internal/store/conversation-store.d.ts.map +1 -0
- package/internal/store/conversation-store.js +95 -0
- package/internal/store/conversation-store.js.map +1 -0
- package/internal/store/index.d.ts +31 -0
- package/internal/store/index.d.ts.map +1 -0
- package/internal/store/index.js +54 -0
- package/internal/store/index.js.map +1 -0
- package/internal/store/structural-share.d.ts +13 -0
- package/internal/store/structural-share.d.ts.map +1 -0
- package/internal/store/structural-share.js +240 -0
- package/internal/store/structural-share.js.map +1 -0
- package/internal/stream-controller.d.ts +85 -0
- package/internal/stream-controller.d.ts.map +1 -0
- package/internal/stream-controller.js +146 -0
- package/internal/stream-controller.js.map +1 -0
- package/internal/useAutoScroll.d.ts +32 -0
- package/internal/useAutoScroll.d.ts.map +1 -0
- package/internal/useAutoScroll.js +97 -0
- package/internal/useAutoScroll.js.map +1 -0
- package/internal/useFetch.d.ts +14 -0
- package/internal/useFetch.d.ts.map +1 -1
- package/internal/useFetch.js +32 -2
- package/internal/useFetch.js.map +1 -1
- package/package.json +7 -5
- package/session/__tests__/useNewSessionFlow.test.js +16 -0
- package/session/__tests__/useNewSessionFlow.test.js.map +1 -1
- package/session/__tests__/usePersistedModel.test.d.ts +2 -0
- package/session/__tests__/usePersistedModel.test.d.ts.map +1 -0
- package/session/__tests__/usePersistedModel.test.js +82 -0
- package/session/__tests__/usePersistedModel.test.js.map +1 -0
- package/session/__tests__/useSession.test.d.ts +2 -0
- package/session/__tests__/useSession.test.d.ts.map +1 -0
- package/session/__tests__/useSession.test.js +130 -0
- package/session/__tests__/useSession.test.js.map +1 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +12 -6
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/usePersistedModel.d.ts +3 -0
- package/session/usePersistedModel.d.ts.map +1 -1
- package/session/usePersistedModel.js +27 -2
- package/session/usePersistedModel.js.map +1 -1
- package/session/useSession.d.ts.map +1 -1
- package/session/useSession.js +1 -1
- package/session/useSession.js.map +1 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +9 -1
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionExecutions.d.ts.map +1 -1
- package/session/useSessionExecutions.js +1 -1
- package/session/useSessionExecutions.js.map +1 -1
- package/session/useSessionPageFlow.js +1 -1
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionUsage.d.ts +24 -40
- package/session/useSessionUsage.d.ts.map +1 -1
- package/session/useSessionUsage.js +64 -97
- package/session/useSessionUsage.js.map +1 -1
- package/settings/BillingSection.d.ts +3 -0
- package/settings/BillingSection.d.ts.map +1 -0
- package/settings/BillingSection.js +3 -0
- package/settings/BillingSection.js.map +1 -0
- package/settings/index.d.ts +2 -0
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +1 -0
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.js +1 -1
- package/settings/settings-nav.js.map +1 -1
- package/src/billing/AutoRechargeCard.tsx +274 -0
- package/src/billing/BillingSection.tsx +255 -0
- package/src/billing/CreditBalanceCard.tsx +81 -0
- package/src/billing/CreditLedgerTable.tsx +281 -0
- package/src/billing/CreditPackGrid.tsx +132 -0
- package/src/billing/LowBalanceBanner.tsx +67 -0
- package/src/billing/PaymentMethodCard.tsx +133 -0
- package/src/billing/credit-packs.ts +54 -0
- package/src/billing/format.ts +97 -0
- package/src/billing/index.ts +51 -0
- package/src/billing/useBillingAccount.ts +64 -0
- package/src/billing/useBillingUsageReport.ts +73 -0
- package/src/billing/useCreateBillingPortalSession.ts +76 -0
- package/src/billing/useCreateCheckoutSession.ts +101 -0
- package/src/billing/useCreditLedger.ts +79 -0
- package/src/billing/useCustomerModelPricing.ts +67 -0
- package/src/billing/useSetAutoRechargeConfig.ts +90 -0
- package/src/composer/ComposerToolbar.tsx +1 -1
- package/src/composer/SessionComposer.tsx +22 -4
- package/src/composer/__tests__/SessionComposer-memo.test.ts +26 -0
- package/src/execution/ApprovalCard.tsx +7 -3
- package/src/execution/ExecutionPhaseBadge.tsx +3 -2
- package/src/execution/MessageEntry.tsx +27 -16
- package/src/execution/MessageThread.tsx +308 -131
- package/src/execution/SetupProgress.tsx +3 -3
- package/src/execution/SubAgentSection.tsx +14 -6
- package/src/execution/ThreadSkeleton.tsx +73 -0
- package/src/execution/ToolCallGroup.tsx +36 -3
- package/src/execution/UsageWidget.tsx +1 -1
- package/src/execution/__tests__/message-entry.test.tsx +236 -0
- package/src/execution/__tests__/thread-keys.test.ts +409 -0
- package/src/execution/__tests__/thread-memoization.test.ts +320 -0
- package/src/execution/__tests__/thread-skeleton.test.tsx +44 -0
- package/src/execution/__tests__/useExecutionStream.test.tsx +109 -12
- package/src/execution/__tests__/useSessionVariables-stability.test.ts +95 -0
- package/src/execution/__tests__/virtualized-thread.test.tsx +401 -0
- package/src/execution/index.ts +3 -0
- package/src/execution/useExecutionStream.ts +123 -48
- package/src/execution/useSessionVariables.ts +17 -12
- package/src/github/useGitHubConnection.ts +18 -13
- package/src/identity-account/index.ts +5 -0
- package/src/identity-account/useIdentityAccountGate.ts +163 -0
- package/src/index.ts +73 -0
- package/src/internal/FetchCacheProvider.tsx +74 -0
- package/src/internal/JumpToLatestButton.tsx +61 -0
- package/src/internal/ThreadItemWrapper.tsx +65 -0
- package/src/internal/VirtualizedThread.tsx +162 -0
- package/src/internal/__tests__/fetch-cache.test.ts +230 -0
- package/src/internal/__tests__/stream-controller.test.ts +395 -0
- package/src/internal/__tests__/thread-animation.test.tsx +121 -0
- package/src/internal/__tests__/useAutoScroll.test.tsx +261 -0
- package/src/internal/__tests__/useFetch-cache.test.ts +214 -0
- package/src/internal/dev/__tests__/use-key-stability.test.ts +124 -0
- package/src/internal/dev/__tests__/use-render-tracer.test.ts +78 -0
- package/src/internal/dev/dom-counter.ts +47 -0
- package/src/internal/dev/index.ts +5 -0
- package/src/internal/dev/profiler-wrapper.tsx +52 -0
- package/src/internal/dev/use-key-stability.ts +86 -0
- package/src/internal/dev/use-render-tracer.ts +70 -0
- package/src/internal/dev/use-stream-rate.ts +138 -0
- package/src/internal/fetch-cache.ts +155 -0
- package/src/internal/store/__tests__/conversation-store.test.ts +257 -0
- package/src/internal/store/__tests__/structural-share.test.ts +454 -0
- package/src/internal/store/conversation-store.ts +128 -0
- package/src/internal/store/index.ts +68 -0
- package/src/internal/store/structural-share.ts +318 -0
- package/src/internal/stream-controller.ts +201 -0
- package/src/internal/useAutoScroll.ts +121 -0
- package/src/internal/useFetch.ts +51 -2
- package/src/session/__tests__/useNewSessionFlow.test.tsx +22 -0
- package/src/session/__tests__/usePersistedModel.test.tsx +117 -0
- package/src/session/__tests__/useSession.test.tsx +187 -0
- package/src/session/useNewSessionFlow.ts +12 -6
- package/src/session/usePersistedModel.ts +28 -2
- package/src/session/useSession.ts +1 -0
- package/src/session/useSessionConversation.ts +11 -2
- package/src/session/useSessionExecutions.ts +1 -0
- package/src/session/useSessionPageFlow.ts +1 -1
- package/src/session/useSessionUsage.ts +102 -123
- package/src/settings/BillingSection.tsx +4 -0
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-nav.ts +1 -1
- package/src/styles.css +31 -0
- package/src/usage/AgentBreakdownList.tsx +147 -0
- package/src/usage/CreditRunwayIndicator.tsx +71 -0
- package/src/usage/ExportButton.tsx +115 -0
- package/src/usage/HarnessSplitCard.tsx +103 -0
- package/src/usage/OrgUsagePanel.tsx +109 -45
- package/src/usage/index.ts +15 -0
- package/src/usage/useExportCSV.ts +115 -0
- package/src/usage/useOrgUsageReport.ts +2 -1
- package/src/workspace/__tests__/useWorkspaceEntries-stability.test.ts +76 -0
- package/src/workspace/useWorkspaceEntries.ts +16 -11
- package/styles.css +1 -1
- package/usage/AgentBreakdownList.d.ts +21 -0
- package/usage/AgentBreakdownList.d.ts.map +1 -0
- package/usage/AgentBreakdownList.js +44 -0
- package/usage/AgentBreakdownList.js.map +1 -0
- package/usage/CreditRunwayIndicator.d.ts +21 -0
- package/usage/CreditRunwayIndicator.d.ts.map +1 -0
- package/usage/CreditRunwayIndicator.js +38 -0
- package/usage/CreditRunwayIndicator.js.map +1 -0
- package/usage/ExportButton.d.ts +20 -0
- package/usage/ExportButton.d.ts.map +1 -0
- package/usage/ExportButton.js +36 -0
- package/usage/ExportButton.js.map +1 -0
- package/usage/HarnessSplitCard.d.ts +17 -0
- package/usage/HarnessSplitCard.d.ts.map +1 -0
- package/usage/HarnessSplitCard.js +38 -0
- package/usage/HarnessSplitCard.js.map +1 -0
- package/usage/OrgUsagePanel.d.ts.map +1 -1
- package/usage/OrgUsagePanel.js +30 -22
- package/usage/OrgUsagePanel.js.map +1 -1
- package/usage/index.d.ts +10 -0
- package/usage/index.d.ts.map +1 -1
- package/usage/index.js +5 -0
- package/usage/index.js.map +1 -1
- package/usage/useExportCSV.d.ts +23 -0
- package/usage/useExportCSV.d.ts.map +1 -0
- package/usage/useExportCSV.js +81 -0
- package/usage/useExportCSV.js.map +1 -0
- package/usage/useOrgUsageReport.d.ts +2 -1
- package/usage/useOrgUsageReport.d.ts.map +1 -1
- package/usage/useOrgUsageReport.js +2 -1
- package/usage/useOrgUsageReport.js.map +1 -1
- package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts +2 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts.map +1 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.js +57 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.js.map +1 -0
- package/workspace/useWorkspaceEntries.d.ts.map +1 -1
- package/workspace/useWorkspaceEntries.js +5 -4
- package/workspace/useWorkspaceEntries.js.map +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
import type { HarnessCostSummary } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
5
|
+
import { formatCost } from "../execution/UsageWidget";
|
|
6
|
+
|
|
7
|
+
/** Props for {@link HarnessSplitCard}. */
|
|
8
|
+
export interface HarnessSplitCardProps {
|
|
9
|
+
/** Harness breakdown entries from the org usage report. */
|
|
10
|
+
readonly breakdown: readonly HarnessCostSummary[];
|
|
11
|
+
/** Additional CSS class names. */
|
|
12
|
+
readonly className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const HARNESS_LABELS: Record<string, string> = {
|
|
16
|
+
native: "Native",
|
|
17
|
+
cursor: "Cursor",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const HARNESS_COLORS: Record<string, string> = {
|
|
21
|
+
native: "bg-chart-1",
|
|
22
|
+
cursor: "bg-chart-3",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Displays the cost split between execution harnesses as a segmented bar.
|
|
27
|
+
*
|
|
28
|
+
* Renders a two-tone horizontal bar with labels showing cost and percentage
|
|
29
|
+
* per harness. Designed for at-a-glance understanding of native vs cursor
|
|
30
|
+
* cost distribution.
|
|
31
|
+
*/
|
|
32
|
+
export function HarnessSplitCard({
|
|
33
|
+
breakdown,
|
|
34
|
+
className,
|
|
35
|
+
}: HarnessSplitCardProps) {
|
|
36
|
+
if (breakdown.length === 0) return null;
|
|
37
|
+
|
|
38
|
+
const totalCost = breakdown.reduce(
|
|
39
|
+
(sum, h) => sum + Number(h.billableCostMicros),
|
|
40
|
+
0,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (totalCost <= 0) return null;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={className}>
|
|
47
|
+
<h3 className="mb-2 text-xs font-semibold text-foreground">
|
|
48
|
+
Harness Split
|
|
49
|
+
</h3>
|
|
50
|
+
<div className="rounded-lg border border-border bg-card px-3.5 py-3">
|
|
51
|
+
{/* Segmented bar */}
|
|
52
|
+
<div
|
|
53
|
+
className="flex h-3 w-full overflow-hidden rounded-full"
|
|
54
|
+
role="img"
|
|
55
|
+
aria-label="Cost distribution by harness"
|
|
56
|
+
>
|
|
57
|
+
{breakdown.map((entry) => {
|
|
58
|
+
const cost = Number(entry.billableCostMicros);
|
|
59
|
+
const pct = (cost / totalCost) * 100;
|
|
60
|
+
if (pct <= 0) return null;
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
key={entry.harness}
|
|
64
|
+
className={cn(
|
|
65
|
+
"transition-all",
|
|
66
|
+
HARNESS_COLORS[entry.harness] ?? "bg-chart-4",
|
|
67
|
+
)}
|
|
68
|
+
style={{ width: `${pct}%` }}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
})}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Labels */}
|
|
75
|
+
<div className="mt-2.5 flex flex-wrap gap-x-5 gap-y-1">
|
|
76
|
+
{breakdown.map((entry) => {
|
|
77
|
+
const cost = Number(entry.billableCostMicros);
|
|
78
|
+
const pct = totalCost > 0 ? (cost / totalCost) * 100 : 0;
|
|
79
|
+
return (
|
|
80
|
+
<div key={entry.harness} className="flex items-center gap-1.5">
|
|
81
|
+
<div
|
|
82
|
+
className={cn(
|
|
83
|
+
"size-2 rounded-full",
|
|
84
|
+
HARNESS_COLORS[entry.harness] ?? "bg-chart-4",
|
|
85
|
+
)}
|
|
86
|
+
/>
|
|
87
|
+
<span className="text-xs text-muted-foreground">
|
|
88
|
+
{HARNESS_LABELS[entry.harness] ?? entry.harness}
|
|
89
|
+
</span>
|
|
90
|
+
<span className="text-xs tabular-nums font-medium text-foreground">
|
|
91
|
+
{formatCost(cost / 1_000_000)}
|
|
92
|
+
</span>
|
|
93
|
+
<span className="text-[0.6rem] tabular-nums text-muted-foreground">
|
|
94
|
+
({pct.toFixed(0)}%)
|
|
95
|
+
</span>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
})}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -17,6 +17,11 @@ import {
|
|
|
17
17
|
presetLabel,
|
|
18
18
|
type DateRangePreset,
|
|
19
19
|
} from "./date-range";
|
|
20
|
+
import { CreditRunwayIndicator } from "./CreditRunwayIndicator";
|
|
21
|
+
import { AgentBreakdownList } from "./AgentBreakdownList";
|
|
22
|
+
import { HarnessSplitCard } from "./HarnessSplitCard";
|
|
23
|
+
import { ExportButton } from "./ExportButton";
|
|
24
|
+
import { useExportCSV } from "./useExportCSV";
|
|
20
25
|
|
|
21
26
|
// ---------------------------------------------------------------------------
|
|
22
27
|
// Public API
|
|
@@ -49,6 +54,8 @@ export function OrgUsagePanel({ orgId, className }: OrgUsagePanelProps) {
|
|
|
49
54
|
const [preset, setPreset] = useState<DateRangePreset>("30d");
|
|
50
55
|
const dateRange = dateRangeFromPreset(preset);
|
|
51
56
|
const { report, isLoading, error } = useOrgUsageReport(orgId, dateRange);
|
|
57
|
+
const { exportCSV, isExporting } = useExportCSV(report, orgId);
|
|
58
|
+
const daysInRange = Number.parseInt(preset, 10);
|
|
52
59
|
|
|
53
60
|
if (isLoading) {
|
|
54
61
|
return (
|
|
@@ -90,18 +97,36 @@ export function OrgUsagePanel({ orgId, className }: OrgUsagePanelProps) {
|
|
|
90
97
|
|
|
91
98
|
return (
|
|
92
99
|
<div className={cn("space-y-6", className)}>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
<div className="flex items-center justify-between gap-3">
|
|
101
|
+
<DateRangeSelector
|
|
102
|
+
activePreset={preset}
|
|
103
|
+
onPresetChange={setPreset}
|
|
104
|
+
dateRange={dateRange}
|
|
105
|
+
/>
|
|
106
|
+
<ExportButton
|
|
107
|
+
onExport={exportCSV}
|
|
108
|
+
isExporting={isExporting}
|
|
109
|
+
disabled={!report || report.modelBreakdown.length === 0}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
98
112
|
|
|
99
|
-
<SummaryCards report={report} />
|
|
113
|
+
<SummaryCards report={report} daysInRange={daysInRange} />
|
|
100
114
|
|
|
101
115
|
{report.dailyCosts.length > 0 && (
|
|
102
116
|
<DailyCostChart entries={report.dailyCosts} />
|
|
103
117
|
)}
|
|
104
118
|
|
|
119
|
+
{report.harnessBreakdown.length > 0 && (
|
|
120
|
+
<HarnessSplitCard breakdown={report.harnessBreakdown} />
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{report.topAgentsByCost.length > 0 && (
|
|
124
|
+
<AgentBreakdownList
|
|
125
|
+
agents={report.topAgentsByCost}
|
|
126
|
+
totalBillableCostMicros={report.totalBillableCostMicros}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
|
|
105
130
|
{report.modelBreakdown.length > 0 && (
|
|
106
131
|
<ModelBreakdownList models={report.modelBreakdown} />
|
|
107
132
|
)}
|
|
@@ -126,7 +151,7 @@ function DateRangeSelector({
|
|
|
126
151
|
}) {
|
|
127
152
|
return (
|
|
128
153
|
<div
|
|
129
|
-
className="flex items-center
|
|
154
|
+
className="flex items-center gap-3"
|
|
130
155
|
role="group"
|
|
131
156
|
aria-label="Date range"
|
|
132
157
|
>
|
|
@@ -159,43 +184,80 @@ function DateRangeSelector({
|
|
|
159
184
|
// SummaryCards (internal)
|
|
160
185
|
// ---------------------------------------------------------------------------
|
|
161
186
|
|
|
162
|
-
function
|
|
187
|
+
function microsToUsd(micros: bigint): number {
|
|
188
|
+
return Number(micros) / 1_000_000;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function SummaryCards({
|
|
192
|
+
report,
|
|
193
|
+
daysInRange,
|
|
194
|
+
}: {
|
|
195
|
+
report: GetOrgUsageReportOutput;
|
|
196
|
+
daysInRange: number;
|
|
197
|
+
}) {
|
|
163
198
|
const { totalLlmCalls, totalTokens } = report.modelBreakdown.reduce(
|
|
164
199
|
(acc, m) => ({
|
|
165
200
|
totalLlmCalls: acc.totalLlmCalls + m.callCount,
|
|
166
201
|
totalTokens:
|
|
167
202
|
acc.totalTokens +
|
|
168
|
-
m.inputTokens +
|
|
169
|
-
m.outputTokens +
|
|
170
|
-
m.
|
|
171
|
-
m.
|
|
203
|
+
Number(m.inputTokens) +
|
|
204
|
+
Number(m.outputTokens) +
|
|
205
|
+
Number(m.cacheCreationInputTokens) +
|
|
206
|
+
Number(m.cacheReadInputTokens),
|
|
172
207
|
}),
|
|
173
208
|
{ totalLlmCalls: 0, totalTokens: 0 },
|
|
174
209
|
);
|
|
175
210
|
|
|
176
|
-
const cards: { label: string; value: string }[] = [
|
|
177
|
-
{ label: "Total Cost", value: formatCost(report.totalCostUsd) },
|
|
178
|
-
{ label: "LLM Calls", value: formatCompactNumber(totalLlmCalls) },
|
|
179
|
-
{ label: "Tokens", value: formatCompactNumber(totalTokens) },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
211
|
return (
|
|
183
|
-
<div
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
>
|
|
188
|
-
{cards.map((card) => (
|
|
189
|
-
<div
|
|
190
|
-
key={card.label}
|
|
191
|
-
className="rounded-lg border border-border bg-card px-3.5 py-3"
|
|
192
|
-
>
|
|
212
|
+
<div role="group" aria-label="Usage summary" className="space-y-2">
|
|
213
|
+
{/* Primary row */}
|
|
214
|
+
<div className="grid grid-cols-3 gap-3">
|
|
215
|
+
<div className="rounded-lg border border-border bg-card px-3.5 py-3">
|
|
193
216
|
<div className="text-lg font-semibold tabular-nums text-foreground">
|
|
194
|
-
{
|
|
217
|
+
{formatCost(microsToUsd(report.totalBillableCostMicros))}
|
|
195
218
|
</div>
|
|
196
|
-
<div className="text-xs text-muted-foreground">
|
|
219
|
+
<div className="text-xs text-muted-foreground">Total Cost</div>
|
|
220
|
+
<CreditRunwayIndicator
|
|
221
|
+
totalBillableCostMicros={report.totalBillableCostMicros}
|
|
222
|
+
daysInRange={daysInRange}
|
|
223
|
+
className="mt-0.5 block"
|
|
224
|
+
/>
|
|
197
225
|
</div>
|
|
198
|
-
|
|
226
|
+
<div className="rounded-lg border border-border bg-card px-3.5 py-3">
|
|
227
|
+
<div className="text-lg font-semibold tabular-nums text-foreground">
|
|
228
|
+
{formatCompactNumber(totalLlmCalls)}
|
|
229
|
+
</div>
|
|
230
|
+
<div className="text-xs text-muted-foreground">LLM Calls</div>
|
|
231
|
+
</div>
|
|
232
|
+
<div className="rounded-lg border border-border bg-card px-3.5 py-3">
|
|
233
|
+
<div className="text-lg font-semibold tabular-nums text-foreground">
|
|
234
|
+
{formatCompactNumber(totalTokens)}
|
|
235
|
+
</div>
|
|
236
|
+
<div className="text-xs text-muted-foreground">Tokens</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Secondary row */}
|
|
241
|
+
<div className="grid grid-cols-3 gap-3">
|
|
242
|
+
<div className="rounded-lg border border-border-muted bg-card/50 px-3.5 py-2.5">
|
|
243
|
+
<div className="text-sm font-semibold tabular-nums text-foreground">
|
|
244
|
+
{formatCompactNumber(report.totalExecutions)}
|
|
245
|
+
</div>
|
|
246
|
+
<div className="text-[0.65rem] text-muted-foreground">Executions</div>
|
|
247
|
+
</div>
|
|
248
|
+
<div className="rounded-lg border border-border-muted bg-card/50 px-3.5 py-2.5">
|
|
249
|
+
<div className="text-sm font-semibold tabular-nums text-foreground">
|
|
250
|
+
{formatCompactNumber(report.totalAgents)}
|
|
251
|
+
</div>
|
|
252
|
+
<div className="text-[0.65rem] text-muted-foreground">Agents</div>
|
|
253
|
+
</div>
|
|
254
|
+
<div className="rounded-lg border border-border-muted bg-card/50 px-3.5 py-2.5">
|
|
255
|
+
<div className="text-sm font-semibold tabular-nums text-foreground">
|
|
256
|
+
{formatCompactNumber(report.totalSessions)}
|
|
257
|
+
</div>
|
|
258
|
+
<div className="text-[0.65rem] text-muted-foreground">Sessions</div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
199
261
|
</div>
|
|
200
262
|
);
|
|
201
263
|
}
|
|
@@ -207,7 +269,7 @@ function SummaryCards({ report }: { report: GetOrgUsageReportOutput }) {
|
|
|
207
269
|
const CHART_HEIGHT_PX = 128;
|
|
208
270
|
|
|
209
271
|
function DailyCostChart({ entries }: { entries: readonly DailyCostEntry[] }) {
|
|
210
|
-
const maxCost = Math.max(...entries.map((e) => e.
|
|
272
|
+
const maxCost = Math.max(...entries.map((e) => microsToUsd(e.billableCostMicros)), 0);
|
|
211
273
|
const [hoveredIdx, setHoveredIdx] = useState<number | null>(null);
|
|
212
274
|
|
|
213
275
|
return (
|
|
@@ -222,9 +284,9 @@ function DailyCostChart({ entries }: { entries: readonly DailyCostEntry[] }) {
|
|
|
222
284
|
<span className="text-xs tabular-nums text-muted-foreground">
|
|
223
285
|
{formatChartDate(entries[hoveredIdx].date)}
|
|
224
286
|
{" \u00B7 "}
|
|
225
|
-
{formatCost(entries[hoveredIdx].
|
|
287
|
+
{formatCost(microsToUsd(entries[hoveredIdx].billableCostMicros))}
|
|
226
288
|
{" \u00B7 "}
|
|
227
|
-
{formatTokenCount(entries[hoveredIdx].totalTokens)} tokens
|
|
289
|
+
{formatTokenCount(Number(entries[hoveredIdx].totalTokens))} tokens
|
|
228
290
|
</span>
|
|
229
291
|
)}
|
|
230
292
|
</div>
|
|
@@ -238,7 +300,7 @@ function DailyCostChart({ entries }: { entries: readonly DailyCostEntry[] }) {
|
|
|
238
300
|
>
|
|
239
301
|
{entries.map((entry, i) => {
|
|
240
302
|
const ratio =
|
|
241
|
-
maxCost > 0 ? entry.
|
|
303
|
+
maxCost > 0 ? microsToUsd(entry.billableCostMicros) / maxCost : 0;
|
|
242
304
|
const heightPx = Math.max(ratio * CHART_HEIGHT_PX, 2);
|
|
243
305
|
|
|
244
306
|
return (
|
|
@@ -326,9 +388,11 @@ function ModelBreakdownList({
|
|
|
326
388
|
</span>
|
|
327
389
|
</div>
|
|
328
390
|
{models.map((m) => {
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
const
|
|
391
|
+
const inputTok = Number(m.inputTokens);
|
|
392
|
+
const cacheCreation = Number(m.cacheCreationInputTokens);
|
|
393
|
+
const cacheRead = Number(m.cacheReadInputTokens);
|
|
394
|
+
const totalInput = inputTok + cacheCreation + cacheRead;
|
|
395
|
+
const hasCache = cacheRead > 0 || cacheCreation > 0;
|
|
332
396
|
|
|
333
397
|
return (
|
|
334
398
|
<div
|
|
@@ -363,24 +427,24 @@ function ModelBreakdownList({
|
|
|
363
427
|
role="cell"
|
|
364
428
|
className="self-center text-right text-xs tabular-nums text-muted-foreground"
|
|
365
429
|
>
|
|
366
|
-
{formatCompactNumber(m.outputTokens)}
|
|
430
|
+
{formatCompactNumber(Number(m.outputTokens))}
|
|
367
431
|
</span>
|
|
368
432
|
<span
|
|
369
433
|
role="cell"
|
|
370
434
|
className="self-center text-right text-xs tabular-nums text-foreground"
|
|
371
435
|
>
|
|
372
|
-
{formatCost(m.
|
|
436
|
+
{formatCost(microsToUsd(m.billableCostMicros))}
|
|
373
437
|
</span>
|
|
374
438
|
</div>
|
|
375
439
|
{hasCache && (
|
|
376
440
|
<div className="mt-0.5 text-[0.6rem] tabular-nums text-muted-foreground">
|
|
377
441
|
cache{" "}
|
|
378
|
-
{
|
|
379
|
-
`${formatCompactNumber(
|
|
380
|
-
{
|
|
442
|
+
{cacheRead > 0 &&
|
|
443
|
+
`${formatCompactNumber(cacheRead)} read`}
|
|
444
|
+
{cacheRead > 0 && cacheCreation > 0 &&
|
|
381
445
|
" · "}
|
|
382
|
-
{
|
|
383
|
-
`${formatCompactNumber(
|
|
446
|
+
{cacheCreation > 0 &&
|
|
447
|
+
`${formatCompactNumber(cacheCreation)} write`}
|
|
384
448
|
</div>
|
|
385
449
|
)}
|
|
386
450
|
</div>
|
package/src/usage/index.ts
CHANGED
|
@@ -4,6 +4,21 @@ export type { UseOrgUsageReportReturn } from "./useOrgUsageReport";
|
|
|
4
4
|
export { OrgUsagePanel } from "./OrgUsagePanel";
|
|
5
5
|
export type { OrgUsagePanelProps } from "./OrgUsagePanel";
|
|
6
6
|
|
|
7
|
+
export { CreditRunwayIndicator } from "./CreditRunwayIndicator";
|
|
8
|
+
export type { CreditRunwayIndicatorProps } from "./CreditRunwayIndicator";
|
|
9
|
+
|
|
10
|
+
export { AgentBreakdownList } from "./AgentBreakdownList";
|
|
11
|
+
export type { AgentBreakdownListProps } from "./AgentBreakdownList";
|
|
12
|
+
|
|
13
|
+
export { HarnessSplitCard } from "./HarnessSplitCard";
|
|
14
|
+
export type { HarnessSplitCardProps } from "./HarnessSplitCard";
|
|
15
|
+
|
|
16
|
+
export { useExportCSV } from "./useExportCSV";
|
|
17
|
+
export type { UseExportCSVReturn, ExportFormat } from "./useExportCSV";
|
|
18
|
+
|
|
19
|
+
export { ExportButton } from "./ExportButton";
|
|
20
|
+
export type { ExportButtonProps } from "./ExportButton";
|
|
21
|
+
|
|
7
22
|
export {
|
|
8
23
|
DATE_RANGE_PRESETS,
|
|
9
24
|
dateRangeFromPreset,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import type { GetOrgUsageReportOutput } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
5
|
+
|
|
6
|
+
/** Export format for the CSV download. */
|
|
7
|
+
export type ExportFormat = "daily_summary" | "model_breakdown";
|
|
8
|
+
|
|
9
|
+
/** Return value of {@link useExportCSV}. */
|
|
10
|
+
export interface UseExportCSVReturn {
|
|
11
|
+
/** Trigger a CSV download for the given format. */
|
|
12
|
+
readonly exportCSV: (format: ExportFormat) => void;
|
|
13
|
+
/** Whether an export is being generated. */
|
|
14
|
+
readonly isExporting: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Behavior hook that generates and downloads usage data as CSV.
|
|
19
|
+
*
|
|
20
|
+
* Operates entirely client-side from data already in memory — no
|
|
21
|
+
* additional RPC calls. Supports two formats:
|
|
22
|
+
* - `daily_summary` — one row per day with date, executions, tokens, cost
|
|
23
|
+
* - `model_breakdown` — one row per model with calls, tokens, cost
|
|
24
|
+
*
|
|
25
|
+
* @param report - The org usage report data (from `useOrgUsageReport`).
|
|
26
|
+
* @param orgId - Organization ID for the filename.
|
|
27
|
+
*/
|
|
28
|
+
export function useExportCSV(
|
|
29
|
+
report: GetOrgUsageReportOutput | null,
|
|
30
|
+
orgId: string,
|
|
31
|
+
): UseExportCSVReturn {
|
|
32
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
33
|
+
|
|
34
|
+
const exportCSV = useCallback(
|
|
35
|
+
(format: ExportFormat) => {
|
|
36
|
+
if (!report) return;
|
|
37
|
+
setIsExporting(true);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const { csv, filename } = format === "daily_summary"
|
|
41
|
+
? buildDailySummaryCSV(report, orgId)
|
|
42
|
+
: buildModelBreakdownCSV(report, orgId);
|
|
43
|
+
|
|
44
|
+
downloadCSV(csv, filename);
|
|
45
|
+
} finally {
|
|
46
|
+
setIsExporting(false);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[report, orgId],
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return { exportCSV, isExporting };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildDailySummaryCSV(
|
|
56
|
+
report: GetOrgUsageReportOutput,
|
|
57
|
+
orgId: string,
|
|
58
|
+
): { csv: string; filename: string } {
|
|
59
|
+
const header = "Date,Executions,Tokens,Cost (USD)";
|
|
60
|
+
const rows = report.dailyCosts.map((entry) => {
|
|
61
|
+
const cost = (Number(entry.billableCostMicros) / 1_000_000).toFixed(6);
|
|
62
|
+
return `${entry.date},${entry.executionCount},${entry.totalTokens},${cost}`;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
csv: [header, ...rows].join("\n"),
|
|
67
|
+
filename: `${orgId}-daily-usage.csv`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildModelBreakdownCSV(
|
|
72
|
+
report: GetOrgUsageReportOutput,
|
|
73
|
+
orgId: string,
|
|
74
|
+
): { csv: string; filename: string } {
|
|
75
|
+
const header =
|
|
76
|
+
"Model,Provider,Calls,Input Tokens,Output Tokens,Cache Read Tokens,Cache Write Tokens,Cost (USD)";
|
|
77
|
+
const rows = report.modelBreakdown.map((m) => {
|
|
78
|
+
const cost = (Number(m.billableCostMicros) / 1_000_000).toFixed(6);
|
|
79
|
+
return [
|
|
80
|
+
escapeCsvField(m.model),
|
|
81
|
+
escapeCsvField(m.provider),
|
|
82
|
+
m.callCount,
|
|
83
|
+
m.inputTokens,
|
|
84
|
+
m.outputTokens,
|
|
85
|
+
m.cacheReadInputTokens,
|
|
86
|
+
m.cacheCreationInputTokens,
|
|
87
|
+
cost,
|
|
88
|
+
].join(",");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
csv: [header, ...rows].join("\n"),
|
|
93
|
+
filename: `${orgId}-model-usage.csv`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function escapeCsvField(value: string): string {
|
|
98
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
99
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function downloadCSV(csv: string, filename: string): void {
|
|
105
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
106
|
+
const url = URL.createObjectURL(blob);
|
|
107
|
+
const link = document.createElement("a");
|
|
108
|
+
link.href = url;
|
|
109
|
+
link.download = filename;
|
|
110
|
+
link.style.display = "none";
|
|
111
|
+
document.body.appendChild(link);
|
|
112
|
+
link.click();
|
|
113
|
+
document.body.removeChild(link);
|
|
114
|
+
URL.revokeObjectURL(url);
|
|
115
|
+
}
|
|
@@ -54,7 +54,8 @@ export interface UseOrgUsageReportReturn {
|
|
|
54
54
|
* if (error) return <ErrorMessage error={error} />;
|
|
55
55
|
* if (!report) return null;
|
|
56
56
|
*
|
|
57
|
-
*
|
|
57
|
+
* const costUsd = Number(report.totalBillableCostMicros) / 1_000_000;
|
|
58
|
+
* return <div>Total cost: {formatCost(costUsd)}</div>;
|
|
58
59
|
* ```
|
|
59
60
|
*/
|
|
60
61
|
export function useOrgUsageReport(
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import { useWorkspaceEntries } from "../useWorkspaceEntries";
|
|
4
|
+
|
|
5
|
+
describe("useWorkspaceEntries — return reference stability", () => {
|
|
6
|
+
it("returns the same object reference across re-renders when state is unchanged", () => {
|
|
7
|
+
const { result, rerender } = renderHook(() => useWorkspaceEntries());
|
|
8
|
+
|
|
9
|
+
const first = result.current;
|
|
10
|
+
rerender();
|
|
11
|
+
const second = result.current;
|
|
12
|
+
|
|
13
|
+
expect(second).toBe(first);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns a new reference after adding an entry", () => {
|
|
17
|
+
const { result } = renderHook(() => useWorkspaceEntries());
|
|
18
|
+
|
|
19
|
+
const before = result.current;
|
|
20
|
+
|
|
21
|
+
act(() => {
|
|
22
|
+
result.current.addGitRepo("https://github.com/acme/repo.git", "main");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.current).not.toBe(before);
|
|
26
|
+
expect(result.current.entries).toHaveLength(1);
|
|
27
|
+
expect(result.current.hasEntries).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("stabilizes after add — re-renders without state change preserve reference", () => {
|
|
31
|
+
const { result, rerender } = renderHook(() => useWorkspaceEntries());
|
|
32
|
+
|
|
33
|
+
act(() => {
|
|
34
|
+
result.current.addGitRepo("https://github.com/acme/repo.git");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const after = result.current;
|
|
38
|
+
rerender();
|
|
39
|
+
|
|
40
|
+
expect(result.current).toBe(after);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("callback references are stable across re-renders", () => {
|
|
44
|
+
const { result, rerender } = renderHook(() => useWorkspaceEntries());
|
|
45
|
+
|
|
46
|
+
const { addGitRepo, addLocalPath, remove, clear, clearLocal, toInput } = result.current;
|
|
47
|
+
|
|
48
|
+
rerender();
|
|
49
|
+
|
|
50
|
+
expect(result.current.addGitRepo).toBe(addGitRepo);
|
|
51
|
+
expect(result.current.addLocalPath).toBe(addLocalPath);
|
|
52
|
+
expect(result.current.remove).toBe(remove);
|
|
53
|
+
expect(result.current.clear).toBe(clear);
|
|
54
|
+
expect(result.current.clearLocal).toBe(clearLocal);
|
|
55
|
+
expect(result.current.toInput).toBe(toInput);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("returns a new reference after removing an entry", () => {
|
|
59
|
+
const { result } = renderHook(() => useWorkspaceEntries());
|
|
60
|
+
|
|
61
|
+
act(() => {
|
|
62
|
+
result.current.addLocalPath("/home/dev/project");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const withEntry = result.current;
|
|
66
|
+
const entryId = withEntry.entries[0].id;
|
|
67
|
+
|
|
68
|
+
act(() => {
|
|
69
|
+
result.current.remove(entryId);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(result.current).not.toBe(withEntry);
|
|
73
|
+
expect(result.current.entries).toHaveLength(0);
|
|
74
|
+
expect(result.current.hasEntries).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
4
|
import type { WorkspaceEntryInput, WorkspaceSourceInput } from "@stigmer/sdk";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -156,14 +156,19 @@ export function useWorkspaceEntries(): UseWorkspaceEntriesReturn {
|
|
|
156
156
|
});
|
|
157
157
|
}, [entries]);
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
const hasEntries = entries.length > 0;
|
|
160
|
+
|
|
161
|
+
return useMemo(
|
|
162
|
+
() => ({
|
|
163
|
+
entries,
|
|
164
|
+
addGitRepo,
|
|
165
|
+
addLocalPath,
|
|
166
|
+
remove,
|
|
167
|
+
clear,
|
|
168
|
+
clearLocal,
|
|
169
|
+
toInput,
|
|
170
|
+
hasEntries,
|
|
171
|
+
}),
|
|
172
|
+
[entries, addGitRepo, addLocalPath, remove, clear, clearLocal, toInput, hasEntries],
|
|
173
|
+
);
|
|
169
174
|
}
|