@agent-native/core 0.117.0 → 0.117.2
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/cli/context-xray-local.ts +1 -1
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +4 -4
- package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +11 -11
- package/corpus/core/src/client/extensions/agent-native-extension-runtime.ts +1 -1
- package/corpus/core/src/client/extensions/delete-extension.ts +2 -2
- package/corpus/core/src/client/org/OrgSwitcher.tsx +25 -1
- package/corpus/core/src/client/settings/SettingsPanel.tsx +102 -10
- package/corpus/core/src/client/sharing/ShareButton.tsx +7 -6
- package/corpus/core/src/db/client.ts +13 -10
- package/corpus/core/src/deploy/workspace-deploy.ts +14 -4
- package/corpus/core/src/extensions/actions.ts +13 -5
- package/corpus/core/src/extensions/local.ts +1 -0
- package/corpus/core/src/extensions/routes.ts +10 -5
- package/corpus/core/src/extensions/schema.ts +5 -0
- package/corpus/core/src/extensions/slots/store.ts +14 -4
- package/corpus/core/src/extensions/store.ts +55 -19
- package/corpus/core/src/extensions/theme.ts +1 -1
- package/corpus/core/src/file-upload/registry.ts +9 -0
- package/corpus/core/src/index.browser.ts +5 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/localization/default-messages.ts +23 -6
- package/corpus/core/src/server/action-discovery.ts +8 -0
- package/corpus/core/src/server/better-auth-instance.ts +7 -2
- package/corpus/core/src/server/email-template.ts +18 -0
- package/corpus/core/src/server/email-templates.ts +3 -0
- package/corpus/core/src/server/email.ts +40 -4
- package/corpus/core/src/sharing/actions/share-resource.ts +1 -0
- package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/core/src/templates/default/app/global.css +1 -1
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +7 -7
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +7 -7
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/core/src/user-profile/actions/get-user-profile.ts +16 -0
- package/corpus/core/src/user-profile/actions/update-user-profile.ts +24 -0
- package/corpus/core/src/user-profile/index.ts +5 -0
- package/corpus/core/src/user-profile/shared.ts +14 -0
- package/corpus/core/src/user-profile/store.ts +50 -0
- package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/assets/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/brain/app/global.css +2 -2
- package/corpus/templates/brain/app/lib/brain.ts +2 -2
- package/corpus/templates/brain/app/root.tsx +2 -2
- package/corpus/templates/brain/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +6 -2
- package/corpus/templates/calendar/app/global.css +1 -1
- package/corpus/templates/calendar/app/root.tsx +2 -2
- package/corpus/templates/calendar/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/chat/app/global.css +1 -1
- package/corpus/templates/chat/app/root.tsx +2 -2
- package/corpus/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/clips/actions/export-to-brain.ts +1 -1
- package/corpus/templates/clips/actions/invite-member.ts +1 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +2 -2
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +20 -11
- package/corpus/templates/clips/app/global.css +1 -1
- package/corpus/templates/clips/app/root.tsx +7 -2
- package/corpus/templates/clips/app/routes/record.tsx +2 -88
- package/corpus/templates/clips/changelog/2026-07-22-builder-connected-recordings-start-resumable-upload-sessions.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-clip-thumbnails-now-come-from-the-recording-instead-of-the-p.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-clips-emails-now-render-the-agent-native-logo-in-gmail-and-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-dismissed-zoom-and-teams-meeting-note-prompts-now-stay-dismi.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-22-quieter-permissions-and-rewind-controls.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +86 -52
- package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +31 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +4 -167
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +10 -1
- package/corpus/templates/clips/desktop/src/styles.css +98 -36
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +67 -4
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +51 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +5 -1
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +0 -189
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +62 -0
- package/corpus/templates/clips/server/plugins/db.ts +6 -2
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -3
- package/corpus/templates/content/app/global.css +1 -1
- package/corpus/templates/content/app/root.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/crm/app/global.css +1 -1
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +19 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +12 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +177 -52
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +179 -59
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +74 -27
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +2 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/design/app/global.css +1 -1
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +6 -5
- package/corpus/templates/design/app/root.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-22-design-owners-can-keep-editing-their-designs-after-switching.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-inspector-tabs-keep-their-text-labels-at-narrow-widths.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-narrow-breakpoint-previews-keep-their-device-labels-readable.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-removed-the-draft-status-chip-from-the-design-editor.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-responsive-breakpoint-controls-stay-within-narrow-sidebars.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-responsive-peers-now-use-a-softer-selection-highlight-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-rotate-from-context-menu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-the-selection-outline-now-stays-attached-while-moving-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-22-view-only-designs-now-support-figma-style-layer-inspection-w.md +6 -0
- package/corpus/templates/design/server/db/index.ts +4 -0
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +2 -2
- package/corpus/templates/dispatch/app/global.css +1 -1
- package/corpus/templates/dispatch/app/root.tsx +2 -2
- package/corpus/templates/dispatch/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +3 -3
- package/corpus/templates/forms/app/global.css +1 -1
- package/corpus/templates/forms/app/hooks/use-forms.ts +55 -2
- package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +3 -3
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +23 -6
- package/corpus/templates/forms/app/root.tsx +2 -2
- package/corpus/templates/forms/changelog/2026-07-22-forms-now-archive-with-optimistic-feedback.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/macros/app/root.tsx +2 -2
- package/corpus/templates/macros/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +3 -3
- package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +2 -2
- package/corpus/templates/mail/app/global.css +1 -1
- package/corpus/templates/mail/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/plan/app/global.css +1 -1
- package/corpus/templates/plan/app/root.tsx +2 -2
- package/corpus/templates/plan/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/slides/app/root.tsx +2 -2
- package/corpus/templates/slides/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-22-slides-no-longer-fail-intermittently-on-fresh-server-instances.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-22-uploaded-screenshots-can-now-be-analyzed-and-recreated-as-ed.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +7 -3
- package/corpus/templates/slides/server/plugins/db.ts +44 -17
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/templates/tasks/app/global.css +1 -1
- package/dist/cli/context-xray-local.js +1 -1
- package/dist/cli/context-xray-local.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +2 -2
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +11 -11
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/extensions/agent-native-extension-runtime.js +1 -1
- package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -1
- package/dist/client/extensions/delete-extension.d.ts +1 -1
- package/dist/client/extensions/delete-extension.d.ts.map +1 -1
- package/dist/client/extensions/delete-extension.js +1 -1
- package/dist/client/extensions/delete-extension.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +12 -4
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +37 -4
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +3 -4
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -2
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +7 -7
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +13 -10
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +14 -4
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +8 -5
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/local.d.ts.map +1 -1
- package/dist/extensions/local.js +1 -0
- package/dist/extensions/local.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +9 -4
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +23 -2
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +5 -0
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/slots/store.d.ts +1 -1
- package/dist/extensions/slots/store.d.ts.map +1 -1
- package/dist/extensions/slots/store.js +4 -4
- package/dist/extensions/slots/store.js.map +1 -1
- package/dist/extensions/store.d.ts +1 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +38 -21
- package/dist/extensions/store.js.map +1 -1
- package/dist/extensions/theme.js +1 -1
- package/dist/extensions/theme.js.map +1 -1
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +9 -0
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -0
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/localization/default-messages.d.ts +16 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +22 -6
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +8 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/better-auth-instance.d.ts +7 -2
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/email-template.d.ts +3 -0
- package/dist/server/email-template.d.ts.map +1 -1
- package/dist/server/email-template.js +13 -0
- package/dist/server/email-template.js.map +1 -1
- package/dist/server/email-templates.d.ts.map +1 -1
- package/dist/server/email-templates.js +3 -0
- package/dist/server/email-templates.js.map +1 -1
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +27 -4
- package/dist/server/email.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/actions/share-resource.js +1 -0
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +2 -2
- package/dist/templates/chat/app/global.css +1 -1
- package/dist/templates/chat/app/root.tsx +2 -2
- package/dist/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/templates/default/app/global.css +1 -1
- package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
- package/dist/templates/default/app/i18n/de-DE.ts +6 -6
- package/dist/templates/default/app/i18n/en-US.ts +6 -6
- package/dist/templates/default/app/i18n/es-ES.ts +6 -6
- package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
- package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
- package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
- package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
- package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
- package/dist/templates/default/app/i18n/zh-CN.ts +7 -7
- package/dist/templates/default/app/i18n/zh-TW.ts +7 -7
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/user-profile/actions/get-user-profile.d.ts +4 -0
- package/dist/user-profile/actions/get-user-profile.d.ts.map +1 -0
- package/dist/user-profile/actions/get-user-profile.js +14 -0
- package/dist/user-profile/actions/get-user-profile.js.map +1 -0
- package/dist/user-profile/actions/update-user-profile.d.ts +6 -0
- package/dist/user-profile/actions/update-user-profile.d.ts.map +1 -0
- package/dist/user-profile/actions/update-user-profile.js +20 -0
- package/dist/user-profile/actions/update-user-profile.js.map +1 -0
- package/dist/user-profile/index.d.ts +2 -0
- package/dist/user-profile/index.d.ts.map +1 -0
- package/dist/user-profile/index.js +2 -0
- package/dist/user-profile/index.js.map +1 -0
- package/dist/user-profile/shared.d.ts +7 -0
- package/dist/user-profile/shared.d.ts.map +1 -0
- package/dist/user-profile/shared.js +6 -0
- package/dist/user-profile/shared.js.map +1 -0
- package/dist/user-profile/store.d.ts +4 -0
- package/dist/user-profile/store.d.ts.map +1 -0
- package/dist/user-profile/store.js +38 -0
- package/dist/user-profile/store.js.map +1 -0
- package/package.json +2 -1
- package/src/cli/context-xray-local.ts +1 -1
- package/src/client/extensions/ExtensionViewer.tsx +4 -4
- package/src/client/extensions/ExtensionsSidebarSection.tsx +11 -11
- package/src/client/extensions/agent-native-extension-runtime.ts +1 -1
- package/src/client/extensions/delete-extension.ts +2 -2
- package/src/client/org/OrgSwitcher.tsx +25 -1
- package/src/client/settings/SettingsPanel.tsx +102 -10
- package/src/client/sharing/ShareButton.tsx +7 -6
- package/src/db/client.ts +13 -10
- package/src/deploy/workspace-deploy.ts +14 -4
- package/src/extensions/actions.ts +13 -5
- package/src/extensions/local.ts +1 -0
- package/src/extensions/routes.ts +10 -5
- package/src/extensions/schema.ts +5 -0
- package/src/extensions/slots/store.ts +14 -4
- package/src/extensions/store.ts +55 -19
- package/src/extensions/theme.ts +1 -1
- package/src/file-upload/registry.ts +9 -0
- package/src/index.browser.ts +5 -0
- package/src/index.ts +1 -0
- package/src/localization/default-messages.ts +23 -6
- package/src/server/action-discovery.ts +8 -0
- package/src/server/better-auth-instance.ts +7 -2
- package/src/server/email-template.ts +18 -0
- package/src/server/email-templates.ts +3 -0
- package/src/server/email.ts +40 -4
- package/src/sharing/actions/share-resource.ts +1 -0
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/templates/chat/app/components/layout/Sidebar.tsx +2 -2
- package/src/templates/chat/app/global.css +1 -1
- package/src/templates/chat/app/root.tsx +2 -2
- package/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/templates/default/app/global.css +1 -1
- package/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/src/templates/default/app/i18n/en-US.ts +6 -6
- package/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/src/templates/default/app/i18n/zh-CN.ts +7 -7
- package/src/templates/default/app/i18n/zh-TW.ts +7 -7
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/user-profile/actions/get-user-profile.ts +16 -0
- package/src/user-profile/actions/update-user-profile.ts +24 -0
- package/src/user-profile/index.ts +5 -0
- package/src/user-profile/shared.ts +14 -0
- package/src/user-profile/store.ts +50 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAiED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAE/D;AAkBD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgHlE"}
|
package/dist/server/email.js
CHANGED
|
@@ -9,7 +9,29 @@
|
|
|
9
9
|
* With neither provider configured, `sendEmail` logs the message to the console
|
|
10
10
|
* so the reset-password flow still works end-to-end for local development.
|
|
11
11
|
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
12
13
|
import { resolveSecret } from "./credential-provider.js";
|
|
14
|
+
import { AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID } from "./email-template.js";
|
|
15
|
+
let cachedAgentNativeLogo;
|
|
16
|
+
function getAgentNativeLogoAttachment() {
|
|
17
|
+
cachedAgentNativeLogo ??= readFileSync(new URL("../../src/assets/branding/favicon.png", import.meta.url));
|
|
18
|
+
return {
|
|
19
|
+
filename: "agent-native-logo.png",
|
|
20
|
+
content: cachedAgentNativeLogo,
|
|
21
|
+
contentType: "image/png",
|
|
22
|
+
contentId: AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,
|
|
23
|
+
disposition: "inline",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function resolveAttachments(args) {
|
|
27
|
+
if (!args.html.includes(`cid:${AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID}`)) {
|
|
28
|
+
return args.attachments;
|
|
29
|
+
}
|
|
30
|
+
if (args.attachments?.some((attachment) => attachment.contentId === AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID)) {
|
|
31
|
+
return args.attachments;
|
|
32
|
+
}
|
|
33
|
+
return [...(args.attachments ?? []), getAgentNativeLogoAttachment()];
|
|
34
|
+
}
|
|
13
35
|
async function resolveEmailTransport() {
|
|
14
36
|
const [resendApiKey, sendgridApiKey, from] = await Promise.all([
|
|
15
37
|
resolveSecret("RESEND_API_KEY"),
|
|
@@ -54,6 +76,7 @@ export async function sendEmail(args) {
|
|
|
54
76
|
const config = await resolveEmailTransport();
|
|
55
77
|
const provider = config.provider;
|
|
56
78
|
const from = getFromAddress(config, args.from);
|
|
79
|
+
const attachments = resolveAttachments(args);
|
|
57
80
|
if (provider === "resend") {
|
|
58
81
|
const payload = {
|
|
59
82
|
from,
|
|
@@ -66,8 +89,8 @@ export async function sendEmail(args) {
|
|
|
66
89
|
payload.cc = Array.isArray(args.cc) ? args.cc : [args.cc];
|
|
67
90
|
if (args.replyTo)
|
|
68
91
|
payload.reply_to = args.replyTo;
|
|
69
|
-
if (
|
|
70
|
-
payload.attachments =
|
|
92
|
+
if (attachments?.length) {
|
|
93
|
+
payload.attachments = attachments.map((a) => ({
|
|
71
94
|
filename: a.filename,
|
|
72
95
|
content: typeof a.content === "string"
|
|
73
96
|
? a.content
|
|
@@ -123,8 +146,8 @@ export async function sendEmail(args) {
|
|
|
123
146
|
sgHeaders["References"] = args.references;
|
|
124
147
|
if (Object.keys(sgHeaders).length)
|
|
125
148
|
sgPayload.headers = sgHeaders;
|
|
126
|
-
if (
|
|
127
|
-
sgPayload.attachments =
|
|
149
|
+
if (attachments?.length) {
|
|
150
|
+
sgPayload.attachments = attachments.map((a) => ({
|
|
128
151
|
filename: a.filename,
|
|
129
152
|
content: typeof a.content === "string"
|
|
130
153
|
? Buffer.from(a.content).toString("base64")
|
package/dist/server/email.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAgCzD,KAAK,UAAU,qBAAqB;IAClC,MAAM,CAAC,YAAY,EAAE,cAAc,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7D,aAAa,CAAC,gBAAgB,CAAC;QAC/B,aAAa,CAAC,kBAAkB,CAAC;QACjC,aAAa,CAAC,YAAY,CAAC;KAC5B,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,IAAI,IAAI,SAAS,CAAC;IACvC,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,YAAY;YACZ,IAAI,EAAE,YAAY;SACnB,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;YACL,QAAQ,EAAE,UAAU;YACpB,cAAc;YACd,IAAI,EAAE,YAAY;SACnB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,OAAO,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,OAAO,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC,QAAQ,CAAC;AAClD,CAAC;AAED,SAAS,cAAc,CACrB,MAA4B,EAC5B,QAAiB;IAEjB,MAAM,QAAQ,GAAG,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC;IACzC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,yEAAyE;IACzE,sEAAsE;IACtE,IAAI,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,sCAAsC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAmB;IACjD,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/C,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,OAAO,GAA4B;YACvC,IAAI;YACJ,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC;QACF,IAAI,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;QAClD,IAAI,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;YAC7B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjD,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,OAAO,EACL,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;oBAC3B,CAAC,CAAC,CAAC,CAAC,OAAO;oBACX,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAClC,YAAY,EAAE,CAAC,CAAC,WAAW;gBAC3B,UAAU,EAAE,CAAC,CAAC,SAAS;aACxB,CAAC,CAAC,CAAC;QACN,CAAC;QACD,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAC5D,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAE3D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,+BAA+B,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,CAAC,YAAY,EAAE;gBAC9C,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,MAAM,eAAe,GAA4B;YAC/C,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;SACzB,CAAC;QACF,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5D,eAAe,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,SAAS,GAA4B;YACzC,gBAAgB,EAAE,CAAC,eAAe,CAAC;YACnC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE;gBACP,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;aACxC;SACF,CAAC;QACF,IAAI,IAAI,CAAC,OAAO;YAAE,SAAS,CAAC,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvE,MAAM,SAAS,GAA2B,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,SAAS;YAAE,SAAS,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9D,IAAI,IAAI,CAAC,UAAU;YAAE,SAAS,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/D,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM;YAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC;QACjE,IAAI,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;YAC7B,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACnD,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,OAAO,EACL,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;oBAC3B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;oBAC3C,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAClC,IAAI,EAAE,CAAC,CAAC,WAAW;gBACnB,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;gBAClE,UAAU,EAAE,CAAC,CAAC,SAAS;aACxB,CAAC,CAAC,CAAC;QACN,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;YAC/D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,CAAC,cAAc,EAAE;gBAChD,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO;IACT,CAAC;IAED,uEAAuE;IACvE,wEAAwE;IACxE,wDAAwD;IACxD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,CACT,uDAAuD;QACrD,4EAA4E;QAC5E,YAAY,IAAI,CAAC,EAAE,WAAW,IAAI,cAAc,IAAI,CAAC,OAAO,MAAM;QAClE,GAAG,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAChD,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI;SACR,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC;SAC7B,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE,CAAC;AACZ,CAAC","sourcesContent":["/**\n * Email transport for system emails (password resets, invitations, notifications).\n *\n * Providers are selected by scoped secrets:\n * RESEND_API_KEY — https://resend.com\n * SENDGRID_API_KEY — https://sendgrid.com\n * EMAIL_FROM — \"Name <addr@domain>\" (optional; defaults to Resend's sandbox)\n *\n * With neither provider configured, `sendEmail` logs the message to the console\n * so the reset-password flow still works end-to-end for local development.\n */\n\nimport { resolveSecret } from \"./credential-provider.js\";\n\nexport type EmailProvider = \"resend\" | \"sendgrid\" | \"dev\";\n\nexport interface EmailAttachment {\n filename: string;\n content: string | Buffer;\n contentType?: string;\n contentId?: string;\n disposition?: \"attachment\" | \"inline\";\n}\n\nexport interface SendEmailArgs {\n to: string;\n subject: string;\n html: string;\n text?: string;\n from?: string;\n cc?: string | string[];\n replyTo?: string;\n inReplyTo?: string;\n references?: string;\n attachments?: EmailAttachment[];\n}\n\ninterface EmailTransportConfig {\n provider: EmailProvider;\n resendApiKey?: string;\n sendgridApiKey?: string;\n from?: string;\n}\n\nasync function resolveEmailTransport(): Promise<EmailTransportConfig> {\n const [resendApiKey, sendgridApiKey, from] = await Promise.all([\n resolveSecret(\"RESEND_API_KEY\"),\n resolveSecret(\"SENDGRID_API_KEY\"),\n resolveSecret(\"EMAIL_FROM\"),\n ]);\n const resolvedFrom = from ?? undefined;\n if (resendApiKey) {\n return {\n provider: \"resend\",\n resendApiKey,\n from: resolvedFrom,\n };\n }\n if (sendgridApiKey) {\n return {\n provider: \"sendgrid\",\n sendgridApiKey,\n from: resolvedFrom,\n };\n }\n return { provider: \"dev\", from: resolvedFrom };\n}\n\nexport async function isEmailConfigured(): Promise<boolean> {\n return (await resolveEmailTransport()).provider !== \"dev\";\n}\n\nexport async function getEmailProvider(): Promise<EmailProvider> {\n return (await resolveEmailTransport()).provider;\n}\n\nfunction getFromAddress(\n config: EmailTransportConfig,\n override?: string,\n): string {\n const explicit = override || config.from;\n if (explicit) return explicit;\n // Resend lets unverified accounts send from its sandbox domain; SendGrid\n // does not, so falling back there would cause silent 403s at runtime.\n if (config.provider === \"sendgrid\") {\n throw new Error(\n \"EMAIL_FROM is required when using SendGrid — save it as a verified sender address.\",\n );\n }\n return \"Agent Native <onboarding@resend.dev>\";\n}\n\nexport async function sendEmail(args: SendEmailArgs): Promise<void> {\n const config = await resolveEmailTransport();\n const provider = config.provider;\n const from = getFromAddress(config, args.from);\n\n if (provider === \"resend\") {\n const payload: Record<string, unknown> = {\n from,\n to: args.to,\n subject: args.subject,\n html: args.html,\n text: args.text,\n };\n if (args.cc) payload.cc = Array.isArray(args.cc) ? args.cc : [args.cc];\n if (args.replyTo) payload.reply_to = args.replyTo;\n if (args.attachments?.length) {\n payload.attachments = args.attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? a.content\n : a.content.toString(\"base64\"),\n content_type: a.contentType,\n content_id: a.contentId,\n }));\n }\n const headers: Record<string, string> = {};\n if (args.inReplyTo) headers[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) headers[\"References\"] = args.references;\n if (Object.keys(headers).length) payload.headers = headers;\n\n const res = await fetch(\"https://api.resend.com/emails\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.resendApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`Resend error ${res.status}: ${body}`);\n }\n return;\n }\n\n if (provider === \"sendgrid\") {\n const personalization: Record<string, unknown> = {\n to: [{ email: args.to }],\n };\n if (args.cc) {\n const ccList = Array.isArray(args.cc) ? args.cc : [args.cc];\n personalization.cc = ccList.map((email) => ({ email }));\n }\n\n const sgPayload: Record<string, unknown> = {\n personalizations: [personalization],\n from: parseSendGridFrom(from),\n subject: args.subject,\n content: [\n ...(args.text ? [{ type: \"text/plain\", value: args.text }] : []),\n { type: \"text/html\", value: args.html },\n ],\n };\n if (args.replyTo) sgPayload.reply_to = parseSendGridFrom(args.replyTo);\n const sgHeaders: Record<string, string> = {};\n if (args.inReplyTo) sgHeaders[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) sgHeaders[\"References\"] = args.references;\n if (Object.keys(sgHeaders).length) sgPayload.headers = sgHeaders;\n if (args.attachments?.length) {\n sgPayload.attachments = args.attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? Buffer.from(a.content).toString(\"base64\")\n : a.content.toString(\"base64\"),\n type: a.contentType,\n disposition: a.disposition ?? (a.contentId ? \"inline\" : undefined),\n content_id: a.contentId,\n }));\n }\n\n const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.sendgridApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sgPayload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`SendGrid error ${res.status}: ${body}`);\n }\n return;\n }\n\n // Dev fallback — no provider configured. Logging the full body exposes\n // reset tokens, so only do it outside production. In production, refuse\n // to send rather than silently leaking secrets to logs.\n if (process.env.NODE_ENV === \"production\") {\n throw new Error(\n \"No email provider configured. Save RESEND_API_KEY or SENDGRID_API_KEY in settings.\",\n );\n }\n console.log(\n `\\n[agent-native:email] No email provider configured. ` +\n `Save RESEND_API_KEY or SENDGRID_API_KEY in settings to send real emails.\\n` +\n `---\\nTo: ${args.to}\\nFrom: ${from}\\nSubject: ${args.subject}\\n\\n` +\n `${args.text || stripHtml(args.html)}\\n---\\n`,\n );\n}\n\nfunction parseSendGridFrom(from: string): { email: string; name?: string } {\n const m = from.match(/^\\s*(.*?)\\s*<(.+)>\\s*$/);\n if (m && m[2]) return { name: m[1] || undefined, email: m[2] };\n return { email: from.trim() };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/ /g, \" \")\n .trim();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Email transport for system emails (password resets, invitations, notifications).\n *\n * Providers are selected by scoped secrets:\n * RESEND_API_KEY — https://resend.com\n * SENDGRID_API_KEY — https://sendgrid.com\n * EMAIL_FROM — \"Name <addr@domain>\" (optional; defaults to Resend's sandbox)\n *\n * With neither provider configured, `sendEmail` logs the message to the console\n * so the reset-password flow still works end-to-end for local development.\n */\n\nimport { readFileSync } from \"node:fs\";\n\nimport { resolveSecret } from \"./credential-provider.js\";\nimport { AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID } from \"./email-template.js\";\n\nexport type EmailProvider = \"resend\" | \"sendgrid\" | \"dev\";\n\nexport interface EmailAttachment {\n filename: string;\n content: string | Buffer;\n contentType?: string;\n contentId?: string;\n disposition?: \"attachment\" | \"inline\";\n}\n\nexport interface SendEmailArgs {\n to: string;\n subject: string;\n html: string;\n text?: string;\n from?: string;\n cc?: string | string[];\n replyTo?: string;\n inReplyTo?: string;\n references?: string;\n attachments?: EmailAttachment[];\n}\n\nlet cachedAgentNativeLogo: Buffer | undefined;\n\nfunction getAgentNativeLogoAttachment(): EmailAttachment {\n cachedAgentNativeLogo ??= readFileSync(\n new URL(\"../../src/assets/branding/favicon.png\", import.meta.url),\n );\n return {\n filename: \"agent-native-logo.png\",\n content: cachedAgentNativeLogo,\n contentType: \"image/png\",\n contentId: AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n disposition: \"inline\",\n };\n}\n\nfunction resolveAttachments(\n args: SendEmailArgs,\n): EmailAttachment[] | undefined {\n if (!args.html.includes(`cid:${AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID}`)) {\n return args.attachments;\n }\n if (\n args.attachments?.some(\n (attachment) =>\n attachment.contentId === AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n )\n ) {\n return args.attachments;\n }\n return [...(args.attachments ?? []), getAgentNativeLogoAttachment()];\n}\n\ninterface EmailTransportConfig {\n provider: EmailProvider;\n resendApiKey?: string;\n sendgridApiKey?: string;\n from?: string;\n}\n\nasync function resolveEmailTransport(): Promise<EmailTransportConfig> {\n const [resendApiKey, sendgridApiKey, from] = await Promise.all([\n resolveSecret(\"RESEND_API_KEY\"),\n resolveSecret(\"SENDGRID_API_KEY\"),\n resolveSecret(\"EMAIL_FROM\"),\n ]);\n const resolvedFrom = from ?? undefined;\n if (resendApiKey) {\n return {\n provider: \"resend\",\n resendApiKey,\n from: resolvedFrom,\n };\n }\n if (sendgridApiKey) {\n return {\n provider: \"sendgrid\",\n sendgridApiKey,\n from: resolvedFrom,\n };\n }\n return { provider: \"dev\", from: resolvedFrom };\n}\n\nexport async function isEmailConfigured(): Promise<boolean> {\n return (await resolveEmailTransport()).provider !== \"dev\";\n}\n\nexport async function getEmailProvider(): Promise<EmailProvider> {\n return (await resolveEmailTransport()).provider;\n}\n\nfunction getFromAddress(\n config: EmailTransportConfig,\n override?: string,\n): string {\n const explicit = override || config.from;\n if (explicit) return explicit;\n // Resend lets unverified accounts send from its sandbox domain; SendGrid\n // does not, so falling back there would cause silent 403s at runtime.\n if (config.provider === \"sendgrid\") {\n throw new Error(\n \"EMAIL_FROM is required when using SendGrid — save it as a verified sender address.\",\n );\n }\n return \"Agent Native <onboarding@resend.dev>\";\n}\n\nexport async function sendEmail(args: SendEmailArgs): Promise<void> {\n const config = await resolveEmailTransport();\n const provider = config.provider;\n const from = getFromAddress(config, args.from);\n const attachments = resolveAttachments(args);\n\n if (provider === \"resend\") {\n const payload: Record<string, unknown> = {\n from,\n to: args.to,\n subject: args.subject,\n html: args.html,\n text: args.text,\n };\n if (args.cc) payload.cc = Array.isArray(args.cc) ? args.cc : [args.cc];\n if (args.replyTo) payload.reply_to = args.replyTo;\n if (attachments?.length) {\n payload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? a.content\n : a.content.toString(\"base64\"),\n content_type: a.contentType,\n content_id: a.contentId,\n }));\n }\n const headers: Record<string, string> = {};\n if (args.inReplyTo) headers[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) headers[\"References\"] = args.references;\n if (Object.keys(headers).length) payload.headers = headers;\n\n const res = await fetch(\"https://api.resend.com/emails\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.resendApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`Resend error ${res.status}: ${body}`);\n }\n return;\n }\n\n if (provider === \"sendgrid\") {\n const personalization: Record<string, unknown> = {\n to: [{ email: args.to }],\n };\n if (args.cc) {\n const ccList = Array.isArray(args.cc) ? args.cc : [args.cc];\n personalization.cc = ccList.map((email) => ({ email }));\n }\n\n const sgPayload: Record<string, unknown> = {\n personalizations: [personalization],\n from: parseSendGridFrom(from),\n subject: args.subject,\n content: [\n ...(args.text ? [{ type: \"text/plain\", value: args.text }] : []),\n { type: \"text/html\", value: args.html },\n ],\n };\n if (args.replyTo) sgPayload.reply_to = parseSendGridFrom(args.replyTo);\n const sgHeaders: Record<string, string> = {};\n if (args.inReplyTo) sgHeaders[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) sgHeaders[\"References\"] = args.references;\n if (Object.keys(sgHeaders).length) sgPayload.headers = sgHeaders;\n if (attachments?.length) {\n sgPayload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? Buffer.from(a.content).toString(\"base64\")\n : a.content.toString(\"base64\"),\n type: a.contentType,\n disposition: a.disposition ?? (a.contentId ? \"inline\" : undefined),\n content_id: a.contentId,\n }));\n }\n\n const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.sendgridApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sgPayload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`SendGrid error ${res.status}: ${body}`);\n }\n return;\n }\n\n // Dev fallback — no provider configured. Logging the full body exposes\n // reset tokens, so only do it outside production. In production, refuse\n // to send rather than silently leaking secrets to logs.\n if (process.env.NODE_ENV === \"production\") {\n throw new Error(\n \"No email provider configured. Save RESEND_API_KEY or SENDGRID_API_KEY in settings.\",\n );\n }\n console.log(\n `\\n[agent-native:email] No email provider configured. ` +\n `Save RESEND_API_KEY or SENDGRID_API_KEY in settings to send real emails.\\n` +\n `---\\nTo: ${args.to}\\nFrom: ${from}\\nSubject: ${args.subject}\\n\\n` +\n `${args.text || stripHtml(args.html)}\\n---\\n`,\n );\n}\n\nfunction parseSendGridFrom(from: string): { email: string; name?: string } {\n const m = from.match(/^\\s*(.*?)\\s*<(.+)>\\s*$/);\n if (m && m[2]) return { name: m[1] || undefined, email: m[2] };\n return { email: from.trim() };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/ /g, \" \")\n .trim();\n}\n"]}
|
|
@@ -228,6 +228,7 @@ export default defineAction({
|
|
|
228
228
|
const appName = process.env.APP_NAME || process.env.VITE_APP_NAME || "Agent Native";
|
|
229
229
|
const subject = `${actor} shared "${resourceTitle}" with you on ${appName}`;
|
|
230
230
|
const { html, text } = renderEmail({
|
|
231
|
+
brandName: appName,
|
|
231
232
|
preheader: subject,
|
|
232
233
|
heading: "You've been given access",
|
|
233
234
|
paragraphs: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"share-resource.js","sourceRoot":"","sources":["../../../src/sharing/actions/share-resource.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, sql, type SQL } from \"drizzle-orm\";\nimport { z } from \"zod\";\n\nimport { defineAction } from \"../../action.js\";\nimport { getDbExec } from \"../../db/client.js\";\nimport { getAppProductionUrl } from \"../../server/app-url.js\";\nimport { renderEmail, emailStrong } from \"../../server/email-template.js\";\nimport { sendEmail, isEmailConfigured } from \"../../server/email.js\";\nimport { invalidateCollabAccessCache } from \"../../server/poll.js\";\nimport { getRequestUserEmail } from \"../../server/request-context.js\";\nimport { assertAccess, ForbiddenError } from \"../access.js\";\nimport { requireShareableResource } from \"../registry.js\";\nimport {\n getExtensionShareChangeTargets,\n notifyExtensionShareChanged,\n} from \"./extension-change.js\";\n\nexport function isSyntheticQaEmail(email: string): boolean {\n const trimmed = email.trim().toLowerCase();\n const at = trimmed.lastIndexOf(\"@\");\n if (at <= 0) return false;\n const local = trimmed.slice(0, at);\n const domain = trimmed.slice(at + 1);\n return (\n local.includes(\"+qa\") &&\n (domain === \"example.test\" ||\n domain.endsWith(\".test\") ||\n domain === \"example.invalid\" ||\n domain.endsWith(\".invalid\"))\n );\n}\n\nfunction appPath(path: string): string {\n if (!path.startsWith(\"/\")) return path;\n const raw = process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const base = raw.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (!base) return path;\n const normalizedBase = `/${base}`;\n if (path === normalizedBase || path.startsWith(`${normalizedBase}/`)) {\n return path;\n }\n return `${normalizedBase}${path}`;\n}\n\nfunction safeNotificationUrl(value: string, appUrl: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n try {\n const base = new URL(appUrl);\n if (trimmed.startsWith(\"/\")) {\n const path = appPath(trimmed);\n const basePath = base.pathname.replace(/\\/+$/, \"\");\n const alreadyIncludesBase =\n basePath && basePath !== \"/\" && path.startsWith(`${basePath}/`);\n const joined = alreadyIncludesBase\n ? `${base.origin}${path}`\n : `${appUrl.replace(/\\/+$/, \"\")}${path}`;\n return new URL(joined).toString();\n }\n\n const url = new URL(trimmed);\n if (![\"http:\", \"https:\"].includes(url.protocol)) return null;\n if (url.origin !== base.origin) return null;\n return url.toString();\n } catch {\n return null;\n }\n}\n\nexport function resolveShareNotificationUrl(\n explicitUrl: string | undefined,\n fallbackPath: string | undefined,\n appUrl = getAppProductionUrl(),\n): string {\n for (const candidate of [explicitUrl, fallbackPath]) {\n if (!candidate) continue;\n const url = safeNotificationUrl(candidate, appUrl);\n if (url) return url;\n }\n return appUrl;\n}\n\nfunction nanoid(size = 12): string {\n const chars =\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n let id = \"\";\n const bytes = crypto.getRandomValues(new Uint8Array(size));\n for (const byte of bytes) id += chars[byte % chars.length];\n return id;\n}\n\nfunction normalizePrincipalId(\n principalType: \"user\" | \"org\",\n principalId: string,\n): string {\n return principalType === \"user\"\n ? principalId.trim().toLowerCase()\n : principalId;\n}\n\nfunction isEmailPrincipalId(value: string): boolean {\n return /^[^\\s@]+@[^\\s@]+$/.test(value.trim());\n}\n\nfunction principalIdMatches(\n sharesTable: any,\n principalType: \"user\" | \"org\",\n principalId: string,\n): SQL {\n return principalType === \"user\"\n ? sql`lower(${sharesTable.principalId}) = ${principalId}`\n : eq(sharesTable.principalId, principalId);\n}\n\n/**\n * Returns true if the given email is either an active member of `orgId` or\n * has a pending invitation to `orgId`. Used by resources whose registration\n * sets `requireOrgMemberForUserShares` (currently extensions) to refuse\n * cross-org user shares.\n *\n * Both `org_members` and `org_invitations` store email case-insensitively\n * via `LOWER()` in the rest of the framework, so we follow the same\n * convention here.\n */\nasync function isOrgMemberOrInvited(\n orgId: string,\n email: string,\n): Promise<boolean> {\n const lower = email.trim().toLowerCase();\n if (!lower || !orgId) return false;\n const client = getDbExec();\n const member = await client.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [orgId, lower],\n });\n if (member.rows.length > 0) return true;\n const invited = await client.execute({\n sql: `SELECT 1 FROM org_invitations WHERE org_id = ? AND LOWER(email) = ? AND status = 'pending' LIMIT 1`,\n args: [orgId, lower],\n });\n return invited.rows.length > 0;\n}\n\nexport default defineAction({\n description:\n \"Grant a user or org access to a shareable resource. Owner or admin role required.\",\n // (audit H5) Sharing-grant operations are admin-tier and let a caller\n // expand who can read/write a resource. Refuse from the tools iframe\n // bridge so a malicious shared tool can't silently re-share its\n // viewer's resources to an attacker-controlled email.\n toolCallable: false,\n schema: z.object({\n resourceType: z\n .string()\n .describe(\"Registered resource type, e.g. 'document', 'form'.\"),\n resourceId: z.string().describe(\"Id of the resource to share.\"),\n principalType: z\n .enum([\"user\", \"org\"])\n .describe(\"'user' for an individual, 'org' for a whole organization.\"),\n principalId: z\n .string()\n .describe(\"Email (user) or org id (org) of the principal.\"),\n role: z\n .enum([\"viewer\", \"editor\", \"admin\"])\n .default(\"viewer\")\n .describe(\"Role to grant.\"),\n notify: z\n .boolean()\n .default(true)\n .describe(\n \"Whether to email the user about a new individual share. Defaults to true.\",\n ),\n resourceUrl: z\n .string()\n .optional()\n .describe(\n \"Optional app-relative or same-origin URL recipients should open. External origins are ignored.\",\n ),\n }),\n run: async (args) => {\n const reg = requireShareableResource(args.resourceType);\n const access = await assertAccess(\n args.resourceType,\n args.resourceId,\n \"admin\",\n );\n const actor = getRequestUserEmail();\n if (!actor) throw new ForbiddenError(\"Not signed in\");\n const principalId = normalizePrincipalId(\n args.principalType,\n args.principalId,\n );\n if (args.principalType === \"user\" && !isEmailPrincipalId(principalId)) {\n throw new Error(\n \"User shares must use an email address, not an internal user id.\",\n );\n }\n const beforeExtensionTargets = await getExtensionShareChangeTargets(\n args.resourceType,\n args.resourceId,\n );\n\n if (reg.requireOrgMemberForUserShares) {\n const resourceOrgId = access.resource?.orgId as string | undefined | null;\n if (!resourceOrgId) {\n throw new ForbiddenError(\n `${reg.displayName} can only be shared from within an organization. Create or join an organization first.`,\n );\n }\n if (args.principalType === \"user\") {\n const ok = await isOrgMemberOrInvited(resourceOrgId, principalId);\n if (!ok) {\n throw new ForbiddenError(\n `${principalId} is not in your organization. Invite them to the organization first, then share.`,\n );\n }\n } else if (args.principalType === \"org\") {\n // Cross-org org shares would let an outside org's members run\n // extension code in the viewer's auth context — the same threat\n // model that blocks public + cross-org user shares. Pin org-\n // principal shares to the resource's own org.\n if (principalId !== resourceOrgId) {\n throw new ForbiddenError(\n `${reg.displayName} can only be shared with its own organization, not a different one.`,\n );\n }\n }\n }\n\n const db = reg.getDb() as any;\n const [existing] = await db\n .select()\n .from(reg.sharesTable)\n .where(\n and(\n eq(reg.sharesTable.resourceId, args.resourceId),\n eq(reg.sharesTable.principalType, args.principalType),\n principalIdMatches(reg.sharesTable, args.principalType, principalId),\n ),\n );\n\n if (existing) {\n await db\n .update(reg.sharesTable)\n .set({ role: args.role })\n .where(eq(reg.sharesTable.id, existing.id));\n invalidateCollabAccessCache(args.resourceType, args.resourceId);\n await notifyExtensionShareChanged(\n args.resourceType,\n args.resourceId,\n beforeExtensionTargets,\n );\n return { id: existing.id, updated: true };\n }\n\n const id = nanoid();\n await db.insert(reg.sharesTable).values({\n id,\n resourceId: args.resourceId,\n principalType: args.principalType,\n principalId,\n role: args.role,\n createdBy: actor,\n createdAt: new Date().toISOString(),\n });\n invalidateCollabAccessCache(args.resourceType, args.resourceId);\n await notifyExtensionShareChanged(\n args.resourceType,\n args.resourceId,\n beforeExtensionTargets,\n );\n\n if (\n args.notify !== false &&\n args.principalType === \"user\" &&\n (await isEmailConfigured()) &&\n !isSyntheticQaEmail(principalId)\n ) {\n try {\n const titleCol = reg.titleColumn ?? \"title\";\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, args.resourceId));\n const resourceTitle: string =\n (resource?.[titleCol] as string | undefined) ?? args.resourceType;\n const appUrl = getAppProductionUrl();\n const resourcePath =\n resource && reg.getResourcePath\n ? reg.getResourcePath(resource)\n : undefined;\n const notificationUrl = resolveShareNotificationUrl(\n args.resourceUrl,\n resourcePath,\n appUrl,\n );\n const appName =\n process.env.APP_NAME || process.env.VITE_APP_NAME || \"Agent Native\";\n const subject = `${actor} shared \"${resourceTitle}\" with you on ${appName}`;\n const { html, text } = renderEmail({\n preheader: subject,\n heading: \"You've been given access\",\n paragraphs: [\n `${emailStrong(actor)} has shared the ${reg.displayName} ${emailStrong(resourceTitle)} with you as a ${emailStrong(args.role)}.`,\n `Use the button below to open it. If prompted, sign in with ${emailStrong(principalId)}.`,\n ],\n cta: { label: `Open ${reg.displayName}`, url: notificationUrl },\n footer: `You received this because ${actor} granted you ${args.role} access.`,\n });\n await sendEmail({ to: principalId, subject, html, text });\n } catch (err) {\n console.error(\n \"[share-resource] failed to send share notification:\",\n err,\n );\n }\n }\n\n return { id, updated: false };\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"share-resource.js","sourceRoot":"","sources":["../../../src/sharing/actions/share-resource.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, sql, type SQL } from \"drizzle-orm\";\nimport { z } from \"zod\";\n\nimport { defineAction } from \"../../action.js\";\nimport { getDbExec } from \"../../db/client.js\";\nimport { getAppProductionUrl } from \"../../server/app-url.js\";\nimport { renderEmail, emailStrong } from \"../../server/email-template.js\";\nimport { sendEmail, isEmailConfigured } from \"../../server/email.js\";\nimport { invalidateCollabAccessCache } from \"../../server/poll.js\";\nimport { getRequestUserEmail } from \"../../server/request-context.js\";\nimport { assertAccess, ForbiddenError } from \"../access.js\";\nimport { requireShareableResource } from \"../registry.js\";\nimport {\n getExtensionShareChangeTargets,\n notifyExtensionShareChanged,\n} from \"./extension-change.js\";\n\nexport function isSyntheticQaEmail(email: string): boolean {\n const trimmed = email.trim().toLowerCase();\n const at = trimmed.lastIndexOf(\"@\");\n if (at <= 0) return false;\n const local = trimmed.slice(0, at);\n const domain = trimmed.slice(at + 1);\n return (\n local.includes(\"+qa\") &&\n (domain === \"example.test\" ||\n domain.endsWith(\".test\") ||\n domain === \"example.invalid\" ||\n domain.endsWith(\".invalid\"))\n );\n}\n\nfunction appPath(path: string): string {\n if (!path.startsWith(\"/\")) return path;\n const raw = process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const base = raw.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (!base) return path;\n const normalizedBase = `/${base}`;\n if (path === normalizedBase || path.startsWith(`${normalizedBase}/`)) {\n return path;\n }\n return `${normalizedBase}${path}`;\n}\n\nfunction safeNotificationUrl(value: string, appUrl: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n try {\n const base = new URL(appUrl);\n if (trimmed.startsWith(\"/\")) {\n const path = appPath(trimmed);\n const basePath = base.pathname.replace(/\\/+$/, \"\");\n const alreadyIncludesBase =\n basePath && basePath !== \"/\" && path.startsWith(`${basePath}/`);\n const joined = alreadyIncludesBase\n ? `${base.origin}${path}`\n : `${appUrl.replace(/\\/+$/, \"\")}${path}`;\n return new URL(joined).toString();\n }\n\n const url = new URL(trimmed);\n if (![\"http:\", \"https:\"].includes(url.protocol)) return null;\n if (url.origin !== base.origin) return null;\n return url.toString();\n } catch {\n return null;\n }\n}\n\nexport function resolveShareNotificationUrl(\n explicitUrl: string | undefined,\n fallbackPath: string | undefined,\n appUrl = getAppProductionUrl(),\n): string {\n for (const candidate of [explicitUrl, fallbackPath]) {\n if (!candidate) continue;\n const url = safeNotificationUrl(candidate, appUrl);\n if (url) return url;\n }\n return appUrl;\n}\n\nfunction nanoid(size = 12): string {\n const chars =\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n let id = \"\";\n const bytes = crypto.getRandomValues(new Uint8Array(size));\n for (const byte of bytes) id += chars[byte % chars.length];\n return id;\n}\n\nfunction normalizePrincipalId(\n principalType: \"user\" | \"org\",\n principalId: string,\n): string {\n return principalType === \"user\"\n ? principalId.trim().toLowerCase()\n : principalId;\n}\n\nfunction isEmailPrincipalId(value: string): boolean {\n return /^[^\\s@]+@[^\\s@]+$/.test(value.trim());\n}\n\nfunction principalIdMatches(\n sharesTable: any,\n principalType: \"user\" | \"org\",\n principalId: string,\n): SQL {\n return principalType === \"user\"\n ? sql`lower(${sharesTable.principalId}) = ${principalId}`\n : eq(sharesTable.principalId, principalId);\n}\n\n/**\n * Returns true if the given email is either an active member of `orgId` or\n * has a pending invitation to `orgId`. Used by resources whose registration\n * sets `requireOrgMemberForUserShares` (currently extensions) to refuse\n * cross-org user shares.\n *\n * Both `org_members` and `org_invitations` store email case-insensitively\n * via `LOWER()` in the rest of the framework, so we follow the same\n * convention here.\n */\nasync function isOrgMemberOrInvited(\n orgId: string,\n email: string,\n): Promise<boolean> {\n const lower = email.trim().toLowerCase();\n if (!lower || !orgId) return false;\n const client = getDbExec();\n const member = await client.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [orgId, lower],\n });\n if (member.rows.length > 0) return true;\n const invited = await client.execute({\n sql: `SELECT 1 FROM org_invitations WHERE org_id = ? AND LOWER(email) = ? AND status = 'pending' LIMIT 1`,\n args: [orgId, lower],\n });\n return invited.rows.length > 0;\n}\n\nexport default defineAction({\n description:\n \"Grant a user or org access to a shareable resource. Owner or admin role required.\",\n // (audit H5) Sharing-grant operations are admin-tier and let a caller\n // expand who can read/write a resource. Refuse from the tools iframe\n // bridge so a malicious shared tool can't silently re-share its\n // viewer's resources to an attacker-controlled email.\n toolCallable: false,\n schema: z.object({\n resourceType: z\n .string()\n .describe(\"Registered resource type, e.g. 'document', 'form'.\"),\n resourceId: z.string().describe(\"Id of the resource to share.\"),\n principalType: z\n .enum([\"user\", \"org\"])\n .describe(\"'user' for an individual, 'org' for a whole organization.\"),\n principalId: z\n .string()\n .describe(\"Email (user) or org id (org) of the principal.\"),\n role: z\n .enum([\"viewer\", \"editor\", \"admin\"])\n .default(\"viewer\")\n .describe(\"Role to grant.\"),\n notify: z\n .boolean()\n .default(true)\n .describe(\n \"Whether to email the user about a new individual share. Defaults to true.\",\n ),\n resourceUrl: z\n .string()\n .optional()\n .describe(\n \"Optional app-relative or same-origin URL recipients should open. External origins are ignored.\",\n ),\n }),\n run: async (args) => {\n const reg = requireShareableResource(args.resourceType);\n const access = await assertAccess(\n args.resourceType,\n args.resourceId,\n \"admin\",\n );\n const actor = getRequestUserEmail();\n if (!actor) throw new ForbiddenError(\"Not signed in\");\n const principalId = normalizePrincipalId(\n args.principalType,\n args.principalId,\n );\n if (args.principalType === \"user\" && !isEmailPrincipalId(principalId)) {\n throw new Error(\n \"User shares must use an email address, not an internal user id.\",\n );\n }\n const beforeExtensionTargets = await getExtensionShareChangeTargets(\n args.resourceType,\n args.resourceId,\n );\n\n if (reg.requireOrgMemberForUserShares) {\n const resourceOrgId = access.resource?.orgId as string | undefined | null;\n if (!resourceOrgId) {\n throw new ForbiddenError(\n `${reg.displayName} can only be shared from within an organization. Create or join an organization first.`,\n );\n }\n if (args.principalType === \"user\") {\n const ok = await isOrgMemberOrInvited(resourceOrgId, principalId);\n if (!ok) {\n throw new ForbiddenError(\n `${principalId} is not in your organization. Invite them to the organization first, then share.`,\n );\n }\n } else if (args.principalType === \"org\") {\n // Cross-org org shares would let an outside org's members run\n // extension code in the viewer's auth context — the same threat\n // model that blocks public + cross-org user shares. Pin org-\n // principal shares to the resource's own org.\n if (principalId !== resourceOrgId) {\n throw new ForbiddenError(\n `${reg.displayName} can only be shared with its own organization, not a different one.`,\n );\n }\n }\n }\n\n const db = reg.getDb() as any;\n const [existing] = await db\n .select()\n .from(reg.sharesTable)\n .where(\n and(\n eq(reg.sharesTable.resourceId, args.resourceId),\n eq(reg.sharesTable.principalType, args.principalType),\n principalIdMatches(reg.sharesTable, args.principalType, principalId),\n ),\n );\n\n if (existing) {\n await db\n .update(reg.sharesTable)\n .set({ role: args.role })\n .where(eq(reg.sharesTable.id, existing.id));\n invalidateCollabAccessCache(args.resourceType, args.resourceId);\n await notifyExtensionShareChanged(\n args.resourceType,\n args.resourceId,\n beforeExtensionTargets,\n );\n return { id: existing.id, updated: true };\n }\n\n const id = nanoid();\n await db.insert(reg.sharesTable).values({\n id,\n resourceId: args.resourceId,\n principalType: args.principalType,\n principalId,\n role: args.role,\n createdBy: actor,\n createdAt: new Date().toISOString(),\n });\n invalidateCollabAccessCache(args.resourceType, args.resourceId);\n await notifyExtensionShareChanged(\n args.resourceType,\n args.resourceId,\n beforeExtensionTargets,\n );\n\n if (\n args.notify !== false &&\n args.principalType === \"user\" &&\n (await isEmailConfigured()) &&\n !isSyntheticQaEmail(principalId)\n ) {\n try {\n const titleCol = reg.titleColumn ?? \"title\";\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, args.resourceId));\n const resourceTitle: string =\n (resource?.[titleCol] as string | undefined) ?? args.resourceType;\n const appUrl = getAppProductionUrl();\n const resourcePath =\n resource && reg.getResourcePath\n ? reg.getResourcePath(resource)\n : undefined;\n const notificationUrl = resolveShareNotificationUrl(\n args.resourceUrl,\n resourcePath,\n appUrl,\n );\n const appName =\n process.env.APP_NAME || process.env.VITE_APP_NAME || \"Agent Native\";\n const subject = `${actor} shared \"${resourceTitle}\" with you on ${appName}`;\n const { html, text } = renderEmail({\n brandName: appName,\n preheader: subject,\n heading: \"You've been given access\",\n paragraphs: [\n `${emailStrong(actor)} has shared the ${reg.displayName} ${emailStrong(resourceTitle)} with you as a ${emailStrong(args.role)}.`,\n `Use the button below to open it. If prompted, sign in with ${emailStrong(principalId)}.`,\n ],\n cta: { label: `Open ${reg.displayName}`, url: notificationUrl },\n footer: `You received this because ${actor} granted you ${args.role} access.`,\n });\n await sendEmail({ to: principalId, subject, html, text });\n } catch (err) {\n console.error(\n \"[share-resource] failed to send share notification:\",\n err,\n );\n }\n }\n\n return { id, updated: false };\n },\n});\n"]}
|
|
@@ -59,6 +59,12 @@ quick controls and deep links such as:
|
|
|
59
59
|
- `/settings/usage`
|
|
60
60
|
- `/settings/apps/:appId`
|
|
61
61
|
|
|
62
|
+
The shared Account section is the canonical profile surface at
|
|
63
|
+
`/settings#account`. It owns the editable display name and existing avatar
|
|
64
|
+
control through the authenticated `get-user-profile` and `update-user-profile`
|
|
65
|
+
actions. Shared workspace chrome such as `OrgSwitcher` should link to this
|
|
66
|
+
surface rather than creating an app-local profile page.
|
|
67
|
+
|
|
62
68
|
When adding a new API key, OAuth grant, provider connection, model selector, app
|
|
63
69
|
preference, notification preference, or usage/billing surface, register it as a
|
|
64
70
|
settings tab or app settings panel first. Only add sidebar UI when it is needed
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type ChatHistoryItem,
|
|
14
14
|
} from "@agent-native/toolkit/chat-history";
|
|
15
15
|
import {
|
|
16
|
-
|
|
16
|
+
IconHierarchy2,
|
|
17
17
|
IconLayoutSidebarLeftCollapse,
|
|
18
18
|
IconLayoutSidebarLeftExpand,
|
|
19
19
|
IconMessageCircle,
|
|
@@ -39,7 +39,7 @@ const navItems = [
|
|
|
39
39
|
view: "chat",
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
|
-
icon:
|
|
42
|
+
icon: IconHierarchy2,
|
|
43
43
|
labelKey: "settings.agentTitle",
|
|
44
44
|
href: "/agent",
|
|
45
45
|
view: "agent",
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useCommandMenuShortcut,
|
|
12
12
|
} from "@agent-native/core/client/navigation";
|
|
13
13
|
import { getThemeInitScript } from "@agent-native/core/client/ui";
|
|
14
|
-
import {
|
|
14
|
+
import { IconHierarchy2, IconSun, IconMoon } from "@tabler/icons-react";
|
|
15
15
|
import { useQueryClient } from "@tanstack/react-query";
|
|
16
16
|
import { useTheme } from "next-themes";
|
|
17
17
|
import { useCallback, useState } from "react";
|
|
@@ -143,7 +143,7 @@ function AppContent() {
|
|
|
143
143
|
"access",
|
|
144
144
|
]}
|
|
145
145
|
>
|
|
146
|
-
<
|
|
146
|
+
<IconHierarchy2 size={16} />
|
|
147
147
|
{t("settings.openAgentSettings")}
|
|
148
148
|
</CommandMenu.Item>
|
|
149
149
|
</CommandMenu.Group>
|
|
@@ -59,6 +59,12 @@ quick controls and deep links such as:
|
|
|
59
59
|
- `/settings/usage`
|
|
60
60
|
- `/settings/apps/:appId`
|
|
61
61
|
|
|
62
|
+
The shared Account section is the canonical profile surface at
|
|
63
|
+
`/settings#account`. It owns the editable display name and existing avatar
|
|
64
|
+
control through the authenticated `get-user-profile` and `update-user-profile`
|
|
65
|
+
actions. Shared workspace chrome such as `OrgSwitcher` should link to this
|
|
66
|
+
surface rather than creating an app-local profile page.
|
|
67
|
+
|
|
62
68
|
When adding a new API key, OAuth grant, provider connection, model selector, app
|
|
63
69
|
preference, notification preference, or usage/billing surface, register it as a
|
|
64
70
|
settings tab or app settings panel first. Only add sidebar UI when it is needed
|
|
@@ -359,24 +359,24 @@ const messages = {
|
|
|
359
359
|
hideEveryone: "إخفاء من الجميع",
|
|
360
360
|
localFileDescription:
|
|
361
361
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
362
|
-
deleteQuestion: "
|
|
362
|
+
deleteQuestion: "هل تريد أرشفة {{name}}؟",
|
|
363
363
|
removeQuestion: "Remove {{name}}?",
|
|
364
364
|
hideForYouDescription:
|
|
365
365
|
"يؤدي هذا إلى إخفائه من قائمة الإضافات الخاصة بك دون حذفه لأي شخص آخر.",
|
|
366
366
|
removeEverywhereDescription:
|
|
367
|
-
"يؤدي هذا إلى
|
|
367
|
+
"يؤدي هذا إلى أرشفته في كل مكان تتم مشاركته فيه.",
|
|
368
368
|
cancel: "إلغاء",
|
|
369
369
|
remove: "إزالة",
|
|
370
370
|
removing: "جارٍ الإزالة...",
|
|
371
|
-
delete: "
|
|
372
|
-
deleting: "
|
|
371
|
+
delete: "أرشفة",
|
|
372
|
+
deleting: "جارٍ الأرشفة...",
|
|
373
373
|
openFullView: "فتح عرض كامل",
|
|
374
374
|
removeFromWidgetArea: "إزالة من منطقة القطعة هذه",
|
|
375
|
-
deleteExtensionEllipsis: "
|
|
375
|
+
deleteExtensionEllipsis: "أرشفة الامتداد...",
|
|
376
376
|
removeFromMyListEllipsis: "إزالة من قائمتي...",
|
|
377
377
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
378
378
|
deleteEverywhereConfirmation:
|
|
379
|
-
"يؤدي هذا إلى
|
|
379
|
+
"يؤدي هذا إلى أرشفة الامتداد في كل مكان، لكل شخص تمت مشاركته معه.",
|
|
380
380
|
addWidget: "إضافة القطعة",
|
|
381
381
|
loading: "جار التحميل...",
|
|
382
382
|
noWidgetsAvailable: "لا توجد أدوات متاحة لهذه الفتحة حتى الآن.",
|
|
@@ -352,24 +352,24 @@ const messages = {
|
|
|
352
352
|
hideEveryone: "Versteck dich vor allen",
|
|
353
353
|
localFileDescription:
|
|
354
354
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
355
|
-
deleteQuestion: "
|
|
355
|
+
deleteQuestion: "{{name}} archivieren?",
|
|
356
356
|
removeQuestion: "Remove {{name}}?",
|
|
357
357
|
hideForYouDescription:
|
|
358
358
|
"Dadurch wird es aus Ihrer Erweiterungsliste ausgeblendet, ohne dass es für andere gelöscht wird.",
|
|
359
359
|
removeEverywhereDescription:
|
|
360
|
-
"Dadurch wird es überall dort
|
|
360
|
+
"Dadurch wird es überall dort archiviert, wo es geteilt wird.",
|
|
361
361
|
cancel: "Abbrechen",
|
|
362
362
|
remove: "Entfernen",
|
|
363
363
|
removing: "Entfernen...",
|
|
364
|
-
delete: "
|
|
365
|
-
deleting: "Wird
|
|
364
|
+
delete: "Archivieren",
|
|
365
|
+
deleting: "Wird archiviert...",
|
|
366
366
|
openFullView: "Vollansicht öffnen",
|
|
367
367
|
removeFromWidgetArea: "Aus diesem Widget-Bereich entfernen",
|
|
368
|
-
deleteExtensionEllipsis: "Erweiterung
|
|
368
|
+
deleteExtensionEllipsis: "Erweiterung archivieren...",
|
|
369
369
|
removeFromMyListEllipsis: "Von meiner Liste entfernen...",
|
|
370
370
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
371
371
|
deleteEverywhereConfirmation:
|
|
372
|
-
"Dadurch wird die Erweiterung überall und für alle
|
|
372
|
+
"Dadurch wird die Erweiterung überall und für alle archiviert, mit denen sie geteilt wird.",
|
|
373
373
|
addWidget: "Widget hinzufügen",
|
|
374
374
|
loading: "Wird geladen...",
|
|
375
375
|
noWidgetsAvailable: "Für diesen Slot sind noch keine Widgets verfügbar.",
|
|
@@ -344,23 +344,23 @@ const messages = {
|
|
|
344
344
|
hideEveryone: "Hide from everyone",
|
|
345
345
|
localFileDescription:
|
|
346
346
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
347
|
-
deleteQuestion: "
|
|
347
|
+
deleteQuestion: "Archive {{name}}?",
|
|
348
348
|
removeQuestion: "Remove {{name}}?",
|
|
349
349
|
hideForYouDescription:
|
|
350
350
|
"This hides it from your Extensions list without deleting it for anyone else.",
|
|
351
|
-
removeEverywhereDescription: "This
|
|
351
|
+
removeEverywhereDescription: "This archives it everywhere it is shared.",
|
|
352
352
|
cancel: "Cancel",
|
|
353
353
|
remove: "Remove",
|
|
354
354
|
removing: "Removing...",
|
|
355
|
-
delete: "
|
|
356
|
-
deleting: "
|
|
355
|
+
delete: "Archive",
|
|
356
|
+
deleting: "Archiving...",
|
|
357
357
|
openFullView: "Open full view",
|
|
358
358
|
removeFromWidgetArea: "Remove from this widget area",
|
|
359
|
-
deleteExtensionEllipsis: "
|
|
359
|
+
deleteExtensionEllipsis: "Archive extension...",
|
|
360
360
|
removeFromMyListEllipsis: "Remove from my list...",
|
|
361
361
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
362
362
|
deleteEverywhereConfirmation:
|
|
363
|
-
"This
|
|
363
|
+
"This archives the extension everywhere, for everyone it's shared with.",
|
|
364
364
|
addWidget: "Add widget",
|
|
365
365
|
loading: "Loading...",
|
|
366
366
|
noWidgetsAvailable: "No widgets available for this slot yet.",
|
|
@@ -355,24 +355,24 @@ const messages = {
|
|
|
355
355
|
hideEveryone: "esconderse de todos",
|
|
356
356
|
localFileDescription:
|
|
357
357
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
358
|
-
deleteQuestion: "
|
|
358
|
+
deleteQuestion: "¿Archivar {{name}}?",
|
|
359
359
|
removeQuestion: "Remove {{name}}?",
|
|
360
360
|
hideForYouDescription:
|
|
361
361
|
"Esto lo oculta de su lista de Extensiones sin eliminarlo para nadie más.",
|
|
362
362
|
removeEverywhereDescription:
|
|
363
|
-
"Esto lo
|
|
363
|
+
"Esto lo archiva en todos los lugares donde se comparte.",
|
|
364
364
|
cancel: "Cancelar",
|
|
365
365
|
remove: "Eliminar",
|
|
366
366
|
removing: "Eliminando...",
|
|
367
|
-
delete: "
|
|
368
|
-
deleting: "
|
|
367
|
+
delete: "Archivar",
|
|
368
|
+
deleting: "Archivando...",
|
|
369
369
|
openFullView: "Abrir vista completa",
|
|
370
370
|
removeFromWidgetArea: "Eliminar de esta área de widgets",
|
|
371
|
-
deleteExtensionEllipsis: "
|
|
371
|
+
deleteExtensionEllipsis: "Archivar extensión...",
|
|
372
372
|
removeFromMyListEllipsis: "Quitar de mi lista...",
|
|
373
373
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
374
374
|
deleteEverywhereConfirmation:
|
|
375
|
-
"Esto
|
|
375
|
+
"Esto archiva la extensión en todas partes, para todas las personas con las que se comparte.",
|
|
376
376
|
addWidget: "Agregar widget",
|
|
377
377
|
loading: "Cargando...",
|
|
378
378
|
noWidgetsAvailable:
|
|
@@ -355,23 +355,23 @@ const messages = {
|
|
|
355
355
|
hideEveryone: "Cacher de tout le monde",
|
|
356
356
|
localFileDescription:
|
|
357
357
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
358
|
-
deleteQuestion: "
|
|
358
|
+
deleteQuestion: "Archiver {{name}} ?",
|
|
359
359
|
removeQuestion: "Remove {{name}}?",
|
|
360
360
|
hideForYouDescription:
|
|
361
361
|
"Cela le masque de votre liste d'extensions sans le supprimer pour quelqu'un d'autre.",
|
|
362
|
-
removeEverywhereDescription: "Cela
|
|
362
|
+
removeEverywhereDescription: "Cela l’archive partout où il est partagé.",
|
|
363
363
|
cancel: "Annuler",
|
|
364
364
|
remove: "Retirer",
|
|
365
365
|
removing: "Suppression...",
|
|
366
|
-
delete: "
|
|
367
|
-
deleting: "
|
|
366
|
+
delete: "Archiver",
|
|
367
|
+
deleting: "Archivage...",
|
|
368
368
|
openFullView: "Ouvrir la vue complète",
|
|
369
369
|
removeFromWidgetArea: "Supprimer de cette zone de widget",
|
|
370
|
-
deleteExtensionEllipsis: "
|
|
370
|
+
deleteExtensionEllipsis: "Archiver l'extension...",
|
|
371
371
|
removeFromMyListEllipsis: "Supprimer de ma liste...",
|
|
372
372
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
373
373
|
deleteEverywhereConfirmation:
|
|
374
|
-
"Cela
|
|
374
|
+
"Cela archive l'extension partout, pour toutes les personnes avec lesquelles elle est partagée.",
|
|
375
375
|
addWidget: "Ajouter un widget",
|
|
376
376
|
loading: "Chargement...",
|
|
377
377
|
noWidgetsAvailable:
|
|
@@ -338,24 +338,24 @@ const messages = {
|
|
|
338
338
|
hideEveryone: "सबसे छुपाओ",
|
|
339
339
|
localFileDescription:
|
|
340
340
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
341
|
-
deleteQuestion: "
|
|
341
|
+
deleteQuestion: "{{name}} को संग्रहित करें?",
|
|
342
342
|
removeQuestion: "Remove {{name}}?",
|
|
343
343
|
hideForYouDescription:
|
|
344
344
|
"यह इसे किसी अन्य के लिए हटाए बिना आपकी एक्सटेंशन सूची से छिपा देता है।",
|
|
345
345
|
removeEverywhereDescription:
|
|
346
|
-
"यह इसे
|
|
346
|
+
"यह इसे साझा किए गए हर स्थान पर संग्रहित कर देता है।",
|
|
347
347
|
cancel: "रद्द करें",
|
|
348
348
|
remove: "हटाएं",
|
|
349
349
|
removing: "हटाया जा रहा है...",
|
|
350
|
-
delete: "
|
|
351
|
-
deleting: "
|
|
350
|
+
delete: "संग्रहित करें",
|
|
351
|
+
deleting: "संग्रहित किया जा रहा है...",
|
|
352
352
|
openFullView: "पूरा दृश्य खोलें",
|
|
353
353
|
removeFromWidgetArea: "इस विजेट क्षेत्र से हटाएँ",
|
|
354
|
-
deleteExtensionEllipsis: "एक्सटेंशन
|
|
354
|
+
deleteExtensionEllipsis: "एक्सटेंशन संग्रहित करें...",
|
|
355
355
|
removeFromMyListEllipsis: "मेरी सूची से हटाएं...",
|
|
356
356
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
357
357
|
deleteEverywhereConfirmation:
|
|
358
|
-
"यह एक्सटेंशन को
|
|
358
|
+
"यह एक्सटेंशन को साझा किए गए हर स्थान पर, सभी लोगों के लिए संग्रहित कर देता है।",
|
|
359
359
|
addWidget: "विजेट जोड़ें",
|
|
360
360
|
loading: "लोड हो रहा है...",
|
|
361
361
|
noWidgetsAvailable: "इस स्लॉट के लिए अभी तक कोई विजेट उपलब्ध नहीं है।",
|
|
@@ -344,24 +344,24 @@ const messages = {
|
|
|
344
344
|
hideEveryone: "みんなから隠れて",
|
|
345
345
|
localFileDescription:
|
|
346
346
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
347
|
-
deleteQuestion: "
|
|
347
|
+
deleteQuestion: "{{name}}をアーカイブしますか?",
|
|
348
348
|
removeQuestion: "Remove {{name}}?",
|
|
349
349
|
hideForYouDescription:
|
|
350
350
|
"これにより、他のユーザーが削除することなく、拡張機能リストから非表示になります。",
|
|
351
351
|
removeEverywhereDescription:
|
|
352
|
-
"
|
|
352
|
+
"これにより、共有されているすべての場所でアーカイブされます。",
|
|
353
353
|
cancel: "キャンセル",
|
|
354
354
|
remove: "削除",
|
|
355
355
|
removing: "削除中...",
|
|
356
|
-
delete: "
|
|
357
|
-
deleting: "
|
|
356
|
+
delete: "アーカイブ",
|
|
357
|
+
deleting: "アーカイブ中...",
|
|
358
358
|
openFullView: "フルビューを開く",
|
|
359
359
|
removeFromWidgetArea: "このウィジェット領域から削除する",
|
|
360
|
-
deleteExtensionEllipsis: "
|
|
360
|
+
deleteExtensionEllipsis: "拡張機能をアーカイブ...",
|
|
361
361
|
removeFromMyListEllipsis: "リストから削除...",
|
|
362
362
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
363
363
|
deleteEverywhereConfirmation:
|
|
364
|
-
"
|
|
364
|
+
"これにより、共有されているすべてのユーザーの拡張機能がどこからでもアーカイブされます。",
|
|
365
365
|
addWidget: "ウィジェットを追加する",
|
|
366
366
|
loading: "読み込み中...",
|
|
367
367
|
noWidgetsAvailable:
|
|
@@ -337,24 +337,24 @@ const messages = {
|
|
|
337
337
|
hideEveryone: "모든 사람에게서 숨기기",
|
|
338
338
|
localFileDescription:
|
|
339
339
|
"This extension is backed by {{entryPath}}. Edit or remove it from the workspace.",
|
|
340
|
-
deleteQuestion: "
|
|
340
|
+
deleteQuestion: "{{name}}을(를) 보관할까요?",
|
|
341
341
|
removeQuestion: "Remove {{name}}?",
|
|
342
342
|
hideForYouDescription:
|
|
343
343
|
"이렇게 하면 다른 사람을 위해 삭제하지 않고 확장 프로그램 목록에서 숨겨집니다.",
|
|
344
344
|
removeEverywhereDescription:
|
|
345
|
-
"이렇게 하면 공유된 모든 위치에서 해당 항목이
|
|
345
|
+
"이렇게 하면 공유된 모든 위치에서 해당 항목이 보관됩니다.",
|
|
346
346
|
cancel: "취소",
|
|
347
347
|
remove: "제거",
|
|
348
348
|
removing: "삭제 중...",
|
|
349
|
-
delete: "
|
|
350
|
-
deleting: "
|
|
349
|
+
delete: "보관",
|
|
350
|
+
deleting: "보관 중...",
|
|
351
351
|
openFullView: "전체 보기 열기",
|
|
352
352
|
removeFromWidgetArea: "이 위젯 영역에서 제거",
|
|
353
|
-
deleteExtensionEllipsis: "확장 프로그램
|
|
353
|
+
deleteExtensionEllipsis: "확장 프로그램 보관...",
|
|
354
354
|
removeFromMyListEllipsis: "내 목록에서 제거...",
|
|
355
355
|
removeFromWidgetAreaForMe: "Remove from this widget area (for me)",
|
|
356
356
|
deleteEverywhereConfirmation:
|
|
357
|
-
"이렇게 하면 확장 프로그램을 공유하는 모든 사람의 모든 위치에서 확장 프로그램이
|
|
357
|
+
"이렇게 하면 확장 프로그램을 공유하는 모든 사람의 모든 위치에서 확장 프로그램이 보관됩니다.",
|
|
358
358
|
addWidget: "위젯 추가",
|
|
359
359
|
loading: "로드 중...",
|
|
360
360
|
noWidgetsAvailable: "이 슬롯에는 아직 사용할 수 있는 위젯이 없습니다.",
|