@agent-native/core 0.15.12 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/agent/app-model-defaults.d.ts +37 -0
- package/dist/agent/app-model-defaults.d.ts.map +1 -0
- package/dist/agent/app-model-defaults.js +136 -0
- package/dist/agent/app-model-defaults.js.map +1 -0
- package/dist/agent/engine/registry.d.ts +12 -7
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +28 -8
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +3 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/browser-sessions/actions.d.ts +7 -0
- package/dist/browser-sessions/actions.d.ts.map +1 -0
- package/dist/browser-sessions/actions.js +223 -0
- package/dist/browser-sessions/actions.js.map +1 -0
- package/dist/browser-sessions/routes.d.ts +7 -0
- package/dist/browser-sessions/routes.d.ts.map +1 -0
- package/dist/browser-sessions/routes.js +159 -0
- package/dist/browser-sessions/routes.js.map +1 -0
- package/dist/browser-sessions/store.d.ts +33 -0
- package/dist/browser-sessions/store.d.ts.map +1 -0
- package/dist/browser-sessions/store.js +506 -0
- package/dist/browser-sessions/store.js.map +1 -0
- package/dist/browser-sessions/types.d.ts +68 -0
- package/dist/browser-sessions/types.d.ts.map +1 -0
- package/dist/browser-sessions/types.js +2 -0
- package/dist/browser-sessions/types.js.map +1 -0
- package/dist/cli/code-agent-commands.d.ts +36 -0
- package/dist/cli/code-agent-commands.d.ts.map +1 -0
- package/dist/cli/code-agent-commands.js +192 -0
- package/dist/cli/code-agent-commands.js.map +1 -0
- package/dist/cli/code-agent-connector.d.ts +17 -0
- package/dist/cli/code-agent-connector.d.ts.map +1 -0
- package/dist/cli/code-agent-connector.js +724 -0
- package/dist/cli/code-agent-connector.js.map +1 -0
- package/dist/cli/code-agent-executor.d.ts +31 -0
- package/dist/cli/code-agent-executor.d.ts.map +1 -0
- package/dist/cli/code-agent-executor.js +921 -0
- package/dist/cli/code-agent-executor.js.map +1 -0
- package/dist/cli/code-agent-runs.d.ts +102 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -0
- package/dist/cli/code-agent-runs.js +277 -0
- package/dist/cli/code-agent-runs.js.map +1 -0
- package/dist/cli/code.d.ts +66 -0
- package/dist/cli/code.d.ts.map +1 -0
- package/dist/cli/code.js +1306 -0
- package/dist/cli/code.js.map +1 -0
- package/dist/cli/create.d.ts +2 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +11 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +26 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/migrate.d.ts +27 -0
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +1328 -20
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +27 -3
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/workspacify.d.ts +2 -0
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +2 -1
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/AgentNative.d.ts +32 -0
- package/dist/client/AgentNative.d.ts.map +1 -0
- package/dist/client/AgentNative.js +79 -0
- package/dist/client/AgentNative.js.map +1 -0
- package/dist/client/AgentNativeEmbedded.d.ts +47 -0
- package/dist/client/AgentNativeEmbedded.d.ts.map +1 -0
- package/dist/client/AgentNativeEmbedded.js +148 -0
- package/dist/client/AgentNativeEmbedded.js.map +1 -0
- package/dist/client/AgentNativeFrame.d.ts +25 -0
- package/dist/client/AgentNativeFrame.d.ts.map +1 -0
- package/dist/client/AgentNativeFrame.js +68 -0
- package/dist/client/AgentNativeFrame.js.map +1 -0
- package/dist/client/AgentPanel.d.ts +19 -2
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +15 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +79 -48
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +2 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +3 -2
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/browser-session-bridge.d.ts +44 -0
- package/dist/client/browser-session-bridge.d.ts.map +1 -0
- package/dist/client/browser-session-bridge.js +339 -0
- package/dist/client/browser-session-bridge.js.map +1 -0
- package/dist/client/client-action.d.ts +7 -0
- package/dist/client/client-action.d.ts.map +1 -0
- package/dist/client/client-action.js +4 -0
- package/dist/client/client-action.js.map +1 -0
- package/dist/client/components/ui/tooltip.d.ts.map +1 -1
- package/dist/client/components/ui/tooltip.js +1 -1
- package/dist/client/components/ui/tooltip.js.map +1 -1
- package/dist/client/composer/AgentComposerFrame.d.ts +17 -0
- package/dist/client/composer/AgentComposerFrame.d.ts.map +1 -0
- package/dist/client/composer/AgentComposerFrame.js +14 -0
- package/dist/client/composer/AgentComposerFrame.js.map +1 -0
- package/dist/client/composer/MentionPopover.d.ts.map +1 -1
- package/dist/client/composer/MentionPopover.js +2 -2
- package/dist/client/composer/MentionPopover.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts +35 -2
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +31 -17
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +20 -2
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +112 -22
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/index.d.ts +3 -2
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -0
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/types.d.ts +1 -0
- package/dist/client/composer/types.d.ts.map +1 -1
- package/dist/client/composer/types.js.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts +58 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.d.ts +2 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.d.ts.map +1 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.js +110 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.js.map +1 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.js +354 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.js.map +1 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.d.ts +2 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.d.ts.map +1 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.js +68 -0
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.js.map +1 -0
- package/dist/client/extensions/agent-native-extension-runtime.d.ts +69 -0
- package/dist/client/extensions/agent-native-extension-runtime.d.ts.map +1 -0
- package/dist/client/extensions/agent-native-extension-runtime.js +348 -0
- package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -0
- package/dist/client/extensions/index.d.ts +2 -0
- package/dist/client/extensions/index.d.ts.map +1 -1
- package/dist/client/extensions/index.js +2 -0
- package/dist/client/extensions/index.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts +86 -0
- package/dist/client/extensions/portable-extension.d.ts.map +1 -0
- package/dist/client/extensions/portable-extension.js +480 -0
- package/dist/client/extensions/portable-extension.js.map +1 -0
- package/dist/client/host-bridge.d.ts +266 -0
- package/dist/client/host-bridge.d.ts.map +1 -0
- package/dist/client/host-bridge.js +745 -0
- package/dist/client/host-bridge.js.map +1 -0
- package/dist/client/host-tools.d.ts +40 -0
- package/dist/client/host-tools.d.ts.map +1 -0
- package/dist/client/host-tools.js +94 -0
- package/dist/client/host-tools.js.map +1 -0
- package/dist/client/index.d.ts +13 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +11 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.d.ts +10 -0
- package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -0
- package/dist/client/resources/BuiltinCapabilityDetail.js +51 -0
- package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -0
- package/dist/client/resources/ResourceEditor.d.ts +3 -1
- package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
- package/dist/client/resources/ResourceEditor.js +40 -17
- package/dist/client/resources/ResourceEditor.js.map +1 -1
- package/dist/client/resources/ResourceTree.d.ts.map +1 -1
- package/dist/client/resources/ResourceTree.js +23 -2
- package/dist/client/resources/ResourceTree.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +77 -17
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/index.d.ts +1 -0
- package/dist/client/resources/index.d.ts.map +1 -1
- package/dist/client/resources/index.js +1 -0
- package/dist/client/resources/index.js.map +1 -1
- package/dist/client/resources/use-builtin-capabilities.d.ts +62 -0
- package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -0
- package/dist/client/resources/use-builtin-capabilities.js +54 -0
- package/dist/client/resources/use-builtin-capabilities.js.map +1 -0
- package/dist/client/resources/use-resources.d.ts +30 -2
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +42 -1
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +151 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +20 -0
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +46 -2
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-chat-threads.spec.js +77 -0
- package/dist/client/use-chat-threads.spec.js.map +1 -1
- package/dist/code-agents/index.d.ts +4 -0
- package/dist/code-agents/index.d.ts.map +1 -0
- package/dist/code-agents/index.js +4 -0
- package/dist/code-agents/index.js.map +1 -0
- package/dist/connections/catalog.d.ts +134 -0
- package/dist/connections/catalog.d.ts.map +1 -0
- package/dist/connections/catalog.js +180 -0
- package/dist/connections/catalog.js.map +1 -0
- package/dist/connections/index.d.ts +2 -0
- package/dist/connections/index.d.ts.map +1 -0
- package/dist/connections/index.js +2 -0
- package/dist/connections/index.js.map +1 -0
- package/dist/extensions/change-marker.d.ts +10 -0
- package/dist/extensions/change-marker.d.ts.map +1 -0
- package/dist/extensions/change-marker.js +42 -0
- package/dist/extensions/change-marker.js.map +1 -0
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +1 -7
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +1 -0
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +1 -0
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/slots/routes.js +1 -1
- package/dist/extensions/slots/routes.js.map +1 -1
- package/dist/extensions/store.d.ts +3 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +112 -4
- package/dist/extensions/store.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/telegram.d.ts.map +1 -1
- package/dist/integrations/adapters/telegram.js +1 -0
- package/dist/integrations/adapters/telegram.js.map +1 -1
- package/dist/integrations/index.d.ts +5 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +4 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/plugin.d.ts +8 -0
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +760 -32
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts +36 -0
- package/dist/integrations/remote-commands-store.d.ts.map +1 -0
- package/dist/integrations/remote-commands-store.js +273 -0
- package/dist/integrations/remote-commands-store.js.map +1 -0
- package/dist/integrations/remote-devices-store.d.ts +43 -0
- package/dist/integrations/remote-devices-store.d.ts.map +1 -0
- package/dist/integrations/remote-devices-store.js +315 -0
- package/dist/integrations/remote-devices-store.js.map +1 -0
- package/dist/integrations/remote-push-store.d.ts +37 -0
- package/dist/integrations/remote-push-store.d.ts.map +1 -0
- package/dist/integrations/remote-push-store.js +299 -0
- package/dist/integrations/remote-push-store.js.map +1 -0
- package/dist/integrations/remote-retry-job.d.ts +7 -0
- package/dist/integrations/remote-retry-job.d.ts.map +1 -0
- package/dist/integrations/remote-retry-job.js +45 -0
- package/dist/integrations/remote-retry-job.js.map +1 -0
- package/dist/integrations/remote-run-events-store.d.ts +18 -0
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -0
- package/dist/integrations/remote-run-events-store.js +82 -0
- package/dist/integrations/remote-run-events-store.js.map +1 -0
- package/dist/integrations/remote-types.d.ts +101 -0
- package/dist/integrations/remote-types.d.ts.map +1 -0
- package/dist/integrations/remote-types.js +2 -0
- package/dist/integrations/remote-types.js.map +1 -0
- package/dist/integrations/webhook-handler.d.ts +2 -0
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +4 -1
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.d.ts +4 -2
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +9 -3
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/mcp-client/builtin-capabilities.d.ts +20 -0
- package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -0
- package/dist/mcp-client/builtin-capabilities.js +75 -0
- package/dist/mcp-client/builtin-capabilities.js.map +1 -0
- package/dist/mcp-client/builtin-store.d.ts +10 -0
- package/dist/mcp-client/builtin-store.d.ts.map +1 -0
- package/dist/mcp-client/builtin-store.js +55 -0
- package/dist/mcp-client/builtin-store.js.map +1 -0
- package/dist/mcp-client/index.d.ts +3 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +3 -1
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +28 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +195 -1
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +34 -0
- package/dist/org/context.js.map +1 -1
- package/dist/resources/handlers.d.ts +4 -0
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +46 -7
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/script-helpers.d.ts +8 -1
- package/dist/resources/script-helpers.d.ts.map +1 -1
- package/dist/resources/script-helpers.js +18 -8
- package/dist/resources/script-helpers.js.map +1 -1
- package/dist/resources/store.d.ts +19 -0
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +86 -3
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +18 -7
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +100 -7
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/resources/delete.d.ts.map +1 -1
- package/dist/scripts/resources/delete.js +4 -1
- package/dist/scripts/resources/delete.js.map +1 -1
- package/dist/scripts/resources/effective.d.ts +11 -0
- package/dist/scripts/resources/effective.d.ts.map +1 -0
- package/dist/scripts/resources/effective.js +54 -0
- package/dist/scripts/resources/effective.js.map +1 -0
- package/dist/scripts/resources/index.d.ts.map +1 -1
- package/dist/scripts/resources/index.js +1 -0
- package/dist/scripts/resources/index.js.map +1 -1
- package/dist/scripts/resources/list.d.ts +1 -1
- package/dist/scripts/resources/list.d.ts.map +1 -1
- package/dist/scripts/resources/list.js +17 -5
- package/dist/scripts/resources/list.js.map +1 -1
- package/dist/scripts/resources/read.d.ts +1 -1
- package/dist/scripts/resources/read.d.ts.map +1 -1
- package/dist/scripts/resources/read.js +20 -5
- package/dist/scripts/resources/read.js.map +1 -1
- package/dist/scripts/resources/write.d.ts.map +1 -1
- package/dist/scripts/resources/write.js +4 -1
- package/dist/scripts/resources/write.js.map +1 -1
- package/dist/scripts/runner.d.ts +11 -1
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +75 -27
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +24 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +608 -54
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/embedded.d.ts +72 -0
- package/dist/server/embedded.d.ts.map +1 -0
- package/dist/server/embedded.js +119 -0
- package/dist/server/embedded.js.map +1 -0
- package/dist/server/index.d.ts +8 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +184 -13
- package/dist/server/poll.js.map +1 -1
- package/dist/server/resources-plugin.d.ts.map +1 -1
- package/dist/server/resources-plugin.js +11 -2
- package/dist/server/resources-plugin.js.map +1 -1
- package/dist/sharing/actions/extension-change.d.ts +4 -0
- package/dist/sharing/actions/extension-change.d.ts.map +1 -0
- package/dist/sharing/actions/extension-change.js +13 -0
- package/dist/sharing/actions/extension-change.js.map +1 -0
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +3 -0
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/dist/sharing/actions/share-resource.d.ts.map +1 -1
- package/dist/sharing/actions/share-resource.js +4 -0
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/sharing/actions/unshare-resource.d.ts.map +1 -1
- package/dist/sharing/actions/unshare-resource.js +3 -0
- package/dist/sharing/actions/unshare-resource.js.map +1 -1
- package/dist/templates/default/AGENTS.md +3 -3
- package/dist/templates/workspace-core/AGENTS.md +7 -0
- package/dist/templates/workspace-root/AGENTS.md +7 -0
- package/dist/templates/workspace-root/README.md +25 -0
- package/dist/triggers/dispatcher.d.ts +3 -1
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +9 -3
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/workspace-connections/index.d.ts +2 -0
- package/dist/workspace-connections/index.d.ts.map +1 -0
- package/dist/workspace-connections/index.js +2 -0
- package/dist/workspace-connections/index.js.map +1 -0
- package/dist/workspace-connections/store.d.ts +229 -0
- package/dist/workspace-connections/store.d.ts.map +1 -0
- package/dist/workspace-connections/store.js +938 -0
- package/dist/workspace-connections/store.js.map +1 -0
- package/docs/content/agent-teams.md +6 -0
- package/docs/content/cloneable-saas.md +1 -0
- package/docs/content/code-agents-ui.md +261 -0
- package/docs/content/dispatch.md +40 -3
- package/docs/content/embedding-sdk.md +459 -0
- package/docs/content/faq.md +1 -0
- package/docs/content/getting-started.md +1 -0
- package/docs/content/mcp-clients.md +41 -3
- package/docs/content/migration-workbench.md +237 -54
- package/docs/content/multi-app-workspace.md +41 -0
- package/docs/content/multi-tenancy.md +1 -1
- package/docs/content/template-brain.md +418 -0
- package/docs/content/template-dispatch.md +30 -0
- package/docs/content/workspace-connections.md +509 -0
- package/docs/content/workspace-management.md +12 -12
- package/docs/content/workspace.md +180 -40
- package/package.json +7 -2
- package/src/templates/default/AGENTS.md +3 -3
- package/src/templates/workspace-core/AGENTS.md +7 -0
- package/src/templates/workspace-root/AGENTS.md +7 -0
- package/src/templates/workspace-root/README.md +25 -0
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { getDbExec, intType, isPostgres, isUniqueViolation, retryOnDdlRace, safeJsonParse, } from "../db/client.js";
|
|
3
|
+
import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
4
|
+
import { listWorkspaceConnectionProviders, } from "../connections/catalog.js";
|
|
5
|
+
let _initPromise;
|
|
6
|
+
function workspaceConnectionsTable() {
|
|
7
|
+
return isPostgres()
|
|
8
|
+
? "public.workspace_connections"
|
|
9
|
+
: "workspace_connections";
|
|
10
|
+
}
|
|
11
|
+
function workspaceConnectionGrantsTable() {
|
|
12
|
+
return isPostgres()
|
|
13
|
+
? "public.workspace_connection_grants"
|
|
14
|
+
: "workspace_connection_grants";
|
|
15
|
+
}
|
|
16
|
+
function isDuplicateColumnError(err) {
|
|
17
|
+
const code = String(err?.code ?? "");
|
|
18
|
+
const message = String(err?.message ?? err)
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.trim();
|
|
21
|
+
return (code === "42701" ||
|
|
22
|
+
message.includes("duplicate column") ||
|
|
23
|
+
message.includes("already exists"));
|
|
24
|
+
}
|
|
25
|
+
async function ensureColumn(client, table, name, definition) {
|
|
26
|
+
try {
|
|
27
|
+
await retryOnDdlRace(() => client.execute(isPostgres()
|
|
28
|
+
? `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${name} ${definition}`
|
|
29
|
+
: `ALTER TABLE ${table} ADD COLUMN ${name} ${definition}`));
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (!isDuplicateColumnError(err))
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function ensureWorkspaceConnectionColumns(client, table) {
|
|
37
|
+
await ensureColumn(client, table, "provider", "TEXT NOT NULL DEFAULT ''");
|
|
38
|
+
await ensureColumn(client, table, "label", "TEXT NOT NULL DEFAULT ''");
|
|
39
|
+
await ensureColumn(client, table, "account_id", "TEXT");
|
|
40
|
+
await ensureColumn(client, table, "account_label", "TEXT");
|
|
41
|
+
await ensureColumn(client, table, "status", "TEXT NOT NULL DEFAULT 'connected'");
|
|
42
|
+
await ensureColumn(client, table, "scopes_json", "TEXT NOT NULL DEFAULT '[]'");
|
|
43
|
+
await ensureColumn(client, table, "config_json", "TEXT NOT NULL DEFAULT '{}'");
|
|
44
|
+
await ensureColumn(client, table, "allowed_apps_json", "TEXT NOT NULL DEFAULT '[]'");
|
|
45
|
+
await ensureColumn(client, table, "credential_refs_json", "TEXT NOT NULL DEFAULT '[]'");
|
|
46
|
+
await ensureColumn(client, table, "owner_email", "TEXT NOT NULL DEFAULT ''");
|
|
47
|
+
await ensureColumn(client, table, "org_id", "TEXT");
|
|
48
|
+
await ensureColumn(client, table, "created_at", `${intType()} NOT NULL DEFAULT 0`);
|
|
49
|
+
await ensureColumn(client, table, "updated_at", `${intType()} NOT NULL DEFAULT 0`);
|
|
50
|
+
await ensureColumn(client, table, "last_checked_at", intType());
|
|
51
|
+
await ensureColumn(client, table, "last_error", "TEXT");
|
|
52
|
+
}
|
|
53
|
+
async function ensureWorkspaceConnectionGrantColumns(client, table) {
|
|
54
|
+
await ensureColumn(client, table, "connection_id", "TEXT NOT NULL DEFAULT ''");
|
|
55
|
+
await ensureColumn(client, table, "provider", "TEXT NOT NULL DEFAULT ''");
|
|
56
|
+
await ensureColumn(client, table, "app_id", "TEXT NOT NULL DEFAULT ''");
|
|
57
|
+
await ensureColumn(client, table, "scopes_json", "TEXT NOT NULL DEFAULT '[]'");
|
|
58
|
+
await ensureColumn(client, table, "config_json", "TEXT NOT NULL DEFAULT '{}'");
|
|
59
|
+
await ensureColumn(client, table, "credential_refs_json", "TEXT NOT NULL DEFAULT '[]'");
|
|
60
|
+
await ensureColumn(client, table, "granted_by_email", "TEXT NOT NULL DEFAULT ''");
|
|
61
|
+
await ensureColumn(client, table, "owner_email", "TEXT NOT NULL DEFAULT ''");
|
|
62
|
+
await ensureColumn(client, table, "org_id", "TEXT");
|
|
63
|
+
await ensureColumn(client, table, "created_at", `${intType()} NOT NULL DEFAULT 0`);
|
|
64
|
+
await ensureColumn(client, table, "updated_at", `${intType()} NOT NULL DEFAULT 0`);
|
|
65
|
+
}
|
|
66
|
+
export async function ensureWorkspaceConnectionsTable() {
|
|
67
|
+
if (!_initPromise) {
|
|
68
|
+
_initPromise = (async () => {
|
|
69
|
+
const client = getDbExec();
|
|
70
|
+
const table = workspaceConnectionsTable();
|
|
71
|
+
const grantsTable = workspaceConnectionGrantsTable();
|
|
72
|
+
await retryOnDdlRace(() => client.execute(`
|
|
73
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
74
|
+
id TEXT PRIMARY KEY,
|
|
75
|
+
provider TEXT NOT NULL DEFAULT '',
|
|
76
|
+
label TEXT NOT NULL DEFAULT '',
|
|
77
|
+
account_id TEXT,
|
|
78
|
+
account_label TEXT,
|
|
79
|
+
status TEXT NOT NULL DEFAULT 'connected',
|
|
80
|
+
scopes_json TEXT NOT NULL DEFAULT '[]',
|
|
81
|
+
config_json TEXT NOT NULL DEFAULT '{}',
|
|
82
|
+
allowed_apps_json TEXT NOT NULL DEFAULT '[]',
|
|
83
|
+
credential_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
84
|
+
owner_email TEXT NOT NULL DEFAULT '',
|
|
85
|
+
org_id TEXT,
|
|
86
|
+
created_at ${intType()} NOT NULL DEFAULT 0,
|
|
87
|
+
updated_at ${intType()} NOT NULL DEFAULT 0,
|
|
88
|
+
last_checked_at ${intType()},
|
|
89
|
+
last_error TEXT
|
|
90
|
+
)
|
|
91
|
+
`));
|
|
92
|
+
await ensureWorkspaceConnectionColumns(client, table);
|
|
93
|
+
await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_workspace_connections_scope_provider ON ${table} (org_id, owner_email, provider)`));
|
|
94
|
+
await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_workspace_connections_updated_at ON ${table} (updated_at)`));
|
|
95
|
+
await retryOnDdlRace(() => client.execute(`
|
|
96
|
+
CREATE TABLE IF NOT EXISTS ${grantsTable} (
|
|
97
|
+
id TEXT PRIMARY KEY,
|
|
98
|
+
connection_id TEXT NOT NULL DEFAULT '',
|
|
99
|
+
provider TEXT NOT NULL DEFAULT '',
|
|
100
|
+
app_id TEXT NOT NULL DEFAULT '',
|
|
101
|
+
scopes_json TEXT NOT NULL DEFAULT '[]',
|
|
102
|
+
config_json TEXT NOT NULL DEFAULT '{}',
|
|
103
|
+
credential_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
104
|
+
granted_by_email TEXT NOT NULL DEFAULT '',
|
|
105
|
+
owner_email TEXT NOT NULL DEFAULT '',
|
|
106
|
+
org_id TEXT,
|
|
107
|
+
created_at ${intType()} NOT NULL DEFAULT 0,
|
|
108
|
+
updated_at ${intType()} NOT NULL DEFAULT 0
|
|
109
|
+
)
|
|
110
|
+
`));
|
|
111
|
+
await ensureWorkspaceConnectionGrantColumns(client, grantsTable);
|
|
112
|
+
await retryOnDdlRace(() => client.execute(`CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_connection_grants_connection_app ON ${grantsTable} (connection_id, app_id)`));
|
|
113
|
+
await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_workspace_connection_grants_scope_app ON ${grantsTable} (org_id, owner_email, app_id)`));
|
|
114
|
+
await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_workspace_connection_grants_updated_at ON ${grantsTable} (updated_at)`));
|
|
115
|
+
})().catch((err) => {
|
|
116
|
+
_initPromise = undefined;
|
|
117
|
+
throw err;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return _initPromise;
|
|
121
|
+
}
|
|
122
|
+
function requireWorkspaceConnectionScope() {
|
|
123
|
+
const ownerEmail = getRequestUserEmail()?.trim().toLowerCase();
|
|
124
|
+
if (!ownerEmail) {
|
|
125
|
+
throw new Error("Workspace connections require an authenticated user.");
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
ownerEmail,
|
|
129
|
+
orgId: getRequestOrgId()?.trim() || null,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function scopedWhere(scope) {
|
|
133
|
+
if (scope.orgId) {
|
|
134
|
+
return { sql: "org_id = ?", args: [scope.orgId] };
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
sql: "owner_email = ? AND org_id IS NULL",
|
|
138
|
+
args: [scope.ownerEmail],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function normalizeStringArray(value) {
|
|
142
|
+
if (!Array.isArray(value))
|
|
143
|
+
return [];
|
|
144
|
+
return value
|
|
145
|
+
.filter((entry) => typeof entry === "string")
|
|
146
|
+
.map((entry) => entry.trim())
|
|
147
|
+
.filter(Boolean);
|
|
148
|
+
}
|
|
149
|
+
function normalizeRequiredString(value, label) {
|
|
150
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
151
|
+
if (!normalized) {
|
|
152
|
+
throw new Error(`${label} is required.`);
|
|
153
|
+
}
|
|
154
|
+
return normalized;
|
|
155
|
+
}
|
|
156
|
+
function normalizeObject(value) {
|
|
157
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
158
|
+
return {};
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
function normalizeCredentialRefs(value) {
|
|
162
|
+
if (!Array.isArray(value))
|
|
163
|
+
return [];
|
|
164
|
+
return value
|
|
165
|
+
.filter((entry) => !!entry &&
|
|
166
|
+
typeof entry === "object" &&
|
|
167
|
+
!Array.isArray(entry) &&
|
|
168
|
+
typeof entry.key === "string")
|
|
169
|
+
.map((entry) => sanitizeCredentialRef(entry))
|
|
170
|
+
.filter((entry) => entry.key.trim().length > 0);
|
|
171
|
+
}
|
|
172
|
+
function normalizeStatus(value) {
|
|
173
|
+
if (value === "checking" ||
|
|
174
|
+
value === "needs_reauth" ||
|
|
175
|
+
value === "error" ||
|
|
176
|
+
value === "disabled") {
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
return "connected";
|
|
180
|
+
}
|
|
181
|
+
function millis(value) {
|
|
182
|
+
if (value == null)
|
|
183
|
+
return null;
|
|
184
|
+
if (value instanceof Date)
|
|
185
|
+
return value.getTime();
|
|
186
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
187
|
+
return value;
|
|
188
|
+
const parsed = Date.parse(String(value));
|
|
189
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
190
|
+
}
|
|
191
|
+
function iso(value) {
|
|
192
|
+
if (value == null)
|
|
193
|
+
return null;
|
|
194
|
+
const num = Number(value);
|
|
195
|
+
if (!Number.isFinite(num) || num <= 0)
|
|
196
|
+
return null;
|
|
197
|
+
return new Date(num).toISOString();
|
|
198
|
+
}
|
|
199
|
+
const SECRET_KEYS = new Set([
|
|
200
|
+
"apikey",
|
|
201
|
+
"authorization",
|
|
202
|
+
"clientsecret",
|
|
203
|
+
"cookie",
|
|
204
|
+
"password",
|
|
205
|
+
"privatekey",
|
|
206
|
+
"refreshtoken",
|
|
207
|
+
"secret",
|
|
208
|
+
"token",
|
|
209
|
+
"accesstoken",
|
|
210
|
+
]);
|
|
211
|
+
function normalizedKey(key) {
|
|
212
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
213
|
+
}
|
|
214
|
+
function sanitizeJson(value, allowCredentialRefKey = false) {
|
|
215
|
+
if (Array.isArray(value)) {
|
|
216
|
+
return value.map((entry) => sanitizeJson(entry, allowCredentialRefKey));
|
|
217
|
+
}
|
|
218
|
+
if (!value || typeof value !== "object")
|
|
219
|
+
return value;
|
|
220
|
+
const result = {};
|
|
221
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
222
|
+
const normalized = normalizedKey(key);
|
|
223
|
+
if (SECRET_KEYS.has(normalized) &&
|
|
224
|
+
!(allowCredentialRefKey && normalized === "key")) {
|
|
225
|
+
result[key] = "[redacted]";
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
result[key] = sanitizeJson(entry, allowCredentialRefKey);
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
function sanitizeConfig(config) {
|
|
233
|
+
return sanitizeJson(normalizeObject(config), false);
|
|
234
|
+
}
|
|
235
|
+
function sanitizeCredentialRef(ref) {
|
|
236
|
+
const sanitized = sanitizeJson(ref, true);
|
|
237
|
+
if (Object.prototype.hasOwnProperty.call(sanitized, "value")) {
|
|
238
|
+
sanitized.value = "[redacted]";
|
|
239
|
+
}
|
|
240
|
+
return sanitized;
|
|
241
|
+
}
|
|
242
|
+
function parseRow(row) {
|
|
243
|
+
return serializeWorkspaceConnection({
|
|
244
|
+
id: String(row.id),
|
|
245
|
+
provider: String(row.provider ?? ""),
|
|
246
|
+
label: String(row.label ?? ""),
|
|
247
|
+
accountId: row.account_id == null ? null : String(row.account_id),
|
|
248
|
+
accountLabel: row.account_label == null ? null : String(row.account_label),
|
|
249
|
+
status: normalizeStatus(row.status),
|
|
250
|
+
scopes: normalizeStringArray(safeJsonParse(row.scopes_json, [])),
|
|
251
|
+
config: normalizeObject(safeJsonParse(row.config_json, {})),
|
|
252
|
+
allowedApps: normalizeStringArray(safeJsonParse(row.allowed_apps_json, [])),
|
|
253
|
+
credentialRefs: normalizeCredentialRefs(safeJsonParse(row.credential_refs_json, [])),
|
|
254
|
+
ownerEmail: String(row.owner_email ?? ""),
|
|
255
|
+
orgId: row.org_id == null ? null : String(row.org_id),
|
|
256
|
+
createdAt: iso(row.created_at) ?? new Date(0).toISOString(),
|
|
257
|
+
updatedAt: iso(row.updated_at) ?? new Date(0).toISOString(),
|
|
258
|
+
lastCheckedAt: iso(row.last_checked_at),
|
|
259
|
+
lastError: row.last_error == null ? null : String(row.last_error),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function parseGrantRow(row) {
|
|
263
|
+
return serializeWorkspaceConnectionGrant({
|
|
264
|
+
id: String(row.id),
|
|
265
|
+
connectionId: String(row.connection_id ?? ""),
|
|
266
|
+
provider: String(row.provider ?? ""),
|
|
267
|
+
appId: String(row.app_id ?? ""),
|
|
268
|
+
scopes: normalizeStringArray(safeJsonParse(row.scopes_json, [])),
|
|
269
|
+
config: normalizeObject(safeJsonParse(row.config_json, {})),
|
|
270
|
+
credentialRefs: normalizeCredentialRefs(safeJsonParse(row.credential_refs_json, [])),
|
|
271
|
+
grantedByEmail: String(row.granted_by_email ?? ""),
|
|
272
|
+
ownerEmail: String(row.owner_email ?? ""),
|
|
273
|
+
orgId: row.org_id == null ? null : String(row.org_id),
|
|
274
|
+
createdAt: iso(row.created_at) ?? new Date(0).toISOString(),
|
|
275
|
+
updatedAt: iso(row.updated_at) ?? new Date(0).toISOString(),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
export function serializeWorkspaceConnection(connection) {
|
|
279
|
+
return {
|
|
280
|
+
...connection,
|
|
281
|
+
scopes: normalizeStringArray(connection.scopes),
|
|
282
|
+
config: sanitizeConfig(connection.config),
|
|
283
|
+
allowedApps: normalizeStringArray(connection.allowedApps),
|
|
284
|
+
credentialRefs: normalizeCredentialRefs(connection.credentialRefs),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
export function serializeWorkspaceConnectionGrant(grant) {
|
|
288
|
+
return {
|
|
289
|
+
...grant,
|
|
290
|
+
scopes: normalizeStringArray(grant.scopes),
|
|
291
|
+
config: sanitizeConfig(grant.config),
|
|
292
|
+
credentialRefs: normalizeCredentialRefs(grant.credentialRefs),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
export function getWorkspaceConnectionAppAccess(connection, appId, grants = []) {
|
|
296
|
+
const normalizedAppId = appId.trim();
|
|
297
|
+
if (!normalizedAppId) {
|
|
298
|
+
return {
|
|
299
|
+
appId: normalizedAppId,
|
|
300
|
+
available: false,
|
|
301
|
+
mode: "unavailable",
|
|
302
|
+
reason: "No app id was provided.",
|
|
303
|
+
grantId: null,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (connection.allowedApps.length === 0) {
|
|
307
|
+
return {
|
|
308
|
+
appId: normalizedAppId,
|
|
309
|
+
available: true,
|
|
310
|
+
mode: "all-apps",
|
|
311
|
+
reason: "Connection is available to every app in the workspace.",
|
|
312
|
+
grantId: null,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (connection.allowedApps.includes(normalizedAppId)) {
|
|
316
|
+
return {
|
|
317
|
+
appId: normalizedAppId,
|
|
318
|
+
available: true,
|
|
319
|
+
mode: "allowed-app",
|
|
320
|
+
reason: `Connection is directly allowed for ${normalizedAppId}.`,
|
|
321
|
+
grantId: null,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const explicitGrant = grants.find((grant) => grant.connectionId === connection.id && grant.appId === normalizedAppId);
|
|
325
|
+
if (explicitGrant) {
|
|
326
|
+
return {
|
|
327
|
+
appId: normalizedAppId,
|
|
328
|
+
available: true,
|
|
329
|
+
mode: "explicit-grant",
|
|
330
|
+
reason: `Connection has an explicit grant for ${normalizedAppId}.`,
|
|
331
|
+
grantId: explicitGrant.id,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
appId: normalizedAppId,
|
|
336
|
+
available: false,
|
|
337
|
+
mode: "unavailable",
|
|
338
|
+
reason: `Grant ${normalizedAppId} access before this connection can be reused by the app.`,
|
|
339
|
+
grantId: null,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
export function workspaceConnectionIsAvailableToApp(connection, appId, grants = []) {
|
|
343
|
+
return getWorkspaceConnectionAppAccess(connection, appId, grants).available;
|
|
344
|
+
}
|
|
345
|
+
function uniqueStrings(values) {
|
|
346
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
|
|
347
|
+
}
|
|
348
|
+
function publicCredentialRefs(refs, source) {
|
|
349
|
+
return refs.map((ref) => ({
|
|
350
|
+
key: ref.key,
|
|
351
|
+
scope: ref.scope,
|
|
352
|
+
provider: ref.provider,
|
|
353
|
+
label: ref.label,
|
|
354
|
+
source,
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
function grantsForConnection(connectionId, grants, appId) {
|
|
358
|
+
return grants.filter((grant) => grant.connectionId === connectionId && (!appId || grant.appId === appId));
|
|
359
|
+
}
|
|
360
|
+
function explicitGrantForConnection(connectionId, grants, appId) {
|
|
361
|
+
return grantsForConnection(connectionId, grants, appId)[0];
|
|
362
|
+
}
|
|
363
|
+
function connectionForApp(connection, appId, grants) {
|
|
364
|
+
const normalizedAppId = appId.trim();
|
|
365
|
+
const appAccess = getWorkspaceConnectionAppAccess(connection, normalizedAppId, grants);
|
|
366
|
+
return {
|
|
367
|
+
...connection,
|
|
368
|
+
appAccess,
|
|
369
|
+
explicitGrant: explicitGrantForConnection(connection.id, grants, normalizedAppId) ??
|
|
370
|
+
null,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function serializeConnectionForApp(connection, appId, grants) {
|
|
374
|
+
const explicitGrant = explicitGrantForConnection(connection.id, grants, appId);
|
|
375
|
+
const appAccess = getWorkspaceConnectionAppAccess(connection, appId, grants);
|
|
376
|
+
return {
|
|
377
|
+
id: connection.id,
|
|
378
|
+
label: connection.label,
|
|
379
|
+
provider: connection.provider,
|
|
380
|
+
accountId: connection.accountId,
|
|
381
|
+
accountLabel: connection.accountLabel,
|
|
382
|
+
status: connection.status,
|
|
383
|
+
grantedToApp: appAccess.available,
|
|
384
|
+
grantScope: connection.allowedApps.length === 0 ? "all-apps" : "selected-apps",
|
|
385
|
+
appAccess,
|
|
386
|
+
allowedApps: connection.allowedApps,
|
|
387
|
+
credentialRefs: publicCredentialRefs(connection.credentialRefs, "connection"),
|
|
388
|
+
lastCheckedAt: connection.lastCheckedAt,
|
|
389
|
+
lastError: connection.lastError,
|
|
390
|
+
explicitGrant: explicitGrant
|
|
391
|
+
? {
|
|
392
|
+
id: explicitGrant.id,
|
|
393
|
+
appId: explicitGrant.appId,
|
|
394
|
+
scopes: explicitGrant.scopes,
|
|
395
|
+
credentialRefs: publicCredentialRefs(explicitGrant.credentialRefs, "grant"),
|
|
396
|
+
updatedAt: explicitGrant.updatedAt,
|
|
397
|
+
}
|
|
398
|
+
: null,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function grantAvailabilityMessage(grantState, providerId, appId) {
|
|
402
|
+
switch (grantState) {
|
|
403
|
+
case "connected":
|
|
404
|
+
return `${appId} has an active ${providerId} workspace connection.`;
|
|
405
|
+
case "granted":
|
|
406
|
+
return `${appId} has ${providerId} access, but the granted connection is not connected yet.`;
|
|
407
|
+
case "needs_grant":
|
|
408
|
+
return `A ${providerId} workspace connection exists; grant ${appId} access to reuse it.`;
|
|
409
|
+
case "not_connected":
|
|
410
|
+
default:
|
|
411
|
+
return `No shared ${providerId} workspace connection is available yet.`;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
export function summarizeWorkspaceConnectionProviderForApp({ providerId, appId, connections, grants = [], includeConnections = "granted", }) {
|
|
415
|
+
const normalizedProviderId = providerId.trim();
|
|
416
|
+
const normalizedAppId = appId.trim();
|
|
417
|
+
const allConnections = connections.filter((connection) => connection.provider === normalizedProviderId);
|
|
418
|
+
const grantedConnections = allConnections.filter((connection) => getWorkspaceConnectionAppAccess(connection, normalizedAppId, grants)
|
|
419
|
+
.available);
|
|
420
|
+
const connectedConnections = grantedConnections.filter((connection) => connection.status === "connected");
|
|
421
|
+
const ungrantedConnectionCount = allConnections.length - grantedConnections.length;
|
|
422
|
+
const unhealthyGrantedConnectionCount = grantedConnections.length - connectedConnections.length;
|
|
423
|
+
const grantState = connectedConnections.length
|
|
424
|
+
? "connected"
|
|
425
|
+
: grantedConnections.length
|
|
426
|
+
? "granted"
|
|
427
|
+
: allConnections.length
|
|
428
|
+
? "needs_grant"
|
|
429
|
+
: "not_connected";
|
|
430
|
+
const explicitGrantCount = allConnections.reduce((count, connection) => explicitGrantForConnection(connection.id, grants, normalizedAppId)
|
|
431
|
+
? count + 1
|
|
432
|
+
: count, 0);
|
|
433
|
+
const credentialRefCount = allConnections.reduce((count, connection) => {
|
|
434
|
+
const explicitGrant = explicitGrantForConnection(connection.id, grants, normalizedAppId);
|
|
435
|
+
return (count +
|
|
436
|
+
connection.credentialRefs.length +
|
|
437
|
+
(explicitGrant?.credentialRefs.length ?? 0));
|
|
438
|
+
}, 0);
|
|
439
|
+
const visibleConnections = includeConnections === "all" ? allConnections : grantedConnections;
|
|
440
|
+
return {
|
|
441
|
+
appId: normalizedAppId,
|
|
442
|
+
provider: normalizedProviderId,
|
|
443
|
+
grantState,
|
|
444
|
+
grantAvailability: grantState === "connected" || grantState === "granted"
|
|
445
|
+
? "available"
|
|
446
|
+
: grantState,
|
|
447
|
+
grantAvailabilityMessage: grantAvailabilityMessage(grantState, normalizedProviderId, normalizedAppId),
|
|
448
|
+
connectionCount: allConnections.length,
|
|
449
|
+
grantedConnectionCount: grantedConnections.length,
|
|
450
|
+
activeConnectionCount: connectedConnections.length,
|
|
451
|
+
ungrantedConnectionCount,
|
|
452
|
+
unhealthyGrantedConnectionCount,
|
|
453
|
+
explicitGrantCount,
|
|
454
|
+
credentialRefCount,
|
|
455
|
+
hasWorkspaceConnection: allConnections.length > 0,
|
|
456
|
+
hasGrantedWorkspaceConnection: grantedConnections.length > 0,
|
|
457
|
+
hasActiveWorkspaceConnection: connectedConnections.length > 0,
|
|
458
|
+
statuses: uniqueStrings(allConnections.map((connection) => connection.status)),
|
|
459
|
+
connections: visibleConnections.map((connection) => serializeConnectionForApp(connection, normalizedAppId, grants)),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function requiredCredentialKeys(provider) {
|
|
463
|
+
return uniqueStrings((provider.credentialKeys ?? [])
|
|
464
|
+
.filter((credential) => credential.required ?? false)
|
|
465
|
+
.map((credential) => credential.key));
|
|
466
|
+
}
|
|
467
|
+
function missingRequiredCredentialKeys(provider, connection, grants = []) {
|
|
468
|
+
const available = new Set([
|
|
469
|
+
...connection.credentialRefs,
|
|
470
|
+
...grants.flatMap((grant) => grant.credentialRefs),
|
|
471
|
+
]
|
|
472
|
+
.map((ref) => ref.key.trim())
|
|
473
|
+
.filter(Boolean));
|
|
474
|
+
return requiredCredentialKeys(provider).filter((key) => !available.has(key));
|
|
475
|
+
}
|
|
476
|
+
export function summarizeWorkspaceConnectionProviderReadiness({ provider, connections, grants = [], appId, includeConnections, }) {
|
|
477
|
+
const providerConnections = connections.filter((connection) => connection.provider === provider.id);
|
|
478
|
+
const activeConnections = providerConnections.filter((connection) => connection.status !== "disabled");
|
|
479
|
+
const attentionConnections = activeConnections.filter((connection) => connection.status === "error" ||
|
|
480
|
+
connection.status === "needs_reauth" ||
|
|
481
|
+
Boolean(connection.lastError));
|
|
482
|
+
const missingKeys = uniqueStrings(activeConnections.flatMap((connection) => missingRequiredCredentialKeys(provider, connection, grantsForConnection(connection.id, grants, appId))));
|
|
483
|
+
const readyConnections = activeConnections.filter((connection) => connection.status === "connected" &&
|
|
484
|
+
missingRequiredCredentialKeys(provider, connection, grantsForConnection(connection.id, grants, appId)).length === 0);
|
|
485
|
+
const checkingConnections = activeConnections.filter((connection) => connection.status === "checking");
|
|
486
|
+
let status = "not_configured";
|
|
487
|
+
if (readyConnections.length > 0) {
|
|
488
|
+
status = "ready";
|
|
489
|
+
}
|
|
490
|
+
else if (attentionConnections.length > 0) {
|
|
491
|
+
status = "needs_attention";
|
|
492
|
+
}
|
|
493
|
+
else if (missingKeys.length > 0) {
|
|
494
|
+
status = "needs_credentials";
|
|
495
|
+
}
|
|
496
|
+
else if (checkingConnections.length > 0) {
|
|
497
|
+
status = "checking";
|
|
498
|
+
}
|
|
499
|
+
else if (providerConnections.length > 0) {
|
|
500
|
+
status = "disabled";
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
status,
|
|
504
|
+
connectionCount: providerConnections.length,
|
|
505
|
+
activeConnectionCount: activeConnections.length,
|
|
506
|
+
readyConnectionCount: readyConnections.length,
|
|
507
|
+
requiredCredentialKeys: requiredCredentialKeys(provider),
|
|
508
|
+
missingRequiredCredentialKeys: missingKeys,
|
|
509
|
+
appGrant: appId
|
|
510
|
+
? summarizeWorkspaceConnectionProviderForApp({
|
|
511
|
+
providerId: provider.id,
|
|
512
|
+
appId,
|
|
513
|
+
connections,
|
|
514
|
+
grants,
|
|
515
|
+
includeConnections,
|
|
516
|
+
})
|
|
517
|
+
: null,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
export async function listWorkspaceConnectionProviderCatalogForApp({ appId, provider, capability, templateUse, includeDisabled = true, includeConnections = "all", }) {
|
|
521
|
+
const [connections, grants] = await Promise.all([
|
|
522
|
+
listWorkspaceConnections({ provider, includeDisabled }),
|
|
523
|
+
listWorkspaceConnectionGrants({ provider, appId }),
|
|
524
|
+
]);
|
|
525
|
+
const providers = listWorkspaceConnectionProviders({
|
|
526
|
+
capability,
|
|
527
|
+
templateUse,
|
|
528
|
+
})
|
|
529
|
+
.filter((item) => !provider || item.id === provider)
|
|
530
|
+
.map((item) => {
|
|
531
|
+
const workspaceConnection = summarizeWorkspaceConnectionProviderForApp({
|
|
532
|
+
providerId: item.id,
|
|
533
|
+
appId,
|
|
534
|
+
connections,
|
|
535
|
+
grants,
|
|
536
|
+
includeConnections,
|
|
537
|
+
});
|
|
538
|
+
const readiness = summarizeWorkspaceConnectionProviderReadiness({
|
|
539
|
+
provider: item,
|
|
540
|
+
connections,
|
|
541
|
+
grants,
|
|
542
|
+
appId,
|
|
543
|
+
includeConnections,
|
|
544
|
+
});
|
|
545
|
+
return {
|
|
546
|
+
...item,
|
|
547
|
+
workspaceConnection,
|
|
548
|
+
readiness,
|
|
549
|
+
};
|
|
550
|
+
});
|
|
551
|
+
return {
|
|
552
|
+
appId,
|
|
553
|
+
providers,
|
|
554
|
+
connections,
|
|
555
|
+
grants,
|
|
556
|
+
counts: {
|
|
557
|
+
providers: providers.length,
|
|
558
|
+
connections: connections.length,
|
|
559
|
+
grants: grants.length,
|
|
560
|
+
readyProviders: providers.filter((item) => item.readiness.status === "ready").length,
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
export async function listWorkspaceConnectionsForApp({ appId, provider, includeDisabled = false, }) {
|
|
565
|
+
const normalizedAppId = normalizeRequiredString(appId, "listWorkspaceConnectionsForApp appId");
|
|
566
|
+
const [connections, grants] = await Promise.all([
|
|
567
|
+
listWorkspaceConnections({ provider, includeDisabled }),
|
|
568
|
+
listWorkspaceConnectionGrants({ provider, appId: normalizedAppId }),
|
|
569
|
+
]);
|
|
570
|
+
return connections
|
|
571
|
+
.map((connection) => connectionForApp(connection, normalizedAppId, grants))
|
|
572
|
+
.filter((connection) => connection.appAccess.available);
|
|
573
|
+
}
|
|
574
|
+
export async function resolveWorkspaceConnectionForApp({ appId, provider, includeDisabled = false, connectionId, requireConnected = false, }) {
|
|
575
|
+
const normalizedAppId = normalizeRequiredString(appId, "resolveWorkspaceConnectionForApp appId");
|
|
576
|
+
const normalizedConnectionId = connectionId?.trim();
|
|
577
|
+
const requestedConnections = await listWorkspaceConnections({
|
|
578
|
+
provider,
|
|
579
|
+
includeDisabled: includeDisabled || Boolean(normalizedConnectionId),
|
|
580
|
+
});
|
|
581
|
+
const candidateConnections = normalizedConnectionId
|
|
582
|
+
? requestedConnections.filter((connection) => connection.id === normalizedConnectionId)
|
|
583
|
+
: requestedConnections;
|
|
584
|
+
const grants = await listWorkspaceConnectionGrants({
|
|
585
|
+
provider,
|
|
586
|
+
appId: normalizedAppId,
|
|
587
|
+
connectionId: normalizedConnectionId,
|
|
588
|
+
});
|
|
589
|
+
if (normalizedConnectionId && candidateConnections.length === 0) {
|
|
590
|
+
return {
|
|
591
|
+
available: false,
|
|
592
|
+
connection: null,
|
|
593
|
+
appAccess: null,
|
|
594
|
+
reason: `Workspace connection "${normalizedConnectionId}" was not found in the current request scope.`,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
for (const connection of candidateConnections) {
|
|
598
|
+
const connectionWithAccess = connectionForApp(connection, normalizedAppId, grants);
|
|
599
|
+
if (!connectionWithAccess.appAccess.available) {
|
|
600
|
+
if (normalizedConnectionId) {
|
|
601
|
+
return {
|
|
602
|
+
available: false,
|
|
603
|
+
connection: connectionWithAccess,
|
|
604
|
+
appAccess: connectionWithAccess.appAccess,
|
|
605
|
+
reason: connectionWithAccess.appAccess.reason,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (!includeDisabled && connection.status === "disabled") {
|
|
611
|
+
if (normalizedConnectionId) {
|
|
612
|
+
return {
|
|
613
|
+
available: false,
|
|
614
|
+
connection: connectionWithAccess,
|
|
615
|
+
appAccess: connectionWithAccess.appAccess,
|
|
616
|
+
reason: `Workspace connection "${connection.id}" is disabled.`,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (requireConnected && connection.status !== "connected") {
|
|
622
|
+
if (normalizedConnectionId) {
|
|
623
|
+
return {
|
|
624
|
+
available: false,
|
|
625
|
+
connection: connectionWithAccess,
|
|
626
|
+
appAccess: connectionWithAccess.appAccess,
|
|
627
|
+
reason: `Workspace connection "${connection.id}" is ${connection.status}; a connected workspace connection is required.`,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
available: true,
|
|
634
|
+
connection: connectionWithAccess,
|
|
635
|
+
appAccess: connectionWithAccess.appAccess,
|
|
636
|
+
reason: connectionWithAccess.appAccess.reason,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
available: false,
|
|
641
|
+
connection: null,
|
|
642
|
+
appAccess: null,
|
|
643
|
+
reason: provider
|
|
644
|
+
? `No available ${provider} workspace connection was found for ${normalizedAppId}.`
|
|
645
|
+
: `No available workspace connection was found for ${normalizedAppId}.`,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
async function getGrantedConnectionIdsForApp(client, scope, appId) {
|
|
649
|
+
const table = workspaceConnectionGrantsTable();
|
|
650
|
+
const where = scopedWhere(scope);
|
|
651
|
+
const { rows } = await client.execute({
|
|
652
|
+
sql: `SELECT connection_id FROM ${table} WHERE app_id = ? AND ${where.sql}`,
|
|
653
|
+
args: [appId, ...where.args],
|
|
654
|
+
});
|
|
655
|
+
return new Set(rows
|
|
656
|
+
.map((row) => String(row.connection_id ?? ""))
|
|
657
|
+
.filter(Boolean));
|
|
658
|
+
}
|
|
659
|
+
export async function listWorkspaceConnections(options = {}) {
|
|
660
|
+
await ensureWorkspaceConnectionsTable();
|
|
661
|
+
const client = getDbExec();
|
|
662
|
+
const table = workspaceConnectionsTable();
|
|
663
|
+
const scope = requireWorkspaceConnectionScope();
|
|
664
|
+
const where = scopedWhere(scope);
|
|
665
|
+
const clauses = [where.sql];
|
|
666
|
+
const args = [...where.args];
|
|
667
|
+
const appId = options.appId?.trim();
|
|
668
|
+
if (options.provider) {
|
|
669
|
+
clauses.push("provider = ?");
|
|
670
|
+
args.push(options.provider);
|
|
671
|
+
}
|
|
672
|
+
if (!options.includeDisabled) {
|
|
673
|
+
clauses.push("status != ?");
|
|
674
|
+
args.push("disabled");
|
|
675
|
+
}
|
|
676
|
+
const { rows } = await client.execute({
|
|
677
|
+
sql: `SELECT * FROM ${table} WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC`,
|
|
678
|
+
args,
|
|
679
|
+
});
|
|
680
|
+
const connections = rows.map((row) => parseRow(row));
|
|
681
|
+
if (!appId)
|
|
682
|
+
return connections;
|
|
683
|
+
const grantedConnectionIds = await getGrantedConnectionIdsForApp(client, scope, appId);
|
|
684
|
+
return connections.filter((connection) => connection.allowedApps.length === 0 ||
|
|
685
|
+
connection.allowedApps.includes(appId) ||
|
|
686
|
+
grantedConnectionIds.has(connection.id));
|
|
687
|
+
}
|
|
688
|
+
export async function getWorkspaceConnection(id) {
|
|
689
|
+
await ensureWorkspaceConnectionsTable();
|
|
690
|
+
const client = getDbExec();
|
|
691
|
+
const table = workspaceConnectionsTable();
|
|
692
|
+
const scope = requireWorkspaceConnectionScope();
|
|
693
|
+
const where = scopedWhere(scope);
|
|
694
|
+
const { rows } = await client.execute({
|
|
695
|
+
sql: `SELECT * FROM ${table} WHERE id = ? AND ${where.sql} LIMIT 1`,
|
|
696
|
+
args: [id, ...where.args],
|
|
697
|
+
});
|
|
698
|
+
if (rows.length === 0)
|
|
699
|
+
return null;
|
|
700
|
+
return parseRow(rows[0]);
|
|
701
|
+
}
|
|
702
|
+
export async function upsertWorkspaceConnection(input) {
|
|
703
|
+
await ensureWorkspaceConnectionsTable();
|
|
704
|
+
const provider = input.provider.trim();
|
|
705
|
+
if (!provider) {
|
|
706
|
+
throw new Error("upsertWorkspaceConnection requires a provider.");
|
|
707
|
+
}
|
|
708
|
+
const client = getDbExec();
|
|
709
|
+
const table = workspaceConnectionsTable();
|
|
710
|
+
const scope = requireWorkspaceConnectionScope();
|
|
711
|
+
const where = scopedWhere(scope);
|
|
712
|
+
const id = input.id?.trim() || randomUUID();
|
|
713
|
+
const now = Date.now();
|
|
714
|
+
const label = input.label?.trim() || input.accountLabel?.trim() || provider;
|
|
715
|
+
const status = normalizeStatus(input.status);
|
|
716
|
+
const scopes = normalizeStringArray(input.scopes);
|
|
717
|
+
const config = sanitizeConfig(input.config);
|
|
718
|
+
const allowedApps = normalizeStringArray(input.allowedApps);
|
|
719
|
+
const credentialRefs = normalizeCredentialRefs(input.credentialRefs);
|
|
720
|
+
const lastCheckedAt = millis(input.lastCheckedAt);
|
|
721
|
+
const lastError = input.lastError ?? null;
|
|
722
|
+
const update = await client.execute({
|
|
723
|
+
sql: `UPDATE ${table}
|
|
724
|
+
SET provider = ?, label = ?, account_id = ?, account_label = ?,
|
|
725
|
+
status = ?, scopes_json = ?, config_json = ?, allowed_apps_json = ?,
|
|
726
|
+
credential_refs_json = ?, updated_at = ?, last_checked_at = ?,
|
|
727
|
+
last_error = ?
|
|
728
|
+
WHERE id = ? AND ${where.sql}`,
|
|
729
|
+
args: [
|
|
730
|
+
provider,
|
|
731
|
+
label,
|
|
732
|
+
input.accountId ?? null,
|
|
733
|
+
input.accountLabel ?? null,
|
|
734
|
+
status,
|
|
735
|
+
JSON.stringify(scopes),
|
|
736
|
+
JSON.stringify(config),
|
|
737
|
+
JSON.stringify(allowedApps),
|
|
738
|
+
JSON.stringify(credentialRefs),
|
|
739
|
+
now,
|
|
740
|
+
lastCheckedAt,
|
|
741
|
+
lastError,
|
|
742
|
+
id,
|
|
743
|
+
...where.args,
|
|
744
|
+
],
|
|
745
|
+
});
|
|
746
|
+
if (update.rowsAffected === 0) {
|
|
747
|
+
try {
|
|
748
|
+
await client.execute({
|
|
749
|
+
sql: `INSERT INTO ${table}
|
|
750
|
+
(id, provider, label, account_id, account_label, status,
|
|
751
|
+
scopes_json, config_json, allowed_apps_json, credential_refs_json,
|
|
752
|
+
owner_email, org_id, created_at, updated_at, last_checked_at,
|
|
753
|
+
last_error)
|
|
754
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
755
|
+
args: [
|
|
756
|
+
id,
|
|
757
|
+
provider,
|
|
758
|
+
label,
|
|
759
|
+
input.accountId ?? null,
|
|
760
|
+
input.accountLabel ?? null,
|
|
761
|
+
status,
|
|
762
|
+
JSON.stringify(scopes),
|
|
763
|
+
JSON.stringify(config),
|
|
764
|
+
JSON.stringify(allowedApps),
|
|
765
|
+
JSON.stringify(credentialRefs),
|
|
766
|
+
scope.ownerEmail,
|
|
767
|
+
scope.orgId,
|
|
768
|
+
now,
|
|
769
|
+
now,
|
|
770
|
+
lastCheckedAt,
|
|
771
|
+
lastError,
|
|
772
|
+
],
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
if (isUniqueViolation(err)) {
|
|
777
|
+
throw new Error(`Workspace connection "${id}" already exists outside the current request scope.`);
|
|
778
|
+
}
|
|
779
|
+
throw err;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const connection = await getWorkspaceConnection(id);
|
|
783
|
+
if (!connection) {
|
|
784
|
+
throw new Error(`Workspace connection "${id}" was not found after upsert.`);
|
|
785
|
+
}
|
|
786
|
+
return connection;
|
|
787
|
+
}
|
|
788
|
+
export async function listWorkspaceConnectionGrants(options = {}) {
|
|
789
|
+
await ensureWorkspaceConnectionsTable();
|
|
790
|
+
const client = getDbExec();
|
|
791
|
+
const table = workspaceConnectionGrantsTable();
|
|
792
|
+
const scope = requireWorkspaceConnectionScope();
|
|
793
|
+
const where = scopedWhere(scope);
|
|
794
|
+
const clauses = [where.sql];
|
|
795
|
+
const args = [...where.args];
|
|
796
|
+
const connectionId = options.connectionId?.trim();
|
|
797
|
+
const appId = options.appId?.trim();
|
|
798
|
+
const provider = options.provider?.trim();
|
|
799
|
+
if (connectionId) {
|
|
800
|
+
clauses.push("connection_id = ?");
|
|
801
|
+
args.push(connectionId);
|
|
802
|
+
}
|
|
803
|
+
if (appId) {
|
|
804
|
+
clauses.push("app_id = ?");
|
|
805
|
+
args.push(appId);
|
|
806
|
+
}
|
|
807
|
+
if (provider) {
|
|
808
|
+
clauses.push("provider = ?");
|
|
809
|
+
args.push(provider);
|
|
810
|
+
}
|
|
811
|
+
const { rows } = await client.execute({
|
|
812
|
+
sql: `SELECT * FROM ${table} WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC`,
|
|
813
|
+
args,
|
|
814
|
+
});
|
|
815
|
+
return rows.map((row) => parseGrantRow(row));
|
|
816
|
+
}
|
|
817
|
+
export async function getWorkspaceConnectionGrant(connectionId, appId) {
|
|
818
|
+
await ensureWorkspaceConnectionsTable();
|
|
819
|
+
const normalizedConnectionId = normalizeRequiredString(connectionId, "getWorkspaceConnectionGrant connectionId");
|
|
820
|
+
const normalizedAppId = normalizeRequiredString(appId, "getWorkspaceConnectionGrant appId");
|
|
821
|
+
const client = getDbExec();
|
|
822
|
+
const table = workspaceConnectionGrantsTable();
|
|
823
|
+
const scope = requireWorkspaceConnectionScope();
|
|
824
|
+
const where = scopedWhere(scope);
|
|
825
|
+
const { rows } = await client.execute({
|
|
826
|
+
sql: `SELECT * FROM ${table} WHERE connection_id = ? AND app_id = ? AND ${where.sql} LIMIT 1`,
|
|
827
|
+
args: [normalizedConnectionId, normalizedAppId, ...where.args],
|
|
828
|
+
});
|
|
829
|
+
if (rows.length === 0)
|
|
830
|
+
return null;
|
|
831
|
+
return parseGrantRow(rows[0]);
|
|
832
|
+
}
|
|
833
|
+
export async function upsertWorkspaceConnectionGrant(input) {
|
|
834
|
+
await ensureWorkspaceConnectionsTable();
|
|
835
|
+
const connectionId = normalizeRequiredString(input.connectionId, "upsertWorkspaceConnectionGrant connectionId");
|
|
836
|
+
const appId = normalizeRequiredString(input.appId, "upsertWorkspaceConnectionGrant appId");
|
|
837
|
+
const connection = await getWorkspaceConnection(connectionId);
|
|
838
|
+
if (!connection) {
|
|
839
|
+
throw new Error(`Workspace connection "${connectionId}" was not found in the current request scope.`);
|
|
840
|
+
}
|
|
841
|
+
const client = getDbExec();
|
|
842
|
+
const table = workspaceConnectionGrantsTable();
|
|
843
|
+
const scope = requireWorkspaceConnectionScope();
|
|
844
|
+
const where = scopedWhere(scope);
|
|
845
|
+
const id = input.id?.trim() || randomUUID();
|
|
846
|
+
const now = Date.now();
|
|
847
|
+
const provider = input.provider?.trim() || connection.provider;
|
|
848
|
+
const scopes = normalizeStringArray(input.scopes);
|
|
849
|
+
const config = sanitizeConfig(input.config);
|
|
850
|
+
const credentialRefs = normalizeCredentialRefs(input.credentialRefs);
|
|
851
|
+
const update = await client.execute({
|
|
852
|
+
sql: `UPDATE ${table}
|
|
853
|
+
SET provider = ?, scopes_json = ?, config_json = ?,
|
|
854
|
+
credential_refs_json = ?, granted_by_email = ?, updated_at = ?
|
|
855
|
+
WHERE connection_id = ? AND app_id = ? AND ${where.sql}`,
|
|
856
|
+
args: [
|
|
857
|
+
provider,
|
|
858
|
+
JSON.stringify(scopes),
|
|
859
|
+
JSON.stringify(config),
|
|
860
|
+
JSON.stringify(credentialRefs),
|
|
861
|
+
scope.ownerEmail,
|
|
862
|
+
now,
|
|
863
|
+
connectionId,
|
|
864
|
+
appId,
|
|
865
|
+
...where.args,
|
|
866
|
+
],
|
|
867
|
+
});
|
|
868
|
+
if (update.rowsAffected === 0) {
|
|
869
|
+
try {
|
|
870
|
+
await client.execute({
|
|
871
|
+
sql: `INSERT INTO ${table}
|
|
872
|
+
(id, connection_id, provider, app_id, scopes_json, config_json,
|
|
873
|
+
credential_refs_json, granted_by_email, owner_email, org_id,
|
|
874
|
+
created_at, updated_at)
|
|
875
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
876
|
+
args: [
|
|
877
|
+
id,
|
|
878
|
+
connectionId,
|
|
879
|
+
provider,
|
|
880
|
+
appId,
|
|
881
|
+
JSON.stringify(scopes),
|
|
882
|
+
JSON.stringify(config),
|
|
883
|
+
JSON.stringify(credentialRefs),
|
|
884
|
+
scope.ownerEmail,
|
|
885
|
+
scope.ownerEmail,
|
|
886
|
+
scope.orgId,
|
|
887
|
+
now,
|
|
888
|
+
now,
|
|
889
|
+
],
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
catch (err) {
|
|
893
|
+
if (isUniqueViolation(err)) {
|
|
894
|
+
throw new Error(`Workspace connection grant for "${connectionId}" and "${appId}" already exists outside the current request scope.`);
|
|
895
|
+
}
|
|
896
|
+
throw err;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const grant = await getWorkspaceConnectionGrant(connectionId, appId);
|
|
900
|
+
if (!grant) {
|
|
901
|
+
throw new Error(`Workspace connection grant for "${connectionId}" and "${appId}" was not found after upsert.`);
|
|
902
|
+
}
|
|
903
|
+
return grant;
|
|
904
|
+
}
|
|
905
|
+
export async function revokeWorkspaceConnectionGrant(connectionId, appId) {
|
|
906
|
+
await ensureWorkspaceConnectionsTable();
|
|
907
|
+
const normalizedConnectionId = normalizeRequiredString(connectionId, "revokeWorkspaceConnectionGrant connectionId");
|
|
908
|
+
const normalizedAppId = normalizeRequiredString(appId, "revokeWorkspaceConnectionGrant appId");
|
|
909
|
+
const client = getDbExec();
|
|
910
|
+
const table = workspaceConnectionGrantsTable();
|
|
911
|
+
const scope = requireWorkspaceConnectionScope();
|
|
912
|
+
const where = scopedWhere(scope);
|
|
913
|
+
const result = await client.execute({
|
|
914
|
+
sql: `DELETE FROM ${table} WHERE connection_id = ? AND app_id = ? AND ${where.sql}`,
|
|
915
|
+
args: [normalizedConnectionId, normalizedAppId, ...where.args],
|
|
916
|
+
});
|
|
917
|
+
return result.rowsAffected > 0;
|
|
918
|
+
}
|
|
919
|
+
export async function deleteWorkspaceConnection(id) {
|
|
920
|
+
await ensureWorkspaceConnectionsTable();
|
|
921
|
+
const client = getDbExec();
|
|
922
|
+
const table = workspaceConnectionsTable();
|
|
923
|
+
const grantsTable = workspaceConnectionGrantsTable();
|
|
924
|
+
const scope = requireWorkspaceConnectionScope();
|
|
925
|
+
const where = scopedWhere(scope);
|
|
926
|
+
const result = await client.execute({
|
|
927
|
+
sql: `DELETE FROM ${table} WHERE id = ? AND ${where.sql}`,
|
|
928
|
+
args: [id, ...where.args],
|
|
929
|
+
});
|
|
930
|
+
if (result.rowsAffected > 0) {
|
|
931
|
+
await client.execute({
|
|
932
|
+
sql: `DELETE FROM ${grantsTable} WHERE connection_id = ? AND ${where.sql}`,
|
|
933
|
+
args: [id, ...where.args],
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
return result.rowsAffected > 0;
|
|
937
|
+
}
|
|
938
|
+
//# sourceMappingURL=store.js.map
|