@invect/ui 0.0.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/Invect-CWpIwZ5F.js +92738 -0
- package/dist/Invect.d.ts +25 -0
- package/dist/InvectShell.d.ts +14 -0
- package/dist/api/agent-tools.api.d.ts +1 -0
- package/dist/api/client.d.ts +207 -0
- package/dist/api/credentials.api.d.ts +47 -0
- package/dist/api/executions.api.d.ts +43 -0
- package/dist/api/flows.api.d.ts +100 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/node-data.api.d.ts +66 -0
- package/dist/api/query-keys.d.ts +22 -0
- package/dist/api/triggers.api.d.ts +44 -0
- package/dist/api/types.d.ts +147 -0
- package/dist/api/use-flow-run-stream.d.ts +12 -0
- package/dist/assets/invect-branding.d.ts +4 -0
- package/dist/assets/provider-icons/index.d.ts +8 -0
- package/dist/babel-C9OtljFZ.js +9721 -0
- package/dist/components/PageLayout.d.ts +17 -0
- package/dist/components/chat/ChatInput.d.ts +17 -0
- package/dist/components/chat/ChatMessageList.d.ts +14 -0
- package/dist/components/chat/ChatModelSelector.d.ts +11 -0
- package/dist/components/chat/ChatPanel.d.ts +19 -0
- package/dist/components/chat/ChatPromptOverlay.d.ts +13 -0
- package/dist/components/chat/ChatProviderSelector.d.ts +9 -0
- package/dist/components/chat/ChatSettingsPanel.d.ts +11 -0
- package/dist/components/chat/InlineCredentialSetup.d.ts +9 -0
- package/dist/components/chat/MarkdownRenderer.d.ts +7 -0
- package/dist/components/chat/chat-memory.d.ts +21 -0
- package/dist/components/chat/chat.store.d.ts +416 -0
- package/dist/components/chat/index.d.ts +5 -0
- package/dist/components/chat/use-chat.d.ts +28 -0
- package/dist/components/credentials/CreateCredentialModal.d.ts +13 -0
- package/dist/components/credentials/CredentialDetailDialog.d.ts +17 -0
- package/dist/components/credentials/EditCredentialModal.d.ts +11 -0
- package/dist/components/credentials/OAuth2ConnectButton.d.ts +38 -0
- package/dist/components/credentials/OAuth2ProviderSelector.d.ts +15 -0
- package/dist/components/credentials/credential-utils.d.ts +12 -0
- package/dist/components/credentials/index.d.ts +9 -0
- package/dist/components/dashboard/FailedRunsAlert.d.ts +3 -0
- package/dist/components/dashboard/FlowCard.d.ts +7 -0
- package/dist/components/dashboard/RecentActivityTable.d.ts +9 -0
- package/dist/components/dashboard/StatCard.d.ts +10 -0
- package/dist/components/dashboard/index.d.ts +5 -0
- package/dist/components/dashboard/status-helpers.d.ts +5 -0
- package/dist/components/flow-editor/ActionsSidebar.d.ts +2 -0
- package/dist/components/flow-editor/FlowEditor.d.ts +21 -0
- package/dist/components/flow-editor/FlowHeader.d.ts +9 -0
- package/dist/components/flow-editor/FlowLayout.d.ts +24 -0
- package/dist/components/flow-editor/ModeSwitcher.d.ts +7 -0
- package/dist/components/flow-editor/NodeSidebar.d.ts +24 -0
- package/dist/components/flow-editor/RunControls.d.ts +12 -0
- package/dist/components/flow-editor/ToolConfigPanel.d.ts +16 -0
- package/dist/components/flow-editor/ValidationPanel.d.ts +5 -0
- package/dist/components/flow-editor/flow-editor.store.d.ts +1 -0
- package/dist/components/flow-editor/index.d.ts +6 -0
- package/dist/components/flow-editor/inline-edit.d.ts +10 -0
- package/dist/components/flow-editor/node-config-panel/ConfigFieldWithTemplate.d.ts +26 -0
- package/dist/components/flow-editor/node-config-panel/CredentialCombobox.d.ts +21 -0
- package/dist/components/flow-editor/node-config-panel/CredentialsSection.d.ts +19 -0
- package/dist/components/flow-editor/node-config-panel/DroppableInput.d.ts +20 -0
- package/dist/components/flow-editor/node-config-panel/DynamicSelectField.d.ts +22 -0
- package/dist/components/flow-editor/node-config-panel/JsonPreviewPanel.d.ts +25 -0
- package/dist/components/flow-editor/node-config-panel/NodeConfigPanel.d.ts +14 -0
- package/dist/components/flow-editor/node-config-panel/NodeConfigPanelHeader.d.ts +15 -0
- package/dist/components/flow-editor/node-config-panel/ParametersSection.d.ts +16 -0
- package/dist/components/flow-editor/node-config-panel/SearchableSelectField.d.ts +17 -0
- package/dist/components/flow-editor/node-config-panel/SwitchCasesField.d.ts +18 -0
- package/dist/components/flow-editor/node-config-panel/hooks/index.d.ts +2 -0
- package/dist/components/flow-editor/node-config-panel/hooks/use-node-config-panel-state.d.ts +24 -0
- package/dist/components/flow-editor/node-config-panel/hooks/use-node-execution.d.ts +46 -0
- package/dist/components/flow-editor/node-config-panel/hooks/use-upstream-slots.d.ts +16 -0
- package/dist/components/flow-editor/node-config-panel/panels/AgentToolsPanel.d.ts +18 -0
- package/dist/components/flow-editor/node-config-panel/panels/ConfigurationPanel.d.ts +49 -0
- package/dist/components/flow-editor/node-config-panel/panels/DataMapperPane.d.ts +40 -0
- package/dist/components/flow-editor/node-config-panel/panels/InputPanel.d.ts +49 -0
- package/dist/components/flow-editor/node-config-panel/panels/OutputPanel.d.ts +7 -0
- package/dist/components/flow-editor/node-config-panel/panels/index.d.ts +4 -0
- package/dist/components/flow-editor/node-config-panel/types.d.ts +19 -0
- package/dist/components/flow-editor/node-config-panel/use-node-config-panel-store.d.ts +49 -0
- package/dist/components/flow-editor/node-config-panel/use-node-config-state.d.ts +26 -0
- package/dist/components/flow-editor/node-config-panel/use-run-node.d.ts +16 -0
- package/dist/components/flow-editor/node-config-panel/utils.d.ts +9 -0
- package/dist/components/flow-editor/serialize-to-sdk.d.ts +20 -0
- package/dist/components/flow-editor/toolbar-context.d.ts +2 -0
- package/dist/components/flow-editor/use-copy-paste.d.ts +7 -0
- package/dist/components/flow-editor/use-copy-paste.types.d.ts +38 -0
- package/dist/components/flow-editor/use-flow-editor.d.ts +44 -0
- package/dist/components/flow-runs-table/FlowRunsTable.d.ts +6 -0
- package/dist/components/flow-runs-table/index.d.ts +1 -0
- package/dist/components/flow-viewer/FlowRunsView.d.ts +7 -0
- package/dist/components/flow-viewer/FlowStatusView.d.ts +21 -0
- package/dist/components/flow-viewer/RunSelector.d.ts +13 -0
- package/dist/components/flow-viewer/RunsSidebar.d.ts +14 -0
- package/dist/components/flow-viewer/agent-tool-executions-list.d.ts +7 -0
- package/dist/components/flow-viewer/index.d.ts +1 -0
- package/dist/components/flow-viewer/logs-panel.d.ts +18 -0
- package/dist/components/flow-viewer/use-execution-log-data.d.ts +113 -0
- package/dist/components/graph/BatchFlowEdge.d.ts +33 -0
- package/dist/components/graph/LayoutSelector.d.ts +9 -0
- package/dist/components/graph/index.d.ts +47 -0
- package/dist/components/graph/styleUtils.d.ts +124 -0
- package/dist/components/nodes/AgentConfigPanel.d.ts +24 -0
- package/dist/components/nodes/AgentNode.d.ts +8 -0
- package/dist/components/nodes/AgentToolsBox.d.ts +41 -0
- package/dist/components/nodes/NodeAppendix.d.ts +19 -0
- package/dist/components/nodes/NodeStatusIndicator.d.ts +30 -0
- package/dist/components/nodes/NodeViewContext.d.ts +18 -0
- package/dist/components/nodes/ToolParamField.d.ts +28 -0
- package/dist/components/nodes/ToolSelectorModal.d.ts +80 -0
- package/dist/components/nodes/ToolSelectorParts.d.ts +30 -0
- package/dist/components/nodes/UniversalNode.d.ts +2 -0
- package/dist/components/nodes/createContextAwareNodes.d.ts +6 -0
- package/dist/components/nodes/index.d.ts +22 -0
- package/dist/components/nodes/nodeRegistry.d.ts +13 -0
- package/dist/components/nodes/withNodeContext.d.ts +7 -0
- package/dist/components/shared/InvectLoader.d.ts +8 -0
- package/dist/components/shared/InvectLogo.d.ts +9 -0
- package/dist/components/shared/ProviderIcon.d.ts +23 -0
- package/dist/components/side-menu/side-menu.d.ts +4 -0
- package/dist/components/sidebar/BaseSidebar.d.ts +17 -0
- package/dist/components/sidebar/index.d.ts +1 -0
- package/dist/components/triggers/CronPreview.d.ts +12 -0
- package/dist/components/triggers/index.d.ts +1 -0
- package/dist/components/ui/alert-dialog.d.ts +18 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/button.d.ts +13 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/codemirror-js-editor.d.ts +25 -0
- package/dist/components/ui/codemirror-json-editor.d.ts +18 -0
- package/dist/components/ui/codemirror-nunjucks-editor.d.ts +13 -0
- package/dist/components/ui/codemirror-vscode-theme.d.ts +24 -0
- package/dist/components/ui/collapsible.d.ts +6 -0
- package/dist/components/ui/command.d.ts +18 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/empty-state.d.ts +21 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/resizable.d.ts +8 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/select.d.ts +18 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/slider.d.ts +4 -0
- package/dist/components/ui/switch.d.ts +3 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tree-view.d.ts +107 -0
- package/dist/contexts/AgentToolCallbacksContext.d.ts +23 -0
- package/dist/contexts/ApiContext.d.ts +11 -0
- package/dist/contexts/FlowDataContext.d.ts +9 -0
- package/dist/contexts/NodeRegistryContext.d.ts +14 -0
- package/dist/contexts/PluginRegistryContext.d.ts +39 -0
- package/dist/contexts/ThemeProvider.d.ts +18 -0
- package/dist/contexts/ValidationContext.d.ts +22 -0
- package/dist/demo/DemoInvect.d.ts +11 -0
- package/dist/demo/FlowViewer.d.ts +31 -0
- package/dist/demo/demo-api-client.d.ts +33 -0
- package/dist/demo/index.d.ts +6 -0
- package/dist/demo/sample-data.d.ts +1538 -0
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +2774 -0
- package/dist/estree-ClbRfS-1.js +7076 -0
- package/dist/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
- package/dist/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/geist-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/iosevka-latin-400-normal.woff2 +0 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-document-title.d.ts +1 -0
- package/dist/hooks/use-flow-data.d.ts +22 -0
- package/dist/hooks/use-invect-portal-class.d.ts +21 -0
- package/dist/hooks/useFlowEditorStore.d.ts +1 -0
- package/dist/index.css +3 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +717 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/prettier.d.ts +13 -0
- package/dist/routes/all-flow-runs.d.ts +5 -0
- package/dist/routes/credentials.d.ts +5 -0
- package/dist/routes/flow-route-layout.d.ts +19 -0
- package/dist/routes/flow-runs.d.ts +5 -0
- package/dist/routes/flow.d.ts +5 -0
- package/dist/routes/home.d.ts +5 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/standalone-C3Df7W52.js +3463 -0
- package/dist/stores/executionViewStore.d.ts +64 -0
- package/dist/stores/flow-editor.store.d.ts +137 -0
- package/dist/stores/flowEditorStore.d.ts +1 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/uiStore.d.ts +45 -0
- package/dist/types/agent-tools.types.d.ts +53 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/node-definition.types.d.ts +85 -0
- package/dist/types/plugin.types.d.ts +100 -0
- package/dist/utils/credentialBranding.d.ts +8 -0
- package/dist/utils/credentialFiltering.d.ts +20 -0
- package/dist/utils/flowTransformations.d.ts +16 -0
- package/dist/utils/layoutUtils.d.ts +23 -0
- package/dist/utils/nodeReferenceUtils.d.ts +37 -0
- package/dist/vendor.d.ts +5 -0
- package/package.json +130 -0
- package/src/.DS_Store +0 -0
- package/src/Invect.tsx +229 -0
- package/src/InvectShell.tsx +55 -0
- package/src/api/agent-tools.api.ts +23 -0
- package/src/api/client.ts +899 -0
- package/src/api/credentials.api.ts +197 -0
- package/src/api/executions.api.ts +228 -0
- package/src/api/flows.api.ts +195 -0
- package/src/api/index.ts +17 -0
- package/src/api/node-data.api.ts +167 -0
- package/src/api/query-keys.ts +44 -0
- package/src/api/triggers.api.ts +120 -0
- package/src/api/types.ts +212 -0
- package/src/api/use-flow-run-stream.ts +206 -0
- package/src/app.css +560 -0
- package/src/assets/.DS_Store +0 -0
- package/src/assets/favicon.ico +0 -0
- package/src/assets/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
- package/src/assets/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
- package/src/assets/fonts/geist-latin-wght-normal.woff2 +0 -0
- package/src/assets/fonts/iosevka-latin-400-normal.woff2 +0 -0
- package/src/assets/invect-branding.ts +51 -0
- package/src/assets/provider-icons/anthropic.svg +1 -0
- package/src/assets/provider-icons/anthropic_light.svg +1 -0
- package/src/assets/provider-icons/github.svg +1 -0
- package/src/assets/provider-icons/github_light.svg +1 -0
- package/src/assets/provider-icons/gmail.svg +1 -0
- package/src/assets/provider-icons/google_calendar.svg +1 -0
- package/src/assets/provider-icons/google_docs.svg +1 -0
- package/src/assets/provider-icons/google_drive.svg +1 -0
- package/src/assets/provider-icons/google_sheets.svg +1 -0
- package/src/assets/provider-icons/index.ts +55 -0
- package/src/assets/provider-icons/linear.svg +1 -0
- package/src/assets/provider-icons/openai.svg +1 -0
- package/src/assets/provider-icons/postgres.svg +1 -0
- package/src/assets/provider-icons/slack.svg +1 -0
- package/src/assets/small-loader-dark.svg +22 -0
- package/src/assets/small-loader-light.svg +22 -0
- package/src/assets/small.svg +7 -0
- package/src/components/.DS_Store +0 -0
- package/src/components/PageLayout.tsx +55 -0
- package/src/components/chat/ChatInput.tsx +115 -0
- package/src/components/chat/ChatMessageList.tsx +788 -0
- package/src/components/chat/ChatModelSelector.tsx +208 -0
- package/src/components/chat/ChatPanel.tsx +243 -0
- package/src/components/chat/ChatPromptOverlay.tsx +150 -0
- package/src/components/chat/ChatProviderSelector.tsx +135 -0
- package/src/components/chat/ChatSettingsPanel.tsx +277 -0
- package/src/components/chat/InlineCredentialSetup.tsx +343 -0
- package/src/components/chat/MarkdownRenderer.tsx +140 -0
- package/src/components/chat/chat-memory.ts +88 -0
- package/src/components/chat/chat.store.ts +479 -0
- package/src/components/chat/index.ts +5 -0
- package/src/components/chat/use-chat.ts +473 -0
- package/src/components/credentials/CreateCredentialModal.tsx +609 -0
- package/src/components/credentials/CredentialDetailDialog.tsx +882 -0
- package/src/components/credentials/EditCredentialModal.tsx +399 -0
- package/src/components/credentials/OAuth2ConnectButton.tsx +288 -0
- package/src/components/credentials/OAuth2ProviderSelector.tsx +360 -0
- package/src/components/credentials/credential-utils.ts +99 -0
- package/src/components/credentials/index.ts +10 -0
- package/src/components/dashboard/FailedRunsAlert.tsx +67 -0
- package/src/components/dashboard/FlowCard.tsx +64 -0
- package/src/components/dashboard/RecentActivityTable.tsx +92 -0
- package/src/components/dashboard/StatCard.tsx +32 -0
- package/src/components/dashboard/index.ts +5 -0
- package/src/components/dashboard/status-helpers.tsx +102 -0
- package/src/components/flow-editor/ActionsSidebar.tsx +503 -0
- package/src/components/flow-editor/FlowEditor.tsx +1002 -0
- package/src/components/flow-editor/FlowHeader.tsx +87 -0
- package/src/components/flow-editor/FlowLayout.tsx +117 -0
- package/src/components/flow-editor/ModeSwitcher.tsx +49 -0
- package/src/components/flow-editor/NodeSidebar.tsx +343 -0
- package/src/components/flow-editor/RunControls.tsx +109 -0
- package/src/components/flow-editor/ToolConfigPanel.tsx +434 -0
- package/src/components/flow-editor/ValidationPanel.tsx +167 -0
- package/src/components/flow-editor/flow-editor.store.ts +2 -0
- package/src/components/flow-editor/index.ts +6 -0
- package/src/components/flow-editor/inline-edit.tsx +111 -0
- package/src/components/flow-editor/node-config-panel/ConfigFieldWithTemplate.tsx +334 -0
- package/src/components/flow-editor/node-config-panel/CredentialCombobox.tsx +217 -0
- package/src/components/flow-editor/node-config-panel/CredentialsSection.tsx +154 -0
- package/src/components/flow-editor/node-config-panel/DroppableInput.tsx +45 -0
- package/src/components/flow-editor/node-config-panel/DynamicSelectField.tsx +223 -0
- package/src/components/flow-editor/node-config-panel/JsonPreviewPanel.tsx +134 -0
- package/src/components/flow-editor/node-config-panel/NodeConfigPanel.tsx +650 -0
- package/src/components/flow-editor/node-config-panel/NodeConfigPanelHeader.tsx +91 -0
- package/src/components/flow-editor/node-config-panel/ParametersSection.tsx +144 -0
- package/src/components/flow-editor/node-config-panel/SearchableSelectField.tsx +126 -0
- package/src/components/flow-editor/node-config-panel/SwitchCasesField.tsx +212 -0
- package/src/components/flow-editor/node-config-panel/hooks/index.ts +2 -0
- package/src/components/flow-editor/node-config-panel/hooks/use-node-config-panel-state.ts +284 -0
- package/src/components/flow-editor/node-config-panel/hooks/use-node-execution.ts +287 -0
- package/src/components/flow-editor/node-config-panel/hooks/use-upstream-slots.ts +310 -0
- package/src/components/flow-editor/node-config-panel/panels/AgentToolsPanel.tsx +837 -0
- package/src/components/flow-editor/node-config-panel/panels/ConfigurationPanel.tsx +383 -0
- package/src/components/flow-editor/node-config-panel/panels/DataMapperPane.tsx +456 -0
- package/src/components/flow-editor/node-config-panel/panels/InputPanel.tsx +338 -0
- package/src/components/flow-editor/node-config-panel/panels/OutputPanel.tsx +109 -0
- package/src/components/flow-editor/node-config-panel/panels/index.ts +4 -0
- package/src/components/flow-editor/node-config-panel/types.ts +20 -0
- package/src/components/flow-editor/node-config-panel/use-node-config-panel-store.ts +283 -0
- package/src/components/flow-editor/node-config-panel/use-node-config-state.ts +172 -0
- package/src/components/flow-editor/node-config-panel/use-run-node.ts +147 -0
- package/src/components/flow-editor/node-config-panel/utils.ts +73 -0
- package/src/components/flow-editor/serialize-to-sdk.ts +204 -0
- package/src/components/flow-editor/toolbar-context.ts +9 -0
- package/src/components/flow-editor/use-copy-paste.ts +575 -0
- package/src/components/flow-editor/use-copy-paste.types.ts +35 -0
- package/src/components/flow-editor/use-flow-editor.ts +241 -0
- package/src/components/flow-runs-table/FlowRunsTable.tsx +631 -0
- package/src/components/flow-runs-table/index.ts +1 -0
- package/src/components/flow-viewer/FlowRunsView.tsx +268 -0
- package/src/components/flow-viewer/FlowStatusView.tsx +351 -0
- package/src/components/flow-viewer/RunSelector.tsx +422 -0
- package/src/components/flow-viewer/RunsSidebar.tsx +125 -0
- package/src/components/flow-viewer/agent-tool-executions-list.tsx +298 -0
- package/src/components/flow-viewer/index.ts +1 -0
- package/src/components/flow-viewer/logs-panel.tsx +567 -0
- package/src/components/flow-viewer/use-execution-log-data.ts +374 -0
- package/src/components/graph/BatchFlowEdge.tsx +229 -0
- package/src/components/graph/LayoutSelector.tsx +42 -0
- package/src/components/graph/index.ts +61 -0
- package/src/components/graph/styleUtils.ts +375 -0
- package/src/components/nodes/.DS_Store +0 -0
- package/src/components/nodes/AgentConfigPanel.tsx +1033 -0
- package/src/components/nodes/AgentNode.tsx +298 -0
- package/src/components/nodes/AgentToolsBox.tsx +193 -0
- package/src/components/nodes/NodeAppendix.tsx +98 -0
- package/src/components/nodes/NodeStatusIndicator.tsx +74 -0
- package/src/components/nodes/NodeViewContext.tsx +45 -0
- package/src/components/nodes/ToolParamField.tsx +282 -0
- package/src/components/nodes/ToolSelectorModal.tsx +648 -0
- package/src/components/nodes/ToolSelectorParts.tsx +505 -0
- package/src/components/nodes/UniversalNode.tsx +356 -0
- package/src/components/nodes/createContextAwareNodes.ts +19 -0
- package/src/components/nodes/index.ts +45 -0
- package/src/components/nodes/nodeRegistry.ts +50 -0
- package/src/components/nodes/withNodeContext.tsx +55 -0
- package/src/components/shared/InvectLoader.tsx +59 -0
- package/src/components/shared/InvectLogo.tsx +59 -0
- package/src/components/shared/ProviderIcon.tsx +115 -0
- package/src/components/side-menu/side-menu.tsx +267 -0
- package/src/components/sidebar/BaseSidebar.tsx +148 -0
- package/src/components/sidebar/index.ts +1 -0
- package/src/components/triggers/CronPreview.tsx +243 -0
- package/src/components/triggers/index.ts +1 -0
- package/src/components/ui/alert-dialog.tsx +152 -0
- package/src/components/ui/badge.tsx +39 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/codemirror-js-editor.tsx +432 -0
- package/src/components/ui/codemirror-json-editor.tsx +816 -0
- package/src/components/ui/codemirror-nunjucks-editor.tsx +451 -0
- package/src/components/ui/codemirror-vscode-theme.ts +243 -0
- package/src/components/ui/collapsible.tsx +12 -0
- package/src/components/ui/command.tsx +162 -0
- package/src/components/ui/dialog.tsx +140 -0
- package/src/components/ui/dropdown-menu.tsx +232 -0
- package/src/components/ui/empty-state.tsx +93 -0
- package/src/components/ui/input.tsx +26 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/popover.tsx +53 -0
- package/src/components/ui/resizable.tsx +61 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +179 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/slider.tsx +58 -0
- package/src/components/ui/switch.tsx +22 -0
- package/src/components/ui/table.tsx +90 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/tooltip.tsx +54 -0
- package/src/components/ui/tree-view.tsx +574 -0
- package/src/contexts/AgentToolCallbacksContext.tsx +31 -0
- package/src/contexts/ApiContext.tsx +51 -0
- package/src/contexts/FlowDataContext.tsx +21 -0
- package/src/contexts/NodeRegistryContext.tsx +54 -0
- package/src/contexts/PluginRegistryContext.tsx +182 -0
- package/src/contexts/ThemeProvider.tsx +106 -0
- package/src/contexts/ValidationContext.tsx +122 -0
- package/src/demo/DemoInvect.tsx +42 -0
- package/src/demo/FlowViewer.tsx +294 -0
- package/src/demo/demo-api-client.ts +246 -0
- package/src/demo/index.ts +28 -0
- package/src/demo/sample-data.ts +1980 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-document-title.ts +8 -0
- package/src/hooks/use-flow-data.ts +144 -0
- package/src/hooks/use-invect-portal-class.ts +27 -0
- package/src/hooks/useFlowEditorStore.ts +2 -0
- package/src/index.ts +70 -0
- package/src/lib/utils.ts +6 -0
- package/src/prettier.d.ts +13 -0
- package/src/routes/all-flow-runs.tsx +27 -0
- package/src/routes/credentials.tsx +362 -0
- package/src/routes/flow-route-layout.tsx +113 -0
- package/src/routes/flow-runs.tsx +22 -0
- package/src/routes/flow.tsx +22 -0
- package/src/routes/home.tsx +282 -0
- package/src/services/index.ts +6 -0
- package/src/stores/executionViewStore.ts +211 -0
- package/src/stores/flow-editor.store.ts +738 -0
- package/src/stores/flowEditorStore.ts +2 -0
- package/src/stores/index.ts +10 -0
- package/src/stores/uiStore.ts +189 -0
- package/src/types/agent-tools.types.ts +64 -0
- package/src/types/index.ts +5 -0
- package/src/types/node-definition.types.ts +104 -0
- package/src/types/plugin.types.ts +123 -0
- package/src/utils/credentialBranding.ts +116 -0
- package/src/utils/credentialFiltering.ts +68 -0
- package/src/utils/flowTransformations.ts +137 -0
- package/src/utils/layoutUtils.ts +127 -0
- package/src/utils/nodeReferenceUtils.ts +135 -0
- package/src/vendor.d.ts +7 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { X } from 'lucide-react';
|
|
3
|
+
import { Button } from '../ui/button';
|
|
4
|
+
import { Input } from '../ui/input';
|
|
5
|
+
import { Label } from '../ui/label';
|
|
6
|
+
import { Textarea } from '../ui/textarea';
|
|
7
|
+
import type {
|
|
8
|
+
Credential,
|
|
9
|
+
UpdateCredentialInput,
|
|
10
|
+
CredentialAuthType,
|
|
11
|
+
CredentialType,
|
|
12
|
+
} from '../../api/types';
|
|
13
|
+
|
|
14
|
+
interface EditCredentialModalProps {
|
|
15
|
+
credential: Credential;
|
|
16
|
+
open: boolean;
|
|
17
|
+
onClose: () => void;
|
|
18
|
+
onSubmit: (data: UpdateCredentialInput) => void;
|
|
19
|
+
isLoading?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ALL_AUTH_TYPES: { value: CredentialAuthType; label: string }[] = [
|
|
23
|
+
{ value: 'bearer', label: 'Bearer Token' },
|
|
24
|
+
{ value: 'apiKey', label: 'API Key' },
|
|
25
|
+
{ value: 'basic', label: 'Basic Auth' },
|
|
26
|
+
{ value: 'oauth2', label: 'OAuth2' },
|
|
27
|
+
{ value: 'custom', label: 'Custom Headers' },
|
|
28
|
+
{ value: 'awsSigV4', label: 'AWS Signature V4' },
|
|
29
|
+
{ value: 'jwt', label: 'JWT' },
|
|
30
|
+
{ value: 'connectionString', label: 'Connection String' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function getAuthTypesForType(type: CredentialType) {
|
|
34
|
+
if (type === 'database') {
|
|
35
|
+
return ALL_AUTH_TYPES.filter((t) => ['basic', 'connectionString', 'oauth2'].includes(t.value));
|
|
36
|
+
}
|
|
37
|
+
if (type === 'llm') {
|
|
38
|
+
return ALL_AUTH_TYPES.filter((t) => ['apiKey', 'bearer'].includes(t.value));
|
|
39
|
+
}
|
|
40
|
+
// http-api
|
|
41
|
+
return ALL_AUTH_TYPES.filter((t) => !['connectionString'].includes(t.value));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const EditCredentialModal: React.FC<EditCredentialModalProps> = ({
|
|
45
|
+
credential,
|
|
46
|
+
open,
|
|
47
|
+
onClose,
|
|
48
|
+
onSubmit,
|
|
49
|
+
isLoading,
|
|
50
|
+
}) => {
|
|
51
|
+
const [formData, setFormData] = useState<UpdateCredentialInput>({
|
|
52
|
+
name: credential.name,
|
|
53
|
+
type: credential.type,
|
|
54
|
+
authType: credential.authType,
|
|
55
|
+
description: credential.description,
|
|
56
|
+
isActive: credential.isActive,
|
|
57
|
+
config: credential.config || {},
|
|
58
|
+
metadata: credential.metadata || {},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setFormData({
|
|
63
|
+
name: credential.name,
|
|
64
|
+
type: credential.type,
|
|
65
|
+
authType: credential.authType,
|
|
66
|
+
description: credential.description,
|
|
67
|
+
isActive: credential.isActive,
|
|
68
|
+
config: credential.config || {},
|
|
69
|
+
metadata: credential.metadata || {},
|
|
70
|
+
});
|
|
71
|
+
}, [credential]);
|
|
72
|
+
|
|
73
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
onSubmit(formData);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const updateConfig = (key: string, value: string) => {
|
|
79
|
+
setFormData((prev) => ({
|
|
80
|
+
...prev,
|
|
81
|
+
config: { ...prev.config, [key]: value },
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (!open) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="fixed inset-0 z-50">
|
|
91
|
+
<div className="fixed inset-0 bg-background/80" onClick={onClose} />
|
|
92
|
+
<div className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-50 w-full max-w-2xl">
|
|
93
|
+
<div className="grid w-full gap-4 border border-border bg-background p-6 shadow-lg sm:rounded-lg max-h-[90vh] overflow-y-auto">
|
|
94
|
+
<div className="flex items-center justify-between">
|
|
95
|
+
<h2 className="text-lg font-semibold">Edit Credential</h2>
|
|
96
|
+
<button
|
|
97
|
+
onClick={onClose}
|
|
98
|
+
className="rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
|
99
|
+
>
|
|
100
|
+
<X className="h-4 w-4" />
|
|
101
|
+
<span className="sr-only">Close</span>
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<form
|
|
106
|
+
onSubmit={handleSubmit}
|
|
107
|
+
className="space-y-4"
|
|
108
|
+
autoComplete="one-time-code"
|
|
109
|
+
data-lpignore="true"
|
|
110
|
+
>
|
|
111
|
+
{/* Name */}
|
|
112
|
+
<div className="space-y-2">
|
|
113
|
+
<Label htmlFor="name">Name *</Label>
|
|
114
|
+
<Input
|
|
115
|
+
id="name"
|
|
116
|
+
value={formData.name}
|
|
117
|
+
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
118
|
+
placeholder="My Stripe Production"
|
|
119
|
+
required
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Credential Type */}
|
|
124
|
+
<div className="space-y-2">
|
|
125
|
+
<Label htmlFor="type">Credential Type *</Label>
|
|
126
|
+
<select
|
|
127
|
+
id="type"
|
|
128
|
+
value={formData.type}
|
|
129
|
+
onChange={(e) => {
|
|
130
|
+
const newType = e.target.value as CredentialType;
|
|
131
|
+
if (newType === 'llm') {
|
|
132
|
+
setFormData({ ...formData, type: newType, authType: 'apiKey' });
|
|
133
|
+
} else {
|
|
134
|
+
const available = getAuthTypesForType(newType);
|
|
135
|
+
const nextAuthType = available.some((t) => t.value === formData.authType)
|
|
136
|
+
? formData.authType
|
|
137
|
+
: available[0]?.value || 'basic';
|
|
138
|
+
setFormData({ ...formData, type: newType, authType: nextAuthType });
|
|
139
|
+
}
|
|
140
|
+
}}
|
|
141
|
+
className="flex h-10 w-full rounded-md border border-input bg-background dark:bg-input/30 px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
142
|
+
required
|
|
143
|
+
>
|
|
144
|
+
<option value="http-api">HTTP API</option>
|
|
145
|
+
<option value="llm">LLM Provider</option>
|
|
146
|
+
<option value="database">Database</option>
|
|
147
|
+
</select>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{/* Auth Type (hidden for LLM — always apiKey) */}
|
|
151
|
+
{formData.type !== 'llm' && (
|
|
152
|
+
<div className="space-y-2">
|
|
153
|
+
<Label htmlFor="authType">Authentication Type *</Label>
|
|
154
|
+
<select
|
|
155
|
+
id="authType"
|
|
156
|
+
value={formData.authType}
|
|
157
|
+
onChange={(e) =>
|
|
158
|
+
setFormData({ ...formData, authType: e.target.value as CredentialAuthType })
|
|
159
|
+
}
|
|
160
|
+
className="flex h-10 w-full rounded-md border border-input bg-background dark:bg-input/30 px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
161
|
+
required
|
|
162
|
+
>
|
|
163
|
+
{getAuthTypesForType(formData.type || 'http-api').map((type) => (
|
|
164
|
+
<option key={type.value} value={type.value}>
|
|
165
|
+
{type.label}
|
|
166
|
+
</option>
|
|
167
|
+
))}
|
|
168
|
+
</select>
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
|
|
172
|
+
{/* Active Status */}
|
|
173
|
+
<div className="flex items-center gap-2">
|
|
174
|
+
<input
|
|
175
|
+
type="checkbox"
|
|
176
|
+
id="isActive"
|
|
177
|
+
checked={formData.isActive}
|
|
178
|
+
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}
|
|
179
|
+
className="h-4 w-4 rounded border-border"
|
|
180
|
+
/>
|
|
181
|
+
<Label htmlFor="isActive" className="cursor-pointer">
|
|
182
|
+
Active
|
|
183
|
+
</Label>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* LLM Provider selector (only shown for LLM type) */}
|
|
187
|
+
{formData.type === 'llm' && (
|
|
188
|
+
<div className="space-y-2">
|
|
189
|
+
<Label htmlFor="edit-llmProvider">LLM Provider</Label>
|
|
190
|
+
<select
|
|
191
|
+
id="edit-llmProvider"
|
|
192
|
+
value={(formData.metadata?.provider as string) || ''}
|
|
193
|
+
onChange={(e) =>
|
|
194
|
+
setFormData({
|
|
195
|
+
...formData,
|
|
196
|
+
metadata: { ...formData.metadata, provider: e.target.value },
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
className="flex h-10 w-full rounded-md border border-input bg-background dark:bg-input/30 px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
200
|
+
>
|
|
201
|
+
<option value="">Select a provider…</option>
|
|
202
|
+
<option value="openai">OpenAI</option>
|
|
203
|
+
<option value="anthropic">Anthropic</option>
|
|
204
|
+
<option value="openrouter">OpenRouter</option>
|
|
205
|
+
</select>
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
|
|
209
|
+
{/* Config fields based on auth type */}
|
|
210
|
+
<div className="space-y-4 p-4 bg-muted rounded-lg">
|
|
211
|
+
<h3 className="font-medium text-sm">Credentials</h3>
|
|
212
|
+
<p className="text-xs text-muted-foreground">
|
|
213
|
+
Leave fields empty to keep existing values
|
|
214
|
+
</p>
|
|
215
|
+
|
|
216
|
+
{/* LLM: plain API key only */}
|
|
217
|
+
{formData.type === 'llm' && (
|
|
218
|
+
<div className="space-y-2">
|
|
219
|
+
<Label htmlFor="apiKey">API Key</Label>
|
|
220
|
+
<Input
|
|
221
|
+
id="apiKey"
|
|
222
|
+
type="text"
|
|
223
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
224
|
+
value={(formData.config?.apiKey as string) || ''}
|
|
225
|
+
onChange={(e) => updateConfig('apiKey', e.target.value)}
|
|
226
|
+
placeholder="Enter API key or leave empty to keep current"
|
|
227
|
+
autoComplete="one-time-code"
|
|
228
|
+
data-1p-ignore
|
|
229
|
+
data-lpignore="true"
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{formData.type !== 'llm' && formData.authType === 'bearer' && (
|
|
235
|
+
<div className="space-y-2">
|
|
236
|
+
<Label htmlFor="token">Token</Label>
|
|
237
|
+
<Input
|
|
238
|
+
id="token"
|
|
239
|
+
type="text"
|
|
240
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
241
|
+
value={(formData.config?.token as string) || ''}
|
|
242
|
+
onChange={(e) => updateConfig('token', e.target.value)}
|
|
243
|
+
placeholder="Enter new bearer token or leave empty"
|
|
244
|
+
autoComplete="one-time-code"
|
|
245
|
+
data-1p-ignore
|
|
246
|
+
data-lpignore="true"
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
|
|
251
|
+
{formData.type !== 'llm' && formData.authType === 'apiKey' && (
|
|
252
|
+
<>
|
|
253
|
+
<div className="space-y-2">
|
|
254
|
+
<Label htmlFor="apiKey">API Key</Label>
|
|
255
|
+
<Input
|
|
256
|
+
id="apiKey"
|
|
257
|
+
type="text"
|
|
258
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
259
|
+
value={(formData.config?.apiKey as string) || ''}
|
|
260
|
+
onChange={(e) => updateConfig('apiKey', e.target.value)}
|
|
261
|
+
placeholder="Enter new API key or leave empty"
|
|
262
|
+
autoComplete="one-time-code"
|
|
263
|
+
data-1p-ignore
|
|
264
|
+
data-lpignore="true"
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
<div className="space-y-2">
|
|
268
|
+
<Label htmlFor="location">Location</Label>
|
|
269
|
+
<select
|
|
270
|
+
id="location"
|
|
271
|
+
value={(formData.config?.location as string) || 'header'}
|
|
272
|
+
onChange={(e) => updateConfig('location', e.target.value)}
|
|
273
|
+
className="flex h-10 w-full rounded-md border border-input bg-background dark:bg-input/30 px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]"
|
|
274
|
+
>
|
|
275
|
+
<option value="header">Header</option>
|
|
276
|
+
<option value="query">Query Parameter</option>
|
|
277
|
+
</select>
|
|
278
|
+
</div>
|
|
279
|
+
<div className="space-y-2">
|
|
280
|
+
<Label htmlFor="paramName">Parameter Name</Label>
|
|
281
|
+
<Input
|
|
282
|
+
id="paramName"
|
|
283
|
+
value={(formData.config?.paramName as string) || ''}
|
|
284
|
+
onChange={(e) => updateConfig('paramName', e.target.value)}
|
|
285
|
+
placeholder="X-API-Key"
|
|
286
|
+
autoComplete="one-time-code"
|
|
287
|
+
data-1p-ignore
|
|
288
|
+
data-lpignore="true"
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
</>
|
|
292
|
+
)}
|
|
293
|
+
|
|
294
|
+
{formData.authType === 'connectionString' && (
|
|
295
|
+
<div className="space-y-2">
|
|
296
|
+
<Label htmlFor="connectionString">Connection String</Label>
|
|
297
|
+
<Input
|
|
298
|
+
id="connectionString"
|
|
299
|
+
type="text"
|
|
300
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
301
|
+
value={(formData.config?.connectionString as string) || ''}
|
|
302
|
+
onChange={(e) => updateConfig('connectionString', e.target.value)}
|
|
303
|
+
placeholder="postgres://user:pass@host:5432/dbname?sslmode=require"
|
|
304
|
+
autoComplete="one-time-code"
|
|
305
|
+
data-1p-ignore
|
|
306
|
+
data-lpignore="true"
|
|
307
|
+
/>
|
|
308
|
+
</div>
|
|
309
|
+
)}
|
|
310
|
+
|
|
311
|
+
{formData.authType === 'basic' && (
|
|
312
|
+
<>
|
|
313
|
+
<div className="space-y-2">
|
|
314
|
+
<Label htmlFor="username">Username</Label>
|
|
315
|
+
<Input
|
|
316
|
+
id="username"
|
|
317
|
+
value={(formData.config?.username as string) || ''}
|
|
318
|
+
onChange={(e) => updateConfig('username', e.target.value)}
|
|
319
|
+
placeholder="Enter new username or leave empty"
|
|
320
|
+
autoComplete="one-time-code"
|
|
321
|
+
data-1p-ignore
|
|
322
|
+
data-lpignore="true"
|
|
323
|
+
/>
|
|
324
|
+
</div>
|
|
325
|
+
<div className="space-y-2">
|
|
326
|
+
<Label htmlFor="password">Password</Label>
|
|
327
|
+
<Input
|
|
328
|
+
id="password"
|
|
329
|
+
type="text"
|
|
330
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
331
|
+
value={(formData.config?.password as string) || ''}
|
|
332
|
+
onChange={(e) => updateConfig('password', e.target.value)}
|
|
333
|
+
placeholder="Enter new password or leave empty"
|
|
334
|
+
autoComplete="one-time-code"
|
|
335
|
+
data-1p-ignore
|
|
336
|
+
data-lpignore="true"
|
|
337
|
+
/>
|
|
338
|
+
</div>
|
|
339
|
+
</>
|
|
340
|
+
)}
|
|
341
|
+
|
|
342
|
+
{formData.authType === 'oauth2' && (
|
|
343
|
+
<>
|
|
344
|
+
<div className="space-y-2">
|
|
345
|
+
<Label htmlFor="clientId">Client ID</Label>
|
|
346
|
+
<Input
|
|
347
|
+
id="clientId"
|
|
348
|
+
value={(formData.config?.clientId as string) || ''}
|
|
349
|
+
onChange={(e) => updateConfig('clientId', e.target.value)}
|
|
350
|
+
placeholder="Enter client ID or leave empty"
|
|
351
|
+
autoComplete="one-time-code"
|
|
352
|
+
data-1p-ignore
|
|
353
|
+
data-lpignore="true"
|
|
354
|
+
/>
|
|
355
|
+
</div>
|
|
356
|
+
<div className="space-y-2">
|
|
357
|
+
<Label htmlFor="clientSecret">Client Secret</Label>
|
|
358
|
+
<Input
|
|
359
|
+
id="clientSecret"
|
|
360
|
+
type="text"
|
|
361
|
+
style={{ WebkitTextSecurity: 'disc' }}
|
|
362
|
+
value={(formData.config?.clientSecret as string) || ''}
|
|
363
|
+
onChange={(e) => updateConfig('clientSecret', e.target.value)}
|
|
364
|
+
placeholder="Enter client secret or leave empty"
|
|
365
|
+
autoComplete="one-time-code"
|
|
366
|
+
data-1p-ignore
|
|
367
|
+
data-lpignore="true"
|
|
368
|
+
/>
|
|
369
|
+
</div>
|
|
370
|
+
</>
|
|
371
|
+
)}
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
{/* Description */}
|
|
375
|
+
<div className="space-y-2">
|
|
376
|
+
<Label htmlFor="description">Description</Label>
|
|
377
|
+
<Textarea
|
|
378
|
+
id="description"
|
|
379
|
+
value={formData.description || ''}
|
|
380
|
+
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|
381
|
+
placeholder="Optional description for this credential"
|
|
382
|
+
rows={3}
|
|
383
|
+
/>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<div className="flex justify-end gap-2 pt-4">
|
|
387
|
+
<Button type="button" variant="outline" onClick={onClose}>
|
|
388
|
+
Cancel
|
|
389
|
+
</Button>
|
|
390
|
+
<Button type="submit" disabled={isLoading}>
|
|
391
|
+
{isLoading ? 'Saving...' : 'Save Changes'}
|
|
392
|
+
</Button>
|
|
393
|
+
</div>
|
|
394
|
+
</form>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
);
|
|
399
|
+
};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 Connect Button Component
|
|
3
|
+
*
|
|
4
|
+
* A button that initiates OAuth2 authorization flow for a specific provider.
|
|
5
|
+
* Opens a popup window for the user to authorize, then handles the callback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
9
|
+
import { Button } from '../ui/button';
|
|
10
|
+
import { Loader2, ExternalLink, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
11
|
+
import { cn } from '../../lib/utils';
|
|
12
|
+
import { useHandleOAuth2Callback, useStartOAuth2Flow } from '../../api/credentials.api';
|
|
13
|
+
import type { OAuth2ProviderDefinition, Credential } from '../../api/types';
|
|
14
|
+
|
|
15
|
+
export interface OAuth2ConnectButtonProps {
|
|
16
|
+
/** The OAuth2 provider to connect to */
|
|
17
|
+
provider: OAuth2ProviderDefinition;
|
|
18
|
+
/** OAuth2 client ID (from your app's OAuth configuration) */
|
|
19
|
+
clientId: string;
|
|
20
|
+
/** OAuth2 client secret (from your app's OAuth configuration) */
|
|
21
|
+
clientSecret: string;
|
|
22
|
+
/** Redirect URI registered with the OAuth provider */
|
|
23
|
+
redirectUri: string;
|
|
24
|
+
/** Optional: Custom scopes to request (defaults to provider's default scopes) */
|
|
25
|
+
scopes?: string[];
|
|
26
|
+
/** Optional: Custom name for the created credential */
|
|
27
|
+
credentialName?: string;
|
|
28
|
+
/** Called when credential is successfully created */
|
|
29
|
+
onSuccess?: (credential: Credential) => void;
|
|
30
|
+
/** Called when an error occurs */
|
|
31
|
+
onError?: (error: Error) => void;
|
|
32
|
+
/** Button variant */
|
|
33
|
+
variant?: 'default' | 'outline' | 'secondary' | 'ghost';
|
|
34
|
+
/** Button size */
|
|
35
|
+
size?: 'default' | 'sm' | 'lg' | 'icon';
|
|
36
|
+
/** Additional CSS classes */
|
|
37
|
+
className?: string;
|
|
38
|
+
/** Disabled state */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Children to render inside button (defaults to "Connect with {provider.name}") */
|
|
41
|
+
children?: React.ReactNode;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type ConnectionStatus = 'idle' | 'connecting' | 'success' | 'error';
|
|
45
|
+
|
|
46
|
+
export function OAuth2ConnectButton({
|
|
47
|
+
provider,
|
|
48
|
+
clientId,
|
|
49
|
+
clientSecret,
|
|
50
|
+
redirectUri,
|
|
51
|
+
scopes,
|
|
52
|
+
credentialName,
|
|
53
|
+
onSuccess,
|
|
54
|
+
onError,
|
|
55
|
+
variant = 'outline',
|
|
56
|
+
size = 'default',
|
|
57
|
+
className,
|
|
58
|
+
disabled,
|
|
59
|
+
children,
|
|
60
|
+
}: OAuth2ConnectButtonProps) {
|
|
61
|
+
const startOAuth2FlowMutation = useStartOAuth2Flow();
|
|
62
|
+
const handleOAuth2CallbackMutation = useHandleOAuth2Callback();
|
|
63
|
+
const [status, setStatus] = useState<ConnectionStatus>('idle');
|
|
64
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
65
|
+
const [popupWindow, setPopupWindow] = useState<Window | null>(null);
|
|
66
|
+
|
|
67
|
+
// Store callback params in refs so we can access them in the message handler
|
|
68
|
+
const callbackParamsRef = useRef({ clientId, clientSecret, redirectUri });
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
callbackParamsRef.current = { clientId, clientSecret, redirectUri };
|
|
71
|
+
}, [clientId, clientSecret, redirectUri]);
|
|
72
|
+
|
|
73
|
+
// Store callbacks in refs to avoid stale closures in the message handler
|
|
74
|
+
const onSuccessRef = useRef(onSuccess);
|
|
75
|
+
const onErrorRef = useRef(onError);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
onSuccessRef.current = onSuccess;
|
|
78
|
+
onErrorRef.current = onError;
|
|
79
|
+
}, [onSuccess, onError]);
|
|
80
|
+
|
|
81
|
+
// Listen for OAuth callback message from popup
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const handleMessage = async (event: MessageEvent) => {
|
|
84
|
+
// Verify origin for security
|
|
85
|
+
if (event.origin !== window.location.origin) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { type, code, state, error } = event.data;
|
|
90
|
+
|
|
91
|
+
if (type !== 'oauth2_callback') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Close the popup
|
|
96
|
+
if (popupWindow && !popupWindow.closed) {
|
|
97
|
+
popupWindow.close();
|
|
98
|
+
}
|
|
99
|
+
setPopupWindow(null);
|
|
100
|
+
|
|
101
|
+
if (error) {
|
|
102
|
+
setStatus('error');
|
|
103
|
+
setErrorMessage(error);
|
|
104
|
+
onErrorRef.current?.(new Error(error));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!code || !state) {
|
|
109
|
+
setStatus('error');
|
|
110
|
+
setErrorMessage('Invalid OAuth callback - missing code or state');
|
|
111
|
+
onErrorRef.current?.(new Error('Invalid OAuth callback'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Exchange code for tokens using the mutation (which invalidates credentials cache)
|
|
116
|
+
try {
|
|
117
|
+
const params = callbackParamsRef.current;
|
|
118
|
+
const credential = await handleOAuth2CallbackMutation.mutateAsync({
|
|
119
|
+
code,
|
|
120
|
+
state,
|
|
121
|
+
clientId: params.clientId,
|
|
122
|
+
clientSecret: params.clientSecret,
|
|
123
|
+
redirectUri: params.redirectUri,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
setStatus('success');
|
|
127
|
+
onSuccessRef.current?.(credential);
|
|
128
|
+
|
|
129
|
+
// Reset to idle after showing success
|
|
130
|
+
setTimeout(() => setStatus('idle'), 2000);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
setStatus('error');
|
|
133
|
+
const message = err instanceof Error ? err.message : 'Failed to exchange OAuth code';
|
|
134
|
+
setErrorMessage(message);
|
|
135
|
+
onErrorRef.current?.(err instanceof Error ? err : new Error(message));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
window.addEventListener('message', handleMessage);
|
|
140
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
141
|
+
}, [popupWindow, handleOAuth2CallbackMutation]);
|
|
142
|
+
|
|
143
|
+
// Check if popup was closed without completing
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!popupWindow) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const checkClosed = setInterval(() => {
|
|
150
|
+
if (popupWindow.closed) {
|
|
151
|
+
clearInterval(checkClosed);
|
|
152
|
+
setPopupWindow(null);
|
|
153
|
+
if (status === 'connecting') {
|
|
154
|
+
setStatus('idle');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}, 500);
|
|
158
|
+
|
|
159
|
+
return () => clearInterval(checkClosed);
|
|
160
|
+
}, [popupWindow, status]);
|
|
161
|
+
|
|
162
|
+
const handleConnect = useCallback(async () => {
|
|
163
|
+
setStatus('connecting');
|
|
164
|
+
setErrorMessage(null);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Start OAuth flow to get authorization URL
|
|
168
|
+
const result = await startOAuth2FlowMutation.mutateAsync({
|
|
169
|
+
providerId: provider.id,
|
|
170
|
+
clientId,
|
|
171
|
+
clientSecret,
|
|
172
|
+
redirectUri,
|
|
173
|
+
scopes,
|
|
174
|
+
credentialName,
|
|
175
|
+
returnUrl: window.location.href,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Open popup window for OAuth
|
|
179
|
+
const width = 600;
|
|
180
|
+
const height = 700;
|
|
181
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
182
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
183
|
+
|
|
184
|
+
const popup = window.open(
|
|
185
|
+
result.authorizationUrl,
|
|
186
|
+
`oauth2_${provider.id}`,
|
|
187
|
+
`width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no`,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (!popup) {
|
|
191
|
+
throw new Error('Failed to open popup window. Please allow popups for this site.');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setPopupWindow(popup);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
setStatus('error');
|
|
197
|
+
const message = err instanceof Error ? err.message : 'Failed to start OAuth flow';
|
|
198
|
+
setErrorMessage(message);
|
|
199
|
+
onError?.(err instanceof Error ? err : new Error(message));
|
|
200
|
+
}
|
|
201
|
+
}, [
|
|
202
|
+
startOAuth2FlowMutation,
|
|
203
|
+
provider.id,
|
|
204
|
+
clientId,
|
|
205
|
+
clientSecret,
|
|
206
|
+
redirectUri,
|
|
207
|
+
scopes,
|
|
208
|
+
credentialName,
|
|
209
|
+
onError,
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
const isDisabled = disabled || status === 'connecting';
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div className="flex flex-col gap-1">
|
|
216
|
+
<Button
|
|
217
|
+
type="button"
|
|
218
|
+
variant={variant}
|
|
219
|
+
size={size}
|
|
220
|
+
onClick={handleConnect}
|
|
221
|
+
disabled={isDisabled}
|
|
222
|
+
className={cn(
|
|
223
|
+
'gap-2',
|
|
224
|
+
status === 'success' && 'bg-success/10 border-success/50 text-success',
|
|
225
|
+
status === 'error' && 'bg-destructive/10 border-destructive/50 text-destructive',
|
|
226
|
+
className,
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
229
|
+
{status === 'connecting' && <Loader2 className="w-4 h-4 animate-spin" />}
|
|
230
|
+
{status === 'success' && <CheckCircle2 className="w-4 h-4" />}
|
|
231
|
+
{status === 'error' && <AlertCircle className="w-4 h-4" />}
|
|
232
|
+
{status === 'idle' && <ExternalLink className="w-4 h-4" />}
|
|
233
|
+
|
|
234
|
+
{children ??
|
|
235
|
+
(status === 'success'
|
|
236
|
+
? 'Connected!'
|
|
237
|
+
: status === 'error'
|
|
238
|
+
? 'Connection Failed'
|
|
239
|
+
: `Connect with ${provider.name}`)}
|
|
240
|
+
</Button>
|
|
241
|
+
|
|
242
|
+
{status === 'error' && errorMessage && (
|
|
243
|
+
<p className="text-xs text-destructive">{errorMessage}</p>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* OAuth2 Callback Handler Component
|
|
251
|
+
*
|
|
252
|
+
* Place this component on your OAuth callback page.
|
|
253
|
+
* It extracts the code/state from URL and sends it to the parent window.
|
|
254
|
+
*/
|
|
255
|
+
export function OAuth2CallbackHandler() {
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const params = new URLSearchParams(window.location.search);
|
|
258
|
+
const code = params.get('code');
|
|
259
|
+
const state = params.get('state');
|
|
260
|
+
const error = params.get('error');
|
|
261
|
+
const errorDescription = params.get('error_description');
|
|
262
|
+
|
|
263
|
+
// Send message to parent window
|
|
264
|
+
if (window.opener) {
|
|
265
|
+
window.opener.postMessage(
|
|
266
|
+
{
|
|
267
|
+
type: 'oauth2_callback',
|
|
268
|
+
code,
|
|
269
|
+
state,
|
|
270
|
+
error: error || errorDescription,
|
|
271
|
+
},
|
|
272
|
+
window.location.origin,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Close this window after a short delay
|
|
277
|
+
setTimeout(() => window.close(), 100);
|
|
278
|
+
}, []);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
282
|
+
<div className="text-center">
|
|
283
|
+
<Loader2 className="w-8 h-8 mx-auto mb-4 animate-spin text-muted-foreground" />
|
|
284
|
+
<p className="text-sm text-muted-foreground">Completing authorization...</p>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
}
|