@stigmer/react 3.3.0 → 3.4.1
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/agent/useAgentCount.d.ts +2 -0
- package/agent/useAgentCount.d.ts.map +1 -1
- package/agent/useAgentCount.js.map +1 -1
- package/api-key/CreateApiKeyForm.d.ts +7 -1
- package/api-key/CreateApiKeyForm.d.ts.map +1 -1
- package/api-key/CreateApiKeyForm.js +2 -2
- package/api-key/CreateApiKeyForm.js.map +1 -1
- package/billing/index.d.ts +0 -18
- package/billing/index.d.ts.map +1 -1
- package/billing/index.js +4 -9
- package/billing/index.js.map +1 -1
- package/cursor-accounts/CursorAccountEditor.d.ts +7 -1
- package/cursor-accounts/CursorAccountEditor.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountEditor.js +21 -5
- package/cursor-accounts/CursorAccountEditor.js.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.d.ts +9 -5
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.js +34 -140
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
- package/cursor-accounts/MemberCoverageTable.d.ts +29 -0
- package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -0
- package/cursor-accounts/MemberCoverageTable.js +150 -0
- package/cursor-accounts/MemberCoverageTable.js.map +1 -0
- package/cursor-accounts/MemberKeysPanel.d.ts +25 -0
- package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -0
- package/cursor-accounts/MemberKeysPanel.js +115 -0
- package/cursor-accounts/MemberKeysPanel.js.map +1 -0
- package/cursor-accounts/badges.d.ts +9 -0
- package/cursor-accounts/badges.d.ts.map +1 -0
- package/cursor-accounts/badges.js +10 -0
- package/cursor-accounts/badges.js.map +1 -0
- package/cursor-accounts/cursor-account-coverage.d.ts +41 -0
- package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -0
- package/cursor-accounts/cursor-account-coverage.js +26 -0
- package/cursor-accounts/cursor-account-coverage.js.map +1 -0
- package/cursor-accounts/cursor-account-format.d.ts +13 -0
- package/cursor-accounts/cursor-account-format.d.ts.map +1 -1
- package/cursor-accounts/cursor-account-format.js +16 -1
- package/cursor-accounts/cursor-account-format.js.map +1 -1
- package/datastore/CollectionRecordsBrowser.js +18 -4
- package/datastore/CollectionRecordsBrowser.js.map +1 -1
- package/datastore/RecordFilterBuilder.d.ts +8 -1
- package/datastore/RecordFilterBuilder.d.ts.map +1 -1
- package/datastore/RecordFilterBuilder.js +2 -2
- package/datastore/RecordFilterBuilder.js.map +1 -1
- package/datastore/recordFilter.d.ts +9 -1
- package/datastore/recordFilter.d.ts.map +1 -1
- package/datastore/recordFilter.js +12 -1
- package/datastore/recordFilter.js.map +1 -1
- package/datastore/useDatastoreCount.d.ts +2 -0
- package/datastore/useDatastoreCount.d.ts.map +1 -1
- package/datastore/useDatastoreCount.js.map +1 -1
- package/execution/ExecutionProgress.d.ts.map +1 -1
- package/execution/ExecutionProgress.js +13 -4
- package/execution/ExecutionProgress.js.map +1 -1
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +10 -2
- package/execution/MessageThread.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +31 -1
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +2 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +8 -6
- package/index.d.ts.map +1 -1
- package/index.js +8 -4
- package/index.js.map +1 -1
- package/internal/form-primitives.d.ts +10 -0
- package/internal/form-primitives.d.ts.map +1 -0
- package/{billing → internal}/form-primitives.js +5 -9
- package/internal/form-primitives.js.map +1 -0
- package/manifest/ApplyManifestDialog.d.ts +10 -5
- package/manifest/ApplyManifestDialog.d.ts.map +1 -1
- package/manifest/ApplyManifestDialog.js +30 -13
- package/manifest/ApplyManifestDialog.js.map +1 -1
- package/manifest/useApplyManifest.d.ts +6 -2
- package/manifest/useApplyManifest.d.ts.map +1 -1
- package/manifest/useApplyManifest.js +5 -5
- package/manifest/useApplyManifest.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts +38 -2
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +21 -5
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/index.d.ts +7 -0
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +9 -0
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/steps/EnvironmentAuthStep.d.ts +4 -0
- package/mcp-server/steps/EnvironmentAuthStep.d.ts.map +1 -1
- package/mcp-server/steps/EnvironmentAuthStep.js +4 -0
- package/mcp-server/steps/EnvironmentAuthStep.js.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.d.ts +6 -0
- package/mcp-server/steps/IdentityTransportStep.d.ts.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.js +7 -1
- package/mcp-server/steps/IdentityTransportStep.js.map +1 -1
- package/mcp-server/steps/ReviewStep.d.ts +5 -0
- package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
- package/mcp-server/steps/ReviewStep.js +16 -2
- package/mcp-server/steps/ReviewStep.js.map +1 -1
- package/mcp-server/useMcpServerCount.d.ts +2 -0
- package/mcp-server/useMcpServerCount.d.ts.map +1 -1
- package/mcp-server/useMcpServerCount.js.map +1 -1
- package/package.json +4 -4
- package/pricing-governance/BaselineEditor.d.ts.map +1 -0
- package/{billing → pricing-governance}/BaselineEditor.js +19 -2
- package/pricing-governance/BaselineEditor.js.map +1 -0
- package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -0
- package/pricing-governance/ModelCatalogPanel.js.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.d.ts.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.js.map +1 -0
- package/pricing-governance/OperatorAccessNotice.d.ts.map +1 -0
- package/pricing-governance/OperatorAccessNotice.js.map +1 -0
- package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -0
- package/{billing → pricing-governance}/PricingGovernanceConsole.js +1 -1
- package/pricing-governance/PricingGovernanceConsole.js.map +1 -0
- package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -0
- package/pricing-governance/PricingGovernancePanel.js.map +1 -0
- package/pricing-governance/RetireConfirm.d.ts.map +1 -0
- package/{billing → pricing-governance}/RetireConfirm.js +1 -1
- package/pricing-governance/RetireConfirm.js.map +1 -0
- package/pricing-governance/governance-primitives.d.ts.map +1 -0
- package/pricing-governance/governance-primitives.js.map +1 -0
- package/pricing-governance/index.d.ts +19 -0
- package/pricing-governance/index.d.ts.map +1 -0
- package/pricing-governance/index.js +20 -0
- package/pricing-governance/index.js.map +1 -0
- package/pricing-governance/pricing-format.d.ts.map +1 -0
- package/pricing-governance/pricing-format.js.map +1 -0
- package/pricing-governance/useDecidePricingOverride.d.ts.map +1 -0
- package/pricing-governance/useDecidePricingOverride.js.map +1 -0
- package/pricing-governance/useModelGovernanceView.d.ts.map +1 -0
- package/pricing-governance/useModelGovernanceView.js.map +1 -0
- package/pricing-governance/useModelPricingBaselines.d.ts.map +1 -0
- package/pricing-governance/useModelPricingBaselines.js.map +1 -0
- package/pricing-governance/usePricingGovernance.d.ts.map +1 -0
- package/pricing-governance/usePricingGovernance.js.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.js.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.js.map +1 -0
- package/resource-creation/CreationPicker.d.ts.map +1 -1
- package/resource-creation/CreationPicker.js +3 -3
- package/resource-creation/CreationPicker.js.map +1 -1
- package/resource-creation/WizardNav.d.ts.map +1 -1
- package/resource-creation/WizardNav.js +1 -1
- package/resource-creation/WizardNav.js.map +1 -1
- package/resource-detail/Section.d.ts +8 -1
- package/resource-detail/Section.d.ts.map +1 -1
- package/resource-detail/Section.js +2 -2
- package/resource-detail/Section.js.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts +9 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.js +2 -1
- package/resource-workbench/components/ResourceWorkbench.js.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.d.ts +9 -0
- package/resource-workbench/hooks/useResourceCollection.d.ts.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.js +2 -2
- package/resource-workbench/hooks/useResourceCollection.js.map +1 -1
- package/search/useResourceCount.d.ts +7 -0
- package/search/useResourceCount.d.ts.map +1 -1
- package/search/useResourceCount.js +2 -1
- package/search/useResourceCount.js.map +1 -1
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +17 -3
- package/session/NewSessionViewer.js.map +1 -1
- package/session/SessionViewer.d.ts +2 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +19 -11
- package/session/SessionViewer.js.map +1 -1
- package/session/SessionViewerLayout.d.ts +90 -0
- package/session/SessionViewerLayout.d.ts.map +1 -0
- package/session/SessionViewerLayout.js +32 -0
- package/session/SessionViewerLayout.js.map +1 -0
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +10 -0
- package/session/index.js.map +1 -1
- package/session/useCreateSession.d.ts +28 -0
- package/session/useCreateSession.d.ts.map +1 -1
- package/session/useCreateSession.js +2 -1
- package/session/useCreateSession.js.map +1 -1
- package/session/useNewSessionFlow.d.ts +30 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +7 -1
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionRailViews.d.ts +2 -2
- package/session/useUpdateSession.d.ts +29 -10
- package/session/useUpdateSession.d.ts.map +1 -1
- package/session/useUpdateSession.js +29 -10
- package/session/useUpdateSession.js.map +1 -1
- package/skill/useSkillCount.d.ts +2 -0
- package/skill/useSkillCount.d.ts.map +1 -1
- package/skill/useSkillCount.js.map +1 -1
- package/src/agent/useAgentCount.ts +2 -0
- package/src/api-key/CreateApiKeyForm.tsx +8 -1
- package/src/api-key/__tests__/CreateApiKeyForm.test.tsx +58 -0
- package/src/billing/index.ts +5 -37
- package/src/cursor-accounts/CursorAccountEditor.tsx +61 -13
- package/src/cursor-accounts/CursorAccountsConsole.tsx +44 -400
- package/src/cursor-accounts/MemberCoverageTable.tsx +439 -0
- package/src/cursor-accounts/MemberKeysPanel.tsx +278 -0
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +306 -10
- package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +73 -0
- package/src/cursor-accounts/badges.tsx +26 -0
- package/src/cursor-accounts/cursor-account-coverage.ts +71 -0
- package/src/cursor-accounts/cursor-account-format.ts +17 -1
- package/src/datastore/CollectionRecordsBrowser.tsx +29 -3
- package/src/datastore/RecordFilterBuilder.tsx +12 -1
- package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +38 -0
- package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +14 -0
- package/src/datastore/__tests__/recordFilter.test.ts +13 -0
- package/src/datastore/recordFilter.ts +15 -1
- package/src/datastore/useDatastoreCount.ts +2 -0
- package/src/execution/ExecutionProgress.tsx +14 -4
- package/src/execution/MessageThread.tsx +10 -2
- package/src/execution/__tests__/ExecutionProgress.test.tsx +67 -0
- package/src/execution/__tests__/MessageThread.test.tsx +44 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +86 -0
- package/src/execution/useCreateAgentExecution.ts +41 -6
- package/src/index.ts +43 -24
- package/src/{billing → internal}/form-primitives.tsx +4 -32
- package/src/manifest/ApplyManifestDialog.tsx +40 -16
- package/src/manifest/__tests__/ApplyManifestDialog.test.tsx +147 -0
- package/src/manifest/__tests__/useApplyManifest.test.tsx +6 -6
- package/src/manifest/useApplyManifest.ts +12 -8
- package/src/mcp-server/McpServerDetailView.tsx +52 -4
- package/src/mcp-server/__tests__/McpServerDetailView.test.tsx +345 -0
- package/src/mcp-server/index.ts +13 -0
- package/src/mcp-server/steps/EnvironmentAuthStep.tsx +4 -0
- package/src/mcp-server/steps/IdentityTransportStep.tsx +9 -1
- package/src/mcp-server/steps/ReviewStep.tsx +18 -1
- package/src/mcp-server/useMcpServerCount.ts +2 -0
- package/src/{billing → pricing-governance}/BaselineEditor.tsx +36 -3
- package/src/{billing → pricing-governance}/PricingGovernanceConsole.tsx +1 -1
- package/src/{billing → pricing-governance}/RetireConfirm.tsx +1 -1
- package/src/pricing-governance/index.ts +50 -0
- package/src/resource-creation/CreationPicker.tsx +7 -0
- package/src/resource-creation/WizardNav.tsx +1 -0
- package/src/resource-detail/Section.tsx +9 -1
- package/src/resource-workbench/components/ResourceWorkbench.tsx +13 -0
- package/src/resource-workbench/hooks/useResourceCollection.ts +11 -1
- package/src/search/useResourceCount.ts +9 -1
- package/src/session/NewSessionViewer.tsx +48 -61
- package/src/session/SessionViewer.tsx +71 -86
- package/src/session/SessionViewerLayout.tsx +131 -0
- package/src/session/__tests__/SessionViewerLayout.test.tsx +140 -0
- package/src/session/__tests__/useCreateSession.test.tsx +94 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
- package/src/session/index.ts +14 -0
- package/src/session/useCreateSession.ts +30 -0
- package/src/session/useNewSessionFlow.ts +45 -1
- package/src/session/useSessionRailViews.tsx +2 -2
- package/src/session/useUpdateSession.ts +29 -10
- package/src/skill/useSkillCount.ts +2 -0
- package/src/test/__tests__/samples.test.ts +159 -2
- package/src/test/index.ts +1 -1
- package/src/test/samples.ts +109 -14
- package/src/workflow/useWorkflowCount.ts +2 -0
- package/styles.css +1 -1
- package/test/index.d.ts +1 -1
- package/test/index.d.ts.map +1 -1
- package/test/index.js +1 -1
- package/test/index.js.map +1 -1
- package/test/samples.d.ts +79 -7
- package/test/samples.d.ts.map +1 -1
- package/test/samples.js +101 -14
- package/test/samples.js.map +1 -1
- package/workflow/useWorkflowCount.d.ts +2 -0
- package/workflow/useWorkflowCount.d.ts.map +1 -1
- package/workflow/useWorkflowCount.js.map +1 -1
- package/billing/BaselineEditor.d.ts.map +0 -1
- package/billing/BaselineEditor.js.map +0 -1
- package/billing/ModelCatalogPanel.d.ts.map +0 -1
- package/billing/ModelCatalogPanel.js.map +0 -1
- package/billing/ModelGovernanceDetail.d.ts.map +0 -1
- package/billing/ModelGovernanceDetail.js.map +0 -1
- package/billing/OperatorAccessNotice.d.ts.map +0 -1
- package/billing/OperatorAccessNotice.js.map +0 -1
- package/billing/PricingGovernanceConsole.d.ts.map +0 -1
- package/billing/PricingGovernanceConsole.js.map +0 -1
- package/billing/PricingGovernancePanel.d.ts.map +0 -1
- package/billing/PricingGovernancePanel.js.map +0 -1
- package/billing/RetireConfirm.d.ts.map +0 -1
- package/billing/RetireConfirm.js.map +0 -1
- package/billing/form-primitives.d.ts +0 -18
- package/billing/form-primitives.d.ts.map +0 -1
- package/billing/form-primitives.js.map +0 -1
- package/billing/governance-primitives.d.ts.map +0 -1
- package/billing/governance-primitives.js.map +0 -1
- package/billing/pricing-format.d.ts.map +0 -1
- package/billing/pricing-format.js.map +0 -1
- package/billing/useDecidePricingOverride.d.ts.map +0 -1
- package/billing/useDecidePricingOverride.js.map +0 -1
- package/billing/useModelGovernanceView.d.ts.map +0 -1
- package/billing/useModelGovernanceView.js.map +0 -1
- package/billing/useModelPricingBaselines.d.ts.map +0 -1
- package/billing/useModelPricingBaselines.js.map +0 -1
- package/billing/usePricingGovernance.d.ts.map +0 -1
- package/billing/usePricingGovernance.js.map +0 -1
- package/billing/useRetireModelPricingBaseline.d.ts.map +0 -1
- package/billing/useRetireModelPricingBaseline.js.map +0 -1
- package/billing/useUpsertModelPricingBaseline.d.ts.map +0 -1
- package/billing/useUpsertModelPricingBaseline.js.map +0 -1
- /package/{billing → pricing-governance}/BaselineEditor.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelCatalogPanel.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelCatalogPanel.js +0 -0
- /package/{billing → pricing-governance}/ModelGovernanceDetail.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelGovernanceDetail.js +0 -0
- /package/{billing → pricing-governance}/OperatorAccessNotice.d.ts +0 -0
- /package/{billing → pricing-governance}/OperatorAccessNotice.js +0 -0
- /package/{billing → pricing-governance}/PricingGovernanceConsole.d.ts +0 -0
- /package/{billing → pricing-governance}/PricingGovernancePanel.d.ts +0 -0
- /package/{billing → pricing-governance}/PricingGovernancePanel.js +0 -0
- /package/{billing → pricing-governance}/RetireConfirm.d.ts +0 -0
- /package/{billing → pricing-governance}/governance-primitives.d.ts +0 -0
- /package/{billing → pricing-governance}/governance-primitives.js +0 -0
- /package/{billing → pricing-governance}/pricing-format.d.ts +0 -0
- /package/{billing → pricing-governance}/pricing-format.js +0 -0
- /package/{billing → pricing-governance}/useDecidePricingOverride.d.ts +0 -0
- /package/{billing → pricing-governance}/useDecidePricingOverride.js +0 -0
- /package/{billing → pricing-governance}/useModelGovernanceView.d.ts +0 -0
- /package/{billing → pricing-governance}/useModelGovernanceView.js +0 -0
- /package/{billing → pricing-governance}/useModelPricingBaselines.d.ts +0 -0
- /package/{billing → pricing-governance}/useModelPricingBaselines.js +0 -0
- /package/{billing → pricing-governance}/usePricingGovernance.d.ts +0 -0
- /package/{billing → pricing-governance}/usePricingGovernance.js +0 -0
- /package/{billing → pricing-governance}/useRetireModelPricingBaseline.d.ts +0 -0
- /package/{billing → pricing-governance}/useRetireModelPricingBaseline.js +0 -0
- /package/{billing → pricing-governance}/useUpsertModelPricingBaseline.d.ts +0 -0
- /package/{billing → pricing-governance}/useUpsertModelPricingBaseline.js +0 -0
- /package/src/{billing → pricing-governance}/ModelCatalogPanel.tsx +0 -0
- /package/src/{billing → pricing-governance}/ModelGovernanceDetail.tsx +0 -0
- /package/src/{billing → pricing-governance}/OperatorAccessNotice.tsx +0 -0
- /package/src/{billing → pricing-governance}/PricingGovernancePanel.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/ModelCatalogPanel.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/PricingGovernanceConsole.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/useModelGovernanceView.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/governance-primitives.tsx +0 -0
- /package/src/{billing → pricing-governance}/pricing-format.ts +0 -0
- /package/src/{billing → pricing-governance}/useDecidePricingOverride.ts +0 -0
- /package/src/{billing → pricing-governance}/useModelGovernanceView.ts +0 -0
- /package/src/{billing → pricing-governance}/useModelPricingBaselines.ts +0 -0
- /package/src/{billing → pricing-governance}/usePricingGovernance.ts +0 -0
- /package/src/{billing → pricing-governance}/useRetireModelPricingBaseline.ts +0 -0
- /package/src/{billing → pricing-governance}/useUpsertModelPricingBaseline.ts +0 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import {
|
|
6
|
+
CursorMemberKeyState,
|
|
7
|
+
type CursorMemberKeyView,
|
|
8
|
+
type CursorTeamMemberView,
|
|
9
|
+
} from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/io_pb";
|
|
10
|
+
import type { CursorMemberSpend } from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/cursor_account_pb";
|
|
11
|
+
import { Button } from "../button/index.js";
|
|
12
|
+
import { StateBadge } from "./badges.js";
|
|
13
|
+
import { formatPoolPercent, formatSpendMicros } from "./cursor-account-format.js";
|
|
14
|
+
import type { CursorAccountCoverage } from "./cursor-account-coverage.js";
|
|
15
|
+
import type { useCursorMemberKeyActions } from "./useCursorMemberKeyActions.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One grid template shared by the header and every row so the columns
|
|
19
|
+
* can never drift apart: Member · Key · First-party % · API % ·
|
|
20
|
+
* Included $ · On-demand $ · Status · Actions.
|
|
21
|
+
*
|
|
22
|
+
* The four numeric columns mirror Cursor's own Members page (First-Party
|
|
23
|
+
* Models %, API %, On-Demand $) plus the included-quota dollars, so an
|
|
24
|
+
* operator can read this table and the Cursor dashboard side by side.
|
|
25
|
+
*
|
|
26
|
+
* Member gets the largest flexible share: the email is the row's
|
|
27
|
+
* identity, so it wraps rather than truncates when space runs out (see
|
|
28
|
+
* MemberCell) while the key name stays truncate-with-tooltip.
|
|
29
|
+
*/
|
|
30
|
+
const ROW_GRID =
|
|
31
|
+
"grid grid-cols-[minmax(0,2.2fr)_minmax(0,1fr)_4.5rem_4.5rem_4.5rem_4.5rem_minmax(6rem,auto)_minmax(10rem,auto)] items-center gap-2 px-3 py-2";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The roster-coverage table: every member and every stored execution key
|
|
35
|
+
* of one Cursor account, classified into three explicit categories
|
|
36
|
+
* (server-computed — this component only renders):
|
|
37
|
+
*
|
|
38
|
+
* 1. **On the team, key held** — the account's routable capacity.
|
|
39
|
+
* 2. **On the team, no key** — the coverage gap; their spend columns
|
|
40
|
+
* help the operator prioritize whose key to import.
|
|
41
|
+
* 3. **Key held, not on the team** — imported keys whose owner left the
|
|
42
|
+
* team or never joined it. Resolved by a Cursor-dashboard invite:
|
|
43
|
+
* when the account carries a team invite link, each row offers
|
|
44
|
+
* one-click copy (never navigation — opening an invite link would
|
|
45
|
+
* join the OPERATOR's own Cursor account to the team).
|
|
46
|
+
*
|
|
47
|
+
* Before the first sync there is no roster to classify against, so keys
|
|
48
|
+
* render unclassified with a "sync now" notice instead of being falsely
|
|
49
|
+
* reported as off-team.
|
|
50
|
+
*/
|
|
51
|
+
export function MemberCoverageTable({
|
|
52
|
+
accountId,
|
|
53
|
+
coverage,
|
|
54
|
+
inviteLink,
|
|
55
|
+
actions,
|
|
56
|
+
onChanged,
|
|
57
|
+
}: {
|
|
58
|
+
readonly accountId: string;
|
|
59
|
+
readonly coverage: CursorAccountCoverage;
|
|
60
|
+
/** The account-level Cursor team invite link; empty when not configured. */
|
|
61
|
+
readonly inviteLink: string;
|
|
62
|
+
readonly actions: ReturnType<typeof useCursorMemberKeyActions>;
|
|
63
|
+
readonly onChanged: () => void;
|
|
64
|
+
}) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
role="table"
|
|
68
|
+
aria-label="Team coverage"
|
|
69
|
+
className="rounded-lg border border-border bg-card"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
role="row"
|
|
73
|
+
className={cn(
|
|
74
|
+
ROW_GRID,
|
|
75
|
+
"border-b border-border text-[11px] font-medium text-muted-foreground",
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
<span role="columnheader">Member</span>
|
|
79
|
+
<span role="columnheader">Key</span>
|
|
80
|
+
<span role="columnheader" className="text-right">
|
|
81
|
+
First-party
|
|
82
|
+
</span>
|
|
83
|
+
<span role="columnheader" className="text-right">
|
|
84
|
+
API
|
|
85
|
+
</span>
|
|
86
|
+
<span role="columnheader" className="text-right">
|
|
87
|
+
Included
|
|
88
|
+
</span>
|
|
89
|
+
<span role="columnheader" className="text-right">
|
|
90
|
+
On-demand
|
|
91
|
+
</span>
|
|
92
|
+
<span role="columnheader">Status</span>
|
|
93
|
+
<span role="columnheader" className="text-right">
|
|
94
|
+
Actions
|
|
95
|
+
</span>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{!coverage.hasRoster && (
|
|
99
|
+
<CoverageGroup
|
|
100
|
+
title="Execution keys — not yet classified"
|
|
101
|
+
description='No roster sync has run for this account. Run "Sync now" to load the team roster and classify these keys.'
|
|
102
|
+
count={coverage.unclassified.length}
|
|
103
|
+
>
|
|
104
|
+
{coverage.unclassified.map((keyView) => (
|
|
105
|
+
<KeyRow
|
|
106
|
+
key={keyView.key?.keyId}
|
|
107
|
+
accountId={accountId}
|
|
108
|
+
keyView={keyView}
|
|
109
|
+
hasRoster={false}
|
|
110
|
+
inviteLink=""
|
|
111
|
+
actions={actions}
|
|
112
|
+
onChanged={onChanged}
|
|
113
|
+
/>
|
|
114
|
+
))}
|
|
115
|
+
</CoverageGroup>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
{coverage.onTeamWithKey.length > 0 && (
|
|
119
|
+
<CoverageGroup
|
|
120
|
+
title="On the team — key held"
|
|
121
|
+
count={coverage.onTeamWithKey.length}
|
|
122
|
+
>
|
|
123
|
+
{coverage.onTeamWithKey.map((keyView) => (
|
|
124
|
+
<KeyRow
|
|
125
|
+
key={keyView.key?.keyId}
|
|
126
|
+
accountId={accountId}
|
|
127
|
+
keyView={keyView}
|
|
128
|
+
hasRoster
|
|
129
|
+
inviteLink=""
|
|
130
|
+
actions={actions}
|
|
131
|
+
onChanged={onChanged}
|
|
132
|
+
/>
|
|
133
|
+
))}
|
|
134
|
+
</CoverageGroup>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{/* Rendered whenever a roster exists — an empty gap is an answer
|
|
138
|
+
("fully covered"), not an absence. Hiding the group made a
|
|
139
|
+
healthy roster indistinguishable from a broken sync. */}
|
|
140
|
+
{coverage.hasRoster && (
|
|
141
|
+
<CoverageGroup
|
|
142
|
+
title="On the team — no execution key"
|
|
143
|
+
description={
|
|
144
|
+
coverage.onTeamWithoutKey.length > 0
|
|
145
|
+
? "Sessions can never run under these members' identity or included quota. Add their user-scoped keys below to close the gap."
|
|
146
|
+
: "Every active roster member holds an execution key — the roster is fully covered."
|
|
147
|
+
}
|
|
148
|
+
count={coverage.onTeamWithoutKey.length}
|
|
149
|
+
>
|
|
150
|
+
{coverage.onTeamWithoutKey.map((memberView) => (
|
|
151
|
+
<GapRow key={memberView.member?.email} memberView={memberView} />
|
|
152
|
+
))}
|
|
153
|
+
</CoverageGroup>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{coverage.offTeamWithKey.length > 0 && (
|
|
157
|
+
<CoverageGroup
|
|
158
|
+
title="Key held — not on the team"
|
|
159
|
+
description={
|
|
160
|
+
inviteLink !== ""
|
|
161
|
+
? "These key owners must join the Cursor team before their usage counts against its quota. Copy the invite link, send it to them out-of-band, then \u201CSync now\u201D reclassifies once they join."
|
|
162
|
+
: "These key owners must join the Cursor team before their usage counts against its quota. Invites are sent from the Cursor dashboard (cursor.com \u2192 Invite Members); paste the team's invite link into the account editor to enable one-click copying here."
|
|
163
|
+
}
|
|
164
|
+
count={coverage.offTeamWithKey.length}
|
|
165
|
+
>
|
|
166
|
+
{coverage.offTeamWithKey.map((keyView) => (
|
|
167
|
+
<KeyRow
|
|
168
|
+
key={keyView.key?.keyId}
|
|
169
|
+
accountId={accountId}
|
|
170
|
+
keyView={keyView}
|
|
171
|
+
hasRoster
|
|
172
|
+
inviteLink={inviteLink}
|
|
173
|
+
actions={actions}
|
|
174
|
+
onChanged={onChanged}
|
|
175
|
+
/>
|
|
176
|
+
))}
|
|
177
|
+
</CoverageGroup>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Group section (internal)
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function CoverageGroup({
|
|
188
|
+
title,
|
|
189
|
+
description,
|
|
190
|
+
count,
|
|
191
|
+
children,
|
|
192
|
+
}: {
|
|
193
|
+
readonly title: string;
|
|
194
|
+
readonly description?: string;
|
|
195
|
+
readonly count: number;
|
|
196
|
+
readonly children: React.ReactNode;
|
|
197
|
+
}) {
|
|
198
|
+
return (
|
|
199
|
+
<div role="rowgroup" className="border-b border-border last:border-b-0">
|
|
200
|
+
<div role="row" className="bg-muted-subtle px-3 py-1.5">
|
|
201
|
+
<div role="cell" aria-colspan={8}>
|
|
202
|
+
<span className="text-[11px] font-semibold text-foreground">
|
|
203
|
+
{title}
|
|
204
|
+
<span className="ml-1.5 font-normal text-muted-foreground">
|
|
205
|
+
{count}
|
|
206
|
+
</span>
|
|
207
|
+
</span>
|
|
208
|
+
{description && (
|
|
209
|
+
<span className="block text-[11px] text-muted-foreground">
|
|
210
|
+
{description}
|
|
211
|
+
</span>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
{children}
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Rows (internal)
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The member-identity cell shared by key rows and gap rows. The email is
|
|
226
|
+
* the row's identity, so it must be fully readable: it wraps onto more
|
|
227
|
+
* lines when the column is narrow (emails are unbroken strings, so they
|
|
228
|
+
* need explicit word-breaking) with a hover tooltip as backup, unlike
|
|
229
|
+
* the other text cells which truncate. The secondary name line stays
|
|
230
|
+
* truncated — it is display sugar, not identity.
|
|
231
|
+
*/
|
|
232
|
+
function MemberCell({
|
|
233
|
+
email,
|
|
234
|
+
name,
|
|
235
|
+
}: {
|
|
236
|
+
readonly email: string;
|
|
237
|
+
readonly name?: string;
|
|
238
|
+
}) {
|
|
239
|
+
return (
|
|
240
|
+
<span role="cell" className="min-w-0">
|
|
241
|
+
<span className="block break-words font-medium text-foreground" title={email}>
|
|
242
|
+
{email}
|
|
243
|
+
</span>
|
|
244
|
+
{name && (
|
|
245
|
+
<span className="block truncate text-[11px] text-muted-foreground">
|
|
246
|
+
{name}
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
</span>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** A key-backed row: categories 1 and 3, plus the pre-sync unclassified state. */
|
|
254
|
+
function KeyRow({
|
|
255
|
+
accountId,
|
|
256
|
+
keyView,
|
|
257
|
+
hasRoster,
|
|
258
|
+
inviteLink,
|
|
259
|
+
actions,
|
|
260
|
+
onChanged,
|
|
261
|
+
}: {
|
|
262
|
+
readonly accountId: string;
|
|
263
|
+
readonly keyView: CursorMemberKeyView;
|
|
264
|
+
readonly hasRoster: boolean;
|
|
265
|
+
readonly inviteLink: string;
|
|
266
|
+
readonly actions: ReturnType<typeof useCursorMemberKeyActions>;
|
|
267
|
+
readonly onChanged: () => void;
|
|
268
|
+
}) {
|
|
269
|
+
const key = keyView.key;
|
|
270
|
+
if (!key) return null;
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div role="row" className={cn(ROW_GRID, "border-t border-border-muted text-xs")}>
|
|
274
|
+
<MemberCell email={key.boundEmail} />
|
|
275
|
+
<span role="cell" className="min-w-0">
|
|
276
|
+
<span
|
|
277
|
+
className="block truncate text-muted-foreground"
|
|
278
|
+
title={key.cursorKeyName || undefined}
|
|
279
|
+
>
|
|
280
|
+
{key.cursorKeyName || "unnamed key"}
|
|
281
|
+
</span>
|
|
282
|
+
{key.label && (
|
|
283
|
+
<span className="block truncate text-[11px] text-muted-foreground" title={key.label}>
|
|
284
|
+
{key.label}
|
|
285
|
+
</span>
|
|
286
|
+
)}
|
|
287
|
+
</span>
|
|
288
|
+
<SpendCells spend={keyView.spend} />
|
|
289
|
+
<span role="cell" className="flex flex-wrap items-center gap-1">
|
|
290
|
+
{keyView.usageGuardTripped && <StateBadge tone="warn" label="Usage guard" />}
|
|
291
|
+
<KeyStatusBadge keyView={keyView} hasRoster={hasRoster} />
|
|
292
|
+
</span>
|
|
293
|
+
<span role="cell" className="flex items-center justify-end gap-2">
|
|
294
|
+
{inviteLink !== "" && <CopyInviteButton inviteLink={inviteLink} />}
|
|
295
|
+
<Button
|
|
296
|
+
size="sm"
|
|
297
|
+
variant="outline"
|
|
298
|
+
disabled={actions.isSubmitting}
|
|
299
|
+
onClick={() => {
|
|
300
|
+
void actions
|
|
301
|
+
.setKeyEnabled(accountId, key.keyId, !key.enabled)
|
|
302
|
+
.then(onChanged, () => {
|
|
303
|
+
// Surfaced via actions.error.
|
|
304
|
+
});
|
|
305
|
+
}}
|
|
306
|
+
>
|
|
307
|
+
{key.enabled ? "Disable" : "Enable"}
|
|
308
|
+
</Button>
|
|
309
|
+
<Button
|
|
310
|
+
size="sm"
|
|
311
|
+
variant="destructive"
|
|
312
|
+
disabled={actions.isSubmitting}
|
|
313
|
+
onClick={() => {
|
|
314
|
+
void actions
|
|
315
|
+
.removeKey({ accountId, keyId: key.keyId })
|
|
316
|
+
.then(onChanged, () => {
|
|
317
|
+
// Surfaced via actions.error (incl. the live-pin guard's
|
|
318
|
+
// "disable instead, or force" message).
|
|
319
|
+
});
|
|
320
|
+
}}
|
|
321
|
+
>
|
|
322
|
+
Remove
|
|
323
|
+
</Button>
|
|
324
|
+
</span>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** A coverage-gap row: on the team, no execution key (category 2). */
|
|
330
|
+
function GapRow({ memberView }: { readonly memberView: CursorTeamMemberView }) {
|
|
331
|
+
const member = memberView.member;
|
|
332
|
+
if (!member) return null;
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div role="row" className={cn(ROW_GRID, "border-t border-border-muted text-xs")}>
|
|
336
|
+
<MemberCell email={member.email} name={member.name} />
|
|
337
|
+
<span role="cell" className="text-muted-foreground">
|
|
338
|
+
—
|
|
339
|
+
</span>
|
|
340
|
+
<SpendCells spend={memberView.spend} />
|
|
341
|
+
<span role="cell">
|
|
342
|
+
<StateBadge tone="muted" label="No key" />
|
|
343
|
+
</span>
|
|
344
|
+
<span role="cell" className="text-right text-muted-foreground">
|
|
345
|
+
—
|
|
346
|
+
</span>
|
|
347
|
+
</div>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* The four numeric cells — first-party pool %, API pool %, included $,
|
|
353
|
+
* on-demand $ — shared by key rows and gap rows. Each maps 1:1 onto a
|
|
354
|
+
* stored `CursorMemberSpend` field; the blended `totalPercentUsed` is
|
|
355
|
+
* deliberately not rendered (it maps to nothing on Cursor's dashboard
|
|
356
|
+
* and reports a flat 100 for removed members). Only a member with no
|
|
357
|
+
* spend row at all renders em-dashes — Cursor omits the row itself for
|
|
358
|
+
* some team shapes.
|
|
359
|
+
*/
|
|
360
|
+
function SpendCells({ spend }: { readonly spend: CursorMemberSpend | undefined }) {
|
|
361
|
+
return (
|
|
362
|
+
<>
|
|
363
|
+
<span role="cell" className="text-right tabular-nums text-muted-foreground">
|
|
364
|
+
{spend ? formatPoolPercent(spend.autoPercentUsed) : "—"}
|
|
365
|
+
</span>
|
|
366
|
+
<span role="cell" className="text-right tabular-nums text-muted-foreground">
|
|
367
|
+
{spend ? formatPoolPercent(spend.apiPercentUsed) : "—"}
|
|
368
|
+
</span>
|
|
369
|
+
<span role="cell" className="text-right tabular-nums text-muted-foreground">
|
|
370
|
+
{spend ? formatSpendMicros(spend.includedSpendUsdMicros) : "—"}
|
|
371
|
+
</span>
|
|
372
|
+
<span role="cell" className="text-right tabular-nums text-muted-foreground">
|
|
373
|
+
{spend ? formatSpendMicros(spend.overageSpendUsdMicros) : "—"}
|
|
374
|
+
</span>
|
|
375
|
+
</>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
// Status badge (internal)
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Membership + enablement status of one key row. With a roster present,
|
|
385
|
+
* "owner unknown" means the owner never appeared in it — "Not on team";
|
|
386
|
+
* without one it is merely missing data — "Awaiting sync".
|
|
387
|
+
*/
|
|
388
|
+
function KeyStatusBadge({
|
|
389
|
+
keyView,
|
|
390
|
+
hasRoster,
|
|
391
|
+
}: {
|
|
392
|
+
readonly keyView: CursorMemberKeyView;
|
|
393
|
+
readonly hasRoster: boolean;
|
|
394
|
+
}) {
|
|
395
|
+
if (!hasRoster) {
|
|
396
|
+
return <StateBadge tone="muted" label="Awaiting sync" />;
|
|
397
|
+
}
|
|
398
|
+
if (keyView.state === CursorMemberKeyState.member_key_owner_removed) {
|
|
399
|
+
return <StateBadge tone="warn" label="Left team" />;
|
|
400
|
+
}
|
|
401
|
+
if (keyView.state === CursorMemberKeyState.member_key_owner_unknown) {
|
|
402
|
+
return <StateBadge tone="muted" label="Not on team" />;
|
|
403
|
+
}
|
|
404
|
+
return keyView.key?.enabled ? (
|
|
405
|
+
<StateBadge tone="ok" label="Active" />
|
|
406
|
+
) : (
|
|
407
|
+
<StateBadge tone="muted" label="Disabled" />
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
// Copy-invite action (internal)
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Copies the account-level team invite link. Deliberately a copy, never
|
|
417
|
+
* a navigation: a Cursor invite link is a join link, and opening it
|
|
418
|
+
* would join the operator's own Cursor account to the team.
|
|
419
|
+
*/
|
|
420
|
+
function CopyInviteButton({ inviteLink }: { readonly inviteLink: string }) {
|
|
421
|
+
const [copied, setCopied] = useState(false);
|
|
422
|
+
|
|
423
|
+
const handleCopy = useCallback(async () => {
|
|
424
|
+
try {
|
|
425
|
+
await navigator.clipboard.writeText(inviteLink);
|
|
426
|
+
setCopied(true);
|
|
427
|
+
setTimeout(() => setCopied(false), 2000);
|
|
428
|
+
} catch {
|
|
429
|
+
// Clipboard unavailable (permissions, non-secure context) — the
|
|
430
|
+
// section description still points at the Cursor dashboard.
|
|
431
|
+
}
|
|
432
|
+
}, [inviteLink]);
|
|
433
|
+
|
|
434
|
+
return (
|
|
435
|
+
<Button size="sm" variant="outline" onClick={() => void handleCopy()}>
|
|
436
|
+
{copied ? "Copied" : "Copy invite"}
|
|
437
|
+
</Button>
|
|
438
|
+
);
|
|
439
|
+
}
|