@agent-native/core 0.14.8 → 0.15.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/README.md +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +30 -9
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +14 -4
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +71 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +9 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/appearance/actions/change-appearance.d.ts +3 -0
- package/dist/appearance/actions/change-appearance.d.ts.map +1 -0
- package/dist/appearance/actions/change-appearance.js +29 -0
- package/dist/appearance/actions/change-appearance.js.map +1 -0
- package/dist/chat-threads/store.d.ts +53 -2
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +172 -12
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +114 -37
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +30 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts +25 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +275 -49
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +23 -4
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +276 -53
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AppearancePicker.d.ts +11 -0
- package/dist/client/AppearancePicker.d.ts.map +1 -0
- package/dist/client/AppearancePicker.js +16 -0
- package/dist/client/AppearancePicker.js.map +1 -0
- package/dist/client/AssistantChat.d.ts +35 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +315 -32
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -2
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/ErrorBoundary.d.ts.map +1 -1
- package/dist/client/ErrorBoundary.js +8 -10
- package/dist/client/ErrorBoundary.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +1 -1
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +13 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +217 -38
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +37 -14
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +5 -0
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-sidebar-state.d.ts +12 -0
- package/dist/client/agent-sidebar-state.d.ts.map +1 -1
- package/dist/client/agent-sidebar-state.js +8 -0
- package/dist/client/agent-sidebar-state.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +175 -3
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/appearance.d.ts +40 -0
- package/dist/client/appearance.d.ts.map +1 -0
- package/dist/client/appearance.js +114 -0
- package/dist/client/appearance.js.map +1 -0
- package/dist/client/builder-frame.d.ts +1 -0
- package/dist/client/builder-frame.d.ts.map +1 -1
- package/dist/client/builder-frame.js +19 -9
- package/dist/client/builder-frame.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +10 -2
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/components/ui/dropdown-menu.js +2 -2
- package/dist/client/components/ui/dropdown-menu.js.map +1 -1
- package/dist/client/components/ui/hover-card.js +1 -1
- package/dist/client/components/ui/hover-card.js.map +1 -1
- package/dist/client/components/ui/popover.js +1 -1
- package/dist/client/components/ui/popover.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts +7 -0
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +63 -32
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +5 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +36 -6
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +13 -1
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/dev-mode.d.ts +14 -0
- package/dist/client/dev-mode.d.ts.map +1 -0
- package/dist/client/dev-mode.js +14 -0
- package/dist/client/dev-mode.js.map +1 -0
- package/dist/client/error-format.d.ts +3 -2
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +9 -2
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/EmbeddedTool.d.ts +20 -0
- package/dist/client/extensions/EmbeddedTool.d.ts.map +1 -0
- package/dist/client/extensions/EmbeddedTool.js +199 -0
- package/dist/client/extensions/EmbeddedTool.js.map +1 -0
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +24 -2
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ToolEditor.d.ts +5 -0
- package/dist/client/extensions/ToolEditor.d.ts.map +1 -0
- package/dist/client/extensions/ToolEditor.js +129 -0
- package/dist/client/extensions/ToolEditor.js.map +1 -0
- package/dist/client/extensions/ToolViewer.d.ts +5 -0
- package/dist/client/extensions/ToolViewer.d.ts.map +1 -0
- package/dist/client/extensions/ToolViewer.js +400 -0
- package/dist/client/extensions/ToolViewer.js.map +1 -0
- package/dist/client/extensions/ToolViewerPage.d.ts +2 -0
- package/dist/client/extensions/ToolViewerPage.d.ts.map +1 -0
- package/dist/client/extensions/ToolViewerPage.js +24 -0
- package/dist/client/extensions/ToolViewerPage.js.map +1 -0
- package/dist/client/extensions/ToolsListPage.d.ts +2 -0
- package/dist/client/extensions/ToolsListPage.d.ts.map +1 -0
- package/dist/client/extensions/ToolsListPage.js +67 -0
- package/dist/client/extensions/ToolsListPage.js.map +1 -0
- package/dist/client/extensions/ToolsSidebarSection.d.ts +2 -0
- package/dist/client/extensions/ToolsSidebarSection.d.ts.map +1 -0
- package/dist/client/extensions/ToolsSidebarSection.js +236 -0
- package/dist/client/extensions/ToolsSidebarSection.js.map +1 -0
- package/dist/client/extensions/tool-order.d.ts +7 -0
- package/dist/client/extensions/tool-order.d.ts.map +1 -0
- package/dist/client/extensions/tool-order.js +47 -0
- package/dist/client/extensions/tool-order.js.map +1 -0
- package/dist/client/index.d.ts +8 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +1 -0
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +23 -2
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +5 -4
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +57 -9
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +10 -6
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/workspace-app-links.d.ts +31 -0
- package/dist/client/org/workspace-app-links.d.ts.map +1 -0
- package/dist/client/org/workspace-app-links.js +268 -0
- package/dist/client/org/workspace-app-links.js.map +1 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +18 -5
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/use-resources.d.ts +18 -13
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +24 -6
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.d.ts.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.js +9 -1
- package/dist/client/settings/BackgroundAgentSection.js.map +1 -1
- package/dist/client/settings/BrowserSection.d.ts.map +1 -1
- package/dist/client/settings/BrowserSection.js +16 -1
- package/dist/client/settings/BrowserSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +4 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +5 -5
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +8 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +50 -13
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.spec.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.spec.d.ts.map +1 -0
- package/dist/client/settings/useBuilderStatus.spec.js +64 -0
- package/dist/client/settings/useBuilderStatus.spec.js.map +1 -0
- package/dist/client/sharing/ShareButton.d.ts +5 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +60 -6
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/theme.js +1 -1
- package/dist/client/theme.js.map +1 -1
- package/dist/client/tools/EmbeddedTool.d.ts +20 -0
- package/dist/client/tools/EmbeddedTool.d.ts.map +1 -0
- package/dist/client/tools/EmbeddedTool.js +199 -0
- package/dist/client/tools/EmbeddedTool.js.map +1 -0
- package/dist/client/tools/ExtensionSlot.d.ts +27 -0
- package/dist/client/tools/ExtensionSlot.d.ts.map +1 -0
- package/dist/client/tools/ExtensionSlot.js +96 -0
- package/dist/client/tools/ExtensionSlot.js.map +1 -0
- package/dist/client/tools/ToolEditor.d.ts +5 -0
- package/dist/client/tools/ToolEditor.d.ts.map +1 -0
- package/dist/client/tools/ToolEditor.js +129 -0
- package/dist/client/tools/ToolEditor.js.map +1 -0
- package/dist/client/tools/ToolViewer.d.ts +5 -0
- package/dist/client/tools/ToolViewer.d.ts.map +1 -0
- package/dist/client/tools/ToolViewer.js +400 -0
- package/dist/client/tools/ToolViewer.js.map +1 -0
- package/dist/client/tools/ToolViewerPage.d.ts +2 -0
- package/dist/client/tools/ToolViewerPage.d.ts.map +1 -0
- package/dist/client/tools/ToolViewerPage.js +24 -0
- package/dist/client/tools/ToolViewerPage.js.map +1 -0
- package/dist/client/tools/ToolsListPage.d.ts +2 -0
- package/dist/client/tools/ToolsListPage.d.ts.map +1 -0
- package/dist/client/tools/ToolsListPage.js +67 -0
- package/dist/client/tools/ToolsListPage.js.map +1 -0
- package/dist/client/tools/ToolsSidebarSection.d.ts +2 -0
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -0
- package/dist/client/tools/ToolsSidebarSection.js +236 -0
- package/dist/client/tools/ToolsSidebarSection.js.map +1 -0
- package/dist/client/tools/iframe-bridge.d.ts +38 -0
- package/dist/client/tools/iframe-bridge.d.ts.map +1 -0
- package/dist/client/tools/iframe-bridge.js +207 -0
- package/dist/client/tools/iframe-bridge.js.map +1 -0
- package/dist/client/tools/index.d.ts +8 -0
- package/dist/client/tools/index.d.ts.map +1 -0
- package/dist/client/tools/index.js +8 -0
- package/dist/client/tools/index.js.map +1 -0
- package/dist/client/tools/tool-order.d.ts +7 -0
- package/dist/client/tools/tool-order.d.ts.map +1 -0
- package/dist/client/tools/tool-order.js +47 -0
- package/dist/client/tools/tool-order.js.map +1 -0
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.js +2 -3
- package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
- package/dist/client/use-change-version.d.ts +46 -0
- package/dist/client/use-change-version.d.ts.map +1 -0
- package/dist/client/use-change-version.js +135 -0
- package/dist/client/use-change-version.js.map +1 -0
- package/dist/client/use-chat-threads.d.ts +16 -2
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +87 -12
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-chat-threads.spec.d.ts +2 -0
- package/dist/client/use-chat-threads.spec.d.ts.map +1 -0
- package/dist/client/use-chat-threads.spec.js +85 -0
- package/dist/client/use-chat-threads.spec.js.map +1 -0
- package/dist/client/use-db-sync.d.ts +5 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +41 -16
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-pinch-zoom.d.ts +35 -0
- package/dist/client/use-pinch-zoom.d.ts.map +1 -0
- package/dist/client/use-pinch-zoom.js +105 -0
- package/dist/client/use-pinch-zoom.js.map +1 -0
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +99 -5
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +3 -0
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/store.d.ts +5 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +16 -1
- package/dist/extensions/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +3 -0
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -0
- package/dist/file-upload/actions/upload-image.js +145 -0
- package/dist/file-upload/actions/upload-image.js.map +1 -0
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +31 -11
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/file-upload/index.d.ts +1 -0
- package/dist/file-upload/index.d.ts.map +1 -1
- package/dist/file-upload/index.js +1 -0
- package/dist/file-upload/index.js.map +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +39 -0
- package/dist/file-upload/pre-upload-attachments.d.ts.map +1 -0
- package/dist/file-upload/pre-upload-attachments.js +110 -0
- package/dist/file-upload/pre-upload-attachments.js.map +1 -0
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +8 -7
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/org/context.d.ts +15 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +25 -0
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.d.ts +2 -2
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +3 -17
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/index.d.ts +1 -1
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +1 -1
- package/dist/org/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +6 -0
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +30 -6
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/script-helpers.d.ts +11 -2
- package/dist/resources/script-helpers.d.ts.map +1 -1
- package/dist/resources/script-helpers.js +20 -3
- package/dist/resources/script-helpers.js.map +1 -1
- package/dist/resources/store.d.ts +28 -3
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +170 -20
- package/dist/resources/store.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 +16 -4
- package/dist/scripts/resources/list.js.map +1 -1
- package/dist/scripts/resources/write.d.ts +1 -1
- package/dist/scripts/resources/write.d.ts.map +1 -1
- package/dist/scripts/resources/write.js +47 -3
- package/dist/scripts/resources/write.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +8 -3
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +214 -25
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts +35 -0
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +139 -8
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/app-url.d.ts +12 -6
- package/dist/server/app-url.d.ts.map +1 -1
- package/dist/server/app-url.js +58 -11
- package/dist/server/app-url.js.map +1 -1
- package/dist/server/auth.d.ts +22 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +316 -65
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +0 -4
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +0 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +23 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +29 -14
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +14 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +88 -11
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/google-auth-plugin.d.ts.map +1 -1
- package/dist/server/google-auth-plugin.js +65 -17
- package/dist/server/google-auth-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +47 -17
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/local-migration.d.ts +41 -0
- package/dist/server/local-migration.d.ts.map +1 -0
- package/dist/server/local-migration.js +235 -0
- package/dist/server/local-migration.js.map +1 -0
- package/dist/server/oauth-public-origin.d.ts.map +1 -1
- package/dist/server/oauth-public-origin.js +19 -1
- package/dist/server/oauth-public-origin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +74 -19
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +20 -5
- package/dist/server/poll.js.map +1 -1
- package/dist/server/request-context.d.ts +8 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +2 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/llm-connection.d.ts +10 -0
- package/dist/shared/llm-connection.d.ts.map +1 -0
- package/dist/shared/llm-connection.js +29 -0
- package/dist/shared/llm-connection.js.map +1 -0
- package/dist/shared/workspace-app-audience.d.ts +25 -0
- package/dist/shared/workspace-app-audience.d.ts.map +1 -0
- package/dist/shared/workspace-app-audience.js +126 -0
- package/dist/shared/workspace-app-audience.js.map +1 -0
- package/dist/shared/workspace-app-id.d.ts +1 -1
- package/dist/shared/workspace-app-id.d.ts.map +1 -1
- package/dist/shared/workspace-app-id.js +1 -0
- package/dist/shared/workspace-app-id.js.map +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +46 -5
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts.map +1 -1
- package/dist/sharing/actions/list-resource-shares.js +8 -1
- package/dist/sharing/actions/list-resource-shares.js.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +12 -3
- 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 +50 -1
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/sharing/registry.d.ts +26 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/styles/agent-native.css +91 -0
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +72 -0
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +60 -37
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +28 -17
- package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/dist/templates/default/AGENTS.md +22 -19
- package/dist/templates/default/actions/navigate.ts +3 -0
- package/dist/templates/default/app/hooks/use-navigation-state.ts +29 -5
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +251 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +264 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +130 -0
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +88 -0
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +191 -0
- package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +74 -0
- package/dist/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +75 -0
- package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +190 -0
- package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +168 -0
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +163 -0
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +205 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +720 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +92 -0
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +285 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +192 -0
- package/dist/templates/workspace-core/.agents/skills/onboarding/SKILL.md +43 -0
- package/dist/templates/workspace-core/.agents/skills/portability/SKILL.md +84 -0
- package/dist/templates/workspace-core/.agents/skills/qa/SKILL.md +313 -0
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +165 -0
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +41 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +239 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +191 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +79 -0
- package/dist/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +73 -0
- package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +217 -0
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +132 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +150 -0
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +124 -0
- package/dist/templates/workspace-core/AGENTS.md +16 -1
- package/dist/templates/workspace-root/AGENTS.md +35 -0
- package/dist/templates/workspace-root/README.md +7 -0
- package/dist/tools/actions.d.ts +3 -0
- package/dist/tools/actions.d.ts.map +1 -0
- package/dist/tools/actions.js +272 -0
- package/dist/tools/actions.js.map +1 -0
- package/dist/tools/fetch-tool.d.ts +23 -0
- package/dist/tools/fetch-tool.d.ts.map +1 -0
- package/dist/tools/fetch-tool.js +178 -0
- package/dist/tools/fetch-tool.js.map +1 -0
- package/dist/tools/html-shell.d.ts +45 -0
- package/dist/tools/html-shell.d.ts.map +1 -0
- package/dist/tools/html-shell.js +514 -0
- package/dist/tools/html-shell.js.map +1 -0
- package/dist/tools/proxy-security.d.ts +12 -0
- package/dist/tools/proxy-security.d.ts.map +1 -0
- package/dist/tools/proxy-security.js +158 -0
- package/dist/tools/proxy-security.js.map +1 -0
- package/dist/tools/routes.d.ts +2 -0
- package/dist/tools/routes.d.ts.map +1 -0
- package/dist/tools/routes.js +627 -0
- package/dist/tools/routes.js.map +1 -0
- package/dist/tools/schema.d.ts +664 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/schema.js +146 -0
- package/dist/tools/schema.js.map +1 -0
- package/dist/tools/slots/routes.d.ts +15 -0
- package/dist/tools/slots/routes.d.ts.map +1 -0
- package/dist/tools/slots/routes.js +94 -0
- package/dist/tools/slots/routes.js.map +1 -0
- package/dist/tools/slots/schema.d.ts +303 -0
- package/dist/tools/slots/schema.d.ts.map +1 -0
- package/dist/tools/slots/schema.js +76 -0
- package/dist/tools/slots/schema.js.map +1 -0
- package/dist/tools/slots/store.d.ts +66 -0
- package/dist/tools/slots/store.d.ts.map +1 -0
- package/dist/tools/slots/store.js +227 -0
- package/dist/tools/slots/store.js.map +1 -0
- package/dist/tools/store.d.ts +40 -0
- package/dist/tools/store.d.ts.map +1 -0
- package/dist/tools/store.js +193 -0
- package/dist/tools/store.js.map +1 -0
- package/dist/tools/theme.d.ts +2 -0
- package/dist/tools/theme.d.ts.map +1 -0
- package/dist/tools/theme.js +67 -0
- package/dist/tools/theme.js.map +1 -0
- package/dist/tools/url-safety.d.ts +24 -0
- package/dist/tools/url-safety.d.ts.map +1 -0
- package/dist/tools/url-safety.js +224 -0
- package/dist/tools/url-safety.js.map +1 -0
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/docs/content/authentication.md +36 -0
- package/docs/content/creating-templates.md +15 -0
- package/docs/content/dispatch.md +3 -3
- package/docs/content/multi-app-workspace.md +5 -0
- package/docs/content/tracking.md +12 -0
- package/docs/content/workspace-management.md +39 -4
- package/package.json +15 -12
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +72 -0
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +60 -37
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +28 -17
- package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/src/templates/default/AGENTS.md +22 -19
- package/src/templates/default/actions/navigate.ts +3 -0
- package/src/templates/default/app/hooks/use-navigation-state.ts +29 -5
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +251 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +264 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +130 -0
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +88 -0
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +191 -0
- package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +74 -0
- package/src/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +75 -0
- package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +190 -0
- package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +168 -0
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +163 -0
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +205 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +720 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +92 -0
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +285 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +192 -0
- package/src/templates/workspace-core/.agents/skills/onboarding/SKILL.md +43 -0
- package/src/templates/workspace-core/.agents/skills/portability/SKILL.md +84 -0
- package/src/templates/workspace-core/.agents/skills/qa/SKILL.md +313 -0
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +165 -0
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +41 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +239 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +191 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +79 -0
- package/src/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +73 -0
- package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +217 -0
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +132 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +150 -0
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +124 -0
- package/src/templates/workspace-core/AGENTS.md +16 -1
- package/src/templates/workspace-root/AGENTS.md +35 -0
- package/src/templates/workspace-root/README.md +7 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-request context using AsyncLocalStorage.\n *\n * Replaces the unsafe pattern of mutating `process.env.AGENT_USER_EMAIL` /\n * `process.env.AGENT_ORG_ID` on every request. On Node.js (Netlify, self-hosted)\n * concurrent requests would overwrite each other's env vars. AsyncLocalStorage\n * gives each async call-chain its own isolated context.\n *\n * Supported on all deployment targets:\n * - Node.js (native)\n * - Cloudflare Workers (via nodejs_compat flag)\n * - Deno Deploy (via node:async_hooks compat)\n *\n * For CLI scripts that run outside a request context, the getters fall back to\n * process.env so existing `AGENT_USER_EMAIL=x pnpm action foo` invocations\n * continue to work.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/**\n * Per-request agent-run state. Lives on `RequestContext.run` so the\n * agent-chat plugin can populate fields as the run progresses (owner,\n * resolved API key, system prompt, engine, model, threadId) without\n * mutating module-scope `let` bindings — those leak across concurrent\n * requests on a single Node.js process.\n *\n * Mutated in-place by `prepareRun`, `onEngineResolved`, `onRunStart` so\n * tool factory closures (automation, fetch, team, builder-browser) read\n * the live per-request value via `getRequestRunContext()`.\n */\nexport interface RequestRunContext {\n /** Origin of the current request (used by the builder-browser tool). */\n requestOrigin?: string;\n /** Resolved owner email (set by prepareRun). */\n owner?: string;\n /** Owner's active Anthropic API key (set by prepareRun). */\n userApiKey?: string;\n /** Thread ID for the current run (set by onRunStart). */\n threadId?: string;\n /** System prompt actually sent to the model for this run. */\n systemPrompt?: string;\n /** Engine instance for this run (set by onEngineResolved). */\n engine?: import(\"../agent/engine/types.js\").AgentEngine;\n /** Model name for this run (set by onEngineResolved). */\n model?: string;\n /** Tool calls made so far in the current agent loop. */\n toolCalls?: Array<{ name: string; input: unknown }>;\n /** Tool results returned so far in the current agent loop. */\n toolResults?: Array<{ name: string; content: string; isError: boolean }>;\n}\n\nexport interface RequestContext {\n userEmail?: string;\n userName?: string;\n orgId?: string;\n timezone?: string;\n /**\n * True when this request is being processed by an integration-platform\n * webhook (Slack, Telegram, etc.) where the function timeout is the\n * binding constraint. Code that calls slow remote APIs can use this to apply\n * tighter budgets on this path while leaving normal agent-chat callers\n * (5+ min budget) unaffected.\n */\n isIntegrationCaller?: boolean;\n /**\n * Metadata for the currently-processing integration task. This lets tools\n * that start long-running remote work persist a continuation that can update\n * the originating platform thread after the current function budget ends.\n */\n integration?: {\n taskId: string;\n attempts?: number;\n incoming: import(\"../integrations/types.js\").IncomingMessage;\n placeholderRef?: string;\n };\n /**\n * Mutable per-request agent-run state. Populated by the agent-chat plugin\n * during a run; tool closures dereference it on each invocation.\n */\n run?: RequestRunContext;\n}\n\nconst GLOBAL_KEY = \"__agentNativeRequestContextAls\" as const;\nconst OBSERVERS_KEY = \"__agentNativeRequestContextObservers\" as const;\ntype RequestContextObserver = (ctx: RequestContext) => void;\ntype GlobalWithRequestContext = typeof globalThis & {\n [GLOBAL_KEY]?: AsyncLocalStorage<RequestContext>;\n [OBSERVERS_KEY]?: RequestContextObserver[];\n};\nconst globalRef = globalThis as GlobalWithRequestContext;\nif (!globalRef[GLOBAL_KEY]) {\n globalRef[GLOBAL_KEY] = new AsyncLocalStorage<RequestContext>();\n}\nif (!globalRef[OBSERVERS_KEY]) {\n globalRef[OBSERVERS_KEY] = [];\n}\nconst als = globalRef[GLOBAL_KEY]!;\nconst observers = globalRef[OBSERVERS_KEY]!;\n\n/**\n * Register a callback fired every time `runWithRequestContext` enters a new\n * scope. The hook runs INSIDE the AsyncLocalStorage scope, so observability\n * helpers that read the current isolation scope (e.g. Sentry) attach to the\n * right per-request context.\n *\n * Returned function unregisters the observer. Observers must never throw —\n * any error is swallowed so a misbehaving observer can't break the request\n * path.\n */\nexport function addRequestContextObserver(\n observer: RequestContextObserver,\n): () => void {\n observers.push(observer);\n return () => {\n const i = observers.indexOf(observer);\n if (i !== -1) observers.splice(i, 1);\n };\n}\n\n/**\n * Run a callback within a per-request context. The context is available to all\n * async operations spawned from `fn` via `getRequestUserEmail()` / `getRequestOrgId()`.\n *\n * Any registered `addRequestContextObserver` callbacks fire inside the new\n * scope before `fn` runs, so observability code can pin user/org info onto\n * isolation-scoped backends (Sentry, OpenTelemetry, etc.).\n */\nexport function runWithRequestContext<T>(\n ctx: RequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n return als.run(ctx, () => {\n if (observers.length > 0) {\n for (const obs of observers) {\n try {\n obs(ctx);\n } catch {\n // Observers must never break the request path.\n }\n }\n }\n return fn();\n });\n}\n\n/**\n * Return the active request context, if this call chain is running under one.\n *\n * This is intentionally distinct from `getRequestUserEmail()`: callers that\n * have an active context with no authenticated user must not fall through to\n * process-wide CLI fallbacks such as `AGENT_USER_EMAIL` or \"latest session\".\n */\nexport function getRequestContext(): RequestContext | undefined {\n return als.getStore();\n}\n\n/**\n * True when AsyncLocalStorage has an active context for this call chain.\n * Useful for helpers that support both HTTP requests and standalone CLI runs.\n */\nexport function hasRequestContext(): boolean {\n return als.getStore() !== undefined;\n}\n\n/**\n * Get the current request's user email.\n *\n * - If a request context exists (HTTP/A2A path), returns its `userEmail` —\n * even when that value is `undefined`. The env fallback MUST NOT fire here:\n * a stale process-wide `AGENT_USER_EMAIL` from a CLI run or previous bug\n * would leak into an unauthenticated A2A/API call (e.g. unsigned or API-key\n * modes where `runWithRequestContext({ userEmail: undefined })` is used).\n * - Only when there is NO request context (CLI scripts) do we fall back to\n * `process.env.AGENT_USER_EMAIL`.\n */\nexport function getRequestUserEmail(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.userEmail;\n return process.env.AGENT_USER_EMAIL;\n}\n\n/**\n * Get the current request's display name, when the auth provider supplied one.\n *\n * The same request-context fallback rules as `getRequestUserEmail()` apply:\n * HTTP/A2A calls only read AsyncLocalStorage, while CLI scripts may opt in via\n * `AGENT_USER_NAME`.\n */\nexport function getRequestUserName(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.userName;\n return process.env.AGENT_USER_NAME;\n}\n\n/**\n * Get the current request's org ID.\n *\n * Same store-aware semantics as `getRequestUserEmail()` — env fallback is\n * CLI-only, so a request that explicitly has no org doesn't inherit a stale\n * `process.env.AGENT_ORG_ID` from a prior request on the same Lambda instance.\n */\nexport function getRequestOrgId(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.orgId;\n return process.env.AGENT_ORG_ID;\n}\n\n/**\n * Get the current request's IANA timezone (e.g. \"America/Los_Angeles\").\n * The UI sends this via the `x-user-timezone` header on every action call, and\n * the agent chat plugin propagates it into the request context so that\n * agent-initiated tool calls also see the user's timezone. Falls back to\n * `process.env.AGENT_USER_TIMEZONE` only for CLI scripts (no request context).\n */\nexport function getRequestTimezone(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.timezone;\n return process.env.AGENT_USER_TIMEZONE;\n}\n\n/**\n * Returns true when this request is on an integration-platform path (Slack,\n * Telegram, etc.) — i.e. we're inside the integration plugin's processor\n * function and the platform's deliver-by deadline plus the host's function\n * timeout are the binding budget. Non-integration callers (CLI, normal\n * agent chat) should treat this as `false`.\n */\nexport function isIntegrationCallerRequest(): boolean {\n return als.getStore()?.isIntegrationCaller === true;\n}\n\nexport function getIntegrationRequestContext():\n | NonNullable<RequestContext[\"integration\"]>\n | undefined {\n return als.getStore()?.integration;\n}\n\n/**\n * Convenience: returns `{ userEmail, orgId }` from the active request context,\n * suitable for passing to `resolveCredential(key, ctx)`. Returns `null` when\n * no user is associated with the call (e.g. an unauthenticated public route).\n *\n * For framework actions auto-mounted at `/_agent-native/actions/...` this is\n * always populated because action-routes wraps every invocation in\n * `runWithRequestContext`. For hand-written `/api/*` routes the calling code\n * is responsible for setting up the context (see `runWithRequestContext`).\n */\nexport function getCredentialContext(): {\n userEmail: string;\n orgId: string | null;\n} | null {\n const userEmail = getRequestUserEmail();\n if (!userEmail) return null;\n return { userEmail, orgId: getRequestOrgId() ?? null };\n}\n\n/**\n * Get the active request's mutable agent-run state. Returns `undefined` when\n * called outside an agent run (e.g. before `prepareRun` or in a non-agent\n * code path). Callers must tolerate the field absence; use the helper\n * `requireRequestRunContext()` if missing context is a programming error.\n */\nexport function getRequestRunContext(): RequestRunContext | undefined {\n const store = als.getStore();\n if (!store) return undefined;\n return store.run;\n}\n\n/**\n * Ensure a `RequestRunContext` exists on the active request store and\n * return it. Used by the agent-chat handler to attach run state once it\n * starts processing a chat request. Returns `undefined` if there is no\n * active request store (caller should not be invoking this outside ALS).\n */\nexport function ensureRequestRunContext(): RequestRunContext | undefined {\n const store = als.getStore();\n if (!store) return undefined;\n if (!store.run) store.run = {};\n return store.run;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAyErD,MAAM,UAAU,GAAG,gCAAyC,CAAC;AAC7D,MAAM,aAAa,GAAG,sCAA+C,CAAC;AAMtE,MAAM,SAAS,GAAG,UAAsC,CAAC;AACzD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;IAC3B,SAAS,CAAC,UAAU,CAAC,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAClE,CAAC;AACD,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC;IAC9B,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;AAChC,CAAC;AACD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAE,CAAC;AACnC,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,CAAE,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAgC;IAEhC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,CAAC;YAAE,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAmB,EACnB,EAAwB;IAExB,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE;QACvB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACH,GAAG,CAAC,GAAG,CAAC,CAAC;gBACX,CAAC;gBAAC,MAAM,CAAC;oBACP,+CAA+C;gBACjD,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,GAAG,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC;AACtC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,SAAS,CAAC;IAChD,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO,GAAG,CAAC,QAAQ,EAAE,EAAE,mBAAmB,KAAK,IAAI,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,4BAA4B;IAG1C,OAAO,GAAG,CAAC,QAAQ,EAAE,EAAE,WAAW,CAAC;AACrC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB;IAIlC,MAAM,SAAS,GAAG,mBAAmB,EAAE,CAAC;IACxC,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,KAAK,CAAC,GAAG,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,GAAG;QAAE,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;IAC/B,OAAO,KAAK,CAAC,GAAG,CAAC;AACnB,CAAC","sourcesContent":["/**\n * Per-request context using AsyncLocalStorage.\n *\n * Replaces the unsafe pattern of mutating `process.env.AGENT_USER_EMAIL` /\n * `process.env.AGENT_ORG_ID` on every request. On Node.js (Netlify, self-hosted)\n * concurrent requests would overwrite each other's env vars. AsyncLocalStorage\n * gives each async call-chain its own isolated context.\n *\n * Supported on all deployment targets:\n * - Node.js (native)\n * - Cloudflare Workers (via nodejs_compat flag)\n * - Deno Deploy (via node:async_hooks compat)\n *\n * For CLI scripts that run outside a request context, the getters fall back to\n * process.env so existing `AGENT_USER_EMAIL=x pnpm action foo` invocations\n * continue to work.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/**\n * Per-request agent-run state. Lives on `RequestContext.run` so the\n * agent-chat plugin can populate fields as the run progresses (owner,\n * resolved API key, system prompt, engine, model, threadId) without\n * mutating module-scope `let` bindings — those leak across concurrent\n * requests on a single Node.js process.\n *\n * Mutated in-place by `prepareRun`, `onEngineResolved`, `onRunStart` so\n * tool factory closures (automation, fetch, team, builder-browser) read\n * the live per-request value via `getRequestRunContext()`.\n */\nexport interface RequestRunContext {\n /** Origin of the current request (used by the builder-browser tool). */\n requestOrigin?: string;\n /** Stable browser tab id for tab-scoped app-state reads/writes. */\n browserTabId?: string;\n /** Resource scope for the current chat thread, e.g. the active deck. */\n chatScope?: {\n type: string;\n id: string;\n label?: string;\n } | null;\n /** Resolved owner email (set by prepareRun). */\n owner?: string;\n /** Owner's active Anthropic API key (set by prepareRun). */\n userApiKey?: string;\n /** Thread ID for the current run (set by onRunStart). */\n threadId?: string;\n /** System prompt actually sent to the model for this run. */\n systemPrompt?: string;\n /** Engine instance for this run (set by onEngineResolved). */\n engine?: import(\"../agent/engine/types.js\").AgentEngine;\n /** Model name for this run (set by onEngineResolved). */\n model?: string;\n /** Tool calls made so far in the current agent loop. */\n toolCalls?: Array<{ name: string; input: unknown }>;\n /** Tool results returned so far in the current agent loop. */\n toolResults?: Array<{ name: string; content: string; isError: boolean }>;\n}\n\nexport interface RequestContext {\n userEmail?: string;\n userName?: string;\n orgId?: string;\n timezone?: string;\n /**\n * True when this request is being processed by an integration-platform\n * webhook (Slack, Telegram, etc.) where the function timeout is the\n * binding constraint. Code that calls slow remote APIs can use this to apply\n * tighter budgets on this path while leaving normal agent-chat callers\n * (5+ min budget) unaffected.\n */\n isIntegrationCaller?: boolean;\n /**\n * Metadata for the currently-processing integration task. This lets tools\n * that start long-running remote work persist a continuation that can update\n * the originating platform thread after the current function budget ends.\n */\n integration?: {\n taskId: string;\n attempts?: number;\n incoming: import(\"../integrations/types.js\").IncomingMessage;\n placeholderRef?: string;\n };\n /**\n * Mutable per-request agent-run state. Populated by the agent-chat plugin\n * during a run; tool closures dereference it on each invocation.\n */\n run?: RequestRunContext;\n}\n\nconst GLOBAL_KEY = \"__agentNativeRequestContextAls\" as const;\nconst OBSERVERS_KEY = \"__agentNativeRequestContextObservers\" as const;\ntype RequestContextObserver = (ctx: RequestContext) => void;\ntype GlobalWithRequestContext = typeof globalThis & {\n [GLOBAL_KEY]?: AsyncLocalStorage<RequestContext>;\n [OBSERVERS_KEY]?: RequestContextObserver[];\n};\nconst globalRef = globalThis as GlobalWithRequestContext;\nif (!globalRef[GLOBAL_KEY]) {\n globalRef[GLOBAL_KEY] = new AsyncLocalStorage<RequestContext>();\n}\nif (!globalRef[OBSERVERS_KEY]) {\n globalRef[OBSERVERS_KEY] = [];\n}\nconst als = globalRef[GLOBAL_KEY]!;\nconst observers = globalRef[OBSERVERS_KEY]!;\n\n/**\n * Register a callback fired every time `runWithRequestContext` enters a new\n * scope. The hook runs INSIDE the AsyncLocalStorage scope, so observability\n * helpers that read the current isolation scope (e.g. Sentry) attach to the\n * right per-request context.\n *\n * Returned function unregisters the observer. Observers must never throw —\n * any error is swallowed so a misbehaving observer can't break the request\n * path.\n */\nexport function addRequestContextObserver(\n observer: RequestContextObserver,\n): () => void {\n observers.push(observer);\n return () => {\n const i = observers.indexOf(observer);\n if (i !== -1) observers.splice(i, 1);\n };\n}\n\n/**\n * Run a callback within a per-request context. The context is available to all\n * async operations spawned from `fn` via `getRequestUserEmail()` / `getRequestOrgId()`.\n *\n * Any registered `addRequestContextObserver` callbacks fire inside the new\n * scope before `fn` runs, so observability code can pin user/org info onto\n * isolation-scoped backends (Sentry, OpenTelemetry, etc.).\n */\nexport function runWithRequestContext<T>(\n ctx: RequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n return als.run(ctx, () => {\n if (observers.length > 0) {\n for (const obs of observers) {\n try {\n obs(ctx);\n } catch {\n // Observers must never break the request path.\n }\n }\n }\n return fn();\n });\n}\n\n/**\n * Return the active request context, if this call chain is running under one.\n *\n * This is intentionally distinct from `getRequestUserEmail()`: callers that\n * have an active context with no authenticated user must not fall through to\n * process-wide CLI fallbacks such as `AGENT_USER_EMAIL` or \"latest session\".\n */\nexport function getRequestContext(): RequestContext | undefined {\n return als.getStore();\n}\n\n/**\n * True when AsyncLocalStorage has an active context for this call chain.\n * Useful for helpers that support both HTTP requests and standalone CLI runs.\n */\nexport function hasRequestContext(): boolean {\n return als.getStore() !== undefined;\n}\n\n/**\n * Get the current request's user email.\n *\n * - If a request context exists (HTTP/A2A path), returns its `userEmail` —\n * even when that value is `undefined`. The env fallback MUST NOT fire here:\n * a stale process-wide `AGENT_USER_EMAIL` from a CLI run or previous bug\n * would leak into an unauthenticated A2A/API call (e.g. unsigned or API-key\n * modes where `runWithRequestContext({ userEmail: undefined })` is used).\n * - Only when there is NO request context (CLI scripts) do we fall back to\n * `process.env.AGENT_USER_EMAIL`.\n */\nexport function getRequestUserEmail(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.userEmail;\n return process.env.AGENT_USER_EMAIL;\n}\n\n/**\n * Get the current request's display name, when the auth provider supplied one.\n *\n * The same request-context fallback rules as `getRequestUserEmail()` apply:\n * HTTP/A2A calls only read AsyncLocalStorage, while CLI scripts may opt in via\n * `AGENT_USER_NAME`.\n */\nexport function getRequestUserName(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.userName;\n return process.env.AGENT_USER_NAME;\n}\n\n/**\n * Get the current request's org ID.\n *\n * Same store-aware semantics as `getRequestUserEmail()` — env fallback is\n * CLI-only, so a request that explicitly has no org doesn't inherit a stale\n * `process.env.AGENT_ORG_ID` from a prior request on the same Lambda instance.\n */\nexport function getRequestOrgId(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.orgId;\n return process.env.AGENT_ORG_ID;\n}\n\n/**\n * Get the current request's IANA timezone (e.g. \"America/Los_Angeles\").\n * The UI sends this via the `x-user-timezone` header on every action call, and\n * the agent chat plugin propagates it into the request context so that\n * agent-initiated tool calls also see the user's timezone. Falls back to\n * `process.env.AGENT_USER_TIMEZONE` only for CLI scripts (no request context).\n */\nexport function getRequestTimezone(): string | undefined {\n const store = als.getStore();\n if (store !== undefined) return store.timezone;\n return process.env.AGENT_USER_TIMEZONE;\n}\n\n/**\n * Returns true when this request is on an integration-platform path (Slack,\n * Telegram, etc.) — i.e. we're inside the integration plugin's processor\n * function and the platform's deliver-by deadline plus the host's function\n * timeout are the binding budget. Non-integration callers (CLI, normal\n * agent chat) should treat this as `false`.\n */\nexport function isIntegrationCallerRequest(): boolean {\n return als.getStore()?.isIntegrationCaller === true;\n}\n\nexport function getIntegrationRequestContext():\n | NonNullable<RequestContext[\"integration\"]>\n | undefined {\n return als.getStore()?.integration;\n}\n\n/**\n * Convenience: returns `{ userEmail, orgId }` from the active request context,\n * suitable for passing to `resolveCredential(key, ctx)`. Returns `null` when\n * no user is associated with the call (e.g. an unauthenticated public route).\n *\n * For framework actions auto-mounted at `/_agent-native/actions/...` this is\n * always populated because action-routes wraps every invocation in\n * `runWithRequestContext`. For hand-written `/api/*` routes the calling code\n * is responsible for setting up the context (see `runWithRequestContext`).\n */\nexport function getCredentialContext(): {\n userEmail: string;\n orgId: string | null;\n} | null {\n const userEmail = getRequestUserEmail();\n if (!userEmail) return null;\n return { userEmail, orgId: getRequestOrgId() ?? null };\n}\n\n/**\n * Get the active request's mutable agent-run state. Returns `undefined` when\n * called outside an agent run (e.g. before `prepareRun` or in a non-agent\n * code path). Callers must tolerate the field absence; use the helper\n * `requireRequestRunContext()` if missing context is a programming error.\n */\nexport function getRequestRunContext(): RequestRunContext | undefined {\n const store = als.getStore();\n if (!store) return undefined;\n return store.run;\n}\n\n/**\n * Ensure a `RequestRunContext` exists on the active request store and\n * return it. Used by the agent-chat handler to attach run state once it\n * starts processing a chat request. Returns `undefined` if there is no\n * active request store (caller should not be invoking this outside ALS).\n */\nexport function ensureRequestRunContext(): RequestRunContext | undefined {\n const store = als.getStore();\n if (!store) return undefined;\n if (!store.run) store.run = {};\n return store.run;\n}\n"]}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -2,5 +2,7 @@ export { agentChat, type AgentChatMessage, type AgentChatCallOptions, type Agent
|
|
|
2
2
|
export { agentEnv, type EnvVar } from "./agent-env.js";
|
|
3
3
|
export { extractOAuthStateAppId } from "./oauth-state.js";
|
|
4
4
|
export { truncate } from "./truncate.js";
|
|
5
|
+
export { llmConnectionTrackingProperties, normalizeLlmConnection, type LlmConnectionStatus, } from "./llm-connection.js";
|
|
5
6
|
export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, } from "./workspace-app-id.js";
|
|
7
|
+
export { DEFAULT_WORKSPACE_APP_AUDIENCE, WORKSPACE_APP_AUDIENCES, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromEnv, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromEnv, workspaceAppRouteAccessFromPackageJson, type WorkspaceAppRouteAccess, type WorkspaceAppRouteAccessFromConfig, type WorkspaceAppAudience, } from "./workspace-app-audience.js";
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,EACtB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAC"}
|
package/dist/shared/index.js
CHANGED
|
@@ -2,5 +2,7 @@ export { agentChat, } from "./agent-chat.js";
|
|
|
2
2
|
export { agentEnv } from "./agent-env.js";
|
|
3
3
|
export { extractOAuthStateAppId } from "./oauth-state.js";
|
|
4
4
|
export { truncate } from "./truncate.js";
|
|
5
|
+
export { llmConnectionTrackingProperties, normalizeLlmConnection, } from "./llm-connection.js";
|
|
5
6
|
export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, } from "./workspace-app-id.js";
|
|
7
|
+
export { DEFAULT_WORKSPACE_APP_AUDIENCE, WORKSPACE_APP_AUDIENCES, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromEnv, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromEnv, workspaceAppRouteAccessFromPackageJson, } from "./workspace-app-audience.js";
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/shared/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAIV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n agentChat,\n type AgentChatMessage,\n type AgentChatCallOptions,\n type AgentChatResponse,\n} from \"./agent-chat.js\";\nexport { agentEnv, type EnvVar } from \"./agent-env.js\";\nexport { extractOAuthStateAppId } from \"./oauth-state.js\";\nexport { truncate } from \"./truncate.js\";\nexport {\n DISPATCH_WORKSPACE_ROOT_REDIRECTS,\n RESERVED_WORKSPACE_APP_IDS,\n assertValidWorkspaceAppId,\n getWorkspaceAppIdValidationError,\n isValidWorkspaceAppIdFormat,\n} from \"./workspace-app-id.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAIV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,GAEvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,GAIvC,MAAM,6BAA6B,CAAC","sourcesContent":["export {\n agentChat,\n type AgentChatMessage,\n type AgentChatCallOptions,\n type AgentChatResponse,\n} from \"./agent-chat.js\";\nexport { agentEnv, type EnvVar } from \"./agent-env.js\";\nexport { extractOAuthStateAppId } from \"./oauth-state.js\";\nexport { truncate } from \"./truncate.js\";\nexport {\n llmConnectionTrackingProperties,\n normalizeLlmConnection,\n type LlmConnectionStatus,\n} from \"./llm-connection.js\";\nexport {\n DISPATCH_WORKSPACE_ROOT_REDIRECTS,\n RESERVED_WORKSPACE_APP_IDS,\n assertValidWorkspaceAppId,\n getWorkspaceAppIdValidationError,\n isValidWorkspaceAppIdFormat,\n} from \"./workspace-app-id.js\";\nexport {\n DEFAULT_WORKSPACE_APP_AUDIENCE,\n WORKSPACE_APP_AUDIENCES,\n normalizeWorkspaceAppAudience,\n normalizeWorkspaceAppPathList,\n workspaceAppAudienceFromEnv,\n workspaceAppAudienceFromPackageJson,\n workspaceAppRouteAccessFromEnv,\n workspaceAppRouteAccessFromPackageJson,\n type WorkspaceAppRouteAccess,\n type WorkspaceAppRouteAccessFromConfig,\n type WorkspaceAppAudience,\n} from \"./workspace-app-audience.js\";\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface LlmConnectionStatus {
|
|
2
|
+
configured?: boolean;
|
|
3
|
+
engine?: string | null;
|
|
4
|
+
model?: string | null;
|
|
5
|
+
source?: string | null;
|
|
6
|
+
envVar?: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function normalizeLlmConnection(engine: string | null | undefined): string;
|
|
9
|
+
export declare function llmConnectionTrackingProperties(status: LlmConnectionStatus | null | undefined): Record<string, unknown>;
|
|
10
|
+
//# sourceMappingURL=llm-connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-connection.d.ts","sourceRoot":"","sources":["../../src/shared/llm-connection.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,MAAM,CAOR;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,GAC7C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function normalizeLlmConnection(engine) {
|
|
2
|
+
const value = typeof engine === "string" ? engine.trim() : "";
|
|
3
|
+
if (!value)
|
|
4
|
+
return "none";
|
|
5
|
+
if (value.startsWith("ai-sdk:")) {
|
|
6
|
+
return value.slice("ai-sdk:".length) || value;
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function llmConnectionTrackingProperties(status) {
|
|
11
|
+
if (!status) {
|
|
12
|
+
return { llm_connection: "unknown" };
|
|
13
|
+
}
|
|
14
|
+
if (!status.configured || !status.engine) {
|
|
15
|
+
return {
|
|
16
|
+
llm_connection: "none",
|
|
17
|
+
llm_connection_configured: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
llm_connection: normalizeLlmConnection(status.engine),
|
|
22
|
+
llm_connection_configured: true,
|
|
23
|
+
llm_engine: status.engine,
|
|
24
|
+
...(status.model ? { llm_model: status.model } : {}),
|
|
25
|
+
...(status.source ? { llm_connection_source: status.source } : {}),
|
|
26
|
+
...(status.envVar ? { llm_connection_env_var: status.envVar } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=llm-connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-connection.js","sourceRoot":"","sources":["../../src/shared/llm-connection.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,sBAAsB,CACpC,MAAiC;IAEjC,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,MAA8C;IAE9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACzC,OAAO;YACL,cAAc,EAAE,MAAM;YACtB,yBAAyB,EAAE,KAAK;SACjC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,cAAc,EAAE,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC;QACrD,yBAAyB,EAAE,IAAI;QAC/B,UAAU,EAAE,MAAM,CAAC,MAAM;QACzB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC","sourcesContent":["export interface LlmConnectionStatus {\n configured?: boolean;\n engine?: string | null;\n model?: string | null;\n source?: string | null;\n envVar?: string | null;\n}\n\nexport function normalizeLlmConnection(\n engine: string | null | undefined,\n): string {\n const value = typeof engine === \"string\" ? engine.trim() : \"\";\n if (!value) return \"none\";\n if (value.startsWith(\"ai-sdk:\")) {\n return value.slice(\"ai-sdk:\".length) || value;\n }\n return value;\n}\n\nexport function llmConnectionTrackingProperties(\n status: LlmConnectionStatus | null | undefined,\n): Record<string, unknown> {\n if (!status) {\n return { llm_connection: \"unknown\" };\n }\n\n if (!status.configured || !status.engine) {\n return {\n llm_connection: \"none\",\n llm_connection_configured: false,\n };\n }\n\n return {\n llm_connection: normalizeLlmConnection(status.engine),\n llm_connection_configured: true,\n llm_engine: status.engine,\n ...(status.model ? { llm_model: status.model } : {}),\n ...(status.source ? { llm_connection_source: status.source } : {}),\n ...(status.envVar ? { llm_connection_env_var: status.envVar } : {}),\n };\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const WORKSPACE_APP_AUDIENCES: readonly ["internal", "public"];
|
|
2
|
+
export type WorkspaceAppAudience = (typeof WORKSPACE_APP_AUDIENCES)[number];
|
|
3
|
+
export declare const DEFAULT_WORKSPACE_APP_AUDIENCE: WorkspaceAppAudience;
|
|
4
|
+
export interface WorkspaceAppRouteAccess {
|
|
5
|
+
publicPaths: string[];
|
|
6
|
+
protectedPaths: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function normalizeWorkspaceAppAudience(value: unknown): WorkspaceAppAudience;
|
|
9
|
+
export declare function normalizeWorkspaceAppPathList(value: unknown): string[];
|
|
10
|
+
export declare function workspaceAppAudienceFromEnv(env?: Record<string, string | undefined>): WorkspaceAppAudience | undefined;
|
|
11
|
+
export declare function workspaceAppRouteAccessFromEnv(env?: Record<string, string | undefined>): WorkspaceAppRouteAccess;
|
|
12
|
+
export declare function workspaceAppAudienceFromPackageJson(pkg: unknown): WorkspaceAppAudience | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Per-app route-access config read from a `package.json`. Each field is
|
|
15
|
+
* `undefined` when the corresponding key is fully absent from every
|
|
16
|
+
* supported alias chain — that lets callers distinguish "user didn't say"
|
|
17
|
+
* from "user set [] to clear inherited overrides". `workspaceAppRouteAccess`
|
|
18
|
+
* always emits a full `WorkspaceAppRouteAccess` for runtime consumption.
|
|
19
|
+
*/
|
|
20
|
+
export interface WorkspaceAppRouteAccessFromConfig {
|
|
21
|
+
publicPaths?: string[];
|
|
22
|
+
protectedPaths?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function workspaceAppRouteAccessFromPackageJson(pkg: unknown): WorkspaceAppRouteAccessFromConfig;
|
|
25
|
+
//# sourceMappingURL=workspace-app-audience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-app-audience.d.ts","sourceRoot":"","sources":["../../src/shared/workspace-app-audience.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,iCAAkC,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,eAAO,MAAM,8BAA8B,EAAE,oBAAiC,CAAC;AAE/E,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,oBAAoB,CAEtB;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAyBtE;AAED,wBAAgB,2BAA2B,CACzC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACvC,oBAAoB,GAAG,SAAS,CAOlC;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACvC,uBAAuB,CAYzB;AAED,wBAAgB,mCAAmC,CACjD,GAAG,EAAE,OAAO,GACX,oBAAoB,GAAG,SAAS,CASlC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iCAAiC;IAChD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAgB,sCAAsC,CACpD,GAAG,EAAE,OAAO,GACX,iCAAiC,CAuBnC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const WORKSPACE_APP_AUDIENCES = ["internal", "public"];
|
|
2
|
+
export const DEFAULT_WORKSPACE_APP_AUDIENCE = "internal";
|
|
3
|
+
export function normalizeWorkspaceAppAudience(value) {
|
|
4
|
+
return value === "public" ? "public" : DEFAULT_WORKSPACE_APP_AUDIENCE;
|
|
5
|
+
}
|
|
6
|
+
export function normalizeWorkspaceAppPathList(value) {
|
|
7
|
+
let rawPaths = [];
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
rawPaths = value;
|
|
10
|
+
}
|
|
11
|
+
else if (typeof value === "string") {
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (!trimmed)
|
|
14
|
+
return [];
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(trimmed);
|
|
17
|
+
// When JSON parses to a non-array (e.g. a single quoted string
|
|
18
|
+
// `"/api"`), use the parsed value, not the original quoted form —
|
|
19
|
+
// otherwise the `/`-prefix filter below silently drops it.
|
|
20
|
+
rawPaths = Array.isArray(parsed) ? parsed : [parsed];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
rawPaths = trimmed.split(",");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const paths = rawPaths
|
|
27
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
28
|
+
.filter((entry) => entry.startsWith("/"))
|
|
29
|
+
.map((entry) => entry.length > 1 && entry.endsWith("/") ? entry.slice(0, -1) : entry);
|
|
30
|
+
return Array.from(new Set(paths));
|
|
31
|
+
}
|
|
32
|
+
export function workspaceAppAudienceFromEnv(env) {
|
|
33
|
+
const source = env ?? (typeof process !== "undefined" ? process.env : {});
|
|
34
|
+
const raw = source.AGENT_NATIVE_WORKSPACE_APP_AUDIENCE ??
|
|
35
|
+
source.VITE_AGENT_NATIVE_WORKSPACE_APP_AUDIENCE;
|
|
36
|
+
if (raw === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
return normalizeWorkspaceAppAudience(raw);
|
|
39
|
+
}
|
|
40
|
+
export function workspaceAppRouteAccessFromEnv(env) {
|
|
41
|
+
const source = env ?? (typeof process !== "undefined" ? process.env : {});
|
|
42
|
+
return {
|
|
43
|
+
publicPaths: normalizeWorkspaceAppPathList(source.AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS ??
|
|
44
|
+
source.VITE_AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS),
|
|
45
|
+
protectedPaths: normalizeWorkspaceAppPathList(source.AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS ??
|
|
46
|
+
source.VITE_AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function workspaceAppAudienceFromPackageJson(pkg) {
|
|
50
|
+
const config = workspaceAppConfigFromPackageJson(pkg);
|
|
51
|
+
const raw = config?.workspaceApp?.audience ??
|
|
52
|
+
config?.workspace?.audience ??
|
|
53
|
+
config?.audience ??
|
|
54
|
+
config?.root?.workspaceAppAudience;
|
|
55
|
+
if (raw === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
return normalizeWorkspaceAppAudience(raw);
|
|
58
|
+
}
|
|
59
|
+
export function workspaceAppRouteAccessFromPackageJson(pkg) {
|
|
60
|
+
const config = workspaceAppConfigFromPackageJson(pkg);
|
|
61
|
+
const rawPublic = config?.workspaceApp?.publicPaths ??
|
|
62
|
+
config?.workspaceApp?.publicPagePaths ??
|
|
63
|
+
config?.workspace?.publicPaths ??
|
|
64
|
+
config?.publicPaths ??
|
|
65
|
+
config?.root?.workspaceAppPublicPaths;
|
|
66
|
+
const rawProtected = config?.workspaceApp?.protectedPaths ??
|
|
67
|
+
config?.workspaceApp?.privatePaths ??
|
|
68
|
+
config?.workspaceApp?.authRequiredPaths ??
|
|
69
|
+
config?.workspace?.protectedPaths ??
|
|
70
|
+
config?.protectedPaths ??
|
|
71
|
+
config?.root?.workspaceAppProtectedPaths;
|
|
72
|
+
return {
|
|
73
|
+
...(isPathConfigValueSet(rawPublic)
|
|
74
|
+
? { publicPaths: normalizeWorkspaceAppPathList(rawPublic) }
|
|
75
|
+
: {}),
|
|
76
|
+
...(isPathConfigValueSet(rawProtected)
|
|
77
|
+
? { protectedPaths: normalizeWorkspaceAppPathList(rawProtected) }
|
|
78
|
+
: {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Only treat a package.json field as "explicitly set" when its raw value is a
|
|
83
|
+
* supported type — an array, a string, or explicit null. Garbage types like
|
|
84
|
+
* `false`, `0`, or `{}` are ignored (left as undefined) so a typo such as
|
|
85
|
+
* `"publicPaths": false` doesn't silently clear an inherited manifest
|
|
86
|
+
* override. (`normalizeWorkspaceAppPathList` happily turns those into `[]`,
|
|
87
|
+
* which without this guard would be indistinguishable from a deliberate
|
|
88
|
+
* empty array.)
|
|
89
|
+
*/
|
|
90
|
+
function isPathConfigValueSet(value) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return false;
|
|
93
|
+
if (value === null)
|
|
94
|
+
return true;
|
|
95
|
+
if (Array.isArray(value))
|
|
96
|
+
return true;
|
|
97
|
+
return typeof value === "string";
|
|
98
|
+
}
|
|
99
|
+
function workspaceAppConfigFromPackageJson(pkg) {
|
|
100
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg))
|
|
101
|
+
return undefined;
|
|
102
|
+
const record = pkg;
|
|
103
|
+
const config = record["agent-native"] ?? record.agentNative;
|
|
104
|
+
const nested = config && typeof config === "object" && !Array.isArray(config)
|
|
105
|
+
? config
|
|
106
|
+
: {};
|
|
107
|
+
const workspaceApp = nested.workspaceApp &&
|
|
108
|
+
typeof nested.workspaceApp === "object" &&
|
|
109
|
+
!Array.isArray(nested.workspaceApp)
|
|
110
|
+
? nested.workspaceApp
|
|
111
|
+
: undefined;
|
|
112
|
+
const workspace = nested.workspace &&
|
|
113
|
+
typeof nested.workspace === "object" &&
|
|
114
|
+
!Array.isArray(nested.workspace)
|
|
115
|
+
? nested.workspace
|
|
116
|
+
: undefined;
|
|
117
|
+
return {
|
|
118
|
+
root: record,
|
|
119
|
+
workspaceApp,
|
|
120
|
+
workspace,
|
|
121
|
+
audience: nested.audience,
|
|
122
|
+
publicPaths: nested.publicPaths,
|
|
123
|
+
protectedPaths: nested.protectedPaths,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=workspace-app-audience.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-app-audience.js","sourceRoot":"","sources":["../../src/shared/workspace-app-audience.ts"],"names":[],"mappings":"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","sourcesContent":["export const WORKSPACE_APP_AUDIENCES = [\"internal\", \"public\"] as const;\n\nexport type WorkspaceAppAudience = (typeof WORKSPACE_APP_AUDIENCES)[number];\n\nexport const DEFAULT_WORKSPACE_APP_AUDIENCE: WorkspaceAppAudience = \"internal\";\n\nexport interface WorkspaceAppRouteAccess {\n publicPaths: string[];\n protectedPaths: string[];\n}\n\nexport function normalizeWorkspaceAppAudience(\n value: unknown,\n): WorkspaceAppAudience {\n return value === \"public\" ? \"public\" : DEFAULT_WORKSPACE_APP_AUDIENCE;\n}\n\nexport function normalizeWorkspaceAppPathList(value: unknown): string[] {\n let rawPaths: unknown[] = [];\n if (Array.isArray(value)) {\n rawPaths = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) return [];\n try {\n const parsed = JSON.parse(trimmed);\n // When JSON parses to a non-array (e.g. a single quoted string\n // `\"/api\"`), use the parsed value, not the original quoted form —\n // otherwise the `/`-prefix filter below silently drops it.\n rawPaths = Array.isArray(parsed) ? parsed : [parsed];\n } catch {\n rawPaths = trimmed.split(\",\");\n }\n }\n\n const paths = rawPaths\n .map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n .filter((entry) => entry.startsWith(\"/\"))\n .map((entry) =>\n entry.length > 1 && entry.endsWith(\"/\") ? entry.slice(0, -1) : entry,\n );\n return Array.from(new Set(paths));\n}\n\nexport function workspaceAppAudienceFromEnv(\n env?: Record<string, string | undefined>,\n): WorkspaceAppAudience | undefined {\n const source = env ?? (typeof process !== \"undefined\" ? process.env : {});\n const raw =\n source.AGENT_NATIVE_WORKSPACE_APP_AUDIENCE ??\n source.VITE_AGENT_NATIVE_WORKSPACE_APP_AUDIENCE;\n if (raw === undefined) return undefined;\n return normalizeWorkspaceAppAudience(raw);\n}\n\nexport function workspaceAppRouteAccessFromEnv(\n env?: Record<string, string | undefined>,\n): WorkspaceAppRouteAccess {\n const source = env ?? (typeof process !== \"undefined\" ? process.env : {});\n return {\n publicPaths: normalizeWorkspaceAppPathList(\n source.AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS ??\n source.VITE_AGENT_NATIVE_WORKSPACE_APP_PUBLIC_PATHS,\n ),\n protectedPaths: normalizeWorkspaceAppPathList(\n source.AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS ??\n source.VITE_AGENT_NATIVE_WORKSPACE_APP_PROTECTED_PATHS,\n ),\n };\n}\n\nexport function workspaceAppAudienceFromPackageJson(\n pkg: unknown,\n): WorkspaceAppAudience | undefined {\n const config = workspaceAppConfigFromPackageJson(pkg);\n const raw =\n config?.workspaceApp?.audience ??\n config?.workspace?.audience ??\n config?.audience ??\n config?.root?.workspaceAppAudience;\n if (raw === undefined) return undefined;\n return normalizeWorkspaceAppAudience(raw);\n}\n\n/**\n * Per-app route-access config read from a `package.json`. Each field is\n * `undefined` when the corresponding key is fully absent from every\n * supported alias chain — that lets callers distinguish \"user didn't say\"\n * from \"user set [] to clear inherited overrides\". `workspaceAppRouteAccess`\n * always emits a full `WorkspaceAppRouteAccess` for runtime consumption.\n */\nexport interface WorkspaceAppRouteAccessFromConfig {\n publicPaths?: string[];\n protectedPaths?: string[];\n}\n\nexport function workspaceAppRouteAccessFromPackageJson(\n pkg: unknown,\n): WorkspaceAppRouteAccessFromConfig {\n const config = workspaceAppConfigFromPackageJson(pkg);\n const rawPublic =\n config?.workspaceApp?.publicPaths ??\n config?.workspaceApp?.publicPagePaths ??\n config?.workspace?.publicPaths ??\n config?.publicPaths ??\n config?.root?.workspaceAppPublicPaths;\n const rawProtected =\n config?.workspaceApp?.protectedPaths ??\n config?.workspaceApp?.privatePaths ??\n config?.workspaceApp?.authRequiredPaths ??\n config?.workspace?.protectedPaths ??\n config?.protectedPaths ??\n config?.root?.workspaceAppProtectedPaths;\n return {\n ...(isPathConfigValueSet(rawPublic)\n ? { publicPaths: normalizeWorkspaceAppPathList(rawPublic) }\n : {}),\n ...(isPathConfigValueSet(rawProtected)\n ? { protectedPaths: normalizeWorkspaceAppPathList(rawProtected) }\n : {}),\n };\n}\n\n/**\n * Only treat a package.json field as \"explicitly set\" when its raw value is a\n * supported type — an array, a string, or explicit null. Garbage types like\n * `false`, `0`, or `{}` are ignored (left as undefined) so a typo such as\n * `\"publicPaths\": false` doesn't silently clear an inherited manifest\n * override. (`normalizeWorkspaceAppPathList` happily turns those into `[]`,\n * which without this guard would be indistinguishable from a deliberate\n * empty array.)\n */\nfunction isPathConfigValueSet(value: unknown): boolean {\n if (value === undefined) return false;\n if (value === null) return true;\n if (Array.isArray(value)) return true;\n return typeof value === \"string\";\n}\n\nfunction workspaceAppConfigFromPackageJson(pkg: unknown):\n | {\n root: Record<string, any>;\n workspaceApp?: Record<string, any>;\n workspace?: Record<string, any>;\n audience?: unknown;\n publicPaths?: unknown;\n protectedPaths?: unknown;\n }\n | undefined {\n if (!pkg || typeof pkg !== \"object\" || Array.isArray(pkg)) return undefined;\n const record = pkg as Record<string, any>;\n const config = record[\"agent-native\"] ?? record.agentNative;\n const nested =\n config && typeof config === \"object\" && !Array.isArray(config)\n ? (config as Record<string, any>)\n : {};\n const workspaceApp =\n nested.workspaceApp &&\n typeof nested.workspaceApp === \"object\" &&\n !Array.isArray(nested.workspaceApp)\n ? (nested.workspaceApp as Record<string, any>)\n : undefined;\n const workspace =\n nested.workspace &&\n typeof nested.workspace === \"object\" &&\n !Array.isArray(nested.workspace)\n ? (nested.workspace as Record<string, any>)\n : undefined;\n return {\n root: record,\n workspaceApp,\n workspace,\n audience: nested.audience,\n publicPaths: nested.publicPaths,\n protectedPaths: nested.protectedPaths,\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DISPATCH_WORKSPACE_ROOT_REDIRECTS: readonly [readonly ["overview", "overview"], readonly ["login", "login"], readonly ["signup", "signup"], readonly ["apps", "apps"], readonly ["apps/new-app", "new-app"], readonly ["new-app", "new-app"], readonly ["vault", "vault"], readonly ["integrations", "integrations"], readonly ["agents", "agents"], readonly ["workspace", "workspace"], readonly ["messaging", "messaging"], readonly ["extensions", "extensions"], readonly ["destinations", "destinations"], readonly ["identities", "identities"], readonly ["approval", "approval"], readonly ["approvals", "approvals"], readonly ["audit", "audit"], readonly ["team", "team"]];
|
|
1
|
+
export declare const DISPATCH_WORKSPACE_ROOT_REDIRECTS: readonly [readonly ["overview", "overview"], readonly ["login", "login"], readonly ["signup", "signup"], readonly ["apps", "apps"], readonly ["apps/new-app", "new-app"], readonly ["new-app", "new-app"], readonly ["vault", "vault"], readonly ["integrations", "integrations"], readonly ["agents", "agents"], readonly ["workspace", "workspace"], readonly ["messaging", "messaging"], readonly ["extensions", "extensions"], readonly ["destinations", "destinations"], readonly ["identities", "identities"], readonly ["approval", "approval"], readonly ["approvals", "approvals"], readonly ["audit", "audit"], readonly ["thread-debug", "thread-debug"], readonly ["team", "team"]];
|
|
2
2
|
export declare const RESERVED_WORKSPACE_APP_IDS: Set<string>;
|
|
3
3
|
export declare function isValidWorkspaceAppIdFormat(appId: string): boolean;
|
|
4
4
|
export declare function getWorkspaceAppIdValidationError(appId: string): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-app-id.d.ts","sourceRoot":"","sources":["../../src/shared/workspace-app-id.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,
|
|
1
|
+
{"version":3,"file":"workspace-app-id.d.ts","sourceRoot":"","sources":["../../src/shared/workspace-app-id.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,iqBAoBpC,CAAC;AAEX,eAAO,MAAM,0BAA0B,aAYrC,CAAC;AAEH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ7E;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-app-id.js","sourceRoot":"","sources":["../../src/shared/workspace-app-id.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iCAAiC,GAAG;IAC/C,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,MAAM,EAAE,MAAM,CAAC;IAChB,CAAC,cAAc,EAAE,SAAS,CAAC;IAC3B,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,MAAM,EAAE,MAAM,CAAC;CACR,CAAC;AAEX,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IAChD,eAAe;IACf,mBAAmB;IACnB,KAAK;IACL,MAAM;IACN,UAAU;IACV,SAAS;IACT,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,OAAO;IACP,GAAG,iCAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC;CAC3D,CAAC,CAAC;AAEH,MAAM,UAAU,2BAA2B,CAAC,KAAa;IACvD,OAAO,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,KAAa;IAC5D,IAAI,0BAA0B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,aAAa,KAAK,uEAAuE,CAAC;IACnG,CAAC;IACD,IAAI,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,qBAAqB,KAAK,iDAAiD,CAAC;IACrF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,MAAM,KAAK,GAAG,gCAAgC,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC","sourcesContent":["export const DISPATCH_WORKSPACE_ROOT_REDIRECTS = [\n [\"overview\", \"overview\"],\n [\"login\", \"login\"],\n [\"signup\", \"signup\"],\n [\"apps\", \"apps\"],\n [\"apps/new-app\", \"new-app\"],\n [\"new-app\", \"new-app\"],\n [\"vault\", \"vault\"],\n [\"integrations\", \"integrations\"],\n [\"agents\", \"agents\"],\n [\"workspace\", \"workspace\"],\n [\"messaging\", \"messaging\"],\n [\"extensions\", \"extensions\"],\n [\"destinations\", \"destinations\"],\n [\"identities\", \"identities\"],\n [\"approval\", \"approval\"],\n [\"approvals\", \"approvals\"],\n [\"audit\", \"audit\"],\n [\"team\", \"team\"],\n] as const;\n\nexport const RESERVED_WORKSPACE_APP_IDS = new Set([\n \"_agent-native\",\n \"_workspace_static\",\n \"api\",\n \"auth\",\n \"dispatch\",\n \"netlify\",\n // Legacy alias — `tools` was the previous name for `extensions`. Keep it\n // reserved so a user can't create a workspace app whose mount path collides\n // with the legacy `/tools` redirect (still served by core-routes-plugin).\n \"tools\",\n ...DISPATCH_WORKSPACE_ROOT_REDIRECTS.map(([from]) => from),\n]);\n\nexport function isValidWorkspaceAppIdFormat(appId: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(appId);\n}\n\nexport function getWorkspaceAppIdValidationError(appId: string): string | null {\n if (RESERVED_WORKSPACE_APP_IDS.has(appId)) {\n return `App name \"${appId}\" conflicts with a reserved workspace route. Choose a different name.`;\n }\n if (!isValidWorkspaceAppIdFormat(appId)) {\n return `Invalid app name \"${appId}\". Use lowercase letters, numbers, and hyphens.`;\n }\n return null;\n}\n\nexport function assertValidWorkspaceAppId(appId: string): void {\n const error = getWorkspaceAppIdValidationError(appId);\n if (error) throw new Error(error);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-app-id.js","sourceRoot":"","sources":["../../src/shared/workspace-app-id.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iCAAiC,GAAG;IAC/C,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,MAAM,EAAE,MAAM,CAAC;IAChB,CAAC,cAAc,EAAE,SAAS,CAAC;IAC3B,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,MAAM,EAAE,MAAM,CAAC;CACR,CAAC;AAEX,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IAChD,eAAe;IACf,mBAAmB;IACnB,KAAK;IACL,MAAM;IACN,UAAU;IACV,SAAS;IACT,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,OAAO;IACP,GAAG,iCAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC;CAC3D,CAAC,CAAC;AAEH,MAAM,UAAU,2BAA2B,CAAC,KAAa;IACvD,OAAO,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,KAAa;IAC5D,IAAI,0BAA0B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,aAAa,KAAK,uEAAuE,CAAC;IACnG,CAAC;IACD,IAAI,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,qBAAqB,KAAK,iDAAiD,CAAC;IACrF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,MAAM,KAAK,GAAG,gCAAgC,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC","sourcesContent":["export const DISPATCH_WORKSPACE_ROOT_REDIRECTS = [\n [\"overview\", \"overview\"],\n [\"login\", \"login\"],\n [\"signup\", \"signup\"],\n [\"apps\", \"apps\"],\n [\"apps/new-app\", \"new-app\"],\n [\"new-app\", \"new-app\"],\n [\"vault\", \"vault\"],\n [\"integrations\", \"integrations\"],\n [\"agents\", \"agents\"],\n [\"workspace\", \"workspace\"],\n [\"messaging\", \"messaging\"],\n [\"extensions\", \"extensions\"],\n [\"destinations\", \"destinations\"],\n [\"identities\", \"identities\"],\n [\"approval\", \"approval\"],\n [\"approvals\", \"approvals\"],\n [\"audit\", \"audit\"],\n [\"thread-debug\", \"thread-debug\"],\n [\"team\", \"team\"],\n] as const;\n\nexport const RESERVED_WORKSPACE_APP_IDS = new Set([\n \"_agent-native\",\n \"_workspace_static\",\n \"api\",\n \"auth\",\n \"dispatch\",\n \"netlify\",\n // Legacy alias — `tools` was the previous name for `extensions`. Keep it\n // reserved so a user can't create a workspace app whose mount path collides\n // with the legacy `/tools` redirect (still served by core-routes-plugin).\n \"tools\",\n ...DISPATCH_WORKSPACE_ROOT_REDIRECTS.map(([from]) => from),\n]);\n\nexport function isValidWorkspaceAppIdFormat(appId: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(appId);\n}\n\nexport function getWorkspaceAppIdValidationError(appId: string): string | null {\n if (RESERVED_WORKSPACE_APP_IDS.has(appId)) {\n return `App name \"${appId}\" conflicts with a reserved workspace route. Choose a different name.`;\n }\n if (!isValidWorkspaceAppIdFormat(appId)) {\n return `Invalid app name \"${appId}\". Use lowercase letters, numbers, and hyphens.`;\n }\n return null;\n}\n\nexport function assertValidWorkspaceAppId(appId: string): void {\n const error = getWorkspaceAppIdValidationError(appId);\n if (error) throw new Error(error);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAUzD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxD,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;gBACL,OAAO,SAAc;CAIlC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,GAAG,GAAE,aAA+B,EACpC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,CAoDL;AAgCD,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,EAAE,GAAG,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,aAA+B,GACnC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAkChC;AA4CD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,SAAS,GAAG,OAAkB,EACvC,GAAG,GAAE,aAA+B,GACnC,OAAO,CAAC,cAAc,CAAC,CAWzB"}
|
package/dist/sharing/access.js
CHANGED
|
@@ -14,8 +14,23 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { and, eq, or, sql } from "drizzle-orm";
|
|
16
16
|
import { getRequestUserEmail, getRequestOrgId, } from "../server/request-context.js";
|
|
17
|
-
import { requireShareableResource, } from "./registry.js";
|
|
17
|
+
import { listShareableResources, requireShareableResource, } from "./registry.js";
|
|
18
18
|
import { ROLE_RANK } from "./schema.js";
|
|
19
|
+
/**
|
|
20
|
+
* Find a registration by its drizzle table identity. Used to look up
|
|
21
|
+
* per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,
|
|
22
|
+
* which receives only the table — not the resource-type name.
|
|
23
|
+
*
|
|
24
|
+
* Identity is stable within a single bundle (Vite dedupes module instances);
|
|
25
|
+
* the SSR/server side is the only caller, so per-bundle identity is fine.
|
|
26
|
+
*/
|
|
27
|
+
function findRegistrationByTable(resourceTable) {
|
|
28
|
+
for (const reg of listShareableResources()) {
|
|
29
|
+
if (reg.resourceTable === resourceTable)
|
|
30
|
+
return reg;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
19
34
|
export class ForbiddenError extends Error {
|
|
20
35
|
statusCode = 403;
|
|
21
36
|
constructor(message = "Forbidden") {
|
|
@@ -50,10 +65,16 @@ export function currentAccess() {
|
|
|
50
65
|
*/
|
|
51
66
|
export function accessFilter(resourceTable, sharesTable, ctx = currentAccess(), minRole = "viewer", options = {}) {
|
|
52
67
|
const { userEmail, orgId } = ctx;
|
|
53
|
-
|
|
68
|
+
// Defense in depth — resources registered with `allowPublic: false` must
|
|
69
|
+
// never participate in cross-user "public" discovery, even if a caller
|
|
70
|
+
// accidentally passes `includePublic: true` or if a stale public row sits
|
|
71
|
+
// in the DB.
|
|
72
|
+
const reg = findRegistrationByTable(resourceTable);
|
|
73
|
+
const publicAllowed = reg?.allowPublic !== false;
|
|
74
|
+
const includePublic = (options.includePublic ?? false) && publicAllowed;
|
|
54
75
|
const clauses = [];
|
|
55
76
|
if (userEmail) {
|
|
56
|
-
clauses.push(eq(resourceTable.ownerEmail, userEmail));
|
|
77
|
+
clauses.push(and(eq(resourceTable.ownerEmail, userEmail), ownerScopeFilter(resourceTable, ctx)));
|
|
57
78
|
}
|
|
58
79
|
if (minRole === "viewer") {
|
|
59
80
|
if (includePublic) {
|
|
@@ -79,6 +100,21 @@ export function accessFilter(resourceTable, sharesTable, ctx = currentAccess(),
|
|
|
79
100
|
}
|
|
80
101
|
return or(...clauses) ?? sql `1=0`;
|
|
81
102
|
}
|
|
103
|
+
function ownerScopeFilter(resourceTable, ctx) {
|
|
104
|
+
if (ctx.orgId) {
|
|
105
|
+
// Rows created before org-scoping, or in solo mode, have no org_id. Keep
|
|
106
|
+
// them manageable by their owner after the owner joins or switches into an
|
|
107
|
+
// organization, while still keeping rows from other orgs out of scope.
|
|
108
|
+
return or(eq(resourceTable.orgId, ctx.orgId), sql `${resourceTable.orgId} IS NULL`);
|
|
109
|
+
}
|
|
110
|
+
return sql `${resourceTable.orgId} IS NULL`;
|
|
111
|
+
}
|
|
112
|
+
function ownerMatchesActiveScope(resource, ctx) {
|
|
113
|
+
const resourceOrgId = resource?.orgId ?? null;
|
|
114
|
+
if (!resourceOrgId)
|
|
115
|
+
return true;
|
|
116
|
+
return ctx.orgId === resourceOrgId;
|
|
117
|
+
}
|
|
82
118
|
function minRoleSql(minRole) {
|
|
83
119
|
if (minRole === "viewer") {
|
|
84
120
|
// any role satisfies viewer
|
|
@@ -103,14 +139,19 @@ export async function resolveAccess(resourceType, resourceId, ctx = currentAcces
|
|
|
103
139
|
if (!resource)
|
|
104
140
|
return null;
|
|
105
141
|
const { userEmail, orgId } = ctx;
|
|
106
|
-
if (userEmail &&
|
|
142
|
+
if (userEmail &&
|
|
143
|
+
resource.ownerEmail === userEmail &&
|
|
144
|
+
ownerMatchesActiveScope(resource, ctx)) {
|
|
107
145
|
return { role: "owner", resource };
|
|
108
146
|
}
|
|
109
|
-
if (resource.visibility === "public") {
|
|
147
|
+
if (resource.visibility === "public" && reg.allowPublic !== false) {
|
|
110
148
|
// No share row needed; default viewer unless upgraded below.
|
|
111
149
|
const role = await highestShareRole(reg, resourceId, ctx);
|
|
112
150
|
return { role: role ?? "viewer", resource };
|
|
113
151
|
}
|
|
152
|
+
// `visibility === "public"` on an `allowPublic: false` resource is treated
|
|
153
|
+
// as private: only owner + explicit shares grant access. Falls through to
|
|
154
|
+
// the explicit-share lookup below.
|
|
114
155
|
if (resource.visibility === "org" && orgId && resource.orgId === orgId) {
|
|
115
156
|
const role = await highestShareRole(reg, resourceId, ctx);
|
|
116
157
|
return { role: role ?? "viewer", resource };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n ctx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n const { userEmail, orgId } = ctx;\n const { includePublic = false } = options;\n const clauses: SQL[] = [];\n\n if (userEmail) {\n clauses.push(eq(resourceTable.ownerEmail, userEmail));\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (userEmail) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and ${sharesTable.principalId} = ${userEmail}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const db = reg.getDb() as any;\n\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n\n if (userEmail && resource.ownerEmail === userEmail) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\") {\n // No share row needed; default viewer unless upgraded below.\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n if (!userEmail && !orgId) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (userEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n eq(reg.sharesTable.principalId, userEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n ctx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n const { userEmail, orgId } = ctx;\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (userEmail) {\n clauses.push(\n and(\n eq(resourceTable.ownerEmail, userEmail),\n ownerScopeFilter(resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (userEmail) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and ${sharesTable.principalId} = ${userEmail}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(resourceTable: any, ctx: AccessContext): SQL {\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(resource: any, ctx: AccessContext): boolean {\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const db = reg.getDb() as any;\n\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n\n if (\n userEmail &&\n resource.ownerEmail === userEmail &&\n ownerMatchesActiveScope(resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless upgraded below.\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n if (!userEmail && !orgId) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (userEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n eq(reg.sharesTable.principalId, userEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-resource-shares.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/list-resource-shares.ts"],"names":[],"mappings":";AAMA,
|
|
1
|
+
{"version":3,"file":"list-resource-shares.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/list-resource-shares.ts"],"names":[],"mappings":";AAMA,wBAyCG"}
|
|
@@ -12,9 +12,15 @@ export default defineAction({
|
|
|
12
12
|
http: { method: "GET" },
|
|
13
13
|
run: async (args) => {
|
|
14
14
|
const reg = requireShareableResource(args.resourceType);
|
|
15
|
+
const policy = {
|
|
16
|
+
// Defaults match registration defaults so the UI behaves the same for
|
|
17
|
+
// resources that haven't opted into restrictions.
|
|
18
|
+
allowPublic: reg.allowPublic !== false,
|
|
19
|
+
requireOrgMemberForUserShares: reg.requireOrgMemberForUserShares === true,
|
|
20
|
+
};
|
|
15
21
|
const access = await resolveAccess(args.resourceType, args.resourceId);
|
|
16
22
|
if (!access)
|
|
17
|
-
return { ownerEmail: null, visibility: null, shares: [] };
|
|
23
|
+
return { ownerEmail: null, visibility: null, shares: [], policy };
|
|
18
24
|
const db = reg.getDb();
|
|
19
25
|
const shares = await db
|
|
20
26
|
.select()
|
|
@@ -32,6 +38,7 @@ export default defineAction({
|
|
|
32
38
|
role: s.role,
|
|
33
39
|
createdAt: s.createdAt,
|
|
34
40
|
})),
|
|
41
|
+
policy,
|
|
35
42
|
};
|
|
36
43
|
},
|
|
37
44
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-resource-shares.js","sourceRoot":"","sources":["../../../src/sharing/actions/list-resource-shares.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sGAAsG;IACxG,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;KACvB,CAAC;IACF,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"list-resource-shares.js","sourceRoot":"","sources":["../../../src/sharing/actions/list-resource-shares.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sGAAsG;IACxG,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;KACvB,CAAC;IACF,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG;YACb,sEAAsE;YACtE,kDAAkD;YAClD,WAAW,EAAE,GAAG,CAAC,WAAW,KAAK,KAAK;YACtC,6BAA6B,EAAE,GAAG,CAAC,6BAA6B,KAAK,IAAI;SAC1E,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM;YACT,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;QAEpE,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAS,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,EAAE;aACpB,MAAM,EAAE;aACR,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;aACrB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAE1D,OAAO;YACL,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI;YAC9C,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI;YACpC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,SAAS;YACnD,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBAC9B,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,SAAS;aACvB,CAAC,CAAC;YACH,MAAM;SACP,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport { z } from \"zod\";\nimport { defineAction } from \"../../action.js\";\nimport { resolveAccess } from \"../access.js\";\nimport { requireShareableResource } from \"../registry.js\";\n\nexport default defineAction({\n description:\n \"List the current visibility and share grants on a shareable resource. Any read access is sufficient.\",\n schema: z.object({\n resourceType: z.string(),\n resourceId: z.string(),\n }),\n http: { method: \"GET\" },\n run: async (args) => {\n const reg = requireShareableResource(args.resourceType);\n const policy = {\n // Defaults match registration defaults so the UI behaves the same for\n // resources that haven't opted into restrictions.\n allowPublic: reg.allowPublic !== false,\n requireOrgMemberForUserShares: reg.requireOrgMemberForUserShares === true,\n };\n const access = await resolveAccess(args.resourceType, args.resourceId);\n if (!access)\n return { ownerEmail: null, visibility: null, shares: [], policy };\n\n const db = reg.getDb() as any;\n const shares = await db\n .select()\n .from(reg.sharesTable)\n .where(eq(reg.sharesTable.resourceId, args.resourceId));\n\n return {\n ownerEmail: access.resource.ownerEmail ?? null,\n orgId: access.resource.orgId ?? null,\n visibility: access.resource.visibility ?? \"private\",\n role: access.role,\n shares: shares.map((s: any) => ({\n id: s.id,\n principalType: s.principalType,\n principalId: s.principalId,\n role: s.role,\n createdAt: s.createdAt,\n })),\n policy,\n };\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-resource-visibility.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"set-resource-visibility.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":";AAOA,wBAmCG"}
|