@agent-native/core 0.79.16 → 0.79.18
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 +14 -0
- package/corpus/core/docs/content/file-uploads.md +11 -4
- package/corpus/core/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/multi-app-workspace.md +3 -3
- package/corpus/core/docs/content/template-clips.md +14 -5
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +8 -3
- package/corpus/core/src/cli/mcp.ts +11 -6
- package/corpus/core/src/client/AssistantChat.tsx +6 -2
- package/corpus/core/src/client/chat/markdown-renderer.tsx +1 -0
- package/corpus/core/src/client/index.ts +7 -1
- package/corpus/core/src/client/org/OrgSwitcher.tsx +5 -5
- package/corpus/core/src/client/org/TeamPage.tsx +11 -3
- package/corpus/core/src/client/settings/SettingsTabsPage.tsx +216 -0
- package/corpus/core/src/client/settings/index.ts +5 -0
- package/corpus/core/src/file-upload/index.ts +1 -0
- package/corpus/core/src/file-upload/registry.ts +22 -6
- package/corpus/core/src/file-upload/types.ts +6 -1
- package/corpus/core/src/integrations/adapters/email.ts +26 -18
- package/corpus/core/src/integrations/adapters/slack.ts +8 -7
- package/corpus/core/src/integrations/adapters/telegram.ts +9 -11
- package/corpus/core/src/integrations/adapters/whatsapp.ts +12 -9
- package/corpus/core/src/integrations/plugin.ts +79 -13
- package/corpus/core/src/onboarding/default-steps.ts +6 -3
- package/corpus/core/src/onboarding/types.ts +1 -1
- package/corpus/core/src/org/handlers.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +4 -7
- package/corpus/core/src/server/better-auth-instance.ts +19 -62
- package/corpus/core/src/server/core-routes-plugin.ts +93 -180
- package/corpus/core/src/server/create-server.ts +55 -116
- package/corpus/core/src/server/email-actions.ts +3 -6
- package/corpus/core/src/server/email.ts +52 -17
- package/corpus/core/src/server/index.ts +2 -1
- package/corpus/core/src/server/scoped-key-storage.ts +223 -0
- package/corpus/core/src/shared/agent-env.ts +3 -3
- package/corpus/core/src/sharing/actions/share-resource.ts +1 -1
- package/corpus/core/src/styles/agent-native.css +55 -3
- package/corpus/core/src/templates/workspace-root/AGENTS.md +3 -2
- package/corpus/core/src/templates/workspace-root/README.md +1 -1
- package/corpus/core/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
- package/corpus/core/src/terminal/pty-server.ts +3 -38
- package/corpus/core/src/triggers/dispatcher.ts +1 -2
- package/corpus/templates/.retired/issues/package.json +7 -0
- package/corpus/templates/.retired/meeting-notes/package.json +7 -0
- package/corpus/templates/.retired/recruiting/package.json +7 -0
- package/corpus/templates/.retired/scheduling/package.json +7 -0
- package/corpus/templates/.retired/voice/package.json +7 -0
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +0 -17
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +7 -1
- package/corpus/templates/analytics/app/i18n-data.ts +71 -2
- package/corpus/templates/analytics/app/pages/Settings.tsx +120 -73
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +0 -11
- package/corpus/templates/analytics/app/routes/catalog.tsx +33 -176
- package/corpus/templates/analytics/app/routes/team.tsx +3 -2
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-templates-are-tucked-into-settings-and-show-simple.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-the-sessions-page-shows-a-single-page-title-instead-of-repea.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +37 -28
- package/corpus/templates/assets/app/routes/settings.tsx +65 -43
- package/corpus/templates/assets/app/routes/team.tsx +2 -10
- package/corpus/templates/brain/app/routes/settings.tsx +390 -358
- package/corpus/templates/brain/app/routes/team.tsx +2 -8
- package/corpus/templates/calendar/actions/db-connect.ts +5 -29
- package/corpus/templates/calendar/app/components/CloudUpgrade.tsx +8 -51
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +2 -1
- package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +4 -2
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/calendar/app/pages/Settings.tsx +304 -271
- package/corpus/templates/calendar/app/routes/_app.team.tsx +2 -17
- package/corpus/templates/calendar/server/handlers/google-auth.ts +22 -9
- package/corpus/templates/calendar/server/lib/booking-emails.ts +1 -1
- package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +1 -1
- package/corpus/templates/calendar/server/lib/google-calendar.ts +48 -28
- package/corpus/templates/chat/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -2
- package/corpus/templates/chat/app/routes/settings.tsx +54 -54
- package/corpus/templates/chat/app/routes/team.tsx +2 -10
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +7 -2
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +7 -0
- package/corpus/templates/clips/AGENTS.md +8 -0
- package/corpus/templates/clips/actions/invite-member.ts +1 -1
- package/corpus/templates/clips/actions/request-transcript.ts +1 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +629 -643
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +219 -155
- package/corpus/templates/clips/changelog/2026-06-26-long-desktop-recordings-are-checkpointed-so-macos-finalizati.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-26-non-mac-desktop-recordings-wait-for-upload-transcription.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-26-s3-compatible-storage-keys-now-save-securely-from-clips-sett.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-26-shared-clip-links-guide-agents-through-processing-transcripts.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-26-the-clip-details-sidebar-now-collapses-into-a-slide-out-pane.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-26-the-clips-library-now-matches-the-shared-recessed-shell-styl.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +55 -3
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +217 -5
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +176 -5
- package/corpus/templates/clips/server/lib/public-agent-context.ts +31 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +60 -14
- package/corpus/templates/clips/server/lib/video-storage.ts +37 -9
- package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +5 -0
- package/corpus/templates/content/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +1 -0
- package/corpus/templates/content/app/routes/_app.settings.tsx +54 -55
- package/corpus/templates/content/app/routes/_app.team.tsx +2 -28
- package/corpus/templates/content/server/lib/notion.ts +43 -11
- package/corpus/templates/content/server/routes/api/notion/auth-url.get.ts +2 -2
- package/corpus/templates/content/server/routes/api/notion/status.get.ts +3 -3
- package/corpus/templates/design/app/routes/settings.tsx +60 -38
- package/corpus/templates/design/app/routes/team.tsx +2 -10
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +2 -2
- package/corpus/templates/dispatch/app/routes/settings.tsx +70 -55
- package/corpus/templates/dispatch/app/routes/team.tsx +5 -1
- package/corpus/templates/forms/actions/db-connect.ts +7 -36
- package/corpus/templates/forms/actions/navigate.ts +2 -2
- package/corpus/templates/forms/app/components/CloudUpgrade.tsx +8 -47
- package/corpus/templates/forms/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/forms/app/components/layout/Layout.tsx +42 -25
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +35 -76
- package/corpus/templates/forms/app/global.css +9 -25
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +4 -4
- package/corpus/templates/forms/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +1 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/forms/app/lib/route-prewarm.ts +9 -8
- package/corpus/templates/forms/app/pages/AskPage.tsx +70 -0
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +74 -81
- package/corpus/templates/forms/app/root.tsx +1 -1
- package/corpus/templates/forms/app/routes/_app.ask.tsx +14 -0
- package/corpus/templates/forms/app/routes/_app.settings.tsx +54 -54
- package/corpus/templates/forms/app/routes/_app.team.tsx +2 -9
- package/corpus/templates/forms/app/routes/_index.tsx +21 -130
- package/corpus/templates/forms/changelog/2026-06-26-ask-forms-opens-as-a-standard-app-tab-with-a-simpler-sidebar.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-26-forms-are-shown-in-a-tighter-row-list-with-a-denser-sidebar.md +6 -0
- package/corpus/templates/forms/shared/navigation.ts +4 -2
- package/corpus/templates/macros/app/routes/settings.tsx +54 -54
- package/corpus/templates/macros/app/routes/team.tsx +2 -10
- package/corpus/templates/mail/actions/helpers.ts +2 -2
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +2 -1
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +5 -5
- package/corpus/templates/mail/app/routes/team.tsx +3 -2
- package/corpus/templates/mail/server/handlers/emails.ts +3 -2
- package/corpus/templates/mail/server/handlers/google-auth.ts +19 -12
- package/corpus/templates/mail/server/lib/automation-engine.ts +3 -2
- package/corpus/templates/mail/server/lib/email-state.ts +2 -3
- package/corpus/templates/mail/server/lib/google-auth.ts +28 -11
- package/corpus/templates/mail/server/lib/jobs.ts +3 -2
- package/corpus/templates/mail/server/onboarding.ts +9 -6
- package/corpus/templates/mail/server/routes/api/attachments.get.ts +3 -3
- package/corpus/templates/plan/actions/request-plan-access.ts +1 -1
- package/corpus/templates/plan/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +4 -2
- package/corpus/templates/plan/app/lib/route-prewarm.ts +0 -4
- package/corpus/templates/plan/app/routes/settings.tsx +75 -73
- package/corpus/templates/plan/app/routes/team.tsx +2 -8
- package/corpus/templates/plan/server/lib/comment-notifications.ts +1 -1
- package/corpus/templates/slides/actions/check-image-gen.ts +2 -1
- package/corpus/templates/slides/actions/db-connect.ts +7 -45
- package/corpus/templates/slides/actions/edit-image.ts +6 -3
- package/corpus/templates/slides/actions/generate-image-api.ts +1 -1
- package/corpus/templates/slides/actions/generate-image.ts +2 -2
- package/corpus/templates/slides/actions/generate-slides-ai.ts +5 -3
- package/corpus/templates/slides/actions/image-gen-status.ts +4 -3
- package/corpus/templates/slides/actions/image-search.ts +6 -3
- package/corpus/templates/slides/actions/search-images.ts +6 -3
- package/corpus/templates/slides/app/components/CloudUpgrade.tsx +8 -51
- package/corpus/templates/slides/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/slides/app/routes/settings.tsx +54 -54
- package/corpus/templates/slides/app/routes/team.tsx +2 -12
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +9 -9
- package/corpus/templates/slides/server/handlers/image-gen.ts +5 -1
- package/corpus/templates/slides/server/handlers/image-providers/gemini.ts +9 -1
- package/corpus/templates/slides/server/handlers/image-providers/index.ts +18 -8
- package/corpus/templates/slides/server/handlers/image-providers/openai.ts +10 -4
- package/corpus/templates/slides/server/handlers/image-providers/types.ts +1 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +59 -34
- package/corpus/templates/slides/server/register-secrets.ts +60 -0
- package/corpus/templates/videos/actions/db-connect.ts +7 -45
- package/corpus/templates/videos/app/components/CloudUpgrade.tsx +8 -51
- package/corpus/templates/videos/app/components/Sidebar.tsx +1 -9
- package/corpus/templates/videos/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -3
- package/corpus/templates/videos/app/routes/settings.tsx +54 -54
- package/corpus/templates/videos/app/routes/team.tsx +2 -8
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +8 -3
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +3 -4
- package/dist/cli/mcp.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +6 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
- package/dist/client/chat/markdown-renderer.js +1 -0
- package/dist/client/chat/markdown-renderer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -3
- package/dist/client/org/OrgSwitcher.js +2 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts +6 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +2 -2
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/settings/SettingsTabsPage.d.ts +27 -0
- package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -0
- package/dist/client/settings/SettingsTabsPage.js +108 -0
- package/dist/client/settings/SettingsTabsPage.js.map +1 -0
- package/dist/client/settings/index.d.ts +1 -0
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -0
- package/dist/client/settings/index.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/file-upload/index.d.ts +1 -1
- package/dist/file-upload/index.d.ts.map +1 -1
- package/dist/file-upload/index.js +1 -1
- package/dist/file-upload/index.js.map +1 -1
- package/dist/file-upload/registry.d.ts +1 -0
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +24 -6
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/file-upload/types.d.ts +6 -1
- package/dist/file-upload/types.d.ts.map +1 -1
- package/dist/file-upload/types.js.map +1 -1
- package/dist/integrations/adapters/email.d.ts.map +1 -1
- package/dist/integrations/adapters/email.js +18 -18
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +8 -7
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/adapters/telegram.d.ts.map +1 -1
- package/dist/integrations/adapters/telegram.js +9 -9
- package/dist/integrations/adapters/telegram.js.map +1 -1
- package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
- package/dist/integrations/adapters/whatsapp.js +10 -9
- package/dist/integrations/adapters/whatsapp.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +47 -13
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +6 -5
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/onboarding/types.d.ts +1 -1
- package/dist/onboarding/types.d.ts.map +1 -1
- package/dist/onboarding/types.js.map +1 -1
- package/dist/org/handlers.js +1 -1
- package/dist/org/handlers.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +4 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +17 -56
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +67 -153
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/create-server.d.ts +3 -9
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +37 -91
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/email-actions.d.ts +2 -3
- package/dist/server/email-actions.d.ts.map +1 -1
- package/dist/server/email-actions.js +3 -6
- package/dist/server/email-actions.js.map +1 -1
- package/dist/server/email.d.ts +3 -3
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +40 -19
- package/dist/server/email.js.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/scoped-key-storage.d.ts +22 -0
- package/dist/server/scoped-key-storage.d.ts.map +1 -0
- package/dist/server/scoped-key-storage.js +152 -0
- package/dist/server/scoped-key-storage.js.map +1 -0
- package/dist/shared/agent-env.d.ts +3 -3
- package/dist/shared/agent-env.d.ts.map +1 -1
- package/dist/shared/agent-env.js +3 -3
- package/dist/shared/agent-env.js.map +1 -1
- package/dist/sharing/actions/share-resource.js +1 -1
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/styles/agent-native.css +55 -3
- package/dist/templates/workspace-root/AGENTS.md +3 -2
- package/dist/templates/workspace-root/README.md +1 -1
- package/dist/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
- package/dist/terminal/pty-server.d.ts.map +1 -1
- package/dist/terminal/pty-server.js +3 -36
- package/dist/terminal/pty-server.js.map +1 -1
- package/dist/triggers/dispatcher.js +1 -1
- package/dist/triggers/dispatcher.js.map +1 -1
- package/docs/content/file-uploads.md +11 -4
- package/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
- package/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
- package/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
- package/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
- package/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
- package/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
- package/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
- package/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
- package/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
- package/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
- package/docs/content/multi-app-workspace.md +3 -3
- package/docs/content/template-clips.md +14 -5
- package/package.json +1 -1
- package/src/templates/workspace-root/AGENTS.md +3 -2
- package/src/templates/workspace-root/README.md +1 -1
- package/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
- package/corpus/core/src/server/env-var-writes.ts +0 -16
- package/corpus/templates/analytics/app/pages/Team.tsx +0 -9
- package/corpus/templates/calendar/app/pages/Team.tsx +0 -12
- package/corpus/templates/mail/app/pages/Team.tsx +0 -17
- package/dist/server/env-var-writes.d.ts +0 -10
- package/dist/server/env-var-writes.d.ts.map +0 -1
- package/dist/server/env-var-writes.js +0 -18
- package/dist/server/env-var-writes.js.map +0 -1
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
2
|
defineEventHandler,
|
|
5
3
|
setResponseStatus,
|
|
@@ -46,7 +44,7 @@ import { mountDbAdminRoutes } from "../db-admin/routes.js";
|
|
|
46
44
|
import { getDbExec } from "../db/client.js";
|
|
47
45
|
import {
|
|
48
46
|
uploadFile,
|
|
49
|
-
|
|
47
|
+
getActiveFileUploadProviderForRequest,
|
|
50
48
|
listFileUploadProviders,
|
|
51
49
|
} from "../file-upload/index.js";
|
|
52
50
|
import { handleMcpConnect } from "../mcp/connect-route.js";
|
|
@@ -57,10 +55,8 @@ import {
|
|
|
57
55
|
} from "../mcp/oauth-route.js";
|
|
58
56
|
import { registerBuiltinNotificationChannels } from "../notifications/channels.js";
|
|
59
57
|
import { createNotificationsHandler } from "../notifications/routes.js";
|
|
60
|
-
import { listOnboardingSteps } from "../onboarding/registry.js";
|
|
61
58
|
import { getOrgContext } from "../org/context.js";
|
|
62
59
|
import { createProgressHandler } from "../progress/routes.js";
|
|
63
|
-
import { findWorkspaceRoot } from "../scripts/utils.js";
|
|
64
60
|
import { registerFrameworkSecrets } from "../secrets/register-framework-secrets.js";
|
|
65
61
|
import {
|
|
66
62
|
createListSecretsHandler,
|
|
@@ -124,14 +120,12 @@ import {
|
|
|
124
120
|
getAllowedCorsOrigin,
|
|
125
121
|
readCorsAllowedOrigins,
|
|
126
122
|
} from "./cors-origins.js";
|
|
127
|
-
import { upsertEnvFile } from "./create-server.js";
|
|
128
123
|
import type { EnvKeyConfig } from "./create-server.js";
|
|
129
124
|
import {
|
|
130
125
|
canUseDeployCredentialFallbackForRequest,
|
|
131
126
|
resolveSecret,
|
|
132
127
|
} from "./credential-provider.js";
|
|
133
128
|
import { createEmbedStartRouteHandler } from "./embed-route.js";
|
|
134
|
-
import { isEnvVarWriteAllowed } from "./env-var-writes.js";
|
|
135
129
|
import {
|
|
136
130
|
getH3App,
|
|
137
131
|
awaitBootstrap,
|
|
@@ -151,6 +145,12 @@ import { createOpenRouteHandler } from "./open-route.js";
|
|
|
151
145
|
import { createPollEventsHandler } from "./poll-events.js";
|
|
152
146
|
import { createPollHandler } from "./poll.js";
|
|
153
147
|
import { runWithRequestContext } from "./request-context.js";
|
|
148
|
+
import {
|
|
149
|
+
findUnsupportedScopedKeyNames,
|
|
150
|
+
saveKeyValuesToScopedSecrets,
|
|
151
|
+
ScopedKeyStorageError,
|
|
152
|
+
type ScopedKeySaveRequestScope,
|
|
153
|
+
} from "./scoped-key-storage.js";
|
|
154
154
|
import { createTranscribeVoiceHandler } from "./transcribe-voice.js";
|
|
155
155
|
import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
|
|
156
156
|
|
|
@@ -340,10 +340,6 @@ function parseBuilderCallbackBoolean(
|
|
|
340
340
|
return /^(1|true)$/i.test(value);
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
const PROVIDER_ENV_VAR_KEYS = new Set(
|
|
344
|
-
Object.values(PROVIDER_ENV_META).map(({ envVar }) => envVar),
|
|
345
|
-
);
|
|
346
|
-
|
|
347
343
|
// Raster-only data-URI allowlist for avatar writes. SVG is deliberately absent:
|
|
348
344
|
// data:image/svg+xml payloads can carry inline <script> and event-handler
|
|
349
345
|
// attributes that execute when the browser renders them as an <img> src or
|
|
@@ -652,7 +648,7 @@ export interface CoreRoutesPluginOptions {
|
|
|
652
648
|
* GET /_agent-native/ping — health check
|
|
653
649
|
* GET /_agent-native/health — DB liveness probe + scale-to-zero warmup
|
|
654
650
|
* GET /_agent-native/env-status — env key configuration status (when envKeys provided)
|
|
655
|
-
* POST /_agent-native/env-vars —
|
|
651
|
+
* POST /_agent-native/env-vars — compatibility route that saves keys to scoped DB secrets
|
|
656
652
|
* GET /_agent-native/application-state/:key — read application state
|
|
657
653
|
* PUT /_agent-native/application-state/:key — write application state
|
|
658
654
|
* DELETE /_agent-native/application-state/:key — delete application state
|
|
@@ -681,20 +677,9 @@ export function createCoreRoutesPlugin(
|
|
|
681
677
|
try {
|
|
682
678
|
await awaitBootstrap(nitroApp);
|
|
683
679
|
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
// vars (Netlify dashboard, process-level) always win.
|
|
688
|
-
//
|
|
689
|
-
// GATED: only rehydrate into `process.env` on local-dev SQLite (or
|
|
690
|
-
// with the explicit single-tenant opt-in). On a shared-DB hosted
|
|
691
|
-
// multi-tenant deploy the `persisted-env-vars` row is deployment-wide
|
|
692
|
-
// global state — pushing user-supplied values into `process.env` from
|
|
693
|
-
// it would let any one tenant's writes (or a stale dev seed) leak
|
|
694
|
-
// into every other tenant's process. The opt-out scrub of legacy
|
|
695
|
-
// BUILDER_* values still runs unconditionally so existing rows on
|
|
696
|
-
// multi-tenant deploys self-heal, but new env-var writes never land
|
|
697
|
-
// in `process.env` outside the allowed contexts.
|
|
680
|
+
// Legacy cleanup: key saves now go to scoped app_secrets rows. Do not
|
|
681
|
+
// rehydrate the old deployment-global `persisted-env-vars` row into
|
|
682
|
+
// process.env; keep only the Builder scrub so stale leaked keys self-heal.
|
|
698
683
|
try {
|
|
699
684
|
const persisted = (await getSetting("persisted-env-vars")) as Record<
|
|
700
685
|
string,
|
|
@@ -702,15 +687,10 @@ export function createCoreRoutesPlugin(
|
|
|
702
687
|
> | null;
|
|
703
688
|
if (persisted) {
|
|
704
689
|
const builderKeys = new Set<string>(BUILDER_ENV_KEYS);
|
|
705
|
-
const writesAllowed = isEnvVarWriteAllowed();
|
|
706
690
|
let scrubbed = 0;
|
|
707
|
-
for (const
|
|
691
|
+
for (const k of Object.keys(persisted)) {
|
|
708
692
|
if (builderKeys.has(k)) {
|
|
709
693
|
scrubbed++;
|
|
710
|
-
continue;
|
|
711
|
-
}
|
|
712
|
-
if (writesAllowed && typeof v === "string" && !process.env[k]) {
|
|
713
|
-
process.env[k] = v;
|
|
714
694
|
}
|
|
715
695
|
}
|
|
716
696
|
if (scrubbed > 0) {
|
|
@@ -2229,24 +2209,7 @@ export function createCoreRoutesPlugin(
|
|
|
2229
2209
|
];
|
|
2230
2210
|
{
|
|
2231
2211
|
const envKeys = [...frameworkEnvKeys, ...(options.envKeys ?? [])];
|
|
2232
|
-
|
|
2233
|
-
// Onboarding form fields are resolved per-request so late-registered
|
|
2234
|
-
// steps (and template overrides) are picked up without a restart.
|
|
2235
|
-
// Builder CLI auth writes scoped Builder credentials through the
|
|
2236
|
-
// credential provider, never through the deploy-global env sink.
|
|
2237
|
-
const collectOnboardingKeys = (): Set<string> => {
|
|
2238
|
-
const keys = new Set<string>();
|
|
2239
|
-
for (const step of listOnboardingSteps()) {
|
|
2240
|
-
for (const method of step.methods) {
|
|
2241
|
-
if (method.kind === "form") {
|
|
2242
|
-
for (const field of method.payload.fields) {
|
|
2243
|
-
if (field?.key) keys.add(field.key);
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
return keys;
|
|
2249
|
-
};
|
|
2212
|
+
const allowedEnvKeyNames = envKeys.map(({ key }) => key);
|
|
2250
2213
|
|
|
2251
2214
|
getH3App(nitroApp).use(
|
|
2252
2215
|
`${P}/env-status`,
|
|
@@ -2264,12 +2227,11 @@ export function createCoreRoutesPlugin(
|
|
|
2264
2227
|
}
|
|
2265
2228
|
return Promise.all(
|
|
2266
2229
|
envKeys.map(async (cfg) => {
|
|
2267
|
-
const
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
: !!process.env[cfg.key];
|
|
2230
|
+
const configured =
|
|
2231
|
+
Boolean(process.env[cfg.key]) ||
|
|
2232
|
+
(await runWithRequestContext({ userEmail, orgId }, () =>
|
|
2233
|
+
resolveSecret(cfg.key).then(Boolean),
|
|
2234
|
+
));
|
|
2273
2235
|
return {
|
|
2274
2236
|
key: cfg.key,
|
|
2275
2237
|
label: cfg.label,
|
|
@@ -2290,101 +2252,37 @@ export function createCoreRoutesPlugin(
|
|
|
2290
2252
|
return { error: "Method not allowed" };
|
|
2291
2253
|
}
|
|
2292
2254
|
|
|
2293
|
-
// Env vars are deployment-wide globals, not per-tenant. On any
|
|
2294
|
-
// shared-DB multi-tenant deploy, allowing authenticated users to
|
|
2295
|
-
// write here lets one tenant overwrite Stripe / OpenAI / Sentry
|
|
2296
|
-
// keys for every other tenant. Disable the endpoint outside of
|
|
2297
|
-
// local-dev SQLite or an explicit single-tenant opt-in, and
|
|
2298
|
-
// direct callers to scoped secret/credential stores instead.
|
|
2299
|
-
if (!isEnvVarWriteAllowed()) {
|
|
2300
|
-
setResponseStatus(event, 403);
|
|
2301
|
-
return {
|
|
2302
|
-
error:
|
|
2303
|
-
"env-vars endpoint disabled on multi-tenant deployments. Use scoped secrets or credentials for user/org API keys.",
|
|
2304
|
-
};
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
2255
|
const body = await readBody(event);
|
|
2308
|
-
const { vars } = body as {
|
|
2256
|
+
const { vars, scope } = body as {
|
|
2309
2257
|
vars?: Array<{ key: string; value: string }>;
|
|
2258
|
+
scope?: ScopedKeySaveRequestScope;
|
|
2310
2259
|
};
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
return { error: "vars array required" };
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
const allowedKeys = new Set<string>([
|
|
2318
|
-
...envKeys.map((k) => k.key),
|
|
2319
|
-
...collectOnboardingKeys(),
|
|
2320
|
-
]);
|
|
2321
|
-
const blockedEnvVarWriteKeys = new Set<string>(BUILDER_ENV_KEYS);
|
|
2322
|
-
const isWritableEnvKey = (key: string) =>
|
|
2323
|
-
allowedKeys.has(key) && !blockedEnvVarWriteKeys.has(key);
|
|
2324
|
-
|
|
2325
|
-
const filtered = vars.filter(
|
|
2326
|
-
(v) =>
|
|
2327
|
-
typeof v.key === "string" &&
|
|
2328
|
-
isWritableEnvKey(v.key) &&
|
|
2329
|
-
typeof v.value === "string" &&
|
|
2330
|
-
v.value.trim().length > 0,
|
|
2260
|
+
const unsupportedKeys = findUnsupportedScopedKeyNames(
|
|
2261
|
+
vars,
|
|
2262
|
+
allowedEnvKeyNames,
|
|
2331
2263
|
);
|
|
2332
|
-
if (
|
|
2264
|
+
if (unsupportedKeys.length > 0) {
|
|
2333
2265
|
setResponseStatus(event, 400);
|
|
2334
|
-
const rejectedEmpty = vars.some(
|
|
2335
|
-
(v) =>
|
|
2336
|
-
typeof v.key === "string" &&
|
|
2337
|
-
isWritableEnvKey(v.key) &&
|
|
2338
|
-
(typeof v.value !== "string" || v.value.trim().length === 0),
|
|
2339
|
-
);
|
|
2340
2266
|
return {
|
|
2341
|
-
error:
|
|
2342
|
-
? "Env values must be non-empty — refusing to clear a saved key"
|
|
2343
|
-
: "No recognized env keys in request",
|
|
2267
|
+
error: `Unsupported env key${unsupportedKeys.length === 1 ? "" : "s"}: ${unsupportedKeys.join(", ")}`,
|
|
2344
2268
|
};
|
|
2345
2269
|
}
|
|
2346
2270
|
|
|
2347
|
-
// Write to .env file. When inside a workspace, write to the
|
|
2348
|
-
// workspace root .env so keys are shared across every app. The
|
|
2349
|
-
// per-app .env still wins at load time if it also defines a key.
|
|
2350
2271
|
try {
|
|
2351
|
-
const
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
// Update process.env immediately
|
|
2366
|
-
for (const { key, value } of filtered) {
|
|
2367
|
-
process.env[key] = value;
|
|
2368
|
-
}
|
|
2369
|
-
|
|
2370
|
-
// Persist to settings table for serverless cold-start recovery.
|
|
2371
|
-
try {
|
|
2372
|
-
const envMap: Record<string, string> = {};
|
|
2373
|
-
for (const { key, value } of filtered) envMap[key] = value;
|
|
2374
|
-
const existing =
|
|
2375
|
-
((await getSetting("persisted-env-vars")) as Record<
|
|
2376
|
-
string,
|
|
2377
|
-
string
|
|
2378
|
-
> | null) ?? {};
|
|
2379
|
-
await putSetting("persisted-env-vars", {
|
|
2380
|
-
...existing,
|
|
2381
|
-
...envMap,
|
|
2382
|
-
});
|
|
2383
|
-
} catch {
|
|
2384
|
-
// DB not ready yet — skip
|
|
2272
|
+
const result = await saveKeyValuesToScopedSecrets(
|
|
2273
|
+
event,
|
|
2274
|
+
vars,
|
|
2275
|
+
scope,
|
|
2276
|
+
);
|
|
2277
|
+
return { saved: result.saved, storage: "scoped-secrets" };
|
|
2278
|
+
} catch (err) {
|
|
2279
|
+
if (err instanceof ScopedKeyStorageError) {
|
|
2280
|
+
setResponseStatus(event, err.statusCode);
|
|
2281
|
+
return { error: err.message };
|
|
2282
|
+
}
|
|
2283
|
+
setResponseStatus(event, 500);
|
|
2284
|
+
return { error: "Failed to save keys" };
|
|
2385
2285
|
}
|
|
2386
|
-
|
|
2387
|
-
return { saved: filtered.map((v) => v.key) };
|
|
2388
2286
|
}),
|
|
2389
2287
|
);
|
|
2390
2288
|
}
|
|
@@ -2729,54 +2627,69 @@ export function createCoreRoutesPlugin(
|
|
|
2729
2627
|
getH3App(nitroApp).use(
|
|
2730
2628
|
`${P}/file-upload/status`,
|
|
2731
2629
|
defineEventHandler(async (event) => {
|
|
2732
|
-
const active = getActiveFileUploadProvider();
|
|
2733
2630
|
// resolveBuilderPrivateKey() reads per-user credentials from app_secrets
|
|
2734
2631
|
// (DB), which requires request context (AsyncLocalStorage) to know which
|
|
2735
2632
|
// user to scope by. Without runWithRequestContext() the ALS store is empty
|
|
2736
2633
|
// and it falls back to process.env only — missing OAuth-connected users.
|
|
2737
2634
|
const session = await getSession(event).catch(() => null);
|
|
2738
2635
|
const userEmail = session?.email;
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2636
|
+
const resolveStatus = async () => {
|
|
2637
|
+
const active = await getActiveFileUploadProviderForRequest();
|
|
2638
|
+
let builderConfigured = !!process.env.BUILDER_PRIVATE_KEY;
|
|
2639
|
+
try {
|
|
2640
|
+
const { resolveBuilderPrivateKey } =
|
|
2641
|
+
await import("./credential-provider.js");
|
|
2642
|
+
builderConfigured = await resolveBuilderPrivateKey().then(
|
|
2643
|
+
(k) => !!k,
|
|
2644
|
+
);
|
|
2645
|
+
} catch {
|
|
2646
|
+
// fall back to env check above
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
const providers = await Promise.all(
|
|
2650
|
+
listFileUploadProviders().map(async (p) => {
|
|
2651
|
+
const scopedConfigured = p.isConfiguredForRequest
|
|
2652
|
+
? await p.isConfiguredForRequest().catch(() => false)
|
|
2653
|
+
: false;
|
|
2654
|
+
return {
|
|
2655
|
+
id: p.id,
|
|
2656
|
+
name: p.name,
|
|
2657
|
+
configured: p.isConfigured() || scopedConfigured,
|
|
2658
|
+
};
|
|
2659
|
+
}),
|
|
2660
|
+
);
|
|
2661
|
+
|
|
2662
|
+
// When the builder builtin is selected via env var, its sync
|
|
2663
|
+
// isConfigured() doesn't reflect per-user OAuth credentials. Use
|
|
2664
|
+
// builderConfigured so status reflects this specific request.
|
|
2665
|
+
const isBuilderEnvActive = active?.id === "builder";
|
|
2666
|
+
const configured = isBuilderEnvActive
|
|
2667
|
+
? builderConfigured
|
|
2668
|
+
: !!active || builderConfigured;
|
|
2669
|
+
const activeProvider = isBuilderEnvActive
|
|
2670
|
+
? builderConfigured
|
|
2768
2671
|
? { id: "builder", name: "Builder.io" }
|
|
2769
|
-
: null
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2672
|
+
: null
|
|
2673
|
+
: active
|
|
2674
|
+
? { id: active.id, name: active.name }
|
|
2675
|
+
: builderConfigured
|
|
2676
|
+
? { id: "builder", name: "Builder.io" }
|
|
2677
|
+
: null;
|
|
2678
|
+
|
|
2679
|
+
return {
|
|
2680
|
+
configured,
|
|
2681
|
+
activeProvider,
|
|
2682
|
+
providers,
|
|
2683
|
+
builderConfigured,
|
|
2684
|
+
};
|
|
2779
2685
|
};
|
|
2686
|
+
|
|
2687
|
+
return userEmail
|
|
2688
|
+
? runWithRequestContext(
|
|
2689
|
+
{ userEmail, orgId: session?.orgId },
|
|
2690
|
+
resolveStatus,
|
|
2691
|
+
)
|
|
2692
|
+
: resolveStatus();
|
|
2780
2693
|
}),
|
|
2781
2694
|
);
|
|
2782
2695
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
2
|
createApp,
|
|
5
3
|
createRouter,
|
|
@@ -11,8 +9,8 @@ import {
|
|
|
11
9
|
type H3Event,
|
|
12
10
|
} from "h3";
|
|
13
11
|
|
|
12
|
+
import { getOrgContext } from "../org/context.js";
|
|
14
13
|
import { readBody } from "../server/h3-helpers.js";
|
|
15
|
-
import { agentEnv } from "../shared/agent-env.js";
|
|
16
14
|
import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
|
|
17
15
|
import {
|
|
18
16
|
EMBED_TRANSPLANT_HEADER,
|
|
@@ -20,12 +18,19 @@ import {
|
|
|
20
18
|
MCP_EMBED_CORS_ALLOW_HEADERS,
|
|
21
19
|
shouldAllowMcpEmbedCredentials,
|
|
22
20
|
} from "../shared/mcp-embed-headers.js";
|
|
23
|
-
import {
|
|
21
|
+
import { getSession } from "./auth.js";
|
|
24
22
|
import {
|
|
25
23
|
getAllowedCorsOrigin,
|
|
26
24
|
readCorsAllowedOrigins,
|
|
27
25
|
} from "./cors-origins.js";
|
|
28
|
-
import {
|
|
26
|
+
import { resolveSecret } from "./credential-provider.js";
|
|
27
|
+
import { runWithRequestContext } from "./request-context.js";
|
|
28
|
+
import {
|
|
29
|
+
findUnsupportedScopedKeyNames,
|
|
30
|
+
saveKeyValuesToScopedSecrets,
|
|
31
|
+
ScopedKeyStorageError,
|
|
32
|
+
type ScopedKeySaveRequestScope,
|
|
33
|
+
} from "./scoped-key-storage.js";
|
|
29
34
|
|
|
30
35
|
export interface EnvKeyConfig {
|
|
31
36
|
/** Environment variable name (e.g. "HUBSPOT_ACCESS_TOKEN") */
|
|
@@ -47,70 +52,10 @@ export interface CreateServerOptions {
|
|
|
47
52
|
pingMessage?: string;
|
|
48
53
|
/** Disable the /_agent-native/ping health check. Default: false */
|
|
49
54
|
disablePing?: boolean;
|
|
50
|
-
/**
|
|
55
|
+
/** Key configuration for the settings UI. Enables status plus the scoped-secret compatibility save route. */
|
|
51
56
|
envKeys?: EnvKeyConfig[];
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
/**
|
|
55
|
-
* Upsert vars into a .env file, preserving existing structure.
|
|
56
|
-
*/
|
|
57
|
-
export async function upsertEnvFile(
|
|
58
|
-
envPath: string,
|
|
59
|
-
vars: Array<{ key: string; value: string }>,
|
|
60
|
-
): Promise<void> {
|
|
61
|
-
// Sanitize: reject values that could inject additional env vars
|
|
62
|
-
for (const { key, value } of vars) {
|
|
63
|
-
if (/[\n\r\0]/.test(value)) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
`Invalid env var value for ${key}: must not contain newlines or control characters`,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const fs = await import("fs");
|
|
71
|
-
|
|
72
|
-
let content = "";
|
|
73
|
-
try {
|
|
74
|
-
content = fs.readFileSync(envPath, "utf-8");
|
|
75
|
-
} catch {
|
|
76
|
-
// File doesn't exist yet
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const lines = content.split("\n");
|
|
80
|
-
const remaining = new Map(vars.map((v) => [v.key, v.value]));
|
|
81
|
-
|
|
82
|
-
// Update existing lines in place
|
|
83
|
-
const updated = lines.map((line) => {
|
|
84
|
-
const trimmed = line.trim();
|
|
85
|
-
if (!trimmed || trimmed.startsWith("#")) return line;
|
|
86
|
-
const eqIndex = trimmed.indexOf("=");
|
|
87
|
-
if (eqIndex === -1) return line;
|
|
88
|
-
const key = trimmed.slice(0, eqIndex).trim();
|
|
89
|
-
if (remaining.has(key)) {
|
|
90
|
-
const value = remaining.get(key)!;
|
|
91
|
-
remaining.delete(key);
|
|
92
|
-
return `${key}=${value}`;
|
|
93
|
-
}
|
|
94
|
-
return line;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Append new vars
|
|
98
|
-
for (const [key, value] of remaining) {
|
|
99
|
-
updated.push(`${key}=${value}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Ensure trailing newline
|
|
103
|
-
let result = updated.join("\n");
|
|
104
|
-
if (!result.endsWith("\n")) result += "\n";
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
fs.mkdirSync(path.dirname(envPath), { recursive: true });
|
|
108
|
-
fs.writeFileSync(envPath, result);
|
|
109
|
-
} catch {
|
|
110
|
-
// Edge runtimes don't have writable filesystem — skip silently
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
59
|
export interface CreateServerResult {
|
|
115
60
|
app: ReturnType<typeof createApp>;
|
|
116
61
|
router: ReturnType<typeof createRouter>;
|
|
@@ -120,7 +65,8 @@ export interface CreateServerResult {
|
|
|
120
65
|
* Create a pre-configured H3 app with standard agent-native setup:
|
|
121
66
|
* - CORS headers via middleware
|
|
122
67
|
* - /_agent-native/ping health check
|
|
123
|
-
* - /_agent-native/env-status and
|
|
68
|
+
* - /_agent-native/env-status and the scoped-secret compatibility save route
|
|
69
|
+
* at /_agent-native/env-vars (when envKeys is provided)
|
|
124
70
|
*
|
|
125
71
|
* Returns { app, router } — mount routes on `router`.
|
|
126
72
|
*/
|
|
@@ -242,7 +188,6 @@ export function createServer(
|
|
|
242
188
|
}
|
|
243
189
|
|
|
244
190
|
const router = createRouter();
|
|
245
|
-
app.use(router);
|
|
246
191
|
|
|
247
192
|
// Health check
|
|
248
193
|
if (!options.disablePing) {
|
|
@@ -259,73 +204,67 @@ export function createServer(
|
|
|
259
204
|
// Env key management routes
|
|
260
205
|
if (options.envKeys) {
|
|
261
206
|
const envKeys = options.envKeys;
|
|
207
|
+
const allowedEnvKeyNames = envKeys.map(({ key }) => key);
|
|
262
208
|
|
|
263
209
|
router.get(
|
|
264
210
|
"/_agent-native/env-status",
|
|
265
|
-
defineEventHandler(() => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
211
|
+
defineEventHandler(async (event) => {
|
|
212
|
+
const session = await getSession(event).catch(() => null);
|
|
213
|
+
const userEmail = session?.email;
|
|
214
|
+
let orgId: string | undefined;
|
|
215
|
+
if (userEmail) {
|
|
216
|
+
const orgCtx = await getOrgContext(event).catch(() => null);
|
|
217
|
+
orgId = orgCtx?.orgId ?? undefined;
|
|
218
|
+
}
|
|
219
|
+
return Promise.all(
|
|
220
|
+
envKeys.map(async (cfg) => ({
|
|
221
|
+
key: cfg.key,
|
|
222
|
+
label: cfg.label,
|
|
223
|
+
required: cfg.required ?? false,
|
|
224
|
+
configured:
|
|
225
|
+
Boolean(process.env[cfg.key]) ||
|
|
226
|
+
(await runWithRequestContext({ userEmail, orgId }, () =>
|
|
227
|
+
resolveSecret(cfg.key).then(Boolean),
|
|
228
|
+
)),
|
|
229
|
+
...(cfg.helpText ? { helpText: cfg.helpText } : {}),
|
|
230
|
+
})),
|
|
231
|
+
);
|
|
273
232
|
}),
|
|
274
233
|
);
|
|
275
234
|
|
|
276
235
|
router.post(
|
|
277
236
|
"/_agent-native/env-vars",
|
|
278
237
|
defineEventHandler(async (event: H3Event) => {
|
|
279
|
-
// Env vars are deployment-wide globals — see isEnvVarWriteAllowed
|
|
280
|
-
// above. Disable the endpoint on any multi-tenant deploy.
|
|
281
|
-
if (!isEnvVarWriteAllowed()) {
|
|
282
|
-
setResponseStatus(event, 403);
|
|
283
|
-
return {
|
|
284
|
-
error:
|
|
285
|
-
"env-vars endpoint disabled on multi-tenant deployments. Use scoped secrets or credentials for user/org API keys.",
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
238
|
const body = await readBody(event);
|
|
290
|
-
const { vars } = body as {
|
|
239
|
+
const { vars, scope } = body as {
|
|
291
240
|
vars?: Array<{ key: string; value: string }>;
|
|
241
|
+
scope?: ScopedKeySaveRequestScope;
|
|
292
242
|
};
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return { error: "vars array required" };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Only allow keys that are in the env config
|
|
300
|
-
const allowedKeys = new Set(envKeys.map((k) => k.key));
|
|
301
|
-
const blockedEnvVarWriteKeys = new Set<string>(BUILDER_ENV_KEYS);
|
|
302
|
-
const filtered = vars.filter(
|
|
303
|
-
(v) =>
|
|
304
|
-
typeof v.key === "string" &&
|
|
305
|
-
allowedKeys.has(v.key) &&
|
|
306
|
-
!blockedEnvVarWriteKeys.has(v.key),
|
|
243
|
+
const unsupportedKeys = findUnsupportedScopedKeyNames(
|
|
244
|
+
vars,
|
|
245
|
+
allowedEnvKeyNames,
|
|
307
246
|
);
|
|
308
|
-
if (
|
|
247
|
+
if (unsupportedKeys.length > 0) {
|
|
309
248
|
setResponseStatus(event, 400);
|
|
310
|
-
return {
|
|
249
|
+
return {
|
|
250
|
+
error: `Unsupported env key${unsupportedKeys.length === 1 ? "" : "s"}: ${unsupportedKeys.join(", ")}`,
|
|
251
|
+
};
|
|
311
252
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
253
|
+
try {
|
|
254
|
+
const result = await saveKeyValuesToScopedSecrets(event, vars, scope);
|
|
255
|
+
return { saved: result.saved, storage: "scoped-secrets" };
|
|
256
|
+
} catch (err) {
|
|
257
|
+
if (err instanceof ScopedKeyStorageError) {
|
|
258
|
+
setResponseStatus(event, err.statusCode);
|
|
259
|
+
return { error: err.message };
|
|
260
|
+
}
|
|
261
|
+
setResponseStatus(event, 500);
|
|
262
|
+
return { error: "Failed to save keys" };
|
|
320
263
|
}
|
|
321
|
-
|
|
322
|
-
// Notify parent (Builder or frame) via postMessage
|
|
323
|
-
agentEnv.setVars(filtered);
|
|
324
|
-
|
|
325
|
-
return { saved: filtered.map((v) => v.key) };
|
|
326
264
|
}),
|
|
327
265
|
);
|
|
328
266
|
}
|
|
329
267
|
|
|
268
|
+
app.use(router);
|
|
330
269
|
return { app, router };
|
|
331
270
|
}
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Framework-level agent action for sending transactional/notification emails
|
|
3
3
|
* via the configured core email transport (Resend or SendGrid).
|
|
4
4
|
*
|
|
5
|
-
* Registered as a native tool in every template
|
|
6
|
-
*
|
|
7
|
-
* from the agent surface so the agent never sees it.
|
|
5
|
+
* Registered as a native tool in every template. sendEmail() checks the
|
|
6
|
+
* scoped Resend/SendGrid configuration at call time.
|
|
8
7
|
*
|
|
9
8
|
* SAFETY: the action description instructs the agent to draft-first and only
|
|
10
9
|
* send when the user explicitly confirms, matching the mail template convention.
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
import type { ActionEntry } from "../agent/production-agent.js";
|
|
19
|
-
import {
|
|
18
|
+
import { sendEmail } from "./email.js";
|
|
20
19
|
|
|
21
20
|
function markdownToText(md: string): string {
|
|
22
21
|
return md
|
|
@@ -115,8 +114,6 @@ function markdownToHtml(md: string): string {
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
export function createCoreEmailActionEntries(): Record<string, ActionEntry> {
|
|
118
|
-
if (!isEmailConfigured()) return {};
|
|
119
|
-
|
|
120
117
|
return {
|
|
121
118
|
"core-send-email": {
|
|
122
119
|
tool: {
|