@stigmer/react 3.2.3 → 3.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/agent/steps/ReviewStep.d.ts.map +1 -1
- package/agent/steps/ReviewStep.js +2 -3
- package/agent/steps/ReviewStep.js.map +1 -1
- package/billing/index.d.ts.map +1 -1
- package/billing/index.js +4 -0
- package/billing/index.js.map +1 -1
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +11 -5
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/composer/SessionComposer.d.ts +1 -1
- package/cursor-accounts/CursorAccountEditor.d.ts +34 -0
- package/cursor-accounts/CursorAccountEditor.d.ts.map +1 -0
- package/cursor-accounts/CursorAccountEditor.js +67 -0
- package/cursor-accounts/CursorAccountEditor.js.map +1 -0
- package/cursor-accounts/CursorAccountsAccessNotice.d.ts +11 -0
- package/cursor-accounts/CursorAccountsAccessNotice.d.ts.map +1 -0
- package/cursor-accounts/CursorAccountsAccessNotice.js +14 -0
- package/cursor-accounts/CursorAccountsAccessNotice.js.map +1 -0
- package/cursor-accounts/CursorAccountsConsole.d.ts +32 -0
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -0
- package/cursor-accounts/CursorAccountsConsole.js +169 -0
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -0
- 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 +23 -0
- package/cursor-accounts/cursor-account-format.d.ts.map +1 -0
- package/cursor-accounts/cursor-account-format.js +41 -0
- package/cursor-accounts/cursor-account-format.js.map +1 -0
- package/cursor-accounts/index.d.ts +18 -0
- package/cursor-accounts/index.d.ts.map +1 -0
- package/cursor-accounts/index.js +13 -0
- package/cursor-accounts/index.js.map +1 -0
- package/cursor-accounts/useCursorAccountView.d.ts +30 -0
- package/cursor-accounts/useCursorAccountView.d.ts.map +1 -0
- package/cursor-accounts/useCursorAccountView.js +25 -0
- package/cursor-accounts/useCursorAccountView.js.map +1 -0
- package/cursor-accounts/useCursorAccounts.d.ts +33 -0
- package/cursor-accounts/useCursorAccounts.d.ts.map +1 -0
- package/cursor-accounts/useCursorAccounts.js +25 -0
- package/cursor-accounts/useCursorAccounts.js.map +1 -0
- package/cursor-accounts/useCursorMemberKeyActions.d.ts +49 -0
- package/cursor-accounts/useCursorMemberKeyActions.d.ts.map +1 -0
- package/cursor-accounts/useCursorMemberKeyActions.js +38 -0
- package/cursor-accounts/useCursorMemberKeyActions.js.map +1 -0
- package/cursor-accounts/useDeleteCursorAccount.d.ts +30 -0
- package/cursor-accounts/useDeleteCursorAccount.d.ts.map +1 -0
- package/cursor-accounts/useDeleteCursorAccount.js +32 -0
- package/cursor-accounts/useDeleteCursorAccount.js.map +1 -0
- package/cursor-accounts/useSyncCursorAccount.d.ts +23 -0
- package/cursor-accounts/useSyncCursorAccount.d.ts.map +1 -0
- package/cursor-accounts/useSyncCursorAccount.js +31 -0
- package/cursor-accounts/useSyncCursorAccount.js.map +1 -0
- package/cursor-accounts/useUpsertCursorAccount.d.ts +26 -0
- package/cursor-accounts/useUpsertCursorAccount.d.ts.map +1 -0
- package/cursor-accounts/useUpsertCursorAccount.js +34 -0
- package/cursor-accounts/useUpsertCursorAccount.js.map +1 -0
- package/datastore/CollectionRecordsBrowser.d.ts +34 -0
- package/datastore/CollectionRecordsBrowser.d.ts.map +1 -0
- package/datastore/CollectionRecordsBrowser.js +223 -0
- package/datastore/CollectionRecordsBrowser.js.map +1 -0
- package/datastore/CollectionSchemaView.d.ts +29 -0
- package/datastore/CollectionSchemaView.d.ts.map +1 -0
- package/datastore/CollectionSchemaView.js +93 -0
- package/datastore/CollectionSchemaView.js.map +1 -0
- package/datastore/DatastoreDetailView.d.ts +44 -0
- package/datastore/DatastoreDetailView.d.ts.map +1 -0
- package/datastore/DatastoreDetailView.js +76 -0
- package/datastore/DatastoreDetailView.js.map +1 -0
- package/datastore/DatastoreSyncReport.d.ts +19 -0
- package/datastore/DatastoreSyncReport.d.ts.map +1 -0
- package/datastore/DatastoreSyncReport.js +45 -0
- package/datastore/DatastoreSyncReport.js.map +1 -0
- package/datastore/DeleteDatastoreDialog.d.ts +35 -0
- package/datastore/DeleteDatastoreDialog.d.ts.map +1 -0
- package/datastore/DeleteDatastoreDialog.js +90 -0
- package/datastore/DeleteDatastoreDialog.js.map +1 -0
- package/datastore/FieldValueControl.d.ts +55 -0
- package/datastore/FieldValueControl.d.ts.map +1 -0
- package/datastore/FieldValueControl.js +138 -0
- package/datastore/FieldValueControl.js.map +1 -0
- package/datastore/RecordFilterBuilder.d.ts +37 -0
- package/datastore/RecordFilterBuilder.d.ts.map +1 -0
- package/datastore/RecordFilterBuilder.js +122 -0
- package/datastore/RecordFilterBuilder.js.map +1 -0
- package/datastore/RecordFormPanel.d.ts +45 -0
- package/datastore/RecordFormPanel.d.ts.map +1 -0
- package/datastore/RecordFormPanel.js +215 -0
- package/datastore/RecordFormPanel.js.map +1 -0
- package/datastore/index.d.ts +43 -0
- package/datastore/index.d.ts.map +1 -0
- package/datastore/index.js +22 -0
- package/datastore/index.js.map +1 -0
- package/datastore/recordFilter.d.ts +62 -0
- package/datastore/recordFilter.d.ts.map +1 -0
- package/datastore/recordFilter.js +104 -0
- package/datastore/recordFilter.js.map +1 -0
- package/datastore/recordValues.d.ts +85 -0
- package/datastore/recordValues.d.ts.map +1 -0
- package/datastore/recordValues.js +282 -0
- package/datastore/recordValues.js.map +1 -0
- package/datastore/useDatastore.d.ts +39 -0
- package/datastore/useDatastore.d.ts.map +1 -0
- package/datastore/useDatastore.js +44 -0
- package/datastore/useDatastore.js.map +1 -0
- package/datastore/useDatastoreCount.d.ts +48 -0
- package/datastore/useDatastoreCount.d.ts.map +1 -0
- package/datastore/useDatastoreCount.js +27 -0
- package/datastore/useDatastoreCount.js.map +1 -0
- package/datastore/useDatastoreDescription.d.ts +54 -0
- package/datastore/useDatastoreDescription.d.ts.map +1 -0
- package/datastore/useDatastoreDescription.js +50 -0
- package/datastore/useDatastoreDescription.js.map +1 -0
- package/datastore/useDatastoreList.d.ts +57 -0
- package/datastore/useDatastoreList.d.ts.map +1 -0
- package/datastore/useDatastoreList.js +29 -0
- package/datastore/useDatastoreList.js.map +1 -0
- package/datastore/useDatastoreSearch.d.ts +19 -0
- package/datastore/useDatastoreSearch.d.ts.map +1 -0
- package/datastore/useDatastoreSearch.js +22 -0
- package/datastore/useDatastoreSearch.js.map +1 -0
- package/datastore/useDeleteDatastore.d.ts +53 -0
- package/datastore/useDeleteDatastore.d.ts.map +1 -0
- package/datastore/useDeleteDatastore.js +40 -0
- package/datastore/useDeleteDatastore.js.map +1 -0
- package/datastore/useDeleteRecord.d.ts +34 -0
- package/datastore/useDeleteRecord.d.ts.map +1 -0
- package/datastore/useDeleteRecord.js +43 -0
- package/datastore/useDeleteRecord.js.map +1 -0
- package/datastore/useInsertRecord.d.ts +50 -0
- package/datastore/useInsertRecord.d.ts.map +1 -0
- package/datastore/useInsertRecord.js +55 -0
- package/datastore/useInsertRecord.js.map +1 -0
- package/datastore/useRecordCollection.d.ts +104 -0
- package/datastore/useRecordCollection.d.ts.map +1 -0
- package/datastore/useRecordCollection.js +136 -0
- package/datastore/useRecordCollection.js.map +1 -0
- package/datastore/useRecordList.d.ts +66 -0
- package/datastore/useRecordList.d.ts.map +1 -0
- package/datastore/useRecordList.js +58 -0
- package/datastore/useRecordList.js.map +1 -0
- package/datastore/useUpdateRecord.d.ts +52 -0
- package/datastore/useUpdateRecord.d.ts.map +1 -0
- package/datastore/useUpdateRecord.js +55 -0
- package/datastore/useUpdateRecord.js.map +1 -0
- package/environment/EnvironmentListPanel.d.ts.map +1 -1
- package/environment/EnvironmentListPanel.js +12 -7
- package/environment/EnvironmentListPanel.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/iam-policy/index.d.ts +1 -1
- package/iam-policy/index.d.ts.map +1 -1
- package/iam-policy/index.js.map +1 -1
- package/iam-policy/useCheckPermission.d.ts +32 -11
- package/iam-policy/useCheckPermission.d.ts.map +1 -1
- package/iam-policy/useCheckPermission.js +24 -19
- package/iam-policy/useCheckPermission.js.map +1 -1
- package/index.d.ts +12 -4
- package/index.d.ts.map +1 -1
- package/index.js +18 -3
- package/index.js.map +1 -1
- package/internal/form-primitives.d.ts +10 -0
- package/internal/form-primitives.d.ts.map +1 -0
- package/internal/form-primitives.js +15 -0
- package/internal/form-primitives.js.map +1 -0
- package/library/index.d.ts +0 -7
- package/library/index.d.ts.map +1 -1
- package/library/index.js +0 -4
- package/library/index.js.map +1 -1
- package/library/useApplyResource.d.ts +12 -12
- package/library/useApplyResource.d.ts.map +1 -1
- package/library/useApplyResource.js +16 -25
- package/library/useApplyResource.js.map +1 -1
- package/library/useExportResource.d.ts +13 -9
- package/library/useExportResource.d.ts.map +1 -1
- package/library/useExportResource.js +18 -36
- package/library/useExportResource.js.map +1 -1
- package/manifest/ApplyManifestDialog.d.ts +48 -0
- package/manifest/ApplyManifestDialog.d.ts.map +1 -0
- package/manifest/ApplyManifestDialog.js +135 -0
- package/manifest/ApplyManifestDialog.js.map +1 -0
- package/manifest/EditResourceYamlDialog.d.ts +51 -0
- package/manifest/EditResourceYamlDialog.d.ts.map +1 -0
- package/manifest/EditResourceYamlDialog.js +104 -0
- package/manifest/EditResourceYamlDialog.js.map +1 -0
- package/manifest/RedactedSecretsNotice.d.ts +14 -0
- package/manifest/RedactedSecretsNotice.d.ts.map +1 -0
- package/manifest/RedactedSecretsNotice.js +14 -0
- package/manifest/RedactedSecretsNotice.js.map +1 -0
- package/manifest/YamlEditor.d.ts +44 -0
- package/manifest/YamlEditor.d.ts.map +1 -0
- package/manifest/YamlEditor.js +216 -0
- package/manifest/YamlEditor.js.map +1 -0
- package/manifest/index.d.ts +13 -0
- package/manifest/index.d.ts.map +1 -0
- package/manifest/index.js +13 -0
- package/manifest/index.js.map +1 -0
- package/manifest/useApplyManifest.d.ts +82 -0
- package/manifest/useApplyManifest.d.ts.map +1 -0
- package/manifest/useApplyManifest.js +180 -0
- package/manifest/useApplyManifest.js.map +1 -0
- package/manifest/useEditResourceYaml.d.ts +106 -0
- package/manifest/useEditResourceYaml.d.ts.map +1 -0
- package/manifest/useEditResourceYaml.js +168 -0
- package/manifest/useEditResourceYaml.js.map +1 -0
- package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
- package/mcp-server/steps/ReviewStep.js +2 -3
- package/mcp-server/steps/ReviewStep.js.map +1 -1
- package/package.json +4 -4
- package/pricing-governance/BaselineEditor.d.ts +9 -0
- package/pricing-governance/BaselineEditor.d.ts.map +1 -0
- package/pricing-governance/BaselineEditor.js +270 -0
- package/pricing-governance/BaselineEditor.js.map +1 -0
- package/pricing-governance/ModelCatalogPanel.d.ts +28 -0
- package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -0
- package/pricing-governance/ModelCatalogPanel.js +102 -0
- package/pricing-governance/ModelCatalogPanel.js.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.d.ts +8 -0
- package/pricing-governance/ModelGovernanceDetail.d.ts.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.js +64 -0
- package/pricing-governance/ModelGovernanceDetail.js.map +1 -0
- package/pricing-governance/OperatorAccessNotice.d.ts +12 -0
- package/pricing-governance/OperatorAccessNotice.d.ts.map +1 -0
- package/pricing-governance/OperatorAccessNotice.js +15 -0
- package/pricing-governance/OperatorAccessNotice.js.map +1 -0
- package/pricing-governance/PricingGovernanceConsole.d.ts +57 -0
- package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -0
- package/pricing-governance/PricingGovernanceConsole.js +164 -0
- package/pricing-governance/PricingGovernanceConsole.js.map +1 -0
- package/pricing-governance/PricingGovernancePanel.d.ts +26 -0
- package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -0
- package/pricing-governance/PricingGovernancePanel.js +64 -0
- package/pricing-governance/PricingGovernancePanel.js.map +1 -0
- package/pricing-governance/RetireConfirm.d.ts +9 -0
- package/pricing-governance/RetireConfirm.d.ts.map +1 -0
- package/pricing-governance/RetireConfirm.js +19 -0
- package/pricing-governance/RetireConfirm.js.map +1 -0
- package/pricing-governance/governance-primitives.d.ts +33 -0
- package/pricing-governance/governance-primitives.d.ts.map +1 -0
- package/pricing-governance/governance-primitives.js +43 -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 +12 -0
- package/pricing-governance/pricing-format.d.ts.map +1 -0
- package/pricing-governance/pricing-format.js +50 -0
- package/pricing-governance/pricing-format.js.map +1 -0
- package/pricing-governance/useDecidePricingOverride.d.ts +46 -0
- package/pricing-governance/useDecidePricingOverride.d.ts.map +1 -0
- package/pricing-governance/useDecidePricingOverride.js +44 -0
- package/pricing-governance/useDecidePricingOverride.js.map +1 -0
- package/pricing-governance/useModelGovernanceView.d.ts +103 -0
- package/pricing-governance/useModelGovernanceView.d.ts.map +1 -0
- package/pricing-governance/useModelGovernanceView.js +144 -0
- package/pricing-governance/useModelGovernanceView.js.map +1 -0
- package/pricing-governance/useModelPricingBaselines.d.ts +40 -0
- package/pricing-governance/useModelPricingBaselines.d.ts.map +1 -0
- package/pricing-governance/useModelPricingBaselines.js +32 -0
- package/pricing-governance/useModelPricingBaselines.js.map +1 -0
- package/pricing-governance/usePricingGovernance.d.ts +39 -0
- package/pricing-governance/usePricingGovernance.d.ts.map +1 -0
- package/pricing-governance/usePricingGovernance.js +31 -0
- package/pricing-governance/usePricingGovernance.js.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.d.ts +31 -0
- package/pricing-governance/useRetireModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.js +36 -0
- package/pricing-governance/useRetireModelPricingBaseline.js.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.d.ts +38 -0
- package/pricing-governance/useUpsertModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.js +37 -0
- package/pricing-governance/useUpsertModelPricingBaseline.js.map +1 -0
- package/resource-creation/templates/workflow-templates.js +4 -4
- 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/useEditSessionPrep.js +3 -3
- package/session/useEditSessionPrep.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/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/settings/index.d.ts +2 -1
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +2 -1
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.d.ts +22 -0
- package/settings/settings-nav.d.ts.map +1 -1
- package/settings/settings-nav.js +32 -1
- package/settings/settings-nav.js.map +1 -1
- package/settings/useSettingsNavGroups.d.ts +29 -0
- package/settings/useSettingsNavGroups.d.ts.map +1 -0
- package/settings/useSettingsNavGroups.js +61 -0
- package/settings/useSettingsNavGroups.js.map +1 -0
- package/src/agent/steps/ReviewStep.tsx +2 -3
- package/src/billing/index.ts +5 -0
- package/src/channel/AgentChannelsPanel.tsx +27 -1
- package/src/composer/SessionComposer.tsx +1 -1
- package/src/cursor-accounts/CursorAccountEditor.tsx +229 -0
- package/src/cursor-accounts/CursorAccountsAccessNotice.tsx +36 -0
- package/src/cursor-accounts/CursorAccountsConsole.tsx +454 -0
- package/src/cursor-accounts/MemberCoverageTable.tsx +439 -0
- package/src/cursor-accounts/MemberKeysPanel.tsx +278 -0
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +680 -0
- 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 +45 -0
- package/src/cursor-accounts/index.ts +29 -0
- package/src/cursor-accounts/useCursorAccountView.ts +49 -0
- package/src/cursor-accounts/useCursorAccounts.ts +49 -0
- package/src/cursor-accounts/useCursorMemberKeyActions.ts +106 -0
- package/src/cursor-accounts/useDeleteCursorAccount.ts +62 -0
- package/src/cursor-accounts/useSyncCursorAccount.ts +54 -0
- package/src/cursor-accounts/useUpsertCursorAccount.ts +57 -0
- package/src/datastore/CollectionRecordsBrowser.tsx +570 -0
- package/src/datastore/CollectionSchemaView.tsx +247 -0
- package/src/datastore/DatastoreDetailView.tsx +201 -0
- package/src/datastore/DatastoreSyncReport.tsx +105 -0
- package/src/datastore/DeleteDatastoreDialog.tsx +202 -0
- package/src/datastore/FieldValueControl.tsx +289 -0
- package/src/datastore/RecordFilterBuilder.tsx +422 -0
- package/src/datastore/RecordFormPanel.tsx +432 -0
- package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +245 -0
- package/src/datastore/__tests__/DeleteDatastoreDialog.test.tsx +133 -0
- package/src/datastore/__tests__/FieldValueControl.test.tsx +160 -0
- package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +155 -0
- package/src/datastore/__tests__/RecordFormPanel.test.tsx +227 -0
- package/src/datastore/__tests__/recordFilter.test.ts +169 -0
- package/src/datastore/__tests__/recordMutations.test.tsx +195 -0
- package/src/datastore/__tests__/recordValues.test.ts +359 -0
- package/src/datastore/__tests__/schemaAndSyncViews.test.tsx +142 -0
- package/src/datastore/__tests__/useDatastoreDescription.test.tsx +143 -0
- package/src/datastore/__tests__/useRecordCollection.test.tsx +250 -0
- package/src/datastore/__tests__/useRecordList.test.tsx +85 -0
- package/src/datastore/index.ts +111 -0
- package/src/datastore/recordFilter.ts +154 -0
- package/src/datastore/recordValues.ts +327 -0
- package/src/datastore/useDatastore.ts +67 -0
- package/src/datastore/useDatastoreCount.ts +67 -0
- package/src/datastore/useDatastoreDescription.ts +90 -0
- package/src/datastore/useDatastoreList.ts +79 -0
- package/src/datastore/useDatastoreSearch.ts +39 -0
- package/src/datastore/useDeleteDatastore.ts +87 -0
- package/src/datastore/useDeleteRecord.ts +77 -0
- package/src/datastore/useInsertRecord.ts +92 -0
- package/src/datastore/useRecordCollection.ts +256 -0
- package/src/datastore/useRecordList.ts +131 -0
- package/src/datastore/useUpdateRecord.ts +95 -0
- package/src/environment/EnvironmentListPanel.tsx +24 -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__/useArtifactInspection.test.tsx +12 -2
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +86 -0
- package/src/execution/useCreateAgentExecution.ts +41 -6
- package/src/iam-policy/__tests__/useCheckPermission.test.tsx +221 -0
- package/src/iam-policy/index.ts +1 -0
- package/src/iam-policy/useCheckPermission.ts +45 -19
- package/src/index.ts +172 -14
- package/src/internal/form-primitives.tsx +40 -0
- package/src/library/index.ts +0 -20
- package/src/library/useApplyResource.ts +27 -34
- package/src/library/useExportResource.ts +21 -40
- package/src/manifest/ApplyManifestDialog.tsx +315 -0
- package/src/manifest/EditResourceYamlDialog.tsx +265 -0
- package/src/manifest/RedactedSecretsNotice.tsx +30 -0
- package/src/manifest/YamlEditor.tsx +272 -0
- package/src/manifest/__tests__/EditResourceYamlDialog.test.tsx +118 -0
- package/src/manifest/__tests__/useApplyManifest.test.tsx +237 -0
- package/src/manifest/__tests__/useEditResourceYaml.test.tsx +252 -0
- package/src/manifest/index.ts +33 -0
- package/src/manifest/useApplyManifest.ts +270 -0
- package/src/manifest/useEditResourceYaml.ts +273 -0
- package/src/mcp-server/steps/ReviewStep.tsx +2 -3
- package/src/pricing-governance/BaselineEditor.tsx +652 -0
- package/src/pricing-governance/ModelCatalogPanel.tsx +283 -0
- package/src/pricing-governance/ModelGovernanceDetail.tsx +289 -0
- package/src/pricing-governance/OperatorAccessNotice.tsx +32 -0
- package/src/pricing-governance/PricingGovernanceConsole.tsx +465 -0
- package/src/pricing-governance/PricingGovernancePanel.tsx +166 -0
- package/src/pricing-governance/RetireConfirm.tsx +82 -0
- package/src/pricing-governance/__tests__/ModelCatalogPanel.test.tsx +253 -0
- package/src/pricing-governance/__tests__/PricingGovernanceConsole.test.tsx +410 -0
- package/src/pricing-governance/__tests__/useModelGovernanceView.test.tsx +308 -0
- package/src/pricing-governance/governance-primitives.tsx +133 -0
- package/src/pricing-governance/index.ts +50 -0
- package/src/pricing-governance/pricing-format.ts +53 -0
- package/src/pricing-governance/useDecidePricingOverride.ts +78 -0
- package/src/pricing-governance/useModelGovernanceView.ts +290 -0
- package/src/pricing-governance/useModelPricingBaselines.ts +69 -0
- package/src/pricing-governance/usePricingGovernance.ts +57 -0
- package/src/pricing-governance/useRetireModelPricingBaseline.ts +65 -0
- package/src/pricing-governance/useUpsertModelPricingBaseline.ts +72 -0
- package/src/resource-creation/templates/workflow-templates.ts +4 -4
- package/src/session/__tests__/useCreateSession.test.tsx +94 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
- package/src/session/useCreateSession.ts +30 -0
- package/src/session/useEditSessionPrep.ts +3 -3
- package/src/session/useNewSessionFlow.ts +45 -1
- package/src/session/useUpdateSession.ts +29 -10
- package/src/settings/__tests__/useSettingsNavGroups.test.tsx +138 -0
- package/src/settings/index.ts +5 -1
- package/src/settings/settings-nav.ts +43 -0
- package/src/settings/useSettingsNavGroups.ts +81 -0
- package/src/workflow/WorkflowYamlEditor.tsx +6 -270
- package/styles.css +1 -1
- package/workflow/CanvasTransitionEdge.d.ts +1 -1
- package/workflow/WorkflowYamlEditor.d.ts +2 -42
- package/workflow/WorkflowYamlEditor.d.ts.map +1 -1
- package/workflow/WorkflowYamlEditor.js +5 -214
- package/workflow/WorkflowYamlEditor.js.map +1 -1
- package/library/ImportResourceDialog.d.ts +0 -43
- package/library/ImportResourceDialog.d.ts.map +0 -1
- package/library/ImportResourceDialog.js +0 -90
- package/library/ImportResourceDialog.js.map +0 -1
- package/library/parse-resource-yaml.d.ts +0 -63
- package/library/parse-resource-yaml.d.ts.map +0 -1
- package/library/parse-resource-yaml.js +0 -407
- package/library/parse-resource-yaml.js.map +0 -1
- package/library/serialize-resource-yaml.d.ts +0 -103
- package/library/serialize-resource-yaml.d.ts.map +0 -1
- package/library/serialize-resource-yaml.js +0 -565
- package/library/serialize-resource-yaml.js.map +0 -1
- package/library/useImportResource.d.ts +0 -84
- package/library/useImportResource.d.ts.map +0 -1
- package/library/useImportResource.js +0 -188
- package/library/useImportResource.js.map +0 -1
- package/src/library/ImportResourceDialog.tsx +0 -227
- package/src/library/parse-resource-yaml.ts +0 -630
- package/src/library/serialize-resource-yaml.ts +0 -756
- package/src/library/useImportResource.ts +0 -253
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import { create } from "@bufbuild/protobuf";
|
|
6
|
+
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
|
|
7
|
+
import { StigmerError } from "@stigmer/sdk";
|
|
8
|
+
import {
|
|
9
|
+
CursorAccountSchema,
|
|
10
|
+
CursorMemberKeySchema,
|
|
11
|
+
CursorTeamMemberSchema,
|
|
12
|
+
CursorMemberSpendSchema,
|
|
13
|
+
CursorAccountSyncSnapshotSchema,
|
|
14
|
+
} from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/cursor_account_pb";
|
|
15
|
+
import {
|
|
16
|
+
CursorAccountSummarySchema,
|
|
17
|
+
CursorAccountViewSchema,
|
|
18
|
+
CursorMemberKeyViewSchema,
|
|
19
|
+
CursorMemberKeyState,
|
|
20
|
+
CursorTeamMemberViewSchema,
|
|
21
|
+
} from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/io_pb";
|
|
22
|
+
import { StigmerContext } from "../../context";
|
|
23
|
+
import { FetchCacheContext } from "../../internal/FetchCacheProvider";
|
|
24
|
+
import { CursorAccountsConsole } from "../CursorAccountsConsole";
|
|
25
|
+
|
|
26
|
+
interface MockCursorAccounts {
|
|
27
|
+
listAccounts: ReturnType<typeof vi.fn>;
|
|
28
|
+
getAccountView: ReturnType<typeof vi.fn>;
|
|
29
|
+
upsertAccount: ReturnType<typeof vi.fn>;
|
|
30
|
+
deleteAccount: ReturnType<typeof vi.fn>;
|
|
31
|
+
addMemberKey: ReturnType<typeof vi.fn>;
|
|
32
|
+
removeMemberKey: ReturnType<typeof vi.fn>;
|
|
33
|
+
setMemberKeyEnabled: ReturnType<typeof vi.fn>;
|
|
34
|
+
syncAccount: ReturnType<typeof vi.fn>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createMockStigmer(cursorAccounts: Partial<MockCursorAccounts> = {}) {
|
|
38
|
+
return {
|
|
39
|
+
cursorAccounts: {
|
|
40
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [] }),
|
|
41
|
+
getAccountView: vi.fn(),
|
|
42
|
+
upsertAccount: vi.fn(),
|
|
43
|
+
deleteAccount: vi.fn(),
|
|
44
|
+
addMemberKey: vi.fn(),
|
|
45
|
+
removeMemberKey: vi.fn(),
|
|
46
|
+
setMemberKeyEnabled: vi.fn(),
|
|
47
|
+
syncAccount: vi.fn(),
|
|
48
|
+
...cursorAccounts,
|
|
49
|
+
},
|
|
50
|
+
} as never;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function wrapper(client: unknown) {
|
|
54
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
55
|
+
return (
|
|
56
|
+
<FetchCacheContext.Provider value={null}>
|
|
57
|
+
<StigmerContext.Provider value={client as never}>
|
|
58
|
+
{children}
|
|
59
|
+
</StigmerContext.Provider>
|
|
60
|
+
</FetchCacheContext.Provider>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function scenarAccount(overrides: Record<string, unknown> = {}) {
|
|
66
|
+
return create(CursorAccountSchema, {
|
|
67
|
+
accountId: "acc-1",
|
|
68
|
+
displayName: "scenar team",
|
|
69
|
+
adminApiKey: "***REDACTED***",
|
|
70
|
+
enabled: true,
|
|
71
|
+
orgIds: ["org-a"],
|
|
72
|
+
memberKeys: [
|
|
73
|
+
create(CursorMemberKeySchema, {
|
|
74
|
+
keyId: "k-1",
|
|
75
|
+
apiKey: "***REDACTED***",
|
|
76
|
+
boundEmail: "zane@scenar.ai",
|
|
77
|
+
cursorKeyName: "stigmer-prod",
|
|
78
|
+
enabled: true,
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
...overrides,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function scenarSummary() {
|
|
86
|
+
return create(CursorAccountSummarySchema, {
|
|
87
|
+
account: scenarAccount(),
|
|
88
|
+
enabledKeyCount: 1,
|
|
89
|
+
lastSyncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function scenarView() {
|
|
94
|
+
return create(CursorAccountViewSchema, {
|
|
95
|
+
account: scenarAccount(),
|
|
96
|
+
snapshot: create(CursorAccountSyncSnapshotSchema, {
|
|
97
|
+
accountId: "acc-1",
|
|
98
|
+
syncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
99
|
+
members: [
|
|
100
|
+
create(CursorTeamMemberSchema, {
|
|
101
|
+
userId: "u1",
|
|
102
|
+
email: "zane@scenar.ai",
|
|
103
|
+
name: "Zane S",
|
|
104
|
+
role: "owner",
|
|
105
|
+
}),
|
|
106
|
+
create(CursorTeamMemberSchema, {
|
|
107
|
+
userId: "u2",
|
|
108
|
+
email: "uncovered@scenar.ai",
|
|
109
|
+
name: "Uncovered U",
|
|
110
|
+
role: "member",
|
|
111
|
+
}),
|
|
112
|
+
// Cursor marks departed seats role:"removed" in place; they stay
|
|
113
|
+
// in the roster snapshot but never in the coverage categories.
|
|
114
|
+
create(CursorTeamMemberSchema, {
|
|
115
|
+
userId: "u3",
|
|
116
|
+
email: "departed@scenar.ai",
|
|
117
|
+
name: "Departed D",
|
|
118
|
+
role: "removed",
|
|
119
|
+
}),
|
|
120
|
+
],
|
|
121
|
+
spend: [
|
|
122
|
+
create(CursorMemberSpendSchema, {
|
|
123
|
+
userId: "u1",
|
|
124
|
+
email: "zane@scenar.ai",
|
|
125
|
+
includedSpendUsdMicros: 169342n,
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
}),
|
|
129
|
+
keyViews: [
|
|
130
|
+
create(CursorMemberKeyViewSchema, {
|
|
131
|
+
key: scenarAccount().memberKeys[0],
|
|
132
|
+
state: CursorMemberKeyState.member_key_active,
|
|
133
|
+
spend: create(CursorMemberSpendSchema, {
|
|
134
|
+
userId: "u1",
|
|
135
|
+
email: "zane@scenar.ai",
|
|
136
|
+
includedSpendUsdMicros: 169342n,
|
|
137
|
+
}),
|
|
138
|
+
}),
|
|
139
|
+
],
|
|
140
|
+
// The server populates both the deprecated flat list and the
|
|
141
|
+
// spend-joined views; the console reads only the views.
|
|
142
|
+
membersWithoutKeys: [
|
|
143
|
+
create(CursorTeamMemberSchema, {
|
|
144
|
+
userId: "u2",
|
|
145
|
+
email: "uncovered@scenar.ai",
|
|
146
|
+
name: "Uncovered U",
|
|
147
|
+
role: "member",
|
|
148
|
+
}),
|
|
149
|
+
],
|
|
150
|
+
membersWithoutKeysViews: [
|
|
151
|
+
create(CursorTeamMemberViewSchema, {
|
|
152
|
+
member: create(CursorTeamMemberSchema, {
|
|
153
|
+
userId: "u2",
|
|
154
|
+
email: "uncovered@scenar.ai",
|
|
155
|
+
name: "Uncovered U",
|
|
156
|
+
role: "member",
|
|
157
|
+
}),
|
|
158
|
+
spend: create(CursorMemberSpendSchema, {
|
|
159
|
+
userId: "u2",
|
|
160
|
+
email: "uncovered@scenar.ai",
|
|
161
|
+
includedSpendUsdMicros: 42000n,
|
|
162
|
+
}),
|
|
163
|
+
}),
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
afterEach(() => {
|
|
169
|
+
cleanup();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("CursorAccountsConsole", () => {
|
|
173
|
+
it("renders the empty state with onboarding guidance", async () => {
|
|
174
|
+
const client = createMockStigmer();
|
|
175
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
176
|
+
|
|
177
|
+
await waitFor(() =>
|
|
178
|
+
expect(screen.getByText(/No Cursor accounts yet/)).toBeTruthy(),
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("lists accounts with routability and the derived pool class at a glance", async () => {
|
|
183
|
+
const notRoutable = create(CursorAccountSummarySchema, {
|
|
184
|
+
account: scenarAccount({
|
|
185
|
+
accountId: "acc-2",
|
|
186
|
+
displayName: "empty team",
|
|
187
|
+
memberKeys: [],
|
|
188
|
+
// No org assignment = shared-pool account (DD-008): the class is
|
|
189
|
+
// derived from org_ids, never from the deprecated default flag.
|
|
190
|
+
orgIds: [],
|
|
191
|
+
}),
|
|
192
|
+
enabledKeyCount: 0,
|
|
193
|
+
});
|
|
194
|
+
const client = createMockStigmer({
|
|
195
|
+
listAccounts: vi
|
|
196
|
+
.fn()
|
|
197
|
+
.mockResolvedValue({ accounts: [scenarSummary(), notRoutable] }),
|
|
198
|
+
});
|
|
199
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
200
|
+
|
|
201
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
202
|
+
expect(screen.getByText("empty team")).toBeTruthy();
|
|
203
|
+
expect(screen.getByText("Not routable")).toBeTruthy();
|
|
204
|
+
expect(screen.getByText("shared pool")).toBeTruthy();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("shows the designed access notice on PERMISSION_DENIED", async () => {
|
|
208
|
+
const client = createMockStigmer({
|
|
209
|
+
listAccounts: vi
|
|
210
|
+
.fn()
|
|
211
|
+
.mockRejectedValue(
|
|
212
|
+
new StigmerError("permission-denied", "only platform operators", 7),
|
|
213
|
+
),
|
|
214
|
+
});
|
|
215
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
216
|
+
|
|
217
|
+
await waitFor(() =>
|
|
218
|
+
expect(screen.getByText("Platform operator access required")).toBeTruthy(),
|
|
219
|
+
);
|
|
220
|
+
expect(screen.queryByText(/only platform operators/)).toBeNull();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("opens the detail view with the coverage table: covered and gap rows in their groups", async () => {
|
|
224
|
+
const client = createMockStigmer({
|
|
225
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
226
|
+
getAccountView: vi.fn().mockResolvedValue(scenarView()),
|
|
227
|
+
});
|
|
228
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
229
|
+
|
|
230
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
231
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
232
|
+
|
|
233
|
+
await waitFor(() =>
|
|
234
|
+
expect(screen.getByRole("table", { name: "Team coverage" })).toBeTruthy(),
|
|
235
|
+
);
|
|
236
|
+
// Category 1: on the team, key held — email, key name, spend, status.
|
|
237
|
+
expect(screen.getByText(/On the team — key held/)).toBeTruthy();
|
|
238
|
+
expect(screen.getByText("zane@scenar.ai")).toBeTruthy();
|
|
239
|
+
expect(screen.getByText("stigmer-prod")).toBeTruthy();
|
|
240
|
+
// The email is the row's identity: full value always hoverable on
|
|
241
|
+
// both row kinds (the cell wraps rather than truncates).
|
|
242
|
+
expect(screen.getByTitle("zane@scenar.ai")).toBeTruthy();
|
|
243
|
+
expect(screen.getByTitle("uncovered@scenar.ai")).toBeTruthy();
|
|
244
|
+
expect(screen.getByText("$0.17")).toBeTruthy(); // included, 169342 micro-USD
|
|
245
|
+
expect(screen.getByText("Active")).toBeTruthy();
|
|
246
|
+
// Category 2: on the team, no key — server-joined spend on the gap row.
|
|
247
|
+
expect(screen.getByText(/On the team — no execution key/)).toBeTruthy();
|
|
248
|
+
expect(screen.getByText("uncovered@scenar.ai")).toBeTruthy();
|
|
249
|
+
expect(screen.getByText("$0.04")).toBeTruthy(); // included, 42000 micro-USD
|
|
250
|
+
expect(screen.getByText("No key")).toBeTruthy();
|
|
251
|
+
// Both rows carry all four numeric columns: zero on-demand renders as
|
|
252
|
+
// a real dollar amount and zero pool percents as "0%" (matching
|
|
253
|
+
// Cursor's dashboard), never as em-dashes — those mean "no spend row".
|
|
254
|
+
expect(screen.getAllByText("$0.00")).toHaveLength(2); // on-demand × 2 rows
|
|
255
|
+
expect(screen.getAllByText("0%")).toHaveLength(4); // first-party + API × 2 rows
|
|
256
|
+
// Removed roster entries never render as coverage rows.
|
|
257
|
+
expect(screen.queryByText("departed@scenar.ai")).toBeNull();
|
|
258
|
+
// Header sync line: active members from server facts, removed seats
|
|
259
|
+
// by list arithmetic (roster entries minus active members).
|
|
260
|
+
expect(screen.getByText(/2 members · 1 removed seat/)).toBeTruthy();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("affirms full coverage instead of hiding the gap group when every active member holds a key", async () => {
|
|
264
|
+
const coveredView = create(CursorAccountViewSchema, {
|
|
265
|
+
account: scenarAccount(),
|
|
266
|
+
snapshot: create(CursorAccountSyncSnapshotSchema, {
|
|
267
|
+
accountId: "acc-1",
|
|
268
|
+
syncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
269
|
+
members: [
|
|
270
|
+
create(CursorTeamMemberSchema, {
|
|
271
|
+
userId: "u1",
|
|
272
|
+
email: "zane@scenar.ai",
|
|
273
|
+
name: "Zane S",
|
|
274
|
+
role: "owner",
|
|
275
|
+
}),
|
|
276
|
+
],
|
|
277
|
+
}),
|
|
278
|
+
keyViews: [
|
|
279
|
+
create(CursorMemberKeyViewSchema, {
|
|
280
|
+
key: scenarAccount().memberKeys[0],
|
|
281
|
+
state: CursorMemberKeyState.member_key_active,
|
|
282
|
+
}),
|
|
283
|
+
],
|
|
284
|
+
// A fully covered roster: the server sends no gap views.
|
|
285
|
+
});
|
|
286
|
+
const client = createMockStigmer({
|
|
287
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
288
|
+
getAccountView: vi.fn().mockResolvedValue(coveredView),
|
|
289
|
+
});
|
|
290
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
291
|
+
|
|
292
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
293
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
294
|
+
|
|
295
|
+
// The gap group renders with an affirmative answer, so "fully
|
|
296
|
+
// covered" is distinguishable from "the sync never classified".
|
|
297
|
+
await waitFor(() =>
|
|
298
|
+
expect(screen.getByText(/On the team — no execution key/)).toBeTruthy(),
|
|
299
|
+
);
|
|
300
|
+
expect(screen.getByText(/roster is fully covered/)).toBeTruthy();
|
|
301
|
+
// A member with no spend row at all renders em-dash cells.
|
|
302
|
+
expect(screen.getAllByText("—").length).toBeGreaterThan(0);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("classifies off-team keys with invite guidance; copy button only with a configured link", async () => {
|
|
306
|
+
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
307
|
+
Object.defineProperty(window.navigator, "clipboard", {
|
|
308
|
+
value: { writeText },
|
|
309
|
+
configurable: true,
|
|
310
|
+
});
|
|
311
|
+
const offTeamView = create(CursorAccountViewSchema, {
|
|
312
|
+
account: scenarAccount({
|
|
313
|
+
teamInviteLink: "https://cursor.com/team-invite/abc",
|
|
314
|
+
}),
|
|
315
|
+
snapshot: create(CursorAccountSyncSnapshotSchema, {
|
|
316
|
+
accountId: "acc-1",
|
|
317
|
+
syncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
318
|
+
}),
|
|
319
|
+
keyViews: [
|
|
320
|
+
create(CursorMemberKeyViewSchema, {
|
|
321
|
+
key: create(CursorMemberKeySchema, {
|
|
322
|
+
keyId: "k-stranger",
|
|
323
|
+
apiKey: "***REDACTED***",
|
|
324
|
+
boundEmail: "stranger@else.where",
|
|
325
|
+
enabled: true,
|
|
326
|
+
}),
|
|
327
|
+
state: CursorMemberKeyState.member_key_owner_unknown,
|
|
328
|
+
}),
|
|
329
|
+
create(CursorMemberKeyViewSchema, {
|
|
330
|
+
key: create(CursorMemberKeySchema, {
|
|
331
|
+
keyId: "k-gone",
|
|
332
|
+
apiKey: "***REDACTED***",
|
|
333
|
+
boundEmail: "gone@scenar.ai",
|
|
334
|
+
enabled: true,
|
|
335
|
+
}),
|
|
336
|
+
state: CursorMemberKeyState.member_key_owner_removed,
|
|
337
|
+
}),
|
|
338
|
+
],
|
|
339
|
+
});
|
|
340
|
+
const client = createMockStigmer({
|
|
341
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
342
|
+
getAccountView: vi.fn().mockResolvedValue(offTeamView),
|
|
343
|
+
});
|
|
344
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
345
|
+
|
|
346
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
347
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
348
|
+
|
|
349
|
+
await waitFor(() =>
|
|
350
|
+
expect(screen.getByText(/Key held — not on the team/)).toBeTruthy(),
|
|
351
|
+
);
|
|
352
|
+
// The two off-team causes carry distinct labels.
|
|
353
|
+
expect(screen.getByText("Not on team")).toBeTruthy();
|
|
354
|
+
expect(screen.getByText("Left team")).toBeTruthy();
|
|
355
|
+
|
|
356
|
+
// One "Copy invite" per off-team row — a copy, never a navigation.
|
|
357
|
+
const copyButtons = screen.getAllByRole("button", { name: "Copy invite" });
|
|
358
|
+
expect(copyButtons).toHaveLength(2);
|
|
359
|
+
await userEvent.click(copyButtons[0]);
|
|
360
|
+
await waitFor(() =>
|
|
361
|
+
expect(writeText).toHaveBeenCalledWith("https://cursor.com/team-invite/abc"),
|
|
362
|
+
);
|
|
363
|
+
expect(screen.getByRole("button", { name: "Copied" })).toBeTruthy();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("points at the Cursor dashboard when no invite link is configured", async () => {
|
|
367
|
+
const offTeamView = create(CursorAccountViewSchema, {
|
|
368
|
+
account: scenarAccount(),
|
|
369
|
+
snapshot: create(CursorAccountSyncSnapshotSchema, {
|
|
370
|
+
accountId: "acc-1",
|
|
371
|
+
syncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
372
|
+
}),
|
|
373
|
+
keyViews: [
|
|
374
|
+
create(CursorMemberKeyViewSchema, {
|
|
375
|
+
key: create(CursorMemberKeySchema, {
|
|
376
|
+
keyId: "k-stranger",
|
|
377
|
+
apiKey: "***REDACTED***",
|
|
378
|
+
boundEmail: "stranger@else.where",
|
|
379
|
+
enabled: true,
|
|
380
|
+
}),
|
|
381
|
+
state: CursorMemberKeyState.member_key_owner_unknown,
|
|
382
|
+
}),
|
|
383
|
+
],
|
|
384
|
+
});
|
|
385
|
+
const client = createMockStigmer({
|
|
386
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
387
|
+
getAccountView: vi.fn().mockResolvedValue(offTeamView),
|
|
388
|
+
});
|
|
389
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
390
|
+
|
|
391
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
392
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
393
|
+
|
|
394
|
+
await waitFor(() =>
|
|
395
|
+
expect(screen.getByText(/Key held — not on the team/)).toBeTruthy(),
|
|
396
|
+
);
|
|
397
|
+
expect(screen.queryByRole("button", { name: "Copy invite" })).toBeNull();
|
|
398
|
+
expect(screen.getByText(/paste the team's invite link/)).toBeTruthy();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("withholds classification before the first sync instead of reporting keys off-team", async () => {
|
|
402
|
+
const unsyncedView = create(CursorAccountViewSchema, {
|
|
403
|
+
account: scenarAccount(),
|
|
404
|
+
keyViews: [
|
|
405
|
+
create(CursorMemberKeyViewSchema, {
|
|
406
|
+
key: scenarAccount().memberKeys[0],
|
|
407
|
+
state: CursorMemberKeyState.member_key_owner_unknown,
|
|
408
|
+
}),
|
|
409
|
+
],
|
|
410
|
+
});
|
|
411
|
+
const client = createMockStigmer({
|
|
412
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
413
|
+
getAccountView: vi.fn().mockResolvedValue(unsyncedView),
|
|
414
|
+
});
|
|
415
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
416
|
+
|
|
417
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
418
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
419
|
+
|
|
420
|
+
await waitFor(() =>
|
|
421
|
+
expect(screen.getByText(/not yet classified/)).toBeTruthy(),
|
|
422
|
+
);
|
|
423
|
+
expect(screen.getByText("Awaiting sync")).toBeTruthy();
|
|
424
|
+
expect(screen.queryByText(/Key held — not on the team/)).toBeNull();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("adds a member key and refreshes both views", async () => {
|
|
428
|
+
const addMemberKey = vi.fn().mockResolvedValue(scenarAccount());
|
|
429
|
+
const client = createMockStigmer({
|
|
430
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
431
|
+
getAccountView: vi.fn().mockResolvedValue(scenarView()),
|
|
432
|
+
addMemberKey,
|
|
433
|
+
});
|
|
434
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
435
|
+
|
|
436
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
437
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
438
|
+
await waitFor(() =>
|
|
439
|
+
expect(screen.getByLabelText(/User-scoped API key/)).toBeTruthy(),
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
await userEvent.type(
|
|
443
|
+
screen.getByLabelText(/User-scoped API key/),
|
|
444
|
+
"key_user_plain",
|
|
445
|
+
);
|
|
446
|
+
await userEvent.click(screen.getByRole("button", { name: "Add key" }));
|
|
447
|
+
|
|
448
|
+
await waitFor(() =>
|
|
449
|
+
expect(addMemberKey).toHaveBeenCalledWith({
|
|
450
|
+
accountId: "acc-1",
|
|
451
|
+
apiKey: "key_user_plain",
|
|
452
|
+
label: undefined,
|
|
453
|
+
}),
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("surfaces Cursor's own explanation when a wrong key class is added", async () => {
|
|
458
|
+
const client = createMockStigmer({
|
|
459
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
460
|
+
getAccountView: vi.fn().mockResolvedValue(scenarView()),
|
|
461
|
+
addMemberKey: vi
|
|
462
|
+
.fn()
|
|
463
|
+
.mockRejectedValue(
|
|
464
|
+
new StigmerError(
|
|
465
|
+
"invalid-argument",
|
|
466
|
+
"Key rejected by Cursor — This is a team API key from the dashboard's Team API Keys tab",
|
|
467
|
+
3,
|
|
468
|
+
),
|
|
469
|
+
),
|
|
470
|
+
});
|
|
471
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
472
|
+
|
|
473
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
474
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
475
|
+
await waitFor(() =>
|
|
476
|
+
expect(screen.getByLabelText(/User-scoped API key/)).toBeTruthy(),
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
await userEvent.type(screen.getByLabelText(/User-scoped API key/), "wrong-class");
|
|
480
|
+
await userEvent.click(screen.getByRole("button", { name: "Add key" }));
|
|
481
|
+
|
|
482
|
+
await waitFor(() =>
|
|
483
|
+
expect(screen.getByText(/Team API Keys tab/i)).toBeTruthy(),
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("creates an account through the editor", async () => {
|
|
488
|
+
const upsertAccount = vi.fn().mockResolvedValue(scenarAccount());
|
|
489
|
+
const client = createMockStigmer({
|
|
490
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [] }),
|
|
491
|
+
upsertAccount,
|
|
492
|
+
});
|
|
493
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
494
|
+
|
|
495
|
+
await waitFor(() =>
|
|
496
|
+
expect(screen.getByRole("button", { name: "Add account" })).toBeTruthy(),
|
|
497
|
+
);
|
|
498
|
+
await userEvent.click(screen.getByRole("button", { name: "Add account" }));
|
|
499
|
+
|
|
500
|
+
await userEvent.type(screen.getByLabelText(/Display name/), "new team");
|
|
501
|
+
await userEvent.type(
|
|
502
|
+
screen.getByLabelText(/Team Admin API key/),
|
|
503
|
+
"key_admin_plain",
|
|
504
|
+
);
|
|
505
|
+
await userEvent.type(
|
|
506
|
+
screen.getByLabelText(/Dedicated organization ids/),
|
|
507
|
+
"org-x org-y",
|
|
508
|
+
);
|
|
509
|
+
await userEvent.type(
|
|
510
|
+
screen.getByLabelText(/Team invite link/),
|
|
511
|
+
"https://cursor.com/team-invite/abc",
|
|
512
|
+
);
|
|
513
|
+
await userEvent.click(screen.getByRole("button", { name: "Create account" }));
|
|
514
|
+
|
|
515
|
+
await waitFor(() => expect(upsertAccount).toHaveBeenCalledTimes(1));
|
|
516
|
+
const submitted = upsertAccount.mock.calls[0][0].account;
|
|
517
|
+
expect(submitted.displayName).toBe("new team");
|
|
518
|
+
expect(submitted.adminApiKey).toBe("key_admin_plain");
|
|
519
|
+
expect(submitted.orgIds).toEqual(["org-x", "org-y"]);
|
|
520
|
+
// Readable round-trip: the invite link submits as typed, no marker.
|
|
521
|
+
expect(submitted.teamInviteLink).toBe("https://cursor.com/team-invite/abc");
|
|
522
|
+
expect(submitted.enabled).toBe(true);
|
|
523
|
+
// Checkbox default checked ("on-demand enabled", Cursor's team default)
|
|
524
|
+
// negates into the proto field.
|
|
525
|
+
expect(submitted.onDemandUsageDisabled).toBe(false);
|
|
526
|
+
// The deprecated default flag is never written by current clients —
|
|
527
|
+
// the shared pool is derived from empty org_ids (DD-008).
|
|
528
|
+
expect(submitted.isPlatformDefault).toBe(false);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it("declares on-demand usage off through the editor checkbox", async () => {
|
|
532
|
+
const upsertAccount = vi.fn().mockResolvedValue(scenarAccount());
|
|
533
|
+
const client = createMockStigmer({
|
|
534
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [] }),
|
|
535
|
+
upsertAccount,
|
|
536
|
+
});
|
|
537
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
538
|
+
|
|
539
|
+
await waitFor(() =>
|
|
540
|
+
expect(screen.getByRole("button", { name: "Add account" })).toBeTruthy(),
|
|
541
|
+
);
|
|
542
|
+
await userEvent.click(screen.getByRole("button", { name: "Add account" }));
|
|
543
|
+
|
|
544
|
+
await userEvent.type(screen.getByLabelText(/Display name/), "capped team");
|
|
545
|
+
await userEvent.type(screen.getByLabelText(/Team Admin API key/), "key_admin");
|
|
546
|
+
await userEvent.click(
|
|
547
|
+
screen.getByRole("checkbox", { name: /On-demand usage enabled/ }),
|
|
548
|
+
);
|
|
549
|
+
await userEvent.click(screen.getByRole("button", { name: "Create account" }));
|
|
550
|
+
|
|
551
|
+
await waitFor(() => expect(upsertAccount).toHaveBeenCalledTimes(1));
|
|
552
|
+
expect(upsertAccount.mock.calls[0][0].account.onDemandUsageDisabled).toBe(true);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it("bulk-imports keys one per line, keeping failed lines for retry", async () => {
|
|
556
|
+
const accountWithNewKey = scenarAccount({
|
|
557
|
+
memberKeys: [
|
|
558
|
+
scenarAccount().memberKeys[0],
|
|
559
|
+
create(CursorMemberKeySchema, {
|
|
560
|
+
keyId: "k-new",
|
|
561
|
+
apiKey: "***REDACTED***",
|
|
562
|
+
boundEmail: "morgan@scenar.ai",
|
|
563
|
+
enabled: true,
|
|
564
|
+
}),
|
|
565
|
+
],
|
|
566
|
+
});
|
|
567
|
+
const addMemberKey = vi
|
|
568
|
+
.fn()
|
|
569
|
+
.mockResolvedValueOnce(accountWithNewKey)
|
|
570
|
+
.mockRejectedValueOnce(
|
|
571
|
+
new StigmerError("invalid-argument", "Key rejected by Cursor", 3),
|
|
572
|
+
);
|
|
573
|
+
const syncAccount = vi.fn().mockResolvedValue(scenarView());
|
|
574
|
+
const client = createMockStigmer({
|
|
575
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
576
|
+
getAccountView: vi.fn().mockResolvedValue(scenarView()),
|
|
577
|
+
addMemberKey,
|
|
578
|
+
syncAccount,
|
|
579
|
+
});
|
|
580
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
581
|
+
|
|
582
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
583
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
584
|
+
await waitFor(() =>
|
|
585
|
+
expect(screen.getByRole("button", { name: "Import keys" })).toBeTruthy(),
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
await userEvent.click(screen.getByRole("button", { name: "Import keys" }));
|
|
589
|
+
const textarea = screen.getByLabelText(/one user-scoped API key per line/);
|
|
590
|
+
await userEvent.type(textarea, "key_good_1234{Enter}key_bad_5678");
|
|
591
|
+
await userEvent.click(screen.getByRole("button", { name: "Import 2 keys" }));
|
|
592
|
+
|
|
593
|
+
await waitFor(() => expect(addMemberKey).toHaveBeenCalledTimes(2));
|
|
594
|
+
expect(addMemberKey).toHaveBeenNthCalledWith(1, {
|
|
595
|
+
accountId: "acc-1",
|
|
596
|
+
apiKey: "key_good_1234",
|
|
597
|
+
});
|
|
598
|
+
expect(addMemberKey).toHaveBeenNthCalledWith(2, {
|
|
599
|
+
accountId: "acc-1",
|
|
600
|
+
apiKey: "key_bad_5678",
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// Per-line outcomes: success bound to its member, failure with the error.
|
|
604
|
+
await waitFor(() =>
|
|
605
|
+
expect(screen.getByText(/added — bound to morgan@scenar\.ai/)).toBeTruthy(),
|
|
606
|
+
);
|
|
607
|
+
expect(screen.getByText(/Key rejected by Cursor/)).toBeTruthy();
|
|
608
|
+
// Only the failed key remains in the box for retry.
|
|
609
|
+
expect((textarea as HTMLTextAreaElement).value).toBe("key_bad_5678");
|
|
610
|
+
// The roster sync runs automatically after the import.
|
|
611
|
+
await waitFor(() => expect(syncAccount).toHaveBeenCalledWith("acc-1"));
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it("shows pool-utilization columns and the usage-guard badge from server-computed facts", async () => {
|
|
615
|
+
const guardedView = create(CursorAccountViewSchema, {
|
|
616
|
+
account: scenarAccount({ onDemandUsageDisabled: true }),
|
|
617
|
+
snapshot: create(CursorAccountSyncSnapshotSchema, {
|
|
618
|
+
accountId: "acc-1",
|
|
619
|
+
syncedAt: timestampFromDate(new Date("2026-07-22T12:00:00Z")),
|
|
620
|
+
}),
|
|
621
|
+
keyViews: [
|
|
622
|
+
create(CursorMemberKeyViewSchema, {
|
|
623
|
+
key: scenarAccount().memberKeys[0],
|
|
624
|
+
state: CursorMemberKeyState.member_key_active,
|
|
625
|
+
usageGuardTripped: true,
|
|
626
|
+
spend: create(CursorMemberSpendSchema, {
|
|
627
|
+
email: "zane@scenar.ai",
|
|
628
|
+
includedSpendUsdMicros: 169342n,
|
|
629
|
+
apiPercentUsed: 100,
|
|
630
|
+
autoPercentUsed: 5.19,
|
|
631
|
+
totalPercentUsed: 26.58,
|
|
632
|
+
}),
|
|
633
|
+
}),
|
|
634
|
+
],
|
|
635
|
+
});
|
|
636
|
+
const client = createMockStigmer({
|
|
637
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
638
|
+
getAccountView: vi.fn().mockResolvedValue(guardedView),
|
|
639
|
+
});
|
|
640
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
641
|
+
|
|
642
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
643
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
644
|
+
|
|
645
|
+
// The four numeric columns map 1:1 onto Cursor's Members page:
|
|
646
|
+
// first-party pool %, API pool %, included $, on-demand $.
|
|
647
|
+
await waitFor(() => expect(screen.getByText("100%")).toBeTruthy()); // API
|
|
648
|
+
expect(screen.getByText("5%")).toBeTruthy(); // first-party (5.19)
|
|
649
|
+
expect(screen.getByText("$0.17")).toBeTruthy(); // included, 169342 micro-USD
|
|
650
|
+
expect(screen.getByText("$0.00")).toBeTruthy(); // on-demand, none
|
|
651
|
+
// The blended totalPercentUsed (26.58) is deliberately not rendered —
|
|
652
|
+
// it maps to nothing on Cursor's dashboard and reads a flat 100 for
|
|
653
|
+
// removed members.
|
|
654
|
+
expect(screen.queryByText("27%")).toBeNull();
|
|
655
|
+
// The badge renders the server's flag — no client-side threshold math.
|
|
656
|
+
expect(screen.getByText("Usage guard")).toBeTruthy();
|
|
657
|
+
// The header carries the declaration driving the guard.
|
|
658
|
+
expect(screen.getByText(/on-demand usage off \(usage guard active\)/)).toBeTruthy();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it("keeps the stored admin key when the editor's masked value is untouched", async () => {
|
|
662
|
+
const upsertAccount = vi.fn().mockResolvedValue(scenarAccount());
|
|
663
|
+
const client = createMockStigmer({
|
|
664
|
+
listAccounts: vi.fn().mockResolvedValue({ accounts: [scenarSummary()] }),
|
|
665
|
+
getAccountView: vi.fn().mockResolvedValue(scenarView()),
|
|
666
|
+
upsertAccount,
|
|
667
|
+
});
|
|
668
|
+
render(<CursorAccountsConsole />, { wrapper: wrapper(client) });
|
|
669
|
+
|
|
670
|
+
await waitFor(() => expect(screen.getByText("scenar team")).toBeTruthy());
|
|
671
|
+
await userEvent.click(screen.getByRole("button", { name: /scenar team/ }));
|
|
672
|
+
await waitFor(() => expect(screen.getByRole("button", { name: "Edit" })).toBeTruthy());
|
|
673
|
+
await userEvent.click(screen.getByRole("button", { name: "Edit" }));
|
|
674
|
+
|
|
675
|
+
await userEvent.click(screen.getByRole("button", { name: "Save changes" }));
|
|
676
|
+
|
|
677
|
+
await waitFor(() => expect(upsertAccount).toHaveBeenCalledTimes(1));
|
|
678
|
+
expect(upsertAccount.mock.calls[0][0].account.adminApiKey).toBe("***REDACTED***");
|
|
679
|
+
});
|
|
680
|
+
});
|