@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
|
@@ -55,9 +55,13 @@ export interface UseApplyManifestReturn {
|
|
|
55
55
|
/**
|
|
56
56
|
* Apply all documents sequentially in dependency order. Entries update
|
|
57
57
|
* live. Stops at the first failure (later entries become `"skipped"`).
|
|
58
|
-
* Resolves
|
|
58
|
+
* Resolves to the final per-document entries with terminal statuses —
|
|
59
|
+
* the caller derives full success (`every applied`) and the applied
|
|
60
|
+
* subset (for a list refresh) without re-reading React state, which is
|
|
61
|
+
* still stale in the calling closure. Empty when there was nothing to
|
|
62
|
+
* apply.
|
|
59
63
|
*/
|
|
60
|
-
readonly applyAll: () => Promise<
|
|
64
|
+
readonly applyAll: () => Promise<readonly ManifestPreviewEntry[]>;
|
|
61
65
|
/** `true` while applies are in flight. */
|
|
62
66
|
readonly isApplying: boolean;
|
|
63
67
|
/** Clear all state (content, preview, errors). */
|
|
@@ -194,8 +198,8 @@ export function useApplyManifest(org: string): UseApplyManifestReturn {
|
|
|
194
198
|
return () => clearTimeout(timer);
|
|
195
199
|
}, [content, stigmer, isApplying]);
|
|
196
200
|
|
|
197
|
-
const applyAll = useCallback(async (): Promise<
|
|
198
|
-
if (!entries || entries.length === 0) return
|
|
201
|
+
const applyAll = useCallback(async (): Promise<readonly ManifestPreviewEntry[]> => {
|
|
202
|
+
if (!entries || entries.length === 0) return [];
|
|
199
203
|
|
|
200
204
|
// Invalidate any in-flight validation so it cannot clobber apply statuses.
|
|
201
205
|
validateSeq.current++;
|
|
@@ -206,10 +210,10 @@ export function useApplyManifest(org: string): UseApplyManifestReturn {
|
|
|
206
210
|
const working = entries.map((entry): ManifestPreviewEntry => ({ ...entry, status: "pending" }));
|
|
207
211
|
const commit = () => setEntries([...working]);
|
|
208
212
|
|
|
209
|
-
let
|
|
213
|
+
let stopped = false;
|
|
210
214
|
|
|
211
215
|
for (let i = 0; i < working.length; i++) {
|
|
212
|
-
if (
|
|
216
|
+
if (stopped) {
|
|
213
217
|
working[i] = { ...working[i], status: "skipped" };
|
|
214
218
|
continue;
|
|
215
219
|
}
|
|
@@ -226,14 +230,14 @@ export function useApplyManifest(org: string): UseApplyManifestReturn {
|
|
|
226
230
|
status: "failed",
|
|
227
231
|
errorMessage: toError(err).message,
|
|
228
232
|
};
|
|
229
|
-
|
|
233
|
+
stopped = true;
|
|
230
234
|
}
|
|
231
235
|
commit();
|
|
232
236
|
}
|
|
233
237
|
|
|
234
238
|
commit();
|
|
235
239
|
setIsApplying(false);
|
|
236
|
-
return
|
|
240
|
+
return working;
|
|
237
241
|
}, [entries, stigmer]);
|
|
238
242
|
|
|
239
243
|
const hasRedactedSecrets = useMemo(
|
|
@@ -14,6 +14,7 @@ import type { ToolApprovalPolicy, McpServerSpec } from "@stigmer/protos/ai/stigm
|
|
|
14
14
|
import { ValidationState } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/status_pb";
|
|
15
15
|
import type { EnvVarDeclaration } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
|
|
16
16
|
import { useMcpServer } from "./useMcpServer.js";
|
|
17
|
+
import type { UseMcpServerReturn } from "./useMcpServer.js";
|
|
17
18
|
import { useUpdateMcpServer } from "./useUpdateMcpServer.js";
|
|
18
19
|
import { mcpServerToInput } from "./internal/mcpServerToInput.js";
|
|
19
20
|
import { useMcpServerConnect } from "./useMcpServerConnect.js";
|
|
@@ -51,6 +52,31 @@ export interface McpServerDetailViewProps {
|
|
|
51
52
|
readonly org: string;
|
|
52
53
|
/** MCP server slug (URL-friendly identifier unique within the org). */
|
|
53
54
|
readonly slug: string;
|
|
55
|
+
/**
|
|
56
|
+
* Hoisted resource state from {@link useMcpServer}, for callers that
|
|
57
|
+
* already own the fetch. When provided, the view issues **no**
|
|
58
|
+
* `getByReference` RPC of its own and renders entirely from this state —
|
|
59
|
+
* loading, error, and not-found included, so a caller mid-fetch still
|
|
60
|
+
* gets the loading skeleton rather than a false not-found.
|
|
61
|
+
*
|
|
62
|
+
* Ownership contract: supplying this state means supplying its
|
|
63
|
+
* lifecycle. The view calls `mcpServerState.refetch()` after connect,
|
|
64
|
+
* disconnect, and inline edits, so the `refetch` you pass must refresh
|
|
65
|
+
* the data behind `mcpServer` or the view will render stale state after
|
|
66
|
+
* those actions.
|
|
67
|
+
*
|
|
68
|
+
* Two callers this serves:
|
|
69
|
+
* - A page that already fetched the server (e.g. for export/YAML
|
|
70
|
+
* actions) passes `useMcpServer(org, slug)` straight through instead
|
|
71
|
+
* of paying a duplicate RPC.
|
|
72
|
+
* - A guided tour or demo supplies frozen state
|
|
73
|
+
* (`{ mcpServer, isLoading: false, isRefetching: false, error: null,
|
|
74
|
+
* refetch: noop }`) so every depicted beat renders deterministically
|
|
75
|
+
* from data the tour owns (scenar-cloud DD-006).
|
|
76
|
+
*
|
|
77
|
+
* When omitted, the view fetches by `org`/`slug` itself, as before.
|
|
78
|
+
*/
|
|
79
|
+
readonly mcpServerState?: UseMcpServerReturn;
|
|
54
80
|
/**
|
|
55
81
|
* Called once when the MCP server resource has been fetched successfully.
|
|
56
82
|
* Provides the resource display name for use cases like breadcrumbs,
|
|
@@ -115,7 +141,8 @@ export interface McpServerDetailViewProps {
|
|
|
115
141
|
/**
|
|
116
142
|
* Detail view for an MCP Server integration.
|
|
117
143
|
*
|
|
118
|
-
* Fetches the server via {@link useMcpServer} internally
|
|
144
|
+
* Fetches the server via {@link useMcpServer} internally — or renders
|
|
145
|
+
* from hoisted state when the caller passes `mcpServerState` — and shows
|
|
119
146
|
* its full configuration: validation banner (if invalid), header,
|
|
120
147
|
* a **Connect bar** with a single Connect/Reconnect action, and a
|
|
121
148
|
* **tabbed capabilities panel** (Tools, Policies, and optionally
|
|
@@ -135,10 +162,20 @@ export interface McpServerDetailViewProps {
|
|
|
135
162
|
* // Minimal — self-contained, fetches its own data
|
|
136
163
|
* <McpServerDetailView org="acme" slug="github" />
|
|
137
164
|
* ```
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```tsx
|
|
168
|
+
* // Hoisted — the page already fetched the server (e.g. for YAML
|
|
169
|
+
* // export actions), so pass that state through instead of paying a
|
|
170
|
+
* // second getByReference RPC.
|
|
171
|
+
* const state = useMcpServer(org, slug);
|
|
172
|
+
* <McpServerDetailView org={org} slug={slug} mcpServerState={state} />
|
|
173
|
+
* ```
|
|
138
174
|
*/
|
|
139
175
|
export function McpServerDetailView({
|
|
140
176
|
org,
|
|
141
177
|
slug,
|
|
178
|
+
mcpServerState,
|
|
142
179
|
onResourceLoad,
|
|
143
180
|
defaultCapabilityTab = "tools",
|
|
144
181
|
defaultShowCredentialForm = false,
|
|
@@ -150,7 +187,16 @@ export function McpServerDetailView({
|
|
|
150
187
|
onResourceUpdated,
|
|
151
188
|
className,
|
|
152
189
|
}: McpServerDetailViewProps) {
|
|
153
|
-
|
|
190
|
+
// Hoisted-state mode: when the caller supplies `mcpServerState`, disable
|
|
191
|
+
// the internal fetch via the hook's documented null-skip (a stable no-op,
|
|
192
|
+
// so `fetched` reports isLoading=false with no RPC) and render from the
|
|
193
|
+
// caller's state instead — all four states (loading/error/not-found/data)
|
|
194
|
+
// transfer intact.
|
|
195
|
+
const fetched = useMcpServer(
|
|
196
|
+
mcpServerState ? null : org,
|
|
197
|
+
mcpServerState ? null : slug,
|
|
198
|
+
);
|
|
199
|
+
const { mcpServer, isLoading, error, refetch } = mcpServerState ?? fetched;
|
|
154
200
|
const { update: updateMcpServer, isUpdating } = useUpdateMcpServer();
|
|
155
201
|
|
|
156
202
|
const saveMcpField = useCallback(
|
|
@@ -493,7 +539,9 @@ export function McpServerDetailView({
|
|
|
493
539
|
/>
|
|
494
540
|
)}
|
|
495
541
|
|
|
496
|
-
|
|
542
|
+
{/* scrollTarget: guided tours/demos bring the connect flow into view
|
|
543
|
+
(see IdentityTransportStep's "mcp-transport" for the convention). */}
|
|
544
|
+
<Section title="Connection" scrollTarget="mcp-connection">
|
|
497
545
|
<StdioSandboxNotice serverType={spec?.serverType} className="mb-3" />
|
|
498
546
|
<OAuthRequiredNotice oauthOnly={spec?.auth?.oauthOnly} className="mb-3" />
|
|
499
547
|
<ConnectBar
|
|
@@ -582,7 +630,7 @@ export function McpServerDetailView({
|
|
|
582
630
|
/>
|
|
583
631
|
</dialog>
|
|
584
632
|
|
|
585
|
-
<Section title="Capabilities">
|
|
633
|
+
<Section title="Capabilities" scrollTarget="mcp-capabilities">
|
|
586
634
|
<Tabs
|
|
587
635
|
tabs={capabilityTabs}
|
|
588
636
|
activeTab={capabilityTab}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent, within } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
|
|
6
|
+
import { createRouterTransport, ConnectError, Code } from "@connectrpc/connect";
|
|
7
|
+
import { Stigmer } from "@stigmer/sdk";
|
|
8
|
+
import { McpServerQueryController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/query_pb";
|
|
9
|
+
import {
|
|
10
|
+
McpServerSpecSchema,
|
|
11
|
+
HttpServerConfigSchema,
|
|
12
|
+
ToolApprovalPolicySchema,
|
|
13
|
+
} from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
|
|
14
|
+
import {
|
|
15
|
+
McpServerStatusSchema,
|
|
16
|
+
DiscoveredCapabilitiesSchema,
|
|
17
|
+
DiscoveredToolSchema,
|
|
18
|
+
ValidationState,
|
|
19
|
+
} from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/status_pb";
|
|
20
|
+
import { EnvVarDeclarationSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
|
|
21
|
+
import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
22
|
+
import { samples } from "../../test/samples";
|
|
23
|
+
import { StigmerContext } from "../../context";
|
|
24
|
+
import { McpServerDetailView } from "../McpServerDetailView";
|
|
25
|
+
import type { UseMcpServerReturn } from "../useMcpServer";
|
|
26
|
+
|
|
27
|
+
// The kebab menu and dialogs portal their content; without a StigmerProvider
|
|
28
|
+
// the portal container is null — pin it to document.body like sibling tests.
|
|
29
|
+
vi.mock("../../portal-container", () => ({
|
|
30
|
+
useStigmerPortalContainer: () => document.body,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
beforeAll(() => {
|
|
34
|
+
// happy-dom lacks ResizeObserver, which Base UI positioners observe.
|
|
35
|
+
if (!("ResizeObserver" in globalThis)) {
|
|
36
|
+
(globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver =
|
|
37
|
+
class {
|
|
38
|
+
observe() {}
|
|
39
|
+
unobserve() {}
|
|
40
|
+
disconnect() {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(cleanup);
|
|
46
|
+
|
|
47
|
+
const ORG = "acme";
|
|
48
|
+
const SLUG = "order-management-api";
|
|
49
|
+
|
|
50
|
+
/** Frozen discovery instant — fixtures must never read the real clock. */
|
|
51
|
+
const DISCOVERED_AT = new Date("2026-07-20T09:30:00Z");
|
|
52
|
+
|
|
53
|
+
/** The server as registered: HTTP transport, one secret env var, no tools. */
|
|
54
|
+
function buildBaseServer(): McpServer {
|
|
55
|
+
const server = samples.mcpServer({ name: "Order Management API", org: ORG, slug: SLUG });
|
|
56
|
+
server.spec = create(McpServerSpecSchema, {
|
|
57
|
+
description: "REST API for order lookup, inventory, and return processing.",
|
|
58
|
+
serverType: {
|
|
59
|
+
case: "http",
|
|
60
|
+
value: create(HttpServerConfigSchema, { url: "https://api.acme.com/mcp" }),
|
|
61
|
+
},
|
|
62
|
+
env: {
|
|
63
|
+
API_TOKEN: create(EnvVarDeclarationSchema, {
|
|
64
|
+
isSecret: true,
|
|
65
|
+
description: "Bearer token for the Order Management API",
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The same server after Connect: 3 discovered tools, 1 classified policy. */
|
|
73
|
+
function buildConnectedServer(): McpServer {
|
|
74
|
+
const server = buildBaseServer();
|
|
75
|
+
server.status = create(McpServerStatusSchema, {
|
|
76
|
+
validationState: ValidationState.valid,
|
|
77
|
+
discoveredCapabilities: create(DiscoveredCapabilitiesSchema, {
|
|
78
|
+
lastDiscoveredAt: timestampFromDate(DISCOVERED_AT),
|
|
79
|
+
tools: [
|
|
80
|
+
create(DiscoveredToolSchema, { name: "get_order", description: "Retrieve one order by ID." }),
|
|
81
|
+
create(DiscoveredToolSchema, { name: "list_orders", description: "List recent orders." }),
|
|
82
|
+
create(DiscoveredToolSchema, { name: "process_return", description: "Initiate a return and refund." }),
|
|
83
|
+
],
|
|
84
|
+
}),
|
|
85
|
+
toolApprovals: [
|
|
86
|
+
create(ToolApprovalPolicySchema, {
|
|
87
|
+
toolName: "process_return",
|
|
88
|
+
message: "Process return for order '{{args.order_id}}'",
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
return server;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Hoisted-state bag in its settled, data-loaded shape. */
|
|
96
|
+
function loadedState(server: McpServer): UseMcpServerReturn {
|
|
97
|
+
return { mcpServer: server, isLoading: false, isRefetching: false, error: null, refetch: vi.fn() };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Mounts the view against a real Connect router transport. RPCs the test
|
|
102
|
+
* does not register fall through to Connect's `unimplemented`, which the
|
|
103
|
+
* SDK hooks degrade from — the same contract the tours rely on.
|
|
104
|
+
*/
|
|
105
|
+
function renderView(
|
|
106
|
+
ui: ReactNode,
|
|
107
|
+
register: Parameters<typeof createRouterTransport>[0] = () => {},
|
|
108
|
+
) {
|
|
109
|
+
const client = new Stigmer({
|
|
110
|
+
baseUrl: "/",
|
|
111
|
+
getAccessToken: () => "test-token",
|
|
112
|
+
customTransport: createRouterTransport(register),
|
|
113
|
+
});
|
|
114
|
+
return render(
|
|
115
|
+
<StigmerContext.Provider value={client}>{ui}</StigmerContext.Provider>,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
describe("McpServerDetailView — hoisted mcpServerState", () => {
|
|
120
|
+
it("renders from injected state without issuing getByReference", async () => {
|
|
121
|
+
const getByReference = vi.fn(() => {
|
|
122
|
+
throw new ConnectError("must not fetch", Code.FailedPrecondition);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
renderView(
|
|
126
|
+
<McpServerDetailView
|
|
127
|
+
org={ORG}
|
|
128
|
+
slug={SLUG}
|
|
129
|
+
mcpServerState={loadedState(buildBaseServer())}
|
|
130
|
+
/>,
|
|
131
|
+
(router) => {
|
|
132
|
+
router.service(McpServerQueryController, { getByReference });
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Content is on screen synchronously — no skeleton frame first.
|
|
137
|
+
expect(
|
|
138
|
+
screen.getByRole("heading", { name: "Order Management API" }),
|
|
139
|
+
).toBeTruthy();
|
|
140
|
+
expect(screen.queryByLabelText("Loading MCP server details")).toBeNull();
|
|
141
|
+
|
|
142
|
+
// Let pending microtasks (credentials/permission fetches) settle, then
|
|
143
|
+
// confirm the resource fetch never fired.
|
|
144
|
+
await screen.findByRole("tab", { name: /Tools/ });
|
|
145
|
+
expect(getByReference).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("shows the loading skeleton while the injected state is loading", () => {
|
|
149
|
+
renderView(
|
|
150
|
+
<McpServerDetailView
|
|
151
|
+
org={ORG}
|
|
152
|
+
slug={SLUG}
|
|
153
|
+
mcpServerState={{
|
|
154
|
+
mcpServer: null,
|
|
155
|
+
isLoading: true,
|
|
156
|
+
isRefetching: false,
|
|
157
|
+
error: null,
|
|
158
|
+
refetch: vi.fn(),
|
|
159
|
+
}}
|
|
160
|
+
/>,
|
|
161
|
+
);
|
|
162
|
+
expect(screen.getByLabelText("Loading MCP server details")).toBeTruthy();
|
|
163
|
+
// A loading owner must never read as a missing resource.
|
|
164
|
+
expect(screen.queryByText("MCP Server not found")).toBeNull();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("routes the error state's Retry to the injected refetch", () => {
|
|
168
|
+
const refetch = vi.fn();
|
|
169
|
+
renderView(
|
|
170
|
+
<McpServerDetailView
|
|
171
|
+
org={ORG}
|
|
172
|
+
slug={SLUG}
|
|
173
|
+
mcpServerState={{
|
|
174
|
+
mcpServer: null,
|
|
175
|
+
isLoading: false,
|
|
176
|
+
isRefetching: false,
|
|
177
|
+
error: new ConnectError("backend down", Code.Unavailable),
|
|
178
|
+
refetch,
|
|
179
|
+
}}
|
|
180
|
+
/>,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
fireEvent.click(screen.getByRole("button", { name: /retry/i }));
|
|
184
|
+
expect(refetch).toHaveBeenCalledTimes(1);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("renders not-found when the injected state settles with no resource", () => {
|
|
188
|
+
renderView(
|
|
189
|
+
<McpServerDetailView
|
|
190
|
+
org={ORG}
|
|
191
|
+
slug={SLUG}
|
|
192
|
+
mcpServerState={{
|
|
193
|
+
mcpServer: null,
|
|
194
|
+
isLoading: false,
|
|
195
|
+
isRefetching: false,
|
|
196
|
+
error: null,
|
|
197
|
+
refetch: vi.fn(),
|
|
198
|
+
}}
|
|
199
|
+
/>,
|
|
200
|
+
);
|
|
201
|
+
expect(screen.getByText("MCP Server not found")).toBeTruthy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("McpServerDetailView — self-fetching mode (unchanged behaviour)", () => {
|
|
206
|
+
it("fetches by org/slug and renders the resolved server", async () => {
|
|
207
|
+
const getByReference = vi.fn(() => buildBaseServer());
|
|
208
|
+
renderView(<McpServerDetailView org={ORG} slug={SLUG} />, (router) => {
|
|
209
|
+
router.service(McpServerQueryController, { getByReference });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// First paint is the skeleton; content follows the fetch.
|
|
213
|
+
expect(screen.getByLabelText("Loading MCP server details")).toBeTruthy();
|
|
214
|
+
expect(
|
|
215
|
+
await screen.findByRole("heading", { name: "Order Management API" }),
|
|
216
|
+
).toBeTruthy();
|
|
217
|
+
expect(getByReference).toHaveBeenCalledTimes(1);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("renders not-found when the fetch 404s", async () => {
|
|
221
|
+
renderView(<McpServerDetailView org={ORG} slug={SLUG} />, (router) => {
|
|
222
|
+
router.service(McpServerQueryController, {
|
|
223
|
+
getByReference: () => {
|
|
224
|
+
throw new ConnectError("no such server", Code.NotFound);
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
expect(await screen.findByText("MCP Server not found")).toBeTruthy();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe("McpServerDetailView — connect bar and capability tabs", () => {
|
|
233
|
+
it("offers Connect with the not-connected status before discovery", () => {
|
|
234
|
+
renderView(
|
|
235
|
+
<McpServerDetailView
|
|
236
|
+
org={ORG}
|
|
237
|
+
slug={SLUG}
|
|
238
|
+
mcpServerState={loadedState(buildBaseServer())}
|
|
239
|
+
/>,
|
|
240
|
+
);
|
|
241
|
+
expect(screen.getByRole("button", { name: /^Connect$/ })).toBeTruthy();
|
|
242
|
+
expect(screen.getByText("Not connected yet")).toBeTruthy();
|
|
243
|
+
expect(
|
|
244
|
+
screen.getByText("Connect to this MCP server to discover its available tools."),
|
|
245
|
+
).toBeTruthy();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("offers Reconnect with the connected status and discovery date after discovery", () => {
|
|
249
|
+
renderView(
|
|
250
|
+
<McpServerDetailView
|
|
251
|
+
org={ORG}
|
|
252
|
+
slug={SLUG}
|
|
253
|
+
mcpServerState={loadedState(buildConnectedServer())}
|
|
254
|
+
/>,
|
|
255
|
+
);
|
|
256
|
+
expect(screen.getByRole("button", { name: /Reconnect/ })).toBeTruthy();
|
|
257
|
+
expect(screen.getByText("Connected")).toBeTruthy();
|
|
258
|
+
expect(screen.getByText("Valid")).toBeTruthy();
|
|
259
|
+
expect(screen.getByText(/Discovered/)).toBeTruthy();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("lists the discovered tools with the count badge", () => {
|
|
263
|
+
renderView(
|
|
264
|
+
<McpServerDetailView
|
|
265
|
+
org={ORG}
|
|
266
|
+
slug={SLUG}
|
|
267
|
+
mcpServerState={loadedState(buildConnectedServer())}
|
|
268
|
+
/>,
|
|
269
|
+
);
|
|
270
|
+
const toolsTab = screen.getByRole("tab", { name: /Tools/ });
|
|
271
|
+
expect(toolsTab.textContent).toContain("3");
|
|
272
|
+
expect(screen.getByText("get_order")).toBeTruthy();
|
|
273
|
+
expect(screen.getByText("list_orders")).toBeTruthy();
|
|
274
|
+
expect(screen.getByText("process_return")).toBeTruthy();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("groups the classified policy under Auto-classified with a badge of 1, not 3", () => {
|
|
278
|
+
renderView(
|
|
279
|
+
<McpServerDetailView
|
|
280
|
+
org={ORG}
|
|
281
|
+
slug={SLUG}
|
|
282
|
+
defaultCapabilityTab="policies"
|
|
283
|
+
mcpServerState={loadedState(buildConnectedServer())}
|
|
284
|
+
/>,
|
|
285
|
+
);
|
|
286
|
+
const policiesTab = screen.getByRole("tab", { name: /Policies/ });
|
|
287
|
+
expect(policiesTab.textContent).toContain("1");
|
|
288
|
+
expect(policiesTab.textContent).not.toContain("3");
|
|
289
|
+
|
|
290
|
+
const panel = screen.getByRole("tabpanel");
|
|
291
|
+
expect(within(panel).getByText(/Auto-classified/)).toBeTruthy();
|
|
292
|
+
expect(within(panel).getByText("process_return")).toBeTruthy();
|
|
293
|
+
expect(within(panel).getByText("requires approval")).toBeTruthy();
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe("McpServerDetailView — tour scroll anchors", () => {
|
|
298
|
+
it("marks the Connection and Capabilities sections as scroll targets", () => {
|
|
299
|
+
const { container } = renderView(
|
|
300
|
+
<McpServerDetailView
|
|
301
|
+
org={ORG}
|
|
302
|
+
slug={SLUG}
|
|
303
|
+
mcpServerState={loadedState(buildBaseServer())}
|
|
304
|
+
/>,
|
|
305
|
+
);
|
|
306
|
+
expect(
|
|
307
|
+
container.querySelector('[data-scroll-target="mcp-connection"]'),
|
|
308
|
+
).toBeTruthy();
|
|
309
|
+
expect(
|
|
310
|
+
container.querySelector('[data-scroll-target="mcp-capabilities"]'),
|
|
311
|
+
).toBeTruthy();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("McpServerDetailView — credential form gating", () => {
|
|
316
|
+
it("opens the credential form for the declared missing variable when asked", async () => {
|
|
317
|
+
renderView(
|
|
318
|
+
<McpServerDetailView
|
|
319
|
+
org={ORG}
|
|
320
|
+
slug={SLUG}
|
|
321
|
+
defaultShowCredentialForm
|
|
322
|
+
mcpServerState={loadedState(buildBaseServer())}
|
|
323
|
+
/>,
|
|
324
|
+
);
|
|
325
|
+
expect(await screen.findByText("Credentials Required")).toBeTruthy();
|
|
326
|
+
// Scope to the credential form — the Environment section also lists
|
|
327
|
+
// API_TOKEN as a declaration.
|
|
328
|
+
const form = screen.getByRole("form", {
|
|
329
|
+
name: /Configure Credentials Required/i,
|
|
330
|
+
});
|
|
331
|
+
// Anchored: the secret input's reveal button is labelled "Show API_TOKEN".
|
|
332
|
+
expect(within(form).getByLabelText(/^API_TOKEN/)).toBeTruthy();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("keeps the form closed by default", () => {
|
|
336
|
+
renderView(
|
|
337
|
+
<McpServerDetailView
|
|
338
|
+
org={ORG}
|
|
339
|
+
slug={SLUG}
|
|
340
|
+
mcpServerState={loadedState(buildBaseServer())}
|
|
341
|
+
/>,
|
|
342
|
+
);
|
|
343
|
+
expect(screen.queryByText("Credentials Required")).toBeNull();
|
|
344
|
+
});
|
|
345
|
+
});
|
package/src/mcp-server/index.ts
CHANGED
|
@@ -100,4 +100,17 @@ export type {
|
|
|
100
100
|
McpServerCreationResult,
|
|
101
101
|
} from "./McpServerCreationWizard.js";
|
|
102
102
|
|
|
103
|
+
// Individual wizard steps are exported as presentational building blocks:
|
|
104
|
+
// they are fully prop-driven (data, validationError, error, isCreating),
|
|
105
|
+
// which lets platform builders and guided tours/demos render any wizard
|
|
106
|
+
// state — including validation and submit failures — deterministically,
|
|
107
|
+
// without driving the stateful McpServerCreationWizard through user events.
|
|
108
|
+
export { IdentityTransportStep } from "./steps/IdentityTransportStep.js";
|
|
109
|
+
export type { IdentityTransportStepProps } from "./steps/IdentityTransportStep.js";
|
|
110
|
+
export { EnvironmentAuthStep } from "./steps/EnvironmentAuthStep.js";
|
|
111
|
+
export type { EnvironmentAuthStepProps } from "./steps/EnvironmentAuthStep.js";
|
|
112
|
+
export { ReviewStep } from "./steps/ReviewStep.js";
|
|
113
|
+
export type { ReviewStepProps } from "./steps/ReviewStep.js";
|
|
114
|
+
|
|
115
|
+
export { createInitialMcpServerWizardData } from "./steps/types.js";
|
|
103
116
|
export type { McpServerWizardData } from "./steps/types.js";
|
|
@@ -16,6 +16,10 @@ export interface EnvironmentAuthStepProps {
|
|
|
16
16
|
* Both sections are optional. Env vars start expanded if any exist;
|
|
17
17
|
* auth starts collapsed unless already enabled. No validation gate —
|
|
18
18
|
* the user can proceed with zero configuration.
|
|
19
|
+
*
|
|
20
|
+
* Fully presentational: form state lives in the `data` prop and edits
|
|
21
|
+
* flow out through `updateData`, so standalone consumers (embedded
|
|
22
|
+
* builders, guided tours) can render any configuration state from props.
|
|
19
23
|
*/
|
|
20
24
|
export function EnvironmentAuthStep({
|
|
21
25
|
data,
|
|
@@ -23,6 +23,12 @@ const SLUG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
|
23
23
|
*
|
|
24
24
|
* Transport type uses a radio group (mutually exclusive modes).
|
|
25
25
|
* Conditional fields render based on the selected transport.
|
|
26
|
+
*
|
|
27
|
+
* Fully presentational: state lives in the `data` prop and every edit
|
|
28
|
+
* flows out through `updateData`. Inside `McpServerCreationWizard` that
|
|
29
|
+
* state comes from `useWizardState`; standalone consumers (embedded
|
|
30
|
+
* builders, guided tours) can render any form state — including a
|
|
31
|
+
* `validationError` — deterministically from props.
|
|
26
32
|
*/
|
|
27
33
|
export function IdentityTransportStep({
|
|
28
34
|
data,
|
|
@@ -195,7 +201,9 @@ export function IdentityTransportStep({
|
|
|
195
201
|
</fieldset>
|
|
196
202
|
</div>
|
|
197
203
|
|
|
198
|
-
|
|
204
|
+
{/* data-scroll-target: guided tours/demos scroll the transport config
|
|
205
|
+
(the step's decision point) into view within the wizard's scroll area. */}
|
|
206
|
+
<fieldset className="space-y-3" data-scroll-target="mcp-transport">
|
|
199
207
|
<legend className="text-sm font-medium text-foreground">
|
|
200
208
|
Transport <span className="text-destructive">*</span>
|
|
201
209
|
</legend>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useMemo } from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import { buildMcpServerProto, getUserMessage, serializeManifest } from "@stigmer/sdk";
|
|
6
6
|
import type { McpServerInput } from "@stigmer/sdk";
|
|
@@ -20,6 +20,11 @@ export interface ReviewStepProps {
|
|
|
20
20
|
* Shows a summary card with key configuration details and a full
|
|
21
21
|
* YAML preview of the MCP server that will be created. The "Create"
|
|
22
22
|
* action is in the WizardNav footer, not in this component.
|
|
23
|
+
*
|
|
24
|
+
* Fully presentational: renders entirely from props, including the
|
|
25
|
+
* `isCreating` in-flight state and a submit `error`. Standalone
|
|
26
|
+
* consumers (embedded builders, guided tours) can therefore depict
|
|
27
|
+
* any review state — success preview or failure — deterministically.
|
|
23
28
|
*/
|
|
24
29
|
export function ReviewStep({
|
|
25
30
|
org,
|
|
@@ -27,6 +32,17 @@ export function ReviewStep({
|
|
|
27
32
|
isCreating,
|
|
28
33
|
error,
|
|
29
34
|
}: ReviewStepProps) {
|
|
35
|
+
const errorRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
|
|
37
|
+
// The error panel renders below the (tall) YAML preview inside the wizard's
|
|
38
|
+
// internal scroll area, so a failed create could otherwise report itself
|
|
39
|
+
// out of view. Bring it into view whenever a new error arrives.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (error) {
|
|
42
|
+
errorRef.current?.scrollIntoView({ block: "nearest" });
|
|
43
|
+
}
|
|
44
|
+
}, [error]);
|
|
45
|
+
|
|
30
46
|
const mcpServerInput = useMemo(
|
|
31
47
|
() => buildMcpServerInput(org, data),
|
|
32
48
|
[org, data],
|
|
@@ -113,6 +129,7 @@ export function ReviewStep({
|
|
|
113
129
|
{/* Error display */}
|
|
114
130
|
{error && (
|
|
115
131
|
<div
|
|
132
|
+
ref={errorRef}
|
|
116
133
|
className="rounded-md border border-destructive bg-muted-faint px-4 py-3"
|
|
117
134
|
role="alert"
|
|
118
135
|
>
|
|
@@ -17,6 +17,8 @@ export interface UseMcpServerCountOptions {
|
|
|
17
17
|
* @default "org"
|
|
18
18
|
*/
|
|
19
19
|
readonly scope?: ResourceListScope;
|
|
20
|
+
/** Opaque token that forces a recount when its value changes. */
|
|
21
|
+
readonly refetchToken?: unknown;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/** Return value of {@link useMcpServerCount}. */
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type ModelPricingBaseline,
|
|
11
11
|
} from "@stigmer/protos/ai/stigmer/billing/v1/model_pricing_baseline_pb";
|
|
12
12
|
import { Button } from "../button/index.js";
|
|
13
|
-
import { Field, INPUT_CLASSES
|
|
13
|
+
import { Field, INPUT_CLASSES } from "../internal/form-primitives.js";
|
|
14
14
|
import {
|
|
15
15
|
ZERO,
|
|
16
16
|
dollarsToMicros,
|
|
@@ -23,10 +23,43 @@ import {
|
|
|
23
23
|
// explicit old→new rate confirmation step (a billing-critical edit is never
|
|
24
24
|
// submitted without the operator seeing exactly which rates move).
|
|
25
25
|
//
|
|
26
|
-
// Internal to the
|
|
27
|
-
// ModelCatalogPanel, never exported from the
|
|
26
|
+
// Internal to the pricing-governance module: rendered by
|
|
27
|
+
// PricingGovernanceConsole and ModelCatalogPanel, never exported from the
|
|
28
|
+
// barrel.
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Dollar-rate input field (decimal keypad, "$ per million tokens").
|
|
33
|
+
* File-local: this is the editor's own composition of the shared form
|
|
34
|
+
* primitives, used nowhere else.
|
|
35
|
+
*/
|
|
36
|
+
function RateField({
|
|
37
|
+
label,
|
|
38
|
+
value,
|
|
39
|
+
placeholder,
|
|
40
|
+
disabled,
|
|
41
|
+
onChange,
|
|
42
|
+
}: {
|
|
43
|
+
readonly label: string;
|
|
44
|
+
readonly value: string;
|
|
45
|
+
readonly placeholder?: string;
|
|
46
|
+
readonly disabled: boolean;
|
|
47
|
+
readonly onChange: (value: string) => void;
|
|
48
|
+
}) {
|
|
49
|
+
return (
|
|
50
|
+
<Field label={label}>
|
|
51
|
+
<input
|
|
52
|
+
className={INPUT_CLASSES}
|
|
53
|
+
inputMode="decimal"
|
|
54
|
+
value={value}
|
|
55
|
+
onChange={(e) => onChange(e.target.value)}
|
|
56
|
+
placeholder={placeholder ?? "0.00"}
|
|
57
|
+
disabled={disabled}
|
|
58
|
+
/>
|
|
59
|
+
</Field>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
// ---------------------------------------------------------------------------
|
|
31
64
|
// Form model — strings while editing, converted + validated on submit.
|
|
32
65
|
// Validation mirrors the protovalidate rules on ModelPricingBaseline so
|